/* =====================================================================
   AZTV Family — Backoffice · Design System (app.css)
   Paleta e tratamento extraídos do dashboard atual (dashboard.php):
   fundo #050505, superfícies em white-alpha, hairlines, pesos leves,
   laranja #FF4D00 com texto preto nos botões, paleta de apoio viva e
   cor por plano (Mensal=azul, Tri=roxo, Sem=amber, Anual=laranja).
   Números em JetBrains Mono / tabular-nums. Sem sombras pesadas.
   ===================================================================== */

/* ------------------------------------------------------------------ */
/* 1. TOKENS (idênticos ao sistema atual + extensões do backoffice)   */
/* ------------------------------------------------------------------ */
:root {
  /* ⭐⭐ C241e A1 — era #050505. O espécime tem a PÁGINA em #0b0b0b e o cartão SEM fundo próprio;
     o cartão é desenhado só pelo anel de 1px. Subir esta linha sem a A3 levaria o cartão a
     #111111 e o problema continuaria um degrau acima — as duas andam juntas.
     ⛔ O tema claro NÃO se move: o `[data-theme="light"]` redefine --bg e vence por ordem. */
  --bg: #0b0b0b;
  --surface-1: rgba(255,255,255,.022);
  --surface-2: rgba(255,255,255,.035);
  --surface-3: rgba(255,255,255,.05);
  --elevated: #1a1a19;                 /* opaco: tooltips, dropdowns, toast, menus — C241: --flutuante do espécime */

  --border-1: rgba(255,255,255,.06);
  --border-2: rgba(255,255,255,.10);
  --border-hover: rgba(255,255,255,.20);

  --text-1: #f0efec;
  --text-2: #c3c2b7;
  --text-3: #898781;   /* C241: era rgba(255,255,255,.46). A guarda de AA CONTINUA honrada e foi REMEDIDA:
                          5,4107:1 sobre --bg #050505 · 4,8488:1 sobre --elevated #1a1a19 — os dois ≥ 4,5. */
  --text-4: rgba(255,255,255,.40);   /* subido de .22 → tier NÃO-TEXTO (ícone/grip/placeholder): 1.85→3.71:1, AA de UI 3:1 */

  --brand: #FF4D00;
  --brand-hover: #FF7818;
  --brand-soft: rgba(255,77,0,.10);
  --brand-border: rgba(255,77,0,.25);

  /* C241 §3.3 — os pares TINT do Console. ⛔ Os `-border` de green/red/amber MANTIVERAM o valor de hoje:
     medido no §3.3.a, eles também vivem em CAIXA (.alertbar, .alert-toast, .stockcard, .btn.is-copied,
     .cms-vazio--secao, .projcell--hero), e tirar borda de caixa é outra decisão. Só blue e purple, que
     têm 1 consumidor cada e ele é chip, foram a transparent. */
  --green: #55bf50;  --green-soft: #11260f;  --green-border: rgba(74,222,128,.22);
  --red: #ec7e7e;    --red-soft: #3c0e0e;    --red-border: rgba(248,113,113,.22);
  --blue: #6da7ec;   --blue-soft: #032042;   --blue-border: transparent;   /* 1 consumidor, e é chip */
  --purple: #a096eb; --purple-soft: #1d1649; --purple-border: transparent; /* 1 consumidor, e é chip */
  --amber: #db9300;  --amber-soft: #311a00;  --amber-border: rgba(251,191,36,.25);
  --whatsapp: #25D366;
  --whatsapp-soft: rgba(37,211,102,.10);

  /* --- tokens extraídos de valores fixos (DARK mantém exatamente o valor que estava inline) --- */
  --on-brand: #000;                      /* texto sobre o laranja (botão/logo) — preto nos 2 temas */
  --brand-ink: #FF4D00;                  /* laranja de TEXTO/ícone (no light escurece p/ passar AA) */
  --ring: var(--brand-ink);              /* cor do anel de foco (:focus-visible) — segue o brand-ink em cada tema */
  --brand-selection: rgba(255,77,0,.3);
  --field-bg: rgba(255,255,255,.04);     /* input, badge, pill.flat, segmented, input-group, cell-input:focus */
  --shimmer: rgba(255,255,255,.05);      /* skeleton */
  --tooltip-title-bg: rgba(255,255,255,.03);
  --overlay: rgba(0,0,0,.6);             /* scrim do menu mobile */
  --overlay-2: rgba(0,0,0,.55);          /* scrim do drawer */
  --track: rgba(255,255,255,.06);        /* trilho da .bar */
  --track-alt: rgba(255,255,255,.08);    /* quadradinhos do RFM */
  --on-cohort: #fff;                     /* texto sobre a célula do cohort (fundo sempre saturado) */
  --shadow-1: rgba(0,0,0,.22);
  --shadow-2: rgba(0,0,0,.3);
  --shadow-3: rgba(0,0,0,.45);
  --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' fill='rgba(255,255,255,.4)' viewBox='0 0 16 16'%3E%3Cpath d='M8 11L3 6h10z'/%3E%3C/svg%3E");

  /* --- paleta das SÉRIES dos gráficos (separada do texto: séries só precisam 3:1, texto precisa 4.5:1).
         DARK = os mesmos valores de hoje -> gráficos idênticos. LIGHT = paleta validada (CVD 9.2 / normal 19.8). --- */
  --chart-brand: #FF4D00;
  --chart-blue: #60a5fa;
  --chart-purple: #a78bfa;
  --chart-amber: #fbbf24;
  --chart-green: #4ade80;
  --chart-red: #f87171;

  --radius-card: 12px;
  --radius-input: 8px;
  --radius-pill: 5px;   /* C241 §4.3 — era 6px. Os 2 consumidores (.pill, .badge) sao CHIP, e o
                           especime mede 5. ⛔ O `--radius-pill` do site.css continua 6: e outro arquivo. */

  --ease: cubic-bezier(0.2,0,0,1);
  /* ⭐⭐ C241 — Manrope, por decisão do Matheus (12/09): "a fonte padrão é a Manrope agora".
     ⛔ Era 'Inter'. A fonte do painel deixou de ser white-label junto com a cor: o `head.php` não
       chama mais `Tema::fontHref()`, então este token é a ÚNICA fonte da verdade. O espécime usa
       a mesma família (`--sans:'Manrope',…`, painel-claude-ui.html:64). ⛔ O SITE não muda. */
  --font-sans: 'Manrope', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-mono: 'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, monospace;

  --sidebar-w: 248px;
  --topbar-h: 60px;

  --form-scheme: dark;   /* color-scheme dos inputs/selects nativos (calendário, spinner, dropdown) — segue o tema */
  color-scheme: dark;
}

/* ============================================================================================
   ⭐⭐⭐ C241 · A MARCA SAI DO PAINEL — decisão do Matheus, 12/09:
   "Marca não reflete mais dentro do painel, só no front do site. Painel é igual pra todo
   operador, só muda o logo."

   ⛔⛔ POR QUE `:root:root` E NÃO `:root`: o `Tema::cssVars()` injeta um `<style id="tema-wl">`
      com `:root{--brand-ink:…}` DEPOIS deste arquivo. Trocar o valor no `:root` daqui não pega —
      medido. `:root:root` tem especificidade (0,2,0) e vence (0,1,0) INDEPENDENTE DA ORDEM, que é
      a lei já escrita no CLAUDE.md ("para neutralizar regra global, SUBA A ESPECIFICIDADE").
   ⭐ E é isto que evita editar as 66 regras de destaque uma a uma: elas continuam lendo os mesmos
     nomes, e o valor por trás do nome é que mudou.
   ⛔ `--brand` e `--on-brand` ficam de FORA de propósito: são o LOGO, o único ponto branco-rótulo
      que sobra no painel. E o site não é tocado — lá a marca continua inteira.
   ⚠️ O tema LIGHT não entra aqui: o `tema-wl` emite `:root[data-theme="light"]`, que também é
     (0,2,0), empata com este bloco e vence pela ordem — logo o light continua com a marca, que é
     o que o §5 do lote manda.
   ============================================================================================ */
:root:root {
  /* ⭐⭐ CORRIGIDO pelo Matheus, 12/09: "no gráfico ele até pode ficar, mas em botões e ícones deve
     ser branco". Eu tinha posto `#6da7ec` aqui — o azul saiu de `--cds-text-accent` do espécime
     (linha 18), apelidado `--destaque` na linha 70 ("item ativo, aba ativa, foco"). Ligar esse
     apelido no `--brand-ink`, que no repo pinta ÍCONE, BOTÃO e LINK, foi um mapeamento largo demais.
     ⛔ Quem é preenchimento primário no espécime é `--cds-fill-primary:#fff` (linha 34); o azul
       pertence só ao gráfico (`--cds-chart-categorical-1:#3987e5`). */
  --brand-ink:       #f0efec;   /* = --text-1: o destaque do painel é BRANCO */
  --brand-hover:     #ffffff;
  --brand-soft:      rgba(255,255,255,.10);   /* = --cds-fill-secondary do espécime */
  --brand-border:    rgba(255,255,255,.20);
  --brand-selection: rgba(255,255,255,.22);
  --ring:            #f0efec;
  /* ⭐ O AZUL FICA AQUI, e só aqui: é o gráfico, liberado pelo Matheus. Valor do espécime
     (`--cds-chart-categorical-1`). ⚠️ 0 consumidores hoje (não existe `charts.js`) — entra para que
     o próximo a ligar um gráfico não herde o laranja que a decisão já tirou. */
  --chart-brand:     #3987e5;
}

/* ---- o BOTÃO PRIMÁRIO: branco com tinta quase-preta ----
   ⛔⛔ MEDIDO e reportado: a prosa do lote (§6.1 item 20) pede o botão AZUL, mas o insumo manda o
      contrário — `.btn--primary{background:var(--cds-fill-primary);color:var(--cds-on-primary)}`
      com `#fff`/`#0b0b0b` (espécime:34,307). A lei do lote é "copie os valores de lá, não da prosa",
      e o Matheus confirmou por escrito. Então vale o espécime.
   ⚠️ Ele usava `var(--brand)`, que continua LARANJA de propósito (o `--brand` é o logo e ficou fora
      do `:root:root`). Isso tinha deixado o botão laranja parado e AZUL no hover — par quebrado que
      esta regra fecha. */
/* ⛔ A regra do `.btn--primary` NAO mora aqui: a original vive na ~725 e, com a mesma
   especificidade (0,1,0), a de baixo vence. Ela foi para o bloco final "C241 · OS COMPONENTES".
   MEDIDO: aqui em cima o botão continuava `rgb(255,77,0)`. */

/* ============================================================================================
   ⭐⭐ C241 · O MESMO PARA O TEMA LIGHT, com o destaque ESCURO.
   ⛔⛔ POR QUE ISTO PRECISOU EXISTIR: enquanto o `<style tema-wl>` era injetado, o seu
      `:root[data-theme="light"]` (0,2,0) empatava com o `:root:root` acima (0,2,0) e vencia pela
      ordem — o light ficava com a marca do operador e o bloco de cima não o alcançava. Tirar o
      `Tema::cssVars()` do `head.php` do painel removeu esse empate, e o `--brand-ink: #f0efec`
      (off-white) passou a valer também sobre o `--bg: #FAFAFA`. MEDIDO: ~1,04:1 — ilegível.
   ⭐ `:root:root[data-theme="light"]` é (0,3,0): vence o bloco de cima E o `:root[data-theme=
     "light"]` do próprio app.css, sem depender de ordem.
   ⭐ O destaque do light é o `--text-1` dele (#18181B) — 16,9:1 sobre #FAFAFA.
   ⛔ `--chart-brand` é o ÚNICO que não inverte: gráfico é azul nos dois temas, foi o que o
      Matheus liberou ("no gráfico ele até pode ficar").
   ============================================================================================ */
:root:root[data-theme="light"] {
  --brand-ink:       #18181B;   /* = --text-1 do light */
  --brand-hover:     #000000;
  --brand-soft:      rgba(0,0,0,.06);
  --brand-border:    rgba(0,0,0,.18);
  --brand-selection: rgba(0,0,0,.15);
  --ring:            #18181B;
  --chart-brand:     #3987e5;
}

/* ------------------------------------------------------------------ */
/* 1b. TEMA LIGHT — paleta PRÓPRIA (não é o dark invertido).           */
/*   · fundo #FAFAFA (não branco puro, cansa menos) + cards brancos    */
/*   · tons de estado ESCURECIDOS: os pastéis do dark lavam no claro   */
/*   · laranja puro só como PREENCHIMENTO (botão, texto preto 7.3:1);  */
/*     como texto usa --brand-ink #C2410C (4.96:1 = AA)                */
/*   Contrastes conferidos: text-1 16.9 · text-2 7.4 · text-3 5.0 ·    */
/*   green 5.2 · red 4.6 · blue 5.7 · purple 5.5 · amber 4.8 (todos AA)*/
/*   Séries dos gráficos: paleta validada (CVD 9.2 · normal 19.8)      */
/* ------------------------------------------------------------------ */
[data-theme="light"] {
  --bg: #FAFAFA;
  --surface-1: #FFFFFF;
  --surface-2: #F4F4F5;
  --surface-3: #ECECEF;
  --elevated: #FFFFFF;

  --border-1: #E7E7EA;
  --border-2: #D8D8DD;
  --border-hover: #B4B4BC;

  --text-1: #18181B;
  --text-2: #52525B;
  --text-3: #6B6B76;
  --text-4: #82828B;

  --brand: #FF4D00;
  --brand-hover: #E04400;
  --brand-ink: #C73C00;
  --brand-soft: rgba(255,77,0,.10);
  --brand-border: rgba(255,77,0,.32);
  --brand-selection: rgba(255,77,0,.22);

  --green:  #047857; --green-soft:  rgba(4,120,87,.09);   --green-border:  rgba(4,120,87,.22);
  --red:    #dc2626; --red-soft:    rgba(220,38,38,.09);  --red-border:    rgba(220,38,38,.22);
  --blue:   #0369a1; --blue-soft:   rgba(3,105,161,.09);  --blue-border:   rgba(3,105,161,.22);
  --purple: #7c3aed; --purple-soft: rgba(124,58,237,.09); --purple-border: rgba(124,58,237,.22);
  --amber:  #b45309; --amber-soft:  rgba(180,83,9,.09);   --amber-border:  rgba(180,83,9,.24);
  --whatsapp: #0F766E; --whatsapp-soft: rgba(15,118,110,.10);

  --on-brand: #000;
  --field-bg: #F4F4F5;
  --shimmer: rgba(0,0,0,.05);
  --tooltip-title-bg: rgba(0,0,0,.03);
  --overlay: rgba(24,24,27,.45);
  --overlay-2: rgba(24,24,27,.38);
  --track: rgba(0,0,0,.08);
  --track-alt: rgba(0,0,0,.11);
  --on-cohort: #fff;                     /* célula do cohort segue saturada -> texto branco */
  --shadow-1: rgba(24,24,27,.06);
  --shadow-2: rgba(24,24,27,.10);
  --shadow-3: rgba(24,24,27,.14);
  --select-arrow: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='10' fill='rgba(0,0,0,.45)' viewBox='0 0 16 16'%3E%3Cpath d='M8 11L3 6h10z'/%3E%3C/svg%3E");

  --chart-brand: #EA580C;
  --chart-blue: #0284c7;
  --chart-purple: #7c3aed;
  --chart-amber: #b45309;
  --chart-green: #059669;
  --chart-red: #dc2626;

  --form-scheme: light;
  color-scheme: light;
}

/* ------------------------------------------------------------------ */
/* 2. BASE                                                            */
/* ------------------------------------------------------------------ */
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body {
  background: var(--bg); color: var(--text-1);
  font-family: var(--font-sans); font-size: 14px; line-height: 1.5; font-weight: 400;
  -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale;
}
::selection { background: var(--brand-selection); }
h1,h2,h3,h4 { font-weight: 600; letter-spacing: -.2px; text-wrap: balance; line-height: 1.2; }
p { text-wrap: pretty; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; cursor: pointer; }
img { display: block; }
/* ⭐⭐⭐ C241d §8.2 · `.num` e `.mono` SEPARADOS — decisão do Matheus, provada antes de aplicar.
   ⭐ `mono` ali NÃO é estilo, é SEMÂNTICA: o usuário precisa distinguir l de 1 e O de 0 num
     identificador que vai COPIAR (slug /r/<x>, id de mídia, template {link}, PIX). `num` não tem
     essa função — `tabular-nums` alinha os dígitos na família certa.
   ⛔⛔ POR QUE ESTAS DUAS LINHAS FICAM EXATAMENTE AQUI, E NÃO NO FIM DO ARQUIVO:
      `.input` (app.css:~780) declara `font-family: inherit`, empata em especificidade com `.mono`
      (0,1,0) e VENCE POR ORDEM — é isso, e só isso, que mantém os 16 campos `class="input mono"`
      em Manrope hoje. Mover `.mono` para um bloco no fim inverteria o desempate e devolveria
      JetBrains Mono a esses 16 sem ninguém ter pedido. ⭐ A POSIÇÃO DESTA LINHA É LOAD-BEARING.
   ⭐⭐ E POR QUE `.mono` VEM ANTES DE `.num`: 34 sites emitem as DUAS classes no mesmo elemento, e
     foi MEDIDO que os 34 são número puro (brl/int0/moneyC/dlMB) — nenhum é identificador. Com
     `.num` depois, esses 34 resolvem em Manrope, que é o certo, SEM precisar editar 30 linhas do
     app.js. A ordem aqui faz o trabalho que a edição faria.
   ⚠️ EXCEÇÕES DECLARADAS, medidas e NÃO tocadas: `.input.num` (app.css:~787) é (0,2,0) e vence
     `.num` — 11 campos seguem monoespaçados; e 1 nó `num cell-time` (app.js:3701) segue o
     `.cell-time`. Estão no relatório. */
.mono { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-feature-settings: 'tnum'; }
.num { font-family: var(--font-sans); font-variant-numeric: tabular-nums; font-feature-settings: 'tnum'; }

/* scrollbar + focus */
* { scrollbar-width: thin; scrollbar-color: var(--border-2) transparent; }
*::-webkit-scrollbar { width: 10px; height: 10px; }
*::-webkit-scrollbar-thumb { background: var(--border-2); border-radius: 99px; border: 2px solid transparent; background-clip: content-box; }
*::-webkit-scrollbar-thumb:hover { background: var(--border-hover); }
:focus-visible { outline: none; border-color: var(--brand-ink); box-shadow: 0 0 0 3px var(--ring); }   /* anel SÓLIDO (brand-ink) -> visível também em nav-item/.clickable/links/.segmented (sem borda). Inputs têm regra própria (mais específica) e não regridem. */

.muted { color: var(--text-2); }
.dim { color: var(--text-3); }
.eyebrow { font-size: 10px; letter-spacing: 1.2px; text-transform: uppercase; color: var(--text-3); font-weight: 500; }
.row { display: flex; align-items: center; }
.gap-1{gap:.5rem}.gap-2{gap:.75rem}.gap-3{gap:1rem}.gap-4{gap:1.5rem}

/* ------------------------------------------------------------------ */
/* 3. APP SHELL                                                       */
/* ------------------------------------------------------------------ */
.app { display: grid; grid-template-columns: var(--sidebar-w) 1fr; grid-template-rows: 100vh; overflow: hidden; transition: grid-template-columns .22s var(--ease); }

/* ⭐ C241 · a lateral sai do `--bg` e ganha cor própria: o espécime separa página (#050505 aqui) de
   lateral (#0d0d0d), e hoje as duas usavam o mesmo token. ⛔ O `border-right` fica.
   ⛔⛔ ATENÇÃO: este valor é do tema ESCURO e PRECISA da contraparte light logo abaixo. Cravá-lo sem
      ela deixava a lateral preta também no tema claro — MEDIDO: `.nav-group__title` caía a 3,69:1
      (reprova o AA de 4,5) e o nome do usuário no rodapé, a 1,1:1. */
.sidebar { background: #0d0d0d; border-right: 1px solid var(--border-1); display: flex; flex-direction: column; overflow-y: auto; }
.sidebar__brand { display: flex; align-items: center; gap: 10px; height: var(--topbar-h); padding: 0 18px; border-bottom: 1px solid var(--border-1); flex-shrink: 0; }
.sidebar__logo { width: 30px; height: 30px; border-radius: 8px; display: grid; place-items: center; background: var(--brand); color: var(--on-brand); font-weight: 700; font-size: 15px; flex-shrink: 0; }
.sidebar__logoimg { max-height: 32px; max-width: 154px; width: auto; height: auto; object-fit: contain; display: block; flex-shrink: 0; }
.sidebar__brand b { font-weight: 600; font-size: 14px; letter-spacing: -.2px; }
.sidebar__brand small { display: block; font-size: 11px; color: var(--text-3); font-weight: 500; }
/* rodapé fixo da sidebar (usuário logado + Sair) — sticky embaixo mesmo se o menu rolar; a cor do avatar segue a marca (white-label, mesmo padrão do .sidebar__logo) */
.sidebar__foot { margin-top: auto; position: sticky; bottom: 0; display: flex; align-items: center; gap: 10px; padding: 12px 14px; border-top: 1px solid var(--border-1); background: var(--bg); flex-shrink: 0; }
.sidebar__avatar { width: 32px; height: 32px; border-radius: 50%; display: grid; place-items: center; background: var(--brand); color: var(--on-brand); font-weight: 700; font-size: 14px; flex-shrink: 0; }
.sidebar__uinfo { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.sidebar__uinfo b { font-size: 13px; font-weight: 600; color: var(--text-1); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sidebar__uinfo small { font-size: 11px; color: var(--text-3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sidebar__logout { flex-shrink: 0; }

/* Upload estilizado (tela Identidade): o <input type=file> nativo fica sr-only dentro de um
   <label class="btn">, focável por teclado (foco visível no botão) — sem o "Escolher arquivo" do browser. */
.filebtn { position: relative; cursor: pointer; }
.filebtn input[type="file"] { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0,0,0,0); border: 0; }
.filebtn:has(:focus-visible) { outline: 2px solid var(--brand-ink); outline-offset: 2px; }   /* só no foco por TECLADO — abrir modal (foco programático) não acende */

/* ============ C49 · ÁRVORE DE MÍDIA — o navegador de pastas da tela Mídia ============
   Lista, não grade: a pergunta que o operador faz aqui é "onde está e quem usa", e isso é texto
   alinhado em coluna, não miniatura. A miniatura vira o ÍCONE da linha (48px), no lugar do ícone de
   arquivo — assim ela informa sem roubar a leitura vertical.
   ⛔ Zero valor de cor à mão: tudo por token, como o resto do painel. */
.arv-topo { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; justify-content: space-between; padding: 14px 16px; border-bottom: 1px solid var(--border-1); }
.arv-migalha { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; font-size: 14px; min-width: 0; }
.arv-migalha .lnk { font-size: 14px; }
.arv-migalha > span:last-child { font-weight: 600; color: var(--text-1); }
.arv-sep { color: var(--text-4); }

.arv-lista { display: flex; flex-direction: column; }
/* grade de 5 colunas: seleção · ícone · nome (elástico) · meta · ação. As 4 fixas mantêm o
   alinhamento vertical mesmo com nome curto — é o que faz a lista ser lida como coluna. */
.arv-linha { display: grid; grid-template-columns: 34px 52px minmax(0, 1fr) auto 76px; align-items: center; gap: 10px; padding: 8px 16px; border-bottom: 1px solid var(--border-1); transition: background 150ms var(--ease); }
.arv-linha:last-child { border-bottom: 0; }
.arv-linha:hover { background: var(--surface-1); }
.arv-linha.is-sel { background: var(--brand-soft); }
.arv-linha--pasta { cursor: pointer; }
.arv-linha--pasta .arv-nome { font-weight: 600; }
.arv-check { display: flex; align-items: center; justify-content: center; }
/* C56 · a coluna do ícone foi de 44 para 52px: a miniatura tem 40 e a folga de 2px de cada lado
 fazia a imagem encostar na checkbox no arredondamento do grid. ⭐ Agora são 6px de cada lado. */
.arv-ico { display: flex; align-items: center; justify-content: center; color: var(--text-3); overflow: hidden; }
.arv-linha--pasta .arv-ico { color: var(--brand-ink); }
.arv-thumb { width: 40px; height: 40px; object-fit: cover; border-radius: 6px; background: var(--surface-2); }
.arv-nome { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 14px; }
.arv-meta { font-size: 12px; white-space: nowrap; }
.arv-acao { display: flex; gap: 4px; justify-content: flex-end; }
.arv-acao .btn--icon { width: 28px; height: 28px; }
.arv-vazio { padding: 28px 16px; text-align: center; font-size: 13px; }
.arv-rodape { padding: 10px 16px; border-top: 1px solid var(--border-1); font-size: 12px; }
.arv-onde { margin: 0; padding-left: 18px; display: flex; flex-direction: column; gap: 6px; font-size: 13px; max-height: 320px; overflow-y: auto; }

/* ============ C54 · o nome clicável, o paginador e a pré-visualização ============
   ⭐ A lista continua LISTA: o nome virou <button>, mas com a mesma altura de linha de antes — a
   densidade é o ponto da tela, e um botão que engorda a linha teria custado o que a organização
   ganhou. Por isso `padding:0` e `font:inherit`. */
.arv-nome-btn { background: none; border: 0; padding: 0; margin: 0; font: inherit; color: inherit; text-align: left; cursor: pointer; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; display: block; }
.arv-nome-btn:hover { color: var(--brand-ink); text-decoration: underline; }
.arv-ico-falha { display: flex; align-items: center; justify-content: center; color: var(--text-4); }

.arv-pager { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding: 10px 16px; border-top: 1px solid var(--border-1); font-size: 12px; }
.arv-pager strong { color: var(--text-1); }

/* a imagem em tamanho natural, mas presa à viewport: `min()` impede que uma foto de 2560px estoure
   o modal, e o fundo xadrez denuncia transparência (um PNG branco em fundo branco parece vazio). */
.arv-prev { display: flex; align-items: center; justify-content: center; padding: 12px; border-radius: var(--radius-input); border: 1px solid var(--border-1);
  background-color: var(--surface-2);
  background-image: linear-gradient(45deg, rgba(255,255,255,.04) 25%, transparent 25%, transparent 75%, rgba(255,255,255,.04) 75%), linear-gradient(45deg, rgba(255,255,255,.04) 25%, transparent 25%, transparent 75%, rgba(255,255,255,.04) 75%);
  background-size: 16px 16px; background-position: 0 0, 8px 8px; }
.arv-prev img { max-width: 100%; max-height: min(52vh, 460px); width: auto; height: auto; object-fit: contain; }
.arv-prev-usos { margin-top: 14px; font-size: 13px; display: flex; flex-direction: column; gap: 8px; }
.arv-prev-usos strong { color: var(--text-1); font-weight: 600; }

/* no estreito, a meta desce para a segunda linha em vez de espremer o nome */
@media (max-width: 640px) {
  .arv-linha { grid-template-columns: 30px 48px minmax(0, 1fr) 72px; row-gap: 2px; }
  .arv-meta { grid-column: 3 / 5; font-size: 11px; }
  .arv-acao { grid-row: 1; grid-column: 4; }
}

/* galeria de mídia (tela Mídia da home): grid de miniaturas arrastáveis */
.midia-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: 12px; }
/* quadro de dimensão fixa (aspect-ratio) — todas as miniaturas do MESMO tamanho, alinhadas */
.midia-thumb { position: relative; aspect-ratio: 1 / 1; background: var(--field-bg); border: 1px solid var(--border-2); border-radius: 10px; overflow: hidden; cursor: grab; display: flex; align-items: center; justify-content: center; transition: border-color 150ms; }
.midia-thumb:hover { border-color: var(--border-hover); }
.midia-thumb img { pointer-events: none; }   /* a img não intercepta o arrasto; o enquadramento vem do modificador do grid */
/* Catálogo (pôsteres): quadro RETRATO 2:3 + COVER — pôsteres uniformes, preenche cortando pouca borda. */
.midia-grid--cover .midia-thumb { aspect-ratio: 2 / 3; }
.midia-grid--cover .midia-thumb img { width: 100%; height: 100%; object-fit: cover; }
/* Canais/Campeonatos (logos): quadro QUADRADO 1:1 + CONTAIN com respiro — nunca corta o logo. */
.midia-grid--contain .midia-thumb { aspect-ratio: 1 / 1; }
.midia-grid--contain .midia-thumb img { max-width: 100%; max-height: 100%; object-fit: contain; padding: 14px; box-sizing: border-box; }
/* item arrastado vira um placeholder tracejado NO LUGAR onde vai cair (drop claro) */
.midia-thumb.dragging { opacity: .6; cursor: grabbing; border: 2px dashed var(--brand-ink); background: var(--brand-soft); }
.midia-thumb.dragging img { opacity: .25; }
.midia-grid.is-dragging .midia-thumb:not(.dragging) { cursor: grabbing; }
.midia-grid.is-dragging .midia-del { pointer-events: none; }   /* não apaga sem querer durante o arrasto */
/* botão remover: chip no padrão do painel (mesmos tokens + hover danger-ghost), ícone SVG */
.midia-del { position: absolute; top: 6px; right: 6px; width: 28px; height: 28px; padding: 0; display: flex; align-items: center; justify-content: center; border-radius: var(--radius-input); background: var(--surface-2); border: 1px solid var(--border-2); color: var(--text-2); opacity: 0; transition: opacity 150ms var(--ease), background 150ms var(--ease), border-color 150ms var(--ease), color 150ms var(--ease); }
.midia-del svg { width: 14px; height: 14px; }
.midia-thumb:hover .midia-del { opacity: 1; }
.midia-del:hover { background: var(--red-soft); border-color: var(--red-border); color: var(--red); }
.midia-badge { position: absolute; top: 5px; left: 5px; font-size: 10px; font-weight: 600; letter-spacing: .3px; text-transform: uppercase; padding: 2px 7px; border-radius: 99px; background: var(--brand); color: var(--on-brand); }
/* card de ESCOLHA (gerenciador Identidade): mesmo molde .midia-thumb, sem 'x' (clicar = definir/trocar); o atual destacado */
.midia-thumb.midia-pick { cursor: pointer; padding: 0; }
.midia-thumb.is-atual { border-color: var(--brand-ink); box-shadow: 0 0 0 2px var(--brand-soft); }
/* upload em andamento (bug de upload de mídia): miniatura local esmaecida + spinner por item; botão inerte */
.midia-thumb.is-uploading { cursor: default; }
.midia-thumb.is-uploading img { opacity: .35; }
/* referência (#id) + copiar de uma imagem AVULSA (área 'geral', sem lugar fixo) — sempre visível pra apontar seções do site */
.midia-ref { position: absolute; left: 5px; bottom: 5px; display: inline-flex; align-items: center; gap: 4px; padding: 2px 5px; border-radius: 99px; background: var(--surface-2); border: 1px solid var(--border-2); }
.midia-ref .mono { font-size: 11px; color: var(--text-2); }
.midia-ref button { display: inline-flex; align-items: center; padding: 2px; margin: 0; border: 0; background: transparent; color: var(--text-3); cursor: pointer; border-radius: 4px; }
.midia-ref button:hover { color: var(--brand-ink); }
.midia-ref button svg { width: 12px; height: 12px; display: block; }
.midia-ref button.is-copied svg { color: var(--green); }
.midia-spin { position: absolute; top: 50%; left: 50%; width: 22px; height: 22px; margin: -11px 0 0 -11px; border-radius: 50%; border: 2px solid var(--border-2); border-top-color: var(--brand-ink); animation: midia-spin .7s linear infinite; }
.filebtn.is-busy { opacity: .55; pointer-events: none; }
@keyframes midia-spin { to { transform: rotate(360deg); } }

/* ===== Seletor de imagem do CMS (tipo 'imagem', ligado à Biblioteca) + grid da Biblioteca ===== */
.cms-imgsel { display: flex; flex-direction: column; gap: 10px; }
.cms-imgsel__prev { width: 96px; height: 96px; border: 1px solid var(--border-2); border-radius: 10px; background: var(--field-bg); display: flex; align-items: center; justify-content: center; overflow: hidden; }
.cms-imgsel__prev img { max-width: 100%; max-height: 100%; object-fit: contain; padding: 8px; box-sizing: border-box; }
.cms-imgsel__acts { display: flex; gap: 8px; flex-wrap: wrap; }
/* Grid do seletor — seletores ESPECÍFICOS (.cms-imgsel >) pra vencer qualquer cascata do modal/card; o
   :not([hidden]) resolve abrir/fechar sem depender do UA (display:grid da classe mataria o atributo hidden). */
/* ☠ MORTO (C250 §E) — a gaveta embutida virou modal: a gaveta recolhida */
/* ☠ MORTO (C250 §E) — a gaveta embutida virou modal: a grade de 64px dentro do campo */
/* thumb = grid item; min-width:0 + width:100% pro <button> encolher pra célula (senão o min-content da img
   estoura a coluna e as thumbs empilham gigantes no modal estreito). box-sizing/margin zeram o reset do button. */
/* thumbs QUADRADAS e uniformes mesmo com imagens de qualquer proporção (pôster 500x750, logo 1280x320):
   grid-auto-rows:1fr iguala a altura das linhas; min-width/min-height:0 deixam o <button> (grid item) encolher
   pra célula (senão o min-content da img estica); a img entra por object-fit:contain. */
/* ☠ MORTO (C250 §E) — a gaveta embutida virou modal: as linhas da gaveta */
/* ☠ MORTO (revisão adversarial C250) — sem consumidor desde que a gaveta de miniaturas virou modal: .cms-imgsel .cms-imgpick */
/* ☠ MORTO (revisão adversarial C250) — sem consumidor desde que a gaveta de miniaturas virou modal: .cms-imgsel .cms-imgpick:hover */
/* ☠ MORTO (revisão adversarial C250) — sem consumidor desde que a gaveta de miniaturas virou modal: .cms-imgsel .cms-imgpick img */
.midia-bibgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); gap: 10px; }
.midia-bibitem { aspect-ratio: 1 / 1; border: 1px solid var(--border-2); border-radius: 10px; background: var(--field-bg); display: flex; align-items: center; justify-content: center; overflow: hidden; }
.midia-bibitem img { max-width: 100%; max-height: 100%; object-fit: contain; padding: 8px; box-sizing: border-box; }
.midia-bibgroup { margin-bottom: 18px; }
.midia-bibgroup:last-child { margin-bottom: 0; }
.midia-bibgroup__h { font-size: 13px; font-weight: 600; color: var(--text-2); margin: 0 0 10px; }

/* preview 16:9 do fundo do hero */
.midia-hero { position: relative; margin-top: 8px; aspect-ratio: 16 / 7; width: 100%; max-width: 420px; border-radius: 10px; overflow: hidden; background: var(--field-bg); border: 1px solid var(--border-2); display: flex; align-items: center; justify-content: center; }
.midia-hero.is-empty { border-style: dashed; }
.midia-hero img { width: 100%; height: 100%; object-fit: cover; }
.midia-hero .midia-badge { top: 8px; left: 8px; }

/* cards de navegação das galerias (overview) */
.midia-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 14px; }
.midia-navcard { display: block; padding: 16px; text-decoration: none; transition: border-color 150ms, transform 150ms; }
.midia-navcard:hover { border-color: var(--border-hover); transform: translateY(-2px); }
.midia-navcard__top { display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px; }
.midia-navcard__top b { font-size: 14px; color: var(--text-1); }
.midia-navcard__top .dim { font-size: 12px; }
.midia-navcard__cover { height: 76px; background: var(--field-bg); border: 1px solid var(--border-1); border-radius: 8px; display: flex; align-items: center; justify-content: center; overflow: hidden; }
.midia-navcard__cover img { max-height: 60px; max-width: 90%; object-fit: contain; }
.midia-navcard__cover .dim { font-size: 12px; }
.midia-navcard__go { font-size: 12px; margin-top: 10px; }

/* barra Salvar/Descartar — só aparece com rascunho pendente */
.midia-bar { position: sticky; bottom: 16px; margin-top: 20px; display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; padding: 12px 16px; border-radius: var(--radius-card); background: var(--surface-2); border: 1px solid var(--brand-border); box-shadow: 0 10px 30px rgba(0,0,0,.35); animation: fade-up 200ms var(--ease); }
.midia-bar__txt { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-1); }
.midia-bar__txt svg { color: var(--brand-ink); flex-shrink: 0; }

.sidebar__nav { padding: 8px 10px 24px; }
.nav-group { margin-top: 16px; }
.nav-group__title { padding: 0 10px 6px; font-size: 10px; letter-spacing: 1px; text-transform: uppercase; color: var(--text-3); font-weight: 500; }   /* título de seção = TEXTO → --text-3 (AA), migrado de --text-4 */
.nav-item {
  position: relative; display: flex; align-items: center; gap: 10px; width: 100%;
  padding: 8px 10px; border-radius: var(--radius-input);
  color: var(--text-2); font-size: 13px; font-weight: 500; text-align: left; border: 0; background: transparent;
  transition-property: background, color; transition-duration: 180ms; transition-timing-function: var(--ease);
}
.nav-item svg { width: 16px; height: 16px; flex-shrink: 0; color: var(--text-3); transition: color 180ms var(--ease); }
.nav-item:hover { background: var(--surface-1); color: var(--text-1); }
.nav-item:hover svg { color: var(--text-2); }
.nav-item.is-active { background: var(--surface-2); color: var(--text-1); }
.nav-item.is-active svg { color: var(--brand-ink); }
.nav-item.is-active::before { content: ""; position: absolute; left: -10px; top: 50%; transform: translateY(-50%); width: 2px; height: 16px; border-radius: 99px; background: var(--brand-ink); }
.nav-item__badge { margin-left: auto; font-size: 10px; color: var(--text-3); }   /* badge = TEXTO → --text-3 (AA), migrado de --text-4 */

.main { display: flex; flex-direction: column; overflow: hidden; min-width: 0; }
.topbar { height: var(--topbar-h); flex-shrink: 0; display: flex; align-items: center; gap: 14px; padding: 0 24px; border-bottom: 1px solid var(--border-1); }
.topbar__title { font-size: 14px; font-weight: 600; letter-spacing: -.2px; }
.topbar__title small { display: block; font-size: 11px; color: var(--text-3); font-weight: 400; letter-spacing: 0; }
.topbar__spacer { flex: 1; }
.status-dot { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; color: var(--text-2); font-weight: 500; }
.status-dot::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: var(--green); box-shadow: 0 0 0 3px var(--green-soft); animation: pulse 2s ease infinite; }
@keyframes pulse { 0%,100%{opacity:1} 50%{opacity:.5} }
.hamburger { display: inline-flex; }   /* visível nos 2 contextos: mobile abre o overlay, desktop recolhe a sidebar */

/* DESKTOP: sidebar recolhida em faixa de ícones (toggle no #hb; persiste em aztv_sidebar).
   Só no desktop — no mobile o #hb controla o overlay (.app.nav-open), não isto. */
@media (min-width: 1024.02px) {   /* casa exatamente com o teste do JS (min-width:1024.02) — sem fresta entre 1024/1025 */
  :root.sidebar-collapsed .app { grid-template-columns: 64px 1fr; }
  :root.sidebar-collapsed .nav-item span,
  :root.sidebar-collapsed .nav-group__title,
  :root.sidebar-collapsed .sidebar__brand b,
  :root.sidebar-collapsed .sidebar__brand small { display: none; }
  :root.sidebar-collapsed .nav-item { justify-content: center; padding: 8px 0; }
  :root.sidebar-collapsed .nav-item.is-active::before { display: none; }
  :root.sidebar-collapsed .sidebar__brand { justify-content: center; padding: 0; overflow: hidden; }
  :root.sidebar-collapsed .sidebar__logoimg { height: auto; max-width: 42px; }   /* wordmark escala p/ caber na faixa, sem cortar */
  :root.sidebar-collapsed .sidebar__nav { padding: 8px 8px 24px; }
  :root.sidebar-collapsed .sidebar__foot { flex-direction: column; gap: 8px; padding: 12px 8px; }   /* faixa estreita: só avatar + Sair, empilhados */
  :root.sidebar-collapsed .sidebar__uinfo { display: none; }
}

.content { flex: 1; overflow-y: auto; padding: 24px; }
.content__head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 20px; flex-wrap: wrap; }
.content__head h1 { font-size: 20px; }
.content__head p { color: var(--text-3); font-size: 13px; margin-top: 3px; }

.kpi-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 12px; margin-bottom: 12px; }
.split { display: grid; grid-template-columns: 1.6fr 1fr; gap: 12px; }
@media (max-width: 900px) { .split { grid-template-columns: 1fr; } }

/* Projeção de fechamento (Visão Geral) — card com barra de progresso + células */
.projhead { align-items: flex-start; gap: 16px; flex-wrap: wrap; }
.projprog { min-width: 200px; flex: 0 0 auto; }
.projprog__txt { font-family: 'JetBrains Mono', monospace; font-size: 12px; color: var(--text-3); text-align: right; margin-bottom: 6px; }
.projprog__txt b { color: var(--text-1); font-weight: 600; }
.projbar { height: 6px; border-radius: 999px; background: var(--surface-3); overflow: hidden; }
.projbar__fill { height: 100%; border-radius: 999px; background: var(--brand-ink); transition: width .4s ease; }
.projgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; }
.projcell { background: var(--surface-1); border: 1px solid var(--border-1); border-radius: 10px; padding: 12px 14px; }
.projcell__label { display: block; font-size: 12px; color: var(--text-3); }
.projcell__value { display: block; font-family: 'JetBrains Mono', monospace; font-size: 20px; font-weight: 500; color: var(--text-1); margin-top: 4px; letter-spacing: -.01em; }
.projcell__sub { display: block; font-size: 11px; color: var(--text-3); margin-top: 3px; }
.projcell--hero { background: var(--brand-soft); border-color: var(--brand-border); }
.projcell--hero .projcell__value { color: var(--brand-ink); }
.projcell--hero.projcell--neg { background: var(--red-soft); border-color: var(--red-border); }
.projcell--hero.projcell--neg .projcell__value { color: var(--red); }

/* Estoque de códigos + Burn Rate (Fase 5) */
.stockgrid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 12px; }
@media (max-width: 900px) { .stockgrid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px) { .stockgrid { grid-template-columns: 1fr; } }
/* ☠ MORTO (C250 §C) — o Estoque passou a usar `.card` + `.kpi`: o cartão paralelo (tingimento + borda + raio 14 + padding próprios) */
/* ☠ MORTO (C250 §C) — o Estoque passou a usar `.card` + `.kpi`: o cabeçalho */
/* ☠ MORTO (C250 §C) — o Estoque passou a usar `.card` + `.kpi`: o nome */
/* ☠ MORTO (C250 §C) — o Estoque passou a usar `.card` + `.kpi`: o chip de 9px com borda verde fixa — a causa do "chips com borda" */
/* ☠ MORTO (C250 §C) — o Estoque passou a usar `.card` + `.kpi`: o valor */
/* ☠ MORTO (C250 §C) — o Estoque passou a usar `.card` + `.kpi`: o subtítulo */
.stockbar { height: 6px; border-radius: 999px; background: var(--surface-3); overflow: hidden; margin: 12px 0 8px; }
.stockbar__fill { height: 100%; border-radius: 999px; background: var(--green); }
/* ☠ MORTO (C250 §C) — o Estoque passou a usar `.card` + `.kpi`: o rodapé */
/* ☠ MORTO (C250 §C) — o Estoque passou a usar `.card` + `.kpi`: a variante baixo (chip) */
/* ☠ MORTO (C250 §C) — o Estoque passou a usar `.card` + `.kpi`: a variante baixo (barra) */
/* ☠ MORTO (C250 §C) — o Estoque passou a usar `.card` + `.kpi`: a variante baixo (valor) */
/* ☠ MORTO (C250 §C) — o Estoque passou a usar `.card` + `.kpi`: a variante crítico */
/* ☠ MORTO (C250 §C) — o Estoque passou a usar `.card` + `.kpi`: a variante crítico (chip) */
/* ☠ MORTO (C250 §C) — o Estoque passou a usar `.card` + `.kpi`: a variante crítico (barra) */
/* ☠ MORTO (C250 §C) — o Estoque passou a usar `.card` + `.kpi`: a variante crítico (valor) */
.alertbar { display: flex; align-items: center; gap: 10px; background: var(--red-soft); border: 1px solid var(--red-border); color: var(--text-1); border-radius: 12px; padding: 12px 16px; margin-bottom: 12px; font-size: 13px; }
.alertbar svg { color: var(--red); flex: none; }
/* variante AVISO/ATENÇÃO (auditoria UI primitivas #4): âmbar tingido (tokens --amber*); o texto segue --text-1 (AA sobre o fundo levemente tingido), só o ícone é âmbar. .alertbar (sem modificador) = ERRO/vermelho. */
.alertbar--warn { background: var(--amber-soft); border-color: var(--amber-border); }
.alertbar--warn svg { color: var(--amber); }
/* variante INFO NEUTRA (auditoria UI primitivas #4 Parte 2): cinza (superfície neutra existente); ícone --text-2. Trio semântico: .alertbar=erro (vermelho) · --warn=aviso (âmbar) · --muted=info (cinza). */
.alertbar--muted { background: var(--surface-2); border-color: var(--border-2); }
.alertbar--muted svg { color: var(--text-2); }

/* ponto de alerta crítico no item da sidebar (Estoque de Códigos) */
.nav-alert { margin-left: auto; width: 7px; height: 7px; border-radius: 50%; background: var(--red); flex: none; box-shadow: 0 0 0 3px var(--red-soft); animation: pulse 2s ease infinite; }
/* pop-up de estoque crítico (canto inferior direito, junto dos toasts) */
.alert-toast { min-width: 300px; max-width: 360px; padding: 14px 16px; background: var(--elevated); border: 1px solid var(--red-border); border-left: 3px solid var(--red); border-radius: var(--radius-card); animation: toast-in .28s var(--ease) both; display: flex; flex-direction: column; gap: 12px; }
.alert-toast.is-out { animation: toast-out .2s var(--ease) both; }
.alert-toast__body { display: flex; gap: 10px; align-items: flex-start; }
.alert-toast__body svg { color: var(--red); flex: none; margin-top: 1px; }
.alert-toast__body b { font-size: 13px; font-weight: 600; }
.alert-toast__body p { font-size: 12px; color: var(--text-2); margin: 4px 0 0; line-height: 1.4; }
.alert-toast__actions { display: flex; gap: 8px; justify-content: flex-end; align-items: center; }
.alert-toast__close { background: none; border: 0; color: var(--text-3); cursor: pointer; padding: 6px; border-radius: 6px; display: inline-flex; }
.alert-toast__close:hover { background: var(--surface-2); color: var(--text-1); }

/* Insights (cards de regra determinística) */
.insight-list { display: grid; gap: 10px; }
.insight { display: flex; gap: 12px; align-items: flex-start; background: var(--surface-1); border: 1px solid var(--border-1); border-left: 3px solid var(--text-3); border-radius: 12px; padding: 14px 16px; transition: background 180ms var(--ease); }
.insight:hover { background: var(--surface-2); }
.insight[data-goto] { cursor: pointer; }
.insight__icon { flex: none; width: 34px; height: 34px; border-radius: 9px; display: grid; place-items: center; background: var(--surface-3); }
.insight__icon svg { color: var(--text-2); }
.insight__body { flex: 1; min-width: 0; }
.insight__sev { font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: .6px; color: var(--text-3); }
.insight__body p { font-size: 13.5px; color: var(--text-1); margin: 2px 0 0; line-height: 1.45; }
.insight__link { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; color: var(--brand-ink); margin-top: 6px; }
.insight--critico { border-left-color: var(--red); }   .insight--critico .insight__icon { background: var(--red-soft); }   .insight--critico .insight__icon svg { color: var(--red); }   .insight--critico .insight__sev { color: var(--red); }
.insight--atencao { border-left-color: var(--amber); } .insight--atencao .insight__icon { background: var(--amber-soft); } .insight--atencao .insight__icon svg { color: var(--amber); } .insight--atencao .insight__sev { color: var(--amber); }
.insight--info { border-left-color: var(--blue); }     .insight--info .insight__icon { background: var(--blue-soft); }     .insight--info .insight__icon svg { color: var(--blue); }     .insight--info .insight__sev { color: var(--blue); }
.insight--positivo { border-left-color: var(--green); }.insight--positivo .insight__icon { background: var(--green-soft); }.insight--positivo .insight__icon svg { color: var(--green); }.insight--positivo .insight__sev { color: var(--green); }
/* Insights embutidos na Visão Geral: o container (#ovInsights) usa .card__body--flush
   (padding 0), então sem isto os cards ficam full-bleed e desalinhados do header. Recompõe
   o recuo (20px horizontais = .card__head), o respiro vertical e um padding interno mais
   folgado. Escopado ao #ovInsights p/ NÃO alterar a tela de Insights (renderiza no .content). */
#ovInsights { padding: 16px 20px; }
#ovInsights .insight-list { gap: 12px; }
#ovInsights .insight { padding: 14px 18px; }

/* Vendas ao Vivo — indicador + destaque de venda nova (sutil, one-shot ~2s) */
.live-ind { display: flex; align-items: center; gap: 12px; }
tr.is-new { animation: rowNew 2.2s var(--ease); }
@keyframes rowNew {
  0%   { background: var(--green-soft); box-shadow: inset 3px 0 0 var(--green); }
  60%  { background: var(--green-soft); box-shadow: inset 3px 0 0 var(--green); }
  100% { background: transparent;       box-shadow: inset 3px 0 0 transparent; }
}
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
@media (max-width: 900px) { .grid-3 { grid-template-columns: 1fr; } }

/* barra de filtros global (espelha o dashboard atual: período + custom + plano) */
.filterbar { display: flex; align-items: center; gap: 12px; padding: 12px 24px; border-bottom: 1px solid var(--border-1); flex-wrap: nowrap; overflow-x: auto; }
.filterbar__label { font-size: 11px; color: var(--text-3); font-weight: 500; text-transform: uppercase; letter-spacing: .8px; white-space: nowrap; flex-shrink: 0; }
.filterbar .select, .filterbar .input { height: 36px; font-size: 13px; }
.filterbar .select { width: 168px; flex-shrink: 0; }
.filterbar .segmented, .filterbar .btn, .filterbar__dates { flex-shrink: 0; }
.filterbar__dates { display: flex; align-items: center; gap: 8px; }
.filterbar__dates[hidden] { display: none; }
.filterbar__dates span { color: var(--text-3); font-size: 12px; }   /* datas = TEXTO → --text-3 (AA), migrado de --text-4 */
.filterbar__sep { width: 1px; height: 20px; background: var(--border-1); }
.filterbar__spacer { flex: 1; }

/* custo por código: 4 campos compactos numa linha só */
.cpc-grid { display: grid; grid-template-columns: repeat(4, minmax(140px, 200px)); gap: 14px; }
.cpc-grid .input-group { max-width: 200px; }
@media (max-width: 720px) { .cpc-grid { grid-template-columns: repeat(2, 1fr); } }

/* toggle de plano (segmented reaproveitado, cor por plano) */
.plan-toggle button[data-plan="mensal"].is-active { color: var(--blue); }
.plan-toggle button[data-plan="tri"].is-active { color: var(--purple); }
.plan-toggle button[data-plan="sem"].is-active { color: var(--amber); }
.plan-toggle button[data-plan="anual"].is-active { color: var(--brand-ink); }

/* ------------------------------------------------------------------ */
/* 4. CARD                                                            */
/* ------------------------------------------------------------------ */
.card { background: var(--surface-1); border: 1px solid var(--border-1); border-radius: var(--radius-card); transition-property: background, border-color; transition-duration: 200ms; transition-timing-function: var(--ease); }
.card:hover { border-color: var(--border-2); }
/* card com filho full-bleed (tabela/heat) como 1º elemento, sem card__head no topo: recorta o conteúdo
   no border-radius pra o canto reto do filho (thead colorido, células heat) não vazar sobre o card.
   Vai no card EXTERNO (não rola) -> o .table-wrap/overflow-x interno segue rolando e o sticky, ancorado
   no wrapper, não regride. Escopado (não global) pra não cortar anel de foco de inputs em tabela editável. */
.card--clip { overflow: hidden; }
.card__head { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 16px 20px; border-bottom: 1px solid var(--border-1); }
.card__head h3 { font-size: 13px; font-weight: 500; color: var(--text-2); }
.card__head .muted { font-size: 12px; color: var(--text-3); }
.card__body { padding: 20px; }
.card__body--flush { padding: 0; }

/* ------------------------------------------------------------------ */
/* 5. KPI                                                             */
/* ------------------------------------------------------------------ */
.card { min-width: 0; }
/* container-type: o .kpi vira container de consulta -> o VALOR reage à largura REAL do card (não da tela) */
.kpi { padding: 18px 18px 16px; display: flex; flex-direction: column; min-height: 138px; min-width: 0; container-type: inline-size; }
.kpi__label { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; font-size: 12px; font-weight: 500; color: var(--text-2); }
.kpi__label svg { width: 14px; height: 14px; color: var(--text-3); }
/* NÚMERO endurecido: NUNCA quebra (nowrap); dígitos tabulares; a fonte encolhe por degrau quando o card aperta */
.kpi__value { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 21px; font-weight: 500; color: var(--text-1); letter-spacing: -.4px; line-height: 1.15; margin-bottom: 8px; white-space: nowrap; }
.kpi__label { min-width: 0; }
.kpi__label span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.kpi__value small { font-size: 13px; color: var(--text-3); }
/* card apertado (modais, colunas estreitas) -> reduz a FONTE do número, nunca quebra. Grids padrão (>=200px) ficam 21px. */
@container (max-width: 190px) { .kpi__value { font-size: 18px; } }
@container (max-width: 166px) { .kpi__value { font-size: 15px; } }
@container (max-width: 144px) { .kpi__value { font-size: 13px; } }
.kpi__foot { margin-top: auto; padding-top: 4px; display: flex; align-items: center; gap: 8px; font-size: 11px; color: var(--text-3); }

/* pills de variação (idênticas ao sistema atual) */
.pill { display: inline-flex; align-items: center; gap: 3px; font-size: 11px; font-weight: 500; padding: 2px 7px; border-radius: var(--radius-pill); line-height: 1.4; font-family: var(--font-mono); font-variant-numeric: tabular-nums; border: 1px solid transparent; }
.pill svg { width: 11px; height: 11px; }
.pill.up { color: var(--green); background: var(--green-soft); border-color: var(--green-border); }
.pill.dn { color: var(--red); background: var(--red-soft); border-color: var(--red-border); }
.pill.flat { color: var(--text-3); background: var(--field-bg); border-color: var(--border-1); }

/* KPI colorido por natureza do valor */
.kpi--pos .kpi__value, .kpi--pos .kpi__label svg { color: var(--green); }
.kpi--neg .kpi__value, .kpi--neg .kpi__label svg { color: var(--red); }
.kpi--brand .kpi__value { color: var(--brand-ink); }

/* Resumo do detalhe do cliente (7 cards) — modal COMPACTO. O número NUNCA quebra (nowrap) e a
   fonte cai pra 15px pro maior valor ("R$ 689,70" / a data "09/01/2027") caber no card estreito
   (~139px, ~7 numa fileira em ~1080px). 7 numa fileira no desktop; empilha só no tablet/mobile.
   Escopado em .cli-resumo -> os KPIs da lista (kpi-grid) seguem em 21px. */
.cli-resumo { display: grid; grid-template-columns: repeat(7, 1fr); gap: 10px; margin-bottom: 16px; }
.cli-resumo .kpi { min-height: 0; padding: 12px 10px 10px; overflow: hidden; }
.cli-resumo .kpi__label { margin-bottom: 7px; }
.cli-resumo .kpi__value { font-size: 15px; white-space: nowrap; margin-bottom: 5px; }
@media (max-width: 1023px) { .cli-resumo { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 560px)  { .cli-resumo { grid-template-columns: repeat(2, 1fr); } }

/* Card de DADOS do cliente (Plano/Status/Vencimento · CPF/Telefone/Email) — 3×2 compacto.
   Email é uma célula normal (~320px no modal de 1080) -> 40 chars cabem numa linha, JUNTO dos
   demais (nunca isolado). Gap enxuto. Compartilhado Base+Atendimento. Mobile empilha. */
.cli-dados { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px 16px; }
@media (max-width: 640px) { .cli-dados { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 380px) { .cli-dados { grid-template-columns: 1fr; } }

/* Renovações — 2 colunas (taxa por plano · tipo de renovação); empilha no mobile. */
.split-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-bottom: 12px; }
@media (max-width: 720px) { .split-2 { grid-template-columns: 1fr; } }

/* Tipo de renovação — donut CSS-SVG inline (Manteve/Upgrade/Downgrade), sem chart lib. */
.trans-donut { display: flex; align-items: center; gap: 22px; flex-wrap: wrap; }
.trans-donut__chart { position: relative; width: 128px; height: 128px; flex: none; }
.trans-donut__chart svg { transform: rotate(-90deg); }
.trans-donut__track { fill: none; stroke: var(--surface-2); stroke-width: 16; }
.trans-donut__seg { fill: none; stroke-width: 16; stroke-linecap: butt; cursor: pointer; transition: stroke-dasharray .55s cubic-bezier(.4, 0, .2, 1), opacity .15s ease; }
/* o centro (valor) sobrepõe o svg — precisa DEIXAR o mouse passar pro anel, senão o hover da fatia não pega */
.trans-donut__center { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 1px; pointer-events: none; }
/* hover: esmaece as outras fatias, destaca a de baixo do cursor (o tooltip vem do donutHover no app.js) */
.trans-donut__chart svg:hover .trans-donut__seg { opacity: .38; }
.trans-donut__chart .trans-donut__seg:hover { opacity: 1; }
/* tooltip da fatia — segue o cursor (position:fixed no JS), fundo/texto do design system, não bloqueia o mouse */
.donut-tip { position: fixed; z-index: 3000; pointer-events: none; display: flex; align-items: center; gap: 7px; flex-wrap: wrap; max-width: 260px; padding: 7px 10px; border-radius: 8px; background: var(--elevated); border: 1px solid var(--border-2); color: var(--text-1); font-size: 12.5px; line-height: 1.35; box-shadow: 0 6px 22px rgba(0, 0, 0, .28); }
.donut-tip[hidden] { display: none; }
.donut-tip__dot { width: 9px; height: 9px; border-radius: 3px; flex: none; }
.donut-tip b { font-family: var(--font-mono); font-weight: 600; }
.donut-tip__pct { color: var(--text-3); }
.trans-donut__center b { font-family: var(--font-mono); font-weight: 600; font-size: 22px; line-height: 1; color: var(--text-1); white-space: nowrap; } /* font-size é fallback; donutCenter() sobrescreve inline p/ caber no furo */
.trans-donut__center span { font-size: 10px; letter-spacing: .05em; text-transform: uppercase; color: var(--text-3); }
.trans-donut__legend { flex: 1; min-width: 148px; display: flex; flex-direction: column; gap: 10px; }
.trans-leg { display: flex; align-items: center; gap: 9px; font-size: 13px; color: var(--text-2); }
.trans-leg__dot { width: 9px; height: 9px; border-radius: 3px; flex: none; }
.trans-leg__val { margin-left: auto; font-family: var(--font-mono); color: var(--text-1); }
.trans-leg__pct { color: var(--text-3); font-size: 12px; min-width: 34px; text-align: right; }
/* MOBILE (<=900px, max-width -> desktop byte-idêntico): empilha o donut — chart em cima, legenda FULL-WIDTH embaixo.
   Resolve Visão Geral/Receita/Vendas/Fidelidade de uma vez. A legenda full-width tira o aperto que esmagava o rótulo
   (o margin-left:auto do valor + min-width do % passam a ter folga de sobra). */
@media (max-width: 900px) {
  .trans-donut { flex-direction: column; align-items: center; gap: 16px; }
  .trans-donut__legend { width: 100%; min-width: 0; flex: none; }
}

/* Tráfego — barras de vendas por dia (CSS puro, sem chart lib); rola no horizontal se muitos dias. */
.daybars { display: flex; align-items: flex-end; gap: 4px; height: 140px; overflow-x: auto; padding-bottom: 4px; }
.daybars__col { flex: 1 0 14px; display: flex; flex-direction: column; align-items: center; justify-content: flex-end; height: 100%; gap: 5px; cursor: pointer; }
.daybars__bar { width: 60%; max-width: 22px; min-height: 4px; background: var(--brand-ink); border-radius: 3px 3px 0 0; transition: height .5s cubic-bezier(.4, 0, .2, 1), opacity .15s ease; }
.daybars__lbl { font-size: 10px; color: var(--text-3); white-space: nowrap; transform: rotate(-45deg); transform-origin: center; }
/* hover na barra: esmaece as outras, destaca a de baixo do cursor (tooltip = o mesmo dos donuts) */
.daybars:hover .daybars__bar { opacity: .4; }
.daybars__col:hover .daybars__bar { opacity: 1; }
/* modificador: barras encolhem p/ caber (ex 24h em meia-largura) SEM scroll — só onde pedido (fit); o .daybars base fica rolável */
.daybars--fit { overflow: hidden; }   /* ambos os eixos: overflow-x:hidden sozinho força overflow-y:auto -> scroll vertical fantasma */
.daybars--fit .daybars__col { flex: 1 1 0; min-width: 0; }

/* Atribuição — drill de clientes por canal (sub-linha expandível da tabela de performance). */
.drill-cell { padding: 10px 14px !important; background: var(--surface-2); }
.drill-cli { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; width: 100%; text-align: left; background: none; border: 0; border-bottom: 1px solid var(--border-1); color: var(--text-1); cursor: pointer; padding: 7px 2px; font-size: 13px; }
.drill-cli:last-child { border-bottom: 0; }
.drill-cli:hover { color: var(--brand-ink, var(--brand)); }


/* Visão Geral (home) — link de bloco (→ detalhe), faixa de ação (neutra, não o vermelho do .alertbar), marcos. */
.ov-link { font-size: 12px; color: var(--text-3); text-decoration: none; white-space: nowrap; }
.ov-link:hover { color: var(--brand-ink, var(--brand)); }
.ov-alertbar { flex-wrap: wrap; gap: 10px 16px; }   /* só o LAYOUT de múltiplos itens (a cor vem de .alertbar--warn); usada só pela faixa "Atenção" da Visão Geral */
.ov-acao { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; color: var(--text-2); text-decoration: none; }
.ov-acao:hover { color: var(--text-1); }

/* Fidelidade (RFM) — cards por faixa, clicáveis (drill-down). */
.seg-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-top: 14px; }
.seg-card { cursor: pointer; }
.seg-card:hover, .seg-card.is-active { border-color: var(--brand-border); }

/* ------------------------------------------------------------------ */
/* 6. BOTÕES                                                          */
/* ------------------------------------------------------------------ */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 7px;
  height: 40px; padding: 0 16px; border-radius: var(--radius-input);
  font-size: 13px; font-weight: 500; border: 1px solid transparent; white-space: nowrap;
  transition-property: background, border-color, transform, color; transition-duration: 150ms; transition-timing-function: var(--ease);
}
.btn svg { width: 15px; height: 15px; }
.btn:active { transform: scale(.98); }
.btn--primary { background: var(--brand); color: var(--on-brand); }
.btn--primary:hover { background: var(--brand-hover); }
.btn--outline { background: transparent; border-color: var(--border-1); color: var(--text-2); }
.btn--outline:hover { background: var(--surface-1); border-color: var(--border-2); color: var(--text-1); }
.btn.is-copied { border-color: var(--green-border); }        /* feedback de "copiado" */
.btn.is-copied svg { color: var(--green); }                  /* SÓ o ícone (check) fica verde — o texto do botão mantém a cor */
.btn--ghost { background: transparent; color: var(--text-3); }
.btn--ghost:hover { background: var(--surface-1); color: var(--text-1); }
.btn--sm { height: 32px; padding: 0 12px; font-size: 12px; }
.btn--icon { width: 34px; height: 34px; padding: 0; }
.btn--danger-ghost { background: transparent; color: var(--text-3); }
.btn--danger-ghost:hover { background: var(--red-soft); color: var(--red); }
.btn--wa { background: transparent; border-color: var(--border-1); color: var(--whatsapp); }
.btn--wa:hover { background: var(--whatsapp-soft); border-color: var(--whatsapp); }
.btn--wa svg { color: var(--whatsapp); }

/* ------------------------------------------------------------------ */
/* 7. INPUTS                                                          */
/* ------------------------------------------------------------------ */
.field { display: flex; flex-direction: column; gap: 6px; }
.field label { font-size: 11px; color: var(--text-3); font-weight: 500; text-transform: uppercase; letter-spacing: .8px; }
/* stack vertical de campos (auditoria UI primitivas): gap único entre campos, elimina os margin-bottom inline soltos */
.field-stack { display: flex; flex-direction: column; gap: 12px; }
.field-stack > .errbox { margin-top: 0; }   /* dentro do stack o gap cuida do espaço; fora, o .errbox mantém seu margin-top */
.field-stack > .alertbar { margin-bottom: 0; }   /* idem p/ barra de aviso dentro do stack (ex.: aviso de contraste na Identidade) */
.input, .select {
  height: 40px; width: 100%; padding: 0 12px;
  background: var(--field-bg); border: 1px solid var(--border-1); border-radius: var(--radius-input);
  color: var(--text-1); font-family: inherit; font-size: 13px; outline: none; color-scheme: var(--form-scheme);
  transition: border-color 180ms var(--ease), box-shadow 180ms var(--ease);
}
.input:hover, .select:hover { border-color: var(--border-hover); }
.input:focus, .select:focus { border-color: var(--brand-ink); box-shadow: 0 0 0 3px var(--brand-soft); }
.input::placeholder { color: var(--text-4); }
input[type="range"] { accent-color: var(--brand); }   /* slider (ex.: tamanho do logo na Identidade) na cor da marca */
.input.num { text-align: right; font-family: var(--font-mono); }
/* textarea NÃO herda a altura fixa de linha única do .input — cresce pelo atributo rows e respira no padding.
   textarea.input (0,1,1) > .input (0,1,0) e só casa em <textarea> → os <input>/<select> seguem 40px intactos. */
textarea.input { height: auto; min-height: 40px; padding: 8px 12px; line-height: 1.5; resize: vertical; }
.select { appearance: none; padding-right: 30px; background-image: var(--select-arrow); background-repeat: no-repeat; background-position: right 12px center; }
/* menu do <select> nativo segue o tema — POR TAG, cobre TODO <select> do painel (não só .select: o modal
   CMS usa .input, e há selects sem classe). color-scheme faz o popup nativo respeitar o tema em qualquer
   navegador; option opaco reforça onde o navegador estiliza o menu (Firefox/Chrome). --elevated é OPACO
   (nunca white-alpha) -> nada de menu branco com texto claro ilegível. */
select { color-scheme: var(--form-scheme); }
select option, select optgroup { background: var(--elevated); color: var(--text-1); }
/* Widget de período (seletor + range personalizado). O range esconde fora do 'custom'. */
.periodo-wrap { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.periodo-range { display: inline-flex; align-items: center; gap: 6px; }
.periodo-range[hidden] { display: none; }
.input--date { width: auto; height: 36px; padding: 0 10px; font-size: 12px; }
.periodo-sep { color: var(--text-3); }

/* Lançamentos (custos) — botões-link na tabela, linha anulada, grid de parâmetros, histórico. */
.lnk { background: none; border: 0; padding: 0 2px; margin: 0 3px; font: inherit; font-size: 12px; color: var(--brand-ink, var(--brand)); cursor: pointer; }
.lnk:hover { text-decoration: underline; }
.lnk--danger { color: var(--red); }
.lanc-acoes { white-space: nowrap; }
/* grupo de botões de AÇÃO numa célula de tabela (molde): flex OCUPA a célula + justify flex-end (alinha à
   direita, concordando com o cabeçalho .num) + wrap se não couber. inline-flex NÃO alinhava (largura=conteúdo). */
.tbl-acoes { display: flex; gap: 6px; justify-content: flex-end; flex-wrap: wrap; }
tr.is-anulado { opacity: .5; }
tr.is-anulado .cell-strong { text-decoration: line-through; }
.paramgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(152px, 1fr)); gap: 14px; }
/* label no topo, input SEMPRE no rodapé da célula -> inputs alinham entre colunas mesmo com label de 1 ou 2 linhas */
.paramgrid .field .input, .paramgrid .field .select { margin-top: auto; }
.hist { display: flex; flex-direction: column; gap: 10px; max-height: 50vh; overflow-y: auto; }
.hist__row { border-left: 2px solid var(--border-2); padding-left: 10px; }
.hist__top { display: flex; justify-content: space-between; gap: 10px; align-items: baseline; font-size: 13px; }

/* Custos — linha de fechamento (Receita − Custos = Lucro). */
.fechamento { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
.fech__item { display: flex; flex-direction: column; gap: 3px; }
.fech__l { font-size: 12px; color: var(--text-3); }
.fech__v { font: 500 22px/1.1 var(--font-mono); color: var(--text-1); }
.fech__v.pos { color: var(--green); }
.fech__v.neg { color: var(--red); }
.fech__sub { font-size: 11px; color: var(--text-3); }
.fech__op { font: 400 20px/1 var(--font-mono); color: var(--text-3); }
.fech__item--hero .fech__v { font-size: 27px; }

/* Projeções — premissa + progresso + faixa (conservador/provável/otimista). */
.proj-premissa { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; font-size: 13px; color: var(--text-2); }
.proj-premissa svg { width: 15px; height: 15px; color: var(--brand-ink); vertical-align: -2px; }
.proj-prog { display: flex; align-items: center; gap: 10px; min-width: 180px; }
.proj-prog__bar { flex: 1; height: 8px; border-radius: 99px; background: var(--surface-2); overflow: hidden; }
.proj-prog__bar span { display: block; height: 100%; background: var(--brand-ink); border-radius: 99px; }
.faixa { display: grid; grid-template-columns: repeat(3, 1fr); gap: 12px; }
.faixa__col { border: 1px solid var(--border-1); border-radius: 12px; padding: 14px 16px; text-align: center; }
.faixa__col.faixa--prov { border-color: var(--brand-border); background: var(--brand-soft); }
.faixa__t { font-size: 12px; color: var(--text-3); font-weight: 500; margin-bottom: 8px; }
.faixa__rec { font: 500 20px/1.1 var(--font-mono); color: var(--text-1); }
.faixa__luc { font: 500 17px/1.1 var(--font-mono); color: var(--green); margin-top: 8px; }
.faixa__cap { font-size: 11px; color: var(--text-3); margin-top: 1px; }
/* Projeção reformada: herói (lucro provável) + banda visual (conservador→otimista, marcador no provável). */
.proj-hero { margin-bottom: 12px; }
.proj-hero__grid { display: flex; justify-content: space-between; align-items: flex-start; gap: 24px; flex-wrap: wrap; padding: 20px 20px 4px; }
.proj-hero__eyebrow { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-3); }
.proj-hero__eyebrow svg { width: 14px; height: 14px; color: var(--brand-ink); }
.proj-hero__val { font: 600 40px/1 var(--font-mono); color: var(--green); margin: 10px 0 5px; letter-spacing: -.5px; }
.proj-hero__sub { font-size: 13px; color: var(--text-2); }
.proj-hero__side { text-align: right; min-width: 190px; }
.proj-hero__ritmo { font-size: 13px; color: var(--text-2); }
.proj-hero__side .proj-prog { min-width: 0; }
.proj-hero__band { padding: 4px 20px 20px; }
.proj-band__head { padding: 0 0 8px; }
.proj-band { padding: 4px 0; }
.proj-band__labels { display: flex; justify-content: space-between; align-items: flex-end; margin-bottom: 9px; gap: 8px; }
.proj-band__l { display: flex; flex-direction: column; font-size: 14px; color: var(--text-2); }
.proj-band__l b { font-family: var(--font-mono); font-weight: 500; }
.proj-band__l small { font-size: 10px; color: var(--text-3); text-transform: uppercase; letter-spacing: .4px; margin-top: 2px; }
.proj-band__l--mid { align-items: center; }
.proj-band__l--mid b { font-size: 18px; font-weight: 600; color: var(--text-1); }
.proj-band__l--r { align-items: flex-end; }
.proj-band__track { position: relative; height: 10px; border-radius: 99px; background: linear-gradient(90deg, var(--amber-soft), var(--green-soft)); border: 1px solid var(--border-1); }
.proj-band__mark { position: absolute; top: -3px; width: 4px; height: 16px; border-radius: 3px; background: var(--brand-ink); transform: translateX(-50%); box-shadow: 0 0 0 3px var(--brand-soft); }
.daybars__col--proj .daybars__bar { background: repeating-linear-gradient(45deg, var(--brand-ink) 0 2px, transparent 2px 5px); opacity: .55; }

/* ---- Campanhas de SMS (admin) ---- */
.sms-list { display: flex; flex-direction: column; gap: 12px; }
.sms-camp__top { display: flex; justify-content: space-between; align-items: flex-start; gap: 14px; }
.sms-camp__id b { font-size: 15px; }
.sms-camp__tags { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.sms-camp__tpl { margin: 12px 0; padding: 10px 12px; background: var(--surface-2); border-radius: 8px; font-size: 12.5px; color: var(--text-2); white-space: pre-wrap; word-break: break-word; }
.sms-camp__foot { display: flex; justify-content: space-between; align-items: center; gap: 12px; flex-wrap: wrap; }
.sms-camp__acoes { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
/* switch ativa/inativa (kill-switch) */
/* position:relative OBRIGATÓRIO: o input abaixo é position:absolute; sem um ancestral posicionado ele
   se ancora no DOCUMENTO (ICB) e, numa LISTA LONGA de toggles, estica o body -> scroll fantasma que
   arrasta a sidebar. Ancorando o input no próprio label, o componente fica imune (input é 0×0 invisível,
   então isto não muda o layout de nenhuma tela que já usa o switch). */
.sms-switch { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; user-select: none; position: relative; }
.sms-switch input { position: absolute; opacity: 0; width: 0; height: 0; }
.sms-switch__tk { position: relative; width: 38px; height: 22px; border-radius: 99px; background: var(--surface-3, rgba(255,255,255,.12)); border: 1px solid var(--border-2); transition: background .18s var(--ease); flex: 0 0 auto; }
.sms-switch__tk::after { content: ''; position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%; background: var(--text-2); transition: transform .18s var(--ease); }
.sms-switch input:checked + .sms-switch__tk { background: color-mix(in srgb, var(--green) 55%, transparent); border-color: transparent; }
.sms-switch input:checked + .sms-switch__tk::after { transform: translateX(16px); background: #fff; }
.sms-switch__lb { font-size: 12px; color: var(--text-2); font-weight: 500; }

/* Gestão de permissões (tela Usuários · dono-only) — grupos de telas com toggle .sms-switch reusado. */
.perm-grp { margin-bottom: 14px; }
.perm-grp__title { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .6px; color: var(--text-3); padding-bottom: 6px; border-bottom: 1px solid var(--border-1); margin-bottom: 4px; }
.perm-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 7px 2px; font-size: 13px; color: var(--text-1); }
.perm-row + .perm-row { border-top: 1px solid var(--border-1); }
/* seções do modal Editar consolidado (dados · permissões · senha) — divisória + título de seção. */
.perm-sec { border-top: 1px solid var(--border-1); margin-top: 14px; padding-top: 12px; }
.perm-sec__t { font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .6px; color: var(--text-3); margin-bottom: 8px; }

/* Page Builder — tela "Layout da home". Drag por Pointer Events pelo grip (mouse + touch); botões ↑/↓
   são o fallback. (O scroll fantasma foi corrigido NA RAIZ: position:relative no .sms-switch global.) */
.lh-list tbody tr.lh-drag { opacity: .45; }
/* grip + ↑ + ↓ SEMPRE numa linha (o molde .tbl-acoes é flex-wrap:wrap -> a seta caía pra baixo quando o
   grip somou largura). nowrap trava; a coluna (150px no colgroup) foi alargada pra caber com folga. */
.lh-list .tbl-acoes { flex-wrap: nowrap; }
.lh-list tbody tr.lh-over td { box-shadow: inset 0 2px 0 0 var(--brand-ink); }
.lh-grip { display: inline-flex; align-items: center; color: var(--text-4); cursor: grab; padding: 2px; touch-action: none; }
.lh-grip:active { cursor: grabbing; }
/* amostra do disparo (DRY) */
.sms-amo-wrap { display: flex; flex-direction: column; gap: 8px; }
.sms-amo { padding: 9px 11px; background: var(--surface-2); border-radius: 8px; }
.sms-amo__h { display: flex; justify-content: space-between; gap: 10px; font-size: 12.5px; margin-bottom: 3px; }
.sms-amo__t { font-size: 12px; color: var(--text-2); white-space: pre-wrap; word-break: break-word; }

/* ---- Alimentar Códigos (retorno com listas expansíveis) ---- */
.alim-det { margin: 3px 0; }
.alim-det summary { cursor: pointer; font-size: 12px; color: var(--text-2); padding: 3px 0; }
.alim-codes { margin: 4px 0 8px; padding: 8px 10px; background: var(--surface-2); border-radius: 6px; font-size: 11px; line-height: 1.9; max-height: 160px; overflow: auto; word-break: break-all; }
.alim-pendbar { border-color: color-mix(in srgb, var(--amber) 40%, var(--border-1)); }
.alim-pendbar__t { display: flex; align-items: center; gap: 8px; font-size: 13px; font-weight: 600; margin-bottom: 10px; }
.alim-pendbar__t svg { color: var(--amber); }
.alim-pend { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; padding: 7px 0; font-size: 13px; border-top: 1px solid var(--border-1); }
.alim-pend:first-of-type { border-top: 0; }

/* ---- Vendas ao Vivo ---- */
.vivo-live { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--text-3); }
.vivo-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--green); box-shadow: 0 0 0 3px color-mix(in srgb, var(--green) 22%, transparent); animation: vivo-pulse 1.6s infinite; }
.vivo-live.is-paused .vivo-dot { background: var(--text-4); box-shadow: none; animation: none; }
@keyframes vivo-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }
.vivo-row--alerta > td { background: color-mix(in srgb, var(--red) 6%, transparent); }
.vivo-new { animation: vivo-rownew 2.2s ease-out; }
@keyframes vivo-rownew { 0% { background: rgba(255,255,255,.10); } 100% { background: transparent; } }   /* C248 P4: era `var(--brand) 20%` — o flash da linha nova pintava na marca */
.vivo-repor { display: flex; width: fit-content; margin-top: 6px; }   /* btn--sm cuida do resto; flex = fica ABAIXO do badge, tamanho do conteúdo */
.vivo-repor svg { width: 12px; height: 12px; }
.vivo-kpi-click { display: flex; cursor: pointer; }
.vivo-kpi-click > .card { width: 100%; transition: border-color .15s var(--ease); }
.vivo-kpi-click:hover > .card { border-color: color-mix(in srgb, var(--red) 45%, var(--border-1)); }
.alim-foco { border-color: var(--brand-ink) !important; box-shadow: 0 0 0 3px var(--brand-soft); }

/* Insights — cards "fato + link" por severidade (borda esquerda), estado "tudo sob controle". */
.ins-sec { margin-bottom: 18px; }
.ins-sec__t { font-size: 12px; font-weight: 500; color: var(--text-3); text-transform: uppercase; letter-spacing: .6px; margin-bottom: 10px; }
.ins-list { display: flex; flex-direction: column; gap: 8px; }
.ins-item { display: flex; align-items: center; gap: 14px; padding: 13px 16px; background: var(--surface-1); border: 1px solid var(--border-1); border-left-width: 3px; border-radius: 10px; text-decoration: none; transition: border-color 150ms var(--ease), background 150ms var(--ease); }
.ins-item:hover { background: var(--surface-2); border-color: var(--border-2); }
.ins-item--red { border-left-color: var(--red); }
.ins-item--amber { border-left-color: var(--amber); }
.ins-item--blue { border-left-color: var(--blue); }
.ins-item--green { border-left-color: var(--green); }
.ins-item__sev { flex: none; width: 78px; font-size: 11px; font-weight: 500; text-transform: uppercase; letter-spacing: .4px; color: var(--text-3); }
.ins-item--red .ins-item__sev { color: var(--red); }
.ins-item--amber .ins-item__sev { color: var(--amber); }
.ins-item--green .ins-item__sev { color: var(--green); }
.ins-item__txt { flex: 1; font-size: 14px; color: var(--text-1); }
.ins-item__go { flex: none; display: inline-flex; align-items: center; gap: 5px; font-size: 12px; color: var(--text-3); white-space: nowrap; }
.ins-item__go svg { width: 13px; height: 13px; }
.ins-item:hover .ins-item__go { color: var(--brand-ink, var(--brand)); }
.ins-ok { display: flex; align-items: center; gap: 14px; }
.ins-ok svg { color: var(--green); flex: none; }
.input-group { display: flex; align-items: center; background: var(--field-bg); border: 1px solid var(--border-1); border-radius: var(--radius-input); overflow: hidden; transition: border-color 180ms var(--ease), box-shadow 180ms var(--ease); }
.input-group:hover { border-color: var(--border-hover); }
.input-group:focus-within { border-color: var(--brand-ink); box-shadow: 0 0 0 3px var(--brand-soft); }
.input-group__prefix { padding: 0 10px; align-self: stretch; display: flex; align-items: center; color: var(--text-3); font-family: var(--font-mono); font-size: 13px; border-right: 1px solid var(--border-1); }
.input-group .input { border: 0; background: transparent; }
.input-group .input:focus { box-shadow: none; }

/* campo de busca com a lupa DENTRO do field (à esquerda). O input mantém borda/foco próprios;
   a lupa é sobreposta (pointer-events:none) e o padding-left evita o texto colar no ícone. */
.search-field { position: relative; display: flex; align-items: center; }
.search-field > svg { position: absolute; left: 11px; top: 50%; transform: translateY(-50%); width: 15px; height: 15px; color: var(--text-3); pointer-events: none; }
.search-field .input { width: 100%; padding-left: 34px; }

/* ------------------------------------------------------------------ */
/* 8. TABELA (densa, estilo do sistema)                               */
/* ------------------------------------------------------------------ */
.table-wrap { width: 100%; overflow-x: auto; }
.table { width: 100%; border-collapse: collapse; font-size: 13px; }
/* Listas de clientes (Base/Fidelidade/Churn) — colunas ESTÁVEIS entre abas/filtros/páginas:
   table-layout:fixed + <colgroup> (largura por coluna, dimensionada pro pior caso) faz a coluna NÃO
   depender do conteúdo -> não "anda" quando os dados mudam. O que estoura a coluna é cortado; a 1ª
   coluna (nome/e-mail) trunca com ellipsis. Cada tabela leva min-width -> se não couber, o .table-wrap
   rola (não espreme). Números seguem nowrap (regra global .table td.num). */
.table--fixed { table-layout: fixed; }
.table--fixed td { overflow: hidden; }
.table--fixed td:first-child { white-space: nowrap; text-overflow: ellipsis; }
.table--fixed td:first-child .cell-strong, .table--fixed td:first-child .cell-sub { display: block; overflow: hidden; text-overflow: ellipsis; }
.table thead th {
  position: sticky; top: 0; z-index: 1; background: var(--elevated);
  text-align: left; font-weight: 500; font-size: 11px; letter-spacing: .6px; text-transform: uppercase; color: var(--text-3);
  padding: 10px 16px; border-bottom: 1px solid var(--border-1); white-space: nowrap;
}
.table tbody td { padding: 11px 16px; border-bottom: 1px solid var(--border-1); vertical-align: middle; color: var(--text-1); }
.table tbody tr { transition: background 150ms var(--ease); }
.table tbody tr:hover { background: var(--surface-1); }
.table tbody tr:last-child td { border-bottom: 0; }
.table th.num, .table td.num { text-align: right; }
/* números NUNCA quebram na célula (lição da Base): ficam numa linha; se a tabela não couber,
   quem rola é o .table-wrap (overflow-x). */
.table td.num { white-space: nowrap; }
/* tabela com MUITAS colunas (ex.: Atribuição "Performance por canal", 8 col + LTV da safra grande):
   piso de largura -> em vez de espremer/quebrar, mantém as colunas legíveis e o .table-wrap rola.
   860px cobre o pior caso real medido (~831px: LTV safra R$ 296.189,53 + nº clientes 2.174). */
.table--wide { min-width: 860px; }
.cell-strong { font-weight: 500; }
.cell-sub { display: block; font-size: 11px; color: var(--text-3); margin-top: 1px; }
.cell-time { font-family: var(--font-mono); font-size: 12px; color: var(--text-3); }

/* tabela editável (Custos & Lançamentos) */
.cell-input { width: 100%; background: transparent; border: 1px solid transparent; border-radius: 6px; padding: 6px 8px; color: var(--text-1); font-family: inherit; font-size: 13px; outline: none; color-scheme: var(--form-scheme); transition: border-color 150ms var(--ease), background 150ms var(--ease); }
.cell-input:hover { border-color: var(--border-1); }
.cell-input:focus { border-color: var(--brand-ink); background: var(--field-bg); box-shadow: 0 0 0 3px var(--brand-soft); }
.cell-input.num { font-family: var(--font-mono); font-variant-numeric: tabular-nums; text-align: right; }
select.cell-input { appearance: none; color-scheme: var(--form-scheme); }
select.cell-input option, .select option { background: var(--elevated); color: var(--text-1); }
.table__addrow td { padding: 10px 16px; border-bottom: 0; }
input[type=checkbox] { accent-color: var(--brand-ink); width: 15px; height: 15px; cursor: pointer; }

/* ------------------------------------------------------------------ */
/* 9. BADGES (soft + colored border, como as pills do sistema)        */
/* ------------------------------------------------------------------ */
.badge { display: inline-flex; align-items: center; gap: 5px; padding: 2px 9px; border-radius: var(--radius-pill); font-size: 11px; font-weight: 500; line-height: 1.5; background: var(--field-bg); color: var(--text-2); border: 1px solid var(--border-1); }
.badge::before { content: ""; width: 5px; height: 5px; border-radius: 50%; background: currentColor; }
.badge--plain::before { display: none; }
.badge--green { color: var(--green); background: var(--green-soft); border-color: var(--green-border); }
.badge--red { color: var(--red); background: var(--red-soft); border-color: var(--red-border); }
.badge--brand { color: var(--brand-ink); background: var(--brand-soft); border-color: var(--brand-border); }
.badge--blue { color: var(--blue); background: var(--blue-soft); border-color: var(--blue-border); }
.badge--purple { color: var(--purple); background: var(--purple-soft); border-color: var(--purple-border); }
.badge--amber { color: var(--amber); background: var(--amber-soft); border-color: var(--amber-border); }
/* box de erro dos forms (auditoria UI): tokeniza o layout que era style inline; a cor vem do .badge--red, o display fica inline (toggle sem migração forçada) */
.errbox { width: 100%; justify-content: center; margin-top: 10px; padding: 8px; }

/* ------------------------------------------------------------------ */
/* 10. SEGMENTED / TABS                                               */
/* ------------------------------------------------------------------ */
.segmented { display: inline-flex; align-items: center; gap: 4px; background: var(--field-bg); border: 1px solid var(--border-1); border-radius: var(--radius-input); padding: 3px; }
.segmented button { background: transparent; border: 0; color: var(--text-3); padding: 6px 12px; border-radius: 5px; font-size: 12px; font-weight: 500; transition: background 150ms var(--ease), color 150ms var(--ease); }
.segmented button:hover { color: var(--text-1); }
.segmented button.is-active { background: var(--surface-3); color: var(--text-1); }

/* abas (auditoria UI primitivas #3): a .tabs órfã foi CALIBRADA pro look das abas das Campanhas (era gap:22 / button /
   texto ativo claro). Reproduz o inline de hoje ao pixel. Funciona com <a class="tab"> ou <button class="tab">. */
.tabs { display: flex; gap: 6px; border-bottom: 1px solid var(--border-1); margin-bottom: 18px; }
.tabs .tab { padding: 9px 16px; font-size: 13px; font-weight: 500; text-decoration: none; color: var(--text-3); background: none; border: 0; border-bottom: 2px solid transparent; margin-bottom: -1px; cursor: pointer; }
.tabs .tab.is-active { color: var(--brand-ink); border-bottom-color: var(--brand-ink); }

/* ------------------------------------------------------------------ */
/* 11. TOAST                                                          */
/* ------------------------------------------------------------------ */
/* ==========================================================================================
   ⭐⭐⭐ C250 §W · A NOTIFICAÇÃO ENTRA NO DESIGN-SYSTEM.
   ⛔ Ele: *"precisa aplicar o design-system às notificações do canto inferior direito, trocar a
      posição para o canto superior direito, e tirar ela do fundo, pq quando aparece ela fica
      apagada, como se estivesse atrás do conteúdo."*

   ⚠️ MEDIDO, e o "como se estivesse atrás" é LITERAL, não impressão:
       `.toast-wrap`  z-index **60**
       `.modal`       z-index **100**
     Com um modal aberto, `elementFromPoint` no centro do toast devolve **`DIV.modal`** — o
     overlay semitransparente do modal fica POR CIMA da notificação e a apaga. E é justamente aí
     que ela mais importa: quase todo toast nasce de uma ação feita DENTRO de um modal.

   ⭐⭐ A LEI DE CAMADA, escrita porque hoje os `z-index` são números soltos pelo arquivo:
       1 sticky de tabela · 30 menu do construtor · 40 overlay da barra · 50 barra lateral móvel
       60 popover (.pdd/.dtc) · 70/71 gaveta · 100 modal · 3000 dica do gráfico
       **4000 NOTIFICAÇÃO** — ela é a camada mais alta do painel, sem exceção.
     ⛔ A razão não é hierarquia por hierarquia: a notificação é o ÚNICO elemento que some
     sozinho. Se algo a cobre por dois segundos, a informação se perde e não volta.

   ⚠️ `pointer-events` fica em `none` na caixa e `auto` no cartão: a notificação passa a flutuar
     por cima de tudo, e uma faixa invisível de 20px no topo da tela bloqueando cliques seria um
     defeito pior que o que estou consertando. */
.toast-wrap {
  position: fixed; top: 20px; right: 20px; bottom: auto;
  display: flex; flex-direction: column; gap: 10px;
  z-index: 4000; pointer-events: none;
  max-width: min(380px, calc(100vw - 40px));
}
/* ⭐ a superfície segue a lei: `--elevated` (OPACA, porque flutua) e ANEL em vez de borda — o
   `.toast` era o último lugar do painel com `border: 1px solid` de contorno. */
.toast {
  display: flex; align-items: center; gap: 12px;
  min-width: 240px; max-width: 100%; padding: 12px 14px;
  background: var(--elevated); border: 0;
  box-shadow: inset 0 0 0 1px var(--border-1), 0 10px 30px rgba(0,0,0,.34);
  border-radius: var(--radius-card);
  pointer-events: auto;
  animation: toast-in .28s var(--ease) both;
  position: relative; overflow: hidden;
}
/* ⛔ a barra de estado era `border-left: 2px solid var(--green)` — **verde fixo**, inclusive em
   "Falha no upload". Medido: das 101 mensagens com texto literal, **35 são de falha** e todas
   saíam verdes. ⭐ Agora ela é um `::before` tingido pela VARIANTE, e o padrão continua o verde. */
.toast::before {
  content: ''; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--green);
}
.toast--erro::before { background: var(--red); }
.toast--aviso::before { background: var(--amber); }
.toast.is-out { animation: toast-out .2s var(--ease) both; }
.toast b { font-size: 13px; font-weight: 600; line-height: 18px; }
/* ⚠️ o subtítulo era 11px/400 — viola a L12 do próprio painel (até 12,5px exige peso ≥500).
   A régua não o pegava porque o toast só existe por 2,6s e nenhuma varredura o via aberto. */
.toast span { font-size: 11px; line-height: 14px; font-weight: 500; color: var(--text-3); }
.toast__txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.toast__txt b, .toast__txt span { overflow-wrap: anywhere; }
/* ⚠️ a entrada vinha DE BAIXO (`translateY(10px)`), que era coerente com o canto inferior.
   No canto superior isso fica errado: ela nasce vindo do conteúdo em vez de vir da borda.
   ⛔ `toast-in` também veste o `.modal` (app.css:1238) — por isso a inclinação é pequena e
   serve aos dois: para o modal ela é só um assentar. */
@keyframes toast-in { from{opacity:0;transform:translateY(-10px)} to{opacity:1;transform:none} }
@keyframes toast-out { to{opacity:0;transform:translateY(-6px)} }

/* ------------------------------------------------------------------ */
/* 12. SKELETON / EMPTY                                               */
/* ------------------------------------------------------------------ */
.skeleton { position: relative; overflow: hidden; background: var(--surface-2); border-radius: var(--radius-input); }
.skeleton::after { content: ""; position: absolute; inset: 0; transform: translateX(-100%); background: linear-gradient(90deg, transparent, var(--shimmer), transparent); animation: shimmer 1.4s infinite; }
@keyframes shimmer { to { transform: translateX(100%); } }
.state { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 8px; padding: 48px 24px; color: var(--text-3); }
.state svg { width: 30px; height: 30px; color: var(--text-4); }
.state b { color: var(--text-1); font-size: 14px; font-weight: 500; }
.state p { font-size: 12px; max-width: 340px; line-height: 1.6; }

/* ------------------------------------------------------------------ */
/* 13. ENTER STAGGER                                                  */
/* ------------------------------------------------------------------ */
.stagger > * { animation: fade-up .42s var(--ease) both; }
.stagger > *:nth-child(1){animation-delay:.02s}
.stagger > *:nth-child(2){animation-delay:.08s}
.stagger > *:nth-child(3){animation-delay:.14s}
.stagger > *:nth-child(4){animation-delay:.20s}
.stagger > *:nth-child(5){animation-delay:.26s}
.stagger > *:nth-child(6){animation-delay:.32s}
@keyframes fade-up { from{opacity:0;transform:translateY(8px)} to{opacity:1;transform:none} }

/* ------------------------------------------------------------------ */
/* 14. APEXCHARTS — overrides                                         */
/* ------------------------------------------------------------------ */
.apexcharts-text, .apexcharts-legend-text { fill: var(--text-3) !important; color: var(--text-3) !important; font-family: var(--font-sans) !important; }
.apexcharts-gridline { stroke: var(--border-1); }
.apexcharts-tooltip { background: var(--elevated) !important; border: 1px solid var(--border-2) !important; box-shadow: none !important; color: var(--text-1) !important; border-radius: var(--radius-input) !important; }
.apexcharts-tooltip-title { background: var(--tooltip-title-bg) !important; border-bottom: 1px solid var(--border-1) !important; font-family: var(--font-sans) !important; font-weight: 500; }
.apexcharts-xaxistooltip, .apexcharts-yaxistooltip { background: var(--elevated) !important; border-color: var(--border-2) !important; color: var(--text-1) !important; }
.apexcharts-xaxistooltip::after { border-bottom-color: var(--elevated) !important; }
.apexcharts-xaxistooltip::before { border-bottom-color: var(--border-2) !important; }

/* ------------------------------------------------------------------ */
/* 15. RESPONSIVO                                                     */
/* ------------------------------------------------------------------ */
@media (max-width: 1024px) {
  .app { grid-template-columns: 1fr; }
  .sidebar { position: fixed; inset: 0 auto 0 0; width: var(--sidebar-w); z-index: 50; transform: translateX(-100%); transition: transform .25s var(--ease); }
  .app.nav-open .sidebar { transform: none; }
  .app.nav-open::after { content: ""; position: fixed; inset: 0; background: var(--overlay); z-index: 40; }
  .hamburger { display: inline-flex; }
  .content { min-width: 0; }   /* 1 coluna: deixa o .content encolher p/ o scroll-x confinar NA tabela (.table-wrap), não arrastar a página */
  /* SCROLL ATÉ O FIM: usa a viewport DINÂMICA (dvh) no lugar de 100vh -> o fundo do .app não fica atrás da barra do
     navegador/gestos do celular (com 100vh o final da página some sob a barra e vira inalcançável). Desktop mantém
     o 100vh base (byte-idêntico); navegador sem suporte a dvh também cai no 100vh base (fallback). */
  .app { grid-template-rows: 100dvh; }
}
@media (max-width: 860px) {
  .status-dot { display: none; }
  .topbar { padding: 0 14px; gap: 10px; }
  .filterbar { padding: 10px 14px; gap: 10px; }
  .filterbar__sep { display: none; }
  .content__head { gap: 12px; }
}
@media (max-width: 560px) {
  .content { padding: 16px; padding-bottom: max(16px, env(safe-area-inset-bottom)); }   /* respira acima da barra de gestos (home indicator) */
  .filterbar .select { min-width: 0; flex: 1; }
  .filterbar__dates { width: 100%; }
  .filterbar__dates .input { flex: 1; min-width: 0; }
  .kpi-grid { grid-template-columns: repeat(2, 1fr); }
  /* piso de largura p/ as tabelas SEM min-width que espremiam no celular -> o .table-wrap ROLA em vez de grudar
     número no rótulo (mesmo mecanismo min-width das que já funcionam, mas escopado no @media = desktop byte-idêntico). */
  .table--m1 { min-width: 480px; }   /* 3-4 colunas */
  .table--m2 { min-width: 640px; }   /* 5-6 colunas */
}
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; } }

/* ------------------------------------------------------------------ */
/* 16. FAIXAS / RFM / MINI-BARRAS (Fase 3)                            */
/* ------------------------------------------------------------------ */
/* barra de força (score, taxa) dentro de célula de tabela */
.bar { position: relative; height: 5px; border-radius: 99px; background: var(--track); overflow: hidden; min-width: 60px; }
.bar__fill { position: absolute; inset: 0 auto 0 0; border-radius: 99px; background: var(--brand-ink); }
.bar__fill.g { background: var(--green); } .bar__fill.a { background: var(--amber); } .bar__fill.r { background: var(--red); }
.scorecell { display: flex; align-items: center; gap: 8px; }
.scorecell .num { min-width: 26px; }

/* quadradinhos R/F/M (1..5) */
.rfm { display: inline-flex; gap: 3px; }
.rfm span { width: 8px; height: 14px; border-radius: 2px; background: var(--track-alt); }
.rfm span.on { background: var(--brand-ink); }

/* linha de tabela clicável (abre o drawer) */
.table tbody tr.clickable { cursor: pointer; }

/* ------------------------------------------------------------------ */
/* 17. DRAWER (detalhe do cliente)                                    */
/* ------------------------------------------------------------------ */
.drawer-backdrop {
  position: fixed; inset: 0; background: var(--overlay-2); z-index: 70;
  opacity: 0; pointer-events: none; transition: opacity .2s var(--ease);
}
.drawer-backdrop.is-open { opacity: 1; pointer-events: auto; }
.drawer {
  position: fixed; top: 0; right: 0; bottom: 0; width: min(440px, 92vw); z-index: 71;
  background: var(--elevated); border-left: 1px solid var(--border-2);
  display: flex; flex-direction: column; overflow: hidden;
  transform: translateX(100%); transition: transform .26s var(--ease);
}
.drawer.is-open { transform: none; }
.drawer__head { display: flex; align-items: flex-start; gap: 12px; padding: 20px 22px; border-bottom: 1px solid var(--border-1); }
.drawer__head .grow { flex: 1; min-width: 0; }
.drawer__name { font-size: 16px; font-weight: 600; letter-spacing: -.2px; }
.drawer__sub { font-size: 12px; color: var(--text-3); margin-top: 3px; word-break: break-all; }
.drawer__close { flex-shrink: 0; }
.drawer__body { flex: 1; overflow-y: auto; padding: 20px 22px; display: flex; flex-direction: column; gap: 20px; }

.metricrow { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.metricrow .m { background: var(--surface-1); border: 1px solid var(--border-1); border-radius: var(--radius-input); padding: 12px; }
.metricrow .m .l { font-size: 10px; letter-spacing: .8px; text-transform: uppercase; color: var(--text-3); }
.metricrow .m .v { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 16px; font-weight: 500; margin-top: 4px; }

.drawer__section-title { font-size: 11px; letter-spacing: .8px; text-transform: uppercase; color: var(--text-3); font-weight: 500; margin-bottom: 12px; }

/* timeline de compras */
.timeline { position: relative; padding-left: 20px; }
.timeline::before { content: ""; position: absolute; left: 5px; top: 4px; bottom: 4px; width: 1px; background: var(--border-1); }
.timeline__item { position: relative; padding-bottom: 16px; }
.timeline__item:last-child { padding-bottom: 0; }
.timeline__dot { position: absolute; left: -18px; top: 3px; width: 9px; height: 9px; border-radius: 50%; background: var(--text-3); box-shadow: 0 0 0 3px var(--elevated); }
.timeline__dot.mensal { background: var(--blue); } .timeline__dot.trimestral { background: var(--purple); }
.timeline__dot.semestral { background: var(--amber); } .timeline__dot.anual { background: var(--brand-ink); }
.timeline__row { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.timeline__plan { font-size: 13px; font-weight: 500; }
.timeline__val { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 13px; }
.timeline__meta { font-size: 11px; color: var(--text-3); margin-top: 2px; display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }

/* ------------------------------------------------------------------ */
/* 18. PAGINADOR (Base de Clientes)                                   */
/* ------------------------------------------------------------------ */
.pager { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 14px 20px; border-top: 1px solid var(--border-1); flex-wrap: wrap; }
.pager:empty { display: none; }
.pager__info { font-size: 12px; color: var(--text-3); font-variant-numeric: tabular-nums; }
.btn[disabled] { opacity: .4; pointer-events: none; }

/* ------------------------------------------------------------------ */
/* 19. COHORT RETENTION (mapa de calor de safras)                     */
/* ------------------------------------------------------------------ */
/* MAPA de calor (não planilha): células como "tiles" com respiro (border-spacing), cantos suaves,
   cor RELATIVA ao intervalo real (JS + legenda). 1ª coluna sticky com sombra; linha Média com peso. */
.cohort-table { min-width: max-content; border-collapse: separate; border-spacing: 4px; }
.cohort-table th, .cohort-table td { text-align: center; white-space: nowrap; border: 0; }
.cohort-table thead th { padding: 2px 10px 8px; font-size: 11px; font-weight: 500; color: var(--text-3); }
.cohort-cell { min-width: 66px; height: 44px; padding: 0 8px; border-radius: 9px; font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-weight: 600; font-size: 13.5px; }
.cohort-cell--empty { background: var(--surface-2); color: var(--text-4); font-weight: 400; }
.cohort-sticky { position: sticky; left: 0; z-index: 2; text-align: left !important; background: var(--surface-1); padding: 0 16px 0 4px; font-size: 12px; box-shadow: 7px 0 9px -7px rgba(0,0,0,.30); }
.cohort-table thead .cohort-sticky { z-index: 3; background: var(--elevated); box-shadow: none; }
.cohort-media td { border-top: 2px solid var(--border-2); padding-top: 4px; }
.cohort-media .cohort-sticky, .cohort-media .num { font-weight: 700; color: var(--text-1); }
.cohort-table tbody tr:hover { background: transparent; }
/* legenda da escala — a cor é RELATIVA ao intervalo real da matriz (evita ler um vermelho reescalado como "péssimo") */
.cohort-legend { display: flex; gap: 16px; align-items: center; flex-wrap: wrap; margin-bottom: 14px; font-size: 12px; color: var(--text-3); }
.cohort-legend__scale { display: inline-flex; align-items: center; gap: 8px; }
.cohort-legend__end { font-family: var(--font-mono); font-weight: 600; color: var(--text-2); }
.cohort-legend__grad { display: inline-block; width: 130px; height: 9px; border-radius: 5px; background: linear-gradient(90deg, var(--red), var(--amber), var(--green)); }

/* ---------- modal de confirmação (ação sensível / gasto de créditos) ---------- */
.modal { position: fixed; inset: 0; z-index: 100; display: flex; align-items: center; justify-content: center; padding: 20px; background: var(--overlay); animation: toast-in .18s var(--ease) both; }
/* MOLDE-BASE (auditoria UI): TODO modal é viewport-aware por padrão — o card limita à altura da tela e o
   scroll fica SÓ no corpo; título e rodapé/botões ficam FIXOS (o rodapé nunca é empurrado pra fora da tela).
   Em tela grande, cabe e nada muda. dvh (Safari/mobile) com fallback vh. */
.modal__card { background: var(--elevated); border: 1px solid var(--border-2); border-radius: var(--radius-card); padding: 22px; max-width: 440px; width: 100%; box-shadow: 0 8px 28px rgba(0,0,0,.22); max-height: calc(100vh - 40px); max-height: calc(100dvh - 40px); display: flex; flex-direction: column; }
.modal__card > h3, .modal__card > .modal__head, .modal__card > .modal__foot, .modal__card > .pf-head, .modal__card > .pf-trilha { flex-shrink: 0; }   /* título e rodapé fixos */
/* ⭐ §L · o cabeçalho: título à esquerda, estado do registro à direita. ⚠️ O `h3` dentro dele
   perde a margem própria — quem dá o respiro passa a ser a faixa, senão o toggle fica alto em
   relação ao texto. */
/* ⛔ CORREÇÃO DELE: *"nos dois que pedi pra mover o toggle para a parte superior direita você
   deve alterar para a parte superior ESQUERDA, ao lado do título do modal."*
   ⭐ E é melhor mesmo: encostado no título, o estado se lê COMO PARTE do título ("Editar link ·
   Ativo"); jogado na outra ponta, ele virava um controle solto competindo com o X de fechar.
   ⚠️ `align-items: baseline` não serve aqui — o toggle não tem linha de base compatível com o
   título e ficaria alto; o alinhamento é pelo CENTRO, e o `gap` dá a separação. */
.modal__head { display: flex; align-items: center; justify-content: flex-start; gap: 14px; margin-bottom: 10px; flex-wrap: wrap; }
.modal__head > h3 { margin-bottom: 0; }
.modal__card > .modal__body { flex: 1 1 auto; overflow-y: auto; min-height: 0; }   /* só o corpo rola */
/* variante AMPLA: detalhe do cliente respira na largura da tela (desktop largo) e empilha no mobile.
   Só o modal do detalhe usa — os modais estreitos (Usuários etc.) seguem em 440px. */
.modal__card--wide { max-width: min(1080px, 92vw); }
.modal__card--mid { max-width: min(540px, 94vw); }   /* modais com linha de KPIs (ex.: disparar SMS) — dá largura pros números */
.modal__card--cms { max-width: min(620px, 94vw); }      /* form CMS de seção simples (hero, features, depoimentos) */
.modal__card--cms-lg { max-width: min(960px, 94vw); }   /* form CMS de seção com lista aninhada (tutoriais) — 3 níveis pedem largura */
/* ⭐⭐⭐ C237 · A CAIXA DO CMS DE SEÇÃO TEM TAMANHO. Antes ela não tinha altura própria e crescia com o
   conteúdo: MEDIDO no DOM, 13 seções davam 620 de largura e NOVE alturas (451 … 1265) e NOVE posições
   do botão Salvar (815 … 1222). O 1265 é o teto `100dvh - 40` desta tela — ou seja, cinco seções já
   batiam na viewport.
   ⭐ O 1080 não é escolha minha: já existia como `--wide`, e é o maior degrau de modal do sistema.
     1080 ÷ 16 × 9 = 607,5 → 608.
   ⭐⭐ DUAS propriedades fecham quatro invariantes, e ZERO media query:
     · `aspect-ratio` dá a razão 16:9 e, com largura constante, a MESMA altura em todas as seções;
     · `min-height` é onde a razão CEDE — quando 16:9 daria menos que 608 (tela estreita), o piso
       vence e o card cresce; quando a viewport é menor que 608, o `min()` interno cede também.
   ⛔ Por medida, não por breakpoint: a nav de blocos empilha em 560, mas o corte da razão NÃO é
      esse — MEDIDO, 16:9 fica inútil por volta de 435px de viewport (é onde a altura resultante cai
      abaixo de cabeçalho 19 + dica 37 + rodapé 40 + paddings 64 + uma linha de campo). Um breakpoint
      em 560 estaria errado nos dois sentidos; o `min-height` acerta sozinho, em qualquer tela.
   ⚠️ Especificidade DE PROPÓSITO em (0,2,0)/(0,3,0): a base `.modal__card` é (0,1,0) e
      `.modal__card > .modal__body` é (0,2,0) — empatar e depender da ordem do arquivo é como se
      perdem divisórias. */
.modal__card.modal__card--cms16 { max-width: min(1080px, 94vw); width: min(1080px, 94vw); aspect-ratio: 16 / 9; min-height: min(608px, calc(100dvh - 40px)); max-height: calc(100dvh - 40px); }
/* o corpo deixa de ser QUEM rola e passa a ser quem DISTRIBUI: a dica fica parada (o lote pede título,
   dica e rodapé imóveis) e o miolo é o único que rola. */
.modal__card.modal__card--cms16 > .modal__body { display: flex; flex-direction: column; min-height: 0; overflow: hidden; margin-bottom: 16px; }
.modal__card.modal__card--cms16 > .modal__body > .dim,
.modal__card.modal__card--cms16 > .modal__body > .errbox { flex: 0 0 auto; }
.modal__card.modal__card--cms16 > .modal__body > .cms-root { flex: 1 1 auto; min-height: 0; overflow-y: auto; }
/* com blocos (C233): a nav é COLUNA PRÓPRIA e rola sozinha; quem rola o conteúdo é o painel do bloco.
   ⛔ `position: static` tira o `sticky`: com a coluna de altura própria ele não tem mais o que resolver. */
.modal__card.modal__card--cms16 > .modal__body > .cms-blocos { flex: 1 1 auto; min-height: 0; align-items: stretch; }
.modal__card.modal__card--cms16 .cms-blocos > .cms-bnavs { position: static; min-height: 0; overflow-y: auto; }
.modal__card.modal__card--cms16 .cms-blocos > .cms-root { min-height: 0; overflow-y: auto; }
.modal__card h3 { font-size: 16px; margin-bottom: 10px; }
.modal__body { font-size: 13px; color: var(--text-2); line-height: 1.55; margin-bottom: 20px; }
.modal__foot { display: flex; justify-content: flex-end; gap: 8px; }
/* a --scroll foi ABSORVIDA pela base (todo card já é viewport-aware); a classe fica como no-op p/ quem ainda a passa (cmsAbrir) */
/* mobile estreito: o card usa mais da tela e respeita a safe-area (notch/barra inferior) */
@media (max-width: 480px) {
  .modal { padding: 12px; padding-bottom: max(12px, env(safe-area-inset-bottom)); }
  .modal__card { padding: 16px; }
  /* Insights: o item (severidade · texto · fonte→) é 3 colunas fixas que não cabem em ~320px -> EMPILHA */
  .ins-item { flex-direction: column; align-items: flex-start; gap: 6px; }
  .ins-item__sev { width: auto; }
  .ins-item__go { white-space: normal; }
  /* Projeções: número-herói reduz a fonte; a faixa (min · provável · máx) de valores mono não cabe em 3 colunas
     -> EMPILHA (cada valor + rótulo em sua linha, largura total) — cabe qualquer valor, sem cortar. */
  .proj-hero__val { font-size: 26px; }
  .proj-band__labels { flex-direction: column; align-items: stretch; gap: 6px; }
  .proj-band__l { flex-direction: row; align-items: baseline; justify-content: space-between; }
  .proj-band__l--mid { align-items: baseline; }
  .proj-band__l small { margin-top: 0; }
  /* Gerador de Links: os 3 campos UTM (source/medium/campaign) lado a lado não cabem em ~320px -> empilha.
     !important vence o style="flex:1" inline dos campos (só aqui, no @480; desktop segue com o flex:1 inline). */
  .lnk-utm { flex-wrap: wrap; }
  .lnk-utm .field { flex: 1 1 100% !important; min-width: 0; }
}

/* CMS de conteúdo — seletor de ícone (preview do SVG + <select>) e prévia de mídia do slot */
.cms-ico { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.cms-icoprev { width: 24px; height: 24px; color: var(--text-2); display: inline-flex; flex: 0 0 auto; }
.cms-icoprev svg { width: 24px; height: 24px; display: block; }
.cms-midia { border: 1px solid var(--border-1); border-radius: 10px; padding: 12px; }
.cms-mgrid { display: flex; flex-wrap: wrap; gap: 8px; }
.cms-mthumb { position: relative; width: 56px; height: 56px; border: 1px solid var(--border-1); border-radius: 8px; background: var(--surface-2); overflow: hidden; }
.cms-mthumb img { width: 100%; height: 100%; object-fit: contain; }
.cms-mthumb.is-hidden { opacity: .45; }
.cms-mthumb > button { position: absolute; top: 2px; right: 2px; width: 18px; height: 18px; padding: 0; display: flex; align-items: center; justify-content: center; font-size: 13px; line-height: 1; border: none; border-radius: 5px; background: rgba(0,0,0,.6); color: #fff; cursor: pointer; }
.cms-mthumb > button:hover { background: var(--brand); color: var(--on-brand); }
/* C52 · o 2º botão (Remover deste lugar) desce para o canto INFERIOR: o de cima já é o "ocultar", e
   dois botões no mesmo canto viram um alvo só. ⭐ Cores diferentes porque as ações são diferentes —
   ocultar é reversível, remover apaga o ponteiro. */
/* C53 · o aviso de "o que some do site se este lugar zerar". Duas gravidades, dois tons: `secao`
   (a seção inteira sai) é âmbar; `parte`/`imagem` (o site continua de pé) é neutro. */
.cms-vazio { display: flex; align-items: flex-start; gap: 8px; padding: 10px 12px; border-radius: var(--radius-input); font-size: 12px; line-height: 1.5; border: 1px solid var(--border-1); background: var(--surface-1); color: var(--text-2); }
.cms-vazio svg { flex: none; margin-top: 1px; }
.cms-vazio strong { color: var(--text-1); font-weight: 600; }
.cms-vazio--secao { border-color: var(--amber-border); background: var(--amber-soft); }
.cms-vazio--secao svg { color: var(--amber); }
.cms-mthumb > .cms-mthumb__rm { top: auto; bottom: 2px; right: 2px; }
.cms-mthumb > .cms-mthumb__rm:hover { background: var(--red); color: #fff; }
/* ⭐ C291 · TROCAR esta imagem, no canto que sobrava. Os outros dois ocupam o direito (ocultar em
   cima, remover embaixo); este fica embaixo à esquerda, e por isso precisa zerar o `right` da
   regra base — senão os dois `right` competem e o botão volta para a direita, em cima do remover. */
.cms-mthumb > .cms-mthumb__tr { top: auto; bottom: 2px; right: auto; left: 2px; }
.cms-mhid { margin-top: 12px; padding-top: 10px; border-top: 1px dashed var(--border-1); }

/* form CMS (redesenho): CARD por item (barra de controles no topo + corpo com os campos), aninhado RECUADO
   com faixa-guia, rótulos em CAIXA NORMAL (só no CMS). Preserva os hooks da coleta (.cms-item/.cms-variant/
   data-field/data-list/data-disc) — é só apresentação. */
.cms-root .field label { text-transform: none; letter-spacing: 0; font-size: 12px; font-weight: 500; color: var(--text-2); }
.cms-listhead { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin-bottom: 8px; }
.cms-listhead label { text-transform: none; letter-spacing: 0; font-size: 13px; font-weight: 600; color: var(--text-1); }
.cms-add { margin-top: 8px; }
/* ⭐⭐ C236 §2.1 · o menu da pergunta "adicionar o quê?". Ancorado no botão, não é modal: a escolha
   é um gesto pequeno e não merece roubar a tela. ⛔ `z-index` acima do card do item para não ser
   recortado pelo `overflow:hidden` do `.cms-item`. */
/* ⭐⭐ C236 §2.4 · o painel do preview mora DENTRO da caixa do modal — e o C237 acabou de dar a ela
   16:9, que é a forma de uma tela. Ocupa o mesmo lugar do formulário (que é escondido, não trocado). */
.cms-prev { flex: 1 1 auto; min-height: 0; display: flex; }
.cms-prev__box { flex: 1 1 auto; min-height: 0; border: 1px solid var(--border-2); border-radius: 10px; overflow: hidden; background: #050505; }
.cms-prev__f { display: block; width: 100%; height: 100%; border: 0; }
.cms-addwrap { position: relative; }
.cms-addmenu { position: absolute; z-index: 30; left: 0; top: calc(100% + 4px); min-width: 190px;
  display: flex; flex-direction: column; gap: 1px; padding: 5px;
  background: var(--elevated); border: 1px solid var(--border-2); border-radius: 10px;
  box-shadow: 0 8px 24px rgba(0,0,0,.24); }
.cms-addmenu__h { font-size: 11px; font-weight: 600; letter-spacing: .04em; text-transform: uppercase;
  color: var(--text-4); padding: 5px 8px 6px; }
.cms-addopt { display: block; width: 100%; text-align: left; padding: 8px 10px; border: 0; border-radius: 7px;
  background: transparent; color: var(--text-1); font-family: inherit; font-size: 13px; cursor: pointer; }
.cms-addopt:hover { background: var(--surface-1); }
.cms-addopt:focus-visible { outline: 2px solid var(--brand-ink); outline-offset: -2px; }

.cms-item { margin-bottom: 10px; border: 1px solid var(--border-1); border-radius: var(--radius-card); background: var(--surface-1); overflow: hidden; }
/* barra = cabeçalho clicável do ACORDEÃO */
.cms-item__bar { display: flex; align-items: center; gap: 8px; padding: 6px 8px; background: var(--surface-2); border-bottom: 1px solid var(--border-1); cursor: pointer; user-select: none; }
.cms-item__bar .cms-spacer { flex: 1 1 auto; }
.cms-item__bar .cms-poli-sel { width: auto; min-width: 130px; height: 32px; flex: 0 1 auto; }
.cms-chevron { width: 14px; height: 14px; color: var(--text-3); flex: 0 0 auto; transition: transform .15s var(--ease); }
.cms-item:not(.is-collapsed) > .cms-item__bar .cms-chevron { transform: rotate(90deg); }
.cms-summary { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 12px; color: var(--text-3); }
.cms-bar-extra { display: inline-flex; flex: 0 1 auto; min-width: 0; }
/* recolhido: o corpo some (só display:none — segue no DOM, coleta lê os inputs) */
.cms-item.is-collapsed > .cms-item__bar { border-bottom: none; }
.cms-item.is-collapsed > .cms-item__body, .cms-item.is-collapsed > .cms-variant { display: none; }
.cms-item__body, .cms-item--poli .cms-variant { display: flex; flex-wrap: wrap; gap: 8px 10px; padding: 10px; }
.cms-item__body > .field, .cms-item--poli .cms-variant > .field { flex: 1 1 200px; margin-bottom: 0; }
.cms-item__body > .cms-fieldfull, .cms-item--poli .cms-variant > .cms-fieldfull, .cms-item__body > .cms-listwrap { flex-basis: 100%; }
.cms-item .cms-sub { width: 100%; }

/* grip: arrastar (drag) ou foco + ↑/↓ (teclado). As setas de mover saíram — barra limpa. */
.cms-grip { display: inline-flex; align-items: center; color: var(--text-4); cursor: grab; padding: 2px; touch-action: none; flex: 0 0 auto; border-radius: 4px; }
.cms-grip:active { cursor: grabbing; }
.cms-grip:focus-visible { outline: 2px solid var(--brand-ink); }
.cms-item.cms-dragging { opacity: .45; }
.cms-item.cms-over { box-shadow: inset 0 2px 0 0 var(--brand-ink); }
.cms-rem { flex: 0 0 auto; }
/* ⭐⭐ C236 · item OCULTO. O lote pede DUAS coisas ao mesmo tempo: "visivelmente apagado" e "continua
   editável". Por isso a opacidade cai na BARRA e a borda vira tracejada — e NÃO no corpo, que é onde
   moram os inputs; apagar o corpo entregaria só a primeira metade do pedido.
   ⛔ E o próprio olho volta a opacidade cheia: ele é o caminho de volta, e caminho de volta apagado
      é o defeito que o `overflow` ensinou — esconder não é consertar. */
.cms-olho { flex: 0 0 auto; }
.cms-item.is-hidden { border-style: dashed; border-color: var(--border-2); }
.cms-item.is-hidden > .cms-item__bar { opacity: .5; }
.cms-item.is-hidden > .cms-item__bar .cms-olho { opacity: 1; }

/* aninhado: a sub-lista (blocos do tutorial) recuada + faixa-guia à esquerda; card do bloco mais leve */
.cms-item__body .cms-listwrap { border-left: 2px solid var(--brand-border); padding-left: 12px; }
.cms-item__body .cms-listwrap .cms-item { background: var(--surface-2); }

.cms-check { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; font-size: 13px; color: var(--text-2); }
.cms-check input { width: 15px; height: 15px; accent-color: var(--brand); cursor: pointer; }

/* prévia da mensagem (Renovação SMS) */
.sms-preview { font-size: 13px; color: var(--text-2); background: var(--surface-2); border: 1px solid var(--border-1); border-radius: var(--radius-input); padding: 12px 14px; line-height: 1.5; white-space: pre-wrap; word-break: break-word; }

/* ===== Venda Assistida (Fase 6 F3b) — só o específico da tela; o resto usa card/paramgrid/field/badge/btn ===== */
.va-form { grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); }
.va-aceite { display: flex; gap: 9px; align-items: flex-start; margin-top: 16px; cursor: pointer; font-size: 12.5px; color: var(--text-2); line-height: 1.5; }
.va-aceite input { margin-top: 2px; flex-shrink: 0; width: 15px; height: 15px; accent-color: var(--brand); }
.va-aceite b { color: var(--text-1); font-weight: 500; }
.va-cob { display: flex; gap: 22px; align-items: flex-start; flex-wrap: wrap; }
.va-qr { width: 180px; height: 180px; flex-shrink: 0; background: #fff; border-radius: var(--radius-input); padding: 9px; display: flex; align-items: center; justify-content: center; }
.va-qr canvas { width: 100% !important; height: 100% !important; }
.va-pay { flex: 1; min-width: 240px; }
textarea.va-emv { resize: none; font-size: 11px; }   /* copia-e-cola: NÃO redimensiona + fonte menor; altura/padding vêm de textarea.input (0,1,1 empata → ordem vence) */
.va-status { margin-top: 20px; padding-top: 18px; border-top: 1px solid var(--border-1); font-size: 13px; color: var(--text-2); }
.va-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--brand-ink); display: inline-block; animation: va-pulse 1.2s ease-in-out infinite; }
@keyframes va-pulse { 0%, 100% { opacity: .3 } 50% { opacity: 1 } }
@media (prefers-reduced-motion: reduce) { .va-dot { animation: none; } }
.va-code { font-family: var(--font-mono); font-size: 20px; font-weight: 600; letter-spacing: 1px; color: var(--text-1); background: var(--surface-2); border: 1px dashed var(--brand-border); border-radius: var(--radius-input); padding: 14px; margin: 8px 0 10px; word-break: break-all; }

/* ================= TABELA -> CARDS em <=640px (opt-in por classe) =======================================
   Nasceu na tela de Saques (F5): a tabela tem 1000px de largura mínima e, numa viewport de ~400px, 4 das 6
   colunas caem FORA da tela. O .table-wrap tem overflow-x e MASCARA o problema — a página não quebra, mas os
   botões Pagar/Recusar ficam a ~440px além da borda e quem não descobrir o scroll lateral conclui que não dá
   pra usar do celular. Na F6 virou base compartilhada com as outras telas do grupo Indicações.

   ⚠️ OPT-IN POR CLASSE, sempre: o .table--fixed é o padrão de dezenas de telas do painel e continua intacto.
   Quem quer virar card pede (.tbl-cards); reescrever o padrão do painel inteiro é outra conversa.
   ⚠️ MARKUP ÚNICO: só o CSS muda de forma. Os botões continuam sendo os MESMOS elementos, com os mesmos
   onclick — nada é re-renderizado por breakpoint, então não existe estado divergente entre as duas formas.
   ⚠️ ESPECIFICIDADE: tem que ser `.tbl-cards tbody td` (0,1,2) — o `.table tbody td` do painel também é
   (0,1,2) e, com (0,1,1), as bordas e o padding das células continuavam desenhando linhas dentro do card. */
/* min-width menor que o wrapper do painel (~970px no desktop comum): a .table ja e width:100%, entao
   ela ACOMPANHA o container em tela larga em vez de travar em 1000 e transbordar 30px. Abaixo deste
   piso o scroll horizontal e legitimo — as 6 colunas nao cabem mesmo. */
.tbl-larga { min-width: 880px; }

@media (max-width: 640px) {
  .tbl-larga { min-width: 0; width: 100%; }
  .tbl-cards colgroup, .tbl-cards thead { display: none; }
  .tbl-cards, .tbl-cards tbody { display: block; }
  .tbl-cards tr {
    display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 7px 10px; align-items: start;
    background: var(--surface-1); border: 1px solid var(--border-1); border-radius: var(--radius-card);
    padding: 14px; margin-bottom: 10px;
  }
  .tbl-cards tbody td { display: block; border: 0; padding: 0; overflow: visible; white-space: normal; vertical-align: baseline; }
  .tbl-cards tbody tr:hover { background: var(--surface-1); }
  /* padrão do card: 1ª célula à esquerda, 2ª à direita, o resto empilha em largura cheia */
  .tbl-cards tbody td:nth-child(1) { grid-column: 1; }
  .tbl-cards tbody td:nth-child(2) { grid-column: 2; text-align: right; }
  .tbl-cards tbody td:nth-child(n+3) { grid-column: 1 / -1; text-align: left; }
  /* o .table--fixed corta o 1º td com ellipsis; no card o nome pode quebrar */
  .tbl-cards tbody td:first-child, .tbl-cards tbody td:first-child .cell-strong { white-space: normal; overflow: visible; }
  .tbl-cards .badge { display: inline-block; margin: 0 4px 4px 0; }
  .tbl-cards .tbl-acoes { display: flex; gap: 8px; }
  .tbl-cards .tbl-acoes .btn { flex: 1 1 0; justify-content: center; }
  /* o ghost sem fundo, dentro do card, não parece alvo de toque — ganha contorno só aqui */
  .tbl-cards .tbl-acoes .btn--danger-ghost { border: 1px solid var(--border-2); }

  /* Saques: a ordem natural das colunas deixaria os SINAIS no fim; eles são o que o dono lê antes de
     decidir, então sobem pra logo abaixo do nome. */
  .saques-mobile tbody td:nth-child(1) { grid-row: 1; }
  .saques-mobile tbody td:nth-child(2) { grid-row: 1; }
  .saques-mobile tbody td:nth-child(5) { grid-row: 2; }
  .saques-mobile tbody td:nth-child(3) { grid-row: 3; }
  .saques-mobile tbody td:nth-child(4) { grid-row: 4; }
  .saques-mobile tbody td:nth-child(6) { grid-row: 5; }
  .saques-mobile tbody td:nth-child(4)::before { content: 'Pedido em '; color: var(--text-3); }
}

/* ================= PERFIL DO CLIENTE (modal · Indicações > Clientes) ====================================
   O modal antes era uma pilha de linhas "label: valor" sem respiro — o texto renderizado saía grudado
   ("CPF***.***.222-22E-maild***@..."). Agora: seções separadas por hairline, label ACIMA do valor, e uma
   grade de 2 colunas nos dados de contato. */
.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.pf-sec { padding: 16px 0; border-top: 1px solid var(--border-1); }
.pf-sec:first-child { padding-top: 4px; border-top: 0; }
.pf-sec-h { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 12px; }
.pf-sec-h b { font-size: 13px; font-weight: 600; color: var(--text-1); }
.pf-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 20px; }
.pf-campo { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.pf-lbl { font-size: 11px; font-weight: 500; color: var(--text-3); text-transform: uppercase; letter-spacing: .4px; }
.pf-val { font-size: 13.5px; color: var(--text-1); word-break: break-word; }
.pf-kpis { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; }
.pf-kpi { display: flex; flex-direction: column; gap: 3px; padding: 12px; background: var(--surface-1);
  border: 1px solid var(--border-1); border-radius: var(--radius-input); min-width: 0; }
.pf-kpi b { font-family: var(--font-mono); font-size: 16px; font-weight: 500; color: var(--text-1); white-space: nowrap; }
.pf-kpi small { font-size: 11px; line-height: 1.3; }
.pf-lista { list-style: none; margin: 0; padding: 0; }
.pf-lista li { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
  padding: 8px 0; border-bottom: 1px solid var(--border-1); font-size: 13px; }
.pf-lista li:last-child { border-bottom: 0; }
@media (max-width: 640px) {
  .pf-grid, .pf-kpis { grid-template-columns: minmax(0, 1fr); }
  .pf-sec-h { flex-wrap: wrap; }
}

/* ================= SELECT DO PAINEL — CHEVRON PRÓPRIO, NUNCA A SETA NATIVA =============================
   1ª tentativa foi só `padding-right: 32px`, e não resolveu: a seta nativa NÃO tem tamanho definido por
   spec — ela varia por navegador, versão, plataforma e escala de DPI. Numa máquina ela cabe nos 32px, na
   outra ela é maior e encosta na borda de novo. Reservar espaço pra algo de tamanho desconhecido é
   apostar; a correção é PARAR de usar o desenho do browser.

   `appearance: none` desliga a seta nativa e o chevron passa a ser um SVG nosso, com posição e tamanho
   fixos. Fica idêntico em Chrome/Firefox/Edge, em qualquer DPI e em qualquer update — e o padding de 32px
   volta a fazer sentido, porque agora o que ele reserva é PREVISÍVEL.

   A cor acompanha o tema (var trocada no light) — não dá pra usar currentColor dentro de data URI. */
:root { --sel-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23a1a1aa' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E"); }
[data-theme="light"] { --sel-chevron: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E"); }

select.input, select.select {
  -webkit-appearance: none; -moz-appearance: none; appearance: none;
  padding-right: 32px;
  background-image: var(--sel-chevron);
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 13px 13px;
}
select.input::-ms-expand, select.select::-ms-expand { display: none; }   /* Edge legado */

/* ---- linha de chips que NÃO pode crescer em altura (dentro de célula de tabela): uma linha só, o que
   não couber vira "+N" com o resto no title. Sem isto, 2-3 sinais empilham e a linha da tabela vai de
   59px pra 93/190px, deixando a grade serrilhada. ---- */
.chips--linha { flex-wrap: nowrap; overflow: hidden; min-width: 0; }
/* o "+N" NUNCA encolhe (flex:0 0 auto) — quem cede é o chip do texto, com reticências. Sem isto o
   overflow:hidden cortava justamente o "+N", que é o que avisa que existe mais sinal escondido. */
.chips--linha .badge { white-space: nowrap; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.chips--linha .badge:last-child { flex: 0 0 auto; }
/* célula de desfecho: uma linha, corta com reticências (o texto inteiro fica no title) */
.cel-1linha { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* ações da tabela de saques: os 2 botões SEMPRE lado a lado (o wrap global empilhava numa coluna estreita) */
.saques-mobile .tbl-acoes { flex-wrap: nowrap; }

/* ---- navegação DENTRO do modal de perfil (Indicações > Clientes) ----
   Clicar num indicado troca o conteudo do modal pelo perfil dele. Antes eram <li> mortos: pra ver o
   indicado o operador fechava o modal, procurava o nome na tabela e abria de novo. */
.pf-lista li.pf-nav { cursor: pointer; border-radius: var(--radius-input); margin: 0 -8px; padding-left: 8px; padding-right: 8px; }
.pf-lista li.pf-nav:hover { background: var(--surface-1); }
.pf-trilha { display: flex; align-items: center; flex-wrap: wrap; gap: 2px; font-size: 12px; color: var(--text-3); margin-bottom: 8px; }
.pf-trilha .lnk { font-size: 12px; }

/* ---- header do modal de perfil: titulo + status + acao NA MESMA LINHA ----
   O botao "Ver dados completos" vivia no header da SEÇÃO de contato, 41px abaixo do <h3> e em outro
   container — parecia solto. Agora os tres dividem a mesma linha flex, com a acao empurrada pra direita. */
.pf-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 4px; }
.pf-head h3 { margin: 0; }
.pf-head .pf-head-acao { margin-left: auto; flex: 0 0 auto; }
.pf-head .pf-head-acao:empty { display: none; }

/* ⭐⭐⭐ C233 · o modal do CMS espelha o LAYOUT: a lista de blocos (o VERDE do desenho) à esquerda,
   os campos do bloco escolhido à direita. ⛔ Substitui o acordeão do C232 — lá eram 5 grupos
   empilhados numa coluna; aqui é UM BLOCO POR VEZ, e a altura para de crescer com o nº de campos.
   ⚠️ A 400px (mobile do painel) empilha: a nav vira uma faixa de chips roláveis. */
.cms-blocos{display:grid;grid-template-columns:186px 1fr;gap:16px;align-items:start}
.cms-bnavs{display:flex;flex-direction:column;gap:2px;position:sticky;top:0}
.cms-bnav{display:flex;align-items:center;justify-content:space-between;gap:8px;width:100%;text-align:left;
  padding:9px 11px;border:0;border-radius:8px;background:transparent;color:var(--text-2);
  font-family:inherit;font-size:13px;font-weight:500;line-height:1.35;cursor:pointer;transition:background 140ms}
.cms-bnav:hover{background:var(--surface-1)}
/* ⭐ C236 §2.3 · a linha da nav passa a ter dois controles: ir pro bloco, e o olho do bloco. */
/* ============================================================================================
   ⭐⭐⭐ C238 · O CONSTRUTOR. Três colunas, a página no meio, e a barra de ações SEMPRE visível.
   ⛔ Nada aqui toca `.modal__card--cms16` (C237): aquele continua sendo a caixa dos MODAIS. O que
      mudou é que o editor de seção deixou de ser modal — um formulário cabe num card, um
      construtor não.
   ⭐ A coluna do meio é `1fr`: ela fica com o que sobra das duas laterais fixas, e é o número que
     o §7 manda reportar.
   ============================================================================================ */
/* ⚠️ os números aqui são MEDIDOS, não chutados: `--topbar-h` é 60px (app.css:78) e o `.content`
   tem `padding: 24px` (app.css:398) — a 1ª versão usou fallbacks de 56 e 20 e o construtor estourou
   4px da viewport. As margens negativas anulam o padding do `.content` para o construtor encostar
   nas bordas; o `100dvh` desconta só a topbar. */
/* ============================================================================================
   ⭐⭐⭐ C240 · A PRANCHETA. A página em escala REAL, e nenhum painel roubando largura.
   ⛔ O defeito das versões anteriores: a página dividia largura com os painéis e nunca aparecia na
      largura de verdade. Um rodapé tem 1100px de conteúdo; em 600px as 4 colunas viram tirinhas que
      não se parecem com o site.
   ⭐ Agora o documento tem a largura escolhida (1440/768/390) e é REDUZIDO POR ESCALA. A barra da
     peça e a caixa de campos vivem FORA da escala, posicionadas por cima — senão encolheriam junto
     e a 63% ficariam ilegíveis.
   ⚠️ Números medidos, não chutados: `--topbar-h` é 60px (app.css:78) e o `.content` tem
     `padding: 24px` (app.css:398).
   ============================================================================================ */
/* ⛔⛔ C240 (3ª volta) · o `.content` do painel ROLA (`overflow-y:auto`, app.css:398), e com o
   construtor dentro dele havia DOIS scrolls aninhados. O de fora levava a barra de ações para fora
   da vista — que é o "scroll bugado" da captura. ⭐ Enquanto o construtor está montado, quem rola é
   só o palco. ⚠️ `:has()` já foi medido como suportado no C237. */
/* ⛔⛔⛔ GUARDA (C242 §0.1, 14/09/2026) — ESTE SELETOR DEPENDE DE FILHO DIRETO (`>`).
   ⛔ QUALQUER invólucro novo entre o `.content` e seus filhos (um `.content__in`, por exemplo)
      faz esta regra PARAR DE CASAR, e volta o scroll duplo aninhado do Page Builder — o
      "scroll bugado" que o C240, na 3ª volta, consertou.
   ⭐ A centragem da coluna em 1200 NÃO precisa de invólucro: ela já é entregue pelo padding do
     pai (`.content { padding: 24px max(32px, calc((100% - 1200px)/2)) }`), medido a 1200, 1456,
     1920 e 2560. ⛔ Se alguém propuser o invólucro de novo, a resposta está aqui. */
.content:has(> .bld) { overflow: hidden; padding: 0; }
.bld { position: relative; display: flex; flex-direction: column; height: calc(100dvh - var(--topbar-h)); min-height: 0; overflow: hidden; }

.bld-top { display: flex; align-items: center; gap: 10px; flex: 0 0 auto; padding: 9px 14px; border-bottom: 1px solid var(--border-1); background: var(--surface-2); }
.bld-t { font-weight: 600; font-size: 14px; }
.bld-d { font-size: 11.5px; color: var(--text-4); }
.bld-sp { flex: 1 1 auto; }
.bld-ms { font-size: 11px; font-variant-numeric: tabular-nums; }
.bld-seg { display: inline-flex; gap: 3px; }
.bld-w { font-size: 10.5px; font-variant-numeric: tabular-nums; color: var(--text-4); border: 1px solid var(--border-1); border-radius: 5px; padding: 4px 8px; background: transparent; cursor: pointer; font-family: inherit; }
.bld-w.on { color: var(--brand-ink); border-color: var(--brand-border); background: var(--brand-soft); }

/* ---- o miolo: Estrutura + prancheta ---- */
.bld-main { position: relative; flex: 1 1 auto; min-height: 0; display: flex; align-items: stretch; }
/* ⭐ C240 §4 · EMPURRA no largo. `flex-basis` animado: a prancheta perde largura e a ESCALA reduz
   junto — ⛔ o layout da página não muda um pixel de proporção. */
.bld-ov { flex: 0 0 0px; width: 0; overflow: hidden; background: var(--elevated); border-right: 1px solid transparent; padding: 0;
  transition: flex-basis .2s cubic-bezier(.4,0,.2,1), width .2s cubic-bezier(.4,0,.2,1); }
.bld-ov.on { flex-basis: 248px; width: 248px; border-right-color: var(--border-1); padding: 8px 6px; overflow: auto; min-height: 0; }
/* ⭐ SOBREPÕE no estreito — e o JS a fecha sozinha quando o operador escolhe uma peça. */
@media (max-width: 900px) {
  .bld-ov { position: absolute; top: 0; left: 0; bottom: 0; z-index: 8; flex-basis: 0; width: 248px; padding: 8px 6px; overflow: auto;
    border-right: 1px solid var(--border-1); box-shadow: 14px 0 40px rgba(0,0,0,.45);
    transform: translateX(-102%); transition: transform .2s cubic-bezier(.4,0,.2,1); }
  .bld-ov.on { transform: none; flex-basis: 0; }
}
@media (max-width: 520px) { .bld-ov { width: 82%; } }
.bld-stage { flex: 1 1 auto; min-width: 0; min-height: 0; overflow: auto; padding: 14px; background: var(--bg, #050505); display: flex; justify-content: center; align-items: flex-start; }
/* o holder carrega o tamanho JÁ ESCALADO: é ele que diz ao fluxo quanto a prancheta ocupa */
.bld-holder { position: relative; flex: 0 0 auto; }
.bld-holder iframe { display: block; border: 0; background: #0e0d0c; transform-origin: top left; box-shadow: 0 10px 40px rgba(0,0,0,.45); border-radius: 8px; }
.bld-zoom { position: absolute; right: 12px; bottom: 10px; z-index: 5; font-size: 10.5px; font-variant-numeric: tabular-nums; color: var(--text-4); background: var(--surface-2); border: 1px solid var(--border-1); border-radius: 6px; padding: 4px 8px; pointer-events: none; }

/* ---- a árvore (Estrutura) ---- */
/* ⛔ a árvore precisa rolar DENTRO da coluna: sem isto ela media 1.210px num limite de 670 e
   empurrava o painel inteiro a rolar. Medido na tela. */
.bld-tree { min-height: 0; max-height: 100%; overflow-y: auto; }
.bld-row { display: flex; align-items: center; gap: 7px; padding: 6px 8px; border-radius: 6px; cursor: pointer; font-size: 12.5px; color: var(--text-2); border: 1px solid transparent; }
.bld-row:hover { background: var(--surface-1); color: var(--text-1); }
.bld-row.sel { background: var(--brand-soft); border-color: var(--brand-border); color: var(--text-1); }
.bld-row--blk { font-weight: 600; color: var(--text-1); margin-top: 6px; }
.bld-row--pc { margin-left: 13px; }
/* ⭐ C261 · a linha de CAMPO e a de IMAGEM moram na mesma sangria da peça — são todas filhas de um cabeçalho. */
.bld-row--cp, .bld-row--md { margin-left: 13px; }
.bld-vl { font-size: 10.5px; color: var(--text-4); flex: 0 0 auto; max-width: 46%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bld-mi { width: 18px; height: 18px; object-fit: contain; border-radius: 3px; background: var(--surface-1); flex: 0 0 auto; }
.bld-row.off .bld-nm { opacity: .45; text-decoration: line-through; }
.bld-row.dragging { opacity: .45; }
.bld-row.over { box-shadow: inset 0 2px 0 0 var(--brand-ink); }
.bld-grip { display: inline-flex; color: var(--text-4); cursor: grab; flex: 0 0 auto; }
.bld-tp { font-size: 9.5px; font-weight: 500; text-transform: uppercase; color: var(--brand-ink); background: var(--brand-soft); border: 1px solid var(--brand-border); border-radius: 3px; padding: 2px 4px; flex: 0 0 auto; }
.bld-nm { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bld-ac { display: flex; gap: 2px; flex: 0 0 auto; opacity: .4; }
.bld-row:hover .bld-ac, .bld-row.sel .bld-ac { opacity: 1; }
.bld-ic { width: 20px; height: 20px; display: grid; place-items: center; border: 0; border-radius: 4px; background: transparent; color: var(--text-3); cursor: pointer; font-size: 11px; }
.bld-ic:hover { background: var(--surface-3); color: var(--text-1); }
.bld-ic--x:hover { color: var(--danger, #ef6b62); }
.bld-add { display: flex; align-items: center; justify-content: center; gap: 6px; width: calc(100% - 13px); margin: 6px 0 0 13px; border: 1px dashed var(--border-2); border-radius: 6px; padding: 6px; font-size: 11.5px; color: var(--text-4); background: transparent; cursor: pointer; font-family: inherit; }
.bld-add:hover { border-color: var(--brand-ink); color: var(--brand-ink); }
.bld-ins { height: 12px; margin-left: 13px; display: flex; align-items: center; cursor: pointer; opacity: 0; transition: opacity 120ms; }
.bld-ins:hover { opacity: 1; }
.bld-ins i { flex: 1 1 auto; height: 1.5px; background: var(--brand-ink); }
.bld-ins b { font-size: 10px; font-weight: 600; color: var(--on-brand, #160a05); background: var(--brand-ink); border-radius: 99px; padding: 1px 7px; margin: 0 5px; }
.bld-row--grp .bld-n { font-size: 10.5px; color: var(--text-4); font-variant-numeric: tabular-nums; flex: 0 0 auto; }
.bld-gi { display: inline-flex; color: var(--text-4); flex: 0 0 auto; }
.bld-row--grp { color: var(--text-2); }

/* ---- ⭐⭐ a BARRA DA PEÇA — flutua FORA da escala ---- */
/* ⛔⛔ C240 (3ª volta) · FUNDO OPACO. MEDIDO: `--surface-2` é `rgba(255,255,255,.035)` — 3,5% de
   branco, feito para empilhar SOBRE o fundo do painel. A barra e a caixa flutuam sobre a PÁGINA, e
   ali ela vazava através delas: dava para ler o rodapé do site atrás dos campos.
   ⭐ `--elevated` (#0d0d10) é o opaco do sistema — o mesmo que o `.modal__card` usa desde sempre. */
.bld-hud { position: absolute; z-index: 9; display: none; gap: 2px; padding: 3px; background: var(--elevated); border: 1px solid var(--brand-border); border-radius: 8px; box-shadow: 0 6px 20px rgba(0,0,0,.55); }
.bld-hud.on { display: flex; }
.bld-hud__t { font-size: 9px; font-weight: 600; text-transform: uppercase; letter-spacing: .06em; color: var(--brand-ink); align-self: center; padding: 0 7px 0 5px; }
.bld-hud__b { width: 26px; height: 26px; display: grid; place-items: center; border: 0; border-radius: 6px; background: transparent; color: var(--text-2); cursor: pointer; font-size: 12px; }
.bld-hud__b:hover { background: var(--surface-3); color: var(--text-1); }
.bld-hud__b--x:hover { color: var(--danger, #ef6b62); }

/* ---- ⭐⭐ a CAIXA DE CAMPOS — ancorada, limitada pela PRANCHETA ---- */
/* ⭐⭐ C273 · A LARGURA DEIXOU DE SER FIXA. Eram 310px para as CINCO molduras, e o item de
   `ajuda/tutoriais` chega a 51 campos — o dono reportou: "Modal ficou muito pequeno para a seção
   de ajuda". ⚠️ `clamp` e não `vw` puro: o palco do construtor tem três larguras (1440/768/390) e
   a caixa não pode crescer mais que o palco. O piso de 310px preserva o que já cabia. */
.bld-pop { position: absolute; z-index: 10; width: clamp(310px, 24vw, 430px); max-height: 82%; display: none; flex-direction: column; background: var(--elevated); border: 1px solid var(--border-2); border-radius: 11px; box-shadow: 0 18px 48px rgba(0,0,0,.6); }
.bld-pop.on { display: flex; }
/* ⚠️ MEDIDO na tela do dono (845px de altura): com 58% a folha comia 62% do palco e engolia o
   rodapé inteiro. 42% deixa a página respirando, e o corpo rola dentro. */
.bld-pop--folha { border-radius: 12px 12px 0 0; max-height: 42%; }
.bld-pop__h { display: flex; align-items: center; gap: 8px; padding: 10px 12px; border-bottom: 1px solid var(--border-1); flex: 0 0 auto; }
.bld-pop__k { font-size: 8.5px; font-weight: 600; text-transform: uppercase; letter-spacing: .06em; color: var(--brand-ink); background: var(--brand-soft); border: 1px solid var(--brand-border); border-radius: 4px; padding: 3px 6px; }
.bld-pop__t { font-size: 12.5px; font-weight: 600; flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bld-pop__x { border: 0; background: transparent; color: var(--text-4); cursor: pointer; font-size: 14px; }
.bld-pop__b { padding: 12px; overflow: auto; min-height: 0; }
/* ⛔⛔⛔ C273 · O RODAPÉ DE VERBOS SAIU DE DENTRO DO CORPO, e este era o defeito que fazia o dono
   dizer "nenhum desses ícones funciona". Eles funcionavam — ele NÃO OS ALCANÇAVA. MEDIDO: numa
   peça alta o olho e a lixeira ficavam 963 px abaixo da dobra, dentro do `overflow:auto` do corpo.
   ⭐ Agora o rodapé é IRMÃO do corpo, com `flex: 0 0 auto`: o corpo rola, os verbos ficam.
   ⚠️ `position: sticky` não resolveria — o pai que rola é o `__b`, e o rodapé precisa estar FORA
     dele para não entrar no scroll. É estrutura, não truque de CSS. */
.bld-pop__f { display: flex; align-items: center; gap: 6px; padding: 10px 12px; border-top: 1px solid var(--border-1); flex: 0 0 auto; }
.bld-pop__f .bld-pop__sep { flex: 1 1 auto; }
.bld-pop__b .cms-item__body, .bld-pop__b .cms-variant { display: flex; flex-direction: column; gap: 10px; padding: 0; }
.bld-pop__b .field { flex-basis: auto !important; margin-bottom: 0; }

/* ---- o formulário REAL, escondido fora da tela: é ele que a coleta e o arrasto leem ----
   ⛔ `left: -99999px` e NÃO `display:none`: um elemento sem caixa não tem `offsetParent`, e o
      seletor de imagem e o repeater precisam medir. É o mesmo motivo do acordeão do C232. */
/* ⛔ `height: 0; overflow: hidden` além do `left`: sem a altura zerada o formulário escondido
   entrava no `scrollHeight` do `.bld` e fazia o painel rolar. ⚠️ E NÃO `display:none` — um elemento
   sem caixa não tem `offsetParent`, e o seletor de imagem e o repeater precisam medir (C232). */
.bld-form { position: absolute; left: -99999px; top: 0; width: 320px; height: 0; overflow: hidden; }
.bld-form .cms-bnavs, .bld-form .cms-bpane-h, .bld-form .cms-listhead, .bld-form .cms-add { display: none; }
.bld-form .cms-blocos, .bld-form .cms-bpane { display: block; }

.cms-bnavrow{display:flex;align-items:center;gap:2px}
.cms-bnavrow .cms-bnav{flex:1 1 auto;width:auto}
.cms-olhob{flex:0 0 auto;opacity:.55}
.cms-bnavrow:hover .cms-olhob,.cms-olhob[aria-pressed="true"]{opacity:1}
.cms-bpane.is-hidden>.cms-bpane-h{opacity:.5}
.cms-bnav.on{background:var(--brand-soft);color:var(--brand)}
.cms-bnav .dim{font-size:11px;font-variant-numeric:tabular-nums}
.cms-bpane{display:none}
.cms-bpane.on{display:block}
.cms-bpane-h{font-size:12px;font-weight:600;letter-spacing:.04em;text-transform:uppercase;
  color:var(--text-3);margin:0 0 12px;padding-bottom:8px;border-bottom:1px solid var(--border)}
@media (max-width:560px){
  .cms-blocos{grid-template-columns:1fr}
  .cms-bnavs{flex-direction:row;overflow-x:auto;gap:6px;padding-bottom:8px;position:static}
  .cms-bnav{width:auto;white-space:nowrap}
}

/* ============================================================================================
   ⭐⭐⭐ C241 · OS COMPONENTES — receitas medidas no espécime `painel-claude-ui.html`.
   ⛔ Só aparência. Nenhuma regra aqui muda texto de tela, rota, endpoint ou schema.
   ⚠️ Tudo abaixo é do tema ESCURO por herança de token — o `[data-theme="light"]` não foi tocado.
   ============================================================================================ */

/* ---- §4.1 · a casca: o contorno do card vira ANEL INTERNO, não borda ----
   ⭐ "Borda come caixa, anel não": `border` entra no box-sizing e encolhe o conteúdo em 2px por eixo;
   `inset box-shadow` pinta por dentro sem tirar espaço. É o que o espécime usa. */
.card { border: 0; box-shadow: inset 0 0 0 1px var(--border-1); }
.card:hover { box-shadow: inset 0 0 0 1px var(--border-2); }

/* ---- §3.5 · o AVATAR do usuário fica NEUTRO ----
   ⛔ Ele usava `--brand` + `--on-brand`, mas o §3.5 é explícito: "é o USUÁRIO, não o logo".
   ⭐ Provado pela mutação do item 14: com `--brand` em magenta, ele era o ÚNICO elemento que ainda
     reagia. O logo deste operador é `<img class="sidebar__logoimg">` e não usa token de cor. */
.sidebar__avatar { background: rgba(255,255,255,.10); color: var(--text-1); }

/* ---- §4.2 · a sidebar: 256 de largura e o grupo que recolhe ---- */
:root { --sidebar-w: 256px; }
/* o cabeçalho do grupo virou <button> — ele precisa parecer linha de nav, não botão */
.nav-group__title { display: flex; align-items: center; justify-content: space-between; gap: 8px;
  width: 100%; height: 36px; padding: 0 8px; border: 0; border-radius: 8px; background: transparent;
  font-family: inherit; font-size: 14px; line-height: 20px; font-weight: 400; letter-spacing: 0;
  text-transform: none; color: var(--text-3); cursor: pointer; }
.nav-group__title:hover { background: rgba(255,255,255,.05); color: var(--text-2); }
.nav-group__title svg { flex: 0 0 auto; transition: transform 140ms var(--ease); }
.nav-group.is-closed .nav-group__title svg { transform: rotate(-90deg); }
.nav-group.is-closed .nav-group__items { display: none; }
/* ⭐ o filho recua 40px, como o Console */
.nav-group__items .nav-item { padding-left: 40px; }
.nav-item { height: 36px; border-radius: 8px; }
.nav-item.is-active { background: rgba(255,255,255,.15); }
/* ⚠️ na FAIXA RECOLHIDA (64px) o cabeçalho some (regra já existente) — então os filhos têm de
   reaparecer, senão um grupo fechado deixaria a faixa sem ícones: um estado que não existia antes
   não pode nascer quebrado. */
:root.sidebar-collapsed .nav-group.is-closed .nav-group__items { display: block; }
:root.sidebar-collapsed .nav-group__items .nav-item { padding-left: 0; }

/* ---- §4.4 · o conteúdo centralizado ----
   ⚠️ MEDIDO: a classe `content__wrap` do lote NÃO EXISTE no repo, e o `setContent()` sobrescreve o
     `innerHTML` do `#content` — um invólucro fixo lá dentro seria apagado a cada tela.
   ⭐ Então o centro vem dos FILHOS diretos, que é o que o `setContent` escreve. ⛔ O `.content` em si
     não foi tocado: ele continua sendo o scroller com o `scrollTop = 0` do router.
   ⚠️ CEDÊNCIA DECLARADA: o lote pede `padding: 24px 32px`; o `.content` tem `24px` e mexer nele é
     proibido pelo escopo. O lateral fica em 24, não 32. */
/* ⛔⛔ CONSERTADO (bug achado pelo Matheus): esta regra NAO centraliza mais por margem.
   ⭐ MEDIDO: o `app.js` carimba as barras de filtro com `style="…;margin:0 0 12px"` INLINE, e o
     shorthand zera `margin-left/right`. Estilo inline vence folha, então a `.row` colava na
     esquerda enquanto os irmãos iam para o meio — o vazio que apareceu em 5 telas.
   ⭐ A centralização passou para o PADDING DO PAI (procure `max(32px, calc(...))` neste arquivo):
     padding de pai não é alcançável por estilo inline de filho, então vale para todos.
   ⚠️ O `max-width` fica: ele ainda segura o filho que, por conta própria, tentaria passar de 1200. */
.content > * { max-width: 1200px; }

/* ---- §4.5 · o período: gatilho + popover (o <select> nativo saiu) ---- */
.pdd { position: relative; display: inline-flex; }
.pdd__trig { display: inline-flex; align-items: center; gap: 6px; height: 32px; padding: 0 10px;
  border: 0; border-radius: 8px; background: rgba(255,255,255,.05);
  box-shadow: inset 0 0 0 1px rgba(255,255,255,.10);
  font-family: inherit; font-size: 14px; line-height: 20px; color: var(--text-1); cursor: pointer; }
.pdd__trig:hover { background: rgba(255,255,255,.075); }
.pdd__trig svg { color: var(--text-3); }
.pdd__pop { position: absolute; z-index: 60; top: calc(100% + 6px); left: 0; width: 256px;
  max-height: 320px; overflow-y: auto; padding: 4px; border-radius: 12px; background: var(--elevated);
  box-shadow: inset 0 0 0 1px var(--border-2), 0 12px 32px var(--shadow-3); }
.pdd__opt { display: flex; align-items: center; justify-content: space-between; gap: 8px;
  width: 100%; height: 32px; padding: 4px 12px; border: 0; border-radius: 8px; background: transparent;
  font-family: inherit; font-size: 14px; line-height: 20px; font-weight: 400; color: var(--text-2);
  cursor: pointer; text-align: left; }
.pdd__opt:hover { background: rgba(255,255,255,.075); color: var(--text-1); }
.pdd__opt.is-on { color: var(--text-1); }
.pdd__opt svg { flex: 0 0 auto; color: var(--brand-ink); }

/* ---- §4.6 · card e KPI ----
   ⭐ o rótulo e a pill dividem a MESMA linha, no topo; o valor fica sozinho embaixo. */
.card { border-radius: 12px; }
.kpi { padding: 16px; gap: 16px; }
.kpi__label { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
.kpi__value { font-size: 30px; line-height: 36px; font-weight: 500; letter-spacing: 0; }

/* ---- §4.7 · a pill ----
   ⛔ SEM BORDA: medido no chip do Console (`bd 0px`). O fundo e a tinta vêm dos pares do §3.3. */
.pill { height: 20px; padding: 0 8px; gap: 4px; border: 0; border-radius: var(--radius-pill);
  font-size: 12px; line-height: 15px; font-weight: 500; font-variant-numeric: tabular-nums; }
.pill svg { width: 16px; height: 16px; flex: 0 0 auto; }

/* ---- §4.8 · o badge continua CHIP (o `--plain` tira só o ponto, não o fundo) ---- */
.badge { height: 20px; padding: 0 8px; gap: 4px; border: 0; border-radius: var(--radius-pill);
  font-size: 12px; line-height: 15px; font-weight: 500; }

/* ---- §4.9 · a tabela ----
   ⛔⛔ MEDIDO na tela `renovacoes`: o `th` solto (0,0,1) PERDIA para `.table thead th` (0,2,1) da
      linha 937 — o cabecalho continuava `uppercase` 11px. A conferencia pegou; a regra subiu de nivel.
   ⚠️ E `.table thead th` puro atropelaria a coorte, que e `class="table cohort-table"` e tem grade
      propria (11px, padding 2px 10px 8px, centralizada). O `:not(.cohort-table)` guarda as duas.
   ⛔ Do bloco antigo ficam de pe: `position:sticky`, `top`, `z-index`, `background`, `text-align`,
      `border-bottom` e `white-space:nowrap` — nada disso e aparencia de tipo. */
th { height: 32px; padding: 0 12px; font-size: 13px; line-height: 17px; font-weight: 500;
  color: var(--text-2); text-transform: none; letter-spacing: 0; }
.table:not(.cohort-table) thead th { height: 32px; padding: 0 12px;
  font-size: 13px; line-height: 17px; font-weight: 500; letter-spacing: 0;
  text-transform: none; color: var(--text-2); }
td { padding: 8px 12px; height: 46px; border-bottom: 1px solid rgba(255,255,255,.04); }
.table:not(.cohort-table) tbody td { padding: 8px 12px; height: 46px; }
/* ⛔ `tabular-nums` só onde há número — não na tabela inteira */
td.num, th.num, td .num { font-variant-numeric: tabular-nums; }

/* ---- §4.7.b · o BOTÃO PRIMÁRIO: branco com tinta quase-preta ----
   ⭐⭐ CORRIGIDO pelo Matheus, 12/09: "em botões e ícones deve ser branco".
   ⛔⛔ A prosa do lote (§6.1 item 20) pedia o botão AZUL, mas o INSUMO manda o contrário —
      `.btn--primary{background:var(--cds-fill-primary);color:var(--cds-on-primary)}` com
      `#fff`/`#0b0b0b` (painel-claude-ui.html:34,307). A lei do lote é "copie os valores de lá,
      não da prosa", e o Matheus confirmou por escrito. Vale o espécime.
   ⛔ Ele lia `var(--brand)`/`var(--on-brand)`, que continuam LARANJA de propósito (o `--brand` é
      do logo e ficou fora do `:root:root`). Como o `--brand-hover` JÁ tinha saído do laranja, o
      botão ficou laranja parado e claro no hover — par quebrado que estas 4 linhas fecham.
   ⭐ MEDIDO: 16,9:1 nos dois temas. ⚠️ Esta regra precisa vir DEPOIS da linha ~725; com a mesma
     especificidade (0,1,0), quem está embaixo é que vale. */
.btn--primary { background: #f0efec; color: #0b0b0b; }
.btn--primary:hover { background: #ffffff; }
:root[data-theme="light"] .btn--primary { background: #18181B; color: #FAFAFA; }
:root[data-theme="light"] .btn--primary:hover { background: #000000; }

/* ---- §4.2.b · a LATERAL no tema claro ----
   ⭐ O espécime é dark-only, então não há valor a copiar: derivo da escada que o light do repo já
     tem — página `--bg` #FAFAFA, lateral um degrau abaixo, `--surface-2` #F4F4F5. A separação
     página/lateral que o espécime pede vale nos DOIS temas, em vez de só no escuro.
   ⭐ REMEDIDO depois: `.nav-group__title` (#6B6B76) sobre #F4F4F5 = 4,61:1 — passa o AA. */
:root[data-theme="light"] .sidebar { background: #F4F4F5; }
/* ⛔ o realce do item ativo do §4.2 e alfa BRANCO (`rgba(255,255,255,.15)`): sobre a lateral clara
   ele some. MEDIDO no light: o TEXTO continua legivel (16,12:1), mas o ESTADO deixa de aparecer.
   ⭐ No claro o realce inverte para alfa PRETO. (Nao e regressao do C241: antes era `--surface-2`
     = #F4F4F5 sobre --bg #FAFAFA, igualmente invisivel. Fica consertado agora.) */
:root[data-theme="light"] .nav-item.is-active { background: rgba(0,0,0,.07); }
/* o avatar neutro do §3.5 também inverte: alfa BRANCO não aparece sobre fundo claro */
:root[data-theme="light"] .sidebar__avatar { background: rgba(0,0,0,.08); color: var(--text-1); }


/* ============================================================================================
   ⭐⭐⭐ C241b · A TIPOGRAFIA — a camada que o C241 não tinha como mover.
   ⛔⛔ POR QUE ELA NÃO ENTROU ANTES: o §2 do lote mandava "re-apontar o VALOR dos tokens que já
      existem", e a tipografia do painel **não é tokenizada**. MEDIDO no `app.css`: 185 regras com
      `font-size` LITERAL, 21 tamanhos distintos, 38 com `letter-spacing`, 23 em CAIXA-ALTA e
      **zero** token de tipo no `:root`. Um lote que só troca valor de token jamais moveria isto.
   ⛔ AQUI NÃO SE TOKENIZA NADA. Tokenizar os 185 é refatoração de sistema e é outro lote.
      Só entram as 7 peças que o espécime especifica, uma a uma, com o valor medido lá.
   ⚠️ Todas estas regras precisam vir DEPOIS das originais (`.nav-item` 443, `.btn` 744,
     `.field label` 772, `.input` 777, `.content__head h1` 486): mesma especificidade perde pela
     ordem — conferido por posição de byte no script que as escreveu.
   ============================================================================================ */

/* 22 · item de nav — era 500 · 13px/19,5 */
.nav-item { font-size: 14px; line-height: 20px; font-weight: 400; letter-spacing: normal; }

/* 23 · rótulo do KPI — era 500 · 12px/18 */
.kpi__label { font-size: 14px; line-height: 20px; font-weight: 500; color: var(--text-2); }

/* 24 · h1 da página — era 600 · 20px/24 · ls -0.2px.
   ⛔ O `-.2px` vem de `h1,h2,h3,h4` (linha 240), que é (0,0,1) e vale para o painel inteiro.
     Esta regra é (0,1,1) e o anula SÓ no h1 de página — os outros títulos não se movem, porque
     o §3.2 não os mediu. */
.content__head h1 { font-size: 22px; line-height: 28px; font-weight: 500; letter-spacing: normal; }

/* 25 · rótulo de campo — era 500 · 11px · CAIXA-ALTA · ls .8px · --text-3.
   ⭐ É a mesma família do `th` que o §12-1 já consertou: a convenção de micro-rótulo do painel
     (11px, caixa-alta, tracking) que o espécime não tem em lugar nenhum.
   ⛔ `.cms-root .field label` (0,2,2) e `.cms-listhead label` continuam vencendo esta (0,1,1) —
     o CMS tem escala própria e não é deste lote. */
.field label { font-size: 13px; line-height: 17px; font-weight: 500; color: var(--text-2);
  text-transform: none; letter-spacing: normal; }

/* 26 · botão — era 500 · 13px/19,5. ⚠️ SÓ O TIPO: o fundo branco do §10.3 não muda, e a
   `height: 40px` também não (o espécime usa 32, mas o adendo pediu a linha de tipo, não a caixa). */
.btn { font-size: 14px; line-height: 20px; font-weight: 400; }

/* 27 · campo — era 13px com `border: 1px solid var(--border-1)` e nenhum anel.
   ⭐ O espécime (linha 297) usa `border:0` + `box-shadow: inset 0 0 0 1px` — a mesma lei do card no
     §4.1: "borda come caixa, anel não".
   ⛔⛔ O PAR DE BRAÇOS: `:hover` e `:focus` pintavam `border-color`. Com `border:0` os dois virariam
      NADA — o hover sumiria e o foco perderia o contorno. Por isso os três estados mudam juntos, e
      o `:focus` mantém o anel externo de 3px POR CIMA do interno.
   ⚠️ CEDÊNCIA DECLARADA: o espécime pede fundo branco 5%; fica `var(--field-bg)` (4% no escuro).
     O delta de 1% é invisível, e cravar alfa branco criaria no tema claro exatamente o defeito que
     o §12-5 acabou de custar — o token já é tematizado nos dois. Mesmo motivo para `--border-2`,
     que no escuro vale exatamente os 10% do espécime. */
.input, .select { font-size: 14px; border: 0; box-shadow: inset 0 0 0 1px var(--border-2); }
.input:hover, .select:hover { box-shadow: inset 0 0 0 1px var(--border-hover); }
.input:focus, .select:focus { box-shadow: inset 0 0 0 1px var(--brand-ink), 0 0 0 3px var(--brand-soft); }

/* 28 · o respiro lateral do conteúdo — era 24 nos quatro lados.
   ⛔⛔ DENTRO DE `min-width` DE PROPÓSITO: existe `@media (max-width:560px){ .content{padding:16px} }`
      na linha ~1115, e este bloco está DEPOIS dela no arquivo. Uma regra solta aqui devolveria
      32px de lateral ao celular e comeria a tela. O breakpoint segue a convenção `.02` do repo. */
/* ⭐⭐ O RESPIRO LATERAL **E** A CENTRALIZACAO, na mesma declaracao.
   `max(32px, calc((100% - 1200px)/2))`:
     · tela larga  -> a sobra alem de 1200 vira padding igual dos dois lados = conteudo centrado
     · tela apertada -> o `max` segura o piso de 32px e nada encolhe
   ⛔ DENTRO DE `min-width` de proposito: existe `@media (max-width:560px){ .content{padding:16px} }`
      ANTES deste ponto no arquivo; uma regra solta aqui devolveria o padding grande ao celular.
      O breakpoint segue a convencao `.02` do repo. */
@media (min-width: 560.02px) {
  .content { padding: 24px max(32px, calc((100% - 1200px) / 2)); }
}


/* ============================================================================================
   ⭐⭐⭐ C241c · O QUE O CENSO DE VOCABULÁRIO ACHOU — e as três amostragens não acharam.
   ⛔ O método mudou: em vez de perguntar "a peça X está certa?", varre-se TUDO que pinta dos dois
     lados com O MESMO script e comparam-se os CONJUNTOS. Medido: espécime 595 nós / 213 com texto;
     painel 530 / 153.
   ============================================================================================ */

/* ---- ACHADO 1 · 21 nós do painel estavam em JetBrains Mono; o espécime é 100% Manrope ----
   ⭐ O Console não troca de família para número nenhum: usa a MESMA face com `tabular-nums`, que
     alinha os dígitos sem mudar a letra. É a receita que estas duas regras passam a seguir.
   ⛔⛔ PARADA DURA: só estas duas, que são as que o espécime especifica. `.num`/`.mono` é classe
      UTILITÁRIA e as outras três (`.projcell__value`, `.stockcard__value`, `.projprog__txt`) são de
      telas que o adendo não mediu. MEDIDO nas 38 telas: 1.359 nós em monoespaçada — `td.num` (307),
      `td.cell-time` (128), `td.mono` (120)… Trocar a família delas muda TODA tabela do painel, e a
      contagem está no relatório para o Matheus decidir.
   ⚠️ CONFERIDO antes de agir (o adendo manda): `--font-mono` NÃO é dado de operador — o `tema.php`
     nunca o emite (0 ocorrências lá); só `--font-sans` era, e o painel já nem recebe isso. */
.kpi__value { font-family: inherit; font-variant-numeric: tabular-nums; }
.pill { font-family: inherit; }   /* o `font-variant-numeric: tabular-nums` dele já estava lá */

/* ---- ACHADO 2 · tracking em 15 nós; o espécime tem ZERO ----
   ⛔ O `adendo-C241b` mandou tirar o tracking do h1 DE PÁGINA, e eu nomeei só o `h1` — os `h3` dos
     cards ficaram. MEDIDO nas 38 telas: `h3` = 50 nós · `.topbar__title` = 38.
   ⭐ O tipo do `h3` também estava fora: painel `500 13px/15.6` (13 × o `line-height:1.2` do
     `h1,h2,h3,h4`), espécime `500 14px/20`.
   ⛔⛔ A regra global `h1,h2,h3,h4 { letter-spacing: -.2px; line-height: 1.2 }` NÃO É TOCADA: ela
      alcança as 38 telas de uma vez, e o §2 do adendo proíbe. Estas regras a anulam apenas onde o
      censo mediu — o título de card e o da topbar. */
.card__head h3 { font-size: 14px; line-height: 20px; letter-spacing: normal; }
.topbar__title { letter-spacing: normal; }

/* ---- ACHADO 3 · as TRÊS primeiras assinaturas órfãs, por nós somados nas 38 telas ----
   1º `400 13px/19.5` — 952 nós — `td.num` (307) · `td` (137) · `td.mono` (120)
   2º `500 13px/19.5` — 513 nós — `span.cell-strong` (321) · `td.num.cell-strong` (108)
   3º `400 12px/18`   — 383 nós — `td.cell-time` (128) · `td.dim` (84) · `span.muted` (35)
   ⭐ As três são a MESMA causa: entrelinha 1.5 herdada do `body` em vez do degrau fixo. E as três
     casam exatamente com as que faltam no espécime: `500 13/17` (23 nós lá), `400 12/17` (19),
     `400 13/17` (14).
   ⛔ `body { line-height: 1.5 }` é a raiz, e mexer nela alcançaria as 38 telas de uma vez — a mesma
     proibição do §2. Então o degrau é fixado NOS NÓS que pintam estas três, e mais nada.
   ⭐ A 2ª resolve por herança: `.cell-strong` vive dentro do `td` e só carrega o peso.
   ⛔ `:not(.cohort-table)` pelo mesmo motivo do §12-1: a coorte é `class="table cohort-table"` e tem
     grade própria. ⛔ E PARO na 4ª (`400 11px/16.5`, 328 nós, `.cell-sub`): mexer nela é mudar
     TAMANHO (11px não existe na escala do espécime), não só entrelinha — vai na lista. */
.table:not(.cohort-table) tbody td { font-size: 13px; line-height: 17px; }
.cell-time { line-height: 17px; }


/* ============================================================================================
   ⭐⭐⭐ C241d · O CENSO DE COMPOSIÇÃO — o que o de vocabulário (C241c) não vê.
   ⛔ Vocabulário mede ÁTOMOS (tipo, cor, forma, contorno). Este mede COMPOSIÇÃO: alinhamento,
     margem, grade, ritmo. Passar num não diz nada sobre o outro.
   ⛔ Os dois lados medidos com o MESMO script e na MESMA largura (1440) — foi o que tornou o
     achado da grade conclusivo, que antes estava marcado como não-conclusivo por janelas diferentes.
   ============================================================================================ */

/* ---- ACHADO 1 · `text-align: center` em 17 nós; o espécime tem 4 ----
   ⭐⭐ A CAUSA, medida: o `.nav-group__title` virou `<button>` no C241 §4.2, e **botão centraliza
     texto por padrão do agente de usuário**. Eu escrevi a regra dele inteira e não declarei
     `text-align` — 10 dos 17 centralizados são esses títulos de grupo.
   ⛔ Os outros 7 NÃO são tocados, e cada um por um motivo medido:
     · 1 é `span` dentro de `.pdd__trig` ("Hoje") — ⭐ o espécime CENTRALIZA o equivalente
       (`span` dentro de `button.sel__btn`, "Este mês"). Mexer aqui repetiria o precedente do
       "tudo à esquerda", que teria quebrado três peças que a prancha manda centralizar.
     · 6 são `b`/`p` dentro de `.state` (os estados vazios: "Sem vendas no período"). ⚠️ A peça
       NÃO EXISTE no espécime — e o adendo é explícito: peça ausente não se decide sozinho,
       lista e devolve. Está no relatório. */
.nav-group__title { text-align: left; }

/* ---- ACHADO 2 · o RITMO VERTICAL: cinco espaçamentos contra um ----
   ⭐⭐ O espécime tem UM ritmo de bloco: `0 0 16px`, em 7 nós — e são EXATAMENTE os mesmos
     seletores onde o painel usava outro valor: `.split-2`, `.content__head`, `.kpi-grid`, `.card`.
   ⭐⭐ E dentro do KPI o espécime NÃO usa margem: usa `gap` (`.kpi{gap:var(--cds-pad-sm)}` = 8px,
     espécime:214). O painel empilhava `margin-bottom` no rótulo (10px) e no valor (8px) — que eram
     justamente as DUAS de maior contagem (14 nós cada, 28 no total). Trocá-las por `gap` remove as
     duas de uma vez e casa com o espécime, em vez de só trocar o número.
   ⛔ CORREÇÃO DE UM ERRO MEU: o C241 §4.6 pôs `.kpi { gap: 16px }`. O espécime usa `--cds-pad-sm`,
      que é **8px**. O censo de composição pegou; o de átomos não via margem nem gap. */
.kpi { gap: 8px; }
.kpi__label { margin-bottom: 0; }
.kpi__value { margin-bottom: 0; }
.split-2 { gap: 16px; margin-bottom: 16px; }
.kpi-grid { margin-bottom: 16px; }
.content__head { margin-bottom: 16px; }

/* ---- ACHADO 3 · a grade de KPI: 5 colunas contra 4 ----
   ⭐ CONCLUSIVO agora que os dois lados rodaram em 1440 — e era uma linha, como o adendo previu:
       espécime  `repeat(auto-fit, minmax(214px, 1fr))` · gap 16  ->  4 col × 256px
       painel    `repeat(auto-fit, minmax(200px, 1fr))` · gap 12  ->  5 col × 212px
   ⛔⛔ DENTRO DE `min-width` DE PROPÓSITO: existe `@media (max-width:560px){ .kpi-grid{
      grid-template-columns: repeat(2,1fr) } }` na linha ~1119, ANTES deste ponto no arquivo. Uma
      regra solta aqui devolveria o `auto-fit` ao celular e desfaria as 2 colunas. Mesma armadilha
      do `.content` no C241b — e o breakpoint segue a convenção `.02` do repo. */
@media (min-width: 560.02px) {
  .kpi-grid { grid-template-columns: repeat(auto-fit, minmax(214px, 1fr)); gap: 16px; }
}

/* ---- §8.1 do adendo · o `.cell-sub` vai a 12px/17 ----
   ⭐ O Matheus liberou: `12px/17` É um degrau do espécime (19 nós lá), não valor novo. E os 174 nós
     eram a maior órfã da fila do C241c — a primeira pelo critério de "quantos nós pinta".
   ⚠️ A condição dele: medir o CONTRASTE depois; se cair abaixo de 4,5:1, parar e reportar.
     MEDIDO e registrado no relatório.
   ⛔ Era `font-size: 11px` — e 11px não existe na escala do espécime, que era a causa raiz de eu
     ter trocado uma órfã por outra no C241c §29. */
.cell-sub { font-size: 12px; line-height: 17px; }


/* ============================================================================================
   ⭐⭐⭐ C241e · O LADO A LADO — medido nos DOIS lados a 1200×564, a MESMA largura.
   ⛔ As quatro rodadas anteriores compararam larguras diferentes (2560 × 1200) e por isso não
     acharam isto. "Medida pertence ao nó e ao ESTADO em que foi tomada."
   ============================================================================================ */

/* ---- A3 · o cartão deixa de ter fundo próprio ----
   ⛔⛔ ESTA É A PEÇA INTEIRA. No Console o cartão É a página — quem o desenha é o anel interno de
      1px. Aqui ele era um bloco cinza sobre um fundo mais escuro: `.card − body = +6` em cada canal.
   ⭐ Com a A1 (`--bg: #0b0b0b`) e esta linha juntas, o delta vai a 0,0,0 — a prova C3 da rodada. */
.card { background: var(--bg); }

/* ---- A4 · a topbar volta a 56 ---- */
:root { --topbar-h: 56px; }

/* ---- A5 · o filete de CASCA é 4%, não 6% ----
   ⭐ O espécime separa duas coisas que o painel tratava como uma: **anel de componente 6%** e
     **linha de casca 4%**. ⛔ O token `--border-1` NÃO é tocado — ele é o anel, e o anel está certo
     (medido: 24 nós em 6%, igual ao espécime). Só os quatro filetes de casca descem. */
.sidebar { border-right-color: rgba(255,255,255,.04); }
.topbar { border-bottom-color: rgba(255,255,255,.04); }
.sidebar__foot { border-top-color: rgba(255,255,255,.04); }

/* ---- A6 · o bloco de marca perde o filete e volta a 16 de padding ---- */
.sidebar__brand { border-bottom: 0; padding: 0 16px; }

/* ---- A7 + A8 · a estrutura de rolagem da lateral ----
   ⛔ No espécime quem rola é a NAV, não o `aside`. É isso que permite o rodapé ficar em
     `margin-top:auto` sem `sticky` — e sem `sticky` ele não precisa de fundo próprio.
   ⭐ Era o fundo `#050505` do rodapé que deixava a faixa do usuário MAIS ESCURA que a lateral.
     Com a estrutura certa, a faixa volta a ser a lateral. */
.sidebar { overflow: visible; }
.sidebar__nav { flex: 1; overflow-y: auto; }
.sidebar__foot { position: static; background: transparent; }

/* ---- A9 · o marcador do item ativo sai ----
   ⚠️ NEUTRALIZADO com `content: none` em vez de apagar a regra: é reversível numa linha, e apagar
     regra alheia no meio do arquivo é o tipo de edição que já me custou caro neste lote. */
.nav-item.is-active::before { content: none; }

/* ---- A10 · o item de nav ---- */
.nav-item { padding: 0 8px; gap: 12px; }
.nav-group__items .nav-item { padding-left: 40px; }

/* ---- A11 · o título de grupo sai do muted e vai para o secundário ----
   ⭐ Medido: espécime `#c3c2b7` (--text-2), painel `#898781` (--text-3). Os grupos ficavam apagados. */
.nav-group__title { color: var(--text-2); }

/* ---- A16 · o rodapé do KPI ----
   ⚠️ O `margin-top:auto` JÁ existia (app.css:659) — o `13.5px` que a medição mostrou é o computado
     de `auto`, não um valor cravado. Só o tamanho muda. */
.kpi__foot { font-size: 12px; }

/* ---- A17 · a altura do card de KPI sai do conteúdo ---- */
.kpi { min-height: auto; }

/* ---- A18 · o gatilho do período ---- */
.pdd__trig { padding: 0 8px; }


/* ============================================================================================
   ⭐⭐⭐ C242 · O CARTÃO — e os 5 px que não eram do cartão.
   ⛔ Medido a 1200 × 564 (innerWidth, nunca o tamanho do PNG), com transição morta antes.
   ⛔ Esta especificação NÃO migra padding entre pai e filho: `.card{padding:0}` FICA. Só troca
     VALOR em regra que já existe — por isso o número de cartões na tela não entra na conta.
   ============================================================================================ */

/* ---- §1 · o subtítulo do cabeçalho de página ----
   Medido: painel 13px/muted × espécime 14px/secundário. Era a divergência 3.3 do C241e, que eu
   tinha devolvido sem canetada; o lote deu a canetada. */
.content__head p { font-size: 14px; color: var(--text-2); }

/* ---- §2 + §3 · o cabeçalho e o corpo do cartão ----
   Os invariantes do espécime, e o que os entrega:
     I1 · não existe filete entre cabeçalho e corpo  -> `border-bottom: 0`
     I2 · recuo interno de 16 nos quatro lados       -> 16 nas laterais de ambos
     I3 · 12 entre a base da linha do cabeçalho e o topo do corpo -> `padding-top: 12px` do corpo
     I4 · a linha do cabeçalho tem a altura do texto -> `padding-bottom: 0` no cabeçalho
   ⭐ MEDIDO ANTES (S1 do lote): a sangria NÃO é feita por margem negativa. `.card__body--flush`
     (app.css:644) zera o padding direto. ⛔ Então ZERO regras precisam mudar de -20 para -16 —
     o suposto S1 do lote está REFUTADO, e para melhor: não há nada a re-apontar.
   ⭐ MEDIDO (S3): varri os 52 corpos de card das 38 telas e catalogei TODOS os filhos diretos.
     NENHUM tem margem negativa; as margens que existem são positivas (`0 0 8px`, `4px 0 0`,
     `12px 0`). Eles dependem de HAVER padding, não do valor — 20 -> 16 muda 4 px, não quebra. */
.card__head { padding: 16px 16px 0; border-bottom: 0; }
.card__body { padding: 12px 16px 16px; }

/* ---- §2 · I5 · o corpo SANGRADO ----
   ⛔ `.card__body--flush` era `padding: 0` e, com o cabeçalho agora sem `padding-bottom`, o conteúdo
      sangrado encostaria na linha do título. O I5 pede as duas coisas ao mesmo tempo: encostar nas
      bordas LATERAL e INFERIOR, e manter os 12 abaixo do cabeçalho. */
.card__body--flush { padding: 12px 0 0; }

/* ---- §4 · o rodapé do KPI — os 5 px que não eram do cartão ----
   ⭐⭐ A conta do lote, conferida por mim no painel: `padding:4px 0 0` + `line-height:18px` = 22;
     o espécime é `padding:0` + `line-height:17` = 17. A diferença de 5 é exatamente o delta de
     altura do card (126 × 121). ⛔ O `.card` não entra nessa conta em lugar nenhum.
   ⭐ PREVISÃO FALSEÁVEL, escrita antes de medir: 20 + 8 + 36 + 8 + 17 + 16 + 16 = 121. */
.kpi__foot { padding: 0; line-height: 17px; }

/* ---- §3 · a DIVISORIA que o filete segurava, devolvida a PECA que dependia dela ----
   ⛔⛔ MEDIDO NO RENDER (nao deduzido do CSS): dos 52 cards com cabecalho, 20 tem corpo sangrado
      logo abaixo, e 8 desses trazem TABELA. Nesses 8 o `border-bottom` do cabecalho era a UNICA
      linha entre o titulo e o `thead` — porque `.table thead th` tem borda em BAIXO e nenhuma em
      CIMA. Telas: estoque · registros · clientes · renovacoes · vendas · custos · projecao (x2).
   ⭐ O §3 do lote previu exatamente isto e deu a regra: "o filete daquela peca e DELA — desca-o para
     dentro do corpo, nao devolva ao cabecalho". E o que esta linha faz: a borda nasce no `thead`,
     nao no `.card__head`, entao o I5 (os 12 abaixo do cabecalho) continua valendo.
   ⚠️ Escopado em `.card__head + .card__body--flush`: so alcanca a tabela que perdeu a divisoria.
     Tabela dentro de corpo com padding, ou sangrada sem cabecalho acima, nao ganha borda nenhuma. */
.card__head + .card__body--flush .table thead th { border-top: 1px solid var(--border-1); }

/* ---- §2 · o ANCORA de 20 que acompanha o corpo ----
   ⭐ Achado do S1 (correcao de rota do proprio agente): o `.pager` mora DENTRO do mesmo cartao,
     logo abaixo da tabela sangrada, e copia o recuo de 20 a mao. Com o corpo em 16, ele ficaria
     4px fora do encaixe.
   ⚠️ O outro ancora que o levantamento citou, `#ovInsights` (app.css:592), foi medido como CSS
     MORTO — `id="ovInsights"` tem 0 ocorrencias no repo. NAO foi tocado. */
.pager { padding: 14px 16px; }


/* ============================================================================================
   ⭐⭐⭐ C243 · O VOCABULÁRIO DE CONTROLE — as 7 queixas são 4 defeitos de sistema.
   ⛔ Medido a 1400 × 900 (innerWidth declarado), com transição morta antes de cada medida.
   ============================================================================================ */

/* ---- §2 · S2 · a altura vem do PAPEL ----
   ⭐ MEDIDO nas 38 rotas: 388 botões a 32 · 19 a 40 · 2 a 28. E a estrutura explica: `.btn` é 40 por
     padrão (app.css:767) e `.btn--sm` é 32 (app.css:781) — ou seja, **388 peças já pedem `--sm` na
     mão para chegar ao 32**. O papel "controle de barra" é o padrão DE FATO; o 40 é que é exceção.
   ⛔⛔ MEDIDO E CORRIGIDO NO MÉTODO: meu primeiro detector agrupou os controles por TOPO e devolveu
      "0 linhas mistas" — eu teria reportado que o §2 já passava. Peças de alturas diferentes
      CENTRALIZADAS numa barra têm topos diferentes e o MESMO centro. Reagrupado por centro:
      **90 linhas mistas em 7 telas** (registros · epg · campanhas ×3 · custos · lancamentos).
   ⭐ A regra do lote decide sozinha: barra = 32; campo de formulário e o botão que SUBMETE aquele
     formulário = 40. Medido: dos 19 a 40, **17 estão fora de `<form>`**. */
.btn { height: 32px; }
form .btn--primary, form button[type="submit"] { height: 40px; }

/* ---- §5 · o modal ----
   ⛔ `border` -> ANEL (a mesma lei do cartão, C241e §1.3, agora no flutuante).
   ⛔ padding 22 -> 24: o 22 não existe na escala (16 `pad-lg` · 24 `pad-xl`).
   ⚠️ DECLARADO, e o lote já o diz: o espécime NÃO TEM modal. O cromo vem do `.pop`, a peça
     flutuante do Console; **o padding 24 é escolha de degrau da escala, não medição.** Fica dito
     para ninguém citar isto depois como número medido.
   ⭐ Os botões `Cancelar`/`Salvar` caem para 32 pelo §2 acima — são ação de diálogo na mesma linha,
     não submit de formulário longo. */
.modal__card { border: 0; box-shadow: inset 0 0 0 1px var(--border-1), 0 12px 32px rgba(0,0,0,.53);
  padding: 24px; }

/* ---- §7 · P1 · o fundo do campo ----
   ⭐ Um ponto percentual, e é a única divergência do campo: altura 40, raio 8, padding `0 12` e anel
     10 % já batem com o espécime. ⛔ A altura 40 do `.input` NÃO se toca (§10 do lote). */
.input, .select { background: rgba(255,255,255,.05); }


/* ---- §1 · S1 · o contorno de CONTROLE é ANEL INTERNO, nunca `border` ----
   ⭐ PARADA NÃO DISPAROU: varri o app.css e são **8 regras ativas** de border em peça de controle
     (o limite do lote é 40). Duas delas são HAIRLINE DIRECIONAL e ficam FORA, como o §1.2 manda:
       · `.input-group__prefix { border-right }` — divisória de 1px de um lado só
       · `.tabs .tab { border-bottom: 2px }`      — o indicador da aba ativa
     Outras duas já estavam mortas: `.pill` e `.badge` receberam `border: 0` no C241 §4.7/§4.8.
   ⛔⛔ O PAR DE BRAÇOS, e ele quase me pegou: existem **18 regras `border-color` órfãs** que pintam
      a cor de uma borda sem declarar largura (`.btn--outline`, `.btn--outline:hover`,
      `.btn.is-copied`, `.btn--wa`, `:hover`, `.input-group:hover`, `:focus-within`…). Tirar a
      `border` do `.btn` sem converter essas 18 deixaria o contorno do botão secundário, o estado
      "copiado" e o foco do grupo de campo **pintando nada**. Por isso cada estado vem junto.
   ⚠️ `border` come caixa: com `border: 0` a peça ganha 2px em cada eixo. O lote manda REMEDIR —
     medido e reportado no relatório (E1). */
.btn { border: 0; }
.btn--outline { box-shadow: inset 0 0 0 1px var(--border-1); }
.btn--outline:hover { box-shadow: inset 0 0 0 1px var(--border-2); }
.btn.is-copied { box-shadow: inset 0 0 0 1px var(--green-border); }
.btn--wa { box-shadow: inset 0 0 0 1px var(--border-1); }
.btn--wa:hover { box-shadow: inset 0 0 0 1px var(--whatsapp); }
.tbl-cards .tbl-acoes .btn--danger-ghost { border: 0; box-shadow: inset 0 0 0 1px var(--border-2); }

.input-group { border: 0; box-shadow: inset 0 0 0 1px var(--border-1); }
.input-group:hover { box-shadow: inset 0 0 0 1px var(--border-hover); }
.input-group:focus-within { box-shadow: inset 0 0 0 1px var(--brand-ink), 0 0 0 3px var(--brand-soft); }

.segmented { border: 0; box-shadow: inset 0 0 0 1px var(--border-1); }

/* ---- §1 · as 11 `border-color` ORFAS — uma REGRESSAO do C241 que estava NO AR ----
   ⛔⛔ ACHADO pelo levantamento do §1, e e defeito vivo, nao teoria: o C241 pos `border: 0` em
      `.card`, `.pill` e `.badge`, mas 11 regras continuavam pintando `border-color` sobre largura
      ZERO — ou seja, **pintando nada**. Quem perdeu o contorno, medido:
        · `.pill.up/.dn/.flat` — TODA pill de variacao de KPI (app.js:4057, `class="pill ${cls}"`)
        · `.badge--green/red/brand/blue/purple/amber` — 6 variantes, 55 usos de `badge--` no app.js
        · `.seg-card:hover/.is-active` — o realce do cartao de faixa ativo (app.js:3399)
        · `.vivo-kpi-click:hover > .card` — o realce do KPI clicavel de Vendas ao Vivo
   ⭐ Cada uma volta como ANEL, com a MESMA cor que ja estava escrita. Nenhuma cor nova. */
.pill.up { box-shadow: inset 0 0 0 1px var(--green-border); }
.pill.dn { box-shadow: inset 0 0 0 1px var(--red-border); }
.pill.flat { box-shadow: inset 0 0 0 1px var(--border-1); }
.badge--green { box-shadow: inset 0 0 0 1px var(--green-border); }
.badge--red { box-shadow: inset 0 0 0 1px var(--red-border); }
.badge--brand { box-shadow: inset 0 0 0 1px var(--brand-border); }
.badge--blue { box-shadow: inset 0 0 0 1px var(--blue-border); }
.badge--purple { box-shadow: inset 0 0 0 1px var(--purple-border); }
.badge--amber { box-shadow: inset 0 0 0 1px var(--amber-border); }
.seg-card:hover, .seg-card.is-active { box-shadow: inset 0 0 0 1px var(--brand-border); }
.vivo-kpi-click:hover > .card { box-shadow: inset 0 0 0 1px var(--border-hover); }

/* ---- §4 · S4 · a PEÇA NOVA: `.icobtn` ----
   ⭐ Copiada do espécime, não inventada (painel-claude-ui.html). MEDIDO: `.icobtn` tinha 0
     ocorrências no painel — é a única peça nova deste lote.
   ⛔⛔ A condição não-negociável do §4: o rótulo NÃO some, vira NOME ACESSÍVEL — `aria-label` E
      `title` em cada botão. Sem isso o conserto vira defeito: 57+ botões mudos para leitor de tela
      e sem dica para quem não reconhece o glifo. O CSS sozinho não garante isso; quem garante é o
      markup, e por isso a conversão é reportada tela a tela (ver relatório). */
.icobtn { width: 28px; height: 28px; padding: 0; border: 0; background: transparent;
  color: var(--text-3); border-radius: var(--radius-input);
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer; flex: 0 0 auto;
  transition-property: background, color; transition-duration: 150ms;
  transition-timing-function: var(--ease); }
.icobtn:hover { background: rgba(255,255,255,.075); color: var(--text-1); }
.icobtn svg { width: 16px; height: 16px; }
.icobtn--danger:hover { background: var(--red-soft); color: var(--red); }

/* ---- §2 + §4 · a LINHA DE ACAO inteira mede 28 ----
   ⛔⛔ DEFEITO QUE EU CRIEI E A REGUA PEGOU: converter as acoes COM icone para `.icobtn` (28) deixou
      as acoes SEM icone ("Inativar", "Vincular"…) em 32 na MESMA linha. O E3 subiu de 90 para 136.
   ⭐ A conciliacao sai das duas regras do proprio lote, sem inventar nada: o §2 diz "acao de icone
     e 28" e "numa mesma linha, todos tem a mesma altura — sem excecao"; o §4 diz que a acao de
     linha e 28. Entao a LINHA DE ACAO inteira e 28 — inclusive a que ainda mostra texto.
   ⛔ Nao converti essas para icone: elas nao tem glifo no catalogo, e a regra do projeto e
     "nao se desenha icone aqui — procura-se o path". Elas ficam com texto, na altura certa. */
.tbl-acoes .btn { height: 28px; padding: 0 10px; }
/* ⭐ E o mesmo para qualquer botao que divida a CELULA com um `.icobtn` — e a regra do §2 ("na
   mesma linha, todos iguais") aplicada ao escopo exato onde a mistura nasce. `:has()` ja foi
   medido como suportado neste projeto (C237). */
td:has(.icobtn) .btn, .tbl-acoes:has(.icobtn) .btn { height: 28px; padding: 0 10px; }


/* ============================================================================================
   ⭐⭐ ADENDO C243 · O CONSTRUTOR — B1, B2 e B3. ⛔ B4/B5/B6 ficam de fora, como o adendo manda.
   ⛔ Medido a 2560 × 1215 na rota `#/layout-site/cromo/footer`.
   ============================================================================================ */

/* ---- B1 · o construtor ocupa a largura útil inteira ----
   ⭐⭐ MEDIDO, e o `max-width` NÃO era uma regra do `.bld`: ele vinha do meu próprio
     `.content > * { max-width: 1200px }` (C241 §4.4). O `.bld` é filho direto do `.content` e
     herdava o teto. Por isso a oficina ficava em 1200 de 2304, com **1104 px de sobra preta**.
   ⛔⛔ A GUARDA DO C242 CONFERIDA ANTES DE MEXER — `.content:has(> .bld){overflow:hidden;padding:0}`
      depende de FILHO DIRETO. Esta linha **não cria invólucro nenhum**: o `.bld` continua filho
      direto do `.content`, e a guarda continua casando. Medido depois: ainda vale.
   ⭐ O teto de 100 % no palco JÁ EXISTE e não precisou de linha: `BLD.k = Math.min(1, disp / w)`
     (app.js:6950). O preview nunca passa de 100 %. */
.content > .bld { max-width: none; }

/* ---- B2 · a barra do construtor alinha com a topbar ----
   ⚠️ MEDIDO 51, não 59 como o adendo estimou — reportado. O padding `9px 14px` não é degrau da
     escala (6 · 8 · 12 · 16 · 24); vira `0 16px` com a altura declarada. */
.bld-top { height: var(--topbar-h); padding: 0 16px; }

/* ---- B3 · os raios entram na escala (7 · 8 · 12) ---- */
.bld-row { border-radius: 7px; }
.bld-pop { border-radius: 12px; }


/* ============================================================================================
   ⭐⭐⭐ C244 · A ALTURA VEM DO CONTEXTO, NÃO DO ELEMENTO.
   ⛔ Medido a 1400 × 900 (innerWidth declarado), transição morta antes.
   ⛔⛔ TUDO AQUI É ESCOPADO. O `.input` global segue 40 e o `.btn--sm` global segue 32 — o §6 do
      lote é explícito, e as duas travas foram conferidas por assert depois do patch.
   ============================================================================================ */

/* ---- §1 · o campo INLINE em linha de lista vai a 32 ----
   ⭐ MEDIDO, e foi a sondagem que deu o escopo: dos 123 campos de 40 do painel, **28 estão dentro
     de `.field`** (formulário, com rótulo acima → ficam 40) e **95 estão fora**. Dos 95:
       · `div.row < td`              → **84**  (o `/epg` inteiro)
       · `div.row < div.content`     →   4  (barra de tela)
       · `div.row < div.card__body`  →   3
       · `div.row.gap-1 < .card__head` → 2 · `div.search-field < .card__head` → 1
       · `form.row < div.card__body` →   1  ⛔ este NÃO entra: está dentro de `<form>`
   ⭐ A cadeia do `/epg`, medida no nó: `input.input.mono < div.row < td < tr < tbody < table`.
     Não é barra de filtro — é LINHA DE REGISTRO, um campo por canal com seus dois botões de 32.
     Pela regra do lote, campo inline em linha de lista é **32**.
   ⚠️ O `mono` FICA: a monoespaçada nesses campos tem função (o operador copia o id e precisa
     distinguir `l` de `1`). ⛔ Só a altura muda.
   ⛔ `textarea` fora de propósito — ele tem `height: auto` e cresce pelo `rows`. */
td .input:not(textarea), td .select,
.card__head .input:not(textarea), .card__head .select,
.content > .row .input:not(textarea), .content > .row .select,
.card__body > .row .input:not(textarea), .card__body > .row .select { height: 32px; }
/* ⚠️ O `select` entrou num 2o passo: a 1a medicao deixou 8 linhas mistas porque eu escopei so
   `.input` — os `<select>` nativos da MESMA barra continuavam em 40. Medido: registros · clientes ·
   campanhas x3 · vendas-vivo · custos · layout-site. Mesma regra, mesmo contexto, mesma altura. */

/* ---- §2 · o cluster de AÇÃO vai a 28 ----
   ⭐ O critério é o do lote: *a linha contém um `.icobtn`? então é cluster de ação, e tudo nela é 28.*
   ⭐ Os containers foram MEDIDOS, um a um, não inventados:
       · `.sms-camp__acoes` → **9** casos (Campanhas, SMS e E-mail)
       · `tr`               → **4** casos (Planos — aqui o `.icobtn` e o "Tornar destaque" vivem em
                                 `td`s DIFERENTES da mesma linha, por isso o container comum é o `<tr>`
                                 e o `td:has()` do C243 não os alcançava)
   ⛔ O 14º NÃO ENTRA: o container comum dele é `div.row`, que é genérico e vive em dezenas de telas.
      O lote manda parar nesse caso e reportar em vez de inventar seletor. Está no relatório. */
.sms-camp__acoes:has(.icobtn) .btn { height: 28px; padding: 0 10px; }
tr:has(.icobtn) .btn { height: 28px; padding: 0 10px; }

/* ---- §6 · G6 · o campo que PEDE `mono` volta a ser monoespacado ----
   ⛔⛔ DEFEITO PRE-EXISTENTE, achado pela conferencia G6 deste lote: `.input,.select{font-family:
      inherit}` (C241b §2 item 27, app.css:~782) vence `.mono` (app.css:265) POR ORDEM. Resultado
      medido no campo do `/epg`: familia **Manrope**, nao JetBrains Mono.
   ⭐ E e exatamente a pergunta que o C241d §44.6 deixou em aberto ("os campos que voce COPIA saem em
     fonte proporcional — deveriam ser mono?"). ⛔ O §6 DESTE lote responde: *"a fonte `mono` dos
     campos que se copiam — TEM FUNCAO"*. Com a resposta escrita, o reparo entra.
   ⚠️ Escopado a quem PEDE: so `.input.mono` / `.select.mono`. O `.input` sem `mono` continua
     herdando Manrope, e os 16 campos que o C241d listou voltam a ser monoespacados de proposito,
     nao por acidente de cascata. */
.input.mono, .select.mono, textarea.input.mono { font-family: var(--font-mono); }


/* ============================================================================================
   ⭐⭐⭐ C245 · O CENSO POR CLASSE — nada ganha ✅ sem a contagem nas 36 rotas.
   ⛔ Medido a 2560 × 1215 (innerWidth declarado), transição morta antes.
   ============================================================================================ */

/* ---- §2 · o CHECKBOX — 33 ocorrências (o lote contou 16) ----
   ⛔ O espécime NÃO desenha checkbox; o `.ck` dele é o check do popover, outra peça. ⚠️ Então isto é
     COMPOSIÇÃO das receitas já medidas (campo: fundo 5 %, anel 10 %, raio; primário: branco com
     tinta escura), e digo que é composição, **não medição**.
   ⛔ Nada de `accent-color`: ele pinta só o preenchimento e deixa a caixa do sistema. Medido no
     estado atual: `appearance: auto`, fundo transparente, sem anel, sem raio — a caixa era do SO.
   ⭐ O glifo do check é desenhado com `clip-path`, não com imagem: o catálogo `ICONS` é JS e não
     alcança pseudo-elemento de CSS. ⛔ Não desenhei ícone novo — é a forma do `check` em polígono. */
input[type="checkbox"] {
  appearance: none; -webkit-appearance: none;
  width: 16px; height: 16px; flex: 0 0 auto; margin: 0; padding: 0;
  border: 0; border-radius: 5px; cursor: pointer;
  background: rgba(255,255,255,.05);
  box-shadow: inset 0 0 0 1px var(--border-2);
  position: relative; display: inline-grid; place-content: center;
  transition-property: background, box-shadow; transition-duration: 150ms;
  transition-timing-function: var(--ease);
}
input[type="checkbox"]:hover { box-shadow: inset 0 0 0 1px var(--border-hover); }
input[type="checkbox"]:checked { background: #f0efec; box-shadow: none; }
input[type="checkbox"]:checked::after {
  content: ""; width: 10px; height: 10px; background: #0b0b0b;
  clip-path: polygon(41% 67%, 84% 24%, 92% 32%, 41% 83%, 8% 50%, 16% 42%);
}
input[type="checkbox"]:focus-visible { box-shadow: inset 0 0 0 1px var(--brand-ink), 0 0 0 3px var(--brand-soft); }
input[type="checkbox"]:disabled { opacity: .45; cursor: not-allowed; }

/* ---- §3 · as DATAS — 8 ocorrências ----
   ⭐ MEDIDO: a caixa JÁ está certa (`.input`, fundo 5 %, anel 10 %, raio 8, altura 32 pelo C244).
     ⛔ O que denuncia é o indicador de calendário do Chrome, que vem branco puro.
   ⚠️ `color-scheme: dark` já está no `:root` — conferido, não tocado (o lote manda não mexer).
   ⛔ O `filter` aqui NÃO estraga contraste: o indicador é um glifo do agente de usuário sobre o
     fundo do campo; `opacity .55` o leva ao tom de `--text-3`, que é o mesmo dos outros ícones de
     campo. Se ele sumisse, eu pararia — o lote é explícito nisso. */
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator,
input[type="month"]::-webkit-calendar-picker-indicator,
input[type="time"]::-webkit-calendar-picker-indicator {
  opacity: .55; cursor: pointer; width: 16px; height: 16px; padding: 0;
}
input[type="date"]::-webkit-calendar-picker-indicator:hover,
input[type="datetime-local"]::-webkit-calendar-picker-indicator:hover,
input[type="month"]::-webkit-calendar-picker-indicator:hover,
input[type="time"]::-webkit-calendar-picker-indicator:hover { opacity: 1; }
input[type="date"]::-webkit-datetime-edit,
input[type="datetime-local"]::-webkit-datetime-edit,
input[type="month"]::-webkit-datetime-edit,
input[type="time"]::-webkit-datetime-edit { font-family: inherit; font-size: 14px; color: var(--text-1); }

/* ---- §5 · o COLOR e o RANGE — 1 cada, ambos na Identidade ----
   ⛔ MEDIDO ANTES: o `input[type=color]` era o **único campo do painel fora do 5 %** — estava em
      `rgba(255,255,255,.04)` com `border: 1px`. Era o resíduo que o §1 procurava, e não estava na
      `Pop-up da home` como o lote supôs (ver o mapa de fundos no relatório).
   ⭐ A receita é a mesma dos outros campos; o polegar do range vai na cor primária do painel. */
input[type="color"] {
  width: 52px; height: 32px; padding: 3px; cursor: pointer;
  border: 0; border-radius: 8px;
  background: rgba(255,255,255,.05); box-shadow: inset 0 0 0 1px var(--border-2);
}
input[type="color"]::-webkit-color-swatch-wrapper { padding: 0; }
input[type="color"]::-webkit-color-swatch { border: 0; border-radius: 5px; }

input[type="range"] {
  appearance: none; -webkit-appearance: none;
  width: 100%; height: 16px; background: transparent; cursor: pointer;
}
input[type="range"]::-webkit-slider-runnable-track {
  height: 6px; border-radius: 99px; background: rgba(255,255,255,.10);
}
input[type="range"]::-webkit-slider-thumb {
  appearance: none; -webkit-appearance: none;
  width: 16px; height: 16px; margin-top: -5px; border: 0; border-radius: 99px;
  background: var(--brand-ink); box-shadow: 0 1px 3px rgba(0,0,0,.5);
}
input[type="range"]:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 3px var(--brand-soft); }

/* ---- §6 · as bordas de classe (a) — contorno de PEÇA vira anel ----
   ⭐ A SEPARAÇÃO, medida nas 36 rotas (o lote pediu as três contagens antes de converter):
       (a) contorno de peça      →    **34**   ← só estes convertem
       (b) hairline direcional   →  **2432**   ← permitido, não entra
       (c) mecanismo             →     **4**   ← spinner, indicador de aba, scrollbar
   ⛔ 34 está abaixo do limite de 60 regras do lote — a parada não dispara.
   ⚠️ `.alertbar` e `.stockcard` ficam de FORA de propósito: o C241 §3.3 declarou que os `-border`
     de green/red/amber existem porque essas peças são CAIXA, não controle. Converter caixa é outra
     decisão, e o §8 deste lote diz que o C241 é trava, não alvo. */
.sms-switch__tk { border: 0; box-shadow: inset 0 0 0 1px var(--border-2); }
.sms-switch input:checked + .sms-switch__tk { box-shadow: none; }
.cms-imgsel__prev { border: 0; box-shadow: inset 0 0 0 1px var(--border-2); }
.midia-hero.is-empty { border: 0; box-shadow: inset 0 0 0 1px var(--border-2); }
.proj-band__track { border: 0; box-shadow: inset 0 0 0 1px var(--border-1); }
/* ☠ MORTO (C250 §C) — o Estoque passou a usar `.card` + `.kpi`: o anel VERDE que valia para TODOS os estados */


/* ============================================================================================
   ⭐⭐⭐ C246 · O ICEBERG — medido pelo instrumento SEM FORMA (`tools/assinaturas.js`).
   ⛔ Espécime a 1400 × 900, painel nas 38 rotas a 2560 × 1215, MESMO arquivo nos dois lados.
   ⭐ H1 antes = **88,8 %** (3.654 de 4.113 nós fora do vocabulário) · 86 assinaturas × 31 aprovadas.
   ⛔ A ordem é por NÚMERO DE NÓS, não por classe que alguém nomeou.
   ============================================================================================ */

/* ---- §1 · A TABELA — 2.367 nós, a maior peça do painel ----
   ⭐⭐ A LIÇÃO DO C242 SE REPETIU: **2.367 nós saem de 13 regras**, e as que decidem são 3.
     Contei REGRAS, não nós, antes de tocar — foi o que o §1 mandou.
   ⭐⭐ E o ESPÉCIME CONFIRMA o alvo, o que eu não podia supor: as três assinaturas de hairline dele
     são **todas 14px** (`T | 0/0/1/0 <cor> | - | 0 | 14px | 400`), e a do painel era **idêntica,
     só com 13px**. Mesma forma, tamanho errado.
   ⚠️ E não há contradição com o C241c, que mediu `400 13px/17px` no espécime: aquilo eram OUTROS
     nós. O espécime usa 13px fora da tabela e 14px dentro dela.
   ⛔ O fundo do `th` (`--elevated`) é MECANISMO, não aparência: o `th` é `position: sticky` e sem
     fundo opaco o corpo passaria por baixo ao rolar. ⭐ Por isso ele não vira `T` como no espécime
     — vira `var(--bg)`: continua opaco, e passa a ser a cor da página, que é o que o espécime
     desenha. */
.table:not(.cohort-table) tbody td { font-size: 14px; line-height: 20px; }
.table:not(.cohort-table) thead th { font-size: 14px; line-height: 20px; background: var(--bg); }
/* o `.dim` DENTRO de célula é célula, não legenda — sobe junto. ⚠️ Fora da tabela ele continua
   12px, que é o degrau de caption do espécime e é legítimo. */
.table:not(.cohort-table) tbody td.dim,
.table:not(.cohort-table) tbody td .dim { font-size: 14px; line-height: 20px; }
/* `cell-strong` era `13px/500`, degrau que não existe: vai ao 14 e mantém só o peso.
   ⛔ CORRIGIDO: eu tinha escrito `font-size: inherit`, e `inherit` pega do elemento PAI — para um
      `td.cell-strong` isso é o `<tr>`, não o próprio `td`. A célula continuava em 13px. Valor
      explícito resolve, e cobre também `td.num.cell-strong`, que era a maioria dos 154 nós. */
.table:not(.cohort-table) tbody td .cell-strong,
.table:not(.cohort-table) tbody td.cell-strong { font-size: 14px; line-height: 20px; }

/* ---- §3 · os BADGES — 688 nós em 7 assinaturas. A FORMA normaliza; a TINTA não. ----
   ⛔⛔ A LEI DO §9, honrada: *"tinta de badge que vem do banco é DADO — normalize a forma, não a
      cor"*. MEDIDO: `rgb(29,22,73)` é `--purple-soft`, e `--blue-soft`/`--amber-soft`/`--green-soft`
      idem — são a paleta de estado do C241 §3.3, não valor solto. **Nenhuma cor foi tocada.**
   ⛔ O que sai é o DEGRAU QUE NÃO EXISTE: 84 badges a **9px** (no EPG). A escala do espécime tem
     12px/500 para chip — medido nas 8 assinaturas de tinta dele, todas `5px | 12px | 500`.
   ⭐ E o `badge--plain` (69 nós, `0,04` a 12px/500) já está na forma certa; só não tem tinta, que é
     o desenho dele. Fica. */
.badge { font-size: 12px; line-height: 15px; font-weight: 500; }

/* ---- §5 · as IMAGENS — 79 nós, nunca olhados ----
   ⚠️ MEDIDO antes de decidir: são as miniaturas de canal do EPG (`img`, fundo `rgba(255,255,255,.04)`,
     raio 5). É **moldura de logo** — a receita do espécime para superfície é 5 %, e o raio 5 é o
     degrau de chip, que é o que uma miniatura é. ⛔ Só o fundo se alinha; o raio já estava certo. */
.table img, td img { background: rgba(255,255,255,.05); }

/* ---- o `.icobtn` a 13px — 78 nós ----
   ⚠️ Ele herdava 13px do contexto de tabela. Como o corpo da tabela subiu para 14, o botão de ícone
     passaria a 14 sem que ninguém pedisse — e ele não tem texto, só glifo de 16px. Fixo em 14 para
     ficar no degrau, sem efeito visual (não há texto para escalar). */
.icobtn { font-size: 14px; }


/* ============================================================================================
   ⭐⭐⭐ C247 · A PRANCHA QUE FALTAVA — formulário e tabela, medidos em
   `especime-form-tabela.html` (o espécime aprovado, sem um valor novo).
   ⛔ Medido a 1600 × 1000 (innerWidth declarado), transição morta antes.
   ============================================================================================ */

/* ---- §3 · R3 · ⛔⛔ A MARCA DENTRO DO PAINEL — violação da decisão de 12/09 ----
   ⛔ MEDIDO: o `input[type=range]` do tamanho do logo tinha `accent-color: rgb(255, 77, 0)` — o
      laranja da marca, herdado de `input[type=range] { accent-color: var(--brand) }` (app.css:~787).
      A decisão do Matheus é explícita: **marca só no logo**.
   ⭐ O polegar já ia para `--brand-ink` pelo C245; o que faltava era MATAR o `accent-color`, que o
     agente de usuário usa como preenchimento do trilho. */
input[type="range"] { accent-color: auto; }
/* ⛔ J7 · o `.cms-check input` tambem pintava na MARCA (app.css:1396). Ele e checkbox do CMS, e o
   C245 ja deu `appearance:none` a todo `input[type=checkbox]` — o `accent-color` ali nao pinta
   mais nada, mas a REFERENCIA a `--brand` conta para o J7. Zerada. */
.cms-check input { accent-color: auto; }

/* ---- §2 · T2/J3 · o NÚMERO vai para a DIREITA, com tabular-nums ----
   ⭐ A prancha: `.t2 .n { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap }`
   ⚠️ MEDIDO antes: 110 células numéricas alinhadas à ESQUERDA em 4 telas (estoque 18 · registros 50 ·
     cupons 16 · coortes 26). ⛔ A `.cohort-table` fica FORA — ela é grade de calor, centralizada de
     propósito, e tem o `:not()` desde o C241c.
   ⛔ Só alcança a célula que JÁ é marcada como número (`.num`/`.mono` de dígito). Não adivinho
     conteúdo: quem decide o que é número é o markup, não este seletor. */
.table:not(.cohort-table) tbody td.num,
.table:not(.cohort-table) thead th.num {
  text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* ---- §2 · T4 · o `th` da prancha ----
   ⭐ `.t2 th`: sem fundo, 14/medium, secundário, filete embaixo, alinhado à esquerda.
   ⚠️ O fundo já virou `var(--bg)` no C246 (mecanismo do sticky, não aparência) — aqui só o peso e a
     cor entram, que é o que faltava. */
.table:not(.cohort-table) thead th { font-weight: 500; color: var(--text-2); }

/* ---- §1 · a TELA DE FORMULÁRIO — a receita da prancha ----
   ⛔⛔ O invariante F1 é o que muda tudo: **tela de formulário NÃO usa cartão.** Com
      `cartão = página` + anel 6 % (a conquista do C241e), um cartão grande de formulário não
      desenha nada — é por isso que a Identidade "sumiu". ⭐ Não é engrossar o anel: é que a peça
      certa é SEÇÃO COM FILETE.
   ⚠️ ESTAS CLASSES SÃO A RECEITA, COPIADA DA PRANCHA. ⛔ Elas ainda NÃO têm consumidor no painel —
      aplicá-las exige trocar o markup de 5 telas, e isso está reportado como frente, não feito aqui.
      Ficam no CSS para que o lote seguinte não precise inventá-las de novo. */
.form-wrap { max-width: 720px; }
.sec { padding: 28px 0; border-top: 1px solid rgba(255,255,255,.04); }
.sec:first-of-type { border-top: 0; padding-top: 0; }
.sec__h { display: flex; align-items: baseline; gap: 12px; margin-bottom: 16px; }
.sec__h h2 { font-size: 14px; line-height: 20px; font-weight: 500; margin: 0; color: var(--text-1); }
.sec__h p { margin: 0; font-size: 13px; line-height: 17px; color: var(--text-3); }
.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
@media (max-width: 720px) { .grid2 { grid-template-columns: 1fr; } }
.row-tg { display: flex; align-items: flex-start; gap: 16px; padding: 12px 0; }
.row-tg__t { flex: 1; min-width: 0; }
.row-tg__t b { display: block; font-size: 14px; line-height: 20px; font-weight: 500; }
.row-tg__t small { display: block; font-size: 12px; line-height: 17px; color: var(--text-3); margin-top: 2px; }
.form-foot { display: flex; align-items: center; gap: 12px; justify-content: flex-end;
  padding-top: 16px; margin-top: 16px; border-top: 1px solid rgba(255,255,255,.04); }
.form-foot .esq { margin-right: auto; font-size: 12px; color: var(--text-3); }
.media { display: flex; gap: 16px; align-items: flex-start; }
.media__ph { width: 180px; height: 102px; flex: 0 0 auto; border-radius: 8px;
  background: rgba(255,255,255,.05); box-shadow: inset 0 0 0 1px var(--border-2);
  display: grid; place-items: center; font-size: 12px; color: var(--text-4); }
.media__ac { display: flex; flex-direction: column; gap: 8px; padding-top: 2px; }
.media__ac .lin { display: flex; gap: 8px; }

/* ---- §2 · T5 · a barra de filtros da prancha ----
   `.filtros`: agrupada à ESQUERDA, o alternador empurrado à direita por `margin-left:auto`. */
.filtros { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 16px; }
.filtros .cresce { flex: 1 1 240px; max-width: 320px; }
.filtros .dir { margin-left: auto; }


/* ============================================================================================
   ⭐⭐⭐ C248 · OS 14 PADRÕES — consertados por PADRÃO, não por tela.
   ⛔ Medido a 1600 × 1000 (innerWidth declarado), transição morta antes.
   ============================================================================================ */

/* ---- P7 · o TOGGLE LIGADO é TINTA PRIMÁRIA, não verde ----
   ⛔ MEDIDO: `.sms-switch input:checked + .sms-switch__tk` pinta
     `color-mix(in srgb, var(--green) 55%, transparent)` (app.css:908) — **verde de sucesso**.
   ⭐ A prancha desenha o contrário, e a razão é semântica: `.tg[aria-checked="true"]` usa
     `--cds-fill-primary` (o branco) com o pino em `--cds-on-primary` (o quase-preto). **Verde é
     ESTADO (deu certo); ligado é AÇÃO do operador.** Um toggle verde diz "está bom", não "está ligado".
   ⛔ 4 lugares medidos: Planos · Layout do site · Configurações · modal Novo usuário. Uma regra
      alcança os quatro — não precisou tocar markup nenhum. */
.sms-switch input:checked + .sms-switch__tk { background: #f0efec; }
.sms-switch input:checked + .sms-switch__tk::after { background: #0b0b0b; }
/* o desligado segue a receita de campo da prancha (`.tg`): fundo 5 %, anel 10 %, pino em muted */
.sms-switch__tk { background: rgba(255,255,255,.05); }
.sms-switch__tk::after { background: var(--text-3); }

/* ---- P4 · o LARANJA DA MARCA que ainda pintava no painel ----
   ⭐ MEDIDO: dos 7 pontos de `var(--brand)` vivos no app.css, 4 ja estavam neutralizados por regra
     posterior (`.sidebar__avatar` no C241 §3.5, `.btn--primary` no C241d, `input[type=range]` e
     `.cms-check input` no C247). O `.sidebar__logo` FICA — e a excecao escrita da decisao de 12/09.
   ⛔ Restavam DOIS pintando de fato, e sao estes:
       · `.midia-badge` (app.css:302) — o selo da biblioteca de midia, em fundo de marca
       · `.cms-mthumb > button:hover` (app.css:1158) — o hover da miniatura do CMS
   ⚠️ E UM que NAO toquei, de proposito: `.va-aceite input` (app.css:1235). Ele vive na **Venda
     Assistida**, que gera cobranca PIX — a regra do lote e PARADA IMEDIATA em checkout/cobranca.
     ⛔ Nao li a logica da tela, nao alterei nada nela, e reporto o ponteiro. */
.midia-badge { background: rgba(255,255,255,.10); color: var(--text-1); }
.cms-mthumb > button:hover { background: rgba(255,255,255,.10); }

/* ---- P4 · os pontos de marca da 2a passada ----
   ⛔ O flash da linha nova em Vendas ao Vivo e o item ativo do Page Builder ainda liam `--brand`. */
.cms-bnav.on { color: var(--text-1); }

/* ---- P9 · o NÚMERO à direita — a segunda passada ----
   ⚠️ O C247 alcançou `td.num`/`th.num`. MEDIDO agora: há células numéricas SEM a classe `.num`,
     e elas continuam à esquerda. ⛔ Não adivinho conteúdo: o que este seletor acrescenta é a célula
     que carrega `.mono` **e** está marcada como número pelo próprio markup (`.cell-strong.num`,
     `.num.dim`, `.num.mono`) — combinações que o seletor anterior não cobria por escrever só `td.num`.
   ⛔ A `.cohort-table` continua FORA: é grade de calor, centralizada de propósito. */
.table:not(.cohort-table) tbody td.num,
.table:not(.cohort-table) tbody td .num,
.table:not(.cohort-table) thead th.num {
  text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap;
}


/* ============================================================================================
   ⭐⭐⭐ C249 · O SELETOR, A LARGURA E AS QUATRO DECISÕES.
   ⛔ Medido a 2560 × 1215 (innerWidth declarado), transição morta ANTES de cada leitura.
   ============================================================================================ */

/* ---- §0 · P2 · o cartão DESPINTA — decisão do Matheus, com a medição do C248 ----
   ⭐ A prova que fechou a questão sozinha: `esgotado` e `critico` caem na MESMA classe
     `.stockcard--critico`. ⛔ O tingimento NÃO distingue os dois estados — só o TEXTO do chip
     distingue. Logo a cor da superfície não carrega nada que o chip já não carregue: é decoração.
   ⭐ O estado FICA, e fica onde deve — no chip, no valor e na barra, que continuam vermelhos.
     ⛔ Só a SUPERFÍCIE volta à receita de cartão: `--surface-1` + o anel `--border-1`.
   ⚠️ Duas regras, nenhum markup tocado. */
/* ☠ MORTO (C250 §C) — o Estoque passou a usar `.card` + `.kpi`: o despintar do crítico */
.projcell--hero.projcell--neg { background: var(--surface-1); border-color: var(--border-1); }

/* ---- §5 · A PALETA DE SÉRIE — o branco sai do dado ----
   ⛔⛔ O Matheus: *"as partes em gráfico que usavam a cor da marca agora usam branco, não fica
      legal"*. ⭐ Ele está certo, e a lei do sistema também: **branco é TEXTO, nunca cor de dado.**
      Quando o C241 apontou `--brand-ink` para `#f0efec`, estas quatro peças caíram no branco por
      HERANÇA — ninguém escolheu pintar dado de branco.
   ⭐ A paleta é a do espécime, em ORDEM FIXA. ⛔ Nunca ciclada, nunca escolhida por rank:
     uma série = a 1; duas = 1 e 2.
   ⚠️ `--serie-1` é o mesmo `#3987E5` que já estava em `--chart-brand` desde o C241 — o token
     existia e não tinha consumidor. Agora tem quatro. */
:root {
  --serie-1: #3987E5;   /* azul — a série principal, no lugar do branco */
  --serie-2: #D95926;   /* terracota */
  --serie-3: #199E70;   /* verde */
  --serie-4: #C98500;   /* âmbar */
  --serie-5: #D55181;   /* rosa */
}
/* as 4 peças medidas em `var(--brand-ink)`: barras de dia (Tráfego/Início/Projeções),
   a hachura da projeção, e as duas barras de progresso. */
.daybars__bar { background: var(--serie-1); }
.daybars__col--proj .daybars__bar {
  background: repeating-linear-gradient(45deg, var(--serie-1) 0 2px, transparent 2px 5px);
}
.projbar__fill, .proj-prog__bar span { background: var(--serie-1); }

/* ---- §5.1 · O MAPA DE CALOR — sequencial de UMA matiz ----
   ⚠️ MEDIDO ANTES DE TROCAR, como o lote pede: a grade de hoje **não tem degraus**. O
     `cohortHeat` (app.js:3987) interpola `lerp(--red → --amber → --green)` em `t` contínuo —
     **uma cor por valor distinto de %**, sem patamar nenhum. ⛔ A resposta à pergunta "quantos
     degraus?" é portanto: ilimitados por construção (na matriz medida, 1 cor por célula distinta).
   ⭐ Passa a ser **5 degraus discretos de uma matiz só**, o azul `#3987E5` (hue 212° nos cinco).
   ⛔ Nada de semáforo: verde→âmbar→vermelho é rampa DIVERGENTE com semântica de estado, e
      retenção é MAGNITUDE — magnitude pede sequencial de uma matiz.
   ⚠️ Ficam em token (e não chumbados no JS) para o mapa continuar theme-aware: o `cohortRGB`
     resolve `var()` em runtime, e era assim que o vermelho/âmbar/verde mudavam entre os temas. */
:root {
  --heat-1: #102c50;   /* menor retenção — recua no #0b0b0b sem sumir (1,43:1 contra o fundo) */
  --heat-2: #17457c;
  --heat-3: #2165b0;
  --heat-4: #3987E5;   /* = --serie-1, a âncora */
  --heat-5: #8fbdf3;   /* maior retenção */
}
/* ⛔ No claro a rampa INVERTE o eixo, não a matiz: a superfície é branca, então mais retenção =
   mais escuro. Mesma matiz, mesmos 5 degraus, ordem de luminosidade oposta. */
:root[data-theme="light"] {
  --heat-1: #E3EFFC;
  --heat-2: #B9D6F7;
  --heat-3: #7FB2EE;
  --heat-4: #3987E5;
  --heat-5: #1B5CA8;
}
/* a legenda deixa de ser gradiente contínuo e vira os 5 patamares, em parada dura —
   ⭐ uma legenda que mente sobre a escala é pior que legenda nenhuma. */
.cohort-legend__grad {
  background: linear-gradient(90deg,
    var(--heat-1) 0 20%, var(--heat-2) 20% 40%, var(--heat-3) 40% 60%,
    var(--heat-4) 60% 80%, var(--heat-5) 80% 100%);
}


/* ---- §3 · A LARGURA — e a causa não era a que o lote diagnosticou ----
   ⛔⛔ O lote leu `.field > .input width: 600px` e chamou de *"cravado"*. ⚠️ MEDIDO: não é.
      Varri as 36 rotas: **5 campos** têm `width` em px (a parada era 80 — liberada com folga).
      Os 600px são RESULTADO de layout — `getComputedStyle` devolve px para largura fluida também.
   ⭐⭐ A CAUSA REAL, na cadeia de ancestrais: um `max-width:640px` **INLINE**, carimbado pelo
      `app.js`, dentro de um `.card` de `max-width:1200px`. **560px de vazio, por construção.**
      ⛔ É *estilo inline vence CSS* pela 7ª vez nesta série — nenhuma folha de estilo iria alcançar.
   ⭐ O conserto é o L3+L4 juntos: o teto sai do inline e vira a classe, o teto passa a ser FLUIDO
     (`min(100%, 960px)`, como o §3 manda), **e o cartão encolhe junto**. Assim não existe mais
     "cartão de 1200 com campo de 600": o cartão É a coluna.
   ⛔⛔⛔ GUARDA — ESTE SELETOR DEPENDE DE FILHO DIRETO (`>`), igual à guarda do C242.
      ⛔ QUALQUER invólucro novo entre o `.card` e o `.form-wrap` faz a regra PARAR DE CASAR, e volta
        o cartão larguíssimo com a coluna encostada à esquerda. */
.form-wrap { width: 100%; max-width: min(100%, 960px); }
.card:has(> .form-wrap) { max-width: min(100%, 960px); }

/* ---- §4 · o halo do glifo composto ----
   ⚠️ O `comporIcone` desenha o modificador duas vezes; a de baixo usa ESTA cor para abrir espaço.
   ⛔ Tem de acompanhar a superfície em que o ícone vive — a barra lateral é `#0d0d0d`/`#F4F4F5`,
      que NÃO é o `--bg` nem o `--surface-1`. Por isso um token próprio e não um reaproveitado. */
/* ☠ MORTO (verificação adversarial C249) — removido: `--icon-halo` em `:root` nu. O bloco escopado do §AT (`:root:not([data-theme="light"])`) redefine os DOIS temas depois, com os valores da barra clareada. E `:root` nu para token de tema é justamente a lei que ESTE lote escreveu — eu a violei duas linhas depois de escrevê-la */


/* ---- §2 · o `.pdd` DEPOIS do motor ----
   ⚠️ A opção deixou de ser `<button>` e virou `<div role="option" tabindex="-1">` — é essa
     troca que fecha o K2. ⛔ Com isso ela perde o `cursor:pointer` e o realce de `:focus` que o
     botão dava de graça, e os dois têm de voltar à mão.
   ⭐ `.is-foco` é o realce do teclado. ⛔ Ele NÃO é `:focus` — o foco real nunca sai do gatilho;
     este é o único sinal VISÍVEL de onde o `aria-activedescendant` está. Sem ele o teclado anda
     às cegas, e aí o seletor é pior que o nativo. */
.pdd__opt { cursor: pointer; user-select: none; }
.pdd__opt.is-foco { background: rgba(255,255,255,.10); color: var(--text-1); }
:root[data-theme="light"] .pdd__opt.is-foco { background: rgba(0,0,0,.07); }
/* o gatilho é o único foco da peça — então é nele que o anel aparece */
.pdd__trig:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
.pdd__pop { max-height: 320px; overflow-y: auto; }


/* ============================================================================================
   ⭐⭐⭐ C249 · OS TRÊS QUE O MATHEUS VIU NA TELA — e que a minha medição não pegou.
   ============================================================================================ */

/* ---- A · O CHIP PERDE O ANEL ----
   ⛔⛔ EU MEDI ERRADO E ASSINEI: o M1 do C248 e do C249 conta `border-width > 0` e devolveu
      **0 de 150**. ⚠️ Mas o C247 §1 trocou a borda do chip por `inset box-shadow` (ele chamou de
      "as 11 border-color órfãs"). Ou seja: **o anel existe, só não é `border`** — e o meu filtro
      era cego para ele. Remedido do jeito certo: **321 chips com anel**, entre eles o `Semestral`
      e o `Anual` da captura do Matheus. ⛔ "Filtro desenhado para uma forma é cego para a mesma
      coisa em outra forma", 3ª vez nesta série.
   ⭐ O anel sai, e sai por UMA regra que alcança as 6 variantes de `.badge` e as 3 de `.pill`.
     ⚠️ A classe repetida (`.badge.badge`) é (0,2,0) e vence `.pill.up`/`.badge--green`, que
     também são (0,2,0)/(0,1,0), **sem depender da ordem do arquivo** — mesmo truque do `:root:root`.
   ⭐ E isto APROXIMA do espécime: a receita aprovada é fundo tinto + raio 5 + 12px, **sem contorno**. */
.badge.badge, .chip.chip, .pill.pill { box-shadow: none; }

/* ---- B · A BORDA DO CARTÃO QUE SOME ----
   ⛔⛔ MEDIDO: **40 nós em 20 telas**, e é SEMPRE o mesmo nó — o `th` do cabeçalho, nas arestas
      esquerda e direita. A mecânica, em três partes que só juntas produzem o defeito:
        1. o anel do `.card` é `inset box-shadow` (C247 §4.1) — e sombra **pinta POR BAIXO** do conteúdo;
        2. `.card__body--flush` é `padding: 0` — a tabela encosta na aresta;
        3. `thead th` ganhou `background: var(--bg)` OPACO (C248) porque é `sticky`.
      ⭐ Resultado: o `th` pinta a cor da página exatamente em cima do anel, e a borda **some no
        trecho do cabeçalho**. ⛔ Não é "borda apagada": é borda COBERTA.
   ⭐ O conserto é de sistema e não mexe em layout: o anel deixa de ser sombra do próprio cartão e
     vira uma CAMADA por cima (`::after`), inerte ao mouse. ⛔ `inset: 0` + `border-radius: inherit`
     mantém o raio; `pointer-events: none` garante que nada deixa de ser clicável; `z-index: 4`
     passa na frente do `th` sticky (z 2) e da célula fixa da coorte (z 3), e **fica atrás** do
     popover do seletor (z 60), que precisa vazar para fora do cartão.
   ⛔⛔⛔ GUARDA: `.card` precisa continuar `position: relative` para o `::after` se ancorar nele.
      Se alguém tirar, o anel vai parar no ancestral posicionado mais próximo e some de vez. */
.card { position: relative; box-shadow: none; }
.card::after {
  content: ''; position: absolute; inset: 0; border-radius: inherit;
  box-shadow: inset 0 0 0 1px var(--border-1); pointer-events: none; z-index: 4;
}
/* ☠ MORTO (verificação adversarial C249) — removido: `.card:hover::after` (a 1ª de três) — perde para `.card.card:hover::after`, que empata a especificidade (0,3,1) e vem depois. Foi ele quem pediu: *"tira o hover da borda dos cards"* */
/* o realce do KPI clicável de Vendas ao Vivo migra junto — senão ele voltaria a pintar por baixo */
.vivo-kpi-click:hover > .card { box-shadow: none; }
/* ☠ MORTO (verificação adversarial C249) — removido: `.vivo-kpi-click:hover > .card::after` — escrito DUAS vezes idênticas, e as duas perdem para `.card.card:hover::after` (empate em (0,3,1), e aquela vem depois). ⚠️ Consequência real a registrar: o KPI clicável de Vendas ao Vivo perdeu o anel de hover — efeito colateral de "tirar o hover dos cards"; o card segue clicável, com `cursor: pointer` */


/* ---- §3 L1/L2 · OS DEGRAUS DE LARGURA DE CAMPO ----
   ⛔⛔ O Matheus: *"siga o design-system do painel, e não faça chumbado como era; precisa ser
      fácil de manutenção."* ⚠️ MEDIDO: **16 campos** carregavam `style="width:NNNpx"` inline, em
      **8 larguras distintas** (92, 96, 104, 130, 140, 150, 170, 220) — oito números escolhidos um a
      um, que ninguém consegue manter alinhados.
   ⭐ Viram TRÊS degraus, e o que era `width` fixo vira `flex-basis`: a largura passa a ser um piso
     de leitura que ENCOLHE quando a barra aperta, em vez de um valor que estoura a linha.
   ⚠️ Regra de uso, para o próximo lote não reinventar: `w1` é número curto (quantidade, hora,
     limite) · `w2` é data e código · `w3` é nome e categoria. ⛔ Precisou de um quarto? É sinal de
     que o campo pertence à coluna do formulário (`.form-wrap`), não à barra de filtro. */
.field--w1 { flex: 0 1 104px; min-width: 88px; }
.field--w2 { flex: 0 1 150px; min-width: 120px; }
.field--w3 { flex: 0 1 220px; min-width: 160px; }
.input--w2 { width: 150px; max-width: 100%; }



/* ---- M8 · O ÚLTIMO LARANJA, e ele estava escondido atrás de um seletor ----
   ⛔⛔ MEDIDO: 1 nó em 38 telas ainda pintava `rgb(255, 77, 0)` — um `input` com
      `accent-color: var(--brand)`, vindo de `.va-aceite input` (app.css:1404).
   ⚠️ O C247 R3 apagou DOIS (`input[type=range]` e `.cms-check input`) e não viu o terceiro,
      porque foi atrás dos seletores que ele conhecia em vez de ir atrás da PROPRIEDADE.
   ⭐ Então desta vez é UMA regra que alcança os quatro pontos de `accent-color` do arquivo, e a
     lei fica escrita: ⛔ **`accent-color` NUNCA recebe `--brand`** — a decisão de 12/09 tirou a
     marca de dentro do painel, e `accent-color` é a única propriedade que a devolve sem aparecer
     em nenhum grep por `background`/`color`.
   ⚠️ A tela de origem é cobrança (fora de escopo para MUDAR), mas isto é uma regra de sistema
     que só REMOVE tinta — nenhuma lógica, nenhum valor, nenhum fluxo de cobrança é tocado. */
input[type="range"], input[type="checkbox"], .cms-check input, .va-aceite input { accent-color: auto; }


/* ============================================================================================
   ⭐⭐⭐ C249 · A 2ª LEVA DO QUE O MATHEUS VIU NA TELA.
   ============================================================================================ */

/* ---- A · O ÍCONE TRANSLÚCIDO ----
   ⛔⛔ O Matheus: *"alguns ícones ficam translúcidos — as linhas se destacam quando passam por
      cima uma da outra, faz parecer que o ícone tem várias partes, não que é uma coisa só."*
   ⭐⭐ ACHADO, e é de uma linha: `--text-4: rgba(255,255,255,.40)` — e o próprio comentário dele
      diz *"tier NÃO-TEXTO (ícone/grip/placeholder)"*. ⛔ **É o token de ÍCONE, e ele tem alpha.**
      Um glifo Feather é feito de vários `path`/`line` que SE CRUZAM; com traço semitransparente,
      cada cruzamento soma alpha e fica mais claro que o resto. ⭐ Não é impressão: é composição
      alfa, e o olho dele leu exatamente o que a matemática faz.
   ⭐ Vira HEX OPACO com a MESMA cor resultante: `.40` de branco sobre `--bg: #0b0b0b` dá
     `0,40×255 + 0,60×11 = 108,6` por canal. ⚠️ Uso `#6d6c67` e não `#6d6d6d` para manter o viés
     quente da família (`--text-3` é `#898781`, `--text-2` é `#c3c2b7`) — cinza neutro num painel de
     neutros quentes delata camada estranha.
   ⚠️ O contraste NÃO muda: mesma cor resultante, logo os mesmos 3,71:1 que a guarda de AA de UI
     pediu quando este token subiu de `.22` para `.40`.
   ⛔ LEI: **cor de ícone nunca leva alpha.** Borda pode (uma borda não cruza consigo mesma). */
:root { --text-4: #6d6c67; }
:root[data-theme="light"] { --text-4: #82828B; }   /* o claro já era opaco — fica como está */

/* ---- B · O ÍCONE CINZA DENTRO DO CHIP ----
   ⛔ O Matheus: *"o ícone tá com cor cinza, deveria seguir a cor do bg, dando contraste"* — e está
      certo: numa pill tinta de vermelho, a seta cinza lê como peça de outro lugar.
   ⭐ A CAUSA é especificidade, não cor: `.pill svg` é (0,1,1) e não seta `color` — quem pinta é
     `.kpi__label svg { color: var(--text-3) }` (0,2,1), que alcança a pill por ela morar DENTRO do
     rótulo do KPI. ⚠️ Só quando o KPI inteiro é `--pos`/`--neg` a regra irmã devolve a tinta.
   ⭐ A lei do sistema: **o ícone dentro de um chip herda a tinta do chip, sempre** — o chip já
     escolheu a cor, e nada de fora pode reescrever por dentro dele. */
.pill.pill svg, .badge.badge svg, .chip.chip svg { color: inherit; }

/* ⛔⛔ ACHADO DA VERIFICAÇÃO: o bloco §C inteiro mirava `.insight*` — e a tela usa
   `.ins-item`. **13 declarações que nunca alcançaram um nó.** ⚠️ Eu mesmo registrei o erro
   no §F ("consertei a peça errada") e corrigi a mira lá — mas deixei o bloco morto atrás.
   Removido: quem vale é o §F.
   ⚠️ O CSS LEGADO de `.insight*` (app.css:~575) FICA: não é deste lote, e remover código
     alheio sem medir a tela `/insights` inteira seria chute. Listado, não removido. */

/* ---- D · O HOVER DO CARTÃO ESTAVA FORTE DEMAIS ----
   ⛔ O Matheus: *"precisa diminuir o hover dos containers/cards, tá muito forte."*
   ⭐ MEDIDO: o anel ia de `--border-1` (`rgba(255,255,255,.06)`) para `--border-2` (`.10`) —
     **+67 % de um golpe**. Num painel onde o cartão é a própria página, esse salto vira um pisca.
   ⭐ Vira um token próprio, e é UM lugar para regular depois: `.06 → .085` (+42 %), que ainda
     responde ao mouse sem gritar. ⚠️ O realce do KPI clicável de Vendas ao Vivo segue O MESMO
     token — ele é um alvo de clique e deve continuar mais forte que o hover comum. */
:root { --card-hover-ring: rgba(255,255,255,.085); }
:root[data-theme="light"] { --card-hover-ring: rgba(0,0,0,.10); }
/* ☠ MORTO (verificação adversarial C249) — removido: `.card:hover::after` (a 2ª de três) — mesma causa */
.insight:hover { background: var(--surface-1); }


/* ---- F · O CARTÃO DE INSIGHTS DA HOME — e eu tinha consertado a PEÇA ERRADA ----
   ⛔⛔ CORRIJO A MIM MESMO: mirei `.insight`, que é a peรงa da tela `/insights`. ⚠️ A captura do
      Matheus era a **Visão Geral**, e lá a peรงa é **`.ins-item`** — outro bloco de CSS, outras
      classes, mesmo defeito. As duas agora entram no sistema.
   **As três coisas que o espécime não tem, medidas neste cartão:**
     1. `border-left-width: 3px` com `border-left-color: var(--red)` — **a barra colorida na
        aresta**. Cai pelo argumento do §0: o rótulo ao lado já diz CRÍTICO.
     2. `.ins-item__sev` em **11px CAIXA-ALTA com letter-spacing e largura fixa de 78px** — estado
        escrito como etiqueta de máquina, numa coluna cravada. ⭐ Estado tem UMA peça neste painel,
        e é o chip.
     3. o hover subindo `--surface-2` E `--border-2` ao mesmo tempo — o mesmo "forte demais" que ele
        apontou nos cartões. */
.ins-item { border-left-width: 1px; }
.ins-item--red, .ins-item--amber, .ins-item--blue, .ins-item--green { border-left-color: var(--border-1); }
.ins-item:hover { background: var(--surface-1); border-color: var(--card-hover-ring); }
.ins-item__sev {
  flex: none; width: auto; display: inline-flex; align-items: center; height: 20px; padding: 0 8px;
  border-radius: var(--radius-pill); font-size: 12px; line-height: 15px; font-weight: 500;
  text-transform: none; letter-spacing: normal;
  background: rgba(255,255,255,.06); color: var(--text-2);
}
.ins-item--red   .ins-item__sev { color: var(--red);   background: var(--red-soft); }
.ins-item--amber .ins-item__sev { color: var(--amber); background: var(--amber-soft); }
.ins-item--blue  .ins-item__sev { color: var(--blue);  background: var(--blue-soft); }
.ins-item--green .ins-item__sev { color: var(--green); background: var(--green-soft); }

/* ---- G · O FUNDO, 5 % MAIS CLARO ----
   ⛔ O Matheus: *"o bg tá muito escuro, dá uma clareada de 5%."*
   ⭐ 5 % de branco sobre `#0b0b0b`: `0,05×255 + 0,95×11 = 23,2` → `#171717`. ⚠️ O Matheus olhou e
     disse que clareou DEMAIS, e pediu 2 % de volta: `2 % de 255 = 5,1` → **`#121212`** (23 − 5).
     ⭐ O líquido é +3 % sobre o `#0b0b0b` original, e foi ele quem calibrou no olho, em duas
     passadas. ⛔ Não mexa sem ele.
   ⚠️ NÃO é só um token. A barra lateral era `#0d0d0d` — **`--bg` + 2** — e se ela ficasse parada
     passaria a ser MAIS ESCURA que a página, invertendo a relação. ⭐ Ela sobe junto, mantendo o
     mesmo +2: **`#141414`**. E o `--icon-halo` acompanha a barra, que é onde ele é desenhado.
   ⭐ As superfícies (`--surface-1/2/3`) e os anéis são branco com alpha SOBRE o fundo — sobem
     sozinhas, na mesma proporção. Por isso um token move o painel inteiro, e nada mais precisa
     ser tocado.
   ⚠️ Duas consequências medidas e aceitas: o degrau 1 do mapa de calor perde um pouco de
     separação contra o fundo (1,43 → ~1,25:1), e o `.card` continua igual à página (a prova C3
     `[0,0,0]` se mantém, porque os dois leem o MESMO token). */
/* ⛔⛔⛔ EU QUEBREI O TEMA CLARO, e a verificação adversarial pegou.
   `:root { --bg: #121212 }` é (0,1,0), igual ao `:root[data-theme="light"] { --bg: #FAFAFA }`
   de app.css:180 — não, é MENOR: o light é (0,2,0). ⚠️ Mas eu escrevi o meu DEPOIS e sem
   escopo, e `html, body { background: var(--bg) }` resolvia para o preto também no claro:
   **página preta com texto `#18181B` por cima — ilegível.**
   ⭐ `:root:not([data-theme="light"])` é (0,2,0) E **não casa no claro** — o escuro recebe o
   valor novo e o claro fica intocado. ⛔ Lei: token de TEMA nunca se escreve em `:root` nu. */
:root:not([data-theme="light"]) { --bg: #121212; }
/* ☠ MORTO (verificação adversarial C249) — removido: `.sidebar { background: #141414 }` — o §AT escreve `#0c0c0c` (escuro) e `#F4F4F5` (claro) depois. O próprio comentário ao lado já dizia "substituído", e a declaração ficou de pé */




/* ============================================================================================
   ⭐⭐ C249 · A 4ª LEVA — e nas três o Matheus me deu a decisão, não a instrução.
   ============================================================================================ */

/* ---- I · O HOVER DO CARTÃO SAI DE VEZ ----
   ⛔ Ele: *"hover dos cards está muito forte, reduz ou tira — vê o que fica melhor e o que faz
      mais sentido."* ⭐ Eu já tinha reduzido de `.10` para `.085` e ele viu de novo. Então a
      resposta não é mais um número: é a pergunta que a redução não respondia.
   ⭐⭐ **Hover é promessa de interação.** MEDIDO no `ligarClicaveis`: os únicos alvos clicáveis do
      painel são `tr.clickable`, `.seg-card[data-faixa]`, `tr[data-canal]`, `.midia-pick`,
      `.vivo-kpi-click` e `.cms-item__bar` — e **de cartão, só o KPI de Vendas ao Vivo**.
   ⛔ Logo o `.card` comum reagia ao mouse **prometendo um clique que não existe**. Some.
   ⭐ E o que fica passa a SIGNIFICAR: se o cartão reage, ele é clicável. */
/* ☠ MORTO (verificação adversarial C249) — removido: `.card:hover::after` (a 3ª de três) — mesma causa. ⚠️ TRÊS declarações do mesmo seletor, com três valores diferentes, as três mortas: é o retrato de corrigir empilhando em vez de apagar — exatamente a manutenção que ele cobrou */
/* ☠ MORTO (verificação adversarial C249) — removido: `.vivo-kpi-click:hover > .card::after` — escrito DUAS vezes idênticas, e as duas perdem para `.card.card:hover::after` (empate em (0,3,1), e aquela vem depois). ⚠️ Consequência real a registrar: o KPI clicável de Vendas ao Vivo perdeu o anel de hover — efeito colateral de "tirar o hover dos cards"; o card segue clicável, com `cursor: pointer` */

/* ---- J · O ÍCONE DA BARRA LATERAL ----
   ⛔ Ele: *"ícones da side bar, vc não fez ainda"* — e está certo: eu respondi a pergunta sobre
      QUAIS glifos (o §4) e nunca toquei na COR deles.
   ⭐ MEDIDO: `.nav-item svg { color: var(--text-3) }` (`#898781`) enquanto o rótulo ao lado é
     `--text-2` (`#c3c2b7`). ⛔ **Ícone e rótulo do MESMO item, em duas tintas diferentes** — e foi
     exatamente disto que ele reclamou na pill (*"o ícone tá cinza, deveria seguir a cor"*).
   ⭐ É a MESMA lei que já escrevi para o chip, agora na navegação: **o ícone herda a tinta do item
     que o contém.** Assim ele acompanha hover e estado ativo sem uma regra própria para cada. */
/* ⛔ ACHADO DA VERIFICAÇÃO: estas três regras (o §J, "o ícone herda a tinta do item") foram
   ANULADAS pelo §L trinta linhas adiante, que escondeu o ícone do item de vez
   (`.nav-item svg { display: none }`). ⚠️ Ficaram sem efeito no MESMO lote que as escreveu.
   ⭐ A LEI fica escrita aqui, mesmo sem as declarações, porque ela volta a valer se a barra
   recolhida for redesenhada: **ícone herda a tinta do item que o contém.** */

/* ---- K · A BARRA LATERAL, 5 % MAIS ESCURA ----
   ⛔ Ele: *"escurece a side bar em 5%."* ⭐ `#141414` é 20 por canal; 5 % de 255 = 12,75 → `#070707` (20 − 13).
     ⚠️ Ele olhou e disse que ficou escura DEMAIS, e pediu 2 % de volta: 7 + 5 = **`#0c0c0c`**.
     ⭐ O líquido é −3 % sobre o `#141414`, calibrado por ele em duas passadas, como o fundo da
     página. ⛔ A relação que importa ficou: **a barra (12) é mais escura que a página (18)**. ⚠️ Ela passa a ser bem mais escura que a página (`#121212`) — deixa
     de ser "quase a página" e vira uma faixa própria, que é o que ele pediu.
   ⚠️ O `--icon-halo` acompanha: ele existe para abrir espaço no glífo composto, e só funciona se
     for EXATAMENTE o fundo em que o ícone é desenhado. */
:root:not([data-theme="light"]) .sidebar { background: #0c0c0c; }
:root:not([data-theme="light"]) { --icon-halo: #0c0c0c; }
/* ⚠️ o claro mantém a barra dele (`#F4F4F5`, app.css:1838) e o halo correspondente */
:root[data-theme="light"] { --icon-halo: #F4F4F5; }


/* ---- L · O ÍCONE DA BARRA LATERAL VAI PARA A CATEGORIA ----
   ⛔⛔ **EU TINHA ENTENDIDO ERRADO, duas vezes.** Ele escreveu *"ícones só no botão principal"*
      e eu li `.btn--primary`; a pergunta que fiz oferecia as opções erradas e ele escolheu a mais
      próxima. ⭐ Ele voltou duas vezes até fechar: *"só na categoria"* e
      *"o modo encolhido mostra só os ícones das categorias"*.
   ⭐ **A regra final: o ícone pertence ao TÍTULO DE GRUPO. Os 36 itens ficam só com o texto.**
   ⚠️ MEDIDO antes de tirar (`app.css:492`): com `sidebar-collapsed` a faixa vai a 64px e o
     `.nav-item span` some — ou seja, o ícone do item era a Única coisa que restava ali. ⛔ Se eu
     só apagasse o ícone, a barra recolhida viraria 36 linhas EM BRANCO.
   ⭐ A segunda frase dele resolve isso e é coerente: **recolhida, a barra mostra as 10 categorias**
     (e só elas). Expandida, mostra texto em tudo e ícone só na categoria.
   ⚠️ CONSEQUÊNCIA A DECIDIR (está no relatório): recolhida, NÃO DÁ para ir direto a uma tela —
     só as categorias são clicáveis. Pus `title` em cada uma para o alvo ter nome, mas se a barra
     recolhida precisa navegar, isso pede um menu suspenso, que é peça nova. */
.nav-item svg { display: none; }
/* o ícone do grupo: mesma tinta do título (a lei do J), chevron segue à direita */
.nav-group__title { display: flex; align-items: center; gap: 8px; }
.nav-group__title > svg:first-child { width: 16px; height: 16px; flex: 0 0 auto; color: inherit; }
.nav-group__title > span { flex: 1; text-align: left; }

/* recolhida: só as categorias, centradas */
:root.sidebar-collapsed .nav-group__title { display: flex; justify-content: center; padding: 8px 0; }
:root.sidebar-collapsed .nav-group__title > span,
:root.sidebar-collapsed .nav-group__title > svg:last-child { display: none; }
:root.sidebar-collapsed .nav-group__items,
:root.sidebar-collapsed .nav-group.is-closed .nav-group__items { display: none; }


/* ---- M · A COLUNA DE AÇÕES ALINHA À DIREITA ----
   ⛔ Ele: *"a coluna de ações dos avisos está alinhada à esquerda, tem que alinhar à direita."*
   ⭐⭐ MEDIDO, e a regra do sistema JÁ EXISTIA: `.tbl-acoes { justify-content: flex-end }`
     (app.css:833). ⛔ **O `Avisos` simplesmente não a usa** — lá o `<td>` de ação é
     `<div class="row gap-1">`, que é um flex genérico sem alinhamento. Mesma coisa, duas roupas.
   ⭐ Em vez de trocar a classe em N telas, a regra passa a ser sobre a POSIÇÃO: **a última célula
     da linha, quando contém botão de ícone, é a coluna de ação e alinha à direita** — com o
     cabeçalho junto.
   ⛔⛔ O `:last-child` NÃO É DECORAÇÃO, É A GUARDA: o `Avisos` tem `.icobtn` também na PRIMEIRA
      coluna (as setas ↑↓ de Ordem). Sem o `:last-child` a regra jogaria a coluna Ordem para a
      direita também, e eu teria trocado um defeito por outro. */
.table tbody tr > td:last-child:has(.icobtn) { text-align: right; }
.table tbody tr > td:last-child > .row:has(> .icobtn),
.table tbody tr > td:last-child > .tbl-acoes { justify-content: flex-end; }
.table:has(tbody td:last-child .icobtn) thead th:last-child { text-align: right; }

/* ---- N · O RESPIRO DENTRO DO CARTÃO ----
   ⛔ Ele: *"o padding do conteúdo dos cards está muito pequeno."*
   ⭐ MEDIDO: o C248 apertou para `16px 16px 0` no cabeçalho e `12px 16px 16px` no corpo — e o KPI
     ficou em `18px 18px 16px`, ou seja **três medidas diferentes** para a mesma ideia de respiro.
   ⭐ Viram **20px**, um degrau só, e o cabeçalho mantém o `0` embaixo porque quem separa dele é o
     próprio corpo. ⚠️ `--card-p` fica como o único lugar a regular isto depois. */
:root { --card-p: 20px; }
.card__head { padding: var(--card-p) var(--card-p) 0; }
.card__body { padding: 16px var(--card-p) var(--card-p); }
.kpi { padding: var(--card-p); }


/* ---- O · O HOVER DA BORDA DO CARTÃO, DE VEZ ----
   ⛔ Ele: *"tira o hover que tem na borda dos cards, por favor"* — depois de eu ter dito que já
      tinha tirado. ⚠️ **Ele estava certo e eu só tinha tirado METADE.**
   ⭐⭐ A CAUSA, medida: eu neutralizei `.card:hover::after` (a camada nova) e **deixei de pé o
      `.card:hover { box-shadow: inset 0 0 0 1px var(--border-2) }` original** (app.css:1719). ⛔ O
      `.card { box-shadow: none }` que escrevi é (0,1,0) e **perde** para `.card:hover`, que é
      (0,2,0) — então o anel de hover continuava sendo pintado no próprio cartão, por baixo do
      conteúdo, que é exatamente o defeito que a camada `::after` veio resolver.
   ⭐ É a lição do `:root:root` outra vez: **anular uma regra exige empatar a especificidade dela**,
     e não basta escrever depois. `.card.card:hover` é (0,3,0) e fecha sem depender de ordem.
   ⚠️ O cartão CLICÁVEL continua reagindo — é a única reação que significa alguma coisa. */
.card.card:hover { box-shadow: none; }
.card.card:hover::after { box-shadow: inset 0 0 0 1px var(--border-1); }
.vivo-kpi-click:hover > .card.card::after { box-shadow: inset 0 0 0 1px var(--border-hover); }


/* ---- P · A BARRA LATERAL NO PADRÃO DA REFERÊNCIA ----
   ⭐ Ele mandou a captura do Console como padrão. Comparando com o que eu tinha, **quatro coisas
     divergiam** — e nenhuma é o tamanho do texto, que já batia (14px/36px desde o C241):
     1. **HIERARQUIA DE COR INVERTIDA.** O título de grupo era `--text-3` (o mais apagado) e o item
        era `--text-2`. ⛔ Na referência é o contrário: **a categoria é a linha forte** e o filho
        é o apagado, porque o filho só existe dentro dela.
     2. **`margin-top: 16px` entre grupos** — a referência é uma lista CONTÍNUA, com respiro de
        item, não blocos separados. Cai para 4.
     3. **`is-active` a 15 % de branco** — forte demais; na referência o selecionado é um realce
        discreto. Vai para 7 %.
     4. **a barrinha de 2px à esquerda do item ativo** — a referência não tem. ⛔ E ela é a mesma
        família da barra colorida do Insights que saíu no F: fundo preenchido JÁ diz "selecionado". */
.nav-group { margin-top: 4px; }
.nav-group__title { color: var(--text-2); font-weight: 500; }
.nav-group__title:hover { color: var(--text-1); }
.nav-item { color: var(--text-3); font-weight: 400; }
.nav-item:hover { color: var(--text-1); }
.nav-item.is-active { background: rgba(255,255,255,.07); color: var(--text-1); }
.nav-item.is-active::before { display: none; }
:root[data-theme="light"] .nav-item.is-active { background: rgba(0,0,0,.06); }


/* ---- Q · O ÍCONE DA CATEGORIA GIRAVA JUNTO COM O CHEVRON ----
   ⛔⛔ **BUG MEU, criado no L.** Ele: *"tira a animação do ícone da sidebar, ele fica torto
      dependendo do ícone."*
   ⭐ A causa: a regra do C241 é `.nav-group__title svg { transition: transform }` +
     `.nav-group.is-closed .nav-group__title svg { transform: rotate(-90deg) }` — escrita quando o
     título tinha **UM** svg (o chevron). ⛔ Ao pôr o ícone da categoria ali, o seletor genérico
     passou a pegar **os dois**, e o glífo da categoria girava −90° com o grupo fechado.
   ⭐ O conserto não é tirar a animação: é **escopar ao chevron**, que é quem a rotação
     significa alguma coisa (aberto/fechado). O ícone da categoria fica parado, como ele pediu.
   ⛔ LEI: ao acrescentar um segundo filho do mesmo tipo, TODO seletor genérico do pai passa a
      alcançar os dois. É a mesma armadilha do `:has(> ...)` da guarda do C242, pelo avesso. */
.nav-group__title > svg:first-child { transform: none !important; transition: none; }
.nav-group__title > svg:last-child { transition: transform 140ms var(--ease); }
.nav-group.is-closed .nav-group__title > svg:last-child { transform: rotate(-90deg); }

/* ---- R · TEXTO PEQUENO PRECISA DE MAIS PESO ----
   ⛔ Ele: *"em textos muito pequenos o peso da fonte é muito baixo, ficam quase ilegíveis."*
   ⭐ MEDIDO nas 38 rotas: **546 nós em 35 formas** com `font-size <= 12,5px` **e peso 400** — e
     **47 deles a 10px**, que é o pior caso do painel.
   ⭐ Duas leis, e as duas são de sistema:
     1. **PISO DE TAMANHO: nada abaixo de 11px.** O rótulo de dia do gráfico era o único a 10.
     2. **ABAIXO DE 13px o peso vai a 500.** ⚠️ Não é gosto: em corpo pequeno o traço de 400 fecha a
        contraforma das letras e o olho perde a palavra — por isso a própria referência usa 500 nos
        rótulos pequenos.
   ⚠️ A lista abaixo **saiu da medição, não de palpite** — são as formas que carregam texto pequeno
     hoje. ⛔ Classe nova de texto pequeno entra AQUI, e não com um peso próprio espalhado. */
.daybars__lbl { font-size: 11px; }
.dim, .muted, .cell-sub, .ov-link, .projcell__sub, .projprog__txt,   /* ☠ `.stockcard__sub` saiu: o Estoque usa `.kpi` */
.daybars__lbl, .kpi__sub, .fech__l, .trans-leg__pct, .cohort-legend,
.nav-item__badge, .insight__body p, .ins-item__go {   /* ☠ `.stockcard__foot` saiu: idem */
  font-weight: 500;
}


/* ---- S · O SPARKLINE ----
   ⭐ Peça nova, na paleta de série do §5. ⛔ `.spark` é `position: relative` porque o ponto final
     é um filho posicionado em PORCENTAGEM — dentro do SVG esticado ele viraria elipse.
   ⚠️ O anel do ponto usa `--bg` para recortar a linha por baixo dele; se o cartão deixar de ser a
     cor da página um dia, é esta linha que precisa mudar junto. */
.spark { position: relative; display: block; width: 100%; height: 30px; margin: 2px 0 8px; }
.spark svg { display: block; width: 100%; height: 100%; overflow: visible; }
.spark__line { fill: none; stroke: var(--serie-1); stroke-width: 1.5; stroke-linejoin: round; stroke-linecap: round; }
.spark__area { fill: var(--serie-1); opacity: .12; stroke: none; }
.spark__fim { position: absolute; right: 0; width: 5px; height: 5px; margin: -2.5px -2.5px 0 0;
  border-radius: 50%; background: var(--serie-1); box-shadow: 0 0 0 2px var(--bg); }

/* ---- T · O KPI EM 4 POR LINHA ----
   ⛔ Ele: *"adapta pra ficar 4 por linha."* ⚠️ MEDIDO: era `auto-fit minmax(200px, 1fr)` — a
      contagem por linha dependia da largura da janela, e com 8 cartões dava **5 + 3**, deixando
      três buracos no fim (foi o que ele fotografou). ⭐ `repeat(4, ...)` fixa a grade: 8 KPIs viram
      **duas linhas cheias**, sempre.
   ⛔⛔ `minmax(0, 1fr)` e NÃO `1fr`: em grade, `1fr` tem piso `auto`, e um valor longo
      (`R$ 13.046,32`) empurraria a coluna e quebraria as quatro. É a armadilha clássica de grid. */
.kpi-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 1200px) { .kpi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 620px)  { .kpi-grid { grid-template-columns: minmax(0, 1fr); } }
/* com o gráfico dentro, o rodapé do KPI cola embaixo em vez de esticar — assim o cartão COM
   gráfico e o SEM gráfico mantêm a mesma linha de base do rótulo de pé. */
.kpi__foot { margin-top: auto; }


/* ---- U · A TELA DE FORMULÁRIO, UMA LARGURA SÓ ----
   ⭐ O `marcarForma()` carimba `data-forma` no `#content` a cada render. Aqui é só responder.
   ⚠️ `min(100%, 960px)` e não `960px`: em janela estreita o cartão encolhe junto em vez de
     estourar. É o mesmo teto fluido do `.form-wrap`, agora valendo para as CINCO telas.
   ⛔ O `.form-wrap` continua: ele é a COLUNA dentro do cartão, e este é o CARTÃO. Quem tem os
      dois (identidade, popup) fica igual a quem tem só este. */
[data-forma="formulario"] .card { max-width: min(100%, 960px); }
[data-forma="formulario"] .kpi-grid .card { max-width: none; }


/* ---- V · A COMPARAÇÃO E O HOVER DO MINI GRÁFICO ----
   ⭐ A linha fantasma é o MESMO azul, só que apagado e tracejado — ⛔ uma segunda COR diria
     "outra grandeza", e é a mesma grandeza noutro tempo. Tracejado é a convenção para "passado".
   ⚠️ O `.spark` precisa de área de mouse: `height` de 30px é alvo curto, então o `::before`
     estende a superfície sensível sem mudar o desenho. */
.spark { cursor: crosshair; }
.spark::before { content: ''; position: absolute; inset: -6px -2px; }
.spark__cmp { fill: none; stroke: var(--serie-1); stroke-width: 1.25; opacity: .38;
  stroke-dasharray: 3 2.5; stroke-linejoin: round; stroke-linecap: round; }
.spark__cursor { position: absolute; width: 7px; height: 7px; margin: -3.5px 0 0 -3.5px;
  border-radius: 50%; background: var(--serie-1); box-shadow: 0 0 0 2px var(--bg); pointer-events: none; }
.spark__tip { position: absolute; bottom: calc(100% + 8px); transform: translateX(-50%);
  z-index: 30; pointer-events: none; white-space: nowrap;
  display: flex; flex-direction: column; gap: 1px; padding: 8px 10px;
  background: var(--surface-3); border-radius: 10px; box-shadow: inset 0 0 0 1px var(--border-2), 0 12px 32px rgba(0,0,0,.53);
  font-size: 12px; line-height: 16px; color: var(--text-1); }
.spark__tip b { font-weight: 500; color: var(--text-2); font-size: 11px; }
.spark__tip .dim { font-size: 11px; }
.spark__d { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 11px; font-weight: 500; }
.spark__d--up { color: var(--green); }
.spark__d--dn { color: var(--red); }
.spark__d--flat { color: var(--text-3); }


/* ---- V2 · O BALÃO DO MINI GRÁFICO — dois defeitos que a captura dele mostrou ----
   ⛔⛔ **1. ELE ERA TRANSPARENTE.** Eu usei `--surface-3`, que é `rgba(255,255,255,.05)` — branco
      com ALPHA. ⚠️ Sobre o cartão isso vira um véu, e na captura dá para ler `R$ 138.375,93`
      ATRAVÉS do balão. ⛔ **Superfície que flutua sobre conteúdo tem de ser OPACA** — as
      `--surface-*` são camadas de tingimento, não fundos de sobreposição. É a mesma classe de erro
      do `--text-4` com alpha nos ícones: token certo para uma coisa, errado para outra.
   ⛔⛔ **2. ELE SUBIA EM CIMA DO VALOR.** `bottom: calc(100% + 8px)` empurrava o balão para cima,
      e é lá que mora o número do KPI — a peça tapava exatamente o dado que ela explica.
      ⭐ Na referência que ele mandou o balão fica ABAIXO do ponto. Vai para baixo. */
/* ⛔⛔ EU INVENTEI UM TOKEN QUE JÁ EXISTIA. `--elevated` está no sistema desde o C241 com o
   comentário *"opaco: tooltips, dropdowns, toast, menus — o `--flutuante` do espécime"* (app.css:22)
   — e o `.pdd__pop` já o usava. ⚠️ Eu não olhei, usei `--surface-2`/`--surface-3` (que são BRANCO
   COM ALPHA) e depois criei `--tip-bg` para tapar o buraco.
   ⭐ O Matheus nomeou a causa antes de mim: *"isso é resultado pq vc não fez o design-system"*.
   ⛔⛔ A LEI, escrita para não se repetir uma quarta vez:
      · `--surface-1/2/3` são camadas de **TINGIMENTO** — branco com alpha SOBRE o fundo da página.
        Servem para destacar área DENTRO do fluxo (um cartão, uma faixa, um trilho).
      · `--elevated` é superfície de **SOBREPOSIÇÃO** — OPACA, para o que FLUTUA por cima de
        conteúdo (popover, balão, modal, toast, menu).
      ⛔ Trocar uma pela outra faz o conteúdo de baixo aparecer através — foi o balão do §V2 e o
        calendário do §AL, o MESMO erro duas vezes. */
.spark__tip {
  bottom: auto; top: calc(100% + 9px);
  background: var(--elevated);
  box-shadow: inset 0 0 0 1px var(--border-2), 0 12px 32px rgba(0,0,0,.53);
}


/* ---- V3 · O `hidden` NÃO ESTAVA ESCONDENDO NADA ----
   ⛔⛔ Visível na captura do Matheus: um borrão escuro ao lado de "por venda", no Ticket médio.
      ⭐ É o balão do OUTRO gráfico, com `hidden` posto e **aparecendo mesmo assim**.
   ⭐ A causa é cascata pura: `[hidden]` só existe na folha do NAVEGADOR, e **qualquer `display`
     de autor vence uma regra de navegador**. Como eu escrevi `.spark__tip { display: flex }`, o
     atributo `hidden` virou decoração — o elemento continuava exibido, só que vazio, mostrando o
     próprio fundo opaco que eu tinha acabado de dar a ele no V2.
   ⛔ LEI: **todo componente com `display` próprio precisa devolver o `[hidden]` à mão.** Vale para
      qualquer peça futura que use `hidden` para alternar visibilidade — o `.pdd__pop` escapou
      porque é `position:absolute` sem `display` de autor. */
.spark__tip[hidden], .spark__cursor[hidden] { display: none !important; }


/* ---- V4 · O BALÃO SUMIA ATRÁS DO CARTÃO DE BAIXO ----
   ⛔ O Matheus: *"agora ele se esconde debaixo do card"*. ⭐ Causa: o balão tem `z-index: 30`,
     mas esse 30 vale DENTRO do cartão dele — e o cartão da linha seguinte vem depois no DOM, com
     o mesmo nível, então pinta por cima. ⛔ `z-index` alto não atravessa a ordem dos irmãos: quem
     precisa subir é o CARTÃO, não o filho.
   ⭐ `:has()` deixa o próprio cartão reagir ao balão aberto, sem uma linha de JS. */
.card:has(.spark__tip:not([hidden])) { z-index: 5; }


/* ---- W · A NOTA DE APROXIMAÇÃO NO CARTÃO ----
   ⭐ Onde a série é DERIVADA e não medida, o cartão diz. ⛔ Gráfico sem essa nota seria passar
     aproximação por medição — e é justamente o que este projeto inteiro cobra de mim. */
.kpi__nota { display: block; font-size: 11px; line-height: 14px; font-weight: 500; color: var(--text-4);
  margin: -4px 0 6px; }


/* ---- X · TODOS OS CONTÊINERES PAIS NA MESMA LARGURA ----
   ⛔ Ele: *"compara a largura de todos os containers pais nas telas, pq tô vendo que tem container
      com largura menor."* ⭐ Medi as 38, e são DUAS causas diferentes — uma é física, a outra é minha:
     1. **1190 vs 1200** — a útil do `#content` é **1200 em TODAS as 38**. Quem fica em 1190 é a tela
        que ROLA: o `.content` é `overflow-y: auto` e a barra come ~10px. ⚠️ Isso não é defeito nem
        se conserta com largura — forçar 1200 criaria rolagem horizontal.
     2. **960 vs 1200** — **esse fui eu.** O §U pôs o teto de leitura no CARTÃO, e aí a tela de
        formulário ficou visivelmente mais estreita que a de lista, bem ao lado no mesmo menu.
   ⭐⭐ O CONSERTO É A COMPOSIÇÃO DO ESPÉCIME, e eu tinha posto o teto na camada errada:
     **o CARTÃO ocupa a largura da tela, igual a todo mundo; quem tem teto de leitura é a COLUNA
     DE CAMPOS dentro dele**, centrada. ⛔ Assim o contêiner pai não muda de tela para tela, e o
     formulário continua legvel — as duas coisas que estavam brigando.
   ⚠️ E alcança as 5 telas sem markup: quem tem `.form-wrap` usa a coluna dele; quem não tem
     (configurações, alimentar) ganha a coluna no próprio `.card__body`. */
[data-forma="formulario"] .card { max-width: none; }
[data-forma="formulario"] .card > .card__body,
[data-forma="formulario"] .card > .form-wrap,
[data-forma="formulario"] .card > .field-stack {
  max-width: min(100%, 960px); margin-inline: auto;
}


/* ---- Y · O CONTEÚDO SE ADAPTA — nada de coluna centrada com folga ----
   ⛔ Ele, olhando Configurações: *"você tem que adaptar o conteúdo e não centralizar e deixar
      com esse espaço."* ⭐ MEDIDO no que eu tinha acabado de fazer: cartão 1200, coluna 960,
      **120px de folga de cada lado**. Eu tinha trocado "vazio à direita" por "vazio dos DOIS lados".
   ⛔⛔ O TETO DE LEITURA SAI DE VEZ. ⚠️ E isto derruba o que eu mesmo escrevi no §3 deste lote
      (`max-width: min(100%, 960px)`): a decisão dele vence, e a razão é boa — **num painel de
      operador o espaço é para trabalhar, não para respirar.** Teto de leitura é lei de TEXTO
      corrido; formulário de operação é grade de controles.
   ⭐ E o vazio à direita que ele apontou lá atrás **não volta**: a causa daquele era o campo de
     600px num cartão de 1200 (`max-width:640px` inline), que saiu no §3. Sem teto E com o campo
     esticando, não sobra buraco em lugar nenhum.
   ⛔ LEI: `.input`/`.select`/`textarea` preenchem a coluna que os contém; quem dá a largura é a
      GRADE (`.grid2`, `.paramgrid`, `.row` com `flex:1`), nunca o campo. */
[data-forma="formulario"] .card > .card__body,
[data-forma="formulario"] .card > .form-wrap,
[data-forma="formulario"] .card > .field-stack {
  max-width: none; margin-inline: 0; width: 100%;
}
.form-wrap { max-width: none; }
[data-forma="formulario"] .field { width: auto; min-width: 0; }
[data-forma="formulario"] .field > .input,
[data-forma="formulario"] .field > .select,
[data-forma="formulario"] .field > textarea { width: 100%; }
/* linha de campos: divide a largura em frações iguais em vez de deixar sobra à direita */
[data-forma="formulario"] .row > .field { flex: 1 1 0; }


/* ---- Z · A GRADE DA TELA DE FORMULÁRIO ----
   ⛔ Ele: *"o layout dessas telas que tava com bug de largura estão bem bagunçados, precisa
      organizar pra ficar melhor de visualizar — isso é UX e UI."*
   ⭐ MEDIDO, e são três desordens distintas, não uma:
     1. **Identidade: 7 campos empilhados, cada um com 1160px de largura.** ⛔ Um campo de texto de
        1160 não é "adaptado à largura", é uma faixa — o olho perde o começo da linha ao chegar
        no fim do campo, e a tela vira uma coluna de tarjas.
     2. **Configurações: `.paramgrid` com SETE colunas de 152px.** O extremo oposto: campo curto
        demais para o próprio rótulo ("Taxa da plataforma %" em 152px).
     3. **Quatro espaçamentos convivendo**: 12, 14, 16 e `normal`.
   ⭐⭐ A regra é uma só, e resolve os três: **grade que se preenche sozinha, com coluna MÍNIMA
      legível.** `auto-fit` + `minmax` dá 3 colunas onde cabem 3 e 1 onde só cabe 1 — sem media
      query, sem contagem por tela, sem número chumbado.
   ⛔ A GUARDA: **o que não é campo curto atravessa a grade inteira.** Textarea, bloco de mídia,
      título de seção, nota e barra de ação ocupam as colunas todas — senão um `<textarea>` viraria
      uma coluna de 280px e a organização pioraria o que veio consertar. */
/* ⛔⛔ RECUO, e ele pegou na hora: *"essa tela tá horrível, pedi pra melhorar UI/UX e parece que
   piorou."* ⭐ MEDIDO na Identidade com `auto-fit minmax(280px)`: deu TRÊS colunas de 376 numa lista
   **heterogênea** (nome curto, seletor de cor, fonte, checkbox com parágrafo, upload de logo,
   slider, favicon) — e o resultado foram BURACOS: "Nome da marca" sozinho com duas colunas vazias
   à direita, rótulos em alturas diferentes na mesma faixa, blocos de mídia partindo a grade.
   ⭐⭐ A lição: **`auto-fit` é para lista HOMOGÊNEA** (a `.paramgrid`, que é só número, melhorou de
      152 para 276 com ela). Numa lista de naturezas mistas ela só espalha o vazio.
   ⚠️ DUAS colunas fixas produzem muito menos buraco: o campo comum pega metade, e tudo que é
     bloco (mídia, textarea, slider) atravessa. ⛔ E isto ainda NÃO é a organização final: a tela de
     formulário de verdade é SEÇÕES com título (a receita `.sec`/`.sec__h` do C247, que segue sem
     consumidor), e isso pede markup — está no relatório como a próxima peça. */
[data-forma="formulario"] .field-stack,
[data-forma="formulario"] .form-wrap,
[data-forma="formulario"] .card > .card__body {
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 16px 20px; align-items: start;
}
@media (max-width: 760px) {
  [data-forma="formulario"] .field-stack,
  [data-forma="formulario"] .form-wrap,
  [data-forma="formulario"] .card > .card__body { grid-template-columns: minmax(0, 1fr); }
}
[data-forma="formulario"] .field-stack > :not(.field),
[data-forma="formulario"] .form-wrap > :not(.field),
[data-forma="formulario"] .card > .card__body > :not(.field),
[data-forma="formulario"] .field:has(.media),
[data-forma="formulario"] .field:has(.filebtn) { grid-column: 1 / -1; }
/* ⛔⛔ REGRESSÃO MINHA, pega na remedição: eu dei `1 / -1` ao campo com `<textarea>`, e em
   `alimentar-codigos` **TODOS os campos são textarea** — os quatro atravessaram a grade e viraram
   faixas de 1160 empilhadas, pior do que o 2×2 de 572 que existia antes.
   ⭐ `span 2` em vez de `1 / -1`: o textarea ganha o dobro de uma coluna comum (≈570 numa grade de
     quatro, o que ele precisa para ser útil) e **para de engolir a linha inteira**. Numa grade de
     duas colunas ele naturalmente ocupa tudo; numa de uma, o grid clampa sozinho. */
/* ⛔⛔ ACHADO DA VERIFICAÇÃO: sem `>` esta regra alcançava também o `.field` DENTRO de um
   `.split-2` — e em `alimentar-codigos` TODO campo é textarea, então os N campos ganhavam
   `span 2` dentro de uma grade de 2 e voltavam a ser faixas empilhadas de 1160px, que é
   exatamente o defeito que eu tinha consertado antes. ⭐ Filho DIRETO do corpo do cartão. */
[data-forma="formulario"] .card > .card__body > .field:has(textarea),
[data-forma="formulario"] .form-wrap > .field:has(textarea),
[data-forma="formulario"] .field-stack > .field:has(textarea) { grid-column: span 2; }
/* a grade de parâmetros deixa de ter 7 colunas fixas e passa a respeitar o mesmo piso */
[data-forma="formulario"] .paramgrid { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 16px; }
/* espaçamento ÚNICO: era 12 · 14 · 16 · normal, quatro medidas para a mesma ideia */
[data-forma="formulario"] .row, [data-forma="formulario"] .split-2 { gap: 16px; }
/* a ação de salvar: à direita, separada por filete — a receita `.form-foot` do C247, agora com
   consumidor, e sem pedir markup novo */
[data-forma="formulario"] .card > .card__body > .row:last-child:has(> .btn--primary),
[data-forma="formulario"] .form-wrap > .row:last-child:has(> .btn--primary) {
  justify-content: flex-end; border-top: 1px solid var(--border-1); padding-top: 16px; margin-top: 4px;
}


/* ============================================================================================
   ⭐⭐⭐ C249 §AA · AS PEÇAS DA TELA DE FORMULÁRIO.
   ⛔ O Matheus DESENHOU em cima da captura da Identidade onde cada coisa devia ficar:
      campos de texto e seletores à ESQUERDA, e o bloco de mídia (Logo + Tamanho + Favicon)
      agrupado num retângulo e levado para a coluna DIREITA, que estava vazia.
   ⭐⭐ E o desenho dele resolve o que três tentativas minhas de CSS não resolveram: **grade
      automática nenhuma adivinha que Logo, Tamanho e Favicon são UM grupo.** Quem sabe agrupar é o
      markup. Por isso estas peças existem — para a próxima tela já nascer composta, que foi
      exatamente o que ele cobrou: *"tem que ter os componentes, pra não ficar criando do zero"*.
   ============================================================================================ */

/* duas colunas de formulário. ⚠️ `align-items: start` é obrigatório: sem ele as colunas esticam
   até a altura da mais alta e o conteúdo da curta flutua no meio do nada. */
.form-2col { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 24px 28px; align-items: start; }
.form-col { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
/* uma linha de campos DENTRO da coluna: frações iguais, nunca px */
.form-col .row > .field { flex: 1 1 0; min-width: 0; }
/* ☠ MORTO (verificação adversarial C249) — removido: `.form-foot`, e o pior é o comentário: ele AFIRMAVA "agora tem [consumidor]" e a busca no app.js dá **zero**. O botão Salvar destas telas foi para o `.content__head` — foi o que ele pediu: *"em outras telas o botão salvar fica em cima"* — então o rodapé não tem lugar. ⛔ Registrar um órfão como resolvido é pior que deixá-lo: a próxima leva confia no registro */
/* o que precisa da largura toda dentro da grade de duas colunas */
.form-2col > .errbox, .form-2col > .alertbar { grid-column: 1 / -1; }
@media (max-width: 900px) { .form-2col { grid-template-columns: minmax(0, 1fr); gap: 20px; } }

/* ---- AA2 · O `.row` DE CAMPOS ACHATA NA GRADE ----
   ⛔⛔ A CAUSA DO BURACO, achada: minha regra mandava todo filho que NÃO é `.field` atravessar a
      grade — e um `.row` com dois campos dentro é exatamente isso. ⚠️ Resultado na Identidade:
      `.field` solto ficava na coluna 1 com a 2 VAZIA, porque o vizinho seguinte era sempre um
      `.row` atravessando. **O buraco não era do campo: era do invólucro.**
   ⭐ `display: contents` dissolve o invólucro: os `.field` de dentro dele viram filhos diretos da
     grade e entram no mesmo fluxo. ⛔ Só no `.row` QUE CONTÉM CAMPO — a barra de ação é `.row`
     também, e dissolvê-la espalharia o botão pelas células. */
[data-forma="formulario"] .card > .card__body > .row:has(> .field),
[data-forma="formulario"] .form-wrap > .row:has(> .field),
[data-forma="formulario"] .field-stack > .row:has(> .field) { display: contents; }

/* ---- AA3 · A GRADE DE PARÂMETROS CABE 5 NA LINHA ----
   ⛔ Ele: *"aqui você pode diminuir a largura dos forms, pra ficar tudo na mesma linha."*
   ⚠️ MEDIDO: com piso de 220px e gap 16 numa faixa de 1150, `auto-fit` resolve **4** colunas
     (5×220 + 4×16 = 1164 > 1150) — e o 5º campo ("Moeda custo/código") descia sozinho.
   ⭐ Piso de 180px: 5×180 + 64 = 964 ≤ 1150, então cabem 5 (e viram ~217px cada, que é largura de
     sobra para câmbio e percentual). ⛔ O piso continua existindo para não espremer no estreito. */
[data-forma="formulario"] .paramgrid { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }


/* ---- AB · a NOTA de campo, tokenizada ----
   ⚠️ Era `style="display:block;font-size:11px;margin-top:6px"` repetido em cada campo. Vira
     classe — mesma regra do §R (texto pequeno leva peso 500 para não fechar a contraforma). */
.campo-nota { display: block; font-size: 11px; line-height: 15px; font-weight: 500;
  color: var(--text-3); margin-top: 6px; }
/* o título de grupo dentro da coluna (a receita `.sec__h` do C247, agora com consumidor) */
.form-col > .sec__h, .form-full > .sec__h { font-size: 12px; font-weight: 500; color: var(--text-2);
  letter-spacing: normal; text-transform: none; margin-bottom: -4px; }

/* ---- C251 · O BLOCO QUE ATRAVESSA a grade de duas colunas ----
   ⛔ Até aqui só `.errbox` e `.alertbar` atravessavam, e os dois são avisos — não havia como um
     GRUPO DE CAMPOS ocupar a largura inteira. As cinco redes sociais são o primeiro caso: postas
     dentro de uma das colunas elas empilhariam cinco linhas e a coluna A ficaria quase o dobro da
     altura da B, numa tela cujo desenho inteiro é o equilíbrio das duas.
   ⭐ `auto-fit` + piso de 210px resolve sozinho quantas cabem por linha — é a mesma receita da
     `.paramgrid` (AA3), e pela mesma razão: o número de campos muda quando uma rede é adicionada,
     então chumbar a contagem de colunas seria chumbar a lista em mais um lugar. */
.form-full { display: flex; flex-direction: column; gap: 16px; min-width: 0; }
.form-2col > .form-full,
[data-forma="formulario"] .card > .card__body.form-2col > .form-full { grid-column: 1 / -1; }
.campo-grade { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 16px; }
/* ⛔ a `.campo-grade` DECLARA a própria composição, então sai do automático do `data-forma`
   (a LEI do §AC) — sem isto cada `.field` de dentro atravessaria a grade externa e empilharia. */
[data-forma="formulario"] .campo-grade > .field { grid-column: auto; }


/* ---- AC · A GRADE GENÉRICA NÃO PODE PISAR NA COMPOSTA ----
   ⛔⛔ BUG MEU, visível na 1ª renderização: as duas colunas da Identidade saíram EMPILHADAS.
   ⭐ A causa: `[data-forma="formulario"] .card > .card__body > :not(.field) { grid-column: 1/-1 }`
     — e `.form-col` **não é `.field`**, então cada coluna atravessava a grade inteira e elas
     caíam uma sob a outra. ⛔ A regra genérica, feita para telas SEM composição, estava
     desmontando justamente a tela que ganhou composição.
   ⭐ LEI: **quem declara a própria composição (`.form-2col`) sai do automático.** É o mesmo
     princípio do `data-forma`: o específico manda, o genérico só cobre quem não disse nada. */
[data-forma="formulario"] .card > .card__body.form-2col { display: grid; gap: 24px 28px; }
[data-forma="formulario"] .card > .card__body.form-2col > .form-col { grid-column: auto; }
[data-forma="formulario"] .card > .card__body.form-2col > .form-foot,
[data-forma="formulario"] .card > .card__body.form-2col > .errbox,
[data-forma="formulario"] .card > .card__body.form-2col > .alertbar { grid-column: 1 / -1; }
/* dentro da coluna NÃO há grade: é pilha. O `.row` de campos volta a ser flex normal. */
.form-col .row { display: flex; }
.form-col .field { width: auto; }


/* ---- AD · A AÇÃO PRIMÁRIA DA TELA DE FORMULÁRIO VAI PARA O CABEÇALHO ----
   ⛔ Ele desenhou a seta indo do topo direito até o `Salvar identidade`. ⭐ E fecha uma questão
     que ele mesmo tinha aberto antes (*"em outras telas o botão salvar fica em cima"*): o lugar
     da ação primária é o **cabeçalho da tela**, junto do título, como em `Layout do site`.
   ⚠️ O `.content__head` já é `justify-content: space-between` — o botão vai para a direita
     sozinho, sem regra nova. Só o alinhamento vertical precisa descer do `flex-end` para o centro
     da faixa, senão ele encosta na linha de base do subtítulo. */
.content__head > .btn { align-self: center; flex: none; }


/* ---- AE · O SELETOR DENTRO DE UM CAMPO TEM ALTURA DE CAMPO ----
   ⛔ Ele: *"o toggle tá menor"*, apontando o seletor `Branco` ao lado do campo de cor.
   ⭐⭐ MEDIDO nas 38 rotas: **`.pdd` dentro de `.field` = 32px em 5 pontos**, contra **`.input`
      dentro de `.field` = 40px em 23**. ⛔ A peça não está menor "um pouco": ela está na altura
      ERRADA, e é a **regra de altura por CONTEXTO do C244** sendo violada —
      **28 = ação · 32 = barra de filtro · 40 = campo de formulário.**
   ⚠️ A causa é histórica: o `.pdd` nasceu no C241 como GATILHO DE BARRA (o widget de período,
     que vive no cabeçalho), e herdou 32. Quando o §2 deste lote o mandou substituir `<select>`
     DENTRO de formulário, ele levou a altura de barra para um lugar de campo.
   ⭐ A regra é de contexto, não de tela: o gatilho dentro de `.field` vira 40 e volta a formar
     linha com o `.input` ao lado. ⛔ Na barra de filtro ele CONTINUA 32, que é o certo lá. */
.field .pdd__trig { height: 40px; padding: 0 12px; }
.field .pdd { width: 100%; }
.field .pdd__trig { width: 100%; justify-content: space-between; }

/* ---- AF · MAIS AR ENTRE AS COLUNAS ----
   ⛔ Ele: *"aumenta o espaçamento entre as colunas."* ⭐ 28 → **48**: as duas colunas são
     assuntos diferentes (o que se escreve × a mídia da marca), e o vale entre elas é o que diz
     isso sem precisar de filete. */
.form-2col { gap: 24px 48px; }
[data-forma="formulario"] .card > .card__body.form-2col { gap: 24px 48px; }


/* ---- AG · O RÓTULO DO BOTÃO ESTAVA FINO DEMAIS ----
   ⛔ Ele: *"aumenta o peso da fonte nos botões, ela está muito fina, difícil de ler."*
   ⭐ MEDIDO: `.btn { font-weight: 400 }` (app.css:1883). ⚠️ Em botão, 400 é peso de TEXTO CORRIDO
     — e botão não é texto corrido: é alvo, tem de sobressair do que está em volta. Pior no
     primário, onde o rótulo é escuro sobre claro e o traço fino some no fundo.
   ⭐ Vai a **500**, o mesmo degrau que o §R deu ao texto pequeno — e pela mesma razão: peso
     sustenta legibilidade onde o tamanho não ajuda.
   ⚠️ Vale para as três variantes; `.icobtn` não tem rótulo e não entra. */
/* ⚠️ 500 ainda ficou fino no olho dele, e a medição explica por quê: a **Manrope tem traço
   estreito** — o 500 dela lê como o 400 de uma Inter. ⭐ CONFERI as faces carregadas antes de
   subir: `Manrope 600 loaded` e `700 loaded` existem de verdade, então 600 é peso REAL e não
   negrito sintético (que borraria o traço em vez de engrossá-lo). */
.btn.btn, .btn.btn > span { font-weight: 600; }
/* ⚠️ classe repetida (0,2,0) de propósito: MEDIDO depois de subir para 600, **dois botões
   pequenos ficaram em 500** — são os `<label class="btn ... filebtn">`, onde o rótulo vive num
   `<span>` interno que herdava de outra regra. ⛔ Um só peso em TODOS, sem depender de ordem
   nem de quem escreveu por último — mesmo truque do `:root:root` e do `.badge.badge`. */


/* ============================================================================================
   ⭐⭐⭐ C249 §AI · A LEI DE ALTURA, escrita de uma vez — e as três exceções que ele achou.
   ⛔ Ele: *"você não fez o design-system e os componentes???"* ⭐ Fiz peças (`.pdd`, `sparkline`,
      `.icobtn`, `.form-2col`), **mas não fiz a LEI que governa todas elas** — e é por isso que ele
      continua achando exceção atrás de exceção. A lei do C244 existia em texto; aqui ela vira CSS.
   ============================================================================================ */

/* ---- AI-1 · A BARRA DE BUSCA ESTAVA ACHATADA ----
   ⛔ Ele: *"barra de pesquisa tá com altura muito baixa, botão buscar não segue o padrão."*
   ⭐⭐ MEDIDO na Consulta: `input h=32` + `botão h=40` na MESMA linha — linha mista de livro.
   ⭐ A CAUSA, achada no próprio arquivo (app.css:2307): a regra do C248 escopava por POSIÇÃO
     (`.content > .row .input`, `.card__body > .row .input`) e não por FUNÇÃO. ⛔ Um `.row` no corpo
     tanto pode ser barra de filtro (32 é certo) quanto **formulário de busca** (40 é certo) — e a
     regra não distinguia, então achatava os dois.
   ⭐ A função está no `<form>`: a barra de busca da Consulta é `form.row`. Formulário é campo, e
     campo é 40 — junto com o botão que está ao lado dele. */
form.row .input:not(textarea), form.row .select, form.row .pdd__trig { height: 40px; }
form.row .btn { height: 40px; }

/* ---- AI-2 · O `display: contents` DESALINHOU O CONFIGURAÇÕES ----
   ⛔ Ele: *"posições e alturas estão diferentes"*, com as linhas verdes sobre Google Ads × Título.
   ⭐⭐ MEDIDO: topo 1350 contra 1362 — **12px**, com os dois em `align-items: start`. ⛔ Se
      estivessem na MESMA linha de grade com `start`, os topos seriam idênticos. Logo **não
      estavam**: o `display: contents` que eu pus dissolveu o `.row` e jogou os campos dele em
      células fora de ordem, ao lado de campos de OUTRA linha.
   ⛔⛔ EU TROQUEI UM DEFEITO POR OUTRO: `contents` matou o buraco e criou desalinhamento, que é
      pior — buraco é feio, desalinhado parece quebrado.
   ⭐ Volta a atravessar: o `.row` ocupa a linha inteira e distribui os campos dele em frações
     iguais. **Previsível vence compacto.** */
[data-forma="formulario"] .card > .card__body > .row:has(> .field),
[data-forma="formulario"] .form-wrap > .row:has(> .field),
[data-forma="formulario"] .field-stack > .row:has(> .field) {
  display: flex; grid-column: 1 / -1; gap: 16px; align-items: flex-start;
}
[data-forma="formulario"] .card > .card__body > .row:has(> .field) > .field,
[data-forma="formulario"] .form-wrap > .row:has(> .field) > .field,
[data-forma="formulario"] .field-stack > .row:has(> .field) > .field { flex: 1 1 0; min-width: 0; }

/* ---- AI-3 · AS TRÊS CAIXAS DO MESMO TAMANHO ----
   ⛔ Ele, duas vezes: *"deixa os 3 com a mesma largura"* — e eu respondi errado da 1ª vez.
   ⭐⭐ MEDIDO: os três `.field` **JÁ mediam 175px iguais**. ⚠️ O que ele vê diferente é a CAIXA:
      no campo de cor, a amostra `input[type=color]` (40px) + o gap (10) comem 48, e o campo de
      texto sobra com **127**, contra 175 dos dois seletores. ⛔ Eu estava certo na letra e errado
      no olho — e o olho dele é quem manda numa questão de aparência.
   ⭐ A amostra sai de FORA e vai para DENTRO do campo, como prefixo: aí a caixa do hex mede os
     mesmos 175 e as três ficam idênticas. É a composição padrão de campo de cor.
   ⚠️ `pointer-events` fica, e a amostra continua sendo o `input[type=color]` de verdade — clicar
     nela ainda abre o seletor do sistema. Nada de comportamento muda. */
.field--cor { position: relative; }
.field--cor .row { display: block; }
.field--cor input[type="color"] { position: absolute; left: 8px; bottom: 8px; width: 24px; height: 24px;
  padding: 0; border-radius: 6px; z-index: 2; }
.field--cor .input { padding-left: 42px; width: 100%; }

/* ---- AI-4 · A LEI, escrita ----
   ⚠️ Para o próximo lote não reabrir isto: **a altura vem do CONTEXTO, e são três.**
     · **28** — ação dentro de linha de tabela (`.icobtn`, botão de `td`)
     · **32** — barra de filtro (cabeçalho de cartão ou de tela)
     · **40** — campo de formulário (dentro de `.field`, ou dentro de um `<form>`)
   ⛔ Toda exceção que aparecer é defeito, não estilo. E se precisar de uma quarta altura, o que
      está errado é o CONTEXTO da peça, não a tabela de alturas. */
.field .input:not(textarea), .field .select, .field .pdd__trig, .field > .btn { height: 40px; }


/* ---- AK · O SELETOR DE DATA ----
   ⭐ Mesma receita do `.pdd`: gatilho de campo (40 no formulário) + popover `surface-2`, raio 12,
     anel de 6 %, sombra longa. ⛔ Nada dentro do popover é tabulável — o foco fica no gatilho e o
     cursor é apontado por `aria-activedescendant`, igual ao §2. */
.dtc { position: relative; display: inline-flex; }
.dtc__trig { display: inline-flex; align-items: center; gap: 8px; height: 40px; padding: 0 12px;
  border-radius: var(--radius-input); background: var(--field-bg); color: var(--text-1);
  box-shadow: inset 0 0 0 1px var(--border-2); border: 0; font: inherit; cursor: pointer;
  font-size: 14px; line-height: 20px; white-space: nowrap; }
.dtc__trig:hover { background: rgba(255,255,255,.075); }
.dtc__trig:focus-visible { outline: 2px solid var(--ring); outline-offset: 2px; }
.dtc__trig svg { color: var(--text-3); flex: none; }
.dtc__trig .dim { color: var(--text-4); }
.dtc__pop { position: absolute; z-index: 60; top: calc(100% + 6px); left: 0; width: 268px;
  padding: 12px; border-radius: 12px; background: var(--elevated);
  box-shadow: inset 0 0 0 1px var(--border-2), 0 12px 32px var(--shadow-3); }
/* ⛔ ACHADO ADVERSARIAL — o comentário do §A promete "limita a altura, com rolagem própria", e
   isso valia só para o `.pdd__pop`, que tem `overflow-y: auto`. O calendário ficava em
   `overflow: visible`: o `max-height` que o `popAncorar` crava apertava a caixa e o conteúdo
   TRANSBORDAVA em vez de rolar. ⚠️ Um calendário com hora passa de 380px — em janela baixa, o
   campo Hora e os botões Limpar/Hoje ficavam fora, sem barra nenhuma para alcançá-los. */
.dtc__pop { overflow-y: auto; }
.dtc__pop[hidden] { display: none !important; }   /* a lição do §V3: `display` de autor vence o [hidden] */
.dtc__nav { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 8px; }
.dtc__mes { font-size: 13px; font-weight: 500; color: var(--text-1); text-transform: capitalize; }
.dtc__sem, .dtc__grade { display: grid; grid-template-columns: repeat(7, 1fr); gap: 2px; }
.dtc__sem span { text-align: center; font-size: 11px; font-weight: 500; color: var(--text-3); padding: 4px 0; }
.dtc__d { height: 32px; border: 0; background: transparent; border-radius: 8px; cursor: pointer;
  font: inherit; font-size: 13px; color: var(--text-2); display: flex; align-items: center; justify-content: center; }
.dtc__d:hover { background: rgba(255,255,255,.075); color: var(--text-1); }
.dtc__d--fora { color: var(--text-4); opacity: .45; pointer-events: none; }
.dtc__d[disabled] { opacity: .3; pointer-events: none; }
/* ⭐ o dia de HOJE é um anel, o SELECIONADO é preenchido — duas coisas diferentes, duas formas
   diferentes. ⛔ Pintá-los da mesma cor faria "hoje" parecer escolhido. */
.dtc__d.is-hoje { box-shadow: inset 0 0 0 1px var(--border-hover); }
.dtc__d.is-on { background: var(--serie-1); color: #fff; font-weight: 600; }
.dtc__d.is-foco { background: rgba(255,255,255,.10); color: var(--text-1); }
.dtc__d.is-on.is-foco { background: var(--serie-1); }
.dtc__hora { display: flex; align-items: center; gap: 8px; margin-top: 10px;
  padding-top: 10px; border-top: 1px solid var(--border-1); }
.dtc__hora label { font-size: 12px; color: var(--text-3); }
.dtc__hora .input { height: 32px; width: 72px; text-align: center; }
.dtc__pe { display: flex; justify-content: space-between; gap: 8px; margin-top: 10px;
  padding-top: 10px; border-top: 1px solid var(--border-1); }
/* na barra de filtro o gatilho segue a lei dos 32 */
.card__head .dtc__trig, .content__head .dtc__trig, td .dtc__trig { height: 32px; }
:root[data-theme="light"] .dtc__trig:hover { background: rgba(0,0,0,.05); }
:root[data-theme="light"] .dtc__d:hover { background: rgba(0,0,0,.06); }
:root[data-theme="light"] .dtc__d.is-foco { background: rgba(0,0,0,.08); }


/* ---- AM · A POP-UP DA HOME, no molde da Identidade ----
/* ☠ MORTO (verificação adversarial C249) — removido: `.pop-liga` — tokenizei um inline que o markup deixou de usar no MESMO lote, quando aquele `<label>` virou a peça `toggle()`. Nasceu órfão */
/* o preview do banner ocupa a coluna e mantém a proporção recomendada (16:9) */
.form-col .midia-hero { width: 100%; aspect-ratio: 16 / 9; min-height: 0; }


/* ---- AN · OS DOIS QUE SOBRARAM NA POP-UP ----
   ⛔ 1. *"deixa os 3 com a mesma largura"* — e de novo os `.field` JÁ mediam 175 iguais.
      ⭐ MEDIDO: quem não preenchia era o GATILHO por dentro. O `.pdd__trig` eu já tinha mandado
        ocupar 100 % no §AE; o `.dtc__trig`, que nasceu depois, ficou com largura de conteúdo —
        "dd/mm/aaaa --:--" dá ~155 contra os 175 do seletor ao lado.
      ⛔ É a mesma lei, e ela vale para QUALQUER gatilho dentro de um campo: **o controle preenche
        a coluna que o contém.** Escrita agora de uma vez, para o próximo gatilho já nascer certo.
   ⛔ 2. *"ficou sobrando um espaço a mais na direita"* — MEDIDO: `.midia-hero` tem
      **`max-width: 420px`** cravado, numa coluna de 556. ⚠️ Aquele teto existe para a tela de
      Mídia, onde o preview é um card de galeria; dentro de uma COLUNA de formulário ele vira
      exatamente o buraco à direita que ele apontou. ⭐ Na coluna, o teto sai. */
.field .dtc, .field .dtc__trig { width: 100%; }
.field .dtc__trig { justify-content: flex-start; }
.form-col .midia-hero { max-width: none; }


/* ---- AO · QUANDO A COLUNA B É MÍDIA, ELA É MAIS ESTREITA ----
   ⛔ Ele: *"agora ficou desalinhado"*, com a linha verde marcando o fim da coluna A contra a B
      ainda correndo. ⭐ MEDIDO: com as colunas em 1:1 (556 cada), o preview 16:9 fica **313px de
      altura** e a coluna da mídia passa da coluna de campos.
   ⛔⛔ E aqui os dois pedidos dele brigavam: *"sobra espaço à direita"* (o preview tinha teto de
      420 numa coluna de 556) e *"ficou desalinhado"* (tirando o teto, o preview cresceu e empurrou
      a coluna). ⭐ **Resolver um de cada vez sempre ia quebrar o outro** — o que precisa mudar é a
      PROPORÇÃO das colunas, não o tamanho do preview.
   ⭐ A lei: **imagem é AMOSTRA, campo é trabalho** — a coluna de mídia recebe menos largura, o
     preview encolhe junto (mantendo 16:9, sem sobra lateral) e as duas colunas param de brigar.
   ⚠️ Modificador, não exceção de tela: qualquer formulário com bloco de mídia à direita usa. */
.form-2col--midia { grid-template-columns: 1.45fr 1fr; }
[data-forma="formulario"] .card > .card__body.form-2col--midia { grid-template-columns: 1.45fr 1fr; }
@media (max-width: 900px) {
  .form-2col--midia,
  [data-forma="formulario"] .card > .card__body.form-2col--midia { grid-template-columns: minmax(0, 1fr); }
}


/* ---- AP · O TOGGLE COMO PEÇA ----
   ⭐ O rótulo ao lado do interruptor herda o tamanho do contexto; em barra de filtro ele continua
     pequeno e apagado, em formulário ele é texto de campo. ⛔ Uma peça, dois pesos, zero exceção. */
.sms-switch__lb { font-size: 14px; line-height: 20px; color: var(--text-1); }
.card__head .sms-switch__lb, .content__head .sms-switch__lb { font-size: 12px; color: var(--text-3); }
.form-col > .sms-switch { align-self: flex-start; }


/* ---- AQ · O TOGGLE ESTAVA DESALINHADO EM TODAS AS 32 OCORRÊNCIAS ----
   ⛔ Ele: *"toggle ficou desalinhado, mesmo defeito que tem em outras telas"* — e "outras telas"
      é a chave: **não era a Pop-up, era a peça.**
   ⭐⭐ MEDIDO: o `.sms-switch` começa em 448 (alinhado com o campo ao lado — desvio ZERO), mas o
      `.sms-switch__tk`, que é o interruptor VISÍVEL, começa em **472**. ⛔ 24px de recuo, e nada no
      `.sms-switch` tem padding ou margin.
   ⭐⭐ A CAUSA: `.sms-switch input { position: absolute; width: 0; height: 0 }` (app.css:905) existe
      para TIRAR a caixa nativa do fluxo. ⚠️ Mas o C245 escreveu `input[type="checkbox"] { width:
      16px; height: 16px; … }` (app.css:2351) — **mesma especificidade (0,1,1), e mais tarde no
      arquivo.** Ela venceu, a caixa nativa voltou a ocupar 16px dentro do toggle, e empurrou o
      interruptor 16 + 8 de gap = os 24px medidos.
   ⛔⛔ Uma regra de um lote quebrou a peça de outro **sem erro, sem console, sem ninguém ver** —
      por meses, nas 32 ocorrências. É a mesma lição do `.card:hover`: **anular exige empatar a
      especificidade, e escrever depois não basta quando o outro também escreve depois.**
   ⭐ `.sms-switch input[type="checkbox"]` é (0,2,1) e fecha a questão sem depender de ordem. */
.sms-switch input[type="checkbox"] {
  position: absolute; opacity: 0; width: 0; height: 0; margin: 0; padding: 0;
  appearance: none; -webkit-appearance: none; border: 0;
}
.sms-switch input[type="checkbox"]::after,
.sms-switch input[type="checkbox"]::before { display: none; }


/* ---- AR · O PINO DO TOGGLE ESTAVA FORA DE CENTRO ----
   ⛔ Ele: *"design do toggle bugado quando desativado"*.
   ⭐⭐ MEDIDO e a conta fecha: o trilho é 22px de altura e o pino 16px, com `top: 2px` — conta
      escrita quando o trilho tinha **borda de 1px**, e aí a caixa interna era 20: 2 em cima, 2
      embaixo, centrado. ⚠️ O C248 trocou a borda por `box-shadow` (app.css:2428, `border: 0`) e a
      caixa interna virou 22 — mas o `top: 2px` ficou. **2 em cima, 4 embaixo:** o pino subiu, e no
      desligado (onde ele está na ponta esquerda) isso salta aos olhos.
   ⭐ `(22 − 16) ÷ 2 = 3`. E o deslocamento do ligado continua 16, porque `38 − 16 − 3 − 3 = 16`.
   ⛔ LEI: quem tira a borda de uma caixa **tem de refazer a conta de quem se posiciona dentro
      dela** — `border-box` muda a caixa interna, e todo `top`/`left` absoluto ali dentro depende. */
.sms-switch__tk::after { top: 3px; left: 3px; }
.sms-switch input:checked + .sms-switch__tk::after { transform: translateX(16px); }
/* ⚠️ desligado: o pino é `--text-3` sobre trilho de 5 % — opaco e legível, sem virar botão branco */
.sms-switch__tk { background: rgba(255,255,255,.06); }
:root[data-theme="light"] .sms-switch__tk { background: rgba(0,0,0,.08); }


/* ---- AT · O PESO DO TEXTO DA BARRA LATERAL ----
   ⛔ Ele: *"aumenta o peso do texto da sidebar"*. ⚠️ MEDIDO: item em **400**, categoria em **500**.
   ⭐ Mesma lição do §AG: a **Manrope tem traço estreito**, e o 400 dela some contra um fundo
     escuro num texto de 14px. ⛔ E a barra é o que o operador lê de relance o dia inteiro.
   ⭐ Item vai a **500**, categoria a **600** — e a hierárquia se mantém (a categoria continua um
     degrau acima do filho, que foi o ponto do §P). ⚠️ As faces existem de verdade: `Manrope 500
     loaded` e `600 loaded`, conferidas no §AG antes de subir — nada de negrito sintético. */
.nav-item { font-weight: 500; }
.nav-group__title { font-weight: 600; }


/* ---- AU · OS FILTROS DO CABEÇALHO ANDAM JUNTOS, À DIREITA ----
   ⛔ Ele: *"junta os 3 e alinha à direita"*, na Base de Clientes.
   ⭐⭐ MEDIDO: o `.card__head` é `justify-content: space-between` — com QUATRO filhos (a busca e
      os três seletores) o espaço se reparte entre todos: **vãos de 140, 140 e 140px.** ⛔ Não é que
      faltasse alinhamento; é que cada seletor era um filho solto disputando o espaço.
   ⭐ A prova de que o conserto é agrupar: `registros`, a única outra tela com 4 controles no
     cabeçalho, **já envolve os dele num só filho** — e lá não há vão nenhum.
   ⭐⭐ E a peça já existia: `.filtros` foi escrita no C247 (app.css:2559) e **nunca teve
      consumidor** — a terceira peça órfã que este lote encontra. Agora tem.
   ⚠️ `margin-left: auto` empurra o grupo para a direita e `flex: 0 0 auto` impede que ele estique;
     quem cresce é a busca (`.cresce`), que é o campo que o operador usa mais. */
/* ⚠️ ELE MUDOU DE IDEIA, e a regra melhorou com isso: *"alinha à esquerda, junto com a barra de
   pesquisa"*. ⭐ E faz sentido — busca e filtros são **a mesma ação** (recortar a lista), então
   andam juntos. Jogar os filtros para a direita os separava do campo que eles qualificam.
   ⭐⭐ A regra de sistema que sai disso: **um `.card__head` que tem filtros e NÃO tem título deixa
      de ser `space-between`.** O `space-between` existe para "título à esquerda, ação à direita";
      sem título, ele vira só um espalhador. ⛔ `:has()` deixa o cabeçalho decidir isso sozinho,
      sem a tela precisar declarar nada. */
.card__head:has(> .filtros):not(:has(h3)),
.content__head:has(> .filtros):not(:has(h1)) { justify-content: flex-start; }
.card__head > .filtros, .content__head > .filtros { margin: 0; flex: 0 0 auto; gap: 8px; }
/* ⚠️ a busca deixa de esticar: com tudo à esquerda, um campo que cresce empurraria os filtros
   de volta para a direita — exatamente o que ele pediu para desfazer. */
.search-field.cresce { flex: 0 1 320px; min-width: 200px; }
@media (max-width: 760px) {
  .card__head > .filtros, .content__head > .filtros { width: 100%; }
  .search-field.cresce { flex: 1 1 100%; max-width: none; }
}


/* ---- AV · O GATILHO DE CALENDÁRIO SEGUE A LEI DE ALTURA, E EU TINHA INVERTIDO O PADRÃO ----
   ⛔ Ele: *"verifica se todos os botões de calendário estão com o mesmo tamanho, vi telas que o
      tamanho é maior"*. ⭐ MEDIDO nos 8 gatilhos: **4 em linha MISTA** — o calendário em 40 ao lado
      de controles em 32, em `campanhas`, `campanhas-sms`, `campanhas-email` e `vendas-vivo`.
   ⛔⛔ O DEFEITO É MEU, e é de premissa: quando escrevi o `.dtc__trig` (§AK) dei **40 como padrão**
      e 32 só no cabeçalho. ⚠️ Mas o `.pdd__trig`, que é a peça irmã, faz o CONTRÁRIO: **32 é o
      padrão e 40 é a exceção de `.field`** — porque a maioria dos seletores vive em barra de filtro.
      Duas peças do mesmo sistema com padrões opostos, e a conta só fecha onde as duas se encontram.
   ⭐ O calendário passa a nascer 32, como o seletor. ⛔ A regra `.field .dtc__trig { height: 40px }`
     do §AN continua e é quem promove no formulário — exatamente como no `.pdd`.
   ⚠️ E a LARGURA também dançava: **122px vazio contra 130px preenchido** — a barra se mexia quando
     o operador escolhia a data. ⭐ `min-width` pelo FORMATO (que é fixo), não pelo valor. */
.dtc__trig { height: 32px; min-width: 142px; }
.dtc[data-hora] .dtc__trig { min-width: 186px; }
.field .dtc__trig { height: 40px; }


/* ============================================================================================
   ⭐⭐⭐ C249 §AW · O QUE A RÉGUA DE CONFORMIDADE ACHOU.
   ⛔ Ele: *"aproveita o gancho e já confere tudo — todos os botões, todos os forms, verifica se
      seguem o padrão"*. ⭐ Virou uma RÉGUA: 14 leis medidas nas 38 rotas, 318 controles, 168
      botões, 51 campos, 696 chips. **341 violações na 1ª passada.**
   ⚠️ E parte delas era a RÉGUA errada, não o painel — está anotado onde.
   ============================================================================================ */

/* ---- AW-1 · TEXTO PEQUENO: o §R passou ao largo de cinco formas ----
   ⛔ MEDIDO: **112 nós** ainda abaixo da lei (piso 11px, peso 500 até 12,5px). O §R consertou as
      formas que a medição daquele momento mostrou; estas cinco vivem em peças que não têm classe
      própria no texto (é um `<span>` nu dentro do bloco), e o filtro por classe não as via.
   ⭐ Agora o alvo é o BLOCO, não o texto — assim vale para qualquer `<span>` que nasça ali dentro.
   ⛔⛔ E o pior deles: `.stockcard__badge` a **9px**, abaixo do piso. Nenhum rótulo do painel deve
      ter 9px — é tamanho de nota de rodapé de contrato, não de estado de estoque. */
.kpi__foot, .kpi__foot span { font-weight: 500; }
.vivo-live, .vivo-live span { font-weight: 500; }
.state span, .state p { font-weight: 500; }
.trans-donut__center span { font-size: 11px; font-weight: 500; }
/* ☠ MORTO (C250 §C) — o Estoque passou a usar `.card` + `.kpi`: a subida de 9 para 11px (o chip do sistema já nasce legivel) */

/* ---- AW-2 · O CAMPO DENTRO DE `.field` VOLTOU A 32 EM TRÊS TELAS ----
   ⛔ MEDIDO: `configuracoes` e `downloads` com `.input` a 32 DENTRO de `.field`.
   ⭐ A CAUSA é a regra do C248 (app.css:2307): `.card__body > .row .input { height: 32px }`. Ela
     escopa por POSIÇÃO (um `.row` no corpo) e não por FUNÇÃO — é a MESMA causa do §AI-1, que eu
     consertei só para `form.row`. ⛔ Aqui fecho pela outra ponta: **estar dentro de `.field` manda
     mais que estar dentro de um `.row`**, porque `.field` é declaração de função e `.row` é só
     arranjo. `.field .input` é (0,2,0) e vence `.card__body > .row .input` (0,2,1)? NÃO — aquela é
     mais específica. ⭐ Por isso aqui vai com classe repetida: `.field.field` → (0,3,0). */
.field.field .input:not(textarea), .field.field .select { height: 40px; }
/* ⚠️ o botão DENTRO de um campo é AÇÃO DE APOIO ("Escolher da biblioteca", "Subir nova"), e ação
   não é controle de entrada: ela fica em 32. ⛔ Isto NÃO é exceção — é a lei lida direito, e a
   minha régua é que cobrava 40 de tudo que estivesse num `.field`. */
.field .btn { height: 32px; }

/* ---- AW-3 · O `.btn--sm` NA BARRA ESTAVA EM 28 ----
   ⛔ MEDIDO em `campanhas`, `campanhas-sms`, `campanhas-email` e `midia`: `btn--outline` a **28**
      numa barra de filtro, ao lado de controles de 32. ⚠️ 28 é a altura de AÇÃO EM LINHA DE
      TABELA; em barra, a lei é 32. */
.card__head .btn--sm, .content__head .btn--sm, .filtros .btn--sm { height: 32px; }


/* ---- AW-5 · O RESTO DA RÉGUA ----
   ⚠️ 2ª passada: **341 → 74 violações**. Estas fecham o que sobrou de CSS.
   ⛔ 1. O `.btn--sm` a 28 em `campanhas`/`midia`: a regra do AW-3 escopava a `.card__head`, e
      nessas telas a barra de ação é um `.row` solto no corpo. ⭐ A lei é por FUNÇÃO: fora de `td`,
      botão pequeno é 32. Dentro de `td` ele continua 28, que é a altura de ação em linha. */
.btn--sm { height: 32px; }
td .btn--sm, td .btn { height: 28px; }

/* ⛔ 2. Texto pequeno, as últimas quatro formas — todas com nome próprio, nenhuma com
      classe de texto: a célula da tabela de links, o gabarito de mensagem, a nota do rodapé de
      página e o `<small>` de Projeções (a **10px**, abaixo do piso). */
.table td, .table td .dim { font-weight: 500; }
.sms-camp__tpl { font-weight: 500; }
.pager__info { font-weight: 500; }
small { font-size: 11px; font-weight: 500; }

/* ⛔ 3. A barra de busca da Consulta ainda tinha 32 ao lado de 40: o §AI-1 alcançou `form.row .input`
      e `form.row .btn`, mas o ícone de busca vive num `.search-field` que é irmão, não filho. */
form.row .search-field, form.row .search-field .input { height: 40px; }


/* ---- AW-6 · A BARRA DE BUSCA, PELA TERCEIRA VEZ ----
   ⛔ MEDIDO: na Consulta o `input` continua em **32** e o botão em **40**, na mesma linha.
   ⭐⭐ E a causa é ARITMÉTICA DE ESPECIFICIDADE, que eu errei no §AI-1:
      · minha regra — `form.row .input:not(textarea)` → 1 elemento + 2 classes + `:not(textarea)`
        (que conta o argumento, mais 1 elemento) = **(0,2,2)**
      · a do C248 — `.content > .row .input:not(textarea)` → 3 classes + 1 elemento = **(0,3,1)**
      ⛔ A dela vence na coluna das CLASSES, que é comparada antes. Escrever depois não adianta.
   ⭐ `form.row.row` repete a classe e leva a (0,3,2) — vence sem depender de ordem, e é o mesmo
     truque do `:root:root`, do `.badge.badge` e do `.card.card:hover`. **Quarta vez neste lote.** */
form.row.row .input:not(textarea), form.row.row .select,
form.row.row .pdd__trig, form.row.row .dtc__trig, form.row.row .btn { height: 40px; }


/* ---- AW-7 · AS ÚLTIMAS SETE DE TEXTO PEQUENO ----
   ⚠️ Sobraram sete nós abaixo da lei, cada um com nome próprio — e o `small` de Projeções ainda
     a **10px**, porque alguma regra mais específica vencia o meu `small { font-size: 11px }`.
   ⭐ Aqui vai pelo ELEMENTO com classe repetida onde precisa, e o resto por nome da peça. */
small, small.small { font-size: 11px; line-height: 14px; font-weight: 500; }
.fech__sub { font-size: 11px; font-weight: 500; }
.mono.mono { font-weight: 500; }
.chat-cfg span, .chat-cfg .input { font-weight: 500; }
textarea.input.mono { font-weight: 500; }


/* ---- AW-8 · OS DOIS ÚLTIMOS, e cada um tem uma causa DIFERENTE ----
   ⛔ MEDIDO, não chutado: rodei um sonda que lê `getComputedStyle` nos dois nós e imprime PAI e AVÔ.

   · **`projecao small` a 10px** — o meu `small { font-size: 11px }` do AW-7 é **(0,0,1)** e perdeu
     para `.proj-band__l small` (app.css:883), que é **(0,1,1)**. ⭐ Não é "a regra não pegou": é
     aritmética de especificidade, a mesma armadilha de sempre. Empato o seletor e escrevo depois.
     ⚠️ E o mérito: 10px em CAIXA-ALTA com `letter-spacing` é exatamente o caso que a lei pega —
     é o mesmo defeito que eu já subi no `.stockcard__badge` (9px) e no `.trans-donut__center span`.

   · **`chat span` 12px/400** — aqui o tamanho está dentro da lei; o que falta é PESO. É o texto
     dentro de uma `.alertbar--warn` (o aviso "Código bruto: é inserido no site"). ⛔ Um aviso é
     justamente o texto que NÃO pode ficar fino — é o que o operador precisa ler antes de colar
     código de terceiro no site. */
.proj-band__l small { font-size: 11px; line-height: 14px; }
.alertbar span, .alertbar p { font-weight: 500; }


/* ---- AZ · OS TRÊS QUE PEDEM CONSERTO, NÃO FAXINA ----

   · **A caixa de erro transbordava.** O `ERRBOX` é `class="badge badge--red errbox"` — ele É um
     badge. E o §4.8 deste lote escreveu `.badge { height: 20px; padding: 0 8px }`, que é (0,1,0)
     e vem DEPOIS do `.errbox { padding: 8px }` (0,1,0). ⛔ Resultado: caixa de erro cravada em
     **20px**. Uma mensagem que quebre em duas linhas (30px) vaza por baixo da borda arredondada —
     e é o que acontece abaixo de 900px, onde a `.form-2col` empilha e a coluna fica estreita.
     ⚠️ Fui eu que criei isto ao padronizar o chip. ⭐ Caixa de erro NÃO tem altura fixa: ela
     cresce com o texto. Empate de especificidade pela classe repetida, e um PISO em vez de um teto.

   · **O campo Hora do calendário a 40px.** `.dtc__hora .input` é (0,2,0) e perdeu para o
     `.field.field .input` (0,3,0) que eu escrevi no AW-2 — o popover vive DENTRO de um `.field` e
     herdou a altura de campo de formulário. ⚠️ Mas a lei de altura é por CONTEXTO, e o contexto
     aqui é o cromo do próprio componente, ao lado de `.btn--sm` de 32 — não a linha do
     formulário. Empato de novo com a classe repetida. */
.errbox.errbox { height: auto; min-height: 32px; padding: 8px 12px; line-height: 16px; }
.dtc__hora .input.input { height: 32px; }

/* ---- AZ-2 · O RÓTULO DO INTERRUPTOR DE FILTRO: pela PEÇA, não pela POSIÇÃO ----
   ⛔ ACHADO (alto): `.card__head .sms-switch__lb` **nunca casa**. Os três interruptores de filtro
      ("mostrar inativos", "mostrar anulados") vivem num `<div class="row">` solto acima da tabela,
      e não num `.card__head`. ⚠️ Com a exceção morta, a base (14px / `--text-1`) valeu em toda
      parte: esses rótulos subiram de 12px apagado para 14px forte e passaram a competir com o
      título da tela — exatamente o "uma peça, dois pesos, zero exceção" que eu dizia estar fazendo.
   ⭐⭐ E a lição é a MESMA do §AI-1 e do AW-2, pela terceira vez neste lote: **escopar por
      POSIÇÃO quebra quando o markup se move; escopar por FUNÇÃO não.** Então o discreto vira uma
      variante declarada da peça (`toggle({ dim: true })`) e não um palpite sobre onde ela foi parar. */
.sms-switch--dim .sms-switch__lb { font-size: 12px; line-height: 16px; color: var(--text-3); }
/* ⛔ ACHADO ADVERSARIAL — o `<label>` do interruptor é `display:flex` e, num `.field` de
   coluna, estica pela largura toda: ~400px de fundo aparentemente inerte que ALTERNAM o
   interruptor ao clique. ⭐ A lei: a superfície que reage ao clique é a que PARECE clicável. */
.sms-switch--fit { align-self: flex-start; width: fit-content; }


/* ---- C250 §C · O QUE SOBRA DO ESTOQUE: só a BARRA, que é a única peça de verdade da tela ----
   ⭐ A barra de ocupação não existe em nenhum outro lugar do painel, então ela FICA — mas agora
     colorida pelo mesmo nome de estado do chip (`red`/`amber`/`green`), em vez de por uma classe
     `--critico`/`--baixo` que só o Estoque conhecia. ⛔ Um nome de estado, dois consumidores. */
.stockbar--green .stockbar__fill { background: var(--green); }
.stockbar--amber .stockbar__fill { background: var(--amber); }
.stockbar--red   .stockbar__fill { background: var(--red); }
/* o subtítulo do valor: é o único texto do cartão que o `.kpi` não tem slot próprio */
.estoque-sub { font-size: 12px; font-weight: 500; margin: -4px 0 0; }
/* ⚠️ o `.kpi__foot` tem `margin-top: auto` para colar no pé; com DOIS ou TRÊS rodapés empilhados,
   só o primeiro deve empurrar — senão eles se separam e o cartão estica. */
.kpi__foot + .kpi__foot { margin-top: 4px; }
/* a grade do estoque segue a mesma do KPI (auto-fit é legitimo aqui: a lista é HOMOGÊNEA) */
.stockgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; }


/* ---- C250 §D · UM `.btn` SEM VARIANTE NÃO PODE SER INVISÍVEL ----
   ⛔⛔ ACHADO (alto) da auditoria dos modais: existem **7 `class="btn"` nus** no app.js, todos nos
      diálogos da árvore de mídia — e um deles é o **"Apagar"** de "Apagar N arquivo(s)?".
   ⚠️ A conta: o `.btn` base declara caixa, altura e tipo, mas **nenhum `background` e nenhum
      `color`** — quem pinta é a variante. E o C247 ainda pôs `.btn { border: 0 }`, tirando até a
      borda transparente. Resultado medido: a ação **destrutiva e sem desfazer** saía como palavra
      solta, sem fundo e sem anel, ao lado de um "Cancelar" `--outline` que TEM anel. A ação
      irreversível era menos visível que a de desistir dela.
   ⭐⭐ Duas coisas, e as duas importam: as chamadas passam a declarar a variante (é a lei), **e**
      um `.btn` sem variante nenhuma passa a cair no visual de `--outline`. ⛔ A rede de segurança
      não é preguiça: é que o modo de falhar de "esqueci a variante" deve ser *um botão feio*, e
      nunca *nenhum botão*. ⚠️ `:not()` com lista mantém a especificidade em (0,1,0), então
      qualquer variante declarada continua vencendo por vir depois. */
.btn:not([class*="btn--"]) {
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--border-1);
  color: var(--text-2);
}
.btn:not([class*="btn--"]):hover {
  background: var(--surface-1);
  box-shadow: inset 0 0 0 1px var(--border-2);
  color: var(--text-1);
}


/* ---- C250 §D4 · `.btn--danger`: a variante que faltava, e eu quase repeti o defeito ----
   ⚠️ Eu ia marcar os dois botões "Apagar" dos diálogos da árvore como `btn--danger` — e só
     existia `.btn--danger-ghost`. ⛔ Pior: com a classe `btn--danger` presente, o seletor da rede
     de segurança (`.btn:not([class*="btn--"])`) DEIXA de casar, e o botão voltaria a ser invisível
     — o mesmo defeito, agora escondido atrás de um nome que parece certo.
   ⭐ Então a variante passa a existir de verdade. Ela é a irmã da `--primary` para ação
     **destrutiva e sem desfazer** (apagar arquivo, apagar pasta): preenchida, para pesar tanto
     quanto a ação pesa. ⛔ O texto é escuro sobre o vermelho, pelo mesmo motivo do botão da
     marca: vermelho claro com texto branco não passa contraste. */
.btn--danger { background: var(--red); color: #1a0a0a; }
.btn--danger:hover { background: color-mix(in srgb, var(--red) 84%, #fff); }


/* ==========================================================================================
   ⭐⭐ C250 §E · O MODAL DA BIBLIOTECA DE MÍDIA.
   ⛔ O que ele pediu duas vezes: *"tem que abrir um modal da tela de mídias, mais fácil de navegar
      e escolher a imagem."* Então o que a gaveta NÃO tinha é o que define esta peça: **espaço**
      (o modal `--wide`, no lugar de 240px espremidos no campo), **nome do arquivo** debaixo de cada
      miniatura, **busca** e **contagem**. Escolher entre trinta quadrados de 64px sem rótulo era
      advinhação — com nome e busca vira navegação.
   ⚠️ `auto-fill` aqui é legítimo: a lista é HOMOGÊNEA (são todas miniaturas do mesmo tamanho).
      É o caso oposto ao da Identidade, onde `auto-fit` espalhou buracos numa lista heterogênea. */
/* ⭐ §E4 · a migalha do modal usa a MESMA `.arv-migalha` da tela de Mídias — aqui só o respiro. */
.bib-topo { margin-bottom: 12px; }
.bib-busca { display: block; width: 100%; margin-bottom: 12px; }
/* ⭐⭐ §E3 · a LISTA, com a geometria da `.arv-linha` da tela de Mídias.
   ⛔ Eu tinha feito uma grade de cartões, e o retrato que ele mandou era uma LISTA. A razão é
      prática: numa grade o nome cabe na largura da miniatura, e `a-and-e-br.webp` vs `a-and-e.png`
      viram dois "a-and-e…" idênticos. Numa lista o nome tem a largura toda.
   ⭐ A linha é `<button>` (foco e Enter de graça) desenhado com a MESMA grade da `.arv-linha`:
      52px de miniatura · nome elástico · tamanho. ⚠️ Um `<button>` traz padrão de agente de
      usuário junto (fundo, borda, fonte, centralizacao) — por isso o `font: inherit` e o
      `text-align: left`, a mesma lição do `.arv-nome-btn`. */
.bib-lista { max-height: min(60vh, 560px); overflow-y: auto; }
.bib-linha {
  display: grid; grid-template-columns: 52px minmax(0, 1fr) auto;
  align-items: center; gap: 10px;
  width: 100%; padding: 8px 16px; margin: 0;
  background: transparent; border: 0; border-bottom: 1px solid var(--border-1);
  font: inherit; text-align: left; cursor: pointer;
  transition: background 150ms var(--ease);
}
.bib-linha:last-child { border-bottom: 0; }
.bib-linha:hover { background: var(--surface-1); }
.bib-linha:hover .bib-linha__nome { color: var(--brand-ink); }
.bib-linha:focus-visible { outline: 2px solid var(--ring); outline-offset: -2px; }
.bib-linha__nome {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 14px; color: var(--text-1);
}
.bib-linha__meta { font-size: 12px; white-space: nowrap; }
/* a pasta se lê como pasta: nome em peso maior e o ícone na cor da marca, igual à árvore. */
.bib-linha--pasta .bib-linha__nome { font-weight: 600; }
.bib-linha--pasta .arv-ico { color: var(--brand-ink); }
/* ⛔ imagem fora do catálogo no modo que exige id: aparece, explica, e não deixa clicar. */
/* ⭐ §H · a linha SELECIONADA. ⛔ Marca de seleção não pode ser só um fundo diferente: fundo
   sozinho some para quem não distingue as duas superfícies. Vai com fundo **e** com uma barra na
   borda esquerda, que é forma e não cor. ⚠️ A barra é `inset box-shadow` para não empurrar a
   grade — uma `border-left` de 3px deslocaria o conteúdo de todas as linhas marcadas. */
.bib-linha.is-sel { background: var(--brand-soft); box-shadow: inset 3px 0 0 0 var(--brand-ink); }
.bib-linha.is-sel .bib-linha__nome { color: var(--text-1); font-weight: 600; }
/* o rótulo do que está selecionado empurra os botões para a direita */
.bib-sel { margin-right: auto; font-size: 12px; font-weight: 500; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; align-self: center; }
.bib-linha[disabled] { cursor: not-allowed; opacity: .45; }
.bib-linha[disabled]:hover { background: transparent; }
.bib-linha[disabled]:hover .bib-linha__nome { color: var(--text-1); }


/* ==========================================================================================
   ⭐⭐⭐ C250 §F · TODO MODAL TEM PISO DE ALTURA.
   ⛔ Ele: *"todos os modais devem ter altura e largura mínima fixa, pq dependendo da quantidade
      de conteúdo que exibe, ele fica menor."*
   ⭐⭐ MAPEADO antes de mexer, abrindo CADA variante com pouco e com muito conteúdo (janela
      1500×950). O que o mapa mostrou, e mudou o conserto:

      | variante   | largura curto → longo | altura curto → longo |
      |------------|----------------------|----------------------|
      | (base)     | 440 → 440  (**0**)   | 149 → 733  (**+584**)|
      | --mid      | 540 → 540  (**0**)   | 149 → 733  (**+584**)|
      | --cms      | 620 → 620  (**0**)   | 149 → 733  (**+584**)|
      | --cms-lg   | 960 → 960  (**0**)   | 149 → 733  (**+584**)|
      | --wide     | 1080 → 1080 (**0**)  | 149 → 733  (**+584**)|

   ⭐ Ou seja: **a largura JÁ é fixa** — o `.modal__card` tem `width: 100%` com `max-width` por
     variante, e medido dá variação **zero** com o conteúdo. Pôr `min-width` ali não mudaria um
     pixel e só quebraria em tela estreita. ⛔ Quem varia é a ALTURA, e muito: de 149 a 733.
   ⭐⭐ Então o piso é de altura, proporcional à largura da variante — um diálogo de confirmar
     não precisa da altura de um formulário de CMS.
   ⚠️ O `min()` NÃO é enfeite: em CSS `min-height` **vence** `max-height`. Sem o clamp, um piso
     de 520px num celular deitado (≈360px de altura útil) faria o modal sair da tela e o rodapé
     ficar inalcançável. ⭐ O molde é o mesmo que o `--cms16` já usa desde o C16x. */
/* ⛔⛔ SEGUNDA PASSADA, e ele estava certo de novo: *"a altura ainda varia de acordo com o
   conteúdo que tem dentro."* — eu tinha posto **piso** (`min-height`), e piso só impede encolher.
   O cartão seguia CRESCENDO: medido, 240 com pouco conteúdo e 733 com muito.
   ⭐⭐ O que ele pediu é altura **fixa**: `height`. O `.modal__body` já é `flex: 1 1 auto` com
   `overflow-y: auto`, então o excedente ROLA dentro do corpo em vez de esticar o cartão — a peça
   já estava pronta desde o C241; faltava parar de deixar o cartão crescer.
   ⚠️ Os números sobem um pouco em relação ao piso: um PISO pode ser apertado, porque o conteúdo
   empurra; um TETO apertado corta. É a mesma medida com dois papéis diferentes.
   ⚠️ E o `min()` continua indispensável: sem ele, 600px fixos num celular deitado (≈360px de
   altura útil) fariam o rodapé sair da tela. */
/* ⚠️ OS NÚMEROS SÃO MEDIDOS, não escolhidos: para cada variante, a maior altura natural que ela
   precisa hospedar DEPOIS da reatribuição do §I. ⛔ A 1ª versão usou os números do PISO e espremeu
   tudo — um piso pode ser apertado porque o conteúdo empurra; um TETO apertado corta.
     base  → hospeda avisos e confirmações (260–300)
     --mid → novo link 482 · lançamento 502 · categorias 457 · confirmações 320–338
     --cms → cupom 466 · campanha 497/674 · plano 700 · usuário 837 · disparo 893/965
     --wide→ biblioteca 600
   ⭐ Onde o conteúdo passa do teto (usuário 837 e disparo 965 em `--cms` de 680), ele ROLA dentro
     do corpo — que é o comportamento projetado, e não um acidente. */
/* ⛔⛔ TERCEIRA VOLTA, e a decisão é dele: *"tirar altura mínima, deixar só no modal de upload de
   imagem, o da biblioteca; o restante não precisa de altura mínima, só altura máxima."*
   ⭐⭐ E ele tem razão sobre o resto: o retrato do "Fechar período" mostrou um aviso de três linhas
   dentro de uma caixa de 520px — mais de 300px de vazio embaixo de um texto curto. **Altura fixa
   faz sentido para um NAVEGADOR** (a biblioteca, onde a lista muda de tamanho a cada pasta e a
   caixa pular seria pior), e não para um diálogo, onde o tamanho É a informação de quanto se está
   pedindo ao operador.
   ⚠️ Então some o `height` de todo mundo. O que fica é o TETO, que já existia desde o C241
   (`max-height: calc(100dvh - 40px)` no `.modal__card`), e o corpo rola quando passa dele.
   ⭐ A biblioteca ganha classe própria em vez de herdar de `--wide`: o `--wide` também veste o
   detalhe do cliente e o perfil do indicador, e eles NÃO devem ter altura fixa. */
.modal__card--bib { height: min(640px, calc(100dvh - 40px)); }
/* ⛔ E aqui morre o SCROLL DENTRO DE SCROLL que ele viu: a `.bib-lista` tinha `max-height` e
   `overflow-y: auto` PRÓPRIOS, dentro de um `.modal__body` que também rola. Com o modal de altura
   fixa, quem rola é só o corpo — a lista cresce à vontade dentro dele. */
.modal__card--bib .bib-lista { max-height: none; overflow-y: visible; }
/* ☠ o escape do `--cms16` saiu junto: sem `height` na base, não há mais do que escapar. */


/* ---- C250 §J · O RÓTULO DO GATILHO NÃO QUEBRA LINHA ----
   ⛔ MEDIDO no modal de campanha: "Todos os planos" num gatilho de 140px virava **duas linhas**
      dentro dos 40px de altura — o texto espremido e cortado no meio.
   ⭐ Um CONTROLE não pode mudar de forma por causa do texto da opção escolhida: o rótulo corta com
      reticências, como já fazem a `.arv-nome` e a `.bib-linha__nome`. ⚠️ E a seta fica FORA do
      encolhimento (`flex: none`), senão ela é a primeira coisa que some quando o nome é longo — e
      a seta é o que diz que aquilo abre. */
.pdd__trig, .dtc__trig { white-space: nowrap; min-width: 0; }
.pdd__trig > span, .dtc__trig > span {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
.pdd__trig > svg, .dtc__trig > svg { flex: none; }


/* ==========================================================================================
   ⭐⭐⭐ C250 §O · `--text-4` DEIXA DE SER TOKEN DE TEXTO.
   ⛔ MEDIDO na régua complementar (a que cobre o que as 14 leis não cobriam), nos DOIS temas:

       token      escuro   claro    AA de texto (4.5:1)
       --text-1   16,29    16,97    passa
       --text-2   10,46     7,41    passa
       --text-3    5,22     5,04    passa
       --text-4    3,56     3,65    **REPROVA nos dois**

   ⚠️ E o `--text-4` estava pintando TEXTO em 21 lugares. A régua achou 5 nós falhando no escuro
      e 7 no claro — entre eles o **placeholder do gatilho de data** ("dd/mm/aaaa"), que é o único
      texto daquele controle quando ele está vazio.
   ⛔ Por que NÃO subi o valor do token: a escala tem quatro degraus (16 → 10 → 5 → 3,6). Subir o
      quarto para 4,5 o cola no terceiro (5,04) e a hierarquia vira três degraus com dois nomes.
   ⭐⭐ A resposta certa é dizer o que ele É: o **nível NÃO-TEXTO**. O WCAG pede 3:1 para
      componente não-textual (ícone, separador, contorno) — e aí 3,56/3,65 passa. Então ele fica
      para ícone decorativo, separador de migalha, alça de arrastar e estado desabilitado; e todo
      consumidor que pinta TEXTO LEGÍVEL sobe para `--text-3`.
   ⚠️ Anular exige EMPATAR a especificidade, então cada regra abaixo repete o seletor original. */

/* ---- os que pintam TEXTO e sobem para --text-3 ---- */
.dtc__trig .dim { color: var(--text-3); }        /* o "dd/mm/aaaa" do gatilho vazio */
.kpi__nota { color: var(--text-3); }             /* a nota do KPI (11px) */
.input::placeholder { color: var(--text-3); }    /* placeholder É texto */
.cohort-cell--empty { color: var(--text-3); }
.cms-addmenu__h { color: var(--text-3); }
.media__ph { color: var(--text-3); }
.bld-d, .bld-w, .bld-zoom, .bld-add { color: var(--text-3); }
.bld-row--grp .bld-n { color: var(--text-3); }
.bld-pop__x { color: var(--text-3); }            /* é um botão: precisa ser legível */

/* ⛔ os que FICAM em --text-4, e o motivo de cada um:
     `.arv-sep`      — a barra "/" da migalha: separador, não texto
     `.arv-ico-falha`, `.state svg`  — ícone
     `.lh-grip`, `.cms-grip`, `.bld-grip`, `.bld-gi` — alça de arrastar (ícone)
     `.vivo-dot`     — é `background`, não cor de texto
     `.dtc__d--fora` — dia de fora do mês: `pointer-events: none` e `opacity: .45`, ou seja,
                       deliberadamente apagado e NÃO acionável */

/* ---- §4.10 · o login: malha pontilhada sobre preto ----
   ⛔ O <form> não muda: method, action, _csrf, name=, label, autofocus, required — só classe e CSS. */
/* ⚠️ MEDIDO: o login não tem classe própria — é `<body>` + um `<form class="card">` com o tamanho
   já inline (`max-width:360px;padding:36px 28px`, login.php:7), exatamente o que o lote pede.
   ⭐ Então basta a malha no body, por um atributo de página; o card herda o anel do `.card` acima. */
body[data-pagina="login"] { background-color: #000;
  background-image: radial-gradient(rgba(255,255,255,.06) 1px, transparent 1px);
  background-size: 24px 24px; }

/* ---- AL4 · O BOTÃO SOLTO NO CORPO NÃO ESTICA ----
   ⛔ ACHADO DA VERIFICAÇÃO: em `#/popup` o "Salvar pop-up" é filho DIRETO do `.form-wrap` (sem
     `.row` em volta, ao contrário das outras quatro telas) — e a minha regra `> :not(.field)`
     dava a ele `grid-column: 1/-1`, esticando o botão primário de ponta a ponta (~1160px).
   ⭐ Ele continua atravessando a linha (é ação, tem de ficar sozinho), mas **não estica**:
     `justify-self: end` o encosta à direita com a largura do próprio rótulo. */
[data-forma="formulario"] .card > .card__body > .btn,
[data-forma="formulario"] .form-wrap > .btn,
[data-forma="formulario"] .field-stack > .btn { justify-self: end; width: auto; }

/* ---- AL5 · O `@media` DE 900px QUE NUNCA DISPAROU ----
   ⛔ ACHADO: `@media (max-width:900px) { .form-2col { ... } }` é (0,1,0) e perdia para a regra
     genérica `[data-forma="formulario"] .card > .card__body` (0,3,0), que está FORA de media query.
     ⚠️ Entre 761 e 900px a Identidade ficava em duas colunas de ~340px com vale de 48 — o trio
     Cor/Texto/Fonte espremido. ⭐ A media query passa a ter a MESMA cadeia, e aí vence. */
@media (max-width: 900px) {
  [data-forma="formulario"] .card > .card__body.form-2col,
  .form-2col { grid-template-columns: minmax(0, 1fr); gap: 20px; }
}

/* flixzera · Ao vivo (grupo App): um card por aparelho conectado. */
.fz-sessoes { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 16px; }
.fz-sessao { padding: 18px; display: flex; flex-direction: column; gap: 6px; }
.fz-sessao__topo { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
.fz-sessao__email { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: inherit; }
.fz-sessao__agora { margin: 4px 0 0; font-weight: 500; }
