# Plan de mejora SEO — Paver Driveways

- **Basado en:** [diagnostico.md](diagnostico.md) (2026-10-30)
- **Resultado:** se mide en [resultado.md](resultado.md) 10 días después del deploy
- **Alcance:** la página base y sus 20 páginas de ciudad. Algunas piezas son
  compartidas con las otras 10 verticales (el template de los titles, el nodo de la
  organización, los componentes del layout de servicio); cada tarea dice qué toca
  fuera de la vertical.

## Objetivo

1. Titles de 60 caracteres o menos en las 21 páginas.
2. Que el sitio no afirme reseñas que no se pueden sostener.
3. Bajar las imágenes de la base de 5,2 MB a menos de 1 MB, con el formato y el recorte
   de cada dispositivo. Las 20 ciudades lo heredan, porque usan las mismas fotos.
4. Sacar las páginas de ciudad del patrón de páginas puerta (hoy ~35 % único).
5. Completar el schema con el subtipo correcto y el área del servicio.
6. No empeorar nada de lo que hoy está bien: LCP, H1, links, FAQs.

**Regla para todo el plan:** no se quita ningún wording que transmita calidad
("Premium", "licensed, insured", "built to last", "upscale" y similares). Si falta
espacio, se recorta otra cosa. Las cifras verificables, como la cantidad y la nota de
las reseñas, no cuentan como wording: se ajustan al dato real.

## Decisiones pendientes (las define el owner antes de ejecutar)

| # | Decisión | Opciones | Recomendación |
|---|---|---|---|
| D1 | Marca al final de los titles de servicio | **A.** `PPD Inc.` en las 11 bases y las 220 ciudades, como la home · **B.** Solo en esta vertical | **A**: es un solo cambio en `serviceCopy()` y `localityPage()`. La B obliga a meter una excepción por servicio en el template |
| D2 | Frase del title de esta vertical | **A.** `Paver Driveways in Palm Beach & Broward \| PPD Inc.` (50) y `Paver Driveways in <Ciudad>, FL \| PPD Inc.` (41–52) · **B.** Mantener "Paver Driveway Installation": base 62, ciudades 53–64 | **A**: la B deja la base y 3 ciudades arriba de 60 (Deerfield Beach, West Palm Beach y Palm Beach Gardens) |
| D3 | Reseñas | Hoy dice `100+ 5.0★ Google Reviews`. Según el owner, serían 100+ reseñas de 5 estrellas sumando todas las plataformas, no solo Google | **Propuesta (2026-10-30, espera autorización):** `100+` · `Five-Star Reviews` en el bloque de números y "100+ five-star reviews" en la FAQ, sin "Google" ni "5.0". Es cierta en las dos lecturas y conserva el wording de calidad |
| D4 | Páginas de ciudad | **A.** Material real por ciudad (proyectos, fotos, testimonios) · **B.** Recortar en las ciudades las secciones genéricas (las que ya están en la base) y dejar lo local · **C.** Fusionar en la base las ciudades sin impresiones, con 301 | **A + B**: B baja la parte repetida sin inventar nada; A suma lo que de verdad hace única a una página. C pierde páginas que todavía no tuvieron tiempo de rankear |
| D5 | Fort Lauderdale | Sumarlo al área de servicio con página propia, o no atenderlo | Lo define el owner. Si no lo atienden, no se hace nada |
| D6 | Coordenadas `geo` | Copiarlas de la ficha | Ya pendiente desde la home |

### Decisiones tomadas

