/* ZapFlow Design System V1 — tokens globais (fonte única de verdade).
   Incluir <link rel="stylesheet" href="/ds.css"> nas páginas. Use var(--zf-*). */

/* Fonte de marca (paridade com a referência): Figtree, AUTO-HOSPEDADA (licença
   OFL em /fonts/figtree/OFL.txt), subconjunto latin (cobre acentos do PT-BR).
   Inter fica só como fallback na pilha --ds-font/--zf-font. */
@font-face { font-family: "Figtree"; font-style: normal; font-weight: 400; font-display: swap; src: url("/fonts/figtree/figtree-latin-400-normal.woff2") format("woff2"); }
@font-face { font-family: "Figtree"; font-style: normal; font-weight: 500; font-display: swap; src: url("/fonts/figtree/figtree-latin-500-normal.woff2") format("woff2"); }
@font-face { font-family: "Figtree"; font-style: normal; font-weight: 600; font-display: swap; src: url("/fonts/figtree/figtree-latin-600-normal.woff2") format("woff2"); }
@font-face { font-family: "Figtree"; font-style: normal; font-weight: 700; font-display: swap; src: url("/fonts/figtree/figtree-latin-700-normal.woff2") format("woff2"); }

/* ============================================================================
   SISTEMA DE TEMA (Light / Dark) — tokens semânticos --t-*.
   O DARK é o visual atual do ZapFlow (fonte da verdade das cores existentes).
   O LIGHT é a versão branca/clean. O theme.js aplica data-theme no <html>.
   Regra: componentes usam var(--t-*); NUNCA hex fixo pra cor estrutural.
   ============================================================================ */
:root, :root[data-theme="dark"] {
  /* DARK neutro (cinza/preto), estilo ZapData — sem o azul-marinho antigo.
     Mantém o VERDE da marca como acento. */
  --t-bg:#0B0B0D;            /* base do app */
  --t-bg-2:#121214;          /* área de conteúdo / fundo alternativo */
  --t-sidebar:#0E0E10;       /* barra lateral */
  --t-surface:#191919;       /* cards */
  --t-surface-2:#1F1F21;     /* header de tabela / card alternativo */
  --t-surface-3:#242426;     /* superfície elevada */
  --t-surface-hover:#2A2A2D; /* hover de linha/superfície */
  --t-input:#141416;         /* fundo de inputs */
  --t-border:#2A2A2E;        /* borda padrão */
  --t-border-2:#34343A;      /* borda de modal/elevado */
  --t-border-soft:#1F1F22;   /* borda muito sutil / divisórias */
  --t-text:#F5F5F6;          /* texto primário */
  --t-text-strong:#FAFAFA;   /* títulos */
  --t-text-1b:#D4D4D8;       /* secundário forte */
  --t-text-2:#A1A1AA;        /* secundário */
  --t-text-3:#71717A;        /* muted */
  --t-accent:#10B981;        /* verde da marca */
  --t-accent-2:#34D399;      /* verde claro (texto ativo) */
  --t-accent-hover:#0EA472;
  --t-accent-soft:rgba(16,185,129,.12);
  --t-on-accent:#02140F;     /* texto sobre o verde */
  --t-hover:rgba(255,255,255,.05); /* hover translúcido sutil */
  --t-shadow:0 1px 2px rgba(0,0,0,.35);
  --t-shadow-pop:0 14px 34px rgba(0,0,0,.6);
  /* Chat (Atendimento) — bolhas (estilo WhatsApp dark) */
  --t-chat-bg:#0B0B0D;
  --t-bubble-in:#202024;   /* recebida (lead) — cinza */
  --t-bubble-out:#0B3D2E;  /* enviada (robô) — verde escuro */
  --t-bubble-op:#1D3A33;   /* operador (manual) — verde-acinzentado */
}
:root[data-theme="light"] {
  --t-bg:#FFFFFF;
  --t-bg-2:#F8FAFC;
  --t-sidebar:#FFFFFF;
  --t-surface:#FFFFFF;
  --t-surface-2:#F8FAFC;
  --t-surface-3:#F1F5F9;
  --t-surface-hover:#F1F5F9;
  --t-input:#FFFFFF;
  --t-border:#E2E8F0;
  --t-border-2:#E2E8F0;
  --t-border-soft:#EEF2F7;
  --t-text:#0F172A;
  --t-text-strong:#0F172A;
  --t-text-1b:#334155;
  --t-text-2:#64748B;
  --t-text-3:#94A3B8;
  --t-accent:#10B981;
  --t-accent-2:#059669;
  --t-accent-hover:#059669;
  --t-accent-soft:#ECFDF5;
  --t-on-accent:#FFFFFF;
  --t-hover:rgba(15,23,42,.04);
  --t-shadow:0 1px 2px rgba(15,23,42,.06);
  --t-shadow-pop:0 14px 34px rgba(15,23,42,.12);
  --t-chat-bg:#EFF2F5;
  --t-bubble-in:#FFFFFF;
  --t-bubble-out:#D9FDD3;
  --t-bubble-op:#E3F2FD;
}
/* Transição suave ao alternar tema (curta, sem flash). */
html, body { background:var(--t-bg); color:var(--t-text); }
html, body, .zf-rail, .zf-item, .zf-surface, [class*="card"], input, select, textarea, button {
  transition: background-color 180ms ease, border-color 180ms ease, color 180ms ease;
}

