/* =============================================================================
   CAMADA DE MARCA — instalação SANTINI DECOR
   -----------------------------------------------------------------------------
   Identidade definida pelo usuário em 02/10/2026:

     logo ........... santinidecor.com.br/wp-content/uploads/2020/12/
                      santini-decoracoes.png
     fundo .......... #fcfdfd
     texto .......... #000000
     corpo .......... Nunito Sans 17px
     títulos ........ Torus, tamanho inalterado
     detalhes ....... #005ea9 · #2e88c5 · #00355f

   Carregado POR ÚLTIMO (depois de components.css) — ver a explicação no
   marca.css da instalação MsX. Nenhuma regra desta identidade entra em
   tokens.css, base.css ou components.css: esses três são do produto e rodam
   iguais nas duas instalações.

   54 pares de contraste medidos, 0 reprovações. As medições estão ao lado de
   cada cor, no mesmo padrão do tokens.css original.

   DUAS DECISÕES TOMADAS AQUI, com a medição que as motivou:

   1. #2e88c5 NÃO é cor de texto nem fundo de botão com texto branco.
      Sobre #fcfdfd dá 3,79:1 e com branco por cima dá 3,86:1 — reprova nos dois
      casos (mínimo 4,5:1 para texto normal). Passa folgado como BORDA, FOCO e
      DETALHE, onde o mínimo é 3:1, e é lá que ele fica. É a cor do traço do
      logo; ela continua visível no sistema, no lugar onde funciona.

   2. A faixa de marca do login usa o gradiente #00355f → #005ea9, não um que
      termine em #2e88c5. O texto branco por cima precisa de 4,5:1 no PIOR ponto
      do gradiente: com #005ea9 dá 6,62:1, com #2e88c5 daria 3,86:1 e a frase do
      login (18px semibold) não é "texto grande" pela definição da WCAG — o
      limiar de 3:1 não valeria para ela.
   ============================================================================= */

/* -----------------------------------------------------------------------------
   1. TIPOGRAFIA
   Auto-hospedada. A CSP do .htaccess é `font-src 'self'`: fonte vinda do
   Google Fonts seria bloqueada pelo navegador, sem erro visível na tela.
   -------------------------------------------------------------------------- */

/* Nunito Sans variável: 400, 600 e 700 saem do MESMO arquivo (eixo wght).
   Um download de 49 KB cobre os três pesos. Subset latin, igual ao recorte que
   o projeto já usa para a Open Sans. SIL Open Font License 1.1. */
