# Redesign visual: paleta SaaS dark (app inteiro) Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. **Goal:** Trocar a identidade visual inteira do app (login + 4 abas + modais) de uma paleta clara/laranja para uma paleta escura única estilo SaaS (Linear/Vercel), com o card de campanha ganhando um tratamento "Dispatch Board" (borda de status colorida + números tabulares monoespaçados), e o toast passando a indicar sucesso/erro por cor. **Architecture:** `public/css/app.css` é quase inteiramente orientado por variáveis CSS (`:root`); a troca de paleta é uma substituição do bloco de tokens + remoção do bloco `@media (prefers-color-scheme: dark)` (o app deixa de ter modo claro). Três gradientes hardcoded (parceiros do laranja atual) viram um novo token `--accent-light`. Tratamento "Dispatch Board" é CSS adicional em `.campaign-card` e nos números de estatística. Toast colorido é uma pequena mudança em `public/js/api.js` (inferência pelo prefixo "Erro" na própria mensagem) + 2 classes CSS novas — sem tocar nos ~40 call-sites de `toast(...)` espalhados pelos arquivos JS. **Tech Stack:** HTML/CSS/JS vanilla (sem framework de build), Node `--test`, Playwright. ## Global Constraints - Este repositório NÃO tem HEAD/commits e commits não são permitidos neste projeto — nenhuma etapa deste plano inclui `git commit`. Ignore os passos de commit do template padrão de planos. - Não há testes automatizados de aparência visual. A verificação "passou" de cada tarefa é: (a) suíte existente 100% verde, (b) checagem visual ao vivo no navegador via Claude Browser. - Servidor de dev roda em `http://127.0.0.1:3910` via `node scripts/servidor.mjs`, já ativo nesta sessão — arquivos estáticos (`public/**`) são servidos direto do disco, um `navigate` com `force: true` já pega mudanças de CSS/JS sem precisar reiniciar o processo Node. Só reiniciar o processo Node se mudar algo em `src/**`. - Design aprovado está em `docs/superpowers/specs/2026-07-30-dark-saas-redesign-design.md` — os valores exatos de cor vêm de lá, não reinvente. --- ### Task 1: Trocar os tokens de cor em `app.css` para a paleta SaaS dark única **Files:** - Modify: `public/css/app.css:1-47` (bloco `:root` e `@media (prefers-color-scheme: dark)`) - Modify: `public/css/app.css:80` (`.brand-monogram`) - Modify: `public/css/app.css:189` (`.btn-primary`) - Modify: `public/css/app.css:520` (`.progress-fill`) - Modify: `public/css/app.css:587` (`.rank-fill, .funil-fill`) **Interfaces:** - Produces: todo o resto do CSS (que já usa `var(--x)` em quase tudo) herda a nova paleta automaticamente. Nenhuma outra tarefa precisa saber os valores exatos — só que os tokens `--page`, `--surface`, `--ink`, `--accent`, `--good`, `--warning`, `--critical`, `--radius*`, `--shadow*` continuam existindo com os mesmos nomes. - [ ] **Step 1: Substituir o bloco `:root` (linhas 2-26) por estes valores** ```css :root { --page: #0b0c10; --page-2: #0d0e13; --surface: #16171d; --surface-2: #1d1f27; --ink: #f4f4f6; --ink-2: #a4a6b0; --muted: #6c6e79; --grid: #2a2c35; --border: rgba(255, 255, 255, 0.08); --accent: #6a63f6; --accent-light: #9d97ff; --accent-2: #4f8cff; --accent-ink: #ffffff; --bar: #6a63f6; --good: #22c55e; --good-text: #4ade80; --warning: #f5a524; --critical: #fb5b7c; --critical-bg: rgba(251, 91, 124, 0.12); --radius-sm: 8px; --radius: 12px; --radius-lg: 16px; --shadow: 0 20px 50px rgba(0, 0, 0, 0.45); --shadow-soft: 0 8px 24px rgba(0, 0, 0, 0.3); } ``` - [ ] **Step 2: Apagar inteiramente o bloco `@media (prefers-color-scheme: dark) { ... }` que vem logo depois (linhas 28-47 do arquivo original)** O app passa a ter uma única aparência, sempre escura — não há mais um segundo bloco de tokens para modo claro/escuro. - [ ] **Step 3: Trocar as 3 cores hardcoded que combinavam com o laranja antigo pelo novo `--accent-light`** Em `.brand-monogram`: ```css .brand-monogram { background: linear-gradient(135deg, var(--accent), var(--accent-light)); color: #fff; font-weight: 900; letter-spacing: -0.06em; box-shadow: var(--shadow-soft); } ``` Em `.btn-primary`: ```css .btn-primary { background: linear-gradient(135deg, var(--accent), var(--accent-light)); border-color: transparent; color: var(--accent-ink); } ``` Em `.progress-fill`: ```css .progress-fill { height: 100%; width: 3%; background: linear-gradient(90deg, var(--accent), var(--accent-light)); border-radius: 999px; transition: width 0.4s; } ``` Em `.rank-fill, .funil-fill`: ```css .rank-fill, .funil-fill { height: 100%; min-width: 3px; border-radius: 999px; background: linear-gradient(90deg, var(--bar), var(--accent-light)); } ``` - [ ] **Step 4: Rodar a suíte de testes existente para confirmar que nada de lógica quebrou** Run: `cd "D:/PROJETOS/COMERCIAL GTI/MAQUINA COMERCIAL PESSOAL" && node --test` Expected: todos os testes continuam passando (a suíte não valida cor/CSS, só comportamento — uma troca de paleta não deveria derrubar nenhum). Preste atenção especial ao teste `interface e arquivos operacionais usam somente a identidade pessoal` (`tests/personal-branding.test.mjs`) — ele lê `public/css/app.css` procurando strings de marca "Grupo GTI"; os valores hex novos não contêm essas strings, então deve continuar passando. - [ ] **Step 5: Checagem visual ao vivo — página de login** Usando o Claude Browser: `navigate` para `http://127.0.0.1:3910` com `force: true`, tirar um `read_page`/`get_page_text` e confirmar visualmente (screenshot, se a pane permitir) que o fundo está escuro (`#0b0c10`-ish), o card de login com fundo `#16171d`-ish, texto claro legível, sem nenhum resquício de fundo claro/branco aparecendo. - [ ] **Step 6: Checagem visual ao vivo — aba Capturar Leads (já vem ativa por padrão) e aba Tratar Leads** Clicar na aba "Tratar Leads", conferir que os `stat-tiles`, tabela de leads, badges de classificação (excelente/bom/médio/baixo) e gráficos de ranking estão legíveis e com contraste adequado no fundo escuro novo. Não deve haver texto escuro sobre fundo escuro (herança de alguma cor que não passou a usar variável). Não há commit nesta etapa (projeto sem git commits). --- ### Task 2: Tratamento "Dispatch Board" nos cards de campanha e números de estatística **Files:** - Modify: `public/css/app.css` (seção `/* campanhas */`, a partir de `.campaign-cards`) - Modify: `public/css/app.css:522-525` (`.stat-row .mini b`) - Modify: `public/css/app.css:562` (`.tile .valor`) **Interfaces:** - Consumes: tokens `--muted`, `--critical`, `--good`, `--radius`, `--border` de Task 1. - Produces: nada que outra tarefa dependa — mudança visual isolada. - [ ] **Step 1: Adicionar borda de status colorida à esquerda do card de campanha** Na seção `/* campanhas */` de `app.css`, depois da regra `.campaign-card { margin-bottom: 0; }`, adicionar: ```css .campaign-card { border-left: 3px solid var(--muted); border-radius: var(--radius); } .campaign-card:has(.chip.warn) { border-left-color: var(--critical); } .campaign-card:has(.chip.ok) { border-left-color: var(--good); } ``` (`:has()` funciona nos navegadores modernos — Chrome/Edge 105+, Firefox 121+ — e o card já usa `.chip.warn` para pausada e `.chip.ok` para ativa, ver `public/js/campanhas.js` função `renderCard`, então não precisa mexer no JS.) - [ ] **Step 2: Números tabulares monoespaçados nos contadores de uso/limite e nas estatísticas** ```css .campaign-usage span, .stat-row .mini b, .tile .valor { font-variant-numeric: tabular-nums; font-family: ui-monospace, SFMono-Regular, Consolas, "Liberation Mono", monospace; } ``` - [ ] **Step 3: Rodar a suíte de testes existente** Run: `node --test` Expected: todos os testes continuam passando (mudança é puramente visual/CSS, nenhuma classe foi removida, `.chip.warn`/`.chip.ok` continuam existindo do mesmo jeito). - [ ] **Step 4: Checagem visual ao vivo — aba Campanhas** Navegar para a aba Campanhas, conferir visualmente: cada card de campanha tem uma borda colorida na lateral esquerda (cinza-neutro para pronta, vermelha para pausada, verde para ativa/rodando); os números de "Manual: X/Y" e "Automático: X/Y" e o bloco de métricas (Visualizados/Aprovados/Enviados/Responderam, visível clicando "Ver fila" numa campanha) estão em fonte monoespaçada tabular. Confirmar que o texto "Pausado: ..." e os botões continuam legíveis e clicáveis. Não há commit nesta etapa. --- ### Task 3: Toast colorido por sucesso/erro **Files:** - Modify: `public/js/api.js:82-94` (função `toast`) - Modify: `public/css/app.css:682-696` (`.toast`) **Interfaces:** - Consumes: `document.getElementById('toast')` (já existe em `index.html`/`login.html`, id `toast`). - Produces: `toast(message, ms)` continua com a mesma assinatura pública — nenhum call-site em `app.js`/`tratamento.js`/`captura.js`/`campanhas.js` precisa mudar. **Por que não tem um teste automatizado nesta tarefa:** `tests/campaign-ui.test.mjs` roda `app.js`/`campanhas.js` dentro de um `vm.createContext` cujo objeto de contexto já define `API`, `el`, `h`, `dataBr`, `fmt` como mocks manuais (não carregados de `api.js`). Se `public/js/api.js` fosse executado nesse mesmo contexto via `vm.runInContext` para testar o `toast()` real, o `const API = {...}` de dentro de `api.js` sombrearia/substituiria o `API` mockado que os outros ~15 testes desse arquivo dependem para interceptar chamadas de rede — quebrando a suíte inteira. Isolar só a função `toast()` do resto de `api.js` exigiria transformar o arquivo num módulo ES (`export`), o que está fora do escopo desta tarefa (o frontend inteiro usa `