/* Raiz · sistema de design v2
   Hierarquia por tom (quatro camadas), quase nenhuma linha. Fraunces para títulos e números; Instrument Sans no corpo.
   Um acento (lima→água) que só aparece onde a IA está trabalhando. Cores restantes carregam significado, não decoração. */
@import url('https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght,SOFT@9..144,300..700,50&family=Instrument+Sans:wght@400;500;600;700&display=swap');
@property --spin { syntax: '<angle>'; inherits: false; initial-value: 0deg; }

:root {
  --l0: #151614; --l1: #1C1D1B; --l2: #232522; --l3: #2C2E2A; --l4: #363934;
  --ink: #F2F0EA; --ink2: #CBC9C1; --mute: #8D8C85; --faint: #5E5E58;
  --acc1: #C6F26B; --acc2: #5FD7C3; --acc: var(--acc1);
  --ai: linear-gradient(110deg, var(--acc1), var(--acc2));
  --leaf: #9BD98A; --honey: #E8B65A; --berry: #F2807A;
  /* derivados: quem troca --leaf na tela de tema troca o fundo suave junto, sem digitar duas vezes */
  --leafSoft: color-mix(in srgb, var(--leaf) 14%, transparent);
  --honeySoft: color-mix(in srgb, var(--honey) 14%, transparent);
  --berrySoft: color-mix(in srgb, var(--berry) 14%, transparent);
  --r: 22px; --r2: 14px; --pill: 999px;
  /* medidas que a tela de tema ajusta */
  --pad: clamp(20px, 2.4vw, 60px);      /* margem lateral da página */
  --gap: 14px;                          /* espaço entre cartões do painel */
  --colmin: 300px;                      /* largura mínima da coluna do painel; menor = mais colunas */
  --size: 14px;                         /* tamanho base do texto */
  --lh: 1.5;                            /* altura da linha do corpo */
  --track: 0em;                         /* espaço entre letras do corpo */
  --bw: 1.5px;                          /* largura da moldura de cartão, botão e linha selecionada */
  --spinDur: 5s;                        /* uma volta do anel animado */
  --ease: cubic-bezier(.2,.7,.2,1);
  --display: "Fraunces", Georgia, serif; --body: "Instrument Sans", ui-sans-serif, system-ui, sans-serif;
  /* Peso e estilo em variável porque a tela de Aparência mexe neles. Os valores são os que o Raiz
     sempre usou: título em 500 (o Fraunces é largo; 700 fecha os contornos) e corpo em 400. */
  --dispWeight: 500; --dispStyle: normal; --bodyWeight: 400; --bodyStyle: normal;
  /* Animação da padronagem do fundo. `none` é nome de animação válido e é o padrão: quem não escolheu
     desenho nem movimento não paga nem um quadro de renderização. Ver os @keyframes pat* abaixo. */
  --bgAnim: none; --bgDur: 60s; --bgDir: normal;
  /* ── tokens de contraste ──
     Existem porque o tema claro precisa invertê-los. Antes eram rgba() e hex fixos espalhados
     pelo arquivo: no fundo claro a zebra da tabela sumia e o texto sobre o acento ficava ilegível.
     Regra: nenhuma cor literal fora deste bloco. */
  --edge: rgba(255,255,255,.03);        /* fio de luz no topo do cartão */
  --zebra: rgba(255,255,255,.018);      /* linha ímpar da tabela e coluna do gráfico sob o mouse */
  --inkHi: #fff;                        /* botão sólido sob o mouse (mais claro que --ink) */
  --onAcc: #10140A;                     /* texto sobre o gradiente de acento; o acento é claro nos dois temas */
  --onSemantic: #1A0D10;                /* texto sobre leaf/honey/berry cheios */
  --accSoft: color-mix(in srgb, var(--acc1) 18%, transparent); /* seleção, brilho do herói, item novo no PDV */
  --kbdBg: rgba(0,0,0,.25);             /* fundo da tecla */
  --scrim: rgba(0,0,0,.55);             /* fundo atrás de gaveta e modal */
  --drop: 0 20px 50px -20px rgba(0,0,0,.8);
  --halo: .35;                          /* intensidade do brilho no hover; 0 desliga */
  --haloSize: 14px;                     /* quanto o brilho escapa para fora do cartão */
  --haloBlur: 22px;                     /* borrão do brilho; pequeno vira contorno, grande vira névoa */
  /* ── opacidade, uma por camada ──
     Antes havia um alfa só (--cardAlpha) e ele alcançava três lugares derivados. O resultado é o que
     se via em Notas e em Entrada/Saída Manual: o cartão ficava translúcido e os CAMPOS dentro dele
     — que são --l3 — continuavam sólidos, mais acesos que o cartão que os contém. O cartão parecia
     não obedecer justamente nas telas em que ele é quase todo feito de campo.
     Agora cada camada tem o seu alfa e cada uma vira uma superfície pronta: `--sN` é a camada `--lN`
     já com a opacidade `--lNa`.
     A REGRA, e ela é curta: **quem pinta fundo usa --sN; quem desenha borda, sombra, traço de gráfico
     ou anel usa --lN cru.** Borda translúcida não revela nada de útil, some contra o próprio fundo, e
     um anel de foco que desaparece é um problema de acessibilidade, não um enfeite.
     Todos nascem em 100%: ninguém vê diferença até mexer no slider da Aparência. */
  --l0a: 100%; --l1a: 100%; --l2a: 100%; --l3a: 100%; --l4a: 100%;
  --s0: color-mix(in srgb, var(--l0) var(--l0a), transparent);
  --s1: color-mix(in srgb, var(--l1) var(--l1a), transparent);
  --s2: color-mix(in srgb, var(--l2) var(--l2a), transparent);
  --s3: color-mix(in srgb, var(--l3) var(--l3a), transparent);
  --s4: color-mix(in srgb, var(--l4) var(--l4a), transparent);
  /* ── padronagem ──
     O ladrilho em si é montado pelo theme.js (um SVG em data-URI) e chega por estas variáveis.
     `none` é o padrão e é um valor válido de camada de fundo: quem não subiu desenho nenhum paga
     zero, porque `none 0 0/auto repeat` simplesmente não pinta nada.
     São duas famílias independentes de propósito — o dono pode querer uma figura discreta atrás da
     página e outra, ou nenhuma, dentro dos cartões. `--pat*Bg` existe para as regras lá embaixo
     citarem a camada inteira numa palavra só: fundo é lista, e repetir os quatro pedaços em cada
     regra é onde se esquece um. */
  /* `*W`/`*H` são a mesma medida de `*Size` separada por eixo. Existem só para as keyframes do fundo
     poderem empurrar o desenho por um ladrilho num eixo e deixar o outro parado — coisa que não se
     escreve a partir de "96px 72px" numa palavra só. */
  --patSite: none; --patSiteSize: auto; --patSiteRepeat: repeat; --patSitePos: 0 0; --patSiteW: 0px; --patSiteH: 0px;
  --patCard: none; --patCardSize: auto; --patCardRepeat: repeat; --patCardPos: 0 0; --patCardW: 0px; --patCardH: 0px;
  --patSiteBg: var(--patSite) var(--patSitePos)/var(--patSiteSize) var(--patSiteRepeat);
  --patCardBg: var(--patCard) var(--patCardPos)/var(--patCardSize) var(--patCardRepeat);
  /* piso opaco das superfícies que se apoiam na página: é o que o slider de opacidade revela.
     Cor crua de propósito — o piso existe justamente para não deixar passar nada. */
  --under: linear-gradient(var(--l0), var(--l0));
  /* Aliases usados pelas páginas. Os que nomeiam SUPERFÍCIE apontam para `--sN` e por isso obedecem
     ao slider; `--line`/`--line2` nomeiam TRAÇO e continuam na cor crua, senão a divisória some. */
  --bg: var(--s0); --bg2: var(--s1); --surface: var(--s1); --surface2: var(--s2); --surface3: var(--s3); --line: var(--l3); --line2: var(--l4); --paper: var(--s0); --soft: var(--s2); --mint: var(--acc2); --brand: var(--ai);
}
/* tema claro: as camadas sobem de tom em vez de descer, e os tokens de contraste invertem.
   Um só atributo em <html> troca a interface inteira — nenhuma regra abaixo sabe qual tema está ativo. */
:root[data-theme="light"] {
  --l0: #F7F6F1; --l1: #FFFFFF; --l2: #F0EEE7; --l3: #E3E0D6; --l4: #D2CEC1;
  --ink: #1A1B18; --ink2: #40423C; --mute: #74766D; --faint: #A3A49B;
  --acc1: #8FD11F; --acc2: #17B49B;
  --leaf: #3E9B31; --honey: #B07914; --berry: #C7443C;
  /* leafSoft/honeySoft/berrySoft/accSoft não aparecem aqui: são color-mix sobre as cores acima e
     acompanham a troca sozinhos. */
  --edge: rgba(0,0,0,.04); --zebra: rgba(0,0,0,.022); --inkHi: #000;
  --onAcc: #10140A; --onSemantic: #FFFFFF;
  --kbdBg: rgba(0,0,0,.07); --scrim: rgba(30,32,28,.38); --drop: 0 18px 44px -22px rgba(0,0,0,.28);
}
* { box-sizing: border-box }
html, body { margin: 0; height: 100% }
/* A animação anda com a camada da padronagem e deixa o degradê de baixo onde está — daí os dois
   valores em cada keyframe, um por camada do `background`. Andar exatamente um ladrilho é o que faz
   a volta fechar sem emenda: no fim do ciclo o desenho está de novo sobre si mesmo. */
@keyframes patX { to { background-position: var(--patSiteW) 0, 0 0 } }
@keyframes patY { to { background-position: 0 calc(-1 * var(--patSiteH)), 0 0 } }
@keyframes patXY { to { background-position: var(--patSiteW) calc(-1 * var(--patSiteH)), 0 0 } }
body { font-family: var(--body); font-size: var(--size); font-weight: var(--bodyWeight); font-style: var(--bodyStyle); letter-spacing: var(--track); line-height: var(--lh); color: var(--ink); background: var(--patSiteBg), linear-gradient(var(--l0), var(--l0)); font-feature-settings: "tnum"; -webkit-font-smoothing: antialiased;
  animation: var(--bgAnim) var(--bgDur) linear infinite var(--bgDir) }
button, input, select, textarea { font: inherit; color: inherit }
a { color: var(--ink); text-decoration: none }
::selection { background: color-mix(in srgb, var(--acc1) 25%, transparent) }
:focus-visible { outline: 2px solid var(--acc); outline-offset: 2px }
::-webkit-scrollbar { width: 10px; height: 10px } ::-webkit-scrollbar-thumb { background: var(--s3); border-radius: 99px; border: 3px solid var(--l0) }

/* ── tipografia ── */
h1 { font-family: var(--display); font-variation-settings: "opsz" 72, "SOFT" 50; font-weight: 500; font-size: 30px; letter-spacing: -.01em; line-height: 1.1; margin: 0 }
h2 { font-family: var(--display); font-variation-settings: "opsz" 36, "SOFT" 50; font-weight: 500; font-size: 20px; margin: 0 }
.m { color: var(--mute) } .b { font-weight: 600 } .r { text-align: right } .sp { flex: 1 } .row { display: flex; justify-content: space-between; align-items: baseline }
.grad { background: var(--ai); -webkit-background-clip: text; background-clip: text; color: transparent }

/* ── camadas ── */
.card, .tile, .modal, .drawer, .box, .msg.a { background: var(--s1); border: 0; border-radius: var(--r) }
/* Janela, gaveta e balão de conversa também leem `--s1`: quem quiser uma gaveta translúcida pode ter.
   Eles existem para tapar o que está atrás, então texto sobre texto é o risco real de baixar o alfa
   dessa camada — mas é uma escolha de quem configura, não uma trava. O slider nasce em 100%. */
