wip: foto da pasta principal antes de trocar de branch
CI / verify (push) Failing after 5m29s

Estado do disco em 14/10/2026, sem filtrar. Inclui as copias .sync-conflict-* do Syncthing de 07/10 e os 15 arquivos que o Syncthing apagou; todo esse conteudo ja existia em outros commits. So havia fora do Git: AGENTS.md, CLAUDE.md e a alteracao em data/project-health.json.
This commit is contained in:
Evandro Fonseca Junior committed 2026-10-14 00:42:49 -03:00
1 parent b2f3899dc2
commit dfcadc71fa
77 files changed
+6944 -2419

No files matched your search

+457 -138
View File
@@ -1,188 +1,507 @@
import Link from "next/link";
import { type CSSProperties, Fragment } from "react";
import HeroField from "./cinematic/HeroField";
"use client";
import { motion, type Variants } from "motion/react";
import dynamic from "next/dynamic";
import { useEffect, useRef, useState } from "react";
import { useReducedMotion } from "@/app/hooks/useReducedMotion";
import { shouldLoadHeroScene } from "@/app/lib/should-load-hero-scene";
import { CV_URL } from "@/lib/site-navigation";
import { buildWhatsAppUrl } from "@/lib/whatsapp";
import { ControlRoomGlobe } from "./cinematic/ControlRoomGlobe";
import { HeroStarfield } from "./cinematic/HeroStarfield";
import MagneticButton from "./cinematic/MagneticButton";
import { TelemetryStrip } from "./cinematic/TelemetryStrip";
const HeroScene = dynamic(() => import("./cinematic/HeroScene"), {
ssr: false,
});
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,
},
];
/** Números reais, calculados em app/page.tsx a partir de public/portfolio-data.json (nada digitado à mão). */
export type HeroStats = {
years?: number;
projects?: number;
demos?: number;
};
const HERO_CONTENT = {
status: "Disponível para novas oportunidades",
chip: "Garuva · Joinville · Curitiba · Remoto",
eyebrow:
"Evandro Fonseca Junior | Engenheiro da Computação e Desenvolvedor de Software",
headline:
"Engenharia de software para sistemas que precisam operar de verdade.",
subtitle:
"Profissional disponível para oportunidades CLT e PJ em Garuva, Joinville, Norte de Santa Catarina e Curitiba; também atuo em consultoria para produtos, integrações e sistemas críticos.",
cta: "Conversar sobre uma oportunidade",
whatsappMessage:
"Olá! Quero conversar sobre uma oportunidade profissional em engenharia de software.",
sideLabel: "Contato profissional direto",
sideTitle: "Procurando um engenheiro da computação para o seu time?",
sideText:
"Vamos conversar sobre o contexto técnico, o desafio da vaga e como posso contribuir da arquitetura à entrega.",
points: [
"Engenharia full-stack com base em sistemas e integrações.",
"Next.js, TypeScript, Rust, IoT e arquitetura de software.",
"Disponível para times do Norte de SC, Curitiba e atuação remota.",
],
proof: [
{
label: "Operação",
value: "Fluxo pensado para discovery, build, deploy e sustentação.",
},
{
label: "Clareza",
value: "Menos ruído entre produto, engenharia e decisão técnica.",
},
{
label: "Resultado",
value: "Base pronta para operar, evoluir e receber próximo ciclo.",
},
],
} as const;
function statItems(stats?: HeroStats) {
if (!stats) return [];
return [
stats.years ? [String(stats.years), "anos de experiência"] : null,
stats.projects ? [String(stats.projects), "projetos publicados"] : null,
stats.demos ? [String(stats.demos), "com demonstração online"] : null,
].filter((item): item is [string, string] => item !== null);
function getGreeting(message: string) {
const hour = new Date().getHours();
if (hour >= 8 && hour < 12) return `Bom dia! ${message}`;
if (hour >= 13 && hour < 18) return `Boa tarde! ${message}`;
return message;
}
/** Título em palavras para a entrada em cascata; o texto segue inteiro para leitores de tela e buscadores. */
const TITLE =
"Sistemas sob medida para operações que não cabem em soluções prontas.".split(
" ",
);
const ACCENT = new Set([1, 2]);
export default function Hero() {
const heroRef = useRef<HTMLElement>(null);
const shouldReduceMotion = useReducedMotion();
const [shouldRenderHeroScene, setShouldRenderHeroScene] = useState(false);
const [isHeroVisible, setIsHeroVisible] = useState(true);
/** O que cada camada da cena significa, em linguagem simples. */
const CAPTIONS = [
["A tela", "O que sua equipe e seus clientes usam no dia a dia."],
[
"As integrações",
"Os programas que você já usa conversando entre si, sem retrabalho.",
],
["Os dados", "Suas informações guardadas, organizadas e seguras."],
] as const;
useEffect(() => {
const reducedMotion = window.matchMedia(
"(prefers-reduced-motion: reduce)",
).matches;
const coarsePointer = window.matchMedia("(pointer: coarse)").matches;
export default function Hero({ stats }: { stats?: HeroStats }) {
const items = statItems(stats);
if (
!shouldLoadHeroScene({
reducedMotion,
coarsePointer,
width: window.innerWidth,
})
) {
return;
}
const timer = window.setTimeout(() => setShouldRenderHeroScene(true), 1500);
return () => window.clearTimeout(timer);
}, []);
useEffect(() => {
const hero = heroRef.current;
if (!hero || !("IntersectionObserver" in window)) return undefined;
const observer = new IntersectionObserver(
([entry]) => setIsHeroVisible(entry?.isIntersecting ?? false),
{ threshold: 0 },
);
observer.observe(hero);
return () => observer.disconnect();
}, []);
const handleWhatsAppClick = () => {
window.open(
buildWhatsAppUrl(getGreeting(HERO_CONTENT.whatsappMessage)),
"_blank",
"noopener,noreferrer",
);
};
const heroContainer: Variants = {
hidden: shouldReduceMotion ? { opacity: 1 } : { opacity: 0 },
visible: {
opacity: 1,
transition: shouldReduceMotion
? { duration: 0 }
: { staggerChildren: 0.1, delayChildren: 0.3 },
},
};
const easeOut: [number, number, number, number] = [0.16, 1, 0.3, 1];
const heroItem: Variants = {
hidden: shouldReduceMotion
? { opacity: 1, y: 0, filter: "blur(0px)" }
: { opacity: 0, y: 40, filter: "blur(10px)" },
visible: {
opacity: 1,
y: 0,
filter: "blur(0px)",
transition: shouldReduceMotion
? { duration: 0 }
: { duration: 1, ease: easeOut },
},
};
const heroItemSlow: Variants = {
hidden: shouldReduceMotion
? { opacity: 1, y: 0, filter: "blur(0px)" }
: { opacity: 0, y: 60, filter: "blur(12px)" },
visible: {
opacity: 1,
y: 0,
filter: "blur(0px)",
transition: shouldReduceMotion
? { duration: 0 }
: { duration: 1.2, ease: easeOut },
},
};
return (
<section
ref={heroRef}
id="home"
className="hero relative w-full bg-[var(--ink)] font-display text-[#f4f2ed]"
className="relative flex min-h-[100svh] w-full items-center overflow-hidden bg-[var(--ink)] text-[#f4f2ed]"
>
<div className="hero-stage">
<div className="hero-glow" aria-hidden="true" />
<HeroField />
<div className="hero-scrim" aria-hidden="true" />
{/* Legendas do mergulho: aparecem enquanto a câmera atravessa cada camada (só no hero fixo, telas grandes). */}
<ol className="hero-captions">
{CAPTIONS.map(([title, text], index) => (
<li key={title} style={{ "--i": index } as CSSProperties}>
<span className="hero-caption-title">{title}</span>
{text}
</li>
))}
</ol>
{shouldRenderHeroScene && <HeroScene isVisible={isHeroVisible} />}
<div className="hero-content relative mx-auto flex w-full max-w-[max(1400px,var(--shell))] flex-1 flex-col justify-center px-sp-6 pt-[calc(var(--site-header-height)+32px)] pb-sp-8 lg:px-sp-12">
<p className="hero-in mb-sp-5 flex items-center gap-sp-3 font-mono text-[11px] uppercase leading-[1.5] tracking-[0.14em] text-[rgba(244,242,237,.72)] sm:text-[12px]">
<span aria-hidden="true" className="h-2 w-2 bg-[var(--slash)]" />
Evandro Fonseca Junior · Engenharia de software
</p>
{/* Sem opacity:0: as palavras pintam no primeiro quadro (LCP); a entrada anima só posição e foco. */}
<h1 className="hero-title max-w-[12.5em] lg:max-w-[min(11em,54vw)] text-[clamp(38px,min(6vw,10.5svh),124px)] font-semibold leading-[0.98] tracking-[-0.05em]">
{TITLE.map((word, index) => (
<Fragment key={word}>
<span
className={`hero-word${ACCENT.has(index) ? " text-[var(--slash)]" : ""}`}
style={{ "--i": index } as CSSProperties}
<div className="pointer-events-none absolute inset-0 z-[1] bg-[radial-gradient(ellipse_80%_60%_at_50%_40%,transparent_0%,rgba(10,10,10,0.4)_50%,var(--ink)_100%)]" />
<div className="pointer-events-none absolute inset-0 z-[1] bg-gradient-to-b from-transparent via-transparent to-[var(--ink)]" />
<div
className="pointer-events-none absolute inset-0 z-[2] opacity-[0.015]"
style={{
backgroundImage:
"repeating-linear-gradient(0deg, transparent, transparent 2px, rgba(255,255,255,0.03) 2px, rgba(255,255,255,0.03) 4px)",
}}
/>
<div className="pointer-events-none absolute inset-0 z-[2] bg-[radial-gradient(circle_at_50%_50%,transparent_40%,rgba(10,10,10,0.6)_100%)]" />
{/* pt >= altura do header fixo (52px): conteúdo do hero nunca nasce sob a barra */}
<div className="relative z-10 mx-auto flex w-full max-w-[1240px] flex-col px-sp-6 pt-sp-16 pb-sp-16 lg:max-h-none lg:px-sp-16 lg:pt-sp-14 lg:pb-sp-12">
<motion.div
initial={shouldReduceMotion ? false : "hidden"}
animate="visible"
variants={heroContainer}
className="grid grid-cols-[minmax(0,1fr)] items-start gap-sp-8 lg:grid-cols-[minmax(0,1.1fr)_minmax(340px,0.9fr)] lg:gap-sp-10"
>
<div className="max-w-[800px]">
<motion.div
className="mb-sp-5 flex flex-wrap items-center gap-sp-3"
variants={heroItem}
>
<div className="cinematic-glass inline-flex items-center gap-sp-3 px-4 py-2">
<motion.span
className="h-2 w-2 rounded-sm bg-[var(--slash)]"
animate={
shouldReduceMotion
? { opacity: 1 }
: {
opacity: [1, 0.25, 1],
boxShadow: [
"0 0 4px rgba(204,255,0,0.6)",
"0 0 12px rgba(204,255,0,0.2)",
"0 0 4px rgba(204,255,0,0.6)",
],
}
}
transition={
shouldReduceMotion
? { duration: 0 }
: {
duration: 2,
ease: "easeInOut",
repeat: Number.POSITIVE_INFINITY,
}
}
/>
<span className="font-mono text-[10px] uppercase tracking-[0.12em] text-[rgba(244,242,237,.82)]">
{HERO_CONTENT.status}
</span>
</div>
<div className="cinematic-glass inline-flex items-center gap-sp-2 px-4 py-2 text-[11px] text-[rgba(244,242,237,.72)]">
{HERO_CONTENT.chip}
</div>
</motion.div>
<motion.p
className="mb-sp-4 max-w-[560px] font-mono text-[10px] uppercase tracking-[0.12em] text-[var(--slash)]"
variants={heroItem}
>
{HERO_CONTENT.eyebrow}
</motion.p>
<motion.h1
className="max-w-[940px] font-display text-[clamp(1.9rem,5.2vw,4.75rem)] font-extrabold leading-[0.95] tracking-[-0.055em] lg:leading-[0.9] lg:text-balance"
variants={heroItemSlow}
>
{HERO_CONTENT.headline}
</motion.h1>
<motion.p
className="mt-sp-6 max-w-[700px] text-[16px] leading-8 text-[rgba(244,242,237,.88)] lg:text-[17px]"
variants={heroItem}
>
{HERO_CONTENT.subtitle}
</motion.p>
<motion.ul
className="mt-sp-8 grid gap-sp-3 sm:grid-cols-3"
variants={heroItem}
>
{HERO_CONTENT.points.map((point, i) => (
<motion.li
key={point}
className="cinematic-glass flex min-w-0 gap-sp-3 px-sp-4 py-sp-4 text-[13px] leading-6 text-[rgba(244,242,237,.82)]"
initial={
shouldReduceMotion
? false
: { opacity: 0, y: 30, scale: 0.95 }
}
animate={{ opacity: 1, y: 0, scale: 1 }}
transition={
shouldReduceMotion
? { duration: 0 }
: {
duration: 0.8,
delay: 0.8 + i * 0.1,
ease: [0.16, 1, 0.3, 1],
}
}
>
{word}
</span>{" "}
</Fragment>
))}
</h1>
<p className="hero-in mt-sp-5 max-w-[34em] text-[clamp(16px,0.95rem+0.4vw,22px)] leading-[1.55] text-[rgba(244,242,237,.8)] [--d:.55s]">
Projeto e desenvolvo sistemas web, aplicativos e integrações, do
entendimento do negócio à arquitetura e à entrega em produção.
</p>
<span className="mt-[8px] h-1.5 w-1.5 shrink-0 rounded-full bg-[var(--slash)] shadow-[0_0_8px_rgba(204,255,0,0.5)]" />
<span>{point}</span>
</motion.li>
))}
</motion.ul>
<div className="hero-in mt-sp-6 flex flex-col gap-sp-3 [--d:.65s] sm:flex-row sm:flex-wrap sm:items-center">
<Link
href="/#contact"
className="group inline-flex min-h-12 items-center justify-center gap-sp-3 bg-[var(--slash)] px-sp-4 py-sp-3 text-center text-[15px] font-semibold leading-6 text-[var(--ink)] no-underline transition-colors hover:bg-[#d9ff40] motion-reduce:transition-none sm:px-sp-6 sm:text-[16px]"
<motion.div
className="mt-sp-8 flex flex-wrap items-center gap-sp-4"
variants={heroItem}
>
Conversar sobre meu projeto
<svg
aria-hidden="true"
viewBox="0 0 16 16"
className="h-4 w-4 transition-transform group-hover:translate-x-1 motion-reduce:transition-none"
fill="none"
stroke="currentColor"
strokeWidth="2"
<MagneticButton
as="button"
type="button"
onClick={handleWhatsAppClick}
className="cinematic-cta-primary inline-flex min-h-12 items-center justify-center px-sp-8 font-mono text-[11px] font-bold uppercase tracking-[0.1em] text-[#0A0A0A] no-underline"
strength={0.25}
>
<path d="M2 8h11M9 3.5 13.5 8 9 12.5" />
</svg>
</Link>
<Link
href="/cases"
className="inline-flex min-h-12 items-center justify-center border border-white/15 bg-[rgba(10,10,10,.35)] px-sp-5 py-sp-3 text-center text-[15px] font-medium leading-6 text-[#f4f2ed] no-underline backdrop-blur-sm transition-colors hover:border-white/40 motion-reduce:transition-none sm:text-[16px]"
>
Ver projetos entregues
</Link>
</div>
</div>
<span className="relative z-10">{HERO_CONTENT.cta}</span>
</MagneticButton>
<div className="hero-foot relative mx-auto flex w-full max-w-[max(1400px,var(--shell))] flex-wrap items-end justify-between gap-sp-5 px-sp-6 pb-sp-5 lg:px-sp-12">
<div className="flex flex-wrap items-end gap-x-sp-8 gap-y-sp-4 border-t border-white/10 pt-sp-4">
{items.length > 0 && (
<dl className="flex gap-x-sp-5 gap-y-sp-4 sm:flex-wrap sm:gap-x-sp-8">
{items.map(([value, label]) => (
<div key={label} className="flex flex-col-reverse">
<dt className="text-[13px] leading-[1.4] text-[rgba(244,242,237,.64)]">
{label}
</dt>
<dd className="font-mono text-[clamp(22px,1.1rem+0.8vw,36px)] font-semibold leading-none tracking-[-0.03em] text-[var(--slash)]">
{value}
</dd>
</div>
))}
</dl>
)}
{/* no celular, serviços e redes ficam no menu e no rodapé: assim o hero cabe numa tela e pode ficar fixo */}
<div className="max-lg:hidden">
<ul className="flex flex-wrap gap-x-sp-5 gap-y-sp-2 text-[14px] leading-[1.5] text-[rgba(244,242,237,.8)]">
{["Sistemas web", "Aplicativos", "Integrações"].map(
(service) => (
<li key={service} className="flex items-center gap-sp-2">
<span
aria-hidden="true"
className="h-1 w-1 bg-[var(--slash)]"
/>
{service}
</li>
),
)}
</ul>
<div className="flex flex-wrap items-center gap-x-sp-5">
<MagneticButton
as="a"
href="/cases"
className="cinematic-cta-secondary inline-flex min-h-12 items-center justify-center px-sp-8 font-mono text-[11px] font-bold uppercase tracking-[0.1em] text-[#f4f2ed] no-underline"
strength={0.25}
>
Ver casos selecionados
</MagneticButton>
<MagneticButton
as="a"
href={CV_URL}
download
className="inline-flex min-h-12 items-center justify-center px-sp-2 text-[12px] font-medium text-[rgba(244,242,237,.76)] no-underline transition-colors duration-300 hover:text-[var(--slash)]"
strength={0.2}
>
Download CV
</MagneticButton>
</motion.div>
<motion.div
className="mt-sp-10 flex flex-wrap items-center gap-x-sp-6 gap-y-sp-3 border-t border-[rgba(255,255,255,.06)] pt-sp-6"
variants={heroItem}
>
<div className="inline-flex items-center gap-sp-3 text-[11px] uppercase tracking-[0.08em] text-[rgba(244,242,237,.68)]">
<span className="h-2 w-2 rounded-full bg-[var(--slash)] shadow-[0_0_8px_var(--slash)]" />
Garuva, SC / Brasil
</div>
<div className="inline-flex items-center gap-sp-3 text-[11px] uppercase tracking-[0.08em] text-[rgba(244,242,237,.68)]">
<span className="h-[1px] w-6 bg-[rgba(255,255,255,.2)]" />
Next.js, Tauri, Rust, Node.js
</div>
<div className="flex items-center gap-sp-2">
{SOCIAL_LINKS.map((link) => (
<a
<MagneticButton
key={link.label}
as="a"
href={link.href}
aria-label={link.label}
target={link.href.startsWith("http") ? "_blank" : undefined}
rel={
link.href.startsWith("http")
? "noopener noreferrer"
: undefined
}
className="inline-flex min-h-11 items-center text-[13px] text-[rgba(244,242,237,.64)] no-underline underline-offset-4 hover:text-[#f4f2ed] hover:underline"
className="cinematic-social-link inline-flex h-11 w-11 items-center justify-center rounded-full"
strength={0.4}
>
{link.label}
</a>
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill={link.stroke ? "none" : "currentColor"}
stroke={link.stroke ? "currentColor" : "none"}
strokeWidth={link.stroke ? "2" : undefined}
aria-hidden="true"
>
{link.icon}
</svg>
</MagneticButton>
))}
<span className="text-[13px] text-[rgba(244,242,237,.55)]">
Santa Catarina · Atendimento remoto
</span>
</div>
</div>
</motion.div>
</div>
<p
aria-hidden="true"
className="hero-cue hidden items-center gap-sp-3 font-mono text-[11px] uppercase tracking-[0.14em] text-[rgba(244,242,237,.55)] lg:flex"
<motion.aside
className="cinematic-aside-card relative overflow-hidden p-sp-6 lg:p-sp-8"
initial={
shouldReduceMotion
? false
: { opacity: 0, x: 60, rotateY: -8, transformPerspective: 1000 }
}
animate={{ opacity: 1, x: 0, rotateY: 0 }}
transition={
shouldReduceMotion
? { duration: 0 }
: {
duration: 1.2,
delay: 0.5,
ease: [0.16, 1, 0.3, 1],
}
}
>
Role para mergulhar
<span className="hero-cue-line" />
</p>
</div>
<motion.div
className="absolute inset-x-0 top-0 h-px bg-[linear-gradient(90deg,transparent,rgba(204,255,0,0.8),transparent)]"
initial={shouldReduceMotion ? false : { scaleX: 0 }}
animate={{ scaleX: 1 }}
transition={
shouldReduceMotion
? { duration: 0 }
: {
duration: 1.5,
delay: 1,
ease: [0.16, 1, 0.3, 1],
}
}
/>
<div className="mb-sp-6 space-y-sp-4">
<HeroStarfield />
<ControlRoomGlobe autoRotate={!shouldReduceMotion} />
<TelemetryStrip reducedMotion={shouldReduceMotion} />
</div>
<div className="border-b border-[rgba(255,255,255,0.08)] pb-sp-5">
<div className="font-mono text-[10px] uppercase tracking-[0.14em] text-[var(--slash)]">
{HERO_CONTENT.sideLabel}
</div>
<h2 className="mt-sp-3 font-display text-[clamp(1.35rem,3vw,2rem)] font-extrabold leading-[1.04] tracking-[-0.03em] text-[#f7f4ee]">
{HERO_CONTENT.sideTitle}
</h2>
<p className="mt-sp-4 text-[14px] leading-7 text-[rgba(244,242,237,.78)]">
{HERO_CONTENT.sideText}
</p>
<button
type="button"
onClick={handleWhatsAppClick}
className="mt-sp-5 inline-flex w-full items-center justify-center bg-[var(--slash)] px-sp-5 py-sp-4 font-mono text-[11px] font-bold uppercase tracking-[0.1em] text-[var(--ink)] transition hover:bg-white focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--slash)] focus-visible:ring-offset-2 focus-visible:ring-offset-[var(--ink)]"
>
{/* Ícone WhatsApp inline: lucide não tem brand icons */}
<svg
aria-hidden="true"
viewBox="0 0 24 24"
fill="currentColor"
className="h-4 w-4"
>
<path d="M12.04 2a9.88 9.88 0 0 0-8.41 4.7A9.88 9.88 0 0 0 2.1 12.6a9.8 9.8 0 0 0 1.34 5.02L2 22l4.53-1.38a9.86 9.86 0 0 0 5.51 1.66h.01a9.87 9.87 0 0 0 7.02-2.9 9.79 9.79 0 0 0 2.89-6.98 9.73 9.73 0 0 0-.29-2.35A9.88 9.88 0 0 0 12.04 2Zm5.77 13.94c-.24.68-1.42 1.3-1.96 1.35-.5.05-1.13.24-3.8-.98-3.18-1.44-5.15-4.72-5.31-4.93-.15-.22-1.25-1.75-1.16-3.29.09-1.54.92-2.28 1.23-2.59.31-.31.66-.37.88-.36h.63c.2 0 .47-.03.71.58.26.65.88 2.25.96 2.41.08.17.13.36.02.57-.11.22-.17.35-.33.54-.17.19-.35.43-.5.58-.16.16-.33.34-.15.66.18.32.81 1.42 1.75 2.3 1.2 1.14 2.21 1.49 2.52 1.66.31.17.5.14.68-.09.18-.22.78-.99.99-1.33.21-.34.42-.27.7-.16.29.11 1.82.95 2.13 1.12.31.17.52.26.6.4.07.15.07.83-.17 1.51Z" />
</svg>
Agendar no WhatsApp
</button>
</div>
<div className="mt-sp-6 space-y-sp-5">
{HERO_CONTENT.proof.map((item, index) => (
<motion.article
key={item.label}
className="border-b border-[rgba(255,255,255,0.08)] pb-sp-5 last:border-b-0 last:pb-0"
initial={
shouldReduceMotion
? false
: { opacity: 0, y: 20, filter: "blur(6px)" }
}
animate={{ opacity: 1, y: 0, filter: "blur(0px)" }}
transition={
shouldReduceMotion
? { duration: 0 }
: {
duration: 0.8,
delay: 0.8 + index * 0.12,
ease: [0.16, 1, 0.3, 1],
}
}
>
<div className="font-mono text-[10px] uppercase tracking-[0.12em] text-[rgba(244,242,237,.56)]">
{item.label}
</div>
<p className="mt-sp-2 text-[14px] leading-7 text-[rgba(244,242,237,.92)]">
{item.value}
</p>
</motion.article>
))}
</div>
</motion.aside>
</motion.div>
</div>
<motion.div
className="absolute bottom-8 left-1/2 z-10 -translate-x-1/2"
initial={shouldReduceMotion ? false : { opacity: 0, y: -10 }}
animate={{ opacity: 1, y: 0 }}
transition={
shouldReduceMotion ? { duration: 0 } : { delay: 2, duration: 1 }
}
>
{shouldReduceMotion ? (
<div className="flex flex-col items-center gap-2" aria-hidden="true">
<span className="font-mono text-[10px] uppercase tracking-[0.18em] text-[rgba(244,242,237,.68)]">
Scroll
</span>
<div className="h-8 w-px bg-gradient-to-b from-[var(--slash)] to-transparent opacity-50" />
</div>
) : (
<motion.div
className="flex flex-col items-center gap-2"
animate={{ y: [0, 8, 0] }}
transition={{
duration: 2,
repeat: Number.POSITIVE_INFINITY,
ease: "easeInOut",
}}
>
<span className="font-mono text-[10px] uppercase tracking-[0.18em] text-[rgba(244,242,237,.68)]">
Scroll
</span>
<div className="h-8 w-px bg-gradient-to-b from-[var(--slash)] to-transparent opacity-50" />
</motion.div>
)}
</motion.div>
</section>
);
}