feat: catálogo como centro del embudo y captura de baja fricción

Reestructuración pedida por el cliente tras revisar la primera versión.

Conversión:
- Formulario de 14 campos a 4: nombre, correo, teléfono y mensaje. La
  clasificación de riesgo ya no se pregunta, se deduce del contexto de la
  página y viaja oculta. Menos fricción, misma riqueza para el análisis.
- El inicio deja de bifurcar y empuja al catálogo: 36 llamados desembocan
  ahí, y las rutas por industria y escenario pasan a ser la forma de entrar
  al catálogo ya filtrado, en lugar de competir como destino.
- Catálogo rehecho como tienda: barra lateral con filtros, búsqueda que
  acepta expresiones regulares, títulos que abren la ficha, un solo botón
  primario por tarjeta y el comparador detrás de un desplegable.
- Ficha de producto orientada al cierre: bloque en lenguaje llano sobre si
  el equipo corresponde al caso, y "Cotizar este equipo" con el formulario
  precargado. Se retiraron los enlaces que sacaban del embudo a leer normas.
- Todos los llamados se unifican en "Recibir cotización".
- Widget de WhatsApp: nombre, teléfono, correo y mensaje libre. El texto que
  escribe la persona es el que viaja a la conversación.

Doble audiencia:
- 13 escenarios con la iconografía original de FSPM traducen un lugar
  reconocible a clases de fuego y productos, para quien no domina la
  terminología. La capa técnica se conserva para búsqueda.
- Héroe con fotografía en todas las landings.

Correcciones:
- El contenedor moría al arrancar porque el CLI de Prisma no podía escribir
  sus motores; el chown ahora incluye /app/prisma-cli.
- Iconos repetidos entre escenarios. El set original no tiene icono de
  cocina, así que ese escenario usa un marcador tipográfico explícito.