/* A camada de padronagem vem antes da cor porque em `background` a primeira camada fica por cima.
   A cor entra como gradiente de uma cor só: numa lista de camadas, `background-color` só pode ser
   a última e não aceita a sintaxe `posição/tamanho` — escrever tudo como imagem deixa as regras
   iguais entre si e evita a pegadinha de a cor apagar a figura.
   `--under` é a ÚLTIMA camada, ou seja, a de baixo: um piso opaco na cor da página. O slider de
   opacidade continua valendo — o cartão a 80% é a mistura da cor dele com a da página, que é o que
   se quer ver —, mas o cartão deixa de ser um buraco. Sem esse piso, tudo o que mora atrás dele
   subia à face: o halo de hover (uma cunha cônica do tamanho do cartão) e a padronagem do site.
   Quem quiser figura DENTRO do cartão tem a padronagem dos cartões, que é um ajuste separado.
   Só entram aqui as superfícies que se apoiam na página. Cartão dentro de cartão não leva piso: ali
   o que está atrás é o cartão de fora, e revelá-lo é justamente o efeito. */
.card, .tile, .box { background: var(--patCardBg), linear-gradient(var(--s1), var(--s1)), var(--under) }
.card { box-shadow: inset 0 1px 0 var(--edge) }
.card .card, .modal .card { background: var(--patCardBg), linear-gradient(var(--s2), var(--s2)) }
/* dentro de um painel com fundo próprio, o piso muda de assunto: o que está atrás não é mais a
   página, é o painel. Vale para o `.tile` (que é a pastilha de produto do PDV e o ícone do
   launcher), e é `> *` e não o próprio seletor porque o painel também usa `--under` no fundo dele —
   declarar na mesma caixa faria a superfície se apoiar em si mesma e o slider dela pararia de valer. */
:is(.card, .modal, .drawer, .left) > * { --under: linear-gradient(var(--l1), var(--l1)) }
/* o brilho do herói é fundo, não pseudo-elemento: com ::after ele precisava de `overflow: hidden`,
   e o overflow recortava o halo de hover lá embaixo. */
.hero { background: var(--patCardBg), radial-gradient(closest-side at 108% 132%, var(--accSoft), transparent 70%), linear-gradient(var(--s2), var(--s2)), var(--under); position: relative }

/* anel animado: só onde a IA trabalha. Fundo duplo, sem máscara. */
.glow { position: relative; border: var(--bw) solid transparent !important; border-radius: var(--r);
  background: linear-gradient(var(--s2), var(--s2)) padding-box, conic-gradient(from var(--spin), var(--l4) 0 55%, var(--acc1) 72%, var(--acc2) 84%, var(--l4) 96% 100%) border-box !important;
  animation: spin var(--spinDur) linear infinite }
.glow::after { content: ""; position: absolute; inset: calc(-1 * var(--haloSize)); z-index: -1; border-radius: inherit; pointer-events: none; opacity: var(--halo); filter: blur(var(--haloBlur));
  background: conic-gradient(from var(--spin), transparent 0 55%, var(--acc1) 72%, var(--acc2) 84%, transparent 96% 100%); animation: spin var(--spinDur) linear infinite }
@keyframes spin { to { --spin: 360deg } }
/* estado "pensando": brilho corre pela superfície */
.thinking { background-image: linear-gradient(100deg, transparent 30%, var(--accSoft) 50%, transparent 70%); background-size: 240% 100%; animation: sweep 1.6s linear infinite }
@keyframes sweep { from { background-position: 120% 0 } to { background-position: -120% 0 } }

/* ── controles ── */
.chip { display: inline-flex; align-items: center; padding: 4px 10px; border-radius: var(--pill); font-size: 12px; font-weight: 600; background: var(--s3); color: var(--ink2) }
.ok { background: var(--leafSoft); color: var(--leaf) } .warn { background: var(--honeySoft); color: var(--honey) } .bad { background: var(--berrySoft); color: var(--berry) }
/* Chip que se escolhe (motivo do ajuste manual de estoque). A cor do chip é a do RISCO — quebra é
   vinho, doação é folha — e ela NÃO pode virar a cor da seleção: se o escolhido ficasse verde, o
   lojista leria "deu certo" onde está escrito "perdi mercadoria". Então o escolhido guarda o próprio
   tom e ganha um anel em `currentColor`; os outros desbotam. O anel é `box-shadow` e não `border`
   para não empurrar o texto meio pixel quando a seleção troca. */
.chip.pick { border: 0; font: inherit; font-size: 12px; font-weight: 600; cursor: pointer; opacity: .5; transition: opacity .16s, box-shadow .16s }
.chip.pick:hover { opacity: .8 }
.chip.pick.on { opacity: 1; box-shadow: 0 0 0 2px currentColor inset }
/* selo com ✕ embutido, só no cadastro do produto: no caixa o mesmo selo é .chip puro, sem botão. */
.chip.rm { gap: 6px; padding-right: 5px } .chip.rm button { border: 0; padding: 0 3px; background: none; color: inherit; font: inherit; line-height: 1; cursor: pointer; opacity: .55; transition: opacity .12s } .chip.rm button:hover { opacity: 1 }
/* interruptor: alvo grande de propósito. A caixinha nativa de 13 px fazia o clique escapar. */
.sw { display: inline-flex; align-items: center; gap: 9px; padding: 7px 14px 7px 9px; border: 0; border-radius: var(--pill); background: var(--s3); color: var(--mute); font: inherit; font-size: 13px; font-weight: 600; cursor: pointer; transition: background .18s, color .18s }
.sw > i { width: 34px; height: 20px; border-radius: 99px; background: var(--s4); position: relative; transition: background .18s }
.sw > i::after { content: ""; position: absolute; top: 3px; left: 3px; width: 14px; height: 14px; border-radius: 99px; background: var(--ink); transition: transform .18s var(--ease) }
.sw.on { background: var(--leafSoft); color: var(--leaf) }
.sw.on > i { background: var(--leaf) } .sw.on > i::after { transform: translateX(14px) }
.kbd { display: inline-block; font-size: 10.5px; font-weight: 600; padding: 1px 6px; border-radius: 6px; background: var(--kbdBg); color: var(--mute); line-height: 1.6 }
.empty { padding: 48px 20px; text-align: center; color: var(--mute) }
.field { width: 100%; padding: 12px 16px; border: 0; border-radius: var(--r2); background: var(--s2); color: var(--ink); transition: background .15s, box-shadow .15s }
.card .field, .modal .field, .drawer .field { background: var(--s3) }
.field::placeholder { color: var(--mute) } .field:hover { background: var(--s3) }
.field:focus { outline: none; background: var(--s3); box-shadow: 0 0 0 2px var(--l4) }
select.field { appearance: none; padding-right: 36px; background-image: linear-gradient(45deg, transparent 50%, var(--mute) 50%), linear-gradient(135deg, var(--mute) 50%, transparent 50%); background-position: calc(100% - 18px) 50%, calc(100% - 13px) 50%; background-size: 5px 5px; background-repeat: no-repeat }
select.field option { background: var(--s2) } input[type=date].field::-webkit-calendar-picker-indicator { filter: invert(.6) }
textarea.field { resize: vertical }
.btn { display: inline-flex; align-items: center; justify-content: center; gap: 8px; padding: 10px 18px; border-radius: var(--pill); font-weight: 600; font-size: 14px; border: 0; cursor: pointer; white-space: nowrap;
  background: var(--ink); color: var(--l0); transition: transform .1s var(--ease), background .15s, opacity .15s }
.btn:hover:not(:disabled) { background: var(--inkHi) } .btn:active:not(:disabled) { transform: scale(.97) } .btn:disabled { opacity: .35; cursor: not-allowed }
.btn.ghost { background: var(--s3); color: var(--ink) } .btn.ghost:hover:not(:disabled) { background: var(--s4) }
.btn.danger { background: var(--berrySoft); color: var(--berry) }
.btn.primary-grad, .btn.ai { background: var(--ai); color: var(--onAcc) } .btn.ai:hover:not(:disabled), .btn.primary-grad:hover:not(:disabled) { background: var(--ai); filter: brightness(1.08) }
.btn .kbd { background: var(--kbdBg); color: inherit } .btn.ghost .kbd { background: var(--kbdBg); color: var(--mute) }
.icon { background: none; border: 0; padding: 6px; cursor: pointer; color: var(--mute); border-radius: 10px; display: inline-flex; transition: background .12s, color .12s }
.icon:hover { background: var(--s3); color: var(--ink) } .icon.del:hover { color: var(--berry); background: var(--berrySoft) }
.tab { padding: 8px 14px; border-radius: var(--pill); font-size: 13px; font-weight: 600; color: var(--ink2); cursor: pointer; border: 0; background: transparent; white-space: nowrap; transition: background .12s, color .12s }
.tab:hover { background: var(--s2); color: var(--ink) } .tab.on { background: var(--s3); color: var(--ink) }

/* ── tabelas: sem linhas, zebra tonal ── */
table { width: 100%; border-collapse: separate; border-spacing: 0; font-size: 14px }
th { text-align: left; font-weight: 500; color: var(--mute); font-size: 12px; padding: 12px 16px }
td { padding: 13px 16px; vertical-align: middle; border: 0 }
tbody tr:nth-child(odd) td { background: var(--zebra) }
tbody tr td:first-child { border-radius: 12px 0 0 12px } tbody tr td:last-child { border-radius: 0 12px 12px 0 }
tbody tr:hover td { background: var(--s2) }

/* ── layout do painel: trilho de ícones ── */
/* ── casca: barra fina em cima, tela cheia embaixo. Navegar é papel do launcher, não de um trilho fixo. ── */
.app { display: grid; grid-template-rows: 56px 1fr; height: 100vh }
/* ".top" e não ".bar": .bar já é o gráfico de barras lá embaixo — a casca herdava height:160px dele. */
.top { display: flex; align-items: center; gap: 14px; padding: 0 18px 0 12px; background: var(--s0) }
.launch { display: flex; align-items: center; gap: 9px; padding: 6px 12px 6px 6px; border: 0; border-radius: var(--pill); background: none; color: var(--ink); font: inherit; font-family: var(--display); font-weight: 600; font-size: 15px; cursor: pointer; transition: background .14s }
.launch:hover { background: var(--s2) }
.launch > i { width: 30px; height: 30px; border-radius: 10px; background: var(--ai); display: grid; place-items: center; color: var(--onAcc); flex-shrink: 0 }
.launch > i svg { width: 17px; height: 17px }
.launch > span + span { color: var(--mute) } .launch > span + span svg { width: 15px; height: 15px }
.where { margin: 0; font-family: var(--body); font-size: 13px; font-weight: 600; color: var(--mute); padding-left: 14px; border-left: 1px solid var(--l3); letter-spacing: .01em }
.lnk { color: var(--mute); font-size: 12.5px; font-weight: 500; text-decoration: none; padding: 6px 10px; border-radius: var(--pill); transition: background .14s, color .14s } .lnk:hover { background: var(--s2); color: var(--ink) }
.me { width: 30px; height: 30px; border-radius: 99px; border: 0; background: var(--s3); color: var(--ink); font: inherit; font-weight: 700; font-size: 11px; cursor: pointer; flex-shrink: 0 } .me:hover { background: var(--s4) }
.live { display: grid; place-items: center; padding: 0 2px } .live i { width: 7px; height: 7px; border-radius: 99px; background: var(--leaf) } .live.off i { background: var(--berry) }

/* ── launcher: a grade É a navegação. Ícone grande, nome embaixo, busca em cima. ── */
/* minmax(0,1fr) nas colunas: sem isso a coluna implícita cresce até o max-content da grade de ícones
   (auto-fill mede todos numa linha só) e o launcher rola de lado com metade dos aplicativos cortada. */
/* As duas linhas são `auto`, e não `auto 1fr`: com `1fr` a faixa da grade já ocupava toda a altura
   sobrando, então centralizar não teria efeito nenhum — os cartões apenas encostavam no topo de uma
   faixa do tamanho da tela. Com as duas em `auto` o bloco mede o que ele é e o `align-content` passa
   a mandar. O `safe` é o que impede o remédio de virar defeito: centralizar conteúdo que não cabe
   corta o começo dele, e a busca sumiria acima da borda sem rolagem que a alcance — com `safe`, se
   transbordar o navegador volta a alinhar pelo topo sozinho. Efeito de lado bem-vindo: o vazio em
   volta agora pertence ao scrim, e não à grade, então clicar longe dos cartões fecha a gaveta. */
