Home mais curta (hero, vitrine de clientes, sobre, serviços, IA resumida, depoimentos e contato), faixa de clientes e telas reais no hero, sem números do GitHub. Barra de cookies fina, WhatsApp fixo no celular, formulário com e-mail opcional e alternativa por e-mail, página /ia, eventos de e-mail, redes e currículo e conversão do Ads opcional por variável. Rodapé em 3 colunas. Remove componentes órfãos (LighthouseMetrics, Skills, Experience, contagem do GitHub).
216 lines
9.1 KiB
TypeScript
216 lines
9.1 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
import { type CSSProperties, Fragment } from "react";
|
|
import { trackEmailClick, trackSocialClick } from "@/lib/analytics";
|
|
import { CONTACT_EMAIL, LINKEDIN_URL } from "@/lib/site-navigation";
|
|
import ClientStrip, { type ClientLink } from "./ClientStrip";
|
|
import HeroField from "./cinematic/HeroField";
|
|
import { HERO_CTA_ID } from "./FloatingWhatsApp";
|
|
import HeroWork, { type HeroWorkUrl } from "./HeroWork";
|
|
|
|
const SOCIAL_LINKS = [
|
|
{
|
|
href: LINKEDIN_URL,
|
|
label: "LinkedIn",
|
|
onClick: () => trackSocialClick({ network: "linkedin" }),
|
|
},
|
|
{
|
|
href: `mailto:${CONTACT_EMAIL}`,
|
|
label: "Email",
|
|
onClick: () => trackEmailClick({ placement: "hero" }),
|
|
},
|
|
];
|
|
|
|
/**
|
|
* Números reais, calculados em app/page.tsx (nada digitado à mão): os anos vêm de public/portfolio-data.json.
|
|
*/
|
|
export type HeroStats = {
|
|
years?: number;
|
|
};
|
|
|
|
type StatItem = {
|
|
value: string;
|
|
label: string;
|
|
};
|
|
|
|
function statItems(stats?: HeroStats): StatItem[] {
|
|
if (!stats?.years) return [];
|
|
return [{ value: String(stats.years), label: "anos de experiência" }];
|
|
}
|
|
|
|
/** 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]);
|
|
|
|
/** 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;
|
|
|
|
export default function Hero({
|
|
stats,
|
|
clients,
|
|
workUrl,
|
|
}: {
|
|
stats?: HeroStats;
|
|
clients?: ClientLink[];
|
|
/** Endereço mostrado na barra do navegador de HeroWork (o do site da Lavação 047). */
|
|
workUrl?: HeroWorkUrl;
|
|
}) {
|
|
const items = statItems(stats);
|
|
|
|
return (
|
|
<section
|
|
id="home"
|
|
className="hero relative w-full bg-[var(--ink)] font-display 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>
|
|
|
|
{/* Os espaços verticais acompanham a altura da tela (svh): subtítulo, botões e faixa de clientes precisam caber no palco fixo. */}
|
|
<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)+clamp(8px,2.5svh,32px))] pb-[clamp(16px,3svh,32px)] lg:px-sp-12">
|
|
<p className="hero-in mb-sp-4 flex items-center gap-sp-3 font-mono text-[12px] uppercase leading-[1.5] tracking-[0.14em] text-[rgba(244,242,237,.72)]">
|
|
<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. */}
|
|
{/* No desktop o título para antes da coluna HeroWork (largura da coluna: clamp(300px,24vw,400px)). */}
|
|
<h1 className="hero-title max-w-[12.5em] lg:max-w-[min(11em,54vw,calc(100%-clamp(300px,24vw,400px)-24px))] text-[clamp(38px,min(6vw,9svh),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}
|
|
>
|
|
{word}
|
|
</span>{" "}
|
|
</Fragment>
|
|
))}
|
|
</h1>
|
|
<p className="hero-in mt-sp-4 max-w-[34em] text-[clamp(16px,0.95rem+0.4vw,22px)] leading-[1.55] text-[rgba(244,242,237,.8)] [--d:.55s] lg:max-w-[min(40em,calc(100vw-clamp(300px,24vw,400px)-9rem))]">
|
|
Para empresas que cresceram além das planilhas e dos sistemas
|
|
prontos: sites, sistemas web, aplicativos e integrações, com contato
|
|
direto com quem desenvolve.
|
|
</p>
|
|
|
|
<div className="hero-in mt-sp-5 flex flex-col gap-sp-3 [--d:.65s] sm:flex-row sm:flex-wrap sm:items-center">
|
|
<Link
|
|
id={HERO_CTA_ID}
|
|
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]"
|
|
>
|
|
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"
|
|
>
|
|
<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/25 bg-transparent px-sp-5 py-sp-3 text-center text-[15px] font-medium leading-6 text-[#f4f2ed] no-underline transition-colors hover:border-white/55 motion-reduce:transition-none sm:text-[16px]"
|
|
>
|
|
Ver projetos entregues
|
|
</Link>
|
|
</div>
|
|
|
|
<ClientStrip
|
|
clients={clients}
|
|
className="hero-in mt-sp-4 [--d:.75s] lg:max-w-[min(44rem,calc(100vw-clamp(300px,24vw,400px)-9rem))]"
|
|
/>
|
|
|
|
<HeroWork url={workUrl} />
|
|
</div>
|
|
|
|
<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">
|
|
{SOCIAL_LINKS.map((link) => (
|
|
<a
|
|
key={link.label}
|
|
href={link.href}
|
|
target={link.href.startsWith("http") ? "_blank" : undefined}
|
|
rel={
|
|
link.href.startsWith("http")
|
|
? "noopener noreferrer"
|
|
: undefined
|
|
}
|
|
onClick={link.onClick}
|
|
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"
|
|
>
|
|
{link.label}
|
|
</a>
|
|
))}
|
|
<span className="text-[13px] text-[rgba(244,242,237,.55)]">
|
|
Santa Catarina · Atendimento remoto
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<p
|
|
aria-hidden="true"
|
|
className="hero-cue hidden items-center gap-sp-3 font-mono text-[12px] uppercase tracking-[0.14em] text-[rgba(244,242,237,.55)] lg:flex"
|
|
>
|
|
Role para mergulhar
|
|
<span className="hero-cue-line" />
|
|
</p>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
);
|
|
}
|