Index: frontend/src/App.css
===================================================================
--- frontend/src/App.css	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ frontend/src/App.css	(revision 72053b5fc3ff291757af1cdd64e8fdfcb9e9b17d)
@@ -1,5 +1,42 @@
+#root {
+  max-width: 1280px;
+  margin: 0 auto;
+  padding: 2rem;
+  text-align: center;
+}
 
-@import "tailwindcss";
-@plugin "daisyui" {
-    themes: light --default;
+.logo {
+  height: 6em;
+  padding: 1.5em;
+  will-change: filter;
+  transition: filter 300ms;
 }
+.logo:hover {
+  filter: drop-shadow(0 0 2em #646cffaa);
+}
+.logo.react:hover {
+  filter: drop-shadow(0 0 2em #61dafbaa);
+}
+
+@keyframes logo-spin {
+  from {
+    transform: rotate(0deg);
+  }
+  to {
+    transform: rotate(360deg);
+  }
+}
+
+@media (prefers-reduced-motion: no-preference) {
+  a:nth-of-type(2) .logo {
+    animation: logo-spin infinite 20s linear;
+  }
+}
+
+.card {
+  padding: 2em;
+}
+
+.read-the-docs {
+  color: #888;
+}
Index: frontend/src/App.jsx
===================================================================
--- frontend/src/App.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ frontend/src/App.jsx	(revision 72053b5fc3ff291757af1cdd64e8fdfcb9e9b17d)
@@ -1,77 +1,35 @@
-import { Navigate, Route, Routes } from "react-router-dom";
-import { clearAuthSession, getAuthToken, isTokenExpired } from "./utils/authSession";
+import { useState } from 'react'
+import reactLogo from './assets/react.svg'
+import viteLogo from '/vite.svg'
+import './App.css'
 
-import LandingPage from "./pages/LandingPage/LandingPage.jsx";
-import Login from "./pages/Login/Login.jsx";
-import Register from "./pages/Register/Register.jsx";
-import DashboardLayout from "./pages/Dashboard/DashboardLayout.jsx";
-import ControlCenter from "./pages/Dashboard/pages/ControlCenter/ControlCenter.jsx";
-import Training from "./pages/Dashboard/pages/Training/Training.jsx";
-import TrainingTracking from "./pages/Dashboard/pages/Training/TrainingTracking.jsx";
-import NewTrainingSession from "./pages/Dashboard/pages/Training/NewTrainingSession.jsx";
-import Weight from "./pages/Dashboard/pages/Weight/Weight.jsx";
-import WeightTracking from "./pages/Dashboard/pages/Weight/WeightTracking.jsx";
-import NewWeightIntake from "./pages/Dashboard/pages/Weight/NewWeightIntake.jsx";
-import Finance from "./pages/Dashboard/pages/Finance/Finance.jsx";
-import FinanceTracking from "./pages/Dashboard/pages/Finance/FinanceTracking.jsx";
-import NewIncome from "./pages/Dashboard/pages/Finance/NewIncome.jsx";
-import Investing from "./pages/Dashboard/pages/Investing/Investing.jsx";
-import InvestingTracking from "./pages/Dashboard/pages/Investing/InvestingTracking.jsx";
-import NewInvestment from "./pages/Dashboard/pages/Investing/NewInvestment.jsx";
-import Discipline from "./pages/Dashboard/pages/Discipline/Discipline.jsx";
-import DisciplineTracking from "./pages/Dashboard/pages/Discipline/DisciplineTracking.jsx";
-import CustomCategoryKanban from "./pages/Dashboard/pages/CustomCategory/CustomCategoryKanban.jsx";
+function App() {
+  const [count, setCount] = useState(0)
 
-function RequireAuth({ children }) {
-  const token = getAuthToken();
-  if (token && isTokenExpired(token)) {
-    clearAuthSession();
-    return <Navigate to="/login" replace />;
-  }
-  if (!token) return <Navigate to="/login" replace />;
-  return children;
+  return (
+    <>
+      <div>
+        <a href="https://vite.dev" target="_blank">
+          <img src={viteLogo} className="logo" alt="Vite logo" />
+        </a>
+        <a href="https://react.dev" target="_blank">
+          <img src={reactLogo} className="logo react" alt="React logo" />
+        </a>
+      </div>
+      <h1>Vite + React</h1>
+      <div className="card">
+        <button onClick={() => setCount((count) => count + 1)}>
+          count is {count}
+        </button>
+        <p>
+          Edit <code>src/App.jsx</code> and save to test HMR
+        </p>
+      </div>
+      <p className="read-the-docs">
+        Click on the Vite and React logos to learn more
+      </p>
+    </>
+  )
 }
 
-function App() {
-  return (
-    <div data-theme="forest" className="min-h-screen w-full">
-      <Routes>
-        <Route path="/" element={<LandingPage />} />
-        <Route path="/login" element={<Login />} />
-        <Route path="/register" element={<Register />} />
-        <Route
-          path="/dashboard"
-          element={
-            <RequireAuth>
-              <DashboardLayout />
-            </RequireAuth>
-          }
-        >
-          <Route index element={<Navigate to="control-center" replace />} />
-          <Route path="control-center" element={<ControlCenter />} />
-          <Route path="training" element={<Training />} />
-          <Route path="training/tracking" element={<TrainingTracking />} />
-          <Route
-            path="training/sessions/new"
-            element={<NewTrainingSession />}
-          />
-          <Route path="weight" element={<Weight />} />
-          <Route path="weight/tracking" element={<WeightTracking />} />
-          <Route path="weight/intakes/new" element={<NewWeightIntake />} />
-          <Route path="finance" element={<Finance />} />
-           <Route path="finance/tracking" element={<FinanceTracking />} />
-           <Route path="finance/incomes/new" element={<NewIncome />} />
-          <Route path="investing" element={<Investing />} />
-          <Route path="investing/tracking" element={<InvestingTracking />} />
-          <Route path="investing/assets/new" element={<NewInvestment />} />
-          <Route path="discipline" element={<Discipline />} />
-           <Route path="discipline/tracking" element={<DisciplineTracking />} />
-           <Route path="custom/:customTrackingId" element={<CustomCategoryKanban />} />
-        </Route>
-        <Route path="*" element={<Navigate to="/" replace />} />
-      </Routes>
-    </div>
-  );
-}
-
-export default App;
+export default App
Index: frontend/src/api/axios.js
===================================================================
--- frontend/src/api/axios.js	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ frontend/src/api/axios.js	(revision 72053b5fc3ff291757af1cdd64e8fdfcb9e9b17d)
@@ -1,32 +1,6 @@
 import axios from "axios";
-import { getAuthToken, isTokenExpired, logoutAndRedirect } from "../utils/authSession";
 
-const api = axios.create({
-  baseURL: import.meta.env.VITE_API_BASE_URL ?? "http://localhost:8080/api",
+export default axios.create({
+  baseURL: "http://localhost:8080/api",
+  withCredentials: true, // important if using cookies
 });
-
-api.interceptors.request.use((config) => {
-  const token = getAuthToken();
-  if (token) {
-    if (isTokenExpired(token)) {
-      logoutAndRedirect("/dashboard");
-      return Promise.reject(new Error("Authentication session expired"));
-    }
-    config.headers = config.headers ?? {};
-    config.headers.Authorization = `Bearer ${token}`;
-  }
-  return config;
-});
-
-api.interceptors.response.use(
-  (response) => response,
-  (error) => {
-    const status = error?.response?.status;
-    if (status === 401 || status === 403) {
-      logoutAndRedirect("/dashboard");
-    }
-    return Promise.reject(error);
-  },
-);
-
-export default api;
Index: frontend/src/api/dailyCompletion.js
===================================================================
--- frontend/src/api/dailyCompletion.js	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,16 +1,0 @@
-import api from "./axios";
-
-export async function computeDailyCompletion(date /* yyyy-mm-dd or undefined */) {
-  const res = await api.post("/discipline/daily-completions/compute", null, {
-    params: date ? { date } : {},
-  });
-  return res?.data;
-}
-
-export async function getDailyCompletions({ page = 0, size = 14 } = {}) {
-  const res = await api.get("/discipline/daily-completions", {
-    params: { page, size },
-  });
-  return res?.data;
-}
-
Index: frontend/src/api/discipline.js
===================================================================
--- frontend/src/api/discipline.js	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,96 +1,0 @@
-import api from "./axios";
-
-export async function getDisciplineStatus() {
-  const res = await api.get("/discipline/status");
-  return Boolean(res?.data?.tracking);
-}
-
-export async function startDisciplineTracking() {
-  return api.post("/discipline/start", {});
-}
-
-export async function getTasks({ page = 0, size = 50 } = {}) {
-  const res = await api.get("/discipline/tasks", { params: { page, size } });
-  return res?.data;
-}
-
-export async function createTask(payload) {
-  const res = await api.post("/discipline/tasks", payload);
-  return res?.data;
-}
-
-export async function updateTask(taskId, payload) {
-  const res = await api.put(`/discipline/tasks/${taskId}`, payload);
-  return res?.data;
-}
-
-export async function updateTaskFinished(taskId, isFinished) {
-  const res = await api.patch(`/discipline/tasks/${taskId}/finished`, {
-    isFinished,
-  });
-  return res?.data;
-}
-
-export async function deleteTask(taskId) {
-  return api.delete(`/discipline/tasks/${taskId}`);
-}
-
-export async function getCustomTrackingCategories() {
-  const res = await api.get("/discipline/custom-tracking-categories");
-  return res?.data;
-}
-
-export async function createCustomTrackingCategory(payload) {
-  const res = await api.post("/discipline/custom-tracking-categories", payload);
-  return res?.data;
-}
-
-export async function getCustomCategoryTasks(customTrackingId) {
-  const res = await api.get(
-    `/discipline/custom-tracking-categories/${customTrackingId}/tasks`
-  );
-  return res?.data;
-}
-
-export async function createCustomCategoryTask(customTrackingId, payload) {
-  const res = await api.post(
-    `/discipline/custom-tracking-categories/${customTrackingId}/tasks`,
-    payload
-  );
-  return res?.data;
-}
-
-export async function updateCustomCategoryTask(customTrackingId, taskId, payload) {
-  const res = await api.put(
-    `/discipline/custom-tracking-categories/${customTrackingId}/tasks/${taskId}`,
-    payload
-  );
-  return res?.data;
-}
-
-export async function updateCustomCategoryTaskFinished(
-  customTrackingId,
-  taskId,
-  isFinished
-) {
-  const res = await api.patch(
-    `/discipline/custom-tracking-categories/${customTrackingId}/tasks/${taskId}/finished`,
-    { isFinished }
-  );
-  return res?.data;
-}
-
-export async function updateCustomCategoryTaskStatus(customTrackingId, taskId, status) {
-  const res = await api.patch(
-    `/discipline/custom-tracking-categories/${customTrackingId}/tasks/${taskId}/status`,
-    { status }
-  );
-  return res?.data;
-}
-
-export async function deleteCustomCategoryTask(customTrackingId, taskId) {
-  return api.delete(
-    `/discipline/custom-tracking-categories/${customTrackingId}/tasks/${taskId}`
-  );
-}
-
Index: frontend/src/api/finance.js
===================================================================
--- frontend/src/api/finance.js	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,26 +1,0 @@
-import api from "./axios";
-
-export async function getFinanceStatus() {
-  const res = await api.get("/finance/status");
-  return Boolean(res?.data?.tracking);
-}
-
-export async function startFinanceTracking(payload) {
-  return api.post("/finance/start", payload);
-}
-
-export async function getFinanceProfile() {
-  const res = await api.get("/finance/profile");
-  return res?.data;
-}
-
-export async function getIncomes({ page = 0, size = 5 } = {}) {
-  const res = await api.get("/finance/incomes", { params: { page, size } });
-  return res?.data;
-}
-
-export async function createIncome(payload) {
-  const res = await api.post("/finance/incomes", payload);
-  return res?.data;
-}
-
Index: frontend/src/api/twelveData.js
===================================================================
--- frontend/src/api/twelveData.js	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,284 +1,0 @@
-const BASE_URL =
-  import.meta.env.VITE_TWELVE_DATA_BASE_URL ?? "https://api.twelvedata.com";
-
-const API_KEY = import.meta.env.VITE_TWELVE_DATA_API_KEY ?? "";
-
-const CACHE_TTL_MS = 5 * 60_000;
-const MAX_SYMBOLS_PER_REQUEST = 20;
-
-// Cache by Twelve Data symbol (normalized)
-const priceCache = new Map();
-const inFlight = new Map();
-
-function getCachedPrice(twelveSymbol) {
-  const item = priceCache.get(twelveSymbol);
-  if (!item) return null;
-  if (Date.now() - item.ts > CACHE_TTL_MS) return null;
-  return item.price;
-}
-
-function setCachedPrice(twelveSymbol, price) {
-  if (!twelveSymbol || typeof price !== "number" || !Number.isFinite(price)) return;
-  priceCache.set(twelveSymbol, { price, ts: Date.now() });
-}
-
-function parsePriceJson(json, requestedSymbols) {
-  const out = {};
-  if (!json || typeof json !== "object" || Array.isArray(json)) return out;
-
-  // Single symbol response
-  if (json.price !== undefined && requestedSymbols.length === 1) {
-    const n = Number(json.price);
-    if (Number.isFinite(n)) out[requestedSymbols[0]] = n;
-    return out;
-  }
-
-  // Multi symbol response: object keyed by symbol
-  for (const sym of requestedSymbols) {
-    const n = Number(json?.[sym]?.price);
-    if (Number.isFinite(n)) out[sym] = n;
-  }
-
-  return out;
-}
-
-async function fetchBatchPrices(twelveSymbols) {
-  const params = new URLSearchParams();
-  params.set("symbol", twelveSymbols.join(","));
-  params.set("apikey", API_KEY);
-
-  const url = `${BASE_URL}/price?${params.toString()}`;
-  const resp = await fetch(url, { method: "GET" });
-
-  if (!resp.ok) {
-    const text = await resp.text().catch(() => "");
-    throw new Error(`Twelve Data request failed (HTTP ${resp.status}) ${text}`);
-  }
-
-  const json = await resp.json();
-  if (json?.status === "error") {
-    throw new Error(String(json?.message ?? "Twelve Data error"));
-  }
-
-  return parsePriceJson(json, twelveSymbols);
-}
-
-async function fetchSinglePrice(twelveSymbol) {
-  const cached = getCachedPrice(twelveSymbol);
-  if (typeof cached === "number") return cached;
-
-  if (inFlight.has(twelveSymbol)) return inFlight.get(twelveSymbol);
-
-  const p = (async () => {
-    const params = new URLSearchParams();
-    params.set("symbol", twelveSymbol);
-    params.set("apikey", API_KEY);
-    const url = `${BASE_URL}/price?${params.toString()}`;
-    const resp = await fetch(url, { method: "GET" });
-    if (!resp.ok) {
-      const text = await resp.text().catch(() => "");
-      throw new Error(`Twelve Data request failed (HTTP ${resp.status}) ${text}`);
-    }
-    const json = await resp.json();
-    if (json?.status === "error") {
-      throw new Error(String(json?.message ?? "Twelve Data error"));
-    }
-    const n = Number(json?.price);
-    if (!Number.isFinite(n)) throw new Error("Twelve Data returned no price");
-    setCachedPrice(twelveSymbol, n);
-    return n;
-  })();
-
-  inFlight.set(twelveSymbol, p);
-  try {
-    return await p;
-  } finally {
-    inFlight.delete(twelveSymbol);
-  }
-}
-
-async function fetchSingleExchangeRate(pairSymbol) {
-  const cached = getCachedPrice(pairSymbol);
-  if (typeof cached === "number") return cached;
-
-  if (inFlight.has(pairSymbol)) return inFlight.get(pairSymbol);
-
-  const p = (async () => {
-    const params = new URLSearchParams();
-    params.set("symbol", pairSymbol);
-    params.set("apikey", API_KEY);
-    const url = `${BASE_URL}/exchange_rate?${params.toString()}`;
-    const resp = await fetch(url, { method: "GET" });
-    if (!resp.ok) {
-      const text = await resp.text().catch(() => "");
-      throw new Error(`Twelve Data request failed (HTTP ${resp.status}) ${text}`);
-    }
-    const json = await resp.json();
-    if (json?.status === "error") {
-      throw new Error(String(json?.message ?? "Twelve Data error"));
-    }
-
-    const n = Number(json?.rate);
-    if (!Number.isFinite(n)) throw new Error("Twelve Data returned no rate");
-    setCachedPrice(pairSymbol, n);
-    return n;
-  })();
-
-  inFlight.set(pairSymbol, p);
-  try {
-    return await p;
-  } finally {
-    inFlight.delete(pairSymbol);
-  }
-}
-
-export function toTwelveSymbol(symbol) {
-  if (!symbol) return "";
-  const s = String(symbol).trim().toUpperCase();
-  if (!s) return "";
-
-  // Already Twelve format (common for FX/crypto)
-  if (s.includes("/")) return s;
-
-  // Common Yahoo-style crypto tickers
-  const cryptoMap = {
-    "BTC-USD": "BTC/USD",
-    "ETH-USD": "ETH/USD",
-    "SOL-USD": "SOL/USD",
-    "BNB-USD": "BNB/USD",
-    "XRP-USD": "XRP/USD",
-    "ADA-USD": "ADA/USD",
-    "DOGE-USD": "DOGE/USD",
-    "AVAX-USD": "AVAX/USD",
-    "DOT-USD": "DOT/USD",
-    "LINK-USD": "LINK/USD",
-    "MATIC-USD": "MATIC/USD",
-    "LTC-USD": "LTC/USD",
-  };
-
-  if (cryptoMap[s]) return cryptoMap[s];
-
-  // Default: assume it's a stock/ETF symbol (AAPL, SPY, BRK-B, ...)
-  return s;
-}
-
-/**
- * Fetch current prices for one or more symbols.
- * Returns an object keyed by *original input symbols* (uppercased), with numeric prices.
- */
-export async function fetchCurrentPricesBySymbol(symbols) {
-  const input = Array.isArray(symbols) ? symbols : [];
-  const uniqueOriginal = Array.from(
-    new Set(input.map((s) => String(s ?? "").trim()).filter(Boolean)),
-  );
-
-  if (uniqueOriginal.length === 0) return {};
-
-  if (!API_KEY) {
-    throw new Error(
-      "Missing Twelve Data API key (set VITE_TWELVE_DATA_API_KEY in frontend/.env.local)",
-    );
-  }
-
-  // Map original -> twelve
-  const originalToTwelve = new Map();
-  for (const orig of uniqueOriginal) {
-    originalToTwelve.set(orig, toTwelveSymbol(orig));
-  }
-
-  const allTwelveSymbols = Array.from(
-    new Set(Array.from(originalToTwelve.values()).filter(Boolean)),
-  );
-
-  const pairSymbols = allTwelveSymbols.filter((s) => String(s).includes("/"));
-  const priceSymbols = allTwelveSymbols.filter((s) => !String(s).includes("/"));
-
-  // Gather prices with cache + resilient fetch (batch first, fall back to per-symbol)
-  const twelvePrices = {};
-  const missing = [];
-  let hadAnyError = false;
-  let lastErrorMessage = "";
-
-  for (const sym of allTwelveSymbols) {
-    const cached = getCachedPrice(sym);
-    if (typeof cached === "number") {
-      twelvePrices[sym] = cached;
-    } else {
-      missing.push(sym);
-    }
-  }
-
-  // Fetch pair symbols via exchange_rate (generally the most reliable for FX/crypto pairs)
-  for (const sym of pairSymbols) {
-    if (twelvePrices[sym] !== undefined) continue;
-    try {
-      const n = await fetchSingleExchangeRate(sym);
-      if (Number.isFinite(n)) twelvePrices[sym] = n;
-    } catch (e) {
-      hadAnyError = true;
-      lastErrorMessage = e?.message || lastErrorMessage;
-    }
-  }
-
-  // Chunk batch requests to avoid overly long URLs; if a batch fails, we continue
-  // with per-symbol fetches so one invalid symbol doesn't break everything.
-  const missingPrices = missing.filter((s) => priceSymbols.includes(s));
-
-  for (let i = 0; i < missingPrices.length; i += MAX_SYMBOLS_PER_REQUEST) {
-    const chunk = missingPrices.slice(i, i + MAX_SYMBOLS_PER_REQUEST);
-    try {
-      const got = await fetchBatchPrices(chunk);
-      for (const [sym, price] of Object.entries(got)) {
-        const n = Number(price);
-        if (Number.isFinite(n)) {
-          twelvePrices[sym] = n;
-          setCachedPrice(sym, n);
-        }
-      }
-
-      // Some symbols might not come back in a multi-symbol response; fetch them individually.
-      for (const sym of chunk) {
-        if (twelvePrices[sym] !== undefined) continue;
-        try {
-          const n = await fetchSinglePrice(sym);
-          if (Number.isFinite(n)) twelvePrices[sym] = n;
-        } catch (e) {
-          // ignore single-symbol failures
-          hadAnyError = true;
-          lastErrorMessage = e?.message || lastErrorMessage;
-        }
-      }
-    } catch (e) {
-      // Fall back to per-symbol if the whole batch failed.
-      hadAnyError = true;
-      lastErrorMessage = e?.message || lastErrorMessage;
-      await Promise.all(
-        chunk.map(async (sym) => {
-          try {
-            const n = await fetchSinglePrice(sym);
-            if (Number.isFinite(n)) twelvePrices[sym] = n;
-          } catch (e2) {
-            // ignore
-            hadAnyError = true;
-            lastErrorMessage = e2?.message || lastErrorMessage;
-          }
-        }),
-      );
-    }
-  }
-
-  // Convert back to original symbols
-  const out = {};
-  for (const [orig, twelve] of originalToTwelve.entries()) {
-    const p = twelvePrices[twelve];
-    if (typeof p === "number") {
-      out[String(orig).toUpperCase()] = p;
-    }
-  }
-
-  if (Object.keys(out).length === 0 && hadAnyError) {
-    throw new Error(lastErrorMessage || "Could not fetch any current prices");
-  }
-
-  return out;
-}
Index: frontend/src/api/yahooFinance.js
===================================================================
--- frontend/src/api/yahooFinance.js	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,148 +1,0 @@
-import { LOCAL_TICKER_SYMBOLS } from "../data/tickers";
-
-const tickerSearchCache = new Map();
-const TICKER_CACHE_TTL_MS = 15 * 60 * 1000;
-
-const quoteCache = new Map();
-const QUOTE_CACHE_TTL_MS = 5 * 60 * 1000;
-let quoteBackoffUntilMs = 0;
-
-async function tryFetchJson(url) {
-  // Note: We intentionally do NOT set custom headers here.
-  // Custom headers usually trigger a CORS preflight which Yahoo won't allow.
-  const resp = await fetch(url, { method: "GET" });
-  if (!resp.ok) {
-    throw new Error(`Yahoo Finance HTTP ${resp.status}`);
-  }
-  return resp.json();
-}
-
-function toTickerOption(raw) {
-  const symbol = raw?.symbol ? String(raw.symbol) : "";
-  if (!symbol) return null;
-
-  const name = raw?.shortname || raw?.longname || raw?.name || "";
-  const exchange = raw?.exchDisp || raw?.exchange || "";
-
-  return {
-    symbol,
-    name: name ? String(name) : "",
-    exchange: exchange ? String(exchange) : "",
-    quoteType: raw?.quoteType ? String(raw.quoteType) : "",
-  };
-}
-
-/**
- * Direct Yahoo Finance search (yfinance data source).
- * Falls back to backend proxy if the browser blocks CORS.
- */
-export async function searchTickers(query, limit = 20) {
-  const q = String(query ?? "").trim();
-  if (!q) return [];
-  const safeLimit = Math.max(1, Math.min(Number(limit) || 20, 25));
-
-  const cacheKey = `${q.toUpperCase()}:${safeLimit}`;
-  const cached = tickerSearchCache.get(cacheKey);
-  const now = Date.now();
-  if (cached && cached.expiresAt > now) {
-    return cached.value;
-  }
-
-  // Offline/local ticker search: avoids Yahoo 429s and keeps the dropdown reliable.
-  const upperQ = q.toUpperCase();
-  const symbols = Array.isArray(LOCAL_TICKER_SYMBOLS) ? LOCAL_TICKER_SYMBOLS : [];
-  const out = symbols
-    .map((s) => String(s ?? "").trim())
-    .filter(Boolean)
-    .filter((s) => s.toUpperCase().includes(upperQ))
-    .slice(0, safeLimit)
-    .map((symbol) => ({ symbol, name: "", exchange: "" }));
-
-  tickerSearchCache.set(cacheKey, {
-    value: out,
-    expiresAt: now + TICKER_CACHE_TTL_MS,
-  });
-  return out;
-}
-
-/**
- * Direct Yahoo Finance quotes (yfinance data source).
- * Returns a map { SYMBOL: priceNumber }.
- * Falls back to backend proxy if the browser blocks CORS.
- */
-export async function getQuotesBySymbol(symbols) {
-  const list = Array.isArray(symbols) ? symbols : [];
-  const unique = Array.from(
-    new Set(
-      list
-        .map((s) => String(s ?? "").trim())
-        .filter(Boolean)
-        .map((s) => s.toUpperCase()),
-    ),
-  );
-
-  if (unique.length === 0) return {};
-
-  const parseYahoo = (json) => {
-    const results = Array.isArray(json?.quoteResponse?.result)
-      ? json.quoteResponse.result
-      : [];
-    const map = {};
-    for (const r of results) {
-      const sym = r?.symbol ? String(r.symbol).toUpperCase() : "";
-      const price = r?.regularMarketPrice;
-      if (!sym) continue;
-      if (typeof price !== "number") continue;
-      map[sym] = price;
-    }
-    return map;
-  };
-
-  const now = Date.now();
-  const out = {};
-
-  // Serve cache first.
-  const toFetch = [];
-  for (const sym of unique) {
-    const cached = quoteCache.get(sym);
-    if (cached && cached.expiresAt > now && typeof cached.price === "number") {
-      out[sym] = cached.price;
-    } else {
-      toFetch.push(sym);
-    }
-  }
-
-  // Production builds: browser CORS blocks Yahoo, so quotes are unavailable without a proxy.
-  if (!import.meta.env.DEV) {
-    return out;
-  }
-
-  // Backoff after 429s.
-  if (now < quoteBackoffUntilMs) {
-    return out;
-  }
-
-  if (toFetch.length === 0) {
-    return out;
-  }
-
-  const yahooUrl = `/yahoo/v7/finance/quote?symbols=${encodeURIComponent(
-    toFetch.join(","),
-  )}`;
-
-  try {
-    const json = await tryFetchJson(yahooUrl);
-    const fresh = parseYahoo(json);
-    for (const [sym, price] of Object.entries(fresh)) {
-      quoteCache.set(sym, { price, expiresAt: Date.now() + QUOTE_CACHE_TTL_MS });
-      out[sym] = price;
-    }
-    return out;
-  } catch (e) {
-    const msg = String(e?.message ?? "");
-    if (msg.includes("HTTP 429")) {
-      quoteBackoffUntilMs = Date.now() + 60_000;
-    }
-    return out;
-  }
-}
Index: frontend/src/assets/graph-placeholder.svg
===================================================================
--- frontend/src/assets/graph-placeholder.svg	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,52 +1,0 @@
-<svg width="1200" height="500" viewBox="0 0 1200 500" fill="none" xmlns="http://www.w3.org/2000/svg">
-  <defs>
-    <linearGradient id="bg" x1="0" y1="0" x2="0" y2="1">
-      <stop offset="0" stop-color="#0b1f14"/>
-      <stop offset="1" stop-color="#0f2a1c"/>
-    </linearGradient>
-    <linearGradient id="line" x1="0" y1="0" x2="1" y2="0">
-      <stop offset="0" stop-color="#34d399"/>
-      <stop offset="1" stop-color="#22c55e"/>
-    </linearGradient>
-  </defs>
-
-  <rect x="0" y="0" width="1200" height="500" rx="24" fill="url(#bg)"/>
-
-  <!-- grid -->
-  <g opacity="0.15" stroke="#FFFFFF">
-    <path d="M96 80H1120"/>
-    <path d="M96 160H1120"/>
-    <path d="M96 240H1120"/>
-    <path d="M96 320H1120"/>
-    <path d="M96 400H1120"/>
-
-    <path d="M192 64V432"/>
-    <path d="M320 64V432"/>
-    <path d="M448 64V432"/>
-    <path d="M576 64V432"/>
-    <path d="M704 64V432"/>
-    <path d="M832 64V432"/>
-    <path d="M960 64V432"/>
-    <path d="M1088 64V432"/>
-  </g>
-
-  <!-- axes -->
-  <path d="M96 432H1120" stroke="#FFFFFF" opacity="0.35"/>
-  <path d="M96 64V432" stroke="#FFFFFF" opacity="0.35"/>
-
-  <!-- line -->
-  <path d="M96 360 C 220 310, 280 240, 384 260 C 480 280, 560 180, 640 190 C 740 205, 820 120, 928 140 C 1010 155, 1060 120, 1120 110" stroke="url(#line)" stroke-width="6" fill="none"/>
-
-  <!-- points -->
-  <g fill="#34d399">
-    <circle cx="96" cy="360" r="7"/>
-    <circle cx="384" cy="260" r="7"/>
-    <circle cx="640" cy="190" r="7"/>
-    <circle cx="928" cy="140" r="7"/>
-    <circle cx="1120" cy="110" r="7"/>
-  </g>
-
-  <text x="96" y="44" fill="#E5E7EB" font-family="ui-sans-serif, system-ui" font-size="18" opacity="0.9">
-    Graph placeholder
-  </text>
-</svg>
Index: frontend/src/components/graphs/MultiPercentChangeAreaChart.jsx
===================================================================
--- frontend/src/components/graphs/MultiPercentChangeAreaChart.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,104 +1,0 @@
-import React, { Suspense, lazy, useMemo } from "react";
-
-import { formatBucketLabel } from "../../utils/timeSeries.js";
-
-// Lazy-load ApexCharts (same as PercentChangeAreaChart)
-const ApexChart = lazy(() => import("react-apexcharts"));
-
-/**
- * Multi-series % change area chart.
- *
- * seriesList: [{ name: string, points: [{ts, value}], color: string }]
- * granularity: daily | weekly | monthly | yearly
- */
-export default function MultiPercentChangeAreaChart({
-  seriesList,
-  granularity,
-  height = 320,
-}) {
-  const series = useMemo(() => {
-    return (seriesList ?? []).map((s) => ({
-      name: s.name,
-      data: (s.points ?? []).map((p) => ({ x: p.ts, y: Number(p.value ?? 0) })),
-    }));
-  }, [seriesList]);
-
-  const colors = useMemo(() => (seriesList ?? []).map((s) => s.color), [seriesList]);
-
-  const options = useMemo(() => {
-    return {
-      chart: {
-        type: "area",
-        stacked: false,
-        toolbar: { show: false },
-        zoom: { enabled: false },
-        animations: { enabled: true, easing: "easeinout", speed: 450 },
-        fontFamily: "inherit",
-        foreColor: "rgba(255,255,255,0.72)",
-      },
-      stroke: { curve: "smooth", width: 2 },
-      fill: {
-        type: "gradient",
-        gradient: {
-          shadeIntensity: 0.18,
-          opacityFrom: 0.22,
-          opacityTo: 0.03,
-          stops: [0, 90, 100],
-        },
-      },
-      dataLabels: { enabled: false },
-      grid: {
-        borderColor: "rgba(255,255,255,0.08)",
-        strokeDashArray: 4,
-      },
-      legend: {
-        show: false, // we render our own legend outside for better layout
-      },
-      xaxis: {
-        type: "datetime",
-        labels: {
-          datetimeUTC: false,
-          formatter: (value, timestamp) => formatBucketLabel(timestamp, granularity),
-        },
-        axisBorder: { color: "rgba(255,255,255,0.10)" },
-        axisTicks: { color: "rgba(255,255,255,0.10)" },
-      },
-      yaxis: {
-        labels: {
-          formatter: (v) => `${Math.round(v)}%`,
-        },
-      },
-      tooltip: {
-        theme: "dark",
-        y: {
-          formatter: (v) => `${Number(v ?? 0).toFixed(1)}%`,
-        },
-      },
-      colors,
-      annotations: {
-        yaxis: [
-          {
-            y: 0,
-            borderColor: "rgba(255,255,255,0.25)",
-            strokeDashArray: 4,
-          },
-        ],
-      },
-    };
-  }, [colors, granularity]);
-
-  return (
-    <div className="w-full">
-      <Suspense
-        fallback={
-          <div className="flex items-center justify-center" style={{ height }}>
-            <span className="loading loading-spinner loading-md" />
-          </div>
-        }
-      >
-        <ApexChart options={options} series={series} type="area" height={height} />
-      </Suspense>
-    </div>
-  );
-}
-
Index: frontend/src/components/graphs/PercentChangeAreaChart.jsx
===================================================================
--- frontend/src/components/graphs/PercentChangeAreaChart.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,103 +1,0 @@
-import React, { Suspense, lazy, useMemo } from "react";
-
-import { formatBucketLabel } from "../../utils/timeSeries.js";
-
-// Code-split the charting library (ApexCharts) so it doesn't bloat the initial bundle.
-const ApexChart = lazy(() => import("react-apexcharts"));
-
-export default function PercentChangeAreaChart({
-  points,
-  granularity,
-  height = 260,
-  positiveColor = "#22c55e",
-}) {
-  const series = useMemo(() => {
-    const data = (points ?? []).map((p) => ({ x: p.ts, y: Number(p.value ?? 0) }));
-    return [{ name: "% change", data }];
-  }, [points]);
-
-  const options = useMemo(() => {
-    return {
-      chart: {
-        type: "area",
-        toolbar: { show: false },
-        zoom: { enabled: false },
-        animations: { enabled: true, easing: "easeinout", speed: 450 },
-        fontFamily: "inherit",
-        foreColor: "rgba(255,255,255,0.72)",
-      },
-      stroke: {
-        curve: "smooth",
-        width: 2,
-      },
-      fill: {
-        type: "gradient",
-        gradient: {
-          shadeIntensity: 0.2,
-          opacityFrom: 0.35,
-          opacityTo: 0.05,
-          stops: [0, 90, 100],
-        },
-      },
-      dataLabels: { enabled: false },
-      grid: {
-        borderColor: "rgba(255,255,255,0.08)",
-        strokeDashArray: 4,
-      },
-      xaxis: {
-        type: "datetime",
-        labels: {
-          datetimeUTC: false,
-          formatter: (value, timestamp) => formatBucketLabel(timestamp, granularity),
-        },
-        axisBorder: { color: "rgba(255,255,255,0.10)" },
-        axisTicks: { color: "rgba(255,255,255,0.10)" },
-      },
-      yaxis: {
-        labels: {
-          formatter: (v) => `${Math.round(v)}%`,
-        },
-      },
-      tooltip: {
-        theme: "dark",
-        y: {
-          formatter: (_v, opts) => {
-            const p = points?.[opts.dataPointIndex];
-            const pct = Number(p?.value ?? 0);
-            const base = Number(p?.base ?? 0);
-            return `${pct.toFixed(1)}% (base ${Math.round(base)})`;
-          },
-        },
-      },
-      colors: [positiveColor],
-      annotations: {
-        yaxis: [
-          {
-            y: 0,
-            borderColor: "rgba(255,255,255,0.25)",
-            strokeDashArray: 4,
-          },
-        ],
-      },
-      // Color negative area differently by using a second series-like trick is overkill;
-      // we keep a single color and rely on the 0% line for readability.
-    };
-  }, [granularity, points, positiveColor]);
-
-  return (
-    <div className="w-full">
-      <Suspense
-        fallback={
-          <div className="flex items-center justify-center" style={{ height }}>
-            <span className="loading loading-spinner loading-md" />
-          </div>
-        }
-      >
-        <ApexChart options={options} series={series} type="area" height={height} />
-      </Suspense>
-    </div>
-  );
-}
-
-
-
Index: frontend/src/components/graphs/TimeRangeToggle.jsx
===================================================================
--- frontend/src/components/graphs/TimeRangeToggle.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,30 +1,0 @@
-import React from "react";
-
-export default function TimeRangeToggle({ value, onChange }) {
-  const TIME_RANGES = [
-    { key: "daily", label: "Daily" },
-    { key: "weekly", label: "Weekly" },
-    { key: "monthly", label: "Monthly" },
-    { key: "yearly", label: "Yearly" },
-  ];
-
-  return (
-    <div className="join">
-      {TIME_RANGES.map((r) => (
-        <button
-          key={r.key}
-          type="button"
-          className={
-            "btn btn-xs join-item " +
-            (value === r.key ? "btn-success" : "btn-ghost")
-          }
-          onClick={() => onChange?.(r.key)}
-        >
-          {r.label}
-        </button>
-      ))}
-    </div>
-  );
-}
-
-
Index: frontend/src/data/tickers.js
===================================================================
--- frontend/src/data/tickers.js	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,173 +1,0 @@
-// A pragmatic, hardcoded list to keep ticker selection reliable.
-// Add/remove symbols as needed.
-// Symbols use Yahoo Finance format (e.g., BTC-USD, BRK-B).
-
-export const LOCAL_TICKER_SYMBOLS = [
-  // Crypto (USD pairs)
-  "BTC/USD",
-  "ETH/USD",
-  "SOL/USD",
-  "BNB/USD",
-  "XRP/USD",
-  "ADA/USD",
-  "DOGE/USD",
-  "AVAX/USD",
-  "DOT/USD",
-  "LINK/USD",
-  "MATIC/USD",
-  "LTC/USD",
-
-  // Major ETFs / indices proxies
-  "SPY",
-  "VOO",
-  "IVV",
-  "QQQ",
-  "VTI",
-  "IWM",
-  "DIA",
-  "VEA",
-  "VWO",
-  "EFA",
-  "EEM",
-  "SCHD",
-  "VIG",
-  "VNQ",
-  "TLT",
-  "IEF",
-  "SHY",
-  "HYG",
-  "LQD",
-  "GLD",
-  "SLV",
-  "USO",
-
-  // Mega-cap / large-cap US
-  "AAPL",
-  "MSFT",
-  "NVDA",
-  "AMZN",
-  "GOOGL",
-  "GOOG",
-  "META",
-  "TSLA",
-  "BRK-B",
-  "JPM",
-  "V",
-  "MA",
-  "LLY",
-  "AVGO",
-  "UNH",
-  "XOM",
-  "COST",
-  "HD",
-  "PG",
-  "JNJ",
-  "ORCL",
-  "KO",
-  "PEP",
-  "BAC",
-  "WMT",
-  "ABBV",
-  "MRK",
-  "CVX",
-  "CRM",
-  "ADBE",
-  "NFLX",
-  "AMD",
-  "INTC",
-  "CSCO",
-  "QCOM",
-  "TXN",
-  "IBM",
-  "INTU",
-  "AMAT",
-  "NOW",
-  "ISRG",
-  "GE",
-  "CAT",
-  "BA",
-  "DE",
-  "MMM",
-  "HON",
-  "LOW",
-  "SBUX",
-  "NKE",
-  "MCD",
-  "DIS",
-  "CMCSA",
-  "T",
-  "VZ",
-  "PFE",
-  "TMO",
-  "ABT",
-  "DHR",
-  "LIN",
-  "NEE",
-  "DUK",
-  "SO",
-  "PLD",
-  "AMT",
-  "CCI",
-
-  // Popular growth / tech
-  "SHOP",
-  "SNOW",
-  "PLTR",
-  "UBER",
-  "ABNB",
-  "RBLX",
-  "SQ",
-  "PYPL",
-  "ROKU",
-  "ZM",
-  "DOCU",
-  "CRWD",
-  "PANW",
-  "ZS",
-  "NET",
-  "DDOG",
-  "MDB",
-
-  // Finance
-  "GS",
-  "MS",
-  "C",
-  "WFC",
-  "AXP",
-  "BLK",
-
-  // Energy
-  "COP",
-  "SLB",
-  "OXY",
-
-  // Consumer / retail
-  "TGT",
-  "TJX",
-  "C",
-  "CROX",
-  "LULU",
-  "COST",
-  "WMT",
-  "AMZN",
-
-  // Autos
-  "F",
-  "GM",
-
-  // Semis
-  "ASML",
-  "TSM",
-  "MU",
-  "LRCX",
-  "KLAC",
-
-  // Misc
-  "SPOT",
-  "BABA",
-  "JD",
-  "PDD",
-  "NVO",
-  "TM",
-  "SONY",
-];
Index: frontend/src/index.css
===================================================================
--- frontend/src/index.css	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ frontend/src/index.css	(revision 72053b5fc3ff291757af1cdd64e8fdfcb9e9b17d)
@@ -1,8 +1,68 @@
-@source "../node_modules/@relume_io/relume-ui/dist/**/*.mjs";
-@source "../node_modules/@relume_io/relume-ui/dist/**/*.js";
+:root {
+  font-family: system-ui, Avenir, Helvetica, Arial, sans-serif;
+  line-height: 1.5;
+  font-weight: 400;
 
-@import "tailwindcss";
+  color-scheme: light dark;
+  color: rgba(255, 255, 255, 0.87);
+  background-color: #242424;
 
-@plugin "daisyui" {
-    themes: forest --default;
+  font-synthesis: none;
+  text-rendering: optimizeLegibility;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
 }
+
+a {
+  font-weight: 500;
+  color: #646cff;
+  text-decoration: inherit;
+}
+a:hover {
+  color: #535bf2;
+}
+
+body {
+  margin: 0;
+  display: flex;
+  place-items: center;
+  min-width: 320px;
+  min-height: 100vh;
+}
+
+h1 {
+  font-size: 3.2em;
+  line-height: 1.1;
+}
+
+button {
+  border-radius: 8px;
+  border: 1px solid transparent;
+  padding: 0.6em 1.2em;
+  font-size: 1em;
+  font-weight: 500;
+  font-family: inherit;
+  background-color: #1a1a1a;
+  cursor: pointer;
+  transition: border-color 0.25s;
+}
+button:hover {
+  border-color: #646cff;
+}
+button:focus,
+button:focus-visible {
+  outline: 4px auto -webkit-focus-ring-color;
+}
+
+@media (prefers-color-scheme: light) {
+  :root {
+    color: #213547;
+    background-color: #ffffff;
+  }
+  a:hover {
+    color: #747bff;
+  }
+  button {
+    background-color: #f9f9f9;
+  }
+}
Index: frontend/src/main.jsx
===================================================================
--- frontend/src/main.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ frontend/src/main.jsx	(revision 72053b5fc3ff291757af1cdd64e8fdfcb9e9b17d)
@@ -1,14 +1,10 @@
-import { StrictMode } from "react";
-import { createRoot } from "react-dom/client";
-import { BrowserRouter } from "react-router-dom";
+import { StrictMode } from 'react'
+import { createRoot } from 'react-dom/client'
+import './index.css'
+import App from './App.jsx'
 
-import "./index.css";
-import App from "./App.jsx";
-
-createRoot(document.getElementById("root")).render(
+createRoot(document.getElementById('root')).render(
   <StrictMode>
-    <BrowserRouter>
-      <App />
-    </BrowserRouter>
+    <App />
   </StrictMode>,
-);
+)
Index: frontend/src/pages/Dashboard.jsx
===================================================================
--- frontend/src/pages/Dashboard.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,61 +1,0 @@
-import React, { useMemo } from "react";
-import { Link, useNavigate } from "react-router-dom";
-
-const Dashboard = () => {
-  const navigate = useNavigate();
-
-  const user = useMemo(() => {
-    try {
-      const raw = localStorage.getItem("authUser");
-      return raw ? JSON.parse(raw) : null;
-    } catch {
-      return null;
-    }
-  }, []);
-
-  const onLogout = () => {
-    localStorage.removeItem("authToken");
-    localStorage.removeItem("authUser");
-    navigate("/", { replace: true });
-  };
-
-  return (
-    <div className="min-h-screen p-6">
-      <div className="max-w-3xl mx-auto">
-        <div className="flex items-center justify-between gap-3">
-          <h1 className="text-2xl font-bold">Dashboard</h1>
-          <div className="flex items-center gap-2">
-            <Link className="btn btn-ghost" to="/">
-              Landing
-            </Link>
-            <button className="btn btn-outline" onClick={onLogout}>
-              Logout
-            </button>
-          </div>
-        </div>
-
-        <div className="mt-6 card bg-base-200 border border-base-300">
-          <div className="card-body">
-            <h2 className="card-title">Account</h2>
-            <div className="text-sm leading-7">
-              <div>
-                <span className="opacity-70">Username:</span>{" "}
-                <span className="font-medium">{user?.username ?? "—"}</span>
-              </div>
-              <div>
-                <span className="opacity-70">Email:</span>{" "}
-                <span className="font-medium">{user?.email ?? "—"}</span>
-              </div>
-              <div>
-                <span className="opacity-70">User ID:</span>{" "}
-                <span className="font-medium">{user?.userId ?? "—"}</span>
-              </div>
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-  );
-};
-
-export default Dashboard;
Index: frontend/src/pages/Dashboard/DashboardLayout.jsx
===================================================================
--- frontend/src/pages/Dashboard/DashboardLayout.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,58 +1,0 @@
-import React, { useMemo } from "react";
-import { Outlet, useNavigate } from "react-router-dom";
-
-import { SidebarInset, SidebarProvider } from "@relume_io/relume-ui";
-
-import DashboardSidebar from "./components/DashboardSidebar.jsx";
-import DashboardTopbar from "./components/DashboardTopbar.jsx";
-import { clearAuthSession } from "../../utils/authSession";
-
-const DashboardLayout = () => {
-  const navigate = useNavigate();
-
-  const user = useMemo(() => {
-    try {
-      const raw = localStorage.getItem("authUser");
-      return raw ? JSON.parse(raw) : null;
-    } catch {
-      return null;
-    }
-  }, []);
-
-  const username = user?.username ?? "";
-
-  const onLogout = () => {
-    clearAuthSession();
-    navigate("/", { replace: true });
-  };
-
-  return (
-    <SidebarProvider>
-      {/* App shell background */}
-      <div className="pointer-events-none fixed inset-0 -z-10">
-        <div className="absolute inset-0 bg-base-300/10" />
-        <div className="absolute inset-0 bg-radial-[circle_at_20%_20%] from-green-400/10 via-transparent to-transparent" />
-        <div className="absolute inset-0 bg-radial-[circle_at_80%_30%] from-blue-400/10 via-transparent to-transparent" />
-        <div className="absolute inset-0 bg-linear-to-b from-black/10 via-transparent to-black/25" />
-      </div>
-
-      <DashboardSidebar />
-
-      <SidebarInset className="pt-16 lg:pt-0">
-        <DashboardTopbar username={username} onLogout={onLogout} />
-
-        <div className="h-[calc(100vh-4rem)] overflow-auto">
-          <div className="mx-auto w-full max-w-7xl px-4 py-6 sm:px-6 sm:py-8 lg:px-8 lg:py-10">
-            <div className="rounded-3xl border border-white/10 bg-base-100/60 shadow-[0_0_0_1px_rgba(255,255,255,0.04),0_24px_90px_rgba(0,0,0,0.35)] backdrop-blur-xl">
-              <div className="p-4 sm:p-6 lg:p-8">
-                <Outlet />
-              </div>
-            </div>
-          </div>
-        </div>
-      </SidebarInset>
-    </SidebarProvider>
-  );
-};
-
-export default DashboardLayout;
Index: frontend/src/pages/Dashboard/components/AddCustomCategoryModal.jsx
===================================================================
--- frontend/src/pages/Dashboard/components/AddCustomCategoryModal.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,101 +1,0 @@
-import React, { useEffect, useMemo, useState } from "react";
-import { createPortal } from "react-dom";
-
-export default function AddCustomCategoryModal({ open, onClose, onSubmit }) {
-  const [name, setName] = useState("");
-  const [submitting, setSubmitting] = useState(false);
-  const [error, setError] = useState(null);
-
-  // Reset the form whenever the modal is opened.
-  // NOTE: Hooks must be called in the same order on every render; do not early-return before hooks.
-  useEffect(() => {
-    if (!open) return;
-    setName("");
-    setError(null);
-    setSubmitting(false);
-  }, [open]);
-
-  const canSubmit = useMemo(() => {
-    return name.trim().length > 0 && name.trim().length <= 100 && !submitting;
-  }, [name, submitting]);
-
-  async function handleSubmit(e) {
-    e.preventDefault();
-    if (!canSubmit) return;
-
-    setSubmitting(true);
-    setError(null);
-    try {
-      await onSubmit({ name: name.trim() });
-      onClose?.();
-    } catch (err) {
-      setError(
-        err?.response?.data?.message ||
-          err?.message ||
-          "Failed to create custom category"
-      );
-      setSubmitting(false);
-    }
-  }
-
-  if (!open) return null;
-
-  return createPortal(
-    <div className="fixed inset-0 z-[100] flex items-center justify-center p-4">
-      <button
-        type="button"
-        className="absolute inset-0 bg-black/60"
-        aria-label="Close modal"
-        onClick={onClose}
-      />
-
-      <div className="relative w-full max-w-md rounded-xl bg-neutral-900 p-5 text-white shadow-xl">
-        <div className="flex items-center justify-between gap-4">
-          <h2 className="text-lg font-semibold">Add Custom Category</h2>
-          <button
-            type="button"
-            onClick={onClose}
-            className="rounded-md px-2 py-1 text-sm text-white/80 hover:bg-white/10"
-          >
-            Close
-          </button>
-        </div>
-
-        <form onSubmit={handleSubmit} className="mt-4 space-y-3">
-          <div>
-            <label className="mb-1 block text-sm text-white/80">Name</label>
-            <input
-              value={name}
-              onChange={(e) => setName(e.target.value)}
-              maxLength={100}
-              autoFocus
-              className="w-full rounded-lg border border-white/10 bg-black/40 px-3 py-2 text-sm outline-none focus:border-green-400"
-              placeholder="e.g. My Habit Tracking"
-            />
-            <div className="mt-1 text-xs text-white/50">
-              {name.trim().length}/100
-            </div>
-          </div>
-
-          {error ? (
-            <div className="rounded-lg border border-red-500/30 bg-red-500/10 p-2 text-sm text-red-200">
-              {error}
-            </div>
-          ) : null}
-
-          <button
-            type="submit"
-            disabled={!canSubmit}
-            className="w-full rounded-lg bg-green-400 px-3 py-2 text-sm font-medium text-black disabled:cursor-not-allowed disabled:opacity-60"
-          >
-            {submitting ? "Creating..." : "Create"}
-          </button>
-        </form>
-      </div>
-    </div>,
-    document.body
-  );
-}
-
-
-
Index: frontend/src/pages/Dashboard/components/DashboardSidebar.jsx
===================================================================
--- frontend/src/pages/Dashboard/components/DashboardSidebar.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,209 +1,0 @@
-import React, { useEffect, useMemo, useState } from "react";
-import { NavLink, useLocation } from "react-router-dom";
-
-import {
-  Button,
-  Sidebar,
-  SidebarContent,
-  SidebarHeader,
-  SidebarMenu,
-  SidebarMenuButton,
-  SidebarMenuItem,
-} from "@relume_io/relume-ui";
-
-import {
-  MdTune,
-  MdFitnessCenter,
-  MdMonitorWeight,
-  MdAccountBalanceWallet,
-  MdTrendingUp,
-  MdChecklist,
-  MdAdd,
-  MdBook,
-  MdFlag,
-  MdStar,
-  MdBolt,
-  MdNoteAlt,
-  MdAutoGraph,
-} from "react-icons/md";
-
-import logo from "../../../assets/logo.png";
-
-import AddCustomCategoryModal from "./AddCustomCategoryModal.jsx";
-import {
-  createCustomTrackingCategory,
-  getCustomTrackingCategories,
-} from "../../../api/discipline.js";
-
-const navItems = [
-  { title: "Control Center", to: "/dashboard/control-center", icon: MdTune },
-  { title: "Training", to: "/dashboard/training", icon: MdFitnessCenter },
-  { title: "Weight", to: "/dashboard/weight", icon: MdMonitorWeight },
-  { title: "Finance", to: "/dashboard/finance", icon: MdAccountBalanceWallet },
-  { title: "Investing", to: "/dashboard/investing", icon: MdTrendingUp },
-  { title: "Discipline", to: "/dashboard/discipline", icon: MdChecklist },
-];
-
-const CUSTOM_ICONS = [MdBook, MdFlag, MdStar, MdBolt, MdNoteAlt, MdAutoGraph];
-
-function stableIconForId(id) {
-  const n = Number(id);
-  if (!Number.isFinite(n)) return CUSTOM_ICONS[0];
-  return CUSTOM_ICONS[Math.abs(n) % CUSTOM_ICONS.length];
-}
-
-const DashboardSidebar = () => {
-  const location = useLocation();
-  const [customOpen, setCustomOpen] = useState(false);
-  const [customCategories, setCustomCategories] = useState([]);
-
-  useEffect(() => {
-    let mounted = true;
-    (async () => {
-      try {
-        const res = await getCustomTrackingCategories();
-        const items = res?.items ?? [];
-        if (mounted) setCustomCategories(items);
-      } catch {
-        // If the user isn't authenticated yet or the endpoint fails,
-        // keep the sidebar usable (no custom categories).
-        if (mounted) setCustomCategories([]);
-      }
-    })();
-    return () => {
-      mounted = false;
-    };
-  }, []);
-
-  const customNavItems = useMemo(() => {
-    return (customCategories ?? []).map((c) => {
-      const Icon = stableIconForId(c.customTrackingId);
-      return {
-        title: c.name,
-        to: `/dashboard/custom/${c.customTrackingId}`,
-        icon: Icon,
-      };
-    });
-  }, [customCategories]);
-
-  return (
-    <Sidebar
-      className="py-6 border-r border-white/10 bg-base-100/60 backdrop-blur-xl"
-      closeButtonClassName="fixed top-4 right-4"
-      collapsible="none"
-    >
-      <SidebarHeader className="hidden lg:block">
-        <NavLink to="/" className="flex items-center gap-3 px-2">
-          <img src={logo} alt="Trekr" className="h-10 w-auto rounded-2xl" />
-        </NavLink>
-      </SidebarHeader>
-
-      <SidebarContent className="mt-6">
-        <SidebarMenu>
-          {navItems.map((item) => (
-            <SidebarMenuItem key={item.title}>
-              {(() => {
-                const isActive =
-                  location.pathname === item.to ||
-                  location.pathname.startsWith(`${item.to}/`);
-
-                return (
-                  <SidebarMenuButton
-                    asChild
-                    isActive={isActive}
-                    className={
-                      isActive
-                        ? "bg-green-400! text-black! hover:bg-green-400! active:bg-green-400!"
-                        : "hover:bg-white/10!"
-                    }
-                  >
-                    <NavLink
-                      to={item.to}
-                      className="flex w-full items-center gap-3"
-                    >
-                      <item.icon
-                        className={
-                          isActive
-                            ? "size-6 shrink-0 text-black"
-                            : "size-6 shrink-0"
-                        }
-                      />
-                      <span className={isActive ? "text-black" : ""}>
-                        {item.title}
-                      </span>
-                    </NavLink>
-                  </SidebarMenuButton>
-                );
-              })()}
-            </SidebarMenuItem>
-          ))}
-
-          {customNavItems.length > 0 ? (
-            <div className="my-4 border-t border-white/10" />
-          ) : null}
-
-          {customNavItems.map((item) => (
-            <SidebarMenuItem key={`custom-${item.to}`}>
-              {(() => {
-                const isActive =
-                  location.pathname === item.to ||
-                  location.pathname.startsWith(`${item.to}/`);
-
-                return (
-                  <SidebarMenuButton
-                    asChild
-                    isActive={isActive}
-                    className={
-                      isActive
-                        ? "bg-green-400! text-black! hover:bg-green-400! active:bg-green-400!"
-                        : "hover:bg-white/10!"
-                    }
-                  >
-                    <NavLink
-                      to={item.to}
-                      className="flex w-full items-center gap-3"
-                    >
-                      <item.icon
-                        className={
-                          isActive
-                            ? "size-6 shrink-0 text-black"
-                            : "size-6 shrink-0"
-                        }
-                      />
-                      <span className={isActive ? "text-black" : ""}>
-                        {item.title}
-                      </span>
-                    </NavLink>
-                  </SidebarMenuButton>
-                );
-              })()}
-            </SidebarMenuItem>
-          ))}
-        </SidebarMenu>
-
-        <div className="mt-6 px-2">
-          <Button
-            variant="secondary"
-            className="w-full justify-start gap-2 bg-green-400! text-black! hover:bg-green-500!"
-            type="button"
-            onClick={() => setCustomOpen(true)}
-          >
-            <MdAdd className="size-5" />
-            <span>Add Custom</span>
-          </Button>
-        </div>
-
-        <AddCustomCategoryModal
-          open={customOpen}
-          onClose={() => setCustomOpen(false)}
-          onSubmit={async (payload) => {
-            const created = await createCustomTrackingCategory(payload);
-            setCustomCategories((prev) => [created, ...(prev ?? [])]);
-          }}
-        />
-      </SidebarContent>
-    </Sidebar>
-  );
-};
-
-export default DashboardSidebar;
Index: frontend/src/pages/Dashboard/components/DashboardTopbar.jsx
===================================================================
--- frontend/src/pages/Dashboard/components/DashboardTopbar.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,59 +1,0 @@
-import React from "react";
-import { Link } from "react-router-dom";
-
-import { MdLogout } from "react-icons/md";
-
-import logo from "../../../assets/logo.png";
-
-const DashboardTopbar = ({ username, onLogout }) => {
-  return (
-    <>
-      {/* Desktop */}
-      <div className="sticky top-0 z-30 hidden min-h-16 w-full items-center border-b border-white/10 bg-base-100/70 px-6 backdrop-blur-xl lg:flex lg:px-8">
-        <div className="mx-auto grid size-full grid-cols-1 items-center justify-end justify-items-end gap-4 lg:grid-cols-[1fr_max-content] lg:justify-between lg:justify-items-stretch">
-          <div className="flex items-center gap-3"></div>
-
-          <div className="flex items-center gap-3">
-            <span className="text-sm opacity-80">{username || "—"}</span>
-            <button
-              type="button"
-              className="btn btn-ghost btn-sm hover:bg-white/10 transition"
-              onClick={onLogout}
-              aria-label="Log out"
-              title="Log out"
-            >
-              <MdLogout className="size-5" />
-            </button>
-          </div>
-        </div>
-      </div>
-
-      {/* Mobile */}
-      <div className="fixed left-0 right-0 top-0 z-30 flex min-h-16 w-full items-center justify-between border-b border-white/10 bg-base-100/70 px-4 backdrop-blur-xl sm:px-6 lg:hidden">
-        <div className="flex items-center gap-4">
-          <Link
-            to="/dashboard"
-            className="flex items-center gap-3"
-            aria-label="Trekr dashboard"
-          >
-            <img src={logo} alt="Trekr" className="h-10 w-auto rounded-2xl" />
-          </Link>
-        </div>
-        <div className="flex items-center gap-3">
-          <span className="text-sm opacity-80">{username || "—"}</span>
-          <button
-            type="button"
-            className="btn btn-ghost btn-sm hover:bg-white/10 transition"
-            onClick={onLogout}
-            aria-label="Log out"
-            title="Log out"
-          >
-            <MdLogout className="size-5" />
-          </button>
-        </div>
-      </div>
-    </>
-  );
-};
-
-export default DashboardTopbar;
Index: frontend/src/pages/Dashboard/pages/ControlCenter/ControlCenter.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/ControlCenter/ControlCenter.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,581 +1,0 @@
-import React, { useCallback, useEffect, useMemo, useState } from "react";
-
-import api from "../../../../api/axios";
-import { getFinanceStatus, getIncomes } from "../../../../api/finance";
-import { getDisciplineStatus } from "../../../../api/discipline";
-import { getDailyCompletions } from "../../../../api/dailyCompletion";
-
-import MultiPercentChangeAreaChart from "../../../../components/graphs/MultiPercentChangeAreaChart.jsx";
-import TimeRangeToggle from "../../../../components/graphs/TimeRangeToggle.jsx";
-import { percentChangeSeries, sumByTimeBucket } from "../../../../utils/timeSeries.js";
-
-import TrainingProgressCard from "../Training/components/TrainingProgressCard.jsx";
-import WeightProgressCard from "../Weight/components/WeightProgressCard.jsx";
-import FinanceProgressCard from "../Finance/components/FinanceProgressCard.jsx";
-import DisciplineProgressCard from "../Discipline/components/DisciplineProgressCard.jsx";
-import InvestingProgressCard from "../Investing/components/InvestingProgressCard.jsx";
-
-function Legend({ items, enabledKeys, onToggle, onShowAll, onHideAll }) {
-  if (!items?.length) return null;
-
-  return (
-    <div className="flex flex-col gap-2">
-      <div className="flex flex-wrap justify-end gap-2">
-        <button
-          type="button"
-          className="btn btn-ghost btn-xs hover:bg-white/10"
-          onClick={onShowAll}
-        >
-          Show all
-        </button>
-        <button
-          type="button"
-          className="btn btn-ghost btn-xs hover:bg-white/10"
-          onClick={onHideAll}
-        >
-          Hide all
-        </button>
-      </div>
-
-      <div className="flex flex-wrap gap-x-4 gap-y-2 text-xs">
-        {items.map((it) => {
-          const enabled = enabledKeys?.has(it.key);
-          return (
-            <button
-              key={it.key}
-              type="button"
-              onClick={() => onToggle?.(it.key)}
-              className={
-                "flex items-center gap-2 rounded-full border px-2.5 py-1 transition " +
-                (enabled
-                  ? "border-base-300 bg-base-100 hover:bg-base-100/70"
-                  : "border-transparent bg-base-300/30 opacity-60 hover:opacity-80")
-              }
-              title={enabled ? "Click to hide" : "Click to show"}
-            >
-              <span
-                className="inline-block h-2.5 w-2.5 rounded-full"
-                style={{ backgroundColor: it.color }}
-                aria-hidden
-              />
-              <span className="opacity-90">{it.label}</span>
-            </button>
-          );
-        })}
-      </div>
-    </div>
-  );
-}
-
-export default function ControlCenter() {
-  const [range, setRange] = useState("weekly");
-
-  const [visibleSeries, setVisibleSeries] = useState(() => new Set());
-
-  const [isLoading, setIsLoading] = useState(true);
-  const [error, setError] = useState("");
-
-  const [tracking, setTracking] = useState({
-    weight: false,
-    training: false,
-    finance: false,
-    discipline: false,
-    investing: false,
-  });
-
-  const [trainingSessions, setTrainingSessions] = useState([]);
-  const [weightProfile, setWeightProfile] = useState(null);
-  const [weightIntakes, setWeightIntakes] = useState([]);
-  const [financeIncomes, setFinanceIncomes] = useState([]);
-  const [disciplineCompletions, setDisciplineCompletions] = useState([]);
-  const [investingAssets, setInvestingAssets] = useState([]);
-
-  const fetchAllPaged = useCallback(async (fnPage, pageSize, maxPages = 400) => {
-    let p = 0;
-    let hasMorePages = true;
-    const all = [];
-    while (hasMorePages) {
-      const res = await fnPage(p, pageSize);
-      const items = res.items;
-      all.push(...items);
-      hasMorePages = Boolean(res.hasMore) && items.length > 0;
-      p += 1;
-      if (p > maxPages) break;
-    }
-    return all;
-  }, []);
-
-  useEffect(() => {
-    let cancelled = false;
-    (async () => {
-      try {
-        setIsLoading(true);
-        setError("");
-
-        // Determine tracking flags (cheap endpoints)
-        const [weightRes, trainingRes, investingRes, financeTracking, disciplineTracking] =
-          await Promise.all([
-            api.get("/weight/status"),
-            api.get("/training/status"),
-            api.get("/investing/status"),
-            getFinanceStatus(),
-            getDisciplineStatus(),
-          ]);
-
-        const nextTracking = {
-          weight: Boolean(weightRes?.data?.tracking),
-          training: Boolean(trainingRes?.data?.tracking),
-          investing: Boolean(investingRes?.data?.tracking),
-          finance: Boolean(financeTracking),
-          discipline: Boolean(disciplineTracking),
-        };
-
-        if (cancelled) return;
-        setTracking(nextTracking);
-
-        // Data fetches (only for tracked features)
-        const tasks = [];
-
-        if (nextTracking.training) {
-          tasks.push(
-            fetchAllPaged(
-              async (page, size) => {
-                const resp = await api.get("/training/sessions", { params: { page, size } });
-                const data = resp?.data ?? {};
-                return {
-                  items: Array.isArray(data.sessions) ? data.sessions : [],
-                  hasMore: Boolean(data.hasMore),
-                };
-              },
-              500,
-              200,
-            ).then((all) => setTrainingSessions(all)),
-          );
-        }
-
-        if (nextTracking.weight) {
-          tasks.push(
-            api.get("/weight/profile").then((res) => setWeightProfile(res?.data ?? null)),
-          );
-          tasks.push(
-            fetchAllPaged(
-              async (page, size) => {
-                const resp = await api.get("/weight/intakes", { params: { page, size } });
-                const data = resp?.data ?? {};
-                return {
-                  items: Array.isArray(data.intakes) ? data.intakes : [],
-                  hasMore: Boolean(data.hasMore),
-                };
-              },
-              500,
-              200,
-            ).then((all) => setWeightIntakes(all)),
-          );
-        }
-
-        if (nextTracking.finance) {
-          tasks.push(
-            fetchAllPaged(
-              async (page, size) => {
-                const data = await getIncomes({ page, size });
-                return {
-                  items: Array.isArray(data?.incomes) ? data.incomes : [],
-                  hasMore: Boolean(data?.hasMore),
-                };
-              },
-              500,
-              200,
-            ).then((all) => setFinanceIncomes(all)),
-          );
-        }
-
-        if (nextTracking.discipline) {
-          tasks.push(
-            fetchAllPaged(
-              async (page, size) => {
-                const data = await getDailyCompletions({ page, size });
-                return {
-                  items: Array.isArray(data?.completions) ? data.completions : [],
-                  hasMore: Boolean(data?.hasMore),
-                };
-              },
-              500,
-              400,
-            ).then((all) => setDisciplineCompletions(all)),
-          );
-        }
-
-        if (nextTracking.investing) {
-          tasks.push(
-            fetchAllPaged(
-              async (page, size) => {
-                const resp = await api.get("/investing/assets", { params: { page, size } });
-                const data = resp?.data ?? {};
-                return {
-                  items: Array.isArray(data.assets) ? data.assets : [],
-                  hasMore: Boolean(data.hasMore),
-                };
-              },
-              200,
-              200,
-            ).then((all) => setInvestingAssets(all)),
-          );
-        }
-
-        await Promise.all(tasks);
-      } catch (e) {
-        if (cancelled) return;
-        setError(e?.response?.data?.message || "Failed to load Control Center.");
-      } finally {
-        if (!cancelled) setIsLoading(false);
-      }
-    })();
-
-    return () => {
-      cancelled = true;
-    };
-  }, [fetchAllPaged]);
-
-  const weightGoalCalories = weightProfile?.goalCalories ?? null;
-  const isBulking = useMemo(() => {
-    const current = Number(weightProfile?.weight);
-    const goal = Number(weightProfile?.goalWeight);
-    if (!Number.isFinite(current) || !Number.isFinite(goal)) return null;
-    return goal > current;
-  }, [weightProfile]);
-
-  const combinedLegend = useMemo(
-    () =>
-      [
-        tracking.training ? { key: "training", label: "Training", color: "#22c55e" } : null,
-        tracking.weight ? { key: "weight", label: "Weight", color: "#60a5fa" } : null,
-        tracking.finance ? { key: "finance", label: "Finance", color: "#a855f7" } : null,
-        tracking.discipline ? { key: "discipline", label: "Discipline", color: "#fbbf24" } : null,
-        tracking.investing ? { key: "investing", label: "Investing", color: "#14b8a6" } : null,
-      ].filter(Boolean),
-    [tracking],
-  );
-
-  // Initialize visible series to "all tracked" whenever tracking changes.
-  useEffect(() => {
-    setVisibleSeries(new Set((combinedLegend ?? []).map((x) => x.key)));
-  }, [combinedLegend]);
-
-  const onToggleSeries = useCallback((key) => {
-    setVisibleSeries((prev) => {
-      const next = new Set(prev);
-      if (next.has(key)) next.delete(key);
-      else next.add(key);
-      return next;
-    });
-  }, []);
-
-  const onShowAllSeries = useCallback(() => {
-    setVisibleSeries(new Set((combinedLegend ?? []).map((x) => x.key)));
-  }, [combinedLegend]);
-
-  const onHideAllSeries = useCallback(() => {
-    setVisibleSeries(new Set());
-  }, []);
-
-  const combinedSeriesList = useMemo(() => {
-    const out = [];
-
-    if (tracking.training) {
-      const buckets = sumByTimeBucket(
-        trainingSessions,
-        range,
-        (s) => s.date,
-        (s) => s.calories,
-      );
-      out.push({
-        name: "Training",
-        key: "training",
-        color: "#22c55e",
-        points: percentChangeSeries(buckets),
-      });
-    }
-
-    if (tracking.finance) {
-      const buckets = sumByTimeBucket(
-        financeIncomes,
-        range,
-        (i) => i.date,
-        (i) => i.amount,
-      );
-      out.push({
-        name: "Finance",
-        key: "finance",
-        color: "#a855f7",
-        points: percentChangeSeries(buckets),
-      });
-    }
-
-    if (tracking.discipline) {
-      // average completion per bucket
-      const sums = sumByTimeBucket(
-        disciplineCompletions,
-        range,
-        (c) => c.date,
-        (c) => Number(c?.procent) || 0,
-      );
-      const counts = sumByTimeBucket(
-        disciplineCompletions,
-        range,
-        (c) => c.date,
-        () => 1,
-      );
-      const countByTs = new Map(counts.map((p) => [p.ts, p.value]));
-      const avgPoints = sums.map((p) => {
-        const count = Number(countByTs.get(p.ts) ?? 0);
-        const avg = count > 0 ? Number(p.value ?? 0) / count : 0;
-        return { ts: p.ts, value: avg };
-      });
-
-      out.push({
-        name: "Discipline",
-        key: "discipline",
-        color: "#fbbf24",
-        points: percentChangeSeries(avgPoints),
-      });
-    }
-
-    if (tracking.investing) {
-      const buckets = sumByTimeBucket(
-        investingAssets,
-        range,
-        (a) => a.buyDate,
-        (a) => (Number(a?.quantity) || 0) * (Number(a?.buyPrice) || 0),
-      );
-      out.push({
-        name: "Investing",
-        key: "investing",
-        color: "#14b8a6",
-        points: percentChangeSeries(buckets),
-      });
-    }
-
-    if (tracking.weight) {
-      // replicate WeightProgressCard base series (closeness %) then % change.
-      const totals = sumByTimeBucket(
-        weightIntakes,
-        range,
-        (i) => i.date,
-        (i) => Number(i?.calories) || 0,
-      );
-      const goalTotals = sumByTimeBucket(
-        weightIntakes,
-        range,
-        (i) => i.date,
-        (i) => {
-          const goal = Number(weightGoalCalories);
-          if (!Number.isFinite(goal) || goal <= 0) return 0;
-          const burned = Number(i?.burnedCalories) || 0;
-          const trained = Boolean(i?.trainedThatDay);
-          return trained ? goal + burned : goal;
-        },
-      );
-      const goalByTs = new Map(goalTotals.map((p) => [p.ts, p.value]));
-      const closeness = totals.map((p) => {
-        const adjustedGoal = Number(goalByTs.get(p.ts) ?? 0);
-        const calories = Number(p.value ?? 0);
-        if (!Number.isFinite(adjustedGoal) || adjustedGoal <= 0) return { ts: p.ts, value: 0 };
-        const pct = 100 - (Math.abs(calories - adjustedGoal) / adjustedGoal) * 100;
-        const clamped = Math.min(200, Math.max(0, pct));
-        return { ts: p.ts, value: clamped };
-      });
-      let weightSeries = percentChangeSeries(closeness);
-      if (isBulking === false) {
-        weightSeries = weightSeries.map((p) => ({ ...p, value: -Number(p.value ?? 0) }));
-      }
-      // Weight graph is reversed (matches WeightProgressCard)
-      weightSeries = weightSeries.map((p) => ({ ...p, value: -Number(p.value ?? 0) }));
-
-      out.push({
-        name: "Weight",
-        key: "weight",
-        color: "#60a5fa",
-        points: weightSeries,
-      });
-    }
-
-    return out;
-  }, [
-    disciplineCompletions,
-    financeIncomes,
-    investingAssets,
-    isBulking,
-    range,
-    tracking,
-    trainingSessions,
-    weightGoalCalories,
-    weightIntakes,
-  ]);
-
-  const filteredCombinedSeriesList = useMemo(() => {
-    return (combinedSeriesList ?? []).filter((s) => visibleSeries.has(s.key));
-  }, [combinedSeriesList, visibleSeries]);
-
-  const trackedCount = Object.values(tracking).filter(Boolean).length;
-
-  if (isLoading) {
-    return (
-      <div className="space-y-6">
-        <h1 className="text-2xl font-bold">Control Center</h1>
-        <div className="card bg-base-200 border border-base-300">
-          <div className="card-body">
-            <p className="opacity-80">Loading…</p>
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-  if (error) {
-    return (
-      <div className="space-y-6">
-        <h1 className="text-2xl font-bold">Control Center</h1>
-        <div className="alert alert-error">
-          <span>{error}</span>
-        </div>
-      </div>
-    );
-  }
-
-  if (trackedCount === 0) {
-    return (
-      <div className="space-y-6">
-        <h1 className="text-2xl font-bold">Control Center</h1>
-        <div className="card bg-base-200 border border-base-300">
-          <div className="card-body">
-            <p className="opacity-80">
-              Start tracking at least one area (Weight / Training / Finance / Discipline / Investing) to see your dashboard.
-            </p>
-          </div>
-        </div>
-      </div>
-    );
-  }
-
-  return (
-    <div className="space-y-8">
-      <div className="flex items-start justify-between gap-4 flex-wrap">
-        <div>
-          <h1 className="text-2xl font-bold tracking-tight">Control Center</h1>
-          <p className="mt-1 text-sm opacity-70">
-            Correlate your progress across everything you track.
-          </p>
-        </div>
-        <TimeRangeToggle value={range} onChange={setRange} />
-      </div>
-
-      <div className="card bg-base-200/60 border border-white/10 shadow-[0_0_0_1px_rgba(255,255,255,0.04)] backdrop-blur">
-        <div className="card-body">
-          <div className="flex items-start justify-between gap-4 flex-wrap">
-            <div>
-              <h2 className="card-title">Correlation</h2>
-              <p className="mt-1 text-xs opacity-70">
-                Toggle lines to isolate signals. All metrics are % change per {range} bucket.
-              </p>
-            </div>
-            <Legend
-              items={combinedLegend}
-              enabledKeys={visibleSeries}
-              onToggle={onToggleSeries}
-              onShowAll={onShowAllSeries}
-              onHideAll={onHideAllSeries}
-            />
-          </div>
-
-          <div className="mt-4 w-full overflow-hidden rounded-xl border border-base-300 bg-base-100 p-2">
-            {filteredCombinedSeriesList.length === 0 ? (
-              <div className="flex h-80 items-center justify-center text-sm opacity-70">
-                Toggle at least one line in the legend to display the chart.
-              </div>
-            ) : filteredCombinedSeriesList.every((s) => (s.points?.length ?? 0) < 2) ? (
-              <div className="flex h-80 items-center justify-center text-sm opacity-70">
-                Add more history to see correlations.
-              </div>
-            ) : (
-              <MultiPercentChangeAreaChart
-                seriesList={filteredCombinedSeriesList}
-                granularity={range}
-                height={340}
-              />
-            )}
-          </div>
-
-          <div className="mt-3 text-xs opacity-70">
-            Each line uses the same time bucket toggle, but has its own base metric (calories, income totals, completion average, etc.).
-          </div>
-        </div>
-      </div>
-
-      <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-2 gap-6">
-        {tracking.training ? (
-          <div>
-            <div className="mb-2 flex items-center justify-between">
-              <h3 className="font-semibold">Training</h3>
-              <span className="badge badge-ghost" style={{ borderColor: "#22c55e" }}>
-                % change
-              </span>
-            </div>
-            <TrainingProgressCard sessions={trainingSessions} />
-          </div>
-        ) : null}
-
-        {tracking.weight ? (
-          <div>
-            <div className="mb-2 flex items-center justify-between">
-              <h3 className="font-semibold">Weight</h3>
-              <span className="badge badge-ghost" style={{ borderColor: "#60a5fa" }}>
-                % change
-              </span>
-            </div>
-            <WeightProgressCard
-              intakes={weightIntakes}
-              goalCalories={weightGoalCalories}
-              isBulking={isBulking}
-            />
-          </div>
-        ) : null}
-
-        {tracking.finance ? (
-          <div>
-            <div className="mb-2 flex items-center justify-between">
-              <h3 className="font-semibold">Finance</h3>
-              <span className="badge badge-ghost" style={{ borderColor: "#a855f7" }}>
-                % change
-              </span>
-            </div>
-            <FinanceProgressCard incomes={financeIncomes} />
-          </div>
-        ) : null}
-
-        {tracking.discipline ? (
-          <div>
-            <div className="mb-2 flex items-center justify-between">
-              <h3 className="font-semibold">Discipline</h3>
-              <span className="badge badge-ghost" style={{ borderColor: "#fbbf24" }}>
-                % change
-              </span>
-            </div>
-            <DisciplineProgressCard completions={disciplineCompletions} />
-          </div>
-        ) : null}
-
-        {tracking.investing ? (
-          <div>
-            <div className="mb-2 flex items-center justify-between">
-              <h3 className="font-semibold">Investing</h3>
-              <span className="badge badge-ghost" style={{ borderColor: "#14b8a6" }}>
-                % change
-              </span>
-            </div>
-            <InvestingProgressCard assets={investingAssets} />
-          </div>
-        ) : null}
-      </div>
-    </div>
-  );
-}
Index: frontend/src/pages/Dashboard/pages/CustomCategory/CustomCategoryKanban.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/CustomCategory/CustomCategoryKanban.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,118 +1,0 @@
-import React, { useEffect, useMemo, useState } from "react";
-import { useParams } from "react-router-dom";
-
-import {
-  createCustomCategoryTask,
-  deleteCustomCategoryTask,
-  getCustomCategoryTasks,
-  getCustomTrackingCategories,
-  updateCustomCategoryTask,
-  updateCustomCategoryTaskStatus,
-} from "../../../../api/discipline.js";
-
-import CustomCategoryKanbanBoard from "./components/CustomCategoryKanbanBoard";
-
-export default function CustomCategoryKanban() {
-  const params = useParams();
-  const customTrackingId = Number(params.customTrackingId);
-
-  const [loading, setLoading] = useState(true);
-  const [categoryName, setCategoryName] = useState("Custom Category");
-  const [tasks, setTasks] = useState([]);
-  const [error, setError] = useState(null);
-
-  const lanes = useMemo(() => {
-    const all = tasks ?? [];
-    return {
-      notStarted: all.filter((t) => t.status === "NOT_STARTED"),
-      inProgress: all.filter((t) => t.status === "IN_PROGRESS"),
-      finished: all.filter((t) => t.status === "FINISHED"),
-    };
-  }, [tasks]);
-
-  async function refresh() {
-    setLoading(true);
-    setError(null);
-    try {
-      const [catsRes, tasksRes] = await Promise.all([
-        getCustomTrackingCategories(),
-        getCustomCategoryTasks(customTrackingId),
-      ]);
-      const cats = catsRes?.items ?? [];
-      const found = cats.find((c) => Number(c.customTrackingId) === customTrackingId);
-      setCategoryName(found?.name ?? "Custom Category");
-      setTasks(tasksRes?.tasks ?? []);
-    } catch (e) {
-      setError(e?.response?.data?.message || e?.message || "Failed to load category");
-    } finally {
-      setLoading(false);
-    }
-  }
-
-  useEffect(() => {
-    if (!Number.isFinite(customTrackingId)) {
-      setLoading(false);
-      setError("Invalid category id");
-      return;
-    }
-    refresh();
-    // eslint-disable-next-line react-hooks/exhaustive-deps
-  }, [customTrackingId]);
-
-  async function handleAddTask(payload) {
-    const created = await createCustomCategoryTask(customTrackingId, payload);
-    setTasks((prev) => [created, ...(prev ?? [])]);
-  }
-
-  async function handleEditTask(taskId, payload) {
-    const updated = await updateCustomCategoryTask(customTrackingId, taskId, payload);
-    setTasks((prev) => (prev ?? []).map((t) => (t.taskId === taskId ? updated : t)));
-  }
-
-  async function handleDeleteTask(taskId) {
-    await deleteCustomCategoryTask(customTrackingId, taskId);
-    setTasks((prev) => (prev ?? []).filter((t) => t.taskId !== taskId));
-  }
-
-  async function handleMoveTask(taskId, toStatus) {
-    setTasks((prev) =>
-      (prev ?? []).map((t) => (t.taskId === taskId ? { ...t, status: toStatus } : t))
-    );
-    try {
-      const saved = await updateCustomCategoryTaskStatus(customTrackingId, taskId, toStatus);
-      setTasks((prev) => (prev ?? []).map((t) => (t.taskId === taskId ? saved : t)));
-    } catch {
-      await refresh();
-    }
-  }
-
-  return (
-    <div className="space-y-6">
-      <div className="flex flex-col gap-2 md:flex-row md:items-end md:justify-between">
-        <div>
-          <h1 className="text-2xl font-bold">{categoryName}</h1>
-          <p className="text-sm opacity-80">
-            Drag tickets between columns or use the edit form to update status.
-          </p>
-        </div>
-      </div>
-
-      {error ? (
-        <div className="rounded-xl border border-red-500/30 bg-red-500/10 p-4 text-red-200">
-          {error}
-        </div>
-      ) : null}
-
-      <CustomCategoryKanbanBoard
-        loading={loading}
-        notStarted={lanes.notStarted}
-        inProgress={lanes.inProgress}
-        finished={lanes.finished}
-        onAddTask={handleAddTask}
-        onEditTask={handleEditTask}
-        onDeleteTask={handleDeleteTask}
-        onMoveTask={handleMoveTask}
-      />
-    </div>
-  );
-}
Index: frontend/src/pages/Dashboard/pages/CustomCategory/components/CustomCategoryKanbanBoard.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/CustomCategory/components/CustomCategoryKanbanBoard.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,194 +1,0 @@
-import React, { useMemo, useState } from "react";
-import { DndContext, PointerSensor, closestCenter, useSensor, useSensors } from "@dnd-kit/core";
-import { SortableContext, rectSortingStrategy } from "@dnd-kit/sortable";
-import { MdAdd, MdSort } from "react-icons/md";
-
-import KanbanColumn from "./KanbanColumn";
-import TaskTicketCard from "./TaskTicketCard";
-import TaskFormModal from "./TaskFormModal";
-
-const LANES = [
-  { id: "NOT_STARTED", droppableId: "lane:NOT_STARTED", title: "Not Started" },
-  { id: "IN_PROGRESS", droppableId: "lane:IN_PROGRESS", title: "In Progress" },
-  { id: "FINISHED",    droppableId: "lane:FINISHED",    title: "Finished" },
-];
-
-const PRIORITY_ORDER = { HIGH: 0, MEDIUM: 1, LOW: 2, null: 3, undefined: 3 };
-
-function sortTasks(tasks, sortBy) {
-  if (!tasks || tasks.length === 0) return tasks;
-  const arr = [...tasks];
-  if (sortBy === "dueDate") {
-    arr.sort((a, b) => {
-      if (!a.dueDate && !b.dueDate) return 0;
-      if (!a.dueDate) return 1;
-      if (!b.dueDate) return -1;
-      return new Date(a.dueDate) - new Date(b.dueDate);
-    });
-  } else if (sortBy === "priority") {
-    arr.sort((a, b) => (PRIORITY_ORDER[a.priority] ?? 3) - (PRIORITY_ORDER[b.priority] ?? 3));
-  }
-  return arr;
-}
-
-function statusForDropId(dropId) {
-  return dropId.replace("lane:", "");
-}
-
-export default function CustomCategoryKanbanBoard({
-  loading, notStarted, inProgress, finished,
-  onAddTask, onEditTask, onDeleteTask, onMoveTask,
-}) {
-  const [modalOpen, setModalOpen] = useState(false);
-  const [editingTask, setEditingTask] = useState(null);
-  const [modalLoading, setModalLoading] = useState(false);
-  const [sortBy, setSortBy] = useState("default");
-
-  const sensors = useSensors(useSensor(PointerSensor, { activationConstraint: { distance: 6 } }));
-
-  const tasksByLane = useMemo(() => ({
-    NOT_STARTED: sortTasks(notStarted ?? [], sortBy),
-    IN_PROGRESS: sortTasks(inProgress ?? [], sortBy),
-    FINISHED:    sortTasks(finished ?? [], sortBy),
-  }), [notStarted, inProgress, finished, sortBy]);
-
-  const allTasks = useMemo(() => [
-    ...(notStarted ?? []),
-    ...(inProgress ?? []),
-    ...(finished ?? []),
-  ], [notStarted, inProgress, finished]);
-
-  function findTask(id) {
-    return allTasks.find((t) => String(t.taskId) === String(id));
-  }
-
-  async function handleDragEnd({ active, over }) {
-    if (!over) return;
-    const activeId = String(active.id);
-    const overId = String(over.id);
-    if (activeId === overId) return;
-
-    const task = findTask(activeId);
-    if (!task) return;
-
-    if (overId.startsWith("lane:")) {
-      const toStatus = statusForDropId(overId);
-      if (task.status !== toStatus) await onMoveTask(task.taskId, toStatus);
-      return;
-    }
-
-    const overTask = findTask(overId);
-    if (!overTask) return;
-
-    if (task.status !== overTask.status) {
-      await onMoveTask(task.taskId, overTask.status);
-    }
-  }
-
-  async function handleModalSubmit(payload) {
-    setModalLoading(true);
-    try {
-      if (editingTask) {
-        await onEditTask(editingTask.taskId, payload);
-      } else {
-        await onAddTask(payload);
-      }
-      setModalOpen(false);
-      setEditingTask(null);
-    } finally {
-      setModalLoading(false);
-    }
-  }
-
-  const total = allTasks.length;
-
-  return (
-    <div className="space-y-4">
-      <div className="flex flex-wrap items-center justify-between gap-3 rounded-xl border border-white/10 bg-neutral-900/40 px-4 py-3">
-        <div className="flex flex-wrap items-center gap-3 text-xs text-white/70">
-          <span className="rounded-full bg-slate-500/20 px-2 py-1 text-slate-200">
-            Not started: {(notStarted ?? []).length}
-          </span>
-          <span className="rounded-full bg-amber-500/15 px-2 py-1 text-amber-200">
-            In progress: {(inProgress ?? []).length}
-          </span>
-          <span className="rounded-full bg-emerald-500/15 px-2 py-1 text-emerald-200">
-            Finished: {(finished ?? []).length}
-          </span>
-          <span className="text-white/40">/ {total} total</span>
-        </div>
-
-        <div className="flex items-center gap-2">
-          <div className="flex items-center gap-1.5 rounded-lg border border-white/10 bg-black/30 px-2 py-1.5">
-            <MdSort className="size-4 text-white/50" />
-            <span className="text-xs text-white/50">Sort:</span>
-            <select
-              value={sortBy}
-              onChange={(e) => setSortBy(e.target.value)}
-              className="bg-transparent text-xs text-white/80 outline-none cursor-pointer"
-            >
-              <option value="default">Default</option>
-              <option value="dueDate">Due date</option>
-              <option value="priority">Priority</option>
-            </select>
-          </div>
-
-          <button
-            onClick={() => { setEditingTask(null); setModalOpen(true); }}
-            className="flex items-center gap-1.5 rounded-lg bg-green-400 px-3 py-1.5 text-sm font-medium text-black hover:bg-green-300"
-          >
-            <MdAdd className="size-4" />
-            New ticket
-          </button>
-        </div>
-      </div>
-
-      <DndContext sensors={sensors} collisionDetection={closestCenter} onDragEnd={handleDragEnd}>
-        <div className="grid grid-cols-1 gap-4 md:grid-cols-3">
-          {LANES.map((lane) => {
-            const tasks = tasksByLane[lane.id] ?? [];
-            return (
-              <KanbanColumn
-                key={lane.id}
-                laneId={lane.id}
-                title={lane.title}
-                droppableId={lane.droppableId}
-                count={tasks.length}
-              >
-                <SortableContext
-                  items={tasks.map((t) => String(t.taskId))}
-                  strategy={rectSortingStrategy}
-                >
-                  <div className="space-y-3">
-                    {loading ? (
-                      <div className="text-sm opacity-70">Loading...</div>
-                    ) : tasks.length === 0 ? (
-                      <div className="text-sm opacity-40">No tickets.</div>
-                    ) : (
-                      tasks.map((t) => (
-                        <TaskTicketCard
-                          key={t.taskId}
-                          task={t}
-                          onEdit={() => { setEditingTask(t); setModalOpen(true); }}
-                          onDelete={() => onDeleteTask(t.taskId)}
-                        />
-                      ))
-                    )}
-                  </div>
-                </SortableContext>
-              </KanbanColumn>
-            );
-          })}
-        </div>
-      </DndContext>
-
-      <TaskFormModal
-        isOpen={modalOpen}
-        onClose={() => { setModalOpen(false); setEditingTask(null); }}
-        onSubmit={handleModalSubmit}
-        task={editingTask}
-        isLoading={modalLoading}
-      />
-    </div>
-  );
-}
Index: frontend/src/pages/Dashboard/pages/CustomCategory/components/KanbanColumn.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/CustomCategory/components/KanbanColumn.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,66 +1,0 @@
-import React, { useMemo } from "react";
-import { useDroppable } from "@dnd-kit/core";
-import { MdCheckCircle, MdRadioButtonUnchecked, MdAutorenew } from "react-icons/md";
-
-const VARIANTS = {
-  NOT_STARTED: {
-    Icon: MdRadioButtonUnchecked,
-    header: "from-slate-500/20 via-slate-500/8 to-transparent",
-    badge: "bg-slate-500/15 text-slate-200 border-slate-500/30",
-    ring: "group-hover:ring-slate-500/25",
-    drop: "border-slate-400/60 bg-slate-400/10",
-    border: "border-slate-500/20",
-  },
-  IN_PROGRESS: {
-    Icon: MdAutorenew,
-    header: "from-amber-500/20 via-amber-500/8 to-transparent",
-    badge: "bg-amber-500/15 text-amber-200 border-amber-500/30",
-    ring: "group-hover:ring-amber-500/25",
-    drop: "border-amber-400/60 bg-amber-400/10",
-    border: "border-amber-500/20",
-  },
-  FINISHED: {
-    Icon: MdCheckCircle,
-    header: "from-emerald-500/25 via-emerald-500/10 to-transparent",
-    badge: "bg-emerald-500/15 text-emerald-200 border-emerald-500/30",
-    ring: "group-hover:ring-emerald-500/25",
-    drop: "border-emerald-400/60 bg-emerald-400/10",
-    border: "border-emerald-500/20",
-  },
-};
-
-export default function KanbanColumn({ laneId, title, droppableId, count, children }) {
-  const { setNodeRef, isOver } = useDroppable({ id: droppableId });
-  const v = VARIANTS[laneId] ?? VARIANTS.NOT_STARTED;
-
-  return (
-    <div
-      className={
-        "group overflow-hidden rounded-xl border bg-neutral-900/40 shadow-sm ring-1 ring-white/5 transition " +
-        v.border + " " + v.ring
-      }
-    >
-      <div className={"bg-gradient-to-b " + v.header + " p-4"}>
-        <div className="flex items-center justify-between gap-3">
-          <div className="flex items-center gap-2">
-            <v.Icon className="size-5 text-white/80" />
-            <h2 className="text-sm font-semibold tracking-wide text-white/90">{title}</h2>
-          </div>
-          <span className={"rounded-full border px-2 py-0.5 text-xs font-medium " + v.badge}>
-            {count}
-          </span>
-        </div>
-      </div>
-
-      <div
-        ref={setNodeRef}
-        className={
-          "min-h-[220px] p-4 transition-colors " +
-          (isOver ? v.drop : "border-t border-white/10")
-        }
-      >
-        {children}
-      </div>
-    </div>
-  );
-}
Index: frontend/src/pages/Dashboard/pages/CustomCategory/components/TaskFormModal.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/CustomCategory/components/TaskFormModal.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,244 +1,0 @@
-import React, { useEffect, useState } from "react";
-import { MdClose } from "react-icons/md";
-
-export default function TaskFormModal({
-  isOpen,
-  onClose,
-  onSubmit,
-  task = null,
-  isLoading = false,
-}) {
-  const isEditing = !!task;
-  const [formData, setFormData] = useState({
-    name: "",
-    description: "",
-    dueDate: "",
-    priority: "MEDIUM",
-    status: "NOT_STARTED",
-  });
-  const [errors, setErrors] = useState({});
-
-  // Initialize form with task data when editing
-  useEffect(() => {
-    if (task) {
-      setFormData({
-        name: task.name || "",
-        description: task.description || "",
-        dueDate: task.dueDate || "",
-        priority: task.priority || "MEDIUM",
-        status: task.status || "NOT_STARTED",
-      });
-    } else {
-      setFormData({
-        name: "",
-        description: "",
-        dueDate: "",
-        priority: "MEDIUM",
-        status: "NOT_STARTED",
-      });
-    }
-    setErrors({});
-  }, [task, isOpen]);
-
-  const handleChange = (e) => {
-    const { name, value } = e.target;
-    setFormData((prev) => ({ ...prev, [name]: value }));
-    // Clear error for this field
-    if (errors[name]) {
-      setErrors((prev) => ({ ...prev, [name]: null }));
-    }
-  };
-
-  const validate = () => {
-    const newErrors = {};
-    if (!formData.name.trim()) {
-      newErrors.name = "Task name is required";
-    }
-    if (formData.name.length > 200) {
-      newErrors.name = "Task name must be 200 characters or less";
-    }
-    if (formData.dueDate && new Date(formData.dueDate) < new Date()) {
-      const today = new Date().toISOString().split('T')[0];
-      if (formData.dueDate < today) {
-        newErrors.dueDate = "Due date cannot be in the past";
-      }
-    }
-    return newErrors;
-  };
-
-  const handleSubmit = async (e) => {
-    e.preventDefault();
-    const newErrors = validate();
-    if (Object.keys(newErrors).length > 0) {
-      setErrors(newErrors);
-      return;
-    }
-
-    const payload = {
-      name: formData.name.trim(),
-      description: formData.description.trim() || null,
-      dueDate: formData.dueDate || null,
-      priority: formData.priority,
-    };
-
-    // Only include status if editing
-    if (isEditing) {
-      payload.status = formData.status;
-    }
-
-    try {
-      await onSubmit(payload);
-      onClose();
-    } catch (error) {
-      console.error("Form submission error:", error);
-    }
-  };
-
-  if (!isOpen) return null;
-
-  return (
-    <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50 p-4">
-      <div className="w-full max-w-md rounded-lg border border-white/10 bg-neutral-900 p-6 shadow-xl">
-        {/* Header */}
-        <div className="mb-4 flex items-center justify-between">
-          <h2 className="text-lg font-bold text-white">
-            {isEditing ? "Edit Task" : "Create New Task"}
-          </h2>
-          <button
-            onClick={onClose}
-            className="rounded-md p-1 text-white/60 hover:bg-white/10 hover:text-white"
-            title="Close"
-          >
-            <MdClose className="size-5" />
-          </button>
-        </div>
-
-        {/* Form */}
-        <form onSubmit={handleSubmit} className="space-y-4">
-          {/* Name */}
-          <div>
-            <label className="block text-sm font-medium text-white/80 mb-1">
-              Task Name <span className="text-red-400">*</span>
-            </label>
-            <input
-              type="text"
-              name="name"
-              value={formData.name}
-              onChange={handleChange}
-              placeholder="Enter task name"
-              maxLength={200}
-              className={`w-full rounded-lg border bg-black/40 px-3 py-2 text-sm text-white outline-none transition ${
-                errors.name
-                  ? "border-red-500/50 focus:border-red-400"
-                  : "border-white/10 focus:border-green-400"
-              }`}
-              disabled={isLoading}
-            />
-            {errors.name && (
-              <p className="mt-1 text-xs text-red-400">{errors.name}</p>
-            )}
-            <p className="mt-1 text-xs text-white/40">
-              {formData.name.length}/200
-            </p>
-          </div>
-
-          {/* Description */}
-          <div>
-            <label className="block text-sm font-medium text-white/80 mb-1">
-              Description
-            </label>
-            <textarea
-              name="description"
-              value={formData.description}
-              onChange={handleChange}
-              placeholder="Enter task description (optional)"
-              rows={3}
-              className="w-full rounded-lg border border-white/10 bg-black/40 px-3 py-2 text-sm text-white outline-none transition focus:border-green-400"
-              disabled={isLoading}
-            />
-          </div>
-
-          {/* Priority */}
-          <div>
-            <label className="block text-sm font-medium text-white/80 mb-1">
-              Priority
-            </label>
-            <select
-              name="priority"
-              value={formData.priority}
-              onChange={handleChange}
-              className="w-full rounded-lg border border-white/10 bg-black/40 px-3 py-2 text-sm text-white outline-none transition focus:border-green-400"
-              disabled={isLoading}
-            >
-              <option value="LOW">Low</option>
-              <option value="MEDIUM">Medium</option>
-              <option value="HIGH">High</option>
-            </select>
-          </div>
-
-          {/* Due Date */}
-          <div>
-            <label className="block text-sm font-medium text-white/80 mb-1">
-              Due Date
-            </label>
-            <input
-              type="date"
-              name="dueDate"
-              value={formData.dueDate}
-              onChange={handleChange}
-              className={`w-full rounded-lg border bg-black/40 px-3 py-2 text-sm text-white outline-none transition ${
-                errors.dueDate
-                  ? "border-red-500/50 focus:border-red-400"
-                  : "border-white/10 focus:border-green-400"
-              }`}
-              disabled={isLoading}
-            />
-            {errors.dueDate && (
-              <p className="mt-1 text-xs text-red-400">{errors.dueDate}</p>
-            )}
-          </div>
-
-          {/* Status (only for editing) */}
-          {isEditing && (
-            <div>
-              <label className="block text-sm font-medium text-white/80 mb-1">
-                Status
-              </label>
-              <select
-                name="status"
-                value={formData.status}
-                onChange={handleChange}
-                className="w-full rounded-lg border border-white/10 bg-black/40 px-3 py-2 text-sm text-white outline-none transition focus:border-green-400"
-                disabled={isLoading}
-              >
-                <option value="NOT_STARTED">Not Started</option>
-                <option value="IN_PROGRESS">In Progress</option>
-                <option value="FINISHED">Finished</option>
-              </select>
-            </div>
-          )}
-
-          {/* Buttons */}
-          <div className="flex gap-3 pt-4">
-            <button
-              type="button"
-              onClick={onClose}
-              className="flex-1 rounded-lg border border-white/10 bg-transparent px-4 py-2 text-sm font-medium text-white transition hover:bg-white/5"
-              disabled={isLoading}
-            >
-              Cancel
-            </button>
-            <button
-              type="submit"
-              className="flex-1 rounded-lg bg-green-400 px-4 py-2 text-sm font-medium text-black transition hover:bg-green-300 disabled:opacity-50"
-              disabled={isLoading}
-            >
-              {isLoading ? "Saving..." : isEditing ? "Update Task" : "Create Task"}
-            </button>
-          </div>
-        </form>
-      </div>
-    </div>
-  );
-}
-
Index: frontend/src/pages/Dashboard/pages/CustomCategory/components/TaskTicketCard.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/CustomCategory/components/TaskTicketCard.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,111 +1,0 @@
-import React, { useMemo } from "react";
-import { useSortable } from "@dnd-kit/sortable";
-import { CSS } from "@dnd-kit/utilities";
-import { MdDelete, MdDragIndicator, MdEdit, MdCalendarToday } from "react-icons/md";
-
-const PRIORITY_STYLES = {
-  HIGH:   "bg-red-500/15 text-red-300 border-red-500/30",
-  MEDIUM: "bg-amber-500/15 text-amber-300 border-amber-500/30",
-  LOW:    "bg-sky-500/15 text-sky-300 border-sky-500/30",
-};
-
-const STATUS_DOT = {
-  NOT_STARTED: "bg-slate-400",
-  IN_PROGRESS: "bg-amber-400",
-  FINISHED:    "bg-emerald-400",
-};
-
-function formatDate(dateStr) {
-  if (!dateStr) return null;
-  const d = new Date(dateStr);
-  return d.toLocaleDateString(undefined, { month: "short", day: "numeric", year: "numeric" });
-}
-
-function isOverdue(dateStr) {
-  if (!dateStr) return false;
-  return new Date(dateStr) < new Date(new Date().toDateString());
-}
-
-export default function TaskTicketCard({ task, onEdit, onDelete }) {
-  const id = String(task.taskId);
-  const { attributes, listeners, setNodeRef, transform, transition, isDragging } = useSortable({ id });
-
-  const style = useMemo(() => ({
-    transform: CSS.Transform.toString(transform),
-    transition,
-  }), [transform, transition]);
-
-  const overdue = task.status !== "FINISHED" && isOverdue(task.dueDate);
-
-  return (
-    <div
-      ref={setNodeRef}
-      style={style}
-      className={
-        "group rounded-lg border border-white/10 bg-black/30 p-3 shadow-sm transition hover:border-white/20 hover:bg-black/35 " +
-        (task.status === "FINISHED" ? "opacity-80" : "") +
-        (isDragging ? " opacity-50 scale-95" : "")
-      }
-    >
-      {/* Header row */}
-      <div className="flex items-start justify-between gap-2">
-        <div className="min-w-0 flex-1">
-          <div className="flex items-center gap-2">
-            <span className={"h-2 w-2 shrink-0 rounded-full " + (STATUS_DOT[task.status] ?? "bg-slate-400")} />
-            <span className="text-sm font-medium text-white/90 leading-snug">{task.name}</span>
-          </div>
-
-          {task.description ? (
-            <p className="mt-1.5 text-xs text-white/50 line-clamp-2">{task.description}</p>
-          ) : null}
-        </div>
-
-        <div className="flex shrink-0 items-center gap-1">
-          <button
-            type="button"
-            onClick={onEdit}
-            className="rounded-md p-1 text-white/50 hover:bg-white/10 hover:text-white"
-            title="Edit"
-          >
-            <MdEdit className="size-4" />
-          </button>
-          <button
-            type="button"
-            onClick={onDelete}
-            className="rounded-md p-1 text-white/50 hover:bg-white/10 hover:text-white"
-            title="Delete"
-          >
-            <MdDelete className="size-4" />
-          </button>
-          <button
-            type="button"
-            className="cursor-grab rounded-md p-1 text-white/50 hover:bg-white/10 hover:text-white active:cursor-grabbing"
-            title="Drag"
-            {...attributes}
-            {...listeners}
-          >
-            <MdDragIndicator className="size-4" />
-          </button>
-        </div>
-      </div>
-
-      {/* Footer row: priority + due date */}
-      <div className="mt-2 flex flex-wrap items-center gap-2">
-        <span className="text-xs text-white/30">#{task.taskId}</span>
-
-        {task.priority ? (
-          <span className={"rounded-full border px-2 py-0.5 text-xs font-medium " + (PRIORITY_STYLES[task.priority] ?? PRIORITY_STYLES.LOW)}>
-            {task.priority.charAt(0) + task.priority.slice(1).toLowerCase()}
-          </span>
-        ) : null}
-
-        {task.dueDate ? (
-          <span className={"flex items-center gap-1 text-xs " + (overdue ? "text-red-400" : "text-white/40")}>
-            <MdCalendarToday className="size-3" />
-            {overdue ? "Overdue · " : ""}{formatDate(task.dueDate)}
-          </span>
-        ) : null}
-      </div>
-    </div>
-  );
-}
Index: frontend/src/pages/Dashboard/pages/Discipline/Discipline.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Discipline/Discipline.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,85 +1,0 @@
-import React, { useEffect, useState } from "react";
-import { useNavigate } from "react-router-dom";
-
-import {
-  getDisciplineStatus,
-  startDisciplineTracking,
-} from "../../../../api/discipline";
-
-import DisciplineCenteredCard from "./components/DisciplineCenteredCard.jsx";
-import DisciplineStartCtaCard from "./components/DisciplineStartCtaCard.jsx";
-
-export default function Discipline() {
-  const navigate = useNavigate();
-
-  const [isLoading, setIsLoading] = useState(true);
-  const [isTracking, setIsTracking] = useState(false);
-  const [error, setError] = useState("");
-  const [isSubmitting, setIsSubmitting] = useState(false);
-
-  useEffect(() => {
-    let isMounted = true;
-    const run = async () => {
-      setIsLoading(true);
-      setError("");
-      try {
-        const tracking = await getDisciplineStatus();
-        if (!isMounted) return;
-        setIsTracking(tracking);
-        if (tracking) {
-          navigate("/dashboard/discipline/tracking", { replace: true });
-        }
-      } catch (err) {
-        if (!isMounted) return;
-        const message =
-          err?.response?.data?.message || "Failed to load discipline status";
-        setError(message);
-      } finally {
-        if (isMounted) setIsLoading(false);
-      }
-    };
-
-    run();
-    return () => {
-      isMounted = false;
-    };
-  }, [navigate]);
-
-  const onStart = async () => {
-    setError("");
-    setIsSubmitting(true);
-    try {
-      await startDisciplineTracking();
-      setIsTracking(true);
-      navigate("/dashboard/discipline/tracking", { replace: true });
-    } catch (err) {
-      const message =
-        err?.response?.data?.message ||
-        Object.values(err?.response?.data?.errors ?? {})[0] ||
-        "Failed to start tracking";
-      setError(message);
-    } finally {
-      setIsSubmitting(false);
-    }
-  };
-
-  if (isLoading) {
-    return <DisciplineCenteredCard title="Discipline" message="Loading…" />;
-  }
-
-  if (isTracking) {
-    return <DisciplineCenteredCard title="Discipline" message="Redirecting…" />;
-  }
-
-  return (
-    <div className="min-h-[70vh] w-full flex items-center justify-center">
-      <div className="w-full max-w-3xl">
-        <DisciplineStartCtaCard
-          error={error}
-          onStart={onStart}
-          isSubmitting={isSubmitting}
-        />
-      </div>
-    </div>
-  );
-}
Index: frontend/src/pages/Dashboard/pages/Discipline/DisciplineTracking.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Discipline/DisciplineTracking.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,420 +1,0 @@
-import React, { useCallback, useEffect, useMemo, useState } from "react";
-
-import {
-  createTask,
-  deleteTask,
-  getTasks,
-  updateTask,
-  updateTaskFinished,
-} from "../../../../api/discipline";
-
-import {
-  computeDailyCompletion,
-  getDailyCompletions,
-} from "../../../../api/dailyCompletion";
-
-import TaskList from "./components/TaskList.jsx";
-import DisciplineProgressCard from "./components/DisciplineProgressCard.jsx";
-
-export default function DisciplineTracking() {
-  const pageSize = 50;
-  const completionPageSize = 14;
-  const graphCompletionPageSize = 500;
-
-  const todayIso = useMemo(() => {
-    const d = new Date();
-    return d.toISOString().slice(0, 10);
-  }, []);
-
-  const [tasks, setTasks] = useState([]);
-  const [page, setPage] = useState(0);
-  const [hasMore, setHasMore] = useState(false);
-  const [isLoading, setIsLoading] = useState(true);
-  const [isLoadingMore, setIsLoadingMore] = useState(false);
-  const [error, setError] = useState("");
-  const [completionInfo, setCompletionInfo] = useState(null);
-  const [isComputing, setIsComputing] = useState(false);
-  const [workingTaskIds, setWorkingTaskIds] = useState([]);
-
-  const [dailyCompletions, setDailyCompletions] = useState([]);
-  const [completionPage, setCompletionPage] = useState(0);
-  const [completionHasMore, setCompletionHasMore] = useState(false);
-  const [isLoadingCompletions, setIsLoadingCompletions] = useState(true);
-  const [isLoadingMoreCompletions, setIsLoadingMoreCompletions] = useState(false);
-  const [completionError, setCompletionError] = useState("");
-
-  const workingSet = useMemo(
-    () => new Set(workingTaskIds),
-    [workingTaskIds],
-  );
-
-  const fetchPage = useCallback(async (nextPage) => {
-    const data = await getTasks({ page: nextPage, size: pageSize });
-    return {
-      nextTasks: Array.isArray(data?.tasks) ? data.tasks : [],
-      nextHasMore: Boolean(data?.hasMore),
-    };
-  }, []);
-
-  const fetchCompletionsPage = useCallback(async (nextPage) => {
-    const data = await getDailyCompletions({
-      page: nextPage,
-      size: completionPageSize,
-    });
-    return {
-      nextCompletions: Array.isArray(data?.completions) ? data.completions : [],
-      nextHasMore: Boolean(data?.hasMore),
-    };
-  }, []);
-
-  const [graphCompletions, setGraphCompletions] = useState([]);
-
-  const fetchAllCompletionsForGraph = useCallback(async () => {
-    let p = 0;
-    let hasMorePages = true;
-    const all = [];
-
-    while (hasMorePages) {
-      const data = await getDailyCompletions({ page: p, size: graphCompletionPageSize });
-      const chunk = Array.isArray(data?.completions) ? data.completions : [];
-      all.push(...chunk);
-      hasMorePages = Boolean(data?.hasMore) && chunk.length > 0;
-      p += 1;
-      if (p > 400) break;
-    }
-
-    return all;
-  }, []);
-
-  useEffect(() => {
-    let cancelled = false;
-    (async () => {
-      try {
-        setIsLoading(true);
-        setError("");
-        const { nextTasks, nextHasMore } = await fetchPage(0);
-        if (cancelled) return;
-        setTasks(nextTasks);
-        setHasMore(nextHasMore);
-        setPage(0);
-      } catch (e) {
-        if (cancelled) return;
-        setError(e?.response?.data?.message || "Failed to load tasks.");
-      } finally {
-        if (!cancelled) setIsLoading(false);
-      }
-    })();
-    return () => {
-      cancelled = true;
-    };
-  }, [fetchPage]);
-
-  useEffect(() => {
-    let cancelled = false;
-    (async () => {
-      try {
-        setIsLoadingCompletions(true);
-        setCompletionError("");
-        const [{ nextCompletions, nextHasMore }, allForGraph] = await Promise.all([
-          fetchCompletionsPage(0),
-          fetchAllCompletionsForGraph(),
-        ]);
-        if (cancelled) return;
-        setDailyCompletions(nextCompletions);
-        setGraphCompletions(allForGraph);
-        setCompletionHasMore(nextHasMore);
-        setCompletionPage(0);
-      } catch (e) {
-        if (cancelled) return;
-        setCompletionError(
-          e?.response?.data?.message ||
-            "Failed to load daily completion history.",
-        );
-      } finally {
-        if (!cancelled) setIsLoadingCompletions(false);
-      }
-    })();
-
-    return () => {
-      cancelled = true;
-    };
-  }, [fetchAllCompletionsForGraph, fetchCompletionsPage]);
-
-  const onLoadMoreCompletions = async () => {
-    if (isLoadingMoreCompletions || !completionHasMore) return;
-    const nextPage = completionPage + 1;
-    try {
-      setIsLoadingMoreCompletions(true);
-      setCompletionError("");
-      const { nextCompletions, nextHasMore } =
-        await fetchCompletionsPage(nextPage);
-      setDailyCompletions((prev) => [...prev, ...nextCompletions]);
-      setCompletionHasMore(nextHasMore);
-      setCompletionPage(nextPage);
-    } catch (e) {
-      setCompletionError(
-        e?.response?.data?.message || "Failed to load more completions.",
-      );
-    } finally {
-      setIsLoadingMoreCompletions(false);
-    }
-  };
-
-  const onLoadMore = async () => {
-    if (isLoadingMore || !hasMore) return;
-    const nextPage = page + 1;
-    try {
-      setIsLoadingMore(true);
-      setError("");
-      const { nextTasks, nextHasMore } = await fetchPage(nextPage);
-      setTasks((prev) => [...prev, ...nextTasks]);
-      setHasMore(nextHasMore);
-      setPage(nextPage);
-    } catch (e) {
-      setError(e?.response?.data?.message || "Failed to load more tasks.");
-    } finally {
-      setIsLoadingMore(false);
-    }
-  };
-
-  const withWorking = async (taskId, fn) => {
-    setWorkingTaskIds((prev) => [...prev, taskId]);
-    try {
-      await fn();
-    } finally {
-      setWorkingTaskIds((prev) => prev.filter((id) => id !== taskId));
-    }
-  };
-
-  const onAdd = async (name) => {
-    setError("");
-    try {
-      const created = await createTask({ name });
-      setTasks((prev) => [created, ...prev]);
-    } catch (e) {
-      setError(e?.response?.data?.message || "Failed to create task.");
-    }
-  };
-
-  const onDelete = async (taskId) => {
-    setError("");
-    await withWorking(taskId, async () => {
-      try {
-        await deleteTask(taskId);
-        setTasks((prev) => prev.filter((t) => t.taskId !== taskId));
-      } catch (e) {
-        setError(e?.response?.data?.message || "Failed to delete task.");
-      }
-    });
-  };
-
-  const onToggleFinished = async (taskId, isFinished) => {
-    if (workingSet.has(taskId)) return;
-    setError("");
-
-    // optimistic update
-    setTasks((prev) =>
-      prev.map((t) => (t.taskId === taskId ? { ...t, isFinished } : t)),
-    );
-
-    await withWorking(taskId, async () => {
-      try {
-        const updated = await updateTaskFinished(taskId, isFinished);
-        setTasks((prev) =>
-          prev.map((t) => (t.taskId === taskId ? updated : t)),
-        );
-      } catch (e) {
-        // revert
-        setTasks((prev) =>
-          prev.map((t) =>
-            t.taskId === taskId ? { ...t, isFinished: !isFinished } : t,
-          ),
-        );
-        setError(e?.response?.data?.message || "Failed to update task.");
-      }
-    });
-  };
-
-  const onRename = async (taskId, name) => {
-    setError("");
-    await withWorking(taskId, async () => {
-      try {
-        const updated = await updateTask(taskId, { name });
-        setTasks((prev) =>
-          prev.map((t) => (t.taskId === taskId ? updated : t)),
-        );
-      } catch (e) {
-        setError(e?.response?.data?.message || "Failed to update task.");
-      }
-    });
-  };
-
-  const onComputeToday = async () => {
-    setError("");
-    setCompletionInfo(null);
-    setIsComputing(true);
-    try {
-      const result = await computeDailyCompletion();
-      setCompletionInfo(result?.completion ?? null);
-
-      // refresh daily completion history
-      try {
-        const { nextCompletions, nextHasMore } = await fetchCompletionsPage(0);
-        setDailyCompletions(nextCompletions);
-        setCompletionHasMore(nextHasMore);
-        setCompletionPage(0);
-
-        // also refresh graph dataset
-        try {
-          const allForGraph = await getDailyCompletions({ page: 0, size: 1000 });
-          setGraphCompletions(
-            Array.isArray(allForGraph?.completions) ? allForGraph.completions : [],
-          );
-        } catch {
-          // ignore
-        }
-      } catch {
-        // ignore
-      }
-
-      // refresh tasks as backend will reset finished -> false
-      const { nextTasks, nextHasMore } = await fetchPage(0);
-      setTasks(nextTasks);
-      setHasMore(nextHasMore);
-      setPage(0);
-    } catch (e) {
-      setError(
-        e?.response?.data?.message || "Failed to compute daily completion.",
-      );
-    } finally {
-      setIsComputing(false);
-    }
-  };
-
-  const isTodayClosed = useMemo(() => {
-    console.log('todayIso:', todayIso);
-    return (dailyCompletions ?? []).some((dc) => dc?.date === todayIso);
-  }, [dailyCompletions, todayIso]);
-
-  return (
-    <div className="space-y-6">
-      <div className="flex items-center justify-between gap-3">
-        <h1 className="text-2xl font-bold">Discipline</h1>
-        <button
-          className="btn btn-primary btn-sm"
-          onClick={onComputeToday}
-          disabled={isComputing || isTodayClosed}
-          title="Compute today's completion and reset tasks for the next day"
-        >
-          {isTodayClosed ? "Day closed" : isComputing ? "Computing…" : "Close day"}
-        </button>
-      </div>
-
-      {completionInfo ? (
-        <div className="card bg-base-200 border border-base-300">
-          <div className="card-body">
-            <h2 className="card-title">Daily completion saved</h2>
-            <p className="text-sm opacity-80">
-              Date: {completionInfo.date} — Completion: {completionInfo.procent}%
-            </p>
-          </div>
-        </div>
-      ) : null}
-
-      {isLoading ? (
-        <div className="card bg-base-200 border border-base-300">
-          <div className="card-body">
-            <p className="opacity-80">Loading tasks…</p>
-          </div>
-        </div>
-      ) : (
-        <>
-          <TaskList
-            tasks={tasks}
-            onAdd={onAdd}
-            onDelete={onDelete}
-            onToggleFinished={onToggleFinished}
-            onRename={onRename}
-            isWorkingTaskIds={workingTaskIds}
-          />
-
-          {error ? <p className="text-error text-sm">{error}</p> : null}
-
-          {hasMore ? (
-            <button
-              className="btn btn-outline btn-sm"
-              disabled={isLoadingMore}
-              onClick={onLoadMore}
-            >
-              {isLoadingMore ? "Loading…" : "Load more"}
-            </button>
-          ) : null}
-
-          <DisciplineProgressCard completions={graphCompletions} />
-
-          <div className="card bg-base-200 border border-base-300">
-            <div className="card-body">
-              <h2 className="card-title">Daily completion</h2>
-              <p className="text-sm opacity-80">
-                Previous days completion percentages.
-              </p>
-
-              {completionError ? (
-                <p className="text-error text-sm mt-2">{completionError}</p>
-              ) : null}
-
-              <div className="mt-4 overflow-x-auto">
-                <table className="table table-zebra">
-                  <thead>
-                    <tr>
-                      <th>Date</th>
-                      <th className="text-right">Completion</th>
-                    </tr>
-                  </thead>
-                  <tbody>
-                    {isLoadingCompletions ? (
-                      <tr>
-                        <td colSpan={2} className="opacity-70">
-                          Loading…
-                        </td>
-                      </tr>
-                    ) : (dailyCompletions ?? []).length === 0 ? (
-                      <tr>
-                        <td colSpan={2} className="opacity-70">
-                          No daily completion records yet. Click “Close day” to
-                          save today.
-                        </td>
-                      </tr>
-                    ) : (
-                      (dailyCompletions ?? []).map((dc) => (
-                        <tr key={dc.dailyCompletionId}>
-                          <td>{dc.date ?? "—"}</td>
-                          <td className="text-right tabular-nums">
-                            {dc.procent ?? 0}%
-                          </td>
-                        </tr>
-                      ))
-                    )}
-                  </tbody>
-                </table>
-              </div>
-
-              <div className="mt-4">
-                {completionHasMore ? (
-                  <button
-                    className="btn btn-outline btn-sm"
-                    disabled={isLoadingMoreCompletions}
-                    onClick={onLoadMoreCompletions}
-                  >
-                    {isLoadingMoreCompletions ? "Loading…" : "Load more"}
-                  </button>
-                ) : null}
-              </div>
-            </div>
-          </div>
-        </>
-      )}
-    </div>
-  );
-}
-
Index: frontend/src/pages/Dashboard/pages/Discipline/components/DisciplineCenteredCard.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Discipline/components/DisciplineCenteredCard.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,17 +1,0 @@
-import React from "react";
-
-export default function DisciplineCenteredCard({ title, message }) {
-  return (
-    <div className="min-h-[70vh] w-full flex items-center justify-center">
-      <div className="w-full max-w-3xl">
-        <div className="card bg-base-200 border border-base-300">
-          <div className="card-body">
-            <h1 className="card-title text-2xl">{title}</h1>
-            <p className="opacity-80">{message}</p>
-          </div>
-        </div>
-      </div>
-    </div>
-  );
-}
-
Index: frontend/src/pages/Dashboard/pages/Discipline/components/DisciplineProgressCard.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Discipline/components/DisciplineProgressCard.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,78 +1,0 @@
-import React, { useMemo, useState } from "react";
-
-import TimeRangeToggle from "../../../../../components/graphs/TimeRangeToggle.jsx";
-import PercentChangeAreaChart from "../../../../../components/graphs/PercentChangeAreaChart.jsx";
-import { percentChangeSeries, sumByTimeBucket } from "../../../../../utils/timeSeries.js";
-
-export default function DisciplineProgressCard({ completions }) {
-  const [range, setRange] = useState("weekly");
-
-  const points = useMemo(() => {
-    // Bucket daily completion percentages by time range.
-    // For weekly/monthly/yearly we compute the *average* completion % in that period.
-    const sums = sumByTimeBucket(
-      completions,
-      range,
-      (c) => c.date,
-      (c) => Number(c?.procent) || 0,
-    );
-
-    const counts = sumByTimeBucket(
-      completions,
-      range,
-      (c) => c.date,
-      () => 1,
-    );
-
-    const countByTs = new Map(counts.map((p) => [p.ts, p.value]));
-
-    const avgPoints = sums.map((p) => {
-      const count = Number(countByTs.get(p.ts) ?? 0);
-      const avg = count > 0 ? Number(p.value ?? 0) / count : 0;
-      return { ts: p.ts, value: avg };
-    });
-
-    return percentChangeSeries(avgPoints);
-  }, [completions, range]);
-
-  const latest = points?.length ? points[points.length - 1] : null;
-  const latestPct = latest ? Number(latest.value ?? 0) : 0;
-  const latestBase = latest ? Number(latest.base ?? 0) : 0;
-
-  return (
-    <div className="card bg-base-200 border border-base-300">
-      <div className="card-body">
-        <div className="flex items-center justify-between gap-4">
-          <h2 className="card-title">Progress</h2>
-          <div className="flex items-center gap-3">
-            <span className="badge badge-ghost">
-              {latestPct >= 0 ? "+" : ""}
-              {latestPct.toFixed(1)}% (last)
-            </span>
-            <TimeRangeToggle value={range} onChange={setRange} />
-          </div>
-        </div>
-
-        <div className="mt-4 w-full overflow-hidden rounded-xl border border-base-300 bg-base-100 p-2">
-          {points.length < 2 ? (
-            <div className="flex h-65 items-center justify-center text-sm opacity-70">
-              Add at least 2 daily completion records to see % change.
-            </div>
-          ) : (
-            <PercentChangeAreaChart
-              points={points}
-              granularity={range}
-              height={260}
-              positiveColor="#fbbf24"
-            />
-          )}
-        </div>
-
-        <div className="mt-2 text-xs opacity-70">
-          Showing percentage change in average completion % per {range} bucket. Latest bucket avg: {Math.round(latestBase)}%.
-        </div>
-      </div>
-    </div>
-  );
-}
-
Index: frontend/src/pages/Dashboard/pages/Discipline/components/DisciplineStartCtaCard.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Discipline/components/DisciplineStartCtaCard.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,26 +1,0 @@
-import React from "react";
-
-export default function DisciplineStartCtaCard({ error, onStart, isSubmitting }) {
-  return (
-    <div className="card bg-base-200 border border-base-300">
-      <div className="card-body">
-        <h1 className="card-title text-2xl">Discipline</h1>
-        <p className="opacity-80">
-          Start tracking your daily discipline tasks. You can add, edit, and mark
-          tasks as completed.
-        </p>
-        {error ? <p className="text-error text-sm mt-2">{error}</p> : null}
-        <div className="mt-4">
-          <button
-            className="btn btn-primary"
-            disabled={isSubmitting}
-            onClick={onStart}
-          >
-            {isSubmitting ? "Starting…" : "Start tracking"}
-          </button>
-        </div>
-      </div>
-    </div>
-  );
-}
-
Index: frontend/src/pages/Dashboard/pages/Discipline/components/TaskList.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Discipline/components/TaskList.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,171 +1,0 @@
-import React, { useMemo, useState } from "react";
-
-function normalizeName(value) {
-  return String(value ?? "").trim();
-}
-
-export default function TaskList({
-  tasks,
-  onAdd,
-  onDelete,
-  onToggleFinished,
-  onRename,
-  isWorkingTaskIds,
-}) {
-  const [newTaskName, setNewTaskName] = useState("");
-  const [editTaskId, setEditTaskId] = useState(null);
-  const [editName, setEditName] = useState("");
-
-  const workingSet = useMemo(
-    () => new Set(isWorkingTaskIds ?? []),
-    [isWorkingTaskIds],
-  );
-
-  const beginEdit = (t) => {
-    setEditTaskId(t.taskId);
-    setEditName(t.name ?? "");
-  };
-
-  const cancelEdit = () => {
-    setEditTaskId(null);
-    setEditName("");
-  };
-
-  const submitRename = async () => {
-    const name = normalizeName(editName);
-    if (!name) return;
-    await onRename(editTaskId, name);
-    cancelEdit();
-  };
-
-  return (
-    <div className="card bg-base-200 border border-base-300">
-      <div className="card-body">
-        <div className="flex items-center justify-between gap-3 flex-wrap">
-          <div>
-            <h2 className="card-title">Daily tasks</h2>
-            <p className="text-sm opacity-80">
-              Check a task to mark it finished. Changes are saved immediately.
-            </p>
-          </div>
-        </div>
-
-        <form
-          className="mt-4 flex gap-2"
-          onSubmit={(e) => {
-            e.preventDefault();
-            const name = normalizeName(newTaskName);
-            if (!name) return;
-            onAdd(name);
-            setNewTaskName("");
-          }}
-        >
-          <input
-            className="input input-bordered w-full"
-            placeholder="New task name…"
-            value={newTaskName}
-            onChange={(e) => setNewTaskName(e.target.value)}
-            maxLength={200}
-          />
-          <button className="btn btn-primary" type="submit">
-            Add
-          </button>
-        </form>
-
-        <div className="mt-4 space-y-2">
-          {(tasks ?? []).length === 0 ? (
-            <p className="opacity-70">No tasks yet.</p>
-          ) : (
-            (tasks ?? []).map((t) => {
-              const isWorking = workingSet.has(t.taskId);
-              const isEditing = editTaskId === t.taskId;
-
-              return (
-                <div
-                  key={t.taskId}
-                  className="flex items-center justify-between gap-3 rounded-lg border border-base-300 bg-base-100 px-3 py-2"
-                >
-                  <div className="flex items-center gap-3 w-full">
-                    <input
-                      type="checkbox"
-                      className="checkbox checkbox-success"
-                      checked={Boolean(t.isFinished)}
-                      disabled={isWorking}
-                      onChange={(e) =>
-                        onToggleFinished(t.taskId, e.target.checked)
-                      }
-                    />
-
-                    {isEditing ? (
-                      <input
-                        className="input input-bordered input-sm w-full"
-                        value={editName}
-                        onChange={(e) => setEditName(e.target.value)}
-                        maxLength={200}
-                      />
-                    ) : (
-                      <div className="w-full">
-                        <div
-                          className={
-                            t.isFinished
-                              ? "line-through opacity-70"
-                              : "font-medium"
-                          }
-                        >
-                          {t.name}
-                        </div>
-                      </div>
-                    )}
-                  </div>
-
-                  <div className="flex items-center gap-2">
-                    {isEditing ? (
-                      <>
-                        <button
-                          className="btn btn-primary btn-xs"
-                          type="button"
-                          disabled={isWorking}
-                          onClick={submitRename}
-                        >
-                          Save
-                        </button>
-                        <button
-                          className="btn btn-ghost btn-xs"
-                          type="button"
-                          disabled={isWorking}
-                          onClick={cancelEdit}
-                        >
-                          Cancel
-                        </button>
-                      </>
-                    ) : (
-                      <>
-                        <button
-                          className="btn btn-outline btn-xs"
-                          type="button"
-                          disabled={isWorking}
-                          onClick={() => beginEdit(t)}
-                        >
-                          Edit
-                        </button>
-                        <button
-                          className="btn btn-error btn-xs"
-                          type="button"
-                          disabled={isWorking}
-                          onClick={() => onDelete(t.taskId)}
-                        >
-                          Delete
-                        </button>
-                      </>
-                    )}
-                  </div>
-                </div>
-              );
-            })
-          )}
-        </div>
-      </div>
-    </div>
-  );
-}
-
Index: frontend/src/pages/Dashboard/pages/Finance/Finance.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Finance/Finance.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,125 +1,0 @@
-import React, { useEffect, useMemo, useState } from "react";
-import { useNavigate } from "react-router-dom";
-
-import { getFinanceStatus, startFinanceTracking } from "../../../../api/finance";
-
-import FinanceCenteredCard from "./components/FinanceCenteredCard.jsx";
-import FinanceStartCtaCard from "./components/FinanceStartCtaCard.jsx";
-import FinanceStartForm from "./components/FinanceStartForm.jsx";
-
-export default function Finance() {
-  const navigate = useNavigate();
-
-  const [isLoading, setIsLoading] = useState(true);
-  const [isTracking, setIsTracking] = useState(false);
-  const [step, setStep] = useState("cta"); // cta | form
-  const [error, setError] = useState("");
-  const [isSubmitting, setIsSubmitting] = useState(false);
-
-  const initialForm = useMemo(
-    () => ({
-      spendingBudget: "50",
-      savingBudget: "20",
-      investingBudget: "20",
-      donationBudget: "5",
-      credit: "5",
-    }),
-    [],
-  );
-
-  const [form, setForm] = useState(initialForm);
-
-  useEffect(() => {
-    let isMounted = true;
-    const run = async () => {
-      setIsLoading(true);
-      setError("");
-      try {
-        const tracking = await getFinanceStatus();
-        if (!isMounted) return;
-        setIsTracking(tracking);
-        if (tracking) {
-          navigate("/dashboard/finance/tracking", { replace: true });
-        }
-      } catch (err) {
-        if (!isMounted) return;
-        const message =
-          err?.response?.data?.message || "Failed to load finance status";
-        setError(message);
-      } finally {
-        if (isMounted) setIsLoading(false);
-      }
-    };
-    run();
-    return () => {
-      isMounted = false;
-    };
-  }, [navigate]);
-
-  const onStart = () => {
-    setError("");
-    setStep("form");
-  };
-
-  const onCancel = () => {
-    setStep("cta");
-    setForm(initialForm);
-    setError("");
-  };
-
-  const onSubmit = async (e) => {
-    e.preventDefault();
-    setError("");
-    setIsSubmitting(true);
-    try {
-      await startFinanceTracking({
-        spendingBudget:
-          form.spendingBudget === "" ? null : Number(form.spendingBudget),
-        savingBudget: form.savingBudget === "" ? null : Number(form.savingBudget),
-        investingBudget:
-          form.investingBudget === "" ? null : Number(form.investingBudget),
-        donationBudget:
-          form.donationBudget === "" ? null : Number(form.donationBudget),
-        credit: form.credit === "" ? null : Number(form.credit),
-      });
-
-      setIsTracking(true);
-      navigate("/dashboard/finance/tracking", { replace: true });
-    } catch (err) {
-      const message =
-        err?.response?.data?.message ||
-        Object.values(err?.response?.data?.errors ?? {})[0] ||
-        "Failed to start tracking";
-      setError(message);
-    } finally {
-      setIsSubmitting(false);
-    }
-  };
-
-  if (isLoading) {
-    return <FinanceCenteredCard title="Finance" message="Loading…" />;
-  }
-
-  if (isTracking) {
-    return <FinanceCenteredCard title="Finance" message="Redirecting…" />;
-  }
-
-  return (
-    <div className="min-h-[70vh] w-full flex items-center justify-center">
-      <div className="w-full max-w-3xl">
-        {step === "cta" ? (
-          <FinanceStartCtaCard error={error} onStart={onStart} />
-        ) : (
-          <FinanceStartForm
-            form={form}
-            setForm={setForm}
-            onSubmit={onSubmit}
-            onCancel={onCancel}
-            error={error}
-            isSubmitting={isSubmitting}
-          />
-        )}
-      </div>
-    </div>
-  );
-}
Index: frontend/src/pages/Dashboard/pages/Finance/FinanceTracking.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Finance/FinanceTracking.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,246 +1,0 @@
-import React, { useCallback, useEffect, useMemo, useState } from "react";
-import { useNavigate } from "react-router-dom";
-
-import {
-  getFinanceProfile,
-  getIncomes,
-  startFinanceTracking,
-} from "../../../../api/finance";
-
-import FinanceSummaryCard from "./components/FinanceSummaryCard.jsx";
-import FinanceIncomesTable from "./components/FinanceIncomesTable.jsx";
-import FinanceStartForm from "./components/FinanceStartForm.jsx";
-import FinanceProgressCard from "./components/FinanceProgressCard.jsx";
-
-function toNumber(value) {
-  const n = Number(value);
-  return Number.isFinite(n) ? n : 0;
-}
-
-export default function FinanceTracking() {
-  const navigate = useNavigate();
-  const pageSize = 5;
-  const graphPageSize = 500;
-
-  const [profile, setProfile] = useState(null);
-  const [incomes, setIncomes] = useState([]);
-  const [page, setPage] = useState(0);
-  const [hasMore, setHasMore] = useState(false);
-
-  const [isLoading, setIsLoading] = useState(true);
-  const [isLoadingMore, setIsLoadingMore] = useState(false);
-  const [error, setError] = useState("");
-
-  const [isEditing, setIsEditing] = useState(false);
-  const [editError, setEditError] = useState("");
-  const [isSavingProfile, setIsSavingProfile] = useState(false);
-  const [editForm, setEditForm] = useState({
-    spendingBudget: "",
-    savingBudget: "",
-    investingBudget: "",
-    donationBudget: "",
-    credit: "",
-  });
-
-  const segments = useMemo(() => {
-    const p = profile ?? {};
-    return [
-      { key: "spending", label: "Spending", value: toNumber(p.spendingBudget), color: "#22c55e" },
-      { key: "saving", label: "Saving", value: toNumber(p.savingBudget), color: "#3b82f6" },
-      { key: "investing", label: "Investing", value: toNumber(p.investingBudget), color: "#a855f7" },
-      { key: "donation", label: "Donation", value: toNumber(p.donationBudget), color: "#f97316" },
-      { key: "credit", label: "Credit", value: toNumber(p.credit), color: "#ef4444" },
-    ];
-  }, [profile]);
-
-  const totalThisMonth = useMemo(() => {
-    const now = new Date();
-    const y = now.getFullYear();
-    const m = now.getMonth();
-
-    return (incomes ?? []).reduce((acc, i) => {
-      const d = new Date(i?.date);
-      if (Number.isNaN(d.getTime())) return acc;
-      if (d.getFullYear() !== y || d.getMonth() !== m) return acc;
-      const amount = Number(i?.amount);
-      if (!Number.isFinite(amount)) return acc;
-      return acc + amount;
-    }, 0);
-  }, [incomes]);
-
-  const fetchPage = useCallback(async (nextPage) => {
-    const data = await getIncomes({ page: nextPage, size: pageSize });
-    return {
-      nextIncomes: Array.isArray(data?.incomes) ? data.incomes : [],
-      nextHasMore: Boolean(data?.hasMore),
-    };
-  }, []);
-
-  const [graphIncomes, setGraphIncomes] = useState([]);
-
-  const fetchAllIncomesForGraph = useCallback(async () => {
-    let p = 0;
-    let hasMorePages = true;
-    const all = [];
-
-    while (hasMorePages) {
-      const data = await getIncomes({ page: p, size: graphPageSize });
-      const chunk = Array.isArray(data?.incomes) ? data.incomes : [];
-      all.push(...chunk);
-      hasMorePages = Boolean(data?.hasMore) && chunk.length > 0;
-      p += 1;
-      if (p > 200) break;
-    }
-
-    return all;
-  }, []);
-
-  useEffect(() => {
-    let cancelled = false;
-    (async () => {
-      try {
-        setIsLoading(true);
-        setError("");
-
-        const [p, inc, allInc] = await Promise.all([
-          getFinanceProfile(),
-          fetchPage(0),
-          fetchAllIncomesForGraph(),
-        ]);
-        if (cancelled) return;
-        setProfile(p);
-        setEditForm({
-          spendingBudget: p?.spendingBudget ?? "",
-          savingBudget: p?.savingBudget ?? "",
-          investingBudget: p?.investingBudget ?? "",
-          donationBudget: p?.donationBudget ?? "",
-          credit: p?.credit ?? "",
-        });
-        setIncomes(inc.nextIncomes);
-        setGraphIncomes(allInc);
-        setHasMore(inc.nextHasMore);
-        setPage(0);
-      } catch (e) {
-        if (cancelled) return;
-        setError(e?.response?.data?.message || "Failed to load finance dashboard.");
-      } finally {
-        if (!cancelled) setIsLoading(false);
-      }
-    })();
-
-    return () => {
-      cancelled = true;
-    };
-  }, [fetchAllIncomesForGraph, fetchPage]);
-
-  const onEdit = () => {
-    setEditError("");
-    setIsEditing(true);
-  };
-
-  const onCancelEdit = () => {
-    setEditError("");
-    const p = profile ?? {};
-    setEditForm({
-      spendingBudget: p?.spendingBudget ?? "",
-      savingBudget: p?.savingBudget ?? "",
-      investingBudget: p?.investingBudget ?? "",
-      donationBudget: p?.donationBudget ?? "",
-      credit: p?.credit ?? "",
-    });
-    setIsEditing(false);
-  };
-
-  const onSaveProfile = async (e) => {
-    e.preventDefault();
-    setEditError("");
-    setIsSavingProfile(true);
-    try {
-      await startFinanceTracking({
-        spendingBudget:
-          editForm.spendingBudget === "" ? null : Number(editForm.spendingBudget),
-        savingBudget:
-          editForm.savingBudget === "" ? null : Number(editForm.savingBudget),
-        investingBudget:
-          editForm.investingBudget === "" ? null : Number(editForm.investingBudget),
-        donationBudget:
-          editForm.donationBudget === "" ? null : Number(editForm.donationBudget),
-        credit: editForm.credit === "" ? null : Number(editForm.credit),
-      });
-
-      const refreshed = await getFinanceProfile();
-      setProfile(refreshed);
-      setEditForm({
-        spendingBudget: refreshed?.spendingBudget ?? "",
-        savingBudget: refreshed?.savingBudget ?? "",
-        investingBudget: refreshed?.investingBudget ?? "",
-        donationBudget: refreshed?.donationBudget ?? "",
-        credit: refreshed?.credit ?? "",
-      });
-      setIsEditing(false);
-    } catch (err) {
-      const message =
-        err?.response?.data?.message ||
-        Object.values(err?.response?.data?.errors ?? {})[0] ||
-        "Failed to update finance profile";
-      setEditError(message);
-    } finally {
-      setIsSavingProfile(false);
-    }
-  };
-
-  const onLoadMore = async () => {
-    if (isLoadingMore || !hasMore) return;
-    const nextPage = page + 1;
-    try {
-      setIsLoadingMore(true);
-      setError("");
-      const { nextIncomes, nextHasMore } = await fetchPage(nextPage);
-      setIncomes((prev) => [...prev, ...nextIncomes]);
-      setHasMore(nextHasMore);
-      setPage(nextPage);
-    } catch (e) {
-      setError(e?.response?.data?.message || "Failed to load more incomes.");
-    } finally {
-      setIsLoadingMore(false);
-    }
-  };
-
-  return (
-    <div className="space-y-6">
-      <div className="flex items-center justify-between gap-3">
-        <h1 className="text-2xl font-bold">Finance</h1>
-      </div>
-
-      {isEditing ? (
-        <FinanceStartForm
-          form={editForm}
-          setForm={setEditForm}
-          onSubmit={onSaveProfile}
-          onCancel={onCancelEdit}
-          error={editError}
-          isSubmitting={isSavingProfile}
-        />
-      ) : (
-        <FinanceSummaryCard
-          segments={segments}
-          totalThisMonth={totalThisMonth}
-          onEdit={onEdit}
-        />
-      )}
-
-      <FinanceProgressCard incomes={graphIncomes} />
-
-      <FinanceIncomesTable
-        incomes={incomes}
-        isLoading={isLoading}
-        error={error}
-        hasMore={hasMore}
-        isLoadingMore={isLoadingMore}
-        onLoadMore={onLoadMore}
-        onAddIncome={() => navigate("/dashboard/finance/incomes/new")}
-      />
-    </div>
-  );
-}
-
Index: frontend/src/pages/Dashboard/pages/Finance/NewIncome.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Finance/NewIncome.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,50 +1,0 @@
-import React, { useState } from "react";
-import { useNavigate } from "react-router-dom";
-
-import { createIncome } from "../../../../api/finance";
-import NewIncomeForm from "./components/NewIncomeForm.jsx";
-
-export default function NewIncome() {
-  const navigate = useNavigate();
-  const [isSubmitting, setIsSubmitting] = useState(false);
-  const [error, setError] = useState("");
-
-  const onSubmit = async ({ date, amount }) => {
-    setError("");
-    setIsSubmitting(true);
-    try {
-      await createIncome({
-        date,
-        amount: amount === "" ? null : Number(amount),
-      });
-      navigate("/dashboard/finance/tracking", { replace: true });
-    } catch (e) {
-      const message =
-        e?.response?.data?.message ||
-        Object.values(e?.response?.data?.errors ?? {})[0] ||
-        "Failed to add income";
-      setError(message);
-    } finally {
-      setIsSubmitting(false);
-    }
-  };
-
-  return (
-    <div className="space-y-4">
-      <div className="flex items-center justify-between">
-        <h1 className="text-2xl font-bold">Finance</h1>
-        <button className="btn btn-ghost" onClick={() => navigate(-1)}>
-          Back
-        </button>
-      </div>
-
-      <NewIncomeForm
-        onSubmit={onSubmit}
-        onCancel={() => navigate("/dashboard/finance/tracking")}
-        error={error}
-        isSubmitting={isSubmitting}
-      />
-    </div>
-  );
-}
-
Index: frontend/src/pages/Dashboard/pages/Finance/components/FinanceCenteredCard.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Finance/components/FinanceCenteredCard.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,17 +1,0 @@
-import React from "react";
-
-export default function FinanceCenteredCard({ title, message }) {
-  return (
-    <div className="min-h-[70vh] w-full flex items-center justify-center">
-      <div className="w-full max-w-3xl">
-        <div className="card bg-base-200 border border-base-300">
-          <div className="card-body">
-            <h1 className="card-title text-2xl">{title}</h1>
-            <p className="opacity-80">{message}</p>
-          </div>
-        </div>
-      </div>
-    </div>
-  );
-}
-
Index: frontend/src/pages/Dashboard/pages/Finance/components/FinanceIncomesTable.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Finance/components/FinanceIncomesTable.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,79 +1,0 @@
-import React from "react";
-
-function formatDate(value) {
-  if (!value) return "—";
-  const d = new Date(value);
-  if (Number.isNaN(d.getTime())) return String(value);
-  return d.toLocaleDateString(undefined, {
-    year: "numeric",
-    month: "short",
-    day: "2-digit",
-  });
-}
-
-export default function FinanceIncomesTable({
-  incomes,
-  isLoading,
-  error,
-  hasMore,
-  isLoadingMore,
-  onLoadMore,
-  onAddIncome,
-}) {
-  return (
-    <div className="card bg-base-200 border border-base-300">
-      <div className="card-body">
-        <div className="flex items-center justify-between gap-3">
-          <h2 className="card-title">Incomes</h2>
-          <button className="btn btn-primary btn-sm" onClick={onAddIncome}>
-            Add income
-          </button>
-        </div>
-
-        {error ? <p className="text-error text-sm mt-2">{error}</p> : null}
-
-        <div className="mt-4 overflow-x-auto">
-          <table className="table table-zebra">
-            <thead>
-              <tr>
-                <th>Date</th>
-                <th className="text-right">Amount</th>
-              </tr>
-            </thead>
-            <tbody>
-              {isLoading ? (
-                <tr>
-                  <td colSpan={2} className="opacity-70">
-                    Loading…
-                  </td>
-                </tr>
-              ) : (incomes ?? []).length === 0 ? (
-                <tr>
-                  <td colSpan={2} className="opacity-70">
-                    No incomes yet.
-                  </td>
-                </tr>
-              ) : (
-                (incomes ?? []).map((i) => (
-                  <tr key={i.incomeId}>
-                    <td>{formatDate(i.date)}</td>
-                    <td className="text-right tabular-nums">{Number(i.amount).toFixed?.(2) ?? i.amount}</td>
-                  </tr>
-                ))
-              )}
-            </tbody>
-          </table>
-        </div>
-
-        <div className="mt-4">
-          {hasMore ? (
-            <button className="btn btn-outline btn-sm" disabled={isLoadingMore} onClick={onLoadMore}>
-              {isLoadingMore ? "Loading…" : "Load more"}
-            </button>
-          ) : null}
-        </div>
-      </div>
-    </div>
-  );
-}
-
Index: frontend/src/pages/Dashboard/pages/Finance/components/FinanceLegend.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Finance/components/FinanceLegend.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,21 +1,0 @@
-import React from "react";
-
-export default function FinanceLegend({ segments }) {
-  return (
-    <div className="space-y-2 text-sm">
-      {(segments ?? []).map((s) => (
-        <div key={s.key} className="flex items-center justify-between gap-3">
-          <div className="flex items-center gap-2">
-            <span
-              className="inline-block h-3 w-3 rounded"
-              style={{ backgroundColor: s.color }}
-            />
-            <span className="opacity-80">{s.label}</span>
-          </div>
-          <span className="font-medium tabular-nums">{Number(s.value) || 0}%</span>
-        </div>
-      ))}
-    </div>
-  );
-}
-
Index: frontend/src/pages/Dashboard/pages/Finance/components/FinancePieChart.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Finance/components/FinancePieChart.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,67 +1,0 @@
-import React, { useMemo } from "react";
-
-function clamp01(n) {
-  if (!Number.isFinite(n)) return 0;
-  return Math.max(0, Math.min(1, n));
-}
-
-function polarToCartesian(cx, cy, r, angleDeg) {
-  const rad = ((angleDeg - 90) * Math.PI) / 180;
-  return { x: cx + r * Math.cos(rad), y: cy + r * Math.sin(rad) };
-}
-
-function arcPath(cx, cy, r, startAngle, endAngle) {
-  const start = polarToCartesian(cx, cy, r, endAngle);
-  const end = polarToCartesian(cx, cy, r, startAngle);
-  const largeArcFlag = endAngle - startAngle <= 180 ? 0 : 1;
-  return `M ${cx} ${cy} L ${start.x} ${start.y} A ${r} ${r} 0 ${largeArcFlag} 0 ${end.x} ${end.y} Z`;
-}
-
-export default function FinancePieChart({ segments, size = 180 }) {
-  const r = size / 2;
-  const cx = r;
-  const cy = r;
-
-  const normalized = useMemo(() => {
-    const values = (segments ?? []).map((s) => ({
-      ...s,
-      value: Math.max(0, Number(s.value) || 0),
-    }));
-    const sum = values.reduce((acc, s) => acc + s.value, 0);
-    if (sum <= 0) return values.map((s) => ({ ...s, frac: 0 }));
-    return values.map((s) => ({ ...s, frac: clamp01(s.value / sum) }));
-  }, [segments]);
-
-  let currentAngle = 0;
-
-  return (
-    <svg
-      width={size}
-      height={size}
-      viewBox={`0 0 ${size} ${size}`}
-      role="img"
-      aria-label="Budget distribution pie chart"
-    >
-      <circle cx={cx} cy={cy} r={r} fill="#111827" opacity="0.15" />
-      {normalized.map((s) => {
-        const start = currentAngle;
-        const sweep = s.frac * 360;
-        const end = start + sweep;
-        currentAngle = end;
-
-        if (sweep <= 0.001) return null;
-        return (
-          <path
-            key={s.key}
-            d={arcPath(cx, cy, r, start, end)}
-            fill={s.color}
-            stroke="rgba(0,0,0,0.2)"
-            strokeWidth="1"
-          />
-        );
-      })}
-      <circle cx={cx} cy={cy} r={r * 0.55} fill="var(--fallback-b1, oklch(var(--b1)/1))" />
-    </svg>
-  );
-}
-
Index: frontend/src/pages/Dashboard/pages/Finance/components/FinanceProgressCard.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Finance/components/FinanceProgressCard.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,60 +1,0 @@
-import React, { useMemo, useState } from "react";
-
-import TimeRangeToggle from "../../../../../components/graphs/TimeRangeToggle.jsx";
-import PercentChangeAreaChart from "../../../../../components/graphs/PercentChangeAreaChart.jsx";
-import { percentChangeSeries, sumByTimeBucket } from "../../../../../utils/timeSeries.js";
-
-export default function FinanceProgressCard({ incomes }) {
-  const [range, setRange] = useState("weekly");
-
-  const points = useMemo(() => {
-    const buckets = sumByTimeBucket(
-      incomes,
-      range,
-      (i) => i.date,
-      (i) => i.amount,
-    );
-    return percentChangeSeries(buckets);
-  }, [incomes, range]);
-
-  const latest = points?.length ? points[points.length - 1] : null;
-  const latestPct = latest ? Number(latest.value ?? 0) : 0;
-  const latestBase = latest ? Number(latest.base ?? 0) : 0;
-
-  return (
-    <div className="card bg-base-200 border border-base-300">
-      <div className="card-body">
-        <div className="flex items-center justify-between gap-4">
-          <h2 className="card-title">Progress</h2>
-          <div className="flex items-center gap-3">
-            <span className="badge badge-ghost">
-              {latestPct >= 0 ? "+" : ""}
-              {latestPct.toFixed(1)}% (last)
-            </span>
-            <TimeRangeToggle value={range} onChange={setRange} />
-          </div>
-        </div>
-
-        <div className="mt-4 w-full overflow-hidden rounded-xl border border-base-300 bg-base-100 p-2">
-          {points.length < 2 ? (
-            <div className="flex h-65 items-center justify-center text-sm opacity-70">
-              Add at least 2 incomes to see % change.
-            </div>
-          ) : (
-            <PercentChangeAreaChart
-              points={points}
-              granularity={range}
-              height={260}
-              positiveColor="#a855f7"
-            />
-          )}
-        </div>
-
-        <div className="mt-2 text-xs opacity-70">
-          Showing percentage change in income totals per {range} bucket. Latest bucket total: {Math.round(latestBase)}.
-        </div>
-      </div>
-    </div>
-  );
-}
-
Index: frontend/src/pages/Dashboard/pages/Finance/components/FinanceStartCtaCard.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Finance/components/FinanceStartCtaCard.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,22 +1,0 @@
-import React from "react";
-
-export default function FinanceStartCtaCard({ error, onStart }) {
-  return (
-    <div className="card bg-base-200 border border-base-300">
-      <div className="card-body">
-        <h1 className="card-title text-2xl">Finance</h1>
-        <p className="opacity-80">
-          Start tracking your finances by setting your yearly budget distribution
-          percentages.
-        </p>
-        {error ? <p className="text-error text-sm mt-2">{error}</p> : null}
-        <div className="mt-4">
-          <button className="btn btn-primary" onClick={onStart}>
-            Start tracking
-          </button>
-        </div>
-      </div>
-    </div>
-  );
-}
-
Index: frontend/src/pages/Dashboard/pages/Finance/components/FinanceStartForm.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Finance/components/FinanceStartForm.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,155 +1,0 @@
-import React, { useMemo } from "react";
-import FinanceLegend from "./FinanceLegend.jsx";
-import FinancePieChart from "./FinancePieChart.jsx";
-
-function toNumber(value) {
-  if (value === "" || value === null || value === undefined) return 0;
-  const n = Number(value);
-  return Number.isFinite(n) ? n : 0;
-}
-
-export default function FinanceStartForm({ form, setForm, onSubmit, onCancel, error, isSubmitting }) {
-  const segments = useMemo(
-    () => [
-      { key: "spending", label: "Spending", value: toNumber(form.spendingBudget), color: "#22c55e" },
-      { key: "saving", label: "Saving", value: toNumber(form.savingBudget), color: "#3b82f6" },
-      { key: "investing", label: "Investing", value: toNumber(form.investingBudget), color: "#a855f7" },
-      { key: "donation", label: "Donation", value: toNumber(form.donationBudget), color: "#f97316" },
-      { key: "credit", label: "Credit", value: toNumber(form.credit), color: "#ef4444" },
-    ],
-    [form],
-  );
-
-  const sum = segments.reduce((acc, s) => acc + (Number(s.value) || 0), 0);
-  const sumOk = Math.abs(sum - 100) <= 0.01;
-
-  const onChange = (key) => (e) => {
-    const value = e.target.value;
-    setForm((prev) => ({ ...prev, [key]: value }));
-  };
-
-  return (
-    <div className="card bg-base-200 border border-base-300">
-      <div className="card-body gap-6">
-        <div>
-          <h2 className="card-title text-xl">Set your budget distribution</h2>
-          <p className="text-sm opacity-80">
-            Enter 5 percentages that sum to 100. You can fine-tune later.
-          </p>
-        </div>
-
-        <form onSubmit={onSubmit} className="grid grid-cols-1 gap-6 lg:grid-cols-2">
-          <div className="space-y-4">
-            <label className="form-control">
-              <div className="label">
-                <span className="label-text">Spending (%)</span>
-              </div>
-              <input
-                className="input input-bordered w-full"
-                type="number"
-                min="0"
-                max="100"
-                step="0.01"
-                value={form.spendingBudget}
-                onChange={onChange("spendingBudget")}
-                required
-              />
-            </label>
-
-            <label className="form-control">
-              <div className="label">
-                <span className="label-text">Saving (%)</span>
-              </div>
-              <input
-                className="input input-bordered w-full"
-                type="number"
-                min="0"
-                max="100"
-                step="0.01"
-                value={form.savingBudget}
-                onChange={onChange("savingBudget")}
-                required
-              />
-            </label>
-
-            <label className="form-control">
-              <div className="label">
-                <span className="label-text">Investing (%)</span>
-              </div>
-              <input
-                className="input input-bordered w-full"
-                type="number"
-                min="0"
-                max="100"
-                step="0.01"
-                value={form.investingBudget}
-                onChange={onChange("investingBudget")}
-                required
-              />
-            </label>
-
-            <label className="form-control">
-              <div className="label">
-                <span className="label-text">Donation (%)</span>
-              </div>
-              <input
-                className="input input-bordered w-full"
-                type="number"
-                min="0"
-                max="100"
-                step="0.01"
-                value={form.donationBudget}
-                onChange={onChange("donationBudget")}
-                required
-              />
-            </label>
-
-            <label className="form-control">
-              <div className="label">
-                <span className="label-text">Credit (%)</span>
-              </div>
-              <input
-                className="input input-bordered w-full"
-                type="number"
-                min="0"
-                max="100"
-                step="0.01"
-                value={form.credit}
-                onChange={onChange("credit")}
-                required
-              />
-            </label>
-
-            <div className="text-sm">
-              <span className={sumOk ? "text-success" : "text-warning"}>
-                Total: {sum.toFixed(2)}%
-              </span>
-              {!sumOk ? (
-                <span className="opacity-70"> (must equal 100%)</span>
-              ) : null}
-            </div>
-
-            {error ? <p className="text-error text-sm">{error}</p> : null}
-
-            <div className="flex gap-2">
-              <button className="btn btn-primary" type="submit" disabled={isSubmitting || !sumOk}>
-                {isSubmitting ? "Saving…" : "Start tracking"}
-              </button>
-              <button className="btn" type="button" onClick={onCancel} disabled={isSubmitting}>
-                Cancel
-              </button>
-            </div>
-          </div>
-
-          <div className="grid grid-cols-1 gap-4 md:grid-cols-[auto_1fr] items-center">
-            <div className="flex justify-center md:justify-start">
-              <FinancePieChart segments={segments} size={200} />
-            </div>
-            <FinanceLegend segments={segments} />
-          </div>
-        </form>
-      </div>
-    </div>
-  );
-}
-
Index: frontend/src/pages/Dashboard/pages/Finance/components/FinanceSummaryCard.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Finance/components/FinanceSummaryCard.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,87 +1,0 @@
-import React from "react";
-import FinanceLegend from "./FinanceLegend.jsx";
-import FinancePieChart from "./FinancePieChart.jsx";
-
-function formatMoney(value) {
-  const n = Number(value);
-  if (!Number.isFinite(n)) return "—";
-  try {
-    return new Intl.NumberFormat(undefined, {
-      minimumFractionDigits: 2,
-      maximumFractionDigits: 2,
-    }).format(n);
-  } catch {
-    return n.toFixed(2);
-  }
-}
-
-export default function FinanceSummaryCard({ segments, totalThisMonth, onEdit }) {
-  return (
-    <div className="card bg-base-200 border border-base-300">
-      <div className="card-body">
-        <div className="flex items-start justify-between gap-6 flex-wrap">
-          <div>
-            <h2 className="card-title">Budget distribution</h2>
-            <p className="text-sm opacity-80">
-              Your current percentages (stored in your finance profile).
-            </p>
-          </div>
-
-          <div className="flex items-center gap-2">
-            <button className="btn btn-outline btn-sm" onClick={onEdit}>
-              Edit
-            </button>
-          </div>
-        </div>
-
-        <div className="mt-4">
-          <div className="rounded-lg border border-base-300 bg-base-100 p-4">
-            <div className="text-sm opacity-70">Total money earned this month</div>
-            <div className="text-2xl font-bold tabular-nums">
-              {formatMoney(totalThisMonth)}
-            </div>
-          </div>
-        </div>
-
-        <div className="mt-6 grid grid-cols-1 gap-6 md:grid-cols-[auto_1fr] items-center">
-          <div className="flex justify-center md:justify-start">
-            <FinancePieChart segments={segments} size={180} />
-          </div>
-
-          <div className="space-y-4">
-            <FinanceLegend segments={segments} />
-
-            <div className="divider my-0" />
-
-            <div className="space-y-2 text-sm">
-              {segments.map((s) => {
-                const pct = Number(s.value) || 0;
-                const allocated = ((Number(totalThisMonth) || 0) * pct) / 100;
-                return (
-                  <div
-                    key={s.key}
-                    className="flex items-center justify-between gap-3"
-                  >
-                    <div className="flex items-center gap-2">
-                      <span
-                        className="inline-block h-3 w-3 rounded"
-                        style={{ backgroundColor: s.color }}
-                      />
-                      <span className="opacity-80">
-                        {s.label} allocation
-                      </span>
-                    </div>
-                    <span className="font-medium tabular-nums">
-                      {formatMoney(allocated)}
-                    </span>
-                  </div>
-                );
-              })}
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-  );
-}
-
Index: frontend/src/pages/Dashboard/pages/Finance/components/NewIncomeForm.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Finance/components/NewIncomeForm.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,66 +1,0 @@
-import React, { useState } from "react";
-
-export default function NewIncomeForm({ onSubmit, onCancel, error, isSubmitting }) {
-  const [date, setDate] = useState(() => {
-    const d = new Date();
-    console.log('Date:', d);
-    return d.toISOString().slice(0, 10);
-  });
-  const [amount, setAmount] = useState("");
-
-  return (
-    <div className="card bg-base-200 border border-base-300">
-      <div className="card-body">
-        <h2 className="card-title">Add income</h2>
-
-        <form
-          className="mt-4 grid grid-cols-1 gap-4 max-w-md"
-          onSubmit={(e) => {
-            e.preventDefault();
-            onSubmit({ date, amount });
-          }}
-        >
-          <label className="form-control">
-            <div className="label">
-              <span className="label-text">Date</span>
-            </div>
-            <input
-              className="input input-bordered"
-              type="date"
-              value={date}
-              onChange={(e) => setDate(e.target.value)}
-              required
-            />
-          </label>
-
-          <label className="form-control">
-            <div className="label">
-              <span className="label-text">Amount</span>
-            </div>
-            <input
-              className="input input-bordered"
-              type="number"
-              min="0.01"
-              step="0.01"
-              value={amount}
-              onChange={(e) => setAmount(e.target.value)}
-              required
-            />
-          </label>
-
-          {error ? <p className="text-error text-sm">{error}</p> : null}
-
-          <div className="flex gap-2">
-            <button className="btn btn-primary" type="submit" disabled={isSubmitting}>
-              {isSubmitting ? "Saving…" : "Save"}
-            </button>
-            <button className="btn" type="button" onClick={onCancel} disabled={isSubmitting}>
-              Cancel
-            </button>
-          </div>
-        </form>
-      </div>
-    </div>
-  );
-}
-
Index: frontend/src/pages/Dashboard/pages/Investing/Investing.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Investing/Investing.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,85 +1,0 @@
-import React, { useEffect, useState } from "react";
-import { useNavigate } from "react-router-dom";
-
-import api from "../../../../api/axios";
-
-import InvestingCenteredCard from "./components/InvestingCenteredCard.jsx";
-import InvestingStartCtaCard from "./components/InvestingStartCtaCard.jsx";
-
-const Investing = () => {
-  const navigate = useNavigate();
-
-  const [isLoading, setIsLoading] = useState(true);
-  const [isTracking, setIsTracking] = useState(false);
-  const [error, setError] = useState("");
-  const [isSubmitting, setIsSubmitting] = useState(false);
-
-  useEffect(() => {
-    let isMounted = true;
-    const run = async () => {
-      setIsLoading(true);
-      setError("");
-      try {
-        const res = await api.get("/investing/status");
-        const tracking = Boolean(res?.data?.tracking);
-        if (!isMounted) return;
-        setIsTracking(tracking);
-        if (tracking) {
-          navigate("/dashboard/investing/tracking", { replace: true });
-        }
-      } catch (err) {
-        if (!isMounted) return;
-        const message =
-          err?.response?.data?.message || "Failed to load investing status";
-        setError(message);
-      } finally {
-        if (isMounted) setIsLoading(false);
-      }
-    };
-
-    run();
-    return () => {
-      isMounted = false;
-    };
-  }, [navigate]);
-
-  const onStart = async () => {
-    setError("");
-    setIsSubmitting(true);
-    try {
-      await api.post("/investing/start", {});
-      setIsTracking(true);
-      navigate("/dashboard/investing/tracking", { replace: true });
-    } catch (err) {
-      const message =
-        err?.response?.data?.message ||
-        Object.values(err?.response?.data?.errors ?? {})[0] ||
-        "Failed to start tracking";
-      setError(message);
-    } finally {
-      setIsSubmitting(false);
-    }
-  };
-
-  if (isLoading) {
-    return <InvestingCenteredCard title="Investing" message="Loading…" />;
-  }
-
-  if (isTracking) {
-    return <InvestingCenteredCard title="Investing" message="Redirecting…" />;
-  }
-
-  return (
-    <div className="min-h-[70vh] w-full flex items-center justify-center">
-      <div className="w-full max-w-3xl">
-        <InvestingStartCtaCard
-          error={error}
-          onStart={onStart}
-          isSubmitting={isSubmitting}
-        />
-      </div>
-    </div>
-  );
-};
-
-export default Investing;
Index: frontend/src/pages/Dashboard/pages/Investing/InvestingTracking.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Investing/InvestingTracking.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,288 +1,0 @@
-import React, { useCallback, useEffect, useMemo, useState } from "react";
-import { useNavigate } from "react-router-dom";
-
-import api from "../../../../api/axios";
-import { fetchCurrentPricesBySymbol } from "../../../../api/twelveData";
-
-import InvestingTrackingHeader from "./components/InvestingTrackingHeader.jsx";
-import InvestingProgressCard from "./components/InvestingProgressCard.jsx";
-import InvestingAssetsTable from "./components/InvestingAssetsTable.jsx";
-
-function formatDate(value) {
-  if (!value) return "—";
-  const d = new Date(value);
-  if (Number.isNaN(d.getTime())) return String(value);
-  return d.toLocaleDateString(undefined, {
-    year: "numeric",
-    month: "short",
-    day: "2-digit",
-  });
-}
-
-function formatMoney(value) {
-  if (value === null || value === undefined || value === "") return "—";
-  const num = Number(value);
-  if (Number.isNaN(num)) return String(value);
-  return num.toLocaleString(undefined, {
-    style: "currency",
-    currency: "USD",
-    maximumFractionDigits: 2,
-  });
-}
-
-export default function InvestingTracking() {
-  const navigate = useNavigate();
-  const pageSize = 5;
-  const graphPageSize = 200;
-
-  const [assets, setAssets] = useState([]);
-  const [page, setPage] = useState(0);
-  const [hasMore, setHasMore] = useState(false);
-  const [isLoading, setIsLoading] = useState(true);
-  const [isLoadingMore, setIsLoadingMore] = useState(false);
-  const [isDeletingId, setIsDeletingId] = useState(null);
-  const [pendingDelete, setPendingDelete] = useState(null);
-  const [error, setError] = useState("");
-
-  const [quotesBySymbol, setQuotesBySymbol] = useState({});
-  const [isQuotesLoading, setIsQuotesLoading] = useState(false);
-  const [quotesError, setQuotesError] = useState("");
-
-  const columns = useMemo(
-    () => [
-      { key: "tickerSymbol", label: "Ticker" },
-      { key: "quantity", label: "Quantity" },
-      { key: "buyPrice", label: "Buy Price" },
-      { key: "buyDate", label: "Buy Date" },
-      { key: "currentPrice", label: "Current Price" },
-      { key: "roi", label: "ROI" },
-      { key: "actions", label: "" },
-    ],
-    [],
-  );
-
-  const fetchPage = useCallback(async (nextPage, size = pageSize) => {
-    const resp = await api.get("/investing/assets", {
-      params: { page: nextPage, size },
-    });
-    const data = resp?.data ?? {};
-    const nextAssets = Array.isArray(data.assets) ? data.assets : [];
-    const nextHasMore = Boolean(data.hasMore);
-    return { nextAssets, nextHasMore };
-  }, []);
-
-  const [graphAssets, setGraphAssets] = useState([]);
-
-  const fetchAllAssetsForGraph = useCallback(async () => {
-    let p = 0;
-    let hasMorePages = true;
-    const all = [];
-
-    while (hasMorePages) {
-      const { nextAssets: chunk, nextHasMore } = await fetchPage(p, graphPageSize);
-      all.push(...chunk);
-      hasMorePages = nextHasMore && chunk.length > 0;
-      p += 1;
-      if (p > 200) break;
-    }
-
-    return all;
-  }, [fetchPage]);
-
-  useEffect(() => {
-    let cancelled = false;
-    (async () => {
-      try {
-        setIsLoading(true);
-        setError("");
-        const [tableFirstPage, allForGraph] = await Promise.all([
-          fetchPage(0, pageSize),
-          fetchAllAssetsForGraph(),
-        ]);
-        if (cancelled) return;
-        setAssets(tableFirstPage.nextAssets);
-        setHasMore(tableFirstPage.nextHasMore);
-        setGraphAssets(allForGraph);
-        setPage(0);
-      } catch (e) {
-        if (cancelled) return;
-        setError(e?.response?.data?.message || "Failed to load assets.");
-      } finally {
-        if (!cancelled) setIsLoading(false);
-      }
-    })();
-    return () => {
-      cancelled = true;
-    };
-  }, [fetchAllAssetsForGraph, fetchPage]);
-
-  const tickerSymbols = useMemo(() => {
-    const set = new Set();
-    for (const a of assets) {
-      if (a?.tickerSymbol) set.add(String(a.tickerSymbol).toUpperCase());
-    }
-    return Array.from(set).sort();
-  }, [assets]);
-
-  const tickerSymbolsKey = useMemo(() => tickerSymbols.join(","), [tickerSymbols]);
-
-  useEffect(() => {
-    if (tickerSymbols.length === 0) {
-      setQuotesBySymbol({});
-      setIsQuotesLoading(false);
-      setQuotesError("");
-      return;
-    }
-
-    let cancelled = false;
-    let intervalId;
-
-    const fetchQuotes = async () => {
-      try {
-        setIsQuotesLoading(true);
-        setQuotesError("");
-        if (cancelled) return;
-        const map = await fetchCurrentPricesBySymbol(tickerSymbols);
-        if (cancelled) return;
-        setQuotesBySymbol(map);
-      } catch (err) {
-        if (cancelled) return;
-        const msg =
-          err?.message ||
-          "Could not fetch current prices (check API key / provider availability).";
-        setQuotesError(msg);
-        // Keep any previously loaded quotes instead of wiping the table.
-        // (If this is the first load, quotesBySymbol will already be empty.)
-      } finally {
-        if (!cancelled) setIsQuotesLoading(false);
-      }
-    };
-
-    fetchQuotes();
-    // Keep refresh infrequent to avoid provider rate limits.
-    intervalId = setInterval(fetchQuotes, 5 * 60_000);
-
-    return () => {
-      cancelled = true;
-      if (intervalId) clearInterval(intervalId);
-    };
-  }, [tickerSymbolsKey, tickerSymbols]);
-
-  const onLoadMore = async () => {
-    if (isLoadingMore || !hasMore) return;
-    const nextPage = page + 1;
-    try {
-      setIsLoadingMore(true);
-      setError("");
-      const { nextAssets, nextHasMore } = await fetchPage(nextPage);
-      setAssets((prev) => [...prev, ...nextAssets]);
-      setHasMore(nextHasMore);
-      setPage(nextPage);
-    } catch (e) {
-      setError(e?.response?.data?.message || "Failed to load more assets.");
-    } finally {
-      setIsLoadingMore(false);
-    }
-  };
-
-  const doDelete = async (assetId) => {
-    if (!assetId || isDeletingId) return;
-    try {
-      setIsDeletingId(assetId);
-      setError("");
-      await api.delete(`/investing/assets/${assetId}`);
-      setAssets((prev) => prev.filter((a) => a.assetId !== assetId));
-      setPendingDelete(null);
-    } catch (e) {
-      setError(e?.response?.data?.message || "Failed to delete asset.");
-    } finally {
-      setIsDeletingId(null);
-    }
-  };
-
-  const onRequestDelete = (asset) => {
-    if (!asset?.assetId) return;
-    setPendingDelete({
-      assetId: asset.assetId,
-      tickerSymbol: asset.tickerSymbol,
-    });
-  };
-
-  return (
-    <div className="space-y-6">
-      <InvestingTrackingHeader
-        onAddAsset={() => navigate("/dashboard/investing/assets/new")}
-      />
-       <InvestingProgressCard assets={graphAssets} quotesBySymbol={quotesBySymbol} />
-
-      {quotesError ? (
-        <div className="text-sm opacity-70">
-          Prices unavailable: {quotesError}
-        </div>
-      ) : null}
-
-      {pendingDelete ? (
-        <div className="modal modal-open" role="dialog" aria-modal="true">
-          <div className="modal-box">
-            <h3 className="text-lg font-semibold">Delete investment?</h3>
-            <p className="opacity-80 mt-2">
-              This will permanently delete{" "}
-              <span className="font-semibold">
-                {pendingDelete.tickerSymbol || "this asset"}
-              </span>
-              .
-            </p>
-
-            <div className="modal-action">
-              <button
-                type="button"
-                className="btn btn-ghost"
-                onClick={() => setPendingDelete(null)}
-                disabled={isDeletingId === pendingDelete.assetId}
-              >
-                Cancel
-              </button>
-              <button
-                type="button"
-                className="btn bg-red-500! text-white! hover:bg-red-600! border-0"
-                onClick={() => doDelete(pendingDelete.assetId)}
-                disabled={isDeletingId === pendingDelete.assetId}
-              >
-                {isDeletingId === pendingDelete.assetId
-                  ? "Deleting…"
-                  : "Delete"}
-              </button>
-            </div>
-          </div>
-          <button
-            type="button"
-            className="modal-backdrop"
-            aria-label="Close"
-            onClick={() => {
-              if (isDeletingId === pendingDelete.assetId) return;
-              setPendingDelete(null);
-            }}
-          />
-        </div>
-      ) : null}
-
-      <InvestingAssetsTable
-        columns={columns}
-        assets={assets}
-        isLoading={isLoading}
-        error={error}
-        quotesBySymbol={quotesBySymbol}
-        isQuotesLoading={isQuotesLoading}
-        isLoadingMore={isLoadingMore}
-        hasMore={hasMore}
-        onLoadMore={onLoadMore}
-        pageSize={pageSize}
-        formatDate={formatDate}
-        formatMoney={formatMoney}
-        onAddAsset={() => navigate("/dashboard/investing/assets/new")}
-        onRequestDelete={onRequestDelete}
-        isDeletingId={isDeletingId}
-      />
-    </div>
-  );
-}
Index: frontend/src/pages/Dashboard/pages/Investing/NewInvestment.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Investing/NewInvestment.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,250 +1,0 @@
-import React, { useEffect, useMemo, useState } from "react";
-import { useNavigate } from "react-router-dom";
-
-import api from "../../../../api/axios";
-import { searchTickers } from "../../../../api/yahooFinance";
-
-export default function NewInvestment() {
-  const navigate = useNavigate();
-
-  const todayIso = useMemo(() => {
-    const now = new Date();
-    const yyyy = now.getFullYear();
-    const mm = String(now.getMonth() + 1).padStart(2, "0");
-    const dd = String(now.getDate()).padStart(2, "0");
-    return `${yyyy}-${mm}-${dd}`;
-  }, []);
-
-  const initialForm = useMemo(
-    () => ({ tickerSymbol: "", quantity: "", buyPrice: "", buyDate: "" }),
-    [],
-  );
-
-  const [form, setForm] = useState(initialForm);
-  const [error, setError] = useState("");
-  const [isSubmitting, setIsSubmitting] = useState(false);
-
-  const [tickerQuery, setTickerQuery] = useState("");
-  const [tickerOptions, setTickerOptions] = useState([]);
-  const [isLoadingTickers, setIsLoadingTickers] = useState(false);
-
-  useEffect(() => {
-    const q = tickerQuery.trim();
-    if (q.length < 2) {
-      setTickerOptions([]);
-      setIsLoadingTickers(false);
-      return;
-    }
-
-    let cancelled = false;
-    const handle = setTimeout(async () => {
-      try {
-        setIsLoadingTickers(true);
-        if (cancelled) return;
-        const data = await searchTickers(q, 20);
-        if (cancelled) return;
-        setTickerOptions(Array.isArray(data) ? data : []);
-      } catch {
-        if (!cancelled) setTickerOptions([]);
-      } finally {
-        if (!cancelled) setIsLoadingTickers(false);
-      }
-    }, 250);
-
-    return () => {
-      cancelled = true;
-      clearTimeout(handle);
-    };
-  }, [tickerQuery]);
-
-  const onSubmit = async (e) => {
-    e.preventDefault();
-    setError("");
-
-    if (!form.tickerSymbol) {
-      setError("Please select a ticker symbol");
-      return;
-    }
-
-    if (form.buyDate && form.buyDate > todayIso) {
-      setError("Buy date cannot be in the future");
-      return;
-    }
-
-    const quantityNum = form.quantity === "" ? null : Number(form.quantity);
-    if (!quantityNum || Number.isNaN(quantityNum) || quantityNum <= 0) {
-      setError("Quantity must be greater than 0");
-      return;
-    }
-
-    const buyPriceNum = form.buyPrice === "" ? null : Number(form.buyPrice);
-    if (
-      buyPriceNum !== null &&
-      (Number.isNaN(buyPriceNum) || buyPriceNum < 0)
-    ) {
-      setError("Buy price must be 0 or greater");
-      return;
-    }
-
-    try {
-      setIsSubmitting(true);
-      await api.post("/investing/assets", {
-        tickerSymbol: form.tickerSymbol,
-        quantity: quantityNum,
-        buyPrice: buyPriceNum,
-        buyDate: form.buyDate === "" ? null : form.buyDate,
-      });
-
-      navigate("/dashboard/investing/tracking", { replace: true });
-    } catch (err) {
-      const message =
-        err?.response?.data?.message ||
-        Object.values(err?.response?.data?.errors ?? {})[0] ||
-        "Failed to add investment";
-      setError(message);
-    } finally {
-      setIsSubmitting(false);
-    }
-  };
-
-  return (
-    <div className="max-w-2xl">
-      <div className="card bg-base-200 border border-base-300">
-        <div className="card-body">
-          <h1 className="text-2xl font-bold">Add new investment</h1>
-          <p className="opacity-80">
-            Add an asset you bought. (We’ll build performance tracking next.)
-          </p>
-
-          {error ? (
-            <div className="alert alert-error mt-4">
-              <span>{error}</span>
-            </div>
-          ) : null}
-
-          <form onSubmit={onSubmit} className="mt-6 space-y-4">
-            <div>
-              <label className="label">
-                <span className="label-text">Ticker symbol</span>
-              </label>
-              <div className="grid grid-cols-1 gap-3 sm:grid-cols-2">
-                <input
-                  className="input input-bordered w-full"
-                  placeholder="Search (e.g. AAPL, TSLA, NVDA)"
-                  value={tickerQuery}
-                  onChange={(e) => setTickerQuery(e.target.value)}
-                />
-                <select
-                  className="select select-bordered w-full"
-                  value={form.tickerSymbol}
-                  onChange={(e) =>
-                    setForm((p) => ({ ...p, tickerSymbol: e.target.value }))
-                  }
-                  required
-                >
-                  <option value="" disabled>
-                    {isLoadingTickers
-                      ? "Loading tickers…"
-                      : tickerOptions.length
-                        ? "Select a ticker"
-                        : "Search to load tickers"}
-                  </option>
-                  {tickerOptions.map((o) => {
-                    const symbol = o?.symbol ?? "";
-                    const name = o?.name ?? "";
-                    const exchange = o?.exchange ?? "";
-                    const label = [
-                      symbol,
-                      name ? `— ${name}` : "",
-                      exchange ? `(${exchange})` : "",
-                    ]
-                      .filter(Boolean)
-                      .join(" ");
-                    return (
-                      <option key={symbol} value={symbol}>
-                        {label}
-                      </option>
-                    );
-                  })}
-                </select>
-              </div>
-              <p className="text-xs opacity-70 mt-2">
-                Only tickers returned by Yahoo Finance can be selected.
-              </p>
-            </div>
-
-            <div className="grid grid-cols-1 gap-4 sm:grid-cols-2">
-              <div>
-                <label className="label">
-                  <span className="label-text">Quantity</span>
-                </label>
-                <input
-                  type="number"
-                  step="any"
-                  min="0"
-                  className="input input-bordered w-full"
-                  placeholder="10"
-                  value={form.quantity}
-                  onChange={(e) =>
-                    setForm((p) => ({ ...p, quantity: e.target.value }))
-                  }
-                  required
-                />
-              </div>
-
-              <div>
-                <label className="label">
-                  <span className="label-text">Buy price (optional)</span>
-                </label>
-                <input
-                  type="number"
-                  step="any"
-                  min="0"
-                  className="input input-bordered w-full"
-                  placeholder="187.20"
-                  value={form.buyPrice}
-                  onChange={(e) =>
-                    setForm((p) => ({ ...p, buyPrice: e.target.value }))
-                  }
-                />
-              </div>
-            </div>
-
-            <div>
-              <label className="label">
-                <span className="label-text">Buy date (optional)</span>
-              </label>
-              <input
-                type="date"
-                className="input input-bordered w-full"
-                value={form.buyDate}
-                onChange={(e) =>
-                  setForm((p) => ({ ...p, buyDate: e.target.value }))
-                }
-                max={todayIso}
-              />
-            </div>
-
-            <div className="flex flex-col gap-3 sm:flex-row sm:justify-end">
-              <button
-                type="button"
-                className="btn btn-ghost"
-                onClick={() => navigate("/dashboard/investing/tracking")}
-                disabled={isSubmitting}
-              >
-                Cancel
-              </button>
-              <button
-                type="submit"
-                className="btn bg-green-400! text-black! hover:bg-green-500! border-0"
-                disabled={isSubmitting}
-              >
-                {isSubmitting ? "Saving…" : "Save investment"}
-              </button>
-            </div>
-          </form>
-        </div>
-      </div>
-    </div>
-  );
-}
Index: frontend/src/pages/Dashboard/pages/Investing/components/InvestingAssetsTable.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Investing/components/InvestingAssetsTable.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,237 +1,0 @@
-import React, { useMemo } from "react";
-import { MdDelete } from "react-icons/md";
-
-export default function InvestingAssetsTable({
-  columns,
-  assets,
-  isLoading,
-  error,
-  quotesBySymbol,
-  isQuotesLoading,
-  isLoadingMore,
-  hasMore,
-  onLoadMore,
-  pageSize,
-  formatDate,
-  formatMoney,
-  onAddAsset,
-  onRequestDelete,
-  onDelete,
-  isDeletingId,
-}) {
-  const totals = useMemo(() => {
-    let totalInvested = 0;
-    let totalCurrent = 0;
-    let investedCount = 0;
-    let currentCount = 0;
-
-    for (const a of assets) {
-      const symbol = String(a?.tickerSymbol ?? "").toUpperCase();
-      const quantity = Number(a?.quantity);
-      if (!symbol || Number.isNaN(quantity) || quantity <= 0) continue;
-
-      const buyPrice = Number(a?.buyPrice);
-      if (!Number.isNaN(buyPrice) && buyPrice >= 0) {
-        totalInvested += buyPrice * quantity;
-        investedCount += 1;
-      }
-
-      const currentPrice = Number(quotesBySymbol?.[symbol]);
-      if (!Number.isNaN(currentPrice) && currentPrice >= 0) {
-        totalCurrent += currentPrice * quantity;
-        currentCount += 1;
-      }
-    }
-
-    const hasInvested = investedCount > 0 && totalInvested > 0;
-    const hasCurrent = currentCount > 0;
-    const roiPct =
-      hasInvested && hasCurrent
-        ? ((totalCurrent - totalInvested) / totalInvested) * 100
-        : null;
-
-    return { totalInvested, totalCurrent, roiPct, hasInvested, hasCurrent };
-  }, [assets, quotesBySymbol]);
-
-  return (
-    <div className="card bg-base-200 border border-base-300">
-      <div className="card-body">
-        <div className="flex items-center justify-between gap-4">
-          <h2 className="text-lg font-semibold">Assets</h2>
-          <span className="text-sm opacity-70">
-            Showing {pageSize} per page
-          </span>
-        </div>
-
-        {error ? (
-          <div className="alert alert-error mt-4">
-            <span>{error}</span>
-          </div>
-        ) : null}
-
-        <div className="mt-4 grid grid-cols-1 gap-3 sm:grid-cols-3">
-          <div className="stat bg-base-100 rounded-box border border-base-300">
-            <div className="stat-title">Total invested</div>
-            <div className="stat-value text-xl">
-              {totals.hasInvested ? formatMoney(totals.totalInvested) : "—"}
-            </div>
-            <div className="stat-desc opacity-70">
-              Cost basis (buy price × quantity)
-            </div>
-          </div>
-
-          <div className="stat bg-base-100 rounded-box border border-base-300">
-            <div className="stat-title">Current value</div>
-            <div className="stat-value text-xl">
-              {totals.hasCurrent && !isQuotesLoading
-                ? formatMoney(totals.totalCurrent)
-                : isQuotesLoading
-                  ? "Loading…"
-                  : "—"}
-            </div>
-            <div className="stat-desc opacity-70">Live price × quantity</div>
-          </div>
-
-          <div className="stat bg-base-100 rounded-box border border-base-300">
-            <div className="stat-title">Total ROI</div>
-            <div className="stat-value text-xl">
-              {typeof totals.roiPct === "number" &&
-              !Number.isNaN(totals.roiPct) ? (
-                <span
-                  className={
-                    totals.roiPct > 0
-                      ? "text-green-400 font-semibold"
-                      : totals.roiPct < 0
-                        ? "text-red-400 font-semibold"
-                        : "opacity-80"
-                  }
-                >
-                  {totals.roiPct > 0 ? "+" : ""}
-                  {totals.roiPct.toFixed(2)}%
-                </span>
-              ) : isQuotesLoading ? (
-                "Loading…"
-              ) : (
-                "—"
-              )}
-            </div>
-            <div className="stat-desc opacity-70">
-              (current − invested) ÷ invested
-            </div>
-          </div>
-        </div>
-
-        <div className="mt-4 overflow-x-auto">
-          <table className="table">
-            <thead>
-              <tr>
-                {columns.map((c) => (
-                  <th key={c.key}>{c.label}</th>
-                ))}
-              </tr>
-            </thead>
-            <tbody>
-              {isLoading ? (
-                <tr>
-                  <td colSpan={columns.length} className="opacity-70">
-                    Loading…
-                  </td>
-                </tr>
-              ) : assets.length === 0 ? (
-                <tr>
-                  <td colSpan={columns.length} className="py-10">
-                    <div className="flex flex-col items-center text-center gap-3">
-                      <p className="opacity-80">No investments yet.</p>
-                      <button
-                        type="button"
-                        className="btn bg-green-400! text-black! hover:bg-green-500! border-0"
-                        onClick={onAddAsset}
-                      >
-                        Add your first investment
-                      </button>
-                    </div>
-                  </td>
-                </tr>
-              ) : (
-                assets.map((a) => (
-                  <tr key={a.assetId}>
-                    <td className="font-semibold">{a.tickerSymbol ?? "—"}</td>
-                    <td>{a.quantity ?? "—"}</td>
-                    <td>{formatMoney(a.buyPrice)}</td>
-                    <td>{formatDate(a.buyDate)}</td>
-                    <td>
-                      {isQuotesLoading
-                        ? "Loading…"
-                        : formatMoney(
-                            quotesBySymbol?.[
-                              String(a.tickerSymbol ?? "").toUpperCase()
-                            ],
-                          )}
-                    </td>
-                    <td>
-                      {(() => {
-                        const symbol = String(
-                          a.tickerSymbol ?? "",
-                        ).toUpperCase();
-                        const current = Number(quotesBySymbol?.[symbol]);
-                        const buy = Number(a.buyPrice);
-                        if (!symbol || Number.isNaN(current)) return "—";
-                        if (Number.isNaN(buy) || buy <= 0) return "—";
-                        const roiPct = ((current - buy) / buy) * 100;
-                        const cls =
-                          roiPct > 0
-                            ? "text-green-400 font-semibold"
-                            : roiPct < 0
-                              ? "text-red-400 font-semibold"
-                              : "opacity-80";
-                        const sign = roiPct > 0 ? "+" : "";
-                        return (
-                          <span className={cls}>
-                            {sign}
-                            {roiPct.toFixed(2)}%
-                          </span>
-                        );
-                      })()}
-                    </td>
-                    <td className="text-right">
-                      <button
-                        type="button"
-                        className="btn btn-ghost btn-sm text-red-400 hover:text-red-300"
-                        title="Delete"
-                        onClick={() => {
-                          if (onRequestDelete) {
-                            onRequestDelete(a);
-                            return;
-                          }
-                          onDelete?.(a.assetId);
-                        }}
-                        disabled={isDeletingId === a.assetId}
-                      >
-                        <MdDelete className="size-5" />
-                      </button>
-                    </td>
-                  </tr>
-                ))
-              )}
-            </tbody>
-          </table>
-        </div>
-
-        <div className="mt-4 flex justify-center">
-          {!isLoading && assets.length === 0 ? null : hasMore ? (
-            <button
-              type="button"
-              className="btn"
-              onClick={onLoadMore}
-              disabled={isLoadingMore}
-            >
-              {isLoadingMore ? "Loading…" : "Load more"}
-            </button>
-          ) : (
-            <span className="text-sm opacity-70">No more assets.</span>
-          )}
-        </div>
-      </div>
-    </div>
-  );
-}
Index: frontend/src/pages/Dashboard/pages/Investing/components/InvestingCenteredCard.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Investing/components/InvestingCenteredCard.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,16 +1,0 @@
-import React from "react";
-
-export default function InvestingCenteredCard({ title, message }) {
-  return (
-    <div className="min-h-[60vh] w-full flex items-center justify-center">
-      <div className="w-full max-w-2xl">
-        <div className="card bg-base-200 border border-base-300">
-          <div className="card-body">
-            <h1 className="text-2xl font-bold">{title}</h1>
-            <p className="opacity-80">{message}</p>
-          </div>
-        </div>
-      </div>
-    </div>
-  );
-}
Index: frontend/src/pages/Dashboard/pages/Investing/components/InvestingProgressCard.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Investing/components/InvestingProgressCard.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,204 +1,0 @@
-import React, { useMemo, useState } from "react";
-
-import TimeRangeToggle from "../../../../../components/graphs/TimeRangeToggle.jsx";
-import PercentChangeAreaChart from "../../../../../components/graphs/PercentChangeAreaChart.jsx";
-import { formatBucketLabel } from "../../../../../utils/timeSeries.js";
-
-function bucketKey(date, granularity) {
-  const d = new Date(date);
-  if (Number.isNaN(d.getTime())) return null;
-
-  function startOfDay(d) {
-    return new Date(d.getFullYear(), d.getMonth(), d.getDate());
-  }
-
-  function startOfISOWeek(d) {
-    const date = startOfDay(d);
-    const day = date.getDay();
-    const diff = (day === 0 ? -6 : 1) - day;
-    date.setDate(date.getDate() + diff);
-    return startOfDay(date);
-  }
-
-  function startOfMonth(d) {
-    return new Date(d.getFullYear(), d.getMonth(), 1);
-  }
-
-  function startOfYear(d) {
-    return new Date(d.getFullYear(), 0, 1);
-  }
-
-  let s;
-  switch (granularity) {
-    case "daily":
-      s = startOfDay(d);
-      break;
-    case "weekly":
-      s = startOfISOWeek(d);
-      break;
-    case "monthly":
-      s = startOfMonth(d);
-      break;
-    case "yearly":
-      s = startOfYear(d);
-      break;
-    default:
-      s = startOfDay(d);
-  }
-  return s.getTime();
-}
-
-export default function InvestingProgressCard({ assets, quotesBySymbol = {} }) {
-  const [range, setRange] = useState("weekly");
-
-  const points = useMemo(() => {
-    // Calculate portfolio percentage gain/loss over time using current prices
-    // Display cumulative portfolio value and percentage change between time buckets
-
-    if (!assets || assets.length === 0) {
-      return [];
-    }
-
-    // Group assets by buy date bucket
-    const buckets = new Map();
-    for (const asset of assets) {
-      if (asset?.buyDate) {
-        const ts = bucketKey(asset.buyDate, range);
-        if (ts !== null) {
-          if (!buckets.has(ts)) {
-            buckets.set(ts, []);
-          }
-          buckets.get(ts).push(asset);
-        }
-      }
-    }
-
-    // Sort buckets chronologically
-    const sortedBuckets = Array.from(buckets.entries()).sort((a, b) => a[0] - b[0]);
-
-    // Calculate cumulative portfolio value at each bucket
-    const points = [];
-    let cumulativeAssets = [];
-
-    for (const [ts, assetsInBucket] of sortedBuckets) {
-      cumulativeAssets = [...cumulativeAssets, ...assetsInBucket];
-
-      let totalInvested = 0;
-      let totalCurrentValue = 0;
-
-      for (const asset of cumulativeAssets) {
-        const qty = Number(asset?.quantity) || 0;
-        const buyPrice = Number(asset?.buyPrice) || 0;
-        const ticker = String(asset?.tickerSymbol || "").toUpperCase();
-        const currentPrice = quotesBySymbol[ticker] || buyPrice;
-
-        totalInvested += qty * buyPrice;
-        totalCurrentValue += qty * currentPrice;
-      }
-
-      if (totalInvested > 0) {
-        points.push({
-          ts,
-          invested: totalInvested,
-          current: totalCurrentValue,
-        });
-      }
-    }
-
-    // Calculate percentage gain from invested to current value
-    const result = [];
-    for (const p of points) {
-      const percentageGain = ((p.current - p.invested) / p.invested) * 100;
-      result.push({
-        ts: p.ts,
-        value: percentageGain,
-        base: p.current,
-        invested: p.invested,
-      });
-    }
-
-    return result;
-  }, [assets, range, quotesBySymbol]);
-
-  const latest = points?.length ? points[points.length - 1] : null;
-  const latestPct = latest ? Number(latest.value ?? 0) : 0;
-  const latestCurrentValue = latest ? Number(latest.base ?? 0) : 0;
-  const latestInvested = latest ? Number(latest.invested ?? 0) : 0;
-
-  const totalCurrentPortfolioValue = assets.reduce((sum, asset) => {
-    const qty = Number(asset?.quantity) || 0;
-    const ticker = String(asset?.tickerSymbol || "").toUpperCase();
-    const currentPrice = quotesBySymbol[ticker] || Number(asset?.buyPrice) || 0;
-    return sum + qty * currentPrice;
-  }, 0);
-
-  const totalInvestedAmount = assets.reduce((sum, asset) => {
-    const qty = Number(asset?.quantity) || 0;
-    const buyPrice = Number(asset?.buyPrice) || 0;
-    return sum + qty * buyPrice;
-  }, 0);
-
-  return (
-    <div className="card bg-base-200 border border-base-300">
-      <div className="card-body">
-        <div className="flex items-center justify-between gap-4">
-          <h2 className="card-title">Portfolio Value</h2>
-          <div className="flex items-center gap-3">
-            <div className="flex flex-col items-end gap-1">
-              <span className="text-sm font-semibold">
-                ${totalCurrentPortfolioValue.toFixed(2)}
-              </span>
-              <span
-                className={`text-xs font-bold ${
-                  latestPct >= 0 ? "text-success" : "text-error"
-                }`}
-              >
-                {latestPct >= 0 ? "+" : ""}
-                {latestPct.toFixed(2)}%
-              </span>
-            </div>
-            <TimeRangeToggle value={range} onChange={setRange} />
-          </div>
-        </div>
-
-        <div className="mt-4 w-full overflow-hidden rounded-xl border border-base-300 bg-base-100 p-2">
-          {points.length < 1 ? (
-            <div className="flex h-65 items-center justify-center text-sm opacity-70">
-              Add investments to see portfolio growth.
-            </div>
-          ) : (
-            <PercentChangeAreaChart
-              points={points}
-              granularity={range}
-              height={260}
-              positiveColor="#10b981"
-            />
-          )}
-        </div>
-
-        <div className="mt-3 grid grid-cols-3 gap-2 text-xs">
-          <div className="rounded bg-base-300 p-2">
-            <div className="opacity-70">Invested</div>
-            <div className="font-semibold">${totalInvestedAmount.toFixed(2)}</div>
-          </div>
-          <div className="rounded bg-base-300 p-2">
-            <div className="opacity-70">Current Value</div>
-            <div className="font-semibold">${totalCurrentPortfolioValue.toFixed(2)}</div>
-          </div>
-          <div className="rounded bg-base-300 p-2">
-            <div className="opacity-70">Gain/Loss</div>
-            <div
-              className={`font-semibold ${
-                totalCurrentPortfolioValue >= totalInvestedAmount
-                  ? "text-success"
-                  : "text-error"
-              }`}
-            >
-              ${(totalCurrentPortfolioValue - totalInvestedAmount).toFixed(2)}
-            </div>
-          </div>
-        </div>
-      </div>
-    </div>
-  );
-}
Index: frontend/src/pages/Dashboard/pages/Investing/components/InvestingStartCtaCard.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Investing/components/InvestingStartCtaCard.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,36 +1,0 @@
-import React from "react";
-
-export default function InvestingStartCtaCard({
-  error,
-  onStart,
-  isSubmitting,
-}) {
-  return (
-    <div className="card bg-base-200 border border-base-300">
-      <div className="card-body">
-        <h1 className="text-2xl font-bold">Investing</h1>
-        <p className="opacity-80">
-          You’re not tracking investments yet. Start tracking to log your assets
-          and visualize your progress.
-        </p>
-
-        {error ? (
-          <div className="alert alert-error mt-4">
-            <span>{error}</span>
-          </div>
-        ) : null}
-
-        <div className="mt-6">
-          <button
-            type="button"
-            className="btn btn-lg w-full bg-green-400! text-black! hover:bg-green-500! border-0"
-            onClick={onStart}
-            disabled={isSubmitting}
-          >
-            {isSubmitting ? "Starting…" : "Start Tracking Investments"}
-          </button>
-        </div>
-      </div>
-    </div>
-  );
-}
Index: frontend/src/pages/Dashboard/pages/Investing/components/InvestingTrackingHeader.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Investing/components/InvestingTrackingHeader.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,19 +1,0 @@
-import React from "react";
-
-export default function InvestingTrackingHeader({ onAddAsset }) {
-  return (
-    <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
-      <div>
-        <h1 className="text-2xl font-bold">Investing</h1>
-        <p className="opacity-80">Track your assets over time.</p>
-      </div>
-      <button
-        type="button"
-        className="btn bg-green-400! text-black! hover:bg-green-500! border-0"
-        onClick={onAddAsset}
-      >
-        + Add new investment
-      </button>
-    </div>
-  );
-}
Index: frontend/src/pages/Dashboard/pages/Training/NewTrainingSession.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Training/NewTrainingSession.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,264 +1,0 @@
-import React, { useEffect, useMemo, useState } from "react";
-import { useNavigate } from "react-router-dom";
-
-import api from "../../../../api/axios";
-
-import WorkoutTypeSelect from "./components/WorkoutTypeSelect.jsx";
-import CaloriesEstimate from "./components/CaloriesEstimate.jsx";
-
-const NewTrainingSession = () => {
-  const navigate = useNavigate();
-
-  const [workoutTypes, setWorkoutTypes] = useState([]);
-  const [profileWeightKg, setProfileWeightKg] = useState(null);
-  const [isLoading, setIsLoading] = useState(true);
-
-  const [type, setType] = useState("");
-  const [durationMinutes, setDurationMinutes] = useState("");
-  const [autoCalculateCalories, setAutoCalculateCalories] = useState(true);
-  const [calories, setCalories] = useState("");
-  const [date, setDate] = useState(() => new Date().toISOString().slice(0, 10));
-
-  const [error, setError] = useState("");
-  const [isSubmitting, setIsSubmitting] = useState(false);
-
-  useEffect(() => {
-    let cancelled = false;
-    (async () => {
-      try {
-        setIsLoading(true);
-        setError("");
-
-        const [typesRes, profileRes] = await Promise.all([
-          api.get("/training/workout-types"),
-          api.get("/training/profile"),
-        ]);
-
-        if (cancelled) return;
-
-        const types = Array.isArray(typesRes?.data) ? typesRes.data : [];
-        setWorkoutTypes(types);
-
-        const weight = profileRes?.data?.weight;
-        setProfileWeightKg(
-          weight === null || weight === undefined || weight === ""
-            ? null
-            : Number(weight),
-        );
-      } catch (err) {
-        if (cancelled) return;
-        setError(
-          err?.response?.data?.message ||
-            "Failed to load workout types/training profile.",
-        );
-      } finally {
-        if (!cancelled) setIsLoading(false);
-      }
-    })();
-    return () => {
-      cancelled = true;
-    };
-  }, []);
-
-  const selectedWorkout = useMemo(
-    () => workoutTypes.find((t) => t.type === type) ?? null,
-    [workoutTypes, type],
-  );
-
-  const estimatedCalories = useMemo(() => {
-    const met = selectedWorkout?.met;
-    const durationNum = durationMinutes === "" ? NaN : Number(durationMinutes);
-    const weightNum = profileWeightKg;
-    if (!Number.isFinite(Number(met))) return null;
-    if (!Number.isFinite(durationNum) || durationNum <= 0) return null;
-    if (!Number.isFinite(weightNum) || weightNum <= 0) return null;
-
-    const caloriesValue = Number(met) * weightNum * (durationNum / 60);
-    if (!Number.isFinite(caloriesValue)) return null;
-    return Math.round(caloriesValue * 100) / 100;
-  }, [durationMinutes, profileWeightKg, selectedWorkout?.met]);
-
-  const onSubmit = async (e) => {
-    e.preventDefault();
-    setError("");
-
-    if (isLoading) return;
-
-    if (!type) {
-      setError("Please select a workout type.");
-      return;
-    }
-
-    const durationNum = durationMinutes === "" ? NaN : Number(durationMinutes);
-    if (!Number.isFinite(durationNum) || durationNum < 1) {
-      setError("Duration must be at least 1 minute.");
-      return;
-    }
-
-    // Date must not be in the future (allow past or today)
-    const todayStr = new Date().toISOString().slice(0, 10);
-    if (!date || date > todayStr) {
-      setError("Date cannot be in the future.");
-      return;
-    }
-
-    const caloriesNum = calories === "" ? NaN : Number(calories);
-    if (!autoCalculateCalories) {
-      if (!Number.isFinite(caloriesNum) || caloriesNum < 0) {
-        setError("Calories must be 0 or greater.");
-        return;
-      }
-    }
-
-    try {
-      setIsSubmitting(true);
-      await api.post("/training/sessions", {
-        type,
-        durationMinutes: durationNum,
-        date,
-        autoCalculateCalories,
-        calories: autoCalculateCalories ? null : caloriesNum,
-      });
-
-      navigate("/dashboard/training/tracking", { replace: true });
-    } catch (err) {
-      setError(
-        err?.response?.data?.message || "Failed to add training session.",
-      );
-    } finally {
-      setIsSubmitting(false);
-    }
-  };
-
-  return (
-    <div className="min-h-[70vh] w-full flex items-center justify-center">
-      <form
-        onSubmit={onSubmit}
-        className="card bg-base-200 border border-base-300 w-full max-w-2xl"
-      >
-        <div className="card-body">
-          <div className="flex items-start justify-between gap-4">
-            <div>
-              <h1 className="text-2xl font-bold">Add training session</h1>
-              <p className="opacity-80 mt-1">
-                Choose a workout type, enter the duration, and we’ll save your
-                session.
-              </p>
-            </div>
-          </div>
-
-          {error ? <p className="text-error text-sm mt-2">{error}</p> : null}
-
-          {isLoading ? (
-            <p className="opacity-80 mt-2">Loading workout types…</p>
-          ) : null}
-
-          <div className="mt-4 grid grid-cols-1 gap-4 md:grid-cols-3">
-            <WorkoutTypeSelect
-              workoutTypes={workoutTypes}
-              value={type}
-              onChange={(e) => setType(e.target.value)}
-              disabled={isLoading}
-            />
-
-            <div>
-              <label className="label">Duration (minutes)</label>
-              <input
-                type="number"
-                className="input input-bordered w-full"
-                value={durationMinutes}
-                onChange={(e) => setDurationMinutes(e.target.value)}
-                min={1}
-                step={1}
-                required
-                disabled={isLoading}
-              />
-            </div>
-
-            <div>
-              <label className="label">Date</label>
-              <input
-                type="date"
-                className="input input-bordered w-full"
-                value={date}
-                onChange={(e) => setDate(e.target.value)}
-                max={new Date().toISOString().slice(0, 10)}
-                required
-                disabled={isLoading}
-              />
-              <p className="text-xs opacity-70 mt-1">Choose a past date or today (no future dates).</p>
-            </div>
-          </div>
-
-          <div className="mt-4">
-            <label className="label cursor-pointer justify-start gap-3">
-              <input
-                type="checkbox"
-                className="checkbox"
-                checked={autoCalculateCalories}
-                onChange={(e) => {
-                  const next = e.target.checked;
-                  setAutoCalculateCalories(next);
-                  if (
-                    !next &&
-                    (calories === "" || calories === null) &&
-                    estimatedCalories !== null
-                  ) {
-                    setCalories(String(estimatedCalories));
-                  }
-                }}
-              />
-              <span className="label-text">Auto-calculate calories burned</span>
-            </label>
-            <p className="text-xs opacity-70 mt-1">
-              Uses your training profile weight and the workout type.
-            </p>
-            <CaloriesEstimate
-              estimatedCalories={estimatedCalories}
-              profileWeightKg={profileWeightKg}
-            />
-          </div>
-
-          <div className="mt-2">
-            <label className="label">Calories (kcal)</label>
-            <input
-              type="number"
-              className="input input-bordered w-full"
-              value={calories}
-              onChange={(e) => setCalories(e.target.value)}
-              min={0}
-              step={1}
-              disabled={autoCalculateCalories}
-              placeholder={
-                autoCalculateCalories
-                  ? "Calculated automatically"
-                  : "Enter calories"
-              }
-              required={!autoCalculateCalories}
-            />
-          </div>
-
-          <div className="mt-6 flex flex-col gap-3 sm:flex-row sm:justify-end">
-            <button
-              type="button"
-              className="btn btn-ghost"
-              onClick={() => navigate("/dashboard/training/tracking")}
-              disabled={isSubmitting}
-            >
-              Cancel
-            </button>
-            <button
-              type="submit"
-              className="btn bg-green-400! text-black! hover:bg-green-500!"
-              disabled={isSubmitting || isLoading}
-            >
-              {isSubmitting ? "Saving…" : "Save session"}
-            </button>
-          </div>
-        </div>
-      </form>
-    </div>
-  );
-};
-
-export default NewTrainingSession;
Index: frontend/src/pages/Dashboard/pages/Training/Training.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Training/Training.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,114 +1,0 @@
-import React, { useEffect, useMemo, useState } from "react";
-import { useNavigate } from "react-router-dom";
-
-import api from "../../../../api/axios";
-
-import TrainingCenteredCard from "./components/TrainingCenteredCard.jsx";
-import TrainingStartCtaCard from "./components/TrainingStartCtaCard.jsx";
-import TrainingStartForm from "./components/TrainingStartForm.jsx";
-
-const Training = () => {
-  const navigate = useNavigate();
-
-  const [isLoading, setIsLoading] = useState(true);
-  const [isTracking, setIsTracking] = useState(false);
-  const [step, setStep] = useState("cta"); // cta | form
-  const [error, setError] = useState("");
-  const [isSubmitting, setIsSubmitting] = useState(false);
-
-  const initialForm = useMemo(() => ({ gender: "", age: "", weight: "" }), []);
-  const [form, setForm] = useState(initialForm);
-
-  useEffect(() => {
-    let isMounted = true;
-    const run = async () => {
-      setIsLoading(true);
-      setError("");
-      try {
-        const res = await api.get("/training/status");
-        const tracking = Boolean(res?.data?.tracking);
-        if (!isMounted) return;
-        setIsTracking(tracking);
-        if (tracking) {
-          navigate("/dashboard/training/tracking", { replace: true });
-        }
-      } catch (err) {
-        if (!isMounted) return;
-        const message =
-          err?.response?.data?.message || "Failed to load training status";
-        setError(message);
-      } finally {
-        if (isMounted) setIsLoading(false);
-      }
-    };
-
-    run();
-    return () => {
-      isMounted = false;
-    };
-  }, [navigate]);
-
-  const onStart = () => {
-    setError("");
-    setStep("form");
-  };
-
-  const onCancel = () => {
-    setStep("cta");
-    setForm(initialForm);
-    setError("");
-  };
-
-  const onSubmit = async (e) => {
-    e.preventDefault();
-    setError("");
-    setIsSubmitting(true);
-    try {
-      await api.post("/training/start", {
-        gender: form.gender,
-        age: form.age === "" ? null : Number(form.age),
-        weight: form.weight === "" ? null : Number(form.weight),
-      });
-
-      setIsTracking(true);
-      navigate("/dashboard/training/tracking", { replace: true });
-    } catch (err) {
-      const message =
-        err?.response?.data?.message ||
-        Object.values(err?.response?.data?.errors ?? {})[0] ||
-        "Failed to start tracking";
-      setError(message);
-    } finally {
-      setIsSubmitting(false);
-    }
-  };
-
-  if (isLoading) {
-    return <TrainingCenteredCard title="Training" message="Loading…" />;
-  }
-
-  if (isTracking) {
-    return <TrainingCenteredCard title="Training" message="Redirecting…" />;
-  }
-
-  return (
-    <div className="min-h-[70vh] w-full flex items-center justify-center">
-      <div className="w-full max-w-3xl">
-        {step === "cta" ? (
-          <TrainingStartCtaCard error={error} onStart={onStart} />
-        ) : (
-          <TrainingStartForm
-            form={form}
-            setForm={setForm}
-            onSubmit={onSubmit}
-            onCancel={onCancel}
-            error={error}
-            isSubmitting={isSubmitting}
-          />
-        )}
-      </div>
-    </div>
-  );
-};
-
-export default Training;
Index: frontend/src/pages/Dashboard/pages/Training/TrainingTracking.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Training/TrainingTracking.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,143 +1,0 @@
-import React, { useCallback, useEffect, useMemo, useState } from "react";
-import { useNavigate } from "react-router-dom";
-import api from "../../../../api/axios";
-
-import TrainingTrackingHeader from "./components/TrainingTrackingHeader.jsx";
-import TrainingProgressCard from "./components/TrainingProgressCard.jsx";
-import TrainingSessionsTable from "./components/TrainingSessionsTable.jsx";
-
-function formatDate(value) {
-  if (!value) return "—";
-  const d = new Date(value);
-  if (Number.isNaN(d.getTime())) return String(value);
-  return d.toLocaleDateString(undefined, {
-    year: "numeric",
-    month: "short",
-    day: "2-digit",
-  });
-}
-
-function titleCase(value) {
-  if (!value) return "—";
-  const s = String(value);
-  return s.charAt(0).toUpperCase() + s.slice(1);
-}
-
-export default function TrainingTracking() {
-  const navigate = useNavigate();
-  const pageSize = 5;
-  const graphPageSize = 500;
-
-  const [sessions, setSessions] = useState([]);
-  const [page, setPage] = useState(0);
-  const [hasMore, setHasMore] = useState(false);
-  const [isLoading, setIsLoading] = useState(true);
-  const [isLoadingMore, setIsLoadingMore] = useState(false);
-  const [error, setError] = useState("");
-
-  const columns = useMemo(
-    () => [
-      { key: "date", label: "Date" },
-      { key: "type", label: "Type" },
-      { key: "duration", label: "Duration (min)" },
-      { key: "calories", label: "Calories" },
-    ],
-    [],
-  );
-
-  const fetchPage = useCallback(async (nextPage, size = pageSize) => {
-    const resp = await api.get("/training/sessions", {
-      params: { page: nextPage, size },
-    });
-    const data = resp?.data ?? {};
-    const nextSessions = Array.isArray(data.sessions) ? data.sessions : [];
-    const nextHasMore = Boolean(data.hasMore);
-    return { nextSessions, nextHasMore };
-  }, []);
-
-  const fetchAllForGraph = useCallback(async () => {
-    let p = 0;
-    let hasMorePages = true;
-    const all = [];
-
-    while (hasMorePages) {
-      const { nextSessions, nextHasMore } = await fetchPage(p, graphPageSize);
-      all.push(...nextSessions);
-      hasMorePages = nextHasMore && nextSessions.length > 0;
-      p += 1;
-      // Basic safety to avoid accidental infinite loops if backend misbehaves.
-      if (p > 200) break;
-    }
-
-    return all;
-  }, [fetchPage]);
-
-  const [graphSessions, setGraphSessions] = useState([]);
-
-  useEffect(() => {
-    let cancelled = false;
-    (async () => {
-      try {
-        setIsLoading(true);
-        setError("");
-        const [tableFirstPage, allForGraph] = await Promise.all([
-          fetchPage(0, pageSize),
-          fetchAllForGraph(),
-        ]);
-        if (cancelled) return;
-        setSessions(tableFirstPage.nextSessions);
-        setHasMore(tableFirstPage.nextHasMore);
-        setGraphSessions(allForGraph);
-        setPage(0);
-      } catch (e) {
-        if (cancelled) return;
-        setError(
-          e?.response?.data?.message || "Failed to load training sessions.",
-        );
-      } finally {
-        if (!cancelled) setIsLoading(false);
-      }
-    })();
-    return () => {
-      cancelled = true;
-    };
-  }, [fetchAllForGraph, fetchPage]);
-
-  const onLoadMore = async () => {
-    if (isLoadingMore || !hasMore) return;
-    const nextPage = page + 1;
-    try {
-      setIsLoadingMore(true);
-      setError("");
-      const { nextSessions, nextHasMore } = await fetchPage(nextPage);
-      setSessions((prev) => [...prev, ...nextSessions]);
-      setHasMore(nextHasMore);
-      setPage(nextPage);
-    } catch (e) {
-      setError(e?.response?.data?.message || "Failed to load more sessions.");
-    } finally {
-      setIsLoadingMore(false);
-    }
-  };
-
-  return (
-    <div className="space-y-6">
-      <TrainingTrackingHeader
-        onAddSession={() => navigate("/dashboard/training/sessions/new")}
-      />
-      <TrainingProgressCard sessions={graphSessions} />
-      <TrainingSessionsTable
-        columns={columns}
-        sessions={sessions}
-        isLoading={isLoading}
-        error={error}
-        isLoadingMore={isLoadingMore}
-        hasMore={hasMore}
-        onLoadMore={onLoadMore}
-        pageSize={pageSize}
-        formatDate={formatDate}
-        titleCase={titleCase}
-      />
-    </div>
-  );
-}
Index: frontend/src/pages/Dashboard/pages/Training/components/CaloriesEstimate.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Training/components/CaloriesEstimate.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,21 +1,0 @@
-import React from "react";
-
-const CaloriesEstimate = ({ estimatedCalories, profileWeightKg }) => {
-  return (
-    <div className="mt-2">
-      {estimatedCalories === null ? (
-        <p className="text-xs opacity-70">
-          Estimated calories: —
-          {profileWeightKg === null ? " (missing profile weight)" : ""}
-        </p>
-      ) : (
-        <p className="text-xs opacity-80">
-          Estimated calories:{" "}
-          <span className="font-semibold">{estimatedCalories}</span> kcal
-        </p>
-      )}
-    </div>
-  );
-};
-
-export default CaloriesEstimate;
Index: frontend/src/pages/Dashboard/pages/Training/components/TrainingCenteredCard.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Training/components/TrainingCenteredCard.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,17 +1,0 @@
-import React from "react";
-
-const TrainingCenteredCard = ({ title, message, children }) => {
-  return (
-    <div className="min-h-[60vh] w-full flex items-center justify-center">
-      <div className="card bg-base-200 border border-base-300 w-full max-w-xl">
-        <div className="card-body items-center text-center">
-          {title ? <h1 className="text-3xl font-bold">{title}</h1> : null}
-          {message ? <p className="opacity-80">{message}</p> : null}
-          {children}
-        </div>
-      </div>
-    </div>
-  );
-};
-
-export default TrainingCenteredCard;
Index: frontend/src/pages/Dashboard/pages/Training/components/TrainingProgressCard.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Training/components/TrainingProgressCard.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,60 +1,0 @@
-import React, { useMemo, useState } from "react";
-
-import TimeRangeToggle from "../../../../../components/graphs/TimeRangeToggle.jsx";
-import PercentChangeAreaChart from "../../../../../components/graphs/PercentChangeAreaChart.jsx";
-import { percentChangeSeries, sumByTimeBucket } from "../../../../../utils/timeSeries.js";
-
-const TrainingProgressCard = ({ sessions }) => {
-  const [range, setRange] = useState("weekly");
-
-  const points = useMemo(() => {
-    const buckets = sumByTimeBucket(
-      sessions,
-      range,
-      (s) => s.date,
-      (s) => s.calories
-    );
-    return percentChangeSeries(buckets);
-  }, [range, sessions]);
-
-  const latest = points?.length ? points[points.length - 1] : null;
-  const latestPct = latest ? Number(latest.value ?? 0) : 0;
-  const latestBase = latest ? Number(latest.base ?? 0) : 0;
-
-  return (
-    <div className="card bg-base-200 border border-base-300">
-      <div className="card-body">
-        <div className="flex items-center justify-between gap-4">
-          <h2 className="card-title">Progress</h2>
-          <div className="flex items-center gap-3">
-            <span className="badge badge-ghost">
-              {latestPct >= 0 ? "+" : ""}
-              {latestPct.toFixed(1)}% (last)
-            </span>
-            <TimeRangeToggle value={range} onChange={setRange} />
-          </div>
-        </div>
-
-        <div className="mt-4 w-full overflow-hidden rounded-xl border border-base-300 bg-base-100 p-2">
-          {points.length < 2 ? (
-            <div className="flex h-65 items-center justify-center text-sm opacity-70">
-              Add at least 2 sessions to see % change.
-            </div>
-          ) : (
-            <PercentChangeAreaChart
-              points={points}
-              granularity={range}
-              height={260}
-            />
-          )}
-        </div>
-
-        <div className="mt-2 text-xs opacity-70">
-          Showing percentage change in calories burned per {range} bucket. Latest bucket total: {Math.round(latestBase)}.
-        </div>
-      </div>
-    </div>
-  );
-};
-
-export default TrainingProgressCard;
Index: frontend/src/pages/Dashboard/pages/Training/components/TrainingSessionsTable.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Training/components/TrainingSessionsTable.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,81 +1,0 @@
-import React from "react";
-
-const TrainingSessionsTable = ({
-  columns,
-  sessions,
-  isLoading,
-  error,
-  isLoadingMore,
-  hasMore,
-  onLoadMore,
-  pageSize,
-  formatDate,
-  titleCase,
-}) => {
-  return (
-    <div className="card bg-base-200 border border-base-300">
-      <div className="card-body">
-        <div className="flex items-center justify-between gap-4">
-          <h2 className="card-title">Recent sessions</h2>
-          <span className="badge badge-ghost">Showing up to {pageSize}</span>
-        </div>
-
-        {error ? <p className="text-error text-sm mt-2">{error}</p> : null}
-
-        <div className="mt-4 overflow-x-auto">
-          <table className="table table-zebra w-full">
-            <thead>
-              <tr>
-                {columns.map((c) => (
-                  <th key={c.key}>{c.label}</th>
-                ))}
-              </tr>
-            </thead>
-            <tbody>
-              {isLoading ? (
-                <tr>
-                  <td colSpan={columns.length}>
-                    <span className="opacity-80">Loading…</span>
-                  </td>
-                </tr>
-              ) : sessions.length === 0 ? (
-                <tr>
-                  <td colSpan={columns.length}>
-                    <span className="opacity-80">No sessions yet.</span>
-                  </td>
-                </tr>
-              ) : (
-                sessions.map((s) => (
-                  <tr
-                    key={
-                      s.trainingId ??
-                      `${s.date}-${s.type}-${s.duration}-${s.calories}`
-                    }
-                  >
-                    <td>{formatDate(s.date)}</td>
-                    <td>{titleCase(s.type)}</td>
-                    <td>{s.duration ?? "—"}</td>
-                    <td>{s.calories ?? "—"}</td>
-                  </tr>
-                ))
-              )}
-            </tbody>
-          </table>
-        </div>
-
-        <div className="mt-4 flex items-center justify-center">
-          <button
-            type="button"
-            className="btn btn-outline"
-            onClick={onLoadMore}
-            disabled={isLoading || isLoadingMore || !hasMore}
-          >
-            {isLoadingMore ? "Loading…" : hasMore ? "Load more" : "No more"}
-          </button>
-        </div>
-      </div>
-    </div>
-  );
-};
-
-export default TrainingSessionsTable;
Index: frontend/src/pages/Dashboard/pages/Training/components/TrainingStartCtaCard.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Training/components/TrainingStartCtaCard.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,26 +1,0 @@
-import React from "react";
-
-const TrainingStartCtaCard = ({ error, onStart }) => {
-  return (
-    <div className="card bg-base-200 border border-base-300">
-      <div className="card-body items-center text-center">
-        <h1 className="text-4xl font-bold">You are not tracking training</h1>
-        <p className="opacity-80 max-w-xl">
-          Start tracking to log sessions, see trends, and build consistency.
-        </p>
-
-        {error ? <p className="text-error text-sm mt-2">{error}</p> : null}
-
-        <button
-          type="button"
-          className="btn btn-lg mt-6 w-full max-w-md bg-green-400! text-black! hover:bg-green-500!"
-          onClick={onStart}
-        >
-          Start Tracking
-        </button>
-      </div>
-    </div>
-  );
-};
-
-export default TrainingStartCtaCard;
Index: frontend/src/pages/Dashboard/pages/Training/components/TrainingStartForm.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Training/components/TrainingStartForm.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,94 +1,0 @@
-import React from "react";
-
-const TrainingStartForm = ({
-  form,
-  setForm,
-  onSubmit,
-  onCancel,
-  error,
-  isSubmitting,
-}) => {
-  return (
-    <form
-      onSubmit={onSubmit}
-      className="card bg-base-200 border border-base-300"
-    >
-      <div className="card-body items-center text-center">
-        <h1 className="text-3xl font-bold">Start tracking training</h1>
-        <p className="opacity-80 max-w-xl">
-          Fill in a few details to set up your training profile.
-        </p>
-
-        {error ? <p className="text-error text-sm mt-2">{error}</p> : null}
-
-        <div className="mt-4 grid w-full grid-cols-1 gap-4 md:grid-cols-3">
-          <div>
-            <label className="label">Gender</label>
-            <select
-              className="select select-bordered w-full"
-              value={form.gender}
-              onChange={(e) =>
-                setForm((p) => ({ ...p, gender: e.target.value }))
-              }
-              required
-            >
-              <option value="" disabled>
-                Select…
-              </option>
-              <option value="male">Male</option>
-              <option value="female">Female</option>
-              <option value="other">Other</option>
-            </select>
-          </div>
-
-          <div>
-            <label className="label">Age</label>
-            <input
-              type="number"
-              className="input input-bordered w-full"
-              value={form.age}
-              onChange={(e) => setForm((p) => ({ ...p, age: e.target.value }))}
-              min={1}
-              max={120}
-              required
-            />
-          </div>
-
-          <div>
-            <label className="label">Weight (kg)</label>
-            <input
-              type="number"
-              className="input input-bordered w-full"
-              value={form.weight}
-              onChange={(e) =>
-                setForm((p) => ({ ...p, weight: e.target.value }))
-              }
-              min={1}
-              step="0.1"
-              required
-            />
-          </div>
-        </div>
-
-        <div className="mt-6 flex w-full flex-col items-center gap-3 sm:flex-row sm:justify-center">
-          <button
-            className="btn btn-lg w-full sm:w-auto bg-green-400! text-black! hover:bg-green-500!"
-            type="submit"
-            disabled={isSubmitting}
-          >
-            {isSubmitting ? "Starting…" : "Start Tracking"}
-          </button>
-          <button
-            type="button"
-            className="btn btn-ghost btn-lg w-full sm:w-auto"
-            onClick={onCancel}
-          >
-            Cancel
-          </button>
-        </div>
-      </div>
-    </form>
-  );
-};
-
-export default TrainingStartForm;
Index: frontend/src/pages/Dashboard/pages/Training/components/TrainingTrackingHeader.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Training/components/TrainingTrackingHeader.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,22 +1,0 @@
-import React from "react";
-
-const TrainingTrackingHeader = ({ onAddSession }) => {
-  return (
-    <div className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
-      <div>
-        <h1 className="text-2xl font-bold">Training</h1>
-        <p className="opacity-80 mt-1">Your recent sessions and progress.</p>
-      </div>
-
-      <button
-        type="button"
-        className="btn bg-green-400! text-black! hover:bg-green-500!"
-        onClick={onAddSession}
-      >
-        + Add new training session
-      </button>
-    </div>
-  );
-};
-
-export default TrainingTrackingHeader;
Index: frontend/src/pages/Dashboard/pages/Training/components/WorkoutTypeSelect.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Training/components/WorkoutTypeSelect.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,27 +1,0 @@
-import React from "react";
-
-const WorkoutTypeSelect = ({ workoutTypes, value, onChange, disabled }) => {
-  return (
-    <div>
-      <label className="label">Workout type</label>
-      <select
-        className="select select-bordered w-full"
-        value={value}
-        onChange={onChange}
-        required
-        disabled={disabled}
-      >
-        <option value="" disabled>
-          Select…
-        </option>
-        {workoutTypes.map((t) => (
-          <option key={t.type} value={t.type}>
-            {t.label ?? t.type}
-          </option>
-        ))}
-      </select>
-    </div>
-  );
-};
-
-export default WorkoutTypeSelect;
Index: frontend/src/pages/Dashboard/pages/Weight/NewWeightIntake.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Weight/NewWeightIntake.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,166 +1,0 @@
-import React, { useEffect, useMemo, useState } from "react";
-import { useNavigate } from "react-router-dom";
-
-import api from "../../../../api/axios";
-
-export default function NewWeightIntake() {
-  const navigate = useNavigate();
-  const [goalCalories, setGoalCalories] = useState(null);
-  const [todayTrainingInfo, setTodayTrainingInfo] = useState(null);
-  const [isLoading, setIsLoading] = useState(true);
-  const [calories, setCalories] = useState("");
-  const [error, setError] = useState("");
-  const [isSubmitting, setIsSubmitting] = useState(false);
-
-  const todayLabel = useMemo(
-    () =>
-      new Date().toLocaleDateString(undefined, {
-        year: "numeric",
-        month: "short",
-        day: "2-digit",
-      }),
-    [],
-  );
-
-  const adjustedGoalCalories = useMemo(() => {
-    if (!goalCalories || !todayTrainingInfo) return null;
-    const goal = Number(goalCalories);
-    const burned = Number(todayTrainingInfo.totalBurnedCalories) || 0;
-    if (!Number.isFinite(goal)) return null;
-    return goal + burned;
-  }, [goalCalories, todayTrainingInfo]);
-
-  useEffect(() => {
-    let cancelled = false;
-    (async () => {
-      try {
-        setIsLoading(true);
-        setError("");
-        const [statusRes, profileRes, trainingRes] = await Promise.all([
-          api.get("/weight/status"),
-          api.get("/weight/profile"),
-          api.get("/weight/today-training"),
-        ]);
-        if (cancelled) return;
-        if (!statusRes?.data?.tracking) {
-          navigate("/dashboard/weight", { replace: true });
-          return;
-        }
-        setGoalCalories(profileRes?.data?.goalCalories ?? null);
-        setTodayTrainingInfo(trainingRes?.data ?? null);
-      } catch (err) {
-        if (cancelled) return;
-        const message = err?.response?.data?.message || "Failed to load weight profile.";
-        if (String(message).toLowerCase().includes("not enabled")) {
-          navigate("/dashboard/weight", { replace: true });
-          return;
-        }
-        setError(message);
-      } finally {
-        if (!cancelled) setIsLoading(false);
-      }
-    })();
-    return () => {
-      cancelled = true;
-    };
-  }, [navigate]);
-
-  const onSubmit = async (e) => {
-    e.preventDefault();
-    setError("");
-
-    const caloriesNum = calories === "" ? NaN : Number(calories);
-    if (!Number.isFinite(caloriesNum) || caloriesNum < 0) {
-      setError("Calories must be 0 or greater.");
-      return;
-    }
-
-    try {
-      setIsSubmitting(true);
-      await api.post("/weight/intakes", {
-        calories: caloriesNum,
-      });
-      navigate("/dashboard/weight/tracking", { replace: true });
-    } catch (err) {
-      setError(err?.response?.data?.message || "Failed to add today's intake.");
-    } finally {
-      setIsSubmitting(false);
-    }
-  };
-
-  return (
-    <div className="min-h-[70vh] w-full flex items-center justify-center">
-      <div className="w-full max-w-2xl">
-        <div className="card bg-base-200 border border-base-300">
-          <div className="card-body">
-            <h1 className="text-2xl font-bold">Add today's intake</h1>
-            <p className="opacity-80">
-              Log the calories you consumed today. Only one intake can be stored per day.
-            </p>
-
-            <div className="mt-2 text-sm opacity-70">Today: {todayLabel}</div>
-            <div className="mt-1 text-sm opacity-70">
-              Goal calories: {goalCalories === null ? "—" : `${goalCalories} kcal`}
-            </div>
-            {todayTrainingInfo?.trainedToday && (
-              <>
-                <div className="mt-2 text-sm opacity-70">
-                  Burned during training: {todayTrainingInfo.totalBurnedCalories} kcal
-                </div>
-                <div className="mt-1 text-sm font-semibold text-green-400">
-                  Adjusted goal for today: {adjustedGoalCalories === null ? "—" : `${adjustedGoalCalories} kcal`}
-                </div>
-              </>
-            )}
-
-            {error ? (
-              <div className="alert alert-error mt-4">
-                <span>{error}</span>
-              </div>
-            ) : null}
-
-            {isLoading ? <p className="opacity-80 mt-4">Loading…</p> : null}
-
-            <form onSubmit={onSubmit} className="mt-6 space-y-4">
-              <div>
-                <label className="label">
-                  <span className="label-text">Calories consumed</span>
-                </label>
-                <input
-                  type="number"
-                  step="0.1"
-                  min="0"
-                  className="input input-bordered w-full"
-                  placeholder="2200"
-                  value={calories}
-                  onChange={(e) => setCalories(e.target.value)}
-                  required
-                  disabled={isLoading}
-                />
-              </div>
-
-              <div className="flex flex-col gap-3 sm:flex-row sm:justify-end">
-                <button
-                  type="button"
-                  className="btn btn-ghost"
-                  onClick={() => navigate("/dashboard/weight/tracking")}
-                  disabled={isSubmitting}
-                >
-                  Cancel
-                </button>
-                <button
-                  type="submit"
-                  className="btn bg-green-400! text-black! hover:bg-green-500! border-0"
-                  disabled={isSubmitting || isLoading}
-                >
-                  {isSubmitting ? "Saving…" : "Save intake"}
-                </button>
-              </div>
-            </form>
-          </div>
-        </div>
-      </div>
-    </div>
-  );
-}
-
Index: frontend/src/pages/Dashboard/pages/Weight/Weight.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Weight/Weight.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,168 +1,0 @@
-import React, { useEffect, useMemo, useState } from "react";
-import { useNavigate } from "react-router-dom";
-
-import api from "../../../../api/axios";
-
-import WeightCenteredCard from "./components/WeightCenteredCard.jsx";
-import WeightStartCtaCard from "./components/WeightStartCtaCard.jsx";
-import WeightStartForm from "./components/WeightStartForm.jsx";
-
-function estimateGoalTimeWeeks(currentWeight, goalWeight) {
-  const current = Number(currentWeight);
-  const goal = Number(goalWeight);
-  if (!Number.isFinite(current) || !Number.isFinite(goal) || current <= 0 || goal <= 0) {
-    return null;
-  }
-  const difference = Math.abs(goal - current);
-  return Math.round((difference / 0.5) * 100) / 100;
-}
-
-function estimateGoalCalories(currentWeight, height, goalWeight) {
-  const current = Number(currentWeight);
-  const h = Number(height);
-  const goal = Number(goalWeight);
-  if (!Number.isFinite(current) || !Number.isFinite(h) || !Number.isFinite(goal)) {
-    return null;
-  }
-
-  const maintenance = current * 10 + h * 6.25 + 50;
-  if (goal < current) return Math.max(0, Math.round((maintenance - 500) * 100) / 100);
-  if (goal > current) return Math.round((maintenance + 300) * 100) / 100;
-  return Math.round(maintenance * 100) / 100;
-}
-
-const Weight = () => {
-  const navigate = useNavigate();
-
-  const [isLoading, setIsLoading] = useState(true);
-  const [isTracking, setIsTracking] = useState(false);
-  const [step, setStep] = useState("cta");
-  const [error, setError] = useState("");
-  const [isSubmitting, setIsSubmitting] = useState(false);
-  const [autoCalculateTargets, setAutoCalculateTargets] = useState(true);
-
-  const initialForm = useMemo(
-    () => ({
-      weight: "",
-      height: "",
-      goalWeight: "",
-      goalTimeWeeks: "",
-      goalCalories: "",
-    }),
-    [],
-  );
-  const [form, setForm] = useState(initialForm);
-
-  const estimatedGoalTimeWeeks = useMemo(
-    () => estimateGoalTimeWeeks(form.weight, form.goalWeight),
-    [form.weight, form.goalWeight],
-  );
-  const estimatedGoalCalories = useMemo(
-    () => estimateGoalCalories(form.weight, form.height, form.goalWeight),
-    [form.weight, form.height, form.goalWeight],
-  );
-
-  useEffect(() => {
-    let isMounted = true;
-    const run = async () => {
-      setIsLoading(true);
-      setError("");
-      try {
-        const res = await api.get("/weight/status");
-        const tracking = Boolean(res?.data?.tracking);
-        if (!isMounted) return;
-        setIsTracking(tracking);
-        if (tracking) {
-          navigate("/dashboard/weight/tracking", { replace: true });
-        }
-      } catch (err) {
-        if (!isMounted) return;
-        const message = err?.response?.data?.message || "Failed to load weight status";
-        setError(message);
-      } finally {
-        if (isMounted) setIsLoading(false);
-      }
-    };
-
-    run();
-    return () => {
-      isMounted = false;
-    };
-  }, [navigate]);
-
-  const onStart = () => {
-    setError("");
-    setStep("form");
-  };
-
-  const onCancel = () => {
-    setStep("cta");
-    setForm(initialForm);
-    setAutoCalculateTargets(true);
-    setError("");
-  };
-
-  const onSubmit = async (e) => {
-    e.preventDefault();
-    setError("");
-    setIsSubmitting(true);
-
-    try {
-      await api.post("/weight/start", {
-        weight: form.weight === "" ? null : Number(form.weight),
-        height: form.height === "" ? null : Number(form.height),
-        goalWeight: form.goalWeight === "" ? null : Number(form.goalWeight),
-        goalCalories: autoCalculateTargets
-          ? null
-          : form.goalCalories === ""
-            ? null
-            : Number(form.goalCalories),
-        autoCalculateTargets,
-      });
-
-      setIsTracking(true);
-      navigate("/dashboard/weight/tracking", { replace: true });
-    } catch (err) {
-      const message =
-        err?.response?.data?.message ||
-        Object.values(err?.response?.data?.errors ?? {})[0] ||
-        "Failed to start weight tracking";
-      setError(message);
-    } finally {
-      setIsSubmitting(false);
-    }
-  };
-
-  if (isLoading) {
-    return <WeightCenteredCard title="Weight" message="Loading…" />;
-  }
-
-  if (isTracking) {
-    return <WeightCenteredCard title="Weight" message="Redirecting…" />;
-  }
-
-  return (
-    <div className="min-h-[70vh] w-full flex items-center justify-center">
-      <div className="w-full max-w-4xl">
-        {step === "cta" ? (
-          <WeightStartCtaCard error={error} onStart={onStart} isSubmitting={isSubmitting} />
-        ) : (
-          <WeightStartForm
-            form={form}
-            setForm={setForm}
-            onSubmit={onSubmit}
-            onCancel={onCancel}
-            error={error}
-            isSubmitting={isSubmitting}
-            autoCalculateTargets={autoCalculateTargets}
-            setAutoCalculateTargets={setAutoCalculateTargets}
-            estimatedGoalTimeWeeks={estimatedGoalTimeWeeks}
-            estimatedGoalCalories={estimatedGoalCalories}
-          />
-        )}
-      </div>
-    </div>
-  );
-};
-
-export default Weight;
Index: frontend/src/pages/Dashboard/pages/Weight/WeightTracking.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Weight/WeightTracking.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,263 +1,0 @@
-import React, { useCallback, useEffect, useMemo, useState } from "react";
-import { useNavigate } from "react-router-dom";
-
-import api from "../../../../api/axios";
-
-import WeightProgressCard from "./components/WeightProgressCard.jsx";
-import WeightTrackingHeader from "./components/WeightTrackingHeader.jsx";
-import WeightSummaryCard from "./components/WeightSummaryCard.jsx";
-import WeightIntakesTable from "./components/WeightIntakesTable.jsx";
-import WeightStartForm from "./components/WeightStartForm.jsx";
-
-function estimateGoalTimeWeeks(currentWeight, goalWeight) {
-  const current = Number(currentWeight);
-  const goal = Number(goalWeight);
-  if (!Number.isFinite(current) || !Number.isFinite(goal) || current <= 0 || goal <= 0) {
-    return null;
-  }
-  const difference = Math.abs(goal - current);
-  return Math.round((difference / 0.5) * 100) / 100;
-}
-
-function estimateGoalCalories(currentWeight, height, goalWeight) {
-  const current = Number(currentWeight);
-  const h = Number(height);
-  const goal = Number(goalWeight);
-  if (!Number.isFinite(current) || !Number.isFinite(h) || !Number.isFinite(goal)) {
-    return null;
-  }
-  const maintenance = current * 10 + h * 6.25 + 50;
-  if (goal < current) return Math.max(0, Math.round((maintenance - 500) * 100) / 100);
-  if (goal > current) return Math.round((maintenance + 300) * 100) / 100;
-  return Math.round(maintenance * 100) / 100;
-}
-
-export default function WeightTracking() {
-  const navigate = useNavigate();
-  const pageSize = 5;
-  const graphPageSize = 500;
-
-  const [profile, setProfile] = useState(null);
-  const [intakes, setIntakes] = useState([]);
-  const [page, setPage] = useState(0);
-  const [hasMore, setHasMore] = useState(false);
-  const [hasTodayIntake, setHasTodayIntake] = useState(false);
-  const [todayTrainingInfo, setTodayTrainingInfo] = useState(null);
-  const [isLoading, setIsLoading] = useState(true);
-  const [isLoadingMore, setIsLoadingMore] = useState(false);
-  const [error, setError] = useState("");
-  const [isEditingProfile, setIsEditingProfile] = useState(false);
-  const [editForm, setEditForm] = useState({ weight: "", height: "", goalWeight: "", goalCalories: "" });
-  const [editAutoCalculate, setEditAutoCalculate] = useState(true);
-  const [editError, setEditError] = useState("");
-  const [isSubmittingEdit, setIsSubmittingEdit] = useState(false);
-
-  const fetchPage = useCallback(async (nextPage, size = pageSize) => {
-    const [profileRes, intakesRes, trainingRes] = await Promise.all([
-      api.get("/weight/profile"),
-      api.get("/weight/intakes", {
-        params: { page: nextPage, size },
-      }),
-      api.get("/weight/today-training"),
-    ]);
-
-    return {
-      profileData: profileRes?.data ?? null,
-      intakesData: intakesRes?.data ?? {},
-      trainingData: trainingRes?.data ?? null,
-    };
-  }, []);
-
-  const [graphIntakes, setGraphIntakes] = useState([]);
-
-  const fetchAllIntakesForGraph = useCallback(async () => {
-    let p = 0;
-    let hasMorePages = true;
-    const all = [];
-
-    while (hasMorePages) {
-      const { intakesData } = await fetchPage(p, graphPageSize);
-      const chunk = Array.isArray(intakesData.intakes) ? intakesData.intakes : [];
-      all.push(...chunk);
-      hasMorePages = Boolean(intakesData.hasMore) && chunk.length > 0;
-      p += 1;
-      if (p > 200) break;
-    }
-
-    return all;
-  }, [fetchPage]);
-
-  useEffect(() => {
-    let cancelled = false;
-    (async () => {
-      try {
-        setIsLoading(true);
-        setError("");
-        const [{ profileData, intakesData, trainingData }, allIntakes] = await Promise.all([
-          fetchPage(0, pageSize),
-          fetchAllIntakesForGraph(),
-        ]);
-        if (cancelled) return;
-        setProfile(profileData);
-        setIntakes(Array.isArray(intakesData.intakes) ? intakesData.intakes : []);
-        setGraphIntakes(allIntakes);
-        setHasMore(Boolean(intakesData.hasMore));
-        setHasTodayIntake(Boolean(intakesData.hasTodayIntake));
-        setTodayTrainingInfo(trainingData);
-        setPage(0);
-      } catch (e) {
-        if (cancelled) return;
-        const message =
-          e?.response?.data?.message || "Failed to load weight tracking data.";
-        if (String(message).toLowerCase().includes("not enabled")) {
-          navigate("/dashboard/weight", { replace: true });
-          return;
-        }
-        setError(message);
-      } finally {
-        if (!cancelled) setIsLoading(false);
-      }
-    })();
-
-    return () => {
-      cancelled = true;
-    };
-  }, [fetchAllIntakesForGraph, fetchPage, navigate]);
-
-  const onLoadMore = async () => {
-    if (isLoadingMore || !hasMore) return;
-    const nextPage = page + 1;
-    try {
-      setIsLoadingMore(true);
-      setError("");
-      const { intakesData } = await fetchPage(nextPage);
-      setIntakes((prev) => [...prev, ...(Array.isArray(intakesData.intakes) ? intakesData.intakes : [])]);
-      setHasMore(Boolean(intakesData.hasMore));
-      setHasTodayIntake(Boolean(intakesData.hasTodayIntake));
-      setPage(nextPage);
-    } catch (e) {
-      setError(e?.response?.data?.message || "Failed to load more intakes.");
-    } finally {
-      setIsLoadingMore(false);
-    }
-  };
-
-  const goalCalories = useMemo(() => profile?.goalCalories ?? null, [profile]);
-
-  const editEstimatedGoalCalories = useMemo(
-    () => estimateGoalCalories(editForm.weight, editForm.height, editForm.goalWeight),
-    [editForm.weight, editForm.height, editForm.goalWeight],
-  );
-
-  if (isLoading && !profile) {
-    return <div className="opacity-80">Loading…</div>;
-  }
-
-  const onOpenEditProfile = () => {
-    setEditForm({
-      weight: String(profile?.weight ?? ""),
-      height: String(profile?.height ?? ""),
-      goalWeight: String(profile?.goalWeight ?? ""),
-      goalCalories: String(profile?.goalCalories ?? ""),
-    });
-    setEditAutoCalculate(false);
-    setEditError("");
-    setIsEditingProfile(true);
-  };
-
-  const onCancelEdit = () => {
-    setIsEditingProfile(false);
-    setEditForm({ weight: "", height: "", goalWeight: "", goalCalories: "" });
-    setEditError("");
-  };
-
-  const onSubmitEdit = async (e) => {
-    e.preventDefault();
-    setEditError("");
-    setIsSubmittingEdit(true);
-    try {
-      const res = await api.put("/weight/profile", {
-        weight: editForm.weight === "" ? null : Number(editForm.weight),
-        height: editForm.height === "" ? null : Number(editForm.height),
-        goalWeight: editForm.goalWeight === "" ? null : Number(editForm.goalWeight),
-        goalCalories: editForm.goalCalories === "" ? null : Number(editForm.goalCalories),
-        autoCalculateTargets: editAutoCalculate,
-      });
-      setProfile(res.data);
-      setIsEditingProfile(false);
-    } catch (err) {
-      setEditError(
-        err?.response?.data?.message ||
-        Object.values(err?.response?.data?.errors ?? {})[0] ||
-        "Failed to update profile"
-      );
-    } finally {
-      setIsSubmittingEdit(false);
-    }
-  };
-
-  return (
-    <div className="space-y-6">
-      <WeightSummaryCard 
-        profile={profile} 
-        onEdit={onOpenEditProfile}
-        todayTrainingInfo={todayTrainingInfo}
-      />
-
-      {isEditingProfile ? (
-        <div className="modal modal-open" role="dialog" aria-modal="true">
-          <div className="modal-box max-w-2xl">
-            <WeightStartForm
-              form={editForm}
-              setForm={setEditForm}
-              onSubmit={onSubmitEdit}
-              onCancel={onCancelEdit}
-              error={editError}
-              isSubmitting={isSubmittingEdit}
-              autoCalculateTargets={editAutoCalculate}
-              setAutoCalculateTargets={setEditAutoCalculate}
-              estimatedGoalTimeWeeks={estimateGoalTimeWeeks(editForm.weight, editForm.goalWeight)}
-              estimatedGoalCalories={editEstimatedGoalCalories}
-            />
-            <button
-              type="button"
-              className="modal-backdrop"
-              aria-label="Close"
-              onClick={onCancelEdit}
-              disabled={isSubmittingEdit}
-            />
-          </div>
-        </div>
-      ) : null}
-
-      <WeightTrackingHeader
-        onAddIntake={() => navigate("/dashboard/weight/intakes/new")}
-        isTodayLogged={hasTodayIntake}
-      />
-
-      <WeightProgressCard
-        intakes={graphIntakes}
-        goalCalories={goalCalories}
-        isBulking={
-          Number.isFinite(Number(profile?.weight)) && Number.isFinite(Number(profile?.goalWeight))
-            ? Number(profile.goalWeight) > Number(profile.weight)
-            : null
-        }
-      />
-
-      <WeightIntakesTable
-        intakes={intakes}
-        isLoading={isLoading}
-        error={error}
-        isLoadingMore={isLoadingMore}
-        hasMore={hasMore}
-        onLoadMore={onLoadMore}
-        pageSize={pageSize}
-        goalCalories={goalCalories}
-        onAddIntake={() => navigate("/dashboard/weight/intakes/new")}
-        currentWeight={profile?.weight}
-        goalWeight={profile?.goalWeight}
-      />
-    </div>
-  );
-}
Index: frontend/src/pages/Dashboard/pages/Weight/components/WeightCenteredCard.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Weight/components/WeightCenteredCard.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,15 +1,0 @@
-import React from "react";
-
-export default function WeightCenteredCard({ title, message }) {
-  return (
-    <div className="min-h-[70vh] w-full flex items-center justify-center">
-      <div className="card bg-base-200 border border-base-300 w-full max-w-2xl">
-        <div className="card-body items-center text-center">
-          <h1 className="text-3xl font-bold">{title}</h1>
-          <p className="opacity-80">{message}</p>
-        </div>
-      </div>
-    </div>
-  );
-}
-
Index: frontend/src/pages/Dashboard/pages/Weight/components/WeightIntakesTable.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Weight/components/WeightIntakesTable.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,152 +1,0 @@
-import React, { useMemo } from "react";
-
-function formatDate(value) {
-  if (!value) return "—";
-  const d = new Date(value);
-  if (Number.isNaN(d.getTime())) return String(value);
-  return d.toLocaleDateString(undefined, {
-    year: "numeric",
-    month: "short",
-    day: "2-digit",
-  });
-}
-
-function formatNumber(value) {
-  if (value === null || value === undefined || value === "") return "—";
-  const num = Number(value);
-  if (Number.isNaN(num)) return String(value);
-  return Number.isInteger(num) ? String(num) : num.toFixed(2);
-}
-
-export default function WeightIntakesTable({
-  intakes,
-  isLoading,
-  error,
-  isLoadingMore,
-  hasMore,
-  onLoadMore,
-  pageSize,
-  goalCalories,
-  onAddIntake,
-  currentWeight,
-  goalWeight,
-}) {
-  const columns = useMemo(
-    () => [
-      { key: "date", label: "Date" },
-      { key: "calories", label: "Calories" },
-      { key: "delta", label: "Vs goal" },
-    ],
-    [],
-  );
-
-  // Determine if user is bulking (goal > current) or cutting (goal < current)
-  const isBulking = useMemo(() => {
-    const current = Number(currentWeight);
-    const goal = Number(goalWeight);
-    if (!Number.isFinite(current) || !Number.isFinite(goal)) {
-      return null; // Can't determine
-    }
-    return goal > current;
-  }, [currentWeight, goalWeight]);
-
-  return (
-    <div className="card bg-base-200 border border-base-300">
-      <div className="card-body">
-        <div className="flex items-center justify-between gap-4">
-          <h2 className="text-lg font-semibold">Daily intakes</h2>
-          <span className="text-sm opacity-70">Showing {pageSize} per page</span>
-        </div>
-
-        {error ? (
-          <div className="alert alert-error mt-4">
-            <span>{error}</span>
-          </div>
-        ) : null}
-
-        <div className="mt-4 overflow-x-auto">
-          <table className="table">
-            <thead>
-              <tr>
-                {columns.map((c) => (
-                  <th key={c.key}>{c.label}</th>
-                ))}
-              </tr>
-            </thead>
-            <tbody>
-              {isLoading ? (
-                <tr>
-                  <td colSpan={columns.length} className="opacity-70">
-                    Loading…
-                  </td>
-                </tr>
-              ) : intakes.length === 0 ? (
-                <tr>
-                  <td colSpan={columns.length} className="py-10">
-                    <div className="flex flex-col items-center text-center gap-3">
-                      <p className="opacity-80">No daily intakes yet.</p>
-                      <button
-                        type="button"
-                        className="btn bg-green-400! text-black! hover:bg-green-500! border-0"
-                        onClick={onAddIntake}
-                      >
-                        Add your first daily intake
-                      </button>
-                    </div>
-                  </td>
-                </tr>
-              ) : (
-                intakes.map((intake) => {
-                  const calories = Number(intake?.calories);
-                  const goal = Number(goalCalories);
-                  const burned = Number(intake?.burnedCalories) || 0;
-                  // If user trained that day, add burned calories to goal
-                  const adjustedGoal = intake?.trainedThatDay ? goal + burned : goal;
-                  const hasGoal = Number.isFinite(adjustedGoal);
-                  const delta = hasGoal && Number.isFinite(calories) ? calories - adjustedGoal : null;
-                  const deltaLabel =
-                    delta === null
-                      ? "—"
-                      : `${delta > 0 ? "+" : ""}${formatNumber(delta)} kcal`;
-                  
-                  // Determine color based on bulking vs cutting
-                  let deltaClass = "opacity-80";
-                  if (delta !== null) {
-                    if (isBulking === true) {
-                      // Bulking: eating MORE is good (green), eating LESS is bad (red)
-                      deltaClass = delta > 0 ? "text-green-400 font-semibold" : delta < 0 ? "text-red-400 font-semibold" : "opacity-80";
-                    } else if (isBulking === false) {
-                      // Cutting: eating LESS is good (green), eating MORE is bad (red)
-                      deltaClass = delta > 0 ? "text-red-400 font-semibold" : delta < 0 ? "text-green-400 font-semibold" : "opacity-80";
-                    }
-                  }
-
-                  return (
-                    <tr key={intake.dailyIntakeId ?? `${intake.date}-${intake.calories}`}>
-                      <td>{formatDate(intake.date)}</td>
-                      <td>{formatNumber(intake.calories)} kcal</td>
-                      <td className={deltaClass}>{deltaLabel}</td>
-                    </tr>
-                  );
-                })
-              )}
-            </tbody>
-          </table>
-        </div>
-
-        <div className="mt-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
-
-          <button
-            type="button"
-            className="btn btn-outline"
-            onClick={onLoadMore}
-            disabled={isLoading || isLoadingMore || !hasMore}
-          >
-            {isLoadingMore ? "Loading…" : hasMore ? "Load more" : "No more"}
-          </button>
-        </div>
-      </div>
-    </div>
-  );
-}
-
Index: frontend/src/pages/Dashboard/pages/Weight/components/WeightProgressCard.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Weight/components/WeightProgressCard.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,114 +1,0 @@
-import React, { useMemo, useState } from "react";
-
-import TimeRangeToggle from "../../../../../components/graphs/TimeRangeToggle.jsx";
-import PercentChangeAreaChart from "../../../../../components/graphs/PercentChangeAreaChart.jsx";
-import { percentChangeSeries, sumByTimeBucket } from "../../../../../utils/timeSeries.js";
-
-function clamp(n, min, max) {
-  const x = Number(n);
-  if (!Number.isFinite(x)) return min;
-  return Math.min(max, Math.max(min, x));
-}
-
-export default function WeightProgressCard({ intakes, goalCalories, isBulking }) {
-  const [range, setRange] = useState("weekly");
-
-
-  const points = useMemo(() => {
-    // For a bucket (week/month/year) we aggregate:
-    // - total calories
-    // - total adjusted goal
-    // then compute bucketCloseness% from those totals.
-    const totals = sumByTimeBucket(
-      intakes,
-      range,
-      (i) => i.date,
-      (i) => Number(i?.calories) || 0,
-    );
-
-    const goalTotals = sumByTimeBucket(
-      intakes,
-      range,
-      (i) => i.date,
-      (i) => {
-        const goal = Number(goalCalories);
-        if (!Number.isFinite(goal) || goal <= 0) return 0;
-        const burned = Number(i?.burnedCalories) || 0;
-        const trained = Boolean(i?.trainedThatDay);
-        return trained ? goal + burned : goal;
-      },
-    );
-
-    const goalByTs = new Map(goalTotals.map((p) => [p.ts, p.value]));
-
-    const closenessPoints = totals.map((p) => {
-      const adjustedGoal = Number(goalByTs.get(p.ts) ?? 0);
-      const calories = Number(p.value ?? 0);
-
-      if (!Number.isFinite(adjustedGoal) || adjustedGoal <= 0) {
-        return { ts: p.ts, value: 0 };
-      }
-
-      const pct = 100 - (Math.abs(calories - adjustedGoal) / adjustedGoal) * 100;
-      return { ts: p.ts, value: clamp(pct, 0, 200) };
-    });
-
-    const pctChange = percentChangeSeries(closenessPoints);
-    const adjusted =
-      isBulking === false
-        ? // Cutting: improvements should read as "positive" when the user stays under goal.
-          // Flip the sign so the line direction matches the user's intent.
-          pctChange.map((p) => ({ ...p, value: -Number(p.value ?? 0) }))
-        : pctChange;
-
-    // Reverse the graph line direction (invert Y) as requested.
-    return adjusted.map((p) => ({ ...p, value: -Number(p.value ?? 0) }));
-  }, [intakes, range, goalCalories, isBulking]);
-
-  const latest = points?.length ? points[points.length - 1] : null;
-  const latestPct = latest ? Number(latest.value ?? 0) : 0;
-  const latestBase = latest ? Number(latest.base ?? 0) : 0;
-
-  return (
-    <div className="card bg-base-200 border border-base-300">
-      <div className="card-body">
-        <div className="flex items-center justify-between gap-4">
-          <h2 className="card-title">Progress</h2>
-          <div className="flex items-center gap-3">
-            <span className="badge badge-ghost">
-              {latestPct >= 0 ? "+" : ""}
-              {latestPct.toFixed(1)}% (last)
-            </span>
-            <TimeRangeToggle value={range} onChange={setRange} />
-          </div>
-        </div>
-
-        <div className="mt-4 w-full overflow-hidden rounded-xl border border-base-300 bg-base-100 p-2">
-          {points.length < 2 ? (
-            <div className="flex h-65 items-center justify-center text-sm opacity-70">
-              Add at least 2 daily intakes to see % change.
-            </div>
-          ) : (
-            <PercentChangeAreaChart
-              points={points}
-              granularity={range}
-              height={260}
-              positiveColor="#60a5fa"
-            />
-          )}
-        </div>
-
-        <div className="mt-2 text-xs opacity-70">
-          Showing percentage change in goal-accuracy (closeness %) per {range} bucket.
-          {isBulking === null ? (
-            <> Trend is not adjusted (missing current/goal weight). </>
-          ) : (
-            <> Trend is adjusted for your goal ({isBulking ? "bulking" : "cutting"}). </>
-          )}
-          Latest bucket closeness: {Math.round(latestBase)}%.
-        </div>
-      </div>
-    </div>
-  );
-}
-
Index: frontend/src/pages/Dashboard/pages/Weight/components/WeightStartCtaCard.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Weight/components/WeightStartCtaCard.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,27 +1,0 @@
-import React from "react";
-
-export default function WeightStartCtaCard({ error, onStart, isSubmitting }) {
-  return (
-    <div className="card bg-base-200 border border-base-300">
-      <div className="card-body items-center text-center">
-        <h1 className="text-4xl font-bold">You are not tracking weight</h1>
-        <p className="opacity-80 max-w-xl">
-          Start tracking to save your weight profile, calculate your target pace,
-          and keep daily calorie intake history.
-        </p>
-
-        {error ? <p className="text-error text-sm mt-2">{error}</p> : null}
-
-        <button
-          type="button"
-          className="btn btn-lg mt-6 w-full max-w-md bg-green-400! text-black! hover:bg-green-500!"
-          onClick={onStart}
-          disabled={isSubmitting}
-        >
-          {isSubmitting ? "Starting…" : "Start Tracking Weight"}
-        </button>
-      </div>
-    </div>
-  );
-}
-
Index: frontend/src/pages/Dashboard/pages/Weight/components/WeightStartForm.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Weight/components/WeightStartForm.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,181 +1,0 @@
-import React from "react";
-
-function formatNumber(value) {
-  if (value === null || value === undefined || value === "") return "—";
-  const num = Number(value);
-  if (Number.isNaN(num)) return String(value);
-  return Number.isInteger(num) ? String(num) : num.toFixed(2);
-}
-
-export default function WeightStartForm({
-  form,
-  setForm,
-  onSubmit,
-  onCancel,
-  error,
-  isSubmitting,
-  autoCalculateTargets,
-  setAutoCalculateTargets,
-  estimatedGoalTimeWeeks,
-  estimatedGoalCalories,
-}) {
-  return (
-    <form onSubmit={onSubmit} className="card bg-base-200 border border-base-300">
-      <div className="card-body items-center text-center">
-        <h1 className="text-3xl font-bold">Start tracking weight</h1>
-        <p className="opacity-80 max-w-2xl">
-          Fill in your current body metrics and the target you want to reach.
-          Trekr will estimate the time and goal calories in the form, and you can
-          override the calculation manually if you want to plan your own target.
-        </p>
-
-        {error ? <p className="text-error text-sm mt-2">{error}</p> : null}
-
-        <div className="mt-4 grid w-full grid-cols-1 gap-4 md:grid-cols-3">
-          <div>
-            <label className="label">Current weight (kg)</label>
-            <input
-              type="number"
-              className="input input-bordered w-full"
-              value={form.weight}
-              onChange={(e) => setForm((p) => ({ ...p, weight: e.target.value }))}
-              min={1}
-              step="0.1"
-              required
-            />
-          </div>
-
-          <div>
-            <label className="label">Height (cm)</label>
-            <input
-              type="number"
-              className="input input-bordered w-full"
-              value={form.height}
-              onChange={(e) => setForm((p) => ({ ...p, height: e.target.value }))}
-              min={1}
-              step="0.1"
-              required
-            />
-          </div>
-
-          <div>
-            <label className="label">Goal weight (kg)</label>
-            <input
-              type="number"
-              className="input input-bordered w-full"
-              value={form.goalWeight}
-              onChange={(e) =>
-                setForm((p) => ({ ...p, goalWeight: e.target.value }))
-              }
-              min={1}
-              step="0.1"
-              required
-            />
-          </div>
-        </div>
-
-        <div className="mt-4 w-full rounded-2xl border border-base-300 bg-base-100 p-4 text-left">
-          <label className="label cursor-pointer justify-start gap-3">
-            <input
-              type="checkbox"
-              className="checkbox"
-              checked={autoCalculateTargets}
-              onChange={(e) => {
-                const next = e.target.checked;
-                setAutoCalculateTargets(next);
-                if (!next) {
-                  if (form.goalTimeWeeks === "" && estimatedGoalTimeWeeks !== null) {
-                    setForm((p) => ({
-                      ...p,
-                      goalTimeWeeks: String(estimatedGoalTimeWeeks),
-                    }));
-                  }
-                  if (form.goalCalories === "" && estimatedGoalCalories !== null) {
-                    setForm((p) => ({
-                      ...p,
-                      goalCalories: String(estimatedGoalCalories),
-                    }));
-                  }
-                }
-              }}
-            />
-            <span className="label-text">Auto-calculate goal time and calories</span>
-          </label>
-          <p className="text-xs opacity-70 mt-1">
-            Trekr calculates the recommended time locally and only saves your
-            weight profile plus the calorie target.
-          </p>
-
-          <div className="mt-3 grid grid-cols-1 gap-3 sm:grid-cols-2">
-            <div className="rounded-xl border border-base-300 p-3">
-              <div className="text-xs uppercase opacity-60">Suggested time</div>
-              <div className="text-xl font-semibold">
-                {formatNumber(estimatedGoalTimeWeeks)} weeks
-              </div>
-            </div>
-            <div className="rounded-xl border border-base-300 p-3">
-              <div className="text-xs uppercase opacity-60">Suggested calories</div>
-              <div className="text-xl font-semibold">
-                {formatNumber(estimatedGoalCalories)} kcal
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <div className="mt-4 grid w-full grid-cols-1 gap-4 md:grid-cols-2">
-          <div>
-            <label className="label">Goal time (weeks)</label>
-            <input
-              type="number"
-              className="input input-bordered w-full"
-              value={form.goalTimeWeeks}
-              onChange={(e) =>
-                setForm((p) => ({ ...p, goalTimeWeeks: e.target.value }))
-              }
-              min={0}
-              step="0.1"
-              disabled={autoCalculateTargets}
-              placeholder={autoCalculateTargets ? "Calculated automatically" : "12.5"}
-              required={!autoCalculateTargets}
-            />
-          </div>
-
-          <div>
-            <label className="label">Goal calories (kcal)</label>
-            <input
-              type="number"
-              className="input input-bordered w-full"
-              value={form.goalCalories}
-              onChange={(e) =>
-                setForm((p) => ({ ...p, goalCalories: e.target.value }))
-              }
-              min={0}
-              step="0.1"
-              disabled={autoCalculateTargets}
-              placeholder={autoCalculateTargets ? "Calculated automatically" : "2200"}
-              required={!autoCalculateTargets}
-            />
-          </div>
-        </div>
-
-        <div className="mt-6 flex w-full flex-col items-center gap-3 sm:flex-row sm:justify-center">
-          <button
-            className="btn btn-lg w-full sm:w-auto bg-green-400! text-black! hover:bg-green-500!"
-            type="submit"
-            disabled={isSubmitting}
-          >
-            {isSubmitting ? "Starting…" : "Start Tracking"}
-          </button>
-          <button
-            type="button"
-            className="btn btn-ghost btn-lg w-full sm:w-auto"
-            onClick={onCancel}
-          >
-            Cancel
-          </button>
-        </div>
-      </div>
-    </form>
-  );
-}
-
Index: frontend/src/pages/Dashboard/pages/Weight/components/WeightSummaryCard.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Weight/components/WeightSummaryCard.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,63 +1,0 @@
-import React, { useMemo } from "react";
-import { MdEdit } from "react-icons/md";
-
-function statValue(value, suffix = "") {
-  if (value === null || value === undefined || value === "") return "—";
-  const num = Number(value);
-  if (Number.isNaN(num)) return String(value);
-  const formatted = Number.isInteger(num) ? String(num) : num.toFixed(2);
-  return `${formatted}${suffix}`;
-}
-
-export default function WeightSummaryCard({ profile, onEdit, todayTrainingInfo }) {
-  const todayCalories = useMemo(() => {
-    if (!profile?.goalCalories || !todayTrainingInfo) return null;
-    const goal = Number(profile.goalCalories);
-    const burned = Number(todayTrainingInfo.totalBurnedCalories) || 0;
-    if (!Number.isFinite(goal)) return null;
-    return goal + burned;
-  }, [profile?.goalCalories, todayTrainingInfo]);
-
-  const stats = [
-    { label: "Current weight", value: profile?.weight, suffix: " kg" },
-    { label: "Goal weight", value: profile?.goalWeight, suffix: " kg" },
-    { label: "Goal calories", value: profile?.goalCalories, suffix: " kcal" },
-    todayTrainingInfo?.trainedToday ? 
-      { label: "Today's calories", value: todayCalories, suffix: " kcal", subtitle: `(+${statValue(todayTrainingInfo.totalBurnedCalories)} burned)` } 
-      : null,
-  ].filter(Boolean);
-
-  return (
-    <div className="card bg-base-200 border border-base-300">
-      <div className="card-body">
-        <div className="flex items-center justify-between gap-4">
-          <div>
-            <h2 className="text-lg font-semibold">Profile summary</h2>
-            <p className="opacity-80">
-              Your current target and calorie plan at a glance.
-            </p>
-          </div>
-          <button
-            type="button"
-            className="btn btn-sm btn-ghost text-blue-400 hover:text-blue-300"
-            title="Edit profile"
-            onClick={onEdit}
-          >
-            <MdEdit className="size-5" />
-          </button>
-        </div>
-
-        <div className="mt-4 grid grid-cols-1 gap-3 sm:grid-cols-3 lg:grid-cols-4">
-          {stats.map((stat) => (
-            <div key={stat.label} className="stat bg-base-100 rounded-box border border-base-300">
-              <div className="stat-title text-xs">{stat.label}</div>
-              <div className="stat-value text-lg">{statValue(stat.value, stat.suffix)}</div>
-              {stat.subtitle && <div className="stat-desc text-xs">{stat.subtitle}</div>}
-            </div>
-          ))}
-        </div>
-      </div>
-    </div>
-  );
-}
-
Index: frontend/src/pages/Dashboard/pages/Weight/components/WeightTrackingHeader.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Weight/components/WeightTrackingHeader.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,23 +1,0 @@
-import React from "react";
-
-export default function WeightTrackingHeader({ onAddIntake, isTodayLogged }) {
-  return (
-    <div className="flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
-      <div>
-        <h1 className="text-2xl font-bold">Weight</h1>
-        <p className="opacity-80">
-          Track your daily calorie intakes and keep an eye on your target.
-        </p>
-      </div>
-      <button
-        type="button"
-        className="btn bg-green-400! text-black! hover:bg-green-500! border-0"
-        onClick={onAddIntake}
-        disabled={isTodayLogged}
-      >
-        {isTodayLogged ? "Today's intake already logged" : "+ Add today's intake"}
-      </button>
-    </div>
-  );
-}
-
Index: frontend/src/pages/LandingPage/LandingPage.jsx
===================================================================
--- frontend/src/pages/LandingPage/LandingPage.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,25 +1,0 @@
-import React from "react";
-import NavbarLanding from "./components/NavbarLanding";
-import Hero from "./components/Hero";
-import Footer from "./components/Footer";
-import { HowItWorks } from "./components/HowItWorks";
-import { Who } from "./components/Who";
-import { Faq } from "./components/Faq";
-import Features from "./components/Features";
-import Benefits from "./components/Benefits";
-const LandingPage = () => {
-  return (
-    <div data-theme="forest" className="min-h-screen w-full overflow-x-hidden">
-      <NavbarLanding />
-      <Hero />
-      <HowItWorks />
-      <Who />
-      <Features />
-      <Benefits />
-      <Faq />
-      <Footer />
-    </div>
-  );
-};
-
-export default LandingPage;
Index: frontend/src/pages/LandingPage/components/Benefits.jsx
===================================================================
--- frontend/src/pages/LandingPage/components/Benefits.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,83 +1,0 @@
-import React from "react";
-
-export const Benefits = () => {
-  const benefits = [
-    {
-      number: "1",
-      title: "Unified tracking",
-      description:
-        "Stop jumping between 5+ different apps. Everything you need is in one place.",
-    },
-    {
-      number: "2",
-      title: "Real-time insights",
-      description:
-        "See your progress at a glance with clear dashboards and intuitive charts.",
-    },
-    {
-      number: "3",
-      title: "Goal-focused",
-      description:
-        "Set targets, log consistently, and watch trends emerge over time.",
-    },
-    {
-      number: "4",
-      title: "Built for habit stacking",
-      description:
-        "Track related areas and discover how fitness, finances, and discipline connect.",
-    },
-    {
-      number: "5",
-      title: "Data security",
-      description:
-        "Your personal metrics are encrypted and private. Only you control access.",
-    },
-    {
-      number: "6",
-      title: "Mobile-friendly",
-      description:
-        "Fully responsive design works seamlessly on phone, tablet, and desktop.",
-    },
-  ];
-
-  return (
-    <section id="benefits" className="px-[5%] py-16 md:py-24 lg:py-28">
-      <div className="mx-auto w-full max-w-6xl">
-        <div className="text-center mb-16 md:mb-20">
-          <p className="font-semibold text-sm md:text-base mb-3 md:mb-4">
-            Why Trekr
-          </p>
-          <h2 className="font-bold mb-6 text-3xl sm:text-4xl md:text-5xl lg:text-6xl leading-tight">
-            Designed for serious progress
-          </h2>
-          <p className="max-w-2xl mx-auto text-base md:text-lg opacity-80">
-            We built Trekr for people who don't settle for half measures. If
-            consistency matters to you, Trekr makes it easier to stay on track.
-          </p>
-        </div>
-
-        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8 md:gap-10">
-          {benefits.map((benefit, index) => (
-            <div key={index} className="flex gap-4 md:gap-6">
-              <div className="shrink-0">
-                <div className="flex items-center justify-center h-12 w-12 md:h-16 md:w-16 rounded-full bg-primary text-white font-bold text-lg md:text-xl">
-                  {benefit.number}
-                </div>
-              </div>
-              <div className="flex-1">
-                <h3 className="text-lg md:text-xl font-bold mb-2">
-                  {benefit.title}
-                </h3>
-                <p className="opacity-80 text-sm md:text-base">
-                  {benefit.description}
-                </p>
-              </div>
-            </div>
-          ))}
-        </div>
-      </div>
-    </section>
-  );
-};
-
-export default Benefits;
Index: frontend/src/pages/LandingPage/components/Faq.defaults.js
===================================================================
--- frontend/src/pages/LandingPage/components/Faq.defaults.js	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,40 +1,0 @@
-export const FaqDefaults = {
-  heading: "Frequently asked questions",
-  description:
-    "Quick answers on how Trekr helps you track self-improvement across multiple areas — in one place.",
-  questions: [
-    {
-      title: "What can I track in Trekr?",
-      answer:
-        "Workouts (type and duration), weight & nutrition (goal, calories), finances & budgeting (income and expenses), investing (portfolio and returns), and discipline (daily tasks you can check off).",
-    },
-    {
-      title: "Do I need multiple apps to do all this?",
-      answer:
-        "No — Trekr is designed to unify the most important self-improvement categories so you can get a clear overview without switching between tools.",
-    },
-    {
-      title: "How do goals and progress tracking work?",
-      answer:
-        "You set a goal (for example: target weight or monthly budget), then log daily/weekly data. Trekr helps you see where you are vs. the goal and how you're trending over time.",
-    },
-    {
-      title: "Where is my data stored?",
-      answer:
-        "Your entries are stored in a database and linked to your user profile so you can track progress consistently over time.",
-    },
-    {
-      title: "Is Trekr a mobile app?",
-      answer:
-        "Right now Trekr is a web app. The goal is a fast, simple experience on both mobile and desktop in the browser.",
-    },
-  ],
-  footerHeading: "Still have questions?",
-  footerDescription: "Send us a message and we’ll help you get started.",
-  button: {
-    title: "Contact",
-    variant: "secondary",
-  },
-};
-
-
Index: frontend/src/pages/LandingPage/components/Faq.jsx
===================================================================
--- frontend/src/pages/LandingPage/components/Faq.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,63 +1,0 @@
-import {
-  Button,
-  Accordion,
-  AccordionContent,
-  AccordionItem,
-  AccordionTrigger,
-} from "@relume_io/relume-ui";
-
-import { FaqDefaults } from "./Faq.defaults";
-
-export const Faq = (props) => {
-  const {
-    heading,
-    description,
-    questions,
-    footerHeading,
-    footerDescription,
-    button,
-  } = {
-    ...FaqDefaults,
-    ...props,
-  };
-  return (
-    <section
-      id="relume"
-      className="px-[5%] py-16 md:py-24 lg:py-28 flex items-center justify-center"
-    >
-      <div className="mx-auto w-full max-w-2xl">
-        <div className="rb-12 mb-12 text-center md:mb-18 lg:mb-20">
-          <h2 className="rb-5 mb-5 font-bold text-4xl sm:text-5xl md:mb-6 md:text-6xl lg:text-7xl leading-tight">
-            {heading}
-          </h2>
-          <p className="text-sm sm:text-base md:text-lg opacity-80">
-            {description}
-          </p>
-        </div>
-        <Accordion type="multiple">
-          {questions.map((question, index) => (
-            <AccordionItem key={index} value={`item-${index}`}>
-              <AccordionTrigger className="md:py-5 md:text-md">
-                {question.title}
-              </AccordionTrigger>
-              <AccordionContent className="md:pb-6">
-                {question.answer}
-              </AccordionContent>
-            </AccordionItem>
-          ))}
-        </Accordion>
-        <div className="mx-auto mt-12 max-w-md text-center md:mt-18 lg:mt-20">
-          <h4 className="mb-3 text-2xl font-bold md:mb-4 md:text-3xl md:leading-[1.3] lg:text-4xl">
-            {footerHeading}
-          </h4>
-          <p className="text-sm sm:text-base md:text-lg opacity-80">
-            {footerDescription}
-          </p>
-          <div className="mt-6 md:mt-8">
-            <Button {...button}>{button.title}</Button>
-          </div>
-        </div>
-      </div>
-    </section>
-  );
-};
Index: frontend/src/pages/LandingPage/components/Features.jsx
===================================================================
--- frontend/src/pages/LandingPage/components/Features.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,83 +1,0 @@
-import React from "react";
-
-export const Features = () => {
-  const features = [
-    {
-      icon: "🏋️",
-      title: "Fitness tracking",
-      description:
-        "Log workouts, stay consistent, and see your training trend over time.",
-    },
-    {
-      icon: "⚖️",
-      title: "Weight & nutrition",
-      description:
-        "Track daily weigh-ins and calories so you always know what’s working.",
-    },
-    {
-      icon: "💰",
-      title: "Budgeting",
-      description:
-        "Record income and expenses to stay on top of your money without spreadsheets.",
-    },
-    {
-      icon: "📈",
-      title: "Investments",
-      description:
-        "Monitor assets and returns to keep a clear view of your portfolio.",
-    },
-    {
-      icon: "✅",
-      title: "Daily discipline",
-      description:
-        "Turn intentions into habits with simple daily tasks and check-ins.",
-    },
-    {
-      icon: "📊",
-      title: "Insights",
-      description:
-        "See patterns across categories so you can make better decisions faster.",
-    },
-  ];
-
-  return (
-    <section
-      id="features"
-      className="px-[5%] py-16 md:py-24 lg:py-28 bg-base-200"
-    >
-      <div className="mx-auto w-full max-w-6xl">
-        <div className="text-center mb-16 md:mb-20">
-          <p className="font-semibold text-sm md:text-base mb-3 md:mb-4">
-            What you can track
-          </p>
-          <h2 className="font-bold mb-6 text-3xl sm:text-4xl md:text-5xl lg:text-6xl leading-tight">
-            Everything in one dashboard
-          </h2>
-          <p className="max-w-2xl mx-auto text-base md:text-lg opacity-80">
-            Trekr brings your health, habits, and money together, so you don’t
-            lose momentum switching tools.
-          </p>
-        </div>
-
-        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6 md:gap-8">
-          {features.map((feature, index) => (
-            <div
-              key={index}
-              className="card bg-base-100 border border-base-300 hover:shadow-md transition-shadow"
-            >
-              <div className="card-body p-6 md:p-8">
-                <div className="text-4xl md:text-5xl mb-4">{feature.icon}</div>
-                <h3 className="text-xl md:text-2xl font-bold mb-3">
-                  {feature.title}
-                </h3>
-                <p className="opacity-80">{feature.description}</p>
-              </div>
-            </div>
-          ))}
-        </div>
-      </div>
-    </section>
-  );
-};
-
-export default Features;
Index: frontend/src/pages/LandingPage/components/Footer.jsx
===================================================================
--- frontend/src/pages/LandingPage/components/Footer.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,53 +1,0 @@
-import React from "react";
-import logo from "../../../assets/logo.png";
-
-const Footer = () => {
-  return (
-    <footer className="footer sm:footer-horizontal bg-neutral text-neutral-content p-8 sm:p-10">
-      <aside>
-        <img src={logo} alt="Trekr" className="h-10 w-auto" />
-        <p>Track self-improvement progress — in one place.</p>
-      </aside>
-      <nav>
-        <h6 className="footer-title">Social</h6>
-        <div className="grid grid-flow-col gap-4">
-          <a>
-            <svg
-              xmlns="http://www.w3.org/2000/svg"
-              width="24"
-              height="24"
-              viewBox="0 0 24 24"
-              className="fill-current"
-            >
-              <path d="M24 4.557c-.883.392-1.832.656-2.828.775 1.017-.609 1.798-1.574 2.165-2.724-.951.564-2.005.974-3.127 1.195-.897-.957-2.178-1.555-3.594-1.555-3.179 0-5.515 2.966-4.797 6.045-4.091-.205-7.719-2.165-10.148-5.144-1.29 2.213-.669 5.108 1.523 6.574-.806-.026-1.566-.247-2.229-.616-.054 2.281 1.581 4.415 3.949 4.89-.693.188-1.452.232-2.224.084.626 1.956 2.444 3.379 4.6 3.419-2.07 1.623-4.678 2.348-7.29 2.04 2.179 1.397 4.768 2.212 7.548 2.212 9.142 0 14.307-7.721 13.995-14.646.962-.695 1.797-1.562 2.457-2.549z"></path>
-            </svg>
-          </a>
-          <a>
-            <svg
-              xmlns="http://www.w3.org/2000/svg"
-              width="24"
-              height="24"
-              viewBox="0 0 24 24"
-              className="fill-current"
-            >
-              <path d="M19.615 3.184c-3.604-.246-11.631-.245-15.23 0-3.897.266-4.356 2.62-4.385 8.816.029 6.185.484 8.549 4.385 8.816 3.6.245 11.626.246 15.23 0 3.897-.266 4.356-2.62 4.385-8.816-.029-6.185-.484-8.549-4.385-8.816zm-10.615 12.816v-8l8 3.993-8 4.007z"></path>
-            </svg>
-          </a>
-          <a>
-            <svg
-              xmlns="http://www.w3.org/2000/svg"
-              width="24"
-              height="24"
-              viewBox="0 0 24 24"
-              className="fill-current"
-            >
-              <path d="M9 8h-3v4h3v12h5v-12h3.642l.358-4h-4v-1.667c0-.955.192-1.333 1.115-1.333h2.885v-5h-3.808c-3.596 0-5.192 1.583-5.192 4.615v3.385z"></path>
-            </svg>
-          </a>
-        </div>
-      </nav>
-    </footer>
-  );
-};
-
-export default Footer;
Index: frontend/src/pages/LandingPage/components/Hero.jsx
===================================================================
--- frontend/src/pages/LandingPage/components/Hero.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,50 +1,0 @@
-import React, { useEffect, useState } from "react";
-import { Link } from "react-router-dom";
-
-const Hero = () => {
-  const [isAuthed, setIsAuthed] = useState(() =>
-    Boolean(localStorage.getItem("authToken")),
-  );
-
-  useEffect(() => {
-    const onStorage = (e) => {
-      if (e.key === "authToken") {
-        setIsAuthed(Boolean(e.newValue));
-      }
-    };
-    window.addEventListener("storage", onStorage);
-    return () => window.removeEventListener("storage", onStorage);
-  }, []);
-
-  return (
-    <div
-      className="hero min-h-[calc(100vh-4rem)] bg-cover bg-center"
-      style={{
-        backgroundImage:
-          "url(https://images.unsplash.com/photo-1551288049-bebda4e38f71?auto=format&fit=crop&w=1920&q=80)",
-      }}
-    >
-      <div className="hero-overlay"></div>
-      <div className="hero-content text-neutral-content text-center px-4 py-16 sm:py-24">
-        <div className="w-full max-w-2xl">
-          <h1 className="mb-5 font-bold text-3xl sm:text-4xl md:text-5xl lg:text-6xl leading-tight">
-            Trekr — track progress, every day
-          </h1>
-          <p className="mb-6 text-sm sm:text-base md:text-lg opacity-90">
-            One self-improvement hub: workouts, weight & nutrition, finances &
-            budgeting, investing, and daily discipline tasks. Set goals, log
-            consistently, and see your progress in one place.
-          </p>
-          <Link
-            className="btn btn-primary !text-white btn-sm sm:btn-md md:btn-lg"
-            to={isAuthed ? "/dashboard" : "/register"}
-          >
-            {isAuthed ? "Go to dashboard" : "Get started"}
-          </Link>
-        </div>
-      </div>
-    </div>
-  );
-};
-
-export default Hero;
Index: frontend/src/pages/LandingPage/components/HowItWorks.defaults.js
===================================================================
--- frontend/src/pages/LandingPage/components/HowItWorks.defaults.js	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,22 +1,0 @@
-import { RxChevronRight } from "react-icons/rx";
-
-export const HowItWorksDefaults = {
-  tagline: "How it works",
-  heading: "Log it. Track it. Improve it.",
-  description:
-    "Log what matters to your goals — workouts, weight/calories, budgets, investments, and daily tasks. Trekr gives you a clear view of trends over time so you can stay consistent and keep moving forward.",
-  buttons: [
-    { title: "Start free", variant: "secondary" },
-    {
-      title: "See a quick overview",
-      variant: "link",
-      size: "link",
-      iconRight: <RxChevronRight />,
-    },
-  ],
-  image: {
-    src: "https://images.unsplash.com/photo-1522202176988-66273c2fd55f?auto=format&fit=crop&w=1600&q=80",
-    alt: "Progress and goals overview on a laptop",
-  },
-};
-
Index: frontend/src/pages/LandingPage/components/HowItWorks.defaults.jsx
===================================================================
--- frontend/src/pages/LandingPage/components/HowItWorks.defaults.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,23 +1,0 @@
-import { RxChevronRight } from "react-icons/rx";
-
-export const HowItWorksDefaults = {
-  tagline: "How it works",
-  heading: "Log it. Track it. Improve it.",
-  description:
-    "Log what matters to your goals — workouts, weight/calories, budgets, investments, and daily tasks. Trekr gives you a clear view of trends over time so you can stay consistent and keep moving forward.",
-  buttons: [
-    { title: "Start free", variant: "secondary" },
-    {
-      title: "See a quick overview",
-      variant: "link",
-      size: "link",
-      iconRight: <RxChevronRight />,
-    },
-  ],
-  image: {
-    src: "https://images.unsplash.com/photo-1522202176988-66273c2fd55f?auto=format&fit=crop&w=1600&q=80",
-    alt: "Progress and goals overview on a laptop",
-  },
-};
-
-
Index: frontend/src/pages/LandingPage/components/HowItWorks.jsx
===================================================================
--- frontend/src/pages/LandingPage/components/HowItWorks.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,42 +1,0 @@
-import { Button } from "@relume_io/relume-ui";
-import { HowItWorksDefaults } from "./HowItWorks.defaults.jsx";
-
-export const HowItWorks = (props) => {
-  const { tagline, heading, description, buttons, image } = {
-    ...HowItWorksDefaults,
-    ...props,
-  };
-  return (
-    <section id="relume" className="px-[5%] py-16 md:py-24 lg:py-28">
-      <div className="mx-auto w-full max-w-6xl">
-        <div className="grid grid-cols-1 gap-y-12 md:grid-cols-2 md:items-center md:gap-x-12 lg:gap-x-20">
-          <div className="text-center md:text-left">
-            <p className="mb-3 font-semibold md:mb-4">{tagline}</p>
-            <h1 className="rb-5 mb-5 font-bold text-4xl sm:text-5xl md:mb-6 md:text-6xl lg:text-7xl leading-tight">
-              {heading}
-            </h1>
-            <p className="text-sm sm:text-base md:text-lg opacity-80">
-              {description}
-            </p>
-            <div className="mt-6 flex flex-wrap items-center gap-4 md:mt-8">
-              {buttons.map((button, index) => (
-                <Button key={index} {...button}>
-                  {button.title}
-                </Button>
-              ))}
-            </div>
-          </div>
-          <div>
-            <img
-              src={image.src}
-              className="w-full rounded-2xl object-cover"
-              alt={image.alt}
-            />
-          </div>
-        </div>
-      </div>
-    </section>
-  );
-};
-
-
Index: frontend/src/pages/LandingPage/components/NavbarLanding.jsx
===================================================================
--- frontend/src/pages/LandingPage/components/NavbarLanding.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,68 +1,0 @@
-import React, { useEffect, useState } from "react";
-import { Link, useNavigate } from "react-router-dom";
-import logo from "../../../assets/logo.png";
-
-const NavbarLanding = () => {
-  const navigate = useNavigate();
-  const [isAuthed, setIsAuthed] = useState(() =>
-    Boolean(localStorage.getItem("authToken")),
-  );
-
-  useEffect(() => {
-    const onStorage = (e) => {
-      if (e.key === "authToken") {
-        setIsAuthed(Boolean(e.newValue));
-      }
-    };
-    window.addEventListener("storage", onStorage);
-    return () => window.removeEventListener("storage", onStorage);
-  }, []);
-
-  const onLogout = () => {
-    localStorage.removeItem("authToken");
-    localStorage.removeItem("authUser");
-    setIsAuthed(false);
-    navigate("/", { replace: true });
-  };
-
-  return (
-    <div className="navbar bg-primary text-primary-content shadow-sm px-4 sm:px-6">
-      <div className="navbar-start flex-1 min-w-0">
-        <Link
-          className="btn btn-ghost text-primary-content"
-          aria-label="Trekr home"
-          to="/"
-        >
-          <img src={logo} alt="Trekr" className="h-12 w-auto rounded-2xl" />
-        </Link>
-      </div>
-      <div className="navbar-end flex-none">
-        {isAuthed ? (
-          <div className="flex flex-wrap items-center justify-end gap-2 sm:gap-3">
-            <Link
-              className="btn btn-sm sm:btn-md btn-outline border-primary-content text-primary-content"
-              to="/dashboard"
-            >
-              Dashboard
-            </Link>
-            <button
-              className="btn btn-sm sm:btn-md btn-outline border-primary-content text-primary-content"
-              onClick={onLogout}
-            >
-              Logout
-            </button>
-          </div>
-        ) : (
-          <Link
-            className="btn btn-sm sm:btn-md btn-tertiary text-blue-200! px-6 sm:px-8"
-            to="/login"
-          >
-            Start Tracking
-          </Link>
-        )}
-      </div>
-    </div>
-  );
-};
-
-export default NavbarLanding;
Index: frontend/src/pages/LandingPage/components/Who.defaults.jsx
===================================================================
--- frontend/src/pages/LandingPage/components/Who.defaults.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,25 +1,0 @@
-import { RxChevronRight } from "react-icons/rx";
-
-const WhoDefaults = {
-  tagline: "Who it's for",
-  heading: "Anyone working on themselves",
-  description:
-    "Whether you're training regularly, trying to manage weight and nutrition, organizing your budget, tracking investments, or building discipline — Trekr brings your key habits and metrics together in one place.",
-  buttons: [
-    { title: "Create an account", variant: "secondary" },
-    {
-      title: "Learn more",
-      variant: "link",
-      size: "link",
-      iconRight: <RxChevronRight />,
-    },
-  ],
-  image: {
-    src: "https://images.unsplash.com/photo-1554284126-aa88f22d8b74?auto=format&fit=crop&w=1600&q=80",
-    alt: "Planning habits and routine",
-  },
-};
-
-export { WhoDefaults };
-
-
Index: frontend/src/pages/LandingPage/components/Who.jsx
===================================================================
--- frontend/src/pages/LandingPage/components/Who.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,41 +1,0 @@
-import { Button } from "@relume_io/relume-ui";
-import { WhoDefaults } from "./Who.defaults.jsx";
-
-export const Who = (props) => {
-  const { tagline, heading, description, buttons, image } = {
-    ...WhoDefaults,
-    ...props,
-  };
-  return (
-    <section id="relume" className="px-[5%] py-16 md:py-24 lg:py-28">
-      <div className="mx-auto w-full max-w-6xl">
-        <div className="grid grid-cols-1 gap-y-12 md:grid-cols-2 md:items-center md:gap-x-12 lg:gap-x-20">
-          <div className="order-2 md:order-1">
-            <img
-              src={image.src}
-              className="w-full rounded-2xl object-cover"
-              alt={image.alt}
-            />
-          </div>
-          <div className="order-1 lg:order-2 text-center md:text-left">
-            <p className="mb-3 font-semibold md:mb-4">{tagline}</p>
-            <h2 className="rb-5 mb-5 font-bold text-4xl sm:text-5xl md:mb-6 md:text-6xl lg:text-7xl leading-tight">
-              {heading}
-            </h2>
-            <p className="text-sm sm:text-base md:text-lg opacity-80">
-              {description}
-            </p>
-            <div className="mt-6 flex flex-wrap gap-4 md:mt-8">
-              {buttons.map((button, index) => (
-                <Button key={index} {...button}>
-                  {button.title}
-                </Button>
-              ))}
-            </div>
-          </div>
-        </div>
-      </div>
-    </section>
-  );
-};
-
Index: frontend/src/pages/Login/Login.jsx
===================================================================
--- frontend/src/pages/Login/Login.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,99 +1,0 @@
-import React, { useEffect, useState } from "react";
-import { Link, useNavigate } from "react-router-dom";
-
-import api from "../../api/axios";
-
-const Login = () => {
-  const navigate = useNavigate();
-  const [usernameOrEmail, setUsernameOrEmail] = useState("");
-  const [password, setPassword] = useState("");
-  const [error, setError] = useState("");
-  const [isSubmitting, setIsSubmitting] = useState(false);
-
-  useEffect(() => {
-    const token = localStorage.getItem("authToken");
-    if (token) navigate("/dashboard", { replace: true });
-  }, [navigate]);
-
-  const onSubmit = async (e) => {
-    e.preventDefault();
-    setError("");
-    setIsSubmitting(true);
-    try {
-      const res = await api.post("/auth/login", {
-        usernameOrEmail,
-        password,
-      });
-      localStorage.setItem("authToken", res.data.token);
-      localStorage.setItem(
-        "authUser",
-        JSON.stringify({
-          userId: res.data.userId,
-          username: res.data.username,
-          email: res.data.email,
-        }),
-      );
-      navigate("/dashboard");
-    } catch (err) {
-      const message =
-        err?.response?.data?.message ||
-        err?.response?.data?.errors?.usernameOrEmail ||
-        "Login failed";
-      setError(message);
-    } finally {
-      setIsSubmitting(false);
-    }
-  };
-
-  return (
-    <div className="min-h-screen flex items-center justify-center p-4">
-      <form
-        onSubmit={onSubmit}
-        className="fieldset bg-base-200 border-base-300 rounded-box w-xs border p-4"
-      >
-        <legend className="fieldset-legend">Login</legend>
-
-        <label className="label">Username or Email</label>
-        <input
-          type="text"
-          className="input"
-          placeholder="Username or Email"
-          value={usernameOrEmail}
-          onChange={(e) => setUsernameOrEmail(e.target.value)}
-          autoComplete="username"
-          required
-        />
-
-        <label className="label">Password</label>
-        <input
-          type="password"
-          className="input"
-          placeholder="Password"
-          value={password}
-          onChange={(e) => setPassword(e.target.value)}
-          autoComplete="current-password"
-          required
-        />
-
-        {error ? <p className="text-error mt-2 text-sm">{error}</p> : null}
-
-        <button
-          className="btn btn-neutral mt-4"
-          type="submit"
-          disabled={isSubmitting}
-        >
-          {isSubmitting ? "Logging in..." : "Login"}
-        </button>
-
-        <p className="mt-3 text-sm">
-          Don&apos;t have an account?{" "}
-          <Link className="link link-primary" to="/register">
-            Register
-          </Link>
-        </p>
-      </form>
-    </div>
-  );
-};
-
-export default Login;
Index: frontend/src/pages/Register/Register.jsx
===================================================================
--- frontend/src/pages/Register/Register.jsx	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,134 +1,0 @@
-import React, { useEffect, useState } from "react";
-import { Link, useNavigate } from "react-router-dom";
-
-import api from "../../api/axios";
-
-const Register = () => {
-  const navigate = useNavigate();
-  const [email, setEmail] = useState("");
-  const [username, setUsername] = useState("");
-  const [password, setPassword] = useState("");
-  const [error, setError] = useState("");
-  const [isSubmitting, setIsSubmitting] = useState(false);
-
-  useEffect(() => {
-    const token = localStorage.getItem("authToken");
-    if (token) navigate("/dashboard", { replace: true });
-  }, [navigate]);
-
-  const onSubmit = async (e) => {
-    e.preventDefault();
-    setError("");
-    setIsSubmitting(true);
-
-    const normalizedEmail = email.trim().toLowerCase();
-    const normalizedUsername = username.trim();
-    const normalizedPassword = password;
-
-    if (normalizedUsername.length < 3 || normalizedUsername.length > 50) {
-      setError("Username must be between 3 and 50 characters");
-      setIsSubmitting(false);
-      return;
-    }
-
-    if (normalizedPassword.length < 6) {
-      setError("Password must be at least 6 characters");
-      setIsSubmitting(false);
-      return;
-    }
-
-    try {
-      const res = await api.post("/auth/register", {
-        email: normalizedEmail,
-        username: normalizedUsername,
-        password: normalizedPassword,
-      });
-      localStorage.setItem("authToken", res.data.token);
-      localStorage.setItem(
-        "authUser",
-        JSON.stringify({
-          userId: res.data.userId,
-          username: res.data.username,
-          email: res.data.email,
-        }),
-      );
-      navigate("/dashboard");
-    } catch (err) {
-      const fieldErrors = err?.response?.data?.errors ?? {};
-      const firstFieldError = Object.values(fieldErrors)[0];
-      const message =
-        firstFieldError ||
-        err?.response?.data?.message ||
-        "Registration failed";
-      setError(message);
-    } finally {
-      setIsSubmitting(false);
-    }
-  };
-
-  return (
-    <div className="min-h-screen flex items-center justify-center p-4">
-      <form
-        onSubmit={onSubmit}
-        className="fieldset bg-base-200 border-base-300 rounded-box w-xs border p-4"
-      >
-        <legend className="fieldset-legend">Register</legend>
-
-        <label className="label">Email</label>
-        <input
-          type="email"
-          className="input"
-          placeholder="Email"
-          value={email}
-          onChange={(e) => setEmail(e.target.value)}
-          autoComplete="email"
-          required
-        />
-
-        <label className="label">Username</label>
-        <input
-          type="text"
-          className="input"
-          placeholder="Username"
-          value={username}
-          onChange={(e) => setUsername(e.target.value)}
-          autoComplete="username"
-          minLength={3}
-          maxLength={50}
-          required
-        />
-
-        <label className="label">Password</label>
-        <input
-          type="password"
-          className="input"
-          placeholder="Password"
-          value={password}
-          onChange={(e) => setPassword(e.target.value)}
-          autoComplete="new-password"
-          minLength={6}
-          required
-        />
-
-        {error ? <p className="text-error mt-2 text-sm">{error}</p> : null}
-
-        <button
-          className="btn btn-neutral mt-4"
-          type="submit"
-          disabled={isSubmitting}
-        >
-          {isSubmitting ? "Creating account..." : "Register"}
-        </button>
-
-        <p className="mt-3 text-sm">
-          Already have an account?{" "}
-          <Link className="link link-primary" to="/login">
-            Login
-          </Link>
-        </p>
-      </form>
-    </div>
-  );
-};
-
-export default Register;
Index: frontend/src/utils/authSession.js
===================================================================
--- frontend/src/utils/authSession.js	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,37 +1,0 @@
-export const AUTH_TOKEN_KEY = "authToken";
-export const AUTH_USER_KEY = "authUser";
-
-function decodeJwtPayload(token) {
-  try {
-    const parts = token.split(".");
-    if (parts.length < 2) return null;
-    const base64 = parts[1].replace(/-/g, "+").replace(/_/g, "/");
-    const normalized = base64.padEnd(Math.ceil(base64.length / 4) * 4, "=");
-    const json = atob(normalized);
-    return JSON.parse(json);
-  } catch {
-    return null;
-  }
-}
-
-export function isTokenExpired(token) {
-  if (!token) return true;
-  const payload = decodeJwtPayload(token);
-  if (!payload || typeof payload.exp !== "number") return true;
-  return payload.exp * 1000 <= Date.now();
-}
-
-export function getAuthToken() {
-  return localStorage.getItem(AUTH_TOKEN_KEY);
-}
-
-export function clearAuthSession() {
-  localStorage.removeItem(AUTH_TOKEN_KEY);
-  localStorage.removeItem(AUTH_USER_KEY);
-}
-
-export function logoutAndRedirect(path = "/dashboard") {
-  clearAuthSession();
-  window.location.assign(path);
-}
-
Index: frontend/src/utils/timeSeries.js
===================================================================
--- frontend/src/utils/timeSeries.js	(revision 0427f599f15f08425a193b5b366fd15cc0a95f42)
+++ 	(revision )
@@ -1,116 +1,0 @@
-// Utilities for bucketing time-series data and computing percentage changes.
-
-function startOfDay(d) {
-  return new Date(d.getFullYear(), d.getMonth(), d.getDate());
-}
-
-function startOfMonth(d) {
-  return new Date(d.getFullYear(), d.getMonth(), 1);
-}
-
-function startOfYear(d) {
-  return new Date(d.getFullYear(), 0, 1);
-}
-
-// ISO week start (Monday)
-function startOfISOWeek(d) {
-  const date = startOfDay(d);
-  const day = date.getDay(); // 0..6 (Sun..Sat)
-  const diff = (day === 0 ? -6 : 1) - day;
-  date.setDate(date.getDate() + diff);
-  return startOfDay(date);
-}
-
-function bucketKey(date, granularity) {
-  const d = new Date(date);
-  if (Number.isNaN(d.getTime())) return null;
-
-  let s;
-  switch (granularity) {
-    case "daily":
-      s = startOfDay(d);
-      break;
-    case "weekly":
-      s = startOfISOWeek(d);
-      break;
-    case "monthly":
-      s = startOfMonth(d);
-      break;
-    case "yearly":
-      s = startOfYear(d);
-      break;
-    default:
-      s = startOfDay(d);
-  }
-
-  return s.getTime();
-}
-
-export function sumByTimeBucket(items, granularity, getDate, getValue) {
-  const map = new Map();
-
-  for (const it of items ?? []) {
-    const rawDate = getDate(it);
-    const k = bucketKey(rawDate, granularity);
-    if (k == null) continue;
-
-    const v = Number(getValue(it) ?? 0);
-    if (!Number.isFinite(v)) continue;
-
-    map.set(k, (map.get(k) ?? 0) + v);
-  }
-
-  const points = Array.from(map.entries())
-    .sort((a, b) => a[0] - b[0])
-    .map(([ts, value]) => ({ ts, value }));
-
-  return points;
-}
-
-export function percentChangeSeries(points) {
-  // Output: [{ ts, value: pctChange, base: sumValue }]
-  const out = [];
-  let prev = null;
-
-  for (const p of points ?? []) {
-    const curr = Number(p.value ?? 0);
-    let pct = 0;
-
-    if (prev == null) {
-      pct = 0;
-    } else if (prev === 0) {
-      // Avoid exploding infinity when previous is 0.
-      // Define: if curr is also 0 -> 0%, else -> 100%.
-      pct = curr === 0 ? 0 : 100;
-    } else {
-      pct = ((curr - prev) / prev) * 100;
-    }
-
-    out.push({ ts: p.ts, value: pct, base: curr });
-    prev = curr;
-  }
-
-  return out;
-}
-
-export function formatBucketLabel(ts, granularity) {
-  const d = new Date(ts);
-  if (Number.isNaN(d.getTime())) return "—";
-
-  const optsDay = { month: "short", day: "2-digit" };
-  const optsMonth = { year: "numeric", month: "short" };
-
-  switch (granularity) {
-    case "daily":
-      return d.toLocaleDateString(undefined, optsDay);
-    case "weekly":
-      return `Wk of ${d.toLocaleDateString(undefined, optsDay)}`;
-    case "monthly":
-      return d.toLocaleDateString(undefined, optsMonth);
-    case "yearly":
-      return String(d.getFullYear());
-    default:
-      return d.toLocaleDateString();
-  }
-}
-
