Recap Redesign — Handoff Prompt Goal Replace the current aggregated /recap view with a two-level drill-down: machine grid → machine-specific 24h detail. One machine = one clear story. No mixed averages. Architecture /recap → grid of machine cards (overview) /recap/[machineId] → full recap detail for one machine Level 1: /recap (grid) Layout Reuse the pattern from app/(app)/machines/MachinesClient.tsx. Same card grid, same responsive breakpoints, same filters (location, status). Card contents (per machine) Header: machine name + location + status dot (green=running, amber=mold-change, red=stopped, gray=offline) Big number: today's OEE % (or good parts — pick one primary metric and stick with it) Secondary row: good parts · scrap · stops count Mini timeline bar: compressed 24h bar (height 20px), same color coding as detail page. No labels, tooltip only. Clicking anywhere navigates to detail. Footer: "Última actividad hace X min" or current WO id if active Banners (inline, colored): If mold-change active → amber: "Cambio de molde en curso · Xm" If machine offline >10 min → red: "Sin señal hace Xm" Data source New endpoint app/api/recap/summary/route.ts — returns array of per-machine summaries in one query. Cache 60s. GET /api/recap/summary?hours=24 → { machines: [{ machineId, name, location, status, oee, goodParts, scrap, stopsCount, lastSeenMs, activeWorkOrderId, moldChange: { active, startMs } | null, miniTimeline: Segment[] // compressed, max ~30 segments }] } Empty / loading states Skeleton cards while loading (pulse animation, same size as real card). Zero-activity machine: card renders but with "Sin producción" muted text, gray mini bar, metric "—". Level 2: /recap/[machineId] Layout (top to bottom) Back arrow + machine name breadcrumb — ← Todas las máquinas / M4-5 Range picker — 24h / Turno actual / Ayer / Personalizado (top-right) Banners — mold-change / offline / ongoing-stop (full-width, colored) KPI row (4 cards) — OEE, Buenas, Paros totales (min), Scrap Timeline 24h — full-width smooth bar (see fix from previous message: min 1.5% width, no dots, merged consecutive stops) Two-column row: Left: Producción por SKU (table) — SKU | Buenas | Scrap | Meta | Avance% Right: Top downtime (pareto) — top 3 reasons with minutes + percent Work orders — two side-by-side lists: Completadas: id, SKU, parts, duration Activa: id, SKU, progress bar, started-at Estado máquina — last heartbeat, uptime %, connection status Data source Endpoint app/api/recap/[machineId]/route.ts — the detailed payload (shape I already documented in the earlier handoff). Cache 60s keyed by {machineId, range}. Navigation Sidebar "Resumen" stays → routes to /recap grid. MachineCard onClick → router.push('/recap/' + machineId). Breadcrumb on detail page navigates back to grid. Deep link safe: /recap/ works standalone. Shared components Build these in components/recap/: RecapMachineCard.tsx — the grid card. Props: machine summary object. RecapMiniTimeline.tsx — 20px-high compressed bar, no labels, tooltip only. RecapFullTimeline.tsx — 48-56px bar, labels on wide segments (>5% width), minimum segment width 1.5%, rounded only on first/last child. RecapKpiRow.tsx — reused from prior design. RecapProductionBySku.tsx, RecapDowntimeTop.tsx, RecapWorkOrders.tsx, RecapMachineStatus.tsx — detail-page sections. RecapBanners.tsx — mold-change / offline / ongoing-stop alert bars. Timeline specifics (fix the ugly-dots issue) Both mini and full versions share segment-builder logic. Server-side: Walk 24h chronologically, produce raw segments from MachineCycle, MachineEvent, MachineWorkOrder. Gap-fill — any time between segments with no data → idle segment. Merge pass — consecutive same-type segments separated by <30s → merge. Absorb micro-runs — runs of microstops closer than 60s → single microstop-cluster segment with aggregated duration and count. Minimum display width — server returns raw segments; client enforces Math.max(1.5, pct) so nothing renders as a dot. Client: display: flex; overflow: hidden; border-radius: 0.75rem on container. Each child: width % only, no margin, no gap, no border-right. Only labels if segment width >5% (else title tooltip). Color map exactly: production: bg-emerald-500 text-black mold-change: bg-sky-400 text-black macrostop: bg-red-500 text-white microstop: bg-orange-500 text-black idle: bg-zinc-700 text-zinc-300 i18n Every user-visible string routes through useI18n(). Add to Spanish locale (primary): recap.grid.title = "Resumen de máquinas" recap.grid.subtitle = "Últimas 24h · click para ver detalle" recap.detail.back = "Todas las máquinas" recap.card.oee = "OEE" recap.card.good = "Piezas buenas" recap.card.stops = "Paros" recap.banner.moldChange = "Cambio de molde en curso desde {time}" recap.banner.offline = "Sin señal hace {min} min" recap.banner.ongoingStop = "Máquina detenida hace {min} min" recap.production.bySku = "Producción por SKU" recap.downtime.top = "Top paros" ... English keys mirror. Accessibility / responsive Cards collapse to single column <640px. Timeline stays readable — horizontal scroll if really tight. Keyboard navigable: cards are