// Erythros Lite Wiki Quick Reference App
// Main React application with bilingual support
const { useState, useMemo, useEffect, useCallback, useContext, createContext, useRef } = React;

// ===== LANGUAGE CONTEXT =====
const LangContext = createContext('zh');
function useLang() {
  const ctx = useContext(LangContext);
  return ctx;
}
function t(key) {
  const lang = useLang();
  const str = window.UI_STRINGS?.[lang];
  return str?.[key] ?? key;
}

// ===== 自动补全 UI_STRINGS（无需修改 data.js） =====
(function ensureUIStrings() {
  if (!window.UI_STRINGS) window.UI_STRINGS = {};
  const defaultStrings = {
    zh: {
      appTitle: 'Erythros Lite 维基速查',
      appSubtitle: 'Wiki Quick Ref',
      searchPlaceholder: '搜索物品名称、ID 或合成配方...',
      totalItems: 'Total Items',
      categories: 'Categories',
      itemCategories: '物品分类',
      gameData: '游戏数据',
      mapSizes: '地图尺寸',
      medicineEffects: '药品效果',
      craftingRecipes: '合成配方',
      buildingDurability: '建筑耐久',
      searchResults: '搜索结果',
      searchResultsFor: 'Search Results for',
      found: 'FOUND',
      itemsItems: 'ITEMS',
      idRange: 'ID RANGE',
      noResults: '未找到匹配物品',
      noResultsDesc: '试试其他关键词，或切换到其他分类',
      noRecipeResults: '没有匹配的配方',
      noRecipeResultsDesc: '试试其他关键词',
      copyId: '复制ID',
      copied: '已复制',
      copyRecipe: '配方',
      copyCmd: '指令',
      itemId: '物品 ID',
      itemNameEn: '英文名称',
      category: '所属分类',
      sizeTiers: '尺寸规格',
      notes: '说明',
      normalPlayers: '普通玩家',
      premiumOnly: 'LARGE 和 INSANE',
      premiumDesc: '尺寸需要 Premium 赞助权限才能创建。',
      insaneNote: '其中 4096×4096 (INSANE) 地图对服务器性能要求较高，适合大型服务器与极限生存体验。',
      iconLegend: '图标含义',
      health: '生命值',
      healthDesc: 'Health points',
      radiation: '辐射值',
      radiationDesc: 'Sickness / radiation',
      bleed: '止血',
      bleedDesc: 'Stops bleeding',
      brokenLeg: '治疗断腿',
      brokenLegDesc: 'Removes broken leg',
      stamina: '恢复体力',
      staminaDesc: 'Stamina boost',
      filterAll: '全部',
      filterHigh: '高级药品',
      filterMid: '中级药品',
      filterLow: '初级药品',
      filterBerry: '浆果类',
      filterSpecial: '特殊',
      medicationTips: '用药指南',
      tipWarning: '注意事项',
      tipWarningText: '大部分回血药同时会增加辐射值，不能无脑连续嗑药',
      tipHighValue: '高性价比回血',
      tipHighValueText: 'Brilliant Green 单次 +75% 血量，回血最高，但辐射上涨也很明显',
      tipEmergency: '应急处理',
      tipEmergencyText: '吗啡治疗断腿、肾上腺素瞬间回满体力；疫苗可以大幅降低自身辐射值',
      tipBerry: '浆果类',
      tipBerryText: '碾碎浆果只能少量回血，前期没药品临时应急使用',
      tipVodka: '伏特加特殊效果',
      tipVodkaText: '血量无增减，小幅降低自身辐射（-1%）',
      tipCombat: '对战要点',
      tipCombatText: '对战时出现流血优先治疗流血，不要只顾回血',
      testWeapons: '测试武器',
      damage: '伤害',
      hitsToDestroy: '破坏次数对比',
      buildingType: '建筑类型',
      hp: '血量',
      invalid: '无效',
      practicalTips: '实用建议',
      craftsmanLevelSystem: '工匠等级系统',
      searchInCategory: '在分类中搜索配方或材料...',
      recipeCount: '配方',
      level: '级',
      expShort: 'EXP',
      ptlCommunity: 'PTL 中国',
      ptlSubtitle: 'Erythros Lite 官方授权社区',
      ptlQQ1: 'QQ一群：666522680',
      ptlQQ2: 'QQ二群：1107305296',
      noSpecialEffect: '无特殊效果',
      stopsBleed: '止血',
      healsBrokenLeg: '治断腿',
      fullStamina: '回满体力',
      playerLogin: '玩家登录',
      playerRegister: '玩家注册',
      playerProfile: '个人资料',
      playerViewProfile: '玩家资料',
      accountLabel: '账号标识 (QQ / DiscordID / 邮箱)',
      passwordLabel: '密码',
      confirmPasswordLabel: '确认密码',
      loginButton: '登录',
      registerButton: '注册',
      noAccountYet: '还没有账号？',
      alreadyHaveAccount: '已有账号？',
      registerNow: '立即注册',
      backToLogin: '返回登录',
      gameIdLabel: '游戏 ID（6-12位纯数字，注册后不可修改）',
      passwordPlaceholder: '请输入密码',
      confirmPasswordPlaceholder: '请再次输入密码',
      gameIdPlaceholder: '只允许填写数字',
      qqPlaceholder: '请输入QQ号（5-15位）',
      externalContactPlaceholder: '不要填写Discord昵称',
      playerTypeLabel: '选择你的玩家类型',
      cnPlayer: '国内玩家（有QQ）',
      intlPlayer: '海外玩家（无QQ）',
      editProfile: '编辑（仅可修改密码）',
      logout: '退出登录',
      saveChanges: '保存修改',
      currentPasswordLabel: '原密码',
      newPasswordLabel: '新密码',
      confirmGameId: '确认你的游戏ID',
      gameIdConfirmMsg: '你填写的游戏ID：{gameId}',
      gameIdConfirmWarning: '⚠️ 注册成功之后游戏ID永久不能修改！',
      gameIdConfirmDesc: '请确认这确实是你游戏内的真实数字ID，填错只能找管理员后台处理。',
      cancel: '取消',
      confirmSubmit: '确认提交',
      gameIdLocked: '游戏ID、联系信息注册完成后不可修改',
      viewOnlyPublic: '仅展示公开游戏ID信息',
      loading: '加载中...',
      adminLogin: '管理员登录',
      adminUsername: '用户名',
      adminPassword: '密码',
      adminLoginBtn: '登录',
      adminDefaultHint: '默认账号 admin / admin123',
      adminLogout: '退出登录',
      adminDashboard: '统计面板',
      adminItems: '物品管理',
      adminRecipes: '合成配方',
      adminMedicines: '药品效果',
      adminBuildings: '建筑耐久',
      adminPlayers: '玩家管理',
      adminPassword: '修改密码',
      adminData: '数据管理',
      adminTotalVisits: '总访问量',
      adminTodayVisits: '今日访问',
      adminWeekVisits: '本周访问',
      adminTotalPlayers: '注册玩家',
      adminTopItems: '热门物品 Top 10',
      adminTopPages: '访问页面分布',
      adminTrend: '近7天访问趋势',
      adminRecentPlayers: '最近注册玩家',
      adminRecentVisits: '最近访问记录',
      adminLoading: '加载中...',
      adminLoadFailed: '加载失败',
      adminNoData: '暂无数据',
      adminEdit: '编辑',
      adminDelete: '删除',
      adminConfirmDelete: '确认删除',
      adminDeleteMsg: '确定要删除「{name}」吗？此操作不可撤销。',
      adminCancel: '取消',
      adminConfirm: '确认',
      adminAdd: '新增',
      adminAddItem: '新增物品',
      adminEditItem: '编辑物品',
      adminItemId: '物品 ID',
      adminItemNameZh: '中文名称',
      adminItemNameEn: '英文名称',
      adminItemCategory: '分类',
      adminSave: '保存',
      adminAddRecipe: '新增配方',
      adminEditRecipe: '编辑配方',
      adminRecipeResultZh: '产物（中文）',
      adminRecipeResultEn: '产物（英文）',
      adminRecipeResultCount: '产出数量',
      adminRecipeCategory: '分类',
      adminRecipeLevel: '工匠等级',
      adminRecipeMaterials: '材料清单',
      adminRecipeMaterialZh: '中文名',
      adminRecipeMaterialEn: '英文名',
      adminRecipeMaterialCount: '数量',
      adminRecipeAddMaterial: '+ 添加材料',
      adminRecipeRemoveMaterial: '✕',
      adminRecipeMaterialError: '请完整填写所有材料（名称和数量）',
      adminMedicinesHealth: '回血%',
      adminMedicinesRadiation: '辐射%',
      adminMedicinesTier: '等级',
      adminMedicinesEffects: '特效',
      adminMedicinesAdd: '新增药品',
      adminMedicinesEdit: '编辑药品',
      adminMedicinesEffectBleed: '止血',
      adminMedicinesEffectBrokenLeg: '治断腿',
      adminMedicinesEffectStamina: '回体力',
      adminBuildingsHp: '血量',
      adminBuildingsWeaponDamage: '武器破坏数据',
      adminBuildingsAdd: '新增建筑',
      adminBuildingsEdit: '编辑建筑',
      adminBuildingsComplexHint: '建筑数据含复杂JSON，建议直接操作数据库或后续完善表单',
      adminPlayersSearch: '搜索...',
      adminPlayersQQ: 'QQ号',
      adminPlayersGameId: '游戏ID',
      adminPlayersRegisterTime: '注册时间',
      adminPasswordChange: '修改管理员密码',
      adminPasswordOld: '原密码',
      adminPasswordNew: '新密码（至少6位）',
      adminPasswordConfirm: '确认新密码',
      adminPasswordSubmit: '修改密码',
      adminPasswordSuccess: '密码修改成功！',
      adminPasswordError: '修改失败',
      adminDataReset: '数据管理',
      adminDataResetDesc: '从初始种子数据重建数据库。警告：会覆盖所有手动修改的数据！',
      adminDataResetDanger: '一键导入初始数据',
      adminDataResetConfirmTitle: '确认重置数据？',
      adminDataResetConfirmMsg: '此操作将覆盖所有物品、配方、药品、建筑数据为初始版本，所有手动修改将丢失。确定继续吗？',
      adminDataResetSuccess: '操作完成',
      adminDataResetFail: '操作失败',
      community: '社区',
      communityTitle: '社区讨论',
      newPost: '发新帖',
      postTitle: '标题',
      postContent: '内容',
      postCategory: '分类',
      categoryGeneral: '综合',
      categoryGuide: '攻略',
      categoryHelp: '求助',
      categoryDiscussion: '讨论',
      categoryFeedback: '反馈',
      postSubmit: '发布',
      postCancel: '取消',
      reply: '回复',
      writeReply: '写回复...',
      noPosts: '暂无帖子，快来发第一个吧！',
      noReplies: '还没有回复，抢个沙发！',
      postDeleted: '帖子已删除',
      replyDeleted: '回复已删除',
      confirmDeletePost: '确认删除此帖子？',
      confirmDeleteReply: '确认删除此回复？',
      delete: '删除',
      edit: '编辑',
      like: '点赞',
      unlike: '取消赞',
      viewCount: '浏览',
      replyCount: '回复',
      postedBy: '发布于',
      author: '作者',
      loadMore: '加载更多',
      backToCommunity: '返回社区',
      welcomeTitle: '欢迎！',
      welcomeButton: '我知道了',
      welcomeDefault: '欢迎来到 Erythros Lite 维基！祝你游戏愉快！🎮',
      // 新增键
      idLabel: 'ID',
      giveCommand: '/give',
      crafting: '合成配方',
      craftingEn: 'Crafting',
      craftingRecipe: '合成配方',
      materialsLabel: '原料',
      quantityLabel: '数量',
      wikiSubtitle: 'PTL社群模组维基速查',
      homeIntro: '本维基收录武器、防具、道具ID、合成配方、药品效果、建筑耐久、地图规格。使用顶部搜索框可以检索物品ID、中英文名称，按 / 快捷键快速聚焦搜索。',
      wikiBy: '维基制作：PTL社群',
      gameCredits: '游戏制作团队',
      creditTitle: '🎯 游戏名称',
      creditCreator: '👨‍💻 创始人 / 开发者',
      creditCreatorDesc: '（个人开发者）',
      creditPublisher: '🏢 发行 / 工作室',
      creditPublisherDesc: '（发行商）',
      creditWebsite: '🌐 官方网站',
      creditEmail: '📧 联系邮箱',
      creditEmailDesc: '（开发者）',
      creditCommunity: '🇨🇳 中文社区',
      creditCommunityDesc: '（玩家社群 / 维基维护）',
      wikiDisclaimer: '本维基由 PTL 中国社区维护，非官方出品，仅供玩家参考。',
      wikiContents: '📖 维基收录范围',
      wikiContent1: '武器、防具、道具完整属性与数值',
      wikiContent2: '合成配方、药品效果、建筑耐久参数',
      wikiContent3: '地图规格、物品ID、中英文名称对照',
      wikiContent4: '支持ID、名称快速检索，分类筛选',
      wikiTip: '提示：修改浏览器地址 Hash #items 进入物品维基页面',
      mapSizeSpec: 'Map Size Specifications',
      scaleLabel: 'SCALE',
      mapPermNormal: '可创建 TINY、SMALL、MEDIUM 三种尺寸的地图。',
      mapPermNormalEn: '普通玩家可创建 TINY、SMALL、MEDIUM 三种尺寸的地图。',
      mapPermPremium: 'LARGE 和 INSANE',
      mapPermPremiumEn: '尺寸需要 Premium 赞助权限才能创建。',
      insaneNote: '其中 4096×4096 (INSANE) 地图对服务器性能要求较高，适合大型服务器与极限生存体验。',
      insaneNoteEn: '4096×4096 (INSANE) 地图对服务器性能要求较高，适合大型服务器与极限生存体验。',
      medicineGuide: '药品效果指南',
      medItems: '药品',
      buildingsLabel: '建筑类型',
      buildingDurabilityData: '建筑耐久数据',
      craftingList: '合成配方列表',
      postNotFound: '帖子不存在',
      loadFailed: '加载失败',
      anonymous: '匿名',
      allLabel: '全部',
      loginToPost: '登录后发帖',
      beFirstPost: '点击“发新帖”成为第一个！',
      pinned: '置顶',
      homeLabel: '首页',
      homeLabelEn: '首页',
      homeLabelRu: '首页',
      mapSizesEn: '地图尺寸',
      mapSizesRu: '地图尺寸',
      medicineEffectsEn: '药品效果',
      medicineEffectsRu: '药品效果',
      craftingRecipesEn: '合成配方',
      craftingRecipesRu: '合成配方',
      buildingDurabilityEn: '建筑耐久',
      buildingDurabilityRu: '建筑耐久',
      communityEn: '社区',
      communityRu: '社区',
      wikiRef: '维基速查',
      registerLogin: '注册 / 登录',
      adminPanel: '管理后台',
      totalItemsLabel: '物品总计',
      itemsUnit: '项',
      langZh: '中文',
      langEn: 'EN',
      langRu: 'RU',
      adminLoading: 'Admin Panel (加载中...)',
      pageNotFound: '页面不存在',
      gameIdFormatError: '游戏ID只能填写6‑12位纯数字！',
      passwordMismatch: '两次输入的密码不一致',
      currentPasswordRequired: '请填写原密码',
      passwordChanged: '密码修改成功！',
      updateFailed: '修改失败',
      myPosts: '我的帖子',
      noMyPosts: '还没有发布过帖子',
      goToCommunity: '去社区发个帖吧！',
    },
    en: {
      appTitle: 'Erythros Lite Wiki',
      appSubtitle: 'Quick Reference',
      searchPlaceholder: 'Search item name, ID, or recipe...',
      totalItems: 'Total Items',
      categories: 'Categories',
      itemCategories: 'Item Categories',
      gameData: 'Game Data',
      mapSizes: 'Map Sizes',
      medicineEffects: 'Medicine Effects',
      craftingRecipes: 'Crafting Recipes',
      buildingDurability: 'Building Durability',
      searchResults: 'Search Results',
      searchResultsFor: 'Results for',
      found: 'FOUND',
      itemsItems: 'ITEMS',
      idRange: 'ID RANGE',
      noResults: 'No matching items found',
      noResultsDesc: 'Try different keywords or switch categories',
      noRecipeResults: 'No matching recipes',
      noRecipeResultsDesc: 'Try different keywords',
      copyId: 'Copy ID',
      copied: 'Copied',
      copyRecipe: 'Recipe',
      copyCmd: 'Cmd',
      itemId: 'Item ID',
      itemNameEn: 'English Name',
      category: 'Category',
      sizeTiers: 'Size Tiers',
      notes: 'Notes',
      normalPlayers: 'Regular players',
      premiumOnly: 'LARGE and INSANE',
      premiumDesc: 'sizes require Premium sponsor access.',
      insaneNote: '4096×4096 (INSANE) maps require high server performance, suitable for large servers and extreme survival.',
      iconLegend: 'Icon Legend',
      health: 'Health',
      healthDesc: 'Health points',
      radiation: 'Radiation',
      radiationDesc: 'Sickness / radiation',
      bleed: 'Stop Bleed',
      bleedDesc: 'Stops bleeding',
      brokenLeg: 'Heal Leg',
      brokenLegDesc: 'Removes broken leg',
      stamina: 'Stamina',
      staminaDesc: 'Stamina boost',
      filterAll: 'All',
      filterHigh: 'Advanced',
      filterMid: 'Intermediate',
      filterLow: 'Basic',
      filterBerry: 'Berries',
      filterSpecial: 'Special',
      medicationTips: 'Medication Tips',
      tipWarning: 'Warning',
      tipWarningText: 'Most healing meds also increase radiation — don\'t spam them blindly.',
      tipHighValue: 'Best Healing',
      tipHighValueText: 'Brilliant Green heals +75% HP (highest), but also significantly increases radiation.',
      tipEmergency: 'Emergency Use',
      tipEmergencyText: 'Morphine heals broken leg; Adrenaline refills stamina; Vaccine greatly reduces radiation.',
      tipBerry: 'Berries',
      tipBerryText: 'Crushed berries only heal slightly — use as emergency med when you have nothing else.',
      tipVodka: 'Vodka Special',
      tipVodkaText: 'No HP change; slightly reduces radiation (-1%).',
      tipCombat: 'Combat Priority',
      tipCombatText: 'Always treat bleeding first in combat — don\'t just heal HP.',
      testWeapons: 'Test Weapons',
      damage: 'dmg',
      hitsToDestroy: 'Hits to Destroy',
      buildingType: 'Building Type',
      hp: 'HP',
      invalid: 'Invalid',
      practicalTips: 'Tips & Strategies',
      craftsmanLevelSystem: 'Craftsman Level System',
      searchInCategory: 'Search recipes or materials in this category...',
      recipeCount: 'Recipes',
      level: 'lv',
      expShort: 'EXP',
      ptlCommunity: 'PTL China',
      ptlSubtitle: 'Erythros Lite Official Community',
      ptlQQ1: 'QQ Group 1: 666522680',
      ptlQQ2: 'QQ Group 2: 1107305296',
      noSpecialEffect: 'No special effects',
      stopsBleed: 'Stops Bleed',
      healsBrokenLeg: 'Heals Leg',
      fullStamina: 'Full Stamina',
      playerLogin: 'Player Login',
      playerRegister: 'Player Register',
      playerProfile: 'Profile',
      playerViewProfile: 'Player Profile',
      accountLabel: 'Account ID (QQ / DiscordID / Email)',
      passwordLabel: 'Password',
      confirmPasswordLabel: 'Confirm Password',
      loginButton: 'Login',
      registerButton: 'Register',
      noAccountYet: "Don't have an account?",
      alreadyHaveAccount: 'Already have an account?',
      registerNow: 'Register Now',
      backToLogin: 'Back to Login',
      gameIdLabel: 'Game ID (6-12 digits, permanent after registration)',
      passwordPlaceholder: 'Enter your password',
      confirmPasswordPlaceholder: 'Re-enter your password',
      gameIdPlaceholder: 'Digits only',
      qqPlaceholder: 'Enter QQ number (5-15 digits)',
      externalContactPlaceholder: "Don't enter Discord nickname",
      playerTypeLabel: 'Select your player type',
      cnPlayer: 'China player (has QQ)',
      intlPlayer: 'International player (no QQ)',
      editProfile: 'Edit (password only)',
      logout: 'Logout',
      saveChanges: 'Save Changes',
      currentPasswordLabel: 'Current Password',
      newPasswordLabel: 'New Password',
      confirmGameId: 'Confirm your Game ID',
      gameIdConfirmMsg: 'Your Game ID: {gameId}',
      gameIdConfirmWarning: '⚠️ Game ID is permanent after registration!',
      gameIdConfirmDesc: 'Please confirm this is your actual in-game numeric ID. Mistakes can only be fixed by admin.',
      cancel: 'Cancel',
      confirmSubmit: 'Confirm Submit',
      gameIdLocked: 'Game ID and contact info are locked after registration',
      viewOnlyPublic: 'Showing public game ID only',
      loading: 'Loading...',
      adminLogin: 'Admin Login',
      adminUsername: 'Username',
      adminPassword: 'Password',
      adminLoginBtn: 'Login',
      adminDefaultHint: 'Default admin / admin123',
      adminLogout: 'Logout',
      adminDashboard: 'Dashboard',
      adminItems: 'Items',
      adminRecipes: 'Recipes',
      adminMedicines: 'Medicines',
      adminBuildings: 'Buildings',
      adminPlayers: 'Players',
      adminPassword: 'Change Password',
      adminData: 'Data',
      adminTotalVisits: 'Total Visits',
      adminTodayVisits: 'Today',
      adminWeekVisits: 'This Week',
      adminTotalPlayers: 'Total Players',
      adminTopItems: 'Top 10 Items',
      adminTopPages: 'Page Distribution',
      adminTrend: '7-Day Trend',
      adminRecentPlayers: 'Recent Players',
      adminRecentVisits: 'Recent Visits',
      adminLoading: 'Loading...',
      adminLoadFailed: 'Load failed',
      adminNoData: 'No data',
      adminEdit: 'Edit',
      adminDelete: 'Delete',
      adminConfirmDelete: 'Confirm Delete',
      adminDeleteMsg: 'Are you sure you want to delete "{name}"? This action cannot be undone.',
      adminCancel: 'Cancel',
      adminConfirm: 'Confirm',
      adminAdd: 'Add',
      adminAddItem: 'Add Item',
      adminEditItem: 'Edit Item',
      adminItemId: 'Item ID',
      adminItemNameZh: 'Chinese Name',
      adminItemNameEn: 'English Name',
      adminItemCategory: 'Category',
      adminSave: 'Save',
      adminAddRecipe: 'Add Recipe',
      adminEditRecipe: 'Edit Recipe',
      adminRecipeResultZh: 'Result (Chinese)',
      adminRecipeResultEn: 'Result (English)',
      adminRecipeResultCount: 'Count',
      adminRecipeCategory: 'Category',
      adminRecipeLevel: 'Craftsman Level',
      adminRecipeMaterials: 'Materials',
      adminRecipeMaterialZh: 'Chinese Name',
      adminRecipeMaterialEn: 'English Name',
      adminRecipeMaterialCount: 'Count',
      adminRecipeAddMaterial: '+ Add Material',
      adminRecipeRemoveMaterial: '✕',
      adminRecipeMaterialError: 'Please complete all materials (name and count)',
      adminMedicinesHealth: 'Health %',
      adminMedicinesRadiation: 'Radiation %',
      adminMedicinesTier: 'Tier',
      adminMedicinesEffects: 'Effects',
      adminMedicinesAdd: 'Add Medicine',
      adminMedicinesEdit: 'Edit Medicine',
      adminMedicinesEffectBleed: 'Stop Bleed',
      adminMedicinesEffectBrokenLeg: 'Heal Leg',
      adminMedicinesEffectStamina: 'Stamina',
      adminBuildingsHp: 'HP',
      adminBuildingsWeaponDamage: 'Weapon Damage Data',
      adminBuildingsAdd: 'Add Building',
      adminBuildingsEdit: 'Edit Building',
      adminBuildingsComplexHint: 'Building data contains complex JSON, please use database or improve form later',
      adminPlayersSearch: 'Search...',
      adminPlayersQQ: 'QQ',
      adminPlayersGameId: 'Game ID',
      adminPlayersRegisterTime: 'Registered',
      adminPasswordChange: 'Change Admin Password',
      adminPasswordOld: 'Old Password',
      adminPasswordNew: 'New Password (min 6 chars)',
      adminPasswordConfirm: 'Confirm New Password',
      adminPasswordSubmit: 'Change Password',
      adminPasswordSuccess: 'Password changed successfully!',
      adminPasswordError: 'Change failed',
      adminDataReset: 'Data Management',
      adminDataResetDesc: 'Reset database to initial seed data. Warning: All manual changes will be overwritten!',
      adminDataResetDanger: 'Reset to Initial Data',
      adminDataResetConfirmTitle: 'Confirm Reset?',
      adminDataResetConfirmMsg: 'This will reset all items, recipes, medicines, buildings to initial version. All manual changes will be lost. Continue?',
      adminDataResetSuccess: 'Operation complete',
      adminDataResetFail: 'Operation failed',
      community: 'Community',
      communityTitle: 'Community Forum',
      newPost: 'New Post',
      postTitle: 'Title',
      postContent: 'Content',
      postCategory: 'Category',
      categoryGeneral: 'General',
      categoryGuide: 'Guide',
      categoryHelp: 'Help',
      categoryDiscussion: 'Discussion',
      categoryFeedback: 'Feedback',
      postSubmit: 'Post',
      postCancel: 'Cancel',
      reply: 'Reply',
      writeReply: 'Write a reply...',
      noPosts: 'No posts yet. Be the first!',
      noReplies: 'No replies yet. Be the first!',
      postDeleted: 'Post deleted',
      replyDeleted: 'Reply deleted',
      confirmDeletePost: 'Confirm delete this post?',
      confirmDeleteReply: 'Confirm delete this reply?',
      delete: 'Delete',
      edit: 'Edit',
      like: 'Like',
      unlike: 'Unlike',
      viewCount: 'Views',
      replyCount: 'Replies',
      postedBy: 'Posted by',
      author: 'Author',
      loadMore: 'Load more',
      backToCommunity: 'Back to Community',
      welcomeTitle: 'Welcome!',
      welcomeButton: 'Got it!',
      welcomeDefault: 'Welcome to Erythros Lite Wiki! Enjoy your game! 🎮',
      // 新增键
      idLabel: 'ID',
      giveCommand: '/give',
      crafting: 'Crafting',
      craftingEn: 'Crafting',
      craftingRecipe: 'Craft Recipe',
      materialsLabel: 'Materials',
      quantityLabel: 'Quantity',
      wikiSubtitle: 'PTL Community Mod Quick Reference',
      homeIntro: 'This wiki contains item IDs, crafting recipes, medicine effects, building durability and map specs. Press / to quick‑focus search box.',
      wikiBy: 'Wiki by: PTL Community',
      gameCredits: 'Game Credits',
      creditTitle: '🎯 Title',
      creditCreator: '👨‍💻 Creator / Developer',
      creditCreatorDesc: '(Independent Developer)',
      creditPublisher: '🏢 Publisher / Studio',
      creditPublisherDesc: '(Publisher)',
      creditWebsite: '🌐 Official Website',
      creditEmail: '📧 Contact',
      creditEmailDesc: '(Developer)',
      creditCommunity: '🇨🇳 Chinese Community',
      creditCommunityDesc: '(Player Community / Wiki Maintainers)',
      wikiDisclaimer: 'This wiki is maintained by PTL China Community, an unofficial fan project.',
      wikiContents: '📖 Wiki Contents',
      wikiContent1: 'Full stats for weapons, armors and items',
      wikiContent2: 'Craft recipes, medicine effects, building durability',
      wikiContent3: 'Map specs, item IDs, bilingual names',
      wikiContent4: 'Search by ID / name, filter categories',
      wikiTip: 'Tip: Change url hash to #items for item wiki',
      mapSizeSpec: 'Size Specifications',
      scaleLabel: 'SCALE',
      mapPermNormal: 'can create TINY, SMALL, and MEDIUM size maps.',
      mapPermNormalEn: 'Regular players can create TINY, SMALL, and MEDIUM size maps.',
      mapPermPremium: 'LARGE and INSANE',
      mapPermPremiumEn: 'sizes require Premium sponsor access.',
      insaneNote: '4096×4096 (INSANE) maps require high server performance, suitable for large servers and extreme survival.',
      insaneNoteEn: '4096×4096 (INSANE) maps require high server performance, suitable for large servers and extreme survival.',
      medicineGuide: 'Medicine Effects Guide',
      medItems: 'Medicine',
      buildingsLabel: 'Buildings',
      buildingDurabilityData: 'Building Durability Data',
      craftingList: 'Crafting Recipes List',
      postNotFound: 'Post not found',
      loadFailed: 'Load failed',
      anonymous: 'Anonymous',
      allLabel: 'All',
      loginToPost: 'Login to post',
      beFirstPost: 'Click "New Post" to be the first!',
      pinned: 'Pinned',
      homeLabel: 'Home',
      homeLabelEn: 'Home',
      homeLabelRu: 'Home',
      mapSizesEn: 'Map Sizes',
      mapSizesRu: 'Map Sizes',
      medicineEffectsEn: 'Medicine Effects',
      medicineEffectsRu: 'Medicine Effects',
      craftingRecipesEn: 'Crafting Recipes',
      craftingRecipesRu: 'Crafting Recipes',
      buildingDurabilityEn: 'Building Durability',
      buildingDurabilityRu: 'Building Durability',
      communityEn: 'Community',
      communityRu: 'Community',
      wikiRef: 'Wiki Ref',
      registerLogin: 'Register / Login',
      adminPanel: 'Admin Panel',
      totalItemsLabel: 'Total',
      itemsUnit: 'items',
      langZh: '中文',
      langEn: 'EN',
      langRu: 'RU',
      adminLoading: 'Admin Panel (Loading...)',
      pageNotFound: 'Page not found',
      gameIdFormatError: 'Game ID must be 6-12 digits only!',
      passwordMismatch: 'Passwords do not match',
      currentPasswordRequired: 'Please enter current password',
      passwordChanged: 'Password changed successfully!',
      updateFailed: 'Update failed',
      myPosts: 'My Posts',
      noMyPosts: 'No posts yet',
      goToCommunity: 'Go to community!',
    },
    ru: {
      appTitle: 'Erythros Lite Wiki',
      appSubtitle: 'Краткий справочник',
      searchPlaceholder: 'Поиск по названию, ID или рецепту...',
      totalItems: 'Всего предметов',
      categories: 'Категории',
      itemCategories: 'Категории предметов',
      gameData: 'Игровые данные',
      mapSizes: 'Размеры карт',
      medicineEffects: 'Эффекты медицины',
      craftingRecipes: 'Рецепты крафта',
      buildingDurability: 'Прочность построек',
      searchResults: 'Результаты поиска',
      searchResultsFor: 'Результаты для',
      found: 'НАЙДЕНО',
      itemsItems: 'ПРЕДМЕТЫ',
      idRange: 'ДИАПАЗОН ID',
      noResults: 'Предметы не найдены',
      noResultsDesc: 'Попробуйте другие ключевые слова или категории',
      noRecipeResults: 'Рецепты не найдены',
      noRecipeResultsDesc: 'Попробуйте другие ключевые слова',
      copyId: 'Копировать ID',
      copied: 'Скопировано',
      copyRecipe: 'Рецепт',
      copyCmd: 'Команда',
      itemId: 'ID предмета',
      itemNameEn: 'Английское название',
      category: 'Категория',
      sizeTiers: 'Размеры',
      notes: 'Примечания',
      normalPlayers: 'Обычные игроки',
      premiumOnly: 'LARGE и INSANE',
      premiumDesc: 'требуют Premium доступа.',
      insaneNote: 'Карты 4096×4096 (INSANE) требуют высокой производительности сервера.',
      iconLegend: 'Значки',
      health: 'Здоровье',
      healthDesc: 'Очки здоровья',
      radiation: 'Радиация',
      radiationDesc: 'Болезнь / радиация',
      bleed: 'Остановить кровотечение',
      bleedDesc: 'Останавливает кровотечение',
      brokenLeg: 'Вылечить ногу',
      brokenLegDesc: 'Убирает перелом ноги',
      stamina: 'Выносливость',
      staminaDesc: 'Восстанавливает выносливость',
      filterAll: 'Все',
      filterHigh: 'Высокий класс',
      filterMid: 'Средний класс',
      filterLow: 'Низкий класс',
      filterBerry: 'Ягоды',
      filterSpecial: 'Особые',
      medicationTips: 'Советы по лекарствам',
      tipWarning: 'Внимание',
      tipWarningText: 'Большинство лечебных средств также повышают радиацию.',
      tipHighValue: 'Лучшее лечение',
      tipHighValueText: 'Brilliant Green восстанавливает +75% HP, но сильно повышает радиацию.',
      tipEmergency: 'Экстренное использование',
      tipEmergencyText: 'Морфин лечит ногу, Адреналин восстанавливает выносливость, Вакцина снижает радиацию.',
      tipBerry: 'Ягоды',
      tipBerryText: 'Измельчённые ягоды слабо лечат — используйте в крайнем случае.',
      tipVodka: 'Водка',
      tipVodkaText: 'Не влияет на HP, слабо снижает радиацию (-1%).',
      tipCombat: 'Бой',
      tipCombatText: 'Сначала останавливайте кровотечение, не только лечите HP.',
      testWeapons: 'Тестовое оружие',
      damage: 'урон',
      hitsToDestroy: 'Ударов до разрушения',
      buildingType: 'Тип постройки',
      hp: 'HP',
      invalid: 'Недействительно',
      practicalTips: 'Советы и стратегии',
      craftsmanLevelSystem: 'Система уровней ремесленника',
      searchInCategory: 'Поиск рецептов в этой категории...',
      recipeCount: 'Рецепты',
      level: 'ур.',
      expShort: 'ОПЫТ',
      ptlCommunity: 'PTL Китай',
      ptlSubtitle: 'Официальное сообщество Erythros Lite',
      ptlQQ1: 'QQ группа 1: 666522680',
      ptlQQ2: 'QQ группа 2: 1107305296',
      noSpecialEffect: 'Нет особых эффектов',
      stopsBleed: 'Останавливает кровотечение',
      healsBrokenLeg: 'Лечит ногу',
      fullStamina: 'Восстанавливает выносливость',
      playerLogin: 'Вход игрока',
      playerRegister: 'Регистрация игрока',
      playerProfile: 'Профиль',
      playerViewProfile: 'Профиль игрока',
      accountLabel: 'Аккаунт (QQ / DiscordID / Email)',
      passwordLabel: 'Пароль',
      confirmPasswordLabel: 'Подтверждение пароля',
      loginButton: 'Войти',
      registerButton: 'Зарегистрироваться',
      noAccountYet: 'Нет аккаунта?',
      alreadyHaveAccount: 'Уже есть аккаунт?',
      registerNow: 'Зарегистрироваться',
      backToLogin: 'Назад ко входу',
      gameIdLabel: 'Game ID (6-12 цифр, постоянно после регистрации)',
      passwordPlaceholder: 'Введите пароль',
      confirmPasswordPlaceholder: 'Повторите пароль',
      gameIdPlaceholder: 'Только цифры',
      qqPlaceholder: 'Введите QQ номер (5-15 цифр)',
      externalContactPlaceholder: 'Не вводите никнейм Discord',
      playerTypeLabel: 'Выберите тип игрока',
      cnPlayer: 'Игрок из Китая (есть QQ)',
      intlPlayer: 'Международный игрок (нет QQ)',
      editProfile: 'Редактировать (только пароль)',
      logout: 'Выйти',
      saveChanges: 'Сохранить изменения',
      currentPasswordLabel: 'Текущий пароль',
      newPasswordLabel: 'Новый пароль',
      confirmGameId: 'Подтвердите Game ID',
      gameIdConfirmMsg: 'Ваш Game ID: {gameId}',
      gameIdConfirmWarning: '⚠️ Game ID постоянен после регистрации!',
      gameIdConfirmDesc: 'Пожалуйста, подтвердите ваш реальный игровой ID. Ошибки исправляет только администратор.',
      cancel: 'Отмена',
      confirmSubmit: 'Подтвердить',
      gameIdLocked: 'Game ID и контактные данные закреплены после регистрации',
      viewOnlyPublic: 'Показан только публичный Game ID',
      loading: 'Загрузка...',
      adminLogin: 'Вход администратора',
      adminUsername: 'Имя пользователя',
      adminPassword: 'Пароль',
      adminLoginBtn: 'Войти',
      adminDefaultHint: 'По умолчанию admin / admin123',
      adminLogout: 'Выйти',
      adminDashboard: 'Статистика',
      adminItems: 'Предметы',
      adminRecipes: 'Рецепты',
      adminMedicines: 'Лекарства',
      adminBuildings: 'Постройки',
      adminPlayers: 'Игроки',
      adminPassword: 'Сменить пароль',
      adminData: 'Данные',
      adminTotalVisits: 'Всего посещений',
      adminTodayVisits: 'Сегодня',
      adminWeekVisits: 'На этой неделе',
      adminTotalPlayers: 'Всего игроков',
      adminTopItems: 'Топ 10 предметов',
      adminTopPages: 'Распределение страниц',
      adminTrend: 'Динамика за 7 дней',
      adminRecentPlayers: 'Новые игроки',
      adminRecentVisits: 'Недавние посещения',
      adminLoading: 'Загрузка...',
      adminLoadFailed: 'Ошибка загрузки',
      adminNoData: 'Нет данных',
      adminEdit: 'Редактировать',
      adminDelete: 'Удалить',
      adminConfirmDelete: 'Подтвердите удаление',
      adminDeleteMsg: 'Удалить "{name}"? Действие необратимо.',
      adminCancel: 'Отмена',
      adminConfirm: 'Подтвердить',
      adminAdd: 'Добавить',
      adminAddItem: 'Добавить предмет',
      adminEditItem: 'Редактировать предмет',
      adminItemId: 'ID предмета',
      adminItemNameZh: 'Китайское название',
      adminItemNameEn: 'Английское название',
      adminItemCategory: 'Категория',
      adminSave: 'Сохранить',
      adminAddRecipe: 'Добавить рецепт',
      adminEditRecipe: 'Редактировать рецепт',
      adminRecipeResultZh: 'Результат (кит.)',
      adminRecipeResultEn: 'Результат (англ.)',
      adminRecipeResultCount: 'Количество',
      adminRecipeCategory: 'Категория',
      adminRecipeLevel: 'Уровень ремесленника',
      adminRecipeMaterials: 'Материалы',
      adminRecipeMaterialZh: 'Кит. название',
      adminRecipeMaterialEn: 'Англ. название',
      adminRecipeMaterialCount: 'Кол-во',
      adminRecipeAddMaterial: '+ Добавить материал',
      adminRecipeRemoveMaterial: '✕',
      adminRecipeMaterialError: 'Заполните все материалы (название и кол-во)',
      adminMedicinesHealth: 'Здоровье %',
      adminMedicinesRadiation: 'Радиация %',
      adminMedicinesTier: 'Уровень',
      adminMedicinesEffects: 'Эффекты',
      adminMedicinesAdd: 'Добавить лекарство',
      adminMedicinesEdit: 'Редактировать лекарство',
      adminMedicinesEffectBleed: 'Остановить кровотечение',
      adminMedicinesEffectBrokenLeg: 'Вылечить ногу',
      adminMedicinesEffectStamina: 'Выносливость',
      adminBuildingsHp: 'HP',
      adminBuildingsWeaponDamage: 'Данные по оружию',
      adminBuildingsAdd: 'Добавить постройку',
      adminBuildingsEdit: 'Редактировать постройку',
      adminBuildingsComplexHint: 'Данные содержат JSON, используйте базу данных',
      adminPlayersSearch: 'Поиск...',
      adminPlayersQQ: 'QQ',
      adminPlayersGameId: 'Game ID',
      adminPlayersRegisterTime: 'Регистрация',
      adminPasswordChange: 'Сменить пароль администратора',
      adminPasswordOld: 'Старый пароль',
      adminPasswordNew: 'Новый пароль (мин. 6 символов)',
      adminPasswordConfirm: 'Подтвердите новый пароль',
      adminPasswordSubmit: 'Сменить пароль',
      adminPasswordSuccess: 'Пароль изменён!',
      adminPasswordError: 'Ошибка изменения',
      adminDataReset: 'Управление данными',
      adminDataResetDesc: 'Сбросить базу данных к начальным данным. ВНИМАНИЕ: все изменения будут потеряны!',
      adminDataResetDanger: 'Сбросить к начальным данным',
      adminDataResetConfirmTitle: 'Подтвердить сброс?',
      adminDataResetConfirmMsg: 'Все предметы, рецепты, лекарства и постройки будут сброшены. Все изменения потеряны. Продолжить?',
      adminDataResetSuccess: 'Операция завершена',
      adminDataResetFail: 'Операция не удалась',
      community: 'Сообщество',
      communityTitle: 'Форум',
      newPost: 'Новый пост',
      postTitle: 'Заголовок',
      postContent: 'Содержание',
      postCategory: 'Категория',
      categoryGeneral: 'Общее',
      categoryGuide: 'Гайды',
      categoryHelp: 'Помощь',
      categoryDiscussion: 'Обсуждение',
      categoryFeedback: 'Отзывы',
      postSubmit: 'Опубликовать',
      postCancel: 'Отмена',
      reply: 'Ответить',
      writeReply: 'Написать ответ...',
      noPosts: 'Пока нет постов. Будьте первым!',
      noReplies: 'Пока нет ответов. Будьте первым!',
      postDeleted: 'Пост удалён',
      replyDeleted: 'Ответ удалён',
      confirmDeletePost: 'Удалить этот пост?',
      confirmDeleteReply: 'Удалить этот ответ?',
      delete: 'Удалить',
      edit: 'Редактировать',
      like: 'Нравится',
      unlike: 'Не нравится',
      viewCount: 'Просмотров',
      replyCount: 'Ответов',
      postedBy: 'Опубликовал',
      author: 'Автор',
      loadMore: 'Загрузить ещё',
      backToCommunity: 'Назад в сообщество',
      welcomeTitle: 'Добро пожаловать!',
      welcomeButton: 'Понятно',
      welcomeDefault: 'Добро пожаловать в вики Erythros Lite! Приятной игры! 🎮',
      // 新增键
      idLabel: 'ID',
      giveCommand: '/give',
      crafting: 'Крафт',
      craftingEn: 'Крафт',
      craftingRecipe: 'Рецепт крафта',
      materialsLabel: 'Материалы',
      quantityLabel: 'Количество',
      wikiSubtitle: 'Краткий справочник мода PTL',
      homeIntro: 'Эта вики содержит ID предметов, рецепты крафта, эффекты медицины, прочность построек и характеристики карт. Нажмите / для быстрого перехода к поиску.',
      wikiBy: 'Вики от: PTL Community',
      gameCredits: 'Создатели игры',
      creditTitle: '🎯 Название',
      creditCreator: '👨‍💻 Создатель / Разработчик',
      creditCreatorDesc: '(Независимый разработчик)',
      creditPublisher: '🏢 Издатель / Студия',
      creditPublisherDesc: '(Издатель)',
      creditWebsite: '🌐 Официальный сайт',
      creditEmail: '📧 Контакт',
      creditEmailDesc: '(Разработчик)',
      creditCommunity: '🇨🇳 Китайское сообщество',
      creditCommunityDesc: '(Игровое сообщество / Администраторы вики)',
      wikiDisclaimer: 'Эта вики поддерживается сообществом PTL China, неофициальный фанатский проект.',
      wikiContents: '📖 Содержание вики',
      wikiContent1: 'Полные характеристики оружия, брони и предметов',
      wikiContent2: 'Рецепты крафта, эффекты медицины, прочность построек',
      wikiContent3: 'Характеристики карт, ID предметов, двуязычные названия',
      wikiContent4: 'Поиск по ID / названию, фильтрация по категориям',
      wikiTip: 'Совет: измените хеш URL на #items для перехода к вики предметов',
      mapSizeSpec: 'Спецификации размеров',
      scaleLabel: 'МАСШТАБ',
      mapPermNormal: 'могут создавать карты размеров TINY, SMALL и MEDIUM.',
      mapPermNormalEn: 'Обычные игроки могут создавать карты размеров TINY, SMALL и MEDIUM.',
      mapPermPremium: 'LARGE и INSANE',
      mapPermPremiumEn: 'требуют Premium доступа.',
      insaneNote: 'Карты 4096×4096 (INSANE) требуют высокой производительности сервера, подходят для больших серверов и экстремального выживания.',
      insaneNoteEn: 'Карты 4096×4096 (INSANE) требуют высокой производительности сервера, подходят для больших серверов и экстремального выживания.',
      medicineGuide: 'Руководство по эффектам медицины',
      medItems: 'ЛЕКАРСТВА',
      buildingsLabel: 'Постройки',
      buildingDurabilityData: 'Данные по прочности построек',
      craftingList: 'Список рецептов крафта',
      postNotFound: 'Пост не найден',
      loadFailed: 'Ошибка загрузки',
      anonymous: 'Аноним',
      allLabel: 'Все',
      loginToPost: 'Войдите, чтобы написать',
      beFirstPost: 'Нажмите «Новый пост», чтобы стать первым!',
      pinned: 'Закреплено',
      homeLabel: 'Главная',
      homeLabelEn: 'Главная',
      homeLabelRu: 'Главная',
      mapSizesEn: 'Размеры карт',
      mapSizesRu: 'Размеры карт',
      medicineEffectsEn: 'Эффекты медицины',
      medicineEffectsRu: 'Эффекты медицины',
      craftingRecipesEn: 'Рецепты крафта',
      craftingRecipesRu: 'Рецепты крафта',
      buildingDurabilityEn: 'Прочность построек',
      buildingDurabilityRu: 'Прочность построек',
      communityEn: 'Сообщество',
      communityRu: 'Сообщество',
      wikiRef: 'Вики',
      registerLogin: 'Регистрация / Вход',
      adminPanel: 'Панель администратора',
      totalItemsLabel: 'Всего предметов',
      itemsUnit: 'предметов',
      langZh: 'Китайский',
      langEn: 'Английский',
      langRu: 'Русский',
      adminLoading: 'Панель администратора (Загрузка...)',
      pageNotFound: 'Страница не найдена',
      gameIdFormatError: 'Game ID должен содержать только 6-12 цифр!',
      passwordMismatch: 'Пароли не совпадают',
      currentPasswordRequired: 'Введите текущий пароль',
      passwordChanged: 'Пароль изменён успешно!',
      updateFailed: 'Ошибка обновления',
      myPosts: 'Мои посты',
      noMyPosts: 'Нет постов',
      goToCommunity: 'Перейти в сообщество!',
    },
  };

  const langs = ['zh', 'en', 'ru'];
  langs.forEach(lang => {
    if (!window.UI_STRINGS[lang]) window.UI_STRINGS[lang] = {};
    const target = window.UI_STRINGS[lang];
    const source = defaultStrings[lang];
    for (const key in source) {
      if (!target.hasOwnProperty(key)) {
        target[key] = source[key];
      }
    }
  });
})();

