/* ═══════════════════════════════════════════════════════════════════════════════
   50-paineis-e-formularios.css — o CORPO das abas que não são tabela, folha
   nem conversa. Carrega DEPOIS de styles.css, contratos.css e das peças 00–40:
   em empate de especificidade vence a última declaração, e é por isso que quase
   nada aqui precisa igualar seletor (onde precisa, está comentado) e NADA aqui
   usa !important.

   COBRE (e é a ÚNICA peça que declara estes seletores, por contrato)
     Dashboard ...... .grid2 / .grid2.even / .grid2>* · .kpis / .kpi · .months /
                      .months .mcol · .canais-wrap
     Filtros ........ #view-leads .filters · #view-pipeline .filters (layout;
                      a APARÊNCIA e o piso de 16px/44px dos campos é da peça 00)
     Agora .......... .qcard / .qc-mid / .qc-name / .qc-why / .qc-side /
                      .qc-wa / .qmore / .qband-head / .qband-hint
     Pipeline ....... .kanban · .kcol · .kbody · .kcard · .kcol > .loadmore ·
                      #view-pipeline > .sub · #view-pipeline .filters::before
     Agenda ......... .ag-controls · .ag-nav · .ag-grid · .ag-cell · .ag-daynum ·
                      .ag-chip · .ag-legend
     Vendas ......... #view-nfse .nf-form (+ campos e botões dela) ·
                      #view-nfse div.nf-acoes · #view-nfse div.nf-resumo ·
                      #view-nfse .field > span · .nf-outro · .nf-comis ·
                      .parc-bloco / .parc-previa / .parc-barra / .parc-receber ·
                      #vNova
     Contratos ...... .ct-barra · .ct-form · .ct-secao · .ct-grade / .ct-largo ·
                      .ct-lead · .ct-sugestoes / .ct-sug · .ct-previa /
                      .ct-previa-rot / .ct-falta / .ct-pronto · .ct-botoes ·
                      .ct-ancora · #ctFormBox · #ctCepNota
     Campanha/E-mail  .mode-cards · .camp-msg-grid (conferidos: sem regra) ·
                      #view-email .prev-body · .mkpis (conferido: sem regra)

   NÃO COBRE (donos vizinhos — nem uma linha destes aqui)
     header.top · .brand · .search · .who · .icon-btn · .menu* · nav.tabs ·
     .tab-badge · #tabsMais · main · thead th · .khead ................. peça 10
     .drawer · .scrim · .dhead · .dbody · .icon-close · .meta-grid ·
     .contact-row · .cbtn · .brief-thread · .ag-slot* · .nf-lead-form ·
     .tag · .pr-tab · .ob · .call-resp ................................. peça 20
     .tablewrap · table · tbody · tr · td · td[data-lab] · td.nf-acoes ·
     td.ct-acoes · .cell-qa · .qa · .pager · .cell-name ................ peça 30
     .chat-* (inclusive a altura do .chat-wrap) ........................ peça 40
     tokens --m-* · piso de 16px e 44px dos campos/primitivos (.chip-btn,
     .newlead-btn, .data-save, .lead-delete, .toggle, #canaisRecalc,
     select.f/input.f) · .toast-wrap · html/body · .kcard:hover ·
     .qcard:hover · tbody tr:hover ..................................... peça 00

   FUROS APLICADOS
     [11] `.nf-acoes` e `.nf-resumo` têm DOIS papéis no DOM (célula de tabela e
          bloco de painel). Aqui SÓ aparecem como `#view-nfse div.nf-acoes` e
          `#view-nfse div.nf-resumo` — nunca a classe nua. `div.` é o que torna
          a regra incapaz de casar com o `<td class="nf-acoes">` de app.js:3869
          e com o `<td class="ct-acoes">` de contratos.js:120, que são da peça 30.
     [12] + [15] o "➕ Nova venda" NÃO é `position:fixed` (cobriria 2 dos 6 slots
          da barra inferior, inclusive o "Mais", e saltaria 250ms a cada entrada
          na aba por causa do `transform` de `@keyframes fade`, styles.css:85) e
          NÃO é `position:sticky` (ver o comentário longo na seção 6.3).
     [19] `.qc-wa` (38×38), `.qc-wa:hover`, `.qmore` (~34px) passam a ser desta
          peça. O `@media (hover:none)` da peça 00 não os cobre de propósito
          ("Os órfãos (.qc-wa:hover, .qmore:hover) são pedido à peça 50"),
          então declaro aqui o meu próprio
          `@media (max-width:780px) and (hover:none)` no fim do arquivo.
      [2] `.parc-receber` ganha alvo, mas o furo [2] está certo: sem `td` (peça
          30) não existe seletor meu que traga o botão para dentro da tela.
          Está em pedidos_a_outras_pecas e em defeitos_nao_resolvidos.

   REGRA DE CAMADA VERTICAL (peça 10, furo [15]): a barra inferior é opaca e
   ocupa sempre os últimos calc(var(--m-nav-h) + var(--m-safe-b)) pixels. Todo
   rodapé desta peça ancora em bottom:calc(var(--m-nav-h) + var(--m-safe-b) + N).
   Vale para o único sticky-bottom daqui: `.ct-form .ct-botoes`.

   NADA fora de @media (max-width:780px). Zero !important. Zero JS.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ⚠️ A CONDICAO DA CAMADA MOBILE, identica nos 6 arquivos:
     largura ate 780px  OU  (altura ate 520px E ponteiro grosso)
   O segundo ramo existe porque celular DEITADO nao cabe no primeiro: um iPhone 12/13/14
   em paisagem mede 844px de largura (Pro Max, 926) e caia FORA da camada — recebia o
   layout de desktop numa tela de 375px de altura, com estouro horizontal de 45px
   (medido). `max-height:520px` pega paisagem de celular (375-428px de altura) e nao
   pega iPad deitado (768). `pointer:coarse` garante que nenhuma janela de desktop
   encolhida na vertical entre aqui. */
