diff --git a/src/app/portfolio/[slug]/(portfolio)/reporting/ReportingClientArea.tsx b/src/app/portfolio/[slug]/(portfolio)/reporting/ReportingClientArea.tsx
index 8a6054b5..a9fb94c8 100644
--- a/src/app/portfolio/[slug]/(portfolio)/reporting/ReportingClientArea.tsx
+++ b/src/app/portfolio/[slug]/(portfolio)/reporting/ReportingClientArea.tsx
@@ -485,17 +485,25 @@ function MetricsBody({
: null;
const scenarioBusy = isScenario && isFetching;
+ // First entry into a scenario from Current stock: there's no previous overlay
+ // to keep, so the scenario KPIs are momentarily empty. Skeleton that band
+ // (the "no data yet" case → same pattern as first page load) rather than
+ // flashing an empty bar. A scenario→scenario switch keeps the old figures, so
+ // it stays on the dim+spinner path below instead.
+ const scenarioLoadingFirst = scenarioBusy && !scenarioData;
return (
- {scenarioBusy && (
+ {/* Refetch-with-data only: the figures stay on screen and dim, so a label
+ clarifies the dim means "updating". First load speaks via its skeleton. */}
+ {scenarioBusy && scenarioData && (
- {scenarioData ? "Updating figures…" : "Loading scenario…"}
+ Updating figures…
)}
@@ -509,7 +517,11 @@ function MetricsBody({
aria-busy={scenarioBusy}
>
{/* KPI band */}
-
+ {scenarioLoadingFirst ? (
+
+ ) : (
+
+ )}
{/* Charts + ledger */}
@@ -610,21 +622,34 @@ function ScenarioRowSkeleton() {
);
}
+/**
+ * The five-cell KPI band in its loading shape. Shared by the initial page-load
+ * skeleton and the first Current-stock → scenario switch, so both "no data yet"
+ * moments read identically.
+ */
+function KpiBandSkeleton() {
+ return (
+
+ {Array.from({ length: 5 }).map((_, i) => (
+
0 ? "border-l border-gray-100" : ""}`}
+ >
+
+
+
+ ))}
+
+ );
+}
+
function MetricsSkeleton() {
return (
- {/* KPI band */}
-
- {Array.from({ length: 5 }).map((_, i) => (
-
0 ? "border-l border-gray-100" : ""}`}
- >
-
-
-
- ))}
-
+
{/* Charts + side panel */}