.lscrim { position: fixed; inset: 0; z-index: 60; display: grid; grid-template-rows: auto auto; grid-template-columns: minmax(0, 1fr); align-content: safe center; gap: 26px; padding: 40px; overflow: auto; background: color-mix(in srgb, var(--s0) 82%, transparent); backdrop-filter: blur(26px) saturate(1.3); animation: lin .22s var(--ease) both }
@keyframes lin { from { opacity: 0; backdrop-filter: blur(0) } }
.lhead { display: flex; align-items: center; gap: 12px; justify-content: center }
.lsearch { width: min(440px, 70vw); padding: 12px 20px; border: 0; border-radius: var(--pill); background: var(--s2); color: var(--ink); font: inherit; font-size: 15px; text-align: center; outline: 0 }
.lsearch::placeholder { color: var(--mute) } .lsearch:focus { background: var(--s3) }
/* Flex, e não grade, porque o que se pede aqui é o contrário do que a grade faz bem. Com colunas
   `1fr` os cartões esticavam para ocupar a largura toda, então o bloco nunca estava centralizado:
   ele era a linha inteira. E a última fileira, sempre incompleta, encostava à esquerda. Em flex com
   largura fixa por cartão, cada fileira mede o que tem e o `justify-content` centraliza — inclusive
   a última. `flex-grow: 0` é a peça essencial: com grow, um cartão sozinho (busca filtrando até
   sobrar um) inflaria para 1600 px de largura. */
.lpad { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px; max-width: 1600px; width: 100%; min-width: 0; margin: 0 auto }
.lpad .empty { flex: 1 1 100% }
/* Cartão, não ícone. O `.tile` que o botão também carrega já traz fundo, raio, anel de hover e o
   afundar no clique — daqui sai só o que é do launcher: o selo em cima e o nome legível embaixo.
   Reusar o cartão do sistema é o que faz o launcher acompanhar sozinho o tema, o alfa e a padronagem.
   O seletor é `.tile.appicon` (duas classes) porque `.tile` está mais abaixo neste arquivo: com uma
   classe só, o empate de especificidade cai para a ordem e a altura do `.tile` venceria a daqui. */
/* `backwards`, não `both`: com `both` o preenchimento do fim da animação continua mandando no
   `transform` depois que ela acaba e o afundar do `.tile:active` nunca aparece. `backwards` só
   segura o estado inicial durante o atraso escalonado e depois devolve o controle ao CSS normal. */
.tile.appicon { flex: 0 0 212px; border: 0; color: var(--ink); text-decoration: none; font: inherit; font-size: 15px; font-weight: 600; line-height: 1.2; justify-content: space-between; min-height: 132px; animation: pop .3s var(--ease) backwards; animation-delay: calc(var(--n) * 14ms) }
@keyframes pop { from { opacity: 0; transform: scale(.9) translateY(8px) } }
.appicon > i { width: 46px; height: 46px; border-radius: 15px; background: var(--s3); display: grid; place-items: center; color: var(--ink); position: relative; flex-shrink: 0; transition: background .16s }
.appicon > i svg { width: 22px; height: 22px }
.appicon.on > i { background: var(--ai); color: var(--onAcc) }
.appicon .badge { position: absolute; top: -3px; right: -3px; margin: 0; background: var(--honey); color: var(--onSemantic); border-radius: 99px; padding: 0 6px; font-size: 10px; font-weight: 700 }
/* isolation: isolate é o que faz o brilho dos cards existir. O halo é um ::before com z-index:-1, e um
   filho negativo é pintado ANTES do fundo de um bloco ancestral — ou seja, o fundo opaco do <main>
   passava por cima e engolia o borrão, sobrando só a linha animada da borda. Virando contexto de
   empilhamento próprio, o main pinta o fundo dele primeiro e o halo por cima. */
main { overflow: auto; min-width: 0; background: var(--patSiteBg), linear-gradient(var(--l0), var(--l0)); isolation: isolate }
/* sem teto de largura: a tela é do dono, não de uma coluna de 1180 px. O respiro vem do padding,
   que cresce junto com o monitor. Só o texto corrido continua limitado, senão a linha fica ilegível. */
/* `backwards` e NÃO `both`: com `both` a animação continua aplicada depois de terminar, então o
   transform do keyframe nunca volta a `none` — fica na matriz identidade, que ainda assim é um
   transform e cria contexto de empilhamento. Aí o ::before do halo (z-index:-1) passa a valer dentro
   do próprio cartão e é pintado DEPOIS do fundo dele, ou seja, por cima: o brilho lavava a face nas
   telas de formulário alto (Notas, Entrada/Saída Manual). `backwards` mantém o efeito do atraso —
   que é o único motivo do fill aqui, senão os filhos 2 e 3 piscam visíveis antes de começar — e
   solta o elemento ao terminar, devolvendo transform: none. */
.page { padding: 34px var(--pad) 60px; animation: reveal .5s var(--ease) backwards }
.page > * { animation: reveal .5s var(--ease) backwards } .page > *:nth-child(2) { animation-delay: .06s } .page > *:nth-child(3) { animation-delay: .12s }
@keyframes reveal { from { opacity: 0; transform: translateY(6px) } }
.head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; margin-bottom: 26px; flex-wrap: wrap } .head p { margin: 6px 0 0; color: var(--mute); max-width: 74ch }
.search { position: relative; max-width: 460px; margin-bottom: 16px } .search .field { padding-left: 42px; border-radius: var(--pill) } .search svg { position: absolute; left: 15px; top: 13px; color: var(--mute) }
/* barra de filtro das listas (o `filtro()` do app.js monta; toda tabela e grade da casa passa por ela).
   O `.search` da casa nasceu para ficar sozinho acima da lista e traz `margin-bottom` — dentro da
   barra ele é um item de linha, então a margem é zerada aqui em vez de removida de lá.
   `.flclear` esconde-se com `visibility`, não com `display`: sumindo da caixa ele levaria junto a
   largura, e a contagem à direita andaria para o lado a cada vez que a primeira letra é digitada. */
.flbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 14px }
.flbar .search { margin: 0; flex: 1 1 240px; max-width: 420px }
.flbar > select.field { width: auto; max-width: 220px; border-radius: var(--pill) }
.flcount { margin-left: auto; color: var(--mute); font-size: 12.5px; white-space: nowrap; font-variant-numeric: tabular-nums }
.flclear { visibility: hidden; padding: 9px 15px }
.flclear.on { visibility: visible }
@media (max-width: 560px) { .flbar .search { flex-basis: 100%; max-width: none } .flcount { margin-left: 0 } }
.grid { display: grid; gap: 14px } .g3 { grid-template-columns: 1.6fr 1fr }
.stat { padding: 26px } .stat .l { color: var(--mute); font-size: 13px } .stat .v { font-family: var(--display); font-variation-settings: "opsz" 96, "SOFT" 50; font-weight: 500; font-size: 44px; letter-spacing: -.02em; line-height: 1.05; margin: 4px 0 }
.stat .b { font-family: var(--display); font-variation-settings: "opsz" 36, "SOFT" 50; font-weight: 500; font-size: 17px }

/* gaveta */
.scrim { position: fixed; inset: 0; background: var(--scrim); display: flex; justify-content: flex-end; z-index: 40; animation: fade .2s } @keyframes fade { from { opacity: 0 } }
.drawer { width: 100%; max-width: 480px; height: 100%; border-radius: 28px 0 0 28px; background: var(--s1); display: flex; flex-direction: column; animation: slide .3s var(--ease) } @keyframes slide { from { transform: translateX(40px); opacity: 0 } }
.drawer header { display: flex; align-items: center; justify-content: space-between; padding: 24px 28px 12px } .drawer .body { padding: 12px 28px 24px; overflow: auto; flex: 1; display: flex; flex-direction: column; gap: 14px } .drawer footer { padding: 16px 28px 24px; display: flex; justify-content: flex-end; gap: 8px }
label.f { display: block } label.f span { display: block; font-weight: 500; color: var(--mute); margin-bottom: 6px; font-size: 12.5px }
.row2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px }
/* Dobras da gaveta. O cadastro de produto tem mais de vinte campos e ninguém preenche os vinte na
   mesma sessão: quem repõe estoque mexe em custo e validade, quem fecha o mês mexe em NCM. Cada
   seção é um <details>, e as que só um perfil usa nascem fechadas. O fundo é `--l0` de propósito —
   os campos são `--l2`, então a dobra precisa ser mais funda que a gaveta para eles flutuarem nela. */
.fold { background: var(--s0); border-radius: var(--r2); padding: 0 14px }
.fold > summary { cursor: pointer; list-style: none; display: flex; align-items: center; gap: 8px; padding: 13px 2px; font-size: 12px; font-weight: 700; letter-spacing: .06em; text-transform: uppercase; color: var(--mute); user-select: none; transition: color .15s }
.fold > summary::-webkit-details-marker { display: none }
.fold > summary:hover { color: var(--ink) }
.fold > summary > i { font: inherit; font-weight: 500; text-transform: none; letter-spacing: 0; opacity: .8 }
.fold > summary::after { content: ""; width: 7px; height: 7px; margin-left: auto; border-right: 2px solid currentColor; border-bottom: 2px solid currentColor; transform: rotate(-45deg); transition: transform .18s var(--ease) }
.fold[open] > summary::after { transform: rotate(45deg) }
.fold > .row2 { padding-bottom: 14px }
/* Campo de leitura: continua legível, mas perde o fundo de campo para não convidar ao clique.
   Estoque se move por nota e por venda — digitar aqui seria inventar um número sem lastro. */
label.f.ro .field { background: none; box-shadow: inset 0 0 0 1px var(--l3); color: var(--ink2); cursor: default }
/* Bloco calculado: o número que o sistema deduziu, com a frase que explica de onde ele saiu. Sem
   o acento lima→água, que nesta casa é reservado a onde a IA atua — isto aqui é aritmética. */
.calc { background: var(--s2); border-radius: var(--r2); padding: 12px 14px; display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 10px }
.calc .t { width: 100%; font-size: 12.5px; font-weight: 500; color: var(--mute) }
.calc b { font-family: var(--display); font-variation-settings: "opsz" 32; font-size: 26px; font-weight: 500; line-height: 1.1 }
.calc small { flex: 1; min-width: 140px; font-size: 12px; line-height: 1.35; color: var(--mute) }
.calc .btn { padding: 6px 12px; font-size: 12px; margin-left: auto }
/* Slot da foto no topo da gaveta: é por ela que se reconhece o item antes de mexer no cadastro.
   Vazio ele tem de parecer campo — a borda tracejada é o que faz a área ler como "solte aqui", e sem
   ela o retângulo liso vira espaçador aos olhos de quem não sabe que ali cabe alguma coisa.
   `.on` (já tem foto) troca a moldura pela própria imagem; `.off` (item ainda não salvo) desliga o
   convite inteiro, porque prometer clique onde o clique não funciona é pior do que não convidar. */
.shotpick { width: 100%; min-height: 168px; border-radius: 16px; background: var(--s2) center/cover no-repeat; border: 1.5px dashed color-mix(in srgb, var(--mute) 45%, transparent); display: grid; place-items: center; align-content: center; gap: 6px; text-align: center; padding: 20px; color: var(--mute); font-size: 13.5px; cursor: pointer; transition: background .15s, border-color .15s, color .15s }
.shotpick .ph { display: inline-flex; color: var(--mute); transition: color .15s } .shotpick .ph svg { width: 30px; height: 30px; stroke-width: 1.6 }
.shotpick b { color: var(--ink); font-weight: 650 } .shotpick .m { font-size: 12px }
.shotpick:hover { background-color: var(--s3); border-color: var(--acc1); color: var(--ink2) } .shotpick:hover .ph { color: var(--acc1) }
.shotpick.off { cursor: default; border-style: solid; border-color: transparent } .shotpick.off:hover { background-color: var(--s2); color: var(--mute) } .shotpick.off:hover .ph { color: var(--mute) } .shotpick.off b { color: var(--mute); font-weight: 600 }
/* com foto: a moldura sai de cena e o convite só aparece por cima quando o mouse chega */
.shotpick.on { border-color: transparent; padding: 0; align-content: end; justify-items: stretch }
.shotpick.on .over { display: flex; align-items: center; justify-content: center; gap: 8px; padding: 10px; border-radius: 0 0 15px 15px; background: color-mix(in srgb, var(--s0) 78%, transparent); backdrop-filter: blur(8px); color: var(--ink); font-weight: 600; opacity: 0; transition: opacity .15s }
.shotpick.on:hover .over { opacity: 1 }
.node { display: flex; align-items: center; gap: 4px; padding: 8px 10px; border-radius: 12px } .node:hover { background: var(--s2) } .node .acts { opacity: 0; margin-left: auto; display: flex } .node:hover .acts { opacity: 1 }
.toast { position: fixed; bottom: 24px; left: 50%; transform: translateX(-50%); background: var(--ink); color: var(--l0); padding: 10px 18px; border-radius: var(--pill); font-weight: 600; z-index: 60; animation: slideup .25s var(--ease) }
.toast.err { background: var(--berry); color: var(--onSemantic) } @keyframes slideup { from { transform: translate(-50%, 10px); opacity: 0 } }