@media (max-width:780px), (max-height:520px) and (pointer:coarse){

/* ╔══════════════════════════════════════════════════════════════════════════╗
   ║ 1 · DASHBOARD                                                            ║
   ╚══════════════════════════════════════════════════════════════════════════╝ */

/* ── 1.1 os dois `.grid2 even` (index.html:93 e :97) nunca colapsavam ──────
   DESFAZ styles.css:96 (`.grid2.even{grid-template-columns:1fr 1fr}`), que está
   FORA de media query e tem especificidade (0,2,0) — maior que o
   `@media (max-width:1100px){.grid2{1fr}}` de styles.css:307, que é (0,1,0),
   porque MEDIA QUERY NÃO SOMA ESPECIFICIDADE. Igualo o seletor em vez de usar
   !important: (0,2,0) contra (0,2,0), e este arquivo carrega depois.
   Conta medida (DOSSIÊ/EVIDENCIA §8): 272 + 874 = 1146 = o scrollWidth do
   documento no Dashboard. Este painel é 685 dos 771px de rolagem horizontal. */
  .grid2,
  .grid2.even{grid-template-columns:1fr}

/* ⚠️ LOAD-BEARING. `1fr` é `minmax(auto,1fr)`, e o mínimo `auto` é o MIN-CONTENT
   do item. Sem `min-width:0` a trilha única volta a estourar pelo min-content do
   painel: `#canaisTabela` mede 874px (tabela com white-space:nowrap em toda
   célula, styles.css:786) e `#chartMonths` 519px. Com esta linha o painel passa
   a caber em 347px e a rolagem vai para dentro dele (1.2 e 1.4). */
  .grid2 > *{min-width:0}

/* ── 1.2 `#chartMonths` (index.html:95) não tem a rolagem que o `#chartCaptures`
   ganhou à mão no HTML (`style="overflow-x:auto"`, index.html:115). Declaro em
   `.months`, que é a classe dos dois, para o conserto não depender de qual id é.
   Sem 1.2 o conserto 1.1 TROCA um estouro por outro: com o painel em 347px os
   484px de `.mcol` passariam a vazar visivelmente para fora da borda. */
  .months{
    overflow-x:auto;
    overscroll-behavior-x:contain;   /* não vira gesto de "voltar" do navegador */
    scrollbar-width:thin;
    height:150px;                    /* era 170px (styles.css:117) */
    padding-bottom:4px;              /* faixa da barra de rolagem */
  }
/* ⚠️ LOAD-BEARING para 1.2. `.mcol{flex:1}` (styles.css:118) sem base fixa deixa
   as 12 colunas ENCOLHEREM para caber, e aí `overflow-x:auto` nunca rola: em vez
   de rolagem aparecem 12 lascas de ~26px com o rótulo "set/26" (10,5px, nowrap,
   styles.css:121) cortado. `flex:1 0 34px` = pode crescer quando há poucos
   meses, nunca encolhe abaixo de 34px → 12×34 + 11×8 = 496px > 309px de painel,
   então a rolagem interna existe de verdade. */
  .months .mcol{flex:1 0 34px}

/* ── 1.3 3 KPIs de 347px empilhados empurram os gráficos pra fora da 1ª tela ──
   DESFAZ styles.css:89 (`repeat(auto-fit,minmax(180px,1fr))`): em 347px
   2×180=360 > 347, então auto-fit resolvia para UMA coluna e os 3 KPIs
   (app.js:457-460: Total de leads, Clientes, No grupo de avisos) somavam
   ~330px + 28 de gap antes do primeiro gráfico.
   `minmax(0,1fr)` e não `minmax(auto,1fr)`: o `.kpi .val` é 27px e o `.delta`
   tem texto ("12,4% de conversão") — min-content ali estoura a trilha. */
  .kpis{
    grid-template-columns:repeat(2,minmax(0,1fr));
    gap:10px;
    margin-bottom:14px;
  }
  .kpi{
    padding:12px 12px 12px 13px;
    min-width:0;
    overflow:hidden;                 /* já é overflow:hidden em styles.css:90 */
  }
  .kpi .label{font-size:11.5px;padding-right:26px} /* não passar debaixo do .ic */
  .kpi .val{font-size:21px;margin-top:6px}
  .kpi .delta{font-size:11px;margin-top:5px}
/* o ícone é decoração: encolhe e sobe para liberar a linha do rótulo */
  .kpi .ic{width:24px;height:24px;border-radius:7px;top:10px;right:10px;font-size:12px}

/* ── 1.4 "Canais por mês": a rolagem interna que `.canais-wrap` já tem
   (styles.css:784) só começa a funcionar depois de 1.1. O que falta é o AVISO
   de que rola — sem borda, a tabela parecia simplesmente cortada.
   NÃO declaro `table.canais` (styles.css:785-791): `table` é da peça 30. */
  .canais-wrap{
    overscroll-behavior-x:contain;
    scrollbar-width:thin;
    border:1px solid var(--border);
    border-radius:var(--m-r-sm);
    padding-bottom:2px;
  }


/* ╔══════════════════════════════════════════════════════════════════════════╗
   ║ 2 · BARRA DE FILTROS (Leads e Pipeline) — o paliativo autorizado         ║
   ╚══════════════════════════════════════════════════════════════════════════╝
   Escopo assumido por herança: a peça 00 escreveu, no próprio arquivo dela,
   "A outra metade (a `.filters` em grade de 2 colunas) não está em escopo
   nenhum: está no relatório como pedido". A metade dela (`select.f,input.f{
   max-width:100%;min-width:0;text-overflow:ellipsis}`) já matou o estouro
   horizontal do `#fOrigem`, cuja opção mais longa — "Captura Hotmart + Grupo de
   Avisos + Curso Hotmart" — é mais larga que a tela. Aqui entra só LAYOUT
   (flex-basis e min-width): nenhum font-size, nenhum min-height, nenhum
   padding, nenhuma cor — essas propriedades continuam sendo da peça 00.
   Escopado em #view-leads e #view-pipeline de propósito: a `.filters` do
   Dashboard (index.html:103) tem 3 controles e não precisa de nada. */

  #view-leads .filters,
  #view-pipeline .filters{gap:8px;margin-bottom:10px}

/* 2 controles por fileira. `calc(50% - 4px)` = metade menos meio gap de 8px:
   com `flex-wrap:wrap` (styles.css:147) dá exatamente 2 por linha e nunca 3. */
  #view-leads .filters > select.f,
  #view-leads .filters > .toggle,
  #view-pipeline .filters > select.f,
  #view-pipeline .filters > .toggle,
  #view-pipeline .filters > input.f{      /* #kSearch, index.html:190 */
    flex:1 1 calc(50% - 4px);
    min-width:0;
  }
/* a contagem (".count-pill", index.html:175 e 191) carrega `margin-left:auto`
   de styles.css:152: sem isto ela e empurrada sozinha pra direita da fileira em
   que cair e desalinha a grade de 2 colunas. Fileira propria, alinhada a direita. */
  #view-leads .filters > .count-pill,
  #view-pipeline .filters > .count-pill{
    flex:1 1 100%;
    margin-left:0;
    text-align:right;
  }
/* o "+ Novo lead" da barra (index.html:171) ganha fileira própria: é a ação, não
   um filtro, e é o que a pessoa procura quando o lead ainda não existe. */
  #view-leads .filters > #newLeadBtn2{flex:1 1 100%}

/* ── 2.1 Pipeline: a tela PARA DE MENTIR ──────────────────────────────────────
   index.html:174 diz "Arraste os cards entre as colunas para atualizar o
   estágio", e bindDnD (app.js:1240-1258) só registra dragstart/dragend/dragover/
   dragleave/drop — HTML5 drag-and-drop não é sintetizado a partir de toque no
   Safari do iOS nem no Chrome do Android, e não existe um único touchstart/
   pointerdown para o kanban no arquivo. O toque cai no handler de clique de
   app.js:1208 e abre a FICHA, onde o `<select id="dStage">` (app.js:1434) muda
   o estágio de verdade.
   `display:none` no `.sub` (e não `font-size:0`) porque font-size:0 deixa o
   texto mentiroso na árvore de acessibilidade: o leitor de tela leria os dois.
   O texto novo entra como `::before` da `.filters`, que é `display:flex;
   flex-wrap:wrap` — um pseudo com `flex:1 1 100%` vira a primeira fileira.
   O texto do HTML continua pedido em pedidos_a_outras_pecas: CSS conserta a
   tela, mas o arquivo continua com a frase errada. */
  /* index.html:185 já traz as DUAS frases, e o `Salva para todo o time.` fora das
     duas — então aqui só se troca qual aparece. Esconder o `.sub` inteiro e reescrever
     num `::before` apagaria essa terceira frase e duplicaria texto para leitor de tela.
     `[hidden]` é regra do navegador (origem UA): qualquer `display` de autor a vence. */
  /* ⚠️ a troca de frase segue o PONTEIRO, nao a largura: o toque longo de mobile.js
     descarta `pointerType === "mouse"`, entao numa janela estreita de desktop o
     "segure para mover" seria mentira e o "arraste" — que e o que funciona ali —
     estaria escondido. */
  @media (hover:none){
    #view-pipeline > .sub > .so-mouse{display:none}
    #view-pipeline > .sub > .so-toque[hidden]{display:inline}
  }


/* ╔══════════════════════════════════════════════════════════════════════════╗
   ║ 3 · AGORA — a fila do dia (a aba nº 1 do celular)                        ║
   ╚══════════════════════════════════════════════════════════════════════════╝ */

/* ── 3.1 o cartão vira duas fileiras ─────────────────────────────────────────
   Espaço real hoje: `.qcard{padding:11px 14px;gap:12px}` (styles.css:549) +
   `.av{flex:0 0 40px}` (551) + `.qc-wa{38px}` (556) deixam
   347−28−40−38−24 = 217px para o `.qc-mid` — divididos entre nome e "por quê".
   Com `flex-wrap:wrap` o `.qc-side` desce para a fileira 2 e o `.qc-mid` fica
   com os mesmos 217px, agora em 2 linhas. */
  .qcard{
    flex-wrap:wrap;
    align-items:center;
    row-gap:8px;
    padding:12px 14px;
  }
  /* ⚠️ base 0, nunca `auto`. O flex quebra linha pelo *hypothetical main size*,
     que com `flex-basis:auto` é o MAX-CONTENT do item — e `min-width:0` não entra
     nessa conta. Como a 3.2 logo abaixo tira o `nowrap` do `.qc-why`, o max-content
     passa a ser o texto inteiro numa linha (~430px em "Comprou o curso há 45d e já
     conversou com você — convide pra mentoria", app.js:2683) contra ~319px de miolo:
     o cartão explodia em 4 fileiras em vez das 2 que esta seção desenha. */
  .qcard .qc-mid{flex:1 1 0;min-width:0}
  .qcard .qc-name{font-size:15px;line-height:1.3}

/* ── 3.2 o "por quê" deixa de ser cortado em uma linha ───────────────────────
   DESFAZ styles.css:554 (`.qc-why{white-space:nowrap;overflow:hidden;
   text-overflow:ellipsis}`). Os textos que classifyLead gera passam MUITO de
   217px: "Comprou o curso há 45d e já conversou com você — convide pra
   mentoria" (app.js:2683) tem ~430px a 12,5px; "Call feita há 12d — faça o
   follow-up" (2658) e "Retorno prometido venceu há 12d" (2647) também. O que
   sobrava na tela era o nome e um "…" no lugar da instrução.
   2 linhas cobrem todos os textos de classifyLead; o clamp mantém o teto de
   altura do cartão previsível numa fila de até 25 por faixa (CAP, app.js:2756). */
  .qcard .qc-why{
    white-space:normal;
    display:-webkit-box;
    -webkit-line-clamp:2;
    -webkit-box-orient:vertical;
    overflow:hidden;
    font-size:13px;
    line-height:1.35;
    margin-top:3px;
  }

