Ir ao conteúdo
SEED engenhariaDesign System

Componentes

48. Painel — grade, lentes, frescor, edição direta e mapa

estávelseed-componentes.md v1.43 · §48seção 50 de 9848-painel-grade-lentes-frescor-edicao-direta-e.md · MD5 0fa2e582

Título completo no canon: Painel — grade, lentes, frescor, edição direta e mapa — estável (v0.62) · validado pelo Rafael em 2026-08-13 · PN1–PN54 · pattern F6 · cinco camadas de validação verdes: suite-painel.mjs 133/133 · render-painel.mjs 37/37 · render-edicao.mjs 142/142 · render-contraste.mjs 6/6 · contraste-painel.py 70/70 · preview v0.6 · BLOCO 6C FECHADO

⚠ FRONTEIRA DA PROMOÇÃO, e ela é dura. O que este estável cobre são os contratos PN1–PN54: grade, lentes, frescor, blocos de dado, edição direta, mapa. A camada visual (§48.3) é PROPOSTA, não canônico.

A ressalva se pagou em menos de 24 horas, e o registro disso é útil. A promoção foi escrita em 2026-08-13 excluindo três decisões de marca; na mesma noite o Rafael retirou o azul ("eu tinha aceitado para podermos parar com os testes") e a âncora escura virou turquesa vivo. Duas das três decisões mudaram de valor ou saíram — e nada do estável precisou ser tocado. Se o azul tivesse entrado no canônico no dia anterior, isto seria errata em marca-seed.md, nos três gêmeos de token, no sistema de e-mail (estável) e nos 40 HTML do repositório.

Estado atual das decisões de marca: S3 revisto — a âncora é turquesa-400 #11B0A0, não mais azul-800. S4 RETIRADO — a ação volta à família da marca, e o conflito com o §3.5 de marca-seed.md deixa de existir: nada a superseder na marca. S6 (tinta da família) segue PROPOSTA, como camada alternável pelo botão "Tinta". Ver §48.8 e a pendência PN-P1 do §48.9, que encolheu na mesma proporção. Quem consumir este §48 hoje para escrever código usa a régua de ação do marca-seed.md §3.5 — que é, desde esta rodada, a MESMA do preview.

Natureza: pattern de COMPOSIÇÃO do F6, como o §33, o §46 e o §47 — nenhum componente novo nasce aqui. Consome o shell (§46), tabela (§40), lista de dados (§43), toolbar (§42), tabs (§34), os gráficos da Fase 5 e os estados de espera do Bloco 3.

Este bloco cumpre uma fronteira declarada há duas fases. O DF6 do seed-dataviz.md (Fase 5, estável desde 2026-08-09) escreveu: "dashboard/layout de painel = F6 — a F5 entrega a peça, não o painel". É esta seção.

O QUE ESTE BLOCO ENTREGA, EM TRÊS RODADAS. O 6C foi produzido em três rodadas, e o registro da ordem importa porque cada uma nasceu de um gate do Rafael sobre a anterior. Rodada 1 (2026-08-12): a grade, o comutador de visualização com pré-requisitos de dado, a gramática do frescor, a variante equipe × cliente, o consumo do movimento (PN1–PN12) e os quatro blocos de dado que não são gráfico (PN13–PN20). Rodada 2 (2026-08-13, manhã): o supersede do PN3 para oito lentes e a camada visual PN21. Rodada 3 (2026-08-13, tarde e noite): a edição direta (PN22–PN41), os contratos que o gate produziu (PN46–PN50) e a lente mapa em dois modos (PN51–PN54). Preview e gate visual são únicos, no fim.

Escopo do produto, decidido em briefing antes da pesquisa (2026-08-12): o ERP atende quatro setores — Admin, Engenharia, Comercial e Qualidade — com áreas dentro deles; a estrutura de setores e áreas e o desenho do workflow são do PROJETO DO ERP, declarados fora deste bloco pelo Rafael. O painel serve dois públicos: a equipe (prioritário) e o cliente, que terá área própria com produtos e serviços contratados, arquivos e contratos, monitoramento de usina e um sistema de indicação de leads com acompanhamento de status. O painel é fixo — o que varia é a forma de exibir a mesma informação, no padrão do ClickUp. O painel precisa poder mostrar informação em tempo real. Referências visuais: ClickUp (comutador) e a plataforma do estudo-viver-de-ia.md (KPI, ranking, progresso, camada visual) — ambas já estudadas e ancoradas; nenhuma referência externa nova.

A DISTINÇÃO QUE DEFINE O TAMANHO DO BLOCO, e ela não estava clara no briefing. O que o Rafael descreveu não é painel configurável — é comutador de visualização. Configurável é o usuário arrastar blocos e escolher o que aparece: caro, e explicitamente descartado. Comutador é a mesma informação em formatos diferentes, com a tela permanecendo fixa. Separar as duas coisas cortou o custo do bloco pela metade e é a razão de o PN1 e o PN3 coexistirem sem conflito.

Base (3 rodadas encadeadas, 2026-08-12): R1 canon internoDF6 (esta fronteira) e DM10 (tempo real é produto); a Régua de Espera do Bloco 3 em três regimes compostos (ação · carga inicial · refresh sobre conteúdo, com delay de 1s e permanência mínima); os dois regimes de live region do §1.13 (discreto × contínuo, uma por componente); o §46 inteiro (SH2 landmarks nomeados, SH5 ativo que sobrevive ao grayscale); o DP12 (destino externo × interno); o §40 (densidade compacta medida em 33px); e a §6b do estudo-viver-de-ia.md — a auditoria que mostrou que a "camada de movimento" não era lacuna de token (existem 4 durações + 4 easings + prefers-reduced-motion desde os primitivos) e sim de consumo. R2 mercado — comutador de visualização no ClickUp, Notion e Linear: as lentes mostram os mesmos itens com filtro e layout próprios sobre a mesma fonte; trocar de lente não altera os itens; e a adequação da lente depende do dado. R3 normasWCAG 2.2.2 (nível A): informação que se atualiza sozinha, começa automaticamente e é apresentada em paralelo com outro conteúdo exige mecanismo para pausar, parar, esconder ou controlar a frequência; painel com intervalo escolhível (30s / 5min / manual) satisfaz pelo controle de frequência. Base adicional da rodada 3 (edição direta): WCAG 2.2, SC 2.5.7 Dragging Movements (AA) — toda funcionalidade que usa arrasto tem de ter caminho de ponteiro único alternativo; e a documentação do ClickUp sobre gantt × timeline (ver PN3c). Consolidados aprovados pelo Rafael em 2026-08-12 e 2026-08-13.

48.1 Decisões estruturais · PN1–PN12#

