/* ==========================================================================
   Nuzap Painel — Componentes
   Depende de nuzap-tokens.css. Zero hex literal, zero !important.
   Prefixo .nz- para conviver com o Bootstrap legado sem colisão.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Base
   -------------------------------------------------------------------------- */
.nz-app,
.nz-app * { box-sizing: border-box; }

.nz-app {
  margin: 0;
  background: var(--nz-bg);
  color: var(--nz-text);
  font-family: var(--nz-font-sans);
  font-size: var(--nz-text-md);
  line-height: var(--nz-leading-normal);
  -webkit-font-smoothing: antialiased;
}

/* Foco visível e consistente em tudo que recebe teclado.
   Nunca remover: é o único caminho de navegação sem mouse. */
.nz-app :focus-visible {
  outline: 2px solid var(--nz-border-focus);
  outline-offset: 2px;
  border-radius: var(--nz-radius-sm);
}

.nz-app :where(button, [role="button"], a, label[for], summary) { cursor: pointer; }

/* Qualquer componente que declare `display:` vence o [hidden] da folha do
   navegador — o elemento "escondido" continua ocupando linha e gap. Esta regra
   tem especificidade suficiente para restaurar o esperado sem !important. */
.nz-app [hidden] { display: none; }

/* Números que se alinham em coluna nunca dançam entre linhas */
.nz-num { font-variant-numeric: tabular-nums; }
.nz-mono {
  font-family: var(--nz-font-mono);
  font-size: 0.9375em;
  font-variant-ligatures: none;
}

.nz-sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* Pular para o conteúdo — primeiro tab da página */
.nz-skip-link {
  position: absolute; left: var(--nz-space-2); top: -100px;
  z-index: var(--nz-z-toast);
  padding: var(--nz-space-2) var(--nz-space-4);
  background: var(--nz-primary); color: var(--nz-primary-fg);
  border-radius: var(--nz-radius-md); font-weight: var(--nz-weight-semibold);
  transition: top var(--nz-duration-fast) var(--nz-ease-out);
}
.nz-skip-link:focus { top: var(--nz-space-2); }

@media (prefers-reduced-motion: reduce) {
  .nz-app *,
  .nz-app *::before,
  .nz-app *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* --------------------------------------------------------------------------
   Shell: sidebar + topbar + conteúdo
   -------------------------------------------------------------------------- */
.nz-shell {
  display: grid;
  grid-template-columns: var(--nz-sidebar-w) minmax(0, 1fr);
  min-height: 100dvh;
  transition: grid-template-columns var(--nz-duration-base) var(--nz-ease-out);
}
.nz-shell[data-nav="collapsed"] { grid-template-columns: var(--nz-sidebar-w-collapsed) minmax(0, 1fr); }

/* ---- Sidebar ---- */
.nz-sidebar {
  grid-row: 1 / -1;
  display: flex; flex-direction: column;
  background: var(--nz-surface);
  border-right: 1px solid var(--nz-border);
  position: sticky; top: 0; height: 100dvh;
  z-index: var(--nz-z-sidebar);
  overflow: hidden;
}

.nz-store-switcher {
  display: flex; align-items: center; gap: var(--nz-space-3);
  width: 100%; min-height: var(--nz-tap-min);
  padding: var(--nz-space-3);
  background: none; border: 0;
  color: var(--nz-text); text-align: left;
  transition: background var(--nz-duration-fast) var(--nz-ease-out);
}
.nz-store-switcher:hover { background: var(--nz-surface-hover); }

.nz-store-mark {
  flex: none; display: grid; place-items: center;
  width: 32px; height: 32px; border-radius: var(--nz-radius-md);
  background: var(--nz-primary); color: var(--nz-primary-fg);
  font-weight: var(--nz-weight-bold); font-size: var(--nz-text-sm);
}
.nz-store-meta { min-width: 0; flex: 1; }
.nz-store-name {
  display: block; font-weight: var(--nz-weight-semibold); font-size: var(--nz-text-md);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nz-store-plan {
  display: block; font-size: var(--nz-text-xs); color: var(--nz-text-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

.nz-nav { flex: 1; overflow-y: auto; padding: var(--nz-space-3) var(--nz-space-2); }
.nz-nav-group + .nz-nav-group { margin-top: var(--nz-space-5); }

.nz-nav-label {
  padding: 0 var(--nz-space-3) var(--nz-space-2);
  font-size: var(--nz-text-2xs); font-weight: var(--nz-weight-semibold);
  letter-spacing: var(--nz-tracking-caps); text-transform: uppercase;
  color: var(--nz-text-muted);
}

.nz-nav-link {
  position: relative;
  display: flex; align-items: center; gap: var(--nz-space-3);
  min-height: var(--nz-tap-min);
  padding: var(--nz-space-2) var(--nz-space-3);
  border-radius: var(--nz-radius-md);
  color: var(--nz-text-secondary); text-decoration: none;
  font-size: var(--nz-text-md); font-weight: var(--nz-weight-medium);
  transition: background var(--nz-duration-fast) var(--nz-ease-out),
              color var(--nz-duration-fast) var(--nz-ease-out);
}
.nz-nav-link:hover { background: var(--nz-surface-hover); color: var(--nz-text); }

/* Estado ativo: cor + peso + barra. Nunca só cor. */
.nz-nav-link[aria-current="page"] {
  background: var(--nz-primary-subtle);
  color: var(--nz-primary-subtle-fg);
  font-weight: var(--nz-weight-semibold);
}
.nz-nav-link[aria-current="page"]::before {
  content: ""; position: absolute; left: 0;
  top: 50%; translate: 0 -50%;
  width: 3px; height: 20px; border-radius: 0 3px 3px 0;
  background: var(--nz-primary);
}
.nz-nav-link svg { flex: none; width: 18px; height: 18px; }
.nz-nav-text { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.nz-nav-count {
  flex: none; padding: 1px var(--nz-space-2);
  border-radius: var(--nz-radius-pill);
  background: var(--nz-neutral-subtle); color: var(--nz-neutral-subtle-fg);
  font-size: var(--nz-text-2xs); font-weight: var(--nz-weight-semibold);
  font-variant-numeric: tabular-nums;
}
.nz-nav-count[data-tone="attention"] { background: var(--nz-warning-subtle); color: var(--nz-warning-subtle-fg); }

.nz-sidebar-footer {
  padding: var(--nz-space-2);
  border-top: 1px solid var(--nz-border);
  display: grid; gap: var(--nz-space-1);
}

/* Rail colapsado: só ícones, rótulo vira tooltip nativo */
.nz-shell[data-nav="collapsed"] .nz-nav-label,
.nz-shell[data-nav="collapsed"] .nz-nav-text,
.nz-shell[data-nav="collapsed"] .nz-nav-count,
.nz-shell[data-nav="collapsed"] .nz-store-meta,
.nz-shell[data-nav="collapsed"] .nz-user-meta { display: none; }
.nz-shell[data-nav="collapsed"] .nz-nav-link,
.nz-shell[data-nav="collapsed"] .nz-store-switcher { justify-content: center; }

/* ---- Topbar ---- */
.nz-topbar {
  position: sticky; top: 0; z-index: var(--nz-z-topbar);
  display: flex; align-items: center; gap: var(--nz-space-4);
  height: var(--nz-topbar-h); padding: 0 var(--nz-space-6);
  background: color-mix(in srgb, var(--nz-surface) 88%, transparent);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--nz-border);
}

/* Breadcrumb: responde "onde estou" — o painel atual não responde */
.nz-breadcrumb { display: flex; align-items: center; gap: var(--nz-space-2); min-width: 0; }
.nz-breadcrumb ol { display: flex; align-items: center; gap: var(--nz-space-2); margin: 0; padding: 0; list-style: none; }
.nz-breadcrumb li { display: flex; align-items: center; gap: var(--nz-space-2); font-size: var(--nz-text-sm); }
.nz-breadcrumb a { color: var(--nz-text-muted); text-decoration: none; }
.nz-breadcrumb a:hover { color: var(--nz-text); text-decoration: underline; }
.nz-breadcrumb [aria-current="page"] { color: var(--nz-text); font-weight: var(--nz-weight-medium); }
.nz-breadcrumb-sep { color: var(--nz-text-disabled); }

.nz-topbar-spacer { flex: 1; }

/* Busca global — atalho / e ⌘K */
.nz-search {
  position: relative; flex: 0 1 340px; display: flex; align-items: center;
}
.nz-search svg {
  position: absolute; left: var(--nz-space-3); width: 16px; height: 16px;
  color: var(--nz-text-muted); pointer-events: none;
}
.nz-search input {
  width: 100%; height: 36px;
  padding: 0 var(--nz-space-12) 0 var(--nz-space-8);
  background: var(--nz-surface-sunken);
  border: 1px solid var(--nz-border);
  border-radius: var(--nz-radius-md);
  color: var(--nz-text); font: inherit; font-size: var(--nz-text-sm);
}
.nz-search input::placeholder { color: var(--nz-text-muted); }
.nz-search kbd {
  position: absolute; right: var(--nz-space-2);
  padding: 2px var(--nz-space-2);
  background: var(--nz-surface); border: 1px solid var(--nz-border);
  border-radius: var(--nz-radius-sm);
  font-family: var(--nz-font-mono); font-size: var(--nz-text-2xs);
  color: var(--nz-text-muted);
}

.nz-main { display: flex; flex-direction: column; min-width: 0; }

/* Repõe o ritmo vertical dentro de um contêiner intermediário. Sempre que um
   bloco agrupar cards — painel de aba, coluna de um grid, conteúdo trocado por
   JS —, ele quebra a cadeia de irmãos de .nz-content e o gap desaparece sem
   erro visível. Marcar o contêiner com .nz-stack é a correção; foi assim com
   .nz-view e voltou a acontecer em #relConteudo. */
.nz-stack { display: flex; flex-direction: column; gap: var(--nz-space-6); }
.nz-stack[hidden] { display: none; }
/* O ritmo vertical da página vive AQUI: cada bloco de primeiro nível dentro de
   .nz-content recebe 24px do vizinho. Se a página envolver os blocos em um
   contêiner intermediário, esse contêiner precisa repetir display+gap — senão
   os blocos encostam e o ritmo desaparece sem erro visível no CSS. */
.nz-content {
  flex: 1; width: 100%; max-width: var(--nz-content-max);
  margin-inline: auto;
  padding: var(--nz-space-6) var(--nz-space-6) var(--nz-space-16);
  display: flex; flex-direction: column; gap: var(--nz-space-6);
}

/* --------------------------------------------------------------------------
   Cabeçalho de página — UM formato para todas as telas
   -------------------------------------------------------------------------- */
.nz-page-header {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--nz-space-4); flex-wrap: wrap;
}
.nz-page-title {
  margin: 0; font-size: var(--nz-text-xl); font-weight: var(--nz-weight-semibold);
  line-height: var(--nz-leading-tight); text-wrap: balance;
}
.nz-page-subtitle {
  margin: var(--nz-space-1) 0 0; max-width: 68ch;
  font-size: var(--nz-text-sm); color: var(--nz-text-muted);
}
.nz-page-actions { display: flex; align-items: center; gap: var(--nz-space-2); flex-wrap: wrap; }

/* --------------------------------------------------------------------------
   Botões — UMA hierarquia
     primary   : ação principal, no máximo uma por tela
     secondary : ações de apoio (contorno neutro)
     ghost     : ações terciárias e de linha de tabela
     danger    : ações destrutivas, sempre separadas das demais
   -------------------------------------------------------------------------- */
.nz-btn {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--nz-space-2);
  min-height: 36px; padding: 0 var(--nz-space-4);
  border: 1px solid transparent; border-radius: var(--nz-radius-md);
  font: inherit; font-size: var(--nz-text-sm); font-weight: var(--nz-weight-semibold);
  white-space: nowrap; text-decoration: none;
  transition: background var(--nz-duration-fast) var(--nz-ease-out),
              border-color var(--nz-duration-fast) var(--nz-ease-out),
              color var(--nz-duration-fast) var(--nz-ease-out);
}
.nz-btn svg { width: 16px; height: 16px; flex: none; }

.nz-btn[data-variant="primary"] { background: var(--nz-primary); color: var(--nz-primary-fg); }
.nz-btn[data-variant="primary"]:hover { background: var(--nz-primary-hover); }
.nz-btn[data-variant="primary"]:active { background: var(--nz-primary-active); }

.nz-btn[data-variant="secondary"] {
  background: var(--nz-surface); color: var(--nz-text);
  border-color: var(--nz-border-strong);
}
.nz-btn[data-variant="secondary"]:hover { background: var(--nz-surface-hover); }

.nz-btn[data-variant="ghost"] { background: transparent; color: var(--nz-text-secondary); }
.nz-btn[data-variant="ghost"]:hover { background: var(--nz-surface-hover); color: var(--nz-text); }

.nz-btn[data-variant="danger"] { background: var(--nz-danger); color: var(--nz-danger-fg); }
.nz-btn[data-variant="danger"]:hover { filter: brightness(1.08); }

.nz-btn[data-variant="danger-ghost"] { background: transparent; color: var(--nz-danger); }
.nz-btn[data-variant="danger-ghost"]:hover { background: var(--nz-danger-subtle); }

.nz-btn:disabled,
.nz-btn[aria-disabled="true"] { opacity: 0.45; pointer-events: none; }

.nz-btn[data-size="sm"] { min-height: 30px; padding: 0 var(--nz-space-3); font-size: var(--nz-text-xs); }
.nz-btn[data-size="lg"] { min-height: 44px; padding: 0 var(--nz-space-5); font-size: var(--nz-text-md); }

/* Botão só de ícone: área de toque de 44px sem inchar o desenho */
.nz-icon-btn {
  position: relative;
  display: inline-grid; place-items: center;
  width: 32px; height: 32px;
  background: transparent; border: 1px solid transparent;
  border-radius: var(--nz-radius-md);
  color: var(--nz-text-secondary);
  transition: background var(--nz-duration-fast) var(--nz-ease-out),
              color var(--nz-duration-fast) var(--nz-ease-out);
}
.nz-icon-btn::after {
  content: ""; position: absolute; inset: 50% auto auto 50%;
  translate: -50% -50%;
  width: var(--nz-tap-min); height: var(--nz-tap-min);
}
.nz-icon-btn svg { width: 16px; height: 16px; }
.nz-icon-btn:hover { background: var(--nz-surface-hover); color: var(--nz-text); }
.nz-icon-btn[data-tone="danger"]:hover { background: var(--nz-danger-subtle); color: var(--nz-danger); }

.nz-badge-dot {
  position: absolute; top: 2px; right: 2px;
  min-width: 16px; height: 16px; padding: 0 4px;
  display: grid; place-items: center;
  background: var(--nz-danger); color: var(--nz-danger-fg);
  border-radius: var(--nz-radius-pill);
  font-size: 10px; font-weight: var(--nz-weight-bold);
  font-variant-numeric: tabular-nums;
}

/* --------------------------------------------------------------------------
   Superfícies
   -------------------------------------------------------------------------- */
.nz-card {
  background: var(--nz-surface);
  border: 1px solid var(--nz-border);
  border-radius: var(--nz-radius-lg);
  box-shadow: var(--nz-shadow-sm);
}
.nz-card-header {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--nz-space-4);
  padding: var(--nz-space-4) var(--nz-space-5);
  border-bottom: 1px solid var(--nz-border);
}
.nz-card-title {
  margin: 0; font-size: var(--nz-text-md); font-weight: var(--nz-weight-semibold);
}
.nz-card-body { padding: var(--nz-space-5); }
.nz-card-body[data-flush="true"] { padding: 0; }

/* --------------------------------------------------------------------------
   KPI — rótulo antes do número, delta com ícone e sinal (nunca só cor)
   -------------------------------------------------------------------------- */
.nz-kpi-grid {
  display: grid; gap: var(--nz-space-4);
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
.nz-kpi {
  display: flex; flex-direction: column; gap: var(--nz-space-2);
  padding: var(--nz-space-4) var(--nz-space-5);
  background: var(--nz-surface);
  border: 1px solid var(--nz-border);
  border-radius: var(--nz-radius-lg);
}
.nz-kpi-label {
  font-size: var(--nz-text-xs); font-weight: var(--nz-weight-medium);
  color: var(--nz-text-muted);
}
.nz-kpi-value {
  font-size: var(--nz-text-2xl); font-weight: var(--nz-weight-semibold);
  line-height: var(--nz-leading-tight); font-variant-numeric: tabular-nums;
}
.nz-kpi-foot { display: flex; align-items: center; gap: var(--nz-space-2); font-size: var(--nz-text-xs); }
.nz-delta {
  display: inline-flex; align-items: center; gap: 3px;
  font-weight: var(--nz-weight-semibold); font-variant-numeric: tabular-nums;
}
.nz-delta svg { width: 12px; height: 12px; }
.nz-delta[data-dir="up"] { color: var(--nz-success-subtle-fg); }
.nz-delta[data-dir="down"] { color: var(--nz-danger-subtle-fg); }
.nz-delta[data-dir="flat"] { color: var(--nz-text-muted); }
.nz-kpi-context { color: var(--nz-text-muted); }

/* --------------------------------------------------------------------------
   Barra de filtros — aplica ao mudar, sem botão "Filtrar"
   -------------------------------------------------------------------------- */
.nz-toolbar {
  display: flex; align-items: center; gap: var(--nz-space-3);
  flex-wrap: wrap;
  padding: var(--nz-space-3) var(--nz-space-4);
  background: var(--nz-surface);
  border: 1px solid var(--nz-border);
  border-radius: var(--nz-radius-lg);
}
.nz-toolbar-search { position: relative; flex: 1 1 240px; min-width: 0; display: flex; align-items: center; }
/* Os dois campos do periodo personalizado (secao 72): largura do conteudo,
   nao a flex da busca — e o [hidden] continua mandando (nada de display aqui
   fora do :not, a licao da secao 57). */
.nz-toolbar-data:not([hidden]) { flex: 0 0 auto; width: 10.5rem; }
.nz-toolbar-search svg {
  position: absolute; left: var(--nz-space-3); width: 16px; height: 16px;
  color: var(--nz-text-muted); pointer-events: none;
}
.nz-field {
  height: 36px; padding: 0 var(--nz-space-3);
  background: var(--nz-surface-sunken);
  border: 1px solid var(--nz-border-strong);
  border-radius: var(--nz-radius-md);
  color: var(--nz-text); font: inherit; font-size: var(--nz-text-sm);
  max-width: 100%;
}
.nz-toolbar-search .nz-field { width: 100%; padding-left: var(--nz-space-8); }
.nz-field::placeholder { color: var(--nz-text-muted); }
.nz-field:hover { border-color: var(--nz-text-muted); }

/* select nativo estilizado — corrige o select branco sobre fundo escuro */
select.nz-field {
  appearance: none;
  padding-right: var(--nz-space-8);
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 16px) 15px, calc(100% - 11px) 15px;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}

.nz-toolbar-count {
  font-size: var(--nz-text-xs); color: var(--nz-text-muted);
  font-variant-numeric: tabular-nums;
}

/* Chips de filtro ativo — tornam visível o que está filtrando */
.nz-chips { display: flex; align-items: center; gap: var(--nz-space-2); flex-wrap: wrap; }
.nz-chip {
  display: inline-flex; align-items: center; gap: var(--nz-space-2);
  height: 26px; padding: 0 var(--nz-space-1) 0 var(--nz-space-3);
  background: var(--nz-primary-subtle); color: var(--nz-primary-subtle-fg);
  border-radius: var(--nz-radius-pill);
  font-size: var(--nz-text-xs); font-weight: var(--nz-weight-medium);
}
/* O × de remover é <button> em uns lugares e <a> em outros (remover filtro é
   navegar, §11) — os dois têm de receber a MESMA caixa: sem ela o <a> fica com
   a cor de link e o svg alinhado por baseline, fora do centro do chip. */
/* Barra de acoes em massa de Produtos (secao 77): vive fora da regiao que o
   AJAX troca e aparece com 1+ selecionados. Os mini-formularios abrem na
   propria barra. */
.nz-massa-barra:not([hidden]) {
  display: flex; align-items: center; gap: var(--nz-space-2); flex-wrap: wrap;
  margin-bottom: var(--nz-space-3); padding: var(--nz-space-2) var(--nz-space-3);
  background: var(--nz-primary-subtle); border-radius: var(--nz-radius-md);
}
.nz-massa-conta { font-size: var(--nz-text-sm); font-weight: var(--nz-weight-medium); color: var(--nz-primary-subtle-fg); margin-right: var(--nz-space-2); }
.nz-massa-form:not([hidden]) {
  display: flex; align-items: center; gap: var(--nz-space-2); flex-wrap: wrap;
  flex-basis: 100%; padding-top: var(--nz-space-2);
}
.nz-massa-form .nz-field { width: auto; }

/* Chip SEM acao (drawer de exportar, rotulos informativos): o padding direito
   de 4px existe para o x de remover completar o respiro — sem ele, a direita
   fica visivelmente menor que a esquerda (secao 73.1, foto do dono). */
.nz-chip:not(:has(button, a)) { padding-right: var(--nz-space-3); }
.nz-chip :is(button, a) {
  display: grid; place-items: center;
  width: 20px; height: 20px; border: 0; border-radius: var(--nz-radius-pill);
  background: transparent; color: inherit;
}
.nz-chip :is(button, a):hover { background: color-mix(in srgb, currentColor 18%, transparent); }
.nz-chip svg { width: 12px; height: 12px; }

/* --------------------------------------------------------------------------
   Blocos acoplados — um controle e sua continuação
   A barra de filtros e os chips do filtro ativo são a mesma unidade: precisam
   de respiro menor que o ritmo de 24px que separa seções distintas.
   -------------------------------------------------------------------------- */
.nz-filters { display: flex; flex-direction: column; gap: var(--nz-space-2); }

/* --------------------------------------------------------------------------
   Tabela de dados
   -------------------------------------------------------------------------- */
.nz-table-wrap { overflow-x: auto; }
.nz-table { width: 100%; border-collapse: collapse; font-size: var(--nz-text-sm); }

.nz-table thead th {
  position: sticky; top: 0; z-index: var(--nz-z-sticky);
  padding: var(--nz-space-3) var(--nz-space-4);
  background: var(--nz-surface-sunken);
  border-bottom: 1px solid var(--nz-border);
  text-align: left; white-space: nowrap;
  font-size: var(--nz-text-xs); font-weight: var(--nz-weight-semibold);
  color: var(--nz-text-secondary);
}
.nz-table th[data-align="right"], .nz-table td[data-align="right"] { text-align: right; }

/* Ordenação acessível: aria-sort é lido, a seta é vista */
.nz-sort {
  display: inline-flex; align-items: center; gap: var(--nz-space-1);
  background: none; border: 0; padding: 0; color: inherit; font: inherit;
  font-weight: var(--nz-weight-semibold);
}
.nz-sort svg { width: 12px; height: 12px; opacity: 0.4; }
th[aria-sort] .nz-sort { color: var(--nz-text); }
th[aria-sort] .nz-sort svg { opacity: 1; color: var(--nz-primary); }
th[aria-sort="ascending"] .nz-sort svg { rotate: 180deg; }

.nz-table tbody td {
  padding: var(--nz-space-3) var(--nz-space-4);
  border-bottom: 1px solid var(--nz-border);
  vertical-align: middle;
}
.nz-table tbody tr { transition: background var(--nz-duration-fast) var(--nz-ease-out); }
.nz-table tbody tr:hover { background: var(--nz-surface-hover); }
.nz-table tbody tr[data-selected="true"] { background: var(--nz-primary-subtle); }
.nz-table tbody tr:last-child td { border-bottom: 0; }

.nz-cell-primary {
  font-weight: var(--nz-weight-medium); color: var(--nz-text);
  text-decoration: none;
}
.nz-cell-primary:hover { color: var(--nz-primary-subtle-fg); text-decoration: underline; }
.nz-cell-secondary { display: block; font-size: var(--nz-text-xs); color: var(--nz-text-muted); }

/* Serve aos DOIS usos: `<img class="nz-thumb">` (Produtos) e
   `<span class="nz-thumb"><img></span>` (Banners, onde o embrulho é quem se
   esconde no `onerror`, §58.5).

   O `display: block` não é enfeite: sem ele o `<span>` fica `inline`, onde
   `width` e `height` são IGNORADOS — o embrulho não media nada, e como o painel
   não tem reset de `img` a imagem de dentro saía no tamanho natural e esticava a
   linha da tabela (medido: 1920px numa célula de 40). O `object-fit` da regra
   original também só valia para o caso `<img>`: elemento não-substituído não
   tem conteúdo para ajustar. */
.nz-thumb {
  display: block; overflow: hidden;
  width: 40px; height: 40px; flex: none;
  border-radius: var(--nz-radius-md); object-fit: cover;
  background: var(--nz-surface-sunken); border: 1px solid var(--nz-border);
}
/* O par do caso embrulhado: quem recorta é o `.nz-thumb`, e a imagem preenche. */
.nz-thumb > img { display: block; width: 100%; height: 100%; object-fit: cover; }
.nz-cell-stack { min-width: 0; }
.nz-media { display: flex; align-items: center; gap: var(--nz-space-3); min-width: 0; }

/* Ações de linha: aparecem no hover mas seguem acessíveis por teclado */
.nz-row-actions { display: flex; align-items: center; gap: var(--nz-space-1); justify-content: flex-end; }
.nz-row-actions .nz-icon-btn { opacity: 0; transition: opacity var(--nz-duration-fast) var(--nz-ease-out); }
tr:hover .nz-row-actions .nz-icon-btn,
.nz-row-actions .nz-icon-btn:focus-visible { opacity: 1; }
@media (hover: none) {
  .nz-row-actions .nz-icon-btn { opacity: 1; }
}

/* --------------------------------------------------------------------------
   Badges de status — UM sistema: ponto + rótulo, fundo tonal
   -------------------------------------------------------------------------- */
.nz-badge {
  display: inline-flex; align-items: center; gap: var(--nz-space-2);
  height: 22px; padding: 0 var(--nz-space-2);
  border-radius: var(--nz-radius-sm);
  font-size: var(--nz-text-xs); font-weight: var(--nz-weight-medium);
  white-space: nowrap;
  background: var(--nz-neutral-subtle); color: var(--nz-neutral-subtle-fg);
}
.nz-badge::before {
  content: ""; width: 6px; height: 6px; border-radius: 50%;
  background: currentColor; flex: none;
}
.nz-badge[data-tone="success"] { background: var(--nz-success-subtle); color: var(--nz-success-subtle-fg); }
.nz-badge[data-tone="warning"] { background: var(--nz-warning-subtle); color: var(--nz-warning-subtle-fg); }
.nz-badge[data-tone="danger"]  { background: var(--nz-danger-subtle);  color: var(--nz-danger-subtle-fg); }
.nz-badge[data-tone="info"]    { background: var(--nz-info-subtle);    color: var(--nz-info-subtle-fg); }
.nz-badge[data-tone="brand"]   { background: var(--nz-primary-subtle); color: var(--nz-primary-subtle-fg); }

/* --------------------------------------------------------------------------
   Formulários
   -------------------------------------------------------------------------- */
.nz-form-grid { display: grid; gap: var(--nz-space-5); grid-template-columns: repeat(12, 1fr); }
.nz-col-2  { grid-column: span 2; }
.nz-col-3  { grid-column: span 3; }
.nz-col-4  { grid-column: span 4; }
.nz-col-6  { grid-column: span 6; }
.nz-col-8  { grid-column: span 8; }
.nz-col-12 { grid-column: span 12; }

.nz-fieldset { border: 0; margin: 0; padding: 0; display: grid; gap: var(--nz-space-5); }
.nz-legend {
  padding: 0; margin-bottom: var(--nz-space-1);
  font-size: var(--nz-text-md); font-weight: var(--nz-weight-semibold);
}
.nz-legend-hint { font-size: var(--nz-text-sm); font-weight: var(--nz-weight-regular); color: var(--nz-text-muted); }

/* align-content: start impede que a linha mais alta (ex.: um campo com erro)
   estique e redistribua o espaço interno dos campos vizinhos */
.nz-field-group { display: grid; gap: var(--nz-space-2); min-width: 0; align-content: start; }
.nz-label {
  font-size: var(--nz-text-sm); font-weight: var(--nz-weight-medium);
  display: flex; align-items: center; gap: var(--nz-space-1);
}
.nz-required { color: var(--nz-danger); }
.nz-optional { color: var(--nz-text-muted); font-weight: var(--nz-weight-regular); font-size: var(--nz-text-xs); }

.nz-input, .nz-select, .nz-textarea {
  width: 100%; min-height: 40px;
  padding: var(--nz-space-2) var(--nz-space-3);
  background: var(--nz-surface);
  border: 1px solid var(--nz-border-strong);
  border-radius: var(--nz-radius-md);
  color: var(--nz-text); font: inherit; font-size: var(--nz-text-md);
  transition: border-color var(--nz-duration-fast) var(--nz-ease-out),
              box-shadow var(--nz-duration-fast) var(--nz-ease-out);
}
.nz-textarea { min-height: 104px; resize: vertical; line-height: var(--nz-leading-normal); }
.nz-input::placeholder, .nz-textarea::placeholder { color: var(--nz-text-muted); }
.nz-input:hover, .nz-select:hover, .nz-textarea:hover { border-color: var(--nz-text-muted); }
.nz-input:focus, .nz-select:focus, .nz-textarea:focus {
  outline: none; border-color: var(--nz-primary);
  box-shadow: 0 0 0 3px var(--nz-primary-ring);
}
.nz-input:disabled, .nz-select:disabled, .nz-textarea:disabled {
  background: var(--nz-surface-sunken); color: var(--nz-text-disabled);
  cursor: not-allowed;
}
/* Somente-leitura ≠ desabilitado: continua selecionável e legível */
.nz-input[readonly] { background: var(--nz-surface-sunken); border-style: dashed; }

.nz-select {
  appearance: none; padding-right: var(--nz-space-8);
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 17px) 17px, calc(100% - 12px) 17px;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}