/* ── 3.3 `.qc-side` volta — é FATO, não instrução ────────────────────────────
   DESFAZ styles.css:596 (`@media(max-width:780px){.qc-side{display:none}}`),
   (0,1,0) contra (0,2,0) aqui. Dentro dele mora headBadges(l) (app.js:2731 →
   206-208): temperatura + ESTÁGIO + "📚 Aluno do curso" (`aluno = !!l.cur &&
   !isMentoriaLead(l)`). Nas faixas "🔥 Sinal de compra", "🆕 Novos" e
   "🌡️ Quentes parados" o rótulo da faixa não diz nada de compra anterior nem de
   estágio: escondido o `.qc-side`, o vendedor abre o WhatsApp sem saber se fala
   com quem nunca comprou ou com aluno do curso — e a abordagem é outra.
   O motivo original do display:none está no próprio CSS (`justify-content:
   flex-end;max-width:190px`, styles.css:555): em fileira própria os dois
   sumiram. */
  .qcard .qc-side{
    display:flex;
    /* ⚠️ `order` LOAD-BEARING: no DOM o `.qc-wa` vem DEPOIS do `.qc-side`
       (app.js:2725-2733). Sem isto o `flex:1 1 100%` daqui consome a fileira 2
       inteira e empurra o botão de WhatsApp — o controle mais tocado da aba — para
       uma 3ª fileira sozinho, encostado na borda esquerda. */
    order:1;
    flex:1 1 100%;
    max-width:none;
    justify-content:flex-start;
    gap:6px;
    margin-top:0;
  }

/* ── 3.4 `.qband-hint` CONTINUA escondido, e fica registrado o que sai ───────
   styles.css:597 esconde a 3ª coluna do `.qband-head` (app.js:2758): o campo
   `hint` de BANDS (app.js:2691-2698) — "pediram preço/inscrição — ligue agora",
   "combinado é combinado — dê o toque prometido", "velocidade no 1º toque é o
   que mais fecha". É COPY DE ORIENTAÇÃO, não dado do lead: o rótulo da faixa
   (`.qband-title`) e a contagem (`.qband-n`) continuam à vista, e o "por quê"
   específico de cada lead voltou inteiro no 3.2. `.qband-hint{flex:1;min-width:0;
   white-space:nowrap}` (styles.css:547) em 347px viraria um "…" de 2 caracteres
   ao lado do título — devolver custaria altura e não devolveria fato nenhum.
   Nenhuma regra aqui, de propósito. A altura economizada foi gasta em 3.2 e 3.3.
   Só o cabeçalho ganha respiro, porque agora ele é título + contagem e nada mais: */
  .qband-head{gap:8px;margin-bottom:8px;flex-wrap:wrap}

/* ── 3.5 o botão de WhatsApp do cartão: 38 → 44px (FURO [19]) ────────────────
   DESFAZ styles.css:556 (`.qc-wa{width:38px;height:38px}`), (0,1,0) contra
   (0,2,0) aqui. É o controle mais tocado da aba nº 1 e o toque final da tarefa
   "abrir o WhatsApp do lead certo" — ele também registra o toque no servidor
   (app.js:2767-2772), então errar o alvo custa um registro perdido.
   `flex:0 0 var(--m-tap)` iguala a base de styles.css:556 (`flex:0 0 auto`) e
   garante que o botão não encolha quando o nome do lead é longo. */
  .qcard .qc-wa{
    flex:0 0 var(--m-tap);
    width:var(--m-tap);
    height:var(--m-tap);
    border-radius:var(--m-r-sm);
  }

/* ── 3.6 `.qmore`: 34 → 44px (FURO [19]) ────────────────────────────────────
   DESFAZ styles.css:599 (`padding:9px;font-size:12.5px`). Seletor NU de
   propósito: a mesma classe é o "+N leads nesta faixa" do Agora (app.js:2760),
   o "testar número" do diagnóstico de WhatsApp (682), o link "conectar
   WhatsApp" (814) e o "Ver pares" do placar do copiloto (932). Os quatro são
   botão/link de verdade e os quatro estavam abaixo de 44px. Nenhuma outra peça
   declara `.qmore` (conferido nos 5 arquivos de public/mobile/). */
  .qmore{
    min-height:var(--m-tap);
    display:flex;
    align-items:center;
    justify-content:center;
    font-size:14px;
    padding:10px 12px;
    border-radius:var(--m-r-sm);
  }


/* ╔══════════════════════════════════════════════════════════════════════════╗
   ║ 4 · PIPELINE — o quadro passa a andar PRO LADO, não pra baixo             ║
   ╚══════════════════════════════════════════════════════════════════════════╝
   A CONTA, medida:
   · Hoje styles.css:310 faz `.kanban{grid-template-columns:1fr}`, então as 5
     colunas de STAGES (app.js:16-20: Novo, Em contato, Call realizada,
     Negociando, Cliente) viram 5 BLOCOS empilhados. O limite por coluna é o de
     desktop (`kanbanLimit[s.key] || 50`, app.js:1200) e cada `.kcard` mede ~92px
     (padding 11 + .kc-top + .kc-meta{margin-top:9} + .kc-owner{margin-top:6},
     styles.css:194-202) dentro de `.kbody{padding:10px;gap:9px}` (193):
     uma coluna cheia = 50×92 + 49×9 + 20 = 5.061px.
     5 colunas cheias = 25.305px; o medido com a base real foi 18.415px, e
     chegar em "Negociando" (4ª de 5) custava ~15.000px de rolagem.
   · Com esta seção: as 5 colunas viram uma faixa horizontal com encaixe. A
     página passa a ter a altura da coluna MAIS ALTA (5.061px no pior caso, e é
     honesto: é uma lista de 50 cards), e trocar de estágio custa UM deslize
     lateral com a página no topo — zero rolagem vertical. 15.000px → 0px.
   POR QUE NÃO "teto de altura com rolagem interna por coluna": 5 roladores
   verticais aninhados dentro do rolador da página. Para passar de uma coluna de
   50 cards a pessoa tem que ESGOTAR a rolagem dela (~14 deslizes) antes de o
   gesto chegar na página. Troca 15.000px de rolagem por uma armadilha de
   rolagem, que é pior. A faixa horizontal é o padrão que o celular já ensina
   (é como todo quadro kanban de telefone se comporta) e não aninha rolagem
   vertical nenhuma.
   POR QUE NÃO "colunas colapsáveis": exigiria um alvo de toque que só existe
   dentro da `.khead`, que é da peça 10, e um estado que só JS mantém. */

  .kanban{
    display:flex;
    flex-wrap:nowrap;
    align-items:flex-start;
    gap:10px;
    overflow-x:auto;
    overscroll-behavior-x:contain;   /* não vira o gesto de "voltar" do navegador */
    scroll-snap-type:x proximity;
    scrollbar-width:thin;
    padding-bottom:6px;
  }
/* 86% de 347 = ~298px: a coluna atual cabe inteira e sobram ~14% da seguinte
   aparecendo na borda — é a dica visual de que há mais estágio pro lado.
   `flex:0 0 86%` DESFAZ o comportamento de item de grade de styles.css:186. */
  .kanban > .kcol{
    flex:0 0 86%;
    min-width:0;
    scroll-snap-align:start;
  }
  .kbody{padding:10px 8px;gap:8px}

/* o card é um ALVO DE TOQUE (abre a ficha, app.js:1208), não uma alça de
   arraste. DESFAZ styles.css:194 (`cursor:grab`) e 196 (`:active{cursor:grabbing}`),
   que prometiam arraste. `-webkit-user-drag:none` + `-webkit-touch-callout:none`
   matam o fantasma de arraste e o menu de contexto que o `draggable="true"` de
   app.js:1234 provoca no toque longo — gesto que hoje não faz absolutamente
   nada. `user-select` fica de fora: o nome do lead continua copiável.
   O `:hover` do .kcard é da peça 00 (já neutralizado lá). */
  .kcard{
    cursor:pointer;
    -webkit-user-drag:none;
    -webkit-touch-callout:none;
    padding:12px 12px;
  }
  .kcard:active{cursor:pointer}
  .kcard .kc-top .nm{font-size:13.5px}

/* o "+ N cards" que soma 50 (app.js:1207) tinha ~33px (`padding:8px`,
   styles.css:203). É ele que revela o resto do estágio. */
  .kcol > .loadmore{
    min-height:var(--m-tap);
    font-size:13.5px;
    margin:2px 8px 10px;
    width:calc(100% - 16px);
  }


/* ╔══════════════════════════════════════════════════════════════════════════╗
   ║ 5 · AGENDA — a grade do MÊS                                              ║
   ╚══════════════════════════════════════════════════════════════════════════╝
   Os horários do dia (`.ag-slot*`) são da peça 20: vivem dentro do #drawer,
   escritos por openDay (app.js:2906-2932). Aqui é só a grade e a navegação. */

/* ── 5.1 trocar de mês em alvos de 44px ─────────────────────────────────────
   DESFAZ styles.css:505 (`.ag-nav button{width:32px;height:32px}`) — os ‹ e ›
   de index.html:198, com handlers em app.js:2979-2980. 32×32 contra o mínimo de
   44×44 (iOS HIG). A fileira inteira passa a ocupar a largura e o nome do mês
   fica no centro, entre os dois alvos: 44 + 10 + flex + 10 + 44 em 347px. */
  .ag-controls{width:100%;gap:8px}
