fix(home): oferta leva ao formulário, ofertas logo depois da vitrine

O botão das ofertas rola até o contato com o tipo marcado e não quebra mais as âncoras; ofertas com o que você recebe e primeiro passo, logo depois da vitrine; bloco de IA fora da home; cartão largo da SEV no hero do celular; separador da faixa de clientes só entre itens; favicon de 13 para 1,8 KB; pré-carregamento só no hover e nunca de /#... fora da home; X do menu e cabeçalho opaco; WhatsApp com "o case"; /ia com 3 passos e modelo de relatório ilustrativo; Sobre alinhado ao currículo; CSP Report-Only com os domínios do Ads.
This commit is contained in:
Evandro Fonseca Junior committed 2026-10-14 19:14:47 -03:00
1 parent fc44a69fb3
commit 9e2d65a926
51 files changed
+1730 -420

No files matched your search

+113 -6
View File
@@ -5,6 +5,7 @@ import {
primaryButton,
textLinkDark,
} from "@/app/components/button-styles";
import HoverPrefetchLink from "@/app/components/HoverPrefetchLink";
import WhatsAppLink from "@/app/components/WhatsAppLink";
import { safeJsonLd } from "@/lib/jsonld";
import { buildBreadcrumbList, buildPageSocialMetadata } from "@/lib/seo";
@@ -53,6 +54,51 @@ const DELIVERABLES = [
},
];
/**
* Como a revisão anda, em três passos, no cartão do hero. Só o que o resto da página já diz: a conversa pelo WhatsApp
* (sem enviar senhas), o teste e a separação do que aproveitar do que refazer, e o orçamento por escrito. Sem prazo e
* sem preço: isso se combina na conversa.
*/
const STEPS = [
{
title: "Conversa pelo WhatsApp",
description:
"Você conta o que o app faz e o que ele precisa fazer. Não envie senhas nem acessos por mensagem.",
},
{
title: "Teste e avaliação",
description:
"Testo se uma pessoa consegue ver ou alterar os dados de outra e separo o que dá para aproveitar do que é melhor refazer.",
},
{
title: "Relatório e orçamento",
description:
"Você recebe o que precisa de correção e o orçamento por escrito para pôr o app em produção.",
},
];
/**
* Três achados de EXEMPLO do relatório, para mostrar como ele é organizado: a falha, a gravidade e a correção sugerida.
* São ilustrativos: não são de nenhum cliente nem de um app real, e a página os rotula assim.
*/
const REPORT_EXAMPLES = [
{
flaw: "Um usuário consegue abrir os dados de outro trocando o número no endereço da página.",
severity: "Alta",
fix: "Conferir no servidor se cada registro pertence a quem está logado e testar com contas de empresas diferentes.",
},
{
flaw: "A chave de acesso ao banco de dados está no código que o navegador baixa.",
severity: "Alta",
fix: "Levar a chave para o servidor, trocar a que ficou exposta e liberar no banco só o que cada perfil precisa.",
},
{
flaw: "Não há cópia de segurança com restauração testada.",
severity: "Média",
fix: "Ligar as cópias automáticas, separar o banco de testes do de produção e ensaiar a restauração.",
},
];
const RESPONSIBILITIES = [
{
title: "Quem pode acessar cada dado?",
@@ -161,24 +207,27 @@ export default function AiPage() {
>
Pedir a revisão do meu app
</WhatsAppLink>
{/* Segue na própria página: os cases do portfólio não são revisões de app feito com IA. */}
<a href="#ia-receber" className={textLinkDark}>
Ver como a revisão funciona
</a>
{/* Link secundário para os cases: mostram como eu entrego, não são revisões de app feito com IA. */}
<HoverPrefetchLink href="/cases" className={textLinkDark}>
Ver projetos entregues
</HoverPrefetchLink>
</div>
</div>
<div
data-testid="ia-review-card"
data-testid="ia-steps-card"
className="min-w-0 border border-white/15 bg-white/[0.04] p-sp-6 sm:p-sp-8"
>
<p className="font-display text-xl font-semibold leading-7 text-white">
O que você recebe
Como funciona em 3 passos
</p>
<ol className="mt-sp-5 flex flex-col">
{DELIVERABLES.map((item, index) => (
{STEPS.map((step, index) => (
<li
key={item.title}
key={step.title}
className="flex items-start gap-sp-4 border-t border-white/12 py-sp-4 first:border-t-0 first:pt-0 last:pb-0"
>
<span
@@ -188,7 +237,10 @@ export default function AiPage() {
{String(index + 1).padStart(2, "0")}
</span>
<span className="min-w-0 font-display text-base leading-6 text-white/90">
{item.title}
<strong className="font-semibold text-white">
{step.title}.
</strong>{" "}
{step.description}
</span>
</li>
))}
@@ -230,6 +282,61 @@ export default function AiPage() {
</div>
</section>
<section
id="ia-modelo"
className="ds-section"
aria-labelledby="ia-modelo-title"
>
<div className="ds-section-shell">
<h2 id="ia-modelo-title" className="ds-section-title max-w-[26ch]">
Modelo do relatório
</h2>
<p className="ds-copy max-w-[65ch]">
Cada falha encontrada entra no relatório com a gravidade e a
correção sugerida. Os três achados abaixo são{" "}
<strong>exemplos ilustrativos</strong> de como ele é organizado: não
são de nenhum cliente nem de um app real.
</p>
<ol className="mt-sp-8 grid gap-sp-6 lg:grid-cols-3">
{REPORT_EXAMPLES.map((example, index) => (
<li key={example.flaw} className="flex">
<article className="ds-card w-full">
<p className="ds-kicker">
Exemplo ilustrativo {String(index + 1).padStart(2, "0")}
</p>
<dl className="mt-sp-4 flex flex-col gap-sp-4">
<div>
<dt className="font-display text-[15px] font-semibold leading-6 text-[var(--ink)]">
Falha
</dt>
<dd className="font-display text-[15px] leading-6 text-[var(--ink-80)]">
{example.flaw}
</dd>
</div>
<div>
<dt className="font-display text-[15px] font-semibold leading-6 text-[var(--ink)]">
Gravidade
</dt>
<dd className="font-display text-[15px] leading-6 text-[var(--ink-80)]">
{example.severity}
</dd>
</div>
<div>
<dt className="font-display text-[15px] font-semibold leading-6 text-[var(--ink)]">
Correção sugerida
</dt>
<dd className="font-display text-[15px] leading-6 text-[var(--ink-80)]">
{example.fix}
</dd>
</div>
</dl>
</article>
</li>
))}
</ol>
</div>
</section>
<section className="ds-section" aria-labelledby="ia-contratar-title">
<div className="ds-section-shell">
<h2 id="ia-contratar-title" className="ds-section-title max-w-[26ch]">