diff --git a/apps/manacore/apps/landing/src/content/config.ts b/apps/manacore/apps/landing/src/content/config.ts
index 174094d91..f9c24b494 100644
--- a/apps/manacore/apps/landing/src/content/config.ts
+++ b/apps/manacore/apps/landing/src/content/config.ts
@@ -203,6 +203,15 @@ const manascoreCollection = defineCollection({
security: z.number().min(0).max(100),
ux: z.number().min(0).max(100),
}),
+ // Score history for trend visualization
+ history: z
+ .array(
+ z.object({
+ date: z.string(), // ISO date string e.g. "2026-03-19"
+ score: z.number().min(0).max(100),
+ })
+ )
+ .optional(),
// Readiness level
status: z.enum(['prototype', 'alpha', 'beta', 'production', 'mature']),
// Stats
diff --git a/apps/manacore/apps/landing/src/content/manascore/2026-03-19-calendar.md b/apps/manacore/apps/landing/src/content/manascore/2026-03-19-calendar.md
index 6dd45aa66..51c017d18 100644
--- a/apps/manacore/apps/landing/src/content/manascore/2026-03-19-calendar.md
+++ b/apps/manacore/apps/landing/src/content/manascore/2026-03-19-calendar.md
@@ -6,6 +6,9 @@ app: 'calendar'
author: 'Till Schneider'
tags: ['audit', 'calendar', 'production-readiness']
score: 97
+history:
+ - { date: '2026-03-19', score: 82 }
+ - { date: '2026-03-24', score: 97 }
scores:
backend: 95
frontend: 96
diff --git a/apps/manacore/apps/landing/src/content/manascore/2026-03-19-todo.md b/apps/manacore/apps/landing/src/content/manascore/2026-03-19-todo.md
index 72b19af93..af1d13587 100644
--- a/apps/manacore/apps/landing/src/content/manascore/2026-03-19-todo.md
+++ b/apps/manacore/apps/landing/src/content/manascore/2026-03-19-todo.md
@@ -6,6 +6,9 @@ app: 'todo'
author: 'Till Schneider'
tags: ['audit', 'todo', 'production-readiness']
score: 96
+history:
+ - { date: '2026-03-19', score: 80 }
+ - { date: '2026-03-24', score: 96 }
scores:
backend: 94
frontend: 95
diff --git a/apps/manacore/apps/landing/src/pages/manascore/[slug].astro b/apps/manacore/apps/landing/src/pages/manascore/[slug].astro
index a5b0264db..3bb58aedd 100644
--- a/apps/manacore/apps/landing/src/pages/manascore/[slug].astro
+++ b/apps/manacore/apps/landing/src/pages/manascore/[slug].astro
@@ -101,23 +101,100 @@ function getBarColor(score: number): string {
{audit.data.description}
- {/* Overall Score */}
-
-
-
-
Gesamtscore
-
Gewichteter Durchschnitt aus 8 Kategorien
-
-
-
- {audit.data.score}
-
- /100
-
-
-
+ {/* Overall Score + Trend */}
+ {
+ (() => {
+ const history = audit.data.history || [
+ { date: audit.data.date.toISOString(), score: audit.data.score },
+ ];
+ const hasTrend = history.length > 1;
+ const trendDelta = hasTrend ? history[history.length - 1].score - history[0].score : 0;
+ const minScore = Math.min(...history.map((h: { score: number }) => h.score)) - 5;
+ const maxScore = Math.max(...history.map((h: { score: number }) => h.score)) + 5;
+ const range = maxScore - minScore || 1;
+ const chartWidth = 200;
+ const chartHeight = 60;
+ const points = history.map((h: { score: number }, i: number) => {
+ const x =
+ history.length > 1 ? (i / (history.length - 1)) * chartWidth : chartWidth / 2;
+ const y = chartHeight - ((h.score - minScore) / range) * (chartHeight - 4) - 2;
+ return { x, y, score: h.score, date: h.date };
+ });
+ const polyline = points.map((p: { x: number; y: number }) => `${p.x},${p.y}`).join(' ');
+ const areaPath = `M${points[0].x},${chartHeight} ${points.map((p: { x: number; y: number }) => `L${p.x},${p.y}`).join(' ')} L${points[points.length - 1].x},${chartHeight} Z`;
+
+ return (
+
+
+
+
Gesamtscore
+
+ Gewichteter Durchschnitt aus 8 Kategorien
+
+ {hasTrend && (
+
= 0 ? 'text-green-500' : 'text-red-500'}`}
+ >
+ {trendDelta > 0 ? '↑' : '↓'} {Math.abs(trendDelta)} Punkte seit erstem
+ Assessment
+
+ )}
+
+
+ {hasTrend && (
+
+ )}
+
+
+ {audit.data.score}
+
+ /100
+
+
+
+
+ );
+ })()
+ }
{/* Category Scores */}
a.data.status))];
const status = statusColors[data.status] || statusColors.alpha;
const scoreColor = getScoreColor(data.score);
+ // Build sparkline SVG path from history
+ const history = data.history || [{ date: data.date.toISOString(), score: data.score }];
+ const sparkWidth = 48;
+ const sparkHeight = 20;
+ const minScore = Math.min(...history.map((h: { score: number }) => h.score));
+ const maxScore = Math.max(...history.map((h: { score: number }) => h.score));
+ const range = maxScore - minScore || 1;
+ const sparkPoints = history
+ .map((h: { score: number }, i: number) => {
+ const x =
+ history.length > 1 ? (i / (history.length - 1)) * sparkWidth : sparkWidth / 2;
+ const y = sparkHeight - ((h.score - minScore) / range) * (sparkHeight - 2) - 1;
+ return `${x},${y}`;
+ })
+ .join(' ');
+ const hasTrend = history.length > 1;
+ const trendDelta = hasTrend ? history[history.length - 1].score - history[0].score : 0;
+
return (
a.data.status))];
)}
- {/* Score */}
-
+ {/* Score + Trend */}
+
{data.score}
-
/100
+ {hasTrend ? (
+
+
+
= 0 ? 'text-green-500' : 'text-red-500'}`}
+ >
+ {trendDelta > 0 ? '+' : ''}
+ {trendDelta}
+
+
+ ) : (
+
/100
+ )}