/* Ajuda persistente, não placeholder que some ao digitar */
.nz-hint { font-size: var(--nz-text-xs); color: var(--nz-text-muted); }
.nz-error {
  display: flex; align-items: flex-start; gap: var(--nz-space-2);
  font-size: var(--nz-text-xs); color: var(--nz-danger-subtle-fg);
  font-weight: var(--nz-weight-medium);
}
.nz-error svg { width: 14px; height: 14px; flex: none; margin-top: 1px; }
.nz-input[aria-invalid="true"], .nz-select[aria-invalid="true"], .nz-textarea[aria-invalid="true"] {
  border-color: var(--nz-danger);
}
.nz-input[aria-invalid="true"]:focus { box-shadow: 0 0 0 3px var(--nz-danger-subtle); }

.nz-input-prefix { position: relative; display: flex; align-items: center; }
.nz-input-prefix > span {
  position: absolute; left: var(--nz-space-3);
  font-size: var(--nz-text-md); color: var(--nz-text-muted); pointer-events: none;
}
.nz-input-prefix .nz-input { padding-left: var(--nz-space-10); }
/* Prefixo longo ("/produto/") não cabe no recuo fixo pensado para "R$" e
   acaba impresso por cima do valor. Aqui o prefixo entra no fluxo e o campo
   ocupa o que sobra — sem posicionamento absoluto, sem número mágico. */
.nz-input-prefix[data-largo="true"] { gap: 2px; }
.nz-input-prefix[data-largo="true"] > span {
  position: static; white-space: nowrap;
  font-family: var(--nz-font-mono); font-size: var(--nz-text-sm);
}
.nz-input-prefix[data-largo="true"] .nz-input { padding-left: var(--nz-space-3); flex: 1; min-width: 0; }

/* Switch com rótulo e descrição — substitui o toggle solto sem explicação */
.nz-switch-row {
  display: flex; align-items: flex-start; gap: var(--nz-space-4);
  padding: var(--nz-space-4);
  border: 1px solid var(--nz-border); border-radius: var(--nz-radius-md);
  background: var(--nz-surface);
}
.nz-switch-text { flex: 1; min-width: 0; display: grid; gap: 2px; }
.nz-switch-title { font-size: var(--nz-text-md); font-weight: var(--nz-weight-medium); }
.nz-switch-desc { font-size: var(--nz-text-xs); color: var(--nz-text-muted); }

.nz-switch { position: relative; flex: none; display: inline-flex; }
.nz-switch input {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; margin: 0; cursor: pointer;
}
.nz-switch span {
  width: 40px; height: 24px; border-radius: var(--nz-radius-pill);
  background: var(--nz-border-strong);
  transition: background var(--nz-duration-base) var(--nz-ease-out);
  display: block;
}
.nz-switch span::after {
  content: ""; display: block;
  width: 18px; height: 18px; margin: 3px; border-radius: 50%;
  background: var(--nz-surface); box-shadow: var(--nz-shadow-sm);
  transition: translate var(--nz-duration-base) var(--nz-ease-out);
}
.nz-switch input:checked + span { background: var(--nz-primary); }
.nz-switch input:checked + span::after { translate: 16px 0; }
.nz-switch input:focus-visible + span { outline: 2px solid var(--nz-border-focus); outline-offset: 2px; }

/* Barra de salvar fixa — sempre alcançável em formulário longo */
/* Em formulário CURTO o sticky não faz nada — a barra parava colada no último
   card, no meio da tela. O form com barra entra no flex do .nz-content (que já
   é flex:1) e a barra se empurra ao fundo com margin-top:auto; o gap dá ao
   miolo do form o mesmo respiro que o .nz-content dá às telas. Escopado por
   :has() para não mexer em nenhum form legado sem barra. */
.nz-content > form:has(> .nz-form-bar) {
  flex: 1; display: flex; flex-direction: column;
  gap: var(--nz-space-6); min-height: 0;
}
.nz-content > form > .nz-form-bar { margin-top: auto; }

.nz-form-bar {
  position: sticky; bottom: 0; z-index: var(--nz-z-sticky);
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--nz-space-4);
  margin-inline: calc(var(--nz-space-6) * -1);
  padding: var(--nz-space-3) var(--nz-space-6);
  background: color-mix(in srgb, var(--nz-surface) 92%, transparent);
  backdrop-filter: blur(8px);
  border-top: 1px solid var(--nz-border);
}
.nz-form-bar-status { font-size: var(--nz-text-xs); color: var(--nz-text-muted); }

/* --------------------------------------------------------------------------
   Abas
   -------------------------------------------------------------------------- */
.nz-tabs {
  display: flex; align-items: center; gap: var(--nz-space-1);
  border-bottom: 1px solid var(--nz-border);
  /* overflow-x: auto faz o eixo Y virar auto também. Sem o hidden explícito,
     o indicador da aba ativa transborda 1px e o navegador desenha uma barra
     de rolagem vertical fantasma de 15px na régua de abas. */
  overflow-x: auto; overflow-y: hidden;
}
.nz-tab {
  position: relative;
  display: inline-flex; align-items: center; gap: var(--nz-space-2);
  min-height: var(--nz-tap-min); padding: 0 var(--nz-space-4);
  background: none; border: 0;
  color: var(--nz-text-muted); font: inherit; font-size: var(--nz-text-sm);
  font-weight: var(--nz-weight-medium); white-space: nowrap;
  text-decoration: none;
  transition: color var(--nz-duration-fast) var(--nz-ease-out);
}
.nz-tab:hover { color: var(--nz-text); }
/* Dois jeitos de marcar a aba ativa: aria-selected em <button role="tab"> que
   troca painel na própria tela, aria-current="page" em <a> quando cada aba é
   uma rota (Importar e exportar). aria-selected fora de role="tab" é ARIA
   inválido — o link ativo precisa do seu próprio atributo. */
.nz-tab[aria-selected="true"],
.nz-tab[aria-current="page"] { color: var(--nz-primary-subtle-fg); font-weight: var(--nz-weight-semibold); }
.nz-tab[aria-selected="true"]::after,
.nz-tab[aria-current="page"]::after {
  content: ""; position: absolute; left: var(--nz-space-2); right: var(--nz-space-2); bottom: 0;
  height: 2px; border-radius: 2px 2px 0 0; background: var(--nz-primary);
}

/* --------------------------------------------------------------------------
   Estados vazios, callouts, carregamento
   -------------------------------------------------------------------------- */
.nz-empty {
  display: grid; justify-items: center; gap: var(--nz-space-3);
  padding: var(--nz-space-12) var(--nz-space-6);
  text-align: center;
}
.nz-empty-icon {
  display: grid; place-items: center;
  width: 48px; height: 48px; border-radius: var(--nz-radius-lg);
  background: var(--nz-surface-sunken); color: var(--nz-text-muted);
}
.nz-empty-icon svg { width: 22px; height: 22px; }
.nz-empty-title { font-size: var(--nz-text-md); font-weight: var(--nz-weight-semibold); }
.nz-empty-text { font-size: var(--nz-text-sm); color: var(--nz-text-muted); max-width: 46ch; }

.nz-callout {
  display: flex; align-items: flex-start; gap: var(--nz-space-3);
  padding: var(--nz-space-4);
  border-radius: var(--nz-radius-md);
  border: 1px solid transparent;
  font-size: var(--nz-text-sm);
  background: var(--nz-info-subtle); color: var(--nz-info-subtle-fg);
}
.nz-callout svg { width: 18px; height: 18px; flex: none; margin-top: 1px; }
.nz-callout[data-tone="warning"] { background: var(--nz-warning-subtle); color: var(--nz-warning-subtle-fg); }
.nz-callout[data-tone="danger"]  { background: var(--nz-danger-subtle);  color: var(--nz-danger-subtle-fg); }
.nz-callout[data-tone="success"] { background: var(--nz-success-subtle); color: var(--nz-success-subtle-fg); }
.nz-callout-title { font-weight: var(--nz-weight-semibold); display: block; margin-bottom: 2px; }

/* Skeleton em vez de spinner bloqueante acima de 300ms */
.nz-skeleton {
  background: linear-gradient(90deg,
    var(--nz-surface-sunken) 25%,
    var(--nz-surface-hover) 37%,
    var(--nz-surface-sunken) 63%);
  background-size: 400% 100%;
  border-radius: var(--nz-radius-sm);
  animation: nz-shimmer 1.4s ease-in-out infinite;
}
@keyframes nz-shimmer { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }

/* --------------------------------------------------------------------------
   Paginação
   -------------------------------------------------------------------------- */
.nz-pagination {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--nz-space-4); flex-wrap: wrap;
  padding: var(--nz-space-3) var(--nz-space-4);
  border-top: 1px solid var(--nz-border);
  font-size: var(--nz-text-sm); color: var(--nz-text-muted);
}
.nz-pagination-pages { display: flex; align-items: center; gap: var(--nz-space-1); }
.nz-page-btn {
  min-width: 32px; height: 32px; padding: 0 var(--nz-space-2);
  display: grid; place-items: center;
  background: transparent; border: 1px solid transparent;
  border-radius: var(--nz-radius-md);
  color: var(--nz-text-secondary); font: inherit; font-size: var(--nz-text-sm);
  font-variant-numeric: tabular-nums;
}
.nz-page-btn:hover { background: var(--nz-surface-hover); color: var(--nz-text); }
.nz-page-btn[aria-current="page"] {
  background: var(--nz-primary); color: var(--nz-primary-fg);
  font-weight: var(--nz-weight-semibold);
}

/* --------------------------------------------------------------------------
   Responsivo
   -------------------------------------------------------------------------- */
@media (max-width: 1024px) {
  .nz-shell, .nz-shell[data-nav="collapsed"] { grid-template-columns: minmax(0, 1fr); }

  /* Sidebar vira painel deslizante */
  .nz-sidebar {
    position: fixed; inset: 0 auto 0 0;
    width: min(84vw, var(--nz-sidebar-w));
    translate: -100% 0;
    z-index: var(--nz-z-drawer);
    box-shadow: var(--nz-shadow-xl);
    transition: translate var(--nz-duration-slow) var(--nz-ease-out);
  }
  .nz-shell[data-nav="open"] .nz-sidebar { translate: 0 0; }
  .nz-scrim {
    position: fixed; inset: 0; z-index: calc(var(--nz-z-drawer) - 1);
    background: var(--nz-overlay); opacity: 0; pointer-events: none;
    transition: opacity var(--nz-duration-base) var(--nz-ease-out);
  }
  .nz-shell[data-nav="open"] .nz-scrim { opacity: 1; pointer-events: auto; }

  .nz-content { padding: var(--nz-space-4) var(--nz-space-4) var(--nz-space-16); gap: var(--nz-space-4); }
  .nz-topbar { padding: 0 var(--nz-space-4); }
  .nz-search { display: none; }
  .nz-form-bar { margin-inline: calc(var(--nz-space-4) * -1); padding-inline: var(--nz-space-4); }
}

@media (min-width: 1025px) {
  .nz-nav-toggle-mobile, .nz-scrim { display: none; }
}

@media (max-width: 768px) {
  .nz-col-3, .nz-col-4, .nz-col-6, .nz-col-8 { grid-column: span 12; }
  /* col-2 vira metade da linha, não linha inteira: são campos curtos (peso e
     dimensões) — empilhados um por linha, quatro linhas de input de 3 dígitos;
     em pares, o 2×2 mantém a harmonia. */
  .nz-col-2 { grid-column: span 6; }
  .nz-page-header { flex-direction: column; align-items: stretch; }
  .nz-page-actions .nz-btn { flex: 1; }

  /* Filtros deixam de empilhar em 5 linhas: viram uma linha rolável + botão */
  .nz-toolbar { flex-wrap: nowrap; overflow-x: auto; }
  .nz-toolbar-search { flex: 1 0 200px; }

  /* Tabela vira lista de cards — elimina o scroll horizontal da página */
  .nz-table[data-responsive="cards"],
  .nz-table[data-responsive="cards"] tbody,
  .nz-table[data-responsive="cards"] tr,
  .nz-table[data-responsive="cards"] td { display: block; width: 100%; }
  .nz-table[data-responsive="cards"] thead { display: none; }
  .nz-table[data-responsive="cards"] tbody tr {
    padding: var(--nz-space-3) var(--nz-space-4);
    border-bottom: 1px solid var(--nz-border);
  }
  .nz-table[data-responsive="cards"] tbody td {
    display: flex; align-items: center; justify-content: space-between;
    gap: var(--nz-space-4);
    padding: var(--nz-space-1) 0; border: 0; text-align: left;
  }
  .nz-table[data-responsive="cards"] td[data-label]::before {
    content: attr(data-label);
    font-size: var(--nz-text-xs); font-weight: var(--nz-weight-medium);
    color: var(--nz-text-muted); flex: none;
  }
  .nz-table[data-responsive="cards"] td[data-cell="header"] { padding-bottom: var(--nz-space-2); }
  .nz-table[data-responsive="cards"] td[data-cell="header"]::before { content: none; }
  .nz-row-actions { justify-content: flex-start; }
  .nz-row-actions .nz-icon-btn { opacity: 1; }
}

@media (max-width: 768px) {
  /* Alvo de toque: campos e botões chegam a 44px no mobile */
  .nz-field { height: var(--nz-tap-min); }
  .nz-btn { min-height: var(--nz-tap-min); }
  .nz-input, .nz-select, .nz-textarea { min-height: var(--nz-tap-min); font-size: 1rem; }

  /* Valor multi-linha dentro da célula-card fica agrupado e alinhado à direita */
  .nz-table[data-responsive="cards"] td[data-label] > .nz-cell-stack { text-align: right; }
}

@media (max-width: 1024px) {
  /* No mobile o menu abre em painel: recolher para rail não faz sentido */
  #navToggle { display: none; }
}