/* DESFAZ styles.css:503 (`.ag-controls .f{min-width:180px}`): o `#agVendor` do
   admin (index.html:197) ganha a linha inteira em vez de 180px travados.
   Só largura — altura e fonte do select são da peça 00. */
  .ag-controls .f{min-width:0;width:100%}
  .ag-nav{width:100%;gap:10px;justify-content:space-between}
  .ag-nav button{
    width:var(--m-tap);
    height:var(--m-tap);
    flex:0 0 var(--m-tap);
    font-size:20px;
    border-radius:var(--m-r-sm);
  }
/* DESFAZ styles.css:507 (`.ag-nav b{min-width:150px}`): em vez de 150px fixos,
   ocupa o que sobra entre os dois botões. */
  .ag-nav b{min-width:0;flex:1 1 auto;font-size:15px}
/* ── 5.1b (SPEC-MEET-6) "+ Marcar nova call": o caminho bem visível que o dono pediu — a linha inteira, primeiro da
   fileira, em alvo de 48px (index.html #agNovaCall; abre a janela da peça 20, secao 11). */
  .ag-controls .ag-nova{width:100%;min-height:var(--m-tap-lg);font-size:15px;order:-1}

/* ── 5.2 a célula do dia: 40 → ~44px de largura ─────────────────────────────
   Conta: `.ag-grid{grid-template-columns:repeat(7,1fr);gap:6px;padding:12px}`
   (styles.css:511) em 347px dá (347 − 2 de borda − 24 de padding − 36 de gaps)/7
   = 40,7px. Com padding 8 e gap 4: (347 − 2 − 16 − 24)/7 = 43,6px.
   `minmax(0,1fr)` porque `1fr` = `minmax(auto,1fr)` e o `.ag-chip` de 5.3 tem
   min-content próprio — sem isto a grade de 7 colunas estouraria a tela. */
  .ag-grid{
    grid-template-columns:repeat(7,minmax(0,1fr));
    gap:4px;
    padding:8px;
    border-radius:var(--m-r);
  }
/* DESFAZ o `min-height:54px` de styles.css:533: 54px cabia o número do dia + o
   badge de contagem, mas não o chip de status que volta em 5.3.
   Conta: .ag-daynum 16 + .ag-chip 15 + badge 16 + 2 gaps de 3 = 53 + padding. */
  /* A mensagem de estado vazio (app.js:2884 e :2889) e um `<div class="muted">` filho
     DIRETO do `#agGrid`, que e `grid-template-columns:repeat(7,1fr)` (styles.css:511):
     sem `grid-column`, ela cai numa celula de 1/7 da largura — no celular sao ~45px, e o
     texto sai UMA PALAVRA POR LINHA. Vale para o "Cadastre um vendedor no time" e para a
     mensagem de erro de carga. */
  #agGrid > .muted{
    grid-column:1 / -1;
    padding:28px 16px;
    line-height:1.5;
  }

  .ag-cell{
    min-height:60px;
    padding:4px 3px;
    gap:3px;
    border-radius:8px;
  }
  .ag-daynum{font-size:12.5px;line-height:1.1}

/* ── 5.3 UM chip de status por dia volta (paliativo declarado no contrato) ───
   DESFAZ a metade de styles.css:534 (`.ag-chip,.ag-more{display:none}`) que
   apaga o PRIMEIRO chip. `.ag-more` continua escondido.
   Por que vale: `data-count` (app.js:2898) soma livre + proposta + confirmada +
   recusada num único número roxo, então 4 horários livres e 4 calls esperando
   confirmação mostram o mesmo "4". O chip traz a COR do status (slotClass,
   app.js:2853-2856: free/prop/conf/decl → verde/âmbar/roxo/rosa, styles.css:
   519-522) e a hora — e a cor já responde a pergunta que o número não responde.
   SELETOR: os chips são `<span>` irmãos do `<span class="ag-daynum">`
   (app.js:2857-2859), e `.ag-daynum` é o primeiro span da célula — por isso
   `:first-of-type` NÃO serviria. `.ag-daynum + .ag-chip` pega exatamente o
   primeiro chip e nenhum outro. Especificidade (0,3,0) contra (0,1,0). */
  .ag-cell .ag-daynum + .ag-chip{
    display:block;
    font-size:9.5px;
    padding:0 2px;
    font-weight:700;
    line-height:1.6;
    border-radius:4px;
  }
  .ag-legend{gap:8px 12px;font-size:11px;margin-bottom:10px}


/* ╔══════════════════════════════════════════════════════════════════════════╗
   ║ 6 · VENDAS / NFS-e — FORMULÁRIOS (a tabela é da peça 30)                 ║
   ╚══════════════════════════════════════════════════════════════════════════╝
   FURO [11] respeitado à risca: `div.nf-acoes` e `div.nf-resumo`, nunca as
   classes nuas. `.nf-acoes{display:flex;gap:8px;flex-wrap:wrap;margin:10px 0}`
   é GLOBAL em styles.css:669 e já hoje transforma o `<td class="nf-acoes">` de
   app.js:3869 em contêiner flex. Como este arquivo carrega DEPOIS da peça 30,
   um `.nf-acoes` nu venceria o empate e brigaria com ela. `div.` torna a regra
   INCAPAZ de casar com um `<td>`. Idem `.nf-resumo` (styles.css:659), que
   aparece no painel de vendas (app.js:3908) e no bloco de parcelamentos (5108). */

/* ── 6.1 os rótulos da emissão: o CSS mira `.field label`, o HTML usa
   `.field > span` ───────────────────────────────────────────────────────────
   Duas convenções coexistem e só uma tinha CSS:
   · `<div class="field"><label>` → `.field label{display:block;font-size:11.5px;
     font-weight:700;text-transform:uppercase;margin-bottom:6px}` (styles.css:223)
   · `<label class="field"><span>` → NADA. São 26 ocorrências em app.js (3874,
     3883-3892, 4094, 4105, 4271-4273, 4292, 4448-4452, 4762-4768…) e o `<span>`
     ficava inline, herdando os 14px do body, sem versalete e sem margem.
   A prova de que a regra faltava está no vizinho: contratos.css:34 teve que
   criar `.ct-form .field > span` localmente para o formulário de contrato.
   Escopado em #view-nfse porque a FICHA (app.js:1388-1449, e o `#nfLead` de
   app.js:162-176) usa a outra convenção e vive no #drawer, que é da peça 20 —
   conferido: dentro de #view-nfse não existe um único `<div class="field">`. */
  #view-nfse .field > span{
    display:block;
    font-size:12px;
    font-weight:700;
    color:var(--muted);
    text-transform:uppercase;
    letter-spacing:.3px;
    margin-bottom:6px;
    line-height:1.3;
  }
/* EXCEÇÃO OBRIGATÓRIA: os rótulos carregam parênteses em minúscula de propósito
   — "Entrada (opcional)" (app.js:4448), "Parcelas (entrada incluída)" (4450),
   "Vence todo dia (opcional)" (4452), "E-mail * recebe o link". Sem isto o
   versalete gritaria "ENTRADA (OPCIONAL)". */
  #view-nfse .field > span .hint,
  #view-nfse .field > span small{
    text-transform:none;
    font-weight:500;
    letter-spacing:0;
  }

/* ── 6.2 os filtros e os formulários: 2 campos por fileira, garantidos ───────
   `.nf-form` continua FLEX e nunca vira grade — de propósito. Três nós dependem
   de `flex:1 1 100%` funcionar dentro dela: `.nf-outro` (styles.css:719), o
   `<div class="nf-form ${id}Campos" style="…;flex:1 1 100%">` de blocoPagador
   (app.js:4270) e o `#nvVenda`/`#nvNovo` que app.js liga e desliga com
   `style.display = ""` (app.js:4783-4785). Virar grade quebraria os três em
   silêncio; `flex-basis` em % resolve o mesmo problema sem tocar em nada disso.
   Conta: painel em 347px − 2 de borda − 36 de padding do `.panel` = 309px úteis.
   `calc(50% - 4px)` com gap 8 = 2 campos de 150px por fileira. Um
   `input[type=date]` a 16px mostra "15/09/2026" em ~110px + 24 de padding: cabe. */
  #view-nfse .nf-form{gap:8px;margin:10px 0 6px}
  #view-nfse .nf-form > .field{
    flex:1 1 calc(50% - 4px);
    min-width:0;
  }
/* ⚠️ LOAD-BEARING. Um `<input>`/`<select>` sem largura declarada dimensiona pelo
   conteúdo (o atributo `size` padrão vale ~20 caracteres ≈ 200px a 16px, e um
   `<select>` mede pela opção mais longa — "— Venda direta (sem closer) —",
   app.js:4095). Sem `width:100%` o campo estoura a metade de 150px e reintroduz
   a rolagem horizontal que a peça 00 acabou de matar nos filtros. */
  #view-nfse .nf-form > .field input,
  #view-nfse .nf-form > .field select,
  #view-nfse .nf-form > .field textarea,
  #view-nfse .nf-form > .field .dinheiro{width:100%;min-width:0}

