# CONTEXTO DEL SISTEMA: CORPORATIVA

Este archivo reune todos los documentos de apoyo de un proyecto Corporativa en uno solo. Cada parte empieza con una linea `=== NOMBRE_DEL_ARCHIVO ===` y termina con `=== FIN DE NOMBRE_DEL_ARCHIVO ===`. Cuando las Instrucciones del Proyecto nombren un archivo, busca esa parte aqui y usa solo la que corresponde a la fase en curso. `Contrato_Corporativa.md` manda sobre los demas.

## Indice

- `Contrato_Corporativa.md`: Todas las fases: contrato Corporativa (manda sobre los demas)
- `Plantilla_Business_Info_Copiar_y_Pegar.md`: Fase 1: plantilla de Business Info (zonas: ver Contrato Corporativa, seccion 2)
- `Esquema_JSON_Wizard_Corp_v1.md`: Fases 5 y 12: esquema del JSON del wizard
- `Ejemplo_JSON_Corporativa.json`: Fase 5: ejemplo de contenido.json que valida
- `Guia_Paso_a_Paso_Header_Settings_v3.1.md`: Fase 6: contrato del header (Parte F, con los cambios del Contrato Corporativa)
- `Guia_Paso_a_Paso_Footer_Settings.md`: Fase 7: contrato del footer (Parte E, con los cambios del Contrato Corporativa)
- `Contrato_Formularios.md`: Fase 8: contrato de formularios
- `Formularios_CSS_Base.css`: Primera seccion con formulario: base del CSS del formulario
- `Contrato_Paginas_Home_About_Contact.md`: Fase 8: contrato de paginas (estructura y bloque de textos; las secciones salen del Contrato Corporativa)
- `Contrato_Selector_Servicios.md`: Fase 8.3: contrato del selector de servicios
- `Selector_Servicios_Base.php`: Fase 8.3: codigo base del selector de servicios (se adapta, no se reescribe)
- `Legales_CSS_Base.css`: Fase 10: base del CSS de paginas legales



=== Contrato_Corporativa.md ===

(Todas las fases: contrato Corporativa (manda sobre los demas))

# CONTRATO CORPORATIVA (manda sobre cualquier otro documento del Contexto)

Este proyecto es de tipo CORPORATIVA. Los demás documentos del Contexto (guía del header, guía del footer, contrato de páginas, plantilla de Business Info) se escribieron primero para sitios con páginas de servicios y de áreas. **Cuando alguno contradiga este contrato, manda este contrato.** Donde este contrato no dice nada, sigue el otro documento.

## 1. Cómo es un sitio Corporativa
| Elemento | Regla |
|---|---|
| Páginas | 8, todas ya creadas, vacías y publicadas en el WordPress base: Home (`/`), About Us (`about-us`), Services (`services`), Gallery (`gallery`), Blog (`blog`), Contact (`contact`), Privacy Policy (`privacy-policy`) y Terms of Use (`terms-of-use`). |
| Servicios | El CPT `service` guarda los datos, pero **un servicio no tiene página ni URL propia**. La página Services los muestra con el selector. Cada servicio tiene el ancla `#svc-{slug}` (el slug de la entrada) dentro de la página Services. |
| Galería por servicio | Cada servicio trae su propia galería (`svc_gallery`, máximo 6 fotos) que se muestra dentro de su detalle en Services. Además existe la página Gallery general. |
| Áreas | **No son páginas ni enlaces.** Son texto: la lista de `biz_areas`. No existe el CPT `area` ni las páginas Service Areas. |
| Blog | Página Blog (`/blog/`) y plantilla Single Post. Las entradas se crean con el wizard. |
| Wizard | CM Wizard Corporativa (crea y actualiza servicios y entradas de blog; nunca áreas). |

## 2. Business Info: zonas (cambia la regla de la fase 1)
- `biz_areas` lleva **todas las zonas que los textos del cliente mencionan** (estados, regiones y ciudades), una por fila, con la **zona principal en la primera fila**. Ciudades como "Lancaster, NH"; estados o regiones por su nombre.
- Siguen valiendo las reglas de siempre: solo lo que los textos dicen de verdad, sin suponer, y las contradicciones van a DUDAS. No hay sección CIUDADES DETECTADAS.
- Las zonas se muestran solo como texto, nunca como enlaces. `{areas}` y `{in_areas}` funcionan igual (máximo 2 zonas en títulos).

## 3. JSON del wizard (fase 5)
- Esquema: `Esquema_JSON_Wizard_Corp_v1.md`. Ejemplo que valida: `Ejemplo_JSON_Corporativa.json`. El JSON lleva `"site_type": "corporate"`.
- Items permitidos: `service` y `post`. Nunca `area`.
- Servicio: obligatorios `svc_short_description` y `svc_intro`; opcionales `svc_icon`, `svc_includes`, `svc_benefits`, `svc_status`, `svc_menu_show`. **No se piden** FAQ, proceso, hero, meta title, meta description ni focus keyword. La galería del servicio no va en el JSON: el equipo sube las fotos en `svc_gallery`.
- Entrada de blog: `id`, `title`, `content` (HTML simple), `excerpt`, `category`, `date` (AAAA-MM-DD) y `status`. Pide cuántas entradas quiere el equipo si no lo dijo; si no hay tema definido, propón temas del rubro del cliente.
- Reglas de contenido: nunca escribas a mano teléfono, email, dirección, años ni nombre de la empresa (usa `{phone}`, `{email}`, `{address}`, `{years}`, `{company}`, `{areas}`), tampoco en el contenido de las entradas; inglés americano; sin guion largo (—) ni "across"; sin imágenes; sin PENDIENTE. Si falta un dato del negocio, redacta sin él.
- `svc_icon` es solo el nombre del ícono de Font Awesome, sin prefijo. `position` ordena los servicios (1, 2, 3...).
- Entrega un archivo descargable `contenido.json`, sin explicaciones, y verifica antes los conteos y que no haya datos críticos escritos a mano.

## 4. Header (fase 6)
Sigue la Parte F de la guía del header, con estos cambios:
1. **Menú fijo, en este orden:** Home, About Us, Services (megamenú), Gallery, Blog y Contact. Todos son páginas. No hay Service Areas.
2. **Enlaces:** cada página se busca por su slug con `get_page_by_path()` y se arma con `get_permalink()`; si no existe o no está publicada, no se imprime. Cada servicio del megamenú lleva a `get_permalink( página services ) . '#svc-' . $slug`. El enlace "Services" lleva a la página Services.
3. **Megamenú de Services:** lista de servicios publicados, por `menu_order` y título, sin los de `svc_menu_show` apagado (reglas 43 a 51 de la guía, sin las de áreas). Menú móvil: Services como acordeón con "All Services" y cada servicio.
4. **Botón principal:** enlace de `hdr_btn_link`; si está vacío, la página Contact.
5. **Enlace activo:** lo calcula PHP en cada página (`is_front_page()`, `is_page()`, y Blog también en las entradas). Services queda activo en la página Services.
6. **Desplazamiento suave:** el CSS del header declara `html { scroll-padding-top: var(--hdr-main-h); scroll-behavior: smooth; }` y, dentro de `@media (prefers-reduced-motion: reduce)`, `scroll-behavior: auto`. Es obligatorio.

## 5. Footer (fase 7)
Sigue la Parte E de la guía del footer, con estos cambios:
1. **Cuatro columnas fijas:** Brand, Services (con Company debajo), Areas We Serve y Contact. Sin hueco si una columna no tiene datos.
2. **Services:** servicios publicados (reglas de la guía), con enlace `get_permalink( página services ) . '#svc-' . $slug`. "View All" al final, hacia la página Services, solo si existe y está publicada.
3. **Areas We Serve:** lista **de texto, sin enlaces**, con las zonas de `biz_areas` (la principal primero), hasta `ftr_list_limit`. Sin zonas, la columna no se imprime. No hay "View All".
4. **Company:** About Us, Gallery, Blog y Contact, cada uno solo si su página está publicada.
5. El botón flotante Free Estimate usa `hdr_btn_link` o, si está vacío, la página Contact.
6. Los enlaces legales (Privacy Policy y Terms of Use) siguen siendo obligatorios.

## 6. Páginas (fase 8)
Cada página se construye con los elementos Code por sección, con su clase `.sec-nombre`, su bloque de textos editables y el contrato de páginas. Un solo H1 por página, en su Hero. Primero el bloque "SEO DE LA PÁGINA" y después el código.

**8.1 Home:** Hero, presentación breve (About) con botón a la página About Us, por qué elegirnos (opcional), **servicios** (tarjetas dinámicas, cada una enlazada a `{página services}#svc-{slug}`, y "View All Services" a la página Services), zonas como texto (si hay `biz_areas`), vista previa de la galería (opcional, con enlace a Gallery), últimas entradas del blog (opcional, con enlace a Blog), testimonios (Trustindex) y llamada final a Contact. Sin formulario obligatorio en la Home.

**8.2 About:** Hero interior (H1), historia, años de experiencia, propietario, misión y visión y licencia (solo los que existan en Business Info) y llamada a Contact.

**8.3 Services:** Hero interior (H1) y la sección del selector con `id="services"`. Se usa `Selector_Servicios_Base.php` de Contexto: **no lo reescribas**, solo adapta el CSS al diseño del proyecto y los textos del bloque editable, y respeta `Contrato_Selector_Servicios.md`. Mantén los nombres de clase `sec-svcsel__*`, los ids `svc-{slug}` y `tab-{slug}`, el atributo `data-svc` y el JavaScript. En Corporativa el selector muestra la galería de cada servicio (`svc_gallery`). Cierra con llamada a Contact.

**8.4 Gallery:** Hero interior (H1), cuadrícula con las imágenes del campo ACF **`gal_images`** (Galería, retorno ID) de la página Gallery, y llamada final a Contact. Cada imagen con `width`, `height`, `alt` (el de la biblioteca; si está vacío, `{company}` y la palabra "project") y `loading="lazy"`. Cuadrícula sin huecos con 1, 2, 3, 4 y 20 imágenes (columnas calculadas en PHP, última fila centrada). Sin filtros ni categorías. Ampliar la imagen es opcional, con `<dialog>`, JavaScript mínimo, cierre con Escape y foco controlado. Con el campo vacío, la cuadrícula no se imprime.

**8.5 Blog Overview (`/blog/`):** Hero interior con H1 "Blog", cuadrícula de entradas publicadas con imagen, título, extracto, fecha y enlace (`get_permalink()`), paginación y llamada final. Sin huecos con 1, 2, 3 y 20 entradas. Sin entradas: un mensaje corto en lugar de la cuadrícula. Sin campos ACF, sin autor ni comentarios.

**8.6 Contact:** Hero interior (H1), datos de contacto de Business Info (teléfono, email, dirección y horario, solo los que existan), mapa si hay dirección y el formulario global `[biz_form]`. Aquí se aplica la regla FORMULARIO: el CSS del formulario va en la primera sección del proyecto que lleve formulario.

## 7. Plantilla Single Post (fase 9)
Una sola plantilla (las entradas no tienen campos ACF), construida con elementos Code dentro de la plantilla Single de Bricks con condición Post type = `post`.
| Dato | Cómo se muestra |
|---|---|
| Título | H1 |
| Imagen destacada | Cabecera, con `alt`. Si no hay, no se imprime hueco |
| Fecha y categoría | Metadatos, con enlace de categoría con `get_category_link()` |
| Contenido | `the_content()` con estilos de lectura legibles (ancho de línea cómodo, títulos, listas, imágenes, citas) |
| Entradas relacionadas | Misma categoría, publicadas, sin la actual, hasta 3, sin huecos |
| Llamada final | Teléfono (`biz_phone_href()`) y botón a Contact |
Reglas: el código empieza comprobando `get_post_type() === 'post'` y se detiene si no; sin autor ni comentarios; texto de campos con `esc_html( biz_tokens() )`; sin etiquetas de edición ni cuadros de SEO (el SEO se pega a mano en Bricks); un solo H1; enlaces solo con `get_permalink()`; diseño libre.

## 8. SEO de las páginas
El bloque "SEO DE LA PÁGINA" (meta title, meta description y focus keyword) se entrega para Home, About, Services, Gallery, Blog, Contact, Privacy Policy y Terms of Use. Los servicios no tienen SEO propio porque no tienen página.

- **El SEO va en texto plano y se pega a mano en Bricks** (Page Settings > SEO de cada página). No hay Rank Math ni marcadores: nunca `{company}`, `{in_areas}` ni otros en el meta title o la meta description. Se escribe el nombre real del servicio, de la empresa y de la zona, tomados de la Ficha_Proyecto.md.
- Meta title máximo 60 caracteres y meta description máximo 155. Máximo 2 zonas; con 3 o más se usa solo la zona principal.
- El focus keyword es de referencia: orienta el H1, el primer párrafo y el resto del texto, y no se pega en ningún campo.
- El SEO de la Home cubre el servicio principal y la zona principal (por ejemplo "Roofing in Newburgh, NY").

## 9. Revisión antes de entregar
- [ ] Un servicio del megamenú abre la página Services con ese servicio abierto y baja a la sección, también desde otras páginas.
- [ ] Cada servicio muestra su galería y no deja huecos si tiene 1, 2, 3 o 6 fotos.
- [ ] Despublicar un servicio: desaparece del megamenú, del selector, de la Home y del footer sin dejar huecos.
- [ ] Las zonas se ven como texto, sin enlaces, con la principal primero.
- [ ] No existe ningún enlace a páginas de servicio ni de área.
- [ ] La galería general muestra las imágenes de `gal_images`; el blog pagina bien; la entrada del blog se ve completa.
- [ ] Un solo H1 por página y sin scroll horizontal en 320, 768 y 1280 px.

=== FIN DE Contrato_Corporativa.md ===


=== Plantilla_Business_Info_Copiar_y_Pegar.md ===

(Fase 1: plantilla de Business Info (zonas: ver Contrato Corporativa, seccion 2))

# Plantilla: Business Info listo para copiar y pegar

**Para qué sirve:** que la IA lea los PDFs y el brief del cliente y entregue **todos los datos de Business Info ya ordenados, campo por campo**, para pegarlos en WordPress sin buscar nada. Reemplaza buscar a mano en los PDFs. No usa el wizard.
**Cuándo se usa:** al empezar cada proyecto, antes de crear el contenido. Se pega el prompt de la Parte A en el chat de la IA y se adjuntan los PDFs del cliente.
**Dónde se pega el resultado:** en **Business Info** de WordPress, campo por campo, en el orden de la tabla.

---

## PARTE A: Prompt para pegar en el chat (junto con los PDFs)

````
Lee todos los PDFs y documentos adjuntos del cliente y entrégame los datos de BUSINESS INFO listos para copiar y pegar en WordPress.

