# Plan SEO — sitio público Labento

> **Estado:** **P0+P1 implementados (2026-08-04).** Pendiente ops: GSC + dominio prod.  
> **Ámbito:** marketing público (`/`, `/registro`) y señales de crawl.  
> **No aplica** al backoffice (admin / empleado / lab / super) como canal de adquisición.  
> **Depende de:** dominio canónico de producción; copy pricing honesto  
> (`$0 en desarrollo · precios por determinarse`).  
> **Complementa:** `MANUAL_IDENTIDAD_PUBLICA.md`, `PLAN_LANDING_REGISTRO.md`.

---

## 1. Objetivo de producto

Que un dueño de laboratorio clínico que busca software en Google pueda:

1. **Encontrar** Labento (indexación limpia de la landing).
2. **Entender** qué es (caja + lab + resultados, SaaS).
3. **Confiar** lo suficiente para ir a `/registro`.

**Outcome (4–8 semanas post-implementación Now):**

- `/` y `/registro` aparecen como indexados en Search Console.
- Impresiones orgánicas > 0 (brand y non-brand).
- CTR de SERP medible con title/description controlados.

**Guardrail:** no indexar portal de resultados (`/r/*`, `/s/*`), login ni rutas de app autenticada.

---

## 2. Diagnóstico (código actual, 2026-07-30)

### 2.1 Lo que ya está bien

| Señal | Dónde | Nota |
|-------|--------|------|
| `lang="es"` | `layouts/public.php` | OK |
| `<title>` + meta description | Controllers `Home`, `Registro` + partial OG | OK |
| Canonical | `partials/opengraph.php` | OK |
| Open Graph + Twitter Card | mismo partial + `img/og-cover.jpg` | OK (1200×630, marca **Labento**) |
| Jerarquía H1 → H2 → H3 | `public/landing.php` | Un H1; FAQ con H3 |
| FAQ en HTML real | acordeón Bootstrap | No depende solo de JS |
| Portal noindex | `layouts/portal.php` | `noindex, nofollow` |
| Errores noindex | `errors/html/*` | OK |

### 2.2 Bloqueantes / gaps

| Gap | Severidad | Estado (2026-08-04) |
|-----|-----------|---------------------|
| `robots.txt` con `Disallow: /` | **P0** | **Resuelto** — `Seo::robots` Allow + Disallow app/portal |
| Sin `sitemap.xml` | P1 | **Resuelto** — `GET /sitemap.xml` (`Seo::sitemap`) |
| Sin JSON-LD | P1 | **Resuelto** — `partials/jsonld_landing.php` |
| Login sin `noindex` | P2 | **Resuelto** — `noindex, follow` en login; backoffice `noindex, nofollow` |
| Superficie de contenido fina | P2 | Abierto (Next: legales / contenido) |
| Favicon no enlazado en layout público | P3 | **Resuelto** — `layouts/public.php` + login |

### 2.3 Keyword primaria (propuesta)

| Tipo | Query |
|------|--------|
| **Primaria** | software para laboratorios clínicos |
| Secundarias | sistema de laboratorio clínico, LIMS en la nube, software de laboratorio con punto de venta |
| Brand | Labento, lab soft |

No hacer keyword stuffing en el H1 de conversión. Preferir KW en **title**, **eyebrow/lead** y schema; H1 puede seguir siendo beneficio-first.

---

## 3. Quick wins (Now) — orden de implementación

Priorización ICE. Estimaciones para un dev familiar con el repo.

### QW-1 · `robots.txt` — desbloquear marketing, proteger app

| | |
|--|--|
| **Esfuerzo** | 15–30 min |
| **Impacto** | Crítico — sin esto no hay SEO |
| **Archivo** | `public/robots.txt` |

**Propuesta de contenido** (ajustar paths si cambian rutas):

```txt
User-agent: *
Allow: /

# App autenticada / operativa
Disallow: /dashboard
Disallow: /inicio
Disallow: /ordenes
Disallow: /super
Disallow: /cotizacion
Disallow: /pendientes
Disallow: /capturas
Disallow: /ayuda
Disallow: /documentacion

# Portal de resultados (también noindex en HTML)
Disallow: /r/
Disallow: /s/

# Opcional: Disallow: /login  (preferible noindex en HTML, ver QW-4)

Sitemap: https://DOMINIO-PRODUCCION/sitemap.xml
```