# Decisão Porquê Descartado
PN1 O painel é uma GRADE de 12 colunas, com blocos de largura declarada (4, 6, 8 ou 12). Layout fixo por tela, definido no projeto do ERP Painel configurável — arrastar, redimensionar, escolher o que aparece — foi descartado pelo Rafael e é várias vezes mais caro: o DS deixaria de desenhar a tela para desenhar as peças mais a grade que as recebe. Grade declarada dá liberdade de composição sem arrasto Painel arrastável · larguras livres em pixel (impede reflow previsível a 360px)
PN2 Todo bloco do painel é <section> com cabeçalho próprio e nome acessível. Um bloco = uma pergunta respondida Painel é uma página com muitas regiões; sem nome, o leitor de tela entrega uma pilha de conteúdo sem estrutura. Herda o SH2 do §46. A regra "uma pergunta" é o que impede o bloco-depósito Bloco sem título · dois assuntos no mesmo bloco
PN3 COMUTADOR DE VISUALIZAÇÃO — OITO lentes (SUPERSEDE 2026-08-13; 1ª redação: cinco): lista · tabela · kanban · gantt · tempo (linha do tempo por responsável) · calendario · carga (carga de trabalho) · mapa. Os oito nomes entre acentos graves são os identificadores canônicos — o atributo data-lente do preview usa exatamente estes É o que o Rafael pediu, no padrão do ClickUp. A tela é fixa; o que troca é a lente. O supersede tem duas causas, ambas de 2026-08-13: (a) a documentação do ClickUp provou que gantt e linha do tempo são lentes DIFERENTES (ver PN3c) — a 1ª redação as tratava como sinônimos; (b) decisão do Rafael: os templates estruturais nascem AGORA, e a alocação de pessoa precisa estar prevista mesmo que o ERP não a use de imediato — o que supersede a proposta anterior de deixar linha do tempo e carga de trabalho "para depois". O mapa entra como lente porque OS tem endereço e usina tem coordenada, e o §4d (DP) já entrega malha IBGE e projeção: ver a mesma lista de OS num mapa é o mesmo dado noutra forma. No modo cliente, o mapa herda o PN10/DP12: só as usinas do próprio cliente Painel configurável · lente única por tela · adiar a alocação de pessoa (o template nasceria sem o lugar dela e obrigaria redesenho — a mesma razão da ordem lote 1 → lote 2) · tratar como lentes o que é tipo de conteúdo ou tela própria no ClickUp (formulário, documento, whiteboard, mapa mental, chat) ou integração de produto (incorporações) · o rótulo "quadro" como nome canônico (ver S2 em §48.8)
PN3b A LENTE DESENHA A FORMA QUE O NOME PROMETE. Linha do tempo é barra posicionada e dimensionada por data, sobre escala de datas. Calendário é grade de mês — sete colunas iguais, primeiro dia da semana calculado, evento no seu dia. Kanban é coluna por estado. Nenhuma delas pode ser uma lista com outro rótulo Achado no gate visual do Rafael. O preview v0.1 renderizava "linha do tempo" e "calendário" como listas — passava em todo teste de comportamento e mentia para o olho. É a mesma classe do botão morto que o projeto já proíbe: "responder significa DEMONSTRAR, não explicar" (§A.3 do ESTADO_ATUAL, emendado no marco v1.7). Guardas permanentes: PN3b-01…08 na suíte e R5b-01…08 no render, este último medindo geometria — barras em posições e larguras distintas, dentro da faixa, e as sete colunas do calendário com a mesma largura Lente que só troca o rótulo · lente "em breve" (é botão morto com outro nome)
PN3c GANTT ≠ LINHA DO TEMPO — são perguntas diferentes sobre as mesmas tarefas. Gantt responde "o que trava o quê": uma faixa por TAREFA, barra posicionada e dimensionada por data, e as dependências DESENHADAS — conector do fim da predecessora ao início da dependente. Linha do tempo responde "quem está ocupado quando": uma faixa por RESPONSÁVEL, com as tarefas da pessoa empilhadas dentro da faixa dela (sub-linhas quando se sobrepõem). Gantt sem dependência desenhada não é gantt Fonte: documentação oficial do ClickUp (Gantt = tarefas conectadas, sequenciamento, caminho crítico, reagendar cadeias; Timeline = cronograma linear por recurso, para roadmap e gestão de alocação). Registro de origem: o preview v0.2 implementava um gantt sem dependências com o rótulo de linha do tempo — a distinção veio de pergunta do Rafael ("tem o gantt e tem a linha do tempo, existe diferença") e foi respondida pela fonte, não por dedução Tratar como sinônimos (o defeito de origem) · gantt sem conector de dependência (é linha do tempo de tarefa com o nome errado)
PN4 O DADO habilita a lente, não a tela. Pré-requisitos declarados: lista e tabela não exigem nada · kanban exige campo de estado com valores finitos em todos os itens · gantt exige ao menos uma ordem com início E fim · tempo exige ao menos uma agendada E alguém com responsável · calendario exige ao menos uma com data · carga exige responsável · mapa exige coordenada ou endereço geocodificável em todos. Lente sem pré-requisito não é oferecida — nunca oferecida-e-vazia; e o motivo da ausência é dito com o nome do campo que falta Responde a dúvida do Rafael ("as telas serão montadas dizendo que é permitido, a não ser que o sistema consiga entender sozinho") sem as duas saídas ruins. Regra estável, sem lista de exceções para manter. O quantificador "ao menos uma" é SUPERSEDE de 2026-08-13 — ver PN47 e S8 em §48.8: a 1ª redação exigia o dado em TODOS os itens e uma única ordem sem data derrubava o gantt inteiro Marcação manual por tela (vira lista viva de manutenção, e lista de exceções apodrece) · inferência mágica sem regra escrita (falha em silêncio) · oferecer a lente e mostrar vazio (o usuário conclui que não há dado, quando o que falta é campo)
PN5 A lente NUNCA altera o conjunto. Filtro e ordenação viajam com a troca de lente; o que muda é a forma Se a lente filtrar, o usuário perde o rastro do que sumiu e passa a desconfiar do painel. É o que a evidência de mercado descreve: trocar de visualização não muda os itens, só como aparecem Filtro embutido na lente · lente que esconde item sem pré-requisito (esconder é filtrar)
PN6 A lente ativa é estado de navegação: aria-current, marca visual que sobrevive ao grayscale, e uma lente padrão declarada por tela Herda o SH5 do §46 sem alteração. A lente padrão existe para que a tela abra resolvida, não perguntando Só cor de fundo (morre em cinza) · última lente usada como padrão global (o mesmo usuário quer kanban numa tela e calendário noutra)
PN7 GRAMÁTICA DO FRESCOR. Todo bloco com dado que se atualiza declara três coisas: momento da última leitura · estado atual (atualizado / atualizando / falhou) · idade do dado quando ela importa O DM10 e o DF6 já declararam que tempo real, websocket e persistência são produto. O DS declara a gramática, nunca o transporte — mesma precisão de fronteira que o §47 fez com a camada GEO. Sem isso, "está gerando 0 kW" e "não sei o que está gerando" viram a mesma tela — e num painel de usina essa diferença é a diferença entre chamar o técnico e não chamar Indicador global de "ao vivo" no topo do painel (não diz qual bloco está velho) · dado velho sem marca · esconder o valor enquanto atualiza (pisca e perde a referência)
PN8 CONTROLE DE FREQUÊNCIA OBRIGATÓRIO em painel que se atualiza sozinho — intervalo escolhível, com opção manual WCAG 2.2.2, nível A: informação que se atualiza automaticamente e é apresentada em paralelo com outro conteúdo exige mecanismo para pausar, parar, esconder ou controlar a frequência. Um painel com intervalo escolhível satisfaz pela frequência. Painel em tempo real sem isso não é AA — não é nem A Atualização fixa e não interrompível · pausar sem retomar (o usuário fica com dado velho sem saber)
PN9 Atualização automática NÃO move o foco, NÃO reordena o que está sendo lido e NÃO fala sozinha. Só o que o usuário pediu para acompanhar anuncia; o resto atualiza em silêncio, com o frescor do PN7 dando a evidência visual. O painel tem UMA live region (role="status", aria-live="polite"), e todo anúncio — de edição, de filtro, de zoom, de fallback de mapa — sai por ela Um painel que anuncia cada mudança em live region é inutilizável com leitor de tela. Herda os dois regimes do §1.13: o discreto (o nó visível é a live region) serve ao bloco acompanhado; o contínuo, com debounce e marcos, ao resto. A regra da região única foi reafirmada na rodada 3: o rótulo do zoom ("15 d na tela") é estado visível, não anúncio — quem anuncia a mudança é a região única Toda atualização em aria-live · reordenar a lista enquanto o usuário lê · rolar automaticamente ao chegar dado novo · uma live region por controle (duas regiões competindo produzem fala sobreposta)
PN10 Variante EQUIPE × CLIENTE declarada, com UM contrato só. O painel do cliente vê apenas o que é dele; identificação de terceiros nunca aparece — inclusive dentro de agregados Herda a estrutura do DP12 (destino externo × interno). Área do cliente com produtos contratados, arquivos, contratos, monitoramento e indicação de leads é o mesmo painel com escopo diferente, não um segundo produto. A ressalva do agregado é a que costuma vazar: "você está entre os 3 maiores clientes" revela o tamanho dos outros Dois painéis (divergem em seis meses) · mesmo painel sem escopo declarado (vazamento por omissão)
PN11 CONSUMO DO MOVIMENTO declarado por papel: registro produtivo para o que serve à tarefa (troca de lente, abrir e fechar bloco, hover) · registro expressivo só para momento significativo (entrada de bloco novo, confirmação de ação). Atualização de dado NÃO anima Fecha a lacuna nº 5 do censo do estudo-viver-de-ia.md — que a auditoria da Fase 5 provou não ser lacuna de token: existem 4 durações, 4 easings e o bloco prefers-reduced-motion desde os primitivos (errata E-EST1). O que faltava era declarar o consumo, e é isto. Dado que pisca a cada leitura é ruído: chama atenção para o que não mudou de importância Animar a troca de valor · registro expressivo em interação de tarefa (300–400ms numa troca de lente parece o sistema travando)
PN12 Densidade: compacta por padrão no ERP, confortável no painel do cliente O §40 já mediu a compacta em 33px e o gate do Bloco 6 a aprovou. Equipe treinada ganha com densidade — mais informação por tela, menos rolagem; cliente eventual perde, porque densidade exige familiaridade Densidade única para os dois públicos

48.2 Blocos de dado que não são gráfico · PN13–PN20#

Escrito em 2026-08-12. Estes quatro blocos — KPI, ranking, progresso e timeline — são os buracos vermelhos que o censo de lacunas do estudo-viver-de-ia.md (§6) encontrou e que a auditoria de impacto do marco da Fase 5 (§6b daquele arquivo) classificou como aditivos: nenhum exige supersede em algo estável. O achado estrutural do censo foi que os buracos não eram gráficos — eram componentes de dado que não são gráficos, uma classe intermediária sem dono entre o Bloco 6 (dados) e o DG (gráficos core). É esta seção.

Três dos quatro são INVÓLUCRO, não desenho — e isso está medido, não suposto: o DG16 já especifica a gramática do cartão-KPI, e o DG15 mais o DG17 já especificam a barra de ranking e o caso de uso dela. O DS promove o que já existe; inventa só onde havia vazio de verdade (timeline).

Base adicional deste lote (R2/R3, 2026-08-12): APG do W3C sobre propriedades de faixa (aria-valuenow, aria-valuemin, aria-valuemax, aria-valuetext); documentação do MDN sobre o papel progressbar e o efeito dele sobre os descendentes; a distinção normativa entre progress (conclusão de tarefa) e meter (quantidade escalar em faixa conhecida); e a orientação de anunciar progresso em marcos, não a cada ponto percentual.

# Decisão Porquê Descartado
PN13 KPI — o número é o PROTAGONISTA. Valor em fs-h2 peso 800 · rótulo em micro-caps mono · variação em badge-pílula suave (fundo feedback-*-bg, texto feedback-*-text). Vive fora de <figure>: é bloco autônomo do painel, não gráfico O DG16 já escreveu esta gramática e está estável desde 2026-08-10. O 6C promove, não inventa — a auditoria de impacto da Fase 5 já havia mostrado que a lacuna era "menos vazia do que parecia". Reescrever produziria duas fontes da mesma regra, que é o problema que o DS existe para matar (DF7) Recriar a gramática aqui (deriva garantida) · KPI dentro de <figure> (obrigaria o fallback textual do DG1 para um número que já é texto)
PN14 O KPI declara o PERÍODO e a BASE de comparação. "R$ 486k · +12% vs. mês anterior" — nunca "+12%" solto Variação sem base é número sem significado: +12% contra o mês passado, contra o ano passado e contra a meta são três afirmações diferentes. É o irmão do MK15 do §47 (número declara fonte e data) — mesma regra, outro contexto. E a seta de variação nunca vai sozinha: a escada de mecanismos do §1.12 exige sinal mais texto, senão morre em escala de cinza Variação sem referência · seta colorida como único sinal · comparação implícita ("o usuário sabe qual é")
PN14b A COR DA VARIAÇÃO VEM DO SENTIDO DO INDICADOR, NUNCA DO SINAL. Cada KPI declara a polaridade — maior é melhor (geração, usinas em operação) ou menor é melhor (chamados abertos, tempo de resposta, perdas) —, e a pílula é positiva quando a variação melhora o indicador. A seta carrega o sinal; a cor carrega o juízo Achado na inspeção do render, não previsto na spec. O preview saiu com "-3 chamados abertos" em vermelho, porque eu havia pintado pelo sinal da variação. Reduzir chamados é bom. Sem a polaridade declarada, todo indicador em que menos é melhor sai pintado ao contrário — e num painel de O&M isso é a maioria deles: falhas, chamados, tempo de parada, perdas. Efeito colateral bom: separar seta (sinal) de cor (juízo) dá dois mecanismos onde antes havia um, o que é a escada do §1.12 de graça Pintar pelo sinal da variação (o defeito) · omitir a seta e deixar só a cor (morre em escala de cinza)
PN15 RANKING — lista ordenada com barra embutida: índice em mono · rótulo · barra fina em pílula completa (rx = metade da altura) · valor numérico à direita. A barra é proporcional ao MAIOR VALOR DA LISTA, e a base é declarada O DG15 já define a pílula da barra fina e o DG17 já nomeia o caso de uso: "ranking de usinas com a usina em foco destacada". Faltava a lista que hospeda, não a barra. A base proporcional precisa ser dita porque duas listas lado a lado com bases diferentes enganam quem compara, e ninguém percebe Barra proporcional a um teto arbitrário ou a 100% (achata o ranking inteiro quando os valores são próximos) · ranking sem o valor numérico (a barra sozinha não se lê, e a lista vira decoração)
PN16 O ranking mostra quem está bem E quem não está, no MESMO bloco: contador de exceção no cabeçalho, sempre com prescrição Padrão medido no estudo-viver-de-ia.md (§3.7 e §3.12): o card de ranking traz "4 sem acesso" no canto, e a exceção vem sempre com o próximo passo ao lado. Dado + diagnóstico + próximo passo, nunca dado sozinho. Num painel de O&M, "as 5 usinas que mais geraram" sem "as 3 que não reportaram" é meia informação Ranking só dos melhores (esconde justamente o que o gestor precisa ver) · exceção sem prescrição (produz ansiedade sem ação)
PN17 PROGRESSO DE CONCLUSÃO usa progressbar — e isso NÃO contraria o DM4 O DM4 escolheu role="meter" para o medidor e descartou explicitamente progressbar. São semânticas opostas, com fonte normativa: progress indica conclusão de tarefa ("você completou 30%"); meter indica quantidade escalar dentro de faixa conhecida ("2GB de 10GB usados"). O DM4 decidiu sobre medidor de valor; este bloco decide sobre progresso de tarefa. O registro explícito é obrigatório — sem ele, parece exceção silenciosa, e foi exatamente isso que a auditoria de impacto da Fase 5 mandou evitar meter para progresso (semântica errada: obra 40% concluída não é "40 de 100 unidades usadas") · adotar progressbar sem declarar a distinção (a exceção silenciosa que a auditoria antecipou)
PN18 NADA SEMÂNTICO VIVE DENTRO DO progressbar. Rótulo, fração e porcentagem ficam fora, associados por aria-labelledby. Progresso indeterminado OMITE aria-valuenow Armadilha documentada no MDN: o navegador aplica role="presentation" a todos os descendentes de um progressbar, porque a API de acessibilidade não representa elementos semânticos dentro dele. Um título ou uma fração colocados na barra deixam de existir para o leitor de tela — e o desenvolvedor não tem como perceber, porque a tela continua certa. Sobre o indeterminado, o APG é explícito: se o valor é desconhecido, omita aria-valuenow Fração ou rótulo dentro da barra · aria-valuenow="0" para indeterminado (afirma "0%", que é falso e diferente de "não sei")
PN19 Progresso anuncia em MARCOS — 25 / 50 / 75 / 100% — nunca a cada ponto Anunciar cada ponto percentual sobrecarrega o usuário de leitor de tela a ponto de inutilizar a tela. Herda o regime contínuo de live region do §1.13, que já prevê debounce e marcos, e é a mesma cadência que o MK19 do §47 adotou para o vídeo. A conclusão é o marco que nunca se omite — é o único que sempre importa Anunciar cada atualização · omitir a conclusão · anunciar progresso que o usuário não pediu para acompanhar (herda o PN9)
PN20 TIMELINE — lista ordenada no tempo, MAIS RECENTE PRIMEIRO, com data em mono, marcador de evento e agrupamento por período. Cada entrada é ENDEREÇÁVEL Território limpo: nada no DS tocava histórico ordenado no tempo, e o ERP precisa dele para histórico de obra, de manutenção e de comissionamento. Mais recente primeiro porque num painel o que importa é o que mudou por último. Endereçável pelo mesmo motivo do MK18 (case em vídeo): um evento de histórico precisa de link próprio para ser citado num laudo, num e-mail ou numa conversa com o cliente Timeline como decoração vertical sem semântica de lista (leitor de tela recebe uma pilha de texto) · ordem cronológica direta (útil em narrativa, não em painel) · entrada sem endereço (não dá para referenciar)

