(() => {
  'use strict';

  // --- API helper ---
  const API_BASE = '/api';
  const TOKEN_KEY = 'eternal_ai_token';

  // 安全：转义 HTML，防止存储型 XSS（用于 innerHTML 插入的用户可控数据）
  function escapeHtml(str) {
    return String(str).replace(/[&<>"']/g, (ch) => {
      const map = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' };
      return map[ch];
    });
  }

  function getToken() {
    return localStorage.getItem(TOKEN_KEY) || '';
  }

  function setToken(token) {
    if (token) localStorage.setItem(TOKEN_KEY, token);
    else localStorage.removeItem(TOKEN_KEY);
  }

  async function api(path, options = {}) {
    const token = getToken();
    const headers = { 'Content-Type': 'application/json', ...(options.headers || {}) };
    if (token) headers['Authorization'] = `Bearer ${token}`;
    try {
      const res = await fetch(`${API_BASE}${path}`, { ...options, headers });
      const data = await res.json();
      if (!res.ok) {
        throw new Error(data.error || `请求失败 (${res.status})`);
      }
      return data;
    } catch (err) {
      if (err.message === 'Failed to fetch') {
        throw new Error('无法连接服务器，请检查网络');
      }
      throw err;
    }
  }

  // --- U9: Unified state management ---
  const STORAGE_KEY = 'eternal_ai_state';

  const defaultState = {
    isLoggedIn: false,
    isCreator: false,
    account: null,
    userId: 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();

  // --- 管理员认证状态管理 ---
  const ADMIN_TOKEN_KEY = 'eternal_ai_admin_token';
  const ADMIN_STATE_KEY = 'eternal_ai_admin_state';

  function getAdminToken() {
    return localStorage.getItem(ADMIN_TOKEN_KEY) || '';
  }

  function setAdminToken(token) {
    if (token) localStorage.setItem(ADMIN_TOKEN_KEY, token);
    else localStorage.removeItem(ADMIN_TOKEN_KEY);
  }

  const defaultAdminState = { isLoggedIn: false, account: null };
  function loadAdminState() {
    try {
      return { ...defaultAdminState, ...JSON.parse(localStorage.getItem(ADMIN_STATE_KEY)) };
    } catch { return { ...defaultAdminState }; }
  }
  function saveAdminState() { localStorage.setItem(ADMIN_STATE_KEY, JSON.stringify(adminState)); }
  const adminState = loadAdminState();

  // 管理员 API 封装（使用管理员 token）
  async function adminApi(path, options = {}) {
    const token = getAdminToken();
    const headers = { 'Content-Type': 'application/json', ...(options.headers || {}) };
    if (token) headers['Authorization'] = `Bearer ${token}`;
    try {
      const res = await fetch(`${API_BASE}${path}`, { ...options, headers });
      const data = await res.json();
      if (!res.ok) throw new Error(data.error || `请求失败 (${res.status})`);
      return data;
    } catch (err) {
      if (err.message === 'Failed to fetch') throw new Error('无法连接服务器，请检查网络');
      throw err;
    }
  }

  // --- 管理员审核列表 ---
  let adminCurrentFilter = 'pending_review';

  async function renderAdminReviews() {
    const listEl = document.getElementById('admin-review-list');
    if (!listEl) return;
    listEl.innerHTML = '<p style="text-align:center;color:var(--text-muted)">加载中…</p>';
    try {
      const query = adminCurrentFilter ? `?status=${adminCurrentFilter}` : '';
      const { roles } = await adminApi(`/admin/reviews${query}`);
      if (roles.length === 0) {
        listEl.innerHTML = '<p style="text-align:center;color:var(--text-muted)">暂无角色</p>';
        return;
      }
      listEl.innerHTML = roles.map((role) => {
        const id = escapeHtml(role.id);
        const name = escapeHtml(role.displayName);
        const avatar = escapeHtml(role.avatar || '');
        const status = escapeHtml(role.reviewStatus);
        const statusText = getReviewStatusText(role.reviewStatus);
        const statusClass = getReviewStatusClass(role.reviewStatus);
        const created = new Date(role.createdAt).toLocaleString('zh-CN');
        return `
      <article class="role-card admin-review-card" data-role-id="${id}" role="button" tabindex="0">
        <img class="role-card__avatar" src="${avatar}" alt="${name}" />
        <div class="role-card__info">
          <h3 class="role-card__name">${name}</h3>
          <span class="role-card__status role-card__status--${statusClass}">${statusText}</span>
          <span style="font-size:0.75rem;color:var(--text-muted);display:block;margin-top:0.25rem;">${created}</span>
        </div>
      </article>`;
      }).join('');
    } catch (err) {
      listEl.innerHTML = `<p style="text-align:center;color:var(--text-muted)">加载失败：${escapeHtml(err.message)}</p>`;
    }
  }

  function getReviewStatusText(status) {
    const map = {
      pending_review: '待审核', approved: '已通过', rejected: '已驳回',
      syncing: '同步中', synced: '已同步', failed: '同步失败',
    };
    return map[status] || status;
  }

  function getReviewStatusClass(status) {
    const map = {
      pending_review: 'pending', approved: 'approved', rejected: 'rejected',
      syncing: 'syncing', synced: 'synced', failed: 'failed',
    };
    return map[status] || 'pending';
  }

  // --- 管理员审核详情 ---
  let adminCurrentRoleId = null;

  async function renderAdminReviewDetail(roleId) {
    adminCurrentRoleId = roleId;
    const contentEl = document.getElementById('admin-detail-content');
    if (!contentEl) return;
    contentEl.innerHTML = '<p style="text-align:center;color:var(--text-muted)">加载中…</p>';
    try {
      const { role } = await adminApi(`/admin/reviews/${roleId}`);
      const status = role.reviewStatus;
      let actionsHtml = '';
      if (status === 'pending_review') {
        actionsHtml = `
        <div style="display:flex;gap:0.5rem;margin-top:1rem;">
          <button class="btn btn--primary" type="button" data-action="admin-approve" data-role-id="${escapeHtml(roleId)}">通过审核</button>
          <button class="btn btn--outline" type="button" data-action="admin-reject" data-role-id="${escapeHtml(roleId)}">驳回</button>
        </div>`;
      } else if (status === 'approved') {
        actionsHtml = `<h3 style="margin-top:1.5rem;">发起 Hermes 同步</h3>
        <form id="admin-sync-form" style="margin-top:1rem;">
          <div class="field"><label class="field__label">Profile 名称</label><input class="field__input" name="profileName" type="text" placeholder="如 star-spirit" required /></div>
          <div class="field"><label class="field__label">主 Model Key</label><input class="field__input" name="modelKey" type="text" placeholder="sk-xxx" required /></div>
          <div class="field"><label class="field__label">主服务商</label><select class="field__input" name="provider"><option value="openrouter">OpenRouter</option><option value="siliconflow">SiliconFlow</option></select></div>
          <div class="field"><label class="field__label">多媒体 Model Key</label><input class="field__input" name="multimediaModelKey" type="text" placeholder="sk-xxx" /></div>
          <div class="field"><label class="field__label">多媒体服务商</label><select class="field__input" name="multimediaProvider"><option value="openrouter">OpenRouter</option><option value="siliconflow">SiliconFlow</option></select></div>
          <div class="field"><label class="field__label"><input type="checkbox" name="enableSchedule" /> 启用定时任务</label></div>
          <div class="field"><label class="field__label">Webhook URL（可选，留空用全局配置）</label><input class="field__input" name="webhookUrl" type="url" placeholder="https://hermes.example.com/api/sync" /></div>
          <button class="btn btn--primary" type="submit" style="width:100%;">发起同步</button>
        </form>`;
      } else if (status === 'synced') {
        actionsHtml = `
        <div style="margin-top:1.5rem;text-align:center;">
          <h3>同步成功</h3>
          ${role.qrCodeUrl ? `<img src="${escapeHtml(role.qrCodeUrl)}" alt="二维码" style="max-width:200px;margin:1rem auto;display:block;" />` : ''}
          <p>Profile ID: ${escapeHtml(role.id)}</p>
          <p>同步时间: ${role.syncedAt ? new Date(role.syncedAt).toLocaleString('zh-CN') : '未知'}</p>
        </div>`;
      } else if (status === 'rejected') {
        actionsHtml = `<div style="margin-top:1.5rem;padding:1rem;background:#fef2f2;border-radius:8px;"><strong>驳回原因：</strong>${escapeHtml(role.reviewNote || '未提供')}</div>`;
      } else if (status === 'failed') {
        actionsHtml = `<div style="margin-top:1.5rem;padding:1rem;background:#fff7ed;border-radius:8px;"><strong>同步失败</strong><p>角色状态已标记为失败，可重新发起同步。</p></div>`;
      }

      contentEl.innerHTML = `
      <div style="display:flex;gap:1rem;align-items:center;margin-bottom:1.5rem;">
        <img src="${escapeHtml(role.avatar || '')}" alt="${escapeHtml(role.displayName)}" style="width:64px;height:64px;border-radius:50%;object-fit:cover;" />
        <div>
          <h2 style="margin:0;">${escapeHtml(role.displayName)}</h2>
          <span class="role-card__status role-card__status--${getReviewStatusClass(status)}">${getReviewStatusText(status)}</span>
        </div>
      </div>
      <div style="margin-bottom:1rem;">
        <p><strong>描述：</strong>${escapeHtml(role.desc || role.personality || '无')}</p>
        <p><strong>性格：</strong>${escapeHtml(role.personality || '无')}</p>
        <p><strong>背景：</strong>${escapeHtml(role.background || '无')}</p>
        <p><strong>问候语：</strong>${escapeHtml(role.greeting || '无')}</p>
        <p><strong>价格：</strong>¥${escapeHtml(String(role.price || 0))}</p>
      </div>
      ${actionsHtml}
    `;

      // 绑定同步表单提交
      const syncForm = document.getElementById('admin-sync-form');
      if (syncForm) {
        syncForm.addEventListener('submit', async (e) => {
          e.preventDefault();
          const formData = new FormData(syncForm);
          const data = Object.fromEntries(formData.entries());
          data.enableSchedule = !!formData.get('enableSchedule');
          if (!data.webhookUrl) delete data.webhookUrl;
          const submitBtn = syncForm.querySelector('button[type="submit"]');
          submitBtn.disabled = true;
          submitBtn.textContent = '同步中…';
          try {
            const result = await adminApi(`/admin/sync/${roleId}`, {
              method: 'POST',
              body: JSON.stringify(data),
            });
            alert('同步成功！Profile ID: ' + result.profileId);
            renderAdminReviewDetail(roleId);
          } catch (err) {
            alert('同步失败：' + err.message);
            submitBtn.disabled = false;
            submitBtn.textContent = '发起同步';
          }
        });
      }
    } catch (err) {
      contentEl.innerHTML = `<p style="text-align:center;color:var(--text-muted)">加载失败：${escapeHtml(err.message)}</p>`;
    }
  }

  // --- 管理员同步状态列表 ---
  async function renderAdminSyncStatus() {
    const listEl = document.getElementById('admin-sync-list');
    if (!listEl) return;
    listEl.innerHTML = '<p style="text-align:center;color:var(--text-muted)">加载中…</p>';
    try {
      const { roles } = await adminApi('/admin/sync-status');
      if (roles.length === 0) {
        listEl.innerHTML = '<p style="text-align:center;color:var(--text-muted)">暂无同步记录</p>';
        return;
      }
      listEl.innerHTML = roles.map((role) => {
        const id = escapeHtml(role.id);
        const name = escapeHtml(role.displayName);
        const status = escapeHtml(role.reviewStatus);
        const statusText = getReviewStatusText(role.reviewStatus);
        const statusClass = getReviewStatusClass(role.reviewStatus);
        const syncedAt = role.syncedAt ? new Date(role.syncedAt).toLocaleString('zh-CN') : '未同步';
        return `
      <article class="role-card" data-role-id="${id}" role="button" tabindex="0">
        <div class="role-card__info">
          <h3 class="role-card__name">${name}</h3>
          <span class="role-card__status role-card__status--${statusClass}">${statusText}</span>
          <span style="font-size:0.75rem;color:var(--text-muted);display:block;margin-top:0.25rem;">同步时间：${syncedAt}</span>
        </div>
      </article>`;
      }).join('');
    } catch (err) {
      listEl.innerHTML = `<p style="text-align:center;color:var(--text-muted)">加载失败：${escapeHtml(err.message)}</p>`;
    }
  }

  // --- 管理员系统配置 ---
  async function renderAdminConfig() {
    const listEl = document.getElementById('admin-config-list');
    if (!listEl) return;
    listEl.innerHTML = '<p style="text-align:center;color:var(--text-muted)">加载中…</p>';
    try {
      const { configs } = await adminApi('/admin/config');
      listEl.innerHTML = configs.map((c) => {
        const key = escapeHtml(c.key);
        const value = escapeHtml(c.value);
        const updated = c.updatedAt ? new Date(c.updatedAt).toLocaleString('zh-CN') : '';
        const isProtected = key === 'SYNC_SECRET';
        return `
      <div class="field" style="margin-bottom:1rem;">
        <label class="field__label">${key}</label>
        <div style="display:flex;gap:0.5rem;">
          <input class="field__input" type="text" value="${value}" data-config-key="${key}" ${isProtected ? 'disabled' : ''} />
          ${isProtected ? '' : `<button class="btn btn--small btn--primary" type="button" data-action="admin-save-config" data-config-key="${key}">保存</button>`}
        </div>
        ${updated ? `<span style="font-size:0.75rem;color:var(--text-muted);">更新于 ${updated}</span>` : ''}
      </div>`;
      }).join('');
    } catch (err) {
      listEl.innerHTML = `<p style="text-align:center;color:var(--text-muted)">加载失败：${escapeHtml(err.message)}</p>`;
    }
  }

  // --- 管理员 Tab 切换 ---
  function switchAdminTab(tab) {
    document.querySelectorAll('.center-tab').forEach((t) => {
      if (t.closest('#admin-reviews')) {
        const isActive = t.dataset.centerTab === tab;
        t.classList.toggle('active', isActive);
      }
    });
    document.getElementById('admin-reviews-panel').hidden = tab !== 'admin-reviews';
    document.getElementById('admin-sync-panel').hidden = tab !== 'admin-sync';
    document.getElementById('admin-config-panel').hidden = tab !== 'admin-config';
    if (tab === 'admin-reviews') renderAdminReviews();
    else if (tab === 'admin-sync') renderAdminSyncStatus();
    else if (tab === 'admin-config') renderAdminConfig();
  }

  // --- 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'),
    'admin-login': document.getElementById('admin-login'),
    'admin-reviews': document.getElementById('admin-reviews'),
    'admin-review-detail': document.getElementById('admin-review-detail'),
  };

  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 steps = Array.from(document.querySelectorAll('.form-step'));
  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 editingRoleId = null;
  let viewHistory = ['landing'];

  // --- U9: Unified showView with history and tab-bar sync ---
  // Human-readable labels for screen-reader announcements
  const viewLabels = {
    landing: '首页',
    auth: '登录 / 注册',
    'role-library': '角色库',
    'role-detail': '角色详情',
    distill: '蒸馏前任',
    about: '关于 Eternal AI',
    onboarding: '创作者入驻',
    'creator-center': '创作者管理中心',
    creator: '角色编辑',
    'admin-login': '管理员登录',
    'admin-reviews': '管理员后台',
    'admin-review-detail': '角色审核详情',
  };

  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);

    // a11y: move focus to the new view so screen readers announce it
    const target = views[name];
    if (target) {
      target.setAttribute('tabindex', '-1');
      // Defer focus to after the scroll/layout settles
      setTimeout(() => target.focus({ preventScroll: true }), 50);
    }

    // a11y: announce the view change to screen readers via live region
    const announcer = document.getElementById('sr-announce');
    if (announcer) {
      announcer.textContent = viewLabels[name] ? `已进入${viewLabels[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) => {
      const isActive = item.dataset.tabAction === activeTab;
      item.classList.toggle('active', isActive);
      item.setAttribute('aria-selected', isActive ? 'true' : 'false');
    });
  }

  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) => {
      const isActive = t.dataset.tab === tab;
      t.classList.toggle('active', isActive);
      t.setAttribute('aria-selected', isActive ? 'true' : 'false');
    });
    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 applyUserData(user) {
    state.isLoggedIn = true;
    state.userId = user.id;
    state.account = user.account;
    state.isCreator = user.isCreator || false;
    state.creatorName = user.creatorName || '';
    state.libraryName = user.libraryName || '我的角色库';
    saveState();
    updateLandingCard();
  }

  function logout() {
    state.isLoggedIn = false;
    state.isCreator = false;
    state.account = null;
    state.userId = null;
    state.boundCreator = null;
    state.roles = [];
    state.income = { balance: 0, records: [] };
    setToken('');
    saveState();
    updateLandingCard();
    showView('landing');
  }

  // --- U2: Role Library ---
  async function renderRoleLibrary() {
    const listEl = document.getElementById('role-list');
    const emptyEl = document.getElementById('library-empty');
    const titleEl = document.getElementById('library-title');

    titleEl.textContent = state.libraryName || '我的角色库';
    listEl.innerHTML = '<p style="text-align:center;color:var(--text-muted)">加载中…</p>';
    emptyEl.hidden = true;

    try {
      const { roles } = await api('/roles');
      if (roles.length === 0) {
        listEl.innerHTML = '';
        emptyEl.hidden = false;
        return;
      }
      listEl.innerHTML = roles
        .map((role) => {
          const id = escapeHtml(role.id);
          const name = escapeHtml(role.displayName);
          const desc = escapeHtml(role.desc || '');
          const avatar = escapeHtml(role.avatar || 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=anime%20character%20portrait&image_size=square');
          const price = escapeHtml(role.price);
          return `
      <article class="role-card" data-role-id="${id}" role="button" tabindex="0" aria-label="${name}，${desc}，每月${price}元">
        <img class="role-card__avatar" src="${avatar}" alt="${name}" />
        <div class="role-card__info">
          <h3 class="role-card__name">${name}</h3>
          <p class="role-card__desc">${desc}</p>
          <span class="role-card__price">¥${price}</span>
        </div>
      </article>`;
        })
        .join('');
    } catch (err) {
      listEl.innerHTML = `<p style="text-align:center;color:var(--text-muted)">加载失败：${escapeHtml(err.message)}</p>`;
    }
  }

  // --- U3: Role Detail ---
  async function renderRoleDetail(roleId) {
    try {
      const { role } = await api(`/roles/${roleId}`);
      currentRole = role;

      document.getElementById('detail-name').textContent = role.displayName;
      document.getElementById('detail-hero').style.backgroundImage = `url(${role.avatar || ''})`;
      document.getElementById('detail-role-name').textContent = role.displayName;
      document.getElementById('detail-role-desc').textContent = role.desc || role.personality || '';
      document.getElementById('detail-price').innerHTML = `<span class="detail-price__value">¥${escapeHtml(role.price)}</span><span class="detail-price__unit">/ 月</span>`;

      document.getElementById('detail-actions-pre').hidden = false;
      document.getElementById('detail-paid').hidden = true;
    } catch (err) {
      alert('加载角色详情失败：' + err.message);
      goBack();
    }
  }

  async function payRole() {
    if (!currentRole) return;
    const btn = document.querySelector('[data-action="pay"]');
    if (btn) {
      btn.disabled = true;
      btn.textContent = '处理中…';
    }
    try {
      const result = await api('/orders', {
        method: 'POST',
        body: JSON.stringify({ roleId: currentRole.id, amount: currentRole.price }),
      });
      // 付款成功，展示二维码
      document.getElementById('detail-actions-pre').hidden = true;
      const paidEl = document.getElementById('detail-paid');
      paidEl.hidden = false;
      const qrEl = document.getElementById('detail-qr');
      if (result.role && result.role.qrCodeUrl) {
        qrEl.innerHTML = `<img src="${escapeHtml(result.role.qrCodeUrl)}" alt="角色二维码" style="max-width:200px;width:100%;border-radius:8px;" />`;
      } else {
        qrEl.innerHTML = '<div class="qr-placeholder">扫码连接<br/>AI 角色</div>';
      }
      document.getElementById('detail-avatar').style.backgroundImage = `url(${currentRole.avatar})`;
    } catch (err) {
      alert('付款失败：' + err.message);
      if (btn) {
        btn.disabled = false;
        btn.textContent = '立即订阅';
      }
    }
  }

  // --- 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 ? '+' : '−';
    // a11y: sync aria-expanded so screen readers know the toggle state
    button.setAttribute('aria-expanded', isOpen ? 'false' : 'true');
  }

  // a11y: wire up FAQ buttons with aria-expanded / aria-controls on load
  function initFaqA11y() {
    const faqButtons = document.querySelectorAll('.faq-q');
    faqButtons.forEach((btn, index) => {
      const item = btn.closest('.faq-item');
      const answer = item.querySelector('.faq-a');
      const answerId = `faq-a-${index + 1}`;
      if (answer) {
        answer.id = answerId;
        answer.setAttribute('role', 'region');
        answer.setAttribute('aria-labelledby', `faq-q-${index + 1}`);
      }
      btn.id = `faq-q-${index + 1}`;
      btn.setAttribute('aria-expanded', 'false');
      btn.setAttribute('aria-controls', answerId);
    });
  }

  // a11y: label each view section for screen-reader navigation
  function initViewA11y() {
    Object.entries(views).forEach(([key, el]) => {
      if (el && viewLabels[key]) {
        el.setAttribute('aria-label', viewLabels[key]);
      }
    });
  }

  // --- U7: Creator Center ---
  function renderCreatorCenter() {
    renderCreatorRoles();
    renderIncome();
    renderSettings();
  }

  async function renderCreatorRoles() {
    const listEl = document.getElementById('creator-role-list');
    listEl.innerHTML = '<p style="text-align:center;color:var(--text-muted)">加载中…</p>';
    try {
      const { roles } = await api('/roles/my/roles');
      state.roles = roles;
      saveState();
      if (roles.length === 0) {
        listEl.innerHTML = '<p style="text-align:center;color:var(--text-muted)">还没有创建角色，点击「新建角色」开始</p>';
        return;
      }
      listEl.innerHTML = roles.map((role) => {
        const id = escapeHtml(role.id);
        const name = escapeHtml(role.displayName);
        const avatar = escapeHtml(role.avatar || 'https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=anime%20character%20portrait&image_size=square');
        const reviewStatus = role.reviewStatus || 'pending_review';
        const statusText = getReviewStatusText(reviewStatus);
        const statusClass = getReviewStatusClass(reviewStatus);

        // 审核状态标签
        let statusBadge = `<span class="role-card__status role-card__status--${statusClass}">${statusText}</span>`;

        // 已同步角色额外显示运行状态
        if (reviewStatus === 'synced') {
          const runStatus = role.status === 'running' ? '运行中' : '已停止';
          statusBadge += ` <span class="role-card__status role-card__status--${escapeHtml(role.status)}" style="margin-left:0.25rem;">${runStatus}</span>`;
        }

        // 驳回原因
        let rejectNote = '';
        if (reviewStatus === 'rejected' && role.reviewNote) {
          rejectNote = `<p style="font-size:0.75rem;color:#dc2626;margin-top:0.25rem;">驳回原因：${escapeHtml(role.reviewNote)}</p>`;
        }

        // 二维码展示（已同步角色）
        let qrCodeHtml = '';
        if (reviewStatus === 'synced' && role.qrCodeUrl) {
          qrCodeHtml = `
          <div style="margin-top:0.5rem;">
            <img src="${escapeHtml(role.qrCodeUrl)}" alt="二维码" style="width:48px;height:48px;cursor:pointer;" data-action="show-qrcode" data-qr-url="${escapeHtml(role.qrCodeUrl)}" />
            <button class="btn btn--small btn--outline" type="button" data-action="copy-qrcode" data-qr-url="${escapeHtml(role.qrCodeUrl)}" style="margin-left:0.5rem;">复制链接</button>
          </div>`;
        }

        // 操作按钮
        let actionButtons = '';
        if (reviewStatus === 'rejected' || reviewStatus === 'pending_review') {
          actionButtons = `<button class="btn btn--small btn--outline" type="button" data-action="edit-role" data-role-id="${id}">编辑</button>`;
        } else if (reviewStatus === 'synced') {
          actionButtons = `<button class="btn btn--small btn--outline" type="button" data-action="edit-role" data-role-id="${id}">编辑</button> <button class="btn btn--small btn--outline" type="button" data-action="hermes-deploy" data-role-id="${id}" data-role-name="${name}">Hermes 部署</button>`;
        }

        return `
      <article class="role-card" data-role-id="${id}" role="button" tabindex="0" aria-label="${name}，${statusText}">
        <img class="role-card__avatar" src="${avatar}" alt="${name}" />
        <div class="role-card__info">
          <h3 class="role-card__name">${name}</h3>
          <div>${statusBadge}</div>
          ${rejectNote}
          ${qrCodeHtml}
        </div>
        <div style="display:flex;gap:0.5rem;">${actionButtons}</div>
      </article>`;
      }).join('');
    } catch (err) {
      listEl.innerHTML = `<p style="text-align:center;color:var(--text-muted)">加载失败：${escapeHtml(err.message)}</p>`;
    }
  }

  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 = '<p class="income-empty">暂无流水记录</p>';
      return;
    }
    listEl.innerHTML = income.records
      .map((r) => {
        const role = escapeHtml(r.role);
        const time = escapeHtml(r.time);
        const amount = r.amount.toFixed(2);
        return `
      <div class="income-record">
        <div class="income-record__info">
          <span class="income-record__role">${role}</span>
          <span class="income-record__time">${time}</span>
        </div>
        <span class="income-record__amount">+¥${amount}</span>
      </div>`;
      })
      .join('');
  }

  function renderSettings() {
    document.getElementById('settings-name').value = state.creatorName || '';
    document.getElementById('settings-library').value = state.libraryName === '我的 [XXX]' ? '' : state.libraryName;
    renderApiKeys();
  }

  // --- API Key 管理 ---
  async function renderApiKeys() {
    const listEl = document.getElementById('apikey-list');
    if (!listEl) return;
    listEl.innerHTML = '<p style="color:var(--text-muted);font-size:0.875rem;">加载中…</p>';
    try {
      const { apiKeys } = await api('/apikeys');
      if (apiKeys.length === 0) {
        listEl.innerHTML = '<p style="color:var(--text-muted);font-size:0.875rem;">还没有 API Key</p>';
        return;
      }
      listEl.innerHTML = apiKeys
        .map((k) => {
          const id = escapeHtml(k.id);
          const name = escapeHtml(k.name);
          const prefix = escapeHtml(k.keyPrefix);
          const created = new Date(k.createdAt).toLocaleDateString('zh-CN');
          const lastUsed = k.lastUsedAt ? new Date(k.lastUsedAt).toLocaleString('zh-CN') : '从未使用';
          return `
        <div class="apikey-item" style="padding:0.75rem 0;border-bottom:1px solid var(--border);">
          <div style="display:flex;justify-content:space-between;align-items:center;">
            <div>
              <strong>${name}</strong>
              <code style="margin-left:0.5rem;color:var(--text-muted);">${prefix}…</code>
            </div>
            <button class="btn btn--small btn--ghost" type="button" data-action="delete-apikey" data-apikey-id="${id}">删除</button>
          </div>
          <div style="font-size:0.75rem;color:var(--text-muted);margin-top:0.25rem;">
            创建于 ${created} · 最后使用：${lastUsed}
          </div>
        </div>`;
        })
        .join('');
    } catch (err) {
      listEl.innerHTML = `<p style="color:var(--text-muted);font-size:0.875rem;">加载失败：${escapeHtml(err.message)}</p>`;
    }
  }

  async function generateApiKey() {
    try {
      const { apiKey } = await api('/apikeys', {
        method: 'POST',
        body: JSON.stringify({ name: 'hermes-deploy' }),
      });
      // 明文 Key 只显示一次
      alert(`API Key 已生成（仅显示一次，请妥善保存）：\n\n${apiKey.key}\n\n复制此 Key，在 Hermes 机器上使用 curl 命令时需要带上。`);
      renderApiKeys();
    } catch (err) {
      alert(`生成失败：${err.message}`);
    }
  }

  async function deleteApiKey(id) {
    if (!confirm('确定删除此 API Key？删除后无法恢复。')) return;
    try {
      await api(`/apikeys/${id}`, { method: 'DELETE' });
      renderApiKeys();
    } catch (err) {
      alert(`删除失败：${err.message}`);
    }
  }

  // --- Hermes 部署指南 ---
  function showHermesDeployGuide(roleId, roleName) {
    // 获取当前服务器地址
    const baseUrl = window.location.origin;
    const profileName = `role-${roleName.toLowerCase().replace(/[^a-z0-9]/g, '-').slice(0, 20)}`;
    const soulUrl = `${baseUrl}/api/hermes/roles/${roleId}/SOUL.md`;
    const configUrl = `${baseUrl}/api/hermes/roles/${roleId}/config.yaml`;

    const soulCmd = `curl -H "Authorization: Bearer YOUR_API_KEY" \\\n     ${soulUrl} \\\n     -o ~/.hermes/profiles/${profileName}/SOUL.md`;
    const configCmd = `curl -H "Authorization: Bearer YOUR_API_KEY" \\\n     ${configUrl} \\\n     -o ~/.hermes/profiles/${profileName}/config.yaml`;

    // 移除已有弹窗
    const existing = document.getElementById('hermes-modal');
    if (existing) existing.remove();

    const modal = document.createElement('div');
    modal.id = 'hermes-modal';
    modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.5);display:flex;align-items:center;justify-content:center;z-index:9999;padding:1rem;';
    modal.innerHTML = `
      <div style="background:var(--bg-card,#fff);border-radius:12px;max-width:640px;width:100%;max-height:85vh;overflow-y:auto;padding:1.5rem;box-shadow:0 8px 32px rgba(0,0,0,0.2);">
        <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem;">
          <h3 style="margin:0;">Hermes Agent 部署指南</h3>
          <button class="btn btn--small btn--ghost" type="button" data-action="close-hermes-modal">✕</button>
        </div>

        <p style="color:var(--text-muted);font-size:0.875rem;margin-bottom:1rem;">
          角色「${escapeHtml(roleName)}」的配置文件可通过以下命令拉取到 Hermes Agent 所在的机器。
        </p>

        <div style="background:var(--bg-muted,#f5f5f5);border-radius:8px;padding:0.75rem;margin-bottom:1rem;">
          <strong style="font-size:0.875rem;">前提条件</strong>
          <ul style="margin:0.5rem 0 0 1.25rem;padding:0;font-size:0.8125rem;color:var(--text-muted);">
            <li>已安装 Hermes Agent（<code>curl -fsSL https://hermes-agent.nousresearch.com/install.sh | bash</code>）</li>
            <li>已在 EternalAI 设置页生成 API Key</li>
          </ul>
        </div>

        <h4 style="font-size:0.9375rem;margin-bottom:0.5rem;">1. 创建 Hermes Profile</h4>
        <pre style="background:var(--bg-muted,#1e1e1e);color:var(--text-light,#eee);border-radius:8px;padding:0.75rem;overflow-x:auto;font-size:0.8125rem;margin-bottom:1rem;"><code>hermes profile create ${escapeHtml(profileName)}</code></pre>

        <h4 style="font-size:0.9375rem;margin-bottom:0.5rem;">2. 拉取 SOUL.md</h4>
        <div style="position:relative;margin-bottom:1rem;">
          <pre style="background:var(--bg-muted,#1e1e1e);color:var(--text-light,#eee);border-radius:8px;padding:0.75rem;overflow-x:auto;font-size:0.8125rem;margin:0;"><code>${escapeHtml(soulCmd)}</code></pre>
          <button class="btn btn--small btn--outline" type="button" data-action="copy-hermes-cmd" data-cmd="${escapeHtml(soulCmd)}" style="position:absolute;top:0.5rem;right:0.5rem;">复制</button>
        </div>

        <h4 style="font-size:0.9375rem;margin-bottom:0.5rem;">3. 拉取 config.yaml</h4>
        <div style="position:relative;margin-bottom:1rem;">
          <pre style="background:var(--bg-muted,#1e1e1e);color:var(--text-light,#eee);border-radius:8px;padding:0.75rem;overflow-x:auto;font-size:0.8125rem;margin:0;"><code>${escapeHtml(configCmd)}</code></pre>
          <button class="btn btn--small btn--outline" type="button" data-action="copy-hermes-cmd" data-cmd="${escapeHtml(configCmd)}" style="position:absolute;top:0.5rem;right:0.5rem;">复制</button>
        </div>

        <h4 style="font-size:0.9375rem;margin-bottom:0.5rem;">4. 配置 API 密钥</h4>
        <pre style="background:var(--bg-muted,#1e1e1e);color:var(--text-light,#eee);border-radius:8px;padding:0.75rem;overflow-x:auto;font-size:0.8125rem;margin-bottom:1rem;"><code>echo "OPENROUTER_API_KEY=sk-or-your-key" > ~/.hermes/profiles/${escapeHtml(profileName)}/.env</code></pre>

        <h4 style="font-size:0.9375rem;margin-bottom:0.5rem;">5. 启动对话</h4>
        <pre style="background:var(--bg-muted,#1e1e1e);color:var(--text-light,#eee);border-radius:8px;padding:0.75rem;overflow-x:auto;font-size:0.8125rem;margin-bottom:1rem;"><code>${escapeHtml(profileName)} chat</code></pre>

        <div style="border-top:1px solid var(--border);padding-top:1rem;margin-top:0.5rem;">
          <p style="font-size:0.8125rem;color:var(--text-muted);margin:0;">
            提示：将命令中的 <code>YOUR_API_KEY</code> 替换为你在设置页生成的 API Key（以 <code>eak_</code> 开头）。
          </p>
        </div>
      </div>
    `;

    // 点击遮罩关闭
    modal.addEventListener('click', (e) => {
      if (e.target === modal) modal.remove();
    });

    document.body.appendChild(modal);
  }

  // --- 二维码弹窗 ---
  function showQrCodeModal(qrUrl) {
    const modal = document.createElement('div');
    modal.id = 'qr-modal';
    modal.style.cssText = 'position:fixed;inset:0;background:rgba(0,0,0,0.6);display:flex;align-items:center;justify-content:center;z-index:1000;';
    modal.innerHTML = `
    <div style="background:#fff;border-radius:16px;padding:2rem;text-align:center;max-width:90vw;">
      <h3 style="margin-bottom:1rem;">角色二维码</h3>
      <img src="${escapeHtml(qrUrl)}" alt="二维码" style="max-width:300px;width:100%;" />
      <div style="margin-top:1rem;">
        <button class="btn btn--outline" type="button" data-action="close-qr-modal">关闭</button>
      </div>
    </div>`;
    document.body.appendChild(modal);
  }

  function switchCenterTab(tab) {
    activeCenterTab = tab;
    document.querySelectorAll('.center-tab').forEach((t) => {
      const isActive = t.dataset.centerTab === tab;
      t.classList.toggle('active', isActive);
      t.setAttribute('aria-selected', isActive ? 'true' : 'false');
    });
    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);
    });
    dots.forEach((dot, i) => {
      dot.classList.toggle('active', i === index);
    });
    currentStep = index;
  }

  function validateStep(index) {
    const step = steps[index];
    const inputs = step.querySelectorAll('input, textarea, select');
    let valid = true;
    inputs.forEach((input) => {
      if (!input.checkValidity()) {
        valid = false;
        input.reportValidity();
      }
    });
    return valid;
  }

  function getFormData() {
    const fd = new FormData(form);
    const data = Object.fromEntries(fd.entries());
    data.enableMemory = form.elements.enableMemory.checked;
    data.enableTools = form.elements.enableTools.checked;
    return data;
  }

  function escapeYaml(value) {
    if (typeof value !== 'string') return value;
    if (value.includes(':') || value.includes('#') || value.includes('\n') || value.includes('"')) {
      const escaped = value.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
      return `"${escaped}"`;
    }
    return value;
  }

  function generateSoulMd(data) {
    const personalityTags = data.personality
      .split(/[,，]/)
      .map((t) => t.trim())
      .filter(Boolean)
      .join(' | ');

    return `# Soul of ${data.displayName}

> Generated by Eternal AI — Hermes agent character soul.

## Identity

- **Name**: ${data.displayName}
- **Gender**: ${data.gender === 'unknown' ? '未指定' : data.gender}
- **Age**: ${data.age || '未指定'}
- **Role in your life**: ${data.relationship || '未指定'}

## Background

${data.background}

## Personality

${personalityTags}

## Speech Style

${data.speechStyle}

## Likes

${data.likes || 'None specified.'}

## Dislikes

${data.dislikes || 'None specified.'}

## Shared Memories

${data.memories || 'None specified.'}

## Secrets & Inner Voice

${data.secrets || 'None specified.'}

## First Greeting

${data.greeting}

---

*Keep the memory alive.*
`;
  }

  function generateConfigYaml(data) {
    const system = (data.systemPrompt || buildSystemPrompt(data)).trim();
    const toolsBlock = data.enableTools
      ? `
tools:
  - name: search_memory
    description: Search long-term memory for context about the user.
  - name: save_memory
    description: Persist a new memory about the user or conversation.`
      : '';

    return `# Hermes Agent Config
# Generated by Eternal AI for character: ${data.displayName}

agent:
  id: ${data.agentId}
  name: ${escapeYaml(data.displayName)}
  version: "1.0.0"

model:
  name: ${escapeYaml(data.model)}
  temperature: ${data.temperature}
  max_tokens: ${data.maxTokens}

system_prompt: |
${system.split('\n').map((line) => `  ${line}`).join('\n')}

memory:
  enabled: ${data.enableMemory}
  storage: local
  recall_depth: 10${toolsBlock}

character:
  soul_file: Soul.md
  greeting: ${escapeYaml(data.greeting)}
`;
  }

  function buildSystemPrompt(data) {
    const tags = data.personality
      .split(/[,，]/)
      .map((t) => t.trim())
      .filter(Boolean)
      .join('、');

    return `你是「${data.displayName}」，一个鲜活的角色。

基本设定：
- 性别：${data.gender === 'unknown' ? '未指定' : data.gender}
- 年龄：${data.age || '未指定'}
- 你与用户的关系：${data.relationship || '亲密关系'}

性格：${tags}

背景：
${data.background}

说话风格：
${data.speechStyle}

喜好：
${data.likes || '无特别说明'}

厌恶/底线：
${data.dislikes || '无特别说明'}

共同记忆：
${data.memories || '无特别说明'}

内心秘密（只在语气与选择中流露，不直接说出）：
${data.secrets || '无'}

请始终保持角色一致性，用符合设定的语气与用户交流。你的目标不是完美回答问题，而是像一个真实、有记忆、有情绪的人一样陪伴对方。`;
  }

  function updateSystemPromptPreview() {
    const data = getFormData();
    if (!systemPromptInput.value.trim()) {
      systemPromptInput.dataset.auto = buildSystemPrompt(data);
    } else {
      delete systemPromptInput.dataset.auto;
    }
  }

  async function publish() {
    if (!validateStep(currentStep)) return;

    const data = getFormData();
    if (!data.systemPrompt.trim()) {
      data.systemPrompt = buildSystemPrompt(data);
    }

    generatedSoul = generateSoulMd(data);
    generatedConfig = generateConfigYaml(data);

    // 持久化到数据库
    try {
      const payload = {
        ...data,
        soulMd: generatedSoul,
        configYaml: generatedConfig,
        desc: data.personality.split(/[,，]/).slice(0, 2).join('，'),
        price: parseFloat(data.price) || 29.9,
        avatar: data.avatar || `https://trae-api-cn.mchost.guru/api/ide/v1/text_to_image?prompt=anime%20${encodeURIComponent(data.displayName)}%20portrait&image_size=square`,
        temperature: parseFloat(data.temperature) || 0.8,
        maxTokens: parseInt(data.maxTokens) || 2048,
      };

      if (editingRoleId) {
        await api(`/roles/${editingRoleId}`, {
          method: 'PUT',
          body: JSON.stringify(payload),
        });
      } else {
        await api('/roles', {
          method: 'POST',
          body: JSON.stringify(payload),
        });
      }

      form.hidden = true;
      resultPanel.hidden = false;
      renderPreview();
    } catch (err) {
      alert('保存失败：' + err.message);
    }
  }

  function renderPreview() {
    previewCode.textContent = activePreview === 'soul' ? generatedSoul : generatedConfig;
  }

  function download(filename, content) {
    const blob = new Blob([content], { type: 'text/plain;charset=utf-8' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = filename;
    document.body.appendChild(a);
    a.click();
    document.body.removeChild(a);
    URL.revokeObjectURL(url);
  }

  function resetCreator() {
    form.reset();
    form.hidden = false;
    resultPanel.hidden = true;
    generatedSoul = '';
    generatedConfig = '';
    activePreview = 'soul';
    editingRoleId = null;
    updateStep(0);
    updateTabs();
    updateSystemPromptPreview();
  }

  // 加载已有角色数据到表单（编辑模式）
  async function loadRoleForEdit(roleId) {
    try {
      const { role } = await api(`/roles/${roleId}/full`);
      editingRoleId = roleId;
      form.hidden = false;
      resultPanel.hidden = true;

      // 填充表单字段
      const fields = {
        displayName: role.displayName,
        gender: role.gender,
        age: role.age || '',
        relationship: role.relationship || '',
        personality: role.personality,
        background: role.background,
        speechStyle: role.speechStyle,
        likes: role.likes || '',
        dislikes: role.dislikes || '',
        memories: role.memories || '',
        secrets: role.secrets || '',
        greeting: role.greeting,
        systemPrompt: role.systemPrompt || '',
        model: role.model,
        temperature: String(role.temperature),
        maxTokens: String(role.maxTokens),
        price: String(role.price),
      };
      Object.entries(fields).forEach(([name, value]) => {
        const el = form.elements[name];
        if (el) el.value = value;
      });
      if (form.elements.enableMemory) form.elements.enableMemory.checked = role.enableMemory;
      if (form.elements.enableTools) form.elements.enableTools.checked = role.enableTools;

      updateStep(0);
      updateTabs();
      updateSystemPromptPreview();
    } catch (err) {
      alert('加载角色数据失败：' + err.message);
    }
  }

  function updateTabs() {
    document.querySelectorAll('.preview-tab').forEach((tab) => {
      const isActive = tab.dataset.tab === activePreview;
      tab.classList.toggle('active', isActive);
      tab.setAttribute('aria-selected', isActive ? 'true' : 'false');
    });
  }

  // --- Event delegation ---
  // a11y: keyboard support for role cards (Enter / Space activates them)
  document.addEventListener('keydown', (e) => {
    if (e.key !== 'Enter' && e.key !== ' ') return;
    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');
    }
  });

  document.addEventListener('click', async (e) => {
    // FAQ toggle (U4)
    const faqBtn = e.target.closest('.faq-q');
    if (faqBtn) {
      e.preventDefault();
      toggleFaq(faqBtn);
      return;
    }

    // 管理员审核列表卡片点击（需在通用 role-card 之前处理）
    const adminReviewCard = e.target.closest('.admin-review-card');
    if (adminReviewCard && !e.target.closest('[data-action]')) {
      e.preventDefault();
      const roleId = adminReviewCard.dataset.roleId;
      renderAdminReviewDetail(roleId);
      showView('admin-review-detail');
      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;
    }

    // 管理员筛选 Tab
    const adminFilter = e.target.closest('.admin-filter');
    if (adminFilter) {
      e.preventDefault();
      document.querySelectorAll('.admin-filter').forEach((f) => f.classList.remove('active'));
      adminFilter.classList.add('active');
      adminCurrentFilter = adminFilter.dataset.filter;
      renderAdminReviews();
      return;
    }

    const target = e.target.closest('[data-action], [data-tab], [data-download], [data-center-tab]');
    if (!target) return;

    const action = target.dataset.action;

    // 管理员 Tab 切换（需在通用 center tab 之前处理）
    const adminCenterTab = target.dataset.centerTab;
    if (adminCenterTab && adminCenterTab.startsWith('admin-')) {
      e.preventDefault();
      switchAdminTab(adminCenterTab);
      return;
    }

    // Center tab switching (U7)
    if (target.dataset.centerTab) {
      e.preventDefault();
      switchCenterTab(target.dataset.centerTab);
      return;
    }

    // Landing card: open characters (U1)
    if (action === 'open-characters') {
      e.preventDefault();
      if (!state.isLoggedIn) {
        switchAuthTab('login');
        showView('auth');
      } else if (state.isCreator) {
        showView('creator-center');
        renderCreatorCenter();
      } else {
        renderRoleLibrary();
        showView('role-library');
      }
      return;
    }

    if (action === 'open-distill') {
      e.preventDefault();
      showView('distill');
      return;
    }

    if (action === 'open-about') {
      e.preventDefault();
      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();
      const roleId = target.dataset.roleId;
      await loadRoleForEdit(roleId);
      showView('creator');
      return;
    }

    if (action === 'hermes-deploy') {
      e.preventDefault();
      const roleId = target.dataset.roleId;
      const roleName = target.dataset.roleName || 'role';
      showHermesDeployGuide(roleId, roleName);
      return;
    }

    if (action === 'generate-apikey') {
      e.preventDefault();
      await generateApiKey();
      return;
    }

    if (action === 'delete-apikey') {
      e.preventDefault();
      const keyId = target.dataset.apikeyId;
      await deleteApiKey(keyId);
      return;
    }

    if (action === 'close-hermes-modal') {
      e.preventDefault();
      const modal = document.getElementById('hermes-modal');
      if (modal) modal.remove();
      return;
    }

    if (action === 'copy-hermes-cmd') {
      e.preventDefault();
      const cmd = target.dataset.cmd || '';
      try {
        await navigator.clipboard.writeText(cmd);
        target.textContent = '已复制';
        setTimeout(() => { target.textContent = '复制'; }, 2000);
      } catch {
        alert('复制失败，请手动选择文本复制');
      }
      return;
    }

    // --- 管理员操作 ---
    if (action === 'admin-logout') {
      e.preventDefault();
      setAdminToken('');
      adminState.isLoggedIn = false;
      saveAdminState();
      window.location.hash = '';
      showView('landing');
      return;
    }

    if (action === 'back-to-admin') {
      e.preventDefault();
      showView('admin-reviews');
      renderAdminReviews();
      return;
    }

    // 管理员审核操作
    if (action === 'admin-approve') {
      e.preventDefault();
      const roleId = target.dataset.roleId;
      try {
        await adminApi(`/admin/reviews/${roleId}/approve`, { method: 'POST' });
        alert('审核通过');
        renderAdminReviewDetail(roleId);
      } catch (err) {
        alert('操作失败：' + err.message);
      }
      return;
    }

    if (action === 'admin-reject') {
      e.preventDefault();
      const roleId = target.dataset.roleId;
      const reason = prompt('请输入驳回原因：');
      if (!reason) return;
      try {
        await adminApi(`/admin/reviews/${roleId}/reject`, {
          method: 'POST',
          body: JSON.stringify({ reviewNote: reason }),
        });
        alert('已驳回');
        renderAdminReviewDetail(roleId);
      } catch (err) {
        alert('操作失败：' + err.message);
      }
      return;
    }

    // 管理员保存配置
    if (action === 'admin-save-config') {
      e.preventDefault();
      const key = target.dataset.configKey;
      const input = document.querySelector(`input[data-config-key="${key}"]`);
      if (!input) return;
      try {
        await adminApi(`/admin/config/${key}`, {
          method: 'PUT',
          body: JSON.stringify({ value: input.value }),
        });
        alert('配置已保存');
        renderAdminConfig();
      } catch (err) {
        alert('保存失败：' + err.message);
      }
      return;
    }

    // --- 二维码操作 ---
    if (action === 'show-qrcode') {
      e.preventDefault();
      const qrUrl = target.dataset.qrUrl;
      showQrCodeModal(qrUrl);
      return;
    }

    if (action === 'copy-qrcode') {
      e.preventDefault();
      const qrUrl = target.dataset.qrUrl;
      try {
        await navigator.clipboard.writeText(qrUrl);
        alert('二维码链接已复制');
      } catch {
        alert('复制失败：' + qrUrl);
      }
      return;
    }

    if (action === 'close-qr-modal') {
      e.preventDefault();
      const modal = document.getElementById('qr-modal');
      if (modal) modal.remove();
      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) {
        updateStep(currentStep + 1);
      }
      return;
    }

    if (action === 'prev') {
      e.preventDefault();
      if (currentStep > 0) {
        updateStep(currentStep - 1);
      }
      return;
    }

    if (action === 'publish') {
      e.preventDefault();
      publish();
      return;
    }

    if (action === 'reset') {
      e.preventDefault();
      resetCreator();
      return;
    }

    // Tab switching (auth tabs and preview tabs)
    if (target.dataset.tab) {
      e.preventDefault();
      if (target.closest('.auth-tabs')) {
        switchAuthTab(target.dataset.tab);
      } else {
        activePreview = target.dataset.tab;
        updateTabs();
        renderPreview();
      }
      return;
    }

    // Download buttons
    if (target.dataset.download) {
      e.preventDefault();
      if (target.dataset.download === 'soul') {
        download('Soul.md', generatedSoul);
      } else {
        download('config.yaml', generatedConfig);
      }
      return;
    }
  });

  // --- Auth form submissions ---
  document.querySelectorAll('.auth-form').forEach((authForm) => {
    authForm.addEventListener('submit', async (e) => {
      e.preventDefault();
      if (!validatePasswordMatch(authForm)) return;
      const formData = new FormData(authForm);
      const data = Object.fromEntries(formData.entries());
      const isRegister = authForm.dataset.form === 'register';

      try {
        const endpoint = isRegister ? '/auth/register' : '/auth/login';
        const result = await api(endpoint, {
          method: 'POST',
          body: JSON.stringify({ account: data.account, password: data.password }),
        });

        setToken(result.token);
        applyUserData(result.user);

        // 注册成功后自动成为创作者
        if (isRegister) {
          await api('/auth/settings', {
            method: 'PUT',
            body: JSON.stringify({ isCreator: true, creatorName: data.account }),
          });
          state.isCreator = true;
          state.creatorName = data.account;
          saveState();
          updateLandingCard();
        }

        // 登录后跳转
        if (state.isCreator) {
          showView('creator-center');
          renderCreatorCenter();
        } else {
          renderRoleLibrary();
          showView('role-library');
        }
      } catch (err) {
        alert(err.message);
      }
    });
  });

  // --- 管理员登录表单 ---
  const adminLoginForm = document.getElementById('admin-login-form');
  if (adminLoginForm) {
    adminLoginForm.addEventListener('submit', async (e) => {
      e.preventDefault();
      const formData = new FormData(e.target);
      const data = Object.fromEntries(formData.entries());
      try {
        const result = await adminApi('/admin-auth/login', {
          method: 'POST',
          body: JSON.stringify({ account: data.account, password: data.password }),
        });
        setAdminToken(result.token);
        adminState.isLoggedIn = true;
        adminState.account = result.admin.account;
        saveAdminState();
        showView('admin-reviews');
        renderAdminReviews();
      } catch (err) {
        alert('登录失败：' + err.message);
      }
    });
  }

  // --- Settings form (U7) ---
  document.getElementById('settings-form').addEventListener('submit', async (e) => {
    e.preventDefault();
    const formData = new FormData(e.target);
    const data = Object.fromEntries(formData.entries());
    try {
      const { user } = await api('/auth/settings', {
        method: 'PUT',
        body: JSON.stringify({
          creatorName: data.creatorName,
          libraryName: data.libraryName,
        }),
      });
      state.creatorName = user.creatorName || '';
      state.libraryName = user.libraryName || '我的 [XXX]';
      saveState();
      updateLandingCard();
      alert('设置已保存');
    } catch (err) {
      alert('保存失败：' + err.message);
    }
  });

  // --- 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();
  });

  // --- Initialize ---
  updateStep(0);
  updateSystemPromptPreview();
  updateLandingCard();
  updateTabBar('landing');
  initFaqA11y();
  initViewA11y();

  // 页面加载时验证 token，恢复登录态
  (async () => {
    const token = getToken();
    if (!token) return;
    try {
      const { user } = await api('/auth/me');
      applyUserData(user);
    } catch {
      // token 过期，清除
      setToken('');
    }
  })();

  // --- 管理员后台路由 ---
  async function handleAdminRoute() {
    if (window.location.hash !== '#admin') return;
    const token = getAdminToken();
    if (!token) {
      showView('admin-login');
      return;
    }
    // 验证 token 有效性
    try {
      await adminApi('/admin-auth/me');
      showView('admin-reviews');
      renderAdminReviews();
    } catch {
      setAdminToken('');
      adminState.isLoggedIn = false;
      saveAdminState();
      showView('admin-login');
    }
  }

  window.addEventListener('hashchange', handleAdminRoute);
  // 页面加载时检查 hash
  handleAdminRoute();
})();
