# Rediseño Bento del prototipo Pulso · spec de diseño

- **Fecha:** 22 de septiembre de 2026
- **Estado:** diseño aprobado por Fede (Ticksy) tras revisar el mockup renderizado
- **Estilo:** Bento Box Grid (ui-ux-pro-max, `style id = bento-box-grid`), aplicado sobre el Ticksy Design System 2.0 sin tocar sus tokens
- **Referencias:** [mockup aprobado](assets/2026-09-22-pulso-bento-mockup.html) (necesita el servidor del prototipo en `http://localhost:8765`) y [generador del gráfico](assets/2026-09-22-pulso-bento-gen-chart.py)

## 1. Contexto

El prototipo es el Dashboard Ticksy para hosteleros: 14 pantallas HTML sin build, móvil de 390 px, tema `tpv-dark` por defecto y `light` opcional. Todo el color viene de `css/tokens.css` (TDS) y de la capa `--app-*` de `css/app.css`. Los componentes Icon, Badge, Button y Toggle se montan reales desde el bundle React del TDS con `data-ds`. El layout de cada tarjeta y su tipografía van inline en cada elemento.

Se quiere rediseñar el prototipo con el estilo Bento Box Grid. El estilo decide layout, jerarquía, profundidad, gráficos y movimiento. Los tokens del TDS (color, Rubik 300/400, espaciado, radios, sin emoji, etiquetas en versalitas) se quedan como están.

El generador `--design-system` del plugin no sirve aquí: está orientado a landing pages y en tres consultas nunca eligió Bento ni respetó la paleta. Por eso el `MASTER.md` se escribe a mano siguiendo la plantilla del plugin (sección 12) y la base de datos del plugin se usa por dominios (`style`, `ux`, `motion`).

## 2. Decisiones aprobadas

1. Estilo Bento Box Grid; tokens TDS intactos.
2. Enfoque técnico A: capa CSS en `css/app.css` y sustitución del markup inline de tarjeta por clases con span en las 14 pantallas. Sin migrar a React ni añadir dependencias.
3. Rejilla de 6 columnas con flujo `dense`; spans 6 (ancho), 3 (mitad), 2 (tercio) y 2 filas (alto).
4. Sin sombras, como manda el TDS en TPV. Jerarquía por capas de fondo y borde. Se elimina `--app-hero-shadow`.
5. Gráfico de Ventas nuevo, variante B: curva suavizada con media móvil de 3 días, área degradada, línea gris de lo esperado, rejilla semanal con fechas, punto de hoy con halo y tooltip. Sustituye a la sparkline con banda.
6. En el gráfico, el punto destacado es hoy. Los tres últimos días se funden gradualmente hacia el valor real para que el punto caiga sobre la línea.
7. Escala Y del gráfico ajustada a la serie dibujada, con un 6 % de aire.
8. Chips de periodo a 44 px de alto.
9. Movimiento dial 3: aparición escalonada de tiles al cargar, en CSS, anulada con `prefers-reduced-motion`.
10. Copiloto con override de página en estilo AI-Native UI.
11. Regla de tamaño de tile: el dinero es hero, la sección con avisos activos crece en alto, la que lleva gráfico de barras ocupa el ancho, el resto cuadrado.

## 3. Alcance

**Dentro:** las 14 pantallas, `css/app.css`, `design-system/pulso/MASTER.md`, `design-system/pulso/pages/copiloto.md`, README, verificación en los dos temas.

**Fuera:** migración a React/Vite, datos reales o API, GSAP u otra dependencia, breakpoints de escritorio (el prototipo es 390 px fijo por diseño), cambios en el bundle del TDS, el gráfico antes/después de `decisiones.html` (se mantiene tal cual), inicializar git.

## 4. Archivos y orden de trabajo

| Archivo | Cambio |
|---|---|
| `css/app.css` | Añadir la capa Bento (sección 5) y el gráfico (sección 6). Quitar `.card`, `.card--link` y `--app-hero-shadow` cuando ninguna pantalla los use. `.chip` pasa a 44 px. |
| `index.html` | Dashboard con el mapa de la sección 7 y el gráfico nuevo. |
| `ventas.html` … `ritmo.html` (9) | Secciones de nivel 1 con el mapa de la sección 8. `ventas.html` lleva el gráfico nuevo. |
| `aviso.html`, `decisiones.html`, `costes.html` | Nivel 2, sección 9. |
| `copiloto.html` | Override AI-Native, sección 10. |
| `design-system/pulso/MASTER.md`, `design-system/pulso/pages/copiloto.md` | Nuevos, sección 12. |
| `README.md` | Sección 13. |
| `.claude/launch.json` | Ya existe con el servidor del prototipo (`proto-pulso`, puerto 8765). Quitar la entrada `pulso-mockups` al terminar: apunta al scratchpad de esta sesión. |