@font-face {
  font-family: 'Nunito Sans';
  src: url('../fonts/nunitosans-latin.woff2') format('woff2');
  font-weight: 400 700;
  font-stretch: 100%;
  font-style: normal;
  font-display: swap;
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* -----------------------------------------------------------------------------
   2. TOKENS INDEPENDENTES DE TEMA
   -------------------------------------------------------------------------- */

:root {

  /* ---- 2.1 Famílias -------------------------------------------------------
     Torus continua no título, como pedido. Open Sans sai da pilha de interface
     e Nunito Sans entra; fica como segundo recurso da Torus, para o caso de a
     fonte de display não carregar. */
  --font-ui: 'Nunito Sans', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-display: 'Torus', 'Nunito Sans', system-ui, sans-serif;
  --font-data: var(--font-ui);

  /* ---- 2.2 Escala tipográfica --------------------------------------------
     Decisão do usuário em 02/10/2026: 17px no que se LÊ (campo, valor, célula,
     botão, corpo); rótulo e cabeçalho de coluna sobem pouco, para a grade não
     perder densidade; título de tela fica no tamanho atual.

     Base 13 → 17px é +31%. Sem subir a linha da grade junto, o texto encosta na
     borda da célula — por isso §2.3 existe. */
  --text-2xs: 11px;   /* era 10 · badge, carimbo de auditoria */
  --text-xs: 13px;    /* era 11 · rótulo de campo, cabeçalho de coluna */
  --text-sm: 15px;    /* era 12 · texto auxiliar, célula secundária */
  --text-md: 17px;    /* era 13 · BASE — pedido do usuário */
  --text-lg: 19px;    /* era 15 · subtítulo de seção, total de rodapé */
  --text-xl: 21px;    /* era 18 · título de card, valor em destaque */
  /* --text-2xl (22px) e --text-3xl (28px) NÃO mudam: são os títulos em Torus,
     e o pedido foi "mesmo tamanho atual". */

  /* ---- 2.3 Densidade ------------------------------------------------------
     Corpo de 17px com entrelinha 1,5 ocupa 25,5px. Numa linha de 34px sobravam
     4,25px em cima e embaixo — menos que o padding lateral da célula, e o texto
     ficava colado na gridline.

     `--row-cozy-src` é lido por MSX.shell.aplicarDensidade() em app.js;
     `--row-cozy` é o que os componentes consomem e o que o JS escreve. Os dois
     precisam nascer iguais, senão a primeira pintura usa um valor e a segunda
     usa outro. */
  --row-compact: 32px;       /* era 28 */
  --row-cozy: 38px;          /* era 34 */
  --row-cozy-src: 38px;
  --row-comfortable: 54px;   /* era 50 */
  --control-height-sm: 28px; /* era 24 */
  --control-height-md: 36px; /* era 30 — input e botão */
  --control-height-lg: 42px; /* era 36 */

  /* ---- 2.4 Marca ----------------------------------------------------------
     O gradiente da MsX (magenta → roxo) sai inteiro. O da Santini sai do azul
     escuro para o azul médio do logo: o ponto mais claro ainda dá 6,62:1 com
     branco, então QUALQUER tamanho de texto passa por cima dele. */
  --brand-deep: #00355f;      /* 12,31:1 sobre o fundo */
  --brand-primary: #005ea9;   /*  6,49:1 */
  --brand-light: #2e88c5;     /*  3,79:1 — borda e detalhe, nunca texto */
  --brand-gradient: linear-gradient(135deg, #00355f, #005ea9);
  --brand-gradient-vertical: linear-gradient(#00355f, #005ea9);
  --brand-gradient-50deg: linear-gradient(50deg, #00355f, #005ea9);

  /* Herdados de tokens.css e mantidos vivos porque base.css e components.css
     ainda os consomem. Apontados para o azul, senão sobra magenta em pontos
     soltos do sistema. */
  --brand-magenta: #005ea9;
  --brand-purple: #00355f;
  --brand-lavender: #dbe8f3;

  /* O logo é horizontal e largo (297×116). Os 15px de raio da MsX vinham do
     site dela; aqui o cartão de marca fica menos arredondado. */
  --radius-brand: 10px;
}

/* -----------------------------------------------------------------------------
   3. TEMA CLARO — a paleta que o usuário definiu
   -----------------------------------------------------------------------------
   Fundo #fcfdfd com texto #000000, como pedido em 02/10/2026.

   **Não é mais o tema de abertura.** Depois de ver os dois no ar, o usuário
   escolheu abrir no escuro (§4). Esta paleta é o que o botão de tema entrega, e
   continua sendo a que vale na impressão — papel é branco.

   O bloco se repete em quatro seletores DE PROPÓSITO. Não é descuido:

     :root                            vence o `:root` escuro de tokens.css
     :root[data-theme="light"]        quando o usuário escolhe claro
     :root:not([data-theme]) em @media  precisa existir porque o mesmo seletor
                                      em tokens.css tem especificidade (0,2,0),
                                      maior que `:root` (0,1,0) — sem este
                                      bloco, um usuário com Windows em tema
                                      claro veria a paleta ROXA da MsX.
   -------------------------------------------------------------------------- */

:root,
:root[data-theme="light"] {
  color-scheme: light;

  /* Superfícies */
  --surface-sunken: #f1f5f8;     /* fundo recuado */
  --surface-base: #fcfdfd;       /* [CLIENTE] fundo primário */
  --surface-raised: #ffffff;     /* card, painel */
  --surface-overlay: #ffffff;    /* modal, dropdown */
  --surface-inset: #e8eef4;
  --surface-hover: #e8eef4;
  --surface-selected: #dbe8f3;
  --surface-zebra: #f5f8fa;      /* linha par da grade */

  /* Texto — medido sobre #fcfdfd */
  --text-primary: #000000;       /* [CLIENTE] 20,61:1 */
  --text-secondary: #444a52;     /*            8,78:1 */
  --text-tertiary: #5c646e;      /*            5,88:1 */
  --text-disabled: #9aa2ab;      /* desabilitado é isento de 1.4.3 */
  --text-on-accent: #ffffff;
  --text-inverse: #fcfdfd;

  /* Bordas — subtle e default são decorativas (gridline, divisor) e a WCAG
     1.4.11 não exige 3:1 delas. A interativa é contorno de input e exige. */
  --border-subtle: #dde6ee;
  --border-default: #c6d4e0;
  --border-interactive: #78879a;  /* 3,60:1 sobre o fundo */
  --border-strong: #5c646e;       /* 5,88:1 */

  /* Acento */
  --accent: #005ea9;             /* [CLIENTE]  6,49:1 */
  --accent-hover: #00355f;       /* [CLIENTE] 12,31:1 */
  --accent-muted: #2e88c5;       /* [CLIENTE] decorativo */
  --accent-surface: #e8f1f9;     /* acento por cima: 5,79:1 */
  --accent-border: #2e88c5;

  /* Ação primária */
  --action-primary: #005ea9;          /* branco  6,62:1 */
  --action-primary-hover: #004b87;    /* branco  8,91:1 */
  --action-primary-pressed: #00355f;  /* branco 12,54:1 */

  /* Semânticas — texto. Escurecidas em relação às da MsX porque o fundo aqui é
     mais claro e as originais passavam raspando. */
  --success: #0b6b3f;            /* 6,46:1 */
  --warning: #9a5a00;            /* 5,37:1 */
  --danger: #b3261e;             /* 6,41:1 */
  --info: #005ea9;               /* 6,49:1 — o info é o azul da marca */

  /* Semânticas — superfície e borda */
  --success-surface: #e9f7ef;    /* texto sucesso por cima: 5,96:1 */
  --warning-surface: #fdf4e3;    /*                         5,01:1 */
  --danger-surface: #fdeceb;     /*                         5,72:1 */
  --info-surface: #e8f1f9;       /*                         5,79:1 */
  --success-border: #0b6b3f;
  --warning-border: #9a5a00;
  --danger-border: #b3261e;
  --info-border: #2e88c5;

  /* Foco — 6,49:1, bem acima dos 3:1 de WCAG 2.4.11 */
  --focus-ring: #005ea9;
  --focus-ring-offset: var(--surface-base);

  /* Elevação — sombra tingida de azul escuro, não de cinza neutro */
  --shadow-sm: 0 1px 2px 0 rgb(0 53 95 / 0.07);
  --shadow-md: 0 4px 8px -2px rgb(0 53 95 / 0.11);
  --shadow-lg: 0 12px 24px -6px rgb(0 53 95 / 0.15);
  --shadow-overlay: 0 24px 48px -12px rgb(0 53 95 / 0.20);
}

@media (prefers-color-scheme: light) {
  :root:not([data-theme]) {
    color-scheme: light;
    --surface-sunken: #f1f5f8;
    --surface-base: #fcfdfd;
    --surface-raised: #ffffff;
    --surface-overlay: #ffffff;
    --surface-inset: #e8eef4;
    --surface-hover: #e8eef4;
    --surface-selected: #dbe8f3;
    --surface-zebra: #f5f8fa;
    --text-primary: #000000;
    --text-secondary: #444a52;
    --text-tertiary: #5c646e;
    --text-disabled: #9aa2ab;
    --text-on-accent: #ffffff;
    --text-inverse: #fcfdfd;
    --border-subtle: #dde6ee;
    --border-default: #c6d4e0;
    --border-interactive: #78879a;
    --border-strong: #5c646e;
    --accent: #005ea9;
    --accent-hover: #00355f;
    --accent-muted: #2e88c5;
    --accent-surface: #e8f1f9;
    --accent-border: #2e88c5;
    --action-primary: #005ea9;
    --action-primary-hover: #004b87;
    --action-primary-pressed: #00355f;
    --success: #0b6b3f;
    --warning: #9a5a00;
    --danger: #b3261e;
    --info: #005ea9;
    --success-surface: #e9f7ef;
    --warning-surface: #fdf4e3;
    --danger-surface: #fdeceb;
    --info-surface: #e8f1f9;
    --success-border: #0b6b3f;
    --warning-border: #9a5a00;
    --danger-border: #b3261e;
    --info-border: #2e88c5;
    --focus-ring: #005ea9;
    --focus-ring-offset: var(--surface-base);
    --shadow-sm: 0 1px 2px 0 rgb(0 53 95 / 0.07);
    --shadow-md: 0 4px 8px -2px rgb(0 53 95 / 0.11);
    --shadow-lg: 0 12px 24px -6px rgb(0 53 95 / 0.15);
    --shadow-overlay: 0 24px 48px -12px rgb(0 53 95 / 0.20);
  }
}

/* -----------------------------------------------------------------------------
   4. TEMA ESCURO — PADRÃO DA SANTINI (decisão de 02/10/2026)
   -----------------------------------------------------------------------------
   É esta a tela que abre. Azul-noite derivado do mesmo azul da marca, sem
   nenhum resquício do roxo da MsX.

   Os azuis do cliente não sobrevivem inteiros aqui, e isso é medição, não
   gosto: #005ea9 sobre o fundo escuro dá 1,9:1 e some. O acento sobe para
   #6cb4e4 (7,55:1). O logo, que é traço quase preto, vai em negativo (§5).
   -------------------------------------------------------------------------- */

/* `.login` entra na lista DE PROPÓSITO, e é o que faz a tela de entrada ser
   sempre escura — decisão do usuário em 02/10/2026.

   Funciona porque variável CSS é herdada: declarada direto em `.login`, ela
   vence a que `:root` passou para baixo, dentro daquela subárvore e só ali. O
   app inteiro continua obedecendo ao tema escolhido; a porta de entrada, não.

   Consequência assumida: quem escolheu tema claro vê login escuro e app claro.
   É o padrão de tela de marca. Para fazer o login seguir o tema, basta remover
   `.login` desta lista e da regra do `html:has()` abaixo. */
:root[data-theme="dark"],
.login {
  color-scheme: dark;

  --surface-sunken: #0b1620;
  --surface-base: #0f1d2a;
  --surface-raised: #152635;
  --surface-overlay: #1b2f40;
  --surface-inset: #1b2f40;
  --surface-hover: #1a2c3c;
  --surface-selected: #16314a;
  --surface-zebra: #122232;

  --text-primary: #eaf2f8;       /* 15,09:1 */
  --text-secondary: #b3c3d1;     /*  9,47:1 */
  --text-tertiary: #93a5b5;      /*  6,74:1 */
  --text-disabled: #5f7488;
  --text-on-accent: #ffffff;
  --text-inverse: #0f1d2a;

  --border-subtle: #1b2c3b;
  --border-default: #2c4055;
  --border-interactive: #6b8095;  /* 4,19:1 */
  --border-strong: #8398ab;

  --accent: #6cb4e4;             /* 7,55:1 — o #005ea9 puro dá 1,9:1 no escuro
                                    e some; clareado até passar */
  --accent-hover: #93cbef;       /* 9,78:1 */
  --accent-muted: #2e88c5;
  --accent-surface: #16314a;
  --accent-border: #2e88c5;

  --action-primary: #1a6fb8;          /* branco 5,24:1 */
  --action-primary-hover: #2177c0;    /* branco 4,70:1 — mais claro que a base,
                                         que é o que faz um hover LER como hover
                                         no tema escuro, e ainda assim passa */
  --action-primary-pressed: #155c9b;

  --success: #3ddc97;            /* 9,66:1 */
  --warning: #f5b944;            /* 9,68:1 */
  --danger: #f87a72;             /* 6,52:1 */
  --info: #6cb4e4;               /* 7,55:1 */

  --success-surface: #0b2a21;    /* 8,69:1 */
  --warning-surface: #2b2410;    /* 8,74:1 */
  --danger-surface: #2e1719;     /* 6,40:1 */
  --info-surface: #10263c;       /* 6,81:1 */
  --success-border: #3ddc97;
  --warning-border: #f5b944;
  --danger-border: #f87a72;
  --info-border: #2e88c5;

  --focus-ring: #6cb4e4;         /* 7,55:1 */
  --focus-ring-offset: var(--surface-base);

  --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.40);
  --shadow-md: 0 4px 8px -2px rgb(0 0 0 / 0.50);
  --shadow-lg: 0 12px 24px -6px rgb(0 0 0 / 0.60);
  --shadow-overlay: 0 24px 48px -12px rgb(0 0 0 / 0.70);
}

/* -----------------------------------------------------------------------------
   5. SUPERFÍCIES DE MARCA — o que o logo da Santini exige
   -----------------------------------------------------------------------------
   base.css fixa fundo ESCURO em três pontos, com um comentário explicando por
   quê: o wordmark da MsX é cinza claro com o X em gradiente magenta, desenhado
   para fundo escuro, e sobre branco o "Ms" desaparecia.

   O logo da Santini é o caso oposto — traço quase preto (#001111) sobre
   transparente. Nos mesmos três pontos escuros ele vira um borrão. As três
   regras abaixo desfazem a faixa escura; nenhuma delas muda base.css, que
   continua igual para a MsX.
   -------------------------------------------------------------------------- */

/* (1) Faixa da marca na sidebar */
.sidebar__brand {
  background: var(--surface-base);
  border-bottom: 1px solid var(--border-subtle);
}
/* base.css fixa #b3a8bd aqui, que era legível na faixa escura e dá 1,9:1 sobre
   o fundo claro. Especificidade (0,2,0) nos dois lados: este arquivo ganha por
   vir depois. */
.sidebar__brand .sidebar__brand-text {
  color: var(--text-secondary);   /* 8,78:1 */
}
/* O logo é largo (297×116 ≈ 2,6:1). Nos 20px de altura que a MsX usava, o
   "decor" em manuscrito fica ilegível. 26px é o mínimo em que ele se lê. */
.sidebar__logo { height: 26px; }
.sidebar__brand { height: 56px; }

/* A tela de login é sempre escura (§4), e ela ocupa a viewport inteira. Só o
   fundo da própria página fica de fora e aparece no "overscroll" — aquele
   respiro que surge ao arrastar a página além do fim. Literal, e não token,
   porque aqui estamos FORA da subárvore `.login` e o token ainda vale claro. */
html:has(.login) { background: #0b1620; }

/* (2) Pastilha escura atrás do logo no painel de login — não existe mais.
   O logo é traço quase preto e o painel agora é sempre escuro: inverte sempre,
   sem depender do tema escolhido. */
:root[data-theme="light"] .login__logo,
.login__logo {
  padding: 0;
  background: none;

  /* `logo-branco.png` tem 297x84. Entrou em 100% pela manha de 03/10/2026 e no
     mesmo dia o usuario pediu **70%**: 297 x 0,70 = 207,9px.

     O decimal e proposital - 208px seria 70,03% e a altura sairia num quebrado
     diferente. UMA dimensao fixa e a outra em `auto` mantem a proporcao; nunca
     ha dois eixos forcados ao mesmo tempo, que foi a causa do achatamento de
     14% medido em B-S1.21.

     Cabe com folga grande: `.login__panel` tem `max-width: 380px` e
     `padding: var(--space-8)` (32px cada lado), logo 316px de caixa de conteudo.
     O `max-width: 100%` so entra em painel mais estreito, e ali ele ENCOLHE
     junto, nunca estica.

     O `filter` sai: este arquivo JA e branco. A inversao por CSS era aproximacao
     - achatava o azul do simbolo junto com o preto do texto.

     Fim do dia 03/10/2026: +20px (207,9 -> 227,9) e, na rodada seguinte, -10px.
     Fecha em 217,9px = 73,4% do arquivo. Continua bem dentro dos 316px de caixa
     de conteudo do painel.

     O respiro ate o campo de usuario tambem caiu 10px: era `var(--space-8)`
     (32px) e agora e 22px, literal, porque nao ha token de 22. */
  width: 217.9px;
  height: auto;
  max-width: 100%;
  filter: none;
  margin-bottom: 22px;
}

/* Redeclarar a COR aqui não é redundância — é o que faz a tela escura funcionar.
   `body` declara `color: var(--text-primary)` e isso COMPUTA para #000000 com os
   tokens claros. Herança passa adiante a cor já computada, não a variável: todo
   texto do login que só herda (título, rótulo do switch) chegava preto sobre
   fundo escuro, ilegível. Declarado aqui, o `var()` resolve DENTRO da subárvore,
   onde o token já é claro. */
.login { color: var(--text-primary); }

/* (3) Faixa lateral do login — ver §7, que a reescreve inteira */
.login__brand-logo {
  height: 48px;
  /* O logo é escuro e vai sobre o gradiente azul escuro. Invertido para branco:
     é o mesmo desenho, em negativo, sem recolorir a marca de forma arbitrária.
     `brightness(0)` achata tudo para preto, `invert(1)` leva a branco puro. */
  filter: brightness(0) invert(1);
}

/* Na sidebar a inversão depende do tema, porque lá a faixa segue a superfície
   do app — clara no tema claro, escura no escuro. */
:root[data-theme="dark"] .sidebar__logo {
  filter: brightness(0) invert(1);
}

/* O quadrado de carregamento do index.html NÃO precisa de regra aqui: ele usa
   `background: var(--brand-gradient)` num <style> inline, e o token já foi
   trocado em §2.4. Uma regra para ele neste arquivo seria morta — o <style>
   inline vem depois do <link> e ganharia com a mesma especificidade. */

/* -----------------------------------------------------------------------------
   6. AJUSTES QUE A ESCALA DE 17px PEDE
   -------------------------------------------------------------------------- */

/* Torus tem face única e desenho largo. No título de tela (28px) ela convive
   bem com corpo de 13px; com corpo de 17px a diferença encolheu e o título
   perdeu hierarquia. O espacejamento compensa sem mexer no tamanho, que o
   usuário pediu para manter. */
.screen__title,
.login__title { letter-spacing: 0.03em; }

/* Cabeçalho de coluna a 13px em caixa alta com tracking de 0,06em fica largo
   demais e quebra em duas linhas nas grades mais apertadas. */
.col-header,
.table thead th { letter-spacing: 0.03em; }

/* -----------------------------------------------------------------------------
   7. TELA DE LOGIN — planeta girando
   -----------------------------------------------------------------------------
   Pedido do usuario em 02/10/2026: "igual dash.cloudflare.com/login, o planeta
   rodando, mas nas cores da Santini, e o espaco do form do outro lado".

   A referencia foi aberta e medida. O que ela faz, e o que esta reproduzido
   aqui com codigo proprio:

     formulario a ESQUERDA, faixa de marca a DIREITA (invertido em relacao ao
       que estava), sangrando ate as bordas, sem canto arredondado;
     o planeta e um <canvas> grande, CORTADO pela borda direita;
     hachura diagonal tom sobre tom, nao pontilhado;
     rotacao lenta, ~2 min por volta.

   O globo em si esta em js/globo.js. Aqui so o lugar dele.

   CONTRASTE - o detalhe que quase passou
   Hachura branca a 20% sobre #005ea9 derruba o texto branco para 4,34:1, abaixo
   do minimo de 4,5. Por isso DUAS decisoes, nao uma:
     1. o gradiente vai do ESCURO (lado do texto) para o azul da marca (lado do
        globo) - o texto nunca cai sobre a parte clara;
     2. um veu por cima do canvas garante o pior caso. Medido: mesmo com o fundo
        mais claro possivel somado a hachura cheia, o veu a 60% entrega 7,62:1.
   -------------------------------------------------------------------------- */

@media (min-width: 900px) {
  /* Sangria total: o painel da marca precisa encostar nas quatro bordas.
     `place-items: center` do base.css centraliza e NAO estica - por isso o
     justify-self explicito, senao sobra respiro dos dois lados. */
  .login {
    padding: 0;
    grid-template-columns: 1.08fr 1fr;
  }
  .login__panel  { order: 1; margin: var(--space-6); }
  .login__brand  { order: 2; justify-self: stretch; }
}

.login__brand {
  border-radius: 0;
  padding: var(--space-12) var(--space-10);
  background: linear-gradient(115deg, #04172a 0%, #072a48 48%, #0a3c66 100%);
}

/* A fita de ondas ocupa o painel inteiro. Não tem tamanho próprio em rem nem
   razão de aspecto: ela É o painel, e por isso acompanha qualquer resolução sem
   regra extra.

   Foi exatamente o que faltou na versão anterior, do planeta: aquela tinha teto
   fixo de 48rem, virava um detalhe perdido num monitor de 3440px, e a correção
   que escrevi para isso quebrou os continentes em telas grandes. Aqui o problema
   não existe porque não há tamanho a escolher. */
.login__fundo {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 0;
}

/* O VÉU FOI REMOVIDO em 02/10/2026, e isso é uma decisão medida, não um
   descuido.

   Ele existia para proteger o texto da hachura do planeta, que chegava a 32% de
   branco. O plexus é outra coisa: linha a 0,26 e ponto a 0,45, de um azul claro,
   não de branco. Medido o pior caso — um ponto inteiro bem embaixo de uma letra:

     ponto 0,45 sobre #04172a (início do gradiente) ...... 5,5:1   ok
     ponto 0,45 sobre #072a48 (onde o texto termina) ..... 4,9:1   ok
     ponto 0,45 sobre #0a3c66 (fim do gradiente) ......... 4,3:1   reprova

   O caso que reprova fica na parte CLARA do gradiente, passados 60% do painel.
   O bloco de texto tem `max-width: 28ch` e acaba antes dos 50% — e quanto maior
   a tela, mais cedo ele acaba em proporção. Nunca chega lá.

   Se algum dia o texto crescer ou a largura máxima mudar, esta conta cai junto:
   é o tipo de coisa que volta a precisar de véu. */

.login__brand-conteudo {
  position: relative;
  z-index: 2;
  max-width: 37ch;   /* o titulo pede 302px; o logo a 144px de altura pede 369px - manda o maior */
}

/* HALO DO TEXTO — o que substitui o véu de painel inteiro.
   -----------------------------------------------------------------------------
   O véu antigo escurecia metade do painel e apagava o efeito justamente onde ele
   devia aparecer. Este halo é uma elipse macia colada ao bloco de texto: protege
   a leitura e deixa o resto da área azul livre.

   POR QUE ELE PRECISA EXISTIR, com número:
   as linhas do plexus somam luz. Uma linha sozinha a 0,42 sobre o painel dá
   5,47:1 e passa. Mas duas linhas cruzando dão alfa efetivo 0,66 (3,24:1) e,
   com um ponto por cima, 0,83 — **2,35:1, ilegível**.

   Medir elemento isolado não bastava, e foi o que eu fiz na rodada anterior.
   O que vale é o empilhamento.

   A elipse estoura o bloco de texto em 3,5rem na vertical e 4–5rem na horizontal
   de propósito: assim a palavra mais externa ainda cai no núcleo de 0,80, que
   sobre o pior caso entrega mais de 10:1. A faixa de 0,52 fica fora do texto. */
/* O HALO FOI REMOVIDO em 02/10/2026, junto com a mudança do plexus para uma
   malha concentrada nas bordas.

   Ele existia para compensar as linhas que passavam por trás do texto. Agora não
   passa nenhuma: a zona povoada começa depois de 54% da largura, e o bloco de
   texto acaba antes dos 47%. O problema deixou de existir em vez de ser
   compensado — e é sempre o arranjo melhor.

   A DEPENDÊNCIA, que precisa ficar visível para quem mexer nos dizeres depois:
   se o texto crescer além de ~50% da largura do painel, ou se `zonaDireita` em
   js/plexus.js diminuir, as linhas voltam a cair atrás das letras. Aí o halo
   precisa voltar — ou a zona precisa recuar. Medido: duas linhas cruzando mais
   um ponto somam luz e derrubam o branco para 2,35:1. */

/* Sem o canvas (instalação sem módulo de fundo) nada acima tem efeito e a faixa
   continua a de antes.

   `align-content` era `center` e virou `stretch` em 03/10/2026, quando o logo
   iUNIX foi ancorado no rodapé do painel: para pendurar algo embaixo, a linha do
   grid tem de ocupar a altura toda em vez de encolher no conteúdo. A
   centralização não se perdeu — ela desceu um nível, para o
   `justify-content: center` do `.login__brand-conteudo`, que agora é um flex em
   coluna.

   Este seletor tem `:has()`, logo especificidade (0,2,0). Uma regra posterior
   escrita como `.login__brand { ... }` é (0,1,0) e **não** ganha dele, por mais
   abaixo que esteja no arquivo. Foi exatamente o que aconteceu na primeira
   tentativa: o logo ficou no meio do painel, em cima do crédito. Mexer aqui é o
   caminho certo. */
.login__brand:has(.login__fundo) { display: grid; align-content: stretch; }

/* -----------------------------------------------------------------------------
   8. CAIXA DO LOGIN - enxugada
   -----------------------------------------------------------------------------
   Pedido do usuario em 02/10/2026: logo maior, e fora o titulo "ERP Santini
   Decor" e a linha "Entre com seu usuario e senha.".

   O titulo some da TELA mas continua no documento. Ele e o unico <h1> da
   pagina: apagar de vez deixaria quem navega por leitor de tela sem saber em
   que pagina esta, e isso nao foi o que o usuario pediu - ele pediu que nao
   APARECESSE. A tecnica abaixo e a de "visualmente oculto": sai do fluxo e do
   desenho, continua sendo lido e anunciado.

   A dica, essa sai inteira: ela repetia o que os rotulos Usuario e Senha ja
   dizem, logo abaixo.
   -------------------------------------------------------------------------- */

.login__title {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  white-space: nowrap;
  clip-path: inset(50%);
  border: 0;
}

.login__hint { display: none; }

/* Sem titulo nem dica, o formulario encostava no logo. */
.login__form { margin-top: 0; }
/* -----------------------------------------------------------------------------
   9. FAIXA DE MARCA - logo maior e titulo em versalete inclinado
   -----------------------------------------------------------------------------
   Pedido do usuario em 02/10/2026: "aumente o logo da santini decor", "deixe o
   texto GESTAO SEM ATRITO em caixa alta italico" e "diminuir o heavy da font".

   O TERCEIRO PEDIDO CORRIGE UM DEFEITO QUE JA EXISTIA.
   O titulo vinha em `--weight-bold` (700), e a Torus **tem face unica, peso
   400**. O proprio tokens.css avisa, na secao 6: "nunca pedir bold dela, o
   navegador sintetiza e o traco engorda de forma irregular". Era exatamente o
   que estava acontecendo - o "heavy" que incomodou era falso negrito.

   Em 400 o desenho volta a ser o da fonte.

   SOBRE O ITALICO: a Torus tambem nao tem face inclinada, entao o navegador
   sintetiza de novo - desta vez uma inclinacao, que e uma deformacao bem mais
   benigna que o engrossamento. Fica declarado aqui para quem for mexer: se um
   dia entrar uma Torus Italic de verdade, esta regra passa a usa-la sozinha.

   SOBRE O ESPACAMENTO: comecei em 0,07em, que e o que caixa alta costuma
   pedir. O usuario achou largo e pediu a frase numa linha so - e as duas coisas
   se resolvem juntas, porque o tracking era justamente o que estourava a
   largura do bloco e quebrava GESTAO SEM ATRITO em duas linhas. Em 0,015em
   cabe, e a caixa alta continua legivel porque a Torus ja tem desenho aberto.
   -------------------------------------------------------------------------- */


.login__brand-title {
  font-weight: var(--weight-regular);
  font-style: italic;
  text-transform: uppercase;
  letter-spacing: 0.015em;
}
/* -----------------------------------------------------------------------------
   10. FAIXA DE MARCA - logo branco oficial, titulo subido e credito da agencia
   -----------------------------------------------------------------------------
   Pedido do usuario em 02/10/2026.
   -------------------------------------------------------------------------- */

/* O LOGO NUNCA PASSA DO TAMANHO DO ARQUIVO.
   Regra que o cliente fixou em 02/10/2026 para `logo-branco.png`: "deixar ele no
   tamanho 100% da imagem... e nao aumentar mais que isso... pois esta
   distorcendo a mesma". Vale para qualquer arquivo que entre aqui.

   Desde 03/10/2026 a faixa leva a marca UNIX, nao mais a Santini. O arquivo foi
   substituido no mesmo dia (236x71 -> `iUNIX`, 336x87) e agora vai a **70% do
   tamanho natural**, por pedido do usuario: 336 x 0,70 = 235,2px.

   O decimal e proposital. 235px seria 69,9% e a altura cairia num valor quebrado
   diferente; 235,2 e o 70% exato, e o navegador resolve o subpixel. O numero
   acompanha o ARQUIVO - trocar a imagem de novo exige recalcular, senao a marca
   de alguem sai esticada.

   `width` fixa na largura natural e `height: auto` fazem a altura sair da
   proporcao - nunca ha dois eixos forcados ao mesmo tempo, que era a causa do
   achatamento de 14% medido em B-S1.21. `max-width: 100%` cuida do painel
   estreito: ali ele ENCOLHE proporcionalmente, nunca estica.

   Sem `filter`: o arquivo tem cor propria (magenta #f51384 no nucleo do traco) e
   qualquer filtro aqui recoloriria a marca. */
.login__brand-logo {
  width: 200.2px;        /* 235,2 -> -15 -> -10 -> -10, 03/10/2026 = 59,6% do arquivo */
  height: auto;
  max-width: 100%;
  filter: none;

  /* Voltou ao FLUXO. Na rodada anterior ele era `position: absolute` para
     chegar no rodape sozinho; agora o credito tem de ficar ABAIXO dele, e
     grupo que anda junto se resolve no fluxo, nao com dois absolutos que
     precisariam ser realinhados a cada mudanca de tamanho.

     Quem leva o grupo ao rodape e o `justify-content: flex-end` do
     `.login__brand-conteudo`. */
  margin: 0;
}

/* O logo saiu do fluxo, entao o bloco precisa ocupar a altura toda do painel
   para ter de onde pendurar o rodape. `justify-content: center` preserva o que
   ja existia: titulo e credito centralizados na vertical.

   Quem manda `stretch` na linha do grid e a regra `:has()` da secao 7 - uma
   regra `.login__brand {}` aqui seria (0,1,0) e nao ganharia dela.

   `flex-end` (era `center`) leva o grupo inteiro - logo iUNIX, "Powered By:" e
   marca da MsX - para o rodape, na ordem em que o app.js os cria. Com
   "GESTAO SEM ATRITO" fora, e so isso que resta na faixa.

   O grupo para no PADDING do painel: 48px da borda de baixo. Os 30px pedidos na
   rodada anterior valiam para o iUNIX quando ele era o elemento mais baixo;
   agora quem fecha o bloco e a marca da MsX, e o respiro natural do painel e o
   que sobra.

   Tentei `margin-bottom: -18px` aqui para forcar 30px e NAO funcionou: com
   `height: 100%` o item tem altura propria, entao a margem negativa nao estica
   coisa nenhuma - ela so deslocaria um irmao seguinte, que nao existe. Medido:
   48px com e sem a declaracao. Removida para nao ficar codigo morto com
   comentario mentindo ao lado. Se os 30px forem mesmo o alvo, o caminho e
   `padding-bottom: 30px` no proprio `.login__brand`. */
.login__brand-conteudo {
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

/* "remover gestao sem atrito", 03/10/2026. Mesmo caminho do subtitulo: por CSS,
   porque `marca()` resolve com `||` e esvaziar `loginTitulo` no config faria a
   faixa cair no titulo PADRAO da MsX. `display: none` tambem tira o no da
   arvore de acessibilidade. O texto segue no config, pronto para voltar. */
.login__brand-title { display: none; }

/* FRASE REMOVIDA a pedido do usuario em 03/10/2026 ("Cadastro, Comercial e
   Financeiro / tudo em um so lugar...").

   Por CSS, e nao apagando `loginTexto` do app-config.js, por um motivo concreto:
   `marca()` resolve com `m[chave] || padrao`, e string vazia e falsy - deixar a
   chave vazia faria a faixa cair no texto PADRAO da MsX, que e pior que o
   problema. `display: none` tambem tira o no da arvore de acessibilidade, entao
   nao sobra texto fantasma para leitor de tela. A frase segue no config, pronta
   para voltar com uma linha. */
.login__brand-text { display: none; }

/* As duas linhas do subtitulo vem do `\n` em app-config.js, nao da largura da
   caixa. `pre-line` respeita a quebra escrita e continua colapsando espacos.

   Peso e entrelinha pedidos pelo usuario em 02/10/2026. base.css traz o
   subtitulo em semibold 600 - desenho pensado para UMA linha curta. Com duas
   linhas o semibold vira uma mancha escura que disputa atencao com o titulo,
   que e quem deveria liderar. Em 400 o subtitulo volta ao papel de apoio.

   O 1.2 nao e so estetico: `--leading-normal` (1.5) abre as duas linhas o
   suficiente para elas pararem de se ler como uma frase so, que e exatamente o
   que elas sao - "Cadastro, Comercial e Financeiro / tudo em um so lugar...".
   Em 1.2 as linhas formam bloco. */
.login__brand-text {
  white-space: pre-line;
  font-weight: var(--weight-regular);
  line-height: 1.2;
}

/* -----------------------------------------------------------------------------
   CREDITO DA AGENCIA
   Discreto de proposito: e assinatura de autoria, nao chamada. Fica abaixo do
   bloco, menor que o subtitulo, e o logo entra no tamanho que a altura mandar.
   -------------------------------------------------------------------------- */
.login__creditos {
  display: flex;
  /* Empilhado por pedido do usuario em 02/10/2026: o rotulo em cima, a marca
     embaixo. O `align-items` precisou trocar junto, nao e detalhe: em `column`
     ele passa a governar o eixo HORIZONTAL, e o `center` que estava aqui
     centralizaria rotulo e logo entre si - o bloco de credito sairia do
     alinhamento a esquerda de todo o resto da faixa (logo, titulo, subtitulo).
     `flex-start` mantem a margem unica. */
  flex-direction: column;
  align-items: flex-start;

  /* Distancia entre o rotulo e a marca, afinada em tres rodadas do mesmo dia:
     15 -> 10 -> 5px. Numero unico - substituiu o par `gap: 8px` +
     `margin-top: -10px` de antes, que dava -2px: dois valores brigando para
     expressar uma distancia so. */
  gap: 5px;

  /* Respiro ate o logo iUNIX: era `var(--space-10)` (40px), foi a 20 e agora a
     10px.

     Isto e o que DESCE o iUNIX, e vale entender por que: o grupo inteiro esta
     ancorado no rodape por `justify-content: flex-end`. A base nao se move,
     entao encurtar o respiro acima do credito empurra o logo para baixo pelo
     mesmo tanto. Mexer no logo direto nunca foi necessario. */
  margin-top: 10px;
}

/* Nunito Sans 17px, pedido do usuario em 03/10/2026 - e exatamente o corpo de
   texto da identidade da Santini ("FONTE DE ESCRITA: NUNITO SANS 17px").
   `--font-ui` ja e Nunito Sans nesta instalacao (secao 3); declarado aqui
   explicitamente porque este texto e o unico da faixa que nao herda do corpo.

   O `letter-spacing` aberto sai: fazia sentido em 13px, onde abrir a letra
   ajuda a ler; em 17px so afrouxa a palavra. */
.login__creditos-texto {
  font-family: var(--font-ui);
  font-size: 16px;      /* era 17 - "diminuir 1px", 03/10/2026 */
  color: var(--text-tertiary);
  white-space: nowrap;
}

/* O logo agora e link para msxdesign.com.br. `display: block` tira o espaco
   fantasma que o inline reserva embaixo da imagem (a linha de base), que no
   empilhamento apareceria como um desalinhamento de 4px sem causa visivel.
   `inline-flex` manteria a area clicavel colada ao desenho. */
.login__creditos-link {
  display: inline-flex;
  border-radius: var(--radius-sm);
}

.login__creditos-link:hover .login__creditos-logo,
.login__creditos-link:focus-visible .login__creditos-logo {
  opacity: 1;
}

/* O foco e desenhado aqui porque o padrao do navegador sobre fundo azul escuro
   fica quase invisivel. Branco com afastamento: visivel sobre o gradiente e
   sobre os pulsos do fundo animado. */
.login__creditos-link:focus-visible {
  outline: 2px solid #ffffff;
  outline-offset: 3px;
}

/* +20px e 10px para cima, pedido do usuario em 03/10/2026.

   Passou a ser governado pela LARGURA e nao mais pela altura: "aumentar em 20px"
   so e verificavel se houver um numero fixo onde somar, e a largura e o eixo que
   o olho compara com o "Powered By:" ao lado. Vinha renderizando 61,6x30 (altura
   fixa em 30); agora 81,6px de largura, altura 39,7 pela proporcao de 2,053 do
   arquivo (271x132, recorte justo, medido).

   O `margin-top: -10px` saiu em 03/10/2026: a distancia ate o rotulo passou a
   ser declarada uma vez so, no `gap: 15px` do `.login__creditos`. */
.login__creditos-logo {
  width: 81.6px;
  height: auto;
  max-width: 100%;
  opacity: 0.9;
  display: block;
  transition: opacity var(--duration-fast) var(--ease-out);
}



/* =============================================================================
   11 · FOTO DE FUNDO DA FAIXA AZUL  (teste - 03/10/2026)
   =============================================================================
   Pedido do usuario, em duas rodadas no mesmo dia:
     1a - foto de fundo na parte azul, a 15%, com o homem centralizado em
          qualquer tamanho de tela ("lembrando das versoes de monitores maiores")
     2a - "focar nessa parte da imagem" (recorte da figura, nao do predio),
          "colocar do lado ESQUERDO", "deixar em 5 alpha" e "suavizar o degrade
          do final da foto pro azul"

   CAMADA. A faixa tem tres niveis e este entra no meio:
     z 0 .. canvas do fundo animado (grade + pulsos)
     z 1 .. ESTA foto
     z 2 .. logo, titulo, subtitulo, credito
   Entrar em z 1 e o que faz a foto cobrir a grade sem nunca cobrir o texto.

   ARQUIVO. O original (`Materiais/img-background.jpg`) tem 4912x7360 e 17,4 MB -
   seria o maior arquivo do site por duas ordens de grandeza, baixado em toda
   abertura do login, para aparecer a 5% de opacidade. Recortado na figura
   (3680x5078 do original, proporcao 0,725, a mesma da referencia enviada) e
   reduzido para 1000x1380 / 100 KB: 179x menor. A 5% nenhum detalhe fino
   sobrevive, entao resolucao maior seria peso jogado fora.

   ENQUADRAMENTO. `cover` corta em eixos diferentes conforme a proporcao da
   faixa: em painel baixo e largo o corte e horizontal; em painel alto e estreito
   e vertical. Por isso a posicao vai declarada nos DOIS eixos. Com o recorte
   fechado, o homem ocupa o centro do arquivo (corpo entre 18% e 85% da largura),
   entao 50% horizontal o mantem centralizado em qualquer largura - que foi o
   pedido. O 45% vertical privilegia tronco e rosto sobre as pernas.
   ========================================================================== */

.login__brand::before {
  content: '';
  position: absolute;
  top: 0;
  left: 0;               /* mudou de `right` na 2a rodada */
  bottom: 0;
  width: 62%;
  z-index: 1;

  background-image: url('../img/fundo-login.jpg');
  background-repeat: no-repeat;
  background-size: cover;
  /* `calc(50% - 60px)`: os 60px para a esquerda pedidos pelo usuario, aceitando
     que o braco da pessoa saia cortado.

     RESSALVA QUE IMPORTA EM MONITOR LARGO. `cover` so tem folga horizontal para
     deslocar quando a tira e mais ESTREITA que a proporcao do arquivo (0,725).
     Em 1920 a tira da 0,628 e os 60px andam. Em 3440x1440 a tira daria 0,769:
     ali o `cover` escala pela largura, nao sobra folga lateral, e o
     deslocamento nao tem para onde ir. Se o enquadramento precisar valer igual
     la, o conserto e baixar `width` de 62% para ~56%, que devolve a folga. */
  background-position: calc(50% - 60px) 45%;

  /* 0,05 -> 0,10 por pedido do usuario na mesma rodada. */
  opacity: 0.10;

  /* O "degrade do final da foto pro azul". Cinco paradas em vez de duas: um
     `linear-gradient` de dois pontos tem derivada constante, e o olho enxerga o
     ponto em que ele comeca e termina como duas linhas tenues. Com as paradas
     intermediarias abaixo da reta (0,92 / 0,55 / 0,18 onde a reta daria 0,72 /
     0,5 / 0,25) a queda vira curva e nao ha onde o olho se agarrar.

     A foto fica inteira ate 20% e so entao comeca a sumir - assim a figura
     aparece resolvida e o que se dissolve e o fundo dela. */
  -webkit-mask-image: linear-gradient(to right,
      #000 0%, #000 20%,
      rgba(0,0,0,0.92) 38%,
      rgba(0,0,0,0.55) 60%,
      rgba(0,0,0,0.18) 82%,
      transparent 100%);
          mask-image: linear-gradient(to right,
      #000 0%, #000 20%,
      rgba(0,0,0,0.92) 38%,
      rgba(0,0,0,0.55) 60%,
      rgba(0,0,0,0.18) 82%,
      transparent 100%);

  /* Decorativo: nunca intercepta clique nem foco. */
  pointer-events: none;
}

/* =============================================================================
   12 · CAIXA DE LOGIN - rodada de 03/10/2026
   ============================================================================= */

/* A caixa chegou a ter `min-height: 60vh` com os itens centralizados na
   vertical, pedido de 03/10/2026. REVERTIDO no mesmo dia ("deixar como estava
   mesmo"): a caixa volta a ter a altura do proprio conteudo, que e o
   comportamento de base.css. Nada a declarar aqui - a ausencia e a regra. */

/* Logo da Santini centralizado na caixa, 03/10/2026.
   `display: block` e obrigatorio junto: `margin: auto` nao centraliza elemento
   inline, e <img> e inline por padrao. Com `block` + largura fixa (217,9px) as
   margens automaticas dividem a sobra dos dois lados. */
.login__logo {
  display: block;
  margin-left: auto;
  margin-right: auto;
}

/* TODAS AS FONTES EM NUNITO SANS dentro da tela de login.

   `--font-display` (Torus) continua existindo no resto do sistema; aqui ele e
   REDECLARADO para a propria Nunito. Fazer assim, e nao sair trocando
   `font-family` regra a regra, cobre tambem o que ainda nem foi escrito: titulo
   de tela, numero, qualquer componente que caia na faixa do login depois.

   `--font-data` acompanha porque ja era `var(--font-ui)`. */
.login {
  --font-display: 'Nunito Sans', system-ui, sans-serif;
}

/* "Entrar" em caixa alta. O `span` e quem carrega a palavra - o botao tambem
   tem o <svg> do icone, e `text-transform` nele seria inofensivo mas impreciso.
   `letter-spacing` leve porque caixa alta sem tracking fecha demais em
   Nunito Sans. */
.login__form .btn--primary span {
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* BORDA TRACEJADA nos campos, so no login.

   `border-style` isolado em vez de `border` inteiro: a cor e a espessura
   continuam vindo de components.css, entao tema claro e escuro seguem
   funcionando sem duplicar valor aqui. O `:focus` mantem o tracejado para a
   mudanca nao parecer defeito quando o campo recebe foco. */
.login__form .input,
.login__form .input:hover,
.login__form .input:focus,
.login__form .input:focus-visible {
  border-style: dashed;
}

/* Rotulos "usuario" e "senha" em minuscula e italico.

   `text-transform: lowercase` e visual: o texto no DOM continua "Usuário" e
   "Senha", com maiuscula, entao leitor de tela e autofill do navegador leem o
   rotulo correto. Trocar a string no app.js mudaria isso para as duas
   instalacoes e pioraria a leitura. */
.login__form .field__label {
  text-transform: lowercase;
  font-style: italic;
}

/* =============================================================================
   13 · MENSAGEM DE ERRO DO LOGIN  (03/10/2026)
   =============================================================================
   Tres pedidos que sao uma decisao so - o erro deixa de gritar e passa a ser
   nota de rodape:

     icone menor ........ 15 -> 12px
     texto .............. minuscula e italico
     ponto final ........ removido (em js/app-config.js, `loginErroSemPonto`)

   O ponto nao da para tirar por CSS: nao existe propriedade que apague um
   caractere do conteudo. Por isso essa parte vive no config, e nao aqui.

   Escopo em `.login__form` de proposito: `.field__error` e componente do
   produto e aparece em toda tela com formulario. Mudar o desenho dele em
   cadastro, orcamento e venda nao foi pedido.
   ========================================================================== */

/* 12px contra os 13px do texto: o icone passa a acompanhar a altura da letra
   em vez de disputar com ela. `flex: none` impede que o flex o comprima quando
   a mensagem for longa - sem isso um erro de duas linhas achata o triangulo. */
.login__form .field__error svg {
  width: 12px;
  height: 12px;
  flex: none;
}

/* `lowercase` e visual: o texto da mensagem continua com a maiuscula inicial no
   DOM, entao leitor de tela e `role="alert"` anunciam a frase como ela foi
   escrita. O italico vem do mesmo tratamento dos rotulos "usuario" e "senha"
   logo acima - o bloco inteiro do formulario fala na mesma voz. */
.login__form .field__error {
  text-transform: lowercase;
  font-style: italic;
}

/* ERRO ABAIXO DO BOTAO, pedido em 03/10/2026.

   Por `order` e nao mexendo na ordem do DOM em app.js: o `.login__form` e um
   grid de coluna unica, entao `order` reposiciona o item sem tocar no codigo
   compartilhado - a instalacao MsX segue com o erro acima do botao.

   E tem um ganho que a troca no DOM NAO teria: a ordem de leitura continua a
   original. O `role="alert"` fica logo depois do switch e antes do botao, que e
   onde um leitor de tela espera encontrar o aviso - o CSS move o desenho, nao a
   semantica.

   `> ` e proposital: pega so o erro do formulario. `.field__error` tambem existe
   DENTRO de cada `.field`, para erro de campo, e esses nao devem sair do lugar. */
.login__form > .field__error {
  order: 1;
}

/* A frase desce 1px e o icone fica onde esta.

   `position: relative` no `span` desloca so o desenho dele; o icone e irmao e
   nao se mexe. Fazer isso com `align-items` ou `padding` na caixa arrastaria os
   dois juntos, que e o oposto do pedido. */
.login__form .field__error span {
  position: relative;
  top: 1px;
}

/* =============================================================================
   14 · CELULAR  (ate 899px)  ·  03/10/2026
   =============================================================================
   Pedido: subir a caixa de login, descer o grupo iUNIX + "Powered By:" + MsX
   para baixo dela, sem o fundo azul, tudo centralizado.

   POR QUE A FAIXA APARECE NO CELULAR. `base.css` esconde ela abaixo de 900px
   (`.login__brand { display: none }`). Quem a traz de volta e uma regra da
   secao 7 deste arquivo - `.login__brand:has(.login__fundo) { display: grid }` -
   escrita sem media query. Nao foi acidente util: e so que ela nunca tinha sido
   pensada para o celular.

   Agora passa a ser deliberado, e por isso o `display` e redeclarado AQUI com o
   mesmo `:has()`: aquele seletor vale (0,2,0), e um `.login__brand {}` simples,
   mesmo dentro de media query, e (0,1,0) e nao ganha dele. Media query nao soma
   especificidade - foi o erro que me custou uma publicacao em B-S1.28.
   ========================================================================== */

@media (max-width: 899px) {

  /* ORDEM. O `order` do desktop vive dentro de `@media (min-width: 900px)`, entao
     aqui valia a ordem do DOM, que traz a faixa primeiro. Declarado explicito: o
     formulario sobe, a marca desce. O DOM nao muda - a ordem de leitura por
     teclado e leitor de tela continua a mesma. */
  .login__panel { order: 1; }
  .login__brand { order: 2; }

  /* `align-content` governa as LINHAS do grid, nao os itens - e e por isso que
     ele precisa estar aqui. O `place-items: center` do base.css centraliza cada
     item DENTRO da sua linha, mas as linhas automaticas continuam ESTICANDO para
     preencher os 100vh. Com duas linhas, o resultado era um vao de 138,7px onde
     o `gap` dizia 32.

     Era `center` e virou `start` em 03/10/2026, com o pedido de 20px de margem
     no topo: centralizado, o bloco ignora qualquer `padding-top` porque sobra
     espaco em cima e embaixo. Com `start` as linhas encolhem no conteudo E se
     encostam no topo, que e onde os 20px passam a valer.

     `min-height: 0` derruba os `100vh` do base.css. Eram eles que criavam o vazio
     depois do logo da MsX: a pagina era obrigada a ter a altura da tela inteira
     mesmo quando o conteudo acabava antes, e com `start` toda a sobra ia para
     baixo. Sem o piso de altura, a pagina mede o conteudo e o `padding-bottom`
     de 20px fecha. O fundo nao falha: `html:has(.login)` ja pinta #0b1620 fora
     da caixa (secao 4), entao encurtar o `.login` nao abre faixa clara. */
  .login {
    gap: var(--space-8);
    align-content: start;
    min-height: 0;
    padding-top: 20px;
    padding-bottom: 20px;
  }

  /* SEM FUNDO. Fora o gradiente, saem as duas camadas que so existem por causa
     dele: o canvas da grade animada e a foto do ::before. Esconder as duas
     tambem poupa o celular de animar um canvas que ninguem veria. */
  .login__brand:has(.login__fundo) { display: block; }

  .login__brand {
    background: none;
    padding: 0;
    overflow: visible;
    min-height: 0;
    align-self: center;
    width: 100%;
  }
  .login__fundo       { display: none; }
  .login__brand::before { display: none; }

  /* CENTRALIZADO. No desktop o bloco e uma coluna alinhada a esquerda, ancorada
     no rodape do painel. Aqui nao ha painel: vira uma coluna centrada, com
     altura do proprio conteudo. */
  .login__brand-conteudo {
    height: auto;
    max-width: none;
    align-items: center;
    text-align: center;
    justify-content: flex-start;
  }

  .login__brand-logo {
    margin-left: auto;
    margin-right: auto;
  }

  .login__creditos {
    align-items: center;
    margin-top: var(--space-5);
  }
}

/* =============================================================================
   15 · MARCA NA SIDEBAR DO ERP  (03/10/2026)
   =============================================================================
   Pedido: logo da Santini a 100% da div e a palavra "ERP" fora.

   Medido antes de mexer, na copia local em modo 'local':
     .sidebar ......... 236px
     .sidebar__brand .. 235 x 56, padding 0 12px  ->  211px uteis
     .sidebar__logo ... render 66,6 x 26  (arquivo 297x116, proporcao 2,56)
     "ERP" ............ 26,6px

   E DE QUEBRA CORRIGE UMA DISTORCAO QUE JA EXISTIA. Com a sidebar RECOLHIDA a
   div cai para 31px uteis, e a combinacao `height: 26px` + `max-width: 100%` do
   base.css forcava os DOIS eixos ao mesmo tempo: o logo renderizava 31x26, ou
   seja proporcao 1,19 contra os 2,56 do arquivo - achatado em 54%. E a mesma
   familia de erro que o cliente apontou em 02/10/2026 ("pois esta distorcendo a
   mesma"), so que escondido no estado recolhido.

   Governando pela LARGURA e deixando a altura em `auto`, nunca ha dois eixos
   forcados: expandido da 211 x 82,4, recolhido da 31 x 12,1, e os dois mantem
   2,56 exato.
   ========================================================================== */

/* A palavra sai por CSS, nao esvaziando `sidebarTexto` no config: `marca()`
   resolve com `||` e string vazia e falsy - cairia de volta no "ERP" padrao.
   `display: none` tambem tira o no da arvore de acessibilidade. */
.sidebar__brand-text { display: none; }

/* A faixa deixa de ter altura fixa porque o logo a 100% da largura passa a
   medir ~82px de alto, contra os 56px de antes. `padding` vertical proprio
   para o desenho nao encostar na borda nem na primeira linha do menu.

   PADDING DE BAIXO MENOR, E NAO E CAPRICHO. Medido em
   `scratchpad\tinta.py`, o `santini-decor.png` tem folga transparente
   ASSIMETRICA dentro do proprio arquivo:

       arquivo 297x116 · tinta de y=11 a y=94
       folga topo ..... 11px        folga baixo .... 21px

   Com `padding` igual nos dois lados, o vao VISIVEL saía desigual, porque a
   folga do arquivo soma ao padding. Na escala em que o logo e exibido
   (196 / 297 = 0,6599):

       acima da tinta ... 12 + 11 x 0,6599 = 19,3px
       abaixo da tinta .. 12 + 21 x 0,6599 = 25,9px

   DEPENDE DA LARGURA DO LOGO, e ja foi recalculado uma vez por isso. Quando o
   logo caiu de 196 para 186px em 03/10/2026, a escala mudou e o desconto junto:

       196px -> 12 - 10 x 196/297 = 5,40px
       186px -> 12 - 10 x 186/297 = 5,74px

   Formula: `12 - (21-11) x largura/297`. Trocar o `width` do `.sidebar__logo`
   sem refazer esta conta desequilibra os vaos de novo. A alternativa definitiva
   seria recortar a folga do PNG, mas o arquivo e do cliente e nao se mexe nele
   sem pedido.

   CENTRALIZADO na horizontal: o logo tem 196px numa caixa de 211px uteis, e
   alinhado a esquerda (padrao do flex) sobrava tudo de um lado so - 12px a
   esquerda contra 27px a direita. `justify-content: center` divide a folga.
   Aqui nao ha correcao a fazer no arquivo: na horizontal a tinta vai de borda a
   borda, 0px de folga dos dois lados. */
.sidebar__brand {
  height: auto;
  padding-top: var(--space-3);
  padding-bottom: 5.74px;      /* ver a conta acima - depende do width do logo */
  justify-content: center;
}

/* Era `width: 100%` (211px na div expandida). Pedido de 03/10/2026: -15px.

   Fixo em 196px e nao `calc(100% - 15px)` por causa do estado RECOLHIDO: ali a
   div tem 31px uteis, e o calc daria 16px - o desenho sumiria. Com largura fixa,
   o `max-width: 100%` do base.css entra como teto e devolve os 31px inteiros
   quando a sidebar fecha. Expandida manda o 196; recolhida manda o teto. Nos
   dois casos a altura fica em `auto` e a proporcao de 2,56 se mantem. */
.sidebar__logo {
  width: 186px;                /* 211 -> 196 -> 186, pedidos de 03/10/2026 */
  height: auto;

  /* Deslocamento para a esquerda: 3px em 03/10/2026, afinado para 2px na mesma
     tarde. `position: relative` desloca so o DESENHO: a caixa continua onde o
     flex a centralizou, entao a altura da faixa e o calculo do padding acima
     nao mudam. Fazer com `margin-right` empurraria o centro e misturaria duas
     coisas num numero so. */
  position: relative;
  left: -2px;
}
/* Item do menu 1px menor, pedido do usuario em 03/10/2026. Ele nao tinha
   `font-size` proprio: herdava os 17px que a identidade da Santini fixou como
   corpo de texto (secao 3). Declarado aqui, o menu passa a 16px sem mexer no
   corpo do resto do sistema. */
.nav-item { font-size: 16px; }

/* Menu 5px mais a esquerda, pedido do usuario em 03/10/2026.

   O recuo e feito em DUAS camadas, e a escolha de qual mexer importa:

       .sidebar__nav   padding-left 8px   -> caixa do item comeca em x=8
       .nav-item       padding-left 8px   -> icone comeca em x=16

   Mexer no `.nav-item` moveria so os itens; os titulos de grupo (VISÃO,
   CADASTROS, COMERCIAL) tem padding proprio e ficariam para tras, quebrando a
   coluna que hoje alinha titulo e item no mesmo x. Mexendo no CONTAINER o bloco
   inteiro anda junto e o alinhamento se mantem. */
.sidebar__nav { padding-left: 3px; }
/* 5px entre o icone e o rotulo, pedido do usuario em 03/10/2026.
   `base.css` traz `gap: var(--space-2-5, 10px)` no `.nav-item`; aqui o valor e
   literal porque nao existe token de 5. Efeito colateral bem-vindo: devolve 5px
   de largura ao rotulo, folga que o item mais longo ("Cobranças em Aberto")
   agradece. */
.nav-item { gap: 5px; }

/* =============================================================================
   16. ESTADO VAZIO — TIPOGRAFIA E RESPIRO
   =============================================================================
   Pedido do usuário em 04/10/2026, olhando a tela de Clientes: título em
   itálico e 15px entre as duas frases.

   POR QUE NÃO MEXI NO `gap` DO `.empty`
   -------------------------------------
   `.empty` é um grid com `gap: var(--space-3)` (12px) e QUATRO filhos: ícone,
   título, texto e botão. O gap do grid é uniforme — subir para 15px afastaria
   também o ícone do título e o texto do botão, mudando três espaços para
   acertar um. A diferença entra como margem só no título:

       12px (gap do grid)  +  3px (margem)  =  15px

   Se algum dia o `--space-3` do produto mudar, esta conta desencosta. Por isso
   ela está escrita como `calc()` em cima do próprio token, e não como "3px"
   solto: o resultado continua 15px sozinho.

   ESCOPO: vale para o estado vazio de TODAS as telas, não só Clientes.
   Itálico em uma tela e romano nas outras leria como defeito, não como
   intenção. As FRASES, essas sim, são só de Clientes — e vêm do app-config.

   O itálico é do título apenas. A segunda linha é chamada para ação; itálico
   nas duas tiraria a hierarquia entre elas.
   ========================================================================== */

.empty__title {
  font-style: italic;
  /* 15px VISÍVEIS, entre o pé das letras de cima e o topo das de baixo.

     A primeira versão (B-S1.49) acertou 15px entre as CAIXAS — medido, e
     correto — mas o olho não vê caixa. Entre as duas frases ainda havia a
     meia-entrelinha de cada uma (line-height 28,5px e 22,5px) e o vão acima
     das maiúsculas. Medido pela tinta, com as métricas reais da fonte
     (canvas measureText), em 04/10/2026: 27,5px na tela. O usuário viu certo.

     Ajuste: 27,5 − 15 = 12,5px a menos  →  margem de 3px vira −9,5px.
     A margem negativa NÃO sobrepõe nada: entre as caixas ainda sobram 2,5px
     (12 de gap − 9,5); o que sai é só o entrelinhamento vazio.

     Vale para estes tamanhos (19px título / 15px texto). Mudou a fonte ou o
     tamanho, remedir — a conta é de tinta, não de token. */
  margin-bottom: -9.5px;
}

/* =============================================================================
   17. CPF / CNPJ NA FONTE DO SISTEMA
   =============================================================================
   Pedido do usuário em 04/10/2026: o documento do cliente saía em fonte
   monoespaçada (`.mono`), destoando do resto da linha. Agora Nunito Sans, no
   tamanho do texto ao redor — na lista e no detalhe do cliente.

   O gancho é a classe `doc`, posta só no CPF/CNPJ (clientes.js). Código do
   cliente e número de orçamento continuam `.mono` — não foram pedidos.

   `tabular-nums`: com fonte proporcional, "111" ficaria mais estreito que
   "888" e os CPFs da coluna deixariam de alinhar. Algarismos de largura fixa
   mantêm a coluna alinhada sem voltar para a monoespaçada.
   ========================================================================== */

.mono.doc {
  font-family: var(--font-ui);
  font-size: inherit;
  font-variant-numeric: tabular-nums;
}

/* =============================================================================
   18. LISTA DE CLIENTES — LINHA 10px MAIS ALTA
   =============================================================================
   Pedido do usuário em 04/10/2026: "uns 10px maior... 5px de cada lado".
   Só a lista de Clientes (gancho `screen--clientes`, posto em clientes.js);
   as outras listas seguem na densidade do produto.

   A célula tinha `padding: 0 12px` e a altura vinha de `height: var(--row-cozy)`.
   Os 5px entram como padding vertical, não mexendo no token: o token é de todas
   as tabelas, e trocar ele aqui esticaria Produtos, Orçamentos e Vendas juntos.
   ========================================================================== */

:is(.screen--clientes, .screen--produtos) .table tbody td {
  padding-top: 5px;
  padding-bottom: 5px;
}

/* =============================================================================
   19. BARRA DO TOPO — MAIOR E MAIS LEGÍVEL
   =============================================================================
   Pedido do usuário em 04/10/2026, olhando a barra inteira:

     · barra 10px mais alta, "5 em cima, 5 embaixo" ........ 48 → 58px
     · "Cadastros / Clientes" em 16px ....................... era 15px
     · ícones de menu (3 tracinhos), lua e teclado maiores .. 14 → 20px
     · "buscar…" minúsculo e itálico ....................... texto no app-config
     · lupa melhor .......................................... desenho novo no
                                                               index.html + 18px

   Os botões crescem junto com os ícones (28 → 36px): ícone de 20px num botão
   de 28 ficaria com 4px de respiro e o hover encostaria no desenho.

   A barra tem `height` fixa no produto (base.css), sem padding vertical; os
   10px entram na própria altura, e o `align-items: center` que já existe
   distribui 5 em cima e 5 embaixo.
   ========================================================================== */

.topbar { height: 58px; }

.topbar__crumb { font-size: 16px; }

.topbar__toggle { width: 36px; height: 36px; }
.topbar__toggle svg { width: 20px; height: 20px; }

.topbar__search-icon {
  width: 18px; height: 18px;
  left: 10px;
  stroke-width: 2;
}
/* 10 (margem) + 18 (lupa) + 8 (respiro) = 36px até o texto */
.topbar__search input { padding-left: 36px; }
.topbar__search input::placeholder { font-style: italic; }

/* =============================================================================
   20. LISTA DE CLIENTES — ÍCONES DE AÇÃO MAIORES
   =============================================================================
   Pedido do usuário em 04/10/2026: Novo Orçamento, Editar e Excluir, ao lado
   de cada cliente, "aumentar também" — na sequência da barra do topo (§19).

   Mesma medida da barra: ícone 14 → 20px, botão 28 → 36px. Ícones de ação do
   sistema com tamanho único; dois tamanhos lado a lado leriam como descuido.
   Cabe na linha de 56px (§18) com 10px de sobra em cima e embaixo.

   Só Clientes (`screen--clientes`), como pedido. A largura da coluna de ações
   se ajusta sozinha: a divisão de folga (ui.js, B-S1.56) mede o grupo de
   botões a cada desenho.
   ========================================================================== */

:is(.screen--clientes, .screen--produtos) .row-actions .btn--icon { width: 36px; height: 36px; }
:is(.screen--clientes, .screen--produtos) .row-actions .btn--icon svg { width: 20px; height: 20px; }

/* =============================================================================
   21. DIVISÓRIA DA COLUNA DE AÇÕES NO MEIO DO VÃO
   =============================================================================
   Pedido do usuário em 04/10/2026: a divisória (a sombra na borda esquerda da
   coluna fixa de ações, components.css) "tem que ficar no centro entre o 0 e o
   ícone do orçamento".

   Medido antes: do fim de "0,00" à divisória 12px; da divisória ao DESENHO do
   ícone 72,8px. Os botões alinhavam à direita, e toda a folga da coluna (a
   divisão igual de B-S1.56) caía entre a divisória e eles.

   A divisória não se move — é a borda da coluna fixa. Os botões passam a
   alinhar à ESQUERDA, com recuo calculado para o desenho do ícone ficar a
   12px da divisória, espelhando os 12px do outro lado:

       12  =  recuo  +  8 (folga do botão: (36 − 20) / 2)
                     +  3,5 (o traço começa em x = 4,17 de 24 no viewBox,
                             × 20/24 = 3,47px)
       recuo = 0,5px

   A folga da coluna passa para a direita dos botões, junto da borda da
   tabela. Depende dos tamanhos da §20 (botão 36, ícone 20): mudou lá,
   remedir aqui.
   ========================================================================== */

/* Centralizado na célula (pedido de 04/10/2026, logo depois): a coluna agora
   tem a largura exata dos botões — ui.js deixou de dar folga a ela — e o
   recuo é o mesmo dos dois lados, então o grupo fica no centro E a 12px da
   divisória. */
/* 25px VISÍVEIS de cada lado (pedido de 04/10/2026, em seguida): da
   divisória até o desenho do ícone de orçamento, e do desenho da lixeira até
   a borda da tabela. Os recuos diferem porque os dois desenhos têm margens
   internas diferentes no viewBox — medido: com 0,5px de recuo, 12px de um
   lado e 14,5px do outro.
       esquerda  0,5 + (25 − 12)   = 13,5px
       direita   0,5 + (25 − 14,5) = 11px
   A coluna cresce sozinha: ui.js mede o recuo do cabeçalho, que tem a mesma
   classe. */
:is(.screen--clientes, .screen--produtos) .table .col-acoes {
  text-align: center;
  padding-left: 13.5px;
  padding-right: 11px;
}

/* =============================================================================
   22. ERRO DE CAMPO — MESMO TRATAMENTO DO LOGIN, EM TODO FORMULÁRIO
   =============================================================================
   Pedido do usuário em 04/10/2026, no cadastro de cliente ("CPF inválido.",
   "Obrigatório."): texto 1px mais baixo, itálico, minúsculo, sem ponto final.

   É exatamente o que a §13 já fazia no login — lá o escopo era `.login__form`
   porque o pedido era só do login. Agora vale para `.field__error` em toda
   tela. O ponto final sai pelo config (`erroCampoSemPonto`), porque CSS não
   apaga caractere.

   `top: 1px` aqui e na §13 é a MESMA propriedade no mesmo elemento: não soma,
   o erro do login continua com 1px, não 2.

   Efeito colateral conhecido do minúsculo: no aviso de documento duplicado,
   o nome do cliente citado também sai em minúsculo ("já cadastrado em
   padaria pão de ouro ltda").
   ========================================================================== */

.field__error span {
  text-transform: lowercase;
  font-style: italic;
  position: relative;
  top: 1px;
}

/* =============================================================================
   23. CABEÇALHO ORDENADO SEM MUDAR DE COR (lista de Clientes)
   =============================================================================
   Pedido do usuário em 04/10/2026: ao clicar para ordenar, "só aparecer a
   setinha mantendo a fonte igual". O produto pinta a coluna ordenada com
   `--accent` (components.css); aqui ela fica na cor dos outros cabeçalhos.
   O hover continua clareando, como nos demais.
   ========================================================================== */

:is(.screen--clientes, .screen--produtos) .table thead th[aria-sort] { color: var(--text-tertiary); }
:is(.screen--clientes, .screen--produtos) .table thead th[aria-sort]:hover { color: var(--text-secondary); }

/* =============================================================================
   24. COLUNA FATURADO — 25px ATÉ A DIVISÓRIA
   =============================================================================
   Pedido do usuário em 04/10/2026: o último dígito do Faturado precisa de 25px
   à direita, como os ícones (§21). Era o padding padrão da célula, 12px.

   Em Clientes a única coluna `.num` é Faturado. O cabeçalho leva o mesmo
   recuo para "FATURADO" continuar alinhado com os valores — e porque é dele
   que a divisão de folga (ui.js) lê o padding da coluna.
   ========================================================================== */

:is(.screen--clientes, .screen--produtos) .table th.num,
:is(.screen--clientes, .screen--produtos) .table td.num { padding-right: 25px; }


/* =============================================================================
   25. BUSCA DO TOPO — CURSOR DE LUPA, NÃO DE "PROIBIDO"
   =============================================================================
   Relatado pelo usuário em 04/10/2026: o mouse sobre a busca mostrava o
   cursor de proibido, embora a busca funcionasse.

   Causa: o campo é `readonly` de propósito — ele só abre a paleta de busca
   (app.js, onfocus/onclick) — e o produto dá `cursor: not-allowed` a todo
   `.input[readonly]` (components.css). A regra serve a campo travado de
   formulário; aqui ela mentia.

   Lupa em SVG: traço branco com contorno escuro, legível no tema claro e no
   escuro. Ponto ativo no centro da lente (10 10). `pointer` é o reserva, se o
   navegador recusar cursor em SVG. Seletor com três classes/atributos para
   vencer o `.input[readonly]` (duas).
   ========================================================================== */

.topbar__search .input[readonly] {
  cursor: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cg fill='none' stroke-linecap='round'%3E%3Ccircle cx='10' cy='10' r='6.5' stroke='%23000' stroke-width='4'/%3E%3Cpath d='M15 15l6 6' stroke='%23000' stroke-width='4'/%3E%3Ccircle cx='10' cy='10' r='6.5' stroke='%23fff' stroke-width='2'/%3E%3Cpath d='M15 15l6 6' stroke='%23fff' stroke-width='2'/%3E%3C/g%3E%3C/svg%3E") 10 10, pointer;
}

/* =============================================================================
   26. LISTA DE CLIENTES NO CELULAR — CARTÃO NO LUGAR DE TABELA
   =============================================================================
   Relatado pelo usuário em 04/10/2026 ("ESTÁ PÉSSIMA"): abaixo da largura da
   tabela ela rolava de lado, e a coluna de ações — fixa à direita (sticky) —
   ficava POR CIMA do CPF; ícone e número embaralhados na mesma faixa.

   Rolagem lateral não tem conserto bom numa tabela de 8 colunas em 400px:
   qualquer largura esconde o que importa. Aqui a linha deixa de ser linha:
   cada cliente vira um cartão empilhado, tudo legível sem rolar de lado.

       código ··········· tipo
       nome (e apelido)
       CPF / CNPJ  (rótulo)
       cidade
       contato
       FATURADO   (rótulo)
       valor ········ ações

   O rótulo vem de `data-label` (ui.js põe em toda célula); o lugar no cartão,
   de `data-col`. Só CPF/CNPJ e Faturado ganham rótulo: número solto não diz o
   que é; nome, cidade e e-mail se explicam sozinhos.

   860px é o mesmo corte em que o produto troca para o layout de celular
   (base.css). Cabeçalho some — a lista abre na ordem do código (§23/B-S1.62).
   A divisão de folga (ui.js) reconhece `display: block` e não mede nada aqui.

   Os ajustes de desktop desta tela (§18, §20, §21, §24) são anulados dentro
   do bloco: seletores com `[data-col]` têm peso maior que os deles.
   ========================================================================== */

@media (max-width: 860px) {
  :is(.screen--clientes, .screen--produtos) .table-wrap { overflow: visible; }

  :is(.screen--clientes, .screen--produtos) .table,
  :is(.screen--clientes, .screen--produtos) .table tbody { display: block; width: 100%; }
  :is(.screen--clientes, .screen--produtos) .table thead { display: none; }

  :is(.screen--clientes, .screen--produtos) .table tbody tr {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas:
      "codigo    tipo"
      "nome      nome"
      "documento documento"
      "cidade    cidade"
      "contato   contato"
      "faturado  acoes";
    column-gap: 12px;
    row-gap: 8px;
    padding: 16px;
    border-bottom: 1px solid var(--border-subtle);
  }

  /* zera o desenho de célula de tabela (altura fixa, padding, corte com
     reticências, largura máxima) — no cartão o texto quebra linha */
  :is(.screen--clientes, .screen--produtos) .table tbody td[data-col] {
    display: block;
    height: auto;
    max-width: none;
    padding: 0;
    border: 0;
    white-space: normal;
    overflow: visible;
    overflow-wrap: anywhere;
    text-align: left;
  }

  :is(.screen--clientes, .screen--produtos) .table tbody td[data-col="codigo"]    { grid-area: codigo; align-self: center; }
  :is(.screen--clientes, .screen--produtos) .table tbody td[data-col="tipo"]      { grid-area: tipo; justify-self: end; align-self: center;
                                                            color: var(--text-tertiary); }
  :is(.screen--clientes, .screen--produtos) .table tbody td[data-col="nome"]      { grid-area: nome; font-size: 17px; }
  :is(.screen--clientes, .screen--produtos) .table tbody td[data-col="documento"] { grid-area: documento; }
  :is(.screen--clientes, .screen--produtos) .table tbody td[data-col="cidade"]    { grid-area: cidade; color: var(--text-secondary); }
  :is(.screen--clientes, .screen--produtos) .table tbody td[data-col="contato"]   { grid-area: contato; }
  :is(.screen--clientes, .screen--produtos) .table tbody td[data-col="faturado"]  { grid-area: faturado; align-self: end; }

  /* ações: sai da coluna fixa (sticky + sombra) e assenta no canto do cartão */
  :is(.screen--clientes, .screen--produtos) .table tbody td[data-col="_acoes"] {
    grid-area: acoes;
    align-self: end;
    justify-self: end;
    position: static;
    box-shadow: none;
    background: transparent;
    margin-right: -8px;    /* folga interna do botão: alinha o desenho à borda do texto */
    width: auto;           /* o produto dá width: 1% (tabela); no cartão isso espremia a
                              célula em 1px e os botões vazavam — medido em 04/10/2026 */
  }

  :is(.screen--clientes, .screen--produtos) .table tbody td[data-col="documento"]::before,
  :is(.screen--clientes, .screen--produtos) .table tbody td[data-col="faturado"]::before {
    content: attr(data-label);
    display: block;
    margin-bottom: 2px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-tertiary);
  }

  :is(.screen--clientes, .screen--produtos) .table tbody tr:last-child { border-bottom: 0; }

  /* Contato em 15px e uma linha por item. E-mail longo quebrava no meio da
     palavra ("…com.b / r") com o `overflow-wrap: anywhere` acima; aqui ele
     termina em "…" se não couber — o endereço inteiro está no detalhe. */
  :is(.screen--clientes, .screen--produtos) .table tbody td[data-col="contato"] { font-size: 15px; }
  :is(.screen--clientes, .screen--produtos) .table tbody td[data-col="contato"] > * {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

/* =============================================================================
   27. LISTA DE CLIENTES — BARRA DE FERRAMENTAS SEPARADA DA LISTA
   =============================================================================
   Pedido do usuário em 04/10/2026, com imagem de referência: busca, filtro,
   contador e CSV num bloco próprio; "uma margin dividindo" e, embaixo, a
   lista com a paginação em outro bloco. Vão de 20px, medido na referência.

   A grade (ui.js) é UM `.card` com três filhos: .toolbar, .table-wrap, .pager.
   Sem mexer no código compartilhado: o card de fora fica transparente e sem
   borda, e cada parte veste o fundo e a borda de card que ele tinha.

       .toolbar ............ card inteiro (4 cantos arredondados)
       ── 20px ──
       .table-wrap ......... cantos de cima
       .pager .............. cantos de baixo

   Sem paginação (lista vazia: ui.js esconde o .pager com `.hidden`), a área
   da tabela fecha sozinha com os quatro cantos — `:has()` olha o vizinho.
   `>` em tudo: só a grade desta tela, nenhum outro card de dentro dela.
   ========================================================================== */

/* :has(> .toolbar): só o card da GRADE. Produtos tem outro .card na tela (aviso
   de estoque baixo) que perdia fundo e borda com a regra genérica — 05/10/2026. */
:is(.screen--clientes, .screen--produtos) > .card:has(> .toolbar) {
  background: transparent;
  border: 0;
}

:is(.screen--clientes, .screen--produtos) > .card > .toolbar {
  background: var(--surface-raised);
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
  margin-bottom: 20px;
}

:is(.screen--clientes, .screen--produtos) > .card > .table-wrap {
  background: var(--surface-raised);
  border: 1px solid var(--border-subtle);
  border-bottom: 0;
  border-radius: var(--radius-md) var(--radius-md) 0 0;
}

:is(.screen--clientes, .screen--produtos) > .card > .pager {
  background: var(--surface-raised);
  border: 1px solid var(--border-subtle);
  border-radius: 0 0 var(--radius-md) var(--radius-md);
}

:is(.screen--clientes, .screen--produtos) > .card > .table-wrap:has(+ .pager.hidden) {
  border-bottom: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
}

/* =============================================================================
   28. CONTADOR "1 Registro" E TÍTULOS DA TABELA 1px ABAIXO
   =============================================================================
   Pedido do usuário em 04/10/2026.

   Contador: "1 registro" → "1 Registro". `capitalize` dá a forma certa nos três
   textos que o componente gera (ui.js): "Nenhum Registro", "1 Registro",
   "5 Registros" — sem palavra de ligação para estragar. Vale para TODAS as
   listas: o contador é um só, e "Registro" numa tela e "registro" na outra
   leria como descuido.

   Títulos (Código, Nome, Tipo…): 1px abaixo SÓ O TEXTO. O `th` é fixo no topo
   ao rolar (sticky) e tem fundo próprio — mover a célula com `top` desalinharia
   o fundo da linha. Com altura fixa de 30px e centro vertical, 2px de padding
   em cima empurram o centro do texto em 1px; a célula continua com 30px.
   ========================================================================== */

.toolbar__count { text-transform: capitalize; }

:is(.screen--clientes, .screen--produtos) .table thead th { padding-top: 2px; }

/* =============================================================================
   29. BUSCA DA LISTA DE CLIENTES IGUAL À BUSCA DO TOPO
   =============================================================================
   Pedido do usuário em 04/10/2026: "colocar o mesmo ícone da busca na parte
   superior" e o texto "nome ou documento…" em itálico.

   O símbolo já é o mesmo (`#i-busca`, redesenhado no index.html, B-S1.57); o
   que diferia era o tamanho e o traço — 14px e 1,7 aqui, 18px e 2 no topo
   (§19). Mesmas medidas, mesmo respiro até o texto:
       10 (margem) + 18 (lupa) + 8 (respiro) = 36px de recuo.
   O texto vem do app-config (`clientesBuscaPlaceholder`).
   ========================================================================== */

:is(.screen--clientes, .screen--produtos) .toolbar__search svg {
  width: 18px; height: 18px;
  left: 10px;
  stroke-width: 2;
}
:is(.screen--clientes, .screen--produtos) .toolbar__search input { padding-left: 36px; }
:is(.screen--clientes, .screen--produtos) .toolbar__search input::placeholder { font-style: italic; }

/* =============================================================================
   30. FICHA DO CLIENTE — CONTADOR DOS CARTÕES E BOTÃO "NOVO ORÇAMENTO"
   =============================================================================
   Pedido do usuário em 04/10/2026, na ficha que abre ao clicar no cliente.

   Contador de Orçamentos/Vendas 3px abaixo. Medido antes: o selo estava
   centrado com o título (centros iguais), e o algarismo ocupa quase todo o
   selo (19px de altura, 1,25px de folga em cima). Descer só o algarismo 3px o
   tiraria de dentro do selo — desce o SELO, com o número junto.

   Texto do botão "Novo Orçamento" 1px abaixo; o ícone fica onde está.
   Ganchos (`card--resumo-doc`, `acao-novo-orcamento`) postos em clientes.js,
   sem efeito na MsX.
   ========================================================================== */

/* REVERTIDO em 04/10/2026, a pedido do usuário ("a div em volta do zero não
   está centralizada verticalmente"). O `top: 3px` acima deslocava o selo
   inteiro e o tirava do centro da faixa: medido 18,25px em cima e 12,25px
   embaixo. Sem ele, 15,25 / 15,25. O algarismo dentro do selo já estava
   centrado (5,25 / 5,75 de tinta) — não precisava de ajuste.
   (regra anterior: .card--resumo-doc .card__head .badge { top: 3px }) */
/* "Novo Orçamento": o +1px de 04/10 foi desfeito em 05/10 ("subir 1px") — texto
   de volta ao centro do botão, alinhado ao ícone. */

/* Selo do tipo na ficha: "Pessoa Física" como o usuário escreveu. O .badge do
   produto põe tudo em maiúsculas (PESSOA FÍSICA) — aqui não. */
.selo-tipo-cliente { text-transform: none; }

/* Fonte maior e 3px em cima e embaixo (pedido de 04/10/2026). O `.badge` do
   produto tem altura FIXA de 19px e padding vertical zero — sem tirar a
   altura fixa, o padding não teria onde entrar. Medido antes: 11px de fonte.
   13px: maior, e ainda abaixo do código ao lado (15px), que é o dado principal. */
.selo-tipo-cliente {
  font-size: 14px;   /* 13 -> 14px: "aumentar 1px ainda", 04/10/2026 */
  height: auto;
  padding-top: 3px;
  padding-bottom: 3px;
  /* +3px dos lados (05/10/2026): o .badge do produto tem 8px -> 11px */
  padding-left: 11px;
  padding-right: 11px;
}

/* =============================================================================
   31. FICHA DO CLIENTE — 5px ENTRE OS ITENS (Nome, CPF, Email…)
   =============================================================================
   Pedido do usuário em 05/10/2026: "diminuir a margin de cada item… deixar em
   5px". O espaço entre as linhas é o `gap` de linha do `.dl` (components.css):
   8px → 5px. A coluna (rótulo ↔ valor) não muda.

   Escopo pela ficha do CLIENTE, sem gancho novo no JS: só ela tem o selo
   `.selo-tipo-cliente` na linha de cima. Outras janelas com `.dl` (venda,
   orçamento) ficam como estão.
   ========================================================================== */

.stack:has(> .row .selo-tipo-cliente) > .dl { row-gap: 5px; }

/* =============================================================================
   32. CONTADOR DOS CARTÕES — ALGARISMO 2px ABAIXO, SELO NO CENTRO
   =============================================================================
   Pedido do usuário em 05/10/2026: "desce o 0 em 2px (e centralizar a div
   verticalmente que o 0 está)".

   O selo tem altura FIXA (19px, do produto) e centraliza o conteúdo. 2px de
   padding em cima encolhem a área de conteúdo por cima e o centro dela desce
   2px — o algarismo desce 2px e o SELO não muda de tamanho nem de lugar,
   continuando no centro da faixa do cartão (§30, B-S1.74).
   ========================================================================== */

/* 4px e nao 2: o padding encolhe a area de conteudo e o centro dela desce METADE
   do que foi somado. Medido com 2px: o algarismo desceu so 1px (5,25 -> 6,25). */
/* 05/10: "subir 1px o numeral" -> 4px vira 2px (o centro anda METADE do padding). */
/* 05/10: "+1px de padding top na div da bolinha e do numeral" -> 3px (os dois descem juntos). */
.card--resumo-doc .card__head .badge { padding-top: 3px; }
/* A bolinha (::before) desceu junto com o algarismo. Medido: centro dela a 11,5px
   do topo do selo, centro do selo a 9,5 — 2px abaixo. Sobe 2px (05/10/2026). */
/* A bolinha anda junto com o numeral: com 2px de padding ela fica 1px abaixo do centro. */
.card--resumo-doc .card__head .badge::before { position: relative; top: -1px; }

/* =============================================================================
   33. FICHA DO CLIENTE — RÓTULOS EM 15px, COM DOIS-PONTOS
   =============================================================================
   Pedido do usuário em 05/10/2026: "aumentar os títulos Nome, CPF, Email…
   pra 15px e colocar um ':' depois de cada palavra".

   Os rótulos (`dt`) vinham em `--text-xs`. O ":" entra por `::after` — o
   texto do rótulo continua o mesmo no HTML (leitor de tela, cópia), e não
   precisa mexer em cada rótulo no JS. Mesmo escopo da §31: só a ficha do
   cliente.
   ========================================================================== */

/* text-align: right (05/10/2026, por imagem): rótulos encostados nos valores,
   o ":" de todos na mesma vertical. */
.stack:has(> .row .selo-tipo-cliente) > .dl dt { font-size: 15px; }
/* (o text-align: right acima foi desfeito no mesmo dia: "deixar os títulos alinhados
   para a esquerda mesmo". Volta ao padrão, à esquerda.) */
.stack:has(> .row .selo-tipo-cliente) > .dl dt::after { content: ":"; }

/* =============================================================================
   34. FICHA DO CLIENTE — CÓDIGO EM NUNITO SANS, NO TAMANHO DOS DADOS
   =============================================================================
   Pedido do usuário em 05/10/2026: "a fonte do código mudar para Nunito Sans e
   deixar com o mesmo tamanho das outras infos". Medido antes: JetBrains Mono
   15px; os valores da ficha (Nome, CPF…) são Nunito Sans 17px.

   Só a fonte e o tamanho; a cor cinza (`.muted`) continua — o código é dado de
   apoio, não o nome do cliente. `tabular-nums` mantém "00001" com algarismos
   de largura igual, como no CPF (§17). Escopo: só a ficha do cliente.
   ========================================================================== */

.stack:has(> .row .selo-tipo-cliente) > .row > .mono {
  font-family: var(--font-ui);
  font-size: 14px;   /* 17 -> 14px (05/10/2026): mesmo tamanho do selo "Pessoa Física" */
  font-variant-numeric: tabular-nums;
}

/* =============================================================================
   35. PAGINAÇÃO COM O MESMO RESPIRO DA BARRA DE BUSCA (lista de Clientes)
   =============================================================================
   Pedido do usuário em 05/10/2026, por imagem: a caixa de busca ligada ao
   "50 / Página" da paginação, com marcas de espaço em cima e embaixo.
   Medido antes: barra 12px / 12px (bloco de 62px); paginação 8px / 8px
   (54px). Controles da mesma altura nos dois (36px) — o que diferia era o
   respiro. Paginação passa a 12 / 12.
   ========================================================================== */

:is(.screen--clientes, .screen--produtos) > .card > .pager { padding-top: 12px; padding-bottom: 12px; }

/* =============================================================================
   36. PAGINAÇÃO EM BLOCO PRÓPRIO, COM CANTOS DE 10px (lista de Clientes)
   =============================================================================
   Pedido do usuário em 05/10/2026, na mesma imagem da §35: "também dei uma
   margin na barra do seletor 50 / página, e dei radius em 10".

   A §27 colava a paginação embaixo da tabela (tabela com cantos só em cima,
   paginação só embaixo). Agora são três blocos soltos:
       barra ── 20px ── tabela ── 20px ── paginação
   20px é o mesmo vão barra→tabela; na imagem os dois vãos são iguais.
   A tabela fecha sozinha (borda e cantos embaixo também); a paginação ganha
   borda inteira e raio de 10px, como pedido.
   ========================================================================== */

:is(.screen--clientes, .screen--produtos) > .card > .table-wrap {
  border-bottom: 1px solid var(--border-subtle);
  border-radius: var(--radius-md);
}
:is(.screen--clientes, .screen--produtos) > .card > .pager {
  margin-top: 20px;
  border-radius: 10px;
}

/* =============================================================================
   37. COLUNA CÓDIGO DA LISTA EM NUNITO SANS (como o CPF)
   =============================================================================
   Pedido do usuário em 05/10/2026, apontando o "00002" da lista: "aqui precisa
   deixar a mesma fonte também". Medido antes: JetBrains Mono 15px; o CPF da
   mesma linha é Nunito Sans 17px com `tabular-nums` (§17). Mesmo tratamento:
   fonte do sistema, tamanho da linha, algarismos de largura igual — os
   códigos continuam alinhados um embaixo do outro.
   ========================================================================== */

:is(.screen--clientes, .screen--produtos) .table td[data-col="codigo"] .mono {
  font-family: var(--font-ui);
  font-size: 16px;   /* 17 -> 16px: "diminuir 1px do cód." (05/10/2026) */
  font-variant-numeric: tabular-nums;
}

/* =============================================================================
   38. FILTRO "PESSOA FÍSICA E JURÍDICA" COM A CARA DO TEXTO DA BUSCA
   =============================================================================
   Pedido do usuário em 05/10/2026, por imagem ligando o "nome ou documento…"
   ao filtro: mesma cor, mesmo tamanho, itálico. Medido antes: o texto de
   ajuda da busca é Nunito Sans 17px, itálico, cor `--text-disabled`
   (rgb 95,116,136); o filtro era 17px, normal, branco. O tamanho já batia.

   As opções da lista aberta voltam ao normal (cor de texto, sem itálico):
   lá são escolhas para ler, não texto de ajuda.
   ========================================================================== */

:is(.screen--clientes, .screen--produtos) .toolbar select {
  color: var(--text-disabled);
  font-style: italic;
}
:is(.screen--clientes, .screen--produtos) .toolbar select option {
  color: var(--text-primary);
  font-style: normal;
}

/* =============================================================================
   39. PAGINAÇÃO — SEM "MOSTRANDO 1–5 DE 5"; "50 / PÁGINA" COMO O FILTRO
   =============================================================================
   Pedidos do usuário em 05/10/2026: "remover isso" (o "Mostrando 1–2 de 2") e
   "colocar a fonte 50 / Página igual" ao filtro "Pessoa Física" (§38: cinza
   do texto de ajuda, itálico).

   O "Mostrando…" é o PRIMEIRO filho da paginação (ui.js); o "1 / 1" e os
   botões ‹ › ficam. `display: none` e não apagar no JS: a contagem continua
   no contador da barra de cima ("5 Registros").
   ========================================================================== */

:is(.screen--clientes, .screen--produtos) > .card > .pager > span:first-child { display: none; }

:is(.screen--clientes, .screen--produtos) > .card > .pager select {
  color: var(--text-disabled);
  font-style: italic;
  font-size: 17px;   /* medido: era 15px; o filtro de cima tem 17px */
}
:is(.screen--clientes, .screen--produtos) > .card > .pager select option {
  color: var(--text-primary);
  font-style: normal;
}

/* =============================================================================
   40. BOTÃO CSV COM A CARA DO FILTRO; CONTADOR 1px MAIOR E 2px ABAIXO
   =============================================================================
   Pedidos do usuário em 05/10/2026: "deixar a div do CSV com a mesma
   aparência das outras divs" (imagem: o filtro "Pessoa Física e Jurídica") e
   "o termo '2 Registros' aumentar 1px na fonte e descer 2px na posição".

   Medido antes: filtro 36px de altura, fundo `--surface-base`, texto cinza
   itálico 17px peso 400; CSV era `btn--sm` (28px), fundo mais claro, texto
   branco 15px peso 500. Mesmos tokens do `.select` (components.css): altura
   `--control-height-md`, fundo `--surface-base`, borda `--border-interactive`,
   raio `--radius-sm`. Ícone em 18px e traço 2, como as lupas (§19/§29).
   Hover igual ao dos campos: só a borda reforça.

   Contador: 13px → 14px; desce 2px com `top` (só ele, a barra não muda).
   ========================================================================== */

:is(.screen--clientes, .screen--produtos) .toolbar .btn {
  height: var(--control-height-md);
  padding: 0 var(--space-3);
  background: var(--surface-base);
  border: 1px solid var(--border-interactive);
  border-radius: var(--radius-sm);
  color: var(--text-disabled);
  font-size: 17px;
  font-style: normal;   /* sem itálico (05/10/2026) */
  font-weight: 400;
}
/* Texto "CSV" 1px abaixo (05/10/2026); ícone e botão no lugar. */
:is(.screen--clientes, .screen--produtos) .toolbar .btn > span { position: relative; top: 1px; }
:is(.screen--clientes, .screen--produtos) .toolbar .btn:hover {
  background: var(--surface-base);
  border-color: var(--border-strong);
}
:is(.screen--clientes, .screen--produtos) .toolbar .btn svg { width: 18px; height: 18px; stroke-width: 2; }

:is(.screen--clientes, .screen--produtos) .toolbar .toolbar__count {
  font-size: 14px;
  position: relative;
  top: 2px;
}

/* =============================================================================
   41. FICHA DO CLIENTE — RÓTULO NA PRIMEIRA LINHA DO VALOR
   =============================================================================
   Pedido do usuário em 05/10/2026: "quando tiver 2 linhas, alinhar o título
   verticalmente no topo" (Endereço longo quebrava e o rótulo ficava no meio
   das duas linhas — `align-self: center` do produto).

   `baseline`, e não `start`: o rótulo tem 15px e o valor 17px; alinhados pelo
   topo da caixa, o rótulo ficaria um pouco acima do texto ao lado. Pela linha
   de base, os dois pousam na mesma linha — com uma linha de valor ou com
   duas, o rótulo fica sempre na primeira.
   ========================================================================== */

.stack:has(> .row .selo-tipo-cliente) > .dl dt,
.stack:has(> .row .selo-tipo-cliente) > .dl dd { align-self: baseline; }


/* =============================================================================
   42. LISTA DE PRODUTOS — O QUE É SÓ DELA
   =============================================================================
   Pedido do usuário em 05/10/2026: Produtos "com o mesmo layout da página de
   clientes — ícones, escritas, filtros, fontes, espaçamentos, tudo".

   As regras de lista acima foram estendidas para `.screen--produtos` com
   `:is(.screen--clientes, .screen--produtos)` — mesmo peso de seletor que
   antes (uma classe), nada muda em Clientes. Aqui fica o que depende das
   colunas de Produtos, que são outras:

     SKU ......... mesma fonte do Código de Clientes (§37): Nunito Sans 16px,
                   algarismos de largura igual.
     celular ..... o cartão (§26) usa áreas com os nomes das colunas de
                   Clientes; Produtos tem sku · descricao · precoVenda ·
                   situacao · ações:
                       sku ··············· situação
                       nome
                       PREÇO (rótulo)
                       valor ·········· ações
   ========================================================================== */

/* Ações: o primeiro ícone de Produtos é outro (lápis, ou caixa quando o item
   controla estoque), com margem interna diferente no desenho. Medido com o
   recuo de Clientes (13,5px): 23,9px da divisória ao desenho. +1,1 = 25. */
/* À DIREITA, e não centralizado: o ícone de estoque (caixa) só existe em item
   que controla estoque. Centralizado, linha com 2 ícones ficava a 45px da
   divisória e o lápis/lixeira mudavam de lugar de uma linha para outra. À
   direita, lápis e lixeira ficam sempre na mesma vertical; a caixa aparece à
   esquerda deles quando existe. Com 3 ícones o resultado segue 25 / 25. */
.screen--produtos .table .col-acoes { padding-left: 14.6px; text-align: right; }
.screen--produtos .table td[data-col="sku"] .mono {
  font-family: var(--font-ui);
  font-size: 16px;
  font-variant-numeric: tabular-nums;
}

@media (max-width: 860px) {
  .screen--produtos .table tbody tr {
    grid-template-areas:
      "sku        situacao"
      "descricao  descricao"
      "precoVenda acoes";
  }
  .screen--produtos .table tbody td[data-col="sku"]        { grid-area: sku; align-self: center; }
  .screen--produtos .table tbody td[data-col="situacao"]   { grid-area: situacao; justify-self: end; align-self: center; }
  .screen--produtos .table tbody td[data-col="descricao"]  { grid-area: descricao; font-size: 17px; }
  .screen--produtos .table tbody td[data-col="precoVenda"] { grid-area: precoVenda; align-self: end; }
  .screen--produtos .table tbody td[data-col="precoVenda"]::before {
    content: attr(data-label);
    display: block;
    margin-bottom: 2px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-tertiary);
  }
}

/* =============================================================================
   43. CANTOS DE 7px EM TODOS OS BLOCOS DE CLIENTES E PRODUTOS
   =============================================================================
   Pedido do usuário em 05/10/2026: "o border-radius dos blocos da página
   clientes e produtos precisam ter 7px… arrumar tudo". Antes: barra e lista
   6px (`--radius-md`), paginação 10px (§36), aviso de estoque 6px.

   O token `--radius-md` NÃO muda: é de todos os cards do sistema. Aqui só os
   blocos destas duas telas. A lista vazia tem regra própria mais forte (§27,
   `:has(+ .pager.hidden)`) — por isso aparece de novo na lista abaixo.
   ========================================================================== */

:is(.screen--clientes, .screen--produtos) > .card:has(> .toolbar) > :is(.toolbar, .table-wrap),
:is(.screen--clientes, .screen--produtos) > .card:has(> .toolbar) > .table-wrap:has(+ .pager.hidden),
:is(.screen--clientes, .screen--produtos) > .card:has(> .toolbar) > .pager,
.screen--produtos > .card:not(:has(> .toolbar)) {
  border-radius: 7px;
}
/* =============================================================================
   44. MENU LATERAL — HOVER DE PONTA A PONTA E MÓDULOS MAIS ESPAÇADOS
   =============================================================================
   Pedido do usuário em 05/10/2026: "deixar o hover cobrindo o menu inteiro…
   0 margin left e right" e "espaçar um pouco os módulos".

   O fundo do hover/selecionado tinha recuo dos dois lados porque o CONTAINER
   (`.sidebar__nav`) tem padding lateral (3px à esquerda, §15; 8px à direita,
   base.css). Zerado ali, o item vai de borda a borda. O recuo que sumiu do
   container volta DENTRO do item e do título de grupo (3 + 8 = 11px), então
   ícone, rótulo e VISÃO/CADASTROS ficam exatamente no mesmo x de antes.
   Sem o recuo, o canto arredondado viraria uma pontinha solta encostada na
   borda — por isso o raio vai a 0.

   Espaço: item de 32 para 36px (2px acima e abaixo do rótulo). No toque
   (pointer: coarse) continua o alvo de 44px da base.

   Só `.sidebar__nav`: o "Sair" do rodapé não é módulo e fica como está. O menu
   recolhido (só ícones) mantém a regra própria da base, mais forte.
   ========================================================================== */

.sidebar__nav { padding-left: 0; padding-right: 0; }
.sidebar__nav .nav-item {
  height: 36px;
  padding: 0 8px 0 11px;
  border-radius: 0;
}
.sidebar__nav .sidebar__section { padding-left: 11px; padding-right: 11px; }

@media (max-width: 860px) {
  .app[data-sidebar="collapsed"] .sidebar__nav .nav-item { padding: 0 8px 0 11px; }
}
@media (pointer: coarse) {
  .sidebar__nav .nav-item { height: var(--touch-target-min); }
}

/* =============================================================================
   45. CELULAR — LISTA SIMPLES, BOTÃO "+" FLUTUANTE E POPUP COMO PÁGINA
   =============================================================================
   Pedido do usuário em 05/10/2026, com imagem de referência, para Clientes "e
   nas demais": no celular, cada registro é uma linha com ícone, nome e duas
   linhas de apoio (Clientes: Email e Celular), separadas por um fio; o "+" de
   novo cadastro flutua no canto; o toque abre o registro inteiro "em forma de
   uma nova página".

   O JS desenha as duas versões (ui.js, `op.movel`, ligado por
   `marca.listaMovel`) e aqui se escolhe qual aparece: até 600px a lista;
   de 601 a 860px o cartão (§26/§46); acima, a tabela de sempre.
   A tabela em cartão das §26/§42 fica sem efeito no celular (a tabela some).

   Popup como página: ui.js põe `.modal--pagina` só quando abre no celular
   (`marca.modalPaginaMovel`), em popup grande (lg/xl). Confirmações (md)
   continuam caixinha no meio da tela — tela cheia para um "Tem certeza?"
   seria desproporcional.
   ========================================================================== */

.lista-movel { display: none; }

/* Corte da lista: 600px (celular). De 601 a 860px (tablet, janela estreita)
   fica o cartão das §26/§46 — decisão do usuário em 05/10/2026. */
@media (max-width: 600px) {
  .table-wrap > .table.table--tem-movel { display: none; }

  .lista-movel {
    display: block;
    list-style: none;
    margin: 0;
    padding: 0;
  }
  .lista-movel > li + li { border-top: 1px solid var(--border-subtle); }

  .lista-movel__item {
    display: flex;
    align-items: flex-start;
    gap: 16px;
    width: 100%;
    padding: 16px;
    border: 0;
    background: transparent;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
  }
  .lista-movel__item:hover,
  .lista-movel__item:active { background: var(--surface-hover); }
  .lista-movel__item:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

  .lista-movel__icone { flex: none; display: grid; place-items: center; width: 24px; height: 24px; margin-top: 1px; }
  .lista-movel__icone svg { width: 22px; height: 22px; }
  .lista-movel__icone--accent  { color: var(--accent); }
  .lista-movel__icone--success { color: var(--success); }
  .lista-movel__icone--danger  { color: var(--danger); }
  .lista-movel__icone--warning { color: var(--warning); }
  .lista-movel__icone--info    { color: var(--info); }
  .lista-movel__icone--neutral { color: var(--text-tertiary); }

  .lista-movel__textos { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1 1 auto; }
  .lista-movel__titulo {
    font-size: 17px;
    color: var(--text-primary);
    overflow-wrap: anywhere;
  }
  .lista-movel__linha {
    font-size: 15px;
    color: var(--text-secondary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .lista-movel__linha--vazia { color: var(--text-tertiary); }

  /* "+" flutuante: o botão principal do cabeçalho da tela (Novo Cliente, Novo
     Produto, Nova Cobrança…). O texto fica para o leitor de tela (recortado,
     não `display: none`, que apagaria o nome do botão). */
  .screen:has(.lista-movel) > .screen__head .screen__actions .btn--primary {
    position: fixed;
    right: 20px;
    bottom: calc(20px + env(safe-area-inset-bottom, 0px));
    z-index: 30;
    width: 56px;
    height: 56px;
    padding: 0;
    border-radius: 50%;
    justify-content: center;
    box-shadow: 0 6px 18px rgb(0 0 0 / 0.35);
  }
  .screen:has(.lista-movel) > .screen__head .screen__actions .btn--primary svg { width: 24px; height: 24px; }
  .screen:has(.lista-movel) > .screen__head .screen__actions .btn--primary span {
    position: absolute; width: 1px; height: 1px; overflow: hidden;
    clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
  }
  /* o último registro não fica escondido atrás do botão */
  .screen:has(.lista-movel) { padding-bottom: 96px; }
}

dialog.modal.modal--pagina {
  inset: 0;
  width: 100vw;
  max-width: 100vw;
  height: 100vh;
  height: 100dvh;
  max-height: 100vh;
  max-height: 100dvh;
  margin: 0;
  border: 0;
  border-radius: 0;
  box-shadow: none;
}
dialog.modal.modal--pagina::backdrop { background: var(--surface-base); backdrop-filter: none; }
dialog.modal.modal--pagina .modal__form { height: 100%; max-height: 100%; }
dialog.modal.modal--pagina .modal__head { gap: 8px; padding: 10px 12px; }
dialog.modal.modal--pagina .modal__head .btn--icon svg { width: 22px; height: 22px; }
dialog.modal.modal--pagina .modal__title { font-size: 19px; }
dialog.modal.modal--pagina .modal__foot {
  flex-wrap: wrap;
  padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
}

/* Ficha como página: as tabelinhas de Orçamentos/Vendas (número, data, status,
   valor) passavam de 390px e cortavam o valor com rolagem lateral — medido em
   05/10/2026. No celular a data sai (está no documento, a um toque) e a tabela
   ocupa a largura exata. */
dialog.modal.modal--pagina .card--resumo-doc .table { width: 100%; }
dialog.modal.modal--pagina .card--resumo-doc .table td:nth-child(2) { display: none; }
dialog.modal.modal--pagina .card--resumo-doc .table td { white-space: nowrap; }
/* Ao abrir, o foco vai para o título (ui.js), não para a seta: sem contorno. */
dialog.modal.modal--pagina .modal__title:focus { outline: none; }

/* =============================================================================
   46. CLIENTES NO TABLET (601–860px) — CARTÃO COM RÓTULOS, ÍCONES NO TOPO
   =============================================================================
   Pedido do usuário em 05/10/2026, sobre o cartão da §26:

       Código: 00001                 [orç] [editar] [excluir]
       Nome:
       Marcus Vinicius Cintra
       CPF:
       334.771.638-85
       Contato:
       contato@msxdesign.com.br
       (11) 99551-4767

   Sai o PF/PJ do topo, a cidade e o faturado (estão na ficha, a um toque).
   CPF ou CNPJ conforme o tipo: vem de `data-rotulo` no próprio número
   (clientes.js). Abaixo de 600px vale a lista simples da §45.
   ========================================================================== */

@media (max-width: 860px) {
  .screen--clientes .table tbody tr {
    grid-template-areas:
      "codigo    acoes"
      "nome      nome"
      "documento documento"
      "contato   contato";
    row-gap: 10px;
  }
  .screen--clientes .table tbody td:is([data-col="tipo"], [data-col="cidade"], [data-col="faturado"]) { display: none; }

  .screen--clientes .table tbody td[data-col="_acoes"] { align-self: center; margin-top: -4px; margin-bottom: -4px; }

  .screen--clientes .table tbody td[data-col="codigo"]::before {
    content: "Código: ";
    font-family: var(--font-ui);
    font-size: 14px;
    color: var(--text-tertiary);
  }
  .screen--clientes .table tbody td:is([data-col="nome"], [data-col="contato"])::before,
  .screen--clientes .table tbody td[data-col="documento"] .doc::before {
    display: block;
    margin-bottom: 2px;
    font-family: var(--font-ui);
    font-size: 14px;
    font-weight: 400;
    letter-spacing: 0;
    text-transform: none;
    color: var(--text-tertiary);
  }
  .screen--clientes .table tbody td[data-col="nome"]::before    { content: "Nome:"; }
  .screen--clientes .table tbody td[data-col="contato"]::before { content: "Contato:"; }
  .screen--clientes .table tbody td[data-col="documento"]::before { content: none; }
  .screen--clientes .table tbody td[data-col="documento"] .doc::before { content: attr(data-rotulo) ":"; }
}

/* =============================================================================
   47. TABELA DE CLIENTES — SEM CIDADE E FATURADO, COM COLUNA CELULAR
   =============================================================================
   Pedido do usuário em 05/10/2026 (imagem riscada): Cidade e Faturado saem; o
   celular deixa a coluna Contato e ganha coluna própria no lugar do Faturado
   (clientes.js + chaves `clientesColuna*` do config). A última coluna antes
   dos ícones mantém os 25px até a divisória que o Faturado tinha (§24).
   No cartão do tablet (§46) o celular continua logo abaixo do email.
   ========================================================================== */

.screen--clientes .table :is(th, td):has(+ .col-acoes) { padding-right: 25px; }

@media (max-width: 860px) {
  .screen--clientes .table tbody tr {
    grid-template-areas:
      "codigo    acoes"
      "nome      nome"
      "documento documento"
      "contato   contato"
      "celular   celular";
  }
  .screen--clientes .table tbody td[data-col="celular"] {
    grid-area: celular;
    margin-top: -8px;           /* junto do email, como uma linha do Contato */
    font-size: 15px;
    color: var(--text-secondary);
  }
  .screen--clientes .table tbody td:is([data-col="celular"], [data-col="contato"]) { padding-right: 0; }
  /* sem celular: no cartão não sobra um "—" solto embaixo do email */
  .screen--clientes .table tbody td[data-col="celular"]:has(> .muted) { display: none; }
}

/* =============================================================================
   48. FICHA DO CLIENTE — ORÇAMENTOS E VENDAS EMPILHADOS, LARGURA INTEIRA
   =============================================================================
   Relatado pelo usuário em 05/10/2026 ("está meio bugado", imagem): lado a
   lado, cada cartão tem ~410px, e a tabelinha (número, data, status, valor)
   precisa de ~480 — o valor saía cortado para fora do cartão ("50.00") e a
   segunda linha perdia o valor de vez. Empilhados, cada um tem a largura da
   ficha e tudo cabe sem esconder coluna; a tabela ocupa o cartão inteiro.
   ========================================================================== */

.cols-2:has(> .card--resumo-doc) { grid-template-columns: minmax(0, 1fr); }
.card--resumo-doc .table { width: 100%; }
.card--resumo-doc .table td { white-space: nowrap; }
.card--resumo-doc .table td.num { width: 1%; }
/* Colunas nas mesmas posições nos dois cartões (Orçamentos em cima, Vendas
   embaixo): larguras fixas por coluna, só fora da página do celular — lá a
   data sai (§45) e a tabela se ajusta sozinha. */
@media (min-width: 861px) {
  .card--resumo-doc .table { table-layout: fixed; }
  .card--resumo-doc .table td:nth-child(1) { width: 32%; }
  .card--resumo-doc .table td:nth-child(2) { width: 24%; }
  .card--resumo-doc .table td:nth-child(3) { width: 24%; }
  .card--resumo-doc .table td.num          { width: 20%; }
}

/* Cantos de 7px nos cartões de Orçamentos e Vendas da ficha (pedido do
   usuário em 05/10/2026, como os blocos da §43). `overflow: hidden`: o fundo
   das linhas da tabelinha não escapa pelos cantos arredondados. */
.card--resumo-doc { border-radius: 7px; overflow: hidden; }
/* Linhas das tabelinhas de Orçamentos E Vendas 10px mais altas: 5px de
   respiro em cima e embaixo (pedido do usuário em 05/10/2026). Medido antes:
   linha de 32px, altura fixa, padding 0 — passa a 42px (box-sizing border-box). */
.card--resumo-doc .table td { height: 42px; padding-top: 5px; padding-bottom: 5px; }

/* =============================================================================
   49. PAGINAÇÃO IGUAL À DE CLIENTES EM TODAS AS LISTAS
   =============================================================================
   Pedido do usuário em 05/10/2026 (imagem Clientes × Produtos): "deixar a
   parte do 50 páginas igual a parte do cliente em todas as páginas".

   Antes (B-S1.94/97) o ui.js MEDIA a coluna de ícones de cada tela — em
   Produtos (2 ícones) e Orçamentos o resultado era diferente do de Clientes.
   Agora as medidas são FIXAS, as de Clientes, que tem a coluna de ícones de
   largura constante (3 botões de 36px + recuos da §21):
       seletor ── 19,14 ── divisória ── 19,14 ── ‹ 1/1 › ── 19,14 ── borda
   (`paginacaoSobAcoes` desligado no config: nada mais é medido.)
   E o resto da paginação de Clientes vale em toda lista: respiro 12/12, sem
   "Mostrando…", "50 / Página" em itálico cinza 17px (§35, §39).
   ========================================================================== */

.card > .pager {
  padding-top: 12px;
  padding-bottom: 12px;
  padding-right: 19.14px;
}
.card > .pager > span:first-child { display: none; }
.card > .pager select {
  margin-right: calc(38.28px - var(--space-2, 8px));   /* o gap da paginação já dá 8 */
  color: var(--text-disabled);
  font-style: italic;
  font-size: 17px;
}
.card > .pager select option {
  color: var(--text-primary);
  font-style: normal;
}

/* Tablet e celular (até 860px), pedido do usuário em 05/10/2026: "50 / Página"
   à ESQUERDA e ‹ 1/1 › à direita. O seletor vem antes do espaçador
   (`order: -1`); as medidas da §49 (que alinham com a coluna de ícones da
   tabela, que aqui não existe) dão lugar a recuo igual dos dois lados. */
@media (max-width: 860px) {
  .card > .pager { padding-right: var(--space-4, 16px); }
  .card > .pager select { order: -1; margin-right: 0; }
}

/* Clientes no celular e no tablet: email e celular na MESMA cor do nome
   (pedido do usuário em 05/10/2026). Eram cinza (`--text-secondary`). O aviso
   "não informado" continua apagado — é ausência, não dado. */
.screen--clientes .lista-movel__linha:not(.lista-movel__linha--vazia) { color: var(--text-primary); }
@media (max-width: 860px) {
  .screen--clientes .table tbody td:is([data-col="contato"], [data-col="celular"]),
  .screen--clientes .table tbody td:is([data-col="contato"], [data-col="celular"]) * { color: var(--text-primary); }
}

/* Clientes no celular e no tablet: email e celular no MESMO tamanho do nome e
   do CPF — 17px (pedido do usuário em 05/10/2026; medido antes: 15px, contra
   17px do nome e do CPF). A reticência do email longo (§26) continua. */
.screen--clientes .lista-movel__linha { font-size: 17px; }
@media (max-width: 860px) {
  .screen--clientes .table tbody td:is([data-col="contato"], [data-col="celular"]),
  .screen--clientes .table tbody td:is([data-col="contato"], [data-col="celular"]) * { font-size: 17px; }
}

/* Ficha do cliente: o email continua link (abre o programa de email), mas na
   cor dos outros dados e sem efeito ao passar o mouse (pedido do usuário em
   05/10/2026). O contorno de foco do teclado fica — sem ele, quem navega por
   Tab não vê onde está. */
.dl dd a[href^="mailto:"],
.dl dd a[href^="mailto:"]:hover,
.dl dd a[href^="mailto:"]:active,
.dl dd a[href^="mailto:"]:visited {
  color: inherit;
  text-decoration: none;
  background: none;
}

/* Filtros da barra (ex.: "Pessoa Física e Jurídica"): 10px entre o fim do
   texto e a setinha (pedido do usuário em 05/10/2026). Medido pela tinta: 8,7px com 24px de recuo, 12,7 com 28 → 25,3px dá 10,0 —
   padding direito de 24px, com a seta desenhada a 14–19px da borda. */
/* 10 -> 15px (05/10/2026, em seguida: "se mudou aumentar para 15px"): +5px de recuo. */
.toolbar select { padding-right: 30.3px; }

/* Lista do celular: nome em negrito (pedido do usuário em 05/10/2026), o mesmo
   peso do nome no cartão do tablet (600). Vale para o título de toda lista. */
.lista-movel__titulo { font-weight: 600; }
/* =============================================================================
   50. NÚMERO DO ORÇAMENTO NA FONTE DO CÓDIGO DO CLIENTE
   =============================================================================
   Pedido do usuário em 05/10/2026: "o número do orçamento também com a mesma
   fonte dos demais, igual o código do cliente" (§37: Nunito Sans 16px,
   algarismos de largura igual, peso normal). Era JetBrains Mono em negrito.
   Na lista de Orçamentos e nas tabelinhas da ficha do cliente (Orçamentos e,
   para os dois cartões não ficarem diferentes, Vendas).
   ========================================================================== */

.screen--orcamentos .table td[data-col="numero"] .mono,
.card--resumo-doc .table td:first-child .mono {
  font-family: var(--font-ui);
  font-size: 16px;
  font-weight: 400;
  font-variant-numeric: tabular-nums;
}

/* =============================================================================
   51. PRODUTOS — COLUNAS CATEGORIA E TIPO (UNIDADE DE VENDA)
   =============================================================================
   Pedido do usuário em 05/10/2026: duas colunas na lista, Categoria e a
   unidade de venda com o título "Tipo" (produtos.js). No cartão do tablet
   (§42) as duas ganham uma linha própria, com rótulo como o do Preço.
   ========================================================================== */

@media (max-width: 860px) {
  .screen--produtos .table tbody tr {
    grid-template-areas:
      "sku        situacao"
      "descricao  descricao"
      "categoria  unidade"
      "precoVenda acoes";
  }
  .screen--produtos .table tbody td[data-col="categoria"] { grid-area: categoria; }
  .screen--produtos .table tbody td[data-col="unidade"]   { grid-area: unidade; }
  .screen--produtos .table tbody td:is([data-col="categoria"], [data-col="unidade"])::before {
    content: attr(data-label);
    display: block;
    margin-bottom: 2px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-tertiary);
  }
}

/* =============================================================================
   52. FOTO DO PRODUTO
   =============================================================================
   Pedido do usuário em 05/10/2026: produto com foto no cadastro, na lista,
   no celular, nos itens do orçamento e no PDF/link; sem foto, "um fundo mais
   escuro e uma máquina fotográfica no meio". Quadrada, recortada no centro
   (escolha do usuário). Marcação: UI.fotoProduto (ui.js).
   ========================================================================== */

.foto-produto {
  flex: none;
  display: inline-grid;
  place-items: center;
  overflow: hidden;
  border-radius: 6px;
  background: var(--surface-sunken);
  border: 1px solid var(--border-subtle);
  vertical-align: middle;
}
.foto-produto img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* sem foto: mais escuro que o cartão, câmera no meio em cinza */
.foto-produto--vazia { background: rgb(0 0 0 / 0.35); color: var(--text-tertiary); }
.foto-produto--vazia svg { width: 46%; height: 46%; stroke-width: 1.5; }
:root[data-theme="light"] .foto-produto--vazia { background: var(--neutral-200, #e2e8f0); color: var(--neutral-500, #64748b); }

/* foto + texto lado a lado (lista de Produtos, itens do orçamento) */
.produto-com-foto { display: flex; align-items: center; gap: 12px; min-width: 0; }
.produto-com-foto > div { min-width: 0; }

/* lista do celular: a miniatura ocupa o lugar do ícone */
.lista-movel__icone.lista-movel__icone--foto { width: auto; height: auto; margin-top: 0; }

/* cadastro: prévia grande + botões */
.foto-form { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
.foto-form__previa .foto-produto { border-radius: 10px; }
.foto-form__acoes { display: flex; flex-direction: column; gap: 8px; min-width: 0; }

/* Com a miniatura no Nome, numa tela de ~1440px a soma das colunas passa da
   largura e o preço era cortado ("5.000,00 /…"). O preço nunca corta: sem
   teto de largura nem reticências; quem cede espaço é o nome. */
@media (min-width: 861px) {
  .screen--produtos .table td[data-col="precoVenda"] { max-width: none; overflow: visible; text-overflow: clip; white-space: nowrap; }
}

/* Foto em coluna própria, a primeira (pedido do usuário em 05/10/2026). No
   cartão do tablet ela abre o cartão, ao lado da situação. */
.screen--produtos .table td[data-col="foto"] { width: 1%; }
@media (max-width: 860px) {
  .screen--produtos .table tbody tr {
    grid-template-areas:
      "foto       situacao"
      "sku        sku"
      "descricao  descricao"
      "categoria  unidade"
      "precoVenda acoes";
  }
  .screen--produtos .table tbody td[data-col="foto"] { grid-area: foto; width: auto; }
}

/* Coluna Cor / Estampa (pedido do usuário em 05/10/2026). No cartão do tablet
   ela entra na linha da categoria; o Tipo desce para junto do preço. */
@media (max-width: 860px) {
  .screen--produtos .table tbody tr {
    grid-template-areas:
      "foto       situacao"
      "sku        sku"
      "descricao  descricao"
      "categoria  cor"
      "unidade    unidade"
      "precoVenda acoes";
  }
  .screen--produtos .table tbody td[data-col="cor"] { grid-area: cor; }
  .screen--produtos .table tbody td[data-col="cor"]::before {
    content: attr(data-label);
    display: block;
    margin-bottom: 2px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-tertiary);
  }
}

/* Com Foto e Cor / Estampa, a tabela passa da largura numa tela de ~1440px e a
   coluna fixa de ícones cobria o preço. O NOME pode quebrar em duas linhas
   (é o texto mais longo e o que menos perde quebrando); o resto não quebra. */
@media (min-width: 861px) {
  .screen--produtos .table td[data-col="descricao"] { white-space: normal; max-width: none; min-width: 220px; }
  .screen--produtos .table td[data-col="descricao"] .strong { overflow-wrap: anywhere; }
}

/* =============================================================================
   53. PRODUTOS — CÓDIGO COLADO NA FOTO
   =============================================================================
   Pedido do usuário em 05/10/2026: "era só pra trazer o código produto... as
   demais ficariam com o mesmo espaçamento". Foto e Código têm `semFolga`
   (produtos.js): ficam com a largura exata, e a folga igual (ui.js,
   dividirFolga) é repartida só entre as outras colunas. Nada a fazer aqui —
   a seção fica como registro da decisão (a versão anterior, que juntava
   TODAS as colunas à esquerda, foi desfeita).
   ========================================================================== */

/* Mais espaço entre Código Produto e Nome (pedido do usuário em 05/10/2026).
   Medido antes: 41px do fim do código mais longo ao nome; +25px no recuo
   direito da coluna. Vale no <th> também: a divisão de folga (ui.js) soma o
   recuo do cabeçalho na largura da coluna. */
@media (min-width: 861px) {
  /* +50px (05/10/2026, em seguida: "pode aumentar em 50px"): 37 -> 87. */
  .screen--produtos .table :is(th.col-sem-folga, td[data-col="sku"]) { padding-right: 37px; }
  .screen--produtos .table th.col-sem-folga:first-child { padding-right: 12px; }
}

/* Coluna Marca (pedido do usuário em 05/10/2026), depois do Nome. No cartão
   do tablet: marca e categoria lado a lado; cor e tipo na linha seguinte. */
@media (max-width: 860px) {
  .screen--produtos .table tbody tr {
    grid-template-areas:
      "foto       situacao"
      "sku        sku"
      "descricao  descricao"
      "categoria  marca"        /* Fabricante depois da Categoria (05/10/2026) */
      "cor        unidade"
      "precoVenda acoes";
  }
  .screen--produtos .table tbody td[data-col="marca"] { grid-area: marca; }
  .screen--produtos .table tbody td[data-col="marca"]::before {
    content: attr(data-label);
    display: block;
    margin-bottom: 2px;
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--text-tertiary);
  }
}

/* Os +50px entre Código e Nome (B-S1.116) só a partir de 1600px de largura
   (a tela do usuário tem ~1650). Com a coluna Fabricante, numa tela de 1440
   eles empurravam o Preço para baixo dos ícones — medido em 05/10/2026. */
@media (min-width: 1600px) {
  .screen--produtos .table :is(th.col-sem-folga, td[data-col="sku"]) { padding-right: 87px; }
  .screen--produtos .table th.col-sem-folga:first-child { padding-right: 12px; }
}
/* Cadastro de produto: Foto e Identificação lado a lado (pedido do usuário em
   05/10/2026, "fazer 2 colunas"). Os dois blocos com a mesma altura; no
   celular voltam a empilhar. */
.form-duas-colunas {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
  gap: var(--space-4, 16px);
  align-items: stretch;
}
.form-duas-colunas > .bloco-form { display: flex; flex-direction: column; min-width: 0; }
.form-duas-colunas > .bloco-form > .bloco-form__corpo { flex: 1 1 auto; }
.form-duas-colunas .foto-form { height: 100%; }
@media (max-width: 700px) {
  .form-duas-colunas { grid-template-columns: minmax(0, 1fr); }
}

/* Confirmações (ex.: "Excluir … do Catálogo?"): a frase de aviso em itálico e a
   10px da pergunta (pedido do usuário em 05/10/2026). Medido pela TINTA (da
   descida das letras da pergunta ao topo das maiúsculas do aviso): a entrelinha
   soma ~8px, então 2px de margem dão os 10px que se vê (6px davam 14). */
.confirmar__detalhe { margin-top: 2px !important; font-style: italic; }

/* Foto do cadastro: botões à direita da foto, um embaixo do outro, e a foto
   maior (150px) — pedido do usuário em 05/10/2026. Sem quebrar a linha. */
.form-duas-colunas .foto-form { flex-wrap: nowrap; gap: 16px; align-items: center; }
.foto-form__botoes { display: flex; flex-direction: column; align-items: stretch; gap: 8px; }
.foto-form__botoes .btn { justify-content: flex-start; }

.foto-form__acoes { flex: 0 0 auto; }
.foto-form__botoes .btn { white-space: nowrap; }

/* Foto 140px; botões alinhados pelo topo da foto (05/10/2026). */
.form-duas-colunas .foto-form { align-items: flex-start; }
.foto-form__acoes { align-self: flex-start; }

/* Botões da foto (pedido do usuário em 05/10/2026): "Trocar Foto" 1px abaixo e
   5px a mais até o "Remover" (vão 8 -> 13px); ícones da câmera e da lixeira
   maiores (14 -> 18px). */
.foto-form__botoes .btn:first-child { margin-top: 1px; margin-bottom: 5px; }
.foto-form__botoes .btn svg { width: 18px; height: 18px; }

/* Texto "Remover" 1px abaixo — só o texto; ícone e caixa ficam (05/10/2026). */
.foto-form__botoes .btn:last-child span { position: relative; top: 1px; }

/* Botões da foto até a borda direita do bloco (05/10/2026). */
.foto-form__acoes { flex: 1 1 auto; min-width: 0; }
.foto-form__botoes { width: 100%; }
.foto-form__botoes .btn { width: 100%; }

/* =============================================================================
   54. PRODUTOS NO TABLET (601–860px) — CARTÃO REDESENHADO
   =============================================================================
   Relatado pelo usuário em 05/10/2026 ("ficou horrível na versão de tablet"):
   tudo empilhado numa coluna, miniatura de 40px perdida no canto e metade do
   cartão vazia. Agora:

       [ FOTO ]  Nome do produto (destaque)            [⧉ ✎ 🗑]
       [ 96px ]  código
       [      ]  CATEGORIA      FABRICANTE
       [      ]  Cortinas       New York
       [      ]  COR / ESTAMPA  TIPO              PREÇO
       [      ]  Azul           un             4.000,00
   ========================================================================== */

@media (min-width: 601px) and (max-width: 860px) {
  .screen--produtos .table tbody tr {
    grid-template-columns: 96px minmax(0, 1fr) minmax(0, 1fr) auto;
    grid-template-areas:
      "foto descricao descricao acoes"
      "foto sku       sku       acoes"
      "foto categoria marca     ."
      "foto cor       unidade   precoVenda";
    column-gap: 16px;
    row-gap: 6px;
    align-items: start;
  }
  .screen--produtos .table tbody td[data-col="foto"] { grid-area: foto; align-self: stretch; }
  .screen--produtos .table tbody td[data-col="foto"] .foto-produto {
    width: 96px !important; height: 96px !important; border-radius: 8px;
  }
  .screen--produtos .table tbody td[data-col="descricao"] { grid-area: descricao; font-size: 18px; }
  .screen--produtos .table tbody td[data-col="sku"] { grid-area: sku; color: var(--text-tertiary); margin-top: -4px; margin-bottom: 6px; }
  .screen--produtos .table tbody td[data-col="sku"] .mono { font-size: 15px; }
  .screen--produtos .table tbody td[data-col="_acoes"] { grid-area: acoes; align-self: start; margin-top: -6px; }
  .screen--produtos .table tbody td[data-col="precoVenda"] {
    grid-area: precoVenda; justify-self: end; align-self: end; text-align: right;
  }
  .screen--produtos .table tbody td[data-col="precoVenda"] .strong { font-size: 18px; }
}

/* Código no cartão do tablet igual ao de Clientes (§46): "Código: 6541684" —
   rótulo 14px cinza, número na cor do texto, Nunito 16px (05/10/2026). */
@media (min-width: 601px) and (max-width: 860px) {
  .screen--produtos .table tbody td[data-col="sku"] { color: var(--text-primary); }
  .screen--produtos .table tbody td[data-col="sku"] .mono { font-family: var(--font-ui); font-size: 16px; }
  .screen--produtos .table tbody td[data-col="sku"]::before {
    content: "Código: ";
    font-family: var(--font-ui);
    font-size: 14px;
    color: var(--text-tertiary);
  }
}

/* §55 Ficha do cliente em blocos (opção C, escolhida em 05/10/2026).
   Montada por clientes.js > fichaBlocos() com `clientesFicha.layout: 'blocos'`.
   4 colunas no desktop, 2 no tablet, 1 no celular. */
.modal:has(.ficha-blocos) .modal__title {
  position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}
.modal:has(.ficha-blocos) .modal__head { justify-content: flex-end; }
.modal--pagina:has(.ficha-blocos) .modal__head { justify-content: flex-start; }
.ficha-blocos { display: flex; flex-direction: column; gap: 18px; }
.fb__topo { margin-top: -8px; }
.fb__linha { display: flex; gap: 10px; align-items: center; font-size: 14px; color: var(--text-tertiary); margin-bottom: 4px; }
.fb__linha b { color: var(--accent); font-weight: 700; }
.fb__nome { font: 500 30px/1.15 var(--font-display); letter-spacing: -.01em; color: var(--text-primary); overflow-wrap: anywhere; }
.fb__razao { font-size: 14px; color: var(--text-secondary); margin-top: 4px; }
.fb__grade { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; }
.fb__t { background: var(--surface-raised); border: 1px solid var(--border-subtle); border-radius: 12px; padding: 14px 16px; min-width: 0; }
.fb__t--2, .fb__t--fat { grid-column: span 2; }
.fb__t--4 { grid-column: 1 / -1; }
.fb__t--fat {
  background: linear-gradient(135deg, var(--accent-surface), var(--surface-raised) 70%);
  border-color: color-mix(in srgb, var(--accent-border) 50%, var(--border-subtle));
}
.fb__rotulo {
  display: flex; align-items: center; gap: 6px; margin: 0 0 6px;
  font: 700 12px var(--font-ui); letter-spacing: .06em; text-transform: uppercase; color: var(--text-tertiary);
}
.fb__rotulo svg, .fb__mapa svg { width: 14px; height: 14px; flex: none; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }
.fb__v { font-size: 16px; color: var(--text-primary); overflow-wrap: anywhere; }
.fb__v a { color: inherit; text-decoration: none; }
.fb__v a:hover { color: inherit; text-decoration: none; }   /* e-mail sem efeito de hover (05/10/2026) */
.fb__fat { font: 600 28px var(--font-display); color: var(--text-primary); }
.fb__sub { font-size: 13px; color: var(--text-secondary); margin-top: 2px; }
.fb__acoes { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
.fb__acao, .fb__mapa {
  font-size: 13px; font-weight: 700; color: var(--accent); text-decoration: none; border-radius: 6px;
}
.fb__acao { background: var(--accent-surface); padding: 4px 10px; }
.fb__acao:hover, .fb__mapa:hover { color: var(--accent-hover); }
.fb__acao:focus-visible, .fb__mapa:focus-visible, .fb__item:focus-visible, .fb__seg-btn:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}
.fb__end { display: flex; justify-content: space-between; align-items: center; gap: 16px; }
.fb__mapa { flex: none; display: inline-flex; align-items: center; gap: 6px; }
.fb__hist-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 10px; }
.fb__hist-titulo { margin: 0; font: 500 18px var(--font-display); color: var(--text-primary); }
.fb__seg { display: inline-flex; background: var(--surface-sunken); border-radius: 8px; padding: 3px; }
.fb__seg-btn {
  border: 0; background: transparent; cursor: pointer; padding: 5px 12px; border-radius: 6px;
  font: 700 13px var(--font-ui); color: var(--text-tertiary);
}
.fb__seg-btn[aria-pressed="true"] { background: var(--surface-overlay); color: var(--text-primary); box-shadow: var(--shadow-sm); }
.fb__lista { display: flex; flex-direction: column; gap: 6px; }
.fb__vazio {
  border: 1px dashed var(--border-default); border-radius: 12px; padding: 22px; text-align: center;
  color: var(--text-tertiary); font-size: 14px;
}
.fb__vazio b { display: block; color: var(--text-secondary); font-size: 15px; margin-bottom: 2px; }
.fb__item {
  display: grid; grid-template-columns: 90px minmax(0, 1fr) 90px auto minmax(110px, auto); align-items: center; gap: 12px;
  width: 100%; text-align: left; cursor: pointer; padding: 10px 14px; border-radius: 10px;
  background: var(--surface-raised); border: 1px solid var(--border-subtle); color: var(--text-primary); font: 15px var(--font-ui);
}
.fb__item:hover { background: var(--surface-hover); }
.fb__item-tipo { font-size: 12px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; color: var(--text-tertiary); }
.fb__item-data { font-size: 14px; color: var(--text-secondary); }
.fb__item .fb__item-num { font-family: var(--font-ui); font-weight: 700; }
.fb__fone { white-space: nowrap; }
.fb__item-valor { text-align: right; font-weight: 700; }
@media (max-width: 860px) {
  .fb__grade { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .fb__t--fat { grid-column: 1 / -1; }
}
@media (max-width: 600px) {
  .fb__topo { margin-top: 0; }
  .fb__nome { font-size: 26px; }
  .fb__grade { grid-template-columns: minmax(0, 1fr); gap: 10px; }
  .fb__t--2, .fb__t--4, .fb__t--fat { grid-column: auto; }
  .fb__end { flex-direction: column; align-items: flex-start; gap: 8px; }
  .fb__hist-head { flex-direction: column; align-items: flex-start; }
  .fb__item { grid-template-columns: minmax(0, 1fr) auto; grid-template-areas: "tipo valor" "num status" "data status"; row-gap: 2px; }
  .fb__item-tipo { grid-area: tipo; } .fb__item-num { grid-area: num; } .fb__item-data { grid-area: data; }
  .fb__item-status { grid-area: status; justify-self: end; } .fb__item-valor { grid-area: valor; }
}
/* §56 Ficha em blocos: sem a faixa vazia do topo — o X desce para a linha
   "Cliente 00002 · Pessoa Física" (pedido de 05/10/2026). Só no diálogo;
   como página (celular) o topo com a seta continua igual. */
.modal:not(.modal--pagina):has(.ficha-blocos) .modal__form { position: relative; }
.modal:not(.modal--pagina):has(.ficha-blocos) .modal__head {
  position: absolute; top: 12.5px; right: 8px; z-index: 1;   /* centro do X = centro da linha; ícone na margem de 16px */ padding: 0; border: 0; background: none; min-height: 0;
}
.modal:not(.modal--pagina):has(.ficha-blocos) .fb__topo { margin-top: 0; }
.modal:not(.modal--pagina):has(.ficha-blocos) .fb__linha { padding-right: 48px; }
/* §57 Ficha em blocos: rótulo do E-mail dentro do bloco do CPF/CNPJ (05/10/2026). */
.fb__rotulo--seguinte { margin-top: 14px; }
/* Desktop: 1ª linha em 3 colunas (Faturado mais estreito) para o e-mail caber
   inteiro no bloco do CPF; Endereço e Observações em largura total. */
@media (min-width: 861px) {
  .fb__grade { grid-template-columns: minmax(0, 1.406fr) minmax(0, 1.239fr) minmax(0, 1fr); }  /* 05/10/2026: Faturado 309 · CPF 273 (-20px) · Telefone 220 */
  .fb__t--fat { grid-column: auto; }
}
/* §58 Ficha em blocos: WhatsApp e Ligar no canto inferior direito do bloco do
   telefone (05/10/2026). */
.fb__t:has(.fb__acoes) { display: flex; flex-direction: column; }
.fb__t .fb__acoes { margin-top: auto; padding-top: 10px; justify-content: flex-end; }
/* §59 Ficha em blocos (05/10/2026): WhatsApp/Ligar sem efeito de hover;
   "Abrir no Mapa" na linha do título ENDEREÇO. */
.fb__acao:hover { color: var(--accent); background: var(--accent-surface); text-decoration: none; }
.fb__rotulo-linha { display: flex; align-items: center; gap: 12px; margin-bottom: 6px; }
.fb__rotulo-linha .fb__rotulo { margin: 0; }
.fb__rotulo-linha .fb__mapa { font-size: 12px; }
/* §60 Ficha em blocos (05/10/2026): espaço rótulo → valor medido na TINTA
   (pé das maiúsculas do rótulo ao topo do texto): CPF 5px, E-mail 3px,
   Celular 5px. Antes 16 / 15 / 16px. +4px em 05/10/2026: 9 / 7 / 9px. */
.fb__grade > .fb__t:nth-child(2) > .fb__rotulo,
.fb__grade > .fb__t:nth-child(3) > .fb__rotulo { margin-bottom: -1px; }
.fb__grade > .fb__t:nth-child(2) > .fb__rotulo--seguinte { margin-bottom: -2px; }
/* §61 Ficha em blocos (05/10/2026): "Abrir no Mapa" — 3px de tinta entre o
   ícone e o texto (era 6,17px) e sem efeito de hover. */
.fb__rotulo-linha .fb__mapa { gap: 2.83px; }
.fb__mapa:hover { color: var(--accent); text-decoration: none; }
/* §62 Ficha em blocos (05/10/2026): "Abrir no Mapa" (ícone + texto) sobe 1px;
   texto do endereço sobe 3px (encosta 3px mais no título ENDEREÇO). */
.fb__rotulo-linha .fb__mapa { position: relative; top: -1px; }
.fb__rotulo-linha + .fb__v { margin-top: -3px; }
/* §63 Ficha em blocos (05/10/2026): histórico vazio só "Nenhum Registro", em itálico. */
.fb__vazio { font-style: italic; color: var(--text-secondary); font-size: 15px; }
/* E-mail na ficha: quebra só no @ (<wbr>); sem quebra no meio do domínio. */
.fb__email { overflow-wrap: normal; }
.fb__email a { overflow-wrap: anywhere; }

/* §64 Ficha em blocos (05/10/2026): "0 Orçamentos · 0 Vendas" desce 3px (margem 2 → 5px). */
.fb__sub { margin-top: 5px; }

/* §65 Ficha em blocos (05/10/2026): selo de status do histórico com padding 5px em cima e embaixo. */
.fb__item .badge { height: auto; padding-top: 5px; padding-bottom: 5px; line-height: 1; }

/* §66 Lista de Clientes em cartões (opção B, escolhida em 05/10/2026).
   ui.js desenha `.lista-cartoes` ao lado da tabela (op.cartoes); aqui ela
   substitui a tabela e a lista do celular até 860px. 2 por linha no tablet,
   1 no celular. Montagem: clientes.js > cartaoCliente(). */
.lista-cartoes { display: none; }
@media (max-width: 860px) {
  .table-wrap > .table.table--tem-cartoes,
  .table-wrap:has(> .lista-cartoes) > .lista-movel { display: none; }
  /* !important: as §§30/40 vestem o .table-wrap de card com seletores mais fortes. */
  .table-wrap:has(> .lista-cartoes) { background: none !important; border: 0 !important; box-shadow: none !important; overflow: visible; }
  .lista-cartoes { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; }
}
@media (max-width: 600px) {
  .lista-cartoes { grid-template-columns: minmax(0, 1fr); gap: 10px; }
}
.cc {
  display: flex; flex-direction: column; min-width: 0; cursor: pointer;
  background: var(--surface-raised); border: 1px solid var(--border-subtle); border-radius: 12px;
}
.cc__topo { padding: 14px 16px 10px; }
.cc__linha { font-size: 13px; color: var(--text-tertiary); }
.cc__linha b { color: var(--accent); font-weight: 700; }
.cc__nome {
  display: block; width: 100%; margin-top: 2px; padding: 0; border: 0; background: none; cursor: pointer; text-align: left;
  font: 500 20px/1.25 var(--font-display); color: var(--text-primary); overflow-wrap: anywhere;
}
.cc__nome:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.cc__dados { flex: 1; display: grid; gap: 8px; padding: 0 16px 12px; }
.cc__info { display: flex; flex-direction: column; min-width: 0; }
.cc__rotulo {
  display: flex; align-items: center; gap: 6px;
  font: 700 11px var(--font-ui); letter-spacing: .06em; text-transform: uppercase; color: var(--text-tertiary);
}
.cc__rotulo svg, .cc__zap svg {
  width: 13px; height: 13px; flex: none; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round;
}
.cc__valor { font-size: 15px; color: var(--text-primary); overflow-wrap: anywhere; }
.cc__valor--vazio { color: var(--text-tertiary); }
.cc__rodape { display: flex; align-items: center; gap: 4px; padding: 8px 10px 8px 12px; border-top: 1px solid var(--border-subtle); }
.cc__esp { flex: 1; }
.cc__zap {
  display: inline-flex; align-items: center; gap: 6px; height: 30px; padding: 0 10px; border-radius: 6px;
  background: var(--accent-surface); color: var(--accent); font-size: 13px; font-weight: 700; text-decoration: none;
}
.cc__zap svg { width: 14px; height: 14px; }
.cc__zap:hover { color: var(--accent); text-decoration: none; }
.cc__zap:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }.cc__rodape .btn--icon { width: 34px; height: 34px; }
.cc__rodape .btn--icon svg { width: 18px; height: 18px; }