# MealMood - Product Requirements Document (PRD)

## 📋 Índice

1. [Visión General](#visión-general)
2. [Problema y Solución](#problema-y-solución)
3. [Usuario Objetivo](#usuario-objetivo)
4. [Propuesta de Valor](#propuesta-de-valor)
5. [Arquitectura de Información](#arquitectura-de-información)
6. [Modelo de Datos](#modelo-de-datos)
7. [Funcionalidades MVP](#funcionalidades-mvp)
8. [Funcionalidades Freemium (Futuro)](#funcionalidades-freemium-futuro)
9. [Identidad Visual y Branding](#identidad-visual-y-branding)
10. [Especificaciones de Pantallas](#especificaciones-de-pantallas)
11. [Interacciones y Micro-animaciones](#interacciones-y-micro-animaciones)
12. [Integración con Calendario Nativo](#integración-con-calendario-nativo)
13. [Algoritmo de Autocompletado](#algoritmo-de-autocompletado)
14. [Sincronización y Persistencia](#sincronización-y-persistencia)
15. [Monetización](#monetización)
16. [Stack Técnico](#stack-técnico)
17. [Plan de Implementación (Sprints)](#plan-de-implementación-sprints)
18. [Anexos](#anexos)

---

## Visión General

### Nombre
**MealMood** 🍽️✨

### Tagline
- **Español**: "Planifica tu semana, disfruta tus comidas"
- **English**: "Plan your week, enjoy your meals"

### Descripción del Producto
MealMood es una aplicación iOS para planificar comidas y cenas semanales de manera rápida, visual y sin estrés. Orientada principalmente a familias (target: madres), permite organizar el menú semanal en 5 minutos usando drag & drop, respetando reglas nutricionales configurables y sincronizando opcionalmente con el calendario nativo de iOS.

**NO es un tracker de calorías ni macros**. El enfoque está en la simplicidad, variedad y disfrute de las comidas familiares.

---

## Problema y Solución

### Problema
- Planificar comidas semanales es una tarea recurrente que genera **estrés y fatiga decisional**.
- Las familias necesitan **variedad** sin repetir platos constantemente.
- Restricciones dietéticas (gluten, frecuencia de proteínas) complican la planificación.
- Apps existentes son **complejas** (trackers nutricionales) o requieren mucha configuración manual.

### Solución
MealMood ofrece:
1. **Planificación en 5 minutos**: interfaz visual con drag & drop.
2. **Reglas configurables**: limita frecuencia de ingredientes (carne, pescado, etc.) sin ser obsesivo.
3. **Autocompletado inteligente**: algoritmo rellena huecos respetando reglas.
4. **Sincronización con calendario**: eventos automáticos en el calendario nativo de iOS.
5. **Diseño acogedor**: tonos pastel, ilustraciones suaves, experiencia sin fricción.

---

## Usuario Objetivo

### Perfil Primario
- **Madres de familia** (25-45 años).
- Con hijos pequeños/adolescentes.
- Responsables de la planificación semanal de comidas.
- Buscan **eficiencia** sin perder variedad.
- No obsesionadas con calorías/macros, pero conscientes de dieta equilibrada.

### Perfil Secundario
- Padres que comparten la responsabilidad de cocinar.
- Personas organizadas que planifican con antelación.
- Familias con restricciones dietéticas leves (gluten, vegetarianos).

### Jobs to Be Done (JTBD)
*"Cuando llega el domingo por la tarde y necesito planificar la semana, quiero una forma rápida de decidir qué cocinar cada día sin repetir los mismos platos, para no estresarme y que mi familia tenga variedad."*

---

## Propuesta de Valor

### Diferenciadores clave
1. **Rapidez**: planificación en 5 minutos vs. 30+ en competencia.
2. **Visual**: interfaz calendario + drag & drop vs. listas aburridas.
3. **Reglas automáticas**: evita repeticiones sin pensar manualmente.
4. **No es un tracker nutricional**: enfoque en disfrute, no en restricción.
5. **Sincronización calendario**: eventos listos para compartir con la familia.

### Competencia
| App | Enfoque | Diferencia con MealMood |
|-----|---------|------------------------|
| **Paprika** | Recetas + listas de compra | Más complejo, orientado a recetas detalladas |
| **Mealime** | Planes nutricionales | Muy centrado en macros/calorías |
| **Plan to Eat** | Recetas + calendario | Requiere importar recetas, setup largo |
| **AnyList** | Listas de compra | No tiene planificación visual con reglas |

---

## Arquitectura de Información

### Flujo principal

```
Onboarding (primera vez)
    ↓
Configuración inicial
    ├── Ventanas de comida (cenas / comidas / ambas)
    ├── ¿Incluir fines de semana?
    └── Sincronización calendario (opcional)
    ↓
Home (pantalla principal)
    ├── Calendario semanal (vista principal)
    ├── Drawer de platos disponibles
    └── Botones: Completar / Resetear
    ↓
Gestión de platos
    ├── Lista de platos
    ├── Crear nuevo plato
    └── Editar/Eliminar platos
    ↓
Gestión de etiquetas (Tags)
    ├── Lista de tags
    ├── Editar reglas de tags
    └── Crear tags custom (Freemium)
    ↓
Settings
    ├── Ventanas de comida
    ├── Fines de semana
    ├── Calendario (sincronización)
    ├── Idioma
    └── Premium
```

---

## Modelo de Datos

### Entidades principales

#### 1. Settings
```swift
struct Settings {
    // General
    var mealWindows: MealWindows // .dinnerOnly, .lunchOnly, .both
    var includeWeekends: Bool
    var language: Language // .spanish, .english
    
    // Calendario
    var calendarId: String? // ID del calendario nativo, nil si no sincroniza
    var syncEnabled: Bool
    var lunchTime: Date // hora del día (default: 14:00)
    var dinnerTime: Date // hora del día (default: 21:00)
    var eventDuration: Int // minutos (default: 60)
    var eventPrefix: String // default: "🍽️"
    var reminderMinutesBefore: Int? // nil = sin alerta, valores: 30, 60, 120
    
    // Premium
    var isPremium: Bool
}

enum MealWindows {
    case dinnerOnly
    case lunchOnly
    case both
}

enum Language {
    case spanish
    case english
}
```

#### 2. Tag (Etiqueta)
```swift
struct Tag: Identifiable, Codable {
    var id: UUID
    var name: String // localizado según idioma
    var color: String // hex (ej: "#E74C3C")
    var rules: TagRules
}

struct TagRules: Codable {
    var maxPerWeek: Int? // nil = sin límite
    var noConsecutive: Bool // no puede estar en días consecutivos
    var noDuplicateInDay: Bool // no puede estar en comida y cena el mismo día
    var mealTypeRestriction: MealType? // .lunch, .dinner, nil
}

enum MealType: String, Codable {
    case lunch
    case dinner
}
```

#### 3. Dish (Plato)
```swift
struct Dish: Identifiable, Codable {
    var id: UUID
    var name: String
    var description: String? // opcional
    var tags: [UUID] // referencias a Tag IDs
    var createdAt: Date
}
```

#### 4. MealSlot (Slot de comida)
```swift
struct MealSlot: Identifiable, Codable {
    var id: UUID
    var dayOfWeek: Int // 0=lunes, 6=domingo
    var mealType: MealType // .lunch o .dinner
    var dish: UUID? // nil si vacío
    var calendarEventId: String? // ID del evento en EventKit, nil si no sincroniza
}
```

#### 5. WeekPlan (Planificación semanal)
```swift
struct WeekPlan: Identifiable, Codable {
    var id: UUID
    var weekStartDate: Date // lunes de esa semana
    var slots: [MealSlot]
    var createdAt: Date
    var updatedAt: Date
}
```

---

## Funcionalidades MVP

### F1: Configuración Inicial (Onboarding)
**Descripción**: Primera vez que el usuario abre la app.

**Pasos**:
1. **Bienvenida**: logo + tagline + ilustración.
2. **Ventanas de comida**: selector (solo cenas / solo comidas / ambas).
3. **Fines de semana**: toggle (incluir o no).
4. **Calendario** (opcional): selector de calendario nativo + configuración de horas.
5. **Crear primeros platos**: formulario rápido para añadir 2-3 platos iniciales.

**Criterios de Aceptación**:
- Usuario puede completar onboarding en <2 minutos.
- Settings guardados en persistencia local (SwiftData/Core Data).
- Navegación fluida sin saltos bruscos.

---

### F2: Gestión de Platos
**Descripción**: Crear, editar y eliminar platos personales.

**Funcionalidad**:
- **Lista de platos**: muestra todos los platos del usuario.
- **Crear plato**:
  - Campo: Nombre (obligatorio).
  - Campo: Descripción (opcional).
  - Selector: Etiquetas (multi-selección de tags disponibles).
- **Editar plato**: mismo formulario, pre-rellenado.
- **Eliminar plato**: swipe-to-delete + confirmación.

**Validaciones**:
- Nombre no puede estar vacío.
- No permitir eliminar plato si está asignado a un slot de la semana actual (mostrar alert).

**Criterios de Aceptación**:
- Usuario puede crear plato en <30 segundos.
- Cambios se reflejan inmediatamente en la lista de platos.
- Eliminación con confirmación para evitar errores.

---

### F3: Gestión de Etiquetas (Tags)
**Descripción**: Ver y editar reglas de etiquetas predefinidas.

**Funcionalidad**:
- **Lista de tags**: muestra tags por defecto (10 en MVP).
- **Editar reglas de tag**:
  - Max por semana (slider 1-7 o "sin límite").
  - Toggle: No consecutivo.
  - Toggle: No duplicar en el mismo día.
  - Selector: Restricción de tipo de comida (ninguna / solo comida / solo cena).
- **Ver color del tag**: visualización en lista con pill de color.

**Tags por defecto (MVP)**:

| Tag (ES) | Tag (EN) | Color | Reglas por defecto |
|----------|----------|-------|-------------------|
| Carne | Meat | #E74C3C | Max 3/semana, no consecutivo, no mismo día |
| Pescado | Fish | #3498DB | Max 2/semana, no consecutivo, no mismo día |
| Legumbres | Legumes | #95A5A6 | Max 2/semana, no mismo día |
| Verduras | Vegetables | #2ECC71 | Sin límite |
| Huevos | Eggs | #F1C40F | Max 2/semana, no mismo día |
| Pasta/Arroz | Pasta/Rice | #D4AC6E | Max 3/semana, no consecutivo |
| Solo Cena | Dinner Only | #9B59B6 | Restricción: solo cena |
| Solo Comida | Lunch Only | #E67E22 | Restricción: solo comida |
| Con Gluten | Contains Gluten | #8B4513 | Informativo (sin reglas) |
| Sin Gluten | Gluten Free | #1ABC9C | Informativo (sin reglas) |

**Criterios de Aceptación**:
- Usuario puede modificar reglas en <1 minuto.
- Cambios se aplican inmediatamente al algoritmo de validación.
- No permitir crear tags custom en MVP (solo Freemium).

---

### F4: Planificación Semanal (Pantalla Principal)
**Descripción**: Vista calendario con drag & drop para asignar platos a slots.

**Layout**:
```
┌─────────────────────────────────────────────┐
│  MealMood          Semana 3-9 Feb     [⚙️]  │
│                                             │
│  ← Anterior              Siguiente →        │
├─────────────────────────────────────────────┤
│                                             │
│  Lun      Mar      Mié      Jue      Vie   │
│                                             │
│  ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐  │
│  │🌙   │ │🌙   │ │🌙   │ │🌙   │ │🌙   │  │
│  │Pollo│ │Sal- │ │Torti│ │     │ │     │  │
│  │asado│ │món  │ │lla  │ │     │ │     │  │
│  │🔴   │ │🔵   │ │🟡   │ │ - - │ │ - - │  │
│  └─────┘ └─────┘ └─────┘ └─────┘ └─────┘  │
│                                             │
├─────────────────────────────────────────────┤
│  ┌──────────────┐  ┌──────────────┐        │
│  │  Completar   │  │  Resetear    │        │
│  │      ✨      │  │      🔄      │        │
│  └──────────────┘  └──────────────┘        │
└─────────────────────────────────────────────┘
       ▲ Arrastra platos aquí ▲
┌─────────────────────────────────────────────┐
│  📋 Mis platos                         [+]  │
├─────────────────────────────────────────────┤
│  ┌─────────────────────────────────────┐   │
│  │  Pollo asado              🔴        │   │
│  │  Jugoso y al horno                  │   │
│  └─────────────────────────────────────┘   │
│                                             │
│  ┌─────────────────────────────────────┐   │
│  │  Salmón a la plancha      🔵        │   │
│  │  Con limón y eneldo                 │   │
│  └─────────────────────────────────────┘   │
└─────────────────────────────────────────────┘
```

**Funcionalidad**:
- **Calendario semanal**:
  - Muestra días según configuración (lun-dom si incluye fines de semana, lun-vie si no).
  - Muestra slots según configuración (solo cena, solo comida, o ambas).
  - **Slots vacíos**: placeholder visual con icono (☀️ para comida, 🌙 para cena) + texto "Arrastra un plato".
  - **Slots llenos**: muestra nombre del plato + descripción corta + pills de colores (tags).

- **Drawer de platos**:
  - Lista scrolleable de todos los platos creados.
  - Card por plato: nombre + descripción + pills de tags.
  - Botón [+] para crear nuevo plato (abre modal/sheet).

- **Drag & Drop**:
  - Long press (200ms) en plato → se "levanta" con sombra.
  - Arrastrar sobre slot:
    - **Válido** (respeta reglas): borde verde, haptic feedback suave.
    - **Inválido** (viola reglas): borde rojo, haptic feedback "error".
  - Drop:
    - **Válido**: plato se asigna al slot, desaparece temporalmente del drawer, animación "plop".
    - **Inválido**: plato vuelve al drawer con bounce animation.

- **Navegación semanal**:
  - Flechas ← → para cambiar de semana.
  - Muestra rango de fechas (ej: "Semana 3-9 Feb").

- **Botones de acción**:
  - **Completar ✨**: ejecuta algoritmo de autocompletado (ver F5).
  - **Resetear 🔄**: limpia todos los slots de la semana (con confirmación).

**Criterios de Aceptación**:
- Usuario puede planificar semana completa en <5 minutos.
- Drag & drop fluido (60fps), sin lag.
- Validación de reglas en tiempo real (antes de drop).
- Navegación entre semanas sin perder datos.

---

### F5: Autocompletado Inteligente
**Descripción**: Algoritmo que rellena slots vacíos respetando reglas de tags.

**Funcionamiento**:
1. Usuario presiona botón "Completar ✨".
2. Algoritmo analiza:
   - Slots vacíos en la semana actual.
   - Platos disponibles (todos los creados por el usuario).
   - Reglas de cada tag asociado a cada plato.
3. Para cada slot vacío:
   - Filtra platos válidos (que no violen reglas).
   - Prioriza platos menos usados en la semana actual (para variedad).
   - Asigna plato aleatoriamente entre válidos.
4. Si no encuentra plato válido para un slot → lo deja vacío.
5. Muestra resultado con animación secuencial (slots se rellenan uno por uno).

**Lógica de validación**:
```python
def violates_rules(dish, slot, current_plan, rules):
    for tag_id in dish.tags:
        tag_rule = rules[tag_id]
        
        # Check max per week
        if tag_rule.maxPerWeek:
            count = current_plan.count_tag_usage(tag_id)
            if count >= tag_rule.maxPerWeek:
                return True
        
        # Check no consecutive
        if tag_rule.noConsecutive:
            adjacent_slots = current_plan.get_adjacent_slots(slot)
            for adj_slot in adjacent_slots:
                if adj_slot.dish and tag_id in adj_slot.dish.tags:
                    return True
        
        # Check no duplicate in same day
        if tag_rule.noDuplicateInDay:
            same_day_slots = current_plan.get_same_day_slots(slot)
            for sd_slot in same_day_slots:
                if sd_slot.dish and tag_id in sd_slot.dish.tags:
                    return True
        
        # Check meal type restriction
        if tag_rule.mealTypeRestriction:
            if slot.mealType != tag_rule.mealTypeRestriction:
                return True
    
    return False
```

**Criterios de Aceptación**:
- Algoritmo completa semana en <2 segundos.
- Respeta todas las reglas configuradas.
- Si no puede completar todos los slots, informa al usuario ("No puedo completar con las reglas actuales. ¿Deseas relajar restricciones?").
- Usuario puede manualmente sobrescribir asignaciones del algoritmo.

---

### F6: Sincronización con Calendario Nativo
**Descripción**: Crear eventos en el calendario nativo de iOS para cada comida planificada.

**Configuración** (Settings → Calendario):
- Toggle: Sincronizar (ON/OFF).
- Selector: Calendario nativo (lista de calendarios del dispositivo).
- Time picker: Hora de comidas (default: 14:00).
- Time picker: Hora de cenas (default: 21:00).
- Slider: Duración del evento (30min, 1h, 1.5h, 2h) - default: 1h.
- Text field: Prefijo del título (default: "🍽️").
- Selector: Recordatorio (sin recordatorio / 30min antes / 1h antes / 2h antes).

**Funcionamiento**:
1. **Al asignar plato a slot**:
   - Si `syncEnabled == true` y `calendarId != nil`:
     - Crear evento en EventKit:
       - **Título**: `{eventPrefix} {mealType}: {dish.name}` (ej: "🍽️ Cena: Pollo asado").
       - **Fecha**: `weekStartDate + dayOfWeek`.
       - **Hora inicio**: `lunchTime` (si es comida) o `dinnerTime` (si es cena).
       - **Hora fin**: `startDate + eventDuration`.
       - **Notas**: `dish.description` (si existe).
       - **Alerta**: según `reminderMinutesBefore`.
     - Guardar `eventId` en `slot.calendarEventId`.

2. **Al eliminar plato de slot**:
   - Si `slot.calendarEventId != nil`:
     - Eliminar evento de EventKit usando `eventId`.
     - Limpiar `slot.calendarEventId`.
     - Mostrar toast: "Evento eliminado del calendario".

3. **Al cambiar hora en Settings**:
   - Preguntar: "¿Actualizar eventos existentes?".
   - Si Sí:
     - Para cada `slot` con `calendarEventId != nil` y fecha >= hoy:
       - Actualizar evento en EventKit con nueva hora.

**Permisos**:
- Solicitar permiso de EventKit al activar sincronización.
- Si usuario deniega permiso → mostrar alert explicando que necesita acceso para sincronizar (con link a Settings del sistema).

**Casos edge**:
- Si calendario sincronizado es eliminado del dispositivo → detectar y mostrar warning: "El calendario sincronizado ya no existe. Selecciona otro."
- Si el usuario edita el evento directamente en la app Calendario nativa → **ignorar cambios** (no sincronización bidireccional en MVP).

**Criterios de Aceptación**:
- Eventos creados correctamente con todos los campos (título, fecha, hora, notas, alerta).
- Eliminación de eventos al quitar plato del slot.
- Usuario puede desactivar sincronización en cualquier momento sin perder datos en la app.

---

### F7: Settings (Ajustes)
**Descripción**: Pantalla de configuración general.

**Secciones**:

#### 🍽️ Planificación
- **Ventanas de comida**: selector (Solo cenas / Solo comidas / Comidas y cenas).
- **Incluir fines de semana**: toggle (ON/OFF).

#### 📅 Calendario
- **Sincronizar**: toggle (ON/OFF).
- **Calendario**: selector de calendarios nativos (solo visible si toggle ON).
- **Hora de comidas**: time picker (default: 14:00).
- **Hora de cenas**: time picker (default: 21:00).
- **Duración del evento**: selector (30min, 1h, 1.5h, 2h).
- **Prefijo del título**: text field (default: "🍽️").
- **Recordatorio**: selector (Sin recordatorio / 30min antes / 1h antes / 2h antes).

#### 🏷️ Etiquetas
- **Gestionar etiquetas**: navegar a pantalla de gestión de tags.

#### 🌍 Idioma
- **Idioma**: selector (Español / English).

#### ⭐ Premium
- **Quitar publicidad**: navegar a pantalla de compra (IAP).
- **Estado**: mostrar si el usuario es Premium o Free.

**Criterios de Aceptación**:
- Cambios se guardan inmediatamente (auto-save).
- Cambio de idioma actualiza toda la interfaz (incluyendo tags predefinidos).
- Navegación fluida entre Settings y otras pantallas.

---

### F8: Persistencia Local
**Descripción**: Guardar datos del usuario en el dispositivo.

**Tecnología**: SwiftData (iOS 17+) o Core Data (si necesitas compatibilidad con iOS 16).

**Entidades persistentes**:
- Settings
- Tag (10 tags por defecto)
- Dish (platos creados por el usuario)
- WeekPlan (planificaciones semanales, al menos la actual y las próximas 2-3)

**Comportamiento**:
- Datos se guardan automáticamente en cada cambio (no requiere botón "Guardar").
- Al abrir la app, carga la planificación de la semana actual.
- Si no existe planificación para la semana actual, crea una nueva vacía.

**Criterios de Aceptación**:
- Datos persisten entre sesiones de la app.
- No hay pérdida de datos al cerrar/abrir la app.
- Performance: carga inicial <1 segundo.

---

### F9: Multiidioma (Español/Inglés)
**Descripción**: Soporte para español e inglés desde día 1.

**Implementación**:
- Usar `Localizable.strings` para textos de UI.
- Tags predefinidos tienen nombre localizado (ej: "Carne" en ES, "Meat" en EN).
- Cambio de idioma en Settings → actualiza toda la interfaz sin reiniciar la app.

**Textos a traducir**:
- Onboarding (bienvenida, pasos).
- Pantalla principal (botones, placeholders).
- Gestión de platos (formularios, validaciones).
- Gestión de tags (nombres, reglas).
- Settings (todas las opciones).
- Alerts y toasts.

**Criterios de Aceptación**:
- Usuario puede cambiar idioma en Settings.
- Cambio se refleja inmediatamente en toda la app.
- Tags predefinidos cambian de nombre según idioma.

---

### F10: Navegación entre Semanas
**Descripción**: Usuario puede planificar múltiples semanas (pasadas y futuras).

**Funcionalidad**:
- Flechas ← → en la pantalla principal.
- Mostrar rango de fechas de la semana actual (ej: "Semana 3-9 Feb 2025").
- Al cambiar de semana:
  - Cargar planificación de esa semana (si existe).
  - Si no existe, crear planificación vacía.
- **Limitación MVP**: solo se pueden editar semana actual y futuras (no pasadas).

**Criterios de Aceptación**:
- Navegación fluida sin lag.
- Cada semana tiene su propia planificación independiente.
- Usuario puede ver hasta 4 semanas futuras.

---

## Funcionalidades Freemium (Futuro)

### Freemium 1: Tags Custom Ilimitados
- Usuario puede crear tags personalizados con nombre, color y reglas.
- En Free: solo 10 tags predefinidos, no se pueden crear nuevos.

### Freemium 2: Platos Ilimitados
- Free: máximo 20 platos.
- Premium: ilimitados.

### Freemium 3: Reglas Avanzadas
- **Separación mínima**: "Este plato no puede repetirse hasta X días después".
- **Día fijo**: "Este plato siempre los viernes" (tradiciones familiares).
- En Free: solo reglas básicas (max/semana, no consecutivo, no mismo día).

### Freemium 4: Múltiples Planificaciones Guardadas
- Guardar "plantillas" de semanas (ej: "Semana típica", "Semana de invitados").
- Aplicar plantilla a cualquier semana futura.

### Freemium 5: Exportar a Lista de Compra
- Generar lista de ingredientes basada en la planificación semanal.
- Exportar a Notes, Reminders o apps de terceros (AnyList).

### Freemium 6: Analytics/Histórico
- Dashboard con estadísticas:
  - "¿Qué platos repito más?"
  - "¿Cuántas veces comí carne esta semana?"
  - Gráficos de variedad semanal.

### Freemium 7: Colaboración Familiar
- Múltiples usuarios pueden editar la misma planificación.
- Notificaciones push: "Papá cambió la cena del jueves".

---

## Identidad Visual y Branding

### Logo
**Descripción conceptual**:
- **Forma**: circular o redondeada (amigable).
- **Icono**: plato con tenedor/cuchara cruzados, estilo line art minimalista.
- **Colores**: gradiente suave coral → menta (#FFB4A2 → #B8E6D5).
- **Líneas**: grosor 2-3px, esquinas redondeadas.
- **Opcional**: pequeño "sparkle" (✨) en la esquina superior derecha.

**Placeholder en código**:
```swift
struct AppIconPlaceholder: View {
    var body: some View {
        ZStack {
            Circle()
                .fill(
                    LinearGradient(
                        colors: [Color(hex: "#FFB4A2"), Color(hex: "#B8E6D5")],
                        startPoint: .topLeading,
                        endPoint: .bottomTrailing
                    )
                )
            
            Image(systemName: "fork.knife.circle")
                .font(.system(size: 60, weight: .light))
                .foregroundColor(.white)
        }
        .frame(width: 120, height: 120)
        .shadow(color: .black.opacity(0.1), radius: 10, y: 5)
    }
}
```

---

### Paleta de Colores

```swift
// Colores principales
extension Color {
    // Primary
    static let mealMoodCoral = Color(hex: "#FFB4A2")      // Coral pastel (acento cálido)
    static let mealMoodMint = Color(hex: "#B8E6D5")       // Menta claro (secundario)
    
    // Background
    static let mealMoodBackground = Color(hex: "#FFF9F5") // Crema muy claro
    static let mealMoodSurface = Color.white              // Blanco puro (cards)
    
    // Text
    static let mealMoodTextPrimary = Color(hex: "#2D2D2D")   // Gris oscuro
    static let mealMoodTextSecondary = Color(hex: "#6B6B6B") // Gris medio
    
    // Feedback
    static let mealMoodSuccess = Color(hex: "#A8D5BA")    // Verde sage
    static let mealMoodWarning = Color(hex: "#FFD6A5")    // Melocotón
    static let mealMoodError = Color(hex: "#FF9999")      // Rojo suave
    
    // Helpers
    init(hex: String) {
        let scanner = Scanner(string: hex)
        scanner.currentIndex = hex.hasPrefix("#") ? hex.index(after: hex.startIndex) : hex.startIndex
        var rgb: UInt64 = 0
        scanner.scanHexInt64(&rgb)
        let r = Double((rgb & 0xFF0000) >> 16) / 255.0
        let g = Double((rgb & 0x00FF00) >> 8) / 255.0
        let b = Double(rgb & 0x0000FF) / 255.0
        self.init(red: r, green: g, blue: b)
    }
}
```

---

### Tipografía

```swift
// Font system
extension Font {
    // Headers
    static let mealMoodH1 = Font.system(size: 28, weight: .bold, design: .rounded)
    static let mealMoodH2 = Font.system(size: 20, weight: .semibold, design: .rounded)
    static let mealMoodH3 = Font.system(size: 18, weight: .semibold, design: .rounded)
    
    // Body
    static let mealMoodBody = Font.system(size: 16, weight: .regular, design: .default)
    static let mealMoodBodyBold = Font.system(size: 16, weight: .semibold, design: .default)
    
    // Small
    static let mealMoodCaption = Font.system(size: 12, weight: .medium, design: .default)
    static let mealMoodSmall = Font.system(size: 14, weight: .regular, design: .default)
}
```

---

### Componentes UI Reutilizables

#### 1. Card Style (para slots y platos)

```swift
struct MealCardStyle: ViewModifier {
    func body(content: Content) -> some View {
        content
            .background(Color.mealMoodSurface)
            .cornerRadius(16)
            .shadow(color: Color.black.opacity(0.05), radius: 8, y: 4)
            .overlay(
                RoundedRectangle(cornerRadius: 16)
                    .stroke(Color(hex: "#F0F0F0"), lineWidth: 1)
            )
    }
}

extension View {
    func mealCardStyle() -> some View {
        modifier(MealCardStyle())
    }
}
```

#### 2. Tag Pill (visual de etiquetas)

```swift
struct TagPill: View {
    let tag: Tag
    
    var body: some View {
        Text(tag.name)
            .font(.mealMoodCaption)
            .foregroundColor(.white)
            .padding(.horizontal, 10)
            .padding(.vertical, 4)
            .background(
                Capsule()
                    .fill(Color(hex: tag.color))
            )
    }
}
```

#### 3. Empty Slot (placeholder visual)

```swift
struct EmptySlot: View {
    let mealType: MealType
    
    var body: some View {
        VStack(spacing: 8) {
            Image(systemName: mealType == .lunch ? "sun.max.fill" : "moon.stars.fill")
                .font(.system(size: 24))
                .foregroundColor(Color(hex: "#C4C4C4"))
            
            Text(mealType == .lunch ? "Comida" : "Cena")
                .font(.mealMoodSmall)
                .foregroundColor(.mealMoodTextSecondary)
            
            Text("Arrastra un plato")
                .font(.mealMoodCaption)
                .foregroundColor(Color(hex: "#A0A0A0"))
        }
        .frame(maxWidth: .infinity, minHeight: 100)
        .background(
            RoundedRectangle(cornerRadius: 12)
                .fill(Color(hex: "#F9F9F9"))
                .overlay(
                    RoundedRectangle(cornerRadius: 12)
                        .strokeBorder(
                            style: StrokeStyle(lineWidth: 2, dash: [5])
                        )
                        .foregroundColor(Color(hex: "#E0E0E0"))
                )
        )
    }
}
```

#### 4. Filled Slot (con plato asignado)

```swift
struct FilledSlot: View {
    let dish: Dish
    let tags: [Tag]
    
    var body: some View {
        VStack(alignment: .leading, spacing: 6) {
            Text(dish.name)
                .font(.system(size: 16, weight: .semibold))
                .foregroundColor(.mealMoodTextPrimary)
                .lineLimit(2)
            
            if let description = dish.description {
                Text(description)
                    .font(.mealMoodCaption)
                    .foregroundColor(.mealMoodTextSecondary)
                    .lineLimit(1)
            }
            
            HStack(spacing: 4) {
                ForEach(tags.prefix(3)) { tag in
                    TagPill(tag: tag)
                }
                if tags.count > 3 {
                    Text("+\(tags.count - 3)")
                        .font(.system(size: 10, weight: .medium))
                        .foregroundColor(.mealMoodTextSecondary)
                }
            }
        }
        .padding(12)
        .frame(maxWidth: .infinity, minHeight: 100, alignment: .topLeading)
        .mealCardStyle()
    }
}
```

#### 5. Primary Button

```swift
struct PrimaryButton: View {
    let title: String
    let icon: String?
    let action: () -> Void
    
    var body: some View {
        Button(action: action) {
            HStack(spacing: 8) {
                Text(title)
                    .font(.mealMoodBodyBold)
                if let icon = icon {
                    Text(icon)
                }
            }
            .foregroundColor(.white)
            .padding(.horizontal, 24)
            .padding(.vertical, 12)
            .background(
                LinearGradient(
                    colors: [Color.mealMoodCoral, Color.mealMoodMint],
                    startPoint: .leading,
                    endPoint: .trailing
                )
            )
            .cornerRadius(12)
            .shadow(color: Color.mealMoodCoral.opacity(0.3), radius: 8, y: 4)
        }
    }
}
```

#### 6. Secondary Button

```swift
struct SecondaryButton: View {
    let title: String
    let icon: String?
    let action: () -> Void
    
    var body: some View {
        Button(action: action) {
            HStack(spacing: 8) {
                Text(title)
                    .font(.mealMoodBodyBold)
                if let icon = icon {
                    Text(icon)
                }
            }
            .foregroundColor(.mealMoodTextPrimary)
            .padding(.horizontal, 24)
            .padding(.vertical, 12)
            .background(Color.mealMoodSurface)
            .overlay(
                RoundedRectangle(cornerRadius: 12)
                    .stroke(Color.mealMoodTextSecondary.opacity(0.3), lineWidth: 1.5)
            )
            .cornerRadius(12)
        }
    }
}
```

---

## Especificaciones de Pantallas

### Pantalla 1: Onboarding

#### 1.1: Bienvenida

```
┌─────────────────────────────────────────────┐
│                                             │
│         [Logo MealMood placeholder]         │
│                                             │
│    ✨ Planifica tu semana en 5 minutos     │
│                                             │
│    [Ilustración: familia cenando feliz]    │
│                                             │
│  • Sin estrés                               │
│  • Sin contar calorías                      │
│  • Sincroniza con tu calendario             │
│                                             │
│                                             │
│           [Comenzar] ──────────►            │
│                                             │
└─────────────────────────────────────────────┘
```

**Elementos**:
- Logo placeholder (usar componente AppIconPlaceholder).
- Título: H1, centrado, coral.
- Ilustración: placeholder con SF Symbols (ej: "figure.2.and.child.holdinghands").
- Lista de beneficios: bullets con iconos.
- Botón primario: "Comenzar".

**Navegación**: tap en "Comenzar" → Onboarding paso 1.

---

#### 1.2: Paso 1 - Ventanas de Comida

```
┌─────────────────────────────────────────────┐
│  ¿Qué quieres planificar?                   │
│                                             │
│  ┌─────────────────────────────────────┐   │
│  │  🌙 Solo cenas                      │   │
│  └─────────────────────────────────────┘   │
│                                             │
│  ┌─────────────────────────────────────┐   │
│  │  ☀️ Solo comidas                    │   │
│  └─────────────────────────────────────┘   │
│                                             │
│  ┌─────────────────────────────────────┐   │
│  │  🌞🌙 Comidas y cenas               │   │
│  └─────────────────────────────────────┘   │
│                                             │
│                      [Continuar] ────────►  │
└─────────────────────────────────────────────┘
```

**Elementos**:
- Título: H2, alineado a la izquierda.
- 3 cards seleccionables (radio button style).
- Botón primario: "Continuar" (solo habilitado si hay selección).

**Navegación**: tap en "Continuar" → Onboarding paso 2.

---

#### 1.3: Paso 2 - Fines de Semana

```
┌─────────────────────────────────────────────┐
│  ¿Incluir fines de semana?                  │
│                                             │
│  ┌─────────────────────────────────────┐   │
│  │  Planificar también sábados         │   │
│  │  y domingos                    [ON] │   │
│  └─────────────────────────────────────┘   │
│                                             │
│  (Puedes cambiar esto después en ajustes)  │
│                                             │
│                      [Continuar] ────────►  │
└─────────────────────────────────────────────┘
```

**Elementos**:
- Título: H2.
- Toggle prominente.
- Nota aclaratoria (small text, gris).
- Botón primario: "Continuar".

**Navegación**: tap en "Continuar" → Onboarding paso 3.

---

#### 1.4: Paso 3 - Calendario (Opcional)

```
┌─────────────────────────────────────────────┐
│  ¿Sincronizar con tu calendario?            │
│                                             │
│  ┌─────────────────────────────────────┐   │
│  │  Sincronizar              [OFF]     │   │
│  └─────────────────────────────────────┘   │
│                                             │
│  [Si activas sincronización:]               │
│                                             │
│  ┌─────────────────────────────────────┐   │
│  │  Calendario                         │   │
│  │  Personal                      ▼    │   │
│  └─────────────────────────────────────┘   │
│                                             │
│  ┌─────────────────────────────────────┐   │
│  │  Hora de comidas          14:00 ⏰  │   │
│  └─────────────────────────────────────┘   │
│                                             │
│  ┌─────────────────────────────────────┐   │
│  │  Hora de cenas            21:00 ⏰  │   │
│  └─────────────────────────────────────┘   │
│                                             │
│  (Opcional, puedes activarlo después)       │
│                                             │
│             [Omitir]    [Continuar] ──────► │
└─────────────────────────────────────────────┘
```

**Elementos**:
- Toggle: "Sincronizar".
- Si toggle ON → mostrar selector de calendario + time pickers.
- Botones: "Omitir" (secondary) y "Continuar" (primary).

**Navegación**:
- "Omitir" o "Continuar" → Onboarding paso 4.
- Si toggle ON, solicitar permiso de EventKit antes de continuar.

---

#### 1.5: Paso 4 - Crear Primeros Platos

```
┌─────────────────────────────────────────────┐
│  Añade tus primeros platos                  │
│                                             │
│  Añade al menos 2-3 platos para empezar     │
│                                             │
│  ┌─────────────────────────────────────┐   │
│  │  Nombre del plato                   │   │
│  │  [Pollo asado                   ]   │   │
│  │                                     │   │
│  │  Etiquetas                          │   │
│  │  🔴 Carne              [×]          │   │
│  │                                     │   │
│  │  [+ Añadir plato]                   │   │
│  └─────────────────────────────────────┘   │
│                                             │
│  Platos añadidos:                           │
│  ✓ Pollo asado (🔴 Carne)                  │
│                                             │
│                       [Finalizar] ────────► │
└─────────────────────────────────────────────┘
```

**Elementos**:
- Formulario simple: nombre + selector de tags.
- Botón "Añadir plato" (añade a lista temporal).
- Lista de platos añadidos (con opción de eliminar).
- Botón "Finalizar" (habilitado cuando hay >= 2 platos).

**Navegación**: tap en "Finalizar" → Home (pantalla principal).

---

### Pantalla 2: Home (Pantalla Principal)

```
┌─────────────────────────────────────────────┐
│  MealMood          Semana 3-9 Feb     [⚙️]  │
│                                             │
│  ← Anterior              Siguiente →        │
├─────────────────────────────────────────────┤
│                                             │
│  Lun      Mar      Mié      Jue      Vie   │
│                                             │
│  ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐  │
│  │🌙   │ │🌙   │ │🌙   │ │🌙   │ │🌙   │  │
│  │Pollo│ │Sal- │ │Torti│ │     │ │     │  │
│  │asado│ │món  │ │lla  │ │     │ │     │  │
│  │🔴   │ │🔵   │ │🟡   │ │ - - │ │ - - │  │
│  └─────┘ └─────┘ └─────┘ └─────┘ └─────┘  │
│                                             │
│  [Si incluye comida y cena:]                │
│  ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐  │
│  │☀️   │ │☀️   │ │☀️   │ │☀️   │ │☀️   │  │
│  │Lente│ │     │ │Pasta│ │     │ │     │  │
│  │jas  │ │     │ │🟡   │ │     │ │     │  │
│  │🟢🟤 │ │ - - │ │     │ │ - - │ │ - - │  │
│  └─────┘ └─────┘ └─────┘ └─────┘ └─────┘  │
│                                             │
├─────────────────────────────────────────────┤
│  ┌──────────────┐  ┌──────────────┐        │
│  │  Completar   │  │  Resetear    │        │
│  │      ✨      │  │      🔄      │        │
│  └──────────────┘  └──────────────┘        │
└─────────────────────────────────────────────┘
       ▲ Arrastra platos aquí ▲
┌─────────────────────────────────────────────┐
│  📋 Mis platos                         [+]  │
├─────────────────────────────────────────────┤
│  ┌─────────────────────────────────────┐   │
│  │  Pollo asado              🔴        │   │
│  │  Jugoso y al horno                  │   │
│  └─────────────────────────────────────┘   │
│                                             │
│  ┌─────────────────────────────────────┐   │
│  │  Salmón a la plancha      🔵        │   │
│  │  Con limón y eneldo                 │   │
│  └─────────────────────────────────────┘   │
│                                             │
│  ┌─────────────────────────────────────┐   │
│  │  Lentejas                🟢🟤       │   │
│  │  Estofadas con verduras             │   │
│  └─────────────────────────────────────┘   │
│                                             │
│  ... (scroll si hay más platos)             │
│                                             │
│  [Banner AdMob - 50px altura]               │
└─────────────────────────────────────────────┘
```

**Elementos**:

#### Header
- Título: "MealMood" (H2, bold).
- Rango de semana: "Semana 3-9 Feb" (body, gris).
- Botón Settings: icono ⚙️ (top-right).

#### Navegación Semanal
- Flechas: ← Anterior / Siguiente →.
- Centrado en el rango de fechas.

#### Calendario Semanal
- **Grid de slots**: 
  - Orientación: horizontal scroll si incluye fines de semana (7 columnas).
  - Orientación: vertical scroll si incluye comida+cena (2 filas por día).
- **Slot vacío**: usar componente `EmptySlot`.
- **Slot lleno**: usar componente `FilledSlot`.

#### Botones de Acción
- **Completar ✨**: PrimaryButton.
- **Resetear 🔄**: SecondaryButton.

#### Drawer de Platos
- Lista scrolleable vertical.
- Card por plato (usar `mealCardStyle`).
- Botón [+]: circular, floating, color coral.

#### AdMob Banner (si Free)
- Altura: 50px.
- Background: mismo color que la app (#FFF9F5).
- Sticky al fondo del drawer.

**Interacciones**:
- **Long press en plato** → drag & drop.
- **Tap en slot lleno** → opción de eliminar plato.
- **Tap en [+]** → abrir sheet "Crear Plato".
- **Tap en ⚙️** → navegar a Settings.
- **Tap en "Completar"** → ejecutar algoritmo de autocompletado.
- **Tap en "Resetear"** → mostrar alert de confirmación.

---

### Pantalla 3: Crear/Editar Plato

```
┌─────────────────────────────────────────────┐
│  ← Nuevo plato                         [✓]  │
├─────────────────────────────────────────────┤
│                                             │
│  Nombre del plato *                         │
│  ┌─────────────────────────────────────┐   │
│  │  Pollo asado                        │   │
│  └─────────────────────────────────────┘   │
│                                             │
│  Descripción (opcional)                     │
│  ┌─────────────────────────────────────┐   │
│  │  Jugoso y al horno con hierbas     │   │
│  └─────────────────────────────────────┘   │
│                                             │
│  Etiquetas *                                │
│  ┌──────────────────────────────────┐      │
│  │  🔴 Carne           [×]           │      │
│  │  🟢 Verduras        [×]           │      │
│  └──────────────────────────────────┘      │
│                                             │
│  [+ Añadir etiqueta]                        │
│                                             │
│                                             │
│                                             │
│  [Eliminar plato] (solo en modo edición)    │
└─────────────────────────────────────────────┘
```

**Elementos**:
- **Header**:
  - Botón back: ← (top-left).
  - Título: "Nuevo plato" (crear) o "Editar plato" (editar).
  - Botón guardar: ✓ (top-right, habilitado solo si campos válidos).

- **Formulario**:
  - Campo: Nombre (obligatorio, TextField).
  - Campo: Descripción (opcional, TextEditor, max 100 caracteres).
  - Lista de tags seleccionados (pills con botón [×] para eliminar).
  - Botón "Añadir etiqueta" → abre sheet selector de tags.

- **Botón Eliminar** (solo en modo edición):
  - SecondaryButton, color rojo.
  - Mostrar alert de confirmación.

**Validaciones**:
- Nombre no puede estar vacío.
- Debe tener al menos 1 etiqueta.

**Navegación**:
- Tap en [✓] → guardar y cerrar sheet.
- Tap en ← → descartar cambios (mostrar alert si hay cambios sin guardar).

---

#### 3.1: Sheet Selector de Tags

```
┌─────────────────────────────────────────────┐
│  Selecciona etiquetas               [×]     │
├─────────────────────────────────────────────┤
│                                             │
│  🔴 Carne              [✓]                  │
│  🔵 Pescado            [ ]                  │
│  🟢 Verduras           [✓]                  │
│  🟡 Huevos             [ ]                  │
│  🟤 Legumbres          [ ]                  │
│  🟠 Pasta/Arroz        [ ]                  │
│  🟣 Solo Cena          [ ]                  │
│  🟠 Solo Comida        [ ]                  │
│  🟤 Con Gluten         [ ]                  │
│  🟢 Sin Gluten         [ ]                  │
│                                             │
│           [Listo]                           │
└─────────────────────────────────────────────┘
```

**Elementos**:
- Lista de todos los tags disponibles.
- Checkbox por tag (multi-selección).
- Color pill al lado del nombre.
- Botón "Listo" (primary).

**Navegación**: tap en "Listo" → cerrar sheet, actualizar lista de tags seleccionados.

---

### Pantalla 4: Gestión de Platos

```
┌─────────────────────────────────────────────┐
│  ← Mis platos                          [+]  │
├─────────────────────────────────────────────┤
│                                             │
│  ┌─────────────────────────────────────┐   │
│  │  Pollo asado              🔴   [›]  │   │
│  │  Jugoso y al horno                  │   │
│  └─────────────────────────────────────┘   │
│                                             │
│  ┌─────────────────────────────────────┐   │
│  │  Salmón a la plancha      🔵   [›]  │   │
│  │  Con limón y eneldo                 │   │
│  └─────────────────────────────────────┘   │
│                                             │
│  ┌─────────────────────────────────────┐   │
│  │  Lentejas                🟢🟤  [›]  │   │
│  │  Estofadas con verduras             │   │
│  └─────────────────────────────────────┘   │
│                                             │
│  ... (scroll si hay más platos)             │
│                                             │
└─────────────────────────────────────────────┘
```

**Elementos**:
- **Header**:
  - Botón back: ← (top-left).
  - Título: "Mis platos".
  - Botón [+]: crear nuevo plato (top-right).

- **Lista de platos**:
  - Card por plato (mealCardStyle).
  - Tap en card → abrir sheet "Editar Plato".
  - Swipe left → botón "Eliminar" (rojo).

**Interacciones**:
- **Tap en plato** → abrir sheet edición.
- **Swipe left** → mostrar botón "Eliminar".
- **Tap en "Eliminar"** → mostrar alert de confirmación.
- **Tap en [+]** → abrir sheet "Crear Plato".

---

### Pantalla 5: Gestión de Etiquetas

```
┌─────────────────────────────────────────────┐
│  ← Etiquetas                                │
├─────────────────────────────────────────────┤
│                                             │
│  🔴 Carne                              [›]  │
│  Max 3/semana, no consecutivo               │
│                                             │
│  🔵 Pescado                            [›]  │
│  Max 2/semana, no consecutivo               │
│                                             │
│  🟢 Verduras                           [›]  │
│  Sin límite                                 │
│                                             │
│  🟡 Huevos                             [›]  │
│  Max 2/semana, no mismo día                 │
│                                             │
│  ... (resto de tags)                        │
│                                             │
│  [+ Crear etiqueta] (solo Premium)          │
└─────────────────────────────────────────────┘
```

**Elementos**:
- Lista de tags con resumen de reglas.
- Tap en tag → abrir pantalla "Editar Reglas de Tag".
- Botón [+ Crear etiqueta] (disabled en Free, enabled en Premium).

**Navegación**: tap en tag → Pantalla 5.1.

---

#### 5.1: Editar Reglas de Tag

```
┌─────────────────────────────────────────────┐
│  ← Carne                               [✓]  │
├─────────────────────────────────────────────┤
│                                             │
│  🔴 Carne                                   │
│                                             │
│  Máximo por semana                          │
│  ┌──────────────────────────────────┐      │
│  │  [1] [2] [3] [4] [5] [6] [7] [∞] │      │
│  └──────────────────────────────────┘      │
│  Seleccionado: 3                            │
│                                             │
│  ┌──────────────────────────────────┐      │
│  │  No consecutivo           [ON]    │      │
│  └──────────────────────────────────┘      │
│  Evita que aparezca días seguidos          │
│                                             │
│  ┌──────────────────────────────────┐      │
│  │  No repetir en el mismo día [ON]  │      │
│  └──────────────────────────────────┘      │
│  No puede estar en comida y cena el mismo  │
│  día                                        │
│                                             │
│  Restricción de horario                     │
│  ┌──────────────────────────────────┐      │
│  │  Sin restricción           ▼      │      │
│  └──────────────────────────────────┘      │
│  Opciones: Sin restricción / Solo comida / │
│            Solo cena                        │
│                                             │
└─────────────────────────────────────────────┘
```

**Elementos**:
- Pill de color del tag (centrado).
- **Max por semana**: segmented control (1-7, ∞).
- **Toggles**: no consecutivo, no mismo día.
- **Restricción horario**: picker (sin restricción / solo comida / solo cena).
- Botón [✓] (top-right): guardar y volver.

**Navegación**: tap en [✓] → guardar y volver a lista de tags.

---

### Pantalla 6: Settings

```
┌─────────────────────────────────────────────┐
│  ← Ajustes                                  │
├─────────────────────────────────────────────┤
│                                             │
│  🍽️ PLANIFICACIÓN                          │
│                                             │
│  Ventanas de comida                         │
│  ┌──────────────────────────────────┐      │
│  │  Solo cenas                 ▼    │      │
│  └──────────────────────────────────┘      │
│                                             │
│  Incluir fines de semana                    │
│  ┌──────────────────────────────────┐      │
│  │  [ON]                            │      │
│  └──────────────────────────────────┘      │
│                                             │
│  ─────────────────────────────────────      │
│                                             │
│  📅 CALENDARIO                              │
│                                             │
│  Sincronizar                                │
│  ┌──────────────────────────────────┐      │
│  │  [OFF]                           │      │
│  └──────────────────────────────────┘      │
│                                             │
│  [Si ON, mostrar:]                          │
│  Calendario                                 │
│  ┌──────────────────────────────────┐      │
│  │  Personal                   ▼    │      │
│  └──────────────────────────────────┘      │
│                                             │
│  Hora de comidas                            │
│  ┌──────────────────────────────────┐      │
│  │  14:00                      ⏰   │      │
│  └──────────────────────────────────┘      │
│                                             │
│  Hora de cenas                              │
│  ┌──────────────────────────────────┐      │
│  │  21:00                      ⏰   │      │
│  └──────────────────────────────────┘      │
│                                             │
│  Duración del evento                        │
│  ┌──────────────────────────────────┐      │
│  │  1 hora                     ▼    │      │
│  └──────────────────────────────────┘      │
│                                             │
│  Prefijo del título                         │
│  ┌──────────────────────────────────┐      │
│  │  🍽️                             │      │
│  └──────────────────────────────────┘      │
│                                             │
│  Recordatorio                               │
│  ┌──────────────────────────────────┐      │
│  │  Sin recordatorio           ▼    │      │
│  └──────────────────────────────────┘      │
│                                             │
│  ─────────────────────────────────────      │
│                                             │
│  🏷️ ETIQUETAS                               │
│                                             │
│  Gestionar etiquetas                   [›]  │
│                                             │
│  ─────────────────────────────────────      │
│                                             │
│  🌍 IDIOMA                                  │
│                                             │
│  Idioma                                     │
│  ┌──────────────────────────────────┐      │
│  │  Español                    ▼    │      │
│  └──────────────────────────────────┘      │
│                                             │
│  ─────────────────────────────────────      │
│                                             │
│  ⭐ PREMIUM                                 │
│                                             │
│  Estado: Free                               │
│  Quitar publicidad                     [›]  │
│                                             │
└─────────────────────────────────────────────┘
```

**Elementos**:
- Secciones separadas por headers (emoji + título).
- Separadores visuales (línea gris claro).
- Controles nativos de iOS (Toggle, Picker, TextField).
- Navegación a sub-pantallas con [›].

**Navegación**:
- **Gestionar etiquetas** → Pantalla 5.
- **Quitar publicidad** → Pantalla de IAP (In-App Purchase).

---

### Pantalla 7: Premium / IAP

```
┌─────────────────────────────────────────────┐
│  ← Premium                            [×]   │
├─────────────────────────────────────────────┤
│                                             │
│         ✨ MealMood Premium ✨              │
│                                             │
│  Disfruta de todas las funciones            │
│                                             │
│  ✓ Sin publicidad                           │
│  ✓ Platos ilimitados                        │
│  ✓ Etiquetas personalizadas                 │
│  ✓ Reglas avanzadas (próximamente)          │
│                                             │
│  ─────────────────────────────────────      │
│                                             │
│  Planes disponibles:                        │
│                                             │
│  ┌─────────────────────────────────────┐   │
│  │  Mensual                            │   │
│  │  $2.99 / mes                        │   │
│  │  [Suscribirse]                      │   │
│  └─────────────────────────────────────┘   │
│                                             │
│  ┌─────────────────────────────────────┐   │
│  │  Anual                              │   │
│  │  $19.99 / año (ahorra 44%)          │   │
│  │  [Suscribirse]                      │   │
│  └─────────────────────────────────────┘   │
│                                             │
│  [Restaurar compras]                        │
│                                             │
└─────────────────────────────────────────────┘
```

**Elementos**:
- Header con título y botón cerrar [×].
- Logo premium (placeholder con ✨).
- Lista de beneficios con checkmarks.
- Cards de planes (mensual / anual).
- Botones "Suscribirse" (StoreKit).
- Link "Restaurar compras".

**Navegación**:
- Tap en "Suscribirse" → StoreKit purchase flow.
- Tap en [×] → cerrar sheet.

---

## Interacciones y Micro-animaciones

### 1. Drag & Drop

**Gestos**:
1. **Long press** (200ms) en card de plato del drawer:
   - Card se "levanta" con escala 1.05.
   - Sombra aumenta (radius: 12, y: 8).
   - Haptic feedback: `.light`.

2. **Drag** sobre slot:
   - **Válido** (respeta reglas):
     - Slot muestra borde verde (#A8D5BA, 2px).
     - Haptic feedback: `.light` (suave, continuo cada 100ms).
   - **Inválido** (viola reglas):
     - Slot muestra borde rojo (#FF9999, 2px).
     - Haptic feedback: `.rigid` (fuerte, único).

3. **Drop**:
   - **Válido**:
     - Animación "plop": slot hace scale 1.0 → 1.1 → 1.0 (duration: 0.3s).
     - Plato desaparece del drawer con fade out.
     - Haptic feedback: `.success`.
   - **Inválido**:
     - Plato vuelve al drawer con spring animation (bouncy).
     - Haptic feedback: `.error`.

**Código ejemplo (SwiftUI)**:
```swift
.onLongPressGesture(minimumDuration: 0.2) {
    // Iniciar drag
    withAnimation(.spring()) {
        draggedDish = dish
        isLifted = true
    }
    HapticManager.shared.impact(style: .light)
}
.gesture(
    DragGesture()
        .onChanged { value in
            dragOffset = value.translation
            validateDrop(at: value.location)
        }
        .onEnded { value in
            performDrop(at: value.location)
        }
)
```

---

### 2. Completar Automáticamente

**Flujo**:
1. Usuario tap en botón "Completar ✨".
2. Botón muestra loading indicator (ProgressView, color coral).
3. Algoritmo ejecuta en background (no bloquea UI).
4. Animación secuencial:
   - Slots vacíos se rellenan uno por uno (delay: 150ms entre cada uno).
   - Cada slot hace fade in + scale animation (0.8 → 1.0).
5. Al terminar:
   - Confetti suave (partículas pequeñas, colores pastel, duration: 2s).
   - Haptic feedback: `.success`.
   - Toast: "¡Listo! Tu semana está completa 🎉" (auto-dismiss en 3s).

**Código ejemplo (SwiftUI)**:
```swift
func autoComplete() {
    isLoading = true
    
    Task {
        let filledSlots = await MealPlanner.shared.autoComplete(weekPlan: weekPlan)
        
        for (index, slot) in filledSlots.enumerated() {
            try? await Task.sleep(nanoseconds: 150_000_000) // 150ms
            
            withAnimation(.spring()) {
                weekPlan.slots[slot.id] = slot
            }
            HapticManager.shared.impact(style: .light)
        }
        
        isLoading = false
        showConfetti = true
        HapticManager.shared.notification(type: .success)
        showToast = true
    }
}
```

---

### 3. Resetear

**Flujo**:
1. Usuario tap en botón "Resetear 🔄".
2. Alert suave (no modal agresivo):
   ```
   ┌─────────────────────────────────────┐
   │  ¿Limpiar la semana completa?       │
   │                                     │
   │  Esto no se puede deshacer          │
   │                                     │
   │     [Cancelar]    [Limpiar]         │
   └─────────────────────────────────────┘
   ```
3. Si usuario confirma:
   - Todos los slots vacíos hacen fade out (simultáneo, duration: 0.3s).
   - Haptic feedback: `.warning`.
   - Platos vuelven al drawer con fade in (staggered, delay: 50ms entre cada uno).

**Código ejemplo (SwiftUI)**:
```swift
func resetWeek() {
    withAnimation(.easeOut(duration: 0.3)) {
        for slot in weekPlan.slots {
            slot.dish = nil
        }
    }
    HapticManager.shared.notification(type: .warning)
}
```

---

### 4. Micro-interactions Adicionales

#### 4.1: Empty State (sin platos)
- Ilustración de familia con plato vacío.
- Texto: "Añade tu primer plato para empezar".
- Botón "Añadir plato" (primary, centrado).

#### 4.2: Pull to Refresh (en lista de platos)
- Arrastrar hacia abajo → loading indicator.
- Aunque no haya backend, simular recarga con delay 1s.

#### 4.3: Swipe to Delete (en lista de platos)
- Swipe left → botón "Eliminar" (rojo, icono 🗑️).
- Tap en eliminar → animación de desaparición (slide left + fade out).

#### 4.4: Toast Notifications
- Posición: top-center.
- Background: coral suave con opacity 0.95.
- Sombra suave.
- Auto-dismiss en 3s.
- Ejemplos:
  - "Plato guardado ✓"
  - "Evento eliminado del calendario"
  - "No puedo completar con las reglas actuales"

---

## Integración con Calendario Nativo

### Permisos

**Solicitar permiso** al activar sincronización por primera vez:
```swift
import EventKit

let eventStore = EKEventStore()

func requestCalendarAccess() async -> Bool {
    do {
        return try await eventStore.requestFullAccessToEvents()
    } catch {
        print("Error requesting calendar access: \(error)")
        return false
    }
}
```

**Si usuario deniega**:
- Mostrar alert: "MealMood necesita acceso a tu calendario para sincronizar eventos. Ve a Ajustes > MealMood > Calendario."
- Link directo a Settings del sistema: `UIApplication.shared.open(URL(string: UIApplication.openSettingsURLString)!)`

---

### Crear Evento

**Función**:
```swift
func createEvent(
    slot: MealSlot,
    dish: Dish,
    weekStartDate: Date,
    settings: Settings
) -> String? {
    guard let calendarId = settings.calendarId,
          let calendar = eventStore.calendar(withIdentifier: calendarId) else {
        return nil
    }
    
    let event = EKEvent(eventStore: eventStore)
    
    // Título
    let prefix = settings.eventPrefix.isEmpty ? "" : "\(settings.eventPrefix) "
    let mealName = slot.mealType == .lunch ? "Comida" : "Cena"
    event.title = "\(prefix)\(mealName): \(dish.name)"
    
    // Fecha y hora
    let slotDate = weekStartDate.addingDays(slot.dayOfWeek)
    let slotTime = slot.mealType == .lunch ? settings.lunchTime : settings.dinnerTime
    event.startDate = combine(date: slotDate, time: slotTime)
    event.endDate = event.startDate.addingMinutes(settings.eventDuration)
    
    // Nota
    event.notes = dish.description
    
    // Calendario
    event.calendar = calendar
    
    // Alerta
    if let reminderMinutes = settings.reminderMinutesBefore {
        let alarm = EKAlarm(relativeOffset: -TimeInterval(reminderMinutes * 60))
        event.addAlarm(alarm)
    }
    
    // Guardar
    do {
        try eventStore.save(event, span: .thisEvent)
        return event.eventIdentifier
    } catch {
        print("Error saving event: \(error)")
        return nil
    }
}
```

---

### Eliminar Evento

**Función**:
```swift
func deleteEvent(eventId: String) {
    guard let event = eventStore.event(withIdentifier: eventId) else {
        return
    }
    
    do {
        try eventStore.remove(event, span: .thisEvent)
    } catch {
        print("Error deleting event: \(error)")
    }
}
```

---

### Actualizar Eventos (cambio de hora)

**Función**:
```swift
func updateEventsTime(weekPlan: WeekPlan, newLunchTime: Date, newDinnerTime: Date) {
    for slot in weekPlan.slots {
        guard let eventId = slot.calendarEventId,
              let event = eventStore.event(withIdentifier: eventId),
              event.startDate >= Date() else { // solo futuros
            continue
        }
        
        // Actualizar hora
        let newTime = slot.mealType == .lunch ? newLunchTime : newDinnerTime
        let newStartDate = combine(date: event.startDate, time: newTime)
        event.startDate = newStartDate
        event.endDate = newStartDate.addingMinutes(settings.eventDuration)
        
        // Guardar
        do {
            try eventStore.save(event, span: .thisEvent)
        } catch {
            print("Error updating event: \(error)")
        }
    }
}
```

---

### Helpers

```swift
extension Date {
    func addingDays(_ days: Int) -> Date {
        Calendar.current.date(byAdding: .day, value: days, to: self)!
    }
    
    func addingMinutes(_ minutes: Int) -> Date {
        Calendar.current.date(byAdding: .minute, value: minutes, to: self)!
    }
}

func combine(date: Date, time: Date) -> Date {
    let calendar = Calendar.current
    let dateComponents = calendar.dateComponents([.year, .month, .day], from: date)
    let timeComponents = calendar.dateComponents([.hour, .minute], from: time)
    
    var combined = DateComponents()
    combined.year = dateComponents.year
    combined.month = dateComponents.month
    combined.day = dateComponents.day
    combined.hour = timeComponents.hour
    combined.minute = timeComponents.minute
    
    return calendar.date(from: combined)!
}
```

---

## Algoritmo de Autocompletado

### Pseudocódigo

```python
def autocomplete(week_plan, available_dishes, tags_rules):
    """
    Rellena slots vacíos respetando reglas de tags.
    
    Args:
        week_plan: WeekPlan actual
        available_dishes: [Dish] todos los platos del usuario
        tags_rules: {tag_id: TagRules} reglas de cada tag
    
    Returns:
        WeekPlan actualizado
    """
    empty_slots = week_plan.get_empty_slots()
    
    for slot in empty_slots:
        # Filtrar platos válidos
        candidates = filter_valid_dishes(
            dishes=available_dishes,
            slot=slot,
            current_plan=week_plan,
            rules=tags_rules
        )
        
        if len(candidates) == 0:
            continue  # dejar slot vacío
        
        # Priorizar platos menos usados esta semana
        dish = weighted_random_pick(candidates, week_plan)
        week_plan.assign(slot, dish)
    
    return week_plan


def filter_valid_dishes(dishes, slot, current_plan, rules):
    """Filtra platos que no violen reglas."""
    valid = []
    
    for dish in dishes:
        if not violates_rules(dish, slot, current_plan, rules):
            valid.append(dish)
    
    return valid


def violates_rules(dish, slot, current_plan, rules):
    """
    Verifica si asignar este plato al slot viola alguna regla.
    
    Returns:
        True si viola alguna regla, False si es válido
    """
    for tag_id in dish.tags:
        tag_rule = rules[tag_id]
        
        # Regla 1: Max por semana
        if tag_rule.maxPerWeek is not None:
            count = current_plan.count_tag_usage(tag_id)
            if count >= tag_rule.maxPerWeek:
                return True
        
        # Regla 2: No consecutivo
        if tag_rule.noConsecutive:
            adjacent_slots = current_plan.get_adjacent_slots(slot)
            for adj_slot in adjacent_slots:
                if adj_slot.dish and tag_id in adj_slot.dish.tags:
                    return True
        
        # Regla 3: No duplicar en el mismo día
        if tag_rule.noDuplicateInDay:
            same_day_slots = current_plan.get_same_day_slots(slot)
            for sd_slot in same_day_slots:
                if sd_slot.dish and tag_id in sd_slot.dish.tags:
                    return True
        
        # Regla 4: Restricción de tipo de comida
        if tag_rule.mealTypeRestriction is not None:
            if slot.mealType != tag_rule.mealTypeRestriction:
                return True
    
    return False


def weighted_random_pick(candidates, current_plan):
    """
    Elige un plato aleatoriamente, priorizando los menos usados.
    
    Lógica:
    - Platos con 0 usos: peso 3
    - Platos con 1 uso: peso 2
    - Platos con 2+ usos: peso 1
    """
    weights = []
    for dish in candidates:
        usage_count = current_plan.count_dish_usage(dish.id)
        
        if usage_count == 0:
            weight = 3
        elif usage_count == 1:
            weight = 2
        else:
            weight = 1
        
        weights.append(weight)
    
    # Random weighted choice
    import random
    return random.choices(candidates, weights=weights)[0]
```

---

### Helpers

```swift
extension WeekPlan {
    func getEmptySlots() -> [MealSlot] {
        slots.filter { $0.dish == nil }
    }
    
    func countTagUsage(_ tagId: UUID) -> Int {
        var count = 0
        for slot in slots {
            if let dishId = slot.dish,
               let dish = DishRepository.shared.getDish(by: dishId),
               dish.tags.contains(tagId) {
                count += 1
            }
        }
        return count
    }
    
    func countDishUsage(_ dishId: UUID) -> Int {
        slots.filter { $0.dish == dishId }.count
    }
    
    func getAdjacentSlots(_ slot: MealSlot) -> [MealSlot] {
        var adjacent: [MealSlot] = []
        
        // Día anterior, mismo tipo de comida
        if slot.dayOfWeek > 0 {
            if let prevSlot = slots.first(where: {
                $0.dayOfWeek == slot.dayOfWeek - 1 && $0.mealType == slot.mealType
            }) {
                adjacent.append(prevSlot)
            }
        }
        
        // Día siguiente, mismo tipo de comida
        if slot.dayOfWeek < 6 {
            if let nextSlot = slots.first(where: {
                $0.dayOfWeek == slot.dayOfWeek + 1 && $0.mealType == slot.mealType
            }) {
                adjacent.append(nextSlot)
            }
        }
        
        return adjacent
    }
    
    func getSameDaySlots(_ slot: MealSlot) -> [MealSlot] {
        slots.filter {
            $0.dayOfWeek == slot.dayOfWeek && $0.id != slot.id
        }
    }
}
```

---

## Sincronización y Persistencia

### SwiftData (iOS 17+)

#### Configuración

```swift
import SwiftData

@main
struct MealMoodApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(for: [
            Settings.self,
            Tag.self,
            Dish.self,
            WeekPlan.self,
            MealSlot.self
        ])
    }
}
```

#### Modelo de Datos

```swift
@Model
final class Settings {
    var mealWindows: String // "dinnerOnly", "lunchOnly", "both"
    var includeWeekends: Bool
    var language: String // "spanish", "english"
    
    var calendarId: String?
    var syncEnabled: Bool
    var lunchTime: Date
    var dinnerTime: Date
    var eventDuration: Int
    var eventPrefix: String
    var reminderMinutesBefore: Int?
    
    var isPremium: Bool
    
    init() {
        self.mealWindows = "dinnerOnly"
        self.includeWeekends = true
        self.language = "spanish"
        self.calendarId = nil
        self.syncEnabled = false
        self.lunchTime = Date() // set to 14:00
        self.dinnerTime = Date() // set to 21:00
        self.eventDuration = 60
        self.eventPrefix = "🍽️"
        self.reminderMinutesBefore = nil
        self.isPremium = false
    }
}

@Model
final class Tag {
    var id: UUID
    var name: String
    var color: String
    var maxPerWeek: Int?
    var noConsecutive: Bool
    var noDuplicateInDay: Bool
    var mealTypeRestriction: String? // "lunch", "dinner", nil
    
    init(id: UUID = UUID(), name: String, color: String, maxPerWeek: Int? = nil, noConsecutive: Bool = false, noDuplicateInDay: Bool = false, mealTypeRestriction: String? = nil) {
        self.id = id
        self.name = name
        self.color = color
        self.maxPerWeek = maxPerWeek
        self.noConsecutive = noConsecutive
        self.noDuplicateInDay = noDuplicateInDay
        self.mealTypeRestriction = mealTypeRestriction
    }
}

@Model
final class Dish {
    var id: UUID
    var name: String
    var descriptionText: String?
    var tagIds: [UUID]
    var createdAt: Date
    
    init(id: UUID = UUID(), name: String, descriptionText: String? = nil, tagIds: [UUID] = [], createdAt: Date = Date()) {
        self.id = id
        self.name = name
        self.descriptionText = descriptionText
        self.tagIds = tagIds
        self.createdAt = createdAt
    }
}

@Model
final class WeekPlan {
    var id: UUID
    var weekStartDate: Date
    var createdAt: Date
    var updatedAt: Date
    
    @Relationship(deleteRule: .cascade)
    var slots: [MealSlot]
    
    init(id: UUID = UUID(), weekStartDate: Date, slots: [MealSlot] = [], createdAt: Date = Date(), updatedAt: Date = Date()) {
        self.id = id
        self.weekStartDate = weekStartDate
        self.slots = slots
        self.createdAt = createdAt
        self.updatedAt = updatedAt
    }
}

@Model
final class MealSlot {
    var id: UUID
    var dayOfWeek: Int
    var mealType: String // "lunch", "dinner"
    var dishId: UUID?
    var calendarEventId: String?
    
    init(id: UUID = UUID(), dayOfWeek: Int, mealType: String, dishId: UUID? = nil, calendarEventId: String? = nil) {
        self.id = id
        self.dayOfWeek = dayOfWeek
        self.mealType = mealType
        self.dishId = dishId
        self.calendarEventId = calendarEventId
    }
}
```

---

### CloudKit (Sincronización iCloud)

#### Configuración

1. Habilitar CloudKit en Xcode:
   - Target → Signing & Capabilities → + Capability → iCloud.
   - Activar "CloudKit".
   - Crear container (ej: `iCloud.com.mealMood.app`).

2. Configurar SwiftData para usar CloudKit:
```swift
@main
struct MealMoodApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
        .modelContainer(
            for: [Settings.self, Tag.self, Dish.self, WeekPlan.self, MealSlot.self],
            isStoredInMemoryOnly: false,
            isAutosaveEnabled: true,
            isUndoEnabled: false,
            cloudKitContainerIdentifier: "iCloud.com.mealMood.app"
        )
    }
}
```

**Comportamiento**:
- Datos se sincronizan automáticamente entre dispositivos del mismo usuario.
- Sincronización en background (no requiere acción del usuario).
- Conflictos resueltos por "last write wins" (por defecto).

---

## Monetización

### Free Tier

**Limitaciones**:
- Máximo 20 platos.
- Solo 10 tags predefinidos (no se pueden crear custom).
- Solo reglas básicas (max/semana, no consecutivo, no mismo día).
- Banner AdMob en parte inferior de Home.

**AdMob Configuración**:
```swift
import GoogleMobileAds

struct AdBannerView: UIViewRepresentable {
    func makeUIView(context: Context) -> GADBannerView {
        let banner = GADBannerView(adSize: GADAdSizeBanner)
        banner.adUnitID = "ca-app-pub-XXXXXXXXXXXXXXXX/XXXXXXXXXX" // reemplazar con tu ID
        banner.rootViewController = UIApplication.shared.windows.first?.rootViewController
        banner.load(GADRequest())
        return banner
    }
    
    func updateUIView(_ uiView: GADBannerView, context: Context) {}
}
```

**Ubicación**:
- Home → parte inferior, sticky.
- Background: #FFF9F5 (mismo color que la app).
- Altura: 50px.
- NO en otras pantallas (Settings, gestión de platos, etc.).

---

### Premium Tier

**Precio**:
- Mensual: $2.99/mes.
- Anual: $19.99/año (ahorro 44%).

**Beneficios**:
- Sin publicidad.
- Platos ilimitados.
- Tags custom ilimitados.
- (Futuro) Reglas avanzadas.
- (Futuro) Múltiples planificaciones guardadas.
- (Futuro) Exportar a lista de compra.
- (Futuro) Analytics/histórico.

**IAP Configuración**:
```swift
import StoreKit

class StoreManager: ObservableObject {
    @Published var products: [Product] = []
    @Published var isPremium: Bool = false
    
    private let productIds = [
        "com.mealmood.premium.monthly",
        "com.mealmood.premium.yearly"
    ]
    
    init() {
        Task {
            await loadProducts()
            await checkPremiumStatus()
        }
    }
    
    func loadProducts() async {
        do {
            products = try await Product.products(for: productIds)
        } catch {
            print("Failed to load products: \(error)")
        }
    }
    
    func purchase(_ product: Product) async {
        do {
            let result = try await product.purchase()
            
            switch result {
            case .success(let verification):
                switch verification {
                case .verified(let transaction):
                    await transaction.finish()
                    isPremium = true
                case .unverified:
                    break
                }
            case .userCancelled, .pending:
                break
            @unknown default:
                break
            }
        } catch {
            print("Purchase failed: \(error)")
        }
    }
    
    func restorePurchases() async {
        do {
            try await AppStore.sync()
            await checkPremiumStatus()
        } catch {
            print("Restore failed: \(error)")
        }
    }
    
    private func checkPremiumStatus() async {
        for await result in Transaction.currentEntitlements {
            if case .verified(let transaction) = result,
               productIds.contains(transaction.productID) {
                isPremium = true
                return
            }
        }
        isPremium = false
    }
}
```

---

## Stack Técnico

### Plataforma
- **iOS**: mínimo iOS 17.0 (para SwiftData).
- **Dispositivos**: iPhone (optimizado para todos los tamaños).
- **Orientación**: Portrait only (landscape no necesario para MVP).

### Lenguajes y Frameworks
- **Swift**: 5.9+.
- **SwiftUI**: para toda la UI.
- **SwiftData**: persistencia local.
- **EventKit**: integración con calendario nativo.
- **CloudKit**: sincronización iCloud.
- **StoreKit 2**: IAP (In-App Purchases).

### Dependencias Externas
- **Google Mobile Ads SDK**: para AdMob.
  - Instalación: Swift Package Manager (SPM) o CocoaPods.
  - URL: `https://github.com/googleads/swift-package-manager-google-mobile-ads`

### Arquitectura
- **MVVM** (Model-View-ViewModel):
  - **Models**: Settings, Tag, Dish, WeekPlan, MealSlot (SwiftData models).
  - **ViewModels**: HomeViewModel, DishViewModel, SettingsViewModel, etc.
  - **Views**: SwiftUI views.

### Testing
- **Unit tests**: lógica de algoritmo de autocompletado, validación de reglas.
- **UI tests**: flujos críticos (onboarding, drag & drop, crear plato).

---

## Plan de Implementación (Sprints)

### Sprint 1: Setup e Infraestructura (1 semana)
- Configurar proyecto Xcode.
- Configurar SwiftData models.
- Configurar CloudKit.
- Crear paleta de colores y componentes UI reutilizables.
- Setup Google AdMob.

**Entregables**:
- Proyecto compilable con estructura de carpetas.
- Modelos de datos persistentes.
- Componentes UI básicos (buttons, cards, pills).

---

### Sprint 2: Onboarding (1 semana)
- Pantalla de bienvenida.
- 4 pasos de configuración inicial.
- Navegación fluida entre pasos.
- Crear tags predefinidos en primera carga.

**Entregables**:
- Onboarding completo y funcional.
- Usuario puede completar setup inicial.

---

### Sprint 3: Gestión de Platos (1 semana)
- Pantalla de lista de platos.
- Crear/editar/eliminar platos.
- Selector de tags (modal).
- Validaciones de formulario.

**Entregables**:
- Usuario puede crear y gestionar platos.
- Lista de platos persistente.

---

### Sprint 4: Pantalla Principal (Calendario) (2 semanas)
- Layout de calendario semanal.
- Slots vacíos y llenos.
- Drawer de platos.
- Drag & drop básico (sin validaciones).
- Navegación entre semanas.

**Entregables**:
- Pantalla Home funcional.
- Usuario puede asignar platos manualmente.

---

### Sprint 5: Validación de Reglas y Drag & Drop Avanzado (1 semana)
- Implementar lógica de validación de reglas.
- Feedback visual en drag & drop (bordes verdes/rojos).
- Haptic feedback.
- Animaciones.

**Entregables**:
- Drag & drop valida reglas en tiempo real.
- Feedback visual y táctil funcional.

---

### Sprint 6: Autocompletado (1 semana)
- Implementar algoritmo de autocompletado.
- Animación secuencial al rellenar slots.
- Confetti y toast.
- Botón "Resetear" con confirmación.

**Entregables**:
- Botón "Completar" funcional.
- Usuario puede autocompletar semana respetando reglas.

---

### Sprint 7: Gestión de Tags (1 semana)
- Pantalla de lista de tags.
- Editar reglas de tags.
- Localización de tags predefinidos.

**Entregables**:
- Usuario puede modificar reglas de tags.
- Cambios se aplican al algoritmo.

---

### Sprint 8: Sincronización con Calendario (1 semana)
- Solicitar permiso de EventKit.
- Crear/eliminar eventos al asignar/quitar platos.
- Configuración en Settings (horarios, prefijo, etc.).
- Actualizar eventos al cambiar hora.

**Entregables**:
- Sincronización con calendario funcional.
- Eventos visibles en app Calendario nativa.

---

### Sprint 9: Settings y Multiidioma (1 semana)
- Pantalla de Settings completa.
- Cambio de idioma (español/inglés).
- Localización de toda la app.
- Persistencia de configuración.

**Entregables**:
- Settings funcional.
- App totalmente bilingüe.

---

### Sprint 10: IAP y Premium (1 semana)
- Configurar StoreKit.
- Pantalla de Premium con planes.
- Compra y restauración.
- Limitar features en Free.

**Entregables**:
- Usuario puede comprar Premium.
- Features Free/Premium funcionan correctamente.

---

### Sprint 11: Testing y QA (1 semana)
- Unit tests para algoritmo.
- UI tests para flujos críticos.
- Bug fixing.
- Performance optimization.

**Entregables**:
- App estable, sin bugs críticos.
- Cobertura de tests >70%.

---

### Sprint 12: Polish y Lanzamiento (1 semana)
- Ajustes finales de UI/UX.
- App Store assets (screenshots, descripción, keywords).
- Beta testing con TestFlight.
- Envío a App Store.

**Entregables**:
- App publicada en App Store.

---

## Anexos

### A. Glosario

- **Slot**: espacio en el calendario donde se asigna una comida (comida o cena de un día específico).
- **Tag**: etiqueta asociada a un plato (ej: Carne, Pescado, Sin Gluten).
- **Tag Rule**: regla asociada a un tag (ej: máximo 3 veces por semana).
- **Autocompletado**: algoritmo que rellena slots vacíos respetando reglas.
- **Drag & Drop**: interacción de arrastrar un plato y soltarlo en un slot.
- **EventKit**: framework de iOS para interactuar con el calendario nativo.
- **SwiftData**: framework de Apple para persistencia local (iOS 17+).
- **CloudKit**: servicio de Apple para sincronización en la nube.
- **IAP**: In-App Purchase (compra dentro de la app).

---

### B. Referencias Técnicas

#### SwiftUI Drag & Drop
- Documentación: https://developer.apple.com/documentation/swiftui/view/ondrag(_:)
- Tutorial: https://www.hackingwithswift.com/quick-start/swiftui/how-to-support-drag-and-drop-in-swiftui

#### EventKit
- Documentación: https://developer.apple.com/documentation/eventkit
- Tutorial: https://www.hackingwithswift.com/example-code/system/how-to-add-events-to-the-users-calendar-using-eventkit

#### SwiftData
- Documentación: https://developer.apple.com/documentation/swiftdata
- Tutorial: https://www.hackingwithswift.com/quick-start/swiftdata

#### StoreKit 2
- Documentación: https://developer.apple.com/documentation/storekit
- Tutorial: https://www.hackingwithswift.com/quick-start/storekit

#### Google Mobile Ads
- Documentación: https://developers.google.com/admob/ios/quick-start
- Setup: https://developers.google.com/admob/ios/quick-start

---

### C. Notas de Implementación

#### Consideraciones de Performance
- **SwiftData queries**: usar `@Query` con predicates para filtrar eficientemente.
- **Drag & Drop**: mantener 60fps, usar `LazyVStack` en lista de platos.
- **Algoritmo de autocompletado**: ejecutar en background thread (`Task`), no bloquear UI.
- **Sincronización CloudKit**: usar queue de operaciones para batch updates.

#### Consideraciones de UX
- **Feedback inmediato**: haptic feedback en cada interacción importante.
- **Animaciones suaves**: spring animations (0.3-0.5s duration).
- **Loading states**: mostrar ProgressView durante operaciones lentas.
- **Empty states**: ilustraciones amigables, no solo texto.
- **Error handling**: mostrar alerts/toasts amigables, no errores técnicos.

#### Consideraciones de Accesibilidad
- **VoiceOver**: etiquetar todos los elementos interactivos.
- **Dynamic Type**: usar `@ScaledMetric` para tamaños de fuente.
- **Color Contrast**: asegurar ratio mínimo 4.5:1 (WCAG AA).
- **Haptic Feedback**: opcional, respetar configuración del sistema.

---

### D. Checklist Pre-Lanzamiento

#### Funcionalidad
- [ ] Onboarding completo y sin bugs.
- [ ] Crear/editar/eliminar platos funcional.
- [ ] Drag & drop fluido (60fps).
- [ ] Validación de reglas correcta.
- [ ] Autocompletado respeta reglas.
- [ ] Sincronización con calendario funcional.
- [ ] Settings guarda cambios correctamente.
- [ ] Multiidioma (ES/EN) sin strings faltantes.
- [ ] IAP (compra y restauración) funcional.
- [ ] AdMob muestra ads correctamente.

#### Testing
- [ ] Unit tests para algoritmo (>80% cobertura).
- [ ] UI tests para flujos críticos.
- [ ] Testeo manual en iPhone SE, iPhone 15 Pro, iPhone 15 Pro Max.
- [ ] Beta testing con 10+ usuarios externos (TestFlight).
- [ ] Performance: app carga en <2s, drag & drop sin lag.
- [ ] Memoria: no memory leaks (Instruments).

#### App Store
- [ ] Screenshots en 3 tamaños (6.7", 6.5", 5.5").
- [ ] Video preview (opcional, recomendado).
- [ ] Descripción optimizada (keywords: meal planner, family meals, weekly menu).
- [ ] Privacy policy URL.
- [ ] Support URL.
- [ ] Categoría: Food & Drink.
- [ ] Keywords: meal planner, weekly menu, family meals, meal organizer, recipe planner.

#### Legal
- [ ] Privacy policy publicada (mencionar EventKit, CloudKit, AdMob).
- [ ] Terms of Service (opcional, recomendado).
- [ ] App Store Review Guidelines (revisar compliance).

---

## Fin del PRD

**Versión**: 1.0  
**Fecha**: 2025-02-16  
**Autor**: Alex + Claude  
**Próxima revisión**: Post-Sprint 6 (evaluar ajustes de MVP)

---

## Notas para Claude Code

Este PRD está diseñado para ser consumido por Claude Code. Contiene:

1. **Especificaciones completas** de funcionalidades MVP y Freemium.
2. **Modelo de datos** con Swift structs/classes.
3. **Ejemplos de código** para componentes clave (drag & drop, EventKit, SwiftData).
4. **Wireframes textuales** detallados de cada pantalla.
5. **Paleta de colores** y componentes UI reutilizables.
6. **Plan de implementación** en sprints secuenciales.

**Instrucciones para Claude Code**:
- Implementar MVP siguiendo orden de sprints.
- Usar SwiftUI + SwiftData (iOS 17+).
- Priorizar UX fluida (animaciones, haptic feedback).
- Código limpio, comentado, siguiendo Swift style guide.
- Tests unitarios para lógica crítica (algoritmo, validaciones).

**Contacto**: Si hay dudas o ambigüedades, consultar con el product owner (Alex).

---

¡Buena suerte con la implementación! 🚀✨
