Merge pull request #1 from evandrodevbr/feat-marketing
feat: Adiciona a estrutura inicial do portfólio com componentes para …
This commit is contained in:
12 files changed
+532
-426
No files matched your search
+84
-44
@@ -1,8 +1,9 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useMemo } from "react";
|
import React from "react";
|
||||||
import { motion, Variants } from "motion/react";
|
import { motion } from "framer-motion";
|
||||||
import { LucideIcon, Brain, Zap, Target, Rocket, Users, Lightbulb, Code, Shield } from "lucide-react";
|
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) ---
|
// --- Types & Interfaces (Tipagem Forte para Previsibilidade) ---
|
||||||
interface StoryStep {
|
interface StoryStep {
|
||||||
@@ -11,6 +12,7 @@ interface StoryStep {
|
|||||||
content: string;
|
content: string;
|
||||||
gradient: string;
|
gradient: string;
|
||||||
accent: string;
|
accent: string;
|
||||||
|
id: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface CoreValue {
|
interface CoreValue {
|
||||||
@@ -22,6 +24,7 @@ interface CoreValue {
|
|||||||
// --- Data (Desacoplado para facilitar manutenção/CMS) ---
|
// --- Data (Desacoplado para facilitar manutenção/CMS) ---
|
||||||
const STORY_DATA: StoryStep[] = [
|
const STORY_DATA: StoryStep[] = [
|
||||||
{
|
{
|
||||||
|
id: "KERNEL_INIT",
|
||||||
icon: Brain,
|
icon: Brain,
|
||||||
title: "Engenharia de Fundamentos",
|
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.",
|
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.",
|
||||||
@@ -29,6 +32,7 @@ const STORY_DATA: StoryStep[] = [
|
|||||||
accent: "text-blue-400",
|
accent: "text-blue-400",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
id: "RUNTIME_EXEC",
|
||||||
icon: Zap,
|
icon: Zap,
|
||||||
title: "Entrega sob Restrições",
|
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.",
|
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.",
|
||||||
@@ -63,21 +67,30 @@ const VALUES_DATA: CoreValue[] = [
|
|||||||
// --- Micro-componentes (Feynman Technique: Simplicidade e Reuso) ---
|
// --- Micro-componentes (Feynman Technique: Simplicidade e Reuso) ---
|
||||||
|
|
||||||
const SectionHeader = () => (
|
const SectionHeader = () => (
|
||||||
<motion.div
|
<div className="text-center md:text-left mb-24 relative z-10">
|
||||||
initial={{ opacity: 0, y: 20 }}
|
<motion.div
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
className="text-center mb-24"
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
>
|
viewport={{ once: true }}
|
||||||
<span className="px-4 py-1.5 rounded-full bg-blue-500/10 border border-blue-500/20 text-blue-400 text-xs font-bold uppercase tracking-widest">
|
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"
|
||||||
Minha Jornada
|
>
|
||||||
</span>
|
<Terminal className="w-3 h-3" />
|
||||||
<h2 className="mt-6 text-5xl font-extrabold tracking-tight text-white sm:text-6xl">
|
<span>WHOAMI.sys</span>
|
||||||
Sobre <span className="text-transparent bg-clip-text bg-gradient-to-r from-blue-400 to-cyan-300">Mim</span>
|
</motion.div>
|
||||||
</h2>
|
<motion.h2
|
||||||
<p className="mt-6 text-lg text-slate-400 max-w-2xl mx-auto leading-relaxed italic">
|
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."
|
"Transformando complexidade técnica em valor de negócio através da engenharia."
|
||||||
</p>
|
</p>
|
||||||
</motion.div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
||||||
const StoryCard = ({ step, index }: { step: StoryStep; index: number }) => {
|
const StoryCard = ({ step, index }: { step: StoryStep; index: number }) => {
|
||||||
@@ -88,23 +101,35 @@ const StoryCard = ({ step, index }: { step: StoryStep; index: number }) => {
|
|||||||
initial={{ opacity: 0, x: isEven ? -30 : 30 }}
|
initial={{ opacity: 0, x: isEven ? -30 : 30 }}
|
||||||
whileInView={{ opacity: 1, x: 0 }}
|
whileInView={{ opacity: 1, x: 0 }}
|
||||||
viewport={{ once: true, margin: "-100px" }}
|
viewport={{ once: true, margin: "-100px" }}
|
||||||
className={`flex flex-col ${isEven ? "md:flex-row" : "md:flex-row-reverse"} items-center gap-12 mb-20`}
|
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="flex-1 w-full">
|
||||||
<div className="group relative p-8 rounded-3xl bg-slate-900/50 border border-slate-800 hover:border-blue-500/50 transition-all duration-500 shadow-2xl backdrop-blur-xl">
|
<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">
|
||||||
<div className={`absolute -top-6 ${isEven ? 'left-6' : 'right-6'} p-4 rounded-2xl bg-slate-900 border border-slate-800 shadow-xl group-hover:scale-110 transition-transform`}>
|
{/* Background Glow */}
|
||||||
<step.icon className={`w-8 h-8 ${step.accent}`} />
|
<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>
|
||||||
<h3 className="mt-4 text-2xl font-bold text-white mb-4">{step.title}</h3>
|
|
||||||
<p className="text-slate-400 leading-relaxed text-base font-light">{step.content}</p>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="hidden md:flex flex-col items-center">
|
|
||||||
<div className="w-12 h-12 rounded-full bg-slate-900 border-4 border-slate-800 flex items-center justify-center z-10">
|
{/* Connection Line & Node */}
|
||||||
<div className="w-3 h-3 rounded-full bg-blue-500 animate-pulse" />
|
<div className="hidden md:flex flex-col items-center justify-center w-12 relative">
|
||||||
</div>
|
<div className="absolute top-0 bottom-0 w-[1px] bg-slate-800 group-hover:bg-blue-500/50 transition-colors" />
|
||||||
<div className="w-[2px] h-32 bg-gradient-to-b from-blue-500/50 to-transparent" />
|
<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>
|
||||||
|
|
||||||
<div className="flex-1 hidden md:block" />
|
<div className="flex-1 hidden md:block" />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
);
|
);
|
||||||
@@ -112,17 +137,20 @@ const StoryCard = ({ step, index }: { step: StoryStep; index: number }) => {
|
|||||||
|
|
||||||
export default function About() {
|
export default function About() {
|
||||||
return (
|
return (
|
||||||
<section id="about" className="relative py-24 bg-[#030712] overflow-hidden">
|
<section id="about" className="relative py-32">
|
||||||
{/* Background Decor (Engenharia Visual) */}
|
{/* Background Gradients */}
|
||||||
<div className="absolute top-0 left-1/2 -translate-x-1/2 w-full h-full pointer-events-none">
|
<div className="absolute top-0 left-0 w-full h-full pointer-events-none">
|
||||||
<div className="absolute top-0 left-1/4 w-96 h-96 bg-blue-600/10 rounded-full blur-[120px]" />
|
<div className="absolute top-[-10%] right-[-10%] w-[600px] h-[600px] bg-blue-900/10 rounded-full blur-[120px]" />
|
||||||
<div className="absolute bottom-0 right-1/4 w-96 h-96 bg-purple-600/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>
|
||||||
|
|
||||||
<div className="container max-w-6xl mx-auto px-6 relative z-10">
|
<div className="container max-w-6xl mx-auto px-6 relative z-10">
|
||||||
<SectionHeader />
|
<SectionHeader />
|
||||||
|
|
||||||
<div className="relative">
|
<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) => (
|
{STORY_DATA.map((step, idx) => (
|
||||||
<StoryCard key={idx} step={step} index={idx} />
|
<StoryCard key={idx} step={step} index={idx} />
|
||||||
))}
|
))}
|
||||||
@@ -131,21 +159,33 @@ export default function About() {
|
|||||||
{/* Grid de Valores com Pareto 80/20 */}
|
{/* 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">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6 mt-32">
|
||||||
{VALUES_DATA.map((val, i) => (
|
{VALUES_DATA.map((val, i) => (
|
||||||
<div key={i} className="p-6 rounded-2xl bg-white/5 border border-white/10 hover:bg-white/10 transition-colors">
|
<motion.div
|
||||||
<val.icon className="w-6 h-6 text-blue-400 mb-4" />
|
key={i}
|
||||||
<h4 className="text-white font-semibold mb-2">{val.title}</h4>
|
initial={{ opacity: 0, y: 20 }}
|
||||||
<p className="text-slate-500 text-sm">{val.description}</p>
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
</div>
|
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>
|
</div>
|
||||||
|
|
||||||
{/* SEO Semantic Content - Visible but subtle */}
|
{/* SEO Semantic Content - Visible but subtle */}
|
||||||
<div className="mt-16 pt-8 border-t border-slate-800">
|
<div className="mt-24 pt-8 border-t border-slate-900">
|
||||||
<p className="text-xs text-slate-600 text-center max-w-2xl mx-auto leading-relaxed">
|
<div className="flex items-center justify-center gap-2 text-slate-700 text-xs font-mono uppercase tracking-widest mb-4">
|
||||||
Evandro Fonseca Junior é Engenheiro Full-Stack especializado em Next.js,
|
<Cpu className="w-3 h-3" />
|
||||||
React, Angular e Node.js. Desenvolvedor de aplicações desktop com Tauri,
|
<span>System_Signature</span>
|
||||||
sistemas web escaláveis e automação de processos. Experiência em PostgreSQL,
|
</div>
|
||||||
MySQL, computação em nuvem e IoT. Localizado em Garuva, Santa Catarina, Brasil.
|
<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
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+120
-92
@@ -4,6 +4,7 @@ import React, { useState } from "react";
|
|||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
import { Send, User, Mail, MessageSquare, Sparkles } from "lucide-react";
|
import { Send, User, Mail, MessageSquare, Sparkles } from "lucide-react";
|
||||||
import { ShimmerButton as MagicShimmerButton } from "@/components/magicui/shimmer-button";
|
import { ShimmerButton as MagicShimmerButton } from "@/components/magicui/shimmer-button";
|
||||||
|
import Squares from "@/components/Squares";
|
||||||
|
|
||||||
// --- CONFIGURAÇÃO DE ENGENHARIA ---
|
// --- CONFIGURAÇÃO DE ENGENHARIA ---
|
||||||
// Substitua pelo seu número real: CódigoPaís + DDD + Número (sem espaços ou símbolos)
|
// Substitua pelo seu número real: CódigoPaís + DDD + Número (sem espaços ou símbolos)
|
||||||
@@ -71,17 +72,17 @@ export default function Contact() {
|
|||||||
chat: "\u{1F4AC}"
|
chat: "\u{1F4AC}"
|
||||||
};
|
};
|
||||||
|
|
||||||
const messageTemplate = `*NOVA REQUISICAO DE PROJETO*
|
const messageTemplate = `*NOVA SOLICITAÇÃO DE PROJETO*
|
||||||
|
|
||||||
*Origem:* Portfolio Profissional
|
*Origem:* Portfólio Profissional
|
||||||
*Prioridade:* Alta (Lead Qualificado)
|
*Prioridade:* Alta (Lead Qualificado)
|
||||||
|
|
||||||
---
|
---
|
||||||
*CLIENTE:* ${formData.name}
|
*CLIENTE:* ${formData.name}
|
||||||
*EMAIL:* ${formData.email}
|
*EMAIL:* ${formData.email}
|
||||||
*AREA:* ${formData.subject.toUpperCase()}
|
*ÁREA:* ${formData.subject.toUpperCase()}
|
||||||
|
|
||||||
*DESCRICAO:*
|
*DESCRIÇÃO:*
|
||||||
"${formData.message}"
|
"${formData.message}"
|
||||||
---
|
---
|
||||||
_Mensagem gerada via Evandro.dev | Garuva, SC_`;
|
_Mensagem gerada via Evandro.dev | Garuva, SC_`;
|
||||||
@@ -94,102 +95,129 @@ _Mensagem gerada via Evandro.dev | Garuva, SC_`;
|
|||||||
};
|
};
|
||||||
|
|
||||||
const inputStyles =
|
const inputStyles =
|
||||||
"w-full bg-slate-950/50 border border-slate-800 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 outline-none transition-all duration-300 font-mono text-sm";
|
"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 =
|
const labelStyles =
|
||||||
"text-xs font-bold text-slate-500 uppercase tracking-widest mb-2 flex items-center gap-2";
|
"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 (
|
return (
|
||||||
<section id="contact" className="w-full max-w-2xl mx-auto p-6">
|
<section id="contact" className="w-full py-24 relative">
|
||||||
<form onSubmit={handleSubmit} className="space-y-6">
|
|
||||||
<div className="grid md:grid-cols-2 gap-6">
|
|
||||||
<div className="space-y-2">
|
<div className="w-full max-w-4xl mx-auto px-6 relative z-10">
|
||||||
<label className={labelStyles}>
|
<motion.div
|
||||||
<User className="w-3 h-3" /> Nome_Completo
|
initial={{ opacity: 0, y: 20 }}
|
||||||
</label>
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
<input
|
transition={{ duration: 0.8 }}
|
||||||
required
|
className="relative bg-zinc-900/40 backdrop-blur-xl border border-white/10 rounded-3xl p-8 md:p-12 shadow-2xl overflow-hidden"
|
||||||
type="text"
|
>
|
||||||
placeholder="Ex: Alan Turing"
|
<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" />
|
||||||
className={inputStyles}
|
|
||||||
value={formData.name}
|
<div className="mb-10 text-center space-y-2">
|
||||||
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<form onSubmit={handleSubmit} className="space-y-8 relative">
|
||||||
<label className={labelStyles}>
|
<div className="grid md:grid-cols-2 gap-8">
|
||||||
<Mail className="w-3 h-3" /> Endpoint_Email
|
<div className="space-y-3 group">
|
||||||
</label>
|
<label className={labelStyles}>
|
||||||
<input
|
<User className="w-3 h-3 text-blue-500" /> NOME COMPLETO
|
||||||
required
|
</label>
|
||||||
type="email"
|
<input
|
||||||
placeholder="[email protected]"
|
required
|
||||||
className={inputStyles}
|
type="text"
|
||||||
value={formData.email}
|
placeholder="Ex: Alan Turing"
|
||||||
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
className={inputStyles}
|
||||||
/>
|
value={formData.name}
|
||||||
</div>
|
onChange={(e) => setFormData({ ...formData, name: e.target.value })}
|
||||||
</div>
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="space-y-2">
|
<div className="space-y-3 group">
|
||||||
<label className={labelStyles}>
|
<label className={labelStyles}>
|
||||||
<Sparkles className="w-3 h-3" /> Tipo_de_Requisicao
|
<Mail className="w-3 h-3 text-blue-500" /> E-MAIL CORPORATIVO
|
||||||
</label>
|
</label>
|
||||||
<select
|
<input
|
||||||
className={inputStyles}
|
required
|
||||||
value={formData.subject}
|
type="email"
|
||||||
onChange={(e) => setFormData({ ...formData, subject: e.target.value })}
|
placeholder="[email protected]"
|
||||||
>
|
className={inputStyles}
|
||||||
<option value="full-stack">Desenvolvimento Full-Stack</option>
|
value={formData.email}
|
||||||
<option value="cloud">Cloud & Infrastructure</option>
|
onChange={(e) => setFormData({ ...formData, email: e.target.value })}
|
||||||
<option value="iot">Internet of Things (IoT)</option>
|
/>
|
||||||
<option value="consultancy">Consultoria Técnica Sênior</option>
|
</div>
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="space-y-2">
|
|
||||||
<label className={labelStyles}>
|
|
||||||
<MessageSquare className="w-3 h-3" /> Descricao_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-4">
|
|
||||||
<CustomShimmerButton
|
|
||||||
type="submit"
|
|
||||||
className="w-full h-14 rounded-xl shadow-2xl"
|
|
||||||
shimmerColor="#3b82f6"
|
|
||||||
shimmerSize="0.2em"
|
|
||||||
disabled={isSubmitting}
|
|
||||||
>
|
|
||||||
<div className="flex flex-row items-center justify-center gap-3 w-full h-full">
|
|
||||||
{isSubmitting ? (
|
|
||||||
<div className="w-6 h-6 border-2 border-white/30 border-t-white rounded-full animate-spin" />
|
|
||||||
) : (
|
|
||||||
<>
|
|
||||||
<span className="text-white font-bold tracking-widest uppercase text-sm leading-none">
|
|
||||||
Executar Request
|
|
||||||
</span>
|
|
||||||
<Send className="w-4 h-4 text-white" />
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</CustomShimmerButton>
|
|
||||||
</motion.div>
|
|
||||||
|
|
||||||
<footer className="pt-4">
|
<div className="space-y-3 group">
|
||||||
<p className="text-[10px] text-center text-slate-600 font-mono uppercase tracking-tighter">
|
<label className={labelStyles}>
|
||||||
Engenharia de Software de Alta Performance | Garuva, SC
|
<Sparkles className="w-3 h-3 text-blue-500" /> TIPO DE PROJETO
|
||||||
</p>
|
</label>
|
||||||
</footer>
|
<div className="relative">
|
||||||
</form>
|
<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>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
+123
-84
@@ -1,18 +1,21 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import React, { useMemo } from "react";
|
import React, { useMemo } from "react";
|
||||||
import { motion, AnimatePresence } from "motion/react";
|
import { motion } from "framer-motion";
|
||||||
import {
|
import {
|
||||||
Calendar, MapPin, Briefcase, Zap,
|
ChevronRight,
|
||||||
Award, Users, Terminal, ChevronRight
|
Terminal,
|
||||||
|
Cpu,
|
||||||
|
Network,
|
||||||
|
Code2
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { usePortfolioData } from "@/lib/usePortfolioData";
|
import { usePortfolioData } from "@/lib/usePortfolioData";
|
||||||
|
|
||||||
// --- Tipagem e Configuração (Otimização Pareto 80/20) ---
|
// --- Tipagem e Configuração (Otimização Pareto 80/20) ---
|
||||||
const CONFIG = {
|
const CONFIG = {
|
||||||
"innovo-tecnologia": { color: "text-blue-400", bg: "bg-blue-400/10", border: "border-blue-400/20" },
|
"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" },
|
"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" }
|
default: { color: "text-slate-400", bg: "bg-slate-400/10", border: "border-slate-400/20", icon: Code2 }
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function Experience() {
|
export default function Experience() {
|
||||||
@@ -33,33 +36,46 @@ export default function Experience() {
|
|||||||
if (loading) return <ExperienceSkeleton />;
|
if (loading) return <ExperienceSkeleton />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="experience" className="relative py-32 bg-[#030712] overflow-hidden">
|
<section id="experience" className="relative py-32">
|
||||||
<div className="max-w-6xl mx-auto px-6 relative">
|
{/* 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 */}
|
{/* Header com Identidade Visual */}
|
||||||
<div className="mb-24">
|
<div className="mb-24 text-center md:text-left">
|
||||||
<motion.span
|
<motion.div
|
||||||
initial={{ opacity: 0 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
whileInView={{ opacity: 1 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
className="text-blue-500 font-mono text-sm tracking-[0.4em] uppercase"
|
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"
|
||||||
>
|
>
|
||||||
System_History
|
<Terminal className="w-3 h-3" />
|
||||||
</motion.span>
|
<span>SYSTEM_LOGS.history</span>
|
||||||
<h2 className="text-5xl md:text-7xl font-black text-white mt-4 tracking-tighter">
|
</motion.div>
|
||||||
Trajetória <br />
|
<motion.h2
|
||||||
<span className="text-slate-700">Profissional</span>
|
initial={{ opacity: 0, y: 20 }}
|
||||||
</h2>
|
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>
|
||||||
|
|
||||||
{/* Timeline Desktop (Linha Central) */}
|
<div className="relative">
|
||||||
<div className="absolute left-[50%] top-[400px] bottom-0 w-[1px] bg-gradient-to-b from-blue-500/50 via-slate-800 to-transparent hidden lg:block" />
|
{/* 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-12">
|
<div className="space-y-24">
|
||||||
<AnimatePresence>
|
|
||||||
{sortedExperiences.map((exp, index) => (
|
{sortedExperiences.map((exp, index) => (
|
||||||
<ExperienceCard key={exp.id} exp={exp} index={index} />
|
<ExperienceCard key={exp.id} exp={exp} index={index} />
|
||||||
))}
|
))}
|
||||||
</AnimatePresence>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -70,86 +86,109 @@ export default function Experience() {
|
|||||||
function ExperienceCard({ exp, index }: { exp: any, index: number }) {
|
function ExperienceCard({ exp, index }: { exp: any, index: number }) {
|
||||||
const isEven = index % 2 === 0;
|
const isEven = index % 2 === 0;
|
||||||
const config = (CONFIG as any)[exp.company.toLowerCase().replace(/\s+/g, "-")] || CONFIG.default;
|
const config = (CONFIG as any)[exp.company.toLowerCase().replace(/\s+/g, "-")] || CONFIG.default;
|
||||||
|
const Icon = config.icon;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<div className={`relative flex flex-col ${isEven ? 'lg:flex-row' : 'lg:flex-row-reverse'} items-center gap-8 lg:gap-16`}>
|
||||||
// CORREÇÃO DO BUG: viewport com margin e once garante que a animação termine
|
|
||||||
initial={{ opacity: 0, y: 50 }}
|
{/* Node Central (O "Ponto" da Timeline) */}
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
<motion.div
|
||||||
viewport={{ once: true, amount: 0.3, margin: "0px 0px -100px 0px" }}
|
initial={{ scale: 0, opacity: 0 }}
|
||||||
transition={{
|
whileInView={{ scale: 1, opacity: 1 }}
|
||||||
duration: 0.8,
|
viewport={{ once: true }}
|
||||||
delay: index * 0.1,
|
transition={{ delay: 0.2, duration: 0.5 }}
|
||||||
ease: [0.16, 1, 0.3, 1] // Custom Ease-Out para suavidade premium
|
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"
|
||||||
}}
|
>
|
||||||
className={`relative flex flex-col ${isEven ? 'lg:flex-row' : 'lg:flex-row-reverse'} items-center gap-8 lg:gap-16`}
|
<div className="w-2 h-2 rounded-full bg-white animate-pulse" />
|
||||||
>
|
</motion.div>
|
||||||
|
|
||||||
{/* Indicador de Data na Timeline */}
|
{/* Indicador de Data na Timeline */}
|
||||||
<div className="hidden lg:flex flex-1 justify-end items-center">
|
<div className="hidden lg:flex flex-1 justify-end items-start pt-2">
|
||||||
<div className={`${isEven ? 'text-right' : 'text-left'} flex-1`}>
|
<motion.div
|
||||||
<span className="text-2xl font-mono font-bold text-slate-800 tracking-tighter group-hover:text-blue-500 transition-colors">
|
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]}
|
{exp.period.split(' ')[0]}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Card Principal */}
|
{/* Card Principal */}
|
||||||
<div className="flex-1 w-full">
|
<div className="flex-1 w-full relative group perspective-1000">
|
||||||
<motion.div
|
<motion.div
|
||||||
whileHover={{ y: -5 }} // Hover sutil sem quebrar o layout
|
initial={{ opacity: 0, x: isEven ? 50 : -50 }}
|
||||||
className={`p-8 rounded-3xl bg-slate-900/40 border ${config.border} backdrop-blur-xl relative overflow-hidden group transition-all`}
|
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`}
|
||||||
>
|
>
|
||||||
{/* Badge de Empresa */}
|
{/* Background Glow Effect on Hover */}
|
||||||
<div className="flex flex-wrap justify-between items-start gap-4 mb-6">
|
<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="space-y-1">
|
|
||||||
<h3 className="text-2xl font-bold text-white group-hover:text-blue-400 transition-colors">
|
{/* Badge de Empresa & Header */}
|
||||||
{exp.title}
|
<div className="relative z-10">
|
||||||
</h3>
|
<div className="flex flex-wrap justify-between items-start gap-4 mb-6">
|
||||||
<div className="flex items-center gap-2">
|
<div className="space-y-2">
|
||||||
<span className={`text-sm font-bold uppercase tracking-widest ${config.color}`}>
|
<div className="flex items-center gap-3">
|
||||||
{exp.company}
|
<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>
|
||||||
|
</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}
|
||||||
</span>
|
</span>
|
||||||
<span className="text-slate-600">•</span>
|
))}
|
||||||
<span className="text-xs text-slate-500 font-mono">{exp.type}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className={`px-4 py-1.5 rounded-full ${config.bg} border ${config.border} text-[10px] font-bold text-white uppercase`}>
|
|
||||||
{exp.period}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className="text-slate-400 leading-relaxed mb-8 text-sm">
|
|
||||||
{exp.description}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
{/* Conquistas (Técnica de Checklist de Engenharia) */}
|
|
||||||
<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 group/item">
|
|
||||||
<ChevronRight className={`w-4 h-4 mt-0.5 flex-shrink-0 transition-transform group-hover/item:translate-x-1 ${config.color}`} />
|
|
||||||
<span>{ach}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Tags de Tech (Aparência de Chip de Hardware) */}
|
|
||||||
<div className="flex flex-wrap gap-2 pt-6 border-t border-slate-800/50">
|
|
||||||
{exp.technologies?.map((tech: string) => (
|
|
||||||
<span key={tech} className="px-3 py-1 bg-slate-950 border border-slate-800 text-slate-500 text-[10px] rounded-md font-mono hover:text-blue-400 hover:border-blue-400/30 transition-all">
|
|
||||||
{tech}
|
|
||||||
</span>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Spacer para Timeline Desktop */}
|
{/* Spacer para Timeline Desktop */}
|
||||||
<div className="hidden lg:block flex-1" />
|
<div className="hidden lg:block flex-1" />
|
||||||
</motion.div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function ExperienceSkeleton() {
|
function ExperienceSkeleton() {
|
||||||
return <div className="h-96 flex items-center justify-center text-slate-600 font-mono animate-pulse">Loading_System_Nodes...</div>;
|
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>
|
||||||
|
);
|
||||||
}
|
}
|
||||||
@@ -21,10 +21,9 @@ export default function Hero() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="home" className="relative min-h-screen flex items-center justify-center overflow-hidden bg-[#030712] pt-28 md:pt-20">
|
<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 */}
|
{/* 1. Background Engineering: Grid Pattern & Radial Shading */}
|
||||||
<div className="absolute inset-0 z-0">
|
<div className="absolute inset-0 z-0 pointer-events-none">
|
||||||
<div className="absolute inset-0 bg-[linear-gradient(to_right,#1f2937_1px,transparent_1px),linear-gradient(to_bottom,#1f2937_1px,transparent_1px)] bg-[size:4rem_4rem] [mask-image:radial-gradient(ellipse_60%_50%_at_50%_0%,#000_70%,transparent_100%)] opacity-20" />
|
|
||||||
<div className="absolute inset-0 bg-radial-at-t from-blue-500/10 via-transparent to-transparent" />
|
<div className="absolute inset-0 bg-radial-at-t from-blue-500/10 via-transparent to-transparent" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ export default function Projects() {
|
|||||||
|
|
||||||
const projects = useMemo(() =>
|
const projects = useMemo(() =>
|
||||||
data?.projects.filter((p) => p.featured) || [],
|
data?.projects.filter((p) => p.featured) || [],
|
||||||
[data]);
|
[data]);
|
||||||
|
|
||||||
const projectsJsonLd = useMemo(() => {
|
const projectsJsonLd = useMemo(() => {
|
||||||
if (!projects || projects.length === 0) return null;
|
if (!projects || projects.length === 0) return null;
|
||||||
@@ -84,7 +84,7 @@ export default function Projects() {
|
|||||||
if (error) return <div className="py-20 text-center text-red-500">System_Error: Failed to sync projects.</div>;
|
if (error) return <div className="py-20 text-center text-red-500">System_Error: Failed to sync projects.</div>;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="projects" className="py-32 px-6 bg-[#030712] relative overflow-hidden">
|
<section id="projects" className="py-32 px-6 relative">
|
||||||
{/* Background Decor */}
|
{/* 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="absolute inset-0 bg-[radial-gradient(circle_at_center,_var(--tw-gradient-stops))] from-blue-500/5 via-transparent to-transparent pointer-events-none" />
|
||||||
|
|
||||||
@@ -188,9 +188,8 @@ function ProjectLink({ href, icon: Icon, highlight }: { href: string; icon: any;
|
|||||||
href={href}
|
href={href}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
className={`p-2 rounded-xl transition-all duration-300 hover:scale-110 ${
|
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'
|
||||||
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'
|
}`}
|
||||||
}`}
|
|
||||||
>
|
>
|
||||||
<Icon className="w-4 h-4" />
|
<Icon className="w-4 h-4" />
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
+27
-17
@@ -28,7 +28,12 @@ export default function Skills() {
|
|||||||
if (error || !data) return <ErrorState />;
|
if (error || !data) return <ErrorState />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="skills" className="relative py-32 px-6 bg-[#030712] overflow-hidden">
|
<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">
|
<div className="max-w-7xl mx-auto relative z-10">
|
||||||
|
|
||||||
{/* Header Seção */}
|
{/* Header Seção */}
|
||||||
@@ -39,20 +44,21 @@ export default function Skills() {
|
|||||||
className="flex flex-col md:flex-row justify-between items-end mb-20 gap-8"
|
className="flex flex-col md:flex-row justify-between items-end mb-20 gap-8"
|
||||||
>
|
>
|
||||||
<div className="text-left">
|
<div className="text-left">
|
||||||
<h2 className="text-sm font-mono text-blue-500 uppercase tracking-[0.3em] mb-4 flex items-center gap-2 font-bold">
|
<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-4 h-4" /> Stack de Engenharia
|
<Layers className="w-3 h-3" />
|
||||||
</h2>
|
<span>MODULE_REGISTRY.lib</span>
|
||||||
<h3 className="text-4xl md:text-6xl font-black text-white">
|
</div>
|
||||||
Arsenal <span className="text-slate-700">Tecnológico</span>
|
<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>
|
</h3>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-slate-400 max-w-md text-lg font-light leading-relaxed border-l border-slate-800 pl-6">
|
<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.
|
Sistemas resilientes construídos com precisão matemática e rigor arquitetural.
|
||||||
</p>
|
</p>
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|
||||||
{/* Grid de Habilidades */}
|
{/* Grid de Habilidades */}
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-6">
|
||||||
{data.skills.technical.map((category, index) => {
|
{data.skills.technical.map((category, index) => {
|
||||||
const Icon = ICON_MAP[category.icon] || Code;
|
const Icon = ICON_MAP[category.icon] || Code;
|
||||||
const styles = getCategoryStyles(category.color);
|
const styles = getCategoryStyles(category.color);
|
||||||
@@ -60,28 +66,32 @@ export default function Skills() {
|
|||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
key={category.category}
|
key={category.category}
|
||||||
initial={{ opacity: 0, y: 90 }}
|
initial={{ opacity: 0, y: 50 }}
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
viewport={{ once: true, amount: 0.5 }}
|
viewport={{ once: true, amount: 0.2 }}
|
||||||
transition={{
|
transition={{
|
||||||
duration: 0.5,
|
duration: 0.5,
|
||||||
delay: index * 0.05,
|
delay: index * 0.05,
|
||||||
ease: "easeOut"
|
ease: "easeOut"
|
||||||
}}
|
}}
|
||||||
|
className="h-full"
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className="group p-8 flex flex-col h-full bg-slate-900/50 border border-slate-800 rounded-lg transition-colors duration-300 cursor-default"
|
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="flex items-start justify-between mb-8">
|
||||||
<div className={`p-4 rounded-2xl ${styles.bg} border border-white/5 transition-all duration-500 group-hover:scale-110 group-hover:rotate-3`}>
|
<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 ${styles.icon}`} />
|
<Icon className={`w-8 h-8 text-slate-400 group-hover:text-blue-400 transition-colors`} />
|
||||||
</div>
|
</div>
|
||||||
<span className="text-[10px] font-mono text-slate-700 uppercase tracking-widest font-bold">
|
<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}
|
ID_0{index}X
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<h3 className="text-xl font-bold text-white mb-6">
|
<h3 className="text-xl font-bold text-white mb-6 group-hover:text-blue-200 transition-colors">
|
||||||
{category.category}
|
{category.category}
|
||||||
</h3>
|
</h3>
|
||||||
|
|
||||||
@@ -89,7 +99,7 @@ export default function Skills() {
|
|||||||
{category.skills.map((skill, sIdx) => (
|
{category.skills.map((skill, sIdx) => (
|
||||||
<span
|
<span
|
||||||
key={sIdx}
|
key={sIdx}
|
||||||
className="px-3 py-1 text-[11px] font-mono bg-slate-950/50 text-slate-400 rounded border border-slate-800 group-hover:border-blue-500/30 transition-all duration-300"
|
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}
|
{skill}
|
||||||
</span>
|
</span>
|
||||||
|
|||||||
+148
-142
@@ -1,7 +1,7 @@
|
|||||||
"use client"; // <--- DIRETIVA OBRIGATÓRIA NA PRIMEIRA LINHA
|
"use client";
|
||||||
|
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import { motion } from "motion/react";
|
import { motion } from "framer-motion";
|
||||||
import {
|
import {
|
||||||
Briefcase,
|
Briefcase,
|
||||||
Clock,
|
Clock,
|
||||||
@@ -12,9 +12,12 @@ import {
|
|||||||
User,
|
User,
|
||||||
Star,
|
Star,
|
||||||
CheckCircle2,
|
CheckCircle2,
|
||||||
|
Terminal,
|
||||||
|
ShieldCheck,
|
||||||
|
Zap,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
|
|
||||||
// --- 1. DATA (Definido aqui para evitar erros de serialização) ---
|
// --- 1. DATA ---
|
||||||
const WorkModalitiesData = [
|
const WorkModalitiesData = [
|
||||||
{
|
{
|
||||||
title: "Intervenção Ad-hoc",
|
title: "Intervenção Ad-hoc",
|
||||||
@@ -28,16 +31,14 @@ const WorkModalitiesData = [
|
|||||||
"Diagnóstico Sênior Imediato",
|
"Diagnóstico Sênior Imediato",
|
||||||
"Sem Compromisso de Longo Prazo",
|
"Sem Compromisso de Longo Prazo",
|
||||||
"Documentação de Alterações",
|
"Documentação de Alterações",
|
||||||
"Code Review de Segurança",
|
|
||||||
"Deploy assistido",
|
|
||||||
],
|
],
|
||||||
icon: Lightbulb,
|
icon: Zap,
|
||||||
color: "blue",
|
color: "blue",
|
||||||
featured: false,
|
featured: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Alocação de Sprint",
|
title: "Alocação de Sprint",
|
||||||
value: "Até 21% de Eficiência Financeira",
|
value: "Eficiência Financeira",
|
||||||
badge: "MAIS ESCOLHIDO",
|
badge: "MAIS ESCOLHIDO",
|
||||||
idealFor:
|
idealFor:
|
||||||
"Desenvolvimento contínuo de features com previsibilidade de budget e prioridade técnica.",
|
"Desenvolvimento contínuo de features com previsibilidade de budget e prioridade técnica.",
|
||||||
@@ -46,8 +47,6 @@ const WorkModalitiesData = [
|
|||||||
benefits: [
|
benefits: [
|
||||||
"Taxa horária reduzida (Lock-in)",
|
"Taxa horária reduzida (Lock-in)",
|
||||||
"Garantia de Disponibilidade",
|
"Garantia de Disponibilidade",
|
||||||
"Preço fixo por 180 dias",
|
|
||||||
"Relatórios de Burndown",
|
|
||||||
"Prioridade em Critical Bugs",
|
"Prioridade em Critical Bugs",
|
||||||
],
|
],
|
||||||
icon: Briefcase,
|
icon: Briefcase,
|
||||||
@@ -55,7 +54,7 @@ const WorkModalitiesData = [
|
|||||||
featured: true,
|
featured: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Engenheiro Dedicado (Retainer)",
|
title: "Engenheiro Dedicado",
|
||||||
value: "A partir de R$ 2.800/mês",
|
value: "A partir de R$ 2.800/mês",
|
||||||
badge: "VIP & ESTRATÉGICO",
|
badge: "VIP & ESTRATÉGICO",
|
||||||
idealFor:
|
idealFor:
|
||||||
@@ -65,17 +64,15 @@ const WorkModalitiesData = [
|
|||||||
benefits: [
|
benefits: [
|
||||||
"SLA de Resposta Prioritária",
|
"SLA de Resposta Prioritária",
|
||||||
"Gestão de Débito Técnico",
|
"Gestão de Débito Técnico",
|
||||||
"Planejamento de Roadmap",
|
|
||||||
"Monitoramento de Infraestrutura",
|
|
||||||
"Consultoria Estratégica",
|
"Consultoria Estratégica",
|
||||||
],
|
],
|
||||||
icon: Clock,
|
icon: ShieldCheck,
|
||||||
color: "purple",
|
color: "purple",
|
||||||
featured: false,
|
featured: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "Squad por Projeto",
|
title: "Squad por Projeto",
|
||||||
value: "Escopo Fechado / Milestone-based",
|
value: "Escopo Fechado",
|
||||||
badge: "CONTROLE TOTAL",
|
badge: "CONTROLE TOTAL",
|
||||||
idealFor:
|
idealFor:
|
||||||
"Produtos novos ou migrações de sistemas onde o orçamento é rigorosamente definido pelo escopo.",
|
"Produtos novos ou migrações de sistemas onde o orçamento é rigorosamente definido pelo escopo.",
|
||||||
@@ -83,10 +80,8 @@ const WorkModalitiesData = [
|
|||||||
"Desenvolvimento orientado a entregáveis (milestones). Mitigação de riscos através de definições claras de 'Done'.",
|
"Desenvolvimento orientado a entregáveis (milestones). Mitigação de riscos através de definições claras de 'Done'.",
|
||||||
benefits: [
|
benefits: [
|
||||||
"Estimativa de Riscos Inclusa",
|
"Estimativa de Riscos Inclusa",
|
||||||
"Cronograma de Entregas (Milestones)",
|
"Cronograma de Entregas",
|
||||||
"Garantia Pós-Entrega",
|
"Garantia Pós-Entrega",
|
||||||
"Foco em Time-to-Market",
|
|
||||||
"Transferência de Conhecimento",
|
|
||||||
],
|
],
|
||||||
icon: HardHat,
|
icon: HardHat,
|
||||||
color: "orange",
|
color: "orange",
|
||||||
@@ -94,173 +89,184 @@ const WorkModalitiesData = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
// --- 2. DESIGN TOKENS ---
|
// --- 2. COMPONENTS ---
|
||||||
const COLOR_VARIANTS: Record<string, string> = {
|
|
||||||
blue: "border-blue-500/15 bg-blue-500/3 text-blue-300 hover:border-blue-500/30 shadow-blue-500/3",
|
|
||||||
green:
|
|
||||||
"border-green-500/15 bg-green-500/3 text-green-300 hover:border-green-500/30 shadow-green-500/3",
|
|
||||||
purple:
|
|
||||||
"border-purple-500/15 bg-purple-500/3 text-purple-300 hover:border-purple-500/30 shadow-purple-500/3",
|
|
||||||
orange:
|
|
||||||
"border-orange-500/15 bg-orange-500/3 text-orange-300 hover:border-orange-500/30 shadow-orange-500/3",
|
|
||||||
};
|
|
||||||
|
|
||||||
export function WorkModalities() {
|
export function WorkModalities() {
|
||||||
return (
|
return (
|
||||||
<section
|
<section
|
||||||
id="modalities"
|
id="modalities"
|
||||||
className="py-32 px-6 bg-[#030712] relative overflow-hidden"
|
className="py-32 px-6 relative"
|
||||||
>
|
>
|
||||||
|
{/* Background Decor handled globally now, avoiding overflow-hidden here allows bleed */}
|
||||||
|
|
||||||
<div className="max-w-7xl mx-auto relative z-10">
|
<div className="max-w-7xl mx-auto relative z-10">
|
||||||
|
|
||||||
{/* Header Profissional */}
|
{/* Header Profissional */}
|
||||||
<motion.div
|
<div className="mb-24 text-center md:text-left">
|
||||||
initial={{ opacity: 0, y: 20 }}
|
<motion.div
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
initial={{ opacity: 0, y: 20 }}
|
||||||
viewport={{ once: true, margin: "-100px" }}
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
className="text-center mb-20"
|
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"
|
||||||
<div className="inline-flex items-center gap-2 bg-blue-500/10 px-4 py-2 rounded-full mb-6 border border-blue-500/20">
|
>
|
||||||
<div className="w-2 h-2 bg-blue-400 rounded-full animate-pulse"></div>
|
<Terminal className="w-3 h-3" />
|
||||||
<span className="text-xs font-bold text-blue-300 uppercase tracking-widest font-mono">
|
<span>CONTRACT_PROTOCOLS.sys</span>
|
||||||
Business_Architect
|
</motion.div>
|
||||||
</span>
|
<motion.h2
|
||||||
</div>
|
initial={{ opacity: 0, y: 20 }}
|
||||||
<h2 className="text-5xl md:text-7xl font-black text-white tracking-tighter leading-none mb-6">
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
Flexibilidade <span className="text-slate-700 italic">Total</span>
|
viewport={{ once: true }}
|
||||||
</h2>
|
transition={{ delay: 0.1 }}
|
||||||
<p className="text-slate-400 max-w-2xl mx-auto text-lg">
|
className="text-4xl md:text-6xl font-black text-white tracking-tighter"
|
||||||
Modelos de contratação adaptáveis para diferentes estágios de
|
>
|
||||||
maturidade tecnológica.
|
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>
|
</p>
|
||||||
</motion.div>
|
</div>
|
||||||
|
|
||||||
{/* Comparação de Mercado (Anchoring Effect) */}
|
{/* Comparação de Mercado (Anchoring Effect) */}
|
||||||
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-24">
|
||||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4 mb-20">
|
|
||||||
{/* AGÊNCIAS: Foco no Overhead */}
|
|
||||||
<MarketCard
|
<MarketCard
|
||||||
title="Agências Software House"
|
title="Agências Tradicionais"
|
||||||
price="R$ 15k - 80k+"
|
price="Alto Overhead"
|
||||||
icon={Building2}
|
icon={Building2}
|
||||||
color="text-red-500"
|
color="text-red-500"
|
||||||
description="Alto overhead administrativo e camadas de gestão que encarecem o setup inicial."
|
delay={0}
|
||||||
|
best={false}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* CONSULTORIAS: Foco na Diluição de Senioridade */}
|
|
||||||
<MarketCard
|
<MarketCard
|
||||||
title="Consultorias Big Tech"
|
title="Consultorias Big Tech"
|
||||||
price="R$ 180 - 450/h"
|
price="Custo Inflacionado"
|
||||||
icon={Users}
|
icon={Users}
|
||||||
color="text-orange-500"
|
color="text-orange-500"
|
||||||
description="Especialistas juniores alocados com margens de lucro agressivas sobre a hora sênior."
|
delay={0.1}
|
||||||
|
best={false}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* FREELANCERS: Foco no Risco de Sustentabilidade */}
|
|
||||||
<MarketCard
|
<MarketCard
|
||||||
title="Freelancers Generalistas"
|
title="Freelancers Generalistas"
|
||||||
price="R$ 40 - 90/h"
|
price="Risco Técnico"
|
||||||
icon={User}
|
icon={User}
|
||||||
color="text-yellow-500"
|
color="text-yellow-500"
|
||||||
description="Falta de rigor arquitetural e risco de manutenção por falta de documentação técnica."
|
delay={0.2}
|
||||||
|
best={false}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{/* VOCÊ: O Ponto de Equilíbrio (The Sweet Spot) */}
|
|
||||||
<MarketCard
|
<MarketCard
|
||||||
title="Engenheiro de Software"
|
title="Engenheiro Especialista"
|
||||||
price="R$ 35 - 75/h"
|
price="High ROI"
|
||||||
icon={Star}
|
icon={Star}
|
||||||
color="text-emerald-400"
|
color="text-emerald-400"
|
||||||
best={true}
|
best={true}
|
||||||
description="Acesso direto ao especialista. Engenharia de nível sênior com custo operacional zero de intermediação."
|
delay={0.3}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Grid de Modalidades */}
|
{/* Grid de Modalidades */}
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||||
{WorkModalitiesData.map((modality, index) => {
|
{WorkModalitiesData.map((modality, index) => (
|
||||||
const style = COLOR_VARIANTS[modality.color] || COLOR_VARIANTS.blue;
|
<ModalityCard key={modality.title} modality={modality} index={index} />
|
||||||
const Icon = modality.icon;
|
))}
|
||||||
|
|
||||||
return (
|
|
||||||
<motion.div
|
|
||||||
key={modality.title}
|
|
||||||
initial={{ opacity: 0, y: 30 }}
|
|
||||||
whileInView={{ opacity: 1, y: 0 }}
|
|
||||||
viewport={{ once: true, amount: 0.2 }}
|
|
||||||
transition={{ duration: 0.6, delay: index * 0.1 }}
|
|
||||||
className={`relative p-8 rounded-[2.5rem] border-2 backdrop-blur-xl transition-all duration-500 shadow-xl ${style}`}
|
|
||||||
>
|
|
||||||
{modality.badge && (
|
|
||||||
<span className="absolute -top-3 right-8 bg-blue-500/20 border border-blue-500/30 text-blue-300 px-4 py-1 rounded-full text-[10px] font-black tracking-widest uppercase shadow-lg shadow-blue-500/10">
|
|
||||||
{modality.badge}
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
|
|
||||||
<div className="flex items-center gap-6 mb-8">
|
|
||||||
<div className="p-4 rounded-2xl bg-slate-900 border border-slate-800 group-hover:scale-110 transition-transform">
|
|
||||||
<Icon className="w-8 h-8" />
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<h3 className="text-2xl font-bold text-white tracking-tight">
|
|
||||||
{modality.title}
|
|
||||||
</h3>
|
|
||||||
<p className="text-xl font-mono font-bold opacity-80">
|
|
||||||
{modality.value}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<p className="text-slate-400 text-sm leading-relaxed mb-8 h-20 line-clamp-3">
|
|
||||||
{modality.description}
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="space-y-3 mb-8">
|
|
||||||
{modality.benefits.map((benefit, bIdx) => (
|
|
||||||
<div
|
|
||||||
key={bIdx}
|
|
||||||
className="flex items-center gap-3 text-slate-300 text-xs font-medium"
|
|
||||||
>
|
|
||||||
<CheckCircle2 className="w-4 h-4 text-green-500 flex-shrink-0" />
|
|
||||||
<span>{benefit}</span>
|
|
||||||
</div>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="pt-6 border-t border-slate-800/50 mt-auto">
|
|
||||||
<p className="text-[11px] text-slate-500 leading-relaxed uppercase tracking-tighter">
|
|
||||||
<strong className="text-slate-400">Ideal para:</strong>{" "}
|
|
||||||
{modality.idealFor}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</motion.div>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function MarketCard({ title, price, icon: Icon, color, best }: any) {
|
function MarketCard({ title, price, icon: Icon, color, best, delay }: any) {
|
||||||
return (
|
return (
|
||||||
<div
|
<motion.div
|
||||||
className={`p-6 rounded-3xl border transition-all duration-500 ${
|
initial={{ opacity: 0, y: 20 }}
|
||||||
best
|
whileInView={{ opacity: 1, y: 0 }}
|
||||||
? "bg-green-500/10 border-green-500/30 shadow-lg shadow-green-500/5"
|
viewport={{ once: true }}
|
||||||
: "bg-slate-900/40 border-slate-800"
|
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)]"
|
||||||
<Icon className={`w-5 h-5 mb-4 ${color}`} />
|
: "bg-zinc-900/40 border-white/5 opacity-70 hover:opacity-100"
|
||||||
<h4 className="text-[10px] font-bold text-slate-500 uppercase tracking-widest">
|
|
||||||
{title}
|
|
||||||
</h4>
|
|
||||||
<p
|
|
||||||
className={`text-lg font-black mt-1 ${
|
|
||||||
best ? "text-green-400" : "text-slate-300"
|
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
|
<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>
|
||||||
|
<p className={`text-sm font-bold ${best ? "text-emerald-400" : "text-slate-500"}`}>
|
||||||
{price}
|
{price}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
+1
-1
@@ -143,7 +143,7 @@
|
|||||||
}
|
}
|
||||||
html,
|
html,
|
||||||
body {
|
body {
|
||||||
@apply bg-background text-foreground overflow-x-hidden max-w-[100vw];
|
@apply bg-[#030712] text-foreground overflow-x-hidden max-w-[100vw];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+7
-7
@@ -23,12 +23,12 @@ const geistMono = Geist_Mono({
|
|||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Evandro Fonseca Junior | Engenheiro de Software (Next.js, Tauri & Rust)",
|
title: "Evandro Fonseca Junior | Engenheiro de Software (Next.js, Tauri & Rust)",
|
||||||
description:
|
description:
|
||||||
"Engenheiro Full-Stack especializado em aplicações de alta performance e sistemas críticos. Desenvolvimento com Next.js 15, React 19, Tauri e arquitetura escalável.",
|
"Engenheiro Full-Stack especializado em aplicações de alta performance e sistemas críticos. Desenvolvimento com Next.js, React, Tauri e arquitetura escalável.",
|
||||||
keywords: [
|
keywords: [
|
||||||
"engenheiro full-stack",
|
"engenheiro full-stack",
|
||||||
"engenheiro de software",
|
"engenheiro de software",
|
||||||
"Next.js 15",
|
"Next.js",
|
||||||
"React 19",
|
"React",
|
||||||
"Tauri",
|
"Tauri",
|
||||||
"Rust",
|
"Rust",
|
||||||
"Python",
|
"Python",
|
||||||
@@ -61,7 +61,7 @@ export const metadata: Metadata = {
|
|||||||
openGraph: {
|
openGraph: {
|
||||||
title: "Evandro Fonseca Junior | Engenheiro de Software (Next.js, Tauri & Rust)",
|
title: "Evandro Fonseca Junior | Engenheiro de Software (Next.js, Tauri & Rust)",
|
||||||
description:
|
description:
|
||||||
"Engenheiro Full-Stack especializado em aplicações de alta performance e sistemas críticos. Desenvolvimento com Next.js 15, React 19, Tauri e arquitetura escalável.",
|
"Engenheiro Full-Stack especializado em aplicações de alta performance e sistemas críticos. Desenvolvimento com Next.js, React, Tauri e arquitetura escalável.",
|
||||||
url: "https://evandro.dev.br",
|
url: "https://evandro.dev.br",
|
||||||
siteName: "Evandro Fonseca Junior",
|
siteName: "Evandro Fonseca Junior",
|
||||||
locale: "pt_BR",
|
locale: "pt_BR",
|
||||||
@@ -79,7 +79,7 @@ export const metadata: Metadata = {
|
|||||||
card: "summary_large_image",
|
card: "summary_large_image",
|
||||||
title: "Evandro Fonseca Junior | Engenheiro de Software (Next.js, Tauri & Rust)",
|
title: "Evandro Fonseca Junior | Engenheiro de Software (Next.js, Tauri & Rust)",
|
||||||
description:
|
description:
|
||||||
"Engenheiro Full-Stack especializado em aplicações de alta performance e sistemas críticos. Desenvolvimento com Next.js 15, React 19, Tauri e arquitetura escalável.",
|
"Engenheiro Full-Stack especializado em aplicações de alta performance e sistemas críticos. Desenvolvimento com Next.js, React, Tauri e arquitetura escalável.",
|
||||||
creator: "@evandrodevbr",
|
creator: "@evandrodevbr",
|
||||||
images: ["/opengraph-image"],
|
images: ["/opengraph-image"],
|
||||||
},
|
},
|
||||||
@@ -124,8 +124,8 @@ export default function RootLayout({ children }: Readonly<RootLayoutProps>) {
|
|||||||
],
|
],
|
||||||
"knowsAbout": [
|
"knowsAbout": [
|
||||||
"Software Engineering",
|
"Software Engineering",
|
||||||
"Next.js 15",
|
"Next.js",
|
||||||
"React 19",
|
"React",
|
||||||
"Tauri",
|
"Tauri",
|
||||||
"Rust",
|
"Rust",
|
||||||
"TypeScript",
|
"TypeScript",
|
||||||
|
|||||||
+1
-1
@@ -55,7 +55,7 @@ const Contact = dynamic(() => import("./components/Contact"), {
|
|||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-black text-white relative">
|
<div className="min-h-screen text-white relative">
|
||||||
<div className="fixed inset-0 z-0 opacity-50">
|
<div className="fixed inset-0 z-0 opacity-50">
|
||||||
<Squares
|
<Squares
|
||||||
speed={0.5}
|
speed={0.5}
|
||||||
|
|||||||
+10
-25
@@ -60,9 +60,10 @@ const Squares: React.FC<SquaresProps> = ({
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
hoveredSquareRef.current &&
|
hoveredSquareRef.current &&
|
||||||
Math.floor((x - startX) / squareSize) ===
|
hoveredSquareRef.current.x >= squareX &&
|
||||||
hoveredSquareRef.current.x &&
|
hoveredSquareRef.current.x < squareX + squareSize &&
|
||||||
Math.floor((y - startY) / squareSize) === hoveredSquareRef.current.y
|
hoveredSquareRef.current.y >= squareY &&
|
||||||
|
hoveredSquareRef.current.y < squareY + squareSize
|
||||||
) {
|
) {
|
||||||
ctx.fillStyle = hoverFillColor;
|
ctx.fillStyle = hoverFillColor;
|
||||||
ctx.fillRect(squareX, squareY, squareSize, squareSize);
|
ctx.fillRect(squareX, squareY, squareSize, squareSize);
|
||||||
@@ -122,42 +123,26 @@ const Squares: React.FC<SquaresProps> = ({
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseMove = (event: MouseEvent) => {
|
const handleMouseMove = (event: MouseEvent) => {
|
||||||
|
if (!canvas) return;
|
||||||
const rect = canvas.getBoundingClientRect();
|
const rect = canvas.getBoundingClientRect();
|
||||||
const mouseX = event.clientX - rect.left;
|
const mouseX = event.clientX - rect.left;
|
||||||
const mouseY = event.clientY - rect.top;
|
const mouseY = event.clientY - rect.top;
|
||||||
|
hoveredSquareRef.current = { x: mouseX, y: mouseY };
|
||||||
const startX = Math.floor(gridOffset.current.x / squareSize) * squareSize;
|
|
||||||
const startY = Math.floor(gridOffset.current.y / squareSize) * squareSize;
|
|
||||||
|
|
||||||
const hoveredSquareX = Math.floor(
|
|
||||||
(mouseX + gridOffset.current.x - startX) / squareSize
|
|
||||||
);
|
|
||||||
const hoveredSquareY = Math.floor(
|
|
||||||
(mouseY + gridOffset.current.y - startY) / squareSize
|
|
||||||
);
|
|
||||||
|
|
||||||
if (
|
|
||||||
!hoveredSquareRef.current ||
|
|
||||||
hoveredSquareRef.current.x !== hoveredSquareX ||
|
|
||||||
hoveredSquareRef.current.y !== hoveredSquareY
|
|
||||||
) {
|
|
||||||
hoveredSquareRef.current = { x: hoveredSquareX, y: hoveredSquareY };
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleMouseLeave = () => {
|
const handleMouseLeave = () => {
|
||||||
hoveredSquareRef.current = null;
|
hoveredSquareRef.current = null;
|
||||||
};
|
};
|
||||||
|
|
||||||
canvas.addEventListener("mousemove", handleMouseMove);
|
window.addEventListener("mousemove", handleMouseMove);
|
||||||
canvas.addEventListener("mouseleave", handleMouseLeave);
|
document.addEventListener("mouseleave", handleMouseLeave); // Use document for leaving window
|
||||||
requestRef.current = requestAnimationFrame(updateAnimation);
|
requestRef.current = requestAnimationFrame(updateAnimation);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener("resize", resizeCanvas);
|
window.removeEventListener("resize", resizeCanvas);
|
||||||
if (requestRef.current) cancelAnimationFrame(requestRef.current);
|
if (requestRef.current) cancelAnimationFrame(requestRef.current);
|
||||||
canvas.removeEventListener("mousemove", handleMouseMove);
|
window.removeEventListener("mousemove", handleMouseMove);
|
||||||
canvas.removeEventListener("mouseleave", handleMouseLeave);
|
document.removeEventListener("mouseleave", handleMouseLeave);
|
||||||
};
|
};
|
||||||
}, [direction, speed, borderColor, hoverFillColor, squareSize]);
|
}, [direction, speed, borderColor, hoverFillColor, squareSize]);
|
||||||
|
|
||||||
|
|||||||
@@ -134,14 +134,14 @@ const Particles: React.FC<ParticlesProps> = ({
|
|||||||
resize();
|
resize();
|
||||||
|
|
||||||
const handleMouseMove = (e: MouseEvent) => {
|
const handleMouseMove = (e: MouseEvent) => {
|
||||||
const rect = container.getBoundingClientRect();
|
// Use window dimensions since the canvas is fixed full screen
|
||||||
const x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
|
const x = (e.clientX / window.innerWidth) * 2 - 1;
|
||||||
const y = -(((e.clientY - rect.top) / rect.height) * 2 - 1);
|
const y = -((e.clientY / window.innerHeight) * 2 - 1);
|
||||||
mouseRef.current = { x, y };
|
mouseRef.current = { x, y };
|
||||||
};
|
};
|
||||||
|
|
||||||
if (moveParticlesOnHover) {
|
if (moveParticlesOnHover) {
|
||||||
container.addEventListener("mousemove", handleMouseMove);
|
window.addEventListener("mousemove", handleMouseMove);
|
||||||
}
|
}
|
||||||
|
|
||||||
const count = particleCount;
|
const count = particleCount;
|
||||||
@@ -227,7 +227,7 @@ const Particles: React.FC<ParticlesProps> = ({
|
|||||||
return () => {
|
return () => {
|
||||||
window.removeEventListener("resize", resize);
|
window.removeEventListener("resize", resize);
|
||||||
if (moveParticlesOnHover) {
|
if (moveParticlesOnHover) {
|
||||||
container.removeEventListener("mousemove", handleMouseMove);
|
window.removeEventListener("mousemove", handleMouseMove);
|
||||||
}
|
}
|
||||||
cancelAnimationFrame(animationFrameId);
|
cancelAnimationFrame(animationFrameId);
|
||||||
if (container.contains(gl.canvas)) {
|
if (container.contains(gl.canvas)) {
|
||||||
|
|||||||
Reference in new issue
Block a user