:root {
  /* Superfícies (dark neutro) */
  --zf-bg: #0E0E10;          /* fundo principal / sidebar */
  --zf-surface-1: #191919;   /* cards */
  --zf-surface-2: #1F1F21;   /* cards alternativos / header de tabela */
  --zf-surface-3: #242426;   /* superfície elevada */
  /* Bordas */
  --zf-border: #2A2A2E;
  --zf-border-soft: rgba(180,180,190,.12);
  --zf-border-hair: rgba(180,180,190,.10);
  /* Texto */
  --zf-text: #F5F5F6;
  --zf-text-2: #A1A1AA;
  --zf-text-3: #71717A;
  /* Marca / semânticas */
  --zf-green: #10B981;
  --zf-blue: #3B82F6;
  --zf-blue-strong: #2563EB;
  --zf-danger: #EF4444;
  --zf-warning: #F59E0B;
  /* Estados translúcidos */
  --zf-green-bg: rgba(16,185,129,.12);
  --zf-danger-bg: rgba(239,68,68,.10);
  --zf-warning-bg: rgba(245,158,11,.10);
  --zf-hover: rgba(255,255,255,.04);
  /* Layout */
  --zf-radius: 10px;
  --zf-radius-card: 14px;
  --zf-radius-lg: 16px;
  --zf-h-control: 40px;
  --zf-font: "Figtree", "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --zf-shadow-card: 0 1px 2px rgba(0,0,0,.35);
  --zf-shadow-pop: 0 14px 34px rgba(0,0,0,.6);

  /* ===== Design System V2 (redesign) — tokens novos, ADITIVOS (não removem os --zf-*) ===== */
  /* Superfícies (dark neutro) */
  --bg-main:#0B0B0D; --bg-sidebar:#0E0E10; --bg-header:#0E0E10;
  --bg-card:#191919; --bg-card-hover:#26262A; --bg-elevated:#242426;
  /* Bordas */
  --border-primary:#2A2A2E; --border-secondary:#34343A;
  /* Texto */
  --text-primary:#F5F5F6; --text-secondary:#A1A1AA; --text-muted:#71717A;
  /* Marca */
  --brand-cyan:#23D5D5; --brand-blue:#2196F3; --brand-green:#18D6A3;
  --brand-gradient:linear-gradient(135deg,#18D6A3 0%,#23D5D5 45%,#258BFF 100%);
  /* Semânticas */
  --success:#18D6A3; --warning:#F4B740; --danger:#FF4D5E; --info:#339AF0;
  /* Espaçamento (base 4px) */
  --space-1:4px; --space-2:8px; --space-3:12px; --space-4:16px; --space-5:20px;
  --space-6:24px; --space-8:32px; --space-10:40px; --space-12:48px; --space-16:64px;
  /* Raio */
  --radius-sm:6px; --radius-md:8px; --radius-lg:12px;
  /* Layout */
  --sidebar-width:240px; --header-height:72px;
  /* Sombras discretas */
  --shadow-sm:0 8px 24px rgba(0,0,0,.15); --shadow-pop2:0 12px 30px rgba(0,0,0,.35);
}

/* Fundo da aplicação (opt-in): cinza/preto neutro com um toque de verde discreto. */
.zf-app-bg {
  background:
    radial-gradient(circle at 12% 100%, rgba(16,185,129,.04), transparent 30%),
    var(--bg-main);
}

/* Scrollbar discreta para o tema dark (global, seguro). */
/* Scrollbar fina — 8px, track transparente, thumb = --ds-border-strong
   (retificação do proprietário, 2026-09-28, item 6). Usa `var(--ds-*)` em
   vez de hex fixo: antes era hex fixo escuro (#2c2c31/#3a3a40), correto só
   no tema dark — no claro a scrollbar ficava escura demais sobre fundo
   branco. Os tokens `--ds-*` já existem em `:root` independente de
   `data-ds="v2"` (comentário da seção DS V2 acima), então isto se aplica a
   TODA página automaticamente, nos dois temas, sem exigir opt-in. */
* { scrollbar-width: thin; scrollbar-color: var(--ds-border-strong) transparent; }
*::-webkit-scrollbar { width: 8px; height: 8px; }
*::-webkit-scrollbar-thumb { background: var(--ds-border-strong); border-radius: 8px; border: 1px solid transparent; background-clip: padding-box; }
*::-webkit-scrollbar-thumb:hover { background: var(--ds-text-muted); background-clip: padding-box; }
*::-webkit-scrollbar-track { background: transparent; }

/* Botão CTA com gradiente da marca (para telas novas). */
.zf-btn-brand { background: var(--brand-gradient); color:#02140F; font-weight:600; border:none; height:var(--zf-h-control); padding:0 16px; border-radius:var(--radius-md); cursor:pointer; display:inline-flex; align-items:center; gap:8px; box-shadow:0 4px 16px rgba(24,214,163,.14); transition:transform .15s ease, filter .15s ease; }
.zf-btn-brand:hover { transform: translateY(-1px); filter: brightness(1.03); }

/* Caixa de ícone minimalista (cards/stat). */
.zf-icon-box { width:40px; height:40px; border-radius:10px; background:rgba(255,255,255,.04); border:1px solid rgba(255,255,255,.06); display:flex; align-items:center; justify-content:center; color:#DCE7F5; flex:none; }

/* Helpers genéricos (opcionais) — para telas novas usarem direto. */
.zf-badge { display:inline-flex; align-items:center; gap:6px; font-size:12px; font-weight:600; padding:4px 10px; border-radius:999px; }
.zf-badge .dot { width:8px; height:8px; border-radius:50%; }
.zf-badge.ok { background:var(--zf-green-bg); color:var(--zf-green); } .zf-badge.ok .dot { background:var(--zf-green); }
.zf-badge.off { background:var(--zf-danger-bg); color:var(--zf-danger); } .zf-badge.off .dot { background:var(--zf-danger); }
.zf-badge.warn { background:var(--zf-warning-bg); color:var(--zf-warning); } .zf-badge.warn .dot { background:var(--zf-warning); }

/* ============================================================================
   DESIGN SYSTEM V2 — namespace `ds` (ADR 0009, direção D1 "Evolução da
   identidade"). Ver docs/design-system/tokens.md (arquitetura, mapeamento
   legado, contraste medido) e docs/design-system/componentes.md.

   ESTE BLOCO É ADITIVO E INERTE POR PADRÃO: os tokens `--ds-*` abaixo NÃO
   alteram nenhuma tela existente (nada os consome hoje). Uma tela só entra
   no DS V2 declarando `<html data-ds="v2">` — aí, e SÓ aí, a "ponte" no
   final deste bloco remapeia os `--t-*`/`--zf-*` legados (que os componentes
   de UI já consomem) para os valores `--ds-*`. Páginas sem `data-ds="v2"`
   continuam pixel-a-pixel iguais.

   Paridade dark/light: TODO token de cor/sombra abaixo existe nos dois temas
   (testado em backend/test/design-system-tokens.test.ts). Tokens que não são
   cor/sombra (tipografia, espaço, raio, duração) não variam por tema e ficam
   num único bloco `:root` — não fazem parte do teste de paridade.
   ============================================================================ */
:root, :root[data-theme="dark"] {
  /* Superfícies — RECALIBRAÇÃO DA ESCALA DE NEUTROS (retificação do
     proprietário, 2026-09-28, item 2: "paridade EXTREMAMENTE próxima" —
     ver docs/design-system/zapdata-derivado.md §9). Medido nos CSS vars
     computados da referência (`00-shell-dashboard.json` `root`):
     `--background` = 0 0% 7% (`--ds-bg`), `--card` = 0 0% 10% (`--ds-surface`/
     `--ds-card`), `--secondary` = 0 0% 14% (`--ds-surface-2`), `--muted` = 0
     0% 18% = `--border` = 0 0% 18% (mesmo valor na referência — `--ds-
     surface-elevated`/`--ds-border` ficam iguais, como no medido).
     `--ds-bg-subtle`/`--ds-input`/`--ds-surface-hover`/`--ds-border-subtle`
     não têm CSS var própria na referência (só os 5 papéis acima) —
     interpolados PROPORCIONALMENTE mantendo a hierarquia bg < bg-subtle <
     card < secondary < elevated/border < hover < border-strong (nunca
     "inventando" um papel que a referência não tem, só preenchendo o degrau
     entre dois papéis medidos). Substitui a escala "preto mais puro"
     anterior (bg `#0A0A0C`), que a fatia anterior (§9, agora revogado pela
     retificação) preservava deliberadamente. */
  --ds-bg:#121212; --ds-bg-subtle:#161616;
  --ds-surface:#1A1A1A; --ds-surface-elevated:#2E2E2E; --ds-surface-2:#242424;
  --ds-surface-hover:#383838; --ds-card:#1A1A1A; --ds-input:#161616;
  /* Cromo da sidebar/header — RECONSTRUÇÃO DO APPSHELL GLOBAL (2026-09-28,
     docs/design-system/zapdata-derivado.md §6): a referência (ZapData) troca
     TODO o cromo (header+sidebar) para claro quando o tema é claro — decisão
     anterior (ADR 0009 D1) mantinha o cromo sempre escuro só por causa do
     texto "Zap" (quase branco) da logo ficar ilegível sobre fundo claro.
     Retificação do proprietário: o cromo agora SEGUE O TEMA como o resto da
     página (tokens abaixo variam dark/light, como qualquer --ds-* de cor);
     rail.js troca a logo por zapflow-symbol.png + texto "ZapFlow" (cor de
     texto do tema) só no claro, onde a wordmark completa perderia legibilidade
     — a wordmark cheia (zapflow-logo.png) continua no escuro, sem mudança. */
  --ds-sidebar:#0D0D0F; --ds-sidebar-text:#FAFAFA; --ds-sidebar-text-2:#B3B3B3;
  --ds-sidebar-border:#1E1E22; --ds-sidebar-hover:rgba(255,255,255,.05);
  --ds-sidebar-active:rgba(10,106,245,.14); --ds-sidebar-active-glow:rgba(10,106,245,.15);
  --ds-sidebar-accent:#0A6AF5; --ds-sidebar-accent-text:#4D9DFF;
  /* Fixo nos dois temas (chip da logo em login/cadastro — ver o componente
     `.ds-auth-brand-chip` abaixo). Mesmo valor de `--ds-sidebar` no escuro,
     mas um token PRÓPRIO porque `--ds-sidebar` agora segue o tema. */
  --ds-brand-chip-bg:#0D0D0F;
  /* Bordas — `--ds-border` = `--muted`/`--border` medidos (18%, mesmo valor
     de `--ds-surface-elevated`, item 2 da retificação). `--ds-border-subtle`
     interpolado mais perto de bg/card (12%, sem CSS var própria na
     referência). `--ds-border-strong` (borda "de função" de botão/input —
     ver `.ds-btn-secondary`/`.ds-input` abaixo) SOBE de 18% pra ~42%: a
     referência usa o MESMO tom pra borda de função e divisor (`--input` = 0
     0% 90% = `--border` no dump claro), mas isso reprova o piso de contraste
     não-textual AA (3:1, `design-system-tokens.test.ts` item 5) contra o
     `--ds-bg`/`--ds-surface` novos, mais claros — o mesmo tipo de ajuste já
     documentado pra `--ds-text-muted` abaixo, registrado em tokens.md §2.1. */
  --ds-border:#2E2E2E; --ds-border-subtle:#1F1F1F; --ds-border-strong:#6B6B6B;
  /* Texto — `--ds-text` = `--foreground` medido (98%). `--ds-text-muted`
     seria 55% pelo `--muted-foreground` medido, mas contra o novo
     `--ds-surface-elevated` (18%, mais claro que antes) isso cai pra 4.04:1
     — abaixo do piso AA 4.5:1 (`design-system-tokens.test.ts` item 5, testa
     texto secundário/muted contra TODA superfície onde pode aparecer,
     inclusive a mais clara). Subiu pro mínimo que ainda fecha AA nas 5
     superfícies (bg/bg-subtle/surface/surface-elevated/input): 59%. Mesma
     lógica pro claro (`--muted-foreground` 45% cai pra 4.35:1 contra
     `--ds-surface-elevated` 96% — desce pro mínimo que fecha, 44%).
     `--ds-text-secondary` não tem CSS var própria medida — interpolado entre
     `--ds-text-muted` e `--ds-text` (mantém a mesma folga de contraste que
     já tinha antes desta fatia). Ver tokens.md §2.1 para a tabela completa. */
  --ds-text:#FAFAFA; --ds-text-secondary:#B3B3B3; --ds-text-muted:#969696;
  --ds-text-on-accent:#FFFFFF;
  /* Accent — AZUL do logo ZapFlow (retificação do proprietário, 2026-09-28:
     mapeamento cromático da reconstrução global — o laranja da referência
     ZapData vira a cor de identidade do ZapFlow, não o verde; ver
     docs/design-system/zapdata-derivado.md §5). O verde permanece só como
     SEMÂNTICA de sucesso (--ds-success, inalterado). hover −8%/active −14%
     de luminosidade sobre o accent base. */
  --ds-accent:#0A6AF5; --ds-accent-hover:#0962E1; --ds-accent-active:#095BD3;
  --ds-accent-soft:rgba(10,106,245,.14); --ds-accent-text:#4D9DFF;
  /* Semânticas */
  --ds-success:#10B981; --ds-success-soft:rgba(16,185,129,.12); --ds-success-text:#10B981;
  --ds-warning:#F5A524; --ds-warning-soft:rgba(245,165,36,.12); --ds-warning-text:#F5A524;
  --ds-danger:#F87171; --ds-danger-soft:rgba(239,68,68,.12); --ds-danger-text:#F87171;
  /* --ds-info-soft desceu de .12 pra .10 de alpha nesta fatia: com o novo
     `--ds-bg` mais claro (7% em vez de ~4%), o chip composto (info-text
     sobre info-soft-sobre-bg) caía pra 4.49:1 — abaixo do piso AA 4.5:1
     (design-system-tokens.test.ts, "info-text/info-soft (composto)"). */
  --ds-info:#3B82F6; --ds-info-soft:rgba(59,130,246,.10); --ds-info-text:#3B82F6;
  /* Interação / estado. --ds-ring: anel de foco 2px a 40% de opacidade do
     accent (retificação do proprietário, 2026-09-28, item 7) — usado em
     campo (`.ds-input`/`.ds-select`/`.ds-textarea`); botão/chip/menu-item
     continuam com outline sólido 2px (`--ds-focus`, mais visível que um
     anel translúcido — ver uso perto do fim do arquivo), sem mudança. */
  --ds-focus:#0A6AF5; --ds-ring:rgba(10,106,245,.4); --ds-hover:rgba(255,255,255,.05);
  --ds-disabled-bg:#1E1E21; --ds-disabled-text:#71717A;
  --ds-overlay:rgba(0,0,0,.6);
  /* Sombras */
  --ds-shadow-sm:0 1px 2px rgba(0,0,0,.4);
  --ds-shadow-pop:0 16px 36px rgba(0,0,0,.55);
  --ds-shadow-modal:0 24px 48px rgba(0,0,0,.6);
  /* Chat (Atendimento) — bolhas. Família D1: zinc neutro pro fundo/recebida
     (reaproveita --ds-surface-2), verde derivado do accent pra enviada
     (robô), e uma nuance PRÓPRIA (zinc esverdeado, não verde nem azul) pro
     operador — nunca o único sinal de quem enviou: o rótulo ".quem" ("Você
     (manual)" / "Robô") continua no HTML de cada bolha (migracao.md §20,
     leads.html 14a). Contraste medido (fórmula de luminância relativa,
     WCAG 2.1) em tokens.md §4 — corpo de texto e "-meta" (rótulo/hora)
     passam 4.5:1 nas 3 bolhas; bolha-sobre-chat-bg (não-textual, ~1.3-1.6:1)
     fica abaixo do 3:1 desejável, registrado e justificado (a bolha tem
     sombra + forma arredondada, não depende só da cor de fundo). */
  --ds-chat-bg:var(--ds-bg);
  /* S3d (paridade medida ZapData, `01-mapa-da-tela.md`): recebida
     `rgb(34,43,48)`/enviada `rgb(0,92,65)` exatas — `--ds-bubble-meta` subiu de
     `--ds-text-secondary` (#A1A1AA, 3.14:1 no verde novo — abaixo do piso
     4.5:1) para #CCCCCC (5.02:1 no verde, 8.98:1/8.48:1 nas outras duas) só
     nesta família de tokens, pra `.quem`/`.hora` (texto) continuarem AA nas 3
     bolhas — ver tabela recalculada em tokens.md §4. */
  --ds-bubble-in:#222B30;
  --ds-bubble-out:#005C41;
  --ds-bubble-op:#26302C;
  --ds-bubble-in-text:var(--ds-text);
  --ds-bubble-out-text:var(--ds-text);
  --ds-bubble-op-text:var(--ds-text);
  --ds-bubble-meta:#CCCCCC;
  /* Citação (reply) dentro do balão — scrim translúcido que funciona sobre
     qualquer uma das 3 bolhas (claro OU escuro), sem hex fixo tipo
     "branco/preto 5%": no escuro clareia a bolha, no claro escurece. */
  --ds-bubble-cita-bg:rgba(255,255,255,.08);
  --ds-bubble-cita-border:rgba(255,255,255,.35);
  /* Texto/ícone SEMPRE branco sobre superfície saturada arbitrária (chip de
     etiqueta com a cor do DADO, chip azul da conexão, botão fechar da
     lightbox sobre o scrim escuro) — não muda com o tema, ao contrário de
     `--ds-text-on-accent` (que inverte no claro/escuro porque o PRÓPRIO
     accent muda de tom). */
  --ds-chip-on-color:#FFFFFF;
}
:root[data-theme="light"] {
  /* Superfícies — mesma recalibração do bloco dark (item 2 da retificação,
     2026-09-28). Medido em `00-shell-light.json` `root`: `--background` = 0
     0% 100% (`--ds-bg`), `--card` = 0 0% 99% (`--ds-surface`/`--ds-card`),
     `--muted`/`--secondary` = 0 0% 96% (idênticos na referência — `--ds-
     surface-elevated`/`--ds-surface-2` ficam iguais, como no medido),
     `--border`/`--input` = 0 0% 90% (`--ds-border`). `--ds-bg-subtle`/
     `--ds-surface-hover`/`--ds-border-subtle` interpolados (sem CSS var
     própria medida), mesma lógica do bloco dark. */
  --ds-bg:#FFFFFF; --ds-bg-subtle:#FAFAFA;
  --ds-surface:#FCFCFC; --ds-surface-elevated:#F5F5F5; --ds-surface-2:#F5F5F5;
  --ds-surface-hover:#EFEFEF; --ds-card:#FCFCFC; --ds-input:#FAFAFA;
  /* Cromo da sidebar/header — agora SEGUE O TEMA (ver o comentário completo
     no bloco dark acima, §6 de zapdata-derivado.md). Valores medidos na
     referência clara (docs/referencia-zapdata-global/00-shell-light.json):
     header/card ~rgb(252,252,252), borda 90% (~#E5E5E5, mesmo tom de
     --ds-border light), texto = --ds-text/--ds-text-secondary do tema claro
     (reaproveitados, não são hex novo). */
  --ds-sidebar:#FCFCFC; --ds-sidebar-text:#1A1A1A; --ds-sidebar-text-2:#404040;
  --ds-sidebar-border:#E5E5E5; --ds-sidebar-hover:rgba(15,23,42,.04);
  --ds-sidebar-active:#EAF2FE; --ds-sidebar-active-glow:#EAF2FE;
  --ds-sidebar-accent:#0A5FD9; --ds-sidebar-accent-text:#0A5FD9;
  --ds-brand-chip-bg:#0D0D0F;
  /* Bordas/texto — ver o comentário completo (com a justificativa do ajuste
     de contraste AA de `--ds-text-muted`/`--ds-border-strong`) no bloco dark
     acima; aqui só o valor medido/interpolado do tema claro. */
  --ds-border:#E5E5E5; --ds-border-subtle:#EFEFEF; --ds-border-strong:#919191;
  --ds-text:#1A1A1A; --ds-text-secondary:#404040; --ds-text-muted:#707070;
  --ds-text-on-accent:#FFFFFF;
  /* Accent — AZUL do logo ZapFlow (mesma retificação do bloco dark). Ao
     contrário do verde antigo (que falhava AA no claro e precisava de um tom
     mais escuro só pra função de botão/link), o azul `#0A5FD9` já passa
     4.5:1 com texto branco nos dois papéis (marca E função) — não precisa de
     um "accent de função" separado do "accent de marca". */
  --ds-accent:#0A5FD9; --ds-accent-hover:#0957C8; --ds-accent-active:#0952BB;
  --ds-accent-soft:#EAF2FE; --ds-accent-text:#0A5FD9;
  --ds-success:#047857; --ds-success-soft:#ECFDF5; --ds-success-text:#047857;
  --ds-warning:#B45309; --ds-warning-soft:#FFFBEB; --ds-warning-text:#B45309;
  --ds-danger:#B91C1C; --ds-danger-soft:#FEF2F2; --ds-danger-text:#B91C1C;
  --ds-info:#2563EB; --ds-info-soft:#EFF6FF; --ds-info-text:#2563EB;
  --ds-focus:#0A5FD9; --ds-ring:rgba(10,95,217,.4); --ds-hover:rgba(15,23,42,.04);
  --ds-disabled-bg:#F4F4F5; --ds-disabled-text:#71717A;
  --ds-overlay:rgba(15,23,42,.45);
  --ds-shadow-sm:0 1px 2px rgba(15,23,42,.08);
  --ds-shadow-pop:0 16px 36px rgba(15,23,42,.14);
  --ds-shadow-modal:0 24px 48px rgba(15,23,42,.18);
  /* Chat (Atendimento) — ver o comentário completo no bloco dark acima. */
  --ds-chat-bg:var(--ds-bg-subtle);
  --ds-bubble-in:var(--ds-surface);
  --ds-bubble-out:#DFF7EA;
  --ds-bubble-op:#E9ECEE;
  --ds-bubble-in-text:var(--ds-text);
  --ds-bubble-out-text:var(--ds-text);
  --ds-bubble-op-text:var(--ds-text);
  --ds-bubble-meta:var(--ds-text-secondary);
  --ds-bubble-cita-bg:rgba(0,0,0,.06);
  --ds-bubble-cita-border:rgba(0,0,0,.25);
  --ds-chip-on-color:#FFFFFF;
}

/* Tokens NÃO-cor (não variam por tema — não entram no teste de paridade). */
:root {
  /* Contêiner de página (medido na referência: max-width 1400 + padding
     lateral 16 => conteúdo de 1368). Ferramentas usa o estreito (1152/32). */
  --ds-page-max:1400px; --ds-page-pad:16px;
  --ds-page-max-narrow:1152px; --ds-page-pad-narrow:32px;
  --ds-font: "Figtree", "Inter", system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  /* Fonte monoespaçada — números/IDs que não devem "dançar" de largura
     (contador de coluna do Kanban, telefone do card, ID curto da tabela de
     Contatos). Paridade de medida com a referência (docs/crm/
     referencia-zapdata/01-mapa-da-tela.md, docs/contatos/referencia-zapdata/
     01-mapa-da-tela.md: `font-mono tabular-nums`). Documentado em
     docs/design-system/tokens.md §2. */
  --ds-font-mono: ui-monospace, "SFMono-Regular", "Consolas", "Liberation Mono", monospace;
  /* Tipografia — escala e papéis (ver docs/design-system/tokens.md §2) */
  --ds-fs-xs:11px; --ds-fs-sm:12.5px; --ds-fs-md:13.5px; --ds-fs-base:14px;
  --ds-fs-lg:15px; --ds-fs-xl:19px; --ds-fs-2xl:23px;
  --ds-fw-regular:400; --ds-fw-medium:500; --ds-fw-semibold:600; --ds-fw-bold:700; --ds-fw-extrabold:800;
  --ds-lh-tight:1.2; --ds-lh-normal:1.5; --ds-lh-relaxed:1.6;
  /* Espaço — reaproveita a escala 4px que já existe (--space-*), não duplica valor. */
  --ds-space-1:var(--space-1); --ds-space-2:var(--space-2); --ds-space-3:var(--space-3);
  --ds-space-4:var(--space-4); --ds-space-5:var(--space-5); --ds-space-6:var(--space-6);
  --ds-space-8:var(--space-8); --ds-space-10:var(--space-10); --ds-space-12:var(--space-12); --ds-space-16:var(--space-16);
  /* Raio — reaproveita --radius-sm/md/lg existentes; -xl é novo (D1: 16px). */
  --ds-radius-sm:var(--radius-sm); --ds-radius-md:var(--radius-md); --ds-radius-lg:var(--radius-lg); --ds-radius-xl:16px; --ds-radius-full:999px;
  /* Movimento — só com função (princípios.md §1.7); respeita prefers-reduced-motion. */
  --ds-dur-fast:120ms; --ds-dur-normal:160ms; --ds-dur-slow:240ms;
  --ds-ease:cubic-bezier(.2,.7,.3,1);
  /* Altura de controles + alvo de toque mínimo (44px, princípios.md §2.2).
     RECALIBRADO na retificação do proprietário (2026-09-28, item 1): medido
     nos dumps `buttons`/`inputs`/`combos` (scratchpad/ref-global/*.json,
     campo `h`) — a referência usa 36px (`h-9`) pra botão/input/select/chip
     de TOOLBAR ("Importar", "Novo Contato", "Filtrar por instância",
     "Buscar leads...", "Sem filtro"...: 56 ocorrências de h=36/br=6px contra
     só 2 de h=40/br=6px) e reserva 40px pra item de nav (`.zf-item`,
     rail.js, já 40px antes desta fatia — inalterado) e CTA/ação positiva
     pontual ("Nova Campanha", verde, medida em 40px — ver `.ds-btn-success`
     abaixo). --ds-h-md desce de 40 pra 36 (afeta .ds-btn/.ds-input/.ds-
     select por padrão); --ds-h-lg desce de 48 pra 40 (CTA verde + telas de
     auth, que reusam --ds-h-lg — ver .ds-auth abaixo); --ds-h-sm mantém
     32px (já batia com o chip/pill de filtro medido, ex.: "Hoje"/"Ontem" em
     01-dashboard.json, h=32/br=9999px). O alvo de toque de 44px continua SÓ
     via `min-height:var(--ds-touch)` no media query `(pointer:coarse)`
     (linha ~1047) — não muda a altura base no desktop. */
  --ds-h-sm:32px; --ds-h-md:36px; --ds-h-lg:40px; --ds-touch:44px;
  /* z-index — compatível com o que rail.js/builder já usam em produção:
     rail=40, burger=45, overlay/backdrop=39, dropdown/menu=60 (docs/design-system/responsividade.md) */
  --ds-z-base:1; --ds-z-sticky:30; --ds-z-overlay:39; --ds-z-rail:40; --ds-z-burger:45;
  --ds-z-dropdown:60; --ds-z-modal:90; --ds-z-toast:100;
  /* AppShell global (rail.js) — medidas do header/sidebar fixos, iguais nos
     dois temas (reconstrução global, referência ZapData medida em
     docs/referencia-zapdata-global/01-design-system.md §1: header 56px,
     sidebar 256px). O cromo em si (cor) continua em --ds-sidebar-* (sempre
     escuro nos dois temas, D1 — ver o bloco de cor acima); aqui só o LAYOUT.
     --ds-z-header fica ACIMA do rail/overlay pra header nunca ficar coberto
     pela gaveta, e ABAIXO de dropdown/modal (popovers continuam por cima). */
  --ds-header-h:56px; --ds-sidebar-w:256px;
  --ds-z-header:50;
}

/* ---- Ponte de opt-in: só faz efeito com <html data-ds="v2">. -------------
   Remapeia os tokens LEGADOS (--t-*, e os --zf-* de estado ainda usados por
   conexoes.html) para os valores --ds-* da D1. Existe para o CSS PRÓPRIO de
   cada tela (seletores que não são .ds-*, ex.: `body`, `.wrap`, `.cf-form`
   no piloto) continuar lendo var(--t-*) e ainda assim ganhar a paleta D1
   quando a página declara data-ds="v2". Os componentes OFICIAIS `.ds-*`
   (ver a seção abaixo) NÃO dependem desta ponte — leem var(--ds-*)
   diretamente (docs/design-system/tokens.md §5, docs/design-system/
   componentes.md).
   Especificidade: :root[data-ds="v2"] e :root[data-theme="light"] empatam
   em (0,2,0) — por isso os dois blocos abaixo qualificam também o
   data-theme, subindo pra (0,3,0), pra nunca depender da ordem do arquivo. */
:root[data-ds="v2"], :root[data-ds="v2"][data-theme="dark"] {
  --t-bg:var(--ds-bg); --t-bg-2:var(--ds-bg-subtle); --t-sidebar:var(--ds-sidebar);
  --t-surface:var(--ds-surface); --t-surface-2:var(--ds-surface-elevated); --t-surface-3:var(--ds-surface-2);
  --t-surface-hover:var(--ds-surface-hover); --t-input:var(--ds-input);
  --t-border:var(--ds-border); --t-border-2:var(--ds-border-strong); --t-border-soft:var(--ds-border-subtle);
  --t-text:var(--ds-text); --t-text-strong:var(--ds-text); --t-text-1b:var(--ds-text-secondary);
  --t-text-2:var(--ds-text-secondary); --t-text-3:var(--ds-text-muted);
  --t-accent:var(--ds-accent); --t-accent-2:var(--ds-accent-text); --t-accent-hover:var(--ds-accent-hover);
  --t-accent-soft:var(--ds-accent-soft); --t-on-accent:var(--ds-text-on-accent);
  --t-hover:var(--ds-hover); --t-shadow:var(--ds-shadow-sm); --t-shadow-pop:var(--ds-shadow-pop);
  /* Chat (Atendimento) — pendência de tokens.md §3 resolvida na fatia
     feature/ds-v2-fatia-atendimento-lista (leads.html 14a). */
  --t-chat-bg:var(--ds-chat-bg);
  --t-bubble-in:var(--ds-bubble-in); --t-bubble-out:var(--ds-bubble-out); --t-bubble-op:var(--ds-bubble-op);
  /* --zf-* residuais (conexoes.html/builder) — inertes até essas telas ganharem data-ds="v2". */
  --zf-green:var(--ds-accent); --zf-green-bg:var(--ds-accent-soft);
  --zf-danger:var(--ds-danger); --zf-danger-bg:var(--ds-danger-soft);
  --zf-warning:var(--ds-warning); --zf-warning-bg:var(--ds-warning-soft);
  --zf-blue-strong:var(--ds-info);
}
:root[data-ds="v2"][data-theme="light"] {
  --t-bg:var(--ds-bg); --t-bg-2:var(--ds-bg-subtle); --t-sidebar:var(--ds-sidebar);
  --t-surface:var(--ds-surface); --t-surface-2:var(--ds-surface-elevated); --t-surface-3:var(--ds-surface-2);
  --t-surface-hover:var(--ds-surface-hover); --t-input:var(--ds-input);
  --t-border:var(--ds-border); --t-border-2:var(--ds-border-strong); --t-border-soft:var(--ds-border-subtle);
  --t-text:var(--ds-text); --t-text-strong:var(--ds-text); --t-text-1b:var(--ds-text-secondary);
  --t-text-2:var(--ds-text-secondary); --t-text-3:var(--ds-text-muted);
  --t-accent:var(--ds-accent); --t-accent-2:var(--ds-accent-text); --t-accent-hover:var(--ds-accent-hover);
  --t-accent-soft:var(--ds-accent-soft); --t-on-accent:var(--ds-text-on-accent);
  --t-hover:var(--ds-hover); --t-shadow:var(--ds-shadow-sm); --t-shadow-pop:var(--ds-shadow-pop);
  --t-chat-bg:var(--ds-chat-bg);
  --t-bubble-in:var(--ds-bubble-in); --t-bubble-out:var(--ds-bubble-out); --t-bubble-op:var(--ds-bubble-op);
  --zf-green:var(--ds-accent); --zf-green-bg:var(--ds-accent-soft);
  --zf-danger:var(--ds-danger); --zf-danger-bg:var(--ds-danger-soft);
  --zf-warning:var(--ds-warning); --zf-warning-bg:var(--ds-warning-soft);
  --zf-blue-strong:var(--ds-info);
}

/* ============================================================================
   COMPONENTES OFICIAIS `.ds-*` (DS V2). Só afetam quem usa a classe — não têm
   efeito em telas que não os usam. Ver docs/design-system/componentes.md.
   Contrato: estas regras leem SOMENTE var(--ds-*) — nunca var(--t-*)/
   var(--zf-*) (a ponte de opt-in acima é para o CSS próprio da tela, não
   para estes componentes). Por convenção, usar `.ds-*` numa tela exige
   `<html data-ds="v2">` — mas note que os tokens --ds-* em si (bloco acima)
   NÃO são condicionados a esse atributo (são sempre `:root`), então essa
   exigência é uma regra de processo (ui-sources.md/como-criar-tela.md), não
   algo tecnicamente barrado pelo CSS.
   ============================================================================ */

/* ---- Botão ---- */
/* <a class="ds-btn"> (navegação com aparência de botão) não herda o sublinhado de link. */
a.ds-btn, a.ds-btn:hover { text-decoration:none; }
.ds-btn { height:var(--ds-h-md); padding:0 15px; border-radius:var(--ds-radius-md); border:1px solid transparent;
  font:inherit; font-size:var(--ds-fs-md); font-weight:var(--ds-fw-semibold); cursor:pointer; display:inline-flex;
  align-items:center; justify-content:center; gap:7px; white-space:nowrap;
  transition:background var(--ds-dur-fast) var(--ds-ease), border-color var(--ds-dur-fast) var(--ds-ease),
    color var(--ds-dur-fast) var(--ds-ease), opacity var(--ds-dur-fast) var(--ds-ease); }
.ds-btn svg { width:16px; height:16px; flex:none; }
.ds-btn-primary { background:var(--ds-accent); border-color:var(--ds-accent); color:var(--ds-text-on-accent); }
.ds-btn-primary:hover:not(:disabled) { background:var(--ds-accent-hover); border-color:var(--ds-accent-hover); }
.ds-btn-primary:active:not(:disabled) { background:var(--ds-accent-active); border-color:var(--ds-accent-active); }
.ds-btn-secondary { background:transparent; border-color:var(--ds-border-strong); color:var(--ds-text); }
.ds-btn-secondary:hover:not(:disabled) { background:var(--ds-surface-hover); }
.ds-btn-ghost { background:var(--ds-surface-elevated); border-color:var(--ds-border); color:var(--ds-text-secondary); }
.ds-btn-ghost:hover:not(:disabled) { background:var(--ds-surface-hover); color:var(--ds-text); }
.ds-btn-danger { background:transparent; border-color:var(--ds-danger-text); color:var(--ds-danger-text); }
.ds-btn-danger:hover:not(:disabled) { background:var(--ds-danger-soft); }
/* Ação positiva "verde" (ex.: "Nova Campanha" em Disparos) — a referência usa
   esse verde como ação positiva PONTUAL, distinto da cor de marca/accent
   (que agora é azul, ver docs/design-system/zapdata-derivado.md §5). Nunca
   usar pra CTA genérico — só quando o padrão de origem já é verde. */
/* 40px (--ds-h-lg), não 36px (--ds-h-md) do .ds-btn base: a referência mede
   a "Nova Campanha" (verde, disparos) em h=40/br=6px, mais alta que os
   botões outline/ghost genéricos da mesma toolbar (36px) — ver comentário
   de --ds-h-lg acima. */
.ds-btn-success { height:var(--ds-h-lg); background:var(--ds-success); border-color:var(--ds-success); color:#FFFFFF; }
.ds-btn-success:hover:not(:disabled) { filter:brightness(.92); }
.ds-btn-success:active:not(:disabled) { filter:brightness(.85); }
.ds-btn-icon { width:var(--ds-h-md); padding:0; }
.ds-btn:disabled { opacity:.5; cursor:not-allowed; }
.ds-btn .ds-spin { margin-right:2px; }

/* ---- Campo (input/select/textarea) + label/hint/erro ---- */
.ds-field { margin-bottom:var(--ds-space-4); }
.ds-field > label, .ds-label { display:block; font-size:var(--ds-fs-xs); font-weight:var(--ds-fw-bold); color:var(--ds-text-secondary);
  margin-bottom:6px; text-transform:uppercase; letter-spacing:.02em; }
.ds-label.ds-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; }
.ds-input, .ds-select, .ds-textarea, .ds-field input, .ds-field select, .ds-field textarea {
  width:100%; min-height:var(--ds-h-md); padding:0 12px; border-radius:var(--ds-radius-md);
  border:1px solid var(--ds-border-strong); background:var(--ds-input); color:var(--ds-text); font:inherit; font-size:var(--ds-fs-md);
  transition:border-color var(--ds-dur-fast) var(--ds-ease), box-shadow var(--ds-dur-fast) var(--ds-ease); }
.ds-textarea, .ds-field textarea { height:auto; min-height:76px; padding:10px 12px; resize:vertical; }
.ds-input:hover, .ds-select:hover, .ds-textarea:hover { border-color:var(--ds-text-muted); }
/* Anel de foco 2px a 40% de opacidade (--ds-ring, item 7 da retificação) —
   antes 3px com --ds-accent-soft (14%, pensado pra fundo de badge, não pra
   anel de foco). */
.ds-input:focus, .ds-select:focus, .ds-textarea:focus,
.ds-field input:focus, .ds-field select:focus, .ds-field textarea:focus {
  outline:none; border-color:var(--ds-focus); box-shadow:0 0 0 2px var(--ds-ring); }
.ds-input:disabled, .ds-select:disabled, .ds-textarea:disabled { opacity:.6; cursor:not-allowed; background:var(--ds-disabled-bg); color:var(--ds-disabled-text); }
.ds-hint { font-size:var(--ds-fs-sm); color:var(--ds-text-muted); margin-top:4px; line-height:var(--ds-lh-normal); }
.ds-error-msg { display:flex; align-items:center; gap:6px; margin-top:6px; font-size:var(--ds-fs-sm); font-weight:var(--ds-fw-semibold); color:var(--ds-danger-text); }
.ds-error-msg svg { width:13px; height:13px; flex:none; }
.ds-field.has-error .ds-input, .ds-field.has-error input { border-color:var(--ds-danger-text); }

/* ---- Adorno dentro do campo (ex.: botão mostrar/ocultar senha) ----
   Envolve um .ds-input com um botão posicionado dentro do próprio campo,
   sem quebrar o contrato de foco/label do campo (o botão é focável à parte,
   depois do input na ordem do DOM/tab). Primeiro consumidor: senha de
   login/cadastro (docs/design-system/migracao.md, fatia login/cadastro). */
.ds-input-wrap { position:relative; }
.ds-input-wrap .ds-input { padding-right:44px; }
.ds-input-adorn { position:absolute; top:0; right:0; height:100%; width:44px; display:flex; align-items:center; justify-content:center;
  background:transparent; border:none; padding:0; margin:0; cursor:pointer; color:var(--ds-text-muted); border-radius:0 var(--ds-radius-md) var(--ds-radius-md) 0; }
.ds-input-adorn:hover { color:var(--ds-text); }
.ds-input-adorn svg { width:18px; height:18px; flex:none; }

/* ---- Alerta inline (feedback geral de formulário: erro/sucesso/aviso/info) ----
   Diferente de .ds-error-msg (erro de UM campo, dentro de .ds-field), o
   .ds-alert é o feedback GERAL da ação (ex.: "usuário ou senha inválidos"
   no login) — texto + ícone, papel role="alert"/aria-live no HTML de quem
   instancia (o CSS não injeta ARIA). Primeiro consumidor: login/cadastro. */
.ds-alert { display:flex; align-items:flex-start; gap:8px; padding:11px 13px; border-radius:var(--ds-radius-md);
  font-size:var(--ds-fs-sm); font-weight:var(--ds-fw-medium); line-height:var(--ds-lh-normal); }
.ds-alert svg { width:16px; height:16px; flex:none; margin-top:1px; }
/* Mensagens vêm do servidor/usuário (e-mail, nome): texto sem espaço quebra em vez de vazar no mobile. */
.ds-alert > :not(svg), .ds-error-msg > :not(svg) { min-width:0; overflow-wrap:anywhere; }
.ds-alert.err { background:var(--ds-danger-soft); color:var(--ds-danger-text); }
.ds-alert.ok { background:var(--ds-success-soft); color:var(--ds-success-text); }
.ds-alert.warn { background:var(--ds-warning-soft); color:var(--ds-warning-text); }
.ds-alert.info { background:var(--ds-info-soft); color:var(--ds-info-text); }

/* ---- Checkbox / Radio / Switch ---- */
.ds-check, .ds-radio { display:inline-flex; align-items:center; gap:8px; cursor:pointer; font-size:var(--ds-fs-md); color:var(--ds-text); }
.ds-check input, .ds-radio input { width:18px; height:18px; accent-color:var(--ds-accent); cursor:pointer; }
.ds-switch { position:relative; display:inline-flex; align-items:center; width:40px; height:var(--ds-touch); cursor:pointer; }
.ds-switch input { position:absolute; inset:0; opacity:0; margin:0; cursor:pointer; width:100%; height:100%; }
.ds-switch .track { width:40px; height:22px; border-radius:999px; background:var(--ds-border-strong); transition:background var(--ds-dur-fast) var(--ds-ease); flex:none; }
.ds-switch .thumb { position:absolute; left:2px; top:50%; transform:translateY(-50%); width:18px; height:18px; border-radius:50%;
  background:#fff; transition:transform var(--ds-dur-fast) var(--ds-ease); box-shadow:0 1px 2px rgba(0,0,0,.3); }
.ds-switch input:checked ~ .track { background:var(--ds-accent); }
.ds-switch input:checked ~ .thumb { transform:translate(18px,-50%); }
.ds-switch input:disabled ~ .track { opacity:.5; cursor:not-allowed; }

/* ---- Card ---- */
.ds-card { background:var(--ds-card); border:1px solid var(--ds-border); border-radius:var(--ds-radius-xl); padding:20px; }
/* Cards empilhados no fluxo da página respiram 16px entre si (em grid, quem espaça é o gap). */
.ds-card + .ds-card { margin-top:var(--ds-space-4); }
/* overflow-wrap:anywhere: título/descrição às vezes vêm de dado do servidor
   (ex.: nome de usuário sem espaço em conta.html) — quebra em vez de
   estourar a largura do card. */
.ds-card-title { font-size:var(--ds-fs-lg); font-weight:var(--ds-fw-bold); margin:0 0 3px; color:var(--ds-text); overflow-wrap:anywhere; }
.ds-card-desc { font-size:var(--ds-fs-sm); color:var(--ds-text-muted); margin:0 0 14px; overflow-wrap:anywhere; }

/* ---- Stat / KPI (número grande + rótulo + rodapé opcional) ----
   Pendência de docs/design-system/migracao.md §4 ("Componente KPI/stat")
   resolvida nesta fatia (feature/ds-v2-fatia-admin) — primeiro consumidor:
   admin.html (5 indicadores no topo do painel). `auto-fit`/`minmax` em vez
   de um número fixo de colunas: em telas largas o grid recalcula quantas
   colunas cabem por linha sem nunca abrir rolagem horizontal. Pensado para
   reuso por inicio.html (`migracao.md` §1, item 13) quando essa fatia
   acontecer. */
.ds-stats { display:grid; grid-template-columns:repeat(auto-fit, minmax(150px, 1fr)); gap:14px; margin-bottom:var(--ds-space-4); } /* respiro igual ao .ds-card + .ds-card (o próximo card encostava) */
.ds-stat { background:var(--ds-card); border:1px solid var(--ds-border); border-radius:var(--ds-radius-xl); padding:16px 18px; min-width:0; }
.ds-stat-label { font-size:var(--ds-fs-sm); font-weight:var(--ds-fw-semibold); color:var(--ds-text-muted); }
/* tabular-nums: o valor muda de dígito sem "dançar" de largura entre
   atualizações; overflow-wrap: um valor muito grande (milhares) ainda
   quebra em vez de estourar o card num container estreito. */
.ds-stat-value { font-size:var(--ds-fs-2xl); font-weight:var(--ds-fw-extrabold); letter-spacing:-.02em; margin-top:6px;
  color:var(--ds-text); font-variant-numeric:tabular-nums; overflow-wrap:anywhere; }
.ds-stat-foot { font-size:var(--ds-fs-xs); color:var(--ds-text-muted); margin-top:2px; min-height:1.4em; }
/* Variante de destaque (ex.: "Aguardando aprovação"): o rótulo já diz o que
   é — a cor não é o único sinal, só reforça (princípios.md §1.6). */
.ds-stat.warn .ds-stat-value { color:var(--ds-warning-text); }
/* ≤640px: `auto-fit`/`minmax(150px,1fr)` sozinho, num .wrap estreito
   (≈362px de conteúdo em 390px de viewport), cabe 2 colunas de 150px+ —
   com um número ÍMPAR de KPIs (ex.: os 5 de admin.html) o último item fica
   sozinho na última linha, ocupando só a metade esquerda da largura (achado
   na verificação visual, migracao.md §13). Fixamos 2 colunas neste
   breakpoint e, só quando o total de itens é ímpar (`:last-child` que
   também é `:nth-child(odd)` — a paridade da posição do último item é a
   paridade da contagem total), o último estica pras duas colunas em vez de
   deixar buraco. Com total par não muda nada (a regra não casa). Genérico:
   não depende de 5 serem sempre os itens, funciona pra qualquer contagem
   ímpar/par em qualquer tela que reuse `.ds-stats`. */
@media (max-width:640px) {
  .ds-stats { grid-template-columns:repeat(2, minmax(0, 1fr)); }
  .ds-stat:last-child:nth-child(odd) { grid-column:1 / -1; }
  /* Valor um degrau menor em 2 colunas: 23px partia números de 9 dígitos no meio
     ("123.456.78" / "9") — verificação visual da fatia início. */
  .ds-stat-value { font-size:var(--ds-fs-xl); }
}

/* ---- Badge ---- */
.ds-badge { display:inline-flex; align-items:center; gap:6px; font-size:var(--ds-fs-sm); font-weight:var(--ds-fw-bold);
  padding:4px 10px; border-radius:var(--ds-radius-full); white-space:nowrap; }
.ds-badge .dot { width:7px; height:7px; border-radius:50%; background:currentColor; flex:none; }
.ds-badge.ok, .ds-badge.ds-success { background:var(--ds-success-soft); color:var(--ds-success-text); }
.ds-badge.warn, .ds-badge.ds-warning { background:var(--ds-warning-soft); color:var(--ds-warning-text); }
.ds-badge.err, .ds-badge.ds-danger { background:var(--ds-danger-soft); color:var(--ds-danger-text); }
.ds-badge.info, .ds-badge.ds-info { background:var(--ds-info-soft); color:var(--ds-info-text); }
.ds-badge.ds-neutral, .ds-badge.soon { background:var(--ds-surface-elevated); color:var(--ds-text-muted); border:1px solid var(--ds-border); }
/* Variantes de STATUS (retificação do proprietário, 2026-09-28, item 4):
   mapeamento direto pro vocabulário do produto — "Conectado" = `.ok`/
   `.ds-success` (verde), "Desconectado" = `.err`/`.ds-danger` (vermelho),
   "Pendente" = `.warn`/`.ds-warning` (âmbar). Já eram fundo tint + texto na
   cor (padrão pedido); nenhum valor mudou aqui, só documentado. */

/* Variante de ETIQUETA/tag (uppercase 10-11px/600, pílula, padding 2px 8px)
   — mesmo padrão medido nos chips "DANIEL TAROT" (contatos) e "FUNIL
   ADESIVOS" (kanban) da referência: `font-size:10px`, pílula (`border-
   radius:9999px`, herdado de `.ds-badge`), `padding:2px 10px`≈`8px`. Já
   existia SÓ escopado a `.ds-kanban-card-tags .ds-badge` (linha ~965) —
   generalizado aqui como `.ds-badge-tag`, standalone, pra qualquer tela
   (Contatos, CRM...) marcar etiqueta sem duplicar a regra. */
.ds-kanban-card-tags .ds-badge, .ds-badge-tag { font-size:10px; font-weight:var(--ds-fw-semibold); text-transform:uppercase; letter-spacing:.02em; padding:2px 8px; }
/* Ícone SVG dentro de badge (ex.: check/x de flags booleanas) — sem esta
   regra o <svg> sem width/height no markup herda o tamanho padrão do
   navegador para elemento substituído (300x150), estourando a pílula. Achado
   na fatia `feature/ds-v2-fatia-integracoes` (migracao.md §10) — corrige
   também `app-review-checklist.html` (STATUS PASS/FAIL/NÃO TESTADO), que já
   colocava <svg> dentro de `.ds-badge` sem essa regra central. */
.ds-badge svg { width:14px; height:14px; flex:none; }

/* ---- Chip/pill de filtro (grupo de opções mutuamente exclusivas: status,
   canal...) — lacuna registrada em docs/referencia-zapdata-global/
   01-design-system.md §5 ("componente próprio, pill 32px, ativo = accent
   sólido"; nenhum `.ds-chip` existia em componentes.md). Primeiro consumidor:
   backend/public/conexoes.html (toolbar de filtros de Conexões). Marcação:
   `<div class="ds-chip-group" role="group"><button class="ds-chip"
   aria-pressed="true|false">…</button>…</div>`. */
.ds-chip-group { display:flex; flex-wrap:wrap; gap:8px; }
.ds-chip { display:inline-flex; align-items:center; gap:6px; height:var(--ds-h-sm); padding:0 14px;
  border-radius:var(--ds-radius-full); border:1px solid var(--ds-border); background:var(--ds-surface-elevated);
  color:var(--ds-text-secondary); font-size:var(--ds-fs-xs); font-weight:var(--ds-fw-semibold);
  cursor:pointer; white-space:nowrap; font-family:inherit; }
.ds-chip svg { width:14px; height:14px; flex:none; }
.ds-chip:hover:not(:disabled) { background:var(--ds-surface-hover); color:var(--ds-text); }
.ds-chip[aria-pressed="true"] { background:var(--ds-accent); border-color:var(--ds-accent); color:var(--ds-text-on-accent); }
.ds-chip:disabled { opacity:.5; cursor:not-allowed; }
.ds-chip:focus-visible { outline:2px solid var(--ds-focus); outline-offset:2px; }

/* ---- Item de lista (padrão dos "cards de atalho" e listas de registros) ---- */
.ds-list-item { display:flex; align-items:center; gap:13px; padding:13px 4px; border-top:1px solid var(--ds-border-subtle); }
.ds-list-item:first-child { border-top:none; }
.ds-list-item .ds-ic { width:38px; height:38px; border-radius:var(--ds-radius-md); background:var(--ds-surface-elevated); border:1px solid var(--ds-border);
  color:var(--ds-text-secondary); display:flex; align-items:center; justify-content:center; flex:none; }
.ds-list-item .ds-ic svg { width:19px; height:19px; }
.ds-list-item .ds-tx { flex:1; min-width:0; overflow-wrap:anywhere; } /* nomes/variáveis longos sem espaço quebram em vez de vazar no mobile */
.ds-list-item .ds-tx b { font-size:var(--ds-fs-base); display:block; font-weight:var(--ds-fw-semibold); color:var(--ds-text); }
.ds-list-item .ds-tx small { font-size:var(--ds-fs-sm); color:var(--ds-text-muted); }
.ds-list-item .ds-go { color:var(--ds-accent-text); font-weight:var(--ds-fw-semibold); font-size:var(--ds-fs-md); text-decoration:none; white-space:nowrap;
  display:inline-flex; align-items:center; gap:4px; min-height:var(--ds-touch); }
.ds-list-item .ds-go:hover { text-decoration:underline; }
.ds-list-item .ds-go.ds-danger { color:var(--ds-danger-text); }
/* Mobile: as ações descem para uma linha própria, alinhadas ao texto, em vez de
   espremer o título/descrição numa coluna estreita. */
@media (max-width:640px) {
  .ds-list-item { flex-wrap:wrap; row-gap:0; }
  .ds-list-item .ds-tx { flex:1 1 calc(100% - 51px); }
  .ds-list-item .ds-tx + .ds-go { margin-left:51px; }
}

/* ---- Layout de autenticação (login/cadastro — telas sem sidebar/rail) ----
   Card centrado (usa .ds-card por baixo — não duplica chrome de card), com
   uma marca no topo. O emblema da marca usa `--ds-brand-chip-bg` — um tom
   FIXO escuro nos dois temas (dedicado a este chip; diferente de
   `--ds-sidebar`, que a partir da reconstrução do AppShell global segue o
   tema — ver zapdata-derivado.md §6): a logo oficial (/brand/zapflow-logo.png)
   tem o texto "Zap" em tom bem claro, ilegível sobre fundo branco no tema
   claro; o emblema escuro garante legibilidade nos dois temas sem redesenhar
   a logo. Primeiro consumidor: login.html/cadastro.html. */
.ds-auth { min-height:100vh; display:flex; align-items:center; justify-content:center; padding:var(--ds-space-6) var(--ds-space-4);
  background:var(--ds-bg-subtle); }
.ds-auth-box { width:100%; max-width:440px; }
.ds-auth-brand { display:flex; justify-content:center; margin-bottom:var(--ds-space-6); }
.ds-auth-brand-chip { display:inline-flex; align-items:center; background:var(--ds-brand-chip-bg); border-radius:var(--ds-radius-lg);
  padding:6px 18px; box-shadow:var(--ds-shadow-sm); }
.ds-auth-brand-chip img { display:block; height:48px; width:auto; } /* mesma escala do rail (50px): o PNG tem margem interna; menor que isso o wordmark fica ilegível */
.ds-auth-head { text-align:center; margin-bottom:var(--ds-space-6); }
.ds-auth-head h1 { margin:0 0 6px; font-size:var(--ds-fs-xl); font-weight:var(--ds-fw-extrabold); color:var(--ds-text); letter-spacing:-.02em; }
.ds-auth-head p { margin:0; color:var(--ds-text-muted); font-size:var(--ds-fs-md); line-height:var(--ds-lh-normal); }
.ds-auth-links { display:flex; justify-content:flex-end; margin:-6px 0 var(--ds-space-4); }
.ds-auth-links a { font-size:var(--ds-fs-sm); font-weight:var(--ds-fw-semibold); color:var(--ds-accent-text); text-decoration:none; }
.ds-auth-links a:hover { text-decoration:underline; }
.ds-auth-foot { text-align:center; margin-top:var(--ds-space-6); font-size:var(--ds-fs-md); color:var(--ds-text-muted); }
/* Links de auth: rodapé e links no meio do texto do card (ex.: "Cadastro recebido"). */
.ds-auth-foot a, .ds-auth .ds-card p a { color:var(--ds-accent-text); font-weight:var(--ds-fw-semibold); text-decoration:none; }
.ds-auth-foot a:hover, .ds-auth .ds-card p a:hover { text-decoration:underline; }
/* Formulário de autenticação: controles maiores (48px) — tela curta, poucos
   campos, prioriza alvo de toque confortável sobre densidade (diferente de
   uma tela de ferramenta de trabalho, principios.md §1.5 não se aplica aqui
   do mesmo jeito). */
.ds-auth .ds-input, .ds-auth .ds-select { min-height:var(--ds-h-lg); }
.ds-auth .ds-btn-primary { width:100%; height:var(--ds-h-lg); font-size:var(--ds-fs-lg); }

/* ---- Resultado de página cheia (auth de 1 etapa: sucesso/erro/carregando)
   Diferente de .ds-empty/.ds-error-state (ícone 38px, pensado pra caber
   DENTRO de uma lista/card denso) — aqui o ícone é maior (64px) porque É o
   conteúdo principal do card inteiro (ex.: "Confira seu email",
   "Email confirmado", "Confirmando seu email…"). Promovido a componente
   central nesta fatia (docs/design-system/migracao.md, fatia
   `feature/ds-v2-fatia-auth-restante`) por já nascer com 3 consumidores
   (esqueci-senha.html, redefinir-senha.html, verificar-email.html) — o
   4º uso (cadastro.html, CSS local `.ok .ic`) fica como dívida registrada
   (migrar cadastro.html pra este componente é trabalho de outra fatia, não
   tocado aqui). */
.ds-result { text-align:center; }
.ds-result .ic { width:64px; height:64px; border-radius:50%; display:flex; align-items:center; justify-content:center; margin:0 auto var(--ds-space-4); }
.ds-result .ic svg { width:32px; height:32px; }
.ds-result .ic .ds-spin { width:28px; height:28px; border-width:3px; }
.ds-result.ok .ic { background:var(--ds-success-soft); color:var(--ds-success-text); }
.ds-result.err .ic { background:var(--ds-danger-soft); color:var(--ds-danger-text); }
.ds-result.load .ic { background:var(--ds-surface-elevated); color:var(--ds-text-muted); }
.ds-result h1, .ds-result h2 { font-size:var(--ds-fs-xl); margin:0 0 8px; color:var(--ds-text); }
.ds-result p { color:var(--ds-text-secondary); font-size:var(--ds-fs-md); line-height:var(--ds-lh-relaxed); margin:0 0 18px; }

/* O atributo `hidden` SEMPRE vence nas telas migradas. Vários componentes
   (.ds-error-msg, .ds-alert, SVGs do .ds-input-adorn, .ds-spin) declaram
   `display` próprio, que sobrescreve o display:none do navegador — sem esta
   regra o JS escondia via `el.hidden = true` e o elemento continuava na tela
   (achado na verificação visual da Etapa 3: erros vazios e os dois ícones do
   olho visíveis ao mesmo tempo). */
[data-ds="v2"] [hidden] { display:none !important; }

/* ---- Page header ---- */
.ds-page-h { display:flex; align-items:center; gap:11px; margin-bottom:4px; }
.ds-page-h svg { width:22px; height:22px; color:var(--ds-text-secondary); }
.ds-page-h h1 { margin:0; font-size:var(--ds-fs-2xl); font-weight:var(--ds-fw-extrabold); letter-spacing:-.02em; color:var(--ds-text); }
/* Voltar do page header: só a seta, sem borda nem fundo (referência). */
.ds-btn-bare { background:transparent; border-color:transparent; color:var(--ds-text-secondary); }
.ds-btn-bare:hover:not(:disabled) { background:var(--ds-surface-hover); color:var(--ds-text); }
/* Chip de ícone do page header: 40px, tint da cor de identidade (referência). */
.ds-page-h .ds-page-ico { flex:none; width:40px; height:40px; border-radius:var(--ds-radius-md); background:var(--ds-accent-soft);
  color:var(--ds-accent-text); display:inline-flex; align-items:center; justify-content:center; }
.ds-page-h .ds-page-ico svg { width:20px; height:20px; color:inherit; }
.ds-page-sub { color:var(--ds-text-muted); font-size:var(--ds-fs-md); margin:0 0 22px; }
/* Mobile: título do page header um degrau menor (quebra de palavra como
   segurança) — com o espaço do hambúrguer, 23px extrabold empurrava o h1
   para uma linha separada do ícone (verificação visual, fatia institucionais). */
@media (max-width:640px) {
  .ds-page-h h1 { font-size:var(--ds-fs-xl); min-width:0; overflow-wrap:anywhere; }
}
/* O body das telas migradas ocupa a viewport inteira: ds.css pinta html e
   body com tokens diferentes, e numa tela curta aparecia uma "emenda" de cor
   abaixo do conteúdo. */
[data-ds="v2"] body { min-height:100vh; }
/* Com a sidebar em modo gaveta (≤820px, ou forçada), o hambúrguer do rail é fixo
   no canto (12px, 42×42): o título abre espaço para não ficar por baixo dele.
   body.zf-has-burger é marcado pelo rail.js. */
[data-ds="v2"] body.zf-has-burger .ds-page-h { padding-left:48px; min-height:42px; }

/* Page actions: wrapper de botões DENTRO do .ds-page-h, empurrado pra
   direita — consolidado a partir de ".hacts" (CSS local idêntico repetido em
   integracoes.html/contatos.html/disparos.html/crm.html, ver
   docs/design-system/migracao.md, seção da consolidação /ds.js). 10px é
   literal (nenhum --ds-space-* bate exato: --space-2 é 8px, --space-3 é
   12px) — mantido como estava nas 4 telas de origem. */
.ds-page-acts { margin-left:auto; display:flex; gap:10px; flex-wrap:wrap; }
@media (max-width:640px) {
  .ds-page-acts { flex:1 1 100%; margin-left:0; }
  /* Sem hambúrguer ao lado na 2ª linha do header: devolve às ações o recuo
     de 48px que .ds-page-h reserva pra ele (regra acima) e desce abaixo do
     botão fixo do rail (12px + 42px) — mesma lição das 4 telas de origem. */
  [data-ds="v2"] body.zf-has-burger .ds-page-h .ds-page-acts { margin-left:-48px; margin-top:10px; }
}

/* ---- Estados: empty / erro / loading / skeleton ---- */
.ds-empty, .ds-error-state { display:flex; flex-direction:column; align-items:center; text-align:center; gap:6px; padding:22px 10px; }
.ds-empty .ic, .ds-error-state .ic { width:38px; height:38px; border-radius:var(--ds-radius-md); display:flex; align-items:center; justify-content:center; margin-bottom:4px; }
.ds-empty .ic { background:var(--ds-accent-soft); color:var(--ds-accent); }
.ds-error-state .ic { background:var(--ds-danger-soft); color:var(--ds-danger-text); }
.ds-empty p, .ds-error-state p { margin:0; font-size:var(--ds-fs-sm); color:var(--ds-text-secondary); line-height:var(--ds-lh-relaxed); max-width:320px; }
/* Estado vazio "em card" (referência: card com borda, ícone cinza 44px sem fundo, título 16/500, texto 14 muted,
   respiro vertical de ~48px). Uso: `<div class="ds-card ds-empty ds-empty--card">`. */
.ds-empty--card { padding:48px 16px; gap:8px; }
.ds-empty--card .ic { width:44px; height:44px; background:transparent; color:var(--ds-text-muted); margin-bottom:8px; }
.ds-empty--card .ic svg { width:44px; height:44px; stroke-width:1.5; }
.ds-empty--card .ds-empty-titulo { margin:0; font-size:16px; font-weight:var(--ds-fw-medium); color:var(--ds-text); }
.ds-empty--card p { font-size:14px; color:var(--ds-text-muted); }
.ds-empty--card .ds-btn { margin-top:8px; }
.ds-skeleton { border-radius:var(--ds-radius-sm); background:linear-gradient(90deg, var(--ds-surface-elevated) 25%, var(--ds-surface-2) 37%, var(--ds-surface-elevated) 63%);
  background-size:400% 100%; animation:ds-shimmer 1.6s ease infinite; }
@keyframes ds-shimmer { 0%{background-position:100% 0} 100%{background-position:0 0} }
.ds-spin { width:15px; height:15px; border-radius:50%; border:2px solid currentColor; border-top-color:transparent; animation:ds-spin .7s linear infinite; flex:none; }
@keyframes ds-spin { to { transform:rotate(360deg); } }

/* ---- Toast ---- */
.ds-toasts { position:fixed; left:16px; bottom:16px; z-index:var(--ds-z-toast); display:flex; flex-direction:column; gap:9px; width:280px; max-width:calc(100vw - 32px); }
.ds-toast { display:flex; align-items:flex-start; gap:10px; padding:11px 13px; border-radius:var(--ds-radius-md);
  background:var(--ds-surface); border:1px solid var(--ds-border); box-shadow:var(--ds-shadow-pop); font-size:var(--ds-fs-sm); }
.ds-toast svg { width:17px; height:17px; flex:none; margin-top:1px; }
.ds-toast.ok svg { color:var(--ds-success-text); } .ds-toast.warn svg { color:var(--ds-warning-text); } .ds-toast.err svg { color:var(--ds-danger-text); } .ds-toast.info svg { color:var(--ds-info-text); }
.ds-toast b { display:block; color:var(--ds-text); font-weight:var(--ds-fw-bold); margin-bottom:1px; }
.ds-toast span { color:var(--ds-text-secondary); }

/* ---- Modal / bottom sheet ----
   Largura medida ao vivo nesta fatia (retificação do proprietário,
   2026-09-28, item 3) — dumps com `role="dialog"` e `getBoundingClientRect`
   real (scratchpad/ref-inbox/*.json, não `cls` sozinha, que corta em ~220
   caracteres e às vezes esconde a classe de largura): 448px sem variante
   (kanban-12-card-dialog-0, "detalhe do card"), 512px na variante
   `max-w-lg` (chat-12-menu-*, kanban-07-nova-etiqueta — "Gerenciar canais",
   "Nova etiqueta"...), 576px no maior modal medido (contatos-11-importar-0,
   "Importar contatos"). Resolve a pendência "NÃO VERIFICADO" registrada em
   zapdata-derivado.md (fatia anterior só tinha os controles internos, não a
   caixa do diálogo) — `.ds-modal` sobe de 440 pra 448 (praticamente igual,
   só ajusta pro valor exato) e ganham variante `.ds-modal-md`/`.ds-modal-lg`
   pra quem precisar de mais campos (aplicar a classe extra no `.ds-modal`
   de quem instancia; `.ds-modal-lg` já herda `overflow:auto` do
   `.ds-modal-body` pra rolagem interna em formulários longos). Cabeçalho/
   corpo/rodapé: padding horizontal 24px (medido, `p-6` = 24px nos 4 lados
   no dialog de referência — aqui dividido em 3 regiões com padding vertical
   próprio, porque `.ds-modal-body` precisa do próprio scroll container,
   diferente da referência, que não pagina cabeçalho/rodapé fixos). Título
   18px/600 (medido: `text-lg font-semibold` = 18px/600, não os 15px/700 que
   `--ds-fs-lg`/`--ds-fw-bold` davam antes). Subtítulo 14px muted (medido:
   `text-sm text-muted-foreground`). */
.ds-modal-ov { position:fixed; inset:0; background:var(--ds-overlay); z-index:var(--ds-z-modal); display:none; align-items:center; justify-content:center; padding:20px; }
.ds-modal-ov.on { display:flex; }
.ds-modal { width:100%; max-width:448px; max-height:88vh; overflow:hidden; display:flex; flex-direction:column; background:var(--ds-surface); border:1px solid var(--ds-border);
  border-radius:var(--ds-radius-xl); box-shadow:var(--ds-shadow-modal); }
/* Variantes de largura — aplicar junto de `.ds-modal` (`class="ds-modal ds-modal-md"`). */
.ds-modal-md { max-width:512px; }
.ds-modal-lg { max-width:576px; }
.ds-modal-head { display:flex; align-items:flex-start; justify-content:space-between; gap:12px; flex:none; padding:20px 24px 16px; border-bottom:1px solid var(--ds-border-subtle); }
.ds-modal-head h3 { margin:0; font-size:18px; font-weight:var(--ds-fw-semibold); color:var(--ds-text); }
.ds-modal-head p { margin:3px 0 0; font-size:var(--ds-fs-base); color:var(--ds-text-muted); }
.ds-modal-close { width:30px; height:30px; border-radius:8px; border:none; background:transparent; color:var(--ds-text-muted); cursor:pointer;
  display:flex; align-items:center; justify-content:center; flex:none; }
.ds-modal-close svg { width:16px; height:16px; flex:none; }
.ds-modal-close:hover { background:var(--ds-surface-hover); color:var(--ds-text); }
/* Só o corpo rola: cabeçalho (fechar) e rodapé (ações) ficam sempre visíveis,
   inclusive no bottom sheet mobile com formulário longo. */
.ds-modal-body { padding:18px 24px; flex:1 1 auto; min-height:0; overflow:auto; }
.ds-modal-foot { display:flex; gap:10px; justify-content:flex-end; flex:none; padding:16px 24px; border-top:1px solid var(--ds-border-subtle); }
.ds-sheet-handle { display:none; }
@media (max-width:640px) {
  .ds-modal-ov { align-items:flex-end; padding:0; }
  .ds-modal { max-width:100%; width:100%; max-height:86vh; border-radius:var(--ds-radius-xl) var(--ds-radius-xl) 0 0;
    animation:ds-sheet-up var(--ds-dur-normal) var(--ds-ease); }
  .ds-sheet-handle { display:block; width:40px; height:4px; border-radius:999px; background:var(--ds-border-strong); margin:10px auto 0; }
}
@keyframes ds-sheet-up { from{ transform:translateY(24px); opacity:.6; } to{ transform:translateY(0); opacity:1; } }
/* Trava de scroll do body enquanto um .ds-modal-ov está aberto — genérica
   para qualquer consumidor de modal; quem abre alterna esta classe no
   <html> (JS de cada tela, nenhum JS central de modal existe ainda — ver
   componentes.md "Modal / bottom sheet"). Primeiro consumidor: cobranca.html
   (migracao.md §12.2). */
.ds-scroll-lock, .ds-scroll-lock body { overflow:hidden; }

/* ---- Tabela responsiva (vira lista de cards em telas estreitas) ---- */
.ds-table { width:100%; border-collapse:collapse; font-size:var(--ds-fs-md); }
/* Cabeçalho com leve tinta de fundo — paridade com a referência (docs/contatos/
   referencia-zapdata/01-mapa-da-tela.md: thead "bg rgba(46,46,46,.2)", altura
   ~40-43px). --ds-surface-elevated é o token mais próximo dessa tinta nos
   dois temas. */
/* Cabeçalho 12px/600 uppercase muted (retificação do proprietário,
   2026-09-28, item 5) — antes 11px/700 (`--ds-fs-xs`/`--ds-fw-bold`). */
.ds-table thead th { text-align:left; font-size:12px; text-transform:uppercase; letter-spacing:.04em;
  color:var(--ds-text-muted); font-weight:var(--ds-fw-semibold); padding:10px 10px; background:var(--ds-surface-elevated); border-bottom:1px solid var(--ds-border); }
/* Célula pode trazer dado do usuário/servidor sem espaço (nome, email, texto
   longo) — quebra em vez de forçar rolagem horizontal da tabela/página.
   Padding/line-height calibrados pra ~56px de altura de linha (medido em
   `06-contatos.json` `tables`, `td` da linha de dado: h=57px — item 5 da
   retificação). */
.ds-table tbody td { padding:18px 12px; line-height:1.5; border-bottom:1px solid var(--ds-border-subtle); color:var(--ds-text); overflow-wrap:anywhere; }
.ds-table tbody tr:last-child td { border-bottom:none; }
.ds-table tbody tr:hover { background:var(--ds-surface-hover); }
/* Célula/linha de ações (Aprovar/Excluir/etc.): várias .ds-btn lado a lado,
   quebrando linha em vez de estourar a largura da coluna/cartão. Primeiro
   consumidor: conta.html (lista de usuários, admin). */
.ds-table-acts { display:flex; gap:8px; flex-wrap:wrap; }
.ds-cards { display:none; flex-direction:column; gap:10px; }
/* Item de DADO dentro de .ds-cards (diferente de .ds-list-item, que é o
   atalho ícone+texto+ir de configuracoes.html): quando o mobile precisa
   mostrar VÁRIOS campos por registro de uma tabela (não só um atalho),
   .ds-cards-item dá um cartão mais leve (borda simples, sem sombra extra)
   pra agrupar título/subtítulo + badges + ações. .ds-cards-acts é a mesma
   ideia de .ds-table-acts, para dentro do cartão. Primeiro consumidor:
   conta.html (lista de usuários, admin). */
.ds-cards-item { border:1px solid var(--ds-border-subtle); border-radius:var(--ds-radius-lg); padding:14px; background:var(--ds-surface-elevated); overflow-wrap:anywhere; }
.ds-cards-item b { display:block; font-size:var(--ds-fs-base); font-weight:var(--ds-fw-semibold); color:var(--ds-text); }
.ds-cards-item small { display:block; font-size:var(--ds-fs-sm); color:var(--ds-text-muted); }
.ds-cards-acts { display:flex; gap:8px; flex-wrap:wrap; margin-top:10px; }
@media (max-width:640px) {
  .ds-table { display:none; }
  .ds-cards { display:flex; }
  /* Toque confortável: cada botão de ação ocupa a largura útil da linha em
     que caiu (em vez de ficar do tamanho do texto) — achado da inspeção
     visual do orquestrador (migracao.md §9.2). */
  .ds-cards-acts .ds-btn { flex:1 1 auto; }
}

/* ---- Sr-only genérico (texto só para leitor de tela) ----
   Diferente de `.ds-label.ds-sr-only` (que exige as DUAS classes, pensado só
   pra rótulo de campo), esta é a versão standalone — qualquer `<span>` de
   texto auxiliar (ex.: "não lida" ao lado de um indicador visual) pode usar
   sozinha. Mesma técnica de clipping (não `display:none`, que o leitor de
   tela ignoraria). Primeiro consumidor: crm.html (Kanban). */
.ds-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; }

/* ---- Kanban (coluna + card) ----
   Pendência de `componentes.md`/`migracao.md` §4 ("Componente Kanban")
   resolvida nesta fatia (`feature/ds-v2-fatia-crm`). `.ds-kanban` é o board:
   rolagem horizontal PRÓPRIA (nunca a página) — quem envolve só precisa não
   forçar uma largura maior que a viewport (não é flex/grid item aqui, então
   não precisa de `min-width:0` extra, mas o CSS da tela consumidora garante
   isso mesmo assim, ver `migracao.md` §15). Cada `.ds-kanban-col` tem largura
   fixa e rola verticalmente por dentro (`.ds-kanban-col-body`) — dá pra ver
   várias colunas ao mesmo tempo sem a página inteira rolar na horizontal.
   Primeiro consumidor real: `crm.html` (Pipeline de Leads). */
/* Medidas alinhadas à referência (docs/crm/referencia-zapdata/
   01-mapa-da-tela.md): board gap 12px, coluna 300px/radius 8px (--ds-radius-md),
   cabeçalho 8px 10px 6px com faixa de cor (--ds-kanban-col-c, setada por quem
   instancia via style, default transparente — nunca hex fixo aqui). */
.ds-kanban { display:flex; gap:12px; align-items:flex-start; overflow-x:auto; overscroll-behavior-x:contain; padding-bottom:6px; min-width:0; }
.ds-kanban-col { --ds-kanban-col-c:transparent; flex:none; width:300px; background:var(--ds-card); border:1px solid var(--ds-border); border-radius:var(--ds-radius-md); display:flex; flex-direction:column; max-height:calc(100vh - 316px); min-height:170px; overflow:hidden; }
.ds-kanban-col-head { display:flex; align-items:center; gap:9px; padding:8px 10px 6px; border-top:3px solid var(--ds-kanban-col-c); border-bottom:1px solid var(--ds-border-subtle); flex:none; }
.ds-kanban-dot { width:10px; height:10px; border-radius:50%; flex:none; }
/* Nome trunca visualmente (ellipsis) mas o `title` (no HTML de quem instancia)
   expõe o texto completo no hover, e o nome acessível da coluna
   (`role="region"`/`aria-labelledby`) usa este mesmo elemento — o texto
   completo sempre chega ao leitor de tela, truncado ou não. */
.ds-kanban-col-nome { flex:1; min-width:0; font-size:12px; font-weight:var(--ds-fw-semibold); color:var(--ds-text); white-space:nowrap; overflow:hidden; text-overflow:ellipsis; }
/* Contagem: texto mono tabular-nums, sem pílula de fundo (paridade com a
   referência — "191" simples, não badge). */
.ds-kanban-count { flex:none; font-family:var(--ds-font-mono); font-size:11px; font-weight:var(--ds-fw-semibold); color:var(--ds-text-muted); font-variant-numeric:tabular-nums; }
.ds-kanban-menu-btn { flex:none; width:30px; height:30px; border-radius:8px; border:1px solid transparent; background:transparent; color:var(--ds-text-muted); cursor:pointer; display:flex; align-items:center; justify-content:center; }
.ds-kanban-menu-btn:hover { background:var(--ds-hover); color:var(--ds-text); }
.ds-kanban-menu-btn svg { width:17px; height:17px; }
.ds-kanban-col-body { padding:10px; overflow-y:auto; display:flex; flex-direction:column; gap:9px; flex:1; min-height:0; }
/* Dropzone: a indicação NUNCA depende só de cor (princípios.md §1.6) — borda
   tracejada + fundo sutil + o texto "Soltar aqui" (`.ds-kanban-drop-hint`,
   inserido/removido por quem instancia nos eventos de dragover/dragleave). */
.ds-kanban-col-body.is-drop { outline:2px dashed var(--ds-accent); outline-offset:-2px; background:var(--ds-accent-soft); border-radius:var(--ds-radius-md); }
.ds-kanban-drop-hint { text-align:center; font-size:var(--ds-fs-xs); font-weight:var(--ds-fw-bold); color:var(--ds-accent-text); padding:4px; }
.ds-kanban-col-body .ds-empty, .ds-kanban-col-body .ds-error-state { padding:16px 6px; } /* compacto — coluna estreita (~284px) */
.ds-kanban-mais-wrap { flex:none; }
.ds-kanban-mais { width:100%; }

/* Card: `<button>` real (foco/teclado nativos, Enter/Espaço ativam sem JS
   extra) — nunca `div` com `onclick` fingindo botão. `draggable="true"` num
   `<button>` funciona normalmente (drag HTML5 não exige que o nó não seja
   focável). */
.ds-kanban-card { --ds-kanban-card-c:transparent; display:block; width:100%; text-align:left; font:inherit; color:inherit; background:var(--ds-surface-elevated); border:1px solid var(--ds-border); border-left:2px solid var(--ds-kanban-card-c); border-radius:var(--ds-radius-md); padding:12px; cursor:grab; position:relative;
  transition:border-color var(--ds-dur-fast) var(--ds-ease), background var(--ds-dur-fast) var(--ds-ease), opacity var(--ds-dur-fast) var(--ds-ease); }
.ds-kanban-card:hover { border-color:var(--ds-border-strong); border-left-color:var(--ds-kanban-card-c); background:var(--ds-surface-hover); }
.ds-kanban-card:focus-visible { outline:2px solid var(--ds-focus); outline-offset:2px; }
.ds-kanban-card.is-dragging { opacity:.5; }
.ds-kanban-card.is-busy { opacity:.6; cursor:wait; }
.ds-kanban-card-row { display:flex; align-items:center; gap:9px; min-width:0; }
.ds-kanban-avatar { width:28px; height:28px; border-radius:50%; background:var(--ds-surface-2); border:1px solid var(--ds-border); color:var(--ds-accent-text); display:flex; align-items:center; justify-content:center; font-weight:var(--ds-fw-bold); font-size:11px; flex:none; overflow:hidden; }
.ds-kanban-avatar img { width:100%; height:100%; object-fit:cover; }
.ds-kanban-card-tx { min-width:0; flex:1; }
.ds-kanban-card-nome { font-weight:var(--ds-fw-semibold); font-size:13px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; color:var(--ds-text); }
.ds-kanban-card-num { font-size:11px; font-family:var(--ds-font-mono); font-variant-numeric:tabular-nums; color:var(--ds-text-muted); }
/* "Não lida": o indicador visual (bolinha) nunca é o único sinal — o texto
   "não lida" (`.ds-sr-only`, dentro do span) sempre existe no DOM, e o
   `aria-label` do card também menciona "(não lida)" quando aplicável. */
.ds-kanban-unread { flex:none; width:9px; height:9px; border-radius:50%; background:var(--ds-danger); margin-left:4px; }
.ds-kanban-card-tags { display:flex; flex-wrap:wrap; gap:5px; margin-top:8px; }
/* Variante de linha única (retrocompatível: só vale com o modificador
   `.is-linha`; sem ele `.ds-kanban-card-tags` segue em flex-wrap). Linhas
   fixas p/ altura constante entre cards: `.ds-kanban-card-cx` (chip da
   conexão, sozinho, truncado) e `.is-linha` (etiquetas em 1 linha, nowrap;
   o JS esconde o que não cabe e mostra o chip `.ds-kanban-etq-mais` "+N"). A 1ª
   etiqueta é a única que encolhe (reticências); as demais têm largura natural. */
.ds-kanban-card-cx { display:flex; min-width:0; min-height:20px; margin-top:6px; }
.ds-kanban-card-cx .ds-kanban-chip-cx { max-width:100%; }
.ds-kanban-card-tags.is-linha { flex-wrap:nowrap; overflow:hidden; min-height:20px; margin-top:6px; align-items:center; }
.ds-kanban-card-tags.is-linha > * { flex:none; max-width:none; }
.ds-kanban-card-tags.is-linha > :first-child { flex:0 1 auto; min-width:0; max-width:100%; }
.ds-kanban-card-tags.is-linha > :first-child > span:last-child { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; min-width:0; }
.ds-kanban-card-tags.is-linha.ds-kanban-medidor { position:absolute; left:-9999px; top:0; width:max-content; visibility:hidden; pointer-events:none; overflow:visible; }
.ds-kanban-card-tags.is-linha > [hidden] { display:none; }
/* Chip do card (etiqueta com ponto de cor OU canal com ícone): pílula 10px
   caixa alta — mantém o padrão "nunca cor de fundo direto vinda do
   servidor" (dot valida a cor, não o fundo do chip; ver crm.html/
   contatos.html, corValidaOuNeutra). Estilo movido pra `.ds-badge-tag`
   (componente central, ver `.ds-badge` acima) — `.ds-kanban-card-tags
   .ds-badge` continua funcionando (mesmo seletor combinado lá). */
.ds-kanban-chip-cx { display:inline-flex; align-items:center; gap:4px; font-size:10px; text-transform:uppercase; letter-spacing:.02em; font-weight:var(--ds-fw-semibold); padding:2px 8px; border-radius:var(--ds-radius-full); background:var(--ds-info-soft); color:var(--ds-info-text); max-width:100%; overflow:hidden; }
.ds-kanban-chip-cx span { overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ds-kanban-chip-cx svg { width:12px; height:12px; flex:none; }
/* Alça de arrastar (paridade com a referência: opacidade 0 → 60% no hover do
   card → 100% no hover/foco da própria alça). Puramente decorativa — o drag
   real funciona no card inteiro (draggable no <button>), então fica
   aria-hidden. */
.ds-kanban-grip { position:absolute; top:8px; right:8px; width:14px; height:14px; color:var(--ds-text-muted); opacity:0; pointer-events:none; transition:opacity var(--ds-dur-fast) var(--ds-ease); }
.ds-kanban-card:hover .ds-kanban-grip { opacity:.6; }
.ds-kanban-card:focus-visible .ds-kanban-grip { opacity:1; }

@media (max-width:640px) { .ds-kanban-col { width:80vw; } }
@media (pointer:coarse) { .ds-kanban-menu-btn { min-width:var(--ds-touch); min-height:var(--ds-touch); } }

/* ---- Drawer (painel lateral) ----
   Mesma anatomia de head/body/foot do `.ds-modal` (só o body rola), mas
   ancorado na borda direita da viewport em vez de centralizado — pensado
   pra abrir o "detalhe" de um item sem tirar o board de trás da tela (o
   overlay ainda cobre tudo, mesma semântica de modal pra teclado/leitor de
   tela). `<=640px` ocupa 100% da largura (vira "tela cheia", mesmo espírito
   do bottom sheet do `.ds-modal`). O JS de abrir/fechar/trap de foco/Escape
   é responsabilidade de quem instancia (mesmo contrato do `.ds-modal`, ver
   `componentes.md`) — dívida documentada: o helper usado pelo primeiro
   consumidor (`crm.html`) é uma cópia adaptada de `criarModal()`
   (`contatos.html`/`cobranca.html`); candidato a util compartilhado quando
   houver um 3º consumidor de painel lateral fora deste arquivo. */
.ds-drawer-ov { position:fixed; inset:0; background:var(--ds-overlay); z-index:var(--ds-z-modal); display:none; }
.ds-drawer-ov.on { display:block; }
/* Fechado, o `.ds-drawer` continua no DOM com `display:flex` (só a
   `transform` o tira da viewport) — sem `visibility:hidden`, ele ficaria
   focável/exposto a leitor de tela fora da tela (achado QA HIGH,
   `migracao.md` §15.6). `visibility:hidden` some com ele de verdade quando
   fechado; a transição de `visibility` tem duração 0 e um DELAY igual à
   duração da animação de `transform`, então: fechando, só vira `hidden`
   DEPOIS do slide-out terminar (`transition-delay` no valor "closed" desta
   regra); abrindo (`.on`), `transition-delay:0s` zera esse atraso e o painel
   fica visível já no 1º frame do slide-in. Quem instancia (`criarPainel()`
   em `crm.html`) ainda soma `inert`/`aria-hidden="true"` via JS — CSS e JS
   cobrem a mesma garantia por camadas diferentes (visual + AT/teclado). */
.ds-drawer { position:fixed; top:0; right:0; height:100%; width:420px; max-width:92vw; background:var(--ds-surface); border-left:1px solid var(--ds-border); z-index:calc(var(--ds-z-modal) + 1); display:flex; flex-direction:column;
  box-shadow:var(--ds-shadow-modal); transform:translateX(100%); visibility:hidden;
  transition:transform var(--ds-dur-normal) var(--ds-ease), visibility 0s linear var(--ds-dur-normal); }
.ds-drawer.on { transform:translateX(0); visibility:visible; transition-delay:0s, 0s; }
.ds-drawer-head { display:flex; align-items:flex-start; gap:12px; flex:none; padding:18px 18px 14px; border-bottom:1px solid var(--ds-border-subtle); }
.ds-drawer-avatar { width:52px; height:52px; border-radius:50%; background:var(--ds-surface-elevated); border:1px solid var(--ds-border); color:var(--ds-accent-text); display:flex; align-items:center; justify-content:center; font-weight:var(--ds-fw-bold); font-size:var(--ds-fs-xl); flex:none; overflow:hidden; }
.ds-drawer-avatar img { width:100%; height:100%; object-fit:cover; }
.ds-drawer-headtx { min-width:0; flex:1; }
.ds-drawer-nome { margin:0; font-size:var(--ds-fs-lg); font-weight:var(--ds-fw-bold); color:var(--ds-text); overflow-wrap:anywhere; }
.ds-drawer-num { margin:2px 0 0; font-size:var(--ds-fs-sm); color:var(--ds-text-muted); overflow-wrap:anywhere; }
/* Só o body rola — head e foot ficam sempre visíveis (mesma regra do `.ds-modal`,
   ver a nota "Modal — estrutura de rolagem" em `componentes.md`). */
.ds-drawer-body { padding:16px 18px; flex:1 1 auto; min-height:0; overflow:auto; }
.ds-drawer-foot { display:flex; gap:8px; flex:none; padding:14px 18px; border-top:1px solid var(--ds-border-subtle); }
.ds-drawer-foot .ds-btn { flex:1; }
.ds-drawer-sec { font-size:var(--ds-fs-xs); font-weight:var(--ds-fw-bold); text-transform:uppercase; letter-spacing:.05em; color:var(--ds-text-muted); margin:18px 0 8px; }
.ds-drawer-sec:first-child { margin-top:0; }
.ds-drawer-row { font-size:var(--ds-fs-md); margin-bottom:6px; color:var(--ds-text); overflow-wrap:anywhere; }
.ds-drawer-row .k { color:var(--ds-text-muted); margin-right:6px; }
@media (max-width:640px) { .ds-drawer { width:100%; max-width:100%; } }

/* ---- Metric card (KPI com ícone à direita) ----
   Diferente de `.ds-stat` (rótulo + valor + rodapé, sem ícone — usado em grid
   denso tipo `.ds-stats`), `.ds-metric-card` reproduz o cartão de métrica da
   referência medido em docs/referencia-zapdata-global/01-design-system.md §4:
   `rounded-2xl` (16px, `--ds-radius-xl`), padding 20px, ícone colorido 40px
   no canto direito com tint próprio. Cor do ícone por instância: quem
   instancia seta `--ds-metric-tint`/`--ds-metric-tint-soft` inline no
   `.ds-metric-icon` (mesmo padrão de `--ds-kanban-card-c` — nunca hex fixo
   aqui). Primeiro consumidor: reconstrução do AppShell global (Entrega 4). */
.ds-metric-card { display:flex; align-items:flex-start; justify-content:space-between; gap:12px;
  background:var(--ds-card); border:1px solid var(--ds-border); border-radius:var(--ds-radius-xl); padding:20px; min-width:0; }
.ds-metric-info { min-width:0; overflow-wrap:anywhere; }
.ds-metric-label { font-size:var(--ds-fs-sm); font-weight:var(--ds-fw-semibold); color:var(--ds-text-muted); }
.ds-metric-value { font-size:var(--ds-fs-2xl); font-weight:var(--ds-fw-extrabold); letter-spacing:-.02em; margin-top:6px;
  color:var(--ds-text); font-variant-numeric:tabular-nums; overflow-wrap:anywhere; }
.ds-metric-foot { font-size:var(--ds-fs-xs); color:var(--ds-text-muted); margin-top:2px; }
.ds-metric-icon { --ds-metric-tint:var(--ds-accent); --ds-metric-tint-soft:var(--ds-accent-soft);
  flex:none; width:40px; height:40px; border-radius:var(--ds-radius-md); background:var(--ds-metric-tint-soft);
  color:var(--ds-metric-tint); display:flex; align-items:center; justify-content:center; }
.ds-metric-icon svg { width:20px; height:20px; }
@media (max-width:640px) { .ds-metric-card { padding:16px; } }

/* ---- Popover / Menu (dropdown flutuante) ----
   `.ds-popover` é o contêiner posicionado (quem instancia calcula `top/left`
   via JS, mesmo contrato de posicionamento que os menus locais já existentes
   em conexoes.html/crm.html tinham como CSS ad-hoc — aqui viram componente
   central, lacuna registrada em docs/referencia-zapdata-global/
   01-design-system.md §5). `.ds-menu`/`.ds-menu-item` é o conteúdo típico
   (lista de ações) — mas `.ds-popover` também aceita qualquer outro conteúdo
   (ex.: um mini-formulário). */
.ds-popover { position:absolute; z-index:var(--ds-z-dropdown); background:var(--ds-surface); border:1px solid var(--ds-border);
  border-radius:var(--ds-radius-md); box-shadow:var(--ds-shadow-pop); padding:6px; min-width:180px; display:none; }
.ds-popover.on { display:block; }
.ds-menu { display:flex; flex-direction:column; gap:1px; }
.ds-menu-item { display:flex; align-items:center; gap:9px; width:100%; padding:8px 10px; border-radius:var(--ds-radius-sm);
  border:none; background:transparent; color:var(--ds-text); font:inherit; font-size:var(--ds-fs-md); text-align:left;
  cursor:pointer; white-space:nowrap; }
.ds-menu-item svg { width:16px; height:16px; flex:none; color:var(--ds-text-muted); }
.ds-menu-item:hover:not(:disabled) { background:var(--ds-hover); }
.ds-menu-item:disabled { opacity:.5; cursor:not-allowed; }
.ds-menu-item.danger { color:var(--ds-danger-text); }
.ds-menu-item.danger svg { color:var(--ds-danger-text); }
.ds-menu-sep { height:1px; background:var(--ds-border-subtle); margin:4px 2px; border:none; }

/* ---- Divider / Section label (agrupar conteúdo dentro de uma página/card) ----
   `.ds-divider` é uma regra horizontal neutra (diferente das bordas de
   head/foot de modal/drawer, que já existem); `.ds-section-label` é o mesmo
   papel tipográfico do rótulo de seção da sidebar (`.zf-sec` em rail.js),
   promovido a componente central pra qualquer tela agrupar um bloco de
   conteúdo (ex.: seções dentro de um card de Configurações). */
.ds-divider { height:1px; background:var(--ds-border-subtle); border:none; margin:var(--ds-space-4) 0; }
.ds-divider.strong { background:var(--ds-border); }
.ds-section-label { font-size:var(--ds-fs-xs); font-weight:var(--ds-fw-bold); text-transform:uppercase;
  letter-spacing:.05em; color:var(--ds-text-muted); margin:0 0 8px; }

/* ---- Dropdown (.ds-dd) — substitui o <select> nativo de filtro ----
   Aprimoramento progressivo via ZFDS.dropdown(selectEl) (ds.js): o <select>
   real continua no DOM (fonte de verdade, valor e evento `change` intactos) e
   fica só visualmente oculto (`.ds-dd-src`); o gatilho é um botão de 40px com
   ícone à esquerda, texto e chevron; a lista é role=listbox/option. Modificador
   `.ds-dd--canal` (copiado do <select>): raio 12 + fundo muted 30% (filtro de
   canal da referência). */
.ds-dd { position:relative; display:inline-block; min-width:0; vertical-align:middle; }
.ds-dd--block { display:block; width:100%; }
.ds-dd-src { position:absolute !important; width:1px !important; height:1px !important; max-width:1px !important; margin:-1px; padding:0; overflow:hidden;
  clip:rect(0,0,0,0); border:0; opacity:0; pointer-events:none; }
.ds-dd-trigger { display:flex; align-items:center; gap:8px; width:100%; height:var(--ds-h-lg); min-height:var(--ds-h-lg);
  padding:0 12px; border:1px solid var(--ds-border-strong); border-radius:var(--ds-radius-md); background:var(--ds-input);
  color:var(--ds-text); font:inherit; font-size:var(--ds-fs-md); text-align:left; cursor:pointer;
  transition:border-color var(--ds-dur-fast) var(--ds-ease), box-shadow var(--ds-dur-fast) var(--ds-ease); }
.ds-dd-trigger:hover:not(:disabled) { border-color:var(--ds-text-muted); }
.ds-dd-trigger:focus-visible { outline:none; border-color:var(--ds-focus); box-shadow:0 0 0 2px var(--ds-ring); }
.ds-dd-trigger:disabled { opacity:.6; cursor:not-allowed; background:var(--ds-disabled-bg); color:var(--ds-disabled-text); }
.ds-dd-ico, .ds-dd-chev { flex:none; width:16px; height:16px; color:var(--ds-text-muted); }
.ds-dd-ico svg, .ds-dd-chev svg { width:16px; height:16px; display:block; }
.ds-dd-label { flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.ds-dd-chev { transition:transform var(--ds-dur-fast) var(--ds-ease); }
.ds-dd.on .ds-dd-chev { transform:rotate(180deg); }
.ds-dd--md .ds-dd-trigger { height:var(--ds-h-md); min-height:var(--ds-h-md); padding:0 8px; gap:6px; font-size:var(--ds-fs-sm); }
.ds-dd--canal .ds-dd-trigger { border-radius:var(--ds-radius-lg); border-color:var(--ds-border);
  background:color-mix(in srgb, var(--ds-surface-elevated) 30%, transparent); }
.ds-dd--canal .ds-dd-ico, .ds-dd--canal .ds-dd-ico svg { width:20px; height:20px; }
/* Variante só-ícone (ex.: atribuição no cabeçalho do chat): gatilho 36px sem texto/chevron; o nome atual vai no title/aria-label. */
.ds-dd--ico { width:var(--ds-h-md); flex:none; }
.ds-dd--ico .ds-dd-trigger { width:var(--ds-h-md); height:var(--ds-h-md); min-height:var(--ds-h-md); padding:0; justify-content:center;
  border-color:transparent; background:transparent; color:var(--ds-text-secondary); }
.ds-dd--ico .ds-dd-trigger:hover:not(:disabled) { background:var(--ds-hover); border-color:transparent; color:var(--ds-text); }
.ds-dd--ico .ds-dd-label, .ds-dd--ico .ds-dd-chev { display:none; }
.ds-dd--ico .ds-dd-ico { width:18px; height:18px; color:inherit; }
.ds-dd--ico .ds-dd-ico svg { width:18px; height:18px; }
.ds-dd-list { position:fixed; left:0; top:0; z-index:calc(var(--ds-z-modal) + 2); min-width:100%; width:max-content;
  max-width:min(320px, 90vw); max-height:280px; overflow-y:auto; margin:0; padding:6px; list-style:none;
  background:var(--ds-surface); border:1px solid var(--ds-border); border-radius:var(--ds-radius-lg); box-shadow:var(--ds-shadow-pop); }
.ds-dd-list[hidden] { display:none; }
.ds-dd-opt { display:flex; align-items:center; gap:8px; padding:8px 10px; border-radius:var(--ds-radius-sm);
  font-size:var(--ds-fs-md); color:var(--ds-text); cursor:pointer; white-space:nowrap; }
.ds-dd-opt.ativa { background:var(--ds-hover); }
.ds-dd-opt[aria-selected="true"] { font-weight:var(--ds-fw-semibold); color:var(--ds-accent-text); }
.ds-dd-opt[aria-disabled="true"] { opacity:.5; cursor:not-allowed; }
.ds-dd-opt .ds-dd-ok { margin-left:auto; width:14px; height:14px; flex:none; visibility:hidden; }
.ds-dd-opt[aria-selected="true"] .ds-dd-ok { visibility:visible; }
.ds-dd-opt .ds-dd-ok svg { width:14px; height:14px; display:block; }
@media (pointer:coarse) { .ds-dd-trigger, .ds-dd-opt { min-height:var(--ds-touch); } }

/* Foco visível consistente em todos os componentes .ds-* (princípios.md §1.3). */
.ds-btn:focus-visible, .ds-input:focus-visible, .ds-select:focus-visible, .ds-textarea:focus-visible,
.ds-list-item .ds-go:focus-visible, .ds-switch input:focus-visible ~ .track, .ds-modal-close:focus-visible,
.ds-check input:focus-visible, .ds-radio input:focus-visible, .ds-input-adorn:focus-visible,
.ds-auth-links a:focus-visible, .ds-auth-foot a:focus-visible, .ds-auth .ds-card p a:focus-visible,
.ds-kanban-card:focus-visible, .ds-kanban-menu-btn:focus-visible, .ds-menu-item:focus-visible {
  outline:2px solid var(--ds-focus); outline-offset:2px;
}
/* Touch target mínimo (44px) em contexto de toque — princípios.md §2.2. */
@media (pointer:coarse) {
  .ds-btn, .ds-btn-icon, .ds-modal-close, .ds-check input, .ds-radio input, .ds-input-adorn, .ds-menu-item { min-height:var(--ds-touch); }
  /* .ds-modal-close só tinha min-height aqui (30x30 de largura ficava abaixo
     do alvo de toque de 44px) — achado da fatia `feature/ds-v2-fatia-cobranca`
     (1º consumidor real de .ds-modal), corrigido aqui por ser genérico ao
     componente, não a esta tela (migracao.md §12.2). */
  .ds-btn-icon, .ds-modal-close, .ds-input-adorn { min-width:var(--ds-touch); }
  /* Links de texto da auth (Esqueci minha senha / Criar conta / Entrar) só
     têm a altura da linha de texto — QA M4: em contexto de toque ganham área
     mínima de 44px sem alterar o layout em desktop/mouse (regra escopada a
     pointer:coarse). */
  .ds-auth-links a, .ds-auth-foot a, .ds-auth .ds-card p a {
    display:inline-flex; align-items:center; min-height:var(--ds-touch); padding:0 4px;
  }
}
/* Movimento respeita prefers-reduced-motion — escopado ao DS V2 (não altera telas legadas).
   .ds-drawer/.ds-kanban-card citados explicitamente (redundante com o coringa
   `[data-ds="v2"] *` acima, que já os cobre por serem descendentes de uma tela
   `data-ds="v2"` — listados aqui do mesmo jeito que `.ds-modal`, pra ficar
   claro/testável que a transição de abrir o drawer e o hover/opacity do card
   também respeitam a preferência, não só a lista original de auth). */
@media (prefers-reduced-motion: reduce) {
  [data-ds="v2"] *, [data-ds="v2"] *::before, [data-ds="v2"] *::after,
  .ds-skeleton, .ds-spin, .ds-modal, .ds-drawer, .ds-kanban-card { animation-duration:.001ms !important; transition-duration:.001ms !important; }
  /* `.ds-drawer` fechado usa `transition-delay` (não `-duration`) pra segurar
     `visibility:hidden` até o slide-out acabar (ver a regra base acima) — a
     linha genérica só zera duração, então some aqui também o delay, senão o
     painel ficaria "visível" (embora fora da tela) por ~160ms extras mesmo
     com a preferência de movimento reduzido. */
  .ds-drawer { transition-delay:0s !important; }
}