Registro de honestidade: o KPI, o ranking e o progresso já tinham gramática escrita no DG15, DG16 e DG17 — esta seção entrega o invólucro. Só a timeline é desenho novo. Dizer isso importa porque a impressão de que um bloco "criou quatro componentes" é falsa, e quem ler daqui a um ano precisa saber onde procurar a regra de origem.

48.3 Camada visual · PN21a–PN21h#

Origem: pedido explícito do Rafael em 2026-08-13 — as técnicas do estudo-viver-de-ia.md estavam entendidas e não estavam refletidas no resultado. Todos os valores abaixo são os medidos na §9.4 e na §2 daquele estudo ou medidos no gate desta rodada, nunca impressão de olho. A adaptação preserva as leis SEED: paleta 2018 intocada (a monocromia navy do estudo, catalogada lá como D1, segue rejeitada), gráfico com anatomia acessível (D2), regra do dourado e variante -stroke continuam valendo por cima.

# Regra Porquê / número medido Descartado
PN21a Sombra premium multicamada COM ANEL: duas difusas + anel de 1px na mesma declaração (0 0 0 1px) Receita §9.4-3 do estudo. O anel é o que separa o cartão do fundo quando a difusa é sutil; sem ele o cartão flutua sem contorno e a borda tem de voltar como traço, que é o que o DS quer evitar Borda de 1px como único separador · sombra de uma camada (lê como carimbo)
PN21b VÉU RADIAL DE LUZ é a única forma de gradiente — nunca gradiente de cor. O teto de alfa é POR SUBSTRATO, não único: sobre superfície clara, ≤ .07 · em camada ::before de substrato escuro, ≤ .16 Emenda medida no gate de 2026-08-13. Sobre o cinza-900 da marca, véu de acento a .04 move o pixel 5 níveis de 255 e a .09 move 12 — invisível no monitor do usuário; a .16 move 21, que é o mínimo perceptível ali. Sobre superfície clara o teto continua baixo, porque o que se acumula lá é sujeira, não luz Teto único de alfa para os dois substratos (a 1ª redação; deixava o escuro sem nenhuma luz visível) · gradiente de cor
PN21b-2 O véu de ACENTO (turquesa) pertence ao substrato ESCURO. Sobre superfície clara o véu é NEUTRO — cinza da marca a 3% Medido no gate: véu de acento sobre claro desvia um canal só — turquesa a 7% tira 16 níveis de R contra 5 de G — e o olho lê tinta por cima, não profundidade. O véu do estudo é quase-neutro (desvio R+10 G+9 B+8); o do claro passou a cinza a 3% (desvio uniforme R+6 G+6 B+6) e a área encolheu Véu turquesa sobre a página clara (a hipótese testada e reprovada)
PN21b-3 O SUBSTRATO DA PÁGINA É ESCURO e a "página clara" é um VÉU BRANCO TRANSLÚCIDO por cima (.995 → .985 → .965 ao longo da altura) §9.4-1 do estudo, medido no código da plataforma (.96 → .84 → .58): a página clara é literalmente iluminada sobre escuro, e é daí que vem a profundidade. As duas redações anteriores fizeram o inverso — página clara chapada com véu escuro por cima — e é por isso que a tela lia sombria em vez de viva Página clara opaca com véu escuro por cima (as duas redações reprovadas)
PN21c UM hero escuro por página, com anatomia fixa: micro-rótulo mono → saudação ciente da hora → data viva em mono caps → apoio → UMA linha de ação (exceção com prescrição + botão da solução ao lado) §3.2 do estudo, relido no gate. A redação anterior empilhava CINCO camadas (stats E exceção E prescrição E controles) e o hero media 456px contra 268px do da plataforma. Os stats saíram por serem os mesmos números dos KPIs logo abaixo — dado repetido na mesma dobra. Controle de painel vive em régua CLARA, fora do escuro editorial Dois blocos escuros competindo na mesma página · stats no hero repetindo KPI · seletor de frequência dentro do hero
PN21d MONO como camada semântica obrigatória: todo dado, medição, data, contagem e rótulo de KPI em JetBrains Mono; micro-rótulo = caps + tracking largo + corpo pequeno + cor secundária Lei dos tokens v1.1 (dado de medição = mono), aqui promovida a obrigação do painel. É também a base da amplitude tipográfica: o número gigante só se lê como número porque o rótulo ao lado é micro Dado em Montserrat · micro-rótulo sem caps/tracking
PN21e INSIGHT-COMO-TÍTULO em todo gráfico do painel (A1 do estudo) — o título é a conclusão, com itálico marcando o sujeito (B2), no máximo um por título O título que descreve o eixo ("OS por estado") obriga o leitor a concluir sozinho; o título que conclui ("Em campo concentra 6 das 14 ordens") entrega a leitura. O limite de um itálico existe porque dois sujeitos marcados no mesmo título anulam a marcação Título descritivo de eixo · dois ou mais itálicos por título
PN21f Tipografia consome a escala canônica: fs-display-fluid (clamp 40→64px) + ls-display no hero; fs-caption no micro-rótulo. Amplitude hero/micro-rótulo = 6,1× Defeito medido: a 1ª redação usou 1,7rem (27px) no hero e desperdiçou a escala — o DS já tinha o display fluido e o tracking negativo, e o hero não os consumia. Amplitude é o que faz a hierarquia existir sem borda nem cor Tamanho digitado em rem no hero · amplitude achatada
PN21g Glow do acento turquesa JAMAIS sobre superfície de dado. Destaque de ação usa sombra projetada, não glow Emenda do gate: com a ação virando pílula branca sobre o escuro (ver S4), o glow turquesa perdeu o portador — brilho de acento atrás de um botão branco vira halo sujo. O destaque passou a sombra projetada. A proibição que importa (glow nunca sobre KPI, gráfico, tabela, mapa) continua medida na suíte Glow sobre gráfico ou KPI (vira ruído sobre dado) · glow atrás de pílula branca
PN21h Movimento consome os 8 tokens dos primitivos (PN11): nenhuma duração ou easing solto em transition; prefers-reduced-motion desliga transform e transição do cartão O DS já tinha os tokens desde os primitivos; o que faltava era o consumo. Duração literal em CSS é o caminho por onde a camada de movimento deriva sem ninguém perceber transition: .2s ease (valor solto)

Fronteira desta camada: a camada visual inteira vale no preview e não é canônica. Depois da inversão do hero (abaixo), restou uma decisão de marca em aberto — a tinta da família (S6), que vive como camada alternável pelo botão "Tinta". O tom da âncora (S3) e a régua de ação (S4 retirado) deixaram de ser conflito: a ação voltou para a família turquesa, que é onde o marca-seed.md §3.5 sempre a colocou. Ver PN-P1 em §48.9.

SUPERSEDE DA ÂNCORA — o azul sai, entra o turquesa vivo (2026-08-13, noite)#

Origem, nas palavras do Rafael: "a questão do azul eu tinha aceitado para podermos parar com os testes, pois não estava acertando. Retome as cores da SEED. Aquela caixa no topo que está azul quero que use um turquesa com cor viva, não pode ser escura nem cinza, senão fica muito escuro e parece velório." E a instrução de método que destravou a solução: "veja como o estudo do Viver de IA soluciona isso."

A RESPOSTA ESTAVA NO ESTUDO, e eu não a tinha lido assim. estudo-viver-de-ia.md §9.2 (medição do código da plataforma): --primary: 173 100% 46% = #00EBCF. A primária deles não é o navy dos prints — é um turquesa vivo, a 1,1° de matiz do nosso turquesa-300 e a 0,9° do turquesa-600. O navy é superfície de tema escuro; o acento é turquesa. A §9.2 diz textualmente: "o sistema é superfície quase-preta + acento turquesa — que é, essencialmente, o território do nosso dark mode."

O erro de leitura que isso expôs: nas duas rodadas anteriores eu copiei a superfície deles (escuro) e deixei o nosso turquesa como enfeite. Para uma marca cujo primitivo é turquesa, é o inverso do certo. A inversão: o turquesa é a SUPERFÍCIE de ênfase, e a tinta é escura da própria família. E o "vivo" que o Rafael pedia não depende de escuro — vem de três mecanismos que o próprio §8 do estudo nomeia: P1 (varredura de 4–6 pontos de luminância dentro do próprio matiz), P2 (sombra em rampa + anel) e P4 (uma âncora por página contra branco). Os três funcionam sobre superfície clara.

O diagnóstico era luminância, não matiz — e é isso que explica o "velório":

azul-800 (antes) turquesa-400 (agora)
Luminância 6,03% 33,71% — 5,6× mais claro
Croma 0,380 0,624 — +64%
Tinta que carrega branco 9,52 turquesa-900 4,99

Um hero a 6% de luminância é quase preto, e nenhuma quantidade de croma corrige isso.

Três consequências que a medição obrigou, e nenhuma era previsível de olho:

(a) A luz inverteu de canto. Com tinta branca, a luz apagava o texto e tinha sido empurrada para a metade vazia do hero. Com tinta escura a luz ajuda — 5,75 no ponto de luz contra 4,99 na base —, então ela volta para o canto onde o texto vive. E não existe poço: descer um degrau para turquesa-500 derrubaria a tinta a 4,20 e reprovaria. O turquesa-400 é o piso da varredura, não o meio dela. Véu de turquesa-300 a .35 → ΔL 5,84 pontos, dentro da faixa de 4–6 que o P1 mede, com croma preservado em 0,553.

(b) O anel virou obrigatório. A base contra a página branca mede 2,71 e reprova o piso de fronteira de 3,0. Anel turquesa-600 na mesma declaração da sombra (padrão PN21a) = 4,60.