**Aceptación**

- [ ] No hay `Disallow: /` global en producción.
- [ ] Staging (si existe) mantiene bloqueo total o auth básica.
- [ ] `Sitemap:` apunta al dominio canónico real.

**Kill:** si el host aún es solo staging privado, **no** abrir robots hasta tener dominio de marketing.

---

### QW-2 · `sitemap.xml` mínimo

| | |
|--|--|
| **Esfuerzo** | 30–60 min |
| **Impacto** | Alto (discovery) |

**URLs**

| URL | priority | changefreq | Incluir |
|-----|----------|------------|---------|
| `/` | 1.0 | weekly | Sí |
| `/registro` | 0.9 | monthly | Sí |
| `/login` | — | — | **No** (o priority baja + noindex) |

**Implementación**

- **Simple:** `public/sitemap.xml` estático con URLs absolutas de prod.
- **Mejor:** ruta `GET /sitemap.xml` con `base_url()` para no hardcodear dominio.

**Aceptación**

- [ ] Sitemap 200 y XML válido.
- [ ] Referenciado en `robots.txt`.
- [ ] Enviado en Google Search Console.

---

### QW-3 · JSON-LD: `SoftwareApplication` + `FAQPage`

| | |
|--|--|
| **Esfuerzo** | 1–2 h |
| **Impacto** | Alto (rich results + semántica) |
| **Dónde** | p. ej. `app/Views/partials/jsonld_landing.php` solo en landing |

**Esquemas**

1. **`SoftwareApplication`** (o `WebApplication`): name, description, `applicationCategory` (`BusinessApplication`), `operatingSystem` (`Web`), `offers` (price `0`, currency `MXN`, nota de fase desarrollo), url, `inLanguage` `es-MX`.
2. **`FAQPage`**: mapear el array `$faqs` de `landing.php` → `Question` / `acceptedAnswer` (misma fuente que el acordeón).
3. Opcional: **`Organization`** + **`WebSite`**.

**Regla de producto:** el offer a $0 debe decir fase de desarrollo / precios por determinarse — no “gratis para siempre”.

**Aceptación**

- [ ] `<script type="application/ld+json">` en HTML de `/`.
- [ ] Sin errores críticos en Rich Results Test.
- [ ] FAQ schema = mismo texto que UI.

---

### QW-4 · Política `robots` por tipo de página

| Página | Política |
|--------|----------|
| `/`, `/registro` | index, follow (default) |
| `/login` | **noindex, follow** (meta en `auth/login.php`) |
| `/r/*`, `/s/*` | ya: noindex, nofollow |
| Errores | ya: noindex |
| Backoffice | auth; opcional header `X-Robots-Tag: noindex` |

**Aceptación**

- [ ] View-source de `/login` incluye `noindex`.
- [ ] Landing y registro **no** llevan noindex.

---

### QW-5 · Alinear title / lead a keyword primaria

| | |
|--|--|
| **Esfuerzo** | 45–90 min |
| **Dueño** | product + eng |

| Elemento | Dirección |
|----------|-----------|
| `<title>` | `Labento — Software para laboratorios clínicos` (≤ ~60 chars útiles; `$0` puede ir al description) |
| H1 | Preferir **mantener beneficio** actual; no forzar KW si degrada conversión |
| Meta description | KW + CTA + $0 / sin tarjeta / precios TBD (~150–160 chars) |
| Lead / eyebrow | Reforzar “software de laboratorio clínico…” de forma natural |

**Aceptación**

- [ ] Title único por URL (landing ≠ registro).
- [ ] Description honesta con pricing.
- [ ] Un solo H1 por página.

---

### Bonus mismo día (~10 min)

- [ ] `<link rel="icon" href="…/favicon.ico">` en `layouts/public.php`.
- [ ] Valorar `lang="es-MX"` alineado a `og:locale`.
- [ ] Propiedad en Google Search Console + Analytics (o Plausible) en dominio prod.

---