| # | Fecha | Decisión |
|---|---|---|
| D1 | 2026-10-30 | **A:** el sufijo `PPD Inc.` va en los titles de los 11 servicios (sus bases y sus páginas de ciudad), con un solo cambio en el template |
| D2 | 2026-10-30 | **A:** `Paver Driveways in Palm Beach & Broward \| PPD Inc.` en la base y `Paver Driveways in <Ciudad>, FL \| PPD Inc.` en las ciudades. Mismo formato que la home |
| D4 | 2026-10-30 | **B:** las ciudades dejan de repetir las 4 secciones genéricas y enlazan a la base. La A queda en pausa: los trabajos por ciudad se hicieron, pero sus fotos nunca se recopilaron. Se descartó generar imágenes con IA por ciudad: no bajan la similitud del texto, no aportan señal local y se leerían como trabajos reales |
| D4 bis | 2026-10-30 | Se confirma la B (quitar, no reescribir: reescribir sin datos nuevos solo cambia sinónimos). Se suma en las 6 ciudades con impresiones una sección propia con lo que pide cada municipio para hacer un driveway (permisos, entrada desde la calle), verificado con la fuente oficial (T5.5). Es un ejercicio: si funciona, se replica |
| D5 | 2026-10-30 | **No:** la empresa no toma trabajos en Fort Lauderdale. No se crea página y T6 no aplica. Esas búsquedas no son clientes |
| D6 | 2026-10-30 | **Coordenadas del pin de la ficha:** `26.494966, -80.089388`, en `config('company.geo')` |

## Tareas

### T1 — Titles de las 21 páginas

- **Dónde:** `app/Services/PageSeoService.php`.
  - `serviceCopy()`: el sufijo de `$record['title']` pasa a `config('company.short_name')`
    (D1), y `titlePhrase` de `PaverDriveways` pasa a `Paver Driveways in Palm Beach & Broward` (D2).
  - `localityPage()`: el sufijo del title pasa a `short_name` (D1). Para la frase (D2), un
    nombre corto del servicio para titles, definido una sola vez en `serviceCopy()`
    (por ejemplo `titleName`), con `serviceName` como valor por defecto para las
    verticales que todavía no lo tengan.
  - La description, el H1, el `Service.name` y los breadcrumbs **no cambian**: siguen
    usando el nombre legal y "Paver Driveway Installation".
- **Fuera de la vertical (si D1 = A):** las otras 10 bases y sus 200 ciudades acortan
  el sufijo. Su frase no cambia.
- **Aceptación:**
  - `<title>` de 60 caracteres o menos en la base y en las 20 ciudades.
  - `og:title`, `twitter:title` y `webPage.name` heredan el valor nuevo.
  - `metadata_template.py` da `templated: false` en la base.
  - `SeoTest` actualizado, con un test que recorra las 21 páginas y verifique el largo.

### T2 — Reseñas (según D3)

- **Dónde:** `components/home/proof-and-process.blade.php` (`100+ 5.0★`) y
  `app/Support/PageFaqMap.php` ("100+ five-star Google reviews").
- **Qué:** el dato vive **en un solo lugar** (`config/company.php`, por ejemplo
  `reviews.google.count` y `reviews.google.rating`) y los dos archivos lo leen. Hoy está
  escrito a mano en los dos.
- **Fuera de la vertical:** `proof-and-process` se ve en las 242 páginas de servicio y
  en `/estimate`; la FAQ es de la home.
- **Aceptación:** ningún texto del sitio afirma una nota o una cantidad distinta de la
  que muestra la ficha. `grep -rn "100+" resources app` no devuelve reseñas escritas a mano.

### T3 — Schema

- **Dónde:** `PageSeoService::organizationSchema()` y `servicePages()`.
- **Qué:**
  - `@type` de la organización: `['Organization', 'GeneralContractor']`.
    `GeneralContractor` ya implica `LocalBusiness` y `HomeAndConstructionBusiness`.
  - `areaServed` en el `Service` de la base: un `AdministrativeArea` por condado,
    derivado de las keys de `config('company.service_areas')`. Nada escrito a mano.
- **Fuera de la vertical:** el nodo de la organización es global (todas las páginas), y
  `servicePages()` arma las 11 bases, así que las 11 ganan `areaServed`. Es correcto:
  todas cubren los mismos dos condados.
