Removed the scan KPI bar, trimmed redundant decision-card content, and shortened the primary reason text so the city analysis cards read like concise signals instead of repeating the same state across sections.
Constraint: Keep existing city decision logic and data flow unchanged while simplifying the UI
Rejected: Rework the full card layout or mobile card flow | larger surface area than requested
Confidence: high
Scope-risk: narrow
Reversibility: clean
Directive: Keep hero, decision band, and market line responsibilities separate to avoid duplicate copy returning
Tested: frontend npm run build
Not-tested: Manual visual QA in browser across desktop/mobile breakpoints
The dashboard had several oversized orchestration, component, and CSS files that made product-copy changes and mobile/performance work risky. This refactor preserves behavior while splitting scan terminal CSS, opportunity helpers, future forecast panels, history/detail charts, and probability/model sections into smaller ownership boundaries.
Constraint: No user-visible version bump because this batch is architecture and performance cleanup, not a release announcement.
Rejected: Rewrite dashboard state management in the same batch | too broad for a safe upload after CSS and component splitting.
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep new component/CSS boundaries instead of moving product copy back into the large dashboard files.
Tested: npm run build; npm run test:business; git diff --check
Not-tested: Browser visual smoke test after push
The scan terminal had grown into overlapping CSS, request-state, AI-provider, and city-card data responsibilities. This refactor separates those boundaries without changing product behavior: CSS modules are split by surface, city AI prompt/provider/fallback logic is isolated, and scan terminal request state now has reusable RemoteData adapters plus business-state tests.
Constraint: Preserve existing global scan-terminal class names and API responses during the refactor
Constraint: No new dependencies; keep this as a file-boundary cleanup
Rejected: Introduce React Query now | higher migration risk than the requested lightweight query-client path
Rejected: Rewrite AI stream behavior | progressive/fallback states are product-sensitive and were only adapter-split
Confidence: high
Scope-risk: moderate
Reversibility: clean
Directive: Keep AI stream state changes covered by business snapshots before changing fallback/cache wording
Tested: npm run test:business; npx tsc --noEmit; npm run build; python pytest -q; ruff check; py_compile targeted city AI modules
Not-tested: Live DeepSeek provider network replay and browser visual QA
Phone users need the city card to answer what matters first instead of inheriting the full desktop analysis hierarchy. This adds a MobileDecisionCard that leads with city, observed temperature, expected high, peak window, one decision reason, status tags, freshness, and a separate market-price row, while keeping AI, model evidence, and the chart behind collapsible sections.
Constraint: Preserve the existing desktop city-card layout and decision state semantics.
Rejected: Continue relying only on CSS hide/show | it keeps mobile coupled to the desktop information architecture.
Confidence: high
Scope-risk: moderate
Reversibility: clean
Tested: TypeScript diagnostics for AiPinnedCityCard and MobileDecisionCard
Tested: npm run build
Not-tested: Device screenshot QA across iOS/Android viewport sizes.
City decision cards rendered Chart.js canvases and AI evidence bodies before the user could see or expand those sections. This keeps the card shell visible while delaying chart data/canvas work until the section nears the viewport and skipping the AI evidence body while its details panel is collapsed.
Constraint: Preserve existing decision-card layout and evidence copy.
Rejected: Add list virtualization in the same pass | card-level render costs should be reduced before changing list mechanics.
Confidence: high
Scope-risk: narrow
Reversibility: clean
Tested: npm run build
Not-tested: Runtime scroll benchmark on a large production city set.
Dashboard.module.css mixed base shell styles with the entire scan terminal surface, making every decision-card or calendar styling pass risky. This extracts the scan terminal layer into its own CSS module and attaches that module root beside the existing dashboard root so global class selectors keep their current behavior.
Constraint: Preserve existing global scan-* class names and visual cascade.
Rejected: Rename scan classes into scoped module keys | too much DOM churn for a behavior-preserving CSS split.
Rejected: Split card/calendar/mobile rules in the same pass | safer to establish the scan-terminal layer first before finer component CSS ownership.
Confidence: high
Scope-risk: moderate
Reversibility: clean
Tested: npm run build
Not-tested: Pixel-level browser comparison across dark and light themes.