Orden: 1) capa CSS + `MASTER.md`; 2) `index.html` y captura en los dos temas como patrón de referencia; 3) las 9 secciones; 4) nivel 2 y Copiloto; 5) README, limpieza de `.card` y verificación final.

## 5. Capa Bento en `css/app.css`

### 5.1 Medidas

Pantalla de 390 px, `.content` con padding 16 → 358 px útiles. Seis columnas con gap 16: columna de 46,33 px. Tile mitad (3 col) = 171 px, tercio (2 col) = 108,67 px, ancho (6 col) = 358 px. Padding interno de tile 16, radio `--radius-xl` (20 px), alto mínimo 132 px.

### 5.2 Contrato CSS

Este bloque va en `css/app.css`, detrás de las variables `--app-*`. Los nombres de clase son el contrato para las 14 pantallas.

```css
/* ---- Variables de aplicación nuevas (añadir a los bloques por tema) ---- */
:root, :root[data-theme="tpv-dark"] { --app-tip-bg: var(--bg-surface-highlight); }
:root[data-theme="light"], :root[data-theme="brand"] { --app-tip-bg: var(--bg-surface); }
/* Eliminar --app-hero-shadow de ambos bloques. */

/* ---- Chips ------------------------------------------------------------- */
.chip { height: 44px; }

/* ---- Rejilla Bento ----------------------------------------------------- */
.bento {
  display: grid;
  grid-template-columns: repeat(6, minmax(0, 1fr));
  grid-auto-flow: dense;
  gap: var(--space-16);
}
.bento__heading {                       /* rótulo de bloque dentro de la rejilla */
  grid-column: span 6;
  margin: var(--space-8) 0 calc(-1 * var(--space-8));
  font-size: 14px; line-height: 16px; font-weight: 400;
  letter-spacing: 1.4px; text-transform: uppercase;
  color: var(--text-accent);
}

/* ---- Tile -------------------------------------------------------------- */
.tile {
  grid-column: span 3;
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
  min-height: 132px;
  padding: var(--space-16);
  background: var(--bg-surface);
  border: 1px solid var(--border-default);
  border-radius: var(--radius-xl);
  color: inherit;
  text-decoration: none;
  transition: transform 150ms ease, border-color 150ms ease;
}
.tile--wide  { grid-column: span 6; }
.tile--third { grid-column: span 2; }
.tile--tall  { grid-row: span 2; }
.tile--hero  { grid-column: span 6; background: var(--bg-surface-raised); }
a.tile:hover { border-color: var(--border-strong); color: inherit; }
a.tile:active { transform: scale(0.98); }
a.tile:focus-visible { outline: 2px solid var(--action-primary); outline-offset: 2px; }

/* Anatomía */
.tile__head { display: flex; align-items: center; gap: var(--space-8); color: var(--text-accent); }
.tile__label { margin: 0; font-size: 14px; line-height: 16px; font-weight: 400; letter-spacing: 1.4px; text-transform: uppercase; }
.tile__chevron { margin-left: auto; color: var(--text-subtle); display: inline-flex; }
.tile__figure { display: flex; align-items: baseline; gap: 6px; font-size: 28px; line-height: 36px; font-weight: 300; color: var(--text-primary); }
.tile--hero .tile__figure { font-size: 44px; line-height: 52px; }
.tile__figure--m { font-size: 33px; line-height: 40px; }          /* cifra de tarjeta grande, si hace falta */
.tile__unit { font-size: 15px; line-height: 20px; font-weight: 300; color: var(--app-text-support); }
.tile__note { margin: 0; font-size: 13px; line-height: 18px; font-weight: 400; color: var(--app-text-support); }
.tile__note--ok   { color: var(--app-status-ok-text); }
.tile__note--warn { color: var(--app-status-warn-text); }
.tile__note--bad  { color: var(--app-status-bad-text); }
.tile__note--light { font-weight: 300; }
.tile__text { margin: 0; font-size: 14px; line-height: 20px; font-weight: 400; color: var(--text-primary); }
.tile__body { margin: 0; font-size: 16px; line-height: 22px; font-weight: 300; color: var(--text-primary); }
.tile__row { display: flex; align-items: center; gap: var(--space-16); flex-wrap: wrap; }
.tile__row--tight { gap: 14px; }
.tile__row > .grow { flex-grow: 1; }
.tile__divider { height: 1px; background: var(--border-subtle); }
.tile__divider--push { margin-top: auto; }                        /* empuja lo que sigue al pie de la tile */
.tile__bar { display: flex; height: 8px; border-radius: 6px; overflow: hidden; background: var(--border-subtle); }
.tile__legend { display: flex; align-items: center; gap: 6px; font-size: 13px; line-height: 18px; font-weight: 300; color: var(--app-text-support); }
.tile__dot { width: 10px; height: 10px; border-radius: 5px; flex-shrink: 0; }
.tile__strong { color: var(--text-primary); font-weight: 400; }

/* Filas de dato (listas "Por franja", "Por canal", "Lo más vendido") */
.tile__kv { display: flex; align-items: center; gap: var(--space-8); }
.tile__kv-label { width: 96px; flex-shrink: 0; margin: 0; font-size: 13px; line-height: 18px; font-weight: 400; color: var(--app-text-support); }
.tile__kv-value { width: 56px; flex-shrink: 0; margin: 0; text-align: right; font-size: 13px; line-height: 18px; font-weight: 400; color: var(--text-primary); }
.tile__kv-track { flex-grow: 1; height: 8px; border-radius: 6px; background: var(--border-subtle); overflow: hidden; }
.tile__kv-fill { height: 100%; border-radius: 6px; background: var(--app-chart-accent); }
.tile__kv-fill--ink { background: var(--app-chart-ink); }

/* ---- Movimiento (dial 3) ----------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
  .bento > .tile {
    animation: tile-in 250ms ease-out backwards;
    animation-delay: calc(var(--i, 0) * 30ms);
  }
  @keyframes tile-in {
    from { opacity: 0; transform: translateY(8px); }
    to   { opacity: 1; transform: none; }
  }
}
```