(c) A tinta é ÚNICA, e a hierarquia deixa de usar cor. Baixar a tinta escura por opacidade reprova: 3,91 a .85 e 2,99 a .70. Dentro do hero há uma tinta só, e a hierarquia vem de corpo e peso — que é literalmente o que o estudo §2.1 diz: "o contraste vem de forma e peso, não de matiz". Por consequência, o itálico do sujeito perdeu a cor: sobre o turquesa vivo nenhuma cor da paleta passa AA (branco 2,71 · amarelo-200 1,90 · turquesa-100 3,36), e a marcação passa a itálico + peso 800 na mesma tinta — mecanismo que, ao contrário da cor, sobrevive ao grayscale. O amarelo sai do hero.

Ganho de estrutura que não estava no pedido: o hero passou a ter UMA face. turquesa-400 mede 6,86 contra a página escura e 5,61 contra o cartão escuro; a tinta segue em 4,99 nos dois temas. Antes, azul-800 media 1,41 contra a página escura e obrigava a subir para azul-700 — duas composições para manter em sincronia. A classe de defeito "camada sem variante de tema" ficou impossível aqui, por construção, e não por disciplina.

A ação dentro do hero desceu um degrau, por decisão do Rafael no gate seguinte ("baixa pra 800"): pílula turquesa-800 #005048 — texto branco 9,37, fronteira 3,45 contra a base e 3,98 contra o ponto de luz. É troca de margem por matiz: o 900 dava 4,99 de fronteira (66% de folga sobre o piso) e o 800 dá 3,45 (15%), e em troca a pílula lê turquesa em vez de quase-preta. O 800 é o piso: turquesa-700 mede 2,33 e reprova. A pílula branca nunca foi opção — 2,71.

48.4 Edição direta · PN22–PN41#

Escrita em 2026-08-13, rodada 3. Esta seção existe porque o painel do ERP não é só leitura: o gestor reagenda uma OS arrastando a barra, reatribui puxando o cartão para outra faixa, muda o estado soltando na coluna. Sem contrato, cada tela implementaria o arrasto de um jeito e o teclado de nenhum.

A regra que unifica cinco lentes é uma só (PN33): ONDE O ITEM CAI É O VALOR DO CAMPO. Coluna do kanban = estado · faixa do tempo e da carga = responsável · dia do calendário = data · posição no gantt = datas. O que é derivado nunca se arrasta (PN22) e o mapa é leitura (PN39).

Norma que governa a seção: WCAG 2.2, SC 2.5.7 Dragging Movements (AA) — toda funcionalidade que use movimento de arrasto tem de oferecer caminho de ponteiro único alternativo. É por isso que o PN23 não é conveniência: é conformidade.

# Decisão Porquê / número medido Descartado
PN22 O DERIVADO NUNCA SE ARRASTA. Carga de trabalho, ranking, KPI e barra de status são resultado de cálculo: não têm campo de destino, e por isso não têm alça Arrastar uma barra de carga significaria "trabalhe menos" — o gesto não tem para onde escrever. Oferecer a alça e ignorar o gesto é a classe do botão morto. Guarda: E-15 mede que nenhuma barra da carga é arrastável Alça em bloco derivado · alça que não faz nada (botão morto)
PN23 CAMINHO DE PONTEIRO ÚNICO PARA TUDO QUE SE ARRASTA. Menu "Mover sem arrastar" aplica a MESMA mutação, com o mesmo desfazer; criar tem botão explícito, sem depender de passar o mouse SC 2.5.7 (AA). Além da norma: em toque real, arrastar dentro de faixa rolável compete com o gesto de rolagem — o caminho alternativo é o que faz a tela funcionar no celular. Guardas: E-22, E-23, I-17, I-19 Arrasto como caminho único (reprova AA) · alternativa que aplica mutação diferente (duas verdades)
PN24 CONTRATO DE TECLADO ÚNICO, IGUAL EM TODA LENTE: Enter/Espaço pega o item e o anúncio ensina o contrato ("setas movem, Enter solta, Esc cancela") · setas anunciam o DESTINO antes de confirmar · Enter solta aplicando a mesma mutação do arrasto · Esc cancela sem aplicar Arrasto sem teclado exclui quem não usa ponteiro. O anúncio que ensina existe porque um contrato de teclado que ninguém descobre é um contrato que não existe. Anunciar o destino antes de confirmar é o que torna o gesto reversível sem custo. Guardas: E-18…E-21 Teclado diferente por lente · mover sem anunciar destino (o usuário confirma no escuro)
PN25 O ÍMÃ É A UNIDADE DA ESCALA: modo hora → passo de 1 hora · dia, semana e mês → passo de 1 dia. Datas inteiras, sem hora residual Precisão maior que a escala é precisão inventada: arrastar num gantt de mês não pode gravar 14h37. Guardas: E-07 (datas inteiras), F-19 (em escala de hora o arrasto move horas) Ímã no pixel · granularidade fixa independente da escala
PN26 A CASCATA DE DEPENDÊNCIA É MOSTRADA EM FANTASMA ANTES DE SOLTAR e DECLARADA EM NÚMERO no anúncio Mover uma ordem que trava outras três muda quatro datas. Se o usuário só descobre depois, o desfazer vira obrigatório em vez de opcional. O número no anúncio é o que dá ao usuário de leitor de tela a mesma informação que o fantasma dá ao olho. Guarda: E-08 Aplicar cascata em silêncio · mostrar cascata só depois de soltar
PN27 MUTAÇÃO OTIMISTA: o gesto aplica na hora, na tela; confirmação e conflito de servidor são produto (fronteira do DM10) Esperar o servidor a cada arrasto torna o gesto inutilizável. O DS entrega o contrato de tela: aplicar, anunciar, oferecer desfazer. Compõe com o PN30 (refresh adiado) Aguardar confirmação para desenhar · aplicar sem desfazer
PN28 DESFAZER DE 8 SEGUNDOS, com contagem visível, cobrindo TAMBÉM criação e exclusão. O anúncio diz o RESULTADO, não o percurso "OS-2288 reagendada para 17/08→19/08" é resultado; "arrastada 3 colunas" é percurso, e percurso não se verifica. O desfazer que não cobre criação é meia verdade — foi o defeito que apareceu ao ligar a linha de criação. Guardas: E-02…E-05, E-29, G-14, G-15, E-41 (o lote em massa volta inteiro) Toast sem contagem (o usuário não sabe quanto tempo tem) · desfazer só de edição · anúncio de percurso
PN29 ITEM TRAVADO NÃO GANHA ALÇA E DIZ O MOTIVO, com aria-disabled e caminho alternativo Alça que existe e não obedece é botão morto. Motivo invisível transforma trava em bug aos olhos de quem usa — foi literalmente o relato do gate (ver PN46). Guarda: E-17b Alça inerte · trava silenciosa
PN30 REFRESH PEDIDO DURANTE EDIÇÃO É ADIADO, NUNCA SILENCIOSO — o item sob edição é imune, e o adiamento é dito Sobrescrever o que o usuário está arrastando é perder trabalho sem aviso. Compõe com o PN9: adiar é decisão de tela, não de transporte. Guarda: E-26 Aplicar refresh sobre item em edição · descartar o refresh sem avisar
PN31 A ALÇA TEM ALVO DE TOQUE PRÓPRIO (≥44px de área efetiva) e a faixa ROLA sob o ponteiro perto das bordas (autoscroll, margem de 48px) Alvo de 44px é regra do DS desde o §1. O autoscroll existe porque reagendar de 15/08 para 30/09 exige sair da janela visível — sem ele, o gesto é impossível sem soltar e rolar. Guarda de origem: o link endereçável da timeline media 14px de altura e só o render pegou Alça de 14px · faixa que não rola durante o arrasto
PN32 ORDENAÇÃO ATIVA DESLIGA O ARRASTO DE REORDENAR, E DIZ POR QUÊ Arrastar para reordenar sob ordenação automática produz um resultado que o próprio sistema desfaz no próximo redesenho. Desligar e explicar é honesto; desligar em silêncio é bug percebido. Guardas: E-24, E-25 (o gesto de fato não muda nada) Permitir os dois (o sistema desfaz o gesto) · desligar sem motivo visível
PN33 ONDE O ITEM CAI É O VALOR DO CAMPO. Coluna do kanban = estado · faixa do tempo e da carga = resp · dia do calendário = data · posição e largura no gantt = inicio/fim É a regra que unifica cinco lentes com um motor de arrasto. Sem ela, cada lente teria semântica própria de soltura e o contrato de teclado do PN24 não poderia ser único. Guardas: E-01, E-11, E-13 Semântica de soltura por lente · zona de soltura sem campo declarado
PN34 CRIAR NO VAZIO, COM O CONTEXTO JÁ PREENCHIDO: nascer na coluna do kanban já traz o estado daquela coluna; clicar num dia vazio do calendário cria naquela data O contexto do gesto é informação; pedir de novo num formulário em branco desperdiça o que o usuário já disse. Guardas: E-28, E-30 Criar sempre em branco · criar sem herdar a zona
PN34b O RESULTADO TEM DE SER VISTO: o que acabou de mudar é realçado por 1.600ms e trazido ao campo de visão (vertical e horizontalmente) Relato literal do gate. Um item criado ou agendado fora do enquadramento faz o usuário concluir que nada aconteceu — e o sistema estava correto. Lição de método permanente: o que nasce fora do campo de visão é lido como "não funcionou". Guardas: I-07, I-08 Aplicar sem realçar · realçar sem rolar até o item
PN35 MOVER ≠ REDIMENSIONAR: punhos nas pontas mudam só a borda arrastada; o gesto mexe em left/width, NUNCA em transform; a borda oposta não se move Defeito do gate: ao esticar, a barra inteira se deslocava. Causa: transform translada o elemento todo. Regra: redimensionamento é geometria de caixa, não transformação. E o anúncio distingue os dois gestos, senão o usuário de leitor de tela não sabe qual aconteceu. Guardas: E-09, E-10, H-06…H-09 (nenhum transform no gesto; a largura cresce durante o gesto) transform no redimensionamento · punho que move a barra · anúncio idêntico para mover e esticar
PN35b A MESMA ETAPA PODE TER VÁRIOS PERÍODOS NA MESMA LINHA. Modelo: segmento -1 é o principal (inicio/fim, lido por calendário, vencimentos e carga) e 0..n são adicionais, que existem só no eixo do tempo. O gesto age sobre o segmento tocado, não sobre a ordem inteira Pergunta do Rafael no gate: "posso trabalhar na mesma linha/etapa em mais de um período?" — e a resposta do domínio é sim: manutenção que para no fim de semana e retoma na segunda é uma etapa, dois períodos. As duas alternativas eram piores: recusar o gesto (o dado real não cabe) ou criar outra ordem (duplica a etapa e mente na contagem). Guardas: J-01…J-08 — o extra se identifica no rótulo ("período 2"), move-se sozinho, e o menu dele oferece remover o período, não excluir a ordem Recusar o segundo período · criar ordem nova (o conjunto cresceria mentindo) · mover o extra arrastando o principal
PN36 DEPENDÊNCIA É LISTA, não campo único. Criada puxando do nó até a barra da predecessora; removida com gesto deliberado (duplo clique ou botão direito); ciclo é recusado e explicado; ligar uma terceira predecessora acrescenta, não substitui Uma ordem pode depender de várias — o campo escalar é o defeito de modelagem que aparece na primeira obra real; o helper aceita o formato antigo para não quebrar dado herdado. Clique simples removia por acidente ao passar sobre o fio: ação destrutiva não mora em alvo invisível que cruza a área. Guardas: E-31…E-33, I-12 (clique simples NÃO remove), I-21…I-24 (duas predecessoras no dado, cada uma com fio próprio) dep escalar · remoção por clique simples · substituir a predecessora ao ligar outra · aceitar ciclo
PN37 A LENTE TABELA É PLANILHA: setas navegam entre células, Enter abre editor na própria célula, e a alça copia o valor para as células abaixo O ERP tem preenchimento repetitivo (mesmo responsável para dez OS). Sem planilha, isso são dez formulários. Herda o §40; a alça é o gesto que o usuário já conhece de fora. Guardas: E-34…E-38 Editar só em formulário · setas que rolam a página em vez de navegar células
PN38 SELEÇÃO E AÇÕES EM MASSA: a barra de ações só aparece com seleção, a ação muda todas de uma vez e o desfazer reverte o lote inteiro Herda o §41 (seleção) e o §42 (toolbar). A barra que existe vazia ocupa espaço e não responde. O desfazer parcial é pior que nenhum: deixa o conjunto num estado que o usuário não pediu. Guardas: E-39…E-41 Barra sempre visível · desfazer item por item depois de uma ação em massa
PN39 O MAPA É LEITURA: o pin NÃO se arrasta. A localização vem do cadastro da usina, não do gesto Mover o pin significaria mudar a coordenada da usina — que é cadastro, não agendamento. Arrastar ali seria editar o cadastro por acidente. Guarda: E-16 Pin arrastável · mapa como editor de cadastro
PN40 FILTRO É DA VISÃO, NÃO DA LENTE: chips removíveis para o que está ativo · contagem honesta ("5 de 14 · 9 ocultas") · vazio que explica que é filtro e diz quantas existem · o mesmo recorte em qualquer lente · limpar devolve o conjunto inteiro Contagem que não diz o que ficou de fora faz o usuário concluir que o dado desapareceu. O recorte igual em toda lente é o PN5 aplicado ao filtro. Defeito medido: criar ou excluir mexe no tamanho do conjunto e a contagem continuava dizendo "14 ordens" — contagem que não acompanha a mutação é contagem que mente. Guardas: F-01…F-07, I-16, I-26 (criar fora do filtro ativo limpa o filtro e diz por quê, em vez de criar no vazio) Filtro por lente · contagem só do que sobrou · vazio genérico ("nada aqui")
PN41 A ESCALA É DENSIDADE, NÃO RECORTE: hora · dia · semana · mês mudam a largura por unidade e a granularidade dos rótulos — o conjunto desenhado é o mesmo. Fins de semana marcados; linha do HOJE única Zoom que troca o intervalo esconde dado; zoom que troca a densidade só aproxima o olho. Guardas: F-08…F-11, F-22, H-01…H-05 (cada escala tem uma coluna por unidade, alinhada à borda da unidade — mês começa no dia 1) Escala que muda o intervalo · escala que só troca o cabeçalho (a mesma classe do PN3b)
PN41b ESCALA DE HORA, com a unidade da faixa parametrizada: 24 unidades por dia no modo hora, 1 nos demais Pedido do gate: a manutenção de campo tem janela ("a equipe entra 8h e sai meio-dia") e sem hora o gantt não responde a pergunta que o cliente faz. O ímã acompanha (PN25) Gantt só em dias (não responde à pergunta do campo) · hora fixa com dia derivado
PN41c A JANELA ABRE NO HOJE, não no início da faixa Faixa rolável de 60 dias abre no dia 1 e o usuário rola até achar hoje — em toda troca de escala. Guarda: F-18 Abrir no início da faixa
PN41d O ZOOM SE DECLARA EM UNIDADES VISÍVEIS, não em pixels: "8 h na tela", "15 d na tela"; o px por unidade é derivado da largura disponível. No fim do curso o botão desabilita, não vira clique morto É a pergunta que o usuário faz de verdade ("quero ver as 8 horas do turno", "quero a semana inteira"). Derivar o px da largura é o que faz o mesmo ajuste funcionar em 1180px e em 390px — px fixo por unidade quebra num dos dois. Guardas: F-21…F-26; o F-25 mede que a quantidade prometida no rótulo é a que de fato cabe na tela Px fixo por unidade · zoom em porcentagem (não responde à pergunta) · botão de zoom que continua clicável no fim do curso
PN41e PANORÂMICA EM MODO EXPLÍCITO: dois botões — Criar × ✋ Mover — mais atalhos que sempre valem (segurar espaço, Shift+arrasto, botão do meio). Só o fundo faz panorâmica; barra, punho e nó continuam sendo edição; limiar de 4px antes de virar panorâmica Dois gestos disputavam o mesmo pixel: arrastar no vazio criava período (PN49) e também deveria deslocar a janela. Em vez de adivinhar a intenção, o modo é explícito, como em ferramenta de desenho — seta cria, mão navega. Lição de método permanente: dois gestos não podem dividir o mesmo pixel. Guardas: F-27…F-29 (a panorâmica não rouba o gesto de edição da barra) Adivinhar a intenção pelo contexto · panorâmica em qualquer ponto (mataria a criação) · modo implícito por tecla apenas (não há tecla no toque)

