/* Design system portado de frontcell-ops/WebApp.html — tokens e componentes provados em uso
   real. Trimado pro essencial do v1 (sem gráficos/analytics avançado). Nome do produto ainda
   não decidido — ver docs/PESQUISA_MERCADO.md — então o header lê o logo/nome da organização
   em runtime (organizacoes.logo_url / nome), não tem marca fixa hardcoded como "FrontCell".
*/
:root{
  --p-blue:#1E6FD9;--p-blue-deep:#124A99;--p-blue-ink:#0D2B52;
  --p-yellow:#FFD400;--p-yellow-soft:#FFF3B8;
  --p-bg:#F4F6F9;--p-surface:#FFFFFF;--p-border:#E3E8EF;
  --p-text:#16202E;--p-sub:#5B6879;
  --p-ok:#1E8E3E;--p-ok-bg:#E6F4EA;--p-warn:#E8710A;--p-warn-bg:#FEF0E3;--p-err:#D93025;--p-err-bg:#FCE8E6;
  /* Versão bem mais sutil de --p-err-bg — nudge visual, não alerta (pedido do Felipe,
     item "peça a custo": campo Venda vazio ganha um fundo vermelho quase imperceptível
     em vez do texto de ajuda "vazio = a custo" que existia antes — ver .fin-vazia-leve
     em os.html). --p-err-bg já era usado pra alertas de verdade (.alert-err/.fchk-alerta);
     este token fica bem mais claro de propósito, pra não competir com eles. */
  --p-err-bg-leve:rgba(217,48,37,.05);
  /* Auditoria adversarial v2 (os.html, achado Médio "7 status → mais cores"): violeta pro
     status "Aguardando peça" (o mais acionável dos antigos "warn"); --p-blue-bg reaproveita o
     mesmo tom já usado em .alert-info pro status "Pronto" (distinto de Entregue/Pago = --p-ok). */
  --p-violet:#7C3AED;--p-violet-bg:#F1E9FE;--p-blue-bg:#EBF5FF;
  /* Paleta de categoria (Financeiro › Extrato/Recebidas/Despesas/Custos Fixos/Contas a
     Pagar) — cor automática e determinística por hash do texto livre da categoria/tipo (ver
     corCategoria() em js/app.js), pra pílula colorida (.badge) tipo a aba "Tabela" da
     planilha antiga. 9 tons curados (não hue aleatório/infinito — cor feia é pior que
     repetir tom); os 4 primeiros reaproveitam tokens semânticos que já existiam
     (--p-ok/--p-warn/--p-blue/--p-violet), os outros 5 são novos só pra esta paleta.
     Despesa dentro do Extrato usa --p-err/--p-err-bg direto (regra fixa, não entra nesta
     paleta — ver comentário em financeiro.html). */
  --cat1:var(--p-ok);--cat1-bg:var(--p-ok-bg);       /* verde */
  --cat2:var(--p-warn);--cat2-bg:var(--p-warn-bg);   /* laranja */
  --cat3:var(--p-blue);--cat3-bg:var(--p-blue-bg);   /* azul */
  --cat4:var(--p-violet);--cat4-bg:var(--p-violet-bg); /* violeta */
  --cat5:#B0208A;--cat5-bg:#FCE8F3;                  /* magenta */
  --cat6:#0F8B82;--cat6-bg:#E1F5F3;                  /* teal */
  --cat7:#8A6D00;--cat7-bg:#FFF6D6;                  /* dourado */
  --cat8:#3949AB;--cat8-bg:#E8EAF9;                  /* índigo */
  --cat9:#5F6368;--cat9-bg:#EDEFF2;                  /* cinza — categoria vazia/neutra */
  --r:8px;--sh:0 1px 4px rgba(0,0,0,.07);--panel-w:470px;
  /* Sidebar (≥769px — ver .sb abaixo). --sb-w é a variável que empurra o
     conteúdo (.hd-desk/.body-wrap margin-left) e o Alpine.store('ui') a
     sobrescreve no <body> conforme sbCollapsed; o valor aqui é só o padrão
     antes do Alpine hidratar, e já nasce igual ao padrão real (expandida). */
  --sb-full:220px;--sb-rail:64px;--sb-w:var(--sb-full);
  /* Fundo da linha de tabela sob o mouse. Era o literal #F8FAFF espalhado na regra
     de hover; virou token porque no tema escuro precisa ser um cinza-azulado ESCURO
     — um literal claro ali "acendia" a linha inteira. */
  --p-row-hover:#F8FAFF;
}

/* ============================================================================
   Tema escuro — pedido do Felipe (10/08). Só redefine os tokens --p-*; nenhum
   componente tem regra própria de tema, então uma cor nova aqui vale pro app
   inteiro de uma vez. O atributo data-theme mora no <html> e quem escreve é o
   Alpine.store('tema') (js/app.js), com um script inline no <head> do index.html
   aplicando antes da primeira pintura pra não piscar branco.

   Escolhas: fundo azulado (não cinza neutro) pra manter o parentesco com o azul da
   marca; --p-blue clareado porque o #1E6FD9 do claro fica ilegível sobre fundo
   escuro; --p-yellow intocado (é a cor de destaque e funciona nos dois temas);
   os *-bg de badge viram versões translúcidas da própria cor, que se dão bem com
   qualquer superfície.
   ============================================================================ */
[data-theme="dark"]{
  --p-blue:#4D93F0;--p-blue-deep:#1B56A8;--p-blue-ink:#0B1524;
  --p-bg:#0E141D;--p-surface:#161E2A;--p-border:#2A3546;
  --p-text:#E7EDF6;--p-sub:#93A2B6;
  --p-ok:#4ADE80;--p-ok-bg:rgba(74,222,128,.14);
  --p-warn:#FBA94C;--p-warn-bg:rgba(251,169,76,.14);
  --p-err:#FF6B60;--p-err-bg:rgba(255,107,96,.14);--p-err-bg-leve:rgba(255,107,96,.07);
  --p-violet:#A78BFA;--p-violet-bg:rgba(167,139,250,.16);--p-blue-bg:rgba(77,147,240,.16);
  --p-yellow-soft:rgba(255,212,0,.18);
  --p-row-hover:#1D2735;
  /* --cat1-4 herdam de --p-ok/--p-warn/--p-blue/--p-violet acima, já redefinidos pro escuro
     nesta mesma regra — só os 5 novos (--cat5-9) precisam de variante própria aqui. */
  --cat5:#E85FC3;--cat5-bg:rgba(232,95,195,.16);
  --cat6:#3DD9CB;--cat6-bg:rgba(61,217,203,.16);
  --cat7:#E0C34A;--cat7-bg:rgba(224,195,74,.16);
  --cat8:#8B9AF0;--cat8-bg:rgba(139,154,240,.16);
  --cat9:#93A2B6;--cat9-bg:rgba(147,162,182,.16);
  --sh:0 1px 4px rgba(0,0,0,.4);
  color-scheme:dark;
}
*,*::before,*::after{box-sizing:border-box;margin:0;padding:0;}
/* Alpine só remove o atributo x-cloak depois de hidratar; sem esta regra, tudo que
   deveria nascer escondido (popovers, dropdowns) aparece por um instante no
   carregamento. Ver https://alpinejs.dev/directives/cloak */
[x-cloak]{display:none!important;}
body{font-family:'Inter',system-ui,sans-serif;font-size:14px;color:var(--p-text);background:var(--p-bg);min-height:100vh;overflow-x:hidden;}
.ico{width:16px;height:16px;stroke-width:2;vertical-align:-3px;}
.ico-sm{width:14px;height:14px;stroke-width:2;vertical-align:-2px;}
.ico-lg{width:20px;height:20px;stroke-width:2;vertical-align:-4px;}

.hd{background:var(--p-blue-ink);position:sticky;top:0;z-index:200;display:flex;align-items:center;gap:16px;padding:0 20px;height:56px;box-shadow:0 2px 8px rgba(0,0,0,.25);}
/* Nome da organização (fallback quando não tem logo, ver #lTxt/#lTxtSb em
   index.html) precisa quebrar em até 2 linhas SEM cortar palavra no meio —
   pedido do Felipe. white-space:normal (era nowrap) permite a quebra; ela só
   acontece nos espaços por padrão do navegador (overflow-wrap/word-break aqui
   ficam explicitamente 'normal' pra nenhum reset global do app forçar
   break-word/break-all, que partiriam uma palavra ao meio se ela sozinha não
   coubesse). -webkit-line-clamp corta na 2ª linha com reticências só no caso
   extremo de um nome muito comprido — o cabeçalho mobile (.hd) é sticky com
   altura fixa (56px, ver media query no fim do arquivo e outros componentes
   que assumem esse valor, tipo .panel{top:56px}), então o texto não pode
   crescer livremente aqui como cresce na sidebar (.sb-logo-t, mais abaixo). */