Reglas de uso:

- `main.content` separa sus bloques (línea de contexto, chips, rejilla) con `gap: var(--space-16)`, en la clase `.content`, no inline. Hoy cada pantalla lo lleva inline con 12.
- Una tile que navega es `<a class="tile …" href="…">`; una informativa es `<div class="tile …">`. El chevron `nav_forward` solo va en las que navegan.
- Cada tile del Dashboard y de las secciones lleva `style="--i:N"` con N = su orden en la rejilla, empezando en 0. Es el único inline permitido en la tile además de los anchos de barra (`width:45%`), la posición del tooltip y la geometría de los SVG.
- Queda prohibido el inline para tipografía, color, padding, gap y flex de la anatomía. Si un caso no cabe en las clases de arriba, se añade una clase, no un `style`.
- La cifra grande de la hero es 44/52 y la de las demás tiles 28/36, ambas Rubik 300. La escala no cambia respecto al prototipo actual, solo pasa de inline a clase.
- `.tile--tall` alinea su fila con las dos tiles de al lado: la vecina inferior se estira. El contenido de todas se alinea arriba (columna flex).
- La anatomía `label_row → cifra → línea de estado` se mantiene como está en el prototipo actual.

## 6. Gráfico de línea de Ventas

Se usa en la hero de `index.html` y en la tarjeta "Hoy" de `ventas.html`. Reemplaza al SVG actual de 326 × 72 con banda.

### 6.1 Estructura