/* o campo de DINHEIRO ganha a fileira inteira: o eco por extenso
   (`.dinheiro-eco`, app.js:4628 — "dois mil e quinhentos reais") é a rede de
   segurança contra vírgula mal lida, e em 150px ele quebrava em 3 linhas.
   `:has()` falha graciosamente: sem suporte a regra é descartada e o campo
   fica com os mesmos 50% dos outros. */
  #view-nfse .nf-form > .field:has(.dinheiro){flex:1 1 100%}

/* ── 6.2b "Nota para empresa" (CNPJ): endereço e cidade (17/09/2026) ─────────
   A prefeitura recusa nota de CNPJ sem endereço e cidade; o bloco `.nf-pj` (app.js,
   `blocoEmpresa`) aparece na Nova venda, na linha "Emitir nota", no "Quem paga", na
   correção de nota recusada e no plano de Pix parcelado — e também na FICHA do lead
   (drawer, fora de #view-nfse), por isso os seletores NÃO dependem de #view-nfse.
   Medido a 375px com os 2 campos por fileira da 6.2: "HIAGO CABRAL ENSINO E EDUCACAO
   CONTINUADA LTDA" e "Avenida Paulista" saíam cortados em 123px — e o que não se lê
   não se confere antes de emitir. A ordem do DOM é a do "próximo" do teclado
   (CEP → rua → número → complemento → bairro → UF → cidade), então aqui só se mexe
   em LARGURA, nunca em `order`. Especificidade (1,4,0)/(0,4,0) vence a (1,2,0) da 6.2.
   ⚠️ `- 6px`, não `- 4px`: na ficha o `gap` é o de styles.css (10px, não os 8 da 6.2), e
   com 4px o par número+complemento somava 321px em 319 — cada um caía numa fileira. */
  #view-nfse .nf-pj .nf-form > .field,
  .nf-pj .nf-form > .field{flex:1 1 100%;min-width:0}
  #view-nfse .nf-pj .nf-form > .field.nf-pj-num,
  .nf-pj .nf-form > .field.nf-pj-num{flex:0 0 calc(34% - 6px)}
  /* o complemento divide a fileira com o número */
  #view-nfse .nf-pj .nf-form > .field.nf-pj-num + .field,
  .nf-pj .nf-form > .field.nf-pj-num + .field{flex:1 1 calc(66% - 6px)}
  #view-nfse .nf-pj .nf-form > .field.nf-pj-uf,
  .nf-pj .nf-form > .field.nf-pj-uf{flex:0 0 calc(30% - 6px)}
  #view-nfse .nf-pj .nf-form > .field.nf-pj-cidade,
  .nf-pj .nf-form > .field.nf-pj-cidade{flex:1 1 calc(70% - 6px)}
  .nf-pj .nf-form > .field input,
  .nf-pj .nf-form > .field select{width:100%;min-width:0}
  .nf-pj{padding:10px}
  .nf-pj-cab{font-size:15px}
  .nf-pj-status{font-size:13.5px;line-height:1.35}
  /* o CNPJ fixo e o motivo da recusa nas correções: linha inteira */
  #view-nfse .nf-form > .field.nf-pj-doc{flex:1 1 100%}
  .nf-motivo{font-size:14px}
  /* nas correções o e-mail de envio e o botão que EMITE ficam na linha inteira: o e-mail
     cortado em 149px é justamente o que precisa ser conferido antes de digitar EMITIR */
  #view-nfse .nf-form.nf-corrige > .field:has(input[type=email]){flex:1 1 100%}
  #view-nfse .nf-form.nf-corrige > .newlead-btn{flex:1 1 100%;min-height:var(--m-tap-lg);font-weight:700}

/* botões dentro da `.nf-form`: "Filtrar"/"Limpar" (app.js:3893-3894) lado a
   lado; "Emitir para <cliente>" (4110) e "Emitir nota" (#nvOk, 4770) são ação
   com consequência e vão para --m-tap-lg, com o "Cancelar" ao lado.
   `.nf-form .newlead-btn{height:36px}` (styles.css:645) continua valendo, mas
   `min-height` CLAMPA `height` — não preciso disputar aquele seletor. */
  #view-nfse .nf-form > button{
    flex:1 1 calc(50% - 4px);
    min-height:var(--m-tap);
  }
  #view-nfse .nf-form > .nfOk,
  #view-nfse .nf-form > #nvOk{
    flex:2 1 58%;
    min-height:var(--m-tap-lg);
    font-weight:700;
  }

/* "Quem paga é outra pessoa" (app.js:4254): checkbox de ~26px
   (`.nf-outro{font-size:13px;padding:6px 0 0}`, styles.css:719-720) que decide
   em nome de QUEM sai a nota fiscal. Marcar por engano emite no nome errado. */
  #view-nfse .nf-outro{
    min-height:var(--m-tap);
    font-size:15px;
    padding:12px 0 2px;
    margin-top:6px;
  }
  #view-nfse .nf-outro input{
    width:22px;
    height:22px;
    flex:0 0 22px;
  }

/* ── 6.3 a barra de ações do painel: "➕ Nova venda" ────────────────────────
   DECISÃO, e os três caminhos descartados com o motivo:
   (a) `position:fixed` (proposta original do dossiê, app.js:3898): REJEITADO
       pelos furos [12] e [15]. Vertical: o FAB ocuparia de 14+safe-b a 66+safe-b
       e a barra inferior ocupa de 0 a 56+safe-b → 42px de sobreposição, com o
       FAB em z-index 100 contra os 70 da barra. Horizontal: "➕ Nova venda" a
       15px ≈ 145px ancorado em right:14 → x ≈ 216 a 361, e os 6 slots medem
       ~59px cada — o FAB engoliria "Contratos" e "Mais" INTEIROS, e o "Mais" é o
       único caminho para Agenda, Dashboard, Pipeline, Campanha e E-mail.
       Pior: `.view{animation:fade .25s}` com `transform:translateY(4px)`
       (styles.css:83-85) faz o `.view` ser bloco contentor de descendente fixed
       durante 250ms, então o botão NASCERIA na caixa do #view-nfse e saltaria
       para o canto quando a animação terminasse — a cada entrada na aba.
   (b) `position:sticky` na própria `div.nf-acoes`: REJEITADO por medição do DOM.
       `<div id="vNovaForm">` (app.js:3900) vem IMEDIATAMENTE DEPOIS da barra, e
       é dentro dele que formNovaVenda (app.js:4726) desenha o formulário inteiro
       — inclusive o "Emitir nota" (#nvOk) e o "Cancelar" no fim. Uma barra
       grudada no rodapé cobriria justamente os últimos ~65px do formulário que
       ela mesma abre: o botão de emitir ficaria atrás dela. Trocaria um defeito
       de distância por um defeito de botão inalcançável.
   (c) `position:sticky` no topo: mesmo problema, com o formulário passando
       debaixo da barra.
   O QUE ENTRA: a barra fica no fluxo, "Nova venda" ganha a fileira inteira com
   48px de altura e vem PRIMEIRO, e o 6.2 encolheu os filtros acima dela de
   ~200px para ~150px. O botão passa a ficar a ~210px do topo do painel (h3 60 +
   filtros 150) em vez de ~420px. Fica registrado em defeitos_nao_resolvidos que
   a redução de verdade dos ~420px exige o `<details>` em volta da `.nf-form`,
   que é HTML. */
  #view-nfse div.nf-acoes{gap:8px;margin:10px 0}
  #view-nfse div.nf-acoes > #vNova{
    flex:1 1 100%;
    min-height:var(--m-tap-lg);
    font-size:15px;
    font-weight:700;
    order:-1;
  }
  #view-nfse div.nf-acoes > .chip-btn{flex:1 1 calc(50% - 4px)}

/* ── 6.4 o resumo (4 blocos) ────────────────────────────────────────────────
   `flex:1 1 130px` (styles.css:660) em 309px já dava 2 colunas; `calc(50% - 4px)`
   garante que continue 2 e não 1 quando o número tiver mais dígitos, e a caixa
   encolhe: são 4 blocos × 2 fileiras ≈ 120px antes da primeira venda. */
  #view-nfse div.nf-resumo{gap:8px;margin:10px 0}
  #view-nfse div.nf-resumo > div{
    flex:1 1 calc(50% - 4px);
    min-width:0;
    padding:9px 10px;
  }
  #view-nfse div.nf-resumo b{font-size:15px}
  #view-nfse div.nf-resumo span{font-size:11px;line-height:1.35}