// ===== ICONS =====
const ICONS = {
  helm: (<svg viewBox="0 0 24 24"><path d="M4 15c0-4.5 3.5-8 8-8s8 3.5 8 8" /><path d="M4 15h16" /><path d="M7 15v3h10v-3" /><path d="M12 7V5" /></svg>),
  bag: (<svg viewBox="0 0 24 24"><path d="M6 8h12l1 12H5L6 8z" /><path d="M9 8V6a3 3 0 016 0v2" /></svg>),
  shield: (<svg viewBox="0 0 24 24"><path d="M12 3l8 3v6c0 5-3.5 8-8 9-4.5-1-8-4-8-9V6l8-3z" /></svg>),
  shirt: (<svg viewBox="0 0 24 24"><path d="M6 4l3 2h6l3-2 4 4-2 2v10H4V10L2 8l4-4z" /></svg>),
  pants: (<svg viewBox="0 0 24 24"><path d="M5 4h14l-2 16h-4l-1-5h-2l-1 5H7L5 4z" /></svg>),
  gun: (<svg viewBox="0 0 24 24"><path d="M3 10h3v3H3zM6 10h13v4H9l-1-1H6v-3z" /><path d="M14 14v4H9v-4" /></svg>),
  axe: (<svg viewBox="0 0 24 24"><path d="M5 21l7-7" /><path d="M14 3l7 7-4 4-7-7 4-4z" /></svg>),
  scope: (<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="7" /><path d="M12 5v14M5 12h14" /><path d="M3 3l3 3M18 18l3 3" /></svg>),
  bullet: (<svg viewBox="0 0 24 24"><path d="M10 3h4v6h-4z" /><path d="M8 9h8v10H8z" /></svg>),
  laser: (<svg viewBox="0 0 24 24"><path d="M4 12h10" /><path d="M14 9v6" /><circle cx="19" cy="12" r="2" /></svg>),
  muzzle: (<svg viewBox="0 0 24 24"><rect x="3" y="9" width="14" height="6" rx="1" /><path d="M17 11h4v2h-4" /><path d="M7 9v6M11 9v6" /></svg>),
  med: (<svg viewBox="0 0 24 24"><rect x="4" y="7" width="16" height="13" rx="1" /><path d="M8 7V5a2 2 0 012-2h4a2 2 0 012 2v2" /><path d="M12 11v5M9.5 13.5h5" /></svg>),
  food: (<svg viewBox="0 0 24 24"><path d="M6 13c0-3 2.5-5 6-5s6 2 6 5v7H6v-7z" /><path d="M10 8V6a2 2 0 014 0v2" /></svg>),
  drink: (<svg viewBox="0 0 24 24"><path d="M8 3h8l-1 18H9L8 3z" /><path d="M7 6h10" /></svg>),
  furn: (<svg viewBox="0 0 24 24"><rect x="5" y="9" width="14" height="10" /><path d="M5 9V7h14v2" /><path d="M7 19v2M17 19v2" /></svg>),
  wall: (<svg viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="16" /><path d="M3 10h18M3 15h18M9 5v5M15 10v5M9 15v6" /></svg>),
  mat: (<svg viewBox="0 0 24 24"><rect x="4" y="8" width="16" height="10" rx="1" /><path d="M8 8V5h8v3" /><path d="M8 13h8" /></svg>),
  obuild: (<svg viewBox="0 0 24 24"><path d="M3 19h18" /><path d="M5 19V9l7-5 7 5v10" /><path d="M9 19v-6h6v6" /></svg>),
  seed: (<svg viewBox="0 0 24 24"><path d="M12 21V9" /><path d="M12 9c-4 0-7-2-7-6 4 0 7 2 7 6z" /><path d="M12 12c3 0 6-1 6-5-3 0-6 1-6 5z" /></svg>),
  grenade: (<svg viewBox="0 0 24 24"><circle cx="12" cy="14" r="7" /><path d="M10 7h4" /><path d="M13 4c1.5 1 2 2 1 4" /></svg>),
  oammo: (<svg viewBox="0 0 24 24"><path d="M10 4l6 8H7l3-8z" /><path d="M9 12h6v5H9z" /></svg>),
  tool: (<svg viewBox="0 0 24 24"><path d="M14.7 6.3a4 4 0 00-5.4 5.4L3 18l3 3 6.3-6.3a4 4 0 005.4-5.4l-2.6 2.6-2.6-2.6 2.6-3z" /></svg>),
  map: (<svg viewBox="0 0 24 24"><path d="M9 3L3 5v16l6-2 6 2 6-2V3l-6 2-6-2z" /><path d="M9 3v16M15 5v16" /></svg>),
  craft: (<svg viewBox="0 0 24 24"><path d="M14.7 6.3a4 4 0 00-5.4 5.4L3 18l3 3 6.3-6.3a4 4 0 005.4-5.4l-2.6 2.6-2.6-2.6 2.6-3z" /></svg>),
  dura: (<svg viewBox="0 0 24 24"><path d="M12 3l8 3v6c0 5-3.5 8-8 9-4.5-1-8-4-8-9V6l8-3z" /><path d="M9 12l2 2 4-4" /></svg>),
  search: (<svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="7" /><path d="M21 21l-4.3-4.3" /></svg>),
  copy: (<svg viewBox="0 0 24 24"><rect x="9" y="9" width="11" height="11" rx="1" /><path d="M5 15V4h11" /></svg>),
  check: (<svg viewBox="0 0 24 24"><path d="M5 12l5 5L20 7" /></svg>),
  menu: (<svg viewBox="0 0 24 24"><path d="M4 6h16M4 12h16M4 18h16" /></svg>),
  heart: (<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 21s-7-4.5-9.5-9C1 9 2.5 5 6 5c2 0 3.5 1 5 3 1.5-2 3-3 5-3 3.5 0 5 4 3.5 7C19 16.5 12 21 12 21z"/></svg>),
  radiation: (<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3" /><path d="M12 2v5M12 17v5M2 12h5M17 12h5M5 5l3.5 3.5M15.5 15.5L19 19M19 5l-3.5 3.5M8.5 15.5L5 19" /></svg>),
  hand: (<svg viewBox="0 0 24 24"><path d="M7 11V6a1.5 1.5 0 013 0v5M10 10V4.5a1.5 1.5 0 013 0V10M13 10V5a1.5 1.5 0 013 0v6M16 11V7a1.5 1.5 0 013 0v8c0 4-3 7-7 7h-2c-3.5 0-6-2-7-5l-2-4 2-1 2 3V9a1.5 1.5 0 013 0v3.5"/></svg>),
  bone: (<svg viewBox="0 0 24 24"><path d="M5 8c-1.5 0-3 1-3 2.5C2 11.5 3 12 3.5 12 2.5 12.5 2 13.5 2 14.5 2 16 3.5 17 5 16L19 8c1-1.5 0-3.5-1.5-3.5-.5 0-1 .2-1.5.5L5 8z"/><path d="M19 16c1.5 0 3-1 3-2.5 0-1-1-1.5-1.5-1.5 1-.5 1.5-1.5 1.5-2.5C22 8 20.5 7 19 8L5 16c-1 1.5 0 3.5 1.5 3.5.5 0 1-.2 1.5-.5L19 16z"/></svg>),
  bolt: (<svg viewBox="0 0 24 24" fill="currentColor"><path d="M13 2L4 14h6l-1 8 9-12h-6l1-8z"/></svg>),
  alert: (<svg viewBox="0 0 24 24"><path d="M12 3L2 20h20L12 3z" /><path d="M12 10v5" /><circle cx="12" cy="18" r="0.5" fill="currentColor" /></svg>),
  empty: (<svg viewBox="0 0 24 24"><circle cx="11" cy="11" r="7" /><path d="M21 21l-4.3-4.3" /><path d="M8 11h6" /></svg>),
  cross: (<svg viewBox="0 0 24 24" strokeWidth="2.5"><path d="M6 6l12 12M18 6L6 18" /></svg>),
  hammer: (<svg viewBox="0 0 24 24"><path d="M12 3l3 3-8 8-3-3 8-8z" /><path d="M4 14l6 6-3 3-3-3-3-3 3-3z" /></svg>),
  globe: (<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="9" /><path d="M3 12h18M12 3c3 3 3 15 0 18M12 3c-3 3-3 15 0 18" /></svg>),
};
function Icon({ name, className = '' }) {
  return <span className={`icon ${className}`}>{ICONS[name] || ICONS.tool}</span>;
}

// ===== 样式常量 =====
const confirmOverlayStyle = {
  position: 'fixed', inset: 0, zIndex: 1000,
  background: 'rgba(0,0,0,0.6)',
  display: 'flex', alignItems: 'center', justifyContent: 'center',
  backdropFilter: 'blur(2px)',
};
const confirmBoxStyle = {
  background: 'linear-gradient(180deg, var(--bg-2), var(--bg-1))',
  border: '1px solid var(--border-1)',
  borderRadius: 6, padding: 24,
  width: 420, maxWidth: '90vw',
  boxShadow: '0 20px 60px rgba(0,0,0,0.5), 0 0 40px rgba(196,90,58,0.1)',
};
const btnPrimaryStyle = {
  padding: '7px 18px',
  background: 'linear-gradient(135deg, var(--accent-rust), var(--accent-rust-dim))',
  border: '1px solid var(--accent-rust)',
  borderRadius: 3, color: '#fff', cursor: 'pointer',
  fontSize: 12, fontWeight: 500,
  boxShadow: '0 2px 6px rgba(196,90,58,0.3)',
  fontFamily: 'var(--sans)',
};
const btnSecondaryStyle = {
  padding: '7px 18px',
  background: 'var(--bg-2)',
  border: '1px solid var(--border-1)',
  borderRadius: 3, color: 'var(--text-1)', cursor: 'pointer',
  fontSize: 12, fontFamily: 'var(--sans)',
};
const btnGhostStyle = {
  padding: '4px 12px',
  background: 'transparent',
  border: '1px solid var(--border-1)',
  borderRadius: 2, color: 'var(--text-2)', cursor: 'pointer',
  fontSize: 11, fontFamily: 'var(--sans)',
};
const inputStyle = {
  width: '100%', height: 36, padding: '0 10px',
  background: 'var(--bg-0)', border: '1px solid var(--border-1)',
  borderRadius: 3, color: 'var(--text-0)',
  fontSize: 12, fontFamily: 'var(--sans)',
  outline: 'none',
};
const labelStyle = {
  display: 'block', color: 'var(--text-2)', fontSize: 11,
  marginBottom: 5, letterSpacing: 0.5,
};
const panelStyle = {
  background: 'linear-gradient(180deg, var(--bg-1), var(--bg-2))',
  border: '1px solid var(--border-0)',
  borderRadius: 5,
  padding: 18,
  boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
};
// ========== 片段 1 结束 ==========
// ===== COPY BUTTON =====
function CopyButton({ text, label = '复制', small = false }) {
  const [copied, setCopied] = useState(false);
  const lang = useLang();
  const str = window.UI_STRINGS[lang];
  const handleClick = (e) => {
    e.stopPropagation();
    navigator.clipboard.writeText(text);
    setCopied(true);
    setTimeout(() => setCopied(false), 1500);
  };
  return (
    <button
      className={`copy-btn ${copied ? 'copied' : ''} ${small ? 'copy-btn-sm' : ''}`}
      onClick={handleClick}
      title={label}
    >
      <Icon name={copied ? 'check' : 'copy'} />
      {copied ? str.copied : label}
    </button>
  );
}

// ===== ITEM CARD =====
function ItemCard({ item, category, isExpanded, onToggle }) {
  const lang = useLang();
  const str = window.UI_STRINGS[lang];
  const itemName = lang === 'zh' ? item.nameZh : lang === 'en' ? item.nameEn : item.nameRu || item.nameEn;
  let catName, catNameOther;
  if (lang === 'zh') {
    catName = category.name;
    catNameOther = category.nameEn;
  } else if (lang === 'en') {
    catName = category.nameEn;
    catNameOther = category.name;
  } else {
    catName = category.nameRu || category.nameEn;
    catNameOther = category.nameEn;
  }
  return (
    <div className={`item-card ${isExpanded ? 'expanded' : ''}`} onClick={onToggle}>
      <div className="side-bar"></div>
      <div className="item-top">
        <div>
          <div className="item-name">{itemName}</div>
          <div className="item-cat-tag-inline"><span style={{ fontSize: 10 }}>◆</span>{catName}</div>
        </div>
        <div className="item-id">{str.idLabel} {item.id}</div>
      </div>
      <div className="item-bottom">
        <div className="item-cat"><span className="dot"></span>{catName} · {catNameOther}</div>
        <CopyButton text={String(item.id)} label={str.copyId} small />
      </div>
      <div className="item-detail">
        <div className="detail-row"><span className="k">{str.itemId}</span><span className="v">{item.id}</span></div>
        <div className="detail-row"><span className="k">{str.itemNameEn}</span><span className="v" style={{ fontFamily: 'var(--sans)', fontWeight: 400 }}>{item.nameEn}</span></div>
        <div className="detail-row"><span className="k">{str.category}</span><span className="v" style={{ fontFamily: 'var(--sans)', fontWeight: 400 }}>{catName} ({catNameOther})</span></div>
        <div className="give-command"><span className="cmd">{str.giveCommand} {item.id}</span><CopyButton text={`/give ${item.id}`} label={str.copyCmd} small /></div>
      </div>
    </div>
  );
}

// ===== ITEMS PAGE =====
function ItemsPage({ category, searchQuery }) {
  const [expandedId, setExpandedId] = useState(null);
  const lang = useLang();
  const str = window.UI_STRINGS[lang];
  const safeQuery = searchQuery ?? '';
  const filteredItems = useMemo(() => {
    if (!safeQuery.trim()) return category.items;
    const q = safeQuery.toLowerCase();
    return category.items.filter(item =>
      item.nameEn.toLowerCase().includes(q) ||
      item.nameZh.toLowerCase().includes(q) ||
      (item.nameRu && item.nameRu.toLowerCase().includes(q)) ||
      String(item.id).includes(q)
    );
  }, [category, safeQuery]);
  let titleName, subName;
  if (lang === 'zh') { titleName = category.name; subName = category.nameEn; }
  else if (lang === 'en') { titleName = category.nameEn; subName = category.name; }
  else { titleName = category.nameRu || category.nameEn; subName = category.nameEn; }
  return (
    <div>
      <div className="page-header">
        <div className="page-title-wrap">
          <div className="page-icon"><Icon name={category.icon} /></div>
          <div className="page-title"><h2>{titleName}</h2><div className="sub">{subName}</div></div>
        </div>
        <div className="page-meta">
          <div className="meta-chip"><span className="key">{str.itemsItems}</span><span className="val">{filteredItems.length}</span></div>
          <div className="meta-chip"><span className="key">{str.idRange}</span><span className="val">{category.items.length > 0 ? `${category.items[0].id}–${category.items[category.items.length-1].id}` : '—'}</span></div>
        </div>
      </div>
      {filteredItems.length === 0 ? (
        <div className="empty-state"><div className="empty-icon"><Icon name="empty" /></div><h3>{str.noResults}</h3><p>{str.noResultsDesc}</p></div>
      ) : (
        <div className="item-grid">
          {filteredItems.map(item => (
            <ItemCard key={`${category.id}-${item.id}`} item={item} category={category}
              isExpanded={expandedId === item.id}
              onToggle={() => setExpandedId(expandedId === item.id ? null : item.id)}
            />
          ))}
        </div>
      )}
    </div>
  );
}

// ===== SEARCH RESULTS PAGE =====
function SearchResultsPage({ query, categories }) {
  const [expandedId, setExpandedId] = useState(null);
  const lang = useLang();
  const str = window.UI_STRINGS[lang];
  const results = useMemo(() => {
    const q = (query ?? '').toLowerCase().trim();
    if (!q) return [];
    const all = [];
    categories.forEach(cat => {
      cat.items.forEach(item => {
        if (
          item.nameEn.toLowerCase().includes(q) ||
          item.nameZh.toLowerCase().includes(q) ||
          (item.nameRu && item.nameRu.toLowerCase().includes(q)) ||
          String(item.id).includes(q)
        ) {
          all.push({ ...item, category: cat });
        }
      });
    });
    const crafting = window.CRAFTING_DATA;
    crafting.categories.forEach(cat => {
      cat.recipes.forEach(r => {
        const rName = lang === 'zh' ? r.resultZh : r.resultEn;
        if (rName.toLowerCase().includes(q)) {
          all.push({
            id: `craft-${cat.id}-${r.resultEn}`,
            nameZh: r.resultZh,
            nameEn: r.resultEn,
            category: { id: 'craft-search', name: str.crafting, nameEn: str.craftingEn, icon: 'craft' },
            isCraft: true,
            recipe: r,
            craftCat: cat,
          });
        }
      });
    });
    return all;
  }, [query, categories, lang, str]);

  function CraftCard({ recipe, category, compact }) {
    const lang = useLang();
    const str = window.UI_STRINGS[lang];
    if (!recipe) return null;
    const resultName = lang === 'zh' ? recipe.resultZh : recipe.resultEn;
    const materialList = recipe.materials.map(m => `${lang === 'zh' ? m.nameZh : m.nameEn} ×${m.count}`).join('\n');
    const craftLine = `${materialList}\n👉 ${resultName} ×${recipe.resultCount}`;
    return (
      <div className="item-card" style={{ borderLeft: '3px solid var(--accent-mustard)' }}>
        <div className="item-top"><div className="item-name">{resultName}</div></div>
        <div className="item-bottom"><span>{str.craftingRecipe}</span></div>
        <div className="item-detail">
          <div>{str.materialsLabel}: {recipe.materials.map(m => lang === 'zh' ? m.nameZh : m.nameEn).join(', ')}</div>
          <div>{str.quantityLabel}: {recipe.resultCount || 1}</div>
          <CopyButton text={craftLine} label={str.copyRecipe} small />
        </div>
      </div>
    );
  }

  return (
    <div>
      <div className="page-header">
        <div className="page-title-wrap">
          <div className="page-icon"><Icon name="search" /></div>
          <div className="page-title"><h2>{str.searchResults}</h2><div className="sub">{str.searchResultsFor} "{query ?? ''}"</div></div>
        </div>
        <div className="page-meta"><div className="meta-chip"><span className="key">{str.found}</span><span className="val">{results.length}</span></div></div>
      </div>
      {results.length === 0 ? (
        <div className="empty-state"><div className="empty-icon"><Icon name="empty" /></div><h3>{str.noResults}</h3><p>{str.noResultsDesc}</p></div>
      ) : (
        <div className="item-grid">
          {results.map((item, idx) => item.isCraft ? (
            <CraftCard key={`craft-${idx}`} recipe={item.recipe} category={item.craftCat} compact />
          ) : (
            <ItemCard key={`search-${item.category.id}-${item.id}`} item={item} category={item.category}
              isExpanded={expandedId === `${item.category.id}-${item.id}`}
              onToggle={() => setExpandedId(expandedId === `${item.category.id}-${item.id}` ? null : `${item.category.id}-${item.id}`)}
            />
          ))}
        </div>
      )}
    </div>
  );
}

// ===== HOME PAGE =====
function HomePage() {
  const lang = useLang();
  const str = window.UI_STRINGS[lang];
  const [showWelcome, setShowWelcome] = useState(true);
  const closeWelcome = () => setShowWelcome(false);
  return (
    <div className="home-page">
      {showWelcome && (
        <div style={{ position:'fixed', inset:0, zIndex:9999, background:'rgba(0,0,0,0.6)', display:'flex', alignItems:'center', justifyContent:'center', backdropFilter:'blur(4px)' }} onClick={closeWelcome}>
          <div style={{ background:'linear-gradient(180deg, var(--bg-2), var(--bg-1))', border:'1px solid rgba(212,168,74,0.3)', borderRadius:12, padding:'40px 48px', maxWidth:420, width:'90%', textAlign:'center', boxShadow:'0 20px 60px rgba(0,0,0,0.5), 0 0 40px rgba(212,168,74,0.08)' }} onClick={e => e.stopPropagation()}>
            <div style={{ fontSize:48, marginBottom:12 }}>🎮</div>
            <h2 style={{ color:'var(--text-0)', fontSize:20, marginBottom:12 }}>{str.welcomeTitle}</h2>
            <p style={{ color:'var(--text-1)', fontSize:15, lineHeight:1.6, marginBottom:20 }}>{str.welcomeDefault}</p>
            <button onClick={closeWelcome} style={{ padding:'10px 32px', background:'linear-gradient(135deg, var(--accent-rust), var(--accent-rust-dim))', border:'1px solid var(--accent-rust)', borderRadius:6, color:'#fff', cursor:'pointer', fontSize:14, fontWeight:500, fontFamily:'var(--sans)' }}>{str.welcomeButton}</button>
          </div>
        </div>
      )}
      <div className="page-header">
        <div className="page-title-wrap">
          <div className="page-icon"><Icon name="obuild" /></div>
          <div className="page-title"><h2>Erythros Lite Wiki</h2><div className="sub">{str.wikiSubtitle}</div></div>
        </div>
      </div>
      <div className="data-section">
        <div className="home-desc-block">
          <p>{str.homeIntro}</p>
          <div className="home-meta-row"><p>{str.wikiBy}</p><p>Version v26.02.10</p></div>
          <hr style={{ margin:"28px 0", opacity:0.22 }} />
          <div style={{ border:'1px solid rgba(212,168,74,0.3)', borderRadius:8, padding:'28px 24px', margin:'16px 0 28px', background:'linear-gradient(135deg, rgba(212,168,74,0.06), rgba(196,90,58,0.04))', position:'relative', overflow:'hidden' }}>
            <div style={{ display:'flex', alignItems:'center', gap:12, marginBottom:16 }}><span style={{ fontSize:22, color:'var(--accent-mustard)' }}>🎮</span><span style={{ fontSize:14, fontWeight:700, letterSpacing:2, color:'var(--accent-mustard)', textTransform:'uppercase' }}>{str.gameCredits}</span></div>
            <div style={{ display:'grid', gridTemplateColumns:'auto 1fr', gap:'6px 20px', fontSize:13, color:'var(--text-1)', lineHeight:1.8 }}>
              <span style={{ color:'var(--text-2)', fontWeight:500 }}>{str.creditTitle}</span><span style={{ color:'var(--text-0)', fontWeight:600 }}>Erythros Lite</span>
              <span style={{ color:'var(--text-2)', fontWeight:500 }}>{str.creditCreator}</span><span style={{ color:'var(--text-0)' }}>Vladyslav Pavliv <span style={{ fontSize:11, color:'var(--text-3)', marginLeft:8 }}>{str.creditCreatorDesc}</span></span>
              <span style={{ color:'var(--text-2)', fontWeight:500 }}>{str.creditPublisher}</span><span style={{ color:'var(--accent-mustard)', fontWeight:600 }}>keyneed <span style={{ fontSize:11, color:'var(--text-3)', marginLeft:8, fontWeight:400 }}>{str.creditPublisherDesc}</span></span>
              <span style={{ color:'var(--text-2)', fontWeight:500 }}>{str.creditWebsite}</span><span><a href="https://keyneed.org" target="_blank" rel="noopener noreferrer" style={{ color:'var(--accent-rust)', textDecoration:'none', borderBottom:'1px solid transparent' }} onMouseEnter={e => e.target.style.borderBottomColor='var(--accent-rust)'} onMouseLeave={e => e.target.style.borderBottomColor='transparent'}>keyneed.org</a> <span style={{ fontSize:11, color:'var(--text-3)', marginLeft:8 }}>↗</span></span>
              <span style={{ color:'var(--text-2)', fontWeight:500 }}>{str.creditEmail}</span><span style={{ color:'var(--text-1)' }}>vlad@essential-key.com <span style={{ fontSize:11, color:'var(--text-3)', marginLeft:8 }}>{str.creditEmailDesc}</span></span>
              <span style={{ color:'var(--text-2)', fontWeight:500 }}>{str.creditCommunity}</span><span style={{ color:'var(--accent-green-light)', fontWeight:500 }}>PTL 中国 <span style={{ fontSize:11, color:'var(--text-3)', marginLeft:8, fontWeight:400 }}>{str.creditCommunityDesc}</span></span>
            </div>
            <div style={{ marginTop:18, paddingTop:16, borderTop:'1px solid rgba(255,255,255,0.06)' }}><p style={{ fontSize:11, color:'var(--text-3)', margin:0, lineHeight:1.6, fontFamily:'var(--mono)' }}>{str.wikiDisclaimer}</p></div>
          </div>
          <hr style={{ margin:"28px 0", opacity:0.22 }} />
          <div><h3 style={{ margin:"0 0 10px" }}>{str.wikiContents}</h3><ul style={{ lineHeight:"1.7", opacity:0.82, paddingLeft:"20px" }}>
            <li>{str.wikiContent1}</li>
            <li>{str.wikiContent2}</li>
            <li>{str.wikiContent3}</li>
            <li>{str.wikiContent4}</li>
          </ul></div>
          <hr style={{ margin:"28px 0", opacity:0.22 }} />
          <div style={{ opacity:0.6, fontSize:"0.85em" }}><p>{str.wikiTip}</p></div>
        </div>
      </div>
    </div>
  );
}
// ========== 片段 2 结束 ==========
// ===== MAPS PAGE =====
function MapsPage() {
  const maps = window.MAPS_DATA;
  const lang = useLang();
  const str = window.UI_STRINGS[lang];
  return (
    <div>
      <div className="page-header">
        <div className="page-title-wrap"><div className="page-icon"><Icon name="map" /></div><div className="page-title"><h2>{str.mapSizes}</h2><div className="sub">{str.mapSizeSpec}</div></div></div>
        <div className="page-meta"><div className="meta-chip"><span className="key">TIERS</span><span className="val">{maps.length}</span></div></div>
      </div>
      <div className="data-section">
        <div className="data-section-title"><span className="bar"></span><h3>{str.sizeTiers}</h3><span className="en">Size Tiers</span></div>
        <div className="map-grid">
          {maps.map((m, i) => {
            const sizeMap = { '256×256':28, '512×512':36, '1024×1024':46, '2048×2048':52, '4096×4096':56 };
            const blkSize = sizeMap[m.size] || 40;
            return (
              <div key={i} className={`map-card ${m.permissionZh.includes('Premium') ? 'premium' : ''}`}>
                <div className="tier-badge">{m.tier}</div>
                <div className="map-size">{m.size}</div>
                <div className="map-size-visual"><div className="map-size-block" style={{ width:`${blkSize}px`, height:`${blkSize}px` }}></div><div className="map-size-label">{str.scaleLabel}</div></div>
                <div className="map-perm">{lang === 'zh' ? m.permissionZh : m.permissionEn}</div>
                <div className="map-desc">{lang === 'zh' ? m.descZh : lang === 'en' ? m.descEn : m.descRu || m.descEn}</div>
              </div>
            );
          })}
        </div>
      </div>
      <div className="data-section">
        <div className="data-section-title"><span className="bar"></span><h3>{str.notes}</h3><span className="en">Notes</span></div>
        <div className="craft-note">
          {lang === 'zh' ? (
            <><strong>{str.normalPlayers}</strong> {str.mapPermNormal}<br /><strong>{str.premiumOnly}</strong> {str.mapPermPremium}<br />{str.insaneNote}</>
          ) : (
            <><strong>{str.normalPlayers}</strong> {str.mapPermNormalEn}<br /><strong>{str.premiumOnly}</strong> {str.mapPermPremiumEn}<br />{str.insaneNoteEn}</>
          )}
        </div>
      </div>
    </div>
  );
}

// ===== MEDICINE PAGE =====
function MedicinePage() {
  const medicineData = window.MEDICINE_DATA ?? [];
  const medCategory = window.ITEM_CATEGORIES?.find(c => c.id === 'medicine');
  const [filter, setFilter] = useState('all');
  const lang = useLang();
  const str = window.UI_STRINGS[lang];
  const filtered = useMemo(() => filter === 'all' ? medicineData : medicineData.filter(m => m.tier === filter), [medicineData, filter]);
  const filterOptions = [
    { key:'all', labelZh:str.filterAll, labelEn:str.filterAll },
    { key:'high', labelZh:str.filterHigh, labelEn:str.filterHigh },
    { key:'mid', labelZh:str.filterMid, labelEn:str.filterMid },
    { key:'low', labelZh:str.filterLow, labelEn:str.filterLow },
    { key:'berry', labelZh:str.filterBerry, labelEn:str.filterBerry },
    { key:'special', labelZh:str.filterSpecial, labelEn:str.filterSpecial },
  ];
  const tierLabels = { zh:{ high:'高级药品', mid:'中级药品', low:'初级药品', berry:'浆果类', special:'特殊' }, en:{ high:'Advanced', mid:'Intermediate', low:'Basic', berry:'Berry', special:'Special' }, ru:{ high:'Высокий', mid:'Средний', low:'Низкий', berry:'Ягоды', special:'Особый' } };
  const findItemId = (nameEn) => medCategory?.items?.find(i => i.nameEn.toLowerCase() === nameEn.toLowerCase())?.id;
  const effectLabel = (eff) => { if(eff === 'bleed') return str.stopsBleed; if(eff === 'brokenLeg') return str.healsBrokenLeg; if(eff === 'stamina') return str.fullStamina; return eff; };
  const tipCards = [
    { title:str.tipWarning, text:str.tipWarningText },
    { title:str.tipHighValue, text:str.tipHighValueText },
    { title:str.tipEmergency, text:str.tipEmergencyText },
    { title:str.tipBerry, text:str.tipBerryText },
    { title:str.tipVodka, text:str.tipVodkaText },
    { title:str.tipCombat, text:str.tipCombatText },
  ];
  const legendItems = [
    { emoji:'❤️', color:'#e86060', labelZh:str.health, labelEn:str.health, descZh:str.healthDesc, descEn:str.healthDesc },
    { emoji:'☢️', color:'#c8a840', labelZh:str.radiation, labelEn:str.radiation, descZh:str.radiationDesc, descEn:str.radiationDesc },
    { emoji:'🖐️', color:'#5a9a55', labelZh:str.bleed, labelEn:str.bleed, descZh:str.bleedDesc, descEn:str.bleedDesc },
    { emoji:'🦴', color:'#d4a84a', labelZh:str.brokenLeg, labelEn:str.brokenLeg, descZh:str.brokenLegDesc, descEn:str.brokenLegDesc },
    { emoji:'⚡', color:'#f0c040', labelZh:str.stamina, labelEn:str.stamina, descZh:str.staminaDesc, descEn:str.staminaDesc },
  ];
  return (
    <div>
      <div className="page-header">
        <div className="page-title-wrap"><div className="page-icon"><Icon name="med" /></div><div className="page-title"><h2>{str.medicineEffects}</h2><div className="sub">{str.medicineGuide}</div></div></div>
        <div className="page-meta"><div className="meta-chip"><span className="key">{str.medItems}</span><span className="val">{medicineData.length}</span></div></div>
      </div>
      <div className="data-section">
        <div className="data-section-title"><span className="bar"></span><h3>{str.iconLegend}</h3><span className="en">Icon Legend</span></div>
        <div className="med-legend">
          {legendItems.map((li, i) => (
            <div key={i} className="med-legend-item">
              <div className="med-legend-icon" style={{ color:li.color }}><span className="emoji-icon">{li.emoji}</span></div>
              <div><div className="med-legend-label">{lang === 'zh' ? li.labelZh : li.labelEn}</div><div className="med-legend-desc">{lang === 'zh' ? li.descZh : li.descEn}</div></div>
            </div>
          ))}
        </div>
      </div>
      <div className="med-filter-row">
        {filterOptions.map(opt => (
          <button key={opt.key} className={`med-filter-btn ${filter === opt.key ? 'active' : ''}`} onClick={() => setFilter(opt.key)}>
            {lang === 'zh' ? opt.labelZh : opt.labelEn}
          </button>
        ))}
      </div>
      <div className="med-card-grid">
        {filtered.map((med, i) => {
          const itemId = findItemId(med.nameEn);
          const healthPct = Math.min(100, Math.abs(med.health));
          const radPct = Math.min(100, Math.abs(med.radiation) * 2);
          const tierLabel = tierLabels[lang][med.tier] || med.tier;
          const safeEffects = med.effects ?? [];
          return (
            <div key={i} className={`med-card tier-${med.tier}`}>
              <div className="med-tier-label" style={{ color: med.tier === 'high' ? 'var(--accent-rust)' : med.tier === 'mid' ? 'var(--accent-mustard)' : med.tier === 'low' ? 'var(--accent-green)' : med.tier === 'berry' ? '#c890d8' : '#5ab8d8', borderColor:'currentColor', background:'rgba(255,255,255,0.04)' }}>{tierLabel}</div>
              <div className="med-card-header"><div className="med-card-name">{lang === 'zh' ? med.nameZh : med.nameEn}</div>{itemId !== undefined && <div className="med-card-id"><span>{str.idLabel} {itemId}</span></div>}</div>
              <div className="med-card-stats">
                <div className="med-stat" style={{ '--progress-color': med.health > 0 ? '#e86060' : '#555' }}>
                  <div className="med-stat-icon"><span className="emoji-icon">❤️</span></div>
                  <div className="med-stat-value" style={{ color: med.health > 0 ? '#e88a8a' : '#888' }}>{med.health > 0 ? `+${med.health}%` : med.health < 0 ? `${med.health}%` : '—'}</div>
                  <div className="med-stat-bar"><div className="med-stat-bar-fill" style={{ width:`${healthPct}%`, background:'linear-gradient(90deg, #c45a3a, #e88a8a)', boxShadow:'0 0 6px rgba(196,90,58,0.5)' }}></div></div>
                  <div className="med-stat-label">{str.health}</div>
                </div>
                <div className="med-stat" style={{ '--progress-color': med.radiation > 0 ? '#d4a84a' : med.radiation < 0 ? '#5a9a55' : '#555' }}>
                  <div className="med-stat-icon"><span className="emoji-icon">☢️</span></div>
                  <div className="med-stat-value" style={{ color: med.radiation > 0 ? '#d4b060' : med.radiation < 0 ? '#5a9a55' : '#888' }}>{med.radiation > 0 ? `+${med.radiation}%` : med.radiation < 0 ? `${med.radiation}%` : '—'}</div>
                  <div className="med-stat-bar"><div className="med-stat-bar-fill" style={{ width:`${radPct}%`, background: med.radiation >= 0 ? 'linear-gradient(90deg, #c49a2a, #e8c850)' : 'linear-gradient(90deg, #4a7a3a, #6aa048)', boxShadow: med.radiation >= 0 ? '0 0 6px rgba(212,168,74,0.4)' : '0 0 6px rgba(90,154,85,0.4)' }}></div></div>
                  <div className="med-stat-label">{str.radiation}</div>
                </div>
              </div>
              <div className="med-card-effects">
                {safeEffects.length === 0 ? <span className="med-effect-none">{str.noSpecialEffect}</span> : safeEffects.map((eff, j) => (
                  <span key={j} className={`med-effect-badge eff-${eff}`}><span className="effect-emoji">{eff === 'bleed' ? '🖐️' : eff === 'brokenLeg' ? '🦴' : '⚡'}</span>{effectLabel(eff)}</span>
                ))}
              </div>
              {itemId !== undefined && <div className="med-card-give"><span className="give-cmd-inline">{str.giveCommand} {itemId}</span><CopyButton text={`/give ${itemId}`} label={str.copyCmd} small /></div>}
            </div>
          );
        })}
      </div>
      <div className="data-section" style={{ marginTop:32 }}>
        <div className="data-section-title"><span className="bar"></span><h3>{str.medicationTips}</h3><span className="en">Medication Tips</span></div>
        <div className="tip-grid">
          {tipCards.map((tip, i) => <div key={i} className="tip-card"><div className="tip-title">{tip.title}</div><div className="tip-text">{tip.text}</div></div>)}
        </div>
      </div>
    </div>
  );
}

// ===== BUILDING DURABILITY PAGE =====
function BuildingDurabilityPage() {
  const data = window.DURABILITY_DATA;
  const lang = useLang();
  const str = window.UI_STRINGS[lang];
  const maxHp = Math.max(...(data?.buildings ?? []).map(b => b.hp));
  return (
    <div>
      <div className="page-header">
        <div className="page-title-wrap"><div className="page-icon"><Icon name="dura" /></div><div className="page-title"><h2>{str.buildingDurability}</h2><div className="sub">{str.buildingDurabilityData}</div></div></div>
        <div className="page-meta">
          <div className="meta-chip"><span className="key">{str.buildingsLabel}</span><span className="val">{(data?.buildings ?? []).length}</span></div>
          <div className="meta-chip"><span className="key">{str.testWeapons}</span><span className="val">{(data?.weapons ?? []).length}</span></div>
        </div>
      </div>
      <div className="data-section">
        <div className="data-section-title"><span className="bar"></span><h3>{str.testWeapons}</h3><span className="en">Test Weapons</span></div>
        <div className="weapon-row">
          {(data?.weapons ?? []).map(w => (
            <div key={w.key} className="weapon-chip"><div className="weapon-name">{lang === 'zh' ? w.nameZh : w.nameEn}</div><div className="weapon-dmg">{str.damage} <b>{w.damage}</b></div></div>
          ))}
        </div>
      </div>
      <div className="data-section">
        <div className="data-section-title"><span className="bar"></span><h3>{str.hitsToDestroy}</h3><span className="en">Hits to Destroy</span></div>
        <div className="dura-table-wrap">
          <table className="dura-table-full">
            <thead><tr><th className="col-building">{str.buildingType}</th><th className="col-hp">{str.hp}</th>{(data?.weapons ?? []).map(w => <th key={w.key} className="col-weapon"><div>{w.shortName}</div><div className="th-sub">{w.damage} {str.damage}</div></th>)}</tr></thead>
            <tbody>
              {(data?.buildings ?? []).map(b => (
                <tr key={b.key}>
                  <td><div className="building-cell"><div className="building-name">{lang === 'zh' ? b.nameZh : b.nameEn}</div><div className="building-en">{lang === 'zh' ? b.nameEn : b.nameZh}</div></div></td>
                  <td className="hp-cell"><div className="hp-bar-wrap"><div className="hp-bar"><div className="hp-bar-fill" style={{ width:`${(b.hp / maxHp) * 100}%`, background: b.hp >= 3000 ? 'linear-gradient(90deg, #8a9a7a, #b0c090)' : b.hp >= 1000 ? 'linear-gradient(90deg, #c49a4a, #e0b860)' : 'linear-gradient(90deg, #b07050, #d49060)' }}></div></div><span className="hp-num">{b.hp}</span></div></td>
                  {(data?.weapons ?? []).map(w => {
                    const hits = b.hits[w.key];
                    const isInvalid = hits === null;
                    return <td key={w.key} className={`hits-cell ${isInvalid ? 'invalid' : ''} ${hits && hits <= 10 ? 'hits-low' : hits && hits < 50 ? 'hits-mid' : hits && hits >= 100 ? 'hits-high' : 'hits-mid'}`}>
                      {isInvalid ? <span className="invalid-mark"><Icon name="cross" /><span className="invalid-text">{str.invalid}</span></span> : <span className="hits-num">{hits}</span>}
                    </td>;
                  })}
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      </div>
      <div className="data-section">
        <div className="data-section-title"><span className="bar"></span><h3>{str.practicalTips}</h3><span className="en">Tips &amp; Strategies</span></div>
        <div className="dura-tips">{(data?.notes ?? []).map((t, i) => <div key={i} className="dura-tip-item">{lang === 'zh' ? t.zh : t.en}</div>)}</div>
      </div>
    </div>
  );
}

// ===== CRAFTING RECIPE CARD =====
function CraftCard({ recipe, category, compact = false }) {
  if (!recipe) return null;
  const lang = useLang();
  const str = window.UI_STRINGS[lang];
  const resultName = lang === 'zh' ? recipe.resultZh : recipe.resultEn;
  const materialList = recipe.materials.map(m => `${lang === 'zh' ? m.nameZh : m.nameEn} ×${m.count}`).join('\n');
  const craftLine = `${materialList}\n👉 ${resultName} ×${recipe.resultCount}`;
  const levelStyle = (() => {
    switch(recipe.level) {
      case 1: return { background:'rgba(180,180,180,0.12)', border:'1px solid rgba(180,180,180,0.25)', color:'#cccccc' };
      case 2: return { background:'rgba(90,140,220,0.12)', border:'1px solid rgba(90,140,220,0.25)', color:'#73a8ed' };
      case 3: return { background:'rgba(110,190,90,0.12)', border:'1px solid rgba(110,190,90,0.25)', color:'#8cd478' };
      case 4: return { background:'rgba(180,100,210,0.12)', border:'1px solid rgba(180,100,210,0.28)', color:'#c488e8' };
      case 5: return { background:'rgba(210,80,80,0.14)', border:'1px solid rgba(210,80,80,0.30)', color:'#ff7777' };
      default: return { background:'rgba(255,255,255,0.06)', border:'1px solid rgba(255,255,255,0.12)', color:'var(--text-muted)' };
    }
  })();
  return (
    <div className={`craft-recipe-card ${compact ? 'compact' : ''}`} style={{ padding:'14px', gap:'12px', display:'flex', flexDirection:'column' }}>
      <div className="craft-recipe-header" style={{ display:'flex', justifyContent:'space-between', alignItems:'center', width:'100%' }}>
        <div className="craft-result" style={{ display:'flex', alignItems:'center', gap:'8px' }}>
          <span className="craft-result-name" style={{ fontWeight:600, fontSize:'1.05rem', color:'var(--text-main)' }}>{resultName}</span>
          <span className="craft-result-count" style={{ marginLeft:'8px', color:'var(--text-muted)', fontSize:'0.95rem' }}>×{recipe.resultCount}</span>
        </div>
        <div className="craft-level-badge" style={{ display:'flex', alignItems:'center', gap:'6px', borderRadius:'999px', padding:'6px 10px', fontSize:'0.85rem', ...levelStyle }}>
          <Icon name="hammer" /><span>{str.level}.{recipe.level}</span>
        </div>
      </div>
      <div className="craft-materials" style={{ display:'flex', flexDirection:'column', gap:'8px', paddingLeft:'4px', width:'100%' }}>
        {recipe.materials.map(mat => (
          <div key={`${mat.nameZh || mat.nameEn}-${mat.count}`} className="craft-material-item" style={{ display:'flex', gap:'8px', alignItems:'center', flexWrap:'nowrap' }}>
            <span className="craft-mat-count" style={{ minWidth:'30px', opacity:0.85, color:'var(--accent-mustard)', fontWeight:600, fontSize:'0.9rem' }}>{mat.count}×</span>
            <span className="craft-mat-name">{lang === 'zh' ? mat.nameZh : mat.nameEn}</span>
          </div>
        ))}
      </div>
      {!compact && <div className="craft-card-footer" style={{ marginTop:'4px' }}><CopyButton text={craftLine} label={str.copyRecipe} small /></div>}
    </div>
  );
}

// ===== CRAFTING PAGE =====
function CraftingPage({ searchQuery }) {
  const lang = useLang();
  const str = window.UI_STRINGS[lang];
  const safeQuery = searchQuery ?? '';
  const craftData = window.CRAFTING_DATA;
  const filteredCategories = useMemo(() => {
    if(!safeQuery.trim()) return craftData.categories;
    const q = safeQuery.toLowerCase();
    return craftData.categories.map(cat => {
      const filterRecipes = cat.recipes.filter(r => {
        const rn = lang === 'zh' ? r.resultZh : r.resultEn;
        if(rn.toLowerCase().includes(q)) return true;
        return r.materials.some(mat => {
          const mn = lang === 'zh' ? mat.nameZh : mat.nameEn;
          return mn.toLowerCase().includes(q);
        });
      });
      return { ...cat, recipes: filterRecipes };
    }).filter(cat => cat.recipes.length > 0);
  }, [safeQuery, lang]);
  return (
    <div>
      <div className="page-header">
        <div className="page-title-wrap"><div className="page-icon"><Icon name="craft" /></div><div className="page-title"><h2>{str.craftingRecipes}</h2><div className="sub">{str.craftingList}</div></div></div>
      </div>
      {filteredCategories.map(cat => (
        <div className="data-section" key={cat.nameZh || cat.nameEn}>
          <div className="data-section-title"><span className="bar"></span><h3>{lang === 'zh' ? cat.nameZh : cat.nameEn}</h3><span className="en">{lang === 'zh' ? cat.nameEn : cat.nameZh}</span></div>
          <div style={{ maxWidth:"1400px", margin:"0 auto", padding:"0 16px" }}>
            <div className="craft-recipe-grid">
              {cat.recipes.map(recipe => <CraftCard key={`${recipe.resultZh || recipe.resultEn}-${recipe.level}`} recipe={recipe} category={cat} />)}
            </div>
          </div>
        </div>
      ))}
    </div>
  );
}

// ============================================================
// ===== 社区功能完整组件 =====
// ============================================================

// ---- 帖子详情页（完整版，含乐观更新） ----
function PostDetailPage({ postId, onBack, onDelete, currentUser }) {
  const lang = useLang();
  const str = window.UI_STRINGS[lang];
  const [post, setPost] = useState(null);
  const [replies, setReplies] = useState([]);
  const [loading, setLoading] = useState(true);
  const [replyContent, setReplyContent] = useState('');
  const [submitting, setSubmitting] = useState(false);
  const [error, setError] = useState('');

  const loadPost = async () => {
    setLoading(true);
    try {
      const data = await window.api.getPost(postId);
      if (data) {
        setPost(data);
        setReplies(data.replies || []);
        await window.api.incrementViewCount(postId);
      } else setError(str.postNotFound || '帖子不存在');
    } catch (err) { setError(str.loadFailed || '加载失败'); }
    setLoading(false);
  };

  useEffect(() => { loadPost(); }, [postId]);

  const handleReplySubmit = async (e) => {
    e.preventDefault();
    if (!replyContent.trim() || !currentUser) return;
    setSubmitting(true);
    setError('');
    
    const newContent = replyContent.trim();
    
    try {
      const newReply = await window.api.createReply(postId, newContent);
      
      // 乐观更新：立即增加回复计数
      setPost(prev => ({
        ...prev,
        reply_count: (prev.reply_count || 0) + 1
      }));
      
      // 构造新回复对象并添加到列表
      const newReplyObj = {
        id: newReply?.id || Date.now(),
        content: newContent,
        author: {
          id: currentUser.id,
          game_id: currentUser.gameId || '匿名'
        },
        author_id: currentUser.id,
        created_at: new Date().toISOString()
      };
      setReplies(prev => [...prev, newReplyObj]);
      
      // 清空输入框
      setReplyContent('');
      
      // 延迟重新加载确保后端数据一致
      setTimeout(() => loadPost(), 500);
      
    } catch (err) {
      setError(err.message || '回复失败，请重试');
      await loadPost();
    }
    setSubmitting(false);
  };

  const handleDeletePost = async () => {
    if (!confirm(str.confirmDeletePost || '确认删除此帖子？')) return;
    try {
      await window.api.deletePost(postId);
      if (onDelete) onDelete(postId);
      onBack();
    } catch (err) { setError(err.message); }
  };

  const handleDeleteReply = async (replyId) => {
    if (!confirm(str.confirmDeleteReply || '确认删除此回复？')) return;
    try {
      await window.api.deleteReply(replyId);
      await loadPost();
    } catch (err) { setError(err.message); }
  };

  const canEditDelete = (authorId) => currentUser && (currentUser.id === authorId || currentUser.role === 'admin');

  if (loading) return <div style={{ textAlign:'center', padding:60, color:'var(--text-2)' }}>{str.loading || '加载中...'}</div>;
  if (error) return <div style={{ textAlign:'center', padding:60, color:'var(--danger)' }}>{error}</div>;
  if (!post) return <div style={{ textAlign:'center', padding:60, color:'var(--text-2)' }}>{str.postDeleted || '帖子已删除'}</div>;

  const authorName = post.author?.game_id || '匿名';
  return (
    <div>
      <div style={{ display:'flex', alignItems:'center', gap:12, marginBottom:16 }}>
        <button style={btnSecondaryStyle} onClick={onBack}>← {str.backToCommunity || '返回社区'}</button>
        <h2 style={{ color:'var(--text-0)', fontSize:18, margin:0, flex:1 }}>{post.title}</h2>
        <div style={{ fontSize:12, color:'var(--text-2)' }}>{str.viewCount || '浏览'}: {post.view_count || 0}</div>
      </div>
      <div style={panelStyle}>
        <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:8 }}>
          <div style={{ fontSize:12, color:'var(--text-2)' }}>{str.postedBy || '发布于'}: {authorName} · {new Date(post.created_at).toLocaleString()}</div>
          {canEditDelete(post.author_id) && <button onClick={handleDeletePost} style={{ ...btnGhostStyle, color:'var(--danger)' }}>{str.delete || '删除'}</button>}
        </div>
        <div style={{ whiteSpace:'pre-wrap', lineHeight:1.7, color:'var(--text-1)' }}>{post.content}</div>
      </div>
      <div style={{ marginTop:24 }}>
        <h4 style={{ color:'var(--text-0)', fontSize:15, marginBottom:12 }}>{str.reply || '回复'} ({replies.length})</h4>
        {replies.length === 0 ? <div style={{ color:'var(--text-3)', fontSize:12 }}>{str.noReplies || '还没有回复'}</div> : replies.map(reply => (
          <div key={reply.id} style={{ ...panelStyle, marginBottom:12, padding:14 }}>
            <div style={{ display:'flex', justifyContent:'space-between', alignItems:'center', marginBottom:6 }}>
              <div style={{ fontSize:12, color:'var(--text-2)' }}>{reply.author?.game_id || '匿名'} · {new Date(reply.created_at).toLocaleString()}</div>
              {canEditDelete(reply.author_id) && <button onClick={() => handleDeleteReply(reply.id)} style={{ ...btnGhostStyle, color:'var(--danger)', fontSize:10 }}>{str.delete || '删除'}</button>}
            </div>
            <div style={{ whiteSpace:'pre-wrap', color:'var(--text-1)' }}>{reply.content}</div>
          </div>
        ))}
      </div>
      {currentUser ? (
        <form onSubmit={handleReplySubmit} style={{ marginTop:20, display:'flex', flexDirection:'column', gap:10 }}>
          <textarea rows="3" style={{ ...inputStyle, height:80, padding:'10px', fontFamily:'var(--sans)' }} placeholder={str.writeReply || '写回复...'} value={replyContent} onChange={e => setReplyContent(e.target.value)} required />
          <div style={{ display:'flex', gap:10, justifyContent:'flex-end' }}>
            <button type="submit" style={btnPrimaryStyle} disabled={submitting || !replyContent.trim()}>{submitting ? str.loading || '发送中...' : str.reply || '回复'}</button>
          </div>
        </form>
      ) : <div style={{ textAlign:'center', padding:20, color:'var(--text-2)', fontSize:12 }}>{str.loginToReply || '请登录后回复'}</div>}
    </div>
  );
}

// ---- 发帖对话框 ----
function CreatePostDialog({ onClose, onSuccess, lang, currentUser }) {
  const str = window.UI_STRINGS[lang] || window.UI_STRINGS.zh;
  const [title, setTitle] = useState('');
  const [content, setContent] = useState('');
  const [category, setCategory] = useState('general');
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState('');

  const categories = [
    { key:'general', label:str.categoryGeneral || '综合' },
    { key:'guide', label:str.categoryGuide || '攻略' },
    { key:'help', label:str.categoryHelp || '求助' },
    { key:'discussion', label:str.categoryDiscussion || '讨论' },
    { key:'feedback', label:str.categoryFeedback || '反馈' },
  ];

  const handleSubmit = async (e) => {
    e.preventDefault();
    if (!title.trim() || !content.trim()) return;
    if (!currentUser) {
      setError('请先登录');
      return;
    }
    setLoading(true);
    try {
      await window.api.createPost(title, content, category, currentUser.id);
      onSuccess();
    } catch (err) {
      setError(err.message);
    }
    setLoading(false);
  };

  return (
    <div style={confirmOverlayStyle} onClick={onClose}>
      <div style={{ ...confirmBoxStyle, width:500 }} onClick={e => e.stopPropagation()}>
        <h3 style={{ margin:0, color:'var(--text-0)', fontSize:16, marginBottom:16 }}>{str.newPost || '发新帖'}</h3>
        <form onSubmit={handleSubmit} style={{ display:'flex', flexDirection:'column', gap:12 }}>
          <div><label style={labelStyle}>{str.postTitle || '标题'}</label><input type="text" style={inputStyle} value={title} onChange={e => setTitle(e.target.value)} required /></div>
          <div><label style={labelStyle}>{str.postCategory || '分类'}</label><select style={inputStyle} value={category} onChange={e => setCategory(e.target.value)}>{categories.map(c => <option key={c.key} value={c.key}>{c.label}</option>)}</select></div>
          <div><label style={labelStyle}>{str.postContent || '内容'}</label><textarea rows="6" style={{ ...inputStyle, height:120, padding:'10px', fontFamily:'var(--sans)' }} value={content} onChange={e => setContent(e.target.value)} required /></div>
          {error && <div style={{ color:'var(--danger)', fontSize:12 }}>{error}</div>}
          <div style={{ display:'flex', gap:10, justifyContent:'flex-end', marginTop:6 }}>
            <button type="button" style={btnSecondaryStyle} onClick={onClose}>{str.postCancel || '取消'}</button>
            <button type="submit" style={btnPrimaryStyle} disabled={loading}>{loading ? str.loading || '发布中...' : str.postSubmit || '发布'}</button>
          </div>
        </form>
      </div>
    </div>
  );
}

// ===== COMMUNITY PAGE =====
function CommunityPage() {
  const lang = useLang();
  const str = window.UI_STRINGS[lang];
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(true);
  const [page, setPage] = useState(1);
  const [category, setCategory] = useState('all');
  const [showCreateDialog, setShowCreateDialog] = useState(false);
  const [hasMore, setHasMore] = useState(false);
  const [currentUser, setCurrentUser] = useState(null);
  const [viewingPost, setViewingPost] = useState(null);

  useEffect(() => {
    const token = localStorage.getItem('player_token');
    if (token) {
      window.api.playerMe(token).then(data => {
        if (data.player) setCurrentUser(data.player);
      }).catch(() => {});
    }
  }, []);

  const loadPosts = async (reset = true) => {
    setLoading(true);
    try {
      const data = await window.api.getPosts({ category, page: reset ? 1 : page, limit: 20 });
      if (reset) {
        setPosts(data || []);
        setPage(1);
        setHasMore((data || []).length === 20);
      } else {
        setPosts(prev => [...prev, ...(data || [])]);
        setHasMore((data || []).length === 20);
      }
    } catch (err) { console.error('加载帖子失败:', err); }
    setLoading(false);
  };

  useEffect(() => { loadPosts(true); }, [category]);

  const loadMore = () => {
    if (!loading && hasMore) {
      setPage(prev => prev + 1);
      loadPosts(false);
    }
  };

  const handlePostDeleted = (postId) => setPosts(prev => prev.filter(p => p.id !== postId));

  if (viewingPost) {
    return <PostDetailPage postId={viewingPost} onBack={() => { setViewingPost(null); loadPosts(true); }} onDelete={handlePostDeleted} currentUser={currentUser} />;
  }

  const categories = [
    { key:'all', label: str.allLabel || '全部' },
    { key:'general', label: str.categoryGeneral || '综合' },
    { key:'guide', label: str.categoryGuide || '攻略' },
    { key:'help', label: str.categoryHelp || '求助' },
    { key:'discussion', label: str.categoryDiscussion || '讨论' },
    { key:'feedback', label: str.categoryFeedback || '反馈' },
  ];

  return (
    <div>
      <div className="page-header">
        <div className="page-title-wrap"><div className="page-icon"><Icon name="globe" /></div><div className="page-title"><h2>{str.communityTitle || '社区讨论'}</h2><div className="sub">{str.community || 'Community'}</div></div></div>
        <div className="page-meta">
          {currentUser ? <button style={btnPrimaryStyle} onClick={() => setShowCreateDialog(true)}>+ {str.newPost || '发新帖'}</button> : <span style={{ fontSize:12, color:'var(--text-2)' }}>{str.loginToPost || '登录后发帖'}</span>}
        </div>
      </div>
      <div className="med-filter-row" style={{ marginBottom:16 }}>
        {categories.map(cat => <button key={cat.key} className={`med-filter-btn ${category === cat.key ? 'active' : ''}`} onClick={() => setCategory(cat.key)}>{cat.label}</button>)}
      </div>
      {loading && posts.length === 0 ? <div style={{ textAlign:'center', padding:60, color:'var(--text-2)' }}>{str.loading || '加载中...'}</div> :
      posts.length === 0 ? <div className="empty-state"><div className="empty-icon"><Icon name="empty" /></div><h3>{str.noPosts || '暂无帖子'}</h3><p>{currentUser ? str.beFirstPost || '点击“发新帖”成为第一个！' : str.loginToPost || '请先登录再发帖'}</p></div> :
      <div className="item-grid" style={{ gridTemplateColumns:'1fr' }}>
        {posts.map(post => {
          const authorName = post.author?.game_id || '匿名';
          return (
            <div key={post.id} className="item-card" onClick={() => setViewingPost(post.id)} style={{ cursor:'pointer' }}>
              <div className="item-top" style={{ justifyContent:'space-between' }}>
                <div style={{ flex:1 }}><div className="item-name" style={{ fontSize:16 }}>{post.title}</div><div style={{ fontSize:12, color:'var(--text-2)', marginTop:4 }}>{str.category || '分类'}: {post.category}</div></div>
                <div style={{ textAlign:'right', fontSize:12, color:'var(--text-2)' }}><div>{str.viewCount || '浏览'}: {post.view_count || 0}</div><div>{str.replyCount || '回复'}: {post.reply_count || 0}</div></div>
              </div>
              <div className="item-bottom" style={{ marginTop:8 }}>
                <div className="item-cat"><span className="dot"></span>{str.postedBy || '发布于'}: {authorName} · {new Date(post.created_at).toLocaleString()}</div>
                {post.is_pinned && <span style={{ fontSize:11, color:'var(--accent-mustard)' }}>📌 {str.pinned || '置顶'}</span>}
              </div>
            </div>
          );
        })}
      </div>}
      {hasMore && !loading && <div style={{ textAlign:'center', marginTop:20 }}><button style={btnSecondaryStyle} onClick={loadMore}>{str.loadMore || '加载更多'}</button></div>}
      {showCreateDialog && (
        <CreatePostDialog 
          onClose={() => setShowCreateDialog(false)} 
          onSuccess={() => { setShowCreateDialog(false); loadPosts(true); }} 
          lang={lang} 
          currentUser={currentUser} 
        />
      )}
    </div>
  );
}
// ========== 片段 3 结束 ==========
// ===== SIDEBAR =====
function Sidebar({
  isOpen, onClose, activeCategory, onSelectCategory, activeDataPage, onSelectDataPage,
  categories, totalItems, player, onOpenPlayerAuth, onOpenAdmin,
}) {
  const lang = useLang();
  const str = window.UI_STRINGS[lang];
  const dataPages = [
    { key:'home', labelZh:str.homeLabel, labelEn:str.homeLabelEn, labelRu:str.homeLabelRu, icon:'obuild' },
    { key:'maps', labelZh:str.mapSizes, labelEn:str.mapSizesEn, labelRu:str.mapSizesRu, icon:'map' },
    { key:'medicine', labelZh:str.medicineEffects, labelEn:str.medicineEffectsEn, labelRu:str.medicineEffectsRu, icon:'med' },
    { key:'crafting', labelZh:str.craftingRecipes, labelEn:str.craftingRecipesEn, labelRu:str.craftingRecipesRu, icon:'craft' },
    { key:'durability', labelZh:str.buildingDurability, labelEn:str.buildingDurabilityEn, labelRu:str.buildingDurabilityRu, icon:'dura' },
    { key:'community', labelZh:str.community, labelEn:str.communityEn, labelRu:str.communityRu, icon:'globe' },
  ];
  const getCatLabel = (cat) => lang === 'zh' ? cat.name : (lang === 'en' ? cat.nameEn : cat.nameRu || cat.nameEn);
  const getDataLabel = (dp) => lang === 'zh' ? dp.labelZh : (lang === 'en' ? dp.labelEn : dp.labelRu || dp.labelEn);
  return (
    <>
      <div className={`sidebar-overlay ${isOpen ? 'show' : ''}`} onClick={onClose} />
      <aside className={`sidebar ${isOpen ? 'open' : ''}`}>
        <div className="sidebar-header">
          <div className="brand"><div className="brand-mark">E</div><div className="brand-text"><h1>Erythros Lite</h1><p>{str.wikiRef}</p></div></div>
        </div>
        <div className="sidebar-section-title">{str.itemCategories}</div>
        <nav className="sidebar-nav">
          {categories.map(cat => (
            <div key={cat.id} className={`nav-item ${activeCategory === cat.id ? 'active' : ''}`} onClick={() => { onSelectCategory(cat.id); onClose(); }}>
              <span className="nav-icon"><Icon name={cat.icon} /></span><span className="nav-label">{getCatLabel(cat)}</span><span className="nav-count">{cat.items.length}</span>
            </div>
          ))}
          <div className="sidebar-section-title" style={{ marginTop:8 }}>{str.gameData}</div>
          {dataPages.map(dp => (
            <div key={dp.key} className={`nav-item ${activeDataPage === dp.key ? 'active' : ''}`} onClick={() => { onSelectDataPage(dp.key); onClose(); }}>
              <span className="nav-icon"><Icon name={dp.icon} /></span><span className="nav-label">{getDataLabel(dp)}</span>
            </div>
          ))}
          <div className="sidebar-section-title" style={{ marginTop:8 }}>{str.accountLabel}</div>
          {player ? (
            <div className="nav-item" onClick={() => { onOpenPlayerAuth(); onClose(); }}>
              <span className="nav-icon">👤</span><span className="nav-label">{player.gameId || player.username || 'Игрок'}</span>
            </div>
          ) : (
            <div className="nav-item" onClick={() => { onOpenPlayerAuth(); onClose(); }}>
              <span className="nav-icon">🔑</span><span className="nav-label">{str.registerLogin}</span>
            </div>
          )}
          {player && player.role === 'admin' && (
            <div className={`nav-item ${activeDataPage === 'admin' ? 'active' : ''}`} onClick={() => { onOpenAdmin(); onClose(); }}>
              <span className="nav-icon">⚙️</span><span className="nav-label">{str.adminPanel}</span>
            </div>
          )}
        </nav>
        <div className="sidebar-footer">
          <div>{str.totalItemsLabel} <span className="ver">{totalItems}</span> {str.itemsUnit}</div>
          <div style={{ marginTop:3 }}>{WIKI_VERSION}</div>
        </div>
      </aside>
    </>
  );
}

// ===== LANGUAGE SWITCHER =====
function LangSwitcher({ lang, onChange }) {
  const str = window.UI_STRINGS[lang] || window.UI_STRINGS.zh;
  return (
    <div className="lang-switcher">
      <button className={`lang-btn ${lang === 'zh' ? 'active' : ''}`} onClick={() => onChange('zh')}>{str.langZh}</button>
      <span className="lang-sep">/</span>
      <button className={`lang-btn ${lang === 'en' ? 'active' : ''}`} onClick={() => onChange('en')}>{str.langEn}</button>
      <span className="lang-sep">/</span>
      <button className={`lang-btn ${lang === 'ru' ? 'active' : ''}`} onClick={() => onChange('ru')}>{str.langRu}</button>
    </div>
  );
}

// ===== PTL WATERMARK =====
function PtlWatermark() {
  const lang = useLang();
  const str = window.UI_STRINGS[lang];
  return (
    <div className="ptl-watermark">
      <div className="ptl-watermark-logo">
        <svg viewBox="0 0 120 40" width="90" height="30">
          <text x="55" y="28" textAnchor="middle" fontFamily="Arial Black, sans-serif" fontSize="22" fontWeight="900" fill="#d4a84a" letterSpacing="1">PTL</text>
          <text x="98" y="15" textAnchor="middle" fontFamily="Noto Sans SC, sans-serif" fontSize="10" fontWeight="700" fill="#d4a84a">中</text>
          <text x="98" y="28" textAnchor="middle" fontFamily="Noto Sans SC, sans-serif" fontSize="10" fontWeight="700" fill="#d4a84a">国</text>
        </svg>
      </div>
      <div className="ptl-watermark-text">
        <div className="ptl-wm-title">{str.ptlCommunity}</div>
        <div className="ptl-wm-sub">{str.ptlSubtitle}</div>
        <div className="ptl-wm-qq"><span>{str.ptlQQ1}</span><span className="qq-sep">·</span><span>{str.ptlQQ2}</span></div>
      </div>
    </div>
  );
}

// ============================================================
// ===== 完整多语言的 PLAYER AUTH MODAL（含“我的帖子”按钮） =====
// ============================================================
function PlayerAuthModal({ isOpen, onClose, onLoginSuccess, currentPlayer, onLogout, lang }) {
  const str = window.UI_STRINGS[lang] || window.UI_STRINGS.zh;
  const [mode, setMode] = useState('login');
  const [isEdit, setIsEdit] = useState(false);
  const [qq, setQq] = useState('');
  const [gameId, setGameId] = useState('');
  const [password, setPassword] = useState('');
  const [confirmPw, setConfirmPw] = useState('');
  const [externalContact, setExternalContact] = useState('');
  const [userRegionMode, setUserRegionMode] = useState('cn');
  const [error, setError] = useState('');
  const [success, setSuccess] = useState('');
  const [loading, setLoading] = useState(false);
  const [showIdConfirm, setShowIdConfirm] = useState(false);

  useEffect(() => {
    if (isOpen) {
      setError(''); setSuccess('');
      setIsEdit(false);
      setShowIdConfirm(false);
      setUserRegionMode('cn');
      setExternalContact('');
      if (currentPlayer) {
        setMode('profile');
        setQq(currentPlayer.qq || '');
        setGameId(currentPlayer.gameId || '');
        setExternalContact(currentPlayer.contactExternal || '');
      } else {
        setMode('login');
        setQq(''); setGameId(''); setPassword(''); setConfirmPw('');
      }
    }
  }, [isOpen, currentPlayer]);

  if (!isOpen) return null;

  const handleLogin = async (e) => {
    e.preventDefault();
    setError(''); setSuccess('');
    setLoading(true);
    const data = await window.api.playerLogin(qq, password);
    setLoading(false);
    if (data.success) {
      localStorage.setItem('player_token', data.token);
      onLoginSuccess(data.player);
      onClose();
    } else {
      setError(data.error || str.loginFailed || '登录失败');
    }
  };

  const onClickRegisterCheck = (e) => {
    e.preventDefault();
    setError('');
    if (!/^\d{6,12}$/.test(gameId)) {
      return setError(str.gameIdFormatError || '游戏ID只能填写6‑12位纯数字！');
    }
    if (password !== confirmPw) {
      return setError(str.passwordMismatch || '两次输入的密码不一致');
    }
    if (userRegionMode === 'cn') {
      if (!qq) return setError(str.qqRequired || '国内模式请填写QQ号');
    } else {
      if (!externalContact) return setError(str.contactRequired || '海外模式请填写Discord纯数字ID或者联系邮箱');
    }
    setShowIdConfirm(true);
  };

  const doRealRegister = async () => {
    setShowIdConfirm(false);
    setLoading(true);
    const registerPayload = {
      qq: userRegionMode === 'cn' ? qq : '',
      contactExternal: userRegionMode === 'intl' ? externalContact : '',
      gameId: gameId,
      password: password,
      regionMode: userRegionMode
    };
    const data = await window.api.playerRegister(registerPayload);
    setLoading(false);
    if (data.success) {
      if (data.needConfirmation) {
        setSuccess(data.message || '注册成功！请检查邮箱确认。');
      } else {
        localStorage.setItem('player_token', data.token);
        onLoginSuccess(data.player);
        onClose();
      }
    } else {
      setError(data.error || str.registerFailed || '注册失败');
    }
  };

  const handleUpdateProfile = async (e) => {
    e.preventDefault();
    setError(''); setSuccess('');
    setLoading(true);
    const payload = {};
    if (password) {
      if (!confirmPw) return setError(str.currentPasswordRequired || '请填写原密码');
      payload.current_password = confirmPw;
      payload.new_password = password;
    }
    const data = await window.api.playerUpdate(payload);
    setLoading(false);
    if (data.success) {
      setSuccess(str.passwordChanged || '密码修改成功！');
      onLoginSuccess(data.player);
      setPassword(''); setConfirmPw('');
      setIsEdit(false);
    } else {
      setError(data.error || str.updateFailed || '修改失败');
    }
  };

  const handleLogout = () => {
    localStorage.removeItem('player_token');
    onLogout();
    onClose();
  };

  const modalStyle = {
    position: 'fixed', inset: 0, zIndex: 1000,
    background: 'rgba(0,0,0,0.6)',
    display: 'flex', alignItems: 'center', justifyContent: 'center',
    backdropFilter: 'blur(2px)',
  };
  const boxStyle = {
    background: 'linear-gradient(180deg, var(--bg-1), var(--bg-2))',
    border: '1px solid var(--border-0)',
    borderRadius: 6, padding: 28,
    width: 420, maxWidth: '90vw',
    boxShadow: '0 20px 60px rgba(0,0,0,0.5), 0 0 40px rgba(196,90,58,0.08)',
  };
  const confirmOverlayStyle2 = {
    position: 'fixed', inset: 0, zIndex: 2000,
    background: 'rgba(0,0,0,0.7)',
    display: 'flex', alignItems: 'center', justifyContent: 'center'
  };
  const confirmBoxStyle2 = {
    background: 'var(--bg-1)', border: '1px solid var(--border-0)',
    borderRadius: 6, padding: 22, width: 340, maxWidth: '90vw'
  };
  const btnPrimary = {
    width: '100%', padding: '10px 0',
    background: 'linear-gradient(135deg, var(--accent-rust), var(--accent-rust-dim))',
    border: '1px solid var(--accent-rust)',
    borderRadius: 3, color: '#fff', cursor: 'pointer',
    fontSize: 13, fontWeight: 500,
    boxShadow: '0 2px 6px rgba(196,90,58,0.3)',
    fontFamily: 'var(--sans)',
    marginTop: 6,
    opacity: loading ? 0.6 : 1,
  };
  const btnSecondary = {
    padding: '6px 0', background: 'transparent', border: 'none',
    color: 'var(--accent-mustard)', cursor: 'pointer',
    fontSize: 12, fontFamily: 'var(--sans)',
  };
  const btnRow = { display: 'flex', gap: 10, marginTop: 16 };
  const inputSt = {
    width: '100%', height: 38, padding: '0 10px',
    background: 'var(--bg-0)', border: '1px solid var(--border-1)',
    borderRadius: 3, color: 'var(--text-0)',
    fontSize: 13, fontFamily: 'var(--sans)',
    outline: 'none',
  };
  const inputDisabled = { ...inputSt, opacity: 0.5, cursor: "not-allowed" };
  const labelSt = {
    display: 'block', color: 'var(--text-2)', fontSize: 11,
    marginBottom: 5, letterSpacing: 0.5,
  };
  const radioWrap = { display: "flex", gap: "16px", margin: "6px 0 12px 0" };

  return (
    <div style={modalStyle} onClick={onClose}>
      <div style={boxStyle} onClick={(e) => e.stopPropagation()}>
        <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 18 }}>
          <h3 style={{ margin: 0, color: 'var(--text-0)', fontSize: 17 }}>
            {mode === 'login' && str.playerLogin}
            {mode === 'register' && str.playerRegister}
            {mode === 'profile' && str.playerProfile}
          </h3>
          <button onClick={onClose} style={{ background: 'transparent', border: 'none', color: 'var(--text-3)', cursor: 'pointer', fontSize: 20, padding: 0, lineHeight: 1 }}>×</button>
        </div>

        {mode === 'login' && (
          <form onSubmit={handleLogin} style={{ display: 'flex', flexDirection: 'column', gap: 14 }}>
            <div><label style={labelSt}>{str.accountLabel}</label><input type="text" style={inputSt} value={qq} onChange={e => setQq(e.target.value)} placeholder={str.accountLabel} required autoFocus /></div>
            <div><label style={labelSt}>{str.passwordLabel}</label><input type="password" style={inputSt} value={password} onChange={e => setPassword(e.target.value)} placeholder={str.passwordPlaceholder} required /></div>
            {error && <div style={{ color:'var(--danger)', fontSize:12, padding:8, background:'rgba(214,69,69,0.1)', borderRadius:3, border:'1px solid rgba(214,69,69,0.3)' }}>{error}</div>}
            <button type="submit" style={btnPrimary} disabled={loading}>{loading ? str.loading : str.loginButton}</button>
            <div style={{ textAlign: 'center' }}><span style={{ fontSize:12, color:'var(--text-2)' }}>{str.noAccountYet}</span><button type="button" style={btnSecondary} onClick={() => setMode('register')}>{str.registerNow}</button></div>
          </form>
        )}

        {mode === 'register' && (
          <form onSubmit={onClickRegisterCheck} style={{ display: 'flex', flexDirection: 'column', gap: 12 }}>
            <label style={labelSt}>{str.playerTypeLabel}</label>
            <div style={radioWrap}>
              <label style={{ fontSize:12, color:'var(--text-2)' }}><input type="radio" checked={userRegionMode === 'cn'} onChange={() => setUserRegionMode('cn')} />{str.cnPlayer}</label>
              <label style={{ fontSize:12, color:'var(--text-2)' }}><input type="radio" checked={userRegionMode === 'intl'} onChange={() => setUserRegionMode('intl')} />{str.intlPlayer}</label>
            </div>
            {userRegionMode === 'cn' && <div><label style={labelSt}>QQ 号</label><input type="text" style={inputSt} value={qq} onChange={e => setQq(e.target.value)} placeholder={str.qqPlaceholder} required autoFocus /></div>}
            {userRegionMode === 'intl' && <div><label style={labelSt}>Discord纯数字ID / 联系邮箱</label><input type="text" style={inputSt} value={externalContact} onChange={e => setExternalContact(e.target.value)} placeholder={str.externalContactPlaceholder} required autoFocus /></div>}
            <div><label style={labelSt}>{str.gameIdLabel}</label><input type="text" style={inputSt} value={gameId} onChange={e => setGameId(e.target.value)} placeholder={str.gameIdPlaceholder} required /></div>
            <div><label style={labelSt}>{str.passwordLabel}</label><input type="password" style={inputSt} value={password} onChange={e => setPassword(e.target.value)} placeholder={str.passwordPlaceholder} required /></div>
            <div><label style={labelSt}>{str.confirmPasswordLabel}</label><input type="password" style={inputSt} value={confirmPw} onChange={e => setConfirmPw(e.target.value)} placeholder={str.confirmPasswordPlaceholder} required /></div>
            {error && <div style={{ color:'var(--danger)', fontSize:12, padding:8, background:'rgba(214,69,69,0.1)', borderRadius:3, border:'1px solid rgba(214,69,69,0.3)' }}>{error}</div>}
            {success && <div style={{ color:'var(--success)', fontSize:12, padding:8, background:'rgba(90,154,85,0.1)', borderRadius:3 }}>{success}</div>}
            <button type="submit" style={btnPrimary} disabled={loading}>{loading ? str.loading : str.registerButton}</button>
            <div style={{ textAlign: 'center' }}><span style={{ fontSize:12, color:'var(--text-2)' }}>{str.alreadyHaveAccount}</span><button type="button" style={btnSecondary} onClick={() => setMode('login')}>{str.backToLogin}</button></div>
          </form>
        )}

        {mode === 'profile' && currentPlayer && (
          <>
            <div style={{ display:'flex', alignItems:'center', gap:12, padding:'12px 0', marginBottom:6 }}>
              <div style={{ width:48, height:48, borderRadius:'50%', background:'linear-gradient(135deg, var(--accent-green), var(--accent-green-dim))', display:'flex', alignItems:'center', justifyContent:'center', fontSize:20, fontWeight:700, color:'#fff', boxShadow:'0 2px 8px rgba(106,138,74,0.3)' }}>{currentPlayer.gameId?.charAt(0) || '?'}</div>
              <div><div style={{ fontSize:15, fontWeight:600, color:'var(--text-0)' }}>游戏ID：{currentPlayer.gameId}</div>{currentPlayer.qq && <div style={{ fontSize:11, color:'var(--text-2)', fontFamily:'var(--mono)' }}>QQ: {currentPlayer.qq}</div>}{currentPlayer.contactExternal && <div style={{ fontSize:11, color:'var(--text-2)', fontFamily:'var(--mono)' }}>外部联系: {currentPlayer.contactExternal}</div>}</div>
            </div>
            {!isEdit ? (
              <div style={{ display:'flex', flexDirection:'column', gap:10 }}>
                <button 
                  type="button" 
                  style={btnPrimary} 
                  onClick={() => {
                    onClose();
                    window.dispatchEvent(new CustomEvent('showMyPosts'));
                  }}
                >
                  📝 {str.myPosts || '我的帖子'}
                </button>
                <button type="button" style={btnPrimary} onClick={() => setIsEdit(true)}>{str.editProfile}</button>
                <button type="button" onClick={handleLogout} style={{ ...btnPrimary, background:'transparent', borderColor:'var(--danger)', color:'var(--danger)', boxShadow:'none' }}>{str.logout}</button>
              </div>
            ) : (
              <form onSubmit={handleUpdateProfile} style={{ display:'flex', flexDirection:'column', gap:12 }}>
                <div><label style={labelSt}>{str.gameIdLabel}</label><input type="text" style={inputDisabled} value={gameId} disabled /></div>
                {currentPlayer.qq && <div><label style={labelSt}>QQ账号</label><input type="text" style={inputDisabled} value={qq} disabled /></div>}
                {currentPlayer.contactExternal && <div><label style={labelSt}>外部联系方式</label><input type="text" style={inputDisabled} value={externalContact} disabled /></div>}
                <div style={{ padding:'6px 0', borderTop:'1px dashed var(--border-0)', marginTop:6 }}>
                  <div style={{ fontSize:11, color:'var(--text-3)', marginBottom:8 }}>修改密码</div>
                  <div style={{ display:'flex', flexDirection:'column', gap:10 }}>
                    <div><label style={labelSt}>{str.currentPasswordLabel}</label><input type="password" style={inputSt} value={confirmPw} onChange={e => setConfirmPw(e.target.value)} /></div>
                    <div><label style={labelSt}>{str.newPasswordLabel}</label><input type="password" style={inputSt} value={password} onChange={e => setPassword(e.target.value)} /></div>
                  </div>
                </div>
                {error && <div style={{ color:'var(--danger)', fontSize:12, padding:8, background:'rgba(214,69,69,0.1)', borderRadius:3 }}>{error}</div>}
                {success && <div style={{ color:'var(--success)', fontSize:12, padding:8, background:'rgba(90,154,85,0.1)', borderRadius:3 }}>{success}</div>}
                <button type="submit" style={btnPrimary} disabled={loading}>{str.saveChanges}</button>
              </form>
            )}
          </>
        )}
      </div>

      {showIdConfirm && (
        <div style={confirmOverlayStyle2} onClick={() => setShowIdConfirm(false)}>
          <div style={confirmBoxStyle2} onClick={e => e.stopPropagation()}>
            <h4 style={{ margin:0, color:'var(--text-0)' }}>{str.confirmGameId}</h4>
            <div style={{ marginTop:12, fontSize:13, color:'var(--text-2)', lineHeight:1.6 }}>
              你填写的游戏ID：<b style={{ color:"var(--accent-mustard)" }}>{gameId}</b>
              <br /><br />
              {str.gameIdConfirmWarning}
              <br />
              {str.gameIdConfirmDesc}
            </div>
            <div style={btnRow}>
              <button style={{ ...btnPrimary, flex:1, background:"transparent", borderColor:"var(--text-2)" }} onClick={() => setShowIdConfirm(false)}>{str.cancel}</button>
              <button style={{ ...btnPrimary, flex:1 }} onClick={doRealRegister} disabled={loading}>{str.confirmSubmit}</button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}

// ===== MY POSTS PAGE =====
function MyPostsPage({ currentUser, onBack }) {
  const lang = useLang();
  const str = window.UI_STRINGS[lang];
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(true);
  const [viewingPost, setViewingPost] = useState(null);

  useEffect(() => {
    if (!currentUser) return;
    const loadMyPosts = async () => {
      setLoading(true);
      try {
        const data = await window.api.getPosts({ limit: 50, sort: 'created_at.desc' });
        const myPosts = data.filter(p => p.author_id === currentUser.id);
        setPosts(myPosts);
      } catch (err) {
        console.error('加载我的帖子失败:', err);
      }
      setLoading(false);
    };
    loadMyPosts();
  }, [currentUser]);

  if (viewingPost) {
    return <PostDetailPage 
      postId={viewingPost} 
      onBack={() => setViewingPost(null)} 
      onDelete={() => {}} 
      currentUser={currentUser} 
    />;
  }

  return (
    <div>
      <div style={{ display:'flex', alignItems:'center', gap:12, marginBottom:16 }}>
        <button style={btnSecondaryStyle} onClick={onBack}>← {str.backToCommunity || '返回'}</button>
        <h2 style={{ color:'var(--text-0)', fontSize:18, margin:0 }}>{str.myPosts || '我的帖子'}</h2>
      </div>
      {loading ? (
        <div style={{ textAlign:'center', padding:60, color:'var(--text-2)' }}>{str.loading || '加载中...'}</div>
      ) : posts.length === 0 ? (
        <div className="empty-state">
          <div className="empty-icon"><Icon name="empty" /></div>
          <h3>{str.noMyPosts || '还没有发布过帖子'}</h3>
          <p>{str.goToCommunity || '去社区发个帖吧！'}</p>
        </div>
      ) : (
        <div className="item-grid" style={{ gridTemplateColumns:'1fr' }}>
          {posts.map(post => {
            const authorName = post.author?.game_id || str.anonymous || '匿名';
            return (
              <div
                key={post.id}
                className="item-card"
                onClick={() => setViewingPost(post.id)}
                style={{ cursor:'pointer' }}
              >
                <div className="item-top" style={{ justifyContent:'space-between' }}>
                  <div style={{ flex:1 }}>
                    <div className="item-name" style={{ fontSize:16 }}>{post.title}</div>
                    <div style={{ fontSize:12, color:'var(--text-2)', marginTop:4 }}>
                      {str.category || '分类'}: {post.category}
                    </div>
                  </div>
                  <div style={{ textAlign:'right', fontSize:12, color:'var(--text-2)' }}>
                    <div>{str.viewCount || '浏览'}: {post.view_count || 0}</div>
                    <div>{str.replyCount || '回复'}: {post.reply_count || 0}</div>
                  </div>
                </div>
                <div className="item-bottom" style={{ marginTop:8 }}>
                  <div className="item-cat"><span className="dot"></span>{str.postedBy || '发布于'}: {authorName} · {new Date(post.created_at).toLocaleString()}</div>
                  {post.is_pinned && <span style={{ fontSize:11, color:'var(--accent-mustard)' }}>📌 {str.pinned || '置顶'}</span>}
                </div>
              </div>
            );
          })}
        </div>
      )}
    </div>
  );
}

// ===== 版本常量 =====
const WIKI_VERSION = "v26.02.10 · PTL Community";

// ===== APP ROOT =====
function App() {
  const categories = window.ITEM_CATEGORIES;
  const [activeCategory, setActiveCategory] = useState('firearm');
  const [activeDataPage, setActiveDataPage] = useState('home');
  const [searchQuery, setSearchQuery] = useState('');
  const [searchFocused, setSearchFocused] = useState(false);
  const [sidebarOpen, setSidebarOpen] = useState(false);
  const [playerAuthOpen, setPlayerAuthOpen] = useState(false);
  const [currentPlayer, setCurrentPlayer] = useState(null);
  const [apiAvailable, setApiAvailable] = useState(false);
  const [lang, setLang] = useState(() => {
    try { return localStorage.getItem('erythros_lang') || 'zh'; } catch { return 'zh'; }
  });
  const [showMyPosts, setShowMyPosts] = useState(false);

  const isHashSyncing = useRef(false);
  const str = window.UI_STRINGS[lang];
  const [isAdmin, setIsAdmin] = useState(false);

  // 监听“我的帖子”事件
  useEffect(() => {
    const handleShowMyPosts = () => setShowMyPosts(true);
    window.addEventListener('showMyPosts', handleShowMyPosts);
    return () => window.removeEventListener('showMyPosts', handleShowMyPosts);
  }, []);

  useEffect(() => {
    const syncHash = () => {
      const hash = window.location.hash || "#home";
      isHashSyncing.current = true;
      if(hash === "#home"){ setActiveDataPage("home"); setActiveCategory(null); }
      else if(hash === "#items"){ setActiveDataPage(null); }
      else if(hash === "#maps"){ setActiveDataPage("maps"); setActiveCategory(null); }
      else if(hash === "#medicine"){ setActiveDataPage("medicine"); setActiveCategory(null); }
      else if(hash === "#crafting"){ setActiveDataPage("crafting"); setActiveCategory(null); }
      else if(hash === "#durability"){ setActiveDataPage("durability"); setActiveCategory(null); }
      else if(hash === "#community"){ setActiveDataPage("community"); setActiveCategory(null); }
      else if(hash === "#admin"){ setActiveDataPage("admin"); setActiveCategory(null); }
      setTimeout(()=>{ isHashSyncing.current = false; },0);
    };
    if(!window.location.hash) window.location.hash = "#home";
    syncHash();
    window.addEventListener("hashchange", syncHash);
    return ()=> window.removeEventListener("hashchange", syncHash);
  }, []);

  useEffect(()=>{
    if(isHashSyncing.current) return;
    if(activeDataPage) window.location.hash = `#${activeDataPage}`;
    else window.location.hash = "#items";
  },[activeDataPage]);

  useEffect(() => {
    const token = localStorage.getItem('player_token');
    if (token) {
      window.api.playerMe(token).then(data => {
        if (data.player) { setCurrentPlayer(data.player); setApiAvailable(true); }
      }).catch(() => {});
    }
    const adminToken = localStorage.getItem('admin_token');
    if (adminToken && adminToken !== '') {
      window.api.adminMe(adminToken).then(data => {
        if (data.admin && data.user) {
          setIsAdmin(true);
          const adminUser = data.user;
          if (!currentPlayer) {
            setCurrentPlayer({
              id: adminUser.id,
              gameId: `管理员 ${data.admin.username}`,
              username: data.admin.username,
              role: 'admin',
              qq: '',
              contactExternal: '',
            });
          } else {
            setCurrentPlayer(prev => ({ ...prev, role:'admin', username:data.admin.username, id: adminUser.id }));
          }
          setApiAvailable(true);
        }
      }).catch((err) => {
        if (err.message && (err.message.includes('403') || err.message.includes('401'))) {
          localStorage.removeItem('admin_token');
          setIsAdmin(false);
        }
      });
    }
  }, []);

  useEffect(() => {
    const handleAdminLogin = (event) => {
      const { admin, token, user } = event.detail;
      if (admin && token && user) {
        setIsAdmin(true);
        setCurrentPlayer({
          id: user.id,
          gameId: `管理员 ${admin.username}`,
          username: admin.username,
          role: 'admin',
          qq: '',
          contactExternal: '',
        });
        setApiAvailable(true);
      }
    };
    window.addEventListener('adminLogin', handleAdminLogin);
    return () => window.removeEventListener('adminLogin', handleAdminLogin);
  }, []);

  const handlePlayerLoginSuccess = (player) => {
    setCurrentPlayer(player);
    setApiAvailable(true);
    const adminToken = localStorage.getItem('admin_token');
    if (adminToken && adminToken !== '') {
      window.api.adminMe(adminToken).then(data => {
        if (data.admin && data.user) {
          setIsAdmin(true);
          const adminUser = data.user;
          setCurrentPlayer(prev => ({ ...prev, role:'admin', username:data.admin.username, id: adminUser.id }));
        }
      }).catch(() => {});
    }
  };

  const handlePlayerLogout = () => {
    setCurrentPlayer(null);
    setIsAdmin(false);
    localStorage.removeItem('player_token');
    localStorage.removeItem('admin_token');
    window.location.hash = '#home';
  };

  const handleOpenAdmin = () => {
    setActiveDataPage('admin');
    setActiveCategory(null);
  };

  const totalItems = useMemo(() => categories.reduce((sum, c) => sum + c.items.length, 0), [categories]);
  const isSearching = searchQuery.trim().length > 0;

  const handleLangChange = useCallback((newLang) => {
    setLang(newLang);
    try { localStorage.setItem('erythros_lang', newLang); } catch {}
  }, []);

  const handleSelectCategory = useCallback((id) => {
    setActiveCategory(id);
    setActiveDataPage(null);
  }, []);
  const handleSelectDataPage = useCallback((page) => {
    setActiveDataPage(page);
    setActiveCategory(null);
  }, []);

  useEffect(() => {
    const handler = (e) => {
      if (e.key === '/' && !e.ctrlKey && !e.metaKey && !e.altKey) {
        const tag = document.activeElement?.tagName;
        if (tag !== 'INPUT' && tag !== 'TEXTAREA') {
          e.preventDefault();
          const input = document.getElementById('search-input');
          if (input) input.focus();
        }
      }
      if (e.key === 'Escape') {
        setSearchQuery('');
        const input = document.getElementById('search-input');
        if (input) input.blur();
      }
    };
    window.addEventListener('keydown', handler);
    return () => window.removeEventListener('keydown', handler);
  }, []);

  useEffect(() => { document.title = str.appTitle; }, [str.appTitle]);
  useEffect(() => {
    if (!window.api || !apiAvailable) return;
    if (activeDataPage && activeDataPage !== 'admin') {
      window.api.logVisit('page', activeDataPage, activeDataPage);
    }
  }, [activeDataPage, apiAvailable]);

  const renderContent = () => {
    if (showMyPosts) {
      return <MyPostsPage 
        currentUser={currentPlayer} 
        onBack={() => setShowMyPosts(false)} 
      />;
    }

    if (activeDataPage === 'admin') {
      if (typeof window.AdminPage !== 'undefined') {
        const AdminPage = window.AdminPage;
        return <AdminPage />;
      } else {
        return <div className="page-header"><h2>{str.adminLoading}</h2></div>;
      }
    }
    if (activeDataPage === 'home') return <HomePage />;
    if (activeDataPage === 'community') return <CommunityPage />;
    if (isSearching) return <SearchResultsPage query={searchQuery} categories={categories} />;
    if (activeDataPage === 'maps') return <MapsPage />;
    if (activeDataPage === 'medicine') return <MedicinePage />;
    if (activeDataPage === 'crafting') return <CraftingPage searchQuery={searchQuery} />;
    if (activeDataPage === 'durability') return <BuildingDurabilityPage />;
    const cat = categories.find(c => c.id === activeCategory);
    if (cat) return <ItemsPage category={cat} searchQuery={searchQuery} />;
    return <div className="page-header"><h2>{str.pageNotFound}</h2></div>;
  };

  return (
    <LangContext.Provider value={lang}>
      <div className="app">
        <Sidebar
          isOpen={sidebarOpen}
          onClose={() => setSidebarOpen(false)}
          activeCategory={activeCategory}
          onSelectCategory={handleSelectCategory}
          activeDataPage={activeDataPage}
          onSelectDataPage={handleSelectDataPage}
          categories={categories}
          totalItems={totalItems}
          player={currentPlayer}
          onOpenPlayerAuth={() => setPlayerAuthOpen(true)}
          onOpenAdmin={handleOpenAdmin}
        />
        <main className="main">
          <div className="topbar">
            <button className="mobile-menu-btn" onClick={() => setSidebarOpen(true)} aria-label="menu">
              <Icon name="menu" />
            </button>
            <div className="search-wrap">
              <span className="search-icon"><Icon name="search" /></span>
              <input
                id="search-input"
                className="search-input"
                type="text"
                placeholder={str.searchPlaceholder}
                value={searchQuery}
                onChange={(e) => setSearchQuery(e.target.value)}
                onFocus={() => setSearchFocused(true)}
                onBlur={() => setSearchFocused(false)}
              />
              {!searchFocused && !searchQuery && (
                <div className="search-shortcut"><span className="kbd">/</span></div>
              )}
            </div>
            <LangSwitcher lang={lang} onChange={handleLangChange} />
            <div className="topbar-info">
              <div className="topbar-stat"><div className="num">{totalItems}</div><div className="lbl">{str.totalItems}</div></div>
              <div className="topbar-stat"><div className="num">{categories.length}</div><div className="lbl">{str.categories}</div></div>
            </div>
          </div>
          <div className="content">
            {renderContent()}
            <PtlWatermark />
          </div>
          <PlayerAuthModal
            isOpen={playerAuthOpen}
            onClose={() => setPlayerAuthOpen(false)}
            onLoginSuccess={handlePlayerLoginSuccess}
            currentPlayer={currentPlayer}
            onLogout={handlePlayerLogout}
            lang={lang}
          />
        </main>
      </div>
    </LangContext.Provider>
  );
}

// ===== BOOT =====
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(<App />);
function announceUpgrade() {
  window.parent.postMessage({ type: 'miaoda:upgrade:available', kind: 'interactive-prototype' }, '*');
}
if (document.readyState === 'complete' || document.readyState === 'interactive') {
  announceUpgrade();
} else {
  window.addEventListener('load', announceUpgrade, { once: true });
}
// ========== 片段 4 结束 ==========
// ========== 全部片段已发送完毕 ==========