48.5 Gantt profissional · PN42–PN45 — RESERVADO, produção no bloco 6D#

Estes quatro códigos estão reservados e deliberadamente vazios. Não são omissão: são o escopo do bloco 6D, separado deste por decisão de tamanho — o 6C entrega o gantt que reagenda e encadeia; o 6D entrega o gantt que planeja.

# Escopo reservado
PN42 Os quatro tipos de dependência (FS, SS, FF, SF) mais folga/lag — hoje o preview implementa apenas fim→início
PN43 Caminho crítico e folga calculada
PN44 Linha de base (baseline) e comparação plano × realizado
PN45 Marco como losango sem duração

Por que reservar em vez de renumerar: as cinco camadas de validação e o preview v0.6 já citam PN46PN54 nos comentários e nas guardas. Renumerar para fechar o vão exigiria reescrever os quatro artefatos executáveis e invalidaria as âncoras MD5 recém-calculadas, trocando um vão documentado por um risco de deriva. O vão é intencional e está declarado aqui.

48.6 Contratos que o gate de 2026-08-13 produziu · PN46–PN50#

Estas cinco decisões não estavam previstas: nasceram do gate visual do Rafael sobre o preview em uso real. Cada uma tem o relato de origem registrado, porque o relato é a evidência.

# Decisão Porquê / relato de origem Descartado
PN46 A POLÍTICA DE TRAVA É DO DOMÍNIO, NÃO DO DESIGN SYSTEM. O DS entrega o contrato (travado + motivo visível + aria-disabled + caminho alternativo); a regra vem de fora, como parâmetro Relato: "a coluna Concluída não move" — lido como defeito. E era, no sentido que importa: "concluída não move" estava soldado no DS, que passou a legislar sobre workflow. Uma empresa quer travar concluídas; outra quer reabrir. O preview expõe as duas políticas (botão "Travar concluídas") para exercer os dois lados — é assim que se prova que o contrato funciona ligado E desligado. Guardas: E-17a (sem política, nada trava — o DS não inventa regra de negócio), E-17b (com política, o travado não ganha alça e diz o motivo) Regra de workflow dentro do DS · política sem os dois lados testados (metade do contrato sem prova)
PN47 PRÉ-REQUISITO DE LENTE PASSA DE "TODOS TÊM O DADO" PARA "ALGUÉM TEM O DADO", e quem não tem vai para a GAVETA DE NÃO AGENDADOS — visível, contada e arrastável de volta para o tempo. No gantt, toda ordem tem LINHA; o que falta é a barra Relato: uma única ordem sem data derrubava o gantt inteiro — a lente sumia e o usuário não sabia por quê. A gaveta resolve os dois lados: a lente aparece e desenha quem tem, e o que falta fica à vista em vez de escondido. Dar linha a quem não tem data é o que permite agendar arrastando na própria linha (ver S8). Guardas: F-12…F-14, I-01, I-02 Exigir o dado em todos (o defeito) · esconder o não agendado (o usuário conclui que não existe) · lente vazia
PN48 RÉGUA DE TEMPO EM DOIS NÍVEIS — contexto em cima, unidade embaixo — com UMA COLUNA POR UNIDADE, alinhada à linha de grade Relato/medida: a régua antiga espalhava N rótulos por interpolação; em escala de hora dava para ver "12/08 19h" a cada 6 horas e nenhuma coluna dizia que hora era. Régua e faixa deixaram de medir o mesmo. Dois níveis é o que permite ler a coluna em vez de adivinhar: hora↔dia · dia↔mês/ano · semana↔mês · mês↔trimestre. Guardas: G-01…G-05, H-01…H-03 Rótulos por interpolação · um nível só · régua com granularidade diferente da faixa
PN49 CRIAR PERÍODO ARRASTANDO NA FAIXA VAZIA, com semente visível seguindo o ponteiro, rascunho desenhado durante o arrasto, e clique sem arrastar também criando. O convite aparece onde o gesto age e promete exatamente o que vai acontecer Relato: a linha vazia não convidava a nada, e onde convidava, o gesto não agia. Lição de método permanente: convite que aparece onde o gesto não age é pior que convite nenhum. A dica é específica por linha: linha sem data promete agendar; linha já agendada promete outro período (PN35b); a linha de criação no fim do gantt promete criar. Guardas: G-06…G-10, I-09…I-11, I-13…I-18 (a faixa da linha de criação é marcada sempre, não só no hover) Faixa vazia sem convite · convite genérico igual em toda linha · convite em zona inerte
PN50 MENU DE CONTEXTO (botão direito) EM TODO ITEM + CARTÃO DE EDIÇÃO por clique, com o menu levando o foco para a primeira ação e oferecendo abrir, duplicar, desagendar, concluir e excluir — excluir reversível, e dizendo isso Arrasto cobre data, responsável e estado; o resto precisa de porta. O menu é o mesmo em todas as lentes, senão o usuário reaprende a cada troca. O cartão de edição aqui é a versão mínima e é o gancho declarado do §49 (painel de detalhe): existe para exercer o contrato, não para especificá-lo. Guardas: G-11…G-17 Menu diferente por lente · menu que abre sem levar o foco (teclado fica órfão) · excluir irreversível

48.7 Lente mapa · PN51–PN54#

A lente mapa tem duas perguntas diferentes por trás, e é isso que produz os dois modos: "onde estão as ordens?" (leitura, laudo, impresso) e "como eu chego lá?" (navegação em campo). Guia de ligação para o produto em mapa-base-lovable.md (a âncora deste guia MUDOU DE CAMINHO em 2026-08-13 — era validacao/, passou à raiz: a pasta validacao/ é declarada no LEIA-ME.md dela como a dos artefatos EXECUTÁVEIS, e este é documentação de entrega ao produto; ver §19-b do MANIFESTO v4.0). Recomendação aprovada pelo Rafael: MapLibre GL + provedor de tiles; Google apenas para geocodificação, se necessário.