/* ── 6.5 o seletor de comissário ────────────────────────────────────────────
   `.nf-comis{max-width:160px;font-size:.86rem;padding:2px 4px;
   border:1px solid transparent;background:transparent}` (styles.css:766-767):
   ~22px de altura, e a borda só aparece em `:hover`/`:focus` — no toque não
   existe hover, então o `<select>` era INDISTINGUÍVEL de texto comum. Ele é
   renderizado por celulaComissario (app.js:3775-3789) e por
   renderParcelamentos (5081-5084); o próprio código reconhece o risco em
   app.js:4020-4022 ("é fácil de encostar sem querer no celular") e mitiga com
   confirm() em 4036 — a confirmação protege contra gravar, mas o seletor nativo
   abrindo no meio da rolagem interrompe o gesto.
   `display:block;width:100%` e não `max-width:none`: dentro do cartão da peça 30
   a célula tem um micro-rótulo inline de `var(--m-lab-w)` (74px) antes do
   conteúdo (`td[data-lab]::before`), e um `inline-block` de 100% depois dele
   estouraria o cartão. Como bloco, ele quebra a linha e usa a largura cheia.
   O font-size 16px é MEU: a peça 00 listou `.nf-comis` entre os infratores do
   piso do iOS mas não o incluiu na regra coletiva dela (conferido em
   00-base.css) — e `.nf-comis` está no meu escopo. */
  #view-nfse .nf-comis{
    display:block;
    width:100%;
    max-width:100%;
    min-width:0;
    min-height:var(--m-tap);
    font-size:16px;
    padding:8px 10px;
    margin-top:4px;
    border-color:var(--border);
    background:var(--surface);
    border-radius:var(--m-r-sm);
    text-overflow:ellipsis;
  }

/* ── 6.6 Pix parcelado: o bloco do plano e a barra de andamento ─────────────
   `.parc-bloco` é o cronograma que precisa ser conferido ANTES de aprovar dez
   notas fiscais (comentário de app.js:4415-4429). A prévia (`#${id}Previa`,
   app.js:4440) tem `b{font-size:14px}` e `span{font-size:11.5px}` — 11,5px para
   ler dez datas no celular é pouco.
   `.parc-barra{max-width:160px}` (styles.css:757) travava a barra de andamento
   em 160px mesmo com o cartão em 300px. */
  .parc-bloco{padding:2px 10px 10px;margin:6px 0 10px;border-radius:var(--m-r-sm)}
  .parc-previa{font-size:13px;line-height:1.55}
  .parc-previa b{font-size:14.5px}
  .parc-previa span{font-size:12px}
  #view-nfse .parc-barra{max-width:none;height:8px;margin:0 0 6px}

/* "✅ Recebi a Nª parcela" — o clique de uma vez por mês (o comentário de
   app.js:5040-5043 diz literalmente "ESTE PAINEL EXISTE PARA UM CLIQUE POR
   MÊS"). `.newlead-btn.parc-receber{font-size:12px;padding:6px 10px}`
   (styles.css:762) dava ~28px.
   ⚠️ LIMITE DECLARADO (FURO [2]): isto dá ALVO ao botão, não VISIBILIDADE. Ele
   está na 6ª de 6 colunas de uma tabela de 534px dentro de um wrap de ~347px
   (EVIDENCIA §4), num `<td style="white-space:nowrap">` sem `data-lab`
   (app.js:5094) — logo o padrão de cartão da peça 30 (`:has(td[data-lab])`) não
   casa nela. Trazer o botão para dentro da tela exige seletor de `td`, que é da
   peça 30. Está em pedidos_a_outras_pecas e em defeitos_nao_resolvidos. */
  #view-nfse .parc-receber{
    min-height:var(--m-tap-lg);
    font-size:15px;
    font-weight:700;
    width:100%;
    margin-bottom:6px;
  }


/* ╔══════════════════════════════════════════════════════════════════════════╗
   ║ 7 · CONTRATOS — barra e FORMULÁRIO (a tabela é da peça 30)               ║
   ╚══════════════════════════════════════════════════════════════════════════╝
   contratos.css tem UMA media query, em 640px (contratos.css:53-56), o que deixa
   a faixa 641–780 sem conserto. Tudo aqui vale em 780 e cobre essa faixa morta.
   A neutralização de `.ct-acoes{min-width:230px}` (contratos.css:11, FORA de
   media query) NÃO está aqui: a peça 30 já a declara como
   `table:has(td[data-lab]) td.ct-acoes{min-width:0}` (conferido em
   30-cartao.css:186) e a tabela de contratos tem data-lab em toda célula
   (contratos.js:115-119), logo casa. Duplicar seria o bug que esta divisão
   existe pra evitar. Registrado em riscos. */

/* ── 7.1 a barra de filtros da lista ───────────────────────────────────────── */
  .ct-barra{gap:8px;margin:0 0 10px}
/* os 4 chips de filtro (contratos.js:72-73) em 2×2, com alvo de 44px que a
   peça 00 já deu ao `.chip-btn` — aqui só a largura. */
  .ct-barra > .chip-btn{flex:1 1 calc(50% - 4px);min-width:0}
/* DESFAZ contratos.css:5 (`.ct-barra .f{flex:1 1 220px;min-width:180px}`):
   o `#ctBusca` (contratos.js:74) ganha a fileira inteira. Mesma especificidade
   (0,2,0), arquivo posterior. A APARÊNCIA dele é da peça 00, que promoveu
   `input.f` para fora de `.filters` — sem isso ele era o input cru do navegador
   (borda 2px inset, 22px de altura, medido na EVIDENCIA §3). */
  .ct-barra .f{flex:1 1 100%;min-width:0}

/* ── 7.2 o formulário de 22 campos: 1 → 2 colunas ──────────────────────────
   Conta: `.ct-grade{repeat(auto-fill,minmax(170px,1fr))}` (contratos.css:31) em
   309px úteis (347 − 2 de borda − 36 de padding do `.panel`) resolve para UMA
   coluna, porque 2×170 = 340 > 309. Cada campo custa rótulo + input de 44px +
   margem ≈ 66px: ~20 campos visíveis = ~1.320px, mais 3 títulos de seção, a
   busca de lead, o parágrafo da cláusula 5.1 e a prévia de ~380px → os ~1.900px
   medidos do primeiro campo ao "Gerar contrato".
   Com 2 colunas de (309 − 8)/2 = 150px: ~13 fileiras ≈ 900px. Economia medida
   de ~450px, ou seja um terço da rolagem do formulário.
   `minmax(0,1fr)` e não `minmax(170px,1fr)`: `auto`/170px como mínimo faz a
   trilha crescer pelo min-content do `<select>` (a opção mais longa de
   `#ct_forma`/`#ct_meio`) e estourar a tela. */
  .ct-form .ct-grade{
    grid-template-columns:repeat(2,minmax(0,1fr));
    gap:10px 8px;
  }
  .ct-form .ct-grade > *{min-width:0}
/* ⚠️ LOAD-BEARING, mesmo motivo do 6.2: input/select sem `width` dimensionam
   pelo conteúdo e estouram uma trilha de 150px. */
  .ct-form .ct-grade input,
  .ct-form .ct-grade select,
  .ct-form .ct-grade textarea,
  .ct-form .ct-grade .dinheiro{width:100%;min-width:0}
/* DESFAZ contratos.css:33 (`.ct-grade .ct-largo{grid-column:span 2}`) e cobre a
   faixa morta 641–780 que contratos.css:54 só conserta abaixo de 640.
   Com exatamente 2 colunas, `1 / -1` e `span 2` dão o mesmo resultado — uso
   `1 / -1` porque não depende da contagem de colunas. Especificidade (0,3,0)
   contra (0,2,0). São os campos que precisam da linha inteira: Nome completo,
   E-mail (recebe o link) e Logradouro (contratos.js:285, 289, 291, 301). */
  .ct-form .ct-grade .ct-largo{grid-column:1 / -1}
/* o campo de dinheiro (campoDinheiro, app.js:4626-4628: `.dinheiro` + o eco por
   extenso `.dinheiro-eco`) também precisa da linha inteira. `> *:has()` e não
   `.field:has()` porque o `ct_entrada` vem embrulhado num `<div>` anônimo
   (contratos.js:317) e o `ct_valor` não (316). Falha graciosamente sem `:has()`. */
  .ct-form .ct-grade > *:has(.dinheiro){grid-column:1 / -1}

  .ct-form .ct-secao{font-size:12px;letter-spacing:.5px;margin:20px 0 8px}
  .ct-form .ct-ancora{font-size:12.5px;line-height:1.5;margin:-2px 0 12px}

/* ── 7.3 ao abrir o formulário, o scrollIntoView esconde o título sob o
   cabeçalho fixo ───────────────────────────────────────────────────────────
   contratos.js:251 é `box.scrollIntoView({behavior:"smooth",block:"start"})`, e
   `header.top` é `position:sticky;top:0` com 60px opacos no celular (peça 10).
   `block:"start"` alinha o topo da caixa com o topo da VIEWPORT, ou seja
   debaixo do cabeçalho: sumiam o `<h3>` ("Novo contrato" / "Editar contrato
   nº …") e o aviso que vai com ele ("salvar gera um PDF novo; o link já enviado
   deixa de valer", contratos.js:276-277) — exatamente a frase que diz que a
   ação tem consequência. `scroll-margin-top` é respeitado pelo scrollIntoView. */
  #ctFormBox{scroll-margin-top:calc(var(--m-header-h) + 10px)}