```html
<div class="chart">
  <svg viewBox="0 0 326 120" preserveAspectRatio="none" aria-label="Ventas de los últimos 30 días frente a lo esperado">
    <defs><linearGradient id="grad-ventas" x1="0" y1="0" x2="0" y2="1">
      <stop offset="0" class="chart__grad-a"/><stop offset="1" class="chart__grad-b"/>
    </linearGradient></defs>
    <line class="chart__grid" x1="…" y1="0" x2="…" y2="120"/>   <!-- 4 lunes -->
    <path d="…" fill="url(#grad-ventas)"/>                     <!-- área bajo la real -->
    <path d="…" class="chart__expected"/>                      <!-- esperado -->
    <path d="…" class="chart__line"/>                          <!-- venta real suavizada -->
    <circle class="chart__halo" cx="318" cy="…" r="9"/>
    <circle class="chart__dot"  cx="318" cy="…" r="4"/>
  </svg>
  <div class="chart__tip" style="right:calc(100% - 97.5% + 16px);top:…px;">
    <span class="chart__tip-date">Hoy · 27 may</span>
    <span class="chart__tip-value">2.957 €</span>
  </div>
  <div class="chart__axis">
    <span style="left:20.2%;">4 may</span><span style="left:43.7%;">11 may</span>
    <span style="left:67.3%;">18 may</span><span style="left:90.8%;">25 may</span>
  </div>
</div>
<div class="tile__row tile__row--tight">
  <span class="tile__legend"><span class="tile__dot" style="background:var(--app-chart-accent);"></span>venta real · 30 días</span>
  <span class="tile__legend"><span class="tile__dot" style="background:var(--text-secondary);"></span>esperado</span>
</div>
```

Cada `id` de degradado es único por documento (`grad-ventas` en la hero; si hubiera dos gráficos en una pantalla, `grad-ventas-2`).

### 6.2 CSS (añadir a `css/app.css`)

```css
.chart { position: relative; }
.chart svg { display: block; width: 100%; height: 120px; overflow: visible; }
.chart__grid { stroke: var(--border-default); stroke-width: 1; stroke-dasharray: 3 5; }
.chart__line { fill: none; stroke: var(--app-chart-accent); stroke-width: 2; stroke-linecap: round; }
.chart__expected { fill: none; stroke: var(--text-secondary); stroke-width: 1.25; opacity: 0.8; }
.chart__grad-a { stop-color: var(--app-chart-accent); stop-opacity: 0.32; }
.chart__grad-b { stop-color: var(--app-chart-accent); stop-opacity: 0; }
.chart__halo { fill: var(--app-chart-accent); opacity: 0.25; }
.chart__dot { fill: var(--app-chart-accent); stroke: var(--bg-surface-raised); stroke-width: 2; }
.chart__axis { position: relative; height: 18px; margin-top: 6px; border-top: 1px solid var(--border-subtle); padding-top: 4px; }
.chart__axis span { position: absolute; transform: translateX(-50%); font-size: 13px; line-height: 18px; font-weight: 300; color: var(--app-text-support); white-space: nowrap; }
.chart__tip { position: absolute; display: flex; flex-direction: column; gap: 2px; padding: 8px 12px; border-radius: var(--radius-sm); background: var(--app-tip-bg); border: 1px solid var(--border-default); white-space: nowrap; }
.chart__tip-date { font-size: 12px; line-height: 16px; font-weight: 300; color: var(--app-text-support); }
.chart__tip-value { font-size: 16px; line-height: 20px; font-weight: 400; color: var(--text-primary); }
```

### 6.3 Algoritmo de trazado

Los datos de partida son los 30 pares de las dos `polyline` del SVG actual de `index.html`: la de trazo 2 px es la venta real, la punteada es la media esperada. Sus `y` están en la caja de 72 px del gráfico viejo; se tratan como valores (menor `y`, más venta). La escala aproximada es 24,1 €/px: hoy (`y = 15,7`) son 2.957 € y lo esperado (`y = 32,1`) 2.562 €.

1. **Suavizado de la real (variante B):** media móvil centrada de 3 puntos. En los tres últimos puntos se mezcla con el valor real con pesos 1/3, 2/3 y 1 (el último punto es el valor real exacto), para que hoy caiga sobre la línea sin escalón.
2. **Esperado:** sin suavizar.
3. **Escala X:** `x_i = i / 29 × 318`. Los 8 px restantes hasta 326 dejan sitio al halo.
4. **Escala Y:** dominio = mínimo y máximo de la real suavizada junto con la esperada, ampliado un 6 % por cada lado; se proyecta en `[10, 110]`.
5. **Curvas:** interpolación cúbica monótona de Fritsch–Carlson convertida a segmentos Bézier cúbicos. No rebasa los datos.
6. **Área:** el trazado de la real cerrado con `L x_29,120 L x_0,120 Z`, relleno con el degradado.
7. **Rejilla y fechas:** líneas verticales en los lunes, índices 6, 13, 20 y 27 → "4 may", "11 may", "18 may", "25 may", centradas con `translateX(-50%)`.
8. **Hoy:** halo `r = 9` y punto `r = 4` en `(x_29, y_29)`. Tooltip a la izquierda del punto, centrado en vertical: `top = max(0, y_29 − 27 px)` para que no invada la línea de estado de encima, `right = calc(100% − 97.5% + 16px)`.

