diff --git a/docs/engine-select-mockups/index.html b/docs/engine-select-mockups/index.html new file mode 100644 index 0000000..bf2bdb6 --- /dev/null +++ b/docs/engine-select-mockups/index.html @@ -0,0 +1,150 @@ + + + + +Engine Select Mockups · 三种风格抽卡 + + + +
+

Engine Select · 三种风格抽卡

+
点击下面任一张卡片预览完整动画。每个 mockup 都是独立 HTML,可单独打开。
+ +
+ +
+ A · 极简 +

极简对角线 · 黑白对比

+
纯黑 vs 纯白,瑞士设计风格。强调内容本身,不引入任何装饰元素。
+
+
最快加载、最稳定(不依赖 backdrop-filter)
+
永不过时,能搭配任何主题
+
高对比度,残障友好
+
+
+ + +
+ B · 赛博 +

渐变发光 · 赛博朋克

+
紫蓝 vs 橙金渐变 + 发光中线 + logo 光圈脉动。每个引擎有强烈的「人格」色彩。
+
+
视觉冲击力最强,第一眼记得住
+
两个引擎气质对比鲜明(冷 vs 暖)
+
动效丰富但不影响性能
+
+
+ + +
+ C · 高级 +

玻璃拟态 + 粒子 · 沉浸高级

+
动态极光 + 玻璃质感 + 漂浮粒子 + 玻璃 logo 卡片。最接近 Apple Vision Pro 的高级感。
+
+
沉浸感最强,"未来感" 拉满
+
背景动态延伸到展开后的主页
+
细节层次丰富(极光、粒子、玻璃高光)
+
+
+
+ + +
+ + diff --git a/docs/engine-select-mockups/index2.html b/docs/engine-select-mockups/index2.html new file mode 100644 index 0000000..28cc281 --- /dev/null +++ b/docs/engine-select-mockups/index2.html @@ -0,0 +1,303 @@ + + + + +V2 抽卡 · 4 张大间距对角线设计 + + + +
+
+

V2 抽卡 — 大间距对角线设计

+
+ 这次修了什么:上一版把内容用 flex center 堆在屏幕中央,导致 OpenClaw 和 Hermes 的字挤在分割线两侧重叠。 + 新版用 absolute 定位把内容明确放在各自三角形的「重心」——左上 11%/6%,右下 11%/6%,间距大幅拉开,分割线清晰。 +
+
+ +
+ +
+ OPENCLAW + HERMES +
+
+ A · BOLD SPLIT +

黑白巨字

+
瑞士设计风。两侧都是巨大产品名(180px),黑底 vs 白底强烈反差。hover 让另一侧变暗,专注感强。
+
+
最强反差,第一眼就抓住视线
+
极简、最稳、最快
+
永不过时(二十年后仍漂亮)
+
+
+
+ + +
+
+ B · CINEMATIC +

电影级双世界

+
深紫蓝(OpenClaw 冷)vs 暖橙琥珀(Hermes 暖)双世界。发光中线 + 玻璃 chips + 大图标 logo + 角标版本号。
+
+
视觉冲击最强,沉浸感最高
+
两个引擎气质对比鲜明(冷 vs 暖)
+
细节层次最丰富
+
+
+
+ + +
+
+ C · MONOLITH +

巨碑感(Linear / Vercel 风)

+
石墨黑 vs 象牙白,每边都有微妙网格纹 + 角落 glow。左上 logo + 序号 + 巨字 + 特性列表 + 实心 CTA。
+
+
最专业、信息密度最高
+
B2B / SaaS 一线产品的常用风格
+
两侧都有完整 CTA 按钮(点击意图明确)
+
+
+
+ + +
+
+ D · AURORA +

极光大字(Stripe / Apple 风)

