/* ============================================================
   DOMA VIABILIDADE — folha única do produto
   ------------------------------------------------------------
   Entrega C / R1 — a casca: home com grade de cartões, rota por
   hash, tarja C@59px nas duas telas, família de botões `Sereno`
   em bloco único e a paleta da marca.

   REGRA DE COR DESTA FOLHA: nada de hex solto fora do `:root`.
   Se você precisou de uma cor nova, ou ela é derivada de um token
   (e vira token, com a medição que a justifica ao lado) ou o que
   você quer não é cor — é estrutura. Foi assim que a decisão P4
   nasceu: aviso e superfície selecionada se separam por BARRA e
   ÍCONE, não por calibração de matiz.

   HIERARQUIA DE COR, em três alturas e por ESTRUTURA:
     MARCA   ≠ AÇÃO ≠ ALERTA
     #FF6700   #FF8A33  âmbar/vermelho
   A marca vive na logo, no fio do topo e nos acentos de
   identidade. A ação vive no botão primário. O alerta vive em
   âmbar e vermelho, sempre com barra e ícone. Nenhum dos três
   empresta o hex do outro.
   ============================================================ */

/* ============================================================
   0 · TIPOGRAFIA — INTER, SERVIDA DO PROJETO
   ------------------------------------------------------------
   Os arquivos vivem em `static/assets/fonts/` e são servidos pelo
   próprio StaticFiles. Nada aqui vai à rede: dependência externa é
   proibida nesta casa, e a licença + procedência estão no
   `OFL.txt` ao lado dos arquivos.

   O NOME DA FAMÍLIA É "Inter Local", E ISSO É PROVA, NÃO CAPRICHO.
   A Inter está TAMBÉM instalada nesta máquina — foi ela que pintou
   os mockups durante a decisão tipográfica. Se o `@font-face` se
   chamasse "Inter", a tela ficaria idêntica nos dois casos: com a
   webfont funcionando E com o `@font-face` quebrado, porque a
   pilha acharia a fonte do SISTEMA pelo nome. Seriam
   indistinguíveis a olho, e "está na Inter" deixaria de ser
   evidência de coisa nenhuma.
   Com um nome que o sistema NÃO tem, a implicação vira de mão
   única: se a tela pinta em Inter, ela só pode ter vindo DESTES
   arquivos. É o mesmo princípio dos espelhos declarados — a prova
   mora na estrutura, não na inspeção.

   SÃO `.otf` e é deliberado: `.woff2` seria ~6× menor, mas a
   conversão exige `fontTools`, que não está instalado — e instalar
   seria dependência nova. `@font-face` aceita OpenType, e a
   aplicação é servida localmente.

   CINCO PESOS, E SÓ CINCO — 400/500/600/700/800. É exatamente o
   que esta folha usa. Peso fora da lista cai em NEGRITO SINTÉTICO
   do navegador, que é pior que o peso certo e não acusa nada.
   SEM ITÁLICO: não há arquivo itálico e nenhum é esperado. Os três
   lugares com `font-style: italic` (rótulos de ausência, tipo
   "endereço não informado") saem em oblíquo SINTÉTICO de propósito
   — é texto de placeholder, e a inclinação é o sinal, não o
   desenho.

   A PILHA DE FALLBACK CONTINUA OBRIGATÓRIA: se um arquivo faltar,
   a tela tem de ficar CORRETA, só não na Inter.

   O QUE NÃO PODE DEPENDER DE A FONTE CARREGAR: os algarismos
   tabulares. O fallback do `--display` no iPad é o SF Pro, cujos
   algarismos são PROPORCIONAIS — a coluna de números desalinha
   sozinha, sem erro nenhum aparecer na tela. Por isso o
   `font-variant-numeric: tabular-nums` está declarado em toda
   superfície numérica (ver o bloco "NÚMEROS TABULARES") e não
   pendurado na webfont.
   ============================================================ */
@font-face {
  font-family: "Inter Local"; font-style: normal; font-weight: 400;
  font-display: swap; src: url("assets/fonts/Inter-Regular.otf") format("opentype");
}
@font-face {
  font-family: "Inter Local"; font-style: normal; font-weight: 500;
  font-display: swap; src: url("assets/fonts/Inter-Medium.otf") format("opentype");
}
@font-face {
  font-family: "Inter Local"; font-style: normal; font-weight: 600;
  font-display: swap; src: url("assets/fonts/Inter-SemiBold.otf") format("opentype");
}
@font-face {
  font-family: "Inter Local"; font-style: normal; font-weight: 700;
  font-display: swap; src: url("assets/fonts/Inter-Bold.otf") format("opentype");
}
@font-face {
  font-family: "Inter Local"; font-style: normal; font-weight: 800;
  font-display: swap; src: url("assets/fonts/Inter-ExtraBold.otf") format("opentype");
}

/* ------------------------------------------------------------
   A MONO DOS NÚMEROS É A IBM PLEX MONO — DESIGN-0 (11/09/2026).
   A DM Mono foi a escolha de 05/08 a 11/09; o sistema de design
   aprovado pelo dono nesta data trouxe a Plex de volta (D-189 é a
   errata: ela registra a reversão, não decide sozinha). Os dois
   arquivos vieram do histórico (`git checkout 246e7ee^ --
   static/assets/fonts/IBMPlexMono-{Regular,Medium}.ttf`), o mesmo
   commit que os apagou em 21/08 — não são arquivo novo.
   FAMÍLIA "Plex Local", DOIS PESOS — 400 e 500, os únicos que a
   folha usa. Licença OFL em `assets/fonts/OFL-IBMPlexMono.txt`.
   Os `DMMono-*.ttf` FICAM NO DISCO nesta fatia (classe A pura não
   apaga arquivo — é decisão da Mesa), órfãos de `@font-face`.
   ------------------------------------------------------------ */
@font-face {
  font-family: "Plex Local"; font-style: normal; font-weight: 400;
  font-display: swap; src: url("assets/fonts/IBMPlexMono-Regular.ttf") format("truetype");
}
@font-face {
  font-family: "Plex Local"; font-style: normal; font-weight: 500;
  font-display: swap; src: url("assets/fonts/IBMPlexMono-Medium.ttf") format("truetype");
}

:root {
  /* ============================================================
     DESIGN-0 (11/09/2026) — OS TOKENS CANÔNICOS. Casa única dos
     nomes novos; os nomes antigos, logo abaixo, viram ALIAS deles
     — "alias até a última tela migrar (DESIGN-0)". Contraste WCAG
     medido pelo Orchestrator no despacho desta fatia (luminância
     relativa, piso AA 4,5:1 para texto): text/bg 14,0 ·
     muted/surface 6,0 · primary/primary-soft 8,3 · branco/primary
     9,6 · compare/branco 7,2 · danger/branco 5,2 — passam. FALHAM:
     branco sobre accent 2,45 e warning como TEXTO sobre branco
     3,64. REGRA DE USO: o botão accent leva texto `--color-text`
     (6,08), nunca branco; `--color-warning` é cor de
     borda/ícone/fundo-soft — o TEXTO de aviso continua no tom
     escuro que a folha já tem (`--ambar` #7E6408, 4,5+), e por
     isso `--ambar` (o tom-texto) NÃO vira alias de `--color-warning`
     — só a borda e o fundo-soft dele viram. */
  --color-primary:      #0A4F36;
  --color-primary-soft: #E4F1EB;
  --color-compare:      #1857A4;  /* única cor de "segundo lado" */
  /* DESIGN-5: fundo-soft do lado B na tabela do Comparar -- mesmo hex que
     `--info-claro` já usava (ver o bloco do azul "B" mais abaixo), só
     declarado aqui perto do par para quem procura ao lado de `--color-
     -primary-soft`. Não é um terceiro token na prática: `--info-claro` segue
     sendo a definição, e este é alias dela. */
  --color-compare-soft: #EAF1FA;
  --color-danger:       #C53B32;
  --color-warning:      #B7791F;  /* borda/ícone/fundo-soft — nunca texto */
  --color-accent:       #F28C28;  /* SÓ a marca e o "+ Novo estudo" (DESIGN-0b: "Ver resultado" saiu do laranja) */
  --color-bg:           #F7F8F6;
  --color-surface:      #FFFFFF;
  --color-border:       #D7DED8;
  --color-text:         #1D2A22;
  --color-text-muted:   #5A665E;
  /* ---- DESIGN-0b (11/09/2026, errata à DESIGN-0) ----
     Hex medidos pela Mesa em 11/09 nos PNGs de referência (Pillow, contagem
     de pixels).
     `--color-primary-strong` é o verde dos botões de EXECUÇÃO ("Rodar
     sensibilidade", "Congelar versão…"); `--color-bar` é o fundo da faixa
     viva de totais; `--color-footer` é o rodapé de efeito sob ela;
     `--color-on-white-cta` é a letra do CTA branco (#btn-calcular) sobre
     fundo branco — contraste medido: branco sobre --color-primary-strong e
     sobre --color-bar passam (ambos mais escuros que --color-primary), e
     --color-on-white-cta sobre branco passa (mesma família tonal de
     --color-primary, mais escura). */
  --color-primary-strong: #004830;
  --color-bar:            #00402D;
  --color-footer:         #F5F7F6;
  --color-on-white-cta:   #01422E;
  /* -soft são tons de fundo DERIVADOS — reaproveitam o mesmo hex que
     `--vermelho-claro`/`--ambar-claro` já usavam, então a troca de
     nome não move um pixel do que já estava medido. */
  --color-danger-soft:  #FAEFEC;
  --color-warning-soft: #FAF2D2;

  /* espaçamento e raio — vocabulário novo, sem alias de token velho
     (esta folha não tinha escala de espaçamento antes da DESIGN-0) */
  --sp-1: 4px; --sp-2: 8px; --sp-3: 12px; --sp-4: 16px;
  --sp-5: 24px; --sp-6: 32px; --sp-7: 48px;
  --r-1: 6px; --r-2: 8px; --r-3: 12px;
  --shadow-card: 0 1px 2px rgba(16, 24, 40, .04), 0 4px 12px rgba(16, 24, 40, .06);

  /* tipografia — ver §0 acima para a Inter/Plex; `--font-sans` e
     `--font-mono` são os canônicos, `--display`/`--mono` (abaixo,
     no bloco de aliases) apontam para eles */
  --font-sans: "Inter Local", system-ui, -apple-system, "Segoe UI", Roboto,
               "Helvetica Neue", Arial, sans-serif;
  --font-mono: "Plex Local", ui-monospace, "Cascadia Mono", Consolas, monospace;

  /* ---------- superfícies e tinta (ALIAS até a última tela migrar — DESIGN-0) ---------- */
  --fundo: var(--color-bg);
  --superficie: var(--color-surface);
  --superficie-2: #f6f8f7;
  --tinta: var(--color-text);
  --tinta-2: var(--color-text-muted);
  --tinta-3: #93a09a;
  --linha: var(--color-border);

  /* ---------- verde do sistema (foco, sucesso, séries) ---------- */
  --verde: #0e6b49;
  --verde-escuro: var(--color-primary);       /* alias até a última tela migrar (DESIGN-0) */
  --verde-claro: var(--color-primary-soft);   /* alias até a última tela migrar (DESIGN-0) */
  --verde-borda: #c7ddd2;
  --verde-vivo: #6fd3a7;        /* sobre superfície escura */

  /* ---------- A PALETA DA MARCA ----------
     Desde a DESIGN-0, a marca e a ação principal ("Ver resultado")
     partilham UMA cor — `--color-accent` #F28C28 —, e é por isso
     que `--doma-laranja` deixou de ser o hex próprio e virou alias.
     O botão primário continua com as 6 variáveis próprias do
     `.btn-primario` (§1); este token é o que a logo/fio/acentos
     de identidade consomem. */
  --doma-laranja:       var(--color-accent); /* alias até a última tela migrar (DESIGN-0) */
  --doma-laranja-medio: #FFAC66;
  --doma-pessego:       #FFE3CC;
  --doma-preto:         #000000;
  --doma-branco:        #FFFFFF;

  /* derivados da marca, cada um com a medição que o obriga:
     #FF6700 mede 2,92:1 contra branco e o mínimo WCAG para
     elemento NÃO-textual (anel de foco, borda de estado) é 3,0.
     `--laranja-escuro` mede 4,14:1 e é usado SÓ onde a norma
     cobra contraste. Em superfície, o laranja é o da marca, puro. */
  --laranja-escuro:  #D94F00;
  --pessego-borda:   #FFCFA8;
  --pessego-fundo:   #FFF4EA;

  /* A COR DA AÇÃO não é token do `:root`: ela mora nas 6 variáveis
     do bloco `.btn-primario`, que é o único jeito legítimo de criar
     variante nesta família. Ver lá a mistura que a gera e a
     medição que a governa. */

  /* ---------- ALERTA — o âmbar novo, que é TEXTO e não só borda ----------
     O âmbar antigo (#b67a0f) FALHAVA como texto: 3,63:1 sobre
     branco e 3,38:1 sobre o próprio fundo, contra o piso AA de
     4,50. O novo mede 5,66:1 sobre branco e 5,03:1 sobre o
     próprio fundo — passa nos dois, que é o teste que importa,
     porque o âmbar é a cor do rótulo "Atenção".
     Separação da marca: ΔE2000 = 32,39 contra o #FF6700 (o
     âmbar antigo media 19,35). MARCA ≠ ALERTA por distância
     medida, e não por vigilância. */
  --ambar:        #7E6408;      /* NÃO é alias — texto de aviso; ver nota da DESIGN-0 acima */
  --ambar-claro:  var(--color-warning-soft); /* alias até a última tela migrar (DESIGN-0) */
  --ambar-borda:  var(--color-warning);      /* alias até a última tela migrar (DESIGN-0) */
  --ambar-vivo:   #e9b254;      /* sobre superfície escura */

  --vermelho: var(--color-danger);           /* alias até a última tela migrar (DESIGN-0) */
  --vermelho-claro: var(--color-danger-soft); /* alias até a última tela migrar (DESIGN-0) */
  --vermelho-borda: #e0b4ab;
  --vermelho-vivo: #f0917f;     /* sobre superfície escura */

  /* ---------- AZUL "B" (COMP-2, D-370) -- a ÚNICA cor de "segundo lado" ----------
     A comparação de estudos (aba Comparar) precisa de uma segunda cor viva
     que NÃO seja o verde do lado A (sucesso/foco) nem o âmbar/vermelho, que
     já são severidade. É a primeira cor nova do sistema desde o âmbar de
     23/08 -- e como aquela, ela carrega a MEDIÇÃO que a autoriza como texto:
     #1857A4 sobre branco mede 7,15:1 pela fórmula de luminância relativa do
     WCAG 2.1 (contraste = (L1+0,05)/(L2+0,05)), bem acima do piso AA de
     4,5:1. Nenhuma outra superfície do sistema usa este azul: ele é
     EXCLUSIVO do lado B da comparação -- reusar aqui é criar uma segunda
     leitura para a mesma cor, a família de defeito que esta casa já paga
     no laranja-marca × âmbar-alerta. */
  --info:        var(--color-compare); /* alias até a última tela migrar (DESIGN-0) */
  --info-escuro: #123F78;
  --info-claro:  #EAF1FA;
  --info-borda:  #B9D0EA;
  --info-vivo:   #8FB8E6;       /* sobre superfície escura */

  /* ---------- superfícies escuras (tarja, faixa viva) ----------
     `--escuro-1`/`--escuro-2`/`--escuro-tinta-2` MORRERAM aqui em 12/09/2026
     (DESIGN-4/PDF): eram só do gradiente do card preto do veredicto, que
     virou card claro — ver a lápide em `.veredicto-wrap`, `static/styles.css`.
     Conferido por grep antes de apagar: nenhuma outra regra os usava.
     `--escuro-3`/`--escuro-4`/`--escuro-linha`/`--escuro-tinta` CONTINUAM —
     `.btn-escuro` e `.dica::after` ainda os consomem. */
  --escuro-3: #1d2c26;
  --escuro-4: #2a3d35;
  --escuro-linha: #2c3b35;
  --escuro-tinta: #dfe7e3;
  --escuro-tinta-3: #7d8e87;

  --sombra: var(--shadow-card); /* alias até a última tela migrar (DESIGN-0) */

  /* RÉGUA DO SUBTÓPICO — o fio de 3px que liga um bloco de premissa ao
     cartão que o contém (`.bloco-aquisicao`). É a única peça cromática da
     hierarquia dos cartões aprovada em 03/08: o CARTÃO não tem barra
     nenhuma, e o subtópico tem esta.
     MEDIDA que a justifica: 1,76:1 contra o branco, contra 1,31:1 do
     `--linha` que desenha o resto da mesma borda. A régua tem de ser mais
     presente que a borda para ler como LIGAÇÃO e não como moldura, e é
     essa distância que faz o trabalho — não o matiz.
     Não precisa dos 3,0:1 da WCAG 1.4.11: ela não identifica componente
     nem estado, é o mesmo posto decorativo do `--linha`. */
  --regra-sub: #a7cbbb;

  /* rampa da composição do preço (medidor de vendas) — derivada do verde */
  --comp-sinal: #0a4f36;
  --comp-mensais: #0e6b49;
  --comp-baloes: #2f8a63;
  --comp-chaves: #5aa383;
  --comp-repasse-fin: #8bbda6;
  --comp-pro-soluto: #7E6408;
  /* Juros do pró-soluto: MESMO matiz do pró-soluto, mais claro. A relação de
     família é a mensagem — é o rendimento daquele saldo, não uma parcela nova
     do preço —, e a rampa verde não tem ouro nenhum para confundir com ela. */
  --comp-receita-fin: #C2A02B;
  --comp-residuo: #c6d3cd;

  /* `--mono` e `--display` são ALIAS de `--font-mono`/`--font-sans`
     (DESIGN-0, declarados no topo do :root) — nome próprio de
     propósito, para que "a tela está na Inter" só possa significar
     "os arquivos carregaram". O `check_fonte` resolve UM nível de
     `var()` antes de julgar a pilha. */
  --mono: var(--font-mono);       /* alias até a última tela migrar (DESIGN-0) */
  --display: var(--font-sans);    /* alias até a última tela migrar (DESIGN-0) */
  --raio: var(--r-3);             /* alias até a última tela migrar (DESIGN-0) — 12px, igual de sempre */

  /* altura da tarja: 56 do miolo + 1 da borda inferior = 57 (DESIGN-7 item 2:
     o fio de 3px do laranja morreu; quem separa agora é a borda `--linha`). */
  --h-tarja: 57px;

  /* ---------- TELA DE LOGIN (LOGIN-1, 25/09/2026) — direção 2 aprovada
     pela Mesa e pelo dono. Nomes vindos do `tokens.json` do design-codex;
     `--laranja-acao` é o ÚNICO dos quatro que já existia nesta folha —
     reaproveita `--color-accent` (#F28C28) em vez de duplicar o hex, e o
     PARECER de LOGIN-1 registra a divergência de nome (o design-codex chama
     de `--laranja-acao`, esta casa já tinha `--color-accent`). Os outros
     três (`--verde-profundo`/`--verde-base`/`--verde-luz`) são o fundo
     escuro do cartão de acesso e não existiam antes. */
  --verde-profundo: #0D251B;
  --verde-base:     #115A3F;
  --verde-luz:      #217553;
  --laranja-acao:   var(--color-accent);
}

* { box-sizing: border-box; }
/* scroll-padding-bottom (P-7, DIURNA-1 item 4d, 13/09/2026): sem ele o foco
   nativo (`block: nearest`) num input abaixo da dobra cai sob a faixa viva
   fixa, em qualquer largura -- a variável é a mesma que `medirFaixaViva`
   (app.js) grava no `<html>`, e no Resultado (faixa ausente) ela vale 0. */
html { scroll-behavior: smooth; scroll-padding-top: 120px; scroll-padding-bottom: calc(var(--faixa-altura, 0px) + 16px); }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }
html, body { margin: 0; }
body {
  font: 14px/1.55 var(--display);
  background: var(--fundo);
  color: var(--tinta);
  -webkit-text-size-adjust: 100%;
}
a { color: inherit; }

/* ------------------------------------------------------------
   GUARDA DO `hidden` — e ela não é zelo, é conserto.
   `[hidden]{display:none}` mora na folha do NAVEGADOR, que perde
   para QUALQUER regra de autor. Vários elementos desta folha
   declaram `display:flex` ou `display:grid`; sem esta linha, eles
   apareceriam MESMO com o atributo `hidden` posto. Já mordeu o
   `mockups/estudo.html` uma vez.
   ------------------------------------------------------------ */
[hidden] { display: none !important; }

/* ------------------------------------------------------------
   TROCA DE TELA — por atributo no <body>, nunca por `hidden`.
   Deliberado: `entradasInvalidas()` usa `el.closest("[hidden]")`
   para pular campo que não está na tela. Se a tela do estudo
   inteira fosse escondida com `hidden`, TODOS os campos dela
   passariam a ser "campo fora da tela" e a varredura de entradas
   ilegíveis ficaria muda — sem erro nenhum aparecer.
   ------------------------------------------------------------ */
[data-quando] { display: none; }
body[data-tela="home"]      [data-quando~="home"],
body[data-tela="estudo"]    [data-quando~="estudo"],
body[data-tela="macro"]     [data-quando~="macro"],
body[data-tela="glossario"] [data-quando~="glossario"] { display: block; }

/* MACRO-1 (14/09/2026): a tarja de `estudo` é COMPARTILHADA com `macro`, e o
   atributo `data-quando` dela CONTINUA sendo só `"estudo"` (o
   `check_troca.mjs` lê a tarja por casamento EXATO do valor,
   `[data-quando="estudo"]`, e um valor de dois tokens deixaria de casar).
   A visibilidade extra na tela `macro` é regra PRÓPRIA, presa à classe
   `.topo-fixo` — nunca ao atributo genérico —, então ela não arrasta o
   `<main data-quando="estudo">` (o conteúdo do estudo) junto. Dentro da
   tarja, `.tj-estudo`/`.tj-acoes` só fazem sentido dentro de um estudo
   aberto, e `.tj-macro-titulo` só faz sentido na tela do cenário macro —
   `display: flex` e não `block`: `.tj-estudo`/`.tj-acoes` já são flex no
   normal, e o `block` do mecanismo genérico desmancharia o alinhamento. */
body[data-tela="macro"] .topo-fixo[data-quando="estudo"] { display: block; }
.tj-macro-titulo { display: none; flex: 1 1 auto; min-width: 0; align-items: center; }
.tj-macro-titulo b {
  font: 700 15.5px/1.25 var(--display); letter-spacing: -0.01em; color: var(--tinta);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
body[data-tela="macro"] .tj-estudo,
body[data-tela="macro"] .tj-acoes { display: none; }
body[data-tela="macro"] .tj-macro-titulo { display: flex; }

/* AJUDA-1 (14/09/2026): a mesma tarja compartilhada, agora com uma terceira
   tela (glossário). Réplica exata da regra do cenário macro acima, mesma
   razão: `data-quando="estudo"` da tarja não muda de valor. */
body[data-tela="glossario"] .topo-fixo[data-quando="estudo"] { display: block; }
.tj-glossario-titulo { display: none; flex: 1 1 auto; min-width: 0; align-items: center; }
.tj-glossario-titulo b {
  font: 700 15.5px/1.25 var(--display); letter-spacing: -0.01em; color: var(--tinta);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
body[data-tela="glossario"] .tj-estudo,
body[data-tela="glossario"] .tj-acoes { display: none; }
body[data-tela="glossario"] .tj-glossario-titulo { display: flex; }

/* GLOSSÁRIO — a lista de verbetes, uma definição por `<dt>/<dd>`. */
.glossario-lista { margin: 0; }
.glossario-lista dt {
  font: 700 15px var(--display); color: var(--tinta); margin: 22px 0 4px;
  padding-top: 14px; border-top: 1px dashed var(--linha); scroll-margin-top: 76px;
}
.glossario-lista dt:first-of-type { margin-top: 0; padding-top: 0; border-top: 0; }
.glossario-lista dd { margin: 0 0 4px; font-size: 13.5px; line-height: 1.55; color: var(--tinta-2); }
.glossario-lista dd.glossario-no-doma { font-size: 12.5px; color: var(--tinta-3); }
.glossario-lista dd.glossario-no-doma b { color: var(--tinta-2); font-weight: 600; }

/* ============================================================
   1 · BOTÕES — COMPONENTE EM BLOCO ÚNICO
   ------------------------------------------------------------
   Este é o único lugar do sistema onde regra de botão mora. Se
   um `transform` ou um `box-shadow` de botão aparecer fora deste
   bloco, o componente virou cópia — que é a família de defeito
   que esta casa mais registrou (TETOS_FINANCIAMENTO,
   CATEGORIAS_PRECO).

   COMO ESTENDER — DOIS EIXOS, E SÓ DOIS
   1) EIXO PAPEL (`--btn-*`, declarado NO PRÓPRIO `.btn`): as 6
      variáveis que separam primário de secundário, de destrutivo,
      de escuro. Variante nova troca essas 6 e nada mais.
   2) EIXO ACABAMENTO (`--acab-*`, HERDADO de um contêiner): raio,
      respiro, peso, borda, brilho, opacidade das sombras. Muda o
      quanto o botão pesa na página sem mexer em quem ele é.

   O ACABAMENTO VIGENTE É O `Sereno`, e ele está escrito como
   DEFAULT nos pontos de uso — não como classe. Acabamento
   aprovado que continua vivendo como classe é convite a duas
   verdades. Sereno = elegância por subtração: sombra larga e
   baixa em vez de curta e densa, brilho interno quase apagado,
   canto de 12px falando a mesma língua do cartão (`--raio`).

   FÍSICA — É CONTRATO E NÃO MUDA:
   - repouso  → o botão "existe": borda inferior sólida
     (`--btn-altura`) mais uma sombra difusa por baixo;
   - hover    → sobe um degrau (-1px, altura maior);
   - :active  → AFUNDA 2px, altura 0 e sombra INTERNA.
   TEMPO: 60 ms para descer, 100 ms para voltar. Assimetria
   deliberada — a descida é o feedback do toque e precisa parecer
   instantânea (abaixo de ~80 ms o olho lê como imediato).

   TOQUE: `:active` no iOS só dispara com `touch-action:
   manipulation` — sem isso o botão não afunda no iPad, que é onde
   o dono usa. O hover fica trancado em ponteiro fino para não
   grudar depois do toque.

   DESABILITADO NÃO AFUNDA E NÃO TEM ELEVAÇÃO. Se afundasse,
   mentiria dizendo que o clique pegou; se tivesse elevação,
   mentiria dizendo que é clicável.
   ============================================================ */
.btn {
  /* --- EIXO PAPEL: as 6 variáveis que uma variante pode trocar --- */
  --btn-a: #ffffff;              /* topo do gradiente */
  --btn-b: #eef2f0;              /* base do gradiente */
  --btn-borda: #d3dbd7;
  --btn-tinta: var(--tinta);
  --btn-sombra: 19, 29, 25;      /* RGB cru da sombra, para compor alfas */
  --btn-altura: 2px;             /* espessura do botão em repouso */

  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;

  /* tipo em longhand, não no atalho `font`: o atalho reseta o que não for
     citado, e aqui cada eixo precisa poder ser trocado sozinho */
  font-family: var(--display);
  font-size: var(--btn-tam, var(--acab-tam, 13px));
  font-weight: var(--acab-peso, 600);
  line-height: 1;
  letter-spacing: var(--acab-track, 0.015em);

  padding: var(--btn-pad-v, var(--acab-pad-v, 12px))
           var(--btn-pad-h, var(--acab-pad-h, 20px));
  border-radius: var(--btn-raio, var(--acab-raio, 12px));
  border: var(--acab-borda-w, 1px) solid var(--btn-borda);
  background-color: var(--btn-b);
  /* o gradiente é knob: um acabamento chapado manda `--acab-fundo: none` e o
     que sobra é a cor sólida da base, sem tocar em quem é o botão */
  background-image: var(--acab-fundo,
                    linear-gradient(180deg, var(--btn-a), var(--btn-b)));
  color: var(--btn-tinta);
  cursor: pointer;
  text-decoration: none;
  white-space: nowrap;
  -webkit-user-select: none;
  user-select: none;
  touch-action: manipulation;                 /* :active responde ao toque */
  -webkit-tap-highlight-color: transparent;   /* sem flash cinza no iOS */
  transform: translateY(0);
  box-shadow:
    inset 0 1px 0 rgba(255, 255, 255, var(--acab-brilho, 0.26)),
    0 var(--btn-altura) 0 rgba(var(--btn-sombra), var(--acab-solida, 0.09)),
    0 var(--acab-difusa-y, 6px) var(--acab-difusa-r, 18px)
      rgba(var(--btn-sombra), var(--acab-difusa-a, 0.07));
  transition:
    transform 100ms cubic-bezier(0.2, 0.7, 0.3, 1),
    box-shadow 100ms cubic-bezier(0.2, 0.7, 0.3, 1),
    background-color 100ms ease,
    border-color 100ms ease,
    color 100ms ease;
}

/* HOVER — sobe um degrau. Trancado em ponteiro fino para não grudar no toque.
   Os deltas são ADITIVOS de propósito: num acabamento mais sutil o degrau de
   hover encolhe junto em vez de pular de volta para a escala antiga. */
@media (hover: hover) and (pointer: fine) {
  .btn:hover:not(:disabled):not([aria-disabled="true"]) {
    --btn-altura: 4px;
    transform: translateY(-1px);
    box-shadow:
      inset 0 1px 0 rgba(255, 255, 255, calc(var(--acab-brilho, 0.26) + 0.05)),
      0 var(--btn-altura) 0 rgba(var(--btn-sombra), calc(var(--acab-solida, 0.09) + 0.01)),
      0 calc(var(--acab-difusa-y, 6px) + 5px) calc(var(--acab-difusa-r, 18px) + 9px)
        rgba(var(--btn-sombra), calc(var(--acab-difusa-a, 0.07) * 1.6));
  }
  /* secundário: a marca EMOLDURA, não disputa — só a borda acende */
  .btn:not(.btn-primario):not(.btn-perigo):not(.btn-escuro):not(.btn-execucao):not(.btn-cta-branco):hover:not(:disabled) {
    --btn-borda: var(--doma-laranja-medio);
  }
}

/* CLIQUE — afunda. */
.btn:active:not(:disabled):not([aria-disabled="true"]) {
  --btn-altura: 0px;
  transform: translateY(2px);
  box-shadow:
    inset 0 2px 4px rgba(var(--btn-sombra), var(--acab-afundo, 0.17)),
    0 0 0 rgba(var(--btn-sombra), 0);
  transition-duration: 60ms;    /* desce rápido, sobe calmo */
  filter: brightness(0.97);
}

/* foco: anel do sistema (`--color-primary`) para todo botão comum -- o
   laranja fica reservado à marca e ao "+ Novo estudo" (DESIGN-7 item 6,
   D-384: "o laranja fica só na marca e nos dois '+ Novo estudo'"). */
.btn:focus-visible:not(.btn-primario),
.kebab:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
  box-shadow: 0 0 0 5px rgba(10, 79, 54, 0.20);
}
/* marca e "+ Novo estudo": o único par que segue com o anel no laranja
   escuro (4,14:1, o que a norma cobra para elemento não-textual) + halo
   do laranja da marca, que é decoração. */
.btn-primario:focus-visible,
.tj-logo:focus-visible {
  outline: 2px solid var(--laranja-escuro);
  outline-offset: 2px;
  box-shadow: 0 0 0 5px rgba(255, 103, 0, 0.30);
}

/* ---------- PRIMÁRIO — A AÇÃO. E este bloco é o DONO dela ----------
   Não há token de "cor da ação" no `:root`: o dono é este bloco, com as 6
   variáveis de papel. `#FF8A33` aparece UMA vez em toda a folha, e é aqui.

   DE ONDE VEM O TOM. Mistura 50/50 do laranja da MARCA (#FF6700) com o laranja
   médio da logo (#FFAC66), média aritmética por canal:
       R (255+255)/2 = 255    -> FF
       G (103+172)/2 = 137,5  -> 8A
       B   (0+102)/2 = 51     -> 33
   A marca continua sendo #FF6700 onde ela É a marca — logo, fio do topo,
   favicon, acento de identidade. O botão usa um tom DERIVADO. Com isso a casa
   passa a ter MARCA ≠ AÇÃO ≠ ALERTA em três tons distintos, por estrutura.

   DE ONDE VEM A BASE DO GRADIENTE, e ela NÃO foi escolhida a olho: é o MESMO
   escurecimento proporcional que levava #FF6700 a #E85800, aplicado ao tom
   novo — R × (232/255) e G × (88/103), com o azul seguindo a razão do verde
   (o azul do original era 0 e não dá razão nenhuma):
       R 255 × 0,90980 = 232,0  -> E8
       G 138 × 0,85437 = 117,9  -> 76
       B  51 × 0,85437 =  43,6  -> 2C     =>  #E8762C
   O gradiente fica: é ele que dá o VOLUME da família, e volume é contrato
   aprovado — a física e o acabamento Sereno foram martelados COM ele.

   MEDIÇÃO, texto PRETO (o P3 não foi revogado; mudou só qual laranja):
       preto sobre #FF8A33 (topo) .............. 8,94:1
       preto sobre #E8762C (base) .............. 7,06:1   <== É ESTE QUE GOVERNA
       [hoje] preto sobre #E85800 (base) ....... 5,83:1   <- o que governava
       branco sobre #E8762C .................... 2,97:1   REPROVA
   D-125: com texto ESCURO sobre gradiente claro, quem governa é a ponta mais
   ESCURA. O 8,94 é a ponta BOA — publicar a ponta boa foi exatamente o defeito
   que gerou o D-125. O número desta variante é 7,06, e ele MELHORA o que
   governava hoje (5,83 -> 7,06): mais claro e mais legível ao mesmo tempo.

   BORDA E SOMBRA reharmonizadas pela MESMA regra, não a olho: a borda é a base
   pelo passo base->borda de hoje (194/232 e 72/88), o que dá #C26124 — 3,53:1
   contra o fundo da página, acima do piso de 3,0 que a norma cobra para
   elemento não-textual. A sombra é a borda pelo passo borda->sombra de hoje
   (148/194 e 56/72), o que dá 148, 75, 28.

   HOVER: os mesmos deltas do hover de hoje, aplicados aos dois stops. Ele
   CLAREIA, nunca escurece, e por isso o repouso segue sendo o pior caso —
   no hover a ponta que governa mede 7,75:1.

   Reversível numa linha se o dono preferir chapado: bastam `--btn-a` e
   `--btn-b` com o mesmo valor, e aí o número que governa passa a ser 8,94. */
.btn-primario {
  --btn-a: #FF8A33;
  --btn-b: #E8762C;
  --btn-borda: #C26124;
  --btn-tinta: var(--doma-preto);
  --btn-sombra: 148, 75, 28;
  --btn-altura: 3px;            /* pesa mais na página: é a ação principal */
  font-weight: 700;             /* peso forte por legibilidade */
}
@media (hover: hover) and (pointer: fine) {
  .btn-primario:hover:not(:disabled) { --btn-a: #FF9D52; --btn-b: #F27D2C; }
}

/* ---------- DESTRUTIVO — distinto por COR, física idêntica ---------- */
.btn-perigo {
  --btn-a: #c04a37;
  --btn-b: #a63b2a;
  --btn-borda: #8b3122;
  --btn-tinta: #ffffff;
  --btn-sombra: 92, 32, 22;
  --btn-altura: 3px;
}
@media (hover: hover) and (pointer: fine) {
  .btn-perigo:hover:not(:disabled) { --btn-a: #cf5340; --btn-b: #b44230; }
}

/* ---------- SOBRE FUNDO ESCURO (tarja, faixa viva) ---------- */
.btn-escuro {
  --btn-a: var(--escuro-4);
  --btn-b: var(--escuro-3);
  --btn-borda: #3c4f46;
  --btn-tinta: var(--escuro-tinta);
  --btn-sombra: 0, 0, 0;
}

/* ---------- EXECUÇÃO (DESIGN-0b) — chapado em `--color-primary-strong`,
   texto branco 700, raio 8, sombra leve. `--btn-a` e `--btn-b` iguais fazem
   o gradiente da base virar cor sólida sem tocar no mixin. Usada nos botões
   que RODAM algo: "Rodar sensibilidade" (#btn-sens) e "Congelar versão…"
   (#btn-versao-congelar). ---------- */
.btn-execucao {
  --btn-a: var(--color-primary-strong);
  --btn-b: var(--color-primary-strong);
  --btn-borda: #00301F;
  --btn-tinta: var(--doma-branco);
  --btn-sombra: 0, 32, 21;
  --btn-raio: 8px;
  font-weight: 700;
}

/* ---------- CTA BRANCO SOBRE FUNDO ESCURO (DESIGN-0b) — só o
   `#btn-calcular` ("Ver resultado →"), que saiu do laranja: fundo branco,
   letra `--color-on-white-cta`, raio 8. ---------- */
.btn-cta-branco {
  --btn-a: var(--doma-branco);
  --btn-b: var(--doma-branco);
  --btn-borda: var(--doma-branco);
  --btn-tinta: var(--color-on-white-cta);
  --btn-sombra: 0, 0, 0;
  --btn-raio: 8px;
  font-weight: 700;
}

/* ---------- DESABILITADO — sem elevação e sem afundar ---------- */
.btn:disabled,
.btn[aria-disabled="true"] {
  --btn-a: #f1f4f2;
  --btn-b: #e9eeeb;
  --btn-borda: #dde4e0;
  --btn-tinta: #a3aeaa;
  --btn-altura: 0px;
  cursor: not-allowed;
  filter: none;
  transform: none;
  box-shadow: none;
  /* opacidade global fica de fora de propósito: apagaria o texto junto, e o
     rótulo do botão precisa continuar legível para o usuário entender o que
     está bloqueado. */
}
.btn:disabled:active,
.btn[aria-disabled="true"]:active {
  transform: none;              /* a mentira que este bloco existe para evitar */
  box-shadow: none;
  filter: none;
}

/* ---------- TAMANHOS ----------
   Cada tamanho é um DESVIO do acabamento vigente, não um valor fixo: por isso
   `calc` em cima do knob e nunca um número solto. O nome lido pelo `.btn`
   (`--btn-pad-v`) é diferente do escrito pelo acabamento (`--acab-pad-v`)
   porque uma variável não pode se referenciar — seria ciclo e o CSS descarta. */
.btn-mini {
  --btn-pad-v: calc(var(--acab-pad-v, 12px) - 4px);
  --btn-pad-h: calc(var(--acab-pad-h, 20px) - 6px);
  --btn-tam:   calc(var(--acab-tam, 13px) - 1px);
  --btn-raio:  calc(var(--acab-raio, 12px) - 2px);
}
.btn-grande {
  --btn-pad-v: calc(var(--acab-pad-v, 12px) + 3px);
  --btn-pad-h: calc(var(--acab-pad-h, 20px) + 6px);
  --btn-tam:   calc(var(--acab-tam, 13px) + 1px);
  --btn-raio:  calc(var(--acab-raio, 12px) + 1px);
}

/* ---------- MOVIMENTO REDUZIDO ----------
   O afundar É feedback funcional, não enfeite: 2px não dispara desconforto
   vestibular. O que sai é a transição — o estado troca sem interpolação. */
@media (prefers-reduced-motion: reduce) {
  .btn { transition: none; }
  .btn:active:not(:disabled) { transition: none; }
}

/* ============================================================
   2 · A TARJA — 57px, nas DUAS telas
   ------------------------------------------------------------
   56px de miolo + 1px de borda inferior = 57 (DESIGN-7 item 2: fundo
   BRANCO, sem o fio de 3px do laranja -- quem separa agora é a borda
   `--linha`, do próprio `.tj`).

   home    · wordmark inteiro + título do sistema + busca
             permanente + ação primária;
   interna · a marca REDUZIDA ao símbolo "+" (que é o VOLTAR),
             a troca rápida "Empreendimentos ▾", o nome do
             empreendimento no lugar de honra, e SEM título do
             sistema.

   POR QUE A INTERNA NÃO TEM TÍTULO, e isto evita um erro futuro:
   não é falta de espaço — é HIERARQUIA. Na interna o nome do
   empreendimento é o lugar de honra, e ponto. No dia em que
   alguém alargar a tarja e pensar "agora cabe o título", NÃO
   CABE VOLTAR, porque nunca foi aperto de largura.

   A física de clique não mora aqui: todo botão da tarja é `.btn`
   e herda os 60/100 ms do bloco 1.
   ============================================================ */
.topo-fixo { position: sticky; top: 0; z-index: 40; }
/* DESIGN-7 item 2: fundo BRANCO com borda inferior de 1px -- a tarja deixa
   de ser a superfície escura da marca. O laranja do `.fio-marca` (3px de
   gradiente) morreu junto; a marca sai do wordmark preto/verde e dos dois
   "+ Novo estudo". */
.tj { position: relative; display: block; width: 100%; background: var(--superficie); border-bottom: 1px solid var(--linha); }
.tj-in {
  max-width: 1180px; margin: 0 auto; padding: 0 28px;
  height: 56px;
  display: flex; align-items: center; gap: 12px; min-width: 0;
}

/* a logo, em <img>. `height` fixo e `width:auto`: os arquivos têm proporções
   DIFERENTES entre si — travar largura distorceria um deles em silêncio. */
.tj-logo { display: inline-flex; align-items: center; gap: 10px; text-decoration: none; flex-shrink: 0; }
.tj-logo img { display: block; width: auto; }
.tj-logo .word { height: 24px; }
.tj-logo .simb { height: 26px; }
.tj-logo:focus-visible { border-radius: 4px; }

/* O TÍTULO DO SISTEMA — SÓ NA HOME.
   A logo é a MARCA; o título diz QUAL SISTEMA é, porque haverá outros sistemas
   sob a mesma marca. É identificação de sistema, não legenda — e por isso
   `flex-shrink: 0`: este elemento nunca encolhe em silêncio. Aparece inteiro
   ou o conflito vai para a mesa.
   O conjunto lê "DOMA+ VIABILIDADE" porque quem escreve o "DOMA+" é a própria
   wordmark, desenhada ao lado. Repetir "DOMA+" em texto colado numa imagem que
   já diz "DOMA+" poria a marca duas vezes no mesmo bloco de 200px. */
.tj-titulo {
  font: 700 10px var(--display);
  letter-spacing: 0.22em;
  text-transform: uppercase;
  white-space: nowrap;
  padding-left: 11px;
  color: var(--tinta-2);
  border-left: 1px solid var(--linha);
}

.tj-espaco { margin-left: auto; }
.tj-acoes { display: flex; align-items: center; gap: 10px; flex-shrink: 0; }
.tj-div { width: 1px; height: 26px; flex-shrink: 0; background: var(--linha); }

/* identidade do estudo dentro da tarja — o lugar de honra da tela interna.

   O `align-self: stretch` + `overflow: hidden` NÃO é acabamento: é a trava que
   faltava em R1. Este é o único bloco da tarja com conteúdo VINDO DO BANCO
   (nome e endereço do estudo), e a tarja é `height: 56px` com `overflow`
   visível dentro de um `.topo-fixo` de `z-index: 40`. Qualquer coisa que
   engorde este bloco não estoura layout nenhum: ela transborda para BAIXO da
   tarja e passa por cima da barra de abas (z-index 30) como uma placa
   transparente, comendo o clique sem pintar um pixel. Foi exatamente o que
   aconteceu (ver o comentário de `.painel > .vazio`, §7).
   Com `stretch`, este bloco é o tamanho da tarja e NUNCA mais do que isso; o
   `justify-content: center` continua centrando as duas linhas, então nada
   muda à vista. Não dá para pôr a trava no `.tj-in`, que precisa deixar o
   menu do kebab e a dica do Exportar ESCAPAREM da tarja. */
/* DESIGN-0: mais largura (cresce até 520px em vez de shrink-to-fit) e o slot
   da miniatura, à esquerda. `padding-left` abre o vão sem tocar no `<b>`/
   `<span>` internos, que continuam empilhados em coluna.
   DESIGN-1: o slot deixou de ser `::before` (não aceitava `background-image`
   nem texto) e virou o elemento real `#tj-mini`, preenchido por
   `atualizarMiniaturaDoTopo` -- foto quando há, monograma quando não há. */
.tj-estudo {
  position: relative; min-width: 0; flex: 1 1 auto; max-width: 520px;
  padding-left: 46px;
  display: flex; flex-direction: column; justify-content: center;
  align-self: stretch; overflow: hidden;
}
/* DESIGN-7 item 2: monograma corrigido para o par que o resto da folha usa
   com este mesmo fundo (`.passo.ativo`, `.sens-pilula-ganho`, `.carimbo-ok`,
   `.cmp-tabela` -- todos `--color-primary-soft` + `--color-primary`). Branco
   sobre este verde claríssimo já era ilegível na tarja escura; sobre a tarja
   branca da DESIGN-7 ficaria pior. */
.tj-mini {
  position: absolute; left: 0; top: 50%; margin-top: -18px;
  width: 36px; height: 36px; border-radius: var(--r-2);
  background: var(--color-primary-soft) center/cover no-repeat;
  display: flex; align-items: center; justify-content: center;
  font: 800 13px var(--display); color: var(--color-primary); letter-spacing: 0.01em;
}
.tj-estudo b {
  font: 700 15.5px/1.25 var(--display); letter-spacing: -0.01em; color: var(--tinta);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block;
}
.tj-estudo span {
  font: 400 11.5px/1.3 var(--display); color: var(--tinta-2);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: block;
}
.tj-estudo span.vazio { color: var(--tinta-3); font-style: italic; }

/* ------------------------------------------------------------
   TROCA RÁPIDA — o rótulo "Empreendimentos ▾" da tarja interna.

   Era um link de VOLTAR, e voltar já é o que o símbolo da marca
   ao lado faz: dois controles para um destino só. Agora ele abre
   a lista e troca de estudo direto (R2.1).

   O `margin-left:-8px` que este componente teria para alinhamento
   óptico é ZERO aqui de propósito: ele vem logo depois do símbolo
   "+", e o recuo puxaria a caixa de hover para 4px do glifo da
   marca. Área de respiro de marca não é folga — é a marca.

   NADA DE LARANJA DE AÇÃO: isto é navegação, e navegação não
   compete com o primário. LÁPIDE (DESIGN-7 item 6, D-384): o anel de
   foco daqui já foi laranja "por ser o mesmo de toda a casa" -- deixou
   de ser; o laranja fica só na marca e no "+ Novo estudo", e este bloco
   passou a usar o anel do sistema (`--color-primary`).
   ------------------------------------------------------------ */
.tj-troca {
  display: inline-flex; align-items: center; gap: 7px; flex-shrink: 0;
  font: 600 12.5px var(--display); color: var(--tinta-2);
  background: transparent; border: 1px solid transparent; cursor: pointer;
  padding: 7px 10px 7px 9px; border-radius: 8px;
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.tj-troca i {
  font-style: normal; font-size: 10px; line-height: 1;
  color: var(--tinta-3); transition: color 0.12s;
}
.tj-troca:hover, .tj-troca[aria-expanded="true"] {
  background: var(--superficie-2); color: var(--tinta);
}
.tj-troca:hover i, .tj-troca[aria-expanded="true"] i { color: var(--tinta); }
.tj-troca[aria-expanded="true"] { border-color: var(--linha); }
.tj-troca:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 1px; }

/* O invólucro é a âncora do painel. `align-self: stretch` sem conteúdo em
   fluxo além do botão: o painel é ABSOLUTO e não empurra nada, então este
   bloco nunca engorda a tarja — a armadilha da placa transparente (§7) mora
   em bloco que CRESCE em fluxo, e aqui não há um. */
.troca-wrap {
  position: relative; flex-shrink: 0; align-self: stretch;
  display: flex; align-items: center;
}

/* O painel herda `.menu` (superfície clara, sombra, raio) e só muda a âncora:
   nasce colado à esquerda e abaixo do fio da marca, nunca por cima dele.
   O seletor é `.menu.troca` e isso NÃO é enfeite: `.menu` mora depois nesta
   folha e, com a mesma especificidade, o `top:34px; right:0` dele venceria por
   ordem — o painel abriria colado na borda ERRADA da tarja. */
.menu.troca {
  left: 0; right: auto; top: calc(100% + 3px);
  min-width: 320px; max-width: 420px; padding: 5px;
}
.troca-rol { max-height: min(58vh, 420px); overflow-y: auto; overscroll-behavior: contain; }

/* A LINHA: nome à esquerda, VGV líquido à direita, em mono e tabular — é a
   mesma medida e o mesmo formato do cartão da home, de propósito. Nome longo
   corta com reticências; o número NUNCA encolhe nem quebra. */
.troca-item {
  display: flex; align-items: center; gap: 10px; text-decoration: none;
  padding: 8px 10px; border-radius: 7px; color: var(--tinta);
  touch-action: manipulation;
}
.troca-item b {
  font: 600 13px var(--display); color: var(--tinta);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; flex: 1;
}
/* B4 (DOBRA, D-263, D-401): piso de 12px -- não há por que este numérico
   fosse o único abaixo da régua; `.troca-item b` já cede largura (ellipsis)
   antes de precisar cortar o VGV. */
.troca-item .troca-vgv {
  font: 500 12px var(--mono); font-variant-numeric: tabular-nums;
  color: var(--tinta-2); flex-shrink: 0; white-space: nowrap;
}
.troca-item:hover { background: var(--superficie-2); }
.troca-item:focus-visible { outline: 2px solid var(--color-primary); outline-offset: -2px; }
/* o estudo aberto agora: fica na lista para o usuário se situar, marcado por
   fundo e por `aria-current` — nunca só por cor */
.troca-item.atual { background: var(--verde-claro); }
.troca-item.atual b { color: var(--verde-escuro); }
.troca-item .marca-atual {
  font: 700 8.5px var(--display); letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--verde); flex-shrink: 0;
}
/* Estado de texto do painel — carregando, lista vazia, falha ao listar.
   NÃO usa `.vazio`: aquele nome tem dois papéis nesta folha (§7) e é
   exatamente o par que já cresceu uma placa transparente por cima das abas. */
.troca-nota {
  margin: 0; padding: 10px; font: 400 12.5px var(--display); color: var(--tinta-2);
}
.troca-todos {
  display: block; margin-top: 5px; padding: 9px 10px;
  border-top: 1px solid var(--linha); text-decoration: none;
  font: 600 12.5px var(--display); color: var(--verde);
}
.troca-todos:hover { background: var(--verde-claro); }
.troca-todos:focus-visible { outline: 2px solid var(--color-primary); outline-offset: -2px; }

/* MACRO-1 (14/09/2026): a segunda entrada da tela do cenário macro, dentro
   do mesmo menu "Empreendimentos ▾" -- tom neutro (`--tinta`), porque não é
   a ação de voltar (essa continua verde, em `.troca-todos`): é navegação
   para uma configuração da empresa. */
.troca-macro {
  display: block; margin-top: 5px; padding: 9px 10px;
  border-top: 1px solid var(--linha); text-decoration: none;
  font: 600 12.5px var(--display); color: var(--tinta);
}
.troca-macro:hover { background: var(--superficie-2); }
.troca-macro:focus-visible { outline: 2px solid var(--color-primary); outline-offset: -2px; }

/* AJUDA-1 (14/09/2026): a terceira entrada do mesmo menu, mesma roupa da
   `.troca-macro` (tom neutro, é navegação e não gravação). */
.troca-glossario {
  display: block; margin-top: 5px; padding: 9px 10px;
  border-top: 1px solid var(--linha); text-decoration: none;
  font: 600 12.5px var(--display); color: var(--tinta);
}
.troca-glossario:hover { background: var(--superficie-2); }
.troca-glossario:focus-visible { outline: 2px solid var(--color-primary); outline-offset: -2px; }

/* busca permanente da home. DESIGN-7 item 2: fundo claro `--superficie-2`
   (a folha não tem um token `--fundo-2` -- este é quem já faz esse papel em
   toda a casa, ver `.troca-item:hover`, `.menu button:hover`) e borda
   `--linha`, texto em `--tinta`. */
.tj-busca {
  display: flex; align-items: center; gap: 8px;
  border-radius: 9px; padding: 0 12px; height: 38px; min-width: 0;
  background: var(--superficie-2); border: 1px solid var(--linha);
  width: min(340px, 38%);
}
.tj-busca input {
  border: 0; background: transparent; outline: none; color: var(--tinta);
  font: 400 13px var(--display); width: 100%; min-width: 0; padding: 0;
}
.tj-busca input::placeholder { color: var(--tinta-3); }
.tj-busca i { font-style: normal; font-size: 13px; opacity: 0.7; color: var(--tinta-2); }
.tj-busca:focus-within { border-color: var(--color-primary); box-shadow: 0 0 0 3px rgba(10,79,54,0.14); }

/* LÁPIDE DO FIO DA MARCA (DESIGN-7 item 2, 12/09/2026). Era 3px de gradiente
   laranja embaixo da tarja -- morreu com a tarja escura: a borda `--linha`
   de 1px do `.tj` faz esse trabalho agora. As duas `<div class="fio-marca">`
   saíram do index.html; a classe não é mais usada. */

/* ------------------------------------------------------------
   DESABILITADO NA TARJA — DESIGN-7 item 2: a tarja deixou de ser escura,
   então o desabilitado do bloco 1 (#f1f4f2 / #a3aeaa, fundo CLARO) já serve
   sem override de cor -- este seletor mais específico fica só com o
   `pointer-events`, que é FÍSICA e não cor.
   ------------------------------------------------------------ */
.tj .btn:disabled,
.tj .btn[aria-disabled="true"] {
  /* deixa o clique atravessar para o invólucro da dica: navegador nenhum
     dispara evento de ponteiro em controle desabilitado, e sem isto a dica
     seria inalcançável no toque — que é justamente onde ela mais falta. */
  pointer-events: none;
}

/* ---- a dica do botão bloqueado ----
   No iPad NÃO EXISTE hover, então `title=""` nativo nunca aparece lá: a dica
   precisa de um caminho por TOQUE ou é decoração de desktop. O invólucro abre
   por hover (ponteiro fino) e por toque (classe `.dica-vista`, ligada no
   clique). O `title` fica no botão assim mesmo, para leitor de tela e para o
   desktop de quem não toca. */
.dica { position: relative; display: inline-flex; cursor: not-allowed; }
.dica::after {
  content: attr(data-dica);
  position: absolute; top: calc(100% + 9px); right: 0; z-index: 60;
  background: #0b1210; color: var(--escuro-tinta); border: 1px solid var(--escuro-linha);
  border-radius: 7px; padding: 6px 10px;
  font: 600 11.5px var(--display); white-space: nowrap;
  box-shadow: 0 8px 20px rgba(0,0,0,0.45);
  opacity: 0; transform: translateY(-3px); pointer-events: none;
  transition: opacity 120ms ease, transform 120ms ease;
}
.dica.dica-vista::after { opacity: 1; transform: translateY(0); }
@media (hover: hover) and (pointer: fine) {
  .dica:hover::after { opacity: 1; transform: translateY(0); }
}
@media (prefers-reduced-motion: reduce) { .dica::after { transition: none; } }

/* ============================================================
   3 · O INDICADOR DE GRAVAÇÃO (D-126)
   ------------------------------------------------------------
   A REGRA, que é contrato do auto-save e não estilo:

     O sistema atualiza e grava tudo a cada nova inserção de
     dados. Gravar é FUNCIONAMENTO NORMAL, e funcionamento normal
     é SILENCIOSO.

   O indicador só existe quando algo IMPEDE o normal, e são DOIS
   estados, só:
     (a) "Pendências · não salvo.": estado inválido NUNCA
         sobrescreve o último válido;
     (b) "Falha ao gravar.": com causa e reagendamento (D-074 —
         reporta e reagenda, nunca perde digitação em silêncio).

   FORA ISSO, NADA NA TELA. Nem transitório. Nem "gravado".
   Não existe `.salvo` e não existe `.gravando` nesta folha, e a
   ausência é a especificação: um transitório que pisca a cada
   650 ms de debounce é ruído intermitente, e movimento no canto
   da tela captura o olho sem ter o que dizer. A confiança vem do
   COMPORTAMENTO (nunca perder dado), não de anúncio.

   ESTADOS-1, item 2 (12/09/2026, prancha 15) TROCOU O CORTE PELA QUEBRA. A
   causa passou a carregar a PRIMEIRA pendência por inteiro — não mais só a
   contagem —, e frase inteira com `overflow: hidden` + `ellipsis` de linha
   única é a mesma classe de defeito que a E1 já baniu da tela (aparar é
   perder a causa). O corpo (`#ind-grav-txt` + `#ind-grav-causa`, dentro de um
   `<span class="ind-grav-corpo">`) agora QUEBRA como prosa; só o `max-width`
   do selo segue limitando a largura na tarja. */
.ind-grav {
  display: inline-flex; align-items: flex-start; gap: 8px;
  padding: 7px 13px 7px 11px; border-radius: 14px;
  border: 1px solid transparent;
  font: 600 11.5px var(--display);
  max-width: 380px;
}
.ind-grav i { width: 7px; height: 7px; border-radius: 50%; flex-shrink: 0; font-style: normal; margin-top: 4px; }
.ind-grav-corpo { min-width: 0; line-height: 1.45; }
/* R1 (item 9, D-263): 11px -> 12px nos dois — a causa agora pode carregar
   dígitos ("2 itens a corrigir."), e a fonte pequena demais é a MESMA classe
   de defeito que a B1 conserta um degrau acima (frase difícil de ler). */
.ind-grav em { font: 400 12px var(--mono); font-style: normal; color: inherit; opacity: 0.78; }
/* O LINK/BOTÃO "Ir ao campo →"/"Ir à seção →" (ESTADOS-1, item 2): mesma
   tinta do estado (herda `color: inherit`), sublinhado para não depender só
   da cor, e sem moldura de botão -- ele lê como parte da frase, com uma ação
   no fim dela. */
.ind-grav-link {
  font: 700 12px var(--display); text-decoration: underline; color: inherit;
  background: none; border: 0; padding: 0; cursor: pointer; touch-action: manipulation;
}
.ind-grav-link:hover, .ind-grav-link:focus-visible { opacity: 0.82; }
/* (a) retido — âmbar. Nunca pêssego: pêssego é interação (P4). */
.ind-grav.retido { background: var(--ambar-claro); border-color: var(--ambar-borda); color: var(--ambar); }
.ind-grav.retido i { background: var(--ambar); }
/* (b) falhou — vermelho, com a causa e o reagendamento no <em> */
.ind-grav.falhou { background: var(--vermelho-claro); border-color: var(--vermelho-borda); color: var(--vermelho); }
.ind-grav.falhou i { background: var(--vermelho); }

/* ============================================================
   4 · HOME — grade de cartões (sem sidebar)
   ============================================================ */
.pagina { max-width: 1180px; margin: 0 auto; padding: 30px 28px 90px; }
/* D2 (DESIGN-7/ADENDO) -- o respiro deixa de ser um número cravado (120px
   fixos, independente da aba e da largura da janela) e passa a seguir a
   altura REAL da faixa viva, medida por `medirFaixaViva` (app.js) e gravada
   em `--faixa-altura` no <html> (P-7, DIURNA-1 item 4d: desde então a
   variável mora no documentElement e não mais no <body>, porque o
   `scroll-padding-bottom` só vale no elemento de rolagem -- o <body>
   continua enxergando o mesmo valor, por herança). Sem faixa visível (aba
   Resultado) a variável vale 0 e o respiro cai para os 32px de folga --
   nunca sobra espaço morto, e o último card nunca fica por baixo da faixa. */
body[data-tela="estudo"] .pagina { padding-bottom: calc(var(--faixa-altura, 0px) + 32px); }

.cabecalho-pagina { display: flex; align-items: flex-end; gap: 18px; flex-wrap: wrap; margin-bottom: 22px; }
.cabecalho-pagina h1 { font: 800 30px/1.15 var(--display); letter-spacing: -0.015em; margin: 0; }
.cabecalho-pagina p { margin: 6px 0 0; color: var(--tinta-2); font-size: 13.5px; }
/* "Carregado em DD/MM HH:MM" (HOME-2 item 4) — canto oposto ao título, mesma
   trava de `margin-left: auto` que `.emp-editado` já usa no rodapé do
   cartão. Fica em branco (some visualmente) antes da primeira listagem. */
.home-atualizado {
  /* 12px, não 11,5 (dobra do item 10, A4): mono com dígito é número, e
     número desta casa não desce de 12px. */
  margin-left: auto; font: 500 12px var(--mono); color: var(--tinta-2); white-space: nowrap;
}

/* CHIPS DE FILTRO DA HOME (HOME-2 item 3) — contagem de cartões por ESTADO
   JÁ PUBLICADO, nunca ranking; a nota ao lado repete por escrito o que a
   interação já cumpre: filtrar não reordena (`_listaCache` continua por
   lançamento, ver `cartaoDeveAparecer`/`renderHome` no app.js). */
.home-filtros {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin: 0 0 16px; flex-wrap: wrap;
}
.home-filtros-chips { display: flex; gap: 8px; flex-wrap: wrap; }
.home-filtro {
  font: 700 12px var(--display); padding: 7px 14px; border-radius: 999px;
  border: 1px solid #dfe5e2; background: var(--superficie); color: var(--tinta-2);
  cursor: pointer;
}
.home-filtro:hover { border-color: var(--pessego-borda); }
.home-filtro.ativo {
  border-color: var(--color-primary); background: var(--color-primary-soft); color: var(--color-primary);
}
.home-filtros-nota { font: 500 11.5px var(--display); color: var(--tinta-3); }

/* DESIGN-1: "2 por linha" é decisão do dono, e não um `auto-fill` que
   decide sozinho quantas colunas cabem. Abaixo de 1100px vira 1 coluna --
   é o ponto em que 320px × 2 + o vão de 16px deixa de caber com conforto. */
.grade-emp { display: grid; gap: 16px; grid-template-columns: 1fr; }
@media (min-width: 1100px) {
  .grade-emp { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.emp {
  position: relative;
  background: var(--superficie); border: 1px solid #dfe5e2;
  border-radius: var(--raio);
  padding: 18px 20px 16px;
  box-shadow: 0 2px 0 rgba(19, 29, 25, 0.07), 0 4px 14px rgba(19, 29, 25, 0.06);
  transform: translateY(0);
  transition:
    transform 100ms cubic-bezier(0.2, 0.7, 0.3, 1),
    box-shadow 100ms cubic-bezier(0.2, 0.7, 0.3, 1),
    background-color 100ms ease, border-color 100ms ease;
}
/* o cartão fala a mesma língua do botão, na mesma escala de tempo. O `:has()`
   é o que permite o cartão INTEIRO afundar quando o link interno é
   pressionado — sem ele só o link afundaria e o cartão pareceria quebrado. */
@media (hover: hover) and (pointer: fine) {
  .emp:hover {
    background: var(--doma-pessego);
    border-color: var(--pessego-borda);
    transform: translateY(-1px);
    box-shadow: 0 4px 0 rgba(148, 56, 0, 0.10), 0 10px 22px rgba(19, 29, 25, 0.12);
  }
  /* o pêssego EMOLDURA, não tinge o dado: as células de número recuperam
     superfície branca no hover (DESIGN-1: cada KPI é célula própria agora) */
  .emp:hover .emp-nums > div { background: rgba(255, 255, 255, 0.72); }
}
.emp:has(.emp-link:active) {
  transform: translateY(2px);
  border-color: var(--pessego-borda);
  background: var(--doma-pessego);
  box-shadow: inset 0 2px 5px rgba(148, 56, 0, 0.18);
  transition-duration: 60ms;
}
.emp:focus-within { border-color: var(--color-primary); }

/* CARD TRACEJADO — "Novo estudo / ou duplicar um existente" (DESIGN-1),
   no fim da grade. CLASSE PRÓPRIA, DE PROPÓSITO: `.emp` é o que vários
   harnesses contam como "quantos estudos existem" (check_ciclo,
   check_autosave...), e este card não é um estudo. Como filho de
   `.grade-emp` (display:grid) ele já ganha a largura da coluna sozinho. */
.emp-novo {
  position: relative; border-radius: var(--raio);
  background: transparent; border: 2px dashed #c8d3ce; box-shadow: none;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px; min-height: 220px; padding: 32px 20px; cursor: pointer;
  font: inherit; width: 100%; color: inherit;
  transition: background-color 100ms ease, border-color 100ms ease;
}
.emp-novo:hover { border-color: var(--pessego-borda); background: var(--doma-pessego); }
@media (prefers-reduced-motion: reduce) { .emp-novo { transition: none; } }
.emp-novo-titulo { font: 800 16px var(--display); color: var(--doma-preto); }
.emp-novo-sub { font: 400 12.5px var(--display); color: var(--tinta-2); }
@media (prefers-reduced-motion: reduce) { .emp { transition: none; } }

/* o cartão inteiro é o alvo do clique; o ::after do link cobre o cartão e o
   menu sobe por cima com z-index — assim o link fica sozinho, sem botão
   aninhado dentro de âncora (que é HTML inválido e quebra no teclado). */
.emp-link { text-decoration: none; color: inherit; display: block; }
.emp-link::after { content: ""; position: absolute; inset: 0; border-radius: var(--raio); }
.emp-link:focus-visible::after { outline: 2px solid var(--color-primary); outline-offset: 2px; }

/* ------------------------------------------------------------
   TOPO DO CARTÃO (DESIGN-1) — foto ou monograma, com nome e endereço
   sobre um gradiente para contraste. O QUE MUDOU DE LUGAR (mockup aprovado
   em 11/09): até aqui o nome e o endereço abriam o cartão em texto corrido;
   agora os dois sobem para dentro da foto, e o LANÇAMENTO — que manda na
   ordem da grade — fica logo abaixo dela.

   A ORDEM NO DOM NÃO É A ORDEM VISUAL, DE PROPÓSITO: dentro de `.emp-legenda`
   o endereço vem ANTES do nome no documento (e `column-reverse` os inverte
   na tela, nome em cima). A razão é o `check_cronograma` (cena 7): ele lê
   `.emp-nome, .emp-lanc, .emp-end` em ORDEM DE DOCUMENTO e exige que
   `.emp-lanc` — que mora FORA da foto, abaixo dela — venha LOGO DEPOIS de
   `.emp-nome` nessa lista. Com endereço-antes-do-nome, a sequência fica
   [end, nome, lanc] e a exigência (`indexOf(lanc) === indexOf(nome) + 1`)
   se cumpre sem tocar a leitura de tela, que continua nome → lançamento →
   endereço. */
.emp-topo {
  position: relative; height: 150px; margin: 0 0 14px;
  border-radius: var(--r-2); overflow: hidden;
  background: var(--color-bg);
}
/* NOTURNO-10-CONSERTO (25/09/2026): slot dedicado da foto/monograma, filho
   de `.emp-topo` -- a legenda (nome) e a pílula de fundo são IRMÃOS deste
   slot, não filhos dele, para que a troca de conteúdo do slot (monograma
   -> foto, quando a busca assíncrona volta) nunca apague os dois. Sem
   `position`: ele é o único filho em fluxo normal de `.emp-topo`, então
   100%/100% herda a caixa de 150px normalmente. */
.emp-slot-foto { width: 100%; height: 100%; }
.emp-foto { width: 100%; height: 100%; object-fit: cover; display: block; }
.emp-mono {
  width: 100%; height: 100%; display: flex; align-items: center; justify-content: center;
  background: linear-gradient(135deg, var(--color-primary), #2f8a63);
}
/* MARCA-2 (14/09/2026) — o monograma da marca (PNG 308×308 com alfa real)
   sobre o gradiente de `.emp-mono`; `width: auto` preserva a proporção do
   arquivo. 44px cai entre o piso (40) e o teto (48) pedidos pela Mesa;
   308/44 ≈ 7×, folga larga acima da nitidez mínima (2×) em qualquer tela. */
.emp-mono-marca { height: 44px; width: auto; display: block; }
/* "Imagem ainda não anexada" (HOME-2 item 5) — ancorada no TOPO da caixa, de
   propósito: a legenda (nome/endereço) fica no gradiente de BAIXO e o
   círculo de trocar imagem no canto inferior direito (`top: 134px`) — o topo
   é o único respiro livre dos dois. `position: absolute` porque ela sobra
   sobre o monograma (que já ocupa o espaço inteiro centralizado), não abaixo
   dele.
   TARDE-1 item 2 (HOME-IMG) — o `top` subiu de 36px para 8px, colado no
   canto superior, para abrir uma faixa de ~45px ENTRE a frase e o monograma
   (que começa a 53px, centralizado nos 150px da caixa): é ali que o botão
   "Anexar imagem" (`.emp-anexar-wrap`, abaixo) mora, sem tocar o monograma
   nem a legenda — a única folga vertical que a caixa tem. */
.emp-sem-imagem {
  position: absolute; z-index: 1; top: 8px; left: 14px; right: 14px;
  margin: 0; text-align: center;
  /* 12px, não 11,5 (dobra do item 10, A3). */
  font: 500 12px var(--display); color: rgba(255, 255, 255, 0.82);
}
.emp-pilula-fundo {
  position: absolute; top: 10px; left: 10px; z-index: 2;
  padding: 3px 9px; border-radius: 999px; background: rgba(19, 29, 25, 0.62);
  color: var(--doma-branco); font: 800 9.5px var(--mono); letter-spacing: 0.06em;
  text-transform: uppercase;
}
/* NOTURNO-6 item (d) — o padding DIREITO cresceu de 14px para 40px: nome e
   endereço são filhos `flex` (stretch) desta caixa, então a largura deles é
   sempre "largura da foto menos o padding dos dois lados", não a largura do
   texto — mesmo curto, o retângulo deles ia até quase a borda da foto. Sem
   este respiro reservado, NENHUMA posição do círculo de "trocar imagem" no
   canto inferior direito escaparia de interseccionar o retângulo do nome
   (medido: com o padding antigo, `.emp-trocar` e `.emp-nome` se
   sobrepunham). O padding assimétrico é o preço, medido e aceito, de manter
   OS CÍRCULOS SEMPRE fora do retângulo do texto, em qualquer largura de
   cartão — e são DOIS círculos, não um, desde que `.emp-remover` passou a
   morar ao lado do `.emp-trocar` (TARDE-1 item 2, DOBRA D-263): 40px media
   só o ✎ sozinho. Com os dois (60px de linha + o mesmo respiro de 28px até
   a borda), o círculo da ESQUERDA (`.emp-remover`) passou a invadir a caixa
   do texto — medido com endereço longo em 1366: texto até r=587,4, `×`
   começando em l=570,5. `.emp:has(.emp-remover)` é o mesmo seletor que já
   pinta o cartão pressionado (`.emp:has(.emp-link:active)`, acima) — condição
   por CONTEÚDO, sem precisar de classe nova no `art`. */
.emp-legenda {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 1;
  display: flex; flex-direction: column-reverse; gap: 2px;
  padding: 30px 40px 10px 14px;
  background: linear-gradient(to top, rgba(10, 16, 13, 0.82), rgba(10, 16, 13, 0));
}
.emp:has(.emp-remover) .emp-legenda { padding-right: 74px; }
/* O botão fica FORA de `<a>` (mesma razão do kebab: botão dentro de âncora é
   HTML inválido) — por isso `.emp-trocar-wrap` segue filho de `.emp` (o
   cartão inteiro) e não de `.emp-topo` (a foto): mover para dentro da foto
   aninharia o `<button>` dentro do `<a>` que a envolve. `top`/`right` são
   por isso CALCULADOS a partir da geometria fixa do cartão (padding de
   `.emp`: 18px no topo, 20px nos lados; altura da foto: 150px) para caírem
   no canto inferior direito da FOTO, com 8px de respiro — nunca `bottom`,
   que aqui mediria a partir da base do CARTÃO inteiro (depois de endereço,
   números e rodapé), não da foto. Medido: círculo contido na foto, zero
   interseção com `.emp-nome`/`.emp-end` em 1720 e 1366. */
/* TARDE-1 item 2 (HOME-IMG) — `.emp-trocar-wrap` virou LINHA (flex) para caber
   o círculo de remover ao lado do ✎, sem ordem nova: `right:28px` continua
   fixando a borda DIREITA do wrap, então o ✎ (último filho) fica exatamente
   onde estava antes — o `.emp-remover`, quando existe, só empurra a origem do
   wrap para a esquerda. */
.emp-trocar-wrap {
  position: absolute; z-index: 3; top: 134px; right: 28px;
  display: flex; align-items: center; gap: 8px;
}
.emp-trocar {
  display: flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0;
  border-radius: 50%; border: 1px solid rgba(255, 255, 255, 0.5);
  background: rgba(19, 29, 25, 0.58); color: var(--doma-branco);
  font-size: 13px; line-height: 1; cursor: pointer;
}
.emp-trocar:hover { background: rgba(19, 29, 25, 0.78); }
.emp-remover {
  display: flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; padding: 0;
  border-radius: 50%; border: 1px solid rgba(255, 255, 255, 0.5);
  background: rgba(19, 29, 25, 0.58); color: var(--doma-branco);
  font-size: 15px; line-height: 1; cursor: pointer;
}
.emp-remover:hover { background: rgba(148, 26, 26, 0.82); }

/* "Anexar imagem" (item 1) — mesma geometria-fantasma do `.emp-trocar-wrap`:
   irmão de `.emp-topo` (não filho, por causa do `<a>`), pousado por cima dela
   via posição absoluta calculada da mesma padding fixa do cartão.
   A CAIXA NÃO TEM FOLGA SOBRANDO (medido): "Imagem ainda não anexada" ocupa
   top:8..~24px (topo-local) e o monograma começa em 53px, centralizado nos
   150px da foto — a ÚNICA faixa livre é a fresta de ~28px entre os dois, e é
   onde este botão mora. Em coordenada de `.emp` (soma do padding-top:18px):
   top = 18 + 28 (logo após a frase) = 46px; o botão (padding 4px, fonte
   12px, borda 1px ≈ 24px de altura) termina em ~70px, ainda antes dos 71px
   (18+53) onde o monograma começa. Medido com PNG antes/depois nos três
   larguras da régua (D-322: aqui o headless é indício, o selo do dono é
   prova). */
.emp-anexar-wrap { position: absolute; z-index: 1; top: 46px; left: 34px; right: 34px; text-align: center; }
.emp-anexar {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 4px 12px; border-radius: 999px; border: 1px solid rgba(255, 255, 255, 0.55);
  background: rgba(19, 29, 25, 0.58); color: var(--doma-branco);
  font: 700 12px var(--display); cursor: pointer;
}
.emp-anexar:hover { background: rgba(19, 29, 25, 0.78); }

/* o NOME domina. Dentro da legenda ele fica branco, sobre o gradiente. */
.emp-nome {
  font: 800 20px/1.2 var(--display); letter-spacing: -0.018em;
  color: var(--doma-preto); margin: 0;
}
.emp-legenda .emp-nome { color: var(--doma-branco); }
.emp-end { margin: 4px 0 0; font-size: 12px; color: var(--tinta-2); opacity: 0.92; }
.emp-end.vazio { color: var(--tinta-3); font-style: italic; }
.emp-legenda .emp-end { color: rgba(255, 255, 255, 0.86); }
.emp-legenda .emp-end.vazio { color: rgba(255, 255, 255, 0.62); }
/* LÁPIDE DE `.emp-editado` (23/08/2026): pintava o "editado há X" do cartão,
   que o dono tirou. RESSUSCITADO NO RODAPÉ pela DESIGN-1 (mockup aprovado em
   11/09) — a classe volta com o MESMO nome porque é o mesmo dado, só que
   agora com dono declarado de novo: ver `.emp-editado` no bloco do rodapé. */

/* SLOT DA DATA DE LANÇAMENTO (martelo 5 do D-192, pago em 22/08).
   É a chave por onde a home ORDENA desde a P6.2, então ela aparece no cartão.

   `min-height` É A PEÇA QUE FAZ DELE UM SLOT, e não uma linha condicional: sem
   ela, o cartão SEM calendário fica ~14px mais baixo que o vizinho com data, e
   a grade desalinha a cada estudo que ganhar data — hoje são dois sem e um com.
   Vazio fica invisível e NO FLUXO, que é a mesma trava geométrica do
   `.ft-status` na faixa viva. Nada de travessão nem de "N/A" (D-187): o vazio
   se escreve com nada.

   A COR É `--tinta`, A TINTA PRINCIPAL — e a escolha é por ESCADA, não por MATIZ
   (ajuste do dono, 22/08: "cor diferente, destacada do metadado de sessão").

   A folha já tem a hierarquia pronta: `tinta` → `tinta-2` → `tinta-3`. A data de
   lançamento é **premissa** e **manda na ordem da grade**; "editado há X"
   (`tinta-3`) é metadado de sessão. Pôr a data no topo da escada resolve a
   hierarquia com o vocabulário que já existe.

   **POR QUE NÃO UM MATIZ, e a folha proíbe os dois candidatos óbvios:** o
   laranja da marca é "INEGOCIÁVEL onde representa a MARCA" e o `laranja-escuro`
   é declarado "SÓ onde a norma cobra contraste" — usá-los aqui seria gastar o
   acento de identidade num campo de dado. E o `--verde` é do sistema para
   "foco, sucesso, séries": *uma data não é sucesso*, e colori-la de verde
   inventaria um significado que ela não tem. **Matiz carrega semântica nesta
   folha; escada de tinta carrega só peso — e o que o dono pediu é peso.**

   O tamanho sobe de 10,5 para 11px pelo mesmo motivo, e para: a linha continua
   sendo `--mono` (é data, e data é número) e continua abaixo do nome na
   hierarquia. */
.emp-lanc {
  margin: 0 0 4px; min-height: 15px;
  font: 500 12px var(--mono); letter-spacing: 0.04em;
  color: var(--tinta);
  font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1;
}

/* ------------------------------------------------------------
   A GRADE DOS TRÊS NÚMEROS REFLUI, e isso é conserto de causa —
   não de sintoma. Com `repeat(3, 1fr)` fixo, cada número ganhava
   um terço da largura do cartão; num cartão estreito (iPad em
   retrato) "R$ 20.800.000" não cabia e o `R$` caía sozinho para a
   linha de cima, descolado do valor. Pôr `nowrap` e parar por aí
   teria trocado a quebra por um TRANSBORDO — o mesmo defeito com
   outra cara.
   Com `auto-fit` + `minmax`, a grade vira 2 colunas (ou 1) quando
   a largura não dá para três, e o número nunca é espremido abaixo
   do que ele mede. O `nowrap` continua, mas agora como garantia,
   não como remendo.
   ------------------------------------------------------------ */
.emp-nums {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px; margin: 0 0 14px;
}
/* DESIGN-1: cada KPI vira CÉLULA própria (fundo, borda, raio 8) em vez de um
   grupo único com fundo por baixo dos quatro — o mockup aprovado em 11/09
   separa VGV/Lucro/VPL/TIR visualmente, não só por rótulo. */
.emp-nums > div {
  min-width: 0; background: var(--color-bg); border: 1px solid #edf1ef;
  border-radius: var(--r-2); padding: 9px 11px 8px;
}
/* LÁPIDE DO VPL EM LINHA INTEIRA (23/08/2026).
   Havia aqui `.emp-nums > div:last-child { grid-column: 1 / -1; }`, e a razão
   dela era ARITMÉTICA, não hierarquia: com TRÊS números numa grade de duas
   colunas sobrava uma célula vazia ao lado do terceiro, e célula vazia num
   cartão lê-se como "faltou algo". Dar a linha ao VPL tapava o buraco.
   A TIR entrou em 23/08 e são QUATRO: a grade fecha 2×2 sozinha, sem buraco
   nenhum, e a regra perdeu o problema que resolvia. Mantê-la faria a TIR — que
   passou a ser o último filho — esticar por duas colunas e reabrir o buraco no
   VPL, exatamente ao contrário do que ela existia para fazer.
   O ARRANJO QUE SOBRA É O QUE O DONO PEDIU: VGV | Lucro em cima, VPL | TIR
   embaixo, com a TIR diretamente sob o Lucro. */
.emp-nums div span {
  display: block; font: 700 8.5px var(--display); letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--tinta-3); margin-bottom: 4px;
}
/* `white-space: nowrap` — o "R$" e o valor são UM token. Ele nunca quebra
   sozinho no meio, e o `tabular-nums` mantém os três números alinhados pela
   coluna do dígito, que é o que faz uma grade de valores ser LIDA como grade. */
.emp-nums div b {
  display: block; font: 500 15px var(--mono); letter-spacing: -0.02em;
  color: var(--doma-preto); white-space: nowrap;
  font-variant-numeric: tabular-nums; min-height: 1.15em;
}
.emp-nums em {
  display: block; font: 400 12px var(--mono); font-style: normal;
  color: var(--tinta-3); margin-top: 2px; white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.emp-nums b.neg { color: var(--vermelho); }
/* DOBRA (revisor do 5bc1274, bloqueante): `.emp-nums div b` (0,1,2) vencia
   `.sem-valor` (0,1,0) sozinho -- "sem cálculo"/"sem taxa única" saía PRETO
   15px/500, idêntico a um número de verdade, no cartão incompleto da home.
   Mesma casa de `.emp-nums b.carregando`/`.emp-nums b.neg` (duas classes),
   para especificidade vencer sem precisar de `!important`. */
.emp-nums b.sem-valor {
  /* P-58, DIURNA-11, 16/09/2026: o piso da D-401 vale onde a regra
     específica vence -- sem o max(), o pai a menos de 14px derrubava
     este texto abaixo de 12px. */
  color: var(--tinta-3); font-weight: 400; font-size: max(0.86em, 12px); letter-spacing: normal;
}
/* LÁPIDE (FINAL-2, 19/09/2026, D-441): `.emp-nums b.tir-nc` morreu com a
   marca "não confiável" do cartão da home (a flag `r.tir_confiavel` saiu do
   motor). */

/* ---- "Premissas incompletas" (HOME-2 item 2, prancha 3) ----
   Substitui `.emp-nums` INTEIRO no cartão incompleto — os quatro KPIs viram
   UMA frase de ausência, em vez de quatro "sem cálculo" repetidos (D-394 do
   card: "duas coisas falsas escritas quatro vezes custa mais que uma coisa
   verdadeira escrita uma vez"). Moldura tracejada, mesma linguagem do
   `.emp-novo` (convite/estado incompleto), nunca sólida (que leria como
   dado). `.emp-continuar` é `<span>`, não `<a>`: já mora dentro do `<a
   class="emp-link">` que cobre o cartão inteiro, e âncora dentro de âncora
   é HTML inválido (mesma razão do botão "trocar imagem"). */
.emp-incompleto {
  margin: 0 0 14px; min-height: 84px;
  border: 1px dashed #c8d3ce; border-radius: var(--r-2);
  padding: 14px 16px 13px;
  display: flex; flex-direction: column; justify-content: center;
}
.emp-incompleto strong { font: 800 14px var(--display); color: var(--doma-preto); }
.emp-incompleto p {
  margin: 5px 0 9px; font: 400 11.5px var(--display); color: var(--tinta-2); line-height: 1.5;
}
.emp-continuar { font: 700 12px var(--display); color: var(--color-primary); }

/* ---- O ESQUELETO, que substituiu o travessão (04/08/2026) ----
   O travessão era um CARACTERE fazendo o trabalho de um ESTADO, e tinha dois
   defeitos: o olho o lê como conteúdo (um valor que é um traço), e ele obriga a
   tela a escrever algo onde não há nada a dizer. A barra não é texto — não há
   como lê-la como número — e some sozinha quando o valor chega.
   ZERO CONTINUA PROIBIDO pelo motivo de sempre: zero é um número e diz
   "calculou e deu nada"; a ausência do número diz "não calculou".
   LÁPIDE (DESIGN-8 item 1, D-335): "estudo incompleto não tem esqueleto, o
   slot fica vazio" descrevia o produto ATÉ o 5bc1274 -- hoje cada slot ESCREVE
   "sem cálculo" (`.emp-nums b.sem-valor`, acima), e o vazio virou o defeito. */
.emp-nums b.carregando {
  color: transparent;
  border-radius: 4px;
  background: linear-gradient(90deg, #e7ecea 25%, #f2f5f4 37%, #e7ecea 63%);
  background-size: 400% 100%;
  animation: esqueleto-desliza 1.3s ease-in-out infinite;
}
@keyframes esqueleto-desliza {
  0% { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}
@media (prefers-reduced-motion: reduce) {
  .emp-nums b.carregando { animation: none; background: #e7ecea; }
}

.emp-rodape { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; row-gap: 6px; }

/* ---- selo curto do cartão ----
   PENDÊNCIA É ÂMBAR, NUNCA O LARANJA DA MARCA. O selo de "Atenção" fica a
   poucos centímetros do botão "+ Novo Empreendimento", que é laranja: se as
   duas cores fossem a mesma, a tela ensinaria o usuário a não distinguir ação
   de aviso.
   Cada selo carrega também a FORMA do seu veredicto (P4): o ponto redondo
   dizia "há um estado aqui" e a severidade vinha só da cor. */
.selo-mini {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 12px 5px 10px; border-radius: 999px;
  font: 800 9.5px var(--display); letter-spacing: 0.16em; text-transform: uppercase;
  border: 1px solid transparent;
}
.selo-mini::before {
  content: "\2713";                          /* ✓ — viável */
  font: 800 11px/1 var(--display);
  /* o selo é caixa alta com tracking de 0,16em e o glifo herdaria os dois:
     sairia um ícone com vão à direita, desalinhando o `gap` do flex. */
  letter-spacing: 0; text-transform: none;
}
.selo-mini.ok         { background: var(--verde-claro); color: var(--verde-escuro); border-color: #bcd7ca; }
.selo-mini.atencao    { background: var(--ambar-claro); color: var(--ambar); border-color: var(--ambar-borda); }
.selo-mini.ruim       { background: var(--vermelho-claro); color: var(--vermelho); border-color: var(--vermelho-borda); }
.selo-mini.incompleto { background: #eef2f0; color: var(--tinta-2); border-color: #dbe2de; }
.selo-mini.atencao::before    { content: "\26A0"; }   /* ⚠ */
.selo-mini.ruim::before       { content: "\2715"; }   /* ✕ */
.selo-mini.incompleto::before { content: "\2026"; }   /* … — não é veredicto, é ausência dele */
.selo-mini.calculando::before { content: "\00B7\00B7\00B7"; }

/* ---- rodapé do cartão (DESIGN-1): pendências, versões, editado ----
   "PENDÊNCIAS" AQUI É O MESMO FATO QUE ACENDE O SELO "Incompleto": o cálculo
   do cartão devolveu erro (409/422, `carregarNumerosDoCartao`). Não é uma
   segunda leitura do estudo — é a MESMA leitura, com outra frase, porque o
   selo fala do VEREDICTO (viável/atenção/inviável/incompleto) e isto fala de
   PENDÊNCIA (há algo para o dono resolver, sim/não). Ícone vermelho é
   deliberado: pendência não é aviso brando. */
/* HOME-2 item 1 (prancha 3): "Preenchimento · sem/com pendências", texto
   corrido — sem ícone, que era o formato do R2 e saiu com o texto exato da
   prancha entrando no lugar dele. */
.emp-pend { font: 700 11px var(--display); }
.emp-pend.tem { color: var(--vermelho); }
.emp-pend.ok { color: var(--verde-escuro); }

/* "Veredicto · <selo>" (HOME-2 item 1) — o rótulo fica FORA de `.selo-mini`
   de propósito (ver o comentário em `cartaoEmpreendimento`, no app.js). */
.emp-veredicto {
  display: inline-flex; align-items: center; gap: 6px;
  font: 700 11px var(--display); color: var(--tinta-2);
}

/* VAR-1 item b: "N versão/versões" carrega número, piso de 12px (D-401). */
.emp-versoes { font: 500 12px var(--display); color: var(--tinta-2); }

/* RESSUSCITADO (ver lápide acima de `.emp-nome`): mesmo dado de sempre
   (`atualizado_em`), agora no rodapé em vez de sob o nome. */
/* 12px, não 11,5 (dobra do item 10, A5): "13/09" é dígito. */
.emp-editado { font: 500 12px var(--display); color: var(--tinta-3); margin-left: auto; }

.aviso-pend {
  display: inline-flex; align-items: center; gap: 5px;
  font: 600 11px var(--display); color: var(--ambar); margin-left: auto;
}

/* ---- menu do cartão: renomear · duplicar · excluir ---- */
.emp-menu-wrap { position: absolute; top: 14px; right: 14px; z-index: 3; }
.kebab {
  width: 30px; height: 30px; border-radius: 8px; cursor: pointer;
  border: 1px solid transparent; background: transparent; color: var(--tinta-3);
  font-size: 17px; line-height: 1; display: grid; place-items: center;
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
  transition: background 0.12s, color 0.12s, border-color 0.12s;
}
.kebab:hover, .kebab[aria-expanded="true"] {
  background: var(--superficie-2); color: var(--tinta); border-color: var(--linha);
}
/* `position:relative` no invólucro devolve o botão ao fluxo (o `absolute` do
   cartão se mediria pela tarja). DESIGN-7 item 2: o kebab não precisa mais de
   cor própria aqui -- a tarja é clara e o `.kebab` do bloco acima já serve. */
.tj .emp-menu-wrap { position: relative; top: auto; right: auto; }

.menu {
  position: absolute; top: 34px; right: 0; min-width: 208px; z-index: 50;
  background: var(--superficie); border: 1px solid var(--linha);
  border-radius: 10px; padding: 5px;
  box-shadow: 0 4px 8px rgba(19,29,25,0.06), 0 14px 34px rgba(19,29,25,0.16);
}
.menu button {
  display: flex; width: 100%; align-items: center; gap: 10px;
  border: 0; background: transparent; cursor: pointer; text-align: left;
  font: 500 13px var(--display); color: var(--tinta);
  padding: 9px 10px; border-radius: 7px; touch-action: manipulation;
}
.menu button i { font-style: normal; font-size: 13px; color: var(--tinta-3); width: 15px; text-align: center; }
.menu button:hover { background: var(--superficie-2); }
.menu button:focus-visible { outline: 2px solid var(--color-primary); outline-offset: -2px; }
.menu hr { border: 0; border-top: 1px solid var(--linha); margin: 5px 2px; }
.menu .destrutivo, .menu .destrutivo i { color: var(--vermelho); }
.menu .destrutivo:hover { background: var(--vermelho-claro); }
/* ESTADOS-1, item 7: as três saídas do Exportar levam a seta "↓" à DIREITA
   (mesma peça que o botão que abre o menu), como a prancha 15 desenha --
   escopado a `#menu-exportar` porque o `.menu button` genérico (Renomear,
   Duplicar...) continua com o ícone à esquerda. */
.menu-exportar button { justify-content: space-between; }
.menu-exportar button i { width: auto; font-size: 12px; }
.menu-exportar .nota { margin: 6px 8px 2px; padding-top: 6px; border-top: 1px solid var(--linha); }
/* EXPORT-2a itens 2 e 3 (19/09/2026): cinco linhas, um perfil por linha, cada
   uma com o rótulo oficial e DOIS botões (PDF/Word) -- o `.menu button`
   genérico (full-width, empilhado) não serve aqui, então a linha é um `div`
   e os botões de formato ganham a régua própria abaixo. O rótulo pode
   quebrar em duas linhas (nomes longos, ex.: "Versão para investidores de
   unidades"); os dois botões NUNCA saem da faixa do rótulo -- `flex-shrink:0`
   nos botões e nenhum `flex-wrap` na linha, só no texto do rótulo. */
.exp-lista { display: flex; flex-direction: column; gap: 2px; margin: 2px 0 4px; }
.exp-linha {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 6px 8px; border-radius: 7px;
}
.exp-linha:hover { background: var(--superficie-2); }
.exp-linha-rotulo { flex: 1 1 140px; min-width: 0; font: 500 13px var(--display); color: var(--tinta); }
.exp-linha-botoes { display: flex; flex-shrink: 0; gap: 6px; }
/* B3 (NOTURNO-9): a linha "Documento completo" ganhou um segundo nível --
   o link "abrir para imprimir" abaixo dos dois botões de formato. Só ela;
   as outras quatro linhas continuam de uma linha só, `.exp-linha` puro. */
.exp-linha-completo { flex-direction: column; align-items: stretch; gap: 2px; }
.exp-linha-principal { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
/* DOBRA B3 (NOTURNO-9, revisor): a mesma armadilha do `.exp-btn-formato` logo
   abaixo -- `.exp-imprimir` sozinho é (0,1,0) e `.menu button`/
   `.menu-exportar button` são (0,1,1), então o link perdia a roupa própria e
   saía pintado como sexta linha do menu (13px, cor --tinta, padding de botão,
   largura cheia). Prefixado com `.menu-exportar`, sobe para (0,2,0) e vence
   sem depender de ordem no arquivo. */
.menu-exportar .exp-imprimir {
  align-self: flex-end; margin: 0; font: 500 12px var(--display); color: var(--tinta-3);
  text-decoration: underline; background: none; border: 0; padding: 2px 4px;
  width: auto; cursor: pointer; touch-action: manipulation;
}
.menu-exportar .exp-imprimir:hover, .menu-exportar .exp-imprimir:focus-visible { color: var(--tinta-2); }
/* `.menu-exportar` prefixado nos quatro: `.menu button` (0,1,1) tem a MESMA
   especificidade de `.exp-btn-formato` sozinho (0,1,0 perderia por elemento
   ausente) -- prefixar com `.menu-exportar` sobe para (0,2,1) e garante que
   este bloco vence sem depender de ordem no arquivo. */
.menu-exportar .exp-btn-formato {
  width: auto; display: inline-flex; align-items: center; gap: 4px;
  padding: 5px 9px; font: 600 12px var(--display); color: var(--tinta);
  background: var(--superficie); border: 1px solid var(--linha); border-radius: 6px;
  cursor: pointer; touch-action: manipulation;
}
.menu-exportar .exp-btn-formato i { width: auto; font-size: 12px; font-style: normal; color: var(--tinta-3); }
.menu-exportar .exp-btn-formato:hover { background: var(--superficie-2); }
.menu-exportar .exp-btn-formato:focus-visible { outline: 2px solid var(--color-primary); outline-offset: -2px; }
.menu-exportar .exp-btn-formato:disabled { opacity: 0.45; cursor: not-allowed; }
/* CHECK-1 (TARDE-1 item 8) -- o painel "pronto para exportar", dentro do
   próprio menu. A largura padrão do `.menu` (min-width 208px) não sobra para
   frase inteira; 380px foi MEDIDO como o que cabe em 834px sem sair da tela
   (o menu abre `right:0`, então o excesso cresce para a esquerda). */
.menu-exportar { width: 380px; }
/* B1 (DOBRA D-263, qualidade-b): sem teto, o menu crescia com a lista e
   empurrava os três botões + o bloqueio para fora do viewport -- rolar a
   PÁGINA não ajuda, porque quem precisa rolar é a LISTA, dentro do menu. O
   teto deriva do que já sobra depois da tarja (`--h-tarja`) e do que a lista
   NÃO ocupa (os 3 botões + a nota + o bloqueio + o padding do menu): medido,
   ~210px cobrem isso nas três larguras (834/1366/1720). */
.exp-checagem { margin: 2px 4px 8px; max-height: calc(100vh - var(--h-tarja) - 210px); overflow-y: auto; }
.exp-check-lista { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
/* Mesma linguagem do `.alerta-item` da seção 09 (borda à esquerda por
   severidade), num corpo mais compacto -- o menu é estreito e a lista pode
   ter até 7 linhas. */
.exp-check-item {
  border-left: 4px solid var(--ambar); background: var(--ambar-claro); color: #4a3c06;
  padding: 8px 10px; border-radius: 0 7px 7px 0; font: 500 12px var(--display); line-height: 1.4;
}
.exp-check-item.critico {
  border-left-width: 6px; border-left-color: var(--vermelho);
  background: var(--vermelho-claro); color: #6b2417; font-weight: 600;
}
.exp-check-link {
  display: inline; margin-left: 4px;
  font: 700 12px var(--display); text-decoration: underline; color: inherit;
  background: none; border: 0; padding: 0; cursor: pointer; touch-action: manipulation;
}
.exp-check-link:hover, .exp-check-link:focus-visible { opacity: 0.82; }
.exp-check-ok {
  margin: 0; padding: 8px 10px; border-radius: 7px;
  background: var(--superficie-2); color: var(--tinta-2); font: 500 12px var(--display);
}
/* O bloqueio é texto PRETO -- D-081: ausência sempre com causa, nunca botão
   mudo -- e some sozinho quando não há crítica (`hidden`, nunca vazio
   fingindo estado). */
.exp-check-bloqueio { margin: 0 8px 6px; color: var(--tinta); font: 500 12px var(--display); line-height: 1.35; }

/* ---- estado vazio da home ---- */
.vazio-caixa {
  background: var(--superficie); border: 1px dashed #c8d3ce; border-radius: var(--raio);
  padding: 64px 32px; text-align: center; max-width: 620px; margin: 8px auto 0;
}
.vazio-caixa h2 { font: 700 21px var(--display); margin: 0 0 8px; }
.vazio-caixa p { color: var(--tinta-2); font-size: 14px; margin: 0 auto 22px; max-width: 46ch; }
.vazio-marca {
  width: 56px; height: 56px; border-radius: 14px; margin: 0 auto 18px;
  background: var(--pessego-fundo); border: 1px solid var(--pessego-borda);
  display: grid; place-items: center;
}
.vazio-marca img { width: 26px; height: auto; display: block; }

/* ============================================================
   5 · MODAIS — criar, renomear, excluir
   ============================================================ */
.fundo-modal {
  position: fixed; inset: 0; z-index: 60;
  background: rgba(19, 29, 25, 0.42);
  display: grid; place-items: center; padding: 20px;
}
.modal {
  background: var(--superficie); border-radius: var(--r-3); width: min(470px, 100%);
  /* A sombra do MODAL fica MAIS FORTE que a do cartão de propósito (não vira
     alias de --shadow-card): ele flutua sobre um fundo escurecido
     (`.fundo-modal`) e precisa de elevação própria, maior que a de um cartão
     dentro da página — usar o token do cartão aqui achataria o modal contra
     o fundo dimerizado (DESIGN-2). */
  box-shadow: 0 10px 24px rgba(19,29,25,0.14), 0 30px 70px rgba(19,29,25,0.26);
  overflow: hidden;
}
.modal-cab { padding: 22px 24px 0; }
/* EYEBROW DO MODAL = EYEBROW DA SEÇÃO (DESIGN-2): a versão anterior pintava
   `--laranja-escuro` aqui, e o laranja desta casa é só ação principal e marca
   (D-380) — um rótulo de cabeçalho de modal não é nenhum dos dois. Sem regra
   própria, `.eyebrow` herda a cor verde que já usa nos cartões das seções. */
.modal-cab .eyebrow.perigo { color: var(--vermelho); }
.modal-cab h2 { font: 700 20px var(--display); margin: 6px 0 0; letter-spacing: -0.01em; }
.modal-corpo { padding: 18px 24px 4px; display: flex; flex-direction: column; gap: 14px; }
.modal-pe { display: flex; justify-content: flex-end; gap: 10px; padding: 18px 24px 22px; margin-top: 12px; }
.modal-pe.perigo { background: var(--vermelho-claro); border-top: 1px solid #e9cdc6; margin-top: 18px; }
.modal p.aviso { font-size: 13px; color: var(--tinta-2); margin: 0; }
.campo-rot { display: flex; flex-direction: column; gap: 6px; font-size: 12px; color: var(--tinta-2); font-weight: 500; }
.campo-rot .obrig { color: var(--vermelho); }
.campo-rot .opcional { color: var(--tinta-3); }
.ajuda { font-size: 12px; color: var(--tinta-2); margin: 0; }

/* o nome a digitar na exclusão: é o LITERAL que o usuário tem de reproduzir, e
   por isso ele se destaca do rótulo em vez de correr junto com a frase. Não usa
   `--mono`: o nome é texto do usuário, não número. */
.nome-alvo {
  font-weight: 800; color: var(--vermelho);
  /* nome longo não pode empurrar a largura do modal nem estourar a caixa */
  overflow-wrap: anywhere;
}

/* ============================================================
   6 · ESTUDO — abas, premissas, faixa viva
   ============================================================ */
/* A faixa de aviso é FIXA e vive fora das duas telas: ela fala tanto pela home
   (criar, duplicar, excluir) quanto pelo estudo. Escrita num elemento que só a
   tela do estudo mostra, uma falha na home seria silenciosa. */
.msg {
  position: fixed; top: calc(var(--h-tarja) + 14px); right: 20px; z-index: 55;
  max-width: min(520px, calc(100vw - 40px));
  padding: 11px 15px; border-radius: 9px; font-size: 13px;
  border: 1px solid var(--linha); background: var(--superficie);
  box-shadow: 0 6px 20px rgba(19,29,25,0.14);
  /* ESTUDO-JSON (D-426): os avisos de importação chegam um por LINHA
     ("\n" entre frases) -- sem isto o navegador os colapsaria num só
     parágrafo corrido. Mensagem de uma linha não muda. */
  white-space: pre-line;
}
.msg.erro { border-color: var(--vermelho-borda); color: var(--vermelho); background: var(--vermelho-claro); }
.msg.ok { border-color: #b8d6c8; color: var(--verde-escuro); background: var(--verde-claro); }

/* P-39 (DIURNA-6), FAIXA NO FLUXO desde a DOBRA 2 do item 2 (martelo do
   qualidade-b: em `position: fixed` o bloco cobria o `h1` "Empreendimentos",
   o `#home-sub` e os chips da Home por baixo dele -- coisa nenhuma na tela
   promete "por cima de conteúdo", e este bloco não tem timeout para
   justificar a exceção do `.msg`, que É um toast). Continua irmã do `.msg`
   acima (`.msg` pode aparecer ao mesmo tempo, no lado direito, fixo) — mas
   este empurra a página para baixo em vez de sobrepor: sem `position`, ele
   entra no FLUXO normal logo depois dos dois `.topo-fixo` (só o ATIVO ocupa
   espaço) e antes das duas telas, então aparece sob a tarja tanto na volta
   para a Home quanto no estudo onde ele nasce. As margens seguem o mesmo
   respiro horizontal de `.pagina` (28px), para não desalinhar do conteúdo
   logo abaixo. */
.avisos-importacao {
  max-width: 1180px; margin: 14px auto 0; padding: 13px 28px;
  display: flex; align-items: flex-start; gap: 10px;
  border-radius: 9px; font-size: 13px;
  border: 1px solid #b8d6c8; background: var(--verde-claro); color: var(--verde-escuro);
  box-shadow: 0 6px 20px rgba(19,29,25,0.14);
}
.avisos-importacao.erro { border-color: var(--vermelho-borda); background: var(--vermelho-claro); color: var(--vermelho); }
.avisos-importacao-corpo { flex: 1; min-width: 0; }
.avisos-importacao-corpo b { display: block; font: 700 13px var(--display); margin-bottom: 4px; }
.avisos-importacao-corpo ul { margin: 0; padding-left: 18px; }
.avisos-importacao-corpo li { margin: 2px 0; }
#avisos-importacao-fechar {
  border: 0; background: transparent; font: 400 18px/1 var(--display); color: inherit;
  cursor: pointer; padding: 0 2px; border-radius: 6px; flex-shrink: 0; opacity: 0.7;
}
#avisos-importacao-fechar:hover, #avisos-importacao-fechar:focus-visible { opacity: 1; }

.abas {
  display: flex; gap: 2px; border-bottom: 1px solid var(--linha);
  background: var(--fundo); position: sticky; top: var(--h-tarja); z-index: 30;
  padding: 6px 28px 0; margin: 0 -28px;
}
.aba {
  font: 600 13px var(--display); border: 0; background: transparent; color: var(--tinta-2);
  padding: 11px 18px; cursor: pointer; border-bottom: 2px solid transparent;
  margin-bottom: -1px; border-radius: 8px 8px 0 0;
  touch-action: manipulation; -webkit-tap-highlight-color: transparent;
  transition: color 0.12s, background 0.12s, border-color 0.12s;
}
.aba:hover { color: var(--tinta); background: rgba(255,255,255,0.55); }
.aba:active { transform: translateY(1px); }
.aba.ativa { color: var(--tinta); border-bottom-color: var(--color-primary); }
.aba:focus-visible { outline: 2px solid var(--color-primary); outline-offset: -3px; }

.painel { display: none; padding-top: 26px; }
.painel.ativo { display: block; }
@media (prefers-reduced-motion: no-preference) {
  .painel.ativo { animation: surge 0.2s ease; }
  @keyframes surge { from { opacity: 0.3; transform: translateY(4px); } }
}

.premissas-layout { display: flex; gap: 28px; align-items: flex-start; }
/* STEPPER VERTICAL COM LINHA — DESIGN-0 (11/09). A linha é um pseudo-elemento
   do próprio `.stepper`, atrás dos badges (`.passo` ganha `z-index` para ficar
   por cima): ela nasce e morre a meia-altura do primeiro e do último badge
   (21px de badge -> 10,5px de inset), e fica centrada sob o círculo (10px de
   padding + 21px/2 de badge - 1px de metade da linha = 19,5px).
   SUBTÍTULO POR ETAPA: o HTML de hoje não tem esse `<span>` (só `<i>` + texto
   do passo) — nada foi inventado aqui. O slot fica RESERVADO e documentado:
   se um dia existir `.passo small`, ele cai dentro de `.passo-txt` (a coluna
   de texto que teria de existir ao lado do badge) com
   `font: 500 10.5px var(--font-sans); color: var(--color-text-muted);
   display: block;` — sem selector morto no CSS de produção enquanto isso não
   existir. */
.stepper {
  position: sticky; top: calc(var(--h-tarja) + 59px); flex-shrink: 0; width: 178px;
  display: flex; flex-direction: column; gap: 3px;
}
.stepper::before {
  content: ""; position: absolute; left: 19.5px; top: 19.5px; bottom: 19.5px;
  width: 2px; background: var(--color-border); z-index: 0;
}
.passo {
  position: relative; z-index: 1;
  display: flex; align-items: center; gap: 10px;
  font: 600 12.5px var(--display); color: var(--tinta-2);
  text-decoration: none; padding: 9px 10px; border-radius: 8px;
}
.passo i {
  font: 500 10px var(--mono); font-style: normal;
  width: 21px; height: 21px; display: grid; place-items: center;
  border: 1px solid var(--linha); border-radius: 50%; background: var(--superficie);
  color: var(--tinta-3); flex-shrink: 0;
}
/* SUBTÍTULO POR ETAPA (DESIGN-8, item 2) — o slot que o comentário do
   `.stepper::before` já reservava. `.passo-txt` é a coluna de texto ao lado
   do badge; o nome da etapa é o próprio texto do link, e o `small` carrega o
   subtítulo — derivado do `h2` real de cada seção, nunca inventado. Uma
   linha só (nowrap + ellipsis): decoração de navegação, não frase de
   ausência — a proibição de elipse (D-208/D-187) é para texto que afirma um
   fato, e este é rótulo. */
.passo-txt { display: flex; flex-direction: column; min-width: 0; line-height: 1.3; }
.passo-txt small {
  font: 500 10.5px var(--font-sans); color: var(--color-text-muted);
  display: block; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.passo:hover { color: var(--tinta); }
.passo.ativo { color: var(--color-primary); background: var(--color-primary-soft); }
.passo.ativo i { border-color: var(--color-primary); background: var(--color-primary); color: var(--color-surface); }
.passo.pendente i { border-color: var(--ambar-borda); background: var(--ambar-claro); color: var(--ambar); }

/* STEPPER COMPACTO (NOTURNO-6 item 11, iPad 834) — `.stepper-lista` é
   `display: contents` em telas largas: o wrapper some do layout e os dez
   `.passo` continuam filhos diretos do `.stepper` para fins de CSS (o
   `gap: 3px` do `.stepper` original), então o visual em desktop/1024 não
   muda um pixel. `.stepper-atual` (o botão "seção atual") fica oculto por
   padrão e só aparece na média de largura estreita, mais abaixo. */
.stepper-lista { display: contents; }
.stepper-atual { display: none; }

/* 34px entre cartões vizinhos, e não os 22 de antes: espaço é o separador
   mais barato que existe e o único que não gasta cor nem tinta. É ele que
   faz o cartão virar objeto na rolagem — o papel que a barra lateral teria
   feito, e que foi recusada em 03/08. */
.secoes { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 34px; }

.cartao {
  background: var(--superficie); border: 1px solid var(--linha);
  border-radius: var(--raio); padding: 26px 28px 28px; box-shadow: var(--sombra);
}
/* ------------------------------------------------------------
   A RÉGUA TIPOGRÁFICA DOS 8 GRUPOS — medida, não estimada.

   Quem manda no cartão é a FRASE da seção ("Aquisição do terreno"),
   em 25px/800; o nome do grupo ("01 · TERRENO") fica ACIMA, como
   sobrancelha — pequena, caixa alta, espaçada. As duas linhas sempre
   existiram; o que mudou é quem leva o corpo grande.

   A ESCADA que a medição fixou, e o piso que ela respeita:
     frase da seção ... 25px/800  display, tracking -0,025em
     subtópico ....... 11,5px/700 display, CAIXA ALTA, +0,1em
   Razão 2,2× entre os dois — acima do piso de 1,8× abaixo do qual dois
   níveis viram "quase o mesmo tamanho" e a hierarquia fica decorativa.
   Os outros pares NÃO se separam por tamanho e isso é declarado: a
   sobrancelha se separa por FAMÍLIA (mono × display) e caixa; o
   subtópico, que é MENOR que o rótulo de um campo (12px) e ainda assim
   lê como título, se separa por CAIXA + PESO. Um subtópico em 15px
   competiria com a frase da seção.

   O FIO DE BASE fecha o cabeçalho sem pintar fundo nenhum — separa
   cabeçalho de conteúdo dentro do cartão, que é o que a barra lateral
   recusada faria de fora.
   ------------------------------------------------------------ */
/* CABEÇALHO INLINE — DESIGN-0: `.eyebrow` e `h2` passam a viver na MESMA
   linha, separados por uma divisória vertical (o `border-right` do próprio
   `.eyebrow`, esticado pela altura do header via `align-items: stretch`).
   O TEXTO não muda — "06 · Obra" + "Custo e cronograma" seguem exatamente
   como estavam; reescrever rótulo é fora do escopo desta fatia (só roupa). */
.cartao-cab {
  display: flex; flex-wrap: wrap; align-items: stretch; gap: 14px;
  padding-bottom: 15px; margin-bottom: 18px; border-bottom: 1px solid var(--linha);
}
.eyebrow {
  display: flex; align-items: center; flex-shrink: 0;
  padding-right: 14px; border-right: 1px solid var(--color-border);
  font: 500 10px var(--font-mono); letter-spacing: 0.22em; text-transform: uppercase; color: var(--verde);
}
.cartao-cab h2 { display: flex; align-items: center; font: 800 25px/1.05 var(--display); letter-spacing: -0.025em; margin: 0; }
.cartao-cab .descricao { flex-basis: 100%; margin: 0; color: var(--tinta-2); font-size: 13px; max-width: 62ch; }

/* DESIGN-7, item 3 — DENTRO DE PREMISSAS o cabeçalho encolhe: o `h2` deixa de
   ser a frase grande (25px, o padrão da DESIGN-0 acima) e vira SUBTÍTULO ao
   lado do `.eyebrow`, os dois na mesma linha e separados por um "·" textual
   (`.cab-sep`) em vez do fio vertical — que aqui ficaria duplicado com o
   fio horizontal de baixo (`border-bottom` de `.cartao-cab`, que continua).
   O parágrafo longo que existia vira o `title` nativo do botão "?"
   (`.cartao-ajuda` — nome próprio para não colidir com `.ajuda`, a nota dos
   modais), e o que fica visível no cartão é uma frase curta. Só a ROUPA
   muda: nenhum campo, rótulo, id ou ordem sai daqui (D-384/D-380/D-382). As
   outras vistas de `.cartao-cab` — Sensibilidade, os modais — ficam
   exatamente como estavam, por isso o escopo é `#aba-premissas`. */
#aba-premissas .cartao-cab {
  align-items: center; gap: 6px; padding-bottom: 8px; margin-bottom: 16px;
}
#aba-premissas .cartao-cab .eyebrow { border-right: 0; padding-right: 0; line-height: 1; }
#aba-premissas .cartao-cab .cab-sep { color: var(--tinta-3); flex-shrink: 0; line-height: 1; }
#aba-premissas .cartao-cab h2 {
  font: 600 16px/1 var(--display); letter-spacing: -0.01em; color: var(--tinta);
}
/* AJUDA-1 (14/09/2026): `.cartao-ajuda` deixou de ser só um tooltip nativo, e
   passou a abrir o painel lateral (`abrirPainelAjuda`, em app.js). A regra
   BASE mora fora do escopo `#aba-premissas`, porque o mesmo botão passou a
   viver também no Resultado, na Sensibilidade, no Fluxo e no Comparar; o
   escopo de premissas continua com a variante menor (18px), de propósito. */
.cartao-ajuda {
  width: 20px; height: 20px; flex-shrink: 0; padding: 0; margin: 0; line-height: 1;
  border-radius: 50%; border: 1px solid var(--linha); background: transparent;
  color: var(--tinta-3); font: 700 11px var(--font-mono);
  display: inline-flex; align-items: center; justify-content: center; cursor: pointer;
}
.cartao-ajuda:hover, .cartao-ajuda:focus-visible { border-color: var(--tinta-3); color: var(--tinta-2); }
#aba-premissas .cartao-cab .cartao-ajuda {
  width: 18px; height: 18px;
}
#aba-premissas .cartao-cab .descricao { font-size: 12.5px; max-width: none; }

/* O "?" dos blocos do Resultado/Comparar mora dentro do `.res-sec-cab`
   (já `display:flex`, ver a definição própria mais abaixo), ao lado do
   `.num`/`.pergunta`; da Sensibilidade e do Fluxo, dentro do `.cartao-cab`
   de cada aba (fora de `#aba-premissas`, então herda só a regra base acima).
   `margin-left: auto` empurra o botão para o fim da linha, sem precisar de
   uma segunda definição de `.res-sec-cab` (casa única). */
.res-sec-cab .cartao-ajuda { margin-left: auto; align-self: center; }

/* ---------------------------------------------------------------
   PAINEL LATERAL DE AJUDA (AJUDA-1) — um painel só para o sistema
   inteiro, à direita, acima de tudo (inclusive dos modais, porque a
   ajuda pode ser aberta a partir de um bloco que já está sob um modal
   de versões). Fecha por botão e por Esc (ligação em app.js); nasce
   `hidden`, que é o estado "nenhum botão foi tocado ainda".
   --------------------------------------------------------------- */
#painel-ajuda {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 80;
  width: min(360px, 100vw); background: var(--superficie);
  box-shadow: -12px 0 32px rgba(19,29,25,0.20);
  padding: 22px 22px 24px; overflow-y: auto;
  display: flex; flex-direction: column; gap: 12px;
}
.painel-ajuda-cab { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.painel-ajuda-cab h2 {
  font: 700 19px/1.2 var(--display); letter-spacing: -0.01em; margin: 0; color: var(--tinta);
}
.painel-ajuda-cab h2:focus { outline: none; }
#painel-ajuda-fechar {
  border: 0; background: transparent; font: 400 22px/1 var(--display); color: var(--tinta-3);
  cursor: pointer; padding: 2px 6px; border-radius: 6px; flex-shrink: 0;
}
#painel-ajuda-fechar:hover, #painel-ajuda-fechar:focus-visible { color: var(--tinta); background: var(--superficie-2); }
.painel-ajuda-corpo p { font-size: 13.5px; line-height: 1.55; color: var(--tinta-2); margin: 0 0 12px; }
.painel-ajuda-corpo p:last-child { margin-bottom: 0; }
.painel-ajuda-link {
  font-size: 13px; font-weight: 700; color: var(--verde); text-decoration: none;
  padding-top: 10px; border-top: 1px solid var(--linha);
}
.painel-ajuda-link:hover { text-decoration: underline; }
/* P-41 (DIURNA-6): `width: 100vw` deixava o painel maior que a área visível
   em 834px (a unidade `vw` conta a faixa do scrollbar que o layout não tem
   -- medido, ~15px caíam fora da borda esquerda, respiro 22 -> 7px). O
   respiro do texto (padding, acima) continua o mesmo; só a largura muda. */
@media (max-width: 834px) { #painel-ajuda { width: calc(100vw - 32px); } }

.subsecao { font: 700 14px var(--display); margin: 22px 0 6px; padding-top: 16px; border-top: 1px dashed var(--linha); }
/* DESIGN-8, item 3c — o sufixo " · N variações · P linhas" de "02.
   VARIAÇÕES", em peso normal e tinta apagada, para não competir com o
   título. Nasce vazio (silêncio) até a primeira leitura dos chips. */
.subsecao-sufixo { font: 500 12.5px var(--display); color: var(--tinta-2); }
.dica-txt { font-weight: 400; color: var(--tinta-2); font-size: 12px; }

.grade { display: grid; grid-template-columns: repeat(auto-fill, minmax(196px, 1fr)); gap: 16px 18px; }
.grade label, .linha-cheia { display: flex; flex-direction: column; gap: 6px; font-size: 12px; color: var(--tinta-2); font-weight: 500; }
.linha-cheia { margin-top: 14px; }

/* campo com unidade (R$ / % / mês) */
.campo {
  display: flex; align-items: stretch;
  border: 1px solid var(--linha); border-radius: 8px; background: var(--superficie-2);
  overflow: hidden; transition: border-color 0.12s, box-shadow 0.12s;
}
.campo:focus-within { border-color: var(--color-primary); box-shadow: 0 0 0 3px rgba(10,79,54,0.14); }
.campo input, .campo select, .campo textarea {
  flex: 1; min-width: 0; border: 0; background: transparent;
  font: 13px var(--mono); color: var(--tinta); padding: 10px 12px;
}
.campo input:focus, .campo select:focus, .campo textarea:focus { outline: none; }
/* EXPORT-3 A2 (20/09/2026): o único `<textarea>` do sistema (Diferenciais do
   empreendimento, nos modais de criar/renomear) -- mesma caixa `.campo` dos
   demais campos, só que ela ESTICA em vez de rolar por dentro, porque o
   texto comercial é livre e a moldura fixa cortaria linha sem aviso. */
.campo textarea { resize: vertical; min-height: 92px; line-height: 1.4; }
/* MACRO-1 (14/09/2026): campo espelhando o cenário macro da empresa -- só
   leitura, mas em tinta legível como VALOR, nunca cinza-fantasma de
   placeholder (D-233 é sobre exatamente essa confusão). A causa vai na nota
   `.derivadas` ao lado, não na cor do campo sozinha. */
.campo input[readonly] { color: var(--tinta-2); cursor: default; }
/* PADRÃO DE CAMPO (FORM-1, item 1) — número à DIREITA dentro da caixa,
   unidade sempre FORA do valor (o `<i>` acima já cumpria a segunda regra;
   esta linha fecha a primeira). Só o VALOR NUMÉRICO alinha à direita — texto
   livre (nome, descrição, lista de curva) continua à esquerda, porque
   alinhar a leitura de um nome pela direita é o defeito que esta regra
   existe para não cometer. `select` fica de fora de propósito: opção de
   lista não é número. */
.campo input[type="number"], .campo input[data-dinheiro] { text-align: right; }
.tabela-edicao input[type="number"], .tabela-edicao input[data-dinheiro] { text-align: right; }
.campo i {
  font: 500 12px var(--mono); font-style: normal; color: var(--tinta-3);
  display: flex; align-items: center; padding: 0 10px;
  background: rgba(19,29,25,0.03); border-left: 1px solid var(--linha); white-space: nowrap;
}
.campo i:first-child { border-left: 0; border-right: 1px solid var(--linha); }
/* AQUI MORAVA `input::placeholder { color: var(--tinta-3) }`, e ele saiu na
   5d.2 (26/08) porque virou regra morta: nenhum campo desta casa carrega
   placeholder. A única exceção é a busca da tarja, que tem regra própria em
   `.tj-busca input::placeholder`, na §da tarja. Deixar a regra genérica de pé
   seria manter a roupa do estado proibido pendurada, pronta para vestir. */

.nota { font-size: 12px; color: var(--tinta-2); margin: 12px 0 0; }

/* CAMPO DERIVADO (T1.3): no modo vinculado o valor da personalizada vem do mix,
   e o servidor IGNORA o que o cliente mandar nele. O `readOnly` é o contrato; a
   aparência existe para o contrato ser LEGÍVEL — campo que parece editável e
   descarta o que se digita é a promessa mais barata de quebrar que existe.
   Fundo da superfície-2 e tinta-2: continua um valor, não um campo apagado. */
.tabela-edicao input.derivado {
  background: var(--superficie-2); color: var(--tinta-2); cursor: default;
}
.nota.alerta { color: var(--ambar); }
.nota.erro { color: var(--vermelho); }

/* VP-2 — A CÉLULA DO VALOR, NOS DOIS MODOS. Input em cima, a pílula
   tabela|negociado embaixo: o toggle mora na MESMA célula que ele decide, em
   vez de um controle solto na linha. `.seletor-modo` é o componente que a
   modalidade de vendas já usa (3 posições); aqui ganha um recorte menor para
   caber na largura de uma coluna de tabela. */
.pers-valor-cel { display: flex; flex-direction: column; gap: 4px; align-items: stretch; }
.pers-modo-pilula.seletor-modo { align-self: flex-start; }
.pers-modo-pilula button { padding: 3px 8px; font-size: 10.5px; }

/* VP-2, item 3 — O ESTUDO ANTIGO SEM `itens` ABRE COM PENDÊNCIA: produto e
   quantidade acendem até o dono digitar as unidades do pacote. Mesma paleta
   de `.campo.invalido`, aplicada direto ao select/input — estas duas células
   são coluna simples de tabela, sem o wrapper `.campo`. */
.tabela-edicao select.invalido, .tabela-edicao input.invalido {
  border-color: var(--vermelho-borda); background: var(--vermelho-claro);
}

/* TABELAS STICKY (FORM-1, item 6) — mix e investidores: cabeçalho e coluna
   de identidade presos durante a rolagem, mesma técnica já provada em
   `.fluxo-wrap` (D-263: `overflow: auto` + `max-height` no PRÓPRIO wrap,
   nunca sticky-na-página — `overflow-y: clip` não gruda como a spec sugere
   nesta combinação, medido no Chrome real). Larguras de coluna e o resto do
   `.tabela-edicao` ficam como estão — só a rolagem ganha moldura própria, e
   só quando a tabela é extensa o bastante para precisar dela. */
.tabela-sticky { overflow: auto; max-height: 58vh; border-radius: var(--raio); }
.tabela-sticky > table { margin-top: 0; }
.tabela-sticky thead th {
  position: sticky; top: 0; z-index: 2; background: var(--superficie-2);
}
.tabela-sticky td:first-child, .tabela-sticky th:first-child {
  position: sticky; left: 0; z-index: 1; background: var(--superficie);
}
.tabela-sticky th:first-child { z-index: 3; background: var(--superficie-2); }
/* A COLUNA DE AÇÃO ("×") PRESA À DIREITA (ressalva 2 do qualidade-b, DOBRA
   DIURNA-6 item 2). `largoConteudo` (P-40/d) deixou o campo "nome" crescer
   até o conteúdo de verdade ("Residencial vertical", "Investidor A"), e
   isso já não cabe no orçamento antigo das outras colunas -- a tabela passa
   a rolar por dentro (mesmo mecanismo que `#tab-inv` já usava em telas
   estreitas, "a tabela rolando por dentro"). Sem prender o `×`, ele ficava
   PARCIALMENTE fora da área visível em repouso (a rolagem existe, mas o
   dono não sabe disso olhando a tela). `tbody`/`thead` escopam a regra para
   fora do `<tfoot>` -- lá o último `<td>` é o total em `colspan`, não a
   ação, e prendê-lo à direita sobreporia a etiqueta ao número. */
.tabela-sticky tbody td:last-child, .tabela-sticky thead th:last-child {
  position: sticky; right: 0; z-index: 1; background: var(--superficie);
}
.tabela-sticky thead th:last-child { z-index: 3; background: var(--superficie-2); }
/* A CAIXA PRESA ENCOLHE ATÉ O BOTÃO (DOBRA 2 da DIURNA-6 item 2, martelo do
   qualidade-b: "4,4 px do VGV cobertos em repouso" · "a coluna inteira sob o
   sticky em 834" · "#tab-inv idem"). MEDIDO no Chrome real: `padding-right`
   na coluna VIZINHA NÃO ADIANTA — numa tabela `table-layout: auto` a coluna
   cresce para caber o padding extra (o texto certo alinhado à direita fica
   NO MESMO lugar absoluto; só a célula fica mais larga, empurrando o botão
   junto). Quem se move de verdade é o próprio "×": sem o `padding` de
   `.tabela-edicao td` (6px 4px), a caixa presa encolhe para o tamanho do
   BOTÃO (que já tem o próprio respiro clicável, `.remover-linha { padding:
   5px 8px }`) — de ~34px de zona pintada para ~25px. Medido em
   `elementFromPoint` (célula VGV, três larguras, início e fim da rolagem):
   fecha em zero px de sobreposição real. */
.tabela-sticky tbody td:last-child, .tabela-sticky thead th:last-child {
  padding: 0;
}
/* O ECO DE "MÊS PGTO." (`#tab-inv`) PRECISA DE UM SEGUNDO PASSO -- a caixa
   presa mais estreita não bastou (medido: em 834 a fatia visível do eco
   ["ABR/29", à esquerda por convenção] seguia embaixo do "×"). A causa é
   OUTRA: `.eco-mes` é `display: block` e nasce alinhado à ESQUERDA (a
   convenção do resto do sistema), então o pouco que sobra visível da coluna
   em telas estreitas é sempre o INÍCIO do texto -- o fim, que teria para
   onde fugir, já está fora da área rolável. Só NESTA coluna (a que encosta
   no "×") o eco troca de lado: à direita, ou o texto cabe inteiro, ou fica
   INTEIRO fora do recorte (rolagem normal, não sobreposição). Nenhum outro
   `.eco-mes` do sistema é tocado -- a regra é escopada à penúltima célula de
   UMA tabela `.tabela-sticky`. */
.tabela-sticky tbody td:nth-last-child(2) .eco-mes { text-align: right; }

/* SEÇÃO VAZIA QUE FALA, NÃO SUSSURRA. Bloco desligado é resposta e a ordem
   pede voz alta — mas seção vazia é ESTADO LEGÍTIMO, não alarme nem erro, e
   por isso ela não pega `.nota.alerta`/`.nota.erro` (cor de severidade que o
   dado não tem). Tamanho e tinta de CORPO, herdados do `body` (14px,
   var(--tinta)): a diferença para `.nota` é só essa — nada de negrito, caixa
   alta, ícone ou borda. */
.aviso-secao { font-size: 14px; color: var(--tinta); margin: 12px 0 0; }

/* AUSÊNCIA PRÓPRIA (FORM-1, item 3) — bloco desligado é resposta (D-233), e a
   frase substitui o vazio que hoje simplesmente não diz nada quando o corpo
   condicional fica `hidden`. Tom NEUTRO (`--tinta-2`), sem ícone e sem borda
   de severidade: isto não é aviso nem erro, é declaração. */
.aviso-ausencia { font-size: 12.5px; color: var(--tinta-2); margin: 10px 0 0; }
/* Mesma frase, dentro de uma tabela: célula única ocupando a linha inteira,
   nunca uma célula vazia por linha (a diferença que a ordem pede). */
.tabela-edicao td.tabela-vazia {
  text-align: center; color: var(--tinta-2); font: 400 12.5px var(--display);
  padding: 14px 6px;
}

/* DATA DERIVADA (P6.3). Mono porque é DATA, e data é número — mesma família
   dos ecos de mês e da faixa viva. O `:empty` não precisa de regra: sem
   calendário o texto é string VAZIA e a caixa não pinta nada, que é o estado
   certo (nunca travessão, D-187). A cor é a terciária de propósito: derivada é
   LEITURA, e não pode competir com o campo que a produziu. */
.derivadas { font: 500 12px var(--mono); letter-spacing: 0.03em; color: var(--tinta-3); }
.nota .derivadas { margin-left: 4px; }
/* O derivado do INÍCIO (exercício fiscal) desceu para dentro do <label> em
   23/08 — derivado mora junto do campo de que deriva. A APARÊNCIA dele vem do
   bloco `.eco-mes, label > .derivadas` mais abaixo: os dois derivados do
   Cronograma partilham UMA regra, que é o que garante que partilhem a base. */

/* ---- ECO MM/AA DO CAMPO DE MÊS (B3, 23/08/2026) ----
   Todo campo de ENTRADA de mês ganha, abaixo, o mês real do calendário.
   Mesma família visual do `.derivadas`: mono (é data), terciária (é LEITURA e
   não pode competir com o campo que a produziu) e `font-style: normal` porque
   o elemento é um <i> — semanticamente o mesmo <i> que as unidades dos campos
   usam, e itálico aqui leria como placeholder.

   ⚠ SLOT DE ALTURA RESERVADA, E É A TÉCNICA ÚNICA DOS DERIVADOS (D-208,
   martelo do dono em 23/08). A regra em uma linha: **a altura não depende do
   conteúdo, então a linha de base é a mesma com e sem derivado — e o silêncio
   deixa de mover a tela.**

   `line-height` e `min-height` são o MESMO valor de propósito: cheio mede 14px
   e vazio mede 14px, então digitar a data não empurra nada. A primeira versão
   desta regra (manhã de 23/08) era inline no flanco do campo, sem reserva, e
   produziu as duas doenças que o dono viu: a etiqueta do Cronograma pulava de
   75,6px para 103,6px ao receber a data, e os dois derivados da mesma linha
   ficavam com topos diferentes (426 × 420) porque tinham sido inseridos por
   TÉCNICAS DIFERENTES — um bloco com margem, o outro inline embrulhando.
   *Duas técnicas para o mesmo papel é o defeito; a unificação é o conserto.*

   O `:empty` NÃO ganha regra, e agora por outro motivo: o slot vazio TEM de
   ocupar o lugar dele. Silêncio aqui é não PINTAR, não é não OCUPAR — sem
   INÍCIO o texto é string vazia e nada aparece, mas a reserva fica. */
.eco-mes,
label > .derivadas {
  display: block; margin-top: 4px;
  line-height: 15px; min-height: 15px;
  font: 500 12px/15px var(--mono); font-style: normal; letter-spacing: 0.03em;
  color: var(--tinta-3); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis;
}
/* A TERCEIRA PEÇA, e ela veio de uma medição que desmentiu a expectativa: com o
   slot reservado, as bases dos dois derivados do Cronograma ainda ficaram a
   426 × 424 — DOIS pixels. A causa não é o slot, é o CAMPO: `type="month"`
   renderiza 2px mais alto que `type="number"`, então derivados pendurados logo
   abaixo do campo herdam a diferença de quem está em cima deles.
   Ancorar no RODAPÉ da etiqueta resolve na raiz: `.grade label` já é coluna
   flex, e a grade estica todas as etiquetas à mesma altura, então `margin-top:
   auto` assenta o derivado na mesma base **qualquer que seja a altura do campo
   que o produziu**. É invariante, e não conserto de 2px — o dia em que um campo
   novo tiver outra altura, ele já nasce alinhado.
   Só dentro de `.grade`: na célula de tabela não há coluna flex nem etiquetas
   irmãs a igualar, e lá quem separa continua sendo o `margin-top` de 4px. */
.grade > label > .eco-mes,
.grade > label > .derivadas { margin-top: auto; }

/* B2 (DOBRA, D-263, bloqueante do revisor do P-18): com o piso de 12px
   (D-401) dois derivados passam da largura da própria etiqueta e a
   ELLIPSIS cortava exatamente o dado que a etiqueta existe para mostrar --
   "1º exercício: 12m até DEZ…" sem o mês, "linear · 24 × R$ 2.813 · OUT…"
   sem o fim do intervalo. Só estes dois (nenhum outro derivado do sistema
   muda de tamanho por esta regra): quebram para DUAS linhas em vez de
   aparar, e o slot reserva SEMPRE as duas -- nunca uma só --, porque a
   promessa do D-208 é que a altura NÃO DEPENDE DO CONTEÚDO. Reservar uma
   linha faria o texto vazio medir menos que o cheio, e o slot voltaria a
   empurrar a etiqueta quando o dono digitasse a data (a régua que o
   check_cronograma mede em 4c: "a etiqueta mede o mesmo com e sem
   derivado"). */
#cr-fiscal,
#o-manut-cal-eco {
  white-space: normal; overflow: visible; text-overflow: clip;
  line-height: 15px; min-height: 30px;
}
/* O PAR DA MESMA LINHA (app.js, `elementoDoEco`): o eco do Lançamento nunca
   precisa de DUAS linhas (o texto é sempre curto), mas precisa reservar a
   MESMA altura que `#cr-fiscal` -- os dois vivem na mesma grade e a grade
   estica as duas etiquetas para a MESMA altura (D-208); com pisos diferentes
   os dois `margin-top: auto` bottom-ancoram em bases diferentes. */
.eco-mes-par-fiscal { min-height: 30px; }

/* MARCOS DO CRONOGRAMA EM LINHA (FORM-1, item 2; MARCOS-1, 14/09/2026) — cinco
   pontos do calendário lado a lado na seção 01: "Estudo M1 · JAN/26 ·
   Lançamento M4 · ABR/26 · Obra começa M6 · JUN/26 · Obra termina M33 ·
   Habite-se M34". Nenhum cálculo novo: cada marco lê um campo já digitado
   (início, lançamento, início/duração da obra) ou o PONTO PUBLICADO pelo
   motor (habite-se, D-320) — `montarMarcosCronograma` no app.js. Vazio até o
   primeiro marco existir (`:empty`, D-076: silêncio é ausência de dado, não
   de moldura). Leitura portada do `doma-periodo` do design-codex para os
   tokens da casa (verde, tinta, linha, mono/display).
   MARCOS-1: a data fica no MESMO EIXO do título (alinhada à esquerda, não
   mais solta à direita) e nunca quebra em duas linhas — a nota do Habite-se
   ("do último cálculo") saiu para um elemento próprio abaixo dela. A grade é
   `auto-fit`: cinco colunas quando cabem, senão o wrap desce para duas linhas
   de marcos — a fonte nunca encolhe (D-401). A GRADE É PARTILHADA COM A
   FAIXA DO PERÍODO DA OBRA (P-22, 14/09/2026, adiante nesta folha):
   `#o-periodo` passou a receber o MESMO padrão dos marcos — a régua
   (`auto-fit`, colunas de 150px) é uma casa só, e duas cópias divergiriam
   na próxima mudança de regra (D-076). O que cada faixa mantém PRÓPRIO é a
   moldura (borda, fundo, o acento verde da Obra) — ver `.periodo-obra`
   adiante. */
.marcos-periodo, .periodo-obra {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 14px 16px;
}
.marcos-periodo {
  margin-top: 14px; padding: 14px 16px;
  border: 1px solid var(--linha); border-radius: 8px; background: var(--superficie-2);
}
.marcos-periodo:empty { display: none; }
.marcos-periodo__item { min-width: 0; border-top: 2px solid var(--verde); padding-top: 8px; }
.marcos-periodo__item b {
  display: block; font: 600 10px var(--display); text-transform: uppercase;
  letter-spacing: 0.04em; color: var(--verde-escuro);
}
.marcos-periodo__item span {
  display: block; margin-top: 4px; font: 500 12px var(--mono);
  color: var(--tinta); text-align: left; white-space: nowrap;
}
.marcos-periodo__nota {
  display: block; margin-top: 2px; font: 500 12px var(--mono);
  color: var(--tinta-2);
}

/* ---- O BLOCO DOS CENÁRIOS NO RESULTADO (CEN.5b, 25/08/2026) ----
   A grade de deltas desceu da 10ª seção das Premissas para cá. Ela é `.cartao`
   como qualquer bloco da casa, mas o cabeçalho é DIFERENTE de propósito
   (martelo 9.2): rótulo só, sem eyebrow e sem `h2`. *Eyebrow e `h2` são a
   gramática de SEÇÃO DE STEPPER — vesti-los aqui ensinaria uma estrutura que
   não existe nesta aba.* Daí o `h3`, que é um degrau abaixo do título de seção
   e um acima da nota, e o `.descricao` reaproveitado sem o `.cartao-cab` (que
   traz a régua divisória de seção, e aqui não há seção a dividir).
   A margem inferior separa a grade da pílula sem inventar régua nova. */
.bloco-cenarios { margin-bottom: 18px; }
.bloco-cenarios h3 {
  font: 800 18px/1.1 var(--display); letter-spacing: -0.02em;
  margin: 0; color: var(--tinta);
}
.bloco-cenarios .descricao {
  margin: 6px 0 10px; color: var(--tinta-2); font-size: 13px; max-width: 62ch;
}

/* ---- A GRADE COMPACTA (CEN-1, NOTURNO-7 item 4, prancha D do Codex) ----
   Herda o espírito da CEN.5c-A (linha de Premissas era grande demais para
   bloco de apoio) e vai mais longe: cada `<tr>` vira uma LINHA de 52px
   (56px no toque, `BP_IPAD`), com o sinal numa faixa FIXA à esquerda do
   campo, o "%" numa faixa FIXA à direita, e o `<input>` no meio. O respiro
   sai do RESPIRO ENTRE linhas — nunca do campo, que continua com min-height
   confortável para o dedo (40/44px).

   ⚠ O SLOT DO ERRO MUDOU DE REGIME, E A MUDANÇA É DELIBERADA. Até aqui ele
   era `min-height` fixo, sempre reservado (nunca empurra, D-208). A grade
   compacta não tem esse respiro de sobra: o `.erro-campo` some por completo
   quando vazio (`:empty { display: none }`) e GANHA altura só quando há
   mensagem, com `overflow: visible` — é o ÚNICO caso em que a linha deixa de
   ter 52/56px. A mensagem nunca é cortada (é célula de tabela: o conteúdo
   que não cabe no mínimo especificado FAZ a célula crescer, e a linha cresce
   com ela). Isto RETARGETA por comportamento o passo 10 do `check_cenarios`,
   que hoje mede o slot como invariante — declarado no relatório da fatia,
   não corrigido aqui (D-335: retarget de guardião não é ajuste calado).

   A SUB-LINHA "Sem deslocamento declarado" (primeiro `<td>`, `.cen-sem-delta`)
   SEGUE A DOUTRINA VELHA: ela É reserva de slot, sempre presente, só o texto
   alterna — porque ali NÃO há razão para a linha crescer (D-076/D-208).

   TUDO ESCOPADO EM `#tab-cenarios`: as outras seis tabelas de edição são de
   PREMISSA, onde a densidade atual é a certa. Mexer na regra genérica seria
   apertar a casa inteira para resolver um bloco. */
#tab-cenarios { margin-top: 2px; }
#tab-cenarios th { padding: 5px 6px; }
#tab-cenarios td { padding: 6px 4px; height: 52px; vertical-align: middle; }
#tab-cenarios tbody td:first-child { vertical-align: top; padding-top: 8px; }
#tab-cenarios .campo { min-height: 40px; }
#tab-cenarios .campo input { padding: 0 10px; font: 600 16px var(--mono); }
/* O PREFIXO (sinal, U+2212/"+") E O SUFIXO ("%") EM FAIXAS FIXAS — a largura
   não depende do glifo, e é isso que mantém a coluna estável entre pessimista
   e otimista, campo com e sem valor. */
#tab-cenarios .campo i:first-child {
  width: 26px; padding: 0; justify-content: center;
  font: 700 16px var(--mono); color: var(--tinta);
}
#tab-cenarios .campo i:last-child {
  width: 30px; padding: 0; justify-content: center;
  font: 500 12px var(--mono);
}
/* A SUB-LINHA DO NOME — slot reservado (D-208): line-height/min-height
   amarrados, texto vazio ou frase, nunca escondida (a diferença para o
   `.erro-campo` está no comentário acima). */
.cen-sem-delta {
  display: block; margin-top: 2px; min-height: 14px;
  font: 500 12px/14px var(--display); font-style: normal; color: var(--tinta-2);
}
#tab-cenarios .erro-campo {
  display: block; overflow: visible; margin-top: 3px;
  line-height: 15px;
  font: 500 12px/15px var(--mono); font-style: normal; letter-spacing: 0.01em;
  color: var(--vermelho);
}
#tab-cenarios .erro-campo:empty { display: none; margin-top: 0; }
@media (max-width: 1100px) {
  #tab-cenarios td { height: 56px; }
  #tab-cenarios .campo { min-height: 44px; }
}
/* A INSTRUÇÃO CURTA, sempre visível, embaixo da grade — classe própria (não
   `.nota`, para a `.nota` de dentro do `<details>` continuar sendo a ÚNICA
   com esse nome no bloco, que é a que `check_cenarios` lê no passo 2). */
.cen-instrucao { font-size: 12px; color: var(--tinta-2); margin: 10px 0 0; }
.bloco-cenarios .nota { margin-top: 8px; }

/* ---- CAMPO INVÁLIDO INLINE (CEN.5a, 25/08/2026) — o PRIMEIRO da casa ----
   Até aqui TODO erro de campo ia para o painel `#pendencias`, e o usuário
   voltava ao campo pelo link de seção. Isso bastava enquanto campo e painel
   moravam na MESMA tela. A CEN.5b os separa em abas diferentes, e lá o link nem
   existe: `secaoDoCampo` procura um `.cartao[id]` ancestral, que o Resultado não
   tem. *O alerta tem de nascer na mão que puxa a alavanca* (M4-2 do D-074).

   ⚠ ESCOPO DECLARADO (D-224): ele nasce para o DELTA DE CENÁRIO. Generalizar
   para os outros campos é martelo futuro, e NÃO precedente automático — a casa
   tem dezenas de campos cujo erro segue indo, de propósito, para o painel.

   A BORDA MORA NA CAIXA, e não no input, porque é a caixa que a desenha:
   `.campo input` tem `border: 0`, então pintar o input não moveria um pixel. O
   `aria-invalid` é que fica no input, que é o que o leitor de tela consulta.

   A MENSAGEM USA A TÉCNICA ÚNICA DOS DERIVADOS (D-208): slot de altura
   reservada, com `line-height` e `min-height` amarrados, para que acender o erro
   NÃO empurre a tela. *Duas técnicas para o mesmo papel é o defeito; aqui a
   unificação vale desde o nascimento em vez de vir por conserto.*
   A reserva é de DUAS linhas porque as frases não cabem em uma na largura de uma
   célula da grade de cenários. Reservar UMA daria o defeito do F4 pelo outro
   lado: mensagem aparada é mensagem ilegível, e mensagem ilegível não cumpre o
   que o erro inline existe para cumprir.

   ⚠ LACUNA DECLARADA, e ela é minha: a reserva de duas linhas foi medida em UMA
   largura só, a do `check_cenarios`, que não varia viewport. O D-210 manda medir
   geometria nas TRÊS (810 · 1024 · 1400), *porque layout de tabela não é
   monótono e a coluna pode encolher num container maior*. A CEN.5b já traz a
   medição em três larguras por ordem da mesa, e é lá que esta reserva será
   conferida. Até então, o que está provado é que a mensagem cabe inteira na
   largura medida, e não em todas. */
.campo.invalido {
  border-color: var(--vermelho-borda);
  background: var(--vermelho-claro);
}
.campo.invalido:focus-within {
  border-color: var(--vermelho);
  box-shadow: 0 0 0 3px rgba(166, 59, 42, 0.12);
}
.erro-campo {
  display: block; margin-top: 4px;
  line-height: 15px; min-height: 30px;
  font: 500 12px/15px var(--mono); font-style: normal; letter-spacing: 0.01em;
  color: var(--vermelho); overflow: hidden;
}

/* LÁPIDE — `#o-periodo { line-height: 14px; min-height: 14px; }` morava
   aqui (OBRA-2, M5) e reservava a altura de uma linha de `.nota` simples: a
   MESMA técnica ("a altura não depende do conteúdo") que a faixa continua
   usando, mas calibrada para um texto corrido, não para a caixa com
   padding/borda que a faixa é desde o martelo C4 (09/09/2026). MUDOU DE
   CASA, não sumiu: a reserva de altura mora agora em `.periodo-obra`
   (adiante neste arquivo), com o número recalibrado para a caixa nova —
   14px era pouco demais e nunca teria efeito ali (D-225: mudança de casa
   não pode virar remoção calada). */

/* A OUTRA METADE DA TÉCNICA, e sem ela a de cima não resolve a TABELA: célula
   de tabela alinha por `middle` no default do navegador, então a célula do mês
   — que é a única que cresce com o slot — puxava os inputs das colunas vizinhas
   para o CENTRO da linha mais alta. Era esse o "eco desalinhou a linha" que o
   dono viu em `#tab-pers`. Com `top`, todos os inputs da linha partem da mesma
   origem, e o slot cresce para BAIXO, onde não há ninguém para empurrar. */
.tabela-edicao td { vertical-align: top; }

/* ---- SUBTÓPICO: os blocos de premissa dentro de um cartão ----
   Três peças, e cada uma faz um trabalho:
     RECUO de 18px ..... o bloco começa depois da margem do cartão, e é o
                         recuo que diz "isto está DENTRO daquilo";
     RÉGUA de 3px ...... liga o bloco à coluna do cartão (ver `--regra-sub`);
     FAIXA de cabeçalho  em `--superficie-2`, com o título em 11,5px/700
                         CAIXA ALTA — a razão de 2,2× contra os 25px do
                         título do grupo.
   O fio que fecha a faixa mora no TOPO DO CORPO e não na base do cabeçalho:
   com o bloco desligado o corpo é `hidden`, e um fio na base do cabeçalho
   ficaria encostado na borda de baixo do bloco desenhando 2px de linha. */
.bloco-aquisicao {
  border: 1px solid var(--linha); border-left: 3px solid var(--regra-sub);
  border-radius: 0 10px 10px 0; margin: 0 0 16px 18px; overflow: hidden;
}
.bloco-aquisicao-cab {
  display: flex; align-items: center; gap: 12px;
  padding: 13px 18px; background: var(--superficie-2);
}
.bloco-aquisicao > .grade, .bloco-aquisicao-corpo { padding: 18px; border-top: 1px solid var(--linha); }
.bloco-aquisicao-corpo .grade { margin-bottom: 4px; }
/* TELA-3, item 1 — O SELECT "COMO REGISTRAR" AINDA CORTAVA a segunda opção
   ("% do VGV (estudo preliminar)"), a mais longa das duas: dimensionado pela
   PRIMEIRA, ele só tinha a largura da coluna da grade (~253px). MEDIDO por
   clone off-screen do PRÓPRIO `<select>` (mesma classe `.campo select`, uma
   `<option>` por vez, `width: auto`) — a largura INTRÍNSECA que o Chrome
   reserva, texto + padding + área nativa da seta, sem estimar termo nenhum à
   parte: opção 1 pede 255px, opção 2 pede 263px. `width` explícito no item da
   grade (e não no `<select>`) é o que faz efeito: `.campo` tem
   `overflow: hidden`, então alargar só o `<select>` o mantinha CLIPADO dentro
   de um `.campo` que não cresceu junto. A COLUNA VIZINHA (`#pf-wrap-pct`, só
   visível no modo "percentual") não se move: ela mora no track seguinte da
   grade, e o item alargado só transborda para o respiro vazio ao lado —
   medido nas duas visibilidades, sem sobreposição.
   HABITE-1, PARTE B, item (3) — O `266px` MORREU. Era número sem guarda
   nenhuma (zero referência em `tools/`), e a revisão da própria TELA-3
   plantou uma opção mais longa sem que nada acusasse: o corte volta no dia
   em que o texto de uma opção crescer de novo. A saída é a DERIVAÇÃO CSS já
   medida naquela fatia — `width: max-content`, `width: fit-content` e
   `min-width: max-content` deram os MESMOS 263px, os seis itens verdes.
   Escolhida `min-width: max-content`: ao contrário de `width`, ela não fixa
   um tamanho absoluto — deixa a caixa crescer com o conteúdo e nunca
   encolher abaixo dele, mesmo que a grade tente comprimir o item. */
#pf-wrap-modo { min-width: max-content; }
.tag { font: 700 11.5px var(--display); letter-spacing: 0.1em; text-transform: uppercase; }
.tag-fixa { color: var(--tinta); }
/* O interruptor É o título do subtópico quando o bloco liga e desliga: mesma
   régua da `.tag`, para "DINHEIRO" e "PERMUTA FÍSICA" lerem no mesmo posto.
   O `<em>` ao lado sai da caixa alta — é legenda, não título. */
.bloco-aquisicao-cab > .toggle { font: 700 11.5px var(--display); letter-spacing: 0.1em; text-transform: uppercase; }
.bloco-aquisicao-cab > .toggle em {
  font: 400 12px var(--display); font-style: normal;
  letter-spacing: normal; text-transform: none; color: var(--tinta-2);
}
/* legenda do bloco SEM interruptor (Dinheiro, Custos de aquisição): mesmo
   posto do `<em>` do interruptor. `font-style: normal` é obrigatório — não há
   arquivo itálico da Inter nesta casa (ver §0), e o navegador inclinaria o
   desenho reto por conta própria. */
.bloco-aquisicao-cab > em {
  font: 400 12px var(--display); font-style: normal; color: var(--tinta-2);
}
.bloco-total { margin-left: auto; font: 500 13px var(--mono); color: var(--verde-escuro); white-space: nowrap; }

.toggle { display: flex; align-items: center; gap: 10px; cursor: pointer; font: 600 13px var(--display); color: var(--tinta); }
.toggle em { font: 400 12px var(--display); font-style: normal; color: var(--tinta-2); }
.toggle input { position: absolute; opacity: 0; width: 0; height: 0; }
.toggle-visual { width: 34px; height: 20px; border-radius: 999px; background: #c8d1cd; position: relative; flex-shrink: 0; transition: background 0.15s; }
.toggle-visual::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px;
  border-radius: 50%; background: var(--doma-branco); transition: transform 0.15s; box-shadow: 0 1px 2px rgba(0,0,0,0.2);
}
.toggle input:checked + .toggle-visual { background: var(--verde); }
.toggle input:checked + .toggle-visual::after { transform: translateX(14px); }
.toggle input:focus-visible + .toggle-visual { outline: 2px solid var(--verde); outline-offset: 2px; }
/* ---- MODALIDADE: o interruptor que troca uma premissa por outra (R3.5) ----
   Hoje só as vendas por safra. Não é `.bloco-aquisicao`, e a diferença é de
   sentido: aquele é um subtópico que LIGA e DESLIGA (pró-soluto, financiamento,
   correção — desligado, o assunto some do estudo). Este ESCOLHE entre duas
   descrições da mesma premissa, e uma delas está sempre valendo. Por isso vem
   sem régua lateral e sem recuo: ele não está dentro de nada, é a cabeça da
   seção.

   O campo do modo vigente mora na MESMA faixa do interruptor porque é a mesma
   decisão — e some inteiro quando o outro modo vale (`hidden`), sem cinza e sem
   desabilitado. O `flex-wrap` é o que segura os dois lado a lado no iPad e os
   empilha na coluna estreita, sem mexer na `.grade` da seção. */
.modalidade {
  display: flex; align-items: center; gap: 12px 22px; flex-wrap: wrap;
  padding: 13px 16px; margin-bottom: 18px;
  background: var(--superficie-2); border: 1px solid var(--linha); border-radius: 10px;
}
.modalidade > .toggle { flex: 1 1 320px; }
/* O NOME DO INTERRUPTOR NÃO QUEBRA. Ele é o título da decisão, e num flex ele
   seria o item que encolhe primeiro: "Vendas por safra" saía em duas linhas ao
   lado de uma legenda inteira numa só. A legenda é quem cede espaço. */
.modalidade > .toggle > b { font: inherit; white-space: nowrap; }
.modalidade > .toggle > em { flex: 1 1 260px; }
.modalidade > label:not(.toggle) {
  display: flex; flex-direction: column; gap: 6px;
  font-size: 12px; color: var(--tinta-2); font-weight: 500; min-width: 176px;
}
.modalidade > label:not(.toggle) > .campo { background: var(--superficie); }
/* O aviso da transição encosta no interruptor de propósito — é dele que fala —,
   e respira antes da grade de campos, que é outro assunto. */
.modalidade + .nota { margin: -8px 2px 18px; }

/* SELETOR DE MODALIDADE (VENDAS-1) — três posições, mesma família visual da
   pílula de cenários (`.cenarios`), simplificada: aqui não há sub-linha de
   número, só o nome do modo. */
/* TELA-3, item 4 — A GRADE EMPACOTA À ESQUERDA VALE TAMBÉM PARA A PÍLULA
   (D-303, a mesma regra única do dono): `flex: 1 1 320px` fazia a pílula
   CRESCER para ocupar toda a sobra de `.modalidade` (era o único item com
   `flex-grow`, já que `#v-wrap-prazo` não declara grow nenhum) — os três
   botões ficavam encostados à esquerda com vazio à direita, DENTRO da borda
   arredondada da pílula. `flex: none` faz a largura ser a do CONTEÚDO (a
   soma dos três botões + padding, medido pelo próprio `inline-flex` de
   `.seletor-modo`), alinhada à esquerda, sem esticar. Nenhum botão muda de
   tamanho: eles não dependiam da largura do pai. */
.modalidade > .seletor-modo { flex: none; }
.seletor-modo {
  display: inline-flex; border: 1px solid var(--linha); border-radius: 999px;
  overflow: hidden; background: var(--superficie);
}
.seletor-modo button {
  border: 0; background: transparent; padding: 8px 16px;
  font: 600 12px var(--display); color: var(--tinta-2); cursor: pointer;
}
.seletor-modo button.ativo { background: var(--color-primary); color: var(--doma-branco); cursor: default; }
.seletor-modo button:focus-visible { outline: 2px solid var(--color-primary); outline-offset: 2px; }
/* CATCH-UP DA EQUITY (FUNDOS-4b) — a única posição que se desabilita por
   PREMISSA (base % do VGV), não por escolha do dono: opacidade mais baixa e
   cursor recusado, mesma linguagem das opções desligadas do `.opcoes-em-linha`
   (D-208, "aviso escondido não é conserto" — aqui o botão fica visível e
   inerte, com a razão na nota ao lado). */
.seletor-modo button:disabled { opacity: 0.45; cursor: not-allowed; }

/* DESPESA-PRE (13/09/2026) — a prancha H do design-codex (revisao-selo-
   2026-09-13), PORTADA aos tokens da DESIGN-0 (D-401): nunca a casca do
   codex, só a geometria (grade de 3 colunas, resumo com chevron). O
   conteúdo inteiro de cada despesa mora num `<td colspan>`, então o layout
   aqui é de BLOCO e não de coluna de `.tabela-edicao`. */
.doma-despesa-pre-cel { padding: 0 !important; }
.doma-despesa-pre-resumo {
  display: grid; grid-template-columns: minmax(0, 1fr) 130px 120px 16px;
  align-items: center; gap: 12px; width: 100%; box-sizing: border-box;
  padding: 10px 6px; border: 0; background: transparent; cursor: pointer;
  font: inherit; color: inherit; text-align: left;
}
.doma-despesa-pre-resumo:hover { background: var(--color-bg); }
.doma-despesa-pre-resumo:focus-visible { outline: 2px solid var(--color-primary); outline-offset: -2px; }
.doma-despesa-pre-resumo-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.doma-despesa-pre-resumo-desc { font: 600 13px var(--display); }
.doma-despesa-pre-resumo-modo { font: 500 12px var(--display); color: var(--tinta-2); }
.doma-despesa-pre-resumo-total {
  font: 500 13px var(--mono); text-align: right; font-variant-numeric: tabular-nums;
}
.doma-despesa-pre-resumo-mes {
  font: 500 12px var(--mono); text-align: right; color: var(--tinta-2);
  font-variant-numeric: tabular-nums;
}
.doma-despesa-pre-chevron {
  font-style: normal; color: var(--tinta-3); transition: transform 0.12s; text-align: center;
}
.doma-despesa-pre-resumo[aria-expanded="true"] .doma-despesa-pre-chevron { transform: rotate(180deg); }

.doma-despesa-pre-detalhe {
  padding: 4px 6px 16px; border-top: 1px dashed var(--linha);
  display: flex; flex-direction: column; gap: 14px;
}
.doma-despesa-pre-pagto {
  display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
}
.doma-despesa-pre-rot { font: 600 12px var(--display); color: var(--tinta-2); }
.doma-despesa-pre-nota-parc { margin: 0; }

/* Interruptor ANINHADO — hoje só o regime a preços correntes, dentro da correção
   monetária. Ganha um fio que o separa dos campos do bloco, e NÃO vira bloco
   próprio: bloco próprio sugeriria que dá para ligá-lo sem a correção, e não dá
   (o schema recusa, e `lerCorrecao` nem chega a formar esse estado). */
.bloco-aquisicao-corpo > .toggle { margin-top: 16px; padding-top: 14px; border-top: 1px dashed var(--linha); }
.bloco-aquisicao-corpo > .toggle + div { margin-top: 14px; }

/* ---- opções em radio (regime de liberação, custo do dinheiro) ----
   O rótulo diz o que o regime FAZ, não só o nome: escolher "antecipada" sem
   saber custa juros que não aparecem em pendência nenhuma. */
.opcoes { border: 1px solid var(--linha); border-radius: 10px; margin: 14px 0 0; padding: 10px 14px 12px; }
.opcoes legend {
  font: 600 10.5px var(--display); letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--tinta-2); padding: 0 6px;
}
.opcao { display: flex; align-items: flex-start; gap: 10px; cursor: pointer; padding: 7px 8px; border-radius: 8px; }
.opcao + .opcao { margin-top: 2px; }
.opcao:hover { background: var(--superficie-2); }
.opcao input { margin: 3px 0 0; accent-color: var(--verde); flex-shrink: 0; }
.opcao b { display: block; font: 600 13px var(--display); color: var(--tinta); }
.opcao em { display: block; font-style: normal; font-size: 12px; color: var(--tinta-2); margin-top: 2px; max-width: 68ch; }
.opcao input:checked ~ span b { color: var(--verde-escuro); }
.opcao input:focus-visible { outline: 2px solid var(--verde); outline-offset: 2px; }
/* RETENÇÃO DENTRO DO QUADRO DA DEVOLUÇÃO (VENDAS-4, item 2) — a retenção é a
   PRIMEIRA linha do `<fieldset>` da devolução, e não mais um `<label>` da
   grade: `max-width` a mantém do tamanho de um campo comum, não da largura
   inteira do quadro (que atravessa a grade inteira, `grid-column: 1 / -1`).
   `margin-bottom` é o respiro que a separa das duas opções logo abaixo. */
.opcoes .campo-inline { max-width: 220px; margin-bottom: 10px; }
.opcoes .campo-inline-nota { margin: 0 0 4px; }

/* O MESMO FIELDSET, DENTRO DE UMA `.grade` (a "Devolução do distrato", na seção
   de vendas). Ele é o único item daquela grade que não é `label`, e por isso
   precisa declarar as duas coisas que os vizinhos ganham de graça:

   · `grid-column: 1 / -1` atravessa a linha inteira. Sem isso o fieldset cai
     numa coluna de 196px e as duas descrições viram torre de palavras. Ele
     também é o que mantém a leitura de cima para baixo: os três campos do
     distrato acima, o resto da forma de pagamento abaixo.

   · `margin: 0` porque o `.opcoes` de fora da grade nasce com `margin: 14px 0 0`
     para respirar do bloco irmão. Aqui o respiro é o `gap` da grade, e a margem
     SOMARIA a ele: a linha das opções ficaria 14px mais longe do campo de cima
     do que qualquer outra linha da mesma grade. É torto que nenhuma validação
     pega, porque nada fica inválido. */
.grade > .opcoes-em-grade { grid-column: 1 / -1; margin: 0; }
/* TOGGLE EM COLUNA CHEIA (DIURNA-8 item 1b, 16/09/2026) -- o mesmo problema
   do `.opcoes-em-grade` acima, achado no toggle `#corr-usar-macro`: sozinho
   na `.grade`, o item herdava a largura de uma coluna (minmax(196px,1fr)) e
   "Usar o cenário macro da empresa" quebrava em duas linhas a partir de
   ~918 px de card. `grid-column: 1 / -1` devolve a largura inteira.
   A segunda linha é OBRIGATÓRIA e não cosmética: `.grade label` (acima,
   ~1940) tem especificidade MAIOR que `.toggle` sozinho (uma classe + um
   tipo vence uma classe só) e empilhava o interruptor em COLUNA — achado
   medido (labelHeight 62px, o toggle-visual sobre o texto sobre o `<em>`)
   antes de existir tela nenhuma para conferir a régua de 918px. */
.grade > .toggle-cheio {
  grid-column: 1 / -1; display: flex; flex-direction: row; align-items: center;
  gap: 10px; font: 600 13px var(--display); color: var(--tinta);
}

.resumo-terreno {
  margin-top: 18px; padding: 14px 18px; border-radius: 10px;
  background: var(--verde-claro); border: 1px solid var(--verde-borda);
  font-size: 13px; color: var(--verde-escuro);
}
.resumo-terreno b { font-family: var(--mono); }
/* Sem negociação descrita a linha CALA — e calar tem de apagar a caixa verde
   junto, senão sobra um retângulo pintado com nada dentro, que é pior que a
   linha de zeros que ele substituiu. */
.resumo-terreno:empty { display: none; }

/* ---- OBRA-2, M6: A GRADE DE 3 COLUNAS FIXAS DA ABA OBRA ----
   `.grade` sozinha é auto-fill (`minmax(196px,1fr)`) e cabem 4 nesta largura
   de card — a ordem pede 3 colunas de verdade, então este modificador FIXA o
   número em vez de deixar o navegador decidir. Os `.grade-vazio` do HTML são
   o terceiro slot das linhas 1 a 3; a linha 4 já tem três campos e não
   precisa dele. */
.grade-3 { grid-template-columns: repeat(3, minmax(160px, 1fr)); }
@media (max-width: 760px) { .grade-3 { grid-template-columns: 1fr; } }

/* A FAIXA DO PERÍODO DA OBRA (martelo C4, 09/09/2026; conserto de geometria
   09/09/2026, DOBRA; fundo trocado 09/09/2026, TELA-4a) — LEITURA no topo da
   aba, nunca campo (a régua não muda: só a roupa). Fundo `--superficie-2`
   (o tom de fundo de `.curva-cab`, mesma variável) — NÃO é `--superficie`
   como a versão original desta faixa usava: contra o `.cartao` branco
   (`--superficie` também), o fundo `--superficie` ficava byte a byte igual
   ao cartão em volta e a faixa desaparecia (D-046, o ramo de fallback da
   ordem: "surface-1" == fundo da página é falso ao pé da letra, mas a faixa
   mora dentro do cartão branco, não da página, e ali a igualdade acontece
   mesmo assim). A borda esquerda verde é da MESMA família que `.resumo-obra`
   já usa logo abaixo — as duas são leitura sobre o mesmo bloco (obra), e a
   cor comum lê como "isto tudo é derivado". O acento assimétrico
   (`border-radius` só do lado direito) é o mesmo desenho de
   `.curva-ed`/`.chips-ed`: a barra colorida funciona como lombada, não como
   moldura inteira.

   A RESERVA DE ALTURA MORA AQUI, E SÓ AQUI (era `#o-periodo`, ver a lápide
   quatro telas acima) — `min-height: 86px`, MEDIDO no navegador real (CDP,
   estudo Gardens North com calendário, obra e habite-se completos) nas TRÊS
   larguras da ordem — 1720/1366/834 — com os quatro marcos cheios (início
   físico, fim físico, duração, habite-se com nota "(+N)"): a altura NATURAL
   do conteúdo cheio mede 84px nas três (o item mais alto, Habite-se, é quem
   define a linha inteira — a grade estica os outros três à mesma altura), e
   o número aqui tem de ficar ACIMA dela — se ficasse abaixo, é a CHEIA que
   cresceria além do mínimo e as duas voltariam a divergir, só que ao
   contrário. `box-sizing: border-box` é global nesta folha, então o
   `min-height` já inclui o padding e a borda: a caixa NUNCA fica mais baixa
   que isso, cheia ou vazia — é a MESMA técnica da lápide, só que calibrada
   para a caixa com QUATRO itens empilhados, não para três `<span>` numa
   linha só. */
.periodo-obra {
  margin: 14px 0 18px; padding: 12px 16px; min-height: 86px;
  background: var(--superficie-2); border: 1px solid var(--linha);
  border-left: 3px solid var(--verde-borda); border-radius: 0 var(--raio) var(--raio) 0;
}
/* VAZIA, ELA NÃO SOME — FICA TRANSPARENTE (conserto da DOBRA, 09/09/2026).
   `display: none` apagava a reserva de altura inteira (o slot empurrava a
   grade 44px, uma versão PIOR do defeito de 23/08 que a lápide acima
   existe para evitar) porque o antigo `#o-periodo` media 14px de reserva —
   pequeno demais para nunca ter efeito real nesta caixa. A saída não é
   escolher entre "não pinta" e "não empurra": as DUAS regras da ordem valem
   ao mesmo tempo — só as CORES desaparecem (fundo e as duas bordas), a
   CAIXA (padding, borda, `min-height`, margem) fica exatamente igual à
   cheia. Nenhuma outra propriedade é tocada aqui de propósito: zerar
   `padding`/`border`/`margin` é voltar a fazer o slot empurrar. */
.periodo-obra:empty { background: transparent; border-color: transparent; }
/* LÁPIDE (P-22, 14/09/2026) — `.periodo-obra-civil`, `.periodo-obra-meio` e
   `.periodo-obra-habite` moravam aqui (martelo C4, 09/09/2026): três
   `<span>` soltos, texto corrido, sem o padrão dos marcos. A ORDEM da Mesa
   mandou vestir `#o-periodo` com o MESMO componente de `.marcos-periodo`
   (`montarFaixaPeriodoObra` no app.js passou a emitir `.marcos-periodo__item`
   via `itemMarco`, a peça que também monta `#cr-marcos`) — os três nomes
   somem daqui porque a tela não os escreve mais, e classe sem uso na tela é
   a mesma prosa morta que o `check_placeholder`/`tracos_declarados` já
   caçam noutra forma (D-225: mudança de casa não é remoção calada — a
   informação continua de pé, só que como `.marcos-periodo__item`). */

/* A mesma "faixa verde de fecho" do `.resumo-terreno`, com nome próprio: é
   OUTRO card (Obra), e o D-233/D-225 já ensinaram que duas casas para o
   mesmo fato divergem sozinhas — ficar preso ao mesmo seletor faria uma
   mudança de estilo do Terreno mexer na Obra sem ninguém pedir. */
.resumo-obra {
  margin-top: 18px; padding: 14px 18px; border-radius: 10px;
  background: var(--verde-claro); border: 1px solid var(--verde-borda);
  font-size: 13px; color: var(--verde-escuro);
}
.resumo-obra b { font-family: var(--mono); }
.resumo-obra:empty { display: none; }

/* A MESMA "faixa verde de fecho", com nome próprio de novo (FUNDOS-2): outro
   card (Fundos), mesma razão do `.resumo-obra` sobre o `.resumo-terreno` —
   mudança de estilo num card não pode mexer no outro por acaso. Lê SÓ o
   publicado em `resultado.fundos` (D-076); vazia até haver cálculo válido, e
   `:empty` some com ela sem empurrar a caixa. */
.resumo-fundos {
  margin-top: 18px; padding: 14px 18px; border-radius: 10px;
  background: var(--verde-claro); border: 1px solid var(--verde-borda);
  font-size: 13px; color: var(--verde-escuro);
}
.resumo-fundos b { font-family: var(--mono); }
.resumo-fundos:empty { display: none; }
/* PENDÊNCIA DENTRO DA FAIXA VERDE (FUNDOS-4b, equity na SPE) — a única das
   cinco faixas que pode carregar uma causa de a cascata não fechar mesmo com
   o cálculo válido (preferência, catch-up ou MOIC não cobertos). Bloco
   próprio, nunca inline no meio da frase: a cor vermelha precisa CHAMAR mais
   que o texto verde ao redor, e travessão/parêntese no meio da frase
   diluiria o aviso (D-224/D-187). */
.resumo-pendencia { display: block; margin-top: 6px; color: var(--vermelho); font-weight: 600; }

/* ---- medidor de composição do preço ---- */
.medidor { margin-top: 6px; padding: 14px 16px; border-radius: 10px; background: var(--superficie-2); border: 1px solid var(--linha); }
.medidor.excede { background: var(--vermelho-claro); border-color: var(--vermelho-borda); }
.medidor-topo {
  display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
  font: 600 12px var(--display); color: var(--tinta-2);
}
.medidor-formato { font: 600 10.5px var(--display); text-transform: uppercase; letter-spacing: 0.08em; color: var(--tinta-3); }
.medidor-linha { margin-top: 12px; }
.medidor-linha + .medidor-linha { border-top: 1px dashed var(--linha); padding-top: 12px; }
.medidor-linha.velha { opacity: 0.55; }
.medidor-rot { display: flex; align-items: baseline; gap: 8px; font: 600 13px var(--display); color: var(--tinta); }
.medidor-rot em { font: 400 11.5px/1.3 var(--display); font-style: normal; color: var(--tinta-3); }
.medidor-rot .medidor-soma { margin-left: auto; }
.medidor-soma { font: 500 15px var(--mono); color: var(--verde-escuro); }
.medidor.excede .medidor-soma, .medidor.excede .medidor-topo { color: var(--vermelho); }
.medidor-linha.vazia .medidor-soma { color: var(--tinta-3); }
.medidor-barra { display: flex; height: 14px; border-radius: 7px; overflow: hidden; background: #e6ebe8; margin: 10px 0; }
.medidor-barra span { display: block; height: 100%; }
.medidor-legenda { display: flex; flex-wrap: wrap; gap: 5px 18px; font-size: 12px; color: var(--tinta-2); }
.medidor-legenda span { display: inline-flex; align-items: center; gap: 6px; }
.medidor-legenda i { width: 9px; height: 9px; border-radius: 3px; flex-shrink: 0; font-style: normal; }
.medidor-legenda b { font: 500 12px var(--mono); color: var(--tinta); }
.medidor-legenda .delta { font: 500 12px var(--mono); font-style: normal; color: var(--ambar); }
.medidor-notas { margin: 8px 0 0; padding-left: 16px; font-size: 12px; line-height: 1.5; color: var(--tinta-2); }
.medidor-notas li { margin-top: 2px; }

/* ---- EDITOR DE CURVAS: colunas arrastáveis + farol ----
   Duas vistas do mesmo campo, e a hierarquia diz isso: o editor é uma peça com
   moldura e cabeçalho próprio; o campo de texto que vem logo abaixo (`.curva-
   atalho`) recua para rótulo pequeno — continua editável, continua sendo o que
   o app lê, e para de competir com o desenho pela atenção.

   O ALVO DE TOQUE É A COLUNA INTEIRA, não a barra: `.curva-col` ocupa a altura
   toda da caixa e o `::after` cobre a área inteira, então tocar no vazio acima
   de uma barra de 2% já pega aquela coluna. `touch-action: none` é requisito e
   não capricho — sem ele o iPad ROLA A PÁGINA em vez de arrastar, e o editor
   fica inútil justamente no aparelho em que o dono decide. */
.curva-ed, .chips-ed {
  border: 1px solid var(--linha); border-left: 3px solid var(--regra-sub);
  border-radius: 0 10px 10px 0; margin: 16px 0 0 18px; overflow: hidden;
  background: var(--superficie);
}
.curva-cab, .chips-cab {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  padding: 12px 18px; background: var(--superficie-2);
}
.curva-cab b, .chips-cab b {
  font: 700 11.5px var(--display); letter-spacing: 0.1em; text-transform: uppercase; color: var(--tinta);
}
.curva-info, .chips-info { font: 400 12px var(--display); color: var(--tinta-2); }
/* DESIGN-3, item 02 — a marca "Base" (0%): não é chip de variação (o zero
   segue descartado da lista), é só o marcador visual de referência. */
.sens-base-marca {
  font: 600 11px var(--display); text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--color-primary); border: 1px solid var(--color-primary);
  border-radius: 999px; padding: 2px 9px;
}
/* O FAROL. Quatro estados e nenhum deles é decorativo:
     ok ..... a soma fecha (verde);
     alerta . fecha por baixo — premissa válida que o motor vai comentar (âmbar);
     erro ... o motor VAI recusar, ou há token ilegível (vermelho);
     vazio .. não há série declarada (neutro, e não é falha). */
.curva-farol {
  margin-left: auto; font: 500 12.5px var(--mono); padding: 3px 11px; border-radius: 999px;
  border: 1px solid var(--linha); background: var(--superficie); color: var(--tinta-2);
  font-variant-numeric: tabular-nums;
}
.curva-farol.ok { background: var(--verde-claro); color: var(--verde-escuro); border-color: var(--verde-borda); }
.curva-farol.alerta { background: var(--ambar-claro); color: var(--ambar); border-color: var(--ambar-borda); }
.curva-farol.erro { background: var(--vermelho-claro); color: var(--vermelho); border-color: var(--vermelho-borda); }

.curva-corpo, .chips-corpo { padding: 16px 18px 14px; border-top: 1px solid var(--linha); }
.curva-recado, .chips-recado { margin: 0; font-size: 12.5px; color: var(--tinta-2); }
.curva-recado.erro, .chips-recado.erro { color: var(--vermelho); }

/* DESIGN-7/ADENDO (D3) — a sensibilidade é a ÚNICA vista de `.chips-ed` que
   vira LINHA e não caixa: `montarChipsSensibilidade` (app.js) para de gerar
   `.chips-cab`/`.chips-corpo` (o cabeçalho-faixa some) e escreve os cinco
   segmentos (marca, chips, presets, contagem, farol) direto como filhos de
   `#sens-ed`.
   `display: contents` — e NÃO `display: flex` — de propósito, e a razão é
   medida: um `#sens-ed` com `display:flex` próprio calcula a LARGURA
   PREFERIDA como se os cinco segmentos NUNCA quebrassem entre si (a
   intrínseca de um contêiner flex multi-linha é a largura de UMA linha só),
   e essa largura hipotética "vaza" para a decisão de quebra da `.sens-acoes`
   -- o botão "Rodar" e o "editar lista…" caem para uma SEGUNDA linha mesmo
   quando os quatro segmentos medidos pela régua caberiam juntos. Com
   `display:contents` o `#sens-ed` some da árvore de caixas e seus FILHOS
   (marca, chips, curva-acoes, chips-info, farol) viram itens FLEX DIRETOS de
   `.sens-acoes`, cada um quebrando pela própria largura -- sem essa
   caixa-fantasma no meio. `#sens-ed .chip`/`.chips-info` continuam
   endereçáveis por seletor (display:contents não tira o elemento do DOM). */
#sens-ed { display: contents; }
#sens-ed .curva-acoes { margin-top: 0; gap: 6px; }
/* A CONTAGEM ("N variações") e o FAROL ("N × M = P linhas") saem da
   disputa pela LINHA 1: `order` maior os manda para DEPOIS do botão "Rodar"
   na ordenação flex (a `.sens-acoes` inteira, não só o que estava dentro do
   `#sens-ed`), e `flex-basis: 100%` garante que eles caiam numa linha
   PRÓPRIA, nunca espremidos entre os quatro segmentos medidos. Continuam no
   DOM, só o lugar na fileira muda -- a régua não mede a posição deles. */
#sens-ed > .chips-info, #sens-ed > .curva-farol {
  order: 1; flex-basis: 100%; margin-top: 4px;
}
/* DESIGN-8, item 3c — a conta "N × M = P linhas" subiu para o subtítulo
   "02. VARIAÇÕES" (`[data-sens-contagem]`); a faixa cheia some SÓ AQUI
   (continua no DOM e continua sendo escrita por `desenhar()`, porque
   `.curva-farol` é a mesma classe que `#ed-obra`/`#ed-vendas` usam e
   `check_curvas.mjs` lê — a régua não mede este id). */
#sens-ed > .curva-farol { display: none; }
/* Pílulas de preset e "limpar" mais compactas -- SÓ aqui (`#sens-ed`, único
   consumidor de `.curva-acoes .btn-mini` fora dos editores de curva de obra/
   vendas, que não são tocados por este seletor porque vivem noutro id). */
/* VAR-1 item b: 11px medido abaixo do piso da casa (D-401) -- os botões "±
   N" carregam número, e número nunca fica abaixo de 12px. */
#sens-ed .curva-acoes .btn-mini { --btn-pad-h: 8px; --btn-pad-v: 5px; --btn-tam: 12px; }

.curva-colunas {
  display: flex; align-items: flex-end; gap: 2px; height: 148px;
  border-bottom: 1.5px solid var(--tinta); position: relative;
}
/* TETO DE LARGURA, e ele vale para as TRÊS faixas (coluna, rótulo do mês e
   célula) ou o alinhamento entre elas quebra. Sem teto, uma curva de 4 safras
   vira quatro tarjas de 200px — o desenho deixa de ler como série e a caixa de
   número fica maior que o número. Com teto, poucas safras ficam à esquerda e o
   resto é ar. */
.curva-col, .curva-meses span, .curva-cel { max-width: 84px; }
.curva-col {
  flex: 1 1 0; min-width: 0; position: relative; height: 100%;
  display: flex; align-items: flex-end; cursor: ns-resize;
  touch-action: none; -webkit-user-select: none; user-select: none;
}
.curva-col::after { content: ""; position: absolute; inset: 0; }
.curva-barra { width: 100%; background: var(--verde); border-radius: 3px 3px 0 0; }
.curva-col:hover .curva-barra, .curva-col.mexendo .curva-barra { background: var(--verde-escuro); }
/* safra negativa: o motor recusa, e a coluna não pode parecer uma barra vazia */
.curva-col.neg .curva-barra { background: var(--vermelho); }
.curva-val {
  position: absolute; bottom: 100%; left: 50%; transform: translateX(-50%);
  font: 500 12px var(--mono); color: var(--tinta); white-space: nowrap;
  padding-bottom: 3px; opacity: 0; pointer-events: none;
  font-variant-numeric: tabular-nums;
}
.curva-col:hover .curva-val, .curva-col.mexendo .curva-val { opacity: 1; }
.curva-meses { display: flex; gap: 2px; margin-top: 5px; }
.curva-meses span {
  flex: 1 1 0; min-width: 0; text-align: center; overflow: hidden;
  font: 400 12px var(--mono); color: var(--tinta-3);
}
.curva-celulas { display: flex; gap: 2px; margin-top: 7px; }
.curva-cel {
  flex: 1 1 0; min-width: 0; text-align: center; padding: 6px 1px;
  border: 1px solid var(--linha); border-radius: 6px; background: var(--superficie-2);
  font: 500 12px var(--mono); color: var(--tinta); font-variant-numeric: tabular-nums;
}
.curva-cel:focus { outline: none; border-color: var(--verde); box-shadow: 0 0 0 3px rgba(14,107,73,0.12); }
/* D-401 (fonte numérica nunca abaixo de 12px): acima de 16 colunas a caixa
   não encolhe mais a fonte para caber. Cada coluna ganha largura MÍNIMA
   própria (36px) e o excesso vira rolagem HORIZONTAL do PALCO
   (`.curva-palco.apertado`, `overflow-x: auto`), nunca da página, que segue
   travada em `max-width: 100%`. As fileiras (barras/gráfico, meses, células)
   e o invólucro do SVG do acumulado (`.curva-grafico`, no editor com
   tabela) crescem juntas (`width: max-content`) porque moram no MESMO
   contêiner de rolagem: rolar o palco move todas pela mesma quantidade, sem
   sincronismo manual em JS. O `padding` interno dá folga ao `box-shadow` do
   `:focus` da célula, que senão seria cortado pela borda do overflow. */
.curva-palco { max-width: 100%; }
.curva-palco.apertado {
  overflow-x: auto; -webkit-overflow-scrolling: touch;
  padding: 0 4px 3px;
}
.curva-palco.apertado .curva-colunas,
.curva-palco.apertado .curva-meses,
.curva-palco.apertado .curva-celulas,
.curva-palco.apertado .curva-grafico {
  width: max-content; min-width: 100%;
}
.curva-palco.apertado .curva-col,
.curva-palco.apertado .curva-meses span,
.curva-palco.apertado .curva-cel {
  flex: 0 0 36px;
}

.curva-acoes { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; margin-top: 14px; }
/* VAR-1 item b: em #ed-vendas a dica embute "…pode somar menos de 100%" --
   número, piso de 12px (D-401); mesma classe serve #ed-obra, sem número. */
.curva-dica { margin-left: auto; font-size: 12px; color: var(--tinta-3); }
/* O ATALHO EM TEXTO — sobrevive, e de propósito: quem já sabe digitar a série
   cola ela inteira e segue. O que muda é o posto, não a função: mesmo recuo do
   editor, para os dois lerem como uma peça só.
   `display: block` e NÃO o `flex` que `.linha-cheia` traz: com o rótulo em
   coluna flex, cada `<b>` do texto ("decimal com **ponto**") vira um ITEM e
   ganha linha própria — o rótulo destas três labels saía quebrado em três. A
   correção é local de propósito: `.linha-cheia` é usada em meia dúzia de outros
   lugares desta tela, e não é região desta entrega. */
.curva-atalho { display: block; margin: 10px 0 0 18px; }
.curva-atalho > .campo { margin-top: 6px; }
.curva-atalho > .campo input { font-size: 12.5px; }

/* ---- EDITOR DE CURVAS COM TABELA (OBRA-3, PARTE B) ----
   `.curva-grafico` é o invólucro NOVO que só existe quando `cfg.tabela` está
   ligado (hoje só `#ed-obra`): ele isola `.curva-colunas` (as barras, sem
   mudar UMA linha da regra delas acima) da LINHA DO ACUMULADO, um `<svg>`
   sobreposto por posição absoluta. Sem este invólucro o SVG teria de ser
   irmão de `.curva-colunas` dentro de `.curva-palco`, e `.curva-palco` tem
   MAIS filhos no modo tabela (o `<table>` vem depois) — o `inset:0` erraria
   o alvo. O invólucro garante que o SVG cobre exatamente a caixa das barras,
   nunca a tabela abaixo dela. */
.curva-grafico { position: relative; }
.curva-acumulado {
  position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none;
}
/* `vector-effect: non-scaling-stroke` — sem ele, `preserveAspectRatio="none"`
   (necessário para o eixo X andar com a LARGURA da caixa e o Y com a ALTURA,
   independentes um do outro) escala o TRAÇO da linha junto com a caixa, e ele
   fica mais grosso na horizontal do que na vertical. */
.curva-acumulado polyline {
  fill: none; stroke: var(--ambar); stroke-width: 1.6; vector-effect: non-scaling-stroke;
}
/* A CÉLULA EDITÁVEL DENTRO DA TABELA. `.tabela-edicao input` (abaixo) já
   dita fonte/borda/fundo — o que falta é o alinhamento à direita, que
   acompanha o `col-num` do cabeçalho "% do mês" (a mesma convenção do resto
   da tela: coluna marcada `col-num` tem conteúdo alinhado à direita).
   TELA-4b, PASSADA 2 (09/09/2026, ordem 6 da Mesa) — o tamanho próprio:
   28px de altura e 64px de largura (ANTES, medido em 1384×849 com o Gardens
   North: 37,00×84,00px), fonte 12px (o genérico `.tabela-edicao input`,
   abaixo, dá 13px). A especificidade de duas classes já vencia o genérico
   antes desta fatia (é dela que vem só o `text-align`); aqui ela também
   vence a LARGURA (100% do genérico) e o PADDING (9px 11px do genérico). */
.curva-tabela .curva-cel {
  text-align: right; font-size: 12px; width: 64px; height: 28px; padding: 0 6px;
}
/* LINHA DE PICO (OBRA-3, PARTE B) — marcada por CLASSE no `<tr>`, nunca por
   texto novo na célula. O `box-shadow` (e não `border-left`) porque
   `border-collapse: collapse` da tabela por baixo apagaria uma borda comum
   entre células vizinhas — o box-shadow não disputa colapso de borda nenhum. */
.curva-tabela tbody tr.curva-pico td { background: var(--ambar-claro); }
.curva-tabela tbody tr.curva-pico td:first-child { box-shadow: inset 3px 0 0 var(--ambar); }

/* TELA-4b, PASSADA 2 (09/09/2026) — A TABELA MÊS A MÊS COMPACTADA (ordem 6/7
   da Mesa, medida em 1384×849 com o Gardens North, 36 linhas). ANTES: altura
   de linha 49,00px (mediana/mínimo, E TAMBÉM a do PICO — medido: o pico mede
   49,00) e 53,00px (a da ÚLTIMA linha, não a do pico); larguras mês
   77,0 · data 126,8 · % 189,4 · acumulado 183,0 · R$ 225,7px, total 802,0px;
   delta vertical texto-vs-input (medido por `Range` sobre o GLIFO, não pela
   caixa do `<td>` — ela nasce do tamanho da LINHA e daria 0,00px sempre) de
   −16,50px em acumulado e em R$ do mês, e de −8,00px na coluna Mês.
   A CAUSA DA LARGURA É A MESMA DO D-303: `.curva-tabela` herda
   `width: 100%` de `.tabela-edicao` (abaixo) e o `table-layout: auto`
   reparte a sobra pelas cinco colunas — encolher só o INPUT (acima) nunca
   empacota enquanto a TABELA continuar esticada. A saída é a mesma das
   outras três grades do anexo (`#tab-pf`, `#tab-unidades`, `#tab-pers`,
   ~linha 2075 — medido; a citação já viveu em "~2013" e o número mudou de
   lugar sem que a citação acompanhasse), mas ELAS têm ID (especificidade
   1,0,0, vence por conta
   própria) e `.curva-tabela` só tem CLASSE (0,1,0 — empataria com
   `.tabela-edicao` e perderia por ORDEM no arquivo, medido: a tabela ficou
   em 802px mesmo com esta regra escrita). O escopo `#ed-obra` (o único pai
   que hoje monta `cfg.tabela`) resolve por especificidade, não por posição.
   Soma das cinco larguras DECLARADAS: 40+72+72+84+110 = 378px (a ordem
   confere esta conta, e ela está certa). ⚠ ERRATA DATADA — 10/09/2026
   (NOTURNO-1, item 7, [[D-341]] ressalva 5.4): esta linha dizia que a
   TABELA mede 378px, e a soma DECLARADA não é a largura RENDERIZADA — as
   duas coisas divergem sob `table-layout: auto`. Medido no Chrome real
   (`#ed-obra .curva-tabela`, `getBoundingClientRect().width`, curva
   "linear" com 24 meses), em viewport 1368×698 (`--window-size=1384,849`):
   379,4px. A diferença (~1,4px) é o genérico arredondando fração de pixel
   ao conteúdo real das células — não é erro de conta, é a distância entre
   o que o CSS PEDE e o que o layout ENTREGA. */
#ed-obra .curva-tabela { width: auto; }
.curva-tabela thead th:nth-child(1), .curva-tabela tbody td:nth-child(1) { width: 40px; }
.curva-tabela thead th:nth-child(2), .curva-tabela tbody td:nth-child(2) { width: 72px; }
.curva-tabela thead th:nth-child(3), .curva-tabela tbody td:nth-child(3) { width: 72px; }
.curva-tabela thead th:nth-child(4), .curva-tabela tbody td:nth-child(4) { width: 84px; }
.curva-tabela thead th:nth-child(5), .curva-tabela tbody td:nth-child(5) { width: 110px; }
/* O RODAPÉ `TOTAL` TEM ÍNDICE DESLOCADO: a primeira célula do `<tfoot>` é
   `colspan="2"` (cobre Mês+Data), então o SEGUNDO `<td>` do DOM ali é
   visualmente a coluna % (não Data), o TERCEIRO é Acumulado (não %) e o
   QUARTO é R$ (não Acumulado) — `nth-child` conta posição no DOM, não coluna
   visual, e o colspan desloca as duas contagens uma da outra. A MECÂNICA
   ACIMA ESTÁ CERTA; A CONSEQUÊNCIA ABAIXO, NÃO (TELA-4b, ressalva 5.2,
   medida por remoção em memória): as três regras seguintes são INERTES —
   removê-las não muda um pixel da tabela renderizada (a causa exata não foi
   isolada nesta medição; a suspeita, não confirmada, é o `table-layout: auto`
   já fechando as cinco colunas pelas regras de `tbody td:nth-child` acima).
   Ficam DE PÉ (a ordem não pediu remoção, e apagar silenciosamente regra
   morta é a mesma classe de defeito que um comentário que mente sobre ela)
   — mas leia-as como PROSA que documenta uma correção de índice, não como
   CSS que hoje produz largura. */
.curva-tabela tfoot td:nth-child(2) { width: 72px; }  /* total-pct = coluna % */
.curva-tabela tfoot td:nth-child(3) { width: 84px; }  /* slot vazio = coluna Acumulado */
.curva-tabela tfoot td:nth-child(4) { width: 110px; } /* total-rs = coluna R$ */
/* MONO 12px NAS CINCO COLUNAS — mês/data nasciam sans 14px (herdado do
   `body`, que não declara `<td>` nenhum); acumulado/R$ já eram mono 12px
   (`.celula-calc`, abaixo) e não mudam de fonte aqui, só de altura de linha,
   junto com o resto. Escopo em `tbody`: o cabeçalho (`th`, maiúsculas
   10,5px) e o rodapé `TOTAL` (fonte própria em `.tabela-com-total tfoot`,
   abaixo) não são tocados — a ordem fala da tabela mês a mês, não deles.
   Uma classe + dois elementos (0,1,2) vencem o genérico
   `.tabela-edicao td { padding: 6px 4px }` (0,1,1) por especificidade, não
   por ordem no arquivo — não depende de onde este bloco mora. */
.curva-tabela tbody td { font: 12px var(--mono); padding: 1px 4px; }
/* ITEM 7 DA ORDEM — o mesmo remendo que já existe em `#tab-pf`/`#tab-pers`
   para a MESMA doença (`.tabela-edicao td { vertical-align: top }`, ~linha
   1468, fica DE PÉ: o slot do eco de mês de outras tabelas depende dele —
   D-225, escopo e não revogação). `middle` centra o texto na altura real
   da linha — a mesma altura que o input de "% do mês" ocupa (a régua do
   `.celula-calc`, abaixo, força `padding: 0 8px !important` nestas células;
   é exatamente esse padding vertical zerado, com a linha esticada pelo
   input vizinho, que faz `middle` valer a pena aqui). */
.curva-tabela tbody td.celula-calc { vertical-align: middle; }

/* ---- tabelas de edição ---- */
.tabela-edicao { width: 100%; border-collapse: collapse; margin-top: 6px; }
.tabela-edicao th {
  text-align: left; font: 600 10.5px var(--display); text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--tinta-2); padding: 8px 6px; border-bottom: 1px solid var(--linha);
}
/* CABEÇALHO EXATAMENTE SOBRE A CÉLULA (TELA-2, regra única do dono): toda
   coluna cujo corpo é número alinhado à direita (`.celula-calc`, ou um input
   monetário tratado como tal) leva este marcador no `<th>`. Um só nome para
   as três grades — Valor (permuta física), Ticket/VGV (mix) — em vez de um
   seletor de posição por tabela, que envelheceria toda vez que uma coluna
   mudasse de lugar. */
.tabela-edicao th.col-num { text-align: right; }
.tabela-edicao td { padding: 6px 4px; }
/* LINHA DE TOTAL DO MIX (VENDAS-4, item 7 · roupa nova na TELA-2, item B) —
   faixa CONTÍNUA no verde-claro da casa, cantos arredondados nas pontas. O
   `border-collapse: collapse` da tabela (linha acima) IMPEDE `border-radius`
   em `td` comuns — a saída é arredondar só a PRIMEIRA e a ÚLTIMA célula da
   linha: como as duas têm a MESMA cor de fundo dos vizinhos, não há costura
   visível entre elas, só o contorno externo arredonda. O "respiro" que separa
   a faixa da última linha do corpo é a borda superior colorida como o próprio
   cartão (`--superficie`) — truque que funciona com `collapse` porque border
   e background nunca disputam a mesma área (a borda fica FORA do padding). */
.tabela-com-total tfoot td {
  background: var(--verde-claro); border-top: 8px solid var(--superficie);
  padding: 10px 8px; vertical-align: middle;
  font: 500 11px var(--display); color: var(--verde-escuro);
}
.tabela-com-total tfoot td:first-child { border-top-left-radius: 8px; border-bottom-left-radius: 8px; }
.tabela-com-total tfoot td:last-child { border-top-right-radius: 8px; border-bottom-right-radius: 8px; }
/* A CELULA-CALC TEM `padding: 0 8px !important` (linha abaixo) — sem
   `!important` aqui TAMBÉM, o padding vertical da linha de cima nunca venceria
   nela, e o total de unidades/VGV ficaria mais RASO que o rótulo "Total" na
   mesma faixa (a caixa da célula sempre ocupa a altura inteira da linha, mas
   o CONTEÚDO ficaria descentralizado sem o mesmo respiro). */
.tabela-com-total tfoot td.celula-calc { font: 600 12px var(--mono); color: var(--verde-escuro); padding: 10px 8px !important; }
/* HABITE-1, PARTE B, item (1) — CORREÇÃO DO QUE A TELA-3 FEZ. A TELA-3
   (comentário anterior, revogado) alinhou o "208" pela BORDA DIREITA dos
   inputs de cima — `right() = right()`. O dono quer agora pela ESQUERDA DO
   TEXTO: `left()` do "208" = `left()` do TEXTO dentro do input Qtd. (borda +
   padding do input, nunca a borda do `<td>`). Medido em Chrome real, com
   Doma América 2, viewport 1720: o `<td>` do Total (`#tot-unidades`) tem seu
   próprio rect.left em 754,5px; o texto do input Qtd. (borda 1px + padding
   11px de `.tabela-edicao input`) começa em 770,5px. A diferença — 16px — é
   o `padding-left` que faz os dois coincidirem, medido por `Range` sobre o
   texto do `<td>` (a caixa não se move com o alinhamento; o GLIFO, sim). */
#tot-unidades { text-align: left; padding-left: 16px !important; }
/* O BLOCO "VGV bruto ... · líquido ..." — SEM QUEBRA DE LINHA (mockup do
   dono), alinhado à direita na célula que ocupa de RETIDAS a VGV. */
.lt-vgv-tag { font-weight: 500; }
.lt-vgv-num { font-weight: 400; }
.lt-vgv-num i { font-style: normal; font-size: 11px; text-transform: lowercase; }
.lt-vgv-num b { font: 500 12px var(--mono); color: var(--verde-escuro); }
.lt-vgv-pt { padding: 0 1px; }
.tabela-edicao input, .tabela-edicao select {
  font: 13px var(--mono); padding: 9px 11px; border: 1px solid var(--linha);
  border-radius: 7px; background: var(--superficie-2); color: var(--tinta); width: 100%;
}
.tabela-edicao input:focus, .tabela-edicao select:focus {
  outline: none; border-color: var(--verde); box-shadow: 0 0 0 3px rgba(14,107,73,0.12);
}
.celula-calc { font: 12px var(--mono); text-align: right; color: var(--tinta-2); white-space: nowrap; padding: 0 8px !important; }
/* R$ MAIS CLARO QUE O NÚMERO (TELA-2, item B) — só onde o dono pediu essa
   hierarquia (Ticket/VGV do mix, via `fmtBRLTag`); o valor da permuta física
   e o da tabela cheia seguem em UM tom só (`.celula-calc` já é secundário
   inteiro), que é o que o item A pediu. O número fica mais escuro que o
   prefixo dentro do corpo do mix — o oposto do padrão "tudo em --tinta-2" —
   para o olho ler a grandeza primeiro e a moeda depois. */
#tab-unidades tbody td.celula-calc { color: var(--tinta); }
.celula-calc .rs { color: var(--tinta-3); font-weight: 400; }
/* TELA-1, item 2a (07/09/2026) — O VALOR DA PERMUTA FÍSICA NA MESMA LINHA DO
   INPUT. A LINHA É MAIS ALTA QUE O TEXTO DO VALOR: `.tabela-edicao td` nasce
   `vertical-align: top` (linha 1465), e o `<input>` da QTD, com o padding
   cheio de `.tabela-edicao input` (9px 11px), é o que define a altura da
   linha — o texto do VALOR, sozinho num `<td>` sem input, ficava ENCOSTADO
   no topo da célula, visivelmente mais alto que o número dentro da caixa da
   QTD (MEDIDO por retângulo, não por olho: ~7px de diferença). `middle`
   centra o texto na altura real da linha, a mesma altura que o input ocupa
   — só nesta tabela, sem tocar a regra genérica de `.celula-calc` (usada
   pelas linhas de TOTAL e por outras tabelas). */
#tab-pf td.celula-calc { vertical-align: middle; }
/* CUSTOS ADICIONAIS DA OBRA (OBRA-3, PARTE B, decisão B3) — "grade empacotada
   à esquerda": as colunas de NÚMERO (Valor, Mês, Parcelas) ficam com largura
   contida, e o espaço que sobra vai para Descrição e Distribuição, que são
   texto — sem isto o layout `auto` do navegador reparte a largura livre por
   igual entre as seis colunas, e o input de "Mês" (três dígitos) fica tão
   largo quanto a Descrição. `min-width` na Distribuição, e não `width` fixo:
   a frase varia de tamanho ("gasto único · OUT/30" contra "6 × R$ 75.000 ·
   JUN/27 a NOV/27") e um teto rígido cortaria a mais longa. */
#tab-obra-extras th:nth-child(2), #tab-obra-extras td:nth-child(2) { width: 130px; }
#tab-obra-extras th:nth-child(3), #tab-obra-extras td:nth-child(3) { width: 70px; }
#tab-obra-extras th:nth-child(4), #tab-obra-extras td:nth-child(4) { width: 80px; }
#tab-obra-extras th:nth-child(5), #tab-obra-extras td:nth-child(5) { min-width: 170px; }
/* DISTRIBUIÇÃO É MONO MUTED (ordem B3, letra exata) — a mesma classe
   `.celula-calc` do resto da tela, só que ALINHADA À ESQUERDA e sem
   `nowrap`: ela é uma frase, não um número, e uma frase que não pode quebrar
   linha estoura a largura contida que a régua acima acabou de dar à coluna. */
.celula-calc[data-calc="distribuicao"] {
  text-align: left; white-space: normal; color: var(--tinta-3);
}
/* FUNDOS-4b — a tabela de tranches da equity segue a MESMA régua do
   `#tab-obra-extras` logo acima: números curtos ("#", "Mês", "% do total")
   com largura contida, a sobra vai para "Distribuição" (texto, mesma classe
   `.celula-calc[data-calc]` acima, com nome próprio para não confundir com a
   distribuição da obra) e "Marco" (texto livre curto). */
#tab-fu-eq-tranches th:nth-child(1), #tab-fu-eq-tranches td:nth-child(1) { width: 26px; }
#tab-fu-eq-tranches th:nth-child(2), #tab-fu-eq-tranches td:nth-child(2) { width: 70px; }
#tab-fu-eq-tranches th:nth-child(3), #tab-fu-eq-tranches td:nth-child(3) { width: 90px; }
#tab-fu-eq-tranches th:nth-child(4), #tab-fu-eq-tranches td:nth-child(4) { min-width: 170px; }
.celula-calc[data-calc="distribuicao-tranche"] {
  text-align: left; white-space: normal; color: var(--tinta-3);
}
/* A ORDEM DE PAGAMENTO (T3) — vocabulário lido do servidor (D-076), tipografia
   de `.nota`: é informação de apoio, não dado editável. */
#fu-eq-camadas { font-size: 12px; color: var(--tinta-2); margin: 12px 0 0; padding-left: 20px; }
#fu-eq-camadas li { margin: 4px 0; }

/* DOBRA (D-263, ajuste da letra da ordem TMA-1): "Como montar a TMA" virou
   três itens em vez de um parágrafo corrido -- mesma régua do #fu-eq-camadas. */
#tma-como-montar { font-size: 12px; color: var(--tinta-2); margin: 12px 0 0; padding-left: 20px; }
#tma-como-montar li { margin: 4px 0; }
/* C1/C3 (martelo do dono, 09/09/2026) — RESPIRO ENTRE A TABELA E O BOTÃO DE
   ADICIONAR, medido no navegador real (1720px, aba Premissas): 0px nos dois
   ANTES desta regra — o botão nascia colado na borda de baixo da tabela
   (`getBoundingClientRect` do botão e da tabela coincidindo, byte a byte).
   ALARGADA EM 09/09/2026 (TELA-4a, martelo do dono): a frase original dizia
   que "custo adicional" e "venda personalizada" não tinham sido pedidos e
   não mudavam — MUDARAM, mesma régua, mesma técnica (`margin-top` no botão),
   medidos com o mesmo 0px de antes (`#tab-obra-extras`→`#btn-add-obra-extra`
   e `#tab-pers`→`#btn-add-pers`). Os demais `.btn-mini` de "+ adicionar"
   depois de tabela (despesa pré, balão...) seguem fora — não foram pedidos e
   não mudam. */
#btn-add-unidade, #btn-add-inv, #btn-add-obra-extra, #btn-add-pers { margin-top: 12px; }
/* MESMA CAUSA-RAIZ, TERCEIRA TABELA (TELA-3, item 3a) — "Tabela cheia" ficava
   ENCOSTADA no topo da célula (`vertical-align: top` do genérico), acima do
   input de VALOR TOTAL ao lado: medido, o topo do texto batia com o topo da
   linha (`padding: 0 8px` de `.celula-calc` não tem respiro vertical), e o
   input começa 6px mais abaixo (o padding-top de `.tabela-edicao td`).
   `middle` centra na altura real da linha, como o `#tab-pf` já fazia. */
#tab-pers td.celula-calc { vertical-align: middle; }
/* GRADE EMPACOTADA À ESQUERDA, SOBRA À DIREITA (TELA-2, regra única do dono,
   D-303) — a CAUSA-RAIZ que a TELA-1 não tinha achado. `.tabela-edicao`
   (linha 1750) estica para 100% do container e o `table-layout: auto`
   REDISTRIBUI a sobra pelas colunas: era por isso que encolher só o INPUT da
   Qtd (o `max-width: 64px` duas telas atrás) não empacotava a coluna — a
   TABELA continuava esticada, e o `<td>` ficava largo com o input pequeno
   boiando dentro. As três grades do anexo perdem o `width: 100%` herdado e
   passam a ocupar só o que o conteúdo pede; o espaço que sobra fica em
   branco à direita da tabela, dentro do cartão — nunca redistribuído. */
#tab-pf, #tab-unidades, #tab-pers { width: auto; }
/* TETO, NÃO SÓ PISO — a outra ponta da MESMA causa-raiz (D-303). O piso
   (`min-width`, já existia) evita que a coluna fique menor que o conteúdo
   pede; SEM UM TETO ao lado, é o tamanho NATIVO do `<input>`/`<select>` sem
   `size` (bem maior que o dígito que ele guarda) que o navegador usa para
   decidir o quanto a tabela "encolhida" precisa — e ela nascia quase do
   tamanho do container de qualquer jeito, mesmo depois do `width: auto` logo
   acima. O `#tab-pf` já fazia isso para a Qtd (o `max-width: 64px` de duas
   fatias atrás); as colunas curtas das outras duas grades ganham o mesmo par
   piso+teto aqui. MEDIDO, não suposto: sem isto `#tab-unidades` continuava
   em 860px de 918 disponíveis, com "Qtd." e "Retidas" em 91px cada. */
#tab-unidades td > input[data-campo="quantidade"],
#tab-unidades td > input[data-campo="retidas"],
#tab-unidades td > input[data-campo="area_m2"] { max-width: 72px; }
#tab-unidades td > input[data-campo="preco_m2"] { max-width: 118px; }
#tab-unidades td > input[data-campo="nome"] { max-width: 130px; }
#tab-pers td > input[data-campo="quantidade"] { max-width: 64px; }
#tab-pers td > input[data-campo="desconto_pct"] { max-width: 72px; }
#tab-pers select[data-papel="unidade-mix"] { max-width: 130px; }
#tab-pers td > input[data-campo="valor_total"] { max-width: 140px; }
/* P-11 (NOTURNO-7 item 6c, 13/09/2026) — CONSERTO NA TABELA, NÃO NO
   BREAKPOINT (ordem literal da Mesa, martelo do dia). Vale em TODA largura,
   e não muda nada em 1720 (MEDIDO abaixo) — só entra em ação onde a coluna
   já não tinha sobra: acima de 980px a coluna lateral do stepper volta
   (P-10) e `#sec-produto` fica mais estreito que a 980 (onde o stepper
   compacto dava largura cheia), e `#tab-pers` (Vendas personalizadas) tem
   largura NATURAL de 721px que não encolhe sozinha (D-303: tabela com
   `width: auto` cresce até o teto de cada coluna, nunca abaixo) — 47px de
   vazamento MEDIDO a 981px (scrollWidth 749 × clientWidth 702 em
   `#sec-produto`), caindo a 4px em 1024 e zero a partir de ~1030. As duas
   colunas abaixo são as que sobram sem uso nessa faixa — nenhuma delas é a
   que o piso+teto de `#tab-unidades` (regra acima) já cobre:
   (1) "Tabela cheia" (`[data-calc="tabela-cheia"]`) é `fmtBRL` em texto
   puro ("R$ 2.100.000", `.textContent`, sem tag) — o espaço depois do "R$"
   já é ponto de quebra válido; só faltava permitir (`.celula-calc` geral é
   `nowrap`, linha ~2696). `white-space: normal` deixa o navegador quebrar
   SÓ QUANDO PRECISA: em 1720px o valor continua numa linha só (MEDIDO,
   88×20 antes e depois — 0px de diferença); a 981px ela quebra em duas
   linhas ("R$" e o número: 102px → 81px). Nenhuma outra tabela usa este
   seletor;
   (2) "Valor total" NÃO é o `<input>` — MEDIDO: o `max-width` do campo
   (140px, regra acima) é código morto para a largura da COLUNA, porque o
   `<td>` (`.pers-valor-cel`, flex em coluna) empilha o input com a PÍLULA
   "tabela/negociado" (`.pers-modo-pilula`, dois botões de 68 e 93px), e é
   ELA quem manda no `<td>` (163px de pílula contra 115px de input MEDIDOS
   lado a lado — encolher só o input não move a coluna um pixel; TESTADO e
   descartado). O aperto é no PADDING dos dois botões (16px de cada lado,
   herdado de `.seletor-modo button`, o mesmo componente da Modalidade de
   Vendas): só NESTA pílula, escopada por `.pers-modo-pilula` — as demais
   pílulas da tela não mudam —, o padding horizontal cai para 9px FIXO
   (nunca `vw`: unidade de viewport reintroduziria dependência de largura
   pela porta dos fundos). "tabela" e "negociado" continuam inteiros, sem
   quebra e sem corte, porque o TEXTO não precisa de padding para caber.
   MEDIDO nas cinco larguras (`#sec-produto.scrollWidth <= clientWidth` e
   `document.documentElement.scrollWidth <= clientWidth`, os dois iguais,
   nenhuma folga negativa): 981, 990, 999, 1024 (a faixa que vazava) e 1720
   (controle — pílula 163px → 139px, texto "tabela"/"negociado" legível,
   `scrollWidth <= clientWidth` do próprio botão nos dois). NENHUM PISO
   desce: nem `descricao` (158px, guarda o passo 7 do `check_espelho_mix`
   contra "Pacote investidor" cortado — TENTATIVA JÁ REVERTIDA uma vez, ver
   comentário abaixo) nem `unidade-mix` (130px, guarda nomes de unidade). */
#tab-pers [data-calc="tabela-cheia"] { white-space: normal; }
#tab-pers .pers-modo-pilula button { padding-left: 9px; padding-right: 9px; }
/* ⚠ TENTATIVA REVERTIDA, e a razão fica escrita para não repetir a
   suposição errada: a hipótese de que a linha 2 (`.pers-quando`, um
   `<td colspan="7">`) fosse a causa da coluna "Qtd." inchada foi TESTADA e
   REFUTADA — escondendo a linha 2 inteira (`display:none`) a coluna
   continuava larga do MESMO jeito. `table-layout: fixed` não ajudou (o
   Chromium, sem largura explícita nenhuma no `<th>`, continuou preenchendo
   o container do mesmo jeito que em `auto`). A causa real é a MESMA do
   `#tab-unidades` logo abaixo: tamanho NATIVO de input/select sem `max-width`
   — e o conserto é o mesmo, coluna a coluna. */
/* CÉLULA COMPOSTA DA TIPOLOGIA (VENDAS-2): select fechado + texto livre
   condicional, empilhados dentro do mesmo `<td>` — o texto só ocupa espaço
   quando "Outra…" está selecionado (`hidden` cuida disso), então a coluna
   não cresce para as linhas que usam o vocabulário fechado. */
.celula-tipologia { display: flex; flex-direction: column; gap: 4px; }
.celula-tipologia select, .celula-tipologia input { width: 100%; }
/* PISO DO SELECT DE TIPOLOGIA (VENDAS-4b, R2) — REGRESSÃO CONSERTADA. Sem
   `min-width` declarado, o `table-layout: auto` de `#tab-unidades` espremia
   este select junto com o resto da linha sempre que outra coluna crescia —
   medido: 81,0px → 68,4px na fatia anterior (a coluna do `<tfoot>` do total,
   item 7, cresceu a tabela).
   HABITE-1, PARTE B, item (2) — O 91px ERA MEDIDA ERRADA, e "Residencial"
   AINDA CORTAVA. O número antigo dizia vir de clone em `width: max-content`,
   mas não era: um `<select>` fechado NÃO denuncia corte por
   `scrollWidth`/`clientWidth` (os dois ficam sempre iguais — o navegador só
   pinta a opção truncada, sem produzir conteúdo "rolável" para medir), e o
   piso nunca tinha sido conferido contra a caixa de verdade. Remedido pela
   régua do D-319 (a mesma técnica do `#pf-wrap-modo`, acima): min/max/width
   liberados por estilo inline no PRÓPRIO `<select>`, dentro do seu lugar
   real na tabela — todas as três palavras do vocabulário fechado
   (Residencial/Comercial/Vaga) pedem os MESMOS 130px, porque o Chromium
   dimensiona a caixa fechada do select pela opção MAIS LARGA da lista
   inteira, não pela selecionada. O próprio comentário da TETO logo abaixo já
   media a caixa nativa em "~134px" — o 91px nunca bateu com o vizinho que o
   citava. */
.celula-tipologia select { min-width: 130px; }
/* TELA-2 — o MESMO piso vira TETO (D-303): sem `max-width`, o tamanho
   NATIVO do `<select>` (mais largo que o texto que ele guarda) é o que a
   tabela "encolhida" enxerga. Pino exato no valor remedido acima (130px). */
.celula-tipologia select { max-width: 130px; }
/* RENOMEADO nesta rodada: chamava-se `.btn-remover`, e o prefixo `btn` mentia.
   Com o componente virando bloco único, `.btn` passa a significar "membro da
   família" — e um `.btn-*` que não tem nenhuma das físicas da família é
   exatamente o tipo de nome que faz o próximo leitor procurar o estilo no
   arquivo errado. Este × não eleva e não afunda: não é botão da família. */
.remover-linha {
  border: 0; margin: 0; background: transparent; color: var(--tinta-3); cursor: pointer;
  font-size: 16px; padding: 5px 8px; border-radius: 6px; line-height: 1;
}
.remover-linha:hover { background: var(--vermelho-claro); color: var(--vermelho); }

/* ---- #tab-pers, LINHA 2: "O QUANDO" (TELA-2, item C) ----
   Cada venda personalizada virou DUAS `<tr>`: `.pers-oque` (as sete colunas
   de sempre, com o × no fim) e, logo abaixo, `.pers-quando` — uma única
   célula `colspan` cobrindo a largura inteira da grade, com os quatro campos
   do widget de recebimento lado a lado (a mesma fonte de `#tab-inv`, ver
   `RECEBIMENTO` no app.js) em vez do grid 2×2 que uma coluna estreita exigia.
   Ela é indentada sob a Descrição, com o ícone de canto (`corner-down-right`)
   marcando que é a "letra miúda" da linha de cima. */
tr.pers-quando td { padding: 2px 4px 12px 22px; border-top: 0; }
/* O TRUQUE DO `width: 1px; min-width: 100%` — não é enfeite, é a peça que
   FECHA o item C1. A `<td colspan="7">` desta linha é bem mais larga que
   qualquer célula da linha 1 sozinha, e sob `table-layout: auto` o CONTEÚDO
   dela (quatro campos + ícone + legenda) contava para a largura PREFERIDA
   das sete colunas de cima — medido: a coluna "Qtd." (que devia ficar em
   ~70px) subia para até 172px só por causa do vizinho de baixo, e escondê-lo
   inteiro (`display:none`) confirmava a causa: a tabela caía para 743px.
   `width: 1px` faz o navegador usar ESSE valor (explícito, mínimo) na conta
   de largura preferida da coluna — os quatro campos deixam de "votar" no
   tamanho da tabela; `min-width: 100%` devolve a largura real assim que as
   colunas já estão decididas, na segunda passada do layout, e o flex volta
   a ocupar a largura inteira disponível (com `flex-wrap` cuidando do que não
   couber numa linha só). */
.pers-recebimento {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px 14px;
  width: 1px; min-width: 100%;
}
.pers-recebimento-icone { color: var(--tinta-3); flex: none; }
.pers-recebimento-rot {
  font: 600 10.5px var(--display); letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--tinta-3); flex: none; white-space: nowrap;
}
/* OS QUATRO CAMPOS SAEM DO GRID 2×2 do `.celula-grupo` genérico (que existe
   para caber numa COLUNA) e viram uma fileira: aqui sobra largura de tabela
   inteira, não largura de uma célula. `flex: none` em cada `.mini` evita que
   eles se espremam — cada um vale pelo próprio conteúdo, sem disputar espaço
   com vizinho nenhum (ver `.pers-recebimento-eco` abaixo). */
.pers-recebimento .mini { flex: none; width: auto; min-width: 0; }
/* MESMA CAUSA-RAIZ, EM MAIS UM LUGAR (D-303): `.mini-campo input` (linha
   ~2589) tem piso (`min-width: 2.6em`) mas nunca precisou de teto — dentro do
   grid 2×2 do `.celula-grupo` genérico quem limita a largura é o `1fr` da
   coluna do grid. Aqui, numa linha `flex` sem grid por cima, o tamanho NATIVO
   do `<input type="number">` sem teto voltava a inflar cada campo para
   ~124-214px (medido). O teto é o tamanho real que os quatro pedem: dois ou
   três dígitos. */
.pers-recebimento .mini-campo input { max-width: 46px; }
.pers-recebimento .mini-campo { min-width: 78px; }
/* OS DOIS ECOS, um por campo (TELA-3, item 3b) — SUBSTITUI a legenda
   combinada do fim da linha ("R$ 34.983 · SET/26"), que o dono reprovou nos
   prints: o "·" não diz a que campo cada metade pertence. Cada eco é filho
   do PRÓPRIO `.mini` (entrada em R$ sob a célula ENTRADA, mês civil sob a
   célula DO MÊS — ver `addPers`), mesmo estilo mono 11px muted que a legenda
   já usava. O `min-height` reserva a linha mesmo vazio (D-208): o campo troca
   de ter/não-ter eco sem empurrar a linha por baixo do dedo — e o `/13px` no
   `font` (a mesma técnica de `.mini-eco`, linha ~2634) FIXA a altura da caixa
   de linha em 13px COM texto ou sem, porque o `line-height` implícito
   ("normal") varia com a métrica da fonte e não com `min-height`: sem ele, o
   `.mini` que TEM texto ficava a caixa ~0,5px mais alta que os vazios (achado
   do `check_espelho_mix`, F2/D-208), e os quatro `.mini-campo` — centrados
   por `align-items:center` — desalinhavam por essa fração. */
.pers-recebimento-eco {
  display: block; margin-top: 2px; min-height: 14px;
  font: 500 12px/14px var(--mono); color: var(--tinta-3); white-space: nowrap;
}

/* ---- C2 (martelo do dono, 09/09/2026) — CADA VENDA PERSONALIZADA NUM
   CARTÃO, POR CSS PURO ----
   Decisão (b) da dobra: NENHUMA estrutura muda — zero `<tbody>` novo, zero
   `<th>`, zero `colspan`. Os seis leitores de `.pers-oque`/`.pers-quando`
   (`lerPremissas` incluído, que depende do `nextElementSibling`) continuam
   vendo exatamente o DOM de sempre; só o FUNDO, a BORDA e o RAIO mudam.

   A ALTERNÂNCIA CONTA A DUPLA, NÃO A LINHA. `:nth-of-type` conta por TAG
   entre irmãos — não por classe —, e como toda venda é sempre
   TR(.pers-oque), TR(.pers-quando) em sequência estrita, a venda N cai nas
   posições (2N−1, 2N): venda 1 e 3 em (4n+1, 4n+2); venda 2 e 4 em
   (4n+3, 4n). `--superficie`/`--superficie-2` são os "surface-1"/"surface-2"
   do mockup — os mesmos tokens de sempre, nenhum valor novo. */
#tab-pers tbody tr.pers-oque:nth-of-type(4n+1),
#tab-pers tbody tr.pers-quando:nth-of-type(4n+2) { background: var(--superficie); }
#tab-pers tbody tr.pers-oque:nth-of-type(4n+3),
#tab-pers tbody tr.pers-quando:nth-of-type(4n) { background: var(--superficie-2); }
/* HAIRLINE NAS DUAS PONTAS DO CARTÃO (a letra da ordem: "no topo da
   `.pers-oque` e embaixo da `.pers-quando`") — a primeira venda não tem
   cartão anterior, então ela não precisa da borda de cima (evita a linha
   dupla colada no `border-bottom` do `<thead>`). */
#tab-pers tbody tr.pers-oque { border-top: 1px solid var(--linha); }
#tab-pers tbody tr.pers-oque:first-child { border-top: none; }
#tab-pers tbody tr.pers-quando td { border-bottom: 1px solid var(--linha); }
/* RAIO NAS QUATRO PONTAS DO CARTÃO — `border-collapse: collapse` (herdado de
   `.tabela-edicao`) impede raio em `td` do meio da tabela; só as pontas
   arredondam, e a dupla inteira lê como um cartão só. `.pers-quando` tem UM
   `<td colspan="7">` (confirmado em `addPers`), então `first-child` já cobre
   as duas pontas de baixo. */
#tab-pers tbody tr.pers-oque td:first-child { border-top-left-radius: var(--raio); }
#tab-pers tbody tr.pers-oque td:last-child { border-top-right-radius: var(--raio); }
#tab-pers tbody tr.pers-quando td:first-child {
  border-bottom-left-radius: var(--raio); border-bottom-right-radius: var(--raio);
}

/* ---- C3 (martelo do dono, 09/09/2026) — CADA INVESTIDOR NUM CARTÃO,
   MESMA DISCIPLINA DO C2 ---- `#tab-inv` não tem par de linhas (uma `<tr>` =
   um investidor), então a alternância é por linha e o cartão fecha nela
   mesma — sem a conta de posição por dupla que `#tab-pers` precisa. */
#tab-inv tbody tr:nth-child(odd) { background: var(--superficie); }
#tab-inv tbody tr:nth-child(even) { background: var(--superficie-2); }
#tab-inv tbody tr { border-top: 1px solid var(--linha); }
#tab-inv tbody tr:first-child { border-top: none; }
#tab-inv tbody tr:last-child { border-bottom: 1px solid var(--linha); }

/* INV-2 (DIURNA-8, 16/09/2026) — #tab-inv-unid REUSA a forma de par de
   `<tr>` do C2 (`.pers-oque`/`.pers-quando`, D-076: casa única do widget de
   recebimento) e a mesma disciplina de cartão da C2/C3 acima; só a largura
   de coluna é própria daqui. */
#tab-inv-unid { width: auto; }
#tab-inv-unid td.celula-calc { vertical-align: middle; }
#tab-inv-unid td > input[data-campo="nome"] { max-width: 120px; }
#tab-inv-unid td > input[data-campo="quantidade"] { max-width: 64px; }
#tab-inv-unid td > input[data-campo="desconto_pct"],
#tab-inv-unid td > input[data-campo="comissao_pct"],
#tab-inv-unid td > input[data-campo="mes_aporte"] { max-width: 72px; }
#tab-inv-unid select[data-papel="unidade-mix"] { max-width: 130px; }
#tab-inv-unid [data-calc="invu-tabela-cheia"], #tab-inv-unid [data-calc="invu-pago"] { white-space: normal; }
#tab-inv-unid tbody tr.pers-oque:nth-of-type(4n+1),
#tab-inv-unid tbody tr.pers-quando:nth-of-type(4n+2) { background: var(--superficie); }
#tab-inv-unid tbody tr.pers-oque:nth-of-type(4n+3),
#tab-inv-unid tbody tr.pers-quando:nth-of-type(4n) { background: var(--superficie-2); }
#tab-inv-unid tbody tr.pers-oque { border-top: 1px solid var(--linha); }
#tab-inv-unid tbody tr.pers-oque:first-child { border-top: none; }
#tab-inv-unid tbody tr.pers-quando td { border-bottom: 1px solid var(--linha); }
#tab-inv-unid tbody tr.pers-oque td:first-child { border-top-left-radius: var(--raio); }
#tab-inv-unid tbody tr.pers-oque td:last-child { border-top-right-radius: var(--raio); }
#tab-inv-unid tbody tr.pers-quando td:first-child {
  border-bottom-left-radius: var(--raio); border-bottom-right-radius: var(--raio);
}
#tab-inv tbody tr td:first-child { border-top-left-radius: var(--raio); border-bottom-left-radius: var(--raio); }
#tab-inv tbody tr td:last-child { border-top-right-radius: var(--raio); border-bottom-right-radius: var(--raio); }

/* ---- faixa viva de totais ---- */
/* DESIGN-0: fundo passa a ser `--color-primary` chapado (era um quase-preto
   translúcido com blur) -- a faixa é a superfície escura da MARCA, não um
   véu sobre o conteúdo por trás, que já não existe (ela é `position: fixed`
   sobre o rodapé da tela, não sobre texto).
   DESIGN-0b: a `position: fixed` sobe para o `.faixa-viva`, que agora é o
   contêiner comum da faixa e do rodapé de efeito (ver bloco abaixo) — os
   dois empilham por fluxo normal dentro dele, sem cada um carregar o
   próprio `position: fixed`. `#faixa-totais` continua com o MESMO id, a
   MESMA ordem de filhos e o MESMO seletor `getComputedStyle(...).display`
   que `check_abas.mjs` mede; só o pai mudou. */
.faixa-viva {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 35;
  display: flex; flex-direction: column;
}
.faixa-totais {
  background: var(--color-bar);
  color: var(--escuro-tinta); border-top: 1px solid rgba(255,255,255,0.08);
}
/* DESIGN-7 item 1: a faixa inteira numa linha só, em 1720 E em 1366.
   `nowrap` no lugar de `wrap` -- o remédio era espaço, não quebra -- e o
   espaço vem de três cortes: a legenda perdeu a segunda linha (o
   `.faixa-legenda-sub` estático saiu do index.html), o `gap` e os paddings
   do miolo encolheram, e os KPIs (`.ft-item`) ficam menores (ver abaixo).
   `align-items: stretch` no lugar de `center`: os filhos têm alturas
   NATURAIS diferentes (a legenda e o `#ft-status` são uma linha; o `.ft-item`
   é duas; o botão carrega padding próprio) -- com `center` cada um centra
   pela PRÓPRIA altura e o `top` sai diferente por filho, ficando com CARA de
   uma linha sem SER uma linha (é o que o `check_abas`/a régua mede: o
   `Math.round(top)` tem de bater). Esticando todos para a altura da faixa e
   centrando o CONTEÚDO por dentro (cada filho abaixo virou coluna com
   `justify-content: center`), o `top` de todos fica o mesmo. */
.faixa-in {
  max-width: 1180px; margin: 0 auto; padding: 9px 24px;
  display: flex; align-items: stretch; gap: 16px; flex-wrap: nowrap;
}
/* RÓTULO À ESQUERDA (DESIGN-0b, encolhida na DESIGN-7): só o título agora --
   o subtítulo estático ("Atualizado com o cenário selecionado.") saiu do
   index.html, e a classe `.faixa-legenda-sub` que o carregava SAIU da folha
   com ele (a régua da fatia exige que ela deixe de existir no DOM). O que a
   faixa irmã do Comparar usava por baixo da mesma classe para um dado de
   verdade (o nome do lado A) virou `.faixa-legenda-meta`, sem herdar nome. */
.faixa-legenda {
  display: flex; flex-direction: column; justify-content: center; gap: 2px;
  padding-right: var(--sp-3, 12px);
}
.faixa-legenda-tit {
  font: 700 10px var(--display); letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--doma-branco); white-space: nowrap;
}
.faixa-legenda-meta {
  font: 400 10px var(--display); color: rgba(255,255,255,0.62); white-space: nowrap;
}
.ft-item {
  border-left: 1px solid rgba(255,255,255,0.18); padding-left: var(--sp-3, 12px);
  flex-shrink: 0;
  display: flex; flex-direction: column; justify-content: center; gap: 1px;
}
.ft-item span {
  display: block; font: 600 8.5px var(--display); letter-spacing: 0.13em;
  text-transform: uppercase; color: #6f8a7e; white-space: nowrap;
}
.ft-item b { font: 500 12px var(--mono); color: var(--doma-branco); white-space: nowrap; }
.ft-sep { width: 1px; align-self: stretch; background: rgba(255,255,255,0.18); flex-shrink: 0; }
/* RODAPÉ DE EFEITO (DESIGN-0b): mesma régua de visibilidade da faixa —
   `ativarAba` espelha o `.hidden` de `#faixa-totais` para `#faixa-rodape`
   (nova linha, sem duplicar a condição) em vez de repetir a lógica.
   DESIGN-7 item 1: mais baixo (~20px) -- a régua de ~56px é da faixa de
   KPIs, e o rodapé de mono é peso visual, não outra faixa. */
.faixa-rodape {
  background: var(--color-footer); color: var(--tinta-2);
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px; padding: 4px 28px;
  font: 500 9.5px var(--mono); letter-spacing: 0.02em;
}
.faixa-rodape span:last-child { text-align: right; }
/* O `min-width` fixo (10,8em) virou `min-width: 0` na DESIGN-7 item 1: com a
   faixa em `nowrap`, sobrar largura para o `.ft-status` deixou de ser
   negociável -- ele é quem cede, e o `white-space: nowrap` já garante que o
   texto do estado nunca quebra dentro de si mesmo. A fonte encolheu para
   10px para caber ao lado dos seis KPIs em 1366px. */
.ft-status {
  display: flex; align-items: center; justify-content: flex-end; gap: 7px;
  margin-left: auto; min-width: 0; flex-shrink: 0;
  font: 600 10px var(--display); letter-spacing: 0.04em; color: var(--tinta-3); white-space: nowrap;
}
.ft-status i { width: 8px; height: 8px; border-radius: 50%; background: #55695f; flex-shrink: 0; font-style: normal; }
/* AQUI HAVIA `.ft-status.ok i` E `.ft-status.ok span`, e a ausência delas É a
   especificação — mesma disciplina do `.ind-grav`. O chip verde "atualizado"
   anunciava funcionamento NORMAL, e funcionamento normal é silencioso: quem
   fala são só os estados que IMPEDEM o normal (`pend`, `erro`). Não devolva
   `.ok` a esta folha; enquanto o estado não tiver aparência declarada, ele não
   ressuscita por descuido.
   `.mudo` é o contrário de aparência: é a única declaração desta folha cujo
   trabalho é NÃO pintar. `visibility` e não `display`, porque a caixa tem de
   ficar no fluxo segurando o slot acima — e o atributo `hidden` não serve
   aqui, que a guarda da §0 desta folha o faz `display: none !important`. */
.ft-status.mudo { visibility: hidden; }
.ft-status.pend i { background: var(--ambar-vivo); }
.ft-status.pend span { color: var(--ambar-vivo); }
.ft-status.erro i { background: var(--vermelho-vivo); }
.ft-status.erro span { color: var(--vermelho-vivo); }
.ft-status.calc i { background: #8fb3a5; animation: pulsa 0.9s ease-in-out infinite; }
@keyframes pulsa { 50% { opacity: 0.25; } }
@media (prefers-reduced-motion: reduce) { .ft-status.calc i { animation: none; } }

/* LEITURA IPAD NA FAIXA VIVA (DESIGN-7 item 1, dobra da bloqueante do
   revisor sobre b24e748) -- em 1024/834 o botão "Ver resultado →" saía
   cortado (medido no print `ipad-1024-fluxo-topo.png`): a regra "uma linha,
   nunca esconder KPI" continua valendo, e a folga vem de ENCOLHER TEXTO, não
   de esconder nada. `.txt-curto`/`.txt-longo` são as DUAS FORMAS do MESMO
   rótulo, os dois em texto de verdade (nunca `::after`/`content`, que o
   `check_fonte` não conseguiria medir como texto) -- só o `display` troca.
   DOBRA (REPROVADO do revisor sobre 4f5df82): `.txt-curto { display: none }`
   tinha especificidade (0,1,0), e `.ft-item span { display: block }` (0,1,1)
   VENCIA -- os dois spans (longo e curto) pintavam empilhados, em TODAS as
   larguras, porque o seletor genérico `.ft-item span` também casa com os
   spans internos `.txt-longo`/`.txt-curto`. O conserto é especificidade
   (0,2,1), amarrada a cada casa que hospeda o par: `.ft-item`, a legenda e
   a faixa do Comparar (que tem seu próprio `.ft-item` com o mesmo par). */
.ft-item span.txt-curto,
.faixa-legenda-tit .txt-curto,
#faixa-comparar .txt-curto { display: none; }
.ft-item span.txt-longo,
.faixa-legenda-tit .txt-longo,
#faixa-comparar .txt-longo { display: inline; }
@media (max-width: 1100px) {
  .ft-item span.txt-longo,
  .faixa-legenda-tit .txt-longo,
  #faixa-comparar .txt-longo { display: none; }
  .ft-item span.txt-curto,
  .faixa-legenda-tit .txt-curto,
  #faixa-comparar .txt-curto { display: inline; }
  /* (1) legenda encolhe · (3) gaps/paddings e o número menores */
  .faixa-in { gap: 8px; padding: 8px 12px; }
  .faixa-legenda { padding-right: 6px; }
  .ft-item { padding-left: 6px; }
  /* D-401: a fonte não cede mais -- fica em 12px em toda largura; quem cede
     é o gap/padding em volta dela (a régua de sempre desta faixa). */
  /* (4) o estado cede GAP, nunca a fonte (a bolinha sozinha não é
     declaração -- D-187): o `min-width: 0` já valia desde a DESIGN-7. */
  .ft-status { gap: 5px; }
}
/* DOBRA (b): em 834 (innerWidth 818) o botão "Ver resultado →" ainda saía
   52 px fora do viewport com a folga do breakpoint de 1100px sozinha --
   aqui a faixa recebe um segundo corte, mais fundo, sem esconder nenhum
   KPI (a mesma disciplina do bloco acima). */
@media (max-width: 900px) {
  .faixa-in { gap: 5px; padding: 6px 10px; }
  .ft-item { padding-left: 5px; }
  .ft-item span.txt-longo,
  .ft-item span.txt-curto { font-size: 8px; }
  .ft-status { gap: 4px; }
}
/* DOBRA (c): faltavam 8 px em 834 — `.faixa-in` de #faixa-totais media
   scrollWidth 811 > clientWidth 803, com o botão vazando pela direita. O
   corte (b) apertou a faixa; este aperta também o BOTÃO, que carregava o
   acabamento padrão do `.btn-mini` (8/14px, 12px) sem entrar na conta.
   MEDIDO COM VALORES REAIS (não string vazia): os seis KPIs com dinheiro de
   verdade (ex.: "R$ 45.678.900") ainda estouravam depois do primeiro corte
   -- este segundo passo aperta gap/padding/fonte mais fundo, sem esconder
   nenhum rótulo nem valor. */
@media (max-width: 900px) {
  .faixa-in { gap: 2px; padding: 6px 4px; }
  .faixa-legenda { padding-right: 2px; }
  .ft-item { padding-left: 2px; }
  .ft-item span.txt-longo,
  .ft-item span.txt-curto { font-size: 7px; }
  .ft-status { gap: 2px; }
  .faixa-in .btn-mini { padding: 5px 7px; }
  /* D-401: nem o `.ft-item b` nem o `.ft-status` encolhem mais abaixo de
     12px em nenhuma largura -- quando o valor não cabe, quem cede é o
     `ajustarFaixaAbreviada()` (app.js), trocando o número por extenso pela
     forma abreviada (`fmtMoedaAbreviada`), nunca a letra. */
}

/* ============================================================
   7 · RESULTADO
   ============================================================ */
/* ---- O PLACEHOLDER DE PAINEL, E POR QUE ELE É ESCOPADO ----
   `vazio` é usada nesta casa em DOIS papéis diferentes:
     (a) BLOCO placeholder de painel — `#res-vazio`, `#fluxo-vazio`: ocupa a
         área toda, centraliza e respira 60px acima e abaixo;
     (b) ESTADO de um texto curto — `#tj-endereco.vazio` (a tarja) e
         `.emp-end.vazio` (o cartão da home), onde só a COR e o itálico
         mudam. Quem liga o (b) é `pintarTarjaEstudo`/`pintarCartao` no app.js.
   Enquanto esta regra era `.vazio` NUA ela valia para os dois, e o (b) herdava
   `padding: 60px 0` — 120px de caixa invisível em torno de uma linha de 15px.
   R1.1: era ISSO que tornava a aba `Sensibilidade` inclicável ao rolar. O
   `#tj-endereco` inchado esticava `.tj-estudo` para 154px dentro de uma tarja
   de 56px; `align-items: center` centrava o excesso, ele descia até y=105 e,
   por morar dentro do `.topo-fixo` (z-index 40), passava POR CIMA da barra de
   abas (z-index 30) — invisível, sem cor, sem borda, comendo o clique. O
   mesmo padding engordava todo cartão da home sem endereço em 120px.
   O `>` é o ponto: um placeholder só pode ser filho DIRETO de um painel.
   Estado de texto nunca é, e não há como esta regra voltar a alcançá-lo. */
.painel > .vazio { color: var(--tinta-2); padding: 60px 0; text-align: center; }

/* ---- ALERTAS — a decisão P4: severidade por ESTRUTURA, não por cor ----
   Três portadores independentes, e NENHUM deles é cor:
     1. a ESPESSURA da barra lateral — 6px crítico, 4px atenção, 2px nota.
        Geometria pura, sobrevive a qualquer filtro;
     2. a FORMA do ícone — ✕ / ⚠ / i, três silhuetas distintas;
     3. o RÓTULO de nível em caixa alta, que já existia.
   A cor é o quarto, e agora é o único dispensável.
   COMO CONFERIR: ponha a tela em escala de cinza. Se ainda dá para separar
   aviso de superfície selecionada, a estrutura está fazendo o trabalho.
   NENHUM `.alerta-*` usa pêssego — pêssego é interação, e só. */
.alertas { display: flex; flex-direction: column; gap: 6px; margin-bottom: 18px; }
.alerta-item {
  display: flex; align-items: flex-start; gap: 11px;
  border-left: 4px solid var(--ambar); background: var(--ambar-claro); color: #4a3c06;
  padding: 10px 14px 10px 13px; font-size: 13px; border-radius: 0 8px 8px 0;
}
.alerta-item::before {
  content: "\26A0";                 /* ⚠ — atenção é o padrão */
  flex-shrink: 0; width: 19px; height: 19px; margin-top: 1px;
  border: 1.5px solid currentColor; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font: 700 10.5px/1 var(--display);
  /* o círculo é DESENHADO em CSS e não depende de fonte nenhuma: se o glifo de
     dentro cair em tofu, ainda sobram o círculo, a espessura da barra e o
     rótulo — a distinção não fica pendurada num caractere. */
}
.alerta-nivel {
  font: 700 9.5px var(--display); letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--ambar); flex-shrink: 0; padding-top: 4px;
}
.alerta-item.critico {
  border-left-width: 6px; border-left-color: var(--vermelho);
  background: var(--vermelho-claro); color: #6b2417;
}
.alerta-item.critico::before { content: "\2715"; font-size: 11px; }   /* ✕ */
.alerta-item.critico .alerta-nivel { color: var(--vermelho); }
.alerta-item.critico span { font-weight: 600; }
.alerta-item.nota {
  border-left-width: 2px; border-left-color: var(--tinta-3);
  background: var(--superficie-2); color: var(--tinta-2);
}
/* o "i" da nota vem RETO: era itálico, e não há arquivo itálico da Inter no
   projeto — um glifo de ícone em oblíquo sintético é desenho falso a troco de
   nada. Nos rótulos de ausência ("endereço não informado") o oblíquo sintético
   FICA, porque lá a inclinação é o sinal e não o desenho. */
.alerta-item.nota::before { content: "i"; font: 800 12px/1 var(--display); }
.alerta-item.nota .alerta-nivel { color: var(--tinta-3); }

/* A MESMA FAIXA, DENTRO DE UM CARTÃO DE PREMISSA (D-074, 24/08/2026).
   O aviso do ano fiscal deslocado é o primeiro alerta a nascer nas premissas, e
   ele não ganha desenho próprio: herda `.alerta-item` inteiro — barra de 4px,
   círculo com o ⚠ desenhado em CSS, âmbar de texto do token. Faixa nova com
   calibração de matiz própria seria a segunda gramática de alerta do sistema, e
   a P4 existe justamente para não haver duas.
   O que muda é só o AR: dentro do cartão ela vem depois de uma grade de campos,
   e usa a mesma distância que a `.nota` usa do bloco de cima. */
.aviso-fiscal { margin: 14px 0 0; }

/* ---- LÁPIDE DO CARD PRETO (DESIGN-4/PDF, 12/09/2026) ----
   Aqui morava o card escuro do veredicto — gradiente `--escuro-1`/`--escuro-2`,
   os dois heróis (lucro, VPL) a 34px e a fila secundária (TIR, MIRR, exposição,
   payback) a 15px. O CARD VIROU CLARO, por ordem da Mesa citando o PDF
   aprovado pelo dono como referência: os números dos heróis e da fila JÁ
   estavam nos oito KPIs (`#kpi-linha`) que nasceram ao lado dele em 04/09 —
   repeti-los num card escuro em cima e num card claro embaixo era eco, não
   reforço. `--escuro-1`/`--escuro-2`/`--escuro-tinta-2` ficaram órfãos (só
   este bloco os usava) e saem do `:root`; `--escuro-3`/`--escuro-4`/
   `--escuro-linha`/`--escuro-tinta` continuam — `.btn-escuro` e
   `.dica::after` ainda os consomem (conferido por grep antes de tocar o
   `:root`).
   O que fica, adiante: `.veredicto-wrap` claro com duas colunas (juízo em
   prosa × o número que decide), o `#selo` INTACTO (é o mesmo veredicto do
   cartão da home, `check_casca` mede os dois juntos) e o contrafactual
   (`.ver-contraf`) traduzido para superfície clara. */
.veredicto-wrap {
  background: var(--superficie); border: 1px solid var(--linha);
  border-radius: var(--raio); overflow: hidden; box-shadow: var(--sombra);
}
.ver-corpo {
  display: flex; gap: 28px; flex-wrap: wrap;
  padding: 20px 24px 18px;
}
.ver-esq { flex: 1 1 320px; min-width: 0; }
/* FAIXA PRÓPRIA DO VPL (RES-2, NOTURNO-7 item 3, pranchas C/E do Codex):
   largura FIXA de ~480px, como a prancha — não mais uma coluna estreita de
   260px. A régua de D-210/D-263 abaixo ("SLOTS DE ALTURA FIXA") reservava o
   PIOR CASO de 2-3 linhas porque a coluna era estreita demais para o VPL
   caber numa linha só ao trocar de lente; a régua NOVA da Mesa inverte a
   prioridade — o número NUNCA quebra (`.ver-vpl-val` é `nowrap`) — e o que
   sobra do achado antigo é só o critério/quando, que ainda podem variar de
   1 para 2 linhas nesta faixa mais larga (`ver-criterio` mantém um
   `min-height` de 2 linhas por segurança). */
.ver-dir {
  flex: 0 0 480px; max-width: 480px; min-width: 220px;
  /* `align-items: stretch` (o padrão do flex, mantido aqui por clareza) em vez
     de `flex-end`: os filhos precisam ocupar a LARGURA INTEIRA da coluna —
     não só encolher até o próprio texto — para `ajustarVplFaixa` (app.js)
     conseguir medir overflow de verdade. Com `flex-end` o bloco encolhe
     junto do texto e `scrollWidth` nunca passa de `clientWidth`, porque os
     dois crescem juntos: a medição ficaria cega. `text-align: right`
     continua fazendo o trabalho visual de alinhamento. */
  display: flex; flex-direction: column; gap: 5px; align-items: stretch; text-align: right;
  padding-left: 26px; border-left: 1px solid var(--linha);
}
.selo {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 6px 14px; border-radius: 999px; margin-bottom: 12px;
  font: 700 11px var(--display); letter-spacing: 0.16em; text-transform: uppercase;
}
.selo::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
.selo.ok { background: var(--color-primary-soft); color: var(--color-primary); }
.selo.atencao { background: var(--color-warning-soft); color: var(--ambar); }
.selo.ruim { background: var(--color-danger-soft); color: var(--color-danger); }
/* SLOT DE ALTURA (D-210/D-263, achado do dono em 12/09), ENCOLHIDO NA RES-2:
   trocar de lente ainda pode trocar o TEXTO do critério ("o fluxo termina
   negativo" × "VPL > 0 à TMA…, e este estudo não passa"), e ele ainda pode
   quebrar entre 1 e 2 linhas — o `min-height` reserva esse caso para a
   coluna não pular na troca. O VPL SAIU desta régua: ele é `nowrap` por
   desenho (abaixo), nunca quebra, e por isso não precisa mais reservar
   pior caso de linhas. */
.ver-frase { margin: 0 0 8px; font: 800 25px/1.15 var(--display); letter-spacing: -0.02em; color: var(--tinta); min-height: 29px; }
.ver-explicacao { margin: 0; font: 400 13px/1.5 var(--display); color: var(--tinta-2); max-width: 52ch; min-height: 39px; }
.ver-vpl-rot {
  font: 600 10px var(--display); letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--tinta-2);
}
/* O NÚMERO QUE DECIDE, A 56PX (RES-2/D-401): sinal, "R$" e os dígitos SEMPRE
   na mesma linha (`white-space: nowrap`) — a quebra do afixo que existe no
   `.kpi-valor` (D-233) NÃO se aplica aqui, porque a régua desta fatia proíbe
   qualquer quebra no VPL do veredicto. Quando o valor por extenso não coube
   nesta largura, `ajustarVplFaixa` (app.js) troca para `fmtMoedaAbreviada`
   em vez de a folha encolher a letra — a letra nunca encolhe (D-401). */
.ver-vpl-val {
  font: 500 56px/1.1 var(--mono); letter-spacing: -0.02em; min-height: 62px; display: block;
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
.ver-vpl-val.positivo { color: var(--color-primary); }
.ver-vpl-val.negativo { color: var(--color-danger); }
.ver-criterio { font: 400 12px/1.4 var(--mono); color: var(--tinta-2); margin-top: 4px; min-height: 34px; display: block; }
.ver-quando { font: 400 12px var(--mono); color: var(--tinta-3); min-height: 14px; }
/* TMA-1 (14/09/2026) — a leitura da TMA + 3 p.p. e da folga de taxa, sob o
   VPL do veredicto: 13px mono, o degrau abaixo do `.ver-vpl-val` de 56px
   (D-401), nunca competindo com ele. Vazia (sem `<span>` dentro) na limpeza
   do Resultado e antes do primeiro cálculo — mesma disciplina de ausência do
   resto do card, `margin` só nasce com conteúdo. */
.ver-tma { margin: 0; display: flex; flex-direction: column; gap: 2px; }
.ver-tma span { font: 400 13px/1.4 var(--mono); color: var(--tinta-3); }
/* LEITURA IPAD (RES-2, ≤1100px, o mesmo `BP_IPAD` do `.kpi-linha` logo
   abaixo): a faixa do VPL sobe para CIMA do selo e ocupa a linha inteira —
   `order` reordena só a PINTURA, o DOM continua com o juízo (`.ver-esq`)
   antes do número (D-225: nenhuma estrutura muda, só a roupa). A 48px o
   valor continua numa linha só nos casos reais medidos (ver o card). */
@media (max-width: 1100px) {
  .ver-corpo { flex-direction: column; gap: 18px; }
  /* `flex: 1 1 320px` é medida de LARGURA (o eixo principal horizontal do
     desktop); em coluna o eixo principal vira vertical, e aquele "320px"
     passaria a ser altura MÍNIMA — um vão vazio abaixo da frase/explicação,
     bem maior que o conteúdo. `flex-basis: auto` volta a medir pelo
     conteúdo, que é o que a pilha vertical precisa. */
  .ver-esq { flex: 0 1 auto; }
  /* MESMO DEFEITO DO `.ver-esq` ACIMA, na outra metade do par: `flex: 0 0
     480px` tinha `flex-shrink: 0`, então a "largura" de 480px virava altura
     FIXA e TRAVADA em coluna — o vão vazio media exatamente 480px menos o
     conteúdo real (medido: 725px de card contra ~165px de conteúdo). */
  .ver-dir {
    order: -1; flex: 0 1 auto; width: 100%; max-width: none;
    padding-left: 0; border-left: 0;
    padding-bottom: 16px; border-bottom: 1px solid var(--linha);
  }
  .ver-vpl-val { font-size: 48px; min-height: 54px; }
}

/* ---- o contrafactual, dentro do MESMO card, agora em superfície clara ----
   Só existe com o financiamento ligado. O par "com × sem" fica junto do número
   que decide, e não a três blocos de distância: o delta é a resposta da
   pergunta "a alavancagem paga NESTE estudo?", e a pergunta se faz aqui. */
.ver-contraf { border-top: 1px solid var(--linha); background: var(--superficie-2); }
.ver-contraf .cf-cab {
  padding: 11px 22px 0; font: 700 9.5px var(--display);
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--tinta-2);
}
.ver-contraf .cf-cab em {
  font: 400 11px var(--mono); font-style: normal; color: var(--tinta-3);
  letter-spacing: 0; text-transform: none;
}
.cf-grade { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); }
.cf-cel { padding: 9px 22px 14px; }
.cf-cel .rot {
  font: 600 9px var(--display); letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--tinta-2); margin-bottom: 5px;
}
.cf-cel .par { display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap; }
.cf-cel .par b { font: 500 15px var(--mono); color: var(--tinta); }
.cf-cel .par i { font: 400 12px var(--mono); font-style: normal; color: var(--tinta-3); }
.cf-cel .delta { font: 500 12px var(--mono); margin-top: 3px; }
.cf-cel .delta.pos { color: var(--verde-escuro); }
.cf-cel .delta.neg { color: var(--vermelho); }

/* A BARRA DE PENDÊNCIAS, DENTRO DO CARD (DESIGN-4/PDF): mesma régua da
   `.res-barra-pend` de sempre (fundo, barra de 4px à esquerda, ícone), só
   fechando o card em vez de flutuar solta — raio só embaixo, sem margem
   própria (o card já respira, e a margem empurraria o que vem depois). */
/* ALTURA LIVRE AQUI (ao contrário do `height:20px` fixo da barra solta): o
   `height:20px` + `padding:10px 16px` + `box-sizing:border-box` (global)
   dava CONTEÚDO DE ALTURA ZERO — os itens centralizavam sobre uma linha sem
   corpo, e a caixa ficava rasa demais para o texto e o ícone, vazando pela
   margem inferior sobre a borda do card (achado do dono, print 1720px). Como
   última linha do card ela pode crescer sem empurrar NADA (não há mais
   irmão abaixo dela dentro do card) — o `min-height` fixo era só para não
   mover o que vinha DEPOIS, na barra solta de antes; aqui não há depois.
   O PISO SUBIU DE 40 PARA 60px NO ADENDO NOTURNO-6 item 3 (12/09/2026): a
   barra passou a mostrar a PRIMEIRA FRASE INTEIRA (D-263/passo 13 continua
   valendo — o slot é fixo e não se move ao trocar de lente), e uma frase
   de alerta real pode ocupar até duas linhas em 13px/1.35. Medido no
   Gardens North, nas três lentes, em 1720 e 1366: a altura bate igual.
   O PISO SUBIU DE 60 PARA 73px NA DOBRA R2 (NOTURNO-6 item 6): o slot de 60
   era aposta na ORDEM dos alertas -- a frase mais longa real do Gardens
   North (239 caracteres) dá 3,2 linhas em 650px, e o piso só cobria duas.
   73px = 3×17,55px (linha, R1) + 20px (padding vertical). Para o resíduo
   que ainda passar de três linhas, `ajustarAlturaBarraPendencias` (app.js)
   aplica um RATCHET: o slot cresce e nunca encolhe dentro do mesmo estudo. */
.veredicto-wrap .res-barra-pend {
  height: auto; min-height: 73px;
  margin: 0; border-radius: 0 0 var(--raio) var(--raio);
}

/* ---- INCORPORADOR × INVESTIDORES — A MESMA GRAMÁTICA DO CARD DO VEREDICTO (R3.4)
   Nasceu clara contra um card preto (a nota abaixo é a ERRATA disso: o
   veredicto virou claro também em 12/09/2026, DESIGN-4/PDF — ver a lápide em
   `.veredicto-wrap`). O que as une continua sendo GEOMETRIA, não cor: topo
   com rótulo dominante (`.inv-topo`/`.inv-marca`, escala de pílula, `--tinta`
   sólido — o mesmo par que `.cenarios button.ativo` já usa), a CONTA como
   equação (`.inv-conta`, 26px, o herói deste bloco) e a fila tabular de
   15px (`.inv-fila`) — MESMAS medidas da fila que o card do veredicto tinha
   e que hoje vive nos oito KPIs.

   A RAZÃO DE CORPO, e ela é medida: 30 ÷ 15 = 2,00× dentro deste bloco. O
   herói daqui (a conta do incorporador) é subordinado ao VPL/lucro do
   projeto nos KPIs — a subordinação é geométrica, não é promessa. */
.inv-wrap {
  background: var(--superficie); border: 1px solid var(--linha);
  border-radius: var(--raio); overflow: hidden; box-shadow: var(--sombra);
}
.inv-topo {
  display: flex; align-items: center; gap: 13px; flex-wrap: wrap;
  padding: 12px 20px; border-bottom: 1px solid var(--linha);
  background: var(--superficie-2);
}
/* o equivalente do selo: rótulo dominante em três palavras. Não é veredicto —
   este bloco não julga nada —, então não usa a rampa ok/atenção/ruim. */
.inv-marca {
  display: inline-flex; align-items: center; gap: 8px;
  padding: 5px 13px; border-radius: 999px;
  background: var(--tinta); color: var(--doma-branco);
  font: 700 10px var(--display); letter-spacing: 0.16em; text-transform: uppercase;
}
.inv-criterio { font: 400 12.5px var(--mono); color: var(--tinta-2); }
.inv-quem { margin-left: auto; font: 400 12px var(--mono); color: var(--tinta-3); }

/* A CONTA — e ela é uma EQUAÇÃO de propósito.
   O delta "existia e não se lia" porque era um cartão solto chamado
   "Participações", do mesmo tamanho dos outros seis. Aqui ele é o termo do
   meio de `A − B = C`: os operadores são colunas próprias da grade, a parcela
   subtraída fica em superfície REBAIXADA (o mesmo recurso do `.ver-contraf`,
   traduzido para o claro) e o resultado é o maior número do bloco. */
.inv-conta { display: grid; grid-template-columns: 1fr auto 1fr auto 1fr; }
.inv-cel { padding: 16px 20px 18px; }
.inv-cel.corte { background: var(--superficie-2); }
.inv-op {
  display: flex; align-items: center; justify-content: center; width: 26px;
  font: 400 19px var(--mono); color: var(--tinta-3);
  border-left: 1px solid var(--linha); border-right: 1px solid var(--linha);
}
.inv-cel .rot {
  font: 600 9.5px var(--display); letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--tinta-2); margin-bottom: 7px;
}
.inv-cel .val { font: 500 26px/1 var(--mono); color: var(--tinta); letter-spacing: -0.02em; }
.inv-cel.total .val { font-size: 30px; }
.inv-cel .val.positivo { color: var(--verde-escuro); }
.inv-cel .val.negativo { color: var(--vermelho); }
.inv-cel .sub { font: 400 12px/1.45 var(--mono); color: var(--tinta-2); margin-top: 7px; }

/* A FILA — 15px, tabular. Cada célula traz o número do INCORPORADOR em cima
   e o do PROJETO embaixo: a comparação das duas visões é sistemática, célula
   a célula, em vez de morar num rótulo. */
.inv-fila {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(158px, 1fr));
  border-top: 1px solid var(--linha); background: var(--superficie-2);
}
.inv-fila .cel { padding: 12px 20px 14px; border-right: 1px solid var(--linha); }
.inv-fila .cel:last-child { border-right: 0; }
.inv-fila .rot {
  font: 600 9px var(--display); letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--tinta-2); margin-bottom: 5px;
}
.inv-fila .val { font: 500 15px var(--mono); color: var(--tinta); letter-spacing: -0.01em; }
.inv-fila .val.positivo { color: var(--verde-escuro); }
.inv-fila .val.negativo { color: var(--vermelho); }
.inv-fila .sub { font: 400 12px var(--mono); color: var(--tinta-3); margin-top: 3px; }
/* o R$ comparado nunca se parte: nem entre o sinal e o "R$", nem entre o "R$" e
   os dígitos. Um menos sozinho no fim da linha lê-se como travessão.
   DOBRA B3 (14/09/2026): o mesmo `.nb` passa a servir o contexto do custo do
   dinheiro (`.kpi-contexto`/`.fluxo-resumo .contexto`) -- cada parcela
   ("banco R$ 6,08 mi") é uma unidade que não quebra por dentro; o espaço
   entre parcelas é o único ponto de quebra que os dois containers aceitam. */
.inv-cel .sub .nb, .inv-fila .sub .nb, .kpi-contexto .nb, .fluxo-resumo .contexto .nb { white-space: nowrap; }

/* a tabela por investidor herda `.tabela-resultado`; o afastamento morava num
   `style=` no HTML e virou regra aqui, que é onde estilo mora nesta casa. */
.tab-inv-det { margin-top: 10px; }

/* ---- RESERVA DO SELETOR DE CENÁRIOS (item f) ----
   Desabilitado HONESTO: aparece, não funciona, e diz por quê — o mesmo padrão
   do Exportar (D-123). O do meio não é desabilitado porque o que está digitado
   É o conservador; desabilitar os três faria o controle parecer quebrado.
   O invólucro `.dica` traz `cursor: not-allowed`, e o botão vivo o desfaz: o
   cursor tem de mentir o mínimo possível num controle meio-vivo. */
.cenarios-linha {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  margin-bottom: 14px;
}
.cenarios-linha .nota { margin: 0; flex: 1; min-width: 260px; }
.cenarios {
  display: inline-flex; border: 1px solid var(--linha); border-radius: 999px;
  overflow: hidden; background: var(--superficie);
}
/* A PÍLULA ACESA (CEN.3): NOME em cima, LUCRO da lente embaixo.
   As três posições têm de medir o MESMO, senão a comparação vertical vira
   comparação de larguras — por isso `min-width` e `tabular-nums` no número: sem
   a figura tabular, "1,2" e "1,1" ocupam larguras diferentes e a coluna dança a
   cada recálculo. O `min-height` do slot do lucro é a regra do D-208: a altura
   não depende do conteúdo, então a lente sem delta declarado (slot vazio) não
   encolhe o botão nem move a linha. */
.cenarios button {
  border: 0; background: transparent; padding: 6px 14px 7px;
  font: 600 11.5px var(--display); color: var(--tinta-2); cursor: pointer;
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  min-width: 104px;
}
.cenarios button.ativo { background: var(--tinta); color: var(--doma-branco); cursor: default; }
.cenarios button[disabled] { color: var(--tinta-3); cursor: not-allowed; }
.cenarios .cen-lucro {
  font: 500 12px/14px var(--mono); min-height: 14px;
  font-variant-numeric: tabular-nums; font-style: normal; opacity: 0.85;
}

.cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(168px, 1fr)); gap: 10px; margin-top: 12px; }
.card {
  background: var(--superficie); border: 1px solid var(--linha); border-radius: 10px;
  padding: 14px 16px; box-shadow: var(--sombra);
}
/* VAR-1 item b (14/09/2026): 9,5px medido abaixo do piso da casa (D-401) --
   o rótulo carrega texto que lê como fato do estudo (ex.: "1ª amortização"),
   não só categoria decorativa; sobe para o piso de 12px, igual às outras
   folhas de rótulo já corrigidas na mesma fatia. */
.card .rotulo { font: 600 12px var(--display); letter-spacing: 0.1em; text-transform: uppercase; color: var(--tinta-2); }
.card .valor { font: 500 15px var(--mono); margin-top: 6px; }
/* Métrica normalizada nunca vem sozinha: esta linha é onde o R$ acompanha a
   taxa e o mês acompanha o pico. O terceiro par — o contrafactual ao lado do
   delta — saiu daqui em 03/08/2026 e mora no card preto (`.cf-cel`), que
   mostra o "com", o "sem" e o delta juntos; a lápide está em
   `renderFinanciamento`, no app.js. */
.card .abaixo { font: 400 12px/1.4 var(--mono); color: var(--tinta-2); margin-top: 4px; }
/* Cartão cujo sub carrega texto do motor (CET recusado traz a causa inteira, em
   ~250 caracteres). Numa coluna de 168px isso empilha em 15 linhas e estica a
   linha inteira de cartões, que se alinham pela mais alta. Só acima do
   breakpoint: com uma coluna só, `span 2` criaria coluna implícita e vazaria. */
@media (min-width: 761px) { .card.largo { grid-column: span 2; } }

/* COMPOSIÇÃO DO ESTUDO (RES-2, NOTURNO-7 item 3, pranchas C/E do Codex):
   os MESMOS oito números que `.cards`/`.card` mostravam soltos, agora em
   QUATRO GRUPOS de largura igual — Receita, Terreno, Obra, Leitura. Classes
   PRÓPRIAS (`.comp-*`), nunca `.cards`/`.card`: aquela dupla continua
   servindo `#cards-indexacao` (o bloco "a preços correntes") sem tocar
   nesta roupa nova (D-225). Rótulo discreto acima da grade (`.comp-titulo`,
   a mesma tipografia do eyebrow de `.res-sec-cab .num`, sem o fundo em
   pílula — o bloco não ganha numeração de seção nova, D-076: esqueleto é
   do dono). */
.comp-titulo {
  font: 700 12px var(--display); letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--color-primary); margin: 26px 0 10px;
}
.comp-estudo { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
/* LEITURA IPAD, mesmo `BP_IPAD` de 1100px do resto da aba: 2×2 em vez de
   4×1 — a prancha C mostra os quatro grupos em duas linhas de dois. */
@media (max-width: 1100px) { .comp-estudo { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.comp-grupo {
  background: var(--superficie); border: 1px solid var(--linha);
  border-radius: var(--raio); box-shadow: var(--sombra); overflow: hidden;
}
.comp-grupo-titulo {
  padding: 12px 16px; background: var(--color-primary-soft); color: var(--color-primary);
  font: 600 13px var(--display);
}
.comp-item { padding: 15px 16px; border-top: 1px solid var(--linha); }
.comp-item:first-child { border-top: 0; }
.comp-item .rotulo { font: 400 12px/1.45 var(--display); color: var(--tinta-2); margin: 0 0 8px; }
.comp-item .valor {
  font: 500 21px var(--mono); text-align: right; white-space: nowrap;
  overflow-wrap: anywhere;
}
/* AUSÊNCIA DENTRO DO ITEM ("não houve exposição"): prosa curta ganha a
   folha da prosa, nunca a mono do número — a mesma régua de
   `.kpi-valor .sem-valor` (D-322), com o texto ainda alinhado à direita. */
.comp-item .valor .sem-valor {
  display: inline-block; font: 400 13px/1.35 var(--display); color: var(--tinta-2);
  white-space: normal; letter-spacing: 0; text-align: right;
}
/* A DECOMPOSIÇÃO DO CUSTO DE OBRA ("Puro"/"Administração"), duas linhas
   menores dentro do MESMO item — nunca um segundo subtotal (D-076): são os
   mesmos dois campos que `.card .abaixo` já mostrava em texto corrido, só
   em pares rótulo/valor como o resto da grade. */
.comp-sub {
  display: flex; justify-content: space-between; gap: 12px;
  padding-top: 8px; font: 400 12px/1.5 var(--display); color: var(--tinta-2);
}
.comp-sub b { font: 500 12px var(--mono); color: var(--tinta); white-space: nowrap; }

.fin-notas {
  margin: 12px 0 0; padding: 12px 16px 12px 32px;
  background: var(--superficie); border: 1px solid var(--linha);
  border-radius: var(--raio); box-shadow: var(--sombra);
  font-size: 12.5px; line-height: 1.55; color: var(--tinta-2);
}
.fin-notas li + li { margin-top: 6px; }
.fin-notas b { color: var(--tinta); }

.sub { font: 700 15px var(--display); margin: 28px 0 10px; }

/* ================= DESIGN-4 — ABA RESULTADO v2 ================= */

/* BARRA DE PENDÊNCIAS, no topo. Estrutura em vez de só cor (mesma disciplina
   da P4/`.alerta-item`): fundo âmbar-claro, borda esquerda de 4px, itens
   separados por divisória vertical. */
/* DOBRA (bloqueante do `check_cenarios`, passo 13, D-263/M2-CEN.6): a barra
   é um SLOT DE ALTURA ESTÁVEL — ela nunca some e nunca cresce entre lentes.
   Trocar de cenário pode acender/apagar pendência (delta de cenário produz
   ou resolve alertas), e "sumir/aparecer" movia tudo abaixo dela — a mesma
   doença que a `.periodo-obra`/`.ft-status` já resolvem com `min-height`
   fixo. `.vazia` é a AUSÊNCIA DECLARADA (D-233): texto mudo, nunca caixa
   vazia nem "hidden". */
.res-barra-pend {
  display: flex; align-items: flex-start; gap: 14px; flex-wrap: nowrap;
  height: 20px; overflow: hidden;
  background: var(--ambar-claro); border-left: 4px solid var(--ambar);
  border-radius: 0 var(--raio) var(--raio) 0;
  padding: 10px 16px; margin-bottom: 18px; font-size: 13px; color: #4a3c06;
}
.res-barra-pend.vazia {
  background: var(--superficie-2); border-left-color: var(--tinta-3); color: var(--tinta-2);
}
/* O "!" EM CÍRCULO (DESIGN-4/PDF) — mesma técnica do `.alerta-item::before`:
   desenhado em CSS, não depende de fonte de ícone nenhuma. */
.res-barra-pend::before {
  content: "!"; flex-shrink: 0; width: 16px; height: 16px; margin: 0;
  border: 1.5px solid currentColor; border-radius: 50%; color: var(--ambar);
  display: inline-flex; align-items: center; justify-content: center;
  font: 700 10px/1 var(--display);
}
.res-barra-pend.vazia::before { content: "i"; color: var(--tinta-3); }
.rbp-resumo { font-weight: 700; flex-shrink: 0; white-space: nowrap; padding-top: 1px; }
/* ITEM 3 (ADENDO NOTURNO-6, 12/09/2026): a barra passou a mostrar A
   PRIMEIRA FRASE INTEIRA (a crítica, se houver; senão a primeira atenção) em
   vez dos 3 primeiros textos recortados com reticências — reticências
   escondiam a causa exata; ninguém lia o que estava pendente sem abrir a
   seção 09. Por isso `.rbp-itens` deixa de truncar e passa a QUEBRAR
   linha — o SLOT vira `min-height` (na regra `.veredicto-wrap
   .res-barra-pend`, que é a única instância viva), dimensionado para DUAS
   linhas de texto: a altura fica igual nas três lentes contanto que a frase
   caiba em até duas linhas, o que foi medido no Gardens North (o vigia de
   `D-263/passo 13` continua de pé — o que mudou é o que cabe no slot, não a
   garantia de que ele não se move ao trocar de lente). */
/* DOBRA R1 (NOTURNO-6 item 6): `line-height` mora em `.rbp-itens`, não no
   `span` -- o `span` herdava o 1.55 do `body` até o layout ser resolvido
   (o strut da PRIMEIRA linha nasce do line-height do BLOCO, não do inline
   dentro dele), e era esse 1.55 fantasma que fazia duas linhas de 13px
   medirem 60,3px contra os 60px do piso do CSS (D-263/passo 13 acusava por
   0,3px). Com a regra no bloco, o span herda e a conta fecha: 2×17,55px. */
.rbp-itens { flex: 1 1 auto; min-width: 0; line-height: 1.35; }
.rbp-itens span { white-space: normal; overflow-wrap: break-word; }
.rbp-link {
  flex-shrink: 0; margin-left: auto; font-weight: 700; color: var(--ambar);
  text-decoration: none; white-space: nowrap; padding-top: 1px;
}
.rbp-link:hover { text-decoration: underline; }

/* CABEÇALHO DA ABA (DESIGN-4/PDF) — reaproveita `.cartao-cab`/`.eyebrow`/`h2`
   (o mesmo par que abre cada cartão de Premissas, DESIGN-0): eyebrow +
   título grande à esquerda, e à direita o estado vivo do estudo. Substitui
   o antigo `.res-cab-01`, que misturava o rótulo da seção 01 com a
   identidade do estudo em tela. */
/* DOBRA (bloqueante do `check_cenarios`, passo 13): a linha do CABEÇALHO
   continua sem `flex-wrap` — o par eyebrow/h2 e o botão não se movem.
   O QUE MUDOU (ADENDO NOTURNO-6 item 1, 12/09/2026): o carimbo
   `.res-cab-estudo` deixou de cortar com reticências (nome + "Atualizado em
   …" saíam como "…202…" em 1720px, medido) — nome e data são CONSTANTES do
   estudo, não mudam ao trocar de lente (D-263/passo 13 protege contra
   variação POR CENÁRIO, e esta não é uma), então deixar o carimbo quebrar em
   duas linhas aqui não reabre aquele defeito: a altura muda uma vez, na
   abertura do estudo, e fica estável dali em diante. */
.res-topo-info {
  margin-left: auto; display: flex; align-items: center; gap: 14px;
  flex-wrap: nowrap; flex-shrink: 0;
}
.res-cab-estudo {
  font: 400 12.5px var(--mono); color: var(--tinta-2);
  display: inline-block; max-width: 320px;
  white-space: normal; overflow-wrap: break-word; vertical-align: bottom;
}
.btn-comparar-versoes {
  font: 600 12.5px var(--display); color: var(--verde-escuro);
  background: var(--superficie); border: 1px solid var(--linha);
  border-radius: var(--r-1); padding: 6px 12px; cursor: pointer;
}
.btn-comparar-versoes:hover { background: var(--superficie-2); }

/* CABEÇALHO NUMERADO DE SEÇÃO (DESIGN-4/PDF) — a faixa clara que abre 01, 02
   e 09 (as três roupas desta fatia; 03 a 08 continuam com o `<h3 class="sub">`
   de sempre, que é roupa de outra fatia). `--color-primary-soft` é o mesmo
   token do selo "Viável", nunca um verde novo. */
.res-sec-cab {
  display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
  background: var(--color-primary-soft); border-radius: var(--r-2);
  padding: 10px 18px; margin: 26px 0 10px;
}
.res-sec-cab .num {
  font: 700 12px var(--display); letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--color-primary);
}
.res-sec-cab .pergunta { font: 400 13px var(--display); color: var(--tinta-2); }

/* OS OITO KPIS. 4×2 SEMPRE (o PDF nunca desenha os oito numa linha só) — a
   regra de 8 colunas em 1400px foi REMOVIDA em 12/09 (DESIGN-4 parte A3):
   ela estourava a página para 2163px em telas de 1720, rolagem horizontal
   inclusa. 2 colunas no celular. */
.kpi-linha {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; margin-bottom: 8px;
}
/* LEITURA IPAD (DESIGN-6) -- 1100px, o breakpoint da casa (ver o bloco de
   medicao logo acima de `.fluxo-resumo`). Abaixo dele o `.kpi` (que nao
   encolhe por baixo de ~250px de conteudo minimo: numero em mono, icone,
   rotulo) nao cabe 4 por linha sem estourar o container -- MEDIDO em
   headless: 1065px de scrollWidth contra 1008px/818px de innerWidth em
   1024/834 (iPad paisagem/retrato), com `repeat(4, 1fr)` sem o `minmax(0, …)`
   deixando a grade crescer alem do container disponivel. */
@media (max-width: 1100px) { .kpi-linha { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* MESMA ALTURA ENTRE OS OITO (RES-2, NOTURNO-7 item 3, prancha C do Codex):
   `min-height` é o piso medido na prancha (152px desktop, 146px no
   `BP_IPAD`); o conteúdo mais alto de uma FILEIRA (ausência em duas linhas,
   por exemplo) segue esticando os vizinhos da mesma fileira pelo
   `align-items: stretch` padrão do grid — o piso só garante que nenhum card
   fique mais baixo que isso mesmo sozinho numa fileira curta. */
.kpi {
  background: var(--superficie); border: 1px solid var(--linha);
  border-radius: var(--raio); box-shadow: var(--sombra); padding: 12px 14px 13px;
  min-height: 152px;
}
@media (max-width: 1100px) { .kpi { min-height: 146px; } }
/* O ÍCONE EM CÍRCULO (DESIGN-4/PDF) — cor pelo SIGNIFICADO: verde/vermelho
   quando `k.cls` traz o sinal do valor (VPL, lucro, exposição, VPL do
   incorporador), neutro para as métricas informativas (TIR, payback, custo
   do dinheiro, MIRR — não têm "bom"/"ruim" sozinhas, dependem do contexto).
   28px (RES-2): a prancha C mede o círculo maior que o antigo de 26px. */
.kpi-icone {
  width: 28px; height: 28px; border-radius: 50%; margin-bottom: 8px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--superficie-2); color: var(--tinta-2);
}
.kpi-icone svg { width: 16px; height: 16px; }
.kpi-icone.positivo { background: var(--color-primary-soft); color: var(--color-primary); }
.kpi-icone.negativo { background: var(--color-danger-soft); color: var(--color-danger); }
.kpi-titulo {
  display: flex; align-items: center; gap: 5px;
  font: 600 9.5px var(--display); letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--tinta-2);
}
/* DOBRA (bloqueante do `check_cenarios`, passo 13): CADA LINHA DO CARD É UM
   SLOT DE UMA LINHA SÓ. Conteúdo publicado pelo motor muda de tamanho entre
   lentes (rótulo de ausência, R$ com mais dígitos, "banco X + fundo Y") — se
   qualquer linha pudesse quebrar em duas, a ALTURA DA LINHA INTEIRA de KPIs
   mudaria ao trocar de cenário, e isso é exatamente o que a régua do dono
   proíbe (slot, nunca linha condicional — a mesma do `.ft-status`/
   `.emp-lanc`). Truncar com reticências é o preço aceito por estabilidade.
   EXCEÇÃO NOMEADA (DOBRA B3, 14/09/2026): `.kpi-contexto` sai desta regra —
   "número nunca corta" venceu "slot de uma linha" só para ELA, porque as
   duas parcelas do custo do dinheiro (banco+fundo) não cabem cortadas nem
   inteiras numa linha de 244px. `.kpi-titulo-txt` e `.kpi-delta` continuam
   presos ao slot de uma linha — só o contexto ganhou `min-height`. */
.kpi-titulo-txt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.kpi-info {
  flex-shrink: 0; width: 13px; height: 13px; border-radius: 50%;
  border: 1px solid var(--tinta-3); color: var(--tinta-3);
  display: inline-flex; align-items: center; justify-content: center;
  font: 700 9px/1 var(--display); font-style: normal;
  /* 1d CONSERTO (DIURNA-8): era `cursor: help` prometendo ajuda que o
     clique não entregava (achado do `check_ajuda` cena 2b) -- agora ele
     abre o painel de ajuda (ver o handler de `[data-ajuda]` no app.js). */
  cursor: pointer;
}
/* ITEM 1 (acabamento do selo de 12/09, ordem da Mesa): "KPIs sem
   reticências" -- o VALOR nunca trunca com "...". `.kpi-titulo-txt` e
   `.kpi-delta` continuam com nowrap+ellipsis (o rótulo pode truncar,
   D-233/D-208 do card DOBRA acima); `.kpi-contexto` SAIU da lista na DOBRA
   B3 (ver o comentário dela, acima) -- só o número muda. A
   quebra acontece no ESPAÇO do próprio `fmtBRL` ("R$ " + número): é a
   "quebra do afixo" que a ordem autoriza, e o número em si (sem espaço,
   só ponto de milhar) nunca parte ao meio nesse caminho.
   `overflow-wrap: anywhere` é a rede de segurança para o caso patológico
   em que o número sozinho, sem espaço nenhum, ainda assim não coubesse. */
.kpi-valor {
  font: 500 22px var(--mono); margin-top: 6px; letter-spacing: -0.01em;
  overflow-wrap: anywhere;
}
.kpi-valor.positivo { color: var(--verde-escuro); }
.kpi-valor.negativo { color: var(--vermelho); }
/* DOBRA B3 (14/09/2026): `height` fixo (uma linha) cortava com ellipsis o
   contexto do custo do dinheiro quando as duas fontes (banco+fundo)
   coexistem -- "banco R$ 6,08 mi + fundo R$ 8,40 mi" mede 252px contra
   244px de caixa. Os OUTROS sete KPIs nunca têm duas parcelas, então
   `min-height` (não `height`) preserva a altura de UMA linha no caso comum
   e só cresce quando o `.nb` de cada parcela força a quebra entre elas —
   nunca DENTRO de uma parcela (ver `.nb` acima). */
.kpi-contexto {
  /* 12px (RES-2/D-401): fonte numérica nunca abaixo do piso da casa. */
  font: 400 12px/1.4 var(--mono); color: var(--tinta-2); margin-top: 3px; min-height: 17px;
  white-space: normal; overflow-wrap: anywhere;
}
.kpi-delta {
  font: 600 12px var(--mono); margin-top: 5px; height: 15px; line-height: 15px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.kpi-delta.pos { color: var(--verde-escuro); }
.kpi-delta.neg { color: var(--vermelho); }
.kpi-delta.empate { color: var(--tinta-3); }
.kpi-delta.sem { color: var(--tinta-3); font-weight: 400; }
/* A LEGENDA ABAIXO DA GRADE (DESIGN-4/PDF) — herda `.nota` (mesma folha que
   toda nota do sistema usa); só o espaçamento muda, porque aqui ela cola no
   grid de cima em vez de abrir um bloco novo. */
.kpi-legenda { margin: 0 0 22px; }

/* LÁPIDE DA GRADE 48/52 (12/09/2026) — o wrapper que dividia a aba em duas
   colunas (incorporador à esquerda, mix/preço à direita) morreu com a nova
   ordem numerada (01..09) em coluna única, do PDF aprovado pelo dono. Nenhum
   id morreu junto — só o WRAPPER e a régua de 1500px que ele carregava. */

/* FINANCIAMENTO EM DUAS COLUNAS (item 05) — os MESMOS `.card` de sempre,
   só em grade de 2 em vez de `auto-fit`. */
.cards-2col { grid-template-columns: 1fr 1fr; }
@media (max-width: 560px) { .cards-2col { grid-template-columns: 1fr; } }

/* FUNDO (06) — tabela de duas colunas rótulo/valor, a mesma anatomia de
   `.tabela-resultado` com a primeira coluna à esquerda e a segunda à
   direita (herdado); aqui só a largura fixa da primeira coluna muda,
   porque o rótulo é mais curto que o valor mono. */
.cards-2col-tab td:first-child, .cards-2col-tab th:first-child { width: 46%; }

/* 07/08 — FINANCIAMENTO × FUNDO LADO A LADO (DESIGN-4/PDF, parte A3,
   12/09/2026). `#cards-fin` e `#tab-fundo` continuam com a anatomia exata de
   antes (D-225) — `check_cartao_cet.mjs` lê `#cards-fin` por regex —, e o que
   muda é só a ROUPA em volta: o card único com cabeçalho, pílula de
   modalidade, total em destaque e as caixas de alerta. Os pequenos cartões de
   `.cards-2col` ficam em UMA coluna dentro do card estreito (a 2ª coluna só
   cabe com a soma dos dois cards passando de ~1500px de largura de tela). */
.res-fin-grade {
  display: grid; grid-template-columns: 1fr; gap: 16px; margin: 26px 0 10px;
}
@media (min-width: 1100px) { .res-fin-grade { grid-template-columns: 1fr 1fr; } }
.res-fin-card {
  background: var(--superficie); border: 1px solid var(--linha);
  border-radius: var(--raio); box-shadow: var(--sombra); overflow: hidden;
}
.res-fin-card .res-sec-cab.res-sec-cab--fin { margin: 0; border-radius: var(--r-2) var(--r-2) 0 0; }
.res-fin-corpo { padding: 16px 20px 18px; }
.res-fin-card .cards.cards-2col { grid-template-columns: 1fr; }
@media (min-width: 1500px) { .res-fin-card .cards.cards-2col { grid-template-columns: 1fr 1fr; } }
.modalidade-pilula {
  display: inline-flex; padding: 5px 12px; border-radius: 999px;
  background: var(--superficie-2); border: 1px solid var(--linha);
  font: 600 11px var(--display); color: var(--tinta-2); margin-bottom: 14px;
}
.capital-total {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  border-bottom: 1px solid var(--linha); padding-bottom: 14px; margin-bottom: 14px;
}
.capital-total:empty { display: none; }
.capital-total .rot { font-size: 11px; color: var(--tinta-2); }
.capital-total .val { font: 500 24px var(--mono); letter-spacing: -0.02em; white-space: nowrap; }
.res-fin-corpo .alertas:empty { margin: 0; }
.res-fin-corpo .alertas { margin-top: 14px; margin-bottom: 0; }

/* HABITE-1, PARTE B, itens (4) e (5) — a tabela da Composição do preço em
   caixa (`#tab-comp-caixa`). NENHUM NÓ NUMÉRICO QUEBRA LINHA: `table-layout`
   é `auto` (herdado de `.tabela-resultado`), e sem `nowrap` a coluna PODE
   preferir quebrar um valor grande em vez de crescer, quando o container
   aperta — a régua declarada pelo dono ("R$ 11.911.817" sem quebrar). Medido
   com os números REAIS do Doma América 2 (o maior R$ da tabela ali é
   R$ 10.345.632) em 1720 e 1366: já não quebrava nem antes desta regra —
   ela é a trava CONTRA a próxima vez que um valor maior aparecer, não o
   conserto de uma quebra observada nesta massa de dados. Com `nowrap`,
   `table-layout: auto` faz o que já fazia por natureza: a coluna cresce até
   caber o MAIOR valor real (não há necessidade de largura fixa por coluna
   — fixá-la seria uma segunda fonte de verdade sobre um número que muda a
   cada estudo). A coluna Categoria fica de fora de propósito: nome de
   categoria é PROSA e pode — deve — quebrar antes de empurrar a tabela
   inteira. */
#tab-comp-caixa td:not(:first-child) { white-space: nowrap; }
/* "NÃO SE APLICA" É ESTADO, NÃO PROSA (item 5, D-233).
   ⚠ DOBRA DA BLOQUEANTE (D-263): o traço morava em `content` de `::after`
   — CSS puro, fora do que `check_ancora.mjs` lê (`app.js`/`index.html`
   apenas). "Declarado" media ZERO enquanto o glifo vivia aqui: o
   guardião não tinha como ver. O traço agora é TEXTO DE VERDADE, escrito
   por `app.js` dentro do `<td>`, e DECLARADO em `tracos_declarados.mjs`
   — a régua volta a valer porque o guardião volta a enxergar. Esta
   classe fica só para a cor (a régua visual, não o glifo). */
.cel-traco { color: var(--tinta-3); }

/* 05. COMPOSIÇÃO DO PREÇO (DESIGN-4/PDF, parte A3, 12/09/2026) — os DOIS
   cards de topo ganham a borda esquerda verde do PDF (mesma anatomia
   `.card > .rotulo/.valor/.abaixo`, D-225 — nenhuma classe nova neles). As
   linhas por categoria (`#cc-linhas-categoria`) são a MESMA leitura das duas
   séries que já enchiam `#medidor-comp-caixa`/`#cc-barras`
   (`valoresContratual`/`valoresHabite`, decimal 0–1) — nenhum número novo,
   só outro desenho: duas trilhas por linha, na MESMA escala 0–100% sem
   normalização (a régua do PDF), a clara para o contratual e a verde para o
   recebido até o habite-se. */
#cards-comp-caixa .card { border-left: 3px solid var(--color-primary); }

/* CAT-1 (NOTURNO-7 item 5, prancha G do Codex, 13/09/2026) — a legenda
   diegética (duas chaves com amostra) substitui a explicação por cor que
   morava só na nota de texto abaixo; a grade ganha uma 4ª coluna fixa para
   "Categoria" e o cabeçalho passa a repetir as MESMAS colunas da linha
   (`240px minmax(90px,1fr) 120px 130px`, 216/86/105/118 no iPad ≤1100), para
   as barras e os dois percentuais alinharem verticalmente. Contratual ganha
   hachura + contorno; os dois tons são tokens já existentes no :root
   (`--verde-claro`/`--comp-baloes`) — nenhum hex novo. Recebido continua
   verde cheio. Nenhum número novo: as mesmas duas séries
   (`valoresContratual`/`valoresHabite`), a mesma escala 0–100% sem
   normalização. */
.cc-legenda {
  display: flex; flex-wrap: wrap; gap: 24px; margin: 14px 0 4px;
  font: 400 12px var(--display); color: var(--tinta-2);
}
.cc-legenda span { display: inline-flex; align-items: center; gap: 8px; }
.cc-amostra {
  width: 32px; height: 9px; display: inline-block; flex-shrink: 0;
  border: 1px solid var(--comp-baloes);
  background: repeating-linear-gradient(135deg, var(--verde-claro) 0 3px, var(--comp-baloes) 3px 4px);
}
.cc-amostra-habite { border-color: var(--color-primary); background: var(--color-primary); }
.cc-cab-linhas {
  display: grid; grid-template-columns: 240px minmax(90px, 1fr) 120px 130px; gap: 12px;
  font: 600 12px var(--display); letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--tinta-2); border-bottom: 1px solid var(--linha);
  padding: 18px 0 9px;
}
.cc-cab-linhas span:nth-child(n+3) { text-align: right; }
.cc-cab-mono { font: 500 12px var(--mono); letter-spacing: normal; text-transform: none; }
.cc-linha-preco {
  /* SELO-1 item (c), 14/09/2026: de 54 para 36px -- nenhum conteúdo muda,
     só a altura da linha; `align-items: center` já cuida do alinhamento de
     barras e rótulos ao centro. */
  display: grid; grid-template-columns: 240px minmax(90px, 1fr) 120px 130px; gap: 12px;
  align-items: center; padding: 4px 0; min-height: 36px;
}
.cc-linha-preco .cc-cat { font-size: 13px; color: var(--tinta-2); white-space: nowrap; }
.cc-linha-preco .cc-pct { font: 400 12px var(--mono); text-align: right; color: var(--tinta); white-space: nowrap; }
.cc-trilhos { display: grid; gap: 5px; }
.cc-trilho { height: 8px; background: var(--superficie-2); border-radius: 2px; overflow: hidden; }
.cc-trilho i {
  display: block; height: 100%; box-sizing: border-box; border-radius: 2px;
  border: 1px solid var(--comp-baloes);
  background: repeating-linear-gradient(135deg, var(--verde-claro) 0 3px, var(--comp-baloes) 3px 4px);
}
.cc-trilho.cc-trilho-habite i { background: var(--color-primary); border-color: var(--color-primary); }
/* CATEGORIA SEM FRAÇÃO NÃO GANHA UM TRAÇO DE BORDA (medido: `border-box` a
   0% de largura ainda desenha ~2px de contorno, porque o cálculo de conteúdo
   negativo é grampeado em zero e a borda declarada some por igual dos dois
   lados — resíduo visível, não decisão). O seletor lê o `style="width:0%"`
   que `linhasComposicaoPreco` já escreve para os dois trilhos (nenhuma
   marcação nova); a categoria zero volta a ser só a trilha vazia. */
.cc-trilho i[style="width:0%"] { border-width: 0; background: none; }
@media (max-width: 1100px) {
  .cc-cab-linhas, .cc-linha-preco {
    grid-template-columns: 216px minmax(86px, 1fr) 105px 118px; gap: 10px;
  }
}

/* 06. MIX DE TIPOLOGIAS SEM VENDA POR LINHA — o card de ausência do PDF:
   ícone em círculo + a frase do motor (`#tip-vazio`), a MESMA gramática dos
   três portadores independentes do `.alerta-item` (espessura, forma, rótulo
   — nunca só cor, D-233). Texto continua sendo o `textContent` que
   `renderTipologias` já escrevia — só a moldura muda. */
.aviso-secao--mix {
  display: flex; align-items: flex-start; gap: 16px;
  background: var(--superficie); border: 1px solid var(--linha);
  border-radius: var(--raio); box-shadow: var(--sombra);
  padding: 18px 20px; max-width: 720px;
}
.aviso-secao--mix::before {
  content: ""; flex-shrink: 0; width: 40px; height: 40px; border-radius: var(--r-2);
  background: var(--color-primary-soft);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%230E6B49' stroke-width='1.3'%3E%3Cpath d='M4 21V9l8-6 8 6v12M9 21v-7h6v7M8 9h1m6 0h1'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: center; background-size: 20px 20px;
}

.grafico {
  background: var(--superficie); border: 1px solid var(--linha);
  border-radius: var(--raio); padding: 16px; box-shadow: var(--sombra);
}
.grafico svg { display: block; width: 100%; height: auto; }

/* 03. EXPOSIÇÃO DE CAIXA (DESIGN-4/PDF, parte A2) — legenda, par de botões de
   período, chips dos marcos e detalhe mensal ao redor do `.grafico` de
   sempre. Fonte 12px (D-401): era 11. */
.grafico-controles {
  display: flex; justify-content: space-between; align-items: center;
  gap: 14px; margin-bottom: 10px; flex-wrap: wrap;
}
.grafico-scroll { overflow-x: auto; max-width: 100%; }
.legenda { display: flex; gap: 16px; align-items: center; flex-wrap: wrap; font-size: 12px; color: var(--tinta-2); }
.legenda span { display: inline-flex; align-items: center; gap: 6px; }
.legenda .traco { width: 24px; border-top: 2.4px solid var(--verde); display: inline-block; }
.legenda .traco.inc { border-top: 1.8px dashed var(--tinta-2); }
.legenda .traco.alav { border-top: 1.8px solid var(--comp-baloes); }
.legenda .traco.div { border-top: 1.8px dashed var(--ambar); }
/* `.eixo-y`/`.linha-zero`/`.barra-*` continuam servindo a CASCATA
   (`#comp-cascata`, `#cc-barras`), que também usa `.grafico svg` -- por isso
   NÃO saem daqui mesmo com os dois gráficos mensais tendo migrado o eixo Y
   para HTML (GRAF-1, ver `.gm-escala` abaixo). `.eixo-x`, `.pico-*` e
   `.marco-ponto`/`.marco-rotulo` morreram: eram só dos dois gráficos
   mensais, e o texto deles virou HTML (`.gm-mes`, `.gm-marco`) -- checado por
   grep que a cascata não os usa (D-076: uma casa por regra). */
.grafico svg .eixo-y { font-family: var(--mono); fill: var(--tinta-2); font-size: 12px; }
.grafico svg .linha-grade { stroke: var(--linha); stroke-width: 1; }
.grafico svg .linha-zero { stroke: var(--tinta-3); stroke-width: 1.3; }
.grafico svg .marco-linha { stroke: var(--linha); stroke-dasharray: 3 4; }
/* Hit-area larga e invisível (mesmo padrão do `.fx-hit`) -- só existe para
   o hover realçar o(s) chip(s) do mês (SELO-1 item (a)). */
.grafico svg .marco-linha-hit { stroke: transparent; stroke-width: 10; pointer-events: stroke; cursor: default; }
/* ITEM 3 (NOTURNO-6 item 8, DESIGN-8) -- seleção sincronizada do mês no
   gráfico do Fluxo: a área de toque é invisível e só existe para o
   ponteiro/dedo (`cursor: pointer`); a barra do mês selecionado ganha um
   contorno; a linha vertical é o mesmo realce que a tabela usa via tinta. */
.grafico svg .fx-hit { cursor: pointer; }
.grafico svg .barra-ganho.fx-mes-ativo, .grafico svg .barra-perda.fx-mes-ativo {
  stroke: var(--tinta); stroke-width: 1.5;
}
.grafico svg .fx-sel-linha { stroke: var(--tinta); stroke-width: 1.5; stroke-dasharray: 4 3; }
.linha-do-tempo { display: flex; gap: 6px; flex-wrap: wrap; margin-top: 14px; }
/* EXPORT-3 item 6 (20/09/2026): o SVG que `POST /api/linha-do-tempo` devolve
   carrega `class="linha-do-tempo"` no próprio elemento raiz (o mesmo nome do
   documento, D-076: casa única) -- que É o nome já usado acima pelos chips
   de `#fx-chips`/`#exp-chips`. O seletor com o tipo do elemento
   (`svg.linha-do-tempo`) tem especificidade maior que a classe sozinha e
   vence sem tocar o componente de chips: o svg some a `display:flex` herdado
   e volta ao mesmo padrão de `.grafico svg` (bloco, largura cheia, altura
   pelo `viewBox`). */
svg.linha-do-tempo { display: block; width: 100%; height: auto; margin-top: 0; }
/* DOBRA (D-263, bloqueante do revisor qualidade-b, D-401): `width:100%` sobre
   um viewBox de 1000 com `font-size 13` só bate o piso de 12px quando o
   RENDERIZADO chega a 924px (13/1000 × 924 = 12,0) -- e a coluna do cartão
   mede 860px em 1720/1366 e menos ainda em 834. `min-width` trava o SVG no
   piso (1000 × 12/13 = 923,08, arredondado para 924) e `overflow-x` no
   container abre rolagem própria em vez de encolher a fonte -- o MESMO
   padrão de `.tabela-sticky` para a tabela do mix, um andar acima. */
#ldt-svg { margin-top: 10px; overflow-x: auto; }
svg.linha-do-tempo { min-width: 924px; }
#ldt-svg .linha-do-tempo-vazia { margin: 0; }
.evento-chip {
  background: var(--superficie-2); border: 1px solid var(--linha); border-radius: var(--r-1);
  padding: 6px 10px; font-size: 12px; min-height: 40px; display: inline-flex; align-items: center;
  gap: 6px; color: var(--tinta);
}
.evento-chip b { font-family: var(--mono); font-weight: 600; color: var(--color-primary); }
.evento-chip.ativo { border-color: var(--color-primary); background: var(--verde-claro); }
/* Realce de HOVER (SELO-1 item (a)) -- distinto do `.ativo` (seleção): o
   contorno interno não muda o fundo, então os dois estados nunca se
   confundem quando coincidem no mesmo chip. */
.evento-chip.gm-chip-realce { box-shadow: inset 0 0 0 2px var(--color-primary); }
.detalhe-mes {
  display: flex; flex-wrap: wrap; gap: 14px; align-items: center; background: var(--superficie-2);
  border-radius: var(--r-2); padding: 12px 14px; margin-top: 14px;
}
.detalhe-mes label { font-size: 12px; display: flex; align-items: center; gap: 9px; color: var(--tinta-2); }
/* A GRADE DE VALORES (Receitas/Despesas/Saldo/Acumulado…), reaproveitada por
   `htmlDetalheMes` nas DUAS telas (item 6, GRAF-1). DIURNA-8 item 1e
   (16/09/2026): passa a ser GRADE DE DUAS LINHAS com o tratamento dos KPIs
   do veredicto -- rótulo pequeno em versalete como `.kpi-titulo-txt`, valor
   mono como `.kpi-valor`. 3 colunas: a linha 1 (Receitas · Despesas · Saldo
   do mês) enche as três; a linha 2 (Acumulado do projeto [· Acumulado do
   incorporador]) nasce do PRÓPRIO auto-flow do grid, sem índice mágico.
   NENHUM TEXTO AQUI FICA ABAIXO DE 12px (D-401) -- inclusive o rótulo: o
   `check_grafico` mede TODO descendente de `.mes-valores`, não só o número
   (a irmã da regra "número é mono, rótulo pode ser menor" não vale NESTE
   componente específico, por causa do harness que já existe). */
.mes-valores { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px 12px; }
.mes-valores > span {
  display: flex; flex-direction: column; gap: 4px; min-width: 0;
  font: 600 12px var(--display); letter-spacing: 0.04em; text-transform: uppercase; color: var(--tinta-2);
}
/* O EYEBROW dos eventos do mês (Fecho comercial, Exposição máxima) --
   `.eyebrow` da casa, span cheio entre as duas linhas, ausente sem evento
   (D-233). `border-right`/`padding-right` da base saem porque aqui ele não
   está ao lado de um `h2`: sem eles ele vira uma etiqueta de largura
   própria, não uma régua cortando a linha inteira. Piso de 12px explícito
   -- a base de `.eyebrow` é 10px, abaixo do que este componente aceita. */
.mes-valores > .eyebrow { grid-column: 1 / -1; width: fit-content; border-right: 0; padding-right: 0; font-size: 12px; }
.mes-valores strong {
  font: 600 14px var(--mono); letter-spacing: normal; text-transform: none;
  color: var(--tinta); text-align: right; white-space: nowrap; overflow-wrap: anywhere;
}
.mes-valores strong.neg { color: var(--vermelho); }
@media (max-width: 1100px) {
  .mes-valores { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* =========================================================================
   GM: O RENDERIZADOR ÚNICO DOS DOIS GRÁFICOS MENSAIS (GRAF-1, NOTURNO-7
   item 1, 13/09/2026). O eixo Y (`.gm-escala`) e o eixo X (`.gm-meses`)
   viraram HTML por cima de um SVG "mudo" (só linha/forma) -- texto de SVG
   não tem piso de fonte confiável entre navegadores, e a régua de 12px
   (D-401) precisa de um elemento que o CSS controle direto. Uma casa por
   regra (D-076): estas classes servem `#grafico` (Resultado) E `#fx-grafico`
   (Fluxo), nunca duplicadas por tela. */
/* SELO-1 item (a), 14/09/2026: os trilhos de nomes empilhados saíram --
   cada mês com marco vira uma linha tracejada fina (`.marco-linha`, no SVG)
   com UMA etiqueta pequena no topo (só "M40" ou "M40 · N marcos"). Os
   NOMES vivem nos chips abaixo do gráfico e na prévia do mês. Altura FIXA
   (nunca cresce a cada redesenho -- era a causa do loop do ResizeObserver). */
.gm-marcos-topo { position: relative; margin-left: 60px; margin-right: 14px; }
.gm-marco-topo {
  position: absolute; top: 0; transform: translateX(-50%); font: 12px var(--mono);
  color: var(--tinta-2); white-space: nowrap; cursor: default;
}
.gm-marco-topo b { font-weight: 600; color: var(--color-primary); }
.gm-marco-topo--inicio { transform: none; }
.gm-marco-topo--fim { transform: translateX(-100%); }
.gm-plano { position: relative; margin-left: 60px; margin-right: 14px; }
.gm-escala { position: absolute; left: -60px; top: 0; width: 52px; height: 100%; }
.gm-escala span {
  position: absolute; right: 8px; transform: translateY(-50%);
  font: 12px var(--mono); color: var(--tinta-2); text-align: right; white-space: nowrap;
}
/* DOBRA 2 (B2, bloqueante do revisor): `.grafico svg { height: auto }`
   (3753) tem especificidade (0,1,1) -- classe + elemento -- e vence
   `.gm-svg { height: 286px }` (0,1,0) INDEPENDENTE da ordem no arquivo: o
   svg media 291px em 1122, 208 em 834, 141 em 600, e o `ALTURA_SVG=286` do
   JS descrevia um mecanismo que a cascata anulava. `.grafico svg.gm-svg`
   soma duas classes + um elemento (0,2,1), que bate a regra genérica. */
.grafico svg.gm-svg { display: block; width: 100%; height: 286px; }
.gm-svg .gm-previa-linha { stroke: var(--tinta-2); stroke-width: 1; stroke-dasharray: 2 2; }
.gm-svg .gm-pico { fill: var(--vermelho); stroke: var(--superficie); stroke-width: 2.5; }
/* O PONTO DO MÊS SELECIONADO na linha do acumulado (item 1c, DIURNA-8) --
   mesma peça nos dois modos ("area" e "barras"), irmã do `.gm-pico` (a
   exposição máxima) e do `.fx-sel-linha` (a linha vertical tracejada). Cor
   neutra (`--tinta`) para não competir com o vermelho da exposição nem com
   o verde/vermelho das barras de saldo. */
.gm-svg .gm-sel-ponto { fill: var(--tinta); stroke: var(--superficie); stroke-width: 2.5; }
.gm-meses { position: relative; height: 20px; margin-left: 60px; margin-right: 14px; margin-top: 6px; }
.gm-mes {
  position: absolute; top: 0; font: 12px var(--mono); color: var(--tinta-2);
  white-space: nowrap; transform: translateX(-50%);
}
.gm-mes:first-child { transform: none; }
.gm-mes:last-child { transform: translateX(-100%); }
/* A PRÉVIA (tooltip do hover) -- nunca presa na borda: o JS já escolhe o
   lado (`left` calculado); aqui só o resto do figurino. `pointer-events:none`
   porque ela nunca é o alvo do ponteiro -- é o próprio `.gm-svg` embaixo. */
.gm-previa {
  position: absolute; top: 8px; width: 272px; padding: 12px 14px; background: var(--superficie);
  border: 1px solid var(--linha); border-radius: var(--r-2); box-shadow: var(--sombra);
  pointer-events: none; z-index: 3; font-size: 12px;
}
.gm-previa[hidden] { display: none; }
/* DOBRA GRAF-1 (13/09/2026, bloqueante do revisor): a prévia é ESTREITA
   (272px) e não comporta a grade de 4 colunas do detalhe fixo -- os quatro
   R$ se atropelavam e "Saldo do mês" quebrava por cima do número. Aqui é
   LISTA VERTICAL, um par rótulo/valor por linha. */
.gm-previa-cab {
  font-size: 12px; color: var(--tinta-2); padding-bottom: 8px; margin-bottom: 8px;
  border-bottom: 1px solid var(--linha);
}
.gm-previa-par {
  display: flex; justify-content: space-between; align-items: baseline; gap: 12px;
  font-size: 12px; color: var(--tinta-2); padding: 2px 0;
}
.gm-previa-par strong { font: 600 13px var(--mono); color: var(--tinta); white-space: nowrap; }
.gm-previa-par strong.neg { color: var(--vermelho); }
.gm-detalhe-cab { font-size: 12px; color: var(--tinta-2); margin-bottom: 8px; }
.gm-eventos { font-size: 12px; color: var(--tinta-2); margin: 8px 0 0; }
.gm-vazio { font-size: 13px; color: var(--tinta-2); padding: 40px 16px; text-align: center; }
/* O CONTAINER DO DETALHE PERSISTENTE -- mesma classe nos DOIS alvos
   (`#exp-mes-valores`, `#fx-detalhe-mes`), item 6 da régua. */
.gm-detalhe { flex: 1; display: flex; flex-direction: column; gap: 6px; min-width: 0; }

/* 04. COMPOSIÇÃO DO RESULTADO — três totais + a cascata (waterfall).
   ADENDO 3b (acabamento do selo de 12/09, martelo do dono): as TRÊS
   células nascem da MESMA largura (`repeat(3, 1fr)`, nunca `flex-basis`
   de conteúdo — nenhuma célula pode ficar mais larga por ter número
   maior), com divisória vertical de 1px entre elas e o número mono
   ALINHADO À ESQUERDA sob o rótulo (antes: rótulo à esquerda, número à
   direita, soltos na largura). D-076: nenhum cálculo novo, só a roupa —
   `renderComposicao` continua escrevendo os mesmos três `<div><span>
   <strong>`, e é por isso que `left(strong) === left(rótulo)` sai de
   graça: os dois são filhos do mesmo `<div>`, sem padding próprio, então
   alinhar `strong` à esquerda os põe na mesma borda. */
.waterfall-total { display: grid; grid-template-columns: repeat(3, 1fr); gap: 24px; margin-bottom: 14px; }
.waterfall-total > div + div { border-left: 1px solid var(--color-border); padding-left: 24px; }
.waterfall-total span { font-size: 11px; color: var(--tinta-2); }
.waterfall-total strong { display: block; font: 600 21px var(--mono); text-align: left; margin-top: 6px; letter-spacing: -0.02em; }
.grafico svg .barra-ganho { fill: var(--verde); }
.grafico svg .barra-custo { fill: var(--vermelho); opacity: 0.82; }
.grafico svg .barra-perda { fill: var(--vermelho); }
.grafico svg .ligacao-cascata { stroke: var(--tinta-3); stroke-dasharray: 3 3; }
.grafico svg .rubrica-cascata { font: 12px var(--display); fill: var(--tinta-2); }
.grafico svg .valor-cascata { font: 600 12px var(--mono); fill: var(--tinta); }
.grafico svg .valor-cascata.neg { fill: var(--vermelho); }

/* GRÁFICOS DA RENDA POR UNIDADE (EXPORT-3 item 6, 20/09/2026) -- um cartão
   por unidade, com as duas leituras (barras + linha) lado a lado; envolve
   em rolagem horizontal como `.tabela-sticky` faz com a tabela vizinha,
   porque os SVGs são de TAMANHO FIXO (nunca encolhem, D-401: só assim o
   piso de 12px se garante sem calibrar a largura ao container). */
#renda-graficos { display: flex; flex-direction: column; gap: 18px; margin-top: 14px; }
#renda-graficos:empty { display: none; }
.gr-renda-cartao {
  border: 1px solid var(--linha); border-radius: var(--r-1); padding: 12px 14px;
  background: var(--superficie-2); overflow-x: auto;
}
.gr-renda-titulo { margin: 0 0 8px; font: 700 13px var(--display); color: var(--tinta); }
.gr-renda-par { display: flex; gap: 22px; flex-wrap: nowrap; }
.gr-renda-item { min-width: 0; }
.gr-renda-legenda { display: block; font-size: 12px; color: var(--tinta-2); margin-bottom: 4px; }
.gr-renda-svg { display: block; }
.gr-renda-eixo { stroke: var(--linha); stroke-width: 1; }
.gr-renda-linha { stroke-linejoin: round; }
.gr-renda-rot { font: 12px var(--display); fill: var(--tinta-2); text-anchor: middle; }
.gr-renda-valor { font: 600 12px var(--mono); fill: var(--tinta); text-anchor: middle; }
.gr-renda-semdado { font: 12px var(--display); fill: var(--tinta-3); font-style: italic; text-anchor: middle; }
.gr-renda-sem { margin: 0; font-size: 12px; }

/* CASCATA HORIZONTAL EM IPAD (DESIGN-6) -- mesmo container `#comp-cascata`,
   mesmos dados de `desenharCascata`: uma rubrica por linha, rotulo a
   esquerda, barra a partir do zero, valor mono a direita. Troca o SVG da
   cascata vertical quando a largura nao alcanca 1100px (o breakpoint da
   casa) -- nao e uma segunda visualizacao, e a MESMA lida na orientacao que
   cabe sem rolagem horizontal. */
.cascata-horiz { display: flex; flex-direction: column; gap: 8px; }
.cascata-h-linha { display: grid; grid-template-columns: 96px 1fr auto; align-items: center; gap: 10px; }
/* VAR-1 item d (14/09/2026): "Permuta financeira" cortava com ellipsis nos
   96px da primeira coluna do grid, em 834px (iPad) -- passa a quebrar em
   duas linhas (o `grid-template-columns` já centraliza via `align-items:
   center`, então a linha inteira cresce junto, sem sobrepor a barra). */
.cascata-h-rot {
  font: 500 11.5px var(--display); color: var(--tinta-2);
  overflow: visible; white-space: normal; line-height: 1.25;
}
.cascata-h-trilho {
  background: var(--superficie-2); border-radius: 4px; height: 14px; overflow: hidden;
}
.cascata-h-barra { display: block; height: 100%; border-radius: 4px; opacity: 0.82; }
.cascata-h-barra.h-pos { background: var(--verde-escuro); }
.cascata-h-barra.h-neg { background: var(--vermelho); }
.cascata-h-valor {
  font: 600 12px var(--mono); font-variant-numeric: tabular-nums; text-align: right;
  min-width: 96px;
}
.cascata-h-valor.pos { color: var(--verde-escuro); }
.cascata-h-valor.neg { color: var(--vermelho); }
.details-nativo { border-top: 1px solid var(--linha); margin-top: 16px; }
.details-nativo > summary {
  font-size: 12px; font-weight: 600; color: var(--color-primary); padding: 14px 0;
  min-height: 44px; display: flex; align-items: center; list-style: none;
}
.details-nativo > summary::-webkit-details-marker { display: none; }
.details-nativo > summary::before { content: "▸"; margin-right: 7px; font-size: 10px; }
.details-nativo[open] > summary::before { content: "▾"; }
.details-nativo[open] > summary { margin-bottom: 4px; }

.tabela-resultado {
  width: 100%; border-collapse: collapse; background: var(--superficie);
  border: 1px solid var(--linha); border-radius: var(--raio); overflow: hidden;
  box-shadow: var(--sombra);
}
.tabela-resultado th, .tabela-resultado td { padding: 10px 14px; text-align: right; font-size: 13px; }
.tabela-resultado th {
  font: 600 10.5px var(--display); text-transform: uppercase; letter-spacing: 0.08em;
  color: var(--tinta-2); border-bottom: 1px solid var(--linha); background: var(--superficie-2);
}
.tabela-resultado td:first-child, .tabela-resultado th:first-child { text-align: left; }
.tabela-resultado td { font-family: var(--mono); border-bottom: 1px solid #eef2f0; }
.tabela-resultado tr:last-child td { border-bottom: 0; }
.tabela-resultado .total td { font-weight: 600; background: var(--verde-claro); }
/* DOBRA (D-263, B1): mesma trava do #tab-comp-caixa (4091) -- "R$" e o
   número não quebram em duas linhas. Em 834 a tabela pode ficar mais larga
   que o cartão; ela rola dentro do PRÓPRIO wrap, nunca a página. */
#tab-tipologias td:not(:first-child), #tab-tipologias th:not(:first-child) { white-space: nowrap; }
.tab-tipologias-scroll { overflow-x: auto; max-width: 100%; }
/* MOLDURA DE ROLAGEM DO CARTÃO DO INVESTIDOR (achado do revisor, R5
   DIURNA-8): a mesma trava do `#tab-comp-caixa`/`#tab-tipologias` acima —
   `overflow-x: auto` no PRÓPRIO wrap (`.invu-val-scroll`, criado em
   `addInvestidorUnidades` no app.js), nunca na página. `white-space: nowrap`
   nas células numéricas preserva o conteúdo — "R$"/o número não quebram em
   duas linhas.
   SÓ ISSO NÃO BASTAVA (medido): `.invu-val-wrap` é filho DIRETO da mesma
   `<td colspan="9">` que `.pers-recebimento` — o TRUQUE `width: 1px;
   min-width: 100%` já documentado duas telas acima (`.pers-recebimento`)
   existe exatamente para esta doença, e `.invu-val-wrap` não o tinha: sob
   `table-layout: auto` o conteúdo do wrap (a tabela de 12 colunas, 1108px)
   contava para a largura PREFERIDA da célula/tabela, e a página inteira
   inflava para caber (`documentElement.scrollWidth` de 3108px em 1720) —
   o `overflow-x: auto` do filho nunca entrava em ação porque o PAI nunca
   ficava mais estreito que o conteúdo. Mesmo mecanismo, mesmo remédio. */
.invu-val-wrap { width: 1px; min-width: 100%; }
.invu-val-scroll { overflow-x: auto; max-width: 100%; }
.invu-val-tabela td:not(:first-child), .invu-val-tabela th:not(:first-child) { white-space: nowrap; }
.mini-barra { height: 6px; border-radius: 3px; display: inline-block; vertical-align: middle; }

/* ---- POR LINHA DO MIX (pacote TIPOLOGIA) ----
   A CHAVE DE COR LIGA A TABELA AO DESENHO. São as duas superfícies do mesmo
   fato: a tabela diz em que mês cada linha zera, o desenho diz o caminho até
   lá. Sem a chave o leitor teria de contar a ordem das curvas para saber qual
   é qual, e a ordem é justamente o que ele não vê. */
.tip-chave { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.tip-chave + .tip-chave { margin-left: 14px; }
.tip-chave i { width: 12px; height: 3px; border-radius: 2px; flex: none; }
/* O gráfico do estoque é mais baixo que o da exposição de caixa: ali a leitura
   é o SINAL do saldo, e aqui é uma descida até zero, que não precisa da mesma
   altura para se ler. */
.grafico-estoque { margin-top: 10px; padding: 12px 16px; }
.nota-retido { margin-top: 12px; }
.neg { color: var(--vermelho); }
.pos { color: var(--verde-escuro); }

/* Selo da apuração fiscal: o período em que o LIMITE DE COMPENSAÇÃO de prejuízo
   mordeu. O percentual impresso é DERIVADO do próprio par (compensado ÷ lucro do
   período) — não há aqui, nem no app.js, cópia da fração que o motor usa. */
.travou {
  display: inline-block; margin-left: 7px; padding: 1px 6px; border-radius: 999px;
  font: 700 9px var(--display); letter-spacing: 0.1em; text-transform: uppercase;
  background: var(--ambar-claro); color: var(--ambar); border: 1px solid var(--ambar-borda);
}

/* ================= SENSIBILIDADE ================= */
/* SENS-2 — os quatro cards de alavanca, empilhados. O toggle decide quem
   entra no tornado (a mesma função de antes, na `.grade-sens` que eles
   substituem); o corpo é preenchido depois que a resposta chega — em
   branco, ele é SILÊNCIO ("ainda não rodou"), nunca travessão. */
.sens-alavancas { display: flex; flex-direction: column; gap: 2px; margin: 4px 0 18px; }
.sens-card {
  display: flex; gap: 14px; align-items: flex-start;
  padding: 14px 4px; border-bottom: 1px solid var(--linha);
}
.sens-card:last-child { border-bottom: 0; }
.sens-card-toggle { flex: none; padding-top: 2px; }
.sens-card-corpo { flex: 1 1 auto; min-width: 0; }
.sens-card-nome { display: block; font: 700 14px var(--display); }
.sens-card-traducao { margin-top: 3px; font-size: 12.5px; color: var(--tinta-2); }
/* DESIGN-3 — os dois extremos testados, em pílulas ↓ (menor variação) e
   ↑ (maior variação). Vazio até a primeira rodada: silêncio, não travessão. */
.sens-card-extremos { display: flex; gap: 6px; margin-top: 6px; flex-wrap: wrap; }
.sens-pilula {
  display: inline-flex; align-items: center; padding: 3px 9px; border-radius: 999px;
  font: 600 12px var(--mono); font-variant-numeric: tabular-nums;
}
/* DOBRA (bloqueante do revisor, D-263): a cor segue a DIREÇÃO DO LUCRO
   (ganho/perda contra a base), nunca o sinal da variação — mesma regra da
   barra do card (D-223). As setas ↓/↑ (no texto) continuam dizendo qual é o
   menor e qual é o maior deslocamento testado. */
.sens-pilula-ganho { background: var(--color-primary-soft); color: var(--color-primary); }
.sens-pilula-perda { background: var(--color-danger-soft); color: var(--color-danger); }
/* A "faixa vermelha" só existe quando há valor de verdade — a ausência
   classificada (`.sem-valor`, definida mais abaixo) continua com o texto
   apagado de sempre, sem banda nenhuma. */
.sens-card-aguenta { margin-top: 8px; font: 700 13px var(--display); border-radius: var(--r-1); }
/* DOBRA (bloqueante do revisor, D-263): `:not(:empty)` — sem isso a faixa
   vermelha pintava VAZIA antes de o break-even chegar (o card nasce em
   branco, silêncio, D-186). */
.sens-card-aguenta:not(.sem-valor):not(:empty) {
  background: var(--color-danger-soft); color: var(--color-danger); padding: 6px 8px;
}
/* A FAIXA E O "?" (DESIGN-8, item 3a) — a explicação longa do motor
   (`be.explicacao`) não cabe mais como parágrafo dentro do card; ela vira
   `title` nativo deste botão, e a faixa ao lado fica na frase curta, em UMA
   linha. O botão só aparece quando há explicação de verdade (ausência
   classificada) — no card com valor ele fica oculto. */
.sens-card-aguenta-linha { display: flex; align-items: center; gap: 6px; }
.sens-card-aguenta-linha .sens-card-aguenta { flex: 1 1 auto; min-width: 0; }
.sens-card-ajuda {
  flex: none; width: 18px; height: 18px; border-radius: 50%;
  border: 1px solid var(--linha); background: var(--superficie-2); color: var(--tinta-2);
  /* 1d CONSERTO (DIURNA-8, causa MEDIDA no `check_ajuda` cena 2b): era
     `cursor: help` -- este botão sempre teve `data-ajuda` e o clique já
     chegava ao handler, só que `itemDeAjuda("")` devolvia `null` e o clique
     era ENGOLIDO. O conserto é no handler; aqui só o cursor deixa de mentir. */
  font: 700 10.5px var(--display); cursor: pointer; padding: 0;
  display: grid; place-items: center; margin-top: 8px;
}
.sens-card-ajuda:hover { border-color: var(--color-primary); color: var(--color-primary); }
/* VAR-1 item b: a legenda carrega o valor da TMA ("VPL zera na TMA X%") --
   número, piso de 12px (D-401). */
.sens-card-legenda { margin-top: 4px; font-size: 12px; color: var(--tinta-3); font-style: italic; }
.sens-card-ausente .sens-card-nome { color: var(--tinta-2); }

/* DESIGN-3, item 01 — quatro colunas fixas a partir de 1100px. Abaixo disso
   o card volta à lista empilhada de sempre (fallback existente, intocado). */
@media (min-width: 1100px) {
  .sens-alavancas { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
  .sens-card {
    flex-direction: column; align-items: stretch;
    border: 1px solid var(--linha); border-radius: var(--r-2); padding: 14px;
  }
  .sens-card-toggle { padding-top: 0; }
}

/* A caixa de texto crua das variações vive atrás de "editar lista…" (item 2):
   os chips e os presets bastam para o uso comum, e quem precisa de um
   token fora dos presets abre o detalhe.
   DESIGN-7/ADENDO (D3) — o `<details>` agora é um segmento da fileira única
   de `.sens-acoes` (mais abaixo): `margin-top` zerado, senão ele cai 10px
   abaixo dos outros segmentos da MESMA linha. O `summary` continua na linha;
   o campo de texto (`.curva-atalho`) segue abrindo ABAIXO dele quando o
   `<details>` é aberto — isso não muda, é a regra de sempre. */
.sens-editar-lista { margin-top: 0; }
.sens-editar-lista > summary {
  cursor: pointer; font: 600 12.5px var(--display); color: var(--tinta-2);
  list-style: none; display: flex; align-items: center; gap: 4px;
  /* `flex` (bloco) e não `inline-flex`: o inline-level caía dentro de uma
     linha anônima e herdava o "half-leading" do line-height do bloco pai
     (`<details>`), 5px de folga no topo que nenhuma margem explicava --
     medido no DESIGN-7/ADENDO (D3), com `margin-top`/`padding-top` computados
     em 0px e o topo mesmo assim mais baixo que os vizinhos da fileira. */
}
.sens-editar-lista > summary::-webkit-details-marker { display: none; }
.sens-editar-lista > summary::before { content: "▸"; font-size: 10px; }
.sens-editar-lista[open] > summary::before { content: "▾"; }
.sens-editar-lista > .curva-atalho { margin-top: 8px; }

/* A configuração da matriz (item 5) — faixa e passo, sem teto cravado: a
   tela só faz a aritmética de quantos pontos a grade teria; o teto de
   verdade é o 422 do servidor. */
.sens-matriz-cfg { border: 1px solid var(--linha); border-radius: 10px; margin: 16px 0 0; padding: 12px 14px; }
.sens-matriz-cfg > legend { font: 700 11px var(--display); letter-spacing: 0.08em; text-transform: uppercase; color: var(--tinta-2); padding: 0 4px; }
/* DESIGN-8, item 3b — DOIS SELETORES COMPACTOS no canto do título, no lugar
   da fileira de 8 pílulas (DESIGN-3/DESIGN-7). O `<fieldset>` e os `<input
   type=radio>` são os MESMOS de sempre (ids/names/valores intocados —
   `app.js` lê `input[name="sens-faixa"]:checked` direto); só a CAIXA que os
   contém virou um `<details>`, para não obrigar `<select>` no lugar do
   radio. Sem legenda visível (o título "04. MATRIZ..." já diz do que se
   trata; o `<legend>` continua no DOM para leitor de tela). */
.sens-matriz-cfg-compacta { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; border: 0; margin: 0; padding: 0; }
.sens-matriz-cfg-compacta > legend {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0, 0, 0, 0); white-space: nowrap;
}
.sens-matriz-cfg-compacta #sens-matriz-contagem { flex-basis: 100%; margin: 4px 0 0; }
.sens-select { position: relative; }
.sens-select > summary {
  list-style: none; cursor: pointer; user-select: none;
  display: flex; align-items: center; gap: 5px;
  padding: 5px 10px; border: 1px solid var(--linha); border-radius: 999px;
  font: 600 12px var(--display); color: var(--tinta);
  background: var(--superficie);
}
.sens-select > summary::-webkit-details-marker { display: none; }
.sens-select > summary::after { content: "\2228"; color: var(--tinta-3); font-size: 11px; } /* ∨ */
.sens-select[open] > summary::after { content: "\2227"; } /* ∧ */
.sens-select > summary b { font: inherit; color: var(--color-primary); }
.sens-select:hover > summary { border-color: var(--color-primary); }
.sens-select[open] > summary { border-color: var(--color-primary); background: var(--color-primary-soft); }
/* O PAINEL ABRE COMO POPOVER, sobre o resto do cabeçalho — se ele empurrasse
   o layout, o título "04. MATRIZ..." pularia de lugar a cada clique.
   DESIGN-8 item 6a — MEDIDO: `position: absolute` faz o painel escapar do
   colapso nativo do `<details>` fechado (o `::details-content` do Chrome não
   basta para um filho posicionado fora do fluxo) — fechado, sem ninguém
   abrir, ele media `display: block` e um rect real (220×173, bottom > topo
   da faixa viva). O `display: none` explícito por atributo `[open]` é quem
   fecha de verdade; `[open] &` reabre. */
.sens-select-painel {
  position: absolute; z-index: 20; top: calc(100% + 6px); left: 0;
  min-width: 220px; padding: 10px; border: 1px solid var(--linha); border-radius: 10px;
  background: var(--superficie); box-shadow: var(--sombra);
  display: none;
}
.sens-select[open] > .sens-select-painel { display: block; }
.sens-select-painel .opcoes-em-linha { margin-bottom: 0; }
.sens-select-painel .campo { margin-top: 8px; }
.opcoes-em-linha { display: flex; flex-wrap: wrap; gap: 6px; border: 0; margin: 0; padding: 0; }
.opcoes-em-linha .opcao {
  padding: 5px 11px; border: 1px solid var(--linha); border-radius: 999px; margin: 0;
  font-size: 12.5px; align-items: center;
}
.opcoes-em-linha .opcao input { margin: 0; }
.opcoes-em-linha .opcao:has(input:checked) { border-color: var(--verde); background: var(--verde-claro); color: var(--verde-escuro); }
/* SENS-2 passada 2, item A — o preset que estouraria o teto fica DESABILITADO
   e DIZ POR QUÊ (o `title` no input, herdado pelo `label` que o envolve):
   desabilitado mudo é pior que o 422 que ele evita. */
.opcoes-em-linha .opcao:has(input:disabled) { opacity: 0.45; cursor: not-allowed; }
.opcoes-em-linha .opcao:has(input:disabled) input { cursor: not-allowed; }

/* DESIGN-7/ADENDO (D3) — "02. VARIAÇÕES" vira UMA LINHA: a caixa de chips
   (`#sens-ed`, ver regra própria em §"editor de curvas"), o atalho em texto
   atrás de "editar lista…" e o botão de rodar. `align-items: flex-start` (e
   não `center`) é o que garante o MESMO TOPO entre peças de altura bem
   diferente — texto solto do `summary` contra um botão com padding, por
   exemplo — sem depender de as alturas baterem por acaso. */
.sens-acoes { display: flex; align-items: flex-start; gap: 10px; margin-top: 18px; flex-wrap: wrap; }
/* DESIGN-3 — o glifo "▶" é CSS puro (::before), nunca no texto do botão: o
   texto continua "Rodar sensibilidade"/"rodar de novo", que é o que os
   harnesses de tela leem. */
#btn-sens::before { content: "▶"; margin-right: 7px; font-size: 10px; }
/* DESIGN-7/ADENDO (D3) — empurra o botão para o fim da fileira única. Id
   único no documento: não há segundo `#btn-sens` para este seletor colidir.
   `--btn-pad-h` reduzido (era 20px): a fileira é apertada com 6 chips
   padrão, e o botão continua o maior alvo de toque da linha. */
#btn-sens { margin-left: auto; --btn-pad-h: 16px; }
.sens-carimbo { display: flex; gap: 10px; flex-wrap: wrap; }
/* DESIGN-7/ADENDO (D3) — o carimbo sai de `.sens-acoes` e vira o último
   filho do `<header class="cartao-cab">`, à direita do título. Escopo
   ESTREITO (só esta aba): `.cartao-cab` é a moldura genérica de TODOS os
   cartões do sistema, e só o de Sensibilidade ganha um quarto filho.
   `.descricao` já cai em linha própria por `flex-basis: 100%` (regra geral,
   intocada); aqui ela só recebe um `order` maior para continuar DEPOIS do
   carimbo na fileira -- ela nasce como último filho no DOM, mas o cabeçalho
   generico ja a desenha antes do que vier depois dela por causa do
   flex-basis, entao sem o order o carimbo cairia numa TERCEIRA linha, abaixo
   da descricao, em vez de ao lado do titulo. */
#aba-sensibilidade .cartao-cab #sens-carimbo { order: 1; margin-left: auto; }
#aba-sensibilidade .cartao-cab .descricao { order: 2; }
/* CARIMBO DE VALIDADE (DESIGN-0b) -- dois cards possíveis, nunca travessão:
   `.carimbo-ok` sempre que há um estudo rodado; `.carimbo-alerta` só quando
   as premissas mudaram desde então (D-181). Sinal desenhado é U+2212 se
   algum dia houver um -- não há, aqui é só o relógio e o "!". */
.carimbo {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 12px; border-radius: var(--r-2, 8px);
  font: 500 12px var(--display);
}
.carimbo-ok { background: var(--color-primary-soft); color: var(--color-primary); }
.carimbo-alerta { background: var(--color-danger-soft); color: var(--color-danger); }
.carimbo-hora { font: 700 14px var(--mono); }
/* relógio em CSS puro: um aro com dois ponteiros por pseudo-elemento */
.carimbo-relogio {
  width: 15px; height: 15px; border-radius: 50%;
  border: 2px solid var(--color-primary); position: relative; flex-shrink: 0;
}
.carimbo-relogio::before, .carimbo-relogio::after {
  content: ""; position: absolute; left: 50%; top: 50%;
  background: var(--color-primary); transform-origin: 0 0;
}
.carimbo-relogio::before { width: 1px; height: 4.5px; transform: translate(-0.5px, -4.5px) rotate(35deg); }
.carimbo-relogio::after { width: 1px; height: 3.5px; transform: translate(-0.5px, -3.5px) rotate(-90deg); }
.carimbo-alerta-icone {
  width: 15px; height: 15px; border-radius: 50%; flex-shrink: 0;
  background: var(--color-danger); color: var(--doma-branco);
  display: flex; align-items: center; justify-content: center;
  font: 700 11px var(--display); font-style: normal; line-height: 1;
}

/* item 4 — tornado e matriz em DUAS COLUNAS DE GRADE REAL, cada bloco em
   card próprio. ADENDO 3f (12/09/2026): o flex antigo deixava o conteúdo
   (tabela larga, `<details>` recém-aberto) crescer por cima do vizinho —
   `overflow` do item flex é `visible` por padrão e não recorta nada; o
   `min-width: auto` de conteúdo largo também empurrava a coluna. A grade
   com `1fr 1.25fr` dá à matriz (9×9, precisa de mais largura) a fatia maior,
   e cada `.sens-coluna` é um CARD de verdade (borda, fundo, raio do sistema)
   — não posicionamento absoluto nenhum aqui. */
.sens-lado-a-lado {
  display: grid; grid-template-columns: 1fr 1.25fr; gap: 24px;
  align-items: start; margin-top: 20px;
}
.sens-coluna {
  min-width: 0; border: 1px solid var(--linha); border-radius: var(--r-2);
  background: var(--superficie); padding: 16px 18px;
}
/* SENS-2 passada 2, item B — acima de 9 pontos por eixo a matriz desce para
   largura INTEIRA, abaixo do tornado (a razão e a conta moram no JS, junto
   de `LIMIAR_PONTOS_LADO_A_LADO`: 550px / 10 colunas cabe, 550px / 14 não).
   Com UMA coluna só na grade, o segundo filho (a matriz) cai sozinho na
   linha seguinte por fluxo normal — não precisa de `grid-column` explícito
   porque só há uma coluna para ocupar. */
.sens-lado-a-lado.sens-largo { grid-template-columns: 1fr; }
/* DESIGN-3, item 04 — o cabeçalho ganha uma terceira peça (o fieldset de
   faixa/passo, movido de casa): flex-wrap deixa ela cair numa linha própria
   quando não há espaço ao lado do título e do toggle "Mostrando VPL".
   DESIGN-7/ADENDO (D3) — `justify-content: space-between` sai (era pensado
   para DOIS filhos; com três, espalhava demais): título e toggle ficam juntos
   à esquerda, e o rótulo "Faixa" (primeiro item que sobra, depois que o
   fieldset virou `display:contents` — ver comentário grande logo abaixo)
   empurra o resto para o canto direito com `margin-left: auto`. */
.sens-matriz-cab { display: flex; align-items: flex-start; gap: 10px; margin-bottom: 6px; flex-wrap: wrap; }
.sens-matriz-cab > b { font: 700 14px var(--display); }
.sens-matriz-cfg-inline .sens-matriz-linha:first-of-type .sens-matriz-rotulo { margin-left: auto; }
.sens-matriz-scroll { overflow-x: auto; }
/* NOTURNO-6 item (c).2 — a Mesa fixou o piso de 12px para todo número da
   Sensibilidade, e a conta da ADENDO 3f (10,5px/11px, "padding 6px 8px a
   12px pedia 693px numa coluna de 573px") deixou de valer: quem cede agora
   para caber em 573px é o CONTEÚDO da célula — `fmtMilhoesNu` (número em
   milhões, sem "R$" e sem sufixo, a unidade só na `<caption>`), nunca a
   fonte. Medido de novo com o conteúdo novo: cabe em 12px sem rolagem. */
.matriz-sens { border-collapse: collapse; font: 12px var(--mono); width: 100%; }
.matriz-sens caption { caption-side: top; text-align: left; font: 500 11.5px var(--display); color: var(--tinta-2); padding-bottom: 6px; }
.matriz-sens th, .matriz-sens td { padding: 3px 4px; text-align: right; white-space: nowrap; }
.matriz-sens thead th { font: 600 12px var(--display); color: var(--tinta-2); border-bottom: 1px solid var(--linha); }
.matriz-sens tbody th { text-align: left; font: 600 12px var(--display); color: var(--tinta-2); }
.matriz-sens td.mtz-neg { background: var(--vermelho-claro); color: var(--vermelho); }
.matriz-sens td.mtz-pos { background: var(--verde-claro); color: var(--verde-escuro); }
/* DESIGN-3, item 04 — a célula base agora é CONTORNADA (não mais chapada em
   tinta): o sinal (mtz-neg/mtz-pos) continua colorindo o fundo, e o contorno
   só ACRESCENTA o destaque de "é aqui que o estudo está". */
.matriz-sens td.mtz-base { outline: 2px solid var(--color-primary); outline-offset: -2px; font-weight: 700; }
/* O canto que nomeia os dois eixos da matriz. */
.matriz-sens th.mtz-eixos { text-align: left; vertical-align: bottom; }
.mtz-eixo-col, .mtz-eixo-lin {
  display: block; font: 600 10px var(--display); color: var(--tinta-2);
  white-space: nowrap; text-transform: none;
}

#sens-saida { margin-top: 20px; }
/* D1 (DESIGN-7/ADENDO) -- ESTADO VAZIO DECLARADO dos blocos 03/04: terceiro
   contexto de `.vazio` nesta folha (ver o comentário grande em §7, `.painel
   > .vazio`) -- aqui a classe nua não tem regra própria (nem `#sens-tornado`
   nem `.sens-matriz-scroll` são `.painel`), quem estiliza é `.sens-vazio`.
   `.erro` empresta a cor de alerta quando o texto é a RAZÃO de uma falha
   ("Não rodou: …"), nunca o convite neutro padrão. */
.sens-vazio { padding: 24px 4px; text-align: center; color: var(--tinta-2); font-size: 13px; }
.sens-vazio.erro { color: var(--vermelho); }
/* ---- AUSÊNCIA, e ela é APAGADA de propósito ----
   Martelo do dono em 04/08/2026: a tela da Doma não usa travessão. Onde a
   métrica NÃO EXISTE (TIR sem taxa única, payback que não retorna, bloco
   desligado), entra uma frase curta em vez do traço — e ela é escrita em tinta
   apagada, minúscula e sem negrito porque é AUSÊNCIA, não alerta. Se tivesse
   peso de aviso, a tela ensinaria que "não há TIR" é um problema, quando na
   maioria dos casos é só a métrica não se aplicar àquele fluxo.
   Nunca "N/A": sigla é o travessão com outra roupa.
   PISO DE 12px (P-53, DIURNA-10, 16/09/2026): frase no lugar de número obedece
   ao mesmo piso do número (D-401, nenhuma fonte numérica abaixo de 12px) --
   dentro da célula de 13px de `.tabela-resultado td` (a tabela do investidor,
   entre outras), 0,86em media 11,18px, medido abaixo do piso. `max()` mantém a
   proporção onde o pai é grande (KPI, card) e trava em 12px onde o pai é
   pequeno; as regras mais específicas abaixo (`.kpi-valor .sem-valor` etc.)
   já escreviam px fixo e não mudam. */
.sem-valor {
  font-style: normal; font-weight: 400;
  font-size: max(0.86em, 12px); color: var(--tinta-3);
  letter-spacing: 0;
}
/* LÁPIDE (FINAL-2, 19/09/2026, P-67/D-441): `.tir-nc` morreu com a marca
   "não confiável" -- TIR ausente do projeto passou a usar `.sem-valor` como
   toda ausência da tela, com a causa do motor visível ao lado (nunca só no
   `title`). */
/* AUSÊNCIA SEM CORTE, DENTRO DO KPI (ADENDO NOTURNO-6 item 1, 12/09/2026).
   `.kpi-valor` é mono 22px — o tamanho do NÚMERO. Uma frase de ausência
   ("não definida para este fluxo", "sem dívida (nem banco, nem fundo)")
   herdando 0,86em desse mono grande (≈18,9px) cortava/quebrava feio dentro
   do card, medido no print da prancha 12. A ausência não é número: é
   PROSA curta, e ganha a folha da prosa — nunca abaixo de 12px (regra de
   toda a Sensibilidade/tela, D-322). As linhas do mesmo card seguem
   alinhadas: `.kpi` estica pela grade (align-items:stretch), então um card
   mais alto nesta fileira eleva só os vizinhos DA MESMA fileira. */
.kpi-valor .sem-valor {
  display: inline-block;
  font: 400 13px/1.35 var(--display); color: var(--tinta-2);
  white-space: normal; letter-spacing: 0;
}

/* ---- TORNADO (ADENDO 3f, 12/09/2026) — uma BARRA por alavanca, e não mais
   uma linha por variação testada: o zero fica no centro, o lado que PIORA
   (vermelho) cresce para a esquerda a partir dele, o lado que MELHORA
   (verde) cresce para a direita, os dois na MESMA escala — o `maxDelta`
   global que `renderSensibilidade` já calculava para as barras antigas —,
   para que o olho compare alavancas ENTRE SI sem reler número nenhum.
   A alavanca mais forte (maior |Δ lucro| do grupo) ganha `--color-primary-
   strong` no nome; as demais ficam em `--color-primary` a 75% (DESIGN-3,
   item 03, preservado da forma anterior). */
.tornado-linha { padding: 10px 0; border-bottom: 1px solid var(--linha); }
.tornado-linha:last-of-type { border-bottom: 0; }
.tornado-nome { font: 600 12.5px var(--display); color: var(--color-primary); opacity: 0.75; margin-bottom: 6px; }
.tornado-nome.tornado-mais-forte { color: var(--color-primary-strong); opacity: 1; }
.tornado-barra-grupo { display: flex; align-items: center; gap: 8px; }
/* NOTURNO-6 item (c).3 — 11,5px pintava NÚMERO (a ponta da barra); a Mesa
   fixou o piso em 12px para todo número da Sensibilidade. */
.tornado-valor { font: 600 12px var(--mono); white-space: nowrap; min-width: 68px; }
.tornado-valor.piora { color: var(--vermelho); text-align: right; }
.tornado-valor.melhora { color: var(--verde-escuro); text-align: left; }
.tornado-eixo { flex: 1 1 auto; min-width: 0; display: flex; align-items: center; height: 14px; }
.tornado-trilha { flex: 1 1 50%; min-width: 0; height: 100%; display: flex; }
.tornado-trilha.piora { justify-content: flex-end; }
.tornado-trilha.melhora { justify-content: flex-start; }
.tornado-lado { height: 100%; border-radius: 2px; min-width: 0; }
.tornado-lado.piora { background: var(--vermelho); }
.tornado-lado.melhora { background: var(--verde-escuro); }
.tornado-zero { width: 1px; align-self: stretch; background: var(--linha); flex-shrink: 0; }
/* SENS-ESTADO (item 2, NOTURNO-6 item 6) — a linha do zero ganha o rótulo
   "base", em texto (≥12px): UMA legenda compartilhada, alinhada com a
   coluna `.tornado-eixo` (mesma flex, mesmo centro) e fora de
   `.tornado-linha` de propósito — não é uma alavanca, é o eixo comum a
   todas. `.tornado-valor-espaco` replica a largura mínima de `.tornado-valor`
   dos dois lados, para o centro de `.tornado-eixo` cair no mesmo x da
   divisória `.tornado-zero` de cada barra. */
.tornado-eixo-legenda { display: flex; align-items: center; gap: 8px; padding-bottom: 6px; }
.tornado-valor-espaco { min-width: 68px; flex-shrink: 0; }
.tornado-eixo-legenda .tornado-eixo { justify-content: center; height: auto; }
.tornado-eixo-legenda-txt { font: 600 12px var(--display); color: var(--tinta-3); }

/* ---- O DETALHE POR ALAVANCA (ADENDO 3f-tabela) ----
   Colapsado por padrão: a barra acima já responde "quanto"; o detalhe
   responde "cada variação testada, ponto a ponto" — a tabela de sempre
   (uma linha por variação, D-169 incluído). UMA `<details>` só, com uma
   `<table>` por alavanca dentro: abrir o detalhe é uma decisão só, não uma
   por alavanca. `table-layout: fixed` + `<colgroup>` medido pelo app.js (o
   maior valor real de cada coluna, em caracteres) fazem as sete colunas
   caberem na largura do card sem rolagem — `nowrap` é reforçado por
   `text-overflow: ellipsis` como rede de segurança do próprio cálculo:
   nenhuma célula pode crescer para uma segunda linha. */
.tornado-detalhe { margin-top: 14px; }
.tornado-detalhe > summary { cursor: pointer; font: 600 12px var(--display); color: var(--color-primary); list-style: none; }
.tornado-detalhe > summary::-webkit-details-marker { display: none; }
.tornado-detalhe[open] > summary { margin-bottom: 10px; }
.tabela-tornado { table-layout: fixed; margin-top: 12px; }
.tabela-tornado:first-of-type { margin-top: 0; }
.tabela-tornado caption { caption-side: top; text-align: left; font: 700 12px var(--display); color: var(--tinta); padding-bottom: 6px; }
.tabela-tornado td, .tabela-tornado th { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* DOBRA (achado 3c) — DEGRAU 2: as larguras agora vêm de MEDIÇÃO real de DOM
   (`ajustarLarguraDetalheTornado` em app.js), e o próprio JS decide, medindo,
   se precisa encolher a fonte — nunca por adivinhação. `!important` porque a
   fonte do `<th>` já vem de um `font:` (shorthand) em `.tabela-resultado th`,
   que teria a mesma especificidade e perderia por ordem se um dia alguém
   reordenar a folha; a classe é aplicada/removida pelo JS, então o
   `!important` não esconde nada de quem lê o CSS puro. */
/* NOTURNO-6 item (c).3 — o piso deixou de ser 11px: fonte de número na
   Sensibilidade nunca desce de 12px (martelo da Mesa). O `--tornado-fonte`
   só chega a 12 agora (`ajustarLarguraDetalheTornado`); o fallback do
   `var()` acompanha para não haver um segundo número de piso divergente. */
.tabela-tornado-compacta td, .tabela-tornado-compacta th { font-size: var(--tornado-fonte, 12px) !important; }
/* DOBRA — DEGRAU 3: nem no piso de 12px coube. O `<details>` sai de dentro
   do card do tornado e vira filho direto de `#sens-saida` (a GRADE), abaixo
   dos dois cards, ocupando as duas colunas — `grid-column: 1 / -1` funciona
   igual com uma coluna só (`.sens-largo`) ou com duas. */
.tornado-detalhe-largo { grid-column: 1 / -1; margin-top: 20px; }
/* ---- LINHA QUE NÃO SE MOVEU (D-169) ----
   Velocidade de vendas é medida em MESES INTEIROS: numa curva de n safras,
   variação menor que 1/n não move mês nenhum. A linha continua na tabela
   (ela FOI testada), mas inteira em tinta apagada e itálico — NUNCA o âmbar
   do "Atenção", porque a premissa não está errada, é o instrumento que não
   tem resolução para o que foi pedido. A frase do motor (`motivo_sem_efeito`)
   deixou de ocupar uma célula própria (ADENDO 3f-tabela): ela vive inteira
   no `title` do ícone "i", e a coluna Δ mostra só "= base". */
.linha-base td { color: var(--tinta-2); font-style: italic; }
.dica-i {
  display: inline-flex; align-items: center; justify-content: center;
  width: 14px; height: 14px; border-radius: 50%; margin-left: 4px;
  background: var(--superficie-2); color: var(--tinta-2);
  font: 700 9px var(--display); font-style: normal; cursor: help;
}
/* 1d CONSERTO (DIURNA-8) -- `.dica-i` tem DUAS origens: a do Comparar
   (`linhaIndicador`) ganhou `data-ajuda` e abre o painel; a do tornado de
   sensibilidade ("= base") não é a do achado medido (`check_ajuda` cena 2b
   só mede `#aba-comparar .dica-i`) e continua só com `title` -- por isso o
   cursor muda SÓ onde o atributo existe, e a irmã continua honesta em
   `cursor: help`. */
.dica-i[data-ajuda] { cursor: pointer; }
/* O PEDIDO ao lado do EFETIVO, na MESMA linha (ADENDO 3f-tabela: o
   `.var-pedida` antigo era `display: block` e forçava a célula a duas
   linhas — exatamente o que a régua de altura agora proíbe). */
/* DOBRA (ressalva A4 do revisor) — `.mono`/`.muted` NÃO SÃO GLOBAIS: já
   havia seis sítios fora desta fatia (o Fluxo mensal, ~app.js 6489/6505/
   6506/7071/7078/7079 — "R$ milhões" e os meses da nota) usando essas
   MESMAS classes sem regra nenhuma no CSS; uma regra global as pegaria de
   carona e mudaria a pintura deles sem ninguém pedir. Escopadas ao
   `.tabela-tornado` (o único container onde ESTA fatia as usa —
   `variacaoHtml`/`tirHtml` em `renderSensibilidade`), os seis sítios do Fluxo
   voltam a não ter regra nenhuma, como sempre tiveram. */
.tabela-tornado .muted { color: var(--tinta-3); }
.tabela-tornado .mono { font-family: var(--mono); }

/* ---- CHIPS das variações ----
   Widget diferente do editor de curvas porque o DADO é diferente: conjunto sem
   eixo do tempo, com negativo e sem ordem obrigatória. A moldura, o cabeçalho e
   o farol são os mesmos (`.chips-ed` divide a regra com `.curva-ed`); o que muda
   é o miolo. O SINAL é o que se lê primeiro — vermelho para baixo, verde para
   cima —, e é ele que diz se a linha da tabela tornado é estresse ou folga. */
/* DESIGN-7/ADENDO (D3) -- `.chip`/`.chips` são exclusivas da sensibilidade
   (nenhum outro editor as usa: obra e vendas desenham `.curva-col`), então
   encolher aqui não tira nada de mais ninguém. Mais compactas para caber os
   6 chips padrão na mesma linha do botão "Rodar" e do "editar lista…". */
.chips { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; min-height: 26px; }
.chip {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 4px 5px 4px 9px; border-radius: 999px; border: 1px solid var(--linha);
  background: var(--superficie); font: 500 12px var(--mono); color: var(--tinta-2);
  font-variant-numeric: tabular-nums;
}
.chip.neg { border-color: var(--vermelho-borda); background: var(--vermelho-claro); color: var(--vermelho); }
.chip.pos { border-color: var(--verde-borda); background: var(--verde-claro); color: var(--verde-escuro); }
.chip button {
  border: 0; background: transparent; cursor: pointer; color: inherit;
  font-size: 13px; line-height: 1; opacity: 0.55; padding: 0 2px; border-radius: 50%;
}
.chip button:hover { opacity: 1; }
.chip button:focus-visible { outline: 2px solid var(--verde); outline-offset: 1px; opacity: 1; }

/* ================= FLUXO MENSAL =================
   TRÊS REGRAS QUE NÃO SÃO ENFEITE (R3.2):
   1. GRUPO — o cabeçalho mora na PRIMEIRA célula, que é a congelada, e por isso
      sobrevive à rolagem lateral. Receita e despesa se distinguiam pelo sinal, e
      só; numa tabela de dezenas de colunas isso é adivinhação.
   2. TOTAL DE GRUPO ≠ RESULTADO — o verde marcava "Receita total" E "Saldo" com a
      mesma tinta. Agora só o RESULTADO é verde; total de grupo é cinza.
   3. HOVER DA LINHA INTEIRA — e a segunda regra do par é o ponto: a primeira
      célula é `position: sticky` COM FUNDO PRÓPRIO. Sem repetir o fundo nela, a
      faixa de hover quebra exatamente na coluna congelada, que é onde o olho
      está. Pêssego porque pêssego é interação (P4), nunca aviso. */
/* B2/FLUXO-1 (12/09/2026): A TENTATIVA DE STICKY-NA-PÁGINA FOI MEDIDA E
   REVERTIDA NO MESMO DIA. A ideia era `overflow-x: auto` (scroll horizontal
   preso ao wrap) + `overflow-y: clip` (sem criar ancestral de rolagem
   vertical, deixando o `th` grudar na PÁGINA) — mas MEDIDO no Chrome real
   (152.0.7977.83) o computado de `overflow-y` não fica `clip`: vira `hidden`,
   que AINDA cria ancestral de rolagem para fins de `position: sticky`.
   Resultado medido: o `th` não gruda em lugar nenhum — ele anda exatamente
   com a rolagem da página, pixel a pixel (o defeito original: o cabeçalho
   media ~636px depois de rolar 250px, exatamente `886 − 250`, isto é,
   `position: static`). A ORDEM já previa esta saída: "se não houver saída
   sem quebrar o scroll horizontal, mantenha o sticky dentro do wrap com
   max-height, e diga" — e não há: `clip` não se comporta como a spec
   sugere nesta combinação. Volta-se à forma ANTERIOR à FLUXO-1, medida e
   funcionando: `overflow: auto` nos dois eixos, `max-height` para dar ao
   wrap uma altura própria de rolagem, e o `th` gruda no TOPO DO WRAP
   (`top: 0`) — sticky-DENTRO-do-wrap, não sticky-na-página. */
.fluxo-wrap {
  overflow: auto; max-height: 66vh;
  background: var(--superficie); border: 1px solid var(--linha);
  border-radius: var(--raio); box-shadow: var(--sombra);
}
/* PLAN-1 (NOTURNO-7 item 2, D-401): base sobe de 11 para 12px -- "os números
   mantêm alinhamento e 12 px" é a régua, e ela só sobe, nunca encolhe. */
.fluxo-wrap table { border-collapse: collapse; font: 12px var(--mono); min-width: 100%; }
.fluxo-wrap th, .fluxo-wrap td { padding: 7px 11px; text-align: right; white-space: nowrap; border-bottom: 1px solid #eef2f0; }
/* CABEÇALHO EM QUATRO FAIXAS (PLAN-1): mês, calendário, marco (SÓ quando há
   evento) e o sufixo de seleção -- todas presentes em TODO `th`, para que a
   altura seja a MESMA com ou sem marco/seleção (o slot reservado do P-8,
   agora com uma linha a mais).
   ⚠ DOBRA (bloqueante B1 do revisor sobre 913420e): esta régua dizia
   "min-width... sem mudar de largura entre um mês com marco e um sem
   (medido no roteiro de selo do card)" -- MEDIDO FALSO no artefato (a
   classe da DP-079): `min-width` sem teto deixa a coluna CRESCER até o
   nome curto mais longo do `th` (M1 "Lançamento" 98,8px, M33 "Fim obra +
   Habite-se" 154,7px, e dois marcos no mesmo mês, forma real de
   `app/schemas.py` "Devolução · <nome>", chegou a 417,9px = 4,35× a coluna
   numérica). "Eventos não alargam colunas" exige LARGURA FIXA, não piso:
   a coluna é `width`/`min-width`/`max-width` iguais (112px cabe
   "Lançamento" e "Investidor" inteiros a 12px, medido), e é a FAIXA 3
   (`.fx-mes-marco-txt`, abaixo) que corta por pixel quando o nome não
   cabe -- o `textContent` continua inteiro (é o que o guardião compara) e
   o nome completo segue no `title`. */
.fluxo-wrap th {
  position: sticky; top: 0;
  background: var(--superficie-2); font: 600 12px var(--display); letter-spacing: 0.05em; z-index: 2;
}
/* O alinhamento/largura das QUATRO FAIXAS é só do `th[data-mes]` -- o
   cabeçalho de MÊS, nas duas tabelas (o `th` de série/grupo da vertical
   segue com o alinhamento numérico de sempre, à direita, sem largura
   fixa). LARGURA FIXA (não piso): todo mês mede o MESMO, com marco longo,
   sem marco, ou selecionado -- o corte é da FAIXA 3, nunca da coluna. */
.fluxo-wrap th[data-mes] {
  text-align: left; vertical-align: top;
  width: 112px; min-width: 112px; max-width: 112px;
}
.fluxo-wrap td:first-child, .fluxo-wrap th:first-child {
  position: sticky; left: 0; background: var(--superficie); text-align: left;
  font: 600 12px var(--display); z-index: 1;
}
.fluxo-wrap th:first-child { z-index: 3; background: var(--superficie-2); }
/* AS QUATRO FAIXAS, EM PÉ (PLAN-1): número do mês (mono), calendário (mono,
   tinta mais fraca), nome curto do marco (display, cor da marca -- vazio
   quando não há evento, D-233: aqui a ausência é o SLOT em branco, não a
   frase "sem marco" repetida por coluna, decisão da Mesa) e o sufixo de
   seleção de sempre (`.fx-sel-sufixo`, inalterado). */
.fx-mes-num { display: block; font: 600 12px var(--mono); }
/* SEM CALENDÁRIO (estudo sem data de início) `rotuloMesAno` devolve string
   VAZIA (D-187/D-233: sem data não se inventa nada) -- a MESMA armadilha do
   marco: bloco vazio sem `min-height` perde a linha inteira. */
.fx-mes-cal {
  display: block; font: 400 12px var(--mono); line-height: 15px; min-height: 15px;
  color: var(--tinta-2); margin-top: 3px;
}
/* SEM MARCO O CONTEÚDO É STRING VAZIA -- e um bloco SEM CONTEÚDO NENHUM não
   gera caixa de linha (diferente do `.fx-sel-sufixo`, que tem TEXTO oculto
   por `visibility:hidden`). Sem `min-height` aqui, o slot da faixa 3
   SOME nos meses sem marco e a altura do cabeçalho passa a variar --
   exatamente o defeito que a régua proíbe. `min-height` reserva a linha
   mesmo vazia (medido no roteiro de selo: 12px de fonte, ~15px de caixa).
   DOBRA (bloqueante B1): a coluna é LARGURA FIXA (112px no `th`, 108px na
   `mes-cel` vertical) -- nunca é ela que cresce.
   VAR-1 item d (14/09/2026) REVOGA O CORTE POR ELLIPSIS: dois marcos unidos
   por " + " ("Fim obra + Habite-se") mediam 372px de conteúdo contra 90px
   de faixa útil, e a reticência escondia a metade do nome sem eco visível
   nenhum. A faixa QUEBRA EM LINHAS dentro da própria célula (a ALTURA da
   linha do cabeçalho/da linha do mês cresce para caber, nunca a largura da
   coluna) -- o nome curto continua o mesmo (`nomeCurtoDeMarco`), só a régua
   de corte muda de PIXEL para LINHA; o nome completo segue no `title` do
   `th`/`td.mes-cel`, que já o carregava. */
.fx-mes-marco-txt {
  display: block; font: 500 12px var(--display); line-height: 15px; min-height: 15px;
  color: var(--color-primary); margin-top: 3px;
  overflow: visible; white-space: normal; overflow-wrap: break-word; max-width: 100%;
}
.fluxo-wrap tr.grupo td, .fluxo-wrap tr.grupo td:first-child {
  background: var(--superficie-2); border-top: 2px solid var(--linha); border-bottom: 0;
  font: 700 9.5px var(--display); letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--tinta-2); padding-top: 10px; padding-bottom: 5px;
}
/* a explicação do grupo mora na parte que ROLA, onde não cobra largura da
   coluna congelada (ver o comentário de `grupo()` no app.js) */
.fluxo-wrap tr.grupo td + td {
  text-align: left; font-weight: 400; letter-spacing: 0.02em;
  text-transform: none; color: var(--tinta-3);
}
.fluxo-wrap tr.subtotal td, .fluxo-wrap tr.subtotal td:first-child {
  background: #eef2f0; font-weight: 600;
}
.fluxo-wrap tr.resultado td, .fluxo-wrap tr.resultado td:first-child {
  background: var(--verde-claro); font-weight: 600; color: var(--verde-escuro);
}
.fluxo-wrap tr.resultado td.neg { color: var(--vermelho); }
/* A CÉLULA DA EXPOSIÇÃO MÁXIMA, na linha Acumulado (DESIGN-7 item 4) --
   marcada por CLASSE que o JS só escreve no mês que `mes_exposicao_maxima`
   publica, nunca por posição adivinhada. `box-shadow` inset em vez de
   `border`: a tabela é `border-collapse: collapse`, e uma borda de verdade
   nesta única célula desalinharia a grade das vizinhas. */
.fluxo-wrap tr.resultado td.exposicao-max {
  background: var(--color-danger-soft); box-shadow: inset 0 0 0 1px var(--vermelho-borda);
}
/* ZEBRA (DESIGN-7 item 4) -- só nas linhas de DADO, nunca nas decoradas
   (grupo/subtotal/resultado já têm fundo próprio, e pintar por cima seria a
   mesma doença do D-076: duas regras para o mesmo pixel). Os `:not()` dão a
   este seletor MAIS especificidade que a regra de hover genérica logo abaixo
   -- por isso o hover ganha um PAR PRÓPRIO, com a mesma cadeia de `:not()`,
   para continuar vencendo em cima de linha zebrada (P4: hover é interação e
   nunca fica preso atrás de decoração parada). */
.fluxo-wrap tbody tr:nth-child(even):not(.grupo):not(.subtotal):not(.resultado) td {
  background: var(--superficie-2);
}
.fluxo-wrap tbody tr:nth-child(even):not(.grupo):not(.subtotal):not(.resultado) td:first-child {
  background: var(--superficie);
}
.fluxo-wrap tbody tr:hover td { background: var(--pessego-fundo); }
.fluxo-wrap tbody tr:hover td:first-child { background: var(--pessego-borda); }
.fluxo-wrap tbody tr:nth-child(even):not(.grupo):not(.subtotal):not(.resultado):hover td {
  background: var(--pessego-fundo);
}
.fluxo-wrap tbody tr:nth-child(even):not(.grupo):not(.subtotal):not(.resultado):hover td:first-child {
  background: var(--pessego-borda);
}
.fluxo-wrap tbody tr.grupo:hover td { background: var(--superficie-2); }

/* ITEM 3 (NOTURNO-6 item 8, DESIGN-8) -- SELEÇÃO SINCRONIZADA DO MÊS: a
   COLUNA inteira na tabela horizontal (série × mês) ou a LINHA inteira na
   vertical (mês × série) ganha a mesma tinta, com o cabeçalho/rótulo do mês
   escrito por extenso com o sufixo "selecionado" (nunca só cor -- cor
   sozinha não fala para leitor de tela). `!important` porque a seleção é o
   juízo mais forte da tabela: ela precisa vencer a zebra e o hover.
   DOBRA R3 (14/09/2026): a menção original citava também o fundo próprio
   das colunas presas à direita (`.res-fixo`) -- essa classe foi REVOGADA
   pela VAR-1 item c (o par de colunas fixas cobria "Repasse…" em 834px);
   só a coluna da rubrica (`td:first-child`) segue presa, e o `!important`
   continua valendo pelas duas razões que restaram. */
.fluxo-wrap th[data-mes], .fluxo-wrap td[data-mes], .fluxo-wrap tbody tr[data-mes] {
  cursor: pointer;
}
/* PLAN-1 (NOTURNO-7 item 2): EVENTO (fundo) e SELEÇÃO (contorno) são
   classes DISTINTAS e coexistem na mesma célula -- "cor nunca sozinha": o
   marco fala pelo nome na faixa 3, a seleção fala pela palavra
   "Selecionado". `!important` pela mesma razão de sempre: vencer a zebra,
   o hover e o fundo próprio das colunas presas. */
.fluxo-wrap th.fx-mes-marco, .fluxo-wrap td.fx-mes-marco,
.fluxo-wrap tbody tr.fx-mes-marco td {
  background: var(--color-primary-soft) !important;
}
/* A SELEÇÃO ERA SÓ FUNDO -- agora é CONTORNO (D-076: um juízo, uma pista).
   Horizontal: o mês é COLUNA, então o contorno fecha nas duas LATERAIS e,
   no cabeçalho, também por CIMA (a prancha B-1720: `inset 2px 0`/`inset
   -2px 0` nas células, mais `inset 0 2px` só no `th`). O fundo próprio da
   seleção usa o MESMO token do marco -- coexistem sem duas tintas
   competindo pelo mesmo pixel. */
.fluxo-wrap th.fx-mes-sel, .fluxo-wrap td.fx-mes-sel {
  background: var(--color-primary-soft) !important;
  box-shadow: inset 2px 0 0 var(--color-primary), inset -2px 0 0 var(--color-primary);
}
.fluxo-wrap th.fx-mes-sel {
  box-shadow: inset 2px 0 0 var(--color-primary), inset -2px 0 0 var(--color-primary), inset 0 2px 0 var(--color-primary);
}
/* Vertical: base do fundo da LINHA selecionada (o contorno próprio da
   orientação vertical -- topo/base na linha, esquerda na `mes-cel` -- mora
   no bloco `.vertical` mais abaixo, onde a especificidade já vence esta
   regra genérica sem duplicar o `!important`). */
.fluxo-wrap tbody tr.fx-mes-sel td {
  background: var(--color-primary-soft) !important;
}
.fluxo-wrap th.fx-mes-sel, .fluxo-wrap tbody tr.fx-mes-sel td.mes-cel {
  color: var(--color-primary); font-weight: 600;
}
/* P-8 (DIURNA-1 item 4e, ressalva do revisor do NOTURNO-6 item 8): o
   sufixo "selecionado" NUNCA fica abaixo de 12px (D-401 -- fonte numérica
   nunca abaixo de 12px, e a régua vale aqui também) e a coluna do mês
   NUNCA muda de largura no clique. Os dois problemas tinham a MESMA causa
   -- o sufixo herdava os 10/11px do `th`/`td` e só existia no DOM quando
   selecionado, então "subir a fonte" ia crescer a coluna bem no instante
   do clique. A saída é o slot reservado (irmã do D-076 no Cronograma): o
   `<span>` mora SEMPRE no HTML (ver `tabelaFluxoHorizontal`/`Vertical`),
   com `display:block` numa linha própria abaixo do rótulo -- então a
   largura da coluna já nasce contando com ele, selecionado ou não, e o
   clique só troca `visibility` (que preserva o espaço) e a cor herdada da
   regra acima. Nada encolhe: a única mudança de tamanho aqui é para CIMA,
   de 10/11px para 12px, e só nesta linha nova -- o rótulo do mês continua
   do tamanho de sempre. */
.fx-sel-sufixo {
  display: block; font: 600 12px var(--display); letter-spacing: 0.01em;
  white-space: nowrap; margin-top: 2px;
}

.legenda-fluxo {
  display: flex; gap: 16px; flex-wrap: wrap; margin-top: 10px;
  font-size: 12px; color: var(--tinta-2);
}
.legenda-fluxo span { display: inline-flex; align-items: center; gap: 6px; }
.legenda-fluxo i {
  width: 14px; height: 10px; border-radius: 3px; display: inline-block;
  font-style: normal; border: 1px solid var(--linha);
}
.legenda-fluxo .fundo-grupo { background: var(--superficie-2); }
.legenda-fluxo .fundo-subtotal { background: #eef2f0; }
.legenda-fluxo .fundo-resultado { background: var(--verde-claro); border-color: var(--verde-borda); }
.legenda-fluxo .fundo-neg { background: var(--superficie); border-color: var(--vermelho-borda); }
.legenda-fluxo .fundo-hover { background: var(--pessego-fundo); border-color: var(--pessego-borda); }
/* PLAN-1: as duas chaves novas -- o fundo do MARCO e o contorno da
   SELEÇÃO, os dois fatos que a régua manda a legenda explicar (item 3). */
.legenda-fluxo .fundo-marco { background: var(--color-primary-soft); border-color: var(--verde-borda); }
.legenda-fluxo .fundo-sel {
  background: var(--superficie); box-shadow: inset 2px 0 0 var(--color-primary), inset -2px 0 0 var(--color-primary);
}

/* TABELA VERTICAL DO FLUXO EM IPAD (DESIGN-6) -- MESMO `#fluxo-wrap`, MESMA
   tabela: `renderFluxo` transpoe -- meses em linha, series em coluna -- e
   marca o container com `.vertical`. Nenhuma segunda tabela no DOM: a classe
   e quem muda a leitura das mesmas regras de grupo/subtotal/resultado, que
   na horizontal vivem na LINHA (`tr.grupo`) e aqui vivem na COLUNA (`td`/`th`
   com a mesma classe). */
/* PLAN-1: a célula empilha as MESMAS quatro faixas do cabeçalho horizontal
   (`.fx-mes-num`/`.fx-mes-cal`/`.fx-mes-marco-txt`/`.fx-sel-sufixo`).
   DOBRA (bloqueante B1, mesma causa do `th[data-mes]` acima): LARGURA FIXA
   -- não piso --, porque `min-width` sozinho deixava a coluna do MÊS
   crescer até o nome curto mais longo (inclusive dois marcos no mesmo mês
   unidos por " + "). É a `.fx-mes-marco-txt` (compartilhada com a
   horizontal) que corta por pixel; o `textContent` e o `title` continuam
   inteiros. */
.fluxo-wrap.vertical td.mes-cel {
  text-align: left; vertical-align: top;
  width: 108px; min-width: 108px; max-width: 108px;
}
/* PLAN-1: o CONTORNO da seleção, na orientação vertical, fecha em
   CIMA/BAIXO (o mês é LINHA, não coluna) -- e a `mes-cel` fecha também à
   ESQUERDA, o papel que o `th` faz na horizontal (D-076: mesma regra, outro
   eixo). Especificidade de duas classes (`.vertical` + `.fx-mes-sel`)
   vence a regra genérica de cima sem precisar de `!important` extra. */
.fluxo-wrap.vertical tbody tr.fx-mes-sel td {
  box-shadow: inset 0 2px 0 var(--color-primary), inset 0 -2px 0 var(--color-primary);
}
.fluxo-wrap.vertical tbody tr.fx-mes-sel td.mes-cel {
  box-shadow: inset 0 2px 0 var(--color-primary), inset 0 -2px 0 var(--color-primary), inset 2px 0 0 var(--color-primary);
}
.fluxo-wrap.vertical thead tr.grupo th {
  background: var(--color-primary-soft); border-bottom: 1px solid var(--linha);
  font: 700 9.5px var(--display); letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--tinta-2); text-align: left;
}
/* DOBRA D-263 (ESTEIRA-1, 12/09/2026), BLOQUEANTE (a) DO REVISOR SOBRE aaeaa22 --
   duas colisoes de sticky no cabecalho de duas linhas, medidas em 1024px com
   `.fluxo-wrap` rolado (scrollLeft 400 / scrollTop 300):
   (1) `th:first-child` GENERICO gruda TODO primeiro `th` de CADA linha na
       esquerda -- e o primeiro `th` da 2a linha (a 1a SERIE, ex.: "Sinal")
       nao e o Mes: ele grudava DENTRO do "Mes" (rowspan=2), 29..110 sobre
       29..179 medido. So a celula do MES fica presa na esquerda; a da serie
       perde so o eixo horizontal (`left: auto`), mantendo o eixo vertical
       (herda `top` da regra abaixo) -- sticky e por EIXO, nao e tudo ou nada.
   (2) as DUAS linhas do thead tinham `top: 0` -- com dois eixos de sticky
       empilhados na mesma referencia, ambas colam no topo do wrap ao rolar,
       uma por cima da outra (medido: as duas com o MESMO top apos rolar).
       A 2a linha ganha `top` = altura FIXA da 1a (26px, medida nesta folha:
       9,5px de fonte + padding 7px/11px do genérico + borda 1px ≈ 25,5px,
       arredondado para cima) -- ela cola LOGO ABAIXO da 1a, nunca por cima.
       A altura vira EXPLICITA nas celulas de GRUPO (exceto a do Mes, que e
       rowspan=2 e cresce sozinha para cobrir as duas linhas) para que o
       offset da 2a linha tenha uma base que nao muda com o conteudo. */
.fluxo-wrap.vertical thead tr.grupo th:not(:first-child) { height: 26px; }
.fluxo-wrap.vertical thead tr:not(.grupo) th { top: 26px; }
.fluxo-wrap.vertical thead tr:not(.grupo) th:first-child { left: auto; }
.fluxo-wrap.vertical td.subtotal, .fluxo-wrap.vertical th.subtotal {
  background: #eef2f0; font-weight: 600;
}
.fluxo-wrap.vertical td.resultado, .fluxo-wrap.vertical th.resultado {
  background: var(--verde-claro); font-weight: 600; color: var(--verde-escuro);
}
.fluxo-wrap.vertical td.resultado.neg { color: var(--vermelho); }
.fluxo-wrap.vertical td.exposicao-max {
  background: var(--color-danger-soft); box-shadow: inset 0 0 0 1px var(--vermelho-borda);
}
/* VAR-1 item c (14/09/2026) REVOGA OS SALDOS FIXOS À DIREITA (DESIGN-6): as
   duas colunas presas na borda direita (`.res-fixo.r0`/`.r1`, cada uma
   assumindo 96px) se sobrepunham quando "Saldo do mês c/ financiamento" e
   "Acumulado c/ financiamento" -- os próprios rótulos, mais largos que
   96px -- cobriam a coluna "Repasse…" vizinha, em 834px. A promessa da casa
   é UMA coluna fixa (a rubrica, à esquerda, `td:first-child`/`.mes-cel`);
   as demais rolam dentro do wrap (`overflow: auto` já é do próprio
   `.fluxo-wrap`), nunca a página. DOBRA R3 (14/09/2026): `fixoCls` FOI
   REMOVIDA do `app.js` no mesmo commit -- o JS não escreve mais
   `.res-fixo`/`.r0`/`.r1` em lugar nenhum; esta regra some do CSS por não
   ter mais alvo (D-225: remoção nas duas pontas, não lápide de atributo
   que ninguém escreve). */
.fluxo-wrap.vertical tfoot td { border-top: 2px solid var(--linha); font-weight: 700; }

/* B2 -- A FAIXA DE RESUMO DO FLUXO (FLUXO-1/ESTEIRA-1). Mesma família visual
   do `.kpi` (DESIGN-4/PDF): ícone em círculo, rótulo pequeno em display,
   valor em mono. Cinco cards em vez de oito, e por isso a coluna é 5 e não 4 --
   nada aqui compete com a grade de KPIs do Resultado, que é outra tela. */
.fluxo-resumo {
  display: grid; grid-template-columns: repeat(5, 1fr); gap: 10px; margin-bottom: 16px;
}
/* LEITURA IPAD (DESIGN-6) -- MESMO breakpoint de `.kpi-linha` (1100px): 3+2
   em vez de 5 numa linha so ou 2+2+1. `repeat(3, 1fr)` com cinco cards basta
   -- o quarto e o quinto caem sozinhos na segunda linha pelo fluxo normal do
   grid, sem coluna extra nem `nth-child`. */
@media (max-width: 1100px) { .fluxo-resumo { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.fluxo-resumo .card {
  background: var(--superficie); border: 1px solid var(--linha);
  border-radius: var(--raio); box-shadow: var(--sombra); padding: 12px 14px 13px;
}
.fx-icone {
  width: 26px; height: 26px; border-radius: 50%; margin-bottom: 8px;
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--superficie-2); color: var(--tinta-2);
}
.fx-icone svg { width: 15px; height: 15px; }
.fx-icone.positivo { background: var(--color-primary-soft); color: var(--color-primary); }
.fx-icone.negativo { background: var(--color-danger-soft); color: var(--color-danger); }
/* O ALERTA (exposição máxima) É SOLTO, círculo cheio em vez do fundo-macio dos
   outros: é o único dos cinco que é sempre má notícia por natureza -- os
   outros dependem de contexto (D-223, a mesma razão pela qual TIR/payback/
   custo do dinheiro nascem neutros na grade de KPIs). */
.fx-icone.alerta { background: var(--vermelho); color: var(--doma-branco); }
/* DOBRA R4 (14/09/2026): esta regra tem a MESMA especificidade de `.card
   .rotulo` e vem DEPOIS no arquivo — vencia com 9,5px mesmo depois do
   VAR-1 item b subir o piso lá. Sobe junto, mesmo piso de 12px (D-401). */
.fluxo-resumo .rotulo {
  font: 600 12px var(--display); letter-spacing: 0.08em; text-transform: uppercase; color: var(--tinta-2);
}
.fluxo-resumo .valor {
  font: 500 20px var(--mono); margin-top: 6px; letter-spacing: -0.01em;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.fluxo-resumo .valor.positivo { color: var(--verde-escuro); }
.fluxo-resumo .valor.negativo { color: var(--vermelho); }
/* ESTADOS-1, item 8 (ACOPLADO, mesma gramática de ausência do item 3 do
   Resultado -- `.kpi-valor .sem-valor`, ADENDO NOTURNO-6 item 1). Os cinco
   cards do Fluxo mensal mostram ausência ("não recupera no horizonte", "sem
   dívida (nem banco, nem fundo)") dentro do MESMO `.valor` mono 20px de
   nowrap+ellipsis dos números -- a mesma doença: frase de prosa herdando a
   folha do número corta/quebra feio. O remédio é o mesmo: a ausência sai do
   mono, vira inline-block com a folha da PROSA (nunca abaixo de 12px, D-322)
   e quebra normalmente -- e por ser inline-block ela cria a própria caixa de
   linha, então o `nowrap`/`ellipsis` do `.valor` ancestral deixa de mordê-la. */
.fluxo-resumo .valor .sem-valor {
  display: inline-block;
  font: 400 13px/1.35 var(--display); color: var(--tinta-2);
  white-space: normal; letter-spacing: 0;
  overflow: visible; text-overflow: clip;
}
/* DOBRA B3 (14/09/2026): mesma troca do `.kpi-contexto` -- em 834px "banco
   R$ 6,08 mi + fundo R$ 8…" cortava a segunda parcela. `.nb` (acima) marca
   cada parcela como indivisível; o container aceita a quebra ENTRE elas. */
.fluxo-resumo .contexto {
  font: 400 12px/1.4 var(--mono); color: var(--tinta-2); margin-top: 3px;
  white-space: normal; overflow-wrap: anywhere;
}
/* AS BARRAS DO SALDO NO GRÁFICO -- pequenas amostras retangulares na legenda
   (o traço em linha, `.legenda .traco`, já serve o acumulado; barra precisa
   de área, não de traço). */
.legenda .barra-legenda { width: 14px; height: 10px; display: inline-block; border-radius: 2px; }
.legenda .barra-legenda.ganho { background: var(--verde); }
.legenda .barra-legenda.perda { background: var(--vermelho); }

/* ================= PENDÊNCIAS ================= */
.pendencias {
  border: 1px solid var(--ambar-borda); background: var(--ambar-claro);
  border-left: 4px solid var(--ambar);
  border-radius: 0 var(--raio) var(--raio) 0; padding: 14px 18px;
}
.pendencias.erro { border-color: var(--vermelho-borda); border-left-color: var(--vermelho); background: var(--vermelho-claro); }
.pendencias h4 {
  display: flex; align-items: center; gap: 8px;
  margin: 0 0 8px; font: 700 12px var(--display);
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--ambar);
  --icone-cor: var(--ambar);
}
/* ÍCONE DE EXCLAMAÇÃO EM CSS — DESIGN-0. Nenhum ícone externo: o glifo é
   `content: "!"` num círculo. A cor do CÍRCULO vem de `--icone-cor` (custom
   property própria, não `currentColor`): o `::before` também declara `color`
   (o do glifo "!", branco), e `currentColor` leria O PRÓPRIO `color` do
   pseudo-elemento — não o do `h4` — se ele fosse usado para o fundo. */
.pendencias h4::before {
  content: "!"; flex-shrink: 0; width: 16px; height: 16px;
  display: inline-flex; align-items: center; justify-content: center;
  border-radius: 50%; background: var(--icone-cor); color: var(--color-surface);
  font: 700 11px var(--font-sans); letter-spacing: 0; text-transform: none;
}
.pendencias.erro h4 { color: var(--vermelho); --icone-cor: var(--vermelho); }
.pendencias ul { margin: 0; padding: 0; list-style: none; display: flex; flex-direction: column; gap: 5px; }
/* DIVISÓRIA VERTICAL POR ITEM — DESIGN-0: cada pendência ganha um fio à
   esquerda, o mesmo papel decorativo do `--regra-sub` nos cartões (§ subtópico),
   separando visualmente o item sem depender de gap nem de linha horizontal. */
.pendencias li { font-size: 13px; padding-left: 10px; border-left: 2px solid var(--color-border); }
.pendencias a { color: inherit; font-weight: 600; text-decoration: underline; text-underline-offset: 2px; }

/* ============================================================
   8 · NÚMEROS TABULARES
   ------------------------------------------------------------
   É tela de tabela financeira: número que dança entre linhas é
   ilegível. Declarado mesmo onde a fonte já é monoespaçada, e o
   motivo é o caso que quebra na mão do dono — se a webfont não
   carregar no iPad, o fallback do `--display` vira SF Pro, cujos
   algarismos são PROPORCIONAIS. A coluna desalinha sozinha, sem
   erro nenhum aparecer. Cinto e suspensório de um bloco.
   ============================================================ */
.emp-nums b, .emp-nums em,
.ver-vpl-val, .ver-criterio, .ver-quando,
.kpi-valor, .kpi-contexto, .kpi-delta,
.cf-cel .par b, .cf-cel .par i, .cf-cel .delta,
.inv-criterio, .inv-quem,
.inv-cel .val, .inv-cel .sub, .inv-fila .val, .inv-fila .sub,
.card .valor, .card .abaixo,
.comp-item .valor, .comp-sub b,
.tabela-resultado td, .tabela-resultado th,
.fluxo-wrap td, .fluxo-wrap th,
.celula-calc, .bloco-total, .ft-item b,
.medidor-soma, .medidor-legenda b, .medidor-legenda .delta,
.campo input, .tabela-edicao input, .tabela-edicao select,
.eco-mes, .ind-grav em {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* ================= RESPONSIVO ================= */
@media (max-width: 980px) {
  /* P-6 (DIURNA-1 item 4d, 13/09/2026) -- `align-items: stretch` e
     `.secoes { width: 100% }` SUBIRAM de 834 para 980 (D-076: uma casa só
     para o mesmo fato): a causa da rolagem horizontal em 835-980 é a mesma
     do 834 -- `.premissas-layout` em coluna com `align-items: flex-start`
     deixa `.secoes` (flex-item sem largura própria) encolher para o
     max-content dos filhos, e um card mais largo que o viewport puxa todos
     os outros junto. */
  .premissas-layout { flex-direction: column; align-items: stretch; }
  .secoes { width: 100%; }

  /* P-10 (NOTURNO-7 item 6b, 13/09/2026) -- o STEPPER COMPACTO também sobe
     para 980px, por ordem da Mesa: a coluna lateral do desktop virava
     fileira quebrada em 2-3 linhas entre 835 e 980, e o botão "seção atual"
     resolve nessa faixa a mesma rolagem que o resto do bloco já resolvia.
     Estas são as ÚNICAS regras que definem `.stepper` em ≤980px (D-076: uma
     casa só) -- elas vencem qualquer fileira que a tela tivesse antes aqui.
     A lista de dez `.passo` nunca duplica: `.stepper-lista` só troca de
     `display`, os dez continuam no DOM para quem lê (check_cenarios,
     check_cronograma). */
  .stepper {
    position: static; width: 100%; flex-direction: column; flex-wrap: nowrap;
    gap: 0; margin-bottom: 4px;
  }
  .stepper::before { display: none; }
  .stepper-atual {
    display: flex; align-items: center; justify-content: space-between; gap: 10px;
    width: 100%; padding: 12px 14px; min-height: 44px; box-sizing: border-box;
    border: 1px solid var(--linha); border-radius: var(--raio);
    background: var(--superficie); cursor: pointer; text-align: left;
    font: inherit; color: var(--tinta);
    touch-action: manipulation; -webkit-tap-highlight-color: transparent;
  }
  .stepper-atual-txt { display: flex; flex-direction: column; min-width: 0; line-height: 1.3; }
  .stepper-atual-txt b { font: 700 14px var(--display); }
  .stepper-atual-txt small { font: 500 11px var(--font-sans); color: var(--tinta-2); }
  .stepper-atual-seta { font-style: normal; color: var(--tinta-3); transition: transform 0.12s; }
  .stepper.aberto .stepper-atual-seta { transform: rotate(180deg); }
  /* fechado: só o botão fica de pé; a lista some (troca o `display: contents`
     do desktop por `none` — a expansão é quem devolve a lista). */
  .stepper-lista { display: none; }
  .stepper.aberto .stepper-lista {
    display: flex; flex-direction: column; gap: 3px; margin-top: 4px;
    border: 1px solid var(--linha); border-radius: var(--raio);
    background: var(--superficie); padding: 6px;
    max-height: 60vh; overflow-y: auto;
  }
}
/* ==================== COMPACTO IPAD 834 (NOTURNO-6 item 11, 12/09/2026;
   P-10, 13/09/2026, subiu o STEPPER para 980px) ====
   O STEPPER deixou de ser exclusivo do 834: ele mora agora no bloco
   `@media (max-width: 980px)` acima, na mesma casa do conserto do
   CONTÊINER (P-6) — as duas rolagens tinham a mesma causa e a Mesa mandou
   tratá-las juntas (D-076). Só ATÉ 834px — nada muda acima — ficam:
   (1) CAMPOS — dois por linha (a grade de 3-4 colunas do desktop não cabe em
   834), 16px de fonte (abaixo disso o Safari de iOS dá zoom automático ao
   focar) e 44px de altura de toque.
   (2) SENSIBILIDADE — tornado e matriz empilham, reusando `.sens-largo`, a
   MESMA classe que o JS já aplica quando a matriz tem pontos demais para
   caber lado a lado (`LIMIAR_PONTOS_LADO_A_LADO`) — nenhuma regra nova de
   layout, só o mesmo empilhamento por um motivo a mais. */
@media (max-width: 834px) {
  .grade, .grade-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .campo { min-height: 44px; }
  .campo input, .campo select { font-size: 16px; }

  .sens-lado-a-lado { grid-template-columns: 1fr; }
}
@media (max-width: 760px) {
  .pagina { padding: 22px 16px 110px; }
  .tj-in { padding: 0 16px; }
  .abas { padding-left: 16px; padding-right: 16px; margin-left: -16px; margin-right: -16px; }
  .faixa-in { padding-left: 16px; padding-right: 16px; gap: 14px; }
  .emp-nums { grid-template-columns: 1fr 1fr; }
  /* A EQUAÇÃO EMPILHA, E OS OPERADORES VIRAM RÉGUAS HORIZONTAIS. Numa coluna
     só, `1fr auto 1fr auto 1fr` espremeria o R$ até quebrar em duas linhas —
     e o "−" e o "=" precisam continuar entre os termos, senão a conta deixa
     de se ler como conta. Por isso a grade vira uma coluna e o operador vira
     uma faixa de altura própria, com o fio embaixo em vez de dos lados. */
  .inv-conta { grid-template-columns: 1fr; }
  .inv-cel { padding: 13px 20px 14px; }
  .inv-op {
    width: auto; justify-content: flex-start; padding: 0 20px 7px; font-size: 15px;
    border-left: 0; border-right: 0; border-bottom: 1px solid var(--linha);
  }
  /* quem cede à largura é a BUSCA, nunca o título do sistema: título é
     identificação e não legenda — ele aparece inteiro ou o conflito vai para a
     mesa. Some-lo aqui seria exatamente o "encolher em silêncio" que o
     `flex-shrink: 0` lá em cima existe para proibir. */
  .tj-busca { width: 140px; }
}

/* ---- CÉLULA DE GRUPO: o widget de RECEBIMENTO (Lote C.3) ----
   O dono escolheu a opção (b) da minuta: entrada · 1º mês · parcelas num BLOCO
   dentro de UMA célula, em vez de três colunas novas. A razão é de tela e não
   de gosto — `#tab-pers` iria de nove para onze colunas, e tabela que rola de
   lado num iPad é exatamente o que ele não quer. Com o grupo ela DESCEU para
   oito.

   Duas colunas internas, e é isso que mantém a linha única: os quatro campos
   ocupam dois pares em vez de uma fila. Dentro do grupo o cabeçalho da coluna
   deixa de nomear o campo, então cada um leva o próprio rótulo minúsculo. */
.celula-grupo {
  display: grid; grid-template-columns: repeat(2, minmax(74px, 1fr));
  gap: 6px 10px; min-width: 190px;
}
.celula-grupo .mini { display: block; min-width: 0; }
/* O RÓTULO É PREFIXO DENTRO DA CAIXA (conserto do F2, 23/08). Acima da caixa
   ele empurrava o input 13px para baixo, e o primeiro campo do grupo nascia
   ABAIXO do topo dos vizinhos de linha única — degrau medido em 2066 × 2079.
   Como prefixo, a caixa começa no topo da célula e o alinhamento vira
   ESTRUTURAL: não há offset a compensar, então não há o que se desajustar
   quando a fonte ou o zoom mudarem. */
.mini-rot {
  font: 600 8.5px var(--display); letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--tinta-3); padding-left: 7px; white-space: nowrap; flex: none;
}
.mini-campo {
  display: flex; align-items: center; gap: 3px;
  border: 1px solid var(--linha); border-radius: 6px;
  background: var(--superficie-2); overflow: hidden;
}
.mini-campo:focus-within { border-color: var(--verde); box-shadow: 0 0 0 2px rgba(14,107,73,0.12); }
.mini-campo input {
  border: 0 !important; background: transparent !important;
  padding: 6px 4px !important; width: 100%; min-width: 2.6em;
  font: 12px var(--mono); font-variant-numeric: tabular-nums; text-align: right;
}
/* ENUMERAÇÃO DENTRO DO GRUPO (pacote TIPOLOGIA). Ela é TEXTO e não número: fica
   à esquerda e na família de display, senão "Entrega da obra" sairia alinhado à
   direita e em mono, lido como valor medido. */
.mini-campo select {
  border: 0 !important; background: transparent !important;
  padding: 6px 4px !important; width: 100%; min-width: 0;
  font: 12px var(--display); text-align: left;
}
/* O CAMPO LARGO OCUPA AS DUAS COLUNAS do grupo. Num meio-lugar o chevron que o
   navegador desenha DENTRO da caixa come o texto da opção, que é o defeito já
   medido no select de produto do mix. `.mini-aviso` usa a mesma técnica. */
.celula-grupo .mini.mini-largo { grid-column: 1 / -1; }
/* O SUFIXO NUNCA ENCOLHE (F4, 23/08). Num flex sem `flex: none` ele e o
   primeiro a ser espremido, e o "%" da entrada sumia antes do valor — o
   usuario ficava vendo um numero sem unidade, que e pior do que ver menos
   numero. O input cede primeiro, e ele tem `min-width` proprio para nao ceder
   alem do que o valor precisa. */
.mini-campo i {
  font-style: normal; font-size: 12px; color: var(--tinta-3);
  padding-right: 7px; flex: none; white-space: nowrap;
}
/* O ECO fica ABAIXO da caixa e ocupa a largura inteira do mini — quem o poe
   ali e o `elementoDoEco`, ancorando no `.mini-campo`. Aqui so se garante que
   ele nao herde o `overflow: hidden` da caixa nem seja espremido. */
.celula-grupo .mini > .eco-mes, .celula-grupo .mini > .mini-eco { width: 100%; }
/* O ECO EM R$ DA ENTRADA e o eco MM/AA vivem no mesmo lugar e com a MESMA
   altura reservada da técnica única (D-208): a célula não muda de tamanho
   quando o eco aparece, e a linha da tabela não se mexe embaixo do dedo. */
.mini-eco, .celula-grupo .eco-mes {
  display: block; margin-top: 2px;
  font: 500 12px/14px var(--mono); font-style: normal;
  min-height: 14px; color: var(--tinta-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* MODO IRREGULAR — a lista de aportes não cabe no widget. Os campos ficam
   travados e o aviso diz O QUE HÁ e por onde se muda; campo desabilitado sem
   explicação é a tela decidindo no lugar do dono e não contando. */
.celula-grupo.irregular .mini { opacity: 0.45; }
.celula-grupo.irregular .mini-campo { background: var(--superficie); }
.mini-aviso {
  grid-column: 1 / -1; font: 500 10.5px/1.35 var(--display); font-style: normal;
  color: var(--ambar);
}

/* ---- F1 (23/08): O BOTÃOZINHO DO `type="number"` COMENDO O VALOR ----
   O dono viu "20" aparecer como "2(" no campo de % de desconto. Não era fonte
   nem largura de coluna sozinha: `input[type=number]` desenha as setinhas de
   incremento POR CIMA do conteúdo quando o campo é estreito, e num `<td>` de
   50px elas não sobra espaço para dois dígitos.

   MEDIDO, e é assim que se sabe que não é caso isolado: com o conteúdo MÁXIMO
   LEGÍTIMO de cada campo (não com o exemplo da foto), `scrollWidth >
   clientWidth` em `desconto_pct` (50 × 60), `valor_total` (85 × 92) e
   `descricao` (105 × 155).

   As setinhas saem SÓ nas tabelas. Nos campos da `.grade` elas cabem e são
   afordância legítima — tirar de lá seria consertar onde não dói. */
.tabela-edicao input[type="number"]::-webkit-outer-spin-button,
.tabela-edicao input[type="number"]::-webkit-inner-spin-button,
.mini-campo input[type="number"]::-webkit-outer-spin-button,
.mini-campo input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none; appearance: none; margin: 0;
}
.tabela-edicao input[type="number"], .mini-campo input[type="number"] {
  -moz-appearance: textfield; appearance: textfield;
}
/* E a largura mínima pelo CONTEÚDO MÁXIMO, para a coluna não poder ser
   espremida abaixo do que ela precisa mostrar. Número curto (até 3 dígitos:
   240 meses, 100%, 999 unidades) e coluna monetária têm pisos diferentes
   porque o conteúdo máximo é diferente. */
.tabela-edicao td > input[type="number"] { min-width: 62px; }
/* TELA-1, item 2a (07/09/2026) — QTD ENCOLHE NA PERMUTA FÍSICA. `quantidade`
   aqui é sempre um inteiro pequeno (3-4 dígitos bastam); MEDIDO no navegador
   real, o piso genérico de 62px mais o `width: 100%` da regra de cima
   deixavam esta caixa em 268-336px — mais larga que UNIDADE (o select do
   mix, que é quem precisa da largura para nomes longos) e que VALOR. O
   `max-width` aqui é TETO, não piso: não colide com a mutação 14 do
   `check_espelho_mix` (que mata um `min-width` de select em OUTRA
   superfície) nem com a proibição de dar `min-width` próprio a `#tab-pers`
   (D-303/B5) — este seletor é outro campo, outra tabela, outra propriedade. */
#tab-pf td > input[data-campo="quantidade"] { max-width: 64px; }
.tabela-edicao td > input[data-campo="valor_total"],
.tabela-edicao td > input[data-campo="valor"],
.tabela-edicao td > input[data-campo="preco_m2"] { min-width: 104px; }
/* `descricao` e `nome` são TEXTO LIVRE e não têm máximo — sempre poderão
   transbordar, e isso não é a mesma doença. O piso aqui só garante que o
   conteúdo CANÔNICO (o próprio placeholder) caiba. */
.tabela-edicao td > input[data-campo="descricao"] { min-width: 158px; }
/* TELA-1, item 2c (07/09/2026) — TENTATIVA REVERTIDA, e a razão fica escrita
   para não repetir o erro: um `min-width` menor aqui (130px) foi testado e
   DERRUBOU o `check_espelho_mix` (passo 7, "nenhum campo CORTA o próprio
   conteúdo máximo legítimo" — "descricao 129x155", cortando "Pacote
   investidor"). O comentário acima fala de PLACEHOLDER, mas o piso de 158px
   protege CONTEÚDO CANÔNICO real (F1), não só uma sugestão em cinza — a
   leitura de que "M2-5d matou a razão" estava errada. MEDIDO no navegador
   real (item 2c): com nomes de unidade longos ("Studio Garden Vista Mar")
   no select do mix, `#tab-pers` vaza ~18px do cartão e o botão de remover
   vaza ~14px além da borda — mas o pedido explícito da ordem (RECEBIMENTO
   cabe, nada corta, x visível) já estava satisfeito ANTES desta tentativa:
   `recebimentoCorta` todo `false` e `xBtnVisivel` `true` na medição B1.
   NOMEADO, NÃO CONSERTADO: encolher a coluna de descrição não é caminho
   seguro sem violar o F1; um conserto real pediria outra coluna (ou uma
   régua nova para o select do mix nesta tabela), fora do escopo desta
   fatia. */
/* Resíduo do F1 medido DEPOIS do conserto: ao dar piso aos numéricos, a
   competição por largura espremeu dois campos de TEXTO abaixo do conteúdo
   canônico deles — `tipologia` e `nome` do investidor. Texto livre não tem
   máximo e sempre poderá transbordar; o que o piso garante é que o conteúdo
   CANÔNICO caiba.

   RE-MEDIDO na VENDAS-1 (06/09/2026), no navegador real: com "Residencial" —
   o maior valor de tipologia dos 4 estudos do dono — o piso de 116px cortava
   (clientWidth 114 × scrollWidth 125; "Residencia" cortado na tela do dono é
   este mesmo defeito). O de `nome` com "1 quarto" — a maior unidade do banco
   — já cabia (94×94), mas no fio da navalha: zero folga é zero margem para
   variação de fonte entre máquinas.
   Os dois pisos sobem com folga medida, não no limite exato — e a folga foi
   contida de propósito: a largura 916px do container a 1400px de janela é
   EXATAMENTE a soma dos pisos de hoje mais 24px de sobra; gastar a sobra
   inteira zeraria a margem no layout mais comum. O piso sobe só o que a
   folga pede: tipologia 130px (clientWidth 128 contra scrollWidth 125,
   folga 3) e nome 100px (clientWidth 98 contra scrollWidth 94, folga 4) —
   6px de sobra ficam de pé a 1400px. */
.tabela-edicao td > input[data-campo="tipologia"] { min-width: 130px; }
.tabela-edicao td > input[data-campo="nome"] { min-width: 100px; }
/* A CÉLULA DA DINÂMICA DE VENDA (pacote TIPOLOGIA) pede mais que o piso geral
   do grupo: são até cinco campos em duas colunas, e o primeiro deles ocupa a
   linha inteira com rótulos por extenso ("Entrega da obra"). Com o piso de
   190px do grupo genérico a enumeração ficaria espremida contra o chevron. */
.celula-grupo[data-grupo="venda-linha"] { min-width: 216px; }
/* O GRUPO DE RECEBIMENTO (VENDAS-4, item 11) — MEDIDO no navegador real
   (Chrome, 1400×1000, `#tab-pers` com uma linha): o piso de 190px do grupo
   genérico deixava o mini "entrada" (rótulo + input + sufixo `%`) com
   scrollWidth 106 contra clientWidth 94 — 12px cortados, sempre, mesmo sem
   nenhum outro campo mais longo na linha. Os outros três minis ("no mês",
   "saldo", "do mês") já cabiam no piso genérico; só este precisava de mais
   sala, e o acréscimo é o mínimo medido para fechar a folga nos dois lados
   do grid de 2 colunas (12px ÷ 2 = 6px por coluna, arredondado para cima). */
.celula-grupo[data-grupo="recebimento"] { min-width: 260px; }
/* A COLUNA "UNIDADE" DE `#tab-venda-produto` (VENDAS-1) é SOMENTE LEITURA — o
   nome é do mix, não se edita aqui — e por isso não é `<input>`: é texto puro
   na célula, sem piso de largura próprio (o conteúdo é o mesmo nome que já
   coube na coluna `nome` do mix, 100px, então não há novo caso a medir). */
.tabela-edicao td.celula-nome-produto { font: 700 13.5px var(--display); color: var(--tinta); vertical-align: middle; }
/* CADA UNIDADE NUM CARTÃO PRÓPRIO (VENDAS-4, item 3) — `#tab-venda-produto`
   continua sendo `<table><tbody><tr>` por dentro (nenhum harness depende
   disto hoje, mas a estrutura não muda: `sincronizarVendaProduto`,
   `criarLinhaVendaProduto` e o vínculo por `data-uid` continuam lendo
   `tbody tr`). O CARTÃO é ROUPA sobre a mesma armação: fundo alternado por
   `nth-child` e `border-collapse: separate` com respiro entre linhas —
   sem isso um `<tr>` não tem como parecer um cartão, porque a borda é do
   navegador desenhada por CÉLULA, nunca por linha. */
#tab-venda-produto { border-collapse: separate; border-spacing: 0 8px; margin-top: 2px; }
#tab-venda-produto tbody tr td { background: var(--superficie); border-top: 1px solid var(--linha); border-bottom: 1px solid var(--linha); }
#tab-venda-produto tbody tr:nth-child(even) td { background: var(--superficie-2); }
#tab-venda-produto tbody tr td:first-child { border-left: 1px solid var(--linha); border-radius: 10px 0 0 10px; padding-left: 14px; }
#tab-venda-produto tbody tr td:last-child { border-right: 1px solid var(--linha); border-radius: 0 10px 10px 0; }
/* CAMPOS NA MESMA LINHA QUANDO COUBER (item 3) — o cartão tem mais largura
   que uma célula de tabela comum, então os quatro campos da dinâmica de
   venda (início · mês/% no início · distrato · zerar em) ganham 4 colunas em
   vez das 2 do piso genérico e passam a caber lado a lado numa linha só.
   `auto-fit`/`auto-fill` FICOU DE FORA DE PROPÓSITO: dentro de um `<td>` de
   tabela `auto` o tamanho do container é circular (a coluna do grid tenta
   ler a largura da célula, e a célula tenta ler a largura do grid) — medido
   no navegador real, o resultado é instável. `repeat(4, ...)` é fixo e
   previsível, e o `min-width: 0` deixa o `<td>` livre para crescer até a
   largura do cartão (a borda vem do `td:first-child`/`:last-child` acima,
   não deste min-width). */
#tab-venda-produto .celula-grupo[data-grupo="venda-linha"] {
  grid-template-columns: repeat(4, minmax(90px, 1fr)); min-width: 0;
}

/* ---- PISO DO SELECT DE PRODUTO (opção A do adendo de 23/08) ----
   O dono viu "Studio" cortado como "Stud:". A causa NÃO era a largura do texto:
   é o CHEVRON, que o navegador desenha DENTRO da caixa e que padding nenhum
   declara. Medir o texto contra o `clientWidth` dizia "cabe" com o defeito na
   tela dele; a medida honesta é um CLONE em `width: max-content`, que devolve
   texto MAIS adorno.

   O QUE CADA NOME PEDE, medido assim:
       "Studio"            60px      "Residencial" (tipologia real)   91px
       "1 quarto"          71px      "3 quartos suite"               110px
   E o que a coluna dava, sem piso: 87px a 1400, 117 a 1024 e 62 a 810 — ou
   seja, no iPad retrato o mix REAL do dono já não cabia.

   O PISO É 100px E NÃO MAIS, e o limite é medido: com 120 a tabela ESTOURA o
   iPad retrato (827 contra 808 disponíveis). Ceder da descrição para passar de
   100 faz a DESCRIÇÃO cortar o próprio placeholder — troca de defeito por
   defeito. As duas colunas são texto livre e já estão no mínimo canônico.

   ⚠ O GATILHO DISPAROU (VENDAS-4b, R2), e é a metade que faltava medir aqui:
   até esta fatia ninguém tinha clonado a opção VAZIA, só os NOMES do mix. Em
   `#tab-pers` o select também mostra "— sem produto (valor livre) —" (o
   caminho de volta do modo vinculado, T1.3) e, sem mix cadastrado, "—
   cadastre o mix primeiro —" — as duas são MENSAGEM DO SISTEMA, não nome de
   produto, e `sel.title` fica VAZIO para elas (`sel.title = sel.value || ""`,
   e o value da opção vazia É vazio): a doutrina "o transbordo do nome
   EXTREMO é coberto pelo `title`" não alcançava as duas, porque não havia
   title nenhum cobrindo. Medido por CLONE: as frases LONGAS pediam 200px e
   195px — bem acima do piso, e SUBIR O PISO PARA 200 FOI TENTADO E REVERTIDO:
   ele só pode crescer pela MESMA regra que `#tab-pf` usa (não há seletor mais
   específico que não vença por ESPECIFICIDADE e blinde `#tab-pers` da
   mutação 14 do `check_espelho_mix`, que mata este `min-width` de propósito
   para provar que "Residencial" pára de caber — um piso paralelo despertence
   dessa prova e a mutação nasce MUDA, D-296(7) do lado do CSS). **A SAÍDA FOI
   ENCURTAR A FRASE, não alargar a caixa**: "— sem produto —" virou
   "— livre —" (81px) e "— cadastre o mix primeiro —" virou "Sem mix" (74px)
   — as duas cabem com folga no piso de 100px que já existe, sem tocar nesta
   regra nem no `check_espelho_mix` (fora do escopo desta fatia). */
.tabela-edicao select[data-papel="unidade-mix"] { min-width: 100px; }


/* ============================================================
   COMPARAR (COMP-2, D-370; DESIGN-5, sobre a prancha 13/mockup do Codex) --
   dois lados, estudo vivo ou versão. A aba não calcula nada: tudo aqui é
   FORMATAÇÃO do que `POST /api/comparar` e `GET /api/projetos/{id}/versoes`
   devolvem prontos. As cores A/B seguem os tokens `--color-primary` (A) e
   `--color-compare` (B). Cabeçalho de seção reusa o `.res-sec-cab` numerado
   da casa (mesmo do Resultado) -- "Configurações" morreu, e com ela os
   blocos preto/âmbar/vermelho que coloriam cada `<h3>`.
   ============================================================ */
.cmp-topo { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 10px; flex-wrap: wrap; }
.cmp-barras { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; flex: 1 1 520px; min-width: 320px; }
/* A BARRA É O ALVO DE CLIQUE INTEIRO: o `<select>` (ver o ID logo abaixo)
   cobre toda a área em opacidade zero -- clicar em qualquer ponto, ou no
   chevron, abre a MESMA lista nativa que ele sempre abriu. */
.cmp-barra {
  position: relative; display: flex; align-items: center; gap: 10px;
  padding: 10px 34px 10px 12px; border-radius: var(--r-2);
  color: var(--doma-branco); overflow: hidden; cursor: pointer;
}
.cmp-barra-a { background: var(--color-primary); }
.cmp-barra-b { background: var(--color-compare); }
.cmp-circulo {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border-radius: 50%; flex: 0 0 auto;
  font: 700 13px var(--display); background: rgba(255,255,255,0.16);
  border: 2px solid var(--doma-branco); box-sizing: border-box;
}
.cmp-barra-txt { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.cmp-barra-sub {
  font: 700 10px var(--display); letter-spacing: 0.08em; text-transform: uppercase;
  color: rgba(255,255,255,0.8);
}
.cmp-barra-nome {
  display: block; font: 600 14px var(--display); text-transform: uppercase;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
/* NOTA ÂMBAR DE DRIFT (item 1 da ordem) -- frase inteira, nunca só a cor.
   `--ambar` já é borda/ícone/fundo-soft por regra geral (D-380); aqui ela
   pinta o TEXTO por cima do fundo escuro da própria barra, onde a régua de
   contraste de "nunca texto" não se aplica (ela é sobre fundo claro). */
.cmp-barra-drift {
  display: block; margin-top: 2px; font: 700 10.5px var(--display);
  color: var(--ambar); white-space: normal;
}
.cmp-chevron { font: 700 18px var(--display); opacity: 0.85; flex: 0 0 auto; line-height: 1; }
/* O `<select>` original fica INTOCADO no HTML (`#cmp-sel-a` é alvo de
   mutação literal em `check_placeholder.mjs`, m6b) -- o estilo mora só no
   ID, nunca numa classe nova no elemento. */
.cmp-barra .campo { position: absolute; inset: 0; margin: 0; border: 0; background: transparent; }
#cmp-sel-a, #cmp-sel-b {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; cursor: pointer; border: 0; padding: 0;
}
.cmp-acoes { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }

/* 60/40 (item 2 da ordem): 01. Resultado à esquerda, 02. Premissas que
   divergem à direita -- as duas nascem com a MESMA ALTURA disponível
   (align-items: start já é o padrão de grid; cada cartão cresce pelo
   próprio conteúdo). */
/* VAR-1 item c (14/09/2026): `60% 40%` + `gap: 20px` estourava o container em
   exatos 20px (medido: 1144px de grade contra 1124px de caixa, em 1720px de
   janela) -- percentual de coluna do Grid NÃO desconta o gap do espaço
   disponível (só `fr` desconta), então o total virava 100% + o gap inteiro.
   O sintoma era os valores A/B de "Exposição máxima" encostando um no outro.
   `fr` mantém a proporção 60/40 (3:2) e resolve o gap antes de repartir o
   espaço; `minmax(0, …)` evita o member ficarem maior que o conteúdo mínimo
   (blowout de grid). */
.cmp-grade { display: grid; grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); gap: 20px; align-items: start; }
/* ITEM 4b da DESIGN-8: 60/40 só de fato lado a lado -- abaixo de 1500px a
   coluna de 40% (Premissas que divergem) fica estreita demais para o nome de
   parâmetro mais longo do acordeão sem quebrar em excesso; empilha antes
   disso incomodar. */
@media (max-width: 1500px) { .cmp-grade { grid-template-columns: 1fr; } }
.cmp-grade .cmp-bloco { margin-top: 0; }

.cmp-bloco { margin-top: 20px; }
/* CABEÇALHO NUMERADO DA CASA (item 2 da ordem, `.res-sec-cab`, ver acima) --
   BLEED até a borda do `.cartao` (o mesmo truque de margem negativa que os
   antigos `.cmp-bloco h3` já faziam), sem herdar a margem superior de 26px
   pensada para viver solta dentro de `.painel`. */
.cmp-bloco .res-sec-cab { margin: -26px -28px 20px; padding: 13px 28px; border-radius: var(--raio) var(--raio) 0 0; }

/* PLACAR (item 2 da ordem): "A ganha em n de m · B ganha em n de m · Sem
   vencedor geral" -- ACIMA da tabela, nunca no rodapé. */
.cmp-placar {
  display: flex; flex-wrap: wrap; gap: 6px 14px; align-items: baseline;
  margin: 0 0 14px; font: 600 13px var(--display); color: var(--tinta-2);
}

/* DOBRA B1 (14/09/2026) -- ERRATA NO MESMO DIA: o `min-width: 820px` da
   primeira tentativa CRIAVA a rolagem que ele deveria só permitir -- numa
   faixa de 604px o wrap passava a rolar 216px por padrão, cortando a
   coluna Δ ("−R$ 3.7…") e tirando o badge VENCEDOR da vista. O conserto
   certo não pede piso nenhum: REDISTRIBUI o colgroup (22/20/20/20/18 em vez
   de 24/18/18/22/18) -- A/B sobem para 20% (≈121px a 604px, contra 109px do
   maior valor do sistema, gap ≈12px) sem tirar espaço de mais ninguém. O
   `.cmp-tabela-wrap` fica como rede de segurança (`overflow-x:auto`), mas
   não é mais ELE quem decide a largura da tabela. */
.cmp-tabela-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
/* ---- 01. Resultado: colunas, zebra, delta com barra e badge fixo ---- */
.cmp-tabela { table-layout: fixed; }
/* LARGURAS DO COLGROUP (item 4a da DESIGN-8, redistribuídas na DOBRA B1
   final): o seletor é `col`, nunca `.cmp-col-a`/`.cmp-col-b` sozinhos --
   essas classes já nomeiam TD/TH para tingir a célula, e uma regra de
   largura ali vazaria para acordeão e card que reusam o mesmo nome de
   classe. A coluna do vencedor guarda 18% -- o badge fixo de 96px cabe
   com folga (18% de 604px ≈ 109px, contra 96px+padding do badge). */
#tab-cmp-indicadores col.cmp-col-nome  { width: 22%; }
#tab-cmp-indicadores col.cmp-col-a     { width: 20%; }
#tab-cmp-indicadores col.cmp-col-b     { width: 20%; }
#tab-cmp-indicadores col.cmp-col-delta { width: 20%; }
#tab-cmp-indicadores col.cmp-col-venc  { width: 18%; }
.cmp-tabela td.cmp-col-a, .cmp-tabela th.cmp-col-a { background: var(--color-primary-soft); color: var(--color-primary); }
.cmp-tabela td.cmp-col-b, .cmp-tabela th.cmp-col-b { background: var(--color-compare-soft); color: var(--info-escuro); }
.cmp-tabela-indicadores th.cmp-col-a, .cmp-tabela-indicadores th.cmp-col-b,
.cmp-tabela-indicadores td.cmp-col-a, .cmp-tabela-indicadores td.cmp-col-b { text-align: center; }
.cmp-tabela-indicadores tbody tr:nth-child(odd) td:not(.cmp-col-a):not(.cmp-col-b) { background: var(--superficie-2); }
/* CÉLULAS NUMÉRICAS NUNCA QUEBRAM LINHA (item 2 da ordem/MEDIÇÃO): a coluna
   nasce larga o bastante (colgroup no HTML) para o maior valor do sistema
   (R$ com milhões + separador de milhar) sem embrulhar. A AUSÊNCIA
   (`.sem-valor`, motivo do motor) quebra linha e NUNCA corta com
   reticências -- D-187/D-224, ausência é frase com a causa e a frase não se
   mutila. */
.cmp-tabela-indicadores td.cmp-col-a, .cmp-tabela-indicadores td.cmp-col-b {
  white-space: nowrap;
}
.cmp-tabela-indicadores td.cmp-col-a.sem-valor, .cmp-tabela-indicadores td.cmp-col-b.sem-valor {
  white-space: normal; text-align: left; font-size: 12px; color: var(--tinta-2);
}
/* O CONTEXTO SEM DIREÇÃO do custo do dinheiro (item 2 da ordem: o R$ some
   sob a taxa, "R$ x · sem direção") -- FONTE NUMÉRICA NUNCA ABAIXO DE 12px
   (regra da Mesa), então 12px é o piso, não 11. */
.cmp-celula-nota { display: block; margin-top: 2px; font: 500 12px var(--mono); color: var(--tinta-2); white-space: normal; }
.cmp-tabela-indicadores td.cmp-col-venc { white-space: normal; overflow-wrap: anywhere; text-align: center; }
/* BLOCK, não flex: a coluna é estreita e o nome do indicador quebra em duas
   ou três linhas -- flex-row com um item de texto que quebra empurra o
   ícone "i" para fora do fluxo (ele "cai" abaixo do texto, longe da última
   palavra). Fluxo normal deixa o "i" colar na última palavra, como o
   `.dica-i` já faz em todo o resto do sistema. */
.cmp-indicador-nome { display: block; font-weight: 600; }
.cmp-indicador-nome .dica-i { margin-left: 3px; vertical-align: 1px; }
.cmp-indicador-def {
  display: block; margin-top: 1px; font-size: 11px; font-weight: 400;
  color: var(--tinta-2); white-space: normal;
}
/* BARRA VERTICAL DE 3px À ESQUERDA DE CADA LINHA (item 2 da ordem), na cor
   de quem venceu -- cinza (a borda comum da tabela) sem vencedor. `box-
   shadow` inset, não `border-left`: borda de verdade somaria à largura da
   linha e nenhuma outra célula participa do cálculo de largura por causa
   dela. */
.cmp-tabela-indicadores tbody tr { box-shadow: inset 3px 0 0 0 var(--linha); }
.cmp-tabela-indicadores tbody tr.cmp-linha-a { box-shadow: inset 3px 0 0 0 var(--verde); }
.cmp-tabela-indicadores tbody tr.cmp-linha-b { box-shadow: inset 3px 0 0 0 var(--info); }
/* BADGE FIXO 96×28 (item 2 da ordem, `.doma-vencedor` do mockup): NUNCA um
   raio circular que "sobra" -- a caixa é a MESMA em toda linha, com ou sem
   vencedor, para o olho não ler "célula maior = juízo mais forte". Reusado
   pelo rodapé dos cards de "03. Quanto aguenta". */
.cmp-badge-venc {
  display: inline-flex; align-items: center; justify-content: center;
  box-sizing: border-box; width: 96px; min-width: 96px; height: 28px;
  border: 1px solid var(--linha); border-radius: var(--r-1);
  background: var(--superficie-2); color: var(--tinta-2);
  font: 700 11px var(--display); text-align: center; line-height: 1.15;
  white-space: normal;
}
.cmp-badge-venc.a { color: var(--verde-escuro); background: var(--verde-claro); border-color: var(--verde); }
.cmp-badge-venc.b { color: var(--info-escuro); background: var(--color-compare-soft); border-color: var(--info); }
.cmp-badge-venc.contexto { border-style: dashed; }
/* DELTA (item 2 da ordem): valor + % + barra grafite curta de escala LOCAL
   ao par -- nunca uma escala entre indicadores diferentes. */
.cmp-delta-valor { display: block; font: 600 13px var(--mono); color: var(--tinta); }
.cmp-delta-pct { display: block; margin-top: 1px; font: 500 12px var(--mono); color: var(--tinta-2); }
.cmp-delta-ausente { font-size: 11.5px; color: var(--tinta-2); white-space: normal; }
.cmp-delta-trilho { height: 4px; border-radius: 2px; background: var(--superficie-2); margin-top: 5px; overflow: hidden; }
.cmp-delta-fill { height: 100%; border-radius: 2px; background: var(--tinta-2); }
.cmp-rodape { margin-top: 10px; font: 500 12px var(--mono); color: var(--tinta-2); }

/* ---- 02. Premissas que divergem: acordeões por bloco ---- */
/* ITEM 4g da DESIGN-8 (D-380): `--color-warning` é "borda/ícone/fundo-soft,
   nunca texto" por regra geral -- a Mesa pediu neste título mesmo assim, e o
   contraste MEDIDO (#B7791F sobre #FFFFFF, fórmula WCAG) é 3,64:1. Passa o
   piso de 3:1 (texto grande/componente de UI); fica abaixo do piso de 4,5:1
   de texto normal de 15px -- exceção nomeada, não regra nova. */
.cmp-divergem-titulo { font: 700 15px var(--mono); color: var(--color-warning); }
.cmp-ponte { margin: 0 0 14px; }
.cmp-ponte a { margin-left: 4px; font-weight: 600; color: var(--color-primary); }
.cmp-acordeao { border-top: 1px solid var(--linha); }
.cmp-acordeao:last-child { border-bottom: 1px solid var(--linha); }
.cmp-acordeao-cab {
  width: 100%; display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 10px 2px; border: 0; background: transparent;
  font: 600 13px var(--display); color: var(--tinta); cursor: pointer; text-align: left;
}
.cmp-acordeao-cab .cmp-acordeao-nome { flex: 1 1 auto; min-width: 100px; }
/* OS VALORES A/B NO CABEÇALHO (item 3 da ordem) -- FONTE NUMÉRICA NUNCA
   ABAIXO DE 12px, e a mesma largura para A e B (senão a leitura vertical
   compara larguras em vez de valores). */
.cmp-acordeao-valores { display: flex; gap: 10px; flex: 0 0 auto; }
.cmp-acordeao-valores .cmp-col-a, .cmp-acordeao-valores .cmp-col-b {
  min-width: 90px; padding: 2px 8px; border-radius: 4px; text-align: right;
  font: 600 12px var(--mono);
}
.cmp-acordeao-cab .cmp-acordeao-contagem { font: 600 12px var(--mono); color: var(--tinta-2); white-space: nowrap; flex: 0 0 auto; }
.cmp-linha-simples {
  display: flex; align-items: center; gap: 10px; padding: 8px 2px; font-size: 13px;
}
.cmp-linha-simples .cmp-linha-nome { flex: 1; min-width: 0; }
/* VAR-1 item c, DOBRA B2 (14/09/2026): `flex-shrink: 0` -- com o container
   espremido é o RÓTULO (`.cmp-linha-nome`, já `min-width: 0`) que cede
   espaço primeiro, nunca os valores A/B. `white-space: nowrap` FOI
   REVOGADO aqui: esta classe também serve linhas de CURVA (a série inteira
   de uma safra/obra divergente, sem espaço nenhum entre os números), e
   forçar uma linha só empurrava a PÁGINA para 4940px de largura -- o
   defeito que a regra existia para evitar, só que na direção oposta.
   `overflow-wrap: anywhere` deixa o valor curto (a maioria dos casos)
   numa linha só por não ter onde quebrar, e quebra a curva DENTRO da
   própria caixa de 30% quando ela não é uma linha só. */
.cmp-linha-simples .cmp-col-a, .cmp-linha-simples .cmp-col-b {
  flex: 0 0 30%; padding: 3px 8px; border-radius: 4px; font-size: 12.5px;
  overflow-wrap: anywhere; flex-shrink: 0;
}
/* 1g (DIURNA-8, 16/09/2026) — O DETALHE DO VETOR, fechado abaixo do resumo
   (contrato de `PremissaDivergente.a_detalhe`/`b_detalhe`, `app/schemas.py`).
   Serve as TRÊS formas de linha do Comparar (folha, bloco, filho) — ver
   `detalheDoVetor` no app.js. Nenhuma cor nova: o Comparar reserva azul só
   para o lado B (`.cmp-col-b` dentro de `.cmp-tabela`), e este bloco não
   introduz um segundo uso da regra. */
.cmp-detalhe-vetor { width: 100%; margin: 6px 0 2px; }
.cmp-detalhe-vetor summary {
  cursor: pointer; font: 500 12px var(--mono); color: var(--tinta-2);
}
.cmp-detalhe-vetor-linha { display: flex; gap: 10px; margin-top: 6px; }
.cmp-detalhe-vetor-linha .cmp-col-a, .cmp-detalhe-vetor-linha .cmp-col-b {
  flex: 1 1 50%; padding: 6px 8px; border-radius: 4px; background: var(--superficie-2);
  font: 500 12px var(--mono); white-space: normal; overflow-wrap: anywhere;
}
.cmp-acordeao-corpo td .cmp-detalhe-vetor { margin: 2px 0; }
.cmp-acordeao-corpo { background: var(--superficie-2); padding: 4px 6px 10px; }
.cmp-acordeao-corpo table { width: 100%; table-layout: fixed; border-collapse: collapse; }
.cmp-acordeao-corpo th, .cmp-acordeao-corpo td { padding: 6px 8px; font-size: 12.5px; text-align: left; }
.cmp-acordeao-corpo th.cmp-col-a, .cmp-acordeao-corpo td.cmp-col-a { width: 32%; }
.cmp-acordeao-corpo th.cmp-col-b, .cmp-acordeao-corpo td.cmp-col-b { width: 32%; }

/* ---- 03. Quanto aguenta: cards enxutos, um badge fixo no rodapé ---- */
.cmp-aguenta-intro { margin: 0 0 12px; }
.cmp-aguenta-ladoausente {
  margin: 0 0 10px; padding: 8px 12px; border-radius: var(--r-2);
  border-left: 3px solid var(--linha); background: var(--superficie-2);
  font-size: 12.5px; color: var(--tinta-2);
}
.cmp-aguenta-ladoausente.a { border-left-color: var(--verde); }
.cmp-aguenta-ladoausente.b { border-left-color: var(--info); }
.cmp-cards-aguenta { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; }
@media (max-width: 1100px) { .cmp-cards-aguenta { grid-template-columns: repeat(2, 1fr); } }
.cmp-card-aguenta { border: 1px solid var(--linha); border-radius: var(--r-2); padding: 14px 16px; background: var(--superficie); }
.cmp-card-aguenta h4 { margin: 0 0 2px; font: 700 13px var(--display); color: var(--tinta); }
.cmp-card-aguenta .cmp-pergunta { margin: 0 0 12px; font-size: 11.5px; color: var(--tinta-2); }
.cmp-aguenta-par { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.cmp-aguenta-linha {
  display: flex; flex-direction: column; gap: 4px; padding-top: 9px;
  border-top: 2px solid var(--linha);
}
.cmp-aguenta-linha.a { border-top-color: var(--verde); }
.cmp-aguenta-linha.b { border-top-color: var(--info); }
.cmp-aguenta-rotulo { font: 700 10px var(--display); letter-spacing: 0.05em; color: var(--tinta-2); }
.cmp-aguenta-linha.a .cmp-aguenta-rotulo { color: var(--verde-escuro); }
.cmp-aguenta-linha.b .cmp-aguenta-rotulo { color: var(--info-escuro); }
.cmp-aguenta-valor { display: block; font: 600 14px var(--mono); color: var(--tinta); text-align: right; }
.cmp-aguenta-ausente { display: block; font-size: 11.5px; color: var(--tinta-2); text-align: right; }
.cmp-aguenta-melhor { align-self: flex-end; font: 700 10px var(--display); letter-spacing: 0.04em; color: var(--tinta-2); text-transform: uppercase; }
/* PINTURA POR ALAVANCA (D-390): o `melhor` do motor, nunca aritmética da
   tela -- a linha vencedora ganha valor mais forte. Empate marca as duas
   sem reivindicar vencedor. */
.cmp-aguenta-linha.cmp-vencedor .cmp-aguenta-valor { font-weight: 700; }
.cmp-aguenta-rodape { display: flex; justify-content: flex-end; margin-top: 14px; }

.cmp-expandir {
  border: 0; background: transparent; padding: 0; font: inherit; color: inherit;
  cursor: pointer; text-align: left;
}
.cmp-expandir:hover { text-decoration: underline; }

/* ---- 04. Versões (bloco novo da DESIGN-5) ---- */
.cmp-versoes-topo {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; margin-bottom: 12px;
}
.cmp-versoes-topo .nota { margin: 0; max-width: 60ch; }
#tab-cmp-versoes th, #tab-cmp-versoes td { padding: 8px 10px; font-size: 12.5px; }
.cmp-versoes-atual { font-weight: 700; color: var(--tinta); }
.cmp-versoes-drift { color: var(--color-warning); font-weight: 600; }

/* ---- painel de versões (T5; DESIGN-5: modal centrado -> FLUTUANTE) ----
   O `#modal-versoes` continua sendo `.fundo-modal`, byte a byte, no HTML --
   ele é alvo de mutação literal em `check_placeholder.mjs` (m4). A troca de
   um modal centrado com backdrop para um painel flutuante no canto inferior
   direito é só CSS, pelo ID: o fundo fica transparente e sem captura de
   clique (`pointer-events: none`), então o resto da tela continua operável
   por trás dele -- diferente de um modal de verdade, que bloqueia. `[hidden]
   { display: none !important }` (§0 da folha) segue valendo por cima disto
   quando o painel está fechado. */
#modal-versoes {
  background: transparent; pointer-events: none;
  display: block; place-items: unset; padding: 0;
}
#modal-versoes .modal {
  pointer-events: auto; position: fixed; right: 24px; bottom: 96px;
  margin: 0; width: min(360px, calc(100vw - 48px));
  max-height: min(70vh, 620px); display: flex; flex-direction: column;
}
#modal-versoes .modal-corpo { overflow-y: auto; flex: 1; min-height: 0; }
#versoes-lado-atual { margin: 2px 0 0; color: var(--tinta-2); }
.cmp-info-verde {
  margin: 0; padding: 10px 12px; border-radius: var(--r-2);
  background: var(--verde-claro); color: var(--verde-escuro); font-size: 12px;
}
.versoes-lista-tit {
  margin: 6px 0 0; font: 700 10px var(--display); letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--tinta-2);
}
.versao-atual-tag {
  font: 700 9px var(--display); letter-spacing: 0.05em; text-transform: uppercase;
  padding: 1px 6px; border-radius: 10px; background: var(--verde-claro);
  color: var(--verde-escuro); white-space: nowrap;
}

/* ---- modal de versões: lista com toggle (T5) ---- */
#modal-versoes .modal-largo, .modal-largo { width: min(560px, 100%); }
#versoes-lista { display: flex; flex-direction: column; gap: 2px; margin-top: 6px; max-height: 46vh; overflow-y: auto; }
.versao-linha { border-top: 1px solid var(--linha); padding: 10px 0; }
.versao-linha:first-child { border-top: 0; }
.versao-cab { display: flex; align-items: center; gap: 10px; }
.versao-cab .cmp-expandir { flex: 1; font: 600 13px var(--display); color: var(--tinta); }
.versao-data { font: 500 12px var(--mono); color: var(--tinta-2); white-space: nowrap; }
.versao-detalhe { margin: 6px 0 0 0; }
