feat: revamp portfolio with case studies, curriculo page, and local SEO
Adds dynamic case study and curriculo routes, project health monitoring for live demo status, and strengthens local SEO (structured data, service-area targeting for Garuva/Joinville/Curitiba, a dedicated "criação de sites" service page) so search results better match the professional title (Engenheiro da Computação) and experience (5 anos). Co-Authored-By: Claude Sonnet 5 <[email protected]>
This commit is contained in:
1 parent
9236fdd685
commit
d76bbf5974
41 files changed
+1359
-492
No files matched your search
+38
-34
@@ -3,6 +3,7 @@
|
||||
import { motion, type Variants } from "framer-motion";
|
||||
import dynamic from "next/dynamic";
|
||||
import { useEffect, useState } from "react";
|
||||
import { shouldLoadHeroScene } from "@/app/lib/should-load-hero-scene";
|
||||
import { buildWhatsAppUrl, getWhatsAppDisplayNumber } from "@/lib/whatsapp";
|
||||
import MagneticButton from "./cinematic/MagneticButton";
|
||||
|
||||
@@ -71,23 +72,25 @@ const HERO_VARIANTS: HeroVariant[] = [
|
||||
weight: 22,
|
||||
scene: "command",
|
||||
layout: "default",
|
||||
status: "Command center online",
|
||||
chip: "Sistemas em producao, sem improviso",
|
||||
status: "Disponível para novas oportunidades",
|
||||
chip: "Garuva · Joinville · Curitiba · Remoto",
|
||||
eyebrow:
|
||||
"Evandro Fonseca Junior | Engenharia para produtos que precisam operar",
|
||||
headline: "Coloque seu sistema no ar com engenharia que nao improvisa.",
|
||||
"Evandro Fonseca Junior | Engenheiro da Computação e Desenvolvedor de Software",
|
||||
headline:
|
||||
"Engenharia de software para sistemas que precisam operar de verdade.",
|
||||
subtitle:
|
||||
"Se voce tem um projeto travado, um MVP para lancar ou um sistema critico precisando operar melhor, agende uma conversa tecnica e saia com o proximo passo claro para producao.",
|
||||
cta: "Botar meu sistema no ar",
|
||||
whatsappMessage: "Ola! Quero colocar meu sistema no ar. Podemos conversar?",
|
||||
sideLabel: "Conversa tecnica pelo WhatsApp",
|
||||
sideTitle: "Seu projeto precisa virar sistema em producao?",
|
||||
"Profissional disponível para oportunidades CLT e PJ em Garuva, Joinville, Norte de Santa Catarina e Curitiba; também atuo em consultoria para produtos, integrações e sistemas críticos.",
|
||||
cta: "Conversar sobre uma oportunidade",
|
||||
whatsappMessage:
|
||||
"Olá! Quero conversar sobre uma oportunidade profissional em engenharia de software.",
|
||||
sideLabel: "Contato profissional direto",
|
||||
sideTitle: "Procurando um engenheiro da computação para o seu time?",
|
||||
sideText:
|
||||
"Me conte objetivo, prazo e contexto. Eu respondo com uma conversa direta para destravar arquitetura, entrega e deploy.",
|
||||
"Vamos conversar sobre o contexto técnico, o desafio da vaga e como posso contribuir da arquitetura à entrega.",
|
||||
points: [
|
||||
"Conversa tecnica para tirar o projeto do papel sem improviso.",
|
||||
"Arquitetura, codigo e deploy conectados ao resultado do negocio.",
|
||||
"Plano claro para colocar o sistema no ar com seguranca.",
|
||||
"Engenharia full-stack com base em sistemas e integrações.",
|
||||
"Next.js, TypeScript, Rust, IoT e arquitetura de software.",
|
||||
"Disponível para times do Norte de SC, Curitiba e atuação remota.",
|
||||
],
|
||||
proof: [
|
||||
{
|
||||
@@ -293,18 +296,6 @@ const LAYOUT_CLASSES: Record<HeroVariant["layout"], string> = {
|
||||
"grid items-center gap-sp-8 lg:grid-cols-[minmax(0,1fr)_minmax(320px,0.72fr)] lg:gap-sp-10",
|
||||
};
|
||||
|
||||
function pickWeightedHeroVariant() {
|
||||
const total = HERO_VARIANTS.reduce((sum, variant) => sum + variant.weight, 0);
|
||||
let ticket = Math.random() * total;
|
||||
|
||||
for (const variant of HERO_VARIANTS) {
|
||||
ticket -= variant.weight;
|
||||
if (ticket <= 0) return variant;
|
||||
}
|
||||
|
||||
return HERO_VARIANTS[0];
|
||||
}
|
||||
|
||||
function getGreeting(message: string) {
|
||||
const hour = new Date().getHours();
|
||||
if (hour >= 8 && hour < 12) return `Bom dia! ${message}`;
|
||||
@@ -436,16 +427,31 @@ function SideVisual({ variant }: { variant: HeroVariant }) {
|
||||
}
|
||||
|
||||
export default function Hero() {
|
||||
const [variant, setVariant] = useState<HeroVariant>(HERO_VARIANTS[0]);
|
||||
const variant = HERO_VARIANTS[0];
|
||||
const [shouldReduceMotion, setShouldReduceMotion] = useState(false);
|
||||
const [shouldRenderHeroScene, setShouldRenderHeroScene] = useState(false);
|
||||
const [whatsAppDisplay, setWhatsAppDisplay] = useState("WhatsApp");
|
||||
|
||||
useEffect(() => {
|
||||
setVariant(pickWeightedHeroVariant());
|
||||
setShouldReduceMotion(
|
||||
window.matchMedia("(prefers-reduced-motion: reduce)").matches,
|
||||
);
|
||||
const reducedMotion = window.matchMedia(
|
||||
"(prefers-reduced-motion: reduce)",
|
||||
).matches;
|
||||
const coarsePointer = window.matchMedia("(pointer: coarse)").matches;
|
||||
setShouldReduceMotion(reducedMotion);
|
||||
setWhatsAppDisplay(getWhatsAppDisplayNumber());
|
||||
|
||||
if (
|
||||
!shouldLoadHeroScene({
|
||||
reducedMotion,
|
||||
coarsePointer,
|
||||
width: window.innerWidth,
|
||||
})
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const timer = window.setTimeout(() => setShouldRenderHeroScene(true), 1500);
|
||||
return () => window.clearTimeout(timer);
|
||||
}, []);
|
||||
|
||||
const handleWhatsAppClick = () => {
|
||||
@@ -505,7 +511,7 @@ export default function Hero() {
|
||||
className="relative flex min-h-[100svh] w-full items-center overflow-hidden bg-[var(--ink)] text-[#f4f2ed]"
|
||||
data-hero-variant={variant.id}
|
||||
>
|
||||
{!shouldReduceMotion && <HeroScene variant={variant.scene} />}
|
||||
{shouldRenderHeroScene && <HeroScene variant={variant.scene} />}
|
||||
|
||||
<div className="pointer-events-none absolute inset-0 z-[1] bg-[radial-gradient(ellipse_80%_60%_at_50%_40%,transparent_0%,rgba(10,10,10,0.4)_50%,var(--ink)_100%)]" />
|
||||
<div className="pointer-events-none absolute inset-0 z-[1] bg-gradient-to-b from-transparent via-transparent to-[var(--ink)]" />
|
||||
@@ -575,9 +581,7 @@ export default function Hero() {
|
||||
className="max-w-[940px] text-balance font-display text-[clamp(3rem,7vw,6.2rem)] font-extrabold leading-[0.9] tracking-[-0.055em]"
|
||||
variants={heroItemSlow}
|
||||
>
|
||||
<span className="cinematic-text-gradient">
|
||||
{variant.headline}
|
||||
</span>
|
||||
{variant.headline}
|
||||
</motion.h1>
|
||||
|
||||
<motion.p
|
||||
|
||||
Reference in new issue
Block a user