El script [`gen-chart.py`](assets/2026-09-22-pulso-bento-gen-chart.py) implementa exactamente esto (función `chart(win=3, gid)`); el plan de implementación puede reutilizar su lógica para emitir el bloque de las dos pantallas.

## 7. Dashboard (`index.html`)

Orden del DOM, igual que ahora: Ventas, Salud, Márgenes, Canales, Control, Previsión, Carta, Clientes, Ritmo. El flujo `dense` produce este mapa:

```
┌───────────────────────────┐
│ VENTAS · hero             │  --i:0  span 6
├─────────────┬─────────────┤
│ SALUD       │ MÁRGENES    │  --i:1 alta (2 filas) · --i:2
│             ├─────────────┤
│             │ CONTROL     │  --i:4 (dense la sube aquí)
├─────────────┴─────────────┤
│ CANALES · ancha           │  --i:3  span 6
├─────────────┬─────────────┤
│ PREVISIÓN   │ CARTA       │  --i:5 · --i:6
├─────────────┼─────────────┤
│ CLIENTES    │ RITMO       │  --i:7 · --i:8
└─────────────┴─────────────┘
```

Contenido por tile (todo lo demás de la pantalla, barra superior, línea de contexto, chips y tabbar, se mantiene):

- **Ventas** (`tile tile--hero`, enlace): head con `menu_bar_income`; cifra 2.957 €; nota `--ok` "+15 % sobre lo esperado (2.562 €) · buen día"; gráfico de la sección 6; leyenda; divisor; fila de datos 155 pedidos · 19,08 € ticket medio · pico 12–13 h.
- **Salud** (`tile tile--tall`, enlace): head con `menu_status`; cifra 3 + unidad "avisos"; Badge `{"label":"1 URGENTE","tone":"danger"}`; divisor; `tile__text` "Vendiste más y ganaste menos"; `tile__note` "371 € de comisión · te quedaste 71 cts de cada euro, tu normal son 75"; `tile__divider--push`; `tile__note tile__note--light` "2 avisos más · 2 para saber".
- **Márgenes** (`tile`): cifra 71 % con la flecha SVG de aviso actual; nota "media 75 %".
- **Canales** (`tile tile--wide`, enlace): head con `delivery_globe`; `tile__row` con cifra 43 % + unidad "por marketplace" a la izquierda y, a la derecha alineado, `tile__note` "mejor canal" sobre `tile__text` "Webapp" (16/22); `tile__bar` 45 % `--channel-web`, 43 % `--channel-uber-eats`, 12 % `--app-chart-accent`; leyenda "canal propio 45 %", "marketplace 43 %", "directo 12 %" con esos mismos colores (los nombres salen de `canales.html`; el mockup los rotuló mal como Webapp y Uber Eats).
- **Control** (`tile`): cifra 6 + "anulaciones"; nota `--warn` "2 más que un miércoles normal".
- **Previsión** (`tile`): cifra 18.677 €; nota "próximos 7 días".
- **Carta** (`tile`): cifra 5 + "platos"; nota "necesitan cambiar su precio".
- **Clientes** (`tile`): cifra 4,3 + "en Google"; nota `--warn` "3 reseñas sin responder".
- **Ritmo** (`tile`): cifra 23 min; nota "hasta listo · Uber y Just Eat por encima de 30".

## 8. Secciones de nivel 1

Regla general: la primera tarjeta de la pantalla es `tile--hero`; un trío de KPI son tres `tile--third`; cuatro KPI son dos filas de `tile` (mitad); todo lo demás es `tile--wide`. Los rótulos de bloque que hoy van sueltos entre tarjetas pasan a `bento__heading`. Las filas de dato con etiqueta, barra y valor usan `tile__kv`. Los enlaces a otra pantalla conservan su `href` y el chevron.