/* ── 7.4 a lista de sugestões de lead: branca sobre branca, sem teto ────────
   contratos.css:40 é `position:absolute;top:100%;left:0;right:0;z-index:20;
   background:var(--surface);border-radius:10px;box-shadow:var(--shadow)` — sem
   `max-height`, sem `overflow` e SEM BORDA. contratos.js:368 corta em 8 achados
   e 370 põe nome + e-mail + WhatsApp numa linha só por botão: um item com nome e
   e-mail longos ocupa 2 a 3 linhas, e 8 itens passam de 400px sobrepostos ao
   formulário — com o teclado aberto sobram ~400px de tela e não havia nada para
   rolar dentro da caixa. E `var(--surface)` é #ffffff no tema claro
   (styles.css:2), o MESMO fundo do `.panel` (styles.css:97): a única separação
   era a sombra `0 1px 2px rgba(16,24,40,.04)`, invisível na tela do celular.
   `max-height` em duas declarações de propósito: quem não tem `dvh` fica com os
   46vh; quem tem usa `dvh`, que é o que sobra de verdade com o teclado aberto. */
  .ct-sugestoes{
    max-height:46vh;
    max-height:46dvh;
    overflow-y:auto;
    overscroll-behavior:contain;     /* rolar a lista não rola o formulário atrás */
    -webkit-overflow-scrolling:touch;
    border:1px solid var(--border-strong);
    box-shadow:var(--shadow-lg);
  }
  .ct-sug{
    min-height:var(--m-tap);
    font-size:15px;
    padding:11px 12px;
    line-height:1.35;
  }
  .ct-lead{gap:8px;margin-bottom:10px}
  .ct-lead .field{flex:1 1 100%;min-width:0}

/* ── 7.5 a prévia: VEREDITO antes dos 773 caracteres de texto jurídico ──────
   contratos.js:324 põe `#ctPrevia` e 325 põe `.ct-botoes`, nessa ordem, no fim
   do painel. pedePrevia (contratos.js:482-488) escreve dentro da caixa nesta
   ordem: `.ct-previa-rot` ("Como vai sair no contrato"), `<p>qualificacao</p>`,
   `<p>pagamento</p>` e SÓ DEPOIS o veredito (`.ct-falta` "Falta: CPF · Cidade"
   ou `.ct-pronto` "✓ pronto para gerar").
   Medido na rota de prévia deste servidor com um contrato real de 12× no cartão:
   qualificacao = 412 caracteres, pagamento = 361 → 773 caracteres a 12,5px/1,55
   (contratos.css:46), ~17 linhas, ~380px — meia tela do iPhone ENTRE o último
   campo e a informação de qual campo falta.
   Reordenação PURA, sem tocar em pedePrevia(): o `.ct-previa` vira coluna flex
   e o veredito recebe `order` negativo. Os `<p>` ficam no order padrão 0. */
  .ct-form .ct-previa{
    display:flex;
    flex-direction:column;
    font-size:12.5px;
    padding:12px;
    margin:14px 0 10px;
  }
  .ct-form .ct-previa .ct-falta,
  .ct-form .ct-previa .ct-pronto{
    order:-2;
    margin:0 0 8px;
    font-size:14px;
    line-height:1.4;
  }
  .ct-form .ct-previa .ct-previa-rot{order:-1;margin-bottom:2px}

/* ── 7.6 "Gerar contrato" grudado no rodapé enquanto o painel está na tela ──
   É a única regra `position:sticky` desta peça, e ela OBEDECE a regra normativa
   de camada vertical da peça 10 (furo [15]): a barra inferior é opaca e ocupa
   sempre os últimos calc(var(--m-nav-h) + var(--m-safe-b)) pixels, então quem
   precisa de rodapé ancora ACIMA disso. Com `bottom:0` o botão nasceria debaixo
   das abas, que têm z-index 70 e interceptam o toque.
   Por que aqui é seguro e na `div.nf-acoes` não era (ver 6.3): `.ct-botoes` é o
   ÚLTIMO filho do `.ct-form` (contratos.js:325-328) — não há nenhum formulário
   depois dela para ela cobrir. Ela solta sozinha quando o painel sai da tela e o
   `#ctListaBox` entra.
   SEM margem negativa de propósito: sangrar até a borda do painel dependeria de
   `.panel{padding:18px}` (styles.css:97) continuar 18px, e `.panel` não está no
   escopo de nenhuma peça. Sem sangria, os 18px laterais mostram o fundo do
   `.panel`, que é o mesmo `var(--surface)` do fundo da barra: nada vaza. */
  .ct-form .ct-botoes{
    position:sticky;
    bottom:calc(var(--m-nav-h) + var(--m-safe-b) + 1px);   /* +1px = border-top da barra (10-casca.css) */
    z-index:5;
    gap:8px;
    padding:10px 0 4px;
    background:var(--surface);
    border-top:1px solid var(--border);
    box-shadow:0 -10px 14px -10px rgba(16,24,40,.22);
  }
  .ct-form .ct-botoes > #ctSalvar{
    flex:2 1 58%;
    min-height:var(--m-tap-lg);
    font-size:15px;
    font-weight:700;
  }
  .ct-form .ct-botoes > #ctFechar{flex:1 1 30%;min-height:var(--m-tap-lg)}

/* ── 7.7 a nota do CEP cresce de 1 para 2 linhas e empurra o campo ──────────
   contratos.js:300 põe a nota DENTRO do rótulo (`CEP <small id="ctCepNota">…`)
   e `.ct-form .field > span` (contratos.css:34) é `display:block`, então o
   rótulo cresce em ALTURA e empurra o `<input>` para baixo. ligaCep troca o
   texto em três etapas: "preenche o endereço" (19 caracteres), "buscando o
   endereço…" (contratos.js:401) e depois "endereço preenchido pelo CEP —
   confira e complete o número" (57 caracteres, contratos.js:427) ou "endereço
   mantido: os campos já estavam preenchidos à mão" (56, contratos.js:428). As
   duas últimas quebram em 2 linhas e somam ~17px de deslocamento no MESMO
   instante em que contratos.js:433 chama `num.focus()` e o navegador rola o
   campo Número para a área visível — a tela pula duas vezes de uma vez.
   Reserva a altura das duas linhas ANTES de o texto chegar. `:has(#ctCepNota)`
   e não `:has(#ct_cep)`: quando o país é estrangeiro o rótulo é "Código postal"
   SEM `<small>` (contratos.js:300), e aí não há altura para reservar.
   Sem suporte a `:has()` a regra é descartada e volta o pulo de hoje. */
  .ct-form .field:has(#ctCepNota) > span{min-height:2.9em}


/* ╔══════════════════════════════════════════════════════════════════════════╗
   ║ 8 · CAMPANHA e E-MAIL                                                    ║
   ╚══════════════════════════════════════════════════════════════════════════╝ */

/* CONFERIDO, SEM REGRA — `.camp-msg-grid` e `.mode-cards` JÁ colapsam para uma
   coluna em styles.css:352 (`@media (max-width:780px){.camp-msg-grid{
   grid-template-columns:1fr} .mode-cards{grid-template-columns:1fr}
   .step-head{flex-wrap:wrap} .camp-count-big{margin-left:42px;text-align:left}}`).
   Reescrever seria ruído. Ficam aqui nomeados para o verificador saber que foram
   abertos e medidos, não esquecidos.

   CONFERIDO, SEM REGRA — `.mkpis{repeat(auto-fit,minmax(140px,1fr))}`
   (styles.css:608): em 347px cabem 2 trilhas (2×140 + 12 = 292 ≤ 347), e o
   mínimo da trilha é 140px e não `auto`, então não existe estouro por
   min-content. As três instâncias (placar do copiloto app.js:870, métricas do
   e-mail 3166, e a do #drawer 3534 — que é da peça 20) já ficam em 2 colunas. */

/* ── 8.1 a prévia do e-mail deixa de ser um rolador dentro do rolador ───────
   `.prev-body{max-height:340px;overflow:auto}` (styles.css:605) faz sentido no
   desktop, onde a prévia divide a tela com o editor. No celular ela vira uma
   janelinha de 340px com rolagem própria DENTRO da página que já rola, e é o
   único lugar onde se confere o que 2.564 pessoas vão receber. Solto o teto e
   deixo a PÁGINA rolar: zero rolagem aninhada, zero armadilha de gesto.
   Escopado em #view-email porque a mesma classe é usada dentro do #drawer, em
   renderCampanha (app.js:3554), e o #drawer é da peça 20 — lá o teto de 340px
   continua valendo, e é isso que eu quero: dentro da folha a rolagem é do
   `.dbody`. */
  #view-email .prev-body{
    max-height:none;
    /* `auto`, nunca `visible`: `.prev-mail` é overflow:hidden (styles.css:602) e
       clipava sem saída qualquer tabela do corpo de e-mail mais larga que ~343px.
       Com `max-height:none` não sobra rolagem vertical — sobra só o eixo x, que é
       exatamente a saída certa. Mesma decisão que a peça 20 tomou para a
       instância dentro do #drawer. */
    overflow:auto;
    padding:14px 14px;
    font-size:14px;
  }
  #view-email .prev-head{padding:10px 12px;line-height:1.6}

