La primera corrida completa dio 57 fallas. Solo una parte eran del producto; el resto era instrumentación mal escrita. Queda en 261 en verde. Defectos del producto: - La atribución de campaña se perdía. La captura vivía en un efecto de React, así que si la persona llegaba con parámetros de campaña y navegaba antes de hidratar, el primer toque nunca se guardaba y utmCampaign llegaba vacío al CRM. Ahora se captura en un script en línea que corre al analizar el documento. - Siete páginas sin og:image: una página que declara su propio openGraph no hereda las imágenes del layout, las sobreescribe. Solo se nota al compartir el enlace. Se resuelve con una función compartida en seo.ts. - El panel no se podía usar desde Safari por HTTP: la cookie Secure no se guarda y el acceso falla en silencio. Queda explícito y documentado. - El panel se corría de lado en móvil: la cabecera no cabía a 375px. - Las tarjetas de perfil del acceso se cortaban en móvil. Un elemento de rejilla nace con min-width auto y no puede encogerse bajo su contenido. Era además el origen de un desplazamiento que se arrastraba al tablero. - Las tablas accesibles de las gráficas volvían desplazable la página: sr-only fija width 1px y el algoritmo de tablas lo ignora. - El campo de búsqueda del catálogo no tenía nombre accesible. - Límite de tasa de 5 envíos por IP: bloqueaba una demostración en vivo con varias personas en la misma red. Configurable, ahora 20. - El salto de contenido pasa de left:-9999px a recorte de 1px. Instrumentación corregida, con la razón escrita en cada caso: - El desborde horizontal se mide intentando desplazar la página, no con documentElement.scrollWidth, que sobreinforma con contenedores propios. - La comprobación de accesibilidad respeta el árbol de accesibilidad. - El aislamiento entre roles se prueba pidiendo por URL una oportunidad ajena, no comparando folios entre listados paginados. - Fuera las esperas por networkidle, que agotaban el tiempo sin fallar. - Que el panel no esté enlazado se comprueba leyendo el HTML servido. - Safari no enfoca enlaces con Tab: declarado como del navegador. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
403 lines
18 KiB
TypeScript
403 lines
18 KiB
TypeScript
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 { CLIENTES, EMPRESA, PRENSA, SITIO_URL } from "@/data/empresa";
|
|
import { INDUSTRIAS } from "@/data/industrias";
|
|
import { imagenesOg } from "@/lib/seo";
|
|
|
|
/**
|
|
* /nosotros — origen de la tecnología, posicionamiento y postura editorial.
|
|
*
|
|
* 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: 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,
|
|
description: DESCRIPCION,
|
|
alternates: { canonical: `${SITIO_URL}/nosotros` },
|
|
openGraph: {
|
|
title: `${TITULO} | ${EMPRESA.nombreCorto}`,
|
|
description: DESCRIPCION,
|
|
url: `${SITIO_URL}/nosotros`,
|
|
...imagenesOg(),
|
|
},
|
|
};
|
|
|
|
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 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.",
|
|
},
|
|
];
|
|
|
|
export default function PaginaNosotros() {
|
|
return (
|
|
<>
|
|
<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>
|
|
</HeroeLanding>
|
|
|
|
{/* ---------- origen de la tecnología ---------- */}
|
|
<section className="grid-bg-lite bg-paper py-14 sm:py-20" aria-labelledby="origen-titulo">
|
|
<div className="shell">
|
|
<div className="grid gap-10 lg:grid-cols-[1fr_1.15fr] lg:gap-16">
|
|
<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"
|
|
>
|
|
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{" "}
|
|
{EMPRESA.razonSocial} y usa esa herencia técnica como respaldo de desarrollo.
|
|
</p>
|
|
<p>
|
|
El activo de diferenciación es el agente FireAde 2000 y la familia de equipos
|
|
Enforcer, que permiten competir en un terreno distinto al del extintor
|
|
convencional: no por precio unitario, sino por tiempo de control, costo de
|
|
limpieza y continuidad de la operación.
|
|
</p>
|
|
<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.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div className="panel-lite p-6 sm:p-8">
|
|
<h3 className="font-display text-lg font-extrabold text-deep sm:text-xl">
|
|
Dónde decidimos jugar
|
|
</h3>
|
|
<p className="mt-4 text-sm leading-relaxed text-deep-muted">
|
|
El estudio de marca fue explícito: {EMPRESA.nombreCorto} no debería competir por
|
|
catálogo más grande ni por precio unitario. El espacio defendible está entre
|
|
tecnología especializada, respuesta rápida, evidencia técnica y costo total de
|
|
operación.
|
|
</p>
|
|
|
|
<hr className="hair-lite my-6" />
|
|
|
|
<h4 className="font-mono text-[0.6875rem] uppercase tracking-[0.16em] text-deep-muted">
|
|
Los cinco segmentos donde eso aplica
|
|
</h4>
|
|
<ul className="mt-4 space-y-3">
|
|
{INDUSTRIAS.map((industria) => (
|
|
<li key={industria.slug} className="flex gap-3">
|
|
<span className="mt-0.5 font-mono text-[0.625rem] font-semibold tracking-[0.08em] text-signal">
|
|
{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"
|
|
>
|
|
{industria.nombre}
|
|
</Link>
|
|
{" — "}
|
|
{industria.dolor}
|
|
</p>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
|
|
{/* ---------- postura editorial ---------- */}
|
|
<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"
|
|
>
|
|
Por qué documentamos en lugar de prometer
|
|
</h2>
|
|
<p className="mt-5 text-base leading-relaxed text-muted">
|
|
La competencia promete. En protección contra incendios, una promesa sin documento no
|
|
es solo una debilidad comercial: es una exposición legal para quien la compra y para
|
|
quien la firma. La auditoría de marca lo señaló como el mayor riesgo del sitio
|
|
anterior, y estas cinco reglas son la respuesta.
|
|
</p>
|
|
</div>
|
|
|
|
<ol className="mt-12 grid gap-px bg-rule sm:grid-cols-2 lg:grid-cols-3">
|
|
{POSTURA.map((regla) => (
|
|
<li key={regla.n} className="bg-ink-2 p-6 sm:p-7">
|
|
<p className="font-mono text-[0.625rem] font-semibold tracking-[0.1em] text-ember">
|
|
{regla.n}
|
|
</p>
|
|
<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>
|
|
))}
|
|
</ol>
|
|
</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="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 sm:text-3xl"
|
|
>
|
|
«Christmas Hero»
|
|
</h2>
|
|
</Revelar>
|
|
<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ó 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
|
|
razones concretas: demuestra que la empresa sabe traducir un problema técnico en
|
|
algo que una persona entiende de inmediato, y demuestra que puede sostener
|
|
atención de prensa cuando tiene algo que decir.
|
|
</p>
|
|
<p>
|
|
Ese es exactamente el trabajo pendiente en el resto de la comunicación: la
|
|
ingeniería está; la traducción es lo que faltaba.
|
|
</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div>
|
|
<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">
|
|
{PRENSA.map((medio) => (
|
|
<li key={medio.medio}>
|
|
<a
|
|
href={medio.url}
|
|
target="_blank"
|
|
rel="noopener noreferrer"
|
|
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}
|
|
alt={`Nota en ${medio.medio}`}
|
|
width={110}
|
|
height={36}
|
|
sizes="110px"
|
|
className="max-h-9 w-auto object-contain opacity-75 transition-opacity duration-200 hover:opacity-100"
|
|
/>
|
|
</a>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
<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>
|
|
|
|
{/* ---------- 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="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>
|
|
</>
|
|
);
|
|
}
|