- FIREMIKS no aparecía en ningún escenario.
- Los ids de campo del formulario colisionaban con dos instancias por página.
- Se documentó el despliegue, que faltaba por completo.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
Uriel Jareth
2026-08-28 01:30:44 -06:00
co-authored by Claude Opus 5
parent 3c6bdcfc06
commit 0e0331c1fc
580 changed files with 7239 additions and 2683 deletions
+179 -99
View File
@@ -1,21 +1,27 @@
import type { Metadata } from "next";
import Image from "next/image";
import Link from "next/link";
import { FormularioContacto } from "@/components/formulario/FormularioContacto";
import { Migas } from "@/components/site/contenido/Migas";
import { FOTOS, HeroeLanding } from "@/components/site/HeroeLanding";
import { Revelar } from "@/components/site/Revelar";
import { CATEGORIAS, PRODUCTOS } from "@/data/catalogo";
import { EMPRESA, PRENSA, SITIO_URL } from "@/data/empresa";
import { CLIENTES, EMPRESA, PRENSA, SITIO_URL } from "@/data/empresa";
import { INDUSTRIAS } from "@/data/industrias";
/**
* /nosotros — origen de la tecnología, posicionamiento y postura editorial.
*
* La parte más importante de esta página es la última sección: por qué la
* La parte más importante de esta página es la sección de postura: por qué la
* empresa documenta en lugar de prometer. Es la traducción del hallazgo central
* del estudio de marca y la razón de ser del resto del sitio.
*
* Los logotipos de clientes se muestran sin atribuirles testimonio ni
* resultado: el estudio pide confirmar la relación antes de usarlos como caso.
*/
const TITULO = "Quiénes somos";
const DESCRIPCION = `${EMPRESA.nombre}: origen de la tecnología FireAde, posicionamiento en operaciones críticas y por qué documentamos en lugar de prometer.`;
const TITULO = "Quiénes somos: protección contra incendios documentada";
const DESCRIPCION = `${EMPRESA.nombre}: de dónde viene la tecnología FireAde, en qué operaciones trabajamos y por qué documentamos en lugar de prometer. La postura editorial que gobierna todo el sitio, explicada en cinco reglas.`;
export const metadata: Metadata = {
title: TITULO,
@@ -28,34 +34,41 @@ export const metadata: Metadata = {
},
};
const MENSAJE_INICIAL = "Quiero hablar con alguien sobre ";
const POSTURA = [
{
n: "01",
titulo: "Una afirmación sin documento es publicidad",
llano: "Si te decimos algo del equipo, te damos el papel que lo dice.",
texto:
"El sitio anterior presentaba vida útil ilimitada, veinte años de garantía y ausencia de recarga como características del producto. Eran, en el mejor de los casos, afirmaciones con condiciones que nadie escribió. Ahora viven en un bloque aparte de cada ficha, junto con lo que hay que entregar para poder sostenerlas.",
},
{
n: "02",
titulo: "Los campos vacíos se quedan vacíos",
llano: "Si no sabemos un dato, la ficha dice que no lo sabemos.",
texto:
"Cuando no hay dato de alcance, de capacidad o de caudal, la ficha lo dice. No ponemos un guion ni una estimación redondeada: un número inventado en una ficha técnica reaparece en una auditoría con el nombre de quien lo firmó.",
},
{
n: "03",
titulo: "No hay botones de descarga que no descarguen nada",
llano: "Ningún botón de este sitio te va a llevar a un archivo que no existe.",
texto:
"El centro de evidencia explica qué documentación corresponde a cada compra y cómo se solicita por número de modelo. Lo que no hace es simular un acervo. Un botón falso destruye la credibilidad de todo lo demás.",
},
{
n: "04",
titulo: "Los casos van rotulados",
llano: "Los ejemplos de aplicación dicen arriba que son ejemplos.",
texto:
"Los escenarios de aplicación están construidos con fines de demostración y lo dicen arriba, no en una nota al pie. Un caso con nombre de cliente requiere autorización por escrito sobre qué se puede contar y qué cifras se pueden publicar.",
"Los escenarios están construidos con fines de demostración y lo declaran en el encabezado, no en una nota al pie. Un caso con nombre de cliente requiere autorización por escrito sobre qué se puede contar y qué cifras se pueden publicar.",
},
{
n: "05",
titulo: "Preferimos la respuesta incómoda",
llano: "Si algo no nos conviene decirte pero te conviene saberlo, te lo decimos.",
texto:
"Si un certificado no cubre tu caso de uso, si un plazo trae condiciones o si simplemente no tenemos un documento, va en la propuesta. Sale más barato ahí que en la verificación.",
},
@@ -64,64 +77,27 @@ const POSTURA = [
export default function PaginaNosotros() {
return (
<>
{/* ---------- encabezado ---------- */}
<section className="grid-bg relative overflow-hidden bg-ink pt-12 pb-14 sm:pt-16 sm:pb-20">
<div className="shell">
<nav aria-label="Ruta de navegación" className="mb-8">
<ol className="flex flex-wrap items-center gap-2 font-mono text-[0.625rem] uppercase tracking-[0.14em] text-faint">
<li>
<Link href="/" className="hover:text-bright">
Inicio
</Link>
</li>
<li aria-hidden="true">/</li>
<li className="text-bright">Nosotros</li>
</ol>
</nav>
<div className="grid gap-12 lg:grid-cols-[1.15fr_1fr] lg:items-center">
<div className="anim-rise">
<p className="eyebrow mb-5">Quiénes somos</p>
<h1 className="font-display text-[2rem] font-black uppercase leading-[1.03] tracking-tight sm:text-[2.75rem] lg:text-5xl">
{EMPRESA.promesa}
</h1>
<p className="mt-6 max-w-xl text-base leading-relaxed text-muted sm:text-lg">
{EMPRESA.descripcion}
</p>
<dl className="spec mt-9 sm:grid-cols-3">
<div className="spec-cell">
<dt className="spec-key">Origen de la tecnología</dt>
<dd className="spec-val">{EMPRESA.fundacionOrigen}</dd>
</div>
<div className="spec-cell">
<dt className="spec-key">Categorías</dt>
<dd className="spec-val">{CATEGORIAS.length}</dd>
</div>
<div className="spec-cell">
<dt className="spec-key">Equipos y agentes</dt>
<dd className="spec-val">{PRODUCTOS.length}</dd>
</div>
</dl>
</div>
<div className="relative aspect-[4/3] w-full overflow-hidden rounded-[6px] border border-rule">
<Image
src="/media/foto/bombero-plataforma.jpg"
alt="Personal de respuesta a incendios trabajando en una plataforma industrial"
fill
priority
sizes="(max-width: 1024px) 92vw, 34rem"
className="object-cover"
/>
<div
aria-hidden="true"
className="absolute inset-0 bg-gradient-to-t from-ink/70 via-transparent to-transparent"
/>
</div>
</div>
<HeroeLanding
antes={<Migas actual="Nosotros" />}
eyebrow="Quiénes somos"
titulo={EMPRESA.promesa}
entrada={EMPRESA.descripcion}
foto={FOTOS.bombero}
enfoque="58% 32%"
alt="Personal de respuesta a incendios trabajando en una plataforma industrial"
altura="alto"
prioridad
>
<div className="flex flex-col gap-4 sm:flex-row sm:items-center">
<Link href="#hablar" className="btn btn-signal w-full sm:w-auto">
Hablar con un especialista
</Link>
<p className="font-mono text-[0.625rem] uppercase leading-relaxed tracking-[0.1em] text-faint">
Tecnología de origen {EMPRESA.fundacionOrigen} · {CATEGORIAS.length} categorías ·{" "}
{PRODUCTOS.length} equipos y agentes
</p>
</div>
</section>
</HeroeLanding>
{/* ---------- origen de la tecnología ---------- */}
<section className="grid-bg-lite bg-paper py-14 sm:py-20" aria-labelledby="origen-titulo">
@@ -130,11 +106,18 @@ export default function PaginaNosotros() {
<div>
<Revelar>
<p className="eyebrow mb-4">Origen</p>
<h2 id="origen-titulo" className="font-display text-2xl font-extrabold text-deep sm:text-3xl">
<h2
id="origen-titulo"
className="font-display text-2xl font-extrabold text-deep sm:text-3xl"
>
La tecnología viene de {EMPRESA.empresaOrigen}
</h2>
</Revelar>
<div className="mt-5 space-y-4 text-sm leading-relaxed text-deep-muted sm:text-base">
<p className="text-base text-deep">
No fabricamos un extintor más barato. Traemos una tecnología distinta y
respondemos por su documentación.
</p>
<p>
{EMPRESA.empresaOrigen} se presenta como fundada en {EMPRESA.fundacionOrigen} por{" "}
{EMPRESA.fundadorOrigen}. {EMPRESA.nombreCorto} opera en México como{" "}
@@ -149,7 +132,8 @@ export default function PaginaNosotros() {
<p>
Ese es también el límite honesto de lo que podemos afirmar aquí. Los datos de
origen provienen de la comunicación pública de la empresa; el desempeño de cada
formulación y de cada modelo se demuestra con su documentación, no con su historia.
formulación y de cada modelo se demuestra con su documentación, no con su
historia.
</p>
</div>
</div>
@@ -177,7 +161,10 @@ export default function PaginaNosotros() {
{String(industria.prioridad).padStart(2, "0")}
</span>
<p className="text-sm leading-relaxed text-deep-muted">
<Link href={`/soluciones/${industria.slug}`} className="link font-semibold text-deep">
<Link
href={`/soluciones/${industria.slug}`}
className="link font-semibold text-deep"
>
{industria.nombre}
</Link>
{" — "}
@@ -192,13 +179,19 @@ export default function PaginaNosotros() {
</section>
{/* ---------- postura editorial ---------- */}
<section className="border-t border-rule bg-ink py-14 sm:py-20" aria-labelledby="postura-titulo">
<section
className="border-t border-rule bg-ink py-14 sm:py-20"
aria-labelledby="postura-titulo"
>
<div className="shell">
<div className="max-w-3xl">
<p className="font-mono text-[0.6875rem] font-medium uppercase tracking-[0.16em] text-ember">
Postura editorial
</p>
<h2 id="postura-titulo" className="mt-4 font-display text-2xl font-extrabold sm:text-3xl">
<h2
id="postura-titulo"
className="mt-4 font-display text-2xl font-extrabold sm:text-3xl"
>
Por qué documentamos en lugar de prometer
</h2>
<p className="mt-5 text-base leading-relaxed text-muted">
@@ -218,6 +211,7 @@ export default function PaginaNosotros() {
<h3 className="mt-3 font-display text-base font-extrabold leading-tight text-bright">
{regla.titulo}
</h3>
<p className="mt-3 text-base leading-relaxed text-bright">{regla.llano}</p>
<p className="mt-3 text-sm leading-relaxed text-muted">{regla.texto}</p>
</li>
))}
@@ -225,23 +219,73 @@ export default function PaginaNosotros() {
</div>
</section>
{/* ---------- logotipos de clientes, sin atribuirles resultado ---------- */}
<section
className="grid-bg-lite border-t border-rule bg-paper py-14 sm:py-16"
aria-labelledby="clientes-titulo"
>
<div className="shell">
<Revelar>
<p className="eyebrow mb-4">Logotipos publicados por la empresa</p>
<h2
id="clientes-titulo"
className="max-w-2xl font-display text-2xl font-extrabold text-deep sm:text-3xl"
>
Marcas que aparecen en la comunicación de FSPM
</h2>
<p className="mt-4 max-w-2xl text-sm leading-relaxed text-deep-muted sm:text-base">
Se muestran como logotipos y nada más. No les atribuimos testimonio, resultado ni
periodo: mientras la relación no esté confirmada por escrito con cada empresa,
presentarlas como caso de éxito sería exactamente lo que este sitio no hace.
</p>
</Revelar>
<ul className="mt-9 grid grid-cols-2 gap-3 sm:grid-cols-4 lg:grid-cols-6">
{CLIENTES.map((cliente) => (
<li
key={cliente.nombre}
className="flex h-20 items-center justify-center rounded-[3px] border border-rule-lite bg-white px-4"
>
<Image
src={cliente.logo}
alt={cliente.nombre}
width={104}
height={36}
sizes="104px"
className="max-h-9 w-auto object-contain opacity-70"
/>
</li>
))}
</ul>
</div>
</section>
{/* ---------- Christmas Hero, sin exagerarlo ---------- */}
<section className="grid-bg-lite border-t border-rule bg-paper py-14 sm:py-20" aria-labelledby="prensa-titulo">
<section
className="border-t border-rule bg-ink py-14 sm:py-20"
aria-labelledby="prensa-titulo"
>
<div className="shell">
<div className="grid gap-10 lg:grid-cols-[1fr_1fr] lg:gap-16">
<div>
<Revelar>
<p className="eyebrow mb-4">Antecedente de comunicación</p>
<h2 id="prensa-titulo" className="font-display text-2xl font-extrabold text-deep sm:text-3xl">
<h2
id="prensa-titulo"
className="font-display text-2xl font-extrabold sm:text-3xl"
>
&laquo;Christmas Hero&raquo;
</h2>
</Revelar>
<div className="mt-5 space-y-4 text-sm leading-relaxed text-deep-muted sm:text-base">
<div className="mt-5 space-y-4 text-sm leading-relaxed text-muted sm:text-base">
<p className="text-base text-bright">
Un adorno navideño que apagaba el fuego de un árbol de Navidad. Salió en prensa y
demuestra algo que importa aquí: la empresa sabe explicar lo técnico.
</p>
<p>
En 2019, {EMPRESA.nombreCorto} desarrolló una pieza de comunicación llamada
Christmas Hero: un adorno navideño que funcionaba como dispositivo de extinción
para árboles de Navidad. Fue publicada por LatinSpots en 2019 y obtuvo una mención
como segundo finalista de El Ojo Local México.
En 2019, {EMPRESA.nombreCorto} desarrolló esa pieza de comunicación. Fue publicada
por LatinSpots en 2019 y obtuvo una mención como segundo finalista de El Ojo Local
México.
</p>
<p>
Lo decimos con esa medida, sin convertirlo en más de lo que fue. Importa por dos
@@ -257,7 +301,7 @@ export default function PaginaNosotros() {
</div>
<div>
<h3 className="font-mono text-[0.6875rem] uppercase tracking-[0.16em] text-deep-muted">
<h3 className="font-mono text-[0.6875rem] uppercase tracking-[0.16em] text-faint">
Publicaciones que han cubierto a FSPM
</h3>
<ul className="mt-6 grid grid-cols-2 gap-3 sm:grid-cols-3">
@@ -267,7 +311,7 @@ export default function PaginaNosotros() {
href={medio.url}
target="_blank"
rel="noopener noreferrer"
className="flex h-20 items-center justify-center rounded-[3px] border border-rule-lite bg-white px-4 transition-colors duration-200 hover:border-signal/60 [-webkit-tap-highlight-color:transparent]"
className="flex h-20 items-center justify-center rounded-[3px] border border-rule bg-ink-2 px-4 transition-colors duration-200 hover:border-signal/60 [-webkit-tap-highlight-color:transparent]"
>
<Image
src={medio.logo}
@@ -281,37 +325,73 @@ export default function PaginaNosotros() {
</li>
))}
</ul>
<p className="mt-5 text-xs leading-relaxed text-deep-muted">
Los enlaces llevan a la nota original de cada medio. Varias corresponden a la cobertura
de Christmas Hero; aparecen como cobertura periodística, no como respaldo del
desempeño de un producto.
<p className="mt-5 text-xs leading-relaxed text-faint">
Los enlaces llevan a la nota original de cada medio. Varias corresponden a la
cobertura de Christmas Hero; aparecen como cobertura periodística, no como respaldo
del desempeño de un producto.
</p>
</div>
</div>
</div>
</section>
{/* ---------- salidas ---------- */}
<section className="border-t border-rule bg-ink-2 py-14 sm:py-16">
{/* ---------- conversión ---------- */}
<section
id="hablar"
className="grid-bg-lite border-t border-rule bg-paper py-14 sm:py-20"
aria-labelledby="hablar-titulo"
>
<div className="shell">
<div className="max-w-3xl">
<Revelar>
<p className="eyebrow mb-4">Siguiente paso</p>
<h2 className="font-display text-2xl font-extrabold sm:text-3xl">
Lo que sigue depende de qué tan urgente sea tu caso
</h2>
</Revelar>
<div className="mt-8 flex flex-wrap gap-3">
<Link href="/contacto" className="btn btn-signal">
Hablar con nosotros
</Link>
<Link href="/catalogo" className="btn btn-ghost">
Ver el catálogo
</Link>
<Link href="/evidencia" className="btn btn-ghost">
Centro de evidencia
</Link>
<div className="grid gap-10 lg:grid-cols-[1fr_1.05fr] lg:items-start lg:gap-16">
<div>
<Revelar>
<p className="eyebrow mb-4">Siguiente paso</p>
<h2
id="hablar-titulo"
className="font-display text-2xl font-extrabold text-deep sm:text-3xl"
>
Cuéntanos qué hay que proteger
</h2>
<p className="mt-4 text-sm leading-relaxed text-deep-muted sm:text-base">
No necesitas llegar con un requerimiento técnico redactado. Con el tipo de
instalación y lo que te preocupa es suficiente para empezar: la primera respuesta
ya trae la selección propuesta y qué documentación le corresponde.
</p>
</Revelar>
<hr className="hair-lite my-8" />
<h3 className="font-mono text-[0.6875rem] uppercase tracking-[0.16em] text-deep-muted">
O empieza por donde te sirva más
</h3>
<ul className="mt-4 flex flex-wrap gap-2">
{[
{ href: "/catalogo", label: "Catálogo" },
{ href: "/cumplimiento-nom-002", label: "Cumplimiento NOM-002" },
{ href: "/evidencia", label: "Documentación técnica" },
{ href: "/preguntas-frecuentes", label: "Preguntas frecuentes" },
].map((enlace) => (
<li key={enlace.href}>
<Link
href={enlace.href}
className="badge !border-rule-lite !text-deep-muted hover:!border-signal hover:!text-deep [-webkit-tap-highlight-color:transparent]"
>
{enlace.label}
</Link>
</li>
))}
</ul>
</div>
<FormularioContacto
id="formulario-nosotros"
canal="catalogo"
variante="claro"
titulo="Hablar con un especialista"
descripcion="Completa la frase del mensaje con lo que necesitas resolver. Te contesta una persona con nombre, no un buzón."
mensajeInicial={MENSAJE_INICIAL}
textoBoton="Solicitar contacto"
/>
</div>
</div>
</section>