.logo-t{font-size:15px;line-height:1.15;font-weight:800;font-style:italic;letter-spacing:-.3px;color:#fff;white-space:normal;overflow-wrap:normal;word-break:normal;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;max-width:190px;}
/* Achado do Felipe (2026-08-12): logo maior (26→38px) + nome sempre visível do lado (não
   mais um-ou-outro, ver _atualizarLogoHeader() em js/app.js) — max-width do nome subiu
   junto (150→190px) pra não ficar apertado ao lado de uma logo maior; overflow-wrap:normal
   + line-clamp:2 (herdados) já garantem que o nome quebra linha em vez de cortar/estourar. */
.logo-img{height:38px;width:auto;object-fit:contain;}
.hd-user{margin-left:auto;display:flex;align-items:center;gap:10px;color:rgba(255,255,255,.75);font-size:12px;}
.hd-user button{background:none;border:1.5px solid rgba(255,255,255,.25);color:#fff;border-radius:100px;padding:4px 12px;font-family:inherit;font-size:11px;font-weight:600;cursor:pointer;}
.hd-user button:hover{border-color:var(--p-yellow);color:var(--p-yellow);}

/* Toggle sidebar ↔ topbar (B2 do plano de melhorias — decisão do Felipe: sempre
   visível no header, não escondido em Configurações). Existe DUPLICADO dentro de
   .hd-mobile E .hd-desk (mesmo padrão já usado por period-wrap/hd-user nos dois
   headers) — cada cópia só aparece quando o header-pai dela está visível. Fica
   display:none por padrão (mobile de verdade, ≤768px, não tem pra onde alternar:
   sidebar não existe nesse breakpoint) e só reaparece ≥769px, ver media query. */
/* `.hd-user button` (linha 33) tem especificidade maior que `.nav-layout-toggle` sozinho
   (1 classe+1 tag > 1 classe) e vencia o padding:0 daqui com seu padding:4px 12px — sobrava
   só ~4px de largura útil num botão de 28px, e o flexbox espremia o ícone de 14px pra ~2px
   de largura (ficava um traço/ponto, ilegível — achado do Felipe usando o app pela 1ª vez).
   `.hd-user .nav-layout-toggle` iguala a especificidade em número de classes só que com uma
   a mais, então vence de propósito. Aproveitado também pra aumentar o botão (28→32px) e o
   ícone (.ico-sm 14px → .ico 16px, trocado no HTML) — pedido explícito do Felipe. */
/* .hd-btn-ico é o visual (botão redondo de ícone no cabeçalho escuro); quem decide
   QUANDO cada botão aparece é a classe extra. O toggle de layout só existe ≥769px
   (abaixo disso não há sidebar pra alternar), mas o de tema vale em qualquer
   largura — por isso o visual saiu de dentro de .nav-layout-toggle. */
.hd-btn-ico{display:inline-flex;background:none;border:1.5px solid rgba(255,255,255,.25);color:#fff;border-radius:100px;width:32px;height:32px;align-items:center;justify-content:center;cursor:pointer;padding:0;}
.hd-user .hd-btn-ico{padding:0;}
.hd-btn-ico:hover{border-color:var(--p-yellow);color:var(--p-yellow);}
.nav-layout-toggle{display:none;}

/* Filtro de período global (cabeçalho) — porte de WebApp.html, mesmo padrão visual,
   variáveis --p-* no lugar de --fc-*. Dirige Dashboard/Caixa da Empresa (DRE)/Análises. */
.period-wrap{display:flex;align-items:center;gap:5px;margin-left:auto;flex-wrap:wrap;}
.pb{padding:4px 10px;border-radius:100px;font-size:11px;font-weight:700;border:1.5px solid rgba(255,255,255,.2);background:transparent;color:rgba(255,255,255,.65);cursor:pointer;transition:all .12s;white-space:nowrap;font-family:inherit;}
.pb:hover{border-color:var(--p-yellow);color:var(--p-yellow);}
.pb.on{background:var(--p-yellow);border-color:var(--p-yellow);color:var(--p-blue-ink);}
.period-sep{color:rgba(255,255,255,.3);font-size:11px;}
.pci{padding:4px 7px;border-radius:4px;border:1.5px solid rgba(255,255,255,.2);background:transparent;color:#fff;font-size:11px;font-family:inherit;outline:none;width:120px;color-scheme:dark;}
/* Achado P2 #15 da auditoria adversarial: outline:none acima zerava o foco do navegador
   sem nenhum substituto — só o .pb (botão de atalho) e os campos .fin/.fsel tinham
   indicação de foco, os dois inputs de data custom ficavam "mudos" ao tabular. Mesmo
   padrão visual de .fin:focus/.fsel:focus (troca a cor da borda), adaptado pro fundo
   escuro do header (borda amarela em vez de azul, que sumiria contra --p-blue-ink). */
.pci:focus{border-color:var(--p-yellow);}

/* Nav horizontal — só <769px (mobile). Ver docs/HANDOFF_2026-08-06.md item 1:
   ≥769px o app usa a sidebar vertical (.sb) abaixo; .hd-mobile/.nav-mobile
   ficam com display:none nesse breakpoint (ver media query no fim do arquivo). */
.nav{background:var(--p-surface);border-bottom:2px solid var(--p-border);display:flex;padding:0 20px;overflow-x:auto;scrollbar-width:none;}
.nav::-webkit-scrollbar{display:none;}
/* Achado P3 #27 da auditoria adversarial: em telas estreitas o .nav vira uma barra
   horizontal com scroll escondido (scrollbar-width:none acima) e nenhuma pista visual
   de que dá pra arrastar pros lados — usuário pode nunca descobrir que Análises/DRE
   existem mais pra direita. .nav-mobile-wrap envolve o <nav> só pra hospedar os
   gradientes de fade nas duas bordas (pointer-events:none — não atrapalha o toque no
   scroll nem nos botões). Só existe visualmente ≤768px porque .nav-mobile já fica
   display:none ≥769px (sidebar assume a navegação nesse breakpoint, ver mais abaixo);
   sem media query aqui mesmo, o wrapper não teria conteúdo visível pra "framear" acima
   desse breakpoint. */
.nav-mobile-wrap{position:relative;}
/* Reforço (pedido do Felipe, 2026-08-12): além do fade, um glifo ‹/› — e os dois só
   ficam visíveis quando há mais conteúdo pra esse lado (.nm-sem-esq/.nm-sem-dir,
   calculados em index.html). Sem essas classes (JS não rodou ainda, ex: primeiro
   paint) o padrão é mostrar os dois — pior caso é uma pista de sobra, nunca uma
   escondida. O FADE em si continua sendo os pseudo-elementos (decoração pura,
   pointer-events:none, sem `content` — só o gradiente). O GLIFO deixou de ser
   `content` de pseudo-elemento e virou um <button> de verdade (.nav-scroll-btn
   logo abaixo — achado do Felipe, 2026-08-12: clicar no glifo antigo "vazava" pro
   botão do menu que estava por baixo dele, porque pseudo-elemento com
   pointer-events:none não intercepta clique nenhum) — precisava ser clicável pra
   de fato rolar o menu, não só sinalizar que dá pra rolar. */
.nav-mobile-wrap::before,.nav-mobile-wrap::after{
  position:absolute;top:0;bottom:2px;width:24px;pointer-events:none;z-index:2;
  opacity:1;transition:opacity .15s;
}
.nav-mobile-wrap::before{content:'';left:0;background:linear-gradient(to right,var(--p-surface) 45%,transparent);}
.nav-mobile-wrap::after{content:'';right:0;background:linear-gradient(to left,var(--p-surface) 45%,transparent);}
.nav-mobile-wrap.nm-sem-esq::before{opacity:0;}
.nav-mobile-wrap.nm-sem-dir::after{opacity:0;}
/* Botão real por cima do fade (z-index:3 > 2 do gradiente) — mesma posição/glifo de
   antes, mas clicável: rola o <nav> por baixo em vez de deixar o clique cair no
   botão de menu logo atrás. background:transparent pra não tampar o gradiente. */
.nav-scroll-btn{position:absolute;top:0;bottom:2px;width:24px;z-index:3;display:flex;align-items:center;background:transparent;border:none;padding:0;font-family:inherit;font-size:15px;font-weight:800;color:var(--p-sub);cursor:pointer;opacity:1;transition:opacity .15s,color .12s;}
.nav-scroll-btn:hover{color:var(--p-blue);}
.nav-scroll-btn-esq{left:0;justify-content:flex-start;padding-left:3px;}
.nav-scroll-btn-dir{right:0;justify-content:flex-end;padding-right:3px;}
.nav-mobile-wrap.nm-sem-esq .nav-scroll-btn-esq{opacity:0;pointer-events:none;}
.nav-mobile-wrap.nm-sem-dir .nav-scroll-btn-dir{opacity:0;pointer-events:none;}
.nb{display:flex;align-items:center;gap:6px;padding:13px 15px;border:none;background:transparent;font-family:inherit;font-size:13px;font-weight:600;color:var(--p-sub);cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-2px;transition:color .12s,border-color .12s;white-space:nowrap;}
.nb:hover{color:var(--p-blue);}
.nb.on{color:var(--p-blue);border-bottom-color:var(--p-blue);}
.nb .ico{width:15px;height:15px;}

/* ============================================================================
   Sidebar vertical — só ≥769px, substitui .hd-mobile/.nav-mobile nesse
   breakpoint. Ver docs/HANDOFF_2026-08-06.md item 1 e comentário do
   Alpine.store('ui') em js/app.js pro comportamento de collapse/preview/pin.

   position:fixed (não absolute — foi o que causou o bug de sobreposição na
   exploração anterior) com um "espaçador" puramente via margin-left em
   .hd-desk/.body-wrap lendo --sb-w. --sb-w só muda com sbCollapsed (estado
   persistido); o preview de hover troca a LARGURA VISUAL da própria .sb sem
   tocar --sb-w — por isso ela flutua por cima do conteúdo em vez de empurrar.
   ============================================================================ */
.sb{
  display:none;position:fixed;top:0;left:0;bottom:0;width:var(--sb-full);
  background:var(--p-blue-ink);z-index:210;flex-direction:column;overflow:hidden;
  transition:width .2s cubic-bezier(0.4, 0, 0.2, 1);
}
.sb.collapsed{width:var(--sb-rail);}
.sb.collapsed.preview{width:var(--sb-full);box-shadow:8px 0 24px rgba(0,0,0,.4);}

/* min-height (era height fixo): nada fora daqui depende da altura exata do
   .sb-top — ele é só a seção de topo DENTRO da sidebar fixa (.sb, altura
   100vh), então deixar crescer quando o nome da organização quebra em 2
   linhas é seguro (diferente do .hd-mobile acima, que é compartilhado com
   outros componentes que assumem 56px). */
.sb-top{display:flex;align-items:center;gap:8px;padding:10px 16px;min-height:56px;flex-shrink:0;border-bottom:1px solid rgba(255,255,255,.08);}
/* Mesmo pedido do Felipe do .logo-img acima, versão sidebar (24→36px — levemente menor
   que a versão topbar porque .sb-top tem menos altura de sobra que .hd). */
.sb-logo-img{height:36px;width:auto;object-fit:contain;flex-shrink:0;}
.sb-logo-t{font-size:17px;line-height:1.15;font-weight:800;font-style:italic;letter-spacing:-.5px;color:#fff;white-space:normal;overflow-wrap:normal;word-break:normal;max-width:150px;}
/* Clique pra trocar a logo (só o dono vê o cursor/hover — ver :class
   podeEditarLogo no index.html). O anel tracejado só aparece no hover pra não
   sugerir "botão" o tempo todo; sutil de propósito, é uma ação secundária. */
.sb-logo-clic,.logo-clic{cursor:pointer;border-radius:6px;transition:box-shadow .12s;}
.sb-logo-clic:hover,.logo-clic:hover{box-shadow:0 0 0 2px var(--p-yellow);}
.sb.collapsed:not(.preview) .sb-logo-t{display:none;}

.sb-toggle-row{display:flex;justify-content:flex-end;padding:6px 10px;flex-shrink:0;}
.sb.collapsed .sb-toggle-row{justify-content:center;}
.sb-toggle{background:none;border:1px solid rgba(255,255,255,.15);color:rgba(255,255,255,.6);border-radius:6px;width:26px;height:26px;display:flex;align-items:center;justify-content:center;cursor:pointer;transition:all .12s;flex-shrink:0;}
.sb-toggle:hover{color:var(--p-yellow);border-color:var(--p-yellow);}
.sb-toggle .ico{width:14px;height:14px;transition:transform .2s cubic-bezier(0.4, 0, 0.2, 1);}
.sb.collapsed .sb-toggle .ico{transform:rotate(180deg);}

.sb-pin-row{padding:0 12px 8px;flex-shrink:0;}
.sb-pin{display:flex;align-items:center;justify-content:center;gap:6px;width:100%;background:var(--p-yellow);color:var(--p-blue-ink);border:none;border-radius:6px;padding:7px 10px;font-family:inherit;font-size:11px;font-weight:700;cursor:pointer;white-space:nowrap;}
.sb-pin:hover{opacity:.88;}

.sb-nav{flex:1;overflow-y:auto;overflow-x:hidden;padding-top:6px;}
.sbi{display:flex;align-items:center;gap:12px;width:100%;padding:11px 18px;border:none;border-left:3px solid transparent;background:transparent;color:rgba(255,255,255,.65);font-family:inherit;font-size:13px;font-weight:600;text-align:left;white-space:nowrap;cursor:pointer;transition:background .12s,color .12s,border-color .12s;}
.sbi:hover{background:rgba(255,255,255,.06);color:#fff;}
.sbi.on{color:var(--p-yellow);border-left-color:var(--p-yellow);background:rgba(255,212,0,.08);}
.sbi .ico{flex-shrink:0;width:16px;height:16px;}
.sbi-lbl{overflow:hidden;text-overflow:ellipsis;}
.sb.collapsed:not(.preview) .sbi{justify-content:center;padding:11px 0;}
.sb.collapsed:not(.preview) .sbi-lbl{display:none;}

/* Submenu inline (acordeão) — hoje só usado pelo item "Financeiro" da sidebar
   desktop, ver web/js/app.js Alpine.store('ui').submenuFinanceiroOpen e
   web/index.html. .sbi-group envolve o botão pai + o painel de sub-itens;
   a abertura/fechamento anima via max-height (sem plugin @alpinejs/collapse,
   que não está carregado neste projeto — ver comentário em app.js).
   Em modo rail (sidebar recolhida, fora do hover-preview) o submenu e a seta
   ficam ocultos e o clique no ícone navega direto pro Financeiro (aba
   corrente) em vez de abrir o acordeão — flyout ao lado do rail fica pra uma
   iteração futura (ver docs/HANDOFF ou commit desta mudança). */
.sbi-group{display:flex;flex-direction:column;}
.sbi-caret{margin-left:auto;flex-shrink:0;transition:transform .2s cubic-bezier(0.4, 0, 0.2, 1);}
.sbi.open .sbi-caret{transform:rotate(180deg);}
.sb.collapsed:not(.preview) .sbi-caret{display:none;}
.sbi-sub{
  display:flex;flex-direction:column;overflow:hidden;max-height:0;
  padding:0 0 0 12px;background:rgba(0,0,0,.15);
  transition:max-height .2s cubic-bezier(0.4, 0, 0.2, 1),padding .2s cubic-bezier(0.4, 0, 0.2, 1);
}
/* 320px (era 220px): folga pra caber os 2 grupos + rótulos verticais + 2
   divisores da reorganização Receitas/Gastos abaixo — max-height só limita o
   teto da transição, sobrar espaço não estica a caixa além do conteúdo real. */
.sbi-sub.open{max-height:320px;padding:4px 0 8px 12px;}
.sb.collapsed:not(.preview) .sbi-sub{display:none;}
.sbi-sub-item{
  display:flex;align-items:center;gap:8px;width:100%;
  padding:8px 12px 8px 32px;color:rgba(255,255,255,.7);
  background:transparent;border:none;cursor:pointer;
  font-family:inherit;font-size:13px;font-weight:600;text-align:left;
  transition:background .12s,color .12s;
}
.sbi-sub-item:hover{background:rgba(255,255,255,.05);color:#fff;}

/* Agrupamento visual Financeiro v2 (Receitas / Gastos) dentro do submenu lateral —
   pedido do Felipe a partir de mockup grosseiro (dele: "desconsidere cores e
   proporções, é só pra demonstrar a ideia"): rótulo lateral vertical por grupo +
   cor semântica + divisor entre grupos; Extrato fica fora dos dois, sem rótulo.
   Rótulo usa a mesma técnica já empregada em web/os-imprimir.html pros blocos
   .bloco-lbl/.apar-lbl/.of-lbl/.via-lbl: writing-mode:vertical-rl + rotate(180deg),
   que faz o texto ler de baixo pra cima (letra de baixo primeiro) — mais natural
   pra rótulo lateral esquerdo do que vertical-rl puro (que leria de cima pra
   baixo). Tipografia do rótulo segue o padrão de "section label" já usado no
   app (.fsec/.kpi-lbl/.flbl: bold+uppercase+letter-spacing).
   Cor só com os tokens semânticos existentes (--p-ok/--p-err — os mesmos de
   .ok/.err), sem cor nova. Só a cor de TEXTO (sem --p-ok-bg/--p-err-bg como
   fundo aqui): a sidebar (.sb) é sempre fundo azul-marinho escuro,
   independente do tema claro/escuro do app — e --p-ok-bg/--p-err-bg no tema
   claro são sólidos e claros (pensados pra ficar sobre --p-surface), não
   translúcidos; um fundo desses aqui destoaria da sidebar escura. O divisor
   usa rgba(255,255,255,...) fixo pelo mesmo motivo, seguindo o padrão já usado
   em .sb-top/.sb-brand (bordas claras fixas sobre fundo sempre escuro). */
.sbi-sub-grupo{display:flex;align-items:stretch;}
.sbi-sub-grupo-lbl{
  writing-mode:vertical-rl;transform:rotate(180deg);
  flex-shrink:0;width:16px;display:flex;align-items:center;justify-content:center;
  font-size:9px;font-weight:800;letter-spacing:.4px;text-transform:uppercase;
}
.sbi-sub-grupo-receitas .sbi-sub-grupo-lbl{color:var(--p-ok);}
.sbi-sub-grupo-gastos .sbi-sub-grupo-lbl{color:var(--p-err);}
.sbi-sub-grupo-itens{display:flex;flex-direction:column;flex:1;min-width:0;}
/* 14px em vez dos 32px padrão de .sbi-sub-item: o rótulo lateral já ocupa o
   papel de indentação/hierarquia visual; resultado fica alinhado com o ícone
   do Extrato (fora do grupo, sem rótulo, que mantém os 32px padrão). */
.sbi-sub-grupo .sbi-sub-item{padding-left:14px;}
.sbi-sub-div{height:1px;margin:4px 12px;background:rgba(255,255,255,.12);flex-shrink:0;}

/* Item de menu "em desenvolvimento" (Cupom Fiscal/NF-e) — pedido do Felipe: em
   vez de esconder a funcionalidade, mostra o item com fonte mais apagada
   (menos contraste que os itens normais, mas ainda legível) pra sinalizar sem
   precisar de um badge. Continua clicável — o clique mostra um toast "em
   desenvolvimento" (ver avisarEmDesenvolvimento() em js/app.js), não navega
   pra lugar nenhum, então NUNCA deveria ganhar a classe .on (nav() só marca
   .on quando data-pagina bate — este botão não tem data-pagina de propósito). */
.sbi-soon{color:rgba(255,255,255,.38);}
.sbi-soon:hover{color:rgba(255,255,255,.7);background:rgba(255,255,255,.06);}
/* Bug (achado do Felipe, 2026-08-12): estava rgba(255,255,255,.4) fixo, pensado só pro
   tema escuro. .nb vive dentro de .nav, que usa var(--p-surface) — troca pra branco no
   tema claro — então texto branco a 40% de opacidade ficava quase invisível ali (o item
   "Cupom Fiscal / NF-e" sumia). opacity em vez de color força um valor fixo: dessaturado
   sobre a cor que .nb já tem (var(--p-sub), que o próprio tema já resolve certo em cada
   modo), então funciona igual nos dois temas sem precisar de mais uma regra [data-theme]. */
.nb-soon{opacity:.55;}
.nb-soon:hover{opacity:.9;}

/* Marca RepForm no rodapé da sidebar — só aparece no layout sidebar (a marca
   do PRODUTO, não a da organização, que já ocupa o topo — ver .sb-top acima e
   img/README-logo.md pro porquê dos dois nunca dividirem o mesmo lugar).
   flex-shrink:0 pra não ser espremida pelo .sb-nav (flex:1) quando a lista de
   módulos é longa; fica sempre visível, colada no fim da coluna.
   icon-mark-dark-trim.png é o glifo BRANCO recortado (o nome "dark" é do tom
   do glifo, não do fundo — ver README-logo.md) — mesmo asset que o login usa
   no painel escuro, já pronto pra esse fundo, sem precisar de filtro. */
.sb-brand{flex-shrink:0;display:flex;align-items:center;justify-content:center;gap:6px;padding:10px 12px;border-top:1px solid rgba(255,255,255,.08);opacity:.4;}
.sb-brand img{height:14px;width:auto;}
.sb-brand span{font-size:10px;font-weight:700;font-style:italic;letter-spacing:-.2px;color:#fff;white-space:nowrap;}
.sb.collapsed:not(.preview) .sb-brand span{display:none;}
.sb.collapsed:not(.preview) .sb-brand{padding:10px 0;}

/* Equivalente mobile — ver docs do commit para o porquê de ser um dropdown
   simples (posicionado sobre o conteúdo) em vez do acordeão inline da
   sidebar desktop: o .nav mobile é uma barra horizontal com scroll, não tem
   espaço vertical pra expandir um item embutido no fluxo. */
.nb-group{position:relative;display:flex;flex-shrink:0;}
.nb-caret{margin-left:4px;transition:transform .2s cubic-bezier(0.4, 0, 0.2, 1);}
.nb.open .nb-caret{transform:rotate(180deg);}
/* position:FIXED, não absolute: `.nav` acima tem overflow-x:auto (pro nav caber em
   tela estreita) e `overflow-x:auto` implica `overflow-y:auto` — um filho absoluto
   fica recortado pela altura da barra e o submenu simplesmente não aparecia no
   layout topbar. Fixed escapa do recorte; as coordenadas top/left vêm do
   Alpine.store('ui').submenuFinanceiroPos (medidas do botão na hora do clique). */
.nb-sub{
  position:fixed;min-width:200px;z-index:250;
  display:flex;flex-direction:column;overflow:hidden;max-height:0;opacity:0;
  background:var(--p-surface);border:1px solid var(--p-border);border-radius:var(--r);box-shadow:var(--sh);
  transition:max-height .2s cubic-bezier(0.4, 0, 0.2, 1),opacity .15s;
}
/* 320px (era 220px) e min-width 200px (era 190px): folga pra caber os 2
   grupos + rótulos verticais + 2 divisores da reorganização Receitas/Gastos
   abaixo — ver .nb-sub-grupo. max-height só limita o teto da transição. */
.nb-sub.open{max-height:320px;opacity:1;}
.nb-sub-item{
  display:flex;align-items:center;gap:8px;width:100%;
  padding:10px 14px;color:var(--p-text);
  background:transparent;border:none;cursor:pointer;
  font-family:inherit;font-size:13px;font-weight:600;text-align:left;
  transition:background .12s,color .12s;
}
.nb-sub-item:hover{background:var(--p-bg);color:var(--p-blue);}

/* Agrupamento visual Financeiro v2 (Receitas / Gastos) dentro do dropdown do
   topbar mobile — mesma ideia/técnica do equivalente na sidebar (ver
   .sbi-sub-grupo acima pro raciocínio completo). Aqui o fundo do dropdown
   (.nb-sub) É var(--p-surface), que troca com o tema — por isso dá pra usar
   o par cor+fundo completo dos tokens semânticos (--p-ok/--p-ok-bg,
   --p-err/--p-err-bg), igual ao badge .ok/.err, sem o problema de opacidade
   que a sidebar sempre-escura tem. */
.nb-sub-grupo{display:flex;align-items:stretch;}
.nb-sub-grupo-lbl{
  writing-mode:vertical-rl;transform:rotate(180deg);
  flex-shrink:0;width:20px;display:flex;align-items:center;justify-content:center;
  font-size:9px;font-weight:800;letter-spacing:.4px;text-transform:uppercase;
}
.nb-sub-grupo-receitas .nb-sub-grupo-lbl{color:var(--p-ok);background:var(--p-ok-bg);}
.nb-sub-grupo-gastos .nb-sub-grupo-lbl{color:var(--p-err);background:var(--p-err-bg);}
.nb-sub-grupo-itens{display:flex;flex-direction:column;flex:1;min-width:0;}
.nb-sub-div{height:1px;margin:4px 10px;background:var(--p-border);flex-shrink:0;}

/* Header slim de desktop (.hd-desk) — mesma classe .hd de base (fundo, altura,
   sticky), só sem os elementos de logo (que moram na sidebar em telas grandes). */
.hd-desk{display:none;}
.hd-desk .logo-img,.hd-desk .logo-t{display:none;}

.body-wrap{display:flex;min-height:calc(100vh - 96px);position:relative;}
.main{flex:1;padding:20px;min-width:0;transition:margin-right .2s;}
.main.shifted{margin-right:var(--panel-w);}

.panel{position:fixed;top:56px;right:calc(-1 * var(--panel-w));bottom:0;width:var(--panel-w);background:var(--p-surface);border-left:1px solid var(--p-border);box-shadow:-4px 0 20px rgba(0,0,0,.1);z-index:150;transition:right .2s ease-out;display:flex;flex-direction:column;overflow:hidden;}
.panel.open{right:0;}
.panel-hd{padding:15px 20px;border-bottom:1px solid var(--p-border);display:flex;align-items:center;justify-content:space-between;flex-shrink:0;gap:10px;}
.panel-title{font-size:15px;font-weight:800;display:flex;align-items:center;gap:8px;}
.panel-close{background:none;border:none;cursor:pointer;color:var(--p-sub);padding:4px;display:flex;}
.panel-body{flex:1;overflow-y:auto;padding:16px 20px;}
.panel-footer{padding:14px 20px;border-top:1px solid var(--p-border);flex-shrink:0;display:flex;gap:8px;}
.overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,.2);z-index:149;}
.overlay.on{display:block;}

/* Ficha do cliente (web/modules/clientes.html, #pnl-ficha) — painel mais largo que o padrão
   .panel (var(--panel-w), 470px) porque acumula cabeçalho + KPIs + 3 abas de tabela; só
   ≥769px, porque o media query mobile logo abaixo (max-width:768px) já força TODO .panel a
   width:100%, e essa regra tem que vencer sem depender de ordem de cascata (min-width evita
   colidir com o breakpoint mobile). */
@media(min-width:769px){
  #pnl-ficha.panel{width:640px;right:-640px;}
  #pnl-ficha.panel.open{right:0;}
}
.ficha-hd{display:flex;align-items:center;gap:12px;margin-bottom:16px;}
.ficha-avatar{width:52px;height:52px;border-radius:50%;background:var(--p-blue-bg);color:var(--p-blue);display:flex;align-items:center;justify-content:center;flex-shrink:0;}
.ficha-nome{font-size:16px;font-weight:800;color:var(--p-text);}
.ficha-sub{font-size:12px;color:var(--p-sub);margin-top:2px;}
.ficha-foot-note{font-size:11px;color:var(--p-sub);text-align:center;padding:10px 0 2px;}

.card{background:var(--p-surface);border:1px solid var(--p-border);border-radius:var(--r);box-shadow:var(--sh);}
.dash-period-lbl{display:flex;align-items:center;gap:6px;font-size:12px;color:var(--p-sub);margin-bottom:10px;}
.dash-period-lbl strong{color:var(--p-blue-deep);}
.kpi-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(160px,1fr));gap:12px;margin-bottom:18px;}
.kpi{background:var(--p-surface);border:1px solid var(--p-border);border-radius:var(--r);padding:16px;box-shadow:var(--sh);position:relative;}
.kpi::after{content:'';position:absolute;left:14px;right:14px;bottom:0;height:2.5px;border-radius:2px;background:var(--kpi-glow,var(--p-blue));opacity:.75;}
.kpi.glow-ok::after{--kpi-glow:var(--p-ok);}
.kpi.glow-warn::after{--kpi-glow:var(--p-warn);}
.kpi.glow-err::after{--kpi-glow:var(--p-err);}
.kpi-lbl{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--p-sub);margin-bottom:5px;display:flex;align-items:center;gap:5px;}
.kpi-val{font-size:24px;font-weight:800;line-height:1;font-variant-numeric:tabular-nums;}
.kpi-sub{font-size:11px;color:var(--p-sub);margin-top:3px;}

/* Hierarquia visual dos KPIs do Dashboard — Auditoria adversarial v2, achado Médio
   "9 KPIs com mesmo peso visual". Os 3 decisórios (Faturamento/Lucro bruto/Resultado)
   usam .kpi-hero: fundo escuro com gradiente + valor maior, pro olho pousar ali primeiro.
   Os demais (Ticket médio + contadores operacionais) usam .kpi-sec: cards menores,
   numa faixa própria separada por borda (.kpi-row-sec). Ver web/modules/dashboard.html. */
.kpi-row-hero{grid-template-columns:repeat(auto-fit,minmax(200px,1fr));}

/* O card hero é o ÚNICO componente do app que inverte de claridade junto com o tema, e
   por um motivo: ele destaca por CONTRASTE com os cards vizinhos, não por cor própria.
   No tema claro os vizinhos são brancos, então ele é escuro. No tema escuro os vizinhos
   viram escuros — se ele continuasse escuro, deixaria de destacar (foi o que o Felipe
   apontou ao ver a primeira versão do modo escuro). Então ele vira claro.

   Tudo que muda entre os dois está nestes tokens; nenhuma regra abaixo repete cor.

   Degradê invertido no tema claro (180°, começa no azul vivo e termina no marinho):
   o de antes ia de escuro pra claro em 155°, ou seja, terminava no tom MAIS CLARO
   exatamente onde a barra de acento encosta — era por isso que a barra sumia, e não
   por falta de brilho nela. */
:root{
  --hero-bg:#0A2247;
  --hero-borda:#0A2247;
  --hero-val:#fff;
  --hero-lbl:rgba(255,255,255,.72);
  --hero-sub:rgba(255,255,255,.68);
  --hero-ajuda:rgba(255,255,255,.75);
  --hero-ajuda-hover-bg:var(--p-yellow);
  --hero-ajuda-hover-txt:var(--p-blue-ink);
  /* Acento da barra inferior. No claro é o amarelo da marca; os semânticos são os
     mesmos --p-* de sempre (ver .kpi-hero.glow-* mais abaixo). */
  --hero-acento:var(--p-yellow);
  --hero-acento-glow:rgba(255,212,0,.45);
}
[data-theme="dark"]{
  /* "Branco quente": #F5F7FA em vez de #FFFFFF puro. Quatro retângulos de branco puro
     sobre página quase preta viram lanterna na cara à noite; este tom dá o mesmo salto
     de destaque com menos brilho. */
  --hero-bg:#F5F7FA;
  --hero-borda:#DDE3EC;
  --hero-val:#16202E;
  --hero-lbl:#5B6879;
  --hero-sub:#5B6879;
  --hero-ajuda:#5B6879;
  --hero-ajuda-hover-bg:var(--p-blue-deep);
  --hero-ajuda-hover-txt:#fff;
  /* Amarelo sobre branco quente é ilegível — no escuro o acento padrão vira o azul da
     marca. Mesmo motivo vale pros semânticos: no tema escuro os --p-ok/warn/err são
     versões CLARAS (pensadas pra fundo escuro) e sumiriam aqui, então o hero usa a
     paleta do tema claro (ver .kpi-hero.glow-* mais abaixo). */
  --hero-acento:var(--p-blue-deep);
  --hero-acento-glow:rgba(18,74,153,.35);
}

.kpi-hero{background:var(--hero-bg);border-color:var(--hero-borda);}
.kpi-hero .kpi-lbl{color:var(--hero-lbl);}
.kpi-hero .kpi-val{font-size:30px;letter-spacing:.4px;color:var(--hero-val);}
.kpi-hero .kpi-sub{color:var(--hero-sub);}
.kpi-hero .ajuda{background:transparent;color:var(--hero-ajuda);}
.kpi-hero .ajuda:hover,.kpi-hero .ajuda:focus,.kpi-hero .ajuda-aberta{background:var(--hero-ajuda-hover-bg);border-color:var(--hero-ajuda-hover-bg);color:var(--hero-ajuda-hover-txt);}

/* A barrinha inferior (.kpi::after) some contra o fundo azul escuro do card hero: o
   gradiente nasce transparente nas pontas e o azul-sobre-azul-marinho não tem contraste
   nenhum. Nos cards hero ela vira amarela (a cor de destaque da marca), mais alta, opaca
   e com um brilho por baixo — é a "linha vívida" que o Apps Script original tem e que
   faltava aqui. Vale só pros cards de fundo azul; os cards brancos seguem sutis. */
.kpi-hero::after{
  left:0;right:0;height:3px;border-radius:0;opacity:1;
  background:var(--kpi-glow,var(--hero-acento));
  box-shadow:0 0 10px 1px var(--hero-acento-glow);
}
.kpi-hero.glow-ok::after{box-shadow:0 0 10px 1px rgba(30,142,62,.5);}
.kpi-hero.glow-err::after{box-shadow:0 0 10px 1px rgba(217,48,37,.5);}
.kpi-hero.glow-warn::after{box-shadow:0 0 10px 1px rgba(232,113,10,.5);}
/* No tema escuro os --p-ok/--p-warn/--p-err são as versões claras (feitas pra fundo
   escuro) e desapareceriam sobre o card branco quente. Aqui o hero volta pra paleta
   forte do tema claro — é o único lugar do app onde isso acontece, porque é o único
   card que anda na contramão do tema. */
[data-theme="dark"] .kpi-hero.glow-ok::after{--kpi-glow:#1E8E3E;}
[data-theme="dark"] .kpi-hero.glow-warn::after{--kpi-glow:#E8710A;}
[data-theme="dark"] .kpi-hero.glow-err::after{--kpi-glow:#D93025;}

.kpi-row-sec{border-top:1px solid var(--p-border);padding-top:16px;margin-top:-6px;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));}
.kpi-sec{padding:12px;}
.kpi-sec .kpi-val{font-size:20px;}

.badge{display:inline-block;padding:1px 7px;border-radius:4px;font-size:10px;font-weight:700;}
.ok{color:var(--p-ok);background:var(--p-ok-bg);}.warn{color:var(--p-warn);background:var(--p-warn-bg);}.err{color:var(--p-err);background:var(--p-err-bg);}
/* Auditoria adversarial v2 (os.html): 2 cores extras pro status de OS, além de ok/warn/err —
   ver statusClasse() em os.html. */
.aguardando{color:var(--p-violet);background:var(--p-violet-bg);}
.pronto{color:var(--p-blue);background:var(--p-blue-bg);}

.tb{display:flex;align-items:center;gap:8px;margin-bottom:14px;flex-wrap:wrap;}
.tb-right{margin-left:auto;}
.search-in{padding:7px 11px;border-radius:var(--r);border:1.5px solid var(--p-border);font-family:inherit;font-size:13px;outline:none;width:240px;}
.search-in:focus{border-color:var(--p-blue);}
.chip{padding:4px 12px;border-radius:6px;font-size:11px;font-weight:600;border:1.5px solid var(--p-border);background:var(--p-surface);color:var(--p-sub);cursor:pointer;transition:all .12s;white-space:nowrap;}
.chip:hover{border-color:var(--p-blue);color:var(--p-blue);}
.chip.on{background:var(--p-blue);border-color:var(--p-blue);color:#fff;}

/* Abas de Cadastros agrupadas em blocos por afinidade (pedido do Felipe, 2026-08-12) —
   mesmo agrupamento/cores dos dropdowns "Cadastros" da sidebar e do menu horizontal (ver
   .nb-sub-grupo/.sbi-sub-grupo mais abaixo): vermelho pra Aparelho/Estoque, verde pra
   Financeiro. Layout horizontal (não vertical como nos dropdowns) — o rótulo do grupo vira
   uma pílula pequena à esquerda dos chips, em vez de faixa lateral com writing-mode. */
.tb-catalogos{row-gap:10px;}
.tb-grupo{display:flex;align-items:center;flex-wrap:wrap;gap:8px;}
.tb-grupo-lbl{
  font-size:9px;font-weight:800;letter-spacing:.4px;text-transform:uppercase;
  padding:4px 9px;border-radius:4px;flex-shrink:0;white-space:nowrap;
}
.tb-grupo-aparelho .tb-grupo-lbl{color:var(--p-err);background:var(--p-err-bg);}
.tb-grupo-financeiro .tb-grupo-lbl{color:var(--p-ok);background:var(--p-ok-bg);}
.tb-grupo-div{width:1px;align-self:stretch;min-height:20px;background:var(--p-border);}

.seg{display:flex;border:1.5px solid var(--p-border);border-radius:var(--r);overflow:hidden;margin-bottom:14px;}
.seg button{flex:1;display:flex;align-items:center;justify-content:center;gap:6px;padding:9px;background:var(--p-surface);border:none;cursor:pointer;font-size:12px;font-weight:700;color:var(--p-sub);transition:all .12s;font-family:inherit;}
.seg button+button{border-left:1.5px solid var(--p-border);}
.seg button.on{background:var(--p-blue);color:#fff;}
/* Variante compacta — cabe numa linha de título de card (ex: agrupamento
   dia/semana/mês/ano do gráfico Faturamento×Lucro no dashboard), sem a
   margem inferior padrão do .seg (que é pensada pra ficar sozinho num form). */
.seg-sm{margin-bottom:0;flex-shrink:0;}
.seg-sm button{padding:5px 9px;font-size:11px;}

/* Aviso "não segue o filtro de período" (dashboard.html, Últimas Ordens de Serviço)
   — texto discreto ao lado do título, mesmo espírito do .proj-badge do card de
   projeção, reduzido pra caber numa linha comum. */
.dash-nao-filtra{display:inline-flex;align-items:center;gap:4px;margin-left:auto;font-size:11px;color:var(--p-sub);cursor:help;}

.btn{display:inline-flex;align-items:center;gap:5px;padding:8px 14px;border:none;border-radius:var(--r);font-family:inherit;font-size:13px;font-weight:600;cursor:pointer;transition:opacity .12s,transform .1s;white-space:nowrap;}
.btn:active{transform:scale(.97);}
.btn:disabled{opacity:.45;cursor:not-allowed;}
.btn-primary{background:var(--p-yellow);color:var(--p-blue-ink);}
.btn-primary:hover{opacity:.88;}
.btn-sec{background:var(--p-surface);color:var(--p-blue);border:1.5px solid var(--p-blue);}
.btn-sec:hover{background:var(--p-bg);}
.btn-ghost{background:var(--p-bg);color:var(--p-sub);border:1.5px solid var(--p-border);}
.btn-sm{padding:4px 9px;font-size:11px;}
.btn-full{width:100%;justify-content:center;padding:10px;}
/* Botões do modal de confirmação (achado P2 #16) — vermelho pra ação destrutiva
   (excluir), laranja pra aviso brando (ex: duplicata) que ainda merece confirmação
   mas não apaga nada. Mesmo padrão visual de .btn-primary (cor sólida, texto claro). */
.btn-err{background:var(--p-err);color:#fff;}
.btn-err:hover{opacity:.88;}
.btn-warn{background:var(--p-warn);color:#fff;}
.btn-warn:hover{opacity:.88;}

.tbl-wrap{border-radius:var(--r);border:1px solid var(--p-border);overflow:hidden;box-shadow:var(--sh);background:var(--p-surface);overflow-x:auto;}
.tbl{width:100%;border-collapse:collapse;font-size:13px;}
.tbl thead tr{background:var(--p-blue);}
.tbl th{padding:10px 12px;text-align:left;font-weight:700;font-size:10px;text-transform:uppercase;letter-spacing:.5px;color:#fff;white-space:nowrap;}
.tbl td{padding:9px 12px;border-bottom:1px solid var(--p-border);vertical-align:middle;}
.tbl tr:last-child td{border-bottom:none;}
.tbl tbody tr:hover td{background:var(--p-row-hover);}
.tbl-click tbody tr{cursor:pointer;}
.tbl-mono{font-family:monospace;font-weight:700;}
.tbl-right{text-align:right;}.tbl-center{text-align:center;}
/* `.tbl th` (1 classe + 1 tag) tem especificidade maior que `.tbl-right` (1 classe) e
   vencia com seu text-align:left — resultado: TODO cabeçalho marcado como .tbl-right/
   .tbl-center ficava alinhado à esquerda enquanto os valores da coluna iam pra direita/
   centro. Era esse o desalinhamento visível na tela de OS (o "AÇÕES" longe dos botões).
   Estas duas regras igualam a especificidade a favor do alinhamento pedido. */
.tbl th.tbl-right{text-align:right;}
.tbl th.tbl-center{text-align:center;}
/* Linha de subtotal no topo do cabeçalho (Vendas Avulsas, Despesas — pedido do Felipe,
   2026-08-12): fica dentro do <thead>, acima da linha de rótulos, só pra herdar o
   alinhamento automático de coluna da tabela (mesma <th>/classe tbl-right/tbl-center de
   sempre). Não é "mais um cabeçalho" — sobrescreve o fundo azul/maiúsculas herdado de
   `.tbl thead tr`/`.tbl th` pra ler como um destaque leve (negrito, fonte um pouco maior
   que o corpo 13px da tabela), não como cards nem como um segundo título. */
.tbl thead tr.tbl-subtotal{background:var(--p-surface);}
.tbl thead tr.tbl-subtotal th{
  color:var(--p-text);font-size:14px;font-weight:700;text-transform:none;
  letter-spacing:normal;border-bottom:1px solid var(--p-border);
}
/* "Ticket com furo" — o número da OS como uma etiqueta de balcão furada, que é a
   metáfora física do negócio (o papelzinho que fica preso ao aparelho). Sobreviveu
   da exploração de redesenho de 05/08 (ver docs/PROXIMOS_PASSOS.md); aqui é o
   componente de verdade, com os tokens do app em vez das variáveis do protótipo.
   O "furo" é o círculo vazado da esquerda (.tstub-h). */
/* color:var(--p-text), não var(--p-blue-ink): --p-blue-ink é "azul-marinho pra
   texto sobre fundo CLARO" — no tema escuro ele vira #0B1524, quase idêntico ao
   próprio --p-bg (#0E141D) da página, e o número da OS ficava invisível até
   passar o mouse (que força color:var(--p-blue) no hover). --p-text já é o token
   que acompanha o tema nos dois sentidos (escuro sobre claro / claro sobre
   escuro), então resolve sem precisar de uma regra extra por tema. */
.tstub{display:inline-flex;align-items:center;gap:7px;background:var(--p-bg);border:1.5px solid var(--p-border);border-radius:5px;padding:3px 9px 3px 8px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-weight:700;font-size:12px;letter-spacing:.03em;color:var(--p-text);white-space:nowrap;box-shadow:1px 1px 0 rgba(13,43,82,.07);}
.tstub-h{width:7px;height:7px;border-radius:50%;border:1.5px solid var(--p-sub);background:var(--p-surface);flex-shrink:0;}
.tbl tbody tr:hover .tstub{border-color:var(--p-blue);color:var(--p-blue);}
.tstub-big{font-size:14px;padding:5px 12px 5px 10px;}
.tstub-big .tstub-h{width:9px;height:9px;}

.th-sort{cursor:pointer;user-select:none;white-space:nowrap;}
.th-sort:hover{background:rgba(255,255,255,.12);}
.sort-ico{font-size:8px;margin-left:4px;}

/* ============================================================================
   Cabeçalho de grid — ordenar clicando no título + funil de filtro por coluna
   (ver criarGrid() em js/app.js). O conteúdo do <th> vira um flex de três partes:
   rótulo, seta de ordenação e botão do funil.
   ============================================================================ */
.th-grid{cursor:pointer;user-select:none;white-space:nowrap;position:relative;}
.th-grid:hover{background:rgba(255,255,255,.12);}
.th-grid-in{display:flex;align-items:center;gap:5px;}
.th-grid.tbl-right .th-grid-in{justify-content:flex-end;}
.th-grid.tbl-center .th-grid-in{justify-content:center;}
.th-seta{font-size:8px;opacity:.9;}
/* O funil fica fantasma até a coluna ter filtro ou o mouse passar — cabeçalho com
   dez ícones sempre acesos vira ruído e some com o nome da coluna. */
.th-funil{display:inline-flex;align-items:center;justify-content:center;width:18px;height:18px;border:none;border-radius:4px;background:transparent;color:#fff;opacity:0;cursor:pointer;padding:0;flex-shrink:0;transition:opacity .12s,background .12s;}
.th-grid:hover .th-funil,.th-funil:focus{opacity:.7;}
.th-funil:hover{background:rgba(255,255,255,.22);opacity:1;}
.th-funil.on{opacity:1;background:var(--p-yellow);color:var(--p-blue-ink);}
.th-funil .ico-sm{width:12px;height:12px;}

/* position:fixed pelo mesmo motivo do submenu do topo: .tbl-wrap tem overflow-x:auto
   e recortaria um popover absoluto. Coordenadas vêm de gridPopoverPos. */
.grid-pop{position:fixed;z-index:260;width:230px;max-height:330px;display:flex;flex-direction:column;background:var(--p-surface);border:1px solid var(--p-border);border-radius:var(--r);box-shadow:0 8px 28px rgba(0,0,0,.18);overflow:hidden;}
.grid-pop-top{padding:8px;border-bottom:1px solid var(--p-border);display:flex;flex-direction:column;gap:6px;}
.grid-pop-busca{width:100%;padding:6px 9px;border:1.5px solid var(--p-border);border-radius:6px;font-family:inherit;font-size:12px;background:var(--p-surface);color:var(--p-text);outline:none;}
.grid-pop-busca:focus{border-color:var(--p-blue);}
.grid-pop-acoes{display:flex;gap:6px;}
.grid-pop-acoes button{flex:1;border:1px solid var(--p-border);background:var(--p-bg);color:var(--p-sub);border-radius:6px;padding:4px 6px;font-family:inherit;font-size:11px;font-weight:600;cursor:pointer;}
.grid-pop-acoes button:hover{border-color:var(--p-blue);color:var(--p-blue);}
.grid-pop-lista{overflow-y:auto;padding:4px 0;}
.grid-pop-item{display:flex;align-items:center;gap:7px;padding:5px 10px;font-size:12px;color:var(--p-text);cursor:pointer;}
.grid-pop-item:hover{background:var(--p-row-hover);}
.grid-pop-item input{cursor:pointer;flex-shrink:0;accent-color:var(--p-blue);}
.grid-pop-item span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;}
.grid-pop-item .so{margin-left:auto;font-size:10px;font-weight:700;color:var(--p-blue);opacity:0;flex-shrink:0;}
.grid-pop-item:hover .so{opacity:1;}
.grid-pop-nota{padding:6px 10px;font-size:10px;color:var(--p-sub);border-top:1px solid var(--p-border);line-height:1.4;}
/* Faixa de "filtros ativos" acima da tabela — sem ela, uma coluna filtrada some da
   vista quando a pessoa rola a tabela pro lado e a tela parece "faltar dados". */
.grid-ativos{display:flex;flex-wrap:wrap;align-items:center;gap:7px;margin-bottom:10px;font-size:12px;color:var(--p-sub);}
.grid-ativo-chip{display:inline-flex;align-items:center;gap:5px;background:var(--p-blue-bg);border:1px solid var(--p-blue);color:var(--p-blue-deep);border-radius:6px;padding:3px 6px 3px 10px;font-size:11px;font-weight:600;}
.grid-ativo-chip button{border:none;background:var(--p-blue);color:#fff;width:15px;height:15px;border-radius:50%;cursor:pointer;font-size:10px;line-height:1;display:flex;align-items:center;justify-content:center;padding:0;}
.grid-limpar{border:none;background:transparent;color:var(--p-err);font-family:inherit;font-size:11px;font-weight:700;cursor:pointer;text-decoration:underline;}

.field{margin-bottom:12px;}
.flbl{display:block;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--p-sub);margin-bottom:4px;}
.fin,.fsel,.fta{width:100%;padding:7px 10px;border:1.5px solid var(--p-border);border-radius:var(--r);font-family:inherit;font-size:13px;color:var(--p-text);background:var(--p-surface);outline:none;transition:border-color .12s;}
.fin:focus,.fsel:focus,.fta:focus{border-color:var(--p-blue);box-shadow:0 0 0 3px rgba(30,111,217,.1);}
/* Campo travado precisa PARECER travado. Sem isto, o e-mail em Configurações › Meu
   perfil ficava idêntico a um campo editável e a pessoa tentava digitar nele. */
.fin:disabled,.fsel:disabled,.fta:disabled{background:var(--p-bg);color:var(--p-sub);cursor:not-allowed;}
/* Peça sem Venda preenchida (os.html) — nudge visual bem sutil pra chamar atenção antes
   de salvar. Vazio = vende de graça (R$0) numa peça NOVA — pedido do Felipe, 2026-08-12,
   reverte a decisão anterior (vazio = vende a custo); pra vender a custo, digita o valor
   do custo direto no campo Venda. Fundo vermelho quase imperceptível, não soa como erro
   de validação (vazio continua sendo um estado válido, só intencional). */
.fin-vazia-leve{background:var(--p-err-bg-leve);}
.fta{resize:vertical;min-height:60px;}
.frow{display:flex;gap:8px;margin-bottom:12px;}
.frow .field{flex:1;margin-bottom:0;}
.fchks{display:flex;gap:12px;flex-wrap:wrap;margin-top:4px;}
.fchk{display:flex;align-items:center;gap:6px;font-size:12px;cursor:pointer;padding:2px 5px;margin:-2px -5px;border-radius:6px;transition:background .12s,color .12s;}
.fchk input{accent-color:var(--p-blue);width:14px;height:14px;cursor:pointer;}
/* Condição de entrada desmarcada (Liga/Bateria OK/Tela OK) — auditoria adversarial v2, achado
   Médio "condições default false" (bônus): destaque visual pra chamar atenção do técnico antes
   de salvar, já que essas condições existem pra proteger loja e cliente numa disputa. */
.fchk-alerta{color:var(--p-err);font-weight:700;background:var(--p-err-bg);}
.fchk-alerta input{accent-color:var(--p-err);}
.fsec{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--p-sub);padding-bottom:5px;border-bottom:1px solid var(--p-border);margin:14px 0 11px;display:flex;align-items:center;gap:6px;}
.hint{font-size:11px;color:var(--p-sub);line-height:1.4;margin:-6px 0 12px;}

/* Combobox (texto livre + sugestões) — ver comboBox() em js/app.js e o molde de
   markup em web/modules/_COMBOBOX.md. .combo-campo é o wrapper posicionado do
   input+ícone; .combo-lista flutua por cima do conteúdo abaixo (position:absolute),
   não empurra layout ao abrir. */
.combo{position:relative;}
.combo-campo{position:relative;}
.combo-campo .fin{padding-right:30px;}
.combo-seta{position:absolute;right:2px;top:50%;transform:translateY(-50%);border:none;background:transparent;color:var(--p-sub);width:26px;height:26px;border-radius:6px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:background .12s,color .12s;}
.combo-seta:hover{background:var(--p-row-hover);color:var(--p-text);}
.combo-lista{position:absolute;top:calc(100% + 4px);left:0;right:0;z-index:30;background:var(--p-surface);border:1px solid var(--p-border);border-radius:var(--r);box-shadow:var(--sh);max-height:220px;overflow-y:auto;padding:4px;}
.combo-item{padding:7px 9px;border-radius:6px;font-size:13px;cursor:pointer;color:var(--p-text);}
.combo-item:hover{background:var(--p-row-hover);}
.combo-item-novo{font-style:italic;color:var(--p-sub);cursor:default;}
.combo-item-novo:hover{background:transparent;}
/* Marca/Modelo pré-preenchidos a partir do último aparelho do cliente (os.html,
   preencherAparelhoDoCliente() — pedido do Felipe: "fonte diferente pra mostrar que foi
   pré-selecionado"). Some sozinho assim que o campo é editado (ver os `set` dos combobox
   de Marca/Modelo, que apagam form._aparelhoAutoPreenchido a cada digitação/escolha). */
.combo-auto .fin{font-style:italic;color:var(--p-sub);}

/* Lista de aparelhos do cliente (clientes.html, migration 0029) — uma linha por
   cliente_aparelhos, com toggle Ativo/Inativo e remover. Inativo fica visualmente apagado
   (histórico, não é o aparelho atual) sem sumir da lista. */
.ap-row{display:flex;align-items:center;gap:10px;padding:7px 9px;border:1px solid var(--p-border);border-radius:var(--r);margin-bottom:6px;background:var(--p-surface);}
.ap-nome{flex:1;font-size:13px;color:var(--p-text);}
.ap-inativo{opacity:.55;}
.ap-inativo .ap-nome{text-decoration:line-through;}

/* Ícone de ajuda "(?)" clicável (estoque.html, Compatibilidade de Películas) — abre um
   .alert-info logo abaixo, não um popover flutuante (mais simples, sem risco de recorte). */
.help-ico{width:20px;height:20px;border-radius:50%;border:1.5px solid var(--p-border);background:transparent;color:var(--p-sub);font-size:11px;font-weight:700;cursor:pointer;display:flex;align-items:center;justify-content:center;flex-shrink:0;transition:background .12s,color .12s,border-color .12s;}
.help-ico:hover{background:var(--p-blue-bg);color:var(--p-blue);border-color:var(--p-blue);}

/* Árvore Marca → Grupo → Modelos (estoque.html, Compatibilidade de Películas) — 2 níveis
   de expand/collapse, nascem fechados (ver comentário de marcasExpandidas/gruposExpandidos
   no <script>). Nível marca em fundo neutro pra separar visualmente do nível grupo, que
   reaproveita o mesmo tom de .card (var(--p-surface)) pra ficar consistente com o resto. */
.tree-marca{border:1px solid var(--p-border);border-radius:var(--r);margin-bottom:8px;overflow:hidden;}
.tree-marca-hd{width:100%;display:flex;align-items:center;gap:8px;padding:10px 14px;background:var(--p-surface);border:none;cursor:pointer;font-size:13px;font-weight:700;color:var(--p-text);text-align:left;font-family:inherit;}
.tree-marca-hd:hover{background:var(--p-row-hover);}
.tree-marca-body{padding:10px 14px 14px;background:var(--p-bg);}
.tree-grupo{border:1px solid var(--p-border);border-radius:var(--r);margin-bottom:8px;background:var(--p-surface);overflow:hidden;}
.tree-grupo:last-child{margin-bottom:0;}
.tree-grupo-hd{display:flex;align-items:center;gap:8px;padding:9px 12px;cursor:pointer;font-size:13px;font-weight:600;color:var(--p-text);}
.tree-grupo-hd:hover{background:var(--p-row-hover);}
.tree-grupo-body{padding:0 12px 12px;}
.tree-caret{transition:transform .15s;color:var(--p-sub);flex-shrink:0;}
.tree-caret.on{transform:rotate(90deg);}
.tree-count{margin-left:auto;font-size:11px;font-weight:600;color:var(--p-sub);background:var(--p-bg);border-radius:4px;padding:2px 8px;flex-shrink:0;}
.tree-marca-hd .tree-count{background:var(--p-bg);}
.tree-grupo-hd .tree-count{background:var(--p-row-hover);}

.alert{padding:9px 12px;border-radius:var(--r);font-size:12px;margin-bottom:12px;display:flex;align-items:flex-start;gap:7px;}
.alert-ok{background:var(--p-ok-bg);color:var(--p-ok);}.alert-warn{background:var(--p-warn-bg);color:var(--p-warn);}
.alert-err{background:var(--p-err-bg);color:var(--p-err);}.alert-info{background:var(--p-blue-bg);color:var(--p-blue);border:1px solid var(--p-border);}

/* Trial / bloqueio de conta (2026-08-12) — ver $store.app.emTrial/contaBloqueada em
   js/app.js e a marcação em index.html (topo do <body>). Banner reaproveita .alert-warn
   acima, só ajusta pra barra de largura cheia; não é sticky de propósito — evita
   acoplar com o top:0/z-index:200 do .hd logo abaixo, que já é sticky por conta própria.
   Overlay é bloqueio de verdade: cobre a tela inteira, z-index acima até do toast (9999). */
.trial-banner{border-radius:0;margin-bottom:0;justify-content:center;gap:14px;flex-wrap:wrap;}
.btn-trial-cta{background:var(--p-ok);color:#fff;border:none;border-radius:100px;padding:6px 16px;font-size:12px;font-weight:700;text-decoration:none;white-space:nowrap;cursor:pointer;flex-shrink:0;}
.btn-trial-cta:hover{filter:brightness(.92);}
.trial-num{font-size:11px;opacity:.75;white-space:nowrap;}
.trial-blocked-overlay{position:fixed;inset:0;background:var(--p-bg);z-index:10000;display:flex;align-items:center;justify-content:center;padding:20px;}
.trial-blocked-card{max-width:380px;text-align:center;display:flex;flex-direction:column;align-items:center;gap:10px;}
.trial-blocked-card .ico-lg{width:40px;height:40px;color:var(--p-warn);margin-bottom:4px;}
.trial-blocked-card h2{font-size:19px;}
.trial-blocked-card p{font-size:13px;color:var(--p-sub);margin-bottom:8px;}
.trial-codigo{width:100%;margin-top:14px;padding-top:14px;border-top:1px solid var(--p-border);text-align:left;}
.trial-codigo label{display:block;font-size:12px;font-weight:600;margin-bottom:6px;}
.trial-codigo-linha{display:flex;gap:8px;}
.trial-codigo-linha input{flex:1;min-width:0;height:40px;padding:0 12px;border:1.5px solid var(--p-border);border-radius:8px;font-size:14px;letter-spacing:.04em;background:var(--p-surface);color:var(--p-text);}
.trial-codigo-linha button{height:40px;padding:0 16px;border:none;border-radius:8px;background:var(--p-blue);color:#fff;font-weight:600;font-size:13px;cursor:pointer;}
.trial-codigo-linha button:disabled{opacity:.6;cursor:default;}
.trial-codigo-erro{font-size:12px;color:var(--p-err);margin-top:6px;}
.btn-trial-sair{background:none;border:none;color:var(--p-sub);font-size:12px;text-decoration:underline;cursor:pointer;margin-top:4px;}

.st-badge{display:inline-flex;padding:2px 8px;border-radius:4px;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.4px;white-space:nowrap;}
/* Status de OS virou <button> pra abrir o popover de troca rápida (auditoria adversarial v2,
   achado Médio "status quick change") — reseta o chrome nativo de botão, mantendo a pílula. */
button.st-badge{border:none;font-family:inherit;cursor:pointer;transition:filter .12s;}
button.st-badge:hover,button.st-badge:focus-visible{filter:brightness(.92);}
button.st-badge:focus-visible{outline:2px solid var(--p-blue);outline-offset:2px;}

/* Popover de troca rápida de status (os.html) — mesmo espírito visual de .nb-sub (fundo,
   borda, sombra), só posicionado via JS (position:fixed) em vez de ancorado num pai relative. */
.status-pop{position:fixed;z-index:260;min-width:190px;max-height:280px;overflow-y:auto;background:var(--p-surface);border:1px solid var(--p-border);border-radius:var(--r);box-shadow:0 10px 30px rgba(0,0,0,.18);padding:4px;display:flex;flex-direction:column;}
.status-pop-item{display:block;width:100%;text-align:left;padding:8px 10px;border:none;background:transparent;border-radius:6px;font-family:inherit;font-size:12px;font-weight:600;color:var(--p-text);cursor:pointer;}
.status-pop-item:hover,.status-pop-item:focus-visible{background:var(--p-bg);}
.status-pop-item.on{background:var(--p-blue);color:#fff;}

.loading{display:flex;align-items:center;justify-content:center;gap:10px;padding:56px;font-size:14px;color:var(--p-sub);}
.spin{width:22px;height:22px;border:3px solid var(--p-border);border-top-color:var(--p-blue);border-radius:50%;animation:spin .65s linear infinite;}
@keyframes spin{to{transform:rotate(360deg);}}
.empty{text-align:center;padding:48px;color:var(--p-sub);}
.load-more{display:flex;flex-direction:column;align-items:center;gap:6px;padding:16px 0 4px;}
.load-more-msg{font-size:12px;color:var(--p-sub);}
.pecas-grid{display:grid;grid-template-columns:1fr 72px 72px 48px 30px;gap:5px;margin-bottom:5px;align-items:end;}
.pecas-grid label{font-size:10px;font-weight:600;color:var(--p-sub);margin-bottom:2px;display:block;}
.tot-row{display:flex;justify-content:space-between;padding:5px 0;border-bottom:1px solid var(--p-border);font-size:12px;}
.tot-row:last-child{border-bottom:none;font-weight:800;font-size:14px;}
.tot-val{font-weight:600;font-variant-numeric:tabular-nums;}

/* Área de toque min. 40x40 (WCAG) — Auditoria adversarial v2, achado Médio "botões
   pequenos demais para toque". min-width/min-height em vez de width/height fixos pra
   não quebrar se o conteúdo (ícone + texto) pedir mais espaço; padding/font mantidos
   pequenos, só a área clicável cresce. margin-right 8px (era 4px) entre Editar/Excluir
   pra reduzir toque errado. */
/* 40x40 de área clicável, 32x32 de caixa desenhada: o quadrado de 40px ao lado de
   uma linha de 13px lê como "botão vazio" quando o ícone tem 14px (foi o que o
   Felipe viu em Custos Fixos — ali o ícone nem chegava a ser desenhado, ver o
   observer de lucide em js/app.js, mas o quadrado grande demais também incomodava).
   O ::before invisível devolve os 40px de toque exigidos pela auditoria. */
.act-btn{display:inline-flex;align-items:center;justify-content:center;position:relative;min-width:32px;min-height:32px;border:1px solid var(--p-border);background:var(--p-surface);border-radius:6px;padding:4px 7px;cursor:pointer;color:var(--p-blue);margin-right:6px;vertical-align:middle;transition:all .12s;font-family:inherit;font-size:11px;}
.act-btn::before{content:'';position:absolute;inset:-4px;}
.act-btn .ico-sm{width:15px;height:15px;}
.act-btn:hover{background:var(--p-blue);color:#fff;border-color:var(--p-blue);}
.act-btn.act-del{color:var(--p-err);}
.act-btn.act-del:hover{background:var(--p-err);color:#fff;border-color:var(--p-err);}

/* Edição inline de documento no painel admin (admin.html) — versão compacta do .act-btn
   (mesma ideia, cabe dentro de uma célula de tabela sem estourar a linha). */
.admin-doc-cell{display:inline-flex;align-items:center;gap:6px;}
.admin-doc-edit{display:inline-flex;align-items:center;gap:4px;}
.admin-doc-input{width:140px;padding:4px 8px;border:1.5px solid var(--p-border);border-radius:var(--r);font-family:inherit;font-size:12px;color:var(--p-text);background:var(--p-surface);outline:none;}
.admin-doc-input:focus{border-color:var(--p-blue);}
.admin-doc-pencil{display:inline-flex;align-items:center;justify-content:center;width:24px;height:24px;border:1px solid var(--p-border);background:var(--p-surface);border-radius:6px;padding:0;cursor:pointer;color:var(--p-sub);transition:all .12s;}
.admin-doc-pencil:hover{background:var(--p-blue);color:#fff;border-color:var(--p-blue);}

/* Toast (feedback não-bloqueante) — mesmo padrão do WebApp.html original */
.fc-toast{position:fixed;bottom:24px;left:50%;transform:translateX(-50%) translateY(80px);background:var(--p-blue-ink);color:#fff;padding:12px 20px;border-radius:100px;font-size:13px;font-weight:600;box-shadow:0 8px 30px rgba(0,0,0,.3);display:flex;align-items:center;gap:9px;z-index:9999;opacity:0;transition:transform .28s cubic-bezier(.2,.8,.2,1),opacity .28s;pointer-events:none;max-width:90vw;}
.fc-toast.show{transform:translateX(-50%) translateY(0);opacity:1;}
.fc-toast.ok{background:var(--p-ok);}
.fc-toast.err{background:var(--p-err);}
.fc-toast.loading{background:var(--p-blue-deep);}
.spin-ic{animation:fc-spin 1s linear infinite;}
@keyframes fc-spin{to{transform:rotate(360deg);}}

/* Modal de confirmação (substitui window.confirm() nativo) — achado P2 #16 da auditoria
   adversarial. window.confirmarAcao() em js/app.js monta esse markup dinamicamente (mesmo
   espírito do toast acima). Visualmente consistente com os .panel existentes: fundo escuro
   semi-transparente, card centralizado, título 16px bold, mensagem 13px, 2 botões. */
.fc-confirm-overlay{position:fixed;inset:0;background:rgba(0,0,0,.45);z-index:9997;display:flex;align-items:center;justify-content:center;padding:20px;animation:fc-fade .15s ease;}
.fc-confirm-card{background:var(--p-surface);border-radius:var(--r);box-shadow:0 20px 60px rgba(0,0,0,.35);width:100%;max-width:380px;padding:20px;}
.fc-confirm-title{font-size:16px;font-weight:800;margin-bottom:8px;color:var(--p-text);}
.fc-confirm-msg{font-size:13px;color:var(--p-sub);line-height:1.5;margin-bottom:18px;}
.fc-confirm-actions{display:flex;justify-content:flex-end;gap:8px;}

/* Skeleton loading */
@keyframes fc-shimmer{0%{opacity:.6;}50%{opacity:1;}100%{opacity:.6;}}
.sk{background:var(--p-border);animation:fc-shimmer 1.25s infinite ease-in-out;border-radius:6px;display:block;}
.sk-kpi-row{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin-bottom:18px;}
.sk-kpi{background:var(--p-surface);border:1px solid var(--p-border);border-radius:var(--r);padding:16px;box-shadow:var(--sh);}
.sk-line{height:10px;margin-bottom:9px;}
.sk-row{display:flex;align-items:center;gap:10px;margin-bottom:13px;}
.sk-circle{border-radius:50%;flex-shrink:0;}
.fc-fade-in{animation:fc-fade .28s ease;}
@keyframes fc-fade{from{opacity:0;transform:translateY(4px);}to{opacity:1;transform:none;}}

/* Tooltip de ajuda "(?)" — reutilizável em qualquer tela. Funciona em mouse (hover) E em
   toque (tap-to-toggle via Alpine, já que hover não existe em celular). Uso:
   <span class="ajuda" x-data="{show:false}" @click="show=!show" @click.outside="show=false"
         :class="{'ajuda-aberta':show}" tabindex="0">?<span class="ajuda-texto">Texto aqui.</span></span> */
/* Tamanho: o (?) virou uma bolota de 32x32 quando a auditoria adversarial v2 pediu área
   de toque WCAG — só que 32px do lado de um rótulo de 10px fica maior que o próprio
   rótulo e rouba o olho do número (achado do Felipe usando o app, comparando com o Apps
   Script original, onde o (?) é discreto). A área de toque continua ≥32px: o círculo
   VISÍVEL é 15px e o ::before invisível estende a região clicável pros lados. */
.ajuda{display:inline-flex;align-items:center;justify-content:center;width:15px;height:15px;border-radius:50%;border:1px solid currentColor;background:transparent;color:var(--p-sub);font-size:9px;font-weight:700;line-height:1;margin-left:4px;cursor:help;position:relative;vertical-align:middle;opacity:.65;flex-shrink:0;transition:opacity .12s,color .12s,background .12s;}
.ajuda::before{content:'';position:absolute;inset:-9px;border-radius:50%;}
.ajuda:hover,.ajuda:focus,.ajuda-aberta{background:var(--p-blue);border-color:var(--p-blue);color:#fff;opacity:1;outline:none;}
.ajuda-texto{position:absolute;bottom:calc(100% + 8px);left:50%;transform:translateX(-50%) translateY(4px);width:220px;background:var(--p-blue-ink);color:#fff;font-size:11px;font-weight:400;line-height:1.45;text-transform:none;letter-spacing:0;padding:9px 11px;border-radius:8px;box-shadow:0 6px 20px rgba(0,0,0,.25);opacity:0;visibility:hidden;transition:opacity .16s,transform .16s;z-index:9998;pointer-events:none;}
.ajuda-texto::after{content:'';position:absolute;top:100%;left:50%;transform:translateX(-50%);border:6px solid transparent;border-top-color:var(--p-blue-ink);}
.ajuda:hover .ajuda-texto,.ajuda:focus .ajuda-texto,.ajuda-aberta .ajuda-texto{opacity:1;visibility:visible;transform:translateX(-50%) translateY(0);}
@media(max-width:768px){.ajuda-texto{left:auto;right:-8px;transform:translateY(4px);}.ajuda:hover .ajuda-texto,.ajuda:focus .ajuda-texto,.ajuda-aberta .ajuda-texto{transform:translateY(0);}.ajuda-texto::after{left:auto;right:12px;transform:none;}}

/* DRE — cascata didática (Entrou → Custo → Bruto → Despesas → Resultado), portada do
   WebApp.html original. Cada bloco tem uma cor/borda própria pra deixar a leitura intuitiva
   mesmo pra quem não é da área financeira. */
.dre-g{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:14px;}
@media(max-width:860px){.dre-g{grid-template-columns:1fr;}}
.dre-card{background:var(--p-surface);border:1px solid var(--p-border);border-radius:var(--r);overflow:hidden;box-shadow:var(--sh);}
.dre-hd{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--p-sub);padding:10px 14px;border-bottom:1px solid var(--p-border);display:flex;justify-content:space-between;}
.dre-row{display:flex;justify-content:space-between;align-items:center;padding:8px 14px;border-bottom:1px solid var(--p-border);font-size:13px;}
.dre-row:last-child{border-bottom:none;}
.dre-total{background:var(--p-bg);font-weight:700;font-size:14px;border-top:2px solid var(--p-border)!important;}
.dre-resultado{background:var(--p-blue-ink);border-radius:var(--r);padding:18px 22px;margin-bottom:16px;display:flex;justify-content:space-between;align-items:center;flex-wrap:wrap;gap:10px;}
.dre-rlbl{color:rgba(255,255,255,.7);font-size:14px;font-weight:600;}
.dre-rval{font-size:30px;font-weight:800;font-variant-numeric:tabular-nums;}
.dre-rval.pos{color:var(--p-yellow);}.dre-rval.neg{color:#FF8A80;}

.cascata{max-width:720px;margin:0 auto 4px;}
.bloco-dre{display:flex;gap:12px;background:var(--p-surface);border:1px solid var(--p-border);border-radius:var(--r);padding:14px 16px;box-shadow:var(--sh);position:relative;}
.bloco-dre.b-entrou{border-color:var(--p-ok);}
.bloco-dre.b-custo{border-color:var(--p-warn);}
.bloco-dre.b-bruto{border-color:var(--p-blue);}
.bloco-dre.b-desp{border-color:var(--p-err);}
.bloco-num{flex-shrink:0;width:26px;height:26px;border-radius:50%;background:var(--p-blue-ink);color:#fff;font-weight:800;font-size:13px;display:flex;align-items:center;justify-content:center;}
.bloco-body{flex:1;min-width:0;}
.bloco-tit{font-size:14px;font-weight:800;margin-bottom:2px;display:flex;align-items:center;gap:8px;flex-wrap:wrap;}
.bloco-tag{font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.4px;background:var(--p-bg);color:var(--p-sub);padding:2px 7px;border-radius:4px;}
.bloco-exp{font-size:11px;color:var(--p-sub);line-height:1.4;margin-bottom:8px;}
.bloco-lin{display:flex;justify-content:space-between;font-size:12px;padding:3px 0;color:var(--p-text);}
.bloco-tot{display:flex;justify-content:space-between;font-size:14px;font-weight:800;margin-top:6px;padding-top:8px;border-top:1.5px solid var(--p-border);}
.b-entrou-tot{color:var(--p-ok);}
.b-custo-tot{color:var(--p-warn);}
.b-bruto-tot{color:var(--p-blue);}
.b-desp-tot{color:var(--p-err);}
.cascata-seta{text-align:center;font-size:20px;font-weight:800;color:var(--p-sub);line-height:1;padding:3px 0;max-width:720px;margin:0 auto;}
@media(max-width:768px){.dre-resultado{padding:14px 16px;}.dre-rval{font-size:24px;}.bloco-tit{font-size:13px;}.bloco-num{width:22px;height:22px;font-size:12px;}}

/* Variação (▲/▼ + %) do DRE — usada dentro de .bloco-lin (categoria de receita) E
   dentro de .bloco-tot (total de cada bloco), sempre COLADA no valor em R$, nunca
   numa linha própria. É o mesmo componente visual do vp() do Apps Script original
   (frontcell-ops/WebApp.html) — o Felipe comparou lado a lado e notou duas
   diferenças: (1) a variação das categorias de receita não tinha NENHUMA cor —
   o markup já usava as classes pos/neg/zero, mas nenhuma regra `.pos`/`.neg`/
   `.zero` bare existia no CSS pra elas pegarem, então o texto saía na cor padrão;
   (2) a variação do total ficava numa linha separada ABAIXO, com sinal +/− em vez
   do triângulo ▲/▼. `.dre-vp` corrige as duas coisas de uma vez — troca a antiga
   `.bloco-delta` (linha própria, mesmo se não é mais usada, ver dre.html). */
.dre-vp{font-size:11px;font-weight:700;margin-left:5px;font-variant-numeric:tabular-nums;}
.dre-vp.pos{color:var(--p-ok);}
.dre-vp.neg{color:var(--p-err);}
.dre-vp.zero{color:var(--p-sub);}
.dre-rdelta{font-size:12px;font-weight:700;color:rgba(255,255,255,.85);font-variant-numeric:tabular-nums;}
.dre-rdelta.neg{color:#FF8A80;}

/* ============================================================================
   Análises — cross-filter BI (porte de WebApp.html original, ver
   docs/HANDOFF_2026-08-05.md item 6). Classes ax-*/chart-*/mes-chip* portadas
   1:1, só trocando o prefixo de variável --fc- por --p- (nomenclatura desta
   versão do design system).
   ============================================================================ */
.chart-g2{display:grid;grid-template-columns:2fr 1fr;gap:14px;margin-bottom:18px;}
.chart-geq{display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:18px;}
@media(max-width:960px){.chart-g2,.chart-geq{grid-template-columns:1fr;}}
.chart-card{background:var(--p-surface);border:1px solid var(--p-border);border-radius:var(--r);padding:16px;box-shadow:var(--sh);}
.chart-title{font-size:13px;font-weight:700;margin-bottom:2px;}
.chart-sub{font-size:11px;color:var(--p-sub);margin-bottom:12px;}
.chart-h{position:relative;height:210px;}.chart-h-sm{position:relative;height:175px;}.chart-h-lg{position:relative;height:265px;}

.mes-chip{padding:4px 10px;border-radius:6px;font-size:11px;font-weight:600;border:1.5px solid var(--p-border);background:var(--p-surface);color:var(--p-sub);cursor:pointer;}
.mes-chip.sel{background:var(--p-blue);border-color:var(--p-blue);color:#fff;}
.mes-chips{display:flex;gap:5px;flex-wrap:wrap;margin-bottom:11px;}

.ax-filterbar{display:flex;flex-wrap:wrap;gap:16px;background:var(--p-surface);border:1px solid var(--p-border);border-radius:var(--r);padding:14px 16px;margin-bottom:12px;box-shadow:var(--sh);}
.ax-fgroup{display:flex;flex-direction:column;gap:6px;}
.ax-flabel{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.4px;color:var(--p-sub);}
.ax-btns{display:flex;gap:4px;flex-wrap:wrap;}
.ax-fb{border:1px solid var(--p-border);background:var(--p-bg);color:var(--p-text);font-family:inherit;font-size:12px;font-weight:600;padding:6px 10px;border-radius:7px;cursor:pointer;transition:all .15s;}
.ax-fb:hover{border-color:var(--p-blue);}
.ax-fb.on{background:var(--p-blue);color:#fff;border-color:var(--p-blue);}
.ax-range{display:flex;align-items:center;gap:6px;}
.ax-range input{width:56px;border:1px solid var(--p-border);border-radius:7px;padding:6px 8px;font-family:inherit;font-size:13px;text-align:center;}
.ax-range span{font-size:12px;color:var(--p-sub);}
.ax-sel{border:1px solid var(--p-border);border-radius:7px;padding:6px 10px;font-family:inherit;font-size:13px;background:var(--p-bg);cursor:pointer;min-width:120px;}
.ax-active{display:flex;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:14px;padding:10px 14px;background:var(--p-blue-bg);border-radius:var(--r);}
.ax-active-lbl{font-size:12px;font-weight:700;color:var(--p-blue-deep);}
.ax-chip-active{display:inline-flex;align-items:center;gap:5px;background:var(--p-surface);border:1px solid var(--p-blue);color:var(--p-blue-deep);font-size:12px;font-weight:600;padding:4px 6px 4px 10px;border-radius:6px;}
.ax-chip-active button{border:none;background:var(--p-blue);color:#fff;width:16px;height:16px;border-radius:50%;cursor:pointer;font-size:11px;line-height:1;display:flex;align-items:center;justify-content:center;padding:0;}
.ax-clear{margin-left:auto;border:none;background:transparent;color:var(--p-err);font-family:inherit;font-size:12px;font-weight:700;cursor:pointer;text-decoration:underline;}
.ax-kpis{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:14px;}
.ax-kpi{background:var(--p-surface);border:1px solid var(--p-border);color:var(--p-text);border-radius:var(--r);padding:14px 16px;position:relative;box-shadow:var(--sh);}
.ax-kpi::after{content:'';position:absolute;left:16px;right:16px;bottom:0;height:2.5px;border-radius:2px;background:var(--p-yellow);opacity:.85;}
.ax-kpi-l{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:var(--p-sub);margin-bottom:5px;}
.ax-kpi-v{font-size:22px;font-weight:800;font-variant-numeric:tabular-nums;letter-spacing:-.5px;color:var(--p-text);}
.ax-kpi-s{font-size:11px;color:var(--p-sub);margin-top:3px;}

.ax-help{position:relative;display:inline-flex;align-items:center;margin-left:5px;color:var(--p-sub);cursor:help;vertical-align:middle;opacity:.7;z-index:1;}
.ax-help:hover,.ax-help:focus{z-index:9999;}
.ax-help:hover,.ax-help:focus{opacity:1;color:var(--p-blue);outline:none;}
.ax-help .ico-sm{width:14px;height:14px;}
.ax-tip{position:absolute;bottom:calc(100% + 8px);left:50%;transform:translateX(-50%) translateY(4px);width:230px;background:var(--p-blue-ink);color:#fff;font-size:11px;font-weight:400;line-height:1.45;text-transform:none;letter-spacing:0;padding:9px 11px;border-radius:8px;box-shadow:0 6px 20px rgba(0,0,0,.25);opacity:0;visibility:hidden;transition:opacity .16s,transform .16s;z-index:9998;pointer-events:none;}
.ax-tip::after{content:'';position:absolute;top:100%;left:50%;transform:translateX(-50%);border:6px solid transparent;border-top-color:var(--p-blue-ink);}
.ax-help:hover .ax-tip,.ax-help:focus .ax-tip{opacity:1;visibility:visible;transform:translateX(-50%) translateY(0);}
.ax-kpi .ax-help{color:var(--p-sub);}
.ax-kpi .ax-help:hover{color:var(--p-blue);}

.ax-hint{display:flex;align-items:center;gap:7px;font-size:12px;color:var(--p-sub);background:var(--p-bg);border-radius:8px;padding:8px 12px;margin-bottom:14px;}
.ax-hint-clear{margin-left:auto;display:inline-flex;align-items:center;gap:5px;border:1px solid var(--p-border);background:var(--p-surface);color:var(--p-blue);font-family:inherit;font-size:12px;font-weight:700;padding:5px 12px;border-radius:7px;cursor:pointer;transition:all .15s;}
.ax-hint-clear:hover{background:var(--p-blue);color:#fff;border-color:var(--p-blue);}
@media(max-width:768px){.ax-kpis{grid-template-columns:repeat(2,1fr);}.ax-filterbar{gap:12px;}.ax-fgroup{width:100%;}}

/* Login/signup (páginas fora do app shell). Mobile-first: .auth-visual (logo/marca/tagline)
   empilha acima do .auth-body (card do formulário); a partir de 860px vira split-screen
   (visual à esquerda, formulário à direita). O conteúdo do .auth-card em si (campos, JS de
   submit) não é tocado por este bloco — só o invólucro visual ao redor. */
.auth-wrap{min-height:100vh;display:flex;flex-direction:column;background:var(--p-blue-ink);}
.auth-visual{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:10px;padding:40px 24px 4px;color:#fff;text-align:center;}
#logo-placeholder{width:48px;height:48px;color:#fff;}
#logo-placeholder svg{width:100%;height:100%;display:block;}
.auth-brand{font-size:23px;font-weight:800;letter-spacing:-.01em;line-height:1;}
.auth-brand span{color:var(--p-yellow);}
.auth-tagline{font-size:12.5px;color:rgba(255,255,255,.7);max-width:280px;line-height:1.5;}
.auth-body{flex:1;display:flex;align-items:center;justify-content:center;padding:24px 20px 40px;}
.auth-card{background:var(--p-surface);border-radius:12px;padding:32px 28px;width:100%;max-width:380px;box-shadow:0 20px 60px rgba(0,0,0,.35);}
.auth-title{font-size:20px;font-weight:800;margin-bottom:4px;color:var(--p-text);}
.auth-sub{font-size:12px;color:var(--p-sub);margin-bottom:20px;}
.auth-switch{text-align:center;font-size:12px;color:var(--p-sub);margin-top:14px;}
.auth-switch a{color:var(--p-blue);font-weight:600;cursor:pointer;text-decoration:none;}
@media(min-width:860px){
  .auth-wrap{flex-direction:row;align-items:stretch;}
  .auth-visual{flex:1 1 46%;padding:48px;gap:18px;}
  #logo-placeholder{width:72px;height:72px;}
  .auth-brand{font-size:32px;}
  .auth-tagline{font-size:14px;max-width:320px;}
  .auth-body{flex:1 1 54%;background:var(--p-bg);padding:40px;}
}

@media(prefers-reduced-motion:reduce){.sk{animation:none;}.fc-fade-in{animation:none;}.spin{animation:none;}}

/* Breakpoint da sidebar — 769px pra não colidir com o media query mobile
   existente logo abaixo, que usa max-width:768px (768 é mobile, 769+ é
   desktop; sem sobreposição nem lacuna entre os dois). */
@media(min-width:769px){
  .hd-mobile,.nav-mobile{display:none;}
  .sb{display:flex;}
  .hd-desk{display:flex;}
  .hd-desk,.body-wrap{margin-left:var(--sb-w);transition:margin-left .2s cubic-bezier(0.4, 0, 0.2, 1);}
  .body-wrap{min-height:calc(100vh - 56px);}
  .nav-layout-toggle{display:inline-flex;}

  /* Modo topbar (B2): alternativa à sidebar, ativada pelo toggle acima e
     persistida em localStorage['fc_nav_layout'] (Alpine.store('ui').navLayout,
     ver web/js/app.js). Body ganha [data-nav-layout="topbar"] via Alpine.
     Reaproveita .hd-mobile/.nav-mobile tal como já existem pro breakpoint
     mobile (mesmo header completo — logo, período, usuário — e mesma nav
     com submenu Financeiro já corrigido, ver commit do fix do bug E1) em vez
     de duplicar essa estrutura de novo só pra desktop. Especificidade do
     seletor com atributo já garante que essas regras vençam as de cima sem
     precisar de !important nem depender de ordem no arquivo. */
  [data-nav-layout="topbar"] .sb,[data-nav-layout="topbar"] .hd-desk{display:none;}
  [data-nav-layout="topbar"] .hd-mobile,[data-nav-layout="topbar"] .nav-mobile{display:flex;}
  [data-nav-layout="topbar"] .body-wrap{margin-left:0;min-height:calc(100vh - 112px);}
  /* CORREÇÃO (2026-08-12): tentativa anterior tinha entendido o pedido do Felipe ao
     contrário — ele quer o OPOSTO de "rolar junto com a página": quer o cabeçalho
     SEMPRE visível ao rolar (igual já funciona no modo sidebar), não sumindo de tela.
     Duas peças formam o cabeçalho no modo topbar, reaproveitando .hd-mobile/.nav-mobile
     do breakpoint mobile (ver comentário acima): <header class="hd hd-mobile"> (filtro de
     período + logo + usuário) e, um elemento IRMÃO diferente, <nav class="nav nav-mobile">
     dentro de <div class="nav-mobile-wrap"> (os botões Dashboard/Vendas/OS/...). .hd já
     tem position:sticky;top:0 (linha ~64) — isso sempre esteve certo, não precisa de nada
     aqui. O que NUNCA teve sticky, nem antes da minha tentativa anterior, é o
     .nav-mobile-wrap — ele só tem position:relative (usado pra posicionar os gradientes
     de fade nas bordas, não pra sticky) — por isso ao rolar a página o menu de navegação
     sempre desapareceu, sobrando só a barra de filtros fixa acima dele (ou nem isso,
     depois do meu engano anterior). Fix: gruda o .nav-mobile-wrap logo abaixo do cabeçalho
     (top: 56px = altura de .hd) só no modo topbar — os dois juntos ficam sempre visíveis,
     como uma coisa só, igual o conjunto sidebar+conteúdo já parece hoje. z-index 190: abaixo
     de .hd (200, pra sombra do cabeçalho continuar por cima) e bem abaixo de dropdowns/
     popovers que abrem a partir da nav (.nb-sub/.grid-pop/.status-pop, todos 250+). */
  [data-nav-layout="topbar"] .nav-mobile-wrap{position:sticky;top:56px;z-index:190;background:var(--p-surface);}
}

@media(max-width:768px){
  .hd{height:auto;min-height:56px;flex-wrap:wrap;padding:8px 12px;gap:8px;}
  .period-wrap{margin-left:0;width:100%;justify-content:flex-start;gap:4px;}
  .pb{padding:5px 9px;font-size:11px;flex:0 0 auto;}
  .period-sep{display:none;}
  .pci{width:calc(50% - 16px);font-size:11px;}
  .logo-t{font-size:16px;}
  .main{padding:12px;}
  .main.shifted{margin-right:0;}
  .panel{width:100%;top:0;right:-100%;z-index:300;}
  .panel.open{right:0;}
  .panel-hd{position:sticky;top:0;background:var(--p-surface);z-index:5;}
  .kpi-row{grid-template-columns:repeat(2,1fr)!important;gap:8px;}
  .kpi-val{font-size:19px;}
  /* Faixa hero (Faturamento/Lucro bruto/Resultado) empilha 1-coluna no mobile —
     em 2 colunas os valores de 30px estourariam a largura do card. */
  .kpi-row-hero{grid-template-columns:1fr!important;}
  .kpi-hero .kpi-val{font-size:24px;}
  .nav{padding:0 8px;}
  .nb{padding:11px 11px;font-size:12px;}
  .search-in{width:100%;}
  .tb{gap:6px;}
  .tb-right{margin-left:0;width:100%;}
  .tb-right .btn{width:100%;justify-content:center;}
  .tbl{font-size:12px;}
  .tbl th,.tbl td{padding:7px 8px;}
  .sk-kpi-row{grid-template-columns:repeat(2,1fr);}
}

/* Configurações (pg-configuracoes) — sub-sidebar interna de seções + conteúdo à direita.
   Nomes cfg-* pra não colidir com .sb/.sbi da sidebar principal do app (propósito
   parecido — lista vertical de seções — mas é outro nível de navegação, interno à página). */
.cfg-shell{display:flex;gap:20px;align-items:flex-start;}
.cfg-side{flex:0 0 200px;display:flex;flex-direction:column;gap:2px;position:sticky;top:76px;}
.cfg-side-item{display:flex;align-items:center;gap:9px;padding:9px 12px;border-radius:var(--r);border:none;background:transparent;color:var(--p-sub);font-family:inherit;font-size:13px;font-weight:600;cursor:pointer;text-align:left;transition:all .12s;}
.cfg-side-item:hover{background:var(--p-surface);color:var(--p-blue);}
.cfg-side-item.on{background:var(--p-surface);color:var(--p-blue);box-shadow:var(--sh);}
.cfg-side-item:disabled{color:var(--p-border);cursor:not-allowed;}
.cfg-side-item:disabled:hover{background:transparent;color:var(--p-border);}
.cfg-content{flex:1;min-width:0;}
.cfg-section-narrow{max-width:380px;}
.fhint{font-size:11px;color:var(--p-sub);line-height:1.4;margin:4px 0 0;}
/* Cores da marca RepForm: "Rep" em azul-marinho, "Form" em preto — é assim que o
   lockup desenhado pelo Felipe é ("Logo RepForm preto-azul-branco.png", ver
   img/README-logo.md). O amarelo que estava aqui é a cor de DESTAQUE do produto
   (botões, KPIs), não uma cor da marca, e ainda por cima quase não se lê sobre
   fundo branco. */
/* ============================================================================
   "Em construção" — página inteira (Cupom Fiscal/NF-e, ver web/modules/fiscal.html
   e o item de menu .nb-soon/.sbi-soon em index.html). Antes era só um toast; o
   Felipe pediu uma TELA inteira, não um aviso passageiro — item de menu real,
   com pg-fiscal próprio, não uma função que só mostra um popup. Reaproveita os
   tokens --p-* de sempre, então já nasce certo nos dois temas.
   ============================================================================ */
.constr-wrap{display:flex;flex-direction:column;align-items:center;text-align:center;padding:64px 24px;max-width:520px;margin:0 auto;}
.constr-icone{width:72px;height:72px;border-radius:50%;background:var(--p-yellow-soft);color:var(--p-blue-ink);display:flex;align-items:center;justify-content:center;margin-bottom:20px;}
.constr-icone .ico{width:32px;height:32px;stroke-width:1.75;}
.constr-selo{background:var(--p-yellow-soft);color:#8A6D00;padding:3px 12px;border-radius:4px;font-size:12px;font-weight:700;margin-bottom:14px;}
[data-theme="dark"] .constr-selo{color:#FFE580;}
.constr-titulo{font-size:22px;font-weight:800;letter-spacing:-.3px;color:var(--p-text);margin-bottom:10px;}
.constr-texto{font-size:14px;color:var(--p-sub);line-height:1.6;margin-bottom:6px;}
.constr-texto strong{color:var(--p-text);}

.cfg-sobre-brand{font-size:22px;font-weight:800;letter-spacing:-.02em;margin-bottom:6px;color:var(--p-blue-ink);}
.cfg-sobre-brand span{color:#0B0F14;}
/* No tema escuro o par azul-marinho/preto desaparece no fundo — mesma marca, tons
   invertidos em claridade (o "Rep" clareia, o "Form" vira o branco do lockup). */
[data-theme="dark"] .cfg-sobre-brand{color:#5FA8FF;}
[data-theme="dark"] .cfg-sobre-brand span{color:#F2F6FC;}

@media(max-width:768px){
  .cfg-shell{flex-direction:column;}
  .cfg-side{flex-direction:row;flex-wrap:wrap;position:static;width:100%;}
}

/* Card "Resultado do mês atual" (Dashboard) — porte fiel do cartão azul com
   gradiente de renderProjecao()/projItem() no WebApp.html original (Apps
   Script), ver web/modules/dashboard.html. Prefixo .proj- isolado dos outros
   componentes pra não colidir com .dre-* e .kpi-* usados em outros módulos. */
/* Card "Resultado do mês atual" (Dashboard) — borda sólida no amarelo de
   destaque da marca (--p-yellow), sem gradiente/animação (padrão anti-slop:
   só cor sólida). */
.card-proj-shell{position:relative;border-radius:calc(var(--r) + 1.5px);padding:1.5px;overflow:hidden;isolation:isolate;margin-bottom:18px;background:var(--p-yellow);}

.card-proj{background:var(--p-blue-ink);color:#fff;padding:16px 18px;border-radius:var(--r);}
.proj-hd{display:flex;align-items:center;gap:8px;margin-bottom:4px;flex-wrap:wrap;}
.proj-title{font-weight:800;font-size:14px;}
/* A palavra PROJETADO em destaque dentro do título — pedido do Felipe pra
   deixar óbvio, batendo o olho, que aqueles números ainda vão acontecer (não
   são o resultado real do mês, que é o que "Real (hoje)" mostra). */
.proj-title-destaque{color:var(--p-yellow);font-weight:900;letter-spacing:.3px;}
.proj-badge{background:rgba(255,255,255,.14);color:#fff;padding:2px 9px;border-radius:4px;font-size:9px;font-weight:700;text-transform:uppercase;letter-spacing:.3px;}
.proj-toggle{margin-left:auto;display:flex;gap:0;background:rgba(255,255,255,.1);border-radius:100px;padding:2px;}
.proj-toggle button{border:none;cursor:pointer;padding:5px 12px;border-radius:100px;font-size:11px;font-weight:700;font-family:inherit;background:transparent;color:rgba(255,255,255,.7);}
.proj-toggle button.on{background:var(--p-yellow);color:#0D2B52;}
.proj-modo-row{margin-bottom:12px;display:flex;align-items:center;flex-wrap:wrap;gap:6px;}
.proj-selo{padding:2px 9px;border-radius:4px;font-size:9px;font-weight:700;text-transform:uppercase;}
.proj-selo-comp{background:rgba(255,212,0,.15);color:#FFD400;}
.proj-selo-caixa{background:rgba(255,255,255,.1);color:#fff;}
.proj-dias{font-size:11px;color:rgba(255,255,255,.5);}
.proj-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(130px,1fr));gap:14px;}
.proj-item-lbl{font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;color:rgba(255,255,255,.55);margin-bottom:4px;}
.proj-item-val{font-size:20px;font-weight:800;font-variant-numeric:tabular-nums;color:#fff;}
.proj-item-sub{font-size:10px;color:rgba(255,255,255,.5);margin-top:2px;}
.proj-equilibrio{border-top:1px solid rgba(255,255,255,.15);padding-top:12px;margin-top:12px;display:flex;align-items:center;gap:8px;flex-wrap:wrap;}
.proj-equilibrio-txt{font-size:11px;color:rgba(255,255,255,.6);}
.proj-equilibrio-txt strong{color:#fff;}
.proj-chip{padding:3px 10px;border-radius:4px;font-size:10px;font-weight:700;text-transform:uppercase;}
.proj-chip-ok{background:rgba(30,142,62,.2);color:#8FE3A6;}
.proj-chip-warn{background:rgba(232,113,10,.2);color:#FFB380;}
.proj-equilibrio-fixos{margin-left:auto;font-size:11px;color:rgba(255,255,255,.5);}
@media(max-width:768px){.card-proj{padding:14px 16px;}.proj-item-val{font-size:18px;}}

/* ============================================================================
   Fluxo novo de OS (protótipo validado 23/09, docs/prototipos/fluxo-os-recebimento-v1.html)
   ============================================================================ */
/* Só do dono (migration 0040): o funcionário não vê. O banco recusa de qualquer jeito. */
body[data-papel="tecnico"] .so-dono{display:none!important;}
.selos{display:flex;flex-direction:column;align-items:flex-start;gap:4px;}
.money{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:6px;font-size:10px;font-weight:700;white-space:nowrap;font-variant-numeric:tabular-nums;}
.m-pago{background:var(--p-ok-bg);color:var(--p-ok);}
.m-falta{background:var(--p-warn-bg);color:var(--p-warn);}
.m-deve{background:var(--p-err-bg);color:var(--p-err);}
.m-sinal{background:var(--p-violet-bg);color:var(--p-violet);}
.m-orcar{background:var(--p-bg);color:var(--p-sub);border:1px dashed var(--p-border);}
.gar{font-size:10px;font-weight:700;color:var(--p-ok);display:inline-flex;gap:3px;align-items:center;}
.gar.off{color:var(--p-sub);}
.late{color:var(--p-err);font-weight:700;}
.st-badge.aprov{background:var(--p-yellow-soft);color:#6B5500;}
:root[data-theme="dark"] .st-badge.aprov{color:#F2D65C;}
.chip .n{font-size:10px;opacity:.8;margin-left:3px;}
.chip-alerta{border-color:var(--p-warn);color:var(--p-warn);}
.chips{display:flex;flex-wrap:wrap;gap:6px;}
.status-pop-sep{border:none;border-top:1px solid var(--p-border);margin:4px 2px;}
.status-pop-item small{font-size:10px;color:var(--p-sub);font-weight:600;margin-left:6px;}
.status-pop-item:disabled{opacity:.4;cursor:not-allowed;}
/* Painéis Entregar/Receber */
.resumo-os{background:var(--p-bg);border-radius:var(--r);padding:8px 12px;}
.resumo-os .tot-row.forte{font-weight:800;font-size:15px;}
.resumo-os .tot-row.forte span:last-child{color:var(--p-warn);}
.formas-btn{display:grid;grid-template-columns:repeat(2,1fr);gap:8px;margin-top:6px;}
.forma-btn{padding:12px 10px;border-radius:var(--r);border:1.5px solid var(--p-border);background:var(--p-surface);font-family:inherit;font-size:13px;font-weight:700;color:var(--p-text);cursor:pointer;}
.forma-btn:hover{border-color:var(--p-blue);}
.forma-btn.on{border-color:var(--p-blue);background:var(--p-blue-bg);color:var(--p-blue);}
.linha-alt{display:flex;gap:8px;margin-top:8px;flex-wrap:wrap;}
.linha-alt .btn{flex:1;}
.info-ok{display:flex;gap:8px;align-items:flex-start;background:var(--p-ok-bg);color:var(--p-ok);border-radius:var(--r);padding:9px 12px;font-size:12px;font-weight:600;margin-top:12px;}
.msg-wa{background:var(--p-bg);border:1px solid var(--p-border);border-radius:var(--r);padding:11px 12px;font-size:13px;line-height:1.5;white-space:pre-wrap;}
.btn-wa{background:#1FA855;color:#fff;}
.btn-wa:hover{filter:brightness(.95);}
.receb-lista{font-size:12px;}
.receb-lista .tot-row{align-items:center;}
/* Nova OS: cliente, senha, padrão de 9 pontos */
.combo-cli{position:relative;}
.combo-cli-lista{position:absolute;left:0;right:0;top:calc(100% + 4px);background:var(--p-surface);border:1px solid var(--p-border);border-radius:var(--r);box-shadow:0 8px 24px rgba(0,0,0,.14);z-index:30;max-height:260px;overflow-y:auto;}
.combo-cli-lista button{display:block;width:100%;text-align:left;padding:9px 11px;border:none;background:none;cursor:pointer;font-family:inherit;font-size:13px;color:var(--p-text);}
.combo-cli-lista button:hover{background:var(--p-row-hover);}
.combo-cli-lista button small{color:var(--p-sub);margin-left:6px;}
.combo-cli-lista .novo{color:var(--p-blue);font-weight:700;border-top:1px solid var(--p-border);}
.cli-sel{display:flex;justify-content:space-between;align-items:center;gap:8px;padding:8px 11px;border:1.5px solid var(--p-blue);border-radius:var(--r);background:var(--p-blue-bg);font-size:13px;}
.cli-sel small{color:var(--p-sub);}
.padrao-wrap{display:flex;gap:16px;align-items:center;flex-wrap:wrap;}
.padrao{position:relative;width:156px;height:156px;display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(3,1fr);border:1.5px solid var(--p-border);border-radius:12px;background:var(--p-bg);touch-action:none;}
.padrao svg{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;}
.padrao .dot{display:flex;align-items:center;justify-content:center;cursor:pointer;border:none;background:none;padding:0;}
.padrao .dot i{width:14px;height:14px;border-radius:50%;background:var(--p-border);border:2px solid var(--p-sub);display:block;}
.padrao .dot.on i{background:var(--p-blue);border-color:var(--p-blue);box-shadow:0 0 0 5px var(--p-blue-bg);}
.padrao-info{font-size:12px;color:var(--p-sub);line-height:1.6;}
.padrao-info b{font-family:ui-monospace,Menlo,monospace;color:var(--p-text);}
.senha-oculta{font-family:ui-monospace,Menlo,monospace;letter-spacing:1px;}
/* Hoje / Quem me deve */
.hoje-grid{display:grid;grid-template-columns:1.25fr 1fr;gap:14px;}
.card-p{background:var(--p-surface);border:1px solid var(--p-border);border-radius:var(--r);box-shadow:var(--sh);}
.sec-lbl{font-size:10px;font-weight:800;text-transform:uppercase;letter-spacing:.6px;color:var(--p-sub);margin:0 0 8px;}
.caixa-total{font-size:30px;font-weight:800;font-variant-numeric:tabular-nums;line-height:1;}
.formas-dia{display:grid;grid-template-columns:repeat(auto-fit,minmax(118px,1fr));gap:8px;margin-top:14px;}
.forma-dia{border:1px solid var(--p-border);border-radius:var(--r);padding:9px 10px;}
.forma-dia small{display:block;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.4px;color:var(--p-sub);}
.forma-dia strong{font-size:15px;font-variant-numeric:tabular-nums;}
.forma-dia em{font-style:normal;font-size:10px;color:var(--p-sub);margin-left:4px;}
.gaveta{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:12px;padding-top:12px;border-top:1px dashed var(--p-border);font-size:12px;color:var(--p-sub);}
.gaveta .fin{width:120px;}
.dif-ok{color:var(--p-ok);font-weight:700;} .dif-bad{color:var(--p-err);font-weight:700;}
.lista-link{display:flex;flex-direction:column;}
.lista-link button{display:flex;justify-content:space-between;align-items:center;gap:10px;padding:11px 14px;background:none;border:none;border-bottom:1px solid var(--p-border);cursor:pointer;color:var(--p-text);font-family:inherit;font-size:13px;text-align:left;}
.lista-link button:last-child{border-bottom:none;}
.lista-link button:hover{background:var(--p-row-hover);}
.lista-link .qt{font-weight:800;font-size:16px;min-width:26px;display:inline-block;font-variant-numeric:tabular-nums;}
.lista-link .dir{display:flex;align-items:center;gap:8px;color:var(--p-sub);font-size:12px;}
.mes-linha{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;}
.mes-linha small{display:block;font-size:10px;font-weight:700;text-transform:uppercase;letter-spacing:.4px;color:var(--p-sub);}
.mes-linha strong{font-size:17px;font-variant-numeric:tabular-nums;}
.dev-row{display:grid;grid-template-columns:1.3fr 1.4fr 110px 90px auto;gap:10px;align-items:center;padding:11px 14px;border-bottom:1px solid var(--p-border);font-size:13px;}
.dev-row:last-child{border-bottom:none;}
.dev-row small{display:block;color:var(--p-sub);font-size:11px;}
@media(max-width:820px){
  .hoje-grid{grid-template-columns:1fr;}
  .mes-linha{grid-template-columns:repeat(2,1fr);}
  .dev-row{grid-template-columns:1fr auto;}
  .dev-row>:nth-child(2){grid-column:1/-1;order:3;}
  .dev-row>:nth-child(4){display:none;}
}

/* Contas a Pagar (0048): data do pagamento sob o status, título cancelado esmaecido, ações
   numa linha só, e a marca discreta do gasto que veio de um título em Gastos. */
.sub-situacao{display:block;font-size:10px;color:var(--p-sub);margin-top:2px;}
tr.linha-cancelada td{color:var(--p-sub);}
tr.linha-cancelada td:first-child{text-decoration:line-through;}
.acoes-titulo{white-space:nowrap;}
.acoes-titulo .btn-sm{margin-right:6px;vertical-align:middle;}
.gasto-titulo{display:block;font-size:11px;color:var(--p-sub);}
.st-badge.neutro{background:var(--p-border);color:var(--p-sub);}
