Alinear panel y auth con la identidad B2B "Architectural Warmth"

Sustituye la paleta negra/azul B2C del panel del reformista por el verde
de marca, neutros cálidos y titulares en Instrument Serif de la landing B2B.
Añade tokens --color-primary-*, --color-stone-50 y --font-display al @theme.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Carlos Narro
2026-06-01 20:01:57 +02:00
parent 15f2d67970
commit bf9e72064b
15 changed files with 54 additions and 45 deletions

View File

@@ -44,6 +44,15 @@
/* Fonts */
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--font-display: 'Instrument Serif', Georgia, 'Times New Roman', serif;
/* Paleta de marca B2B "Architectural Warmth" — panel y área autenticada */
--color-primary-50: #f4f8f5;
--color-primary-100: #e8f0eb;
--color-primary-500: #4d8a6d;
--color-primary-700: #2f5c46;
--color-primary-900: #1f3a2e;
--color-stone-50: #f7f8f7;
/* Transitions */
--transition-fast: 150ms ease;

View File

@@ -16,7 +16,7 @@ export default function LoginPage() {
>
<form action={formAction} className="flex flex-col gap-5">
<div>
<h1 className="text-2xl font-black tracking-tight text-black">Entra en tu panel</h1>
<h1 className="font-display text-3xl tracking-tight text-black">Entra en tu panel</h1>
<p className="mt-1 text-sm text-gray-500">Gestiona tus leads y tu funnel.</p>
</div>
@@ -27,7 +27,7 @@ export default function LoginPage() {
type="email"
required
autoComplete="email"
className="rounded-lg border border-gray-300 px-3 py-2.5 focus:border-gray-900 focus:outline-none focus:ring-1 focus:ring-gray-900"
className="rounded-lg border border-gray-300 px-3 py-2.5 focus:border-primary-700 focus:outline-none focus:ring-1 focus:ring-primary-700"
/>
</label>
<label className="flex flex-col gap-1 text-sm">
@@ -37,19 +37,19 @@ export default function LoginPage() {
type="password"
required
autoComplete="current-password"
className="rounded-lg border border-gray-300 px-3 py-2.5 focus:border-gray-900 focus:outline-none focus:ring-1 focus:ring-gray-900"
className="rounded-lg border border-gray-300 px-3 py-2.5 focus:border-primary-700 focus:outline-none focus:ring-1 focus:ring-primary-700"
/>
</label>
{error && <p className="text-sm text-red-600">{error}</p>}
<button type="submit" disabled={pending} className="btn btn-primary w-full disabled:opacity-60">
<button type="submit" disabled={pending} className="btn bg-primary-700 text-white hover:bg-primary-900 w-full disabled:opacity-60">
{pending ? 'Entrando…' : 'Entrar'}
</button>
<p className="text-center text-sm text-gray-500">
¿No tienes cuenta?{' '}
<Link href="/signup" className="font-semibold text-black hover:underline">
<Link href="/signup" className="font-semibold text-primary-700 hover:underline">
Empieza gratis
</Link>
</p>

View File

@@ -47,7 +47,7 @@ export default async function LeadDetailPage({ params }: { params: Promise<{ id:
return (
<div className="flex flex-col gap-6">
<Link href="/panel" className="text-sm text-gray-500 hover:text-black w-fit">
<Link href="/panel" className="text-sm text-gray-500 hover:text-primary-700 w-fit">
Volver a leads
</Link>
@@ -55,7 +55,7 @@ export default async function LeadDetailPage({ params }: { params: Promise<{ id:
<div className="bg-white border border-gray-200 rounded-xl p-5 flex flex-col gap-4">
<div className="flex flex-wrap items-start justify-between gap-4">
<div className="flex flex-col gap-1">
<h1 className="text-2xl font-black tracking-tight text-black">{lead.nombre}</h1>
<h1 className="font-display text-3xl tracking-tight text-black">{lead.nombre}</h1>
<p className="text-sm text-gray-500">
{lead.tipoReforma ? TIPO_LABEL[lead.tipoReforma] : 'Reforma'} ·{' '}
{lead.provincia ?? '—'} · entró {formatFecha(lead.createdAt)}
@@ -80,7 +80,7 @@ export default async function LeadDetailPage({ params }: { params: Promise<{ id:
<p className="text-sm text-gray-500">
Solicitada el {formatFecha(lead.testimonioSolicitadoAt)}. Comparte este enlace con el
cliente para que deje su opinión. Cuando la envíe, la verás en{' '}
<Link href="/panel/opiniones" className="text-black underline underline-offset-2">
<Link href="/panel/opiniones" className="text-primary-700 underline underline-offset-2">
Opiniones
</Link>{' '}
para aprobarla.
@@ -96,7 +96,7 @@ export default async function LeadDetailPage({ params }: { params: Promise<{ id:
<form action={solicitarOpinion.bind(null, lead.id)}>
<button
type="submit"
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-black text-white text-sm font-semibold w-fit hover:bg-gray-800"
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-primary-700 text-white text-sm font-semibold w-fit hover:bg-primary-900"
>
Solicitar opinión al cliente
</button>
@@ -254,7 +254,7 @@ export default async function LeadDetailPage({ params }: { params: Promise<{ id:
<div className="flex flex-wrap items-center gap-3">
<a
href={`/panel/${lead.id}/presupuesto?download=1`}
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-black text-white text-sm font-semibold w-fit hover:bg-gray-800"
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-primary-700 text-white text-sm font-semibold w-fit hover:bg-primary-900"
>
Descargar PDF
</a>
@@ -262,7 +262,7 @@ export default async function LeadDetailPage({ params }: { params: Promise<{ id:
href={`/panel/${lead.id}/presupuesto`}
target="_blank"
rel="noopener"
className="text-sm font-medium text-gray-500 hover:text-black"
className="text-sm font-medium text-gray-500 hover:text-primary-700"
>
Ver en el navegador
</a>
@@ -320,7 +320,7 @@ export default async function LeadDetailPage({ params }: { params: Promise<{ id:
<form action={recalcularPresupuesto.bind(null, lead.id)}>
<button
type="submit"
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-black text-white text-sm font-semibold w-fit hover:bg-gray-800"
className="inline-flex items-center gap-2 px-4 py-2 rounded-lg bg-primary-700 text-white text-sm font-semibold w-fit hover:bg-primary-900"
>
Recalcular desde el catálogo
</button>

View File

@@ -18,7 +18,7 @@ export default async function EmpresaPage() {
return (
<div className="space-y-10 max-w-2xl">
<div>
<h1 className="text-2xl font-black tracking-tight text-black">Datos de empresa</h1>
<h1 className="font-display text-3xl tracking-tight text-black">Datos de empresa</h1>
<p className="text-sm text-gray-500 mt-1">
Estos datos y el logo aparecen en la cabecera de los presupuestos en PDF que recibe el
cliente. Manténlos al día.
@@ -62,7 +62,7 @@ export default async function EmpresaPage() {
href={funnelUrl}
target="_blank"
rel="noopener noreferrer"
className="text-black underline underline-offset-2 break-all"
className="text-primary-700 underline underline-offset-2 break-all"
>
{funnelUrl}
</a>
@@ -159,7 +159,7 @@ export default async function EmpresaPage() {
type="checkbox"
name="aboutEnabled"
defaultChecked={perfil.aboutEnabled}
className="w-4 h-4 accent-black"
className="w-4 h-4 accent-[#2f5c46]"
/>
<span className="text-gray-700">Mostrar el bloque Quiénes somos en mi funnel</span>
</label>
@@ -185,7 +185,7 @@ export default async function EmpresaPage() {
/>
</label>
<button className="md:col-span-2 justify-self-start inline-flex items-center justify-center rounded-lg bg-black px-4 py-2 text-sm font-semibold text-white transition hover:bg-gray-900">
<button className="md:col-span-2 justify-self-start inline-flex items-center justify-center rounded-lg bg-primary-700 px-4 py-2 text-sm font-semibold text-white transition hover:bg-primary-900">
Guardar datos
</button>
</form>

View File

@@ -11,7 +11,7 @@ export default async function GaleriaPage() {
return (
<div className="space-y-8 max-w-3xl">
<div>
<h1 className="text-2xl font-black tracking-tight text-black">Galería de trabajos</h1>
<h1 className="font-display text-3xl tracking-tight text-black">Galería de trabajos</h1>
<p className="text-sm text-gray-500 mt-1">
Sube fotos de reformas que ya has hecho. Aparecen en tu funnel para dar confianza al
cliente antes de pedir presupuesto.

View File

@@ -27,14 +27,14 @@ export default async function PanelLayout({ children }: { children: React.ReactN
const nombreEmpresa = tenant?.nombreEmpresa ?? 'Reformix';
return (
<div className="min-h-screen bg-gray-50">
<div className="min-h-screen bg-stone-50">
<header className="sticky top-0 z-20 bg-white border-b border-gray-200">
<div className="relative max-w-6xl mx-auto px-6 h-16 flex items-center justify-between">
<Link href="/panel" className="flex items-center gap-2 min-w-0">
<span className="inline-flex shrink-0 items-center justify-center w-8 h-8 rounded-lg bg-black text-white font-black italic text-lg leading-none">
<span className="inline-flex shrink-0 items-center justify-center w-8 h-8 rounded-lg bg-primary-700 text-white font-black italic text-lg leading-none">
R
</span>
<span className="font-black tracking-tight text-black">Reformix</span>
<span className="font-bold tracking-tight text-black">Reformix</span>
<span className="hidden sm:inline text-gray-300">/</span>
<span className="hidden sm:inline text-sm font-medium text-gray-600 truncate">
{nombreEmpresa}

View File

@@ -33,7 +33,7 @@ export default async function OpinionesPage() {
return (
<div className="space-y-8 max-w-3xl">
<div>
<h1 className="text-2xl font-black tracking-tight text-black">Opiniones</h1>
<h1 className="font-display text-3xl tracking-tight text-black">Opiniones</h1>
<p className="text-sm text-gray-500 mt-1">
Las opiniones que te dejan tus clientes. Aprueba las que quieras mostrar en tu funnel; solo
las publicadas aparecen en tu página.

View File

@@ -50,7 +50,7 @@ export default async function PanelPage({
return (
<div className="flex flex-col gap-6">
<div className="flex flex-col gap-1">
<h1 className="text-2xl font-black tracking-tight text-black">Leads</h1>
<h1 className="font-display text-3xl tracking-tight text-black">Leads</h1>
<p className="text-sm text-gray-500">
{resumen.total} leads en total · {resumen.porEstado['nuevo'] ?? 0} sin contactar
</p>
@@ -79,7 +79,7 @@ export default async function PanelPage({
href={f.value === 'todos' ? '/panel' : `/panel?estado=${f.value}`}
className={`px-3 py-1.5 rounded-full text-sm font-medium border transition-colors ${
active
? 'bg-black text-white border-black'
? 'bg-primary-700 text-white border-primary-700'
: 'bg-white text-gray-600 border-gray-200 hover:border-gray-400'
}`}
>

View File

@@ -28,7 +28,7 @@ export default async function PreciosPage() {
return (
<div className="space-y-10">
<div>
<h1 className="text-2xl font-black tracking-tight text-black">Tabla de precios</h1>
<h1 className="font-display text-3xl tracking-tight text-black">Tabla de precios</h1>
<p className="text-sm text-gray-500 mt-1">
Define los precios unitarios y la mano de obra. El motor calcula el presupuesto a
partir de estos valores y las medidas del lead.
@@ -73,7 +73,7 @@ export default async function PreciosPage() {
</span>
</span>
</label>
<button className="self-start bg-black text-white rounded-lg px-4 py-2 text-sm font-medium">
<button className="self-start bg-primary-700 text-white rounded-lg px-4 py-2 text-sm font-medium">
Guardar preferencia
</button>
</form>
@@ -112,7 +112,7 @@ export default async function PreciosPage() {
/>
</label>
))}
<button className="col-span-2 md:col-span-5 justify-self-start bg-black text-white rounded-lg px-4 py-2 text-sm font-medium">
<button className="col-span-2 md:col-span-5 justify-self-start bg-primary-700 text-white rounded-lg px-4 py-2 text-sm font-medium">
Guardar configuración
</button>
</form>
@@ -212,7 +212,7 @@ export default async function PreciosPage() {
<label className="col-span-2 flex items-center gap-2 text-gray-500 sm:col-auto">
<input type="checkbox" name="esDefault" /> Marcar como default
</label>
<button className="col-span-2 bg-black text-white rounded-lg px-3 py-2 font-medium sm:col-auto sm:py-1.5">
<button className="col-span-2 bg-primary-700 text-white rounded-lg px-3 py-2 font-medium sm:col-auto sm:py-1.5">
Añadir
</button>
</form>
@@ -234,7 +234,7 @@ export default async function PreciosPage() {
placeholder="categoria,nombre,calidad,precio,unidad,descriptor_render,sku"
className="w-full border border-gray-300 rounded-lg px-3 py-2 font-mono text-xs"
/>
<button className="mt-2 bg-black text-white rounded-lg px-4 py-2 text-sm font-medium">
<button className="mt-2 bg-primary-700 text-white rounded-lg px-4 py-2 text-sm font-medium">
Importar
</button>
</form>

View File

@@ -6,7 +6,7 @@ import { signup } from './actions';
import AuthShell from '@/components/auth/AuthShell';
const inputClass =
'rounded-lg border border-gray-300 px-3 py-2.5 text-sm focus:border-gray-900 focus:outline-none focus:ring-1 focus:ring-gray-900';
'rounded-lg border border-gray-300 px-3 py-2.5 text-sm focus:border-primary-700 focus:outline-none focus:ring-1 focus:ring-primary-700';
export default function SignupPage() {
const [error, formAction, pending] = useActionState(signup, null);
@@ -19,7 +19,7 @@ export default function SignupPage() {
>
<form action={formAction} className="flex flex-col gap-4">
<div>
<h1 className="text-2xl font-black tracking-tight text-black">Empieza gratis 14 días</h1>
<h1 className="font-display text-3xl tracking-tight text-black">Empieza gratis 14 días</h1>
<p className="mt-1 text-sm text-gray-500">
Sin tarjeta. Configura tu catálogo y recibe leads.
</p>
@@ -51,19 +51,19 @@ export default function SignupPage() {
className={inputClass}
/>
<label className="flex items-center gap-2 text-xs text-gray-500">
<input name="optInMarketing" type="checkbox" className="h-4 w-4 accent-black" /> Quiero
<input name="optInMarketing" type="checkbox" className="h-4 w-4 accent-[#2f5c46]" /> Quiero
recibir novedades de Reformix
</label>
{error && <p className="text-sm text-red-600">{error}</p>}
<button type="submit" disabled={pending} className="btn btn-primary w-full disabled:opacity-60">
<button type="submit" disabled={pending} className="btn bg-primary-700 text-white hover:bg-primary-900 w-full disabled:opacity-60">
{pending ? 'Creando cuenta…' : 'Crear cuenta'}
</button>
<p className="text-center text-sm text-gray-500">
¿Ya tienes cuenta?{' '}
<Link href="/login" className="font-semibold text-black hover:underline">
<Link href="/login" className="font-semibold text-primary-700 hover:underline">
Entrar
</Link>
</p>