feat: rebrand a Manual de Identidad Visual v1.0

Migra todo el sitio a la marca oficial aprobada por la dueña:
- Logotipo cre≡er (el símbolo ≡ reemplaza la 'e' central, en dorado)
- Paleta: Azul Profundo #0A1D5D + Dorado Estratégico #C4942E + Blanco + neutros
- Tipografía: Aptos (Inter como fallback web), fin del sistema híbrido Georgia/Inter
- Tagline 'CLARIDAD · DIRECCIÓN · CRECIMIENTO' + eslogan 'Estrategia que transforma.'

Componentes, tokens de Tailwind, lib/content.ts, docs (dueña-directrices + spec)
y tests actualizados. Typecheck, lint, 27/27 tests y build estática verdes.

Basado en docs/branding.jpeg (Manual v1.0, mayo 2026).
This commit is contained in:
erikgh committed 2026-07-28 10:51:48 -06:00
1 parent 8fdfc1be5c
commit 60d3f22e6b
22 files changed
+212 -181

No files matched your search

+3 -3
View File
@@ -4,11 +4,11 @@ import { footer } from '@/lib/content';
export function Footer() {
return (
<footer className="bg-bg-dark text-cream border-t border-cream/10">
<footer className="bg-azul-profundo text-white border-t border-white/10">
<Container className="flex flex-col items-center gap-3 py-10 text-center md:flex-row md:justify-between md:text-left">
<Wordmark size="sm" onDark />
<p className="text-sm text-muted-dark">{footer.tagline}</p>
<p className="text-xs text-muted-dark">{footer.legal}</p>
<p className="text-sm text-gris-medio">{footer.tagline}</p>
<p className="text-xs text-gris-medio">{footer.legal}</p>
</Container>
</footer>
);
+1 -1
View File
@@ -5,7 +5,7 @@ import { header } from '@/lib/content';
export function Header() {
return (
<header className="sticky top-0 z-50 border-b border-ink/10 bg-base/80 backdrop-blur">
<header className="sticky top-0 z-50 border-b border-azul-profundo/10 bg-white/80 backdrop-blur">
<Container className="flex items-center justify-between py-3">
<a href="#top" aria-label="creeer — inicio">
<Wordmark size="md" />
+1 -1
View File
@@ -5,7 +5,7 @@ import { Footer } from '../Footer';
describe('<Footer />', () => {
it('renders the tagline and the legal line', () => {
render(<Footer />);
expect(screen.getByText('tu estratega de crecimiento')).toBeInTheDocument();
expect(screen.getByText('CLARIDAD · DIRECCIÓN · CRECIMIENTO')).toBeInTheDocument();
expect(screen.getByText(/© 2026 creeer/)).toBeInTheDocument();
});
it('does not introduce a second h1', () => {
+1 -1
View File
@@ -5,7 +5,7 @@ import { Header } from '../Header';
describe('<Header />', () => {
it('renders the wordmark', () => {
const { container } = render(<Header />);
expect(container.textContent).toContain('creeer');
expect(container.textContent).toContain('cre≡er');
});
it('renders a persistent CTA linking to the contact section', () => {
render(<Header />);
+2 -2
View File
@@ -1,7 +1,7 @@
type Variant = 'light' | 'dark';
const styles: Record<Variant, string> = {
light: 'bg-ink text-cream hover:bg-ink/90',
dark: 'bg-gold-bright text-ink hover:bg-gold-bright/90',
light: 'bg-azul-profundo text-white hover:bg-azul-profundo/90',
dark: 'bg-dorado text-white hover:bg-dorado/90',
};
export function CTA({ href, label, variant = 'light', className = '' }: { href: string; label: string; variant?: Variant; className?: string }) {
+5 -6
View File
@@ -9,15 +9,14 @@ const sizeClass: Record<Size, string> = {
};
export function Wordmark({ size = 'lg', onDark = false }: { size?: Size; onDark?: boolean }) {
const { before, accent, after, dot } = wordmark.parts;
const baseColor = onDark ? 'text-cream' : 'text-ink';
const goldColor = onDark ? 'text-gold-bright' : 'text-gold';
const { before, after } = wordmark.parts;
const baseColor = onDark ? 'text-white' : 'text-azul-profundo';
const goldColor = 'text-dorado';
return (
<span className={`font-serif italic font-bold tracking-tight leading-none ${sizeClass[size]} ${baseColor}`}>
<span className={`font-sans font-bold tracking-tight leading-none ${sizeClass[size]} ${baseColor}`}>
{before}
<span data-accent="true" className={`not-italic font-black ${goldColor}`}>{accent}</span>
<span data-accent="true" className={goldColor}>≡</span>
{after}
<span className={goldColor}>{dot}</span>
</span>
);
}
@@ -6,13 +6,13 @@ describe('<Wordmark />', () => {
it('renders the wordmark in lowercase', () => {
render(<Wordmark />);
const text = screen.getByText(/cre/).closest('span')?.textContent;
expect(text?.toLowerCase()).toBe('creeer.');
expect(text?.toLowerCase()).toBe('cre≡er');
});
it('renders the accent "e" separately for gold styling', () => {
it('renders the accent "≡" symbol separately for dorado styling', () => {
const { container } = render(<Wordmark />);
const accent = container.querySelector('[data-accent="true"]');
expect(accent).not.toBeNull();
expect(accent?.textContent).toBe('e');
expect(accent?.textContent).toBe('≡');
});
it('never renders uppercase CREEER', () => {
const { container } = render(<Wordmark />);
+8 -8
View File
@@ -10,25 +10,25 @@ export function Cierre() {
const reduced = usePrefersReducedMotion();
const { whatsapp, email, linkedin } = cierre.contact;
return (
<section id="contacto" className="bg-bg-dark text-cream scroll-mt-24">
<section id="contacto" className="bg-azul-profundo text-white scroll-mt-24">
<Container className="py-24 md:py-32 text-center">
<motion.div variants={staggerContainer} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)}
className="flex flex-col items-center">
<motion.div variants={fadeInUp}><Wordmark size="lg" onDark /></motion.div>
<motion.blockquote variants={fadeInUp}
className="mt-10 font-serif italic text-2xl md:text-4xl font-bold leading-snug max-w-3xl">
<span className="text-gold-bright">&ldquo;</span>{cierre.quote}<span className="text-gold-bright">&rdquo;</span>
className="mt-10 font-sans italic text-2xl md:text-4xl font-bold leading-snug max-w-3xl">
<span className="text-dorado">&ldquo;</span>{cierre.quote}<span className="text-dorado">&rdquo;</span>
</motion.blockquote>
<motion.div variants={fadeInUp} className="mt-10">
<CTA href={whatsapp.href} label={cierre.cta} variant="dark" />
</motion.div>
<motion.nav variants={fadeInUp} aria-label="Contacto" className="mt-12">
<ul className="flex flex-wrap justify-center gap-6 text-sm">
<li><a href={whatsapp.href} className="hover:text-gold-bright transition-colors">{whatsapp.label}</a></li>
<li aria-hidden="true" className="text-muted-dark">·</li>
<li><a href={email.href} className="hover:text-gold-bright transition-colors">{email.label}</a></li>
<li aria-hidden="true" className="text-muted-dark">·</li>
<li><a href={linkedin.href} className="hover:text-gold-bright transition-colors">{linkedin.label}</a></li>
<li><a href={whatsapp.href} className="hover:text-dorado transition-colors">{whatsapp.label}</a></li>
<li aria-hidden="true" className="text-gris-medio">·</li>
<li><a href={email.href} className="hover:text-dorado transition-colors">{email.label}</a></li>
<li aria-hidden="true" className="text-gris-medio">·</li>
<li><a href={linkedin.href} className="hover:text-dorado transition-colors">{linkedin.label}</a></li>
</ul>
</motion.nav>
</motion.div>
+7 -7
View File
@@ -10,20 +10,20 @@ export function ComoEmpezar() {
const reduced = usePrefersReducedMotion();
const waHref = `${cierre.contact.whatsapp.href}?text=${encodeURIComponent(comoEmpezar.whatsappText)}`;
return (
<section className="bg-base">
<section className="bg-white">
<Container className="py-24 md:py-32">
<motion.p variants={fadeInUp} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)}
className="text-xs tracking-[0.3em] uppercase font-semibold text-gold-deep">{comoEmpezar.eyebrow}</motion.p>
className="text-xs tracking-[0.3em] uppercase font-semibold text-dorado">{comoEmpezar.eyebrow}</motion.p>
<motion.h2 variants={fadeInUp} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)}
className="mt-4 font-serif italic font-bold text-3xl md:text-5xl text-ink">{comoEmpezar.title}</motion.h2>
className="mt-4 font-sans font-bold text-3xl md:text-5xl text-azul-profundo">{comoEmpezar.title}</motion.h2>
<motion.ol variants={staggerContainer} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)}
className="mt-12 grid gap-10 md:grid-cols-3">
{comoEmpezar.steps.map((step) => (
<motion.li key={step.n} variants={fadeInUp}
className="border-t border-gold/30 pt-6 transition-all duration-300 ease-out hover:-translate-y-1.5 hover:border-gold/70 motion-reduce:transform-none motion-reduce:transition-none">
<div className="font-serif italic font-black text-5xl md:text-6xl text-gold leading-none">{step.n}</div>
<h3 className="mt-4 font-sans font-bold text-xl text-ink">{step.name}</h3>
<p className="mt-3 text-muted leading-relaxed">{step.desc}</p>
className="border-t border-dorado/30 pt-6 transition-all duration-300 ease-out hover:-translate-y-1.5 hover:border-dorado/70 motion-reduce:transform-none motion-reduce:transition-none">
<div className="font-sans font-black text-5xl md:text-6xl text-dorado leading-none">{step.n}</div>
<h3 className="mt-4 font-sans font-bold text-xl text-azul-profundo">{step.name}</h3>
<p className="mt-3 text-gris-medio leading-relaxed">{step.desc}</p>
</motion.li>
))}
</motion.ol>
+7 -7
View File
@@ -7,21 +7,21 @@ import { fadeInUp, staggerContainer, viewportOnce, usePrefersReducedMotion, moti
export function Disciplinas() {
const reduced = usePrefersReducedMotion();
return (
<section className="bg-base">
<section className="bg-white">
<Container className="py-24 md:py-32">
<motion.p variants={fadeInUp} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)}
className="text-xs tracking-[0.3em] uppercase font-semibold text-gold-deep">{disciplinas.eyebrow}</motion.p>
className="text-xs tracking-[0.3em] uppercase font-semibold text-dorado">{disciplinas.eyebrow}</motion.p>
<motion.h2 variants={fadeInUp} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)}
className="mt-4 font-serif italic font-bold text-3xl md:text-5xl text-ink">{disciplinas.title}</motion.h2>
className="mt-4 font-sans font-bold text-3xl md:text-5xl text-azul-profundo">{disciplinas.title}</motion.h2>
<motion.p variants={fadeInUp} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)}
className="mt-4 text-muted text-lg">{disciplinas.lead}</motion.p>
className="mt-4 text-gris-medio text-lg">{disciplinas.lead}</motion.p>
<motion.ul variants={staggerContainer} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)}
className="mt-12 grid gap-8 md:grid-cols-3 list-none p-0">
{disciplinas.items.map((item) => (
<motion.li key={item.name} variants={fadeInUp}
className="border-t-2 border-gold pt-5 transition-all duration-300 ease-out hover:-translate-y-1.5 hover:border-gold-deep motion-reduce:transform-none motion-reduce:transition-none">
<h3 className="font-serif italic font-bold text-2xl text-ink">{item.name}</h3>
<p className="mt-3 text-muted leading-relaxed">{item.desc}</p>
className="border-t-2 border-dorado pt-5 transition-all duration-300 ease-out hover:-translate-y-1.5 hover:border-dorado motion-reduce:transform-none motion-reduce:transition-none">
<h3 className="font-sans font-bold text-2xl text-azul-profundo">{item.name}</h3>
<p className="mt-3 text-gris-medio leading-relaxed">{item.desc}</p>
</motion.li>
))}
</motion.ul>
+6 -6
View File
@@ -7,21 +7,21 @@ import { fadeInUp, staggerContainer, viewportOnce, usePrefersReducedMotion, moti
export function Estratega() {
const reduced = usePrefersReducedMotion();
return (
<section className="bg-base">
<section className="bg-white">
<Container className="py-24 md:py-32">
<motion.div variants={staggerContainer} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)}
className="grid gap-12 md:grid-cols-[auto_1fr] md:items-center">
{/* Portrait — placeholder gradient until dueña provides a photo */}
<motion.div variants={fadeInUp}
className="w-40 h-40 md:w-56 md:h-56 rounded-full flex-shrink-0 bg-gradient-to-br from-ink to-gold"
className="w-40 h-40 md:w-56 md:h-56 rounded-full flex-shrink-0 bg-gradient-to-br from-azul-profundo to-dorado"
aria-label="Retrato de la estratega"
role="img"
/>
<div>
<motion.p variants={fadeInUp} className="text-xs tracking-[0.3em] uppercase font-semibold text-gold-deep">{estratega.eyebrow}</motion.p>
<motion.h2 variants={fadeInUp} className="mt-4 font-serif italic font-bold text-3xl md:text-5xl text-ink">{estratega.title}</motion.h2>
<motion.p variants={fadeInUp} className="mt-6 text-lg text-muted leading-relaxed max-w-2xl">{estratega.bio}</motion.p>
<motion.blockquote variants={fadeInUp} className="mt-6 font-serif italic text-xl md:text-2xl text-ink border-l-4 border-gold pl-4">
<motion.p variants={fadeInUp} className="text-xs tracking-[0.3em] uppercase font-semibold text-dorado">{estratega.eyebrow}</motion.p>
<motion.h2 variants={fadeInUp} className="mt-4 font-sans font-bold text-3xl md:text-5xl text-azul-profundo">{estratega.title}</motion.h2>
<motion.p variants={fadeInUp} className="mt-6 text-lg text-gris-medio leading-relaxed max-w-2xl">{estratega.bio}</motion.p>
<motion.blockquote variants={fadeInUp} className="mt-6 font-sans italic text-xl md:text-2xl text-azul-profundo border-l-4 border-dorado pl-4">
{estratega.quote}
</motion.blockquote>
</div>
+5 -5
View File
@@ -8,7 +8,7 @@ import { fadeInUp, viewportOnce, usePrefersReducedMotion, motionProps } from '@/
export function FraseAncla() {
const reduced = usePrefersReducedMotion();
return (
<section className="bg-base">
<section className="bg-white">
<Container className="py-24 md:py-32 text-center max-w-4xl">
<motion.blockquote
variants={fadeInUp}
@@ -16,12 +16,12 @@ export function FraseAncla() {
whileInView="visible"
viewport={viewportOnce}
{...motionProps(reduced)}
className="font-serif italic text-2xl md:text-4xl font-bold leading-snug text-ink"
className="font-sans italic text-2xl md:text-4xl font-bold leading-snug text-azul-profundo"
>
<span className="text-gold">“</span>
<span className="text-dorado">“</span>
{fraseAncla.quote.before}{' '}
<span className="text-gold">{fraseAncla.quote.accent}</span>
<span className="text-gold">”</span>
<span className="text-dorado">{fraseAncla.quote.accent}</span>
<span className="text-dorado">”</span>
</motion.blockquote>
</Container>
</section>
+6 -6
View File
@@ -14,22 +14,22 @@ export function Hero({ variant = 'editorial' }: { variant?: Variant }) {
const isHormozi = variant === 'hormozi';
const headlineBase = isHormozi
? 'font-sans font-black uppercase tracking-tight'
: 'font-serif italic font-bold';
: 'font-sans font-bold';
const headlineSize = 'text-4xl md:text-6xl leading-[1.1]';
return (
<section className="bg-bg-pure">
<section className="bg-white">
<Container className="min-h-[88vh] flex flex-col items-center justify-center text-center py-24">
<motion.div variants={staggerContainer} initial="hidden" animate="visible" {...motionProps(reduced)} className="flex flex-col items-center">
<motion.div variants={fadeInUp}><Wordmark size="xl" /></motion.div>
<motion.p variants={fadeInUp} className="mt-6 text-xs md:text-sm tracking-[0.3em] uppercase font-semibold text-gold-deep">
<motion.p variants={fadeInUp} className="mt-6 text-xs md:text-sm tracking-[0.3em] uppercase font-semibold text-dorado">
{hero.eyebrow}
</motion.p>
<motion.h1 variants={fadeInUp} className={`mt-8 ${headlineBase} ${headlineSize} text-ink`}>
<motion.h1 variants={fadeInUp} className={`mt-8 ${headlineBase} ${headlineSize} text-azul-profundo`}>
{hero.headline.line1}{' '}
<span className="text-gold">{hero.headline.effort}</span>{' '}
<span className="text-dorado">{hero.headline.effort}</span>{' '}
{hero.headline.line3}{' '}
<span style={{ borderBottom: isHormozi ? '5px solid #B8901f' : '3px solid #B8901f', paddingBottom: 2 }}>
<span style={{ borderBottom: isHormozi ? '5px solid #C4942E' : '3px solid #C4942E', paddingBottom: 2 }}>
{hero.headline.clarity}
</span>
</motion.h1>
+6 -6
View File
@@ -8,20 +8,20 @@ import { fadeInUp, staggerContainer, viewportOnce, usePrefersReducedMotion, moti
export function Mecanismo() {
const reduced = usePrefersReducedMotion();
return (
<section className="bg-bg-dark text-cream">
<section className="bg-azul-profundo text-white">
<Container className="py-24 md:py-32">
<motion.p variants={fadeInUp} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)}
className="text-xs tracking-[0.3em] uppercase font-semibold text-gold-bright">{mecanismo.eyebrow}</motion.p>
className="text-xs tracking-[0.3em] uppercase font-semibold text-dorado">{mecanismo.eyebrow}</motion.p>
<motion.h2 variants={fadeInUp} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)}
className="mt-4 font-serif italic font-bold text-3xl md:text-5xl">{mecanismo.title}</motion.h2>
className="mt-4 font-sans font-bold text-3xl md:text-5xl">{mecanismo.title}</motion.h2>
<motion.ol variants={staggerContainer} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)}
className="mt-12 grid gap-10 md:grid-cols-3">
{mecanismo.steps.map((step) => (
<motion.li key={step.n} variants={fadeInUp}
className="border-t border-gold-bright/30 pt-6 transition-all duration-300 ease-out hover:-translate-y-1.5 hover:border-gold-bright/70 motion-reduce:transform-none motion-reduce:transition-none">
<div className="font-serif italic font-black text-5xl md:text-6xl text-gold-bright leading-none">{step.n}</div>
className="border-t border-dorado/30 pt-6 transition-all duration-300 ease-out hover:-translate-y-1.5 hover:border-dorado/70 motion-reduce:transform-none motion-reduce:transition-none">
<div className="font-sans font-black text-5xl md:text-6xl text-dorado leading-none">{step.n}</div>
<h3 className="mt-4 font-sans font-bold text-xl">{step.name}</h3>
<p className="mt-3 text-muted-dark leading-relaxed">{step.desc}</p>
<p className="mt-3 text-gris-medio leading-relaxed">{step.desc}</p>
</motion.li>
))}
</motion.ol>
+5 -5
View File
@@ -8,18 +8,18 @@ import { fadeInUp, staggerContainer, viewportOnce, usePrefersReducedMotion, moti
export function ParaQuien() {
const reduced = usePrefersReducedMotion();
return (
<section className="bg-base">
<section className="bg-white">
<Container className="py-24 md:py-32">
<motion.div variants={staggerContainer} initial="hidden" whileInView="visible" viewport={viewportOnce} {...motionProps(reduced)} className="max-w-3xl">
<motion.h2 variants={fadeInUp} className="font-serif italic font-bold text-3xl md:text-5xl text-ink leading-tight">
<motion.h2 variants={fadeInUp} className="font-sans font-bold text-3xl md:text-5xl text-azul-profundo leading-tight">
{paraQuien.title}
</motion.h2>
<motion.p variants={fadeInUp} className="mt-6 text-lg text-muted">{paraQuien.lead}</motion.p>
<motion.p variants={fadeInUp} className="mt-6 text-lg text-gris-medio">{paraQuien.lead}</motion.p>
<motion.ul variants={fadeInUp} className="mt-8 space-y-3">
{paraQuien.criteria.map((c) => (
<li key={c} className="flex items-start gap-3">
<span className="text-gold font-bold mt-1" aria-hidden="true">✓</span>
<span className="text-ink text-lg">{c}</span>
<span className="text-dorado font-bold mt-1" aria-hidden="true">✓</span>
<span className="text-azul-profundo text-lg">{c}</span>
</li>
))}
</motion.ul>