diff --git a/refactor-strategy.md b/refactor-strategy.md
new file mode 100644
index 0000000..8731009
--- /dev/null
+++ b/refactor-strategy.md
@@ -0,0 +1,62 @@
+# Refactor Strategy
+
+This document outlines a pragmatic, step-by-step refactor plan to improve structure, readability, and maintainability. Each step includes a clear outcome and a checkbox to track progress.
+
+Last updated: 2025-10-18
+
+## Goals
+- Separate concerns (routing, HTTP/API, mapping/normalization, UI logic)
+- Improve readability and testability
+- Establish light-weight standards (naming, lint/format) without blocking development
+- Keep changes incremental and safe
+
+## Phases and Steps
+
+### Phase 1 — Routing and Auth (Foundational)
+- [x] Extract router into `src/router/index.js` with named routes
+- [x] Add route meta `requiresAuth` and a global auth guard
+- [x] Remove auth-redirect from `App.vue` (handled by guard instead)
+- [ ] Convert top-level nav to use route names consistently (optional)
+
+Outcome: Routing logic is centralized and testable; pages redirect consistently based on auth.
+
+### Phase 2 — API Layer Split (Incremental)
+- [x] Add `src/api/http.js` wrapper for JSON fetch with error handling and env-based base URL
+- [x] Add `src/api/mappers/mealMapper.js` to normalize Meal data (dates)
+- [x] Add `src/api/meals.js` and migrate MealPlan API calls (get upcoming, mark consumed, delete)
+- [ ] Create `src/api/recipes.js` and migrate recipe endpoints
+- [ ] Create `src/api/shopping.js` and migrate shopping endpoints
+- [ ] Create `src/api/auth.js` and migrate auth endpoints
+
+Outcome: Feature modules call cohesive services; logic for mapping/normalization is isolated and testable.
+
+### Phase 3 — Composables (UI-Facing Logic)
+- [ ] Add `src/composables/useAuth.js` (user ref, ensureAuth)
+- [ ] Add `src/composables/useMeals.js` (fetch and mutate meals)
+- [ ] Refactor pages to use composables and `
diff --git a/src/api/http.js b/src/api/http.js
new file mode 100644
index 0000000..d5fbc7f
--- /dev/null
+++ b/src/api/http.js
@@ -0,0 +1,30 @@
+const BASE = process.env.VUE_APP_API_BASE || '/api'
+
+async function request(path, options = {}) {
+ const url = path.startsWith('http') ? path : `${BASE}${path}`
+ const resp = await fetch(url, {
+ credentials: 'include',
+ ...options,
+ headers: {
+ 'Accept': 'application/json',
+ ...(options.body ? { 'Content-Type': 'application/json' } : {}),
+ ...(options.headers || {}),
+ },
+ })
+ if (!resp.ok) {
+ let message = `${resp.status} ${resp.statusText}`
+ try { const err = await resp.json(); message = err.message || message } catch (_) { /* ignore */ }
+ const error = new Error(message)
+ error.status = resp.status
+ throw error
+ }
+ if (resp.status === 204) return null
+ return resp.json()
+}
+
+export const http = {
+ get: (p) => request(p),
+ post: (p, body) => request(p, { method: 'POST', body: JSON.stringify(body) }),
+ put: (p, body) => request(p, { method: 'PUT', body: JSON.stringify(body) }),
+ del: (p) => request(p, { method: 'DELETE' }),
+}
diff --git a/src/api/mappers/mealMapper.js b/src/api/mappers/mealMapper.js
new file mode 100644
index 0000000..f3973ae
--- /dev/null
+++ b/src/api/mappers/mealMapper.js
@@ -0,0 +1,16 @@
+function toDate(value) {
+ return value ? new Date(value) : value
+}
+
+export function mapMeal(meal) {
+ if (!meal) return meal
+ meal.suggested_date = toDate(meal.suggested_date)
+ meal.purchase_date = toDate(meal.purchase_date)
+ meal.consumed_date = toDate(meal.consumed_date)
+ return meal
+}
+
+export function mapMeals(list) {
+ if (!Array.isArray(list)) return []
+ return list.map(mapMeal)
+}
diff --git a/src/api/meals.js b/src/api/meals.js
new file mode 100644
index 0000000..ed02936
--- /dev/null
+++ b/src/api/meals.js
@@ -0,0 +1,18 @@
+import { http } from './http'
+import { mapMeal, mapMeals } from './mappers/mealMapper'
+
+export async function getUpcomingMeals(from, to) {
+ const params = `?from=${encodeURIComponent(from.toISOString())}&to=${encodeURIComponent(to.toISOString())}`
+ const meals = await http.get(`/meals/upcoming${params}`)
+ const normalized = mapMeals(meals)
+ return normalized.sort((a, b) => a.suggested_date - b.suggested_date)
+}
+
+export async function markMealConsumed(mealId) {
+ const meal = await http.post(`/meals/${encodeURIComponent(mealId)}/consumed`)
+ return mapMeal(meal)
+}
+
+export async function deleteMeal(mealId) {
+ return http.del(`/meals/${encodeURIComponent(mealId)}`)
+}
diff --git a/src/components/meals/MealPlanPage.vue b/src/components/meals/MealPlanPage.vue
index 9940678..1a54975 100644
--- a/src/components/meals/MealPlanPage.vue
+++ b/src/components/meals/MealPlanPage.vue
@@ -81,7 +81,7 @@ ul.actions {