- **Aceptación:**
  - `SeoTest` cubre el subtipo y el `areaServed` de la base.
  - El hook `validate-schema` no da errores nuevos (ojo: hoy se cae con los `@type` en
    lista, un bug de claude-seo).
  - El Rich Results Test sobre la base sale sin errores nuevos.

### T4 — Imágenes

#### T4.1 (chore) — Componentes del layout de servicio con `<x-responsive-image>`

- **Dónde:** los 6 componentes con fotos que usa `layouts/service.blade.php`:
  `heroes/service`, `home/why-choose-us`, `home/centered-photo-feature`,
  `home/centered-feature-list`, `home/centered-photo-feature-v2` y
  `home/image-with-checklist`.
- **Qué:**
  - Cada `<img>` pasa a `<x-responsive-image>`.
  - El layout hoy les pasa la URL ya resuelta (`$imageUrl(...)`). Tiene que pasarles la
    ruta lógica, como se hizo con las tarjetas de la home.
- **Fuera de la vertical:** los 6 componentes son de las 242 páginas de servicio. Las
  imágenes que no estén en `variants.config.json` siguen saliendo como `<img>` simple,
  así que las otras verticales no cambian hasta que se mapeen sus fotos.
- **Aceptación:** sin regresión visual en una página de otra vertical (por ejemplo
  `/pool-renovation-palm-beach-broward`), comparada antes y después.

#### T4.2 — Variantes de las 17 fotos de la vertical

- **Dónde:** `resources/images/variants.config.json` y `pnpm images:variants`.
- **Qué:**
  - Mapear las 17 fotos de `resources/images/services/paver-driveways/`.
  - Perfil nuevo para el hero de servicio: se pinta a 1440×540 en desktop y a 390×506
    en mobile, distinto del hero de la home. Recorte mobile centrado.
  - Las fotos de sección (se pintan entre 167 y 440 px) usan `tile` o `card`, o un perfil
    nuevo si ninguno encaja. Los perfiles se miden con Playwright, no se suponen.
- **Aceptación:**
  - Peso de imágenes (`medir-pagina.py`) **menor a 1.000 KB** en desktop y en mobile.
  - En Chromium, todas las imágenes raster se sirven en AVIF.
  - El LCP sigue siendo el hero, con `fetchpriority="high"` y sin empeorar.

### T5 — Páginas de ciudad (según D4)

- **T5.1 (B) — Recortar lo genérico:**
  - Los data files de las 20 ciudades dejan de traer las secciones de mayor
    similitud (`outdoorMatch`, `whyChoose`, `design`, `renovation`). Ese contenido
    queda en la base, que ya recibe links desde el mega-menú (las reglas de
    interlinking no permiten links a la base desde el texto de una ciudad).
  - Se mantienen la introducción, los barrios, las FAQs y lo que D4-A sume.
  - El wording de calidad de las secciones recortadas se conserva en lo que queda
    de la página.
  - Es una vista propia de esta vertical: no toca las otras 10.
- **T5.2 (A) — Material real (en pausa, D4):** proyectos, fotos y testimonios por
  ciudad, empezando por las que ya tienen impresiones (Deerfield Beach, Jupiter, Boca
  Raton, Delray Beach, West Palm Beach y Wellington). Hoy no hay material recopilado.
  Fuentes posibles: la ficha de Google, Instagram y Facebook, los celulares de los
  encargados de obra y las direcciones de los trabajos en el CRM. De acá en adelante,
  2 o 3 fotos por trabajo terminado, con ciudad y material.
- **T5.3 — Introducción:** hoy es una plantilla (78 % de similitud). Reescribirla
  con datos de la ciudad que ya existan en el repo (barrios, condado), sin inventar.
