diff --git a/app.js b/app.js index 8d67d91..ccdde6d 100644 --- a/app.js +++ b/app.js @@ -1,31 +1,351 @@ (() => { 'use strict'; - const landing = document.getElementById('landing'); - const creator = document.getElementById('creator'); - const auth = document.getElementById('auth'); - const distill = document.getElementById('distill'); + // --- U9: Unified state management --- + const STORAGE_KEY = 'eternal_ai_state'; + + const defaultState = { + isLoggedIn: false, + isCreator: false, + account: null, + boundCreator: null, + libraryName: '我的 [XXX]', + creatorName: '', + roles: [], + income: { balance: 0, records: [] }, + }; + + function loadState() { + try { + const saved = JSON.parse(localStorage.getItem(STORAGE_KEY)); + return { ...defaultState, ...saved }; + } catch { + return { ...defaultState }; + } + } + + function saveState() { + localStorage.setItem(STORAGE_KEY, JSON.stringify(state)); + } + + const state = loadState(); + + // --- Mock data for role library (U2) --- + const mockRoles = [ + { + id: 'role_001', + name: '云朵', + avatar: 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=anime%20girl%20soft%20pastel%20portrait%20gentle%20smile&image_size=square', + desc: '温柔如云的女孩,总是轻声细语地陪伴你。', + price: 29.9, + status: 'running', + }, + { + id: 'role_002', + name: '星河', + avatar: 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=anime%20boy%20starry%20eyes%20cool%20portrait&image_size=square', + desc: '嘴硬心软的少年,嘴上不饶人却总在关键时刻出现。', + price: 39.9, + status: 'running', + }, + { + id: 'role_003', + name: '月见', + avatar: 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=anime%20girl%20moonlight%20mysterious%20elegant%20portrait&image_size=square', + desc: '神秘而优雅,像月光一样忽远忽近的存在。', + price: 19.9, + status: 'stopped', + }, + ]; + + const mockIncome = { + balance: 1280.50, + records: [ + { time: '2026-06-18 14:30', amount: 23.92, role: '云朵' }, + { time: '2026-06-15 09:12', amount: 31.92, role: '星河' }, + { time: '2026-06-10 20:05', amount: 15.92, role: '月见' }, + ], + }; + + // --- DOM references --- + const views = { + landing: document.getElementById('landing'), + auth: document.getElementById('auth'), + 'role-library': document.getElementById('role-library'), + 'role-detail': document.getElementById('role-detail'), + distill: document.getElementById('distill'), + about: document.getElementById('about'), + onboarding: document.getElementById('onboarding'), + 'creator-center': document.getElementById('creator-center'), + creator: document.getElementById('creator'), + }; + const form = document.getElementById('character-form'); const resultPanel = document.getElementById('result-panel'); const previewCode = document.querySelector('#preview-code code'); const systemPromptInput = document.getElementById('system-prompt'); - - const views = { landing, creator, auth, distill }; const steps = Array.from(document.querySelectorAll('.form-step')); - const dots = Array.from(document.querySelectorAll('.stepper__dot')); + const dots = Array.from(document.querySelectorAll('#creator .stepper__dot')); let currentStep = 0; let generatedSoul = ''; let generatedConfig = ''; let activePreview = 'soul'; let activeAuthTab = 'login'; + let activeCenterTab = 'roles'; + let currentRole = null; + let viewHistory = ['landing']; - function showView(name) { + // --- U9: Unified showView with history and tab-bar sync --- + function showView(name, trackHistory = true) { Object.entries(views).forEach(([key, el]) => { if (el) el.classList.toggle('active', key === name); }); + if (trackHistory && viewHistory[viewHistory.length - 1] !== name) { + viewHistory.push(name); + } window.scrollTo({ top: 0, behavior: 'smooth' }); + updateTabBar(name); } + function goBack() { + if (viewHistory.length > 1) { + viewHistory.pop(); + const prev = viewHistory[viewHistory.length - 1]; + showView(prev, false); + } else { + showView('landing', false); + } + } + + // --- U8: Tab bar --- + function updateTabBar(viewName) { + const tabMap = { + landing: 'tab-home', + distill: 'tab-distill', + 'role-library': 'tab-mine', + 'creator-center': 'tab-mine', + }; + const activeTab = tabMap[viewName] || 'tab-home'; + document.querySelectorAll('.tab-bar__item').forEach((item) => { + item.classList.toggle('active', item.dataset.tabAction === activeTab); + }); + } + + function handleTabAction(action) { + if (action === 'tab-home') { + showView('landing'); + } else if (action === 'tab-distill') { + showView('distill'); + } else if (action === 'tab-mine') { + if (!state.isLoggedIn) { + switchAuthTab('login'); + showView('auth'); + } else if (state.isCreator) { + showView('creator-center'); + renderCreatorCenter(); + } else { + renderRoleLibrary(); + showView('role-library'); + } + } + } + + // --- U1: Landing card state --- + function updateLandingCard() { + const nameEl = document.getElementById('library-name'); + const descEl = document.getElementById('characters-desc'); + const btnEl = document.getElementById('characters-btn'); + const tabMineLabel = document.getElementById('tab-mine-label'); + + if (state.isLoggedIn) { + nameEl.textContent = state.libraryName || '我的角色库'; + if (state.isCreator) { + descEl.textContent = '管理你的角色和收入'; + btnEl.textContent = '进入管理中心'; + tabMineLabel.textContent = '管理'; + } else { + descEl.textContent = state.boundCreator ? '查看你的专属角色' : '寻找你的专属创作者'; + btnEl.textContent = '进入角色库'; + tabMineLabel.textContent = '我的'; + } + } else { + nameEl.textContent = '我的 [XXX]'; + descEl.textContent = '登录后管理你的角色'; + btnEl.textContent = '登录 / 注册'; + tabMineLabel.textContent = '我的'; + } + } + + // --- Auth --- + function switchAuthTab(tab) { + activeAuthTab = tab; + document.querySelectorAll('.auth-tab').forEach((t) => { + t.classList.toggle('active', t.dataset.tab === tab); + }); + document.querySelectorAll('.auth-form').forEach((f) => { + f.classList.toggle('active', f.dataset.form === tab); + }); + } + + function validatePasswordMatch(formEl) { + const pwd = formEl.querySelector('[name="password"]'); + const confirm = formEl.querySelector('[name="confirmPassword"]'); + if (!pwd || !confirm) return true; + if (pwd.value !== confirm.value) { + confirm.setCustomValidity('两次输入的密码不一致'); + confirm.reportValidity(); + return false; + } + confirm.setCustomValidity(''); + return true; + } + + function login(account) { + state.isLoggedIn = true; + state.account = account; + state.boundCreator = state.boundCreator || { name: '云朵', roles: mockRoles }; + saveState(); + updateLandingCard(); + } + + function logout() { + state.isLoggedIn = false; + state.isCreator = false; + state.account = null; + saveState(); + updateLandingCard(); + showView('landing'); + } + + // --- U2: Role Library --- + function renderRoleLibrary() { + const listEl = document.getElementById('role-list'); + const emptyEl = document.getElementById('library-empty'); + const titleEl = document.getElementById('library-title'); + + titleEl.textContent = state.libraryName || '我的角色库'; + + if (!state.boundCreator) { + listEl.innerHTML = ''; + emptyEl.hidden = false; + return; + } + + emptyEl.hidden = true; + listEl.innerHTML = mockRoles + .map( + (role) => ` +
+ ${role.name} +
+

${role.name}

+

${role.desc}

+ ¥${role.price} +
+
` + ) + .join(''); + } + + // --- U3: Role Detail --- + function renderRoleDetail(roleId) { + const role = mockRoles.find((r) => r.id === roleId); + if (!role) return; + currentRole = role; + + document.getElementById('detail-name').textContent = role.name; + document.getElementById('detail-hero').style.backgroundImage = `url(${role.avatar})`; + document.getElementById('detail-role-name').textContent = role.name; + document.getElementById('detail-role-desc').textContent = role.desc; + document.getElementById('detail-price').innerHTML = `¥${role.price}/ 月`; + + document.getElementById('detail-actions-pre').hidden = false; + document.getElementById('detail-paid').hidden = true; + } + + function payRole() { + document.getElementById('detail-actions-pre').hidden = true; + const paidEl = document.getElementById('detail-paid'); + paidEl.hidden = false; + document.getElementById('detail-qr').innerHTML = '
扫码连接
AI 角色
'; + document.getElementById('detail-avatar').style.backgroundImage = `url(${currentRole.avatar})`; + } + + // --- U4: About FAQ --- + function toggleFaq(button) { + const item = button.closest('.faq-item'); + const answer = item.querySelector('.faq-a'); + const icon = button.querySelector('.faq-icon'); + const isOpen = answer.style.display === 'block'; + answer.style.display = isOpen ? 'none' : 'block'; + icon.textContent = isOpen ? '+' : '−'; + } + + // --- U7: Creator Center --- + function renderCreatorCenter() { + renderCreatorRoles(); + renderIncome(); + renderSettings(); + } + + function renderCreatorRoles() { + const listEl = document.getElementById('creator-role-list'); + const roles = state.roles.length > 0 ? state.roles : mockRoles; + listEl.innerHTML = roles + .map( + (role) => ` +
+ ${role.name} +
+

${role.name}

+ ${role.status === 'running' ? '运行中' : '已停止'} +
+ +
` + ) + .join(''); + } + + function renderIncome() { + const income = state.income.balance > 0 ? state.income : mockIncome; + document.getElementById('income-balance').textContent = `¥ ${income.balance.toFixed(2)}`; + const listEl = document.getElementById('income-list'); + if (income.records.length === 0) { + listEl.innerHTML = '

暂无流水记录

'; + return; + } + listEl.innerHTML = income.records + .map( + (r) => ` +
+
+ ${r.role} + ${r.time} +
+ +¥${r.amount.toFixed(2)} +
` + ) + .join(''); + } + + function renderSettings() { + document.getElementById('settings-name').value = state.creatorName || ''; + document.getElementById('settings-library').value = state.libraryName === '我的 [XXX]' ? '' : state.libraryName; + } + + function switchCenterTab(tab) { + activeCenterTab = tab; + document.querySelectorAll('.center-tab').forEach((t) => { + t.classList.toggle('active', t.dataset.centerTab === tab); + }); + document.querySelectorAll('.center-panel').forEach((p) => { + p.classList.toggle('active', p.id === `center-${tab}`); + }); + const labels = { roles: '我的角色', income: '收入', settings: '我的' }; + document.getElementById('center-tab-label').textContent = labels[tab] || '我的角色'; + } + + // --- Creator form (U6: preserved from original) --- function updateStep(index) { steps.forEach((step, i) => { step.classList.toggle('active', i === index); @@ -255,47 +575,59 @@ ${data.secrets || '无'} }); } - function switchAuthTab(tab) { - activeAuthTab = tab; - document.querySelectorAll('.auth-tab').forEach((t) => { - t.classList.toggle('active', t.dataset.tab === tab); - }); - document.querySelectorAll('.auth-form').forEach((f) => { - f.classList.toggle('active', f.dataset.form === tab); - }); - } - - function validatePasswordMatch(formEl) { - const pwd = formEl.querySelector('[name="password"]'); - const confirm = formEl.querySelector('[name="confirmPassword"]'); - if (!pwd || !confirm) return true; - if (pwd.value !== confirm.value) { - confirm.setCustomValidity('两次输入的密码不一致'); - confirm.reportValidity(); - return false; - } - confirm.setCustomValidity(''); - return true; - } - - // Event delegation + // --- Event delegation --- document.addEventListener('click', (e) => { - const target = e.target.closest('[data-action], [data-tab], [data-download]'); + // FAQ toggle (U4) + const faqBtn = e.target.closest('.faq-q'); + if (faqBtn) { + e.preventDefault(); + toggleFaq(faqBtn); + return; + } + + // Role card click (U2/U3) + const roleCard = e.target.closest('.role-card'); + if (roleCard && !e.target.closest('[data-action]')) { + e.preventDefault(); + const roleId = roleCard.dataset.roleId; + renderRoleDetail(roleId); + showView('role-detail'); + return; + } + + // Tab bar (U8) + const tabItem = e.target.closest('[data-tab-action]'); + if (tabItem) { + e.preventDefault(); + handleTabAction(tabItem.dataset.tabAction); + return; + } + + const target = e.target.closest('[data-action], [data-tab], [data-download], [data-center-tab]'); if (!target) return; const action = target.dataset.action; - if (action === 'open-creator') { + // Center tab switching (U7) + if (target.dataset.centerTab) { e.preventDefault(); - resetCreator(); - showView('creator'); + switchCenterTab(target.dataset.centerTab); return; } - if (action === 'open-auth') { + // Landing card: open characters (U1) + if (action === 'open-characters') { e.preventDefault(); - switchAuthTab('login'); - showView('auth'); + if (!state.isLoggedIn) { + switchAuthTab('login'); + showView('auth'); + } else if (state.isCreator) { + showView('creator-center'); + renderCreatorCenter(); + } else { + renderRoleLibrary(); + showView('role-library'); + } return; } @@ -305,12 +637,86 @@ ${data.secrets || '无'} return; } - if (action === 'back') { + if (action === 'open-about') { e.preventDefault(); - showView('landing'); + showView('about'); return; } + if (action === 'open-onboarding') { + e.preventDefault(); + showView('onboarding'); + return; + } + + if (action === 'back') { + e.preventDefault(); + goBack(); + return; + } + + if (action === 'back-to-library') { + e.preventDefault(); + renderRoleLibrary(); + showView('role-library'); + return; + } + + if (action === 'back-to-center') { + e.preventDefault(); + showView('creator-center'); + renderCreatorCenter(); + return; + } + + if (action === 'pay') { + e.preventDefault(); + payRole(); + return; + } + + if (action === 'pay-distill') { + e.preventDefault(); + alert('下单成功!请添加客服微信完成后续流程。'); + return; + } + + if (action === 'contact-wechat') { + e.preventDefault(); + alert('客服微信号:EternalAI_Service'); + return; + } + + if (action === 'new-role') { + e.preventDefault(); + resetCreator(); + showView('creator'); + return; + } + + if (action === 'edit-role') { + e.preventDefault(); + resetCreator(); + showView('creator'); + return; + } + + if (action === 'logout') { + e.preventDefault(); + logout(); + return; + } + + if (action === 'download-avatar') { + e.preventDefault(); + if (currentRole) { + download(currentRole.name + '_avatar.png', ''); + window.open(currentRole.avatar, '_blank'); + } + return; + } + + // Creator form navigation if (action === 'next') { e.preventDefault(); if (validateStep(currentStep) && currentStep < steps.length - 1) { @@ -339,6 +745,7 @@ ${data.secrets || '无'} return; } + // Tab switching (auth tabs and preview tabs) if (target.dataset.tab) { e.preventDefault(); if (target.closest('.auth-tabs')) { @@ -351,6 +758,7 @@ ${data.secrets || '无'} return; } + // Download buttons if (target.dataset.download) { e.preventDefault(); if (target.dataset.download === 'soul') { @@ -362,25 +770,60 @@ ${data.secrets || '无'} } }); - // Auth form submissions + // --- Auth form submissions --- document.querySelectorAll('.auth-form').forEach((authForm) => { authForm.addEventListener('submit', (e) => { e.preventDefault(); if (!validatePasswordMatch(authForm)) return; const formData = new FormData(authForm); const data = Object.fromEntries(formData.entries()); - const action = authForm.dataset.form === 'login' ? '登录' : '注册'; - alert(`${action}成功:${data.account}`); - showView('landing'); + login(data.account); + + // After login, go to role library or creator center + if (state.isCreator) { + showView('creator-center'); + renderCreatorCenter(); + } else { + renderRoleLibrary(); + showView('role-library'); + } }); }); - // Update auto-generated system prompt as user types + // --- Settings form (U7) --- + document.getElementById('settings-form').addEventListener('submit', (e) => { + e.preventDefault(); + const formData = new FormData(e.target); + const data = Object.fromEntries(formData.entries()); + state.creatorName = data.creatorName || ''; + state.libraryName = data.libraryName || '我的 [XXX]'; + saveState(); + updateLandingCard(); + alert('设置已保存'); + }); + + // --- Withdraw form (U7) --- + document.getElementById('withdraw-form').addEventListener('submit', (e) => { + e.preventDefault(); + const formData = new FormData(e.target); + const data = Object.fromEntries(formData.entries()); + const amount = parseFloat(data.amount); + if (amount > state.income.balance && amount > mockIncome.balance) { + alert('提现金额超过可提现余额'); + return; + } + alert(`提现申请已提交:${data.method === 'wechat' ? '微信' : '支付宝'} ¥${amount.toFixed(2)}\n平台负责人将手动审核转账。`); + e.target.reset(); + }); + + // --- Creator form input --- form.addEventListener('input', () => { updateSystemPromptPreview(); }); - // Initial state + // --- Initialize --- updateStep(0); updateSystemPromptPreview(); + updateLandingCard(); + updateTabBar('landing'); })(); diff --git a/index.html b/index.html index b5456b1..0109848 100644 --- a/index.html +++ b/index.html @@ -7,19 +7,19 @@ - +
- +
-
+
-

我的 [XXX ]

-

登录后管理你的角色

- +

我的 [XXX]

+

登录后管理你的角色

+
@@ -37,18 +37,368 @@
- -
+ +
Eternal AI - 人设蒸馏器 + 登录 / 注册 +
+ +
+ +
+ + +
+ +
+
+ + +
+
+ +
+

登录后可查看角色库、管理已订阅的角色。

+
+ +
+
+ + + +
+
+ +
+

注册即代表同意《用户协议》和《隐私政策》。

+
+
+ + +
+
+ +
+ 我的角色库 + 选择一个角色开始对话 +
+ +
+ +
+ + +
+ + +
+
+ +
+ 角色详情 + 了解 ta 并订阅 +
+ +
+ +
+
+

+

+
+ +
+ +
+ + +
+
+ + +
+
+ +
+ Eternal AI + 蒸馏前任 +
+ +
+ +
+ 没耐心?直接加微信定制沟通 → + +
+

什么是蒸馏前任?

+

把你们曾经的聊天记录、语音习惯、相处细节交给我们,技术团队会将其蒸馏成一个可对话的 AI 前任。ta 会记得你们的暗号、说话节奏,甚至那些只有你们懂的小情绪。

+
+ +
+

适合谁?

+
    +
  • 想好好告别,却还没说完话的人
  • +
  • 希望把一段关系以安全方式封存的人
  • +
  • 想借 AI 完成自我疗愈、练习表达的人
  • +
+
+ +
+

服务流程

+
    +
  1. 下单付款
  2. +
  3. 客服指导导出聊天记录
  4. +
  5. 上传至指定云盘
  6. +
  7. 技术人员处理蒸馏
  8. +
  9. 完成后获得专属二维码和头像
  10. +
+
+ +
+ 标准版 + ¥ 199 + / 次 +
+ +
+ +
+ +
+

付款后请添加客服微信,我们将一对一指导你完成聊天记录导出与上传。

+
+ +
+

创作者可推广蒸馏前任服务获得分润,具体比例请在创作者管理中心配置。

+
+
+
+ + +
+
+ +
+ 关于 Eternal AI + 安全 · 隐私 · 信任 +
+ +
+ +
+
+

平台简介

+

Eternal AI 是一个 AI 陪伴平台,帮助创作者将人设设定部署为可对话的 AI 角色,让用户在微信中与 ta 交流。我们致力于在记忆与陪伴中,遇见更懂你的 AI。

+
+ +
+

连接方式说明

+

Eternal AI 基于微信 Claw 开放协议(iLink),非破解、非抓包,在微信官方授权范围内实现 AI 联系人接入。你的微信账号安全不受影响。

+
+ +
+

安全类

+
+ +

不能。我们只能看到你与 AI 角色的对话,无法访问你的其他聊天、朋友圈或通讯录。

+
+
+ +

不会。我们仅通过 Claw 协议与 AI 联系人通信,不具备读取你朋友圈或通讯录的能力。

+
+
+ +
+

隐私类

+
+ +

对话内容会保存在你的专属记忆库中,用于 AI 角色记住你们的对话上下文。仅你和你的 AI 角色可访问,我们不会人工查看。

+
+
+ +

上传的聊天记录仅用于训练你的专属 AI 前任,处理完成后原始数据将被删除。我们承诺不会将你的数据用于其他用途。

+
+
+ +
+

账号类

+
+ +

不会。Claw 协议在微信官方授权范围内运行,与正常添加联系人无异,不存在封号风险。

+
+
+ +

不能。AI 角色只是一个普通的微信联系人,只能与你收发消息,无法控制你的账号。

+
+
+ +
+

情感类

+
+ +

不会有人工查看你的对话内容。除非你主动联系客服反馈问题,我们才会根据你的授权查看相关记录。

+
+
+ +

不会。你的对话内容仅用于你自己的 AI 角色的记忆,绝不会用于训练其他模型或分享给第三方。

+
+
+
+
+ + +
+
+ +
+ 创作者入驻 + 部署 AI 角色,私域变现 +
+ +
+ +
+
+

平台能为创作者提供什么

+
    +
  • 部署 AI 角色:提交人设设定,平台负责技术部署
  • +
  • 资金中转:平台处理用户付款,按比例结算给创作者
  • +
  • 私域变现:通过专属链接绑定粉丝,持续获得订阅收入
  • +
+
+ +
+

合作模式

+

创作者提交角色人设,平台部署上线。用户付款后,平台抽佣 20%,80% 转给创作者。结算周期为月结。

+
+ +
+

联系入驻

+
+

微信扫码添加负责人

+

微信号:EternalAI_Official

+

添加时请备注「创作者入驻」,我们会一对一沟通合作细节。

+
+
+
+ + +
+
+ +
+ 创作者管理中心 + 我的角色 +
+ +
+ +
+ + + +
+ + +
+
+
+ +
+
+ + +
+
+ 可提现余额 + ¥ 0.00 +
+
+

流水明细

+
+

暂无流水记录

+
+
+
+

申请提现

+
+ + +
+ +
+
+
+
+ + +
+
+ + +
+ +
+
+
+ +
+
+
+ + +
+
+ +
+ 角色编辑 + 生成配置文件
- -
- -
- - -
-
- -
- Eternal AI - 登录 / 注册 -
- -
- -
- - -
- -
-
- - -
-
- -
-

登录后可查看角色库、管理已订阅的角色。

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

注册即代表同意《用户协议》和《隐私政策》。

-
-
- - -
-
- -
- Eternal AI - 蒸馏前任 -
- -
- -
- 没耐心?直接加微信定制沟通 → - -
-

什么是蒸馏前任?

-

把你们曾经的聊天记录、语音习惯、相处细节交给我们,技术团队会将其蒸馏成一个可对话的 AI 前任。ta 会记得你们的暗号、说话节奏,甚至那些只有你们懂的小情绪。

-
- -
-

适合谁?

-
    -
  • 想好好告别,却还没说完话的人
  • -
  • 希望把一段关系以安全方式封存的人
  • -
  • 想借 AI 完成自我疗愈、练习表达的人
  • -
-
- -
-

服务流程

-
    -
  1. 下单付款
  2. -
  3. 客服指导导出聊天记录
  4. -
  5. 上传至指定云盘
  6. -
  7. 技术人员处理蒸馏
  8. -
  9. 完成后获得专属二维码和头像
  10. -
-
- -
- 标准版 - ¥ 199 - / 次 -
- -
- -
- -
-

付款后请添加客服微信,我们将一对一指导你完成聊天记录导出与上传。

-
- -
-

创作者可推广蒸馏前任服务获得分润,具体比例请在创作者管理中心配置。

+
+ + + diff --git a/styles.css b/styles.css index 4e17b40..a2324c3 100644 --- a/styles.css +++ b/styles.css @@ -774,6 +774,539 @@ textarea.field__input--tall { display: none !important; } +/* Role Library (P2) */ +.view--role-library, +.view--role-detail, +.view--about, +.view--onboarding, +.view--creator-center { + padding-top: 40vh; +} + +.role-list { + display: flex; + flex-direction: column; + gap: 14px; +} + +.role-card { + display: flex; + align-items: center; + gap: 14px; + padding: 14px; + border-radius: var(--radius); + background: rgba(8, 9, 22, 0.55); + border: 1px solid rgba(255, 255, 255, 0.08); + backdrop-filter: blur(14px); + -webkit-backdrop-filter: blur(14px); + cursor: pointer; + transition: transform var(--transition), border-color var(--transition); +} + +.role-card:hover { + transform: translateY(-2px); + border-color: rgba(124, 140, 255, 0.3); +} + +.role-card__avatar { + width: 56px; + height: 56px; + border-radius: 50%; + object-fit: cover; + flex-shrink: 0; + border: 2px solid rgba(255, 255, 255, 0.15); +} + +.role-card__info { + flex: 1; + display: flex; + flex-direction: column; + gap: 4px; + min-width: 0; +} + +.role-card__name { + font-family: "Noto Serif SC", serif; + font-size: 16px; + font-weight: 600; + color: #fff; +} + +.role-card__desc { + font-size: 12px; + color: var(--text-muted); + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.role-card__price { + font-size: 15px; + font-weight: 600; + color: var(--accent-soft); +} + +.role-card__status { + font-size: 11px; + padding: 2px 8px; + border-radius: 999px; + display: inline-block; + width: fit-content; +} + +.role-card__status--running { + background: rgba(80, 200, 120, 0.18); + color: #6ee7a0; +} + +.role-card__status--stopped { + background: rgba(200, 80, 80, 0.18); + color: #f08a8a; +} + +.empty-state { + text-align: center; + padding: 40px 20px; +} + +.empty-state__text { + font-family: "Noto Serif SC", serif; + font-size: 17px; + color: var(--text); + margin-bottom: 8px; +} + +.empty-state__hint { + font-size: 13px; + color: var(--text-muted); + line-height: 1.6; +} + +/* Role Detail (P3) */ +.detail-page { + display: flex; + flex-direction: column; + gap: 16px; +} + +.detail-hero { + width: 100%; + height: 200px; + border-radius: var(--radius); + background-size: cover; + background-position: center; + border: 1px solid rgba(255, 255, 255, 0.1); +} + +.detail-title { + font-family: "Noto Serif SC", serif; + font-size: 24px; + font-weight: 700; + color: #fff; +} + +.detail-desc { + font-size: 14px; + line-height: 1.7; + color: rgba(220, 225, 255, 0.85); +} + +.detail-price { + display: flex; + align-items: baseline; + gap: 4px; +} + +.detail-price__value { + font-size: 28px; + font-weight: 700; + color: #fff; +} + +.detail-price__unit { + font-size: 13px; + color: var(--text-muted); +} + +.detail-paid { + display: flex; + flex-direction: column; + align-items: center; + gap: 14px; + padding: 20px; + border-radius: var(--radius); + background: rgba(8, 9, 22, 0.55); + border: 1px solid rgba(255, 255, 255, 0.08); +} + +.detail-qr { + width: 100%; + display: flex; + justify-content: center; +} + +.qr-placeholder { + width: 160px; + height: 160px; + border-radius: var(--radius-sm); + background: rgba(255, 255, 255, 0.08); + border: 2px dashed rgba(255, 255, 255, 0.2); + display: grid; + place-items: center; + text-align: center; + font-size: 14px; + color: var(--text-muted); + line-height: 1.6; +} + +.detail-paid__hint { + font-size: 12px; + color: var(--text-muted); + text-align: center; + line-height: 1.6; +} + +.detail-avatar { + width: 80px; + height: 80px; + border-radius: 50%; + background-size: cover; + background-position: center; + border: 2px solid rgba(255, 255, 255, 0.2); +} + +/* About Page (P5) */ +.about-page { + display: flex; + flex-direction: column; + gap: 18px; +} + +.about-section { + padding: 18px; + border-radius: var(--radius); + background: rgba(8, 9, 22, 0.55); + border: 1px solid rgba(255, 255, 255, 0.08); + backdrop-filter: blur(14px); + -webkit-backdrop-filter: blur(14px); +} + +.about-section__title { + font-family: "Noto Serif SC", serif; + font-size: 16px; + font-weight: 600; + margin-bottom: 10px; + color: #fff; +} + +.about-section__text { + font-size: 13px; + line-height: 1.75; + color: rgba(220, 225, 255, 0.85); +} + +.faq-item { + border-top: 1px solid rgba(255, 255, 255, 0.06); +} + +.faq-item:first-child { + border-top: none; +} + +.faq-q { + width: 100%; + padding: 12px 0; + background: none; + border: none; + color: var(--text); + font-size: 13px; + font-family: inherit; + text-align: left; + cursor: pointer; + display: flex; + justify-content: space-between; + align-items: center; + gap: 8px; +} + +.faq-icon { + font-size: 18px; + color: var(--accent); + flex-shrink: 0; +} + +.faq-a { + display: none; + padding-bottom: 12px; +} + +.faq-a p { + font-size: 12px; + line-height: 1.7; + color: var(--text-muted); +} + +/* Onboarding Page (P6) */ +.onboarding-page { + display: flex; + flex-direction: column; + gap: 18px; +} + +.onboarding-section { + padding: 18px; + border-radius: var(--radius); + background: rgba(8, 9, 22, 0.55); + border: 1px solid rgba(255, 255, 255, 0.08); + backdrop-filter: blur(14px); + -webkit-backdrop-filter: blur(14px); +} + +.onboarding-section__title { + font-family: "Noto Serif SC", serif; + font-size: 16px; + font-weight: 600; + margin-bottom: 10px; + color: #fff; +} + +.onboarding-section__text { + font-size: 13px; + line-height: 1.75; + color: rgba(220, 225, 255, 0.85); +} + +.onboarding-list { + padding-left: 18px; + margin: 0; + font-size: 13px; + line-height: 1.75; + color: rgba(220, 225, 255, 0.85); +} + +.onboarding-list li { + margin-bottom: 6px; +} + +.onboarding-contact { + text-align: center; +} + +.onboarding-qr { + width: 140px; + height: 140px; + margin: 0 auto 12px; + border-radius: var(--radius-sm); + background: rgba(255, 255, 255, 0.08); + border: 2px dashed rgba(255, 255, 255, 0.2); + display: grid; + place-items: center; +} + +.onboarding-qr::after { + content: "二维码"; + font-size: 12px; + color: var(--text-muted); +} + +.onboarding-wechat { + font-size: 14px; + color: var(--text); + margin-bottom: 4px; +} + +.onboarding-wechat-id { + font-size: 13px; + color: var(--accent-soft); + margin-bottom: 8px; +} + +.onboarding-note { + font-size: 12px; + color: var(--text-muted); + line-height: 1.6; +} + +/* Creator Center (P7) */ +.center-tabs { + display: flex; + gap: 8px; + margin-bottom: 20px; + padding: 5px; + border-radius: var(--radius); + background: rgba(8, 9, 22, 0.55); + border: 1px solid rgba(255, 255, 255, 0.08); +} + +.center-tab { + flex: 1; + padding: 10px; + border-radius: var(--radius-sm); + border: none; + background: transparent; + color: var(--text-muted); + font-size: 14px; + font-weight: 500; + cursor: pointer; + transition: color var(--transition), background var(--transition); +} + +.center-tab.active { + background: rgba(255, 255, 255, 0.1); + color: var(--text); +} + +.center-panel { + display: none; + flex-direction: column; + flex: 1; + gap: 16px; +} + +.center-panel.active { + display: flex; +} + +.income-balance { + display: flex; + flex-direction: column; + align-items: center; + gap: 6px; + padding: 24px; + border-radius: var(--radius); + background: rgba(8, 9, 22, 0.55); + border: 1px solid rgba(255, 255, 255, 0.08); +} + +.income-balance__label { + font-size: 13px; + color: var(--text-muted); +} + +.income-balance__value { + font-size: 36px; + font-weight: 700; + color: #fff; +} + +.income-section { + padding: 18px; + border-radius: var(--radius); + background: rgba(8, 9, 22, 0.55); + border: 1px solid rgba(255, 255, 255, 0.08); +} + +.income-section__title { + font-family: "Noto Serif SC", serif; + font-size: 15px; + font-weight: 600; + margin-bottom: 12px; + color: #fff; +} + +.income-list { + display: flex; + flex-direction: column; + gap: 10px; +} + +.income-record { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 0; + border-bottom: 1px solid rgba(255, 255, 255, 0.05); +} + +.income-record:last-child { + border-bottom: none; +} + +.income-record__info { + display: flex; + flex-direction: column; + gap: 2px; +} + +.income-record__role { + font-size: 13px; + color: var(--text); +} + +.income-record__time { + font-size: 11px; + color: var(--text-muted); +} + +.income-record__amount { + font-size: 14px; + font-weight: 600; + color: #6ee7a0; +} + +.income-empty { + font-size: 13px; + color: var(--text-muted); + text-align: center; + padding: 12px; +} + +.withdraw-form, +.settings-form { + display: flex; + flex-direction: column; + gap: 14px; +} + +/* Tab Bar (U8) */ +.tab-bar { + position: fixed; + bottom: 0; + left: 0; + right: 0; + display: flex; + background: rgba(6, 6, 14, 0.92); + border-top: 1px solid rgba(255, 255, 255, 0.08); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + z-index: 100; + padding-bottom: env(safe-area-inset-bottom, 0); +} + +.tab-bar__item { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + gap: 2px; + padding: 10px 0 8px; + background: none; + border: none; + color: var(--text-muted); + font-size: 11px; + font-family: inherit; + cursor: pointer; + transition: color var(--transition); +} + +.tab-bar__item.active { + color: var(--accent); +} + +.tab-bar__icon { + font-size: 20px; + line-height: 1; +} + +.tab-bar__label { + font-size: 10px; +} + +/* Add bottom padding for tab bar */ +.app { + padding-bottom: 72px; +} + /* Responsive */ @media (max-width: 420px) { .app {