## 4. Orden de un día (checklist)

```text
1. QW-1 robots.txt
2. QW-4 noindex /login
3. QW-2 sitemap
4. QW-3 JSON-LD
5. QW-5 copy title/description
6. GSC: verificar propiedad + enviar sitemap
```

---

## 5. Now / Next / Later

| Horizonte | Bets | Outcome |
|-----------|------|---------|
| **Now** | QW-1 … QW-5 | Sitio indexable + semántica clara |
| **Next** (2–4 sem) | GSC estable; `/precios` o ancla fuerte; `/privacidad` y `/terminos` (confianza + SEO); medir queries reales | Impresiones y términos no-brand |
| **Later** | Contenido por intent (guías, comparativas honestas); CWV/performance; hreflang si multi-país | Tráfico orgánico → registro |

### Explicit non-goals (primer sprint)

- Blog o decenas de landings sin baseline de GSC.
- SEO del backoffice como canal.
- Schema `Review` sobre escenarios por rol (no son reseñas de terceros).
- Comprar backlinks.
- Optimización profunda de Core Web Vitals antes de indexación limpia.

---

## 6. Métricas

| Métrica | Fórmula | Ventana | Fuente |
|---------|---------|---------|--------|
| Indexación | `/` y `/registro` en estado indexed | 14–28 días post-deploy | GSC → Pages |
| Impresiones orgánicas | sum(impressions) brand + non-brand | 28 días rolling | GSC |
| CTR orgánico landing | clicks / impressions en `/` | 28 días | GSC |
| Guardrail conversión | registros trial / visitas `/registro` | semanal | analytics + tenants `origen=public` |

### Kill criteria (SEO v1)

Si en **45 días** tras QW-1 + QW-2 + GSC no hay **ninguna** impresión ni “indexed” en `/`:

1. No invertir en contenido largo.
2. Revisar dominio canónico, HTTPS, GSC, bloqueos de hosting/CDN, y de nuevo `robots.txt`.

---

## 7. Mapa de archivos tocados (al implementar)

| Archivo | Cambio |
|---------|--------|
| `public/robots.txt` | Allow público + Disallow app + Sitemap |
| `public/sitemap.xml` **o** controller/ruta | URLs canónicas |
| `app/Views/partials/jsonld_landing.php` (nuevo) | Schema |
| `app/Views/public/landing.php` o `layouts/public.php` | Incluir JSON-LD |
| `app/Views/auth/login.php` | meta robots noindex |
| `app/Controllers/Home.php` / `Registro.php` | title/description finales |
| `app/Views/layouts/public.php` | favicon opcional; lang |
| `tests/unit/LandingRegistroTest.php` | asserts robots/schema/opengraph si aplica |

---

## 8. Bitácora

| Fecha | Avance |
|-------|--------|
| 2026-07-30 | Diagnóstico HTML + plan QW-1…5 documentado. Implementación pendiente. |
| 2026-08-04 | **P0+P1 implementados:** `Seo` controller (`/robots.txt`, `/sitemap.xml`), JSON-LD (`SoftwareApplication` + `Organization` + `WebSite` + `FAQPage`), noindex login + layouts backoffice, titles/descriptions canónicos, `lang=es-MX` + favicon en layout público, tests en `LandingRegistroTest`. **Ops pendiente:** verificar propiedad en Google Search Console y enviar sitemap en dominio de producción. |
| 2026-08-06 | Sitemap de marketing con 4 URLs (`/`, `/registro`, `/privacidad`, `/terminos`) + `lastmod`. Estrategia de IA/crawl en [`SITEMAP_SEO.md`](./SITEMAP_SEO.md) (pilares de marca → clusters Later; gobernanza de URLs nuevas). |

---

## 9. Referencias internas

- Identidad y secciones marketing: `MANUAL_IDENTIDAD_PUBLICA.md`
- Landing + registro trial: `PLAN_LANDING_REGISTRO.md`
- Contexto general: `ANALISIS_CONTEXTO.md`
- Partial actual OG: `app/Views/partials/opengraph.php`
- Landing: `app/Views/public/landing.php`
- Robots actual (bloqueante hasta QW-1): `public/robots.txt`
