diff --git a/frontend/components/dashboard/ScanTerminalLightTheme.module.css b/frontend/components/dashboard/ScanTerminalLightTheme.module.css
index 1918eb85..bb95acf3 100644
--- a/frontend/components/dashboard/ScanTerminalLightTheme.module.css
+++ b/frontend/components/dashboard/ScanTerminalLightTheme.module.css
@@ -104,8 +104,7 @@
.root :global(.scan-terminal.light .panel-meta-chip),
.root :global(.scan-terminal.light .panel-weather-chip),
.root :global(.scan-terminal.light .scan-distribution-card),
-.root :global(.scan-terminal.light .scan-chart-legend),
-.root :global(.scan-terminal.light .scan-kpi-note) {
+.root :global(.scan-terminal.light .scan-chart-legend) {
color: #94A3B8;
}
@@ -120,7 +119,6 @@
.root :global(.scan-terminal.light .scan-mode-tab),
.root :global(.scan-terminal.light .scan-table-shell),
.root :global(.scan-terminal.light .scan-table-header),
-.root :global(.scan-terminal.light .scan-kpi-card),
.root :global(.scan-terminal.light .scan-calendar-group),
.root :global(.scan-terminal.light .scan-calendar-card),
.root :global(.scan-terminal.light .scan-summary-card),
@@ -195,11 +193,6 @@
background: linear-gradient(180deg, rgba(248, 252, 255, 0.96), rgba(238, 247, 255, 0.96));
}
-.root :global(.scan-terminal.light .scan-kpi-card) {
- box-shadow: 0 10px 24px rgba(40, 70, 110, 0.08);
-}
-
-.root :global(.scan-terminal.light .scan-kpi-value),
.root :global(.scan-terminal.light .scan-opportunity-phase > span) {
color: #14253a;
}
@@ -472,7 +465,6 @@
.root :global(.scan-terminal.light .scan-ai-decision-metrics b),
.root :global(.scan-terminal.light .scan-ai-market-bucket strong),
.root :global(.scan-terminal.light .scan-ai-weather-summary),
-.root :global(.scan-terminal.light .scan-ai-decision-reasons small),
.root :global(.scan-terminal.light .scan-ai-market-decision strong),
.root :global(.scan-terminal.light .scan-ai-market-decision-stats b) {
color: #0f172a;
@@ -646,7 +638,6 @@
.root :global(.scan-terminal.light .panel-weather-chip),
.root :global(.scan-terminal.light .scan-distribution-card),
.root :global(.scan-terminal.light .scan-chart-legend),
-.root :global(.scan-terminal.light .scan-kpi-note),
.root :global(.scan-terminal.light .scan-calendar-subtitle),
.root :global(.scan-terminal.light .scan-calendar-action span),
.root :global(.scan-terminal.light .scan-calendar-countdown small),
diff --git a/frontend/components/dashboard/ScanTerminalList.module.css b/frontend/components/dashboard/ScanTerminalList.module.css
index a0165dd9..77c58a74 100644
--- a/frontend/components/dashboard/ScanTerminalList.module.css
+++ b/frontend/components/dashboard/ScanTerminalList.module.css
@@ -1,73 +1,6 @@
/* Scan terminal KPI, list tabs, table rows, v4 analysis, and AI workspace.
Kept separate from the shell/filter CSS so dense decision-board styles can evolve independently. */
-.root :global(.scan-kpi-bar) {
- grid-template-columns: repeat(4, minmax(0, 1fr));
- gap: 14px;
-}
-
-.root :global(.scan-kpi-card) {
- min-height: 124px;
- padding: 18px;
- border-radius: 16px;
- border: 1px solid rgba(92, 124, 170, 0.12);
- background: linear-gradient(180deg, rgba(14, 28, 48, 0.92), rgba(11, 21, 37, 0.94));
- transform: none;
-}
-
-.root :global(.scan-kpi-card:hover) {
- transform: translateY(-2px);
-}
-
-.root :global(.scan-kpi-card::after) {
- display: none;
-}
-
-.root :global(.scan-kpi-card.green .scan-kpi-label) {
- color: #17d98b;
-}
-
-.root :global(.scan-kpi-card.cyan .scan-kpi-label) {
- color: #37d6ff;
-}
-
-.root :global(.scan-kpi-card.purple .scan-kpi-label) {
- color: #a35cff;
-}
-
-.root :global(.scan-kpi-card.blue .scan-kpi-label) {
- color: #49a3ff;
-}
-
-.root :global(.scan-kpi-card.red .scan-kpi-label) {
- color: #ff647c;
-}
-
-.root :global(.scan-kpi-card.amber .scan-kpi-label) {
- color: #ffb84f;
-}
-
-.root :global(.scan-kpi-card.orange .scan-kpi-label) {
- color: #F59E0B;
-}
-
-.root :global(.scan-kpi-label) {
- font-size: 13px;
- text-transform: none;
- letter-spacing: 0;
-}
-
-.root :global(.scan-kpi-value) {
- margin-top: 10px;
- font-size: 28px;
-}
-
-.root :global(.scan-kpi-note) {
- margin-top: 8px;
- font-size: 14px;
- color: #a8bedb;
-}
-
.root :global(.scan-list-section) {
display: flex;
flex-direction: column;
diff --git a/frontend/components/dashboard/ScanTerminalMobile.module.css b/frontend/components/dashboard/ScanTerminalMobile.module.css
index ede3850f..fade389c 100644
--- a/frontend/components/dashboard/ScanTerminalMobile.module.css
+++ b/frontend/components/dashboard/ScanTerminalMobile.module.css
@@ -176,10 +176,6 @@
max-width: 100%;
}
- .root :global(.scan-kpi-bar) {
- grid-template-columns: repeat(2, minmax(0, 1fr));
- }
-
.root :global(.scan-topbar) {
flex-direction: column;
align-items: flex-start;
@@ -310,10 +306,6 @@
font-size: 13px;
}
- .root :global(.scan-kpi-bar) {
- grid-template-columns: 1fr;
- }
-
.root :global(.scan-topbar-tabs) {
gap: 14px;
flex-wrap: wrap;
@@ -389,12 +381,6 @@
font-size: 20px;
}
- .root :global(.scan-ai-decision-long),
- .root :global(.scan-ai-decision-reasons),
- .root :global(.scan-ai-decision-risk) {
- display: none;
- }
-
.root :global(.scan-ai-market-mobile-line) {
display: flex;
align-items: center;
diff --git a/frontend/components/dashboard/scan-terminal/AiForecastKPIBar.tsx b/frontend/components/dashboard/scan-terminal/AiForecastKPIBar.tsx
deleted file mode 100644
index 39117d30..00000000
--- a/frontend/components/dashboard/scan-terminal/AiForecastKPIBar.tsx
+++ /dev/null
@@ -1,77 +0,0 @@
-import type { CityDetail, ScanOpportunityRow } from "@/lib/dashboard-types";
-import { getTodayPaceView } from "@/lib/pace-utils";
-import { formatTemperatureValue } from "@/lib/temperature-utils";
-import { getPeakWindowLabel } from "@/components/dashboard/scan-terminal/decision-utils";
-
-export function AiForecastKPIBar({
- pinnedCount,
- activeCityName,
- activeDetail,
- activeRow,
- locale,
-}: {
- pinnedCount: number;
- activeCityName?: string | null;
- activeDetail?: CityDetail | null;
- activeRow?: ScanOpportunityRow | null;
- locale: string;
-}) {
- const isEn = locale === "en-US";
- const tempSymbol = activeDetail?.temp_symbol || activeRow?.temp_symbol || "°C";
- const displayName =
- activeDetail?.display_name ||
- activeRow?.city_display_name ||
- activeRow?.display_name ||
- activeCityName ||
- "--";
- const deb = activeDetail?.deb?.prediction ?? activeRow?.deb_prediction ?? null;
- const paceView = activeDetail
- ? getTodayPaceView(activeDetail, locale as "zh-CN" | "en-US")
- : null;
- const peakWindow =
- paceView?.peakWindowText ||
- (activeRow ? getPeakWindowLabel(activeRow) : null) ||
- "--";
- const cards = [
- {
- label: isEn ? "Decision Cards" : "决策卡",
- value: String(pinnedCount),
- note: isEn ? "Cities opened from opportunities or map" : "从机会榜或地图加入",
- tone: "green",
- },
- {
- label: isEn ? "Current City" : "当前城市",
- value: displayName,
- note: isEn ? "City briefing stays in the right rail" : "右侧城市简报同步显示,不自动切页",
- tone: "blue",
- },
- {
- label: isEn ? "Forecast Center" : "预测中枢",
- value:
- deb != null
- ? formatTemperatureValue(deb, tempSymbol, { digits: 1 })
- : "--",
- note: isEn ? "Weather center before price mapping" : "先定天气中枢,再映射价格",
- tone: "cyan",
- },
- {
- label: isEn ? "Peak Window" : "峰值窗口",
- value: peakWindow,
- note: isEn ? "Key window for daily high judgment" : "用于判断是否接近今日最高温",
- tone: "amber",
- },
- ];
-
- return (
-
- {cards.map((card) => (
-
- {card.label}
- {card.value}
- {card.note}
-
- ))}
-
- );
-}
-
diff --git a/frontend/components/dashboard/scan-terminal/AiPinnedCityCard.tsx b/frontend/components/dashboard/scan-terminal/AiPinnedCityCard.tsx
index 71c56c98..ec08036d 100644
--- a/frontend/components/dashboard/scan-terminal/AiPinnedCityCard.tsx
+++ b/frontend/components/dashboard/scan-terminal/AiPinnedCityCard.tsx
@@ -613,15 +613,12 @@ export function AiPinnedCityCard({
{detail && !collapsed ? (
diff --git a/frontend/components/dashboard/scan-terminal/WeatherDecisionBand.tsx b/frontend/components/dashboard/scan-terminal/WeatherDecisionBand.tsx
index 54b542ad..da2b622b 100644
--- a/frontend/components/dashboard/scan-terminal/WeatherDecisionBand.tsx
+++ b/frontend/components/dashboard/scan-terminal/WeatherDecisionBand.tsx
@@ -8,25 +8,19 @@ import type {
import { MarketDecisionLine } from "@/components/dashboard/scan-terminal/MarketDecisionLine";
export function WeatherDecisionBand({
- currentTempText,
decisionView,
decisionWhyText,
isEn,
- longText,
marketDecisionView,
marketLineText,
paceDeltaText,
- peakWindow,
}: {
- currentTempText: string;
decisionView: WeatherDecisionView;
decisionWhyText: string;
isEn: boolean;
- longText: string;
marketDecisionView: MarketDecisionView;
marketLineText: string;
paceDeltaText: string;
- peakWindow: string;
}) {
return (
@@ -34,13 +28,6 @@ export function WeatherDecisionBand({
{decisionView.kicker}
{decisionView.action}
{decisionWhyText}
- {longText}
-
- {decisionView.reasons.map((reason, index) => (
- {reason}
- ))}
-
- {decisionView.risk}
-
- {isEn ? "Expected high" : "预计高点"}
- {decisionView.expectedHigh}
-
{isEn ? "Weather range" : "天气区间"}
{decisionView.targetRange}
@@ -60,22 +43,9 @@ export function WeatherDecisionBand({
{isEn ? "Confidence" : "信心"}
{decisionView.confidence}
-
- {isEn ? "Observed" : "实测"}
- {currentTempText}
-
{isEn ? "Path delta" : "路径偏差"} {paceDeltaText}
-
- {isEn ? "Peak window" : "峰值窗口"} {peakWindow}
-
-
- {isEn ? "Market implied" : "市场隐含"} {marketDecisionView.impliedText}
-
-
- {isEn ? "Model prob" : "模型概率"} {marketDecisionView.modelText}
-
{isEn ? "Quote status" : "报价状态"}{" "}
{marketDecisionView.status === "ready" ? (isEn ? "Ready" : "已同步") : marketDecisionView.status === "loading" ? (isEn ? "Loading" : "同步中") : (isEn ? "Unavailable" : "不可用")}
diff --git a/frontend/components/dashboard/scan-terminal/city-decision-state.ts b/frontend/components/dashboard/scan-terminal/city-decision-state.ts
index 38ccd1f0..f3a99b37 100644
--- a/frontend/components/dashboard/scan-terminal/city-decision-state.ts
+++ b/frontend/components/dashboard/scan-terminal/city-decision-state.ts
@@ -142,31 +142,31 @@ export function buildCityDecisionState({
: "watch";
const primaryReason = observedHighBreak
? isEn
- ? "Worth watching now: observation has broken above the model range."
- : "当前值得关注:实测已突破模型上沿。"
+ ? "Observation has broken above the model range."
+ : "实测已突破模型上沿。"
: peakHasPassed
? isEn
- ? "Avoid chasing now: peak window has passed; wait to confirm no new high."
- : "当前不宜追高:峰值窗口已过,等待确认是否还有新高。"
+ ? "Peak window has passed; confirm whether a new high can still form."
+ : "峰值窗口已过,确认是否还会出现新高。"
: observationStale
? isEn
- ? "Use as background only: observation is stale and needs the next report."
- : "当前仅作背景:观测已过旧,需要下一报文确认。"
+ ? "Observation is stale and needs the next report."
+ : "观测已过旧,需要下一报文确认。"
: marketStatus === "unavailable"
? isEn
- ? "Weather evidence remains usable, but no tradable quote is available yet."
- : "当前可参考天气:暂无可交易价格。"
+ ? "Weather evidence is usable, but no tradable quote is available yet."
+ : "天气证据可参考,但暂无可交易价格。"
: modelHighlyConsistent
? isEn
- ? "Worth watching now: models agree; wait for observation confirmation."
- : "当前值得关注:模型高度一致,等待实测确认。"
+ ? "Models are aligned; wait for observation confirmation."
+ : "模型高度一致,等待实测确认。"
: needsNextBulletin
? isEn
- ? "Wait for confirmation: the next bulletin should decide direction."
- : "当前建议等待:下一报文更适合决定方向。"
+ ? "The next bulletin is more likely to decide direction."
+ : "下一报文更可能决定方向。"
: isEn
- ? "Watch the peak window and compare observations against the expected high."
- : "当前重点:盯住峰值窗口,把实测与预计高点对照。";
+ ? "Compare new observations with the expected high through the peak window."
+ : "在峰值窗口内继续对照实测与预计高点。";
const badges = uniqueStatusBadges([
observedHighBreak