/* ── 8.9 · MOVER O CARD DE ESTAGIO (toque longo) ────────────────────────────
   mobile.js §6: no celular a `.kanban` e uma faixa de 1.532px com 5 colunas de
   298px numa tela de 375 — arrastar de "novo" ate "cliente" seria segurar o card
   por 1.234px de rolagem automatica, com um tremor do polegar soltando na coluna
   errada (e coluna errada aqui e PATCH no estagio de um lead de verdade).
   Toque longo abre a lista de estagios: 2 toques, zero precisao.
   O JS reusa o handler de `drop` de app.js:1253 — nenhuma regra nova. */

  /* o card avisa que o toque longo pegou */
  .kcard.m-pressionando{
    transform:scale(.97);
    transition:transform .12s ease;
    border-color:var(--accent);
  }

  /* a folha. `position:fixed` ancorada ACIMA da barra inferior — a regra
     normativa de camada vertical da peca 10: nada fixo ocupa os ultimos
     calc(var(--m-nav-h) + var(--m-safe-b)) px da tela. */
  .m-mover{
    position:fixed;
    left:10px;
    right:10px;
    /* `--kb` (publicado por mobile.js) e obrigatorio: com o teclado do filtro de cards
       aberto, ancorar so na barra punha a folha ATRAS do teclado. */
    bottom:calc(var(--m-nav-h) + var(--m-safe-b) + var(--kb,0px) + 10px);
    z-index:110;                  /* acima da barra (70) e do scrim (80) */
    display:flex;
    flex-direction:column;
    gap:6px;
    max-height:calc(100dvh - var(--m-header-h) - var(--m-nav-h) - var(--m-safe-b) - 40px);
    overflow-y:auto;
    overscroll-behavior:contain;
    padding:12px;
    background:var(--surface);
    border:1px solid var(--border);
    border-radius:var(--m-r);
    box-shadow:var(--shadow-lg);
  }
  /* ⚠️ TRAVA DE SEGURANCA, nao transicao. A folha aparece com o dedo ainda na tela (o
     toque longo dispara em 300ms) e o `click` que vem atras e re-alvejado na posicao do
     dedo — que pode ser um botao de estagio. Mover para "Cliente" ou "Negociando"
     dispara conversao no Meta, que nao volta atras. Inerte ate o dedo sair (mobile.js
     tira a classe no primeiro pointerup + 120ms). */
  .m-mover.m-armando{ pointer-events:none }

  .m-mover-tit{
    font-size:12.5px;
    color:var(--muted);
    padding:2px 2px 6px;
    line-height:1.4;
  }
  .m-mover-tit b{ color:var(--text) }
  .m-mover-op{
    min-height:var(--m-tap-lg);
    border:1px solid var(--border);
    background:var(--surface-2);
    color:var(--text);
    border-radius:var(--m-r-sm);
    font-size:15px;
    font-weight:600;
    text-align:left;
    padding:0 14px;
  }
  .m-mover-op:active{ background:var(--accent-soft); border-color:var(--accent) }
  .m-mover-op.atual{
    opacity:.55;
    font-weight:500;
    background:transparent;
    border-style:dashed;
  }
  .m-mover-cancelar{
    min-height:var(--m-tap);
    margin-top:2px;
    border:0;
    background:transparent;
    color:var(--muted);
    font-size:14px;
    font-weight:600;
  }

/* ── 9 · AS BARRAS DO DASHBOARD SAO ALVO DE TOQUE ───────────────────────────
   `drawBars(..., {clickable:true})` (app.js:462) filtra a base inteira pelo
   clique na linha, e o proprio <h3> promete ("clique pra filtrar",
   index.html:101 e 107). A linha media ~21px de altura — metade do minimo.
   Sao quatro paineis: perfil, funil, origem e canal.
   Orfao de escopo nas 6 pecas; fica aqui, que e a dona do corpo do Dashboard. */
  #view-dashboard .bars > *{
    min-height:var(--m-tap);
    align-items:center;
  }

}
/* ── fim do @media (max-width:780px) ─────────────────────────────────────── */


/* ╔══════════════════════════════════════════════════════════════════════════╗
   ║ 9 · HOVER COLADO NO TOQUE — só os :hover DESTA peça                      ║
   ╚══════════════════════════════════════════════════════════════════════════╝
   Num aparelho de toque o `:hover` fica GRUDADO no último elemento tocado, e o
   desenho de hover deste app é o mesmo desenho de "selecionado" — então o card,
   o dia da agenda ou a sugestão que a pessoa acabou de tocar ficam pintados como
   se estivessem escolhidos. `(hover:none)` isola isso do desktop de forma dura:
   mouse não casa com `hover:none`, logo NADA deste bloco vale na máquina do time.

   O bloco `@media (hover:none)` da peça 00 é dela, e ela DEIXOU DE FORA de
   propósito os hovers do meu escopo — o comentário dela diz literalmente
   "Os órfãos (.qc-wa:hover, .qmore:hover) são pedido à peça 50". Por isso
   declaro o meu próprio, com o breakpoint explícito na própria consulta como o
   briefing pediu. NÃO repito `.kcard:hover`, `.qcard:hover` nem `tbody tr:hover`:
   esses três já estão neutralizados em 00-base.css e redeclarar seria
   sobreposição de seletor entre peças. */
@media (hover:none) and (max-width:780px), (hover:none) and (max-height:520px) and (pointer:coarse){

  /* DESFAZ styles.css:557 (`.qc-wa:hover{border-color:var(--green)}`) — o botão
     de WhatsApp do último lead que se abriu ficava com borda verde acesa, igual
     a "este é o lead selecionado". Volta ao repouso de styles.css:556
     (`border:1px solid transparent`). */
  .qcard .qc-wa:hover{border-color:transparent}

  /* DESFAZ styles.css:600 (`.qmore:hover{border-color:var(--accent);
     color:var(--accent-text)}`) — volta ao repouso de styles.css:599. */
  .qmore:hover{
    border-color:var(--border-strong);
    color:var(--muted);
  }

  /* DESFAZ styles.css:514 (`.ag-cell:hover{border-color:var(--accent);
     background:var(--accent-soft)}`). Grave aqui em particular: `.ag-cell.today`
     (styles.css:516) usa borda de acento para dizer QUE DIA É HOJE, e um dia
     qualquer tocado ficava com a mesma cara de hoje. Volta ao repouso de
     styles.css:513. A regra de `.today` tem (0,2,0) contra (0,2,0) daqui — mas
     `.today` não tem `background:var(--accent-soft)`, e eu não declaro
     `border-color` para `.today`, então o `box-shadow ... inset` dela continua
     marcando o dia. */
  .ag-cell:hover{
    border-color:var(--border);
    background:var(--surface-2);
  }

  /* DESFAZ contratos.css:44 (`.ct-sug:hover{background:var(--surface-2)}`) — o
     último lead tocado na lista de sugestões ficava destacado como se tivesse
     sido escolhido, e escolher ali PREENCHE 10 campos do contrato. */
  .ct-sug:hover{background:transparent}

  /* DESFAZ styles.css:769 (`.nf-comis:hover,.nf-comis:focus{border-color:
     currentColor;opacity:.95}`) no que ele tem de hover. O 6.5 já deu borda
     permanente ao seletor; sem isto, o comissário tocado por último fica com
     borda da cor do texto, mais forte que os outros — "este está sendo editado"
     quando não está. O `:focus` NÃO entra: enquanto o seletor nativo está
     aberto, marcar qual campo tem o foco é correto. */
  #view-nfse .nf-comis:hover{
    border-color:var(--border);
    opacity:1;
  }

  /* DESFAZ styles.css:344 (`.mode-card:hover{border-color:var(--accent)}`).
     `.mode-card.on` (styles.css:345) é justamente `border-color:var(--accent);
     background:var(--accent-soft)`: tocar em "Assistido" e depois em
     "Automático" deixava OS DOIS com borda de acento, e o modo escolhido decide
     se a campanha sai do WhatsApp da pessoa ou do número oficial. (0,2,0) aqui
     contra (0,2,0) da `.on`, e a `.on` também pinta o fundo — então o cartão
     realmente escolhido continua distinguível. Escopado em `.mode-cards` porque
     o contêiner é que é meu. */
  .mode-cards .mode-card:hover{border-color:var(--border)}

  /* DESFAZ styles.css:120 (`.mcol .mbar:hover::after{content:attr(data-v)…}`) —
     a tarja "12 compras" nascia em `top:-22px` sobre a barra tocada e ficava
     lá, tampando os meses vizinhos do gráfico. No celular o número já está
     impresso em cima da barra (`.mv`, styles.css:122, app.js:476). */
  .months .mcol .mbar:hover::after{content:none}
}