# Decisão Porquê / número medido Descartado
PN51 DOIS MODOS DECLARADOS: cobertura (PADRÃO) e operacao. Cobertura é a malha IBGE do §4d — projetada, imprimível, sem rede. Operação é mapa base navegável com tiles O modo é do contexto, não da preferência: laudo, e-mail e papel são cobertura; ir até a usina é operação. Cobertura é padrão porque é a que sempre funciona e porque é a que entra em documento. Guardas: M-01, M-02 Um modo só (nenhum dos dois serve os dois casos) · operação como padrão (quebra em papel e sem rede)
PN52 O ESTILO DO MAPA BASE CONSOME TOKENS, e o MARCADOR É PEÇA DO DS — a mesma forma no mapa base e na lista textual Mapa com a paleta do fornecedor é o único lugar da tela onde a identidade não se aplica, e é justo o que o cliente fotografa. Marcador próprio garante que o mesmo símbolo apareça nos dois modos e na alternativa textual. Guardas: M-09, M-10 (cada usina carrega coordenada geográfica além da posição projetada) Estilo default do fornecedor · marcador do fornecedor · símbolo diferente entre mapa e lista
PN53 FALLBACK OBRIGATÓRIO E DECLARADO: sem biblioteca, sem chave, sem rede ou em impressão, o modo operação cai para a cobertura e DIZ o que aconteceu. Nenhuma chave de API vive no artefato — a fonte de tiles vem da configuração do ambiente Equipe em campo com sinal ruim é o caso normal, não a exceção. O mapa não fica em branco nem mente: cai para a cobertura oficial e anuncia. A chave fora do artefato é requisito de segurança e de LGPD, não conveniência. Guardas: M-04, M-05 (o fallback diz o que aconteceu, em vez de mostrar retângulo vazio), M-07, M-08 Retângulo vazio · fallback silencioso · chave embutida no HTML
PN54 ALTERNATIVA TEXTUAL SEMPRE PRESENTE, NOS DOIS MODOS, COMO CONTEÚDO — lista de usinas com marcador, nome, município/UF e contagem de OS ativas; nunca só aria-label Mapa interativo não é navegável por leitor de tela — e a lista não é enfeite de acessibilidade: é a forma mais rápida de ler o mesmo dado para qualquer pessoa, e a única que sobrevive ao copiar-e-colar num e-mail. Guardas: M-03, M-06 aria-label descrevendo o mapa · alternativa só no modo cobertura · alternativa escondida atrás de botão

48.8 Supersedes formais desta rodada#

Regra do projeto: todo contrato substituído recebe supersede formal, com o número que o motivou — nunca nota solta. Estes dez são os desta rodada.

# O que dizia O que passa a dizer Número que motivou
S1 PN3, 1ª redação: cinco lentes (lista · tabela · quadro · linha do tempo · calendário) Oito lentes, com gantt e tempo separados e carga e mapa acrescentados A documentação do ClickUp separa gantt (tarefas conectadas) de timeline (recurso), e o Rafael decidiu que o template estrutural nasce com o lugar da alocação de pessoa
S2 O rótulo "quadro" como nome da lente kanban kanban é o identificador canônico; "quadro" fica como sinônimo de fala, não de código Os artefatos usam data-lente="kanban"; manter dois nomes para a mesma lente é a porta da deriva
S3 (revisto na mesma data — ver S11-b abaixo) Tom do escuro estrutural: cinza-900 (croma 0,063) azul-800 #004C61 SUPERSEDIDO NA MESMA NOITE por turquesa-400 #11B0A0 (croma 0,624, L 33,71%) — a âncora deixou de ser escura. O texto abaixo preserva a cadeia de medições que levou ao azul, porque ela documenta quatro tentativas reprovadas que não devem ser repetidas: ~~azul-800 #004C61 (croma 0,380, L 6,03%), com luz em azul-700 #006783 e poço em azul-900 #003241. No tema escuro o hero SOBE para azul-700 Cadeia de quatro medições no mesmo gate: (a) o hero SEED já era quase 3× mais claro que o do estudo (L 9,12% × 3,25%) e lia mais morto, porque tinha metade do croma (0,087 × 0,175) — escuro dessaturado lê como fumaça, escuro saturado lê como tinta; (b) turquesa-900 #00352F (croma 0,208) resolveu o croma sem navy alheio, branco em 13,55; (c) "precisamos usar uma cor mais clara"turquesa-800 (L 6,21%, croma 0,314), teto da luz em turquesa-600 porque acima disso (500 = 3,22) o branco reprovaria; (d) "essa cor é muito escura e deixa tudo preto e branco; o deles usa um azul" → a escala turquesa esbarra num teto (no 700 o micro-rótulo cai a 4,11), e o azul-800 entrega 21% mais croma na mesma luminância. No tema escuro, azul-800 media 1,41 contra a página e 1,24 contra o cartão — deixava de ancorar; azul-700 dá 2,65 e 2,19 com branco em 6,43, e azul-600 separaria melhor (3,69) mas derrubaria o branco a 4,61. Regra que fica: no escuro, elevação é LUZ, não sombra
S4RETIRADO em 2026-08-13, na mesma data em que nasceu marca-seed.md §3.5: "ação primária é sempre família turquesa/verde — nunca azul"e esta regra PERMANECE VIGENTE, intocada O supersede foi RETIRADO. Com a âncora virando turquesa vivo (S11), a ação voltou à família da marca e não há mais nada a superseder em marca-seed.md: a régua vigente é turquesa-600 #098475 + branco = 4,60, e o #098475 é o hex que o próprio §3.5 nomeia. O texto abaixo fica como registro histórico da tentativa, não como contrato: ~~A ação não tem cor própria: ela usa o ESCURO ESTRUTURAL. Primária sobre claro = azul-800 + branco (9,52) · primária dentro do escuro = pílula branca + azul-900 (13,70) · secundária no escuro = ghost translúcido + branco (9,52) · secundária sobre claro = outline azul-800 (9,52) · terciária = link com seta (4,60). No tema escuro a ação SOBE na escala: azul-400 (superfície 5,71 · texto azul-900 5,56). O turquesa continua no sistema como ACENTO — itálico do sujeito, séries de dataviz, pontos de estado —, o que muda é que ele deixa de carregar a ação Gate do Rafael com o print da plataforma: "a cor do botão tem que seguir o mesmo padrão da caixa grande". Tentativa reprovada pela medição, registrada para não se repetir: baixar a primária do escuro para azul-500 deixaria o botão menos "neon" (superfície 4,24) mas o texto cai para 4,13, abaixo do piso de 4,5 — e nenhum tom escuro da paleta salva (azul-900 4,13 · cinza-900 4,17 · turquesa-900 4,08 · branco por cima 3,32). azul-400 é o degrau mais baixo que sustenta texto pequeno no escuro. ⚠ Este supersede vale no PRODUTO DIGITAL e é decisão de MARCA: exige registro do Rafael em marca-seed.md antes de ser canônico
S5 "Ordem concluída não move" como regra do DS PN46: o DS entrega o contrato de trava; a política é parâmetro do domínio Relato do gate "a coluna Concluída não move" leu como defeito uma regra de workflow embutida no DS
S6 Tinta neutra: text-primary e text-secondary na família cinza PROPOSTA, não canônico: tinta da família do escuro estrutural — text-primary #0B3330 (× branco 13,72) · text-secondary #2F5A54 (× branco 7,75) · bordas e superfícies esverdeadas; no escuro #DDECE8 / #A3C4BE / #0F1F1C / #16302C O estudo (§2.1) usa tinta da mesma família do escuro estrutural, medida em croma 0,192 e saturação 83% contra 0,063 e 31% da nossa. Como texto e bordas ocupam a maior parte da área, tinta neutra faz a tela inteira ler cinza mesmo com o hero colorido — foi o que o gate apontou. Vive como camada alternável no preview (botão "Tinta"), não no canônico: muda o DS inteiro (ERP, e-mail, documentos, site) e depende de decisão de marca. Defeito medido a registrar: a 1ª versão desta camada foi escrita sem variante de tema e no escuro impunha tinta clara sobre cartão escuro — o painel virava ilegível. Regra permanente: toda sobrescrita de token semântico precisa das DUAS faces; um bloco só é meia implementação
S7 Largura da faixa de tempo em px fixo por dia PN41d: a faixa é função da escala e da largura disponível — declara-se quantas unidades devem caber, e o px por unidade é derivado O mesmo ajuste tinha de funcionar em 1180px e em 390px; px fixo por unidade quebra num dos dois. Guarda F-25 mede que a quantidade prometida no rótulo é a que cabe de fato
S8 PN4: o gantt exige início E fim em todos os itens PN47/PN4-03e: o gantt exige ao menos uma ordem agendada, dá LINHA a quem não tem data (a barra é que não existe) e manda o resto para a gaveta Uma única ordem sem data derrubava a lente inteira. Consequência de projeto, não só de tolerância: dar linha a quem não tem data é o que torna possível agendar arrastando na própria linha (PN49)
S9 Gesto em linha já ocupada = recusar, ou criar ordem nova PN35b/I-06: gesto em linha ocupada acrescenta um PERÍODO à mesma etapa, sem criar ordem Pergunta do Rafael ("posso trabalhar na mesma linha/etapa em mais de um período?") contra o dado real da manutenção que para e retoma. Guarda J-05 mede que o conjunto não cresce (14 → 14)
S11 Âncora ESCURA — a caixa de ênfase da página é escura, com texto claro; a ação usa o escuro estrutural (S3+S4) ÂNCORA CROMÁTICA: turquesa-400 #11B0A0 com tinta única turquesa-900, anel turquesa-600 e véu de luz turquesa-300 a .35 no canto do texto (ΔL 5,84). A ação volta à família: primária sobre claro turquesa-600 + branco 4,60 · dentro do hero pílula turquesa-800 + branco 9,37 (fronteira 3,45) · secundária sobre claro outline turquesa-700 6,32 · no tema escuro a ação sobe para turquesa-400 + turquesa-900 (superfície 5,61 · texto 4,99) "Parece velório" + a instrução de olhar o estudo. §9.2 do estudo-viver-de-ia.md: a primária da plataforma é #00EBCF, turquesa vivo a 1,1° do nosso turquesa-300 — o navy é superfície, o acento é turquesa. Nós vínhamos copiando a superfície e deixando o turquesa como enfeite. Diagnóstico medido: luminância, não matiz — 6,03% × 33,71%. Reprovações que fecham as alternativas: turquesa-500 derruba a tinta a 4,20 · pílula branca 2,71 · turquesa-700 como pílula 2,33 · itálico em qualquer cor da paleta reprova (branco 2,71 · amarelo-200 1,90 · turquesa-100 3,36)
S11-b PN21c: "UM hero ESCURO por página"; e a face escura do hero como composição própria UMA ÂNCORA DE ÊNFASE por página — a palavra "escuro" sai do contrato, porque era descrição de implementação, não invariante. E a face escura desaparece: o mesmo par serve os dois temas O tom mudou cinco vezes em dois dias (cinza-900turquesa-900turquesa-800azul-800turquesa-400), sempre com a guarda exigindo o valor da vez. O invariante real nunca foi a escuridão: é uma âncora cromática da marca por página. Ganho medido: turquesa-400 mede 6,86 × página escura e 5,61 × cartão escuro, contra 1,41 do azul-800duas composições viraram uma, e a classe "camada sem variante de tema" ficou impossível aqui por construção
S12 Fronteira de 3,0 exigida de toda pílula que pousa no hero COMPONENTE × CONTEÚDO. A fronteira de 3,0 (SC 1.4.11) é exigida de pílula interativa (btn-solucao, btn-ghost-hero); o chip de status é ISENTO, com o motivo escrito no CSS Erro meu de enquadramento, corrigido no gate: eu havia reprovado o chip branco a 2,71 pelo mesmo critério da ação. O 1.4.11 alcança "informação visual necessária para IDENTIFICAR COMPONENTES e seus estados" — o chip é conteúdo (rótulo de status, não focável, não acionável), e dele a norma exige o texto pelo 1.4.3, medido em 6,58. E o estudo §2.4 fecha por outro lado: "hierarquia por PREENCHIMENTO, não por forma" — todo anel que passasse dos dois lados obrigaria a escurecer o chip, e o turquesa-900 (4,99) ficaria mais escuro que o próprio botão (800), invertendo a hierarquia; o anel semântico, que seria o correto, reprova (feedback-danger-text 2,65). Guardas: PN21-05e (pílula interativa só com fill medido), PN21-05f (o chip não é interativo — é o que sustenta a isenção; no dia em que virar botão, a suíte reprova), PN21-05g (a isenção está declarada no artefato, não só aqui)
S10 PN21g, 1ª redação: glow do acento turquesa em elemento de destaque de ação Sombra projetada no destaque de ação; o glow sai. A proibição que importa — glow jamais sobre superfície de dado — permanece Com a ação virando pílula branca sobre o escuro (S4), o glow turquesa perdeu o portador: brilho de acento atrás de botão branco vira halo sujo