@media print {
  .nz-sidebar, .nz-topbar, .nz-toolbar, .nz-row-actions, .nz-pagination, .nz-form-bar { display: none; }
  .nz-content { padding: 0; max-width: none; }
  .nz-card { border-color: #999; box-shadow: none; }
}

/* ==========================================================================
   Drawer lateral — padrão único de criação/edição rápida
   Substitui a navegação para uma tela cheia só para cadastrar um registro:
   o contexto da listagem permanece visível atrás.
   ========================================================================== */
.nz-drawer-backdrop {
  position: fixed; inset: 0; z-index: var(--nz-z-overlay);
  background: var(--nz-overlay);
  opacity: 0; pointer-events: none;
  transition: opacity var(--nz-duration-base) var(--nz-ease-out);
}
.nz-drawer-backdrop[data-open="true"] { opacity: 1; pointer-events: auto; }

.nz-drawer {
  position: fixed; inset: 0 0 0 auto; z-index: var(--nz-z-drawer);
  width: min(100vw, 480px);
  display: flex; flex-direction: column;
  background: var(--nz-surface);
  border-left: 1px solid var(--nz-border);
  box-shadow: var(--nz-shadow-xl);
  translate: 100% 0;
  transition: translate var(--nz-duration-slow) var(--nz-ease-out);
}
.nz-drawer[data-open="true"] { translate: 0 0; }

/* Drawer AMPLO — a segunda das duas medidas do sistema.
   -------------------------------------------------------------------------
   480px serve formulário: rótulo em cima, campo embaixo, uma coluna. Não serve
   curadoria — escolher produtos num catálogo precisa de miniatura, nome, preço e
   chips de variação na MESMA linha, senão cada item vira quatro linhas e a lista
   deixa de ser comparável.

   960px e não "quase a tela inteira", e a diferença não é estética: o que faz um
   drawer valer é a lista continuar visível ao lado, orientando. A 95% de largura
   essa propriedade desaparece e sobra uma página com fundo escuro em volta —
   herdando os problemas de página (endereço, F5, voltar) sem as soluções dela.

   Duas medidas, um modelo: adicionar e editar SEMPRE abre painel lateral. É o que
   converge a UI sem transformar drawer em página disfarçada. §57.1
   ------------------------------------------------------------------------- */
.nz-drawer[data-tamanho="amplo"] { width: min(100vw, 960px); }

.nz-drawer-header {
  display: flex; align-items: flex-start; gap: var(--nz-space-4);
  padding: var(--nz-space-4) var(--nz-space-5);
  border-bottom: 1px solid var(--nz-border);
}
/* O bloco do título ocupa a largura e empurra o × para a borda direita — sem
   isto o fechar fica colado no título. Alguns headers já carregam
   `style="flex:1;min-width:0"` inline; a regra aqui torna o contrato do
   componente, não uma lembrança por tela. `min-width: 0` deixa o título longo
   truncar em vez de empurrar o × para fora. */
.nz-drawer-header > :first-child { flex: 1; min-width: 0; }
.nz-drawer-title { margin: 0; font-size: var(--nz-text-lg); font-weight: var(--nz-weight-semibold); }
.nz-drawer-subtitle { margin: 2px 0 0; font-size: var(--nz-text-xs); color: var(--nz-text-muted); }
/* Drawer com <form> em volta de body+footer (Clientes, Configurações da loja):
   o form entra como filho do flex e, sem estas regras, cresce com o conteúdo —
   o body não rola e o footer com o Salvar fica abaixo da dobra. Clientes nunca
   sentiu porque o formulário de lá cabe na tela; o da taxa de entrega não. */
.nz-drawer > form { display: flex; flex-direction: column; flex: 1; min-height: 0; }

.nz-drawer-body { flex: 1; overflow-y: auto; padding: var(--nz-space-5); display: grid; gap: var(--nz-space-5); align-content: start; }
.nz-drawer-footer {
  display: flex; align-items: center; justify-content: flex-end; gap: var(--nz-space-2);
  padding: var(--nz-space-4) var(--nz-space-5);
  border-top: 1px solid var(--nz-border);
  background: var(--nz-surface-sunken);
}

@media (max-width: 560px) {
  /* Em telas pequenas o drawer sobe de baixo: alcance do polegar */
  .nz-drawer {
    inset: auto 0 0 0; width: 100%; height: min(92dvh, 100%);
    border-left: 0; border-top: 1px solid var(--nz-border);
    border-radius: var(--nz-radius-xl) var(--nz-radius-xl) 0 0;
    translate: 0 100%;
  }
  .nz-drawer[data-open="true"] { translate: 0 0; }
  .nz-drawer-footer .nz-btn { flex: 1; }
}

/* ==========================================================================
   Estado "pronto para consultar" — a tela adia a consulta cara, mas
   entrega o primeiro clique feito. Nunca "use os filtros acima".
   ========================================================================== */
.nz-quick-filters {
  display: flex; flex-wrap: wrap; gap: var(--nz-space-2);
  justify-content: center; max-width: 640px;
}
.nz-quick-filter {
  display: inline-flex; align-items: center; gap: var(--nz-space-2);
  min-height: var(--nz-tap-min); padding: 0 var(--nz-space-4);
  background: var(--nz-surface); color: var(--nz-text);
  border: 1px solid var(--nz-border-strong); border-radius: var(--nz-radius-pill);
  font: inherit; font-size: var(--nz-text-sm); font-weight: var(--nz-weight-medium);
  /* No painel real o atalho é um <a> (filtrar é navegar, §11) — sem esta
     linha ele herda o sublinhado global de âncora; no protótipo era <button>
     e o problema não aparecia. */
  text-decoration: none;
  transition: border-color var(--nz-duration-fast) var(--nz-ease-out),
              background var(--nz-duration-fast) var(--nz-ease-out);
}
.nz-quick-filter:hover { background: var(--nz-surface-hover); border-color: var(--nz-primary); }
.nz-quick-filter .nz-nav-count { background: var(--nz-primary-subtle); color: var(--nz-primary-subtle-fg); }

/* Recorte com contagem ZERO: a informação fica, o convite sai. Um atalho que
   leva a "nenhum resultado" gasta um clique para não dizer nada — então ele
   nasce <span> e não <a>, sem hover e sem cursor de link. A borda mais fraca é o
   que diferencia à vista; o `aria-disabled` é o que diferencia no leitor. */
.nz-quick-filter[data-vazio] {
  color: var(--nz-text-muted);
  border-color: var(--nz-border);
  border-style: dashed;
  cursor: default;
}
.nz-quick-filter[data-vazio]:hover {
  background: var(--nz-surface);
  border-color: var(--nz-border);
}
.nz-quick-filter[data-vazio] .nz-nav-count {
  background: var(--nz-surface-hover);
  color: var(--nz-text-muted);
}

/* ==========================================================================
   Curadoria de produtos — o que só o drawer AMPLO comporta (§57.1).

   Buscar-e-escolher dentro de um painel lateral tem duas armadilhas próprias:
   a sugestão precisa flutuar sem esticar o painel, e a lista escolhida precisa
   rolar SEM criar rolagem dentro de rolagem, que é onde o dedo se perde no
   celular. Daí `position:relative` na caixa de busca e teto de altura na lista.
   ========================================================================== */
.nz-busca-produto { position: relative; }

.nz-sugestoes {
  position: absolute; top: calc(100% - 18px); left: 0; right: 0;
  z-index: 20;
  max-height: 320px; overflow-y: auto;
  background: var(--nz-surface);
  border: 1px solid var(--nz-border-strong);
  border-radius: var(--nz-radius-md);
  box-shadow: var(--nz-shadow-xl);
}
.nz-sugestao {
  display: flex; align-items: center; gap: var(--nz-space-3);
  width: 100%; padding: var(--nz-space-3) var(--nz-space-4);
  background: none; border: 0; border-bottom: 1px solid var(--nz-border);
  color: var(--nz-text); font: inherit; text-align: left; cursor: pointer;
}
.nz-sugestao:last-child { border-bottom: 0; }
.nz-sugestao:hover,
.nz-sugestao[aria-selected="true"] { background: var(--nz-surface-hover); }
.nz-sugestao-nome { font-weight: var(--nz-weight-medium); }
.nz-sugestao-meta { font-size: var(--nz-text-xs); color: var(--nz-text-muted); }
.nz-sugestao-vazio {
  padding: var(--nz-space-4); text-align: center;
  color: var(--nz-text-muted); font-size: var(--nz-text-sm);
}

/* A miniatura do produto. Mesma medida na sugestão e na lista escolhida, para o
   olho não recalibrar entre uma e outra. */
.nz-prod-foto {
  width: 40px; height: 40px; flex: none;
  object-fit: cover; border-radius: var(--nz-radius-sm);
  background: var(--nz-surface-hover);
}
/* `:not([hidden])` não é preciosismo — é o conserto de um defeito que eu criei
   aqui: o atributo `hidden` funciona porque o navegador aplica `display:none`, e
   qualquer regra de classe que declare `display` o ANULA. Sem o `:not`, o lugar
   vazio que nasce escondido (para o `onerror` revelar) aparecia junto da imagem, e
   cada linha da tabela mostrava DUAS miniaturas. Vale para todo componente do
   design system que declare `display` e possa nascer com `hidden`. */
.nz-prod-foto[data-vazia]:not([hidden]) {
  display: grid; place-items: center;
  font-size: 9px; letter-spacing: .04em; color: var(--nz-text-muted);
}

.nz-prod-escolhidos {
  list-style: none; margin: var(--nz-space-4) 0 0; padding: 0;
  display: grid; gap: var(--nz-space-2);
  max-height: 46vh; overflow-y: auto;
}
.nz-prod-item {
  display: flex; align-items: flex-start; gap: var(--nz-space-3);
  padding: var(--nz-space-3);
  border: 1px solid var(--nz-border); border-radius: var(--nz-radius-md);
  background: var(--nz-surface);
}
.nz-prod-corpo { flex: 1; min-width: 0; display: grid; gap: 2px; }
.nz-prod-nome { font-weight: var(--nz-weight-medium); }
.nz-prod-meta { font-size: var(--nz-text-xs); color: var(--nz-text-muted); }
.nz-prod-preco { font-size: var(--nz-text-sm); }
.nz-prod-preco s { color: var(--nz-text-muted); margin-right: var(--nz-space-1); }

/* Os controles de ordem. `flex: none` e coluna: a lista é a ordem que o cliente
   vê, então mover tem de ser possível sem arrastar — arrastar não funciona com
   teclado, e num painel que rola é ainda pior. */
.nz-prod-acoes { flex: none; display: flex; align-items: center; gap: 2px; }

/* Chips de variação: o que NÃO estiver marcado sai do catálogo. Começam todos
   marcados, que é a semântica do backend (ausência = tudo dentro). */
.nz-var-grupo { margin-top: var(--nz-space-2); }
.nz-var-titulo {
  font-size: var(--nz-text-xs); color: var(--nz-text-muted);
  margin-bottom: 2px;
}
.nz-var-itens { display: flex; flex-wrap: wrap; gap: 4px; }
.nz-var-chip {
  padding: 2px var(--nz-space-2);
  border: 1px solid var(--nz-border-strong); border-radius: var(--nz-radius-pill);
  background: none; color: var(--nz-text-muted);
  font: inherit; font-size: var(--nz-text-xs); cursor: pointer;
}
.nz-var-chip[aria-pressed="true"] {
  background: var(--nz-primary-subtle); color: var(--nz-primary-subtle-fg);
  border-color: transparent;
}

/* Capa do catálogo. Proporção 3:4, que é a do PDF — mostrar quadrado aqui
   mentiria sobre o recorte que a impressão faz. */
.nz-capa-linha { display: flex; align-items: flex-start; gap: var(--nz-space-4); }
.nz-capa-previa {
  width: 120px; aspect-ratio: 3 / 4; flex: none;
  display: grid; place-items: center; overflow: hidden;
  border: 1px solid var(--nz-border); border-radius: var(--nz-radius-md);
  background: var(--nz-surface-hover);
}
.nz-capa-previa img { width: 100%; height: 100%; object-fit: cover; }
/* A prévia é zona de soltar da capa; mesmo realce do `.nz-drop`. */
.nz-capa-previa[data-sobre="true"] { border-color: var(--nz-primary); background: var(--nz-primary-subtle); }
.nz-capa-vazia { font-size: var(--nz-text-xs); color: var(--nz-text-muted); }
.nz-capa-acoes { display: grid; gap: var(--nz-space-2); align-content: start; }

@media (max-width: 560px) {
  /* No estreito o drawer já é folha de baixo: a lista escolhida perde o teto
     próprio, senão sobram duas barras de rolagem competindo pelo dedo. */
  .nz-prod-escolhidos { max-height: none; }
  .nz-capa-linha { flex-direction: column; }
}

/* ==========================================================================
   Zona de upload com prévia — banner desktop e mobile (§59.3).

   Componente e não CSS de tela porque já há dois usos no mesmo formulário e um
   terceiro à espera (a capa do catálogo). O que ele resolve: a área de soltar tem
   de ser grande o bastante para acertar arrastando, e a prévia tem de ocupar o
   MESMO espaço da área vazia — senão o formulário salta quando o arquivo entra, e
   o campo seguinte foge do dedo.
   ========================================================================== */
.nz-upload {
  position: relative;
  display: grid; place-items: center;
  min-height: 148px;
  padding: var(--nz-space-4);
  border: 1px dashed var(--nz-border-strong);
  border-radius: var(--nz-radius-md);
  background: var(--nz-surface);
  text-align: center;
  transition: border-color var(--nz-duration-fast) var(--nz-ease-out),
              background var(--nz-duration-fast) var(--nz-ease-out);
}
.nz-upload[data-arrastando="true"] {
  border-color: var(--nz-primary);
  background: var(--nz-surface-hover);
}
.nz-upload-convite { display: grid; gap: var(--nz-space-2); justify-items: center; cursor: pointer; }
.nz-upload-convite svg { width: 28px; height: 28px; color: var(--nz-text-muted); }
.nz-upload-titulo { font-size: var(--nz-text-sm); font-weight: var(--nz-weight-medium); }
.nz-upload-dica { font-size: var(--nz-text-xs); color: var(--nz-text-muted); line-height: 1.5; }

/* A prévia enche a zona. `object-fit: contain` e não `cover`: banner é recorte
   largo, e cortar a prévia mostraria uma composição que não é a que vai ao ar. */
.nz-upload-previa {
  display: grid; gap: var(--nz-space-2); width: 100%; justify-items: center;
}
.nz-upload-previa img,
.nz-upload-previa video {
  max-width: 100%; max-height: 180px;
  border-radius: var(--nz-radius-sm);
  object-fit: contain;
  background: var(--nz-surface-sunken);
}
.nz-upload-acoes { display: flex; gap: var(--nz-space-2); flex-wrap: wrap; justify-content: center; }
.nz-upload-arquivo {
  font-size: var(--nz-text-xs); color: var(--nz-text-muted);
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* O lugar vazio da prévia da vitrine. Precisa de ALTURA própria: sem ela o
   contêiner colapsava para zero quando a imagem falhava, e o card dizia "1 no ar"
   mostrando nada — que se lê como prévia quebrada e não como arquivo ausente
   (§59.6). `:not([hidden])` porque declarar `display` anula o `hidden` (§58.3). */
.nz-vitrine-vazia:not([hidden]) {
  display: flex; align-items: center; gap: var(--nz-space-3);
  min-height: 96px; padding: var(--nz-space-4);
  border: 1px dashed var(--nz-border-strong); border-radius: var(--nz-radius-md);
  color: var(--nz-text-muted); font-size: var(--nz-text-sm);
}
.nz-vitrine-vazia svg { width: 24px; height: 24px; flex: none; }

/* ==========================================================================
   Esqueleto de carregamento — o que aparece entre o clique e o dado.
   Preserva a altura da linha, então a tabela não salta quando o dado chega.
   ========================================================================== */
.nz-skeleton-row {
  display: flex; align-items: center; gap: var(--nz-space-4);
  padding: var(--nz-space-3) var(--nz-space-4);
  border-bottom: 1px solid var(--nz-border);
  height: 57px;
}
.nz-skeleton-row > * { height: 12px; border-radius: var(--nz-radius-sm); }
.nz-skeleton-row:last-child { border-bottom: 0; }

/* ==========================================================================
   Árvore — categorias mãe/filha (a coluna parent_id já existe e o Dapic
   já a preenche; a listagem plana de hoje esconde a hierarquia real)
   ========================================================================== */
.nz-tree { display: grid; }
.nz-tree-row {
  display: flex; align-items: center; gap: var(--nz-space-3);
  min-height: 52px; padding: var(--nz-space-2) var(--nz-space-4);
  border-bottom: 1px solid var(--nz-border);
}
.nz-tree-row:last-child { border-bottom: 0; }
.nz-tree-row:hover { background: var(--nz-surface-hover); }
.nz-tree-row[data-depth="1"] { padding-left: var(--nz-space-10); }
.nz-tree-row[data-depth="2"] { padding-left: var(--nz-space-16); }
.nz-tree-row[data-depth="3"] { padding-left: calc(var(--nz-space-16) + var(--nz-space-6)); }

/* A guia vertical torna a profundidade legível sem contar pixels */
.nz-tree-row[data-depth]:not([data-depth="0"]) { position: relative; }
.nz-tree-row[data-depth]:not([data-depth="0"])::before {
  content: ""; position: absolute; top: 0; bottom: 0;
  left: calc(var(--nz-space-4) + 9px);
  border-left: 1px solid var(--nz-border);
}
.nz-tree-row[data-depth="2"]::before { left: calc(var(--nz-space-10) + 9px); }
.nz-tree-row[data-depth="3"]::before { left: calc(var(--nz-space-16) + 9px); }

.nz-tree-toggle {
  display: grid; place-items: center; flex: none;
  width: 20px; height: 20px; border: 0; border-radius: var(--nz-radius-sm);
  background: transparent; color: var(--nz-text-muted);
}
.nz-tree-toggle:hover { background: var(--nz-surface-active); color: var(--nz-text); }
.nz-tree-toggle svg { width: 14px; height: 14px; transition: rotate var(--nz-duration-fast) var(--nz-ease-out); }
.nz-tree-toggle[aria-expanded="true"] svg { rotate: 90deg; }
.nz-tree-spacer { width: 20px; flex: none; }
.nz-tree-name { flex: 1; min-width: 0; font-size: var(--nz-text-sm); font-weight: var(--nz-weight-medium); }
.nz-tree-meta { font-size: var(--nz-text-xs); color: var(--nz-text-muted); white-space: nowrap; }
.nz-tree-handle { color: var(--nz-text-disabled); cursor: grab; flex: none; }
.nz-tree-handle svg { width: 14px; height: 14px; display: block; }

/* ==========================================================================
   Controle segmentado — escolha entre poucas opções mutuamente exclusivas
   que mudam o formulário abaixo (ex.: tipo de desconto)
   ========================================================================== */
.nz-segmented {
  display: inline-flex; padding: 3px; gap: 2px;
  background: var(--nz-surface-sunken);
  border: 1px solid var(--nz-border); border-radius: var(--nz-radius-md);
}
.nz-segmented button {
  min-height: 32px; padding: 0 var(--nz-space-3);
  background: transparent; border: 0; border-radius: var(--nz-radius-sm);
  color: var(--nz-text-secondary); font: inherit; font-size: var(--nz-text-sm);
  font-weight: var(--nz-weight-medium); white-space: nowrap;
  transition: background var(--nz-duration-fast) var(--nz-ease-out),
              color var(--nz-duration-fast) var(--nz-ease-out);
}
.nz-segmented button:hover { color: var(--nz-text); }
.nz-segmented button[aria-pressed="true"] {
  background: var(--nz-surface); color: var(--nz-text);
  font-weight: var(--nz-weight-semibold); box-shadow: var(--nz-shadow-sm);
}

/* ==========================================================================
   Menu suspenso — conta do usuário
   ========================================================================== */
.nz-menu {
  position: absolute; z-index: var(--nz-z-overlay);
  min-width: 240px; padding: var(--nz-space-1);
  background: var(--nz-surface-raised);
  border: 1px solid var(--nz-border); border-radius: var(--nz-radius-lg);
  box-shadow: var(--nz-shadow-lg);
  display: grid; gap: 1px;
}
.nz-menu[hidden] { display: none; }
.nz-menu-label {
  padding: var(--nz-space-2) var(--nz-space-3) var(--nz-space-1);
  font-size: var(--nz-text-2xs); font-weight: var(--nz-weight-semibold);
  letter-spacing: var(--nz-tracking-caps); text-transform: uppercase;
  color: var(--nz-text-muted);
}
.nz-menu-item {
  display: flex; align-items: center; gap: var(--nz-space-3);
  min-height: 36px; padding: var(--nz-space-2) var(--nz-space-3);
  border: 0; border-radius: var(--nz-radius-md);
  background: transparent; color: var(--nz-text);
  font: inherit; font-size: var(--nz-text-sm); text-align: left; text-decoration: none;
  width: 100%;
}
.nz-menu-item:hover { background: var(--nz-surface-hover); }
.nz-menu-item svg { width: 16px; height: 16px; flex: none; color: var(--nz-text-muted); }
.nz-menu-item[data-tone="danger"] { color: var(--nz-danger); }
.nz-menu-item[data-tone="danger"]:hover { background: var(--nz-danger-subtle); }
.nz-menu-item[data-tone="danger"] svg { color: currentColor; }
.nz-menu-sep { height: 1px; margin: var(--nz-space-1) 0; background: var(--nz-border); }
.nz-menu-item-note { margin-left: auto; font-size: var(--nz-text-xs); color: var(--nz-text-muted); }

/* Identidade da conta no topo do menu */
.nz-menu-account {
  display: flex; align-items: center; gap: var(--nz-space-3);
  padding: var(--nz-space-3);
  border-bottom: 1px solid var(--nz-border); margin-bottom: var(--nz-space-1);
}
.nz-menu-avatar {
  display: grid; place-items: center; flex: none;
  width: 36px; height: 36px; border-radius: var(--nz-radius-pill);
  background: var(--nz-primary-subtle); color: var(--nz-primary-subtle-fg);
  font-weight: var(--nz-weight-semibold); font-size: var(--nz-text-sm);
}
.nz-menu-account-name { font-size: var(--nz-text-sm); font-weight: var(--nz-weight-semibold); }
.nz-menu-account-mail { font-size: var(--nz-text-xs); color: var(--nz-text-muted); }

/* ==========================================================================
   Divulgação progressiva — seção que só abre quando pedida
   Trocar a senha é raro e tem 3 campos; deixá-los sempre abertos faz a tela
   de conta parecer um formulário de senha com dados pessoais em volta.
   ========================================================================== */
.nz-collapse { border: 1px solid var(--nz-border); border-radius: var(--nz-radius-md); }
.nz-collapse-trigger {
  display: flex; align-items: center; gap: var(--nz-space-3);
  width: 100%; min-height: var(--nz-tap-min);
  padding: var(--nz-space-3) var(--nz-space-4);
  background: none; border: 0; border-radius: var(--nz-radius-md);
  color: var(--nz-text); font: inherit; text-align: left;
}
.nz-collapse-trigger:hover { background: var(--nz-surface-hover); }
.nz-collapse-trigger svg:first-child { width: 18px; height: 18px; flex: none; color: var(--nz-text-muted); }
.nz-collapse-text { flex: 1; min-width: 0; }
.nz-collapse-title { display: block; font-size: var(--nz-text-md); font-weight: var(--nz-weight-medium); }
.nz-collapse-desc { display: block; font-size: var(--nz-text-xs); color: var(--nz-text-muted); }
.nz-collapse-arrow {
  width: 16px; height: 16px; flex: none; color: var(--nz-text-muted);
  transition: rotate var(--nz-duration-base) var(--nz-ease-out);
}
.nz-collapse-trigger[aria-expanded="true"] .nz-collapse-arrow { rotate: 180deg; }
/* Variante nativa com <details>/<summary>: teclado, aria-expanded implícito e
   funcionamento sem JS vêm de graça. Reusa a mesma pele do gatilho por botão
   para que os dois não pareçam componentes diferentes. */
.nz-collapse > summary {
  display: flex; align-items: center; gap: var(--nz-space-3);
  min-height: var(--nz-tap-min); padding: var(--nz-space-3) var(--nz-space-4);
  cursor: pointer; list-style: none; border-radius: var(--nz-radius-md);
}
.nz-collapse > summary::-webkit-details-marker { display: none; }
.nz-collapse > summary:hover { background: var(--nz-surface-hover); }
.nz-collapse > summary:focus-visible { outline: 2px solid var(--nz-border-focus); outline-offset: -2px; }
.nz-collapse > summary .nz-collapse-title { flex: 1; min-width: 0; }
.nz-collapse > summary > svg:last-child {
  width: 16px; height: 16px; flex: none; color: var(--nz-text-muted);
  transition: rotate var(--nz-duration-base) var(--nz-ease-out);
}
.nz-collapse[open] > summary > svg:last-child { rotate: 180deg; }
@media (prefers-reduced-motion: reduce) {
  .nz-collapse > summary > svg:last-child { transition: none; }
}

/* Barra de salvamento colada ao rodapé da área de conteúdo. Aparece só quando
   há mudança pendente: uma configuração de cinco cards não pode obrigar a
   rolar até o fim para salvar, nem deixar dúvida sobre haver algo por salvar. */
.nz-barra-salvar {
  position: sticky; bottom: var(--nz-space-4); z-index: var(--nz-z-sticky);
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--nz-space-4); flex-wrap: wrap;
  padding: var(--nz-space-3) var(--nz-space-4);
  border: 1px solid var(--nz-border-strong); border-radius: var(--nz-radius-md);
  background: var(--nz-surface-raised); box-shadow: var(--nz-shadow-lg);
}
.nz-barra-salvar[hidden] { display: none; }

.nz-collapse-panel {
  display: grid; gap: var(--nz-space-4);
  padding: var(--nz-space-4);
  border-top: 1px solid var(--nz-border);
}

/* Campo de senha com botão mostrar/ocultar dentro do próprio campo */
.nz-password { position: relative; display: flex; align-items: center; }
.nz-password .nz-input { padding-right: var(--nz-space-12); }
.nz-password button {
  position: absolute; right: var(--nz-space-1);
  display: grid; place-items: center;
  width: 36px; height: 36px; border: 0; border-radius: var(--nz-radius-sm);
  background: transparent; color: var(--nz-text-muted);
}
.nz-password button:hover { background: var(--nz-surface-hover); color: var(--nz-text); }
.nz-password button svg { width: 16px; height: 16px; }

/* ==========================================================================
   Torradas — confirmação do que acabou de acontecer
   Não roubam foco; anunciadas por aria-live para leitor de tela.
   ========================================================================== */
.nz-toasts {
  position: fixed; z-index: var(--nz-z-toast);
  bottom: var(--nz-space-4); right: var(--nz-space-4);
  display: flex; flex-direction: column; gap: var(--nz-space-2);
  pointer-events: none; max-width: min(92vw, 380px);
}
.nz-toast {
  display: flex; align-items: flex-start; gap: var(--nz-space-3);
  padding: var(--nz-space-3) var(--nz-space-4);
  background: var(--nz-surface-raised);
  border: 1px solid var(--nz-border); border-left: 3px solid var(--nz-primary);
  border-radius: var(--nz-radius-md); box-shadow: var(--nz-shadow-lg);
  font-size: var(--nz-text-sm); pointer-events: auto;
  animation: nz-toast-in var(--nz-duration-slow) var(--nz-ease-out);
}
.nz-toast[data-tone="success"] { border-left-color: var(--nz-success); }
.nz-toast[data-tone="danger"] { border-left-color: var(--nz-danger); }
.nz-toast > svg { width: 18px; height: 18px; flex: none; margin-top: 1px; color: var(--nz-text-muted); }
.nz-toast-body { flex: 1; min-width: 0; display: grid; gap: 2px; }
.nz-toast-title { font-weight: var(--nz-weight-semibold); }
.nz-toast-desc {
  font-size: var(--nz-text-xs); color: var(--nz-text-muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.nz-toast-action { align-self: center; flex: none; white-space: nowrap; }
.nz-toast-action {
  background: none; border: 0; padding: 0;
  color: var(--nz-primary-subtle-fg); font: inherit; font-size: var(--nz-text-xs);
  font-weight: var(--nz-weight-semibold); text-decoration: underline;
}
@keyframes nz-toast-in { from { opacity: 0; translate: 0 8px; } to { opacity: 1; translate: 0 0; } }

@media (max-width: 560px) {
  .nz-toasts { left: var(--nz-space-4); right: var(--nz-space-4); max-width: none; }
}

/* ==========================================================================
   Rodapé — o que é raro e legal mora aqui, não no chrome do app
   ========================================================================== */
.nz-footer {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--nz-space-4); flex-wrap: wrap;
  padding: var(--nz-space-5) var(--nz-space-6);
  border-top: 1px solid var(--nz-border);
  font-size: var(--nz-text-xs); color: var(--nz-text-muted);
}
.nz-footer nav { display: flex; align-items: center; gap: var(--nz-space-4); flex-wrap: wrap; }
.nz-footer a { color: var(--nz-text-muted); text-decoration: none; }
.nz-footer a:hover { color: var(--nz-text); text-decoration: underline; }

/* ==========================================================================
   Seletor de loja — lista com busca quando cresce
   ========================================================================== */
.nz-store-list { max-height: 280px; overflow-y: auto; display: grid; gap: 1px; }
.nz-store-item {
  display: flex; align-items: center; gap: var(--nz-space-3);
  width: 100%; min-height: var(--nz-tap-min);
  padding: var(--nz-space-2) var(--nz-space-3);
  background: none; border: 0; border-radius: var(--nz-radius-md);
  color: var(--nz-text); font: inherit; font-size: var(--nz-text-sm); text-align: left;
}
.nz-store-item:hover { background: var(--nz-surface-hover); }
.nz-store-item[aria-current="true"] { background: var(--nz-primary-subtle); color: var(--nz-primary-subtle-fg); font-weight: var(--nz-weight-semibold); }
.nz-store-item .nz-store-mark { width: 26px; height: 26px; font-size: var(--nz-text-2xs); }
.nz-store-item-name { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* ==========================================================================
   Cabeçalho da loja — identidade + atalho para a vitrine na mesma linha
   ========================================================================== */
.nz-store-header {
  display: flex; align-items: center; gap: var(--nz-space-1);
  padding-right: var(--nz-space-2);
  border-bottom: 1px solid var(--nz-border);
}
.nz-store-header .nz-store-switcher { border-bottom: 0; }
.nz-shell[data-nav="collapsed"] .nz-ver-loja { display: none; }

/* Separador entre o bloco de ajuda e o bloco institucional no rodapé */
.nz-footer-sep {
  width: 1px; height: 12px; background: var(--nz-border); flex: none;
}

/* Coluna de cards dentro de um grid de duas colunas */
.nz-coluna { display: flex; flex-direction: column; gap: var(--nz-space-4); min-width: 0; }

/* ==========================================================================
   Paleta de comandos (⌘K)
   Atalho de quem já sabe para onde vai. Convive com a navegação lateral em vez
   de substituí-la: o menu ensina o que existe, a paleta encurta o caminho.
   ========================================================================== */

/* O gatilho na topbar tem cara de campo mas é botão — ele não recebe texto,
   abre a paleta. Fingir um input que não aceita digitação é mentira de UI. */
.nz-search-trigger {
  display: flex; align-items: center; gap: var(--nz-space-2);
  flex: 0 1 340px; height: 36px;
  padding: 0 var(--nz-space-2) 0 var(--nz-space-3);
  background: var(--nz-surface-sunken);
  border: 1px solid var(--nz-border); border-radius: var(--nz-radius-md);
  color: var(--nz-text-muted); font: inherit; font-size: var(--nz-text-sm);
  text-align: left;
  transition: border-color var(--nz-duration-fast) var(--nz-ease-out);
}
.nz-search-trigger:hover { border-color: var(--nz-border-strong); }
.nz-search-trigger > svg { width: 16px; height: 16px; flex: none; }
.nz-search-trigger-label { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nz-search-trigger kbd {
  padding: 2px var(--nz-space-2);
  background: var(--nz-surface); border: 1px solid var(--nz-border);
  border-radius: var(--nz-radius-sm);
  font-family: var(--nz-font-mono); font-size: var(--nz-text-2xs);
  color: var(--nz-text-muted); flex: none;
}

.nz-palette-backdrop {
  position: fixed; inset: 0; z-index: var(--nz-z-modal);
  background: var(--nz-overlay); backdrop-filter: blur(2px);
  opacity: 0; pointer-events: none;
  transition: opacity var(--nz-duration-base) var(--nz-ease-out);
}
.nz-palette-backdrop[data-open="true"] { opacity: 1; pointer-events: auto; }

/* O [hidden] explícito porque a paleta vive FORA do .nz-app (no fim do body),
   onde a guarda `.nz-app [hidden]` não alcança. Sem isto ela fica display:flex
   com opacity 0 — um escudo de cliques invisível sobre a faixa do topo, que
   passou despercebido até a régua de abas de Vendedores ser a primeira coisa
   clicável a morar naquela altura: "Perfis de acesso" só clicava até o "f". */
.nz-palette[hidden] { display: none; }

.nz-palette {
  position: fixed; z-index: calc(var(--nz-z-modal) + 1);
  top: 12vh; left: 50%; translate: -50% 0;
  width: min(92vw, 640px); max-height: 70vh;
  display: flex; flex-direction: column;
  background: var(--nz-surface-raised);
  border: 1px solid var(--nz-border); border-radius: var(--nz-radius-xl);
  box-shadow: var(--nz-shadow-xl);
  overflow: hidden;
  opacity: 0; scale: 0.98;
  transition: opacity var(--nz-duration-base) var(--nz-ease-out),
              scale var(--nz-duration-base) var(--nz-ease-out);
}
.nz-palette[data-open="true"] { opacity: 1; scale: 1; }

.nz-palette-field {
  display: flex; align-items: center; gap: var(--nz-space-3);
  padding: var(--nz-space-4);
  border-bottom: 1px solid var(--nz-border);
}
.nz-palette-field > svg { width: 18px; height: 18px; flex: none; color: var(--nz-text-muted); }
.nz-palette-field input {
  flex: 1; min-width: 0; height: 24px;
  background: none; border: 0; outline: none;
  color: var(--nz-text); font: inherit; font-size: var(--nz-text-lg);
}
.nz-palette-field input::placeholder { color: var(--nz-text-muted); }
.nz-palette-field kbd {
  padding: 2px var(--nz-space-2);
  background: var(--nz-surface-sunken); border: 1px solid var(--nz-border);
  border-radius: var(--nz-radius-sm);
  font-family: var(--nz-font-mono); font-size: var(--nz-text-2xs);
  color: var(--nz-text-muted); flex: none;
}

.nz-palette-results { flex: 1; overflow-y: auto; padding: var(--nz-space-2); }
.nz-palette-group {
  padding: var(--nz-space-3) var(--nz-space-3) var(--nz-space-1);
  font-size: var(--nz-text-2xs); font-weight: var(--nz-weight-semibold);
  letter-spacing: var(--nz-tracking-caps); text-transform: uppercase;
  color: var(--nz-text-muted);
}

.nz-palette-item {
  display: flex; align-items: center; gap: var(--nz-space-3);
  width: 100%; min-height: 40px;
  padding: var(--nz-space-2) var(--nz-space-3);
  background: none; border: 0; border-radius: var(--nz-radius-md);
  color: var(--nz-text); font: inherit; font-size: var(--nz-text-sm);
  text-align: left; cursor: pointer;
}
.nz-palette-item > svg { width: 16px; height: 16px; flex: none; color: var(--nz-text-muted); }
.nz-palette-item-label { flex: 1; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nz-palette-item-meta { flex: none; font-size: var(--nz-text-xs); color: var(--nz-text-muted); font-variant-numeric: tabular-nums; }
.nz-palette-item mark { background: none; color: var(--nz-primary-subtle-fg); font-weight: var(--nz-weight-semibold); }

/* Seleção do teclado e hover são o MESMO estado visual: a lista tem um único
   "item corrente", senão mouse e teclado brigam por destaque. */
.nz-palette-item[aria-selected="true"] { background: var(--nz-surface-hover); }
.nz-palette-item[aria-selected="true"] > svg { color: var(--nz-primary); }
.nz-palette-item[aria-selected="true"] .nz-palette-enter { opacity: 1; }
.nz-palette-enter {
  flex: none; opacity: 0;
  padding: 1px var(--nz-space-2);
  background: var(--nz-surface); border: 1px solid var(--nz-border);
  border-radius: var(--nz-radius-sm);
  font-family: var(--nz-font-mono); font-size: var(--nz-text-2xs);
  color: var(--nz-text-muted);
}

.nz-palette-foot {
  display: flex; align-items: center; gap: var(--nz-space-4);
  padding: var(--nz-space-2) var(--nz-space-4);
  border-top: 1px solid var(--nz-border);
  background: var(--nz-surface-sunken);
  font-size: var(--nz-text-2xs); color: var(--nz-text-muted);
}
.nz-palette-foot span { display: inline-flex; align-items: center; gap: var(--nz-space-1); }
.nz-palette-foot kbd {
  padding: 0 4px; min-width: 16px; text-align: center;
  background: var(--nz-surface); border: 1px solid var(--nz-border);
  border-radius: 3px; font-family: var(--nz-font-mono);
}

@media (max-width: 640px) {
  /* Em tela pequena a paleta ocupa tudo: teclado virtual + lista precisam do
     espaço inteiro, e um painel flutuante fica espremido contra o teclado. */
  .nz-palette {
    top: 0; left: 0; translate: none;
    width: 100%; max-height: 100dvh; height: 100dvh;
    border: 0; border-radius: 0;
  }
  .nz-palette-field kbd, .nz-palette-foot { display: none; }
  .nz-search-trigger { display: none; }
}

/* ==========================================================================
   Relatórios — barra de proporção
   Usada no funil e nas distribuições. Barra em CSS em vez de SVG: o valor e a
   porcentagem ficam como TEXTO, que é o que o leitor de tela lê e o que o
   olho procura primeiro. A barra é o reforço visual, não o dado.
   ========================================================================== */
.nz-barra-linha {
  display: grid;
  /* Coluna de valor com largura fixa: com "auto" cada linha dimensiona pelo
     próprio número e os trilhos deixam de começar e terminar no mesmo lugar —
     comparar comprimento de barra vira impossível. */
  grid-template-columns: minmax(120px, 1.4fr) minmax(0, 3fr) 180px;
  align-items: center; gap: var(--nz-space-4);
  padding: var(--nz-space-3) var(--nz-space-5);
  border-bottom: 1px solid var(--nz-border);
}
.nz-barra-linha:last-child { border-bottom: 0; }
.nz-barra-rotulo { font-size: var(--nz-text-sm); font-weight: var(--nz-weight-medium); }
.nz-barra-sub { display: block; font-size: var(--nz-text-xs); color: var(--nz-text-muted); }

/* Linha que não é uma entidade comparável — "Sem vendedor atribuído" é um
   agregado do que ninguém atendeu. Recuar o peso evita que seja lida como
   mais um membro da equipe. */
.nz-table tr[data-muted="true"] .nz-cell-primary { color: var(--nz-text-secondary); font-weight: 400; }

/* Nota de rodapé da tabela: onde mora a definição da métrica. Sem ela,
   "conversão" é um número que cada leitor calcula na cabeça de um jeito. */
/* Subtítulo do card: onde mora a instrução de leitura do gráfico. Sem ela,
   uma barra que mede taxa de passagem é lida como se medisse volume. */
.nz-card-sub {
  margin: var(--nz-space-1) 0 0; font-size: var(--nz-text-xs);
  color: var(--nz-text-muted); line-height: 1.5;
}

.nz-table-nota code {
  font-family: var(--nz-font-mono); font-size: 0.95em;
  padding: 1px 4px; border-radius: var(--nz-radius-sm);
  background: var(--nz-neutral-subtle); color: var(--nz-text-secondary);
}
.nz-table-nota {
  margin: 0; padding: var(--nz-space-3) var(--nz-space-5) var(--nz-space-4);
  font-size: var(--nz-text-xs); line-height: 1.6; color: var(--nz-text-muted);
  border-top: 1px solid var(--nz-border);
}

.nz-barra-trilho {
  /* display: block é obrigatório — height e width não se aplicam a elemento
     inline, e o trilho/preenchimento são <span>. */
  display: block;
  position: relative; height: 22px; border-radius: var(--nz-radius-sm);
  /* surface-sunken quase não se distingue do card no tema escuro — o trilho
     precisa ser visível para a barra comunicar proporção. */
  background: var(--nz-neutral-subtle); overflow: hidden;
}
.nz-barra-preenche {
  display: block; height: 100%; min-width: 2px; border-radius: var(--nz-radius-sm);
  background: var(--nz-data-1);
  transition: width var(--nz-duration-slow) var(--nz-ease-out);
}
.nz-barra-preenche[data-tone="perda"] { background: var(--nz-danger); }
.nz-barra-preenche[data-tone="neutro"] { background: var(--nz-border-strong); }

.nz-barra-valor {
  display: flex; align-items: baseline; justify-content: flex-end; gap: var(--nz-space-2);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.nz-barra-num { font-size: var(--nz-text-md); font-weight: var(--nz-weight-semibold); }
.nz-barra-pct { font-size: var(--nz-text-xs); color: var(--nz-text-muted); min-width: 46px; text-align: right; }

/* A queda entre etapas é a informação do funil — ela ganha destaque próprio */
.nz-queda {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 1px var(--nz-space-2); border-radius: var(--nz-radius-sm);
  font-size: var(--nz-text-2xs); font-weight: var(--nz-weight-semibold);
  background: var(--nz-neutral-subtle); color: var(--nz-neutral-subtle-fg);
  font-variant-numeric: tabular-nums;
}
.nz-queda[data-pior="true"] { background: var(--nz-danger-subtle); color: var(--nz-danger-subtle-fg); }
.nz-queda svg { width: 10px; height: 10px; }

@media (max-width: 768px) {
  .nz-barra-linha { grid-template-columns: 1fr auto; gap: var(--nz-space-2) var(--nz-space-3); }
  .nz-barra-trilho { grid-column: 1 / -1; order: 3; }
}

/* ==========================================================================
   Catálogos de venda
   Um catálogo é uma seleção curada de produtos com link próprio. É um objeto
   VISUAL e de baixo volume (a loja tem 5, não 500) — por isso grade de cards
   com capa, e não a tabela que Pedidos e Produtos usam. Tabela de 5 linhas
   com uma coluna "Link" gasta a largura toda para não mostrar nada.
   ========================================================================== */
/* Luminosidade do mosaico por tema: a mesma matiz precisa de passos
   diferentes no claro e no escuro para não estourar nem sumir. */
:root { --nz-mosaico-l: 88%; }
[data-theme="dark"] { --nz-mosaico-l: 26%; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) { --nz-mosaico-l: 26%; }
}

.nz-cat-grid {
  display: grid; gap: var(--nz-space-4);
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}

.nz-cat-card {
  display: flex; flex-direction: column;
  background: var(--nz-surface); border: 1px solid var(--nz-border);
  border-radius: var(--nz-radius-lg); overflow: hidden;
  transition: border-color var(--nz-duration-fast) var(--nz-ease-out),
              box-shadow var(--nz-duration-fast) var(--nz-ease-out);
}
.nz-cat-card:hover { border-color: var(--nz-border-strong); box-shadow: var(--nz-shadow-sm); }
.nz-cat-card:focus-within { border-color: var(--nz-primary); }

/* aspect-ratio reserva o espaço antes da imagem carregar — sem isto o card
   pula de altura e a grade inteira reflui (CLS). */
.nz-cat-capa {
  position: relative; aspect-ratio: 16 / 10;
  background: var(--nz-neutral-subtle);
  display: flex; align-items: center; justify-content: center;
  border-bottom: 1px solid var(--nz-border);
}
.nz-cat-capa img { width: 100%; height: 100%; object-fit: cover; display: block; }
.nz-cat-capa-vazia {
  display: flex; flex-direction: column; align-items: center; gap: var(--nz-space-1);
  color: var(--nz-text-muted); font-size: var(--nz-text-xs);
}
.nz-cat-capa-vazia svg { width: 28px; height: 28px; opacity: 0.6; }

/* Mosaico das primeiras imagens do catálogo quando não há capa definida:
   um catálogo sem capa ainda tem produtos, e mostrar do que ele trata é mais
   útil que um ícone de arquivo. */
.nz-cat-mosaico { display: grid; grid-template-columns: 1fr 1fr; width: 100%; height: 100%; }
.nz-cat-mosaico span {
  background-size: cover; background-position: center;
  background-color: var(--nz-neutral-subtle);
  border-right: 1px solid var(--nz-surface); border-bottom: 1px solid var(--nz-surface);
}

/* No preview de 120px o rótulo não cabe e vaza por cima da borda; o ícone
   sozinho já diz que não há capa. */
#catCapaPreview .nz-cat-capa-vazia { font-size: 0; gap: 0; }
#catCapaPreview .nz-cat-capa-vazia svg { width: 22px; height: 22px; }

.nz-cat-estado { position: absolute; top: var(--nz-space-2); left: var(--nz-space-2); }

/* Em coluna única a capa 16:10 ocupa quase metade da tela por card e obriga a
   rolar um card de cada vez. Achatar devolve a lista à condição de lista. */
@media (max-width: 620px) {
  .nz-cat-capa { aspect-ratio: 21 / 9; }
}

.nz-cat-corpo {
  display: flex; flex-direction: column; gap: var(--nz-space-1);
  padding: var(--nz-space-4) var(--nz-space-4) var(--nz-space-3);
  flex: 1;
}
.nz-cat-nome {
  margin: 0; font-size: var(--nz-text-md); font-weight: var(--nz-weight-semibold);
  line-height: var(--nz-leading-tight);
  /* Nome longo trunca em 2 linhas em vez de esticar o card e desalinhar a grade */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.nz-cat-meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--nz-space-2);
  font-size: var(--nz-text-xs); color: var(--nz-text-muted);
}
.nz-cat-meta-sep { opacity: 0.5; }

.nz-cat-rodape {
  display: flex; align-items: center; gap: var(--nz-space-1);
  padding: var(--nz-space-2) var(--nz-space-3);
  border-top: 1px solid var(--nz-border);
  background: var(--nz-surface-sunken);
}
.nz-cat-rodape .nz-btn { flex: 0 0 auto; }
.nz-cat-rodape-espaco { flex: 1; }

/* --------------------------------------------------------------------------
   Editor: curar produtos é a tarefa; o formulário é configuração
   Por isso os produtos ficam na coluna principal e os campos na lateral. Seis
   campos empilhados no topo empurrariam a tarefa real para baixo da dobra.
   -------------------------------------------------------------------------- */
.nz-editor {
  display: grid; gap: var(--nz-space-6);
  grid-template-columns: minmax(0, 1fr) 340px;
  align-items: start;
}
.nz-editor-lateral {
  display: flex; flex-direction: column; gap: var(--nz-space-6);
  position: sticky; top: var(--nz-space-6);
}
@media (max-width: 1100px) {
  .nz-editor { grid-template-columns: minmax(0, 1fr); }
  /* Na coluna única a configuração vem PRIMEIRO: o nome é obrigatório e um
     catálogo sem nome não salva. */
  .nz-editor-lateral { position: static; order: -1; }
}

/* Linha de produto selecionado. Grid explícito para que alça, imagem, texto e
   ações fiquem alinhados entre linhas independentemente do tamanho do nome. */
/* Separa a área de adicionar da lista já montada. Sem a linha, o campo de
   busca parece flutuar sobre o primeiro produto. */
#catListaProdutos:not(:empty) { border-top: 1px solid var(--nz-border); }

.nz-metrica-linha {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--nz-space-3); padding: var(--nz-space-3) var(--nz-space-5);
  border-top: 1px solid var(--nz-border);
}
.nz-metrica-num {
  font-size: var(--nz-text-xl); font-weight: var(--nz-weight-semibold);
  font-variant-numeric: tabular-nums;
}

.nz-item-prod {
  display: grid; align-items: center; gap: var(--nz-space-3);
  grid-template-columns: auto 48px minmax(0, 1fr) auto auto;
  padding: var(--nz-space-3) var(--nz-space-4);
  border-bottom: 1px solid var(--nz-border);
  background: var(--nz-surface);
}
.nz-item-prod:last-child { border-bottom: 0; }
.nz-item-prod[data-arrastando="true"] { opacity: 0.4; }
.nz-item-prod[data-alvo="true"] { box-shadow: inset 0 2px 0 var(--nz-primary); }

.nz-alca {
  display: flex; align-items: center; justify-content: center;
  width: 28px; height: 32px; padding: 0; border: 0; border-radius: var(--nz-radius-sm);
  background: transparent; color: var(--nz-text-muted); cursor: grab;
}
.nz-alca:hover { background: var(--nz-neutral-subtle); color: var(--nz-text-secondary); }
.nz-alca:active { cursor: grabbing; }
.nz-alca svg { width: 16px; height: 16px; }

.nz-item-thumb {
  width: 48px; height: 48px; border-radius: var(--nz-radius-sm);
  object-fit: cover; background: var(--nz-neutral-subtle);
  border: 1px solid var(--nz-border);
}
.nz-item-txt { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.nz-item-nome {
  font-size: var(--nz-text-sm); font-weight: var(--nz-weight-medium);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nz-item-sub {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--nz-space-2);
  font-size: var(--nz-text-xs); color: var(--nz-text-muted);
}
.nz-item-preco { font-size: var(--nz-text-sm); font-variant-numeric: tabular-nums; }

/* Setas ↑↓ ao lado da alça: arrastar precisa de alternativa por teclado, e
   quem usa leitor de tela ou toque impreciso não tem como arrastar. */
.nz-item-mover { display: flex; gap: 2px; }

/* Resultado da busca de produtos, ancorado ao campo */
.nz-busca-prod { position: relative; }
.nz-busca-res {
  position: absolute; z-index: 20; left: 0; right: 0; top: calc(100% + 4px);
  max-height: 320px; overflow-y: auto;
  background: var(--nz-surface); border: 1px solid var(--nz-border);
  border-radius: var(--nz-radius-md); box-shadow: var(--nz-shadow-lg);
  padding: var(--nz-space-1);
}
.nz-busca-item {
  display: grid; align-items: center; gap: var(--nz-space-3);
  grid-template-columns: 36px minmax(0, 1fr) auto;
  width: 100%; padding: var(--nz-space-2); border: 0; border-radius: var(--nz-radius-sm);
  background: transparent; color: inherit; text-align: left; cursor: pointer;
}
.nz-busca-item:hover, .nz-busca-item[data-ativo="true"] { background: var(--nz-neutral-subtle); }
.nz-busca-item[disabled] { opacity: 0.45; cursor: default; }
.nz-busca-item img {
  width: 36px; height: 36px; border-radius: var(--nz-radius-sm); object-fit: cover;
  background: var(--nz-neutral-subtle);
}

/* Bloco de link compartilhável */
.nz-link-box {
  display: flex; align-items: center; gap: var(--nz-space-2);
  padding: var(--nz-space-2); border: 1px solid var(--nz-border);
  border-radius: var(--nz-radius-md); background: var(--nz-surface-sunken);
}
.nz-link-box input {
  flex: 1; min-width: 0; border: 0; background: transparent; color: var(--nz-text-secondary);
  font-family: var(--nz-font-mono); font-size: var(--nz-text-xs); padding: 0 var(--nz-space-1);
}
.nz-link-box input:focus { outline: none; }

.nz-qr {
  display: block; width: 128px; height: 128px; margin: 0 auto;
  padding: var(--nz-space-2); border-radius: var(--nz-radius-md);
  background: #FFFFFF; /* QR precisa de fundo branco real, inclusive no escuro */
}

/* Marca o que é proposta e ainda não existe no painel, para o protótipo não
   ser confundido com o sistema atual. */
.nz-sugestao {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 1px 6px; border-radius: var(--nz-radius-sm);
  font-size: 10px; font-weight: var(--nz-weight-semibold);
  letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--nz-info-subtle-fg); background: var(--nz-info-subtle);
  border: 1px dashed var(--nz-info);
}
/* O selo divide o nome com a opção do autocompletar (button.nz-sugestao,
   acima), que tem width: 100% — no selo isso virava uma faixa inteira. */
span.nz-sugestao { width: auto; border-bottom: 1px dashed var(--nz-info); }
/* Dentro de um botão colorido o par info-sobre-danger não tem contraste.
   Herdar a cor do botão e marcar só pelo tracejado resolve nos dois temas. */
.nz-btn .nz-sugestao {
  color: inherit; background: transparent; border-color: currentColor; opacity: 0.85;
}

/* ==========================================================================
   Importar e exportar
   A importação hoje é irreversível, sem transação e sem prévia: grava metade
   e só então informa os erros. O assistente existe para inverter isso —
   conferir ANTES de gravar é a única correção que importa aqui.
   ========================================================================== */

/* Trilho de passos. Numeral + rótulo, e o estado atual anunciado por
   aria-current, não só por cor. */
.nz-passos {
  display: flex; align-items: flex-start; gap: var(--nz-space-2);
  list-style: none; margin: 0; padding: 0; overflow-x: auto;
}
.nz-passo {
  display: flex; align-items: center; gap: var(--nz-space-3);
  flex: 1 1 0; min-width: 0;
}
.nz-passo-marca {
  display: grid; place-items: center; flex: none;
  width: 28px; height: 28px; border-radius: 50%;
  border: 1px solid var(--nz-border-strong);
  font-size: var(--nz-text-xs); font-weight: var(--nz-weight-semibold);
  font-variant-numeric: tabular-nums;
  color: var(--nz-text-muted); background: var(--nz-surface);
}
.nz-passo-marca svg { width: 15px; height: 15px; }
.nz-passo-txt { display: grid; gap: 1px; min-width: 0; }
.nz-passo-nome {
  font-size: var(--nz-text-sm); font-weight: var(--nz-weight-medium);
  color: var(--nz-text-muted); white-space: nowrap;
}
.nz-passo-desc { font-size: var(--nz-text-xs); color: var(--nz-text-muted); }

.nz-passo[data-estado="atual"] .nz-passo-marca {
  border-color: var(--nz-primary); background: var(--nz-primary); color: var(--nz-primary-fg);
}
.nz-passo[data-estado="atual"] .nz-passo-nome { color: var(--nz-text); }
.nz-passo[data-estado="feito"] .nz-passo-marca {
  border-color: var(--nz-success); background: var(--nz-success-subtle); color: var(--nz-success-subtle-fg);
}
.nz-passo[data-estado="feito"] .nz-passo-nome { color: var(--nz-text-secondary); }

/* A linha liga os passos; ela some no último para não apontar para o vazio. */
.nz-passo::after {
  content: ""; flex: 1; height: 1px; min-width: 12px;
  background: var(--nz-border); margin-left: var(--nz-space-2);
}
.nz-passo:last-child { flex: none; }
.nz-passo:last-child::after { display: none; }
@media (max-width: 760px) {
  /* Só o passo atual mostra texto; os demais viram numerais. A descrição sai de
     vez — em pouco mais de 200px ela quebra "planilha e tipo" em três linhas e
     desalinha a fileira inteira. */
  .nz-passo-txt, .nz-passo::after { display: none; }
  .nz-passo { flex: none; }
  .nz-passo[data-estado="atual"] { flex: 1 1 auto; min-width: 0; }
  .nz-passo[data-estado="atual"] .nz-passo-txt { display: block; min-width: 0; }
  .nz-passo-desc { display: none; }
  .nz-passo-nome { overflow: hidden; text-overflow: ellipsis; }
  .nz-passos { gap: var(--nz-space-3); align-items: center; }
}

/* Linha de caixa de seleção com rótulo. O design system tinha o estilo do
   input (.nz-check) mas nenhum invólucro, então cada tela alinhava do seu
   jeito. Alvo de 44px vem do padding, não do tamanho do quadradinho. */
.nz-opcao {
  display: flex; align-items: center; gap: var(--nz-space-3);
  min-height: var(--nz-tap-min); padding: var(--nz-space-1) 0;
  font-size: var(--nz-text-sm); cursor: pointer;
}
.nz-opcao input[type="checkbox"], .nz-opcao input[type="radio"] {
  width: 16px; height: 16px; flex: none;
  accent-color: var(--nz-primary); cursor: pointer;
}
.nz-opcao:has(input:disabled) { color: var(--nz-text-disabled); cursor: default; }

/* Opção TRAVADA LIGADA é diferente de opção indisponível, e a regra acima
   confundia as duas: as duas usam `disabled`, mas uma significa "você não pode
   escolher isto" e a outra "isto está sempre incluído". Pintadas iguais, a segunda
   se lê como a primeira — foi assim que "Início — sempre" no editor de perfis foi
   lido como "Início bloqueado" (§60.4).

   Aqui o texto volta à cor normal, porque a opção está ATIVA. O que muda é o
   cursor e o selo ao lado. */
/* `flex-wrap` só na variante travada: ela é a única linha com selo E dica além do
   nome, e sem o wrap a dica era espremida numa coluna de quatro palavras
   empilhadas no celular. As opções normais continuam sem wrap, que é o certo para
   caixa + nome. */
.nz-opcao[data-fixo]:has(input:disabled) { color: var(--nz-text); cursor: default; flex-wrap: wrap; }
.nz-opcao[data-fixo] input:disabled { opacity: 1; }
.nz-opcao-selo {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 1px var(--nz-space-2);
  border-radius: var(--nz-radius-pill);
  background: var(--nz-primary-subtle); color: var(--nz-primary-subtle-fg);
  font-size: var(--nz-text-xs); font-weight: var(--nz-weight-medium);
  white-space: nowrap;
}
.nz-opcao-selo svg { width: 11px; height: 11px; }

/* Zona de arquivo. label envolvendo input file: clique e teclado funcionam
   de graça, sem interceptar evento nem simular clique. */
.nz-drop {
  display: flex; flex-direction: column; align-items: center; gap: var(--nz-space-2);
  padding: var(--nz-space-10) var(--nz-space-6); text-align: center; cursor: pointer;
  border: 1px dashed var(--nz-border-strong); border-radius: var(--nz-radius-lg);
  background: var(--nz-surface-sunken);
  transition: border-color var(--nz-duration-fast) var(--nz-ease-out),
              background var(--nz-duration-fast) var(--nz-ease-out);
}
.nz-drop:hover { border-color: var(--nz-primary); }
.nz-drop:focus-within { border-color: var(--nz-primary); outline: 2px solid var(--nz-border-focus); outline-offset: 2px; }
.nz-drop[data-sobre="true"] { border-color: var(--nz-primary); background: var(--nz-primary-subtle); }
.nz-drop input[type="file"] { position: absolute; width: 1px; height: 1px; opacity: 0; }
.nz-drop-icone { color: var(--nz-text-muted); }
.nz-drop-icone svg { width: 30px; height: 30px; }
.nz-drop-titulo { font-size: var(--nz-text-md); font-weight: var(--nz-weight-medium); }
.nz-drop-sub { font-size: var(--nz-text-xs); color: var(--nz-text-muted); }

.nz-arquivo {
  display: flex; align-items: center; gap: var(--nz-space-3);
  padding: var(--nz-space-3) var(--nz-space-4);
  border: 1px solid var(--nz-border); border-radius: var(--nz-radius-md);
  background: var(--nz-surface);
}
.nz-arquivo-icone {
  display: grid; place-items: center; flex: none; width: 36px; height: 36px;
  border-radius: var(--nz-radius-sm);
  background: var(--nz-primary-subtle); color: var(--nz-primary-subtle-fg);
}
.nz-arquivo-icone svg { width: 18px; height: 18px; }
.nz-arquivo-txt { flex: 1; min-width: 0; display: grid; gap: 1px; }

/* Mapeamento coluna→campo. Grid de 3 colunas para que a seta fique alinhada
   entre linhas mesmo com nomes de tamanhos diferentes. */
.nz-mapa {
  display: grid; align-items: center; gap: var(--nz-space-3);
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  padding: var(--nz-space-3) var(--nz-space-5);
  border-bottom: 1px solid var(--nz-border);
}
.nz-mapa:last-child { border-bottom: 0; }
.nz-mapa-origem { display: grid; gap: 2px; min-width: 0; }
.nz-mapa-col {
  font-family: var(--nz-font-mono); font-size: var(--nz-text-xs);
  color: var(--nz-text-muted);
}
.nz-mapa-amostra {
  font-size: var(--nz-text-sm);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nz-mapa-seta { color: var(--nz-text-muted); }
.nz-mapa-seta svg { width: 16px; height: 16px; display: block; }

/* Cartões de resumo do que a importação vai fazer. Cada um é um número e um
   verbo: novos, atualizados, ignorados. */
.nz-impacto {
  display: grid; gap: var(--nz-space-3);
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}
.nz-impacto-card {
  display: grid; gap: 2px;
  padding: var(--nz-space-4); border-radius: var(--nz-radius-md);
  border: 1px solid var(--nz-border); background: var(--nz-surface);
}
.nz-impacto-num {
  font-size: var(--nz-text-2xl); font-weight: var(--nz-weight-semibold);
  font-variant-numeric: tabular-nums; line-height: 1.1;
}
.nz-impacto-rot { font-size: var(--nz-text-sm); color: var(--nz-text-secondary); }
.nz-impacto-sub { font-size: var(--nz-text-xs); color: var(--nz-text-muted); }
.nz-impacto-card[data-tone="danger"] { border-color: var(--nz-danger); }
.nz-impacto-card[data-tone="danger"] .nz-impacto-num { color: var(--nz-danger-subtle-fg); }
.nz-impacto-card[data-tone="warning"] { border-color: var(--nz-warning); }

/* Erro por linha da planilha. O número da linha é o dado que permite abrir o
   arquivo e corrigir — por isso vem primeiro e em fonte mono. */
.nz-erro-linha {
  display: grid; align-items: baseline; gap: var(--nz-space-3);
  grid-template-columns: auto minmax(0, 1fr);
  padding: var(--nz-space-3) var(--nz-space-5);
  border-bottom: 1px solid var(--nz-border);
}
.nz-erro-linha:last-child { border-bottom: 0; }
.nz-erro-num {
  font-family: var(--nz-font-mono); font-size: var(--nz-text-xs);
  padding: 2px 6px; border-radius: var(--nz-radius-sm);
  background: var(--nz-danger-subtle); color: var(--nz-danger-subtle-fg);
  white-space: nowrap;
}
.nz-erro-txt { display: grid; gap: 2px; }
.nz-erro-comoresolver { font-size: var(--nz-text-xs); color: var(--nz-text-muted); }

/* Barra de progresso do processamento. role=progressbar no elemento externo;
   o preenchimento é decorativo. */
.nz-progresso {
  height: 8px; border-radius: var(--nz-radius-pill);
  background: var(--nz-neutral-subtle); overflow: hidden;
}
.nz-progresso-fill {
  display: block; height: 100%; border-radius: inherit;
  background: var(--nz-primary);
  transition: width var(--nz-duration-base) var(--nz-ease-out);
}
@media (prefers-reduced-motion: reduce) {
  .nz-progresso-fill { transition: none; }
}

/* Prévia do preço ao replicar para outra loja: digitar "10%" e não ver o
   resultado é o que faz o lojista errar a casa decimal. */
.nz-preco-previa {
  display: flex; align-items: center; gap: var(--nz-space-3); flex-wrap: wrap;
  padding: var(--nz-space-3) var(--nz-space-4);
  border-radius: var(--nz-radius-md); background: var(--nz-surface-sunken);
  border: 1px solid var(--nz-border);
  font-variant-numeric: tabular-nums;
}
.nz-preco-de { color: var(--nz-text-muted); text-decoration: line-through; }
.nz-preco-para { font-weight: var(--nz-weight-semibold); font-size: var(--nz-text-lg); }

/* ==========================================================================
   Banners (store_slide)
   A tela controla o carrossel do topo da vitrine, e hoje não mostra o
   carrossel. Sem ver o resultado, o lojista sobe uma imagem, sai do painel,
   abre a loja, volta e corrige — o ciclo inteiro por tentativa.
   ========================================================================== */

/* Prévia da vitrine: UMA tela por vez, escolhida no controle segmentado —
   Computador ou Celular. A loja troca a arte em 468px (`image_mobile` abaixo,
   `image_desktop` acima), e é arte DIFERENTE — 1920×887 contra 1200×1200 nos
   dados reais —, então ver uma não responde pela outra.

   Lado a lado foi tentado e recuado, e o motivo é de layout e não de gosto: as
   duas visualizações têm regras INCOMPATÍVEIS. O computador é largura 100% com
   altura livre; o celular é largura fixa com altura livre. Para caberem na mesma
   linha, o computador tinha de deixar de ser 100% — virava moldura de 680px com
   teto de altura na arte, e o que sobrava era tarja de fundo nas laterais de
   qualquer arte que não fosse 16:5. Uma tela por vez devolve a cada modo a regra
   que é dele, e de quebra apaga o teto de altura, o `flex-wrap` e o teto de
   largura que só existiam para forçar a convivência.

   O `data-modo` é da `.nz-vitrine` — estado da prévia, um modo por vez, que é
   onde ele já estava antes de virar identidade de moldura. */
.nz-vitrine {
  /* Alinhado ao INÍCIO e não centrado: centrado, a moldura começava 125px à
     direita do título do card, e desalinhamento de bloco grande lê-se como
     descuido. */
  display: grid; gap: var(--nz-space-3); justify-items: start;
  /* `minmax(0, ...)` é o que impede a faixa de grade de crescer com o conteúdo.
     Sem isso, um filho de largura intrínseca — um `<img>` de 1920px, por
     exemplo — dita a faixa e transborda o card, a `.nz-content` e o documento,
     porque nenhum ancestral tem `overflow-x` para conter (§82). */
  grid-template-columns: minmax(0, 1fr);
}
/* As duas telas ficam no DOM e uma se esconde: trocar de aba é instantâneo, sem
   ida ao servidor. Quem estava no banner 3 continua no banner 3 ao trocar, porque
   o `data-atual` é da prévia e vale para as duas. */
.nz-vitrine[data-modo="desktop"] .nz-vitrine-tela[data-modo="mobile"],
.nz-vitrine[data-modo="mobile"]  .nz-vitrine-tela[data-modo="desktop"] { display: none; }

.nz-vitrine-tela { margin: 0; display: grid; gap: var(--nz-space-2); min-width: 0; width: 100%; }
/* A régua do celular. 375px é a largura de celular onde a loja de fato usa
   `image_mobile` (ela troca abaixo de 468px), então é a medida honesta — e não um
   teto escolhido para caber em algum lugar. `max-width` para o card estreito. */
.nz-vitrine-tela[data-modo="mobile"] { width: 375px; max-width: 100%; }

.nz-vitrine-moldura {
  position: relative; width: 100%; overflow: hidden;
  border: 1px solid var(--nz-border); border-radius: var(--nz-radius-lg);
  background: var(--nz-neutral-subtle);
}
/* Os tetos saíram (§34.5), e não é descuido: com uma tela por vez o computador
   ocupa a régua inteira do card, como na loja, e a altura vem da proporção da
   arte. Os dois tetos existiam para fazer as duas telas conviverem numa linha —
   morreram com a convivência.

   `aspect-ratio` também não volta: fixar proporção recortaria justamente a
   mensagem que o lojista abre a prévia para conferir. O 4:5 que ele supunha para
   o celular também não é o que os arquivos são: medidos, são 1200×1200.
   .nz-vitrine[data-modo="desktop"] .nz-vitrine-moldura { max-width: 680px; aspect-ratio: 16 / 5; }
   .nz-vitrine[data-modo="mobile"]  .nz-vitrine-moldura { max-width: 300px; aspect-ratio: 4 / 5; }
   .nz-vitrine-tela[data-modo="desktop"] .nz-vitrine-moldura { max-width: 680px; } */

/* Legenda da tela: diz em que faixa a loja usa esta arte, e é onde o aviso de
   arte herdada aparece. */
.nz-vitrine-rotulo {
  display: flex; align-items: baseline; gap: var(--nz-space-2); flex-wrap: wrap;
  font-size: var(--nz-text-xs); color: var(--nz-text-muted);
}
.nz-vitrine-rotulo b { font-weight: var(--nz-weight-medium); color: var(--nz-text); }
/* A arte de celular que a loja NÃO tem: ela cai na do computador, e isso é
   defeito de cadastro que a prévia tem de dizer em vez de simular. */
.nz-vitrine-herdada { color: var(--nz-warning-subtle-fg); }

/* Um slide por vez. O ATUAL fica em fluxo e é ele que dá a altura à moldura; os
   outros saem do fluxo, empilhados e transparentes.

   A alternativa era pôr os slides em fila e deslocar a fila por `transform`,
   mas aí a moldura mede o slide MAIS ALTO da fila: medido, um banner de 1920×887
   dentro de uma fila que tem um 810×540 ficava com 140px de vazio em cima e
   embaixo. Com o atual em fluxo, a moldura mede exatamente o que está à vista. */
.nz-vitrine-slide {
  position: absolute; inset: 0;
  display: grid; place-items: center; text-align: center;
  opacity: 0; transition: opacity var(--nz-duration-slow) var(--nz-ease-out);
}
.nz-vitrine-slide[data-atual="true"] {
  position: relative; inset: auto;
  opacity: 1;
}
/* O slide não tem `padding`: ele inseriria a imagem e deixaria uma borda do
   fundo aparecendo em volta. O respiro do caso de aviso é do próprio
   `.nz-vitrine-vazia`, que já traz o seu — e é ele que dá altura à moldura
   quando o arquivo falha. */
.nz-vitrine-slide > img,
.nz-vitrine-slide > video {
  /* A arte ocupa a LARGURA da moldura e a altura vem da proporção — é o que a
     loja faz (`<img style="width: 100%">` no `Vitrine.svelte`), e é o que apaga a
     tarja de fundo nas laterais que aparecia quando a arte era menor que a
     moldura.

     Sem teto de altura: com uma tela por vez ele deixou de ser necessário, e
     `max-width: 100%` continua fechando a porta do transbordo no próprio
     elemento que a abriu — o painel não carrega reset de `img` (§82). */
  display: block;
  width: 100%; height: auto; max-width: 100%;
}
@media (prefers-reduced-motion: reduce) {
  .nz-vitrine-slide { transition: none; }
}

/* Marcadores do carrossel: botões de verdade, com alvo de 44px vindo do
   padding — a bolinha de 8px é só o que se vê.

   Centrados sob a MOLDURA da tela visível, seja ela qual for: como as duas telas
   e os marcadores são irmãos numa grade de uma coluna, `align-self`/`width: 100%`
   dá aos marcadores a largura da tela em cartaz — sem número mágico. */
.nz-vitrine-pontos {
  display: flex; gap: 2px; justify-content: center;
  width: 100%;
}
/* No modo celular a moldura tem 375px, então os marcadores acompanham em vez de
   ficarem centrados numa régua que não existe mais. */
.nz-vitrine[data-modo="mobile"] .nz-vitrine-pontos { width: 375px; max-width: 100%; }
.nz-vitrine-ponto {
  width: 28px; height: 28px; padding: 0; border: 0; background: none;
  display: grid; place-items: center; cursor: pointer; border-radius: 50%;
}
.nz-vitrine-ponto::before {
  content: ""; width: 8px; height: 8px; border-radius: 50%;
  background: var(--nz-border-strong);
  transition: background var(--nz-duration-fast) var(--nz-ease-out);
}
.nz-vitrine-ponto[aria-current="true"]::before { background: var(--nz-primary); }
.nz-vitrine-ponto:hover::before { background: var(--nz-text-muted); }

/* Linha de banner na lista. Miniatura em 16:5 porque é a proporção que o
   lojista precisa julgar, não um quadrado. */
.nz-banner-linha {
  display: grid; align-items: center; gap: var(--nz-space-4);
  grid-template-columns: auto 128px minmax(0, 1fr) auto auto;
  padding: var(--nz-space-3) var(--nz-space-4);
  border-bottom: 1px solid var(--nz-border);
  background: var(--nz-surface);
}
.nz-banner-linha:last-child { border-bottom: 0; }
.nz-banner-linha[data-arrastando="true"] { opacity: 0.4; }
.nz-banner-linha[data-alvo="true"] { box-shadow: inset 0 2px 0 var(--nz-primary); }
/* Expirado e pausado não aparecem na loja: recuar o brilho diz isso antes de
   qualquer texto. */
.nz-banner-linha[data-fora="true"] .nz-banner-thumb { opacity: 0.45; }

.nz-banner-thumb {
  position: relative; width: 128px; aspect-ratio: 16 / 5;
  border-radius: var(--nz-radius-sm); overflow: hidden;
  border: 1px solid var(--nz-border); background: var(--nz-neutral-subtle);
  display: grid; place-items: center;
}
.nz-banner-thumb img { width: 100%; height: 100%; object-fit: cover; }
.nz-banner-midia {
  position: absolute; right: 3px; bottom: 3px;
  display: inline-flex; align-items: center; gap: 2px;
  padding: 1px 4px; border-radius: var(--nz-radius-sm);
  font-size: 9px; font-weight: var(--nz-weight-semibold); letter-spacing: 0.04em;
  background: rgba(0, 0, 0, 0.72); color: #FFFFFF;
}
.nz-banner-midia svg { width: 9px; height: 9px; }

.nz-banner-txt { display: grid; gap: 2px; min-width: 0; }
.nz-banner-nome {
  font-size: var(--nz-text-sm); font-weight: var(--nz-weight-medium);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nz-banner-meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--nz-space-2);
  font-size: var(--nz-text-xs); color: var(--nz-text-muted);
}

@media (max-width: 860px) {
  /* Em coluna estreita a linha vira duas: miniatura + texto em cima, controles
     embaixo — em vez de espremer cinco colunas em 320px. */
  .nz-banner-linha {
    grid-template-columns: 96px minmax(0, 1fr);
    grid-template-areas: "thumb texto" "acoes acoes";
    row-gap: var(--nz-space-3);
  }
  .nz-banner-thumb { grid-area: thumb; width: 96px; }
  .nz-banner-txt { grid-area: texto; }
  .nz-banner-linha > .nz-item-mover,
  .nz-banner-linha > .nz-banner-acoes { grid-area: acoes; }
  .nz-banner-linha > .nz-item-mover { justify-self: start; }
  .nz-banner-linha > .nz-banner-acoes { justify-self: end; }
}
.nz-banner-acoes { display: flex; align-items: center; gap: var(--nz-space-2); }

/* Campo de imagem com prévia e o tamanho recomendado dito em algum lugar —
   hoje não está dito em lugar nenhum, e o lojista sobe o que tiver. */
.nz-img-campo {
  display: grid; gap: var(--nz-space-2);
  grid-template-columns: 132px minmax(0, 1fr); align-items: start;
}
.nz-img-previa {
  position: relative; width: 132px; overflow: hidden;
  border: 1px dashed var(--nz-border-strong); border-radius: var(--nz-radius-md);
  background: var(--nz-surface-sunken);
  display: grid; place-items: center;
}
.nz-img-previa img { width: 100%; height: 100%; object-fit: cover; }
.nz-img-previa[data-formato="desktop"] { aspect-ratio: 16 / 5; }
.nz-img-previa[data-formato="mobile"]  { aspect-ratio: 4 / 5; }
.nz-img-vazia { color: var(--nz-text-muted); font-size: var(--nz-text-2xs); text-align: center; }

/* ==========================================================================
   Dapic (store_webpic v2)
   Diferente das outras telas, esta nao chega abandonada: expoe telemetria,
   valida credencial antes de gravar e explica o que faz. O problema e de
   ORDEM — quem abre esta tela quase sempre abre porque algo parou, e a
   resposta a "esta funcionando?" estava no ultimo card da pagina.
   ========================================================================== */

/* Cabecalho de saude: um veredito em texto, nao um carimbo de data. */
.nz-saude {
  display: flex; align-items: flex-start; gap: var(--nz-space-4);
  padding: var(--nz-space-5);
  border: 1px solid var(--nz-border); border-radius: var(--nz-radius-lg);
  background: var(--nz-surface);
}
.nz-saude[data-tone="success"]  { border-color: var(--nz-success); }
.nz-saude[data-tone="warning"]  { border-color: var(--nz-warning); }
.nz-saude[data-tone="danger"]   { border-color: var(--nz-danger); }

.nz-saude-icone {
  display: grid; place-items: center; flex: none;
  width: 40px; height: 40px; border-radius: 50%;
}
.nz-saude-icone svg { width: 20px; height: 20px; }
.nz-saude[data-tone="success"] .nz-saude-icone { background: var(--nz-success-subtle); color: var(--nz-success-subtle-fg); }
.nz-saude[data-tone="warning"] .nz-saude-icone { background: var(--nz-warning-subtle); color: var(--nz-warning-subtle-fg); }
.nz-saude[data-tone="danger"]  .nz-saude-icone { background: var(--nz-danger-subtle);  color: var(--nz-danger-subtle-fg); }
.nz-saude[data-tone="neutral"] .nz-saude-icone { background: var(--nz-neutral-subtle); color: var(--nz-neutral-subtle-fg); }

.nz-saude-txt { flex: 1; min-width: 0; display: grid; gap: 2px; }
.nz-saude-titulo {
  font-size: var(--nz-text-lg); font-weight: var(--nz-weight-semibold);
  line-height: var(--nz-leading-tight);
}
.nz-saude-sub { font-size: var(--nz-text-sm); color: var(--nz-text-secondary); max-width: 68ch; }

/* Sinais vitais lado a lado. Cada um traz o valor E a leitura — "ha 12 min"
   nao diz nada sem "o esperado e a cada 30 min". */
.nz-sinais {
  display: grid; gap: var(--nz-space-3);
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
}
.nz-sinal {
  display: grid; gap: 2px; padding: var(--nz-space-4);
  border: 1px solid var(--nz-border); border-radius: var(--nz-radius-md);
  background: var(--nz-surface);
}
.nz-sinal-rot {
  font-size: var(--nz-text-xs); color: var(--nz-text-muted);
  text-transform: uppercase; letter-spacing: var(--nz-tracking-caps);
}
.nz-sinal-valor {
  font-size: var(--nz-text-lg); font-weight: var(--nz-weight-semibold);
  font-variant-numeric: tabular-nums;
}
.nz-sinal-nota { font-size: var(--nz-text-xs); color: var(--nz-text-muted); }
.nz-sinal[data-tone="danger"] .nz-sinal-valor { color: var(--nz-danger-subtle-fg); }
.nz-sinal[data-tone="warning"] .nz-sinal-valor { color: var(--nz-warning-subtle-fg); }

/* Segredo exibido uma unica vez. O fundo tratado marca que aquilo ali nao
   volta — e o botao de copiar precisa ser o alvo obvio. */
.nz-segredo {
  display: grid; gap: var(--nz-space-3);
  padding: var(--nz-space-4); border-radius: var(--nz-radius-md);
  border: 1px solid var(--nz-warning); background: var(--nz-warning-subtle);
  color: var(--nz-warning-subtle-fg);
}
.nz-segredo-par { display: grid; gap: var(--nz-space-1); }
.nz-segredo-rot { font-size: var(--nz-text-xs); font-weight: var(--nz-weight-semibold); }
.nz-segredo-valor {
  display: flex; align-items: center; gap: var(--nz-space-2);
  padding: var(--nz-space-2) var(--nz-space-3); border-radius: var(--nz-radius-sm);
  background: var(--nz-surface); border: 1px solid var(--nz-border);
  color: var(--nz-text);
}
.nz-segredo-valor code {
  flex: 1; min-width: 0; overflow-x: auto; white-space: nowrap;
  font-family: var(--nz-font-mono); font-size: var(--nz-text-xs);
}

/* Passo a passo numerado do tutorial. <ol> de verdade: leitor de tela anuncia
   "item 2 de 5", que e exatamente a informacao util aqui. */
.nz-tutorial { margin: 0; padding-left: var(--nz-space-5); display: grid; gap: var(--nz-space-3); }
.nz-tutorial li { font-size: var(--nz-text-sm); line-height: 1.6; }
.nz-tutorial code, .nz-copiavel {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 6px; border-radius: var(--nz-radius-sm);
  font-family: var(--nz-font-mono); font-size: 0.92em;
  background: var(--nz-neutral-subtle); color: var(--nz-text-secondary);
}
.nz-copiavel { cursor: pointer; border: 1px solid transparent; }
.nz-copiavel:hover { border-color: var(--nz-border-strong); color: var(--nz-text); }
.nz-copiavel svg { width: 11px; height: 11px; opacity: 0.6; }

/* Linha de log. Fonte mono e tamanho fixo: log e para comparar linhas. */
.nz-log {
  margin: 0; padding: var(--nz-space-3) var(--nz-space-4);
  max-height: 220px; overflow: auto;
  border-radius: var(--nz-radius-md); border: 1px solid var(--nz-border);
  background: var(--nz-surface-sunken);
  font-family: var(--nz-font-mono); font-size: var(--nz-text-xs);
  line-height: 1.7; color: var(--nz-text-secondary);
  white-space: pre-wrap; word-break: break-word;
}
.nz-log-linha { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: var(--nz-space-3); }
.nz-log-hora { color: var(--nz-text-muted); white-space: nowrap; }
.nz-log-linha[data-tone="danger"] .nz-log-msg { color: var(--nz-danger-subtle-fg); }
.nz-log-linha[data-tone="warning"] .nz-log-msg { color: var(--nz-warning-subtle-fg); }

/* ==========================================================================
   Chaves de API (user_api → store_api)
   Um segredo nao deve ficar legivel numa listagem. A tela atual imprime o
   token inteiro em toda linha da tabela, toda vez que a pagina abre; aqui ele
   aparece uma vez, na criacao, e depois so o prefixo.
   ========================================================================== */
.nz-chave-linha {
  display: grid; align-items: center; gap: var(--nz-space-4);
  grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr) auto auto;
  padding: var(--nz-space-4);
  border-bottom: 1px solid var(--nz-border);
  background: var(--nz-surface);
}
.nz-chave-linha:last-child { border-bottom: 0; }
/* Revogada continua na lista para o historico de uso nao sumir, mas recuada:
   ela nao autentica mais nada. */
.nz-chave-linha[data-revogada="true"] .nz-chave-nome,
.nz-chave-linha[data-revogada="true"] .nz-chave-prefixo { opacity: 0.55; }

.nz-chave-txt { display: grid; gap: 4px; min-width: 0; }
.nz-chave-nome {
  font-size: var(--nz-text-sm); font-weight: var(--nz-weight-medium);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nz-chave-meta {
  display: flex; flex-wrap: wrap; align-items: center; gap: var(--nz-space-2);
  font-size: var(--nz-text-xs); color: var(--nz-text-muted);
}
.nz-chave-prefixo {
  font-family: var(--nz-font-mono); font-size: var(--nz-text-xs);
  padding: 2px 6px; border-radius: var(--nz-radius-sm);
  background: var(--nz-neutral-subtle); color: var(--nz-text-secondary);
}
.nz-chave-uso { display: grid; gap: 2px; justify-items: end; text-align: right; }
.nz-chave-uso-num {
  font-size: var(--nz-text-sm); font-weight: var(--nz-weight-medium);
  font-variant-numeric: tabular-nums;
}
.nz-chave-uso-nota { font-size: var(--nz-text-xs); color: var(--nz-text-muted); }
.nz-chave-acoes { display: flex; align-items: center; gap: var(--nz-space-2); }

@media (max-width: 860px) {
  /* Quatro colunas nao cabem em 320px sem espremer o nome da chave, que e o
     unico jeito de identificar qual e qual. */
  .nz-chave-linha {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "texto uso" "acoes acoes";
    row-gap: var(--nz-space-3);
  }
  .nz-chave-txt { grid-area: texto; }
  .nz-chave-uso { grid-area: uso; }
  .nz-chave-linha > .nz-switch,
  .nz-chave-linha > .nz-chave-acoes { grid-area: acoes; }
  .nz-chave-linha > .nz-switch { justify-self: start; }
  .nz-chave-linha > .nz-chave-acoes { justify-self: end; }
}

/* Aviso de mudanca de escopo. Nao e erro nem sucesso: e uma migracao que o
   lojista precisa entender antes de criar chave nova. */
.nz-migracao {
  display: grid; gap: var(--nz-space-3);
  padding: var(--nz-space-5);
  border: 1px solid var(--nz-info); border-radius: var(--nz-radius-lg);
  background: var(--nz-info-subtle); color: var(--nz-info-subtle-fg);
}
.nz-migracao-titulo {
  display: flex; align-items: center; gap: var(--nz-space-2);
  font-size: var(--nz-text-md); font-weight: var(--nz-weight-semibold);
}
.nz-migracao-titulo svg { width: 18px; height: 18px; flex: none; }
.nz-migracao p { margin: 0; font-size: var(--nz-text-sm); line-height: 1.6; max-width: 78ch; }

/* Antes e depois do escopo. Duas colunas que so existem enquanto a migracao
   nao terminou — e a forma mais curta de explicar o que muda. */
.nz-escopo {
  display: grid; gap: var(--nz-space-3);
  grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
}
.nz-escopo-col {
  display: grid; gap: var(--nz-space-2); padding: var(--nz-space-4);
  border-radius: var(--nz-radius-md); border: 1px solid var(--nz-border);
  background: var(--nz-surface);
}
.nz-escopo-rot {
  font-size: var(--nz-text-xs); text-transform: uppercase;
  letter-spacing: var(--nz-tracking-caps); color: var(--nz-text-muted);
}
.nz-escopo-item {
  display: flex; align-items: flex-start; gap: var(--nz-space-2);
  font-size: var(--nz-text-sm); line-height: 1.5;
}
.nz-escopo-item svg { width: 15px; height: 15px; flex: none; margin-top: 2px; }
.nz-escopo-col[data-lado="antes"] .nz-escopo-item svg { color: var(--nz-danger); }
.nz-escopo-col[data-lado="depois"] .nz-escopo-item svg { color: var(--nz-success); }

/* ==========================================================================
   Webhooks (store_postback + store_postback_queue)
   A fila ja guarda tudo: codigo de resposta, corpo, duracao, tentativas. A
   tela atual nao mostra uma linha disso — e por isso 163 eventos ficaram
   parados desde fevereiro de 2025 sem ninguem perceber.
   ========================================================================== */
.nz-entrega {
  display: grid; align-items: center; gap: var(--nz-space-4);
  grid-template-columns: auto minmax(0, 1fr) auto auto auto;
  width: 100%; padding: var(--nz-space-3) var(--nz-space-4);
  border: 0; border-bottom: 1px solid var(--nz-border);
  background: var(--nz-surface); color: inherit;
  text-align: left; cursor: pointer; font: inherit;
}
.nz-entrega:last-child { border-bottom: 0; }
.nz-entrega:hover { background: var(--nz-surface-hover); }
.nz-entrega[aria-expanded="true"] { background: var(--nz-surface-hover); }

/* Codigo HTTP como pastilha: e o primeiro dado que se procura numa lista de
   entregas, e precisa de forma alem da cor para quem nao a distingue. */
.nz-http {
  display: inline-grid; place-items: center; min-width: 46px;
  padding: 3px 8px; border-radius: var(--nz-radius-sm);
  font-family: var(--nz-font-mono); font-size: var(--nz-text-xs);
  font-weight: var(--nz-weight-semibold); font-variant-numeric: tabular-nums;
}
.nz-http[data-faixa="ok"]       { background: var(--nz-success-subtle); color: var(--nz-success-subtle-fg); }
.nz-http[data-faixa="erro"]     { background: var(--nz-danger-subtle);  color: var(--nz-danger-subtle-fg); }
.nz-http[data-faixa="pendente"] { background: var(--nz-neutral-subtle); color: var(--nz-neutral-subtle-fg); }

.nz-entrega-txt { display: grid; gap: 2px; min-width: 0; }
.nz-entrega-evento { font-size: var(--nz-text-sm); font-weight: var(--nz-weight-medium); }
.nz-entrega-url {
  font-family: var(--nz-font-mono); font-size: var(--nz-text-xs); color: var(--nz-text-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nz-entrega-num {
  font-size: var(--nz-text-xs); color: var(--nz-text-muted);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.nz-entrega-seta {
  width: 16px; height: 16px; color: var(--nz-text-muted);
  transition: rotate var(--nz-duration-base) var(--nz-ease-out);
}
.nz-entrega[aria-expanded="true"] .nz-entrega-seta { rotate: 90deg; }
@media (prefers-reduced-motion: reduce) { .nz-entrega-seta { transition: none; } }

.nz-entrega-detalhe {
  display: grid; gap: var(--nz-space-4);
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  padding: var(--nz-space-4);
  border-bottom: 1px solid var(--nz-border);
  background: var(--nz-surface-sunken);
}
.nz-entrega-detalhe[hidden] { display: none; }
.nz-payload { display: grid; gap: var(--nz-space-2); min-width: 0; }
.nz-payload-rot {
  font-size: var(--nz-text-xs); text-transform: uppercase;
  letter-spacing: var(--nz-tracking-caps); color: var(--nz-text-muted);
}

@media (max-width: 860px) {
  .nz-entrega {
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas: "http texto seta" "meta meta meta";
    row-gap: var(--nz-space-2);
  }
  .nz-entrega > .nz-http { grid-area: http; }
  .nz-entrega > .nz-entrega-txt { grid-area: texto; }
  .nz-entrega > .nz-entrega-seta { grid-area: seta; }
  .nz-entrega > .nz-entrega-num { grid-area: meta; }
  /* Duas metas na mesma area: exibi-las em linha evita empilhar quatro
     alturas so para dizer "1 tentativa · 0,85 s". */
  .nz-entrega > .nz-entrega-num ~ .nz-entrega-num { justify-self: end; margin-top: -18px; }
}

/* Endpoint configurado: URL, eventos e saude resumida. */
.nz-endpoint {
  display: grid; align-items: center; gap: var(--nz-space-4);
  grid-template-columns: minmax(0, 1fr) auto auto auto;
  padding: var(--nz-space-4);
  border-bottom: 1px solid var(--nz-border);
}
.nz-endpoint:last-child { border-bottom: 0; }
.nz-endpoint-txt { display: grid; gap: var(--nz-space-2); min-width: 0; }
.nz-endpoint-url {
  font-family: var(--nz-font-mono); font-size: var(--nz-text-sm);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nz-eventos { display: flex; flex-wrap: wrap; gap: 4px; }

@media (max-width: 860px) {
  .nz-endpoint {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "texto saude" "acoes acoes";
    row-gap: var(--nz-space-3);
  }
  .nz-endpoint-txt { grid-area: texto; }
  .nz-endpoint > .nz-entrega-num { grid-area: saude; justify-self: end; }
  .nz-endpoint > .nz-switch { grid-area: acoes; justify-self: start; }
  .nz-endpoint > .nz-chave-acoes { grid-area: acoes; justify-self: end; }
}

/* ==========================================================================
   Cadastro de produto — abas Imagens, Variações e SEO
   As tres abas existiam como botao sem painel. O que segue e o que o banco ja
   suporta e o formulario nunca expos: sort_order e subtitle das imagens, a
   grade cor x tamanho, o slug, a janela da promocao e o peso.
   ========================================================================== */

/* Grade de imagens. A primeira e a capa — posicao e o unico jeito de dizer
   isso sem inventar um segundo controle. */
/* Par de campos lado a lado. Existe como CLASSE, não como style inline: um
   display no atributo style vence qualquer seletor, inclusive o
   .nz-app [hidden] que desliga os blocos ocultos — e o bloco reapareceria. */
.nz-campos-par {
  display: grid; gap: var(--nz-space-3);
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
}

.nz-img-grade {
  display: grid; gap: var(--nz-space-3);
  grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
}
.nz-img-item {
  position: relative; display: grid; gap: var(--nz-space-2);
}
.nz-img-quadro {
  position: relative; aspect-ratio: 1; overflow: hidden;
  border: 1px solid var(--nz-border); border-radius: var(--nz-radius-md);
  background: var(--nz-neutral-subtle);
}
.nz-img-item[data-arrastando="true"] { opacity: 0.4; }
.nz-img-item[data-alvo="true"] .nz-img-quadro { box-shadow: 0 0 0 2px var(--nz-primary); }
.nz-img-capa {
  position: absolute; top: var(--nz-space-2); left: var(--nz-space-2);
}
/* Ações sobre a imagem: sempre visíveis, não em hover — em toque não existe
   hover, e esconder a única forma de remover é esconder a função. */
.nz-img-acoes {
  position: absolute; right: var(--nz-space-1); bottom: var(--nz-space-1);
  display: flex; gap: 2px;
}
.nz-img-btn {
  display: grid; place-items: center; width: 28px; height: 28px;
  border: 0; border-radius: var(--nz-radius-sm); cursor: pointer;
  background: rgba(0, 0, 0, 0.62); color: #FFFFFF;
}
.nz-img-btn:hover { background: rgba(0, 0, 0, 0.8); }
.nz-img-btn:focus-visible { outline: 2px solid #FFFFFF; outline-offset: -2px; }
.nz-img-btn svg { width: 14px; height: 14px; }
.nz-img-alt { width: 100%; }

/* Foto a caminho da galeria (e capa em envio). A prévia local fica esmaecida
   sob a barra; falha marca a borda e o rótulo em perigo. Medidas da barra
   vão inline na tela (§79.6) — aqui só cor e estado. */
.nz-img-item[data-estado] .nz-img-quadro img { opacity: 0.55; }
.nz-img-item[data-estado="falhou"] .nz-img-quadro { border-color: var(--nz-danger); }
.nz-img-progresso { background: rgba(0, 0, 0, 0.55); }
.nz-img-progresso-fill { background: var(--nz-primary); transition: width var(--nz-duration-fast) var(--nz-ease-out); }
.nz-img-item[data-estado="fila"] .nz-img-progresso-fill { background: var(--nz-text-muted); }
.nz-img-estado {
  display: flex; justify-content: space-between; gap: var(--nz-space-2);
  font-size: var(--nz-text-2xs); color: var(--nz-text-muted); min-width: 0;
}
.nz-img-estado-nome { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nz-img-estado-rotulo { flex: none; font-variant-numeric: tabular-nums; }
.nz-img-item[data-estado="falhou"] .nz-img-estado-rotulo { color: var(--nz-danger); font-weight: var(--nz-weight-medium); }
.nz-capa-previa[data-enviando="true"] img { opacity: 0.55; }
@media (prefers-reduced-motion: reduce) {
  .nz-img-progresso-fill { transition: none; }
}

/* Grade de variações: cor nas linhas, tamanho nas colunas — o modelo real do
   banco (variation = cor, variation_item = tamanho). */
.nz-grade-wrap { overflow-x: auto; }
.nz-grade {
  width: 100%; border-collapse: separate; border-spacing: 0;
  font-size: var(--nz-text-sm);
}
.nz-grade th, .nz-grade td {
  padding: var(--nz-space-2) var(--nz-space-3);
  border-bottom: 1px solid var(--nz-border);
  text-align: left; white-space: nowrap;
}
.nz-grade thead th {
  font-size: var(--nz-text-xs); font-weight: var(--nz-weight-semibold);
  color: var(--nz-text-secondary); background: var(--nz-surface-sunken);
  position: sticky; top: 0; z-index: 1;
}
/* A coluna da cor fica congelada: rolando a grade na horizontal, perder de
   vista qual cor se está editando é o erro mais caro desta tela. */
.nz-grade th[scope="row"] {
  position: sticky; left: 0; z-index: 2;
  background: var(--nz-surface); font-weight: var(--nz-weight-medium);
}
.nz-grade tbody tr:hover th[scope="row"],
.nz-grade tbody tr:hover td { background: var(--nz-surface-hover); }
.nz-grade-cor { display: flex; align-items: center; gap: var(--nz-space-2); cursor: pointer; }
.nz-grade-cor-txt { display: flex; flex-direction: column; line-height: 1.25; }
.nz-grade-cor-nome { font-weight: var(--nz-weight-medium); }
.nz-grade-cor-sub { font-size: var(--nz-text-xs); color: var(--nz-text-muted); font-variant-numeric: tabular-nums; }
.nz-grade-amostra {
  width: 22px; height: 22px; flex: none; border-radius: var(--nz-radius-sm);
  border: 1px solid var(--nz-border);
}
/* A cor sem foto não é um quadrado cinza: é o lugar de colocar a foto. Ela
   existe em `store_product_variation.image` e só 327 das 3.649 a usam. */
.nz-grade-amostra[data-sem-foto="true"] {
  display: grid; place-items: center;
  border-style: dashed; color: var(--nz-text-muted);
  background: var(--nz-surface-sunken);
}
.nz-grade-amostra svg { width: 12px; height: 12px; }
.nz-grade tbody tr[data-inativa="true"] th[scope="row"] .nz-grade-cor-nome { color: var(--nz-text-muted); }

/* Cabeçalho de coluna clicável: marcar "G" seleciona aquele tamanho em todas
   as cores — o recorte que uma confecção realmente usa. */
.nz-grade-colrot { display: flex; align-items: center; gap: var(--nz-space-2); cursor: pointer; }
.nz-grade-canto { width: 1%; }

/* Célula que NÃO existe. Combinação inexistente e combinação zerada são
   coisas diferentes, e a tela atual mostra as duas como ausência. */
.nz-grade td[data-vazia="true"] { background: var(--nz-surface-sunken); }
.nz-cel-criar {
  display: grid; place-items: center;
  width: 32px; height: 32px; padding: 0;
  border: 1px dashed var(--nz-border-strong); border-radius: var(--nz-radius-sm);
  background: transparent; color: var(--nz-text-muted); cursor: pointer;
  transition: border-color var(--nz-duration-fast) var(--nz-ease-out),
              color var(--nz-duration-fast) var(--nz-ease-out);
}
.nz-cel-criar:hover { border-color: var(--nz-primary); color: var(--nz-primary); }
.nz-cel-criar svg { width: 14px; height: 14px; }

.nz-grade td[data-sel="true"] { background: var(--nz-primary-subtle); }
/* A borda vermelha diz "esgotado" — só faz sentido no modo Estoque. Nos
   outros modos ela seria lida como "valor inválido". */
.nz-grade[data-modo="estoque"] td[data-zero="true"] input { border-color: var(--nz-danger); }

/* Totais: qual tamanho acaba primeiro não se lê célula a célula. */
.nz-grade .nz-grade-total { text-align: right; font-variant-numeric: tabular-nums; }
.nz-grade tfoot th, .nz-grade tfoot td {
  background: var(--nz-surface-sunken);
  font-weight: var(--nz-weight-semibold); border-bottom: 0;
}
.nz-grade-esgotado {
  display: block; font-size: 10px; font-weight: var(--nz-weight-medium);
  text-transform: uppercase; letter-spacing: 0.04em; color: var(--nz-danger);
}

/* Eixos da grade */
.nz-eixos { display: grid; gap: var(--nz-space-4); margin-bottom: var(--nz-space-4); }
@media (min-width: 800px) { .nz-eixos { grid-template-columns: 1fr 1fr; } }
.nz-eixo {
  display: flex; flex-direction: column; gap: var(--nz-space-2);
  padding: var(--nz-space-3);
  border: 1px solid var(--nz-border); border-radius: var(--nz-radius-md);
  background: var(--nz-surface-sunken);
}
.nz-eixo-topo { display: flex; align-items: baseline; justify-content: space-between; gap: var(--nz-space-2); }
.nz-eixo-nome { font-size: var(--nz-text-sm); font-weight: var(--nz-weight-semibold); }
.nz-eixo-conta { font-size: var(--nz-text-xs); color: var(--nz-text-muted); }
.nz-eixo-chips { display: flex; flex-wrap: wrap; gap: var(--nz-space-2); }
.nz-chip-valor {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 32px; padding: 2px 6px 2px 8px;
  border: 1px solid var(--nz-border); border-radius: 999px;
  background: var(--nz-surface); font-size: var(--nz-text-sm);
}
.nz-chip-valor[data-sem-ordem="true"] { border-style: dashed; border-color: var(--nz-warning); }
.nz-chip-valor > button {
  display: grid; place-items: center; width: 24px; height: 24px; padding: 0;
  border: 0; border-radius: 50%; background: transparent;
  color: var(--nz-text-muted); cursor: pointer;
}
.nz-chip-valor > button:hover { background: var(--nz-surface-hover); color: var(--nz-text); }
.nz-chip-valor > button:disabled { opacity: 0.35; cursor: default; }
.nz-chip-valor svg { width: 12px; height: 12px; }
.nz-chip-add {
  min-height: 32px; padding: 0 var(--nz-space-3);
  border: 1px dashed var(--nz-border-strong); border-radius: 999px;
  background: transparent; color: var(--nz-text-secondary);
  font: inherit; font-size: var(--nz-text-sm); cursor: pointer;
}
.nz-chip-add:hover { border-color: var(--nz-primary); color: var(--nz-primary); }

/* Modo à esquerda, ação em massa à direita, ambos acima da grade que governam. */
.nz-grade-barra {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--nz-space-3);
}
.nz-grade-barra .nz-massa { margin: 0; flex: 1 1 320px; }
.nz-switch-cel { transform: scale(0.9); }
.nz-sku-padrao { display: flex; flex-direction: column; gap: 4px; margin-top: var(--nz-space-3); max-width: 340px; }
.nz-grade input {
  width: 84px; padding: 4px 8px; font-size: var(--nz-text-sm);
  font-variant-numeric: tabular-nums;
  border: 1px solid var(--nz-border); border-radius: var(--nz-radius-sm);
  background: var(--nz-surface); color: var(--nz-text);
}
.nz-grade input:focus { outline: 2px solid var(--nz-border-focus); outline-offset: -1px; }
.nz-grade input[data-vazio="true"] { border-color: var(--nz-warning); }
.nz-grade-sku { width: 116px !important; font-family: var(--nz-font-mono); font-size: var(--nz-text-xs); }

/* Barra de edição em massa: mexer em 12 células uma a uma é o que faz o
   lojista desistir e deixar o estoque errado. */
.nz-massa {
  display: flex; align-items: center; gap: var(--nz-space-3); flex-wrap: wrap;
  padding: var(--nz-space-3) var(--nz-space-4);
  border-radius: var(--nz-radius-md); border: 1px solid var(--nz-border);
  background: var(--nz-surface-sunken);
}
.nz-massa label { font-size: var(--nz-text-sm); color: var(--nz-text-secondary); }

/* O cabeçalho da linha é um BOTÃO (abre o painel da cor, §76) — o reset tira
   o cromo de botão e preserva o visual que a célula sempre teve. */
button.nz-grade-cor {
  border: 0; padding: 0; background: transparent;
  font: inherit; color: inherit; text-align: left;
}
button.nz-grade-cor:focus-visible { outline: 2px solid var(--nz-border-focus); outline-offset: 2px; border-radius: var(--nz-radius-sm); }
button.nz-grade-cor:hover .nz-grade-cor-nome { color: var(--nz-primary); }
/* `.nz-chip-valor > button` é a regra do × (24px, redonda) — este botão é o
   CORPO do chip e precisa vencê-la, senão o nome transborda da pílula. */
.nz-chip-valor > button.nz-chip-corpo {
  display: inline-flex; align-items: center; gap: 6px;
  width: auto; height: auto; border-radius: 999px;
  border: 0; padding: 0; background: transparent;
  font: inherit; color: inherit; cursor: pointer;
}
.nz-chip-valor > button.nz-chip-corpo:hover { color: var(--nz-primary); background: transparent; }
.nz-chip-valor > button.nz-chip-corpo:focus-visible { outline: 2px solid var(--nz-border-focus); outline-offset: 2px; }

/* Promocional preenchido se distingue do vazio sem depender do modo. */
.nz-grade input[data-com-promo="true"] { border-color: var(--nz-success); }

/* Publicado: interruptor + remover, lado a lado. */
.nz-cel-ativo { display: inline-flex; align-items: center; gap: var(--nz-space-2); }
.nz-cel-excluir {
  display: grid; place-items: center; width: 26px; height: 26px; padding: 0;
  border: 0; border-radius: var(--nz-radius-sm);
  background: transparent; color: var(--nz-text-muted); cursor: pointer;
}
.nz-cel-excluir:hover { color: var(--nz-danger); background: var(--nz-danger-subtle); }
.nz-cel-excluir svg { width: 14px; height: 14px; }

/* Painel da cor (§76): foto escolhida da galeria do produto + nome do
   catálogo — o que antes exigia a tela legada de variações. */
.nz-cor-painel {
  display: grid; gap: var(--nz-space-4);
  margin: var(--nz-space-4) 0;
  padding: var(--nz-space-4);
  border: 1px solid var(--nz-border); border-radius: var(--nz-radius-md);
  background: var(--nz-surface-sunken);
}
.nz-cor-painel[aria-busy="true"] { opacity: 0.6; pointer-events: none; }
.nz-cor-painel-topo {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--nz-space-2);
}
.nz-cor-fotos { display: flex; flex-wrap: wrap; gap: var(--nz-space-2); }
.nz-cor-foto {
  position: relative; display: grid; place-items: center;
  width: 56px; height: 56px; padding: 0;
  border: 1px solid var(--nz-border); border-radius: var(--nz-radius-md);
  background-color: var(--nz-surface); background-size: cover; background-position: center;
  color: var(--nz-text-secondary); cursor: pointer;
  font: inherit; font-size: var(--nz-text-xs);
}
.nz-cor-foto:hover { border-color: var(--nz-primary); }
.nz-cor-foto:focus-visible { outline: 2px solid var(--nz-border-focus); outline-offset: 2px; }
.nz-cor-foto[data-atual="true"] {
  border: 2px solid var(--nz-primary);
  box-shadow: 0 0 0 2px var(--nz-primary-subtle);
  color: #FFFFFF; cursor: default;
}
.nz-cor-foto[data-atual="true"] svg {
  width: 18px; height: 18px;
  filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.7));
}
.nz-cor-foto[data-pf-cor-envio] {
  flex-direction: column; display: flex; align-items: center; justify-content: center; gap: 2px;
  border-style: dashed; border-color: var(--nz-border-strong);
  background: transparent;
}
.nz-cor-foto[data-pf-cor-envio]:hover { border-color: var(--nz-primary); color: var(--nz-primary); }
.nz-cor-foto[data-pf-cor-envio] svg { width: 16px; height: 16px; }

/* A bolinha do seletor da vitrine (§79): cor sólida OU imagem. Duas fontes,
   porque 87% das cores não têm foto de onde recortar — e as duas maiores nem
   são cores (LISO, ESTAMPADO). */
.nz-swatch-topo {
  display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: var(--nz-space-3);
}
.nz-swatch-amostra {
  display: inline-block; vertical-align: middle;
  width: 20px; height: 20px; margin: 0 var(--nz-space-2);
  border-radius: 50%; border: 1px solid var(--nz-border);
  background-position: center;
}
/* Sem bolinha é o estado que a vitrine mostra como círculo branco: tracejado
   aqui para o lojista reconhecer o defeito, não um cinza qualquer. */
.nz-swatch-amostra[data-vazia="true"] {
  border-style: dashed; border-color: var(--nz-warning); background: transparent;
}
.nz-swatch-paleta {
  display: grid; grid-template-columns: repeat(auto-fill, 28px); gap: var(--nz-space-2);
  margin-bottom: var(--nz-space-3);
}
.nz-swatch-cor {
  width: 28px; height: 28px; padding: 0; cursor: pointer;
  border: 1px solid var(--nz-border); border-radius: 50%;
}
.nz-swatch-cor:hover { transform: scale(1.12); }
.nz-swatch-cor:focus-visible { outline: 2px solid var(--nz-border-focus); outline-offset: 2px; }
.nz-swatch-cor[data-atual="true"] {
  box-shadow: 0 0 0 2px var(--nz-surface), 0 0 0 4px var(--nz-primary);
}
.nz-swatch-linha {
  display: flex; align-items: center; gap: var(--nz-space-2); flex-wrap: wrap;
  margin-bottom: var(--nz-space-2);
}
.nz-swatch-input {
  width: 40px; height: 32px; padding: 2px; cursor: pointer;
  border: 1px solid var(--nz-border); border-radius: var(--nz-radius-sm);
  background: var(--nz-surface);
}
.nz-swatch-herda {
  display: flex; align-items: center; gap: var(--nz-space-2);
  margin-bottom: var(--nz-space-3); padding: var(--nz-space-2) var(--nz-space-3);
  border-radius: var(--nz-radius-md); border: 1px dashed var(--nz-border-strong);
  font-size: var(--nz-text-xs); color: var(--nz-text-secondary);
}
.nz-check-linha {
  display: flex; align-items: center; gap: var(--nz-space-2);
  margin-bottom: var(--nz-space-2); font-size: var(--nz-text-sm); cursor: pointer;
}

/* Prévia de busca. Reproduz a proporção do resultado do Google para que o
   corte do título e da descrição seja visível ANTES de publicar. */
.nz-serp {
  display: grid; gap: 2px; max-width: 600px;
  padding: var(--nz-space-4); border-radius: var(--nz-radius-md);
  border: 1px solid var(--nz-border); background: var(--nz-surface);
}
.nz-serp-url { font-size: var(--nz-text-xs); color: var(--nz-text-muted); }
.nz-serp-titulo {
  font-size: 18px; line-height: 1.3; color: #1a0dab;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
[data-theme="dark"] .nz-serp-titulo { color: #8ab4f8; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .nz-serp-titulo { color: #8ab4f8; }
}
.nz-serp-desc {
  font-size: var(--nz-text-sm); line-height: 1.5; color: var(--nz-text-secondary);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.nz-contador {
  font-size: var(--nz-text-xs); color: var(--nz-text-muted);
  font-variant-numeric: tabular-nums;
}
.nz-contador[data-estado="longo"] { color: var(--nz-warning-subtle-fg); }
.nz-contador[data-estado="curto"] { color: var(--nz-text-muted); }

/* Seleção de eventos do webhook. Agrupada por assunto e com marcar-tudo por
   grupo: onze caixas soltas viram uma parede, e o lojista marca todas para não
   pensar — que e exatamente o que a tela atual ja faz por ele. */
.nz-grupo-evento {
  border: 1px solid var(--nz-border); border-radius: var(--nz-radius-md);
  overflow: hidden;
}
.nz-grupo-evento + .nz-grupo-evento { margin-top: var(--nz-space-3); }
.nz-grupo-topo {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--nz-space-3); padding: var(--nz-space-2) var(--nz-space-3);
  background: var(--nz-surface-sunken);
  border-bottom: 1px solid var(--nz-border);
}
.nz-grupo-nome {
  font-size: var(--nz-text-xs); font-weight: var(--nz-weight-semibold);
  text-transform: uppercase; letter-spacing: var(--nz-tracking-caps);
  color: var(--nz-text-secondary);
}
.nz-grupo-corpo { padding: var(--nz-space-1) var(--nz-space-3); }
.nz-grupo-corpo .nz-opcao { min-height: 38px; }
/* Evento que o sistema garante nao entregar: recuado e com o motivo ao lado,
   em vez de escondido — esconder faria o lojista procurar por ele. */
.nz-opcao[data-bloqueado="true"] { color: var(--nz-text-muted); }

.nz-teste-res {
  display: grid; gap: var(--nz-space-2);
  padding: var(--nz-space-3) var(--nz-space-4);
  border-radius: var(--nz-radius-md); border: 1px solid var(--nz-border);
  background: var(--nz-surface-sunken);
}
.nz-teste-topo { display: flex; align-items: center; gap: var(--nz-space-3); }

/* ==========================================================================
   Detalhe do pedido
   86% dos pedidos da loja estao aguardando pagamento. A tela existe para
   responder duas perguntas: o que foi comprado, e o que fazer agora para o
   dinheiro entrar.
   ========================================================================== */

/* Linha do tempo do pedido. store_order_status ja grava old_status/new_status
   com data — 1.155 eventos para 1.103 pedidos — e nada disso e exibido. */
.nz-linha-tempo { display: grid; gap: 0; }
.nz-tempo-item {
  display: grid; grid-template-columns: 24px minmax(0, 1fr); gap: var(--nz-space-3);
  padding-bottom: var(--nz-space-4);
}
.nz-tempo-item:last-child { padding-bottom: 0; }
.nz-tempo-marca { position: relative; display: grid; justify-items: center; }
.nz-tempo-ponto {
  width: 11px; height: 11px; margin-top: 5px; border-radius: 50%;
  background: var(--nz-border-strong); flex: none;
}
.nz-tempo-item[data-atual="true"] .nz-tempo-ponto { background: var(--nz-primary); }
.nz-tempo-item[data-tone="danger"] .nz-tempo-ponto { background: var(--nz-danger); }
.nz-tempo-item[data-tone="success"] .nz-tempo-ponto { background: var(--nz-success); }
/* A linha liga um marco ao seguinte; some no ultimo para nao apontar ao vazio. */
.nz-tempo-item:not(:last-child) .nz-tempo-marca::after {
  content: ""; position: absolute; top: 20px; bottom: -12px; width: 1px;
  background: var(--nz-border);
}
.nz-tempo-txt { display: grid; gap: 1px; min-width: 0; padding-top: 1px; }
.nz-tempo-nome { font-size: var(--nz-text-sm); font-weight: var(--nz-weight-medium); }
.nz-tempo-quando { font-size: var(--nz-text-xs); color: var(--nz-text-muted); }
.nz-tempo-item[data-futuro="true"] .nz-tempo-nome { color: var(--nz-text-muted); font-weight: 400; }

/* Item do pedido. Grid fixo para que preço e quantidade fiquem alinhados
   entre linhas, independentemente do tamanho do nome. */
.nz-item-pedido {
  display: grid; align-items: center; gap: var(--nz-space-3);
  grid-template-columns: 48px minmax(0, 1fr) auto auto;
  padding: var(--nz-space-3) var(--nz-space-5);
  border-bottom: 1px solid var(--nz-border);
}
.nz-item-pedido:last-child { border-bottom: 0; }
.nz-item-qtd {
  font-size: var(--nz-text-sm); color: var(--nz-text-secondary);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.nz-item-total {
  font-size: var(--nz-text-sm); font-weight: var(--nz-weight-medium);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

/* Resumo financeiro. Duas colunas com o rotulo a esquerda e o valor alinhado a
   direita — conferir soma exige que os digitos fiquem na mesma coluna. */
.nz-resumo { display: grid; gap: var(--nz-space-2); }
.nz-resumo-linha {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--nz-space-4); font-size: var(--nz-text-sm);
}
.nz-resumo-linha > span:last-child { font-variant-numeric: tabular-nums; }
.nz-resumo-rot { color: var(--nz-text-secondary); }
.nz-resumo-total {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--nz-space-4);
  padding-top: var(--nz-space-3); margin-top: var(--nz-space-1);
  border-top: 1px solid var(--nz-border);
  font-size: var(--nz-text-lg); font-weight: var(--nz-weight-semibold);
}
.nz-resumo-total > span:last-child { font-variant-numeric: tabular-nums; }

/* Bloco de dado com rotulo. Repete em cliente, entrega e pagamento. */
.nz-dado { display: grid; gap: 2px; }
.nz-dado-rot {
  font-size: var(--nz-text-xs); color: var(--nz-text-muted);
  text-transform: uppercase; letter-spacing: var(--nz-tracking-caps);
}
.nz-dado-val { font-size: var(--nz-text-sm); line-height: 1.5; }

/* Duas colunas: o pedido a esquerda, o contexto a direita. */
.nz-pedido-layout {
  display: grid; gap: var(--nz-space-6);
  grid-template-columns: minmax(0, 1fr) 340px; align-items: start;
}
.nz-pedido-lateral {
  display: flex; flex-direction: column; gap: var(--nz-space-6);
  position: sticky; top: var(--nz-space-6);
}
@media (max-width: 1100px) {
  .nz-pedido-layout { grid-template-columns: minmax(0, 1fr); }
  /* Em coluna unica o contexto sobe: quem abre um pedido aguardando pagamento
     quer o telefone do cliente antes da lista de itens. */
  .nz-pedido-lateral { position: static; order: -1; }
}

/* --------------------------------------------------------------------------
   Cobrancas — o que o adquirente respondeu (secao 64).

   Uma linha por TENTATIVA, empilhadas como os itens do pedido: 195 pedidos tem
   mais de uma cobranca e um tem 66, entao isto e uma LISTA e nao um campo.

   Mora na coluna de 340px, o que decide o desenho: nada em duas colunas aqui
   dentro, e o valor em `tabular-nums` para as tentativas do mesmo pedido
   alinharem quando forem do mesmo valor.
   -------------------------------------------------------------------------- */
.nz-cobranca {
  display: flex; flex-direction: column; gap: var(--nz-space-2);
  padding: var(--nz-space-4) var(--nz-space-5);
  border-bottom: 1px solid var(--nz-border);
}
.nz-cobranca:last-child { border-bottom: 0; }
/* A que MANDA, quando ha mais de uma. Barra a esquerda e nao cor de fundo: o
   card ja vive dentro de um drawer, e mais um tom de superficie aqui empilharia
   cinza sobre cinza. */
.nz-cobranca[data-vencedora="true"] { box-shadow: inset 2px 0 0 var(--nz-primary); }

.nz-cobranca-topo {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: var(--nz-space-2);
}
.nz-cobranca-quem {
  font-size: var(--nz-text-sm); font-weight: var(--nz-weight-medium);
  line-height: 1.4;
}
.nz-cobranca-linha {
  display: flex; align-items: baseline; gap: var(--nz-space-2); flex-wrap: wrap;
  font-size: var(--nz-text-sm);
}
.nz-cobranca-linha .nz-num { font-weight: var(--nz-weight-semibold); }
.nz-cobranca-quando { font-size: var(--nz-text-xs); color: var(--nz-text-muted); }

.nz-cobranca-bruto { font-size: var(--nz-text-xs); color: var(--nz-text-muted); }

.nz-cobranca-acoes { display: flex; flex-wrap: wrap; gap: var(--nz-space-2); }

.nz-cobranca-hist > summary {
  cursor: pointer; font-size: var(--nz-text-xs); color: var(--nz-text-muted);
  padding: var(--nz-space-1) 0;
}
.nz-cobranca-hist > summary:focus-visible {
  outline: 2px solid var(--nz-border-focus); outline-offset: 2px; border-radius: var(--nz-radius-sm);
}
.nz-cobranca-eventos {
  display: flex; flex-direction: column; gap: var(--nz-space-2);
  padding-top: var(--nz-space-2);
}
.nz-cobranca-evento {
  display: flex; flex-direction: column; gap: 2px;
  font-size: var(--nz-text-xs); line-height: 1.45;
  padding-left: var(--nz-space-3);
  border-left: 2px solid var(--nz-border);
}
.nz-cobranca-evento-quando { color: var(--nz-text-muted); }
.nz-cobranca-evento-origem {
  color: var(--nz-text-muted); text-transform: uppercase;
  letter-spacing: var(--nz-tracking-caps); font-size: 10px;
}
.nz-cobranca-evento-bruto { color: var(--nz-text-muted); }
/* Retorno que chegou fora de ordem. Selo proprio e nao `.nz-sugestao`: aquele
   significa "desenhado e ainda nao existe", e aqui o fato existe — o que nao
   houve foi efeito. Tom de atencao, nao de perigo: nada se perdeu. */
.nz-cobranca-ignorado {
  display: inline-block; font-size: 10px; font-weight: var(--nz-weight-semibold);
  text-transform: uppercase; letter-spacing: var(--nz-tracking-caps);
  padding: 1px 5px; border-radius: var(--nz-radius-sm);
  background: var(--nz-warning-subtle); color: var(--nz-warning-subtle-fg);
}

/* --------------------------------------------------------------------------
   Modo edicao da ficha do pedido (secao 70). So existe na janela de aguardando
   pagamento; pago e cancelado duplicam em vez de editar.

   Uma linha por ITEM: nome ocupa o que sobra, quantidade e preco tem larguras
   fixas para as colunas alinharem entre linhas — quem compara precos de
   variacoes le em coluna, nao em linha.
   -------------------------------------------------------------------------- */
.nz-editar-lista { display: flex; flex-direction: column; }
.nz-editar-item {
  display: grid; align-items: center; gap: var(--nz-space-2);
  grid-template-columns: minmax(0, 1fr) 72px 110px 32px;
  padding: var(--nz-space-2) var(--nz-space-5);
  border-bottom: 1px solid var(--nz-border);
}
.nz-editar-nome {
  font-size: var(--nz-text-sm); line-height: 1.35;
  display: flex; flex-direction: column; min-width: 0;
}
.nz-editar-cod { font-size: var(--nz-text-xs); color: var(--nz-text-muted); }
.nz-editar-qtd, .nz-editar-preco { height: 32px; font-size: var(--nz-text-sm); }
.nz-editar-preco { text-align: right; }

/* Removido e um estado VISIVEL e reversivel antes de salvar: risca e esmaece,
   e o mesmo botao desfaz — apagar de verdade so no salvar. */
.nz-editar-item[data-removido="true"] .nz-editar-nome { text-decoration: line-through; opacity: .45; }
.nz-editar-item[data-removido="true"] .nz-editar-qtd,
.nz-editar-item[data-removido="true"] .nz-editar-preco { opacity: .35; pointer-events: none; }

.nz-editar-add { position: relative; padding: var(--nz-space-3) var(--nz-space-5); }
.nz-editar-sugestoes {
  position: absolute; left: var(--nz-space-5); right: var(--nz-space-5); top: 100%;
  z-index: 30; max-height: 240px; overflow-y: auto;
  background: var(--nz-surface); border: 1px solid var(--nz-border);
  border-radius: var(--nz-radius-sm); box-shadow: var(--nz-shadow-lg);
}
.nz-editar-sugestao {
  display: flex; justify-content: space-between; gap: var(--nz-space-3);
  width: 100%; text-align: left; border: 0; background: none; cursor: pointer;
  padding: var(--nz-space-2) var(--nz-space-3);
  font-size: var(--nz-text-sm); color: var(--nz-text);
}
.nz-editar-sugestao:hover, .nz-editar-sugestao:focus-visible { background: var(--nz-surface-sunken); }
.nz-editar-sugestao:focus-visible { outline: 2px solid var(--nz-border-focus); outline-offset: -2px; }
.nz-editar-sugestao .nz-mono { color: var(--nz-text-muted); font-size: var(--nz-text-xs); white-space: nowrap; }

.nz-editar-ajustes {
  display: grid; grid-template-columns: 1fr 1fr; gap: var(--nz-space-3);
  padding: 0 var(--nz-space-5) var(--nz-space-3);
}

/* Estados do builder de itens fora da ficha (Novo pedido, secao 71): o vazio
   orienta, o erro mora AO LADO do campo que falhou — nao num toast que some. */
.nz-editar-vazio {
  margin: 0; padding: var(--nz-space-3) 0;
  font-size: var(--nz-text-sm); color: var(--nz-text-muted);
}
.nz-editar-erro {
  margin: var(--nz-space-2) 0 0;
  font-size: var(--nz-text-xs); font-weight: var(--nz-weight-medium);
  color: var(--nz-danger-subtle-fg);
}
/* Fora da ficha as linhas do builder nao tem o padding do card. */
#nvItens .nz-editar-item { padding-left: 0; padding-right: 0; }

/* O chip do cliente escolhido. `:not([hidden])` e obrigatorio: declarar display
   numa regra anula o atributo hidden (o defeito das duas miniaturas, secao 57). */
.nz-novo-cliente-chip:not([hidden]) {
  display: flex; align-items: center; gap: var(--nz-space-2); flex-wrap: wrap;
}

/* ==========================================================================
   Ficha do cliente
   890 clientes na loja, 26 com pedido. A pergunta que a tela responde nao e
   "quem sao meus clientes" — e "com quem vale a pena falar".
   ========================================================================== */
.nz-cliente-topo {
  display: grid; align-items: center; gap: var(--nz-space-4);
  grid-template-columns: auto minmax(0, 1fr);
}
.nz-avatar {
  display: grid; place-items: center; flex: none;
  width: 56px; height: 56px; border-radius: 50%;
  font-size: var(--nz-text-lg); font-weight: var(--nz-weight-semibold);
  background: var(--nz-primary-subtle); color: var(--nz-primary-subtle-fg);
}
.nz-etiquetas { display: flex; flex-wrap: wrap; gap: 4px; }

/* Números do cliente. Em linha, com o valor grande e o rótulo embaixo — a
   comparação entre eles é o que responde se vale insistir. */
.nz-numeros {
  display: grid; gap: var(--nz-space-3);
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
}
.nz-numero {
  display: grid; gap: 2px; padding: var(--nz-space-4);
  border: 1px solid var(--nz-border); border-radius: var(--nz-radius-md);
  background: var(--nz-surface);
}
.nz-numero-val {
  font-size: var(--nz-text-xl); font-weight: var(--nz-weight-semibold);
  font-variant-numeric: tabular-nums; line-height: 1.15;
}
.nz-numero-rot { font-size: var(--nz-text-xs); color: var(--nz-text-muted); }

/* Tarefa do cliente. store_shopper_task ja existe com title, description,
   status, priority e date_task — 18 linhas gravadas. */
.nz-tarefa {
  display: grid; align-items: start; gap: var(--nz-space-3);
  grid-template-columns: auto minmax(0, 1fr) auto;
  padding: var(--nz-space-3) var(--nz-space-5);
  border-bottom: 1px solid var(--nz-border);
}
.nz-tarefa:last-child { border-bottom: 0; }
.nz-tarefa input[type="checkbox"] { width: 16px; height: 16px; margin-top: 2px; accent-color: var(--nz-primary); }
.nz-tarefa[data-feita="true"] .nz-tarefa-nome {
  text-decoration: line-through; color: var(--nz-text-muted);
}
.nz-tarefa-txt { display: grid; gap: 2px; min-width: 0; }
.nz-tarefa-nome { font-size: var(--nz-text-sm); }
/* Vencida: a data e o dado que decide a ordem do dia, entao ela muda de cor
   e ganha texto — nao so cor. */
.nz-tarefa-quando { font-size: var(--nz-text-xs); color: var(--nz-text-muted); }
.nz-tarefa[data-vencida="true"] .nz-tarefa-quando { color: var(--nz-danger-subtle-fg); }

/* Extrato: credito e debito na mesma coluna, com sinal explicito. */
.nz-extrato {
  display: grid; align-items: center; gap: var(--nz-space-3);
  grid-template-columns: minmax(0, 1fr) auto auto;
  padding: var(--nz-space-3) var(--nz-space-5);
  border-bottom: 1px solid var(--nz-border);
  font-size: var(--nz-text-sm);
}
.nz-extrato:last-child { border-bottom: 0; }
.nz-extrato-val { font-variant-numeric: tabular-nums; white-space: nowrap; }
.nz-extrato-val[data-sinal="mais"] { color: var(--nz-success-subtle-fg); }
.nz-extrato-saldo {
  font-variant-numeric: tabular-nums; color: var(--nz-text-muted);
  font-size: var(--nz-text-xs); white-space: nowrap;
}

.nz-cliente-layout {
  display: grid; gap: var(--nz-space-6);
  grid-template-columns: 320px minmax(0, 1fr); align-items: start;
}
.nz-cliente-lateral {
  display: flex; flex-direction: column; gap: var(--nz-space-6);
  position: sticky; top: var(--nz-space-6);
}
@media (max-width: 1100px) {
  .nz-cliente-layout { grid-template-columns: minmax(0, 1fr); }
  .nz-cliente-lateral { position: static; }
}

/* ==========================================================================
   Produtos — grade de estoque, preço promocional e galeria
   ========================================================================== */

/* Ação destrutiva empurrada para a ponta oposta das ações construtivas:
   perto do "Ativar" ela vira erro de clique numa seleção de 200 itens. */
.nz-bulk [data-massa][data-variant="danger-ghost"] { margin-left: auto; }

/* As duas células numéricas empilham valor e contexto alinhados à direita,
   para que os dígitos fiquem na mesma coluna óptica ao longo da lista. */
.nz-cel-preco,
.nz-cel-estoque {
  display: flex; flex-direction: column; align-items: flex-end; gap: 2px;
}

.nz-preco-antigo {
  font-size: var(--nz-text-xs); color: var(--nz-text-muted);
  text-decoration: line-through; font-variant-numeric: tabular-nums;
}

/* Cobertura da grade: quantos tamanhos ainda têm estoque. A barra é leitura
   periférica; o texto ao lado carrega o dado, porque cor sozinha não informa.
   Nome próprio: `.nz-grade` já é a TABELA de variações do formulário, e esta
   regra, por vir depois, transformava aquela tabela em flex container. */
.nz-cobertura { display: flex; align-items: center; gap: var(--nz-space-2); }
.nz-cobertura-barra {
  display: block; width: 44px; height: 4px; flex: none;
  border-radius: 999px; background: var(--nz-surface-sunken);
  border: 1px solid var(--nz-border); overflow: hidden;
}
.nz-cobertura-barra > span { display: block; height: 100%; background: var(--nz-success); }
.nz-cobertura-barra[data-tom="warning"] > span { background: var(--nz-warning); }
.nz-cobertura-barra[data-tom="danger"] > span { background: var(--nz-danger); }
.nz-cobertura-texto {
  font-size: var(--nz-text-xs); color: var(--nz-text-muted);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}

.nz-selos { display: flex; flex-wrap: wrap; gap: var(--nz-space-1); justify-content: flex-start; }

/* Miniatura ausente em 2.191 dos 2.202 produtos: o vazio precisa parecer um
   pendência, não um defeito de carregamento. */
.nz-thumb[data-vazio="true"] {
  display: grid; place-items: center;
  color: var(--nz-text-muted);
  background: var(--nz-surface-sunken);
  border-style: dashed;
}
.nz-thumb[data-vazio="true"] svg { width: 18px; height: 18px; }

/* TERCEIRO estado da miniatura: a foto ESTÁ cadastrada e o arquivo não carregou
   (404 no S3). Sem isto o navegador desenha o papel rasgado, que o lojista lê
   como "a tela quebrou" — e foi por confundir os dois que o `onerror` legado
   tinha sido removido: ele trocava por um placeholder indistinguível de
   "sem foto".

   Os três estados se separam por FORMA e RÓTULO, não por cor (regra
   `color-not-only`): foto boa não tem moldura; "sem foto" é tracejada com
   ícone de paisagem; "não carregou" é SÓLIDA em tom de atenção com a paisagem
   CORTADA. Quem não distingue cor ainda lê a diferença — e o `title`/
   `aria-label` diz por extenso.

   Tom de atenção e não de perigo: o dado não se perdeu nem foi destruído, o
   arquivo é que não veio. `--nz-warning-*` existe nos dois temas.

   `:not([hidden])` NÃO é decoração: declarar `display` numa classe anula o
   atributo `hidden` (o navegador esconde por `display:none`), e sem a guarda
   cada linha mostraria as DUAS miniaturas (§58.3). */
.nz-thumb[data-erro="true"]:not([hidden]) {
  display: grid; place-items: center;
  color: var(--nz-warning-subtle-fg);
  background: var(--nz-warning-subtle);
  border: 1px solid var(--nz-warning);
  border-style: solid;
}
.nz-thumb[data-erro="true"] svg { width: 18px; height: 18px; }

/* Galeria */
.nz-galeria {
  display: grid; gap: var(--nz-space-4);
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  padding: var(--nz-space-4);
}
.nz-galeria-item {
  display: flex; flex-direction: column; gap: var(--nz-space-2);
  padding: var(--nz-space-3);
  border: 1px solid var(--nz-border); border-radius: var(--nz-radius-lg);
  background: var(--nz-surface); color: inherit; text-decoration: none;
  transition: border-color var(--nz-duration-fast) var(--nz-ease-out),
              box-shadow var(--nz-duration-fast) var(--nz-ease-out);
}
.nz-galeria-item:hover { border-color: var(--nz-border-strong); box-shadow: var(--nz-shadow-sm); }
.nz-galeria-foto {
  display: grid; place-items: center; gap: var(--nz-space-1);
  aspect-ratio: 1 / 1; border-radius: var(--nz-radius-md);
  background: var(--nz-surface-sunken); color: var(--nz-text-muted);
}
.nz-galeria-foto[data-vazio="true"] { border: 1px dashed var(--nz-border); color: var(--nz-text-muted); }
.nz-galeria-foto svg { width: 28px; height: 28px; }
.nz-galeria-ini { font-size: var(--nz-text-2xl); font-weight: var(--nz-weight-semibold); }
.nz-galeria-vazio { font-size: var(--nz-text-xs); }
.nz-galeria-nome {
  font-size: var(--nz-text-sm); font-weight: var(--nz-weight-medium);
  color: var(--nz-text); line-height: 1.35;
}
.nz-galeria-preco {
  font-size: var(--nz-text-sm); font-weight: var(--nz-weight-semibold);
  font-variant-numeric: tabular-nums; color: var(--nz-text);
}

/* Ações dentro de um aviso: a frase explica, o botão resolve. Sem elas o
   aviso só informa que existe um problema e deixa o lojista procurar onde. */
.nz-callout-acoes { display: flex; flex-wrap: wrap; gap: var(--nz-space-2); margin-top: var(--nz-space-3); }
.nz-callout p { margin: 0; }

/* Segunda linha da barra de filtros: nota à esquerda, controle de exibição
   colado na ponta direita, acima da tabela que ele governa. */
.nz-toolbar[data-alinha="fim"] { justify-content: space-between; }
.nz-toolbar-nota {
  margin: 0; flex: 1 1 320px; min-width: 0;
  font-size: var(--nz-text-xs); color: var(--nz-text-muted); line-height: 1.5;
}
.nz-toolbar-nota .nz-sugestao { margin-right: var(--nz-space-2); vertical-align: 1px; }

/* Prévia do reajuste em massa: de → para, lado a lado. Aplicar uma regra
   percentual sem ver o resultado é assinar em branco em 200 produtos. */
.nz-previa-preco {
  display: flex; flex-direction: column;
  border: 1px solid var(--nz-border); border-radius: var(--nz-radius-md);
  background: var(--nz-surface-sunken); overflow: hidden;
}
.nz-previa-linha {
  display: grid; align-items: center; gap: var(--nz-space-2);
  grid-template-columns: minmax(0, 1fr) auto 16px auto;
  padding: var(--nz-space-2) var(--nz-space-3);
  font-size: var(--nz-text-sm);
  border-bottom: 1px solid var(--nz-border);
}
.nz-previa-linha:last-child { border-bottom: 0; }
.nz-previa-linha svg { width: 16px; height: 16px; color: var(--nz-text-muted); }
.nz-previa-nome {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--nz-text-secondary);
}
.nz-previa-de { color: var(--nz-text-muted); font-variant-numeric: tabular-nums; }
.nz-previa-para {
  font-weight: var(--nz-weight-semibold); color: var(--nz-text);
  font-variant-numeric: tabular-nums;
}

/* Numa lista de 20 linhas, um caminho de categoria em três linhas dobra a
   altura da tabela e some com o resto do catálogo. A linha de apoio fica em
   uma só, com o texto inteiro no title. */
.nz-media .nz-cell-secondary { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nz-table td[data-label="Referência"] .nz-cell-secondary { white-space: nowrap; }
.nz-galeria-estoque { display: flex; flex-wrap: wrap; align-items: center; gap: var(--nz-space-2); }
.nz-galeria-estoque .nz-num { font-size: var(--nz-text-sm); color: var(--nz-text-secondary); }

/* ==========================================================================
   Ponte com o painel legado — barra lateral (Fase 2 do redesenho)

   Estas regras usam nomes de classe do `modern-sidebar.css` de propósito: o
   menu ainda é o componente legado, e a IA nova precisa de dois elementos que
   ele não tem. Ficam aqui, e não no CSS legado, para que a Fase 2 seja
   revertida com um `git checkout` de um arquivo só.
   ========================================================================== */

/* Rótulo de grupo. Substitui o `.nav-group-header` recolhível: com 14 itens
   tudo cabe aberto, e sanfona fechada obriga a adivinhar onde está o que se
   procura — que é o problema que a IA nova resolve. */
.modern-sidebar .nav-section-title {
  padding: 0 0.75rem;
  margin-bottom: 0.375rem;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--nz-text-muted);
  opacity: 0.6;
}
.modern-sidebar .nav-section-items { display: flex; flex-direction: column; }

/* O respiro de 1.5rem entre seções foi dimensionado para quatro sanfonas
   fechadas. Com cinco grupos abertos ele empurra "Conexões" para fora da
   tela sem necessidade — o rótulo já separa os grupos sozinho. */
.modern-sidebar .nav-section:has(> .nav-section-title) { margin-bottom: 0.875rem; }

/* --------------------------------------------------------------------------
   Ponte com o painel legado — topbar e paleta de comandos (Fase 2)
   -------------------------------------------------------------------------- */

/* Trilha + título. Antes a topbar tinha só o botão de menu: em
   `painel/produtos_editar` a única pista de onde se estava era o item aceso
   no menu lateral, que some no mobile. */
.nz-topbar-local { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.nz-trilha {
  display: flex; align-items: center; flex-wrap: wrap; gap: 0.375rem;
  font-size: 0.75rem; line-height: 1.2; color: var(--nz-text-muted);
}
/* O `a { color: var(--nz-text-secondary) }` solto do template legado alcança a trilha;
   estes seletores têm especificidade maior e vencem sem !important. */
.nz-trilha a { color: var(--nz-text-muted); text-decoration: none; }
.nz-trilha a:hover { color: var(--nz-text); text-decoration: underline; }
.nz-trilha [aria-current="page"] { color: var(--nz-text); font-weight: 500; }
.nz-trilha-sep { opacity: 0.45; }
.modern-topbar .topbar-title {
  margin: 0; font-size: 1.0625rem; font-weight: 600;
  line-height: 1.25; color: var(--nz-text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* Gatilho da busca. Ocupa o vão central da topbar, que estava vazio. */
.nz-busca-trigger {
  display: flex; align-items: center; gap: 0.5rem;
  flex: 0 1 380px; margin: 0 auto; min-width: 0;
  height: 36px; padding: 0 0.75rem;
  background: var(--nz-surface-hover); color: var(--nz-text-muted);
  border: 1px solid var(--nz-border); border-radius: 0.5rem;
  font: inherit; font-size: 0.8125rem; cursor: pointer;
  transition: border-color 0.15s ease, color 0.15s ease;
}
.nz-busca-trigger:hover { border-color: var(--nz-text-muted); color: var(--nz-text); }
.nz-busca-rotulo { flex: 1; text-align: left; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.nz-busca-kbd,
.nz-paleta kbd {
  padding: 1px 5px; border-radius: 4px; font-size: 0.6875rem; font-family: inherit;
  border: 1px solid var(--nz-border); color: var(--nz-text-muted);
}

/* A paleta usa `.nz-palette-*` do design system — eu havia escrito uma
   segunda, `.nz-paleta-*`, sem ícone por item, sem grupo, sem destaque do
   termo e sem rodapé de atalhos. Era a quinta vez que inventei um nome que
   já existia; as regras próprias saíram. */

/* Abaixo de 900px a topbar já disputa espaço com o título; o rótulo e o
   atalho saem e sobra o ícone, que continua com 36px de alvo. */
@media (max-width: 900px) {
  .nz-busca-trigger { flex: none; margin-left: auto; width: 36px; padding: 0; justify-content: center; }
  .nz-busca-rotulo, .nz-busca-kbd { display: none; }
}
@media (max-width: 600px) {
  .nz-trilha { display: none; }
}

/* ==========================================================================
   Sobre o Reboot do Bootstrap — e por que NÃO há um reset aqui

   Medição no painel: das 1.913 regras legadas carregadas, 12 alcançam
   elementos .nz-, todas do Reboot (`*`, `label`, `button`,
   `input/select/textarea`, `kbd`, `code/pre/samp`).

   Tentei neutralizá-las com um reset escopado em `.nz-shell`. Foi erro: essa
   classe fica no <body>, então o reset alcançou TODO button, input, label e
   select do painel e desmontou as telas legadas — que dependem do Reboot que
   eu estava removendo. Escopar por ancestral não isola nada quando o
   ancestral é o documento.

   O caminho certo é o que este arquivo já vinha fazendo: cada componente .nz-
   declara o que precisa. É por isso que `.nz-busca-kbd` traz fundo e cor
   próprios em vez de herdar os do `kbd` do Reboot. Doze regras não pagam um
   reset global; pagam doze declarações explícitas.
   ========================================================================== */

/* ==========================================================================
   Shell — barra lateral e topbar reconstruídas (etapa 2b)
   ========================================================================== */

/* Cabeçalho da barra: nome E domínio. Quem opera mais de uma loja precisa
   saber em qual está antes de mexer em preço — o nome sozinho não basta
   quando as lojas se chamam parecido. */
.nz-loja {
  display: flex; align-items: center; gap: var(--nz-space-2);
  padding: var(--nz-space-3);
  border-bottom: 1px solid var(--nz-border);
}
.nz-loja-marca {
  display: grid; place-items: center;
  width: 34px; height: 34px; flex: none;
  border-radius: var(--nz-radius-md);
  background: var(--nz-primary); color: var(--nz-primary-fg);
  font-weight: var(--nz-weight-semibold); font-size: var(--nz-text-sm);
}
.nz-loja-txt { display: flex; flex-direction: column; min-width: 0; flex: 1; line-height: 1.25; }
.nz-loja-nome {
  font-size: var(--nz-text-sm); font-weight: var(--nz-weight-semibold);
  color: var(--nz-text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nz-loja-dominio {
  font-size: var(--nz-text-xs); color: var(--nz-text-muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Trocar de loja e abrir a loja são ações diferentes e ficavam na mesma seta.
   Separadas, cada uma diz o que faz. */
.nz-loja-acoes { display: flex; gap: 2px; flex: none; }
.nz-loja-acoes button,
.nz-loja-acoes a {
  display: grid; place-items: center; width: 30px; height: 30px;
  border: 0; border-radius: var(--nz-radius-sm); background: transparent;
  color: var(--nz-text-muted); cursor: pointer;
}
.nz-loja-acoes button:hover,
.nz-loja-acoes a:hover { background: var(--nz-surface-hover); color: var(--nz-text); }
.nz-loja-acoes svg { width: 15px; height: 15px; }

/* Contagem no item de menu. É o que separa um menu que navega de um que
   avisa: nesta loja são 537 pedidos aguardando pagamento, e o número
   pertence ao lugar onde se age sobre ele. */
.modern-sidebar .nav-conta {
  margin-left: auto; flex: none;
  padding: 1px 7px; border-radius: 999px;
  font-size: 0.6875rem; font-weight: 600;
  font-variant-numeric: tabular-nums;
  background: var(--nz-warning-subtle); color: var(--nz-warning-subtle-fg);
}

/* Conta na topbar */
.nz-conta { position: relative; }
/* `.nz-conta-botao` e `.nz-conta-avatar` saíram: o botão passou a usar
   `.nz-menu-avatar`, que o design system já desenha. */
.nz-conta-menu {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: 9998;
  min-width: 232px; padding: var(--nz-space-1);
  background: var(--nz-surface-raised);
  border: 1px solid var(--nz-border);
  border-radius: var(--nz-radius-lg); box-shadow: var(--nz-shadow-lg);
}
.nz-conta-menu[hidden] { display: none; }
.nz-conta-cabecalho {
  display: flex; flex-direction: column; gap: 1px;
  padding: var(--nz-space-3); margin-bottom: var(--nz-space-1);
  border-bottom: 1px solid var(--nz-border);
}
.nz-conta-cabecalho strong { font-size: var(--nz-text-sm); color: var(--nz-text); }
.nz-conta-cabecalho span {
  font-size: var(--nz-text-xs); color: var(--nz-text-muted);
  overflow: hidden; text-overflow: ellipsis;
}
.nz-conta-menu a {
  display: block; padding: var(--nz-space-2) var(--nz-space-3);
  border-radius: var(--nz-radius-sm);
  color: var(--nz-text); text-decoration: none; font-size: var(--nz-text-sm);
}
.nz-conta-menu a:hover { background: var(--nz-surface-hover); color: var(--nz-text); text-decoration: none; }
.nz-conta-menu a[data-tom="danger"] { color: var(--nz-danger); }
.nz-conta-sep { height: 1px; margin: var(--nz-space-1) 0; background: var(--nz-border); }

/* Cabeçalho de página das telas ainda não migradas. O título saiu da topbar
   e passou a viver na página, como no protótipo — mas as ~30 telas legadas
   não têm um. Este é emitido pelo template para todas elas, e sai sozinho
   quando a tela migrada trouxer o seu. */
.nz-page-header-legado {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: var(--nz-space-4); margin-bottom: var(--nz-space-5);
}
.nz-page-header-legado .nz-page-title {
  margin: 0; font-size: var(--nz-text-2xl); font-weight: var(--nz-weight-semibold);
  color: var(--nz-text); line-height: 1.2;
}

/* --------------------------------------------------------------------------
   As regras que mediam `.modern-sidebar`, `.modern-topbar`, `.main-container`
   e `.nz-recolher` viviam aqui e foram removidas: o shell deixou de usar os
   nomes legados e passou a emitir as classes do protótipo, que este arquivo
   já desenha. Ajustar dois vocabulários para dizerem a mesma coisa era o
   trabalho que esta limpeza dispensa.
   -------------------------------------------------------------------------- */


/* --------------------------------------------------------------------------
   Base do documento — assume o que o <style> do template fazia

   Com o CSS legado fora, ninguém mais define fundo, cor e fonte do body. Estas
   quatro declarações substituem as ~394 linhas removidas, agora saindo dos
   tokens em vez de um segundo conjunto de variáveis paralelo.
   -------------------------------------------------------------------------- */
html { color-scheme: light dark; }
body {
  margin: 0;
  background: var(--nz-surface-sunken);
  color: var(--nz-text);
  font-family: var(--nz-font-sans);
  font-size: var(--nz-text-md);
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}
*, *::before, *::after { box-sizing: border-box; }

/* Links de conta dentro do drawer — o que era o rodapé do dropdown. */
.nz-conta-links { display: flex; flex-direction: column; gap: 2px; padding-top: var(--nz-space-2); border-top: 1px solid var(--nz-border); }
.nz-conta-links a {
  padding: var(--nz-space-2) 0; color: var(--nz-text-secondary);
  text-decoration: none; font-size: var(--nz-text-sm);
}
.nz-conta-links a:hover { color: var(--nz-text); text-decoration: underline; }
.nz-conta-links a[data-tom="danger"] { color: var(--nz-danger); }

/* ============================================================== nzTextoRico
   Editor de texto enxuto (assets/painel/js/nz-texto-rico.js). Mesma borda,
   raio e anel de foco dos campos; a área usa a tipografia de leitura. */
.nz-rte {
  border: 1px solid var(--nz-border-strong); border-radius: var(--nz-radius-md);
  background: var(--nz-surface); overflow: hidden;
  transition: border-color var(--nz-duration-fast) var(--nz-ease-out),
              box-shadow var(--nz-duration-fast) var(--nz-ease-out);
}
.nz-rte:hover { border-color: var(--nz-text-muted); }
.nz-rte:focus-within { border-color: var(--nz-primary); box-shadow: 0 0 0 3px var(--nz-primary-ring); }
.nz-rte-barra {
  display: flex; align-items: center; gap: 2px; flex-wrap: wrap;
  padding: 4px; border-bottom: 1px solid var(--nz-border); background: var(--nz-surface-sunken);
}
.nz-rte-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  min-width: 34px; height: 34px; padding: 0 8px; border: 0; border-radius: var(--nz-radius-sm);
  background: transparent; color: var(--nz-text-secondary); cursor: pointer;
  font: inherit; font-size: var(--nz-text-xs); font-weight: var(--nz-weight-medium);
  transition: background var(--nz-duration-fast), color var(--nz-duration-fast);
}
.nz-rte-btn:hover { background: var(--nz-surface-hover); color: var(--nz-text); }
.nz-rte-btn:focus-visible { outline: 2px solid var(--nz-border-focus); outline-offset: 1px; }
.nz-rte-btn[aria-pressed="true"] { background: var(--nz-primary-subtle); color: var(--nz-primary-subtle-fg); }
.nz-rte-sep { width: 1px; height: 20px; margin: 0 4px; background: var(--nz-border); }
.nz-rte-espaco { flex: 1; }
.nz-rte-modelo { border: 1px dashed var(--nz-border-strong); }
.nz-rte-link {
  display: flex; gap: var(--nz-space-2); align-items: center;
  padding: var(--nz-space-2); border-bottom: 1px solid var(--nz-border); background: var(--nz-surface);
}
.nz-rte-link[hidden] { display: none; }
.nz-rte-link .nz-input { min-height: 34px; flex: 1; }
.nz-rte-area {
  position: relative; min-height: 180px; max-height: 460px; overflow-y: auto;
  padding: var(--nz-space-3) var(--nz-space-4); outline: none;
  color: var(--nz-text); font-size: var(--nz-text-md); line-height: 1.6; overflow-wrap: anywhere;
}
/* O anel de foco é da caixa inteira (:focus-within), não da área. */
.nz-app .nz-rte-area:focus-visible { outline: none; }
.nz-rte-vazio .nz-rte-area::before {
  content: attr(data-placeholder); color: var(--nz-text-muted); pointer-events: none; position: absolute;
}
.nz-rte-area p, .nz-rte-area ul, .nz-rte-area ol, .nz-rte-area blockquote { margin: 0 0 0.6em; }
.nz-rte-area ul { list-style: disc; padding-left: 1.4em; }
.nz-rte-area ol { list-style: decimal; padding-left: 1.4em; }
.nz-rte-area li { margin: 0.15em 0; }
.nz-rte-area h3 { font-size: 1.05em; font-weight: var(--nz-weight-semibold); margin: 0.9em 0 0.35em; }
.nz-rte-area h3:first-child { margin-top: 0; }
.nz-rte-area a { color: var(--nz-primary); text-decoration: underline; text-underline-offset: 3px; }
.nz-rte-area blockquote { border-left: 3px solid var(--nz-border); padding-left: 0.75em; color: var(--nz-text-secondary); }
.nz-rte-area > :last-child { margin-bottom: 0; }
.nz-rte-area .nz-rte-ph::before { content: attr(data-ph); color: var(--nz-text-muted); pointer-events: none; }
.nz-rte-rodape {
  display: flex; flex-wrap: wrap; gap: var(--nz-space-1) var(--nz-space-3); justify-content: space-between;
  margin-top: var(--nz-space-1);
}
@media (max-width: 640px) {
  .nz-rte-btn-txt { display: none; }
  .nz-rte-area { min-height: 150px; }
}
