feat: Implement initial portfolio website structure with core components and a new post creator tool.
This commit is contained in:
1 parent
036363b521
commit
aa07d6faeb
15 files changed
+1337
-1855
No files matched your search
+62
-166
@@ -1,194 +1,90 @@
|
||||
"use client";
|
||||
import { Section } from "./Section";
|
||||
|
||||
import React from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import { LucideIcon, Brain, Zap, Target, Rocket, Users, Lightbulb, Code, Shield, Terminal, Cpu } from "lucide-react";
|
||||
import Squares from "@/components/Squares";
|
||||
|
||||
// --- Types & Interfaces (Tipagem Forte para Previsibilidade) ---
|
||||
interface StoryStep {
|
||||
icon: LucideIcon;
|
||||
title: string;
|
||||
content: string;
|
||||
gradient: string;
|
||||
accent: string;
|
||||
id: string;
|
||||
}
|
||||
|
||||
interface CoreValue {
|
||||
icon: LucideIcon;
|
||||
title: string;
|
||||
description: string;
|
||||
}
|
||||
|
||||
// --- Data (Desacoplado para facilitar manutenção/CMS) ---
|
||||
const STORY_DATA: StoryStep[] = [
|
||||
const STORY_DATA = [
|
||||
{
|
||||
id: "KERNEL_INIT",
|
||||
icon: Brain,
|
||||
title: "Engenharia de Fundamentos",
|
||||
content: "Minha mentalidade é pautada pela redução de entropia. Não apenas escrevo código; projeto sistemas onde a escalabilidade é uma propriedade intrínseca da arquitetura, não um ajuste posterior.",
|
||||
gradient: "from-blue-600 to-cyan-500",
|
||||
accent: "text-blue-400",
|
||||
title: "Engenharia de fundamentos",
|
||||
content:
|
||||
"Projeto sistemas com foco em previsibilidade, manutenção e crescimento real. Escala entra na arquitetura desde o inÃcio.",
|
||||
},
|
||||
{
|
||||
id: "RUNTIME_EXEC",
|
||||
icon: Zap,
|
||||
title: "Entrega sob Restrições",
|
||||
content: "Em 3 anos de atuação profissional, especializei-me em cenários de alta criticidade. Atuei na ponte entre sistemas legados e microserviços, garantindo a resolução do problema.",
|
||||
gradient: "from-purple-600 to-pink-600",
|
||||
accent: "text-purple-400",
|
||||
title: "Entrega sob restrições",
|
||||
content:
|
||||
"Atuo bem em cenários com legado, prazos apertados e decisões de produto em movimento, sem sacrificar legibilidade técnica.",
|
||||
},
|
||||
];
|
||||
|
||||
const VALUES_DATA: CoreValue[] = [
|
||||
const VALUES_DATA = [
|
||||
{
|
||||
icon: Code,
|
||||
title: "Pragmatismo Arquitetural",
|
||||
description: "Priorizo a manutenibilidade e a resiliência. Código limpo é o meio, sistemas que geram ROI são o fim."
|
||||
title: "Pragmatismo arquitetural",
|
||||
description:
|
||||
"Decisões guiadas por ROI, operabilidade e clareza para o time que mantém o sistema depois.",
|
||||
},
|
||||
{
|
||||
icon: Brain,
|
||||
title: "Fundacionalismo Técnico",
|
||||
description: "Domínio profundo de estruturas de dados e protocolos. Tecnologias mudam, fundamentos são perenes."
|
||||
title: "Fundação técnica",
|
||||
description:
|
||||
"Fundamentos fortes em dados, protocolos e modelagem para evitar soluções frágeis e modismos vazios.",
|
||||
},
|
||||
{
|
||||
icon: Users,
|
||||
title: "Propriedade e Ownership",
|
||||
description: "Comunicação de baixa latência e responsabilidade radical sobre cada entrega e decisão técnica."
|
||||
title: "Ownership real",
|
||||
description:
|
||||
"Responsabilidade sobre entrega, comunicação e qualidade final, não só sobre o trecho de código escrito.",
|
||||
},
|
||||
{
|
||||
icon: Shield,
|
||||
title: "Security by Design",
|
||||
description: "Integridade e conformidade técnica (GDPR/LGPD) tratadas como requisitos funcionais, não opcionais."
|
||||
title: "Security by design",
|
||||
description:
|
||||
"Integridade, proteção de dados e conformidade tratadas como parte do produto, não como remendo.",
|
||||
},
|
||||
];
|
||||
|
||||
// --- Micro-componentes (Feynman Technique: Simplicidade e Reuso) ---
|
||||
|
||||
const SectionHeader = () => (
|
||||
<div className="text-center md:text-left mb-24 relative z-10">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-blue-950/30 border border-blue-500/20 text-blue-400 font-mono text-xs tracking-wider mb-6"
|
||||
>
|
||||
<Terminal className="w-3 h-3" />
|
||||
<span>WHOAMI.sys</span>
|
||||
</motion.div>
|
||||
<motion.h2
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: 0.1 }}
|
||||
className="text-4xl md:text-6xl font-black text-white tracking-tighter"
|
||||
>
|
||||
Engenharia <br />
|
||||
<span className="text-transparent bg-clip-text bg-gradient-to-r from-blue-500 to-cyan-400">Nuclear</span>
|
||||
</motion.h2>
|
||||
<p className="mt-6 text-lg text-slate-400 max-w-2xl leading-relaxed font-light border-l-2 border-slate-800 pl-6">
|
||||
"Transformando complexidade técnica em valor de negócio através da engenharia."
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
|
||||
const StoryCard = ({ step, index }: { step: StoryStep; index: number }) => {
|
||||
const isEven = index % 2 === 0;
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: isEven ? -30 : 30 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
className={`flex flex-col ${isEven ? "md:flex-row" : "md:flex-row-reverse"} items-stretch gap-8 mb-20 relative`}
|
||||
>
|
||||
<div className="flex-1 w-full">
|
||||
<div className="group relative h-full p-8 rounded-3xl bg-zinc-900/40 border border-white/5 hover:border-blue-500/30 transition-all duration-500 shadow-xl backdrop-blur-xl overflow-hidden">
|
||||
{/* Background Glow */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-blue-500/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
|
||||
|
||||
<div className="relative z-10">
|
||||
<div className="flex items-center gap-4 mb-6">
|
||||
<div className="p-3 rounded-xl bg-blue-500/10 border border-blue-500/20 text-blue-400 group-hover:scale-110 transition-transform duration-300">
|
||||
<step.icon className="w-6 h-6" />
|
||||
</div>
|
||||
<span className="text-xs font-mono text-slate-500 uppercase tracking-widest">
|
||||
{step.id}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h3 className="text-2xl font-bold text-white mb-4 group-hover:text-blue-400 transition-colors">{step.title}</h3>
|
||||
<p className="text-slate-400 leading-relaxed text-sm">{step.content}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Connection Line & Node */}
|
||||
<div className="hidden md:flex flex-col items-center justify-center w-12 relative">
|
||||
<div className="absolute top-0 bottom-0 w-[1px] bg-slate-800 group-hover:bg-blue-500/50 transition-colors" />
|
||||
<div className="w-4 h-4 rounded-full bg-slate-900 border-2 border-slate-700 z-10 group-hover:border-blue-500 transition-colors" />
|
||||
</div>
|
||||
|
||||
<div className="flex-1 hidden md:block" />
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
|
||||
export default function About() {
|
||||
return (
|
||||
<section id="about" className="relative py-32">
|
||||
{/* Background Gradients */}
|
||||
<div className="absolute top-0 left-0 w-full h-full pointer-events-none">
|
||||
<div className="absolute top-[-10%] right-[-10%] w-[600px] h-[600px] bg-blue-900/10 rounded-full blur-[120px]" />
|
||||
<div className="absolute bottom-[-10%] left-[-10%] w-[600px] h-[600px] bg-purple-900/10 rounded-full blur-[120px]" />
|
||||
</div>
|
||||
|
||||
<div className="container max-w-6xl mx-auto px-6 relative z-10">
|
||||
<SectionHeader />
|
||||
|
||||
<div className="relative mt-20">
|
||||
{/* Center Line for Mobile/Desktop */}
|
||||
<div className="absolute left-6 md:left-1/2 top-0 bottom-0 w-[1px] bg-slate-800 md:-translate-x-1/2 hidden md:block" />
|
||||
|
||||
{STORY_DATA.map((step, idx) => (
|
||||
<StoryCard key={idx} step={step} index={idx} />
|
||||
<Section
|
||||
id="about"
|
||||
eyebrow="WHOAMI.sys"
|
||||
title="Engenharia orientada por clareza, não por ruÃdo"
|
||||
description="Transformo complexidade técnica em sistemas estáveis, operáveis e alinhados ao negócio."
|
||||
aside={
|
||||
<div className="mt-sp-4 ds-metric">
|
||||
<span className="inline-block h-1.5 w-1.5 rounded-full bg-slash" />
|
||||
Garuva, SC · Remote worldwide
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<div className="ds-grid-2">
|
||||
<div className="ds-grid-2">
|
||||
{STORY_DATA.map((step) => (
|
||||
<article key={step.id} className="ds-card">
|
||||
<div className="ds-kicker">{step.id}</div>
|
||||
<h3 className="ds-card-title">{step.title}</h3>
|
||||
<p className="ds-copy">{step.content}</p>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Grid de Valores com Pareto 80/20 */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 mt-32">
|
||||
{VALUES_DATA.map((val, i) => (
|
||||
<motion.div
|
||||
key={i}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: i * 0.1 }}
|
||||
className="p-6 rounded-2xl bg-zinc-900/30 border border-white/5 hover:border-blue-500/20 hover:bg-zinc-900/50 transition-all duration-300 group"
|
||||
>
|
||||
<div className="mb-4 p-2 w-fit rounded-lg bg-slate-800/50 text-slate-400 group-hover:text-blue-400 group-hover:bg-blue-500/10 transition-colors">
|
||||
<val.icon className="w-5 h-5" />
|
||||
</div>
|
||||
<h4 className="text-white font-bold mb-2 text-sm uppercase tracking-wide group-hover:text-blue-200 transition-colors">{val.title}</h4>
|
||||
<p className="text-slate-500 text-xs leading-relaxed group-hover:text-slate-400 transition-colors">{val.description}</p>
|
||||
</motion.div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* SEO Semantic Content - Visible but subtle */}
|
||||
<div className="mt-24 pt-8 border-t border-slate-900">
|
||||
<div className="flex items-center justify-center gap-2 text-slate-700 text-xs font-mono uppercase tracking-widest mb-4">
|
||||
<Cpu className="w-3 h-3" />
|
||||
<span>System_Signature</span>
|
||||
</div>
|
||||
<p className="text-[10px] text-slate-700 text-center max-w-2xl mx-auto leading-relaxed font-mono">
|
||||
Evandro Fonseca Junior :: Engenheiro Full-Stack <br />
|
||||
Next.js / React / Angular / Node.js / Tauri / PostgreSQL / IoT <br />
|
||||
Garuva, Santa Catarina, Brasil :: Lat: -26.0264, Long: -48.8544
|
||||
<div className="ds-card">
|
||||
<div className="ds-kicker">System Signature</div>
|
||||
<h3 className="ds-card-title">Base técnica e postura de entrega</h3>
|
||||
<p className="ds-copy">
|
||||
Evandro Fonseca Junior. Engenheiro Full-Stack com foco em Next.js,
|
||||
React, Node.js, Tauri, integrações e arquitetura pragmática.
|
||||
</p>
|
||||
<div className="ds-grid-2 mt-sp-6">
|
||||
{VALUES_DATA.map((value) => (
|
||||
<div
|
||||
key={value.title}
|
||||
className="border-t border-[var(--border)] pt-sp-4"
|
||||
>
|
||||
<div className="text-[11px] font-medium uppercase tracking-[0.08em]">
|
||||
{value.title}
|
||||
</div>
|
||||
<p className="ds-copy !mt-sp-2">{value.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
}
|
||||
+98
-198
@@ -1,223 +1,123 @@
|
||||
"use client";
|
||||
"use client";
|
||||
|
||||
import React, { useState } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import { Send, User, Mail, MessageSquare, Sparkles } from "lucide-react";
|
||||
import { ShimmerButton as MagicShimmerButton } from "@/components/magicui/shimmer-button";
|
||||
import Squares from "@/components/Squares";
|
||||
import type React from "react";
|
||||
import { useState } from "react";
|
||||
import { Section } from "./Section";
|
||||
|
||||
// --- CONFIGURAÇÃO DE ENGENHARIA ---
|
||||
// Substitua pelo seu número real: CódigoPaís + DDD + Número (sem espaços ou símbolos)
|
||||
const WHATSAPP_NUMBER = "5547992742365";
|
||||
|
||||
interface FormData {
|
||||
name: string;
|
||||
email: string;
|
||||
subject: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrapper Component: Resolve o erro de tipagem TS2322 (IntrinsicAttributes)
|
||||
* Estendemos a interface para aceitar propriedades de estilo customizadas.
|
||||
*/
|
||||
interface ExtendedShimmerProps extends React.ComponentProps<typeof MagicShimmerButton> {
|
||||
shimmerColor?: string;
|
||||
shimmerSize?: string;
|
||||
type?: "button" | "submit" | "reset";
|
||||
disabled?: boolean;
|
||||
}
|
||||
|
||||
const CustomShimmerButton = ({
|
||||
children,
|
||||
className,
|
||||
shimmerColor = "#3b82f6",
|
||||
shimmerSize = "0.1em",
|
||||
...props
|
||||
}: ExtendedShimmerProps) => {
|
||||
return (
|
||||
<MagicShimmerButton
|
||||
{...props}
|
||||
className={className}
|
||||
// @ts-ignore - Ignora restrição de tipos da lib original se necessário
|
||||
shimmerColor={shimmerColor}
|
||||
shimmerSize={shimmerSize}
|
||||
>
|
||||
{children}
|
||||
</MagicShimmerButton>
|
||||
);
|
||||
};
|
||||
|
||||
export default function Contact() {
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
const [formData, setFormData] = useState<FormData>({
|
||||
const [formData, setFormData] = useState({
|
||||
name: "",
|
||||
email: "",
|
||||
subject: "full-stack",
|
||||
message: "",
|
||||
});
|
||||
|
||||
const handleSubmit = async (e: React.FormEvent) => {
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setIsSubmitting(true);
|
||||
|
||||
await new Promise((resolve) => setTimeout(resolve, 800));
|
||||
|
||||
// Definimos os emojis via Unicode Escape para máxima compatibilidade
|
||||
const icons = {
|
||||
rocket: "\u{1F680}",
|
||||
person: "\u{1F464}",
|
||||
email: "\u{1F4E7}",
|
||||
tools: "\u{1F6E0}",
|
||||
chat: "\u{1F4AC}"
|
||||
};
|
||||
|
||||
const messageTemplate = `*NOVA SOLICITAÇÃO DE PROJETO*
|
||||
|
||||
*Origem:* Portfólio Profissional
|
||||
*Prioridade:* Alta (Lead Qualificado)
|
||||
|
||||
---
|
||||
*CLIENTE:* ${formData.name}
|
||||
*EMAIL:* ${formData.email}
|
||||
*ÁREA:* ${formData.subject.toUpperCase()}
|
||||
|
||||
*DESCRIÇÃO:*
|
||||
"${formData.message}"
|
||||
---
|
||||
_Mensagem gerada via Evandro.dev | Garuva, SC_`;
|
||||
const messageTemplate = `*NOVA SOLICITACAO DE PROJETO*\n\n*Origem:* Portfolio Profissional\n*Prioridade:* Alta\n\n---\n*CLIENTE:* ${formData.name}\n*EMAIL:* ${formData.email}\n*AREA:* ${formData.subject.toUpperCase()}\n\n*DESCRICAO:*\n"${formData.message}"\n---\n_Mensagem gerada via Evandro.dev | Garuva, SC_`;
|
||||
|
||||
const encodedMessage = encodeURIComponent(messageTemplate);
|
||||
const whatsappUrl = `https://wa.me/${WHATSAPP_NUMBER}?text=${encodedMessage}`;
|
||||
|
||||
window.open(whatsappUrl, "_blank", "noopener,noreferrer");
|
||||
setIsSubmitting(false);
|
||||
};
|
||||
|
||||
const inputStyles =
|
||||
"w-full bg-black/40 border border-white/5 rounded-xl px-4 py-3 text-slate-200 placeholder:text-slate-600 focus:border-blue-500/50 focus:ring-2 focus:ring-blue-500/10 focus:bg-black/60 outline-none transition-all duration-300 font-mono text-sm backdrop-blur-sm group-hover:border-white/10";
|
||||
const labelStyles =
|
||||
"text-[10px] font-bold text-slate-500 uppercase tracking-widest mb-2 flex items-center gap-2 transition-colors group-hover:text-slate-400";
|
||||
|
||||
return (
|
||||
<section id="contact" className="w-full py-24 relative">
|
||||
|
||||
|
||||
<div className="w-full max-w-4xl mx-auto px-6 relative z-10">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
transition={{ duration: 0.8 }}
|
||||
className="relative bg-zinc-900/40 backdrop-blur-xl border border-white/10 rounded-3xl p-8 md:p-12 shadow-2xl overflow-hidden"
|
||||
>
|
||||
<div className="absolute top-0 left-0 w-full h-1 bg-gradient-to-r from-blue-500/0 via-blue-500/50 to-blue-500/0" />
|
||||
|
||||
<div className="mb-10 text-center space-y-2">
|
||||
<h2 className="text-3xl md:text-4xl font-bold bg-clip-text text-transparent bg-gradient-to-b from-white to-white/60">
|
||||
Vamos Construir o Futuro
|
||||
</h2>
|
||||
<p className="text-slate-400 text-sm md:text-base max-w-lg mx-auto">
|
||||
Descreva seu desafio técnico. Retorno com uma arquitetura sólida em até 24h.
|
||||
</p>
|
||||
<Section
|
||||
id="contact"
|
||||
eyebrow="CONTACT_PROTOCOL.init"
|
||||
title="Vamos discutir o próximo sistema"
|
||||
description="Descreva o contexto, o gargalo ou o produto. O contato abre direto no WhatsApp com briefing estruturado."
|
||||
dark
|
||||
>
|
||||
<div className="ds-grid-2">
|
||||
<div className="ds-card">
|
||||
<div className="ds-kicker">Response SLA</div>
|
||||
<h3 className="ds-card-title">Retorno inicial em até 24h</h3>
|
||||
<p className="ds-copy">
|
||||
Ideal para discovery técnico, revisão de arquitetura, rescue de
|
||||
projeto, otimização de performance ou construção de produto do
|
||||
zero.
|
||||
</p>
|
||||
<div className="mt-sp-6 space-y-sp-4 text-[12px] leading-7 text-[rgba(255,255,255,.7)]">
|
||||
<div>WhatsApp: +55 47 99274-2365</div>
|
||||
<div>E-mail: evandrofonsecajunior@gmail.com</div>
|
||||
<div>Base: Garuva, Santa Catarina · atuação remota</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="space-y-8 relative">
|
||||
<div className="grid md:grid-cols-2 gap-8">
|
||||
<div className="space-y-3 group">
|
||||
<label className={labelStyles}>
|
||||
<User className="w-3 h-3 text-blue-500" /> NOME COMPLETO
|
||||
</label>
|
||||
<input
|
||||
required
|
||||
type="text"
|
||||
placeholder="Ex: Alan Turing"
|
||||
className={inputStyles}
|
||||
value={formData.name}
|
||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3 group">
|
||||
<label className={labelStyles}>
|
||||
<Mail className="w-3 h-3 text-blue-500" /> E-MAIL CORPORATIVO
|
||||
</label>
|
||||
<input
|
||||
required
|
||||
type="email"
|
||||
placeholder="[email protected]"
|
||||
className={inputStyles}
|
||||
value={formData.email}
|
||||
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3 group">
|
||||
<label className={labelStyles}>
|
||||
<Sparkles className="w-3 h-3 text-blue-500" /> TIPO DE PROJETO
|
||||
</label>
|
||||
<div className="relative">
|
||||
<select
|
||||
className={`${inputStyles} appearance-none cursor-pointer`}
|
||||
value={formData.subject}
|
||||
onChange={(e) => setFormData({ ...formData, subject: e.target.value })}
|
||||
>
|
||||
<option value="full-stack">Desenvolvimento Full-Stack</option>
|
||||
<option value="cloud">Cloud & Infrastructure</option>
|
||||
<option value="iot">Internet of Things (IoT)</option>
|
||||
<option value="consultancy">Consultoria Técnica Sênior</option>
|
||||
</select>
|
||||
<div className="absolute right-4 top-1/2 -translate-y-1/2 pointer-events-none text-slate-500">
|
||||
<svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="m6 9 6 6 6-6" /></svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="space-y-3 group">
|
||||
<label className={labelStyles}>
|
||||
<MessageSquare className="w-3 h-3 text-blue-500" /> DETALHES DO PROJETO
|
||||
</label>
|
||||
<textarea
|
||||
required
|
||||
rows={5}
|
||||
placeholder="Descreva os requisitos técnicos e objetivos do sistema..."
|
||||
className={`${inputStyles} resize-none`}
|
||||
value={formData.message}
|
||||
onChange={(e) => setFormData({ ...formData, message: e.target.value })}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<motion.div whileHover={{ scale: 1.01 }} whileTap={{ scale: 0.99 }} className="pt-6">
|
||||
<CustomShimmerButton
|
||||
type="submit"
|
||||
className="w-full h-14 rounded-xl shadow-lg shadow-blue-500/20"
|
||||
shimmerColor="#60a5fa"
|
||||
shimmerSize="0.1em"
|
||||
disabled={isSubmitting}
|
||||
>
|
||||
<div className="flex flex-row items-center justify-center gap-3 w-full h-full">
|
||||
{isSubmitting ? (
|
||||
<div className="w-5 h-5 border-2 border-white/30 border-t-white rounded-full animate-spin" />
|
||||
) : (
|
||||
<>
|
||||
<span className="text-white font-bold tracking-widest uppercase text-xs md:text-sm">
|
||||
Iniciar Protocolo de Contato
|
||||
</span>
|
||||
<Send className="w-4 h-4 text-white" />
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</CustomShimmerButton>
|
||||
</motion.div>
|
||||
|
||||
<footer className="pt-2">
|
||||
<p className="text-[10px] text-center text-slate-600 font-mono uppercase tracking-widest opacity-60">
|
||||
Secured by End-to-End Encryption
|
||||
</p>
|
||||
</footer>
|
||||
</form>
|
||||
</motion.div>
|
||||
<form onSubmit={handleSubmit} className="ds-card space-y-sp-5">
|
||||
<div>
|
||||
<label htmlFor="contact-name" className="ds-kicker block">
|
||||
Nome completo
|
||||
</label>
|
||||
<input
|
||||
id="contact-name"
|
||||
required
|
||||
type="text"
|
||||
value={formData.name}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, name: e.target.value })
|
||||
}
|
||||
className="mt-sp-2 w-full border border-[rgba(255,255,255,.12)] bg-[rgba(255,255,255,.03)] px-sp-4 py-[14px] text-[12px] text-white outline-none transition-colors focus:border-slash"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="contact-email" className="ds-kicker block">
|
||||
E-mail corporativo
|
||||
</label>
|
||||
<input
|
||||
id="contact-email"
|
||||
required
|
||||
type="email"
|
||||
value={formData.email}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, email: e.target.value })
|
||||
}
|
||||
className="mt-sp-2 w-full border border-[rgba(255,255,255,.12)] bg-[rgba(255,255,255,.03)] px-sp-4 py-[14px] text-[12px] text-white outline-none transition-colors focus:border-slash"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="contact-subject" className="ds-kicker block">
|
||||
Tipo de projeto
|
||||
</label>
|
||||
<select
|
||||
id="contact-subject"
|
||||
value={formData.subject}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, subject: e.target.value })
|
||||
}
|
||||
className="mt-sp-2 w-full border border-[rgba(255,255,255,.12)] bg-[rgba(255,255,255,.03)] px-sp-4 py-[14px] text-[12px] text-white outline-none transition-colors focus:border-slash"
|
||||
>
|
||||
<option value="full-stack">Desenvolvimento Full-Stack</option>
|
||||
<option value="cloud">Cloud & Infrastructure</option>
|
||||
<option value="iot">Internet of Things (IoT)</option>
|
||||
<option value="consultancy">Consultoria Técnica Sênior</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label htmlFor="contact-message" className="ds-kicker block">
|
||||
Detalhes do projeto
|
||||
</label>
|
||||
<textarea
|
||||
id="contact-message"
|
||||
required
|
||||
rows={6}
|
||||
value={formData.message}
|
||||
onChange={(e) =>
|
||||
setFormData({ ...formData, message: e.target.value })
|
||||
}
|
||||
className="mt-sp-2 w-full resize-none border border-[rgba(255,255,255,.12)] bg-[rgba(255,255,255,.03)] px-sp-4 py-[14px] text-[12px] text-white outline-none transition-colors focus:border-slash"
|
||||
/>
|
||||
</div>
|
||||
<button type="submit" className="ds-link ds-link-accent border-0">
|
||||
Iniciar protocolo de contato
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</section>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
}
|
||||
+44
-170
@@ -1,194 +1,68 @@
|
||||
"use client";
|
||||
|
||||
import React, { useMemo } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import {
|
||||
ChevronRight,
|
||||
Terminal,
|
||||
Cpu,
|
||||
Network,
|
||||
Code2
|
||||
} from "lucide-react";
|
||||
import { useMemo } from "react";
|
||||
import { usePortfolioData } from "@/lib/usePortfolioData";
|
||||
|
||||
// --- Tipagem e Configuração (Otimização Pareto 80/20) ---
|
||||
const CONFIG = {
|
||||
"innovo-tecnologia": { color: "text-blue-400", bg: "bg-blue-400/10", border: "border-blue-400/20", icon: Cpu },
|
||||
"apolo-energia": { color: "text-emerald-400", bg: "bg-emerald-400/10", border: "border-emerald-400/20", icon: Network },
|
||||
default: { color: "text-slate-400", bg: "bg-slate-400/10", border: "border-slate-400/20", icon: Code2 }
|
||||
};
|
||||
import { Section } from "./Section";
|
||||
|
||||
export default function Experience() {
|
||||
const { data, loading, error } = usePortfolioData();
|
||||
|
||||
// Memória: Evita re-processamento de regex em cada re-render (Princípio de Eficiência)
|
||||
const sortedExperiences = useMemo(() => {
|
||||
if (!data?.experiences) return [];
|
||||
return [...data.experiences]
|
||||
.filter((exp) => exp.featured)
|
||||
.sort((a, b) => {
|
||||
const yearA = parseInt(a.period.match(/\d{4}/)?.[0] || "0");
|
||||
const yearB = parseInt(b.period.match(/\d{4}/)?.[0] || "0");
|
||||
return yearB - yearA; // Da mais recente para a mais antiga (Padrão Senior)
|
||||
const yearA = parseInt(a.period.match(/\d{4}/)?.[0] || "0", 10);
|
||||
const yearB = parseInt(b.period.match(/\d{4}/)?.[0] || "0", 10);
|
||||
return yearB - yearA;
|
||||
});
|
||||
}, [data]);
|
||||
|
||||
if (loading) return <ExperienceSkeleton />;
|
||||
if (loading) return <div className="ds-loading">Loading system logs</div>;
|
||||
if (error || !data)
|
||||
return <div className="ds-error">Error loading system logs</div>;
|
||||
|
||||
return (
|
||||
<section id="experience" className="relative py-32">
|
||||
{/* Background Gradients */}
|
||||
<div className="absolute top-0 left-0 w-full h-full pointer-events-none">
|
||||
<div className="absolute top-[20%] left-[-10%] w-[500px] h-[500px] bg-blue-900/10 rounded-full blur-[100px]" />
|
||||
<div className="absolute bottom-[20%] right-[-10%] w-[500px] h-[500px] bg-indigo-900/10 rounded-full blur-[100px]" />
|
||||
</div>
|
||||
|
||||
<div className="max-w-6xl mx-auto px-6 relative z-10">
|
||||
|
||||
{/* Header com Identidade Visual */}
|
||||
<div className="mb-24 text-center md:text-left">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-blue-950/30 border border-blue-500/20 text-blue-400 font-mono text-xs tracking-wider mb-6"
|
||||
>
|
||||
<Terminal className="w-3 h-3" />
|
||||
<span>SYSTEM_LOGS.history</span>
|
||||
</motion.div>
|
||||
<motion.h2
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: 0.1 }}
|
||||
className="text-4xl md:text-6xl font-black text-white tracking-tighter"
|
||||
>
|
||||
Trajetória <span className="bg-gradient-to-r from-blue-500 to-indigo-500 bg-clip-text text-transparent">Profissional</span>
|
||||
</motion.h2>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
{/* Timeline Desktop (Linha Central com Gradiente) */}
|
||||
<div className="absolute left-[50%] top-0 bottom-0 w-[2px] bg-gradient-to-b from-blue-500/50 via-indigo-500/20 to-transparent hidden lg:block translate-x-[-50%]" />
|
||||
|
||||
<div className="space-y-24">
|
||||
{sortedExperiences.map((exp, index) => (
|
||||
<ExperienceCard key={exp.id} exp={exp} index={index} />
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
// --- Sub-componente Card (Encapsulamento de Lógica de Transição) ---
|
||||
function ExperienceCard({ exp, index }: { exp: any, index: number }) {
|
||||
const isEven = index % 2 === 0;
|
||||
const config = (CONFIG as any)[exp.company.toLowerCase().replace(/\s+/g, "-")] || CONFIG.default;
|
||||
const Icon = config.icon;
|
||||
|
||||
return (
|
||||
<div className={`relative flex flex-col ${isEven ? 'lg:flex-row' : 'lg:flex-row-reverse'} items-center gap-8 lg:gap-16`}>
|
||||
|
||||
{/* Node Central (O "Ponto" da Timeline) */}
|
||||
<motion.div
|
||||
initial={{ scale: 0, opacity: 0 }}
|
||||
whileInView={{ scale: 1, opacity: 1 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: 0.2, duration: 0.5 }}
|
||||
className="hidden lg:flex absolute left-[50%] top-0 translate-x-[-50%] w-4 h-4 rounded-full bg-blue-500 shadow-[0_0_20px_rgba(59,130,246,0.5)] z-20 items-center justify-center"
|
||||
>
|
||||
<div className="w-2 h-2 rounded-full bg-white animate-pulse" />
|
||||
</motion.div>
|
||||
|
||||
{/* Indicador de Data na Timeline */}
|
||||
<div className="hidden lg:flex flex-1 justify-end items-start pt-2">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: isEven ? -20 : 20 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: 0.3 }}
|
||||
className={`${isEven ? 'text-right' : 'text-left'} flex-1`}
|
||||
>
|
||||
<span className="text-4xl font-black text-transparent bg-clip-text bg-gradient-to-b from-slate-700 to-slate-800 tracking-tighter opacity-50 select-none">
|
||||
{exp.period.split(' ')[0]}
|
||||
</span>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Card Principal */}
|
||||
<div className="flex-1 w-full relative group perspective-1000">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: isEven ? 50 : -50 }}
|
||||
whileInView={{ opacity: 1, x: 0 }}
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
transition={{ duration: 0.6, delay: 0.1 * index }}
|
||||
whileHover={{ y: -5, transition: { duration: 0.2 } }}
|
||||
className={`p-8 rounded-3xl bg-zinc-900/40 border border-white/5 backdrop-blur-md relative overflow-hidden transition-all duration-300 hover:shadow-[0_0_30px_rgba(59,130,246,0.1)] hover:border-blue-500/20`}
|
||||
>
|
||||
{/* Background Glow Effect on Hover */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-blue-500/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
|
||||
|
||||
{/* Badge de Empresa & Header */}
|
||||
<div className="relative z-10">
|
||||
<div className="flex flex-wrap justify-between items-start gap-4 mb-6">
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className={`p-2 rounded-lg ${config.bg} ${config.color}`}>
|
||||
<Icon className="w-5 h-5" />
|
||||
</div>
|
||||
<span className={`text-xs font-bold uppercase tracking-widest ${config.color}`}>
|
||||
{exp.company}
|
||||
</span>
|
||||
<Section
|
||||
id="experience"
|
||||
eyebrow="SYSTEM_LOGS.history"
|
||||
title="Trajetória focada em produto, legado e crescimento sustentável"
|
||||
description="Experiências recentes onde combinei entrega rápida com responsabilidade arquitetural."
|
||||
>
|
||||
<div className="space-y-sp-6">
|
||||
{sortedExperiences.map((exp) => (
|
||||
<article key={exp.id} className="ds-card">
|
||||
<div className="ds-grid-2">
|
||||
<div>
|
||||
<div className="ds-kicker">{exp.period}</div>
|
||||
<h3 className="ds-card-title">{exp.title}</h3>
|
||||
<div className="mt-sp-3 text-[11px] uppercase tracking-[0.1em] text-meta">
|
||||
{exp.company} · {exp.type}
|
||||
</div>
|
||||
<h3 className="text-2xl font-bold text-white group-hover:text-blue-400 transition-colors">
|
||||
{exp.title}
|
||||
</h3>
|
||||
</div>
|
||||
|
||||
<div className="px-3 py-1 rounded-full bg-white/5 border border-white/10 text-[10px] font-mono font-bold text-slate-400 uppercase tracking-wide">
|
||||
{exp.period}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p className="text-slate-400 leading-relaxed mb-8 text-sm">
|
||||
{exp.description}
|
||||
</p>
|
||||
|
||||
{/* Conquistas (Checklist Técnico) */}
|
||||
<div className="grid grid-cols-1 gap-3 mb-8">
|
||||
{exp.achievements?.map((ach: string, i: number) => (
|
||||
<div key={i} className="flex gap-3 text-sm text-slate-300 items-start">
|
||||
<div className="mt-1.5 w-1.5 h-1.5 rounded-full bg-blue-500 flex-shrink-0" />
|
||||
<span>{ach}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Tags de Tech */}
|
||||
<div className="flex flex-wrap gap-2 pt-6 border-t border-white/5">
|
||||
{exp.technologies?.map((tech: string) => (
|
||||
<span key={tech} className="px-3 py-1 bg-black/20 border border-white/5 text-slate-500 text-[10px] rounded-full font-mono hover:text-blue-400 hover:border-blue-400/30 transition-all cursor-default">
|
||||
{tech}
|
||||
<div className="flex flex-wrap items-start justify-start gap-sp-3 lg:justify-end">
|
||||
<span className="ds-tag">{exp.location}</span>
|
||||
<span className="ds-tag">
|
||||
{exp.technologies.length} tecnologias
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
<p className="ds-copy">{exp.description}</p>
|
||||
<ul className="ds-list">
|
||||
{exp.achievements?.map((achievement) => (
|
||||
<li key={achievement}>{achievement}</li>
|
||||
))}
|
||||
</ul>
|
||||
<ul className="ds-tags mt-sp-6">
|
||||
{exp.technologies?.map((tech) => (
|
||||
<li key={tech} className="ds-tag">
|
||||
{tech}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Spacer para Timeline Desktop */}
|
||||
<div className="hidden lg:block flex-1" />
|
||||
</div>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
function ExperienceSkeleton() {
|
||||
return (
|
||||
<div className="h-96 flex flex-col items-center justify-center gap-4 text-slate-600 font-mono animate-pulse">
|
||||
<Cpu className="w-8 h-8 animate-spin" />
|
||||
<span>BOOTING_SYSTEM_HISTORY...</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+106
-150
@@ -1,171 +1,127 @@
|
||||
"use client";
|
||||
"use client";
|
||||
|
||||
import React, { useState, useEffect } from "react";
|
||||
import { motion, AnimatePresence } from "framer-motion";
|
||||
import { MenuIcon, XIcon } from "lucide-react";
|
||||
import Image from "next/image";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useState } from "react";
|
||||
import { Logo } from "./Logo";
|
||||
|
||||
// Definição de Tipos para consistência de dados
|
||||
interface NavItem {
|
||||
href: string;
|
||||
label: string;
|
||||
}
|
||||
|
||||
const navItems: NavItem[] = [
|
||||
const navItems = [
|
||||
{ href: "#home", label: "Home" },
|
||||
{ href: "#about", label: "Sobre" },
|
||||
{ href: "#skills", label: "Habilidades" },
|
||||
{ href: "#experience", label: "Experiência" },
|
||||
{ href: "#experience", label: "Experiencia" },
|
||||
{ href: "#projects", label: "Projetos" },
|
||||
{ href: "#modalities", label: "Contratacao" },
|
||||
{ href: "#contact", label: "Contato" },
|
||||
];
|
||||
|
||||
export function Header() {
|
||||
const [isMenuOpen, setIsMenuOpen] = useState(false);
|
||||
const [scrolled, setScrolled] = useState(false);
|
||||
|
||||
// Hook de detecção de scroll para mudança de estado do Header
|
||||
useEffect(() => {
|
||||
const handleScroll = () => {
|
||||
setScrolled(window.scrollY > 20);
|
||||
};
|
||||
window.addEventListener("scroll", handleScroll);
|
||||
return () => window.removeEventListener("scroll", handleScroll);
|
||||
}, []);
|
||||
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
|
||||
|
||||
return (
|
||||
<>
|
||||
<motion.header
|
||||
initial={{ y: -100 }}
|
||||
animate={{ y: 0 }}
|
||||
className={cn(
|
||||
"fixed top-0 left-0 right-0 z-[100] transition-all duration-500 ease-in-out px-6 py-4",
|
||||
scrolled
|
||||
? "bg-black/40 backdrop-blur-xl border-b border-white/5 py-3"
|
||||
: "bg-transparent py-6"
|
||||
)}
|
||||
>
|
||||
<nav className="max-w-7xl mx-auto flex items-center justify-between">
|
||||
{/* Logo com Identidade Visual de Engenheiro */}
|
||||
<motion.a
|
||||
href="#home"
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
const element = document.querySelector("#home");
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
}
|
||||
}}
|
||||
whileHover={{ scale: 1.05 }}
|
||||
className="flex items-center gap-2 cursor-pointer"
|
||||
<header
|
||||
className="fixed left-0 right-0 top-0 z-50 border-b backdrop-blur-xl"
|
||||
style={{
|
||||
borderColor: "rgba(255,255,255,.1)",
|
||||
background: "rgba(10,10,10,.96)",
|
||||
}}
|
||||
>
|
||||
<div className="mx-auto flex h-[52px] w-full max-w-[1240px] items-center justify-between px-6 lg:px-16">
|
||||
<a href="#home" className="flex items-center gap-3 no-underline">
|
||||
<Logo variant="mark" size="sm" theme="dark" />
|
||||
<span
|
||||
className="font-display text-sm font-extrabold tracking-tight"
|
||||
style={{ color: "#FFFFFF" }}
|
||||
>
|
||||
<div className="w-8 h-8 flex items-center justify-center">
|
||||
<Image
|
||||
src="/edev_logo.svg"
|
||||
alt="Evandro.dev Logo"
|
||||
width={32}
|
||||
height={32}
|
||||
className="w-8 h-8"
|
||||
priority
|
||||
/>
|
||||
</div>
|
||||
<span className="text-xl font-black tracking-tighter text-white">
|
||||
EVANDRO<span className="text-blue-500">.DEV</span>
|
||||
</span>
|
||||
</motion.a>
|
||||
evandro<span style={{ color: "#E8FF00" }}>.dev.br</span>
|
||||
</span>
|
||||
</a>
|
||||
|
||||
{/* Desktop Navigation - Focada em Espaçamento Ótimo */}
|
||||
<div className="hidden md:flex items-center gap-1 bg-white/5 p-1 rounded-full border border-white/10 backdrop-blur-md">
|
||||
<nav className="hidden items-center lg:flex">
|
||||
<ul className="flex list-none items-center gap-2">
|
||||
{navItems.map((item) => (
|
||||
<a
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
const element = document.querySelector(item.href);
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
}
|
||||
}}
|
||||
className="px-5 py-2 text-xs font-bold uppercase tracking-widest text-slate-300 hover:text-white hover:bg-white/10 rounded-full transition-all duration-300 cursor-pointer"
|
||||
>
|
||||
{item.label}
|
||||
</a>
|
||||
<li key={item.href}>
|
||||
<a
|
||||
href={item.href}
|
||||
className="rounded-sm px-3 py-2 text-[10px] uppercase tracking-[0.14em] no-underline transition-all duration-200 hover:bg-white/10"
|
||||
style={{ color: "rgba(255,255,255,.86)" }}
|
||||
>
|
||||
{item.label}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Botão de Ação CTA (Call to Action) */}
|
||||
<div className="hidden md:block">
|
||||
<a
|
||||
href="/Profile.pdf"
|
||||
download="Evandro_Fonseca_Junior_CV.pdf"
|
||||
className="px-6 py-2 bg-white text-black text-xs font-bold uppercase tracking-widest rounded-full hover:bg-blue-500 hover:text-white transition-all duration-500 inline-block"
|
||||
>
|
||||
Download CV
|
||||
</a>
|
||||
</div>
|
||||
|
||||
{/* Mobile Toggle */}
|
||||
<button
|
||||
onClick={() => setIsMenuOpen(!isMenuOpen)}
|
||||
className="md:hidden p-2 text-white bg-white/5 rounded-lg border border-white/10"
|
||||
>
|
||||
{isMenuOpen ? <XIcon size={20} /> : <MenuIcon size={20} />}
|
||||
</button>
|
||||
</ul>
|
||||
</nav>
|
||||
</motion.header>
|
||||
|
||||
{/* Mobile Menu Overlay com Framer Motion para fluidez */}
|
||||
<AnimatePresence>
|
||||
{isMenuOpen && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: "100%" }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
exit={{ opacity: 0, x: "100%" }}
|
||||
transition={{ type: "spring", damping: 25, stiffness: 200 }}
|
||||
className="fixed inset-0 z-[110] bg-black/95 backdrop-blur-2xl md:hidden flex flex-col items-center justify-center gap-8"
|
||||
<div className="flex items-center gap-4">
|
||||
<span
|
||||
className="hidden rounded-full border px-[10px] py-1 text-[9px] uppercase tracking-[0.12em] lg:inline-block"
|
||||
style={{
|
||||
borderColor: "rgba(255,255,255,.15)",
|
||||
color: "rgba(255,255,255,.35)",
|
||||
}}
|
||||
>
|
||||
<button
|
||||
onClick={() => setIsMenuOpen(false)}
|
||||
className="absolute top-8 right-8 p-4 text-white/50 hover:text-white"
|
||||
>
|
||||
<XIcon size={32} />
|
||||
</button>
|
||||
|
||||
{navItems.map((item, i) => (
|
||||
<motion.a
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: i * 0.1 }}
|
||||
onClick={(e) => {
|
||||
e.preventDefault();
|
||||
setIsMenuOpen(false);
|
||||
const element = document.querySelector(item.href);
|
||||
if (element) {
|
||||
element.scrollIntoView({ behavior: "smooth", block: "start" });
|
||||
}
|
||||
}}
|
||||
className="text-3xl font-black text-white uppercase tracking-tighter hover:text-blue-500 transition-colors cursor-pointer"
|
||||
>
|
||||
{item.label}
|
||||
</motion.a>
|
||||
v1.0 / 2026
|
||||
</span>
|
||||
<a
|
||||
href="/Profile.pdf"
|
||||
download="Evandro_Fonseca_Junior_CV.pdf"
|
||||
className="hidden rounded-sm px-4 py-2 text-[10px] font-semibold uppercase tracking-[0.1em] no-underline transition-all duration-200 hover:shadow-[0_0_0_1px_rgba(232,255,0,.8),0_8px_24px_rgba(232,255,0,.35)] lg:inline-flex"
|
||||
style={{ background: "#E8FF00", color: "#0A0A0A" }}
|
||||
>
|
||||
Download CV
|
||||
</a>
|
||||
<button
|
||||
type="button"
|
||||
className="flex h-8 w-8 flex-col items-center justify-center gap-1.5 border-none bg-transparent lg:hidden"
|
||||
onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
|
||||
aria-label="Menu"
|
||||
>
|
||||
<span
|
||||
className={`h-0.5 w-5 bg-white transition-transform duration-200 ${mobileMenuOpen ? "translate-y-2 rotate-45" : ""}`}
|
||||
/>
|
||||
<span
|
||||
className={`h-0.5 w-5 bg-white transition-opacity duration-200 ${mobileMenuOpen ? "opacity-0" : ""}`}
|
||||
/>
|
||||
<span
|
||||
className={`h-0.5 w-5 bg-white transition-transform duration-200 ${mobileMenuOpen ? "-translate-y-2 -rotate-45" : ""}`}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
className={`absolute left-0 right-0 top-[52px] overflow-hidden border-b backdrop-blur-xl transition-all duration-300 lg:hidden ${mobileMenuOpen ? "max-h-[420px] opacity-100" : "max-h-0 opacity-0"}`}
|
||||
style={{
|
||||
borderColor: "rgba(255,255,255,.1)",
|
||||
background: "rgba(10,10,10,.98)",
|
||||
}}
|
||||
>
|
||||
<nav className="px-6 py-4">
|
||||
<ul className="flex list-none flex-col gap-2">
|
||||
{navItems.map((item) => (
|
||||
<li key={item.href}>
|
||||
<a
|
||||
href={item.href}
|
||||
className="block rounded-sm px-3 py-3 text-[11px] uppercase tracking-[0.14em] no-underline transition-all duration-200 hover:bg-white/10"
|
||||
style={{ color: "rgba(255,255,255,.9)" }}
|
||||
onClick={() => setMobileMenuOpen(false)}
|
||||
>
|
||||
{item.label}
|
||||
</a>
|
||||
</li>
|
||||
))}
|
||||
<motion.a
|
||||
href="/Profile.pdf"
|
||||
download="Evandro_Fonseca_Junior_CV.pdf"
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
transition={{ delay: navItems.length * 0.1 }}
|
||||
className="mt-8 px-8 py-4 bg-white text-black text-xl font-black uppercase tracking-tighter rounded-full hover:bg-blue-500 hover:text-white transition-all duration-300"
|
||||
>
|
||||
Download CV
|
||||
</motion.a>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</>
|
||||
<li>
|
||||
<a
|
||||
href="/Profile.pdf"
|
||||
download="Evandro_Fonseca_Junior_CV.pdf"
|
||||
className="mt-2 inline-flex rounded-sm px-4 py-3 text-[10px] font-semibold uppercase tracking-[0.1em] no-underline"
|
||||
style={{ background: "#E8FF00", color: "#0A0A0A" }}
|
||||
>
|
||||
Download CV
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
);
|
||||
}
|
||||
}
|
||||
+199
-124
@@ -1,145 +1,220 @@
|
||||
"use client";
|
||||
"use client";
|
||||
|
||||
import { motion } from "motion/react";
|
||||
import Image from "next/image";
|
||||
import { AuroraText } from "@/components/magicui/aurora-text";
|
||||
import { AnimatedGradientText } from "@/components/magicui/animated-gradient-text";
|
||||
import { Github, Linkedin, MessageCircle, Mail, MapPin, Terminal } from "lucide-react";
|
||||
import TextType from "@/components/TextType";
|
||||
import { Logo } from "./Logo";
|
||||
|
||||
const SOCIAL_LINKS = [
|
||||
{
|
||||
href: "https://github.com/evandrodevbr",
|
||||
label: "GitHub",
|
||||
icon: (
|
||||
<path d="M12 0c-6.626 0-12 5.373-12 12 0 5.302 3.438 9.8 8.207 11.387.599.111.793-.261.793-.577v-2.234c-3.338.726-4.033-1.416-4.033-1.416-.546-1.387-1.333-1.756-1.333-1.756-1.089-.745.083-.729.083-.729 1.205.084 1.839 1.237 1.839 1.237 1.07 1.834 2.807 1.304 3.492.997.107-.775.418-1.305.762-1.604-2.665-.305-5.467-1.334-5.467-5.931 0-1.311.469-2.381 1.236-3.221-.124-.303-.535-1.524.117-3.176 0 0 1.008-.322 3.301 1.23.957-.266 1.983-.399 3.003-.404 1.02.005 2.047.138 3.006.404 2.291-1.552 3.297-1.23 3.297-1.23.653 1.653.242 2.874.118 3.176.77.84 1.235 1.911 1.235 3.221 0 4.609-2.807 5.624-5.479 5.921.43.372.823 1.102.823 2.222v3.293c0 .319.192.694.801.576 4.765-1.589 8.199-6.086 8.199-11.386 0-6.627-5.373-12-12-12z" />
|
||||
),
|
||||
},
|
||||
{
|
||||
href: "https://www.linkedin.com/in/evandro-fonseca-junior-0969aa1b0/",
|
||||
label: "LinkedIn",
|
||||
icon: (
|
||||
<path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z" />
|
||||
),
|
||||
},
|
||||
{
|
||||
href: "mailto:[email protected]",
|
||||
label: "Email",
|
||||
icon: (
|
||||
<>
|
||||
<path d="M4 4h16c1.1 0 2 .9 2 2v12c0 1.1-.9 2-2 2H4c-1.1 0-2-.9-2-2V6c0-1.1.9-2 2-2z" />
|
||||
<polyline points="22,6 12,13 2,6" />
|
||||
</>
|
||||
),
|
||||
stroke: true,
|
||||
},
|
||||
];
|
||||
|
||||
const STATS = [
|
||||
{ value: "4", accent: "px", label: "Base unit" },
|
||||
{ value: "9", accent: "x", label: "Type scale" },
|
||||
{ value: "4", accent: "+", label: "Core tokens" },
|
||||
];
|
||||
|
||||
export default function Hero() {
|
||||
const getGreetingMessage = () => {
|
||||
const hour = new Date().getHours();
|
||||
if (hour >= 8 && hour < 12) return "Olá! Vi seu portfólio. Podemos conversar?";
|
||||
if (hour >= 13 && hour < 18) return "Boa tarde! Vi seu portfólio. Podemos conversar?";
|
||||
return "Olá! Vi seu portfólio. Podemos conversar quando estiver disponível?";
|
||||
};
|
||||
|
||||
const generateWhatsAppUrl = () => {
|
||||
const message = encodeURIComponent(getGreetingMessage());
|
||||
return `https://wa.me/5547992742365?text=${message}`;
|
||||
};
|
||||
const hour = new Date().getHours();
|
||||
const greeting =
|
||||
hour >= 8 && hour < 12
|
||||
? "Ola! Vi seu portfolio. Podemos conversar?"
|
||||
: hour >= 13 && hour < 18
|
||||
? "Boa tarde! Vi seu portfolio. Podemos conversar?"
|
||||
: "Ola! Vi seu portfolio. Podemos conversar quando estiver disponivel?";
|
||||
|
||||
return (
|
||||
<section id="home" className="relative min-h-screen flex items-center justify-center overflow-hidden pt-28 md:pt-20">
|
||||
{/* 1. Background Engineering: Grid Pattern & Radial Shading */}
|
||||
<div className="absolute inset-0 z-0 pointer-events-none">
|
||||
<div className="absolute inset-0 bg-radial-at-t from-blue-500/10 via-transparent to-transparent" />
|
||||
</div>
|
||||
<section
|
||||
id="home"
|
||||
className="relative overflow-hidden px-6 py-24 lg:px-16"
|
||||
style={{ background: "#0A0A0A", color: "#FFFFFF" }}
|
||||
>
|
||||
<div className="pointer-events-none absolute inset-0 bg-grid-light opacity-70" />
|
||||
<div className="pointer-events-none absolute inset-x-0 top-0 h-40 bg-[radial-gradient(circle_at_top,rgba(232,255,0,0.18),transparent_60%)]" />
|
||||
|
||||
<div className="relative z-10 w-full max-w-6xl mx-auto px-6 grid grid-cols-1 lg:grid-cols-12 gap-12 items-center">
|
||||
<div className="relative z-10 mx-auto grid min-h-[calc(100vh-52px)] w-full max-w-[1240px] grid-cols-1 items-center gap-16 lg:grid-cols-2">
|
||||
<div>
|
||||
<div className="mb-8 inline-flex items-center gap-2">
|
||||
<span
|
||||
className="h-1.5 w-1.5 animate-pulse rounded-full"
|
||||
style={{ background: "#E8FF00" }}
|
||||
/>
|
||||
<span
|
||||
className="text-[9px] uppercase tracking-[0.2em]"
|
||||
style={{ color: "rgba(255,255,255,.38)" }}
|
||||
>
|
||||
Systems engineer available
|
||||
</span>
|
||||
</div>
|
||||
|
||||
{/* Lado Esquerdo: Conteúdo Textual (80/20 do Impacto) */}
|
||||
<div className="lg:col-span-7 text-left">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, x: -20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
transition={{ duration: 0.6 }}
|
||||
<h1
|
||||
className="mb-6 font-display text-5xl font-extrabold leading-[0.92] tracking-[-0.04em] md:text-6xl lg:text-7xl xl:text-[5.4rem]"
|
||||
style={{ color: "#FFFFFF" }}
|
||||
>
|
||||
<AnimatedGradientText className="mb-6 inline-flex items-center gap-2 px-4 py-2 rounded-full border border-blue-500/20 bg-blue-500/5 backdrop-blur-md">
|
||||
<span className="relative flex h-2 w-2">
|
||||
<span className="animate-ping absolute inline-flex h-full w-full rounded-full bg-cyan-400 opacity-75"></span>
|
||||
<span className="relative inline-flex rounded-full h-2 w-2 bg-cyan-500"></span>
|
||||
</span>
|
||||
<span className="text-sm font-medium text-cyan-300 tracking-wide uppercase">
|
||||
Systems Engineer available
|
||||
</span>
|
||||
</AnimatedGradientText>
|
||||
Evandro
|
||||
<br />
|
||||
<span style={{ color: "#E8FF00" }}>.dev</span>
|
||||
<br />
|
||||
.br
|
||||
</h1>
|
||||
|
||||
<h1 className="text-5xl md:text-7xl font-black text-white mb-4 tracking-tight leading-none">
|
||||
Evandro <br />
|
||||
<AuroraText className="italic">Fonseca Junior</AuroraText>
|
||||
</h1>
|
||||
<p
|
||||
className="mb-10 max-w-[460px] text-[13px] leading-7"
|
||||
style={{ color: "rgba(255,255,255,.72)" }}
|
||||
>
|
||||
Engenheiro de Software especializado em aplicacoes de alta
|
||||
performance, interfaces de produto e sistemas criticos com Next.js,
|
||||
React, Tauri e arquitetura escalavel.
|
||||
</p>
|
||||
|
||||
<div className="h-10 flex items-center gap-2 mb-6">
|
||||
<Terminal className="w-5 h-5 text-blue-400" />
|
||||
<TextType
|
||||
text={["Engenheiro de Software", "Next.js & Tauri Specialist", "High Performance Systems Architect"]}
|
||||
className="text-2xl font-mono text-slate-400"
|
||||
typingSpeed={60}
|
||||
/>
|
||||
</div>
|
||||
<div className="mb-8 flex flex-wrap items-center gap-4">
|
||||
<a
|
||||
href={`https://wa.me/5547992742365?text=${encodeURIComponent(greeting)}`}
|
||||
className="inline-flex h-11 items-center rounded-sm px-6 text-[11px] font-semibold uppercase tracking-[0.08em] no-underline transition-all duration-200 hover:-translate-y-0.5 hover:shadow-[0_0_0_1px_rgba(232,255,0,.8),0_8px_24px_rgba(232,255,0,.35)]"
|
||||
style={{ background: "#E8FF00", color: "#0A0A0A" }}
|
||||
>
|
||||
Iniciar projeto
|
||||
</a>
|
||||
|
||||
<p className="text-lg text-slate-400 max-w-xl mb-10 leading-relaxed">
|
||||
Engenheiro de Software especializado em aplicações de alta performance e sistemas críticos. Desenvolvimento com <span className="text-white font-medium">Next.js 15, React 19, Tauri</span> e arquitetura escalável. Transformo requisitos complexos em software robusto.
|
||||
</p>
|
||||
|
||||
<div className="flex flex-wrap gap-4 items-center">
|
||||
<motion.a
|
||||
whileHover={{ scale: 1.05 }}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
href={generateWhatsAppUrl()}
|
||||
className="px-8 py-4 bg-blue-600 hover:bg-blue-500 text-white rounded-xl font-bold transition-all shadow-lg shadow-blue-600/20 flex items-center gap-2"
|
||||
>
|
||||
<MessageCircle className="w-5 h-5" />
|
||||
Iniciar Projeto
|
||||
</motion.a>
|
||||
|
||||
<div className="flex gap-4 px-4">
|
||||
{[
|
||||
{ icon: Github, link: "https://github.com/evandrodevbr", rel: "me" },
|
||||
{ icon: Linkedin, link: "https://www.linkedin.com/in/evandro-fonseca-junior-0969aa1b0/", rel: "me" },
|
||||
{ icon: Mail, link: "mailto:[email protected]" }
|
||||
].map((social, i) => (
|
||||
<a
|
||||
key={i}
|
||||
href={social.link}
|
||||
rel={social.rel ? `${social.rel} noopener noreferrer` : "noopener noreferrer"}
|
||||
target={social.link.startsWith("mailto:") ? undefined : "_blank"}
|
||||
className="p-3 rounded-lg border border-slate-800 text-slate-400 hover:text-white hover:border-slate-600 transition-all"
|
||||
<div className="flex items-center gap-2">
|
||||
{SOCIAL_LINKS.map((link) => (
|
||||
<a
|
||||
key={link.label}
|
||||
href={link.href}
|
||||
target={link.href.startsWith("http") ? "_blank" : undefined}
|
||||
rel={
|
||||
link.href.startsWith("http")
|
||||
? "noopener noreferrer"
|
||||
: undefined
|
||||
}
|
||||
className="inline-flex h-11 w-11 items-center justify-center rounded-sm border transition-all duration-200 hover:bg-white/10"
|
||||
style={{
|
||||
borderColor: "rgba(255,255,255,.18)",
|
||||
color: "rgba(255,255,255,.84)",
|
||||
}}
|
||||
aria-label={link.label}
|
||||
>
|
||||
<svg
|
||||
width="18"
|
||||
height="18"
|
||||
viewBox="0 0 24 24"
|
||||
fill={link.stroke ? "none" : "currentColor"}
|
||||
stroke={link.stroke ? "currentColor" : "none"}
|
||||
strokeWidth={link.stroke ? "2" : undefined}
|
||||
role="img"
|
||||
aria-label={link.label}
|
||||
>
|
||||
<social.icon className="w-5 h-5" />
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
{link.icon}
|
||||
</svg>
|
||||
</a>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mt-8 flex items-center gap-2 text-slate-500 text-sm">
|
||||
<MapPin className="w-4 h-4" />
|
||||
<span>Garuva, SC | Remote Worldwide</span>
|
||||
</div>
|
||||
</motion.div>
|
||||
</div>
|
||||
|
||||
{/* Lado Direito: Visual (A "Âncora" do Design) */}
|
||||
<div className="lg:col-span-5 relative flex justify-center">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, scale: 0.8 }}
|
||||
animate={{ opacity: 1, scale: 1 }}
|
||||
transition={{ duration: 0.8, ease: "easeOut" }}
|
||||
className="relative w-72 h-72 md:w-96 md:h-96"
|
||||
<div
|
||||
className="text-[10px] uppercase tracking-[0.14em]"
|
||||
style={{ color: "rgba(255,255,255,.38)" }}
|
||||
>
|
||||
{/* Elementos Orbitais (Vibe Engenharia) */}
|
||||
<div className="absolute inset-0 rounded-full border border-dashed border-blue-500/30 animate-[spin_20s_linear_infinite]" />
|
||||
<div className="absolute inset-10 rounded-full border border-blue-500/10 animate-[spin_15s_linear_infinite_reverse]" />
|
||||
Garuva, SC | Remote worldwide
|
||||
</div>
|
||||
|
||||
{/* Foto de Perfil com Masking de Engenharia */}
|
||||
<div className="relative z-10 w-full h-full rounded-[2.5rem] overflow-hidden border-2 border-slate-800 shadow-2xl">
|
||||
<div className="absolute inset-0 bg-blue-500/10 z-10 hover:opacity-0 transition-opacity duration-500" />
|
||||
<Image
|
||||
src="/edev.jpeg"
|
||||
alt="Evandro Fonseca Junior"
|
||||
fill
|
||||
className="object-cover grayscale hover:grayscale-0 transition-all duration-700 scale-105 hover:scale-100"
|
||||
priority
|
||||
fetchPriority="high"
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Floating Stats Badge */}
|
||||
<div className="absolute -bottom-6 -right-6 bg-slate-900 border border-slate-800 p-4 rounded-2xl shadow-2xl z-20 backdrop-blur-xl">
|
||||
<p className="text-xs text-slate-500 uppercase font-bold tracking-tighter">Exp. Level</p>
|
||||
<p className="text-xl font-mono text-blue-400">Mid-Level Engineer</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
<div
|
||||
className="mt-10 grid max-w-[440px] grid-cols-3 gap-px border max-sm:grid-cols-1"
|
||||
style={{
|
||||
borderColor: "rgba(255,255,255,.12)",
|
||||
background: "rgba(255,255,255,.08)",
|
||||
}}
|
||||
>
|
||||
{STATS.map((item) => (
|
||||
<div
|
||||
key={item.label}
|
||||
className="p-5"
|
||||
style={{ background: "rgba(255,255,255,.03)" }}
|
||||
>
|
||||
<div
|
||||
className="font-display text-2xl font-extrabold tracking-[-0.03em]"
|
||||
style={{ color: "#FFFFFF" }}
|
||||
>
|
||||
{item.value}
|
||||
<span style={{ color: "#E8FF00" }}>{item.accent}</span>
|
||||
</div>
|
||||
<div
|
||||
className="mt-1 text-[9px] uppercase tracking-[0.12em]"
|
||||
style={{ color: "rgba(255,255,255,.35)" }}
|
||||
>
|
||||
{item.label}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Scroll Indicator (Refinado) */}
|
||||
<div className="absolute bottom-8 left-1/2 -translate-x-1/2 flex flex-col items-center gap-2">
|
||||
<span className="text-[10px] text-slate-600 uppercase tracking-[0.2em]">Scroll</span>
|
||||
<div className="w-[1px] h-12 bg-gradient-to-b from-blue-500 to-transparent" />
|
||||
<div className="flex flex-col items-end gap-6 max-lg:items-start">
|
||||
<div
|
||||
className="relative flex aspect-square w-full max-w-[440px] items-center justify-center border max-lg:max-w-[320px]"
|
||||
style={{
|
||||
borderColor: "rgba(255,255,255,.12)",
|
||||
background: "rgba(255,255,255,.03)",
|
||||
}}
|
||||
>
|
||||
<span
|
||||
className="absolute left-6 top-5 text-[9px] tracking-[0.14em]"
|
||||
style={{ color: "rgba(255,255,255,.32)" }}
|
||||
>
|
||||
D / SLASH MARK
|
||||
</span>
|
||||
<Logo variant="mark" size="xl" theme="dark" />
|
||||
<span
|
||||
className="absolute bottom-5 right-6 text-[9px] tracking-[0.14em]"
|
||||
style={{ color: "#E8FF00", opacity: 0.78 }}
|
||||
>
|
||||
CONCEITO ESCOLHIDO
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
className="flex items-center gap-3 text-[9px] tracking-[0.12em]"
|
||||
style={{ color: "rgba(255,255,255,.32)" }}
|
||||
>
|
||||
<span
|
||||
className="h-px w-6"
|
||||
style={{ background: "rgba(255,255,255,.18)" }}
|
||||
/>
|
||||
D / Slash mark · conceito final
|
||||
</div>
|
||||
<div
|
||||
className="flex items-center gap-3 text-[9px] tracking-[0.12em]"
|
||||
style={{ color: "rgba(255,255,255,.32)" }}
|
||||
>
|
||||
<span
|
||||
className="h-px w-6"
|
||||
style={{ background: "rgba(255,255,255,.18)" }}
|
||||
/>
|
||||
Escala: 280x200 · viewbox
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
}
|
||||
+73
-194
@@ -1,207 +1,86 @@
|
||||
"use client";
|
||||
"use client";
|
||||
|
||||
import React, { useMemo } from "react";
|
||||
import { motion } from "motion/react";
|
||||
import { BentoGrid, BentoGridItem } from "@/components/magicui/bento-grid";
|
||||
import {
|
||||
ExternalLink,
|
||||
Github,
|
||||
Star,
|
||||
Zap,
|
||||
Globe,
|
||||
Cpu,
|
||||
Code2,
|
||||
Terminal,
|
||||
} from "lucide-react";
|
||||
import { useMemo } from "react";
|
||||
import { usePortfolioData } from "@/lib/usePortfolioData";
|
||||
|
||||
// --- Configuração de Cores (Harmonizado com a seção anterior) ---
|
||||
const CATEGORY_THEMES: Record<string, any> = {
|
||||
"full-stack": { color: "text-blue-400", bg: "bg-blue-500/10", border: "border-blue-500/20", icon: Globe },
|
||||
iot: { color: "text-purple-400", bg: "bg-purple-500/10", border: "border-purple-500/20", icon: Cpu },
|
||||
api: { color: "text-orange-400", bg: "bg-orange-500/10", border: "border-orange-500/20", icon: Code2 },
|
||||
web: { color: "text-green-400", bg: "bg-green-500/10", border: "border-green-500/20", icon: Globe },
|
||||
desktop: { color: "text-emerald-400", bg: "bg-emerald-500/10", border: "border-emerald-500/20", icon: Terminal },
|
||||
};
|
||||
|
||||
// Funções auxiliares para Schema
|
||||
function getApplicationCategory(category: string): string {
|
||||
const map: Record<string, string> = {
|
||||
"full-stack": "WebApplication",
|
||||
"iot": "DesktopApplication",
|
||||
"api": "WebApplication",
|
||||
"web": "WebApplication",
|
||||
"desktop": "DesktopApplication"
|
||||
};
|
||||
return map[category.toLowerCase()] || "WebApplication";
|
||||
}
|
||||
|
||||
function getOperatingSystem(category: string): string {
|
||||
if (category.toLowerCase() === "desktop") return "Windows, Linux";
|
||||
return "Web Browser";
|
||||
}
|
||||
import { Section } from "./Section";
|
||||
|
||||
export default function Projects() {
|
||||
const { data, loading, error } = usePortfolioData();
|
||||
|
||||
const projects = useMemo(() =>
|
||||
data?.projects.filter((p) => p.featured) || [],
|
||||
[data]);
|
||||
|
||||
const projectsJsonLd = useMemo(() => {
|
||||
if (!projects || projects.length === 0) return null;
|
||||
|
||||
return {
|
||||
"@context": "https://schema.org",
|
||||
"@type": "ItemList",
|
||||
"itemListElement": projects.map((project, index) => ({
|
||||
"@type": "ListItem",
|
||||
"position": index + 1,
|
||||
"item": {
|
||||
"@type": "SoftwareApplication",
|
||||
"name": project.title,
|
||||
"description": project.description,
|
||||
"applicationCategory": getApplicationCategory(project.category),
|
||||
"operatingSystem": getOperatingSystem(project.category),
|
||||
"url": project.demoUrl || project.githubUrl || `https://evandro.dev.br/#projects`,
|
||||
"offers": {
|
||||
"@type": "Offer",
|
||||
"price": "0",
|
||||
"priceCurrency": "BRL"
|
||||
},
|
||||
"softwareVersion": "1.0.0",
|
||||
"author": {
|
||||
"@type": "Person",
|
||||
"name": "Evandro Fonseca Junior",
|
||||
"url": "https://evandro.dev.br"
|
||||
}
|
||||
}
|
||||
}))
|
||||
};
|
||||
}, [projects]);
|
||||
|
||||
if (loading) return <div className="py-20 text-center animate-pulse text-slate-500 font-mono">Loading_Assets...</div>;
|
||||
if (error) return <div className="py-20 text-center text-red-500">System_Error: Failed to sync projects.</div>;
|
||||
|
||||
return (
|
||||
<section id="projects" className="py-32 px-6 relative">
|
||||
{/* Background Decor */}
|
||||
<div className="absolute inset-0 bg-[radial-gradient(circle_at_center,_var(--tw-gradient-stops))] from-blue-500/5 via-transparent to-transparent pointer-events-none" />
|
||||
|
||||
<div className="max-w-7xl mx-auto relative z-10">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="text-left mb-20"
|
||||
>
|
||||
<span className="text-blue-500 font-mono text-sm tracking-[0.3em] uppercase block mb-4">Produção_Intelectual</span>
|
||||
<h2 className="text-5xl md:text-7xl font-black text-white tracking-tighter">
|
||||
Projetos <span className="text-slate-700 italic">Principais</span>
|
||||
</h2>
|
||||
</motion.div>
|
||||
|
||||
<BentoGrid className="grid-cols-1 md:grid-cols-3 gap-4">
|
||||
{projects.map((project, index) => {
|
||||
const theme = CATEGORY_THEMES[project.category.toLowerCase()] || CATEGORY_THEMES.web;
|
||||
|
||||
// Lógica de Grid Inteligente:
|
||||
// Index 0: NutriScan (Destaque - 2 colunas)
|
||||
// Index 1: Twins Lab (Complemento - 1 coluna)
|
||||
// Demais: 1 coluna cada (Preenche a linha 2 perfeitamente)
|
||||
const colSpan = index === 0 ? "md:col-span-2" : "md:col-span-1";
|
||||
|
||||
return (
|
||||
<BentoGridItem
|
||||
key={project.id}
|
||||
className={`${colSpan} group relative flex flex-col justify-between overflow-hidden rounded-3xl border border-slate-800 bg-slate-900/40 backdrop-blur-xl p-8 transition-all duration-500 hover:border-blue-500/30 min-h-[22rem]`}
|
||||
>
|
||||
{/* Header: Icon & Links */}
|
||||
<div className="relative z-10">
|
||||
<div className="flex items-start justify-between mb-8">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className={`p-4 rounded-2xl ${theme.bg} border ${theme.border} group-hover:scale-110 transition-transform duration-500`}>
|
||||
<theme.icon className={`w-6 h-6 ${theme.color}`} />
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="text-2xl font-bold text-white tracking-tight">{project.title}</h3>
|
||||
<p className="text-sm text-slate-500 font-medium">{project.subtitle}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex gap-2">
|
||||
{project.githubUrl && <ProjectLink href={project.githubUrl} icon={Github} />}
|
||||
{project.demoUrl && <ProjectLink href={project.demoUrl} icon={ExternalLink} highlight />}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Badges de Tecnologias */}
|
||||
<div className="flex flex-wrap gap-2 mb-6">
|
||||
{project.technologies.slice(0, 5).map((tech: string) => (
|
||||
<span key={tech} className="px-2 py-1 text-[10px] font-mono font-bold bg-slate-950 text-slate-400 border border-slate-800 rounded-md">
|
||||
{tech}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<p className="text-slate-400 text-sm leading-relaxed mb-6 line-clamp-3 group-hover:line-clamp-none transition-all duration-500">
|
||||
{project.description}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Footer: Metrics & Status */}
|
||||
<div className="relative z-10 pt-6 border-t border-slate-800/50 mt-auto flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="text-[10px] uppercase tracking-widest text-slate-600 font-bold">Complexity</span>
|
||||
<ComplexityStars rating={project.metrics?.complexity || 3} />
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<div className={`w-2 h-2 rounded-full animate-pulse ${project.status === 'Em Produção' ? 'bg-emerald-500' : 'bg-blue-500'}`} />
|
||||
<span className="text-[10px] font-bold text-slate-400 uppercase">{project.status}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Background Hover Glow */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-blue-500/5 via-transparent to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-700" />
|
||||
</BentoGridItem>
|
||||
);
|
||||
})}
|
||||
</BentoGrid>
|
||||
|
||||
{/* JSON-LD Schema for Projects */}
|
||||
{projectsJsonLd && (
|
||||
<script
|
||||
type="application/ld+json"
|
||||
dangerouslySetInnerHTML={{ __html: JSON.stringify(projectsJsonLd) }}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
const projects = useMemo(
|
||||
() => data?.projects.filter((p) => p.featured) || [],
|
||||
[data],
|
||||
);
|
||||
}
|
||||
|
||||
// --- Subcomponentes Auxiliares ---
|
||||
if (loading)
|
||||
return <div className="ds-loading">Loading featured projects</div>;
|
||||
if (error)
|
||||
return <div className="ds-error">Error loading featured projects</div>;
|
||||
|
||||
function ProjectLink({ href, icon: Icon, highlight }: { href: string; icon: any; highlight?: boolean }) {
|
||||
return (
|
||||
<a
|
||||
href={href}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={`p-2 rounded-xl transition-all duration-300 hover:scale-110 ${highlight ? 'bg-blue-600 text-white shadow-lg shadow-blue-600/20' : 'bg-slate-900 border border-slate-800 text-slate-500 hover:text-white'
|
||||
}`}
|
||||
<Section
|
||||
id="projects"
|
||||
eyebrow="PRODUCAO_INTELECTUAL"
|
||||
title="Projetos com recorte técnico claro e impacto observável"
|
||||
description="Seleção de produtos e ferramentas onde performance, confiabilidade e clareza de arquitetura foram requisitos reais."
|
||||
dark
|
||||
>
|
||||
<Icon className="w-4 h-4" />
|
||||
</a>
|
||||
<div className="ds-grid-2">
|
||||
{projects.map((project) => (
|
||||
<article key={project.id} className="ds-card">
|
||||
<div className="flex items-start justify-between gap-sp-6">
|
||||
<div>
|
||||
<div className="ds-kicker">{project.category}</div>
|
||||
<h3 className="ds-card-title">{project.title}</h3>
|
||||
<p className="mt-sp-2 text-[11px] uppercase tracking-[0.1em] text-[rgba(255,255,255,.36)]">
|
||||
{project.subtitle}
|
||||
</p>
|
||||
</div>
|
||||
<span className="ds-tag">{project.status}</span>
|
||||
</div>
|
||||
<p className="ds-copy">{project.description}</p>
|
||||
<div className="mt-sp-6 flex flex-wrap gap-sp-3">
|
||||
{project.githubUrl && (
|
||||
<a
|
||||
href={project.githubUrl}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="ds-link ds-link-muted"
|
||||
>
|
||||
GitHub
|
||||
</a>
|
||||
)}
|
||||
{project.demoUrl && (
|
||||
<a
|
||||
href={project.demoUrl}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="ds-link ds-link-accent"
|
||||
>
|
||||
Live demo
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
<ul className="ds-tags mt-sp-6">
|
||||
{project.technologies.slice(0, 6).map((tech: string) => (
|
||||
<li key={tech} className="ds-tag">
|
||||
{tech}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
<div className="mt-sp-6 flex flex-wrap gap-sp-4">
|
||||
<span className="ds-metric">
|
||||
Complexity {project.metrics?.complexity || 3}/5
|
||||
</span>
|
||||
{project.metrics?.teamSize && (
|
||||
<span className="ds-metric">
|
||||
Team {project.metrics.teamSize}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
function ComplexityStars({ rating }: { rating: number }) {
|
||||
return (
|
||||
<div className="flex gap-1">
|
||||
{[...Array(5)].map((_, i) => (
|
||||
<div key={i} className={`w-1.5 h-1.5 rounded-full ${i < rating ? 'bg-yellow-500' : 'bg-slate-800'}`} />
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+29
-121
@@ -1,134 +1,42 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import {
|
||||
Code, Database, Cloud, Cpu, Smartphone,
|
||||
Zap, GitBranch, TestTube, Terminal, Layers
|
||||
} from "lucide-react";
|
||||
import { usePortfolioData } from "@/lib/usePortfolioData";
|
||||
|
||||
const ICON_MAP: Record<string, React.ElementType> = {
|
||||
Code, Database, Cloud, Cpu, Smartphone, Zap, GitBranch, TestTube, Terminal, Layers
|
||||
};
|
||||
|
||||
const getCategoryStyles = (color: string) => {
|
||||
const base = color.replace("text-", "");
|
||||
return {
|
||||
icon: color,
|
||||
bg: `bg-${base}/10`,
|
||||
border: `border-slate-800/50`,
|
||||
};
|
||||
};
|
||||
import { Section } from "./Section";
|
||||
|
||||
export default function Skills() {
|
||||
const { data, loading, error } = usePortfolioData();
|
||||
|
||||
if (loading) return <LoadingState />;
|
||||
if (error || !data) return <ErrorState />;
|
||||
if (loading) return <div className="ds-loading">Loading skill registry</div>;
|
||||
if (error || !data) {
|
||||
return <div className="ds-error">Error loading skill registry</div>;
|
||||
}
|
||||
|
||||
return (
|
||||
<section id="skills" className="relative py-32 px-6">
|
||||
{/* Background Decoration */}
|
||||
<div className="absolute top-0 left-0 w-full h-full pointer-events-none">
|
||||
<div className="absolute top-[40%] left-[50%] -translate-x-1/2 w-[800px] h-[800px] bg-blue-900/5 rounded-full blur-[100px]" />
|
||||
</div>
|
||||
|
||||
<div className="max-w-7xl mx-auto relative z-10">
|
||||
|
||||
{/* Header Seção */}
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-100px" }}
|
||||
className="flex flex-col md:flex-row justify-between items-end mb-20 gap-8"
|
||||
>
|
||||
<div className="text-left">
|
||||
<div className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-blue-950/30 border border-blue-500/20 text-blue-400 font-mono text-xs tracking-wider mb-4">
|
||||
<Layers className="w-3 h-3" />
|
||||
<span>MODULE_REGISTRY.lib</span>
|
||||
<Section
|
||||
id="skills"
|
||||
eyebrow="MODULE_REGISTRY.lib"
|
||||
title="Stack modular para produto, operação e escala"
|
||||
description="Ferramentas escolhidas por previsibilidade operacional, velocidade de entrega e facilidade de manutenção."
|
||||
dark
|
||||
>
|
||||
<div className="ds-grid-2">
|
||||
{data.skills.technical.map((category, index) => (
|
||||
<article key={category.category} className="ds-card">
|
||||
<div className="flex items-center justify-between gap-sp-4">
|
||||
<div className="ds-kicker">ID_0{index + 1}X</div>
|
||||
<div className="ds-metric">{category.skills.length} items</div>
|
||||
</div>
|
||||
<h3 className="text-4xl md:text-6xl font-black text-white tracking-tighter">
|
||||
Arsenal <span className="text-transparent bg-clip-text bg-gradient-to-r from-blue-500 to-indigo-500">Tecnológico</span>
|
||||
</h3>
|
||||
</div>
|
||||
<p className="text-slate-400 max-w-md text-sm md:text-base font-light leading-relaxed border-l-2 border-slate-800 pl-6">
|
||||
Sistemas resilientes construídos com precisão matemática e rigor arquitetural.
|
||||
</p>
|
||||
</motion.div>
|
||||
|
||||
{/* Grid de Habilidades */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||
{data.skills.technical.map((category, index) => {
|
||||
const Icon = ICON_MAP[category.icon] || Code;
|
||||
const styles = getCategoryStyles(category.color);
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
key={category.category}
|
||||
initial={{ opacity: 0, y: 50 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, amount: 0.2 }}
|
||||
transition={{
|
||||
duration: 0.5,
|
||||
delay: index * 0.05,
|
||||
ease: "easeOut"
|
||||
}}
|
||||
className="h-full"
|
||||
>
|
||||
<div
|
||||
className="group relative h-full p-8 flex flex-col bg-zinc-900/40 border border-white/5 rounded-2xl backdrop-blur-sm hover:border-blue-500/20 hover:bg-zinc-900/60 transition-all duration-300 overflow-hidden"
|
||||
>
|
||||
{/* Scanning Line Effect */}
|
||||
<div className="absolute top-0 left-0 w-full h-[2px] bg-gradient-to-r from-transparent via-blue-500/50 to-transparent -translate-x-full group-hover:animate-scan" />
|
||||
|
||||
<div className="flex items-start justify-between mb-8">
|
||||
<div className={`p-4 rounded-xl bg-slate-800/50 border border-white/5 group-hover:bg-blue-500/10 group-hover:border-blue-500/20 transition-all duration-300`}>
|
||||
<Icon className={`w-8 h-8 text-slate-400 group-hover:text-blue-400 transition-colors`} />
|
||||
</div>
|
||||
<span className="text-[10px] font-mono text-slate-600 uppercase tracking-widest font-bold group-hover:text-blue-500/50 transition-colors">
|
||||
ID_0{index}X
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<h3 className="text-xl font-bold text-white mb-6 group-hover:text-blue-200 transition-colors">
|
||||
{category.category}
|
||||
</h3>
|
||||
|
||||
<div className="flex flex-wrap gap-2 mt-auto">
|
||||
{category.skills.map((skill, sIdx) => (
|
||||
<span
|
||||
key={sIdx}
|
||||
className="px-2.5 py-1 text-[10px] font-mono bg-black/40 text-slate-400 rounded border border-white/5 group-hover:border-blue-500/20 group-hover:text-blue-300 transition-all duration-300 cursor-default"
|
||||
>
|
||||
{skill}
|
||||
</span>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
<h3 className="ds-card-title">{category.category}</h3>
|
||||
<ul className="ds-tags mt-sp-6">
|
||||
{category.skills.map((skill) => (
|
||||
<li key={skill} className="ds-tag">
|
||||
{skill}
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
|
||||
// --- Componentes de Suporte (Manter no mesmo arquivo para contexto ou mover para /components) ---
|
||||
function LoadingState() {
|
||||
return (
|
||||
<div className="py-32 flex flex-col items-center justify-center space-y-4 bg-[#030712]">
|
||||
<div className="w-12 h-12 border-4 border-blue-500/20 border-t-blue-500 rounded-full animate-spin" />
|
||||
<span className="text-slate-500 font-mono text-sm animate-pulse uppercase tracking-tighter">Initializing Systems...</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function ErrorState() {
|
||||
return (
|
||||
<div className="py-32 text-center text-red-400 font-mono bg-[#030712]">
|
||||
[FATAL_ERROR]: Failed to synchronize portfolio data.
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -1,272 +1,110 @@
|
||||
"use client";
|
||||
|
||||
import React from "react";
|
||||
import { motion } from "framer-motion";
|
||||
import {
|
||||
Briefcase,
|
||||
Clock,
|
||||
HardHat,
|
||||
Lightbulb,
|
||||
Building2,
|
||||
Users,
|
||||
User,
|
||||
Star,
|
||||
CheckCircle2,
|
||||
Terminal,
|
||||
ShieldCheck,
|
||||
Zap,
|
||||
} from "lucide-react";
|
||||
import { Section } from "./Section";
|
||||
|
||||
// --- 1. DATA ---
|
||||
const WorkModalitiesData = [
|
||||
{
|
||||
title: "Intervenção Ad-hoc",
|
||||
value: "R$ 35 - 70/hora",
|
||||
badge: "AGILIDADE",
|
||||
badge: "Agilidade",
|
||||
idealFor:
|
||||
"Resolução de gargalos técnicos específicos e consultoria de arquitetura imediata.",
|
||||
description:
|
||||
"Engenharia sob demanda com foco em troubleshooting e otimização de performance. Rastreabilidade total via timesheet detalhado.",
|
||||
"Engenharia sob demanda com foco em troubleshooting, performance e decisões rápidas com rastreabilidade.",
|
||||
benefits: [
|
||||
"Diagnóstico Sênior Imediato",
|
||||
"Sem Compromisso de Longo Prazo",
|
||||
"Documentação de Alterações",
|
||||
"Diagnóstico sênior imediato",
|
||||
"Sem compromisso de longo prazo",
|
||||
"Documentação de alterações",
|
||||
],
|
||||
icon: Zap,
|
||||
color: "blue",
|
||||
featured: false,
|
||||
},
|
||||
{
|
||||
title: "Alocação de Sprint",
|
||||
value: "Eficiência Financeira",
|
||||
badge: "MAIS ESCOLHIDO",
|
||||
title: "Alocação de sprint",
|
||||
value: "Eficiência financeira",
|
||||
badge: "Mais escolhido",
|
||||
idealFor:
|
||||
"Desenvolvimento contínuo de features com previsibilidade de budget e prioridade técnica.",
|
||||
"Times que precisam de entrega contínua com previsibilidade de orçamento e prioridade técnica.",
|
||||
description:
|
||||
"Pacotes de horas pré-pagas que garantem disponibilidade na agenda e redução do custo unitário por hora. Ideal para scaling.",
|
||||
"Pacotes de horas pré-pagas com redução de custo unitário e espaço reservado na agenda para evolução constante.",
|
||||
benefits: [
|
||||
"Taxa horária reduzida (Lock-in)",
|
||||
"Garantia de Disponibilidade",
|
||||
"Prioridade em Critical Bugs",
|
||||
"Taxa horária reduzida",
|
||||
"Garantia de disponibilidade",
|
||||
"Prioridade em bugs críticos",
|
||||
],
|
||||
icon: Briefcase,
|
||||
color: "green",
|
||||
featured: true,
|
||||
},
|
||||
{
|
||||
title: "Engenheiro Dedicado",
|
||||
title: "Engenheiro dedicado",
|
||||
value: "A partir de R$ 2.800/mês",
|
||||
badge: "VIP & ESTRATÉGICO",
|
||||
badge: "Estratégico",
|
||||
idealFor:
|
||||
"Empresas que necessitam de um 'Fractional CTO' ou Lead Engineer para governança técnica.",
|
||||
"Empresas que precisam de liderança técnica contínua, coordenação de backlog e direção arquitetural.",
|
||||
description:
|
||||
"Parceria estratégica com foco em sustentabilidade do sistema, manutenção preventiva e evolução arquitetural contínua.",
|
||||
"Parceria recorrente para sustentação, evolução de sistema, revisão de dívida técnica e tomada de decisão.",
|
||||
benefits: [
|
||||
"SLA de Resposta Prioritária",
|
||||
"Gestão de Débito Técnico",
|
||||
"Consultoria Estratégica",
|
||||
"SLA prioritário",
|
||||
"Gestão de débito técnico",
|
||||
"Consultoria estratégica",
|
||||
],
|
||||
icon: ShieldCheck,
|
||||
color: "purple",
|
||||
featured: false,
|
||||
},
|
||||
{
|
||||
title: "Squad por Projeto",
|
||||
value: "Escopo Fechado",
|
||||
badge: "CONTROLE TOTAL",
|
||||
title: "Squad por projeto",
|
||||
value: "Escopo fechado",
|
||||
badge: "Controle total",
|
||||
idealFor:
|
||||
"Produtos novos ou migrações de sistemas onde o orçamento é rigorosamente definido pelo escopo.",
|
||||
"Produtos novos, replatforming ou migrações com orçamento definido por entregáveis e marcos claros.",
|
||||
description:
|
||||
"Desenvolvimento orientado a entregáveis (milestones). Mitigação de riscos através de definições claras de 'Done'.",
|
||||
"Execução orientada a milestones com redução de risco através de definição objetiva de escopo e critérios de done.",
|
||||
benefits: [
|
||||
"Estimativa de Riscos Inclusa",
|
||||
"Cronograma de Entregas",
|
||||
"Garantia Pós-Entrega",
|
||||
"Estimativa de riscos inclusa",
|
||||
"Cronograma de entregas",
|
||||
"Garantia pós-entrega",
|
||||
],
|
||||
icon: HardHat,
|
||||
color: "orange",
|
||||
featured: false,
|
||||
},
|
||||
];
|
||||
|
||||
// --- 2. COMPONENTS ---
|
||||
|
||||
export function WorkModalities() {
|
||||
return (
|
||||
<section
|
||||
<Section
|
||||
id="modalities"
|
||||
className="py-32 px-6 relative"
|
||||
eyebrow="CONTRACT_PROTOCOLS.sys"
|
||||
title="Formatos de contratação para contextos técnicos distintos"
|
||||
description="Modelos pensados para reduzir overhead, preservar velocidade de decisão e manter previsibilidade."
|
||||
>
|
||||
{/* Background Decor handled globally now, avoiding overflow-hidden here allows bleed */}
|
||||
|
||||
<div className="max-w-7xl mx-auto relative z-10">
|
||||
|
||||
{/* Header Profissional */}
|
||||
<div className="mb-24 text-center md:text-left">
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
className="inline-flex items-center gap-2 px-3 py-1 rounded-full bg-blue-950/30 border border-blue-500/20 text-blue-400 font-mono text-xs tracking-wider mb-6"
|
||||
>
|
||||
<Terminal className="w-3 h-3" />
|
||||
<span>CONTRACT_PROTOCOLS.sys</span>
|
||||
</motion.div>
|
||||
<motion.h2
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay: 0.1 }}
|
||||
className="text-4xl md:text-6xl font-black text-white tracking-tighter"
|
||||
>
|
||||
Flexibilidade <br />
|
||||
<span className="text-transparent bg-clip-text bg-gradient-to-r from-blue-500 to-cyan-400">Contratual</span>
|
||||
</motion.h2>
|
||||
<p className="mt-6 text-lg text-slate-400 max-w-2xl leading-relaxed font-light border-l-2 border-slate-800 pl-6">
|
||||
Modelos de engajamento adaptáveis para diferentes estágios de maturidade tecnológica e orçamentária.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Comparação de Mercado (Anchoring Effect) */}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-24">
|
||||
<MarketCard
|
||||
title="Agências Tradicionais"
|
||||
price="Alto Overhead"
|
||||
icon={Building2}
|
||||
color="text-red-500"
|
||||
delay={0}
|
||||
best={false}
|
||||
/>
|
||||
<MarketCard
|
||||
title="Consultorias Big Tech"
|
||||
price="Custo Inflacionado"
|
||||
icon={Users}
|
||||
color="text-orange-500"
|
||||
delay={0.1}
|
||||
best={false}
|
||||
/>
|
||||
<MarketCard
|
||||
title="Freelancers Generalistas"
|
||||
price="Risco Técnico"
|
||||
icon={User}
|
||||
color="text-yellow-500"
|
||||
delay={0.2}
|
||||
best={false}
|
||||
/>
|
||||
<MarketCard
|
||||
title="Engenheiro Especialista"
|
||||
price="High ROI"
|
||||
icon={Star}
|
||||
color="text-emerald-400"
|
||||
best={true}
|
||||
delay={0.3}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Grid de Modalidades */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||
{WorkModalitiesData.map((modality, index) => (
|
||||
<ModalityCard key={modality.title} modality={modality} index={index} />
|
||||
))}
|
||||
</div>
|
||||
<div className="ds-grid-4 mb-sp-6">
|
||||
{[
|
||||
["Agências tradicionais", "Alto overhead"],
|
||||
["Consultorias big tech", "Custo inflacionado"],
|
||||
["Freelancers generalistas", "Risco técnico"],
|
||||
["Engenheiro especialista", "High ROI"],
|
||||
].map(([label, value]) => (
|
||||
<div key={label} className="ds-panel">
|
||||
<div className="ds-kicker">{label}</div>
|
||||
<div className="mt-sp-3 font-display text-2xl font-extrabold tracking-[-0.02em]">
|
||||
{value}
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
|
||||
function MarketCard({ title, price, icon: Icon, color, best, delay }: any) {
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 20 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true }}
|
||||
transition={{ delay }}
|
||||
className={`p-6 rounded-2xl border backdrop-blur-sm transition-all duration-300 ${best
|
||||
? "bg-emerald-500/5 border-emerald-500/20 shadow-[0_0_20px_rgba(16,185,129,0.1)]"
|
||||
: "bg-zinc-900/40 border-white/5 opacity-70 hover:opacity-100"
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-3 mb-3">
|
||||
<Icon className={`w-4 h-4 ${color}`} />
|
||||
<h4 className="text-[10px] font-bold text-slate-400 uppercase tracking-widest">
|
||||
{title}
|
||||
</h4>
|
||||
<div className="ds-grid-2">
|
||||
{WorkModalitiesData.map((modality) => (
|
||||
<article key={modality.title} className="ds-card">
|
||||
<div className="flex items-center justify-between gap-sp-4">
|
||||
<span className="ds-kicker">{modality.badge}</span>
|
||||
<span className="ds-tag">{modality.value}</span>
|
||||
</div>
|
||||
<h3 className="ds-card-title">{modality.title}</h3>
|
||||
<p className="ds-copy">{modality.description}</p>
|
||||
<div className="mt-sp-5 rounded-[var(--r-md)] bg-[rgba(10,10,10,.03)] p-sp-4 text-[11px] leading-7 text-ink-80">
|
||||
Ideal para: {modality.idealFor}
|
||||
</div>
|
||||
<ul className="ds-list">
|
||||
{modality.benefits.map((benefit) => (
|
||||
<li key={benefit}>{benefit}</li>
|
||||
))}
|
||||
</ul>
|
||||
</article>
|
||||
))}
|
||||
</div>
|
||||
<p className={`text-sm font-bold ${best ? "text-emerald-400" : "text-slate-500"}`}>
|
||||
{price}
|
||||
</p>
|
||||
</motion.div>
|
||||
);
|
||||
}
|
||||
|
||||
function ModalityCard({ modality, index }: { modality: any; index: number }) {
|
||||
const Icon = modality.icon;
|
||||
const isFeatured = modality.featured;
|
||||
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ opacity: 0, y: 30 }}
|
||||
whileInView={{ opacity: 1, y: 0 }}
|
||||
viewport={{ once: true, margin: "-50px" }}
|
||||
transition={{ duration: 0.5, delay: index * 0.1 }}
|
||||
className={`group relative flex flex-col p-8 rounded-3xl border transition-all duration-500 overflow-hidden ${isFeatured
|
||||
? "bg-zinc-900/60 border-blue-500/30 shadow-2xl shadow-blue-900/10"
|
||||
: "bg-zinc-900/40 border-white/5 hover:border-white/10"
|
||||
}`}
|
||||
>
|
||||
{/* Hover Gradient */}
|
||||
<div className="absolute inset-0 bg-gradient-to-br from-blue-500/5 to-transparent opacity-0 group-hover:opacity-100 transition-opacity duration-500" />
|
||||
|
||||
{/* Badge */}
|
||||
{modality.badge && (
|
||||
<div className="absolute top-6 right-6">
|
||||
<span className={`px-3 py-1 rounded-full text-[10px] font-bold tracking-widest uppercase border ${isFeatured
|
||||
? "bg-blue-500/10 border-blue-500/20 text-blue-300"
|
||||
: "bg-slate-800/50 border-slate-700 text-slate-400"
|
||||
}`}>
|
||||
{modality.badge}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Header */}
|
||||
<div className="relative z-10 mb-8">
|
||||
<div className={`w-12 h-12 rounded-xl flex items-center justify-center mb-6 transition-transform group-hover:scale-110 ${isFeatured ? "bg-blue-500/10 text-blue-400" : "bg-slate-800/50 text-slate-400"
|
||||
}`}>
|
||||
<Icon className="w-6 h-6" />
|
||||
</div>
|
||||
|
||||
<h3 className="text-2xl font-bold text-white mb-2 group-hover:text-blue-200 transition-colors">
|
||||
{modality.title}
|
||||
</h3>
|
||||
<p className="font-mono text-sm text-slate-500">
|
||||
{modality.value}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
{/* Content */}
|
||||
<div className="relative z-10 flex-1">
|
||||
<p className="text-slate-400 text-sm leading-relaxed mb-8">
|
||||
{modality.description}
|
||||
</p>
|
||||
|
||||
<ul className="space-y-3 mb-8">
|
||||
{modality.benefits.map((benefit: string, i: number) => (
|
||||
<li key={i} className="flex items-start gap-3 text-xs text-slate-300">
|
||||
<CheckCircle2 className={`w-4 h-4 flex-shrink-0 mt-0.5 ${isFeatured ? "text-blue-400" : "text-slate-500"}`} />
|
||||
<span>{benefit}</span>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{/* Footer */}
|
||||
<div className="relative z-10 pt-6 border-t border-white/5 mt-auto">
|
||||
<p className="text-[10px] uppercase tracking-wider text-slate-500">
|
||||
<span className="text-slate-600 font-bold mb-1 block">Ideal Para:</span>
|
||||
{modality.idealFor}
|
||||
</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
</Section>
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user