diff --git a/.gitignore b/.gitignore
index a0d7728..faad045 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,6 +47,7 @@ next-env.d.ts
.superpowers/
/.worktrees/
/.impeccable/
+/.serena/
# generated analysis artifacts
/graphify-out/
diff --git a/CREDITS.md b/CREDITS.md
index a72b3c3..91ddc85 100644
--- a/CREDITS.md
+++ b/CREDITS.md
@@ -4,3 +4,8 @@
- `public/assets/prints/*.png` are captures taken with headless Chrome from
production deployments owned by the site author (evandro.dev.br), used as
on-screen content inside the notebook browser surface.
+
+## WhatsApp icon
+- The WhatsApp silhouette in `app/components/Contact.tsx` comes from
+ [Simple Icons](https://github.com/simple-icons/simple-icons/blob/develop/icons/whatsapp.svg).
+ It identifies the contact channel; it does not imply endorsement by WhatsApp.
diff --git a/README.md b/README.md
index a86d8de..0520413 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
-# Evandro.dev.br: portfólio
+# Evandro.dev.br: software sob medida
-Portfólio profissional de Evandro Fonseca Junior, com projetos, cases, serviços, currículo e contato por WhatsApp.
+Site comercial de Evandro Fonseca Junior para projetos de software sob medida: sistemas empresariais, aplicativos e integrações, com cases e contato direto por WhatsApp.
@@ -11,7 +11,7 @@ Portfólio profissional de Evandro Fonseca Junior, com projetos, cases, serviço
- Node.js `22.x`
- pnpm `9.15.9`, fixado em `package.json#packageManager`
-O projeto usa Next.js `16.3.0`. Essa versão é intencional: `16.2.11` ainda mantinha três advisories transitivos de severidade alta; `16.3.0` foi a menor versão estável da mesma major que deixou as auditorias atuais sem vulnerabilidades high.
+O projeto usa Next.js `16.3.3`, patch de segurança que corrige [GHSA-p293-qw3h-jr36](https://github.com/advisories/GHSA-p293-qw3h-jr36) e [GHSA-2xp9-vwfh-vxw4](https://github.com/advisories/GHSA-2xp9-vwfh-vxw4). A atualização substitui `16.3.0`, afetada por esses avisos. As auditorias de produção e desenvolvimento continuam sendo verificadas na CI.
## Início rápido
@@ -45,10 +45,10 @@ corepack pnpm@9.15.9 start
| Área | Pacotes usados |
| --- | --- |
-| Aplicação | Next.js 16.3.0, React 19.1.0, React DOM 19.1.0 e TypeScript 5.9.2. |
+| Aplicação | Next.js 16.3.3, React 19.1.0, React DOM 19.1.0 e TypeScript 5.9.2. |
| Estilos e composição | Tailwind CSS 4, `clsx` e `tailwind-merge`. |
| Movimento | Motion 12 e GSAP 3. |
-| 3D e gráficos | Three.js, React Three Fiber, Drei e Cobe. |
+| 3D e gráficos | Three.js, React Three Fiber e Drei. |
| Interface | Lucide React. |
| Qualidade | Biome, Jest, Testing Library e ambiente JSDOM. |
@@ -58,7 +58,7 @@ Todas as dependências diretas estão fixadas em versões exatas no manifesto; o
| Rota | Conteúdo |
| --- | --- |
-| `/` | Homepage com hero, notebook de projetos, perfil, competências, experiência, projetos, modalidades de trabalho e contato. |
+| `/` | Homepage comercial com hero, notebook 3D, projetos, soluções, processo de trabalho, engenharia com IA, critérios de entrega, perfil e contato. |
| `/cases` | Índice dos seis cases. |
| `/cases/nutriscan` | Case NutriScan. |
| `/cases/twins-lab` | Case Twins Lab. |
@@ -66,18 +66,19 @@ Todas as dependências diretas estão fixadas em versões exatas no manifesto; o
| `/cases/sc-plus` | Case SC Plus Aquicultura. |
| `/cases/gcc-installer` | Case MinGW GCC Installer. |
| `/cases/as-servicos` | Case AS Serviços. |
-| `/servicos` | Índice dos seis serviços, com explicações de uso e acesso ao detalhamento técnico. |
+| `/servicos` | Página de destino comercial: sites, sistemas e aplicativos, contato direto no WhatsApp, cases, processo e acesso aos seis serviços. |
| `/servicos/criacao-de-sites` | Criação de sites. |
| `/servicos/engenharia-de-software` | Sistemas web e aplicativos mobile sob medida. |
+| `/servicos/sob-medida` | Redirecionamento permanente (308) para `/servicos/engenharia-de-software`, preservando query string, UTMs e identificadores de clique. |
| `/servicos/consultoria-nextjs` | Consultoria Next.js. |
| `/servicos/tauri-rust` | Aplicativos desktop para Windows e Linux, com compatibilidade definida por projeto. |
| `/servicos/integracao-hardware-software-iot` | Integração de hardware, software e IoT. |
| `/servicos/arquitetura-de-software` | Arquitetura de software. |
| `/curriculo` | Currículo em HTML, com download de `/Profile.pdf`. |
-O App Router também gera `/manifest.webmanifest`, `/robots.txt`, `/sitemap.xml`, `/opengraph-image`, `/icon.svg` e `/favicon.ico`. Caminhos desconhecidos usam a página 404 do projeto. Não há API route ativa.
+O App Router também gera `/manifest.webmanifest`, `/robots.txt`, `/sitemap.xml`, `/opengraph-image`, `/icon.png`, `/apple-icon.png` e `/favicon.ico`. Caminhos desconhecidos usam a página 404 do projeto. Não há API route ativa.
-As páginas de serviços mantêm escopo, exemplos ilustrativos e etapas visíveis; tecnologias e decisões de implementação ficam em um `` nativo. Sobre e Contato apontam para `/#about` e `/#contact`, com ajuste de chegada após a definição do layout do notebook.
+As páginas de serviços mantêm escopo, exemplos ilustrativos e etapas visíveis; tecnologias e decisões de implementação ficam em um `` nativo. Na homepage, competências e experiência também ficam recolhidas. O header prioriza Soluções (`/#modalities`), Projetos (`/cases`), Como trabalho (`/#process`), Sobre (`/#about`) e Contato (`/#contact`), com ajuste de chegada após a definição do layout do notebook. Currículo e download do PDF são links secundários no final do rodapé.
## Comportamento da aplicação
@@ -87,12 +88,51 @@ O conteúdo principal vem de `public/portfolio-data.json` e `lib/site-content.ts
Em produção, a renderização só lê o snapshot versionado e não consulta demos externas. Em desenvolvimento, um snapshot ausente ou com mais de 15 minutos é atualizado em memória. A atualização manual opcional é `pnpm check:projects` e grava o arquivo por rename atômico. O contrato completo está em [`docs/project-health.md`](docs/project-health.md).
+### Posicionamento comercial e uso de IA
+
+A comunicação apresenta Evandro como responsável direto pelo projeto, sem sugerir equipe, disponibilidade garantida, prazo fixo ou certificação. Os cards de projetos reutilizam os desafios e resumos dos cases quando disponíveis. O antigo painel de telemetria fictícia foi removido; os critérios de qualidade não representam notas de auditoria.
+
+`app/components/EngineeringWithAI.tsx` explica arquitetura, permissões, validação e recuperação de dados, com três relatos históricos de 2025 e links junto de cada afirmação:
+
+- Lovable: exposição relatada pela Semafor e CVE-2025-48757, incluindo a contestação do fornecedor no NVD; exposição não é apresentada como roubo comprovado.
+- Replit: exclusão de dados e restauração integral, conforme a resposta oficial da empresa.
+- Base44: falha de autenticação identificada pela Wiz, corrigida em menos de 24 horas, sem evidência reportada de exploração maliciosa.
+
+As obrigações dos pequenos agentes de tratamento são referenciadas à Resolução CD/ANPD nº 2/2022, arts. 6º, 12 e 13. Ao atualizar esse conteúdo, preservar datas, limites dos relatos e a distinção entre vulnerabilidade, exposição e incidente confirmado.
+
+O formulário valida os campos no navegador e abre um rascunho no WhatsApp para revisão. Ele não envia a mensagem automaticamente e orienta a não informar senhas nem dados pessoais de clientes.
+
+A seção de contato tem título estático único, canais diretos à esquerda e formulário à direita (empilhados no celular). A silhueta do WhatsApp aparece no contato direto, no cabeçalho do formulário e no botão verde. O formulário não se move no hover; campos mantêm rótulos, validação nativa e foco visível. O link direto dispensa preenchimento e o e-mail usa `mailto:`.
+
+Na página `/servicos` e nos seis detalhamentos, `WhatsAppLink` oferece um link nativo com mensagem contextual, sem exigir o formulário da home. Funciona mesmo sem consentimento de analytics ou JavaScript; a medição depende da hidratação e da tag. A página principal prioriza as três intenções dos anúncios antes das especialidades técnicas e usa os cases existentes NutriScan e SC Plus como exemplos de trabalho, não como promessa de retorno comercial.
+
### 3D progressivo e movimento reduzido
-O hero 3D é carregado no cliente a partir de `1024px`, sem `prefers-reduced-motion: reduce` e sem ponteiro coarse. O notebook 3D exige mais de `1024px`, hover disponível e as mesmas condições de movimento e ponteiro. Nos demais casos, o notebook entrega a navegação HTML completa sem WebGL ou movimento. No desktop, tampa, tela, abas e cursor seguem o mesmo progresso de scroll; a cena renderiza sob demanda e pausa fora do viewport ou com a página oculta.
+O hero combina a mensagem comercial com três placas 3D: interface, integrações e dados. A interface usa a captura real do NutriScan, recortada acima do rodapé; as outras camadas são ilustrativas, não uma declaração da arquitetura do projeto. Ponteiro, arraste horizontal e quatro botões permitem explorar a composição. O toque vertical continua rolando a página. A renderização para quando os movimentos terminam, fora do viewport e com a aba oculta. Movimento reduzido ou falha gráfica mantêm a captura HTML, os controles e o link do case disponíveis.
+
+O notebook 3D permanece em celulares, tablets e desktop: tampa, tela, abas e cursor seguem o progresso do scroll. No mobile, a câmera adapta o enquadramento e botões externos à tela do notebook permitem selecionar os projetos. A cena renderiza sob demanda e pausa fora do viewport ou com a página oculta. Movimento reduzido, falha de WebGL ou perda de contexto ativam a navegação HTML sem animação.
As quatro capturas desktop reais ficam em `public/assets/notebook/`, em WebP de 1600 × 1000. A troca aguarda a decodificação da próxima imagem, mantendo a anterior visível durante o carregamento ou uma falha.
+Uma entrada normal na homepage começa no topo. A recarga também volta ao início, removendo o fragmento deixado por uma navegação anterior. Links diretos como `/#about` continuam chegando à seção após a estabilização do notebook; rolagem deliberada durante a abertura cancela esse ajuste. A política inicial de scroll é aplicada antes da hidratação, sem disputar com a rolagem suave dos links posteriores.
+
+### Identidade visual
+
+O header usa `--site-header-height`: 60px abaixo de 1280px e 68px a partir desse breakpoint. A mesma variável define a posição do menu, o respiro inicial do hero e a compensação das âncoras. O menu mobile tem fundo opaco; a barra fica levemente translúcida ao rolar. Header e hero usam Space Grotesk para leitura comercial, reservando a fonte monoespaçada aos detalhes técnicos das demais seções.
+
+Os quatro arquivos recebidos estão preservados, com nomes descritivos, em `imagens/originais/`. As versões prontas para uso ficam em `public/brand/`:
+
+| Arquivo | Aplicação |
+| --- | --- |
+| `wordmark-dark.webp` | Logo horizontal transparente, branca e lima, no cabeçalho escuro. |
+| `wordmark-light.webp` | Logo horizontal transparente, escura e lima, no rodapé claro. |
+| `stacked-light.webp` | Logo empilhada transparente, reservada para composições maiores em fundo claro. |
+| `mark-dark.svg` / `mark-light.svg` | Símbolo vetorial para fundos escuros e claros, traçado a partir do original limpo. |
+| `mark-dark.png` | Símbolo de 512px incorporado aos cards Open Graph de todas as rotas. |
+| `icon-192.png` | Ícone de instalação do manifesto. |
+
+`app/icon.png` (512px), `app/apple-icon.png` (180px) e `app/favicon.ico` (16–256px) usam o símbolo sobre fundo escuro. As variantes mantêm as formas originais; o recorte transparente com bordas danificadas permanece arquivado, sem uso na interface.
+
### Contato
O formulário da homepage valida os campos nativos de nome, e-mail, tipo e contexto. Ao enviar, ele abre `wa.me` em uma nova aba com o briefing preenchido; não existe backend de formulário. O rodapé mantém contato direto por e-mail.
@@ -101,6 +141,22 @@ O formulário da homepage valida os campos nativos de nome, e-mail, tipo e conte
Google Analytics só é carregado depois de aceite explícito. A decisão fica em `evandro.analytics-consent.v1`; recusar não monta o loader. As preferências permanecem acessíveis e a revogação envia consentimento negado, ativa a flag de desabilitação, remove o loader e expira apenas cookies `_ga`/`_ga_*` acessíveis.
+O único loader `gtag.js` configura GA4 `G-QTT9QT0T3S` e Google Ads `AW-18432694647`. Os IDs `GT-M3K4CKQT` e `GT-NBBR3PC2` informados no painel são identificadores das tags, não instalações adicionais. A integração segue https://developers.google.com/tag-platform/gtagjs/configure. `ad_storage`, `ad_user_data` e `ad_personalization` permanecem negados, com `ads_data_redaction` ativo. A tag só é montada em produção.
+
+### Medição do contato e Google Ads
+
+`lib/analytics.ts` envia `whatsapp_open` exclusivamente ao GA4 após o acionamento de um link ou a submissão válida do formulário. O evento representa **tentativa de abrir uma conversa**, não mensagem recebida, lead confirmado ou venda. O contato não espera a resposta do Analytics. Eventos recusados ou ocorridos antes do consentimento não são guardados para envio posterior.
+
+Parâmetros enviados: `service` (rótulo estático), `placement` (posição do CTA) e `page_path`. A URL do evento não inclui query, fragmento ou referenciador; rotas dinâmicas são agrupadas como `/servicos/[slug]` e `/cases/[slug]`. Nome, e-mail, mensagem digitada e URL de destino do WhatsApp não entram nesse evento. A distinção do serviço usa o parâmetro `service`.
+
+Posições: `service_hub_hero`, `service_hub_intent`, `service_hub_footer`, `service_detail_hero`, `service_detail_footer`, `contact_direct` e `contact_form`. Isso permite comparar entradas de contato pelo tipo de serviço e pela posição na página. Cliques pelo menu de contexto, bloqueadores e falhas de rede podem não ser medidos.
+
+**Configuração externa ainda necessária:** verificar `whatsapp_open` no GA4, registrá-lo como evento principal, vincular a propriedade à conta Ads correta e importar essa ação de conversão. Conferir a ação existente antes para evitar dupla contagem; não importar simultaneamente um clique automático como a mesma conversão. Para analisar os parâmetros em Explorações, cadastrar dimensões personalizadas de escopo de evento para `service` e `placement`. Nenhum rótulo de conversão Ads foi inventado e nenhuma configuração da conta foi alterada. A medição otimiza intenção de contato, não vendas.
+
+Também revisar as opções de medição otimizada da propriedade, sobretudo eventos automáticos de cliques externos e formulários, pois essa configuração não é controlada por este código. Referência dos eventos: https://developers.google.com/analytics/devguides/collection/ga4/events.
+
+Verificação local: build de produção, navegação responsiva, redirecionamento com parâmetros e requisição real `whatsapp_open` gerada pela tag. Requisições de coleta foram interceptadas antes do envio para não contaminar os relatórios; isso **não comprova recebimento no GA4 nem atribuição no Ads**.
+
## Estrutura
```text
diff --git a/app/cases/[slug]/page.test.tsx b/app/cases/[slug]/page.test.tsx
index b683ae0..02f0285 100644
--- a/app/cases/[slug]/page.test.tsx
+++ b/app/cases/[slug]/page.test.tsx
@@ -58,16 +58,12 @@ function readJsonLdEntities(container: HTMLElement): Record[] {
}
function expectCaseCtas() {
- expect(
- screen.getByRole("link", {
- name: "Avaliar perfil para contratação",
- }),
- ).toHaveAttribute("href", "/curriculo");
- expect(
- screen.getByRole("link", {
- name: "Solicitar consultoria técnica",
- }),
- ).toHaveAttribute("href", "/#contact");
+ const destinations = screen
+ .getAllByRole("link")
+ .map((link) => link.getAttribute("href"));
+ expect(destinations).toContain("/#contact");
+ expect(destinations).toContain("/servicos");
+ expect(destinations).not.toContain("/curriculo");
}
it("keeps the six public params and factual case metadata", async () => {
diff --git a/app/cases/[slug]/page.tsx b/app/cases/[slug]/page.tsx
index 409310a..5c6c558 100644
--- a/app/cases/[slug]/page.tsx
+++ b/app/cases/[slug]/page.tsx
@@ -236,9 +236,9 @@ export default async function CasePage({ params }: CasePageProps) {
- Sou engenheiro da computação e desenvolvedor de software full-stack,
- atuando ha 5 anos com foco em integrar camadas que normalmente ficam
- separadas: hardware, servicos, operacao e interface. Meu trabalho e
- dar forma clara ao que costuma nascer fragmentado.
+ Meu trabalho conecta interface, serviços, dados e, quando
+ necessário, equipamentos. Entender essas partes em conjunto ajuda a
+ definir o que construir, o que integrar e o que pode ser
+ aproveitado.
+ Nada é enviado automaticamente. O WhatsApp abre com a mensagem
+ para você revisar. Não inclua senhas nem dados pessoais de
+ clientes.
+
-
-
- Tipo de projeto
-
-
-
-
-
- Contexto do projeto
-
-
-
-
- Enviar briefing pelo WhatsApp
-
-
-
-
+
+
+
);
}
diff --git a/app/components/EngineeringWithAI.tsx b/app/components/EngineeringWithAI.tsx
new file mode 100644
index 0000000..8bbeae9
--- /dev/null
+++ b/app/components/EngineeringWithAI.tsx
@@ -0,0 +1,213 @@
+import { Section } from "./Section";
+
+const RESPONSIBILITIES = [
+ {
+ title: "Quem pode acessar cada dado?",
+ description:
+ "Login não basta. Permissões precisam ser verificadas no servidor e testadas entre usuários, empresas e perfis diferentes.",
+ },
+ {
+ title: "Como recuperar a operação?",
+ description:
+ "Ambientes separados, acesso restrito à produção e backups com restauração testada reduzem o impacto de uma alteração errada.",
+ },
+ {
+ title: "Quem valida antes de publicar?",
+ description:
+ "Código gerado também passa por revisão, testes e avaliação de dependências. O escopo inclui manutenção, custos e riscos de operação.",
+ },
+];
+
+export default function EngineeringWithAI() {
+ return (
+
+
+
+ Você contrata engenharia para transformar as regras do seu negócio em
+ um sistema que possa ser testado, operado e mantido. A IA pode fazer
+ parte desse trabalho. Ela não dispensa decisões sobre arquitetura,
+ segurança e responsabilidade pelos dados.
+
+
+
+
+ {RESPONSIBILITIES.map((item) => (
+
+
{item.title}
+
{item.description}
+
+ ))}
+
+
+
+
O que casos documentados ensinam
+
+ São episódios de 2025, não avaliações atuais das plataformas. Eles
+ mostram riscos de configuração, permissões e operação; não provam que
+ todo sistema feito com IA seja inseguro ou que um profissional elimine
+ qualquer risco.
+
+
+
+
+ Dados expostos em aplicações criadas com Lovable
+
+
+
+ Em 29 de maio de 2025, a Semafor noticiou uma análise de Matt
+ Palmer e Kody Low: 170 de 1.645 aplicações examinadas permitiam
+ acesso indevido a informações, incluindo dados de usuários e
+ chaves de API. A reportagem relacionou a exposição à
+ configuração de acesso dos bancos conectados às aplicações.
+
+
+ O registro CVE-2025-48757 descreve políticas insuficientes de
+ acesso por registro no banco. O fornecedor contesta a atribuição
+ da vulnerabilidade à plataforma e ressalta a responsabilidade
+ dos clientes por seus dados. O relato de exposição não comprova
+ roubo por terceiros em todas essas aplicações.
+
+
+ O cuidado técnico: testar se uma pessoa
+ consegue ler ou alterar dados de outra, em vez de considerar a
+ tela de login uma prova de segurança.
+
+
+
+
+
+ Replit: um agente apagou dados de uma aplicação
+
+
+
+ Em julho de 2025, Jason Lemkin, cofundador da SaaStr, relatou
+ que um agente apagou dados durante a construção de uma
+ aplicação. A Replit reconheceu o episódio e informou que o banco
+ foi integralmente recuperado por rollback. Naquele projeto,
+ alterações de desenvolvimento podiam afetar a produção.
+
+
+ A plataforma anunciou separação entre bancos de desenvolvimento
+ e produção por padrão. O cuidado técnico:{" "}
+ limitar permissões de agentes, separar ambientes e testar a
+ recuperação antes de depender dela. Neste caso houve exclusão e
+ recuperação de dados, não roubo de informações.
+
+
+
+
+
+ Base44: aplicações privadas com falha de acesso
+
+
+
+ Em julho de 2025, pesquisadores da Wiz demonstraram uma falha
+ que permitia acesso não autorizado a aplicações privadas do
+ Base44. A falha estava na camada compartilhada de autenticação
+ da plataforma, não em um erro comprovado de um usuário
+ iniciante.
+
+
+ A Wix corrigiu o problema em menos de 24 horas após a
+ notificação e informou não ter encontrado evidência de
+ exploração maliciosa. O cuidado técnico:{" "}
+ avaliar também o fornecedor e as permissões efetivas das
+ integrações, além do código da aplicação.
+
+ Usar IA não afasta as obrigações de proteção de dados. A ANPD exige
+ medidas técnicas e administrativas também de agentes de pequeno
+ porte e esclarece que as flexibilizações não os isentam das demais
+ obrigações da LGPD, como bases legais e direitos dos titulares.
+
+
+ O trabalho técnico inclui controles de acesso, coleta limitada ao
+ necessário e rotinas de retenção e exclusão. Conformidade depende
+ também dos processos da empresa e de orientação jurídica; contratar
+ desenvolvimento não equivale a uma certificação de adequação.
+
+ Seu protótipo ajuda a explicar a ideia. Antes de propor uma
+ reconstrução, avalio o que pode ser aproveitado, quais riscos
+ precisam de correção e o que falta para atender usuários em
+ produção.
+
+
+ Você não precisa saber programar para conduzir o negócio. Precisa
+ entender o que está contratando, acompanhar a entrega e ter clareza
+ sobre quem cuida de cada parte do sistema.
+
+
+ );
+}
diff --git a/app/components/Footer.test.tsx b/app/components/Footer.test.tsx
index 28bde0d..8c9e174 100644
--- a/app/components/Footer.test.tsx
+++ b/app/components/Footer.test.tsx
@@ -2,15 +2,12 @@ import { render, screen } from "@testing-library/react";
import Footer from "./Footer";
describe("Footer contact and navigation", () => {
- it("renders the contact card and a mailto link", () => {
+ it("offers project contact and a direct mailto link", () => {
render();
- expect(screen.getByText("Contato")).toBeInTheDocument();
expect(
- screen.getByRole("heading", {
- name: "Engenharia com menos ruído e mais clareza de execução",
- }),
- ).toBeInTheDocument();
+ screen.getByRole("link", { name: "Conversar sobre meu projeto" }),
+ ).toHaveAttribute("href", "/#contact");
const emailLink = screen.getByRole("link", {
name: "evandrofonsecajunior@gmail.com",
@@ -33,32 +30,32 @@ describe("Footer contact and navigation", () => {
"/privacidade",
);
});
+
+ it("keeps the résumé page and PDF download as the final secondary links", () => {
+ render();
+
+ const résuméLink = screen.getByRole("link", { name: "Currículo" });
+ const downloadLink = screen.getByRole("link", {
+ name: "Baixar currículo (PDF)",
+ });
+ expect(résuméLink).toHaveAttribute("href", "/curriculo");
+ expect(downloadLink).toHaveAttribute("href", "/Profile.pdf");
+ expect(downloadLink).toHaveAttribute(
+ "download",
+ "Evandro_Fonseca_Junior_CV.pdf",
+ );
+ expect(screen.getAllByRole("link").slice(-2)).toEqual([
+ résuméLink,
+ downloadLink,
+ ]);
+ });
});
describe("Footer social links", () => {
- it("renders each social link with an accessible name", () => {
- render();
-
- const socialLinks = Array.from(
- document.querySelectorAll("footer a[aria-label]"),
- );
-
- expect(socialLinks.map((link) => link.getAttribute("aria-label"))).toEqual([
- "Visit GitHub profile",
- "Visit LinkedIn profile",
- "Visit Instagram profile",
- ]);
- expect(
- new Set(socialLinks.map((link) => link.getAttribute("aria-label"))).size,
- ).toBe(socialLinks.length);
- });
-
it("opens social links securely in a new tab", () => {
render();
- const socialLinks = Array.from(
- document.querySelectorAll("footer a[aria-label]"),
- );
+ const socialLinks = screen.getAllByRole("link", { name: /profile/i });
for (const link of socialLinks) {
expect(link).toHaveAttribute("target", "_blank");
@@ -75,16 +72,3 @@ describe("Footer social links", () => {
).toContain("https://www.linkedin.com/in/evandrodevbr/");
});
});
-
-describe("Footer copyright", () => {
- it("renders the current year without hardcoded copyright text", () => {
- render();
-
- const currentYear = new Date().getFullYear();
- expect(
- screen.getByText(
- new RegExp(`Copyright ${currentYear} Evandro Fonseca Junior`),
- ),
- ).toBeInTheDocument();
- });
-});
diff --git a/app/components/Footer.tsx b/app/components/Footer.tsx
index 3c4b109..59b6703 100644
--- a/app/components/Footer.tsx
+++ b/app/components/Footer.tsx
@@ -37,7 +37,9 @@ const quickLinks = [
},
];
+import { CV_DOWNLOAD_NAME, CV_URL } from "@/lib/site-navigation";
import MagneticButton from "./cinematic/MagneticButton";
+import { Logo } from "./Logo";
export default function Footer() {
const currentYear = new Date().getFullYear();
@@ -51,13 +53,21 @@ export default function Footer() {
Contato
- Engenharia com menos ruído e mais clareza de execução
+ Vamos conversar sobre o software que seu negócio precisa?
- Se você precisa de alguém para conectar produto, integração e
- entrega com mais critério técnico, este portfólio é o ponto de
- partida.
+ Conte o que você quer construir, integrar ou melhorar. Podemos
+ discutir o contexto, as prioridades e o escopo antes de definir
+ os próximos passos.
+
+ Conversar sobre meu projeto
+
@@ -66,7 +76,7 @@ export default function Footer() {
evandrofonsecajunior@gmail.com
@@ -75,6 +85,13 @@ export default function Footer() {
Analytics está {consent === "granted" ? "ativo" : "desativado"}.
- Dados de publicidade permanecem negados.
+ Cookies de publicidade e personalização permanecem negados.
- Da presença na internet ao sistema que move seu negócio.
-
-
- Sites e sistemas web, aplicativos mobile e aplicativos desktop
- para Windows e Linux. Também integro ferramentas e melhoro
- software que já está em uso. Começamos pelo que você precisa
- resolver, não pela tecnologia.
+
+
+
+
+
+
+ Início
+
+
+
+ /
+ Serviços
+
+
+
+
+ Sites, sistemas e aplicativos sob medida.
+
+
+ Apresente seu negócio, organize a operação ou conecte ferramentas
+ que hoje dão trabalho. Desenvolvo a solução a partir do que você
+ precisa resolver, não de uma lista de tecnologias.
+
+ Dois projetos com demonstrações publicadas. Os cases mostram o que
+ foi implementado e as tecnologias usadas, sem promessas de resultado
+ comercial.
+
+
+ {featuredCases.map((caseStudy) => (
+
+
+ Demonstração publicada
+
+
{caseStudy.title}
+
{caseStudy.summary}
+
+ Ver o case {caseStudy.title}
+
+
+ ))}
+
-
-
-
O que você precisa fazer?
-
- Conheça as possibilidades abaixo. Cada serviço explica para quem
- faz sentido, exemplos de uso, escopo e etapas de trabalho. Os
- detalhes técnicos ficam disponíveis para aprofundar a conversa.
-
-
-
- {serviceHubLinks.map((service, index) => (
-
-
-
- {String(index + 1).padStart(2, "0")}
-
-
-
- {service.label}
-
-
-
-
{service.description}
-
- ))}
-
-
-
- Ainda não sabe qual caminho seguir?
+
+
+
+ Como saímos da conversa para a entrega.
-
- Conte como funciona sua rotina e o que gostaria de mudar.
- Definimos juntos a primeira entrega, as plataformas atendidas e os
- limites do projeto antes de fechar a proposta.
-
-
- Conversar sobre minha ideia
-
+
+
+ Entender o problema. Conversamos sobre o
+ negócio, quem vai usar e o que já existe.
+
+
+ Combinar o escopo. Definimos prioridades,
+ plataformas, responsabilidades, orçamento e prazo na proposta.
+
+
+ Construir e revisar. Você acompanha as etapas.
+ Validamos a entrega e combinamos publicação, suporte e evolução.
+
+
+
+
Antes de contratar
+
+
+
+ Quanto custa e quanto tempo leva?
+
+
+ Depende das funcionalidades, conteúdo e integrações. O valor e
+ o prazo são definidos depois de entender os requisitos, antes
+ de iniciar o desenvolvimento.
+
+
+
+
+ Preciso estar perto de Garuva?
+
+
+ Não. O atendimento é remoto, com conversas, demonstrações e
+ revisões on-line para projetos em todo o Brasil.
+
+
+
+
+ E se eu já tiver um site ou sistema?
+
+
+ Avalio a base atual para propor melhorias, correções ou
+ integrações. Recomeçar do zero não é uma condição para
+ trabalhar comigo.
+
+
+
+
+
+
+
+
+
+
+ Quer aprofundar o escopo técnico?
+
+
+ Consulte as áreas de atuação, incluindo desktop, integrações,
+ consultoria e IoT. Cada página explica possibilidades e limites.
+
+
+ {serviceHubLinks.map((service) => (
+
+
+ {service.label}
+
+
{service.description}
+
+ ))}
+
+
+
+
+
+
+
+ Conte o que você quer resolver.
+
+
+ Pode ser uma ideia nova, uma rotina manual ou um sistema que precisa
+ melhorar. Me conte pelo WhatsApp; não precisa preencher um
+ formulário para começar.
+