| Pantalla | Tarjeta actual | Tile |
|---|---|---|
| `ventas.html` | Hoy (2.957 €, gráfico) | hero, con el gráfico de la sección 6 |
| | Pedidos 155 · Ticket 19,08 € · Neto 2.096 € | third × 3 |
| | Por franja (4 filas) | wide, filas `tile__kv` |
| | Un miércoles normal | wide |
| | Próximos 7 días (enlace a previsión) | wide, enlace |
| `salud.html` | Estado de hoy (3, Badge) | hero |
| | Rótulo "Avisos" y sus 3 avisos | `bento__heading` + un `tile--wide` enlace por aviso (a `aviso.html`) con `tile__text` título y `tile__note` explicación |
| | Rótulo "Para saber" y sus 2 avisos | igual que arriba |
| | Hiciste, ganaste (+1.398 €, enlace) | wide, enlace |
| `margenes.html` | De cada euro que paga el cliente (71 %, barra) | hero |
| | Por canal, te queda (7 filas) | wide, `tile__kv` |
| | Lo que deja cada pedido, de verdad (7 filas) | wide, `tile__kv` |
| | Resultado del mes (12.335 €, Badge, enlace a costes) | wide, enlace |
| `canales.html` | Por dónde vendes (43 %, barra) | hero; conserva su contenido: 43 % + "del día por marketplace", nota "tu normal 42 % · máximo 52 %", `tile__bar` y `tile__legend` con canal propio 45 % / marketplace 43 % / directo 12 %, y el párrafo explicativo en `tile__note` |
| | Canal a canal | wide |
| | Y si empujas tu canal propio | wide |
| `control.html` | Hoy (6, Badge) | hero |
| | 48 € · 0,00 € · 3 · 2 | `tile` × 4 en dos filas |
| | Por turno (2 filas) | wide, `tile__kv` |
| | Lo que te cobra la plataforma (Badge) | wide |
| `prevision.html` | Próximos 7 días (18.677 €) | hero |
| | En el radar | wide |
| `carta.html` | Lo más vendido hoy (5 filas) | hero, `tile__kv` |
| | Precios distintos por canal (Badge) | wide |
| | Extras (+3.841 €, Badge, enlace a aviso) | wide, enlace |
| `clientes.html` | Google (Badge) | hero |
| | 4,3 "nota media" · +14 "reseñas este mes" | `tile` × 2 (mitad) |
| | 3 "sin responder" (párrafo + acción Responder) | wide, nota `--warn` |
| | Del escaparate a la venta | wide |
| | Qué escriben para encontrarte | wide |
| | Vuelven (3 filas, Button) | wide, `tile__kv` |
| `ritmo.html` | Hasta listo (23) | hero |
| | Por canal · minutos hasta listo (7 filas) | wide, `tile__kv` |
| | Venta por hora | wide |
| | Lo que dice tu TPV | wide |

Los textos, cifras, Badges y Buttons no cambian. Las tarjetas de KPI ("PEDIDOS", "TICKET", "NETO"…) conservan su rótulo en versalitas arriba como `tile__label`, la cifra como `tile__figure` con la unidad en `tile__unit` y la línea de abajo ("normal 128") como `tile__note`.

## 9. Nivel 2

| Pantalla | Tarjeta actual | Tile |
|---|---|---|
| `aviso.html` | +16.123 € (Badge AVISO) | hero |
| | Plato a plato, en Glovo (tabla) | wide |
| | Párrafos de explicación entre tarjetas | `tile__body` dentro de un `tile--wide tile--plain` (ver abajo) |
| `decisiones.html` | 12 jun · subiste el precio (gráfico antes/después, Badge) | hero, el SVG antes/después se conserva tal cual |
| | +7,4 % · +1.240 € | `tile` × 2 |
| | Tu registro de decisiones | wide |
| `costes.html` | Seis cifras, una vez | wide |
| | 14.000 · 30 (tarjetas de entrada con banda) | wide cada una |
| | Toggle "No lo sé exacto, usa una banda" | wide |
| | El resto (Button GUARDAR) | wide |

Clase adicional para texto corrido entre tiles, si hace falta en `aviso.html` y `salud.html`:

```css
.tile--plain { background: transparent; border-color: transparent; padding: 0 var(--space-4); min-height: 0; }
```

## 10. Copiloto (`copiloto.html`) · override AI-Native UI

