versão v1.2.239+ · 📈 Lote 60.123 (Fluxo: empty-state + CTA de ativação): ATIVAÇÃO do user novo (última lacuna que o explorer apontou): o Fluxo de Caixa mostrava chart zerado + meses vazios pra quem não tinha o que projetar (frio, sem direção). Agora um bloco acolhedor + CTA pro +Novo, ADAPTATIVO: user novo (0 lançamentos) → "🔮 Veja o futuro do seu dinheiro" + "Adicionar 1º lançamento"; tem-dados-sem-recorrência → "Falta uma recorrência pra projetar" + "Adicionar recorrência". Condição = nenhum mês FUTURO tem income/expense. Sem sinal futuro, esconde chart/seletor flat mas MANTÉM o card do mês atual (contexto + botões de fatura). Revisão adversarial: sólido, 0 ALTA; 1 MÉDIA (redundância empty-state + chart chapado) corrigida. 4 tests novos. 3768 verdes. Frontend-only: src/App.tsx. Base: ⏳ Lote 60.122 (Fluxo: aviso de fatura pendente no saldo): polimento do cash-basis (fecha a MÉDIA da revisão do 60.121): no mês corrente, a fatura NÃO paga não entra no saldo (cash-basis) → o acumulado do Fluxo fica otimista. Agora `FluxoMonth.pendingFaturas` soma as faturas pendentes que ficaram fora do expense e o Fluxo mostra um aviso sutil âmbar ("⏳ saldo não desconta R$X de fatura(s) a pagar"). INFORMACIONAL — não muda income/expense/net/cumulative. 1 test novo. 3764 verdes. Base: 🩹 Lote 60.121 (Cash-basis de fatura — só conta como saída quando paga): CASH-BASIS de fatura (pedido do dono) — a compra de cartão só reduz o SALDO depois que a fatura é marcada PAGA; até lá é compromisso pendente (aparece badgeada, mas não conta). Cash-basis PURO: fatura vencida não paga continua pendente (não vira dívida). Helper isTxRealized + realizedTxs pré-filtram nas fronteiras de saldo (Dashboard/Lançamentos/Mony/snapshot/GVA/reserva) → todas herdam sem mudar assinatura; Fluxo Ponto B (fatura só soma no mês corrente/passado se paga, futuro projeta). LISTA mostra tudo (badge do 60.118); NÚMEROS cash-basis; maior-gasto/categoria seguem comportamentais (a compra é gasto quando feita). Revisão adversarial: 1 ALTA (cats vs expense incoerente → cats cash-basis) + 2 MÉDIA (snapshot congelado → comportamental estável) corrigidas. 8 files canônicos atualizados. 3763 verdes (era 3756). src/App.tsx. Base: 🩹 Lote 60.120 (Fluxo: despesa recorrente editável): a DESPESA recorrente no Fluxo (Nextel/Aluguel, kind txReal) não ganhava ✏️/⊘ — só a ENTRADA (kind salary). Agora o gate canEdit inclui txReal + os handlers do modal operam qualquer série recorrente por (monthKey && recurring), entrada ou saída. Fecha o follow-up do dono (continuo sem editar recorrente). Base: 🩹 Lote 60.119 (Bug 2+3: recorrência no mês atual — pendente + confirmar + editar): bug PROD do dono — salário recorrente com ORIGEM em mês anterior SUMIA do mês atual (R$0 entradas), embora projetasse no futuro (o mês corrente do computeFluxoProjection só olhava txs realizadas). Agora sintetiza a recorrência do mês atual como PENDENTE ("a confirmar", azul/itálico): CONTA na projeção (igual à provisão) mas só vira lançamento real quando o user confirma "✓ Já recebi/paguei" (materializeRecurring cria tx linkada via fromRecurringId = dedup; nada criado antes). Dashboard/Lançamentos seguem sem a recorrência até confirmar (modelo do dono). Bug 3: gate canEdit inclui pendingRecurring → ✏️/⊘ no mês atual (editar valor/pular/encerrar a série), inclusive despesa. Sanitizador backend preserva fromRecurringId (paridade). Revisão adversarial: 1 ALTA (botões mortos da despesa txReal no EditFluxoEventModal) + 2 MÉDIA (double-count via impactMonth no dedup; duplo-clique no confirmar) corrigidas. 13 tests novos. 3756 verdes (era 3739). src/App.tsx + api/user/txs.ts. Base: 🩹 Lote 60.118 (Bug 1: selo de fatura paga em Lançamentos): a fatura de cartão em Lançamentos não sinalizava se estava paga (user achava que "já saiu"). Decisão (com o dono): NÃO esconder (contabilidade canônica intacta) — só marcar "⏳ não paga · pague no Fluxo" (âmbar, só no mês atual e só com fluxoBeta) / "✓ paga" (verde). Chave de fatura paga single-sourced no helper faturaPaidKey (4 sites) → sem drift marcar↔ler. Revisão adversarial: 2 ALTA (selo só com fluxoBeta senão beco; "não paga" só no mês corrente — mês passado não afirma) corrigidas. 4 tests novos. Base: 🚀 Lote 60.117 (Onboarding: welcome proativo do 1º load): welcome PROATIVO no 1º load fecha a lacuna login→1ª ação (o onboarding era 100% reativo — só via a lâmpada da Mony). Modal ÚNICO (LS) pro user NOVO (sem lançamentos), na home, quando nenhum outro modal está aberto: 2 CTAs acionáveis (adicionar 1º lançamento → Lançamentos c/ form; conhecer a Mony → chat fresco c/ os chips do 60.116) + link do tour + explorar sozinho. shouldShowWelcome puro/testado (tabela-verdade). Revisão adversarial: 1 ALTA (não disparar atrás do LoginSplash — senão gravava "visto" sem o user ver) + 2 MÉDIA (não empilhar em modais de 1º load fora do gate; reset-data não re-mostra) + 1 BAIXA (foco no 1º CTA) corrigidas. 8 tests novos. 3739 verdes (era 3731). Frontend-only: src/App.tsx. Base: ✨ Lote 60.116 (Mony: chips de perguntas iniciais — fim da paralisia do campo vazio): os chips tocáveis eliminam a paralisia do campo vazio do user NOVO (a Mony abria com "pergunte o que quiser" + campo vazio). SÓ na saudação padrão (chat fresco): 3 chips que já submetem, adaptativos — <3 lançamentos → onboarding (Como funciona o app?, O que você faz?, Como economizo?) / com dados → análise (Como tô financeiramente?, Qual meu maior gasto?, Como economizo mais?). O click chama send(chip). Contrato blindado: cada chip roteia pra um handler determinístico bom (teste roda localReply em todos → zero fallback; um chip que leva a "não entendi" é pior que não ter chip). Não aparecem no re-engajamento (60.114) nem no histórico restaurado (60.115). Revisão adversarial (1 agente): 0 ALTA; 2 MÉDIA aplicadas — gate na saudação PADRÃO (antes reapareciam no re-engajamento e queimavam quota do free) + tap target 48px (regra Lighthouse). 4 tests novos. 3731 verdes (era 3727). Zero regressão. Frontend-only: src/App.tsx. Base: 💬 Lote 60.115 (Mony: histórico de mensagens persistido — a conversa do dia sobrevive): o chat da Mony deixa de ser efêmero — a conversa do MESMO DIA sobrevive ao recarregar. Complemento do 60.114. Local-first: só no seu device (localStorage, per-user), NÃO vai pro backend → sem PII de conversa no servidor. persistableHistory puro: filtra "prem" + cap + CRISIS-AWARE (descarta QUALQUER troca com o hotline CVV 188 — crise ou desabafo — pra não re-exibir sofrimento ao reabrir; desabafo financeiro persiste). O initializer restaura a conversa do dia (re-filtrada); novo dia → re-engajamento do 60.114. LGPD: limpo nos 3 pontos de logout/reset + email-resync. Adversarial: SÓLIDO nos 2 eixos safety-critical — crise excluída, sem falso-positivo de "CVV do cartão", GVA desmonta no logout (writer não re-grava stale → evita o bug do 60.106). 6 tests novos. 3727 verdes (era 3721). Frontend-only: src/App.tsx. Base: 🤝 Lote 60.114 (Mony: continuidade cross-sessão — a Mony retoma o fio ao voltar): quando o user volta ao chat (1ª vez de um novo dia), a Mony abre RETOMANDO o fio mais relevante da memória comportamental em vez da saudação genérica — fecha o arco (a memória que ela já tem vira CONVERSA). buildReengagementGreeting puro: prioridade — compromisso ATIVO do mês PASSADO (fechado) → status real via resolveCommitment (✅ celebra / 👀 acolhe+renovar); compromisso do mês CORRENTE → "rolando esse mês"; categoria-problema crônica; null → saudação padrão. Na abertura do chat, vira contextual SE !autoAsk E 1ª abertura do dia E há gancho. Custo zero, zero LLM. Escopo: re-engajamento na abertura (histórico de mensagens NÃO persistido). Adversarial: SÓLIDO, zero ALTA — usa só commitVerbo (tipo+categoria), NUNCA rawText; honesto; LGPD ok. 1 MÉDIA aplicada: anti-nag (compromisso do mês passado só retoma nos 1ºs 10 dias do mês). 9 tests novos. 3721 verdes (era 3712). Frontend-only: src/App.tsx. Base: 🔗 Lote 60.113 (Fix: chat-context descartava 10 tópicos de continuidade — irmão do 60.112): sweep proativo de paridade backend↔frontend achou e corrigiu 1 irmão. sanitizeCtx (api/user/chat-context.ts) tinha allowlist de lastTopic congelado em 7 valores (era ~Lote 21); o frontend persiste 16 (extractReplyContext). Os 10 tópicos de continuidade (60.91+: ver_onde_cortar, calc_corte_cat, divida_plano, etc.) caíam fora → sumiam no round-trip; no reload/2º device dentro de 20 min, "Quero" após um CTA caía no menu genérico. Fix: allowlist espelha a union do frontend. Sweep confirmou os demais endpoints sólidos. 4 tests funcionais novos. 3712 verdes (era 3708). Backend: api/user/chat-context.ts. Base: 🐛 Lote 60.112 (Fix PROD: saída recorrente parava de projetar no Fluxo de Caixa): bug reportado pelo dono — ao marcar uma SAÍDA como "🔁 se repete todo mês", ela não aparecia no Fluxo dos próximos meses. Rastreado ponta a ponta: o FRONTEND estava todo certo (toggle, saveTx, computeFluxoProjection — repro isolado confirmou que projeta saída recorrente). A causa era o BACKEND: o sanitizador sanitizeTxNewFields (api/user/txs.ts ~L100) gateava recurring em t.type === "income" SÓ → pra saída DELETAVA o campo no sync → no reload a tx voltava sem ele → parava de projetar. Causa raiz: frontend liberou recorrência em saída no 60.29, mas o sanitizador do backend ficou na regra legada income-only. Fix: aceitar (income || expense). Nota: saídas recorrentes marcadas ANTES do fix precisam ser re-marcadas. 5 tests funcionais novos. 3708 verdes (era 3703). Backend: api/user/txs.ts. Base: 📊 Lote 60.111 (Mony: Raio-X de saúde financeira enriquecido com a timeline): o "como tô financeiramente?" deixa de ser foto e vira filme. O handler de saúde financeira (Raio-X com status 🔴/🟡/🟠/🟢, do 60.28/29) ganha a dimensão TEMPORAL, conectando a memória comportamental (lotes 102–110) à pergunta que o usuário mais faz. (1) Tendência (meses fechados): poupança subindo/caindo ou rombo diminuindo/aumentando/saiu-do-vermelho. (2) Seu padrão: a categoria-problema crônica. Reuso puro de computeMonthSnapshots + computeProblemCategory. PROGRESSIVE ENHANCEMENT: as 2 linhas só aparecem com ≥2 meses fechados — usuário novo mantém o raio-X de mês único (zero regressão). Revisão adversarial (1 agente): bloco sólido (sem crash/NaN, income=0 não mascara déficit, crise protegida). 3 achados de clareza/tom corrigidos: (A1 ALTA) "📈 melhorando" sob "🔴 Déficit" → rótulo "(meses fechados)" + reconcilia "mas este mês apertou"; (M1 MÉDIA) categoria crônica == maior gasto do mês → funde em "E não é só este mês"; (B1 BAIXA) "saiu do vermelho 🎉". 9 tests novos. 3703 verdes (era 3694). Frontend-only: src/App.tsx. Base: 🧪 Lote 60.110 (Mony: harness de cobertura expandido — 57 vertentes + 1 gap calibrado): a pedido do dono, auditoria da cobertura conversacional. As frentes novas (102–108: Consultora plano/simulação, evolução cross-mês, recaída, compromissos, categoria-problema) foram incorporadas ao corpus central do harness (lote60-92), que estava congelado em 60.94 — agora protegidas contra beco-sem-saída. +6 categorias → 57 vertentes. A incorporação ACHOU 1 GAP REAL: "evoluí ou piorei?" caía no fallback → calibrado o regex do handler de evolução. Auditoria medida: 27/27 frases canônicas respondem deterministicamente (0 fallback). 7 tests. 3694 verdes (era 3687). Frontend: src/App.tsx (1 linha). Base: 📚 Lote 60.109 (Mony: perfil — não re-explicar conceito já ensinado): tijolo 2 de perfil/personalização. A Mony lembra o que JÁ te ensinou e para de repetir a aula do zero. A 2ª vez que você pergunta "o que é reserva de emergência?", "qual a regra dos 4%?", "como dividir meu salário?", ela dá um RELEMBRETE curto ("a gente já viu — resumão: …") em vez da explicação completa, e oferece a aula inteira de novo ("repete com 'de novo'"). 1ª vez = aula completa (zero regressão). Estado per-user (localStorage, custo zero, zero LLM), espelha o padrão de compromissos (60.106): persiste + re-sync no email + 3 limpezas. GRAVAÇÃO ROBUSTA: o send() marca "ensinado" SÓ quando a resposta REAL contém a ASSINATURA da aula completa — dupla-trava (detect do q + assinatura na reply) nunca marca um conceito que não foi de fato dado. Revisão adversarial (1 agente): arquitetura sólida (gravação, escape hatch, regressão, crise/LGPD). 3 ALTA de amplitude do detect (colisão de roteamento): \bfire\b solto pegava "fire emblem/tv"; "distribuição ideal" roubava o handler de distribuição de categorias; "% ideal" solto → fix: os detects passam a ESPELHAR exatamente os regexes dos handlers reais (o relembrete só dispara onde a aula completa também dispararia). Crise vence (CVV 188), só grava chave+timestamp (nunca o texto do user). 12 tests novos. 3687 verdes (era 3675). Frontend-only: src/App.tsx. Base: 🪞 Lote 60.108 (Mony: perfil — categoria-problema, a Mony conhece seu padrão): tijolo 1 de perfil/personalização. A Mony deixa de só saber "seu maior gasto DESTE mês" e passa a conhecer seu PADRÃO — a categoria variável que mais REAPARECE no seu top ao longo dos meses fechados. computeProblemCategory(txs) puro: últimos 6 meses fechados, presença no top-3 variável + total (reusa computeMonthSnapshots + calcMonthStats); EXCLUI fixas. Handler reativo ("qual minha categoria-problema?", "onde eu sempre escorrego?", "qual meu maior gasto recorrente?") no cluster CEDO — exige CRONICIDADE. Read-only/derivado: zero PII nova, zero LLM, custo zero. Revisão adversarial (1 agente): 2 ALTA — ramo "meu vício de…" era dead code (auto-bloqueado pelo guard !EMO_DISTRESS_RE que contém "vício") → removido; overclaim — limiar fixo ≥2 em janela de 6 declarava "vilão" com 33% de presença + desempate elegia categoria pequena onipresente → gate de recorrência ≥ METADE dos meses + entre crônicos vence o maior dreno. + MÉDIA (avg honesto; tom "mais reaparece" não "escorrega/vilão") + BAIXA (caso "tudo fixo"). Crise/EMO vencem mesmo com palavras de gasto. 16 tests novos. 3675 verdes (era 3659). Frontend-only: src/App.tsx. Base: 🏆 Lote 60.107 (Mony: card proativo de compromisso — celebra/cutuca sozinho): fecha o loop do 60.106 + entrega o reforço positivo proativo. Em vez de esperar "o que eu prometi?", um card no Dashboard (Trigger 8 em computeProactiveInsights, reusa resolveCommitment; o Dashboard lê os compromissos do localStorage). MÊS FECHADO (mensurável de verdade): resultado definitivo — ✅ celebra ("Você cumpriu! 🎉", só pra cortar/parar MEDIDOS pelo gasto; "guardar" usa título honesto "deu folga" pois ✅ = teve folga, não confirma que separou) / 👀 acolhe sem cobrança ("escorregar acontece — quer renovar?"). MÊS CORRENTE: só check-in gentil (janela dia 12–24), NUNCA "cumpriu". Revisão adversarial (1 agente): 2 ALTA — (1) "cortar" celebrava cedo demais no mês PARCIAL (parcial < mês cheio anterior por definição); (2) jitter celebrei→cobrei. Fix: celebração definitiva SÓ no mês fechado (cheio vs cheio). + MÉDIA (nag → janela 12–24) + BAIXA (guardar priority 1). Autolesão não chega ao card (compromissos já passaram pelos guards de crise na entrada). 10 tests novos. 3659 verdes (era 3649). Frontend-only: src/App.tsx. Base: 📝 Lote 60.106 (Mony: memória de compromissos — "vou cortar/guardar" → "o que eu prometi?"): a Mony lembra do que você se comprometeu. 100% determinística, localStorage, CUSTO ZERO, zero LLM/Haiku (pedido do dono). detectCommitment puro (só 1ª pessoa futura + âncora financeira) → anota e reconhece na hora SEM LLM (curto-circuita o Haiku); "o que eu prometi?" lista com status ao vivo (✅/👀/📝, contabilidade canônica); resolveCommitment HONESTO ("guardar" nunca afirma "guardou X" — saldo ≠ dinheiro separado). Per-user, não vaza no device. SEGURANÇA DE VIDA (3 rodadas adversariais): autolesão NUNCA vira "📝 anotei" — 3 camadas (detectLifeCrisis + isSelfHarmCut posição-independente + cortarFin) + guard de crise movido pro TOPO do localReply (precede todos os handlers) + detectLifeCrisis ampliado. 68 tests novos. 3649 verdes (era 3581). Frontend-only: src/App.tsx. Base: 📣 Lote 60.105 (Mony: coaching proativo — a recaída aparece sozinha): a Mony deixa de esperar você perguntar. Fecha o ciclo da timeline (60.102-104): em vez de só responder "tô recaindo?", ela levanta a recaída sozinha como card no Dashboard. Novo trigger no computeProactiveInsights chamando detectRelapse (reuso, já blindado) — teaser gentil cuja ação abre a consulta completa. Cooldown natural sem state novo (id mensal + dismiss persistido). Usuário novo (<3 meses) ou melhorando → nenhum card. Revisão adversarial: 1 ALTA — ranking ordenava por magnitude crua (incomparável entre triggers) → recaída afogava/era afogada; fix: ProactiveInsight ganhou priority (urgência: time-sensitive > comportamental > padrão > estrutural). 6 tests novos. 3581 verdes (era 3575). Frontend-only: src/App.tsx. Base: 🔁 Lote 60.104 (Mony: detecção de recaída — "tô recaindo?"): o diferencial competitivo — a Mony percebe quando você VOLTA a escorregar. Recaída = vinha BEM e escorregou de forma SUSTENTADA (2+ meses consecutivos), não isolado. detectRelapse(snaps) puro (vermelho/poupança; baseline = MEDIANA, imune a bônus; exclui one-time/crash/crônico; ≥3 meses) + buildConsultaRecaida (tom de PERGUNTA, sem cobrança, sem score). Handler com 3 camadas de segurança: detectLifeCrisis (crise→CVV) + EMO_DISTRESS_RE + RELAPSE_EMO_RE (recaída de vício/relacionamento/emoção → EMO, nunca financeira). Revisão adversarial: 3 ALTA corrigidos (ambiguidade emocional/vício, crash isolado, baseline por max). Limiares conservadores. 23 tests novos. 3575 verdes (era 3552). Frontend-only: src/App.tsx. Base: 🛟 Lote 60.103 (Mony "como evoluí?" cross-mês + correção CRÍTICA do guard de crise): FEATURE — a Mony compara os 2 meses FECHADOS mais recentes (buildConsultaEvolucao, puro, via computeMonthSnapshots, sem backend novo). Sinal-chave = taxa de poupança (% da renda, métrica real, nunca score). Tom honesto: melhora encoraja 📈, queda observa+pergunta (nunca veredito), déficit fala de SALDO (saiu do vermelho 🎉 / reduziu o rombo / rombo aumentou — nunca "guardou da renda"). SEGURANÇA (descoberto na revisão adversarial) — o guard de crise era PRÉ-EXISTENTEMENTE incompleto (só matar/suicidar, sem "morrer"/autolesão/ideação passiva/métodos): em PROD "queria morrer" NÃO acionava o CVV. Criada detectLifeCrisis (detecção CENTRALIZADA e idiom-aware, fonte única do guard + da exclusão do handler de evolução): pega suicídio, métodos, autolesão, ideação passiva e desaparecimento; e NÃO dispara nos dramas financeiros ("morrer de fome", "cortar gastos", "sumir com a dívida", "cansei de viver no vermelho"). Processo (ultracode): 6 rodadas adversariais (1 agente + workflow de 4 + 3 focadas). 55 tests novos (evolucao + crise, ambos contratos). 3552 verdes (era 3499). Frontend-only: src/App.tsx. Base: 🧬 Lote 60.102 (Persistência da timeline financeira · fundação temporal): a Mony deixa de ser cega ao passado — fundação pra comparação cross-mês, detecção de recaída e ciclos. Tx.createdAt? (timestamp do lançamento, aditivo) + computeMonthSnapshots (resumo canônico de cada mês fechado, backfilla + congela) + novo endpoint api/user/history.ts (hash user::history, auth + rate limit, FROZEN, cap 24 meses, sanitize server-side). Frontend carrega no login + sincroniza SEM polling novo. Revisão adversarial (1 agente): ZERO ALTA — segura pra PROD (aditiva, retrocompatível, converge, frozen, sem vazamento); 4 endurecimentos. 4 tests novos. 3425 verdes (era 3421). Backend: api/user/history.ts (novo) + txs.ts. Base: 🔮 Lote 60.101 (Mony: A Consultora — Onda 4 · simulação de mudança recorrente): a Consultora simula "e se". Novo 4º cérebro buildConsultaSimulacao — "e se eu guardar/ganhar/cortar/gastar R$ X por mês (por N meses)?". Projeta o impacto acumulado sobre a trajetória do saldo canônico, mostra o custo anual de um gasto novo e alerta se derruba algum mês no vermelho. Preenche um gap (faltava a mudança recorrente ao longo do tempo). Horizonte 1-12 meses; aceita R$/mil e % da renda. Revisão adversarial (1 agente): 2 ALTA + 1 MÉDIA corrigidos — off-by-one da projeção (descartava o cálculo), "~N semanas" enganoso → cruzamento sobre a projeção real, percentual via renda. 17 tests novos. 3421 verdes (era 3404). Frontend: src/App.tsx. Base: 🗺️ Lote 60.100 (Mony: A Consultora — Onda 3 · plano de ação multi-passo): a Consultora vira PLANO. Novo 3º cérebro determinístico buildConsultaPlano — 4 metas (sair do vermelho / juntar pra X / cortar gastos / quitar dívida), passos em SEMANAS, raciocínio pelo saldo canônico. Acha as alavancas (maiores categorias variáveis), calcula quanto cada corte libera, soma a sobra e projeta o prazo. Fecha os 4 CTAs que compra/déficit prometiam e caíam em fallback. Consulta ganhou meta + passos[]; renderConsulta rende 🎯 meta + 🗺️ plano numerado. Revisão adversarial (1 agente): 0 ALTA; 6 achados corrigidos (quitar sem valor → playbook rico existente; regexes ancoradas; tom honesto). 18 tests novos. 3404 verdes (era 3386). Frontend: src/App.tsx. Base: 🔭 Lote 60.99 (Mony: sweep proativo de auto-contradições — 5 coerências): a pedido do dono, caça PROATIVA de pares de handlers que divergem pra mesma entrada. Workflow de sweep (5 mapeadores + cruzamento) → 39 candidatos → triagem (maioria já-corrigida nos 60.85-98 ou falso-positivo) → 5 reais: (1) "quanto gastei no cartão?" rotulava "esse mês: R$ X" → agora "compras feitas em [mês]" + "compra entra na fatura, não no saldo do mês"; (2) "quantos dias aguento sem renda?" somava só meses positivos → inflava fôlego de quem tem dívida → agora signed + clamp 0; (3) insight "N dias até o limite" disparava pelo mês isolado → agora só se a reserva não cobrir; (4) "causa principal" = fatura aponta "como dividi meus gastos?"; (5) "liberdade mensal" (renda − fixas) ≠ "quanto sobra" (renda − tudo). Adversarial (1 agente): 0 ALTA, 2 MÉDIA resolvidas. 7 tests novos. 3386 verdes (era 3379). Frontend: src/App.tsx. Base: 🔧 Lote 60.98 (Mony: "e se eu adiar X?" para de se contradizer com o gasto — bug do dono): "e se eu adiar valenmodas?" dizia "não achei" enquanto "ValenModas" (consulta de gasto) achava o item (categoria, R$ 12.757,60, maior gasto). Causa: o handler de adiar/manter só varria eventos top-level da projeção (cartão vira "Fatura {cardName}", itens "Para Cartão") — uma categoria nunca casava. Fix: novo helper findSpendingByName reconcilia (acha por categoria/cartão/nome em grupo coeso, exato antes de substring) e, em vez de "não achei", aplica a regra canônica (Lote 60.96): cartão = fatura = compromisso ("adiar não some o gasto, atrasar vira juros → renegociar/cortar compras novas"); gasto comum já feito → "já saiu, não dá pra adiar o passado". Idem no "manter" + regex aceita "e se eu manter". Processo (ultracode): workflow de diagnóstico (3 mapeadores + síntese) + revisão adversarial (1 agente) — zero ALTA, 2 MÉDIA corrigidas. 10 tests novos. 3379 verdes (era 3369). Frontend: src/App.tsx. Base: 🔧 Lote 60.97 (Mony: 3 incoerências de render/coerência — bug do dono em PROD + auditoria): (1) itálico quebrado — 11 respostas usavam _"frase"_ (itálico markdown) que o renderizador da Mony (só **negrito**) mostrava como underscore literal → removidos, viram aspas; (2) FC5 vs realizado — "vou ficar negativo?" dizia "nenhum mês fecha negativo" (projeção, que conta provisões pending) enquanto "estou negativo?" dizia "Sim, déficit -R$ X" (realizado) → FC5 agora reconcilia com o balance canônico; (3) FC4 (achado por agente auditor) — "como vou estar daqui N meses?" reportava cumulative SEM somar previousBalance → contradizia a aba Fluxo quando há reserva; agora saldo canônico (cumulative + previousBalance). Auditoria adversarial (1 agente): markdown não-suportado limpo; demais usos projeção-vs-realizado (FC16, buildConsulta*, FC3, FC8, adiar) todos corretos — FC4 era o único restante. 8 tests novos. 3369 verdes (era 3361). Frontend: src/App.tsx. Base: 🩺 Lote 60.96 (Mony: A Consultora — Onda 2): mês negativo + compra genérica viram conselho. (1) novo cérebro buildConsultaDeficit — mês que fecha no vermelho vira Diagnóstico → Porquê (causa + % do déficit) → Opções de ajuste REAIS (simulateMonthWithoutEvent nos 3 maiores) → Recomendação; plugado em FC3-negativo e FC5. (2) handler de compra genérica (6502) reconhece "comprar" e responde no formato Consultora. Revisão adversarial (1 agente) pegou 2 ALTA: déficit ignorava a reserva (alarmista) → agora usa previousBalance+cumulative, "atenção, não emergência" quando cobre; e "Adiar/cortar" em fatura/financiamento (atraso = juros) → rótulo por tipo (discricionário "Cortar/adiar" ✅ / compromisso "Renegociar" nunca ✅). 11 tests novos. 3361 verdes (era 3350). Frontend: src/App.tsx. Base: 💡 Lote 60.95 (Mony: A Consultora — Onda 1): a Mony deixa de só responder e passa a ACONSELHAR. Decisão de compra ("posso/consigo/dá comprar [carro/tv/notebook/celular/...] de R$ Y" — handler J2) agora vira formato Diagnóstico → Porquê → Opções (à vista/parcelar/esperar, com impacto calculado) → Recomendação + dica, sobre o saldo CANÔNICO (totalAvailable) + projeção real. Arquitetura nova determinística (zero LLM): separa ANÁLISE (objeto Consulta) de RENDER. Lote A = fundação (buildConsultaCompra + renderConsulta); Lote B = integração no J2. Revisão adversarial (1 agente) pegou 2 bugs ALTA: parcelar recomendado a quem está no vermelho (corrigido com gate noVermelho) + "esperar" usando cumulative sem previousBalance (corrigido pro saldo canônico). + gate de prudência (≤30% renda) + dica acionável. 18 tests. 3350 verdes (era 3332). Frontend: src/App.tsx. Base: 🎓 Lote 60.94 (Mony: 3ª onda — backlog conversacional ZERADO · investimento/juros/FIRE): fecha o backlog do harness (60.92/93). 7 últimas vertentes calibradas, verificadas contra o código real + guard onde havia risco: cálculo de rendimento sem verbo ("rendimento de 50 mil a 0.5% ao mês..."), FIRE conceitual ("regra dos 4%" = 25× gasto anual), FIRE inverso ("quanto pra viver dos juros?"), simetria de juros com SÍMBOLOS ("+20% depois -20%" → 100×1,2×0,8=96, não volta ao mesmo), projeção de patrimônio ("simule meu crescimento em 8 anos"), FC3 ("qual meu gasto previsto?" + "quanto vou gastar em julho?" com parsing de mês). KNOWN_GAPS agora VAZIO — todas as ~270 vertentes do corpus respondem. Processo (ultracode): verificação contra código real + revisão adversarial. 7 tests novos. 3332 verdes (era 3325). Zero regressão. Frontend: src/App.tsx. Base: 🎯 Lote 60.93 (Mony: 2ª onda de calibração conversacional — 24 vertentes): continuação do harness (60.92). Um workflow de 5 agentes Explore investigou os ~30 gaps do KNOWN_GAPS; fixes verificados contra o código real e aplicados com guard de contexto onde havia risco. 24 vertentes calibradas: Fluxo de Caixa beta (quando recebo, sobra média, renda total, causa principal, projeção em N meses, risco de mês negativo, quais cartões), emocional (desespero/afundado/ansioso financeiro — crise PURA sem contexto financeiro ainda vai pro CVV 188), saldo (tô negativo?/tô devendo?, como dividi meus gastos? → breakdown por categoria, vou fechar no positivo?), meta-conversa (e aí mony, recomeça, o que você consegue fazer? → menu de capacidades). Backlog restante (3ª onda): cálculo de investimento sem verbo, FIRE conceitual, simetria de juros com símbolos, FC3 (parsing de mês). Processo (ultracode): workflow de investigação (5 agentes) + verificação contra código real + revisão adversarial. 8 tests de qualidade novos. 3325 verdes (era 3317). Zero regressão nos 124 arquivos existentes. Frontend: src/App.tsx. Base: 🧪 Lote 60.92 (Mony: harness de cobertura conversacional + 1ª onda de calibração): pedido do dono — "crie agentes que testem todas as vertentes da Mony (pergunta → resposta → continuação) pra ter um sistema completo de expandir o público". Entregue um harness multi-turno DETERMINÍSTICO que simula a conversa sem custo de LLM (localReply → extractReplyContext → afirmação → localReply); corpus de categorias × frases reais de usuário enumerado por workflow de 5 agentes Explore. Detecta 3 modos de falha: beco sem saída (fallback/menu), continuidade quebrada (proposta "Quer X?" + "Quero" trava) e inconsistência. Estrutura honesta: anti-regressão no que funciona + KNOWN_GAPS (backlog visível das ~30 vertentes que ainda caem em fallback) → calibração em ondas. 1ª onda — 11 vertentes calibradas: "quanto sobra?"/"quanto me resta?", dívida ("tô endividado"/"como saio das dívidas?"/"tenho X de dívida"), "como economizo mais?", juros compostos conceitual (sem números → "juros que rendem juros"), taxa de poupança. Guard de negação (iOS-safe) impede falso-positivo de plano de dívida em "não tenho dívida". Processo (ultracode): workflow de enumeração (5 agentes) + checagem adversarial. 69 tests novos. 3317 verdes (era 3248). Frontend: src/App.tsx. Base: 🧠 Lote 60.91 (Mony: continuidade conversacional — afirmação após proposta da própria Mony): a Mony propõe follow-ups ("Quer 3 formas práticas de cortar em ValenModas?") mas quando o user diz "Quero"/"Sim"/"Pode" ela não continua, cai no menu genérico. Causa (workflow de mapeamento, 3 agentes): o reconhecimento de afirmação já é amplo; o elo que quebra é o extractReplyContext — só "lembra" o tópico se reconhecer a frase exata. Fix: (1) extractReplyContext captura a família de CTAs de corte → ver_onde_cortar; + "calcule quanto sobra se cortar N%?" → calc_corte_cat; + "transformar essa sobra em meta?" → meta_da_sobra. (2) handlers de afirmação novos: calc_corte_cat (15% da maior categoria + novo saldo vermelho), meta_da_sobra (reserva/meta/investimento), saude_financeira e wow_salario_aluguel (eram órfãos). (3) isCompoundAffirm: "quero sim", "pode sim", "sim quero", "com certeza", "por favor", "quero muito". Processo (ultracode): workflow de mapeamento (3 agentes) + checagem adversarial (removeu 1 regex frouxo; isCompoundAffirm sem falso-positivo; achou os órfãos). 33 tests novos (captura das CTAs, 21 variantes de afirmação, cada elo da cadeia, ponta-a-ponta). 3248 verdes (era 3215). Frontend: src/App.tsx. Base: 🎨 Lote 60.90 (Mony: déficit SEMPRE em vermelho nos handlers de saldo + "sangramento" determinístico): 2 pontos do dono. (1) "essas perguntas caem no LLM?" — "saldo", "explique meu saldo" (KPI do header), "por que meu saldo é esse?", "onde estou desperdiçando?" JÁ eram determinísticas (handler do localReply, não a IA); só "sangramento" caía no LLM → novo handler determinístico. (2) "saldo incorreto" — valor está certo; o déficit não vinha em vermelho porque fmt() usa Math.abs (mostrava negativo como POSITIVO). Fix: handlers de saldo/déficit usam fmtSigned → "-R$" vermelho (saldo 5838, saudação 3911, limite diário 5164, impacto de compra 5225, fim de mês 5204, % poupança 4302, visão do mês 4614, cenários 3895/3906). Processo (ultracode): checagem adversarial. 16 tests novos. 3215 verdes (era 3199). Frontend: src/App.tsx. Base: 🎨 Lote 60.89 (Mony: negativos SEM sinal de menos também viram vermelhos): continuação do 60.88 (screenshots do dono). Depois do 60.87/60.88 os números ficaram consistentes (saldo −1.320,20 em toda resposta), MAS o fallback LLM (Claude Haiku) escrevia a perda SEM o "−" ("perdendo R$ 1.320,20 por mês" — em negrito virava VERDE). Fix: (1) o system prompt do LLM instrui a SEMPRE marcar negativos com "−" colado no R$; (2) normalizeMonyNeg injeta "−" quando há contexto de perda (perdendo/perda de/rombo de/déficit de/negativo em/no negativo de/estourou em), tolerando ** entre o contexto e o R$; MONY_NEG_TOKEN também pega "R$ X negativo". Guard de negação (sem/evitar/impediu) pra "evitar perda de R$ X" NÃO ficar vermelho. iOS-safe (só replace + lookahead, sem lookbehind). 11 tests novos. 3199 verdes (era 3188). Frontend: src/App.tsx; Backend: api/pulse/llm-fallback.ts. Base: 🎨 Lote 60.88 (Mony: valores negativos em VERMELHO — render central + 2 deferidos do 60.87): pedido do dono "o que for negativo, traga na cor vermelha na Mony". O render das bolhas só colorava valores DENTRO de **bold** e checava o sinal só no INÍCIO do trecho — então "Diferença: -R$ 5.307,40" (minus no meio) saía VERDE. Fix: colorização CENTRALIZADA (MONY_NEG_TOKEN + splitMonyNegatives pura/exportada + renderMonyText) — qualquer negativo (−R$/-R$ X, -X%, ou "R$ X no vermelho") vira VERMELHO em QUALQUER posição (bold ou texto normal), inclusive minus antes do ** (normaliza "-**" → "**-"); **bold** sem sinal segue lime; cobre handlers do localReply E o fallback LLM. Bloqueia falso-positivo de range ("10-15%") via matchAll + checagem do char anterior — SEM lookbehind (quebraria iOS Safari < 16.4 no parse do regex). + 2 deferidos do 60.87: buildAlerts conta o MÊS CORRENTE por impacto (alerta "guardando R$ X este mês" usava acumulado); handler de viagem usa totalAvailable (reserva + mês). Processo (ultracode): revisão adversarial (3 lentes — pegou o range + o risco iOS). 16 tests novos + 60.27.1/60.27.2 migrados. 3188 verdes (era 3172). Frontend: src/App.tsx. Base: 🩹 Lote 60.87 (Mony: fonte única de saldo — handlers + snapshot do fallback LLM): bug PROD de credibilidade — a Mony se CONTRADIZIA entre as próprias respostas no mesmo mês. Header −1.320,20 (correto), mas "qual meu maior gasto?" dizia "positivo +5.079,80, sem déficit" e o fallback LLM (Claude Haiku) dizia "−5.307,40 / saídas 39.584,17". Causa (auditoria por workflow de 4 agentes): cada camada contava diferente — (a) o handler "causa principal" decidia déficit/positivo por currentFluxo.net (projeção do Fluxo, que INCLUI provisões pending; user tinha provisão de entrada pending ~6.400: −1.320,20 + 6.400 = +5.079,80); (b) buildPulseSnapshot (alimenta o Haiku) usava calcStats(txs) = acumulado da vida TODA. Fix: tudo pela MESMA fonte canônica do header (calcMonthStats(filterTxsByImpactMonth) + calcReserve, por mês de impacto) — (1) o gate "positivo vs déficit" do handler usa o saldo REALIZADO (balance), nunca a projeção; (2) buildPulseSnapshot usa o mês corrente e agora envia previousBalance + totalAvailable pro LLM (aviso anti-alarmista: mês negativo MAS reserva cobre); backend api/pulse/llm-fallback.ts atualizado; (3) as 3 chamadas de computeFluxoProjection no localReply passam previousBalance + paidFaturas. Deferido: buildAlerts por mês + handler de viagem usar totalAvailable. Processo (ultracode): workflow de auditoria (4 agentes) + revisão adversarial (3 lentes). 9 tests novos = 3172 verdes (era 3163). Frontend: src/App.tsx; Backend: api/pulse/llm-fallback.ts. Base: 🩹 Lote 60.86 (Radar/insights/gráfico/Mony por mês de fatura — consistência total): continuação do 60.85 — 3 absurdos pegos pelo user em PROD. O 60.85 unificou Dashboard/Lançamentos/Fluxo pro mês da fatura, mas o Radar do Dia e os insights/alertas ficaram contando por DATA DA COMPRA → divergiam da Home. Sintomas (Junho, saldo Home −1.320,20): (1) Radar mostrava "saldo negativo em R$ 4.795,20" (= −1.320,20 − 3.475 das compras Junho-data/Julho-fatura); (2) projeção "−R$ 560,64" confusa (menos negativa que o saldo — a fatura sumia do mês); (3) alerta "1 dia até entrar no limite" estando JÁ em déficit. Causa (workflow de diagnóstico, 4 agentes): computeRadarOfDay (t.date.startsWith) e computeProactiveInsights (ymOf(t.date)) não foram migrados no 60.85. Fix (impact-month via txImpactMonth/filterTxsByImpactMonth + exclui pending): Radar (thisMonth + prevMonthTxs por impacto → bate com a Home e projeção coerente), insights (todos os triggers; o Trigger 5 "dias até o limite" não dispara quando já em vermelho), deriveChart (gráfico de 8 meses), estimateFixedExpenses/estimateSurvivalDays (Mony) e computeMicroReaction (toast). Não tocados (justificado por revisão adversarial): buildAlerts (calcStats sobre toda a história, sem atribuição de mês) e detectRecurringTxs (já ignora cartão desde o 60.84). Processo (ultracode): workflow diagnóstico (4 agentes) + revisão adversarial (3 lentes — correção ✅, regressão ✅, completude pegou 6 candidatos → 4 corrigidos, 2 falso-positivos). 13 tests novos = 3163 verdes (era 3150). Backend INTACTO. Frontend: src/App.tsx. Base: 🩹 Lote 60.85 (contabilidade de cartão unificada nas 3 telas — saldo consistente): bug PROD CRÍTICO de credibilidade — um user com 2 cartões (faturas pagas) viu 3 saldos diferentes pro MESMO mês (Home −2.023, Lançamentos +1.451, Fluxo +19.684). Cada tela contava cartão diferente: Home por data da compra, Lançamentos por mês da fatura, Fluxo pulava fatura paga (sumia + inflava). Fix (decisão do dono — mês da fatura canônico + fatura paga conta + selo): helper puro txImpactMonth (fonte única do mês de impacto: cartão→fatura); Dashboard/Mony/Lançamentos passaram a usar filterTxsByImpactMonth (saldo conta cartão pela fatura em toda tela); computeFluxoProjection mostra fatura paga marcada "✓ PAGA" e continua contando (não some); calcUpcomingFaturas exclui as pagas. Resultado: 3 telas com o MESMO saldo. Sem cartão = idêntico. Processo (ultracode): workflow de diagnóstico (5 agentes) + revisão adversarial (3 lentes; pegou 1 HIGH na Mony → corrigido). 14 tests novos = 3150 verdes (era 3136). Backend INTACTO. Frontend: src/App.tsx. Base: 🎯 Lote 60.84 (seletor de vencimento: obrigatório sem pré-seleção + vale pra compra única): 2 pedidos do user — (1) ao marcar parcelado no cartão o vencimento não deve vir preenchido, e sim ser escolha obrigatória; (2) compra ÚNICA no cartão também deve deixar escolher o vencimento direto. Fix: condição do seletor virou só `form.cardOn` (vale pra compra única E parcelada, label "parcela N/M" ou "compra"); sem pré-seleção (form.date limpo ao ligar cartão, nenhum chip marcado); guard no saveTx torna a escolha obrigatória; removidos o label "da compra" + a dica antiga do Lote 60.44; detectRecurringTxs passou a excluir tx de cartão (anti falso-positivo "recorrente ~dia 1" — bug pego por revisão adversarial em 3 lentes). 16 tests novos = 3136 verdes (era 3127). Backend INTACTO. Frontend: src/App.tsx. Base: 🎯 Lote 60.83 (cartão+parcelado — seletor de VENCIMENTO direto): pedido do user no cartão Uniclass ("fechamento só na tela sem ação por trás, e ao lançar perguntar se a parcela 9 vence 15/06 ou 15/07, e a partir daí ajustar a próxima"). O modelo era de trás pra frente: o user sabia o vencimento mas tinha que codificar isso numa data (dia vs fechamento). Fix: pra cartão+parcelado o campo Data vira um SELETOR DE VENCIMENTO (chips: mês atual + 2 à frente). O user escolhe direto em qual fatura a parcela atual cai; a próxima encadeia sozinha ("parcela 9/10 → 15/06 · parcela 10/10 → 15/07"). Fechamento fica informativo. Sem tocar no motor: ao escolher o mês mk, grava tx.date = "-01" → calcFaturaMonthKey mapeia certo e o display (Lote 60.82) mostra o vencimento. Novo helper puro faturaCandidates(today, count). 14 tests novos = 3127 verdes (era 3113, +14). Backend INTACTO. Frontend: src/App.tsx. Base: 🩹 Lote 60.82 (cartão — data exibida vira o VENCIMENTO da fatura, não a âncora): observação fina do user no cartão Uniclass ("o cartão vence todo dia 15, do jeito que está, está incorreto") — a tx "Para Cartão 9/10 · Uniclass" mostrava 01/06 (a data-âncora da parcela), mas pra cartão a data que importa é o vencimento (15), quando o dinheiro sai. Causa (meio-caminho do Lote 60.52, que já agrupava o cartão pelo MÊS da fatura mas exibia o DIA da compra). Fix: helper puro `txDisplayDayMonth(date, card)` — pra cartão devolve o `dueDay` no mês da fatura; sem cartão, data própria. Aplicado em Lançamentos (item + subitem de grupo), Recentes do Dashboard e linha da parcela na fatura do Fluxo. Resultado: 01/06 → 15/06 (fatura Junho). Campo de ENTRADA segue sendo data (o dia decide a fatura); só o EXIBIDO virou o vencimento. 14 tests novos = 3113 verdes (era 3099, +14). Backend INTACTO. Frontend: src/App.tsx. Base: 🩹 Lote 60.81 (Fluxo de Caixa — parcela ATUAL de cartão editável na fatura): bug PROD do user no cartão Uniclass ("deveria conseguir editar e não consigo") — a parcela atual de um cartão parcelado (ex: "Para cartão (9/10)" na Fatura Uniclass) não tinha os botões ✏️/⊘, enquanto a parcela atual de parcelas SEM cartão (Carro 25/48, MP Dessa 21/24) sempre teve. Causa: o Lote 60.22.7 só dava `txId+parcelaIdx` pra parcela FUTURA (`parcelaIdx > current`) no cartão, assumindo "a atual já é tx real do mês, edita via Lançamentos" — premissa que quebra no cartão+parcelado, onde a parcela atual pode cair numa fatura FUTURA (compra 15/06 parcela 9/10 fechamento 08 → fatura Julho). Fix: removido o gate `isFutureParcela` — toda parcela de cartão (inclusive a atual) recebe `txId+parcelaIdx`, expondo os mesmos handlers de edit/skip já provados (espelha as parcelas não-cartão). Compra única no cartão continua sem botões. 8 tests novos = 3099 verdes (era 3091, +8). Backend INTACTO. Frontend: src/App.tsx. Base: 🩹 Lote 60.80 (cartão parcelado — a Data ancora a parcela ATUAL): confusão PROD do user no cartão Uniclass — lançou compra parcelada 9/10 pondo a data da COMPRA original (10/09/2025) estando na parcela 9, fechamento 08 / vencimento 15, pedindo "contar a partir de junho", mas o app jogou tudo em Outubro/2025. Causa raiz: a projeção ancora a parcela ATUAL na `tx.date` via `parcelaDate(date, pIdx - current)`. Fix (Opção B turbinada, sem migração de dados, backend intacto): (1) label da Data com parcelado vira "parcela {current}/{total}" em vez de "da compra"/"1ª parcela" (precedência nova provisioned > installments > card); (2) dica dinâmica ao vivo mostra em qual fatura (cartão) ou mês (sem cartão) a parcela atual + as próximas 2 caem — user vê "parcela 9/10 → Outubro 2025" e corrige a data; (3) gate `!installmentsOn` na dica de cartão antiga pra não duplicar. 21 tests novos = 3091 verdes (era 3070, +21). Backend INTACTO. Frontend: src/App.tsx. Base: 📅 Lote 60.79 (Lançamentos: sort por date DESC + filtro chips por período): Bug reportado pelo user via screenshot PROD — lista de Lançamentos vinha ordenada por `createdAt` (último lançado primeiro), confuso quando lançava tx retroativa (cenário real: JD Joelma 02/06 lançada AGORA aparecia ACIMA de Smart TV 06/06). Fix: helper puro `sortTxsByDateDesc(txs)` ordena por `date` DESC, com `id` DESC como tiebreaker (preserva ordem de inserção entre tx de mesma data). Bonus na mesma screenshot: user pediu filtro por data dentro do mês. Implementado: chips toggle "Mês inteiro · Hoje · Ontem · Últimos 7d" abaixo do tab de tipo, dentro do bloco `📅 Período`. Helper puro `applyDateFilter(txs, range, today)` testável isolado. Combina AND com filtros existentes de tipo + categoria. Default "all" = comportamento atual preservado. Visível só fora da aba Metas. Não aparece em screenshot/visual regression por estar dentro da aba Lançamentos (não Dashboard). 24 tests novos = 3070 verdes (era 3046, +24). Backend INTACTO. Frontend: src/App.tsx. Base: 📣 Lote 60.78 (sobre.html — labels leigos nas 6 badges de auditoria): User notou que os labels técnicos das badges ("Security Headers", "HTTP Observatory", "Website Grader", "PageSpeed Mobile") não eram entendíveis pelo usuário leigo. Sugestão dele: frase amigável em destaque + nome técnico embaixo. Trocas: "Security Headers" → **Servidor blindado**; "HTTP Observatory" → **Conexão segura**; "Website Grader" → **Site bem feito**; "PageSpeed Mobile" → **Rápido no celular**; "Acessibilidade" → **Acessível pra todos**; "Sem malware" → **Sem vírus nem golpe**. Nome técnico vai pra `badge-source` em parênteses: "Snyk (Security Headers)", "MDN/Mozilla (HTTP Observatory)", "HubSpot (Website Grader)", "Google (PageSpeed)". Preserva credibilidade do nome técnico pra quem reconhece + comunica claramente pro leigo. Meta description também atualizada com linguagem leiga. Backend INTACTO. Frontend: só public/sobre.html. Base: 🏅 Lote 60.77 (sobre.html — 6 badges atualizados): Após Lote 60.76 conquistar PageSpeed mobile **100/100/100/100** (Desempenho + Acessibilidade + Boas Práticas + SEO TODOS perfeitos), a seção "Pode confiar" do /sobre.html ficou desatualizada. Atualizações: (1) PageSpeed 94+ → **100** (label vira "PageSpeed Mobile" pra ser explícito); (2) Headline "4 auditorias" → "6 auditorias"; (3) Badge novo **WAVE AIM 9.9** (Acessibilidade · WebAIM); (4) Badge novo **Sucuri ✓** (Sem malware — 9 blacklists limpas: Google Safe Browsing + McAfee + ESET + PhishTank + Yandex + Opera + Sucuri + outras); (5) meta description atualizada citando novas auditorias. Badges originais Snyk A+ + MDN/Mozilla A+ + HubSpot 100 preservados. Grid CSS mantido (1fr 1fr) — 6 badges viram 3 linhas × 2 colunas naturalmente. 12 tests novos = 3046 verdes (era 3034, +12). Backend INTACTO. Frontend: só public/sobre.html. Base: 🎯 Lote 60.76 (skeleton estático LCP — PageSpeed 92 → 95+ esperado): Lote 60.75 fez code-split do AdminPanel (bundle -17%) mas PageSpeed continuou 92. Diagnóstico real do PageSpeed v1.2.149 revelou que o problema NÃO era tamanho do bundle e sim: *"Detalhamento da LCP — Atraso na renderização do elemento: 2810ms"* — o `` ficava VAZIO no HTML até React montar. Fix cirúrgico: replicar visualmente o conteúdo da tela de login (logo "monify.app" + "COPILOTO COMPORTAMENTAL" + `Evita o colapso financeiro
` + tagline completa) DENTRO de `` no index.html como HTML estático. CSS do skeleton inline no `