Index: frontend/src/App.jsx
===================================================================
--- frontend/src/App.jsx	(revision 37307aadda57d1e5b86180b9b2b8a6318ec36095)
+++ frontend/src/App.jsx	(revision 708af9efba48db0445f07728b5d091a3d8898876)
@@ -10,4 +10,6 @@
 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 Investing from "./pages/Dashboard/pages/Investing/Investing.jsx";
@@ -46,4 +48,6 @@
           />
           <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="investing" element={<Investing />} />
Index: frontend/src/pages/Dashboard/pages/Weight/NewWeightIntake.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Weight/NewWeightIntake.jsx	(revision 708af9efba48db0445f07728b5d091a3d8898876)
+++ frontend/src/pages/Dashboard/pages/Weight/NewWeightIntake.jsx	(revision 708af9efba48db0445f07728b5d091a3d8898876)
@@ -0,0 +1,166 @@
+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 37307aadda57d1e5b86180b9b2b8a6318ec36095)
+++ frontend/src/pages/Dashboard/pages/Weight/Weight.jsx	(revision 708af9efba48db0445f07728b5d091a3d8898876)
@@ -1,12 +1,164 @@
-import React from "react";
+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="space-y-4">
-      <h1 className="text-2xl font-bold">Weight</h1>
-      <div className="card bg-base-200 border border-base-300">
-        <div className="card-body">
-          <p className="opacity-80">Weight dashboard content goes here.</p>
-        </div>
+    <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>
Index: frontend/src/pages/Dashboard/pages/Weight/WeightTracking.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Weight/WeightTracking.jsx	(revision 708af9efba48db0445f07728b5d091a3d8898876)
+++ frontend/src/pages/Dashboard/pages/Weight/WeightTracking.jsx	(revision 708af9efba48db0445f07728b5d091a3d8898876)
@@ -0,0 +1,232 @@
+import React, { useCallback, useEffect, useMemo, useState } from "react";
+import { useNavigate } from "react-router-dom";
+
+import api from "../../../../api/axios";
+import graphPlaceholder from "../../../../assets/graph-placeholder.svg";
+
+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 [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) => {
+    const [profileRes, intakesRes, trainingRes] = await Promise.all([
+      api.get("/weight/profile"),
+      api.get("/weight/intakes", {
+        params: { page: nextPage, size: pageSize },
+      }),
+      api.get("/weight/today-training"),
+    ]);
+
+    return {
+      profileData: profileRes?.data ?? null,
+      intakesData: intakesRes?.data ?? {},
+      trainingData: trainingRes?.data ?? null,
+    };
+  }, []);
+
+  useEffect(() => {
+    let cancelled = false;
+    (async () => {
+      try {
+        setIsLoading(true);
+        setError("");
+        const { profileData, intakesData, trainingData } = await fetchPage(0);
+        if (cancelled) return;
+        setProfile(profileData);
+        setIntakes(Array.isArray(intakesData.intakes) ? intakesData.intakes : []);
+        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;
+    };
+  }, [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 graphSrc={graphPlaceholder} />
+
+      <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 708af9efba48db0445f07728b5d091a3d8898876)
+++ frontend/src/pages/Dashboard/pages/Weight/components/WeightCenteredCard.jsx	(revision 708af9efba48db0445f07728b5d091a3d8898876)
@@ -0,0 +1,15 @@
+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 708af9efba48db0445f07728b5d091a3d8898876)
+++ frontend/src/pages/Dashboard/pages/Weight/components/WeightIntakesTable.jsx	(revision 708af9efba48db0445f07728b5d091a3d8898876)
@@ -0,0 +1,160 @@
+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 bg-green-400! text-black! hover:bg-green-500! border-0"
+            onClick={onAddIntake}
+            disabled={isLoading}
+          >
+            + Add today's intake
+          </button>
+
+          <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 708af9efba48db0445f07728b5d091a3d8898876)
+++ frontend/src/pages/Dashboard/pages/Weight/components/WeightProgressCard.jsx	(revision 708af9efba48db0445f07728b5d091a3d8898876)
@@ -0,0 +1,29 @@
+import React from "react";
+
+export default function WeightProgressCard({ graphSrc }) {
+  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">Progress</h2>
+            <p className="opacity-80">
+              Graph placeholder — calorie intake over time will appear here.
+            </p>
+          </div>
+          <span className="badge badge-ghost">Graph placeholder</span>
+        </div>
+        <div className="mt-4 overflow-hidden rounded-xl bg-base-300/30 border border-base-300">
+          <div className="p-4">
+            <img
+              src={graphSrc}
+              alt="Weight progress graph placeholder"
+              className="w-full max-h-64 object-contain opacity-90"
+            />
+          </div>
+        </div>
+      </div>
+    </div>
+  );
+}
+
Index: frontend/src/pages/Dashboard/pages/Weight/components/WeightStartCtaCard.jsx
===================================================================
--- frontend/src/pages/Dashboard/pages/Weight/components/WeightStartCtaCard.jsx	(revision 708af9efba48db0445f07728b5d091a3d8898876)
+++ frontend/src/pages/Dashboard/pages/Weight/components/WeightStartCtaCard.jsx	(revision 708af9efba48db0445f07728b5d091a3d8898876)
@@ -0,0 +1,27 @@
+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 708af9efba48db0445f07728b5d091a3d8898876)
+++ frontend/src/pages/Dashboard/pages/Weight/components/WeightStartForm.jsx	(revision 708af9efba48db0445f07728b5d091a3d8898876)
@@ -0,0 +1,181 @@
+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 708af9efba48db0445f07728b5d091a3d8898876)
+++ frontend/src/pages/Dashboard/pages/Weight/components/WeightSummaryCard.jsx	(revision 708af9efba48db0445f07728b5d091a3d8898876)
@@ -0,0 +1,63 @@
+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 708af9efba48db0445f07728b5d091a3d8898876)
+++ frontend/src/pages/Dashboard/pages/Weight/components/WeightTrackingHeader.jsx	(revision 708af9efba48db0445f07728b5d091a3d8898876)
@@ -0,0 +1,23 @@
+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>
+  );
+}
+