+
底层动态极光 + 噪声纹理 + 玻璃覆盖层。两侧都是统一暗色调,但 hover 时该侧的玻璃会变亮、另一侧变暗。最高级感。
+
+
沉浸感最强,"未来感" 拉满
+
背景与展开后的主页可无缝衔接
+
动态背景延伸到整个产品的视觉体系
+
+
+
+
+ + +
+ + diff --git a/docs/engine-select-mockups/mockup-a-minimal.html b/docs/engine-select-mockups/mockup-a-minimal.html new file mode 100644 index 0000000..e7267bf --- /dev/null +++ b/docs/engine-select-mockups/mockup-a-minimal.html @@ -0,0 +1,229 @@ + + + + +Mockup A — 极简对角线(黑白对比) + + + + +
+ +
+
+ +
OpenClaw
+
通用 AI 助理 · 模型 / 渠道 / 记忆 / 智能体
+
+
+ + +
+
+ +
Hermes
+
Agent 工作流 · 工具调用 / Profile / Kanban / Skills
+
+
+ +
SELECT YOUR ENGINE
+
+ +
+
+ ✓ 已进入 主页 +
+
点击任意三角形预览动画 · 按 R 重置
+ + + + diff --git a/docs/engine-select-mockups/mockup-b-gradient.html b/docs/engine-select-mockups/mockup-b-gradient.html new file mode 100644 index 0000000..e51f884 --- /dev/null +++ b/docs/engine-select-mockups/mockup-b-gradient.html @@ -0,0 +1,269 @@ + + + + +Mockup B — 渐变发光(赛博朋克) + + + + +
+
+ +
+
+
通用助理
+ +
OpenClaw
+
模型管理 · 渠道集成 · 记忆系统 · 智能体编排
+
+
+ +
+
+
Agent 工作流
+ +
Hermes
+
工具调用 · Profile · Kanban · Skills · OAuth
+
+
+ +
— SELECT —
+
+ +
+
+
+
Loading workspace...
+
+
点击预览 · 按 R 重置
+ + + + diff --git a/docs/engine-select-mockups/mockup-c-glass.html b/docs/engine-select-mockups/mockup-c-glass.html new file mode 100644 index 0000000..a3b9773 --- /dev/null +++ b/docs/engine-select-mockups/mockup-c-glass.html @@ -0,0 +1,339 @@ + + + + +Mockup C — 玻璃拟态 + 粒子(沉浸高级感) + + + + +
+
+
+ +
+
+
通用助理
+
+ + + + + +
+
OpenClaw
+
模型管理 · 渠道集成 · 记忆系统 · 智能体编排
+
+
Models
+
Channels
+
Memory
+
+
+
+ +
+
+
Agent 工作流
+
+ + + +
+
Hermes
+
工具调用 · Kanban · Skills · OAuth · Multi Profile
+
+
Tools
+
Skills
+
Profiles
+
+
+
+ +
CHOOSE YOUR PATH
+
+ +
+
+
+
+
Loading workspace…
+
+
+
点击预览 · 按 R 重置
+ + + + diff --git a/docs/engine-select-mockups/v2-aurora.html b/docs/engine-select-mockups/v2-aurora.html new file mode 100644 index 0000000..ae5e89b --- /dev/null +++ b/docs/engine-select-mockups/v2-aurora.html @@ -0,0 +1,395 @@ + + + + +V2-D · Aurora — 极光大字(Stripe / Apple 风) + + + + +
+
+
+ +
+
+
+ +
— Choose your engine —
+
CLAWPANEL
+
v0.15.3
+ +
+
01 · Universal Assistant
+
OpenClaw
+
从模型到智能体,一站式打造你的 AI 工作台。
+
+
Models
+
Channels
+
Memory
+
Agents
+
+
+ Enter OpenClaw + +
+
+ +
+
Agent Workflow · 02
+
Hermes
+
让 Agent 真正能干活。工具调用、Profile、Kanban 一应俱全。
+
+
Tools
+
Skills
+
Kanban
+
Profiles
+
+
+ + Enter Hermes +
+
+
+ +
+
+
+
Initializing workspace
+
+
Click a side · Press R to reset
+ + + + diff --git a/docs/engine-select-mockups/v2-bold-split.html b/docs/engine-select-mockups/v2-bold-split.html new file mode 100644 index 0000000..ca352b9 --- /dev/null +++ b/docs/engine-select-mockups/v2-bold-split.html @@ -0,0 +1,284 @@ + + + + +V2-A · Bold Split — 黑白巨字 + + + + +
+
+
+
+ +
+
01 · 通用助理
+
OpenClaw
+
通用 AI 助理平台。模型、渠道、记忆、智能体,一个面板搞定全部。
+
+ Models + Channels + Agents +
+
+ Choose + +
+
+ +
+
02 · Agent 工作流
+
Hermes
+
Agent 工作流引擎。工具调用、Profile、Kanban、Skills,让 Agent 真正能干活。
+
+ Tools + Skills + Profiles +
+
+ + Choose +
+
+
+ +
+
+
+
Loading workspace…
+
+
Click a side · Press R to reset
+ + + + diff --git a/docs/engine-select-mockups/v2-cinematic.html b/docs/engine-select-mockups/v2-cinematic.html new file mode 100644 index 0000000..21c89d0 --- /dev/null +++ b/docs/engine-select-mockups/v2-cinematic.html @@ -0,0 +1,366 @@ + + + + +V2-B · Cinematic — 电影级双世界 + + + + +
+
+
+
+ +
— Choose your engine —
+
CLAWPANEL · 2026
+
v0.15.3
+ +
+
01
+
OpenClaw
+
通用 AI 助理平台。模型、渠道、记忆、智能体——一个面板搞定全部。
+
+
Models
+
Channels
+
Memory
+
Agents
+
+
+ Enter OpenClaw + +
+
+ +
+
02
+
Hermes
+
Agent 工作流引擎。工具调用、Profile、Kanban、Skills——让 AI 真正能干活。
+
+
Tools
+
Skills
+
Kanban
+
Profiles
+
+
+ + Enter Hermes +
+
+
+ +
+
+
+
Initializing workspace…
+
+
Click a side · Press R to reset
+ + + + diff --git a/docs/engine-select-mockups/v2-monolith.html b/docs/engine-select-mockups/v2-monolith.html new file mode 100644 index 0000000..f252b40 --- /dev/null +++ b/docs/engine-select-mockups/v2-monolith.html @@ -0,0 +1,423 @@ + + + + +V2-C · Monolith — 巨碑感(Linear / Vercel 风) + + + + +
+
+
+
+
+
+
+
+ +
— Pick your path —
+
CLAWPANEL
+
v0.15.3
+ +
+
+
+ +
+
01 · 通用助理
+
+
OpenClaw
+
从模型到智能体,一站式打造你的 AI 工作台。
+ +
+ Enter OpenClaw + +
+
+ +
+
+
+ +
+
Agent 工作流 · 02
+
+
Hermes
+
让 Agent 真正能干活。工具调用、Profile、Kanban 一应俱全。
+ +
+ + Enter Hermes +
+
+
+ +
+
+
+
Initializing workspace
+
+
Click a side · Press R to reset
+ + + + diff --git a/src/locales/modules/engine.js b/src/locales/modules/engine.js index 9a2cb76..38e3994 100644 --- a/src/locales/modules/engine.js +++ b/src/locales/modules/engine.js @@ -29,6 +29,21 @@ export default { choiceLaterDesc: _('先停留在引擎选择页,不立即安装任何引擎。准备好后再选择即可。', 'Stay on the engine chooser without installing an engine now. Pick one when you are ready.', '先停留在引擎選擇頁,不立即安裝任何引擎。準備好後再選擇即可。'), choiceLaterMeta: _('不会启动安装流程', 'No setup flow will start', '不會啟動安裝流程'), choiceLaterBadge: _('跳过', 'Skip', '跳過'), + // —— Monolith 启动选择屏(对角线分割版) —— + choiceTopBanner: _('— 选择你的引擎 —', '— Choose your engine —', '— 選擇你的引擎 —'), + choiceCtaEnter: _('立即进入', 'Enter', '立即進入'), + choiceOpenclawTagline: _('从模型到智能体,一站式打造你的 AI 工作台。', 'From models to agents — your full AI workbench in one panel.', '從模型到智能體,一站式打造你的 AI 工作台。'), + choiceOpenclawFeat1: _('多模型 / 多渠道并行管理', 'Multi-model · multi-channel orchestration', '多模型 / 多渠道並行管理'), + choiceOpenclawFeat2: _('持久化记忆 + 上下文工程', 'Persistent memory + context engineering', '持久化記憶 + 上下文工程'), + choiceOpenclawFeat3: _('无代码搭建智能体', 'No-code agent builder', '無程式碼搭建智能體'), + choiceOpenclawCategory: _('通用助理', 'Universal Assistant', '通用助理'), + choiceHermesTagline: _('让 Agent 真正能干活。工具调用、Profile、Kanban 一应俱全。', 'Make agents actually work — tool calling, profiles, Kanban built-in.', '讓 Agent 真正能幹活。工具調用、Profile、Kanban 一應俱全。'), + choiceHermesFeat1: _('原生工具调用 + Approval Flow', 'Native tool calling + approval flow', '原生工具調用 + Approval Flow'), + choiceHermesFeat2: _('多 Profile 隔离 + 多 Gateway', 'Multi-profile isolation + multi-gateway', '多 Profile 隔離 + 多 Gateway'), + choiceHermesFeat3: _('内置 Kanban / Skills / OAuth', 'Kanban / Skills / OAuth built-in', '內建 Kanban / Skills / OAuth'), + choiceHermesCategory: _('Agent 工作流', 'Agent Workflow', 'Agent 工作流'), + choiceSecondaryBoth: _('两个都要 ↗', 'Use both ↗', '兩個都要 ↗'), + choiceSecondaryLater: _('稍后再说', 'Decide later', '稍後再說'), hermesSetupDesc: _('安装并配置 Hermes Agent', 'Install and configure Hermes Agent', '安裝並配置 Hermes Agent'), hermesPhaseClickHint: _('点击可返回此步骤', 'Click to go back to this step', '點擊可返回此步驟', 'このステップに戻るにはクリック', '이 단계로 돌아가려면 클릭'), hermesSetupIntro: _( diff --git a/src/pages/engine-select.js b/src/pages/engine-select.js index 85221b1..5e466ab 100644 --- a/src/pages/engine-select.js +++ b/src/pages/engine-select.js @@ -3,27 +3,24 @@ import { t } from '../lib/i18n.js' import { applyEngineSelection } from '../lib/engine-manager.js' import { toast } from '../components/toast.js' -const OPTIONS = [ +const PRIMARY_OPTIONS = [ { id: 'openclaw', - key: 'Openclaw', - icon: 'layers', activeEngineId: 'openclaw', enabledEngineIds: ['openclaw'], targetRoute: '/setup', }, { id: 'hermes', - key: 'Hermes', - icon: 'bolt', activeEngineId: 'hermes', enabledEngineIds: ['hermes'], targetRoute: '/h/setup', }, +] + +const SECONDARY_OPTIONS = [ { id: 'both', - key: 'Both', - icon: 'spark', activeEngineId: 'openclaw', enabledEngineIds: ['openclaw', 'hermes'], engineMode: 'both', @@ -31,8 +28,6 @@ const OPTIONS = [ }, { id: 'later', - key: 'Later', - icon: 'clock', activeEngineId: 'openclaw', enabledEngineIds: [], deferred: true, @@ -41,83 +36,212 @@ const OPTIONS = [ ] const ICONS = { - layers: '', - bolt: '', - spark: '', - clock: '', + openclaw: '', + hermes: '', } +let _busy = false +let _revealEl = null +let _homeEl = null + export async function render() { const page = document.createElement('div') - page.className = 'page engine-select-page' + page.className = 'page engine-select-page es-monolith' page.innerHTML = ` -
-
${esc(t('engine.choiceKicker'))}
-

${esc(t('engine.choiceTitle'))}

-

${esc(t('engine.choiceSubtitle'))}

-
-
- ${OPTIONS.map(renderOption).join('')} -
-
-
${esc(t('engine.choiceNoteTitle'))}
-
${esc(t('engine.choiceNoteDesc'))}
-
+
+
+
+
+
+
+
+
+ +
${esc(t('engine.choiceTopBanner'))}
+
CLAWPANEL
+
v—
+ + ${renderContent('openclaw')} + ${renderContent('hermes')} + +
+ + + +
+
` - page.addEventListener('click', async (event) => { - const card = event.target.closest('.engine-choice-card') - if (!card) return - const option = OPTIONS.find(item => item.id === card.dataset.choice) - if (!option || card.classList.contains('loading')) return - await chooseOption(page, card, option) - }) + // 注入版本号(package.json 同步,Vite define 注入) + try { + const v = typeof __APP_VERSION__ !== 'undefined' ? __APP_VERSION__ : '' + const tag = page.querySelector('[data-version-tag]') + if (tag && v) tag.textContent = `v${v}` + } catch (_) {} + + bindHover(page) + bindClick(page) return page } -function renderOption(option) { - const badge = t(`engine.choice${option.key}Badge`) +function renderContent(id) { + const num = id === 'openclaw' ? '01' : '02' + const cap = id === 'openclaw' ? 'OpenClaw' : 'Hermes' + const cat = id === 'openclaw' ? t('engine.choiceOpenclawCategory') : t('engine.choiceHermesCategory') + const tagline = id === 'openclaw' ? t('engine.choiceOpenclawTagline') : t('engine.choiceHermesTagline') + const feats = id === 'openclaw' + ? [t('engine.choiceOpenclawFeat1'), t('engine.choiceOpenclawFeat2'), t('engine.choiceOpenclawFeat3')] + : [t('engine.choiceHermesFeat1'), t('engine.choiceHermesFeat2'), t('engine.choiceHermesFeat3')] + const cta = `${t('engine.choiceCtaEnter')} ${cap}` + + // OpenClaw(左上):序号在前 / Hermes(右下):序号在后 + const productRow = id === 'openclaw' + ? `${ICONS[id]}${esc(num)} · ${esc(cat)}` + : `${esc(cat)} · ${esc(num)}${ICONS[id]}` + return ` - +
+
${productRow}
+
${esc(cap)}
+
${esc(tagline)}
+ + +
` } -async function chooseOption(page, card, option) { - setBusy(page, card, true) - try { - await applyEngineSelection({ - activeEngineId: option.activeEngineId, - enabledEngineIds: option.enabledEngineIds, - deferred: !!option.deferred, - choice: option.id, - engineMode: option.engineMode || '', +function bindHover(page) { + // 用 attribute 替代 :has() — 兼容性更好(旧 WebKit / Linux WebKitGTK) + const stage = page.querySelector('.es-stage') + page.querySelectorAll('.es-panel').forEach(panel => { + const engine = panel.dataset.engine + panel.addEventListener('mouseenter', () => { + if (_busy) return + stage.dataset.hover = engine }) - toast(t('engine.choiceSaved'), 'success') - navigate(option.targetRoute) - } catch (error) { - console.error('[engine-select] choose failed:', error) - toast(t('engine.choiceSaveFailed'), 'error') - setBusy(page, card, false) + panel.addEventListener('mouseleave', () => { + if (_busy) return + delete stage.dataset.hover + }) + }) +} + +function bindClick(page) { + const stage = page.querySelector('.es-stage') + + // 主区:点击三角形选引擎 + stage.addEventListener('click', (event) => { + if (_busy) return + const panel = event.target.closest('.es-panel') + if (!panel) return + const engine = panel.dataset.engine + const option = PRIMARY_OPTIONS.find(o => o.id === engine) + if (option) chooseWithAnimation(page, panel, option, engine) + }) + + // 次级链接:两个都要 / 稍后再说(无对角线动画,直接走选择) + page.querySelectorAll('[data-secondary]').forEach(btn => { + btn.addEventListener('click', async (event) => { + event.stopPropagation() + if (_busy) return + const id = btn.dataset.secondary + const option = SECONDARY_OPTIONS.find(o => o.id === id) + if (!option) return + _busy = true + btn.classList.add('loading') + try { + await applyEngineSelection({ + activeEngineId: option.activeEngineId, + enabledEngineIds: option.enabledEngineIds, + deferred: !!option.deferred, + choice: option.id, + engineMode: option.engineMode || '', + }) + toast(t('engine.choiceSaved'), 'success') + navigate(option.targetRoute) + } catch (error) { + console.error('[engine-select] secondary choose failed:', error) + toast(t('engine.choiceSaveFailed'), 'error') + _busy = false + btn.classList.remove('loading') + } + }) + }) +} + +async function chooseWithAnimation(page, panel, option, engine) { + _busy = true + const stage = page.querySelector('.es-stage') + delete stage.dataset.hover + stage.dataset.expanding = engine + + // 先把 reveal / home mock 节点 attach 到 body — 路由切换时它们不会被销毁 + ensureRevealNodes() + + // 阶段 1: 三角形扩满(CSS 通过 [data-expanding] 触发 clip-path 变化) + // 阶段 2: 600ms 后开始中心圆扩散 + setTimeout(() => { + _revealEl.dataset.engine = engine + _revealEl.classList.add('es-reveal-active') + }, 600) + + // 阶段 3: 1300ms 后保存选择 + 切换路由 + setTimeout(async () => { + try { + await applyEngineSelection({ + activeEngineId: option.activeEngineId, + enabledEngineIds: option.enabledEngineIds, + deferred: !!option.deferred, + choice: option.id, + engineMode: option.engineMode || '', + }) + navigate(option.targetRoute) + // 给新页面一点渲染时间后淡出 reveal 层 + setTimeout(() => { + if (_revealEl) { + _revealEl.classList.add('es-reveal-fadeout') + setTimeout(() => removeRevealNodes(), 600) + } + }, 280) + } catch (error) { + console.error('[engine-select] choose failed:', error) + toast(t('engine.choiceSaveFailed'), 'error') + // 失败回退:移除动画层 + 解除 busy + removeRevealNodes() + delete stage.dataset.expanding + _busy = false + } + }, 1300) +} + +function ensureRevealNodes() { + if (!_revealEl) { + _revealEl = document.createElement('div') + _revealEl.className = 'es-reveal' + document.body.appendChild(_revealEl) + } + if (!_homeEl) { + _homeEl = document.createElement('div') + _homeEl.className = 'es-reveal-home' + document.body.appendChild(_homeEl) } } -function setBusy(page, activeCard, busy) { - page.querySelectorAll('.engine-choice-card').forEach(card => { - card.disabled = busy - card.classList.toggle('loading', busy && card === activeCard) - }) +function removeRevealNodes() { + if (_revealEl) { _revealEl.remove(); _revealEl = null } + if (_homeEl) { _homeEl.remove(); _homeEl = null } + _busy = false +} + +export function cleanup() { + // 路由切走时不主动销毁 reveal 节点(动画完成后会自行淡出) + // 这里仅重置 busy(防卡死) + _busy = false } function esc(value) { diff --git a/src/style/pages.css b/src/style/pages.css index d4b79c8..c03708f 100644 --- a/src/style/pages.css +++ b/src/style/pages.css @@ -2657,189 +2657,399 @@ engines/hermes/style/hermes.css)。原来 .hm-memory-* 那一坨规则已无任何 JS / CSS 引用,已在此处删除。 */ -.engine-select-page { - max-width: 1120px; - margin: 0 auto; - padding: 48px 32px; +/* === Engine Select · Monolith 对角线全屏 === + 左上三角 OpenClaw(石墨黑)vs 右下三角 Hermes(象牙白)。 + 用 position: fixed 跳出 #content 范围,覆盖整个 viewport(含 sidebar)。 */ + +.engine-select-page.es-monolith { + position: fixed; + inset: 0; + z-index: 1000; + margin: 0; + padding: 0; + max-width: none; + overflow: hidden; + background: #f5f3ee; /* 兜底色 */ + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, 'PingFang SC', 'Microsoft YaHei', sans-serif; + -webkit-font-smoothing: antialiased; + animation: es-fadein 0.5s ease; +} +@keyframes es-fadein { from { opacity: 0 } to { opacity: 1 } } + +.es-stage { position: absolute; inset: 0 } + +/* —— 两个三角形 —— */ +.es-panel { + position: absolute; + inset: 0; + cursor: pointer; + transition: filter 0.6s ease, clip-path 0.8s cubic-bezier(0.7, 0, 0.3, 1); +} +.es-panel-openclaw { + clip-path: polygon(0 0, 0 100%, 100% 0); + background: #0c0d12; +} +/* 微妙网格纹(OpenClaw 侧) */ +.es-panel-openclaw::after { + content: ''; + position: absolute; + inset: 0; + background-image: + linear-gradient(rgba(255, 255, 255, 0.025) 1px, transparent 1px), + linear-gradient(90deg, rgba(255, 255, 255, 0.025) 1px, transparent 1px); + background-size: 60px 60px; + background-position: -1px -1px; + pointer-events: none; } -.engine-select-hero { - max-width: 720px; +.es-panel-hermes { + clip-path: polygon(100% 0, 100% 100%, 0 100%); + background: #f5f3ee; +} +.es-panel-hermes::after { + content: ''; + position: absolute; + inset: 0; + background-image: + linear-gradient(rgba(0, 0, 0, 0.04) 1px, transparent 1px), + linear-gradient(90deg, rgba(0, 0, 0, 0.04) 1px, transparent 1px); + background-size: 60px 60px; + background-position: -1px -1px; + pointer-events: none; +} + +/* —— 角落极光发光 —— */ +.es-glow { + position: absolute; + pointer-events: none; + transition: opacity 0.5s ease; +} +.es-glow-openclaw { + top: -10%; + left: -10%; + width: 50%; + height: 50%; + background: radial-gradient(circle, rgba(110, 100, 255, 0.4) 0%, transparent 60%); + filter: blur(40px); + clip-path: polygon(0 0, 100% 0, 0 100%); +} +.es-glow-hermes { + bottom: -10%; + right: -10%; + width: 50%; + height: 50%; + background: radial-gradient(circle, rgba(220, 130, 60, 0.28) 0%, transparent 60%); + filter: blur(40px); + clip-path: polygon(100% 100%, 0 100%, 100% 0); +} + +/* —— 中线(极细发光) —— */ +.es-divider { + position: absolute; + inset: 0; + background: linear-gradient(45deg, transparent calc(50% - 0.5px), rgba(180, 180, 180, 0.6) 50%, transparent calc(50% + 0.5px)); + pointer-events: none; + transition: opacity 0.5s ease; +} + +/* —— 顶部 banner + 角标 —— */ +.es-top-banner { + position: absolute; + top: 32px; + left: 50%; + transform: translateX(-50%); + color: rgba(180, 180, 180, 0.78); + font-size: 11px; + letter-spacing: 0.4em; + text-transform: uppercase; + z-index: 5; + pointer-events: none; + font-weight: 500; + transition: opacity 0.5s ease; +} +.es-corner-mark { + position: absolute; + font-size: 11px; + letter-spacing: 0.32em; + text-transform: uppercase; + pointer-events: none; + z-index: 5; + font-weight: 500; + transition: opacity 0.5s ease; +} +.es-corner-tl { top: 32px; left: 36px; color: rgba(255, 255, 255, 0.45) } +.es-corner-br { bottom: 32px; right: 36px; color: rgba(0, 0, 0, 0.45) } + +/* —— 内容定位(重心放在三角形质心) —— */ +.es-content { + position: absolute; + pointer-events: none; + z-index: 3; + transition: transform 0.7s cubic-bezier(0.22, 1, 0.36, 1), opacity 0.5s, filter 0.5s; +} +.es-content-openclaw { + top: 11%; + left: 6.5%; + color: #fafafa; + text-align: left; + max-width: 44vw; +} +.es-content-hermes { + bottom: 11%; + right: 6.5%; + color: #18171a; + text-align: right; + max-width: 44vw; +} + +/* —— hover 联动(用 [data-hover] attribute 替代 :has()) —— */ +.es-stage[data-hover='openclaw'] .es-panel-hermes { filter: brightness(0.94) saturate(0.85) } +.es-stage[data-hover='hermes'] .es-panel-openclaw { filter: brightness(0.6) } +.es-stage[data-hover='openclaw'] .es-content-hermes, +.es-stage[data-hover='hermes'] .es-content-openclaw { + opacity: 0.25; + filter: blur(2px); +} +.es-stage[data-hover='openclaw'] .es-content-openclaw { transform: translateX(8px) } +.es-stage[data-hover='hermes'] .es-content-hermes { transform: translateX(-8px) } + +/* —— 顶部 logo + 序号 —— */ +.es-product-row { + display: flex; + align-items: center; + gap: 16px; + margin-bottom: 28px; +} +.es-content-hermes .es-product-row { flex-direction: row-reverse } + +.es-product-icon { + width: 44px; + height: 44px; + border-radius: 12px; + background: rgba(255, 255, 255, 0.08); + border: 1px solid rgba(255, 255, 255, 0.12); + display: grid; + place-items: center; +} +.es-content-hermes .es-product-icon { + background: rgba(0, 0, 0, 0.05); + border: 1px solid rgba(0, 0, 0, 0.12); +} +.es-product-icon svg { width: 22px; height: 22px; stroke: currentColor; fill: none; stroke-width: 1.6 } + +.es-product-tag { + font-size: 12px; + font-weight: 500; + letter-spacing: 0.18em; + text-transform: uppercase; + opacity: 0.55; +} + +/* —— 巨字标题 —— */ +.es-title { + font-size: clamp(80px, 13vw, 200px); + font-weight: 200; + letter-spacing: -0.055em; + line-height: 0.92; margin-bottom: 28px; } -.engine-select-kicker { +/* —— 副标题 —— */ +.es-tagline { + font-size: clamp(20px, 1.8vw, 28px); + line-height: 1.4; + font-weight: 300; + max-width: 540px; + margin-bottom: 32px; + opacity: 0.78; + letter-spacing: -0.01em; +} +.es-content-hermes .es-tagline { margin-left: auto } + +/* —— 特性列表 —— */ +.es-feature-list { + display: flex; + flex-direction: column; + gap: 12px; + margin-bottom: 44px; + font-size: 14px; + font-weight: 400; + line-height: 1.6; + list-style: none; + padding: 0; +} +.es-content-hermes .es-feature-list { align-items: flex-end } +.es-feature-list li { + display: flex; + align-items: center; + gap: 12px; + opacity: 0.7; +} +.es-content-hermes .es-feature-list li { flex-direction: row-reverse } +.es-feature-list li::before { + content: ''; + width: 16px; + height: 1px; + background: currentColor; + opacity: 0.4; +} + +/* —— CTA 按钮 —— */ +.es-cta { display: inline-flex; align-items: center; - padding: 4px 10px; - margin-bottom: 14px; - border: 1px solid var(--border-primary); - border-radius: 999px; - color: var(--accent); - background: var(--bg-glass); - font-size: 12px; - font-weight: 700; - letter-spacing: .08em; - text-transform: uppercase; -} - -.engine-select-hero h1 { - margin: 0 0 12px; - color: var(--text-primary); - font-size: clamp(28px, 4vw, 44px); - line-height: 1.1; - letter-spacing: -0.03em; -} - -.engine-select-hero p { - margin: 0; - color: var(--text-secondary); - font-size: 16px; - line-height: 1.7; -} - -.engine-choice-grid { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 16px; -} - -.engine-choice-card { - position: relative; - display: flex; - align-items: flex-start; - gap: 16px; - width: 100%; - min-height: 172px; - padding: 22px; - border: 1px solid var(--border-primary); - border-radius: 22px; - background: - radial-gradient(circle at top right, rgba(99, 102, 241, 0.12), transparent 34%), - var(--bg-card); - color: var(--text-primary); - text-align: left; + gap: 12px; + padding: 16px 28px; + border-radius: 8px; + font-size: 13px; + font-weight: 600; + letter-spacing: 0.04em; cursor: pointer; - transition: transform .18s ease, border-color .18s ease, background .18s ease, box-shadow .18s ease; + pointer-events: none; + transition: all 0.35s ease; + font-family: inherit; } - -.engine-choice-card:hover { - transform: translateY(-2px); - border-color: var(--accent); - background: - radial-gradient(circle at top right, rgba(99, 102, 241, 0.18), transparent 38%), - var(--bg-card-hover); - box-shadow: 0 18px 50px rgba(0, 0, 0, 0.12); +.es-content-openclaw .es-cta { + background: rgba(255, 255, 255, 0.08); + color: #fff; + border: 1px solid rgba(255, 255, 255, 0.18); } - -.engine-choice-card:disabled { - cursor: wait; - opacity: .72; +.es-content-hermes .es-cta { + background: rgba(0, 0, 0, 0.06); + color: #18171a; + border: 1px solid rgba(0, 0, 0, 0.18); + flex-direction: row-reverse; } - -.engine-choice-card.loading .engine-choice-arrow { - animation: engine-choice-pulse .8s ease-in-out infinite alternate; +.es-stage[data-hover='openclaw'] .es-content-openclaw .es-cta { + background: #fff; + color: #0a0a0a; + border-color: #fff; } - -.engine-choice-icon { +.es-stage[data-hover='hermes'] .es-content-hermes .es-cta { + background: #0a0a0a; + color: #fff; + border-color: #0a0a0a; +} +.es-cta-arrow { + width: 18px; + height: 18px; display: grid; place-items: center; - width: 48px; - height: 48px; - flex: 0 0 auto; - border-radius: 16px; - color: var(--accent); - background: var(--bg-glass); - border: 1px solid var(--border-primary); + transition: transform 0.3s; + font-weight: 400; } +.es-stage[data-hover='openclaw'] .es-content-openclaw .es-cta-arrow { transform: translateX(4px) } +.es-stage[data-hover='hermes'] .es-content-hermes .es-cta-arrow { transform: translateX(-4px) } -.engine-choice-icon svg { - width: 24px; - height: 24px; -} - -.engine-choice-content { - display: flex; - min-width: 0; - flex: 1; - flex-direction: column; - gap: 10px; -} - -.engine-choice-title-row { +/* —— 底部次级链接(两个都要 / 稍后再说) —— */ +.es-secondary { + position: absolute; + bottom: 28px; + left: 50%; + transform: translateX(-50%); display: flex; align-items: center; - gap: 10px; - flex-wrap: wrap; -} - -.engine-choice-title { - font-size: 18px; - font-weight: 800; - letter-spacing: -0.02em; -} - -.engine-choice-badge { - padding: 2px 8px; + gap: 14px; + z-index: 4; + padding: 8px 18px; border-radius: 999px; - color: var(--accent); - background: var(--accent-muted, rgba(99, 102, 241, 0.12)); + background: rgba(150, 150, 150, 0.08); + backdrop-filter: blur(20px) saturate(1.4); + -webkit-backdrop-filter: blur(20px) saturate(1.4); + border: 1px solid rgba(180, 180, 180, 0.18); + transition: opacity 0.5s ease; +} +.es-secondary-link { + background: transparent; + border: 0; + font-family: inherit; + font-size: 12.5px; + letter-spacing: 0.04em; + color: rgba(150, 150, 150, 0.85); + cursor: pointer; + padding: 6px 8px; + border-radius: 6px; + transition: color 0.2s ease, background 0.2s ease; +} +.es-secondary-link:hover { + color: rgba(20, 20, 20, 0.95); + background: rgba(180, 180, 180, 0.18); +} +.es-secondary-link.loading { + opacity: 0.5; + cursor: wait; +} +.es-secondary-sep { + color: rgba(150, 150, 150, 0.5); font-size: 11px; - font-weight: 700; + user-select: none; } -.engine-choice-desc { - color: var(--text-secondary); - font-size: 14px; - line-height: 1.6; +/* —— 选中态:三角形扩满(数据属性触发) —— */ +.es-stage[data-expanding='openclaw'] .es-panel-openclaw, +.es-stage[data-expanding='hermes'] .es-panel-hermes { + clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%); + z-index: 5; +} +.es-stage[data-expanding='openclaw'] .es-panel-hermes, +.es-stage[data-expanding='hermes'] .es-panel-openclaw { + opacity: 0; + transition: opacity 0.4s ease; +} +.es-stage[data-expanding] .es-content, +.es-stage[data-expanding] .es-divider, +.es-stage[data-expanding] .es-top-banner, +.es-stage[data-expanding] .es-corner-mark, +.es-stage[data-expanding] .es-secondary { + opacity: 0; } -.engine-choice-meta { - margin-top: auto; - color: var(--text-tertiary); - font-size: 12px; -} - -.engine-choice-arrow { - margin-left: auto; - color: var(--text-tertiary); - font-size: 22px; - line-height: 1; -} - -.engine-choice-note { - margin-top: 18px; - padding: 16px 18px; - border: 1px solid var(--border-primary); - border-radius: 16px; - background: var(--bg-glass); - color: var(--text-secondary); - font-size: 13px; - line-height: 1.6; -} - -.engine-choice-note-title { - margin-bottom: 4px; - color: var(--text-primary); - font-weight: 700; -} - -@keyframes engine-choice-pulse { - from { transform: translateX(0); opacity: .5; } - to { transform: translateX(4px); opacity: 1; } +/* —— 全屏 reveal(attach 到 body,跨路由切换存活) —— */ +.es-reveal { + position: fixed; + top: 50%; + left: 50%; + width: 0; + height: 0; + border-radius: 50%; + transform: translate(-50%, -50%); + z-index: 9000; + pointer-events: none; + background: #0c0d12; + transition: width 0.9s cubic-bezier(0.65, 0, 0.35, 1), height 0.9s cubic-bezier(0.65, 0, 0.35, 1), opacity 0.6s ease; } +.es-reveal[data-engine='openclaw'] { background: #0c0d12 } +.es-reveal[data-engine='hermes'] { background: #f5f3ee } +.es-reveal.es-reveal-active { width: 260vmax; height: 260vmax } +.es-reveal.es-reveal-fadeout { opacity: 0 } +/* —— 移动端响应式 —— */ @media (max-width: 760px) { - .engine-select-page { - padding: 28px 18px; - } + .es-content-openclaw { top: 8%; left: 5% } + .es-content-hermes { bottom: 8%; right: 5% } + .es-title { font-size: clamp(56px, 14vw, 120px) } + .es-tagline { font-size: 16px; margin-bottom: 20px } + .es-feature-list { gap: 8px; font-size: 12.5px; margin-bottom: 28px } + .es-cta { padding: 12px 22px; font-size: 12px } + .es-corner-tl, .es-corner-br { font-size: 10px; letter-spacing: 0.24em } + .es-corner-tl { top: 16px; left: 18px } + .es-corner-br { bottom: 16px; right: 18px } + .es-top-banner { top: 18px; font-size: 10px; letter-spacing: 0.32em } + .es-secondary { bottom: 16px } +} - .engine-choice-grid { - grid-template-columns: 1fr; - } - - .engine-choice-card { - min-height: auto; - padding: 18px; +@media (prefers-reduced-motion: reduce) { + .engine-select-page.es-monolith, + .es-panel, + .es-content, + .es-cta, + .es-glow, + .es-divider, + .es-reveal, + .es-top-banner, + .es-corner-mark, + .es-secondary { + transition: none !important; + animation: none !important; } } \ No newline at end of file