/* OSCAR */
.chat { display: flex; flex-direction: column; height: 100vh }
.msgs { flex: 1; overflow: auto; padding: 40px; display: flex; flex-direction: column; gap: 14px; max-width: 880px; width: 100%; margin: 0 auto }
.msg { max-width: 82%; padding: 14px 18px; border-radius: 20px; white-space: pre-wrap; animation: reveal .3s var(--ease); line-height: 1.55 }
.msg.u { align-self: flex-end; background: var(--s3); color: var(--ink); border-bottom-right-radius: 6px } .msg.a { align-self: flex-start; border-bottom-left-radius: 6px }
.msg .acts { margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--l3); font-size: 12px; color: var(--mute); font-family: ui-monospace, monospace }
.compose { padding: 12px 40px 28px } .compose .in { display: flex; gap: 8px; max-width: 880px; margin: 0 auto; padding: 8px 8px 8px 20px; border-radius: 28px; background: var(--s2) }
.compose .in .field { background: transparent; box-shadow: none; padding: 8px 0 } .compose .in .field:focus, .compose .in .field:hover { background: transparent }
.sugg { display: flex; gap: 6px; flex-wrap: wrap; max-width: 880px; margin: 0 auto 12px } .sugg button { background: transparent; border: 1px solid var(--l3); border-radius: var(--pill); padding: 6px 12px; cursor: pointer; font-size: 12.5px; font-weight: 500; color: var(--ink2) } .sugg button:hover { background: var(--s2); color: var(--ink) }

