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
+324 -182
View File
@@ -1,21 +1,30 @@
import type { Metadata } from "next";
import Link from "next/link";
import { FormularioContacto } from "@/components/formulario/FormularioContacto";
import { TarjetaEscenario } from "@/components/marca/IconoEscenario";
import { Migas } from "@/components/site/contenido/Migas";
import { FOTOS, HeroeLanding } from "@/components/site/HeroeLanding";
import { Revelar } from "@/components/site/Revelar";
import { EMPRESA, NORMAS, SITIO_URL } from "@/data/empresa";
import { ESCENARIOS, type Escenario } from "@/data/escenarios";
import { INDUSTRIAS } from "@/data/industrias";
/**
* /cumplimiento-nom-002 — página de intención de búsqueda alta.
* /cumplimiento-nom-002 — la página de mayor intención de compra del sitio.
*
* Regla del contenido: no se citan artículos, umbrales ni plazos que no estén
* en src/data/empresa.ts o en la fuente pública. Lo que no está verificado se
* declara como no reproducido, no se estima.
* Orden deliberado: primero las tres preguntas que hace el dueño de un negocio
* —me aplica, qué me pueden multar, qué tengo que tener— y solo después la
* precisión normativa. Cada bloque está escrito en dos niveles: la primera
* frase la entiende cualquiera, la segunda le da al experto el dato.
*
* Regla del contenido: no se citan artículos, umbrales, plazos ni montos de
* sanción que no estén en src/data/empresa.ts o en la fuente pública. Lo que no
* está verificado se declara como no reproducido; no se estima.
*/
const TITULO = "Cumplimiento NOM-002-STPS-2010";
const TITULO = "Cumplimiento NOM-002-STPS-2010: qué te van a pedir";
const DESCRIPCION =
"Qué exige la NOM-002-STPS-2010 a un centro de trabajo, qué documentación se acumula y cómo se ve un expediente completo. Referencia orientativa, no dictamen técnico ni jurídico.";
"¿La NOM-002-STPS-2010 te aplica y qué te van a pedir en una verificación? Explicado sin tecnicismos y con la precisión normativa después: clasificación de riesgo, equipo, plan de emergencia, brigadas, simulacros y los ocho documentos del expediente.";
export const metadata: Metadata = {
title: TITULO,
@@ -31,43 +40,83 @@ export const metadata: Metadata = {
const AVISO_ALCANCE =
"Esta página es una referencia orientativa construida con información pública. La normativa puede actualizarse y complementarse por estado, municipio, tipo de instalación y autoridad competente. No sustituye un dictamen técnico ni una opinión jurídica.";
const MENSAJE_INICIAL = "Necesito saber si cumplo con la NOM-002. Mi instalación es un ";
/**
* Las tres preguntas reales, en el orden en el que llegan. Cada respuesta abre
* en lenguaje llano y cierra con el dato normativo.
*/
const PREGUNTAS_REALES = [
{
id: "me-aplica",
pregunta: "¿Esto me aplica a mí?",
llano:
"Si tienes gente trabajando, sí. No hay tamaño mínimo: aplica igual a una oficina de quince personas que a una planta de trescientas.",
tecnico:
"La NOM-002-STPS-2010 rige para todo centro de trabajo en México. Lo que cambia entre una oficina y una planta no es la norma: es el nivel de riesgo de incendio con el que se clasifica el centro, y de ese nivel dependen el equipo, los sistemas y el plan que te corresponden.",
},
{
id: "que-me-multan",
pregunta: "¿Qué me pueden multar?",
llano:
"No te vamos a dar una cifra. Quien te promete un monto exacto en una página web se lo está inventando, y con eso no se toma una decisión.",
tecnico:
"El monto de una sanción lo determina la autoridad competente según lo que encuentre en la verificación, y no lo publicamos porque no podemos sostenerlo con una fuente verificable. Lo que sí es concreto —y es lo que conviene preparar— son los ocho documentos del expediente que aparece más abajo.",
},
{
id: "que-necesito",
pregunta: "¿Qué tengo que tener?",
llano:
"Equipo que sirva para lo que se puede quemar en cada área, un plan escrito, personal que sepa qué hacer y registro de todo lo anterior. No se cumple solo comprando extintores.",
tecnico:
"En el orden que sigue la norma: clasificación del riesgo de incendio, equipo contra incendio, sistemas fijos y medios de detección y alarma cuando el nivel lo exige, plan de atención a emergencias por escrito, brigadas con capacitación acreditada y simulacros ejecutados y documentados.",
},
];
/** Elementos que la norma contempla, según la referencia de NORMAS. */
const ELEMENTOS = [
{
n: "01",
titulo: "Clasificación del riesgo de incendio",
llano:
"Es el primer paso y define todo lo demás. Si se hace mal, todo lo que se compre encima queda mal.",
detalle:
"Es el primer paso y condiciona todo lo demás. De la clasificación depende qué equipo, qué sistemas y qué plan te corresponden. Se determina con el procedimiento de la propia norma; los umbrales no se reproducen aquí porque dependen del material y de la superficie de cada centro de trabajo.",
"De la clasificación depende qué equipo, qué sistemas y qué plan te corresponden. Se determina con el procedimiento de la propia norma; los umbrales no se reproducen aquí porque dependen del material y de la superficie de cada centro de trabajo.",
},
{
n: "02",
titulo: "Equipo contra incendio",
llano: "Extintores que se vean, se alcancen y sirvan para lo que hay en esa área concreta.",
detalle:
"Extintores disponibles, accesibles y señalizados, seleccionados según la clase de fuego presente en cada área. El equipo correcto en el lugar equivocado no cumple.",
"Disponibles, accesibles y señalizados, seleccionados según la clase de fuego presente en cada zona. El equipo correcto en el lugar equivocado no cumple, y el conteo total no sustituye a la distancia de recorrido.",
},
{
n: "03",
titulo: "Sistemas fijos y de alarma",
llano: "Si tu riesgo es alto, con equipo portátil no alcanza.",
detalle:
"Según el nivel de riesgo, el centro de trabajo puede requerir sistemas fijos de supresión y medios de detección y alarma, no solo equipo portátil.",
"Según el nivel de riesgo, el centro de trabajo puede requerir sistemas fijos de supresión y medios de detección y alarma, además del equipo portátil. Es el punto donde la conversación deja de ser una compra y pasa a ser un proyecto.",
},
{
n: "04",
titulo: "Plan de atención a emergencias",
llano: "Un documento que diga quién hace qué el día del incidente. Por escrito, no de palabra.",
detalle:
"Documento por escrito. Es lo primero que se pide en una verificación y lo que más frecuentemente no existe o está desactualizado.",
"Es lo primero que se pide en una verificación y lo que con más frecuencia no existe o quedó desactualizado tras un cambio de instalación, de proceso o de personal.",
},
{
n: "05",
titulo: "Brigadas y capacitación",
llano: "Personas con nombre y apellido que sepan operar el equipo, y un papel que lo compruebe.",
detalle:
"Personal designado, entrenado y con constancia. La capacitación sin registro documental no es demostrable ante una autoridad.",
"Personal designado, entrenado y con constancia por persona y fecha. La capacitación sin registro documental no es demostrable ante una autoridad, aunque haya ocurrido.",
},
{
n: "06",
titulo: "Simulacros",
llano: "Practicarlo antes, y dejar constancia de que se practicó.",
detalle:
"Ejecutados y documentados. El registro del simulacro es evidencia tanto del plan como de la capacitación.",
"Ejecutados y documentados. El registro del simulacro es evidencia doble, del plan y de la capacitación, y es donde se detectan los huecos que un plano no muestra.",
},
];
@@ -114,171 +163,215 @@ const EXPEDIENTE = [
},
];
/** Los ocho escenarios más frecuentes en una conversación de cumplimiento. */
const SLUGS_CUMPLIMIENTO = [
"industria",
"gasolineras",
"bodegas",
"cocinas-hoteles",
"oficinas",
"auditorios-afluencia",
"flotas-maquinaria",
"minas",
];
const ESCENARIOS_CUMPLIMIENTO = ESCENARIOS.filter((e) => SLUGS_CUMPLIMIENTO.includes(e.slug));
function hrefEscenario(escenario: Escenario): string {
if (escenario.industria) return `/soluciones/${escenario.industria}`;
const categoria = escenario.categorias[0];
return categoria ? `/catalogo/${categoria}` : "/catalogo";
}
export default function PaginaCumplimiento() {
return (
<>
{/* ---------- encabezado con la advertencia de alcance ---------- */}
<section className="grid-bg 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">Cumplimiento</li>
</ol>
</nav>
<HeroeLanding
antes={<Migas actual="Cumplimiento NOM-002" />}
eyebrow="Cumplimiento NOM-002-STPS-2010"
titulo="¿La NOM-002 te aplica? Sí. Esto es lo que te van a pedir"
entrada="Primero en español llano y después con la precisión normativa. Si solo quieres saber por dónde empezar, empieza por la revisión: te decimos qué te falta antes de venderte nada."
foto={FOTOS.normas}
enfoque="62% 42%"
altura="alto"
prioridad
>
<div className="flex flex-col gap-4 sm:flex-row sm:items-center">
<Link href="#validar" className="btn btn-signal w-full sm:w-auto">
Revisar mi cumplimiento
</Link>
<p className="font-mono text-[0.625rem] uppercase leading-relaxed tracking-[0.1em] text-faint">
Cuatro campos · Sin costo · Sin compromiso de compra
</p>
</div>
</HeroeLanding>
<div className="max-w-3xl anim-rise">
<p className="eyebrow mb-5">Referencia normativa</p>
<h1 className="font-display text-[2rem] font-black uppercase leading-[1.03] tracking-tight sm:text-[2.75rem] lg:text-5xl">
Cumplimiento NOM-002: qué te van a pedir y cómo se ve un expediente completo
</h1>
<p className="mt-6 text-base leading-relaxed text-muted sm:text-lg">
La norma no se cumple comprando extintores. Se cumple demostrando, por escrito, que el
riesgo está clasificado, el equipo corresponde a ese riesgo y el personal sabe qué
hacer. Esta página explica esa lógica y qué documentos la sostienen.
{/* ---------- las tres preguntas reales ---------- */}
<section className="grid-bg-lite bg-paper py-14 sm:py-20" aria-labelledby="preguntas-titulo">
<div className="shell">
<Revelar>
<p className="eyebrow mb-4">Empecemos por lo básico</p>
<h2
id="preguntas-titulo"
className="max-w-2xl font-display text-2xl font-extrabold text-deep sm:text-3xl"
>
Las tres preguntas que hace todo el mundo, respondidas sin rodeos
</h2>
<p className="mt-4 max-w-2xl text-sm leading-relaxed text-deep-muted sm:text-base">
En cada respuesta, lo primero es lo que necesitas para decidir. Lo segundo es el
detalle normativo, para quien tiene que sostenerlo ante una auditoría.
</p>
</Revelar>
<div className="mt-10 grid gap-px bg-rule-lite lg:grid-cols-3">
{PREGUNTAS_REALES.map((item) => (
<article key={item.id} id={item.id} className="bg-white p-6 sm:p-7">
<h3 className="font-display text-lg font-extrabold leading-tight text-deep sm:text-xl">
{item.pregunta}
</h3>
<p className="mt-4 text-base leading-relaxed text-deep">{item.llano}</p>
<hr className="hair-lite my-5" />
<p className="font-mono text-[0.625rem] uppercase tracking-[0.14em] text-deep-muted">
El dato normativo
</p>
<p className="mt-2.5 text-sm leading-relaxed text-deep-muted">{item.tecnico}</p>
</article>
))}
</div>
<div
role="note"
aria-label="Alcance de esta página"
className="mt-10 max-w-3xl rounded-[6px] border border-rule border-l-2 border-l-ember bg-ink-2 p-5 sm:p-6"
className="mt-10 max-w-3xl rounded-[6px] border border-rule-lite border-l-2 border-l-ember bg-white p-5 sm:p-6"
>
<p className="font-mono text-[0.6875rem] uppercase tracking-[0.16em] text-ember">
<p className="font-mono text-[0.6875rem] uppercase tracking-[0.16em] text-deep-muted">
Alcance de esta página
</p>
<p className="mt-3 text-sm leading-relaxed text-muted">{AVISO_ALCANCE}</p>
<p className="mt-3 text-sm leading-relaxed text-deep-muted">{AVISO_ALCANCE}</p>
</div>
</div>
</section>
{/* ---------- a quién aplica ---------- */}
<section className="grid-bg-lite bg-paper py-14 sm:py-20" aria-labelledby="aplica-titulo">
{/* ---------- identifica tu caso con la iconografía de escenarios ---------- */}
<section
className="border-t border-rule bg-ink py-14 sm:py-20"
aria-labelledby="escenarios-titulo"
>
<div className="shell">
<div className="grid gap-10 lg:grid-cols-[1fr_1.1fr] lg:gap-16">
<div>
<Revelar>
<p className="eyebrow mb-4">A quién aplica</p>
<h2 id="aplica-titulo" className="font-display text-2xl font-extrabold text-deep sm:text-3xl">
A todo centro de trabajo, sin mínimo de tamaño
</h2>
<p className="mt-4 text-sm leading-relaxed text-deep-muted sm:text-base">
{NORMAS[0].aplica}
</p>
<p className="mt-4 text-sm leading-relaxed text-deep-muted">
Es la razón por la que una oficina de veinte personas y una planta petroquímica
están bajo la misma norma con obligaciones muy distintas: lo que cambia no es la
norma, es la clasificación del riesgo.
</p>
</Revelar>
</div>
<Revelar>
<p className="eyebrow mb-4">Encuentra tu caso</p>
<h2
id="escenarios-titulo"
className="max-w-2xl font-display text-2xl font-extrabold sm:text-3xl"
>
Busca el lugar que se parece al tuyo
</h2>
<p className="mt-4 max-w-2xl text-sm leading-relaxed text-muted sm:text-base">
No hace falta que sepas qué clase de fuego tienes. Si administras una gasolinera, un
hotel o una bodega, eso ya define el perfil de riesgo y, con él, las obligaciones que
se te aplican con más peso.
</p>
</Revelar>
<div className="panel-lite p-6 sm:p-8">
<h3 className="font-display text-lg font-extrabold text-deep sm:text-xl">
Dos niveles, dos conversaciones distintas
</h3>
<ul className="mt-10 grid gap-4 sm:grid-cols-2 lg:grid-cols-4">
{ESCENARIOS_CUMPLIMIENTO.map((escenario) => (
<li key={escenario.slug} className="grid">
<TarjetaEscenario
escenario={escenario}
href={hrefEscenario(escenario)}
conRiesgo={false}
/>
</li>
))}
</ul>
<div className="mt-6 space-y-6">
<div className="border-l-2 border-l-rule-lite pl-4">
<p className="font-mono text-[0.625rem] uppercase tracking-[0.14em] text-deep-muted">
Riesgo ordinario
</p>
<p className="mt-2 text-sm leading-relaxed text-deep-muted">
El foco está en tener el equipo correcto, señalizado y revisado, con un plan por
escrito y personal capacitado. La mayoría de las PyMEs y oficinas queda aquí y
el error caro es comprar de más por miedo.
</p>
</div>
<div className="border-l-2 border-l-signal pl-4">
<p className="font-mono text-[0.625rem] uppercase tracking-[0.14em] text-signal">
Riesgo alto
</p>
<p className="mt-2 text-sm leading-relaxed text-deep-muted">
La norma establece requerimientos mayores para los centros clasificados como de
riesgo alto de incendio: la conversación pasa de equipo portátil a sistemas
fijos, detección, brigadas formales y evidencia sostenida en el tiempo.
</p>
</div>
</div>
<hr className="hair-lite my-6" />
<p className="text-xs leading-relaxed text-deep-muted">
No reproducimos aquí los umbrales de clasificación: dependen del material y de la
superficie de cada centro de trabajo, y determinarlos mal invalida todo lo que se
construya encima. Ese es el primer punto de un diagnóstico.
</p>
</div>
</div>
<p className="mt-8 max-w-3xl text-sm leading-relaxed text-muted">
Si el tuyo no está en la lista, escríbelo tal cual en la revisión: lo clasificamos
nosotros.{" "}
<Link href="#validar" className="link">
Ir a la revisión
</Link>
.
</p>
</div>
</section>
{/* ---------- qué contempla la norma ---------- */}
<section className="border-t border-rule bg-ink py-14 sm:py-20" aria-labelledby="elementos-titulo">
<section
className="grid-bg-lite border-t border-rule bg-paper py-14 sm:py-20"
aria-labelledby="elementos-titulo"
>
<div className="shell">
<Revelar>
<p className="eyebrow mb-4">Qué contempla</p>
<h2 id="elementos-titulo" className="font-display text-2xl font-extrabold sm:text-3xl">
<p className="eyebrow mb-4">Qué contempla la norma</p>
<h2
id="elementos-titulo"
className="max-w-2xl font-display text-2xl font-extrabold text-deep sm:text-3xl"
>
Seis frentes, y ninguno se cierra con una compra
</h2>
<p className="mt-3 max-w-2xl text-sm leading-relaxed text-muted sm:text-base">
Cada uno genera un documento distinto. Un expediente incompleto suele fallar en los
frentes que no se compran: el plan, la capacitación y el registro.
<p className="mt-4 max-w-2xl text-sm leading-relaxed text-deep-muted sm:text-base">
Cada frente genera un documento distinto. Los expedientes incompletos casi siempre
fallan en los tres que no se compran: el plan, la capacitación y el registro.
</p>
</Revelar>
<ol className="mt-10 grid gap-px bg-rule sm:grid-cols-2 lg:grid-cols-3">
<ol className="mt-10 grid gap-px bg-rule-lite sm:grid-cols-2 lg:grid-cols-3">
{ELEMENTOS.map((elemento) => (
<li key={elemento.n} className="bg-ink-2 p-6">
<li key={elemento.n} className="bg-white p-6 sm:p-7">
<p className="font-mono text-[0.625rem] font-semibold tracking-[0.1em] text-signal">
{elemento.n}
</p>
<h3 className="mt-3 font-display text-base font-extrabold leading-tight text-bright">
<h3 className="mt-3 font-display text-base font-extrabold leading-tight text-deep">
{elemento.titulo}
</h3>
<p className="mt-3 text-sm leading-relaxed text-muted">{elemento.detalle}</p>
<p className="mt-3 text-sm leading-relaxed text-deep">{elemento.llano}</p>
<p className="mt-3 text-sm leading-relaxed text-deep-muted">{elemento.detalle}</p>
</li>
))}
</ol>
<div className="mt-10 rounded-[6px] border border-rule bg-ink-2 p-5 sm:p-6">
<p className="font-mono text-[0.6875rem] uppercase tracking-[0.16em] text-faint">
Mantenimiento de extintores
<div className="mt-10 rounded-[6px] border border-rule-lite bg-white p-5 sm:p-6">
<p className="font-mono text-[0.6875rem] uppercase tracking-[0.16em] text-deep-muted">
La obligación que más se pierde de vista
</p>
<p className="mt-3 max-w-3xl text-sm leading-relaxed text-muted">
{NORMAS[1].aplica} Es la obligación recurrente que más seguido se pierde: no por
desconocerla, sino porque nadie quedó a cargo de la bitácora.
<h3 className="mt-3 font-display text-lg font-extrabold text-deep">
Los extintores se revisan cada mes
</h3>
<p className="mt-3 max-w-3xl text-sm leading-relaxed text-deep-muted">
{NORMAS[1].aplica} Casi nunca se pierde por desconocerla: se pierde porque nadie
quedó a cargo de la bitácora.
</p>
</div>
</div>
</section>
{/* ---------- el expediente ---------- */}
<section className="grid-bg-lite bg-paper py-14 sm:py-20" aria-labelledby="expediente-titulo">
<section
className="border-t border-rule bg-ink py-14 sm:py-20"
aria-labelledby="expediente-titulo"
>
<div className="shell-wide">
<Revelar>
<p className="eyebrow mb-4">Expediente</p>
<h2 id="expediente-titulo" className="font-display text-2xl font-extrabold text-deep sm:text-3xl">
Cómo se ve un expediente que aguanta una verificación
<h2
id="expediente-titulo"
className="max-w-2xl font-display text-2xl font-extrabold sm:text-3xl"
>
Los ocho documentos que piden en una verificación
</h2>
<p className="mt-3 max-w-2xl text-sm leading-relaxed text-deep-muted sm:text-base">
Ocho documentos. Ninguno es opcional cuando alguien pregunta, y cada uno prueba algo
distinto.
<p className="mt-4 max-w-2xl text-sm leading-relaxed text-muted sm:text-base">
Esta es la lista con la que conviene compararse antes de que alguien llegue a
pedirla. Ninguno es opcional cuando se pregunta, y cada uno prueba algo distinto.
</p>
</Revelar>
<p className="mt-6 font-mono text-[0.6875rem] uppercase tracking-[0.14em] text-deep-muted sm:hidden">
<p className="mt-6 font-mono text-[0.6875rem] uppercase tracking-[0.14em] text-faint sm:hidden">
Desliza la tabla para ver todas las columnas
</p>
<div
className="mt-4 overflow-x-auto rounded-[6px] border border-rule-lite bg-white [-webkit-overflow-scrolling:touch]"
className="mt-4 overflow-x-auto rounded-[6px] border border-rule bg-ink-2 [-webkit-overflow-scrolling:touch]"
tabIndex={0}
role="region"
aria-label="Documentos del expediente, tabla desplazable en horizontal"
@@ -289,12 +382,12 @@ export default function PaginaCumplimiento() {
lo solicita.
</caption>
<thead>
<tr className="border-b border-rule-lite bg-paper-2">
<tr className="border-b border-rule bg-ink-3">
{["Documento", "Qué prueba", "Quién lo pide"].map((c) => (
<th
key={c}
scope="col"
className="whitespace-nowrap px-4 py-3 font-mono text-[0.625rem] font-medium uppercase tracking-[0.14em] text-deep-muted"
className="whitespace-nowrap px-4 py-3 font-mono text-[0.625rem] font-medium uppercase tracking-[0.14em] text-faint"
>
{c}
</th>
@@ -303,16 +396,16 @@ export default function PaginaCumplimiento() {
</thead>
<tbody>
{EXPEDIENTE.map((fila) => (
<tr key={fila.documento} className="border-b border-rule-lite last:border-b-0">
<tr key={fila.documento} className="border-b border-rule last:border-b-0">
<th scope="row" className="px-4 py-4 text-left align-top">
<span className="font-sans text-sm font-semibold text-deep">
<span className="font-sans text-sm font-semibold text-bright">
{fila.documento}
</span>
</th>
<td className="max-w-[24rem] px-4 py-4 align-top text-sm leading-relaxed text-deep-muted">
<td className="max-w-[24rem] px-4 py-4 align-top text-sm leading-relaxed text-muted">
{fila.quePrueba}
</td>
<td className="px-4 py-4 align-top font-mono text-xs leading-relaxed text-deep-muted">
<td className="px-4 py-4 align-top font-mono text-xs leading-relaxed text-muted">
{fila.quienLoPide}
</td>
</tr>
@@ -321,22 +414,85 @@ export default function PaginaCumplimiento() {
</table>
</div>
<p className="mt-6 max-w-3xl text-xs leading-relaxed text-deep-muted">
En una compra con nosotros, los documentos que dependen del equipo se entregan por
número de modelo.{" "}
<Link href="/evidencia" className="link">
Ver el índice de documentación por categoría
<div className="mt-8 flex flex-col gap-4 sm:flex-row sm:items-center">
<Link href="#validar" className="btn btn-signal w-full sm:w-auto">
Ver qué me falta del expediente
</Link>
.
</p>
<p className="max-w-lg text-xs leading-relaxed text-faint">
En una compra con nosotros, los documentos que dependen del equipo se entregan por
número de modelo.{" "}
<Link href="/evidencia" className="link">
Ver el índice de documentación por categoría
</Link>
.
</p>
</div>
</div>
</section>
{/* ---------- referencias ---------- */}
<section className="border-t border-rule bg-ink py-14 sm:py-16" aria-labelledby="referencias-titulo">
{/* ---------- lo que cambia por operación ---------- */}
<section
className="grid-bg-lite border-t border-rule bg-paper py-14 sm:py-20"
aria-labelledby="segmentos-titulo"
>
<div className="shell">
<Revelar>
<p className="eyebrow mb-4">Referencias</p>
<p className="eyebrow mb-4">Por operación</p>
<h2
id="segmentos-titulo"
className="max-w-2xl font-display text-2xl font-extrabold text-deep sm:text-3xl"
>
Lo que además se te pide según dónde trabajas
</h2>
<p className="mt-4 max-w-2xl text-sm leading-relaxed text-deep-muted sm:text-base">
La NOM-002 es el piso común. Encima de ella, cada sector acumula requisitos propios
de su autoridad, de su aseguradora o de su cliente corporativo.
</p>
</Revelar>
<ul className="mt-10 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{INDUSTRIAS.map((industria) => (
<li key={industria.slug} className="flex">
<Link
href={`/soluciones/${industria.slug}`}
className="group flex h-full w-full flex-col rounded-[6px] border border-rule-lite bg-white p-5 transition-colors duration-200 hover:border-signal [-webkit-tap-highlight-color:transparent]"
>
<h3 className="font-display text-base font-extrabold leading-tight text-deep">
{industria.nombre}
</h3>
<p className="mt-2 text-sm leading-relaxed text-deep">{industria.dolor}</p>
<ul className="mt-4 space-y-2">
{industria.cumplimiento.map((obligacion) => (
<li
key={obligacion}
className="flex gap-2.5 text-xs leading-relaxed text-deep-muted"
>
<span
aria-hidden="true"
className="mt-1.5 h-1 w-1 shrink-0 rounded-full bg-signal"
/>
{obligacion}
</li>
))}
</ul>
<span className="mt-auto pt-5 font-mono text-[0.625rem] uppercase tracking-[0.14em] text-signal transition-colors group-hover:text-signal-hi">
Ver el segmento &rarr;
</span>
</Link>
</li>
))}
</ul>
</div>
</section>
{/* ---------- referencias normativas ---------- */}
<section
className="border-t border-rule bg-ink-2 py-14 sm:py-16"
aria-labelledby="referencias-titulo"
>
<div className="shell">
<Revelar>
<p className="eyebrow eyebrow-muted mb-4">Referencias</p>
<h2 id="referencias-titulo" className="font-display text-xl font-extrabold sm:text-2xl">
Normas citadas en esta página
</h2>
@@ -371,65 +527,48 @@ export default function PaginaCumplimiento() {
</div>
</section>
{/* ---------- por industria ---------- */}
<section className="border-t border-rule bg-ink-2 py-14 sm:py-16" aria-labelledby="segmentos-titulo">
<div className="shell">
<Revelar>
<p className="eyebrow eyebrow-muted mb-4">Por operación</p>
<h2 id="segmentos-titulo" className="font-display text-xl font-extrabold sm:text-2xl">
Lo que cambia según dónde trabajas
</h2>
</Revelar>
<ul className="mt-8 grid gap-4 sm:grid-cols-2 lg:grid-cols-3">
{INDUSTRIAS.map((industria) => (
<li key={industria.slug}>
<Link
href={`/soluciones/${industria.slug}`}
className="group flex h-full flex-col rounded-[6px] border border-rule bg-ink p-5 transition-colors duration-200 hover:border-rule-2 [-webkit-tap-highlight-color:transparent]"
>
<h3 className="font-display text-base font-extrabold leading-tight text-bright">
{industria.nombre}
</h3>
<ul className="mt-3 space-y-2">
{industria.cumplimiento.map((obligacion) => (
<li key={obligacion} className="flex gap-2.5 text-xs leading-relaxed text-muted">
<span
aria-hidden="true"
className="mt-1.5 h-1 w-1 shrink-0 rounded-full bg-signal"
/>
{obligacion}
</li>
))}
</ul>
<span className="mt-auto pt-5 font-mono text-[0.625rem] uppercase tracking-[0.14em] text-signal transition-colors group-hover:text-signal-hi">
Ver el segmento &rarr;
</span>
</Link>
</li>
))}
</ul>
</div>
</section>
{/* ---------- formulario y cierre con la advertencia ---------- */}
<section id="validar" className="grid-bg-lite border-t border-rule bg-paper py-14 sm:py-20">
{/* ---------- conversión ---------- */}
<section
id="validar"
className="grid-bg-lite border-t border-rule bg-paper py-14 sm:py-20"
aria-labelledby="validar-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 className="font-display text-2xl font-extrabold text-deep sm:text-3xl">
Validar cumplimiento NOM-002
<h2
id="validar-titulo"
className="font-display text-2xl font-extrabold text-deep sm:text-3xl"
>
Te decimos qué te falta antes de venderte nada
</h2>
<p className="mt-4 text-sm leading-relaxed text-deep-muted sm:text-base">
Revisamos qué tienes, qué te falta y en qué orden conviene resolverlo. Si algo de tu
Revisamos qué tienes, qué te falta y en qué orden conviene resolverlo. Si tu
situación requiere un dictamen formal, te lo decimos en lugar de venderte equipo.
</p>
</Revelar>
<ul className="mt-8 space-y-3.5">
{[
"No necesitas saber tu nivel de riesgo para escribirnos: eso es justo lo que revisamos.",
"Si ya te levantaron una observación, mándala tal cual y respondemos sobre ella.",
"Si lo que te falta no es equipo sino documentación, lo decimos así.",
].map((punto) => (
<li key={punto} className="flex gap-3 text-sm leading-relaxed text-deep-muted">
<span
aria-hidden="true"
className="mt-2 h-1 w-1 shrink-0 rounded-full bg-signal"
/>
{punto}
</li>
))}
</ul>
<div
role="note"
aria-label="Alcance de esta página"
aria-label="Recordatorio de alcance"
className="mt-8 rounded-[6px] border border-rule-lite border-l-2 border-l-ember bg-white p-5"
>
<p className="font-mono text-[0.6875rem] uppercase tracking-[0.16em] text-deep-muted">
@@ -440,10 +579,13 @@ export default function PaginaCumplimiento() {
</div>
<FormularioContacto
id="formulario-cumplimiento"
canal="cumplimiento"
variante="claro"
titulo="Validar mi cumplimiento"
descripcion="Dinos qué tipo de instalación es y qué te pidieron. Respondemos con el hueco documental que vemos y por dónde empezar."
titulo="Revisar mi cumplimiento NOM-002"
descripcion="Completa la frase del mensaje con tu tipo de instalación y envía. Respondemos con el hueco documental que vemos y por dónde empezar."
mensajeInicial={MENSAJE_INICIAL}
textoBoton="Solicitar revisión"
/>
</div>
</div>