4df8dc6f87
新建 web/website/(Astro 零 SSR): - 组件迁移:交互块保留 .jsx 经 @astrojs/react(Header/AnnouncementBar/SignupForm/PricingPlans),纯展示块转 .astro 去运行时 JS;像素以原型为基准。 - 令牌同源:build-tokens.mjs 从 design/colors_and_type.css 生成 tokens.gen.css,仅剔除第三方 Google Fonts @import,数值不改。 - 字体自托管:@fontsource(Sora/Manrope/Noto Sans SC/JetBrains Mono),无第三方 CDN。 - 图标:Lucide 构建期内联 SVG(替代 unpkg CDN),零运行时、零 CDN。 - i18n:/(zh)与 /en/(en)双路由单显,语言切换组件;文案沿用 ui_kits 脱敏文案。 - 安全:public/_headers 严格 CSP(全 self + 自托管资源 + 内联片段 sha256,无 unsafe-inline)+ HSTS;无支付表单。 - CI:.gitea/workflows/website.yml 构建(红线扫描+lint+CSP 哈希)→ 同时发布 Cloudflare Pages 主站与镜像。 - 灾备:README 写明干净环境 npm ci && npm run build 可直接部署到任意静态托管;dist 指纹确定性,主站镜像一致。 测试:npm run lint(0 error)/ npm test(build+CSP 哈希注入+红线扫描 0 命中)/ 两次干净构建 dist 指纹一致。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
62 lines
2.2 KiB
Plaintext
62 lines
2.2 KiB
Plaintext
---
|
|
import Icon from './Icon.astro';
|
|
import SignupForm from './SignupForm.jsx';
|
|
import type { T } from '../i18n/strings';
|
|
|
|
interface Props { t: T }
|
|
const { t } = Astro.props;
|
|
---
|
|
<section class="hero">
|
|
<div class="wrap grid">
|
|
<div>
|
|
<div class="eyebrow">{t('hero.eyebrow')}</div>
|
|
<h1 class="h1-preline">{t('hero.h1')}</h1>
|
|
<p class="lede">{t('hero.lede')}</p>
|
|
|
|
<SignupForm
|
|
client:idle
|
|
ph={t('su.ph')}
|
|
btn={t('su.btn')}
|
|
ok={t('su.ok')}
|
|
hint={t('su.hint')}
|
|
dl={t('su.dl')}
|
|
/>
|
|
|
|
<div class="trust">
|
|
<span><Icon name="check" /><span>{t('hero.t1')}</span></span>
|
|
<span><Icon name="check" /><span>{t('hero.t2')}</span></span>
|
|
<span><Icon name="check" /><span>{t('hero.t3')}</span></span>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="orb-wrap">
|
|
<div class="orb-card">
|
|
<div class="orb">
|
|
<svg class="ring" viewBox="0 0 184 184"><circle cx="92" cy="92" r="85" fill="none" stroke="rgba(255,255,255,.85)" stroke-width="4" stroke-linecap="round"></circle></svg>
|
|
<Icon name="shield-check" class="ic" />
|
|
<span class="tm">02:14:08</span>
|
|
<span class="sf">{t('orb.cap')}</span>
|
|
</div>
|
|
<div class="orb-meta">
|
|
<div class="m"><div class="k"><Icon name="arrow-down" /><span>{t('orb.down')}</span></div><div class="v">86.4<span class="orb-unit"> Mb/s</span></div></div>
|
|
<div class="m"><div class="k"><Icon name="arrow-up" /><span>{t('orb.up')}</span></div><div class="v">12.1<span class="orb-unit"> Mb/s</span></div></div>
|
|
</div>
|
|
<div class="orb-node">
|
|
<div class="cc">HK</div>
|
|
<div class="orb-node-grow"><div class="nn">{t('orb.node')}</div><div class="ns">{t('orb.nodesub')}</div></div>
|
|
<Icon name="chevron-right" class="orb-node-chev" />
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="wrap strip">
|
|
<span>{t('strip.txt')}</span>
|
|
<span><Icon name="smartphone" />iOS</span>
|
|
<span><Icon name="smartphone" />Android</span>
|
|
<span><Icon name="laptop" />macOS</span>
|
|
<span><Icon name="monitor" />Windows</span>
|
|
<span><Icon name="terminal" />Linux</span>
|
|
</div>
|
|
</section>
|