# Prompt 4 — Planes, votos, lugares y el cierre al mapa

Requiere la página del prompt 3 conectada a la hoja. Este prompt convierte el
divisor de cuentas en el tablero del grupo, sobre la misma bitácora, y agrega
el botón que al volver siembra tu mapa de viajes (la receta
[mapa de viajes](/kit/mapa-de-viajes/)).

---

Extiende mi divisor de gastos de viaje para que sea también el tablero del
grupo, SIN tocar el motor de gastos ni el almacén: un motor nuevo
`motor_grupo.js` (puro, sin DOM, con pruebas en Node, exportado como
`MotorGrupo` con `reducirGrupo`, `marcador`, `ordenarPlanes`, `paraElMapa` y
`mesDelViaje`) y tres pestañas en la página: Gastos | Planes | Lugares.

Tipos nuevos en la MISMA bitácora de eventos (todos con id propio; `quien` y
`cuando` los pone la captura):

- `{ tipo: 'viaje', nombre, pais, inicio, fin, moneda }` — metadatos; gana el
  último, campo por campo, y SOLO si el campo es texto (un objeto colado no
  debe volverse "[object Object]" en el título).
- `{ tipo: 'plan', id, texto, dia? }` — propuesta. `dia` solo vale si es
  `YYYY-MM-DD`; cualquier otra cosa se trata como "sin día".
- `{ tipo: 'voto', id, plan, valor }` — 1 = sí, 0 = no. Por persona y plan
  gana el ÚLTIMO voto. Un voto a un plan que no existe se ignora; un valor
  que no sea 0 o 1 (o que no sea número ni texto) se ignora sin tronar.
- `{ tipo: 'lugar', id, nombre, ciudad?, estrellas?, nota? }` — dónde
  estuvieron, en caliente. Estrellas 1..5; fuera de rango = 0.
- `{ tipo: 'anulacion_plan', plan }` y `{ tipo: 'anulacion_lugar', lugar }`.

Reglas del motor: un id repetido cuenta UNA vez (la cola offline reintenta);
`ordenarPlanes` pone primero los del día más próximo (los sin día al final);
a igual día, más votos "sí"; a empate, mayor saldo neto (sí menos no); si
persiste, alfabético por id; lo malformado se ignora.

Interfaz: Planes con "proponer" (texto + día) y dos botones Sí/No por plan
con el marcador; Lugares con nombre, estrellas tocables, una nota corta y un
campo "¿En qué ciudad?" que SE QUEDA PUESTO entre lugares (recuérdalo en
localStorage por viaje). Todo por textContent, nada por innerHTML.

Sincronía: en modo hoja, relee la bitácora cada 40 segundos y al volver a la
pestaña del navegador. REGLA DE ORO: la bitácora solo crece, así que la vista
NUNCA encoge — lo leído se UNE por identidad de evento a lo que ya se tenía,
nunca lo reemplaza (una lectura vieja que llega tarde no puede borrar el voto
que acabo de dar). Un latido cada 5 segundos empuja la cola pendiente si la
hay: el evento 'online' del navegador no siempre dispara.

El cierre del viaje (al final de Lugares): un formulario con nombre, país,
desde y hasta, precargados con lo que se sepa: el nombre del viaje, y las
fechas del gasto y el lugar más viejos y más nuevos (no de las personas ni
los planes, que se crean semanas antes de salir). Al guardar: (1) agrega el
evento `viaje`; (2) arma un "recuerdo" con `paraElMapa(estado, mes,
nombresDePersonas)`:

```json
{ "version": 1, "id": "identificador-del-cierre",
  "viaje": { "nombre": "", "pais": "", "inicio": "", "fin": "" }, "fecha": "YYYY-MM",
  "personas": ["nombres"],
  "ciudades": [ { "nombre": "", "puntuacion": 0,
                  "lugares": [ { "nombre": "", "estrellas": 0, "nota": "" } ] } ] }
```

con los lugares agrupados por ciudad (comparando sin acentos ni mayúsculas;
los que no traen ciudad van juntos con nombre vacío para que el mapa lo
pregunte), `puntuacion` = promedio redondeado de las estrellas mayores que
cero, y `fecha` = el mes del inicio dicho, o del viaje, o del lugar más
viejo; cada cierre lleva un `id` propio (uno por sesión de página: "copiar"
y "guardar" dan el mismo); (3) lo APILA en `localStorage['mapa-recuerdo']`
(una lista: dos cierres seguidos no se pisan) y abre
`../mapa/mapa.html?recuerdo=1` (o la ruta donde viva tu mapa). Sin red el
cierre NO navega: navegar dejaría la cola sin quien la suba; avisa que el
viaje queda por subir y deja el recuerdo listo. Un botón "Copiar el
recuerdo" deja el mismo JSON en una caja de texto y en el portapapeles,
para un mapa que viva en otro aparato.

Pruebas en Node para el motor (`pruebas_grupo.js`): un duplicado por id
cuenta una vez; el último voto por persona gana; valores inválidos y votos
sin `quien` no cuentan; el orden de planes; un Symbol o un objeto en un
campo no tumba el reductor; `paraElMapa` agrupa "Kioto", " kioto" y "Kióto"
en una ciudad y promedia (5 y 2 → 4); metadatos que no son texto salen
vacíos.

Del lado del mapa, si sigues la receta de mapa de viajes, pídele a tu IA un
módulo puro `recuerdo.js` (exportado como `MapaRecuerdo`) con:

- `leer(x)`: nunca lanza; recorta y descarta lo que no es texto o número;
  `fecha` solo `YYYY-MM` con un mes real; estrellas solo 0..5; conserva el
  `id` si es texto.
- `empatarPersonas(paquete, personasMapa)`: devuelve `{ ids, invitados }`
  empatando por nombre normalizado (sin acentos ni mayúsculas).
- `fundir(data, paquete, { iso, personas, nombres })`: `personas` son IDS
  del mapa (no nombres) y tienen que existir en `data.personas`. Sin país,
  sin personas válidas, o con un `id` ya apuntado en
  `data.recuerdosAplicados`, no toca nada (en ese último caso devuelve
  `yaAplicado`). Ciudad existente por país + nombre normalizado → suma una
  visita, conserva su primera fecha, toma la puntuación nueva si la hay y
  agrega la nota por entradas completas ("Lugar ★★★ — nota", sin repetir);
  dos grupos del mismo cierre con el mismo nombre final son UNA visita;
  ciudad nueva → se crea con fecha, visitantes y nota. Marca el país para
  esas personas y apunta el `id` del cierre. Devuelve `{ nuevas,
  actualizadas, omitidas, fusionadas, lugares, yaAplicado, personasFuera }`.
- `pendientes(almacen)`, `guardarPendiente(paquete)` y `olvidar(almacen, id)`:
  la llave de localStorage guarda una LISTA y se olvida uno por id.

Y un panel en el mapa que se abre solo si hay un recuerdo pendiente en
localStorage o al pegar el JSON, con el país preseleccionado por nombre, las
personas del mapa empatadas por nombre con las del viaje (si ninguna empata,
NADIE queda marcado y se listan como invitados que no van al mapa) y el
nombre de cada ciudad editable. Al aplicar, guarda de inmediato y olvida el
pendiente SOLO cuando el servidor confirmó el guardado; cerrar o aplicar
limpia el `?recuerdo=1` de la URL — si no, cada recarga reabre el panel.