48.9 Fronteiras declaradas e pendências#

Fora deste bloco, e a maior parte por decisão explícita do Rafael: quais blocos existem em cada tela e como o workflow se organiza (projeto do ERP) · a estrutura de setores e áreas — Admin, Engenharia, Comercial, Qualidade — declarada pelo Rafael como desenho do ERP · qual perfil vê o quê (regra de negócio) · quais KPIs cada tela mostra, o que entra na timeline e como o progresso é calculado (projeto do ERP) · cálculo e agregação de indicador (produto) · transporte de tempo real, websocket, persistência e resolução de conflito de edição concorrente (produto — Lovable/Supabase, fronteira do DM10) · BI exploratório ad-hoc (fora de escopo desde o DF6, revisável) · geocodificação de endereço e contratação de provedor de tiles (produto).

Ainda sem dono declarado, e não entram por decisão: os itens 🟡 do censo do estudo-viver-de-ia.mdcomparação com referência fora de gráfico (semântica órfã desde o supersede do DM8), agenda/sessão/vaga, hero de identidade + stats e documento-cerimônia. Entrar sem consumidor real repetiria o erro que o §7 daquele estudo mandou evitar.

PENDÊNCIAS ABERTAS, nomeadas para não se perderem:

# Pendência Bloqueia
PN-P1 (encolheu em 2026-08-13) Restou UMA decisão de marca e meia. A S6 (tinta da família — text-primary #0B3330 × branco 13,72, text-secondary #2F5A54 × branco 7,75, bordas e superfícies esverdeadas) segue proposta, como camada alternável pelo botão "Tinta". A S11 (âncora turquesa-400 + régua de ação na família) precisa de registro em marca-seed.md e nos gêmeos de token v1.8 → v1.9, mas não precisa de decisão — ela é convergente com o §3.5 vigente, e por isso é a meia. O S4 saiu da pendência: não há conflito de marca a resolver A S6 bloqueia o canônico inteiro (ERP, e-mail, documentos, site) porque muda tinta, bordas e superfícies. A S11 bloqueia só a propagação dos 40 HTML (PN-P7). Segue sendo o próximo bloco
PN-P2 PN42–PN45 (dependências FS/SS/FF/SF com lag · caminho crítico · linha de base · marco) FECHADA em 2026-08-16 pelo §58 (GT1–GT16). Os quatro números seguem reservados e vazios nesta numeração de propósito — renumerar invalidaria âncoras MD5 de cinco artefatos executáveis que já citam PN46–PN54. O conteúdo vive no §58, e a fronteira está declarada lá: o §48 tem a lente temporal (PN47/PN48/PN49), o §58 acrescenta a camada de RELAÇÃO entre itens
PN-P3 §49 Painel de detalhe — modal, gaveta lateral e página cheia, servindo o sistema inteiro. Hoje existe só a versão mínima do PN50 Bloco próprio
PN-P4 MAPA NACIONAL. A malha canônica cobre só MG/ES/BA (1.348 municípios, IBGE 2025, Albers). Um espelho de terceiro com as 27 UFs foi testado (raw.githubusercontent, giuliano-macedo/geodata-br-states) e não declara edição. Duas saídas: usar com a pendência registrada, ou o Rafael fornece o shapefile oficial. Falta também a regra de escala (município → UF → país) Atendimento fora de MG/ES/BA na lente mapa
PN-P5 SH-P1, aberta desde o 6A: nenhum token de borda semântico passava 3:1 contra surface-raised no tema claro. ✅ FECHADA em 2026-08-14 (gêmeos v1.12): nasce --seed-border-interactive (cinza-500 promovido, ≥3,0 nas seis superfícies dos dois temas); o shell consome desde o preview v0.3. Registro completo no §46.2 e no seed-tokens.md v1.12 §3 — (fechada; resta só o retroativo do artefato do §47, classe PN-P7)
PN-P6 Três medidas de composição da camada visual sem guarda automatizada: massa escura ≤32% da primeira dobra · hero ≤35% da altura · página lê branca (luminância média ≥248). São decisões declaradas e medidas por inspeção, não por script — nenhuma das cinco camadas as verifica hoje Nada; é dívida de instrumento. Candidata natural a uma sexta camada de composição
PN-P7 Retroativos nos outros 40 HTML do repositório (camada visual e régua de ação) Chat separado, a pedido do Rafael

48.10 Os testes#

  1. Nenhum par de cor novo — o painel compõe peças já medidas; a medição do bloco confere os pares em uso, e a lição do §46 e do §47 vale: compor pares medidos separadamente não garante que a combinação passe.
  2. Escada de mecanismos: lente ativa por aria-current + marca visual + peso, nunca só cor; variação de KPI por seta (sinal) + cor (juízo), nunca cor sozinha (PN14b).
  3. Grayscale: lente ativa, estado de frescor e severidade permanecem legíveis (DF4).
  4. Regra do um: uma lente ativa por bloco · uma pergunta por bloco · UMA live region no painel inteiro (PN9/§1.13) · um itálico por título-insight (PN21e) · um hero escuro por página (PN21c).
  5. Landmarks e nomes: todo bloco é section nomeada; nenhum nome repetido no painel.
  6. Frescor e norma: todo bloco que se atualiza declara última leitura e estado; existe controle de frequência com opção manual (PN8, WCAG 2.2.2 nível A); atualização não move foco nem anuncia sem pedido (PN9).
  7. Escopo do cliente: no modo cliente, nenhum identificador de terceiro aparece — nem em agregado. Teste documental além de visual, como o de procedência do §47.
  8. Blocos de dado: nenhum KPI sem período e base de comparação (PN14) · barra de ranking com base proporcional declarada e valor numérico à vista (PN15) · nada semântico dentro do progressbar e aria-valuenow omitido quando indeterminado (PN18) · toda entrada de timeline endereçável (PN20).
  9. Edição direta: todo gesto de arrasto tem caminho de ponteiro único (PN23, SC 2.5.7) · contrato de teclado idêntico em toda lente (PN24) · nada derivado tem alça (PN22) · ímã na unidade da escala (PN25) · nenhum transform no redimensionamento (PN35) · desfazer de 8s cobrindo criação e exclusão (PN28) · o item mutado é realçado e trazido à vista (PN34b).
  10. Contagem honesta: a contagem acompanha filtro e mutação — criar ou excluir muda o total exibido no mesmo instante (PN40).
  11. Mapa: dois modos declarados, cobertura como padrão (PN51) · estilo por token e marcador do DS (PN52) · fallback que diz o que aconteceu e nenhuma chave no artefato (PN53) · alternativa textual como conteúdo nos dois modos (PN54).
  12. Convite e gesto no mesmo lugar: toda zona que convida a um gesto executa aquele gesto, e a promessa da dica é a ação que acontece (PN49) · dois gestos nunca dividem o mesmo pixel (PN41e) · ação destrutiva não mora em alvo invisível que cruza a área (PN36).

48.11 Estado da validação#

(Estado: estável. Gate visual do Rafael APROVADO em 2026-08-13 sobre o preview v0.6 — o quarto e último gate do bloco, depois das quatro camadas automatizadas. Com ele o bloco 6C FECHA e a Fase 6 vai a 3/3 blocos concluídos (6A §46 Shell · 6B §47 Página institucional · 6C §48 Painel). A promoção é dos contratos PN1–PN54; as decisões de marca S3, S4 e S6 seguem proposta — ver a ressalva no cabeçalho do §48 e a pendência PN-P1 em §48.9. Este bloco é o primeiro do projeto validado em cinco camadas, e a quinta (render-contraste.mjs) nasceu de um defeito que as outras quatro não podiam ver.)

Rodada 1 — 2026-08-12, preview v0.1 → v0.2. suite-painel.mjs 67 PASS · 0 FAILprova de que as guardas são reais: contra um preview com 5 defeitos deliberados (lente desabilitada em vez de oculta, frequência sem opção manual, pílula pintada pelo sinal, aria-valuemin ausente, base proporcional não declarada) ela reprova em 6; render-painel.mjs 17 PASS · 0 FAIL; contraste-painel.py 44 PASS · 0 FAIL após as correções que a própria medição exigiu. O gate visual reprovou o v0.1 e produziu o PN3b — as lentes "linha do tempo" e "calendário" desenhavam listas. Preview a v0.2, suíte a 75, render a 25; contra a versão em que as duas lentes voltam a ser lista, a suíte reprova em 6.

Rodada 2 — 2026-08-13, preview v0.3. Oito lentes (S1) e camada visual (PN21). suite-painel.mjs v2 107 PASS · 0 FAIL; prova: contra um preview com 4 defeitos plantados — gantt sem dependências, véu a 35%, dois heros, transition com duração solta — ela reprova exatamente nos 4. render-painel.mjs v2 37 PASS · 0 FAIL, medindo em Chrome real 3 conectores de dependência com extensão em pixels, 5 faixas de responsável com sub-linhas de sobreposição em tops distintos, marca de capacidade no mesmo x, excedente com largura real, contorno do mapa com bbox medido e 5 pontos dentro do viewBox. contraste-painel.py v2 70 PASS · 0 FAIL, incluindo os pares do hero escuro (branco × cinza-900 = 13,86; itálico turquesa-300 = 7,57; botão-solução = 5,11).

Rodada 3 — 2026-08-13, preview v0.5 → v0.6. Cinco camadas, todas verdes, todas re-executadas contra o artefato final:

Camada Ferramenta Placar O que ela mede
suite-painel.mjs v4 Node + jsdom 133 PASS · 0 FAIL Estrutura, ARIA, tokens, classes fantasma, referências fantasma, higiene
render-painel.mjs v3 Chrome headless 37 PASS · 0 FAIL Colapso da grade em 3 larguras, geometria das oito lentes, frescor vivo
render-edicao.mjs v1 Chrome headless 142 PASS · 0 FAIL Os 33 contratos de edição direta, filtro, escala, mapa e criação — gesto por gesto, com pixels
render-contraste.mjs v1 Chrome headless 6 PASS · 0 FAIL Contraste de composição renderizada nos dois temas (368 textos medidos), não de par de token
contraste-painel.py v2 Python 70 PASS · 0 FAIL Pares declarados, calculados fora do navegador

Camada nova desta rodada, e a razão dela: o render-contraste.mjs nasceu de uma lição medida — contraste de TOKEN não substitui contraste de COMPOSIÇÃO. Os pares do contraste-painel.py estavam todos verdes enquanto, no tema escuro, a ação primária em azul-800 media 1,48 de superfície contra o cartão (sumia) e a secundária tinha texto azul-800 sobre cartão escuro (1,48 — ilegível). Nenhum par de token estava errado; a combinação estava. A camada varre todo texto sobre superfície opaca nos dois temas e reporta o pior caso: hoje 5,56 em button.

Rodada 4 — 2026-08-13 (noite), preview v0.6 → v0.7 → v0.8. A inversão do hero. Cinco camadas re-executadas contra o v0.8 final: suite-painel.mjs 139 PASS · 0 FAIL (era 133; entram NV-01, PN21-05c/05d/05e/05f/05g) · render-painel.mjs 37 · 0 · render-edicao.mjs 142 · 0 · render-contraste.mjs 6 · 0 · contraste-painel.py 78 · 0 (era 70; entram PC-H5b, PC-H6PC-H11). Nenhum artefato canônico foi tocado — cinco arquivos de preview e validação, e nenhum dos 40 HTML do repositório.

DEFEITO REAL DA RODADA, e quem o pegou foi a quinta camada: TOKEN FANTASMA. Na primeira execução após a inversão, o render-contraste.mjs reprovou 15 textos com razão 1,0–1,26, incluindo branco sobre branco. Causa: o molde passou a consumir turquesa-400/600/700 e a lista PRIMITIVOS do gen-painel.py ainda era a da era do azul — e variável CSS indefinida é valor inválido, então o fundo simplesmente não pintou. É a classe NV-01 do §47 (nascida quando a command palette abriu transparente), que tinha detector na suíte de navegação e faltava nesta. Corrigido na causa e detector criado aqui: NV-01 — todo var(--seed-*) consumido tem definição no bloco de tokens. Pega na suíte jsdom, antes de qualquer render. Lição de método: guarda genérica criada num bloco não se propaga sozinha para os outros — quando uma classe de defeito ganha detector, o detector precisa ser levado para toda suíte que possa sofrer dela.

Errata E-6C-01 (2026-08-13, achado na conferência de ambiente): título e selo declaravam versões diferentes. O <title> do preview dizia "preview v0.3" e o selo na tela dizia "preview v0.5" — duas declarações de versão no mesmo arquivo, discordando. A guarda HIG-03 passava verde porque media apenas presença de versão no título. O defeito importa porque o título da aba é o primeiro sinal de arquivo fresco — três relatos de "não funciona" nesta sessão foram cache, e um título que mente cega justamente o instrumento usado para detectar cache. Correção estrutural, não sintomática: a versão passa a ter fonte única (constante VERSAO_PREVIEW no gen-painel.py), consumida por dois marcadores do molde (@@VERSAO_TITULO@@ no título, @@VERSAO@@ no selo, este com data e hora) — divergir deixa de ser possível por construção. Preview bumpado a v0.6 porque o artefato corrigido tem de ser distinguível do defeituoso na tela. Guarda de regressão permanente, creditada ao Rafael: HIG-06 — título e selo declaram a MESMA versão; contra um artefato com a versão do título plantada de volta em v0.3, a suíte reprova em exatamente 1, e a mensagem nomeia os dois valores.

Nota de determinismo, para quem for reconferir hashes: o selo carimba data e hora de geração, então regenerar o preview produz um MD5 diferente sem nenhuma mudança de comportamento — conferido: a cadeia malha-projetada.json → gen-mapa-lente.py → mapa-lente.json → gen-painel.py + painel-template.html + seed-tokens.css → seed-painel-preview.html reproduz 209.288 bytes e 3.730 linhas idênticas, com um único diff: a linha do selo. Para o preview, a âncora é o arquivo entregue, não a receita.

Registro de defeitos e lições — rodada 2 (2026-08-13). Todos viraram guarda ou nota de método. (1) RDP em anel fechado come o fecho. A simplificação Ramer–Douglas–Peucker do contorno externo do mapa, aplicada ao anel inteiro, ancorava o primeiro e o último vértice no mesmo ponto e degenerava o fecho. Conserto no gen-mapa-lente.py v2.2: dividir o anel em duas metades, simplificar cada uma e recosturar. Classe: algoritmo de polilinha aplicado a polígono sem tratar a topologia. (2) Token fantasma evitado antes do build. O hero usou --seed-tq-300, que não existe — o primitivo é --seed-turquesa-300. Pego por conferência de nome contra o CSS canônico antes da geração (classe NV-01 do §47: nome de token digitado de memória). (3) Instrumento calibrado no dado quebra com o dado. O R4-02 do render v1 exigia a menor barra do ranking entre 30% e 50% — faixa que descrevia os dados ilustrativos antigos, não a propriedade. Com dados novos (58/486 = 11,9%) reprovou sem haver defeito. Recalibrado para a propriedade: largura renderizada ≈ razão do próprio dado. Mesma família da lição do 6A (instrumento errado invalida a medição), variante temporal. (4) Dado de exercício precisa conter o caso que a guarda mede. O R7-03 (sub-linhas de sobreposição) reprovava porque nenhuma pessoa tinha duas OS sobrepostas — o mecanismo existia e a suíte estrutural passava, mas a geometria não tinha o que mostrar. A OS-2301 foi movida para colidir com a OS-2296 do mesmo responsável. Regra: todo comportamento especificado precisa de pelo menos um exemplar no dado ilustrativo. (5) --single-process do pacote @sparticuz/chromium trava element-screenshot. Os args padrão do pacote (feitos para Lambda) deadlockam o Chrome em element.screenshot() sequencial. Args enxutos (--no-sandbox) resolvem. Nota de ambiente para qualquer captura futura.

Duas correções que a medição impôs, e as duas são reaproveitáveis fora deste bloco. (1) A regra do dourado vale fora do dataviz. O ponto de "dado antigo" do frescor usava feedback-warning-solid (#F9B11C) e media 1,85 contra o cartão no tema claro. A regra do dourado da §3c veta o dourado como fill solitário — e um ponto de 8px é exatamente isso. Passou a consumir feedback-warning-text: 9,84 no claro, 10,09 no escuro. A regra nasceu no dataviz e se aplica a qualquer preenchimento pequeno e isolado. (2) Barra fina de dado consome a variante -stroke. O preenchimento cheio chart-cat-1 media 2,97 contra o trilho no claro — reprovava 3:1 por 0,03. A variante chart-cat-1-stroke existe exatamente para "linha fina e marcador pequeno, o pior caso do 1.4.11" e mede 4,23. Vale para a barra do ranking (PN15) e para a de progresso (PN17).

A correção do PN3b expôs um segundo defeito, de raiz diferente. Ao desenhar o calendário de verdade, os eventos caíram um dia antes e a grade saiu com sete larguras diferentes. Causa: new Date("2026-08-12") interpreta a string como UTC e desloca no fuso do navegador. A primeira tentativa de conserto foi somar um dia — remendo sobre o sintoma, que teria quebrado em qualquer fuso diferente. O conserto real é decompor a data em números e fazer a aritmética em UTC puro; e as colunas exigem minmax(0, 1fr), senão o conteúdo do dia empurra a coluna. Classe de defeito: remendo sobre sintoma em vez de causa — sobrevive ao teste na máquina de quem escreveu e falha na de quem usa.

E um defeito de alvo de toque que só o render pegou: o link de cada evento da timeline — justamente o que torna a entrada endereçável (PN20) — media 14px de altura. Alvo de toque ≥44px vale para ele como para qualquer controle.

LIÇÕES MEDIDAS NA RODADA 3 (2026-08-13). Catorze, e nenhuma é sobre o componente — todas são sobre COMO se descobre que algo está errado. Registradas aqui porque valem para qualquer bloco futuro. (1) Instrumento calibrado no dado quebra com o dado — faixa fixa em vez de propriedade (reincidência da lição 3 da rodada 2; virou regra). (2) Feições que se tocam, simplificadas em separado, deixam de se tocar. No mapa, a simplificação independente de cada fronteira abria vão entre municípios vizinhos. Conserto: RDP com nós de junção preservados; vão medido 4,9px → 0,000px. (3) Filtro pela grandeza errada. Contar vértices não mede extensão: a divisa BA–ES tem 6 arestas e desaparecia num filtro de complexidade. (4) Classe CSS derivada de rótulo humano quebra em silêncio. "Em campo" virava .st-Emcampo, que nunca existiu no CSS. Detector permanente criado (PN21-04d/PN21-04e): toda chave de estado emitida pelo script tem regra CSS correspondente, e a classe não é derivada do rótulo. (5) Referência órfã mata o script inteiro. getElementById para id inexistente derruba tudo abaixo dele, e a tela fica parada sem erro visível. Detectores REF-01/REF-02: todo getElementById e todo querySelector de id fixo no script têm alvo existente no documento. (6) Camada de sobrescrita de token sem variante de tema é meia implementação. A camada de tinta (S6) nasceu só com a face clara; no escuro o painel inteiro ficou ilegível. (7) Contraste de TOKEN não substitui contraste de COMPOSIÇÃO — o render-contraste.mjs nasceu disso. (8) O que nasce fora do campo de visão é lido como "não funcionou" → PN34b (realce + rolagem até o item mutado). (9) Convite que aparece onde o gesto não age é pior que convite nenhum → PN49. (10) Ação destrutiva não pode morar em alvo invisível que cruza a área → PN36 (o fio de dependência apagava por acidente ao clique simples). (11) Dois gestos não podem dividir o mesmo pixel — nó de dependência × punho; criar × panorâmica → modo explícito (PN41e). (12) viewBox e área têm de medir o mesmo, senão a camada esticada desalinha tudo (o fim do conector não coincidia com a barra dependente; guarda I-20). (13) Todo comportamento especificado precisa de um exemplar no dado ilustrativo (a lição 4 da rodada 2, confirmada duas vezes: sub-linhas de sobreposição e ordem com duas predecessoras). (14) Defeitos do INSTRUMENTO têm o mesmo peso que defeitos do artefato. Quatro medidos: coordenada de viewport em vez de rect da área · medir durante transição CSS · escolher ponto de clique sem verificar o que está sob ele · medir no sentido em que o navegador já está no fim do curso. Uma suíte errada reprova artefato bom e aprova artefato ruim — a auditoria do instrumento é parte da validação, não etapa opcional.

Nota de método da rodada 3, registrada por ordem do Rafael: duas vezes nesta sessão um lote de edições imprimiu "OK" sem que o arquivo tivesse sido salvo — o write fica no fim do script e o abort intermediário deixava tudo no meio. Regra permanente: se um lote de edições abortar, RE-EXECUTAR O LOTE INTEIRO; "OK" impresso não significa arquivo salvo, e só a leitura de volta por MD5 significa.


PROMOÇÃO A estável — 2026-08-16. As seções §49 a §55 foram promovidas em bloco pelo Rafael ("aprovo") após o gate visual sobre as seis telas do projeto. O gate encontrou dois defeitos reais que as cinco camadas automatizadas atravessaram, e os dois viraram guarda permanente antes da promoção: (a) no regime MODAL a lista de fundo acompanhava a navegação entre irmãos — verbatim, "está mexendo as duas janelas… era pra mexer só a que abriu sobreposta" —, o que gerou o PD19; (b) a topbar não degradava por CONTÊINER, só por viewport, e no estreitamento o rótulo da marca quebrava em duas linhas e a busca empurrava o avatar. Guardas novas: SNC-01 a SNC-03, TOP-01 a TOP-04. Nenhuma seção é promovida sem que o defeito que o gate achou tenha virado teste — é a regra que o projeto pratica desde a Fase 6.

TelaSEED · Painel — §48 (preview v0.15)abrir em página própria ↗

Também cita o §48: banco-edicao-linha, banco-escolha, tela-gantt, tela-onboarding, tela-shell.

Esc