- **T5.5 — Permisos por municipio (D4 bis):** en Deerfield Beach, Jupiter, Boca Raton,
  Delray Beach, West Palm Beach y Wellington, una sección con lo que pide el municipio
  para instalar o reemplazar un driveway (permiso, entrada y swale, reglas propias).
  Cada dato sale de una fuente oficial (web del municipio o su código en Municode),
  anotada en `mediciones/`. Lo que no se pueda verificar no se publica.
- **T5.4 (chore) — Script de similitud:** guardar la prueba de intercambio en
  `seo-optimizations/_scripts/similitud-ciudades.py`, para volver a medirla en
  `resultado.md` y en las próximas verticales.
- **Aceptación:** similitud media entre ciudades **por debajo del 40 %** (más del 60 %
  único), medida con el script.

### T6 — Fort Lauderdale (no aplica: D5 = no)

- Sumar la ciudad a `config('company.service_areas')` y crear su data file con
  contenido propio.
- **Fuera de la vertical:** `service_areas` alimenta las 11 verticales, así que
  aparecen 11 páginas nuevas. Hay que decidir si se generan todas o solo esta.

### T7 — Verificación con Playwright (antes de deploy, en el sitio local de lerd)

1. `medir-pagina.py` sobre la base y sobre una ciudad, guardado en `mediciones/AAAAMMDD-local/`.
2. Revisar en `medicion.json`: AVIF en todas las raster, ninguna imagen servida a más
   del doble de lo que se pinta por el DPR, recorte vertical en el hero mobile, LCP en el
   hero y CLS sin empeorar.
3. Comparar las capturas con las de `mediciones/20261030-antes/`, más una página de otra
   vertical (aceptación de T4.1).
4. `/seo-page` sobre la base local: un H1, title de 60 o menos, JSON-LD válido.
5. `similitud-ciudades.py` (aceptación de T5).
6. Suite de tests completa.

### T8 — Deploy y seguimiento

1. PR hacia `production` (no `master`) y registrar la **fecha de deploy** en `resultado.md`.
2. `medir-pagina.py` contra producción, guardado en `mediciones/AAAAMMDD-despues/`.
3. Search Console → URL Inspection → **Request indexing** de la base y de las 6 ciudades
   con más impresiones (las de T5.2). La Indexing API no sirve para esto.
4. A los 10 días, completar `resultado.md`.

## Fuera del código (owner)

| # | Qué | Por qué |
|---|---|---|
| O1 | Confirmar en la ficha la nota y la cantidad de reseñas en Google y Yelp (D3) | Desbloquea T2 |
| O2 | Cambiar el link de la ficha a `https://www.…/` | Ya pedido en el plan de la home |
| O3 | Revisar la categoría principal de la ficha y sumar unas 4 secundarias | Es el factor número 1 del mapa local |
| O4 | Reclamar o verificar Bing Places y Apple Business Connect | Bing alimenta a ChatGPT y Copilot |
| O5 | Ficha en BBB, y reclamar y responder Yelp | Son fuentes de los buscadores con IA |
| O6 | Corregir la dirección en BuildZoom y cargar el negocio en Data Axle y Foursquare | Consistencia de nombre, dirección y teléfono |
| O7 | Pasar proyectos, fotos y testimonios por ciudad (D4-A) | Desbloquea T5.2 |
| O8 | Autoridad local: cámara de comercio de Boynton Beach, página de instalador autorizado del fabricante de pavers, prensa local | Autoridad hoy en 2/10 |
| O9 | Coordenadas de la ficha (D6) | Completa `geo` |

## Commits sugeridos

1. `chore(seo): script de similitud entre páginas de ciudad`
2. `chore(ui): componentes del layout de servicio con <x-responsive-image>`
3. `perf(paver-driveways): variantes AVIF/WebP y recortes mobile de las fotos`
4. `feat(seo): titles cortos en las páginas de servicio`
5. `feat(seo): GeneralContractor y areaServed del servicio`
6. `fix(content): reseñas desde una sola fuente con el dato real`
7. `feat(paver-driveways): páginas de ciudad sin secciones repetidas`