Contenido actual: hilo con marca de hora "Hoy · 8:04", pregunta del usuario, respuesta del copiloto con cifras en `text-primary`, dos tarjetas de cita con rótulo en versalitas de 11/14 ("Ticksy · 155 pedidos del 27 may · comisiones que declaraste", "Liquidación Glovo mayo 2026 · cruzada con 2.516 pedidos"), chip de adjunto "Liquidación Glovo · mayo.pdf" con icono `menu_bar_invoice`, fila horizontal de sugerencias ("¿Qué pasa si cierro Glovo?", "Mi alquiler sube a 2.800", "¿Cuánto vendí el sábado?"), aviso "El copiloto solo habla de lo ya calculado…", compositor con `input` "Pregunta lo que quieras", botón de subir documento y botón de enviar, y tabbar.

Override:

- **Hilo** `.chat`: columna con gap 12, sin rejilla Bento. La marca de hora centrada en `tile__note tile__note--light`.
- **Mensaje del usuario** `.msg .msg--user`: burbuja alineada a la derecha, máximo 88 % de ancho, fondo `--action-secondary`, texto `--text-primary` 16/22 Rubik 300, radio `--radius-lg` con la esquina inferior derecha en `--radius-sm`, padding 12 16. El chip de adjunto va dentro de la burbuja.
- **Mensaje del copiloto** `.msg .msg--bot`: bloque alineado a la izquierda, máximo 88 %, fondo `--bg-surface`, borde `--border-default`, radio `--radius-lg` con la esquina inferior izquierda en `--radius-sm`, padding 12 16. Texto 16/22 Rubik 300; las cifras destacadas en `tile__strong`.
- **Citas** `.cite`: dentro del mensaje del copiloto, fondo `--bg-surface-raised`, borde izquierdo de 2 px en `--text-accent`, radio `--radius-sm`, padding 8 12, rótulo 11/14 versalitas en `--text-accent` y cuerpo 13/18 en `--app-text-support`.
- **Sugerencias** `.suggestions`: fila con desplazamiento horizontal (`overflow-x: auto`, sin barra visible), chips `.chip` de 44 px sin `flex-grow` y con `white-space: nowrap`.
- **Compositor** `.composer`: va después de `</main>` y antes de la tabbar, con `position: sticky; bottom: calc(64px + env(safe-area-inset-bottom, 0px))` para quedar pegado encima de la tabbar cuando el hilo es largo; fondo `--bg-surface`, borde superior `--border-default`, padding 8 16. Campo de 44 px de alto, radio 22 px, fondo `--bg-canvas`, borde `--border-default`, texto 16/22. Botones de adjuntar y enviar de 44 × 44 con `aria-label`, el de enviar con fondo `--action-primary` y el icono en `--text-on-accent`.
- **Aviso legal**: `tile__note tile__note--light` centrado, como último elemento del hilo (dentro de `.chat`, detrás de las sugerencias), no entre el compositor y la tabbar.
- No se implementan indicador de escritura ni texto en streaming: el prototipo es estático. Quedan documentados como patrones en `pages/copiloto.md`.

## 11. Tema `light`

Las mismas clases sirven en los dos temas. Puntos a comprobar en la revisión:

- Texto de apoyo `--app-text-support` (gray-600 sobre blanco) ≥ 4,5:1.
- Línea del gráfico `--app-chart-accent` (cyan-700 en claro) sobre `--bg-surface-raised`.
- Notas de estado en claro usan las variantes `-strong` a través de `--app-status-*-text`.
- Tooltip blanco (`--app-tip-bg`) sobre la hero gris claro, con borde.
- Chip activo `--app-chip-selected-bg` sobre `--bg-canvas`.

## 12. `design-system/pulso/MASTER.md` y `pages/copiloto.md`

Se escriben a mano con las secciones de la plantilla del plugin, en este orden: título "Design System Master File", nota de lógica Master + overrides, `Global Rules` (Color Palette, Typography, Spacing Variables, Shadow Depths), `Component Specs` (Buttons, Cards, Inputs, Modals), `Style Guidelines`, `Page Pattern`, `Motion`, `Anti-Patterns (Do NOT Use)`, `Pre-Delivery Checklist`.

Contenido:

