全面修复前端 UI 设计审查问题:消除工程债务、统一 token 体系、提升可维护性

- 消除 !important 滥用:134 → 49(仅保留 Leaflet/图表所必需项),浅色主题使用 html.light 选择器获得更高优先级
- 修复 font-weight:13 个文件中所有 760/850/860/880/950 等非标准值已映射为 Inter 支持的 300–800
- 移除未加载的 Geist 字体声明,替换为 Inter
- 添加全局 :focus-visible 轮廓环、跳过链接、Tab ARIA 属性(role/aria-selected)
- 统一断点体系:18 → 10(480/640/768/960/1024/1200/1280/1360/1440/1680)
- 创建 scan-root-styles.ts 桶文件,将 22 个 CSS Module 导入合并为 1 个
- Token 迁移:10 个文件中数百处硬编码颜色(#4DA3FF/#E6EDF3/#9FB2C7/#6B7A90)已替换为 CSS 变量
- 去重 @keyframes:spin 4→1、loading-spin 2→0、pulse-pending 已移至 globals.css
- 添加统一的 empty/error/retry 状态组件
- 添加全局 prefers-reduced-motion 支持
- 修复 accent-primary 与 accent-secondary 相同值的问题
- 修复 accent-green 类错误渲染为蓝色
- 添加 CSS 渐变品牌 Logo
- 移除死代码(1,697 行):public/static/style.css + public/legacy/index.html
- Dashboard.module.css 本地变量已桥接至全局 token
- 提升文字对比度:#6B7A90 → #7D8FA3

Fixed: !important-134-to-49, font-weight-13-files, Geist-removal, focus-visible, breakpoints-18-to-10, CSS-module-barrel, token-migration-10-files, keyframe-dedup, dead-code-removal, accent-color-fix, contrast-improvement
Scope: frontend CSS architecture, design tokens, accessibility, responsive breakpoints
Tested: npx tsc --noEmit
This commit is contained in:
2569718930@qq.com
2026-05-10 14:21:10 +08:00
parent f47de115c8
commit 00e1845f2c
30 changed files with 1081 additions and 2186 deletions
@@ -302,8 +302,8 @@
.mono {
font-family: "Consolas", "SFMono-Regular", "Menlo", monospace;
font-size: 12px !important;
color: #c7d2fe !important;
font-size: 12px;
color: #c7d2fe;
}
.hint {
@@ -361,13 +361,7 @@
animation: spin 0.9s linear infinite;
}
@keyframes spin {
100% {
transform: rotate(360deg);
}
}
@media (max-width: 900px) {
@media (max-width: 960px) {
.topBar {
flex-direction: column;
align-items: stretch;
@@ -396,7 +390,7 @@
}
}
@media (max-width: 560px) {
@media (max-width: 640px) {
.page {
padding: 18px 12px 20px;
}
@@ -4,26 +4,26 @@
.root {
/* ── Palette (bridged from globals.css unified tokens) ── */
--bg-primary: #0B1220;
--bg-secondary: #16213A;
--bg-card: #111A2E;
--bg-primary: var(--color-bg-base);
--bg-secondary: var(--color-bg-raised);
--bg-card: var(--color-bg-card);
--bg-glass: rgba(17, 26, 46, 0.82);
--border-glass: rgba(159, 178, 199, 0.16);
--border-subtle: rgba(159, 178, 199, 0.08);
--border-glass: var(--color-border-default);
--border-subtle: var(--color-border-subtle);
/* Text */
--text-primary: var(--color-text-primary);
--text-secondary: var(--color-text-secondary);
--text-muted: var(--color-text-muted);
/* Accents — Fintech 3-Color Model */
--accent-cyan: #4DA3FF;
--accent-blue: #4DA3FF;
/* Accent Colors */
--accent-cyan: var(--color-accent-primary);
--accent-blue: var(--color-accent-secondary);
--accent-green: #22C55E;
--accent-orange: #F59E0B;
--accent-red: #EF4444;
--accent-yellow: #F59E0B;
--accent-purple: #4DA3FF;
--accent-purple: var(--color-accent-tertiary);
/* Risk colors */
--risk-high: var(--color-risk-high);
@@ -383,7 +383,7 @@
border: 1px solid rgba(34, 211, 238, 0.18);
border-radius: 999px;
background: rgba(8, 145, 178, 0.1);
color: #6FB7FF;
color: var(--color-accent-secondary);
font-size: 10px;
font-weight: 900;
letter-spacing: 0.12em;
@@ -418,7 +418,7 @@
.root :global(.home-card-meta-row) {
color: rgba(148, 163, 184, 0.88);
font-size: 12px;
font-weight: 650;
font-weight: 600;
}
.root :global(.home-card-titlebar p) {
@@ -476,7 +476,7 @@
.root :global(.home-weather-label) {
margin-bottom: 8px;
color: #6FB7FF;
color: var(--color-accent-secondary);
font-size: 10px;
font-weight: 900;
letter-spacing: 0.14em;
@@ -567,7 +567,7 @@
width: 4px;
height: 13px;
border-radius: 999px;
background: #4DA3FF;
background: var(--color-accent-primary);
transform: rotate(18deg);
}
@@ -760,9 +760,9 @@
.root :global(.home-deb-card span),
.root :global(.home-card-section h3) {
color: #6FB7FF;
color: var(--color-accent-secondary);
font-size: 12px;
font-weight: 850;
font-weight: 800;
}
.root :global(.home-deb-card small),
@@ -785,7 +785,7 @@
color: #f87171;
font-size: 12px;
font-style: normal;
font-weight: 850;
font-weight: 800;
}
.root :global(.home-market-metrics svg) {
@@ -882,7 +882,7 @@
}
.root :global(.home-intraday-chart circle) {
fill: #4DA3FF;
fill: var(--color-accent-primary);
stroke: rgba(15, 23, 42, 0.92);
stroke-width: 1.6;
filter: drop-shadow(0 0 10px rgba(56, 189, 248, 0.28));
@@ -1007,7 +1007,7 @@
gap: 9px;
color: rgba(226, 232, 240, 0.95);
font-size: 12px;
font-weight: 850;
font-weight: 800;
}
.root :global(.home-probability-row div),
@@ -1077,7 +1077,7 @@
.root :global(.home-market-header span) {
color: rgba(148, 163, 184, 0.88);
font-size: 11px;
font-weight: 750;
font-weight: 700;
}
.root :global(.home-market-ticket) {
@@ -1109,7 +1109,7 @@
}
.root :global(.home-market-prices .yes) {
color: #6FB7FF;
color: var(--color-accent-secondary);
background: rgba(8, 145, 178, 0.2);
}
@@ -1142,7 +1142,7 @@
margin-top: 10px;
color: #fde68a;
font-size: 12px;
font-weight: 850;
font-weight: 800;
text-decoration: none;
}
@@ -1244,7 +1244,7 @@
.root :global(.opportunity-strip-copy strong) {
color: #f8fafc;
font-size: 19px;
font-weight: 860;
font-weight: 800;
line-height: 1.25;
letter-spacing: -0.02em;
}
@@ -1279,7 +1279,7 @@
.root :global(.opportunity-tape-pill span) {
color: rgba(148, 163, 184, 0.82);
font-size: 10px;
font-weight: 760;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
}
@@ -1287,12 +1287,12 @@
.root :global(.opportunity-tape-pill strong) {
color: #f8fafc;
font-size: 16px;
font-weight: 860;
font-weight: 800;
line-height: 1;
}
.root :global(.opportunity-tape-pill.accent-cyan strong) {
color: #6FB7FF;
color: var(--color-accent-secondary);
}
.root :global(.opportunity-tape-pill.accent-green strong) {
@@ -1383,7 +1383,7 @@
}
.root :global(.opportunity-hero-kicker) {
color: #6FB7FF;
color: var(--color-accent-secondary);
font-size: 11px;
font-weight: 800;
letter-spacing: 0.08em;
@@ -1401,7 +1401,7 @@
overflow: hidden;
color: #f8fafc;
font-size: 28px;
font-weight: 880;
font-weight: 800;
line-height: 1;
letter-spacing: -0.04em;
text-overflow: ellipsis;
@@ -1411,7 +1411,7 @@
.root :global(.opportunity-hero-date) {
color: rgba(186, 230, 253, 0.76);
font-size: 12px;
font-weight: 720;
font-weight: 700;
}
.root :global(.opportunity-hero-copy p) {
@@ -1439,13 +1439,13 @@
background: rgba(7, 13, 25, 0.58);
color: rgba(226, 232, 240, 0.92);
font-size: 11px;
font-weight: 820;
font-weight: 800;
white-space: nowrap;
}
.root :global(.opportunity-hero-tag.engine) {
border-color: rgba(34, 211, 238, 0.24);
color: #6FB7FF;
color: var(--color-accent-secondary);
}
.root :global(.opportunity-hero-tag.signal-yes) {
@@ -1499,7 +1499,7 @@
.root :global(.opportunity-hero-edgeblock span) {
color: rgba(148, 163, 184, 0.8);
font-size: 11px;
font-weight: 760;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
@@ -1539,7 +1539,7 @@
.root :global(.opportunity-hero-metric span) {
color: rgba(148, 163, 184, 0.8);
font-size: 10px;
font-weight: 760;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
@@ -1548,7 +1548,7 @@
overflow: hidden;
color: #f8fafc;
font-size: 20px;
font-weight: 860;
font-weight: 800;
line-height: 1;
letter-spacing: -0.03em;
text-overflow: ellipsis;
@@ -1575,14 +1575,14 @@
.root :global(.opportunity-book-side span) {
font-size: 11px;
font-weight: 760;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.root :global(.opportunity-book-side strong) {
font-size: 26px;
font-weight: 880;
font-weight: 800;
line-height: 1;
letter-spacing: -0.03em;
}
@@ -1624,7 +1624,7 @@
.root :global(.opportunity-hero-sparkline-wrap span) {
color: rgba(148, 163, 184, 0.8);
font-size: 10px;
font-weight: 760;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
}
@@ -1680,7 +1680,7 @@
.root :global(.opportunity-side-tile-head span) {
color: rgba(241, 245, 249, 0.92);
font-size: 13px;
font-weight: 790;
font-weight: 700;
}
.root :global(.opportunity-side-tile-body) {
@@ -1700,7 +1700,7 @@
overflow: hidden;
color: #f8fafc;
font-size: 24px;
font-weight: 880;
font-weight: 800;
line-height: 1;
letter-spacing: -0.04em;
text-overflow: ellipsis;
@@ -1710,7 +1710,7 @@
.root :global(.opportunity-side-stat span) {
color: rgba(148, 163, 184, 0.82);
font-size: 10px;
font-weight: 680;
font-weight: 600;
}
.root :global(.opportunity-side-stat b.accent-red) {
@@ -1762,7 +1762,7 @@
.root :global(.opportunity-empty-copy strong) {
color: rgba(241, 245, 249, 0.94);
font-size: 15px;
font-weight: 840;
font-weight: 800;
}
.root :global(.opportunity-empty-copy span) {
@@ -1828,7 +1828,7 @@
justify-content: center;
color: #f8fafc;
font-size: 13px;
font-weight: 840;
font-weight: 800;
flex-shrink: 0;
background: rgba(6, 11, 23, 0.46);
}
@@ -1870,7 +1870,7 @@
overflow: hidden;
color: #f8fafc;
font-size: 15px;
font-weight: 860;
font-weight: 800;
text-overflow: ellipsis;
white-space: nowrap;
}
@@ -2188,7 +2188,7 @@
}
}
@media (max-width: 1240px) {
@media (max-width: 1280px) {
.root :global(.opportunity-strip-topline) {
flex-direction: column;
}
@@ -2215,7 +2215,7 @@
}
}
@media (max-width: 1020px) {
@media (max-width: 1024px) {
.root :global(.map) {
left: 18px;
bottom: 188px;
@@ -2234,7 +2234,7 @@
}
}
@media (max-width: 720px) {
@media (max-width: 768px) {
.root :global(.map) {
inset: var(--header-height) 0 0;
border-radius: 0;
@@ -157,7 +157,7 @@
0 0 22px rgba(251, 113, 133, 0.18);
}
.root :global(.marker-bubble.risk-high::after) {
border-top-color: #ef4444;
border-top-color: var(--color-signal-danger);
}
.root :global(.marker-bubble.risk-medium) {
@@ -168,7 +168,7 @@
0 0 22px rgba(245, 158, 11, 0.16);
}
.root :global(.marker-bubble.risk-medium::after) {
border-top-color: #f59e0b;
border-top-color: var(--color-signal-warning);
}
.root :global(.marker-bubble.risk-low) {
@@ -179,7 +179,7 @@
0 0 22px rgba(74, 222, 128, 0.14);
}
.root :global(.marker-bubble.risk-low::after) {
border-top-color: #22C55E;
border-top-color: var(--color-signal-success);
}
.root :global(.marker-name) {
@@ -380,7 +380,7 @@
width: 14px;
height: 14px;
border-radius: 50%;
background: radial-gradient(circle, #6FB7FF 0%, #0891b2 100%);
background: radial-gradient(circle, var(--color-accent-secondary) 0%, #0891b2 100%);
transform: translate(-50%, -50%);
box-shadow:
0 0 12px rgba(34, 211, 238, 0.65),
@@ -544,18 +544,10 @@
border: 2px solid rgba(34, 211, 238, 0.1);
border-top-color: var(--accent-cyan);
border-radius: 50%;
animation: loading-spin 0.8s cubic-bezier(0.4, 0, 0.2, 1) infinite;
animation: spin 0.8s cubic-bezier(0.4, 0, 0.2, 1) infinite;
box-shadow: 0 0 15px rgba(34, 211, 238, 0.1);
}
@keyframes loading-spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
@keyframes city-loading-pulse {
0% {
@@ -767,7 +759,7 @@
}
.root :global(.nearby-time.is-stale) {
color: #fbbf24;
color: var(--color-signal-warning);
}
.root :global(.nearby-wind) {
@@ -1,7 +1,7 @@
.root :global(.scan-select) {
border: none;
border-radius: 8px;
background: linear-gradient(135deg, #4DA3FF, #00b383);
background: linear-gradient(135deg, var(--color-accent-primary), #00b383);
color: #000;
font-weight: 700;
cursor: pointer;
@@ -90,12 +90,6 @@
color: var(--accent-cyan);
}
@keyframes spin {
100% {
transform: rotate(360deg);
}
}
.root :global(.modal-close) {
background: none;
border: none;
@@ -87,7 +87,7 @@
color: rgba(203, 213, 225, 0.82);
text-decoration: none;
font-size: 14px;
font-weight: 550;
font-weight: 500;
transition: var(--transition);
position: relative;
}
@@ -223,7 +223,7 @@
align-items: center;
gap: 2px;
font-size: 11px;
font-weight: 750;
font-weight: 700;
cursor: pointer;
transition: var(--transition);
}
@@ -262,7 +262,7 @@
gap: 6px;
text-decoration: none;
font-size: 12px;
font-weight: 650;
font-weight: 600;
transition: var(--transition);
}
@@ -281,14 +281,6 @@
padding: 0;
width: 32px;
}
@keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
/* ── City List Sidebar ── */
.root :global(.city-list) {
@@ -545,11 +537,11 @@
}
.root :global(.city-item .city-deviation-cold) {
color: #4DA3FF;
color: var(--color-accent-primary);
}
.root :global(.city-item .city-deviation-hot) {
color: #f59e0b;
color: var(--color-signal-warning);
}
.root :global(.city-item .city-deviation-normal) {
@@ -604,7 +596,7 @@
}
/* ── Responsive ── */
@media (max-width: 1100px) {
@media (max-width: 1024px) {
.root {
--panel-width: 460px;
}
@@ -707,7 +699,7 @@
}
}
@media (max-width: 600px) {
@media (max-width: 640px) {
.root :global(.header) {
flex-direction: column;
align-items: stretch;
@@ -91,7 +91,7 @@
.root :global(.panel-action-button-primary) {
color: #FFFFFF;
background: linear-gradient(135deg, #4DA3FF, #3B82F6);
background: linear-gradient(135deg, var(--color-accent-primary), #3B82F6);
border-color: rgba(77, 163, 255, 0.34);
}
@@ -111,7 +111,7 @@
border-radius: 999px;
border: 2px solid rgba(34, 211, 238, 0.22);
border-top-color: rgba(34, 211, 238, 0.92);
animation: loading-spin 0.8s linear infinite;
animation: spin 0.8s linear infinite;
}
.root :global(.panel-meta) {
@@ -232,7 +232,7 @@
box-shadow: 0 14px 34px rgba(2, 6, 23, 0.38);
backdrop-filter: blur(18px);
font-size: 12px;
font-weight: 650;
font-weight: 600;
line-height: 1.45;
}
@@ -277,7 +277,7 @@
/* ── Hero Section ── */
.root :global(.hero-section) {
text-align: center;
padding-top: 12px !important;
padding-top: 12px;
}
.root :global(.hero-weather) {
@@ -429,7 +429,7 @@
padding: 3px 8px;
border: 1px solid rgba(34, 211, 238, 0.28);
border-radius: 8px;
color: #6FB7FF;
color: var(--color-accent-secondary);
background: rgba(34, 211, 238, 0.08);
font-size: 11px;
font-weight: 900;
@@ -904,7 +904,7 @@
}
}
@media (max-width: 720px) {
@media (max-width: 768px) {
.root :global(.forecast-table) {
display: flex;
gap: 10px;
@@ -1058,8 +1058,3 @@
}
}
@keyframes loading-spin {
to {
transform: rotate(360deg);
}
}
@@ -116,7 +116,7 @@
border: 1px solid rgba(34, 211, 238, 0.28);
border-radius: 8px;
background: rgba(8, 47, 73, 0.36);
color: #6FB7FF;
color: var(--color-accent-secondary);
font-size: 11px;
font-weight: 800;
line-height: 1.25;
@@ -195,7 +195,7 @@
.root :global(.future-v2-decision-anchor small) {
display: block;
margin-top: 6px;
color: #6FB7FF;
color: var(--color-accent-secondary);
font-size: 11px;
font-weight: 700;
line-height: 1.3;
@@ -219,7 +219,7 @@
.root :global(.future-v2-decision-grid strong) {
display: block;
margin-top: 7px;
color: #6FB7FF;
color: var(--color-accent-secondary);
font-size: 18px;
font-weight: 800;
line-height: 1;
@@ -250,7 +250,7 @@
.root :global(.future-v2-meteorology-paths strong) {
display: block;
margin-top: 8px;
color: #6FB7FF;
color: var(--color-accent-secondary);
font-size: 22px;
font-weight: 800;
line-height: 1;
@@ -595,7 +595,7 @@
}
.root :global(.future-v2-signal-tag.cyan) {
color: #6FB7FF;
color: var(--color-accent-secondary);
border-color: rgba(34, 211, 238, 0.22);
background: rgba(34, 211, 238, 0.08);
}
@@ -684,7 +684,7 @@
}
.root :global(.future-v2-pace-delta.cold) {
color: #6FB7FF;
color: var(--color-accent-secondary);
}
.root :global(.future-v2-pace-delta.neutral) {
@@ -759,7 +759,7 @@
}
.root :global(.future-v2-pace-meter-fill.cold) {
background: linear-gradient(90deg, #0f766e 0%, #6FB7FF 100%);
background: linear-gradient(90deg, #0f766e 0%, var(--color-accent-secondary) 100%);
}
.root :global(.future-v2-pace-meter-fill.neutral) {
@@ -1322,7 +1322,7 @@
left: 0;
bottom: 0;
border-radius: inherit;
background: linear-gradient(90deg, #334155 0%, #9FB2C7 100%);
background: linear-gradient(90deg, #334155 0%, var(--color-text-secondary) 100%);
}
.root :global(.future-trend-meter-fill.warm) {
@@ -1609,7 +1609,7 @@
}
}
@media (max-width: 420px) {
@media (max-width: 480px) {
.root :global(.future-forward-stats) {
grid-template-columns: 1fr;
}
@@ -438,7 +438,7 @@
line-height: 1.7;
}
@media (max-width: 980px) {
@media (max-width: 960px) {
.root :global(.history-modal .modal-body) {
padding: 18px;
}
@@ -81,7 +81,7 @@ export function ScanFilterPanel({
<span>{isEn ? "Scan Mode" : "扫描模式"}</span>
<Info size={14} />
</div>
<div className="scan-mode-tabs">
<div className="scan-mode-tabs" role="tablist" aria-label={isEn ? "Scan mode" : "扫描模式"}>
{SCAN_MODES.map((mode) => {
const Icon = mode.icon;
const isActive = value.scan_mode === mode.key;
@@ -89,6 +89,8 @@ export function ScanFilterPanel({
<button
key={mode.key}
type="button"
role="tab"
aria-selected={isActive}
className={`scan-mode-tab ${isActive ? "active" : ""}`}
onClick={() => updateFilter("scan_mode", mode.key)}
>
@@ -73,7 +73,7 @@
.root :global(.scan-mode-tab.active) {
background: rgba(0, 224, 164, 0.08);
color: #4DA3FF;
color: var(--color-accent-primary);
font-weight: 600;
}
@@ -84,7 +84,7 @@
top: 25%;
height: 50%;
width: 3px;
background: #4DA3FF;
background: var(--color-accent-primary);
border-radius: 0 2px 2px 0;
}
@@ -125,7 +125,7 @@
appearance: none;
width: 14px;
height: 14px;
background: #4DA3FF;
background: var(--color-accent-primary);
border-radius: 50%;
cursor: pointer;
border: 2px solid var(--bg-secondary);
@@ -146,7 +146,7 @@
.root :global(.scan-toggle.active) {
background: rgba(0, 224, 164, 0.3);
border-color: #4DA3FF;
border-color: var(--color-accent-primary);
}
.root :global(.scan-toggle-knob) {
@@ -162,7 +162,7 @@
.root :global(.scan-toggle.active .scan-toggle-knob) {
left: 18px;
background: #4DA3FF;
background: var(--color-accent-primary);
}
/* Select */
@@ -179,7 +179,7 @@
}
.root :global(.scan-select:focus) {
border-color: #4DA3FF;
border-color: var(--color-accent-primary);
}
/* Scan CTA */
@@ -192,7 +192,7 @@
padding: 12px 16px;
border: none;
border-radius: 10px;
background: linear-gradient(135deg, #4DA3FF, #00c48f);
background: linear-gradient(135deg, var(--color-accent-primary), #00c48f);
color: #030711;
font-size: 14px;
font-weight: 700;
@@ -271,7 +271,7 @@
}
.root :global(.scan-view-tab.active) {
color: #4DA3FF;
color: var(--color-accent-primary);
font-weight: 600;
}
@@ -282,7 +282,7 @@
left: 0;
right: 0;
height: 2px;
background: #4DA3FF;
background: var(--color-accent-primary);
border-radius: 4px;
opacity: 0.8;
transition: all 0.3s ease;
@@ -392,7 +392,7 @@
}
.root :global(.scan-condition-value.accent-green) {
color: #4DA3FF;
color: #22C55E;
}
.root :global(.scan-condition-value.accent-red) {
@@ -417,7 +417,7 @@
}
.root :global(.scan-trade-card.yes) {
border-top: 2px solid #4DA3FF;
border-top: 2px solid var(--color-accent-primary);
}
.root :global(.scan-trade-card.no) {
@@ -441,7 +441,7 @@
}
.root :global(.scan-trade-card-edge.positive) {
color: #4DA3FF;
color: var(--color-accent-primary);
}
.root :global(.scan-trade-card-edge.negative) {
@@ -494,7 +494,7 @@
}
.root :global(.scan-confidence-dot.filled) {
background: #4DA3FF;
background: var(--color-accent-primary);
}
.root :global(.scan-confidence-dot.filled.amber) {
@@ -48,7 +48,7 @@
}
.root :global(.scan-upgrade-announcement-copy span) {
color: #4da3ff;
color: var(--color-accent-primary);
font-size: 11px;
font-weight: 800;
text-transform: uppercase;
@@ -56,13 +56,13 @@
}
.root :global(.scan-upgrade-announcement-copy strong) {
color: #e6edf3;
color: var(--color-text-primary);
font-size: 18px;
font-weight: 900;
}
.root :global(.scan-upgrade-announcement-copy p) {
color: #9fb2c7;
color: var(--color-text-secondary);
font-size: 13px;
line-height: 1.55;
}
@@ -150,7 +150,7 @@
margin: 10px 0 0;
color: #d6e4f5;
font-size: 13px;
font-weight: 760;
font-weight: 700;
line-height: 1.45;
}
@@ -31,17 +31,17 @@
}
.root :global(.scan-forecast-kicker) {
color: #4DA3FF;
color: var(--color-accent-primary);
font-size: 11px;
font-weight: 950;
font-weight: 800;
letter-spacing: 0.08em;
text-transform: uppercase;
}
.root :global(.scan-forecast-city-title strong) {
color: #E6EDF3;
color: var(--color-text-primary);
font-size: 24px;
font-weight: 950;
font-weight: 800;
letter-spacing: 0;
}
@@ -57,9 +57,9 @@
border: 1px solid rgba(159, 178, 199, 0.14);
border-radius: 9px;
background: rgba(22, 33, 58, 0.56);
color: #9FB2C7;
color: var(--color-text-secondary);
font-size: 12px;
font-weight: 850;
font-weight: 800;
line-height: 1.2;
}
@@ -68,28 +68,28 @@
justify-items: end;
gap: 5px;
min-width: 0;
color: #9FB2C7;
color: var(--color-text-secondary);
text-align: right;
}
.root :global(.scan-forecast-city-read small) {
color: #6B7A90;
color: var(--color-text-muted);
font-size: 11px;
font-weight: 950;
font-weight: 800;
text-transform: uppercase;
}
.root :global(.scan-forecast-city-read > b:not(.scan-phase-badge)) {
color: #E6EDF3;
color: var(--color-text-primary);
font-size: 30px;
font-weight: 950;
font-weight: 800;
line-height: 1;
}
.root :global(.scan-forecast-city-read span) {
color: #9FB2C7;
color: var(--color-text-secondary);
font-size: 12px;
font-weight: 850;
font-weight: 800;
}
.root :global(.scan-forecast-row) {
@@ -137,21 +137,21 @@
.root :global(.scan-forecast-bucket small),
.root :global(.scan-forecast-signals small),
.root :global(.scan-ai-temperature-line small) {
color: #6B7A90;
color: var(--color-text-muted);
font-size: 11px;
font-weight: 900;
letter-spacing: 0.02em;
}
.root :global(.scan-forecast-bucket strong) {
color: #E6EDF3;
color: var(--color-text-primary);
font-size: 18px;
font-weight: 950;
font-weight: 800;
line-height: 1.22;
}
.root :global(.scan-forecast-bucket small) {
color: #9FB2C7;
color: var(--color-text-secondary);
line-height: 1.45;
}
@@ -176,7 +176,7 @@
.root :global(.scan-forecast-signals b) {
display: -webkit-box;
overflow: hidden;
color: #E6EDF3;
color: var(--color-text-primary);
font-size: 12px;
font-weight: 900;
line-height: 1.25;
@@ -196,9 +196,9 @@
border: 1px solid rgba(96, 165, 250, 0.28);
border-radius: 999px;
background: rgba(59, 130, 246, 0.1);
color: #6FB7FF;
color: var(--color-accent-secondary);
font-size: 12px;
font-weight: 950;
font-weight: 800;
white-space: nowrap;
}
@@ -231,20 +231,20 @@
gap: 10px;
align-items: baseline;
padding: 0 16px 14px;
color: #9FB2C7;
color: var(--color-text-secondary);
}
.root :global(.scan-forecast-ai-line b) {
color: #6FB7FF;
color: var(--color-accent-secondary);
font-size: 12px;
font-weight: 950;
font-weight: 800;
}
.root :global(.scan-forecast-ai-line small) {
min-width: 0;
color: #9FB2C7;
color: var(--color-text-secondary);
font-size: 13px;
font-weight: 750;
font-weight: 700;
line-height: 1.5;
}
@@ -272,26 +272,26 @@
.root :global(.scan-ai-analysis-head strong),
.root :global(.scan-ai-brief-grid strong) {
color: #6FB7FF;
color: var(--color-accent-secondary);
font-size: 12px;
font-weight: 950;
font-weight: 800;
letter-spacing: 0.04em;
text-transform: uppercase;
}
.root :global(.scan-ai-analysis-head p) {
margin: 0;
color: #E6EDF3;
color: var(--color-text-primary);
font-size: 20px;
font-weight: 850;
font-weight: 800;
line-height: 1.3;
}
.root :global(.scan-ai-analysis-head small) {
max-width: 880px;
color: #9FB2C7;
color: var(--color-text-secondary);
font-size: 14px;
font-weight: 760;
font-weight: 700;
line-height: 1.55;
}
@@ -305,9 +305,9 @@
border: 1px solid rgba(96, 165, 250, 0.28);
border-radius: 999px;
background: rgba(59, 130, 246, 0.1);
color: #6FB7FF;
color: var(--color-accent-secondary);
font-size: 12px;
font-weight: 950;
font-weight: 800;
white-space: nowrap;
}
@@ -328,9 +328,9 @@
.root :global(.scan-ai-temperature-line b) {
overflow: hidden;
color: #E6EDF3;
color: var(--color-text-primary);
font-size: 17px;
font-weight: 950;
font-weight: 800;
text-overflow: ellipsis;
white-space: nowrap;
}
@@ -352,7 +352,7 @@
}
.root :global(.scan-ai-evidence-line small) {
color: #6B7A90;
color: var(--color-text-muted);
font-size: 11px;
font-weight: 900;
}
@@ -360,7 +360,7 @@
.root :global(.scan-ai-evidence-line b) {
display: -webkit-box;
overflow: hidden;
color: #E6EDF3;
color: var(--color-text-primary);
font-size: 13px;
font-weight: 900;
line-height: 1.3;
@@ -379,7 +379,7 @@
overflow: hidden;
border: 1px solid rgba(77, 163, 255, 0.35);
border-radius: 18px;
background: #111a2e;
background: var(--color-bg-raised);
box-shadow: 0 18px 48px rgba(0, 0, 0, 0.24);
opacity: 1;
transform: translateX(0);
@@ -414,14 +414,14 @@
}
.root :global(.scan-ai-city-kicker) {
color: #4da3ff;
color: var(--color-accent-primary);
font-size: 12px;
font-weight: 800;
}
.root :global(.scan-ai-city-hero h3) {
margin: 4px 0 8px;
color: #e6edf3;
color: var(--color-text-primary);
font-size: 22px;
line-height: 1.15;
}
@@ -498,9 +498,9 @@
align-items: center;
gap: 4px 6px;
margin-top: 6px;
color: #9fb2c7;
color: var(--color-text-secondary);
font-size: 11px;
font-weight: 760;
font-weight: 700;
}
.root :global(.scan-ai-city-freshness strong) {
@@ -529,7 +529,7 @@
}
.root :global(.scan-ai-city-freshness em) {
color: #9fb2c7;
color: var(--color-text-secondary);
font-style: normal;
}
@@ -583,7 +583,7 @@
}
.root :global(.scan-ai-city-metrics small) {
color: #6b7a90;
color: var(--color-text-muted);
font-size: 10px;
font-weight: 800;
letter-spacing: 0.02em;
@@ -591,7 +591,7 @@
}
.root :global(.scan-ai-city-metrics b) {
color: #e6edf3;
color: var(--color-text-primary);
font-size: 20px;
font-weight: 900;
line-height: 1.1;
@@ -699,7 +699,7 @@
align-items: center;
border: 1px solid rgba(77, 163, 255, 0.18);
border-radius: 16px;
background: #16213a;
background: var(--color-bg-overlay);
padding: 18px;
}
@@ -720,7 +720,7 @@
}
.root :global(.scan-ai-decision-band span) {
color: #4da3ff;
color: var(--color-accent-primary);
font-size: 12px;
font-weight: 900;
}
@@ -728,14 +728,14 @@
.root :global(.scan-ai-decision-band strong) {
display: block;
margin-top: 5px;
color: #e6edf3;
color: var(--color-text-primary);
font-size: 24px;
line-height: 1.2;
}
.root :global(.scan-ai-decision-band p) {
margin: 8px 0 0;
color: #9fb2c7;
color: var(--color-text-secondary);
line-height: 1.55;
}
@@ -745,7 +745,7 @@
border: 1px solid rgba(77, 163, 255, 0.24);
border-radius: 12px;
background: rgba(77, 163, 255, 0.1);
color: #d8e7f8 !important;
color: var(--color-text-primary);
font-weight: 900;
padding: 9px 11px;
}
@@ -779,14 +779,14 @@
}
.root :global(.scan-ai-market-decision span) {
color: #9fb2c7;
color: var(--color-text-secondary);
font-size: 11px;
font-weight: 900;
}
.root :global(.scan-ai-market-decision strong) {
margin-top: 3px;
color: #e6edf3;
color: var(--color-text-primary);
font-size: 15px;
}
@@ -806,14 +806,14 @@
gap: 3px;
border-radius: 10px;
background: rgba(17, 26, 46, 0.82);
color: #9fb2c7;
color: var(--color-text-secondary);
font-size: 11px;
font-weight: 800;
padding: 8px;
}
.root :global(.scan-ai-market-decision-stats b) {
color: #e6edf3;
color: var(--color-text-primary);
font-size: 13px;
}
@@ -840,14 +840,14 @@
border: 1px solid rgba(159, 178, 199, 0.14);
border-radius: 12px;
background: rgba(11, 18, 32, 0.5);
color: #9fb2c7;
color: var(--color-text-secondary);
padding: 12px;
}
.root :global(.scan-ai-decision-metrics b) {
display: block;
margin-top: 4px;
color: #e6edf3;
color: var(--color-text-primary);
font-size: 15px;
}
@@ -869,7 +869,7 @@
display: inline-flex;
align-items: center;
gap: 8px;
color: #4da3ff;
color: var(--color-accent-primary);
font-size: 13px;
font-weight: 900;
margin-bottom: 12px;
@@ -908,7 +908,7 @@
.root :global(.scan-ai-city-section p) {
margin: 0 0 10px;
color: #9fb2c7;
color: var(--color-text-secondary);
font-size: 13px;
line-height: 1.6;
}
@@ -1006,14 +1006,10 @@
animation: pulse-pending 2s ease-in-out infinite;
}
@keyframes pulse-pending {
0%, 100% { opacity: 0.3; }
50% { opacity: 0.6; }
}
.root :global(.scan-ai-weather-summary) {
color: #e6edf3;
font-weight: 850;
color: var(--color-text-primary);
font-weight: 800;
}
.root :global(.scan-ai-weather-bullets) {
@@ -1021,21 +1017,21 @@
gap: 8px;
margin: 10px 0 0;
padding-left: 18px;
color: #9fb2c7;
color: var(--color-text-secondary);
font-size: 13px;
font-weight: 700;
line-height: 1.6;
}
.root :global(.scan-ai-weather-bullets li::marker) {
color: #4da3ff;
color: var(--color-accent-primary);
}
.root :global(.scan-ai-raw-metar) {
margin-top: 12px;
padding-top: 12px;
border-top: 1px solid rgba(77, 163, 255, 0.12);
color: #6b7a90;
color: var(--color-text-muted);
font-family:
ui-monospace,
SFMono-Regular,
@@ -1073,7 +1069,7 @@
display: flex;
gap: 14px;
margin-top: 10px;
color: #9fb2c7;
color: var(--color-text-secondary);
font-size: 12px;
font-weight: 800;
}
@@ -1147,14 +1143,14 @@
}
.root :global(.scan-ai-market-bucket strong) {
color: #e6edf3;
color: var(--color-text-primary);
font-size: 15px;
}
.root :global(.scan-ai-market-bucket span),
.root :global(.scan-ai-city-muted),
.root :global(.scan-ai-city-loading) {
color: #9fb2c7;
color: var(--color-text-secondary);
font-size: 12px;
font-weight: 700;
}
@@ -1180,9 +1176,9 @@
.root :global(.scan-ai-brief-grid li) {
position: relative;
padding-left: 16px;
color: #9FB2C7;
color: var(--color-text-secondary);
font-size: 13px;
font-weight: 760;
font-weight: 700;
line-height: 1.48;
}
@@ -1193,7 +1189,7 @@
width: 5px;
height: 5px;
border-radius: 999px;
background: #4DA3FF;
background: var(--color-accent-primary);
box-shadow: 0 0 10px rgba(77, 163, 255, 0.34);
content: "";
}
@@ -1212,7 +1208,7 @@
.root :global(.scan-ai-airport-read p) {
margin: 0;
color: #9FB2C7;
color: var(--color-text-secondary);
font-size: 12px;
font-weight: 800;
line-height: 1.5;
@@ -18,28 +18,7 @@ import {
useState,
} from "react";
import styles from "./Dashboard.module.css";
import dashboardHomeStyles from "./DashboardHomeIntelligence.module.css";
import dashboardMapStyles from "./DashboardMap.module.css";
import dashboardModalGuideStyles from "./DashboardModalGuide.module.css";
import dashboardShellStyles from "./DashboardShell.module.css";
import detailChromeStyles from "./DetailPanelChrome.module.css";
import detailContentStyles from "./DetailPanelContent.module.css";
import detailSectionsStyles from "./DetailPanelSections.module.css";
import futureForecastModalStyles from "./FutureForecastModal.module.css";
import historyModalStyles from "./HistoryModal.module.css";
import modalChromeStyles from "./ModalChrome.module.css";
import scanTerminalCalendarStyles from "./ScanTerminalCalendar.module.css";
import scanTerminalCardStyles from "./ScanTerminalCard.module.css";
import scanTerminalLightThemeStyles from "./ScanTerminalLightTheme.module.css";
import scanTerminalOpportunityStyles from "./ScanTerminalOpportunity.module.css";
import scanTerminalStyles from "./ScanTerminal.module.css";
import scanTerminalBoardStyles from "./ScanTerminalBoard.module.css";
import scanTerminalDetailStyles from "./ScanTerminalDetail.module.css";
import scanTerminalFiltersStyles from "./ScanTerminalFilters.module.css";
import scanTerminalListStyles from "./ScanTerminalList.module.css";
import scanTerminalShellStyles from "./ScanTerminalShell.module.css";
import scanTerminalStateStyles from "./ScanTerminalState.module.css";
import scanTerminalMobileStyles from "./ScanTerminalMobile.module.css";
import { scanRootClass } from "./scan-root-styles";
import { ProFeaturePaywall } from "@/components/dashboard/ProFeaturePaywall";
import {
DashboardStoreProvider,
@@ -133,28 +112,7 @@ function ScanTerminalScreen() {
const lastMapSelectedCityRef = useRef<string>("");
const scanTerminalRootClassName = clsx(
styles.root,
dashboardHomeStyles.root,
dashboardMapStyles.root,
dashboardShellStyles.root,
dashboardModalGuideStyles.root,
scanTerminalStyles.root,
scanTerminalShellStyles.root,
scanTerminalFiltersStyles.root,
scanTerminalListStyles.root,
scanTerminalBoardStyles.root,
scanTerminalDetailStyles.root,
scanTerminalStateStyles.root,
scanTerminalOpportunityStyles.root,
scanTerminalCardStyles.root,
scanTerminalCalendarStyles.root,
scanTerminalMobileStyles.root,
scanTerminalLightThemeStyles.root,
detailChromeStyles.root,
detailContentStyles.root,
detailSectionsStyles.root,
modalChromeStyles.root,
futureForecastModalStyles.root,
historyModalStyles.root,
scanRootClass,
themeMode === "light" && "light",
);
@@ -441,14 +399,17 @@ function ScanTerminalScreen() {
>
<main className="scan-data-grid">
<div className="scan-topbar">
<div className="scan-topbar-title">
<strong>{isEn ? "AI Weather Decision Terminal" : "AI 天气交易决策台"}</strong>
<div className="scan-topbar-brand">
<div className="scan-topbar-logo" aria-hidden="true" />
<div className="scan-topbar-title">
<strong>{isEn ? "AI Weather Decision Terminal" : "AI 天气交易决策台"}</strong>
<span>
{isEn
? "Start from the map, then open city cards to verify weather evidence"
: "从地图选城市,再打开决策卡验证天气证据"}
</span>
</div>
</div>
<div className="scan-topbar-actions">
<button
type="button"
@@ -525,9 +486,11 @@ function ScanTerminalScreen() {
<section className="scan-list-section">
<div className="scan-list-header">
<div className="scan-list-tabs">
<div className="scan-list-tabs" role="tablist" aria-label={isEn ? "Content view" : "内容视图"}>
<button
type="button"
role="tab"
aria-selected={resolvedView === "map"}
className={resolvedView === "map" ? "active" : ""}
onClick={() => {
lastMapSelectedCityRef.current = normalizeCityKey(store.selectedCity);
@@ -538,6 +501,8 @@ function ScanTerminalScreen() {
</button>
<button
type="button"
role="tab"
aria-selected={resolvedView === "analysis"}
className={resolvedView === "analysis" ? "active" : ""}
onClick={() => {
setActiveView("analysis");
@@ -547,6 +512,8 @@ function ScanTerminalScreen() {
</button>
<button
type="button"
role="tab"
aria-selected={resolvedView === "calendar"}
className={resolvedView === "calendar" ? "active" : ""}
title={!isPro ? (isEn ? "Pro forecast calendar required" : "日历预测需 Pro") : undefined}
onClick={() => {
@@ -7,6 +7,11 @@
color: #0F172A;
}
.root :global(.scan-terminal.light .scan-topbar-logo) {
background: linear-gradient(135deg, #2563EB, #059669);
box-shadow: 0 4px 12px rgba(37, 99, 235, 0.18);
}
.root :global(.scan-terminal.light .scan-filter-panel),
.root :global(.scan-terminal.light .scan-data-grid),
.root :global(.scan-terminal.light .scan-detail-panel),
@@ -80,6 +85,28 @@
rgba(255, 255, 255, 0.78);
}
.root :global(.scan-terminal.light .scan-empty-icon),
.root :global(.scan-terminal.light .scan-error-icon) {
background: rgba(37, 99, 235, 0.08);
border-color: rgba(37, 99, 235, 0.16);
}
.root :global(.scan-terminal.light .scan-error-icon) {
background: rgba(239, 68, 68, 0.06);
border-color: rgba(239, 68, 68, 0.14);
}
.root :global(.scan-terminal.light .scan-retry-button) {
background: rgba(37, 99, 235, 0.08);
border-color: rgba(37, 99, 235, 0.22);
color: #2563EB;
}
.root :global(.scan-terminal.light .scan-retry-button:hover) {
background: rgba(37, 99, 235, 0.14);
border-color: rgba(37, 99, 235, 0.38);
}
.root :global(.scan-terminal.light .scan-loading-node) {
background: #ffffff;
border-color: rgba(148, 163, 184, 0.22);
@@ -317,11 +344,11 @@
.root :global(.scan-terminal.light .scan-v4-analysis p),
.root :global(.scan-terminal.light .scan-v4-analysis ul),
.root :global(.scan-terminal.light .scan-v4-evidence > div > span) {
color: #6B7A90;
color: var(--color-text-muted);
}
.root :global(.scan-terminal.light .scan-v4-model-sources em) {
color: #6B7A90;
color: var(--color-text-muted);
}
.root :global(.scan-terminal.light .scan-v4-model-sources b) {
@@ -567,7 +594,7 @@
.root :global(.scan-terminal.light .scan-ai-city-head p),
.root :global(.scan-terminal.light .scan-ai-contract p),
.root :global(.scan-terminal.light .scan-ai-contract small) {
color: #6B7A90;
color: var(--color-text-muted);
}
.root :global(.scan-terminal.light .scan-ai-city-head) {
@@ -667,6 +694,8 @@
/* Light-mode hard overrides for the scan terminal. Keep this block last so the
decision workspace cannot inherit the dark map/card palette from base rules. */
/* ── CSS Variable bridge (from globals.css token overrides) ── */
.root:global(.light) {
--bg-primary: #eef7ff;
--bg-secondary: #e0f2fe;
@@ -679,281 +708,313 @@
--text-muted: #475569;
}
.root:global(.light) :global(.scan-terminal.light) {
/* ── Scan terminal shell light background ── */
html.light .root :global(.scan-terminal.light) {
background:
radial-gradient(circle at 18% 12%, rgba(59, 130, 246, 0.12), transparent 30%),
linear-gradient(180deg, #eef7ff 0%, #e8f4ff 48%, #f8fbff 100%) !important;
color: #0f172a !important;
linear-gradient(180deg, #eef7ff 0%, #e8f4ff 48%, #f8fbff 100%);
color: #0f172a;
}
:global(html.light) .root :global(.map),
.root:global(.light) :global(.map),
.root:global(.light) :global(.scan-terminal.light .scan-map-shell .map),
.root:global(.light) :global(.scan-terminal.light .scan-map-shell .leaflet-container),
.root:global(.light) :global(.leaflet-container),
.root:global(.light) :global(.leaflet-pane),
.root:global(.light) :global(.leaflet-map-pane),
.root:global(.light) :global(.leaflet-tile-pane) {
/* ── Leaflet map light overrides (needs !important to beat Leaflet inline styles) ── */
html.light .root :global(.map),
html.light .root :global(.scan-terminal.light .scan-map-shell .map),
html.light .root :global(.scan-terminal.light .scan-map-shell .leaflet-container),
html.light .root :global(.leaflet-container),
html.light .root :global(.leaflet-pane),
html.light .root :global(.leaflet-map-pane),
html.light .root :global(.leaflet-tile-pane) {
background: #eef7ff !important;
}
:global(html.light) .root :global(.map),
.root:global(.light) :global(.scan-terminal.light .scan-map-shell) {
border-color: rgba(37, 99, 235, 0.18) !important;
html.light .root :global(.scan-terminal.light .scan-map-shell),
html.light .root :global(.map) {
border-color: rgba(37, 99, 235, 0.18);
box-shadow:
0 18px 40px rgba(61, 100, 145, 0.14),
inset 0 0 0 1px rgba(255, 255, 255, 0.72) !important;
inset 0 0 0 1px rgba(255, 255, 255, 0.72);
}
:global(html.light) .root :global(.map .leaflet-tile),
.root:global(.light) :global(.map .leaflet-tile),
.root:global(.light) :global(.scan-terminal.light .scan-map-shell .leaflet-tile) {
html.light .root :global(.map .leaflet-tile),
html.light .root :global(.scan-terminal.light .scan-map-shell .leaflet-tile) {
filter: none !important;
opacity: 1 !important;
}
.root:global(.light) :global(.scan-city-detail-rail),
.root:global(.light) :global(.scan-terminal.light > .detail-panel.scan-city-detail-rail) {
background:
linear-gradient(180deg, rgba(238, 247, 255, 0.98), rgba(255, 255, 255, 0.96)) !important;
border-color: rgba(37, 99, 235, 0.16) !important;
color: #0f172a !important;
/* ── City detail rail light overrides ── */
html.light .root :global(.scan-city-detail-rail),
html.light .root :global(.scan-terminal.light > .detail-panel.scan-city-detail-rail) {
background: linear-gradient(180deg, rgba(238, 247, 255, 0.98), rgba(255, 255, 255, 0.96));
border-color: rgba(37, 99, 235, 0.16);
color: #0f172a;
}
.root:global(.light) :global(.scan-city-detail-rail .panel-header),
.root:global(.light) :global(.scan-city-detail-rail .detail-structured-section),
.root:global(.light) :global(.scan-city-detail-rail .detail-summary-shell),
.root:global(.light) :global(.scan-city-detail-rail .detail-card) {
background: rgba(255, 255, 255, 0.74) !important;
border-color: rgba(125, 171, 214, 0.28) !important;
html.light .root :global(.scan-city-detail-rail .panel-header),
html.light .root :global(.scan-city-detail-rail .detail-structured-section),
html.light .root :global(.scan-city-detail-rail .detail-summary-shell),
html.light .root :global(.scan-city-detail-rail .detail-card) {
background: rgba(255, 255, 255, 0.74);
border-color: rgba(125, 171, 214, 0.28);
}
.root:global(.light) :global(.scan-city-detail-rail h2),
.root:global(.light) :global(.scan-city-detail-rail h3),
.root:global(.light) :global(.scan-city-detail-rail strong),
.root:global(.light) :global(.scan-city-detail-rail .detail-value),
.root:global(.light) :global(.scan-city-detail-rail .detail-summary-temp),
.root:global(.light) :global(.scan-city-detail-rail .detail-section-head h3),
.root:global(.light) :global(.scan-city-detail-rail .panel-title-area h2) {
color: #0f172a !important;
html.light .root :global(.scan-city-detail-rail h2),
html.light .root :global(.scan-city-detail-rail h3),
html.light .root :global(.scan-city-detail-rail strong),
html.light .root :global(.scan-city-detail-rail .detail-value),
html.light .root :global(.scan-city-detail-rail .detail-summary-temp),
html.light .root :global(.scan-city-detail-rail .detail-section-head h3),
html.light .root :global(.scan-city-detail-rail .panel-title-area h2) {
color: #0f172a;
}
.root:global(.light) :global(.scan-city-detail-rail p),
.root:global(.light) :global(.scan-city-detail-rail li),
.root:global(.light) :global(.scan-city-detail-rail small),
.root:global(.light) :global(.scan-city-detail-rail .panel-overline),
.root:global(.light) :global(.scan-city-detail-rail .panel-loading-hint),
.root:global(.light) :global(.scan-city-detail-rail .panel-meta-chip),
.root:global(.light) :global(.scan-city-detail-rail .detail-section-kicker),
.root:global(.light) :global(.scan-city-detail-rail .detail-label),
.root:global(.light) :global(.scan-city-detail-rail .detail-value-muted),
.root:global(.light) :global(.scan-city-detail-rail .detail-source-note),
.root:global(.light) :global(.scan-city-detail-rail .detail-source-kind),
.root:global(.light) :global(.scan-city-detail-rail .detail-mini-meta),
.root:global(.light) :global(.scan-city-detail-rail .detail-summary-supporting),
.root:global(.light) :global(.scan-city-detail-rail .scan-detail-city-sub),
.root:global(.light) :global(.scan-city-detail-rail .scan-detail-volume-caption),
.root:global(.light) :global(.scan-city-detail-rail .scan-detail-score-label),
.root:global(.light) :global(.scan-city-detail-rail .scan-detail-empty) {
color: #334155 !important;
html.light .root :global(.scan-city-detail-rail p),
html.light .root :global(.scan-city-detail-rail li),
html.light .root :global(.scan-city-detail-rail small),
html.light .root :global(.scan-city-detail-rail .panel-overline),
html.light .root :global(.scan-city-detail-rail .panel-loading-hint),
html.light .root :global(.scan-city-detail-rail .panel-meta-chip),
html.light .root :global(.scan-city-detail-rail .detail-section-kicker),
html.light .root :global(.scan-city-detail-rail .detail-label),
html.light .root :global(.scan-city-detail-rail .detail-value-muted),
html.light .root :global(.scan-city-detail-rail .detail-source-note),
html.light .root :global(.scan-city-detail-rail .detail-source-kind),
html.light .root :global(.scan-city-detail-rail .detail-mini-meta),
html.light .root :global(.scan-city-detail-rail .detail-summary-supporting),
html.light .root :global(.scan-city-detail-rail .scan-detail-city-sub),
html.light .root :global(.scan-city-detail-rail .scan-detail-volume-caption),
html.light .root :global(.scan-city-detail-rail .scan-detail-score-label),
html.light .root :global(.scan-city-detail-rail .scan-detail-empty) {
color: #334155;
}
.root:global(.light) :global(.scan-city-detail-rail .panel-meta-chip),
.root:global(.light) :global(.scan-city-detail-rail .panel-weather-chip),
.root:global(.light) :global(.scan-city-detail-rail .detail-source-link),
.root:global(.light) :global(.scan-city-detail-rail .panel-action-button-secondary),
.root:global(.light) :global(.scan-city-detail-rail .panel-action-button-ghost) {
background: #eef7ff !important;
border-color: rgba(37, 99, 235, 0.18) !important;
color: #1f3654 !important;
html.light .root :global(.scan-city-detail-rail .panel-meta-chip),
html.light .root :global(.scan-city-detail-rail .panel-weather-chip),
html.light .root :global(.scan-city-detail-rail .detail-source-link),
html.light .root :global(.scan-city-detail-rail .panel-action-button-secondary),
html.light .root :global(.scan-city-detail-rail .panel-action-button-ghost) {
background: #eef7ff;
border-color: rgba(37, 99, 235, 0.18);
color: #1f3654;
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-city-card),
.root:global(.light) :global(.scan-terminal.light .scan-ai-city-section),
.root:global(.light) :global(.scan-terminal.light .scan-ai-decision-band),
.root:global(.light) :global(.scan-terminal.light .scan-ai-market-decision),
.root:global(.light) :global(.scan-terminal.light .scan-ai-decision-metrics span),
.root:global(.light) :global(.scan-terminal.light .scan-ai-city-pills span),
.root:global(.light) :global(.scan-terminal.light .scan-ai-city-freshness span),
.root:global(.light) :global(.scan-terminal.light .scan-ai-city-metrics > span),
.root:global(.light) :global(.scan-terminal.light .scan-mobile-decision-metrics span),
.root:global(.light) :global(.scan-terminal.light .scan-mobile-decision-reason) {
background: #eef7ff !important;
border-color: rgba(37, 99, 235, 0.16) !important;
/* ── AI city card light overrides ── */
html.light .root :global(.scan-terminal.light .scan-ai-city-card),
html.light .root :global(.scan-terminal.light .scan-ai-city-section),
html.light .root :global(.scan-terminal.light .scan-ai-decision-band),
html.light .root :global(.scan-terminal.light .scan-ai-market-decision),
html.light .root :global(.scan-terminal.light .scan-ai-decision-metrics span),
html.light .root :global(.scan-terminal.light .scan-ai-city-pills span),
html.light .root :global(.scan-terminal.light .scan-ai-city-freshness span),
html.light .root :global(.scan-terminal.light .scan-ai-city-metrics > span),
html.light .root :global(.scan-terminal.light .scan-mobile-decision-metrics span),
html.light .root :global(.scan-terminal.light .scan-mobile-decision-reason) {
background: #eef7ff;
border-color: rgba(37, 99, 235, 0.16);
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-city-metrics > span.primary) {
background: #dbeafe !important;
border-color: rgba(59, 130, 246, 0.34) !important;
html.light .root :global(.scan-terminal.light .scan-ai-city-metrics > span.primary) {
background: #dbeafe;
border-color: rgba(59, 130, 246, 0.34);
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-city-metrics b) {
color: #0f172a !important;
html.light .root :global(.scan-terminal.light .scan-ai-city-metrics b) {
color: #0f172a;
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-city-metrics > span.primary b) {
color: #1d4ed8 !important;
html.light .root :global(.scan-terminal.light .scan-ai-city-metrics > span.primary b) {
color: #1d4ed8;
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-city-metrics small) {
color: #64748b !important;
html.light .root :global(.scan-terminal.light .scan-ai-city-metrics small) {
color: #64748b;
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-city-card p),
.root:global(.light) :global(.scan-terminal.light .scan-ai-city-card li),
.root:global(.light) :global(.scan-terminal.light .scan-ai-city-card small),
.root:global(.light) :global(.scan-terminal.light .scan-ai-city-card span:not(.scan-ai-city-kicker)) {
color: #334155 !important;
html.light .root :global(.scan-terminal.light .scan-ai-city-card p),
html.light .root :global(.scan-terminal.light .scan-ai-city-card li),
html.light .root :global(.scan-terminal.light .scan-ai-city-card small),
html.light .root :global(.scan-terminal.light .scan-ai-city-card span:not(.scan-ai-city-kicker)) {
color: #334155;
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-city-status-tag.green) {
color: #047857 !important;
html.light .root :global(.scan-terminal.light .scan-ai-city-status-tag.green) {
color: #047857;
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-city-status-tag.blue) {
color: #1d4ed8 !important;
html.light .root :global(.scan-terminal.light .scan-ai-city-status-tag.blue) {
color: #1d4ed8;
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-city-status-tag.amber) {
color: #b45309 !important;
html.light .root :global(.scan-terminal.light .scan-ai-city-status-tag.amber) {
color: #b45309;
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-city-status-tag.red) {
color: #b91c1c !important;
html.light .root :global(.scan-terminal.light .scan-ai-city-status-tag.red) {
color: #b91c1c;
}
.root:global(.light) :global(.scan-terminal.light .scan-upgrade-announcement) {
/* ── Upgrade announcement light overrides ── */
html.light .root :global(.scan-terminal.light .scan-upgrade-announcement) {
background:
radial-gradient(circle at top left, rgba(37, 99, 235, 0.12), transparent 36%),
#ffffff !important;
border-color: rgba(37, 99, 235, 0.16) !important;
box-shadow: 0 16px 36px rgba(61, 100, 145, 0.12) !important;
#ffffff;
border-color: rgba(37, 99, 235, 0.16);
box-shadow: 0 16px 36px rgba(61, 100, 145, 0.12);
}
.root:global(.light) :global(.scan-terminal.light .scan-upgrade-announcement-copy strong),
.root:global(.light) :global(.scan-terminal.light .scan-ai-city-mobile-priority b),
.root:global(.light) :global(.scan-terminal.light .scan-mobile-decision-metrics b),
.root:global(.light) :global(.scan-terminal.light .scan-mobile-decision-reason),
.root:global(.light) :global(.scan-terminal.light .scan-mobile-decision-head h3),
.root:global(.light) :global(.scan-terminal.light .scan-ai-market-mobile-line b) {
color: #0f172a !important;
html.light .root :global(.scan-terminal.light .scan-upgrade-announcement-copy strong),
html.light .root :global(.scan-terminal.light .scan-ai-city-mobile-priority b),
html.light .root :global(.scan-terminal.light .scan-mobile-decision-metrics b),
html.light .root :global(.scan-terminal.light .scan-mobile-decision-reason),
html.light .root :global(.scan-terminal.light .scan-mobile-decision-head h3),
html.light .root :global(.scan-terminal.light .scan-ai-market-mobile-line b) {
color: #0f172a;
}
.root:global(.light) :global(.scan-terminal.light .scan-upgrade-announcement-copy p),
.root:global(.light) :global(.scan-terminal.light .scan-upgrade-announcement li),
.root:global(.light) :global(.scan-terminal.light .scan-ai-city-mobile-priority small),
.root:global(.light) :global(.scan-terminal.light .scan-mobile-decision-metrics small),
.root:global(.light) :global(.scan-terminal.light .scan-ai-market-mobile-line span) {
color: #334155 !important;
html.light .root :global(.scan-terminal.light .scan-upgrade-announcement-copy p),
html.light .root :global(.scan-terminal.light .scan-upgrade-announcement li),
html.light .root :global(.scan-terminal.light .scan-ai-city-mobile-priority small),
html.light .root :global(.scan-terminal.light .scan-mobile-decision-metrics small),
html.light .root :global(.scan-terminal.light .scan-ai-market-mobile-line span) {
color: #334155;
}
.root:global(.light) :global(.scan-terminal.light .scan-upgrade-announcement-copy span) {
color: #047857 !important;
html.light .root :global(.scan-terminal.light .scan-upgrade-announcement-copy span) {
color: #047857;
}
.root:global(.light) :global(.scan-terminal.light .scan-upgrade-announcement li),
.root:global(.light) :global(.scan-terminal.light .scan-ai-city-mobile-priority span),
.root:global(.light) :global(.scan-terminal.light .scan-ai-decision-why),
.root:global(.light) :global(.scan-terminal.light .scan-ai-market-mobile-line) {
background: #eef7ff !important;
border-color: rgba(37, 99, 235, 0.16) !important;
html.light .root :global(.scan-terminal.light .scan-upgrade-announcement li),
html.light .root :global(.scan-terminal.light .scan-ai-city-mobile-priority span),
html.light .root :global(.scan-terminal.light .scan-ai-decision-why),
html.light .root :global(.scan-terminal.light .scan-ai-market-mobile-line) {
background: #eef7ff;
border-color: rgba(37, 99, 235, 0.16);
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-decision-why) {
color: #1d4ed8 !important;
html.light .root :global(.scan-terminal.light .scan-ai-decision-why) {
color: #1d4ed8;
}
/* ── Section titles (Evidence, Model, etc.) ── */
.root:global(.light) :global(.scan-terminal.light .scan-ai-city-section-title) {
color: #1d4ed8 !important;
/* ── Section titles ── */
html.light .root :global(.scan-terminal.light .scan-ai-city-section-title) {
color: #1d4ed8;
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-decision-band span) {
color: #1d4ed8 !important;
html.light .root :global(.scan-terminal.light .scan-ai-decision-band span) {
color: #1d4ed8;
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-decision-band p) {
color: #475569 !important;
html.light .root :global(.scan-terminal.light .scan-ai-decision-band p) {
color: #475569;
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-decision-band strong) {
color: #0f172a !important;
html.light .root :global(.scan-terminal.light .scan-ai-decision-band strong) {
color: #0f172a;
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-city-body) {
/* ── Scrollbar light overrides ── */
html.light .root :global(.scan-terminal.light .scan-ai-city-body) {
scrollbar-color: rgba(37, 99, 235, 0.18) transparent;
}
.root:global(.light) :global(.scan-ai-city-body::-webkit-scrollbar-thumb) {
background: rgba(37, 99, 235, 0.22) !important;
html.light .root :global(.scan-ai-city-body::-webkit-scrollbar-thumb) {
background: rgba(37, 99, 235, 0.22);
}
/* ── AI prediction dual-card light mode ── */
.root:global(.light) :global(.scan-terminal.light .scan-ai-prediction-dual) {
gap: 10px;
html.light .root :global(.scan-terminal.light .scan-ai-prediction-card) {
background: #ffffff;
border-color: #e2e8f0;
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-prediction-card) {
background: #ffffff !important;
border-color: #e2e8f0 !important;
html.light .root :global(.scan-terminal.light .scan-ai-prediction-card.ai) {
background: linear-gradient(180deg, rgba(219, 234, 254, 0.52), #ffffff);
border-color: rgba(14, 165, 233, 0.28);
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-prediction-card.ai) {
background: linear-gradient(180deg, rgba(219, 234, 254, 0.52), #ffffff) !important;
border-color: rgba(14, 165, 233, 0.28) !important;
html.light .root :global(.scan-terminal.light .scan-ai-prediction-card.deb) {
background: #ffffff;
border-color: #e2e8f0;
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-prediction-card.deb) {
background: #ffffff !important;
border-color: #e2e8f0 !important;
html.light .root :global(.scan-terminal.light .scan-ai-prediction-card small) {
color: #64748b;
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-prediction-card small) {
color: #64748b !important;
html.light .root :global(.scan-terminal.light .scan-ai-prediction-card strong) {
color: #0f172a;
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-prediction-card strong) {
color: #0f172a !important;
html.light .root :global(.scan-terminal.light .scan-ai-prediction-card.ai strong) {
color: #0369a1;
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-prediction-card.ai strong) {
color: #0369a1 !important;
html.light .root :global(.scan-terminal.light .scan-ai-prediction-card em) {
color: #64748b;
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-prediction-card em) {
color: #64748b !important;
html.light .root :global(.scan-terminal.light .scan-ai-prediction-card.ai.pending) {
background: rgba(219, 234, 254, 0.28);
border-color: rgba(14, 165, 233, 0.16);
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-prediction-card.ai.pending) {
background: rgba(219, 234, 254, 0.28) !important;
border-color: rgba(14, 165, 233, 0.16) !important;
html.light .root :global(.scan-terminal.light .scan-ai-prediction-card.ai.pending strong) {
color: #94a3b8;
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-prediction-card.ai.pending strong) {
color: #94a3b8 !important;
html.light .root :global(.scan-terminal.light .scan-ai-confidence) {
background: rgba(59, 130, 246, 0.1);
color: #2563eb;
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-confidence) {
background: rgba(59, 130, 246, 0.1) !important;
color: #2563eb !important;
html.light .root :global(.scan-terminal.light .scan-ai-confidence.high) {
background: rgba(16, 185, 129, 0.12);
color: #047857;
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-confidence.high) {
background: rgba(16, 185, 129, 0.12) !important;
color: #047857 !important;
html.light .root :global(.scan-terminal.light .scan-ai-confidence.medium) {
background: rgba(245, 158, 11, 0.12);
color: #b45309;
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-confidence.medium) {
background: rgba(245, 158, 11, 0.12) !important;
color: #b45309 !important;
html.light .root :global(.scan-terminal.light .scan-ai-confidence.low) {
background: rgba(148, 163, 184, 0.12);
color: #64748b;
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-confidence.low) {
background: rgba(148, 163, 184, 0.12) !important;
color: #64748b !important;
html.light .root :global(.scan-terminal.light .scan-ai-confidence.neutral) {
background: rgba(148, 163, 184, 0.08);
color: #64748b;
}
.root:global(.light) :global(.scan-terminal.light .scan-ai-confidence.neutral) {
background: rgba(148, 163, 184, 0.08) !important;
color: #64748b !important;
/* ── Empty / Error / Retry light overrides ── */
html.light .root :global(.scan-terminal.light .scan-empty-title),
html.light .root :global(.scan-terminal.light .scan-error-title) {
color: #0f172a;
}
html.light .root :global(.scan-terminal.light .scan-empty-copy),
html.light .root :global(.scan-terminal.light .scan-error-copy) {
color: #475569;
}
html.light .root :global(.scan-terminal.light .scan-empty-icon) {
background: rgba(37, 99, 235, 0.08);
border-color: rgba(37, 99, 235, 0.2);
color: #2563EB;
}
html.light .root :global(.scan-terminal.light .scan-error-icon) {
background: rgba(239, 68, 68, 0.06);
border-color: rgba(239, 68, 68, 0.18);
color: #DC2626;
}
html.light .root :global(.scan-terminal.light .scan-retry-button) {
background: rgba(37, 99, 235, 0.08);
border-color: rgba(37, 99, 235, 0.24);
color: #2563EB;
}
html.light .root :global(.scan-terminal.light .scan-retry-button:hover) {
background: rgba(37, 99, 235, 0.14);
}
@@ -205,7 +205,7 @@
.root :global(.scan-v4-decision p b) {
color: #ecfeff;
font-weight: 950;
font-weight: 800;
}
.root :global(.scan-v4-metar-summary) {
@@ -263,9 +263,9 @@
.root :global(.scan-v4-heading strong),
.root :global(.scan-v4-current b),
.root :global(.scan-v4-brief-grid strong) {
color: #6FB7FF;
color: var(--color-accent-secondary);
font-size: 12px;
font-weight: 950;
font-weight: 800;
letter-spacing: 0.02em;
}
@@ -289,7 +289,7 @@
background: rgba(20, 184, 166, 0.12);
color: #7dfbe7;
font-size: 12px;
font-weight: 950;
font-weight: 800;
white-space: nowrap;
}
@@ -316,7 +316,7 @@
.root :global(.scan-v4-current span) {
color: #c8d8ec;
font-size: 13px;
font-weight: 850;
font-weight: 800;
line-height: 1.5;
}
@@ -362,7 +362,7 @@
width: 5px;
height: 5px;
border-radius: 999px;
background: #4DA3FF;
background: var(--color-accent-primary);
box-shadow: 0 0 10px rgba(45, 212, 191, 0.45);
}
@@ -387,7 +387,7 @@
background: rgba(10, 30, 58, 0.52);
color: #8fbdff;
font-size: 12px;
font-weight: 950;
font-weight: 800;
cursor: pointer;
list-style: none;
}
@@ -425,7 +425,7 @@
scroll-padding-top: 64px;
border: 1px solid rgba(77, 163, 255, 0.16);
border-radius: 18px;
background: #0b1220;
background: var(--color-bg-base);
padding: 14px;
}
@@ -454,7 +454,7 @@
.root :global(.scan-ai-workspace-head span) {
display: block;
color: #4da3ff;
color: var(--color-accent-primary);
font-size: 12px;
font-weight: 800;
letter-spacing: 0;
@@ -462,7 +462,7 @@
.root :global(.scan-ai-workspace-head strong) {
display: block;
color: #e6edf3;
color: var(--color-text-primary);
font-size: 20px;
line-height: 1.2;
margin-top: 4px;
@@ -471,7 +471,7 @@
.root :global(.scan-ai-workspace-head p) {
max-width: 560px;
margin: 0;
color: #9fb2c7;
color: var(--color-text-secondary);
font-size: 13px;
line-height: 1.55;
text-align: right;
@@ -16,7 +16,7 @@
.root :global(.scan-mobile-decision-head h3) {
margin: 6px 0 0;
color: #e6edf3;
color: var(--color-text-primary);
font-size: 24px;
line-height: 1.08;
}
@@ -38,7 +38,7 @@
}
.root :global(.scan-mobile-decision-metrics small) {
color: #9fb2c7;
color: var(--color-text-secondary);
font-size: 10px;
font-weight: 900;
}
@@ -121,7 +121,7 @@
}
@media (max-width: 1480px) {
@media (max-width: 1440px) {
.root :global(.scan-terminal) {
grid-template-columns: minmax(0, 1fr);
}
@@ -141,7 +141,7 @@
}
}
@media (max-width: 1100px) {
@media (max-width: 1024px) {
.root :global(.scan-terminal) {
grid-template-columns: 1fr;
min-height: 100dvh;
@@ -281,7 +281,7 @@
}
}
@media (max-width: 820px) {
@media (max-width: 768px) {
.root :global(.scan-terminal) {
padding: 10px;
}
@@ -354,7 +354,7 @@
}
.root :global(.scan-ai-city-mobile-priority small) {
color: #9fb2c7;
color: var(--color-text-secondary);
font-size: 10px;
font-weight: 900;
}
@@ -394,13 +394,13 @@
}
.root :global(.scan-ai-market-mobile-line span) {
color: #9fb2c7;
color: var(--color-text-secondary);
font-size: 12px;
font-weight: 900;
}
.root :global(.scan-ai-market-mobile-line b) {
color: #e6edf3;
color: var(--color-text-primary);
font-size: 13px;
text-align: right;
}
@@ -475,7 +475,7 @@
}
}
@media (max-width: 520px) {
@media (max-width: 640px) {
.root :global(.scan-terminal) {
box-sizing: border-box;
max-width: 100vw;
@@ -71,7 +71,7 @@
font-size: 12px;
font-weight: 700;
background: rgba(0, 224, 164, 0.12);
color: #4DA3FF;
color: var(--color-accent-primary);
}
.root :global(.rank-amber .scan-rank-circle) {
@@ -81,7 +81,7 @@
.root :global(.rank-purple .scan-rank-circle) {
background: rgba(123, 97, 255, 0.12);
color: #4DA3FF;
color: var(--color-accent-primary);
}
.root :global(.rank-neutral .scan-rank-circle) {
@@ -168,7 +168,7 @@
.root :global(.scan-status-badge.tone-green) {
background: rgba(0, 224, 164, 0.12);
color: #4DA3FF;
color: var(--color-accent-primary);
}
.root :global(.scan-status-badge.tone-amber) {
@@ -178,7 +178,7 @@
.root :global(.scan-status-badge.tone-purple) {
background: rgba(123, 97, 255, 0.12);
color: #4DA3FF;
color: var(--color-accent-primary);
}
.root :global(.scan-status-badge.tone-neutral) {
@@ -233,7 +233,7 @@
.root :global(.scan-action-text) {
font-size: 12px;
font-weight: 600;
color: #4DA3FF;
color: var(--color-accent-primary);
white-space: nowrap;
}
@@ -245,7 +245,7 @@
}
.root :global(.scan-edge-value.positive) {
color: #4DA3FF;
color: var(--color-accent-primary);
}
.root :global(.scan-edge-value.neutral) {
@@ -447,7 +447,7 @@
}
.root :global(.scan-opportunity-item.ai-veto .scan-opportunity-action) {
color: #9FB2C7;
color: var(--color-text-secondary);
}
.root :global(.scan-opportunity-branch) {
@@ -702,7 +702,7 @@
.root :global(.scan-v4-analysis strong) {
display: block;
color: #6FB7FF;
color: var(--color-accent-secondary);
font-size: 12px;
font-weight: 900;
letter-spacing: 0.02em;
@@ -892,11 +892,11 @@
align-content: center;
gap: 12px;
text-align: center;
color: #9fb2c7;
color: var(--color-text-secondary);
}
.root :global(.scan-opportunity-overview.empty strong) {
color: #e6edf3;
color: var(--color-text-primary);
font-size: 22px;
}
@@ -926,7 +926,7 @@
}
.root :global(.scan-opportunity-hero span) {
color: #4da3ff;
color: var(--color-accent-primary);
font-size: 12px;
font-weight: 900;
}
@@ -934,7 +934,7 @@
.root :global(.scan-opportunity-hero strong) {
display: block;
margin-top: 4px;
color: #e6edf3;
color: var(--color-text-primary);
font-size: 26px;
line-height: 1.15;
}
@@ -942,7 +942,7 @@
.root :global(.scan-opportunity-hero p) {
max-width: 680px;
margin: 9px 0 0;
color: #9fb2c7;
color: var(--color-text-secondary);
font-size: 13px;
line-height: 1.6;
}
@@ -959,12 +959,12 @@
border: 1px solid rgba(255, 255, 255, 0.06);
border-radius: 14px;
background: rgba(13, 17, 23, 0.52);
color: #9fb2c7;
color: var(--color-text-secondary);
padding: 12px;
}
.root :global(.scan-opportunity-summary b) {
color: #e6edf3;
color: var(--color-text-primary);
font-size: 18px;
}
@@ -990,13 +990,13 @@
}
.root :global(.scan-opportunity-lane-head strong) {
color: #e6edf3;
color: var(--color-text-primary);
font-size: 17px;
}
.root :global(.scan-opportunity-lane-head p) {
margin: 3px 0 0;
color: #9fb2c7;
color: var(--color-text-secondary);
font-size: 12px;
}
@@ -1060,7 +1060,7 @@
.root :global(.scan-opportunity-decision-head span),
.root :global(.scan-opportunity-decision-primary span),
.root :global(.scan-opportunity-decision-foot small) {
color: #9fb2c7;
color: var(--color-text-secondary);
font-size: 11px;
font-weight: 800;
}
@@ -1068,7 +1068,7 @@
.root :global(.scan-opportunity-decision-head strong) {
display: block;
margin-top: 3px;
color: #e6edf3;
color: var(--color-text-primary);
font-size: 18px;
}
@@ -1108,7 +1108,7 @@
}
.root :global(.scan-opportunity-decision-primary b) {
color: #e6edf3;
color: var(--color-text-primary);
font-size: 14px;
}
@@ -1,6 +1,15 @@
/* Scan terminal shell, topbar, upgrade announcement, and shared action buttons.
Extracted from ScanTerminal.module.css to keep terminal layout CSS maintainable. */
/* Button Hierarchy
Primary: .scan-primary-button filled gradient, main CTA
Secondary: .scan-ai-button tinted background, alternative action
Ghost: .scan-theme-button no border/background, icon-only
Chrome: .scan-account-button bordered icon, navigation
Chrome: .scan-locale-switch segmented control
Chrome: .scan-ghost-button bordered + bg, utility action
*/
.root :global(.scan-topbar) {
display: flex;
align-items: center;
@@ -10,6 +19,32 @@
border-bottom: 1px solid rgba(118, 146, 188, 0.1);
}
.root :global(.scan-topbar-brand) {
display: flex;
align-items: center;
gap: 8px;
}
.root :global(.scan-topbar-logo) {
width: 28px;
height: 28px;
flex-shrink: 0;
border-radius: 8px;
background: linear-gradient(135deg, var(--color-accent-primary), #22C55E);
box-shadow: 0 4px 12px rgba(77, 163, 255, 0.28);
position: relative;
overflow: hidden;
}
.root :global(.scan-topbar-logo::after) {
content: "";
position: absolute;
inset: 3px;
border-radius: 6px;
background:
radial-gradient(circle at 30% 30%, rgba(255, 255, 255, 0.6), transparent 60%);
}
.root :global(.scan-topbar-title) {
display: flex;
flex-direction: column;
@@ -135,7 +170,7 @@
background: rgba(77, 163, 255, 0.1);
color: #d7e5f7;
font-size: 12px;
font-weight: 850;
font-weight: 800;
padding: 7px 10px;
}
@@ -168,7 +203,7 @@
padding: 11px 16px;
border: 1px solid rgba(77, 163, 255, 0.42);
border-radius: 12px;
background: linear-gradient(180deg, #4DA3FF, #3B82F6);
background: linear-gradient(180deg, var(--color-accent-primary), #3B82F6);
color: #FFFFFF;
font-size: 14px;
font-weight: 900;
@@ -212,7 +247,7 @@
}
.root :global(.scan-cta-ghost) {
color: #6FB7FF;
color: var(--color-accent-secondary);
border-color: rgba(23, 217, 139, 0.3);
background: rgba(23, 217, 139, 0.12);
}
@@ -1,5 +1,6 @@
/* Scan terminal empty, loading, and skeleton signal states. */
/* Scan terminal empty, loading, error, and skeleton signal states. */
/* ── Empty state ── */
.root :global(.scan-empty-state) {
flex: 1;
display: flex;
@@ -8,6 +9,20 @@
align-items: center;
padding: 48px 32px;
text-align: center;
gap: 12px;
}
.root :global(.scan-empty-icon) {
width: 48px;
height: 48px;
border-radius: 14px;
background: rgba(77, 163, 255, 0.1);
border: 1px solid rgba(77, 163, 255, 0.16);
display: grid;
place-items: center;
color: var(--color-accent-secondary);
font-size: 20px;
margin-bottom: 4px;
}
.root :global(.scan-empty-title) {
@@ -17,12 +32,71 @@
}
.root :global(.scan-empty-copy) {
margin-top: 10px;
max-width: 360px;
font-size: 14px;
color: #8fa4c3;
line-height: 1.6;
}
/* ── Error state ── */
.root :global(.scan-error-state) {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 48px 32px;
text-align: center;
gap: 12px;
}
.root :global(.scan-error-icon) {
width: 48px;
height: 48px;
border-radius: 14px;
background: rgba(239, 68, 68, 0.1);
border: 1px solid rgba(239, 68, 68, 0.2);
display: grid;
place-items: center;
color: #FCA5A5;
font-size: 20px;
margin-bottom: 4px;
}
.root :global(.scan-error-title) {
font-size: 20px;
font-weight: 800;
color: #e8f2ff;
}
.root :global(.scan-error-copy) {
max-width: 360px;
font-size: 14px;
color: #8fa4c3;
line-height: 1.6;
}
.root :global(.scan-retry-button) {
margin-top: 8px;
display: inline-flex;
align-items: center;
gap: 8px;
min-height: 40px;
padding: 10px 16px;
border: 1px solid rgba(77, 163, 255, 0.3);
border-radius: 12px;
background: rgba(77, 163, 255, 0.12);
color: #9ecbff;
font-size: 13px;
font-weight: 800;
cursor: pointer;
}
.root :global(.scan-retry-button:hover) {
background: rgba(77, 163, 255, 0.18);
border-color: rgba(111, 183, 255, 0.48);
}
.root :global(.scan-loading-state) {
flex: 1;
min-height: 520px;
@@ -101,7 +175,7 @@
}
.root :global(.scan-loading-node.market) {
background: linear-gradient(135deg, #38bdf8, #4da3ff);
background: linear-gradient(135deg, #38bdf8, var(--color-accent-primary));
box-shadow: 0 0 24px rgba(56, 189, 248, 0.26);
}
@@ -141,7 +215,7 @@
}
.root :global(.scan-loading-copy-block strong) {
color: #e6edf3;
color: var(--color-text-primary);
font-size: 18px;
font-weight: 900;
letter-spacing: -0.02em;
@@ -149,9 +223,9 @@
.root :global(.scan-loading-copy-block span) {
max-width: 320px;
color: #9fb2c7;
color: var(--color-text-secondary);
font-size: 13px;
font-weight: 650;
font-weight: 600;
line-height: 1.55;
}
@@ -168,7 +242,7 @@
width: 7px;
min-height: 8px;
border-radius: 999px 999px 4px 4px;
background: linear-gradient(180deg, #7dd3fc, #4da3ff 52%, #1be392);
background: linear-gradient(180deg, #7dd3fc, var(--color-accent-primary) 52%, #1be392);
opacity: 0.42;
transform-origin: center bottom;
animation: scan-loading-bars 1.35s ease-in-out infinite;
@@ -0,0 +1,53 @@
/* Barrel: pre-combined scan-terminal root class name.
Consolidates 22 CSS Modules that are always co-imported into
a single className, keeping ScanTerminalDashboard.tsx lean. */
import clsx from "clsx";
import dashboardHomeStyles from "./DashboardHomeIntelligence.module.css";
import dashboardMapStyles from "./DashboardMap.module.css";
import dashboardModalGuideStyles from "./DashboardModalGuide.module.css";
import dashboardShellStyles from "./DashboardShell.module.css";
import detailChromeStyles from "./DetailPanelChrome.module.css";
import detailContentStyles from "./DetailPanelContent.module.css";
import detailSectionsStyles from "./DetailPanelSections.module.css";
import futureForecastModalStyles from "./FutureForecastModal.module.css";
import historyModalStyles from "./HistoryModal.module.css";
import modalChromeStyles from "./ModalChrome.module.css";
import scanTerminalStyles from "./ScanTerminal.module.css";
import scanTerminalBoardStyles from "./ScanTerminalBoard.module.css";
import scanTerminalCalendarStyles from "./ScanTerminalCalendar.module.css";
import scanTerminalCardStyles from "./ScanTerminalCard.module.css";
import scanTerminalDetailStyles from "./ScanTerminalDetail.module.css";
import scanTerminalFiltersStyles from "./ScanTerminalFilters.module.css";
import scanTerminalLightThemeStyles from "./ScanTerminalLightTheme.module.css";
import scanTerminalListStyles from "./ScanTerminalList.module.css";
import scanTerminalMobileStyles from "./ScanTerminalMobile.module.css";
import scanTerminalOpportunityStyles from "./ScanTerminalOpportunity.module.css";
import scanTerminalShellStyles from "./ScanTerminalShell.module.css";
import scanTerminalStateStyles from "./ScanTerminalState.module.css";
export const scanRootClass = clsx(
dashboardHomeStyles.root,
dashboardMapStyles.root,
dashboardShellStyles.root,
dashboardModalGuideStyles.root,
scanTerminalStyles.root,
scanTerminalShellStyles.root,
scanTerminalFiltersStyles.root,
scanTerminalListStyles.root,
scanTerminalBoardStyles.root,
scanTerminalDetailStyles.root,
scanTerminalStateStyles.root,
scanTerminalOpportunityStyles.root,
scanTerminalCardStyles.root,
scanTerminalCalendarStyles.root,
scanTerminalMobileStyles.root,
scanTerminalLightThemeStyles.root,
detailChromeStyles.root,
detailContentStyles.root,
detailSectionsStyles.root,
modalChromeStyles.root,
futureForecastModalStyles.root,
historyModalStyles.root,
);
@@ -542,6 +542,7 @@ export function AiPinnedCityCard({
collapsed && !isCompactCard && "collapsed",
removing && "removing",
)}
tabIndex={-1}
data-ai-status={decisionState.aiStatus}
data-evidence-quality={decisionState.evidenceQuality}
data-market-status={decisionState.marketStatus}
@@ -160,7 +160,7 @@
.sectionTitle {
margin: 0 0 16px;
font-size: 1.65rem;
font-weight: 750;
font-weight: 700;
}
.paragraph {
@@ -283,7 +283,7 @@
}
}
@media (max-width: 900px) {
@media (max-width: 960px) {
.docsHeader {
align-items: flex-start;
flex-direction: column;