Files
Evandro Fonseca Junior 08dd046887 feat: add animated components for enhanced UI/UX
- Introduced DrawLine component for drawing animations.
- Added LazyRevealImage component for lazy loading images with reveal animations.
- Implemented TextDiffuseReveal for animated text reveals with staggered effects.
- Created TopProgressBar for a dynamic loading indicator.
- Added PointCloud mock for testing animations.
- Developed useReducedMotion hook to handle user preferences for reduced motion.
- Centralized animation configurations in config.ts for easier management.
- Defined animation variants in variants.ts for consistent animation effects.
- Established AnimationProvider to supply global animation settings.
- Created tests for AnimationProvider and useReducedMotion hook.
- Added SVG assets for branding and UI elements.
- Documented frontend review plan and hero circuit board animation plan.
2026-03-15 21:06:43 -03:00

213 lines
6.5 KiB
TypeScript

"use client";
import { motion, type Variants, useReducedMotion } from "framer-motion";
import { Section } from "./Section";
import { DrawLine } from "./animations/DrawLine";
interface Tier {
id: string;
badge: string;
level: string;
title: string;
tagline: string;
description: string;
deliverables: string[];
pricing: string;
pricingNote: string;
featured?: boolean;
ctaLabel: string;
ctaHref: string;
}
const TIERS: Tier[] = [
{
id: "discovery",
badge: "Entrada / Baixo Risco",
level: "01",
title: "Auditoria & Diagnostico Tecnico",
tagline: "Fotografia clara do problema antes de qualquer comprometimento.",
description:
"Analise profunda de arquitetura, performance ou seguranca com entrega de um plano de acao executavel. Ideal para quem precisa de direcao antes de contratar execucao.",
deliverables: [
"Relatorio de gargalos e divida tecnica",
"Mapeamento de riscos e dependencias",
"Sugestao de stack e cronograma de mitigacao",
"Plano de acao priorizado",
],
pricing: "Valor Fixo",
pricingNote: "Contrato unico. Entrega em ate 5 dias uteis.",
featured: false,
ctaLabel: "Solicitar diagnostico",
ctaHref: "#contact",
},
{
id: "cycles",
badge: "Mais Escolhido",
level: "02",
title: "Ciclos de Engenharia",
tagline: "Capacidade produtiva garantida. Entrega a cada 2 semanas.",
description:
"Desenvolvimento continuo com a previsibilidade de orcamento do escopo fechado e a agilidade de sprints. Milestones claros, sem amarras de contrato longo.",
deliverables: [
"Features em producao a cada ciclo",
"Refatoracao iterativa com rastreabilidade",
"Documentacao tecnica atualizada",
"Capacidade de pivotar prioridades no inicio de cada sprint",
],
pricing: "Fixo por Ciclo",
pricingNote: "Assinatura quinzenal ou mensal. Cancele a qualquer ciclo.",
featured: true,
ctaLabel: "Iniciar ciclo de engenharia",
ctaHref: "#contact",
},
{
id: "fractional",
badge: "Premium / High-Ticket",
level: "03",
title: "Fractional Tech Lead",
tagline: "Lideranca tecnica senior. Sem o custo de um CTO full-time.",
description:
"Direcao arquitetural, gestao de risco e sustentacao de operacoes criticas. Para empresas que precisam de decisoes de alto impacto com responsabilidade continua.",
deliverables: [
"SLA prioritario Zero-Risk",
"Code review e lideranca do time interno",
"Decisoes de infra: Cloud, VPS, CI/CD",
"Consultoria estrategica recorrente",
],
pricing: "Retainer Mensal",
pricingNote: "Renovacao mensal. Vagas limitadas.",
featured: false,
ctaLabel: "Agendar conversa estrategica",
ctaHref: "#contact",
},
];
function TierCard({ tier }: Readonly<{ tier: Tier }>) {
const shouldReduceMotion = useReducedMotion();
return (
<motion.article
tabIndex={0}
className={`ds-card h-full relative outline-none ${
tier.featured ? "border-[var(--slash)] shadow-sm" : ""
}`}
style={{
borderWidth: tier.featured ? "2px" : "1px",
}}
whileHover={
shouldReduceMotion
? undefined
: {
y: -4,
boxShadow: "0 24px 50px rgba(0, 0, 0, 0.08)",
}
}
whileTap={shouldReduceMotion ? undefined : { scale: 0.99 }}
transition={{ type: "spring", stiffness: 180, damping: 18 }}
>
<div className="flex justify-between items-start">
<span className="ds-kicker">{tier.badge}</span>
<span className="font-display text-[11px] font-extrabold tracking-widest text-[var(--meta)]">
{tier.level}
</span>
</div>
<DrawLine delay={0.04} />
<div className="mt-sp-4">
<h3 className="ds-card-title">{tier.title}</h3>
<p className="mt-sp-2 text-[11px] font-medium leading-relaxed text-[var(--meta)]">
{tier.tagline}
</p>
</div>
<p className="ds-copy">{tier.description}</p>
<ul className="ds-list">
{tier.deliverables.map((deliverable) => (
<li key={deliverable}>{deliverable}</li>
))}
</ul>
<div className="mt-sp-6 border-t border-[var(--border)] pt-sp-4">
<div className="font-display text-[clamp(14px,1.5vw,18px)] font-extrabold tracking-tight">
{tier.pricing}
</div>
<div className="mt-1 text-[10px] tracking-widest text-[var(--meta)] uppercase">
{tier.pricingNote}
</div>
</div>
<motion.a
href={tier.ctaHref}
className={`ds-link mt-sp-6 justify-center w-full ${
tier.featured ? "ds-link-accent" : "ds-link-muted"
}`}
whileHover={shouldReduceMotion ? undefined : { scale: 1.02 }}
whileTap={shouldReduceMotion ? undefined : { scale: 0.98 }}
transition={{ type: "spring", stiffness: 260, damping: 20 }}
>
{tier.ctaLabel}
{" ->"}
</motion.a>
</motion.article>
);
}
export function WorkModalities() {
const shouldReduceMotion = useReducedMotion();
const containerVariants: Variants = {
hidden: {},
visible: {
transition: {
delayChildren: 0.08,
staggerChildren: 0.08,
},
},
};
const itemVariants: Variants = {
hidden: shouldReduceMotion
? { opacity: 0 }
: { opacity: 0, y: 20, clipPath: "inset(100% 0 0 0)" },
visible: {
opacity: 1,
y: 0,
clipPath: "inset(0% 0 0 0)",
transition: shouldReduceMotion
? { duration: 0.25 }
: { duration: 0.75, ease: [0.16, 1, 0.3, 1] },
},
};
return (
<Section
id="modalities"
eyebrow="CONTRACT_PROTOCOLS.sys"
title="Engenharia sob demanda. Previsibilidade de entrega sem overhead."
description="Tres niveis de contratacao desenhados como uma esteira de valor, do diagnostico rapido a parceria estrategica de longo prazo."
disableChildrenAnimation
>
<motion.div
style={{
display: "grid",
gridTemplateColumns: "repeat(3, minmax(0, 1fr))",
gap: "var(--sp-6)",
}}
className="max-md:grid-cols-1 max-md:!flex max-md:!flex-col"
initial="hidden"
whileInView="visible"
viewport={{ once: true, amount: 0.2 }}
variants={containerVariants}
>
{TIERS.map((tier) => (
<motion.div key={tier.id} variants={itemVariants}>
<TierCard tier={tier} />
</motion.div>
))}
</motion.div>
</Section>
);
}