- **Cabecera:** proyecto Pulso, estilo `bento-box-grid` (ui-ux-pro-max 2.13.0), generado a mano porque el generador propone paleta y tipografía ajenas; fecha.
- **Color Palette:** ninguna paleta nueva. Tabla de roles → tokens TDS (`--bg-canvas`, `--bg-surface`, `--bg-surface-raised`, `--text-primary`, `--text-accent`, `--action-primary`, `--status-*`, `--channel-*`) y capa `--app-*`. Prohibido el hex en pantallas.
- **Typography:** Rubik 300/400 desde Google Fonts, escala móvil del prototipo (44/52, 33/40, 28/36, 20/24, 16/22, 15/20, 14/16, 13/18, 12/16, 11/14).
- **Spacing:** tokens `--space-*`; gap de rejilla 16, padding de tile 16, radio de tile `--radius-xl`.
- **Shadow Depths:** ninguna en TPV; jerarquía por `bg-canvas → bg-surface → bg-surface-raised → bg-surface-highlight` y borde.
- **Cards:** la sección 5 de esta spec (clases y spans) y la regla de tamaño de tile.
- **Buttons / Inputs / Modals:** remitir al TDS; en el prototipo, Button real del bundle y el compositor del Copiloto.
- **Style Guidelines:** checklist Bento del plugin adaptada: rejilla 6 columnas, spans variados, radios consistentes, sin sombra, hover por borde y pulsación `scale(0.98)`, contenido que cabe en la tile.
- **Page Pattern:** Dashboard con el mapa de la sección 7 y la regla de la sección 8.
- **Motion:** dial 3, la animación `tile-in` y la regla de `prefers-reduced-motion`.
- **Anti-Patterns:** emoji, hex en pantallas, sombras, texto de apoyo por debajo de 13 px, cifras sin unidad o sin línea de estado, color de estado sin palabra.
- **Checklist:** la de la sección 14.

`pages/copiloto.md`: cabecera "Copiloto Page Overrides" con la nota de que sus reglas prevalecen sobre el Master, tipo de página "AI-Native UI (conversational)", y las reglas de la sección 10 más los patrones no implementados (indicador de tres puntos con pulso de 900 ms escalonado 150 ms, texto en streaming) para el producto.

## 13. README

Actualizar la sección "Estructura" con `design-system/pulso/` y `docs/superpowers/`, sustituir la descripción del layout inline por las clases Bento y el gráfico, y en "Cómo está hecho" explicar la regla de tamaño de tile y cómo regenerar el gráfico con el script. Quitar la mención a la banda del rango esperado.

## 14. Verificación y criterios de aceptación

Por pantalla, en `tpv-dark` y `light` a 390 px, con el servidor `proto-pulso`:

- Captura y revisión visual contra el mockup aprobado. En `index.html`, el mapa coincide con la sección 7.
- Sin mensajes en consola de `ds-mount` ni errores.
- `grep -c 'class="card' *.html` devuelve 0 en todas.
- `grep -oE '#[0-9a-fA-F]{6}\b' *.html` devuelve 0 coincidencias.
- Ningún `style="` en una tile salvo `--i`, anchos de barra, posición del tooltip y atributos de SVG.
- Chips de 44 px; tiles con separación de 16; foco visible al tabular; con `prefers-reduced-motion: reduce` las tiles aparecen sin animación.
- Gráfico: el punto de hoy cae sobre la línea, el tooltip dice "Hoy · 27 may" y "2.957 €", las cuatro fechas no se solapan ni se cortan, el halo no queda recortado.
- Checklist de entrega del plugin: sin emoji como icono, `cursor: pointer` en lo pulsable, transiciones de 150–300 ms, contraste 4,5:1 en claro, foco visible, `prefers-reduced-motion` respetado.

## 15. Riesgos y notas

- `grid-auto-flow: dense` cambia el orden visual respecto al DOM solo en Control, que sube junto a Salud. El orden de lectura con lector de pantalla sigue el DOM. Aceptado.
- El componente `Kpi` del TDS pone el rótulo debajo de la cifra y usa la escala del TPV, así que no se usa; la cifra sigue siendo texto con clase.
- `preserveAspectRatio="none"` estira el SVG de 326 a los 324 px interiores de la hero: distorsión despreciable.
- El mockup de referencia carga el TDS por URL absoluta; solo funciona con el servidor del prototipo levantado.
- El directorio no es un repositorio git. Esta spec queda en disco sin commit hasta que se decida inicializarlo.
