diff --git a/frontend/app/dashboard/page.tsx b/frontend/app/dashboard/page.tsx deleted file mode 100644 index 982d0910..00000000 --- a/frontend/app/dashboard/page.tsx +++ /dev/null @@ -1,12 +0,0 @@ -import type { Metadata } from "next"; -import { DashboardEntry } from "@/components/dashboard/DashboardEntry"; - -export const metadata: Metadata = { - title: "PolyWeather - Global Weather Intelligence Map", - description: - "PolyWeather dashboard with METAR, MGM, DEB fusion forecast, multi-model comparison, and history reconciliation.", -}; - -export default function DashboardPage() { - return ; -} diff --git a/frontend/app/page.tsx b/frontend/app/page.tsx index fcdeb953..af0bebb0 100644 --- a/frontend/app/page.tsx +++ b/frontend/app/page.tsx @@ -1,12 +1,12 @@ import type { Metadata } from "next"; -import { LandingPage } from "@/components/landing/LandingPage"; +import { DashboardEntry } from "@/components/dashboard/DashboardEntry"; export const metadata: Metadata = { - title: "PolyWeather | Weather Market Intelligence", + title: "PolyWeather - Global Weather Intelligence Map", description: - "PolyWeather turns real-time weather observations and model spreads into settlement-focused probabilities, market scan signals, and Pro decision support.", + "PolyWeather dashboard with METAR, MGM, DEB fusion forecast, multi-model comparison, and history reconciliation.", }; export default function HomePage() { - return ; + return ; } diff --git a/frontend/components/landing/LandingPage.module.css b/frontend/components/landing/LandingPage.module.css deleted file mode 100644 index 066f7af7..00000000 --- a/frontend/components/landing/LandingPage.module.css +++ /dev/null @@ -1,547 +0,0 @@ -.page { - position: relative; - overflow: hidden; -} - -.shell { - margin: 0 auto; - width: min(1180px, calc(100% - 32px)); -} - -.heroGrid { - display: grid; - gap: 24px; - grid-template-columns: minmax(0, 1.2fr) minmax(320px, 0.8fr); -} - -.panel { - border: 1px solid rgba(148, 163, 184, 0.16); - background: - linear-gradient(180deg, rgba(15, 23, 42, 0.88), rgba(2, 6, 23, 0.78)); - box-shadow: - 0 24px 80px rgba(2, 6, 23, 0.42), - inset 0 1px 0 rgba(255, 255, 255, 0.06); - backdrop-filter: blur(14px); -} - -.heroCard { - padding: 28px; - border-radius: 28px; -} - -.heroEyebrow { - display: inline-flex; - align-items: center; - gap: 8px; - margin-bottom: 18px; - border-radius: 999px; - border: 1px solid rgba(34, 211, 238, 0.24); - background: rgba(8, 47, 73, 0.34); - padding: 8px 14px; - color: #a5f3fc; - font-size: 12px; - font-weight: 700; - letter-spacing: 0.12em; - text-transform: uppercase; -} - -.heroTitle { - max-width: 11ch; - font-size: clamp(46px, 8vw, 84px); - line-height: 0.94; - font-weight: 800; - letter-spacing: -0.05em; -} - -.heroLead { - margin-top: 22px; - max-width: 60ch; - color: rgba(226, 232, 240, 0.84); - font-size: 17px; - line-height: 1.7; -} - -.heroActions { - display: flex; - flex-wrap: wrap; - gap: 12px; - margin-top: 28px; -} - -.primaryButton, -.secondaryButton { - display: inline-flex; - min-height: 48px; - align-items: center; - gap: 10px; - border-radius: 14px; - padding: 12px 18px; - font-weight: 700; - transition: - transform 180ms ease, - border-color 180ms ease, - background 180ms ease; -} - -.primaryButton:hover, -.secondaryButton:hover { - transform: translateY(-1px); -} - -.primaryButton { - background: linear-gradient(135deg, #22d3ee, #3b82f6); - color: #020617; -} - -.secondaryButton { - border: 1px solid rgba(148, 163, 184, 0.22); - background: rgba(15, 23, 42, 0.58); - color: #e2e8f0; -} - -.bulletGrid { - display: grid; - gap: 14px; - margin-top: 28px; -} - -.bulletItem { - display: flex; - gap: 12px; - align-items: flex-start; -} - -.bulletItem svg { - margin-top: 3px; - color: #67e8f9; - flex: 0 0 auto; -} - -.bulletItem strong { - display: block; - margin-bottom: 4px; - font-size: 15px; -} - -.bulletItem span { - color: rgba(203, 213, 225, 0.76); - font-size: 14px; - line-height: 1.55; -} - -.heroSide { - display: grid; - gap: 16px; -} - -.signalCard { - padding: 22px; - border-radius: 24px; -} - -.signalHeader { - display: flex; - justify-content: space-between; - gap: 16px; - align-items: flex-start; -} - -.signalHeader h3 { - font-size: 18px; - font-weight: 700; -} - -.signalHeader p { - margin-top: 6px; - color: rgba(191, 219, 254, 0.76); - font-size: 13px; -} - -.statusPill { - border-radius: 999px; - background: rgba(34, 197, 94, 0.14); - color: #86efac; - padding: 7px 12px; - font-size: 12px; - font-weight: 700; -} - -.metricGrid { - display: grid; - gap: 12px; - margin-top: 18px; - grid-template-columns: repeat(2, minmax(0, 1fr)); -} - -.metricCard { - border-radius: 18px; - border: 1px solid rgba(148, 163, 184, 0.12); - background: rgba(15, 23, 42, 0.52); - padding: 16px; -} - -.metricCard label { - display: block; - color: rgba(148, 163, 184, 0.9); - font-size: 12px; - letter-spacing: 0.08em; - text-transform: uppercase; -} - -.metricCard strong { - display: block; - margin-top: 8px; - font-size: 28px; - font-weight: 800; - letter-spacing: -0.03em; -} - -.metricCard span { - display: block; - margin-top: 6px; - color: rgba(226, 232, 240, 0.74); - font-size: 13px; - line-height: 1.5; -} - -.previewStrip { - margin-top: 18px; - display: grid; - gap: 10px; -} - -.previewRow { - display: flex; - justify-content: space-between; - gap: 12px; - align-items: center; - border-radius: 14px; - background: rgba(2, 6, 23, 0.42); - padding: 12px 14px; - color: #cbd5e1; - font-size: 14px; -} - -.previewRow strong { - color: #f8fafc; -} - -.section { - margin-top: 64px; -} - -.sectionHeader { - display: flex; - justify-content: space-between; - gap: 18px; - align-items: end; - margin-bottom: 24px; -} - -.sectionHeader h2 { - font-size: clamp(28px, 4vw, 42px); - line-height: 1.05; - font-weight: 800; - letter-spacing: -0.04em; -} - -.sectionHeader p { - max-width: 58ch; - color: rgba(203, 213, 225, 0.72); - line-height: 1.7; -} - -.problemGrid, -.featureGrid, -.proofGrid, -.pricingGrid, -.ctaGrid { - display: grid; - gap: 18px; -} - -.problemGrid { - grid-template-columns: repeat(3, minmax(0, 1fr)); -} - -.featureGrid { - grid-template-columns: repeat(4, minmax(0, 1fr)); -} - -.proofGrid, -.pricingGrid { - grid-template-columns: repeat(2, minmax(0, 1fr)); -} - -.ctaGrid { - grid-template-columns: 1.1fr 0.9fr; -} - -.miniCard, -.pricingCard, -.ctaCard { - border-radius: 24px; - padding: 22px; -} - -.miniCard h3, -.pricingCard h3, -.ctaCard h3 { - font-size: 20px; - font-weight: 700; -} - -.miniCard p, -.pricingCard p, -.ctaCard p { - margin-top: 10px; - color: rgba(203, 213, 225, 0.74); - line-height: 1.65; -} - -.miniCard ul, -.pricingCard ul { - margin-top: 18px; - display: grid; - gap: 10px; - color: rgba(241, 245, 249, 0.92); - font-size: 14px; -} - -.miniCard li, -.pricingCard li { - display: flex; - gap: 10px; - align-items: flex-start; -} - -.miniCard li::before, -.pricingCard li::before { - content: ""; - margin-top: 7px; - width: 6px; - height: 6px; - border-radius: 999px; - background: linear-gradient(135deg, #22d3ee, #60a5fa); - flex: 0 0 auto; -} - -.previewPanel { - overflow: hidden; - border-radius: 28px; - padding: 0; -} - -.previewTop { - padding: 22px; - border-bottom: 1px solid rgba(148, 163, 184, 0.12); -} - -.previewTop h3 { - font-size: 22px; - font-weight: 700; -} - -.previewTop p { - margin-top: 10px; - max-width: 56ch; - color: rgba(203, 213, 225, 0.74); - line-height: 1.65; -} - -.previewBody { - display: grid; - gap: 14px; - padding: 22px; - grid-template-columns: 1.2fr 0.8fr; -} - -.previewMap, -.previewStack { - border-radius: 22px; - background: rgba(15, 23, 42, 0.56); -} - -.previewMap { - min-height: 320px; - position: relative; - overflow: hidden; -} - -.previewMap::before, -.previewMap::after { - content: ""; - position: absolute; - inset: 0; -} - -.previewMap::before { - background: - radial-gradient(circle at 20% 40%, rgba(34, 211, 238, 0.2), transparent 20%), - radial-gradient(circle at 68% 30%, rgba(59, 130, 246, 0.22), transparent 20%), - radial-gradient(circle at 76% 72%, rgba(14, 165, 233, 0.16), transparent 18%), - linear-gradient(180deg, rgba(8, 47, 73, 0.24), rgba(2, 6, 23, 0.16)); -} - -.previewMap::after { - background-image: - linear-gradient(rgba(148, 163, 184, 0.09) 1px, transparent 1px), - linear-gradient(90deg, rgba(148, 163, 184, 0.09) 1px, transparent 1px); - background-size: 48px 48px; - mask-image: linear-gradient(180deg, rgba(255,255,255,1), rgba(255,255,255,0.2)); -} - -.mapChip { - position: absolute; - display: inline-flex; - align-items: center; - gap: 6px; - border-radius: 999px; - border: 1px solid rgba(255,255,255,0.14); - background: rgba(2, 6, 23, 0.78); - padding: 8px 12px; - color: #e2e8f0; - font-size: 12px; - font-weight: 700; - box-shadow: 0 14px 32px rgba(2, 6, 23, 0.3); -} - -.mapChip strong { - color: #f8fafc; -} - -.mapChip span { - color: rgba(34, 211, 238, 0.92); -} - -.previewStack { - padding: 18px; - display: grid; - gap: 12px; -} - -.stackCard { - border-radius: 18px; - border: 1px solid rgba(148, 163, 184, 0.1); - background: rgba(2, 6, 23, 0.5); - padding: 16px; -} - -.stackCard h4 { - font-size: 15px; - font-weight: 700; -} - -.stackCard p { - margin-top: 8px; - color: rgba(191, 219, 254, 0.72); - font-size: 13px; - line-height: 1.55; -} - -.price { - margin-top: 18px; - display: flex; - align-items: baseline; - gap: 8px; -} - -.price strong { - font-size: 42px; - line-height: 1; - font-weight: 800; - letter-spacing: -0.04em; -} - -.price span { - color: rgba(203, 213, 225, 0.78); - font-size: 15px; -} - -.pricingCardAccent { - border-color: rgba(34, 211, 238, 0.2); - box-shadow: 0 18px 44px rgba(8, 47, 73, 0.28); -} - -.ctaCard { - min-height: 100%; -} - -.ctaActions { - display: flex; - flex-wrap: wrap; - gap: 12px; - margin-top: 20px; -} - -.footer { - margin-top: 72px; - padding: 28px 0 56px; - color: rgba(148, 163, 184, 0.76); - font-size: 13px; -} - -.footerRow { - display: flex; - justify-content: space-between; - gap: 18px; - align-items: center; -} - -.footerLinks { - display: flex; - gap: 18px; - flex-wrap: wrap; -} - -@media (max-width: 1080px) { - .heroGrid, - .previewBody, - .ctaGrid { - grid-template-columns: 1fr; - } - - .featureGrid { - grid-template-columns: repeat(2, minmax(0, 1fr)); - } - - .problemGrid, - .proofGrid, - .pricingGrid { - grid-template-columns: 1fr; - } -} - -@media (max-width: 720px) { - .shell { - width: min(100% - 20px, 1180px); - } - - .heroCard, - .signalCard, - .miniCard, - .pricingCard, - .ctaCard, - .previewTop, - .previewBody { - padding: 18px; - } - - .featureGrid { - grid-template-columns: 1fr; - } - - .metricGrid { - grid-template-columns: 1fr; - } - - .section { - margin-top: 48px; - } - - .footerRow, - .sectionHeader { - flex-direction: column; - align-items: flex-start; - } -} diff --git a/frontend/components/landing/LandingPage.tsx b/frontend/components/landing/LandingPage.tsx deleted file mode 100644 index a8fe7053..00000000 --- a/frontend/components/landing/LandingPage.tsx +++ /dev/null @@ -1,471 +0,0 @@ -import Link from "next/link"; -import { - ArrowRight, - Bot, - ChartColumnIncreasing, - ChevronRight, - Compass, - MapPinned, - Radar, - ShieldCheck, - Sparkles, - Target, - Wallet, -} from "lucide-react"; -import styles from "./LandingPage.module.css"; - -const problemCards = [ - { - title: "普通天气预报不等于结算判断", - body: "大多数天气产品只给一个最高温数字。温度市场真正关心的是最终会落进哪个结算桶,以及这个判断有多稳。", - points: ["不是看“几度左右”", "而是看结算桶概率", "以及当前市场是否错价"], - }, - { - title: "单一数据源不够稳", - body: "机场实测、官方结算源、集合预报、多模型 spread 会互相打架。PolyWeather 的价值,在于把这些冲突信息组织成决策视图。", - points: ["METAR / HKO / CWA", "Open-Meteo ensemble", "DEB 融合与回看"], - }, - { - title: "交易需要即时上下文", - body: "当日高温窗口、max so far、模型崩盘、盘口偏移,都比一个静态 forecast 更接近真实交易判断。", - points: ["今日日内分析", "历史对账", "market scan 与 edge"], - }, -]; - -const featureCards = [ - { - icon: Radar, - title: "多源天气", - body: "整合 METAR、官方源、Open-Meteo、ensemble 与 multi-model,不把风险押在单一来源上。", - }, - { - icon: Target, - title: "结算概率", - body: "把 forecast 变成 settlement-focused bucket distribution,而不是只给一个模糊最高温。", - }, - { - icon: ChartColumnIncreasing, - title: "错价扫描", - body: "把模型概率和市场报价放在一张桌子上,直接判断 signal、risk 和 tradable 状态。", - }, - { - icon: Bot, - title: "双端交付", - body: "Web 仪表盘负责看全局,Telegram Bot 负责高频使用与即时查询。", - }, -]; - -const trustCards = [ - { - title: "官方参考出口", - body: "重点城市已经补上官方气象机构、机场页面与 METAR 入口。用户可以自己交叉验证,不必盲信结论。", - points: ["Singapore / Wellington / Hong Kong", "机场实测与国家气象机构", "降低临近结算争议成本"], - }, - { - title: "历史对账与日内分析", - body: "不只给今天的判断,也给你过去表现与 intraday 结构,帮助理解模型稳定性而不是只看单点结果。", - points: ["历史最高温对账", "今日日内曲线", "模型与实测偏差回看"], - }, -]; - -export function LandingPage() { - return ( -
-
-
-
-
-
- -
-
-
-
-
- -
-
-
- PolyWeather -
-
- Weather market intelligence -
-
-
- -
- - 打开 Dashboard - - - 登录账户 - - -
-
- -
-
-
- - Settlement-Focused Weather Intelligence -
-

- 面向天气结算市场的实时概率情报平台 -

-

- PolyWeather 把机场实测、官方结算源、ensemble 区间、多模型 spread - 与当日高温窗口,整理成结算导向概率、错价扫描和可操作的风控视图。 - 不是做“明天大概多少度”的天气站,而是做“今天这个市场值不值得动”的决策终端。 -

- -
- - 查看仪表盘 - - - - 开通 Pro - - - - Telegram Bot - - -
- -
-
- -
- 30 个城市,覆盖不同结算口径 - 包括 METAR 城市、香港 HKO、台北 CWA,以及多地区风险分布。 -
-
-
- -
- 不是黑箱结论 - 已经补上官方参考链接、历史对账、今日日内分析和市场对照。 -
-
-
- -
- Dashboard + Bot 双端交付 - Web 看全局,Bot 处理高频操作,Pro 用于更完整的决策支持。 -
-
-
-
- -
-
-
-
-

交易前真正需要看什么

-

不是单一 forecast,而是结算、分布、盘口和时段上下文。

-
- Live System -
- -
-
- - 30 - 同时覆盖摄氏和华氏结算市场。 -
-
- - μ + 桶 - 不只给中心值,还给 bucket distribution。 -
-
- - 实测 + 预报 - METAR、官方源、ensemble、multi-model 共同进入决策链。 -
-
- - Web + Bot - 既能看地图,也能在 Telegram 里快速查询。 -
-
- -
-
- 今日日内分析 - 高温窗口 + max so far -
-
- 历史对账 - 按结算口径回看命中 -
-
- 市场扫描 - 模型概率 vs 市场价格 -
-
-
- -
-
-
-

当前产品定位

-

从天气数据走到市场判断,而不是停留在天气展示。

-
-
- -
-
- Free - 基础看板与账户 -
-
- Pro - 完整分析、Bot 支持、更多上下文 -
-
- Ops - 会员、补分、支付异常单、运营视图 -
-
-
-
-
-
-
- -
-
-
-
-

普通天气预报,不等于结算判断

-
-

- 温度市场真正值钱的不是“明天最高温大概几度”,而是“今天最终更可能落进哪个桶,这个判断有多稳,市场价格有没有偏离”。 -

-
- -
- {problemCards.map((card) => ( -
-

{card.title}

-

{card.body}

-
    - {card.points.map((item) => ( -
  • {item}
  • - ))} -
-
- ))} -
-
-
- -
-
-
-
-

把天气数据转成可交易的结算情报

-
-

- PolyWeather 的核心不是增加信息数量,而是把采集、概率、市场和交付整理成一个可执行工作流。 -

-
- -
- {featureCards.map(({ icon: Icon, title, body }) => ( -
-
- -
-

{title}

-

{body}

-
- ))} -
-
-
- -
-
-
-
-

不是黑箱结论,而是可验证的判断

-
-

- 当市场接近结算,真正有价值的是可验证性。用户可以同时看官方参考、机场实测、历史对账和当日结构,而不是只看一个数字。 -

-
- -
- {trustCards.map((card) => ( -
-

{card.title}

-

{card.body}

-
    - {card.points.map((item) => ( -
  • {item}
  • - ))} -
-
- ))} -
-
-
- -
-
-
-
-

一个页面看完当日温度交易所需信息

-

- 地图筛城市,侧边栏看风险,详情面板看今日日内分析、历史对账、官方参考和市场扫描。你不需要在多个天气网站、机场页和市场页之间来回切。 -

-
-
-
-
- London - 12°C -
-
- Hong Kong - 17°C -
-
- New York - 54°F -
-
- Singapore - 31°C -
-
- -
-
-

今日日内分析

-

高温窗口、max so far、集合区间和死盘提示,解决“今天还有没有变盘空间”。

-
-
-

历史对账

-

把过去真实结算和模型表现放回同一张表里,避免只看当天情绪。

-
-
-

官方参考

-

重点城市直连国家气象机构、机场页面和 METAR,降低临近结算争议。

-
-
-

支付与会员

-

账户页支持浏览器钱包、订阅、积分与支付异常恢复,不再只是“有个付费按钮”。

-
-
-
-
-
-
- -
-
-
-
-

从公开看板到 Pro 决策支持

-
-

- 先让用户看到产品价值,再把更重的使用场景放到账户、Bot 和订阅能力里,不靠含糊营销话术成交。 -

-
- -
-
-

Free

-

先理解产品逻辑,再决定是否进入更高频的使用方式。

-
- $0 - 基础入口 -
-
    -
  • 地图与城市风险概览
  • -
  • 基础账户能力
  • -
  • 公开可见的页面与数据口径
  • -
-
- -
-

Pro

-

面向高频使用者、市场观察者和需要 Telegram 辅助的人群。

-
- 5 USDC - / 月 -
-
    -
  • 完整分析、更多上下文与账户中心能力
  • -
  • Telegram Bot 交互与更高频使用场景
  • -
  • 支付、积分、订阅与异常恢复链路
  • -
-
-
-
-
- -
-
-
-
-

开始使用 PolyWeather

-

- 如果你只是想先看产品,直接打开 Dashboard。需要账户、订阅、支付与会员能力,再进入账户中心。 -

-
- - 打开 Dashboard - - - - 进入账户中心 - -
-
- -
-

高频使用更适合 Bot

-

- 如果你本来就习惯在 Telegram 中工作,用 Bot 会比网页切换更快。Web 负责看全局,Bot 负责高频查询和快速反馈。 -

- -
-
- - -
-
-
- ); -}