diff --git a/src/app/targets/page.tsx b/src/app/targets/page.tsx
index a95989188..a7169f80d 100644
--- a/src/app/targets/page.tsx
+++ b/src/app/targets/page.tsx
@@ -27,6 +27,7 @@ import {
Droplet,
} from "lucide-react";
import { convertGlucose, resolveGlucoseUnit } from "@/lib/glucose";
+import { TargetsSummaryHeader } from "@/components/targets/targets-summary-header";
interface TargetData {
type: string;
@@ -699,6 +700,17 @@ export default function TargetsPage() {
)}
+ {/* v1.4.25 W3e — page-level summary line. Renders nothing when
+ the API hasn't shipped pageSummary yet (older clients during
+ rollout / mocked test fixtures that pre-date this addition). */}
+ {data.pageSummary && (
+
+ )}
+
{/* Target cards grid */}
{visibleTargets.length > 0 ? (
diff --git a/src/components/targets/__tests__/targets-summary-header.test.tsx b/src/components/targets/__tests__/targets-summary-header.test.tsx
new file mode 100644
index 000000000..5449d80b8
--- /dev/null
+++ b/src/components/targets/__tests__/targets-summary-header.test.tsx
@@ -0,0 +1,63 @@
+import { describe, expect, it } from "vitest";
+import { renderToStaticMarkup } from "react-dom/server";
+
+import { I18nProvider } from "@/lib/i18n/context";
+import { TargetsSummaryHeader } from "../targets-summary-header";
+
+/**
+ * v1.4.25 W3e — page-level summary line. Three load-bearing cases:
+ * 1. Partial: "4 of 6 targets met this week"
+ * 2. Full: "All 6 targets met this week" + check mark
+ * 3. With streak highlight chip
+ */
+
+function render(props: Parameters
[0]) {
+ return renderToStaticMarkup(
+
+
+ ,
+ );
+}
+
+describe("", () => {
+ it("renders the partial-count headline when met < total", () => {
+ const html = render({
+ targetsMetThisWeek: 4,
+ totalTargets: 6,
+ streakHighlight: null,
+ });
+ expect(html).toContain('data-slot="targets-summary-title"');
+ expect(html).toContain("4 of 6 targets met this week");
+ // Check mark should NOT render in the partial state.
+ expect(html).not.toContain("lucide-check");
+ });
+
+ it("renders the all-met headline + check mark when every target is met", () => {
+ const html = render({
+ targetsMetThisWeek: 6,
+ totalTargets: 6,
+ streakHighlight: null,
+ });
+ expect(html).toContain("All 6 targets met this week");
+ expect(html).toContain("text-[var(--dracula-green)]");
+ });
+
+ it("renders the streak highlight chip when present", () => {
+ const html = render({
+ targetsMetThisWeek: 4,
+ totalTargets: 6,
+ streakHighlight: { metric: "BLOOD_PRESSURE", days: 5 },
+ });
+ expect(html).toContain('data-slot="targets-summary-streak"');
+ expect(html).toContain("5-day streak: Blood pressure");
+ });
+
+ it("renders nothing when there are no targets at all", () => {
+ const html = render({
+ targetsMetThisWeek: 0,
+ totalTargets: 0,
+ streakHighlight: null,
+ });
+ expect(html).toBe("");
+ });
+});
diff --git a/src/components/targets/targets-summary-header.tsx b/src/components/targets/targets-summary-header.tsx
new file mode 100644
index 000000000..290670d1b
--- /dev/null
+++ b/src/components/targets/targets-summary-header.tsx
@@ -0,0 +1,91 @@
+"use client";
+
+import { CheckCircle2 } from "lucide-react";
+
+import { cn } from "@/lib/utils";
+import { useTranslations } from "@/lib/i18n/context";
+
+/**
+ * v1.4.25 W3e — page-level summary line.
+ *
+ * "4 of 6 targets met this week" (DE: "4 von 6 Zielwerten diese Woche
+ * erreicht"). When every target hits the bar we surface a calm check
+ * mark + "All N targets met this week" copy — no animation, no
+ * celebration. Marc directive: stay quieter than the v1.4.20 Insights
+ * hero.
+ *
+ * Optional streak highlight chip when one metric has a current ≥ 3 day
+ * streak; the metric label is resolved from the targets.label.* i18n
+ * map so it renders in the user's locale.
+ */
+export interface TargetsSummaryHeaderProps {
+ targetsMetThisWeek: number;
+ totalTargets: number;
+ streakHighlight: { metric: string; days: number } | null;
+ className?: string;
+}
+
+export function TargetsSummaryHeader({
+ targetsMetThisWeek,
+ totalTargets,
+ streakHighlight,
+ className,
+}: TargetsSummaryHeaderProps) {
+ const { t } = useTranslations();
+
+ if (totalTargets === 0) return null;
+
+ const allMet = targetsMetThisWeek === totalTargets;
+ const headline = allMet
+ ? t("targets.summary.weekTitleAll", { total: String(totalTargets) })
+ : t("targets.summary.weekTitle", {
+ met: String(targetsMetThisWeek),
+ total: String(totalTargets),
+ });
+
+ let highlightCopy: string | null = null;
+ if (streakHighlight && streakHighlight.days >= 3) {
+ const metricLabel = t(`targets.label.${streakHighlight.metric}`);
+ const resolvedMetric =
+ metricLabel === `targets.label.${streakHighlight.metric}`
+ ? streakHighlight.metric
+ : metricLabel;
+ highlightCopy = t("targets.summary.streakHighlight", {
+ count: String(streakHighlight.days),
+ metric: resolvedMetric,
+ });
+ }
+
+ return (
+
+
+ {allMet && (
+
+ )}
+
+ {headline}
+
+
+ {highlightCopy && (
+
+ {highlightCopy}
+
+ )}
+
+ );
+}