REGLAS
1. Usa SOLO información que aparezca en los documentos. Prohibido inventar o completar datos. Si un dato no existe, escribe: NO ENCONTRADO.
2. Si dos documentos se contradicen en un dato, escribe el valor más reciente o más completo y márcalo con (REVISAR) y una línea explicando el conflicto en la sección DUDAS.
3. Todo el texto va en inglés americano. Sin guion largo (—) y sin la palabra "across".
4. Respeta exactamente los formatos de abajo. El valor de cada campo va **solo**, sin comillas y sin explicaciones dentro del valor.
5. COPIA SIN ERRORES: cada valor se entrega dentro de su **propio bloque de código** (para que el equipo use el ícono de copiar y no copie a mano). Dentro del bloque va ÚNICAMENTE el valor: sin espacios al inicio ni al final, sin saltos de línea, sin comillas, sin etiquetas y sin texto extra. Usa solo caracteres normales de teclado: comillas rectas ("), apóstrofe recto ('), guion simple (-) y espacios normales (nunca espacios no separables, comillas curvas ni guiones largos).
6. No pongas colores, tipografías ni contenido de páginas. Solo los datos de esta lista.

FORMATOS
- biz_company_name: nombre legal tal como aparece en el documento (con LLC, Inc., etc. si lo trae).
- biz_years_experience: solo un número entero.
- biz_mission y biz_vision: una frase corta cada una, tomada del brief. Si no existe, NO ENCONTRADO.
- biz_phones: hasta 3 teléfonos. Solo 10 dígitos, sin +1, sin espacios ni guiones. Con su etiqueta opcional (Main, Office, Emergency). El primero es el principal.
- biz_email: en minúsculas.
- biz_whatsapp_number: 11 dígitos con el 1 delante (ej. 15085550123). Si no hay WhatsApp, NO ENCONTRADO.
- biz_areas: SOLO la zona de cobertura principal, en 1 o 2 filas (máximo 2), tal como el brief la nombra. Si el brief habla de un estado (ejemplo: New York), una fila con ese estado. Si nombra dos regiones o condados principales, una fila cada una. NUNCA listes aquí las ciudades individuales: este campo arma los títulos ("Roofing in New York") y los textos, y con 3 o más zonas el título pierde la zona. Las ciudades que el brief mencione van en la sección CIUDADES DETECTADAS (fase 5), no en Business Info. No agregues zonas que no estén en el brief.
- biz_has_license: Sí o No.
- biz_license_number: solo si biz_has_license es Sí.
- biz_address: una sola línea, tal como viene en el documento, con formato "Calle, Ciudad, ST 00000". Si solo hay ciudad y estado, pon solo eso.
- biz_has_hours: Sí o No.
- biz_hours: una sola línea. Ejemplo: Monday to Sunday: 7:00 AM to 3:30 PM
- biz_facebook_url, biz_instagram_url, biz_tiktok_url, biz_youtube_url: URL completa con https://. Si no hay, NO ENCONTRADO.
- biz_has_owner: Sí o No.
- biz_owner_name: solo si biz_has_owner es Sí.

ANÁLISIS DE ZONAS (hazlo ANTES de decidir biz_areas)
Los PDFs traen los textos que irán en el sitio. Lee TODOS los textos y anota cada estado, región, condado y ciudad que mencionan, con el nombre del PDF y la página. Decide biz_areas SOLO con lo que esos textos dicen. Si los textos solo nombran un estado (ejemplo: New York), biz_areas es ese estado, aunque en otro lugar aparezcan ciudades de otro estado. No uses conocimiento externo ni supongas: si un estado no aparece en los textos, no entra. Si los PDFs se contradicen (un PDF dice New York y otro dice Connecticut), pon la contradicción en DUDAS y usa lo que dicen los textos del sitio.

ENTREGA EN ESTE ORDEN
0. ZONAS ENCONTRADAS EN LOS TEXTOS: una tabla con Zona | Tipo (estado, condado, ciudad) | Veces que aparece | Fuente (PDF y página) | Qué se hace (biz_areas, o CIUDADES DETECTADAS, o descartada por qué). Primero esta tabla, antes de los bloques.
1. BLOQUES PARA COPIAR, agrupados por pestaña del formulario de Business Info (1. Identidad de marca, 2. Contacto, 3. Redes sociales). Para cada campo:
   - una línea de texto con el nombre del campo (ejemplo: **biz_company_name**), fuera del bloque;
   - debajo, un bloque de código con SOLO el valor.
   Para biz_phones y biz_areas, un bloque por cada fila (indica "fila 1", "fila 2"...). Para el teléfono pon la etiqueta (Main, Office, Emergency) en su propio bloque aparte.
   Los campos de Sí/No (biz_has_license, biz_has_owner, biz_has_hours) NO llevan bloque: indícalos como "Activar" o "Dejar desactivado" en una línea.
   Los campos con NO ENCONTRADO NO llevan bloque: indícalo en una línea ("dejar vacío").
   El orden de los campos es el de la pestaña: Identidad de marca (biz_company_name, biz_years_experience, biz_address, biz_mission, biz_vision, biz_has_license, biz_license_number, biz_has_owner, biz_owner_name, biz_has_hours, biz_hours), Contacto (biz_phones, biz_email, biz_whatsapp_number, biz_areas), Redes sociales (biz_facebook_url, biz_instagram_url, biz_tiktok_url, biz_youtube_url).
2. Una tabla de revisión con tres columnas: Campo | Valor | Fuente (nombre del PDF y página). Es solo para comprobar de dónde sale cada dato; no se copia de ahí.
3. Sección ARCHIVOS A SUBIR: lista de los logos y badges que aparecen en los documentos (logo principal, logo claro si existe, logos de licencias), con el nombre del archivo.
4. Sección DUDAS: conflictos entre documentos y datos que parecen incorrectos (teléfono con formato raro, email con error, dirección incompleta).
5. Sección FALTANTES: lista de los campos con NO ENCONTRADO que hay que pedir al cliente.
6. Sección CIUDADES DETECTADAS (para la fase 5): las ciudades, condados o zonas específicas que el brief nombra y que NO se cargaron en biz_areas. No se pegan en Business Info: se usan en la fase 5 para crear las áreas. Una por línea, con su estado (ejemplo: White Plains, NY).
````

---

## PARTE B: Orden de los campos en WordPress

La IA entrega la tabla en este orden, el mismo de Business Info. Se pega de arriba hacia abajo.

| # | Pestaña del formulario | Campos (en este orden) |
|---|---|---|
| 1 | Identidad de marca | `biz_company_name`, `biz_years_experience`, `biz_address`, `biz_logo`, `biz_logo_light`, `biz_mission`, `biz_vision`, `biz_has_license`, `biz_license_number`, `biz_license_logos`, `biz_has_owner`, `biz_owner_name`, `biz_has_hours`, `biz_hours` |
| 2 | Contacto | `biz_phones` (hasta 3 filas), `biz_email`, `biz_whatsapp_number`, `biz_areas` (una fila por área) |
| 3 | Redes sociales | `biz_facebook_url`, `biz_instagram_url`, `biz_tiktok_url`, `biz_youtube_url` |

**Qué NO entrega la IA**
- Los logos como archivo: los sube el equipo a la Biblioteca de Medios y los elige en `biz_logo`, `biz_logo_light` y `biz_license_logos`. La IA solo lista cuáles son.
- El formulario de contacto y su reCAPTCHA: no están en Business Info. Se configuran en la plantilla global de Bricks.

---

## PARTE C: Ejemplo de la entrega (datos ficticios)

### 1. Bloques para copiar (así se ve en el chat; cada bloque tiene su ícono de copiar)

**Pestaña 1. Identidad de marca**

**biz_company_name**
```
Northline Roofing LLC
```

**biz_years_experience**
```
12
```

**biz_address**
```
100 Main St, Worcester, MA 01608
```

**biz_mission**: dejar vacío (NO ENCONTRADO)

**biz_has_license**: Activar

**biz_license_number**
```
HIC.0000000
```

**biz_has_owner**: dejar desactivado

**biz_has_hours**: Activar

**biz_hours**
```
Monday to Friday: 8:00 AM to 5:00 PM
```

**Pestaña 2. Contacto**

**biz_phones, fila 1: número**
```
5085550123
```
**biz_phones, fila 1: etiqueta**
```
Main
```

**biz_email**
```
info@northlineroofing.example
```

**biz_areas, fila 1**
```
Massachusetts
```

*(y así con el resto de los campos)*

### 2. Tabla de revisión

| Campo | Valor para pegar | Fuente |
|---|---|---|
| `biz_company_name` | Northline Roofing LLC | Brief.pdf, p. 1 |
| `biz_years_experience` | 12 | Brief.pdf, p. 2 |
| `biz_address` | 100 Main St, Worcester, MA 01608 | Brief.pdf, p. 1 |
| `biz_mission` | NO ENCONTRADO | |
| `biz_vision` | NO ENCONTRADO | |
| `biz_has_license` | Sí | Brief.pdf, p. 2 |
| `biz_license_number` | HIC.0000000 | Brief.pdf, p. 2 |
| `biz_has_owner` | No | |
| `biz_has_hours` | Sí | Brief.pdf, p. 2 |
| `biz_hours` | Monday to Friday: 8:00 AM to 5:00 PM | Brief.pdf, p. 2 |
| `biz_phones` fila 1 | 5085550123 · Main | Brief.pdf, p. 1 |
| `biz_phones` fila 2 | 5085550177 · Emergency | Brief.pdf, p. 1 |
| `biz_email` | info@northlineroofing.example | Brief.pdf, p. 1 |
| `biz_whatsapp_number` | NO ENCONTRADO | |
| `biz_areas` | Massachusetts | Brief.pdf, p. 3 |
| `biz_facebook_url` | https://www.facebook.com/northlineroofing | Brief.pdf, p. 4 |
| `biz_instagram_url` | NO ENCONTRADO | |
| `biz_tiktok_url` | NO ENCONTRADO | |
| `biz_youtube_url` | NO ENCONTRADO | |

**ARCHIVOS A SUBIR:** `northline-logo.png` (principal), `northline-logo-white.png` (claro), `hic-badge.png` (licencia).
**DUDAS:** el teléfono de la p. 1 y el de la p. 5 son distintos (5085550123 y 5085550199). Se usó el de la p. 1 (REVISAR).
**FALTANTES:** misión, visión, WhatsApp, Instagram, TikTok, YouTube.
**CIUDADES DETECTADAS (fase 5, no van en Business Info):** Worcester, MA · Framingham, MA · Natick, MA.

---

## PARTE D: Cómo se usa, paso a paso

1. Abre un chat nuevo del proyecto en la IA y adjunta todos los PDFs del cliente.
2. Pega el prompt de la Parte A.
3. Revisa DUDAS y FALTANTES. Los faltantes se piden al comercial o al cliente.
4. Sube los logos de ARCHIVOS A SUBIR a la Biblioteca de Medios.
5. Abre **Business Info** y, para cada campo, pulsa el **ícono de copiar** del bloque de la IA y pega en el campo. **No selecciones el texto a mano** ni lo escribas: el ícono copia exactamente el valor, sin espacios de más.
6. Pulsa **Guardar**.
7. Comprueba con el Header y el Footer: teléfono, email, dirección y redes deben verse tal como los pegaste.

**Si un valor no queda bien:** pídele a la IA "entrégame de nuevo el bloque de {campo} sin espacios al inicio ni al final". Nunca corrijas a mano un dato dentro del bloque sin comprobar la fuente.

**Regla de calidad:** un campo con NO ENCONTRADO se deja vacío en WordPress. Así el sitio no muestra el dato ni su ícono. Nunca se rellena con un dato aproximado.

**Siguiente paso previsto:** esta plantilla pasa a ser la Fase 1 del prompt maestro nuevo.

=== FIN DE Plantilla_Business_Info_Copiar_y_Pegar.md ===


=== Esquema_JSON_Wizard_Corp_v1.md ===

(Fases 5 y 12: esquema del JSON del wizard)

# Esquema del JSON de CM Wizard Corp (v1)

Cada tipo de proyecto tiene su propio wizard: **CM Wizard One Page** (`cm-wizard-onepage.zip`) y **CM Wizard Corporativa** (`cm-wizard-corporativa.zip`). Son plugins distintos del CM Wizard de los sitios SEO, con el mismo esquema de JSON, y nunca van instalados en el mismo WordPress.

## 1. Estructura
```json
{
  "schema": 1,
  "site_type": "onepage",
  "items": [ { "action": "add", "type": "service", "id": "chimney-repair", "title": "Chimney Repair", "fields": { ... } } ]
}
```
- `schema`: siempre 1.
- `site_type`: obligatorio. `"onepage"` o `"corporate"`. Debe ser igual al tipo del wizard instalado (el tipo viene fijo en el plugin, no hay nada que configurar). Un JSON de otro tipo da error y no cambia nada.
- `items`: lista de servicios y, solo en corporate, entradas de blog.

## 2. Qué aplica el wizard según el tipo
| | `onepage` | `corporate` |
|---|---|---|
| Items permitidos | `service` | `service` y `post` |
| Áreas como página | No existen (error si vienen en el JSON) | No existen |
| Blog | No existe (error) | Sí |

Cada tipo tiene su propio WordPress base, con solo las páginas que usa. El wizard no crea, publica ni esconde páginas.


## 3. Las áreas
No van en el JSON. Se escriben como texto en Business Info (`biz_areas`), con la zona principal en la primera fila, y las secciones imprimen esa lista sin enlaces.

## 4. Servicio (`type: "service"`)
Campos obligatorios: `svc_short_description` y `svc_intro`. Opcionales: `svc_icon`, `svc_includes`, `svc_benefits`, `svc_status`, `svc_menu_show`. No se piden FAQ, meta title, meta description ni focus keyword, porque el servicio no tiene página propia. Las reglas de texto (inglés americano, marcadores `{company}`, `{years}`, `{phone}`, `{areas}`, sin datos escritos a mano, una sola línea en los campos de línea) son las mismas del wizard SEO.

## 5. Entrada de blog (`type: "post"`, solo corporate)
Igual que en el wizard SEO: título, `content` y, si se quiere, categoría e imagen.

## 6. Acciones
`add`, `update`, `rename`, `retire` y `reorder`, con el mismo comportamiento que el wizard SEO. Como los servicios no tienen URL propia, no se crean redirecciones al renombrarlos o retirarlos.

## 7. Imágenes
Los servicios reciben `image-test.webp` como imagen de prueba en sus campos de imagen. En Corporativa, la galería de cada servicio (`svc_gallery`, máximo 6) también recibe la imagen de prueba, y se llena con `images.gallery` cuando se tengan las fotos reales. En One Page no existe galería por servicio.

## 8. Ejemplos que validan
Cada plugin trae su ejemplo que valida: `Ejemplo_JSON_OnePage.json` y `Ejemplo_JSON_Corporativa.json`.

=== FIN DE Esquema_JSON_Wizard_Corp_v1.md ===


=== Ejemplo_JSON_Corporativa.json ===

(Fase 5: ejemplo de contenido.json que valida)

```json
{
  "schema": 1,
  "site_type": "corporate",
  "items": [
    {
      "action": "add",
      "type": "service",
      "id": "chimney-repair",
      "title": "Chimney Repair",
      "position": 1,
      "fields": {
        "svc_short_description": "Brick and mortar chimney repair for homes and businesses in {areas}.",
        "svc_icon": "house-chimney",
        "svc_intro": "{company} has repaired chimneys for {years} years. We find the cause of the damage first, then fix it so it stays fixed.\n\nCall {phone} to schedule a free estimate.",
        "svc_includes": [
          {
            "text": "Full chimney inspection"
          },
          {
            "text": "Tuckpointing and brick replacement"
          },
          {
            "text": "Crown and flashing repair"
          }
        ],
        "svc_benefits": [
          {
            "title": "Stops water damage",
            "text": "We seal the entry points that let moisture into your home."
          },
          {
            "title": "Matches your brick",
            "text": "We match brick and mortar so the repair blends in."
          }
        ],
        "svc_status": "ready"
      }
    },
    {
      "action": "add",
      "type": "service",
      "id": "masonry-restoration",
      "title": "Masonry Restoration",
      "position": 2,
      "fields": {
        "svc_short_description": "Careful masonry restoration for homes and businesses in {areas}.",
        "svc_icon": "trowel-bricks",
        "svc_intro": "{company} restores brick, stone and block so it looks right and lasts.\n\nCall {phone} to schedule a free estimate.",
        "svc_includes": [
          {
            "text": "Brick and stone repair"
          },
          {
            "text": "Repointing and tuckpointing"
          },
          {
            "text": "Cleaning and sealing"
          }
        ],
        "svc_benefits": [
          {
            "title": "Protects the structure",
            "text": "Sound masonry keeps water out and walls strong."
          },
          {
            "title": "Better curb appeal",
            "text": "Matched materials make repairs hard to spot."
          }
        ],
        "svc_status": "ready"
      }
    },
    {
      "action": "add",
      "type": "post",
      "id": "chimney-maintenance-tips",
      "title": "5 Chimney Maintenance Tips Before Winter",
      "date": "2026-10-01",
      "category": "Chimney Care",
      "excerpt": "Simple checks that prevent costly chimney repairs.",
      "content": "<p>Check your chimney crown and mortar each fall. Small cracks grow quickly in freezing weather.</p><p>Call {phone} if you see loose brick or water stains.</p>",
      "status": "publish"
    }
  ]
}
```

=== FIN DE Ejemplo_JSON_Corporativa.json ===


=== Guia_Paso_a_Paso_Header_Settings_v3.1.md ===

(Fase 6: contrato del header (Parte F, con los cambios del Contrato Corporativa))

# Guía paso a paso: Header Settings (ACF PRO) — v3.1

**Objetivo:** crear la página de opciones que controla el header sin tocar código: altura, colores, Call Now, botón principal, topbar opcional y modo transparente. El código del header (generado por la IA y pegado en Bricks) lee estos campos. Los datos del negocio salen de Business Info.
**Dónde se hace:** en el WordPress base, nunca en un proyecto de cliente.
**Requisitos previos:** Business Info creado y el kit de variables de Bricks cargado, incluidas las 5 variables nuevas de `Reglas_Prompt_Actual_vs_Sistema_Nuevo.md` (`z-dropdown`, `z-header`, `z-modal`, `container-max`, `container-padding`).
**Footer:** va después, con su propia guía.

> **Regla de idioma:** todo lo que se escribe en WordPress va en inglés; las instrucciones de ayuda para el equipo quedan en español.
> **Prefijos:** `hdr_` para todo el header. Los campos del estilo transparente llevan `hdr_t_`. Los sub-campos de repetidores no llevan prefijo.

---

## Cambios respecto a v2 (por el prompt actual)

| Cambio | Motivo |
|---|---|
| Nuevo `hdr_call_style` | El prompt define dos variantes del Call Now: compacta y completa |
| `hdr_call_color` y `hdr_t_call_color` ahora controlan **solo el ícono** | La etiqueta y el número toman el color del texto del menú |
| Eliminados `hdr_topbar_hide_scroll` y `hdr_topbar_mobile` | El topbar es fijo en escritorio y tablet, y se oculta en celular. Sus datos pasan al menú móvil. Menos movimiento, menos riesgo de parpadeo |
| Eliminado `hdr_dropdown_mode` | El prompt define dos megamenús fijos: Services y Service Areas |
| Tipos del menú: `page`, `services`, `areas` | El tipo `blog` sobra: es un enlace de página normal |
| Parte F (contrato) reescrita | Incluye las reglas del prompt actual |

**Si ya creaste campos con v2:** borra `hdr_topbar_hide_scroll`, `hdr_topbar_mobile` y `hdr_dropdown_mode`; agrega `hdr_call_style`; cambia las etiquetas de `hdr_call_color` y `hdr_t_call_color`. Si creaste `hdr_topbar_badges` o `hdr_menu_items`, bórralos.

**Total: 7 pestañas y 29 campos** (el menú ya no tiene campos: está fijo en el código y se llena solo; el logo claro pasó a Business Info como `biz_logo_light`; la v3.1 no agrega campos al header: los datos del menú viven en cada servicio, ver Parte C2).

## Novedades de la v3.1

**Menú simplificado:** se eliminó la pestaña Menu y el repetidor `hdr_menu_items`. El menú es siempre Home, About Us, Services, Service Areas, Blog y Contact, lo arma el código y se llena solo. Si ya creaste `hdr_menu_items`, bórralo.

| Cambio | Motivo |
|---|---|
| Parte C2 nueva: preparar el servicio para el menú | Orden, ícono, imagen y descripción del megamenú se editan **en el servicio**, sin regenerar código |
| Regla 7 reforzada: el ícono también es condicional | Que no quede un ícono suelto cuando falta el dato |
| Parte F7 nueva: megamenú de diseño libre con datos fijos | Cada proyecto puede usar ícono, imagen o descripción, pero siempre con la misma fuente de datos |
| Marca visible de imagen de prueba en el megamenú | El equipo ve qué imágenes faltan reemplazar |
| Pruebas y checklist ampliados | Cubren lo anterior |

---

## Cómo se comporta cada modo

| | Modo **Solid** | Modo **Transparent** |
|---|---|---|
| Posición del Hero | **Debajo** del header | **Detrás** del header (su texto empieza debajo) |
| Header arriba de todo | Fondo y colores del estilo Solid | Sin fondo; colores del estilo Transparent |
| Después del primer scroll | Igual, con sombra | Pasa a fondo y colores del estilo Solid, con sombra |
| Altura | La del estilo Solid, siempre fija | La del estilo Transparent arriba; la del Solid después |
| Hero de Home | Header + Hero suman 100vh | El Hero ocupa 100vh y el header va encima |

El estilo "después del scroll" del modo transparente **reutiliza el estilo Solid**: menos campos y contraste garantizado.

---

## PARTE A: Crear la página de opciones

### Paso 1. Header Settings
**ACF → Páginas de opciones → Añadir nueva**

| Ajuste | Valor |
|---|---|
| Título de la página | `Header Settings` |
| Slug del menú | `header-settings` |
| Página superior | `Business info` (si aparece en el desplegable; si no, déjala como página principal) |
| Capacidad | Por defecto (`edit_posts`) |

---

## PARTE B: Lista de colores permitidos

Todos los campos de color son **Selección** con estas 10 opciones. En **Opciones** se pegan igual en cada uno (una por línea, formato `valor : Etiqueta`):

```
primary : Primary
primary-d-5 : Primary dark
primary-l-10 : Primary lightest
secondary : Secondary
secondary-l-10 : Secondary lightest
cta : CTA
cta-d-3 : CTA dark
color-text : Text gray
white : White
black : Black
```

**¿Se pueden agregar más colores? Sí.** Para agregar uno:
1. La variable debe existir en Bricks (el kit trae `primary`, `secondary` y `cta`, cada uno con `-l-1` a `-l-10`, `-d-1` a `-d-10` y `-t-1` a `-t-10`).
2. En ACF, abre el campo de color y agrega una línea en **Opciones**: `nombre-de-la-variable : Etiqueta`, sin `--`. Ejemplo: `primary-d-8 : Primary darkest`.
3. Repite en los campos de color donde lo quieras (son 15). Si solo lo necesitas en uno, agrégalo solo ahí.
4. Guarda el grupo y anota el color nuevo en esta lista.

El valor guardado es el **nombre de la variable de Bricks**. El código lo convierte en `var(--valor)`. Todos van con **Formato de retorno: Valor**.

> **Contraste:** los valores por defecto de abajo son seguros (blanco sobre `primary` o `cta`). Como los 3 colores de marca cambian por proyecto, el equipo debe revisar el contraste del Call Now y del botón en cada sitio.

---

## PARTE C: Crear el grupo de campos `info header`

### Paso 2. Crear el grupo
**ACF → Grupos de campos → Añadir nuevo**. Título: `info header`

| Ajuste | Valor |
|---|---|
| Reglas de ubicación | Página de opciones **es igual a** Header Settings |
| Estilo | Estándar |
| Posición | Alta |
| Ubicación de la etiqueta | Alineada arriba |
| Ubicación de las instrucciones | Debajo de las etiquetas |

### Paso 3. Cómo se llena cada campo (recordatorio)

| Qué | Dónde |
|---|---|
| Etiqueta, Nombre, Tipo, opciones, valor por defecto, mínimo/máximo | Pestaña **General** del campo |
| Instrucciones | Pestaña **Presentación** |
| "Mostrar solo si..." | Pestaña **Lógica condicional**: activar → elegir el campo → **es igual a** → valor |

Escribe primero la Etiqueta y luego corrige el **Nombre del campo**. Guarda cambios al terminar cada pestaña. En los **Verdadero / Falso**, activa "Mostrar interfaz con estilo".

### Paso 4. Pestañas y campos (en este orden)

**Pestaña `1. Mode`**
| Label | Nombre | Tipo | Ajustes | Instrucciones (pegar) |
|---|---|---|---|---|
| Header mode | `hdr_mode` | Selección | Opciones: `solid : Solid` y `transparent : Transparent` · Defecto `solid` · Retorno: Valor | Solid: el Hero queda debajo del header. Transparent: el Hero queda detrás del header y, al primer scroll, el header se vuelve sólido. |
| Transparent on | `hdr_transparent_scope` | Selección | Opciones: `home : Home page only` y `all : Entire site` · Defecto `home` · Lógica condicional: `hdr_mode` es igual a `transparent` | Home page only: solo la portada es transparente; el resto del sitio usa el header sólido. Entire site: todas las páginas con Hero son transparentes (el sitio debe empezar cada página con un Hero apto). Las páginas 404 y de búsqueda siempre usan el header sólido. |

**Pestaña `2. Solid style`**

Instrucción de la pestaña (en el primer campo): este estilo también se usa en el header transparente después del primer scroll.

| Label | Nombre | Tipo | Ajustes | Instrucciones (pegar) |
|---|---|---|---|---|
| Height desktop | `hdr_height_desktop` | Número | Mín. **56** · Máx. **120** · Defecto **80** · Añadido: `px` | Altura de la barra principal en computador, sin contar el topbar. |
| Height mobile | `hdr_height_mobile` | Número | Mín. **48** · Máx. **90** · Defecto **64** · Añadido: `px` | Altura de la barra principal en celular y tablet (menú hamburguesa). |
| Background | `hdr_bg_color` | Selección | Lista de colores · Defecto `white` | Color de fondo del header. |
| Menu text color | `hdr_menu_color` | Selección | Lista de colores · Defecto `color-text` | Color de los enlaces del menú y del texto del Call Now. |
| Call Now background | `hdr_call_bg` | Selección | Lista de colores · Defecto `primary` | Color del círculo del Call Now. |
| Call Now icon | `hdr_call_color` | Selección | Lista de colores · Defecto `white` | Color del ícono dentro del círculo. |
| Button background | `hdr_btn_bg` | Selección | Lista de colores · Defecto `cta` | Color de fondo del botón principal. |
| Button text | `hdr_btn_color` | Selección | Lista de colores · Defecto `white` | Color del texto del botón principal. |

**Pestaña `3. Transparent style`**

Lógica condicional de la **pestaña**: `hdr_mode` es igual a `transparent`. Si tu versión de ACF no oculta los campos de dentro, aplica la misma lógica campo por campo.

Instrucción de la pestaña (en el primer campo): colores del header mientras está arriba, sobre el Hero. Después del primer scroll se usa el estilo de la pestaña 2.

| Label | Nombre | Tipo | Ajustes | Instrucciones (pegar) |
|---|---|---|---|---|
| Height desktop | `hdr_t_height_desktop` | Número | Mín. **56** · Máx. **120** · Defecto **90** · Añadido: `px` | Altura de la barra principal arriba, en computador. |
| Height mobile | `hdr_t_height_mobile` | Número | Mín. **48** · Máx. **90** · Defecto **64** · Añadido: `px` | Altura de la barra principal arriba, en celular y tablet. |
| Menu text color | `hdr_t_menu_color` | Selección | Lista de colores · Defecto `white` | Color de los enlaces del menú y del texto del Call Now sobre el Hero. |
| Call Now background | `hdr_t_call_bg` | Selección | Lista de colores · Defecto `white` | Color del círculo del Call Now sobre el Hero. |
| Call Now icon | `hdr_t_call_color` | Selección | Lista de colores · Defecto `primary` | Color del ícono dentro del círculo. |
| Button background | `hdr_t_btn_bg` | Selección | Lista de colores · Defecto `cta` | Fondo del botón principal sobre el Hero. |
| Button text | `hdr_t_btn_color` | Selección | Lista de colores · Defecto `white` | Texto del botón principal sobre el Hero. |

**Pestaña `4. Logo`**
| Label | Nombre | Tipo | Ajustes | Instrucciones (pegar) |
|---|---|---|---|---|
| Logo height desktop | `hdr_logo_height_desktop` | Número | Mín. **24** · Máx. **100** · Defecto **48** · Añadido: `px` | Altura del logo en computador. |
| Logo height mobile | `hdr_logo_height_mobile` | Número | Mín. **20** · Máx. **70** · Defecto **36** · Añadido: `px` | Altura del logo en celular y tablet. |

Los logos salen de Business Info: `biz_logo` (normal) y `biz_logo_light` (versión clara, para el header transparente y fondos oscuros). **Crea `biz_logo_light` en Business Info** (Imagen, retorno ID, opcional). Si está vacío, el header transparente usa `biz_logo`.

**Pestaña `5. Call Now & button`**
| Label | Nombre | Tipo | Ajustes | Instrucciones (pegar) |
|---|---|---|---|---|
| Call Now style | `hdr_call_style` | Selección | Opciones: `full : Icon, label and number` y `compact : Icon only` · Defecto `full` | Solo para computador. Completo: círculo con ícono, etiqueta y número. Compacto: solo el círculo con el ícono. En celular siempre se muestra el ícono con la etiqueta, sin número. |
| Call Now text | `hdr_call_text` | Texto | Defecto `Call Now` | Etiqueta del botón de llamada. Se muestra en mayúsculas, en computador y en celular. |
| Call Now icon | `hdr_call_icon` | Texto | Defecto `phone` | Solo el nombre del ícono de Font Awesome, sin prefijo. Ejemplo: phone |
| Button text | `hdr_btn_text` | Texto | Defecto `Get Free Estimate` | Texto del botón principal. Se oculta siempre en celular. Si lo dejas vacío, el botón no se muestra. |
| Button link | `hdr_btn_link` | Enlace | Retorno: **Matriz de enlace** | A dónde lleva el botón. Normalmente la página de contacto. |

**Pestaña `6. Topbar`**
| Label | Nombre | Tipo | Ajustes | Instrucciones (pegar) |
|---|---|---|---|---|
| Show topbar | `hdr_topbar_show` | Verdadero / Falso | Defecto: **Sí** | Activa la barra superior. Si está apagada no se muestra y el Hero se ajusta solo. En celular la barra se oculta y sus datos pasan al menú móvil. |
| Topbar background | `hdr_topbar_bg` | Selección | Lista de colores · Defecto `primary-d-5` · Lógica condicional: igual que arriba | Color de fondo de la barra superior. |
| Topbar text color | `hdr_topbar_color` | Selección | Lista de colores · Defecto `white` · Lógica condicional: igual que arriba | Color del texto y de los íconos. |


**Pestaña `7. Mobile`**
| Label | Nombre | Tipo | Ajustes | Instrucciones (pegar) |
|---|---|---|---|---|
| Mobile menu background | `hdr_mobile_bg` | Selección | Lista de colores · Defecto `primary` | Fondo del menú móvil desplegado. |
| Mobile menu text | `hdr_mobile_text` | Selección | Lista de colores · Defecto `white` | Color del texto del menú móvil. |

### Paso 5. Guardar y revisar nombres
**Guardar cambios** y comparar la columna **Nombre** con las tablas, letra por letra. Un nombre mal escrito rompe el código del header.

---

## PARTE C2: Preparar el servicio para el menú

El megamenú no tiene lista propia: lee los servicios y áreas publicados. Así, crear, renombrar, reordenar o despublicar un servicio actualiza el menú sin tocar el header ni regenerar código.

### Qué dato sale de dónde

| Dato del megamenú | Dónde se edita | Campo |
|---|---|---|
| Nombre | Servicio → Título | Nativo |
| Enlace | Automático | `get_permalink()` |
| **Orden** | Servicio → cuadro Atributos de página → Orden (número) | `menu_order` nativo |
| **Ícono** | Servicio → pestaña `1. Identity` | `svc_icon` (ya existe) |
| **Imagen** | Servicio → Imagen destacada | Nativo |
| Descripción corta | Servicio → pestaña `1. Identity` | `svc_short_description` (ya existe) |
| Mostrar en el menú | Servicio → pestaña `1. Identity` | `svc_menu_show` (**nuevo**) |

### Paso A. Un campo nuevo en `Info service`
**ACF → Grupos de campos → info service → pestaña `1. Identity`**, debajo de `svc_icon`:

| Label | Nombre | Tipo | Ajustes | Instrucciones (pegar) |
|---|---|---|---|---|
| Show in menu | `svc_menu_show` | Verdadero / Falso | Mostrar interfaz con estilo · Defecto: **Sí** | Apágalo para ocultar este servicio del menú sin despublicarlo. La página sigue existiendo. |

### Paso B. Mejorar las instrucciones de `svc_icon`
Pestaña **Presentación** del campo `svc_icon`, reemplaza por:

> Solo el nombre del ícono de Font Awesome, sin prefijo. Ejemplo: house-chimney. Para cambiarlo, busca el servicio en fontawesome.com/icons, abre el ícono y copia solo el nombre. Los íconos Pro están permitidos. Si el ícono no aparece en el sitio, revisa que el nombre esté bien escrito.

Se cambia el ícono escribiendo otro nombre y guardando el servicio. No hay que tocar el header.

> **Font Awesome Pro:** el sitio usa el kit Pro, así que se pueden usar todos los íconos de pago. El estilo (regular, light, thin, duotone o solid) lo define el código del header, un solo estilo por proyecto, para que todos los íconos se vean coherentes. El nombre guardado en el campo no incluye el estilo.

### Paso C. Ordenar con un número
`service` ya soporta **Atributos de página**, que trae un campo numérico nativo: **Orden**. Es el número que define la posición en el menú. **No hace falta crear otro campo.**

1. Abre el servicio. En la barra lateral, en el cuadro **Atributos de página**, está el campo **Orden**.
2. Menor número = aparece primero. Usa saltos de 10 (10, 20, 30...) para poder intercalar uno nuevo sin renumerar.
3. En la lista de Services también se cambia con **Edición rápida → Orden**.
4. El wizard asigna estos números al crear los servicios, según el orden del JSON.

> **Opcional:** el plugin *Simple Page Ordering* permite además arrastrar las filas en la lista. No es necesario.

---

### Paso D. Imagen de prueba visible
El wizard asigna `image-test.webp` como imagen destacada cuando el JSON no trae una. El megamenú la usa, así que el equipo sabe que falta la foto real por tres vías:
1. El código del megamenú marca esa imagen con un borde de color y una etiqueta "Replace image", **solo para usuarios con sesión de editor o administrador**. El visitante no ve nada.
2. El reporte final del wizard lista las páginas que aún usan la imagen de prueba.
3. El checklist de QA bloquea pasar a `ready` mientras haya una.

---

## PARTE D: Guardar los valores por defecto

ACF no graba los valores por defecto de una página de opciones hasta que se pulsa **Guardar**.

1. Abre **Header Settings**, revisa que todo esté en sus valores por defecto.
2. Pulsa **Guardar**.

Los valores quedan en la base de datos, así que **viajan con el clon de WPVivid**.

---

## PARTE E: Lo que NO es un campo (fijo en el código)

| Elemento | Valor fijo |
|---|---|
| Topbar oculto | Desde 767 px hacia abajo |
| Menú hamburguesa y megamenú desactivado | Desde 1023 px hacia abajo |
| Estructura del menú | Home, About Us, Services, Service Areas, Blog, Contact (los ítems sin contenido no se imprimen) |
| Tamaño del texto del menú | `var(--text-m)` |
| Tamaño del texto del topbar | `var(--text-s)` |
| Efecto hover del menú | Subrayado con el color del texto, sin cambiar de color |
| Link activo | Subrayado permanente, sin cambiar el peso de la letra |
| Sombra del header después del scroll | `var(--shadow-s)` |
| Transición | `var(--transition)` |
| Capa (z-index) | `var(--z-header)` |
| Altura del topbar | Fija en el código |

---

## PARTE F: Contrato para la IA (header)

Estas reglas se pegan en el prompt maestro. Son obligatorias. Vienen del prompt actual, adaptadas al sistema nuevo (ver `Reglas_Prompt_Actual_vs_Sistema_Nuevo.md`).

### F1. Datos y estilos
1. **Solo estos campos**, leídos con `get_field('nombre', 'option')`. Nunca se inventan campos.
2. **Los datos del negocio salen de Business Info con PHP**, nunca escritos a mano ni con `window.SITE` ni atributos `data-site`. Teléfono, email, dirección, redes y logo se leen de los campos `biz_`.
3. **Todo con variables de Bricks.** Colores, `--text-*`, `--space-*`, `--radius-*`, `--shadow-*`, `--transition`, `--z-*`, `--container-*`. Nada de hex, `px`, `rem` ni `z-index` sueltos. Excepciones: las alturas que vienen de los campos numéricos, `50%` para círculos y la compensación de la barra de administración.
4. **Valor de respaldo en todo.** `var(--primary, #253C6D)`. Si un campo está vacío o la página de opciones nunca se guardó, se usa el valor por defecto de las tablas.
5. **Los selectores de color se validan** contra las opciones del propio campo (`get_field_object('nombre', 'option')['choices']`), no contra una lista escrita en el código. Así, agregar un color en ACF no exige tocar código. Un valor que no esté en las opciones usa el valor por defecto.
6. **Todo lo que se imprime va escapado** (`esc_html`, `esc_url`, `esc_attr`).
7. **Todo es condicional por campo vacío o apagado, ícono incluido.** Cada elemento y su ícono van dentro de la misma condición `if ( valor )`. Si el topbar está apagado, **no se imprime su HTML**. Sin teléfono no hay Call Now ni su ícono. Una red social sin URL no se muestra. Sin `svc_icon` no hay hueco de ícono en el menú. Un wrapper vacío tampoco se imprime.
8. **Prohibido:** `href="#"`, enlaces inventados, mostrar el dominio del sitio como texto, guion largo (—) y la palabra "across" en cualquier texto.
9. **Sin regex con backslash** (`\d`, `\s`, `\w`...) en JavaScript ni en PHP: Bricks los borra al guardar. Para quitar caracteres no numéricos del teléfono en PHP usa `preg_replace('/[^0-9]/', '', $valor)`.

### F2. Estructura y nombres (Bricks)
10. **El bloque BEM se llama `.site-header`**, nunca `.header` (choca con la clase interna de Bricks y rompe el header). Elementos: `.site-header__topbar`, `.site-header__main`, `.site-header__logo`, `.site-header__nav`, `.site-header__link`, `.site-header__mega`, `.site-header__phone`, `.site-header__cta`, `.site-header__toggle`.
11. **Dentro de `<header class="site-header" id="siteHeader">` solo van** `.site-header__topbar` y `.site-header__main`.
12. **El overlay del menú móvil (`.site-header__mobile`) va FUERA del `<header>`**, como hermano directo. Dentro rompe el stacking context.
13. **Nunca se activa el Sticky nativo de Bricks.** Bricks pone `overflow: hidden` en sus contenedores y rompe `position: sticky`. El header es fijo con código propio.
14. **En `.site-header`:** `position: fixed; top: 0; left: 0; width: 100%; z-index: var(--z-header)`. **Prohibido** `sticky`, `absolute`, `transform` y `will-change` en `.site-header`. Se permiten `transform` y `opacity` en sus elementos hijos.
15. **Sin H1 a H6** dentro del header ni del nav.
16. **Logo:** `<img>` (nunca un encabezado), con atributos `width` y `height` reales, sin `lazy` (está sobre el pliegue) y `alt` con el nombre de la empresa.
17. **Sin `!important`.** Única excepción permitida: el estado `.is-active`, si Bricks lo sobrescribe.
18. **Entrega:** `header.html` (con el PHP embebido), `header.css` y `header.js`, siempre como archivos. El HTML va en **un solo elemento Code de Bricks en modo PHP**. Sin archivo `.php` aparte ni plugin de snippets.
19. **Íconos:** el kit de Font Awesome se carga una sola vez en Bricks, nunca en el HTML. Se permiten los íconos de Font Awesome Pro. Un solo estilo por proyecto para todos los íconos (por defecto `fa-regular`; la IA puede elegir `fa-light`, `fa-thin`, `fa-duotone` o `fa-solid` si encaja con el diseño) y `fa-brands` para las redes. El estilo elegido se declara en el informe de entrega. El ícono del Call Now sale de `hdr_call_icon`; la plantilla agrega el prefijo.

### F3. Header fijo, cero parpadeos, Hero sin tapar
20. **La altura se calcula en el servidor.** PHP imprime, al inicio del header, un bloque `<style>` con `--hdr-main-h` (barra principal), `--hdr-top-h` (topbar; 0 si está apagado o bajo 767 px) y `--hdr-h` (la suma). Se redefinen para tablet y celular. **No se mide la altura con JavaScript.** Aplicar el espacio con JS al cargar causa el salto de contenido.
21. **El modo se decide en el servidor.** Con `hdr_transparent_scope` = `all`, la clase `hdr-transparent` se aplica a todas las páginas **excepto** `is_404()` y `is_search()`, que siempre llevan `hdr-solid` porque no tienen Hero. Con `home`, solo a `is_front_page()`. PHP agrega a `<body>` la clase `hdr-solid` o `hdr-transparent`, según `hdr_mode`, la página actual y `hdr_transparent_scope`. Nunca con JS al cargar.
22. **Modo Solid:** el contenedor principal de Bricks (`main.brx-content`, con `.brx-content` de respaldo, igual que el prompt actual) lleva `padding-top: var(--hdr-h)`. Además `html { scroll-padding-top: var(--hdr-main-h); }` para que los enlaces ancla no queden tapados.
23. **Modo Transparent:** sin `padding-top` en el contenedor. El código de cada Hero incluye `body.hdr-transparent .hero, body.hdr-transparent .hero-interior { padding-top: calc(var(--hdr-h) + var(--space-l)); }` para que su texto no quede bajo el header.
24. **Altura del Hero de Home:** en Solid, `min-height: calc(100vh - var(--hdr-h))` para que header y Hero sumen 100vh exactos. En Transparent, `min-height: 100vh`. En celular, `min-height: unset` y altura por contenido. El Hero interior sigue con `clamp(28rem, 45vh, 50rem)`.
25. **Barra de administración de WordPress:** `body.admin-bar .site-header { top: 32px; }` y `top: 46px` bajo 783 px. Se usa la clase `admin-bar` que WordPress agrega al `<body>`.
26. **Scroll:** una función `handleScrollState` (antes `handleScrollShadow`) alterna `.is-scrolled` en el header cuando `scrollY > 20`, con `requestAnimationFrame` y listener `passive`. **Prohibido** cualquier otro listener de scroll sin `requestAnimationFrame`. `.is-scrolled` nunca cambia `position`.
27. **Estado correcto desde el primer pintado.** Un script mínimo y en línea, al inicio del header, revisa `scrollY` y agrega `is-scrolled` antes de que la página se pinte. Así no hay parpadeo al recargar a mitad de página o al entrar por un enlace ancla.
28. **Efecto de `.is-scrolled`:** en Solid, solo `box-shadow: var(--shadow-s)`. En Transparent, además el header pasa a fondo, colores y logo del estilo Solid.
29. **Transiciones solo en** `color`, `background-color`, `box-shadow`, `opacity` y `transform`. **Prohibido** animar `width`, `height`, `top`, `left`, `padding` o `margin`. Excepción documentada: en modo Transparent la altura de `.site-header__main` puede animarse porque el header está fuera del flujo. En Solid la altura nunca cambia al hacer scroll.

### F4. Menú y navegación
30. **El menú no tiene campos: su estructura está fija en el código y se arma sola.** Orden: Home, About Us, Services (megamenú), Service Areas (megamenú), Blog, Contact. Cada enlace a una página se busca por su slug (`home`, `about-us`, `blog`, `contact`) con `get_page_by_path()` y se arma con `get_permalink()`. **Si la página no existe o no está publicada, el ítem no se imprime.** Services y Service Areas no se imprimen si no hay servicios o áreas publicados. Los textos del menú los define la IA en el código de cada proyecto. Los enlaces de servicios y áreas salen de consultas a contenido **publicado**, ordenado por `menu_order`, y se arman con `get_permalink()`. Nunca se escriben a mano.
31. **Megamenú Services:** lista de los servicios publicados. **Megamenú Service Areas:** servicios a la izquierda; al pasar el mouse por uno, el panel derecho muestra sus ciudades. El texto del enlace es el título de la página: "Chimney Repair in Lancaster, NH".
32. **El megamenú se abre y cierra solo con JavaScript.** Nunca con `:hover` de CSS. `mouseenter` y `mouseleave` con 150 ms de retraso al cerrar, clic fuera y Escape. El panel está oculto por defecto (`opacity`, `visibility`, `pointer-events`) y se muestra con `.is-open`. No debe provocar scroll horizontal.
33. **Bajo 1023 px el megamenú de escritorio está desactivado** y los servicios se listan en el menú móvil como acordeón con toggle al tocar.
34. **Hover y activo en todos los links del menú.** Subrayado animado con `transform: scaleX` y color `currentColor`, para que se vea sobre fondo claro y sobre el Hero oscuro. El link activo lleva subrayado permanente **sin cambiar el peso de la letra**, porque eso ensancha el texto y mueve el menú.
35. **El link activo lo calcula PHP en cada página.** Home: `is_front_page()`. Services: `is_singular('service')` o la página `/services/`. Service Areas: `is_singular('area')` o la página `/service-areas/`. Blog: la página del blog y las entradas. Contact y About Us: su página.
36. **Menú móvil (`≤ 1023 px`):** panel fijo bajo el header (`z-index: calc(var(--z-header) - 1)`), botón con `aria-expanded`, cierre con Escape, foco controlado y bloqueo del scroll del fondo. Si el topbar está activo, su ubicación, email y redes aparecen dentro del menú móvil.
37. **Barra principal en celular, en una sola fila y en este orden:** logo, botón de llamada (círculo con ícono y la etiqueta `hdr_call_text` en mayúsculas, **sin el número**) y botón hamburguesa. El botón principal se oculta. Si `hdr_call_text` está vacío, solo el círculo con el ícono. Siempre se muestra, salvo que Business Info no tenga teléfono.

### F5. Topbar
38. **Izquierda:** ubicación con ícono (`location-dot`) y email con ícono (`envelope`, en una sola línea con `white-space: nowrap`). **Derecha:** la etiqueta "FOLLOW US:" y las redes como íconos redondos. Todo o nada: el topbar se muestra completo o no se muestra (`hdr_topbar_show`), sin interruptores por elemento. Cada dato (ubicación, email, cada red social) se imprime solo si existe en Business Info. **No hay badges ni texto central**: el topbar tiene solo estos dos grupos, alineados a los extremos.
39. **Topbar delgado:** una sola fila, texto `var(--text-s)`, relleno vertical `var(--space-2xs)`, íconos pequeños alineados con el texto.

### F6. Call Now y botón principal
40. **Call Now en escritorio (más de 1023 px):** un `<a href="tel:...">` con el círculo del ícono. En estilo `full`, junto al círculo van dos líneas apiladas: la etiqueta (`hdr_call_text`, en mayúsculas) y el número en negrita. En estilo `compact`, solo el círculo con `aria-label="Call now"`. **En celular y tablet (1023 px o menos) se muestra siempre el círculo con la etiqueta y nunca el número**, sea cual sea el estilo. El círculo usa el color de fondo del Call Now y el ícono su color; la etiqueta y el número usan el color del texto del menú.
41. **El teléfono es dinámico y sale del primer teléfono de Business Info** (`biz_phones`, primera fila, sub-campo `phone_number`). PHP quita lo que no sea dígito con `preg_replace('/[^0-9]/', '', $valor)` y arma: el `href` en formato internacional `tel:+1XXXXXXXXXX` y el texto visible `(555) 123-4567`. Nunca se escribe un número a mano. Si no hay teléfono, no se imprime el Call Now (ni escritorio ni celular) ni su ícono. Si cambia el teléfono en Business Info, cambia en el header sin tocar nada.
42. **Botón principal:** sólido, con ícono de flecha a la derecha, relleno y radio con variables del kit. Se oculta en celular.

### F7. Megamenú: diseño libre, datos fijos
43. **El diseño del megamenú es libre.** Cada proyecto elige su propuesta: ícono y nombre, imagen y nombre, imagen con descripción, columnas, panel destacado u otra. No se repite la misma estructura en todos los sitios.
44. **Los datos son siempre estos y salen de los servicios publicados:** título, `get_permalink()`, `svc_icon`, imagen destacada (`get_the_post_thumbnail`), `svc_short_description`, ordenados por `menu_order` ASC y luego por título. Se excluyen los que tengan `svc_menu_show` apagado. Nunca se escribe un servicio a mano.
45. **Ícono:** se imprime solo si `svc_icon` tiene valor, con el prefijo de estilo del proyecto (Font Awesome Pro permitido, un solo estilo para todo el megamenú). Si el diseño usa ícono y el servicio no lo tiene, no se imprime nada en su lugar.
46. **Imagen:** si el diseño usa imagen, se imprime con `width`, `height`, `loading="lazy"` y `alt` con el título del servicio. Si el servicio no tiene imagen destacada, no se imprime la imagen ni su contenedor.
47. **Marca de imagen de prueba (solo editores):** si la imagen destacada es `image-test` y `current_user_can('edit_posts')`, el elemento lleva la clase `is-placeholder`, con un borde de `var(--cta)` y la etiqueta "Replace image". El visitante nunca la ve y la etiqueta no entra en el HTML público.
48. **Descripción:** se imprime solo si el diseño la usa y `svc_short_description` tiene valor, recortada a una o dos líneas.
49. **Áreas en el megamenú:** servicios a la izquierda y ciudades a la derecha (regla 31). La ciudad se saca del título del área o del término de `location`; nunca se escribe a mano.
50. **Informe de entrega obligatorio.** Junto con `header.html`, `header.css` y `header.js`, la IA entrega una tabla "Datos que usa este megamenú" que indica qué usa el diseño (ícono, imagen, descripción) y qué debe subir o revisar el equipo. Ejemplo: "Usa imagen destacada: reemplazar las imágenes de prueba. Usa ícono: revisar que cada ícono corresponda al servicio. No usa descripción."
51. **Sin huecos al quitar un servicio.** El megamenú calcula en PHP las columnas según cuántos servicios hay publicados (1, 2, 3, 4 o más), centra la última fila y ajusta el ancho del panel. Nunca columnas fijas con celdas vacías. Se prueba despublicando un servicio.
52. **Entrega por campos del Code de Bricks.** PHP y HTML en el campo "PHP & HTML" (solo con un `<style>` mínimo de variables calculadas por PHP), CSS en el campo CSS sin `<style>`, y JavaScript en el campo JS sin `<script>`. Tres archivos: `header.html`, `header.css`, `header.js`.
53. **Modo sólido o transparente:** el elemento Code no puede agregar clases al `<body>`. El modo se marca con `is-solid` o `is-transparent` en el `<header>` y los estilos de página usan `:has()`.

---

## PARTE G: Pruebas cuando exista el código

- [ ] Modo Solid: el Hero queda debajo del header, no se tapa ningún texto y en Home header y Hero suman 100vh
- [ ] Modo Transparent: el Hero queda detrás, su texto libre, y al primer scroll el header cambia fondo, colores y logo
- [ ] Recargar a mitad de página: el header ya tiene el estilo correcto, sin parpadeo
- [ ] Enlaces ancla: el título de destino no queda tapado
- [ ] Activar y desactivar el topbar: el Hero se ajusta solo, sin tocar código
- [ ] Con sesión de administrador (barra de WordPress visible): el header no queda tapado
- [ ] Megamenús Services y Service Areas: abren solo con JavaScript, cierran con retraso, clic fuera y Escape
- [ ] Link activo correcto en Home, un servicio, un área, el blog y Contact
- [ ] Celular: logo, botón de llamada (ícono y CALL NOW, sin número) y hamburguesa en una fila; el menú móvil incluye los datos del topbar si está activo
- [ ] Call Now en escritorio en las dos variantes (con número y compacto) y en celular siempre ícono con etiqueta, sin número
- [ ] Cambiar el primer teléfono en Business Info: el `href` y el número del header cambian solos
- [ ] Borrar el teléfono: el Call Now desaparece completo, sin ícono suelto
- [ ] Alcance `home`: las páginas interiores (ej. Contact) llevan el header sólido y se leen bien
- [ ] Alcance `all`: cada página con Hero (servicio, área, About, Blog, Contact) funciona con el header transparente; la 404 y la búsqueda quedan con header sólido
- [ ] Sin scroll horizontal en ningún ancho, incluido 320 px
- [ ] Despublicar un servicio: desaparece del menú
- [ ] Apagar `svc_menu_show` en un servicio: desaparece del menú y su página sigue abierta
- [ ] Cambiar el número de Orden de un servicio: el orden del megamenú cambia sin tocar código
- [ ] Cambiar `svc_icon` de un servicio: el ícono nuevo aparece; vaciarlo: no queda hueco
- [ ] Servicio con la imagen de prueba: con sesión de editor se ve la marca "Replace image"; sin sesión no se ve nada
- [ ] La entrega incluye la tabla "Datos que usa este megamenú"
- [ ] Limpiar el caché al guardar Header Settings: el cambio se ve de inmediato

---

## Lista de verificación final

- [ ] Las 5 variables nuevas creadas en Bricks y el JSON re-exportado
- [ ] Página de opciones `Header Settings` creada
- [ ] Campo `biz_logo_light` creado en Business Info
- [ ] Grupo `info header` con 7 pestañas y 29 campos con el nombre exacto
- [ ] Los selectores de color tienen las 10 opciones y su valor por defecto
- [ ] Lógica condicional puesta en `hdr_transparent_scope`, pestaña 3 y los campos del topbar
- [ ] Campo `svc_menu_show` creado en `info service` e instrucciones de `svc_icon` actualizadas
- [ ] Orden numérico de los servicios probado (cambiar el número cambia el menú)
- [ ] ACF exportado y backup de WPVivid actualizado

**Siguiente paso:** con esto creado, genero el código de ejemplo del header siguiendo la Parte F. Después, la guía del footer.

=== FIN DE Guia_Paso_a_Paso_Header_Settings_v3.1.md ===


=== Guia_Paso_a_Paso_Footer_Settings.md ===

(Fase 7: contrato del footer (Parte E, con los cambios del Contrato Corporativa))

# Guía paso a paso: Footer Settings (ACF PRO)

**Objetivo:** crear la página de opciones que controla el footer sin tocar código: colores, descripción corta, texto del copyright y el botón flotante (Free Estimate o Back to top). La **estructura del footer es fija** (4 columnas) y sus listas se llenan solas con lo que está publicado. El **diseño** de cada proyecto sigue siendo libre.
**Dónde se hace:** en el WordPress base, nunca en un proyecto de cliente.
**Requisitos previos:** Header Settings creado (el botón flotante reutiliza el enlace `hdr_btn_link`) y Business Info con sus campos.
**Reemplaza** la sección de footer de `Guia_Paso_a_Paso_Header_Footer_Settings.md`.

> **Regla de idioma:** todo lo que se escribe en WordPress va en inglés; las instrucciones de ayuda para el equipo quedan en español.
> **Prefijo:** `ftr_` para todos los campos del footer.

**Total: 3 pestañas y 16 campos.**

---

## Qué es fijo y qué se edita

| Parte | Cómo se maneja |
|---|---|
| Número de columnas y su orden | **Fijo en el código:** 4 columnas |
| Lista de servicios, áreas y enlaces de empresa | **Automático:** sale de lo publicado |
| Datos de contacto, horario, redes y licencia | **Automático:** salen de Business Info |
| Colores, descripción, copyright, botón flotante | **Campos de esta página** |
| Títulos de columna (Services, Contact...) | Los define la IA en el código de cada proyecto |
| Franja de llamada a la acción sobre el footer | No es campo: va como sección de Bricks o en el código del proyecto |

### Las 4 columnas (siempre en este orden)

| # | Columna | Contenido |
|---|---|---|
| 1 | **Brand** | Logo, descripción corta (`ftr_description`) y, según el diseño, las redes |
| 2 | **Services** | Lista de servicios publicados + enlace "View All". Debajo, los enlaces **Company** (About Us, Blog, Contact) |
| 3 | **Service Areas** | Lista de áreas publicadas + enlace "View All" |
| 4 | **Contact** | Teléfono, email, dirección, horario y, según el diseño, las redes |

Abajo, la barra del **copyright**, que SIEMPRE lleva los dos enlaces legales: **Privacy Policy** y **Terms of Use**. En tablet el footer pasa a 2 columnas y en celular a 1, en el mismo orden.

---

## PARTE A: Crear la página de opciones

### Paso 1. Footer Settings
**ACF → Páginas de opciones → Añadir nueva**

| Ajuste | Valor |
|---|---|
| Título de la página | `Footer Settings` |
| Slug del menú | `footer-settings` |
| Página superior | `Business info` (si aparece en el desplegable; si no, déjala como principal) |
| Capacidad | Por defecto (`edit_posts`) |

---

## PARTE B: Lista de colores del footer

Los 8 campos de color son **Selección** con estas 11 opciones (las 10 de siempre más `primary-d-8`, que sirve para la barra del copyright). Pégalas en **Opciones**, una por línea, con **Formato de retorno: Valor**:

```
primary : Primary
primary-d-5 : Primary dark
primary-d-8 : Primary darkest
primary-l-10 : Primary lightest
secondary : Secondary
secondary-l-10 : Secondary lightest
cta : CTA
cta-d-3 : CTA dark
color-text : Text gray
white : White
black : Black
```

El valor guardado es el nombre de la variable de Bricks. Para agregar otro color: la variable debe existir en Bricks y se añade una línea `nombre : Etiqueta` en las opciones del campo.

> **Contraste:** los valores por defecto asumen un footer oscuro. Si un proyecto usa un footer claro, hay que cambiar los colores de fondo, títulos, textos y copyright juntos, y elegir el logo normal.

---

## PARTE C: Crear el grupo de campos `info footer`

### Paso 2. Crear el grupo
**ACF → Grupos de campos → Añadir nuevo**. Título: `info footer`

| Ajuste | Valor |
|---|---|
| Reglas de ubicación | Página de opciones **es igual a** Footer Settings |
| Estilo | Estándar |
| Posición | Alta |
| Ubicación de la etiqueta | Alineada arriba |
| Ubicación de las instrucciones | Debajo de las etiquetas |

### Paso 3. Recordatorio
- Escribe primero la **Etiqueta** y luego corrige el **Nombre del campo**.
- Las instrucciones van en la pestaña **Presentación** del campo.
- La lógica condicional va en su propia pestaña.
- En el **Verdadero / Falso** no hay ninguno aquí. En las **Selección**, el **Formato de retorno** es **Valor**.
- Guarda cambios al terminar cada pestaña.

### Paso 4. Pestañas y campos (en este orden)

**Pestaña `1. Colors`**
| Label | Nombre | Tipo | Ajustes | Instrucciones (pegar) |
|---|---|---|---|---|
| Footer background | `ftr_bg_color` | Selección | Lista de colores · Defecto `primary-d-5` | Color de fondo del footer. |
| Titles color | `ftr_title_color` | Selección | Lista de colores · Defecto `white` | Color de los títulos de las columnas. |
| Text and links color | `ftr_text_color` | Selección | Lista de colores · Defecto `primary-l-10` | Color de los textos, de los enlaces en reposo y de los íconos del footer. |
| Link hover color | `ftr_link_hover_color` | Selección | Lista de colores · Defecto `secondary` | Color al que cambian los enlaces y los íconos de redes cuando el mouse pasa por encima. |
| Copyright background | `ftr_copy_bg` | Selección | Lista de colores · Defecto `primary-d-8` | Color de fondo de la barra del copyright. |
| Copyright text color | `ftr_copy_color` | Selección | Lista de colores · Defecto `primary-l-10` | Color del texto del copyright. |

**Pestaña `2. Content`**
| Label | Nombre | Tipo | Ajustes | Instrucciones (pegar) |
|---|---|---|---|---|
| Short description | `ftr_description` | Área de texto | 3 filas | Frase corta bajo el logo, de 2 a 4 líneas. Si está vacía no se muestra. |
| Footer logo | `ftr_logo_version` | Selección | `light : Light logo` y `normal : Normal logo` · Defecto `light` | Light: usa el logo claro de Business Info, para footer oscuro. Normal: usa el logo principal, para footer claro. Si falta el logo claro se usa el normal. |
| Logo height desktop | `ftr_logo_height_desktop` | Número | Mín. **24** · Máx. **120** · Defecto **56** · Añadido: `px` | Altura del logo en el footer en computador. El ancho se ajusta solo. |
| Logo height mobile | `ftr_logo_height_mobile` | Número | Mín. **20** · Máx. **90** · Defecto **44** · Añadido: `px` | Altura del logo en el footer en celular y tablet (1023 px o menos). El ancho se ajusta solo. |
| Copyright text | `ftr_copyright` | Texto | Defecto `© {year} {company}. All Rights Reserved.` | Escribe `{year}` para el año (se actualiza solo) y `{company}` para el nombre de la empresa de Business Info. |
| Max items per list | `ftr_list_limit` | Número | Mín. **4** · Máx. **12** · Defecto **8** | Cuántos servicios y cuántas áreas se muestran en el footer. |

**Pestaña `3. Floating button`**
| Label | Nombre | Tipo | Ajustes | Instrucciones (pegar) |
|---|---|---|---|---|
| Floating button | `ftr_float_mode` | Selección | `estimate : Free Estimate button` · `top : Back to top button` · `none : None` · Defecto `estimate` | Free Estimate: botón fijo en pantalla que lleva al formulario o contacto (usa el enlace del botón del header). Back to top: botón que sube al inicio de la página. None: ninguno de los dos. |
| Free Estimate text | `ftr_float_text` | Texto | Defecto `Free Estimate` · Condicional: `ftr_float_mode` es igual a `estimate` | Texto del botón fijo. Debe ser corto. |
| Button background | `ftr_float_bg` | Selección | Lista de colores · Defecto `cta` · Condicional: `ftr_float_mode` es distinto de `none` | Color de fondo del botón flotante. |
| Button text and icon | `ftr_float_color` | Selección | Lista de colores · Defecto `white` · Condicional: igual que arriba | Color del texto o del ícono del botón flotante. |

### Paso 5. Guardar y revisar nombres
**Guardar cambios** y compara la columna **Nombre** con las tablas, letra por letra. Después abre **Footer Settings** y pulsa **Guardar**, para que se graben los valores por defecto.

### Paso 6. Un ajuste en `Info service`
El campo `svc_menu_show` ahora oculta el servicio del **menú y del footer**. Cambia su instrucción por:

> Apágalo para ocultar este servicio del menú y del footer sin despublicarlo. La página sigue existiendo.

---

## PARTE D: Datos de Business Info que usa el footer

| Dato | Campo | Se muestra si... |
|---|---|---|
| Logo | `biz_logo` y `biz_logo_light` | Existe el logo elegido |
| Nombre de la empresa | `biz_company_name` | Existe (para alt del logo y el copyright) |
| Teléfono | Primera fila de `biz_phones` | Hay teléfono |
| Email | `biz_email` | Hay email |
| Dirección | `biz_address` | Hay dirección |
| Horario | `biz_hours` | `biz_has_hours` está activo y hay texto |
| Redes | `biz_facebook_url`, `biz_instagram_url`, `biz_tiktok_url`, `biz_youtube_url` | Cada una, solo si tiene URL |
| Licencia | `biz_license_number` y `biz_license_logos` | `biz_has_license` activo y hay datos. El diseño decide si los muestra |

---

## PARTE E: Contrato para la IA (footer)

Estas reglas se pegan en el prompt maestro. Son obligatorias. Además aplican al footer las reglas del header sobre variables de Bricks (3), valores de respaldo (4), validación de colores (5), escapado (6), prohibiciones de texto (8), sin regex con backslash (9) y sin `!important` (17).

### E1. Datos y estilos
1. **Solo estos campos:** los de `info footer`, los `biz_` de la Parte D y `hdr_btn_link` (para el enlace del botón flotante). Nunca se inventan campos.
2. **Todo con variables de Bricks.** Colores, `--text-*`, `--space-*`, `--radius-*`, `--shadow-*`, `--transition`, `--z-*` y `--container-*`. Nada de hex, `px`, `rem` ni `z-index` sueltos. Excepción: `50%` para círculos.
3. **Colores validados contra las opciones del propio campo** (`get_field_object(...)['choices']`). Un valor desconocido usa el valor por defecto.
4. **Todo es condicional por dato vacío, ícono incluido.** Sin teléfono no hay fila de teléfono ni su ícono; una red sin URL no se muestra; no se imprimen contenedores vacíos.
5. **Los títulos de columna no son encabezados.** Sin H1 a H6 en el footer. Usar `<p>` con la clase del título.

### E2. Estructura
6. **El bloque BEM se llama `.site-footer`**, nunca `.footer`. Elementos: `.site-footer__top`, `.site-footer__col`, `.site-footer__brand`, `.site-footer__title`, `.site-footer__list`, `.site-footer__link`, `.site-footer__contact`, `.site-footer__social`, `.site-footer__bottom`, `.site-footer__copy`.
7. **Cuatro columnas fijas, en este orden:** Brand, Services (con Company debajo), Service Areas y Contact. En escritorio, 4 columnas. En tablet (1023 px o menos), 2. En celular (767 px o menos), 1, en el mismo orden. **No se oculta ninguna columna en celular.**
8. **El diseño es libre** dentro de esa estructura: tipografía, separadores, íconos, subcolumnas (por ejemplo, las áreas en dos subcolumnas), forma de las redes, dónde van las redes (en Brand o en Contact, nunca en ambas).
9. **El footer va en el flujo normal de la página** (nunca `fixed` ni `sticky`), siempre al final, con ancho `var(--container-max)` y margen lateral `var(--container-padding)`.
10. **Entrega:** `footer.html` (con el PHP embebido), `footer.css` y `footer.js`, siempre como archivos. El HTML va en **un solo elemento Code de Bricks en modo PHP**. Sin archivo `.php` aparte ni plugin de snippets.
11. **Íconos:** Font Awesome Pro, con el mismo estilo que el header, y `fa-brands` para las redes. El kit se carga una sola vez en Bricks, nunca en el HTML.
12. **Sin scroll horizontal** en ningún ancho, incluido 320 px.
13. **Hover de los enlaces del footer:** los enlaces y los íconos de redes cambian de `ftr_text_color` a `ftr_link_hover_color`, con `transition` solo en `color` y `background-color`. A diferencia del header, aquí el hover sí cambia el color. Los textos que no son enlaces (descripción, dirección, horario) no cambian.

### E2b. Sin huecos y entrega por campos
- **Columnas sin hueco:** si una columna no tiene datos (sin áreas, sin contacto), no se imprime y las demás se reparten el ancho; el número de columnas se calcula en PHP. Las listas deben verse bien con 1 a 20 elementos.
- **Entrega por campos del Code de Bricks:** PHP y HTML en su campo, CSS en el campo CSS sin `<style>` y JavaScript en el campo JS sin `<script>`. Tres archivos: `footer.html`, `footer.css`, `footer.js`.

### E3. Listas dinámicas
14. **Services:** contenido `service` publicado, ordenado por `menu_order` ASC y luego por título, sin los que tengan `svc_menu_show` apagado, hasta `ftr_list_limit`. Enlaces con `get_permalink()`; el texto es el título.
15. **Service Areas:** contenido `area` publicado, ordenado por `menu_order` ASC y luego por título, hasta `ftr_list_limit`. El texto es el título de la página (por ejemplo "Drywall Services in Hyannis, MA").
16. **"View All":** el enlace aparece solo si existe y está publicada una página con el slug `services` (para Services) o `service-areas` (para Service Areas), buscada con `get_page_by_path()`. El texto es siempre `View All`.
17. **Company:** About Us, Blog y Contact, buscados por los slugs `about-us`, `blog` y `contact`. Cada enlace aparece solo si su página está publicada.
18. **Contacto:** el teléfono y su `href` se arman igual que en el header (primer teléfono de `biz_phones`, `tel:+1...`, texto `(555) 123-4567`). Email con `mailto:`. Dirección y horario con saltos de línea convertidos a `<br>`. Redes solo si tienen URL, con `target="_blank"` y `rel="noopener"`.
19. **Licencia:** si `biz_has_license` está activo, el diseño puede mostrar `biz_license_number` (por ejemplo "License #12345") y `biz_license_logos`. Sin datos no se imprime nada.
20. **Nada de enlaces inventados:** nada de `href="#"`, y no se escribe ningún servicio, área ni página a mano.

### E4. Contenido editable
21. **Logo:** con `ftr_logo_version` en `light` usa `biz_logo_light`, y si está vacío, `biz_logo`. Con `normal` usa `biz_logo`. `<img>` con `width` y `height` reales, altura visible de `ftr_logo_height_desktop` (más de 1023 px) y `ftr_logo_height_mobile` (1023 px o menos) con `width: auto`, `loading="lazy"` y `alt` con el nombre de la empresa.
22. **Descripción:** `ftr_description` con `esc_html` y `nl2br`. Si está vacía no se imprime nada.
23. **Copyright:** reemplaza `{year}` por el año actual (`wp_date('Y')`) y `{company}` por `biz_company_name`, con `esc_html`. **Sin créditos de agencia y sin dominio visible.**
24. **Enlaces legales (obligatorios):** en la barra del copyright van siempre "Privacy Policy" y "Terms of Use", junto al texto del copyright. Cada enlace busca su página por slug con `get_page_by_path( 'privacy-policy' )` y `get_page_by_path( 'terms-of-use' )`, solo si está publicada, y usa `get_permalink()`. Si una página no existe o no está publicada, ese enlace no se imprime (nunca `href="#"`). Mismo estilo de enlace que el resto del footer, con hover y foco visible, y en celular se acomodan debajo del copyright sin romper el ancho.
25. **Prohibido** guion largo (—) y la palabra "across" en cualquier texto.

### E5. Botón flotante
26. **`ftr_float_mode` = `estimate`:** botón fijo en pantalla con el texto de `ftr_float_text` y el enlace `hdr_btn_link` (con su `target`). Si el enlace o el texto están vacíos, no se imprime. El diseño decide su posición (pestaña vertical al costado, esquina inferior o barra inferior en celular), sin tapar el contenido principal ni el Call Now del header.
27. **`ftr_float_mode` = `top`:** botón de ícono con `aria-label="Back to top"`. Aparece cuando `scrollY > 400`, con `requestAnimationFrame` y listener `passive`. Sube con scroll suave, excepto si el usuario tiene `prefers-reduced-motion`.
28. **`ftr_float_mode` = `none`:** no se imprime ningún botón flotante.
29. **El botón flotante va fuera del `<footer>`**, como elemento aparte, con `position: fixed` y `z-index: var(--z-dropdown)`. Colores de `ftr_float_bg` y `ftr_float_color`.
30. **Sin parpadeo:** el botón Free Estimate está visible desde el HTML, sin depender de JavaScript. El Back to top nace oculto con una clase y la clase se alterna al hacer scroll.
31. **Accesibilidad:** foco visible, área táctil mínima de 44 px y contraste suficiente entre fondo y texto.
32. **Espacio en celular:** si el botón flotante es una barra inferior, el footer lleva el `padding-bottom` necesario para que no tape el copyright.

---

## PARTE F: Pruebas cuando exista el código

- [ ] El footer tiene 4 columnas en escritorio, 2 en tablet y 1 en celular, en el mismo orden
- [ ] Publicar un servicio nuevo: aparece en la columna Services
- [ ] Despublicar un servicio, o apagar `svc_menu_show`: desaparece del footer
- [ ] Cambiar el Orden de un servicio: cambia el orden en el footer
- [ ] Más áreas que `ftr_list_limit`: solo se muestran las primeras, y aparece "View All" si existe la página
- [ ] Borrar el email o una red social en Business Info: desaparece su fila, sin ícono suelto
- [ ] Cambiar el teléfono en Business Info: cambian el texto y el enlace `tel:`
- [ ] La barra del copyright muestra Privacy Policy y Terms of Use y ambos enlaces abren su página
- [ ] Despublicar una página legal: su enlace desaparece del footer, sin enlace roto
- [ ] Cambiar `ftr_description` y `ftr_copyright`: el footer se actualiza; `{year}` y `{company}` se reemplazan
- [ ] Cambiar los 6 colores de la pestaña 1: el footer los refleja y el texto sigue legible
- [ ] Pasar el mouse por un enlace y por una red social: cambian al color de hover; la descripción y la dirección no cambian
- [ ] Modo `estimate`: botón fijo visible, con el enlace de `hdr_btn_link`
- [ ] Modo `top`: aparece tras el scroll, sube suave y tiene `aria-label`
- [ ] Modo `none`: no hay botón flotante en el HTML
- [ ] Sin scroll horizontal en ningún ancho, incluido 320 px
- [ ] Limpiar el caché al guardar Footer Settings: el cambio se ve de inmediato

---

## Lista de verificación final

- [ ] Página de opciones `Footer Settings` creada
- [ ] Grupo `info footer` con 3 pestañas y 16 campos con el nombre exacto
- [ ] Los 8 selectores de color tienen las 11 opciones y su valor por defecto
- [ ] Lógica condicional puesta en `ftr_float_text`, `ftr_float_bg` y `ftr_float_color`
- [ ] Instrucción de `svc_menu_show` actualizada
- [ ] Footer Settings guardado una vez
- [ ] ACF exportado y backup de WPVivid actualizado

**Siguiente paso:** con esto creado, genero el código de ejemplo del footer siguiendo la Parte E.

=== FIN DE Guia_Paso_a_Paso_Footer_Settings.md ===


=== Contrato_Formularios.md ===

(Fase 8: contrato de formularios)

# Contrato del formulario global

**Decisión:** el sitio tiene **un solo formulario**, global y con código, generado por el **Snippet 5** (`Snippet_5_biz_form.php`) con el shortcode `[biz_form]`. El mismo formulario se usa en Contact, en el Hero y en las llamadas finales. El equipo no escribe código de formulario: solo coloca el shortcode. El diseño es propio de cada proyecto: la IA genera el CSS del formulario a partir de `Formularios_CSS_Base.css` y de la Ficha de componentes. Los leads van solo por correo al cliente, al correo de **Business Info** (`biz_email`). No hay consentimiento de SMS ni página de gracias: al enviar aparece una notificación arriba de la pantalla (verde si se envió, roja si hubo error) con barra de progreso de 5 segundos, y desaparece sola.
**Qué hace la IA (en las fases de páginas y plantillas, sin fase aparte):** (1) decide dónde va el formulario según la plantilla y los PDFs, coloca el shortcode y entrega el texto de apoyo de cada sección; (2) en la PRIMERA sección del proyecto que lleve formulario, incluye dentro de su CSS un bloque de CSS del formulario adaptado al diseño del proyecto; las demás secciones no lo repiten y la Ficha registra en qué fase se entregó. **No escribe HTML ni JS de formularios.**
**Qué cambia en cada proyecto:** nada en el snippet. Cambia el CSS del formulario (solo valores visuales). Una línea en `wp-config.php` fija el estilo de íconos (ver sección 4).

---

## 1. El formulario (estándar americano)

| Campo | Tipo | Obligatorio | Ejemplo que muestra |
|---|---|---|---|
| Full Name | Texto | Sí | John Smith |
| Phone | Teléfono, se escribe con formato `(555) 555-5555` automáticamente | Sí | (508) 000-0000, con el código de área del cliente |
| Email | Email | Sí | you@email.com |
| ZIP Code | 5 dígitos (o ZIP+4) | Sí | El ZIP de la dirección del cliente |
| Service Needed | Desplegable con los servicios publicados | Sí | Select a service |
| Message | Texto largo | No | Project Description |
| Best Time to Call | Pastillas: Morning o Afternoon | No | |
| Botón | | | Send Message (cambiable con `button`) |

Mensaje de éxito: *Thank you! Your message was sent. We will contact you shortly.*
Mensaje de error: *Something went wrong. Please try again or call us at {phone}.* (el teléfono sale de Business Info).

## 2. Cómo se usa

```
<?php echo do_shortcode( '[biz_form]' ); ?>
<?php echo do_shortcode( '[biz_form button="Get Free Estimate"]' ); ?>
```

El título y el texto de apoyo de la sección (por ejemplo "Send Us a Message") van en el bloque de textos de esa sección, no dentro del formulario. Se puede usar varias veces en una página.

## 3. Qué hace el snippet solo

1. **Correo:** llega a `biz_email`. Si el campo está vacío, llega al correo del administrador. Responder al visitante es posible con el email que escribió.
2. **Servicios:** el desplegable se llena con los servicios publicados y visibles. Un servicio retirado o en borrador desaparece solo.
3. **Anti-spam:** campo trampa, tiempo mínimo de llenado, límite de 5 envíos cada 10 minutos por visitante y, opcionalmente, Cloudflare Turnstile.
4. **Validación:** nombre, teléfono de 10 dígitos, email, ZIP de 5 dígitos y servicio válido. Todo se limpia antes de enviarse.
5. **Mensaje:** éxito o error en la misma página, sin redirección.
6. **JS:** un script mínimo, solo en las páginas que tienen el formulario.

## 4. Configuración por sitio (una vez)

| Qué | Cómo |
|---|---|
| Snippets | Instalar el Snippet 1 y el Snippet 5 en Code Snippets |
| CSS | Pegar el bloque de CSS del formulario que entrega la IA al final del CSS global de Bricks del proyecto (una sola vez; no pegar el archivo base). Variables que debe tener el CSS global (opcionales con valor por defecto: `--success`, `--error`, `--radius-pill`, `--z-modal`): `--white`, `--color-text`, `--title`, `--primary`, `--cta`, `--cta-d-1`, `--cta-d-2`, `--radius-m/l/pill`, `--shadow-m`, `--space-*`, `--text-*`, `--transition` |
| Íconos | En `wp-config.php`: `define( 'BIZ_FA_STYLE', 'fa-regular' );` con el prefijo de estilo que dice la Ficha del proyecto (por ejemplo `fa-sharp fa-light`). Sin esta línea, usa `fa-regular` |
| Correo | Plugin SMTP configurado con una cuenta del dominio, para que no caiga en spam |
| Turnstile (opcional) | En `wp-config.php`: `define( 'BIZ_TURNSTILE_SITE', '...' );` y `define( 'BIZ_TURNSTILE_SECRET', '...' );` |
| `biz_email` | Llenado en Business Info |

## 5. CSS del formulario por proyecto (reglas)

1. El CSS del formulario es propio de cada proyecto. La IA lo genera partiendo de `Formularios_CSS_Base.css` y lo entrega como un solo bloque, que se pega una vez al final del CSS global de Bricks del proyecto.
2. Se conservan los nombres de clase y la estructura: `.biz-form`, `.biz-form__grid`, `.biz-form__field`, `.biz-form__control` (el ícono va DENTRO de esta caja y el input va sin borde ni fondo), `.biz-form__chip`, `.biz-form__btn` y `.biz-toast`. Lo que se adapta son los valores visuales: radios, colores, fondos, bordes, sombras, tamaños y espacios.
3. Solo variables globales de Bricks que ya existen. Prohibido `:root` y variables nuevas. Los fondos y bordes neutros de los campos se derivan de `--color-text` y `--white`, no de las rampas de color.
4. El botón es idéntico al botón principal de la Ficha de componentes. Los radios de campos, chips y notificación siguen el radio de la Ficha.
5. Se mantiene: dos columnas en escritorio y una en celular, foco visible en todos los campos, alto táctil mínimo de 4,4 rem, notificación arriba con barra de progreso de 5 segundos (verde si se envió, roja si hubo error).
6. La entrega incluye una línea por cada decisión visual tomada (qué variable usa el fondo del campo, el borde, el botón y la notificación), para que el revisor pueda comprobarlo.

## 6. Pruebas obligatorias

| Prueba | Resultado esperado |
|---|---|
| Enviar el formulario completo | Llega un correo al cliente con nombre, teléfono, email, ZIP, servicio, horario, mensaje y la página de origen; se ve el mensaje de éxito |
| Escribir el teléfono | Se da formato `(555) 555-5555` solo |
| ZIP de 4 dígitos | Muestra "Please enter a valid 5-digit ZIP code." |
| Llenar el campo trampa | No llega ningún correo (el bot ve un "enviado" falso) |
| Enviar 6 veces seguidas | A partir del sexto, mensaje de error |
| Cambiar `biz_email` | El siguiente envío llega al correo nuevo |
| Retirar un servicio con el wizard | El desplegable ya no lo muestra |
| El correo no cae en spam | Revisar la bandeja de entrada y el spam en Gmail y Outlook |
| 320, 768 y 1280 px | Sin scroll horizontal; una columna en celular |
| Estilo | El botón es igual al de las demás secciones; los íconos usan el estilo del proyecto |

=== FIN DE Contrato_Formularios.md ===


=== Formularios_CSS_Base.css ===

(Primera seccion con formulario: base del CSS del formulario)

```css
/* BASE del CSS del formulario (Snippet 5). NO se pega tal cual en los proyectos.
   Es la referencia que usa la IA en las fases de páginas para generar el CSS propio de cada proyecto.
   Se conservan SIEMPRE los nombres de clase y la mecanica (icono dentro de .biz-form__control,
   notificacion .biz-toast, rejilla de 2 columnas, chips). Lo que cambia por proyecto son solo
   los valores visuales: radios, colores, fondos, bordes, sombras, tamanos y el boton, segun
   la Ficha de componentes y el diseno del proyecto.
   Solo variables globales que ya existen. Nunca :root ni variables nuevas. */

.biz-form {
	position: relative;
	display: flex;
	flex-direction: column;
	gap: var(--space-m);
	box-sizing: border-box;
	width: 100%;
	padding: var(--space-l);
	background-color: var(--white);
	border-radius: var(--radius-l);
	box-shadow: var(--shadow-m);
}

.biz-form__grid {
	display: grid;
	grid-template-columns: 1fr;
	gap: var(--space-m);
}

.biz-form__field {
	display: flex;
	flex-direction: column;
	gap: var(--space-2xs);
	min-width: 0;
	margin: 0;
	padding: 0;
	border: 0;
}

.biz-form label,
.biz-form legend {
	padding: 0;
	font-size: var(--text-xs);
	font-weight: 600;
	letter-spacing: 0.04em;
	text-transform: uppercase;
	color: var(--color-text);
}

/* Campo con icono: el contenedor ES la caja visual, el icono vive dentro */
.biz-form__control {
	display: flex;
	align-items: center;
	gap: var(--space-xs);
	box-sizing: border-box;
	width: 100%;
	min-height: 4.8rem;
	padding: 0 var(--space-s);
	background-color: color-mix(in srgb, var(--color-text) 5%, var(--white));
	border: 1px solid color-mix(in srgb, var(--color-text) 14%, var(--white));
	border-radius: var(--radius-m);
	transition: border-color var(--transition), box-shadow var(--transition), background-color var(--transition);
}

.biz-form__control:focus-within {
	background-color: var(--white);
	border-color: var(--primary);
	box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 20%, var(--white));
}

.biz-form__control > i {
	flex: 0 0 auto;
	margin: 0;
	font-size: var(--text-m);
	line-height: 1;
	color: var(--primary);
	pointer-events: none;
}

.biz-form__control--area {
	align-items: flex-start;
	padding-top: var(--space-s);
}

/* Flecha del desplegable, a la derecha */
.biz-form__control--select > i:last-child {
	font-size: var(--text-s);
	color: var(--color-text);
}

/* Los campos van sin caja propia: la caja es el contenedor */
.biz-form__control input:not([type="hidden"]):not([type="radio"]),
.biz-form__control select,
.biz-form__control textarea {
	flex: 1 1 auto;
	box-sizing: border-box;
	width: 100%;
	min-width: 0;
	min-height: 4.6rem;
	margin: 0;
	padding: 0;
	font: inherit;
	font-size: var(--text-m);
	color: var(--title);
	background: transparent;
	border: 0;
	border-radius: 0;
	box-shadow: none;
	outline: none;
}

.biz-form__control select {
	appearance: none;
	cursor: pointer;
}

.biz-form__control textarea {
	min-height: 11rem;
	padding-top: 0;
	resize: vertical;
}

.biz-form__control input::placeholder,
.biz-form__control textarea::placeholder {
	color: var(--color-text);
	opacity: 0.55;
}

/* Horario preferido: pastillas */
.biz-form__chips {
	flex-direction: row;
	flex-wrap: wrap;
	align-items: center;
	gap: var(--space-xs);
}

.biz-form__chips legend {
	width: 100%;
	margin-bottom: var(--space-2xs);
}

.biz-form .biz-form__chip {
	position: relative;
	display: inline-flex;
	text-transform: none;
	letter-spacing: 0;
	font-size: var(--text-s);
	font-weight: 500;
	cursor: pointer;
}

.biz-form .biz-form__chip input {
	position: absolute;
	opacity: 0;
	width: 1px;
	height: 1px;
}

.biz-form__chip span {
	display: inline-flex;
	align-items: center;
	gap: var(--space-xs);
	min-height: 4.4rem;
	padding: 0 var(--space-m);
	color: var(--title);
	background-color: color-mix(in srgb, var(--color-text) 5%, var(--white));
	border: 1px solid color-mix(in srgb, var(--color-text) 14%, var(--white));
	border-radius: var(--radius-pill, 999px);
	transition: background-color var(--transition), border-color var(--transition), color var(--transition);
}

.biz-form__chip input:checked + span {
	color: var(--white);
	background-color: var(--primary);
	border-color: var(--primary);
}

.biz-form__chip input:focus-visible + span {
	box-shadow: 0 0 0 3px color-mix(in srgb, var(--primary) 20%, var(--white));
}

/* Boton: identico al boton principal del sitio */
.biz-form__btn {
	display: inline-flex;
	align-items: center;
	justify-content: center;
	gap: var(--space-xs);
	box-sizing: border-box;
	width: 100%;
	min-height: 5.2rem;
	padding: var(--space-xs) var(--space-l);
	font: inherit;
	font-size: var(--text-m);
	font-weight: 700;
	color: var(--white);
	background-color: var(--cta);
	border: 0;
	border-radius: var(--radius-m);
	cursor: pointer;
	transition: background-color var(--transition), opacity var(--transition);
}

.biz-form__btn:hover,
.biz-form__btn:focus-visible {
	background-color: var(--cta-d-1);
}

.biz-form__btn:disabled {
	opacity: 0.6;
	cursor: wait;
}

/* Notificacion: aparece arriba, barra de progreso de 5 s y desaparece sola */
.biz-toast {
	position: fixed;
	top: var(--space-m);
	left: 50%;
	z-index: var(--z-modal, 9999);
	box-sizing: border-box;
	width: min(92vw, 44rem);
	overflow: hidden;
	padding: var(--space-s) var(--space-m);
	font-size: var(--text-m);
	font-weight: 600;
	line-height: 1.4;
	border: 1px solid;
	border-radius: var(--radius-m);
	box-shadow: var(--shadow-m);
	cursor: pointer;
	opacity: 0;
	transform: translate(-50%, -2rem);
	transition: opacity 0.35s ease, transform 0.35s ease;
}

.biz-toast.is-visible {
	opacity: 1;
	transform: translate(-50%, 0);
}

.biz-toast.is-success {
	color: color-mix(in srgb, var(--success, #1e8e3e) 75%, #000);
	background-color: color-mix(in srgb, var(--success, #1e8e3e) 12%, var(--white));
	border-color: var(--success, #1e8e3e);
}

.biz-toast.is-error {
	color: color-mix(in srgb, var(--error, #c62828) 80%, #000);
	background-color: color-mix(in srgb, var(--error, #c62828) 10%, var(--white));
	border-color: var(--error, #c62828);
}

.biz-toast__bar {
	position: absolute;
	left: 0;
	bottom: 0;
	width: 100%;
	height: 4px;
	background-color: currentColor;
	opacity: 0.55;
	transform-origin: left center;
	animation: biz-toast-bar 5s linear forwards;
}

.biz-toast.is-visible .biz-toast__bar {
	animation-play-state: running;
}

@keyframes biz-toast-bar {
	from { transform: scaleX(1); }
	to { transform: scaleX(0); }
}

@media (prefers-reduced-motion: reduce) {
	.biz-toast {
		transition: none;
	}
}

/* Campo trampa anti-bots: fuera de pantalla, nunca display:none */
.biz-form__trap {
	position: absolute;
	left: -9999px;
	width: 1px;
	height: 1px;
	overflow: hidden;
}

@media (min-width: 768px) {
	.biz-form__grid {
		grid-template-columns: 1fr 1fr;
	}

	.biz-form__field:not(.biz-form__field--half) {
		grid-column: 1 / -1;
	}
}
```

=== FIN DE Formularios_CSS_Base.css ===


=== Contrato_Paginas_Home_About_Contact.md ===

(Fase 8: contrato de paginas (estructura y bloque de textos; las secciones salen del Contrato Corporativa))

# Contrato de páginas: Home, About y Contact (con código, sin ACF)

**Decisión cerrada:** Home, About y Contact se construyen con código generado por la IA, **sin campos ACF propios**. Los datos críticos salen siempre de Business Info (ver `Regla_Datos_Dinamicos_Obligatoria.md`) y los contenidos dinámicos (servicios, áreas, entradas) salen de su contenido publicado.
**Para qué sirve este contrato:** que ese código sea fácil de editar y no se rompa cuando lo toque alguien del equipo.
**Cuándo se pega:** en el prompt maestro, en la fase de páginas.

---

## 1. Qué es cada parte de estas páginas

| Parte | Cómo se hace | Cómo se edita |
|---|---|---|
| Secciones creativas (Hero, About, Por qué elegirnos) | Código de la IA, diseño libre por proyecto | El bloque de textos al inicio del código (sección 3) |
| Servicios, áreas, entradas | Dinámicos, con su contrato actual (consultas PHP con `WP_Query` y `get_field()`) | Se editan en el propio servicio, área o entrada |
| Testimonios | Shortcode de Trustindex en un elemento Code (`<?php echo do_shortcode( '[trustindex no-registration=google]' ); ?>`). El plugin se instala por proyecto y se conecta al perfil de Google del cliente | No se edita en la página: se gestiona en el plugin Trustindex |
| Formulario (único, global) | Shortcode del Snippet 5: `[biz_form]`. Detalle en `Contrato_Formularios.md` | El correo de destino sale de Business Info; el estilo, del CSS del formulario del proyecto (va en la primera sección del proyecto que lleva formulario) |
| Datos del negocio (teléfono, email, dirección, años...) | Siempre de Business Info | Se edita en Business Info y cambia en todo el sitio |

---

## 2. Reglas de estructura

1. **Una sección = un elemento Code de Bricks en modo PHP**, con su propio CSS. Así el equipo puede mover, duplicar o reemplazar una sección sin afectar a las demás.
2. **Cada sección lleva su propia clase raíz** con el patrón `.sec-nombre` (por ejemplo `.sec-hero`, `.sec-about`, `.sec-contact`). Los elementos internos usan BEM (`.sec-hero__title`).
3. **Un solo H1 por página**, dentro del Hero. Las demás secciones usan H2, y los subtítulos H3. Nunca se saltan niveles.
4. **Variables de Bricks en todo el CSS.** Colores, `--text-*`, `--space-*`, `--radius-*`, `--shadow-*`, `--transition`, `--container-max` y `--container-padding`. Nada de hex, `px`, `rem` ni `z-index` sueltos.
5. **Puntos de corte fijos:** celular hasta 767 px, tablet hasta 1023 px, escritorio desde 1024 px.
6. **Sin scroll horizontal** en ningún ancho, incluido 320 px.
7. **Compatibilidad con el header:** el Hero respeta las reglas 22 a 24 del contrato del header (el espacio superior depende del modo sólido o transparente y de `--hdr-h`).
8. **Enlaces:** a servicios, áreas, entradas y páginas con `get_permalink()`. El enlace de contacto se obtiene buscando la página con el slug `contact` (`get_page_by_path('contact')`). Si no existe, el botón no se imprime. Nunca `href="#"`.
9. **Prohibido en cualquier texto:** guion largo (—), la palabra "across", el dominio del sitio como texto y datos inventados.

---

## 2b. Diseño libre y reglas del Hero

El contrato fija estructura, datos y calidad técnica. **El aspecto es libre y creativo en cada proyecto**: ningún ejemplo es un modelo, no hay patrón de diseño que repetir entre proyectos y se sigue la plantilla elegida con los colores, tipografía e íconos del proyecto.

17. **Sin estadísticas en el Hero.** Nada de contadores ni números destacados.
18. **Las insignias o sellos de confianza no parecen botones.** Son `<span>` o `<li>`, sin hover ni cursor pointer, con radio máximo `var(--radius-m)` y sin combinar relleno sólido, borde y sombra. Solo un `<a>` o `<button>` real puede verse como botón.
19. **Un dato aparece una sola vez por página.** No se repiten años, garantías ni cifras entre el Hero, una barra de confianza y otras secciones.
20. **Los números salen de Business Info** (`{years}`), nunca inventados.
21. **Listas dinámicas sin huecos.** Servicios, áreas y entradas deben verse bien con 1, 2, 3, 4 y 20 elementos: columnas calculadas en PHP según la cantidad, última fila centrada, sin celdas vacías. Se prueba quitando un elemento.
22. **Entrega por campos del Code de Bricks:** PHP y HTML en su campo, CSS en el campo CSS (sin `<style>`) y JavaScript en el campo JS (sin `<script>`).

---

## 3. Regla de edición fácil: el bloque de textos al inicio

Cada sección empieza con **un bloque donde están todos los textos y las imágenes editables**. Debajo, el HTML solo los imprime. Para cambiar un texto se toca **una línea** de ese bloque.

```php
<?php
// ===== TEXTOS EDITABLES: cambiar solo aqui =====
$t = [
  'badge'    => 'Free Estimates',
  'title'    => 'Roofing You Can Trust {in_areas}',
  'text'     => '{company} has served homeowners for {years} years. Call {phone} for a free estimate.',
  'cta_text' => 'Get Free Estimate',
];
$i = [
  'image' => '2026/10/hero.webp',
];
// ===== NO EDITAR DEBAJO DE ESTA LINEA =====

$contact    = get_page_by_path( 'contact' );
$contact_url = $contact ? get_permalink( $contact ) : '';
$upload     = wp_get_upload_dir();
$image_url  = $upload['baseurl'] . '/' . $i['image'];
?>
<section class="sec-hero">
  <p class="sec-hero__badge"><?php echo esc_html( $t['badge'] ); ?></p>
  <h1 class="sec-hero__title"><?php echo esc_html( biz_tokens( $t['title'] ) ); ?></h1>
  <p class="sec-hero__text"><?php echo esc_html( biz_tokens( $t['text'] ) ); ?></p>
  <?php if ( $contact_url ) : ?>
    <a class="sec-hero__cta" href="<?php echo esc_url( $contact_url ); ?>"><?php echo esc_html( $t['cta_text'] ); ?></a>
  <?php endif; ?>
</section>
```

> `biz_tokens()` ya existe: vive en el snippet **Biz Tokens** del plugin Code Snippets (`Snippet_1_biz_tokens.php`), viaja con el Kit Base y reemplaza `{years}`, `{company}`, `{phone}`, `{areas}` y los demás marcadores. Funciones de apoyo del mismo snippet: `biz_phone_href()` para el enlace `tel:`, `biz_whatsapp_href()`, `biz_areas_list()` y `biz_phone_display()`.
>
> **Regla de etiquetas PHP (probada):** en el elemento Code de Bricks (campo "PHP & HTML", con "Execute code" activo y el código firmado) el PHP **lleva** `<?php ... ?>`. Sin etiquetas, Bricks muestra el código como texto. En el plugin Code Snippets, en cambio, el PHP va **sin** `<?php` al inicio.

### Reglas del bloque de textos
10. **Todos los textos visibles van en `$t`.** Ningún texto suelto dentro del HTML.
11. **Todas las imágenes van en `$i`**, con la ruta relativa a la carpeta de uploads (`2026/10/hero.webp`). La URL se arma con `wp_get_upload_dir()`, nunca con el dominio escrito a mano.
12. **Datos críticos con marcador.** Dentro de `$t`, el número de años, el teléfono, el nombre de la empresa, las zonas, etc. se escriben con `{years}`, `{phone}`, `{company}`, `{areas}`. **Nunca un número o un nombre fijo.**
13. **Sin lógica dentro del bloque.** Solo textos y rutas. El PHP de lectura de datos va debajo de la línea de "no editar".
14. **Sin comillas dobles dentro de los textos** (se usan comillas simples en el PHP, y los apóstrofes se escapan con una barra invertida o se evitan). Un texto con comillas mal cerradas rompe la sección.
15. **Imagen faltante:** si no hay imagen real, se usa `2026/10/image-test.webp` (el placeholder del sitio base), nunca una imagen de internet ni un enlace roto.
16. **Imágenes:** formato WebP, con `width` y `height` reales, `alt` descriptivo (puede usar `{company}`), `loading="lazy"` en todas menos la del Hero, que lleva `fetchpriority="high"`.

---

## 4. Contenido de cada página

La IA propone las secciones según el brief. Esta es la base:

| Página | Secciones base |
|---|---|
| **Home** | Hero (con H1), presentación breve, por qué elegirnos, servicios (dinámico), áreas (dinámico), testimonios, franja de llamada a la acción |
| **About** | Hero interior (H1), historia, años de experiencia, propietario, misión y visión (de Business Info, si existen), licencia (si existe), llamada a la acción |
| **Contact** | Hero interior (H1), datos de contacto (teléfono, email, dirección, horario, mapa, todo de Business Info), formulario global |

Una sección cuyo dato falte en Business Info **no se imprime** (por ejemplo, sin propietario no hay bloque de propietario).

---

## 5. Cómo edita el equipo (guía de una línea)

1. En Bricks, abre la página y haz clic en el elemento Code de la sección.
2. Cambia solo lo que está **arriba de la línea "NO EDITAR DEBAJO"**.
3. Guarda, y comprueba la página en el navegador.
4. Para cambiar un dato crítico (teléfono, años, email, dirección) **no se edita la página**: se cambia en Business Info.

---

## 6. Revisión antes de aprobar (para quien aprueba)

- [ ] Todos los textos están en el bloque `$t` y todas las imágenes en `$i`
- [ ] No hay teléfonos, emails, direcciones, nombres de empresa ni años escritos a mano
- [ ] Cambiar `biz_years_experience` y comprobar que cambia en Home y About
- [ ] Un solo H1 por página, y los H2 y H3 en orden
- [ ] Todos los enlaces funcionan y ninguno es `href="#"`
- [ ] Una sección con un dato vacío no deja huecos ni íconos sueltos
- [ ] Las imágenes cargan, son WebP, el Hero no tiene `lazy` y las demás sí
- [ ] Modo sólido y transparente del header: el Hero queda bien en los dos
- [ ] Sin scroll horizontal en 320, 768 y 1280 px
- [ ] Velocidad: sin imágenes pesadas ni scripts que bloqueen la carga
- [ ] Cambiar un texto del bloque `$t` funciona y no rompe la sección

---

## 7. Cuándo pasar una página a ACF

Si el cliente o el equipo de copy necesitan editar los textos por su cuenta, esa página se pasa a campos ACF. Como el código ya lee los textos desde un único bloque, el cambio no obliga a rediseñar nada: el bloque `$t` se reemplaza por lecturas de campos.

---

## 8. Texto para pegar en el prompt maestro

````
CONTRATO DE PÁGINAS (Home, About, Contact)
1. Cada sección es un solo elemento Code de Bricks en modo PHP, con su propia clase raíz .sec-nombre y su propio CSS.
2. Al inicio del código de cada sección va un bloque "TEXTOS EDITABLES" con un arreglo $t (todos los textos visibles) y un arreglo $i (rutas de imágenes relativas a uploads). Debajo, una línea "NO EDITAR DEBAJO DE ESTA LINEA". Ningún texto o imagen suelto en el HTML.
3. Los datos críticos del negocio nunca se escriben a mano: en $t se usan los marcadores {company}, {years}, {phone}, {email}, {address}, {hours}, {areas}, {license}, {owner}, {year}, y se imprimen con esc_html( biz_tokens( $t['clave'] ) ).
4. Un solo H1 por página, en el Hero. Los demás títulos en orden H2 y H3.
5. Todo el CSS con variables de Bricks. Sin hex, px, rem ni z-index sueltos. Puntos de corte: 767 y 1023 px. Sin scroll horizontal.
6. Enlaces con get_permalink(). El enlace de contacto se obtiene con get_page_by_path('contact'); si no existe, el botón no se imprime. Nunca href="#".
7. Imágenes WebP con width, height y alt. Lazy en todas menos el Hero (fetchpriority="high"). Si falta la imagen real, se usa image-test.webp.
8. Una sección cuyo dato esté vacío no se imprime, ni su ícono.
9. Prohibido: guion largo, la palabra "across", el dominio visible, datos inventados y comillas dobles dentro de los textos.
10. El Hero respeta las reglas del header para modo sólido y transparente.
11. Diseño libre y creativo, sin repetir fórmulas de otros proyectos. Sin estadísticas en el Hero. Las insignias de confianza son span o li, sin hover ni apariencia de botón, radio máximo var(--radius-m). Un dato aparece una sola vez por página.
12. Los grupos dinámicos se ven bien con 1 a 20 elementos: columnas calculadas en PHP, última fila centrada, sin celdas vacías.
13. Entrega por campos del Code de Bricks: PHP y HTML, CSS (sin style) y JS (sin script), en archivos separados.
````

---

## 9. Pendiente

- ~~Escribir la función `biz_tokens()`~~: hecho y probado (`{company}`, `{years}`, `{phone}`, `{areas}`).
- Probar el contrato en un piloto con una página Home real.
- Sumar este contrato y la regla de datos dinámicos al prompt maestro nuevo.

=== FIN DE Contrato_Paginas_Home_About_Contact.md ===


=== Contrato_Selector_Servicios.md ===

(Fase 8.3: contrato del selector de servicios)

# Contrato del selector de servicios (One Page y Corporativa)

**Dónde aplica:** solo en sitios de tipo `onepage` o `corporate` (viene fijo en el wizard instalado: CM Wizard One Page o CM Wizard Corporativa). En estos sitios los servicios NO tienen página propia: el CPT `service` guarda los datos y esta sección los muestra. En los sitios SEO no se usa: allí los servicios tienen su plantilla Single Service.

**Archivo base:** `Selector_Servicios_Base.php` (un solo elemento Code de Bricks, con su CSS y su JS dentro). La IA lo adapta al diseño del proyecto; no lo reescribe desde cero.

## 1. Dónde se coloca
| Tipo de sitio | Dónde va | Galería por servicio |
|---|---|---|
| `onepage` | Sección de la Home, con `id="services"` | No |
| `corporate` | Página Services, con `id="services"` | Sí (`svc_gallery`, máximo 6 fotos) |

## 2. Cómo funciona
- Escritorio: lista de servicios a la izquierda y detalle del servicio elegido a la derecha. Celular: acordeón, un servicio abierto a la vez.
- Siempre hay un servicio abierto: el primero, o el que pida el enlace.
- Cada servicio tiene un ancla `#svc-{slug}` (el slug de la entrada del CPT). Al abrir la página con ese ancla, o al cambiar el ancla estando en la página, se abre ese servicio y la página baja a la sección (con el margen del header fijo, variable `--hdr-h`).
- Al hacer clic en un servicio se actualiza el ancla sin saltar, para poder compartir el enlace.
- Sin JavaScript todos los servicios se ven abiertos, uno debajo de otro.

## 3. Enlaces del header
| Tipo de sitio | Enlace de cada servicio en el megamenú |
|---|---|
| `onepage` | `home_url( '/#svc-' . $slug )` (funciona desde la Home y desde Gallery) |
| `corporate` | `get_permalink( página services ) . '#svc-' . $slug` |
El enlace "Services" del menú va a `/#services` (One Page) o a la página Services (Corporativa). Todo se arma con consultas al CPT, nunca a mano.

## 4. Datos que muestra (todos dinámicos)
Título del servicio; `svc_icon`; imagen (`svc_banner`, o la imagen destacada); `svc_intro` (o `svc_short_description` si falta); `svc_includes`; `svc_benefits`; en Corporativa también `svc_gallery`; botón de llamada a la acción. Un bloque sin datos no se imprime. Se muestran solo los servicios publicados con `svc_menu_show` activo, en el orden del menú.

## 5. Qué puede cambiar la IA y qué no
- Puede cambiar: valores visuales del CSS (colores, radios, espacios, tamaños con las variables globales del proyecto), los textos del bloque TEXTOS EDITABLES y el orden visual de los bloques internos.
- No puede cambiar: los nombres de clase `sec-svcsel__*`, los ids `svc-{slug}` y `tab-{slug}`, el atributo `data-svc`, el JavaScript ni la lógica de consulta.
- Prohibido escribir servicios, textos o datos de contacto a mano.

## 6. Áreas en estos sitios
Las áreas no son páginas. Se mencionan como texto: la sección o el footer imprimen la lista de nombres cargados en `biz_areas` (todas las filas, la zona principal primero). `{areas}` y `{in_areas}` siguen funcionando con las reglas de siempre. No hay enlaces ni "View All" de áreas.

=== FIN DE Contrato_Selector_Servicios.md ===


=== Selector_Servicios_Base.php ===

(Fase 8.3: codigo base del selector de servicios (se adapta, no se reescribe))

```php
<?php
// ===== TEXTOS EDITABLES: cambiar solo aqui =====
$t = [
  'title'    => 'Our Services',
  'subtitle' => 'Choose a service to see what is included.',
  'cta'      => 'Get a Free Estimate',
  'gallery'  => 'Recent Work',
  'includes' => 'What is included',
  'benefits' => 'Why it matters',
];
$fa = 'fa-regular'; // mismo estilo de iconos del header
// ===== NO EDITAR DEBAJO DE ESTA LINEA =====

$tok = function ( $s ) { return function_exists( 'biz_tokens' ) ? biz_tokens( $s ) : $s; };
$site_type = function_exists( 'cmc_site_type' ) ? (string) cmc_site_type() : ''; // el tipo viene fijo en el wizard instalado (One Page o Corporativa)
$is_corp   = ( $site_type === 'corporate' ); // la galeria por servicio solo existe en Corporativa
$services = get_posts( array(
	'post_type' => 'service', 'post_status' => 'publish', 'numberposts' => -1,
	'orderby' => array( 'menu_order' => 'ASC', 'title' => 'ASC' ),
) );
$services = array_values( array_filter( $services, function ( $p ) {
	return ! ( metadata_exists( 'post', $p->ID, 'svc_menu_show' ) && ! get_post_meta( $p->ID, 'svc_menu_show', true ) );
} ) );
if ( ! $services ) { return; }
if ( $is_corp ) {
	$cp = get_page_by_path( 'contact' );
	$cta_url = ( $cp && $cp->post_status === 'publish' ) ? get_permalink( $cp ) : '';
} else {
	$cta_url = '#contact';
}
$n = count( $services );
?>
<style>
.sec-svcsel{--v-text-s:var(--text-s,1.4rem);--v-text-m:var(--text-m,1.6rem);--v-text-l:var(--text-l,1.8rem);--v-text-xl:var(--text-xl,2.2rem);--v-text-3xl:var(--text-3xl,clamp(2.6rem,3.4vw,4rem));--v-sp-xs:var(--space-xs,1rem);--v-sp-s:var(--space-s,1.3rem);--v-sp-m:var(--space-m,1.8rem);--v-sp-l:var(--space-l,2.4rem);--v-sp-4xl:var(--space-4xl,clamp(5rem,7vw,8.4rem));--v-r-m:var(--radius-m,.8rem);--v-sh-s:var(--shadow-s,0 .2rem .8rem rgba(0,0,0,.12));--v-tr:var(--transition,.25s ease);--v-cmax:var(--container-max,120rem);--v-cpad:var(--container-padding,clamp(1.6rem,4vw,4rem));background:var(--primary-l-10,#E8ECF4);color:var(--color-text,#3A3F4A);padding:var(--v-sp-4xl) 0;scroll-margin-top:calc(var(--hdr-h,8rem) + 1.6rem)}
.sec-svcsel *{box-sizing:border-box}
.sec-svcsel__in{max-width:var(--v-cmax);margin:0 auto;padding:0 var(--v-cpad)}
.sec-svcsel__head{max-width:70rem;margin:0 auto var(--v-sp-l);text-align:center}
.sec-svcsel__title{margin:0 0 var(--v-sp-xs);font-size:var(--v-text-3xl);color:var(--title,var(--primary-d-5,#1A2A4D))}
.sec-svcsel__sub{margin:0;font-size:var(--v-text-l)}
.sec-svcsel__list{display:grid;grid-template-columns:minmax(24rem,32rem) minmax(0,1fr);column-gap:var(--v-sp-l);align-items:start}
.sec-svcsel__item{display:contents}
.sec-svcsel__tab{grid-column:1;display:flex;align-items:center;gap:var(--v-sp-s);width:100%;margin:0 0 var(--v-sp-xs);padding:var(--v-sp-s) var(--v-sp-m);border:0;border-radius:var(--v-r-m);background:var(--white,#fff);color:var(--title,var(--primary-d-5,#1A2A4D));font:inherit;font-size:var(--v-text-m);font-weight:700;text-align:left;cursor:pointer;box-shadow:var(--v-sh-s);transition:background var(--v-tr),color var(--v-tr)}
.sec-svcsel__tab:hover{background:var(--primary-l-5,#F3F5FA)}
.sec-svcsel__tab:focus-visible{outline:.3rem solid var(--cta,#D9480F);outline-offset:.2rem}
.sec-svcsel__tab[aria-expanded="true"]{background:var(--primary,#253C6D);color:var(--white,#fff)}
.sec-svcsel__ico{flex:0 0 auto;width:4rem;height:4rem;border-radius:50%;display:inline-flex;align-items:center;justify-content:center;background:var(--primary-l-10,#E8ECF4);color:var(--primary,#253C6D);font-size:1.8rem}
.sec-svcsel__tab[aria-expanded="true"] .sec-svcsel__ico{background:rgba(255,255,255,.16);color:var(--white,#fff)}
.sec-svcsel__panel{grid-column:2;grid-row:1 / span var(--n);background:var(--white,#fff);border-radius:var(--v-r-m);box-shadow:var(--v-sh-s);overflow:hidden}
.sec-svcsel.is-js .sec-svcsel__panel[hidden]{display:none}
.sec-svcsel__img{display:block;width:100%;aspect-ratio:16/7;object-fit:cover}
.sec-svcsel__body{display:flex;flex-direction:column;gap:var(--v-sp-m);padding:var(--v-sp-l)}
.sec-svcsel__name{margin:0;font-size:var(--v-text-xl);color:var(--title,var(--primary-d-5,#1A2A4D))}
.sec-svcsel__intro{margin:0;line-height:1.6;font-size:var(--v-text-m)}
.sec-svcsel__h{margin:0 0 var(--v-sp-xs);font-size:var(--v-text-l);color:var(--title,var(--primary-d-5,#1A2A4D))}
.sec-svcsel__checks{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--v-sp-xs) var(--v-sp-m)}
.sec-svcsel__checks li{display:flex;gap:var(--v-sp-xs);align-items:flex-start;line-height:1.45}
.sec-svcsel__checks li::before{content:"";flex:0 0 auto;width:1.1rem;height:1.1rem;margin-top:.45rem;border-radius:50%;background:var(--cta,#D9480F)}
.sec-svcsel__bens{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:var(--v-sp-m)}
.sec-svcsel__bens li{padding:var(--v-sp-s) var(--v-sp-m);border-radius:var(--v-r-m);background:var(--primary-l-10,#E8ECF4)}
.sec-svcsel__bens strong{display:block;margin-bottom:.4rem;color:var(--title,var(--primary-d-5,#1A2A4D))}
.sec-svcsel__gal{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:var(--v-sp-xs)}
.sec-svcsel__gal img{display:block;width:100%;aspect-ratio:4/3;object-fit:cover;border-radius:var(--v-r-m)}
.sec-svcsel__btn{align-self:flex-start;display:inline-block;padding:var(--v-sp-xs) var(--v-sp-l);border-radius:5rem;background:var(--cta,#D9480F);color:var(--white,#fff);font-weight:700;text-decoration:none}
@media (max-width:1023px){.sec-svcsel__list{grid-template-columns:minmax(0,1fr)}.sec-svcsel__tab,.sec-svcsel__panel{grid-column:1;grid-row:auto}.sec-svcsel__panel{margin:calc(var(--v-sp-xs) * -.5) 0 var(--v-sp-m)}.sec-svcsel__checks,.sec-svcsel__bens{grid-template-columns:1fr}.sec-svcsel__gal{grid-template-columns:repeat(2,minmax(0,1fr))}}
</style>
<section class="sec-svcsel" id="services">
	<div class="sec-svcsel__in">
		<div class="sec-svcsel__head">
			<h2 class="sec-svcsel__title"><?php echo esc_html( $tok( $t['title'] ) ); ?></h2>
			<?php if ( $t['subtitle'] !== '' ) : ?><p class="sec-svcsel__sub"><?php echo esc_html( $tok( $t['subtitle'] ) ); ?></p><?php endif; ?>
		</div>
		<div class="sec-svcsel__list" style="--n:<?php echo (int) $n; ?>">
			<?php foreach ( $services as $i => $s ) :
				$slug     = $s->post_name;
				$icon     = preg_replace( '/[^a-z0-9-]/', '', strtolower( (string) get_field( 'svc_icon', $s->ID ) ) );
				$intro    = trim( (string) get_field( 'svc_intro', $s->ID ) );
				if ( $intro === '' ) { $intro = trim( (string) get_field( 'svc_short_description', $s->ID ) ); }
				$includes = (array) get_field( 'svc_includes', $s->ID );
				$benefits = (array) get_field( 'svc_benefits', $s->ID );
				$banner   = (int) get_field( 'svc_banner', $s->ID );
				if ( ! $banner ) { $banner = (int) get_post_thumbnail_id( $s->ID ); }
				$im       = $banner ? wp_get_attachment_image_src( $banner, 'large' ) : false;
				$gal      = $is_corp ? array_slice( array_filter( array_map( 'intval', (array) get_field( 'svc_gallery', $s->ID ) ) ), 0, 6 ) : array();
				$open     = ( $i === 0 );
				?>
			<div class="sec-svcsel__item">
				<button type="button" class="sec-svcsel__tab" id="tab-<?php echo esc_attr( $slug ); ?>" aria-expanded="<?php echo $open ? 'true' : 'false'; ?>" aria-controls="svc-<?php echo esc_attr( $slug ); ?>" data-svc="<?php echo esc_attr( $slug ); ?>">
					<?php if ( $icon !== '' ) : ?><span class="sec-svcsel__ico"><i class="<?php echo esc_attr( $fa . ' fa-' . $icon ); ?>" aria-hidden="true"></i></span><?php endif; ?>
					<span><?php echo esc_html( get_the_title( $s ) ); ?></span>
				</button>
				<div class="sec-svcsel__panel" id="svc-<?php echo esc_attr( $slug ); ?>" role="region" aria-labelledby="tab-<?php echo esc_attr( $slug ); ?>"<?php echo $open ? '' : ' hidden'; ?>>
					<?php if ( $im ) : ?><img class="sec-svcsel__img" src="<?php echo esc_url( $im[0] ); ?>" width="<?php echo (int) $im[1]; ?>" height="<?php echo (int) $im[2]; ?>" alt="<?php echo esc_attr( get_the_title( $s ) ); ?>" loading="lazy"><?php endif; ?>
					<div class="sec-svcsel__body">
						<h3 class="sec-svcsel__name"><?php echo esc_html( get_the_title( $s ) ); ?></h3>
						<?php if ( $intro !== '' ) : ?><p class="sec-svcsel__intro"><?php echo esc_html( $tok( $intro ) ); ?></p><?php endif; ?>
						<?php if ( $includes ) : ?>
						<div><h4 class="sec-svcsel__h"><?php echo esc_html( $t['includes'] ); ?></h4>
							<ul class="sec-svcsel__checks"><?php foreach ( $includes as $r ) : if ( empty( $r['text'] ) ) { continue; } ?><li><?php echo esc_html( $tok( $r['text'] ) ); ?></li><?php endforeach; ?></ul></div>
						<?php endif; ?>
						<?php if ( $benefits ) : ?>
						<div><h4 class="sec-svcsel__h"><?php echo esc_html( $t['benefits'] ); ?></h4>
							<ul class="sec-svcsel__bens"><?php foreach ( $benefits as $r ) : if ( empty( $r['title'] ) ) { continue; } ?><li><strong><?php echo esc_html( $tok( $r['title'] ) ); ?></strong><?php echo esc_html( $tok( (string) ( $r['text'] ?? '' ) ) ); ?></li><?php endforeach; ?></ul></div>
						<?php endif; ?>
						<?php if ( $gal ) : ?>
						<div><h4 class="sec-svcsel__h"><?php echo esc_html( $t['gallery'] ); ?></h4>
							<ul class="sec-svcsel__gal"><?php foreach ( $gal as $gid ) : $g = wp_get_attachment_image_src( $gid, 'medium_large' ); if ( ! $g ) { continue; } ?><li><img src="<?php echo esc_url( $g[0] ); ?>" width="<?php echo (int) $g[1]; ?>" height="<?php echo (int) $g[2]; ?>" alt="<?php echo esc_attr( trim( (string) get_post_meta( $gid, '_wp_attachment_image_alt', true ) ) ?: get_the_title( $s ) ); ?>" loading="lazy"></li><?php endforeach; ?></ul></div>
						<?php endif; ?>
						<?php if ( $cta_url !== '' ) : ?><a class="sec-svcsel__btn" href="<?php echo esc_url( $cta_url ); ?>"><?php echo esc_html( $t['cta'] ); ?></a><?php endif; ?>
					</div>
				</div>
			</div>
			<?php endforeach; ?>
		</div>
	</div>
</section>
<script>
(function(){
	var sec=document.getElementById('services'); if(!sec||!sec.classList.contains('sec-svcsel')) return;
	sec.classList.add('is-js');
	var tabs=[].slice.call(sec.querySelectorAll('.sec-svcsel__tab'));
	function show(slug){
		var found=false;
		tabs.forEach(function(b){
			var on=b.getAttribute('data-svc')===slug; if(on) found=true;
			b.setAttribute('aria-expanded',on?'true':'false');
			var p=document.getElementById(b.getAttribute('aria-controls')); if(p){ if(on){p.removeAttribute('hidden');}else{p.setAttribute('hidden','');} }
		});
		return found;
	}
	function fromHash(scroll){
		var m=/^#svc-(.+)$/.exec(window.location.hash||''); if(!m) return;
		if(show(decodeURIComponent(m[1])) && scroll){ setTimeout(function(){ sec.scrollIntoView({behavior:'smooth',block:'start'}); },60); }
	}
	tabs.forEach(function(b){ b.addEventListener('click',function(){
		show(b.getAttribute('data-svc'));
		if(window.history&&history.replaceState){ history.replaceState(null,'','#svc-'+b.getAttribute('data-svc')); }
		if(window.matchMedia('(max-width:1023px)').matches){ b.scrollIntoView({behavior:'smooth',block:'start'}); }
	}); });
	window.addEventListener('hashchange',function(){ fromHash(true); });
	fromHash(true);
})();
</script>
```

=== FIN DE Selector_Servicios_Base.php ===


=== Legales_CSS_Base.css ===

(Fase 10: base del CSS de paginas legales)

```css
/* BASE del CSS de las paginas legales (Snippet 6). NO se pega tal cual en los proyectos.
   Es la referencia que usa la IA en las fases de páginas para generar el CSS propio de cada proyecto.
   Se conservan los nombres de clase. Cambian los valores visuales segun la Ficha de componentes.
   Solo variables globales que ya existen. Nunca :root ni variables nuevas.
   Los titulos del contenido usan el MISMO tamano que el H3 global del proyecto. */

/* Hero compacto: baja altura, solo el titulo de la pagina */
.sec-legal-hero {
	display: flex;
	align-items: center;
	box-sizing: border-box;
	width: 100%;
	min-height: 22vh;
	padding: var(--space-l) var(--container-padding);
	background-color: var(--primary);
}

.sec-legal-hero__inner {
	width: 100%;
	max-width: var(--container-max);
	margin: 0 auto;
}

.sec-legal-hero h1 {
	margin: 0;
	color: var(--white);
}

/* Contenido de lectura */
.sec-legal-body {
	box-sizing: border-box;
	width: 100%;
	padding: var(--space-xl) var(--container-padding);
}

.biz-legal {
	max-width: 80ch;
	margin: 0 auto;
	color: var(--color-text);
	line-height: 1.7;
}

.biz-legal__date {
	margin: 0 0 var(--space-l);
	font-size: var(--text-s);
	opacity: 0.75;
}

/* Titulos de cada bloque: tamano de H3 (la etiqueta HTML es h2 para respetar la jerarquia) */
.biz-legal h2 {
	margin: var(--space-l) 0 var(--space-2xs);
	font-size: var(--text-l);
	line-height: 1.3;
	color: var(--title);
}

.biz-legal h2:first-of-type {
	margin-top: 0;
}

.biz-legal p {
	margin: 0 0 var(--space-xs);
	font-size: var(--text-m);
}

.biz-legal ul {
	margin: 0 0 var(--space-xs);
	padding-left: var(--space-m);
	font-size: var(--text-m);
}

.biz-legal li {
	margin-bottom: var(--space-2xs);
}

.biz-legal a {
	color: var(--primary);
	text-decoration: underline;
	text-underline-offset: 0.2em;
	transition: color var(--transition);
}

.biz-legal a:hover,
.biz-legal a:focus-visible {
	color: var(--cta);
}
```

=== FIN DE Legales_CSS_Base.css ===