/* Agente GPT flutuante: composição limpa, familiar e integrada ao sistema de design. */
.assistant-dock { position: fixed; right: 22px; bottom: 22px; z-index: 70; display: flex; flex-direction: column; align-items: flex-end; gap: 12px }
.assistant-launch { width: 58px; height: 58px; border: 0; border-radius: 50%; padding: 4px; display: grid; place-items: center; cursor: pointer; background: var(--s1); box-shadow: 0 18px 48px -16px var(--scrim), 0 0 22px var(--accSoft); transition: transform .2s var(--ease), box-shadow .2s }
.assistant-launch:hover { transform: translateY(-2px); box-shadow: 0 22px 54px -16px var(--scrim), 0 0 32px var(--accSoft) }
.assistant-launch .assistant-orb { width: 50px; height: 50px }
.assistant-orb { display: block; flex: none; border-radius: 50%; position: relative; overflow: hidden; background: radial-gradient(circle at 28% 20%, var(--inkHi) 0 3%, transparent 21%), radial-gradient(circle at 72% 70%, var(--acc2) 0 8%, transparent 44%), radial-gradient(circle at 30% 72%, var(--berry) 0 5%, transparent 42%), conic-gradient(from var(--spin), var(--acc1), var(--acc2), var(--berry), var(--honey), var(--acc1)); animation: assistant-orbit 7s linear infinite, assistant-breathe 3.4s ease-in-out infinite; box-shadow: inset 0 0 18px color-mix(in srgb, var(--inkHi) 24%, transparent), 0 0 24px var(--accSoft) }
.assistant-orb::before, .assistant-orb::after { content: ""; position: absolute; border-radius: inherit; pointer-events: none }
.assistant-orb::before { inset: 8%; background: radial-gradient(circle at 66% 30%, color-mix(in srgb, var(--inkHi) 65%, transparent), transparent 30%), linear-gradient(140deg, transparent 25%, color-mix(in srgb, var(--acc2) 60%, transparent), transparent 74%); filter: blur(5px); mix-blend-mode: screen }
.assistant-orb::after { inset: 22%; border: 1px solid color-mix(in srgb, var(--inkHi) 42%, transparent); box-shadow: 0 0 16px color-mix(in srgb, var(--inkHi) 25%, transparent) }
.assistant-orb i { position: absolute; width: 38%; height: 38%; left: 31%; top: 31%; border-radius: 50%; background: color-mix(in srgb, var(--s1) 36%, transparent); filter: blur(7px) }
@keyframes assistant-orbit { to { --spin: 360deg } }
@keyframes assistant-breathe { 50% { transform: scale(.93); filter: saturate(1.2) brightness(1.06) } }
.assistant-panel { width: min(430px, calc(100vw - 28px)); height: min(660px, calc(100dvh - 98px)); display: none; grid-template-rows: auto auto 1fr auto; overflow: hidden; border: 1px solid var(--l3); border-radius: 24px; background: var(--s1); box-shadow: 0 30px 90px -22px var(--scrim); transform-origin: bottom right }
.assistant-panel.open { display: grid; animation: rise .22s var(--ease) }
.assistant-panel.dragging-files { outline: 2px dashed var(--acc1); outline-offset: -8px }
.assistant-panel > header { min-height: 64px; padding: 12px 14px 12px 16px; display: flex; align-items: center; justify-content: space-between; border-bottom: 1px solid var(--l3); background: var(--s1) }
.assistant-panel > header .m { margin-top: 2px; font-size: 11px }
.assistant-identity, .assistant-head-actions { display: flex; align-items: center; gap: 9px }
.assistant-mini-orb { width: 28px; height: 28px; border-radius: 50%; background: var(--ai); box-shadow: 0 0 15px var(--accSoft); animation: assistant-breathe 3.4s ease-in-out infinite }
.assistant-voice-toggle, .assistant-panel > header .icon { width: 36px; max-width: 36px; height: 36px; overflow: hidden; flex: none; padding: 0; border: 0; border-radius: 50%; display: grid; place-items: center; cursor: pointer; background: transparent; color: var(--mute); transition: background .16s, color .16s }
.assistant-voice-toggle:hover, .assistant-panel > header .icon:hover { background: var(--s3); color: var(--ink) }
.assistant-voice-toggle.on { color: var(--ink); background: var(--accSoft) }
.assistant-voice-label { display: none }
.assistant-presence { border: 0; background: var(--s1); color: var(--ink); padding: 24px 18px 12px; display: flex; flex-direction: column; align-items: center; gap: 9px; cursor: pointer }
.assistant-presence .assistant-orb { width: 62px; height: 62px }
.assistant-presence-copy { display: flex; flex-direction: column; gap: 2px; align-items: center }
.assistant-presence-copy b { font-size: 14px }.assistant-presence-copy small { color: var(--mute); font-size: 11px }
.assistant-messages { overflow: auto; padding: 12px 18px 18px; display: flex; flex-direction: column; gap: 16px }
.assistant-messages .msg { max-width: 88%; padding: 0; font-size: 13px; line-height: 1.55; border-radius: 18px }
.assistant-messages .msg.u { align-self: flex-end; padding: 10px 13px; background: var(--s3); color: var(--ink) }
.assistant-messages .msg.a { align-self: flex-start; position: relative; padding-right: 34px; color: var(--ink2) }
.assistant-messages .acts { margin-top: 8px; padding: 8px 10px; border-radius: 10px; background: var(--s2); color: var(--mute); font-size: 11px; white-space: pre-line }
.assistant-sent-files { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 8px }
.assistant-sent-files span { max-width: 100%; display: inline-flex; align-items: center; gap: 4px; padding: 4px 7px; border-radius: var(--pill); background: var(--s2); font-size: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
.assistant-sent-files svg { width: 11px; height: 11px; flex: none }
.assistant-listen { position: absolute; right: 0; top: -6px; width: 30px; height: 30px; border: 0; border-radius: 50%; display: grid; place-items: center; cursor: pointer; background: transparent; color: var(--mute) }
.assistant-listen:hover { color: var(--ink); background: var(--s3) }
.assistant-listen svg { width: 14px; height: 14px }
.assistant-compose { padding: 10px 12px 12px; background: var(--s1) }
.assistant-composer-shell { border: 1px solid var(--l3); border-radius: 20px; padding: 10px; background: var(--s2); box-shadow: 0 7px 24px -20px var(--scrim) }
.assistant-compose textarea { width: 100%; min-height: 52px; max-height: 130px; resize: none; border: 0; padding: 2px 3px 8px; background: transparent; box-shadow: none }
.assistant-compose textarea:focus { outline: 0; box-shadow: none }
.assistant-composer-tools, .assistant-compose-left, .assistant-compose-right { display: flex; align-items: center }
.assistant-composer-tools { justify-content: space-between; gap: 10px }
.assistant-compose-left, .assistant-compose-right { gap: 7px }
.assistant-compose-hint { color: var(--mute); font-size: 10px }
.assistant-attachments { display: flex; gap: 6px; overflow-x: auto; padding: 0 2px 8px }
.assistant-attachments[hidden] { display: none }
.assistant-file { min-width: 0; max-width: 180px; display: inline-flex; align-items: center; gap: 5px; background: var(--s2); padding: 6px 8px; border-radius: var(--pill); color: var(--ink2); font-size: 10px }
.assistant-file > span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
.assistant-file svg { width: 12px; height: 12px; flex: none }
.assistant-file button { border: 0; background: none; color: var(--mute); cursor: pointer; padding: 0; font-size: 16px; line-height: 1 }
.assistant-attach, .assistant-mic, .assistant-send { width: 34px; height: 34px; border: 0; border-radius: 50%; display: grid; place-items: center; cursor: pointer; overflow: hidden; flex: none; transition: transform .15s, background .15s, color .15s }
.assistant-head-actions button > span, .assistant-composer-tools button > span { width: 16px; height: 16px; display: inline-flex; align-items: center; justify-content: center }
.assistant-head-actions svg, .assistant-composer-tools svg { width: 16px; height: 16px; display: block }
.assistant-attach, .assistant-mic { background: transparent; color: var(--ink2) }
.assistant-attach:hover, .assistant-mic:hover { background: var(--s3); transform: translateY(-1px) }
.assistant-send { background: var(--ink); color: var(--s1) }
.assistant-send:hover { transform: translateY(-1px) scale(1.03) }
.assistant-mic.recording { background: var(--berry); color: var(--onSemantic); animation: assistant-pulse 1.1s infinite }
.assistant-panel[data-state="listening"] .assistant-orb, .assistant-launch[data-state="listening"] .assistant-orb { animation-duration: 1.1s, .75s; filter: saturate(1.3) }
.assistant-panel[data-state="thinking"] .assistant-orb, .assistant-launch[data-state="thinking"] .assistant-orb { animation-duration: .8s, 1.4s }
.assistant-panel[data-state="speaking"] .assistant-orb, .assistant-launch[data-state="speaking"] .assistant-orb { animation: assistant-orbit 2.2s linear infinite, assistant-wave .65s ease-in-out infinite alternate }
.assistant-panel[data-state="error"] .assistant-orb, .assistant-launch[data-state="error"] .assistant-orb { filter: saturate(.35) }
@keyframes assistant-pulse { 50% { box-shadow: 0 0 0 7px var(--berrySoft) } }
@keyframes assistant-wave { from { transform: scale(.88) } to { transform: scale(1.06) } }

/* Controles do cartão Assistente GPT. */
.assistant-settings-card { padding: 22px; display: grid; gap: 12px; align-content: start }
.voice-control { display: grid; gap: 6px; color: var(--ink2); font-size: 12px }
.voice-speed { display: grid; gap: 8px; padding: 12px 14px; border-radius: 14px; background: var(--s2) }
.voice-speed-head, .voice-speed-scale, .voice-test-row { display: flex; align-items: center; justify-content: space-between; gap: 8px }
.voice-speed-head { color: var(--ink2); font-size: 12px }
.voice-speed-value { min-width: 48px; padding: 3px 8px; border-radius: var(--pill); background: var(--s3); color: var(--ink); font: inherit; font-weight: 700; text-align: center }
.voice-speed input[type="range"] { width: 100%; accent-color: var(--acc1) }
.voice-speed-scale { color: var(--mute) }
.voice-test-row { justify-content: flex-start; flex-wrap: wrap }
.agent-permission { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 14px; padding: 13px 14px; border-radius: 14px; background: var(--s2); cursor: pointer }
.agent-permission-copy { display: grid; gap: 3px }
.agent-permission-copy b { font-size: 13px }.agent-permission-copy small { color: var(--mute); font-size: 11px }
.agent-permission input { position: absolute; opacity: 0; pointer-events: none }
.agent-switch { width: 44px; height: 25px; padding: 3px; border-radius: var(--pill); background: var(--s4); transition: background .18s }
.agent-switch::after { content: ""; display: block; width: 19px; height: 19px; border-radius: 50%; background: var(--s1); box-shadow: 0 2px 8px var(--scrim); transition: transform .18s var(--ease) }
.agent-permission input:checked + .agent-switch { background: var(--leaf) }
.agent-permission input:checked + .agent-switch::after { transform: translateX(19px) }
.agent-permission input:focus-visible + .agent-switch { outline: 2px solid var(--acc1); outline-offset: 2px }

/* gráficos */
.chart { width: 100%; height: 190px; display: block; overflow: visible }
.chart .area { fill: url(#areaGrad); opacity: 0; animation: fade-in .9s .6s var(--ease) forwards }
.chart .line { fill: none; stroke: var(--ink); stroke-width: 1.75; stroke-linecap: round; stroke-linejoin: round; stroke-dasharray: 2000; stroke-dashoffset: 2000; animation: draw 1.4s var(--ease) forwards }
.chart .dot { fill: var(--ink); stroke: var(--l2); stroke-width: 3; opacity: 0; transition: opacity .15s } .chart .col:hover .dot { opacity: 1 }
.chart .lbl { fill: var(--mute); font-size: 10px; font-family: var(--body) } .chart .tip { fill: var(--ink); font-size: 11px; font-weight: 600; opacity: 0; transition: opacity .15s; font-family: var(--body) } .chart .col:hover .tip { opacity: 1 }
.chart .col rect { fill: transparent } .chart .col:hover rect { fill: var(--zebra) }
@keyframes draw { to { stroke-dashoffset: 0 } } @keyframes fade-in { to { opacity: 1 } }
.bar { display: flex; align-items: flex-end; gap: 6px; height: 160px; padding: 8px 0 }
.bar div { flex: 1; background: var(--s4); border-radius: 6px 6px 2px 2px; min-height: 2px; position: relative; transform-origin: bottom; animation: grow .7s var(--ease) both; animation-delay: calc(.03s * var(--i, 0)) }
.bar div:hover { background: var(--ink) } @keyframes grow { from { transform: scaleY(0) } }
.bar div b { position: absolute; bottom: 100%; left: 0; right: 0; text-align: center; font-size: 10px; color: var(--mute); font-weight: 500; margin-bottom: 3px }
.bar div s { position: absolute; top: 100%; left: 0; right: 0; text-align: center; font-size: 10px; color: var(--mute); text-decoration: none; margin-top: 5px; white-space: nowrap; overflow: hidden }

/* ── marca: o mesmo bloco (desenho + nome) na entrada, no caixa e na loja ── */
/* Uma linha só, com o desenho e o nome centrados: um no outro e no espaço que sobrar. O <i> precisa
   de caixa própria (grid) — enquanto era inline, o logo ficava pendurado na linha de base do texto,
   torto ao lado do nome, e o tamanho em porcentagem da tela de Aparência não tinha onde pegar. */
.brand { display: flex; align-items: center; justify-content: center; gap: 10px; font-family: var(--display); font-weight: 600; font-size: 16px; color: var(--ink); text-decoration: none }
.brand > i { width: 30px; height: 30px; border-radius: 10px; background: var(--ai); color: var(--onAcc); display: grid; place-items: center; flex-shrink: 0 }
.brand > i svg { width: 17px; height: 17px }

/* ── PDV ── */
header.top { display: flex; align-items: center; gap: 12px; padding: 0 18px; height: 60px; flex-shrink: 0 }
header.top .brand { padding: 0 } header.top .brand > i { width: 32px; height: 32px; border-radius: 11px; transition: transform .12s var(--ease) }
/* no caixa o logo é a porta de volta para o painel de aplicativos, então precisa parecer clicável —
   sem isso o caixa não descobre que dá para clicar ali. */
header.top a.brand:hover > i { transform: scale(1.08) } header.top a.brand:active > i { transform: scale(.94) }
/* três colunas: grade 2/4, ficha do produto 1/4, venda 1/4. Os minmax existem porque a proporção pura
   espremeria a venda a nada em tela de 13"; abaixo de 1100px a ficha some (ver o @media lá embaixo). */
.pos { flex: 1; display: grid; grid-template-columns: 2fr minmax(300px, 1fr) minmax(330px, 1fr); min-height: 0; gap: 12px; padding: 0 12px 12px }
/* mesmo motivo do isolate do <main> (linha acima): qualquer painel opaco ENTRE o cartão e o contexto de
   empilhamento engole o halo, porque o ::before de z-index:-1 vai parar atrás do fundo do painel. Aqui o
   contexto era o <main>, então o brilho das pastilhas de produto era pintado debaixo do fundo do .left e
   só sobrava a borda animada. Denunciava o problema o item esgotado, o único que acendia: .tile.dim tem
   opacity < 1, e opacity < 1 já cria contexto de empilhamento sozinho. */
.left { display: flex; flex-direction: column; min-height: 0; background: var(--s1); border-radius: var(--r); isolation: isolate }
.left .search { margin: 14px 16px 6px; max-width: none } .left .search .field { background: var(--s2); font-size: 16px; padding: 13px 44px 13px 44px } .left .search svg { top: 15px } .left .search .kbd { position: absolute; right: 14px; top: 15px }
.tabs { display: flex; gap: 4px; padding: 4px 16px 10px; overflow: auto }
.pgrid { flex: 1; overflow: auto; padding: 0 16px 16px; display: grid; grid-template-columns: repeat(auto-fill, minmax(172px, 1fr)); gap: 10px; align-content: start }
.tile { padding: 16px; text-align: left; cursor: pointer; border-radius: 18px; background: var(--patCardBg), linear-gradient(var(--s2), var(--s2)), var(--under); transition: background .15s, transform .1s var(--ease); display: flex; flex-direction: column; gap: 10px; min-height: 108px }
/* o hover repete a camada de padronagem porque `background` é atalho: escrever só a cor aqui apagaria
   o `background-image` e a padronagem sumiria debaixo do mouse. */
.tile:hover { background: var(--patCardBg), linear-gradient(var(--s3), var(--s3)), var(--under) } .tile:active { transform: scale(.97) } .tile.dim { opacity: .35; cursor: not-allowed }
.tile .n { font-weight: 500; line-height: 1.25; color: var(--ink2) } .tile .p { margin-top: auto; display: flex; justify-content: space-between; align-items: baseline; font-family: var(--display); font-variation-settings: "opsz" 36, "SOFT" 50; font-weight: 500; font-size: 19px } .tile .p small { font-family: var(--body); font-weight: 500; font-size: 12px; color: var(--mute) }
/* ficha do produto sob o mouse (ou da linha escolhida na venda). Só leitura: não é .tile de propósito,
   senão herdaria o cursor de clique e o anel de hover das pastilhas e pareceria botão. */
.peek { display: flex; flex-direction: column; gap: 12px; min-height: 0; overflow: auto; padding: 16px; background: var(--s1); border-radius: var(--r) }
.peek .shot { height: 148px; flex-shrink: 0; border-radius: 18px; background: var(--s3) center/cover no-repeat; display: grid; place-items: center; color: var(--mute); font-size: 12px }
/* 56px é tamanho de cartaz, não de rótulo: o caixa lê o nome do item de longe, sem se debruçar na tela.
   Nesse corpo a `opsz` sobe para 96 (a fonte afina os traços em tamanho grande, senão engorda) e o
   `line-height` fecha para 1.02, porque entrelinha de texto corrido em duas palavras grandes abre um
   buraco. `anywhere` é a rede de segurança: nome comprido quebra dentro da palavra em vez de vazar
   da coluna — feio é melhor que ilegível. */
.peek h3 { font-family: var(--display); font-variation-settings: "opsz" 96, "SOFT" 50; font-weight: 500; font-size: 56px; line-height: 1.02; letter-spacing: -.02em; margin: 0; overflow-wrap: anywhere }
.peek .price { font-family: var(--display); font-variation-settings: "opsz" 96, "SOFT" 50; font-weight: 500; font-size: 32px; letter-spacing: -.02em; margin-top: -4px }
.peek .price small { font-family: var(--body); font-size: 13px; font-weight: 500; color: var(--mute) }
.peek .badges { display: flex; flex-wrap: wrap; gap: 6px }
.peek .nuthead { font-weight: 600; font-size: 12.5px; color: var(--mute); margin-bottom: 4px }
.peek .empty { padding: 30px 6px; font-size: 13px; line-height: 1.5 }
/* Rótulo nutricional no desenho clássico de embalagem (o "Nutrition Facts" do FDA): moldura grossa,
   título pesado, três espessuras de divisória (grossa 8px / média 4px / fio), valor colado no nome,
   %VD em coluna própria à direita e subitens indentados. Só a ESTRUTURA vem do modelo — cor é sempre
   do tema (--ink para barras e texto, --line para fios), e a língua é a nossa. Os cantos são retos de
   propósito: é a reprodução de um rótulo, não um cartão da interface. */
/* o respiro lateral é pedido do dono: colada nas bordas da coluna, a moldura parecia parte dela (8% pareceu exagero, ficou 3%) */
.nfacts { border: 2px solid var(--ink); padding: 8px 10px; margin-inline: 3%; font-size: 12.5px; line-height: 1.3; color: var(--ink) }
.nfacts .nfTitle { font-weight: 800; font-size: 25px; line-height: 1.02; letter-spacing: -.015em; margin-bottom: 5px }
.nfacts .nfServing { font-weight: 700; font-size: 13.5px; padding: 4px 0; border-top: 1px solid var(--line) }
.nfacts .nfBar { height: 8px; background: var(--ink); margin: 3px 0 }
.nfacts .nfBar.mid { height: 4px }
.nfacts .nfAmt { font-weight: 700; font-size: 10.5px; padding-top: 2px }
.nfacts .nfKcal { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; font-weight: 800; padding-bottom: 2px }
.nfacts .nfKcal span { font-size: 17px }
.nfacts .nfKcal b { font-size: 27px; line-height: 1; font-variant-numeric: tabular-nums; white-space: nowrap }
.nfacts .nfKcal b small { font-size: 13px }
.nfacts .nfVdHead { text-align: right; font-weight: 700; font-size: 10.5px; padding: 3px 0 }
.nfacts .nfRow { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; padding: 3.5px 0; border-top: 1px solid var(--line) }
/* linha logo depois de uma barra grossa não leva fio — no modelo a barra já é a divisória */
.nfacts .nfBar + .nfRow { border-top: 0 }
.nfacts .nfRow .n { min-width: 0; overflow-wrap: anywhere }
.nfacts .nfRow .n b { font-weight: 700 }
.nfacts .nfRow.sub { padding-left: 16px }
.nfacts .nfRow.sub2 { padding-left: 32px }
.nfacts .nfRow .vd { font-weight: 700; text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums }
.nfacts .nfFoot { font-size: 10.5px; line-height: 1.4; color: var(--ink); padding-top: 4px }
/* Comparação NOVA. Duas barras na MESMA escala de 0 a 100% e nunca normalizadas pela maior: a barra
   é o argumento, e esticar a menor até encostar na maior transformaria 0,3% em "quase igual". Barra
   curta é a notícia. O acento lima marca a linha da loja e a média nacional fica em cinza — não é
   verde-bom/vermelho-ruim, é "este é você / este é o país", que é o que o dado diz. */
.nova { display: flex; flex-direction: column; gap: 6px }
.nova .lin { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; font-size: 13px }
.nova .lin b { font-weight: 600; font-variant-numeric: tabular-nums }
.nova .lin.ref, .nova .lin.ref b { color: var(--mute); font-weight: 500 }
.nova .bar { height: 6px; border-radius: 3px; background: var(--s3); overflow: hidden }
.nova .bar > i { display: block; height: 100%; border-radius: 3px; background: var(--acc); min-width: 2px }
.nova .bar.ref > i { background: var(--faint) }
.nova .bar.zero > i { min-width: 0 }
aside.cart { display: flex; flex-direction: column; min-height: 0; background: var(--s1); border-radius: var(--r) }
.cust { display: flex; align-items: center; gap: 8px; padding: 12px 12px 8px }
.lines { flex: 1; overflow: auto; padding: 0 8px }
.line { display: flex; align-items: center; gap: 8px; padding: 12px 10px; border-radius: 14px; cursor: default; transition: background .15s }
.line.sel { border: var(--bw) solid transparent; background: linear-gradient(var(--s3), var(--s3)) padding-box, conic-gradient(from var(--spin), var(--l4) 0 55%, var(--acc1) 72%, var(--acc2) 84%, var(--l4) 96% 100%) border-box; animation: spin var(--spinDur) linear infinite }
.line:not(.sel) { border: var(--bw) solid transparent } .line.new { animation: pop .5s } @keyframes pop { from { background: var(--accSoft) } }
.line .t { flex: 1; min-width: 0 } .line .t b { display: block; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap } .line .t small { color: var(--mute) }
.qty { display: flex; align-items: center; background: var(--s3); border-radius: var(--pill) } .qty button { background: none; border: 0; padding: 5px 10px; cursor: pointer; color: var(--ink2) } .qty span { width: 26px; text-align: center; font-weight: 600 }
.sum { padding: 14px 16px 16px; background: var(--s2); border-radius: 0 0 var(--r) var(--r); display: flex; flex-direction: column; gap: 10px }
.tot { display: flex; justify-content: space-between; align-items: baseline } .tot b { font-family: var(--display); font-variation-settings: "opsz" 96, "SOFT" 50; font-weight: 500; font-size: 38px; letter-spacing: -.02em }
.modalscrim { position: fixed; inset: 0; background: var(--scrim); display: grid; place-items: center; padding: 16px; z-index: 50; animation: fade .2s } .modalscrim.solid { background: var(--s0) }
.modal { width: 100%; max-width: 480px; overflow: hidden; animation: rise .25s var(--ease); max-height: 92vh; display: flex; flex-direction: column; border-radius: 28px }
@keyframes rise { from { opacity: 0; transform: translateY(12px) scale(.985) } }
.modal header { padding: 22px 24px 8px; display: flex; align-items: center; gap: 8px } .modal .body { padding: 12px 24px 24px; overflow: auto }
.big { font-family: var(--display); font-variation-settings: "opsz" 96, "SOFT" 50; font-weight: 500; font-size: 40px; text-align: center; letter-spacing: -.02em }
.pay { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8px; margin-bottom: 14px } .pay button { padding: 14px 6px; cursor: pointer; font-weight: 600; font-size: 13px; border: 0; border-radius: 16px; background: var(--s3); color: var(--ink2); transition: background .12s } .pay button.on { background: var(--ink); color: var(--l0) }

/* ── login ── */
.box { width: min(400px, 92vw); padding: 34px; animation: rise .35s var(--ease); border-radius: 30px }
.box .brand { padding: 0 0 6px; font-size: 18px }
.dots { display: flex; gap: 12px; justify-content: center; margin: 22px 0 16px; height: 12px } .dots i { width: 12px; height: 12px; border-radius: 99px; background: var(--s3); transition: background .12s } .dots i.on { background: var(--ink) }
.pad { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px } .pad button { padding: 16px; border-radius: 16px; border: 0; background: var(--s2); font-family: var(--display); font-size: 22px; font-weight: 500; cursor: pointer; transition: background .1s } .pad button:hover { background: var(--s3) } .pad button:active { background: var(--s4) } .pad .soft { font-family: var(--body); font-size: 14px; color: var(--mute) }
.err { color: var(--berry); font-weight: 600; min-height: 20px; margin-top: 10px; text-align: center }

/* ── loja ── */
.shop { max-width: 1140px; margin: 0 auto; padding: 0 28px 100px }
.shop-head { display: flex; align-items: center; gap: 14px; padding: 22px 0; position: sticky; top: 0; z-index: 10; background: linear-gradient(var(--s0) 75%, transparent) }
.shop-head .brand { padding: 0; font-size: 18px } .shop-head .brand > i { width: 34px; height: 34px; border-radius: 12px }
.hero-shop { padding: 64px 0 34px } .hero-shop h1 { font-size: clamp(44px, 7vw, 84px); font-variation-settings: "opsz" 144, "SOFT" 80; font-weight: 400; letter-spacing: -.02em; line-height: .98; max-width: 720px } .hero-shop h1 em { font-style: italic; font-weight: 300 }
.hero-shop p { color: var(--ink2); font-size: 18px; max-width: 520px; margin: 22px 0 0; line-height: 1.5 }
.cats { display: flex; gap: 6px; overflow: auto; padding: 8px 0 20px }
.pgrid.shop-grid { padding: 0; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr)); gap: 12px; overflow: visible }
.ptile { padding: 20px; display: flex; flex-direction: column; gap: 8px; min-height: 200px; position: relative; overflow: hidden; border-radius: 24px; background: var(--s1); animation: reveal .5s var(--ease) both; animation-delay: calc(var(--i, 0) * 25ms); transition: background .15s }
.ptile:hover { background: var(--s2) }
.ptile .swatch { height: 78px; border-radius: 14px; background: radial-gradient(120% 100% at 20% 0%, var(--c1), var(--c2)); opacity: .8 }
.ptile .n { font-family: var(--display); font-variation-settings: "opsz" 36, "SOFT" 50; font-weight: 500; font-size: 18px; line-height: 1.15 } .ptile .p { margin-top: auto; display: flex; align-items: center; justify-content: space-between } .ptile .p b { font-size: 18px; font-weight: 600 }
.ptile .out { position: absolute; inset: 0; background: color-mix(in srgb, var(--s0) 78%, transparent); display: grid; place-items: center; color: var(--mute); font-weight: 600 }
.cartbar { position: fixed; bottom: 22px; left: 50%; transform: translateX(-50%); display: flex; align-items: center; gap: 16px; padding: 8px 8px 8px 22px; border-radius: var(--pill); z-index: 20; animation: slideup .3s var(--ease); background: var(--s2); box-shadow: var(--drop) }
.sheet { position: fixed; inset: 0; z-index: 30; display: flex; justify-content: flex-end; background: var(--scrim); animation: fade .2s }
.sheet .panel { width: 100%; max-width: 500px; height: 100%; background: var(--s1); border-radius: 28px 0 0 28px; display: flex; flex-direction: column; animation: slide .3s var(--ease) }
.steps { display: flex; gap: 6px; padding: 0 28px 8px } .steps i { flex: 1; height: 3px; border-radius: 99px; background: var(--s3) } .steps i.on { background: var(--ink) }
.check { width: 68px; height: 68px; border-radius: 99px; background: var(--leafSoft); display: grid; place-items: center; color: var(--leaf); margin: 0 auto 14px; font-size: 26px; animation: rise .4s var(--ease) }

/* ── painel: grade de 12 colunas com widgets pequenos ──
   Um cartão = uma pergunta respondida. Título em caixa alta pequena para não competir
   com o número; o número é que carrega a hierarquia.
   A grade não tem número fixo de colunas: `auto-fill` cria quantas couberem a partir de 300 px, então
   o mesmo painel mostra 2 cartões por linha num notebook e 6 num monitor grande, sem breakpoint novo.
   Larguras relativas: o normal ocupa 1 coluna, .w6/.w8 ocupam 2, .w12 a linha inteira. */
.dash { display: grid; grid-template-columns: repeat(auto-fill, minmax(var(--colmin), 1fr)); gap: var(--gap) }
.dash > * { grid-column: span 1 }
.dash > .w6, .dash > .w8 { grid-column: span 2 } .dash > .w12 { grid-column: 1 / -1 }
/* o cartão de hoje acompanha a altura da coluna de números ao lado; o gráfico desce até o pé */
.dash > .stat.hero { display: flex; flex-direction: column } .dash > .stat.hero > *:last-child { margin-top: auto }
.wid { padding: 18px 20px; display: grid; gap: 10px; align-content: start }
.wid > h3 { margin: 0; font-family: var(--body); font-size: 11px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase; color: var(--mute) }
.wid .n { font-family: var(--display); font-variation-settings: "opsz" 72, "SOFT" 50; font-weight: 500; font-size: 30px; letter-spacing: -.02em; line-height: 1 }
.sub { color: var(--mute); font-size: 12.5px; line-height: 1.45 }
.delta { font-size: 12px; font-weight: 600 } .delta.up { color: var(--leaf) } .delta.down { color: var(--berry) }
.donut { width: 118px; height: 118px; flex-shrink: 0 }
/* listinha genérica de widget: rótulo à esquerda, número à direita, separador quase invisível */
.lst { display: grid }
.lst > * { display: flex; align-items: center; justify-content: space-between; gap: 10px; padding: 7px 0; border-top: 1px solid var(--l2) }
.lst > *:first-child { border-top: 0; padding-top: 0 }
.lst .t { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
.meter { height: 3px; border-radius: 99px; background: var(--s3); overflow: hidden; margin-top: 6px }
.meter i { display: block; height: 100%; border-radius: 99px; animation: wipe .8s var(--ease) both } @keyframes wipe { from { transform: scaleX(0); transform-origin: left } }

/* ── faixa de alertas: o que precisa de alguém hoje, antes de qualquer número ── */
.alerts { display: grid; grid-template-columns: repeat(auto-fill, minmax(272px, 1fr)); gap: 8px; margin-bottom: 18px }
.al { display: flex; align-items: center; gap: 11px; padding: 11px 15px; border: 0; border-radius: 15px; background: var(--s1); text-align: left; font: inherit; color: inherit; transition: background .15s }
button.al { cursor: pointer } button.al:hover { background: var(--s3) }
.al > i { width: 8px; height: 8px; border-radius: 99px; flex-shrink: 0; background: var(--mute) }
.al.bad > i { background: var(--berry) } .al.warn > i { background: var(--honey) } .al.ok > i { background: var(--leaf) }
.al .t { flex: 1; min-width: 0 } .al .t b { display: block; font-weight: 600; font-size: 13px }
.al .t small { display: block; color: var(--mute); font-size: 12px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }

/* ── anel animado no hover: mesmo desenho do .glow, porém efêmero, em cartões e botões ──
   A borda transparente é permanente (com box-sizing: border-box não desloca nada); no hover ela
   ganha o gradiente cônico. --fill é a camada interna do BOTÃO e precisa bater com o fundo dele,
   senão o gradiente vaza por baixo; o cartão não usa --fill (ver o anel recortado abaixo).
   Vem depois das regras de .btn de propósito. */
.card, .tile, .box, .btn { border: var(--bw) solid transparent }
.btn { --fill: linear-gradient(var(--inkHi), var(--inkHi)) }
.btn.ghost { --fill: linear-gradient(var(--s4), var(--s4)) }
.btn.danger { --fill: linear-gradient(var(--berrySoft), var(--berrySoft)), linear-gradient(var(--s1), var(--s1)) }
.btn.ai, .btn.primary-grad { --fill: var(--ai) }
.btn.btn:hover:not(:disabled) {
  background: var(--fill) padding-box, conic-gradient(from var(--spin), var(--l4) 0 55%, var(--acc1) 72%, var(--acc2) 84%, var(--l4) 96% 100%) border-box;
  animation: spin var(--spinDur) linear infinite }
/* O cartão não usa o truque de duas camadas acima, e sim um anel recortado, porque a opacidade do
   cartão é regulável: o truque só esconde o cônico enquanto --fill é opaco, e com o cartão
   translúcido o gradiente aparecia inteiro por baixo do conteúdo — o cartão lavava de roxo no
   hover. Aqui o cônico é pintado num ::after e a máscara vaza só a moldura de --bw, então nada
   depende do fundo ser sólido. O `inset` negativo é porque o pseudo-elemento se posiciona contra a
   caixa de padding e precisa crescer para alcançar a borda.
   Como o anel não depende mais do preenchimento, o cartão não declara --fill nenhum: o hover só
   liga a opacidade do ::after e deixa a cor de fundo de cada elemento em paz (é assim que o
   `.tile:hover` consegue clarear e manter a padronagem ao mesmo tempo). */
:is(.card, .tile, .box):not(.glow)::after {
  content: ""; position: absolute; inset: calc(-1 * var(--bw)); border-radius: inherit; pointer-events: none;
  padding: var(--bw); opacity: 0; transition: opacity .2s var(--ease);
  background: conic-gradient(from var(--spin), var(--l4) 0 55%, var(--acc1) 72%, var(--acc2) 84%, var(--l4) 96% 100%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0) }
/* o :has() evita que o cartão de fora acenda junto quando o mouse está num filho que já acende */
:is(.card, .tile, .box):not(.glow):hover:not(:has(:is(.card, .tile, .box, .btn):hover))::after {
  opacity: 1; animation: spin var(--spinDur) linear infinite }

/* ── halo no hover: o mesmo brilho difuso do .glow, agora em todo cartão ──
   Antes só o anel da borda acendia; o brilho que escapa para fora era exclusividade do .glow
   permanente, então parecia que o efeito só existia em dois ou três lugares.
   ::before e não ::after porque o ::after do cartão já é o anel da moldura (regra acima).
   z-index:-1 põe o borrão atrás do cartão — só aparece o que transborda dos --haloSize, e aparece
   difuso, porque o desfoque cai inteiro para fora sem nenhum corte no caminho. Quem garante o
   "atrás" é o piso opaco de --under lá em cima (ver o comentário das camadas): sem ele o halo
   atravessava a face do cartão translúcido e a cunha do cônico, que é medida sobre a caixa do
   elemento, cobria meia tela num cartão grande. Recortar o borrão pela área do cartão também
   resolveria a mancha, mas o desfoque é aplicado ANTES da máscara: o corte cai no meio do
   degradê e o que sobra vira um contorno duro, não uma aura. Piso opaco preserva o cônico inteiro.
   O outro lado da mesma moeda está no `reveal` lá em cima: o piso só protege se o halo estiver mesmo
   atrás, e para isso o cartão não pode virar contexto de empilhamento (ver o comentário do .page).
   Consequência (inalterada): cartão dentro de cartão não mostra halo (o fundo do pai cobre), então
   o aninhado ganha um box-shadow equivalente logo abaixo. */
:is(.card, .tile, .box) { position: relative }
:is(.card, .tile, .box):not(.glow)::before {
  content: ""; position: absolute; inset: calc(-1 * var(--haloSize)); z-index: -1; border-radius: inherit; pointer-events: none;
  opacity: 0; filter: blur(var(--haloBlur)); transition: opacity .28s var(--ease);
  background: conic-gradient(from var(--spin), transparent 0 55%, var(--acc1) 72%, var(--acc2) 84%, transparent 96% 100%) }
:is(.card, .tile, .box):not(.glow):hover:not(:has(:is(.card, .tile, .box, .btn):hover))::before { opacity: var(--halo); animation: spin var(--spinDur) linear infinite }
/* ── peso e itálico dos títulos ──
   Uma regra só, e no fim do arquivo de propósito: cada bloco de título já declara `font-weight: 500`
   com a mesma especificidade desta lista, e quem empata perde para quem vem depois. Trocar as treze
   declarações uma a uma daria no mesmo e espalharia por 400 linhas um controle que é um só na tela
   de Aparência. Ficam de fora os títulos que NÃO estão em 500 — total do caixa (700), soma do
   orçamento (800), nome da marca (600): eles carregam ênfase própria, e igualar tudo aqui apagaria
   justamente a hierarquia que o dono não pediu para mexer. */
h1, h2, .stat .v, .stat .b, .calc b, .tile .p, .peek h3, .peek .price, .tot b, .big, .pad button, .ptile .n, .wid .n {
  font-weight: var(--dispWeight); font-style: var(--dispStyle) }
/* aninhado: sem pseudo-elemento, porque ele ficaria escondido atrás do cartão de fora */
:is(.card, .modal, .drawer) :is(.card, .tile, .box):not(.glow):hover:not(:has(:is(.card, .tile, .box, .btn):hover)) {
  box-shadow: 0 0 var(--haloBlur) -4px color-mix(in srgb, var(--acc1) calc(var(--halo) * 60%), transparent) }

@media print { body * { visibility: hidden } .receipt, .receipt * { visibility: visible } .receipt { position: fixed; inset: 0; width: 80mm; margin: auto; font-family: monospace; color: #000; background: var(--inkHi) } }
@media (prefers-reduced-motion: reduce) { *, *::before, *::after { animation: none !important; transition: none !important } .chart .line { stroke-dashoffset: 0 } .chart .area { opacity: 1 } }
/* o antigo breakpoint de 1240 px sumiu: com auto-fill a contagem de colunas se resolve sozinha.
   Sobra só o piso — abaixo de ~640 px cabe uma coluna só, e aí `span 2` transbordaria a tela. */
@media (max-width: 640px) { .dash > *, .dash > .w6, .dash > .w8 { grid-column: 1 / -1 } }
/* a ficha do produto é conforto, não ferramenta: em tela estreita ela roubaria espaço da grade e da
   venda, que são o que o caixa precisa para trabalhar. Some antes de apertar as outras duas. */
@media (max-width: 1100px) { .pos { grid-template-columns: 1fr minmax(340px, 420px) } .peek { display: none } }
@media (max-width: 820px) {
  .where, .lnk { display: none } .lscrim { padding: 28px 16px; gap: 20px }
  /* dois cartões por linha no celular: menos que isso vira lista, mais que isso volta a ser o ícone
     miúdo que o dono reclamou. Aqui a largura é proporcional e não fixa — os 212 px do desktop
     deixariam um cartão só por linha numa tela de 375 px, com folga sobrando dos dois lados. */
  .lpad { gap: 10px } .tile.appicon { flex-basis: calc(50% - 5px); min-height: 110px; font-size: 14px }
  .g3 { grid-template-columns: 1fr } .pos { grid-template-columns: 1fr } aside.cart { position: fixed; inset: auto 12px 12px 12px; max-height: 52vh }
  .assistant-dock { right: 10px; bottom: 10px } .assistant-panel { width: calc(100vw - 20px); height: calc(100dvh - 82px); border-radius: 20px } .assistant-messages { padding-inline: 14px } .assistant-compose-hint { display: none }
  .assistant-settings-card { padding: 18px } .agent-permission { grid-template-columns: minmax(0, 1fr) auto }
}

/* ── tela de Aparência ──
   O campo de token é texto + paleta nativa lado a lado, com um controle deslizante embaixo quando
   o valor é uma medida. O texto é a fonte da verdade: a paleta do sistema só entende #rrggbb, e
   quem precisa de rgba() ou color-mix() digita. */
.tk .tkrow { display: flex; gap: 8px; align-items: center }
.tk .tkrow .field { flex: 1; min-width: 0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px }
.tk span i { font-style: normal; font-weight: 400 }
input.tsw { width: 38px; height: 38px; flex: none; padding: 0; border: 0; border-radius: var(--r2); background: var(--s3); cursor: pointer }
input.tsw::-webkit-color-swatch-wrapper { padding: 4px } input.tsw::-webkit-color-swatch { border: 0; border-radius: 9px }
input.trg { width: 100%; margin: 8px 0 0; accent-color: var(--acc1); cursor: pointer }
/* prévia de gradiente e de sombra: mesma pastilha do seletor de cor, mas só para olhar — não existe
   paleta do sistema para "linear-gradient(…)", então quem manda é o texto ao lado. */
.tprev { width: 38px; height: 38px; flex: none; border-radius: var(--r2); background: var(--s3); box-shadow: inset 0 0 0 4px var(--l1) }
.tile.preset { min-height: 0; gap: 5px; padding: 12px }
.tile.preset .swat { height: 26px; border-radius: 9px; display: block }
/* Prévia do logo. O <i> de dentro repete a placa da barra do topo: é ele que some no "sem fundo" e é
   a caixa dele que a porcentagem estica, então a moldura precisa de folga para o logo a 200% caber. */
.logoprev { width: 124px; height: 124px; border-radius: var(--r2); background: var(--s2); display: grid; place-items: center; color: var(--ink) }
.logoprev > i { width: 50px; height: 50px; border-radius: 16px; background: var(--ai); display: grid; place-items: center; color: var(--onAcc) }
.logoprev > i svg { width: 28px; height: 28px }
/* Prévia da padronagem. O ladrilho entra por `style` inline (é o mesmo que o theme.js gera para a
   tela), então aqui só mora a moldura. Cor de fundo é a camada base do painel para a opacidade do
   desenho ser lida com o mesmo contraste que vai ter de verdade. */
.patprev { height: 132px; border-radius: var(--r2); background-color: var(--s2); border: 1px solid var(--l3) }
/* "×" de apagar tema salvo: botão dentro de botão não existe, então ele é irmão do bloco e só aparece
   quando o ponteiro está por perto — a ação normal é aplicar o tema, apagar é a exceção. */
.mypreset { position: relative } .mypreset > .tile { width: 100% }
.tilex { position: absolute; top: 5px; right: 5px; width: 22px; height: 22px; border: 0; border-radius: 8px; background: var(--s3); color: var(--mute); font: inherit; font-size: 15px; line-height: 1; cursor: pointer; opacity: 0; transition: opacity .15s, background .15s, color .15s }
.mypreset:hover .tilex, .galit:hover .tilex, .tilex:focus-visible { opacity: 1 } .tilex:hover { background: var(--berrySoft); color: var(--berry) }

/* ── galeria de desenhos ──
   Carrossel e não grade: a galeria fica DENTRO do editor de logo e do de padronagem, que já são
   cartões cheios. Uma grade cresceria para baixo e empurraria os controles para fora da tela toda
   vez que o dono guardasse um desenho; a faixa que rola de lado tem altura fixa, então o cartão
   não muda de tamanho entre um desenho e quarenta.
   `scroll-snap` porque o passo do carrossel é uma miniatura, não um punhado de pixels: as setas
   rolam e o navegador encaixa. A barra de rolagem some (o dedo e as setas bastam) mas a rolagem
   continua existindo — é ela que faz o carrossel funcionar em tela de toque sem código nenhum. */
.galwrap { display: grid; grid-template-columns: auto 1fr auto; gap: 6px; align-items: center }
.gal { display: flex; gap: 8px; overflow-x: auto; scroll-snap-type: x mandatory; scrollbar-width: none; padding: 2px }
.gal::-webkit-scrollbar { display: none }
.galit { position: relative; flex: 0 0 76px; scroll-snap-align: start }
.galit > button.use { width: 76px; height: 76px; padding: 6px; border: var(--bw) solid transparent; border-radius: var(--r2); background: var(--s2); color: var(--ink); cursor: pointer; display: grid; place-items: center; transition: background .15s }
.galit > button.use:hover { background: var(--s3) } .galit > button.use svg { max-width: 100%; max-height: 100%; width: auto; height: auto }
.galit.on > button.use { border-color: var(--acc1) }
.galit .cap { display: block; margin-top: 3px; font-size: 10.5px; line-height: 1.2; color: var(--mute); text-align: center; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
.galnav { width: 28px; height: 28px; flex: none; border: 0; border-radius: 9px; background: var(--s3); color: var(--ink2); font: inherit; cursor: pointer; display: grid; place-items: center }
.galnav:hover { background: var(--s4) } .galnav:disabled { opacity: .35; cursor: default }
/* Campo colado no botão que age — o nome do desenho no logo, nas duas padronagens, na galeria e nos
   temas; e também a categoria nova, o número da nota de entrada e o cupom do cliente, que antes eram
   `prompt()`. O campo encolhe (`min-width: 0`) e o botão não (`flex: none`): quem some primeiro numa
   gaveta estreita tem de ser a largura do campo, nunca o rótulo do botão, que é onde está escrito o
   que o clique vai fazer. Abaixo de 420 px viram duas linhas, o botão ocupando tudo. */
.fieldrow { display: flex; gap: 8px; align-items: center; flex-wrap: wrap }
.fieldrow > .field { flex: 1 1 150px; min-width: 0 }
.fieldrow > .btn { flex: none }
@media (max-width: 420px) { .fieldrow > .field { flex-basis: 100% } .fieldrow > .btn { width: 100%; justify-content: center } }
/* "voltar ao padrão" de um token só. Nasce apagado porque a resposta certa quase sempre é não mexer;
   acende junto com o campo, como o "×" do tema salvo. */
.tkdef { border: 0; background: none; color: var(--faint); font: inherit; font-size: 11.5px; cursor: pointer; padding: 0 2px; opacity: 0; transition: opacity .15s, color .15s }
.tk:hover .tkdef, .tkdef:focus-visible { opacity: 1 } .tkdef:hover { color: var(--ink2) }

/* ── lançador de assuntos da Aparência ──
   Um cartão pequeno por assunto, na mesma grade do lançador de aplicativos: a página inteira cabe
   numa tela e o dono escolhe o que quer mexer antes de ver campo nenhum. `auto-fill` e não uma
   contagem fixa de colunas — a página tem quinze cartões e a barra lateral do painel muda de
   largura entre o notebook e o monitor da loja. */
.tsecs { display: grid; grid-template-columns: repeat(auto-fill, minmax(238px, 1fr)); gap: 12px }
/* `backwards` e não `both`, pelo mesmo motivo do lançador: com `both` o cartão fica preso na
   transformação final da animação e o afundar do clique nunca acontece. */
.tile.tsec { text-align: left; color: var(--ink); font: inherit; min-height: 0; padding: 15px; gap: 12px; animation: pop .3s var(--ease) backwards; animation-delay: calc(var(--n) * 12ms) }
.tshead { display: flex; gap: 11px; align-items: flex-start }
.tshead > i { width: 34px; height: 34px; flex: none; border-radius: 11px; background: var(--s3); color: var(--ink); display: grid; place-items: center; transition: background .16s, color .16s }
.tile.tsec:hover .tshead > i { background: var(--ai); color: var(--onAcc) }
.tshead b { display: block; font-size: 14.5px; font-weight: 650; line-height: 1.25 }
.tshead span { display: block; margin-top: 2px; font-size: 11.5px; line-height: 1.35 }
/* A tira de prévia. Quase todas são feitas só de `var(--…)`: como salvar não é o que repinta a tela
   (o `T.apply()` reescreve o `:root` a cada tecla), a tira acompanha a digitação sem uma linha de
   JavaScript. Só as que dependem de dado — logo, temas guardados, galeria, modo aceso — têm função
   de redesenho do outro lado. `margin-top:auto` cola a tira no rodapé para os cartões de descrição
   curta e longa terminarem alinhados. */
.tsprev { height: 46px; margin-top: auto; padding: 8px; border-radius: 12px; background: var(--s3); box-shadow: inset 0 0 0 1px var(--l4); display: flex; align-items: center; gap: 6px; overflow: hidden }
.tslayers i { flex: 1; align-self: stretch; border-radius: 7px }
.tsink b { font-family: var(--display); font-size: 19px; line-height: 1 }
.tsbar { flex: 1; align-self: stretch; border-radius: 8px }
.tsdots i { width: 18px; height: 18px; border-radius: 99px }
.tsshape i { width: 30px; height: 30px; flex: none; border: var(--bw) solid var(--acc1); background: var(--s4) }
.tstype { flex-direction: column; align-items: flex-start; justify-content: center; gap: 1px }
.tstype b { font-family: var(--display); font-weight: var(--dispWeight); font-style: var(--dispStyle); font-size: 17px; line-height: 1.1 }
.tstype span { font-family: var(--body); font-weight: var(--bodyWeight); font-style: var(--bodyStyle); font-size: 11px; line-height: 1.1; color: var(--mute) }
/* A tira de estado das Configurações. Diferente das tiras da Aparência, esta não mostra a cor: ela
   responde de olho batido "como está esse assunto agora?" — 10% sem aprovação, Token guardado,
   Homologação. Duas linhas dentro dos mesmos 46px, então tudo aqui é apertado de propósito e o
   texto corta com reticências: o nome da loja e a razão social não têm tamanho previsível, e sem
   o corte a tira estouraria o cartão. */
.tsstat { flex-direction: column; align-items: flex-start; justify-content: center; gap: 3px }
.tsstat > * { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
.tsstat b { font-family: var(--display); font-size: 17px; line-height: 1.1 }
.tsstat > .m { font-size: 11.5px; line-height: 1.1 }
.tsstat .chip { padding: 2px 9px; font-size: 11px }
/* o mesmo ladrilho e a mesma animação do fundo do site, em miniatura: é a única prévia honesta de
   um desenho que anda sozinho. As `@keyframes` do fundo movem duas camadas porque o `body` tem
   duas; aqui só existe uma e o valor sobrando é ignorado sem estrago. */
.tsmove { background-image: var(--patSite); background-size: var(--patSiteSize); background-repeat: var(--patSiteRepeat); animation: var(--bgAnim) var(--bgDur) linear infinite var(--bgDir) }
.tsglow i { width: 26px; height: 26px; border-radius: 9px; background: var(--ai); box-shadow: 0 0 var(--haloBlur) color-mix(in srgb, var(--acc1) calc(var(--halo) * 90%), transparent) }
.tspat-site { background-image: var(--patSite); background-size: var(--patSiteSize); background-repeat: var(--patSiteRepeat) }
.tspat-card { background-image: var(--patCard); background-size: var(--patCardSize); background-repeat: var(--patCardRepeat) }
.tsbrand > i { width: 30px; height: 30px; flex: none; border-radius: 10px; background: var(--ai); color: var(--onAcc); display: grid; place-items: center; overflow: hidden }
.tsbrand b { font-family: var(--display); font-size: 15px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
.tsgal i svg, .tsbrand > i svg { width: 26px; height: 26px }
.tsgal b { font-size: 12px; color: var(--mute); font-weight: 600 }
.tsthemes b { font-size: 12px; color: var(--ink2); font-weight: 600; white-space: nowrap }
.tspill { display: inline-flex; align-items: center; gap: 5px; padding: 5px 10px; border-radius: var(--pill); background: var(--s3); color: var(--mute); font-size: 11.5px; font-weight: 600 }
.tspill.on { background: var(--ai); color: var(--onAcc) }
.tspill svg { width: 13px; height: 13px }
/* Única prévia do lançador que **não** usa as cores da tela: o `doc.html` não carrega o `ui.css`
   porque papel é branco mesmo com a loja no tema escuro. Mostrar o orçamento no tom do painel
   prometeria um documento preto. Só o acento é herdado, que é o que o documento herda de verdade. */
.tsdoc { background: #fff; flex-direction: column; align-items: stretch; justify-content: center; gap: 4px; padding: 9px 12px }
.tsdoc i { height: 4px; border-radius: 2px; background: #d9dcd5 }
.tsdoc i:first-child { width: 38%; height: 6px; background: var(--acc1) }
.tsdoc i:last-child { width: 62% }

/* A gaveta da Aparência é a mesma do cadastro de produto, com uma diferença de propósito: o
   escurecido de trás fica quase transparente. Em toda outra tela o escuro serve para tirar a
   página de cena; aqui a página atrás É a prévia, e cobri-la enquanto se escolhe a cor do cartão
   seria pedir para o dono editar às cegas.
   Não chamar de `.peek`: esse nome já é a ficha do produto no PDV, que é `flex-direction: column`
   com padding — o escurecido herdava as duas coisas e a gaveta ia parar no canto esquerdo. */
.scrim.thru { background: color-mix(in srgb, var(--l0) 18%, transparent) }
.drawer header p { max-width: 62ch }

/* A busca não filtra a página, leva até o campo: abre a gaveta certa e pisca a linha. Sem o piscar,
   "abriu a gaveta de Forma" não responde quem digitou "borda" — são seis campos parecidos lá. */
.tfindwrap { max-width: 560px }
.tfind { display: none; position: absolute; left: 0; right: 0; top: calc(100% + 6px); z-index: 6; max-height: 340px; overflow: auto; padding: 5px; border-radius: var(--r2); background: var(--s1); box-shadow: var(--drop) }
.tfind.on { display: grid; gap: 2px }
/* `.search svg` posiciona a lupa em absoluto, e a regra alcança **todo** svg dentro da busca — os
   ícones das linhas do resultado saíam do fluxo e se empilhavam em cima da lupa. */
.tfind svg { position: static; flex: none; width: 15px; height: 15px; color: var(--mute) }
/* `justify-content` explícito: a regra geral de `button` centraliza o conteúdo, e resultado de
   busca centrado obriga a ler cada linha inteira para comparar duas. */
.tfrow { display: flex; align-items: center; justify-content: flex-start; gap: 9px; width: 100%; padding: 8px 10px; border: 0; border-radius: 10px; background: none; color: var(--ink); font: inherit; font-size: 13px; text-align: left; cursor: pointer }
.tfrow:hover, .tfrow:focus-visible { background: var(--s3) }
/* `.m` e não `span`: o ícone da linha também vem embrulhado num span, e o `margin-left:auto` caía
   nos dois — o primeiro empurrava a linha inteira para o meio. */
.tfrow b { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap }
.tfrow > .m { margin-left: auto; font-size: 11.5px; white-space: nowrap }
/* Realce por `box-shadow` e não por `padding` + fundo: a linha é um `<label>` no meio da coluna da
   gaveta, e ganhar padding por um segundo empurraria todas as outras para baixo — o campo que a
   busca acabou de trazer para o centro sairia de lugar justamente enquanto pisca. */
.flash { animation: flash 1.2s var(--ease); border-radius: 10px }
@keyframes flash { 0%, 100% { box-shadow: 0 0 0 9px transparent } 22% { box-shadow: 0 0 0 9px var(--accSoft) } }

/* Barra de salvar. A tela já mostra o tema novo enquanto se mexe, então sem um aviso explícito o
   dono fecha a página convencido de ter gravado. Ela só existe quando há mexida pendente — botão
   de salvar sempre visível e sempre inerte ensina a ignorar botão de salvar.
   `.page > *` anima tudo que entra na página; aqui isso brigaria com a subida da própria barra. */
.savebar { position: fixed; left: 50%; bottom: 22px; z-index: 30; display: flex; align-items: center; gap: 10px; padding: 9px 9px 9px 18px; border-radius: var(--pill); background: var(--s1); box-shadow: var(--drop); font-size: 13px; transform: translate(-50%, 90px); opacity: 0; pointer-events: none; transition: transform .28s var(--ease), opacity .28s var(--ease) }
.savebar.on { transform: translate(-50%, 0); opacity: 1; pointer-events: auto }
/* Some enquanto a gaveta está aberta: ali o rodapé já tem "Salvar aparência", e a barra, que é
   centrada na tela, ficava metade escondida atrás da gaveta. Vem depois de `.on` de propósito —
   as duas têm a mesma especificidade e quem decide o empate é a ordem do arquivo. */
.savebar.hid { transform: translate(-50%, 90px); opacity: 0; pointer-events: none }
.page > .savebar { animation: none }
/* No celular a frase inteira não cabe ao lado dos dois botões e a barra transbordaria a tela. O
   aviso vira a própria presença da barra — some o texto, ficam "Descartar" e "Salvar". */
@media (max-width: 560px) { .savebar { left: 12px; right: 12px; transform: translate(0, 90px) } .savebar.on { transform: translate(0, 0) } .savebar > span { display: none } .savebar .btn { flex: 1; justify-content: center } }

/* ── orçamentos e faturas ──
   O editor de documento é largo porque cada linha tem produto, quantidade, preço e desconto: no
   `.drawer` padrão de 480px os campos ficariam do tamanho de um selo. */
.drawer.wide { max-width: 760px }
.qline { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; padding: 10px; border-radius: var(--r2); background: var(--s2); margin-bottom: 8px }
.qline .grow { flex: 1 1 220px; min-width: 0 }
.qline .num { width: 78px; flex: none; text-align: right }
.qline .sum { margin-left: auto; font-weight: 700; font-family: var(--display); white-space: nowrap }
.qline .lbl { font-size: 11.5px; color: var(--mute) }
.totbox { display: grid; gap: 4px; padding: 16px; border-radius: var(--r2); background: var(--s2) }
.totbox div { display: flex; justify-content: space-between; font-size: 13px; color: var(--ink2) }
.totbox div.big { font-size: 22px; font-weight: 800; font-family: var(--display); color: var(--ink); margin-top: 6px }
