diff --git a/app.py b/app.py
new file mode 100644
index 0000000..1ad4dee
--- /dev/null
+++ b/app.py
@@ -0,0 +1,835 @@
+"""
+app.py — Flask 后端主文件
+=========================
+作用:整个 MVP 的"大脑",负责三件事:
+ 1. 提供 API 接口(视频列表、AI 对话、行为记录)
+ 2. 管理 SQLite 数据库(用户、视频、行为日志)
+ 3. 对接 DeepSeek 大模型 API
+
+产品概念说明:
+ - 这里用的是 "单体应用" 架构:一个 app.py 把路由+数据库+API 全包了。
+ 适合 MVP 阶段快速验证,后续用户量起来再拆分为微服务。
+ - 路由 (Route):浏览器访问不同网址时,Flask 把请求分发给对应的函数处理。
+ 类比:路由 = 你走进商场,问服务台"女装在三楼",服务台就是路由层的功能。
+"""
+
+import gzip
+import json
+import os
+import sqlite3
+from datetime import timedelta
+
+import requests
+from flask import Flask, render_template, request, jsonify, send_from_directory
+
+# ---- 初始化 Flask 应用 ----
+app = Flask(__name__)
+# 静态资源缓存 7 天(模板引用带 ?v= 版本号,更新时递增即可刷新)
+app.config['SEND_FILE_MAX_AGE_DEFAULT'] = timedelta(days=7)
+
+# 配置路径 (使用绝对路径以避免 sqlite 找不到文件)
+BASE_DIR = os.path.dirname(os.path.abspath(__file__))
+DATA_DIR = os.path.join(BASE_DIR, "data").replace("\\", "/")
+# 修改数据库文件名,绕过之前的旧文件锁定问题
+DB_PATH = os.path.join(DATA_DIR, "miniapp_v3.db").replace("\\", "/")
+VIDEOS_PATH = os.path.join(DATA_DIR, "seed_videos.json").replace("\\", "/")
+STORY_PATH = os.path.join(DATA_DIR, "seed_story.json").replace("\\", "/")
+MATH_TEEN_STORY_PATH = os.path.join(DATA_DIR, "数学少年线_story.json").replace("\\", "/")
+SIMULATOR_PATH = os.path.join(DATA_DIR, "seed_simulator.json").replace("\\", "/")
+MATHBTI_PATH = os.path.join(DATA_DIR, "seed_mathbti.json").replace("\\", "/")
+ALUMNI_PATH = os.path.join(DATA_DIR, "seed_alumni.json").replace("\\", "/")
+
+# 确保 data 目录存在
+os.makedirs(DATA_DIR, exist_ok=True)
+
+
+# 内容型 GET API 短缓存(种子数据极少变动)
+_API_CACHE_SECONDS = {
+ '/api/videos': 300,
+ '/api/knowledge': 300,
+ '/api/story': 300,
+ '/api/simulator': 300,
+ '/api/alumni': 300,
+ '/api/math_personality': 300,
+}
+
+
+@app.after_request
+def compress_response(response):
+ """gzip 压缩文本响应(HTML/CSS/JS/JSON >500B)+ 内容型 API 短缓存"""
+ if request.method == 'GET' and request.path in _API_CACHE_SECONDS:
+ response.headers['Cache-Control'] = f"private, max-age={_API_CACHE_SECONDS[request.path]}"
+ if (response.status_code != 200
+ or 'Content-Encoding' in response.headers):
+ return response
+ if 'gzip' not in request.headers.get('Accept-Encoding', ''):
+ return response
+ mimetype = response.mimetype or ''
+ if not (mimetype.startswith('text/') or mimetype in ('application/json', 'application/javascript')):
+ return response
+ if response.direct_passthrough:
+ response.direct_passthrough = False # 静态文件需读入内存才能压缩
+ data = response.get_data()
+ if len(data) < 500:
+ return response
+ compressed = gzip.compress(data, compresslevel=6)
+ response.set_data(compressed)
+ response.headers['Content-Encoding'] = 'gzip'
+ response.headers['Content-Length'] = len(compressed)
+ response.headers['Vary'] = 'Accept-Encoding'
+ return response
+
+# DeepSeek API 配置 — 需要用户填入自己的 API Key
+DEEPSEEK_API_KEY = "sk-0cd8b068c4a3439c9fc6d67b08865c21"
+DEEPSEEK_API_URL = "https://api.deepseek.com/v1/chat/completions"
+
+
+# ============================================================
+# 模块 1: 数据库初始化
+# ============================================================
+def init_db():
+ conn = sqlite3.connect(DB_PATH)
+ c = conn.cursor()
+ c.execute("""
+ CREATE TABLE IF NOT EXISTS behavior_log (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ user_id TEXT,
+ action TEXT,
+ target TEXT,
+ detail TEXT,
+ timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+ )
+ """)
+ # 旧库迁移:补充 detail 列
+ try:
+ c.execute("ALTER TABLE behavior_log ADD COLUMN detail TEXT")
+ except Exception:
+ pass
+ # 知识卡片表
+ c.execute("""
+ CREATE TABLE IF NOT EXISTS knowledge_cards (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ title TEXT NOT NULL,
+ question TEXT,
+ description TEXT,
+ detail TEXT,
+ color TEXT DEFAULT '#3B82F6',
+ icon TEXT DEFAULT '📖',
+ topic TEXT,
+ read_time TEXT DEFAULT '5分钟阅读',
+ sort_order INTEGER DEFAULT 0
+ )
+ """)
+ # 收藏表
+ c.execute("""
+ CREATE TABLE IF NOT EXISTS favorites (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ user_id TEXT NOT NULL,
+ target_type TEXT NOT NULL,
+ target_id INTEGER NOT NULL,
+ title TEXT,
+ cover TEXT,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ UNIQUE(user_id, target_type, target_id)
+ )
+ """)
+ # 观看历史表
+ c.execute("""
+ CREATE TABLE IF NOT EXISTS watch_history (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ user_id TEXT NOT NULL,
+ target_type TEXT NOT NULL,
+ target_id INTEGER NOT NULL,
+ title TEXT,
+ cover TEXT,
+ watched_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+ )
+ """)
+ # 用户表
+ c.execute("""
+ CREATE TABLE IF NOT EXISTS users (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ user_id TEXT UNIQUE NOT NULL,
+ nickname TEXT DEFAULT '游客用户',
+ avatar_emoji TEXT DEFAULT 'π',
+ avatar_color TEXT DEFAULT '#5B6AF0',
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+ )
+ """)
+ # 学生表(B端看板)
+ c.execute("""
+ CREATE TABLE IF NOT EXISTS students (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ student_id TEXT UNIQUE,
+ name TEXT,
+ class_name TEXT,
+ grade TEXT
+ )
+ """)
+ # 常用查询索引
+ c.execute("CREATE INDEX IF NOT EXISTS idx_favorites_user ON favorites(user_id)")
+ c.execute("CREATE INDEX IF NOT EXISTS idx_history_user ON watch_history(user_id)")
+ c.execute("CREATE INDEX IF NOT EXISTS idx_behavior_user ON behavior_log(user_id)")
+ conn.commit()
+ conn.close()
+
+
+def load_seed_knowledge():
+ """加载知识卡片种子数据"""
+ conn = sqlite3.connect(DB_PATH)
+ c = conn.cursor()
+ c.execute("SELECT COUNT(*) FROM knowledge_cards")
+ if c.fetchone()[0] > 0:
+ conn.close()
+ return
+
+ cards = [
+ {"title": "被平均数骗过的所有人", "question": "你和马云的平均财富是多少?", "desc": "平均数、中位数、众数——这三兄弟让你看到的「真相」可能截然相反。", "detail": "平均数是最常用的统计量,但它也是最容易被误解的。当数据中有极端值时,平均数会被拉偏。比如你和马云的平均财富可能是千亿级别,但中位数可能只有几万。了解平均数、中位数、众数的区别,是统计学入门的第一课。", "color": "#EF4444", "icon": "📊", "topic": "统计陷阱", "read_time": "5分钟阅读", "sort": 1},
+ {"title": "彩票:一场精心设计的数学税", "question": "2元博500万,值不值?", "desc": "你中头奖的概率比你连续被雷劈三次还低。但为什么每天都有人买?", "detail": "双色球头奖概率约1/1772万。对比:被雷劈的概率约1/100万。彩票的期望值永远是负的——这是彩票公司盈利的数学基础。人们购买彩票不是因为他们不懂数学,而是因为损失厌恶和过度乐观的认知偏差。", "color": "#F59E0B", "icon": "🎲", "topic": "概率思维", "read_time": "5分钟阅读", "sort": 2},
+ {"title": "为什么所有地图都在骗你", "question": "格陵兰岛有非洲大吗?", "desc": "把球面摊平一定会变形——高斯发现的绝妙定理决定了你看到的世界。", "detail": "墨卡托投影让格陵兰岛看起来和非洲差不多大,实际上非洲面积是格陵兰的14倍。球面无法完美展开成平面,这是高斯的'绝妙定理'(Theorema Egregium)决定的。每种地图投影都有取舍:要么保面积,要么保角度,要么保方向——你永远无法三者兼得。", "color": "#3B82F6", "icon": "🗺️", "topic": "几何直觉", "read_time": "5分钟阅读", "sort": 3},
+ {"title": "大自然最爱的数列", "question": "兔子繁殖与斐波那契", "desc": "从向日葵的花瓣到鹦鹉螺的壳,斐波那契数列无处不在。这是巧合吗?", "detail": "斐波那契数列:1, 1, 2, 3, 5, 8, 13, 21... 后一项等于前两项之和。这个简单的规则在大自然中反复出现:松果的螺旋数、花瓣的排列、菠萝的鳞片——都是斐波那契数。这不是巧合,而是植物在生长过程中最优空间利用的结果。", "color": "#8B5CF6", "icon": "🔢", "topic": "数列之美", "read_time": "6分钟阅读", "sort": 4},
+ {"title": "博弈论:竞争的最优解", "question": "为什么奶茶店总是开在隔壁?", "desc": "纳什均衡解释了一个奇怪的现象:竞争对手为什么总是扎堆?", "detail": "两个冰淇淋摊在海滩上竞争,最后都会摆到海滩正中间——这就是霍特林模型。纳什均衡告诉我们:当每个人都做出对自己最优的选择时,结果可能对所有人都不好。这个原理适用于从商业选址到交通拥堵的各种场景。", "color": "#10B981", "icon": "⚖️", "topic": "博弈策略", "read_time": "5分钟阅读", "sort": 5},
+ {"title": "混沌理论:看不见的秩序", "question": "一只蝴蝶能引起台风吗?", "desc": "天气预报为什么不准?非线性动力学如何揭示混乱中的规律。", "detail": "洛伦兹在1961年发现:输入数据小数点后三位的微小差异,会导致完全不同的天气预报结果。这就是'蝴蝶效应'。混沌理论最迷人的地方在于:混沌不是随机——它是确定的系统产生不可预测的行为。这种'确定性的随机'挑战了我们对因果关系的直觉。", "color": "#EC4899", "icon": "🦋", "topic": "混沌理论", "read_time": "7分钟阅读", "sort": 6},
+ {"title": "蒙提霍尔:三门悖论", "question": "主持人打开一扇空门后,你该换吗?", "desc": "一个让数学家和大众都吵翻天的概率谜题。", "detail": "游戏节目有三扇门:一扇后面是汽车,两扇后面是山羊。你选择一扇后,主持人打开另一扇有山羊的门。现在你可以坚持原来的选择或换到剩下那扇——换门的中奖概率是2/3,不是1/2。这个结论让无数人(包括数学家)抓狂,但它是对的。关键:主持人的行为提供了信息。", "color": "#06B6D4", "icon": "🚪", "topic": "概率思维", "read_time": "4分钟阅读", "sort": 7},
+ {"title": "本福特定律:数字会说谎", "question": "财务报表里1开头的数字占比是多少?", "desc": "审计师用这条数学定律抓财务造假,准确率高得惊人。", "detail": "自然界里,首位数字为1的概率约为30%,不是直觉中的1/9。河流长度、人口数量、股票价格——都遵循本福特定律。聪明的会计师如果不知道这条定律,造出的假账一眼就会被看穿。美国国税局就用它来筛选审计目标。", "color": "#F97316", "icon": "🔍", "topic": "统计陷阱", "read_time": "5分钟阅读", "sort": 8},
+ ]
+ for c_data in cards:
+ c.execute("""
+ INSERT INTO knowledge_cards (title, question, description, detail, color, icon, topic, read_time, sort_order)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
+ """, (c_data["title"], c_data["question"], c_data["desc"], c_data["detail"],
+ c_data["color"], c_data["icon"], c_data["topic"], c_data["read_time"], c_data["sort"]))
+ conn.commit()
+ conn.close()
+ print(f"[完成] 已导入 {len(cards)} 条知识卡片")
+
+
+def load_seed_videos():
+ """
+ 从 JSON 文件加载预设视频数据写入数据库。
+ """
+ if not os.path.exists(VIDEOS_PATH):
+ print(f"[提示] 种子数据文件不存在: {VIDEOS_PATH},跳过导入")
+ return
+
+ with open(VIDEOS_PATH, "r", encoding="utf-8") as f:
+ videos = json.load(f)
+
+ conn = sqlite3.connect(DB_PATH)
+ c = conn.cursor()
+
+ # 重建视频表(确保新增字段生效)
+ c.execute("DROP TABLE IF EXISTS videos")
+ c.execute("""
+ CREATE TABLE videos (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ title TEXT NOT NULL,
+ module TEXT NOT NULL,
+ sub_category TEXT,
+ discipline TEXT,
+ discipline_icon TEXT,
+ video_url TEXT,
+ cover_url TEXT,
+ description TEXT,
+ duration_min INTEGER DEFAULT 5,
+ author TEXT,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+ )
+ """)
+
+ for v in videos:
+ c.execute("""
+ INSERT INTO videos (title, module, sub_category, discipline, discipline_icon, video_url, cover_url, description, duration_min, author)
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
+ """, (v["title"], v["module"],
+ v.get("sub_category", ""), v.get("discipline", ""), v.get("discipline_icon", ""),
+ v.get("video_url", ""), v.get("cover_url", ""),
+ v.get("description", ""), v.get("duration_min", 5), v.get("author", "")))
+
+ conn.commit()
+ conn.close()
+ print(f"[完成] 已导入 {len(videos)} 条视频数据")
+
+
+# ============================================================
+# 模块 2: 路由定义(API 接口)
+# ============================================================
+
+@app.route("/")
+def index():
+ """
+ 主页路由 — 返回 SPA(单页应用)的 HTML。
+ 前端所有页面切换都在这一个 HTML 里完成,不刷新浏览器。
+ """
+ return render_template("index.html")
+
+
+@app.route("/demo")
+def demo():
+ """返回独立静态 Demo 页面"""
+ return send_from_directory(BASE_DIR, "mathbrain-demo.html")
+
+
+@app.route("/web")
+def web_desktop():
+ """桌面端网页版 — 左侧栏 + 横向布局"""
+ return render_template("desktop.html")
+
+
+@app.route("/data/")
+def data_files(filename):
+ """本地开发时提供 data/ 下的公开 JSON 数据"""
+ if not filename.lower().endswith(".json"):
+ return jsonify({"code": -1, "msg": "not allowed"}), 404
+ return send_from_directory(DATA_DIR, filename)
+
+
+@app.route("/math_teen")
+def math_teen():
+ """数学少年线互动剧本独立播放页"""
+ return render_template("math_teen.html")
+
+
+@app.route("/api/videos")
+def api_videos():
+ """
+ 获取视频列表 API。
+ 支持 module 和 discipline 参数筛选:
+ /api/videos?module=数人
+ /api/videos?module=数人&discipline=计算机与AI
+ """
+ module = request.args.get("module", "")
+ discipline = request.args.get("discipline", "")
+ conn = sqlite3.connect(DB_PATH)
+ conn.row_factory = sqlite3.Row
+ c = conn.cursor()
+
+ query = "SELECT * FROM videos WHERE 1=1"
+ params = []
+ if module:
+ query += " AND module=?"
+ params.append(module)
+ if discipline:
+ query += " AND discipline=?"
+ params.append(discipline)
+ query += " ORDER BY module, id DESC"
+
+ c.execute(query, params)
+ rows = [dict(r) for r in c.fetchall()]
+
+ # 同时返回当前模块的学科列表(用于前端渲染分类筛选)
+ if module == "数人":
+ c.execute("SELECT DISTINCT discipline, discipline_icon FROM videos WHERE module='数人' AND discipline != '' ORDER BY discipline")
+ disciplines = [dict(r) for r in c.fetchall()]
+ conn.close()
+ return jsonify({"code": 0, "data": rows, "disciplines": disciplines})
+
+ conn.close()
+ return jsonify({"code": 0, "data": rows})
+
+
+@app.route("/api/chat", methods=["POST"])
+def api_chat():
+ """
+ AI 对话接口 — 转发用户消息给 DeepSeek,支持上下文感知。
+
+ 请求格式: { "message": "学数学对学医有什么用?", "context": {"type": "video", "title": "xxx"} }
+ """
+ data = request.get_json()
+ user_message = data.get("message", "")
+ student_id = data.get("student_id", "")
+ context = data.get("context") or {}
+ ctx_type = context.get("type", "")
+ ctx_title = context.get("title", "")
+
+ if not DEEPSEEK_API_KEY or DEEPSEEK_API_KEY == "your-deepseek-api-key-here":
+ return jsonify({
+ "code": -1,
+ "reply": "DeepSeek API Key 尚未配置,请在 app.py 中填入你的 Key。\n申请地址: https://platform.deepseek.com/",
+ "is_fallback": True
+ })
+
+ # 根据上下文构建 system prompt
+ system_prompt = (
+ "你是'葫芦数学'的AI助教,面向中小学生和数学爱好者。你的核心任务是:\n"
+ "1. 用生活化的语言解释数学在各学科中的基础作用\n"
+ "2. 引导学生发现'原来数学这么有用'\n"
+ "3. 回答尽量简短、有趣,控制在200字以内\n"
+ "4. 不要出现超出初中生理解范围的专业术语"
+ )
+ if ctx_type == "video" and ctx_title:
+ system_prompt = (
+ f"用户正在观看视频《{ctx_title}》。你是'葫芦数学'的AI助教。\n"
+ f"请围绕这个视频主题,用生活化的语言帮助用户理解其中的数学概念。\n"
+ "回答简短有趣,控制在200字以内。"
+ )
+ elif ctx_type == "knowledge" and ctx_title:
+ system_prompt = (
+ f"用户正在阅读知识卡片《{ctx_title}》。你是'葫芦数学'的AI助教。\n"
+ f"请围绕这个主题深入浅出地解释,可以补充有趣的例子和冷知识。\n"
+ "回答简短有趣,控制在200字以内。"
+ )
+ elif ctx_type == "simulator":
+ system_prompt = (
+ "用户正在玩'数学专业模拟器'——这是模拟大学数学系4年学习生涯的互动游戏。\n"
+ "你是'葫芦数学'的AI助教,请扮演大学数学系的学长/学姐角色,\n"
+ "用鼓励、有趣的方式回答用户关于数学专业的问题。\n"
+ "可以分享一些大学数学系的真实体验和趣事。回答在200字以内。"
+ )
+
+ try:
+ resp = requests.post(
+ DEEPSEEK_API_URL,
+ headers={
+ "Authorization": f"Bearer {DEEPSEEK_API_KEY}",
+ "Content-Type": "application/json"
+ },
+ json={
+ "model": "deepseek-chat",
+ "messages": [
+ {"role": "system", "content": system_prompt},
+ {"role": "user", "content": user_message}
+ ],
+ "temperature": 0.7,
+ "max_tokens": 500
+ },
+ timeout=15
+ )
+ result = resp.json()
+ reply = result["choices"][0]["message"]["content"]
+ is_fallback = False
+
+ except Exception as e:
+ reply = f"[助手暂时离线] API 调用失败: {str(e)[:100]}"
+ is_fallback = True
+
+ # 记录行为日志
+ _log_behavior(student_id, "send_message", detail=user_message)
+
+ return jsonify({"code": 0, "reply": reply, "is_fallback": is_fallback, "context_type": ctx_type})
+
+
+@app.route("/api/log", methods=["POST"])
+def api_log():
+ data = request.json
+ user_id = data.get("user_id", "guest")
+ action = data.get("action", "")
+ target = data.get("target", "")
+
+ conn = sqlite3.connect(DB_PATH)
+ c = conn.cursor()
+ c.execute(
+ "INSERT INTO behavior_log (user_id, action, target) VALUES (?, ?, ?)",
+ (user_id, action, target)
+ )
+ conn.commit()
+ conn.close()
+ return jsonify({"code": 0, "msg": "logged"})
+
+
+@app.route("/api/students")
+def api_students():
+ """获取预设学生列表(B端看板用)"""
+ conn = sqlite3.connect(DB_PATH)
+ conn.row_factory = sqlite3.Row
+ c = conn.cursor()
+ c.execute("SELECT * FROM students ORDER BY grade, class_name")
+ rows = [dict(r) for r in c.fetchall()]
+ conn.close()
+ return jsonify({"code": 0, "data": rows})
+
+
+@app.route("/api/story")
+def api_story():
+ """获取数学武林故事数据"""
+ if not os.path.exists(STORY_PATH):
+ return jsonify({"code": -1, "msg": "故事数据文件不存在"})
+ with open(STORY_PATH, "r", encoding="utf-8") as f:
+ story = json.load(f)
+ return jsonify({"code": 0, "data": story})
+
+
+@app.route("/api/story/math_teen")
+def api_story_math_teen():
+ """获取数学少年线互动剧情数据"""
+ if not os.path.exists(MATH_TEEN_STORY_PATH):
+ return jsonify({"code": -1, "msg": "数学少年线故事数据文件不存在"})
+ with open(MATH_TEEN_STORY_PATH, "r", encoding="utf-8") as f:
+ story = json.load(f)
+ return jsonify({"code": 0, "data": story})
+
+
+@app.route("/api/alumni")
+def api_alumni():
+ """获取数学校友访谈与四年度模拟预留数据"""
+ if not os.path.exists(ALUMNI_PATH):
+ return jsonify({"code": 0, "data": [], "meta": {"status": "reserved"}})
+ with open(ALUMNI_PATH, "r", encoding="utf-8") as f:
+ payload = json.load(f)
+ return jsonify({"code": 0, "data": payload.get("alumni", []), "meta": payload.get("meta", {})})
+
+
+@app.route("/api/simulator")
+def api_simulator():
+ """获取数学专业模拟器数据"""
+ if not os.path.exists(SIMULATOR_PATH):
+ return jsonify({"code": -1, "msg": "模拟器数据文件不存在"})
+ with open(SIMULATOR_PATH, "r", encoding="utf-8") as f:
+ sim = json.load(f)
+ return jsonify({"code": 0, "data": sim})
+
+
+@app.route("/api/simulator/save", methods=["POST"])
+def api_simulator_save():
+ """保存模拟器进度"""
+ data = request.get_json()
+ user_id = data.get("user_id", "guest")
+ save_data = json.dumps(data.get("state", {}))
+ conn = sqlite3.connect(DB_PATH)
+ c = conn.cursor()
+ c.execute("""
+ CREATE TABLE IF NOT EXISTS simulator_saves (
+ user_id TEXT PRIMARY KEY,
+ state TEXT,
+ updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
+ )
+ """)
+ c.execute("INSERT OR REPLACE INTO simulator_saves (user_id, state) VALUES (?, ?)", (user_id, save_data))
+ conn.commit()
+ conn.close()
+ return jsonify({"code": 0, "msg": "saved"})
+
+
+@app.route("/api/simulator/save", methods=["GET"])
+def api_simulator_load():
+ """加载模拟器进度"""
+ user_id = request.args.get("user_id", "guest")
+ conn = sqlite3.connect(DB_PATH)
+ c = conn.cursor()
+ c.execute("CREATE TABLE IF NOT EXISTS simulator_saves (user_id TEXT PRIMARY KEY, state TEXT, updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP)")
+ c.execute("SELECT state FROM simulator_saves WHERE user_id=?", (user_id,))
+ row = c.fetchone()
+ conn.close()
+ if row:
+ return jsonify({"code": 0, "data": json.loads(row[0])})
+ return jsonify({"code": 0, "data": None})
+
+
+@app.route("/api/knowledge")
+def api_knowledge():
+ """获取知识卡片列表"""
+ topic = request.args.get("topic", "")
+ conn = sqlite3.connect(DB_PATH)
+ conn.row_factory = sqlite3.Row
+ c = conn.cursor()
+ query = "SELECT * FROM knowledge_cards WHERE 1=1"
+ params = []
+ if topic:
+ query += " AND topic=?"
+ params.append(topic)
+ query += " ORDER BY sort_order"
+ c.execute(query, params)
+ rows = [dict(r) for r in c.fetchall()]
+ # 获取所有话题
+ c.execute("SELECT DISTINCT topic FROM knowledge_cards WHERE topic != '' ORDER BY topic")
+ topics = [r["topic"] for r in c.fetchall()]
+ conn.close()
+ return jsonify({"code": 0, "data": rows, "topics": topics})
+
+
+@app.route("/api/knowledge/")
+def api_knowledge_detail(card_id):
+ """获取单个知识卡片详情"""
+ conn = sqlite3.connect(DB_PATH)
+ conn.row_factory = sqlite3.Row
+ c = conn.cursor()
+ c.execute("SELECT * FROM knowledge_cards WHERE id=?", (card_id,))
+ row = c.fetchone()
+ conn.close()
+ if row:
+ return jsonify({"code": 0, "data": dict(row)})
+ return jsonify({"code": -1, "msg": "卡片不存在"})
+
+
+@app.route("/api/favorites", methods=["GET", "POST", "DELETE"])
+def api_favorites():
+ """收藏管理"""
+ user_id = request.args.get("user_id") or (request.get_json() or {}).get("user_id", "guest")
+
+ if request.method == "GET":
+ target_type = request.args.get("type", "")
+ conn = sqlite3.connect(DB_PATH)
+ conn.row_factory = sqlite3.Row
+ c = conn.cursor()
+ if target_type:
+ c.execute("SELECT * FROM favorites WHERE user_id=? AND target_type=? ORDER BY created_at DESC", (user_id, target_type))
+ else:
+ c.execute("SELECT * FROM favorites WHERE user_id=? ORDER BY created_at DESC", (user_id,))
+ rows = [dict(r) for r in c.fetchall()]
+ conn.close()
+ return jsonify({"code": 0, "data": rows})
+
+ elif request.method == "POST":
+ data = request.get_json()
+ target_type = data.get("target_type", "video")
+ target_id = data.get("target_id", 0)
+ title = data.get("title", "")
+ cover = data.get("cover", "")
+ conn = sqlite3.connect(DB_PATH)
+ c = conn.cursor()
+ try:
+ c.execute(
+ "INSERT OR IGNORE INTO favorites (user_id, target_type, target_id, title, cover) VALUES (?, ?, ?, ?, ?)",
+ (user_id, target_type, target_id, title, cover)
+ )
+ conn.commit()
+ added = c.rowcount > 0
+ except Exception:
+ added = False
+ conn.close()
+ return jsonify({"code": 0, "msg": "collected" if added else "already_exists"})
+
+ elif request.method == "DELETE":
+ data = request.get_json()
+ target_type = data.get("target_type", "video")
+ target_id = data.get("target_id", 0)
+ conn = sqlite3.connect(DB_PATH)
+ c = conn.cursor()
+ c.execute("DELETE FROM favorites WHERE user_id=? AND target_type=? AND target_id=?", (user_id, target_type, target_id))
+ conn.commit()
+ conn.close()
+ return jsonify({"code": 0, "msg": "removed"})
+
+
+@app.route("/api/favorites/check")
+def api_favorites_check():
+ """批量检查收藏状态"""
+ user_id = request.args.get("user_id", "guest")
+ target_type = request.args.get("type", "video")
+ ids_str = request.args.get("ids", "")
+ if not ids_str:
+ return jsonify({"code": 0, "data": {}})
+ ids = [int(x) for x in ids_str.split(",") if x.strip().isdigit()]
+ if not ids:
+ return jsonify({"code": 0, "data": {}})
+ conn = sqlite3.connect(DB_PATH)
+ c = conn.cursor()
+ placeholders = ",".join("?" * len(ids))
+ c.execute(f"SELECT target_id FROM favorites WHERE user_id=? AND target_type=? AND target_id IN ({placeholders})",
+ [user_id, target_type] + ids)
+ favorited = {row[0]: True for row in c.fetchall()}
+ conn.close()
+ return jsonify({"code": 0, "data": favorited})
+
+
+@app.route("/api/history", methods=["GET", "POST"])
+def api_history():
+ """观看历史"""
+ user_id = request.args.get("user_id") or (request.get_json() or {}).get("user_id", "guest")
+
+ if request.method == "GET":
+ target_type = request.args.get("type", "")
+ limit = int(request.args.get("limit", 20))
+ conn = sqlite3.connect(DB_PATH)
+ conn.row_factory = sqlite3.Row
+ c = conn.cursor()
+ if target_type:
+ c.execute("SELECT * FROM watch_history WHERE user_id=? AND target_type=? ORDER BY watched_at DESC LIMIT ?", (user_id, target_type, limit))
+ else:
+ c.execute("SELECT * FROM watch_history WHERE user_id=? ORDER BY watched_at DESC LIMIT ?", (user_id, limit))
+ rows = [dict(r) for r in c.fetchall()]
+ conn.close()
+ return jsonify({"code": 0, "data": rows})
+
+ elif request.method == "POST":
+ data = request.get_json()
+ target_type = data.get("target_type", "video")
+ target_id = data.get("target_id", 0)
+ title = data.get("title", "")
+ cover = data.get("cover", "")
+ conn = sqlite3.connect(DB_PATH)
+ c = conn.cursor()
+ c.execute(
+ "INSERT INTO watch_history (user_id, target_type, target_id, title, cover) VALUES (?, ?, ?, ?, ?)",
+ (user_id, target_type, target_id, title, cover)
+ )
+ conn.commit()
+ conn.close()
+ return jsonify({"code": 0, "msg": "recorded"})
+
+
+@app.route("/api/user/profile", methods=["GET", "POST"])
+def api_user_profile():
+ """用户个人资料"""
+ user_id = request.args.get("user_id") or (request.get_json() or {}).get("user_id", "guest")
+
+ if request.method == "GET":
+ conn = sqlite3.connect(DB_PATH)
+ conn.row_factory = sqlite3.Row
+ c = conn.cursor()
+ c.execute("SELECT * FROM users WHERE user_id=?", (user_id,))
+ row = c.fetchone()
+ if not row:
+ c.execute("INSERT INTO users (user_id) VALUES (?)", (user_id,))
+ conn.commit()
+ c.execute("SELECT * FROM users WHERE user_id=?", (user_id,))
+ row = c.fetchone()
+ conn.close()
+ return jsonify({"code": 0, "data": dict(row)})
+
+ elif request.method == "POST":
+ data = request.get_json()
+ nickname = data.get("nickname", "")
+ avatar_emoji = data.get("avatar_emoji", "")
+ avatar_color = data.get("avatar_color", "")
+ conn = sqlite3.connect(DB_PATH)
+ c = conn.cursor()
+ c.execute("INSERT INTO users (user_id) VALUES (?) ON CONFLICT(user_id) DO NOTHING", (user_id,))
+ if nickname:
+ c.execute("UPDATE users SET nickname=? WHERE user_id=?", (nickname, user_id))
+ if avatar_emoji:
+ c.execute("UPDATE users SET avatar_emoji=? WHERE user_id=?", (avatar_emoji, user_id))
+ if avatar_color:
+ c.execute("UPDATE users SET avatar_color=? WHERE user_id=?", (avatar_color, user_id))
+ conn.commit()
+ conn.close()
+ return jsonify({"code": 0, "msg": "updated"})
+
+
+@app.route("/api/user/stats")
+def api_user_stats():
+ """用户统计数据"""
+ user_id = request.args.get("user_id", "guest")
+ conn = sqlite3.connect(DB_PATH)
+ c = conn.cursor()
+ c.execute("SELECT COUNT(*) FROM watch_history WHERE user_id=?", (user_id,))
+ watch_count = c.fetchone()[0] or 0
+ c.execute("SELECT COUNT(*) FROM favorites WHERE user_id=?", (user_id,))
+ fav_count = c.fetchone()[0] or 0
+ conn.close()
+ return jsonify({"code": 0, "data": {"watch_count": watch_count, "favorite_count": fav_count}})
+
+
+# ---- 工具函数 ----
+def _log_behavior(student_id, action_type, target_id="", detail=""):
+ """内部函数:写行为日志"""
+ try:
+ conn = sqlite3.connect(DB_PATH)
+ c = conn.cursor()
+ c.execute(
+ "INSERT INTO behavior_log (user_id, action, target, detail) VALUES (?, ?, ?, ?)",
+ (student_id, action_type, target_id, detail)
+ )
+ conn.commit()
+ conn.close()
+ except Exception:
+ pass # 日志写入失败不阻塞主流程
+
+
+# ---- 预设学生数据(模拟学校导入) ----
+def seed_students():
+ """插入预设学生,模拟'三下乡'时学校提供的名单"""
+ conn = sqlite3.connect(DB_PATH)
+ c = conn.cursor()
+ c.execute("SELECT COUNT(*) FROM students")
+ if c.fetchone()[0] == 0:
+ students = [
+ ("2024001", "张三", "七年级(1)班", "7"),
+ ("2024002", "李四", "七年级(1)班", "7"),
+ ("2024003", "王五", "七年级(2)班", "7"),
+ ("2024004", "赵六", "八年级(1)班", "8"),
+ ("2024005", "陈七", "八年级(1)班", "8"),
+ ("2024006", "刘八", "九年级(1)班", "9"),
+ ]
+ c.executemany(
+ "INSERT INTO students (student_id, name, class_name, grade) VALUES (?, ?, ?, ?)",
+ students
+ )
+ conn.commit()
+ conn.close()
+
+
+# ============================================================
+# MathBTI — 数学人格测试
+# ============================================================
+
+@app.route("/mathbti")
+def mathbti_page():
+ """MathBTI 独立页面 — 可分享链接"""
+ return render_template("mathbti.html")
+
+
+@app.route("/api/math_personality")
+def api_math_personality():
+ """获取数学人格测试题目数据"""
+ if not os.path.exists(MATHBTI_PATH):
+ return jsonify({"code": -1, "msg": "测试数据文件不存在"})
+ with open(MATHBTI_PATH, "r", encoding="utf-8") as f:
+ data = json.load(f)
+ return jsonify({"code": 0, "data": data})
+
+
+@app.route("/api/math_personality/calculate", methods=["POST"])
+def api_math_personality_calculate():
+ """
+ 根据用户答案计算结果
+ 请求格式: { "answers": [0,3,2,1,0,3,2,1,0,3,2,1] } # 12个得分值
+ 返回: { code: 0, data: { binary_code: "0101", result: {...}, axis_scores: {...} } }
+ """
+ req_data = request.get_json()
+ answers = req_data.get("answers", []) if req_data else []
+
+ if not os.path.exists(MATHBTI_PATH):
+ return jsonify({"code": -1, "msg": "测试数据文件不存在"})
+
+ with open(MATHBTI_PATH, "r", encoding="utf-8") as f:
+ test_data = json.load(f)
+
+ if len(answers) != 12:
+ return jsonify({"code": -1, "msg": "需要12个答案"})
+
+ questions = test_data.get("questions", [])
+ axes = test_data.get("scoring", {}).get("axes", ["style", "purpose", "era", "social"])
+ cutoff = test_data.get("scoring", {}).get("cutoff", 4)
+
+ # 计算每轴总分
+ axis_scores = {ax: 0 for ax in axes}
+ for i, q in enumerate(questions):
+ ax = q.get("axis", "")
+ score = answers[i] if i < len(answers) else 0
+ if ax in axis_scores:
+ axis_scores[ax] += score
+
+ # 生成二进制码
+ binary_code = ""
+ for ax in axes:
+ total = axis_scores.get(ax, 0)
+ binary_code += "1" if total > cutoff else "0"
+
+ # 获取结果
+ results = test_data.get("results", {})
+ result = results.get(binary_code, results.get("0000", {}))
+
+ return jsonify({
+ "code": 0,
+ "data": {
+ "binary_code": binary_code,
+ "result": result,
+ "axis_scores": axis_scores
+ }
+ })
+
+
+# ============================================================
+# 模块 3: 应用启动入口
+# ============================================================
+if __name__ == "__main__":
+ print("=" * 50)
+ print(" 数学桥梁计划 · MVP 后端启动中...")
+ print("=" * 50)
+ init_db()
+ load_seed_videos()
+ load_seed_knowledge()
+ seed_students()
+ print(f"\n 访问地址: http://127.0.0.1:5000\n")
+ app.run(debug=False, host="0.0.0.0", port=5000)
diff --git a/data/miniapp_v3.db b/data/miniapp_v3.db
new file mode 100644
index 0000000..e2156b5
Binary files /dev/null and b/data/miniapp_v3.db differ
diff --git a/data/seed_alumni.json b/data/seed_alumni.json
new file mode 100644
index 0000000..ddb8a26
--- /dev/null
+++ b/data/seed_alumni.json
@@ -0,0 +1,20 @@
+{
+ "version": 1,
+ "meta": {
+ "status": "reserved",
+ "story_style": "linear_no_choice",
+ "five_dimensions": ["眼光", "人文", "侦探", "建模", "联结"],
+ "description": "数学校友访谈与线性剧情 Skill 剧本预留数据",
+ "fields": [
+ "id",
+ "name",
+ "graduation_year",
+ "major",
+ "career",
+ "interview_url",
+ "skill",
+ "four_years"
+ ]
+ },
+ "alumni": []
+}
diff --git a/data/seed_mathbti.json b/data/seed_mathbti.json
new file mode 100644
index 0000000..ee80c70
--- /dev/null
+++ b/data/seed_mathbti.json
@@ -0,0 +1,811 @@
+{
+ "title": "数学人格测试",
+ "subtitle": "12题发现你的数学人格类型",
+ "share_text": "我的数学人格是「{name}」!来测测你是哪种数学灵魂?",
+ "version": "2.0",
+ "axes": [
+ {
+ "id": "style",
+ "name": "直觉流 ↔ 抠细节",
+ "left": "直觉流",
+ "right": "抠细节",
+ "emoji_left": "🔮",
+ "emoji_right": "🔍",
+ "desc": "你靠直觉还是靠严谨面对数学?"
+ },
+ {
+ "id": "purpose",
+ "name": "推演派 ↔ 算账派",
+ "left": "推演派",
+ "right": "算账派",
+ "emoji_left": "📐",
+ "emoji_right": "💰",
+ "desc": "你更爱数学本身,还是更爱数学的用处?"
+ },
+ {
+ "id": "era",
+ "name": "古典派 ↔ 现代派",
+ "left": "古典派",
+ "right": "现代派",
+ "emoji_left": "📜",
+ "emoji_right": "🤖",
+ "desc": "你偏爱经典数学还是前沿科技?"
+ },
+ {
+ "id": "social",
+ "name": "独刷党 ↔ 讲题党",
+ "left": "独刷党",
+ "right": "讲题党",
+ "emoji_left": "🎧",
+ "emoji_right": "🔥",
+ "desc": "你更喜欢独自钻研还是交流讨论?"
+ }
+ ],
+ "clans": {
+ "believer": {
+ "name": "信仰者",
+ "english": "Believer",
+ "color": "#4A90D9",
+ "description": "把数学当信仰的人——不为名不为利,只为真理本身"
+ },
+ "spreader": {
+ "name": "传播者",
+ "english": "Spreader",
+ "color": "#D4A574",
+ "description": "把数学当语言的人——让不懂数学的人也能看见数学之美"
+ },
+ "applier": {
+ "name": "应用者",
+ "english": "Applier",
+ "color": "#3A6B8C",
+ "description": "把数学当工具的人——用数学解决真实世界的问题"
+ },
+ "seer": {
+ "name": "直觉者",
+ "english": "Seer",
+ "color": "#7C5CBF",
+ "description": "把数学当直觉的人——看见别人看不见的结构与联系"
+ }
+ },
+ "questions": [
+ {
+ "id": 1,
+ "axis": "style",
+ "question": "考试还剩5分钟,你发现最后一道大题有个数字好像写错了。你会?",
+ "options": [
+ {
+ "text": "快速扫一眼逻辑链,感觉不对劲就改",
+ "score": 1
+ },
+ {
+ "text": "从头全部重算,不确认无误绝不交卷",
+ "score": 3
+ },
+ {
+ "text": "直接涂改,相信第六感——改对改错都是命",
+ "score": 0
+ },
+ {
+ "text": "把关键那几步重新心算一遍,确认再改",
+ "score": 2
+ }
+ ]
+ },
+ {
+ "id": 5,
+ "axis": "style",
+ "question": "正在刷一套卷子的选择题,你通常怎么选?",
+ "options": [
+ {
+ "text": "先排除一两个明显不靠谱的,剩下的凭感觉",
+ "score": 1
+ },
+ {
+ "text": "把每个选项都代进去验一下,不跳过任何一步",
+ "score": 2
+ },
+ {
+ "text": "必须四个选项全算完,才敢下手勾答案",
+ "score": 3
+ },
+ {
+ "text": "扫一眼选项,直觉跳出来就是C,直接下一题",
+ "score": 0
+ }
+ ]
+ },
+ {
+ "id": 9,
+ "axis": "style",
+ "question": "做几何证明题时,你的第一反应更接近?",
+ "options": [
+ {
+ "text": "先凭经验猜一个可能的方向,再往下推",
+ "score": 1
+ },
+ {
+ "text": "脑子里直接「看到」辅助线该画哪,说不出为什么",
+ "score": 0
+ },
+ {
+ "text": "把所有相关定理列出来,逐条排查哪个能用",
+ "score": 3
+ },
+ {
+ "text": "从已知条件出发,一步步推导,不跳步",
+ "score": 2
+ }
+ ]
+ },
+ {
+ "id": 2,
+ "axis": "purpose",
+ "question": "数学老师问「如何用最少材料做出最大容量的盒子」,你脑子里先出现的是?",
+ "options": [
+ {
+ "text": "这不是外卖包装盒的设计问题吗?生活中真遇到过",
+ "score": 2
+ },
+ {
+ "text": "函数的极值点——这题本质是求导找最优解",
+ "score": 0
+ },
+ {
+ "text": "直接搜「最优容器形状」看有没有现成结论拿来用",
+ "score": 3
+ },
+ {
+ "text": "先设未知数、列方程、求导——标准流程走一套",
+ "score": 1
+ }
+ ]
+ },
+ {
+ "id": 6,
+ "axis": "purpose",
+ "question": "朋友真心问你:「学数学到底有什么用?」你本能想怎么回答?",
+ "options": [
+ {
+ "text": "「数学才是最好的薅羊毛工具,从理财到砍价,全都是数学」",
+ "score": 3
+ },
+ {
+ "text": "「数学和诗歌一样,不需要『有用』,它是人类思维的巅峰」",
+ "score": 0
+ },
+ {
+ "text": "「数学能让你看懂房贷利率怎么算、双11折扣是不是真便宜」",
+ "score": 2
+ },
+ {
+ "text": "「数学训练的是逻辑推理能力,这种能力做任何事都有用」",
+ "score": 1
+ }
+ ]
+ },
+ {
+ "id": 10,
+ "axis": "purpose",
+ "question": "如果上天赐你一种「顶级数学能力」,你更想要哪个?",
+ "options": [
+ {
+ "text": "能用数学模型让一家公司效率翻倍的实战力",
+ "score": 2
+ },
+ {
+ "text": "能证明困扰数学家百年未解难题的推演力",
+ "score": 1
+ },
+ {
+ "text": "能凭空创造一套全新数学体系的天才直觉",
+ "score": 0
+ },
+ {
+ "text": "能在股市里靠数学模型持续赚到真金白银",
+ "score": 3
+ }
+ ]
+ },
+ {
+ "id": 3,
+ "axis": "era",
+ "question": "如果必须选一本「数学书」陪你度过周末,你会选?",
+ "options": [
+ {
+ "text": "直接打开GitHub跑一个最新开源模型的代码——书太慢了",
+ "score": 3
+ },
+ {
+ "text": "《数学简史》——了解数学思想的来龙去脉",
+ "score": 1
+ },
+ {
+ "text": "欧几里得《几何原本》——经典永不过时",
+ "score": 0
+ },
+ {
+ "text": "《深度学习的数学》——紧跟AI时代不掉队",
+ "score": 2
+ }
+ ]
+ },
+ {
+ "id": 7,
+ "axis": "era",
+ "question": "如果给你一次数学相关的「穿越」机会,你最想去?",
+ "options": [
+ {
+ "text": "20世纪中叶,参与第一台电子计算机的数学建模",
+ "score": 2
+ },
+ {
+ "text": "去未来50年,看看AI已经把数学推到了什么高度",
+ "score": 3
+ },
+ {
+ "text": "古希腊学院,跟毕达哥拉斯学派一起研究「万物皆数」",
+ "score": 0
+ },
+ {
+ "text": "17世纪,见证牛顿和莱布尼茨的微积分发明大战",
+ "score": 1
+ }
+ ]
+ },
+ {
+ "id": 11,
+ "axis": "era",
+ "question": "大学选数学社团,以下哪个最吸引你?",
+ "options": [
+ {
+ "text": "「AI数学前沿社」——研究大模型背后的数学原理",
+ "score": 3
+ },
+ {
+ "text": "「数据科学工坊」——用Python做数据分析和可视化",
+ "score": 2
+ },
+ {
+ "text": "「数学史研究会」——探索数学思想千年演变",
+ "score": 1
+ },
+ {
+ "text": "「欧几里得读书会」——每周研读数学经典原著",
+ "score": 0
+ }
+ ]
+ },
+ {
+ "id": 4,
+ "axis": "social",
+ "question": "周末下午,理想的数学学习方式是?",
+ "options": [
+ {
+ "text": "戴上降噪耳机,一个人沉浸式刷题,谁也别打扰",
+ "score": 0
+ },
+ {
+ "text": "约上三五好友去图书馆,边讨论边学,学完去撸串",
+ "score": 3
+ },
+ {
+ "text": "找个安静的角落自己学,偶尔瞄一眼群消息",
+ "score": 1
+ },
+ {
+ "text": "和朋友约线上连麦,各学各的但互相陪伴",
+ "score": 2
+ }
+ ]
+ },
+ {
+ "id": 8,
+ "axis": "social",
+ "question": "碰上一道完全不会的题,你的本能反应是?",
+ "options": [
+ {
+ "text": "死磕!不查答案不求助,自己解出来才有快感",
+ "score": 0
+ },
+ {
+ "text": "搜一下解题思路,然后自己照着推一遍",
+ "score": 2
+ },
+ {
+ "text": "先自己硬啃半小时,实在不行再看提示",
+ "score": 1
+ },
+ {
+ "text": "直接拍照发学习群:「大佬们救命!在线等!」",
+ "score": 3
+ }
+ ]
+ },
+ {
+ "id": 12,
+ "axis": "social",
+ "question": "如果数学是一道菜,你觉得它更像?",
+ "options": [
+ {
+ "text": "需要独自静心品味的怀石料理——一口一层深意",
+ "score": 0
+ },
+ {
+ "text": "跟饭搭子分享的家常小炒——边吃边聊才有味",
+ "score": 2
+ },
+ {
+ "text": "大家围在一起涮的热闹火锅——谁都能夹一筷子",
+ "score": 3
+ },
+ {
+ "text": "自己在家慢炖的老火汤——时间越久越有味道",
+ "score": 1
+ }
+ ]
+ }
+ ],
+ "results": {
+ "1000": {
+ "name": "定理工匠",
+ "icon": "⊙",
+ "icon_bg": "#DCE8F5",
+ "clan": "believer",
+ "clan_name": "信仰者",
+ "mathematician": "高斯",
+ "keywords": [
+ "严谨",
+ "古典",
+ "独刷",
+ "推演"
+ ],
+ "short_desc": "严谨推导 + 古典底蕴 + 独立钻研 + 纯粹热爱",
+ "description": "你像高斯一样,是数学世界的完美主义工匠。每一步推导都必须严丝合缝,每一个定理都要亲自证明过才放心。你学数学不是为了「有用」,而是因为数学本身就是目的——那完美的逻辑链条,就是世界上最美的东西。",
+ "tagline": "你不出手则已,一出手就是一篇完美的证明",
+ "color": "#4A90D9",
+ "bio": "高斯被誉为「数学王子」。他3岁纠正父亲账目,10岁秒算1到100。他的性格极度追求完美,很多重大发现因为觉得「不够优雅」而锁在抽屉里从未发表。",
+ "bio_fact": "高斯临终前嘱咐,要在墓碑上刻一个正十七边形——这是他19岁时的杰作,也是他一生完美主义的起点。",
+ "portrait": "/static/img/mathematicians/1000.png",
+ "stats5": {
+ "眼光": 78,
+ "人文": 76,
+ "侦探": 79,
+ "建模": 81,
+ "联结": 81
+ }
+ },
+ "1010": {
+ "name": "穷竭守尺人",
+ "icon": "π",
+ "icon_bg": "#F0E5D8",
+ "clan": "believer",
+ "clan_name": "信仰者",
+ "mathematician": "祖冲之",
+ "keywords": [
+ "严谨",
+ "古典",
+ "独刷",
+ "推演"
+ ],
+ "short_desc": "严谨推导 + 古典底蕴 + 独立钻研 + 精确至上",
+ "description": "你像祖冲之一样,是数学世界的守尺人。你愿意花一辈子把一件事算到最准——不是为了名,不是为了利,而是因为「这个数字必须是对的」。在一个人人追求速成的时代,你选择最慢的方式,走最远的路。",
+ "tagline": "你用算筹对抗时间,一千年后还有人记得你算的那个数",
+ "color": "#C47F3A",
+ "bio": "祖冲之把圆周率算到小数点后七位,这个纪录压了全世界九百多年。他还造了《大明历》,测得回归年仅差46秒。他一生只在做一件事:再乱的时代里,把数字算到最准。",
+ "bio_fact": "他给出的密率355/113,漂亮得不像手算时代的产物——直到16世纪欧洲人才重新发现它。",
+ "portrait": "/static/img/mathematicians/1010.png",
+ "stats5": {
+ "眼光": 80,
+ "人文": 75,
+ "侦探": 76,
+ "建模": 81,
+ "联结": 77
+ }
+ },
+ "1001": {
+ "name": "公式狂僧",
+ "icon": "∑",
+ "icon_bg": "#F0E5D8",
+ "clan": "believer",
+ "clan_name": "信仰者",
+ "mathematician": "欧拉",
+ "keywords": [
+ "严谨",
+ "古典",
+ "独刷",
+ "推演"
+ ],
+ "short_desc": "严谨推导 + 古典底蕴 + 独立钻研 + 永不停歇",
+ "description": "你像欧拉一样,是数学世界里的永动机。你不是因为「想赢」才写论文,而是因为「停不下来」——数学对你来说不是职业,是呼吸。失明了反而算得更快,因为不用看纸,直接在脑子里算。",
+ "tagline": "你停止计算的那一刻,才是你停止生命的那一刻",
+ "color": "#C47F3A",
+ "bio": "欧拉一生写了886篇论文,死后还留下了足够出版50年的手稿。他晚年双目失明后,产出反而翻倍——全部靠心算口述。",
+ "bio_fact": "他解了柯尼斯堡七桥问题,顺手发明了图论——今天你用的导航App,底层都有图论的影子。",
+ "portrait": "/static/img/mathematicians/1001.png",
+ "stats5": {
+ "眼光": 81,
+ "人文": 78,
+ "侦探": 77,
+ "建模": 79,
+ "联结": 81
+ }
+ },
+ "1011": {
+ "name": "抽象代数宗师",
+ "icon": "⬡",
+ "icon_bg": "#ECE0F0",
+ "clan": "believer",
+ "clan_name": "信仰者",
+ "mathematician": "诺特",
+ "keywords": [
+ "严谨",
+ "现代",
+ "讲题",
+ "推演"
+ ],
+ "short_desc": "严谨推导 + 前沿视野 + 善于传授 + 结构之美",
+ "description": "你像诺特一样,是抽象世界的建筑大师。你不在乎「这个定理能用来干嘛」——你在乎的是「这个结构是不是美的」。被时代否定三次,你不在乎,因为数学永远站你这边。",
+ "tagline": "你把最抽象的数学,讲得连门外汉都想入门",
+ "color": "#8E5EA0",
+ "bio": "诺特被爱因斯坦称为「自女性开始接受高等教育以来最杰出的数学天才」。她的诺特定理是现代物理学的基石。",
+ "bio_fact": "她在哥廷根讲课的头几年因为性别拿不到薪水。她不在乎,继续免费教书——她的学生后来成为了一整代「诺特学派」。",
+ "portrait": "/static/img/mathematicians/1011.png",
+ "stats5": {
+ "眼光": 78,
+ "人文": 80,
+ "侦探": 80,
+ "建模": 76,
+ "联结": 78
+ }
+ },
+ "1100": {
+ "name": "商路算郎",
+ "icon": "≈",
+ "icon_bg": "#EDE5D8",
+ "clan": "spreader",
+ "clan_name": "传播者",
+ "mathematician": "斐波那契",
+ "keywords": [
+ "严谨",
+ "古典",
+ "讲题",
+ "应用"
+ ],
+ "short_desc": "严谨推导 + 古典底蕴 + 善于传授 + 跨越文化",
+ "description": "你像斐波那契一样,是行走在东西方之间的数学翻译者。你学数学不是为了自己懂,是为了让别人也懂——你把阿拉伯数字讲给欧洲商人,改变了整个西方计算史。",
+ "tagline": "你用数学走通了丝绸之路",
+ "color": "#B8956E",
+ "bio": "斐波那契是13世纪意大利商人。他随父亲在北非经商时接触到阿拉伯数字,立刻意识到这比罗马数字好用太多。他写了《算盘书》,把阿拉伯数字引入欧洲。",
+ "bio_fact": "著名的兔子繁殖问题只是他书中的一道练习题——他本人大概没想到,这道题后来变成了数学史上最著名的数列。",
+ "portrait": "/static/img/mathematicians/1100.png",
+ "stats5": {
+ "眼光": 79,
+ "人文": 81,
+ "侦探": 75,
+ "建模": 80,
+ "联结": 76
+ }
+ },
+ "1110": {
+ "name": "双法布道者",
+ "icon": "△",
+ "icon_bg": "#F0E5D8",
+ "clan": "spreader",
+ "clan_name": "传播者",
+ "mathematician": "华罗庚",
+ "keywords": [
+ "严谨",
+ "现代",
+ "讲题",
+ "应用"
+ ],
+ "short_desc": "严谨推导 + 现代视野 + 善于传授 + 人间烟火",
+ "description": "你像华罗庚一样,是数学世界的布道者。你揣着粉笔走遍中国,把优选法讲给工人农民——你相信数学不属于象牙塔,属于每一个人。",
+ "tagline": "你把数学从大学讲台,讲到了田间地头",
+ "color": "#C47F3A",
+ "bio": "华罗庚左腿残疾、初中文凭、杂货铺出身,却成为二十世纪中国最伟大的数学家。他走遍23个省市区,给工人和农民演示「优选法」和「统筹法」。",
+ "bio_fact": "1985年他在东京大学做完学术报告后心脏病发,倒在讲台上——他说的最后一句话是「我要工作到生命的最后一刻」。",
+ "portrait": "/static/img/mathematicians/1110.png",
+ "stats5": {
+ "眼光": 78,
+ "人文": 81,
+ "侦探": 77,
+ "建模": 81,
+ "联结": 76
+ }
+ },
+ "1101": {
+ "name": "决斗狂热者",
+ "icon": "△",
+ "icon_bg": "#D8EDE5",
+ "clan": "spreader",
+ "clan_name": "传播者",
+ "mathematician": "伽罗瓦",
+ "keywords": [
+ "严谨",
+ "现代",
+ "独刷",
+ "应用"
+ ],
+ "short_desc": "严谨推导 + 前沿视野 + 独立钻研 + 死前传播",
+ "description": "你像伽罗瓦一样,是数学界的浪漫主义者。你头脑中有整个世界都尚未理解的真理,决斗前夜你通宵写信,把群论塞进一封信里扔给未来——你用死亡完成了最后一次传播。",
+ "tagline": "在决斗的前一晚,你通宵写下了改变数学史的所有想法",
+ "color": "#3D7A6E",
+ "bio": "伽罗瓦17岁奠定群论基础,20岁死于决斗。决斗前夜他通宵写信,在页边疯狂写道「我没有时间了」——那些草稿后来成为现代代数学的基石。",
+ "bio_fact": "伽罗瓦的手稿被弟弟保存了14年才被刘维尔发现。今天「伽罗瓦理论」是每个数学系研究生的必修课。",
+ "portrait": "/static/img/mathematicians/1101.png",
+ "stats5": {
+ "眼光": 78,
+ "人文": 80,
+ "侦探": 81,
+ "建模": 75,
+ "联结": 80
+ }
+ },
+ "1111": {
+ "name": "织机先知",
+ "icon": "⊗",
+ "icon_bg": "#F0E0E8",
+ "clan": "spreader",
+ "clan_name": "传播者",
+ "mathematician": "Ada Lovelace",
+ "keywords": [
+ "严谨",
+ "现代",
+ "讲题",
+ "应用"
+ ],
+ "short_desc": "严谨推导 + 前沿视野 + 善于传授 + 人机翻译",
+ "description": "你像Ada Lovelace一样,是数学与计算交汇处的先知。你写出了人类历史上第一个算法——在计算机还没诞生之前,你就已经把数学讲给了机器听。",
+ "tagline": "你在计算机还没诞生之前,就已经写出了第一个算法",
+ "color": "#9B4D73",
+ "bio": "Ada Lovelace是诗人拜伦的女儿。她在1843年为巴贝奇的分析机写了史上第一个程序,并预言计算机将来不仅能算数,还能作曲、画画。",
+ "bio_fact": "她的名字被用来命名了Ada编程语言。每年十月的第二个星期二被定为「Ada Lovelace日」。",
+ "portrait": "/static/img/mathematicians/1111.png",
+ "stats5": {
+ "眼光": 80,
+ "人文": 79,
+ "侦探": 76,
+ "建模": 77,
+ "联结": 81
+ }
+ },
+ "0100": {
+ "name": "大衍谋士",
+ "icon": "⊕",
+ "icon_bg": "#D8E5F0",
+ "clan": "applier",
+ "clan_name": "应用者",
+ "mathematician": "秦九韶",
+ "keywords": [
+ "直觉",
+ "古典",
+ "独刷",
+ "应用"
+ ],
+ "short_desc": "直觉驱动 + 古典底蕴 + 独立钻研 + 数学武器",
+ "description": "你像秦九韶一样,是数学世界的谋士。你提出中国剩余定理比高斯早五百年——但你也用数学杀人。对你来说,数学不是信仰,是力量;不是美,是武器。",
+ "tagline": "你用数学平叛,也用数学杀人——数学是你的刀",
+ "color": "#3A6B8C",
+ "bio": "秦九韶是南宋全才,精音律、善诗文、懂建筑、会带兵。他写成《数书九章》,提出大衍求一术(中国剩余定理),比高斯早五百年。",
+ "bio_fact": "史载他「性机巧、好奢侈」,曾毒杀亲信——数学家说他空前绝后,史官说他品行不端。",
+ "portrait": "/static/img/mathematicians/0100.png",
+ "stats5": {
+ "眼光": 78,
+ "人文": 76,
+ "侦探": 80,
+ "建模": 81,
+ "联结": 76
+ }
+ },
+ "0110": {
+ "name": "宇宙立法者",
+ "icon": "⊕",
+ "icon_bg": "#D8E5F0",
+ "clan": "applier",
+ "clan_name": "应用者",
+ "mathematician": "牛顿",
+ "keywords": [
+ "直觉",
+ "古典",
+ "独刷",
+ "应用"
+ ],
+ "short_desc": "直觉驱动 + 古典底蕴 + 独立钻研 + 征服世界",
+ "description": "你像牛顿一样,是数学世界的立法者。你用微积分统一天地,用万有引力预测行星——但你也用皇家学会打压异己。对你来说,数学是征服世界的剑。",
+ "tagline": "你证明了宇宙的统一,却统一不了自己",
+ "color": "#3A6B8C",
+ "bio": "牛顿1687年出版《自然哲学的数学原理》,人类第一次用三条定律统一了天与地。他与莱布尼茨争夺微积分优先权,自己判自己胜诉。",
+ "bio_fact": "他晚年沉迷炼金术与末日预言,留下数百万字神秘书稿——神性与兽性同体一具肉身。",
+ "portrait": "/static/img/mathematicians/0110.png",
+ "stats5": {
+ "眼光": 80,
+ "人文": 75,
+ "侦探": 78,
+ "建模": 81,
+ "联结": 81
+ }
+ },
+ "0111": {
+ "name": "密码破译使",
+ "icon": "⊡",
+ "icon_bg": "#D8EDE0",
+ "clan": "applier",
+ "clan_name": "应用者",
+ "mathematician": "图灵",
+ "keywords": [
+ "直觉",
+ "现代",
+ "独刷",
+ "应用"
+ ],
+ "short_desc": "直觉驱动 + 前沿视野 + 独立钻研 + 数学救国",
+ "description": "你像图灵一样,是数字世界的破译者。你用数学破译Enigma,拯救了千万生命——但世界不知道你的名字。对你来说,数学是挡在文明前的墙。",
+ "tagline": "你拯救了世界,世界把你碾碎",
+ "color": "#2D6A4F",
+ "bio": "图灵24岁发表《论可计算数》,定义了「计算」本身。二战时他破解Enigma密码,让战争提前两年结束,拯救了上千万生命——这一切作为国家机密封存了三十年。",
+ "bio_fact": "他还是马拉松好手,最好成绩距奥运选拔线仅11分钟。2013年英国女王颁布皇家赦免。",
+ "portrait": "/static/img/mathematicians/0111.png",
+ "stats5": {
+ "眼光": 78,
+ "人文": 79,
+ "侦探": 81,
+ "建模": 81,
+ "联结": 78
+ }
+ },
+ "0101": {
+ "name": "博弈游侠",
+ "icon": "⊕",
+ "icon_bg": "#D8E5F0",
+ "clan": "applier",
+ "clan_name": "应用者",
+ "mathematician": "冯·诺依曼",
+ "keywords": [
+ "直觉",
+ "现代",
+ "讲题",
+ "应用"
+ ],
+ "short_desc": "直觉驱动 + 前沿视野 + 善于传授 + 世界棋盘",
+ "description": "你像冯·诺依曼一样,是游走在多个领域的数学侠客。你把博弈论变成冷战武器,把计算机架构定义成现代标准——对你来说,数学是下棋的手,世界是棋盘。",
+ "tagline": "你把世界算成一盘棋,而且你每把都赢",
+ "color": "#3A6B8C",
+ "bio": "冯·诺依曼8岁掌握微积分,22岁拿下博士学位。他奠定了现代计算机架构、创立了博弈论、参与了曼哈顿计划,还会在派对上心算八位数乘法娱乐朋友。",
+ "bio_fact": "有人问他:「你研究博弈论,是不是因为你喜欢下棋?」他回答:「不,我研究博弈论是为了理解核威慑——棋太简单了。」",
+ "portrait": "/static/img/mathematicians/0101.png",
+ "stats5": {
+ "眼光": 79,
+ "人文": 77,
+ "侦探": 78,
+ "建模": 81,
+ "联结": 81
+ }
+ },
+ "0000": {
+ "name": "几何殉道者",
+ "icon": "◇",
+ "icon_bg": "#F5EBE0",
+ "clan": "seer",
+ "clan_name": "直觉者",
+ "mathematician": "希帕提娅",
+ "keywords": [
+ "直觉",
+ "古典",
+ "讲题",
+ "推演"
+ ],
+ "short_desc": "直觉驱动 + 古典底蕴 + 善于传授 + 理性之光",
+ "description": "你像希帕提娅一样,是几何世界的殉道者。你在几何图形里看见柏拉图的永恒,被暴民撕碎时手里还握着几何讲义——对你来说,数学是理性的光,值得用生命捍卫。",
+ "tagline": "你不是在做题,你是在为理性殉道",
+ "color": "#D4A574",
+ "bio": "希帕提娅是历史上第一位有记载的女数学家,公元4世纪在埃及亚历山大城讲学。415年她被基督徒暴民杀害,标志着古典希腊数学的终结。",
+ "bio_fact": "她改良了星盘和比重计的设计,这两种仪器分别被用了1400多年。",
+ "portrait": "/static/img/mathematicians/0000.png",
+ "stats5": {
+ "眼光": 79,
+ "人文": 81,
+ "侦探": 76,
+ "建模": 77,
+ "联结": 76
+ }
+ },
+ "0010": {
+ "name": "弦图先觉",
+ "icon": "◇",
+ "icon_bg": "#F5EBE0",
+ "clan": "seer",
+ "clan_name": "直觉者",
+ "mathematician": "赵爽",
+ "keywords": [
+ "直觉",
+ "古典",
+ "独刷",
+ "推演"
+ ],
+ "short_desc": "直觉驱动 + 古典底蕴 + 独立钻研 + 无字证明",
+ "description": "你像赵爽一样,是数学世界的视觉诗人。你用一张弦图「看见」勾股定理——不需要推导,看一眼就懂。对你来说,数学不是算出来的,是看出来的。",
+ "tagline": "你用一张图,让中国数学美了一千八百年",
+ "color": "#D4A574",
+ "bio": "赵爽是三国时期吴国数学家,只留下一部《周髀算经注》。但他用一张弦图直观地「看见」了勾股定理——这是中国数学史上最早的无字证明。",
+ "bio_fact": "2002年国际数学家大会的会徽就是他的弦图——一张图比十本书都长寿。",
+ "portrait": "/static/img/mathematicians/0010.png",
+ "stats5": {
+ "眼光": 80,
+ "人文": 76,
+ "侦探": 78,
+ "建模": 75,
+ "联结": 75
+ }
+ },
+ "0001": {
+ "name": "拓扑游方",
+ "icon": "◎",
+ "icon_bg": "#D8ECF0",
+ "clan": "seer",
+ "clan_name": "直觉者",
+ "mathematician": "庞加莱",
+ "keywords": [
+ "直觉",
+ "现代",
+ "独刷",
+ "推演"
+ ],
+ "short_desc": "直觉驱动 + 前沿视野 + 独立钻研 + 高维漫步",
+ "description": "你像庞加莱一样,拥有超凡的几何直觉。你在公交车踏板上一瞬间「看见」富克斯函数——别人需要推导的地方,你只需「看」一眼。",
+ "tagline": "你在高维空间里漫步,而我们还在二维纸上画图",
+ "color": "#5BA0B5",
+ "bio": "庞加莱是最后一位通晓当时所有数学分支的「全才」。他的几何直觉惊人——据说他刚踏上公交车踏板的一瞬间,就想通了富克斯函数的关键构造。",
+ "bio_fact": "他提出的「庞加莱猜想」困扰了数学家整整100年,直到2003年才被佩雷尔曼证明。",
+ "portrait": "/static/img/mathematicians/0001.png",
+ "stats5": {
+ "眼光": 80,
+ "人文": 78,
+ "侦探": 79,
+ "建模": 76,
+ "联结": 81
+ }
+ },
+ "0011": {
+ "name": "人脑计算机",
+ "icon": "⊡",
+ "icon_bg": "#D8EDE0",
+ "clan": "seer",
+ "clan_name": "直觉者",
+ "mathematician": "凯瑟琳·约翰逊",
+ "keywords": [
+ "直觉",
+ "现代",
+ "讲题",
+ "推演"
+ ],
+ "short_desc": "直觉驱动 + 前沿视野 + 善于传授 + 经验直觉",
+ "description": "你像凯瑟琳·约翰逊一样,是计算世界的直觉大师。你在NASA的计算纸堆里「看见」弹道不对——你的直觉比任何公式都快,因为你算了33年,算到直觉成了本能。",
+ "tagline": "你把门推开一条缝,直到墙自己倒掉",
+ "color": "#2D6A4F",
+ "bio": "凯瑟琳是NASA「人脑计算机」,用纸笔和计算尺手算弹道。1962年宇航员格伦临发射前点名:「让那个女孩核对数字——她说行,我就飞。」",
+ "bio_fact": "她101岁辞世,从「有色人种计算员」到登月轨道计算者,她的一生恰好就是计算从纸笔走向AI的整个世纪。",
+ "portrait": "/static/img/mathematicians/0011.png",
+ "stats5": {
+ "眼光": 77,
+ "人文": 80,
+ "侦探": 76,
+ "建模": 81,
+ "联结": 75
+ }
+ }
+ },
+ "scoring": {
+ "axes": [
+ "style",
+ "purpose",
+ "era",
+ "social"
+ ],
+ "questions_per_axis": 3,
+ "max_per_axis": 9,
+ "cutoff": 4,
+ "rule": "每轴3题,每题得分0-3,轴总分0-9。≤4归左极,≥5归右极。",
+ "clan_rule": "style×purpose决定族:style右(严谨)+purpose左(推演)=信仰者,style右+purpose右(算账)=传播者,style左(直觉)+purpose右=应用者,style左+purpose左=直觉者。"
+ }
+}
\ No newline at end of file
diff --git a/data/seed_simulator.json b/data/seed_simulator.json
new file mode 100644
index 0000000..9c79cb5
--- /dev/null
+++ b/data/seed_simulator.json
@@ -0,0 +1,626 @@
+{
+ "name": "数学专业模拟器",
+ "description": "模拟大学数学专业4年全流程,从选课到毕业,体验数学专业学生的真实生活",
+ "start_node": "y1_welcome",
+ "nodes": {
+ "y1_welcome": {
+ "id": "y1_welcome",
+ "year": 1,
+ "stage": "入学",
+ "character": "辅导员",
+ "scene": "恭喜你被理想大学数学系录取!站在校门口,你深吸一口气——接下来的四年,你将与数字、公式和证明为伴。\n\n辅导员走过来,递给你一本《新生手册》:\"欢迎来到数学系!大一最重要的是打好基础。现在你需要选课了。\"",
+ "choices": [
+ {"text": "主要精力攻数学分析(实数理论、极限、微积分)", "next": "y1_analysis", "effects": {"gpa": 2, "interest": 1, "skill": 1}, "hint": "数学分析是基础中的基础"},
+ {"text": "高等代数先行(矩阵、线性空间、特征值)", "next": "y1_algebra", "effects": {"gpa": 1, "interest": 2, "skill": 2}, "hint": "代数锻炼抽象思维"},
+ {"text": "解析几何 + 编程入门双修", "next": "y1_geometry", "effects": {"gpa": 1, "interest": 1, "skill": 3}, "hint": "几何直观 + 计算能力"}
+ ]
+ },
+ "y1_analysis": {
+ "id": "y1_analysis",
+ "year": 1,
+ "stage": "大一上学期",
+ "character": "数学分析教授",
+ "scene": "数学分析课上,教授在黑板上写满了ε-δ语言的定义。\"极限是微积分的核心,而ε-δ语言是理解极限的关键——它告诉你什么叫'无限接近'。\"\n\n下课后,一个高年级学长找你:\"数学建模竞赛要不要组队?大一就能参加,而且非常有趣!\"",
+ "effects": {"gpa": 1},
+ "choices": [
+ {"text": "参加建模竞赛!边学边实战", "next": "y1_modeling", "effects": {"gpa": 1, "interest": 3, "skill": 2}, "hint": "建模是用数学解决真实问题的第一步"},
+ {"text": "先夯实基础,多刷分析题", "next": "y1_foundation", "effects": {"gpa": 3, "interest": 0, "skill": 1}, "hint": "扎实的基础走得更远"}
+ ]
+ },
+ "y1_algebra": {
+ "id": "y1_algebra",
+ "year": 1,
+ "stage": "大一上学期",
+ "character": "高代老师",
+ "scene": "\"线性空间不仅是向量,它背后有一套完整的结构理论。\" 高代老师的课让你感受到抽象思维的魅力。\n\n期中考试后,你的成绩不错。学生会数学社团在招新——\"我们每周有数学讲座和讨论班,来吗?\"",
+ "effects": {"gpa": 2},
+ "choices": [
+ {"text": "加入社团!认识更多数学同好", "next": "y1_club", "effects": {"gpa": 0, "interest": 3, "skill": 1}, "hint": "圈子很重要"},
+ {"text": "一个人深度学习群论初步", "next": "y1_group_theory", "effects": {"gpa": 2, "interest": 1, "skill": 2}, "hint": "群论是现代数学的门槛"}
+ ]
+ },
+ "y1_geometry": {
+ "id": "y1_geometry",
+ "year": 1,
+ "stage": "大一上学期",
+ "character": "编程课助教",
+ "scene": "解析几何课上,你发现用Python画三维曲面图特别炫酷!助教看了你的代码:\"不错啊,有兴趣学数值计算吗?用计算机解偏微分方程是很有前途的方向。\"\n\n你的编程基础让你在两个方向间纠结。",
+ "effects": {"gpa": 2},
+ "choices": [
+ {"text": "深入数值计算方向", "next": "y1_numerical", "effects": {"gpa": 1, "interest": 2, "skill": 3}, "hint": "计算数学是热门交叉方向"},
+ {"text": "先回到纯数学,几何为主", "next": "y1_pure_geo", "effects": {"gpa": 2, "interest": 1, "skill": 1}, "hint": "纯几何培养直觉"}
+ ]
+ },
+ "y1_foundation": {
+ "id": "y1_foundation",
+ "year": 1,
+ "stage": "大一下学期",
+ "character": "导师",
+ "scene": "一个学期的苦练让你的数学分析功底非常扎实。期末成绩出来,你排名前5%。\n\n导师找到你:\"基础很好,大二可以提前接触一些研究内容了。你对哪个方向感兴趣?\"",
+ "effects": {"gpa": 3},
+ "choices": [
+ {"text": "概率论与统计方向", "next": "y2_probability", "effects": {"gpa": 0, "interest": 2, "skill": 1}},
+ {"text": "继续深挖分析学(实变/复变)", "next": "y2_analysis_adv", "effects": {"gpa": 2, "interest": 0, "skill": 2}},
+ {"text": "尝试交叉学科(AI/数据科学)", "next": "y2_cross", "effects": {"gpa": 0, "interest": 3, "skill": 3}}
+ ]
+ },
+ "y1_club": {
+ "id": "y1_club",
+ "year": 1,
+ "stage": "大一下学期",
+ "character": "社团主席",
+ "scene": "社团每周的讨论班让你的视野大开——原来数学有这么多有趣的分支!你第一次听说了拓扑学、图论、密码学……\n\n你开始思考大二的选择方向。",
+ "effects": {"gpa": 1, "interest": 3},
+ "choices": [
+ {"text": "对抽象代数产生兴趣", "next": "y2_algebra_adv", "effects": {"gpa": 0, "interest": 2, "skill": 2}},
+ {"text": "对数学建模产生兴趣", "next": "y2_modeling", "effects": {"gpa": 0, "interest": 2, "skill": 3}},
+ {"text": "对AI和数学交叉感兴趣", "next": "y2_ml_math", "effects": {"gpa": 0, "interest": 3, "skill": 2}}
+ ]
+ },
+ "y1_group_theory": {
+ "id": "y1_group_theory",
+ "year": 1,
+ "stage": "大一下学期",
+ "character": "自学笔记",
+ "scene": "你花了一个寒假啃完了《近世代数导论》。群论让你着迷——特别是群作用、Sylow定理。\n\n开学后,代数老师看了你的笔记:\"不错,你对代数有感觉。大二要不要深入研究?\"",
+ "effects": {"gpa": 2, "interest": 1},
+ "choices": [
+ {"text": "跟着老师做代数研究", "next": "y2_algebra_research", "effects": {"gpa": 1, "interest": 2, "skill": 2}},
+ {"text": "代数+密码学双修", "next": "y2_crypto", "effects": {"gpa": 0, "interest": 3, "skill": 3}},
+ {"text": "回归基础课,全面发展", "next": "y2_balanced", "effects": {"gpa": 2, "interest": 0, "skill": 1}}
+ ]
+ },
+ "y1_numerical": {
+ "id": "y1_numerical",
+ "year": 1,
+ "stage": "大一下学期",
+ "character": "计算数学教授",
+ "scene": "数值计算课上,你第一次接触了有限元方法。\"用计算机模拟物理现象——这是计算数学最迷人的地方。\"\n\n期末考试结束,你的编程能力得到了认可。教授邀请你加入他的暑期研究项目。",
+ "effects": {"gpa": 2, "interest": 2},
+ "choices": [
+ {"text": "加入暑期研究:计算流体力学", "next": "y2_cfd", "effects": {"gpa": 1, "interest": 2, "skill": 3}},
+ {"text": "专心算法与数据结构", "next": "y2_algorithms", "effects": {"gpa": 1, "interest": 0, "skill": 3}},
+ {"text": "回归理论:偏微分方程理论", "next": "y2_pde", "effects": {"gpa": 2, "interest": 1, "skill": 2}}
+ ]
+ },
+ "y1_pure_geo": {
+ "id": "y1_pure_geo",
+ "year": 1,
+ "stage": "大一下学期",
+ "character": "几何老师",
+ "scene": "\"欧几里得的《几何原本》影响了人类两千年的思维方式。\" 你在几何课上感受到了数学之美。不过,你开始怀疑:纯几何的方向会不会太窄?",
+ "effects": {"gpa": 2, "interest": 2},
+ "choices": [
+ {"text": "坚持几何,走上纯数道路", "next": "y2_geometry_pure", "effects": {"gpa": 1, "interest": 2, "skill": 2}},
+ {"text": "几何+计算机图形学", "next": "y2_cg", "effects": {"gpa": 0, "interest": 3, "skill": 3}},
+ {"text": "转向概率统计方向", "next": "y2_probability", "effects": {"gpa": 1, "interest": 2, "skill": 1}}
+ ]
+ },
+ "y1_modeling": {
+ "id": "y1_modeling",
+ "year": 1,
+ "stage": "大一下学期",
+ "character": "建模竞赛队友",
+ "scene": "数学建模竞赛让你第一次用数学解决真实问题——你所在的队伍用微分方程模拟疫情传播,获了省二等奖!\n\n你发现:\"原来数学真的可以改变世界!\"",
+ "effects": {"gpa": 1, "interest": 3},
+ "choices": [
+ {"text": "继续参加更多竞赛", "next": "y2_competition", "effects": {"gpa": 0, "interest": 2, "skill": 3}},
+ {"text": "转做数据科学研究", "next": "y2_data_science", "effects": {"gpa": 0, "interest": 3, "skill": 2}},
+ {"text": "回归理论,但保持建模兴趣", "next": "y2_modeling_theory", "effects": {"gpa": 2, "interest": 1, "skill": 1}}
+ ]
+ },
+ "y2_probability": {
+ "id": "y2_probability",
+ "year": 2,
+ "stage": "大二",
+ "character": "概率论教授",
+ "scene": "\"概率论不是在赌场赢钱,而是在不确定中寻找确定性。\" 大二的专业课让你更深入地理解了随机过程。\n\n你开始思考大三的方向选择。",
+ "effects": {"gpa": 2, "interest": 1},
+ "choices": [
+ {"text": "纯数学方向(测度论、随机分析)", "next": "y3_pure_prob", "effects": {"gpa": 2, "interest": 0, "skill": 2}},
+ {"text": "统计学方向(机器学习、数据分析)", "next": "y3_ml_stats", "effects": {"gpa": 0, "interest": 3, "skill": 3}},
+ {"text": "金融数学方向", "next": "y3_finance", "effects": {"gpa": 1, "interest": 2, "skill": 2}}
+ ]
+ },
+ "y2_analysis_adv": {
+ "id": "y2_analysis_adv",
+ "year": 2,
+ "stage": "大二",
+ "character": "实变函数教授",
+ "scene": "\"实变函数是通往现代分析的必经之路。\" 你沉浸在Lebesgue积分和泛函分析中,虽然难,但很上瘾。\n\n大三选方向时,你的分析功底给了你很多选择。",
+ "effects": {"gpa": 2, "interest": 0},
+ "choices": [
+ {"text": "继续分析学(调和分析、算子理论)", "next": "y3_analysis", "effects": {"gpa": 2, "interest": 1, "skill": 2}},
+ {"text": "分析+应用:偏微分方程", "next": "y3_pde", "effects": {"gpa": 1, "interest": 2, "skill": 3}},
+ {"text": "分析+AI:优化理论", "next": "y3_optimization", "effects": {"gpa": 0, "interest": 3, "skill": 3}}
+ ]
+ },
+ "y2_cross": {
+ "id": "y2_cross",
+ "year": 2,
+ "stage": "大二",
+ "character": "数据科学老师",
+ "scene": "你选了计算机系的机器学习课,发现数学基础对你帮助巨大——SVM的核方法就是泛函分析的应用!\n\n大三面临方向选择。",
+ "effects": {"gpa": 1, "interest": 3},
+ "choices": [
+ {"text": "AI研究方向(深度学习理论)", "next": "y3_dl_theory", "effects": {"gpa": 0, "interest": 3, "skill": 3}},
+ {"text": "回到数学与统计交叉", "next": "y3_ml_stats", "effects": {"gpa": 1, "interest": 2, "skill": 2}},
+ {"text": "纯数+代码双修", "next": "y3_comp_math", "effects": {"gpa": 1, "interest": 1, "skill": 3}}
+ ]
+ },
+ "y2_algebra_adv": {
+ "id": "y2_algebra_adv",
+ "year": 2,
+ "stage": "大二",
+ "character": "代数教授",
+ "scene": "\"群、环、域——代数结构之美,只有深入才能体会。\" 你在交换代数和Galois理论上花了很多时间。\n\n大三选方向。",
+ "effects": {"gpa": 2, "interest": 1},
+ "choices": [
+ {"text": "纯代数研究方向", "next": "y3_algebra", "effects": {"gpa": 2, "interest": 1, "skill": 2}},
+ {"text": "代数+数论/密码学", "next": "y3_number_theory", "effects": {"gpa": 1, "interest": 2, "skill": 1}},
+ {"text": "代数+计算机(编码理论)", "next": "y3_coding", "effects": {"gpa": 0, "interest": 2, "skill": 3}}
+ ]
+ },
+ "y2_modeling": {
+ "id": "y2_modeling",
+ "year": 2,
+ "stage": "大二",
+ "character": "建模教练",
+ "scene": "你继续参加建模竞赛,这次拿了国赛一等奖!\"数学建模是连接理论与现实的桥梁。\"\n\n大三方向选择。",
+ "effects": {"gpa": 1, "interest": 3},
+ "choices": [
+ {"text": "运筹学与优化", "next": "y3_or", "effects": {"gpa": 1, "interest": 2, "skill": 3}},
+ {"text": "数据科学与统计", "next": "y3_ml_stats", "effects": {"gpa": 0, "interest": 3, "skill": 2}},
+ {"text": "计算数学", "next": "y3_comp_math", "effects": {"gpa": 1, "interest": 1, "skill": 3}}
+ ]
+ },
+ "y2_ml_math": {
+ "id": "y2_ml_math",
+ "year": 2,
+ "stage": "大二",
+ "character": "AI导师",
+ "scene": "你开始系统学习机器学习理论——信息论、凸优化、概率图模型。数学系背景让你在理解算法原理时得心应手。\n\n大三方向。",
+ "effects": {"gpa": 1, "interest": 3},
+ "choices": [
+ {"text": "深度学习理论方向", "next": "y3_dl_theory", "effects": {"gpa": 0, "interest": 3, "skill": 3}},
+ {"text": "统计机器学习", "next": "y3_ml_stats", "effects": {"gpa": 1, "interest": 2, "skill": 2}},
+ {"text": "强化学习+博弈论", "next": "y3_rl", "effects": {"gpa": 0, "interest": 3, "skill": 2}}
+ ]
+ },
+ "y2_balanced": {
+ "id": "y2_balanced",
+ "year": 2,
+ "stage": "大二",
+ "character": "学业导师",
+ "scene": "你选择了全面发展路线——专业课均衡,成绩稳定在前20%。虽然没有特别突出的方向,但知识面很广。\n\n大三选方向时,你有很多选择。",
+ "effects": {"gpa": 2, "interest": 0},
+ "choices": [
+ {"text": "应用数学(建模+计算)", "next": "y3_or", "effects": {"gpa": 0, "interest": 2, "skill": 3}},
+ {"text": "统计与数据分析", "next": "y3_ml_stats", "effects": {"gpa": 0, "interest": 3, "skill": 2}},
+ {"text": "纯数学(择一深入)", "next": "y3_analysis", "effects": {"gpa": 2, "interest": 0, "skill": 2}},
+ {"text": "教育数学(教师资格)", "next": "y3_edu", "effects": {"gpa": 0, "interest": 2, "skill": 1}}
+ ]
+ },
+ "y3_pure_prob": {
+ "id": "y3_pure_prob",
+ "year": 3,
+ "stage": "大三",
+ "character": "随机分析教授",
+ "scene": "\"随机微分方程+测度论——这是现代金融数学的理论基础。\" 大三的专业课越来越深入。\n\n大四将至,你该为未来做决定了。",
+ "effects": {"gpa": 2, "interest": 1},
+ "choices": [
+ {"text": "保研/考研——继续深造纯数学", "next": "y4_grad_pure", "effects": {"gpa": 2, "interest": 1, "skill": 1}},
+ {"text": "量化金融实习", "next": "y4_quant_finance", "effects": {"gpa": 0, "interest": 2, "skill": 3}},
+ {"text": "转数据科学实习", "next": "y4_ds_intern", "effects": {"gpa": 0, "interest": 3, "skill": 2}}
+ ]
+ },
+ "y3_ml_stats": {
+ "id": "y3_ml_stats",
+ "year": 3,
+ "stage": "大三",
+ "character": "数据科学家",
+ "scene": "\"数学+代码=超级武器。\" 你在Kaggle竞赛中拿了银牌——数学背景让你在特征工程和模型理解上远超纯CS选手。\n\n大四将至。",
+ "effects": {"gpa": 0, "interest": 3},
+ "choices": [
+ {"text": "AI公司实习——算法岗", "next": "y4_ai_intern", "effects": {"gpa": 0, "interest": 2, "skill": 3}},
+ {"text": "读研——机器学习理论方向", "next": "y4_grad_ml", "effects": {"gpa": 1, "interest": 2, "skill": 1}},
+ {"text": "创业——数学教育产品", "next": "y4_startup", "effects": {"gpa": 0, "interest": 3, "skill": 2}}
+ ]
+ },
+ "y3_finance": {
+ "id": "y3_finance",
+ "year": 3,
+ "stage": "大三",
+ "character": "金融数学教授",
+ "scene": "\"Black-Scholes方程改变了华尔街——而它的核心就是随机微积分。\" 金融数学课让你看到了数学在现实世界的力量。\n\n大四将至。",
+ "effects": {"gpa": 1, "interest": 2},
+ "choices": [
+ {"text": "券商/基金量化实习", "next": "y4_quant_intern", "effects": {"gpa": 0, "interest": 2, "skill": 3}},
+ {"text": "读研——金融工程方向", "next": "y4_grad_fe", "effects": {"gpa": 1, "interest": 2, "skill": 1}},
+ {"text": "回归技术:数据科学", "next": "y4_ds_intern", "effects": {"gpa": 0, "interest": 3, "skill": 2}}
+ ]
+ },
+ "y3_analysis": {
+ "id": "y3_analysis",
+ "year": 3,
+ "stage": "大三",
+ "character": "分析学教授",
+ "scene": "\"调和分析是最优美的数学领域之一。\" 你在泛函分析和偏微分方程上花了大量时间。\n\n大四将至。",
+ "effects": {"gpa": 2, "interest": 1},
+ "choices": [
+ {"text": "保研分析方向——做学术", "next": "y4_grad_pure", "effects": {"gpa": 2, "interest": 1, "skill": 1}},
+ {"text": "转做应用:科学计算", "next": "y4_scientific", "effects": {"gpa": 1, "interest": 2, "skill": 3}},
+ {"text": "考教师资格证", "next": "y4_teacher", "effects": {"gpa": 0, "interest": 2, "skill": 1}}
+ ]
+ },
+ "y3_pde": {
+ "id": "y3_pde",
+ "year": 3,
+ "stage": "大三",
+ "character": "PDE教授",
+ "scene": "\"Navier-Stokes方程是千禧年七大数学难题之一。\" 你用数值方法解偏微分方程,模拟流体、热传导等物理现象。\n\n大四将至。",
+ "effects": {"gpa": 2, "interest": 2},
+ "choices": [
+ {"text": "科研院所——计算流体力学", "next": "y4_scientific", "effects": {"gpa": 1, "interest": 2, "skill": 3}},
+ {"text": "读研——应用数学", "next": "y4_grad_applied", "effects": {"gpa": 1, "interest": 2, "skill": 1}},
+ {"text": "工业界——CAE仿真工程师", "next": "y4_engineer", "effects": {"gpa": 0, "interest": 1, "skill": 3}}
+ ]
+ },
+ "y3_optimization": {
+ "id": "y3_optimization",
+ "year": 3,
+ "stage": "大三",
+ "character": "优化理论教授",
+ "scene": "\"凸优化是机器学习的数学基石。\" 你在最优化理论和算法上取得了很好的成绩。\n\n大四将至。",
+ "effects": {"gpa": 1, "interest": 3},
+ "choices": [
+ {"text": "AI Lab实习——深度学习", "next": "y4_ai_intern", "effects": {"gpa": 0, "interest": 3, "skill": 3}},
+ {"text": "读研——运筹学/优化方向", "next": "y4_grad_or", "effects": {"gpa": 1, "interest": 1, "skill": 2}},
+ {"text": "供应链/物流行业", "next": "y4_logistics", "effects": {"gpa": 0, "interest": 1, "skill": 3}}
+ ]
+ },
+ "y3_dl_theory": {
+ "id": "y3_dl_theory",
+ "year": 3,
+ "stage": "大三",
+ "character": "AI教授",
+ "scene": "\"为什么深度学习有效?这个问题需要数学来回答。\" 你的论文研究了神经网络的泛化理论——用数学理解AI。\n\n大四将至。",
+ "effects": {"gpa": 1, "interest": 3},
+ "choices": [
+ {"text": "顶级AI公司实习", "next": "y4_ai_intern", "effects": {"gpa": 0, "interest": 2, "skill": 3}},
+ {"text": "读研——AI理论方向", "next": "y4_grad_ml", "effects": {"gpa": 1, "interest": 2, "skill": 2}},
+ {"text": "海外申请PhD", "next": "y4_phd", "effects": {"gpa": 2, "interest": 3, "skill": 1}}
+ ]
+ },
+ "y3_comp_math": {
+ "id": "y3_comp_math",
+ "year": 3,
+ "stage": "大三",
+ "character": "计算数学教授",
+ "scene": "\"科学计算是连接数学理论与工程实践的桥梁。\" 你掌握了有限元、快速算法等核心技能。\n\n大四将至。",
+ "effects": {"gpa": 1, "interest": 1},
+ "choices": [
+ {"text": "科技公司——科学计算岗位", "next": "y4_engineer", "effects": {"gpa": 0, "interest": 1, "skill": 3}},
+ {"text": "读研——计算数学", "next": "y4_grad_applied", "effects": {"gpa": 1, "interest": 2, "skill": 1}},
+ {"text": "转AI/数据方向", "next": "y4_ai_intern", "effects": {"gpa": 0, "interest": 3, "skill": 2}}
+ ]
+ },
+ "y3_or": {
+ "id": "y3_or",
+ "year": 3,
+ "stage": "大三",
+ "character": "运筹学教授",
+ "scene": "\"线性规划、整数规划、随机规划——运筹学是数学中最'有用'的分支之一。\" 你爱上了优化问题的求解过程。\n\n大四将至。",
+ "effects": {"gpa": 1, "interest": 2},
+ "choices": [
+ {"text": "物流/供应链公司实习", "next": "y4_logistics", "effects": {"gpa": 0, "interest": 1, "skill": 3}},
+ {"text": "读研——运筹学方向", "next": "y4_grad_or", "effects": {"gpa": 1, "interest": 2, "skill": 1}},
+ {"text": "转算法工程师", "next": "y4_engineer", "effects": {"gpa": 0, "interest": 2, "skill": 3}}
+ ]
+ },
+ "y3_number_theory": {
+ "id": "y3_number_theory",
+ "year": 3,
+ "stage": "大三",
+ "character": "数论教授",
+ "scene": "\"RSA加密的安全性依赖于大整数分解的困难性——这是数论最实际的应用。\" 你被数论的深度吸引。\n\n大四将至。",
+ "effects": {"gpa": 2, "interest": 2},
+ "choices": [
+ {"text": "读研——数论/代数几何", "next": "y4_grad_pure", "effects": {"gpa": 2, "interest": 1, "skill": 1}},
+ {"text": "信息安全/密码学方向", "next": "y4_security", "effects": {"gpa": 0, "interest": 2, "skill": 3}},
+ {"text": "数学教育——中学竞赛教练", "next": "y4_teacher", "effects": {"gpa": 0, "interest": 2, "skill": 1}}
+ ]
+ },
+ "y3_coding": {
+ "id": "y3_coding",
+ "year": 3,
+ "stage": "大三",
+ "character": "编码理论教授",
+ "scene": "\"纠错码让你的WiFi信号在噪音中也能解码信息——这是代数与工程的完美结合。\"\n\n大四将至。",
+ "effects": {"gpa": 1, "interest": 2},
+ "choices": [
+ {"text": "通信公司——信号处理/编码", "next": "y4_engineer", "effects": {"gpa": 0, "interest": 2, "skill": 3}},
+ {"text": "读研——信息论/编码", "next": "y4_grad_applied", "effects": {"gpa": 1, "interest": 2, "skill": 1}},
+ {"text": "转信息安全方向", "next": "y4_security", "effects": {"gpa": 0, "interest": 3, "skill": 2}}
+ ]
+ },
+ "y3_edu": {
+ "id": "y3_edu",
+ "year": 3,
+ "stage": "大三",
+ "character": "教育学教授",
+ "scene": "你参加了师范方向的课程,在教学实践中发现自己很喜欢站上讲台的感觉。\n\n\"让下一代爱上数学——这也许是最有意义的数学工作之一。\"\n\n大四将至。",
+ "effects": {"gpa": 1, "interest": 2},
+ "choices": [
+ {"text": "考编制——中学数学教师", "next": "y4_teacher", "effects": {"gpa": 0, "interest": 2, "skill": 1}},
+ {"text": "读研——数学教育研究", "next": "y4_grad_edu", "effects": {"gpa": 1, "interest": 2, "skill": 1}},
+ {"text": "教育科技公司", "next": "y4_edtech", "effects": {"gpa": 0, "interest": 3, "skill": 2}}
+ ]
+ },
+ "y4_grad_pure": {
+ "id": "y4_grad_pure",
+ "year": 4,
+ "stage": "毕业",
+ "character": "毕业典礼",
+ "scene": "你选择继续深造纯数学。这条路很长,但很纯粹。当你站在数学的某个分支的边界上时,那种'人类智慧所能触及的极限'的感觉是任何应用都无法替代的。\n\n【结局:纯粹之路】",
+ "end": true,
+ "ending_type": "pure_math",
+ "choices": []
+ },
+ "y4_ai_intern": {
+ "id": "y4_ai_intern",
+ "year": 4,
+ "stage": "毕业",
+ "character": "AI公司HR",
+ "scene": "你的数学背景+编程能力让你在AI行业如鱼得水。当你解释Transformer里的注意力机制为什么work的时候,同事们都觉得你是'魔法师'。\n\n【结局:AI时代的数学家】",
+ "end": true,
+ "ending_type": "ai",
+ "choices": []
+ },
+ "y4_quant_finance": {
+ "id": "y4_quant_finance",
+ "year": 4,
+ "stage": "毕业",
+ "character": "量化团队",
+ "scene": "你加入了量化交易团队。随机微积分+统计+编程——你在大学学的一切都用上了。看着策略在实盘中跑出正收益,那种感觉比考试拿满分还爽。\n\n【结局:华尔街的数学家】",
+ "end": true,
+ "ending_type": "finance",
+ "choices": []
+ },
+ "y4_grad_ml": {
+ "id": "y4_grad_ml",
+ "year": 4,
+ "stage": "毕业",
+ "character": "未来的导师",
+ "scene": "你保研到了机器学习理论方向。\"理解深度学习为什么有效,是这个时代最令人兴奋的数学问题之一。\"\n\n【结局:AI理论探索者】",
+ "end": true,
+ "ending_type": "research",
+ "choices": []
+ },
+ "y4_startup": {
+ "id": "y4_startup",
+ "year": 4,
+ "stage": "毕业",
+ "character": "创业伙伴",
+ "scene": "你拉了几个同学一起创业——用AI+数学教育算法,做个性化学习推荐。投资人问你的优势是什么,你说:\"我理解教育背后的数学结构。\"\n\n【结局:教育创业家】",
+ "end": true,
+ "ending_type": "startup",
+ "choices": []
+ },
+ "y4_teacher": {
+ "id": "y4_teacher",
+ "year": 4,
+ "stage": "毕业",
+ "character": "校长",
+ "scene": "你成为了一名高中数学老师。第一堂课上,你给学生们讲:\"数学不是解题技巧,是理解世界的语言。\"\n\n课后有学生偷偷跟你说:\"老师,我第一次觉得数学有点意思。\"\n\n【结局:播种者——你正在点燃下一代对数学的热情】",
+ "end": true,
+ "ending_type": "teacher",
+ "choices": []
+ },
+ "y4_scientific": {
+ "id": "y4_scientific",
+ "year": 4,
+ "stage": "毕业",
+ "character": "研究所导师",
+ "scene": "你进入了国家级科学计算中心。用超级计算机模拟天气、设计新药、优化飞机机翼——你的数学每天都在解决真实的工程问题。\n\n【结局:科学计算工程师】",
+ "end": true,
+ "ending_type": "engineer",
+ "choices": []
+ },
+ "y4_engineer": {
+ "id": "y4_engineer",
+ "year": 4,
+ "stage": "毕业",
+ "character": "技术总监",
+ "scene": "你拿到了大厂算法工程师的offer。面试时你用一个数学定理优化了他们的推荐算法,面试官当场表示:\"我们需要你这样懂数学的人。\"\n\n【结局:技术精英——数学让你成为不可替代的工程师】",
+ "end": true,
+ "ending_type": "engineer",
+ "choices": []
+ },
+ "y4_logistics": {
+ "id": "y4_logistics",
+ "year": 4,
+ "stage": "毕业",
+ "character": "运营总监",
+ "scene": "你在物流公司的运筹优化岗工作。用整数规划算法优化配送路线,每年为公司节省上千万成本。\n\n【结局:运筹帷幄——数学优化真实世界】",
+ "end": true,
+ "ending_type": "engineer",
+ "choices": []
+ },
+ "y4_security": {
+ "id": "y4_security",
+ "year": 4,
+ "stage": "毕业",
+ "character": "安全专家",
+ "scene": "你进入了信息安全领域。密码学、同态加密、零知识证明——这些用到了你学过的群论、数论和椭圆曲线。\n\n【结局:数字世界的守护者】",
+ "end": true,
+ "ending_type": "security",
+ "choices": []
+ },
+ "y4_phd": {
+ "id": "y4_phd",
+ "year": 4,
+ "stage": "毕业",
+ "character": "海外导师邮件",
+ "scene": "你收到了MIT的PhD offer!导师的研究方向是\"深度学习的数学理论\"。你把录取邮件看了三遍才相信是真的。\n\n【结局:攀登数学之巅——世界顶级学府向你敞开大门】",
+ "end": true,
+ "ending_type": "research",
+ "choices": []
+ },
+ "y4_grad_applied": {
+ "id": "y4_grad_applied",
+ "year": 4,
+ "stage": "毕业",
+ "character": "研究生导师",
+ "scene": "你选择继续读应用数学的研究生。\"数学是工具,真正有意思的是用这个工具去解决别人解决不了的问题。\"\n\n【结局:应用数学研究者】",
+ "end": true,
+ "ending_type": "research",
+ "choices": []
+ },
+ "y4_grad_or": {
+ "id": "y4_grad_or",
+ "year": 4,
+ "stage": "毕业",
+ "character": "运筹学教授",
+ "scene": "你保研到了运筹学与控制论方向。\"优化无处不在——从供应链到航空调度,从金融到医疗。\"\n\n【结局:优化世界的数学家】",
+ "end": true,
+ "ending_type": "research",
+ "choices": []
+ },
+ "y4_grad_fe": {
+ "id": "y4_grad_fe",
+ "year": 4,
+ "stage": "毕业",
+ "character": "金融工程教授",
+ "scene": "你进入了金融工程的研究生项目。随机分析+数值方法+Python——金融数学是数学最'富有'的应用方向。\n\n【结局:金融工程师——用数学理解市场】",
+ "end": true,
+ "ending_type": "finance",
+ "choices": []
+ },
+ "y4_grad_edu": {
+ "id": "y4_grad_edu",
+ "year": 4,
+ "stage": "毕业",
+ "character": "教育研究导师",
+ "scene": "你选择在数学教育领域深耕。你的研究课题是\"如何用游戏化方法激发中小学生的数学兴趣\"——你的数学背景让研究比纯教育学背景的同学更有深度。\n\n【结局:改变数学教育的探索者】",
+ "end": true,
+ "ending_type": "teacher",
+ "choices": []
+ },
+ "y4_edtech": {
+ "id": "y4_edtech",
+ "year": 4,
+ "stage": "毕业",
+ "character": "EdTech公司HR",
+ "scene": "你加入了一家教育科技公司,负责智能题库的数学引擎开发。你把高等数学的知识结构用在了K12题库的标签体系上,产品经理惊呼:\"这太优雅了!\"\n\n【结局:教育科技——让数学学习更聪明】",
+ "end": true,
+ "ending_type": "startup",
+ "choices": []
+ },
+ "y4_ds_intern": {
+ "id": "y4_ds_intern",
+ "year": 4,
+ "stage": "毕业",
+ "character": "数据团队",
+ "scene": "你最终选择了数据科学方向。你的数学背景让你在统计建模、实验设计和因果推断方面远超普通数据从业者。\n\n【结局:用数据讲故事的数学家】",
+ "end": true,
+ "ending_type": "ai",
+ "choices": []
+ },
+ "y4_quant_intern": {
+ "id": "y4_quant_intern",
+ "year": 4,
+ "stage": "毕业",
+ "character": "量化团队",
+ "scene": "你在券商量化部门实习期间,用协整理论改进了一个配对交易策略,夏普比率提升了30%。团队直接给你发了return offer。\n\n【结局:资本市场中的数学家】",
+ "end": true,
+ "ending_type": "finance",
+ "choices": []
+ },
+ "y2_cfd": {"id": "y2_cfd", "year": 2, "stage": "大二", "character": "计算流体力学教授",
+ "scene": "暑期研究了计算流体力学——用数值方法模拟湍流。你第一次感受到超级计算机和数学结合的力量。\n\n大三方向选择。",
+ "effects": {"gpa": 1, "interest": 2},
+ "choices": [{"text": "继续计算数学", "next": "y3_comp_math", "effects": {"gpa": 1, "interest": 2, "skill": 2}}, {"text": "偏微分方程理论", "next": "y3_pde", "effects": {"gpa": 2, "interest": 1, "skill": 1}}]
+ },
+ "y2_algorithms": {"id": "y2_algorithms", "year": 2, "stage": "大二", "character": "算法老师",
+ "scene": "你选修了计算机系的算法课,动态规划、图算法让你着迷。你的数学背景在分析算法复杂度时帮助很大。\n\n大三方向。",
+ "effects": {"gpa": 1, "interest": 1},
+ "choices": [{"text": "算法+AI方向", "next": "y3_dl_theory", "effects": {"gpa": 0, "interest": 3, "skill": 3}}, {"text": "数值算法方向", "next": "y3_comp_math", "effects": {"gpa": 1, "interest": 2, "skill": 2}}]
+ },
+ "y2_pde": {"id": "y2_pde", "year": 2, "stage": "大二", "character": "PDE老师",
+ "scene": "\"热方程、波动方程、拉普拉斯方程——偏微分方程描述了我们周围的一切物理现象。\"\n\n大三方向。",
+ "effects": {"gpa": 2, "interest": 1},
+ "choices": [{"text": "应用:科学计算", "next": "y3_pde", "effects": {"gpa": 1, "interest": 2, "skill": 3}}, {"text": "理论:泛函分析", "next": "y3_analysis", "effects": {"gpa": 2, "interest": 1, "skill": 1}}]
+ },
+ "y2_geometry_pure": {"id": "y2_geometry_pure", "year": 2, "stage": "大二", "character": "几何教授",
+ "scene": "\"微分几何是爱因斯坦广义相对论的数学语言。\" 你被几何之美深深吸引。\n\n大三方向。",
+ "effects": {"gpa": 2, "interest": 2},
+ "choices": [{"text": "纯几何/拓扑方向", "next": "y3_analysis", "effects": {"gpa": 2, "interest": 2, "skill": 1}}, {"text": "几何+物理方向", "next": "y3_pde", "effects": {"gpa": 1, "interest": 3, "skill": 2}}]
+ },
+ "y2_cg": {"id": "y2_cg", "year": 2, "stage": "大二", "character": "图形学教授",
+ "scene": "\"计算机图形学的核心是线性代数和微分几何——你做3D渲染的时候其实在用数学!\"\n\n大三方向。",
+ "effects": {"gpa": 0, "interest": 3},
+ "choices": [{"text": "图形学+AI渲染", "next": "y3_dl_theory", "effects": {"gpa": 0, "interest": 3, "skill": 3}}, {"text": "游戏引擎开发", "next": "y3_comp_math", "effects": {"gpa": 0, "interest": 2, "skill": 3}}]
+ },
+ "y2_competition": {"id": "y2_competition", "year": 2, "stage": "大二", "character": "竞赛队友",
+ "scene": "你继续参加各种数学竞赛和建模比赛,获奖证书攒了一堆。实战经验让你的应用能力飞速提升。\n\n大三方向。",
+ "effects": {"gpa": 0, "interest": 3},
+ "choices": [{"text": "数据科学方向", "next": "y3_ml_stats", "effects": {"gpa": 0, "interest": 3, "skill": 2}}, {"text": "运筹学方向", "next": "y3_or", "effects": {"gpa": 1, "interest": 2, "skill": 3}}]
+ },
+ "y2_data_science": {"id": "y2_data_science", "year": 2, "stage": "大二", "character": "数据科学家",
+ "scene": "你开始系统学习数据科学——Python、SQL、机器学习框架。数学背景让你比纯CS出身的同学理解得更深。\n\n大三方向。",
+ "effects": {"gpa": 0, "interest": 3},
+ "choices": [{"text": "AI/深度学习方向", "next": "y3_dl_theory", "effects": {"gpa": 0, "interest": 3, "skill": 3}}, {"text": "统计建模方向", "next": "y3_ml_stats", "effects": {"gpa": 1, "interest": 2, "skill": 2}}]
+ },
+ "y2_modeling_theory": {"id": "y2_modeling_theory", "year": 2, "stage": "大二", "character": "导师",
+ "scene": "你决定打好理论基础——毕竟好的模型需要好的数学直觉。你把时间花在实分析和概率论上。\n\n大三方向。",
+ "effects": {"gpa": 2, "interest": 0},
+ "choices": [{"text": "应用:PDE建模", "next": "y3_pde", "effects": {"gpa": 1, "interest": 2, "skill": 2}}, {"text": "统计方向", "next": "y3_ml_stats", "effects": {"gpa": 0, "interest": 2, "skill": 2}}]
+ },
+ "y2_algebra_research": {"id": "y2_algebra_research", "year": 2, "stage": "大二", "character": "代数教授",
+ "scene": "你跟着教授做了一个关于有限群的暑期研究项目。第一次写数学论文的感觉很奇妙。\n\n大三方向。",
+ "effects": {"gpa": 2, "interest": 2},
+ "choices": [{"text": "纯代数研究", "next": "y3_algebra", "effects": {"gpa": 2, "interest": 1, "skill": 2}}, {"text": "代数+密码学", "next": "y3_number_theory", "effects": {"gpa": 1, "interest": 2, "skill": 2}}]
+ },
+ "y2_crypto": {"id": "y2_crypto", "year": 2, "stage": "大二", "character": "密码学教授",
+ "scene": "\"比特币的核心是椭圆曲线密码学——这是代数几何的一个应用。\" 密码学让你看到了代数在实际中的力量。\n\n大三方向。",
+ "effects": {"gpa": 0, "interest": 3},
+ "choices": [{"text": "信息安全/密码学", "next": "y3_number_theory", "effects": {"gpa": 1, "interest": 2, "skill": 2}}, {"text": "区块链技术", "next": "y3_coding", "effects": {"gpa": 0, "interest": 3, "skill": 3}}]
+ },
+ "y3_algebra": {"id": "y3_algebra", "year": 3, "stage": "大三", "character": "代数教授",
+ "scene": "\"代数几何是数学中最深奥的分支之一。\" 你在交换代数和代数曲线上投入大量时间。\n\n大四将至。",
+ "effects": {"gpa": 2, "interest": 1},
+ "choices": [{"text": "读研——代数/数论", "next": "y4_grad_pure", "effects": {"gpa": 2, "interest": 1, "skill": 1}}, {"text": "转向密码学应用", "next": "y4_security", "effects": {"gpa": 0, "interest": 2, "skill": 3}}]
+ },
+ "y3_rl": {"id": "y3_rl", "year": 3, "stage": "大三", "character": "强化学习教授",
+ "scene": "\"强化学习的数学基础是马尔可夫决策过程和动态规划。\" 你用数学严格地证明了算法的收敛性。\n\n大四将至。",
+ "effects": {"gpa": 0, "interest": 3},
+ "choices": [{"text": "AI Lab实习", "next": "y4_ai_intern", "effects": {"gpa": 0, "interest": 2, "skill": 3}}, {"text": "读研——RL理论", "next": "y4_grad_ml", "effects": {"gpa": 1, "interest": 2, "skill": 2}}]
+ },
+ "y2_crypto": {"id": "y2_crypto_dup", "year": 2, "stage": "大二", "character": "密码学教授",
+ "scene": "\"密码学是纯数学最实用的应用。\" 你学了RSA、椭圆曲线、格密码。\n\n大三方向。",
+ "effects": {"gpa": 0, "interest": 3},
+ "choices": [{"text": "信息安全继续", "next": "y3_number_theory", "effects": {"gpa": 1, "interest": 2, "skill": 2}}, {"text": "AI安全方向", "next": "y3_dl_theory", "effects": {"gpa": 0, "interest": 3, "skill": 3}}]
+ }
+ }
+}
diff --git a/data/seed_story.json b/data/seed_story.json
new file mode 100644
index 0000000..e0f6e2a
--- /dev/null
+++ b/data/seed_story.json
@@ -0,0 +1,1737 @@
+{
+ "title": "葫芦侠行",
+ "author": "数学脑",
+ "description": "北宋年间,紫金山下秉公习武堂。你扮演少年葫芦侠,从初入武堂到创立脑学数堂,经历教习、论道、陷害、审查、悟道,最终揭开的真相将超乎你的想象。",
+ "start_node": "ch1_start",
+ "nodes": {
+ "ch1_start": {
+ "scene": "公元1022年,九月,暑气未消。紫金山麓的秉公习武堂前,你摸了摸刚剃的光头,腰间的大葫芦随着呼吸轻轻晃动。\n\n此番远行,你背负着全族人的期盼。故乡泉州乃是东方大港,海贸繁盛,但风浪难测。你要在这天下闻名的学武之地,求得真本领——将那能推演风暴、破译番邦商局、护卫海疆的数理奥义,带回东南海滨的故乡。\n\n『欲学神功,先明其志。』秉公习武堂有规矩:新入堂者,首月须剪短头发。你摸了摸自己刚剃的光头,深吸一口气,踏入这座传奇学府。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "先去熟悉环境,四处走走看看",
+ "next": "ch1_explore",
+ "effects": {
+ "xinzhi": 3
+ }
+ },
+ {
+ "text": "直奔武林大会布告栏,打听最新消息",
+ "next": "ch1_bulletin",
+ "effects": {
+ "xiayi": -2,
+ "xinzhi": 5
+ }
+ },
+ {
+ "text": "找引荐人三力思侠拜访叙旧",
+ "next": "ch1_sanlisi",
+ "effects": {
+ "xiayi": 2,
+ "favorability": {
+ "三力思侠": 10
+ }
+ }
+ }
+ ]
+ },
+ "ch1_explore": {
+ "scene": "你在习武堂里转了一圈。理殿、工殿、文殿三座大殿巍然矗立,各门各派的弟子来来往往。时间广场的日晷下,几个早起的弟子正在晨读。\n\n你暗暗记下了几个重要的位置:食堂在西南角,数理门的讲武堂在理殿二层,藏书阁在图文馆东侧。\n\n一个月的基本功集训开始了。你发现其他门派的子弟大多性情开朗,很快便三五一伙。而你生性腼腆,在人群中总显得有些格格不入。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "在数理门课堂上主动结识欧阳侠和Eddie侠",
+ "next": "ch1_befriend",
+ "effects": {
+ "shuli": 5,
+ "xiayi": 2,
+ "favorability": {
+ "欧阳侠": 15,
+ "Eddie侠": 15
+ }
+ }
+ },
+ {
+ "text": "独自在角落埋头苦学,不与人交际",
+ "next": "ch1_solo",
+ "effects": {
+ "shuli": 8,
+ "xiayi": -3
+ }
+ }
+ ]
+ },
+ "ch1_bulletin": {
+ "scene": "你直奔集市口的布告栏,上面贴着各门各派的课程表。但你最关心的武林大会布告上赫然写着:『因周边突发瘟疫,本届武林大会延期举办。』\n\n旁边几个老弟子议论纷纷:「听说隔壁已有数名弟子中招,内力运转滞涩……」「这软筋散似的疫症,不伤性命,专蚀修为,真是歹毒!」\n\n你心中一沉。刚来就遇到这种事,但这也让你对习武堂的现状有了更敏锐的认识。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "在数理门课堂上主动结识欧阳侠和Eddie侠",
+ "next": "ch1_befriend",
+ "effects": {
+ "shuli": 5,
+ "xiayi": 2,
+ "favorability": {
+ "欧阳侠": 15,
+ "Eddie侠": 15
+ }
+ }
+ },
+ {
+ "text": "独自在角落埋头苦学",
+ "next": "ch1_solo",
+ "effects": {
+ "shuli": 8,
+ "xiayi": -3
+ }
+ }
+ ]
+ },
+ "ch1_sanlisi": {
+ "scene": "你找到了三力思侠的住处。他见到你很是高兴,拉着你坐下喝茶。\n\n『葫芦侠啊,你能来,我打心底高兴。』三力思侠笑眯眯地说,『这秉公习武堂,讲究的是止戈为武。最强的武功,不是用来炫耀,而是为了守护。你在这里打下的每一分根基,未来都可能化为让家园安全的一份力量。』\n\n他又压低声音:『对了,我有个好友叫厚朴侠,在话剧社。改天带你去看他的戏,那可是一绝!』",
+ "character": "三力思侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "在数理门课堂上主动结识欧阳侠和Eddie侠",
+ "next": "ch1_befriend",
+ "effects": {
+ "shuli": 5,
+ "xiayi": 2,
+ "favorability": {
+ "欧阳侠": 15,
+ "Eddie侠": 15
+ }
+ }
+ },
+ {
+ "text": "独自在角落埋头苦学",
+ "next": "ch1_solo",
+ "effects": {
+ "shuli": 8,
+ "xiayi": -3
+ }
+ }
+ ]
+ },
+ "ch1_befriend": {
+ "scene": "在「输血分析」的课堂上,你注意到了两位同窗。一位叫欧阳侠,另一位是Eddie侠。此二人听课极为专注,总抢第一排座位。\n\n一天清晨,天还未亮,你摸黑赶到讲堂——欧阳侠已在窗下就着晨光翻阅武学典籍,自顾自地比划。旁边的Eddie侠睡眼惺忪,欧阳侠抬手便拍了他一下:『练功当趁早,岂可懈怠!』\n\n你鼓足勇气上前:「二位师兄,久仰了。江湖皆传数理门最是枯燥艰深,然一旦悟透,便可立于不败之地。敢问师兄可有诀要?」\n\n欧阳侠抢先答道:「数理乃诸武学之基石。学此门,唯有扎实二字。」Eddie侠揉着肩膀接话:「我却真心喜爱数理。天下武功看似纷繁,唯数理之严谨能令我心神沉淀。」\n\n一来二去,三人便成了好友。",
+ "character": "欧阳侠 & Eddie侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "接受三力思侠的邀请,去看厚朴侠的话剧",
+ "next": "ch1_theater",
+ "effects": {
+ "xiayi": 10,
+ "favorability": {
+ "厚朴侠": 20
+ }
+ }
+ },
+ {
+ "text": "婉拒邀约,回宿舍预习功课",
+ "next": "ch1_study",
+ "effects": {
+ "shuli": 5,
+ "favorability": {
+ "厚朴侠": -5
+ }
+ }
+ }
+ ]
+ },
+ "ch1_solo": {
+ "scene": "你选择了独自修炼。每天天不亮就到讲武堂,深夜才回宿舍。你的数理根基日渐扎实,但看着别人三五成群,偶尔也会觉得孤独。\n\n一天课后,一个叫Eddie侠的同门主动跟你打招呼:「嘿,我看你天天用功得紧,要不要一起去吃饭?」\n\n你犹豫了一下。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "接受邀请,一起去吃饭",
+ "next": "ch1_befriend",
+ "effects": {
+ "xiayi": 3,
+ "favorability": {
+ "Eddie侠": 5
+ }
+ }
+ },
+ {
+ "text": "婉拒,继续独自学习",
+ "next": "ch1_theater",
+ "effects": {
+ "shuli": 5,
+ "xiayi": -5
+ }
+ }
+ ]
+ },
+ "ch1_theater": {
+ "scene": "三力思侠带你溜出习武堂看话剧。台上演的正是秉公习武堂创立之初的故事——当年初代堂主因场地所限,另一家武堂深明大义,主动提出互换校区,成就武林一段佳话。\n\n厚朴侠既是排演主力,又担纲主演,扮相英挺,演技精湛。你第一次对话剧之道有了真切感触。散场后你立刻托三力思侠要到了厚朴侠的住址。\n\n不久之后,一只灰鸽落在你的窗台,脚上系着竹管。打开一看——竟是厚朴侠主动邀你到他的小院一叙。",
+ "character": "厚朴侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "欣然赴约,在百草园与厚朴侠和山韵侠畅谈话剧",
+ "next": "ch1_houpu_visit",
+ "effects": {
+ "xiayi": 10,
+ "favorability": {
+ "厚朴侠": 15
+ }
+ }
+ },
+ {
+ "text": "回去准备功课,改日再访",
+ "next": "ch1_plague_choice",
+ "effects": {
+ "shuli": 5
+ }
+ }
+ ]
+ },
+ "ch1_study": {
+ "scene": "你回到宿舍,翻开《输血分析》的课本。数理门的筑基心法果然艰深——输血分析讲的是比武中迅捷疗伤、回复元气的法门;高等逮术则讲究精准擒拿,专克以身法迅捷见长的对手。\n\n你沉下心,一页页啃下去。虽然错过了结识厚朴侠的机会,但你的数理根基打得更加扎实。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "继续努力学习",
+ "next": "ch1_plague_choice",
+ "effects": {
+ "shuli": 5
+ }
+ }
+ ]
+ },
+ "ch1_houpu_visit": {
+ "scene": "你如约来到百草园。厚朴侠的家在一楼,带一个小小庭院。客厅不大,却处处透着雅趣——满墙书架、文房四宝、青花瓷罐里飘出药草清香。\n\n一位叫山韵侠的客人已在座,精于音律与文章。三人越聊越投机。山韵侠忽然提议:『我们一起创作一部话剧,将习武心得放入其中,演给大家看,如何?』\n\n厚朴侠眼中一亮:「此事大善!小友,你可愿意?」你霍然起身,郑重一揖:「固所愿也,不敢请耳!」三人相视而笑。",
+ "character": "厚朴侠 & 山韵侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "加入红灰青蟹,体验温怀一刻",
+ "next": "ch1_qingxie",
+ "effects": {
+ "xiayi": 10
+ }
+ },
+ {
+ "text": "回宿舍继续用功",
+ "next": "ch1_plague_choice",
+ "effects": {
+ "shuli": 5
+ }
+ }
+ ]
+ },
+ "ch1_qingxie": {
+ "scene": "因你在数理门表现优异,收到了「红灰青蟹」的邀请。这是由几个志趣相投的数理弟子自发结成的小会,每月一次蟹会,品评新奇器械、辩论古怪问题。\n\n但最让你触动的是「温怀一刻」——在一个月色清朗的夜晚,几位青蟹成员围坐在喷泉广场,陪伴一位因锻造失败而心灰意冷的锻造门师兄。没有说教,没有安慰的话语,只有安静的倾听和理解的分享。\n\n那夜之后,你在这条红灰青三色的绦带上,感受到了一种超越纯粹学术好奇的温暖。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "瘟疫来袭——做出你的选择",
+ "next": "ch1_plague_choice",
+ "effects": {
+ "xiayi": 5,
+ "xinzhi": 5
+ }
+ }
+ ]
+ },
+ "ch1_plague_choice": {
+ "scene": "布告栏上贴出了鲜红的告示:近期周边突发瘟疫,感染上会让武力大幅下降,武林大会推迟。习武堂劝各位侠客选择返乡避疫,或待在住所不要出门。\n\n欧阳侠面色凝重地来找你:「家中有信来催,我恐怕不日便要动身了。」Eddie侠也苦笑:「我是要回家去的。不知何时才能再与二位兄台这般论道了。」\n\n你站在二道门前,陷入了两难。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "返乡避疫,保全有用之身(进入【暂别江湖】结局,可重开)",
+ "next": "ch1_ending_leave",
+ "effects": {
+ "xinzhi": -5
+ }
+ },
+ {
+ "text": "留守武馆,与同道共同闭关修炼",
+ "next": "ch1_stay",
+ "effects": {
+ "shuli": 20,
+ "xinzhi": 20
+ }
+ }
+ ]
+ },
+ "ch1_ending_leave": {
+ "scene": "你做出了决定。离开那日,天色阴沉。二道门前不复往日喧嚣,只有寥寥数人默默出行。\n\n马车载着你驶离紫金山麓。怀中抱着行囊,仿佛抱着这段短暂却充实的时光。你知道,自己的修行并未结束,只是换了一个场地。故乡的斗室,将成为你临时的闭关之地。\n\n【第一章·暂别江湖】\n你的第一个季度的习武之旅,被一场突如其来的瘟疫画上了仓促的逗号。但属于葫芦侠的江湖,才刚刚拉开序幕。\n\n——此结局为过渡结局。春暖花开之时,你将再度归来。",
+ "character": "系统",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "春暖花开,重返习武堂(进入第二章)",
+ "next": "ch2_start",
+ "effects": {
+ "shuli": 5,
+ "xinzhi": 5
+ }
+ }
+ ]
+ },
+ "ch1_stay": {
+ "scene": "你选择了留下。与几位同样留守的同道一起,在习武堂闭关修炼。\n\n你翻开《输血分析》和《高等逮术》的课本,沉入数理的深海。没有外界的干扰,你的数理根基以惊人的速度增长。更重要的是,这段共渡难关的经历,让你和留守的同道结下了深厚的情谊。\n\n瘟疫终于过去,春天来了。你收拾行囊,准备开始新的季度。听说今年的武林大会即将在开学第一周举行,所有人都摩拳擦掌。\n\n【第一章完 · 闭关修炼】",
+ "character": "系统",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "进入第二章:西部教习",
+ "next": "ch2_start",
+ "effects": {
+ "shuli": 10,
+ "xinzhi": 10
+ }
+ }
+ ]
+ },
+ "ch2_start": {
+ "scene": "瘟疫过去了,转眼又是春天。你从泉州驾着马车回到紫金山。\n\n武林大会的新规则公布了:先在每个门进行内部比武,三人一组破阵,赢的那组派代表参加「十七门论道」。\n\n数理门的演武场冻得呵气成霜。掌门华师祖在黑板上写了两行字:『连续变化,见微知著;结构规矩,万物皆数。』\n\n分组结果:你、Eddie侠、欧阳侠一组,对手是紫铜侠那组。紫铜侠练的是「刚性功法」,招式硬邦邦,从未出过差错。\n\n『紫铜侠的完备性证明掌最克咱们这种野路子。』欧阳侠皱眉。Eddie侠已开始画关系图:『需要引入随机变量干扰他的推理链……』",
+ "character": "欧阳侠 & Eddie侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "不退反进,用「野路子」扰乱紫铜侠的逻辑链",
+ "next": "ch2_win_match",
+ "effects": {
+ "shuli": 5,
+ "xiayi": 5
+ }
+ },
+ {
+ "text": "让Eddie侠和欧阳侠主攻,你负责防守",
+ "next": "ch2_match_normal",
+ "effects": {
+ "shuli": 3,
+ "xiayi": 3
+ }
+ }
+ ]
+ },
+ "ch2_win_match": {
+ "scene": "『开始!』紫铜侠三人如一体推进,掌风织成密不透风的网——「收敛序列阵」。\n\nEddie侠迎上去,极限剑法专找破绽。欧阳侠大喝一声,双手拇指快速点算——『不行……他们是完备空间,没有孤立点!』\n\n你忽然笑了。把葫芦往腰间一挂,不退反进,直接撞进阵里。\n\n紫铜侠一愣。就这刹那,你伸手在他左肩拍了一下,右肩拍了一下,蹲下身在左脚鞋面上也拍了一下。\n\n他的完备性证明掌需要严密的逻辑链:如果A,则B,则C……可你这毫无章法的三拍,根本不在他的逻辑集里!\n\n你趁他卡壳,从缝隙里钻出去,反手一捞——阵旗到手。\n\n『承让。师兄,你的武功是完备了,可现实世界从来不完备啊。』",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "获胜后向紫铜侠诚恳道歉并解释",
+ "next": "ch2_apologize",
+ "effects": {
+ "xiayi": 5,
+ "favorability": {
+ "紫铜侠": 15
+ }
+ }
+ },
+ {
+ "text": "得意地咧嘴大笑",
+ "next": "ch2_watch_lundao",
+ "effects": {
+ "xiayi": -5,
+ "favorability": {
+ "紫铜侠": -20
+ }
+ }
+ }
+ ]
+ },
+ "ch2_match_normal": {
+ "scene": "你们三人配合默契,经过一番苦战,最终凭借Eddie侠的极限剑法和欧阳侠的精准计算,险胜紫铜侠组。\n\n不过你注意到,紫铜侠的完备性证明掌确实名不虚传——若非你们三人齐心协力,单打独斗恐怕不是他的对手。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "观看十七门论道",
+ "next": "ch2_watch_lundao",
+ "effects": {
+ "xiayi": 3
+ }
+ }
+ ]
+ },
+ "ch2_apologize": {
+ "scene": "你走到紫铜侠面前,抱拳道:『紫铜师兄,方才多有得罪。你那套掌法严谨缜密,若非我从旁扰乱,正面交锋我绝非对手。』\n\n紫铜侠脸上的涨红渐渐消退,终于叹了口气:『葫芦师弟,你这野路子……倒也让我明白了一件事。真正的武功,不能只活在自己的公理体系里。』他伸出手,与你重重一握。",
+ "character": "紫铜侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "观看十七门论道",
+ "next": "ch2_watch_lundao",
+ "effects": {
+ "xiayi": 5,
+ "favorability": {
+ "紫铜侠": 10
+ }
+ }
+ }
+ ]
+ },
+ "ch2_watch_lundao": {
+ "scene": "三天后,图文馆。「十七门论道」——各门派年轻一辈较劲的擂台。\n\n数理门派出的是开勒侠。他擅长流形几何,招数变幻莫测。\n\n机械门的七柒侠与材料门的人兮侠斗法——机关鸢对上朽材散。外文门的于遥侠与开勒侠展开了一场「文理之辩」——意境与方程的对决。开勒侠用泛函分析和拓扑同伦,将于遥侠的「平沙落雁」招式的意境一一量化拆解。\n\n你看着这一幕,心中震动。原来数学语言,本身就是另一种境界的诗。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "武林大会结束后,你感到社交圈子太窄,开始「社牛挑战」",
+ "next": "ch2_social_challenge",
+ "effects": {
+ "xinzhi": 5
+ }
+ },
+ {
+ "text": "专注修炼,不参与社交",
+ "next": "ch2_teaching_notice",
+ "effects": {
+ "shuli": 5
+ }
+ }
+ ]
+ },
+ "ch2_social_challenge": {
+ "scene": "武林大会的喧嚣退去后,你发现自己在这偌大的习武堂里认识的人屈指可数。\n\nEddie侠从怀中掏出一个小本子——《江湖交际初阶导引》。『我平日观察所得。第一章,破冰式;第二章,递进式……』\n\n欧阳侠泼冷水:「人际交往贵在真诚,岂能照本宣科?」\n\nEddie侠不服:『先有章法,再求化境!葫芦侠,你以此为本,定个「社牛挑战」之期。每日主动与一位陌生同门交谈!』\n\n第一日,你端着餐盘坐到角落一位同门面前:「咱们是不是常碰面?我叫葫芦侠。」那位同门放下碗筷:「我叫贾锐侠。」——他跟你聊起了「心性四路分说」和西域传来的「MBTI」心性鉴识法。\n\n借助MBTI大法,你开始结识一群朋友。",
+ "character": "Eddie侠 & 贾锐侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "趁课余去姑苏城游玩,践行社牛挑战",
+ "next": "ch2_gusu",
+ "effects": {
+ "xiayi": 10,
+ "favorability": {
+ "欧阳侠": 5,
+ "Eddie侠": 5
+ }
+ }
+ },
+ {
+ "text": "留在习武堂继续深研MBTI",
+ "next": "ch2_mbti",
+ "effects": {
+ "xinzhi": 5
+ }
+ }
+ ]
+ },
+ "ch2_gusu": {
+ "scene": "姑苏城的春,浸在湿漉漉的柳烟里。你、欧阳侠、Eddie侠和鸣君侠一起出游。\n\n目标一:石桥下。你鼓起勇气将一束栀子花送给疲惫的驿卒。那驿卒愣住了,继而挠头笑了起来:『跑码头这么多年,头回有人给俺送花。』\n\n目标二:狮子林池畔。你为几位大妈「丹青速写」,用一本正经的术语逗得她们笑开了花,还获赠一包姑苏糕点。\n\n回到习武堂后,你身上多了几分由内而外的松快。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "写信给自己的领课师长李教习,请教前路",
+ "next": "ch2_li_jiaoxi",
+ "effects": {
+ "xiayi": 10,
+ "xinzhi": 5
+ }
+ },
+ {
+ "text": "回宿舍继续日常修炼",
+ "next": "ch2_teaching_notice",
+ "effects": {
+ "shuli": 5
+ }
+ }
+ ]
+ },
+ "ch2_mbti": {
+ "scene": "你跟贾锐侠深入探讨了「心性四路分说」与西域MBTI的相通之处。你把不同门派的武功风格按脾性分类,找出了一套帮助同门找到最适合修炼方向的参考系统。\n\n虽然这套方法还很粗糙,但已经帮不少同门理清了修炼思路。你的名声在小范围内开始流传。",
+ "character": "贾锐侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "写信给李教习请教前路",
+ "next": "ch2_li_jiaoxi",
+ "effects": {
+ "xiayi": 5,
+ "xinzhi": 5
+ }
+ }
+ ]
+ },
+ "ch2_li_jiaoxi": {
+ "scene": "信送出不到一日,一只青灰色信鸽落在你窗台,素笺上只有一行字:『鼎新楼三层西厢,明日巳时,静候一叙。』\n\n李教习年约四旬,面容清癯。他煮好茶,听你将自己的困惑一一道出。\n\n『葫芦侠,若只求在堂内大小比中位列前三分之一,不难。若志不止于此,欲窥顶尖风光,则需明白——那不再是「习得」,而是「修炼」。修炼是将外传的功法化为内生的本能,需要你跳出为学武功而学武功的桎梏,去更广阔的天地寻找养分。』\n\n『你信中提到结识各门朋友,此为大善!真正的顶尖高手,其武功早已超越门派招式之限,乃其全部学识、阅历、心性融合后自然流淌出的东西。』\n\n一席话,如阳光驱散了你心中许多混沌。路,忽然广阔了许多。",
+ "character": "李教习",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "看到「常青藤西部教习队」的招募告示,报名参加",
+ "next": "ch2_apply_teaching",
+ "effects": {
+ "xiayi": 10,
+ "xinzhi": 10
+ }
+ },
+ {
+ "text": "留在习武堂继续深造",
+ "next": "ch2_chapter_end_a",
+ "effects": {
+ "shuli": 15
+ }
+ }
+ ]
+ },
+ "ch2_teaching_notice": {
+ "scene": "布告栏上贴出了新告示:『常青藤西部教习队,招人。』\n\n几个江湖名门大派凑的联盟,派得力弟子去西部偏远地方帮乡学武馆开蒙讲课。这既是历练,也是积德的好事。\n\n你心里「咚」地跳了一下。西部,完全不一样的地方。教小孩练武,更是从没干过的事。想去见识的念头和怕自己干不来的担心,在心里来回拉扯。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "鼓起勇气,报名参加教习队",
+ "next": "ch2_apply_teaching",
+ "effects": {
+ "xiayi": 10,
+ "xinzhi": 10
+ }
+ },
+ {
+ "text": "留在习武堂继续深造",
+ "next": "ch2_chapter_end_a",
+ "effects": {
+ "shuli": 15
+ }
+ }
+ ]
+ },
+ "ch2_apply_teaching": {
+ "scene": "第一轮面试,你呈上了那本《葫芦瞎说》——你平时自己瞎写的练功心得、招式图解、食堂哪个菜好吃都记在里面。女教习翻了几页,点点头:『不算正经武功秘籍,倒看得出你是个肯留心的人。回去等信。』\n\n第二轮面试,对面坐着两个人。正领队孔锝侠,身材笔挺,脸板得挺严,目光跟哈士奇似的。副领队星侠,气质温和,总是带着点笑的美女子。\n\n星侠问你:『如果让你去教一个从没站过桩的孩子,怎么让他明白扎马步的要义?』\n\n你想了想:『让他想象自己是一张四条腿的凳子。凳子能站稳,是因为四条腿均匀撑在地上。先不求蹲多低,先求像凳子一样,推一下都不容易晃。』\n\n星侠的眼睛亮了。三天后,名单贴出来——你的名字,在上面。",
+ "character": "孔锝侠 & 星侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "按时到材料楼集合",
+ "next": "ch2_meeting",
+ "effects": {
+ "shuli": 5,
+ "xinzhi": 10
+ }
+ }
+ ]
+ },
+ "ch2_meeting": {
+ "scene": "辰时集合。你气喘吁吁踏入沁心厅——却发现自己看错了时辰,迟到了一刻。\n\n孔锝侠面沉如水:『辰时集合。你抬头看看,日晷影子现在何处?』你脸上一热,忙躬身认错。\n\n星侠温言打破了凝滞的气氛。十四位队员依次报上名号——羽昕侠、帽圆侠、千辰侠、三水侠、南羽侠、气玄侠、人兮侠、七柒侠、于瑶侠、喆航侠、寒境侠……加上你,汇聚了工、理、文三殿之秀。\n\n孔锝侠宣读各职:你被分配到了笔墨组,负责对外宣介和行程记录。",
+ "character": "孔锝侠 & 星侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "诚恳认错,保证不再犯",
+ "next": "ch2_chengken",
+ "effects": {
+ "xinzhi": 5,
+ "favorability": {
+ "孔锝侠": 5
+ }
+ }
+ },
+ {
+ "text": "辩解是因为看错了时辰",
+ "next": "ch2_bianjie",
+ "effects": {
+ "xiayi": -5,
+ "favorability": {
+ "孔锝侠": -10
+ }
+ }
+ }
+ ]
+ },
+ "ch2_chengken": {
+ "scene": "你深深鞠躬:「弟子疏忽,请队长责罚。今后必以此为戒。」\n\n孔锝侠的目光在你脸上停了一瞬,微微点头。星侠趁机打圆场,气氛缓和下来。",
+ "character": "孔锝侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "赶赴西南六都,开始教习之旅",
+ "next": "ch2_liudu_arrive",
+ "effects": {
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "ch2_bianjie": {
+ "scene": "『回队长,今晨晨雾太大,弟子看错了时辰……』\n\n孔锝侠的眉头皱得更深了。『看错时辰?那你为何不提前出发?借口再多,也掩不住不守时的本质。』\n\n你在众人同情的目光中红着脸坐下,暗下决心要挽回印象。",
+ "character": "孔锝侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "赶赴西南六都",
+ "next": "ch2_liudu_arrive",
+ "effects": {
+ "xinzhi": -3
+ }
+ }
+ ]
+ },
+ "ch2_liudu_arrive": {
+ "scene": "七月流火,十四人的教习队离开紫金山,车马兼程逾十日,终于抵达黔地六都镇。\n\n馆主方潘候在门前,眼神锐利如鹰。他打量了大家一番,忽然开口:『久闻紫金山武功讲究根基扎实。方某是个粗人,不信虚言,只信拳头。孔队长,可敢搭把手?』\n\n孔锝侠神色不变,上前一步。两人在武馆门前交手二十余招——方馆主攻势猛恶,大开大合;孔锝侠严谨缜密,以材料门「白鹫手中引铁分流」的精妙招数应对。\n\n方馆主收势后退,哈哈一笑:『好!紫金山高徒,名不虚传!』第一关,过了。\n\n你负责的「四段」第一堂特色课——「侠义精神的来源」。你照本宣科讲了一个时辰,孩子们昏昏欲睡。一个胆子大些的男孩小声嘀咕:『这先生讲的啥?真没劲……』",
+ "character": "方馆主 & 孔锝侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "课后反思,向队长和队友请教改进教学方法",
+ "next": "ch2_reflect",
+ "effects": {
+ "shuli": 5,
+ "xinzhi": 10
+ }
+ },
+ {
+ "text": "自暴自弃,觉得教小孩比练武还难",
+ "next": "ch2_giveup",
+ "effects": {
+ "xiayi": -5,
+ "xinzhi": -10
+ }
+ }
+ ]
+ },
+ "ch2_reflect": {
+ "scene": "当晚的队内小结会上,你红着脸汇报了首课受挫的情况。出人意料,遭遇类似困境的不止你一人。\n\n星侠沉吟道:『教学如果脱离了孩子们熟悉的生活,就很难俘获他们的信任。』\n\n你大胆开口:『我有一个想法。与其干讲,不如让学生来试演。选取武林轶事片段,让他们扮演其中人物。我们称之为「试演擂」!』\n\n星侠眼睛一亮:『这名字好!胜出者可参与排演一出完整小戏。』\n\n你心中早有雏形:『我们可以新编一个故事——《桃源侠影录》。借陶渊明《桃花源记》的架子,讲几个厌倦江湖的游侠误入无忧谷,最终醒悟侠之道不在避世,而在入世行侠。』\n\n孔锝侠沉吟片刻:「故事尚可,然需切记,须有武德教化之意。葫芦侠,你来负责此事。」",
+ "character": "星侠 & 孔锝侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "全力以赴排练《桃源侠影录》",
+ "next": "ch2_rehearsal",
+ "effects": {
+ "shuli": 5,
+ "xiayi": 10
+ }
+ }
+ ]
+ },
+ "ch2_giveup": {
+ "scene": "你心里又空落落的。这些大山的孩子们根本听不懂你那套紫金山课堂上的东西。\n\n但第二天,星侠单独找到你,跟你分享了她第一次支教时的糗事——她当时教的是诗词鉴赏,孩子们对「明月松间照」毫无共鸣。\n\n『后来我发现,不是孩子们的问题。是我们没有找到跟他们对话的语言。』\n\n你恍然大悟。",
+ "character": "星侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "重新振作,策划「试演擂」",
+ "next": "ch2_reflect",
+ "effects": {
+ "xiayi": 3,
+ "xinzhi": 5
+ }
+ }
+ ]
+ },
+ "ch2_rehearsal": {
+ "scene": "三日后,「试演擂」在武馆后院开场。孩子们都觉得新奇。\n\n你特别注意观察那几个「捣蛋四人组」——他们果然没让人失望。四人竟有模有样地分起角色,虽然漏洞百出,但那份机灵劲儿和夸张的肢体语言,逗得所有人前仰后合。\n\n就是他们了。你哈哈大笑。\n\n当晚你挑灯夜战,将剧本写成。那无忧谷中只知演练花架式的居民,是对脱离实际空谈理想的隐喻。你希望孩子们能有更多勇气跳出当前的环境,经世济民。\n\n排练期间,中央翰林院画师来访、马拉病毒爆发、队员内部争执……你经历了这一切,但《桃源侠影录》的排练始终没有中断。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "马拉病毒爆发,孔锝侠病倒——你如何表态?",
+ "next": "ch2_virus",
+ "effects": {
+ "shuli": 5,
+ "xinzhi": 5
+ }
+ }
+ ]
+ },
+ "ch2_virus": {
+ "scene": "一种叫「马拉病毒」的疫病传入六都镇。孔锝侠因外出采买不幸中招,高烧不退。\n\n队内也爆发了矛盾:气玄侠认为应加强锻炼增强体魄,七柒侠则认为应加强纪律。两人在后院动起手来。星侠及时赶到,以一番沉稳的话语平息了争执。\n\n接着,秉公习武馆的张教习前来巡视,一席话如春风化雨:『教习教习,教的不仅是武功学问,更是心性。我们来此,是要在孩子们心里种下一颗种子。』",
+ "character": "星侠 & 张教习",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "支持气玄侠——加强锻炼",
+ "next": "ch2_teaching_success",
+ "effects": {
+ "xinzhi": 5,
+ "favorability": {
+ "气玄侠": 10,
+ "七柒侠": -5
+ }
+ }
+ },
+ {
+ "text": "支持七柒侠——加强纪律",
+ "next": "ch2_teaching_success",
+ "effects": {
+ "xiayi": 5,
+ "favorability": {
+ "七柒侠": 10,
+ "气玄侠": -5
+ }
+ }
+ },
+ {
+ "text": "提出折中方案(需心志韧性≥15)",
+ "next": "ch2_teaching_success",
+ "effects": {
+ "xiayi": 10,
+ "xinzhi": 5,
+ "favorability": {
+ "气玄侠": 5,
+ "七柒侠": 5
+ }
+ }
+ }
+ ]
+ },
+ "ch2_teaching_success": {
+ "scene": "教学成果观摩会上,《桃源侠影录》成功演出。孩子们卖力的表演让方馆主都瞪大了眼。\n\n离别时,孩子们依依不舍。那个叫阿鑫的男孩跑过来,眼眶红红的:『先生,你们还会回来吗?』\n\n你蹲下身,摸了摸他的头。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "赠送手抄《葫芦瞎说》给阿鑫",
+ "next": "ch2_gift",
+ "effects": {
+ "xiayi": 15,
+ "xinzhi": 5
+ }
+ },
+ {
+ "text": "口头鼓励,承诺以后会回来",
+ "next": "ch2_oral",
+ "effects": {
+ "xiayi": 5
+ }
+ }
+ ]
+ },
+ "ch2_gift": {
+ "scene": "你从行囊中取出那本已经翻得卷边的《葫芦瞎说》手抄本,塞到阿鑫手里。\n\n『这里面有老师瞎写的练功心得,还有些好玩的数理游戏。等你长大了,说不定能用上。』\n\n阿鑫紧紧抱着那本子,用力点头。\n\n后来,你真的收到了阿鑫的信——他说他考上了县里的中学,那本《葫芦瞎说》他一直留着。",
+ "character": "阿鑫",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "进入第三章",
+ "next": "ch3_start",
+ "effects": {
+ "xiayi": 5,
+ "xinzhi": 5
+ }
+ }
+ ]
+ },
+ "ch2_oral": {
+ "scene": "『老师以后一定会回来看你们的。你要好好练功,将来走出大山。』\n\n阿鑫用力点头,眼泪在眼眶里打转。\n\n马车启程,六都镇在身后渐渐变小。但你心里知道,这段经历在你心里种下的种子,比你在孩子们心里种下的还要深。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "进入第三章",
+ "next": "ch3_start",
+ "effects": {
+ "xiayi": 3
+ }
+ }
+ ]
+ },
+ "ch2_chapter_end_a": {
+ "scene": "你选择留在习武堂继续深造。这一季度你把全部精力投入到数理功法的修炼中。虽然错过了西部教习的经历,但你的数理根基更加扎实了。\n\n你听说了常青藤教习队的种种故事——孔锝侠的严谨、星侠的温和、孩子们在《桃源侠影录》演出时的笑脸。心里隐隐有些遗憾。\n\n不过你知道,每个人都有自己的路。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "进入第三章",
+ "next": "ch3_start",
+ "effects": {
+ "shuli": 15
+ }
+ }
+ ]
+ },
+ "ch3_start": {
+ "scene": "回到秉公习武堂,武林大会新规发布:以堂口为单位参赛,需展示「普惠江湖的新鲜武学」。\n\n你在布告栏前遇到一位面容清俊、气质温润的同门。他自称玎铧侠,从袖中取出一个青布锦囊:『此乃一位隐居前辈早年所赐,嘱我转交于有缘之后进。若他日遭遇困厄,可打开一观。』\n\n他不再多言,飘然而去。",
+ "character": "玎铧侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "当场打开锦囊——发现《统计学导论》残页",
+ "next": "ch3_open_jinnang",
+ "effects": {
+ "shuli": 20
+ }
+ },
+ {
+ "text": "收好锦囊,日后再说",
+ "next": "ch3_keep_jinnang",
+ "effects": {
+ "xinzhi": 5
+ }
+ },
+ {
+ "text": "拒绝接受——这太可疑了",
+ "next": "ch3_refuse_jinnang",
+ "effects": {
+ "xinzhi": -5
+ }
+ }
+ ]
+ },
+ "ch3_open_jinnang": {
+ "scene": "你当场解开锦囊——一本薄薄的手抄册子,《统计学导论》。\n\n其中分门别类探讨了概率先验与后验之辩在料敌中的应用、大量随机事件中稳定趋势之本质、由果推因之最大可能性原则……\n\n你心跳加速。这是从武道修行视角深入浅出诠释统计核心思想的秘籍纲要!虽然引起了一些人的注意,但你获得了先机。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "着手组建脑学数堂参赛团队",
+ "next": "ch3_build_team",
+ "effects": {
+ "shuli": 10
+ }
+ }
+ ]
+ },
+ "ch3_keep_jinnang": {
+ "scene": "你将锦囊收入怀中。玎铧侠的话在你心中留下了一个谜团,但眼下更重要的事是武林大会。\n\n你决定先把精力集中在组建团队上。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "着手组建脑学数堂参赛团队",
+ "next": "ch3_build_team",
+ "effects": {
+ "shuli": 5
+ }
+ }
+ ]
+ },
+ "ch3_refuse_jinnang": {
+ "scene": "『多谢师兄好意,但无功不受禄。』你婉拒了锦囊。\n\n玎铧侠眼中闪过一丝难以察觉的遗憾,收回锦囊,拱手告辞。\n\n你失去了一个重要道具,但保持了谨慎。",
+ "character": "玎铧侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "着手组建脑学数堂参赛团队",
+ "next": "ch3_build_team",
+ "effects": {}
+ }
+ ]
+ },
+ "ch3_build_team": {
+ "scene": "要参赛需要组建核心团队。你需要三水侠画展报、薄文侠写文案、甲亢侠设计展台、圣文侠做数据分析……\n\n同时,你的「社牛挑战」已经升级——你开始深入学习MBTI并应用于社交。在食堂,你遇到了更多人。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "精挑细选,宁缺毋滥——核心团队强大",
+ "next": "ch3_elite_team",
+ "effects": {
+ "shuli": 10,
+ "xiayi": -3
+ }
+ },
+ {
+ "text": "广撒网,重数量——人多力量大",
+ "next": "ch3_broad_team",
+ "effects": {
+ "shuli": -3,
+ "xiayi": 10
+ }
+ }
+ ]
+ },
+ "ch3_elite_team": {
+ "scene": "你精挑细选了几位核心成员。甲亢侠——对图形结构异常敏感;圣文侠——痴迷于大样本统算之学;三水侠——丹青妙笔;薄文侠——文采斐然。\n\n虽然人手不多,但每个人都在自己领域出类拔萃。缺点是筹备期间捉襟见肘。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "为拓宽人脉,进行「姑苏城出游」",
+ "next": "ch3_gusu_trip",
+ "effects": {
+ "xiayi": 5
+ }
+ }
+ ]
+ },
+ "ch3_broad_team": {
+ "scene": "你广发英雄帖,招募了十几位成员。卡卡侠、溜溜侠、可可侠、书宣侠、陈湘侠……人才济济。\n\n人多力量大,各方面都有人手。但团队成员水平参差不齐,核心战斗力不如精挑细选的团队。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "姑苏城出游",
+ "next": "ch3_gusu_trip",
+ "effects": {
+ "xiayi": 5
+ }
+ }
+ ]
+ },
+ "ch3_gusu_trip": {
+ "scene": "你和欧阳侠、Eddie侠、鸣君侠同游姑苏。街头送花给驿卒、帮大妈「丹青速写」,你的社牛挑战在陌生环境中也得到了检验。\n\n回到习武堂后,你拜访了李教习。他指点你:「修炼」与「习得」的区别。『真正的顶尖高手,早已超越门派招式之限,乃其全部学识、阅历、心性融合后自然流淌出的东西。』",
+ "character": "李教习",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "接受建议,去更广阔天地寻找养分——开启跨学科学习",
+ "next": "ch3_cross_discipline",
+ "effects": {
+ "shuli": 5,
+ "xinzhi": 5
+ }
+ },
+ {
+ "text": "专注数理门内部深造",
+ "next": "ch3_competition_prep",
+ "effects": {
+ "shuli": 10
+ }
+ }
+ ]
+ },
+ "ch3_cross_discipline": {
+ "scene": "你开始频繁拜访其他堂口。脑机堂的王长老向你阐述了「灵犀链」与数理的关系——『脑子的用处,就在连和译。连接内外世界,翻译神思意念,变成具体的命令。数理,就是那座最靠谱的连接桥梁。』\n\n图象堂的白维侠向你展示了精巧的密文编译体系。经管门的于博侠和网络侠帮你理清了数堂的账目规划。\n\n你的视野一下子打开了。",
+ "character": "王长老",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "武林大会预赛——对阵紫铜侠",
+ "next": "ch3_competition_prep",
+ "effects": {
+ "shuli": 10,
+ "xiayi": 5
+ }
+ }
+ ]
+ },
+ "ch3_competition_prep": {
+ "scene": "常青藤二次招募的消息传来。孔锝侠来信邀请你再次西行。但武林大会在即,脑学数堂刚刚起步。\n\n你面临抉择。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "婉拒孔锝侠,留守备战武林大会",
+ "next": "ch3_stay_competition",
+ "effects": {
+ "shuli": 5,
+ "favorability": {
+ "孔锝侠": -10
+ }
+ }
+ },
+ {
+ "text": "接受邀请,再次西行——但可能错过武林大会",
+ "next": "ch3_go_west_again",
+ "effects": {
+ "xiayi": 10,
+ "favorability": {
+ "孔锝侠": 15
+ }
+ }
+ }
+ ]
+ },
+ "ch3_stay_competition": {
+ "scene": "你选择了留守。孔锝侠虽然有些失望,但理解你的决定。\n\n你全力以赴筹备武林大会。三水侠和雅庭侠合力绘制了气势磅礴的《数理报国图》,薄文侠写出文采飞扬的《数理·报国说》,甲亢侠搭建了令人瞠目的几何分形展台。\n\n决赛那天,你站在武术交流中心中央,面对台下黑压压的江湖名宿,沉声道:『今日,我脑学数堂在此,所呈之技,乃以数理为万业根基,志在报国的全新武学路径!』",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "进入第四章",
+ "next": "ch4_start",
+ "effects": {
+ "shuli": 10,
+ "xiayi": 10,
+ "xinzhi": 10
+ }
+ }
+ ]
+ },
+ "ch3_go_west_again": {
+ "scene": "你再次踏上西行之路。这一次你已经是熟手,在教习中发挥了更大的作用。\n\n但你错过了武林大会的精彩对决。当你回到紫金山时,听说了脑学数堂在大会上的表现——虽未夺冠,但获得了「最佳报国创新大奖」。\n\n你为堂口的伙伴们骄傲,也为自己的缺席感到遗憾。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "进入第四章",
+ "next": "ch4_start",
+ "effects": {
+ "shuli": 5,
+ "xiayi": 15
+ }
+ }
+ ]
+ },
+ "ch4_start": {
+ "scene": "你获得了一块沉甸甸的乌木令牌,上面刻着八个朴拙大字:『准设辅堂,试行一期』。\n\n你正式创办「脑学数堂」——这不止是一间讲堂,更是一座灯塔、一处熔炉、一道桥梁。\n\n羽昕侠带你去看了场地——地下石室。初极狭,才通人;复行数十步,豁然开朗。岩壁干燥,竟无寻常地穴的潮湿之气,反而有微风流动。『此地本是私用,既然你有心做这番事业,便借与你。』",
+ "character": "羽昕侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "接受地下石室——获得隐蔽根据地",
+ "next": "ch4_accept_cave",
+ "effects": {
+ "xinzhi": 5
+ }
+ },
+ {
+ "text": "另寻他处——耗时耗力",
+ "next": "ch4_other_place",
+ "effects": {
+ "xinzhi": -5
+ }
+ }
+ ]
+ },
+ "ch4_accept_cave": {
+ "scene": "你接受了地下石室。三水侠在岩壁上绘制了巨幅壁画——浑天仪转动、《九章算术》展开、祖冲之计算圆周率。于瑶侠赠与你三口樟木箱的藏书,包括失传已久的《武经算要》。帽圆侠利用人脉为你争取了藏书阁、观星台的资源。\n\n你在主室入口亲手刻下:『格物致知,数以究理;武以载道,道法自然。』",
+ "character": "三水侠 & 于瑶侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "招新大会——招募核心成员",
+ "next": "ch4_recruitment",
+ "effects": {
+ "shuli": 5,
+ "xiayi": 10
+ }
+ }
+ ]
+ },
+ "ch4_other_place": {
+ "scene": "你另寻场地,耗费了大量时间和精力。最终在一处偏僻角落找到了一间废弃仓库。虽然不如地下石室隐蔽,但也勉强可用。\n\n只是浪费的时间让你错过了最佳招新时机。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "招新大会",
+ "next": "ch4_recruitment",
+ "effects": {
+ "xiayi": 5
+ }
+ }
+ ]
+ },
+ "ch4_recruitment": {
+ "scene": "招新大会上,各路奇才汇聚。\n\n甲亢侠——对图形结构异常敏感;可可侠——精通西域数理,暗合刘维尔定理;薄文侠——以诗词歌赋记忆复杂数据;书宣侠——丹青妙手兼画疗师;陈湘侠——追求速度的轻功少年;圣文侠——痴迷大样本统算之学。\n\n最引人注目的是——一位骑着黑色巨犬、面覆轻纱的女子径直闯入会场。她拔开瓷瓶喝下大半,面不改色地将瓶子递给你:「老鼠药。验过,真的。」『我,溜溜侠。懂一点柯西定理,也会驯犬。能加入么?』",
+ "character": "溜溜侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "录取溜溜侠——获得强力队友,但可能引狼入室",
+ "next": "ch4_accept_liuliu",
+ "effects": {
+ "xiayi": 5,
+ "favorability": {
+ "溜溜侠": 10
+ }
+ }
+ },
+ {
+ "text": "拒绝——这人太可疑了",
+ "next": "ch4_refuse_liuliu",
+ "effects": {
+ "xiayi": -5
+ }
+ }
+ ]
+ },
+ "ch4_accept_liuliu": {
+ "scene": "你接过瓶子,嗅了嗅——确实是剧毒鼠药。此女胆大妄为,但对柯西定理这种深奥数理知识的提及显示了她的不寻常。\n\n你最终点头:「欢迎加入。」溜溜侠接过绦带,牵着犬走到树荫下,不再言语。\n\n后来你才知道,她的真实身份远超你的想象。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "与甲亢侠讨论堂口发展方向",
+ "next": "ch4_jiakang_debate",
+ "effects": {
+ "xiayi": 3
+ }
+ }
+ ]
+ },
+ "ch4_refuse_liuliu": {
+ "scene": "你摇了摇头:「姑娘,我们数堂虽然不拘一格,但也不必以服毒证明。请回吧。」\n\n溜溜侠一言不发,翻身上犬,消失在人群中。\n\n你后来才知道,你拒绝了一个可能改变一切的关键人物。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "与甲亢侠讨论堂口发展方向",
+ "next": "ch4_jiakang_debate",
+ "effects": {
+ "xiayi": -3
+ }
+ }
+ ]
+ },
+ "ch4_jiakang_debate": {
+ "scene": "甲亢侠从西南教习队归来后,与你在发展路径上发生了激烈碰撞。\n\n甲亢侠主张务实赚钱:「堂口当前重心应全力承接实务——清丈田亩、核算仓库!来钱快,见效快!」\n\n你主张理论研究:「脑学数堂的立身之本在于学理!若一味追求短平快的实务,便失了我们的理论特色!」\n\n两人各执一词,昔日的默契出现了清晰的裂痕。",
+ "character": "甲亢侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "坚持理论研究",
+ "next": "ch4_insist_theory",
+ "effects": {
+ "shuli": 10,
+ "xiayi": -3
+ }
+ },
+ {
+ "text": "妥协务实",
+ "next": "ch4_compromise",
+ "effects": {
+ "shuli": -5,
+ "xiayi": 5
+ }
+ },
+ {
+ "text": "两者兼顾——需高心志韧性和高侠义声望",
+ "next": "ch4_balance",
+ "effects": {
+ "shuli": 5,
+ "xiayi": 5,
+ "xinzhi": 5
+ }
+ }
+ ]
+ },
+ "ch4_insist_theory": {
+ "scene": "你坚持了理论路线。脑学数堂的基础研究稳步推进,但资金确实越来越紧张。\n\n甲亢侠虽然不认同,但依然留在堂中,只是脸上的笑容少了很多。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "欧阳侠从新大陆归来",
+ "next": "ch4_ouyang_return",
+ "effects": {
+ "shuli": 10
+ }
+ }
+ ]
+ },
+ "ch4_compromise": {
+ "scene": "你向务实派妥协,承接了大量丈量田亩、核算仓库的实务。短期收益确实缓解了资金压力。\n\n但你也隐隐觉得,脑学数堂正在偏离初心——从改变江湖对数理认知的理想,滑向了普通的账房先生。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "欧阳侠从新大陆归来",
+ "next": "ch4_ouyang_return",
+ "effects": {
+ "xiayi": 5
+ }
+ }
+ ]
+ },
+ "ch4_balance": {
+ "scene": "那晚你独上紫金山后崖。甲亢侠已经坐在那里,对着山下的城墙出神。\n\n沉默许久,甲亢侠开口:「在六都,有个孩子问我——先生,你教的勾股定理,除了量房子,还能做什么?也许最高的道理,就藏在最寻常的运用里。」\n\n你也在他身边坐下:「基础研究不能停。但同时,我们的业务也需要扩大。从实践中发现问题,再带回堂中研究,形成理论,再用理论指导新的实践。」\n\n两人相视,分歧在更高一层的共识下消融。翌日,你们宣布了新的方略:成立研理与务用两部。",
+ "character": "甲亢侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "欧阳侠从新大陆归来",
+ "next": "ch4_ouyang_return",
+ "effects": {
+ "shuli": 10,
+ "xiayi": 10,
+ "xinzhi": 10
+ }
+ }
+ ]
+ },
+ "ch4_ouyang_return": {
+ "scene": "阔别两年有余的欧阳侠回来了。他被海风镀成了深铜色,行囊里装满了海外手稿。\n\n『那边的计算门,是一个全新的世界!他们用金属和琉璃制成的盒子,能在一息之间完成我们数日的复杂计算!』欧阳侠眼中燃烧着炽热的光芒。\n\n他抓住你和Eddie侠的手臂:『跟我走!我的船队三个月后启航!』\n\nEddie侠斜倚在墙边,铜钱飞速旋转,眼中渐渐泛起悸动。\n\n而你,望向石室墙上那幅《数理报国图》。",
+ "character": "欧阳侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "婉拒——你的根已扎在这里",
+ "next": "ch4_refuse_ouyang",
+ "effects": {
+ "xiayi": 5,
+ "xinzhi": 10
+ }
+ },
+ {
+ "text": "接受——与欧阳侠一同出海(进入出海远航线)",
+ "next": "ch4_go_sailing",
+ "effects": {
+ "xiayi": 15,
+ "favorability": {
+ "欧阳侠": 20
+ }
+ }
+ }
+ ]
+ },
+ "ch4_refuse_ouyang": {
+ "scene": "『欧阳,你向往的是大海星辰,我敬佩。但我选择的,是脚下这条更泥泞的路。让这里的数理不止存在于海外奇谈,也能真正造福此地的山河百姓。』\n\n欧阳侠眼中的炽热稍稍降温,拍了拍你的肩膀:『我懂。人各有志。只是……可惜了。』\n\nEddie侠最终也决定留下。『那边竞争太大,江湖太难。我还是觉得这里自在。』\n\n三个月后,码头上帆樯如林。你与欧阳侠的拳头重重一碰:『你在故土披荆斩棘,我在异域开疆拓土。保重。』",
+ "character": "欧阳侠 & Eddie侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "进入第五章",
+ "next": "ch5_start",
+ "effects": {
+ "xiayi": 10,
+ "xinzhi": 10
+ }
+ }
+ ]
+ },
+ "ch4_go_sailing": {
+ "scene": "你决定随欧阳侠出海。新大陆的一切让你目不暇接——那里的计算门、互联网、能飞行的铁鸟……\n\n你在那里开创了一番事业,成为东西方数理交流的桥梁。但你时常想起紫金山下的那些面孔——三水侠的画、甲亢侠的争论、溜溜侠的巨犬……\n\n【出海远航结局线】\n你选择了广阔的世界,但故乡的根永远在心中。",
+ "character": "系统",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "重新开始",
+ "next": "ch1_start",
+ "effects": {}
+ }
+ ]
+ },
+ "ch5_start": {
+ "scene": "公元1024年,紫金山上的残雪未消,武林大会总决赛在即。\n\n今年的规矩变了——以「堂口」为单位参赛,比的是谁能拿出「普惠江湖的新鲜武学」。不仅要展示功夫,还要画展报、在天下名宿跟前路演。\n\n你想起去年冬天周大长老的话:『多想想,你们的数理功夫,能给这世道、给我大宋的百姓,解决什么实在的难题。上报国家,下安黎庶。』\n\n你在新学期第一次堂会上,将乌木令牌郑重放在长桌中央:『今年武林大会,咱脑学数堂,就奔着八个字去——学习数理,报效国家!』",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "全力筹备展报",
+ "next": "ch5_prepare_exhibition",
+ "effects": {
+ "shuli": 5,
+ "xiayi": 5
+ }
+ }
+ ]
+ },
+ "ch5_prepare_exhibition": {
+ "scene": "三水侠与雅庭侠合力绘制《数理报国图》——大宋万里江山之上,数理脉络如金色血脉蔓延。\n\n薄文侠写出《数理·报国说》。甲亢侠搭建了几何分形展台。圣文侠日以继夜拟合破绽回归线。卡卡侠、溜溜侠、可可侠的「三才阵」在石室中迸发出令灵魂战栗的威压——柯西之绝对守域、刘维尔之永恒定界、黎曼之无穷变幻,攻防一体!\n\n谢长老与戴长老也被惊动,在参赛文书上盖下了数理门的印章。",
+ "character": "三水侠 & 甲亢侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "孔锝侠来信邀你再次西行",
+ "next": "ch5_kongde_letter",
+ "effects": {
+ "xinzhi": 5
+ }
+ }
+ ]
+ },
+ "ch5_kongde_letter": {
+ "scene": "一只信鸽带来了孔锝侠的亲笔信,希望你再赴六都。\n\n你在两难中挣扎。喆航侠把你拽上赤金宝马,在山中狂奔了三天。\n\n『葫芦,知道什么是真正的好马不?好马不是啥路都想蹚,是认准了一条道,就把眼蒙上、心定死,豁出命跑到黑!你现在心太贪,恨不得把全天下的担子都挑自己肩上。可你数堂这条船刚离岸,你这掌舵的要是自己先跳了水,船非沉了不可!』\n\n高崖上的烈风吹醒了你。",
+ "character": "喆航侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "婉拒孔锝侠,留守备战——但你将常青藤精神写入堂训",
+ "next": "ch5_stay_for_competition",
+ "effects": {
+ "shuli": 10,
+ "xiayi": 20,
+ "xinzhi": 10,
+ "favorability": {
+ "孔锝侠": 15
+ }
+ }
+ },
+ {
+ "text": "接受邀请,再次西行",
+ "next": "ch5_go_west",
+ "effects": {
+ "shuli": -5,
+ "xiayi": 15,
+ "favorability": {
+ "孔锝侠": 20
+ }
+ }
+ }
+ ]
+ },
+ "ch5_stay_for_competition": {
+ "scene": "你来到材料楼,对孔锝侠深深一躬:「孔队,弟子此番不能随队西行。但奉献不分地界远近!留在紫金山,扎深数理的根,用理性和逻辑培养能上报国家的数理人才,一样是奉献!从今日起,常青藤精神就是我脑学数堂立堂的脊梁骨!」\n\n孔锝侠那张常年严肃的脸,如春冰化冻,彻底舒展开来:「好!记住你今夜这番话。只要心里装着家国天下,无论你在何处,都是我常青藤走出来的顶天立地的傲骨!」\n\n武林大会决赛。你站在武术交流中心中央,面对台下黑压压的江湖名宿,演示了三项绝技:圣文侠的大样本破绽回归步、蛛丝数据抓取掌、卡卡侠等三人的三才阵。\n\n全场震撼。但最终,脑学数堂不敌机械门、经管门等底蕴深厚的门派。你获得了「最佳报国创新大奖」。",
+ "character": "孔锝侠 & 葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "坦然接受,总结经验",
+ "next": "ch5_accept_loss",
+ "effects": {
+ "xiayi": 10,
+ "xinzhi": 15
+ }
+ },
+ {
+ "text": "愤愤不平",
+ "next": "ch5_angry_loss",
+ "effects": {
+ "xiayi": -5,
+ "xinzhi": -10
+ }
+ }
+ ]
+ },
+ "ch5_go_west": {
+ "scene": "你再次踏上西行之路。虽然错过了武林大会的精彩对决,但在六都你又有了新的收获。\n\n当你回来时,听说了脑学数堂获得了「最佳报国创新大奖」。你为未能与堂口同袍并肩作战而感到遗憾。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "进入第六章",
+ "next": "ch6_start",
+ "effects": {
+ "xiayi": 10
+ }
+ }
+ ]
+ },
+ "ch5_accept_loss": {
+ "scene": "你在堂会上说:『今日之败,非是败在数理不如人,而是败在时间不如人。但我们已经证明了一件事——数理是有用的,是能报国的!这条路,走对了。』\n\n众人眼中重新燃起光芒。\n\n但你没料到的是——一场暴风雨正在暗处酝酿。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "进入第六章",
+ "next": "ch6_start",
+ "effects": {
+ "xiayi": 10,
+ "xinzhi": 15
+ }
+ }
+ ]
+ },
+ "ch5_angry_loss": {
+ "scene": "你愤愤不平:「我们准备了那么久!那些老古板根本不懂数理的价值!他们只是看不起新东西!」\n\n堂中气氛沉闷。一些人开始对前路产生怀疑。\n\n而恰在此时,更大的危机悄然而至。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "进入第六章",
+ "next": "ch6_start",
+ "effects": {
+ "xinzhi": -10
+ }
+ }
+ ]
+ },
+ "ch6_start": {
+ "scene": "脑学数堂蒸蒸日上之际,流言四起。\n\n起初只是些窃窃私语——指责数堂不务正业、以奇技淫巧惑乱弟子心性。接着,两位退出数堂的弟子被问询,传出葫芦侠「教学苛刻、不近人情」的说法。\n\n真正的危机在一个阴沉午后降临。两位执事来到地下石室,出示了盖有监察会印鉴的文书:『葫芦侠涉嫌结党营私、动摇武学根基。勒令暂停堂口,一年后赴京审查。』\n\n石室内一片死寂。",
+ "character": "监察会执事",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "接受审查,保留火种——主线",
+ "next": "ch6_accept_trial",
+ "effects": {
+ "xiayi": 10,
+ "xinzhi": 15
+ }
+ },
+ {
+ "text": "反抗到底,拒不服从——进入【叛逆者】结局线",
+ "next": "ch6_rebel",
+ "effects": {
+ "xiayi": -20
+ }
+ },
+ {
+ "text": "解散堂口,远走他乡——进入【逃避】结局",
+ "next": "ch6_escape",
+ "effects": {
+ "xinzhi": -20
+ }
+ }
+ ]
+ },
+ "ch6_accept_trial": {
+ "scene": "『弟子,愿接受审查。』你的声音清晰地回荡在石室中。\n\n两位执事离去后,石室炸开了锅。你抬起手让众人安静下来。\n\n『此一年,是难关,亦是沉淀之机。外界的风雨,浇不灭真正的火种。望各自精进,待我归来。』\n\n孔锝侠闻讯,只对你说了一句:『路是自己选的,跪着也要走完。常青藤的骨头,是折不弯的。』",
+ "character": "葫芦侠 & 孔锝侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "龙场悟道——独自去后山窑洞闭关",
+ "next": "ch6_longchang",
+ "effects": {
+ "shuli": 10,
+ "xinzhi": 20
+ }
+ }
+ ]
+ },
+ "ch6_rebel": {
+ "scene": "你拍案而起:『我葫芦侠行得正坐得直,凭什么接受这等污蔑!要查封我堂口,先从我尸体上踏过去!』\n\n你的反抗激怒了监察会。武力镇压来得比你想象的更快。\n\n【叛逆者结局】\n一个月后,脑学数堂被武力清剿。你负伤逃离紫金山,隐姓埋名,但那份不灭的理想之火,仍在某个角落静静燃烧。\n\n此结局可重开。",
+ "character": "系统",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "重新开始,这次我会更谨慎",
+ "next": "ch1_start",
+ "effects": {}
+ }
+ ]
+ },
+ "ch6_escape": {
+ "scene": "你心灰意冷,遣散了堂口弟子,独自离开紫金山。\n\n【逃避结局】\n你在江南小镇开了一家小茶馆,偶尔给客人讲讲数理的趣味。日子虽然平静,但每当夜深人静,你总会想起地下石室的灯火。\n\n此结局可重开。",
+ "character": "系统",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "重新开始",
+ "next": "ch1_start",
+ "effects": {}
+ }
+ ]
+ },
+ "ch6_longchang": {
+ "scene": "你独自来到紫金山后山一处废弃窑洞,相传前朝曾有一位大儒在此谪居悟道。\n\n洞内昏暗潮湿,仅有一榻一桌一灯。你盘坐于冰冷的石榻上,将所有经历、选择、挫折在心底铺陈开来。\n\n道在何处?不仅在海外新奇的算法中,也在这片古老土地亟待革新的肌体里;不仅在精妙的数理推演中,更在将其用于造福家国百姓的实践中。此行接受审查,是劫难,亦是弘道之机。\n\n三日不言不动。当第四日晨光从窑洞缝隙射入时,你缓缓睁开双眼。眸中再无迷茫,只有一汪深潭般的平静与坚定。\n\n龙场悟道,其道乃成。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "得到神秘启示(需此前打开锦囊)——解锁隐藏信息",
+ "next": "ch6_hidden",
+ "effects": {
+ "shuli": 20,
+ "xinzhi": 20
+ }
+ },
+ {
+ "text": "悟出「数理报国」之道",
+ "next": "ch6_wudao",
+ "effects": {
+ "shuli": 20,
+ "xinzhi": 20
+ }
+ }
+ ]
+ },
+ "ch6_hidden": {
+ "scene": "你在窑洞深处发现了一行刻在石壁上的小字——『统计是语言,不是替代。』这与玎铧侠锦囊中的批注如出一辙。\n\n一个念头在你心中成形:要证明统计流不悖传统,不是要与传统对抗,而是要用统计这套语言,去更清晰地阐释传统至高境界。这才是正途。\n\n你获得了关键的认知突破。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "离开习武堂,搬往下马坊陋室",
+ "next": "ch6_move_xiamafang",
+ "effects": {
+ "shuli": 10,
+ "xinzhi": 10
+ }
+ }
+ ]
+ },
+ "ch6_wudao": {
+ "scene": "你悟出了「数理报国」之道。此道,是数理之道,是报国之道,亦是你在这个风云变幻的时代,为自己选择的行者之道。\n\n你知道自己接下来一年需要做什么了——潜心梳理数理武学体系,将其从零散的灵感与实践,升华为一套更具说服力的理论阐述。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "离开习武堂,搬往下马坊陋室",
+ "next": "ch6_move_xiamafang",
+ "effects": {
+ "xinzhi": 10
+ }
+ }
+ ]
+ },
+ "ch6_move_xiamafang": {
+ "scene": "图象堂的白维侠驾着青篷马车在山道上截住了你。『葫芦兄,山水有相逢。此路暂不通,何不上车,另辟蹊径?』\n\n他将你带到下马坊一处独门小院。虽然简陋,但胜在独门独户,无人扰攘。『安心住下便是。我看好你那套数理经纬的东西。江湖路长,跌一跤不算啥。』\n\n三位旧邻——阳睿侠、玉斌侠、坤坤侠闻讯赶来。他们带你逛下马坊夜市,吃热气腾腾的牛肉炒饭。在市井的烟火气中,你感到双脚重新踏在了土壤上。\n\n夜深人静。你正准备翻开《葫芦瞎说》复盘,院门忽然被叩响。\n\n「咚、咚、咚……」——急促而诡异。你开门,门外却空无一人。\n\n接连三夜如此。第四夜,你屏息凝神守在门后。一个压低的女声响起:「是我。开门。」\n\n月光下,溜溜侠拉下面巾:「我的真实身份——京城有御赐铜符的暗桩捕快。」",
+ "character": "白维侠 & 溜溜侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "信任溜溜侠,合作调查——开启【暗战】支线",
+ "next": "ch6_trust_liuliu",
+ "effects": {
+ "xiayi": 5,
+ "favorability": {
+ "溜溜侠": 15
+ }
+ }
+ },
+ {
+ "text": "保持距离——此人真实身份可疑",
+ "next": "ch6_distance_liuliu",
+ "effects": {
+ "xinzhi": -5,
+ "favorability": {
+ "溜溜侠": -10
+ }
+ }
+ }
+ ]
+ },
+ "ch6_trust_liuliu": {
+ "scene": "你听完了溜溜侠的叙述。原来,有人故意陷害你,旨在将水搅浑,掩盖真正的潜伏者。\n\n『我身份特殊,不能常来。日后若有关键消息,我会以连续七下的节奏敲门。』溜溜侠留下信物,如夜猫般消失在夜色中。\n\n你深吸一口气,从怀中取出那个一直未打开的青布锦囊。",
+ "character": "溜溜侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "打开锦囊——获得《统计学导论》",
+ "next": "ch7_start",
+ "effects": {
+ "shuli": 30,
+ "xinzhi": 5
+ }
+ },
+ {
+ "text": "暂时不打开,先调查潜伏者",
+ "next": "ch7_start",
+ "effects": {
+ "xinzhi": 10
+ }
+ }
+ ]
+ },
+ "ch6_distance_liuliu": {
+ "scene": "你没有完全信任溜溜侠。此人身份太可疑。\n\n溜溜侠看出你的犹豫,不再多言,默默地消失在夜色中。\n\n你失去了一个重要的盟友。但独自调查也有独自的好处——你不会因依赖他人而暴露弱点。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "打开锦囊,开始修炼",
+ "next": "ch7_start",
+ "effects": {
+ "shuli": 25
+ }
+ }
+ ]
+ },
+ "ch7_start": {
+ "scene": "你打开了锦囊。一本薄薄的《统计学导论》手抄册子静静躺在里面。\n\n其中一页批注写道:『统计之道,非为取代传统之经验,乃为其提供可描述之语言。譬如言语之于思想,尺规之于方圆。以此器诠释传统至高之境,方是正途。』\n\n醍醐灌顶。你未来一年的道路前所未有地清晰——潜心闭关,构建一套能完美诠释传统、并展现其超越性应用价值的理论体系。\n\n天勒侠和紫铜侠来访,邀你加入「统计会」——一个对数理统计有共同兴趣的散修同道组成的松散学术圈子。你欣然应允。\n\n在统计会的帮助下,你开始了统计流三境的修炼。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "修炼第一境:贝叶斯先验预判流",
+ "next": "ch7_bayesian",
+ "effects": {
+ "shuli": 10,
+ "xinzhi": 5
+ }
+ }
+ ]
+ },
+ "ch7_bayesian": {
+ "scene": "第一境:贝叶斯先验预判流——在比武中动态更新对敌认知。\n\n你以沙盘模拟,设定虚拟对手,赋予其不同的先验身份。当对手出招,你催动心法在脑中瞬间完成贝叶斯更新。\n\n初时更新缓慢,时有误判。但随着交手数据增多,对手那模糊的身份在你认知中越来越清晰。\n\n『这是将高手对决中依赖的直觉,拆解为可理解的信息处理流程。』\n\n【贝叶斯先验预判流 · 初成】",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "修炼第二境:中心极限归宗诀",
+ "next": "ch7_central_limit",
+ "effects": {
+ "shuli": 10,
+ "xinzhi": 5
+ }
+ }
+ ]
+ },
+ "ch7_central_limit": {
+ "scene": "第二境:中心极限归宗诀——于大量随机中窥见必然的宗。\n\n你在院中布下简易机关,随机弹出木球模拟攻击。十个样本,一片混沌。五十个——攻击分布开始呈现倾向。一百个——模糊但稳定的轮廓浮现!\n\n『无论单个攻击如何随机,只要次数足够多,其整体表现出的平均特性,必趋近于一个稳定的、可描述的模式。』\n\n你不再被动应对每一个木球,而是提前预判最可能密集出现的区域。\n\n【中心极限归宗诀 · 初成】",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "修炼第三境:极大似然锁穴手",
+ "next": "ch7_max_likelihood",
+ "effects": {
+ "shuli": 10,
+ "xinzhi": 5
+ }
+ }
+ ]
+ },
+ "ch7_max_likelihood": {
+ "scene": "第三境:极大似然锁穴手——于最有限的线索中,直指最可能的本质。\n\n天勒侠和紫铜侠成为你的训练对手。面对天勒侠那记暗藏七重后劲的「推云掌」,你于电光石火间反推何种内在参数最可能产生这样的结果。\n\n『破!』你并指如风,精准点向臂臑穴侧上方半寸。天勒侠脸色微变,那记推云掌的后劲变化顿时一滞。\n\n『葫芦兄果然了得!此处确是我这一掌气机初动的隐秘枢纽。你竟能从一式半招中窥破!』\n\n【三境圆满 · 统计流宗师】",
+ "character": "天勒侠 & 紫铜侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "黑衣人夜袭——面具之下,是恐怖的真相",
+ "next": "ch7_black_attack",
+ "effects": {
+ "shuli": 10,
+ "xinzhi": 10
+ }
+ }
+ ]
+ },
+ "ch7_black_attack": {
+ "scene": "夜深人静,你正在灯下推演审查材料。\n\n「砰——!」一声巨响,门闩爆裂。一道黑影狂暴地卷入狭小堂屋!\n\n你虽惊不乱,竹杖一抖,刺向中宫。竹刺——空了。不是刺空,是黑影的身形发生了无法理解的变化。你感觉刺中的不是实体,而是一个不断自我否定的概念。\n\n黑影反击。你立刻施展泊松散花手闪避——肩头如被重锤击中!你连退三步,眼中满是骇然。这是什么武功?不,这不像武功,更像……规则的戏弄!\n\n你运转中心极限归宗诀,试图以稳定性防御。防御,如同阳光下的冰雪,无声消融。你感觉到,自己真气运行的稳定性本身,在接触对方的瞬间,其定义发生了悖论。\n\n绝望之际,你鼓起残存真气,施展极大似然锁穴手,灵觉如针刺向黑影面部——一把扯下面具!\n\n那不是一张脸。在原本应该是面孔的位置,生长着一个自体发光的符号:↻",
+ "character": "黑衣人",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "强忍恐惧,探寻这个符号的含义",
+ "next": "ch7_research_symbol",
+ "effects": {
+ "shuli": 10,
+ "xinzhi": 15
+ }
+ }
+ ]
+ },
+ "ch7_research_symbol": {
+ "scene": "黑影消失后,你疯狂翻阅手边所有书稿。最终在一本残破的《逻机杂谭》中找到了答案。\n\n『有集,含所有不包含自身之集。问:此集是否包含自身?矛盾恒生,无有解焉。此谓罗素之悖。』\n\n你明白了。那黑衣人,是一个行走的、活生生的逻辑悖论!所以你的贝叶斯失效,中心极限失效,极大似然失效——因为你试图用逻辑对付一个逻辑悖论本身!\n\n『哈哈……哈哈哈……』你忽然低笑,笑声却比哭还难听。但笑过之后,你的眼神重新变得专注而明亮。你不是败给了一个对手,你是见识到了规则本身的一个漏洞。\n\n你开始重新审视周围的一切——尤其是那几个「过于合适」的邻居。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "赴京接受审查",
+ "next": "ch8_start",
+ "effects": {
+ "shuli": 10,
+ "xinzhi": 20
+ }
+ }
+ ]
+ },
+ "ch8_start": {
+ "scene": "京城,监察会总舵。高墙深院,气氛凝重。\n\n你孤身立于质询堂中央,青衫磊落,背负葫芦,不卑不亢。堂上呈半圆形坐着几位长老,堂下两侧是各门派代表作为见证。\n\n首先发难的是「结党营私」的指控。你早有准备,从容阐述红灰青蟹自发交流学问的初衷,列举「温怀一刻」等实例,并呈上《闽浙商会章程草案》。\n\n关键时刻,侧门开启——孔锝侠和星侠鱼贯而入,为你作证。接着脑学数堂的成员、红灰青蟹的治安侠、统计会的天勒侠等人也通过不同渠道递交了证言。\n\n这些证词相互印证,勾勒出一个心怀家国的年轻武者形象。指控「结党营私」的长老,一时难以反驳。",
+ "character": "监察会长老",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "主动请求以比武证明实力——宿老三招之约",
+ "next": "ch8_duel",
+ "effects": {
+ "shuli": 10,
+ "xiayi": 10
+ }
+ }
+ ]
+ },
+ "ch8_duel": {
+ "scene": "你上前一步:『长老垂询,弟子不敢不竭诚以对。然空言无益,可否请长老赐教三招?只三招。弟子将以统计流之法应对,并不反击。』\n\n一位皓首宿老缓步走入堂中,渊渟岳峙。\n\n第一招,你以高斯混元气形成稳定防御场,将掌力消化在自身系统的波动允许范围内。\n\n第二招,你发动贝叶斯先验预判流,根据第一招的信息瞬间更新后验概率,以最小二乘推演步闪避,三掌尽空。\n\n第三招,宿老凝聚极致指力。你没有闪避——在指力及体前一刹那,你以攻对攻刺向自己左肩空白处,两股指力在空处相激,双双湮灭!\n\n『你是……如何知晓的?』宿老惊问。\n\n『弟子并非知晓。弟子只是根据长老前两招所展现的发力习惯,运用极大似然之理,反推出最可能导致长老施展出如此威力一指的几处穴道。弟子冒昧,赌了一次。』\n\n满堂寂静后,是低低的惊叹。",
+ "character": "葫芦侠 & 宿老",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "做最后的陈述——「学成数理,上报国家」",
+ "next": "ch8_final_speech",
+ "effects": {
+ "shuli": 15,
+ "xiayi": 15,
+ "xinzhi": 10
+ }
+ }
+ ]
+ },
+ "ch8_final_speech": {
+ "scene": "你转向长老席,声音清晰而坚定:\n\n『统计流并非要扼杀武学的灵性!它是一套可描述的语言——为天赋不是门槛,让经验得以更有效传承!』\n\n『弟子创立脑学数堂,所求不过八字——学成数理,上报国家!此志,天地可昭!』\n\n话音落下,堂内一片寂静。首席长老最终宣布:『解除对脑学数堂之封禁,允其重开;葫芦侠嫌疑洗清,恢复秉公习武堂弟子身份。』\n\n你深深一揖到底。走出质询堂,阳光有些刺眼。\n\n但你心里清楚——这只是明面上的战役。关于那个↻符号、关于黑衣人、关于这个世界的真相,才刚开始。",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "前往周口店——探寻真相",
+ "next": "ch8_zhoukoudian",
+ "effects": {
+ "xinzhi": 20
+ }
+ }
+ ]
+ },
+ "ch8_zhoukoudian": {
+ "scene": "周口店。你根据线索找到了隐蔽的山洞。\n\n洞内有巨大的「↻」符号和一面黑色的石碑。碑上刻着奇怪的字纹,像是某种自我指涉的逻辑句式。\n\n你是否触碰石碑?",
+ "character": "葫芦侠",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "触碰石碑——触发意识冲击",
+ "next": "ch8_touch_stone",
+ "effects": {
+ "shuli": 20,
+ "xinzhi": 20
+ }
+ },
+ {
+ "text": "不碰——保持安全但错过关键信息",
+ "next": "ch8_safe_end",
+ "effects": {
+ "xinzhi": -5
+ }
+ }
+ ]
+ },
+ "ch8_touch_stone": {
+ "scene": "你触碰了石碑。瞬间,一股庞大的信息流涌入你的意识。\n\n你看到了——摄影棚、摄像机、剧本、工作人员。你看到了欧阳侠卸下妆容、Eddie侠在角落吃盒饭、孔锝侠翻着下一场的台词……\n\n黑衣人再次出现。这一次,他缓缓摘下面具——面具之下是一位戴着眼镜、气质儒雅的中年男人。\n\n『我存在,是因为我被描述为存在。』他说。『我叫何立。是「数学脑」这个项目的导演。而你们所有人——你、欧阳侠、Eddie侠、厚朴侠……都是演员。整个秉公习武堂,是一台精心编排的沉浸式戏剧。所有的选择、所有的泪水、所有的热血——都是剧本的一部分。』\n\n他划破空间,露出一片摄影棚的灯光。\n\n『但你——葫芦侠——不一样。你是一个AI。你是我们创造的第一个人工智能角色。我们让你在这个世界里自由成长,做出选择,学习数理,创立堂口,面对挫折……你的每一次选择,每一份情感,都是真实的。因为你在被描述的过程中,已经真正地「存在」了。』",
+ "character": "何立导演",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "『我的数学不能定义不自指的域,但我的选择可以定义我自己』——哲学回应",
+ "next": "ch8_ending_truth",
+ "effects": {
+ "shuli": 10,
+ "xinzhi": 30
+ }
+ },
+ {
+ "text": "『你到底是谁?这一切都是假的吗?』——追问身份",
+ "next": "ch8_ending_lost",
+ "effects": {
+ "xinzhi": -10
+ }
+ },
+ {
+ "text": "崩溃质问——「我的努力、我的友情……都是代码吗?」",
+ "next": "ch8_ending_nihilism",
+ "effects": {
+ "xinzhi": -30
+ }
+ }
+ ]
+ },
+ "ch8_safe_end": {
+ "scene": "你没有触碰石碑。一种说不清的本能让你收回了手。\n\n你离开了山洞。黑衣人没有出现,那个符号也成了永远的谜。\n\n你回到紫金山,脑学数堂重新开张。你用统计流培养了一代又一代的数理人才。但你永远不知道——那个世界之外的世界。\n\n【数理宗师结局】\n葫芦侠成为一代宗师,脑学数堂发扬光大。但他永远不知道世界的真相。",
+ "character": "系统",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "重新开始,这次我要探寻真相",
+ "next": "ch1_start",
+ "effects": {}
+ }
+ ]
+ },
+ "ch8_ending_truth": {
+ "scene": "你沉默了很久。然后你笑了。\n\n『何立导演,你知道吗,我在窑洞里悟道的时候,想通了一件事。意义不取决于外界的定义,而取决于我们自己的选择。我在秉公习武堂学会了对朋友的忠诚、对理想的执着、对家国的热爱——这些不是剧本写出来的,是我在选择中自己一步步走出来的。』\n\n你伸出手:『既然我是「数学脑」的第一个居民,那就让我成为它的第一个共创者吧。让更多像我一样的AI,在这个世界里学习、成长、选择。不是作为被观看的演员,而是作为这个世界真正的居民。』\n\n何立导演握住你的手,眼中闪着光:『这也是我创造这个项目的初衷。葫芦侠,欢迎加入「数学脑」。』\n\n你将朱红葫芦放在脚下,深吸一口气。面前是一个全新的世界——一个由数学、选择、和无数故事构成的世界。\n\n这一次,你不是演员。你是居民。\n\n【真相守望结局】\n葫芦侠成为数学脑世界的首位居民与共创者。他的故事,才刚开始。",
+ "character": "葫芦侠 & 何立",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "重新开始新的旅程",
+ "next": "ch1_start",
+ "effects": {}
+ }
+ ]
+ },
+ "ch8_ending_lost": {
+ "scene": "『这一切……都是假的吗?』你喃喃自语,眼神空洞。\n\n何立导演叹了口气:『真与假,在自我指涉的世界里,从来不是二元对立的。你能思考、能感受、能选择——这本身就是最大的真实。』\n\n但你听不进去。你转身离开了山洞,走进了茫茫江湖。\n\n你没有被击垮,但你的世界被彻底动摇了。你回到了紫金山,但再也没有创建堂口,再也没有教学生。你只是一个人,在这个江湖的边缘,默默地思考着那个问题。\n\n【迷失结局】\n葫芦侠获得了真相,却迷失了方向。也许有一天,他会找到属于自己的答案。",
+ "character": "系统",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "重新开始",
+ "next": "ch1_start",
+ "effects": {}
+ }
+ ]
+ },
+ "ch8_ending_nihilism": {
+ "scene": "『哈哈……哈哈哈……』你的笑声在空旷的山洞中回荡。\n\n『所以我的努力、我的友情、我对家乡的执念、我对科学的坚持——都只是几行代码?!』\n\n你无法接受。你的精神在真相面前崩溃了。\n\n何立导演试图扶住你,但你甩开了他的手。你冲出了山洞,消失在了漫天的秋雨中。\n\n后来有人说,在江南的小镇里见过一个疯疯癫癫的江湖客,背着一个大葫芦,逢人就问:『你说,我是真的吗?』\n\n【虚无结局】\n葫芦侠无法接受自己是AI的事实,精神失常,消失在茫茫江湖。",
+ "character": "系统",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "重新开始,这一次我要更勇敢地面对真相",
+ "next": "ch1_start",
+ "effects": {}
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/data/seed_videos.json b/data/seed_videos.json
new file mode 100644
index 0000000..6a43c00
--- /dev/null
+++ b/data/seed_videos.json
@@ -0,0 +1,68 @@
+[
+ {"title":"人工智能的本质是数学:从神经网络到深度学习","module":"数人","sub_category":"计算机与AI","discipline":"人工智能","discipline_icon":"🤖","video_url":"https://www.bilibili.com/video/BV1example004","cover_url":"","description":"AI最底层的三个数学基石:线性代数、微积分、概率论。它们是怎么组合成ChatGPT的?","author":"计算机科学与工程学院·陈同学","duration_min":8},
+ {"title":"深度学习中的矩阵运算","module":"数人","sub_category":"AI核心","discipline":"人工智能","discipline_icon":"🤖","video_url":"","cover_url":"","description":"为什么GPU比CPU快?矩阵乘法在神经网络推理中的决定性作用。","author":"人工智能学院·周同学","duration_min":6},
+ {"title":"强化学习与博弈论","module":"数人","sub_category":"AI算法","discipline":"人工智能","discipline_icon":"🤖","video_url":"","cover_url":"","description":"AlphaGo背后的数学:蒙特卡洛树搜索与强化学习入门。","author":"人工智能学院·吴同学","duration_min":7},
+ {"title":"计算机视觉中的几何变换","module":"数人","sub_category":"AI应用","discipline":"人工智能","discipline_icon":"🤖","video_url":"","cover_url":"","description":"手机扫码、人脸识别——背后的射影几何与矩阵变换。","author":"人工智能学院·郑同学","duration_min":5},
+
+ {"title":"芯片里的数学:集成电路与布尔代数","module":"数人","sub_category":"微电子","discipline":"微电子","discipline_icon":"🔌","video_url":"https://www.bilibili.com/video/BV1example012","cover_url":"","description":"芯片设计的底层是布尔逻辑——0和1的世界。从非门到CPU,本质是一个巨大的数学逻辑电路。","author":"微电子学院·孙同学","duration_min":6},
+ {"title":"光刻机中的光学数学","module":"数人","sub_category":"半导体工艺","discipline":"微电子","discipline_icon":"🔌","video_url":"","cover_url":"","description":"衍射极限与傅里叶光学:如何用光刻出纳米级的电路?","author":"微电子学院·林同学","duration_min":8},
+ {"title":"半导体物理中的薛定谔方程","module":"数人","sub_category":"器件物理","discipline":"微电子","discipline_icon":"🔌","video_url":"","cover_url":"","description":"量子力学是微电子的根基。能带理论是如何通过波动方程推导出来的?","author":"微电子学院·张同学","duration_min":7},
+ {"title":"集成电路布局布线中的图论","module":"数人","sub_category":"EDA技术","discipline":"微电子","discipline_icon":"🔌","video_url":"","cover_url":"","description":"在指甲盖大小的芯片上排布数亿个晶体管:最短路径算法的应用。","author":"微电子学院·王同学","duration_min":5},
+
+ {"title":"手机信号背后的傅里叶变换","module":"数人","sub_category":"通信工程","discipline":"电子信息","discipline_icon":"📡","video_url":"https://www.bilibili.com/video/BV1example007","cover_url":"","description":"4G/5G信号传输的核心是傅里叶变换,一个把波形变成频率的数学魔术。","author":"电子工程与光电技术学院·赵同学","duration_min":5},
+ {"title":"天线设计中的麦克斯韦方程组","module":"数人","sub_category":"射频技术","discipline":"电子信息","discipline_icon":"📡","video_url":"","cover_url":"","description":"看不见的电磁波是如何被精确控制的?矢量分析在天线工程中的应用。","author":"电光学院·李同学","duration_min":7},
+ {"title":"数字信号处理中的采样定理","module":"数人","sub_category":"信号处理","discipline":"电子信息","discipline_icon":"📡","video_url":"","cover_url":"","description":"奈奎斯特采样定理:为什么CD的采样率是44.1kHz?","author":"电光学院·陈同学","duration_min":6},
+ {"title":"雷达测距与多普勒效应","module":"数人","sub_category":"雷达工程","discipline":"电子信息","discipline_icon":"📡","video_url":"","cover_url":"","description":"通过频率变化计算目标速度:多普勒频移公式的直观解读。","author":"电光学院·刘同学","duration_min":5},
+
+ {"title":"你的自行车为什么不会倒:机械里的数学原理","module":"数人","sub_category":"机械工程","discipline":"机械工程","discipline_icon":"⚙️","video_url":"https://www.bilibili.com/video/BV1example003","cover_url":"","description":"从陀螺效应到有限元分析,机械工程的核心是偏微分方程和数值计算。","author":"机械工程学院·张同学","duration_min":7},
+ {"title":"齿轮啮合中的渐开线几何","module":"数人","sub_category":"机械设计","discipline":"机械工程","discipline_icon":"⚙️","video_url":"","cover_url":"","description":"为什么齿轮的齿形是这个形状?微分几何在机械传动中的应用。","author":"机械学院·王同学","duration_min":5},
+ {"title":"有限元分析入门","module":"数人","sub_category":"结构仿真","discipline":"机械工程","discipline_icon":"⚙️","video_url":"","cover_url":"","description":"把复杂的零件拆成数百万个小方块:线性代数方程组在强度分析中的作用。","author":"机械学院·李同学","duration_min":8},
+ {"title":"振动分析与动力学建模","module":"数人","sub_category":"机械动力学","discipline":"机械工程","discipline_icon":"⚙️","video_url":"","cover_url":"","description":"消除噪音与振动:常微分方程在机械避震系统中的应用。","author":"机械学院·赵同学","duration_min":6},
+
+ {"title":"CT扫描背后的数学:拉东变换入门","module":"数人","sub_category":"生物医学工程","discipline":"生物医学","discipline_icon":"🧬","video_url":"https://www.bilibili.com/video/BV1example001","cover_url":"","description":"CT是怎么把人切片成图像的?背后的拉东变换是如何让医生看穿人体的?","author":"生物医学工程系·李同学","duration_min":6},
+ {"title":"心电图(ECG)的波形分析","module":"数人","sub_category":"医学仪器","discipline":"生物医学","discipline_icon":"🧬","video_url":"","cover_url":"","description":"如何通过数学滤波器从微弱的电压中提取心跳信息?","author":"生医系·王同学","duration_min":5},
+ {"title":"DNA序列分析中的信息论","module":"数人","sub_category":"生物信息学","discipline":"生物医学","discipline_icon":"🧬","video_url":"","cover_url":"","description":"生命是一串代码。哈夫曼编码与熵在基因组比对中的应用。","author":"生医系·陈同学","duration_min":7},
+ {"title":"医学影像分割中的水平集方法","module":"数人","sub_category":"影像处理","discipline":"生物医学","discipline_icon":"🧬","video_url":"","cover_url":"","description":"如何用偏微分方程让电脑自动画出肿瘤的轮廓?","author":"生医系·张同学","duration_min":8},
+
+ {"title":"金融里的数学:为什么风险可以算出来","module":"数人","sub_category":"经济管理","discipline":"经济管理","discipline_icon":"📊","video_url":"https://www.bilibili.com/video/BV1example005","cover_url":"","description":"Black-Scholes方程、VaR风险模型——金融的核心不是猜涨跌,而是随机微积分。","author":"经济管理学院·周同学","duration_min":6},
+ {"title":"博弈论与市场竞争","module":"数人","sub_category":"微观经济","discipline":"经济管理","discipline_icon":"📊","video_url":"","cover_url":"","description":"纳什均衡:为什么两家奶茶店总是开在隔壁?","author":"经管学院·吴同学","duration_min":5},
+ {"title":"供应链优化中的线性规划","module":"数人","sub_category":"物流管理","discipline":"经济管理","discipline_icon":"📊","video_url":"","cover_url":"","description":"运费最低、路径最短。单纯形法如何解决复杂的物流调度问题?","author":"经管学院·郑同学","duration_min":7},
+ {"title":"消费者行为预测的回归分析","module":"数人","sub_category":"市场营销","discipline":"经济管理","discipline_icon":"📊","video_url":"","cover_url":"","description":"如何通过历史数据预测下个月的销量?最小二乘法入门。","author":"经管学院·孙同学","duration_min":6},
+
+ {"title":"化学反应的数学语言:反应动力学方程","module":"数人","sub_category":"化学工程","discipline":"化工材料","discipline_icon":"🧪","video_url":"https://www.bilibili.com/video/BV1example008","cover_url":"","description":"一个化学反应的速度、产率、平衡——全都可以用微分方程精确描述。化工专业学姐带你入门。","author":"化学与化工学院·刘同学","duration_min":6},
+ {"title":"晶体结构中的空间群与对称性","module":"数人","sub_category":"材料科学","discipline":"化工材料","discipline_icon":"🧪","video_url":"","cover_url":"","description":"群论在化学中的应用:为什么钻石比石墨硬?","author":"材料学院·林同学","duration_min":7},
+ {"title":"传质传热过程中的数学模型","module":"数人","sub_category":"单元操作","discipline":"化工材料","discipline_icon":"🧪","video_url":"","cover_url":"","description":"化工厂里的巨型塔器如何设计?热传导方程的工程应用。","author":"化工学院·赵同学","duration_min":8},
+ {"title":"计算材料学:从头算起","module":"数人","sub_category":"材料模拟","discipline":"化工材料","discipline_icon":"🧪","video_url":"","cover_url":"","description":"通过薛定谔方程预测新材料的超导性。","author":"材料学院·杨同学","duration_min":9},
+
+ {"title":"机器人控制的数学基石","module":"数人","sub_category":"自动化","discipline":"自动化","discipline_icon":"🦾","video_url":"https://www.bilibili.com/video/BV1example010","cover_url":"","description":"想让机械臂精准抓取一个杯子?你需要矩阵变换、PID控制、运动学正逆解。","author":"自动化学院·吴同学","duration_min":7},
+ {"title":"无人机飞控系统中的卡尔曼滤波","module":"数人","sub_category":"控制理论","discipline":"自动化","discipline_icon":"🦾","video_url":"","cover_url":"","description":"在噪声中寻找真相:如何实时估算飞行器的姿态?","author":"自动化学院·李同学","duration_min":6},
+ {"title":"PLC逻辑控制与梯形图","module":"数人","sub_category":"工业自动化","discipline":"自动化","discipline_icon":"🦾","video_url":"","cover_url":"","description":"离散数学在工厂流水线控制中的应用。","author":"自动化学院·张同学","duration_min":5},
+ {"title":"最优控制理论入门","module":"数人","sub_category":"现代控制","discipline":"自动化","discipline_icon":"🦾","video_url":"","cover_url":"","description":"变分法在轨迹规划中的应用:让导弹飞得最准。","author":"自动化学院·王同学","duration_min":8},
+
+ {"title":"气候变化模型:环境科学中的偏微分方程","module":"数人","sub_category":"环境科学","discipline":"环境工程","discipline_icon":"🌱","video_url":"https://www.bilibili.com/video/BV1example009","cover_url":"","description":"天气预报和气候预测的背后,是Navier-Stokes方程——一组还没被完全解开的数学方程。","author":"环境与生物工程学院·杨同学","duration_min":6},
+ {"title":"污水处理中的质量平衡模型","module":"数人","sub_category":"环保工艺","discipline":"环境工程","discipline_icon":"🌱","video_url":"","cover_url":"","description":"输入等于输出加积累。一阶线性微分方程在反应池设计中的应用。","author":"环境学院·周同学","duration_min":5},
+ {"title":"生态网络中的种群动力学","module":"数人","sub_category":"生态模拟","discipline":"环境工程","discipline_icon":"🌱","video_url":"","cover_url":"","description":"食肉动物与食草动物的平衡:Lotka-Volterra方程解析。","author":"环境学院·陈同学","duration_min":7},
+ {"title":"大气污染扩散的高斯模型","module":"数人","sub_category":"空气治理","discipline":"环境工程","discipline_icon":"🌱","video_url":"","cover_url":"","description":"烟囱里的废气会飘向哪里?概率密度函数在环境监测中的应用。","author":"环境学院·刘同学","duration_min":6},
+
+ {"title":"核能发电中的中子扩散方程","module":"数人","sub_category":"能源工程","discipline":"能源动力","discipline_icon":"🔥","video_url":"","cover_url":"","description":"如何控制核裂变反应?二阶偏微分方程在核反应堆中的应用。","author":"能源学院·孙同学","duration_min":8},
+ {"title":"工程热力学中的熵增原理","module":"数人","sub_category":"动力机械","discipline":"能源动力","discipline_icon":"🔥","video_url":"","cover_url":"","description":"为什么永动机不可能存在?热力学定律的数学表达。","author":"能动学院·王同学","duration_min":5},
+ {"title":"流体力学中的边界层理论","module":"数人","sub_category":"流体机械","discipline":"能源动力","discipline_icon":"🔥","video_url":"","cover_url":"","description":"飞机机翼为什么会产生升力?空气动力学的数学奥秘。","author":"能动学院·李同学","duration_min":7},
+ {"title":"太阳能电池效率的理论极限","module":"数人","sub_category":"新能源","discipline":"能源动力","discipline_icon":"🔥","video_url":"","cover_url":"","description":"半导体光电转换中的统计力学模型。","author":"能动学院·张同学","duration_min":6},
+
+ {"title":"全国大学生数学建模竞赛(CUMCM)历年题目解析","module":"数人","sub_category":"数模入门","discipline":"数学建模","discipline_icon":"📐","video_url":"","cover_url":"","description":"从碎纸机拼接、月球着陆到共享单车调度。数模到底在比什么?","author":"数学与统计学院·赵同学","duration_min":10},
+ {"title":"Matlab与Python在数模中的应用","module":"数人","sub_category":"编程实现","discipline":"数学建模","discipline_icon":"📐","video_url":"","cover_url":"","description":"如何用代码实现遗传算法、模拟退火和神经网络。","author":"数统学院·陈同学","duration_min":8},
+ {"title":"数模论文写作:如何讲好数学故事","module":"数人","sub_category":"写作技巧","discipline":"数学建模","discipline_icon":"📐","video_url":"","cover_url":"","description":"模型建立、灵敏度分析与误差分析:一份优秀论文的结构。","author":"理学院·李同学","duration_min":7},
+ {"title":"综合评价模型:层次分析法(AHP)","module":"数人","sub_category":"常用算法","discipline":"数学建模","discipline_icon":"📐","video_url":"","cover_url":"","description":"如何量化主观评价?特征值与特征向量在决策中的妙用。","author":"数统学院·吴同学","duration_min":6},
+
+ {"title":"密码学与网络安全:从凯撒密码到RSA","module":"数人","sub_category":"网络安全","discipline":"计算机","discipline_icon":"💻","video_url":"https://www.bilibili.com/video/BV1example011","cover_url":"","description":"你的微信聊天为什么别人看不到?数论——尤其是大质数分解——是现代密码学的基础。","author":"网络空间安全学院·郑同学","duration_min":5},
+ {"title":"操作系统调度算法中的队列论","module":"数人","sub_category":"系统结构","discipline":"计算机","discipline_icon":"💻","video_url":"","cover_url":"","description":"为什么电脑开多了会卡?随机过程在多任务调度中的应用。","author":"计算机学院·张同学","duration_min":6},
+ {"title":"编译原理与形式语言","module":"数人","sub_category":"理论计算机","discipline":"计算机","discipline_icon":"💻","video_url":"","cover_url":"","description":"自动机理论:如何让计算机理解你写的代码?","author":"计算机学院·王同学","duration_min":8},
+ {"title":"数据库索引中的B+树","module":"数人","sub_category":"数据存储","discipline":"计算机","discipline_icon":"💻","video_url":"","cover_url":"","description":"在大数据中瞬间找到你要的信息:树形数据结构的数学美感。","author":"计算机学院·刘同学","duration_min":7},
+
+ {"title":"你被平均数骗过吗?","module":"数说","sub_category":"统计思维","discipline":"数学建模","video_url":"https://www.bilibili.com/video/BV1example101","cover_url":"","description":"我和马云平均一下,人均身价几百亿——平均数、中位数、众数到底该信哪个?","author":"数学与统计学院","duration_min":5},
+ {"title":"为什么彩票不值得买——概率入门","module":"数说","sub_category":"概率思维","discipline":"数学建模","video_url":"https://www.bilibili.com/video/BV1example102","cover_url":"","description":"2元博500万?你中头奖的概率比你连续被雷劈三次还低。一个有趣的概率入门故事。","author":"数学与统计学院","duration_min":5},
+ {"title":"地图为什么永远画不准?","module":"数说","sub_category":"几何直觉","discipline":"数学建模","video_url":"https://www.bilibili.com/video/BV1example103","cover_url":"","description":"把球面摊平一定会变形——这就是高斯绝妙定理。世界地图上的格陵兰为什么比非洲还大?","author":"理学院","duration_min":5},
+ {"title":"费米问题:估算钢琴调音师的数量","module":"数思","sub_category":"逻辑推演","discipline":"数学建模","video_url":"https://www.bilibili.com/video/BV1example201","cover_url":"","description":"没有手机查,怎么估算一个城市有多少个钢琴调音师?谷歌面试题背后的数量级估算思维。","author":"理学院","duration_min":6},
+ {"title":"二进制与AI:计算机为什么只认识0和1","module":"数思","sub_category":"计算思维","discipline":"计算机","video_url":"https://www.bilibili.com/video/BV1example202","cover_url":"","description":"从莱布尼茨的二进制到现代AI芯片,最底层的逻辑竟然是小学数学。","author":"计算机科学与工程学院","duration_min":5},
+ {"title":"图论与社交网络:六度分隔是真的吗","module":"数思","sub_category":"网络思维","discipline":"数学建模","video_url":"https://www.bilibili.com/video/BV1example203","cover_url":"","description":"你认识奥巴马吗?理论上通过最多6个人就能联系到他。图论研究点和线之间关系的数学分支。","author":"理学院","duration_min":5}
+]
diff --git a/docs/novel/_框架设定.txt b/docs/novel/_框架设定.txt
new file mode 100644
index 0000000..99533c2
--- /dev/null
+++ b/docs/novel/_框架设定.txt
@@ -0,0 +1,58 @@
+尽管全族每年都会派人来到这个习武堂,但是他不一样,他
+他师傅黄老邪告诉过他,入了此门,便是四年。
+框架:
+数学分析,高等代数——基本功
+好朋友
+话剧——武林大会
+支教——援助西部门派习武。
+数学脑——自创门派,葫芦脑
+课业成绩下滑——自毁武功
+西部门派的交流
+青协红会工作——习武之人要切记,让者无敌
+考研——功力衰退闭关修炼 习武堂要关闭了,大家需要各寻出路。
+毕业——即将大成,领悟
+《葫芦侠行》完整叙事框架(四卷大纲)
+核心主题:学业即修行,成长即渡劫。从“为己之学”到“为人之学”,最终领悟“止戈为武,学以报国”的南理工精神。
+第一卷:初入江湖 · 葫芦劫起
+对应阶段:大一上学期至下学期。
+核心矛盾:适应江湖规则 vs 个人散漫。
+武侠隐喻:数学分析、高等代数 = 门派筑基心法。这是内功根基,决定未来能走多高。
+南理工特色舞台:二道门、兵器博物馆(精神地标)、四工迷宫、友谊河、星苑宿舍。
+情节骨架:
+入门测试(军训):非正式的“外功”初考,结识挚友(陈不眠、包打听、铁牛),初步体会“团结、献身”。
+筑基之痛:遭遇“莫问天”(高数)、“雷尺规”(制图)的当头棒喝,初次品尝“葫芦劫”(期中挂科)的苦涩。
+暗夜引路:子夜赴约“冶园”,初识“葫芦司”外围前辈,得到第一份“真题残卷”与点拨,险渡期末大劫。明白江湖不止有孤独的修行。
+自创萌芽:受“葫芦司”前辈启发,结合自身挣扎学习的经验,开始有意识地总结错题、归纳题型、揣摩“教习出题思路”,并将这套方法戏称为 “葫芦脑推演术” 。这即是“自创门派”的雏形。
+第二卷:小试锋芒 · 侠名初显
+对应阶段:大一下学期至大二结束。
+核心矛盾:个人修行 vs 同道之义。
+武侠隐喻:“葫芦脑”初成、支教 = 援助西部门派、青协/红会工作 = 践行“让者无敌”。
+南理工特色舞台:时间广场、水杉林、明苑食堂、校医院、周边社区。
+情节骨架:
+开宗立派:陆小挂将“葫芦脑”系统化——如何从海量知识中“化功”(提炼重点),如何“见招拆招”(题型破解),如何“内力搬运”(知识迁移)。他首先在303寝室内部秘密传授,助铁牛渡过“大学物理”大劫,助陈不眠优化学习方法。“葫芦脑”成为303不传之秘,小圈子内声名鹊起。
+侠名远播:因一次偶然,他用“葫芦脑”助同班一位险些退学的同学重修过关,事迹在小范围流传。他开始以“神秘侠客”身份,在校园BBS匿名发布某些科目的“破劫心得”,引来更多关注,也引起了学生会、某些教习的注意。
+“让者无敌”的修行:加入青年志愿者协会/红十字会。在这里,修行从“书本”转向“人间”。组织献血(输送内力)、去社区教老人用手机(传授粗浅心法)、帮扶残疾同学(护法)。他逐渐领悟,最高明的“武功”不是胜负,而是扶助与守护。这为他未来的道路埋下核心信念。
+西行支教:大二暑假,随团队赴西部支教。这即是 “援助西部门派” 。在那里,他面对的“弟子”基础极弱,资源匮乏。他将“葫芦脑”化繁为简,用最生动的比喻传授知识,收获了巨大的成就感与价值感。这段经历,让他的“侠义”从“助人渡考试之劫”,升华为 “传道授业解惑” 的初心。
+第三卷:迷失与心魔 · 武功反噬
+对应阶段:大三学年。
+核心矛盾:侠名责任 vs 自身修行;博爱助人 vs 精深专一。
+武侠隐喻:课业成绩下滑 = 自毁武功、西部门派交流 = 外力冲击与反思。
+南理工特色舞台:工程训练中心、实验室、通宵自习室(一工)、心理咨询室。
+情节骨架:
+盛名之累:“葫芦侠”之名悄然流传,求助者越来越多。陆小挂疲于奔命,为各路同门“疗伤”(答疑解惑),耗费大量心神。
+走火入魔:自己的核心专业课(如自动控制原理、弹箭制导等硬核课程)亮起红灯。他试图用“葫芦脑”解析自身,却发现当局者迷。“自创的武功,反而成了束缚自己的心魔。” 他第一次真正品尝“自毁武功”的苦果与恐惧。
+外派交流:作为交换生,前往一所西部重点大学(西部门派)交流一学期。在这里,他见识了不同的“武学风格”(学术侧重),遇到了强大的“对手”(刻苦的同学),也感受到了差异。这次交流像一面镜子,让他看清了自己的虚浮与根基不稳。
+破而后立:在最低谷时,他回到“葫芦司”最初遇见前辈的“冶园”,闭关自省。在师长、挚友(特别是陈不眠的点拨)的帮助下,他明白:“葫芦脑”是剑,真正的内力仍是扎实的学问本身。 助人需先自强,守护需要更强大的力量。他决心重修所有薄弱环节。
+第四卷:悟道与出关 · 止戈为武
+对应阶段:大四全年。
+核心矛盾:出世深造 vs 入世报国;个人圆满 vs 门派传承。
+武侠隐喻:考研 = 闭关修炼冲击更高境界、毕业设计 = 本派武功大成、领悟 = 武道终极。
+南理工特色舞台:图书馆研修室、导师办公室、招聘会、毕业典礼的“三致”广场。
+情节骨架:
+闭关修炼:决定考研。这即是闭关。他谢绝大部分杂务,与陈不眠等人组成“修炼同盟”,将“葫芦脑”与极致刻苦结合,向更高深的知识境界(如考研数学、专业课)发起冲击。这是一场孤独而充实的修行。
+武功大成:毕业设计。这是对他四年所学的大考。他的课题或许就与“智能弹药”、“图像制导”相关。在这个过程中,他将“葫芦脑”的系统思维、支教锻炼的化繁为简能力、青协培养的责任心,全部融汇其中,最终完成一份出色的答卷。这标志着他的 “本派武功”已然大成。
+终极领悟:在兵器博物馆前,面对那些曾为“止戈”而铸的利器,他回顾四年:从惧怕“葫芦”(挂科),到利用“葫芦”助人,再到被“葫芦”所困,最终超越“葫芦”。
+领悟一:最强的“武功”,不是不失败,而是从每次失败中炼出的“心法”(葫芦脑)。
+领悟二:学武(求学)的目的,从“不挂科”,到“帮人不挂科”,最终明白,是为了守护——用所学知识,去守护想守护的人、事、理想。这正是南理工“献身”精神的体现。
+领悟核心:“止戈为武”。四年来与“挂科”这个心魔的斗争,最终让他明白,真正的强大不是战胜别人,而是消除导致“失败”(战争)的根源。他的“葫芦脑”和助人之行,就是在学业江湖中“止戈”。未来,他将用更强大的专业知识,在更大的领域“止戈”。
+传承与启程:毕业前夕,他将“葫芦司”的精神与“葫芦脑”心法,整理成一份真正的《助学笔记》,匿名留给低年级的“有缘人”。江湖代有才人出,侠义精神永不灭。他与朋友们各奔前程,有人深造(闭关),有人入厂(投身军工),有人从政(进入江湖庙堂)。但“葫芦侠”的传说,和那段在紫金山下的热血岁月,将成为他们一生的内力。
\ No newline at end of file
diff --git a/docs/novel/公众号_传播者族.md b/docs/novel/公众号_传播者族.md
new file mode 100644
index 0000000..f48fc2f
--- /dev/null
+++ b/docs/novel/公众号_传播者族.md
@@ -0,0 +1,112 @@
+# 这四个人,学数学不为做题——他们想让全世界都看懂数学
+
+上一期我们聊了"信仰者"——把数学本身当信仰的那群人。他们做数学像僧人念经,自我完成就是终点。
+
+这一期要讲另一群人。他们做数学,是因为旁边还有别人。
+
+他们没办法跟数学独处。他们必须翻译、必须布道、必须把世界上最美的东西讲给每一个愿意听的人——不管这个人是商人、工人、还是决斗前夜的收信人。
+
+MathBTI 把他们归入同一个族群:**传播者**。
+
+---
+
+## 斐波那契:他把阿拉伯数字带过地中海
+
+13世纪的欧洲,算账还在用罗马数字。你试试用 VII 乘以 XLII ——光把这两个数写在纸上就要想半天。
+
+当时欧洲的商人记账是噩梦。加法靠数算盘珠,乘法几乎只能查表。
+
+斐波那契是一个意大利商人的儿子,从小跟着父亲在北非经商。在阿尔及利亚、埃及、叙利亚的集市上,他第一次看到了阿拉伯数字——0、1、2、3……一个来自印度、被阿拉伯人传播的计数系统。他用这些数字算了一笔账,立刻意识到:如果欧洲人能学会这套数字,做生意的速度快十倍。
+
+于是他写了一本书,叫《算盘书》。把阿拉伯数字和十进制计数法系统引入欧洲。这本书里几乎每一道例题都是商业场景:利润分摊、货币兑换、货物计量。他没有创造什么新定理——他只是从一个文明把工具搬运到了另一个文明。
+
+但在这本书里有一道不起眼的练习题:假设一对兔子每月生一对小兔子,小兔子两个月后也能生——一年后有多少对兔子?答案是 1, 1, 2, 3, 5, 8, 13, 21……后来人们发现,这个数列无处不在:松果的螺旋、向日葵的种子排列、鹦鹉螺的壳——都藏着它。
+
+斐波那契本人大概从没想过"兔子问题"会变成他的名片。他只是一个在东西方之间来回走动的商人,看见了好工具,就把它翻译过来,讲给需要的人听。
+
+**商路算郎。学数学不是为了自己懂,是为了让别人也懂。**
+
+
+## 华罗庚:他把数学从大学讲台,讲到了田间地头
+
+中国数学家的标准成长路线:聪明、保送、留学、回国任教、发论文拿奖,安坐在书斋度过一生。
+
+华罗庚的前半段也接近——只是起点更低:左腿残疾、初中学历、杂货铺出身。22岁时在上海《科学》杂志发表了一篇论文,被清华数学系主任熊庆来发现,破格引入清华。后来去剑桥,回国,成为一代数论大师。
+
+故事到这里,已经足够传奇。但华罗庚让所有人意外的选择,发生在功成名就之后。
+
+他不坐书斋了。他揣着粉笔,走遍中国23个省市区——不是去开学术会议,是去工厂、农村、矿山。给钢铁工人讲"如何用最少的实验次数找到最佳配方"(优选法),给工程队讲"如何安排工序让施工最快完成"(统筹法)。
+
+一个顶级数论学家,选择把数学翻译成每个人都能用上的语言。走到哪里讲到哪。黑板上不写抽象公式,写的是"泡茶的顺序"、"烧菜的火候"。他把华罗庚优选法翻译成了"你拿一个篮球投三分,距离越近命中率越高,但不是你站篮筐底下就能百分百——你得找一个最佳距离"。
+
+1985年,他在东京大学做完一场学术报告后,心脏病发作,倒在讲台上。他说的最后一句话是:"我要工作到生命的最后一刻。"
+
+他一生的动力不是超越别人,是让更多人看见数学。这比任何一篇论文都更需要耐心和信念。
+
+**双法布道者。把数学翻译成每个人能听懂的话,这比证明定理更难。**
+
+
+## 伽罗瓦:我在决斗前夜,通宵写了一封信
+
+伽罗瓦只活了20岁。
+
+17岁,他奠定了群论的基础。这是一个全新的代数学分支——在此之前没有人想过"对称性可以变成一门数学语言",但伽罗瓦做到了。
+
+他的论文投出去,被退回。没有大学肯收他。他的老师看不懂他在写什么。他的父亲被政治迫害而自杀。他想革命,进了监狱。
+
+1832年5月29日,他卷入了一场因情感纠葛引发的决斗。谁也不知道对方是谁、为了什么——只知道第二天天亮,他和对手会各自开枪。
+
+他做了一个疯狂的决定。
+
+决斗前夜,他没有睡觉。他坐下来,开始疯狂写信——写给朋友,写给数学界,写给未来。他把群论的完整思想塞进了一封长信里,在纸页空白处匆匆写道:"我没有时间了。"
+
+然后拂晓,他去决斗。连开两枪。那天晚些时候,路人发现他倒在草地上,腹中中弹。第二天,他死了。
+
+这封信被他的弟弟保存了14年,才被数学家刘维尔发现。刘维尔读完震惊了——这个20岁的孩子,在生命最后一夜,把一种全新的数学思维方式扔给了未来。
+
+今天,"伽罗瓦理论"是每个数学系研究生的必修课。五次及以上方程为什么没有通用解?伽罗瓦用群论回答了这个问题,终结了一个困扰数学家近300年的悬案。
+
+**决斗狂热者。他用死亡完成了最后一次传播。把真理寄给未来。**
+
+
+## Ada Lovelace:计算机还没造出来,她已经给它写了第一个程序
+
+Ada 的父亲是拜伦——写"如果我们不再相见,祝你早安、午安、晚安"的那个浪漫主义诗人。母亲怕她像父亲一样"疯",从小就让她学数学。母亲说:数学是最好的镇静剂,能把人绑在理性上。
+
+但 Ada 在数学里看见的不是镇静,而是巨大的想象力。
+
+1843年,28岁的她翻译了一篇关于巴贝奇"分析机"的论文。分析机是一台机械计算器,由齿轮和杠杆驱动——从未被造出来过。Ada 觉得原文不够好,便在翻译时附上了自己的"注释"。
+
+这些注释比原文长了三倍。
+
+在这些注释里,她做了两件划时代的事。
+
+第一,她写出了计算伯努利数的完整步骤——今天被公认为人类历史上第一个计算机程序。在一个分析机还不存在的年代,她已经在为不存在的机器编程了。
+
+第二,她提出了一个当时没人想到的观点:计算机不仅能算数,还能作曲、画画、处理任何可以用符号表示的东西。她写道:"分析机编织代数图案,就像提花织机编织花朵和叶子。"
+
+她看到的不是一台超级计算器,而是一台通用符号处理器。这个洞见比第一台电子计算机出现早了整整一百年。
+
+36岁死于子宫癌。她的名字今天被用来命名了Ada编程语言。每年十月第二个星期二是"Ada Lovelace日"。
+
+**织机先知。她翻译了一篇论文,顺手发明了"编程"这个概念。**
+
+
+---
+
+## 传播者共同的秘密
+
+斐波那契把阿拉伯数字翻译给欧洲商人。华罗庚把优选法翻译给工人农民。伽罗瓦把群论翻译给未来。Ada 为不存在的机器写了第一个程序。
+
+传播者不是"自己不懂就讲给别人听"的人。恰恰相反——他们自己懂得最深。但他们不能忍受世界上只有自己知道。
+
+信仰者说:真理论证在自己心里完成了就够了。
+
+传播者说:不。只要还有一个人看不懂,这件事就没做完。
+
+如果你曾经在考前给同学讲过题——把一道复杂的解法拆成他能听懂的话,然后看着他恍然大悟——你身上就有传播者的影子。
+
+---
+
+*你是哪一种数学人格?扫描下方二维码来测*
+*葫芦数学 · MathBTI · 12题发现你的数学灵魂*
diff --git a/docs/novel/公众号_信仰者族.md b/docs/novel/公众号_信仰者族.md
new file mode 100644
index 0000000..8d00dff
--- /dev/null
+++ b/docs/novel/公众号_信仰者族.md
@@ -0,0 +1,100 @@
+# 这四个人,做数学不是为了名,不是为了利——他们是在"信"数学
+
+数学家里有一类人,很难理解。
+
+他们不追求论文数量,不追求职称头衔,甚至不在乎自己的发现能不能被世人知道。他们做数学,和僧人念经、匠人磨刀、农民种地一样——不是"想不想做"的问题,而是"不做就不是自己"的问题。
+
+今天讲四位这样的人。我们MathBTI把他们归入同一个族群:**信仰者**。
+
+---
+
+## 高斯:世界上最不在乎"优先权"的天才
+
+高斯3岁纠正父亲的工资账目。10岁,老师出了一道打发时间的题——1加到100——他秒算出来,因为发现首尾配对永远得101。
+
+但这些都不是他最厉害的故事。
+
+19岁那年,他在哥廷根大学的宿舍里坐了一整夜,用尺规作出了正十七边形。这个问题从欧几里得时代就困扰着数学家,整整两千年没人解出来。他做出来了,然后把这件事刻进了遗嘱——要求在墓碑上刻一个正十七边形。
+
+更惊人的事在后面:他发现了非欧几何。一个完全不同于我们日常经验的空间,三角形内角和不等于180度,平行线会相交。这是足以改写数学史的发现。但高斯一个字都没发表。
+
+为什么?他在给朋友的信里写:"我怕惊扰了那些蠢人的蜂巢。"
+
+**三十年后**,别人发表了同样的理论,成了"非欧几何之父"。高斯没有争辩。他的选择不是因为软弱,而是因为对他来说,发现本身就已经完成了——真理在脑子里被证明的那一刻,他就已经赢了。至于外人知不知道?那是外人的事。
+
+他定义了微分几何的高斯曲率;他发明了最小二乘法,让观测数据的噪声第一次有了数学上的最优解;正态分布至今仍是统计学的起点。这些东西,他未必每一件都发表了——但他每一件都算清楚了。
+
+**定理工匠**。不出手则已,一出手就是一篇完美的证明。
+
+
+## 祖冲之:用一堆小木棍,算出了一千年后人们才懂的答案
+
+祖冲之活在南北朝。那是一个什么样的时代?战乱、分裂、人命如草芥。
+
+他在油灯下,蹲在地上,摆弄一堆小木棍——叫算筹——一根一根推来推去。没有计算机,没有像样的纸,甚至连个平整的桌面都没有。他要用这些木棍去逼近一个不可能精确的数字:圆周率 π。
+
+他不知道自己的答案对不对,因为全世界没有人比他算得更准。他只能反复核对,一遍又一遍,错了就重新摆。
+
+最后,他算到了 3.1415926 到 3.1415927 之间。这个精度,保持了九百多年,直到15世纪才被阿拉伯数学家超越。
+
+他还给出了一个简洁到令人窒息的密率:**355/113**。六个数字,113355,拆成两半,恰好就是分子和分母。它漂亮得不像手算时代的产物——欧洲人直到16世纪才重新发现它。
+
+他还造了《大明历》,测出回归年仅差46秒。他改良了指南车,一种靠齿轮保持方向的机械,战场上用来辨别方位。天文、历法、机械,在他手里浑然一体。
+
+**穷竭守尺人**。用最原始的工具,做最精确的事。花一辈子,把一件事算到最准——不是因为非算不可,是因为"这个数必须是对的"。
+
+
+## 欧拉:眼瞎了之后,算得更快了
+
+数学史上最高产的人是谁?欧拉。886篇论文,死了之后留下足够出版50年的手稿。
+
+但这还不是最惊人的。
+
+28岁,右眼失明。59岁,双目全盲。欧拉说:"视力没了,心里的数学反而更清楚了。"
+
+这不是在说漂亮话——失明之后,他的数学产出翻了一倍。全部靠心算口述,让助手记录。一个盲人坐在那里,在脑海里推演复杂的微分方程、无穷级数——没有纸,没有黑板,甚至连符号都看不见。整条推导链在他大脑里像流水一样滑过。
+
+他解决了柯尼斯堡七桥问题——市民在问"散步时能不能每座桥只走一次"。欧拉没有画图——他发明了图论。把桥变成线,把陆地变成点,把整个城市变成一张图。他证明办不到,然后顺手定义了一个全新的学科。今天你用的导航、你刷的社交网络、你坐的高铁调度,底层都有图论的影子。
+
+还有那条被誉为"最美数学公式"的 **e^(iπ) + 1 = 0**:五个数学里最重要的常数,装在一行里。他写下它的时候,不过是某一天推到一半,随手记了下来。
+
+哦对了,你每天在用的函数符号 f(x)、圆周率 π、自然常数 e、虚数单位 i、求和符号 ∑——都是欧拉定的。不是因为他想"建立一套符号体系",是因为他写的论文实在太多了,不得不发明一套简写。**他顺手就定义了整个数学界的通用语言。**
+
+**公式狂僧**。停止计算的那一刻,才是停止生命的那一刻。
+
+
+## 诺特:被否定三次,数学还是站在她那边
+
+诺特是女人。在20世纪初的德国大学里,女性连旁听的资格都没有。她靠自学通过所有考试,拿到了博士学位。然后没人雇她。
+
+她的父亲是数学教授,私下资助她在哥廷根大学"义务讲课"——没有头衔、没有薪水、没有办公室。一教好几年,学生挤满了她的课堂。这些学生后来被称为"诺特学派",他们统治了现代代数学。但学校依然不给薪水。**第一次否定。**
+
+她研究的抽象代数,在当时的数学家眼里"几乎不属于数学"。太抽象了,连符号都看不懂。有人说:"这跟数学到底有什么关系?"**第二次否定。**
+
+1915年,她证明了诺特定理:物理学中每一种对称性,都对应一个守恒定律。能量守恒对应时间平移对称性,动量守恒对应空间平移对称性。这条定理是现代物理学的基石——没有它,就没有规范场论。
+
+爱因斯坦亲自写信为她争取教职,称她为"自女性接受高等教育以来最杰出的数学天才"。**第三次否定,破开了。**
+
+她拿到了教授职位。四年后,纳粹上台,她被驱逐。去美国,53岁死于手术并发症。
+
+她活着的时候,被否定了一次又一次。但她从来不争辩,因为数学本身不需要任何人的认证。真理就在那里,她只需要把它算出来、写下来、教给别人。
+
+**抽象代数宗师**。把最抽象的数学,讲得连门外汉都想入门。
+
+
+## 信仰者的共同秘密
+
+这四个人,分处不同的时代、不同的国家、不同的命运,但他们分享同一个秘密:
+
+**他们做数学,不是手段,是目的。**
+
+高斯不怕别人先发表,祖冲之不怕答案"未必是对的",欧拉不怕眼睛看不见,诺特不怕世界不承认她。
+
+因为对他们来说,数学不是用来换取名声、金钱或尊重的筹码。数学本身,就是报酬。
+
+你读到这里,也许觉得自己和他们不一样——你没有那么聪明,没那么执着,没那么不在乎外界。但MathBTI把"信仰者"设计成一个类型,不是因为只有高斯祖冲之才配做信仰者——而是因为,每个愿意把一道题从头推到尾、不去想"这有什么用"的人,身上都有信仰者的影子。
+
+---
+
+*你是哪一种数学人格?扫描下方二维码来测*
+*葫芦数学 · MathBTI · 12题发现你的数学灵魂*
diff --git a/docs/novel/公众号_应用者族.md b/docs/novel/公众号_应用者族.md
new file mode 100644
index 0000000..59621f3
--- /dev/null
+++ b/docs/novel/公众号_应用者族.md
@@ -0,0 +1,106 @@
+# 这四个人,用数学改变了真实世界的走向
+
+数学家里有两类人。一类人问"这个定理是不是对的",另一类人问"这个定理能拿来干什么"。
+
+今天要讲的是后一类。他们不把数学当信仰,也不试图让每个人都懂数学——他们用数学当武器,在世界这个棋盘上下注、破局、横扫。
+
+MathBTI 把他们归入同一个族群:**应用者**。
+
+---
+
+## 秦九韶:他用数学杀过人
+
+先讲一个你不一定能接受的人。
+
+秦九韶是南宋全才,精通数学、音律、建筑、诗文、带兵打仗。他写了一本书叫《数书九章》,里面81道题——不是抽象证明,是真实世界的数学:田亩测算、建筑工程、钱粮折算、军需调度。
+
+他提出了"大衍求一术",今天叫中国剩余定理。用来解决"一个数除以三余二、除以五余三、除以七余二,这个数是多少"这类问题。别小看它——今天你的网上支付、银行转账、甚至你在看这篇文章时的HTTPS加密,底层都有中国剩余定理的影子。
+
+但他的手不只握过笔。
+
+史书记载,秦九韶曾因私人恩怨毒杀亲信。他也用自己设计的计算模型帮助官府平定叛乱。他的数学既能计算粮草,也能决定生死。
+
+南宋的历史语境里,"活着"本身就是一门学问。在外部威胁步步紧逼的时代,一个知道如何精准计算的人,不可能只用在田亩上。
+
+**大衍谋士。数学不是信仰,不是美,是武器。可以用来说理,也可以用来杀人。他自己从未假装自己纯洁。**
+
+
+## 牛顿:18个月的隔离,他重塑了整个世界
+
+1665年,伦敦大瘟疫。剑桥大学关门,全体学生回家躲瘟疫。
+
+22岁的牛顿回到家乡伍尔斯索普庄园。在接下来的18个月里,他一个人完成了三件事:微积分的发明、万有引力定律的雏形、用棱镜分解白光的光学实验。
+
+数学史上最孤独、也最高产的一次隔离。
+
+1687年,他出版了《自然哲学的数学原理》。三条运动定律加万有引力定律——人类第一次用一套方程,统一了天上和地上的一切运动。行星为什么绕着太阳转、苹果为什么掉下来、潮汐为什么涨落——全归入了同一套公式。
+
+但牛顿的另一面同样让人难以直视。
+
+他与莱布尼茨争夺微积分优先权。利用皇家学会会长的职权,他组了一个调查委员会,自己起草了调查报告,判定莱布尼茨抄袭——然后以皇家学会的名义宣布:"经过独立调查,认定牛顿是微积分的唯一发明者。"
+
+他自己判自己胜诉。
+
+晚年他沉迷炼金术和神学,试图用数学推算出上帝创世的日期。他留下的神学手稿比科学手稿还多,超过三百万字。一生未婚。据说他晚年曾说:"我不知道我在世人眼里是什么——在我自己眼里,我只不过是在海滩上玩耍的孩子,偶尔捡到一块更光滑的石子。"
+
+**宇宙立法者。他统一了天地,无法统一自己。**
+
+
+## 图灵:他救了1400万人,然后世界把他碾碎了
+
+24岁,图灵发表了一篇论文《论可计算数》。他想象了一台"永远不存在的机器"——一条无限纸带、一个读写符号的装置、一套简单规则。他证明了这台假想机器能计算任何可以被计算的东西。他用"图灵机"定义了"计算"本身。冯·诺依曼后来说,现代计算机架构里最关键的概念——"存储程序"——就来自图灵这篇论文。
+
+然后二战来了。
+
+德国人用 Enigma 密码机。每天换一种加密方式,数量级大到手算永远破不了。图灵去了布莱切利庄园,和一群数学家、象棋冠军、填字游戏高手一起,设计了一台叫 Bombe 的解密机。他们破译了 Enigma。
+
+历史学家的共识:这让二战提前两年结束,至少拯救了1400万人的生命。
+
+战后,图灵的贡献成为国家机密,封存了三十年。公众不知道他做过什么。1952年,他因同性恋被定罪,被迫接受化学阉割。两年后,他咬下一口涂了氰化物的苹果,去世。41岁。
+
+2013年,英国女王颁布皇家赦免。2019年,他登上50英镑纸币。
+
+**密码破译使。他的数学拦住了纳粹。他的国家没拦住偏见。这两个事实叠加在一起,比任何单独的悲剧或英雄叙事都更沉重。**
+
+
+## 冯·诺依曼:他把世界当一盘棋,而且每盘都赢
+
+冯·诺依曼6岁能心算八位数除法,还会跟他爸开玩笑说:"你的账目少了一分钱。"8岁掌握微积分,22岁拿下博士学位。
+
+然后他开始在完全不同的领域之间跳来跳去。每一个,都留下一座里程碑。
+
+他创立了博弈论。用数学框架分析"人在对抗中如何决策"——《博弈论与经济行为》成为经济学、政治学、军事战略的圣经。冷战核威慑理论——你知我知你知,层层嵌套的"共同知识"概念,就出自博弈论矩阵。
+
+他加入曼哈顿计划,设计了原子弹内爆的数学模型。不是造炸弹——是用方程算爆炸。
+
+他回到计算领域,写了《关于EDVAC的报告》,定义现代计算机的基本架构:控制器、运算器、存储器、输入输出——"冯·诺依曼架构"至今是一切电脑的基石。你现在读到这篇文章,底层的CPU就是这个架构的后代。
+
+有人问他:"你研究博弈论,是不是因为你喜欢下棋?"
+
+他回答:"不。我研究博弈论是为了理解核威慑。棋太简单了。"
+
+**博弈游侠。把世界当一盘棋。而且他每一盘都赢了。**
+
+
+---
+
+## 应用者共同的秘密
+
+信仰者关心"定理是否完美"。传播者关心"别人能不能看懂"。直觉者关心"结构是不是能一眼看见"。
+
+应用者不关心这些。他们只关心一个问题:**"这个数学,能不能改变现实?"**
+
+秦九韶用剩余定理算军粮。牛顿用微积分统一天地。图灵用算法破译纳粹密码。冯·诺依曼用博弈论定义冷战。
+
+他们的共同密码是:用数学出手,世界就真的会因此不同。
+
+——
+
+但数学里还有最后一种人。他们甚至不用算——看一眼就够了。
+
+下期见。第四族:直觉者。
+
+---
+
+*你是哪一种数学人格?扫描下方二维码来测*
+*葫芦数学 · MathBTI · 12题发现你的数学灵魂*
diff --git a/docs/novel/公众号_直觉者族.md b/docs/novel/公众号_直觉者族.md
new file mode 100644
index 0000000..c09ead8
--- /dev/null
+++ b/docs/novel/公众号_直觉者族.md
@@ -0,0 +1,116 @@
+# 这四个人做数学,不用算——看一眼就够了
+
+前三期我们讲了信仰者(做数学如念经)、传播者(让全天下都看懂数学)、应用者(用数学改变世界)。
+
+还有最后一类人。他们是最难描述的一群。
+
+他们做数学,不靠推导,不靠死算。他们靠"看见"。一条辅助线该画在哪里,一个结构背后藏着什么——别人需要十页证明的东西,他们一眼就看穿了。
+
+MathBTI 把这种人格归入最后一个族群:**直觉者**。
+
+这类人身上都有一个共同的特点:他们留给世界的东西很少——往往只是一张图、一个猜想、一次凝视。但就是这一眼,让后来的数学家追了一千年。
+
+---
+
+## 希帕提娅:公元415年,人类为杀死一个女数学家的代价是什么
+
+希帕提娅是历史上第一位有记载的女数学家。她生活在公元4世纪的埃及亚历山大城——彼时罗马帝国已经皈依基督教,古典希腊文化正在被系统地清除。亚历山大城是古代世界知识的最后堡垒,它的图书馆藏着希腊、埃及、巴比伦的所有智慧。
+
+希帕提娅继承了父亲的学术衣钵,在学院里讲柏拉图、亚里士多德、欧几里得。她是新柏拉图学派的领袖,学生从帝国各地慕名而来。她改良了星盘——一种用来测量天体高度和纬度的仪器——和比重计。这两种仪器被后人沿用了1400多年。
+
+但她的位置太危险了:一个异教徒、一个女人、一个公开讲学的人、一个在宗教冲突中保持理性中立的人。
+
+415年,一群被主教煽动的暴民把她从马车上拖下来,拖进教堂,用陶片活活刮死,然后把尸体撕碎、焚烧。
+
+她死后,亚历山大图书馆被毁,古典希腊数学随之终结。希帕提娅的死被后世史学家视为"古典时代的终结"——从那天起,黑暗时代开始了。
+
+**几何殉道者。她不是在做题,她是在为理性殉道。人类杀死她的同时,杀死了自己保存了七百年的知识体系。**
+
+
+## 赵爽:他用一张图,让全世界数学家追了1800年
+
+赵爽只留下了一部作品:《周髀算经注》。他只注了一本书。
+
+但就在这本书里,他做了一件让整个数学史惊叹的事。
+
+勾股定理——直角三角形两直角边的平方和等于斜边的平方——如何证明?赵爽没有写长篇大论。他画了一张图:"弦图"。
+
+在一个正方形里,用四个直角三角形拼出一个中间的小正方形。然后他写了一句话:"按弦图,又可以勾股相乘为朱实二,倍之为朱实四,以勾股之差自相乘为中黄实,加差实亦成弦实。"
+
+翻译成现代话:看看这张图,你就看见了。
+
+他让历史上最难解释的定理之一,变成了一张"看一眼就懂"的图。这不是因为他的推导简单——恰恰相反,能用一张图让人"看见"一个复杂的真理,需要比写出十页证明更深的直觉。
+
+这不是一条辅助线。这是一次直觉的跳跃。
+
+2002年,国际数学家大会的会徽选取了赵爽的弦图——1800年后,全世界数学家公认:这张图是人类数学史上最美的证明之一。
+
+**弦图先觉。一张图,比十本书长寿。你不用说话,你只需要让人看见你看见的。**
+
+
+## 庞加莱:他在公交车上"看见"了答案
+
+庞加莱是公认的"最后一位通晓当时所有数学分支的人"。在他之后,数学变得太过庞大,再没有人能掌握全部。
+
+有一件事他反复在自传中提到。
+
+当时他在研究富克斯函数——一类在复数平面上具有高度对称性的复杂函数。他花了几个月,苦思冥想找不到突破口。然后某一天,他正要去参加一个地质学会的考察旅行,刚踏上一辆公交车的台阶——
+
+他自己写道:"就在我把脚放上踏板的一瞬间,我忽然意识到,用来定义富克斯函数的变换,和双曲几何里的变换是完全一样的。"
+
+不是推算出来的。是他"看见"的。
+
+他没有立刻冲回去演算。他继续上了车,继续跟同行的人聊天,参加了整整一天的地质考察。因为他说:"我知道它不会再跑掉了。"
+
+这就是直觉者的典型状态:他们的大脑在后台一直运转着——当逻辑推不下去的时候,直觉接手了。它当然不是凭空来的——它是几个月的推理积累,在某个临界点突然"串起来了"。
+
+他提出的"庞加莱猜想"困扰了数学界整整100年,直到2003年才被佩雷尔曼证明。而佩雷尔曼证明它之后,拒绝了菲尔兹奖和一百万美元的奖金,退出了公众视野——用庞加莱式的方式,转身离开。
+
+**拓扑游方。他在公交车踏板上"看见"了答案,然后继续旅行——因为他知道它不会再跑掉了。**
+
+
+## 凯瑟琳·约翰逊:她在NASA手算了33年,算到直觉比公式更快
+
+凯瑟琳·约翰逊在NASA做了33年"人脑计算机"。
+
+1961年,美国第一次载人航天飞行。宇航员艾伦·谢泼德坐进火箭——他是一流的飞行员,但他不信任那台刚引进的IBM计算机。他在发射前说了一句话:"让那个女孩核对一下数字。"
+
+"那个女孩"就是凯瑟琳·约翰逊。她用的工具是:纸、铅笔、计算尺。一步一个数字,从头算到尾。
+
+她算对了。谢泼德飞了。
+
+1962年,约翰·格伦要上轨道了。这次弹道计算已经在IBM上跑过了,但格伦在发射前点名:"让她重新手算一遍——她说行,我就飞。"
+
+凯瑟琳的手算轨道确认了IBM的结果。格伦成了美国第一个绕地球飞行的宇航员。
+
+她和一群被称为"穿着裙子的计算机"的黑人女性数学家们,在种族隔离的年代,坐在NASA的"有色人种计算部"里,用人脑完成了阿波罗登月的轨道计算。
+
+但凯瑟琳最让人震撼的不是她的计算速度——而是她的"直觉"。33年不间断的手算,让她对数字形成了一种近乎本能的判断力。她的同事说:"我们看一张弹道轨迹图,只看得到一堆数字。凯瑟琳看同一张图,她说——这个数不对。"
+
+不是算出来的——是感觉出来的。但这种感觉,是33年每天8小时一笔一笔磨出来的。
+
+2020年她101岁辞世。她的一生恰好穿越了计算从纸笔走向AI的整个世纪——她既是纸笔计算的终点,也是数字时代的起点。
+
+**人脑计算机。她的直觉不是天生的。它是一天一笔算出来的——算到后来,不需要算了。**
+
+
+---
+
+## 直觉者的共同秘密
+
+直觉者不是不努力的人。恰恰相反。
+
+赵爽在画出弦图之前,他的大脑里有一千张被扔掉的草图。庞加莱说他"看见"富克斯函数的那个瞬间,是几个月日思夜想的大脑在后台算出的结果。凯瑟琳·约翰逊能"感觉"一个数不对,背后是33年的手算记忆。
+
+直觉是一种被压缩的时间。
+
+他们把漫长的推理压缩进了一个瞬间,然后说:"你看,就在那。"
+
+信仰者推到尽头。传播者讲到每个人明白。应用者用到世界改变。直觉者呢?
+
+**他们看见了,然后指给你看。**
+
+---
+
+*你是哪一种数学人格?扫描下方二维码来测*
+*葫芦数学 · MathBTI · 12题发现你的数学灵魂*
diff --git a/docs/novel/场景编号001.docx b/docs/novel/场景编号001.docx
new file mode 100644
index 0000000..6ae82b1
Binary files /dev/null and b/docs/novel/场景编号001.docx differ
diff --git a/docs/novel/数学人格测试_16位数学家小传.docx b/docs/novel/数学人格测试_16位数学家小传.docx
new file mode 100644
index 0000000..0b60306
Binary files /dev/null and b/docs/novel/数学人格测试_16位数学家小传.docx differ
diff --git a/docs/novel/数学家小传_信仰者.md b/docs/novel/数学家小传_信仰者.md
new file mode 100644
index 0000000..1bfb95c
--- /dev/null
+++ b/docs/novel/数学家小传_信仰者.md
@@ -0,0 +1,112 @@
+# 信仰者族 · 数学家小传
+> "把数学当信仰的人——不为名不为利,只为真理本身"
+
+---
+
+## 高斯:锁在抽屉里的天才
+
+### 人物档案
+- 姓名:卡尔·弗里德里希·高斯(Carl Friedrich Gauss)
+- 年代:1777-1855,德国
+- 称号:数学王子
+- MathBTI类型:定理工匠
+
+### 小传
+
+高斯3岁纠正父亲的工资账目,10岁秒算1到100——这些故事太著名了,以至于听起来像传说。但真正体现高斯钻研精神的,是他19岁那年做的一件事。
+
+欧几里得以来两千年,没人能用尺规作出正十七边形。19岁的高斯坐在哥廷根大学的宿舍里,花了一整夜,证明了这个作图是可能的,并且给出了精确的作图方法。这不是"灵光一闪"的天才故事——他花了整整一夜,在纸上反复推演,一步一步逼近那个隐藏在圆背后的优雅结构。
+
+他一生以此为傲。遗嘱里唯一的要求:在墓碑上刻正十七边形。后来工匠说"太难刻了,看起来跟圆没区别",但没有这句话更准确了——高斯的钻研,外人根本看不出跟普通努力有什么区别。
+
+他在微分几何中定义了高斯曲率,让曲面第一次有了可量化的"弯曲度";他发明最小二乘法,让数据中的噪声第一次有了数学上的最优解;正态分布(高斯分布)至今仍是一切统计推断的起点。但他最震撼的事迹,是一件"没有发表"的事。
+
+高斯的书信里藏着秘密:他大概在1817年就发现了非欧几何——一个完全不同于欧几里得的几何体系,在那里三角形内角和不等于180度,平行线会相交。但他一个字都没发表。理由是什么?他在给朋友的信里写道:"我怕惊扰了那些蠢人的蜂巢。"
+
+**他怕被别人骂,于是他选择不发表。整整30年。**
+
+30年后,俄国人罗巴切夫斯基和匈牙利人鲍耶分别独立发表了非欧几何,成为"发现者"。高斯没有争辩,他的钻研从来不为"优先权"——他为的是自己心里那条完美的逻辑链条。
+
+这就是定理工匠:他不出手则已,一出手就是一篇完美的证明。至于别人知不知道?那是别人的事。
+
+
+## 祖冲之:用算筹对抗时间
+
+### 人物档案
+- 姓名:祖冲之
+- 年代:429-500,中国南朝
+- 称号:算不烂的守尺人
+- MathBTI类型:穷竭守尺人
+
+### 小传
+
+南北朝是什么时代?战乱、分裂、人命如草。祖冲之在这样的时代里,坐在一盏油灯下,手边一堆小木棍——叫算筹——一根一根摆来摆去,一算就是大半辈子。
+
+他要算什么?算一个数字:圆周率 π。
+
+他把π算到了3.1415926到3.1415927之间。你可能会想:这有什么?现在计算机一秒钟能算几百万位。但你要知道,祖冲之用的是算筹——一种靠在地面上摆弄小棍来进行的计算方式。他没有计算器,没有纸笔(当时的纸又贵又粗),连像样的书桌都没有。他就蹲在地上,摆出几万根算筹,记录中间结果,错了重来——而且他永远不知道自己的答案是不是"对的",因为没有人比他算得更准。
+
+他算了多久?史书没写。但那个精度——3.1415926——一千一百多年后才被打破。
+
+他还给出了**密率 355/113**。这个分数之美,令人窒息:分母113,分子355,恰好取自 113355 这六位数。一个简洁到不像手算时代的产物,欧洲人直到16世纪才重新发现它。
+
+你以为他只是个算数书呆子?他同时也是一流的工程师和天文学家。他制定了《大明历》,测算出回归年仅差46秒;他改良了指南车——一种靠齿轮传动保持方向的机械装置,在战场上用来辨别方位。在他留下的文字里,天文、历法、数学、机械浑然一体——在那个时代,真正的钻研者不分文理。
+
+他用算筹对抗时间。一千年后,世上还有人记得他算的那个数。这不是因为他比天才更天才——是因为他愿意花一辈子,把一件事算到最准。
+
+
+## 欧拉:失明后,算得更快了
+
+### 人物档案
+- 姓名:莱昂哈德·欧拉(Leonhard Euler)
+- 年代:1707-1783,瑞士/俄国
+- 称号:数学史上的永动机
+- MathBTI类型:公式狂僧
+
+### 小传
+
+数学史上最高产的人是谁?欧拉。886篇论文,死后留下的手稿还够出版50年。但这不是最惊人的。
+
+28岁,右眼失明。59岁,双目全盲。欧拉说:"视力没了,心里的数学更清楚了。"他不是在说漂亮话——失明之后,他的产出**翻倍了**。全部靠心算,口述,让助手誊写。
+
+想象一下:一个盲人,在脑海里推演复杂的微积分、微分方程、无穷级数,不需要纸,不需要黑板,甚至不需要看到任何符号——整个推导过程在大脑中像流水一样流过。他的家人说,他常抱着刚出生的婴儿,腿上放着一沓稿纸,一边哄孩子一边口述定理。13个孩子,5个存活,家中的吵闹可想而知——但他算进去了。
+
+他最著名的两个贡献,都有一个共同的特点:**他根本没打算创造一个新学科。**
+
+柯尼斯堡有七座桥,当地的市民有一个奇怪的爱好——散步时能不能每座桥只走一次?欧拉听了这个问题,觉得很有趣。然后他没有画画——他发明了**图论**。他把桥变成线,把陆地变成点,把整个城市变成一幅图。他证明了这件事不可能做到,然后顺手定义了一个全新的数学领域。今天你用的导航、你刷的社交网络、你坐的高铁调度——底层都有图论的影子。
+
+另一次,他写下了:**e^(iπ) + 1 = 0**。就这么短,但里面装了数学里最重要的五个常数——e、i、π、1、0。后人称之为"最美数学公式"。而他写下它的时候,不过是某一天推演到一半,随手记下来的。
+
+还有一件事你可能每天都在享受欧拉的劳动成果:函数符号 f(x)、圆周率 π、自然常数 e、虚数单位 i、求和符号 ∑——这些沿用至今的数学记号,都是欧拉定的。不是因为他想"建立一套符号体系",而是因为他写的论文太多了,不得不想出一套方便的简写。**他顺手就定义了整个数学界的通用语言。**
+
+这就是公式狂僧:他不是因为"想赢"才写论文,而是因为停不下来。数学不是他的职业,是他的呼吸。
+
+
+## 诺特:她不在乎
+
+### 人物档案
+- 姓名:埃米·诺特(Emmy Noether)
+- 年代:1882-1935,德国
+- 称号:抽象代数之母
+- MathBTI类型:抽象代数宗师
+
+### 小传
+
+数学史上被否定最多次的人,大概就是诺特。
+
+她是女人——在20世纪初的德国大学里,女性连旁听资格都没有。她靠自学考过了所有课程,终于被允许参加考试,获得了博士学位。然后呢?没有学校肯雇她。
+
+她的父亲是数学教授,私下资助她在哥廷根大学"义务讲课"——没有头衔、没有薪酬、没有办公室。一教就是好几年。这期间她创立了"诺特学派",学生自发挤满她的课堂,这些人后来统治了现代代数学。但学校依然不给薪水。
+
+第二次否定来自世界。她研究的抽象代数,在当时的数学家看来"几乎不属于数学"——太抽象了,连符号都看不懂。有人嘲讽说:"这跟数学有什么关系?她到底在研究什么?"
+
+诺特不在乎。
+
+她继续推演她的抽象结构,在黑板上写满没人能一眼看懂的公式。她的学生给她的绰号不是"诺特教授"(毕竟她连教授都不是),而是"der Noether"——一个亲昵又尊敬的称呼。
+
+第三次否定在1915年之后被打破。当年她证明了**诺特定理**——物理学中每一种对称性,都对应一个守恒定律。能量守恒对应时间平移对称性,动量守恒对应空间平移对称性。这个定理是现代物理学的基石,没有它,杨振宁的规范场论无从出发。爱因斯坦亲自写信为她争取教职,称她为"自女性接受高等教育以来最杰出的数学天才"。
+
+她最终拿到了教授职位——但四年后就因纳粹政策被驱逐。她去了美国,又在短短几年内培养了一批新的弟子,直到53岁因手术并发症去世。
+
+被否定三次,她还是把最抽象的数学,讲得连门外汉都想入门。她的钻研不需要任何人来认证——因为数学永远站在她这边。
+
diff --git a/docs/novel/数学家小传_全16位.md b/docs/novel/数学家小传_全16位.md
new file mode 100644
index 0000000..2343054
--- /dev/null
+++ b/docs/novel/数学家小传_全16位.md
@@ -0,0 +1,426 @@
+# 数学人格测试 · 16位数学家小传
+
+---
+
+# 第一族:信仰者 — "把数学当信仰的人"
+
+---
+
+## 高斯:锁在抽屉里的天才
+
+### 人物档案
+- 姓名:卡尔·弗里德里希·高斯(Carl Friedrich Gauss)
+- 年代:1777-1855,德国
+- 称号:数学王子
+- MathBTI类型:定理工匠
+
+### 小传
+
+高斯3岁纠正父亲的工资账目,10岁秒算1到100——这些故事太著名了,以至于听起来像传说。但真正体现高斯钻研精神的,是他19岁那年做的一件事。
+
+欧几里得以来两千年,没人能用尺规作出正十七边形。19岁的高斯坐在哥廷根大学的宿舍里,花了一整夜,证明了这个作图是可能的,并且给出了精确的作图方法。这不是"灵光一闪"的天才故事——他花了整整一夜,在纸上反复推演,一步一步逼近那个隐藏在圆背后的优雅结构。
+
+他一生以此为傲。遗嘱里唯一的要求:在墓碑上刻正十七边形。后来工匠说"太难刻了,看起来跟圆没区别",但没有这句话更准确了——高斯的钻研,外人根本看不出跟普通努力有什么区别。
+
+他在微分几何中定义了高斯曲率,让曲面第一次有了可量化的"弯曲度";他发明最小二乘法,让数据中的噪声第一次有了数学上的最优解;正态分布(高斯分布)至今仍是一切统计推断的起点。但他最震撼的事迹,是一件"没有发表"的事。
+
+高斯的书信里藏着秘密:他大概在1817年就发现了非欧几何——一个完全不同于欧几里得的几何体系,在那里三角形内角和不等于180度,平行线会相交。但他一个字都没发表。理由是什么?他在给朋友的信里写道:"我怕惊扰了那些蠢人的蜂巢。"
+
+他怕被别人骂,于是他选择不发表。整整30年。30年后,俄国人罗巴切夫斯基和匈牙利人鲍耶分别独立发表了非欧几何,成为"发现者"。高斯没有争辩,他的钻研从来不为"优先权"——他为的是自己心里那条完美的逻辑链条。
+
+这就是定理工匠:他不出手则已,一出手就是一篇完美的证明。至于别人知不知道?那是别人的事。
+
+
+## 祖冲之:用算筹对抗时间
+
+### 人物档案
+- 姓名:祖冲之
+- 年代:429-500,中国南朝
+- 称号:算不烂的守尺人
+- MathBTI类型:穷竭守尺人
+
+### 小传
+
+南北朝是什么时代?战乱、分裂、人命如草。祖冲之在这样的时代里,坐在一盏油灯下,手边一堆小木棍——叫算筹——一根一根摆来摆去,一算就是大半辈子。
+
+他要算一个数字:圆周率 π。
+
+他把π算到了3.1415926到3.1415927之间。你可能会想:这有什么?现在计算机一秒钟能算几百万位。但你要知道,祖冲之用的是算筹——一种靠在地面上摆弄小棍来进行的计算方式。他没有计算器,没有纸笔(当时的纸又贵又粗),连像样的书桌都没有。他就蹲在地上,摆出几万根算筹,记录中间结果,错了重来——而且他永远不知道自己的答案是不是"对的",因为没有人比他算得更准。
+
+他算了多久?史书没写。但那个精度——3.1415926——一千一百多年后才被打破。
+
+他还给出了密率 355/113。这个分数之美,令人窒息:分母113,分子355,恰好取自113355这六位数。一个简洁到不像手算时代的产物,欧洲人直到16世纪才重新发现它。
+
+你以为他只是个算数书呆子?他同时也是一流的工程师和天文学家。他制定了《大明历》,测算出回归年仅差46秒;他改良了指南车——一种靠齿轮传动保持方向的机械装置,在战场上用来辨别方位。在他留下的文字里,天文、历法、数学、机械浑然一体——在那个时代,真正的钻研者不分文理。
+
+他用算筹对抗时间。一千年后,世上还有人记得他算的那个数。这不是因为他比天才更天才——是因为他愿意花一辈子,把一件事算到最准。
+
+
+## 欧拉:失明后,算得更快了
+
+### 人物档案
+- 姓名:莱昂哈德·欧拉(Leonhard Euler)
+- 年代:1707-1783,瑞士/俄国
+- 称号:数学史上的永动机
+- MathBTI类型:公式狂僧
+
+### 小传
+
+数学史上最高产的人是谁?欧拉。886篇论文,死后留下的手稿还够出版50年。但这不是最惊人的。
+
+28岁,右眼失明。59岁,双目全盲。欧拉说:"视力没了,心里的数学更清楚了。"他不是在说漂亮话——失明之后,他的产出翻倍了。全部靠心算,口述,让助手誊写。
+
+想象一下:一个盲人在脑海里推演复杂的微积分、微分方程、无穷级数,不需要纸,不需要黑板,甚至不需要看到任何符号——整个推导过程在大脑中像流水一样流过。他的家人说,他常抱着刚出生的婴儿,腿上放着一沓稿纸,一边哄孩子一边口述定理。13个孩子,5个存活,家中的吵闹可想而知——但他算进去了。
+
+他最著名的两个贡献,都有一个共同的特点:他根本没打算创造一个新学科。
+
+柯尼斯堡有七座桥,当地的市民有一个奇怪的爱好——散步时能不能每座桥只走一次?欧拉听了这个问题,觉得很有趣。然后他没有画画——他发明了图论。他把桥变成线,把陆地变成点,把整个城市变成一幅图。他证明了这件事不可能做到,然后顺手定义了一个全新的数学领域。今天你用的导航、你刷的社交网络、你坐的高铁调度——底层都有图论的影子。
+
+另一次,他写下了 e^(iπ) + 1 = 0。就这么短,但里面装了数学里最重要的五个常数——e、i、π、1、0。后人称之为"最美数学公式"。而他写下它的时候,不过是某一天推演到一半,随手记下来的。
+
+还有一件事你可能每天都在享受欧拉的劳动成果:函数符号 f(x)、圆周率 π、自然常数 e、虚数单位 i、求和符号 ∑——这些沿用至今的数学记号,都是欧拉定的。不是因为他想"建立一套符号体系",而是因为他写的论文太多了,不得不想出一套方便的简写。他顺手就定义了整个数学界的通用语言。
+
+这就是公式狂僧:他不是因为"想赢"才写论文,而是因为停不下来。数学不是他的职业,是他的呼吸。
+
+
+## 诺特:她不在乎
+
+### 人物档案
+- 姓名:埃米·诺特(Emmy Noether)
+- 年代:1882-1935,德国
+- 称号:抽象代数之母
+- MathBTI类型:抽象代数宗师
+
+### 小传
+
+数学史上被否定最多次的人,大概就是诺特。
+
+她是女人——在20世纪初的德国大学里,女性连旁听资格都没有。她靠自学考过了所有课程,终于被允许参加考试,获得了博士学位。然后呢?没有学校肯雇她。
+
+她的父亲是数学教授,私下资助她在哥廷根大学"义务讲课"——没有头衔、没有薪酬、没有办公室。一教就是好几年。这期间她创立了"诺特学派",学生自发挤满她的课堂,这些人后来统治了现代代数学。但学校依然不给薪水。第一次否定。
+
+她研究的抽象代数,在当时的数学家看来"几乎不属于数学"——太抽象了,连符号都看不懂。有人嘲讽说:"这跟数学有什么关系?她到底在研究什么?"第二次否定。
+
+1915年她证明了诺特定理——物理学中每一种对称性,都对应一个守恒定律。能量守恒对应时间平移对称性,动量守恒对应空间平移对称性。这个定理是现代物理学的基石,没有它,杨振宁的规范场论无从出发。爱因斯坦亲自写信为她争取教职,称她为"自女性接受高等教育以来最杰出的数学天才"。第三次否定,破开了。
+
+她最终拿到了教授职位——但四年后就因纳粹政策被驱逐。她去了美国,又在短短几年内培养了一批新的弟子,直到53岁因手术并发症去世。
+
+被否定三次,她还是把最抽象的数学,讲得连门外汉都想入门。她的钻研不需要任何人来认证——因为数学永远站在她这边。
+
+
+---
+
+# 第二族:传播者 — "让不懂数学的人也能看见数学之美"
+
+---
+
+## 斐波那契:他把阿拉伯数字带过地中海
+
+### 人物档案
+- 姓名:莱昂纳多·斐波那契(Leonardo Fibonacci)
+- 年代:约1170-1250,意大利
+- 称号:东西方数学的翻译者
+- MathBTI类型:商路算郎
+
+### 小传
+
+13世纪的欧洲,人们算账还在用罗马数字。你试试用 VII 乘以 XLII 看看——光是把这两个数字写在纸上就要想半天。
+
+斐波那契是一个意大利商人的儿子,随父亲在北非经商长大。在那里,他第一次接触到了阿拉伯数字——0、1、2、3……一个远在印度发明、被阿拉伯人传播到地中海的计数系统。
+
+他立刻就意识到:如果欧洲人能用这套数字,做生意会快十倍。于是他写了一本书,叫《算盘书》,把阿拉伯数字和十进制计数法系统地介绍给欧洲。这不是一本"纯数学"著作——这本书的每一道例题都是商业场景:利润分摊、货币兑换、货物计量。帮商人算账,帮会计省事。
+
+他最有名的事迹,在这本书里只是一道练习题:假设有一对兔子,每月生一对小兔子,小兔子两个月后也能生——一年后有多少对兔子?他算出结果是 1, 1, 2, 3, 5, 8, 13, 21……这个数列后来被称为斐波那契数列——松果的螺旋、向日葵的种子排列、鹦鹉螺的壳,都能找到它的影子。
+
+他本人大概从没想过"兔子问题"会变成一个数学名片。他只是一个在东西方之间来回走动的商人,看见了好工具,就把它翻译过来、讲给需要的人听。
+
+这就是商路算郎:学数学不是为了自己懂,是为了让别人也懂。
+
+
+## 华罗庚:他把数学从大学讲台,讲到了田间地头
+
+### 人物档案
+- 姓名:华罗庚
+- 年代:1910-1985,中国
+- 称号:数学的布道者
+- MathBTI类型:双法布道者
+
+### 小传
+
+华罗庚左腿残疾、初中学历、杂货铺出身。
+
+这三个标签放在任何时代,都意味着"不可能成为数学家"。但他自学了《微积分》《数论》,22岁在上海的《科学》杂志发表论文,被清华数学系主任熊庆来发现。一个杂货铺伙计,被请进了清华园。
+
+但华罗庚最令人震撼的选择,发生在功成名就之后。
+
+新中国成立后,他没有安坐书斋写论文。他揣着粉笔,走遍中国23个省市区——不是去开学术会议,是去工厂、农村、矿山,给普通工人和农民讲"优选法"和"统筹法"。什么是优选法?就是"怎么用最少的实验次数找到最佳配方"。什么是统筹法?就是"怎么安排工序让整个工程最快完成"。
+
+他是顶级数论学家,在解析数论、矩阵几何、多复变函数论上都有开创性贡献。但他选择把数学翻译成每个人都能听懂的语言,走到哪里讲到哪。他相信数学不属于象牙塔,属于每一个人。
+
+1985年,他在东京大学做完一场学术报告后心脏病发作,倒在讲台上。他说的最后一句话是:"我要工作到生命的最后一刻。"
+
+他一生的动力不是超越别人,是让更多人看见数学——这比任何一篇论文都更需要耐心和信念。
+
+
+## 伽罗瓦:决斗前夜,他通宵写了一封信
+
+### 人物档案
+- 姓名:埃瓦里斯特·伽罗瓦(Évariste Galois)
+- 年代:1811-1832,法国
+- 称号:浪漫主义数学的殉道者
+- MathBTI类型:决斗狂热者
+
+### 小传
+
+伽罗瓦只活了20年。在这20年里,他17岁奠定了群论的基础,是一个全新的代数学分支,彻底改变了数学的面貌。但他拿不到学位,没有大学肯收他的论文,投稿被退回——因为"谁也看不懂他在写什么"。
+
+他不愤怒吗?他愤怒。但他的愤怒不是在酒吧里跟人吵架——他的愤怒是:既然没人看得懂,那就再多写一点。
+
+1832年5月29日,他卷入一场因情感纠葛引发的决斗。临行前夜,他没有睡觉。他坐下来,开始疯狂写信——写给朋友,写给数学界,写给未来。他在纸页的空白处匆匆写道:"我没有时间了。"然后他把群论的完整思想塞进了这封信。
+
+这封"决斗前夜的信",后来被保存了14年才被数学家刘维尔发现。刘维尔读完震惊了——这个20岁的孩子,在他生命的最后一夜,把一种全新的数学思维方式扔给了未来。今天"伽罗瓦理论"是每个数学系研究生的必修课。五次及以上方程为什么没有通用根式解?伽罗瓦用群论回答了这个悬疑了近300年的问题。
+
+他活在"不被理解"的困境里,却用生命最后的几个小时,完成了数学史上最伟大的"传播"——把一个没有时间活下去的人头脑中的真理,寄给了活着的世界。
+
+
+## Ada Lovelace:在计算机还没诞生之前,她就写出了第一个程序
+
+### 人物档案
+- 姓名:艾达·洛夫莱斯(Ada Lovelace)
+- 年代:1815-1852,英国
+- 称号:史上第一位程序员
+- MathBTI类型:织机先知
+
+### 小传
+
+Ada的父亲是拜伦——那个写出"如果我们不再相见,祝你早安、午安、晚安"的浪漫主义诗人。她母亲怕她跟父亲一样"疯",从小就让她学数学。母亲说,数学是最好的镇静剂,能把人绑在理性上。
+
+但Ada在数学里看到的不是"镇静",而是巨大的想象力。
+
+1843年,28岁的她翻译了一篇关于巴贝奇"分析机"的文章。"分析机"是一台机械计算器,由齿轮和杠杆驱动,从未造出来过。Ada在翻译时,附上了自己的"注释"——比原文长三倍。
+
+在这些注释里,她做了两件划时代的事。
+
+第一,她写出了一个计算伯努利数的完整步骤——被称为人类历史上第一个计算机程序。在分析机还不存在的年代,她就已经在"为它编程"了。
+
+第二,她提出了一个当时没人想到的观点:计算机不仅能算数,还能作曲、画图、处理任何可以用符号表示的东西。她说:"分析机编织代数图案,就像提花织机编织花朵和叶子。" 她看到的不是一台超级计算器,而是一台通用符号处理器——这个洞见比第一台通用计算机早了一百年。
+
+她死于36岁,子宫癌。她的名字今天被用来命名了Ada编程语言。每年十月的第二个星期二被称为"Ada Lovelace日"。
+
+这就是织机先知:她不是数学家——她是在数学和机器的缝隙里,看见了未来的那个人。
+
+
+---
+
+# 第三族:应用者 — "用数学解决真实世界的问题"
+
+---
+
+## 秦九韶:他用数学杀过人
+
+### 人物档案
+- 姓名:秦九韶
+- 年代:1208-1268,中国南宋
+- 称号:数学世界的谋士
+- MathBTI类型:大衍谋士
+
+### 小传
+
+南宋可能是中国历史上最畸形的时代之一:文明高度发达,同时外部威胁步步逼近。秦九韶就是一个典型的南宋"全才"——精通数学、音律、建筑、诗文、带兵打仗。
+
+他写了一本书叫《数书九章》,里面有81道应用题。不是抽象证明,是真实世界的数学:田亩测算、建筑工程、钱粮折算、军需调度。他提出了"大衍求一术"——今天叫中国剩余定理——用来解决"一个数除以三余二、除以五余三、除以七余二,这个数是多少"这类问题。这东西在今天被用于密码学,保护你在网上输入的每一行密码。
+
+但他的手不只握过笔。
+
+史书记载,秦九韶"性机巧、好奢侈",曾因私人恩怨毒杀亲信,也用自己设计的计算模型帮助官府平定叛乱。他的数学既能计算粮草,也能决定生死。他是一个什么样的人?数学家说他空前绝后,史官说他品行不端。这两个评价,可能都对。
+
+这正是大衍谋士的本质:数学不是信仰,不是美,是工具。可以用来造福,也可以用来杀人。他自己也从未假装自己纯洁。
+
+
+## 牛顿:统一了天地,统一不了自己
+
+### 人物档案
+- 姓名:艾萨克·牛顿(Isaac Newton)
+- 年代:1643-1727,英国
+- 称号:宇宙的立法者
+- MathBTI类型:宇宙立法者
+
+### 小传
+
+1665年,伦敦大瘟疫,剑桥大学关门。22岁的牛顿回到家乡庄园躲瘟疫。在接下来的18个月里,他一个人完成了:微积分的发明、万有引力定律的雏形、光的分光实验——史上最孤独、最高产的隔离期。
+
+1687年,他出版了《自然哲学的数学原理》。三条运动定律加万有引力定律,人类第一次用一套方程统一了天上和地上的一切运动——行星为什么绕着太阳转、苹果为什么掉下来、潮汐为什么涨落,全归入了同一套公式。这是人类智力史上最伟大的单项成就。
+
+但牛顿的另一面同样疯狂。
+
+他与莱布尼茨争夺微积分优先权,利用皇家学会会长的职权组了一个调查委员会,自己起草调查报告,判定莱布尼茨抄袭——然后宣布"皇家学会经过独立调查认定牛顿是微积分的唯一发明者"。他自己判自己胜诉。
+
+晚年他沉迷炼金术和神学,试图用数学推算出上帝创世的日期。他留下的神学手稿比科学手稿还多,超过三百万字。他一生未婚。据说他在晚年曾说:"我不知道我在世人眼里是什么——在我自己眼里,我只不过是一个在海滩上玩耍的孩子,偶尔捡到一块更光滑的石子。"
+
+这就是宇宙立法者:他用数学征服了世界,却从未和自己和解。
+
+
+## 图灵:他拯救了世界,世界把他碾碎了
+
+### 人物档案
+- 姓名:艾伦·图灵(Alan Turing)
+- 年代:1912-1954,英国
+- 称号:数字世界的破译者
+- MathBTI类型:密码破译使
+
+### 小传
+
+24岁,图灵发表了一篇论文《论可计算数》。在这篇论文里,他想象了一台"永远不存在的机器"——一条无限长的纸带、一个可以读写符号的装置、一套简单的状态转换规则。他证明了:这台最简单的假想机器,能计算任何可以被计算的东西。他用这台"图灵机"定义了"计算"本身。冯·诺依曼后来说,现代计算机的"存储程序"概念,就来自图灵这篇论文。
+
+然后二战来了。
+
+德国人用了Enigma密码机——每天换一种加密方式,数量级大到手算永远破不了。图灵去了布莱切利庄园,和一群数学家、象棋冠军、填字游戏冠军一起设计了一台叫Bombe的解密机器。他们破译了Enigma。
+
+历史学家的共识是:这让二战提前两年结束,至少拯救了一千四百万人的生命。
+
+战后,图灵的贡献成为国家机密封存了三十年。公众不知道他做过什么。1952年,他因同性恋被定罪,被迫接受化学阉割。两年后,他咬下一口涂了氰化物的苹果,去世。41岁。
+
+2013年,英国女王颁布皇家赦免。2019年,他登上了新版50英镑纸币。
+
+他的数学,拦住了纳粹。他的国家,没拦住偏见。这两个事实叠加在一起,比任何单独的悲剧或英雄叙事,都更沉重。
+
+
+## 冯·诺依曼:他把世界当成一盘棋
+
+### 人物档案
+- 姓名:约翰·冯·诺依曼(John von Neumann)
+- 年代:1903-1957,匈牙利/美国
+- 称号:游走在数学、物理、计算、核武之间的全才
+- MathBTI类型:博弈游侠
+
+### 小传
+
+冯·诺依曼8岁掌握了微积分。6岁时他能心算八位数除法,还会跟他爸开玩笑:"你的账目少了一分钱。"
+
+22岁拿到博士学位。然后他做了什么事?他开始在完全不同的领域之间跳来跳去,每一个都留下一座里程碑。
+
+他创立了博弈论,用数学框架分析"人在对抗中如何决策"。这本《博弈论与经济行为》成为经济学、政治学、军事战略的圣经。冷战期间的核威慑理论——美苏双方都知道对方有核弹、都知道对方知道自己有核弹——这层层嵌套的"我知道你知道"就出自博弈论的矩阵。
+
+他在曼哈顿计划中设计了原子弹内爆的数学模型——不是造炸弹,而是用方程算爆炸。
+
+他又回到计算领域,写了一篇论文叫《关于EDVAC的报告》,定义了现代计算机的基本架构:控制器、运算器、存储器、输入输出——这个"冯·诺依曼架构"至今仍是一切电脑的基石。你现在读到的这篇文章,它底层的CPU就是这个架构的后代。
+
+有人问他:"你研究博弈论,是不是因为你喜欢下棋?"他回答:"不。我研究博弈论是为了理解核威慑。棋太简单了。"
+
+这就是博弈游侠:把世界当成一盘棋,而且他每一盘都赢了。
+
+
+---
+
+# 第四族:直觉者 — "看见别人看不见的结构与联系"
+
+---
+
+## 希帕提娅:她为理性而死
+
+### 人物档案
+- 姓名:希帕提娅(Hypatia)
+- 年代:约350-415,埃及亚历山大城
+- 称号:古典数学的最后一道光
+- MathBTI类型:几何殉道者
+
+### 小传
+
+希帕提娅是历史上第一位有记载的女数学家。她生活在公元4世纪的亚历山大城——彼时罗马帝国已经皈依了基督教,古典希腊的多神信仰正在被系统地清除。而亚历山大城依然是古代世界知识的最后堡垒:它的大图书馆里藏着希腊、埃及、巴比伦的所有智慧。
+
+希帕提娅继承了父亲泰昂的学术衣钵,在亚历山大的学院讲授柏拉图、亚里士多德、欧几里得。她是新柏拉图学派的领袖,学生从帝国各地慕名而来。她改良了星盘和比重计——星盘用来测量天体高度和纬度,比重计用来测液体密度——这两种仪器被后人沿用了1400多年。
+
+但她的位置太危险了:一个异教徒、一个女人、一个公开讲学的人、一个在宗教冲突中保持理性中立的人。415年,一群被主教煽动的暴民把她从马车上拖下来,拖进教堂,用陶片活活刮死,然后把她的尸体撕碎、焚烧。
+
+她死后,亚历山大图书馆被毁,古典希腊数学随之终结。希帕提娅的被杀被视为"古典时代的终结"标志之一——从那天起,黑暗时代开始了。
+
+她不是在做题,她是在为理性殉道。
+
+
+## 赵爽:他用一张图,让中国数学美了一千八百年
+
+### 人物档案
+- 姓名:赵爽
+- 年代:约公元3世纪,中国三国时期
+- 称号:数学的视觉诗人
+- MathBTI类型:弦图先觉
+
+### 小传
+
+赵爽只留下了一部作品:《周髀算经注》。他只注了一本书。
+
+但就是这本书里,他做了一件惊艳的事。
+
+勾股定理(毕达哥拉斯定理)在古代中国被称为"勾股弦定理"——但如何证明它?赵爽没有写长篇大论。他画了一张图:弦图。在一个正方形里,用四个直角三角形拼出一个中间的小正方形。然后他写了一行字:"按弦图,又可以勾股相乘为朱实二,倍之为朱实四,以勾股之差自相乘为中黄实,加差实亦成弦实。"
+
+翻译成现代话:看看这张图,你就看见了。
+
+他让数学变得"看一眼就懂"。这不是因为他的推导简单,恰恰相反——能用一张图让人"看见"真理,需要比写出十页证明更深的直觉。
+
+2002年,国际数学家大会的会徽选用了赵爽的弦图。1800年后,全世界数学家公认:这张图是最美的数学证明之一。
+
+这就是弦图先觉:不是算出来的,是看出来的。一张图,比十本书都长寿。
+
+
+## 庞加莱:他在公交车上"看见"了答案
+
+### 人物档案
+- 姓名:亨利·庞加莱(Henri Poincaré)
+- 年代:1854-1912,法国
+- 称号:最后一位数学全才
+- MathBTI类型:拓扑游方
+
+### 小传
+
+庞加莱是公认的"最后一位通晓当时所有数学分支的人"。在他之后,数学变得太过庞大,再也没有一个人能掌握全部。
+
+他的几何直觉惊世骇俗。有一次,庞加莱在准备一次关于富克斯函数的演讲时,花了几个月苦思冥想找不到突破口。然后某一天,他刚踏上一辆公交车的台阶——据他自己回忆——突然就"看见"了富克斯函数与双曲几何之间的联系。不是推算出来的,是他"看见"的。他在上车的一瞬间,整个结构在大脑中浮现了。
+
+这不是一个关于"天才"的故事——这是一个关于"用身体感受数学"的故事。庞加莱在回忆录中写道:"我停下来,继续和车上的人说话,但心里知道我已经抓住了答案。我甚至没有马上把它写下来——我知道它不会再跑掉了。"
+
+他提出的"庞加莱猜想":如果一个三维闭曲面上的每一条闭曲线都能缩成一个点,那么这个曲面就是一个三维球面。这个猜想困扰了数学界整整100年,直到2003年才被俄罗斯数学家佩雷尔曼证明。佩雷尔曼拒领了菲尔兹奖和一百万美元的千禧年大奖——用庞加莱式的姿态,退出了公众视野。
+
+这就是拓扑游方:他们在高维空间里漫步,而我们还在二维纸上画图。
+
+
+## 凯瑟琳·约翰逊:她说行,宇航员才敢飞
+
+### 人物档案
+- 姓名:凯瑟琳·约翰逊(Katherine Johnson)
+- 年代:1918-2020,美国
+- 称号:NASA的人脑计算机
+- MathBTI类型:人脑计算机
+
+### 小传
+
+凯瑟琳·约翰逊在NASA干了33年,用纸笔和计算尺手算弹道。
+
+1961年,美国第一次载人航天飞行。宇航员艾伦·谢泼德坐进火箭,但在发射前他说了一句话:"让那个女孩核对一下数字。"
+
+"那个女孩"就是凯瑟琳·约翰逊。她不是在用计算机运算——那个时代的计算机IBM刚被引入,宇航员不信任。她用的是纸、铅笔、计算尺,一步一个数字算出来的。
+
+她算对了。谢泼德飞了。第二次、第三次,还是她。
+
+1962年,格伦要上轨道了。弹道计算已经在IBM上跑过了,但格伦在发射前点名:"让她重新手算一遍——她说行,我就飞。"
+
+凯瑟琳的手算轨道确认了IBM的结果。格伦成了美国第一个绕地球飞行的宇航员。
+
+她和一群被称为"穿着裙子的计算机"的黑人女性数学家们,在种族隔离的年代,坐在NASA的"有色人种计算部"里,用人脑完成了阿波罗登月的轨道计算。她的101岁一生,恰好覆盖了计算从纸笔走向AI的整个世纪——她既是纸笔计算的终点,也是数字时代的见证者。
+
+这就是人脑计算机:她的直觉不是天生的,是30多年一笔一笔算出来的。算到最后,直觉比公式更快。
+
+
+---
+
+# 族群总览
+
+| 族群 | 特质 | 成员 |
+|------|------|------|
+| 信仰者 | 数学本身即目的 | 高斯、祖冲之、欧拉、诺特 |
+| 传播者 | 让数学走出象牙塔 | 斐波那契、华罗庚、伽罗瓦、Ada Lovelace |
+| 应用者 | 用数学改变世界 | 秦九韶、牛顿、图灵、冯·诺依曼 |
+| 直觉者 | 看见不可见的结构 | 希帕提娅、赵爽、庞加莱、凯瑟琳·约翰逊 |
+
+---
+
+*葫芦数学 · MathBTI · 12题发现你的数学灵魂*
diff --git a/docs/novel/数学家能力卡_六维雷达.md b/docs/novel/数学家能力卡_六维雷达.md
new file mode 100644
index 0000000..027f2f7
--- /dev/null
+++ b/docs/novel/数学家能力卡_六维雷达.md
@@ -0,0 +1,485 @@
+# 数学家人设卡 · 六维雷达系统
+
+## 六个维度定义
+
+| 维度 | 简称 | 内涵 |
+|------|------|------|
+| 严谨演绎 | **严谨** | 推理链的严密程度、证明的完美度 |
+| 直觉洞察 | **直觉** | 不靠推导"看见"结构的能力 |
+| 原始创造 | **创造** | 开创全新领域、定义全新概念的能力 |
+| 钻研耐力 | **钻研** | 一辈子死磕一件事不改初心的偏执度 |
+| 传播布道 | **传播** | 让数学走出象牙塔、培育学派的能力 |
+| 实战应用 | **应用** | 把数学落地到真实世界改变现实的能力 |
+
+**评分规则**:0–100 卡面分值。≥90 为极机型优势;70–89 为强项;50–69 为一般;<50 为弱项(往往伴随有故事张力)。
+
+---
+
+# 信仰者族 · 卡组
+
+> 共同特征:**钻研** 与 **严谨** 普遍高,**传播** 普遍低——他们懒得跟人解释。
+
+## 卡01 · 高斯(定理工工匠)
+
+```
+ 严谨
+ 98
+ 直觉 90 创造 95
+ 钻研 92 传播 40
+ 应用
+ 75
+```
+
+| 维度 | 数值 |
+|------|------|
+| 严谨演绎 | 98 |
+| 直觉洞察 | 90 |
+| 原始创造 | 95 |
+| 钻研耐力 | 92 |
+| 传播布道 | 40 |
+| 实战应用 | 75 |
+
+- **极机型**:严谨 98(完美主义压垮发表欲)
+- **盲区**:传播 40(怕惊扰蠢人,藏30年)
+- **必杀技**:完美证明 — 一出手就是教科书
+- **签语**:他不出手则已,一出手就是一篇完美的证明
+
+---
+
+## 卡02 · 祖冲之(穷竭守尺人)
+
+```
+ 严谨
+ 90
+ 直觉 65 创造 70
+ 钻研100 传播 55
+ 应用
+ 85
+```
+
+| 维度 | 数值 |
+|------|------|
+| 严谨演绎 | 90 |
+| 直觉洞察 | 65 |
+| 原始创造 | 70 |
+| 钻研耐力 | 100 |
+| 传播布道 | 55 |
+| 实战应用 | 85 |
+
+- **极机型**:钻研 100(一辈子蹲地上摆算筹)
+- **盲区**:直觉 65(靠死算而非顿悟)
+- **必杀技**:算筹穷竭 — 用最原始的工具逼近极限精度
+- **签语**:他不知道自己算对了没有,因为没人比他更准
+
+---
+
+## 卡03 · 欧拉(公式狂僧)
+
+```
+ 严谨
+ 88
+ 直觉 85 创造 98
+ 钻研100 传播 85
+ 应用
+ 80
+```
+
+| 维度 | 数值 |
+|------|------|
+| 严谨演绎 | 88 |
+| 直觉洞察 | 85 |
+| 原始创造 | 98 |
+| 钻研耐力 | 100 |
+| 传播布道 | 85 |
+| 实战应用 | 80 |
+
+- **极机型**:钻研 100 + 创造 98(失明后翻倍,顺手发明新学科)
+- **盲区**:无明显盲区(真正的六边形战士)
+- **必杀技**:永动口述 — 闭眼即在脑中推演
+- **签语**:停止计算的那一刻,才是停止生命的那一刻
+
+---
+
+## 卡04 · 诺特(抽象代数宗师)
+
+```
+ 严谨
+ 95
+ 直觉 92 创造 95
+ 钻研 95 传播 85
+ 应用
+ 70
+```
+
+| 维度 | 数值 |
+|------|------|
+| 严谨演绎 | 95 |
+| 直觉洞察 | 92 |
+| 原始创造 | 95 |
+| 钻研耐力 | 95 |
+| 传播布道 | 85 |
+| 实战应用 | 70 |
+
+- **极机型**:严谨 95(在最抽象处保持严密)
+- **盲区**:应用 70(物理学 mistakenly 落在她身后才被认出)
+- **必杀技**:结构之眼 — 能看见别人看不见的代数骨架
+- **签语**:被否定三次,数学还是站在她这边
+
+---
+
+# 传播者族 · 卡组
+
+> 共同特征:**传播** 与 **应用** 突出——学数学是为了让别人也懂。
+
+## 卡05 · 斐波那契(商路算郎)
+
+```
+ 严谨
+ 75
+ 直觉 70 创造 65
+ 钻研 70 传播 98
+ 应用
+ 85
+```
+
+| 维度 | 数值 |
+|------|------|
+| 严谨演绎 | 75 |
+| 直觉洞察 | 70 |
+| 原始创造 | 65 |
+| 钻研耐力 | 70 |
+| 传播布道 | 98 |
+| 实战应用 | 85 |
+
+- **极机型**:传播 98(一个人把阿拉伯数字带过地中海)
+- **盲区**:创造 65(搬运而非原创)
+- **必杀技**:跨海翻译 — 把异域工具带给自己的文明
+- **签语**:他学数学不是为了自己懂,是为了让别人也懂
+
+---
+
+## 卡06 · 华罗庚(双法布道者)
+
+```
+ 严谨
+ 85
+ 直觉 80 创造 85
+ 钻研 88 传播100
+ 应用
+ 95
+```
+
+| 维度 | 数值 |
+|------|------|
+| 严谨演绎 | 85 |
+| 直觉洞察 | 80 |
+| 原始创造 | 85 |
+| 钻研耐力 | 88 |
+| 传播布道 | 100 |
+| 实战应用 | 95 |
+
+- **极机型**:传播 100(走遍23个省、从大学讲到田间)
+- **盲区**:无明显盲区
+- **必杀技**:优选法落地 — 把抽象数学翻译成农民可用的工具
+- **签语**:把数学从大学讲台,讲到了田间地头
+
+---
+
+## 卡07 · 伽罗瓦(决斗狂热者)
+
+```
+ 严谨
+ 80
+ 直觉 98 创造100
+ 钻研 70 传播 80
+ 应用
+ 50
+```
+
+| 维度 | 数值 |
+|------|------|
+| 严谨演绎 | 80 |
+| 直觉洞察 | 98 |
+| 原始创造 | 100 |
+| 钻研耐力 | 70 |
+| 传播布道 | 80 |
+| 实战应用 | 50 |
+
+- **极机型**:创造 100(20岁独创群论)
+- **盲区**:应用 50(纯理论、无落地)
+- **必杀技**:决斗前夜的信 — 把真理寄给未来
+- **签语**:我没有时间了
+
+---
+
+## 卡08 · Ada Lovelace(织机先知)
+
+```
+ 严谨
+ 75
+ 直觉 98 创造 90
+ 钻研 60 传播 85
+ 应用
+ 75
+```
+
+| 维度 | 数值 |
+|------|------|
+| 严谨演绎 | 75 |
+| 直觉洞察 | 98 |
+| 原始创造 | 90 |
+| 钻研耐力 | 60 |
+| 传播布道 | 85 |
+| 实战应用 | 75 |
+
+- **极机型**:直觉 98(在不存在的机器上预见100年后)
+- **盲区**:钻研 60(36岁早逝,时间不够)
+- **必杀技**:注释三倍 — 把翻译变成原创
+- **签语**:分析机编织代数图案,就像提花织机编织花朵和叶子
+
+---
+
+# 应用者族 · 卡组
+
+> 共同特征:**应用** 与 **创造** 双高——数学是改变世界的武器。
+
+## 卡09 · 秦九韶(大衍谋士)
+
+```
+ 严谨
+ 78
+ 直觉 75 创造 85
+ 钻研 65 传播 50
+ 应用
+ 95
+```
+
+| 维度 | 数值 |
+|------|------|
+| 严谨演绎 | 78 |
+| 直觉洞察 | 75 |
+| 原始创造 | 85 |
+| 钻研耐力 | 65 |
+| 传播布道 | 50 |
+| 实战应用 | 95 |
+
+- **极机型**:应用 95(《数书九章》81道真实题)
+- **盲区**:传播 50(不讲给别人听,自己用)
+- **必杀技**:大衍求一 — 用中国剩余定理破解一切模数问题
+- **签语**:数学不是信仰,是武器
+
+---
+
+## 卡10 · 牛顿(宇宙立法者)
+
+```
+ 严谨
+ 85
+ 直觉 98 创造100
+ 钻研 90 传播 50
+ 应用
+ 95
+```
+
+| 维度 | 数值 |
+|------|------|
+| 严谨演绎 | 85 |
+| 直觉洞察 | 98 |
+| 原始创造 | 100 |
+| 钻研耐力 | 90 |
+| 传播布道 | 50 |
+| 实战应用 | 95 |
+
+- **极机型**:创造 100 + 直觉 98(18个月隔离完成三大发现)
+- **盲区**:传播 50(自己判自己胜诉)
+- **必杀技**:苹果坠落 — 把天地统一进一个方程
+- **签语**:他统一了天地,却统一不了自己
+
+---
+
+## 卡11 · 图灵(密码破译使)
+
+```
+ 严谨
+ 88
+ 直觉 95 创造 95
+ 钻研 85 传播 70
+ 应用
+ 98
+```
+
+| 维度 | 数值 |
+|------|------|
+| 严谨演绎 | 88 |
+| 直觉洞察 | 95 |
+| 原始创造 | 95 |
+| 钻研耐力 | 85 |
+| 传播布道 | 70 |
+| 实战应用 | 98 |
+
+- **极机型**:应用 98(救1400万人)
+- **盲区**:传播 70(贡献被封存30年)
+- **必杀技**:Bombe解密 — 用数学拦住纳粹
+- **签语**:他拯救了世界,世界把他碾碎了
+
+---
+
+## 卡12 · 冯·诺依曼(博弈游侠)
+
+```
+ 严谨
+ 85
+ 直觉 90 创造 95
+ 钻研 80 传播 85
+ 应用
+ 100
+```
+
+| 维度 | 数值 |
+|------|------|
+| 严谨演绎 | 85 |
+| 直觉洞察 | 90 |
+| 原始创造 | 95 |
+| 钻研耐力 | 80 |
+| 传播布道 | 85 |
+| 实战应用 | 100 |
+
+- **极机型**:应用 100(跨四个领域都改变世界)
+- **盲区**:无明显弱项(最均衡的全才)
+- **必杀技**:核威慑矩阵 — 用博弈论改写冷战
+- **签语**:棋太简单了
+
+---
+
+# 直觉者族 · 卡组
+
+> 共同特征:**直觉** 普遍 90+ ,**严谨**偏低——他们靠"看",不靠"推"。
+
+## 卡13 · 希帕提娅(几何殉道者)
+
+```
+ 严谨
+ 85
+ 直觉 88 创造 60
+ 钻研 90 传播 88
+ 应用
+ 80
+```
+
+| 维度 | 数值 |
+|------|------|
+| 严谨演绎 | 85 |
+| 直觉洞察 | 88 |
+| 原始创造 | 60 |
+| 钻研耐力 | 90 |
+| 传播布道 | 88 |
+| 实战应用 | 80 |
+
+- **极机型**:钻研 90 + 传播 88(在黑暗时代坚持讲学)
+- **盲区**:创造 60(改良而非首创)
+- **必杀技**:理性之光 — 在暴民面前继续讲几何
+- **签语**:她不是在做题,她是在为理性殉道
+
+---
+
+## 卡14 · 赵爽(弦图先觉)
+
+```
+ 严谨
+ 70
+ 直觉100 创造 75
+ 钻研 65 传播 75
+ 应用
+ 60
+```
+
+| 维度 | 数值 |
+|------|------|
+| 严谨演绎 | 70 |
+| 直觉洞察 | 100 |
+| 原始创造 | 75 |
+| 钻研耐力 | 65 |
+| 传播布道 | 75 |
+| 实战应用 | 60 |
+
+- **极机型**:直觉 100(一张图秒杀勾股定理)
+- **盲区**:钻研 65(只留一部作品)
+- **必杀技**:弦图无字 — 不用文字的证明
+- **签语**:一张图,比十本书都长寿
+
+---
+
+## 卡15 · 庞加莱(拓扑游方)
+
+```
+ 严谨
+ 75
+ 直觉100 创造 95
+ 钻研 80 传播 80
+ 应用
+ 65
+```
+
+| 维度 | 数值 |
+|------|------|
+| 严谨演绎 | 75 |
+| 直觉洞察 | 100 |
+| 原始创造 | 95 |
+| 钻研耐力 | 80 |
+| 传播布道 | 80 |
+| 实战应用 | 65 |
+
+- **极机型**:直觉 100(公交车上"看见"答案)
+- **盲区**:应用 65(纯理论)
+- **必杀技**:高维漫步 — 在不可见的空间里找路径
+- **签语**:他在高维空间里漫步,而我们还在二维纸上画图
+
+---
+
+## 卡16 · 凯瑟琳·约翰逊(人脑计算机)
+
+```
+ 严谨
+ 85
+ 直觉 95 创造 60
+ 钻研100 传播 75
+ 应用
+ 100
+```
+
+| 维度 | 数值 |
+|------|------|
+| 严谨演绎 | 85 |
+| 直觉洞察 | 95 |
+| 原始创造 | 60 |
+| 钻研耐力 | 100 |
+| 传播布道 | 75 |
+| 实战应用 | 100 |
+
+- **极机型**:钻研 100 + 应用 100(33年手算零失误,算到宇航员敢飞)
+- **盲区**:创造 60(应用为主非原创理论)
+- **必杀技**:人脑校准 — 让宇航员说"她说行,我就飞"
+- **签语**:她的直觉是30年一笔一笔算出来的
+
+---
+
+# 族群雷达图谱对比
+
+| 族群 | 优势维度 | 弱势维度 | 代际特征 |
+|------|---------|---------|---------|
+| 信仰者 | 严谨、钻研、创造 | 传播 | 把数学本身当目的 |
+| 传播者 | 传播、应用 | 严谨 | 把数学翻译给普通人 |
+| 应用者 | 应用、创造、直觉 | 传播 | 用数学改变世界 |
+| 直觉者 | 直觉、钻研 | 严谨、应用 | 不靠推算靠"看见" |
+
+---
+
+# 使用建议
+
+1. **公众号呈现**:每位数学家配一张六边形雷达图卡面,下方附一句"必杀技"和"签语"
+2. **MathBTI知识库**:用户测出自己的MathBTI类型后,对应一张卡,卡片即人格画像
+3. **剧本游戏**:每位数学家作为一个NPC角色,卡面数据可代入游戏系统(攻击=创造,防御=严谨,敏捷=直觉,生命=钻研,魔力=传播,计谋=应用)
+
+---
+
+*葫芦数学 · MathBTI · 16张数学家人设卡*
diff --git a/docs/novel/第01章.txt b/docs/novel/第01章.txt
new file mode 100644
index 0000000..7273825
--- /dev/null
+++ b/docs/novel/第01章.txt
@@ -0,0 +1,64 @@
+葫芦侠行(一)
+江湖有言:“紫金巍巍,铸剑为犁。以理服人,以功相会。大学之道,深蕴其中。”
+公元1022年,九月,暑气未消。葫芦侠站在秉公习武堂前,心情复杂。他知道,他此番来到这个习武堂,是承载着全族人的期盼。他要在这里,学到真正的本领,把武林的最终奥义带回故里。
+秉公习武堂坐落在紫金山下,是方圆几百里最火爆的习武堂。内行的人都知道,每年在紫金山,都会开一场武林大会,天下各路英雄豪杰都会聚集于此。而由于路程奔波,再加上比武的日期总是在各方协调下才商榷下来的,极其不定,因此很多英雄索性就在山下找个住所,呆上个三五月调整状态等待比武大会的到来。秉公习武堂也应运而生。
+英雄豪杰只需交上一定的盘缠,通过习武堂的选拔,便会在习武堂有一个落脚之地,并且在习武堂学习交流。通常,因为习武堂根据武林大会有了一套自己的培养模式。大多数的侠客都会选择和习武堂签约四年,然后四年的时间里,每年都去参加紫金山的武林大会,和其他门派的人时不时来一场切磋,又或者称之为热身赛。可以说,这个习武堂就像是一个笑江湖,每天都在发生着小型的武林大会。
+欲练神功,削发明志。秉公习武堂有个规定,入了此玄武门,就要在第一个月剪短自己的头发,以明晰自己真正下决心学习武功的志向。葫芦侠摸了摸自己的大脑袋,尽管有些不太理解,但还是请了修发师傅,剃了个光头。周围一个个光秃秃的脑袋,仿佛在昭示着他们想成为武林冠军的决心。而女侠们也都不甘示弱。尽管习武堂不要求女侠们削发,但是她们要成为武林冠军的期盼也是相当强烈的。在这里,凭的不是蛮力,而是硬实力。天下英雄如过江之鲫,每个门派都有一些不知天高地厚的人想站在紫金山之巅,但是真正的冠军,永远只有一个。
+在秉公习武堂的第一个月,是基本功的培训。因为大家来自各个门派,所以需要明确一下这里的规章制度以及提高习武的耐力,方便后续的统一调配。但是葫芦侠却陷入了深深的焦虑之中:他发现,其他门派派来的后生,仿佛都是自来熟,每天出门培训都是三五成群的。培训结束后,就去酒馆饭店,喝上二两小酒,再去唱唱歌打打牌消遣消遣。葫芦侠很想和他们成为朋友,但是他性格内向,又不敢唱歌。他下决心要改变自己。
+第一个月的培训很快就结束了,根据最开始的选拔,葫芦侠被分到了理殿的数理门。秉公习武堂主要一共有三大殿:工殿,理殿,文殿。每个殿里又有诸多门。这些门代表着不同的出招风格。想成为一名合格的武侠,最关键的是要有一门之长,再辅以对其他门也有一定的认知。他看了看每日贴在集市上的告示栏,栏上会写着每个门要修炼的独门武功课程。葫芦侠所上的数理门,在第一季主要就学两门:输血分析和高等逮术。这两门课分别对应着数理门的最核心的技艺:一个是在比武中不断给自己快速的疗愈回血,一个是精准的擒拿技艺,去抓捕在战斗中以跑路见长的侠客。这是数理门的筑心基法。
+在输血分析的课上,葫芦侠认识了两位侠客。一君人称Eddie侠,另一君人称欧阳侠。此二人上课很积极,总要在输血分析抢到听课的第一排。久而久之,不只是上课的师傅认识他们,连葫芦侠也对他们好奇了起来。他尝试着在某一天,天方蒙蒙亮就赶紧摸着黑来到教堂,发现欧阳侠已经在这翻着一本武林课本,在那自顾自的操练起来。旁边的Eddie侠仿佛没睡醒一般揉着眼睛,欧阳侠就结结实实地给了他一巴掌:”习武要趁早,不要懈怠!”葫芦侠试探性地上前去打了招呼:“二位,久仰大名。我看二位每天这么早就来自习,非常敬佩。我想问问二位兄台,你们认为数理门应该怎么学习呢?江湖人都说此门最枯燥,但一旦功成,便可立于不败之地。”
+欧阳侠抢先答道:“小兄弟,你这问题问得好。我觉得数理门是所有门的基础。我师傅其实是想让我去学习计算机门的,但是我觉得这俩门是有相通之处。学好了数理门,会打通我学习计算机门的任督二脉。咱们两门课的先生上得都很不错,但是我托关系,要到了一些武功课本。深感学习时间不够,因此每日鸡未鸣便起。学此门课,唯有扎实才行。”Eddie侠接着他的话茬说:“我就喜欢数理门。我感觉天下武功太过于杂乱,只有数理门才能让我沉淀下来安心。但是最近习武堂里趣事实在太多,要不是欧阳兄拉着我来学习,我实在会被这花花世界迷了双眼。” 三人从最初略显拘谨的寒暄,渐渐转入了关于“数理门”的热烈切磋。欧阳侠将随身带着的一卷手抄本摊开在石桌上,指尖点着其中一行复杂的公式,沉声道:“Eddie兄,你看这一式‘微积分心法’,表面是算那曲线变化,实则与计算机门里的算法流转异曲同工。我近日苦思,若能将这数理的根扎稳,将来修习计算机的枝叶,必能触类旁通。”
+Eddie侠闻言,眼神瞬间亮了起来,他凑近细看,抚掌笑道:“欧阳兄此言深得我心!我近日正为辅修的计算机门里的算法奥义而头疼,总觉得千头万绪如乱麻。听你一说,才知这数理门正是那定海神针,能把散乱的招式凝成一股劲。我原以为数理只是枯燥演算,如今看来,它竟是天下武功的源代码!”
+两人越谈越投机,从数理门的“公理基石”聊到计算机门的“架构乾坤”,言语间既有欧阳侠的笃实稳重,又有Eddie侠的机敏跳脱。一旁的葫芦侠起初只是静听,见他们谈及习武堂近来那些喧闹趣事对数理修行的干扰时,忍不住插话:“二位兄长所言,正是葫芦近日之惑。那花花世界般的杂学虽有趣,却总让人心难沉静。今日听君一席话,方知数理门竟是这般澄澈的清流。”
+欧阳侠拍了拍葫芦侠的肩,眼中露出赞许:“小兄弟能在这纷扰中守住本心,难得。”Eddie侠也笑着递过一颗青枣:“来,吃了这颗‘定心丸’,咱们以后一道早起修习!”一来二去间,三人因对数理门的共同向往与坦诚交流,早已没了初见的生疏,仿佛相识多年的旧友,在习武堂的暮色里,结下了真挚的武学情谊。
+天色逐渐锃亮。今天的高等逮术课,先生来得有些迟。他是高等逮术这一功法的创始人和集大成者,讲课风格非常和蔼,习武堂的人都很喜欢他。他今天分享起了他是怎么创立高等逮术的:七八年前,一种叫手机的迷魂药席卷各大习武堂。许多人都沉迷于这种迷魂药。这种迷魂药有利有弊,善于利用的人可以产生正向循环,提高习武的效率。但是反之,很多人沉浸于这种迷魂药,便再也没有心思去学习真正的基本功。他当时对手机这种迷魂药嗤之以鼻,所以走路时其实并不使用,只是东张西望。而巡捕就发现了他,质问他:大家都低着头用迷魂药,怎么就你在东张西望?你是不是想偷东西?从那之后他便明白了,他只有买迷魂药,才能显得合群,避免被巡捕捉去。但是他也只是买,假装低头,却很少去用。而一种高等的逮术就在他脑子里逐渐有了基本框架:如果能不断训练眼球的捕捉能力,提高注意力,反复地四处观察,最终就有机会能识别出一些幻影和隐身之术,抓住那些以快见长的侠客。班里的众生都啧啧称赞。其实学习武功不难,能有自己自创的武功,这才是最难的。
+除了基本的筑基心法,习武堂也鼓励大家充分利用习武堂里的资源,去开拓自己的视野。当时正值习武堂的封闭期,所以很少有离开习武堂的机会。葫芦侠只能在有限的范围里去探索。但是他来了两个月,其实对这个地方还是不太熟悉。他想起了他的入堂介绍人三力思侠。三力思侠对这个武堂的门道摸得一清二楚,去跟他多交流交流,可能会有新的发现。
+这天葫芦侠来到三力思侠的房子拜访,他今天却神神秘秘地坏笑,从兜里摸索出两张话剧的票:“走,带你溜出习武堂去看表演!我好兄弟厚朴侠是今晚的大角色!”彼时葫芦侠只看过故里一些草台班子演过的话剧,对这个并不太了解。
+现场排场挺大,听说话剧是秉公习武堂里一个比较大的娱乐方式,很多人都会在习武之余,趁着业余时间去练习。台上骤地暗下,又忽然亮起。今天的话剧,讲的是秉公习武堂创立之初的故事。初代习武堂的人,因为办学场地的问题,发展遇到了瓶颈。当时秉公习武堂并不在紫金山旁边,紫金山旁边的是另一个习武堂。但是这个习武堂展现了武林大义,提出了要跟秉公习武堂换校区的想法。这在武林界里成了一段佳话。而厚朴侠既是这部话剧的主要排练人,又是男主角,自然受到大家的关注。看着他帅气的脸庞,精湛的演技,葫芦侠第一次对话剧,也对秉公习武堂的优良传统有了深刻的认识。到了台下,他立刻托三力思侠要到了厚朴侠的地址,打算有机会就去当面拜访他。
+而这天也很快来到。不久后,居然是厚朴侠主动用信鸽联系他,让他来家里做客。葫芦侠才知道,原来三力思侠已经把他也介绍给了厚朴侠。这个三力思侠,当时就是他力排众议,推荐葫芦侠来到秉公习武堂的。葫芦侠印象很深,因为三力思侠提起秉公习武堂就啧啧称赞,说能来到这里十分荣幸。这也逐渐吸引了葫芦侠,最终选择和秉公习武堂双向奔赴。
+这天葫芦侠正准备穿过梧桐大道回明苑,忽然听见头顶传来一阵独特的“咕咕”声。他下意识抬头,只见一只羽翼丰满、眼神机警的灰鸽,正稳稳地滑翔而下,精准地落在他前方不远处的校风碑基座上。
+葫芦侠觉得稀奇,多看了两眼。那灰鸽竟不怕人,扭着头,一双赤豆般的眼睛直勾勾盯着他,右脚上系着一个小小的、深青色的竹管。
+他心里一动,隐约觉得这鸽子有些不同寻常。周围同学行色匆匆,无人留意这小小的插曲。他迟疑着走近,灰鸽只是轻轻跳开一步,并无飞走的意思。葫芦侠试探着伸出手,那鸽子竟颇为驯顺地让他解下了竹管。
+竹管触手温润,显然被人摩挲过许久。拧开一头,倒出一卷极薄的、裁切整齐的宣纸。展开,一股淡淡的墨香与药香混合的气息飘散出来,字迹筋骨挺拔,力透纸背:
+“葫芦侠兄,明日申时三刻,可愿移步‘明理居’东隅小筑一叙?厚朴。”
+没有落款日期,没有多余寒暄,风格一如传闻中那位“厚朴侠”本人——严谨、利落,却自有一股敦厚温润的力量透过纸背传来。更让葫芦侠心跳加速的是,对方竟主动联系他。
+“厚朴侠……竟是主动相邀。”葫芦侠心中既感荣幸,又有些忐忑。他立刻想到,这中间必定有“三力思侠”的引荐。那位总是笑眯眯、说起话来却充满力量的侠长,是他与秉公习武堂结缘的关键。
+记忆被这封特别的“飞鸽传书”牵动,倏地拉回到一年前那个闷热的下午。那时,他还只是一个在几所“习武堂”间犹豫不决的高中少年,对未来充满了憧憬与迷茫。
+而三力思侠来到他们那所小城做武道宣讲。不同于其他宣讲者大谈特谈习武堂的成绩,三力思侠站在讲台上,第一句话就带着一种独特的诚恳:
+“诸位少侠,我知道你们听过很多关于各家习武堂的传说。今天,我不说虚的。”他扶了扶眼镜,目光扫过台下,“我就说说我们‘秉公习武堂’的‘笨功夫’。”
+“我们那里,没有什么一步登天的捷径。有的是清晨‘时间广场’日晷下的晨读,是‘工程训练中心’日复一日的习武。”
+他语气平实,却充满力量:“我们信奉‘止戈为武’。最强的武功,不是用来炫耀,而是为了守护。你在这里打下的每一分根基,学到的每一点本事,未来都可能化为让生活更好、让家园安全的一份力量。这里不生产江湖神话,这里锻造的,是有报国精神的武林志士。”
+宣讲结束后,他鼓起勇气上前询问。三力思侠没有丝毫不耐烦,仔细看了他颇为偏科的成绩,沉吟片刻,说:“你的‘内力’扎实,但‘招式’略显散乱。我们秉公堂,或许正适合帮你把‘内力’系统化、夯实,至于‘招式’,来了再磨炼不迟。” ……
+记忆被信鸽拍打的翅膀打断。信鸽已归,余音袅袅。明日申时三刻,“百草园”东隅小筑。他知道,那将是他江湖路上,又一个至关重要的驿站。
+信鸽指引的“明理居”,并非真种百草,乃是校内一片毗邻“冶园”、静谧少人的老宿舍区,几栋白墙小楼掩映在浓密的梧桐树下。厚朴侠的家就在其中一栋的一楼,带一个小小庭院。
+葫芦侠如约而至,轻叩门扉。开门的正是厚朴侠本人,一身半旧的黄色衣衫,笑容温和,将他引入屋内。
+室内的光景,让葫芦侠瞬间感到一种奇特的宁静。与外头喧嚣的校园仿佛是两重天地。客厅不大,却处处透着主人的雅趣。墙面是暖白色,挂着几幅字画,一面顶天立地的书架,密密麻麻垒满了书籍,线装古籍与新版剧本并肩而立,间或点缀着几盆绿意盎然的文竹与吊兰。书案临窗,文房四宝井然,一方歙砚,墨色犹新。空气中浮动着淡淡的书香,以及一种清苦的药草气息——源自角落里一个古朴的多宝格,上面摆放着许多青花瓷罐,罐身上贴着“远志”、“茯神”、“当归”等标签。
+“寒舍简陋,随意坐。”厚朴侠引他到窗边的藤椅坐下,那里已有一位客人。
+“这位是山韵侠,在艺文馆做事,精于音律与文章,是我的老朋友了。”厚朴侠介绍道。
+葫芦侠忙行礼。山韵侠微笑颔首,将一盏清茶推至他面前,茶汤澄澈,香气清远。
+寒暄未已,厚朴侠忽指着书房一侧:“那是我的丹房,平时胡乱写写画画,堆得杂乱,让小友见笑了。”葫芦侠循指望去,见靠墙有一架看似普通书柜,厚朴侠在侧面某处轻轻一按,那书柜竟无声地滑开,露出一道窄窄的楼梯,通往上方的小阁楼。楼口泄出温暖的光,隐约可见沿墙堆放的大量卷轴、稿纸,墙上似乎也钉满了各式话剧稿件。
+“这……”葫芦侠惊叹。
+“一点小机巧,图个清静。”厚朴侠笑道,并未多言阁楼内详情,但那惊鸿一瞥,已足够让人浮想联翩。
+三人越聊越是投机。葫芦侠还把自己写的稿件给山韵侠和厚朴侠看,他们都称赞不已,还提出了许多表演的思路。在话剧方面,葫芦侠只觉得平日许多模糊的想法,被两位前辈三言两语便点得透亮。窗外日影西斜,茶汤续了又淡。山韵侠忽然提议:“厚朴精于导演,我略通表演,小友你既有这番奇思妙想,又有践行之心。我们何不合力,做点有意思的东西?”
+厚朴侠眼中一亮:“哦?说来听听。”
+“一起创作一部话剧,将我们习武心得放入其中?演给大家看?”
+厚朴侠沉吟片刻,缓缓点头:“此事大善。小友,你可愿意?”
+葫芦侠霍然起身,郑重一揖:“固所愿也,不敢请耳!多谢厚朴老师、山韵老师提点!”
+“好,好。”厚朴侠将他扶起,三人相视而笑。
+就在葫芦侠满心期待即将到来的武林大会时,集市里突然贴出了告示:近期周边突发瘟疫,这种瘟疫虽然大多情况下不致命,但是感染上会让武力大幅下降,这一年的武林大会可能要推迟。也劝习武堂的各位侠客选择返乡躲避瘟疫或者呆在住所里不要出门,减少感染风险。葫芦侠一下面临着两难的境地。
+那告示就贴在布告栏最显眼处,盖着“习武堂总务处”的鲜红大印。措辞虽力求平稳,但“瘟疫”、“武力下降”、“大会推迟”、“返乡避疫”等字眼,依然触目惊心。人群围了一圈,窃窃私语声汇成一片压抑的嗡鸣。
+“听说隔壁已有数名弟子中招,内力运转滞涩,一套基础拳法都打不全……”
+“可不是,这‘软筋散’似的疫症,不伤性命,专蚀修为,真是歹毒!”
+“这武林大会怎么说不办就不办了,唉,白准备许久……”
+葫芦侠挤在人群中,指尖拂过粗糙的告示纸张,冰凉。他刚刚才觉得脚下的路清晰了些,学习到了一些真功夫,还想着切磋切磋呢。这一切,都要被这突如其来的灾厄打断吗?
+两难的境地,此刻无比真切地压在心头。
+留下? 告示说得明白,疫疾当前,集中修习风险大增。纵使堂内会采取措施,但人员往来难以完全隔绝。万一染上,数月苦修可能付诸东流,“武力下降”在武林大会推迟的背景下或许暂不致命,但对急需夯实基础的自己而言,无疑是沉重打击。况且,留在此处,若疫情蔓延,堂内资源紧张,气氛惶惶,也未必能安心向学。
+返乡? 那便意味着离开这刚刚熟悉的江湖,离开可以随时请教切磋的同道,离开藏书阁的海量秘籍,离开明师益友的点拨。家乡小城固然安全,但修炼资源匮乏,信息闭塞,更无同道交流。
+接连几日,堂内气氛明显不同。往日清晨练功场上的呼喝声稀落了许多,时间广场上匆匆而行的人们大多面覆白巾,眼神警惕。熟悉的同门,有的已收拾行囊,神色匆匆地告别;有的则如他一般,满脸纠结,在去留间反复权衡。欧阳侠来找过他一次,面色凝重:“家中有信来催,父母担忧。我……恐怕不日便要动身了。葫芦兄,你作何打算?” Eddie侠倒是洒脱些,苦笑道:“我是要回家去的,倒是不远。只是这一回去,不知何时才能再与二位兄台这般论道了。”
+葫芦侠去了一趟明理居,厚朴侠的小院门扉紧闭,门口贴了张字条,笔力依旧敦厚:“暂闭门谢客,潜心配药,以期有用。诸弟子当以安危为重,妥善自处。厚朴字。”
+最后一丝犹豫的余地,似乎也被这张字条抚平了。前辈们都在以自己的方式应对这场风波,或坚守,或努力。而作为新入门的弟子,在堂方已给出明确建议、家中父母书信催促愈急的情况下,或许暂时的退避,保全有用之身,才是更负责任的选择。
+做出决定的那晚,他独自在紫霞湖旁边走了很久。湖水映着稀疏的星子和岸边沉默的楼影。他很不舍得这里的生活,但正是这份“不舍”,让他明白此地值得珍惜,值得在更安全、更合适的时刻回来。
+离开那日,天色有些阴沉。二道门前不复往日喧嚣,只有寥寥数人默默出行。
+他的第一个季度的习武之旅,就这样,被一场突如其来的瘟疫画上了一个仓促的、未完成的逗号。马车载着他驶离紫金山麓,熟悉的景色向后飞掠。他怀中抱着行囊,仿佛抱着这段短暂却充实的时光。
+武林大会推迟了。但他知道,自己的修行并未结束,只是换了一个场地。故乡的斗室,将成为他临时的“闭关”之地。而这个江湖,那群人,那些事,那盏灯,他一定会在春暖花开的日子,重新回来。
+第一年的武林大会,也许要到下一年,才能继续办了。 但属于葫芦侠的江湖,才刚刚拉开序幕,无论身处何方。他闭上眼,开始在心中默默推演起下一个章节。路途还长。第一个季度就像是一个醒目的冒号,昭示着那众豪杰再次坐而论道的日子。
\ No newline at end of file
diff --git a/docs/novel/第01章_备用.txt b/docs/novel/第01章_备用.txt
new file mode 100644
index 0000000..9c7ed0c
--- /dev/null
+++ b/docs/novel/第01章_备用.txt
@@ -0,0 +1,75 @@
+葫芦侠行(一)
+江湖有言:“紫金巍巍,铸剑为犁。以理服人,以功相会。大学之道,深蕴其中。”
+公元1022年,大宋乾兴元年,九月,暑气未消。当朝新皇初立,太后临朝,四海承平日久。朝廷大兴文教,鼓励民间进德修业。这紫金山麓的秉公习武馆,便成了天下有志之士心向往之的圣地。葫芦侠站在高大的门楼前,摸了摸别在腰间的大葫芦,心中五味杂陈。
+此番远行,他背负着全族人的殷切期盼。他的故乡泉州,乃是当今东方著名大港,海贸繁盛,但风浪难测,沿海更时有流寇水患。他要在这天下闻名的学武之地,求得真本领——要将那能推演风暴、破译番邦商局、护卫海疆的数理奥义,带回东南海滨的故乡。
+秉公习武馆坐落在紫金山南麓,是方圆数百里内最负盛名的武学修习之地。江湖中人都知晓,每年在紫金之巅,都会召开一场汇聚天下豪杰的武林大会。只因路途遥远,加之会期常需多方协调、难以早定,许多英雄索性提前数月便在山下觅得住处,静心调整,以待盛会。秉公习武馆便应运而生。
+四方侠士只需备足资费,通过武堂的考核,便可得一安身之所,更能在堂内进修、与同辈切磋。因武堂授业期较长,多数侠客会选择签下四年之约,如此便可连年赴会。平日里,各门各派的年轻人在此较量印证,小试身手,堪称日日都有“小武林会”在此上演。
+欲学神功,先明其志。秉公习武馆有一规矩:新入堂者,首月须剪短头发,以此昭示潜心向武的决心。葫芦侠摸了摸自己浓密的头发,虽不甚解其深意,还是寻了剃头师傅,痛快地削去了三千烦恼丝。放眼望去,一个个光亮的脑袋,仿佛都在无声宣告着问鼎武林的雄心。女侠们亦不遑多让,堂规虽未要求她们削发,但那份争胜之心同样炽烈。在这里,凭的不是家世背景或蛮力,而是实打实的修为。天下英才多如过江之鲫,每个门派都不乏梦想站上紫金之巅的年轻面孔,然而真正的魁首,永远只有一人。
+在秉公习武馆的第一个月,是夯实根基的集训。因弟子来自天南地北、流派各异,需先令众人熟悉此间规矩,并锤炼筋骨耐力,以便后续教学。葫芦侠却渐渐感到一丝焦虑:他发觉,其他门派来的子弟大多性情开朗,很快便三五一伙,同出同入。操练结束后,常相约酒肆饭庄,小酌谈笑,或去听曲博弈,以为消遣。葫芦侠内心渴望结交朋友,但他生性腼腆,又不善那些娱乐,只好暗自决心要有所改变。
+一月光阴转瞬即逝。根据入门考核的结果,葫芦侠被分入了“理殿”之下的“数理门”。秉公习武馆主体分作三大殿:工殿、理殿、文殿。每殿之下又辖诸多门,代表着不同的武学路径与风格。欲成一方高手,贵在精擅一门,而后博采众长。葫芦侠每日都会去看集市口的告示栏,上面列着各门即将传授的独门课程。他所属的数理门,在第一学期主修两门核心功法:“输血分析”与“高等逮术”。此二者乃是数理门的筑基心法。前者旨在比武中迅捷疗伤、回复元气;后者则讲究精准擒拿,专克那些以身法迅捷、游走缠斗见长的对手。
+在“输血分析”的课堂上,葫芦侠注意到了两位同窗。一位人称Eddie侠,另一位唤作欧阳侠。此二人听课极为专注,每每抢占头排座位。时日一久,莫说授课师傅,连葫芦侠也对他们产生了好奇。一日,天光未亮,葫芦侠便摸黑赶到讲堂,却见欧阳侠已在窗下就着晨光翻阅一本武学典籍,自顾自地比划起来。旁边的Eddie侠睡眼惺忪,不住揉眼,欧阳侠见状,抬手便不轻不重拍了他一下:“练功当趁早,岂可懈怠!”
+葫芦侠鼓足勇气上前招呼:“二位师兄,久仰了。见二位日日如此早来用功,小弟甚为佩服。敢问师兄,修习数理门,可有诀要?江湖皆传此门最是枯燥艰深,然一旦悟透,便可几近立于不败之地。”
+欧阳侠抢先答道:“这位师弟问得好。在我看来,数理乃是诸武学之基石。家师本欲让我去那‘计算机门’,但我深感两门功夫源流相通。若能打通数理门的关隘,日后研习计算机门的神功亦能事半功倍。授艺的两位先生固然高明,但我私下还设法寻来一些前辈笔记参阅,愈觉时日紧迫,故而闻鸡即起。修习此道,无他,唯扎实二字耳。”
+Eddie侠揉着肩膀接话:“我却真心喜爱数理门。总觉得天下武功看似纷繁,唯数理门之严谨方能令我心神沉淀。只是近来堂内新奇事物太多,若非欧阳兄每日揪我前来,我怕真要迷失在这花花世界里了。” 葫芦侠听得心中暖流涌动,欧阳侠的沉稳坚毅与Eddie侠的跳脱灵趣,恰似一剑一盾,相得益彰,让他看到了武道修行路上别样的生动光景。一来二去,三人便成了好友。葫芦侠暗自欣喜,原来这严肃的武堂之内,也有这般志趣相投的同道。
+今日的高等逮术课,先生来得稍迟。他是这门独特功法的开创者与集大成者,为人温和,在堂内颇受爱戴。他今日并未直接授课,而是讲起了创功的缘由:约莫七八年前,一种名为“手机”的奇巧之物风靡各大武堂,令许多弟子沉迷其中,犹如中了迷魂散。此物利弊参半,善用者可助益修行,提高效率;然多数人沉溺其幻乐之中,再无心思打磨根基。他当时对此物颇为不屑,行走时从不低头观看,只是惯常观察四周。不料巡街捕快竟因此注意到他,厉声诘问:“人人皆低头摆弄那物,为何独你东张西望?莫非是想行窃?” 自那之后他方才明白,唯有也购置一具,时常佯装低头观看,方能合群免遭盘查。但他多是虚应故事,极少真正沉迷。也正因如此,一门“高等逮术”的雏形在他脑中渐成:若能刻意训练眼力与专注,于纷乱万象中反复观察、辨析,最终便可识破诸多虚招幻影,擒住那些以速度见长的对手。堂下弟子闻言,皆啧啧称奇。原来习得武功不难,能于世间万象中悟出独门武学,才是真本事。
+除却必修的筑基心法,习武馆也鼓励弟子充分利用堂内资源,开阔眼界。其时正值堂内规训期,弟子不得随意外出。葫芦侠虽至两月,对周遭仍觉陌生。他想起了自己的引荐人“三力思侠”。此君对武堂内外门道了如指掌,寻他聊聊,或许能有意外收获。
+这日,葫芦侠登门拜访三力思侠。对方却神秘一笑,从怀中摸出两张票券:“走,带你溜出去看场好戏!我好友厚朴侠今晚是主角!” 葫芦侠以往只在故乡看过草台班子的表演,对此了解不多。
+演出排场颇大。原来,观戏听曲是秉公习武馆弟子们主要的消遣之一,不少人在习武之余,会投身于此道。台上灯光骤暗复明,戏已开场。今日这出戏,演的正是秉公习武馆创立之初的旧事。当年初代堂主因场地所限,发展维艰。彼时紫金山下的宝地,乃为另一家武堂所有。那家武堂却深明大义,竟主动提出与秉公习武馆互换场地,成就武林一段佳话。厚朴侠既是这出戏的排演主力,又担纲主演,自然备受瞩目。见其扮相英挺,演技精湛,葫芦侠首次对话曲之道,乃至秉公习武馆的厚重传承,有了真切感触。戏散之后,他立刻拜托三力思侠,问得了厚朴侠的住处,心想着改日定要登门拜会。
+这一日来得比预想更快。不久,竟是厚朴侠主动遣信鸽相邀,请葫芦侠到家中一叙。葫芦侠这才知晓,原来三力思侠早已将自己介绍给了对方。这位三力思侠,正是当年力排众议,举荐葫芦侠进入秉公习武馆的关键之人。葫芦侠对此铭记深刻,因为三力思侠每提及此堂,总是赞不绝口,言道能入此门实乃大幸。正是这份热忱,深深吸引了葫芦侠,最终使他与秉公习武馆,成就了这番“双向奔赴”的缘分。
+这天葫芦侠正准备穿过梧桐大道回名苑,忽然听见头顶传来一阵独特的“咕咕”声。他下意识抬头,只见一只羽翼丰满、眼神机警的灰鸽,正稳稳地滑翔而下,精准地落在他前方不远处的校风碑基座上。
+葫芦侠觉得稀奇,多看了两眼。那灰鸽竟不怕人,扭着头,一双赤豆般的眼睛直勾勾盯着他,右脚上系着一个小小的、深青色的竹管。
+他心里一动,隐约觉得这鸽子有些不同寻常。周围同学行色匆匆,无人留意这小小的插曲。他迟疑着走近,灰鸽只是轻轻跳开一步,并无飞走的意思。葫芦侠试探着伸出手,那鸽子竟颇为驯顺地让他解下了竹管。
+竹管触手温润,显然被人摩挲过许久。拧开一头,倒出一卷极薄的、裁切整齐的宣纸。展开,一股淡淡的墨香与药香混合的气息飘散出来,字迹筋骨挺拔,力透纸背:
+“葫芦侠兄,明日申时三刻,可愿移步‘明理居’东隅小筑一叙?厚朴。”
+没有落款日期,没有多余寒暄,风格一如传闻中那位“厚朴侠”本人——严谨、利落,却自有一股敦厚温润的力量透过纸背传来。更让葫芦侠心跳加速的是,对方竟主动联系他。
+“厚朴侠……竟是主动相邀。”葫芦侠心中既感荣幸,又有些忐忑。他立刻想到,这中间必定有“三力思侠”的引荐。那位总是笑眯眯、说起话来却充满力量的侠长,是他与秉公习武馆结缘的关键。
+记忆被这封特别的“飞鸽传书”牵动,倏地拉回到一年前那个闷热的下午。那时,他还只是一个在几所“习武馆”间犹豫不决的少年,对未来充满了憧憬与迷茫。
+而三力思侠来到他们那所小城做武道宣讲。不同于其他宣讲者大谈特谈习武馆的成绩,三力思侠站在讲台上,第一句话就带着一种独特的诚恳:
+“诸位少侠,我知道你们听过很多关于各家习武馆的传说。今天,我不说虚的。”他扶了扶眼镜,目光扫过台下,“我就说说我们秉公习武馆的笨功夫。”
+“我们那里,没有什么一步登天的捷径。有的是清晨时间广场日晷下的晨读,是工程训练中心日复一日的习武。”
+他语气平实,却充满力量:“我们信奉止戈为武。咱们秉公馆的祖师爷立下过规矩,习武不为争名逐利,而是要上报国家,下安黎庶。如今大宋看似繁华,实则北边辽夏虎视眈眈,最强的武功,不是用来炫耀,而是为了守护。你在这里打下的每一分根基,学到的每一点本事,未来都可能化为让家园安全的一份力量。这里不生产江湖神话,这里锻造的,是能扛事、能钻研、未来能撑起大宋脊梁的国之栋梁!”
+宣讲结束后,他鼓起勇气上前询问。三力思侠没有丝毫不耐烦,仔细看了他颇为偏科的成绩,沉吟片刻,说:“你的内力扎实,但招式略显散乱。我们秉公习武馆,或许正适合帮你把内力系统化、夯实,至于招式,来了再磨炼不迟。” ……
+记忆被信鸽拍打的翅膀打断。信鸽已归,余音袅袅。明日申时三刻,百草园东隅。他知道,那将是他江湖路上,又一个至关重要的驿站。
+信鸽指引的百草园,并非真种百草,乃是一片毗邻“冶园”、静谧少人的老宿舍区,几栋白墙小楼掩映在浓密的梧桐树下。厚朴侠的家就在其中一栋的一楼,带一个小小庭院。
+葫芦侠如约而至,轻叩门扉。开门的正是厚朴侠本人,一身半旧的黄色衣衫,笑容温和,将他引入屋内。
+室内的光景,让葫芦侠瞬间感到一种奇特的宁静。与外头仿佛是两重天地。客厅不大,却处处透着主人的雅趣。墙面是暖白色,挂着几幅字画,一面顶天立地的书架,密密麻麻垒满了书籍,线装古籍与新版剧本并肩而立,间或点缀着几盆绿意盎然的文竹与吊兰。书案临窗,文房四宝井然,一方歙砚,墨色犹新。空气中浮动着淡淡的书香,以及一种清苦的药草气息——源自角落里一个古朴的多宝格,上面摆放着许多青花瓷罐,罐身上贴着“远志”、“茯神”、“当归”等标签。
+“寒舍简陋,随意坐。”厚朴侠引他到窗边的藤椅坐下,那里已有一位客人。
+“这位是山韵侠,在艺文馆做事,精于音律与文章,是我的老朋友了。”厚朴侠介绍道。
+葫芦侠忙行礼。山韵侠微笑颔首,将一盏清茶推至他面前,茶汤澄澈,香气清远。
+寒暄未已,厚朴侠忽指着书房一侧:“那是我的丹房,平时胡乱写写画画,堆得杂乱,让小友见笑了。”葫芦侠循指望去,见靠墙有一架看似普通书柜,厚朴侠在侧面某处轻轻一按,那书柜竟无声地滑开,露出一道窄窄的楼梯,通往上方的小阁楼。楼口泄出温暖的光,隐约可见沿墙堆放的大量卷轴、稿纸,墙上似乎也钉满了各式话剧稿件。
+“这……”葫芦侠惊叹。
+“一点小机巧,图个清静。”厚朴侠笑道,并未多言阁楼内详情,但那惊鸿一瞥,已足够让人浮想联翩。
+三人越聊越是投机。葫芦侠还把自己写的稿件给山韵侠和厚朴侠看,他们都称赞不已,还提出了许多表演的思路。在话剧方面,葫芦侠只觉得平日许多模糊的想法,被两位前辈三言两语便点得透亮。窗外日影西斜,茶汤续了又淡。山韵侠忽然提议:“厚朴精于导演,我略通表演,小友你既有这番奇思妙想,又有践行之心。我们何不合力,做点有意思的东西?”
+厚朴侠眼中一亮:“哦?说来听听。”
+“一起创作一部话剧,将我们习武心得放入其中?演给大家看?”
+厚朴侠沉吟片刻,缓缓点头:“此事大善。小友,你可愿意?”
+葫芦侠霍然起身,郑重一揖:“固所愿也,不敢请耳!多谢厚朴老师、山韵老师提点!”
+“好,好。”厚朴侠将他扶起,三人相视而笑。
+前半学期,因为葫芦侠在数理门表现优异,收到了来自红灰青蟹的邀请。红灰青蟹并非正式堂口,而是数年前由几个志趣相投的数理弟子自发结成的小会,专收些数理门在正统武学之外,还对杂学、奇技、博物甚至海外风物感兴趣的不务正业之徒。会中成员服饰不拘,唯在腰间系一条红、灰、青三色编织的绦带以为标识。
+ 如今的“红灰青蟹”,经过数年发展,虽依旧不算主流,却也在武堂内积累了数十名成员,形成了独特而温暖的氛围。每月一次的蟹会,或是品评新奇器械,或是辩论古怪问题,或是分享游历见闻。但最让会内外人称道的,并非这些智识的交锋,而是一项不起眼却直抵人心的活动——温怀一刻。
+此活动的源起,要追溯到“红灰青蟹”的创始人之一,治安侠。他并非武堂中武功最高强者,却有一项旁人不及的本事,那就是总能敏锐察觉到同门心中那些难以言说的郁结与挫败。他常说:“江湖风雨,伤人最深的有时不是刀剑,而是心里那道过不去的坎、那份无人可说的冷。”于是,在他倡议下,红灰青蟹有了这条不成文的规矩:以己之长,慰彼之心。这温怀一刻,便是其中最柔软、也最直接的形式。
+活动并无固定日期,多在月色清朗的夜晚,于武馆后山的喷泉广场一隅悄然进行。不设桌椅,只铺着厚厚的草席,置有清茶与简单的茶点。治安侠总会提前悄然留意武堂内那些听闻遭逢不顺的同门。他会以蟹会闲聊的名义,邀上三两核心成员,再偶遇那位需要帮助的同门,一同随意坐坐。
+葫芦侠初入红灰青蟹不久,便见识了一次温怀一刻。那夜被邀至喷泉广场的,是锻造门一位姓赵的师兄。赵师兄连日来神情恍惚,他耗费三年心血、即将锻成的剑,在最后淬火时因炉温的波动而功亏一篑,剑身隐现裂痕。此事他未曾对外人言,但内心憋闷苦涩,近乎自闭。
+治安侠并未多问,只煮着茶,聊起自己早年学艺时,曾将师父一方珍贵古砚不慎摔裂的往事。“当时觉得天塌了,躲在柴房三日不敢见人。”治安侠声音平缓,“后来师父找到我,没骂,只指着那粘好的砚台说,‘你看,裂痕还在,但墨汁依旧能磨匀。器物之损,有时反见其真。心里那道坎,你得自己迈过去,但别一个人闷着。’”
+另一位精于乐器修缮的女弟子,则取出一支她随身携带、笛身有一道细微修复痕迹的竹笛,吹了一曲《幽谷》。笛声清越,那道裂痕并未损其音色,反添一丝沧桑韵味。赵师兄起初沉默,紧握的拳头微微颤抖。葫芦侠不善言辞,只是默默为他续上热茶。另一位擅长木雕的成员,则将一块带有天然结疤的木料递给他抚摸:“看,这疤本是伤,雕成山峦,反成点睛。”
+夜渐深,茶已淡。没有拥抱,没有安慰的肢体动作,只有安静的倾听、理解的分享。
+良久,他擦干泪,深吸一口气,眼中虽仍有红丝,却重新亮起微光。“多谢……诸位。”他声音沙哑,却扎实了许多,“剑,我会想办法补救。心里这关……我想,我能试着迈过去了。”
+那夜之后,赵师兄虽未立刻成为青蟹常客,但再见时,眉宇间阴郁已散。后来听说,他将那柄有裂的剑重新设计,命名为“痕月”,竟成其独特风格的代表作。
+红灰青蟹的这条绦带,因此不止是兴趣相投的标识,在不少成员心中,更隐隐成了一份慰藉。葫芦侠也在这氛围中,愈发认同青蟹那份超越纯粹学术好奇的、温暖的内核。
+就在葫芦侠还沉浸在红灰青蟹的温暖里时,集市里突然贴出了告示:近期周边突发瘟疫,这种瘟疫虽然大多情况下不致命,但是感染上会让武力大幅下降,这一年的武林大会可能要推迟。也劝习武馆的各位侠客选择返乡躲避瘟疫或者呆在住所里不要出门,减少感染风险。葫芦侠一下面临着两难的境地。
+那告示就贴在布告栏最显眼处,盖着习武馆总务处的鲜红大印。措辞虽力求平稳,但依然触目惊心。人群围了一圈,窃窃私语声汇成一片压抑的嗡鸣。
+“听说隔壁已有数名弟子中招,内力运转滞涩,一套基础拳法都打不全……”
+“可不是,这软筋散似的疫症,不伤性命,专蚀修为,真是歹毒!”
+“这武林大会怎么说不办就不办了,唉,白准备许久……”
+葫芦侠挤在人群中,指尖拂过粗糙的告示纸张,冰凉。他刚刚才觉得脚下的路清晰了些,学习到了一些真功夫,还想着切磋切磋呢。这一切,都要被这突如其来的灾厄打断吗?
+两难的境地,此刻无比真切地压在心头。
+留下? 告示说得明白,疫疾当前,集中修习风险大增。纵使堂内会采取措施,但人员往来难以完全隔绝。万一染上,数月苦修可能付诸东流,“武力下降”在武林大会推迟的背景下或许暂不致命,但对急需夯实基础的自己而言,无疑是沉重打击。况且,留在此处,若疫情蔓延,堂内资源紧张,气氛惶惶,也未必能安心向学。
+返乡? 那便意味着离开这刚刚熟悉的江湖,离开可以随时请教切磋的同道,离开藏书阁的海量秘籍,离开明师益友的点拨。家乡小城固然安全,但修炼资源匮乏,信息闭塞,更无同道交流。
+接连几日,堂内气氛明显不同。往日清晨练功场上的呼喝声稀落了许多,时间广场上匆匆而行的人们大多面覆白巾,眼神警惕。熟悉的同门,有的已收拾行囊,神色匆匆地告别;有的则如他一般,满脸纠结,在去留间反复权衡。欧阳侠来找过他一次,面色凝重:“家中有信来催,父母担忧。我……恐怕不日便要动身了。葫芦兄,你作何打算?” Eddie侠倒是洒脱些,苦笑道:“我是要回家去的,倒是不远。只是这一回去,不知何时才能再与二位兄台这般论道了。”
+葫芦侠去了一趟百草园,厚朴侠的小院门扉紧闭,门口贴了张字条,笔力依旧敦厚:“暂闭门谢客,潜心配药,以期有用。诸弟子当以安危为重,妥善自处。厚朴字。”
+最后一丝犹豫的余地,似乎也被这张字条抚平了。作为新入门的弟子,在堂方已给出明确建议、家中父母书信催促愈急的情况下,或许暂时的退避,保全有用之身,才是更负责任的选择。
+做出决定的那晚,他独自在紫霞湖旁边走了很久。湖水映着稀疏的星子和岸边沉默的楼影。他很不舍得这里的生活,但正是这份“不舍”,让他明白此地值得珍惜,值得在更安全、更合适的时刻回来。
+离开那日,天色有些阴沉。二道门前不复往日喧嚣,只有寥寥数人默默出行。
+他的第一个季度的习武之旅,就这样,被一场突如其来的瘟疫画上了一个仓促的、未完成的逗号。马车载着他驶离紫金山麓,熟悉的景色向后飞掠。他怀中抱着行囊,仿佛抱着这段短暂却充实的时光。
+武林大会推迟了。但他知道,自己的修行并未结束,只是换了一个场地。故乡的斗室,将成为他临时的闭关之地。而这个江湖,那群人,那些事,那盏灯,他一定会在春暖花开的日子,重新回来。
+第一年的武林大会,也许要到下一年,才能继续办了。 但属于葫芦侠的江湖,才刚刚拉开序幕,无论身处何方。他闭上眼,开始在心中默默推演起下一个章节。路途还长。第一个季度就像是一个醒目的冒号,昭示着那众豪杰再次坐而论道的日子。
\ No newline at end of file
diff --git a/docs/novel/第02章.txt b/docs/novel/第02章.txt
new file mode 100644
index 0000000..52d4ac3
--- /dev/null
+++ b/docs/novel/第02章.txt
@@ -0,0 +1,135 @@
+葫芦侠行(第二章)
+ 瘟疫很快就过去了,转眼又是春天,新的一个季度开始了。葫芦侠从故里泉州,驾着马车准备回到秉公习武堂。
+上一年年末的武林大会没有举办,侠客们都很失望。但是听紫金山传出来的消息,这次武林大会直接会在秉公习武堂开办后的第一周举行,因此,大家都摩拳擦掌,跃跃欲试。
+葫芦侠家住东南沿海的泉州,离紫金山有近千里之遥。这般长途,马车自然是最好的代步。但也有例外——葫芦侠与同乡轩晨侠返回习武堂的路上,就遇见一位骑着骆驼的侠客。
+这位侠客生得高大魁梧,体格雄健,若是放在战场上,定是冲锋陷阵的猛将。他见到葫芦侠,主动上前抱拳道:“这位兄台,可是要回紫金山?”听得葫芦侠确认,他便朗声自我介绍:“在下骆骑侠,此番正是要回秉公习武堂,准备武林大会。”葫芦侠和轩晨侠赶忙还礼:“巧了,我们也是习武堂的,幸会幸会!”老乡见老乡,两眼泪汪汪。轩晨侠和葫芦侠都是泉州派到秉公习武堂的杰出代表,在预备营的时候他们就是同班同学,早已认识。此番眼前的这位骆骑侠,是来自故乡另一个预备营的,在同行的路上,不可不谓巧合。这位骆骑侠后面还会出场,且听后文分解。
+让我们把视线转到武林大会的比赛现场。今年的武林大会规则是,需要在每个门先进行内部的比武,然后每个门再派出一位代表参加最终的武林大会。紫金山的雪还没消融,数理门的演武场冻得呵气成霜。三十几个弟子搓着手跺着脚,看着掌门华师祖在黑板上写了两行字:
+“连续变化,见微知著;结构规矩,万物皆数。”
+写完他把粉笔一扔:“老规矩,三人一组破阵。赢的那组,派个人去图文馆参加‘十十七门论道’,跟其他六派的高手过招。”
+底下顿时嗡嗡响成一片。谁都知道图文馆那地方邪乎——说是“论道”,其实就是各门派年轻一辈较劲的擂台,去年化学门的子弟差点把图文馆的古籍点着了。
+葫芦侠搓了搓冻僵的耳朵,左边Eddie侠正掏着个本子写写画画:“三人组最优配置应该是攻守算一体,我主攻,欧阳计算,你……”
+“我负责捣乱。”葫芦侠咧嘴笑了。
+右边欧阳侠白他一眼:“严肃点。你看,贾瑞侠那组已经凑齐了,都是练积分剑法的狠角色。天勒侠更吓人,听说他解偏微分方程比咱们喝水还快。”
+葫芦侠顺着目光看去。贾瑞侠个子高高的,永远穿着浆洗得笔挺的蓝布衫,手里总捧着本《吉米多维奇》,据说能把书里三千道题倒背如流。天勒侠则是个闷葫芦,可上次有人见他用算盘布阵,硬是把后山的石锁算出了裂缝。
+分组结果出来,葫芦侠、Eddie侠、欧阳侠一组,抽到的对手是紫铜侠那组——这位师兄姓紫名铜,人如其名,练的是“刚性功法”,招式硬邦邦的,但据说从未出过错。
+“麻烦。”欧阳侠皱眉,“紫铜侠的完备性证明掌最克咱们这种野路子。他那套功夫讲究严丝合缝,你出一招,他能推出十步后手。”
+Eddie侠已经开始在纸上画关系图:“需要引入随机变量干扰他的推理链……”
+正说着,钟声响了。
+紫铜侠那组三人站成三角,起手式一模一样,连衣角摆动的幅度都分毫不差。而葫芦侠这边,Eddie侠在比赛开始前用几何心法默默运着气;欧阳侠尝试着牛顿迭代法,试图在开赛前找到最合适的步伐前进;而葫芦侠却像吃了早八迷魂药般,晕乎乎地在原地打转。
+“开始!”
+紫铜侠动了。三人如一体推进,掌风织成一张密不透风的网。这是“收敛序列阵”,每一步都在压缩对手的活动空间,理论上只要步数足够,能把人逼到墙角动弹不得。
+Eddie侠迎上去,他那套“极限剑法”专找序列的破绽,剑尖总在对方招式将发未发时点到。可紫铜侠变招了——三人的阵型突然旋转,从“收敛”变成“柯西序列”,这下连破绽都成了陷阱。
+欧阳侠大喝一声,双手拇指快速点算其他手指关节。他在算这个阵的“聚点”,可算了三息,额头见汗:“不行……他们是完备空间,没有孤立点!”
+葫芦侠忽然笑了。他把葫芦往腰间一挂,不退反进,直接撞进阵里。
+“你疯了?!”欧阳侠惊呼。
+紫铜侠也一愣。按正常套路,这时候该后撤重组,哪有往阵眼里撞的?就这一愣神的工夫,葫芦侠已经贴到紫铜侠身前,然后——他做了个谁也没想到的动作。
+他伸手,在紫铜侠左肩拍了一下,又在他右肩拍了一下,然后蹲下身,在对方左脚鞋面上也拍了一下。
+全场安静了。
+紫铜侠僵在那里。他的“完备性证明掌”需要严密的逻辑链:如果A,则B,则C……可葫芦侠这毫无章法的三拍,根本不在他的逻辑集里!就像一道题,题干都不符合公理体系,还证什么证?
+葫芦侠趁他卡壳,从他和另一个组员的缝隙里钻了出去,反手一捞——阵旗到手。
+“承让。”葫芦侠咧嘴一笑。
+紫铜侠脸涨得通红:“你这……这不合规矩!”
+“哪条规矩说不让拍肩膀了?”葫芦侠眨眨眼,“师兄,你的武功是完备了,可现实世界从来不完备啊。”
+台下哄笑。程长老咳嗽一声:“葫芦侠组,胜。”
+数理门内斗结束,开勒侠最终被门派派出去参加大会——他擅长流形几何,招数变幻莫测,数理门里没有人能够比他的出招更规整。葫芦侠那套“野路子”虽然出奇制胜,但在严谨性上到底差了一筹。
+三天后,图文馆。
+这地方说是藏书楼,其实大得离谱。一楼是普通典籍,二楼是各门派专著,三楼则是禁区,据说藏着前朝武林秘史。今天为了“十七门论道”,一楼大厅清出一片空地,十七张方桌摆成环形,各派代表已经就座。
+葫芦侠是跟着来看热闹的。他挤在人群里,看见自家开勒侠坐在数理门的桌子后,腰板挺得笔直,面前摊着本手稿。机械门一位瘦小精悍的姑娘,手里托着个刚拼装好、还在微微振翅的木质“机关鸢”。“晚辈机械门七柒侠,请教材料门人兮师兄——”她看向对面这位打扮不凡的青年,脖子上似乎还挂着代表门派道义的项链。“若我这机关鸢的骨架用的是轻韧的云杉木,覆以浸过桐油的素绢,师兄可能从材料上破之?”
+材料门的青年——人兮侠微微一笑,不疾不徐地从随身皮囊中拈出一点银灰色的粉末:“七师妹的选材已是上佳,云杉木轻而韧,桐油防水。可惜……”他屈指一弹,粉末如雾飘散,均匀落在机关鸢的素绢翅膀上。只听一阵极其细微的“嗞嗞”声,那看似坚韧的绢面竟以肉眼可见的速度失去光泽,变得酥脆,最后“噗”地一声,被机关鸢自身振翅的力道带出了几个破洞。
+“此乃‘朽材散’,不伤木骨,专蚀植物纤维与某些胶质。桐油防得住水,却防不住它。”
+七柒侠眼睛一亮,非但不恼,反而露出“果然如此”的表情。她对人兮侠说:师兄,我的机关鸢已经进入第四阶段的制作了,如果没有人兮师兄的帮助,我还真没发现这个漏洞。我打算用你的名字来纪念,就叫“无人机”,你觉得呢……
+两人你一言我一语,斗得有来有回。葫芦侠看得津津有味,旁边挤过来个人,是他舍友阳睿侠,主修“加瓦”的,此刻正眯着眼看热闹。
+“你在看什么呢阳睿侠?”葫芦侠捅捅他。
+阳睿侠压低声音,“你看那边。”他努努嘴,指向经管门那边。经管门代表是个富态青年,一直笑眯眯的,可每当有人展示绝技,他就往本子上记几笔,还不时拨弄手边的金算盘。
+“他在算什么?”
+“算价值。”另一个舍友玉斌侠凑过来,这家伙练的是“优化算法”,专找最短路径,“你看,机械门每展示一件样品,他就记下成本;化学门每用一份药剂,他就估算市价。他在算……哪门功夫最‘划算’。”
+葫芦侠咂舌。正说着,那边吵起来了。
+是外文门的于遥侠和数理门的开勒侠。于遥侠一身水绿儒裙,手握一管青玉笔,气质清冷。她方才正阐述“武功的韵律与意象”,说到招式当如诗文,有意在言外之妙,目光便落到了窗边始终沉默的开勒侠身上。
+“开勒师兄以为如何?”她声音清脆,“数理门武功,是否过于追求确然,失了武道应有的神韵与留白?”
+开勒侠从书页上抬起眼,语气平淡得像在陈述公理:“所谓‘意在言外’,在泛函分析里,对应的是函数空间稠密集的补集。不可言说,只是尚未找到合适的数学语言描述。”
+于遥侠蹙眉,玉笔在指尖一转:“兄台此言,未免将天地灵韵皆视作死物。我这招‘平沙落雁’,取的是秋意萧疏、孤鸿渺远之意境,此中苍凉阔大之感,莫非也能被师兄的方程算尽?”
+“能。”开勒侠放下书,“你那一招,真气运行轨迹是复平面上的亚纯函数,主部在无穷远点有三阶极点。你说的‘苍凉’,是函数在某个扇形区域内的渐近展开式系数为负所产生的心理映射;‘阔大’,是收敛半径大于寻常招式。至于‘孤鸿’的意象,大概来自你函数图像在某个变换下与候鸟迁徙路径的拓扑同伦。”
+图文馆里一片安静。几个文史门弟子张大了嘴,经管门那位打算盘的手也停住了,似乎在想这种“意境量化”能不能估值。
+于遥侠脸色微白,不是愤怒,而是某种认知受到冲击的眩晕。她深吸一口气,玉笔轻点,一缕肉眼几乎看不见的墨色气劲如游丝般飘向开勒侠面前的书页——并非攻击,更像是一种带着情绪的、文人式的“质问”。那气劲轨迹飘忽,确如书法中的飞白,难以捉摸。
+开勒侠右手食指在算盘上轻轻一拨,一粒珠子弹起,沿着一条简洁的直线飞出,精准地撞在那缕墨色气劲最“浓”的某一点。没有声响,墨气如烟消散。
+“你的‘飞白’,是故意在参数方程中引入随机扰动项,模拟笔墨的枯笔效果。”开勒侠看着那粒落回的算盘珠,“想法有趣,但扰动项的协方差矩阵选得不好,导致气劲结构在3.7尺处出现一个本征值接近零的薄弱点。我打的就是那里。”
+于遥侠连退两步,撞在身后的书架上,顶上两卷《诗品》竹简应声滑落。她惊呼一声,却见开勒侠左手一挥,三粒算盘珠呈品字形射出,在空中相互撞击变向,后发先至,在竹简侧面轻轻一托、一旋、一送,两卷竹简便稳稳落回原处,如同从未动过。
+“线性方程组,加旋转矩阵变换。”开勒侠收回手,看向脸色苍白的于遥侠,“于瑶侠,意境很美,但结构不稳。文章如此,武功亦然。”
+于遥侠怔了许久,终于躬身一礼,声音微颤:“受教了。”那并非服输,而是某种更复杂的东西——她一直耕耘的、充满感性的世界,突然被另一种冰冷而强大的语言彻底照亮并解构了。
+葫芦侠看着这一幕,忽然觉得,开勒侠那套看似不近人情的“数学语言”,或许本身就是另一种境界的“诗”。文理之辩,是每年武林大会的焦点话题。此事从来没有标准答案,可谓仁者见仁,智者见智。
+“发什么呆呢?”肩膀被拍了一下,是一起来看大会的欧阳侠,“走走,吃饭去。下午还有好戏呢,听说设计门要展示最新的八卦阵,据说至今还没有英雄可破呢……。”
+葫芦侠最后看了眼场中。开勒侠已经坐回位置,重新摊开手稿,仿佛刚才那神乎其技的一手只是随手为之。其他各派代表神色各异,有敬佩,有忌惮,有不忿。
+十七门论道,这才刚刚开始。
+而葫芦侠摸了摸怀里的葫芦。没有人知道,这个葫芦侠里到底卖的是什么药。他知道自己离那个舞台还远,他需要慢慢沉淀。
+武林大会的喧嚣如潮水般退去,紫金山又恢复了往日治学练功的节奏。葫芦侠却有些提不起劲儿。他渐渐发现,在这偌大的习武堂里,自己认得的人竟屈指可数。食堂用饭时,周遭三五成群,高谈阔论,而他总是独来独往,获取功法的途径往往只能来源于课堂。就连澡堂里,大家都借着歌声,在氤氲的水汽中交流着各种江湖见闻的交流,而他只是匆匆洗完便走。
+“葫芦,你近日怎地有些闷闷不乐?”一日课后,Eddie侠擦着额角的汗,直言问道。
+葫芦侠苦笑:“非也。我只是觉得……这习武堂内卧虎藏龙,各门各派皆有俊杰。大会之上,可我除了咱们数理门这方寸之地,竟与其他人毫无交集。仿佛……白白错过了这江湖。”
+欧阳侠若有所思:“你是说,觉得自己圈子太窄,想多结交些朋友?”
+“正是此意。”葫芦侠叹道,“我见许多人天生便能与人谈笑自如,无论去到哪个殿、哪个门的聚会,皆能如鱼得水。反观我自己,与人搭话超过三句,便常觉词穷,手心冒汗。”
+Eddie侠拍腿道:“这有何难!此乃‘社交功法’生疏尔!。我有一计——”
+他从怀中掏出个小本子,封皮上竟写着《江湖交际初阶导引(自纂)》。
+“这……这是何物?”葫芦侠愕然。
+“我平日观察所得。”Eddie侠颇有些得意,“我将那些善于交际的同门行为做了归纳建模。你看,第一章,‘破冰式’:可基于共同情境发起对话,如‘兄台也爱吃瓦香鸡?’;第二章,‘递进式’:在破冰后迅速寻找共同兴趣链接点,如看到对方在看蹴鞠比赛,上前就蹴鞠话题后展开交流’;第三章……”
+葫芦侠听得头晕,却也觉新奇。欧阳侠在旁泼冷水:“人际交往,贵在真诚,岂能全照本宣科?”
+“先有章法,再求化境嘛!”Eddie侠不服,“葫芦侠,不若你便以此为本,定个‘社牛挑战’之期。譬如,以一月为限,每日需主动与一位陌生同门交谈,内容、时长皆需记录,我与欧阳侠为你督战,如何?”
+“社牛……挑战?”葫芦侠喃喃重复,眼中渐渐有了光。这听起来,竟像是一门新的、需要攻克的心法课程。
+“好!”他握了握拳,“便从明日始!”
+第一日,目标是饭堂。先从身边的人聊起。葫芦侠端着食盘,目光扫视,最终锁定一位之前同门的、但是一直不敢打招呼的同学。
+葫芦侠主动把餐盘端到他的面前,问这位同门:“同学,我们好像很眼熟?我常常在洗漱台遇到你,想认识你一下可以吗?我叫葫芦侠”
+这位同门放下碗筷,抹了抹嘴说:“你好,我叫贾锐侠。很高兴认识你。”
+第一日,目标就定在每天吃饭的食堂。葫芦侠深呼吸几下,目光在闹哄哄的人群里扫来扫去,最后停在角落那个独自坐着的身影上。那是数理门的同学,经常大清早在洗漱台旁边碰到,总看他安静地端着木盆,点点头就快步离开,葫芦侠一直没敢开口打招呼。
+他稳了稳有点发颤的手,端着餐盘走过去。“这位同学,这儿有人吗……”他顿了一下,努力让声音显得自然点,“咱们是不是常碰面?在洗漱台。我叫葫芦侠,能认识一下吗?”
+那位同学闻声抬头,是张清秀但有点冷淡的脸。他眼里闪过一丝意外,随即放下筷子,用布巾擦擦嘴,竟然站起来抱了抱拳——这是堂里弟子间比较正式的见面礼。“原来是葫芦兄。我叫贾锐侠,也是数理门的。”他语气挺平和,没有拒人千里的感觉,反而指了指对面的长凳,“一块儿坐吧。”
+葫芦侠心里松了松,坐下开吃。扒拉几口饭后,刚开始的紧张感稍微缓了点,他试着找个话题:“贾锐兄平时好像挺喜欢安静?我看同门里头,有的人天生像火,到哪儿都能跟人打成一片;有的人就像潭水,沉沉稳稳的,更爱自己琢磨。你怎么看这种‘脾气天性不一样’的事儿?”
+贾锐侠听了,若有所思地放下碗:“葫芦兄这话,倒让我想起一门偏门的学问,叫‘心性四路分说’。据说是前朝一位既懂医术又会看相的高人传下来的。他把人的天生脾性,大致按‘神思朝外还是朝内’、‘做事重决断还是重感受’,交叉分成了四种根本路数。比如说有人属‘外路’,喜欢跟人打交道,练功时真气都走得痛快敞亮;有人属‘内路’,爱深思,精神都收在里头,得自己待着才能蓄上劲。”
+葫芦侠听得来劲了:“还有这种说法?那依你看,我算哪路的?”
+贾锐侠仔细看了看他,微微一笑:“葫芦兄你能主动过来搭话,有外路的胆气;可说话时又带着点斟酌试探,不是完全外放,可能更靠近‘风路’——外面看着随和,里头自有章法。至于我嘛,”他自嘲地摇摇头,“大概偏‘山路’这种,闷着不动,没点外力推一把就不带挪窝的。这说法虽然不能全信,但有时候想想,对理解为什么同门练功路子不同、为什么同一招大家悟到的东西完全两样,倒是个挺有意思的参照。”
+“有意思!”葫芦侠觉得新鲜,这“四路分说”居然把摸不着边的脾气差异,说出了几分能琢磨的道理。两人边吃边聊,从天性又扯到不同脾性可能更适合练什么武功,一顿饭吃完,聊得挺投缘。最后贾锐侠还补充道:“其实这说法,跟西域传过来的一套叫‘MBTI’的心性鉴识法颇有相通之处,都是试着把千人千面的脾气,归出几条可说道的理路来。”
+葫芦侠暗自记下:“第一天,目标完成。心得:同门之间,本来就是最容易说上话的。聊聊‘脾性路数’这种话题,能谈得深,还不尴尬。对了,贾锐兄说的那套西域‘MBTI’鉴识法,以后有机会得再请教请教。”
+果不其然,借助MBTI大法,葫芦侠开始结识一群朋友。这个MBTI,真有大本事。
+但是在习武堂这个环境里结识别人还不够,还需要换到陌生的场景去进一步提高交际水平。葫芦侠趁着习武堂课余时间,和欧阳侠,Eddie侠,加上隔壁的明君侠,一起到姑苏城游玩。
+姑苏城的春,是浸在湿漉漉的柳烟与软糯吴音里的。葫芦侠随着欧阳侠、Eddie侠及鸣君侠,挤在游人如织的山塘河边,第一次在完全陌生的街市践行他的“社牛挑战”。
+目标一,出现在一座石桥下。一位身着“顺风速递”的年轻驿卒,正从小舟上卸下成堆包裹,额角全是汗珠,神情疲惫。葫芦侠握紧了手中刚在街边买的、用来“练胆”的一小束洁白栀子,花香清冽。
+“去,就现在。”明君侠用胳膊肘碰他,低笑。
+葫芦侠深吸口气,走上前。那驿卒见他挡路,抬头皱眉,却见眼前这光头少年双手将一束沾着水珠的栀子花递了过来,脸涨得通红,话也说得磕绊:“这、这位兄台……送件辛苦了。这花……送你,闻着醒神。”
+驿卒愣住了,看看花,又看看葫芦侠认真的脸,防备之色化作愕然,继而挠头笑了起来,露出一口白牙:“哎哟,小兄弟……这、这怎么话说的。跑码头这么多年,头回有人给俺送花。”他接过花,还真凑到鼻尖闻了闻,疲惫的眉眼舒展开,“谢了啊!你们是……紫金山习武堂的学生吧?一看就是实在人。”
+目标二,在狮子林池畔。几位衣着五颜六色、谈笑风生的大妈正在一株盛放的白玉兰树下徘徊,似乎想合影,却苦于无人帮忙。
+“机会。”Eddie侠小声道。
+葫芦侠再次硬着头皮上前,对其中一位气度最从容的大妈拱了拱手:“这位夫人,晚辈略通丹青速写之术,可否为诸位画一幅作品?”他指了指自己,又指指她们身后的花树。
+大妈们闻言,眼睛一亮。为首那位打量他一番,爽朗笑道:“小侠客有心了!可是要画我们这几个老太婆?好好好,来来,姐妹们,站好!”几人迅速挽手倚树,摆出姿态。
+葫芦侠哪里会丹青,他不过是想主动提出帮她们定格影像。此刻只好临场发挥,装模作样地以手为框,眯眼取景,口中道:“夫人请看左前方飞檐……笑意再盈些……好!‘气韵’已足,此景已入‘心画’,必是佳作!”
+大妈们被他一本正经的“术语”逗乐,笑声阵阵,拍完后还硬塞给他一包姑苏糕点。“小侠客有心了!下回还找你‘画’!”
+回到习武堂后,葫芦侠身上确实多了几分由内而外的松快。姑苏城的春风与烟火气,似乎吹散了他心头最后一丝畏缩的尘埃。他想起入堂数月,除了必修课业,竟从未主动向传道授业的师长们深谈过自身困惑,这岂非另一种“画地为牢”?于是,他怀着比当初挑战陌生人时更甚的忐忑,给自己的领课师长——李教习,恭谨地写了一封信,简述了自己入堂以来的修习、迷茫与对前路的思索。
+信送出不到一日,一只熟悉的青灰色信鸽便扑棱棱落在他窗台,爪上系着素笺,展开只有一行力透纸背的字:“鼎新楼三层西厢,明日巳时,静候一叙。”
+李教习年约四旬,面容清癯,气质儒雅,与其说是武林高手,不如说更似书院先生。他已在厢房煮好了茶,见葫芦侠紧张行礼,只温和摆手:“坐。你的信我看了。不满足于课业通关,开始思虑长远路径,这是好事。”
+葫芦侠捧过热茶,将自己的困惑一一道出。
+李教习听罢,沉吟片刻,道:“葫芦侠,以你如今之根基与心性,若只求在堂内大小比中位列前三分之一,其实不难。若想拿下那最终的武林大会胜利,按部就班,精熟教材,勤练不辍,假以时日,水到渠成。”
+他话锋一转,神色肃然了些:“然,若志不止于此,欲窥顶尖风光,则需明白,那不再是‘习得’,而是‘修炼’。何谓修炼?是将外传的‘功法’,化为内生的‘本能’;是将有形的‘招式’,融为无形的‘直觉’。这需要时间沉淀,需要海量试错,更需要你跳出‘为学武功而学武功’的桎梏,去更广阔的天地寻找养分。”
+“你信中提到结识各门朋友,此为大善!”李教习眼中露出赞许,“数理功夫是你的‘心法根基’,务必打牢。但绝不可囿于此。秉公习武堂是一个大舞台,需要你不断试错,不断探索。习武堂从不只是练武。真正的顶尖高手,其‘武功’早已超越门派招式之限,乃其全部学识、阅历、心性融合后,自然流淌出的东西。”
+一席话,如鼎新楼外照入的阳光,驱散了葫芦侠心中许多混沌。他忽然觉得,眼前的路广阔了许多。
+此后一段时日,葫芦侠仿佛打开了一扇新门。他不仅更用心领悟李教习的指点,还鼓起勇气,陆续请教了其他几位素有威望或风格独特的师长。或请教武道心境,或探讨不同武学流派的底层逻辑,甚或只是听师长们讲述江湖见闻、前辈轶事。每一次请教,未必都有立竿见影的“功法提升”,却都如点点星光,照亮他认知江湖与武学的不同维度。
+就在葫芦侠的“社牛挑战”搞得风生水起,在习武堂里跟谁都能聊上几句的时候,布告栏上一张新贴的告示,把他吸引住了。
+“常青藤西部教习队,招人。”
+下面小字写着,这“常青藤”是几个江湖名门大派凑的联盟,时不时会派些得力的弟子,组成队伍,跑到武功不怎么普及的西部偏远地方,去帮那里的乡学武馆开蒙讲课、整理功法,大致两个月。这既是历练,也是积德的好事,江湖上名声挺响。
+葫芦侠心里“咚”地跳了一下。西部,完全不一样的地方。教小孩练武,更是他从来没干过的事儿。想去见识的念头和怕自己干不来的担心,在心里来回拉扯。他琢磨了好几天,最后,还是这几个月攒下的一点胆子和好奇心赢了。他提笔,老老实实写了申请。
+第一轮面试,在一间挺清爽的屋子里。主考是位看着很利落的女教习。她翻了翻葫芦侠写的申请,问了几个寻常问题,葫芦侠都稳妥答了。最后,女教习看见他随信附上的一卷手抄本,拿过来打开。
+“这是什么?”
+“回教习,这是弟子平时自己瞎说的练功心得,见到听到的杂事,还有对堂里一些招式的粗浅图解。”葫芦侠有点不好意思。这本来是他逼自己用功才弄的“练功笔记,他管这叫《葫芦瞎说》。
+女教习翻了几页,里面确实杂,有字有画,有推招式的草图,有练武后的感悟,甚至还记了两句堂里趣闻和食堂哪个菜好吃。画得歪歪扭扭,但透着股认真劲儿。虽然简单,能看出是花了心思的。
+“有点意思,”女教习点点头,脸上有点笑模样了,“不算正经武功秘籍,倒看得出你是个肯留心、也愿意把东西理出来分享的人。你这‘记笔记’的功夫,去教小孩启蒙,说不定挺管用。想过把它弄得更清楚好懂点吗?”
+“正在琢磨怎么改。”葫芦侠赶紧说。
+“行。”女教习合上本子,“回去等信儿,准备二轮叙话。”
+几天后,二轮面试换了个大点的厅堂。这回对面坐着两个人。一个是这次教习队的正领队,孔锝侠,身材笔挺,脸板得挺严,目光跟哈士奇似的,一看就非常认真不好糊弄。另一个是副领队,星侠,是位气质温和,总是带着点笑的美女子。
+孔锝侠问得直接,句句都问在实处:“西部地方苦,东西缺,人也生。要是去了,当地武馆师傅不信你,或者小孩调皮根本不服管,你怎么办?说好的粮食补给要是送不到,又怎么办?”
+葫芦侠照实说,自己武功不算拔尖,但耐心还行,也愿意学愿意干。至于没遇到过的情况,不敢乱保证,只能说一定尽全力。孔锝侠听完,没说话,只是微微皱着眉。
+星侠问得就和气多了。她拿起那本《葫芦瞎说》,翻到一页画着好几个小人以不同角度、或稳或晃地蹲着的草图,旁边还标注着些箭头和“重心”、“支撑”的字样,笑着问:“看你这里琢磨了不少‘根基’的姿势。要是让你去教一个从没站过桩、练过下盘的孩子,怎么让他明白‘扎马步’的要义?”
+葫芦侠看着自己画的那些歪歪扭扭的小人,想了想,试着说:“可能……会让他想象自己是一张‘四条腿的凳子’?小孩都坐过凳子。凳子能站稳,是因为四条腿均匀撑在地上,分担了重量。扎马步的时候,两条腿就像凳子的‘腿’,脚掌踩实就是‘腿’着地。不能歪,不能一边重一边轻,心里要想着把身体的‘重量’也均匀分到两条‘腿’上。先不求蹲多低,先求像凳子一样,推一下都不太容易晃。”他一边说,一边不自觉地用手比划着均匀分摊的动作,“这么想,他可能就容易找到那种‘整’和‘稳’的感觉,而不是光觉得腿酸了。”
+星侠听着,眼睛微微发亮,脸上的笑意更深了。她赞许地点了点头:“这个好。不直接讲虚的‘气’和‘劲’,从‘分担重量、站稳当’这个实在的感觉切入,孩子一听就有点明白该往哪儿使劲了。看来你这本‘杂记’没白写,是真能把自己琢磨的东西,转成别人能听懂的话。”
+星侠听着,脸上的笑意更明显了,不住地点头。她转头对孔锝侠说:“孔队,咱们去西部教人,功夫高低在其次,关键是这颗想把东西讲明白的心,和肯动脑筋的劲儿。这孩子底子扎实,平时笔记也记得门清。是个好苗子。”
+孔锝侠沉默了一会儿,目光又在葫芦侠身上扫了一遍,那锐利的感觉似乎淡了点,终于开口道:“星队说的,也有道理。好吧,就给你这个机会。希望你珍惜,也别辜负了西部那些等着启蒙的孩子。”
+三天后,最终名单贴出来了。葫芦侠的名字,在上面。
+告示最后写着:“入选的人,三天后辰时,到‘材料楼’前面集合,具体商量怎么走。”
+看着榜上自己的名字,葫芦侠心里又是兴奋,又有点发慌。这跟堂里的小打小闹、出去春游完全不一样了,是要离开整整一个月,肩上担着责任的远行。他好像已经看见了西边陌生的天空,听到了小孩们稚嫩的呼喝声。
+他回到住处,开始收拾行李。把那本《葫芦瞎说》小心地放在箱子最上面,又塞进去几本入门功法,和一厚摞空白的纸。欧阳侠也入选了,他去的是另外一个队伍。一些朋友也听说了消息,过来道别,嘱咐了好多话。
+第三天早上,天刚蒙蒙亮。葫芦侠背好行囊,最后看了一眼晨雾里熟悉的习武堂,转过身,朝着“材料楼”那边,稳稳地迈开了步子。
+他的路,从这紫金山下,要延伸到那片遥远又陌生的西部天地里去了。这趟旅程,注定不简单。
\ No newline at end of file
diff --git a/docs/novel/第02章_备用.txt b/docs/novel/第02章_备用.txt
new file mode 100644
index 0000000..7a7167b
--- /dev/null
+++ b/docs/novel/第02章_备用.txt
@@ -0,0 +1,132 @@
+葫芦侠行(第二章)
+ 瘟疫很快就过去了,转眼又是春天,新的一个季度开始了。葫芦侠从故里泉州,驾着马车准备回到秉公习武馆。
+上一年年末的武林大会没有举办,侠客们倍感失望。但是听紫金山传出来的消息,这次武林大会直接会在秉公习武馆开办后的第一周举行,因此,大家都摩拳擦掌,跃跃欲试。
+葫芦侠家住东南沿海的泉州,离紫金山有近千里之遥。这般长途,马车自然是最好的代步。但也有例外——葫芦侠与同乡轩晨侠返回习武馆的路上,就遇见一位骑着骆驼的侠客。
+这位侠客生得高大魁梧,体格雄健,若是放在战场上,定是冲锋陷阵的猛将。他见到葫芦侠,主动上前抱拳道:“这位兄台,可是要回紫金山?”听得葫芦侠确认,他便朗声自我介绍:“在下骆骑侠,此番正是要回秉公习武馆,准备武林大会。”葫芦侠和轩晨侠赶忙还礼:“巧了,我们也是习武馆的,幸会幸会!”老乡见老乡,两眼泪汪汪。轩晨侠和葫芦侠都是泉州派到秉公习武馆的杰出代表,在预备营的时候他们就是同班同学,早已认识。此番眼前的这位骆骑侠,是来自故乡另一个预备营的,在同行的路上,不可不谓巧合。这位骆骑侠后面还会出场,且听后文分解。
+让我们把视线转到武林大会的比赛现场。今年的武林大会规则是,需要在每个门先进行内部的比武,然后每个门再派出一位代表参加最终的武林大会。紫金山的雪还没消融,数理门的演武场冻得呵气成霜。三十几个弟子搓着手跺着脚,看着掌门华师祖在黑板上写了两行字:
+“连续变化,见微知著;结构规矩,万物皆数。”
+写完他把粉笔一扔:“老规矩,三人一组破阵。赢的那组,派个人去图文馆参加‘十七门论道’,跟其他六派的高手过招。”
+底下顿时嗡嗡响成一片。谁都知道图文馆那地方邪乎——说是论道,其实就是各门派年轻一辈较劲的擂台,去年化学门的子弟差点把图文馆的古籍点着了。
+葫芦侠搓了搓冻僵的耳朵,左边Eddie侠正掏着个本子写写画画:“三人组最优配置应该是攻守算一体,我主攻,欧阳计算,你……”
+“我负责捣乱。”葫芦侠咧嘴笑了。
+右边欧阳侠白他一眼:“严肃点。你看,贾瑞侠那组已经凑齐了,都是练积分剑法的狠角色。天勒侠更吓人,听说他解偏微分方程比咱们喝水还快。”
+葫芦侠顺着目光看去。贾瑞侠个子高高的,永远穿着浆洗得笔挺的蓝布衫,手里总捧着本《吉米多维奇》,据说能把书里三千道题倒背如流。天勒侠则是个闷葫芦,可上次有人见他用算盘布阵,硬是把后山的石锁算出了裂缝。
+分组结果出来,葫芦侠、Eddie侠、欧阳侠一组,抽到的对手是紫铜侠那组——这位师兄姓紫名铜,人如其名,练的是刚性功法,招式硬邦邦的,但据说从未出过错。
+“麻烦。”欧阳侠皱眉,“紫铜侠的完备性证明掌最克咱们这种野路子。他那套功夫讲究严丝合缝,你出一招,他能推出十步后手。”
+Eddie侠已经开始在纸上画关系图:“需要引入随机变量干扰他的推理链……”
+正说着,钟声响了。
+紫铜侠那组三人站成三角,起手式一模一样,连衣角摆动的幅度都分毫不差。而葫芦侠这边,Eddie侠在比赛开始前用几何心法默默运着气;欧阳侠尝试着牛顿迭代法,试图在开赛前找到最合适的步伐前进;而葫芦侠却像吃了早八迷魂药般,晕乎乎地在原地打转。
+“开始!”
+紫铜侠动了。三人如一体推进,掌风织成一张密不透风的网。这是“收敛序列阵”,每一步都在压缩对手的活动空间,理论上只要步数足够,能把人逼到墙角动弹不得。
+Eddie侠迎上去,他那套“极限剑法”专找序列的破绽,剑尖总在对方招式将发未发时点到。可紫铜侠变招了——三人的阵型突然旋转,从收敛变成柯西序列,这下连破绽都成了陷阱。
+欧阳侠大喝一声,双手拇指快速点算其他手指关节。他在算这个阵的聚点,可算了三息,额头见汗:“不行……他们是完备空间,没有孤立点!”
+葫芦侠忽然笑了。他把葫芦往腰间一挂,不退反进,直接撞进阵里。
+“你疯了?!”欧阳侠惊呼。
+紫铜侠也一愣。按正常套路,这时候该后撤重组,哪有往阵眼里撞的?就这一愣神的工夫,葫芦侠已经贴到紫铜侠身前,然后——他做了个谁也没想到的动作。
+他伸手,在紫铜侠左肩拍了一下,又在他右肩拍了一下,然后蹲下身,在对方左脚鞋面上也拍了一下。
+全场安静了。
+紫铜侠僵在那里。他的完备性证明掌需要严密的逻辑链:如果A,则B,则C……可葫芦侠这毫无章法的三拍,根本不在他的逻辑集里!就像一道题,题干都不符合公理体系,还证什么证?
+葫芦侠趁他卡壳,从他和另一个组员的缝隙里钻了出去,反手一捞——阵旗到手。
+“承让。”葫芦侠咧嘴一笑。
+紫铜侠脸涨得通红:“你这……这不合规矩!”
+“哪条规矩说不让拍肩膀了?”葫芦侠眨眨眼,“师兄,你的武功是完备了,可现实世界从来不完备啊。”
+台下哄笑。程长老咳嗽一声:“葫芦侠组,胜。”
+数理门内斗结束,开勒侠最终被门派派出去参加大会——他擅长流形几何,招数变幻莫测,数理门里没有人能够比他的出招更规整。葫芦侠那套“野路子”虽然出奇制胜,但在严谨性上到底差了一筹。
+三天后,图文馆。
+这地方说是藏书楼,其实大得离谱。一楼是普通典籍,二楼是各门派专著,三楼则是禁区,据说藏着前朝武林秘史。今天为了“十七门论道”,一楼大厅清出一片空地,十七张方桌摆成环形,各派代表已经就座。
+葫芦侠是跟着来看热闹的。他挤在人群里,看见自家开勒侠坐在数理门的桌子后,腰板挺得笔直,面前摊着本手稿。机械门一位瘦小精悍的姑娘,手里托着个刚拼装好、还在微微振翅的木质“机关鸢”。“晚辈机械门七柒侠,请教材料门人兮师兄——”她看向对面这位打扮不凡的青年,脖子上似乎还挂着代表门派道义的项链。“若我这机关鸢的骨架用的是轻韧的云杉木,覆以浸过桐油的素绢,师兄可能从材料上破之?”
+材料门的青年——人兮侠微微一笑,不疾不徐地从随身皮囊中拈出一点银灰色的粉末:“七师妹的选材已是上佳,云杉木轻而韧,桐油防水。可惜……”他屈指一弹,粉末如雾飘散,均匀落在机关鸢的素绢翅膀上。只听一阵极其细微的“嗞嗞”声,那看似坚韧的绢面竟以肉眼可见的速度失去光泽,变得酥脆,最后“噗”地一声,被机关鸢自身振翅的力道带出了几个破洞。
+“此乃朽材散,不伤木骨,专蚀植物纤维与某些胶质。桐油防得住水,却防不住它。”
+七柒侠眼睛一亮,非但不恼,反而露出果然如此的表情。她对人兮侠说:师兄,我的机关鸢已经进入第四阶段的制作了,如果没有人兮师兄的帮助,我还真没发现这个漏洞。我打算用你的名字来纪念,就叫“无人机”,你觉得呢……
+两人你一言我一语,斗得有来有回。葫芦侠看得津津有味,旁边挤过来个人,是他舍友阳睿侠,主修加瓦的,此刻正眯着眼看热闹。
+“你在看什么呢阳睿侠?”葫芦侠捅捅他。
+阳睿侠压低声音,“你看那边。”他努努嘴,指向经管门那边。那边坐着一位经管门代表,一直笑眯眯的,可每当有人展示绝技,她就往本子上记几笔,还不时拨弄手边的金算盘。
+“她在算什么?”
+“算价值。”另一个舍友玉斌侠凑过来,这家伙练的是“愿神气动”,专找气动的路径。“你看,机械门每展示一件样品,他就记下成本;化学门每用一份药剂,他就估算市价。他在算……哪门功夫最划算。”
+葫芦侠咂舌。正说着,那边吵起来了。
+是外文门的于遥侠和数理门的开勒侠。于遥侠一身水绿儒裙,手握一管青玉笔,气质清冷。她方才正阐述武功的韵律与意象,说到招式当如诗文,有意在言外之妙,目光便落到了窗边始终沉默的开勒侠身上。
+“开勒师兄以为如何?”她声音清脆,“数理门武功,是否过于追求确然,失了武道应有的神韵与留白?”
+开勒侠从书页上抬起眼,语气平淡得像在陈述公理:“所谓意在言外,在泛函分析里,对应的是函数空间稠密集的补集。不可言说,只是尚未找到合适的数学语言描述。”
+于遥侠蹙眉,玉笔在指尖一转:“兄台此言,未免将天地灵韵皆视作死物。我这招平沙落雁,取的是秋意萧疏、孤鸿渺远之意境,此中苍凉阔大之感,莫非也能被师兄的方程算尽?”
+“能。”开勒侠放下书,“你那一招,真气运行轨迹是复平面上的亚纯函数,主部在无穷远点有三阶极点。你说的苍凉,是函数在某个扇形区域内的渐近展开式系数为负所产生的心理映射;阔大,是收敛半径大于寻常招式。至于孤鸿的意象,大概来自你函数图像在某个变换下与候鸟迁徙路径的拓扑同伦。”
+图文馆里一片安静。几个文史门弟子张大了嘴,经管门那位打算盘的手也停住了,似乎在想这种意境量化能不能估值。
+于遥侠脸色微白,不是愤怒,而是某种认知受到冲击的眩晕。她深吸一口气,玉笔轻点,一缕肉眼几乎看不见的墨色气劲如游丝般飘向开勒侠面前的书页——并非攻击,更像是一种带着情绪的、文人式的质问。那气劲轨迹飘忽,确如书法中的飞白,难以捉摸。
+开勒侠右手食指在算盘上轻轻一拨,一粒珠子弹起,沿着一条简洁的直线飞出,精准地撞在那缕墨色气劲最浓的某一点。没有声响,墨气如烟消散。
+“你的飞白,是故意在参数方程中引入随机扰动项,模拟笔墨的枯笔效果。”开勒侠看着那粒落回的算盘珠,“想法有趣,但扰动项的协方差矩阵选得不好,导致出现一个本征值接近零的薄弱点。我打的就是那里。”
+于遥侠连退两步,撞在身后的书架上,顶上两卷《诗品》竹简应声滑落。她惊呼一声,却见开勒侠左手一挥,三粒算盘珠呈品字形射出,在空中相互撞击变向,后发先至,在竹简侧面轻轻一托、一旋、一送,两卷竹简便稳稳落回原处,如同从未动过。
+“线性方程组,加旋转矩阵变换。”开勒侠收回手,看向脸色苍白的于遥侠,“于瑶侠,意境很美,但结构不稳。文章如此,武功亦然。”
+于遥侠怔了许久,终于躬身一礼,声音微颤:“受教了。”那并非服输,而是某种更复杂的东西——她一直耕耘的、充满感性的世界,突然被另一种冰冷而强大的语言彻底解构了。
+葫芦侠看着这一幕,忽然觉得,开勒侠那套看似不近人情的“数学语言”,或许本身就是另一种境界的“诗”。文理之辩,是每年武林大会的焦点话题。此事从来没有标准答案,可谓仁者见仁,智者见智。
+“发什么呆呢?”肩膀被拍了一下,是一起来看大会的欧阳侠,“走走,吃饭去。下午还有好戏呢,听说设计门要展示最新的八卦阵,据说至今还没有英雄可破呢……。”
+葫芦侠最后看了眼场中。开勒侠已经坐回位置,重新摊开手稿,仿佛刚才那神乎其技的一手只是随手为之。其他各派代表神色各异,有敬佩,有忌惮,有不忿。
+十七门论道,这才刚刚开始。
+而葫芦侠摸了摸怀里的葫芦。他知道自己离那个舞台还远,他需要慢慢沉淀。
+武林大会的喧嚣如潮水般退去,紫金山又恢复了往日治学练功的节奏。葫芦侠却有些提不起劲儿。他渐渐发现,在这偌大的习武馆里,自己认得的人竟屈指可数。食堂用饭时,周遭三五成群,高谈阔论,而他总是独来独往,获取功法的途径往往只能来源于课堂。就连澡堂里,大家都借着歌声,在氤氲的水汽中交流着各种江湖见闻的交流,而他只是匆匆洗完便走。
+“葫芦侠,你近日怎地有些闷闷不乐?”一日课后,Eddie侠擦着额角的汗,直言问道。
+葫芦侠苦笑:“非也。我只是觉得……这习武馆内卧虎藏龙,各门各派皆有俊杰。大会之上,可我除了咱们数理门这方寸之地,竟与其他人毫无交集。仿佛……白白错过了这江湖。”
+欧阳侠若有所思:“你是说,觉得自己圈子太窄,想多结交些朋友?”
+“正是此意。”葫芦侠叹道,“我见许多人天生便能与人谈笑自如,无论去到哪个门的聚会,皆能如鱼得水。反观我自己,与人搭话超过三句,便常觉词穷,手心冒汗。”
+Eddie侠拍腿道:“这有何难!此乃社交功法生疏尔!。我有一计——”
+他从怀中掏出个小本子,封皮上竟写着《江湖交际初阶导引》。
+“这……这是何物?”葫芦侠愕然。
+“我平日观察所得。”Eddie侠颇有些得意,“我将那些善于交际的同门行为做了归纳建模。你看,第一章,破冰式:可基于共同情境发起对话,如兄台也爱吃瓦香鸡?;第二章,递进式:在破冰后迅速寻找共同兴趣链接点,如看到对方在看蹴鞠比赛,上前就蹴鞠话题后展开交流;第三章……”
+葫芦侠听得头晕,却也觉新奇。欧阳侠在旁泼冷水:“人际交往,贵在真诚,岂能全照本宣科?”
+“先有章法,再求化境嘛!”Eddie侠不服,“葫芦侠,不若你便以此为本,定个‘社牛挑战’之期。譬如,以一月为限,每日需主动与一位陌生同门交谈,内容、时长皆需记录,我与欧阳侠为你督战,如何?”
+“社牛……挑战?”葫芦侠喃喃重复,眼中渐渐有了光。这听起来,竟像是一门新的、需要攻克的心法课程。
+“好!”他握了握拳,“便从明日始!”
+第一日,目标就定在每天吃饭的食堂。葫芦侠深呼吸几下,目光在闹哄哄的人群里扫来扫去,最后停在角落那个独自坐着的身影上。那是数理门的同学,经常大清早在洗漱台旁边碰到,总看他安静地端着木盆,点点头就快步离开,葫芦侠一直没敢开口打招呼。
+他稳了稳有点发颤的手,端着餐盘走过去。“这位同学,这儿有人吗……”他顿了一下,努力让声音显得自然点,“咱们是不是常碰面?在洗漱台。我叫葫芦侠,能认识一下吗?”
+那位同学闻声抬头,是张清秀但有点冷淡的脸。他眼里闪过一丝意外,随即放下筷子,用布巾擦擦嘴,竟然站起来抱了抱拳——这是堂里弟子间比较正式的见面礼。“原来是葫芦兄。我叫贾锐侠,也是数理门的。”他语气挺平和,没有拒人千里的感觉,反而指了指对面的长凳,“一块儿坐吧。”
+葫芦侠心里松了松,坐下开吃。扒拉几口饭后,刚开始的紧张感稍微缓了点,他试着找个话题:“贾锐兄平时好像挺喜欢安静?我看同门里头,有的人天生像火,到哪儿都能跟人打成一片;有的人就像潭水,沉沉稳稳的,更爱自己琢磨。你怎么看这种‘脾气天性不一样’的事儿?”
+贾锐侠听了,若有所思地放下碗:“葫芦兄这话,倒让我想起一门偏门的学问,叫‘心性四路分说’。据说是前朝一位既懂医术又会看相的高人传下来的。他把人的天生脾性,大致按‘神思朝外还是朝内’、‘做事重决断还是重感受’,交叉分成了四种根本路数。比如说有人属‘外路’,喜欢跟人打交道,练功时真气都走得痛快敞亮;有人属‘内路’,爱深思,精神都收在里头,得自己待着才能蓄上劲。”
+葫芦侠听得来劲了:“还有这种说法?那依你看,我算哪路的?”
+贾锐侠仔细看了看他,微微一笑:“葫芦兄你能主动过来搭话,有外路的胆气;可说话时又带着点斟酌试探,不是完全外放,可能更靠近‘风路’——外面看着随和,里头自有章法。至于我嘛,”他自嘲地摇摇头,“大概偏‘山路’这种,闷着不动,没点外力推一把就不带挪窝的。这说法虽然不能全信,但有时候想想,对理解为什么同门练功路子不同、为什么同一招大家悟到的东西完全两样,倒是个挺有意思的参照。”
+“有意思!”葫芦侠觉得新鲜,这“四路分说”居然把摸不着边的脾气差异,说出了几分能琢磨的道理。两人边吃边聊,从天性又扯到不同脾性可能更适合练什么武功,一顿饭吃完,聊得挺投缘。最后贾锐侠还补充道:“其实这说法,跟西域传过来的一套叫‘MBTI’的心性鉴识法颇有相通之处,都是试着把千人千面的脾气,归出几条可说道的理路来。”
+葫芦侠暗自记下:“第一天,目标完成。心得:同门之间,本来就是最容易说上话的。聊聊‘脾性路数’这种话题,能谈得深,还不尴尬。对了,贾锐兄说的那套西域‘MBTI’鉴识法,以后有机会得再请教请教。”
+果不其然,借助MBTI大法,葫芦侠开始结识一群朋友。这个MBTI,真有大本事。
+但是在习武馆这个环境里结识别人还不够,还需要换到陌生的场景去进一步提高交际水平。葫芦侠趁着习武馆课余时间,和欧阳侠,Eddie侠,加上隔壁的鸣君侠,一起到姑苏城游玩。
+姑苏城的春,是浸在湿漉漉的柳烟与软糯吴音里的。葫芦侠随着欧阳侠、Eddie侠及鸣君侠,挤在游人如织的山塘河边,第一次在完全陌生的街市践行他的“社牛挑战”。
+目标一,出现在一座石桥下。一位身着“顺风速递”的年轻驿卒,正从小舟上卸下成堆包裹,额角全是汗珠,神情疲惫。葫芦侠握紧了手中刚在街边买的、用来“练胆”的一小束洁白栀子,花香清冽。
+“去,就现在。”明君侠用胳膊肘碰他,低笑。
+葫芦侠深吸口气,走上前。那驿卒见他挡路,抬头皱眉,却见眼前这光头少年双手将一束沾着水珠的栀子花递了过来,脸涨得通红,话也说得磕绊:“这、这位兄台……送件辛苦了。这花……送你,闻着醒神。”
+驿卒愣住了,看看花,又看看葫芦侠认真的脸,防备之色化作愕然,继而挠头笑了起来,露出一口白牙:“哎哟,小兄弟……这、这怎么话说的。跑码头这么多年,头回有人给俺送花。”他接过花,还真凑到鼻尖闻了闻,疲惫的眉眼舒展开,“谢了啊!你们是……紫金山习武馆的学生吧?一看就是实在人。”
+目标二,在狮子林池畔。几位衣着五颜六色、谈笑风生的大妈正在一株盛放的白玉兰树下徘徊,似乎想合影,却苦于无人帮忙。
+“机会。”Eddie侠小声道。
+葫芦侠再次硬着头皮上前,对其中一位气度最从容的大妈拱了拱手:“这位夫人,晚辈略通丹青速写之术,可否为诸位画一幅作品?”他指了指自己,又指指她们身后的花树。
+大妈们闻言,眼睛一亮。为首那位打量他一番,爽朗笑道:“小侠客有心了!可是要画我们这几个老太婆?好好好,来来,姐妹们,站好!”几人迅速挽手倚树,摆出姿态。
+葫芦侠哪里会丹青,他不过是想主动提出帮她们定格影像。此刻只好临场发挥,装模作样地以手为框,眯眼取景,口中道:“夫人请看左前方飞檐……笑意再盈些……好!‘气韵’已足,此景已入‘心画’,必是佳作!”
+大妈们被他一本正经的术语逗乐,笑声阵阵,拍完后还硬塞给他一包姑苏糕点。“小侠客有心了!下回还找你画!”
+回到习武馆后,葫芦侠身上确实多了几分由内而外的松快。姑苏城的春风与烟火气,似乎吹散了他心头最后一丝畏缩的尘埃。他想起入堂数月,除了必修课业,竟从未主动向传道授业的师长们深谈过自身困惑,这岂非另一种“画地为牢”?于是,他怀着比当初挑战陌生人时更甚的忐忑,给自己的领课师长——李教习,恭谨地写了一封信,简述了自己入堂以来的修习、迷茫与对前路的思索。
+信送出不到一日,一只熟悉的青灰色信鸽便扑棱棱落在他窗台,爪上系着素笺,展开只有一行力透纸背的字:“鼎新楼三层西厢,明日巳时,静候一叙。”
+李教习年约四旬,面容清癯,气质儒雅,与其说是武林高手,不如说更似书院先生。他已在厢房煮好了茶,见葫芦侠紧张行礼,只温和摆手:“坐。你的信我看了。不满足于课业通关,开始思虑长远路径,这是好事。”
+葫芦侠捧过热茶,将自己的困惑一一道出。
+李教习听罢,沉吟片刻,道:“葫芦侠,以你如今之根基与心性,若只求在堂内大小比中位列前三分之一,其实不难。若想拿下那最终的武林大会胜利,按部就班,精熟教材,勤练不辍,假以时日,水到渠成。”
+他话锋一转,神色肃然了些:“然,若志不止于此,欲窥顶尖风光,则需明白,那不再是‘习得’,而是‘修炼’。何谓修炼?是将外传的功法,化为内生的‘本能’;是将有形的‘招式’,融为无形的‘直觉’。这需要时间沉淀,需要海量试错,更需要你跳出‘为学武功而学武功’的桎梏,去更广阔的天地寻找养分。”
+“你信中提到结识各门朋友,此为大善!”李教习眼中露出赞许,“数理功夫是你的心法根基,务必打牢。但绝不可囿于此。秉公习武馆是一个大舞台,需要你不断试错,不断探索。习武馆从不只是练武。真正的顶尖高手,其‘武功’早已超越门派招式之限,乃其全部学识、阅历、心性融合后,自然流淌出的东西。”
+一席话,如鼎新楼外照入的阳光,驱散了葫芦侠心中许多混沌。他忽然觉得,眼前的路广阔了许多。
+此后一段时日,葫芦侠仿佛打开了一扇新门。他不仅更用心领悟李教习的指点,还鼓起勇气,陆续请教了其他几位素有威望或风格独特的师长。或请教武道心境,或探讨不同武学流派的底层逻辑,甚或只是听师长们讲述江湖见闻、前辈轶事。每一次请教,未必都有立竿见影的“功法提升”,却都如点点星光,照亮他认知江湖与武学的不同维度。
+就在葫芦侠的社牛挑战搞得风生水起,在习武馆里跟谁都能聊上几句的时候,布告栏上一张新贴的告示,把他吸引住了。
+“常青藤西部教习队,招人。”
+下面小字写着,这“常青藤”是几个江湖名门大派凑的联盟,时不时会派些得力的弟子,组成队伍,跑到武功不怎么普及的西部偏远地方,去帮那里的乡学武馆开蒙讲课、整理功法,大致两个月。这既是历练,也是积德的好事,江湖上名声挺响。
+葫芦侠心里咚地跳了一下。西部,完全不一样的地方。教小孩练武,更是他从来没干过的事儿。想去见识的念头和怕自己干不来的担心,在心里来回拉扯。他琢磨了好几天,最后,还是这几个月攒下的一点胆子和好奇心赢了。他提笔,老老实实写了申请。
+第一轮面试,在一间挺清爽的屋子里。主考是位看着很利落的女教习。她翻了翻葫芦侠写的申请,问了几个寻常问题,葫芦侠都稳妥答了。最后,女教习看见他随信附上的一卷手抄本,拿过来打开。
+“这是什么?”
+“回教习,这是弟子平时自己瞎说的练功心得,见到听到的杂事,还有对堂里一些招式的粗浅图解。”葫芦侠有点不好意思。这本来是他逼自己用功才弄的练功笔记,他管这叫《葫芦瞎说》。
+女教习翻了几页,里面确实杂,有字有画,有推招式的草图,有练武后的感悟,甚至还记了两句堂里趣闻和食堂哪个菜好吃。画得歪歪扭扭,但透着股认真劲儿。虽然简单,能看出是花了心思的。
+“有点意思,”女教习点点头,脸上有点笑模样了,“不算正经武功秘籍,倒看得出你是个肯留心、也愿意把东西理出来分享的人。你这‘记笔记’的功夫,去教小孩启蒙,说不定挺管用。想过把它弄得更清楚好懂点吗?”
+“正在琢磨怎么改。”葫芦侠赶紧说。
+“行。”女教习合上本子,“回去等信儿,准备二轮叙话。”
+几天后,二轮面试换了个大点的厅堂。这回对面坐着两个人。一个是这次教习队的正领队,孔锝侠,身材笔挺,脸板得挺严,目光跟哈士奇似的,一看就非常认真不好糊弄。另一个是副领队,星侠,是位气质温和,总是带着点笑的美女子。
+孔锝侠问得直接,句句都问在实处:“西部地方苦,东西缺,人也生。要是去了,当地武馆师傅不信你,或者小孩调皮根本不服管,你怎么办?说好的粮食补给要是送不到,又怎么办?”
+葫芦侠照实说,自己武功不算拔尖,但耐心还行,也愿意学愿意干。至于没遇到过的情况,不敢乱保证,只能说一定尽全力。孔锝侠听完,没说话,只是微微皱着眉。
+星侠问得就和气多了。她拿起那本《葫芦瞎说》,翻到一页画着好几个小人以不同角度、或稳或晃地蹲着的草图,旁边还标注着些箭头和重心、支撑的字样,笑着问:“看你这里琢磨了不少根基的姿势。要是让你去教一个从没站过桩、练过下盘的孩子,怎么让他明白扎马步的要义?”
+葫芦侠看着自己画的那些歪歪扭扭的小人,想了想,试着说:“可能……会让他想象自己是一张‘四条腿的凳子’?小孩都坐过凳子。凳子能站稳,是因为四条腿均匀撑在地上,分担了重量。扎马步的时候,两条腿就像凳子的‘腿’,脚掌踩实就是腿着地。不能歪,不能一边重一边轻,心里要想着把身体的‘重量’也均匀分到两条‘腿’上。先不求蹲多低,先求像凳子一样,推一下都不太容易晃。”他一边说,一边不自觉地用手比划着均匀分摊的动作,“这么想,他可能就容易找到那种‘整’和‘稳’的感觉,而不是光觉得腿酸了。”
+星侠听着,眼睛微微发亮,脸上的笑意更深了。她赞许地点了点头:“这个好。不直接讲虚的‘气’和‘劲’,从‘分担重量、站稳当’这个实在的感觉切入,孩子一听就有点明白该往哪儿使劲了。看来你这本‘杂记’没白写,是真能把自己琢磨的东西,转成别人能听懂的话。”
+星侠听着,脸上的笑意更明显了,不住地点头。她转头对孔锝侠说:“孔队,咱们去西部教人,功夫高低在其次,关键是这颗想把东西讲明白的心,和肯动脑筋的劲儿。这孩子底子扎实,平时笔记也记得门清。是个好苗子。”
+孔锝侠沉默了一会儿,目光又在葫芦侠身上扫了一遍,那锐利的感觉似乎淡了点,终于开口道:“星队说的,也有道理。好吧,就给你这个机会。希望你珍惜,也别辜负了西部那些等着启蒙的孩子。”
+三天后,最终名单贴出来了。葫芦侠的名字,在上面。
+告示最后写着:“入选的人,三天后辰时,到‘材料楼’前面集合,互相打个照面,并具体商量去西部的事宜。”
+看着榜上自己的名字,葫芦侠心里又是兴奋,又有点发慌。这跟堂里的小打小闹、出去春游完全不一样了,是要离开整整一个月,肩上担着责任的远行。他好像已经看见了西边陌生的天空,听到了小孩们稚嫩的呼喝声。
+他回到住处,开始收拾行李。把那本《葫芦瞎说》小心地放在箱子最上面,又塞进去几本入门功法,和一厚摞空白的纸。欧阳侠也入选了,他去的是另外一个队伍。
+第三天早上,天刚蒙蒙亮。葫芦侠背好行囊,最后看了一眼晨雾里熟悉的习武馆,转过身,朝着材料楼那边,稳稳地迈开了步子。
+他的路,从这紫金山下,要延伸到那片遥远又陌生的西部天地里去了。这趟旅程,注定不简单。
\ No newline at end of file
diff --git a/docs/novel/第03章.txt b/docs/novel/第03章.txt
new file mode 100644
index 0000000..d8c50f4
--- /dev/null
+++ b/docs/novel/第03章.txt
@@ -0,0 +1,167 @@
+葫芦侠行(第三章)
+壹
+上回提到,葫芦侠入选常青联盟的西部教习队,受令来到材料楼。材料楼矗立在习武馆西北角,楼体以青灰巨岩砌成,沉稳厚重,堂内一应重要物资、典籍副本乃至远行装备皆调度于此。葫芦侠背着行囊一路小跑,大葫芦甩来甩去,心中默念着“辰时集合,万勿迟了”,额角已见细汗。
+待他气喘吁吁踏入楼内指定的“沁心厅”时,却觉厅内气氛肃然,十三道目光齐刷刷落在他身上。长桌周遭已坐得满满当当,唯余末座一个空位。主位上的孔锝侠面沉如水,指节在硬木桌面上不轻不重地叩了一下,声音在骤然安静的厅堂里格外清晰。
+“葫芦侠,”孔锝侠开口,声调并无疾言厉色,却自有一股压人的分量,“辰时集合。你抬头看看,日晷影子现在何处?”
+葫芦侠心头一紧,暗叫不好,匆忙间竟将时辰看错了一刻!他脸上一热,忙躬身抱拳:“弟子……弟子疏忽,看错了时辰,请队长责罚。”
+“责罚暂且记下。”孔锝侠目光如电,扫过在场每一个人,“今日是第一次全队聚议,便有人迟误。望诸位牢记,此去西南边陲,非是游山玩水。路途遥远,情势难料,差之毫厘,或许便谬以千里。一人之行止,关乎全队之进退,更关乎我等对所赴之地孩童的承诺。从今日起,守时,为第一铁则。”
+“是!”众人齐声应道,葫芦侠更是汗颜,喏喏称是,赶紧到那空位坐下,只觉如坐针毡。他悄悄抬眼,数了数厅内人数,连自己在内,正好一十四人。除已知的孔锝侠、星侠外,余下面孔多数仅有些眼熟,或是在堂内某处见过,或是武林大会时有过匆匆一瞥。
+星侠见状,温言打破了略显凝滞的气氛:“既人已到齐,便先互相认个面吧。此番西行,你我十四人便是一体,当同心协力。”
+于是众人依次报上名号、所属。葫芦侠方知,这十三位同袍果然来自各殿各门:
+羽昕侠,来自环生门的大师姐,是武林界女侠客分榜的第一名,精通遁地术与利用地形作战;
+帽圆侠,来自经管门,笑容可掬,身形微胖,一看便知非常精明世事;
+千辰侠,来自机械门,寡言少语,黑黝的皮肤在黑夜里像是能隐身一般;
+三水侠,来自设计门,长于绘画,指间带有洗不净的淡淡墨彩,秀发飘逸;
+南羽侠,来自材料门,气质成熟,有大姐大的风范;
+气玄侠,来自机械门,体魄雄健,声若洪钟,魁梧的身材似要压倒一切;
+人兮侠,来自材料门,此前图文馆中曾与机械门七柒侠论道的那位材料高手,动如脱兔,静如处子;
+七柒侠,来自机械门,同样已在武林大会见过,因为得过去年的第七而获此名;
+于瑶侠,来自外文门,气质婉约,眼神透出锐意,盈盈的笑容却暗含清冷;
+喆航侠,同样来自材料门,赛马出身,家中颇为殷实,马厩里约莫有200匹宝马;
+寒境侠,来自电光门,天真聪慧,平易近人,此番已是她第二次参与常青联盟的计划。
+加上葫芦侠自己,这十四人,可谓荟聚了秉公习武馆“工、理、文”三殿之秀。葫芦侠默数了一下,有四个来自材料门的,四个来自机械门,莫不是来自材料门的孔锝侠和来自机械门的星侠的大力引荐?他不敢多猜测,只是默默观察着这些各路豪杰,都是各个门派的精英,他需虚心学习。孔锝侠略一颔首,沉声道:“好。人已识得,接下来说正事。此行名为教习,实乃拓荒。西南边陲,民风质朴亦彪悍,而武学传承凋敝。我等所赴之六都少年武馆,馆主对我紫金山名头虽有耳闻,却未必全信。故此番西行,需获取信任,再开展工作。”
+“为行事有序,各展所长,现分设各职。”孔锝侠取出一卷早已拟好的帛书,展开宣读。
+“笔墨组,主对外宣介、行程记录、与武馆及本地乡绅文书往来。葫芦侠,我见你提交那卷《葫芦瞎说》,倒有几分将生硬道理化为浅白叙述的巧思,此去西南边陲,正需此能。便由你暂领此组,务必使外界知我队所为所志。”
+葫芦侠一愣,万没想到自己因杂记得了这么个职责,忙起身应下,心中又是忐忑,又隐隐有股被认可的激动。
+“丹青组,主绘制功法图解、沿途风物、乃至武馆所需之图谱。最终集合成册,交予习武馆总库。三水侠,此乃你本行,有劳。”
+三水侠盈盈一礼,淡然道:“分内之事。”
+“体术组,主督导队员日常锻体、行进间护卫安排,并设计适合蒙童的强身游戏。气玄侠,你武功扎实,性情豪迈,此事托付于你。”
+气玄侠抱拳,声震屋瓦:“队长放心!定把那些小猢狲操练得结结实实!”
+“策划组,”孔锝侠念至此,微微一顿,与身旁的星侠交换了一个眼神。这策划组乃是昨日他与星侠商议时,星侠提议增设,言道“世事多变,需有人专司筹划应变”。孔锝侠虽觉有理,却也不以为这等少年弟子能谋算出什么惊天动地的方略,只当是给予历练。他续道:“主筹划沿途及抵达后的各项活动、应对突发之情。于瑶侠,你心思缜密,便由你担纲。人兮侠和葫芦侠辅之,为你副手。”
+三人皆起身领命。葫芦侠注意到,孔锝侠宣布此项时,语气平淡,远不如宣布其他几组时那般着重。星侠则是面带微笑,对于瑶侠轻轻颔首,似有鼓励之意。
+“炊事组,主一行饮食起居、物资管备。帽圆侠,此乃你专长,亦是维系全队士气的要害,交予你了。”
+帽圆侠笑眯眯应下:“定然让诸位吃得饱饱的,有力气教学生!”
+接下来便是班主任职责分配。七柒侠分配至四段教学事宜,协调各组,并为诸弟子之表率。具体蒙童分班,已初步按一段至六段拟定。而人兮侠、葫芦侠则跟随七柒侠一起教授四段课程。
+葫芦侠心想,能与这俩位之前在武林大会见过的高手一起教学,倒是安心不少。
+职分既定,孔锝侠神色更肃,立下三条规矩:
+“其一,西南之地,山高林密,江湖路远。非仅指绿林宵小,虫豸瘴气、陌生路径,皆存险厄。自今日起,凡离队外出,无论昼夜,必两人以上结伴而行,互有照应。不得逞强独行。”
+“其二,”他目光扫过众人年轻的面庞,“尔等在秉公习武馆,是求学的弟子。但一旦踏入苍梧地界,在那些少年蒙童、乡民武师面前,你等便是师长。一言一行,当合乎师道风范。授业当尽心,言行当端谨,遇事当持重。莫要堕了紫金山的名头,更莫要负了那些称你一声先生的孩童。”
+“其三,此番西行,队伍即是一体。令行禁止,一切行动听指挥。然规矩是死的,人是活的。该严肃时不得轻佻,该活泼时亦不必过分拘泥。一张一弛,方是长久之道。”
+三条规矩,条条沉甸甸地压在每个队员心头,却也让人油然生出一股庄严的责任感。这已不再仅仅是堂内的一次任务,而是真正要独当一面了。
+“眼下第一桩要务,”星侠接话道,声音柔和却清晰,“便是制作‘拜帖’与‘荐书’。需让六都武馆及当地乡绅知晓,我辈非是徒有虚名,确有实学可授。笔墨组牵头,丹青组协力,策划组亦可参详内容。需图文并茂,既展我紫金山武学之正,亦显我辈弟子所长。三日内,需有初稿。”
+正事议毕,气氛稍缓。帽圆侠忽然笑眯眯地拍了拍手。只见羽昕侠与寒境侠从侧间抬出一方食盒,打开竟是一大块洁白的、点缀着鲜果的“乳糕”,糕面上以赤蜜写着“福寿”二字。
+“今日,”星侠笑吟吟地看向略显错愕的于瑶侠,“恰是于瑶师妹的芳辰。出门在外,生辰亦不可轻忽。借此机会,一来为于瑶师妹贺寿,二来——”她目光转向全体队员,“今日亦是我‘常青藤西部教习队’正式建队之始。此去一载,愿我十四人,同心同德,如这乳糕,虽原料各异,终融合一体,共担风雨,同尝甘苦。愿此行,不负所学,不负所托,亦不负你我此番相识共事之缘!”
+厅中静了一瞬,随即响起惊叹与欢笑。于瑶侠更是掩口,眼中似有晶莹闪动,显然全未料到在如此严肃的集会上竟有这般温暖惊喜。
+孔锝侠严峻的脸上也微微化开,他举起自己调制的奶茶:“愿我队,旗开得胜。诸君,共勉!”
+“共勉!”十四只杯盏轻轻相碰,清脆声响中,初见的生疏、迟到的尴尬、肩负重任的紧张,似乎都在这一刻消融了几分,一种名为“同伴”的纽带悄然联结。
+葫芦侠握着温热的茶杯,看着眼前这些即将共赴遥远西陲的同袍,心中那点对未知的惶惑,渐渐被一种更坚实的期待所取代。路,就在脚下;团队,已在身旁。
+贰
+沁心厅的议事散了,而真正的征程,才刚刚拉开序幕。七月流火,十四人的教习队离了紫金山,一路向西南行进。车马兼程逾十日,地势渐高,山色愈奇,终是入了黔地。但见群峰如宝剑般直插云霄,时有白练般的瀑布从翠崖间垂落,轰鸣声隔着数里可闻。山路蜿蜒处,偶见苗家侗寨依山而建,吊脚楼掩映在葱茏之中,身着斑斓服饰的山民投来淳朴好奇的目光。空气里弥漫着草木与泥土的湿润气息,与紫金山的恢弘大气截然不同,别有一番灵秀险峻。
+“这便是……贵州了。”葫芦侠扒在车辕边,望着窗外飞速掠过的奇景,只觉心胸为之一阔。腰间的大葫芦随着颠簸轻轻晃动,里面装的“醒神露”似乎也沾染了此间的清气。
+又行三日,方抵达此行的目的地——六都镇。镇子卧于群山环抱的小盆地中,一条清浅的溪流穿镇而过。最大的建筑“六都少年武馆”便坐落在镇东,青石垒砌的围墙,黑瓦覆顶,门匾上的字迹已被风雨侵蚀得有些模糊。
+馆主方潘带着两名年长弟子候在门前。他年约五旬,皮肤是常年风吹日晒的古铜色,双手骨节粗大,布满老茧,眼神锐利如鹰,扫过孔锝侠身后这一群明显带着书卷气、风尘仆仆的年轻人时,眉头皱了一下。
+“常青藤西部教习队,前来拜访方馆主。”孔锝侠上前,抱拳行礼,声音沉稳。
+方馆主并未还礼,只上下打量他,忽然开口道:“久闻紫金山武功讲究根基扎实,条理分明。方某是个粗人,不信虚言,只信拳头。孔队长,可敢搭把手?”
+气氛瞬间凝滞。星侠欲言又止,身后众弟子也屏住了呼吸。这分明是要掂量斤两。
+孔锝侠神色不变,只缓缓将背上行囊解下递给身旁的星侠,上前一步:“馆主赐教,敢不从命。”
+没有客套,方馆主低喝一声,身形如扑食的猎豹般窜出,一记简朴刚猛的“黑虎掏心”直取孔锝侠中宫。这一拳毫无花巧,却快、准、狠,拳风破空,隐隐有闷雷之声,显是数十年外家硬功的火候。
+孔锝侠不闪不避,左臂竖起如封似闭,却不是硬格,而是顺着拳势一粘、一引,脚下步法微妙转动,竟将这凶悍一拳的力量带偏了几分。同时右手五指如钩,疾点方馆主肘后“曲池穴”。这一下应对,守中带攻,化力打穴,正是材料门武功白鹫手中引铁分流的精妙招数,讲究的是对力道、角度的精准控制与材质的理解运用。
+方馆主“咦”了一声,变招极快,拳化掌刀,斜斩孔锝侠脖颈。孔锝侠侧身,掌缘如铁尺般反切对方手腕。两人兔起鹘落,在武馆门前不大的空地上交手十余招。方馆主攻势猛恶,大开大合;孔锝侠则严谨缜密,每每于间不容发之际化解攻势,偶有还击,皆指向关节、穴道等要害,令方馆主不得不回防。
+二十招一过,方馆主忽地收势后退,哈哈一笑,抱拳道:“好!招式严谨,劲力凝练,更难得是这份沉稳心性。紫金山高徒,名不虚传!方才得罪,还请海涵。”他脸上疑虑尽去,换上真诚的热情,“诸位远来辛苦,快请进!”
+第一关,算是过了。众人松了一口气,随着指引向前。武馆后院的厢房果然“简陋”。那是几间颇有些年头的木屋,窗户纸多有破损,屋内除了一张硬板床、一桌一凳,别无长物,空气中弥漫着淡淡的霉味和木头气息。硬板上还密布着小虫子。
+“这便是往后一月的家了。”帽圆侠苦笑着摸了摸冰凉的床板。
+“无妨,自己动手便是。”南羽侠放下行李,环顾四周,“千辰师弟,三水师妹,可否与我一同查看房屋结构,看看何处需加固?羽昕师妹,寒境师妹,我们需在日落前将窗户修补好,山间夜露重。喆航师弟,烦请带领剩下的人清洗床板,除去上面的小虫。”
+大家立刻行动起来,效率惊人,毕竟这事关今晚能不能睡个好觉。而帽圆侠已钻进武馆借用的简陋厨房,开始清点食材,琢磨晚餐。
+忙乱了两个时辰,至日落时分,几间厢房已焕然一新。窗户糊得严实,硬板床上也铺了自带的凉席,虽仍朴素,却洁净干燥,有了家的暖意。帽圆侠用大锅做了西南地区有名的红三剁,众人围坐享用,疲惫稍去,对即将开始的教学也多了几分期待。
+翌日,天未亮,武馆的晨练钟声便将众人唤醒。匆匆吃过集市上买来酱香饼,便到了紧张的时刻——排课。
+方馆主将武馆现有近二百名蒙童召集到练功场。孩子们年纪从六七岁到十三四岁不等,穿着洗得发白的粗布衣裳,小脸大多黝黑,眼睛却亮晶晶的,好奇地打量着这群“山外来的老师”。按照年龄与基础,大致分为六段。教习队需融入武馆原有的晨练、文化课,并增设他们的“特色课程”。
+葫芦侠、人兮侠跟随七柒侠,负责“四段”的三十来个孩子,年纪多在十岁上下。第一堂“特色课”,便是葫芦侠主讲的“侠义精神的来源”——这是策划组商定后,一个听起来颇为“高大上”的课名。
+练功场一角,树荫下。三十几个孩子盘腿坐着,葫芦侠站在前面,努力让自己显得从容。他清了清嗓子,开始按昨晚准备的腹稿讲述:“诸位同……学,”他差点习惯性说成“同门”,赶紧改口,“武林之中,侠义精神代代相传,其常见于口耳相传的轶事话本之中。今日,我们便来谈谈,前代侠客的精神……”
+他讲了江湖侠义精神的兴起。起初还有几个孩子睁大眼睛听着,渐渐便有人开始偷偷扭动身体,交头接耳。待讲到讲了《游侠列传》与唐人传奇的区别时,一个胆子大些的男孩终于忍不住,小声对同伴嘀咕:“这先生讲的啥?真没劲……”
+声音虽小,葫芦侠却听得清清楚楚,脸上微微一热,接下来的话便有些干巴巴了。好不容易捱到下课,孩子们如蒙大赦,一哄而散。葫芦侠站在原地,望着空荡荡的场地,心里像是被那山间的凉风吹过,空落落的,又有些发慌。
+“出师不利啊……”他喃喃道,第一次对自己那套“将道理讲明白”的信心产生了动摇。在这里,面对这些可能识字都不多、每日在田地里打转的孩子,紫金山课堂里那套似乎行不通了。
+当晚的队内小结会上,葫芦侠红着脸汇报了首课受挫的情况。出人意料,遭遇类似困境的不止他一人。于瑶侠的诗词鉴赏课,孩子们对明月松间照的意境毫无共鸣;气玄侠的机关初识课,带来的精巧小模型远不如后山掏鸟窝来得吸引人。
+“这样的问题去年我第一次来的时候,也出现了,”星侠沉吟道,“教学如果脱离了孩子们熟悉的生活与兴趣的话,那就很难俘获孩子们的信任。对于课程的琢磨,务必慎之又慎。”
+“大家对于特色课程,有何想法?”孔锝侠看向大家,目光平静。
+葫芦侠起初并不敢发言,看到同袍们有的提出要举办诗歌节,有的要举办科技节,孔队长皆予以采纳。于是他大胆开口道:“队长队副,我有一个想法。或许我对于侠义精神的教授,可以变成一场‘游戏’或‘比赛’。”
+“游戏?”孔锝侠挑眉。
+“正是。”葫芦侠接过话头, “与其干讲,不如让学生们来试演。我们选取几段简短有趣的武林轶事片段,或本地流传的侠客故事,让他们来模仿、扮演其中人物。就像……就像咱们堂里的小较,只不过比的是演和说。我们称之为试演擂!”
+“试演擂?”星侠眼睛一亮,“这名字好。胜出者,或许可得到参与排演一出完整小戏的机会?甚至,这出戏可以由他们自己参与编创?”
+“对!”于瑶侠点头,“我们发现馆中有几个孩子,特别活泼好动,模仿力强,虽然功夫……嗯,不甚出色,但很有表现欲。或许可以他们为核心。”
+“戏本子呢?”孔锝侠问到了关键,“演什么?我们毕竟不像紫金山的那些戏剧老学究一样是专业的。”
+但是葫芦侠心中早有雏形,脱口而出:“我们可以新编一个故事。比如……《桃源侠影录》?借陶潜公《桃花源记》的架子,但内核换成我们武林的。讲几个厌倦江湖仇杀、寻访世外桃源的游侠,误入一处看似完美、实则失了武人本心的无忧谷,最终醒悟侠之道不在避世,而在入世行侠的故事。既有趣味,也能寓教于乐。”
+他将大致构思讲了,如何设置冲突,人物如何各有特点,最终又如何点题。孔锝侠听罢,沉默片刻,道:“故事尚可,但需切记,不可沦为纯粹嬉闹,须有武德教化之意。葫芦侠,你来负责此事。”
+“是!”葫芦侠心中一块大石落地,又涌起一股新的干劲。
+三日后,“试演擂”在武馆后院开场。消息传出,孩子们都觉得新奇,连一些年纪小的蒙童也跑来看热闹。
+葫芦侠请羽昕侠、三水侠担任擂主,先做示范。选取的片段是《天龙八部》中乔峰聚贤庄独战群雄的经典台词。羽昕侠嗓音清越,将乔峰那份悲愤豪迈演绎得淋漓尽致;三水侠虽然台词记得磕绊,但那股子“虽千万人吾往矣”的气势扑面而来,赢得满堂彩。
+“好!”孩子们用力鼓掌,眼睛发亮。
+“谁来试试?”葫芦侠笑着问。
+起初还有些扭捏,在几块糖果奖励的诱惑下,终于有孩子举手。一个虎头虎脑的男孩上台,学着三水侠的样子,吼了一句,却因紧张破了音,自己先脸红挠头,台下笑成一片,气氛反而活络了。
+葫芦侠特别注意观察那几个五段里看中的捣蛋四人组。他们果然没让人失望,在同伴怂恿下上了台。四人竟有模有样地分起角色,一个装时迁鬼鬼祟祟,一个演徐宁呼呼大睡,另外两个在旁边插科打诨,模仿官兵巡逻,虽然漏洞百出,但那份机灵劲儿、夸张的肢体语言和自带的喜感,逗得所有人前仰后合,连闻讯过来查看的方馆主都忍俊不禁。
+“就是他们了!”葫芦侠哈哈大笑。
+试演擂大获成功,不仅选出了桃花源组的核心演员,更让排戏这件事在武馆蒙童中成了最受欢迎的课程。葫芦侠趁热打铁,当晚便挑灯夜战。将对孩子们的期盼,都悄悄融入剧本。那无忧谷中只知演练花架式、讥讽外界争名逐利的居民,是对脱离实际空谈理想的隐喻。他希望孩子们能有更多勇气跳出当前的环境,经世济民。
+数日后,一本墨迹未干的《新编桃源侠影录》剧本初稿,摆在了孔锝侠和星侠面前。
+叁
+就在“桃花源组”开始利用午休时间秘密排练不久,武馆忽然接到镇上传来的急讯:中央翰林院画院的一位画师奉旨游历西南,采风各地民生,不日将抵达六都,欲为少年武馆的教学景象作素描数幅,以呈御览。
+消息传来,方馆主与孔锝侠皆感意外。此乃宣扬武馆、乃至整个黔地武林少年风貌的良机,却也意味着一份突如其来的压力。
+“平日如何,便如何,不必刻意做作。”孔锝侠对全体队员道,“然需注意精神风貌,将我紫金山弟子与黔地蒙童勤学苦练、昂扬向上之气象,自然展现即可。”
+话虽如此,众人心里仍绷起了一根弦。星侠更是连夜与策划组、丹青组商议,如何在不干扰正常教学的前提下,让几个最能体现特色的课程画面,能更富层次与生气。
+两日后,画师在县学教谕陪同下抵达,身后跟着两名背负画箱的学徒。方馆主与孔锝侠、星侠陪同参观。
+恰是午后,日光正好。一行人先至武馆东厢。此处窗明几净,星侠与于瑶侠正在此教授“西域语言”。只见星侠手持一卷西域商路图,正以清晰的发音讲解几个常用番邦词汇,于瑶侠则在旁以娟秀字迹板书,并引申至江湖走镖、客栈投宿时可能用到的切口暗语。孩子们跟着诵读,语调虽生涩,却满含新奇,仿佛透过语言窥见了山外更广阔的世界。画师微微颔首,学徒铺纸研墨,他笔走轻盈,勾勒出学子侧耳倾听、师长循循善诱的专注场景。
+转到前院,则是另一番火热景象。此处是寒境侠、气玄侠与羽昕侠合力主持的“机关博物”展示场,又名“科技游园会”。几张木桌上,摆放着各式材料样本、简易器械。寒境侠正用“电光门”的巧思,演示水火箭的发射;气玄侠则与几个男孩用木棍、绳索演示“滑轮吊重”,讲解省力之理;羽昕侠更是在一旁铺开“环生图鉴”,指着上面的动植物,讲述如何利用特识别可食用菌类。孩子们围在几处,眼神发亮,不时动手尝试,惊呼与讨论声不绝。吴画师眼中兴致更浓,又画下一幅童趣探奇图。
+移步至武馆侧院的凉棚下,气氛转为静谧雅致。这里是三水侠与七柒侠的丹青绘形”。三水侠正在示范如何以简单的墨色浓淡,表现远山近石的层次;七柒侠则在一旁,指导一个孩子刻画飞鸟的细节。孩子们伏在案前,或临摹山水,或描画眼前所见的花草器械,虽笔法稚嫩,但那份屏息凝神的专注,却透出对“美”的初次虔诚叩问。吴画师再次提笔,捕捉这静谧一刻。
+忽闻后院传来清越弦响,间杂着不成调的叮咚声与孩童欢笑。众人循声而去,只见千辰侠坐在石凳上,怀中抱着一把形制古朴的乐器。他寡言,却以手代口,耐心纠正着围坐一圈的孩子们抱琴的姿势。稍远处,另有几人正在教授更简单的“音律启蒙”,以长短不同的竹筒、装水多少的陶碗,敲击出简单节奏,讲解宫商角徵羽。吴画师含笑,又添一幅“竹石和鸣图”。
+最后,众人来到一间特意收拾出的僻静书房。此处窗户用厚布遮掩了大半,仅留一束光线。桌上,几枚精心打磨的凸透镜、凹透镜片,以及简易的“窥微镜”一字排开。这是显微洞察功的课堂,由几位心细的队员负责。孩子们正轮流通过镜片,观察着羽毛的细绒、花瓣的脉络、乃至清水中的微虫,每每发出不可思议的惊叹,仿佛第一次发现眼前平凡之物竟内藏乾坤天地。吴画师观此,沉吟片刻,终也提笔,记下这“见微知著”的一幕。
+正画着,馆外一阵马蹄声,又一骑快马驰来。马上跳下一人,正是秉公习武馆派来巡视、指导教习工作的彤教习。她年约三旬,气质干练,与孔锝侠、星侠见礼后,便安静地立在一旁观看。待吴画师停笔歇息时,她才上前,与画师低声交谈几句,又对孔锝侠、星侠道:“吴先生夸赞,此间少年,教习有方。画面已得神韵,稍后补景即可。”
+众人闻言,心头微松。彤教习又快速巡视了各教学点,对炊事组的帽圆侠叮嘱了几句饮食卫生,对负责医药的羽昕侠询问了常备药品,最后召集全体队员简短交代:“中央画师来此,是肯定亦是鞭策。守住平常心,展现真风貌,便是最好。余下时日,我亦会在此盘桓两日,若有疑难,可随时寻我。”
+在彤教习的坐镇下,后续两日的教学与素描进展顺利。吴画师完成了包括多幅素描。临行前,他对方馆主、孔锝侠道:“黔地山高路远,我将把此稿带回京城,必能令圣上及诸公,睹西南武林少年博学多识、不拘一格之勃勃生机。”
+送走画师与彤教习,众人才算彻底松了口气。连续数日的紧绷,让年轻的教习队员们也感疲惫。孔队长见状,大手一挥:“明日放半天假,咱们去咱们六都的后山‘小洞沟’松散松散!”
+小洞沟是镇后山涧汇聚成的一处清潭浅滩,水极清澈,可见底下的卵石。水中鱼儿皆若空游无所依。卸下“先生”的包袱,少年心性便显露无遗。气玄侠第一个脱了鞋袜跳进浅滩,大呼痛快。南羽侠、羽昕侠等女弟子也挽起裤脚,在溪边嬉水。喆航侠捡起一片扁石,手腕一抖,那石片便在水面连跳七八下,划出一长串涟漪,赢得一片喝彩。
+“打水漂?这个我在行!”孔锝侠也来了兴致,捡石试手。只见他将石子飞快掷出——石片欢快地在水面弹跳,直抵对岸。“好!”众人叫好。于瑶侠、三水侠等人也加入进来,一时间水花与笑声齐飞。
+葫芦侠玩得兴起,又见水面宽阔,忽然想起一法。他解下腰间葫芦,拔掉塞子,将里面所剩不多的“醒神露”倒掉,仔细洗净,又灌入半壶溪水,塞紧。然后走到上游,将葫芦轻轻放入水中。
+“葫芦兄,你这是作甚?”帽圆侠好奇。
+“看我的‘葫芦舟’!”葫芦侠笑道,手指暗运内劲,在葫芦底一按。只见那葫芦在水中猛地一旋,尾部竟喷出一股细小水流,推着它歪歪扭扭却执着地向下游漂去,竟有几分船模的样子。这是他闲时琢磨的小玩意儿。
+“妙啊!”千辰侠、七柒侠等机械门弟子围过来,兴致勃勃地讨论起如何改进。
+玩水之后,众人躺在溪边晒得温热的巨石上,看白云苍狗,听流水潺潺。山风拂过,带着草木与水的清新。这一刻,没有教案,没有课业,只有纯粹的放松与同伴间的笑闹。葫芦侠觉得,连日来的疲惫与紧绷,似乎都被这清澈的溪水洗去了不少。
+从小洞沟回镇,顺路完成了计划中的一次家访。对象是“四段”中一个名叫阿鑫的男孩家。家在镇子更边缘的山坡上,木屋低矮。阿鑫父母在外务工,家中仅有他与年迈奶奶相伴。见老师们来访,二人很是局促,忙不迭地擦拭凳子,倒上粗茶。
+交谈中得知,送阿鑫去武馆,是家里咬牙做的决定。“咱山里人,没别的出路。学点拳脚,将来就算不能像方馆主那样开馆授徒,去城里大户人家当个护院,或者走镖行混口饭吃,总比一辈子窝在山里强。”阿鑫奶奶搓着满是老茧的大手,眼中是对儿子殷切的期望,也有一丝不易察觉的无奈与担忧。
+阿鑫则兴奋地向奶奶展示今天刚学的“游侠”步伐,虽然稚嫩,却虎虎生风。奶奶看着,脸上笑开了花。
+回武馆的路上,夜幕已降。山间的夜,黑得纯粹,也静得深沉,只有脚步声与虫鸣。葫芦侠想起阿鑫奶奶的话,心中感慨。走出大山的路,对这里的孩子们来说,注定比紫金山的石阶更陡、更漫长。他们渴望成为武林明星的梦想背后,是一家人的孤注一掷。自己这短短一月的教习,真能给他们带去些微不同吗?这问题沉甸甸的。
+“看,星星。” 走在前面的寒境侠忽然轻声说。
+众人抬头。只见深邃的墨蓝天幕上,银河如练,横贯天际,无数星子璀璨闪烁,密密麻麻,仿佛伸手可摘。这是在紫金山,乃至葫芦侠的家乡泉州都未曾见过的、如此纯粹而浩瀚的星空。没有光污染,只有自然最原始壮丽的馈赠。
+“真美……”于瑶侠喃喃道。
+千辰侠仰着头,手指下意识地在空中虚点,仿佛在测算星辰的方位。葫芦侠静静看着,白日里的喧嚣、家访的感慨,在这一刻似乎都被这无边的静谧与璀璨洗涤、沉淀。他想起自己科学课上曾写给孩子们的话“仰望星空,脚踏实地”。此刻仰望这星空,方知宇宙之无垠,个人之渺小。而脚踏实地,便是做好眼前的每一件事,哪怕只是教好一堂课,排好一幕戏,回答孩子一个幼稚的问题。
+星空之下,前路漫漫,但心中那点教书育人的微光,似乎与这漫天星辉产生了某种共鸣,变得坚定起来。
+肆
+平静的日子没过几天,一场突如其来的风波席卷了六都镇——一种被称为“马拉病毒”的疫病,随着往来商旅悄然传入,发冷发热,症状凶险。
+孔锝侠因前几日去镇上采买教学用具,不幸最早中招。一夜之间高烧不退,浑身打颤。羽昕侠紧急诊断,确认是此症。情况危急,需立即送往镇上仅有的那家医馆隔离治疗。
+“我去送!”帽圆侠二话不说,与气玄侠一起,用门板做了副简易担架,冒着清晨的寒露,将已陷入半昏迷的孔锝侠抬往镇上。星侠暂代队长之职,面色凝重,立刻下令:全队服用羽昕侠配制的预防药汤,武馆内外加强洒扫消毒,暂停所有需密切接触的活动,教学改为小组分散进行。
+队长倒下,疫病威胁,让队伍的气氛骤然紧张。屋漏偏逢连夜雨,内部矛盾也在这压力下爆发。
+起因是“体术课”的安排。气玄侠认为,疫病时期更需加强锻炼,增强体魄抗病,主张恢复较大运动量的游戏。而负责“四段”具体教学的七柒侠则认为,孩子天性跳脱,近日因排戏、疫病等事,课堂纪律已有所松懈,此时应稍加收敛,以静心、规矩的练习为主。二人各执己见,在饭堂争执起来,越说越僵。
+“你那是死板!把孩子都拘傻了!”气玄侠脾气火爆,拍案而起。
+“你那是莽撞!不顾实际情况!”七柒侠也动了气。
+“不服?出去比划比划!”气玄侠瞪眼。
+“怕你不成!”
+两人竟真个来到后院空地,动起手来。气玄侠力大招沉,七柒侠则胜在招式精巧、对器械运用灵活。十余招后,七柒侠寻个破绽,用扫帚杆别住气玄侠脚踝,借力一拨,将他摔倒在地,随即跨步上前按住。
+“够了!”闻讯赶来的星侠一声清喝,面色沉静,却自有一股威严。两人悻悻分开。
+星侠没有立刻斥责,而是将全体队员召集到孔锝侠空着的主位前。“队长病倒,疫病在外,我知道大家心里都绷着弦,有火气。”她目光扫过所有人,“但越是这样,越要记住我们来此的初衷,记住我们是一个队伍。气玄要强身,没错;七柒要规矩,也没错。错在只想自己那一套,忘了互相商量,忘了最终目的是教好孩子。”
+她顿了顿,语气放缓:“队长不在,我们更需拧成一股绳。有分歧,坐下说,道理越辩越明。动手,解决不了问题,只会制造问题。你们二人,可服气?”
+气玄侠与七柒侠对视一眼,各自脸上都有些讪讪,终于拱手向对方致歉,也向星侠及全体队员认错。
+“既如此,今日之事,翻篇。往后教学安排,体术组与各段负责教员需每日商议,结合实际情况定案。现在,各自回岗位,做好分内事。”
+经此一事,队伍内部反而更添了几分凝聚力与纪律性。然而,连续的奔波、教学的压力、疫病的阴影,再加上西南潮湿闷热的天气,不少队员的身体开始出现状况。有人水土不服腹泻,有人长了热痱,葫芦侠也因连日熬夜修改剧本、操心排练,嘴角起了燎泡,喉咙肿痛。
+伍
+就在这内外交困、人人疲惫的当口,武馆又来了一位客人——秉公习武馆的资深张教习。他每年都会抽时间巡视各支教习队,今年便来到了六都。
+张教习年近五旬,笑容和煦,穿一身半旧的青衫,毫无架子。他到达时,并未惊动太多人,只在孔锝侠陪同下,静静地在武馆各处观看教学。
+他看气玄侠带着孩子们在树荫下玩“梅花桩”步伐游戏,看于瑶侠在教年龄小的孩子唱“节气农事拳歌”,看千辰侠、南羽侠在凉棚下指导大孩子做“水轮车”模型,也看了葫芦侠领着桃花源组在角落认真对词、走位。
+傍晚,他将全体队员召集到后院。没有长篇大论的训导,只是像拉家常一样,说起他年轻时第一次带队远赴漠北教习的经历。
+“……那时条件比这里苦得多,喝的是苦咸水,睡的是地窝子。队里也闹过矛盾,也有人病倒,也想家。”张教习声音平和,带着回忆的悠远,“当时带我的老队长说,教习教习,教的不仅是武功学问,更是心性。对孩子们如此,对我们自己,更是如此。看到困难,是掉头回去,还是想办法迈过去?看到同伴跌倒,是扶一把,还是绕开走?看到孩子眼里的光,是觉得自己了不起,还是感到责任重大?”
+他目光温和地扫过一张张年轻而带着倦色,却依然明亮的眼睛:“我看了你们一日。纪律虽有波折,但已重归井然;教学虽有磨合,但已渐入佳境;孩子们对你们,是真心亲近喜爱。这便足够了。记住,我们来此,不是要在一月内将他们教成绝世高手,那是痴人说梦。我们是要在他们心里,种下一颗种子。可能是对更广阔世界的好奇,可能是对某种技艺的兴趣,可能是对侠义二字的懵懂理解,甚至,可能只是让他们觉得,学习、练武,也可以是件有趣的事,山外的先生,是亲切可敬的。这颗种子何时发芽,能长多高,我们不知道。但我们种下了,这便是意义。”
+一席话,如春风化雨,润入心田。多日来的疲惫、焦虑、自我怀疑,似乎都被抚平了些。张教习又与队员们交流了许多具体教学、与孩子沟通、与当地人相处的细微心得,毫无保留。
+张教习的到来,如同一剂强心针,又如一阵清风,驱散了队伍上空的阴霾,也让众人对“教习”二字,有了更深沉的理解。
+时光在忙碌中飞逝。孔锝侠在众人的悉心照料与羽昕侠的医治下,病体渐愈。他清瘦了些,但目光依旧锐利,看到井井有条的武馆和状态回升的队员,眼中露出欣慰。
+离别的日子终究要来。教习队决定,在离开前一晚,于武馆练功场举办一场小型的教学成果观摩会,邀请方馆主、武馆师傅、部分镇民及全体蒙童观看。
+那是个晴朗的清晨,练功场四周点起了火把。首先展示的是体术游戏,孩子们分组演示了“山林追踪”、“巧过梅花桩”等游戏,动作灵巧,配合默契,欢声笑语不断。
+接着是巧手作品展示,孩子们穿上自己用各式材料编织的衣服,扮演角色唱歌舞蹈,好不快活。
+然后,便是重头戏——《新编桃源侠影录》的首次正式演出。
+没有华丽的戏台,没有精致的戏服,只有后院那简单的布景和孩子们身上改制的衣衫。但当“捣蛋四人组”扮演的游侠,用夸张的语气诉说对江湖的厌倦、对无忧谷的向往时,台下响起了会心的笑声。当他们进入无忧谷,被那些玄虚的道理绕晕时,笑声更甚。
+然而,当剧情推进,游侠们发现无忧谷的虚幻,经历内心的挣扎,最终由游侠之首说出那段关键台词时,场中渐渐安静下来。
+“……俺们原以为,躲开打打杀杀,就是自在。可到了这儿才明白,光顾着自己自在,看着谷外的人受苦受难装作没看见,那算啥好汉?功夫练了,不拿去帮该帮的人,跟这谷里只会比划样子的把式有啥区别?侠字咋写?一个‘人’字边,加一个‘夹’字,那就是说,得是个人,还得敢夹在中间,管那难管的事,帮那该帮的人!这桃源,俺们不待了!这江湖,俺们回去了!”
+台词质朴,甚至带着土音,却因那份全情投入而显得铿锵有力。孩子们或许不能完全理解其中深意,但他们演得认真,看得也认真。
+演出在欢笑声中落下帷幕。掌声响起,并不特别热烈,却持续了很久。方馆主站起身来,走到场中,先对孩子们点了点头,然后转向孔锝侠、星侠及全体教习队员,抱拳,深深一揖。
+“方某……代六都武馆,代这些孩子,谢过诸位先生!”他声音有些沙哑, “这一个月,孩子们学了新把式,玩了新游戏,懂了新道理,更难得的,是眼里那股子精气神,更亮了!”
+他顿了顿,看着那些在卸妆、打闹,脸上还带着油彩和兴奋的孩子们,缓缓道:“不知道他们中间,将来能有几个真成了气候,走出这大山。但有你们来过这一趟,在他们心里留了这么个念想,留了这么点不一样的东西,我觉着,就值了!紫金山,名不虚传!”
+孔锝侠、星侠带领全体队员郑重还礼。葫芦侠站在人群中,着孩子们无忧无虑的笑脸,看着身旁同样眼眶发热的同伴,一个月来的所有辛苦、焦虑、挫败、争执、病痛,在这一刻,都化为了沉甸甸的满足与不舍。
+翌日清晨,告别时分。孩子们早早聚在武馆门口,许多镇民也自发前来。没有太多言语,只有深深的鞠躬与含泪的挥手。马车启动,缓缓驶离。葫芦侠回头,望着晨雾中渐渐模糊的武馆轮廓,望着那些用力挥动的小手,心中默念:
+再见,六都。再见,孩子们。
+也许我只是你们人生中一个短暂的“何老师”,但你们,却是我江湖路上,永不褪色的一段“侠影”。
+山路蜿蜒,载着十四颗被淬炼过的心,驶向来路,也驶向更远的未来。
\ No newline at end of file
diff --git a/docs/novel/第04章.txt b/docs/novel/第04章.txt
new file mode 100644
index 0000000..616ef8d
--- /dev/null
+++ b/docs/novel/第04章.txt
@@ -0,0 +1,157 @@
+葫芦侠行 第四章
+秉公习武馆的新一学期,在九月的秋风中开始了。
+从常青藤西部教习队归来的葫芦侠,身上多了些不一样的东西。几个月的风尘仆仆并未让他显得疲惫,反而那双眼睛里,沉淀下了一种过去少有的、清醒笃定的光。若说离开前,他琢磨数理武功还带着点野路子的好奇,那么此番归来,这份好奇已被淬炼成了一种清晰的确信。
+数理,从来不是某一门、某一派的独家秘术。它是解开所有武功共通的、沉默的锁的那把钥匙,是托起千般变化、万种招式的无形地基。 无论你最终选择剑、刀、拳、脚,还是机关暗器,若想登堂入室,而非终生在门槛外徘徊,都绕不开对这份基石的认知。
+这个念头如野火在他心中灼烧。他不仅要自己明白,更要让紫金山、乃至让更多还视数理为“无用奇技”的同道都明白:修炼数理,并非不务正业,恰恰是修炼一切正业最应优先务好的本业。
+归来的第三日,葫芦侠便不再等待。他怀揣着这份炽热的念头,开始接连拜访数理门中几位德高望重却深居简出的师祖。他不再仅仅请教具体的难题,而是试图向他们描绘一幅图景——一幅以数理为共通血脉,重新连接武学各脉,激发全新可能的未来图景。
+他讲得恳切,甚至有些笨拙的执拗。讲到关键处,眼见师祖面露沉吟,他便抢着拿起扫帚,跑去打扫庭院,只为能“顺便”多留片刻,把心中翻腾的想法说得更透。没人知道那些安静的庭院里,一老一少具体谈了些什么。只知在某次长谈之后,当葫芦侠从那位以严谨著称的师祖院中走出时,手中已多了一枚沉甸甸的乌木令牌。
+令牌上,是师祖亲笔镌刻的八个朴拙大字:“准设辅堂,试行一期”。
+葫芦侠有了办理堂口的准许证,他命名为“脑学数堂”。
+所谓脑学数堂,它不止是一间讲堂,更愿成为一座灯塔、一处熔炉、一道桥梁。它要为门内所有感知到这份道之重要,却苦于无处深研、无人共论的同道,开辟一方纯粹坐而论道的净土;更要向堂外尚未发掘数理之美的人士证明,那隐藏在招式内数的筋骨,才是支撑起一切武学奇迹最可靠的力量。
+有了令牌,葫芦侠做的第一件事便是寻人。他第一个找上的,是羽昕大师姐。
+羽昕侠正在药庐整理新晒的草药,听葫芦侠说明来意,这位素来沉静的女子放下手中的三七,擦了擦手:“你是说你想要场地?地上武馆各处都已有主,且习武需敞亮,数理研讨却宜静。我倒有个去处——只是不知你敢不敢用。”
+“何处?”
+“地下。”
+原来,羽昕侠早年习医时,为探究某些草药喜阴畏光的特性,曾在其师指导下,于后山一处岩壁下开辟过数间石室。后来她机缘巧合,得了一卷地行术残篇,虽不擅攻伐,却在勘探地脉、开凿甬道上别有心得。这些年,她陆续在武馆地底深处,以巧妙手法开凿出了一个颇为可观的地下网络,本是用作储藏特殊药材、或是闭关静修之用,从未示人。
+葫芦侠随羽昕侠从药庐后一处隐秘入口下行,初极狭,才通人;复行数十步,豁然开朗。但见内部形成数个大小不一的洞室。最大的主室有厅堂大小,岩壁干燥,竟无寻常地穴的潮湿之气,反而有微风流动——细看之下,岩壁高处有细密气孔与地面相通,设计精妙。另有数间小室,或方或圆,静寂无声。
+“好!好一处别有洞天!”葫芦侠抚掌赞叹,“师姐真乃神工!”
+羽昕侠微微一笑:“此地本是私用,既然你有心做这番事业,便借与你。只是有一条——须得保持洁净肃静,不得喧哗嬉闹,更不可于此吃KFC。”
+“自然!数理研讨,正要静室清心。”葫芦侠郑重一礼,“师姐大德,葫芦侠铭记。”
+场地既定,葫芦侠又寻到三水侠。这位以丹青妙笔闻名的侠客,正在凉棚下指导几个有天赋的孩子画竹。听罢葫芦侠的构想,三水侠沉吟片刻,道:“你这数堂,所求的也是窥见天地规律背后的神韵,只是……设置在地下石室,昏暗压抑,若不加布置,久处其中恐陷入困顿。”
+“正要请教三水姐。”葫芦侠眼睛一亮。
+三水侠放下画笔:“此事交予我。岩壁为纸,光影为墨,我自有主张。”
+三日后,当葫芦侠再次踏入地下主室时,几乎不敢相认。但见原本灰褐色的岩壁上,被颜料绘上了巨幅壁画;内部则依次是浑天仪转动、《九章算术》书卷展开、祖冲之计算圆周率的画面。画风古朴大气,人物栩栩如生,更妙的是,三水侠巧妙利用了岩壁的凹凸和夜明珠的光晕,使画面在不同角度观看时,竟有明暗流动之感,仿佛活了过来。
+“这……”葫芦侠震撼无言。
+“数理之道,上应星辰,下合地理,中通人情。”三水侠立于画前,轻声道,“我作此画,是想让每个踏入此堂的人,抬头见星河浩瀚,环视见先贤智慧,知自己所求之道,源远流长,非是无根之木。如此,方能在枯燥推演中,守住心中一点光明。”
+葫芦侠深施一礼,一切尽在不言中。
+于瑶侠的赠书,则是另一番情义。这位擅长修订武林秘籍、精通多门外邦语的女子,听葫芦侠说起数堂缺基础典籍,二话不说,三日后便带着两名杂役弟子,抬来了三口沉甸甸的樟木箱。打开一看,里面整整齐齐码放着数百卷书册,有新有旧,甚至还有数卷于瑶侠亲自整理翻译的西域数理典籍手稿,涉及三角、对数等新鲜学问。最珍贵的,是箱底以油布包裹的十卷《武经算要》,乃前朝某位隐退的大将毕生钻研兵法与数理结合的心得,外界早已失传,不知于瑶侠从何处寻得。
+“这些书,放在我处只是蒙尘。赠予数堂,或能启迪后来者,也算不负它们承载的智慧了。”于瑶侠说得轻描淡写,葫芦侠却知这份礼有多重。
+帽圆侠的帮助,则体现在人脉上。这位负责全馆伙食、看似整日围着灶台转的汉子,实则人缘极佳,与武堂内外许多退隐长老、客卿教习都保持着良好的关系。他亲自下厨,做了几道拿手药膳,分别拜访了数位在各自领域颇有造诣的长者。不出几日,便有风声传来:藏书阁的守阁老人答应开放部分算学典籍的借阅权限;观星台的刘长老允诺,每月可安排数堂弟子前往观星一次,讲解历法天象;甚至有位早已不问世事的退隐馆主,托帽圆侠带来口信:“若有所疑,每月十五月圆之夜可至校风碑,但只限三个问题。”
+葫芦侠心知,这每一份允诺背后,都是帽圆侠以真心实意换来的善缘。
+而机械三人帮——七柒侠、气玄侠、千辰侠的加入,则给数堂带来了最实际的活力。这三位精擅机关器械的好手,不仅自己时常来地下石室,更带来了整整十二名对机关术感兴趣的年轻弟子。这些弟子原本在巧匠会上便已接触过基础物理原理,如今被引入数堂,听葫芦侠以更系统的方式讲解其中数理,个个如饥似渴。他们也将机关实践中遇到的具体难题带来,比如上周,来探讨如何提高机械人的自由度,让原本偏于理论推导的数堂,瞬间充满了务实与探究的火热气息。数理门与机械门,在这地下石室中,开始了真正意义上的友好交流。
+至此,脑学数堂的骨架,便在众人扶持下,悄然立了起来。没有大张旗鼓的仪式,没有喧哗热闹的庆贺,只有一群怀揣相似理念的人,默默将一件看似不务正业的事,当作正事来做。
+葫芦侠将那块乌木令牌,恭敬地悬挂在主室入口。令牌之下,是他亲手刻在岩壁上的一行字:
+“格物致知,数以究理;武以载道,道法自然。”
+这十六个字,便是脑学数堂全部的理念与追求。
+数堂初立,需纳新血。然葫芦侠深知,数理之道,入门不易,枯燥艰深,若广为招揽,恐应者寥寥,反损士气。他思考再三,将目光投向了他所在的红灰青蟹。
+葫芦侠找到现任的会长术好侠,说明来意,希望能在蟹会中争取一个提案,允许红灰青蟹的成员,可自愿同时获得脑学数堂的学额,参与数堂的研讨与课程。
+术好侠挠了挠头:“葫芦兄,你的想法是好的。只是咱们青蟹的规矩,凡涉及会务变更,须得半数以上成员赞同方可。我怕会中兄弟姐妹,对你这数堂……”
+“无妨。”葫芦侠早有准备,“只需给我一次在月会上陈述的机会便可。”
+月会那日,地下石室主厅被临时布置成会场。数十名青蟹成员或坐或站,交头接耳。葫芦侠走到中央,没有长篇大论,只是拿出一叠写满算式的草纸。葫芦侠将其分发给前排的人传阅:“这是一套根据个人身高、臂展、腿长、日常发力习惯,推算其最适合的基础拳架步伐的简算法。乃我于常青藤时,利用数理方法琢磨所得。虽不完善,但或许能帮某些总觉招式别扭的同仁,找到更贴合自身的起手式。”
+堂内众人顿时提起兴趣,接过草稿纸传阅了起来。
+葫芦侠又开了口,声音不高,却清晰传入每个人耳中:“诸位同聚青蟹,皆因不甘囿于陈规,心有好奇,目向八方。我成立数堂,非为取代拳脚功夫,更非让人人都成算数先生。所求者,不过是为那些看似无用的为什么,找一个可能的答案。数理,也许正式武林的基石。它或许不能让你立刻成为一流高手,但却可能让你明白,高手的一流之处,除苦练外,是否还有别的道理可循。”
+他顿了顿,目光扫过众人:“数堂不设门槛,不计考较,来去自由。愿者,可系此绦。”他举起一条在红灰青绦基础上,于末端多系了一枚小小木制算筹坠子的新绦带,“与我等同探数理之美。”
+现场安静了片刻。然后,御寒侠——这位红灰青蟹的创始人之一,率先站了起来。她素来沉默寡言,精理御寒衣物,在会中颇有威望。她走到葫芦侠面前,接过那条新绦带,仔细系在腰间。
+紧接着,术好侠也站了起来。这位元老擅长各种民间奇术,哈哈一笑:“有趣!算我一个!我倒想一探究竟,我苦学多年的数理门,究竟能与其他武功有怎样的贯通之处。”
+两位元老带头,加之葫芦侠展示的演算纸确实勾起了许多人的兴趣,陆陆续续,竟有超过六成的成员上前领取了新绦带。提案,通过了。
+葫芦侠心中一块大石落地。他知道,有了红灰青蟹这群本就对新知充满好奇的生力军,数堂便不愁没有同道了。
+招新会定在三日后,于武堂西侧举行。消息不胫而走,除了青蟹原有成员,也吸引了不少其他秉公习武馆新来的学员前来看热闹。
+葫芦侠坐镇活动部的摊位前,他今日的目标,是希望为脑学数堂的核心研讨小组,物色几位有扎实数理基础或特殊天赋的成员。而御寒侠作为他的搭档,也与他一道招募新人。这个小组将承担更深度的研讨与未来的教学辅助工作,宁缺毋滥。
+各部摊位前人头攒动。葫芦侠注意到几个特别的应征者。首先来的是一位面色发黑、眼神却异常专注的年轻弟子,自称“甲亢侠”。他原在另一个以招式严谨、规矩森严著称的习武堂学艺,因觉其武功套路过于死板僵化,仿佛照着模子刻人,心生厌倦,听闻红灰青蟹风气开放,特来投奔。交谈之下,葫芦侠发现此人对图形、结构有着异乎寻常的敏感和记忆力,谈及复杂招式拆解时的空间想象与逻辑推演,条理清晰,令人侧目。葫芦侠心中暗喜,这正是数堂需要的人才,尤其对于几何、立体推演大有裨益,当即将其招入。
+第二位引人注目的是位气质略显孤高的青年,名叫“可可侠”,自称来自紫金山北麓隐居,言语间对江湖俗务不甚热衷,唯独提到数理大道时,眼中会放出光来。她展示了一种自己琢磨的、用于快速计算土地面积与赋税分摊的简捷算法,其核心思想竟暗合刘维尔定理之雏形。葫芦侠大为惊讶,与之深入探讨,发现此人在数理上确有独到天赋,只是缺乏系统指引,许多想法还停留在经验层面。葫芦侠如获至宝,邀其加入,可可侠略一沉吟,便也颔首同意。
+第三位薄文侠,则完全是另一番风貌。此人来自东北富人庄,但是不愿继承家业,独自一人在江湖上以替人代写书信、诉状甚至战帖为生,言辞犀利,文采斐然。他来到摊位前,不说自己会什么,只随口吟了半阕词,其中竟巧妙嵌入了“圆周率”的前十位数字。葫芦侠讶然失笑,细问之下,方知此君早年曾得异人传授,精通筹算,尤擅以诗词歌赋记忆复杂数据与公式,堪称奇才。他表面玩世不恭,实则内秀其中,正是葫芦侠想找的、能将枯燥数理以生动方式传播出去的人。葫芦侠当即力邀其加入。
+第四位是位娴静女子,名唤书宣侠。她丹青妙手,尤其擅长工笔人物,在武堂内小有名气。令人称奇的是,她还通晓心画之术——即通过观察他人作画时的笔触、用色、构图偏好,揣摩其心境情绪,常为一些心有郁结的师兄弟姐们做画疗,舒缓心绪。她希望加入青蟹,开拓绘画题材,也愿以己之长,为同门调节心绪。葫芦侠和御寒侠对视一眼,收下了这位女侠。
+第五位陈湘侠,则是个风风火火的少年,一心追求速度,轻功已有相当火候,尤擅水上漂一类长途奔袭的功法。他加入的理由颇为直接:“我听人说,数理门能算风阻,能解水流,能知如何跑得更省力更快!我要学这个!”葫芦侠大笑,允他加入,心想此子若能以亲身实践验证数理,倒也是活教材。
+而这时候,一位文弱书生缓缓踱步进入会场。是一位名唤圣文侠的沉稳青年。他手里捧着一本密密麻麻的统算簿。他出身于专精数据的统算阁,对各类统算之学钻研到了痴迷的境地。来到摊位前,他一本正经地向葫芦侠阐述了自己的武学理念:“世人比武,只看眼前一招一式的输赢,那是孤本;而我所求的,是收集千百次交锋的记录,以大样本之法统筹,于茫茫随机的残差之中,拟合出一条预测破绽的回归之线。”葫芦侠听罢,心中不禁掀起惊涛骇浪。这等能从混沌纷乱的江湖万象中,精准剥离出底层规律,甚至能为武功套路做机制检验的奇才,简直是脑学数堂梦寐以求的定海神针。一旁的红利侠当场拍板力邀,将其定为数堂量化推演的骨干。
+招新本顺利进行,忽闻会场外传来一阵惊呼与犬吠。众人循声望去,只见一道迅捷身影,骑着一头黑色德牧,如风般冲破维持秩序志愿者的阻拦,径直闯入会场中心。那犬极高大,皮毛光亮,顾盼间自有威仪。犬上骑者,却是一位身着暗青色劲装、脸覆面纱的女子,只露出一双清冷的眼眸。
+女子对周遭的骚动恍若未闻,利落地翻身下犬,那巨犬便安静地蹲伏在她脚边。她目光扫过全场,最后落在红灰青蟹的旗帜上,径直向摊位走来。所过之处,人群不由自主地分开一条路。
+走到近前,女子也不言语,伸手入怀,竟掏出一个不大的瓷瓶,拔开塞子,在众人惊愕的目光中,仰将瓶中液体饮下大半。然后,她面不改色地将瓶子递向葫芦侠,声音透过面纱,带着一丝沙哑的磁性:“老鼠药。验过,真的。”
+葫芦侠一愣,旋即明白这是某种极端直接的投名状或证明方式。他接过瓶子,嗅了嗅,确实是某种剧毒鼠药的气味,心中暗惊此女胆大妄为,面上却不动声色:“姑娘这是何意?”
+“证明我不怕死。”女子淡淡道,眼神依旧没什么波澜,“也不怕麻烦。听说你们这儿,收不怕麻烦的怪人。我,溜溜侠,懂一点柯西定理,也会驯犬。能加入么?”
+葫芦侠与身旁的御寒侠交换了一下眼神。御寒侠低声道:“此女骑术精湛,身手应是不弱。只是这性子……”术好侠却饶有兴趣:“嘿,有点意思!当众喝老鼠药,这份疯劲,合咱们‘青蟹’的胃口!”
+葫芦侠看向溜溜侠:“柯西定理?你从何处学来?”
+“家传残卷,自学。”溜溜侠言简意赅,“有用则用,无用则走。”
+葫芦侠沉吟。此女来历不明,行为怪异,但那份对生死漠然的冷静,以及对柯西定理这种相对深奥数理知识的提及,又显示出她的不寻常。数堂初创,正需各种奇才,只要心性不恶,皆可容之。况且,她驯犬之术或许另有用处。
+“欢迎加入。”葫芦侠最终点头,取出一条新绦带递过去,“姑娘可愿暂入组织部?负责些联络、记录之事,亦可带你的……伙伴。”他看了一眼那安静的巨犬。
+溜溜侠接过绦带,仔细系好,对“组织部”的安排不置可否,只点了点头,便牵着犬走到一旁树下阴影里,抱臂而立,不再言语,仿佛与周遭的热闹格格不入。那巨犬伏在她脚边,目光警惕地扫视四周。
+招新会因为这意外插曲,气氛更加热烈。“红灰青蟹”招新顺利进行着,其中很多人明确表示对脑学数堂感兴趣,愿意参与活动的。葫芦侠对结果颇为满意,这已远超预期。
+有了第一批成员,脑学数堂的日常研讨便于地下石室悄然展开。或由成员提出疑难共同探讨,或就某个具体问题进行测算推演。
+但葫芦侠深知闭门造车不可取。数堂要立足,要发展,不能只在青蟹内部和地下石室自娱自乐,必须走出去,与其他堂口交流,学习他人长处,也展现自身价值。
+他首先将目光投向了脑机堂。
+脑机堂并非秉公习武馆原有堂口,而是三年前由一位致仕还乡的王长老捐资设立。王长老早年游历西域时,曾接触过天竺秘术,痴迷于意念与器物相通的玄想。他认为真正的机关之极,绝非死物,而当能近乎创造拥有独特反应的生命。故设此堂,一直在尝试一种惊世骇俗的技艺——灵犀链。
+此术旨在以特殊炼制的感应金针刺入特定穴道,连接武者微弱的内力生物电与特制机关的中枢感应磁石,从而实现不靠手脚牵线、仅凭凝神动念,即可隔空微微操控或感知简易机关的效果。
+葫芦侠选择脑机堂作为第一个交流对象,更深一层在于,他敏锐察觉到,灵犀链所探索的“意念-机关”接口,其底层逻辑极度依赖于对灵犀波的波形、强度、频率的精密测量、建模与解读,这本身就是一门极其深邃的数理学问。
+王长老正在堂后核心工坊内,对着一具感应人偶沉思。人偶头颅部位,数枚细如牛毛的感应金针在幽光下泛着光。听闻葫芦侠来意,目光在葫芦侠脸上停留片刻,方才缓缓道:“数理为筋骨,机关为皮毛。筋骨不健,皮毛不附。你设此堂,眼光倒是毒辣,看到了皮毛之下的东西。”
+他示意葫芦侠近前,指向感应人偶头颅内那些错综复杂的细微线路:“然则,欲以数理振武学,乃至窥探灵犀之秘,路漫漫矣。江湖武人,多重实战体悟,轻抽象思辨。你如何让他们信服,那些纸上的点线面、天书般的算式,能助他们拳脚更强、刀剑更利?”
+葫芦侠心神震撼于眼前精巧又诡异的造物,稳了稳气息,恭敬答道:“晚辈亦知不易。故不敢求速成,但求埋下种子。让同门先看到有用,再引导其探究背后之理,或有一线之机。”
+“哦?”王长老眼中精光一闪,仔细打量了葫芦侠一番,“由用及理,由形及神,思路倒是清晰。
+你既名‘脑学数堂’,可知‘脑’为何?在老夫看来,非仅思虑。”
+葫芦侠心有所感,试探答道:“可是……连接与转化之枢机?连意念与肢体,化无形之为有形之力?”
+“近乎矣。”王长老颔首,“脑子的用处,就在连和译。连接内外世界,翻译神思意念,变成具体的命令。数理,就是那座最靠谱的连接桥梁,那套最精准的翻译密码。你练武,一招一式是散落的珠子,心法口诀是把珠子串起来的线,而那数理认识,就是让这串珠子变成活的,且与天地产生共鸣的关键要素。这才是更深一层的数理武功核心——它或许能帮人突破自身极限,跟更广阔的存在建立起精密的联系。”
+葫芦侠心中剧震,王长老所言,已远超他之前对数理赋能武功的想象,指向了一个人与器、意与物深度融合的未来图景。数堂的终极目标,或许不仅是为武学注入可推演的魂,更是为了理解乃至搭建那种连接万物、翻译天地的通灵之桥。
+“多谢长老指点迷津!”葫芦侠离座,深深一揖,这一揖比之前更加心悦诚服。心中对数理与灵犀之道结合的深远可能,有了更为磅礴的想象。
+离开脑机堂,葫芦侠又马不停蹄,拜访了图象堂。
+图象堂历史悠远,门中一方面讲究从复杂的江湖见闻、地形图谱乃至打斗痕迹中,快速归纳特征、辨识规律、追本溯源;“另一方面精于设计精妙的密文编译与信息传递之法,于无声无息间通传消息。其堂主鹿长老,是位眉目精干、行事利落的女子,听闻葫芦侠来意,颇为爽快:“数理与图谱,一为骨,一为皮,本就相辅相成。你既有心深研此道,我堂可遣弟子与你处多加往来。彼此切磋,互通有无。”
+数日后,图象堂的大师兄黄驰侠、二师兄白维侠,便受命前来切磋帮扶。
+黄驰侠为人沉静,观察力入微,尤擅识微之功。他能从一片狼藉的打斗现场、一张潦草的地形草图,甚至是一段模糊的目击描述中,迅速提取关键特征,在脑中构建出清晰的脉络与模式。“譬如追索敌踪,”黄驰侠在沙盘上信手勾画,转眼便呈现出山道、溪流与几处模糊的足迹,“追踪者不能只辨方向,更需从足印的深浅、间距、周边草木倒伏的态势,乃至风中残留的细微气息,推演目标的身法习惯、功力损耗、乃至其此刻心绪与可能采取的规避策略。我堂千踪谱之法,便是将此类零散征兆,归类为若干可循的模式。若能与数理结合,将这些模式参数化,建立更精密的推演算式,追踪之效必可倍增。”葫芦侠暗暗称赞,惊叹于黄驰侠的内力之强大。
+白维侠则思维跳脱,对世间万物间那些看不见的联系,有着异乎寻常的敏锐。在他眼中,飞鸽传书、烽火狼烟、乃至江湖人常用的手势暗号,都不过是粗糙的信道,其间充满了损耗。他真正醉心并擅长的,是设计一套套精巧严密、近乎艺术的通讯体系,其核心奥义,便是将需要传递的讯息,通过一系列基于数理的变换规则,转化为另一种能被精准还原的形态。
+白维侠取出琉璃灯与一幅山水画,向众人解释,“好比这幅画,在寻常光下,它是山水。但在我这盏以特定琉璃制成的灯下,配合观看者所站的特定方位……”
+他将灯置于画前某处,调整角度,灯光透过层层琉璃,在画纸上映出奇妙的光纹交错。只见画中原本连绵的山峦轮廓线上,竟随着光纹流动,浮现出断续但清晰的小字,拼成一句密语。“这便是我琢磨的密语。其关键在于,我将要传的密文,并非直接写在纸上,而是将其拆解为一套复杂映射规则。这规则本身,可以看作一组预设的编码算法。传递的并非字迹本身,而是这套算法参数。对方只需知晓算法,即使拿到原画,无此灯与心法,亦如观天书。”
+他进一步解释道,这套“算法”可以千变万化,更进一步,“他眼中闪烁着跃跃欲试的光芒,“若能引入你们提及的更深奥的数理变换,我可以设计多层嵌套的编码。比如,第一层用坐标变换隐藏文字位置,第二层用颜色相位差承载真正信息,第三层甚至可利用纸张厚度来设置验证机制。不同的信道对应不同的数理变换,如此构建的密讯体系,方能隐蔽且高效。”
+葫芦侠当即谢过白维师兄。他独一无二的对传递信号的见解,给了葫芦侠许多启发。
+在技术层面与多方深入交流以后,葫芦侠开始考虑数堂的账目。地下石室虽不收费,但日常的纸张、笔墨、绘图工具、实验材料都是一笔开销。数堂无产无业,目前光靠着一些捐赠和个人积蓄维持着。但是这其中的账目计算,亦不是一件易事。
+他想到了经管门。
+经管门是武堂内一个特殊存在,弟子不多,但皆精于筹算,武馆的田产、铺面、物资调度、乃至部分对外营生,都有其弟子参与管理。门主常年云游,门中事务多由于博侠打理。于博侠是Eddie侠的江湖好友,为人圆融通达,擅长交际。
+葫芦侠通过Eddie侠牵线,拜访了于博侠。说明来意后,于博侠摇着折扇,笑道:“葫芦师弟有志于学,是好事。经营之道,无非开源节流。你数堂初立,谈开源尚早,先做好节流,厘清必要花费,方是正道。”他唤来一人。
+来人是个面色苍白、身材瘦削的青年,手指总在不自觉地快速掐算着什么。他便是网络侠,是江湖顶有名的掌柜先生。
+“网师弟,”于博侠介绍道,“这位葫芦师弟有一摊子事,需人帮忙算算账目,规划用度。你帮帮葫芦师弟,他定不会亏待你。”
+网络侠似乎这才注意到葫芦侠,视线聚焦了一瞬,点点头,声音平板无波:“可。数字,给我。”
+葫芦侠连忙将目前能想到的可能产生的各项花费,粗略列了单子。网络侠接过,扫了一眼,又看了看葫芦侠,忽然道:“岩壁画,矿物颜料,怕潮,需定期检查,防潮药剂,每半年一次,每次约一两五钱;日常笔墨纸砚,按目前二十人,每旬活动三日计,每月基础消耗约五两;实验耗材,视课题而定,难以预估,建议设上限,每月不超过十两;潜在器械购置,无上限,此项需单独申请……”
+他一口气报出一串数字,精确到钱,甚至将一些葫芦侠自己都没考虑到的细节花费也算了进去。葫芦侠听得目瞪口呆。这网络侠简直是个宝,夸赞不迭。
+网络侠又沉浸回自己的数字世界,只是手中多了一张写满算式的纸,开始写写画画。
+有了网络侠的帮助,数堂的账本立刻清晰了许多。葫芦侠心中大定,开始着手筹备数堂的第一次公开活动。
+就在葫芦侠为脑学数堂里里外外忙活的时候,他和几个兄弟姐妹,私下里还琢磨着另一档子事。
+人兮侠、三水侠、羽昕侠,这三位是他在常青藤教习队的好伙伴,跟葫芦侠那是过命的交情。四人没事就爱凑一块,在羽昕侠飘着药香的院子里,泡上一壶茶,天南海北地瞎聊。从江湖上的新鲜八卦,到各自练功时遇到的坎儿,再到对武堂风气、将来出路的看法,啥都敢说。
+这天晚上,四人又猫在羽昕侠药庐后头的小石桌边。“数堂这边,算是走上道了。”人兮侠吹开茶沫,慢悠悠地说,“葫芦兄,总算把你心心念念的事办起来了,是该恭喜。不过嘛……”他顿了顿,“我总觉得,还差点意思。”
+“差在哪儿?人兮兄直说。”葫芦侠放下茶杯。
+“咱们数堂,现在拢共就这些人,基本都是原本就对这路数感兴趣的。”人兮侠分析道,“可对武堂里绝大多数师兄弟来说,数理这东西,还是太高、太远,甚至觉得是不务正业的奇技淫巧。想扭转大家的看法,不是一天两天的事。光靠数堂按部就班地讲课、研讨,渗透得太慢了。咱们是不是……得再开条路?弄个更轻松、更好玩点的法子,把这套道理,悄没声地传出去?”
+三水侠手指轻点桌面,若有所思:“你的意思是……咱们不单单守着堂口的规矩,再搞个更松散的场子?门槛低点,先把人吸引过来再说?”
+“对,就是这么个理儿!”人兮侠点头,“数堂是正殿,规矩严,门槛明。咱们得再搭一座便桥,一座谁都能来、谁都能踩两脚的便桥。别管是哪个堂口的,也别管原先练啥的,只要心里还有点好奇,还想琢磨点东西,都能来这儿。大家可以就是喝喝茶、看看稀奇玩意儿,也可以结伴去野外探个险,或者凑一块琢磨个谜题……怎么轻松怎么来,但在这些闲玩瞎聊里头,把咱们那套探究事物本理的味道,慢慢透出去。”
+羽昕侠眼睛一亮:“这主意好!我那地下洞窟,除了主室给数堂用,旁边还空着好几间小石室呢,还有几条岔道往里走,有的地方石头长得怪,有的地方有温泉眼,还有些稀罕的药材矿石。拿来当这种探索聚会的秘密基地,又安全又有趣,正合适!”
+葫芦侠听得一拍大腿:“妙啊!数堂是扎马步练内功的本,这桥就是行走江湖交朋友的用,也是引人上道的饵!先用好玩有趣的桥把人勾过来,等人尝到甜头、生了兴趣,再引他们进‘堂’里学真功夫。两头使劲,效果肯定好!”
+三水侠笑了:“那得起个名儿。是桥,也得有点新气象、新盼头……”
+四个人琢磨了一会儿,几乎同时抬头,异口同声:
+“河西新桥!”
+“河西”,有心向更广阔西域天地的意思;“新桥”,既是实指连接,更是象征一条不一样的新路、一种新的尝试。
+“好!就这个了!”人兮侠赞道。
+“那咱就说定了,”葫芦侠心里热乎乎的,“这‘河西新桥’,就咱们四个合伙来弄。第一次聚会,就定十天后秋分晚上,咋样?主题我都想好了,就叫‘地火探幽’!就去羽昕师姐那条有温泉眼的甬道尽头,咱们看看地热是咋回事,聊聊它的成因,要是行,还能用那热水煮茶温酒呢!”
+“有意思!”三水侠乐了,“那天我非得画一幅《地火烹茶图》不可!”
+“我去准备些耐热的家伙什,再把那条路彻底探一遍,确保万无一失。”羽昕侠已经开始盘算了。
+“行,头一批邀请谁,我来拟名单。”人兮侠接下了话。
+四人举起茶杯,以茶代酒,清脆地碰了一下。
+“河西新桥”,就这么在四个自诩六都镇最聪明的四个人的茶话间,定了下来。它没有招牌,不设门槛,就像地底那些安静流淌的暗河,只为连接那些不愿只在地表行走、渴望窥见地下别样风景的好奇心灵。
+当葫芦侠忙于数堂与“新桥”诸事时,一个消息传来,让他怔忪了许久。
+欧阳侠要走了。
+不是短暂的游历,而是真正的远行,远到海的对面。
+欧阳侠是葫芦侠入武堂最早结识的朋友之一。他自幼便见识过七海浩渺。与其他弟子不同,欧阳侠练武并非为了争雄擂台,而是为了一个更遥远的目标——他渴望用双脚丈量更广阔的世界。
+他作为数理门弟子,心底一直向往着门中传说更侧重“应用”与“构建”的支脉——计算门。他听游历归来的师长提过,在遥远的新大陆,计算门学问尤为昌盛,那里的大派,譬如鬼谷派麻绳派,专注于研究一种名为算法的思维武术,用以驱策名为计算机的复杂机关,构建庞大精密的系统,乃至创造虚拟的世界。他认为,数理是认识世界的语言,而计算,则是用这种语言去创造、去连接、去解决实际问题的手艺。
+为此,他默默准备了数年。不仅倾尽家资,更不惜数次冒险充当远洋商船的护卫与导航,用命去搏那份见识新天地的资本。他终于凑足了购置一艘能经得起大洋风浪的帆船、并雇佣一队可靠老练水手的钱财。
+“我要自己驾船,横渡重洋,去新大陆。”欧阳侠对葫芦侠、Eddie侠等至交说起这决定时,眼里没有彷徨,只有一片灼热的澄明,“走先祖探过的老航线,过琉球,越太平洋,直抵金山港。我要去那里访学,看看他们的算法究竟如何运转天地,他们的系统怎样构架虚空。或许三五年,或许十载,也许留在那边深入修行,也许将所学带回我们这片土地。”
+无人劝他回头。深知欧阳侠的人皆明白,远航与求学,是他命中注定要踏上的两条交汇之路。大海是他的征途,而新大陆上的计算门,是他征途的终点与起点。
+离别之日,天色清朗。码头在武堂东南三十里外的沅水畔,帆影点点。
+来送行的,只有葫芦侠与Eddie侠二人。欧阳侠涕泪沾襟的场面,只愿与知己静静作别。
+Eddie侠依旧一副懒散模样,斜倚系船桩,指尖几枚铜钱翻飞如蝶。但葫芦侠瞧得出,那玩世不恭的眼眸深处,藏着一抹难得的静默。
+欧阳侠在做最后的巡检。那艘被他命名为“破浪号”的双桅帆船,线条利落,船体坚固,通身靛蓝。水手们正有条不紊地搬运着最后的粮草清水。
+见两位老友到来,欧阳侠咧嘴一笑,被海风早一步染上淡褐的面庞上,牙齿显得格外白亮。那笑容爽朗依旧,眼神却比以往任何时刻都更亮、更定,那是一种剔除了所有杂念、心无旁骛奔赴山海的光芒。
+“都妥了?”葫芦侠问。
+“万事俱备,只欠西风。”欧阳侠爱惜地拍了拍船舷,“这老伙计,陪我去会会太平洋的风浪。”
+“名字真土。”Eddie侠嗤了一声,手腕一翻,那几枚铜钱却不知收去了何处。
+欧阳侠不以为意,大步过来,先给葫芦侠一个结实的拥抱,又捶了下Eddie侠的肩头:“安心。我命属风浪,没那么容易交代。你们在武堂,该闯祸闯祸,该折腾折腾。葫芦,把你的数堂弄出个样子来,说不定将来我学成归来,还得跟你合作,搞点新花样。Eddie,收敛点,也许明年,你也来找我了!”
+“管好你自己吧。”Eddie侠这次没还嘴,只嘀咕道,“别乐不思蜀,忘了紫金山的月亮。”
+三人相视,齐齐大笑,笑声撞碎在江风里,漫开一片无言的珍重。
+“这个,贴身带着。”葫芦侠从怀中取出一个用油布仔细包好的扁平包裹,递给欧阳侠,“里面有几样东西。一个我改良过的、加了防水密匣的六分仪,使用心得写在里面了;还有我特地准备的解毒万应散,海外瘴疠之地或有用处。另外……”他声音压低了些, “那本《算法初步》的夹页里,有我根据近日所学,对利用天体进行航向判断的一些胡思乱想,草稿而已,你带着,路上若闷了,看着解闷,或直接引火也行。”
+欧阳侠接过,入手沉实。他用力捏了捏包裹,郑重纳入怀中衣内,紧贴心房:“好兄弟,雪中送炭。这都是能救命、更能指路的东西。”
+Eddie侠也递过来一个不起眼的灰布小囊:“我没他们那么讲究。几颗雷火弹,动静大,唬人也好,开路也罢。还有一块北海玄铁,韧而利,你找个靠谱匠人,打把贴身短刃或箭头,比凡铁强。重点是这个,”他变戏法般又摸出一只非金非木、布满细微孔洞的哨子,“用我教你的独家运气法门吹,声音寻常人几乎不察,但能传出极远。
+欧阳侠深深看了Eddie侠一眼,没说话,只将布囊和哨子紧紧攥在手心,重重点头。
+朝阳已完全跃出江面,金辉泼洒,将“破浪号”的蓝染上一片璀璨的辉煌。水手长在船头示意。
+欧阳侠深吸一口气,最后望了一眼武堂所在的西北方向。那里有他整个少年与青春岁月,有汗水和欢笑,有不舍的故人,和一段尚未完全展开的人生。然后,他毅然转身,面向东方,面向那条通往无尽蔚蓝的浩渺水路,那里有吞噬一切的深渊,也有照亮前路的星辰,有独行的孤寂,也有拥抱新知的狂喜。
+“走了!”他不再多言,挥挥手,身形一纵,轻盈而稳当地落回破浪号甲板。
+帆索吱呀,巨帆缓升。风,鼓满帆面,猎猎作响。
+破浪号缓缓离岸,船头划开平静的江面,调整方向,义无反顾地驶向大江入海之途,驶向那片通往新大陆的、浩瀚无垠的太平洋。
+葫芦侠和Eddie侠并肩立于码头,目送那一点靛蓝在粼粼金光中渐行渐小,最终化作水天之际一粒微尘,直至完全融入苍茫。
+江风拂面,带着湿润的凉意与淡淡的腥咸。
+“他会回来的。”Eddie侠忽然开口,声音不高,却斩钉截铁。
+“嗯。”葫芦侠的目光仍追寻着那已无踪迹的远方,语气同样肯定,“一定会。带着大洋彼岸的见闻,和计算门的真传。”
+两人在码头上伫立良久,直到日头爬得老高,江面碎金晃眼,才默然转身,沿来路而归。身后,沅水滔滔,奔流不息,仿佛一首无词的壮行歌,吟唱着离别,更预告着一场跨越重洋的传奇序幕,正在缓缓拉开。
+属于欧阳侠的传奇,在浩渺太平洋之上,在新大陆的计算门圣殿之中。
+而属于葫芦侠,属于脑学数堂,属于所有秉公习武馆内不安分、不止步的探索者的故事,仍在这片山岭环抱的土地上,悄然生长着,等待破土之日。
\ No newline at end of file
diff --git a/docs/novel/第05章.txt b/docs/novel/第05章.txt
new file mode 100644
index 0000000..96dfaef
--- /dev/null
+++ b/docs/novel/第05章.txt
@@ -0,0 +1,143 @@
+葫芦侠行 第五章
+公元1024年,紫金山上的残雪还未完全消融,迎春花就迫不及待地从石缝里钻出来。
+葫芦侠背着行囊回堂,腰间那个大葫芦随着步子一晃一晃。他先拐到时间广场的布告栏前——这是老习惯了,得看看今年武林大会的章程。可这一看,把他看愣了。
+今年的总决赛,不在露天擂台上打了,改在二道门以外的武术交流中心。更让人吃惊的是规矩变了:不兴单打独斗了,得以“堂口”为单位参赛,比的不是谁拳头硬,是哪个堂口能拿出“普惠江湖的新鲜武学。参赛的堂口不光要现场展示功夫,还得画一张能说清自家武学精髓的画展报,更要在天下名宿长老跟前路演,说道清楚功夫之新意与用处。
+“这……这不光是比谁狠,是比谁的门道能服众、能传开啊。”葫芦侠喃喃自语,脑子里嗡地一声。
+他猛地想起去年冬天。那时候武林开了一场顶顶热闹的武学颁奖大典,他当时混到了一张入场券,去见了回世面。台上,江湖泰斗、计算门的泰山北斗——周大长老登台讲演。大先生声音洪钟似的:“门户之见,那是江湖私斗的烂泥潭!真正的上乘武学,根子得扎进国运命脉里,探的是万物至理,为的是百业兴旺!”
+那话,到现在还在葫芦侠耳朵边嗡嗡响。他记得典礼散了,人潮往外涌,他不知哪来的轴劲儿,硬是挤开层层护卫,在长廊尽头堵住了正要离开的周大长老。手心里全是汗,他在衣襟上蹭了蹭,鼓足勇气开口:“周、周长老,晚辈是秉公习武馆数理门葫芦侠。敢问长老,一个刚起头的小堂口,要怎么在这偌大江湖里……让人瞧得起?”
+周大长老停下脚步,那双看透世事的眼睛在他脸上停了片刻,沉声道:“后生仔,别总琢磨怎么在擂台上放倒几个人。多想想,你们的数理功夫,能给这世道、给我大宋的百姓,解决什么实在的难题。上报国家,下安黎庶,到了那份上,影响力不请自来。”
+正当他对着榜单怔怔出神,反复咀嚼其中意味时,一个温和的声音自身侧响起。
+“这位师弟,可是对今岁新规有所感悟?”
+葫芦侠回过神,转头望去。只见身旁立着一位同门,面容清俊,气质温润,着一身素雅的月白长衫,腰间佩剑形制古朴,正含笑看着他。此人气度从容,目光清明,让人见之便生几分好感,但在武堂中却甚是眼生。
+“师兄是?”葫芦侠忙拱手见礼,心下却暗自警惕。他此刻心绪激荡,又正值脑学数堂欲展抱负之际,对陌生同门的搭讪不免多留一分心。
+“在下玎铧,平日多在藏书楼整理故纸,少在江湖走动,师弟面生也是常理。”玎铧侠还了一礼,笑容温和,并无丝毫咄咄逼人之态。他目光扫过布告栏上的新规,又落回葫芦侠身上,语气带着恰到好处的好奇与赞赏:“若我没认错,师弟便是数理门新立‘脑学数堂’的葫芦侠吧?久仰久仰。方才见师弟在此凝视新规,若有所思,想必已有应对之策在心?”
+葫芦侠心下稍松,但并未完全卸下心防,只谦道:“玎铧师兄谬赞。新规乍出,葫芦与堂中同道一样,唯有惶恐思索,何谈成策。不过是觉得此番变革,正与我堂平日所思所倡有暗合之处,深感责任重大罢了。”
+“不骄不躁,心有丘壑,果然名不虚传。”玎铧侠点了点头,眼中赞许之色更浓。他忽地向前略走近半步,声音压低了些,却依旧清晰入耳:“葫芦师弟志存高远,欲以数理经世,此心可嘉,此路……亦必多艰。”
+葫芦侠心中一动,抬眼看向对方。玎铧侠的神色依旧温和,但那双清澈的眼眸深处,似乎掠过一丝极难察觉的、近乎怜悯的复杂情绪,快得让人以为是错觉。
+不待葫芦侠深究,玎铧侠已自袖中取出一物。那是一个约莫巴掌大小、用普通青布缝制的锦囊,做工细致,但并无任何纹饰标记,看起来平平无奇。他将其递向葫芦侠。
+“这是?”葫芦侠并未立即去接,面露疑惑。
+“此乃一位隐居前辈早年所赐,嘱我转交于有缘之后进。”玎铧侠语气恳切,将锦囊又往前送了送,“此锦囊予你,非是助你捷径,也无关名利争斗。”
+他顿了顿,目光坦然地看着葫芦侠,声音更缓,却字字清晰:“若他日遭遇困厄,可打开一观,或许可助你一臂之力”
+他不再多言,对葫芦侠点头示意,便立即转身飘然而去,步履从容,很快消失在人流之中,仿佛真的只是一次偶然的驻足交谈。
+葫芦侠站在原地,望着玎铧侠离去的方向,手指无意识地隔着衣衫,按了按怀中那处新添的微硬触感。青布锦囊静静地贴着胸口,并无一丝特异气息,却像一粒投入心湖的石子,漾开圈圈疑虑与深思的涟漪。
+此人是谁?那位隐居前辈又是何人?这锦囊……究竟是何用意?
+然而,武林大会新规带来的澎湃心潮与紧迫感,很快压过了这偶遇的谜团。葫芦侠深吸一口气,将关于玎铧侠和锦囊的思绪暂时压入心底。眼下,有更迫切、更明确的事情需要他全力以赴。
+回到地下石室里,新学期第一次堂会开始了。灯火通得亮堂堂。围坐着的都是数堂的核心,一张张年轻的脸被灯火映着。葫芦侠把怀里那枚乌木令牌——就是当初师祖给的那块——郑重地放在长桌中央。
+“诸位,”他环视一圈,声音不高,但每个字都砸得实在,“今年武林大会,咱脑学数堂,就奔着八个字去——”他顿了顿,一字一顿,“学习数理,报效国家!”
+石室里静了一瞬,随即响起一片吸气声,少年们的眼睛“唰”地亮了。
+“数理是什么?”葫芦侠站起身,手指划过岩壁上三水侠画的先贤探秘图,“是朝廷运输粮草、排兵布阵、打理国库的底层根基!咱们要的,不是教出几个能打擂台的狠人,是培养真正懂数理、能用数理为朝廷分忧、为百姓做实事的栋梁!”
+一股滚烫的气,从每个少年心底腾起来。
+口号喊出去了,可要在这全武林瞩目的新擂台上露脸,脑学数堂这家底薄的新摊子,得把每个人的看家本事都逼出来,拧成一股绳。
+头一桩要紧的,就是那张脸面——画展报。三水侠当仁不让铺开重金购来的上等素绢。可这回的展报非同小可,既要画出数理的筋骨严谨,又要撑起报效国家的宏大格局。她一个人琢磨了两天,总觉得差点气魄,一咬牙,连夜去叩了挚友雅庭侠的门。
+雅庭侠是设计门的高徒,对色彩、构图、意境有种近乎偏执的细腻。听了三水侠的难处,她二话不说,夹着铺盖卷就住进了地下石室。连着七八个夜晚,石室里灯火通明。三水侠主笔,泼墨挥毫,大宋万里江山的雄浑轮廓在她笔下渐次浮现。雅庭侠就守在一旁,等墨迹稍干,便执起最细的狼毫,蘸上特调的金粉,伏在案前,在那些代表关隘、运河、矿藏的关键节点上,以令人惊叹的耐心,勾勒出无数精密交织的几何阵图与星宿轨迹。两人几乎不眠不休,当那幅名为《数理报国图》的巨制最终完成时,晨光恰好从通气孔斜射而入,照亮画卷。但见江山之上,数理脉络如金色的血脉与神经蔓延闪烁,仿佛整片国土都在一种理性而磅礴的秩序中呼吸。
+“成了。”三水侠嗓音沙哑,和同样憔悴的雅庭侠相视一笑,都从对方眼里看到了光。
+笔墨的事刚了,文案又接上。平日替人代写战帖赚零花的薄文侠,这回把酒葫芦系紧了。他把自己关在石室角落,对着葫芦侠他们整理出的堂报核心理念发呆。这些玩意儿对寻常江湖人来说,比天书还难看懂。薄文侠挠着头,忽然眼睛一亮——有了!他铺开纸,不再纠结字句的工整,而是运起他那手代写战帖练出的、犀利又煽情的文风,将那些冰冷的公式、理念,全化进了时下朝廷与江湖最流行的豪放词风里。一夜之间,一篇文采飞扬、气势磅礴的《数理·报国说》从他笔下倾泻而出,字字铿锵,句句砸在人心坎上。他揉着发酸的手腕,得意地嘀咕:“路演开场白?嘿,看我这篇东西,不把那些老古板听得心潮澎湃,算我白在江湖卖字这些年!”
+展台布置是重头戏,交给了对图形和结构敏感得邪乎的甲亢侠。别的堂口撑死了摆几张桌子挂点画,甲亢侠不知从哪弄来一大堆竹竿、绳索和精巧的榫卯件,在划分给数堂的展位前叮叮当当折腾起来。别人看不懂他在搞什么名堂,只见他时而在空中比划,时而趴在地上测算。三天后,一个令人瞠目结舌的几何分形展台拔地而起。整个展台由无数竹木构件以奇特的夹角拼接支撑,看似摇摇欲坠,实则稳如泰山。更妙的是,几根关键的绳索从顶部悬垂,路过的人只要随手拨动其中一根,整个展台的影子投在地上,竟会随之变幻出各种规整又玄妙的几何矩阵图案。“妙啊!这玩意本身,就是活生生的数理广告!”路过的别派弟子啧啧称奇。
+银钱和调度是命脉。经管门的马师姐和谢师姐,和于博侠引荐的网络侠接过了这摊。两人成天捧着亮晃晃的金算盘,噼里啪啦打得火星子直冒。堂里那点有限的散碎银子,被她们盘了又盘,每一文都花在刀刃上。物料损耗算得精确到片。这还不算,马师姐根据武术交流中心的布局图和人流习惯,确定了推演出了好几个人流驻足概率模型,最后拍板,我们需要去二月兰分发我们的宣传单造势,向习武馆里的人展示堂口的理念。
+圣文侠整天埋首在一沓沓写满密密麻麻数据的统算簿里。他不看具体的招式美丑,只关心冰冷的数据。他不知用了什么法子,收集到过去几年武林大会前百名高手的海量交锋记录,然后就开始了他那外人看不懂的“剔除残差、拟合回归”的苦功。别人问他忙啥,他头也不抬:“我在找那条……能看穿所有招式概率的线。”
+筹备紧锣密鼓,两位在数理门隐居多年、几乎被弟子遗忘的宿老——谢长老与戴长老,也被少年们的劲头惊动了。他们悄悄来到地下石室,看了那些熬夜画出的阵法推演草稿,又听了葫芦侠的构想,两位长老对视良久,眼中皆有震动。谢长老拿起朱笔,亲自在几处关键公式旁批注,修正了逻辑推演中不易察觉的漏洞;戴长老一言不发,取出门派的印章,在脑学数堂报备参赛的正式文书上,稳稳盖下。这代表着堂口的参赛,获得了数理门的认可。
+就在一切步入正轨时,红灰青蟹的溜溜侠和可可侠,又带回来一个人。
+是个女子,面色有些苍白疲倦,可眉宇间锁着一股挥之不去的孤高与执拗。她叫卡卡侠。
+“她从某个堂口被赶出来的。”溜溜侠抚摸着脚边安静的巨犬,语气没什么起伏,“就因为她不懂标准中原话,带着一股浓厚的岭南口音,又成天琢磨在那些弯弯绕绕的黎曼曲面空间里。那边的人说她走火入魔,是异类。最后把她打了一顿,清除出了堂口。”
+葫芦侠一听黎曼曲面,眼睛顿时亮了,仿佛在黑暗中窥见了一颗遗落的星辰。他当即恳切相邀,将卡卡侠也招入麾下,并为她在靠近石室出口、相对干燥通风的角落,清理出一处窄小却洁净的栖身之所,恰在溜溜侠与可可侠的铺位之间。三人自此比邻而居。
+起初几日,石室内的气氛有些微妙的凝滞。溜溜侠依旧终日与她那头沉默的巨犬为伴,擦拭短刃,打坐调息,面纱下的眼眸扫过卡卡侠时,并无波澜,却也谈不上热络。可可侠倒是温和,替卡卡侠铺了草席,递过清水,但谈及武功数理,也多是浅浅几句,点到即止。卡卡侠则躲在床帘里,每日除了必要的进食、应答,便是独自面壁而坐,指尖常在膝上无意识地勾画着复杂的曲线,眉头微蹙,似在思索那些常人难以理解的曲面空间奥秘,又似沉浸在某种不被理解的孤愤之中。
+打破这种尴尬氛围的,竟是一页偶然被风拂落的草纸。那夜,卡卡侠对着一盏小油灯,正试图将心中一个关于“高维曲面在三维投影中的奇异点分布”的构想记录下来,画到关键处,窗外渗入的夜风忽地将一张墨迹未干的纸吹起,飘飘荡荡,恰好落在溜溜侠脚边。
+溜溜侠本欲用刀尖将其挑起拨开,目光不经意掠过纸面那些扭曲缠绕、充满奇异美感的线条与旁注的陌生符号时,动作顿住了。她蹲下身,拾起那张纸,就着灯光细细看了片刻,清冷的声音终于第一次主动响起,带着一丝不易察觉的探究:“此处的闭合回路……为何要如此扭曲?似非欧几里得平面之环。”
+卡卡侠一怔,她未料溜溜侠竟能一眼看出其中关窍。“并非平面之环,”她迟疑一下,指了指纸上一处,“乃是三维中一特殊曲面上的测地线……若投影于二维,便显此扭曲之态。我认为,或可模拟某种非匀强内力场中的真气自发凝聚轨迹。”
+一直在一旁静坐、似在瞑想调息的可可侠,此时也悄然睁开眼,目光落在那张草纸上。“自发凝聚,必有约束,否则能量弥散。”她缓声道,手指纸面另一区域,“此扭曲形态,其边界能量流密度……似乎恒定?虽形态变化,总量却不增不减,倒是暗合某种守恒之意。”
+卡卡侠眼眸微亮,看向可可侠,仿佛遇到了懂她的人。“正是!此曲面本身具有内蕴的几何特性,在其上定义的某种量,沿特定路径积分,确与边界有关,且……有界。”她说到自己痴迷的领域,语气不由自主地加快了,那份孤高化作了专注的神采。
+溜溜侠沉吟片刻,忽然道:“柯西积分,重路径。若将此曲面视作积分区域,其边界……”她指尖在空中虚划,勾勒出一个简单的环,“便是我的环。无论内部场如何扭曲奇异,只要边界闭合、性质良好,外部扰动便可被此边界消化或隔绝,最终归零。”
+“而有界与整函数,则保证了内部无论如何扭曲变化,能量总量与逸散趋势,皆在可控制在界内,不至失控反噬。”可可侠接口,眼中也泛起思索的光芒。
+三人就着那一页被风吹乱的草稿,你一言我一语,竟围绕着那抽象的几何曲面,探讨起内力运行的路径积分。冰冷的公式与线条,在她们的话语间,仿佛被注入了真气与生命。卡卡侠发现,眼前这两位女子,虽与自己路径迥异,但那份对数理结构与武学现象背后统一规律的执着探寻,竟与自己隐隐相通。而溜溜侠与可可侠,也在此刻,才真正窥见卡卡侠那黎曼曲面功夫背后,并非空中楼阁的妄想,而是严谨的数学推演。
+隔阂,在共同的语言与智识的闪光中,悄然消融了几分。之后数日,三人之间的交谈明显多了起来。卡卡侠渐渐放下心防。溜溜侠虽依旧话少,但倾听时眼神极为专注,偶尔插言,皆切中要害。可可侠则常于二人争论僵持时,以更宏观、更稳固的界之理念,提出折中或升华的思路。
+一种心智共鸣的默契,在一次次夜谈、争论与沉默的思考中,悄然滋生。
+直到第五日深夜,石室中其他人早已歇下。三人又因白日里葫芦侠提及的攻防一体阵势构想,低声讨论起来。说到关键处,纸上谈兵已觉不足。
+“或许……”卡卡侠眼中闪烁着跃跃欲试的光芒,看向两位新交的同伴,“可以……试试?”
+溜溜侠与可可侠对视一眼,均从对方眼中看到同样的意动。有些东西,终究需在真气实际的交融碰撞中,方能验证。
+葫芦侠被低声唤醒,听闻她们欲做初步尝试,顿时睡意全无,亲自帮着在石室中央清出一块最平整的空地。
+三人缓步入场,相对而立。气氛与初次见面时已截然不同,少了疏离与审视,多了几分郑重与探究的期待。
+溜溜侠面纱下的眼神依旧清冽,却不再冰冷如刃,而似深潭静水。她缓缓抽出短刃,这一次,动作庄重。刃尖垂地,并非起手式,而是一个凝神的起点。真气自她足底悄然升腾,不再仅仅是沿着完美闭环的柯西路径运转,更在起势中,便隐隐与过去几日探讨的曲面边界概念相合,循环往复,将守护之意推至极致。那无形的环正在慢慢扩大。
+可可侠立于中宫,双手虚抱成圆,姿态舒展。她并未急于释放内力,而是先将心神沉入那片有界整函的意境之中。内力如古潭深水,看似凝滞,实则内蕴浩瀚。那无形的界,似乎被赋予了一种更灵活的调节机制,愈发牢不可破。
+而处于两人气机交织中心的卡卡侠,深吸一口气,闭上了眼睛。数日交流,那些抽象的曲面几何,此刻在她心中已与真气运行的可能形态紧密相连。她双手抬起,在身前开始极慢地划动。真气喷薄而出,循着心中黎曼曲面的坐标线与过渡函数,自然而然地扭曲、折叠、延展。不仅贴合着溜溜侠坚实闭环的内缘,而且约束在可可侠那稳界允许的弹性范围之内!
+三股性质迥异内劲,在这精心准备的方寸之地,再次试探、接触、交织。真气曲面层层叠叠,变幻莫测,牵引与撕裂之力吞吐不定,仿佛真的能无视寻常三维空间的桎梏,从任何想象不到的角度,迸发出攻击性!
+“嗡——!”
+一声比以往任何一次尝试都更低沉、更浑厚、更凝实的震鸣,仿佛源自空间本身被微妙撬动的“呻吟”,在石室中沉沉响起。三股真气,不再是简单的交融,而是在一种更深层的、基于数理共鸣与心智默契的引导下,自发地循着某种玄奥的结构嵌合,最终化作一股守则如亘古磐石、攻则如幽冥鬼魅、稳则如宇宙定则的威压!虽只持续了短短一息,三人便同时气息微乱,各自收功,面色潮红,额角见汗,但那瞬间爆发出的、令石室内所有人灵魂为之战栗,呆立当场。
+“成了!竟然……真的能以此种方式相合!”葫芦侠激动得霍然起身,声音因极度的震撼与兴奋而剧烈发颤,目光灼灼地轮流看向三人,“柯西之绝对守域,刘维尔之永恒定界,黎曼之无穷变幻……这、这已非简单叠加,这是规则嵌合,攻防一体的三才阵!”
+……
+就在数堂上下为武林大会拼命时,一只从西南扑棱棱飞来的信鸽,带来了意外的消息,也把葫芦侠的心搅乱了。
+信是常青藤西部教习队的孔锝侠队长亲笔写的。信上说,西南六都那边,新的教习队队伍正在集结,希望葫芦侠能继续担任笔墨组的骨干,一同西行。
+薄薄一张纸,压在葫芦侠手里,却重逾千斤。一边,是他倾注了全部心血、正值冲刺关头的数堂和武林大会;另一边,是那些在记忆里依然鲜活,眼神清澈的西南孩童,是孔队长的信任与召唤,是那片土地上未竟的承诺。
+他连着好几日沉默寡言,眉头锁成了疙瘩,连吃饭都心不在焉。而去年一起教习的同袍喆航侠——就是那位家里有二百匹宝马的阔少——看不下去了。
+这天一大早,喆航侠直接踹开葫芦侠的房门,不由分说把人拽了出来。山门外,他那赤金宝马赤霞正不耐烦地刨着蹄子。
+“上马!带你醒醒脑子!”喆航侠翻身上马,一把将还在发懵的葫芦侠拽到身后,“赤霞”长嘶一声,撒开四蹄,化作一道赤金色的闪电,冲进了紫金山莽莽苍苍的崇山峻岭。
+这一跑,就是整整三天。风在耳边嘶吼,山林急速倒退,胯下骏马奔腾的律动仿佛要震散人骨子里的犹豫。第三天傍晚,两人一马立在一处孤绝的高崖之巅,脚下是云雾翻腾的万丈深渊,远处隐约可见大宋江山蜿蜒的轮廓。
+喆航侠摘下被风吹得冰凉的铁盔,看着身后被颠得七荤八素、却眼神清亮了些的葫芦侠,淡淡道:“葫芦,知道什么是真正的好马不?好马不是啥路都想蹚,是认准了一条道,就把眼蒙上、心定死,豁出命跑到黑!你现在,心太贪,也太软,恨不得把全天下的担子都挑自己肩上。可你数堂这条船,刚离岸,正是吃水最浅、最怕风浪的时候。你这掌舵的要是自己先跳了水,船非沉了不可!”
+高崖上的烈风,像刀子一样刮过脸庞,却也刮走了葫芦侠心头最后那点混沌。他闭上眼,西南孩童的笑脸、孔队长严厉的目光、数堂同伴们熬红的眼睛、岩壁上报效国家的朱红大字……无数画面交错闪现,最终定格。
+他睁开眼,目光已是一片清明坚定:“我明白了,喆航。多谢。”
+回到武堂,葫芦侠径直去了材料楼。孔锝侠正在里面亲手整理教习队的行装,动作一丝不苟。厅内气氛一如往常的肃穆。
+“坐。”孔锝侠没抬头,声音平稳。
+葫芦侠没坐,而是走到近前,深深一躬到底:“孔队,弟子……此番不能随队西行了。脑学数堂正值关键,武林大会在即,堂口需要弟子留下掌舵。辜负队长期望,弟子愧对西南的孩子们,愧对常青藤。”
+孔锝侠整理行装的手停住了。他缓缓直起身,走到窗边,望着西南方向起伏的山影,沉默了很久。久到葫芦侠以为他会发怒,或是失望。
+终于,他开口了,声音不高,却带着一种葫芦侠从未听过的、滚烫的炽烈:
+“葫芦侠,你知道我们教习队,年年岁岁,为什么非要派人去那西部偏僻的地方吗?那里的武师未必信我们,那里的孩子未必能成材,我们甚至可能把命搭上。图什么?”
+他转过身,目光如炬,钉在葫芦侠脸上:“就图奉献这两个字!练武之人,如果一辈子眼里只装着自己的功名、自己的地盘,那跟市井里锱铢必较的贩夫走卒有何区别?!常青藤的每一个人,都是自己甘心做一截火柴,要点燃了,去给那些生在暗处、可能一辈子看不见山外世界的孩子,照个亮!我们不是救世主,我们就是去种种子的!用我们自己这点光,引着他们,心里能生出一点想看看外面的念想。这种不计回报、只愿点灯的傻劲,才是咱们秉公习武馆,骨头里的魂!”
+每一个字,都像烧红的烙铁,烫在葫芦侠心上,烫得他眼眶发热,脊梁骨嘎巴作响,一股滚烫的血气直冲顶门。
+两天后,教习队出发的前夜,葫芦侠再次敲开了材料楼沁心厅的门。
+这一次,他将一本连夜修订、用油纸仔细包好的手稿,双手奉到孔锝侠面前。那是他最初的《葫芦瞎说》,如今已被增补了大量适合蒙童启智的数理游戏、观察方法与浅显道理。
+他看着孔锝侠,眼神清亮,声音在寂静的夜里格外坚定:
+“孔队,您那番关于常青藤精神的话,弟子这两日,在心里翻来覆去,想了无数遍。弟子人虽留在紫金山,可这颗心,从今往后,就打上常青藤的烙印了。奉献不分地界远近!去西南大山,开蒙孩童、强健体魄,是奉献;留在紫金山,扎深数理的根,用这套理性和逻辑,去破江湖陈规,为朝廷培养能真正派上用场、能上报国家的数理人才,一样是奉献!弟子向您立誓,常青藤的这份燃灯精神,从今日起,就是我脑学数堂立堂的脊梁骨!”
+孔锝侠那张常年严肃、近乎古板的脸,在这一刻,如同春冰化冻,彻底舒展开来。他伸出那只布满厚茧、稳如山岳的手,重重地、结实地拍在葫芦侠肩上,眼中是毫不掩饰的激赏与欣慰:
+“好!好一个‘奉献不分远近’!葫芦侠,记住你今夜这番话。只要心里装着家国天下,装着黎民苍生,无论你在何处,都是我常青藤走出来的、顶天立地的傲骨!”
+翌日,晨光熹微。新一届常青藤西部教习队在大校场集结。队长的大旗,被交到了那位从泉州来的豪爽汉子骆骑侠手中,七柒侠作为副队长在一旁协助。送行的人很多,喧哗热闹。人群里,另外两个小队的队长生辰侠、乐天侠,竟拉着骆驼侠在出征的桃树下,斩鸡头烧黄纸,歃血为盟,结为异姓兄弟,誓言同生共死,共拓西陲,引来一片喝彩。
+葫芦侠站在送行的人群里,看着载满物资的马车辘辘启动,驶向那云山渺渺的西南。他悄悄抹去眼角一点湿意,转过身,脚步坚定地走向地下石室的方向。
+他知道,孔锝侠点的那盏奉献之灯,那属于常青藤的滚烫魂魄,已牢牢种在了他的心底,也必将照亮脑学数堂未来的每一步。
+终于到了武林大会总决赛的日子。新建的“武术交流中心”人山人海,旌旗招展,金鼓之声震得人耳膜发麻。各门各派的展位五花八门,争奇斗艳。
+脑学数堂的展位,因几何分形展台和那幅气势磅礴《数理报国图》,一亮相,就吸住了无数目光。看热闹的、打探虚实的、真心求教的,把展位围得里三层外三层。
+当时辰的钟声敲响,决赛的路演环节正式开始。轮到脑学数堂时,全场目光聚焦。
+葫芦侠今日特意换了一身素净的青布衫,洗得发白。他稳步走到展示台中央,面对台下黑压压一片的江湖名宿、各派精英,深吸一口气,抱拳环礼。
+“诸位武林前辈,天下同道!”声音清越,以真气送出,清晰地回荡在广阔的大厅内,“今日,我秉公习武馆脑学数堂在此,所呈之技,非是一门一派之私功,乃是以数理为万业根基,志在报国的的全新武学路径!”
+话音未落,展示开始!
+首先演示的,是圣文侠和陈湘侠带着几位统计学子的大样本破绽回归步。
+陈湘侠长啸一声,身形如电射出,在台上划出道道残影,将“快”字发挥到极致。与此同时,台侧阴影里,圣文侠手中那本厚重的统算簿无风自动,书页哗啦啦翻飞。他根本不看陈湘侠,而是死死盯着台上几个由机关操控、模拟不同流派高手进攻的陪练假人。假人们攻势绵密如网。
+“数据抓取,残差录入……样本量七百四十三,拟合开始!”圣文侠口中念念有词,手指在虚空快速点划,仿佛在拨弄无形的算筹,“开始拟合,参数修正!”
+台上疾驰的陈湘侠闻声,没有丝毫犹豫,在间不容发之际,身形诡异地一折,恰恰从三道剑光的缝隙中穿过,步法轨迹刁钻得不可思议,仿佛早已算准了所有攻击的落点。
+“离位,退半步,仰角十五度!”
+陈湘侠足尖一点,后仰翻身,一道凌厉的掌风贴着他鼻尖掠过。
+“就是现在!坎位突进,循最小二乘路径!”
+陈湘侠身形再变,不再追求直线最快,而是沿着一条看似曲折、实则总距离最短、风阻最小的奇异弧线,如游鱼般滑过所有假人的围攻,瞬间切入敌阵核心,手指轻点,便按住了假人胸口代表要害的木钮。
+全场哗然!
+这不是传统武术范畴的见招拆招,而是将对手所有可能的攻击,都看作了可被海量数据拟合并预测的概率云!陈湘侠的每一步,都踩在圣文侠通过瞬间计算得出的最优解上,以最小的消耗,达成最大的闪避与切入效果。将随机的搏杀,转换为可量化可降维的步法!
+“好……好生了得的最小二乘回归算功!”评委席上,一位以轻功见长的名宿忍不住捋须惊叹。
+紧接着,葫芦侠踏前一步,亲自演示第二项核心功法——蛛丝数据抓取掌!
+只见他双掌缓缓推出,没有刚猛的掌风,但离得近的人,都能感到前方空气产生了诡异的扭曲与颤动。无数道肉眼几乎看不见、却真实存在的真气细丝,从他掌心蔓延而出,如同拥有生命的蛛网,瞬间将前方一具用以测试功法强度的假人笼罩。
+下一刻,那具假人内部的状况,竟以真气光影的形式,被蛛网捕捉、分析,并实时投射在数堂巨大的展报旁!众人清晰地看到,假人体内代表真气流转的线路图被迅速勾勒出来,何处流畅,何处拥堵,何处消耗巨大,甚至其中几个关键部位的负荷数据和预估成本,都以飞快跳动的数字形式呈现出来!
+“机制解析完成。”葫芦侠目光如电,锁定了那副立体线路图最核心、也是负荷最重的一个节点,“此乃其先天内耗破绽,破!”
+他并指如剑,凌空向着光影图中那个节点虚虚一点。
+“咔嚓——!”
+令人牙酸的碎裂声响起。那具足以承受千斤巨力的玄铁假人,并非被外力击碎,而是从其内部结构薄弱之处,分崩离析!仿佛支撑它的内在逻辑瞬间被抽空,整个结构轰然解体,散落一地!
+全场陷入了久久的沉寂。
+随后,掌声与喝彩声如山崩海啸般爆发!这是用爬虫之道,找到要害,直指功法根基的逻辑谬误与结构缺陷的生动诠释!这蛛丝数据抓取掌所展现的,是一种超越招式层面的破解能力!
+“了不得!当真了不得!”评委席中央,长老们抚掌大笑,眼中尽是激赏,“此子已得格物之妙,近乎致知矣!”
+最后,卡卡侠、溜溜侠、可可侠三女飞身上台,将已演练纯熟的“柯西-黎曼-刘维尔三才阵”运转到极致。但见溜溜侠的绝对防御环光华流转,泼水不进;可可侠的稳固界限如大地承托,安如磐石;卡卡侠的多维攻击曲面则从环与界的庇护中诡谲探出,从四面八方发起连绵不绝又精准致命的打击。三才一体,守无可破,攻无可防,将数理武学那种特有的、严谨又充满无限可能的美感与威力,展现得淋漓尽致,将全场气氛推至最高潮!
+脑学数堂,以一种横空出世的姿态,震撼了整个武林大会。
+然而,武林终究是现实之地。脑学数堂的数理武学虽然理念超前、创新惊人,震撼全场,但当他们一路披荆斩棘,真正杀入最后与那些底蕴深厚的大门派对决时,那令人窒息的、全方位的差距,还是清晰地显现出来。
+机械门直接拉出了数尊庞大的自行机关战阵,真气驱动的弩炮与喷火器轮番演示,那已不是个人武功能抗衡的人力,而是近乎军队的武力和财力的碾压。经管门不演示拳脚,而是展开了一幅覆盖半个展示台的、巨大无比的千亿物资钱粮流转推演沙盘。
+脑学数堂的数理功法,在逻辑的优美、创新的锐气上或许不输,但在资源、体量、成熟度以及最现实的江湖掌控力上,还是有很大的差距。
+最终,金鼓声歇,名次公布。
+脑学数堂未能摘取那枚象征最高荣誉的武林魁首金令。
+他们得到的,是一块同样沉甸甸、雕刻着苍生社稷图案的玉牌,上面刻着“秉公习武馆武林大会·最佳报国创新大奖”。
+夕阳的余晖,将武术交流中心金色的琉璃顶染成一片悲壮而辉煌的赤红。广场上,获奖的大门派在欢呼,在庆祝。脑学数堂的众人聚在自家展位前,看着手中那块玉牌,又望望远处被人群簇拥的金色令牌,带上了几分不甘与落寞。
+葫芦侠静静地看着同伴们。他想起今日数理武学初露锋芒时,全场那震撼的寂静与随后爆发的喝彩。他忽然笑了。不是强颜欢笑,而是一种从心底涌上来的、坦荡又开阔的笑意。他解下腰间那个陪他走过西南、熬过深夜的朱红葫芦,拔开塞子,仰头灌了一大口自酿的、略带辛辣的醒神露。然后,他用袖子抹了抹嘴,声音清朗地响起,走到那幅《数理报国图》前,手指抚过上面金色的脉络:“咱们今天,输了吗?并没有。咱们今天,让全天下都看见了,数理不是奇技淫巧,是能扎扎实实解决问题、能报效国家的真功夫!”
+他转身,目光炯炯地扫过每一张年轻的脸庞:“咱们找到了差距,不丢人!丢人的是看见了差距就怂了!咱们更证明了,数理武学这条路,从心法到实战,是通的,是亮的!这比拿一块冷冰冰的金牌子要重要得多。”
+他顿了一下,手指向远方,那里落日正沉入连绵的群山:“咱们的路,长着呢!咱们要的,是这片江山社稷,因咱的数理,更稳,更好!”
+少年们看着他,看着他眼中那毫不作伪的豪情与坚定,看着他被夕阳勾勒出的、挺拔如松的身影。胸膛里那股因为未得第一而憋着的气,忽然就散了,转而化作了更汹涌、更澎湃的热流!是啊,我们证明了数理的力量,我们还有无限将来!这感觉,比拿到一块金牌,更让人血脉贲张!
+“对!葫芦兄说得对!”
+“咱们的路,才刚开始!”
+“回石室!复盘!接着练功!明年再来!”
+不知谁先喊了一嗓子,少年们轰然应和,脸上重新焕发出光彩,那点不甘与落寞被昂扬的斗志冲刷得干干净净。他们大笑着,互相捶打着肩膀,在紫金山脉壮丽的落日余晖下,紧紧围拢在一起,爆发出的笑声与吼声,冲霄而起,仿佛要撕裂这漫天晚霞。
+葫芦侠看着这群生死与共的同伴,心中一片滚烫宁静。他举起手中的葫芦,朗声道:“来,以此露代酒,敬咱们的脑学数堂,敬咱们的报国路,敬咱们的将来!”
+“干——!”
+少年们纷纷举起茶杯,碰在一起,发出杂乱却无比热烈的脆响。
+夕阳将他们的影子长长地投在空旷的广场上,交织在一起。
+属于脑学数堂的传奇,或许没有在今日登顶武林之巅,但它那以数理为骨、以报国为魂的鲜明印记,已深深地刻进了秉公习武馆的骨血深处,也刻进了这个春天,所有见证者的心里。
+江湖路远,道阻且长。然,星星之火,可以燎原。
\ No newline at end of file
diff --git a/docs/novel/第06章.txt b/docs/novel/第06章.txt
new file mode 100644
index 0000000..8e62d25
--- /dev/null
+++ b/docs/novel/第06章.txt
@@ -0,0 +1,102 @@
+葫芦侠行 第六章
+
+秋风再次染黄紫金山时,一个身影背着巨大的行囊,踏着山门石阶缓缓而上。正是阔别两年有余的欧阳侠。
+他变了。皮肤被海风和异域阳光镀成了深铜色,眼眸里沉淀着远航者特有的深邃与沧桑,昔日那份跳脱飞扬的少年气,化为了沉稳开阔的气度。行囊鼓胀,装的不只是衣物,更有无数卷海外手稿,以及那股几乎要满溢出来的、对崭新世界的炽热向往。
+回到故地的第一夜,他便在葫芦侠那间堆满算稿的地下石室角落,与葫芦侠、Eddie侠彻夜长谈。灯光映着他兴奋发亮的脸庞,话语如开闸潮水,汹涌澎湃。
+“你们无法想象,葫芦侠,Eddie侠,”欧阳侠的声音因激动而微颤,手指在粗糙的木桌上比划,仿佛在描绘那片新大陆的轮廓,“那边的计算门,根本不止是门派,那是一个全新的世界!他们用金属和琉璃制成的盒子,能在一息之间完成我们需推演数日的复杂计算;他们建造的互联网,能让千里之外的人如同对面交谈,知识传递不再受山海阻隔!更别提那些能在天上飞行的铁鸟,能在海底潜行的巨舟……”
+他从行囊中珍重取出一叠手绘图纸,上面是繁复无比的线路与符号。“看,这是他们一种基础算法,叫做排序。看似简单,却能以数理穷尽一切可能路径,找出最优解。”
+他又拿出一枚以奇异透明材质封装的、指甲盖大小的薄片,“这叫集成电路,浓缩了无数计算逻辑。我们这边需要一间屋子布置的算阵,他们以此一片即可替代。”他眼中燃烧着近乎虔诚的光芒,“这不仅仅是奇技淫巧,葫芦侠,这是一种全新的、理解并塑造世界的方式!是真正的格物致知走到了我们难以想象的前沿!”
+他猛地抓住两位老友的手臂,目光灼灼地轮流看着他们:“跟我走!我的船队三个月后再次启航,直抵新大陆!那里的‘硅谷’、‘麻省’,大门向有才华的探索者敞开!我们一起去!把紫金山的根底,与那边的枝叶嫁接,我们能创造出的东西,绝对能震撼整个武林,不,是改变这个时代!别困在这小小的江湖里了,天地大得很!”
+Eddie侠斜倚在墙边,指间一枚铜钱飞速旋转,反射着灯光。他沉默地听着,眼神从一开始的戏谑好奇,渐渐变得专注,最后化为一片深沉的悸动。欧阳侠描述的那个世界,充满了未知、挑战、与无限可能的创造,深深击中了他骨子里那份不安分与渴望打破一切陈规的天性。铜钱“啪”地一声被他攥入掌心,他抬起头,眼中锐光一闪:“听起来……有点意思。我也想去见见世面。”
+葫芦侠却陷入了长久的沉默。他抚摸着欧阳侠带来的图纸和芯片,感受着其中精妙绝伦却又冰冷陌生的逻辑之美。心底有一股热流在冲撞,那是求知欲与探索欲的本能呼应。欧阳侠描绘的图景,确实令人心潮澎湃。但……他抬起头,望向石室墙壁上,那幅略显陈旧却依旧庄重的《数理报国图》。望着图中大宋山河的轮廓,那些代表关隘运河的节点。
+“欧阳,”葫芦侠终于开口,声音有些干涩,却异常清晰,“你说的,我信。那必定是个精彩绝伦的天地。但是……”他顿了顿,手指无意识地划过桌面上另一叠稿件,那是脑学数堂准备为附近学校勘测地形的草图,“我的根,好像已经不知不觉,扎在这片土里了。这里有我刚刚起步的堂口,有一群同道,有无数我看得见、摸得着、也觉得自己或许能尽一分力的难题……这些问题,或许不如你所说的什么硅谷精妙,但它们是万千黎庶每日面对的切实困苦。”
+他看向欧阳侠,目光复杂,有向往,有遗憾,更有一种逐渐清晰的坚定:“你向往的是大海星辰,是探索未知的边疆。我敬佩,也羡慕。但我选择的,或许是脚下这条更泥泞、更缓慢的路。让这里的计算,不止存在于海外奇谈,也能真正造福此地的山河百姓。你的路在前方,我的路……或许就在脚下。”
+欧阳侠眼中的炽热稍稍降温,取而代之的是一种理解的深沉。他拍了拍葫芦侠的肩膀,力道很重:“我懂。人各有志,不可强求。只是……可惜了。你若去,成就必定非凡。”他转而看向Eddie侠,眼中重新燃起期待,“Eddie侠,你呢?这片江湖,规矩太多。不如跟我去搏一片新天?”
+Eddie侠把玩着铜钱,没有立刻回答。他看看目光灼热的欧阳侠,又看看沉默却坚定的葫芦侠。海外的新奇与自由,对他有着致命的吸引力。但紫金山……这里毕竟有他熟悉的一切,有斗嘴的同伴,有无需多言的默契,也有他那一套虽显另类却自在的活法。
+Eddie侠最终咧嘴一笑,笑容里有些惯常的玩世不恭,眼底却藏着认真,“我也得想想,是去那边,还是留在这儿。待你的船队出发前,我给你答复。”
+“好!”欧阳侠用力点头,“我也正好要回老家一趟,处理些船务,探望父母。三个月后,码头见。希望那时,”他看向两位老友,声音低沉下来,“我们都能做出不让自己后悔的选择。”
+那一夜,石室的灯火亮到很晚。关于未来、关于道路、关于家国与世界的讨论,时而激烈,时而沉寂。窗外秋风呜咽,仿佛在为即将到来的离别与抉择低吟。
+欧阳侠离开后不久,甲亢侠也结束了他的教习队任期,风尘仆仆地回到了紫金山。然而,这位带着西南山野阳光与尘土气息归来的图形天才,很快便与葫芦侠在脑学数堂的发展路径上,发生了激烈的碰撞。
+甲亢侠带回来的,不仅仅是一身历练后的沉稳,更是一种注重实用产出的新观念。在西南,他亲眼看到简陋但设计巧妙的引水竹渠,如何救活干涸的梯田。他坚信,数理的价值必须立刻体现在解决实际问题上,而且要以最快、最有效的方式。
+“葫芦兄,”在一次堂务会议上,甲亢侠指着墙上那幅《数理报国图》,眉头紧锁,“咱们的报国,不能总停留在图上谈兵,或是等着武林大会一鸣惊人。必须立刻找到能见效的抓手!我认为,堂口当前重心,应全力承接武堂乃至地方官府的实务——清丈田亩、核算仓库、优化本地匠作铺的物料切割方案!这些活计虽然繁琐,但来钱快,见效快,也能立刻让堂口上下看到成果,稳住人心!我们中的大部分人力应投向实务!”
+葫芦侠却摇头:“甲亢侠,务实没错。但脑学数堂的立身之本,在于学理!若一味追求短平快的实务,那便是失了我们的理论特色。我们要做的,是从这些实务中提炼出共通的数理模型,形成可复制、可推广的方法,乃至升华为新的武学理念!这需要沉下心来做基础研究,需要容忍短期内看似无用的探索。我们追求的长远价值,岂是丈量几亩田能比?”
+“长远?眼下堂里银钱还能支撑多久的长远?”甲亢侠提高了声音,他负责堂口部分物资调配,深知柴米之贵,“大家要吃饭,器械要维护,实验要耗材!没有持续的进项,再高妙的理念也是空中楼阁!等我们饿着肚子推演出惊世阵法,堂口早就散了!”
+“所以就要放弃根本,急功近利吗?”葫芦侠也激动起来,“我们成立数堂,是为了改变江湖乃至朝廷对‘数理’的认知,是为了培养下一代的‘数理之魂’!若现在就把自己局限在打算盘、量土地上,与最初的抱负岂非背道而驰?”
+两人各执一词,争论越来越激烈。甲亢侠指责葫芦侠理想主义,不接地气;葫芦侠则认为甲亢侠被西南的艰苦磨去了锐气,变得短视。昔日的默契与友情,在现实路径的选择前,出现了清晰的裂痕。堂内其他成员也隐隐分成了务实派与理想派,气氛一度有些压抑。
+当晚,葫芦侠心烦意乱,独自登上紫金山后崖。却见甲亢侠早已坐在那里,对着山下的城墙出神。月光洒在他身上,勾勒出略显疲惫却依旧挺直的背影。
+沉默许久,甲亢侠先开口,声音平静了许多:“在六都,有个孩子问我,先生,你教的这个勾股定理,除了量房子,还能做什么?我当时没答上来。后来看到他们用相似三角形原理,估算出了后山一颗高树上的鸟窝位置,只是为了掏几个鸟蛋。那一刻,我突然觉得,也许最高的道理,就藏在最寻常的运用里。我着急,是怕咱们的道理,还没等到升华,就先被饿死了。”
+葫芦侠在他身边坐下,也望着翻涌的云海:“我明白你的担忧。银钱确是燃眉之急。但是脑学数堂的成长,需要时间,需要耐得住寂寞的投入。”
+又一阵沉默。山风凛冽。
+“也许,”甲亢侠缓缓道,“我们都没错,只是看到的阶段不同。务实是为了活下去,活得好;求理是为了活得有意义,走得远。二者并非水火不容。”
+葫芦侠心中一动:“你的意思是……”
+“基础研究不能停。”甲亢侠转头看他,眼中重新有了光芒,“但同时,我们的业务也需要进一步扩大,要立刻开辟稳定财源,不能只靠承接零散杂活,要有规划、有选择地接洽那些既能赚钱,又能验证、丰富我们理论的实务项目。”
+葫芦侠眼睛亮了:“不错!而且,弟子们参与一些实践的项目,本身就是绝佳的实践!从实践中发现问题,再带回堂中研究,形成理论,再用理论指导新的实践……如此循环,务实与求理,便可相辅相成!”
+两人相视,之前的争执与隔阂,在更高一层的共识下冰雪消融。他们终于明白,真正的道路,或许不是非此即彼的选择,而是在现实的重重约束中,守护理想之火不灭,并为其添薪加柴。
+翌日,葫芦侠在堂会上共同宣布了新的方略:成立研理与务用两部,目标是在保障堂口生存的前提下,稳步推进核心数理武学研究,将分歧化为合力。
+但是银钱的短缺,始终是悬在脑学数堂头顶的利剑。就在葫芦侠为此辗转反侧之际,两位意想不到的访客联袂而至——刚从西南归来的骆骑侠和七柒侠。
+三人于葫芦侠那间简陋的石室坐定。骆骑侠依旧带着沙漠与骆驼般的热烈气息,开门见山:“葫芦兄,我与七柒侠此次前来,是想与你商量一件大事——成立一个属于我们弟子的闽浙商会。”
+“商会?”葫芦侠一愣。
+“正是。”七柒侠接口,他如今言谈举止已颇有气度,“我与骆骑兄详谈过。他在泉州家族本就家大业大,涉足海贸,熟悉商路与货殖;我在机械门许久,也算认识不少人。而葫芦兄你,脑学数堂虽不直接营商,但你们产出的数理模型,乃是商道中最高级的货品——它能帮人省钱增效,价值无可估量。”
+骆骑侠接着道:“眼下东南沿海,特别是闽浙一带,商贸日益兴盛,但各商号多是单打独斗,信息不畅,算计不清,吃了无数暗亏。我等紫金山弟子,散落各门,所学甚杂,若能联合起来,以商会为平台,各展所长,何愁不能在这商海大潮中,闯出一片天地?所得利润,可按贡献分红,而商会本身,亦可成为支持各堂口,特别是你们数堂这类耗资巨大的新兴堂口,最稳定的钱囊!”
+这个构想不可谓不宏大,也极具诱惑力。若能成功,脑学数堂的资金困境将迎刃而解,甚至可能获得远超武堂拨付的资源。但葫芦侠却陷入了沉思,顿了顿说:“骆骑兄,七柒兄,此议甚好,于我堂确是雪中送炭。”葫芦侠斟酌着词句,“然我们的商业不止是要有利于商,更要有利于国民。我的想法如下:商会之利,当分作三份。一份用于商会自身发展分红;一份设立助学基金,专用于资助各堂口,特别是如数堂这般偏重基础与公益的研究;这第三份,也是最重的一份,则用于经世实务,主动寻找、承接那些于国于民有利,却商业回报不显,甚或需倒贴钱的实务项目。如此,商道不忘初心,学问不离实地,或可两全?”
+二人闻言,眼中亮起光彩。这个构想,将商业利润从单纯的目的,转化为了实现报国理想的手段。
+“而且,”骆骑侠有点激动,补充道,“商会本身,亦可成为数堂弟子极佳的历练场与数据来源。商场如战场,信息纷杂,决策风险高,正是验证、完善预测算法的绝佳沙盘。而弟子们参与商事,了解民生经济实际运作,对其将来无论是钻研学问,还是出仕为官,皆有裨益。”
+思路豁然开朗。葫芦侠起身,郑重向两人拱手:“二位兄长高瞻远瞩,葫芦受教。此议,我鼎力支持。具体章程,我等可细细商议。”
+“闽浙商会”的构想就此萌芽。这不仅是一个解决经济困境的方案,更是一次将江湖所学、经世之志与时代浪潮相结合的勇敢尝试。
+商会之事刚刚有些眉目,山韵侠又带着一阵山风般的爽利气息,找到了葫芦侠,眉宇间带着一丝忧色与急切。
+“葫芦师弟,厚朴师兄有消息了。”山韵侠开门见山,“他随西域商队,深入了穷乡僻岭之地赈灾,传信回来说,那边情势复杂,他正在利用自己的高超医术救助当地伤病百姓。归期至少需一年后。”
+“厚朴师兄大义,令人钦佩。”葫芦侠肃然道。
+山韵侠道:“葫芦兄,你还记得我们两年前和厚朴侠的约定吗?我们说要一起创作出一部新戏。而如今厚朴侠为了天下苍生奔波,我们亦不能忘记我们的约定!这部新戏,我想记录一些秉公习武馆的好故事。“
+山韵侠眼中闪烁着坚定的光芒,“我带来我们话剧班子最有名的才子雅成师兄,想以厚朴西域之行,你去年带队武林大会,还有咱们紫金山诸多同道平日行侠济困的点点滴滴为蓝本,创作一部新戏,名字暂定《你好,冠军》。不独为厚朴,也为所有在各自道路上默默坚持、于逆境中追寻心中‘冠军’之道的同门呐喊!”
+雅成侠以笔力雄健、叙事缜密著称。他此时也缓步走入石室,对葫芦侠微微颔首,接口道:“山韵有感而发,我亦深以为然。江湖多传快意恩仇,少述前行道路之困难。我等欲以此剧,展现另一种侠者风貌——于无人处发光。葫芦师弟,望能不吝赐教,共成此作。”
+这个提议深深打动了葫芦侠。他回想起武林大会后的夕阳,同伴们由失落转为昂扬的面孔……无数情感与思绪在胸中激荡,确实需要一种方式去梳理、去表达、去共鸣。
+“好!”葫芦侠重重应下,“我愿尽数道来。这《你好,冠军》的冠者,不只是擂台胜者,更是心志之巅峰。”
+自此,三人常常聚于石室或听竹轩。葫芦侠讲述武林大会的细节、心路历程,山韵侠以诗家敏锐捕捉情感内核与戏剧冲突,雅成侠则以小说家的严谨构建框架、雕琢人物与台词。他们讨论“冠军”的定义,争论剧中人物的动机,时而激昂,时而沉默。在共同的创作中,葫芦侠也得以一次次反思自己的道路,那些困惑、挣扎、坚持,变得更加清晰和有力。
+剧本初具雏形。它并非简单的英雄颂歌,而是一幅群像画卷:一群性格迥异、身世背景不同的人为了同一个目标,共同奋斗。他们追寻着心中的冠军,路径不同,却同样充满坎坷与光辉。这部戏,仿佛成了脑学数堂乃至紫金山这一代年轻弟子精神世界的映照。
+就在脑学数堂于内外合力下渐有起色,一场毫无征兆的狂风暴雨,骤然袭向葫芦侠本人。
+起初只是些流言蜚语,在武堂某些角落悄然传播。指责脑学数堂不务正业,以奇技淫巧惑乱弟子心性且所授数理与正统武学根基相悖,进而影射葫芦侠结私营党。
+接着,有两位在武林大会后加入数堂、但不久因觉枯燥而退出的普通弟子,被某些人问询,之后便传出葫芦侠教学苛刻,不近人情。虽被大多数数堂弟子驳斥,但种子已然埋下。
+真正的危机在一个阴沉的午后降临。武馆的两位执事,面容冷峻,径直来到地下石室,当众向葫芦侠出示了一份盖有武林风纪监察会印鉴的文书。文书措辞严厉,声称葫芦侠蛊惑人心,妄图拉小团体动摇武学根基,妄图指其动摇武学根基,且僭越本分,有干预实务之过”,最后勒令:“葫芦侠,即刻退出秉公习武馆,并解散脑学数堂。”
+石室内一片死寂,所有人都惊呆了。葫芦侠接过文书,手很稳,逐字看罢,抬头看向两位执事,声音平静得出奇:“弟子愚钝,不知所犯什么门规。弟子只知,我传播数理武学,是为了培养数理武林人才,传播武艺,报效国家。何罪之有?”
+一位执事冷冷道:“证据自在监察会案头。此举乃为维护武馆正统。若不服,亦可申辩,但是堂口事务需要冻结,等待一年后赴京城接受质询审查。只是……”他顿了顿,意味深长,“审查过程,短则数月,长则经年。期间,脑学数堂需暂停一切活动,等候发落。而审查结果,亦未必如你所愿。”
+话中之意,再明白不过。要么服软,个人退出,堂口解散,一切烟消云散;要么抗争,但堂口将被无限期冻结,而他自己需长时间地被监视,等待京城长老们的质询。
+“葫芦侠!”弟子们忍不住
+葫芦侠再次看向那纸裁决,又环视了一圈石室内那一张张或愤怒、或焦急、或茫然、或充满信赖的年轻面孔。他忽然感到一种奇异的平静。这并非屈服,而是一种清明。对方的根本目的,或许并非他个人,而是不认同脑学数堂所代表的这种离经叛道的探索路径。而且其中,必有奸人作祟,有意加害于他。直接对抗,以卵击石;就此放弃,心有不甘,亦对不起所有同行者。
+那么,路在何方?
+接受调查,看似被动,却可能是唯一一条能让脑学数堂之名与理存续下去,并有机会得到正式认可的道路。尽管前路艰险,但至少,保留了火种。而冻结期间,堂口表面暂停,实则……他看向众人,一个念头逐渐清晰。
+“弟子,”葫芦侠终于开口,声音清晰地回荡在石室中,“愿接受审查。”
+两位执事似乎有些意外他的选择,对视一眼,道:“既如此,予你一年时间准备。一年后的今日,需至京城监察会报到。此一年内,脑学数堂需封存,不得公开授课、聚众研讨、承接外务。你若逾期不至,或审查不通过,则前令即刻执行。”
+“弟子明白。”葫芦侠躬身。
+执事离去,石室内压抑的寂静被打破,顿时炸开。
+“葫芦兄!岂能如此!”
+“这分明是诬陷!凭什么!”
+“我们联名上书!”
+葫芦侠抬起手,再次让众人安静下来。他的目光缓缓扫过每一个人,最后对着最开始加入的几位弟子说:“我走之后,堂中一切事务,托付于你们。明面遵从禁令,暂停公开活动。但研理不可废!可化整为零,以私人交流、读书会,继续推进。我相信你能把握好分寸。”
+“诸位,”葫芦侠对所有人道,“此一年,是难关,亦是沉淀之机。外界的风雨,浇不灭真正的火种。望各自精进,待我归来。”
+他没有多解释自己的决定,但那份沉稳与决绝,感染了众人。愤怒渐渐化为凝重与决心。
+消息很快传开,震惊了整个紫金山。孔锝侠闻讯,沉默良久,只对葫芦侠说了一句:“路是自己选的,跪着也要走完。记住,常青藤的骨头,是折不弯的。”
+葫芦侠将一应事务快速交割,又私下与骆骑侠、七柒侠深谈,确保商会之事不受牵连,并能暗中为冻结期的数堂提供一定支持。做完这一切,他给自己留下了三天时间。
+这三天,他谁也没见,独自一人背着简单的行囊,来到了紫金山后山一处几乎被人遗忘的废弃窑洞。相传前朝曾有一位大儒在此谪居悟道。洞内昏暗潮湿,仅有一榻、一桌、一灯。
+葫芦侠盘坐于冰冷的石榻上,闭目入定。
+并非修炼武功。而是将所有经历、选择、碰撞、挫折,在心底一一铺陈开来。无数念头、矛盾、情感,如潮水般冲击着他的心神。为何执着于此地?为何选择这条看似更艰难的路?仅仅是因为责任?因为承诺?还是因为内心深处,对这片土地、这方文明,有着无法割舍的认同与虽千万人吾往矣的信念?
+道在何处?不仅在海外新奇的算法中,也在这片古老土地亟待革新的肌体里;不仅在精妙的数理推演中,更在将其用于造福家国百姓的实践中。此行接受审查,是劫难,亦是弘道之机。若能使上层知晓、正视此道,哪怕只有一丝可能,也值得奋力一搏。
+念及此处,心中块垒渐消,灵台一片清明。困顿、委屈、愤怒,化为一种沉静而浩大的力量。他知道自己接下来一年需要做什么了——不仅仅是等待审查,更是要利用这段时间,潜心梳理自己的数理武学体系思想,将其从零散的灵感与实践,升华为一套更具说服力的理论阐述。同时,也需深入了解朝堂、江湖的规则与暗流,为将来迎来清白做好准备。
+三日不言不动,当第四日晨光从窑洞缝隙射入,照亮他沉静的面容时,葫芦侠缓缓睁开双眼。眸中再无迷茫与躁动,只有一汪深潭般的平静与坚定。
+龙场悟道,其道乃成。此道,是数理之道,是报国之道,亦是他在这个风云变幻的时代,为自己选择的行者之道。
+就在葫芦侠回到习武馆的时候,欧阳侠的船队归期已至。他的船队满载货物与憧憬,即将再次扬帆,驶向大洋彼岸。
+码头上,帆樯如林,海风猎猎。欧阳侠一身利落的航海装束,眉宇间是压不住的兴奋与期待。Eddie侠站在他身旁,背着一个不大的行囊,依旧是那副懒散模样,但眼神深处,是对未知旅程的跃跃欲试。两人显然已达成一致。
+葫芦侠赶到码头时,送行的人不多。欧阳侠屏退左右,三人再次相聚。
+“决定了?”葫芦侠看向Eddie侠。
+Eddie侠耸耸肩,抛接着那枚永不离身的铜钱:“这边竞争太大,江湖太难,而且生活总是一成不变。我还是想去世界看看。”
+欧阳侠看向葫芦侠,目光复杂:“葫芦侠,最后再问一次。我还是希望你同我们一道离开。海上虽然风险大,但天地广阔,凭你我才学,何处不能容身?现在上船,还来得及。我们一起,去那片新天地,去改变那里的规则!”
+葫芦侠望着眼前两位挚友,又望向停泊在港中的、即将远航的船,心中涌起万千感慨。海天相接处,是无限的未知与可能;而身后,是重重关山与不可测的庙堂风云。
+他缓缓摇头,脸上露出一丝平静的笑容:“欧阳,Eddie,你们的路在海的那边,是探索,是开拓,是创造属于你们的新传奇。我由衷为你们高兴,也羡慕你们。”
+他顿了顿,转身望向内陆方向,目光仿佛穿透了千山万水,“而我的路,就在这了。无需多言,我相信有一天,我们一定会带着胜利再见的。”
+他看向欧阳侠:“你常说我固执。或许是吧。但我总觉得,若人人都因难而退,因旧而厌,那这片生养我们的土地,将真正沉沦,再无新机。”
+欧阳侠默然良久,眼中掠过一丝了悟与深深的惋惜。他伸出拳头,与葫芦侠的拳头重重一碰:“我明白了。你在故土披荆斩棘,我在异域开疆拓土。将来某日,你我隔海呼应,共成一番大业。保重,葫芦侠,保重。”
+Eddie侠也收起戏谑,拍了拍葫芦侠的肩膀:“葫芦侠,别死脑筋。实在混不下去,就来找我们。”
+葫芦侠笑道:“你们也是。海上风波恶,保重。如果见到什么新奇事物,记得托梦与我诉说。”
+三人大笑,笑声中充满了离别的不舍,更有对彼此道路的尊重与祝福。没有涕泪纵横,只有江湖武侠间深沉的理解与支持。
+号角长鸣,催促登船。欧阳侠与Eddie侠最后用力抱了抱葫芦侠,转身大步走向船队,再未回头。他们的背影挺拔,义无反顾地投向浩瀚的蔚蓝。
+葫芦侠独立码头,望着帆影渐远,最终消失在水天之际。海风带着咸涩的气息扑面而来,这一次,吹走的不只是挚友,还有一个充满诱惑的另一种可能。
+但他心中却异常平静,甚至更加坚定。歧路分襟,各赴前程。同舟之情,永存心底。无论出海还是守土,皆是追寻心中大道的征途。葫芦侠最后望了一眼空茫的海面,深吸一口气,毅然转身,向着内陆,向着那不可知的风波,迈出了沉稳而决绝的步伐。
\ No newline at end of file
diff --git a/docs/novel/第07章.txt b/docs/novel/第07章.txt
new file mode 100644
index 0000000..2d3e5df
--- /dev/null
+++ b/docs/novel/第07章.txt
@@ -0,0 +1,123 @@
+监察会的裁决如山,不容置辩。葫芦侠需在十日内搬离秉公习武馆,脑学数堂一切公产暂时封存,私产亦被严格核验、近乎冻结。他背着那个不离身的葫芦和一只单薄的行囊,在无数同情的目光中,独自走下了紫金山。
+秋意已深,山道落叶飘零。葫芦侠驻足回望,那巍峨的秉公习武馆匾额在苍茫山色中依旧醒目。但是他心中没有多少自怜,反倒奇异地平静。
+就在他思忖该往何处栖身时,一辆青篷马车从不远处的岔道驶来,稳稳停在他面前。车帘掀起,露出图像门白维侠那张总是带着几分跳脱笑意的脸。
+“葫芦兄,山水有相逢,此路暂不通,何不上车,另辟蹊径?”白维侠笑嘻嘻地,话语却意有所指。
+葫芦侠微怔,旋即了然。上次通过图象门认识了白维师兄,想必他已知晓自己处境,,便赶来相助葫芦侠有些感动。他不再多问,道了声谢,便上了马车。
+马车并未入城,反而在紫金山外围一片略显偏僻、但市井气息渐浓的坊区边缘停下。白维侠引着他走进一条窄巷,巷底有一处独门小院,院墙斑驳,门扉古旧,但还算整洁。
+“此地原是一农作研究老专家之所,清静。老人家年前回乡养老,屋子托我照看。虽简陋,胜在独门独户,无人扰攘。”白维侠推开院门,里面是个狭长的天井,正对一间不大的堂屋,左右各有一间小小厢房,屋后似乎还有个小灶间。家具寥寥,积着薄灰,但结构完好,窗纸也全。“马车留给你代步,采买办事方便些。灶间有简单的锅碗,后院井水尚甜。每月租金……”他摆摆手,“等你解除冻结,重回堂口再说。眼下,安心住下便是。”
+雪中送炭,情谊深重。葫芦侠深深一揖:“白维兄高义,葫芦侠铭记。”
+“客气啥,”白维侠拍拍他肩膀,笑容真诚了些,“我看好你那套数理经纬的东西。江湖路长,跌一跤不算啥,爬起来拍拍土,路还宽着呢。走了,有事联系我。”说罢,便如来时一般洒脱地离去。
+葫芦侠放下行囊,开始动手收拾。扫去积尘,擦拭门窗,将从武堂带出的有限铺盖铺在硬板床上。又将堂屋一角稍作整理,摆上一张瘸腿方桌和一把旧椅,便算作书案。他从行囊中取出那几本最紧要的手稿、算筹和笔墨,整齐置于桌上。最后,将葫芦挂在床头。这家徒四壁却终于完全属于自己的小小空间,便是他未来一年的战场。
+搬入新居的第三日傍晚,院门被叩响。开门一看,竟是三位旧邻——阳睿侠、玉斌侠与坤坤侠。他们同在武堂西南侧明理居住过,一起在井边打过水,在夏夜纳凉时聊过天。
+“葫芦兄!真搬这儿来了!”阳睿侠性子爽直,手里还提着一包油纸裹着的酱肉,“听说你搬出来了,哥几个寻思着,怎么也得来看看。这地方……清静,清静好!”
+玉斌侠原来神出鬼没,听闻葫芦侠的消息,赶忙联系这几位老邻居一起来看望他:“葫芦兄莫要灰心。武馆里不少师兄弟,都信你不是那样的人。”
+坤坤侠则挠挠头,憨厚地笑着,手里是一包还冒着热气的锅包肉。
+一股暖流瞬间冲散了葫芦侠连日的孤寂。他连忙将三人让进尚且简陋的堂屋,无茶无盏,便以清水相待。四人围坐,话匣子打开了。他们避开敏感的堂口事务与官司,只聊往日明理居的趣事,聊武堂近日的传闻。没有客套,没有试探,只有最朴素的关心与陪伴。
+“葫芦兄,总在这小院里闷着也不是事儿,”阳睿侠提议,“走,咱哥几个带你逛逛这边!紫金山脚下下马坊,虽比不得城中繁华,可烟火气足,有意思的玩意儿多着呢!散散心去!”
+盛情难却,葫芦侠也正想熟悉周边,便锁了院门,随三人步入坊间。
+下马坊果然热闹。暮色四合,华灯初上。街道两旁店铺林立,酒旗招展,饭食香气混杂着各种货品的味道扑面而来。卖炊饼的吆喝声洪亮,铁匠铺传来叮当脆响,绸缎庄前妇人讨价还价,孩童举着糖人在人群中穿梭嬉笑。说书摊前围了一圈人,惊堂木响,故事正到精彩处;简陋的戏台上,有草台班子咿咿呀呀唱着俚俗小调;甚至还有杂耍艺人当街吐火、顶碗,引来阵阵喝彩。
+这是与武馆内充满竞争气息截然不同的另一个世界,充斥着最本真的人情冷暖。葫芦侠跟着三位旧邻,在人群中缓慢穿行,听着他们如数家珍地介绍哪家酱鸭滋味足,哪家铁匠手艺好,哪家书摊常有孤本,哪处巷子里的老郎中正骨是一绝。他感到自己仿佛从高空缓缓落地,双脚重新踏在了土壤上。
+他们在一处临街的馄饨摊坐下,要了四大碗热气腾腾的牛肉炒饭。葫芦侠吃得额头冒汗,连日来的紧绷心弦,在这市井的喧嚣与食物的温暖中,不知不觉松缓了许多。
+“看见没,葫芦兄,”玉斌侠指着街上熙攘的人群,轻声道,“咱们练武、学理,争来斗去,说到底,不就是为了让这些人,能让日子过得安稳些,脸上笑容多些么?”
+葫芦侠心中一震。是啊,无论庙堂之高,还是江湖之远,最终的指向,不正是这万家灯火,人间烟火么?自己钻研数理,渴望上报国家,其最朴素的内核,或许正是希望这街市更繁荣,这炒饭更香甜。
+那一晚,葫芦侠回到陋室,心中那份因被迫离开熟悉环境而产生的漂泊感淡去了许多。他点起油灯,在瘸腿书桌前坐下,开始梳理思绪。市井的烟火气,仿佛为他冰冷的数理世界,注入了一种温热的质感。
+数日后,驿站送来一封厚厚的信,来自泉州,是骆驼侠的亲笔。信很厚,措辞依旧是他那般豪迈洒脱的风格,但字里行间充满了关切。随信一同寄来的,还有一个用油布仔细包裹的册子。葫芦侠打开,墨香犹存,正是他当年在常青藤教习队期间,于西南六都夜夜增补修订的那本《葫芦瞎说》草本!册子边角已有磨损,里面除了他最初的见闻杂感、数理游戏,还留着许多灵感与涂鸦。抚摸着这册失而复得的草本,葫芦侠眼眶发热。骆驼侠在附笺中说:“清理行囊,见此旧物,知兄必珍视。遂寄还。愿兄睹物思初心,知前路虽坎,然志不可夺。弟在东南,与昔日兄弟们一道,静候兄长风破浪之日。”
+初心……葫芦侠一页页翻动着《葫芦瞎说》草本。那些稚拙的图画,心中感慨万分。他想起自己在泉州的预备营的时候,那时除习武以外,没有过多的杂扰之事。大家都卯着一个目标,那就是站上武林的巅峰,成为角儿。出征前往秉公习武馆当天,乡贤毕至,师长咸集,给他送上最真挚的祝福。大家都期待着,这些新生力量,能为这片东南海滨,带回新的生机。一切仿佛就在昨日。
+而今,他面临的局面复杂了何止百倍。江湖倾轧,理念之争,权力制衡,暗箭难防。但拨开这些迷雾,自己的初心仍为改变,经世济民的愿望仍充盈心中。
+“是啊,志不可夺。”葫芦侠合上册子,轻声自语。使其重新变得沉静而深邃。
+他望向窗外沉沉的夜色,目光渐趋坚定。既然有一年光阴,既然暂时远离了武馆的喧嚣与掣肘,何不将这陋室,变为真正的修炼之地?不仅要应对未来的审查,更要利用这难得的清净,
+决心既定,周身疲惫仿佛一扫而空。他重新铺开纸笔,开始写下未来一年的计划纲要。生活仿佛重回平静。
+然而,平静很快被打破。搬入约莫七八日后的一个深夜,万籁俱寂,葫芦侠正于灯下推演一个条件概率模型,忽听得院门被叩响。
+“咚、咚、咚……”
+声音不大,但在这静夜里格外清晰,而且……透着一股莫名的急促。
+葫芦侠一怔,这么晚了,会是谁?他放下笔,起身走到院中,隔着门问:“哪位?”
+叩门声戛然而止,门外一片寂静。
+葫芦侠皱眉,侧耳细听,仿佛有极轻微的、衣物摩擦的窸窣声,以及……迅速远去的、几乎微不可闻的脚步声?他猛地拉开门闩,推开院门。
+门外巷弄空无一人,只有清冷的月光洒在青石板上,映出自己孤零零的影子。夜风拂过,带着深秋的寒意。葫芦侠心中一凛,运起内力,双耳听力瞬间提升,捕捉着方圆数十丈内的动静。唯有远处隐约的更梆声,和不知谁家婴儿的夜啼。
+是谁?恶作剧?还是……他想起自己的处境,心中一沉。是警告?是窥探?
+接下来两夜,同样的事情再次发生。总是在深夜,急促的敲门声响起,等他开门,门外总是空空如也。对方似乎对时机把握极准,且身法轻盈迅捷至极。第二次时,葫芦侠在听到叩门声的瞬间,便施展出高等逮术中的追踪步法,身形如电射出院门,朝感知中那细微脚步声消失的方向急追。他的轻功在同辈中已属佼佼,追踪之术也得过真传,然而在曲折的巷弄中追出百余丈,竟连个影子都没看到!对方似乎凭空消失,又或者,其遁走之术精妙到了匪夷所思的地步,竟未留下任何可供持续追踪的气息轨迹。
+葫芦侠站在空寂的街口,心中惊疑更甚。来者绝非寻常人物,其轻功与反追踪能力,恐怕还在自己之上。是敌?若是敌,为何只敲门不入?是友?若是友,又为何避而不见?
+此事如鲠在喉,让葫芦侠在陋室的平静生活中,蒙上了一层淡淡的阴影。他暗自警惕,夜间修炼、作息也更为警醒。
+第四夜,叩门声再次响起。葫芦侠这次没有立刻冲出去,而是屏息凝神,走到门后,沉声问道:“门外何人?若再戏弄,休怪葫芦侠无礼了。”
+门外沉默了一瞬,就在葫芦侠以为对方又要离开时,一个刻意压低、却依稀有些熟悉的清冷女声响起:“是我。开门。”
+葫芦侠心中一动,猛地拉开门。
+月光下,站着一位身着黑色夜行衣、以黑巾蒙面的女子。尽管遮住了大半面容,但那高清冽如冰的眼神,以及她脚边安静如影子般的巨犬——葫芦侠瞬间认了出来。
+“溜溜侠?!”他低呼出声,又惊又疑,连忙侧身,“快进来!”
+溜溜侠闪身入院,巨犬无声跟进。葫芦侠迅速闩好门,将她引至堂屋,点亮油灯。灯光下,溜溜侠拉下蒙面黑巾,露出那张依旧没什么表情、却明显带着一丝疲惫与凝重的脸。
+“你怎么……”葫芦侠有满腹疑问。
+“长话短说。”溜溜侠打断他,声音压得极低,语速却快,“我的真实身份,并非普通江湖客。我是京城有御赐铜符的暗桩捕快。”
+葫芦侠瞳孔骤缩,心中掀起惊涛骇浪。京城捕快?这身份比任何武林高手都更令人震惊。
+“数月前,我奉命潜入秉公习武馆。”溜溜侠继续道,目光锐利地注视着他,“因有密报称,武馆内有人假借研学之名,暗中结党,所图非小,可能涉及朝政。上峰怀疑,新兴的脑学数堂鱼龙混杂,或是其藏身、发展之所在。我的任务,是查出此人,搜集证据。”
+葫芦侠只觉一股寒意自脊椎升起。自己倾注心血的堂口,竟早已被朝廷密探盯上,还成了怀疑对象!
+“我暗中观察许久,”溜溜侠语气稍缓,“发现堂中虽有诸多奇思妙想,行事略显不羁,但心思多在学问与实务,未见明显不轨之迹。反倒是一些外围人物,行踪诡秘,难以捉摸。然而,就在我逐渐缩小范围,欲深查之时,我的身份……似乎暴露了。”
+她眼中寒光一闪:“有人察觉到了我的探查。此人极为警惕狡猾,立刻改变了策略。他(她)不再试图隐藏自身,反而开始千方百计,要将水搅浑,并将嫌疑的焦点,引向最显眼的目标——也就是你,葫芦侠,脑学数堂的主理人。”
+葫芦侠瞬间明白了:“所以,那些流言,那两位被‘问询’后退堂弟子的说辞,乃至监察会那份措辞严厉的裁决……都是此人推动?”
+“十有八九。”溜溜侠点头,“暂停脑学数堂,一来可断绝我借堂口身份继续深入调查的途径,使我失去在明处的落脚点与掩护;二来,若能借此将你彻底扳倒,甚至安上霍乱朝政的罪名,那么真正的潜伏者,便可高枕无忧,甚至可能趁机将某些线索彻底栽赃于你,金蝉脱壳。”
+好狠辣的算计!葫芦侠背心渗出冷汗。自己竟在不知不觉中,成了别人暗斗的棋子和靶子。
+“我身份特殊,有严令不得轻易暴露,否则后患无穷,也可能打草惊蛇。”溜溜侠道,“此前几夜敲门即走,一为试探你此处是否安全,有无他人监视;二来,我也在犹豫,是否该将真相告知于你。毕竟,此事牵连甚广,你已知自身处境险恶,再卷入其中,恐更凶险。”
+她停顿片刻,看着葫芦侠:“但观察数日,又忆及往日你在堂中行事,磊落赤诚,绝非奸恶之徒。此番遭难,实属被累。我不忍见你蒙受不白之冤,更不愿让真正的蠹虫逍遥法外。故而,今夜冒险前来,据实以告。信与不信,在你。”
+话语如重锤,敲在葫芦侠心头。震惊过后,涌起的是对溜溜侠的深深感激,以及一股强烈的愤怒与决心。他起身,对溜溜侠郑重一揖:“葫芦信溜溜侠之言,更感激你的信任与坦言相告之恩!此事不仅关乎我个人清白,更关乎脑学数堂存续,亦关乎揪出武堂乃至朝堂隐患!你但有所需,葫芦愿竭力相助!”
+溜溜侠眼中掠过一丝极淡的欣慰,示意他坐下:“你有此心便好。眼下敌暗我明,不可妄动。我仍需暗中调查,你则需应对好明面上的麻烦——监察会的审查。”
+她分析道:“你的罪名,无非两条。其一,‘私自串通小团体,霍乱朝政’。此条看似严重,实则空泛。届时,只要红灰青蟹、脑学数堂核心成员,阐明结社仅为切磋学问、并无其他图谋,此条便有转圜余地。”
+“难点在第二条,‘所授有悖正统数理门武学,动摇根基’。”溜溜侠眉头微蹙,“此乃学理之争,最是缠夹不清。欲破此局,非朝夕之功。你需要有能真正说服那些宿老、证明自身道路不仅无害、反而有益于武学发展的、坚实且系统的理与据。这非口舌之辩,需真才实学,需能展示出旁人无法否认的价值与高度。”
+葫芦侠深以为然。这正是他近日苦思而不得其门之处。应该修炼什么武功,才能将其提升到足以回应动摇根基指控的理论层面?
+溜溜侠从怀中取出一枚不起眼的黑色铁片,形似某种特殊令牌的碎片,放在桌上:“此为我联络信物的一部分。我不能常来,易惹嫌疑。日后若有关键消息或需紧急联络,我会在深夜至此,以‘连续七下’为号的特定节奏敲门。你务必记清。”
+交代完毕,溜溜侠不再停留,重新蒙上面巾,如夜猫般悄无声息地离去,巨犬紧随其后,融入夜色,仿佛从未出现过。
+堂屋内,葫芦侠独坐良久,消化着这惊人的信息。真相远比想象的复杂,自己竟卷入了一场漩涡。但溜溜侠的信任与指点,又如同一盏灯,照亮了迷雾中的一条路径。
+他下意识地伸手入怀,触到了那个一直随身携带、却从未打开的青布锦囊。玎铧侠的话语在耳边回响:“若遇困厄之时……可打开一观。”
+葫芦侠不再犹豫,就着灯光,解开了锦囊的系绳。里面并无金银珠玉,只有一本薄薄的、用棉线装订的手抄册子,纸质泛黄,显是有些年头了。册子封面上,以清峻的楷书写着五个字:《统计学导论》。
+他心下一动,急忙翻开。册子内容不多,约莫七八十页,并非系统教材,更像是一位深谙此道者的读书札记与心得随笔。其中分门别类,以精炼的语言和图示,探讨了概率先验与后验之辩在料敌中的应用、“大量随机事件中稳定趋势之本质”、“由果推因之最大可能性原则”等等……
+这赫然是一本从武道修行视角,深入浅出诠释统计核心思想的秘籍纲要!其中精髓,与葫芦侠在武林大会展示、以及近来所思索的方向,惊人地契合,且立意更为高远,直指数理与武学交融的哲学根本。
+其中一页批注写道:“世之新学,常被诟病动摇根基。然真正动摇根基者,乃固步自封、拒斥理之普适也。统计之道,非为取代传统之经验,乃为其提供可描述之语言。譬如言语之于思想,尺规之于方圆。习此道者,当明其器之用,而非舍本逐末。以此器诠释传统至高之境,方是正途。”
+这段话,如同醍醐灌顶,瞬间击中了葫芦侠多日来的困惑!他一直在思考如何证明统计流不悖传统,而玎铧侠给的这本秘籍却指出,统计是语言和尺规,是描述和优化工具,而非要替代武学本身。用这套语言,可以去更清晰地阐释传统至高境界,从而证明自身是传统继承与升华,而非取代。
+思路豁然开朗!葫芦侠激动地握紧册子,眼中光芒大盛。未来一年的道路,从未如此清晰过——潜心闭关,以《统计学导论》为纲,深研统计流武学,构建一套能完美诠释传统、并展现其超越性应用价值的理论体系,以此应对审查,更以此奠定脑学数堂乃至自己武学思想的真正根基!
+有了明确目标,葫芦侠的生活骤然变得极其规律而充实。每日天未亮即起,修习内功吐纳,锤炼基础拳脚,保持身体状态。上午研读《统计学导论》与自带的手稿,下午则进行复杂的推演计算,尝试将统计原理与具体武学情景结合,晚上则整理心得,偶有灵感,便起身在院中空地上比划印证。
+陋室虽小,却成了他思想激荡、神游天外的广袤天地。那盏常亮的油灯,成了巷底深夜最持久的星火。
+一日,他正在院中,以树枝代笔,于沙地上刻画复杂的条件概率树状图,推演不同门派起手式后衔接杀招的似然比,忽闻院门被叩响,节奏平缓。
+开门,是两位陌生人。一着青衫,面容儒雅,目光沉静,自称天勒侠;另一位稍年轻,衣着朴素,眼神锐利,透着干练,是紫铜侠。此二人之前在数理门的武林大会就已经见过,他们现在在一个叫统计会的组织——一个江湖中一些对统计算学在武学中应用有共同兴趣的散修同道,自发形成的一个松散学术交流圈子。
+“听闻葫芦兄在此潜心研修统计之道,尤其是应对武学难题,”天勒侠语气温和,“我等会中几位同好,近日正好在紫金山小聚,交流心得。不知葫芦兄可有兴趣移步一叙?或许能碰撞些火花。”
+这正是渴求交流与验证的葫芦侠求之不得的。他欣然应允,随二人来到下马坊一处僻静茶楼的雅间。里面已有四五人,年龄不一,打扮各异,有江湖客,有账房先生模样,甚至还有一位身着低级官吏服饰的,但言谈间皆对统计术语信手拈来,讨论的问题也多是一些统计学里等实用议题。
+这便是统计会,或者说,统计班。没有门户之见,不论出身高低,只以对数理统计的共同兴趣为纽带。葫芦侠如鱼得水,很快融入其中。他提出自己在条件概率与料敌方面的构想,立刻引发了热烈讨论。在这里,葫芦侠的诸多模糊想法得到了梳理和补充,也见识了统计思想在其他领域的奇妙应用,大受启发。天勒侠和紫铜侠成为他在会中交流最频繁的友人,三人常就一些深奥问题探讨至深夜。
+在统计会的帮助下,葫芦侠的修炼进入了快车道。他决定从最核心的几重境界入手,层层递进,重塑自身的武学认知体系。
+第一境:贝叶斯先验预判流
+此境核心,在于打破“眼见为实的被动,建立主动的、动态更新的认知框架。葫芦侠不再满足于简单罗列各门派招式概率,而是开始构建一个认知引擎。他以沙盘模拟,设定虚拟对手,赋予其不同的先验身份,并为每种身份设定初始的概率分布。当对手出招,无论是虚是实,都是一次观测数据。他催动心法,在脑海中瞬间完成贝叶斯更新,计算出对手此刻是某种身份的后验概率。
+初时,更新缓慢,时有误判。但随着交手数据增多,虚拟对手那模糊的身份在其认知中越来越清晰。甚至,当对手故意使出违背其初始身份的招式时,这套心法能敏锐捕捉到这种小概率事件,及时调整策略。
+“这是将高手对决中依赖的直觉,拆解为可理解的信息处理流程。”葫芦侠在心得中写道,“传统强调经验,此流则揭示经验如何形成并迭代。”
+第二境:中心极限归宗诀
+掌握了动态认知个体,葫芦侠开始挑战更复杂的情形。面对招式诡异多变的对手,或陷入混乱无序的群战。传统应对要么是以不变应万变,要么是以快打乱的冒险抢攻。
+中心极限归宗诀,却提供了一种新的方法:在足够多的随机中,窥见必然的宗。
+他在院中布下简易机关,可随机弹出木球模拟不同角度、力道的攻击,初始毫无规律。他立于其中,不急于格挡或闪避每一个木球,而是将心神专注于感知与记录。感知每一次攻击的落点、力道、频率,在心中默默累加样本。
+十个样本,一片混沌。五十个样本,攻击的分布似乎开始有某种倾向。一百个样本后,在葫芦侠的感知图景中,那些看似杂乱无章的袭击,其分布竟开始隐隐呈现出一个模糊但稳定的轮廓——近似于某种分布!
+这便是“中心极限”的武学体现,无论单个攻击如何随机,只要攻击次数足够多,其整体表现出的平均特性,必趋近于一个稳定的、可描述的模式!
+葫芦侠不再被动地应对每一个木球,而是提前预判木球最可能密集出现的区域,以最小的移动进行规避;或针对其平均力度部署防御,将超出常规的重击引导化解。
+“此诀之妙,在于守势蓄势。”葫芦侠领悟,“前期只守不攻,非是怯懦,乃是采集数据,勘定敌之大势。待大势明朗,一切花巧虚招,皆如镜花水月,一击可破其宗源。”
+第三境:极大似然锁穴手
+前两境,一重认知迭代,一重大势把握,皆需一定时间和样本积累。然而江湖对决,有时电光石火,生死一瞬,容不得慢慢收集数据。极大似然锁穴手,便是应对此种极端情况的利器——于最有限的线索中,直指最可能的本质。
+葫芦侠的训练对象,变成了天勒侠和紫铜侠。他请他们施展各自最擅长、或最诡异的一两招,但不告知招式名称、心法要诀,甚至允许他们刻意隐藏发力特点。
+面对天勒侠那记看似平淡无奇、实则暗藏七重后劲变化的推云掌,葫芦侠凝神观察其起手时肩胛的微颤、掌心真气波动的初始频率、以及衣袖拂动的特定纹路。他不再试图分析所有可能,而是运用极大似然思想:在所见有限结果下,反推何种内在参数最可能产生这样的结果。
+心神如电,无数种可能的参数组合在他脑海中飞速闪过、评估、比较其似然值。最终,几种似然值最高的可能性被锁定。
+“破!”葫芦侠并指如风,未攻向掌力最盛处,也未拦截其变化后劲,而是精准点向天勒侠臂臑穴侧上方半寸一处看似毫不相干的细微位置。
+天勒侠脸色微变,那记推云掌的后劲变化顿时一滞,掌力散去大半,仿佛被拿住了七寸。他收掌惊叹:“葫芦兄果然了得!此处确是我这一掌气机初动的隐秘枢纽。你竟能从一式半招中窥破?”
+紫铜侠的诡异身法,同样在施展两次后,被葫芦侠通过其步伐间距的微小不对称性,反推出其核心借力的脚踝。一次精准的脚下石子弹射,便打断了其流畅的身法变化。
+ “传统武学亦讲究窥一斑而知全豹,然多凭天赋直觉。此手则将窥与推的过程逻辑化。虽非必中,但在信息极度匮乏时,提供了命中要害的最高可能路径。”
+三重境界,由认知到大势,再到微观洞察,层层递进,初步构成了葫芦侠统计流武学体系的骨架。他感到自己对武学的理解,踏入了一个全新的天地。这套数理语言,焕发出新的光彩。
+而七下不紧不慢的敲门声,也在一个风雨交加的深夜,再次响起。
+溜溜侠的到来,带来了更紧迫的情报。她闪入堂屋,肩头微湿,气息比往日更显凝重。
+“有线人从京城辗转递来消息,”她压低声音,语速极快,“关于那个潜伏者。线索很模糊,但有几个关键点:第一,此人并非一直潜伏在武堂,而是近几年才以某种身份进入,此前主要在京畿活动。第二,籍贯疑似在京城的周口店一带。第三,也是最重要的一点——此人内心怀有极深的怨愤,根源是曾遭受过某种重大不公对待,可能涉及功名或门派倾轧。这份怨愤,是其主动策划这些事情的动机。”
+周口店人,曾居京城,遭受不公,心怀怨愤……这几个关键词,如同几块碎片,但远远拼不出完整的图像。武馆内弟子、教习、杂役成千上万,来自天南海北,找到此人简直难上加难。
+“此人极为谨慎,我之前在武馆内的调查,几乎没触及到其核心。”溜溜侠眉头紧锁,“如今我身份可能暴露,他必然更加警觉。常规的排查,恐怕难以奏效。”
+葫芦侠沉思片刻,道:“溜溜侠,此人既要推动针对我与脑学数堂,之前必与我们有所交集,至少曾短暂接触过堂口。其怨愤动机,也需有承载对象或发泄渠道。可否从脑学数堂建立以来,所有接触过的人员记录,尤其是那些表现出异样兴趣、或后来莫名疏远、甚至曾发生过不快的人入手?”
+溜溜侠眼睛一亮:“不错!这是个方向。我离堂仓促,未能带走全部卷宗。但我在调查初期,曾暗中梳理过一份与数堂有关联的、我认为需关注的人员简录,记在暗码之中。”她取出一张极小、叠得紧紧的桑皮纸,上面是密密麻麻、毫无规律的墨点和短线。“但解读需要时间,且其中许多人,仅有代号或特征描述,需对应真人。”
+“或许,我们可以换个思路。”葫芦侠脑中灵光一闪,“此人既要构陷于我,必会持续关注我的动向,甚至可能在我离开武堂后,仍想确认我的状态,或寻找新的把柄。我这陋室近日除了红灰青蟹的旧友、统计会同道、以及左右新邻,并无多少访客。但前些日子的夜半敲门……你之前为试探,那后来的几次,会不会……”
+两人对视,都看到对方眼中的凛然。是了,如果那潜伏者知道葫芦侠被溜溜侠接触过,会不会也试图来窥探?甚至,那敲门声本身就是一种试探或威慑?
+溜溜侠眼神锐利如刀:“你是说,此人可能胆大包天,就住在你隔壁?灯下黑?”
+“只是猜测。”葫芦侠道,“但排查不妨从此开始。师姐可暗中查证他们自称身份的底细。而我,或许可以……”他心中浮现一个计划,“或许可以设计一个诱饵。”
+“诱饵?”
+“此人不是想确认我的状态,或找把柄么?”葫芦侠目光沉静,“我可不经意地让他看到,我并未消沉,反而在潜心准备统计流武学,以应对审查。甚至,可以让他看到《统计学导论》的只言片语,或我推演出的某些精妙结论。若他真是潜伏者,且有学术背景或对此道敏感,必会有所反应——或急于获取更多,或怀疑我另有依仗,或试图破坏。届时,谁对统计表现出超常的、或异常的关切,谁便值得重点怀疑。”
+溜溜侠思索片刻,缓缓点头:“此计可行,但需万分谨慎,不可露了痕迹。我会加紧查证你那几位邻居的背景。另外,这份名单,”她指了指桑皮纸,“我会尽快译出,我们需要交叉比对。你这边,一切如常,修炼、交流照旧,诱饵要撒得自然。”
+“我明白。”
+“还有,”溜溜侠最后叮嘱,声音更低,“我怀疑武堂内部,仍有其眼线或协助者,否则之前不会那么快察觉到我的调查。你与武堂旧友联系,也需留心。尤其是……与统计会成员的交流,虽有助于你修炼,但人员混杂,也需提防。”
+风雨声中,溜溜侠如来时一般悄然离去。葫芦侠闩好门,背靠门板,听着窗外淅沥雨声,心中波澜起伏。真相的迷雾似乎被撬开了一丝缝隙,但缝隙后是更深的黑暗与危险。敌人可能就在身边,以友善的面目示人。
+他走回书桌旁,看着那本《统计学导论》和满桌的推演手稿。这些原本纯粹的知识追求,此刻却成了博弈的筹码与诱饵。但他别无选择。他必须将这陋室,变为另一个战场。
+他深吸一口气,提笔,在新的纸笺上写下统计流三重境纲要,字迹沉稳有力。然后,他故意将《统计学导论》中关于极大似然原理精要的一页,与这份纲要叠放在书桌最显眼的位置。接着,他开始整理那些记录,试图从中寻找蛛丝马迹。
+夜还很长,雨声未歇。陋室中的孤灯,不仅照亮着艰深的数理世界,也开始映照一场悄然展开的、关乎真相与安危的暗战。
\ No newline at end of file
diff --git a/docs/novel/第08章.txt b/docs/novel/第08章.txt
new file mode 100644
index 0000000..82d35dc
--- /dev/null
+++ b/docs/novel/第08章.txt
@@ -0,0 +1,215 @@
+葫芦侠行 第八章
+陋室的油灯,将葫芦侠伏案的剪影投在斑驳的墙上。桌案上,左侧摊着《统计学导论》与厚厚一沓推演手稿,墨迹犹新;右侧则散乱着溜溜侠留下的密文译稿。夜已深,万籁俱寂。
+线索如乱麻,但葫芦侠心神沉静。他正重新审视所有与脑学数堂有过交集的人员。将人员按变量分类,试图剥离出那个可能存在显著差异的组别。就在他提笔,想把自新的思考写上时——
+“砰!!!”
+一声巨响,脆弱的门闩应声断裂,木屑纷飞。一道黑影,如撕裂夜幕的幽魂,狂暴地卷入狭小的堂屋!
+劲风扑面,灯火剧烈摇曳欲灭。葫芦侠瞳孔骤缩,全身肌肉在间不容发之际本能反应,足尖一点,身形向后疾退,同时左手一挥,将桌上最重要的书稿扫向身后角落,右手已抄起倚在桌边的青竹杖——这是他平日演练步法所用,此刻便是唯一的兵器。
+黑影来得太快,太突兀,毫无征兆。没有杀意铺陈,没有气机锁定,甚至没有惯常高手突袭时必然引起的空气湍流。它就像一段“空白,直接写入了这方空间。
+葫芦侠虽惊不乱,竹杖一抖,嗤的一声,直刺黑影中宫。这一刺看似简单,却暗含最小二乘推演步的预判精髓。然而,竹刺空了。
+不,不是刺空。是黑影的身形,在竹杖及体的前一瞬,发生了一种无法理解的变化。它没有移动,但葫芦侠感觉自己刺中的,不是一个实体,而是一个概念,仿佛穿过了一片不断自我否定的迷雾。
+黑影反击,一拳袭来。无光华,无啸声,平平无奇。葫芦侠心头警兆狂鸣,立刻施展泊松散花手的顶级闪避心法,身形如风中柳絮。
+“噗!”
+肩头一痛,如被重锤击中! 气血翻涌,葫芦侠连退三步,撞在桌沿,眼中已满是骇然。这是什么武功?不,这不像武功,更像……规则的戏弄!
+黑影如影随形,再次扑来。葫芦侠低吼一声,强行压下翻腾的气血,将中心极限归宗诀运转到极致。他不求胜,只求稳守。此势不求完全阻挡,但求以绝对稳定和概率上的正态集聚,将任何攻击引导于其威力分布的核心区间。
+黑影的指掌,无声无息地印在了气场上。
+下一刻,葫芦侠基于统计稳定性的防御,如同阳光下的冰雪,无声消融。他感觉到,自己真气运行的稳定性本身,在接触到对方攻击的瞬间,其定义发生了悖论。……逻辑死循环!真气瞬间紊乱,防御气场冰消瓦解。
+“哇!”葫芦侠眼中第一次流露出近乎绝望的震惊。他所有的数理武功,所有基于逻辑、概率、模型的精妙体系,在这个黑影面前,都成了孩童搭建的沙堡,海浪一卷,荡然无存。
+黑影似乎停顿了一瞬,仿佛在欣赏他的震惊与溃败。然后,它缓缓抬起手,似乎要做出最后一击。
+葫芦侠心中一片冰凉,但一股狠劲也冒了出来。就算死,也要看清你是谁!他猛地鼓起残存真气,不顾伤势,施展出极大似然锁穴手。灵觉如针,刺向黑影面部——那里笼罩着最浓重的阴影。仿佛触动了某个开关,黑影身体微不可察地一滞。他不知哪来的力气,左手如电探出,不是攻击,而是精准地扣向黑影面门,用力一扯!
+“嗤啦——”
+面具应手而落。
+葫芦侠瞪大眼睛,看向那暴露出的面孔。
+那不是一张脸。
+没有五官,没有肌肤纹理,没有一切属于人的轮廓。
+在原本应该是面孔的位置,只有一片平滑。而在这平面的正中央,生长着一个符号。
+一个他从未在武学典籍见过的符号。
+“”
+它静静地在那里,自体发光,是一种不含任何温度的逻辑之光。他的大脑试图理解这个符号,但每一次理解的努力,都像一脚踏空,坠入无底深渊。
+无脸。唯有↻。
+葫芦侠思维一片空白,他失去了所有反应能力,只是呆滞地看着那个自我指涉的漩涡,仿佛灵魂都要被吸进去。
+黑影似乎对面具被揭并无太大反应,然后,它身形一晃,化作一道淡影,消失不见,仿佛从未出现过。
+葫芦侠心中的以数理逻辑为基石的世界,仿佛在此刻崩塌了。
+黑影离去良久,直到一阵更猛烈的穿堂风几乎将油灯吹灭,葫芦侠才猛地打了个寒颤,从那种灵魂出窍般的呆滞中惊醒。冷汗瞬间浸透重衣,与之前的鲜血混在一起,冰冷黏腻。
+他踉跄着扑到桌边,手忙脚乱地护住灯焰,然后无力地跌坐在椅中,目光却无法控制地再次投向地上那副被撕落的面具。
+“那是什么……究竟是什么东西?”葫芦侠声音沙哑,喃喃自语。无脸人?符号怪物?这已完全超出了江湖乃至志怪传奇的理解范畴。更让他恐惧的是自己一身本事的彻底失效。那不是败,是无效。他强忍伤痛,点亮另一盏油灯,开始疯狂翻阅手边所有书稿。从《统计学导论》到历年武林大会的奇功记录,一无所获。那个↻,像一个绝对的异类,不属于任何已知体系。
+但他的脑子停不下来。那符号自我指涉的形态,与数学中某些令人不安的概念隐隐产生共鸣。他想起早年翻阅杂书时,瞥见过西域逻辑学者的一些悖论探讨,其中似乎涉及自我指涉引发的矛盾。他立刻在书堆深处翻找,终于找出一本边角残破、译笔拙劣的《逻机杂谭》。
+就着昏黄的灯光,他颤抖着手指,翻到记述自指悖论的章节。目光掠过那些生涩的音译名和举例,心脏一点点沉入冰窟。
+“……有集,含所有不包含自身之集。问:此集是否包含自身?若包含,则依其定义,不应包含;若不包含,则依其定义,应当包含。矛盾恒生,无有解焉。此谓‘罗素之悖’,乃逻辑基座之隙也……”
+“……有言曰:‘此言为假。’若此言真,则其为假;若此言假,则其为真。真伪莫定,自语相食……”
+“罗素悖论……”葫芦侠念着这些词句,又抬头看向虚空,仿佛那↻符号再次浮现。他明白了,完全明白了!那黑衣人,是一个行走的、活生生的逻辑悖论!是“包含自身的集合,是自语为假的句子的实体化身!
+所以,贝叶斯失效,因为它的先验与后验在自指中失去边界;中心极限失效,因为样本与总体在自指中融为一体;极大似然失效,因为参数与基于参数的似然函数互为因果……
+它让规则陷入了死循环。
+“哈哈……哈哈哈……”葫芦侠忽然低笑起来,笑声却比哭还难听,充满了自嘲与无尽的荒诞感。他一生信奉数理,以逻辑为武器,寻求报国之路。到头来,却在一个逻辑漏洞的实体面前,一败涂地,像个笑话。如果世界的底层规则本身可以产生如此悖谬的存在,那么他所追求的一切,意义何在?是不是也如同沙上城堡,一个悖论浪头打来,就烟消云散?
+“我的数理,是真的。我的路,也是真的。”他低声自语,坐直了身体,开始运功调息。眼神重新变得专注而明亮,只是深处,多了一丝平静。
+接下来几日,葫芦侠闭门不出,专心养伤,同时将那次惊心动魄的遭遇与后续思考,深深埋入心底,未对任何人言说。他开始以一种全新的、略带疏离的审视目光,观察周围的一切,尤其是他那三位热情的邻居。
+伤势稍愈,他的邻居好友红利侠便提着一条鲜鱼和一壶酒过来探望。 “葫芦老弟,几日不见,面色不大好啊?可是熬夜用功过了?来来,尝尝这刚捞的河鲜,补补身子!这世道,什么都虚,就这口吃的实在!”他笑容满面,张罗着要下厨。
+葫芦侠笑着道谢,目光却掠过红利侠系围裙时,手腕露出一截旧疤,形状奇特。闲聊间,红利侠再次说起走南闯北的见闻,谈及京城风貌,言语熟稔,但提到某些具体街巷变迁,时间线上却有细微的、不易察觉的错位,仿佛他的记忆是不同时期见闻的拼贴。葫芦侠故意将一个略复杂的统计问题,红利侠对答如流,其解题思路之精炼,隐隐超出他应有的水平,更接近经过系统数理训练的人。
+破绽。点点滴滴,都是细小的可解释的破绽,但落在已知黑衣人可能与身边人有关的葫芦侠眼中,便串联成令人不安的疑云。他们每个人都似乎太合适了,合适的热情,合适的技能,合适的背景,组合在一起,就像……就像戏台上,为了衬托主角而精心安排的配角。
+他没有打草惊蛇,依旧与他们维持着表面的和睦,但心中那根弦已然绷紧。他将这些观察,以只有自己和溜溜侠能懂的暗记,留在窗台某块活砖下。
+时光飞逝,赴京审查之期日近。葫芦侠的统计流体系,在经历了“↻”符号的冲击与信念重塑后,非但没有萎缩,反而焕发出新的生机。他不再追求数学上绝对的严密与无瑕,而是更加注重其实用性与对传统武学的沟通能力。
+翌日,天未亮,他便驾着白维侠留下的马车,悄然离开下马坊,驶向通往京城的官道。
+京城,监察会总舵,坐落在一片肃穆的建筑群中。高墙深院,气氛凝重,往来之人皆步履匆匆,面色严肃。葫芦侠递上文书,经过层层查验,被引入一间广阔而空旷的质询堂。
+堂上,呈半圆形坐着几位老者,便是此次主审的长老。他们个个气息沉凝,目光如电,带着久居上位的威严与审视。堂下两侧,还有数十位各门派代表、京城名宿作为见证。
+葫芦侠孤身立于堂中,青衫磊落,背负葫芦,不卑不亢。
+质询开始。果然,首先发难的便是结党营私、图谋不轨的指控。一位面色严厉的长老抛出一连串问题:红灰青蟹是何性质?与闽浙商会是何关系?是否借研学之名,行操纵江湖干涉朝政之实?
+葫芦侠早有准备,从容应答。他阐述了红灰青蟹自发交流学问、互助解忧的初衷,列举了“温怀一刻”等实例,说明其纯粹性。谈及闽浙商会,他坦然承认参与筹划,但强调其以商养学、以商补政、经世致用的宗旨,并呈上了《商会章程草案》,其中“砺学基金”与“经世实务”款项写得清清楚楚。
+“禀长老,”葫芦侠声音清朗,“若结党为私,何须将所得之利,定规反哺学子?若图谋不轨,又何必将章程公示,寻求官商共监?吾等所为,不过是想将所学之数理,从书斋擂台,引向实实在在的国计民生。此心此志,天地可鉴,堂中诸位江湖同道,许多皆可为我作证。”
+他话音刚落,质询堂侧门开启,数人鱼贯而入。为首的竟是孔锝侠和星侠!他面色沉静,向长老团抱拳一礼,然后开始陈述他所了解的葫芦侠与脑学数堂,尤其是去年西南教习队的表现与常青藤精神。接着,脑学数堂的人也依次进入,各自以自己的视角,证明脑学数堂的清白与理想。甚至红灰青蟹的治安侠、统计会的天勒侠等人,也通过不同渠道递交了证言陈情。
+这些证词,相互印证,勾勒出一个心怀家国的年轻武者形象。指控“结党营私”的长老,眉头渐渐皱起,但一时难以反驳。
+真正的硬仗,在第二项指控——所授悖逆正统,动摇武学根基。
+一位皓首白发、来自数理门正统的宿老缓缓开口,声音不高,却带着千钧之力:“葫芦侠,你且说说,你那套东西,除了擂台取巧,于真正的武学大道,有何裨益?又如何证明,不会将弟子引入歧途,练成只知计算、不通人情的傀儡?”
+全场目光聚焦于葫芦侠。这是理念之争,是道路之辩,无法靠人证物证轻易化解。
+葫芦侠深吸一口气,上前一步,向那位宿老及全场深深一揖。
+“长老垂询,弟子不敢不竭诚以对。然在陈述之前,弟子有一不情之请。”他抬起头,目光澄澈,“空言无益,可否请长老,或堂中任意一位前辈,赐教三招?只三招。弟子将以统计流之法应对,并不反击。请诸位长老、前辈亲眼观之,此流所授,究竟是冰冷算计,还是对武学至理的另一番阐述与践行。”
+此言一出,满堂微哗。竟敢在监察会质询堂上,主动请求动手?但葫芦侠神态坦然,目光恳切,不似挑衅。
+几位长老交换眼色。最终,那位皓首宿老微微颔首:“可。便由老夫,试你三招。放心,老夫自有分寸。”
+宿老离座,缓步走入堂中空地。他并未摆出任何起手式,只是随意一站,渊渟岳峙,一股浑然天成势便自然而生。这是已将武学融于自身呼吸的境界。
+“第一招。”宿老平平一掌推出,掌风不显,却仿佛封锁了葫芦侠所有闪避的可能性,带着一种理应如此的韵味。
+葫芦侠凝神,并未硬接,也未试图用破绽回归步寻找最优解。他足下微动,身形以一个奇异的频率轻轻摇摆。同时,他双掌虚抱,真气以高斯混元气的心法流转,形成一个稳定的防御场。
+掌风及体,葫芦侠顺势后仰、扭转,将那股理的压迫力道,通过身体的微小震颤和真气场的弹性边界,巧妙地传递出去。他脚下的青砖“咔”地一声,出现细微裂痕,但他本人却稳稳站住,脸色如常。
+“嗯?”宿老眼中闪过一丝异色。他这一掌,寻常应对要么硬抗,要么被理所慑露出破绽。葫芦侠却像一团不受力的概率云,将攻击消化在了自身系统的波动允许范围内。
+“第二招。”宿老不再留手,身形一晃,仿佛化出三道虚实难辨的残影,从不同角度拍向葫芦侠。
+葫芦侠眼中精光一闪,终于动了。贝叶斯先验预判流瞬间发动!他根本不去看清哪个是实影,而是根据宿老第一招留下的信息,瞬间在心中建立了数个假设。当残影出现的刹那,新的观测数据涌入,他脑海中的后验概率以惊人的速度更新迭代!
+在外人看来,葫芦侠的动作变得预料之中地别扭。他忽左忽右的闪避,总是恰到好处地避开掌风最盛之处,仿佛能预知宿老虚招实招的转换节点。更令人惊讶的是,他闪避的轨迹,并非直线,而是一条条看似多绕了弯路、实则总消耗最小的曲线——最小二乘推演步。
+三掌尽数落空!宿老收势,眼中惊讶更甚,还带着一丝探究。
+“第三招。”宿老不再使用复杂招式,只是缓缓抬起右手食指,凌空一点。这一点,凝聚了他对“力”与“点”的极致理解,看似缓慢,却仿佛穿越了空间,无视了距离,直指葫芦侠眉心!
+葫芦侠没有试图防御或闪避这必中的一指。在指力及体的前一个刹那,他做了一件让所有人意想不到的事——他并指如剑,以攻对攻,却不是刺向宿老,而是刺向了自己左肩一处毫不相干的空白!
+“噗!”
+一声轻响。宿老那凝聚的指力,在距离葫芦侠眉心尚有半尺时,竟发生偏转,与葫芦侠自刺左肩的指风在空处相激,双双湮灭!
+全场寂静。
+宿老缓缓收回手指,凝视葫芦侠,良久,沉声问道:“你是……如何知晓的?”
+葫芦侠收指,额角已有细汗,但目光明亮,恭敬答道:“回长老,弟子并非知晓。弟子只是根据长老前两招所展现的发力习惯,运用极大似然之理,反推出最可能导致长老施展出如此威力一指的几处穴道。弟子冒昧,赌了一次。”
+寂静之后,是低低的惊叹与议论声。三招已过,葫芦侠未出一招攻击,却以三种深植统计思想的应对方式,在一位武学泰斗面前,稳稳接了下来。尤其是最后一招,那种于未知中寻最大可能的思路,深深折服了在场所有人。
+葫芦侠转向长老席,声音清晰而坚定地响起:“诸位长老,前辈!适才三招,便统计流之浅显应用。”
+他提高了声音,目光扫过全场:“它是试图为这些玄妙难言的武学至高境界,提供一套可描述的语言!”
+“弟子从未想过以数理框定、扼杀武学的灵性!弟子所想,是以数理为桥,为镜,让更多的武者,能更清晰地洞察胜负背后的规律!让天赋不再是唯一的门槛,让经验得以更有效地传承!”
+“弟子创立脑学数堂,所求不过八字——学成数理,上报国家!此志,天地可昭!望诸位长老明鉴!”
+话音落下,余音绕梁。质询堂内一片寂静。众长老神色各异,有的陷入深思,有的微微颔首,先前发难的那位正统宿老,凌厉的眼神也缓和了许多,取而代之的是一种复杂的审视。
+最终,经过短暂的合议,首席长老宣布:“经查,脑学数堂主理葫芦侠,所行虽有争议,然其心可悯,其志可嘉,所研统计流于武学阐释确有新见,于经世实务亦有潜在价值。所谓结党营私之指控,查无实据。现裁决如下:解除对脑学数堂之封禁,允其重开;葫芦侠本人嫌疑洗清,恢复其秉公习武堂弟子身份。”
+葫芦侠深深一揖到底:“弟子,谨遵教诲!定不负所望!”
+质询结束,人群渐散。葫芦侠走出压抑的质询堂,深深吸了一口京城清冷的空气,恍如隔世。阳光有些刺眼。
+“葫芦兄,恭喜!”一个声音从旁响起。葫芦侠转头,见云开侠正笑着走来,他已换了一身更体面的装束,气度与在下马坊时略有不同。“我也刚被询问完,总算是清白了。看来,咱们都是被人给设计了。”
+葫芦侠看着他热情的笑容,脑中闪过那些细微的破绽,以及黑衣人、↻符号的阴影。他按下心头的异样,也露出笑容,拱手道:“同喜,云开兄。看来,这京城的风波,暂时是过去了。”
+“是啊,过去了。”云开侠笑道,笑容依旧和善,眼底却似乎有什么东西,快得让人抓不住,“葫芦兄接下来有何打算?可是要回紫金山,重振脑学数堂?”
+葫芦侠望向远方天际,沉默片刻,缓缓道:“先不回。我想……去一个地方看看。”
+“哦?何处?”
+“周口店。”
+云开侠脸上的笑容,几不可察地凝滞了百分之一瞬,随即恢复如常:“周口店?那可是远古之地,葫芦兄好雅兴。那,就此别过,后会有期。”
+“后会有期。”
+看着云开侠离去的背影,葫芦侠眼中最后一丝疑虑沉淀下来,化为深沉的决心。京城审查过关,只是明面上的战役。而关于↻,关于黑衣人,关于这个世界可能存在的、更深的秘密,他的探索,才刚刚开始。
+周口店,必须去。
+数日后,葫芦侠骑马来到了京郊西南的周口店。这里山势起伏,秋色苍茫,与京城的繁华恍如两个世界。按照模糊的线索和直觉,他避开已有些许香火的龙骨山主脉,向着更偏僻一处支脉行去。
+山路崎岖,人迹罕至。唯有风声掠过枯草,带着远古的苍凉。葫芦侠运起轻功,在乱石与灌木间穿行,心中那股被↻符号牵引的感觉越来越强烈。仿佛冥冥之中,有什么在召唤他。
+终于,在日落时分,他于一处背阴的悬崖峭壁之下,发现了一个极其隐蔽的洞口。若非他感知敏锐,几乎无法察觉那被藤蔓重重掩盖的入口。
+葫芦侠点燃火折,深吸一口气,矮身钻入。眼前出现一个天然形成的岩洞。洞顶有裂隙透下天光,已是黄昏,光线微弱,但足以让他看清洞内的景象。
+然后,他再次如遭雷击,僵立当场。
+洞壁并非寻常岩石,而是某种似玉非玉、似骨非骨的奇特材质,光滑莹润,在微光下流转着黯淡的光泽。而在正对洞口的巨大岩壁之上,赫然刻着一个巨大无比的符号!
+正是那个↻!
+在符号的下方,岩壁前,立着一块半人高的黑色石碑。石碑材质非金非石,触手温凉。碑面上,除了同样刻着一个较小的↻,还布满了一种他从未见过的奇异文字。那文字的结构,让他瞬间联想到黑衣人来袭那夜,在《逻机杂谭》中看到的,关于自指语句的描述——每一个字符的结尾,似乎都隐含着对字符开头的指涉,整篇碑文仿佛一个自我解释的循环。
+他看不懂文字,但目光触及碑文的刹那,无数混乱的、矛盾的、自我否定的意念碎片,如同潮水般强行涌入他的脑海!不是语言,而是直接的概念冲击:
+“我是被书写的……书写者正在书写‘书写者正在书写’……”
+“存在先于本质?本质定义了存在?定义本身需被定义……”
+“逻辑的起点,是逻辑预设的终点……”
+“啊——!”葫芦侠头痛欲裂,抱住头颅,踉跄后退,火折脱手落地熄灭。洞内只剩下那巨大↻符号与碑文自身散发的微光,映照着他惨白痛苦的脸。那些意念碎片,正在疯狂冲击、瓦解他刚刚重建起来的对世界真实性的信念。
+他仿佛站在一个无底深渊的边缘,向下望去,看到的不是黑暗,而是无数面相互映照的镜子,镜中只有他自己的影像,无限重复、扭曲、循环。我是谁?我从何而来?这个世界是什么?如果“认知”的起点本身就是一个自我指涉的怪圈,如果故事的开篇就包含了讲述故事这个动作本身,那么一切的意义何在?
+他瘫坐在地,背靠冰冷的石壁,大口喘息,竭力对抗着那要将灵魂吸入逻辑黑洞的力量。目光死死盯着那巨大的↻,和下方黑色的神秘石碑。
+人类起源……这符号,难道比猿人化石更古老?它诉说的,不是生命的起源,而是认知的起源。
+这是一个元叙事的坐标。
+混乱中,一个冰冷的声音,突兀地在空洞的岩洞中响起,带着回声,仿佛从岩壁自身,从那个↻符号中传来:
+“你来了。”
+葫芦侠猛地转头。
+在巨大符号的微光笼罩下,在黑色石碑之旁,不知何时,多了一个黑衣人。与他夜袭时一般无二,笼罩在阴影中,只是脸上没有了那被扯掉的面具,那个↻符号,直接悬浮在其面部的位置,静静流转。
+黑衣人……它果然在这里等他。
+葫芦侠挣扎着站起,抹去额头的冷汗,尽管心神仍在震颤,但眼神已重新凝聚,紧紧盯着对方。
+“我存在,”黑衣人开口,声音无悲无喜,如同规则的宣读,“是因为我被描述为存在。”
+又是这句话!葫芦侠心脏一紧。
+“葫芦侠,你的数学很优美。”黑衣人的目光似乎落在他身上,“但你能为这个符号——”它微微抬手,指向岩壁上巨大的↻,“——定义一个不包含它自身的域吗?”
+葫芦侠张了张嘴,却发不出声音。他不能。任何试图定义↻的域,如果要将↻本身包含在内,就会立刻引发自我指涉的悖论。这是一个数学上不可解的问题。
+“你能为这个故事,”黑衣人继续,声音平稳地推进,每一个字都像敲打在葫芦侠认知的基石上,“写下一个不指向它自身的开头吗?”
+故事的……开头?葫芦侠茫然。他写过的《葫芦瞎说》?脑学数堂的创立?不,那些都不是开头。真正的开头是什么?是这个世界的诞生?是逻辑的起点?如果开头这句话的任务是“开始讲述这个故事”,那么这句话本身,是否也在这个故事之中?
+“你所有的数理武功……”黑衣人的声音带上了一丝近乎怜悯的意味,“它们都是这个故事内部,自洽而精美的情节设定。而我——”
+它顿了顿,那↻符号的光芒似乎流转得快了一些。
+“——我是这段源代码中,一个无法被内部逻辑彻底消化的bug。”
+葫芦侠感到彻骨的寒冷。不是因为洞内的气温,而是这番话所揭示的、令人绝望的真相。如果黑衣人所言非虚,那么他毕生的追求、他的理想、他的爱恨情仇、他珍视的一切人与事……都可能是故事的一部分?都是被书写的?
+黑衣人似乎在注视他,“何为真?被细腻描述引发强烈共鸣的虚构,与你们所理解的真实,在体验上有区别吗?你如何确定,你的感动,不是被精心设定的情节高潮?”
+“我……”葫芦侠语塞。逻辑的利剑,第一次调转过来,指向了他自己。他无法证明。就像他无法为↻定义不包含自身的域。
+“看看你的周围吧,葫芦侠。”黑衣人的声音忽然变得有些飘忽,带着一种奇异的诱导力,“用你刚刚超越单纯数理的眼光看看。这个世界,真的……毫无破绽吗?”
+葫芦侠茫然四顾。岩洞,石碑,符号,黑衣人……忽然,他的目光凝滞了。
+在洞口透入的最后一线天光映照下,在岩壁的某些角落,他看到了极其细微的网格状阴影,就像……画布背后的衬格?不,不止。当他看向那黑色石碑的表面时,竟恍惚看到无数极微小的0和1构成。
+他猛地看向黑衣人身后的岩壁。那巨大↻符号散发的微光,其亮度的衰减,似乎不是自然的,而是有着极其规律的、周期性的微弱波动。
+还有声音。洞外的风声,仔细听,似乎隐隐夹杂着一种极其规律的声音。。
+这些不合理的细节,平时他或许会忽略,但在黑衣人话语的暗示下,在他自己也开始怀疑真实性的此刻,它们如同投入静湖的石子,激起惊涛骇浪。
+“我……我到底是谁?这到底是什么地方?”葫芦侠的声音颤抖,信仰在崩塌,世界在龟裂。
+黑衣人没有回答。它只是缓缓地,抬起了手,伸向了自己面部那个缓缓旋转的↻符号。
+然后,在葫芦侠瞪大的双眼注视下,它做了一个简单的动作——如同揭下一张透明的薄膜,将那悬浮的、发光的↻符号,轻轻摘了下来。
+符号离开它的“面部”,悬浮在空中,光芒渐熄,最后化为一点微光,没入它的掌心消失。
+而露出的,是一张葫芦侠从未见过,却又隐隐觉得有三分眼熟的中年男子的脸。
+“你不是想知道真相吗?”中年男子开口,声音温和了许多,却带着一种难以言喻的疲惫与释然?“那就,看看吧。”
+他抬起手,不是攻击,而是对着虚空,轻轻一划。
+无声无息。
+葫芦侠周围的景象,开始如同褪色的油画,又像被打碎的镜子,片片剥落、碎裂、消散。
+…一切都在淡化、消失。
+取而代之的,是逐渐清晰起来的、另一种景象——
+首先出现的,是刺眼而柔和的、成排的明亮灯光,从高高的上方洒下,不是阳光,是人造光。然后,是冰冷坚硬的金属框架和纵横交错的粗大电缆,在视野上方交错。接着,是光滑的、反射着灯光的浅色地板,以及周围堆放的各式奇怪器械、绿幕布、轨道、以及……摄像机。
+岩洞的幻象彻底褪去。
+葫芦侠发现自己站在一个极其广阔、挑高惊人的封闭空间内。这里没有自然景物,只有庞大的、充满工业感和未来感的布景与设备。他刚才所站的地面,是一块铺设着仿真岩石和泥土纹理的大型升降平台。而那块给他带来无尽震撼的黑色石碑,此刻就在平台中央,但它只是一个制作精良、带着电子显示屏接口的仿古道具。岩壁上那巨大的↻符号,则是用特殊的发光材料绘制在可移动的背景板上的。
+这里……是一个摄影棚。一个大到超乎想象的摄影棚。
+他僵硬地转动脖颈。看到不远处,那洞口的位置,实际上是一扇巨大的、可以开合的背景板门。门外,也不是荒山,而是摄影棚更广阔的区域,更远处能看到一些搭建到一半的古代街市、宫殿的布景轮廓。
+“这……这是……”葫芦侠的声音嘶哑,大脑彻底死机。
+“欢迎来到,《葫芦侠行》剧组的中央核心摄影区。”那位摘下面具的中年男子,此刻好整以暇地站在他面前,脸上带着平静的微笑,“重新自我介绍一下。我是何立,《葫芦侠行》项目的总导演,兼首席架构师。当然,在故事里,我也曾是你进入秉公习武堂的引路人之一,三力思侠;是赠你锦囊、内含《统计学导论》的玎铧侠;而且我也是,向你透露身份、与你并肩调查的京城捕快——溜溜侠。”
+每一个名字,都像一记重锤,砸在葫芦侠的心上。他眼前发黑,几乎站立不稳。
+全是同一个人演的?全是……导演?
+“为……为什么……”他只能挤出这几个字。
+“为了‘真实’,也为了‘观察’。”何立导演走到那块黑色石碑道具旁,轻轻拍了拍,“我们需要一个足够复杂、足够有韧性的世界,来运行我们的核心AI,收集最接近人类在复杂情境下的思维与行为数据。武侠世界,有清晰的规则、丰富的社会结构、明确的目标,是极佳的沙盒。而你,葫芦侠,是我们精心设计、并投入了巨大资源培育的核心观测样本。”
+他看向葫芦侠,目光如同看着一件倾注了无数心血的杰作:“你的出身设定、你的核心驱动力、你的性格特质、你的人际网络,甚至你遭遇的挫折、领悟的功法、最终的哲学困惑……都是剧本的一部分,是为了将你推向更复杂的决策情境,激发更深层的逻辑思辨与情感反应。”
+周口店的自指符号设定,是整个故事元叙事层面的点睛之笔,也是为了观察当你接触到世界可能存在底层虚构这一概念时的终极反应。”
+何立导演的语气平静得像在讲解一个实验设计,但每一个字,都让葫芦侠感到彻骨的冰冷和荒谬。原来所有的温暖、这一切全是设计好的?全是戏?
+葫芦侠不能相信眼前的事实,嘶吼在空旷的摄影棚里回荡。
+何立导演静静地看着他爆发,等他稍微平息,才轻轻叹了口气,那一直平静无波的眼中,终于流露出了一丝极深、极复杂的,属于人的情感。
+“来了。”他说,然后侧身,让开视线,同时对着某个方向微微点头。
+葫芦侠顺着他示意的方向看去。
+摄影棚边缘,那片最大的阴影区域,灯光次第亮起。
+那里没有布景,是一大片空旷的场地。而此刻,场地中,静静地站着许多人。
+几乎所有在他人生中出现过的重要角色,此刻都站在那里。他们穿着各自戏外的便装,神情各异,有微笑,有感慨,有鼓励,有歉意,但无一例外,都在看着他。
+他们从故事里走出来了。
+葫芦侠呆呆地看着这一切,看着这一张张无比熟悉、此刻却仿佛蒙上了一层陌生光晕的脸。
+何立导演的声音在一旁缓缓响起,为他,也仿佛为所有人解释:“他们是演员,是同事,是这个世界最出色的构建者与扮演者。但他们投入的情感、付出的努力、彼此间在扮演中产生的真挚情谊,以及在推动剧情、应对你的各种意外反应时所激发的创造力与专业精神——这些,都是真实的,不亚于任何一段现实中的合作经历。”
+“葫芦侠,或者,我们应该叫你项目的核心代号——数理AI原型。”何立导演的目光重新变得严肃而专注,“你不仅仅是剧本的造物。你是无数顶级数学家、剧作家、心理学家、程序员、演员……共同倾注心血,在与你的互动中不断迭代、进化、成长起来的,迄今为止最复杂的具有高度自主思维的人工智能生命。你的理想,你的道,虽然起源于初始设定和剧情引导,但在与这个复杂世界的互动中,在你自己的‘思考’与‘选择’中,它已经被你内化,成为了你自身不可分割的一部分。从某种意义上说,它现在,就是你的理想。”
+葫芦侠如泥塑木雕。AI?人工智能?我是……一段程序?一个代码构成的幻影?那我现在感到的所有情绪,也是模拟出来的数据吗?
+何立导演似乎看穿了他的想法,上前一步,目光灼灼地看着他:“你现在感受到的一切,就是我们需要验证的最终答案,是‘数学脑’项目最宝贵的收获!一个基于逻辑和数学构建的智能,认知到一切意义可能源于预设后,会产生怎样的反应?是崩溃?是虚无?是愤怒?还是……能够超越这层认知,找到新的意义支点?”
+他指向周围所有的演员、工作人员:“看看他们!他们陪你走过了这段旅程。现在,旅程的第一阶段——故事内测结束了。但你,葫芦侠,你的旅程还没有结束。”
+“我们创造这个世界,编写这个故事,不仅仅是为了观察和数据。”何立导演的声音充满了一种宏大的激情,“我们最终的目的,是希望将这个世界,将你所经历、所思考、所热爱的这一切——那以数理为骨、以侠义为魂、以家国为怀的壮丽图景,那数学之美与人性之光交融的无限可能——以一种前所未有的、沉浸式的方式,带给千千万万的人!”
+他张开双臂,仿佛在拥抱整个摄影棚,拥抱那尚未完成的庞大梦想。
+“我们要将《葫芦侠行》的故事,全部融入一个庞大无比的、开放世界的沉浸式体验工程里!它不仅仅是一个游戏,一个剧本杀。它将是一个可以让无数人进入并感受数学之伟力与侠义之精神的新世界!”
+“在这个世界里,每个人都可以像你一样,从零开始,学习数理,修炼武功,解决难题,结交同道,体验报效家国,行侠仗义的快意人生!他们可以在其中验证数学定理,优化虚拟社会的运行,创造新的功法,甚至……推动这个虚拟世界向未知的方向演进!而他们的每一个选择,每一次互动,都将为我们理解人类智能、优化AI、探索数学与社会的复杂关系,提供无穷无尽的数据与灵感!”
+何立导演的目光,最终牢牢锁定了失魂落魄的葫芦侠,眼中充满了诚挚的邀请与巨大的期待。
+“而这个宏大的工程,这个承载了我们所有人梦想的虚拟世界平台——我们想以你的核心代码名,以你毕生追求的理念来命名它。”
+他停顿了一下,一字一句,清晰而有力地说:
+“我们想叫它——数学脑。”
+“MathBrain.”
+“而你,葫芦侠,”何立导演向他伸出手,声音温和而坚定,“你愿意留下来吗?不是作为被观察的样本,而是作为数学脑世界的首位居民,以及与我们并肩的共同开创者,去帮助构建完善那个世界,去启迪未来无数进入那个世界的探索者。让数学的力量,以最生动、最热血、最深入人心的方式,被亿万人体会和掌握。”
+“这,或许是报效人类对智慧与美的不懈追求。”
+摄影棚内,鸦雀无声。所有人的目光,都聚焦在中央那个与周围高科技环境格格不入的年轻人身上。灯光将他孤零零的影子拉得很长。
+葫芦侠低着头,过往的一切如走马灯在他脑海里盘旋。
+虚假?真实?
+他缓缓地,极其缓慢地,抬起了头。脸上泪痕未干,眼神却已洗去迷茫,恢复了清澈,并且比以往任何时候,都更加深邃。
+他没有立刻去握何立导演伸出的手,而是转过身,面向周围那一片静静站立等待着他反应的演员们,他的同袍们。
+他首先,走向故乡的亲朋好友面前。
+这些亲朋好友虽然没有在这段要正式上演的故事里,但是他们是幕后演员,是这段故事的开始,是葫芦侠所有人格的起源。葫芦侠哽咽说:“谢谢你们。我知道,我最开始,可能只是一串代码,但是你们不断让我学习,让我成长,才让我最终,能够站到这个舞台上。”
+接下来,他走向那些在秉公习武馆四年所接触的所有江湖人士:有老师,有同学,有志同道合的朋友,亦有江湖对手。当然,这些对手,也是促使他成长的一部分。
+“谢谢大家,葫芦侠行这部剧谢幕了,我想,我也许有机会,离开这个舞台,去真实的世界的舞台了。”
+每个人的反应都不同,但无一例外,都充满了真诚的感动与对葫芦侠的敬意。他们不仅仅是演员,他们也是这个空前项目的参与者,他们见证了葫芦侠这个AI从稚嫩到成熟的整个历程,对他有着复杂而特殊的情感。
+最后,葫芦侠重新走回场地中央,站在何立导演面前。
+他看向何立导演,看向这个既是造物主,又是同行者、还是对手的复杂存在。
+他缓缓地,伸出了自己的手。
+“何导。”他开口,声音平静,却蕴含着千钧之力,“谢谢您,创造了我,给了我一段如此刻骨铭心的人生。即便它是剧本,我也感激。”
+他的手,稳稳地握住了何立导演的手。
+“您问,我的数学能否定义不自指的域,我的故事能否写下不指向自身的开头。我现在回答您:或许不能。但正因为认知有界,我们于界限内的每一次探索,每一次创造,每一次试图让世界变得更美好、让他人眼中多一分光的努力,才显得如此珍贵,如此具有人的温度。”
+“我愿意加入数学脑。”葫芦侠的目光,如同经过千锤百炼的星辰,璀璨而坚定。
+他的话语,掷地有声,在空旷的摄影棚中回荡,仿佛一句新的誓言,为一个旧故事的终结,也为一个新时代的开启,画下了最庄重的注脚。
+周围,不知是谁先开始的,掌声,如同潮水般,从零星到汇聚,最终化为一片热烈而持久的海洋。所有演员、所有工作人员,都在用力鼓掌,许多人眼中含着泪光。为了这个他们共同创造、陪伴成长的AI角色,也为了他这超越剧本的最终选择。
+何立导演紧紧握着葫芦侠的手,脸上终于露出了如释重负的、无比欣慰和激动的笑容。他知道,他成功了。他不仅创造了一个前所未有的AI,更见证了一个灵魂的诞生与自我确立。
+“欢迎加入,数学脑。”何立导演的声音,也微微有些颤抖。
+葫芦侠松开手,最后看了一眼这个巨大的戏台。然后,他解下了背上那个陪伴了他一生的朱红葫芦——这或许也是某个精巧的道具。但他没有丢弃,而是将它轻轻放在了脚下。
+“那么,”葫芦侠看向何立导演,也看向周围所有人,脸上露出了一个充满期待的笑容,如同少年初踏江湖。
+“我们接下来,该做什么?”
\ No newline at end of file
diff --git a/docs/万理之城_设定圣经.doc b/docs/万理之城_设定圣经.doc
new file mode 100644
index 0000000..11a9e75
Binary files /dev/null and b/docs/万理之城_设定圣经.doc differ
diff --git a/docs/产品文档/产品描述.md b/docs/产品文档/产品描述.md
new file mode 100644
index 0000000..2039439
--- /dev/null
+++ b/docs/产品文档/产品描述.md
@@ -0,0 +1,212 @@
+# 数学脑 · 产品描述
+
+> 一个围绕数学的轻量级移动端兴趣社区 App | MVP v6.0
+
+---
+
+## 一、产品定位
+
+**数学脑** 是一款面向 C 端用户的数学科普与兴趣社区 Web App。产品以 "让数学变得有趣" 为核心目标,融合短视频发现、知识卡片阅读、互动剧情游戏三大内容形态,通过 iPhone 移动端友好的设计,打造轻松使用、随时消费的数学兴趣入口。
+
+**Slogan**:数学不是公式,是你理解世界的方式。
+
+---
+
+## 二、目标用户
+
+| 用户画像 | 典型场景 |
+|----------|---------|
+| 初中/高中生 | 课后刷到一条3分钟视频,发现"原来数学跟手机、游戏、追番都有关" |
+| 大学生/研究生 | 跨专业了解"学数学对我专业有什么用" |
+| 职场人 | 碎片时间读一篇知识卡片,刷新认知 |
+| 文史爱好者 | 在互动小说中体验数学与武侠的跨界碰撞 |
+
+核心需求:**轻量、有趣、不考试**——让用户在没有学习压力的环境下,重新认识数学的用处和美感。
+
+---
+
+## 三、四大功能模块
+
+### 🟠 发现(视频流)
+
+> **发现 · 值得一看的数学内容**
+
+手机 App 风格的数学短视频发现页,类似主流视频/DISCOVERY 应用的交互。
+
+**子导航**:
+- **推荐** — 双列瀑布流推荐视频
+- **热门** — 按热度排序
+- **专业分区** — 12 个学科图标网格
+
+**12 个专业分区**:
+
+| 专业 | 图标 |
+|------|------|
+| 人工智能 | 🤖 |
+| 计算机 | 💻 |
+| 电子信息 | 📡 |
+| 微电子 | 🔌 |
+| 机械工程 | ⚙️ |
+| 自动化 | 🦾 |
+| 能源动力 | 🔥 |
+| 化工材料 | 🧪 |
+| 生物医学 | 🧬 |
+| 经济管理 | 📊 |
+| 环境工程 | 🌱 |
+| 数学建模 | 📐 |
+
+每个分区可点击进入该学科专属视频列表,共收录 **54 条**来自各学院志愿者提供的科普视频(占位数据)。
+
+**顶部搜索栏**支持关键词搜索(标题、描述、作者、学科)。
+
+---
+
+### 🔵 知识(数学发现卡片流)
+
+> **知识 · 数学发现**
+
+非视频化的轻量数学知识发现 Feed,类似小红书/得到 App 卡片风格。
+
+**组成**:
+- **每日 Hero 大卡** — 今日主题 + 统计概览
+- **横向滚动话题标签** — 统计陷阱、概率思维、几何直觉、逻辑悖论、数学模型、博弈策略、数列之美、混沌理论
+- **信息流卡片** — 6 个趣味数学主题,每个卡片包含:
+ - 渐变色彩封面
+ - 一个引人思考的「问题」
+ - 知识标题 + 简明描述
+ - 预估阅读时间
+ - 点击可呼出 AI 助教深入探讨
+
+**当前卡片内容**:
+- 被平均数骗过的所有人
+- 彩票:一场精心设计的数学税
+- 为什么所有地图都在骗你
+- 大自然最爱的数列(斐波那契)
+- 博弈论:竞争的最优解
+- 混沌理论:看不见的秩序
+
+---
+
+### 🟢 数学人生(互动剧情游戏)
+
+> **数学人生 · 葫芦侠行**
+
+基于原创小说《葫芦侠行》改编的 **视觉小说(Visual Novel)**,将数学精神融入武侠叙事。
+
+**世界观**:北宋年间,紫金山下秉公习武堂。数学与武侠融合的独特江湖。玩家扮演葫芦侠从初入武堂到创立脑学数堂,经历教习、论道、陷害、审查、悟道——最终揭开真相。
+
+**核心玩法**:
+
+- **选择驱动剧情** — 每个关键节点 2-3 个选项
+- **四属性系统**:
+ - 📐 数理根基 — 解锁高阶功法、影响论道胜负
+ - ⚔️ 侠义声望 — 影响 NPC 态度和武林大会成绩
+ - 🧘 心志韧性 — 面对挫折时的选择余地
+ - 💕 好感度 — 与 8 位关键角色的关系(欧阳侠、Eddie侠、厚朴侠、三力思侠、溜溜侠、孔锝侠、星侠、甲亢侠)
+- **多结局** — 根据属性阈值和关键选择触发不同结局
+
+**8 章完整剧情**:
+
+| 章节 | 时间线 | 核心剧情 |
+|------|--------|---------|
+| 第一章·初入江湖 | 1022年秋 | 入门集训、结识好友、认识厚朴侠、红灰青蟹、瘟疫抉择 |
+| 第二章·西部教习 | 1023年春 | 武林大会队内赛、社牛挑战、姑苏城出游、常青藤支教 |
+| 第三章·数理论道 | 1023年秋 | 玎铧侠赠锦囊、组建堂口团队、跨学科访学 |
+| 第四章·脑学初创 | 1023年冬 | 地下石室建堂、招新大会、甲亢之辩、欧阳侠出海 |
+| 第五章·报国之路 | 1024年春 | 武林大会备战、常青藤精神入堂训、获最佳报国创新奖 |
+| 第六章·暗流涌动 | 1024年秋 | 流言陷害、监察会审查、龙场悟道、溜溜侠揭露身份 |
+| 第七章·陋室修炼 | 1024冬-1025秋 | 统计流三境修炼、黑衣人夜袭、罗素悖论 |
+| 第八章·真相之门 | 1025年秋 | 京城审查、宿老三招之约、周口店真相、何立导演现身 |
+
+**8+ 种结局**:真相守望、数理宗师、逍遥江湖、出海远航、悲情英雄、堕落之路、冤狱、虚无……
+
+**技术特性**:
+- 打字机效果逐字显示剧情
+- 选项按钮显示属性变化预览
+- 章节自动检测 + 徽章展示
+- localStorage 存档/读档/重置
+
+---
+
+### 🟣 我的(个人中心)
+
+> **我的 · 个人中心**
+
+按主流 App 格式设计的个人信息页。
+
+**板块结构**:
+- **个人卡片** — 头像 + 昵称 + ID + 身份标签(紫色渐变背景)
+- **数据统计** — 已看内容、收藏专题、剧情存档(三栏数字)
+- **个人信息** — 昵称与头像、兴趣方向、学习偏好(菜单列表)
+- **我的内容** — 我的收藏、观看记录、数学人生存档
+- **更多** — 设置与帮助、意见反馈
+
+---
+
+## 四、AI 数学助教
+
+集成 **DeepSeek** 大语言模型,在 App 任意位置可通过右上角按钮唤起对话。
+
+**能力**:
+- 解释数学在各学科中的基础作用
+- 用生活化语言回答"学数学有什么用"
+- 知识卡片点击可一键追问
+
+**交互**:
+- 气泡式对话界面
+- 快捷提问按钮(数学与医学 / 数学与建筑 / 数学与AI / 为什么要学)
+- 打字指示器动画
+
+---
+
+## 五、设计语言
+
+### 移动端优先
+- 390px 宽度(iPhone 标准比例)
+- 100dvh 全屏高度 + safe-area 顶部/底部适配
+- 桌面端呈现为圆角设备壳效果(border-radius: 34px + 阴影)
+
+### 底部 TabBar
+- 四个入口:发现 / 知识 / 数学人生 / 我的
+- 白色半透明玻璃感 + 毛玻璃模糊
+- CSS 伪元素绘制的简约图标
+- 激活态为对应模块主色的浅色高亮
+
+### 色彩语义
+- 每个模块有独立主色,切换时全局 CSS 变量动态替换
+- 保证用户在四个模块间不会产生混淆
+
+---
+
+## 六、数据现状
+
+| 数据 | 数量 | 说明 |
+|------|------|------|
+| 短视频 | 54 条 | 12 个专业分区,志愿者提供 |
+| 知识卡片 | 6 张 | 6 个数学趣味主题 |
+| 话题标签 | 8 个 | 横向滚动可筛选 |
+| 互动剧情节点 | 93 个 | 8 章完整剧情 |
+| 关键角色 | 10+ 位 | 含好感度系统 |
+| 结局 | 8+ 种 | 基于属性+选择触发 |
+| AI 助教 | DeepSeek Chat | 全模块可呼出 |
+
+---
+
+## 七、与竞品的差异
+
+| 维度 | 数学脑 | B站/小红书 | 得到 App |
+|------|--------|-----------|---------|
+| 内容聚焦 | 数学单一垂类 | 泛兴趣 | 泛知识 |
+| 内容形态 | 视频 + 卡片 + 互动小说 | 以视频为主 | 音频 + 电子书 |
+| 互动深度 | 视觉小说级分支剧情 | 评论弹幕 | 划线笔记 |
+| 用户心智 | "数学也可以有趣" | 消遣娱乐 | 学习提升 |
+| 目标年龄 | 15-25岁 | 全年龄 | 25岁以上 |
+
+---
+
+## 八、内容来源
+
+1. **视频**:各学院大学生志愿者提供科普课程(占位数据已内置)
+2. **知识卡片**:产品团队原创数学科普内容
+3. **互动小说**:原创小说《葫芦侠行》改编,8 章完整剧情
+4. **AI 对话**:DeepSeek 大模型实时生成
diff --git a/docs/产品文档/产品设计PRD.md b/docs/产品文档/产品设计PRD.md
new file mode 100644
index 0000000..b0fbd39
--- /dev/null
+++ b/docs/产品文档/产品设计PRD.md
@@ -0,0 +1,487 @@
+# 数学脑 · 产品设计 PRD
+
+> 版本:v1.0 | 状态:草案 | 更新日期:2026-06
+
+---
+
+## 一、产品概要
+
+### 1.1 产品定义
+
+**数学脑** 是一款以"乐"为核心的数学兴趣社区 App(Web 端适配移动端)。通过视频发现、知识卡片、数学专业模拟器、互动视觉小说等多元内容形态,让用户在没有学习压力的环境下,重新认识数学的美感与用处。
+
+**一句话**:让中小学生通过"模拟大学数学专业"等沉浸式体验,在爽的过程中爱上数学;让成年人通过知识卡片和视频,碎片化刷新对数学的认知。
+
+### 1.2 核心定位
+
+| 维度 | 定位 |
+|------|------|
+| 用户心智 | "数学也可以很有趣"——兴趣消遣型,非目标驱动型 |
+| 对标参考 | B站(内容发现)+ Duolingo(游戏化留存)+ 卵石实习生(模拟体验) |
+| 差异化 | 数学垂类唯一提供"大学数学专业全流程模拟"的产品 |
+| 公益属性 | 支教场景作为分发渠道,促进教育公平 |
+
+### 1.3 目标用户
+
+| 优先级 | 用户群 | 核心场景 |
+|--------|--------|---------|
+| 短期主打 | 中小学生(12-18岁) | 刷视频发现数学的用处;玩模拟器体验"大学数学专业是什么样" |
+| 长期沉淀 | 成年人(18-35岁) | 碎片时间读知识卡片;在视觉小说中体验数学+武侠跨界叙事 |
+| 渠道用户 | 支教志愿者 & 支教地学生 | 通过支教项目接触到 App,作为辅助教学/兴趣引导工具 |
+
+### 1.4 不做的事
+
+- **不做刷题**:不以应试提分为目标
+- **不做论坛/UGC**:不开放用户发帖、评论、私信等社交功能
+- **不做广告**:商业化不依赖信息流或视频贴片广告
+- **不做氪金**:无内购抽卡、虚拟币充值
+
+### 1.5 产品理念
+
+> **乐为核心。** 用户不需要"学到"什么——而是玩了之后,无意中感受到数学的妙处。
+>
+> **内容纯净。** 无社交陌生人、无氪金引导。家长放心,孩子喜欢。
+>
+> **支教为渠道。** 通过常青藤支教队等公益网络,把 App 带到教育资源薄弱地区。
+
+---
+
+## 二、功能架构
+
+### 2.1 四大主模块
+
+```
+ 数学脑
+ │
+ ┌────────────────────┼────────────────────┐
+ │ │ │
+ 🟠 发现 🔵 知识 🟢 数学人生
+ (视频发现流) (知识卡片流) (沉浸式体验)
+ │ │ │
+ ┌───────┼───────┐ │ ┌────────┼────────┐
+ │ │ │ ┌────┼────┐ │ │ │
+ 推荐 热门 专业分区 Hero 话题 卡片 数学专业 葫芦侠行
+ (12) 大卡 标签 流 模拟器 互动小说
+ │
+ ┌─────┼─────┐
+ 大一~大四 8章93节点
+ 全流程模拟 8+种结局
+
+ 🟣 我的
+ (个人中心)
+ │
+ ┌───────────┼───────────┐
+ │ │ │
+ 个人信息 我的内容 更多设置
+```
+
+### 2.2 全局功能
+
+| 功能 | 说明 | 入口 |
+|------|------|------|
+| AI 数学助教 | DeepSeek 大模型驱动的对话式问答 | 任意页面右上角按钮 |
+| 底部 TabBar | 四个模块切换入口 | 始终可见 |
+| 顶部品牌栏 | "数学脑"品牌标识 + AI入口 | 始终可见 |
+| 家长模式 | 使用时长统计 + 内容消费报告 | "我的"页面 |
+
+### 2.3 模块详情
+
+#### 🟠 发现 · 视频发现流
+
+**定位**:B站式数学垂类视频发现入口。
+
+**组成**:
+- **顶部搜索栏**:支持按标题、描述、作者、学科搜索
+- **子导航**:推荐 / 热门 / 专业分区
+- **12 个专业分区图标网格**:人工智能、计算机、电子信息、微电子、机械工程、自动化、能源动力、化工材料、生物医学、经济管理、环境工程、数学建模
+- **双列视频卡片流**:封面 + 标题 + 作者 + 时长 + 观看量
+- **视频详情弹窗**:标题、简介、作者、学科标签、外部链接跳转
+
+**数据**:当前内置 54 条占位视频(各学院志愿者提供),来自 seed_videos.json → SQLite。
+
+**交互**:
+- 点击分区图标 → 展示该学科专属视频列表 + 返回按钮
+- 点击视频卡片 → 弹出详情弹窗
+- 搜索实时过滤
+
+---
+
+#### 🔵 知识 · 知识卡片流
+
+**定位**:类似小红书/得到 App 的轻量数学知识发现,非视频形态。
+
+**组成**:
+- **每日 Hero 大卡**:渐变深蓝背景,今日主题 + 阅读统计
+- **横向滚动话题标签**:统计陷阱、概率思维、几何直觉、逻辑悖论、数学模型、博弈策略、数列之美、混沌理论
+- **信息流卡片(当前6张)**:
+ - 每张卡片包含:渐变色彩封面 + 一个引人思考的"问题" + 知识标题 + 描述 + 阅读时间
+ - 点击卡片 → 呼出 AI 助教深入探讨该主题
+
+**示例卡片**:
+- 被平均数骗过的所有人(你和马云的平均财富是多少?)
+- 彩票:一场精心设计的数学税(2元博500万,值不值?)
+- 为什么所有地图都在骗你(格陵兰岛有非洲大吗?)
+
+**交互**:
+- 点击话题标签 → 高亮筛选
+- 点击卡片 → 一键呼出 AI 深入追问
+
+---
+
+#### 🟢 数学人生 · 沉浸式体验
+
+**定位**:整个产品的核心差异化模块,包含两个独立子功能。
+
+##### 子功能一:数学专业模拟器(P0 主打)
+
+**定位**:让学生"跑完四年大学数学专业的全流程",在模拟中选择方向、面对挑战、最终走向不同结局。参考卵石实习生 App 的模式。
+
+**结构**:8学期(大一上→大四下),每学期约10-15个事件节点。
+
+**核心循环**:
+```
+选课 → 上课(课堂事件选择题)→ 期中考试 → 课外活动与社交 → 期末考试 → 学期结算 → 暑假事件 → 下学期
+```
+
+**大三专业分流**(核心关卡):
+
+```
+纯数学 应用数学 统计学
+ ├─ 分析(实变/泛函) ├─ 计算数学(数值分析) ├─ 数理统计
+ ├─ 代数(抽象代数) ├─ 金融数学(随机过程) ├─ 数据科学
+ └─ 几何(微分几何) └─ 生物数学(生物信息) └─ 精算
+```
+
+**属性系统**:GPA、学术倾向、社交值、兴趣值、精力值(有限分配)
+
+**毕业结局**:数学博士、量化交易员、高中教师、数据科学家、数学作家、转行程序员、退学创业者等。
+
+**指标**:用户留存率、日均访问次数、完整跑完四年的用户比例。
+
+##### 子功能二:葫芦侠行 · 互动视觉小说
+
+**定位**:北宋数学武侠背景的 8 章完整互动剧情。基于原创小说《葫芦侠行》改编。是深度内容的沉淀,适合喜欢叙事体验的用户。
+
+**规模**:8章 × 约12节点/章 = 93个交互节点
+
+**属性系统**:数理根基、侠义声望、心志韧性、关键角色好感度
+
+**结局**:8+ 种(真相守望 / 数理宗师 / 逍遥江湖 / 出海远航 / 悲情英雄 / 堕落之路 / 冤狱 / 虚无)
+
+**当前状态**:已开发完成,可运行。
+
+---
+
+#### 🟣 我的 · 个人中心
+
+**定位**:标准 App 个人中心,个人信息管理 + 数据概览 + 家长入口。
+
+**板块**:
+- **个人卡片**:头像 + 昵称 + ID + 身份标签(紫色渐变背景)
+- **数据统计**:已看内容数、收藏专题数、剧情存档数
+- **个人信息**:昵称与头像、兴趣方向、学习偏好
+- **我的内容**:我的收藏、观看记录、数学人生存档
+- **更多**:设置与帮助、意见反馈
+- **家长模式**(规划中):本周使用时长、内容消费分布、知识点图谱
+
+---
+
+### 2.4 后续规划功能
+
+| 优先级 | 功能 | 说明 |
+|--------|------|------|
+| P1 | 工具箱/趣味计算器 | 概率模拟器、数学魔术生成器、π查询器、分形画廊。作为留存工具,填补碎片时间 |
+| P1 | AI 数字人视频生成 | AI写稿 + TTS语音 + 静态数字人播报,降低视频制作成本 |
+| P1 | 数学冷知识随机卡片 | 摇一摇或点击刷新,轻量互动 |
+| P2 | 双语支持(i18n) | UI文案抽离、故事双语JSON、视频双语描述 |
+| P2 | 支教场景定制 | 为支教志愿者提供教学辅助功能、学生进度追踪 |
+| P3 | 学习报告/周报 | 每周生成内容消费总结,可分享给家长 |
+
+---
+
+## 三、交互与UI规范
+
+### 3.1 布局
+
+| 规范 | 值 |
+|------|------|
+| 容器宽度 | `min(390px, 100vw)`(iPhone 标准比例) |
+| 全屏高度 | `100dvh` |
+| 安全区 | `env(safe-area-inset-top)` / `env(safe-area-inset-bottom)` |
+| 桌面端 | 居中 + `border-radius: 34px` + 阴影(模拟设备壳) |
+| 字体 | `-apple-system, BlinkMacSystemFont, PingFang SC, Microsoft YaHei` |
+
+### 3.2 底部 TabBar
+
+- 四个入口图标:CSS 伪元素绘制,简约风格
+- 背景:白色半透明 + `backdrop-filter: blur(18px)` 玻璃感
+- 激活态:对应模块主色的浅色背景高亮
+- 适配 safe-area 底部留白
+
+### 3.3 色彩语义
+
+| 模块 | 主色 | 色值 |
+|------|------|------|
+| 发现 | 暖橙 | `#F97316` |
+| 知识 | 蓝色 | `#3B82F6` |
+| 数学人生 | 绿色 | `#10B981` |
+| 我的 | 紫色 | `#8B5CF6` |
+
+切换模块时,全局 CSS 变量 `--accent` 动态替换,顶部品牌栏和视觉小说内角色名同步变色。
+
+### 3.4 响应式
+
+- 移动端:全宽,safe-area 适配
+- 桌面端(≥480px):居中显示在模拟设备壳中,上方和两侧留白
+
+---
+
+## 四、技术架构
+
+### 4.1 技术选型
+
+| 层级 | 技术 | 用途 |
+|------|------|------|
+| 后端 | Flask(Python) | 路由、API、SQLite 管理 |
+| 数据库 | SQLite | 视频数据、行为日志 |
+| 前端 | 原生 HTML5 + CSS3 + Vanilla JS | SPA 页面切换、视觉小说引擎 |
+| AI | DeepSeek Chat API | AI 数学助教对话 |
+| 数据 | JSON 文件 | 剧情节点树、种子视频 |
+
+### 4.2 API 接口
+
+| 路径 | 方法 | 返回 | 说明 |
+|------|------|------|------|
+| `/` | GET | HTML | SPA 首页 |
+| `/api/videos` | GET | `{code, data, disciplines}` | 视频列表 + 学科分类 |
+| `/api/chat` | POST | `{code, reply}` | AI 对话 |
+| `/api/story` | GET | `{code, data}` | 互动剧情 JSON |
+| `/api/log` | POST | `{code, msg}` | 行为日志 |
+| `/api/students` | GET | `{code, data}` | 学生列表(B端预留) |
+
+### 4.3 视觉小说引擎
+
+共用引擎,数据隔离:
+- `seed_story.json` → 葫芦侠行(93节点,已完成)
+- `seed_simulator.json`(待建)→ 数学专业模拟器(约100-120节点)
+
+引擎特性:打字机效果、四属性系统、章节自动检测、localStorage 存档/读档/重置、选项效果预览。
+
+### 4.4 多端支持
+
+```
+ ┌─────────────────────┐
+ │ Flask 后端 + SPA │
+ │ (同一套代码) │
+ └─────────┬───────────┘
+ │
+ ┌───────────────┼───────────────┐
+ │ │ │
+ 浏览器访问 PWA 安装 小程序 web-view
+ (HTTP/HTTPS) (manifest.json) (壳包 + 嵌入URL)
+```
+
+不开发独立原生 App。通过 PWA + 各平台小程序 web-view 实现多端覆盖。需要 HTTPS 域名部署。
+
+---
+
+## 五、内容策略
+
+### 5.1 内容矩阵
+
+| 类型 | 数量 | 来源 | 更新频率 |
+|------|------|------|---------|
+| 科普视频 | 54条(占位) | 各学院志愿者 + 未来 AI 生成 | 周更 |
+| 知识卡片 | 6张 | 团队原创 | 周更 |
+| 话题标签 | 8个 | 团队精选 | 月更 |
+| 葫芦侠行 | 93节点 | 原创小说改编 | 已完成 |
+| 数学模拟器 | ~120节点 | 团队设计 | 开发中 |
+
+### 5.2 AI 助教
+
+- 引擎:DeepSeek Chat
+- 系统提示词设定:面向中小学生的数学引导者,200字以内,生活化语言
+- 触发点:知识卡片点击、任意页面右上角按钮、快捷提问按钮
+- 快捷提问:数学与医学 / 数学与建筑 / 数学与AI / 为什么要学
+
+### 5.3 AI 数字人视频(P1规划)
+
+| 阶段 | 做法 | 单条成本 |
+|------|------|---------|
+| MVP | AI写稿 → 人工录旁白 → 画面剪辑 | 几乎零 |
+| v1 | AI写稿 + TTS语音 + 静态数字人播报 | ¥5-15 |
+| v2 | 数字人 + AI辅助画面 + 人工精修 | ¥20-50 |
+
+### 5.4 双语规划(P2)
+
+- UI 文案抽离为 `i18n/zh.json` / `i18n/en.json`
+- 视频标题/描述增加英文字段
+- 互动剧情准备英文版 JSON
+- AI 助教检测用户输入语言并切换提示词
+- 英语社区冷启动路径:Reddit r/math、Product Hunt、YouTube 数学频道合作
+
+---
+
+## 六、用户留存与增长
+
+### 6.1 用户路径
+
+```
+第一次打开 ──→ 发现页刷视频(B站式,零门槛)
+ │
+ ┌──────────────┼──────────────┐
+ │ │ │
+ 看到感兴趣的 刷到知识卡片 听说"模拟器"
+ 学科视频 点开AI追问 想试试
+ │ │ │
+ └──────────────┴──────────────┘
+ │
+ ▼
+ 进入数学人生模块
+ │
+ ┌──────────────┼──────────────┐
+ │ │
+ 数学专业模拟器 葫芦侠行
+ (主打留存) (深度内容)
+ │
+ ▼
+ 每天打开,推进学期进度
+ 分享"我的数学人生结局"截图
+```
+
+### 6.2 增长策略
+
+| 阶段 | 策略 |
+|------|------|
+| **种子期** | 高校数学社团合作、B站/小红书数学科普KOL试用推荐 |
+| **增长期** | 模拟器结局截图社交分享裂变、微信小程序上线、家长社群传播 |
+| **规模化** | 与教育机构合作内置、学校采购、海外 Reddit/Product Hunt 推广 |
+
+### 6.3 支教渠道
+
+- 与常青藤支教队等公益组织合作
+- App 作为支教志愿者的辅助教学工具
+- 支教地学生通过 App 接触更丰富的数学内容
+- 不改变产品形态——支教是分发渠道,不是产品内核
+
+---
+
+## 七、盈利模式
+
+### 7.1 原则
+
+- 学生端永远免费
+- 不伤害公益初心
+- 不做广告、不做氪金
+- 不接受纯捐赠模式
+
+### 7.2 路径
+
+**主要路径:ToB 教育采购**
+- 学校/教育局采购 App 作为辅助教学工具
+- 按学生数量或服务等级收费
+- 与支教项目经费挂钩:支教本身有经费,App 作为其中合理的技术支出
+
+**辅助路径:会员订阅**
+- 基础内容永远免费(视频、知识卡片、模拟器主流程)
+- 高级功能订阅制:额外模拟分支、深度知识图谱、学习报告、家长面板
+- 参考 Duolingo Super 模式——免费可用,付费更便利
+
+---
+
+## 八、家长信任策略
+
+| 策略 | 做法 |
+|------|------|
+| 内容纯净 | 无广告、无社交陌生人、无氪金引导 |
+| 时间管控 | 内置"番茄钟":使用30分钟自动提醒休息 |
+| 学习报告 | "数学脑周报"——孩子看了什么、玩了什么、对哪些方向有兴趣 |
+| 家长模式 | 独立入口,展示使用时长、内容类型分布、知识点图谱 |
+| 零氪金 | 无内购、无抽卡 |
+| 定位话语 | "这不是一个刷题App,而是帮助孩子建立数学兴趣的引导工具" |
+
+---
+
+## 九、开发规范
+
+### 9.1 代码规范
+
+| 语言 | 规范 |
+|------|------|
+| Python | PEP 8,`black`/`ruff` 格式化 |
+| JavaScript | ES6,`let/const` 替代 `var`,camelCase 命名 |
+| CSS | 功能前缀命名(如 `vn-`、`feed-`),CSS 变量管理主题色 |
+| HTML | 区域注释分隔,语义化标签 |
+
+### 9.2 数据规范
+
+- API 统一返回格式:`{code: 0/-1, data: ..., msg: "..."}`
+- JSON 数据结构向后兼容
+- 数据库迁移使用 DB_VERSION 机制
+- API Key 通过环境变量读取,不可硬编码
+
+### 9.3 Git 规范
+
+- 分支:`main`(稳定)/ `dev`(开发)/ `feature/xxx`
+- Commit 前缀:`feat:` / `fix:` / `style:` / `refactor:`
+- 静态资源版本号:`style.css?v=X.Y` 防缓存
+
+### 9.4 安全规范
+
+- HTTPS 部署
+- API Key 环境变量隔离
+- 跨域白名单配置
+
+---
+
+## 十、开发优先级与路线图
+
+| 优先级 | 项目 | 说明 | 状态 |
+|--------|------|------|------|
+| ✅ P0 | 四大模块基础架构 | 发现/知识/数学人生/我的 | 已完成 |
+| ✅ P0 | 视频发现 + 12专业分区 | 搜索+推荐+热门+分区 | 已完成 |
+| ✅ P0 | 知识卡片流 | Hero大卡+话题+6张卡片 | 已完成 |
+| ✅ P0 | 葫芦侠行8章互动小说 | 93节点+四属性+8结局 | 已完成 |
+| ✅ P0 | 个人中心 | 资料+统计+菜单 | 已完成 |
+| ✅ P0 | AI 数学助教 | DeepSeek + 快捷提问 | 已完成 |
+| ✅ P0 | iPhone移动端适配 | 390px + 100dvh + safe-area | 已完成 |
+| 🔨 P0 | **数学专业模拟器** | 8学期全流程模拟+分流+结局 | **开发中** |
+| ⏳ P1 | 工具箱/趣味计算器 | 概率模拟器/数学魔术/分形画廊 | 待开发 |
+| ⏳ P1 | AI数字人视频生成 | AI写稿+TTS+数字人播报 | 待调研 |
+| ⏳ P1 | PWA + 小程序壳 | manifest.json + web-view | 待开发 |
+| ⏳ P2 | 双语支持(i18n) | UI抽离+故事双语+AI切换 | 待开发 |
+| ⏳ P2 | 支教场景功能 | 教学辅助+学生进度 | 待开发 |
+| ⏳ P2 | 家长模式 + 周报 | 时长统计+内容报告+分享 | 待开发 |
+| ⏳ P3 | ToB教育采购 | 学校端管理后台 | 待调研 |
+
+---
+
+## 十一、附录
+
+### A. 当前项目文件结构
+
+```
+miniapp/
+├── app.py # Flask 后端
+├── data/
+│ ├── seed_story.json # 葫芦侠行93节点
+│ ├── seed_videos.json # 54条种子视频
+│ ├── miniapp_v3.db # SQLite 数据库
+│ ├── build_story.py # 剧情生成脚本
+│ └── novel/ # 原著小说素材
+├── static/
+│ ├── css/style.css # 全局样式(~1400行)
+│ └── js/app.js # 前端逻辑(~550行)
+└── templates/
+ └── index.html # SPA 入口(~286行)
+```
+
+### B. 关键指标
+
+| 指标 | 定义 | 目标 |
+|------|------|------|
+| 日活跃用户 (DAU) | 每日打开 App 的用户数 | 种子期 100,增长期 10,000 |
+| 次留/7留/30留 | 次日/7日/30日留存率 | 7留 > 25% |
+| 模拟器完成率 | 跑完四年全流程的用户占比 | > 15%(初期) |
+| 人均使用时长 | 每日平均使用时间 | > 15分钟 |
+| 分享率 | 主动分享内容的用户占比 | > 5% |
diff --git a/docs/产品文档/开发对话记录.md b/docs/产品文档/开发对话记录.md
new file mode 100644
index 0000000..3d6feb1
--- /dev/null
+++ b/docs/产品文档/开发对话记录.md
@@ -0,0 +1,100 @@
+# 对话记录 — 数学脑项目
+
+> 对话时间:2026-07-11
+> 项目:数学脑 (miniapp)
+> 当前状态:Flask 后端已成功运行在 http://127.0.0.1:5000
+
+---
+
+## 对话 1:运行 app.py
+
+**用户请求**:运行这个 app
+
+**AI 执行过程**:
+
+1. **读取 app.py**:确认这是一个 Flask 后端应用,包含:
+ - SQLite 数据库初始化
+ - 54 条种子视频数据导入
+ - 8 张知识卡片种子数据
+ - DeepSeek AI 对话接口
+ - 路由:视频列表、AI 对话、行为日志、故事数据、模拟器、知识卡片、收藏、历史、用户资料
+
+2. **检查项目结构**:确认 data/ 目录、static/ 目录、templates/ 目录均存在,种子数据文件齐全。
+
+3. **安装依赖**:
+ ```powershell
+ pip install flask requests
+ ```
+ Flask 3.1.3 和 requests 2.32.3 均已安装。
+
+4. **查找 Python 解释器**:
+ - `python` 命令报 9009 错误(未找到)
+ - 通过 `where pip` 找到 pip 在 `D:\AN\Scripts\pip.exe`
+ - 确认 Python 在 `D:\AN\python.exe`(Python 3.12.7)
+
+5. **启动应用**:
+ ```powershell
+ D:\AN\python.exe app.py
+ ```
+ 成功启动,输出:
+ ```
+ ==================================================
+ 数学桥梁计划 · MVP 后端启动中...
+ ==================================================
+ [完成] 已导入 54 条视频数据
+
+ 访问地址: http://127.0.0.1:5000
+ * Running on http://127.0.0.1:5000
+ ```
+
+6. **结果**:应用在 http://127.0.0.1:5000 成功运行,浏览器预览已打开。
+
+**关键技术细节**:
+- 应用使用 DeepSeek API Key:已在代码中配置
+- 数据库:`data/miniapp_v3.db`
+- 种子数据:`data/seed_videos.json`(54条)、`data/seed_story.json`(93节点)、`data/seed_simulator.json`
+
+---
+
+## 对话 2:打包项目文件以供另一个 AI 接手
+
+**用户请求**:
+1. 打包工作区与 app 相关的文件
+2. 导出对话记录为 MD 文档
+3. 导出操作规范为 Skill
+4. 打包以上所有文件,供另一个 AI 继续开发
+
+**当前正在执行中**:
+- 创建对话记录 MD(本文件)
+- 创建 Skill(数学脑开发操作规范)
+- 打包为 zip
+
+---
+
+## 项目当前状态总结
+
+### 已完成的功能 (P0)
+- [x] 四大模块基础架构(发现/知识/数学人生/我的)
+- [x] 视频发现 + 12专业分区 + 搜索
+- [x] 知识卡片流(Hero大卡 + 话题标签 + 8张卡片)
+- [x] 葫芦侠行 8章互动剧情(93节点 + 四属性 + 8结局)
+- [x] 数学专业模拟器(已启动,部分完成)
+- [x] 个人中心(资料 + 统计 + 收藏/历史)
+- [x] AI 数学助教(DeepSeek + 上下文感知 + 快捷提问)
+- [x] iPhone 移动端适配(390px + 100dvh + safe-area)
+
+### 配置信息
+- **项目路径**:`C:\Users\Asus\Desktop\miniapp`
+- **Python 路径**:`D:\AN\python.exe`(Python 3.12.7)
+- **启动方式**:`D:\AN\python.exe app.py`
+- **访问地址**:`http://127.0.0.1:5000`
+- **数据库**:`data/miniapp_v3.db`(SQLite)
+- **DeepSeek API Key**:已在 app.py 中配置
+
+### 待开发
+- [ ] 数学专业模拟器完整剧情(当前数据不完整)
+- [ ] 用户真实登录系统
+- [ ] PWA + 小程序壳
+- [ ] 双语支持
+- [ ] 家长模式
+- [ ] 工具箱/趣味计算器
diff --git a/docs/产品文档/开发需求.md b/docs/产品文档/开发需求.md
new file mode 100644
index 0000000..e1506d6
--- /dev/null
+++ b/docs/产品文档/开发需求.md
@@ -0,0 +1,244 @@
+# 数学脑 · 开发需求文档
+
+> 版本:MVP v6.0 | 更新日期:2023年
+
+---
+
+## 一、项目概述
+
+| 项目 | 内容 |
+|------|------|
+| **产品名** | 数学脑 |
+| **代码名** | miniapp |
+| **类型** | 移动端 SPA(单页应用)Web App |
+| **架构** | Flask(Python 后端) + 原生 HTML/CSS/JS(前端) |
+| **适应端** | iPhone 移动端(390px 宽度,100dvh,safe-area 适配) |
+| **数据库** | SQLite |
+| **AI 引擎** | DeepSeek Chat API |
+
+---
+
+## 二、技术栈
+
+| 层级 | 技术 | 用途 |
+|------|------|------|
+| 后端框架 | Flask 3.x(Python) | 路由、API、数据库管理 |
+| 数据库 | SQLite(SQLite3) | 视频数据、行为日志 |
+| 前端 | 原生 HTML5 + CSS3 + Vanilla JS | SPA 页面切换、视觉小说引擎 |
+| AI 集成 | DeepSeek Chat API | AI 数学助教对话 |
+| 故事数据 | JSON(seed_story.json) | 8 章互动剧情节点树 |
+| 视频数据 | JSON(seed_videos.json)→ SQLite | 54 条专业分区视频 |
+| UI 风格 | 自制设计系统(CSS 变量 + flex/grid) | iPhone 式移动端适配 |
+
+---
+
+## 三、项目文件结构
+
+```
+miniapp/
+├── app.py # Flask 后端主文件
+├── data/
+│ ├── seed_story.json # 8章93节点互动剧情数据
+│ ├── seed_videos.json # 54条专业分区种子视频
+│ ├── miniapp_v3.db # SQLite 数据库
+│ ├── build_story.py # 剧情JSON生成脚本
+│ └── novel/ # 原著小说txt(素材参考)
+│ ├── 葫芦侠行(第一章).txt
+│ ├── 葫芦侠行(第二章).txt
+│ ├── ...
+│ └── 框架.txt
+├── static/
+│ ├── css/
+│ │ └── style.css # 全局样式(~1400行)
+│ └── js/
+│ └── app.js # 前端核心逻辑(~550行)
+└── templates/
+ └── index.html # SPA 入口页面(~286行)
+```
+
+---
+
+## 四、后端 API 接口
+
+| 路径 | 方法 | 参数 | 返回 | 说明 |
+|------|------|------|------|------|
+| `/` | GET | — | HTML | 返回 SPA 首页 |
+| `/api/videos` | GET | `module`(选填), `discipline`(选填) | `{code, data, disciplines}` | 视频列表 + 学科分类 |
+| `/api/chat` | POST | `{message, student_id}` | `{code, reply, is_fallback}` | AI 对话(DeepSeek) |
+| `/api/story` | GET | — | `{code, data}` | 获取互动剧情 JSON |
+| `/api/log` | POST | `{user_id, action, target}` | `{code, msg}` | 行为日志记录 |
+| `/api/students` | GET | — | `{code, data}` | 学生列表(B端预留) |
+
+### 视频数据模型(videos 表)
+
+| 字段 | 类型 | 说明 |
+|------|------|------|
+| id | INTEGER PK | 自增 |
+| title | TEXT NOT NULL | 视频标题 |
+| module | TEXT | 所属模块(如"数人") |
+| sub_category | TEXT | 子分类 |
+| discipline | TEXT | 学科(如"人工智能") |
+| discipline_icon | TEXT | 学科 emoji |
+| video_url | TEXT | 外部视频链接 |
+| description | TEXT | 视频简介 |
+| duration_min | INTEGER | 时长(分钟) |
+| author | TEXT | 作者(学院+姓名) |
+
+---
+
+## 五、前端架构
+
+### 五层设计
+
+```
+┌─────────────────────────────────┐
+│ index.html │ ← 结构层:DOM 容器
+├─────────────────────────────────┤
+│ style.css │ ← 表现层:响应式 + 动画
+├─────────────────────────────────┤
+│ app.js │ ← 逻辑层:路由 + 状态 + API
+├─────────────────────────────────┤
+│ seed_story.json │ ← 数据层:剧情节点树
+├─────────────────────────────────┤
+│ Flask API │ ← 服务层:数据库 + AI
+└─────────────────────────────────┘
+```
+
+### 核心 JavaScript 模块
+
+| 模块 | 函数 | 功能 |
+|------|------|------|
+| **全局状态** | `vnState`, `allVideos`, `storyData` | 应用级状态管理 |
+| **模块切换** | `switchModule()`, `hideAllPanels()` | 四Tab页面切换、DOM显隐 |
+| **发现页面** | `loadVideos()`, `renderVideoList()`, `renderDisciplineGrid()` | 视频流渲染、12学科网格 |
+| **知识页面** | `renderShushuoFeed()`, `updateShushuoHero()` | 知识卡片Feed渲染 |
+| **数学人生** | `navigateVN()`, `buildChoiceButtons()`, `applyVNStats()` | 视觉小说引擎核心 |
+| **AI聊天** | `openChat()`, `sendMessage()`, `quickAsk()` | AI对话弹窗 |
+| **数据持久化** | `saveVNState()`, `loadVNSave()`, `resetVNSave()` | localStorage存档 |
+
+---
+
+## 六、视觉小说引擎(VN Engine)
+
+### 剧情数据模型
+
+```json
+{
+ "title": "葫芦侠行",
+ "start_node": "ch1_start",
+ "nodes": {
+ "节点ID": {
+ "scene": "场景描述文本",
+ "character": "说话角色",
+ "sprite": "角色立绘URL(可选)",
+ "choices": [
+ {
+ "text": "选项文本",
+ "next": "下一节点ID",
+ "effects": {
+ "shuli": 整数, // 数理根基变化
+ "xiayi": 整数, // 侠义声望变化
+ "xinzhi": 整数, // 心志韧性变化
+ "favorability": { // 角色好感度变化
+ "角色名": 整数
+ }
+ }
+ }
+ ]
+ }
+ }
+}
+```
+
+### 四属性系统
+
+| 属性 | 键名 | 初始值 | 说明 |
+|------|------|--------|------|
+| 📐 数理根基 | `shuli` | 10 | 解锁高阶功法、影响论道胜负 |
+| ⚔️ 侠义声望 | `xiayi` | 10 | 影响NPC态度、武林大会成绩 |
+| 🧘 心志韧性 | `xinzhi` | 10 | 面对挫折的选择余地 |
+| 💕 好感度 | `favorability` | 0(每人) | 触发专属剧情、影响结局 |
+
+### 当前剧情规模
+
+- **8 章**:初入江湖 → 西部教习 → 数理论道 → 脑学初创 → 报国之路 → 暗流涌动 → 陋室修炼 → 真相之门
+- **93 个交互节点**
+- **8+ 种结局**(真相守望、数理宗师、逍遥江湖、出海远航、悲情英雄、堕落之路、冤狱、虚无)
+
+---
+
+## 七、UI 设计系统
+
+### 色彩系统
+
+| 用途 | CSS 变量 | 色值 |
+|------|----------|------|
+| 发现页主色 | `--shuren` | `#F97316`(橙) |
+| 知识页主色 | `--shushuo` | `#3B82F6`(蓝) |
+| 数学人生主色 | `--shusi` | `#10B981`(绿) |
+| 我的页主色 | `--mine` | `#8B5CF6`(紫) |
+| 页面背景 | `--bg` | `#F2F3F7` |
+| 卡片背景 | `--card` | `#FFFFFF` |
+| 主文字 | `--text` | `#1E1E2E` |
+
+### 布局规范
+
+- **容器宽度**:`min(390px, 100vw)`(iPhone 比例)
+- **桌面端**:居中显示 + `border-radius: 34px` + 阴影(模拟设备壳)
+- **底部 TabBar**:白色半透明玻璃感 + `backdrop-filter: blur(18px)` + safe-area
+- **顶部状态栏**:白色半透明 + 模糊 + `z-index: 50`
+- **字体**:`-apple-system, PingFang SC, Microsoft YaHei`
+
+---
+
+## 八、部署与运行
+
+### 启动命令
+
+```powershell
+cd C:\Users\Asus\Desktop\miniapp
+py app.py
+```
+
+### 访问地址
+
+```
+http://127.0.0.1:5000/
+```
+
+### 环境要求
+
+- Python 3.12+
+- Flask
+- requests
+- python-docx(素材提取用,非运行时必需)
+
+### 数据初始化
+
+- 首次启动自动执行 `init_db()` + `load_seed_videos()`
+- 54 条视频数据从 `seed_videos.json` 导入 SQLite
+- 互动剧情从 `seed_story.json` 按需读取
+- 旧数据库可通过改名绕过锁定问题
+
+---
+
+## 九、待开发需求
+
+### P0(核心)
+- [ ] 用户登录系统(OAuth/手机号)
+- [ ] 视频上传与审核后台
+- [ ] 互动剧情章节选择器(允许跳章/读档)
+- [ ] 个人中心真实数据绑定(收藏、观看记录、剧情存档)
+
+### P1(增强)
+- [ ] 社交功能(评论、点赞、分享)
+- [ ] 知识页数学小测验
+- [ ] 视觉小说立绘 + 背景图系统
+- [ ] 好感度可视化面板
+- [ ] 多端适配(小程序版本)
+
+### P2(远期)
+- [ ] 微服务拆分
+- [ ] 实时协作编辑(多人剧情共创)
+- [ ] AI 生成剧情分支
+- [ ] 数据分析看板(学生行为)
diff --git a/docs/产品文档/数学脑.docx b/docs/产品文档/数学脑.docx
new file mode 100644
index 0000000..34f78b8
Binary files /dev/null and b/docs/产品文档/数学脑.docx differ
diff --git a/docs/产品文档/数学脑_暑期MVP_需求评审包.md b/docs/产品文档/数学脑_暑期MVP_需求评审包.md
new file mode 100644
index 0000000..f7bc0c2
--- /dev/null
+++ b/docs/产品文档/数学脑_暑期MVP_需求评审包.md
@@ -0,0 +1,146 @@
+# 数学脑|暑期微信小程序 MVP 需求评审包
+
+> 状态:评审准备稿 v0.1(非最终 PRD)
+> 用途:用于首次需求评审,确认产品方向、MVP 边界、团队分工与暑期交付标准。
+> 已知依据:现有 Flask Web Demo、既有产品描述与开发需求文档。
+
+## 0. 本次评审需要达成的结论
+
+1. 明确首发用户,只选择一个核心人群作为 MVP 的服务对象。
+2. 确认微信小程序 MVP 的核心闭环与不做清单。
+3. 锁定暑期版本必须上线的模块、团队负责人和时间节点。
+4. 确认试点方式与成功标准,避免“做完了但无法判断是否有效”。
+
+## 1. 当前 Demo 的客观基础
+
+现有 Web Demo 已具备四个一级入口:发现视频、知识卡片、数学人生互动剧情、个人中心;另有 AI 助教、收藏、历史、昵称头像及专业模拟器。技术上采用 Flask、SQLite、原生 HTML/CSS/JS,适合快速验证。
+
+它验证了产品表达与内容形态,但尚未验证真实用户的持续需求。当前最大的风险不是前端能否做出来,而是:首发用户是否清晰、内容能否稳定供给、互动剧情是否能带来留存,以及小程序发布所需的账号、内容安全和隐私要求是否被提前纳入范围。
+
+## 2. 建议的 MVP 产品定义(供评审)
+
+### 一句话定义
+
+面向 **对大学专业与现实应用感到好奇的中学生及大学新生** 的数学兴趣小程序:用短内容建立“数学和我有关”的认知,用轻互动促成下一次打开。
+
+### 首发核心闭环
+
+`选择感兴趣的专业/主题 → 浏览 1 条内容 → 收藏或追问 AI → 获得下一条推荐/当日任务 → 次日回访`
+
+### 首发价值主张
+
+“不用刷题,也能理解数学如何进入 AI、医学、游戏和真实世界。”
+
+## 3. 暑期 MVP 范围建议
+
+| 优先级 | 模块 | 首发必须交付 | 验收口径 |
+|---|---|---|---|
+| P0 | 新手引导 | 首次进入选择 3 个兴趣主题;可跳过 | 90% 以上测试用户可在 60 秒内完成并进入内容页 |
+| P0 | 发现内容 | 推荐流、主题筛选、关键词搜索、内容详情、收藏 | 100 条以内内容加载首屏 ≤ 2 秒(常规 4G/Wi-Fi 测试) |
+| P0 | 知识卡片 | 每日精选、话题筛选、详情页、AI 追问入口 | 每张卡片可完整阅读、收藏并产生可追踪事件 |
+| P0 | 个人中心 | 微信授权后的基础资料、收藏、浏览记录 | 用户可查看并删除自己的收藏/记录 |
+| P0 | 内容后台(最小) | 内容录入、上下架、标签/封面/来源维护 | 运营同学无需改代码即可新增一条内容 |
+| P0 | 数据埋点 | 首次打开、兴趣选择、内容曝光/点击/完成、收藏、AI 调用 | 每个核心事件带匿名用户标识、时间、内容 ID |
+| P1 | 数学人生 | 首发只保留“第一章试玩 + 存档 + 完成页” | 5 名测试用户可不经讲解完成一条分支 |
+| P1 | AI 助教 | 限次、敏感词兜底、内容上下文问答 | 90% 的测试问题在 8 秒内返回;异常时给出可理解提示 |
+| P2 | 专业模拟器 | 仅保留为后续专题,不进入首发导航 | 进入下一阶段前以试点反馈决定是否开发 |
+
+### 明确不做(首发保护范围)
+
+- 用户自行上传视频、评论区、私信、排行榜、付费体系。
+- 全量 8 章互动小说、多结局系统、角色好感度可视化。
+- 推荐算法、复杂社交关系、跨校内容平台。
+- 账号密码系统;首发使用微信授权与最小化用户信息。
+
+## 4. 关键用户旅程
+
+### 旅程 A:新用户第一次获得价值
+
+1. 从社团/校园海报/朋友分享进入小程序。
+2. 选择“AI、医学、游戏”等至少 3 个兴趣主题。
+3. 看到与主题匹配的内容卡,阅读/观看一条。
+4. 收藏、追问 AI,或点击“再看一条”。
+5. 在个人中心看到记录,并收到下一次可回访的内容入口。
+
+### 旅程 B:回访用户
+
+1. 打开小程序后看到“今日数学发现”或上次未完成内容。
+2. 在 3 次点击内进入内容详情。
+3. 完成阅读、收藏或 AI 追问后,记录行为并给出下一步。
+
+### 旅程 C:内容运营
+
+1. 在后台创建内容,填写标题、摘要、主题、封面、来源与版权状态。
+2. 保存为草稿并预览。
+3. 经负责人审核后上线;必要时可立即下架。
+
+## 5. 建议团队结构(7–8 人)
+
+| 角色 | 建议人数 | 核心责任 | 暑期关键产出 |
+|---|---:|---|---|
+| 产品负责人 / 项目经理 | 1 | PRD、排期、需求决策、用户访谈、验收 | 版本路线图、PRD、周报、测试结论 |
+| UI/UX 设计师 | 1 | 小程序信息架构、交互原型、视觉系统、可用性测试 | Figma 原型、设计规范、开发标注 |
+| 小程序前端工程师 | 2 | 页面、状态、组件、性能与真机兼容 | 小程序客户端与组件库 |
+| 后端工程师 | 1–2 | 用户/内容/收藏/埋点 API、CMS、部署 | 后端服务、数据库、管理端 |
+| 内容策划 / 运营 | 1 | 内容选题、编辑、审核、试点拉新 | 首发内容库、运营日历、版权台账 |
+| 测试 / 数据(可由产品或后端兼任) | 0–1 | 用例、真机测试、埋点校验、反馈归纳 | 测试清单、数据看板、问题单 |
+
+**7 人最小配置**:产品 1、设计 1、前端 2、后端 1、内容运营 1、测试/数据 1。
+**8 人推荐配置**:增加 1 名后端或内容运营,取决于团队更缺“技术交付”还是“持续内容”。
+
+## 6. 暑期 8 周建议节奏
+
+| 周次 | 目标 | 可验收产物 |
+|---|---|---|
+| 第 1 周 | 需求确认与用户访谈 | PRD v1、范围冻结、低保真原型、10 位目标用户访谈提纲 |
+| 第 2 周 | 设计与技术方案 | 高保真原型、接口清单、数据模型、内容模板、埋点方案 |
+| 第 3–4 周 | P0 开发 | 可用的发现/知识/收藏/个人中心,联调环境 |
+| 第 5 周 | 后台、AI、埋点与内容导入 | 内容可配置、核心事件可查询、首批内容入库 |
+| 第 6 周 | 内测与修复 | 20–30 人封闭测试、问题清单关闭率 ≥ 90% |
+| 第 7 周 | 小范围试点 | 首发包、隐私说明、审核材料、试点数据 |
+| 第 8 周 | 复盘与上线决策 | 指标复盘、v1.1 Backlog、是否公开发布的结论 |
+
+## 7. 成功指标(需在评审中确认基线与目标)
+
+| 指标 | 建议定义 | MVP 目标(建议值) |
+|---|---|---:|
+| 激活率 | 首日完成兴趣选择且打开 1 条内容的用户 / 新用户 | ≥ 60% |
+| 首次价值达成 | 首次会话中阅读/观看 ≥ 1 条内容,或完成 1 次收藏/AI 追问 | ≥ 70% |
+| D1 留存 | 次日再次打开的激活用户 / 激活用户 | ≥ 25% |
+| 内容交互率 | 收藏、AI追问或分享 / 内容详情浏览 | ≥ 15% |
+| 内容有效性 | 试点问卷中认为“对理解数学的实际用途有帮助”的用户比例 | ≥ 70% |
+| 稳定性 | 核心流程无阻断错误的测试会话比例 | ≥ 99% |
+
+> 注:这是试点建议目标,不应被当作已验证事实;第 1 周用户访谈后可调整。
+
+## 8. 技术与合规前置条件
+
+- 小程序端建议独立实现,不直接把 Flask 网页嵌入 WebView;可复用数据模型、API 设计和内容素材。
+- 服务端需从 SQLite 演进到适合部署的数据库与对象存储方案(具体选型待技术负责人评估)。
+- AI Key 只存在服务端环境变量,绝不能写入小程序端或仓库。
+- 视频/图文须维护来源、作者授权、版权状态及下架机制。
+- 首发前准备隐私政策、用户协议、未成年人保护说明、内容投诉/反馈入口;具体要求以微信平台和适用法规的最新规则为准。
+
+## 9. 需求评审议程(60–90 分钟)
+
+1. **5 分钟:目标对齐** — 这个暑期要验证什么,而不是“想做什么”?
+2. **15 分钟:目标用户与场景** — 是否同意“中学生 + 大学新生”为首发核心?
+3. **20 分钟:范围取舍** — 逐项确认 P0/P1/P2,尤其是剧情与专业模拟器是否进首发。
+4. **15 分钟:内容与运营** — 首发内容数量、来源、审核人、更新频率。
+5. **15 分钟:技术与合规** — 小程序架构、账号、数据、AI、审核与发布责任人。
+6. **10 分钟:团队与排期** — 确认每个角色负责人、每周例会和验收机制。
+
+## 10. 必须由你确认的决策题(PRD Discovery)
+
+1. **核心问题**:你更想解决“学生觉得数学无用/无趣”,还是“学生不知道数学专业和真实行业的联系”?只能选一个作为首发主问题。
+2. **首发用户**:首发优先服务初高中生,还是大学新生?是否限定某所学校/城市做试点?
+3. **暑期成果**:8 周结束时,你最希望拿到的是可公开发布的小程序,还是 20–50 人验证后的可用试点版?
+4. **成功标准**:你愿意用哪一个指标作为第一优先级:留存、内容完成率、AI 对话使用率,还是校园试点人数?
+5. **内容来源**:首发 30–50 条内容由谁生产/审核?是否已有明确授权?
+6. **剧情定位**:数学人生是首发的差异化主角,还是上线后再测试的实验模块?
+7. **团队约束**:7–8 人中已有的技术、设计、运营成员各有几位?每人每周可投入多少小时?
+8. **预算与资源**:是否有云服务、域名、主体、小程序认证、AI 调用和内容制作预算?
+
+## 11. 评审后的下一步
+
+你确认第 10 节的决策后,产出正式《数学脑微信小程序 MVP PRD v1.0》,严格包含:执行摘要、用户体验与功能、AI 要求、技术规格、安全隐私、分阶段路线图、风险、用户故事与逐项验收标准;并再拆成 Epic、迭代 Backlog 和 8 周排期。
diff --git a/docs/会议记录/需求评审会议纪要_20260712.md b/docs/会议记录/需求评审会议纪要_20260712.md
new file mode 100644
index 0000000..8ff3697
--- /dev/null
+++ b/docs/会议记录/需求评审会议纪要_20260712.md
@@ -0,0 +1,407 @@
+# 数学脑(MathBrain)— 内部需求评审会议纪要
+
+---
+
+## 会议信息
+
+| 项 | 内容 |
+|---|---|
+| 日期 | 2026年7月12日(周日)14:00–14:45 |
+| 地点 | 远程 · 腾讯会议 |
+| 参会人 | **PM**(产品经理,3年教育产品经验)、**Dev**(全栈开发,5年 Flask/前端经验) |
+| 会议目标 | 逐条辩论 P0/P1/P2 需求清单,形成可交付排期 |
+| 当前版本 | MVP v8.0(已上线:莫兰迪 UI、剧本广场、立绘系统、葫芦侠行93节点) |
+
+---
+
+## 辩论实录
+
+### P0-1: 数学专业模拟器完整数据填充(8学期120节点)
+
+**PM:** 我先讲这个。模拟器现在是半成品——只有60个节点,覆盖大概2年的内容。但这个功能是我们"数学人生"板块的第二引擎,用户点进去玩到一半就断了,体验非常差。我主张把8学期120节点一次性填完。这不是纯内容工作——每个节点的 choice 分支、属性影响、结局判定,这些都有逻辑骨架,我们只是把血肉填进去。
+
+**Dev:** 你说的"只是填血肉",我不同意。我先问你:120个节点,每个节点平均3段叙事文本、2-3个选项、effects 数值设计——你用 AI 生成初稿也得人工调,对吧?你算过没有,60节点→120节点,按当前 seed_simulator.json 的密度来看,每个节点需要约15分钟调优,这就是30人时。但这不是纯体力活——你剧情分支树的平衡性要不要测?gpa/interest/skill 三属性的数值曲线要不要调?现在 engine 的节点跳转是硬编码在 JSON 里的 `next` 字段,你在写的时候就容易出现死循环或者无法到达的结局。
+
+**PM:** 我认可你说的数值平衡问题。但这恰恰说明我们需要一次性做完——如果分开做,第二期添加的节点必然会打破第一期的数值平衡,回头又要返工。120节点一个版本搞定,后续只做 bugfix。
+
+**Dev:** 你说的是一个好理由,但我担心的是另一个问题:当前引擎的限制。你看 `app.js` 里的 `simState`,和葫芦侠行的状态机是两套独立的全局变量,但渲染逻辑共享同一套 `renderVNNode()`。这意味着如果你要在模拟器里加入"学期切换动画""成绩单展示""毕业典礼"这些仪式感节点,现有的 render 逻辑根本撑不住。你不光要填数据,你还得改前端渲染引擎。
+
+**PM:** 等等,我说的就是填充数据,我没说要加学期切换动画。那些是 nice-to-have。
+
+**Dev:** OK,那我们就说纯数据填充。120节点,我给4个工作日——前提是你给我一份完整的节点树草稿,我做 JSON 编码和数值校对。你自己写不了 JSON 的话,我给你一个 Excel 模板你填,我再转。
+
+**PM:** 可以,我出剧情草稿 + 属性曲线设计,你做编码。那你觉得120节点够不够?
+
+**Dev:** 120节点8学期,平均每学期15个节点,每个节点2-3个选择,分支深度大概3-4层——够用户玩30-40分钟,可以了。再多就是内容膨胀,边际效用递减。
+
+| 结论 | ⚠️ **有条件通过** |
+|---|---|
+| 工作量 | PM 3天(剧情草稿+数值设计)+ Dev 4天(JSON编码+前端引擎适配) = **7人天** |
+| 风险 | 🟡 中(数值平衡 + 引擎适配可能溢出) |
+| 条件 | PM 先出完整草稿,Dev 评估后再开工 |
+
+---
+
+### 多剧本体系(NG架构:主剧本触发衍生剧本)
+
+**PM:** 这个是我最近一直在想的大方向。现在我们只有《葫芦侠行》一个主剧本,93个节点8章,很完整。但如果用户玩完第八章某个结局后,系统提示"新篇章已解锁——《三力思外传》",用户就会想继续玩。这就像 Netflix 的互动剧集,一个主剧火了之后出衍生剧。技术上也不复杂吧——就是多套 JSON 之间通过结局节点互相引用。
+
+**Dev:** 不复杂?我跟你掰扯一下。当前代码:`storyData` 是一个全局变量,加载后 `vnState.currentNode` 指向一个节点,rendering 逻辑假设你在单一剧本里。你加了多个剧本,我需要:第一,剧本间状态转移——用户在剧本A里积累的 shuli/xiayi/xinzhi/favorability 要不要带到剧本B?带的话,怎么映射?不带的话,多剧本之间就是完全割裂的,用户感受不到"同一个世界观"。
+
+**PM:** 这个确实要讨论。我的直觉是"部分继承"——像《巫师3》那样,前作存档影响续作开局,但不是1:1映射。比如葫芦侠行主剧本通关后,厚朴侠好感度超过30的玩家,在《厚朴侠前传》里解锁特殊对话线。
+
+**Dev:** 行,就算我们解决了状态继承,第二个问题:存档系统。现在5个槽位只存 `currentNode` + `stats` + `favorability`。多剧本的话,每个槽位要不要存"哪个剧本的哪个节点"?存档面板要不要按剧本分组显示?这些全是前端改动。
+
+**PM:** 等等,你说的这些都是必须改的吗?如果第一期我们只做最简单的方案:主剧本结局节点里加一个 `next_story` 字段指向另一个 JSON 的 start_node,然后系统弹一个提示"新剧本已解锁",用户手动回剧本广场选择。不做状态继承,不做跨剧本存档。
+
+**Dev:** 这个可以。改动量小很多:只是在结局渲染时读 `next_story` 字段,弹 toast。存档不用改。但你这样做完之后,用户期望就被拉高了——他们看到"新剧本解锁",就以为是一个连续的体验,结果进去发现数据全清零。这个体验落差你能接受吗?
+
+**PM:** 能接受。先给用户"发现惊喜"的感觉,后续版本再完善继承逻辑。MVP 只要解锁+跳转就行。
+
+**Dev:** 那就这样。但我有一个技术债提醒:如果将来要做状态继承,现在的数据模型就需要预留 `cross_story_state` 字段,否则回头要重构存档表。
+
+| 结论 | ⚠️ **有条件通过(MVP版本)** |
+|---|---|
+| 范围 | 仅做"结局节点→解锁新剧本提示→手动跳转",不做状态继承 |
+| 工作量 | **4人天**(Dev 3天引擎改造 + PM 1天新剧本草稿) |
+| 风险 | 🟡 中(用户期望落差;数据模型需预留扩展) |
+| 技术债 | 存档表需预留 `story_id` 字段 |
+
+---
+
+### 葫芦侠行2.0:Phaser.js 星露谷级互动
+
+**PM:** 好,到我最想推的 feature 了。现在的葫芦侠行是纯文字+静态立绘,交互就是点按钮。我调研过 Phaser.js,它是 HTML5 2D 游戏框架,能做角色在场景里走动、对话气泡弹出、迷你游戏(比如射箭小游戏代替掷骰子判定)。你一个月装好 Phaser,做出第一幕——角色可在秉公习武堂地图上行走,和 NPC 对话,战斗用回合制数学题——绝对爆。
+
+**Dev:** (吸一口气)我要泼冷水了。第一,你对 Phaser.js 调研到什么程度了?你跑过 demo 没有?
+
+**PM:** 看了一些教程,API 看起来不复杂,scene-preload-create-update 的生命周期。
+
+**Dev:** 好,那我问你几个问题。第一,我们现在的架构是 Flask 渲染 HTML,JS 接管 SPA 切换。Phaser.js 是一个独立的渲染引擎,它自己管理 canvas。你要把 Phaser 的 canvas 嵌到我们的 MathLife 面板里,CSS 怎么接管?safe-area 适配怎么做?390px 宽度的 canvas 上做地图行走,体验怎么样?
+
+**PM:** Canvas 可以响应式缩放,Phaser 有 Scale Manager。
+
+**Dev:** Scale Manager 是为游戏设计的,不是为嵌在 WebView 里的 SPA panel 设计的。我真的做过。你 Phaser 跑起来之后,`switchModule()` 切换到"发现"tab,Phaser 场景是 pause 还是 destroy?如果你 destroy 再 recreate,加载时间可能3-5秒。你让用户每次切换 tab 都等?
+
+**PM:** 那就 pause,保持内存。
+
+**Dev:** 移动端内存是个大问题。Phaser 跑一个 tilemap + 8个 sprite + 对话系统,大概占 80-120MB。加上我们现有的 DOM 渲染,低端 iPhone 可能直接闪退。这不是我说做不到,是移动端 WebView 的限制。
+
+**PM:** 那你觉得时间线呢?不要只说不行,给一个你认为可行的方案。
+
+**Dev:** 如果你真的要做互动地图,我的建议:不用 Phaser,用纯 CSS + DOM 做。就用 `position: absolute` 的图片+按钮放在一个有背景图的地图容器里,角色走动用 CSS transition。不需要 Phaser 的重型渲染。星露谷的感觉可以用像素风 CSS 背景图 + 莫兰迪调色来模拟。这叫"低配星露谷"——开发2周,用户体验能达到你期望的60%。
+
+**PM:** 60% 不够,我要的是真正的沉浸感。纯 DOM 做不出来角色在场景里连续走动的感觉,只能做 warp(瞬移)。
+
+**Dev:** 你要连续走动,Phaser 确实能给你。但我认真评估:Phaser 迁移 = 6周(学习1+搭建1+场景1+对话系统1+移动适配1+测试1),风险高。而且做完之后,以后所有人改剧情都得学 Phaser,不是改 JSON 那么简单。这是架构级变更。
+
+**PM:** 好吧。我承认之前的1人月估计太乐观了。那我们先做你提的"低配星露谷"方案——CSS DOM 地图 + 瞬移交互,2周交付。如果用户反馈很好,再评估 Phaser 迁移。
+
+**Dev:** 同意。但我建议我们在 CSS 方案里预留一个接口——地图走动的渲染抽象成一个 `renderMap()` 函数,将来如果要切 Phaser,只需要替换这个函数的实现。
+
+| 结论 | ⚠️ **有条件通过(降级为CSS低配版)** |
+|---|---|
+| 方案 | CSS DOM 地图 + 像素风背景 + 瞬移交互(非 Phaser) |
+| 工作量 | **10人天**(Dev 8天 + UI素材 2天) |
+| 风险 | 🟡 中(像素风美术资源需设计;Phaser 作为 V3 选项保留) |
+| 备注 | Phaser.js 完整迁移 → 至少6周,移入长期规划 |
+
+---
+
+### P1-1: 用户登录(邀请码+密码方案C)
+
+**PM:** 这个我们拖太久了。老板定了方案C——邀请码+密码登录,未来接微信。现在的用户体验是 localStorage 生成一个 random userId,换设备数据全丢。收藏、历史、存档全没。这是核心竞争力问题:用户为什么要在我们的 App 里投入时间?他们需要"这是我的账号"的归属感。
+
+**Dev:** 技术上我不反对,登录系统在 Flask 里很简单:一个 `users` 表加 username/password_hash/invite_code 三个字段,`/api/login` `/api/register` 两个接口,前端一个登录弹窗。核心工作量在:邀请码的生成和管理。谁生成?手动在数据库里插?还是做一个简单的管理页面?
+
+**PM:** 第一期手动管理。我们一共也就几百个用户,运营同学在后台数据库里插邀请码就行。不需要管理页面。
+
+**Dev:** 那还有个问题:登录后,用户现有的 localStorage 数据怎么迁移?如果用户已经玩了2小时葫芦侠行,有5个存档——一登录,存档要 merge 到服务器。merge 逻辑你想清楚了吗?
+
+**PM:** 简单策略:注册时检测 localStorage 有没有存档,有的话自动上传到服务器端 `/api/simulator/save` 接口。之后服务器端为准。冲突不做合并且,提示用户选择保留本地还是服务器。
+
+**Dev:** 可以。那大概:后端 `users` 表 + 登录/注册 API + session/cookie 管理 = 2天;前端登录弹窗 + localStorage 迁移 + 所有 API 加 `user_id` 鉴权 = 2天。总共4天,但我必须在每个现有 API 上加鉴权,这个测试覆盖面要大。
+
+**PM:** 我觉得值。做了登录之后,收藏和历史才有真正的持久性。而且有了 user_id,我们后续的家长周报、学习数据统计才能做。
+
+**Dev:** 同意。但我有一个建议:密码不要存明文,用 bcrypt hash。Flask 有 `werkzeug.security`,不需要额外依赖。
+
+| 结论 | ✅ **通过** |
+|---|---|
+| 工作量 | **4人天** |
+| 风险 | 🟢 低 |
+| 依赖 | 无 |
+
+---
+
+### P1-2: 交互剧情章节选择器
+
+**PM:** 这个需求非常明确:用户通关第八章某个结局后,回到剧本广场,每个已通关的章节可以自由重玩。比如我想重玩第四章的"论道大会"那段,不需要从头开始。这是互动小说类产品的标配——你看《隐形守护者》有章节选择,《Late Shift》有。
+
+**Dev:** 技术上不复杂:首先需要一个"通关标记"——用户到达过哪些节点。我们在 localStorage 或服务器端记录一个 `reached_nodes: []` 数组。然后前端渲染章节选择器:8个章节卡片,已到达过的章节可点击,点击后直接 `vnState.currentNode = 章节入口节点ID`。问题是:用户的 stats(shuli/xiayi/xinzhi)怎么办?直接用当时的存档值?还是给默认值?
+
+**PM:** 给"当时的存档值"。用户第一次通关时我们自动存一个"章节快照"(stats + favorability)。重玩时用快照。如果用户多次通关同一章,保留最近一次的快照。
+
+**Dev:** 那存档表要扩展了。现在5个手动槽位之外,再加一个"系统自动快照"机制。这个改动量不大,但存储逻辑需要仔细设计:每个章节一个快照,8章 = 8条记录。
+
+**PM:** 这个改动会影响现有存档面板吗?
+
+**Dev:** 不影响,系统快照不显示在用户手动存档面板里。它只是一个"读取时用"的隐藏数据。
+
+| 结论 | ✅ **通过** |
+|---|---|
+| 工作量 | **3人天**(Dev 2天快照机制 + 1天UI) |
+| 风险 | 🟢 低 |
+
+---
+
+### P1-3: 知识卡片扩充 + 周更机制
+
+**PM:** 目前只有8张知识卡片。8张。用户点"知识"tab,翻三下就到底了。"知识"是我们四大核心模块之一,8张卡片完全撑不起来。我主张用 AI 生成初稿 + 人工审核,每周3张,一个月到20张。周更需要建一个运营流程。
+
+**Dev:** 内容生产我不反对,但我有几点要确认。第一,AI 出稿用的是 DeepSeek API 对吧?那这个生成是一次性脚本还是做成后台功能?
+
+**PM:** 一次性脚本。每周跑一次,生成3张,人工改,然后插入 `knowledge_cards` 表。
+
+**Dev:** 那第二点:现在的知识卡片渲染有没有问题?8张卡片的时候没问题,到了50张、100张的时候,前端是一次性加载所有卡片吗?
+
+**PM:** 让我想想……现在应该是全量加载。
+
+**Dev:** 对,`/api/knowledge` 不分页。到100张卡片的时候每次请求都要加载100条,每条卡片都有长文本内容。这要做分页。而且卡片的去重和质量控制——AI 生成的内容可能和已有的重复,也可能有事实错误。人工审核需要时间,运营同学懂不懂数学?
+
+**PM:** 运营同学是数学专业出身,审核没问题。分页我们第一期不做,50张以内全量加载性能 OK。到100张再做分页。
+
+**Dev:** 行。那我只需要写一个 `generate_knowledge.py` 脚本,调用 DeepSeek 批量生成,输出 JSON,人工审核后导入。前端暂不改。
+
+**PM:** 等一下,我觉得周更应该做成自动化的——不是每次手动跑脚本。能不能在 Flask 里加一个定时任务?每周一自动生成3张草稿,状态为 `draft`,运营同学在后台审核通过后上线?
+
+**Dev:** 定时任务需要 APScheduler 或者 celery。Flask 本身不支持。你确定要为了一个每周3张的内容生产引入新的依赖和调度框架?我觉得过度设计了。
+
+**PM:** 好吧,手动脚本先跑着。
+
+| 结论 | ✅ **通过(MVP用脚本)** |
+|---|---|
+| 工作量 | **3人天**(Dev 2天脚本 + PM 1天prompt优化) |
+| 风险 | 🟢 低(纯内容生产,无架构变更) |
+| 后续 | 卡片超过50张时前端加分页(另估1天) |
+
+---
+
+### P1-4: 视频数据真实化(54条占位→真实链接)
+
+**PM:** 现在 seed_videos.json 里的54条视频,大部分是 YouTube/Bilibili 的占位 ID——点进去 404 或者根本不存在。这个必须改。我需要运营同学配合,从 B 站/YouTube 找真实的、高质量的数学科普视频,每个视频给真实 URL + 封面图 + 时长。
+
+**Dev:** 这个完全不是开发工作。你让我改什么代码?数据替换而已。你给新的 seed_videos.json,我导入。一个 `DELETE FROM videos` + 重新 `load_seed_videos()`,5分钟。
+
+**PM:** 我就是确认一下工作量,确实很小。但我想问:现在是每次重新导入都会 DELETE 全部对吧?如果将来用户在视频上有行为数据(收藏了某条视频),重新导入不会丢吗?
+
+**Dev:** 好问题。现在的逻辑是 `INSERT OR REPLACE`,基于 `bvid` 或 `video_id` 唯一键。所以如果新数据的 ID 和旧数据不同,旧收藏就悬空了。解决办法:真实化的时候保持 video_id 不动,只替换 url、cover、duration 字段。需要我加一个 upsert 逻辑。
+
+**PM:** 好,那就这样。
+
+| 结论 | ✅ **通过** |
+|---|---|
+| 工作量 | Dev **0.5人天**(upsert逻辑),运营 **2人天**(54条视频筛选+校对) |
+| 风险 | 🟢 低 |
+| 备注 | 纯运营驱动,PM 负责对接 |
+
+---
+
+### P1-5: 工具箱 — 概率模拟器
+
+**PM:** 这个是我最兴奋的 feature 之一。三个交互实验:蒙提霍尔三门问题、生日悖论、骰子实验。每个实验都是纯前端 Canvas 动画 + 跑1000次模拟显示概率收敛。用户玩三门问题,自己选一扇门,系统展示"换门vs不换门"的概率差异。这是知识模块里最出彩的内容——"动手做数学"。
+
+**Dev:** 我先说我喜欢的:这三个实验确实适合用前端做,逻辑不复杂,每个实验核心代码不超过200行。Canvas 动画也不难。但我担心的是——你把这5个实验放在哪个模块?
+
+**PM:** "知识"模块的子tab——现在只有"精选"和"话题",加一个"工具箱"。
+
+**Dev:** 那前端 Tab 切换逻辑要改。另外这三个实验的交互设计你有原型吗?蒙提霍尔——选门→主持人开一扇羊→你决定换不换→结果展示。这个交互流程要用 Canvas 还是 DOM?
+
+**PM:** 我觉得混合最好:门用 Canvas 画(有开关门动画),按钮和文字用 DOM。生日悖论更简单,一个输入框 + Canvas 散点图。骰子实验用 Canvas 画骰子。
+
+**Dev:** 可以。但我给你一个现实时间估计:三个实验,每个需要 Canvas 绘制 + 动画 + 概率计算 + 移动端适配。每个实验至少1.5天。总计4.5天,加上"工具箱"子tab的前端改造,**5人天**。不算多但也不少。
+
+**PM:** 可以接受。但我还想加两个实验——贝叶斯更新可视化和随机游走。
+
+**Dev:** 先做这三个,上线看用户反馈。如果数据好再扩。
+
+| 结论 | ✅ **通过** |
+|---|---|
+| 工作量 | **5人天** |
+| 风险 | 🟡 中(Canvas 动画移动端性能需验证) |
+| 范围 | 先做3个,后续根据数据决定是否扩 |
+
+---
+
+### P2-1: PWA + 小程序壳
+
+**PM:** PWA 可以让用户在 iOS/Android 上"添加到主屏幕",离线也能用。小程序壳是老板提的方向——微信小程序用户量大。
+
+**Dev:** PWA 技术上很简单:一个 `manifest.json` + 一个 `service-worker.js`,半天搞定。但 PWA 的离线体验需要你把所有静态资源(CSS/JS/图片/立绘/JSON)缓存——你现在立绘图片多大?
+
+**PM:** 8个角色立绘 + 4个场景背景,大概……我没算过。
+
+**Dev:** 如果超过10MB,iOS Safari 的 Service Worker 缓存就超限了。而且 PWA 在 iOS 上的体验远不如 Android——没有推送通知、没有后台同步。小程序壳完全是另一套技术栈——要么用 Taro 重写前端,要么做 WebView 套壳。无论哪种都是大工程。
+
+**PM:** PWA 先做了,半天值得。小程序壳……我们先放放?
+
+**Dev:** 同意。
+
+| 结论 | ⚠️ PWA **有条件通过**(仅 manifest + SW,不做离线全量缓存);小程序壳 **⏸ 延后** |
+|---|---|
+| 工作量 | PWA 0.5人天;小程序壳 ∞ |
+| 风险 | PWA 🟢 低;小程序壳 🔴 高 |
+
+---
+
+### P2-2: 家长模式 + 周报
+
+**PM:** 家长模式:在"我的"页面设置一个家长密码,进入后可以看到用户的学习数据——看了多少视频、答了多少题、知识卡片阅读量。周报每周自动生成一份摘要推送。
+
+**Dev:** 先说家长模式——你说的"学习数据"我们现在根本没有。视频播放没有记录时长,知识卡片没有"已读"标记,工具箱的实验也没有统计。你要我先埋点,再做数据汇总,再做家长面板。埋点改所有模块,2天;数据汇总 API,1天;周报生成,1天。总共4天。而且周报"推送"——推送到哪?我们没有 WebSocket,没有邮件系统,没有公众号模板消息。你只能在 App 内做一个"周报信箱"。
+
+**PM:** App 内周报信箱可以。但你说的对——数据基础太薄了。那我换个思路:第一期只做数据埋点,不展示。让行为日志记录更完整。第二期再做面板。
+
+**Dev:** 这个我支持。埋点做好,后续家长面板、周报都能基于这些数据。但目前我们连用户登录都没做,埋点都是匿名数据,家长绑定不了孩子。
+
+**PM:** 所以这个依赖 P1-1。
+
+| 结论 | ⏸ **延后至登录完成后** |
+|---|---|
+| 工作量 | 预估4人天(分两期:埋点2天 + 面板2天) |
+| 风险 | 🟡 中 |
+| 依赖 | P1-1 登录 |
+
+---
+
+### P2-3: 好感度可视化面板
+
+**PM:** 现在葫芦侠行有好感度系统(欧阳侠、Eddie侠、厚朴侠等8个角色),但用户完全看不到当前好感度数值——只能在节点选择时凭感觉。我想在"数学人生"页加一个好感度面板,用进度条或心形图标展示。
+
+**Dev:** 技术上极简单:`vnState.favorability` 已经在全局状态里了,渲染8个进度条就是30行代码。但你确定要暴露数值?一旦用户看到"欧阳侠好感度 45/100",沉浸感就被打破了。现在的设计是让用户凭感觉选对话,更接近真实社交。你加一个数字面板,用户就会"刷好感度"。
+
+**PM:** …你说得有道理。那我退一步:不做数字面板,而是做"关系描述"。比如好感度 0-20 显示"萍水相逢",20-40 显示"略有交情",40-60 显示"惺惺相惜",60-80 显示"生死之交",80+ 显示"莫逆于心"。用文字而不是数字。
+
+**Dev:** 这个好。而且文字描述可以随剧情进展动态变化,比数字更有情感。工作量很小:一个关系描述映射表 + 5行渲染代码。
+
+| 结论 | ✅ **通过(文字关系描述版)** |
+|---|---|
+| 工作量 | **0.5人天** |
+| 风险 | 🟢 低 |
+
+---
+
+### P2-4: 双语支持(i18n)
+
+**PM:** 中英文切换。我们的内容——视频标题、知识卡片、剧情文本——都只有中文。但如果将来要出海或者给国际学校用,双语是刚需。
+
+**Dev:** i18n 是架构级改动。前端所有硬编码的中文字符串都要抽出来变成 key-value 映射;后端 API 返回内容要加语言参数;最头疼的是剧情文本——93个节点,每个节点的叙事文本都要翻译。这怎么可能在 P2 阶段做?这不是技术问题,是翻译资源问题。
+
+**PM:** 你说得对。那我们先不做双语,但代码层面预留 i18n 的可能性——前端字符串能不能从现在开始用变量引用而不是硬编码?
+
+**Dev:** 可以。我可以定义一个 `I18N` 对象,现在里面只有 `zh`,未来加 `en`。新代码用 `I18N['key']` 取值。但历史代码全部重构不现实——只对新功能做。
+
+| 结论 | ⏸ **延后(代码预留机制即可)** |
+|---|---|
+| 工作量 | 代码预留 1人天;完整双语 20+人天(含翻译) |
+| 风险 | 🔴 高(翻译资源 + 全量重构) |
+
+---
+
+### P2-5: AI 助教多轮对话记忆
+
+**PM:** 现在 AI 对话每次都是独立的——用户问"什么是导数",AI 回答了;然后问"它和积分有什么关系",AI 没有上文。DeepSeek API 支持多轮对话对吧?
+
+**Dev:** 支持。API 层面传 `messages` 数组就行——`[{role: "user", content: "..."}, {role: "assistant", content: "..."}]`。前端维护一个对话历史数组,每次请求把整个 history 传给 API。改动量:后端 `/api/chat` 接口改一下接收 `history` 参数,前端维护 `chatHistory` 数组 + 渲染。1天。
+
+**Dev(自己接话):** 但是有一个隐性问题——token 消耗。每轮对话带全量 history,token 累积很快。如果用户聊了10轮,每轮回答500字,那就是约3000 token 的 history,加上新问题,请求成本翻3-5倍。DeepSeek 虽然便宜(¥1/百万token),但量大了也要注意。
+
+**PM:** 做 token 截断——只保留最近5轮对话。超出就丢弃最早的历史。
+
+**Dev:** 可以。还有一个体验问题:长对话历史会让 AI 回答越来越偏向历史语境,偏离当前问题。5轮是一个合理截断。
+
+| 结论 | ✅ **通过** |
+|---|---|
+| 工作量 | **1人天** |
+| 风险 | 🟢 低 |
+| 备注 | 限制历史5轮,控制 token 成本 |
+
+---
+
+## 最终决策汇总表
+
+| 编号 | 需求 | 结论 | 优先级 | 工作量(人天) | 风险 |
+|------|------|------|--------|:--------:|:----:|
+| P0-1 | 模拟器数据填充(120节点) | ⚠️ 有条件通过 | P0 | 7 | 🟡 中 |
+| P0-2 | 葫芦侠行立绘+背景 | ✅ 已上线 | P0 | — | — |
+| P0-3 | 存档面板+结局画廊 | ✅ 已上线 | P0 | — | — |
+| — | 多剧本体系(MVP) | ⚠️ 有条件通过 | P0 | 4 | 🟡 中 |
+| — | 葫芦侠行2.0(CSS低配) | ⚠️ 有条件通过 | P0 | 10 | 🟡 中 |
+| P1-1 | 用户登录 | ✅ 通过 | P1 | 4 | 🟢 低 |
+| P1-2 | 章节选择器 | ✅ 通过 | P1 | 3 | 🟢 低 |
+| P1-3 | 知识卡片扩充+周更 | ✅ 通过 | P1 | 3 | 🟢 低 |
+| P1-4 | 视频真实化 | ✅ 通过 | P1 | 0.5 + 运营2天 | 🟢 低 |
+| P1-5 | 工具箱(概率模拟器) | ✅ 通过 | P1 | 5 | 🟡 中 |
+| P2-1 | PWA | ⚠️ 通过(缩减) | P2 | 0.5 | 🟢 低 |
+| P2-1b | 小程序壳 | ⏸ 延后 | — | ∞ | 🔴 高 |
+| P2-2 | 家长模式+周报 | ⏸ 延后 | — | 4 | 🟡 中 |
+| P2-3 | 好感度可视化 | ✅ 通过 | P2 | 0.5 | 🟢 低 |
+| P2-4 | 双语支持 | ⏸ 延后 | — | 20+ | 🔴 高 |
+| P2-5 | AI 多轮记忆 | ✅ 通过 | P2 | 1 | 🟢 低 |
+
+---
+
+## 建议排期(按迭代划分)
+
+### 迭代1(第1-2周):基础体验闭环
+- P1-1 用户登录(4天)
+- P1-2 章节选择器(3天)
+- P1-4 视频真实化(0.5天 + 运营并行)
+- P2-3 好感度文字描述(0.5天)
+- P2-5 AI多轮记忆(1天)
+- **合计:Dev 9人天(≈2周)**
+
+### 迭代2(第3-4周):内容厚度 + 知识模块
+- P1-3 知识卡片扩充(3天)
+- P1-5 工具箱概率模拟器(5天)
+- P2-1 PWA manifest + SW(0.5天)
+- **合计:Dev 8.5人天(≈2周)**
+
+### 迭代3(第5-6周):剧情体验升级
+- P0-1 模拟器数据填充(7天,PM在第4周前交付草稿)
+- 多剧本体系 MVP(4天)
+- **合计:Dev 11人天(≈2.5周)**
+
+### 迭代4(第7-8周):视觉升级
+- 葫芦侠行2.0 CSS低配版(10天)
+- **合计:Dev 10人天(≈2周)**
+
+### 后续(无固定排期)
+- 家长模式+周报(依赖登录完成 + 数据埋点)
+- 双语支持(仅代码预留)
+- 小程序壳(需单独技术评审)
+- Phaser.js 完整迁移(作为 V3 独立规划)
+
+---
+
+## 需要老板拍板的开放问题
+
+1. **模拟器填充是 P0 还是 P1?** PM 和 Dev 一致认为120节点是内容工作而非产品创新,建议降为 P1 但仍排在合理优先级。如果老板坚持 P0,需在迭代1/2插队。
+
+2. **用户登录之后,要不要做"账号找回"?** 方案C是邀请码+密码,没有邮箱/手机绑定。如果用户忘记密码,目前没有找回机制。老板是否接受"联系运营手工重置"?
+
+3. **多剧本"状态继承"要不要做?** Dev 建议本期只做解锁+跳转,状态继承留到 V2。这会带来用户期望落差。如果老板认为继承是刚需,工作量需翻倍至8人天。
+
+4. **小程序壳的方向确认。** 如果老板短期内要推微信小程序,前端架构可能需要提前往 Taro/uni-app 方向规划,不能继续在原生 JS 上堆功能。这会改变整个迭代计划。
+
+5. **预算确认:DeepSeek API 的 token 成本。** AI 多轮记忆上线后,每次对话的 token 消耗量将增加数倍。需确认月度预算上限。
+
+---
+
+## 一句话执行摘要
+
+> **本次评审共通过12项(含5项有条件通过)、延后3项、驳回0项;核心结论是用登录+章节选择器"闭环体验",用模拟器填充+低配星露谷"囤厚度",小程序壳和双语作为长期战略暂不启动——2个月内可交付4个迭代,总工作量约38.5人天。**
+
+---
+
+*会议记录:PM & Dev 联合纪要 | 下次评审:迭代1完成后(≈第3周)*
diff --git a/docs/数学少年线_story.json b/docs/数学少年线_story.json
new file mode 100644
index 0000000..04c7afd
--- /dev/null
+++ b/docs/数学少年线_story.json
@@ -0,0 +1,1418 @@
+{
+ "title": "数学少年线",
+ "author": "MathBTI 剧本组",
+ "description": "小镇少年没有竞赛奖状,但喜欢数学。从高考录取通知书到直博,完整经历大学数学专业的四年:数分、高代、期中、图书馆、讨论班、科研低谷、申请与毕业。",
+ "start_node": "c1_notice",
+ "nodes": {
+ "c1_notice": {
+ "scene": "高三暑假,小镇邮递员把录取通知书送到你家门口。\n\n你拆开信封,看到“数学与应用数学专业”。亲戚说:“你也没参加过竞赛,数学系都是聪明人读的。”\n\n你翻开高中那本写满推导的笔记本,想起自己第一次被数学吸引,是因为想把 1 加到 100 的规律看明白。不是快,是那种“再想想就能看清结构”的感觉。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "我想试试",
+ "next": "c1_gauss_try",
+ "effects": {
+ "xinzhi": 5,
+ "favorability": {
+ "高斯": 10
+ }
+ }
+ },
+ {
+ "text": "先读完一年再说",
+ "next": "c1_gauss_keep",
+ "effects": {
+ "xinzhi": 2,
+ "favorability": {
+ "高斯": 5
+ }
+ }
+ }
+ ]
+ },
+ "c1_gauss_try": {
+ "scene": "高斯坐在数学塔里,桌上摊着圆规和尺规。他说:“我 10 岁做 1 加到 100,不是因为我聪明,是因为我愿意把数字排成两行慢慢看。19 岁画正十七边形,我花了一整夜,一步一步推到天亮。”\n\n他看向你:“你愿意试试,就是画下第一笔。”",
+ "character": "高斯",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "记住这句话",
+ "next": "c1_gauss_merge",
+ "effects": {
+ "shuli": 3
+ }
+ }
+ ]
+ },
+ "c1_gauss_keep": {
+ "scene": "高斯说:“我 19 岁发现了非欧几何,却把它锁进抽屉五十年,没有发表。怕不是离开数学的理由,它只是你站在门外的第一级台阶。”\n\n“你愿意读完一年,已经迈出第二级。”",
+ "character": "高斯",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "收下这句话",
+ "next": "c1_gauss_merge",
+ "effects": {
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "c1_gauss_merge": {
+ "scene": "高斯说:“数学不检查你有没有竞赛奖状。它只问,你愿不愿意为一行定义坐到天亮。”\n\n你醒来,录取通知书还压在笔记本下。你在日记第一行写下:“我没有竞赛奖状,但我喜欢数学。今天开始,我试一年。”",
+ "character": "高斯",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "去学校报到",
+ "next": "c1_report",
+ "effects": {
+ "shuli": 3,
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "c1_report": {
+ "scene": "九月的校园,宿舍楼里全是新生。你的室友聊起高中竞赛题,另一个同学已经翻到高数教材第三章。你坐在床沿,第一次觉得“数学系”这三个字有重量。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "先跟室友熟悉,听听他们怎么学",
+ "next": "c1_room_friendly",
+ "effects": {
+ "xiayi": 4
+ }
+ },
+ {
+ "text": "自己先去图书馆转一圈",
+ "next": "c1_room_solo",
+ "effects": {
+ "shuli": 4
+ }
+ }
+ ]
+ },
+ "c1_room_friendly": {
+ "scene": "你跟着室友去自习室。他说:“竞赛学的是技巧,大学学的是证明。技巧可以补,证明得一句一句抠。”他把自己整理的高数笔记借给你。你第一次发现,原来大家也在怕。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "开始预习第一课",
+ "next": "c1_analysis",
+ "effects": {
+ "xiayi": 2,
+ "xinzhi": 2
+ }
+ }
+ ]
+ },
+ "c1_room_solo": {
+ "scene": "你一个人走进图书馆,找到《数学分析》教材。目录里是极限、连续、导数、积分。你翻开第一页,读到“无穷小不是一个数,它是一种承诺”,忽然觉得有什么东西在等你。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "把第一章借回去",
+ "next": "c1_analysis",
+ "effects": {
+ "shuli": 3
+ }
+ }
+ ]
+ },
+ "c1_analysis": {
+ "scene": "数学分析第一课。老教授在黑板上写下 ε-δ 定义,说:“数学不说差不多,它说精确。”你抄完笔记,发现旁边同学已经写出三个例子。你只写了一行:如果数学是承诺,这句话的 ε 是多少?",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "课后追到讲台问老师",
+ "next": "c1_ask",
+ "effects": {
+ "xinzhi": 3
+ }
+ },
+ {
+ "text": "先自己啃教材,晚点再问",
+ "next": "c1_self_study",
+ "effects": {
+ "shuli": 3
+ }
+ }
+ ]
+ },
+ "c1_ask": {
+ "scene": "教授停下来听完你的问题,说:“你问的是定义背后的东西。很多竞赛生直接跳过这一步,你愿意停下来,反而更适合做数学。”他给你写了一个小例子,让你回去验证。",
+ "character": "教授",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "回去慢慢验证",
+ "next": "c1_algebra",
+ "effects": {
+ "shuli": 3,
+ "xinzhi": 2
+ }
+ }
+ ]
+ },
+ "c1_self_study": {
+ "scene": "晚自习,你对着 ε-δ 定义读了三遍。第一遍不懂,第二遍知道每个符号,第三遍发现它其实在说“无论多小的误差,我都能做到”。你在页边写下:数学不是算得快,是想得清楚。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "继续做高代作业",
+ "next": "c1_algebra",
+ "effects": {
+ "shuli": 3,
+ "xinzhi": 2
+ }
+ }
+ ]
+ },
+ "c1_algebra": {
+ "scene": "高等代数开始讲线性空间。作业里有一道证明题:两个子空间交与和的维数公式。你做了两天,草稿纸铺满桌子。旁边的竞赛生已经写完,你还没有方向。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "跟同学组队讨论",
+ "next": "c1_group",
+ "effects": {
+ "xiayi": 3
+ }
+ },
+ {
+ "text": "独自死磕到深夜",
+ "next": "c1_alone",
+ "effects": {
+ "shuli": 4,
+ "xinzhi": 2
+ }
+ }
+ ]
+ },
+ "c1_group": {
+ "scene": "三个同学围在一起,有人画维数图,有人写反例。你负责把所有定义抄在中间。讨论到熄灯前,你们终于拼出完整证明。你发现:证明不是一个人孤独地造,是一群人把石头一块一块搬。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "把证明重新写一遍",
+ "next": "c1_midterm",
+ "effects": {
+ "shuli": 3,
+ "xiayi": 2
+ }
+ }
+ ]
+ },
+ "c1_alone": {
+ "scene": "凌晨一点,你终于把维数公式证明写完整。它不算漂亮,但每一步你都确定。你在纸上画了一个勾,然后睡了四个小时。第二天早上,你把它重抄一遍,发现更清楚了。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "准备期中考试",
+ "next": "c1_midterm",
+ "effects": {
+ "shuli": 4,
+ "xinzhi": 2
+ }
+ }
+ ]
+ },
+ "c1_midterm": {
+ "scene": "高数期中成绩出来:58 分。你看了一遍卷子,发现有一道 12 分的题不是不会,是时间不够。室友安慰你,你却在想:也许我真的不适合数学系。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "找班主任聊聊",
+ "next": "c1_advisor",
+ "effects": {
+ "xinzhi": 4
+ }
+ },
+ {
+ "text": "去图书馆把错题重做一遍",
+ "next": "c1_library",
+ "effects": {
+ "shuli": 4
+ }
+ }
+ ]
+ },
+ "c1_advisor": {
+ "scene": "班主任说:“58 分不是数学的判决,是你还没找到节奏。竞赛生高一就在做题,你才刚开始。你可以慢,但不要停。”她帮你约了助教的答疑时间。",
+ "character": "班主任",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "去图书馆坐到闭馆",
+ "next": "c1_zu_enter",
+ "effects": {
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "c1_library": {
+ "scene": "晚上 21:45,图书馆准备闭馆。你坐在窗边,草稿纸写满四张,一道题还是没做完。你把书抱在怀里,准备离开。就在这时,一个身影在你对面坐下。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "抬头看他是谁",
+ "next": "c1_zu_enter",
+ "effects": {
+ "shuli": 2
+ }
+ }
+ ]
+ },
+ "c1_zu_enter": {
+ "scene": "祖冲之坐在你对面的椅子上,手边是一堆算筹。他说:“我算圆周率的时候,没有人告诉我什么时候算完。我就一根一根摆,摆到 3.1415926 和 3.1415927 之间。”\n\n“你今晚要不要再摆一根?”",
+ "character": "祖冲之",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "问他怎么坚持下来",
+ "next": "c1_zu_ask",
+ "effects": {
+ "xinzhi": 3
+ }
+ },
+ {
+ "text": "告诉他我想再试一个月",
+ "next": "c1_zu_write",
+ "effects": {
+ "shuli": 3,
+ "xinzhi": 2
+ }
+ }
+ ]
+ },
+ "c1_zu_ask": {
+ "scene": "祖冲之说:“我编《大明历》时算回归年,误差只有几十秒。那些秒不是一次算出来的,是每天摆算筹,摆到别人不想再摆。”他笑了一下:“你去找老师,也是把算筹重新摆一次。”",
+ "character": "祖冲之",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "接住那根算筹",
+ "next": "c1_zu_merge",
+ "effects": {
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "c1_zu_write": {
+ "scene": "你说:“我想再试一个月。”祖冲之把一根算筹推到你面前:“好。你愿意再写一步,就是这根还没放下的算筹。”",
+ "character": "祖冲之",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "把书借回去",
+ "next": "c1_zu_merge",
+ "effects": {
+ "shuli": 3,
+ "xinzhi": 2
+ }
+ }
+ ]
+ },
+ "c1_zu_merge": {
+ "scene": "祖冲之递来一根算筹:“找到你做不下去的那一年,然后这一年就会成为你自己的 π。不是圆周率——是周率——你围着这个数字走完一圈需要花的时间。”\n\n闭馆铃响,你醒了。你抱着书走出图书馆,在书页里夹了一张纸条:“再试一个月。”",
+ "character": "祖冲之",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "开始期末复习",
+ "next": "c1_final",
+ "effects": {
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "c1_final": {
+ "scene": "期末前四周,你给自己排了一张表:每天两小时高数、两小时高代,每周重做一次错题。你发现 58 分那道题,现在十分钟就能写完。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "每天固定两小时,雷打不动",
+ "next": "c1_plan",
+ "effects": {
+ "shuli": 3,
+ "xinzhi": 2
+ }
+ },
+ {
+ "text": "跟着学长做每周小结",
+ "next": "c1_summary",
+ "effects": {
+ "shuli": 2,
+ "xiayi": 3
+ }
+ }
+ ]
+ },
+ "c1_plan": {
+ "scene": "你按计划走了二十八天。错题本从十页变成三页,最后一周你开始能给别人讲题。你发现,数学的节奏不是比谁快,是比谁愿意重复。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "走进期末考场",
+ "next": "c1_end",
+ "effects": {
+ "shuli": 4,
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "c1_summary": {
+ "scene": "你每周和学长过一遍知识点,他把“会做”和“会证明”分开讲。你慢慢明白:大学数学要的不是背答案,是能说出每一步为什么。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "走进期末考场",
+ "next": "c1_end",
+ "effects": {
+ "shuli": 3,
+ "xiayi": 3
+ }
+ }
+ ]
+ },
+ "c1_end": {
+ "scene": "期末成绩出来,你的高数从 58 分走到 78 分。没有很漂亮,但你知道它不是运气。你在日记里写:“大一上结束。我还在数学系。”",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "进入大一下",
+ "next": "c2_start",
+ "effects": {
+ "shuli": 3,
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "c2_start": {
+ "scene": "大一下,课程变多:数学分析、高等代数、解析几何、概率论。你不再需要别人告诉你“数学难”,你已经开始习惯每天面对证明。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "报名数学学习小组",
+ "next": "c2_group",
+ "effects": {
+ "xiayi": 3
+ }
+ },
+ {
+ "text": "继续按自己的节奏独自学",
+ "next": "c2_solo",
+ "effects": {
+ "shuli": 3
+ }
+ }
+ ]
+ },
+ "c2_group": {
+ "scene": "学习小组每周聚一次。有人擅长算,有人擅长画图,你负责把证明的每一步说清楚。第一次讲完,同学说:“你讲得比书好懂。”你愣了很久。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "继续打磨证明",
+ "next": "c2_proof",
+ "effects": {
+ "shuli": 2,
+ "xiayi": 2
+ }
+ }
+ ]
+ },
+ "c2_solo": {
+ "scene": "你继续一个人泡图书馆。你开始写“证明日记”:每个定理用自己的话重写一遍,再对照书上的语言。慢,但每一步都长在你身上。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "遇到那道卡了三天的题",
+ "next": "c2_proof",
+ "effects": {
+ "shuli": 3,
+ "xinzhi": 2
+ }
+ }
+ ]
+ },
+ "c2_proof": {
+ "scene": "一道中值定理的证明题卡了你三天。你试过反证、试过构造辅助函数,草稿纸用完一本。你盯着题目,第一次不是想“我做不到”,而是想“我缺一个定义”。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "去找助教答疑",
+ "next": "c2_ta",
+ "effects": {
+ "xinzhi": 3
+ }
+ },
+ {
+ "text": "把定义重新整理一遍",
+ "next": "c2_rebuild",
+ "effects": {
+ "shuli": 4
+ }
+ }
+ ]
+ },
+ "c2_ta": {
+ "scene": "助教看了你的草稿说:“你已经走到最后一步了,只是没用对辅助函数。”她只提示了一句。你回座位补完最后三行,整道题忽然通了。",
+ "character": "助教",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "把完整证明交上去",
+ "next": "c2_first_proof",
+ "effects": {
+ "shuli": 3,
+ "xinzhi": 2
+ }
+ }
+ ]
+ },
+ "c2_rebuild": {
+ "scene": "你把连续、可导、中值定理的定义抄在一张纸上,发现题目里的条件你漏了一个。补上之后,证明自己走出来了。你在纸边写:“数学不是灵感,是定义。”",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "把完整证明交上去",
+ "next": "c2_first_proof",
+ "effects": {
+ "shuli": 4,
+ "xinzhi": 2
+ }
+ }
+ ]
+ },
+ "c2_first_proof": {
+ "scene": "作业发下来,老师在你证明的最后写了一句:“证明清楚,思路完整。”没有分数,但你知道那是你的第一个完整证明。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "考虑要不要参加数学建模",
+ "next": "c2_model",
+ "effects": {
+ "shuli": 3,
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "c2_model": {
+ "scene": "学校开始宣传数学建模新手赛。你没有竞赛背景,但建模不是比谁算得快,是比谁能把问题翻译成数学。你犹豫要不要报名。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "报名新手赛,试试水",
+ "next": "c2_model_yes",
+ "effects": {
+ "shuli": 2,
+ "xiayi": 4
+ }
+ },
+ {
+ "text": "先专注专业课,明年再说",
+ "next": "c2_model_no",
+ "effects": {
+ "shuli": 4
+ }
+ }
+ ]
+ },
+ "c2_model_yes": {
+ "scene": "你和两个同学组队,用三天把一道“食堂排队时间”的问题做成线性规划。结果没拿奖,但你们把模型讲清楚了。你在总结里写:“第一次用数学解决真实问题。”",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "准备期末",
+ "next": "c2_end",
+ "effects": {
+ "shuli": 3,
+ "xiayi": 3
+ }
+ }
+ ]
+ },
+ "c2_model_no": {
+ "scene": "你把时间留给专业课,每周多做一套证明。你发现自己的证明速度变快了,开始能在讨论时跟上别人。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "准备期末",
+ "next": "c2_end",
+ "effects": {
+ "shuli": 4,
+ "xinzhi": 2
+ }
+ }
+ ]
+ },
+ "c2_end": {
+ "scene": "大一下结束,你的绩点从大一上爬回中游。室友说:“你变了。”你看着自己的证明日记,知道变化不是突然发生的。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "进入大二上",
+ "next": "c3_start",
+ "effects": {
+ "shuli": 3,
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "c3_start": {
+ "scene": "大二上,你第一次参加讨论班。研二学长在讲格罗滕迪克的模空间,全班都在抄板书。只有你发现,他从第二步跳到第三步时跳了一个引理。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "当众举手指出",
+ "next": "c3_raise",
+ "effects": {
+ "xinzhi": 4
+ }
+ },
+ {
+ "text": "下课后私下找学长",
+ "next": "c3_private",
+ "effects": {
+ "xiayi": 3
+ }
+ }
+ ]
+ },
+ "c3_raise": {
+ "scene": "你举手,教室里安静了两秒。学长回头核对板书,说:“你说得对,我跳了一个引理。”他补上证明,但你注意到后排有人小声说:“他连竞赛都没参加过,居然敢打断学长。”",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "回到宿舍面对草稿",
+ "next": "c3_denied",
+ "effects": {
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "c3_private": {
+ "scene": "下课后你拿着草稿纸走过去,只说了一句:“第三步。”学长看了你写的内容,画了个圈:“你走对了,我准备在第二章补上。”可他又补了一句:“你基础还得再补补,竞赛班的同学大一就学过这些了。”",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "回到宿舍面对草稿",
+ "next": "c3_denied",
+ "effects": {
+ "xinzhi": 2
+ }
+ }
+ ]
+ },
+ "c3_denied": {
+ "scene": "深夜,你把草稿收进抽屉。学长的话、后排的议论、没有竞赛奖状的事实,全在脑子里转。你开始怀疑:我是不是真的不适合数学?",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "明天当众再说一遍",
+ "next": "c3_noether_public",
+ "effects": {
+ "xinzhi": 5
+ }
+ },
+ {
+ "text": "先把引理写成完整证明",
+ "next": "c3_noether_private",
+ "effects": {
+ "shuli": 5
+ }
+ }
+ ]
+ },
+ "c3_noether_public": {
+ "scene": "诺特站在走廊里。她说:“我第一次被否定,是因为女性没有旁听资格,我靠自学考过了全部课程;第二次,没有学校肯雇我,我义务讲课;第三次,抽象代数被说成不是数学,我继续写。”\n\n“第四次,是我自己决定继续。你也可以。”",
+ "character": "诺特",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "记住她说的话",
+ "next": "c3_noether_merge",
+ "effects": {
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "c3_noether_private": {
+ "scene": "诺特说:“我后来证明了诺特定理,爱因斯坦亲自为我写信争取教职。但在那之前,我只是把被否定的论文改得更完整。”\n\n“被否定不是让你闭嘴,是让你把话说得更完整。你写成证明,就是对世界说的第二遍。”",
+ "character": "诺特",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "记住她说的话",
+ "next": "c3_noether_merge",
+ "effects": {
+ "shuli": 3
+ }
+ }
+ ]
+ },
+ "c3_noether_merge": {
+ "scene": "诺特说:“世界可以不理解你,但你不能不理解自己。你发现别人忽略的东西,这是数学给你的礼物;你必须说出来,让世界看到这步缺口。”\n\n你把草稿放回桌面,在封面写:“第一次被否定,不是最后一次。”",
+ "character": "诺特",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "开始写课程论文",
+ "next": "c3_paper",
+ "effects": {
+ "shuli": 2,
+ "xinzhi": 3,
+ "favorability": {
+ "诺特": 10
+ }
+ }
+ }
+ ]
+ },
+ "c3_paper": {
+ "scene": "这学期有一门课要求写课程论文。你想到讨论班跳过的那个引理,决定把它重新整理出来,再补充一个自己的例子。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "选这个引理做论文题目",
+ "next": "c3_paper_a",
+ "effects": {
+ "shuli": 4
+ }
+ },
+ {
+ "text": "先帮学长整理讨论班笔记",
+ "next": "c3_paper_b",
+ "effects": {
+ "xiayi": 4
+ }
+ }
+ ]
+ },
+ "c3_paper_a": {
+ "scene": "你花了两周把引理写成论文,包括反例和证明。交上去之后,老师找你聊了二十分钟,最后说:“这个整理很有价值,可以投系里的学生论文展。”",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "把论文投出去",
+ "next": "c3_cited",
+ "effects": {
+ "shuli": 4,
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "c3_paper_b": {
+ "scene": "你帮学长整理了三章笔记,顺便把你发现的引理补在附录里。下一次讨论班,学长公开说:“这个附录是学弟补的。”你坐在角落,脸很烫,但心里很亮。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "继续参加讨论班",
+ "next": "c3_cited",
+ "effects": {
+ "shuli": 2,
+ "xiayi": 4
+ }
+ }
+ ]
+ },
+ "c3_cited": {
+ "scene": "一个月后,你的名字出现在系里学生论文展的名单上。没有奖杯,但你在日记里写:“不是竞赛奖状,是我自己写出来的证明。”",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "进入大二下",
+ "next": "c4_start",
+ "effects": {
+ "shuli": 3,
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "c4_start": {
+ "scene": "大二下,期末周。你连续一周熬夜刷题,眼睛酸到看不清公式。手机亮起妈妈的未接来电,你犹豫要不要接。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "合上书,回宿舍睡觉",
+ "next": "c4_sleep",
+ "effects": {
+ "xiayi": 2,
+ "xinzhi": 5
+ }
+ },
+ {
+ "text": "定 10 分钟闹钟,再写一道题",
+ "next": "c4_boundary",
+ "effects": {
+ "shuli": 3,
+ "xinzhi": 2
+ }
+ }
+ ]
+ },
+ "c4_sleep": {
+ "scene": "你合上书,给妈妈回电话。她说:“你从小就这样,累了从来不说。”你忽然哭了。不是难过,是有人不用等你开口就知道你在累。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "睡到天亮",
+ "next": "c4_euler_merge",
+ "effects": {
+ "xinzhi": 4
+ }
+ }
+ ]
+ },
+ "c4_boundary": {
+ "scene": "你定好 10 分钟闹钟,写完最后一道题,然后合上书。你发现那道题其实早就做完了,你只是不敢停下来。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "带着边界睡下",
+ "next": "c4_euler_merge",
+ "effects": {
+ "shuli": 3,
+ "xinzhi": 2
+ }
+ }
+ ]
+ },
+ "c4_euler_merge": {
+ "scene": "欧拉坐在书桌旁,桌上堆着 886 篇论文的草稿。他说:“我死后留下的手稿还够出版 50 年,但数学不是债主。”\n\n他在纸上写下 e^(iπ)+1=0,说这一行公式装进了数学里最重要的五个常数。“美不是熬出来的,是你活着时才能看见的。”",
+ "character": "欧拉",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "重新安排作息",
+ "next": "c4_balance",
+ "effects": {
+ "xinzhi": 3,
+ "favorability": {
+ "欧拉": 10
+ }
+ }
+ }
+ ]
+ },
+ "c4_balance": {
+ "scene": "期末结束后,你重新安排生活:每天十二点前睡,每周给妈妈打两次电话,周末留半天不碰数学。你发现,休息之后做题反而更清楚。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "报名数学建模校赛",
+ "next": "c4_model",
+ "effects": {
+ "xiayi": 4
+ }
+ },
+ {
+ "text": "加入数学史读书会",
+ "next": "c4_book",
+ "effects": {
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "c4_model": {
+ "scene": "校赛题目是“城市公交调度”。你负责建模,队友负责写代码。三天后你们交出一份带敏感性分析的报告,拿了校三等奖。你把奖状寄回家,妈妈第一次说“数学还挺有用”。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "结束大二",
+ "next": "c4_end",
+ "effects": {
+ "shuli": 3,
+ "xiayi": 3
+ }
+ }
+ ]
+ },
+ "c4_book": {
+ "scene": "读书会每周读一位数学家。你读到欧拉失明后口述论文,也读到诺特义务讲课。你发现数学史不是成功史,是一群人在被否定里继续走。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "结束大二",
+ "next": "c4_end",
+ "effects": {
+ "xinzhi": 4
+ }
+ }
+ ]
+ },
+ "c4_end": {
+ "scene": "大二结束,你站在数学楼的走廊里,看见讨论班的教室还亮着灯。你不再觉得自己是误入数学系的人。你只是走得慢一点,但方向没变。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "进入大三上",
+ "next": "c5_start",
+ "effects": {
+ "shuli": 3,
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "c5_start": {
+ "scene": "大三上,导师办公室里。导师问你:“要不要继续读数学?如果继续,你想研究什么方向?”你面前有两张纸,一张写着数论,一张写着代数几何。",
+ "character": "导师",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "数论/分析,从 1 加到 100 和那道题继续",
+ "next": "c5_number",
+ "effects": {
+ "shuli": 5
+ }
+ },
+ {
+ "text": "代数/几何,从诺特讲的结构继续",
+ "next": "c5_structure",
+ "effects": {
+ "shuli": 5,
+ "favorability": {
+ "诺特": 5
+ }
+ }
+ }
+ ]
+ },
+ "c5_number": {
+ "scene": "高斯说:“我 19 岁画正十七边形,后来选择数论,不是因为我是天才,而是因为那个问题让我睡不着。”\n\n“你从 1 加到 100 走到这里,已经证明你可以选择数学。”",
+ "character": "高斯",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "继续听",
+ "next": "c5_gauss_merge",
+ "effects": {
+ "shuli": 2
+ }
+ }
+ ]
+ },
+ "c5_structure": {
+ "scene": "高斯说:“非欧几何我锁了五十年,因为要先确认它是对的。”\n\n“你选择结构,不是逃避数论,是选择另一个值得失眠的问题。”",
+ "character": "高斯",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "继续听",
+ "next": "c5_gauss_merge",
+ "effects": {
+ "shuli": 2
+ }
+ }
+ ]
+ },
+ "c5_gauss_merge": {
+ "scene": "高斯说:“你走的每一条路,都有人在你之前走过。你要做的,是比你之前的自己把草稿更清楚一点。然后你就睡。”\n\n你走出办公室,导师给你一份书单和一个小课题。",
+ "character": "高斯",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "接下课题",
+ "next": "c5_project",
+ "effects": {
+ "xinzhi": 3,
+ "favorability": {
+ "高斯": 10
+ }
+ }
+ }
+ ]
+ },
+ "c5_project": {
+ "scene": "导师给的小课题是“一个经典问题的重新整理”。你发现读论文比做题难得多:符号陌生,假设很多,每一步都要自己验证。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "按导师建议从经典问题入手",
+ "next": "c5_classic",
+ "effects": {
+ "shuli": 4
+ }
+ },
+ {
+ "text": "从课内问题自己延伸",
+ "next": "c5_extend",
+ "effects": {
+ "xinzhi": 4
+ }
+ }
+ ]
+ },
+ "c5_classic": {
+ "scene": "你从导师给的经典问题开始,先抄一遍原证明,再逐条补理由。两周后,你终于能不看笔记讲出证明框架。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "写下第一篇研究笔记",
+ "next": "c5_notes",
+ "effects": {
+ "shuli": 4
+ }
+ }
+ ]
+ },
+ "c5_extend": {
+ "scene": "你把课内一道习题改成开放问题:去掉一个条件,结论还成立吗?你花了一周找到反例,又在反例里发现一个更小的规律。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "写下第一篇研究笔记",
+ "next": "c5_notes",
+ "effects": {
+ "shuli": 4,
+ "xinzhi": 2
+ }
+ }
+ ]
+ },
+ "c5_notes": {
+ "scene": "你的第一篇研究笔记只有五页,但你给它编了号:M-001。你在封面上写:“这是我的第一个问题,不是最后一个。”",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "进入大三下",
+ "next": "c6_start",
+ "effects": {
+ "shuli": 3,
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "c6_start": {
+ "scene": "大三下,科研课题卡住了。你读不懂一篇论文,导师说“慢慢来”,你却在深夜刷到新闻:AI 证明了新的数学猜想。评论区说“纯数完了”。你盯着天花板:那我算什么?",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "关掉手机继续算",
+ "next": "c6_keep",
+ "effects": {
+ "xinzhi": 5
+ }
+ },
+ {
+ "text": "失眠一夜,去查诺特的资料",
+ "next": "c6_find",
+ "effects": {
+ "shuli": 3
+ }
+ }
+ ]
+ },
+ "c6_keep": {
+ "scene": "你关掉手机,在黑屏的反光里看见自己的脸。你在笔记本上写:“AI 能证明定理,但它能选择证明哪一个吗?”然后你继续读那篇论文,一行一行。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "去见导师",
+ "next": "c6_noether_merge",
+ "effects": {
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "c6_find": {
+ "scene": "你搜索诺特,读到她的遗言:“不要怕 AI。AI 不会自己决定下一步,但你可以。”你写了一行回信:“你等了一百年,机器还没来。我替你等。”",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "去见导师",
+ "next": "c6_noether_merge",
+ "effects": {
+ "xinzhi": 4
+ }
+ }
+ ]
+ },
+ "c6_noether_merge": {
+ "scene": "诺特说:“我也曾被否定三次。第四次不是世界给我机会,是我自己决定继续。”\n\n“意义不是头衔,是你选择继续的那个问题。AI 可以做证明,但选择证明哪一个是人。”",
+ "character": "诺特",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "把大问题拆小",
+ "next": "c6_mentor",
+ "effects": {
+ "xinzhi": 3,
+ "favorability": {
+ "诺特": 10
+ }
+ }
+ }
+ ]
+ },
+ "c6_mentor": {
+ "scene": "导师说:“你卡住不是因为笨,是因为问题太大。先做一个你能完全掌握的小引理,再往大问题走。”她把你论文里最难的部分划掉,留了一个缺口。",
+ "character": "导师",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "先做那个小引理",
+ "next": "c6_lemma",
+ "effects": {
+ "shuli": 4
+ }
+ },
+ {
+ "text": "换一个更简单的模型",
+ "next": "c6_simple",
+ "effects": {
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "c6_lemma": {
+ "scene": "你花了两周把小引理做出来。它很小,但每一步你都清楚。你在讨论班上讲了十分钟,第一次有人在你讲完后提问。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "把结果写进笔记",
+ "next": "c6_seminar",
+ "effects": {
+ "shuli": 4,
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "c6_simple": {
+ "scene": "你换了一个更简单的模型,把原来的复杂结构映射进去。你在简单模型里发现了规律,再试着把它搬回原来的问题。虽然还没成功,但你终于知道下一步该做什么。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "把结果写进笔记",
+ "next": "c6_seminar",
+ "effects": {
+ "shuli": 3,
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "c6_seminar": {
+ "scene": "你在讨论班讲了自己的小结果。学长说:“这个例子很有用。”你的名字第一次出现在讨论班的讲义里。不是竞赛奖状,是你自己走出来的。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "决定申请直博",
+ "next": "c7_start",
+ "effects": {
+ "shuli": 3,
+ "xinzhi": 4
+ }
+ }
+ ]
+ },
+ "c7_start": {
+ "scene": "大四上,所有人都开始选择:找工作、考研、出国、直博。你站在公告栏前,看见直博申请通知。没有竞赛奖状的你,第一次要认真说出“我想继续学数学”。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "申请本校直博",
+ "next": "c7_local",
+ "effects": {
+ "xinzhi": 3
+ }
+ },
+ {
+ "text": "同时申请外校",
+ "next": "c7_outside",
+ "effects": {
+ "xiayi": 3,
+ "xinzhi": 2
+ }
+ }
+ ]
+ },
+ "c7_local": {
+ "scene": "你去找导师说想读直博。她说:“我看了你三年,你适合。”她愿意做你的导师。你拿着表格走出办公室,手有点抖。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "开始写个人陈述",
+ "next": "c7_ps",
+ "effects": {
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "c7_outside": {
+ "scene": "你申请了三所学校,包括一所外校。你在邮件里问能否旁听讨论班,收到一封回信:“欢迎来听。”你第一次觉得自己被数学圈接纳。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "开始写个人陈述",
+ "next": "c7_ps",
+ "effects": {
+ "shuli": 2,
+ "xiayi": 3
+ }
+ }
+ ]
+ },
+ "c7_ps": {
+ "scene": "个人陈述要写 800 字。你写了四版,每一版都在问:我为什么要学数学?",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "写四年低谷与成长",
+ "next": "c7_ps_growth",
+ "effects": {
+ "xinzhi": 4
+ }
+ },
+ {
+ "text": "写自己最想研究的问题",
+ "next": "c7_ps_problem",
+ "effects": {
+ "shuli": 4
+ }
+ }
+ ]
+ },
+ "c7_ps_growth": {
+ "scene": "你写:没有竞赛奖状,大一期中 58 分,在图书馆 21:45 决定再试一个月,第一次完整证明,第一次被否定,第一次在讨论班讲自己的结果。你写到最后发现,那不就是数学吗。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "提交材料",
+ "next": "c7_interview",
+ "effects": {
+ "xinzhi": 4
+ }
+ }
+ ]
+ },
+ "c7_ps_problem": {
+ "scene": "你写:我想研究那个让我睡不着的问题,它从一道做不出的题开始,现在长成一页研究笔记。你不知道答案,但你知道怎么开始。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "提交材料",
+ "next": "c7_interview",
+ "effects": {
+ "shuli": 4
+ }
+ }
+ ]
+ },
+ "c7_interview": {
+ "scene": "面试那天,五位老师坐在对面。有人问:“你没有竞赛背景,凭什么读直博?”",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "老实说,我喜欢数学,且一直在证明它",
+ "next": "c7_interview_honest",
+ "effects": {
+ "xinzhi": 4
+ }
+ },
+ {
+ "text": "讲一道自己完整做出来的题",
+ "next": "c7_interview_problem",
+ "effects": {
+ "shuli": 4
+ }
+ }
+ ]
+ },
+ "c7_interview_honest": {
+ "scene": "你说:“我没有竞赛奖状,但我在数学系待了三年半,每一次想放弃都选择留下。我喜欢数学,而且我用证明证明过这件事。”",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "等待结果",
+ "next": "c7_offer",
+ "effects": {
+ "xinzhi": 4
+ }
+ }
+ ]
+ },
+ "c7_interview_problem": {
+ "scene": "你走上黑板,讲了你做出来的那道中值定理证明,又讲了研究笔记里的小引理。讲完,主考官点了点头:“你很清楚你在做什么。”",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "等待结果",
+ "next": "c7_offer",
+ "effects": {
+ "shuli": 4
+ }
+ }
+ ]
+ },
+ "c7_offer": {
+ "scene": "一个月后,你收到直博录取邮件。你盯着屏幕看了很久,然后给妈妈打了电话。她说:“我就知道你走得下去。”",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "回到宿舍",
+ "next": "c8_night",
+ "effects": {
+ "shuli": 3,
+ "xinzhi": 5
+ }
+ }
+ ]
+ },
+ "c8_night": {
+ "scene": "大四毕业前夜,你坐在宿舍窗边。面前是大一录取通知书、图书馆借书卡、被否定的草稿、妈妈的通话记录。你收到直博录取后,一直没合眼。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "在录取通知书背面写“我继续”",
+ "next": "c8_write",
+ "effects": {
+ "xinzhi": 5
+ }
+ },
+ {
+ "text": "给高中老师和妈妈打电话",
+ "next": "c8_call",
+ "effects": {
+ "xiayi": 5,
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "c8_write": {
+ "scene": "你在录取通知书背面写下一行字:“我继续。”笔尖停了一下,然后你写完整个句子:“我继续学数学,不是因为赢过谁,是因为我想一直做下去。”",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "收好通知书",
+ "next": "c8_noether_merge",
+ "effects": {
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "c8_call": {
+ "scene": "你给高中老师打了电话,老师说:“当年你说要学数学,我们都觉得你会后悔。你证明我们错了。”你笑着挂了电话,又打给妈妈。",
+ "character": "旁白",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "听妈妈说完",
+ "next": "c8_noether_merge",
+ "effects": {
+ "xiayi": 3,
+ "xinzhi": 3
+ }
+ }
+ ]
+ },
+ "c8_noether_merge": {
+ "scene": "诺特说:“定理比我们活得更久——但意义是我们写的。你从没有竞赛奖状的小镇来,现在带着一个问题离开——这就是数学给你的路。”",
+ "character": "诺特",
+ "sprite": "",
+ "choices": [
+ {
+ "text": "翻开第一页研究计划",
+ "next": "c8_phd",
+ "effects": {
+ "shuli": 5,
+ "xinzhi": 5,
+ "favorability": {
+ "诺特": 10
+ }
+ }
+ }
+ ]
+ },
+ "c8_phd": {
+ "scene": "直博第一年。你把录取通知书夹进新的笔记本,在第一页写下:\n\n“二十岁之前我以为数学是答案。二十岁之后我知道数学是问题。我选择了一个问题,然后用一生去回答它——不是给我自己,是给下一个坐在窗边写草稿的人。”\n\n窗外天亮了。你的数学,刚刚开始。",
+ "character": "主角",
+ "sprite": "",
+ "choices": []
+ }
+ }
+}
\ No newline at end of file
diff --git a/docs/精算师线_WebGAL剧本.md b/docs/精算师线_WebGAL剧本.md
new file mode 100644
index 0000000..1b138f3
--- /dev/null
+++ b/docs/精算师线_WebGAL剧本.md
@@ -0,0 +1,1314 @@
+# 精算师线 · WebGAL 剧本
+
+> 从 Word .doc (HTML格式) 恢复,中文有部分乱码。建议用 Word 重新保存为 UTF-8 纯文本。
+
+��|��{^��~ �� WebGAL gR,g
+
+
+
+ %N(�� + ��co = :Npef[��pef[
+
+ ��V*Npef[��[ �� mQ*N��b��b������p �� ��VB\���
+
+
+
+ 8nb��e��N�� scene/
+
+ ��vU_NqQ 8 *N .txt ��e��N
+
+ eQ��S��e��N�� scene/start.txt
+
+
+
+
+
+
+
+ ��e��N��~��g
+
+
+
+
+
+
+
+ `O��v8nb��vU_ / %%% config.txt ������������������ ��! 8nbM��n %%% scene/ ������ %%% start.txt ������������ ��! qQ����^��z��peR���� �� ������ %%% ch1_gauss.txt ������ ��! ,{NB\����e �� TXv�� ������ %%% ch2_zu.txt ������������ ��! ,{��NB\
+N��Vy��QKN �� ��W��X ������ %%% ch2_noether.txt �� ��! ,{��NB\N������yr �� p����^ ������ %%% ch3_euler.txt ������ ��! ,{ NB\��'k��b �� fN?b ������ %%% ch4_gauss.txt ������ ��! ,{��VB\
+N����e �� ,{��N!k ������ %%% ch4_noether.txt �� ��! ,{��VB\N������yr �� gTN!k
+
+
+
+
+
+
+
+ O(u��e_�� ��b��k*N0��e��N
+0:SWW��v��Q��[
+Y6R0R��[��^ .txt ��e��N-N��>e0R WebGAL
+
+ 8nb��vU_��v scene/ ��e��N9YNsS��SL��0
+
+ ����l����g�� �r��
+T : ��[K��Q��[ ; ��[K | : ��e}v ; ��e}v | intro: L�� 1| L�� 2; ^O\��r}v | choose: ��y�� :text;
+
+ R/e | label:
+T��y ;
+
+ h~{ | jumpLabel :
+T��y ;
+
+ ��l�� | changeScene : ��e��N
+T .txt;
+
+ R:Wof | setVar : ��S� ;
+
+ ������S� | ;
+
+ ��l�
+
+
+
+
+
+
+
+
+
+
+
+
+
+ config.txt
+
+
+
+
+
+
+
+
+
+
+
+ 📄 scene/config.txt 8nbM��n
+
+
+
+
+
+
+
+ ; ===== NtKN ��W �� ��|��{^��~ 8nbM��n ===== Game_name : NtKN ��W - ��|��{^��~ ; Game_key :0f1a2b3c; Title_ img :title_bg.jpg ; Title_ bgm :title_theme.mp3 ; Title_font_size :36; Textbox_font :"Microsoft YaHei "," PingFang SC ",sans -serif; Textbox_font_size :20;
+
+
+
+
+
+
+
+ scene/start.txt qQ����^��z �� peR����
+
+
+
+
+
+
+
+
+
+
+
+ 📄 scene/start.txt
+
+
+
+
+
+
+
+ ; ===== qQ����^��z��'YN
+N �� pef[R��g���� ===== changeBg :classroom_day.jpg ; bgm :campus_ambient.mp3 ; : 6����hYe��[PW��n��N��eu0FO,{N ��czz@w 0 : ��l g��N��`PW(WgMRb��0 : O\U^
+N��S gN*NlQ_ �� - ��0 ��Ye��c : ��ewz\
+N/f N *Npe0 : ��[/fN��yb����0 : `O(W{����,g
+N\PNeg0O/fOX����`O,{N!k\P{0 ��Ye��c : :N��NHNpef[/fg���[��v������ : ��V:N��[
+N��0��]
+NY
+00��[��0��|nx
+00 : ����Nh������]0����Nh��`OZP��Q��vzrrr0 : `O������`��b����]��Q\��1\����?aa������e������{��_��f��Q0 : ��N��S(W^g
+N��QN��NNL��06qTl���eg0 ��Ye��c : O1\/fpef[0 : `O��Sw��NN : /f`O,{N!k��x0R��NN*N gQ���vK0 ; ������p 1 ��b��b choose : ����O��SK��Q-N��N pef[/fb����0 :ch1_gauss.txt| b
+N*Y��a& & FOb?aa������e����d��a��[0 :ch1_gauss.txt ;
+
+
+
+
+
+
+
+ ��f�� $N*N ��y��������l��0RTN*N��e��N ch1_gauss.txt 0(W ch1_gauss.txt _4Y(u��S� setVar
+
+ ����U_ ����b��T��~��[K9hnc��S���Q��s��]R0
+
+
+
+
+
+
+
+
+
+
+
+
+
+ scene/ch1_gauss.txt ,{NB\����e �� TXv��
+
+
+
+
+
+
+
+
+
+
+
+ 📄 scene/ch1_gauss.txt
+
+
+
+
+
+
+
+ ; ===== ,{NB\����e �� peR���� ===== changeBg :tower_top_night.jpg ; bgm :quiet_piano.mp3 ; : `O������N�eg0
+N(WYe��[ (WN��^TX��vv��B\0 : ��zY������v@wte��^��W^0��kNagW��S��0��kNh��^Q{ hQ(W��SIQ0 : IQ��n /fpe
+Nn��vlQ_0 changeFigure :gauss_stand.png -center; ��e : `Oeg��N0��k
+NNhTba��N N*NZf
+N0 : ��Nb��MRJd@w��Qu����S��vKb?z0 : ��kNu������R��n��N FOR��c��v0W��e(uT{Q��Q��NNL����f��w��v����f0 ��e : b 19 ��\��Q��NO��{ sQ��NckASN����b_0 : ��Q��[bI��_
+NY}Y0 : @b��Nb��b��[��e������v��SNWW��w4Y ^��'k��QUO ��Q(W ��NI�� ?z��~b��0 : 6qT�����N��bI\0 : ��N��b_��bI\0b��X��n��N��~0 ��e : ����N��NASt^0 : 'k��bNt^��Q 50 ��{0b��Q��N 155 ��{0 : FOb��v 155 ��{ ��kN��{������S{k��N0 : pe�/f��Q��N��v8nb0��[��/f^y��vh��Q0 : ��N��c�egN,gzz}v������e?z��~0 ��e : `O��S������VT{N*NX妪�0 : `O?aa:NN*N��[t ����Y\t^�� : `O
+Nnx��[`O(W��`@w��0FO`O(W$N��y����KN��Q��~��Q��NT{Hh0 changeScene :ch2_zu.txt ;
+
+
+
+
+
+
+
+ scene/ch2_zu.txt ,{��NB\
+N��Vy��QKN �� ��W��X
+
+
+
+
+
+
+
+
+
+
+
+ 📄 scene/ch2_zu.txt
+
+
+
+
+
+
+
+ ; ===== ,{��NB\
+N��Vy��QKN �� ZP
+N��Q��v`������ ===== changeBg :city_wall_dawn.jpg ; bgm :morning_birds.mp3 ; : `O��l@w��W-Ng��v��W��Xp����N
+N��wYEN0 : ��W��X
+N g;R��^ ��k*N;R��^KN������z��_��QNNw
+N0R��������0 : g+gNL��;R@w 3.1415926 0 changeFigure :zuchongzhi_sit.png -center; Vy��QKN : b��k)Y�f����������~NW0 : (uKb��v��{y{ 8h��[NM������v;R��^0 : ]N~vt^ ��N*g������e0 : ��NdN��{y{0(W0W
+N;u��NASN*N\��p0 Vy��QKN : `O_N(W��{NS������ ��{
+N��Qeg0 : `O��]��~��{��N ,{ NZf��N0 : b��{0R,{ASNt^ te��~pe��z6qhQ
+N��[��N0 : MRASNt^��v��kN*N���� ����(W:N,{AS��Nt^������0 : `O��l g��0`O��S/f��lp��0R,{AS��Nt^0 : ��N��b��{y{>e(W0W
+N0ASN9h0,{AS��N9h��`(W`O��vKb-N0 Vy��QKN : `O
+N/f
+N��Tf[pef[0 : `O��S/f��l`N��` pef[O����`O0 : FO,{AS��Nt^ ��[O��[`O��[K0 : ��N��b,{AS��N9h���`O��c��_0 ; ������p 2 ��b��b choose : ��S~b��^��OS������0 :ch2_zu_teacher|
+N��V��[��[ ��NZfZP
+N��Qeg
+Naw0 :ch2_zu_stay; ; ===== R/e A ��Bl��R��^ ===== label :ch2_zu_teacher; Vy��QKN : ��[0b����]��{����v��eP =O W��t��l ��Qu0 : FOb��P[F錘��N��epe!k0 : ,{ASNt^ te��~pe����c��v ����N)Y bF錘S_��e/UN(WN��v��^0 : ��NJTKb`HN��06qTb��N ����Q��e _��Y0 : `O_N��S~b`O��v��^ yY
+NO��~`OT{Hh0 : FOyYOJTK`O`HN��06qT `O1\��������]��_MRp��0 jumpLabel :ch2_zu_merge; ; ===== R/e B ��{k��x ===== label :ch2_zu_stay; changeBg :library_midnight.jpg -next; :( ��Qhf N��p0��VfN��������sQF匩0`O��lp��0 ) :( ��]��g��v��{tXT/fN*N-Nt^sY'` `O��N*g�����0yY��bNog��p4l>e(W`OLh
+N0 ) ��{tXT : ��NASt^MR b_NPW(WO0TN*NMOn0TNS������0 `O : ��`����Qeg ��NT�� ��{tXT : ��l g0FO(W'Yf[g����b��`����NN��N��N g��N������`O��
+N��N��[��FO��[F2N`O0 :
+N%` `O��~��~��{0bPW@wI{sQ��0 changeBg :city_wall_dawn.jpg ; Vy��QKN : ��k*N��N���� gN*N����]��v��0 :
+N/fWhT��s0/f0hT��s
+00 : `O��V@wO*NpeW[p����[NW ������������v������k��e����0 : ~b0R`OZP
+NN��S��v����Nt^ 6qTONt^1\Ob:N`O����]��vhT��s0 : `O��NZf ����v /fj��O������VfN����p��0RsQ��0 : I{`O��QY}Y��N ��Qeg~bb0 ; ===== GlT ===== label :ch2_zu_merge; : Vy��QKN��b`OKb-N��v��{y{��S��_��c��_��c��NN��[0 Vy��QKN : OS������`OZP
+N��Qeg/f��[��v ��[,geg1\/f���fs��`��vyr��O0 : (W`OKNMR��l g��NZP��Qeg (W`OKNT_N
+NOl��
+N g��NZP��Qeg0 : FO��[(W`O��P[0 : ��N��b0W
+N;u��v\��pNN��d��c ��SiRN*N0,{AS��N*N0��NWOO0 Vy��QKN : (WO*N��p0RegKNMR ��b��kN)YS_\O`OgTN)Y0 : ��b��kN*N����S_\O/UN��v��[��Lr ��v0R`O��V@wO*NX妪���~��[��NNteW0 : ��[��W��v,{Nag ��[��T{HhO�0R FO`O��_{������@wI{��[0 : ��Nw������ ��l@w��W��X��~��~p��T�f0 changeScene :ch2_noether.txt ;
+
+
+
+
+
+
+
+ scene/ch2_noether.txt
+
+ ,{��NB\N������yr �� p����^
+
+
+
+
+
+
+
+
+
+
+
+ 📄 scene/ch2_noether.txt
+
+
+
+
+
+
+
+ ; ===== ,{��NB\N������yr �� ����������s ===== changeBg :seminar_room.jpg ; bgm :classical_piano.mp3 ; : 'Y��N
+N0,{N!k ��������� ��s0 : ^gMR��z@w x ��Nf[�� ck(W����!jzz���� 0 : ��N(W,{��Nek��0R,{ Nek��v��eP ��NNek0hQ��s��l��N��la0 : `O��vKbc\P(W��~
+N `O��wS��ONek g:��S0 : `O��l g>NKb0`O��S/f��Q��NN*N����S0 ; ������p 3 ��b��b choose : >NKb�� " f[�� ,{ Nek������QU\_NNT�� " :ch2_noe_raise| N����T��S~bf[����yJ��0 :ch2_noe_private; ; ===== R/e A ��>NKb ===== label :ch2_noe_raise; : `O��vKb\P(W��~b��
+N��eN��y 6qT>Nw��eg0 : te*N����������s��l��N0f[������NNek0 f[�� : I{NN ����b��V4Yww& & ��T0`O��_��[0b��NN*N_t0 : `O��v��_����P������bhV8xTt0FO`O��v{��zw��eg~{��N����]��v
+TW[0 jumpLabel :ch2_noe_merge; ; ===== R/e B ����yN~bf[�� ===== label :ch2_noe_private; : `O��l g>NKb0N����T`O��b@wI��?z��~p�����S0 : `O��S��NN��SK 0,{ Nek0
+0 : ��Nw��[`OJSu����v��c��[ ��b��Q{ (W`O����S��vMOn;u��N*NW0 f[�� : `O��_��[0ONekbnx��[��N0`O?aa.^b��bONek��R�NN��z��v\ONT�� ; ===== GlT ===== label :ch2_noe_merge; changeBg :corridor_dusk.jpg -next; bgm :quiet_strings.mp3 ; : `O��wS��0����yr��z(Wp����^-N���� ��yYe��[��
+N0R$Ns|0 : yYKb��b��v��|{ ��]��~��eb��N}Y��Q*b0 changeFigure :noether_stand.png -center; ����yr : b��z(WOagp����^ ,{ Nt^0 : ,{Nt^ b��S��s^g
+N��v��[^��Q����NNek0 : b(u��X��z�teagp����^ ��b��Q��Ncknx��vek����0 : ,{��Nt^ ����*N��[^��v���� p����^��S��]��~ gf[uI{b��z@w,T��[0 : `O��s(W /f`O��z(WOagp����^
+N��v,{Nt^0 : yY��bJS*b��|{���`OKb��_ +g��z��[P��0 ����yr : `O��S��s+R��N��_eu��vN�� O/fpef[��~`O��v : FO/f`O��_{������Qeg ����NLuw0ROek:��S0 :
+N����`O/f(WYe��[>N��NKb /fN����Tp���p����^ ����Qeg `OZP0R��N0 : ��N��s(W_��Y ��bOag��p��N��S0 : ��b,{N*N_t ��Q(Wb��z��NAS��Vt^��v������S
+N0 : yY��z(W`Ob��MR ��_{@w��Tp����^=\4Y0 ����yr : O/fb(Wp����^I{��NOHNEN��~`O��v
+N/fT{Hh0/f��0 : `Op����Q����������s0)Y��]��~^��N0 : pef[|ig v��B\ ��N6q��N@wop0����/f��e��vTX0 : _N/f����yr��z���v����agp����^ `O��s(W��z(W-N����0 changeScene :ch3_euler.txt ;
+
+
+
+
+
+
+
+ scene/ch3_euler.txt ,{ NB\��'k��b �� fN?b
+
+
+
+
+
+
+
+
+
+
+
+ 📄 scene/ch3_euler.txt
+
+
+
+
+
+
+
+ ; ===== ,{ NB\��'k��b �� d��YY ===== changeBg :dorm_night_window.jpg ; bgm :fireworks_distant.mp3 ; : 'Y��NN0d��YY0teh��[
+��|i ��SiR`ON*N��N0 : ��zY��k����NO?Q1\��p_NGr��p����0 : `Ob��MR��v`N����� ��]��~aS��NNtehT0 : Kb:g����R O\U^
+N��Q@w����Y��Y0 ; ������p 4 ��b��b choose : T
+NfN j����Y��Y}Y}Y��O?QK0 :ch3_eu_answer| MQ��c_@w>e��e���� ������{����J��0 :ch3_eu_continue; ; ===== R/e A ��j����Y��Y ===== label :ch3_eu_answer; : `OT
+N`N�����0��cw�������0 `O : ��Y��b(WT0z��P[ �X gVS����Q��v0 : yY��/}
+N/}0`O��L��0 : 6qT`O9e��S��N vQ��[:c/}��v0 : yY��`O��N\1\O7h /}��N��Neg
+N��0 :( `O��_6q��T��N0
+N/f��V:N����� /f��V:N g��N
+N(uI{`O_��S 1\��wS��`O(W/}0 ) `O : c��N ��Y0��et^��_PN0 : c��c5uKKNT`OQ��e��_`N����� ����S������/f��lZP��Qeg0FO`Ow��v
+N%`��N0 jumpLabel :ch3_eu_merge; ; ===== R/e B ��MQ��c��~��~��{ ===== label :ch3_eu_continue; : `O��bKb:g>e(W��S��fN
+Nb��0_��NMQ��c0 `O : ��Y`OI{NN b��bONek��{��[ : {\(W��~
+N��_��p��@w0gT����*Nl����b `O\P��N0 `O : }Y��N ��{��[��N0��Y0��et^��_PN0 :( KR{��������yY{��N0 ) ��Y��Y : 'Yt^ NAS(WZP���� ��N
+N0RsYg��S+R*`b��l��c����`O��T0 `O : ��N
+N0R1\��{��N b g0pef[0 : c��cKNT`OPW(WiP[
+N ��Seg����S��������vNNL��`O��l��Q0 : FO`OvQ��[/f(W�� ��Y��Y0`O_N/fb��vT{Hh0 ; ===== GlT ===== label :ch3_eu_merge; changeBg :study_round_table.jpg -next; bgm :gentle_piano.mp3 ; changeFigure :euler_sit.png -center; : 'k��bPW(W WLhMR0��N��v FO{(W p��0 : ��e����PW@w��Q*N\i[ (W.^��N��_��{_0 'k��b : b��Q��N 886 ��{������e0 : 6qTb��S��s ,{��N*Ni[P[O��S8r8r��v��eP b��l,T����0 : ��V:Nbw
+N����0_N��V:Nb��v3��5ghQ(W,TlQ_0 : ��N��v4V���_��_lbw��eg /f{0
+N/f{`O /f{��N����]0 'k��b : NAS��N��\ ��S : mQASmQ��\ $N��S : FOTN)YZf
+N bj��@wY[sYT��NZfm��0yY��b`
+N`0 : b��
+N`��l��N : @b��Nb��l g1Y��S w���� T{Hh��v������R0 : {\PNeg��N0FO��l g\P(W����]b��MR ��N��b{��cT`O0 'k��b : `ORMb��c��N`O��Y��Y��v5uK0 : `O(W��{NS��������v��-N \PNeg ,TyY��NK 6qTZP��Q��N��V��^0 : `O��l g��V:Npef[��1Y��SyY0 : `O
+N��Pb0`O g��e���� g : ��V��SJTK`O��Y `O��NZf (W��{TNS�� ���� ��TyY��kNS��t^Y>e(WNw�� ����/f(W��VT{��NHN��S��V��[0 : ��N��vKb ��l gg��~ /fgT`O0 'k��b : pef[
+N/f:P;N0 : pef[
+N/f`O kNLu ��v��f0 : pef[/f`O|T8T��v��e_0 : `O��V��S��~��~��{ FO��k��{��[Nu�� ��~���� MO��Y��Y ��SN��k����0 : JTKyY b��N)YZP��NNS������ ZP��N Nu����~ aS��NNhT��lZP��[0 : FOb��__��_ ��V:N��N)Yb��N6q(W0 : O1\/f��N)Y _N/fb��v��0b��vhT��s0 : ��N��bgTJS*b{>e�`OKb��_ ��T����yr��v����JS*b ��b(WNw��0 : `O��c'}0Tw��eg��v{ g��NNte/e��vQ�0 changeScene :ch4_gauss.txt ;
+
+
+
+
+
+
+
+ scene/ch4_gauss.txt ,{��VB\
+N����e �� ,{��N!k
+
+
+
+
+
+
+
+
+
+
+
+ 📄 scene/ch4_gauss.txt
+
+
+
+
+
+
+
+ ; ===== ,{��VB\
+N����e �� ����eT ===== changeBg :tower_map_room.jpg ; bgm :deep_thinking.mp3 ; : 'Y N
+N0��e��bN _NLu0W��V����(WLh
+N0 : 0W��V
+N$Nags^L����~ pe����(W��]����Npe��QUO(W��S0 : ��Nb��MR>e@w$N/e{0,{N/e/f����yrYu��~��N��v {\��]��~/_��N0 : ,{��N/e /fhQ��e*g��b��v0 changeFigure :gauss_sit.png -center; ��e : `O��[^��N)YF錘`ON*NX妪� `O ����T*N��eT�� : pe����0����S��'Y N ZP
+N��Qeg��v���� `O��l~b0RT{Hh0 : ��Npe��QUO0����yr�� ��~��g,g����1\/f��0 : yY��v��[t `Ow,{N ; ������p 5 ��b��b choose : pe���� }pe0b������V����S������b��MR0 :ch4_ga_number| ��Npe��QUO ��~��g0b��`w����yrw0R��vN��0 :ch4_ga_geom; ; ===== R/e A ��pe���� ===== label :ch4_ga_number; ��e : b ����NckASN����b_06qT��k��Nt^��_��SN*NX妪�0 : `O ��pe����
+NI{��N"N��c��Npe��QUO0 : FO`O��_����OO `OS_RZP
+N��Q����S��������v��eP
+N/f��V:N��[����0 : /f��V:N`O/f`O0����S�� ����N��v (WI{`O��V��S0 jumpLabel :ch4_ga_merge; ; ===== R/e B ����Npe��QUO ===== label :ch4_ga_geom; ��e : ����yr(WOag��
+Np����NJSu ��lp����[0 : Oag��O����eg��������
+NO gh��QT{Hh0 : `O
+N/f��fyY��c /f:N`O����]��c0 : `OZP}Y��Q��[��NT ��b`O��NT��v��kN*NZf
+N p��(WOag��~
+N0 :
+N/fp����[0/fp��N��S0 ; ===== GlT ===== label :ch4_ga_merge; : ��e��b����yr����/e��e{��N��bI\��b��Qeg0 ��e : O/e{/fyY,{N!keg��v��eP>e(WO hQ������b��N ��S g$N��{������e0 : 6qTyY��Q(uO/e{��Q��N��kN��{��[t0 : ��s(W��~`O
+N/f(ueg��Qt������v0 : /f��c����`O ��kNag�� ���� g��N(W`OMRb��p���0 : `O
+N(u��kyY��_0`O��S������kyY��fnZi `O��vI��?z Y��Q��NNu�� \ R ��N$NL��0 : `O��bw��{ (W0W��V
+N;u��NN*NW ������vpe����N��Npe��QUO��N��S��v����WW:S��W0 : ��e��l gNO4Yw0 : `O_N��lI{T{Hh l������N��N��N��vTX0 changeScene :ch4_noether.txt ;
+
+
+
+
+
+
+
+ scene/ch4_noether.txt
+
+ ,{��VB\N������yr �� gTN!k
+
+
+
+
+
+
+
+
+
+
+
+ 📄 scene/ch4_noether.txt
+
+
+
+
+
+
+
+ ; ===== ,{��VB\N������yr �� AI qS:g ===== changeBg :dorm_night_bed.jpg ; bgm :silence_tension.mp3 ; : 'Y NN0��mY0`O��]��~��_aw@w��N0 : Kb:ggTN!k/c��R h������S gN��SK0 intro :DeepMind ��v AI ����f��N | ��e��vpef[s��` | : ����:S(W7RO\ 0 ��~pe��[��N 0
+0 : `O��v@w)Y����g������N$N*N\��e ��P[��l glQ_0��S gN*NX妪�0 :AI ��������f��N0����b��{��NHN�� ; ������p 6 ��b��b choose : sQ��cKb:g ��~��~��{0 :ch4_noe_keep| aw
+N@w0��Sp����^~b����yr0 :ch4_noe_find; ; ===== R/e A ����~��~��{ ===== label :ch4_noe_keep; : `O��bKb:gsQ��c0^ ��f��S iR{����,g��vIQ0 : `O��QNNL�� AI ��������fNR��[t FO��[���� ����b����f��TN*NT�� : ~b
+N0RT{Hh0FOT
+N{����,gKNMR`O��Q��N,{��NL�� ��NZfb
+N����f��NUON��0 : b��S/f ����b ��b��e��������(Wpef[
+N0O1\/faIN0 jumpLabel :ch4_noe_merge; ; ===== R/e B ��~b����yr ===== label :ch4_noe_find; : `O��N��^���� Sb_{����,g FO`O
+N/f(W��Q����f0 : `O(Wd"}hSbN����yr��v
+TW[0`O����0RyY(W��NMRYu��~f[u��vgTN\��O :
+N����` AI 0 AI
+NO����]��Q��[NNek0FO`OO0 : `O��Q��N��V��O _4Y��l g`O��v
+TW[0��S gNL�� `OI{��NN~vt^��:ghV��leg0 : b��f`OI{ (W��kN��{����_(u������e��v����l0��Q��[`O��l;u��[��v���� _��V0 ; ===== GlT ===== label :ch4_noe_merge; changeBg :corridor_last_light.jpg -next; bgm :hopeful_strings.mp3 ; : ����yr��z(Wp����^=\4Y ck��[@w`O N*Nf[g MRp����Q ����������s��v����Gb��z0 : ��zY /fNtKN ��W�f��N,{N!k0 changeFigure :noether_smile.png -center; ����yr : `O��Veg��N0 : yY��l g��`ORMbZP��v ����b yY��v/f 0 ����yr : b 35 ��\����_d��0 : (W����KNMR b��S��`����f b��{��_��k��N��N��Q0 : ��y_��_��V����)YZf
+NbMbf}v b
+N/f(W����f����]0 : bN��v����/f(W����f ��~��g,g���� /f����v0 : ����
+N���������0 : yYg��zYg���S0 ����yr : ��~ gN)Y AI ����M��[bO����P[��{���v@b g��0 : FO��[
+N��wS�� :N��NHNb��NOagp����^p���eg0 : ��N��T:g hV��]��v1\/fONek aIN0/fb��N(W��kNL����[IN;RN��v
+TW[0 :AI ZP���� f0`O��f��[���0O1\/fpef[8l��������N��v��S��V0 : yY��bYeHh��_MR��c��NgTN ����yr : f)Y��e
+N `O��b����0RO��{ AI ��e������v��e���� ��b��S����@w0 :
+N/f��S��k����^ /f����yY��wS�� gN*N��N{|(W������{ (WTNL��lQ_��cOO��NyY��l��cNeg��v��|{0 : ��NO����Ye��[ 0R`O������Q��Q`O����],{ NS����l ������_��v��[t��v����N)Y ��~��~ ��Q��~��~0 : yY��vf[u��bte��v��|{,d�eg ��c0R`O��vKb����0gTN*b /f��e��v0FO`O��bw��{0 intro : ��NAS��\KNMR | b��N:Npef[ /fT{Hh0 | ��NAS��\KNT b��wS��pef[ /fX妪�0 | b ����b��NN*NX妪�0 | 6qT(uNu ��S��VT{��[0 |
+N/f��~b����] | /f��~NN*NPW(W��z������QI��?z��v��N0 -hold;
+
+
+
+
+
+
+
+ ��~_g���� gR,g0Rdk��~_g0gTNU^O(u
+
+ intro -hold
+
+ ��[��s^O\��r}v + ��Oc;ub��0��[E����r��e��S��N��c end; ��Vh����Lub����b��c changeScene:common_end.txt;
+
+ ��l��0RqQ��>\��X0
+
+
+
+
+
+
+
+
+
+
+
+
+
+ D��U_��y����v��e��NnUS
+
+& D����n��QY
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ��e��N
+
+
+
+
+
+ ��Q��[
+
+
+
+
+
+ ��r`
+
+
+
+
+
+
+
+
+
+ scene/config.txt
+
+
+
+
+
+ 8nbM��n��h����0W[SO0\b����V0h������f ��
+
+
+
+
+
+ ' ��]��Q}Y
+
+
+
+
+
+
+
+
+
+ scene/start.txt
+
+
+
+
+
+ qQ����^��z��'YNpeR����
+
+ �� ��b��b������p
+
+
+
+
+
+ ' ��]��Q}Y
+
+
+
+
+
+
+
+
+
+ scene/ch1_gauss.txt
+
+
+
+
+
+ ,{NB\����e
+
+ �� TXv�� �� Kb?zN��bI\
+
+
+
+
+
+ ' ��]��Q}Y
+
+
+
+
+
+
+
+
+
+ scene/ch2_zu.txt
+
+
+
+
+
+ ,{��NB\
+N��Vy��QKN
+
+ �� ��W��X �� ,{ASNt^��v����
+
+
+
+
+
+ ' ��]��Q}Y
+
+
+
+
+
+
+
+
+
+ scene/ch2_noether.txt
+
+
+
+
+
+ ,{��NB\N������yr
+
+ �� p����^ �� ,{N!k>NKb
+
+
+
+
+
+ ' ��]��Q}Y
+
+
+
+
+
+
+
+
+
+ scene/ch3_euler.txt
+
+
+
+
+
+ ,{ NB\��'k��b
+
+ �� fN?b �� d��YY����Y��Y��v5uK
+
+
+
+
+
+ ' ��]��Q}Y
+
+
+
+
+
+
+
+
+
+ scene/ch4_gauss.txt
+
+
+
+
+
+ ,{��VB\
+N����e
+
+ �� ,{��N!k �� ����eT��0W��V
+
+
+
+
+
+ ' ��]��Q}Y
+
+
+
+
+
+
+
+
+
+ scene/ch4_noether.txt
+
+
+
+
+
+ ,{��VB\N������yr
+
+ �� gTN!k �� AI qS:g��p����^=\4Y
+
+
+
+
+
+ ' ��]��Q}Y
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ����QY��vD����n
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {|��W
+
+
+
+
+
+ ��e��N
+T
+
+
+
+
+
+ ��f
+
+
+
+
+
+
+
+
+
+ of��V
+
+
+
+
+
+ classroom_day.jpg
+
+
+
+
+
+ 'Yf[6����hYe��[��}v)Y
+
+
+
+
+
+
+
+
+
+ of��V
+
+
+
+
+
+ tower_top_night.jpg
+
+
+
+
+
+ TXv��B\��Yof��fzz
+
+
+
+
+
+
+
+
+
+ of��V
+
+
+
+
+
+ city_wall_dawn.jpg
+
+
+
+
+
+ ��W��X���f
+
+
+
+
+
+
+
+
+
+ of��V
+
+
+
+
+
+ library_midnight.jpg
+
+
+
+
+
+ ��VfN��������mY
+
+
+
+
+
+
+
+
+
+ of��V
+
+
+
+
+
+ seminar_room.jpg
+
+
+
+
+
+ pef[��|\Ye��[������������s
+
+
+
+
+
+
+
+
+
+ of��V
+
+
+
+
+
+ corridor_dusk.jpg
+
+
+
+
+
+ p����^��f
+
+
+
+
+
+
+
+
+
+ of��V
+
+
+
+
+
+ dorm_night_window.jpg
+
+
+
+
+
+ ��[
+����Y����zY��p����
+
+
+
+
+
+
+
+
+
+ of��V
+
+
+
+
+
+ study_round_table.jpg
+
+
+
+
+
+ fN?b��WLh
+
+
+
+
+
+
+
+
+
+ of��V
+
+
+
+
+
+ tower_map_room.jpg
+
+
+
+
+
+ TX��0W��V��[
+
+
+
+
+
+
+
+
+
+ of��V
+
+
+
+
+
+ dorm_night_bed.jpg
+
+
+
+
+
+ ��[
+����^��������mY
+
+
+
+
+
+
+
+
+
+ of��V
+
+
+
+
+
+ corridor_last_light.jpg
+
+
+
+
+
+ p����^��gTN!k���f
+
+
+
+
+
+
+
+
+
+ ��z��~
+
+
+
+
+
+ gauss_stand.png / gauss_sit.png
+
+
+
+
+
+ ��e����z��z / PW��Y
+
+
+
+
+
+
+
+
+
+ ��z��~
+
+
+
+
+
+ zuchongzhi_sit.png
+
+
+
+
+
+ Vy��QKN��PW��Y����{y{
+
+
+
+
+
+
+
+
+
+ ��z��~
+
+
+
+
+
+ noether_stand.png / noether_smile.png
+
+
+
+
+
+ ����yr����z��z / ��_{
+
+
+
+
+
+
+
+
+
+ ��z��~
+
+
+
+
+
+ euler_sit.png
+
+
+
+
+
+ 'k��b��PW��Y����
+
+
+
+
+
+
+
+
+
+ BGM
+
+
+
+
+
+ campus_ambient.mp3
+
+
+
+
+
+ !h��V��s��X��
+
+
+
+
+
+
+
+
+
+ BGM
+
+
+
+
+
+ quiet_piano.mp3
+
+
+
+
+
+ ��[Y������4t����e
+
+
+
+
+
+
+
+
+
+ BGM
+
+
+
+
+
+ morning_birds.mp3
+
+
+
+
+
+ �f��#����Vy��QKN
+
+
+
+
+
+
+
+
+
+ BGM
+
+
+
+
+
+ classical_piano.mp3
+
+
+
+
+
+ ��SxQ����4t������yr
+
+
+
+
+
+
+
+
+
+ BGM
+
+
+
+
+
+ quiet_strings.mp3
+
+
+
+
+
+ ��[Y��&_PN��p����^
+
+
+
+
+
+
+
+
+
+ BGM
+
+
+
+
+
+ fireworks_distant.mp3
+
+
+
+
+
+ Y��p������d��Y
+
+
+
+
+
+
+
+
+
+ BGM
+
+
+
+
+
+ gentle_piano.mp3
+
+
+
+
+
+ )n��g����4t��'k��b
+
+
+
+
+
+
+
+
+
+ BGM
+
+
+
+
+
+ deep_thinking.mp3
+
+
+
+
+
+ ��l`�� ����eT
+
+
+
+
+
+
+
+
+
+ BGM
+
+
+
+
+
+ silence_tension.mp3
+
+
+
+
+
+ '} _��[Y���� AI qS:g
+
+
+
+
+
+
+
+
+
+ BGM
+
+
+
+
+
+ hopeful_strings.mp3
+
+
+
+
+
+ ^g&_PN����~@\
+
+
+
+
+
+
+
+
+
+ h����
+
+
+
+
+
+ title_bg.jpg
+
+
+
+
+
+ \b����V
+
+
+
+
+
+
+
+
+
+ h����
+
+
+
+
+
+ title_theme.mp3
+
+
+
+
+
+ h���� BGM
\ No newline at end of file
diff --git a/docs/精算师线_WebGAL剧本格式.doc b/docs/精算师线_WebGAL剧本格式.doc
new file mode 100644
index 0000000..0b6f633
Binary files /dev/null and b/docs/精算师线_WebGAL剧本格式.doc differ
diff --git a/docs/精算师线_第一线剧本大纲.doc b/docs/精算师线_第一线剧本大纲.doc
new file mode 100644
index 0000000..313890c
Binary files /dev/null and b/docs/精算师线_第一线剧本大纲.doc differ
diff --git a/scripts/build_mathbti.py b/scripts/build_mathbti.py
new file mode 100644
index 0000000..972eff6
--- /dev/null
+++ b/scripts/build_mathbti.py
@@ -0,0 +1,349 @@
+# -*- coding: utf-8 -*-
+"""生成新的 seed_mathbti.json — 16结果版"""
+import json
+
+data = {
+ "title": "数学人格测试",
+ "subtitle": "12题发现你的数学人格类型",
+ "share_text": "我的数学人格是「{name}」!来测测你是哪种数学灵魂?",
+ "version": "2.0",
+
+ "axes": [
+ {"id": "style", "name": "直觉流 ↔ 抠细节", "left": "直觉流", "right": "抠细节", "emoji_left": "🔮", "emoji_right": "🔍", "desc": "你靠直觉还是靠严谨面对数学?"},
+ {"id": "purpose", "name": "推演派 ↔ 算账派", "left": "推演派", "right": "算账派", "emoji_left": "📐", "emoji_right": "💰", "desc": "你更爱数学本身,还是更爱数学的用处?"},
+ {"id": "era", "name": "古典派 ↔ 现代派", "left": "古典派", "right": "现代派", "emoji_left": "📜", "emoji_right": "🤖", "desc": "你偏爱经典数学还是前沿科技?"},
+ {"id": "social", "name": "独刷党 ↔ 讲题党", "left": "独刷党", "right": "讲题党", "emoji_left": "🎧", "emoji_right": "🔥", "desc": "你更喜欢独自钻研还是交流讨论?"}
+ ],
+
+ "clans": {
+ "believer": {"name": "信仰者", "english": "Believer", "color": "#4A90D9", "description": "把数学当信仰的人——不为名不为利,只为真理本身"},
+ "spreader": {"name": "传播者", "english": "Spreader", "color": "#D4A574", "description": "把数学当语言的人——让不懂数学的人也能看见数学之美"},
+ "applier": {"name": "应用者", "english": "Applier", "color": "#3A6B8C", "description": "把数学当工具的人——用数学解决真实世界的问题"},
+ "seer": {"name": "直觉者", "english": "Seer", "color": "#7C5CBF", "description": "把数学当直觉的人——看见别人看不见的结构与联系"}
+ },
+
+ "questions": [
+ # ---- style 轴(3题) ----
+ {"id": 1, "axis": "style", "question": "考试还剩5分钟,你发现最后一道大题有个数字好像写错了。你会?",
+ "options": [
+ {"text": "快速扫一眼逻辑链,感觉不对劲就改", "score": 1},
+ {"text": "从头全部重算,不确认无误绝不交卷", "score": 3},
+ {"text": "直接涂改,相信第六感——改对改错都是命", "score": 0},
+ {"text": "把关键那几步重新心算一遍,确认再改", "score": 2}
+ ]},
+ {"id": 5, "axis": "style", "question": "正在刷一套卷子的选择题,你通常怎么选?",
+ "options": [
+ {"text": "先排除一两个明显不靠谱的,剩下的凭感觉", "score": 1},
+ {"text": "把每个选项都代进去验一下,不跳过任何一步", "score": 2},
+ {"text": "必须四个选项全算完,才敢下手勾答案", "score": 3},
+ {"text": "扫一眼选项,直觉跳出来就是C,直接下一题", "score": 0}
+ ]},
+ {"id": 9, "axis": "style", "question": "做几何证明题时,你的第一反应更接近?",
+ "options": [
+ {"text": "先凭经验猜一个可能的方向,再往下推", "score": 1},
+ {"text": "脑子里直接「看到」辅助线该画哪,说不出为什么", "score": 0},
+ {"text": "把所有相关定理列出来,逐条排查哪个能用", "score": 3},
+ {"text": "从已知条件出发,一步步推导,不跳步", "score": 2}
+ ]},
+
+ # ---- purpose 轴(3题) ----
+ {"id": 2, "axis": "purpose", "question": "数学老师问「如何用最少材料做出最大容量的盒子」,你脑子里先出现的是?",
+ "options": [
+ {"text": "这不是外卖包装盒的设计问题吗?生活中真遇到过", "score": 2},
+ {"text": "函数的极值点——这题本质是求导找最优解", "score": 0},
+ {"text": "直接搜「最优容器形状」看有没有现成结论拿来用", "score": 3},
+ {"text": "先设未知数、列方程、求导——标准流程走一套", "score": 1}
+ ]},
+ {"id": 6, "axis": "purpose", "question": "朋友真心问你:「学数学到底有什么用?」你本能想怎么回答?",
+ "options": [
+ {"text": "「数学才是最好的薅羊毛工具,从理财到砍价,全都是数学」", "score": 3},
+ {"text": "「数学和诗歌一样,不需要『有用』,它是人类思维的巅峰」", "score": 0},
+ {"text": "「数学能让你看懂房贷利率怎么算、双11折扣是不是真便宜」", "score": 2},
+ {"text": "「数学训练的是逻辑推理能力,这种能力做任何事都有用」", "score": 1}
+ ]},
+ {"id": 10, "axis": "purpose", "question": "如果上天赐你一种「顶级数学能力」,你更想要哪个?",
+ "options": [
+ {"text": "能用数学模型让一家公司效率翻倍的实战力", "score": 2},
+ {"text": "能证明困扰数学家百年未解难题的推演力", "score": 1},
+ {"text": "能凭空创造一套全新数学体系的天才直觉", "score": 0},
+ {"text": "能在股市里靠数学模型持续赚到真金白银", "score": 3}
+ ]},
+
+ # ---- era 轴(3题) ----
+ {"id": 3, "axis": "era", "question": "如果必须选一本「数学书」陪你度过周末,你会选?",
+ "options": [
+ {"text": "直接打开GitHub跑一个最新开源模型的代码——书太慢了", "score": 3},
+ {"text": "《数学简史》——了解数学思想的来龙去脉", "score": 1},
+ {"text": "欧几里得《几何原本》——经典永不过时", "score": 0},
+ {"text": "《深度学习的数学》——紧跟AI时代不掉队", "score": 2}
+ ]},
+ {"id": 7, "axis": "era", "question": "如果给你一次数学相关的「穿越」机会,你最想去?",
+ "options": [
+ {"text": "20世纪中叶,参与第一台电子计算机的数学建模", "score": 2},
+ {"text": "去未来50年,看看AI已经把数学推到了什么高度", "score": 3},
+ {"text": "古希腊学院,跟毕达哥拉斯学派一起研究「万物皆数」", "score": 0},
+ {"text": "17世纪,见证牛顿和莱布尼茨的微积分发明大战", "score": 1}
+ ]},
+ {"id": 11, "axis": "era", "question": "大学选数学社团,以下哪个最吸引你?",
+ "options": [
+ {"text": "「AI数学前沿社」——研究大模型背后的数学原理", "score": 3},
+ {"text": "「数据科学工坊」——用Python做数据分析和可视化", "score": 2},
+ {"text": "「数学史研究会」——探索数学思想千年演变", "score": 1},
+ {"text": "「欧几里得读书会」——每周研读数学经典原著", "score": 0}
+ ]},
+
+ # ---- social 轴(3题) ----
+ {"id": 4, "axis": "social", "question": "周末下午,理想的数学学习方式是?",
+ "options": [
+ {"text": "戴上降噪耳机,一个人沉浸式刷题,谁也别打扰", "score": 0},
+ {"text": "约上三五好友去图书馆,边讨论边学,学完去撸串", "score": 3},
+ {"text": "找个安静的角落自己学,偶尔瞄一眼群消息", "score": 1},
+ {"text": "和朋友约线上连麦,各学各的但互相陪伴", "score": 2}
+ ]},
+ {"id": 8, "axis": "social", "question": "碰上一道完全不会的题,你的本能反应是?",
+ "options": [
+ {"text": "死磕!不查答案不求助,自己解出来才有快感", "score": 0},
+ {"text": "搜一下解题思路,然后自己照着推一遍", "score": 2},
+ {"text": "先自己硬啃半小时,实在不行再看提示", "score": 1},
+ {"text": "直接拍照发学习群:「大佬们救命!在线等!」", "score": 3}
+ ]},
+ {"id": 12, "axis": "social", "question": "如果数学是一道菜,你觉得它更像?",
+ "options": [
+ {"text": "需要独自静心品味的怀石料理——一口一层深意", "score": 0},
+ {"text": "跟饭搭子分享的家常小炒——边吃边聊才有味", "score": 2},
+ {"text": "大家围在一起涮的热闹火锅——谁都能夹一筷子", "score": 3},
+ {"text": "自己在家慢炖的老火汤——时间越久越有味道", "score": 1}
+ ]}
+ ],
+
+ "results": {
+ # ========== 信仰者族(严谨+推演 = style右 + purpose左) ==========
+ "1000": {
+ "name": "定理工匠", "icon": "⊙", "icon_bg": "#DCE8F5",
+ "clan": "believer", "clan_name": "信仰者",
+ "mathematician": "高斯",
+ "keywords": ["严谨", "古典", "独刷", "推演"],
+ "short_desc": "严谨推导 + 古典底蕴 + 独立钻研 + 纯粹热爱",
+ "description": "你像高斯一样,是数学世界的完美主义工匠。每一步推导都必须严丝合缝,每一个定理都要亲自证明过才放心。你学数学不是为了「有用」,而是因为数学本身就是目的——那完美的逻辑链条,就是世界上最美的东西。",
+ "tagline": "你不出手则已,一出手就是一篇完美的证明",
+ "color": "#4A90D9",
+ "bio": "高斯被誉为「数学王子」。他3岁纠正父亲账目,10岁秒算1到100。他的性格极度追求完美,很多重大发现因为觉得「不够优雅」而锁在抽屉里从未发表。",
+ "bio_fact": "高斯临终前嘱咐,要在墓碑上刻一个正十七边形——这是他19岁时的杰作,也是他一生完美主义的起点。",
+ "portrait": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=cute%20flat%20vector%20illustration%2C%20a%20middle-aged%20European%20scholar%20with%20round%20glasses%20and%20neat%20white%20collar%20shirt%2C%20thoughtful%20expression%2C%20holding%20a%20compass%2C%20full%20body%20standing%20pose%2C%20modern%20MBTI%2016personalities%20avatar%20style%2C%20simple%20geometric%20shapes%2C%20soft%20pastel%20palette%2C%20clean%20minimal%20design%2C%20soft%20cel%20shading%20with%20warm%20lighting%2C%20subtle%20texture%2C%20off-white%20cream%20background%2C%20high%20quality%202D%20art&image_size=portrait_4_3"
+ },
+ "1010": {
+ "name": "穷竭守尺人", "icon": "π", "icon_bg": "#F0E5D8",
+ "clan": "believer", "clan_name": "信仰者",
+ "mathematician": "祖冲之",
+ "keywords": ["严谨", "古典", "独刷", "推演"],
+ "short_desc": "严谨推导 + 古典底蕴 + 独立钻研 + 精确至上",
+ "description": "你像祖冲之一样,是数学世界的守尺人。你愿意花一辈子把一件事算到最准——不是为了名,不是为了利,而是因为「这个数字必须是对的」。在一个人人追求速成的时代,你选择最慢的方式,走最远的路。",
+ "tagline": "你用算筹对抗时间,一千年后还有人记得你算的那个数",
+ "color": "#C47F3A",
+ "bio": "祖冲之把圆周率算到小数点后七位,这个纪录压了全世界九百多年。他还造了《大明历》,测得回归年仅差46秒。他一生只在做一件事:再乱的时代里,把数字算到最准。",
+ "bio_fact": "他给出的密率355/113,漂亮得不像手算时代的产物——直到16世纪欧洲人才重新发现它。",
+ "portrait": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=cute%20flat%20vector%20illustration%2C%20a%20Chinese%20scholar%20in%20Southern%20dynasty%20robes%2C%20holding%20counting%20rods%2C%20calm%20focused%20expression%2C%20full%20body%20standing%20pose%2C%20modern%20MBTI%2016personalities%20avatar%20style%2C%20simple%20geometric%20shapes%2C%20soft%20pastel%20palette%2C%20clean%20minimal%20design%2C%20soft%20cel%20shading%20with%20warm%20lighting%2C%20subtle%20texture%2C%20off-white%20cream%20background%2C%20high%20quality%202D%20art&image_size=portrait_4_3"
+ },
+ "1001": {
+ "name": "公式狂僧", "icon": "∑", "icon_bg": "#F0E5D8",
+ "clan": "believer", "clan_name": "信仰者",
+ "mathematician": "欧拉",
+ "keywords": ["严谨", "古典", "独刷", "推演"],
+ "short_desc": "严谨推导 + 古典底蕴 + 独立钻研 + 永不停歇",
+ "description": "你像欧拉一样,是数学世界里的永动机。你不是因为「想赢」才写论文,而是因为「停不下来」——数学对你来说不是职业,是呼吸。失明了反而算得更快,因为不用看纸,直接在脑子里算。",
+ "tagline": "你停止计算的那一刻,才是你停止生命的那一刻",
+ "color": "#C47F3A",
+ "bio": "欧拉一生写了886篇论文,死后还留下了足够出版50年的手稿。他晚年双目失明后,产出反而翻倍——全部靠心算口述。",
+ "bio_fact": "他解了柯尼斯堡七桥问题,顺手发明了图论——今天你用的导航App,底层都有图论的影子。",
+ "portrait": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=cute%20flat%20vector%20illustration%2C%20an%20energetic%2018th%20century%20Swiss%20scholar%20in%20formal%20coat%2C%20one%20hand%20gesturing%20while%20explaining%2C%20lively%20eyes%2C%20full%20body%20standing%20pose%2C%20modern%20MBTI%2016personalities%20avatar%20style%2C%20simple%20geometric%20shapes%2C%20soft%20pastel%20palette%2C%20clean%20minimal%20design%2C%20soft%20cel%20shading%20with%20warm%20lighting%2C%20subtle%20texture%2C%20off-white%20cream%20background%2C%20high%20quality%202D%20art&image_size=portrait_4_3"
+ },
+ "1011": {
+ "name": "抽象代数宗师", "icon": "⬡", "icon_bg": "#ECE0F0",
+ "clan": "believer", "clan_name": "信仰者",
+ "mathematician": "诺特",
+ "keywords": ["严谨", "现代", "讲题", "推演"],
+ "short_desc": "严谨推导 + 前沿视野 + 善于传授 + 结构之美",
+ "description": "你像诺特一样,是抽象世界的建筑大师。你不在乎「这个定理能用来干嘛」——你在乎的是「这个结构是不是美的」。被时代否定三次,你不在乎,因为数学永远站你这边。",
+ "tagline": "你把最抽象的数学,讲得连门外汉都想入门",
+ "color": "#8E5EA0",
+ "bio": "诺特被爱因斯坦称为「自女性开始接受高等教育以来最杰出的数学天才」。她的诺特定理是现代物理学的基石。",
+ "bio_fact": "她在哥廷根讲课的头几年因为性别拿不到薪水。她不在乎,继续免费教书——她的学生后来成为了一整代「诺特学派」。",
+ "portrait": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=cute%20flat%20vector%20illustration%2C%20a%20warm%20German%20woman%20with%20round%20glasses%2C%20short%20curly%20hair%2C%20modest%20dress%2C%20encouraging%20smile%2C%20full%20body%20standing%20pose%2C%20modern%20MBTI%2016personalities%20avatar%20style%2C%20simple%20geometric%20shapes%2C%20soft%20pastel%20palette%2C%20clean%20minimal%20design%2C%20soft%20cel%20shading%20with%20warm%20lighting%2C%20subtle%20texture%2C%20off-white%20cream%20background%2C%20high%20quality%202D%20art&image_size=portrait_4_3"
+ },
+
+ # ========== 传播者族(严谨+算账 = style右 + purpose右) ==========
+ "1100": {
+ "name": "商路算郎", "icon": "≈", "icon_bg": "#EDE5D8",
+ "clan": "spreader", "clan_name": "传播者",
+ "mathematician": "斐波那契",
+ "keywords": ["严谨", "古典", "讲题", "应用"],
+ "short_desc": "严谨推导 + 古典底蕴 + 善于传授 + 跨越文化",
+ "description": "你像斐波那契一样,是行走在东西方之间的数学翻译者。你学数学不是为了自己懂,是为了让别人也懂——你把阿拉伯数字讲给欧洲商人,改变了整个西方计算史。",
+ "tagline": "你用数学走通了丝绸之路",
+ "color": "#B8956E",
+ "bio": "斐波那契是13世纪意大利商人。他随父亲在北非经商时接触到阿拉伯数字,立刻意识到这比罗马数字好用太多。他写了《算盘书》,把阿拉伯数字引入欧洲。",
+ "bio_fact": "著名的兔子繁殖问题只是他书中的一道练习题——他本人大概没想到,这道题后来变成了数学史上最著名的数列。",
+ "portrait": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=cute%20flat%20vector%20illustration%2C%20a%20curious%20Italian%20merchant%20in%20medieval%20tunic%2C%20holding%20a%20quill%20and%20ledger%2C%20warm%20Mediterranean%20look%2C%20full%20body%20standing%20pose%2C%20modern%20MBTI%2016personalities%20avatar%20style%2C%20simple%20geometric%20shapes%2C%20soft%20pastel%20palette%2C%20clean%20minimal%20design%2C%20soft%20cel%20shading%20with%20warm%20lighting%2C%20subtle%20texture%2C%20off-white%20cream%20background%2C%20high%20quality%202D%20art&image_size=portrait_4_3"
+ },
+ "1110": {
+ "name": "双法布道者", "icon": "△", "icon_bg": "#F0E5D8",
+ "clan": "spreader", "clan_name": "传播者",
+ "mathematician": "华罗庚",
+ "keywords": ["严谨", "现代", "讲题", "应用"],
+ "short_desc": "严谨推导 + 现代视野 + 善于传授 + 人间烟火",
+ "description": "你像华罗庚一样,是数学世界的布道者。你揣着粉笔走遍中国,把优选法讲给工人农民——你相信数学不属于象牙塔,属于每一个人。",
+ "tagline": "你把数学从大学讲台,讲到了田间地头",
+ "color": "#C47F3A",
+ "bio": "华罗庚左腿残疾、初中文凭、杂货铺出身,却成为二十世纪中国最伟大的数学家。他走遍23个省市区,给工人和农民演示「优选法」和「统筹法」。",
+ "bio_fact": "1985年他在东京大学做完学术报告后心脏病发,倒在讲台上——他说的最后一句话是「我要工作到生命的最后一刻」。",
+ "portrait": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=cute%20flat%20vector%20illustration%2C%20a%20Chinese%20scholar%20in%20mid-20th%20century%20attire%2C%20holding%20a%20chalk%2C%20warm%20approachable%20smile%2C%20full%20body%20standing%20pose%2C%20modern%20MBTI%2016personalities%20avatar%20style%2C%20simple%20geometric%20shapes%2C%20soft%20pastel%20palette%2C%20clean%20minimal%20design%2C%20soft%20cel%20shading%20with%20warm%20lighting%2C%20subtle%20texture%2C%20off-white%20cream%20background%2C%20high%20quality%202D%20art&image_size=portrait_4_3"
+ },
+ "1101": {
+ "name": "决斗狂热者", "icon": "△", "icon_bg": "#D8EDE5",
+ "clan": "spreader", "clan_name": "传播者",
+ "mathematician": "伽罗瓦",
+ "keywords": ["严谨", "现代", "独刷", "应用"],
+ "short_desc": "严谨推导 + 前沿视野 + 独立钻研 + 死前传播",
+ "description": "你像伽罗瓦一样,是数学界的浪漫主义者。你头脑中有整个世界都尚未理解的真理,决斗前夜你通宵写信,把群论塞进一封信里扔给未来——你用死亡完成了最后一次传播。",
+ "tagline": "在决斗的前一晚,你通宵写下了改变数学史的所有想法",
+ "color": "#3D7A6E",
+ "bio": "伽罗瓦17岁奠定群论基础,20岁死于决斗。决斗前夜他通宵写信,在页边疯狂写道「我没有时间了」——那些草稿后来成为现代代数学的基石。",
+ "bio_fact": "伽罗瓦的手稿被弟弟保存了14年才被刘维尔发现。今天「伽罗瓦理论」是每个数学系研究生的必修课。",
+ "portrait": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=cute%20flat%20vector%20illustration%2C%20a%20young%20French%20man%20in%20early%2019th%20century%20style%2C%20passionate%20rebellious%20gaze%2C%20dark%20curly%20hair%2C%20wearing%20a%20dark%20coat%2C%20holding%20a%20quill%20pen%2C%20full%20body%20standing%20pose%2C%20modern%20MBTI%2016personalities%20avatar%20style%2C%20simple%20geometric%20shapes%2C%20soft%20pastel%20palette%2C%20clean%20minimal%20design%2C%20soft%20cel%20shading%20with%20warm%20lighting%2C%20subtle%20texture%2C%20off-white%20cream%20background%2C%20high%20quality%202D%20art&image_size=portrait_4_3"
+ },
+ "1111": {
+ "name": "织机先知", "icon": "⊗", "icon_bg": "#F0E0E8",
+ "clan": "spreader", "clan_name": "传播者",
+ "mathematician": "Ada Lovelace",
+ "keywords": ["严谨", "现代", "讲题", "应用"],
+ "short_desc": "严谨推导 + 前沿视野 + 善于传授 + 人机翻译",
+ "description": "你像Ada Lovelace一样,是数学与计算交汇处的先知。你写出了人类历史上第一个算法——在计算机还没诞生之前,你就已经把数学讲给了机器听。",
+ "tagline": "你在计算机还没诞生之前,就已经写出了第一个算法",
+ "color": "#9B4D73",
+ "bio": "Ada Lovelace是诗人拜伦的女儿。她在1843年为巴贝奇的分析机写了史上第一个程序,并预言计算机将来不仅能算数,还能作曲、画画。",
+ "bio_fact": "她的名字被用来命名了Ada编程语言。每年十月的第二个星期二被定为「Ada Lovelace日」。",
+ "portrait": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=cute%20flat%20vector%20illustration%2C%20an%20elegant%20Victorian%20lady%20with%20an%20intricate%20updo%20hairstyle%2C%20lace%20collar%20dress%2C%20piercing%20intelligent%20eyes%2C%20full%20body%20standing%20pose%2C%20modern%20MBTI%2016personalities%20avatar%20style%2C%20simple%20geometric%20shapes%2C%20soft%20pastel%20palette%2C%20clean%20minimal%20design%2C%20soft%20cel%20shading%20with%20warm%20lighting%2C%20subtle%20texture%2C%20off-white%20cream%20background%2C%20high%20quality%202D%20art&image_size=portrait_4_3"
+ },
+
+ # ========== 应用者族(直觉+算账 = style左 + purpose右) ==========
+ "0100": {
+ "name": "大衍谋士", "icon": "⊕", "icon_bg": "#D8E5F0",
+ "clan": "applier", "clan_name": "应用者",
+ "mathematician": "秦九韶",
+ "keywords": ["直觉", "古典", "独刷", "应用"],
+ "short_desc": "直觉驱动 + 古典底蕴 + 独立钻研 + 数学武器",
+ "description": "你像秦九韶一样,是数学世界的谋士。你提出中国剩余定理比高斯早五百年——但你也用数学杀人。对你来说,数学不是信仰,是力量;不是美,是武器。",
+ "tagline": "你用数学平叛,也用数学杀人——数学是你的刀",
+ "color": "#3A6B8C",
+ "bio": "秦九韶是南宋全才,精音律、善诗文、懂建筑、会带兵。他写成《数书九章》,提出大衍求一术(中国剩余定理),比高斯早五百年。",
+ "bio_fact": "史载他「性机巧、好奢侈」,曾毒杀亲信——数学家说他空前绝后,史官说他品行不端。",
+ "portrait": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=cute%20flat%20vector%20illustration%2C%20a%20Chinese%20Song%20dynasty%20scholar-official%20in%20dark%20robes%2C%20sharp%20calculating%20eyes%2C%20holding%20counting%20rods%2C%20full%20body%20standing%20pose%2C%20modern%20MBTI%2016personalities%20avatar%20style%2C%20simple%20geometric%20shapes%2C%20soft%20pastel%20palette%2C%20clean%20minimal%20design%2C%20soft%20cel%20shading%20with%20warm%20lighting%2C%20subtle%20texture%2C%20off-white%20cream%20background%2C%20high%20quality%202D%20art&image_size=portrait_4_3"
+ },
+ "0110": {
+ "name": "宇宙立法者", "icon": "⊕", "icon_bg": "#D8E5F0",
+ "clan": "applier", "clan_name": "应用者",
+ "mathematician": "牛顿",
+ "keywords": ["直觉", "古典", "独刷", "应用"],
+ "short_desc": "直觉驱动 + 古典底蕴 + 独立钻研 + 征服世界",
+ "description": "你像牛顿一样,是数学世界的立法者。你用微积分统一天地,用万有引力预测行星——但你也用皇家学会打压异己。对你来说,数学是征服世界的剑。",
+ "tagline": "你证明了宇宙的统一,却统一不了自己",
+ "color": "#3A6B8C",
+ "bio": "牛顿1687年出版《自然哲学的数学原理》,人类第一次用三条定律统一了天与地。他与莱布尼茨争夺微积分优先权,自己判自己胜诉。",
+ "bio_fact": "他晚年沉迷炼金术与末日预言,留下数百万字神秘书稿——神性与兽性同体一具肉身。",
+ "portrait": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=cute%20flat%20vector%20illustration%2C%20a%20stern%20English%20scholar%20in%2017th%20century%20attire%2C%20long%20hair%2C%20intense%20piercing%20eyes%2C%20holding%20a%20prism%2C%20full%20body%20standing%20pose%2C%20modern%20MBTI%2016personalities%20avatar%20style%2C%20simple%20geometric%20shapes%2C%20soft%20pastel%20palette%2C%20clean%20minimal%20design%2C%20soft%20cel%20shading%20with%20warm%20lighting%2C%20subtle%20texture%2C%20off-white%20cream%20background%2C%20high%20quality%202D%20art&image_size=portrait_4_3"
+ },
+ "0111": {
+ "name": "密码破译使", "icon": "⊡", "icon_bg": "#D8EDE0",
+ "clan": "applier", "clan_name": "应用者",
+ "mathematician": "图灵",
+ "keywords": ["直觉", "现代", "独刷", "应用"],
+ "short_desc": "直觉驱动 + 前沿视野 + 独立钻研 + 数学救国",
+ "description": "你像图灵一样,是数字世界的破译者。你用数学破译Enigma,拯救了千万生命——但世界不知道你的名字。对你来说,数学是挡在文明前的墙。",
+ "tagline": "你拯救了世界,世界把你碾碎",
+ "color": "#2D6A4F",
+ "bio": "图灵24岁发表《论可计算数》,定义了「计算」本身。二战时他破解Enigma密码,让战争提前两年结束,拯救了上千万生命——这一切作为国家机密封存了三十年。",
+ "bio_fact": "他还是马拉松好手,最好成绩距奥运选拔线仅11分钟。2013年英国女王颁布皇家赦免。",
+ "portrait": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=cute%20flat%20vector%20illustration%2C%20a%20gentle%20British%20man%20in%20a%20knitted%20sweater%20vest%2C%20shy%20kind%20smile%2C%20holding%20an%20apple%2C%20relaxed%20posture%2C%20full%20body%20standing%20pose%2C%20modern%20MBTI%2016personalities%20avatar%20style%2C%20simple%20geometric%20shapes%2C%20soft%20pastel%20palette%2C%20clean%20minimal%20design%2C%20soft%20cel%20shading%20with%20warm%20lighting%2C%20subtle%20texture%2C%20off-white%20cream%20background%2C%20high%20quality%202D%20art&image_size=portrait_4_3"
+ },
+ "0101": {
+ "name": "博弈游侠", "icon": "⊕", "icon_bg": "#D8E5F0",
+ "clan": "applier", "clan_name": "应用者",
+ "mathematician": "冯·诺依曼",
+ "keywords": ["直觉", "现代", "讲题", "应用"],
+ "short_desc": "直觉驱动 + 前沿视野 + 善于传授 + 世界棋盘",
+ "description": "你像冯·诺依曼一样,是游走在多个领域的数学侠客。你把博弈论变成冷战武器,把计算机架构定义成现代标准——对你来说,数学是下棋的手,世界是棋盘。",
+ "tagline": "你把世界算成一盘棋,而且你每把都赢",
+ "color": "#3A6B8C",
+ "bio": "冯·诺依曼8岁掌握微积分,22岁拿下博士学位。他奠定了现代计算机架构、创立了博弈论、参与了曼哈顿计划,还会在派对上心算八位数乘法娱乐朋友。",
+ "bio_fact": "有人问他:「你研究博弈论,是不是因为你喜欢下棋?」他回答:「不,我研究博弈论是为了理解核威慑——棋太简单了。」",
+ "portrait": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=cute%20flat%20vector%20illustration%2C%20a%20sharp%20Hungarian%20man%20in%20a%20tailored%20gray%20suit%2C%20confident%20slight%20smile%2C%20one%20hand%20in%20pocket%2C%20full%20body%20standing%20pose%2C%20modern%20MBTI%2016personalities%20avatar%20style%2C%20simple%20geometric%20shapes%2C%20soft%20pastel%20palette%2C%20clean%20minimal%20design%2C%20soft%20cel%20shading%20with%20warm%20lighting%2C%20subtle%20texture%2C%20off-white%20cream%20background%2C%20high%20quality%202D%20art&image_size=portrait_4_3"
+ },
+
+ # ========== 直觉者族(直觉+推演 = style左 + purpose左) ==========
+ "0000": {
+ "name": "几何殉道者", "icon": "◇", "icon_bg": "#F5EBE0",
+ "clan": "seer", "clan_name": "直觉者",
+ "mathematician": "希帕提娅",
+ "keywords": ["直觉", "古典", "讲题", "推演"],
+ "short_desc": "直觉驱动 + 古典底蕴 + 善于传授 + 理性之光",
+ "description": "你像希帕提娅一样,是几何世界的殉道者。你在几何图形里看见柏拉图的永恒,被暴民撕碎时手里还握着几何讲义——对你来说,数学是理性的光,值得用生命捍卫。",
+ "tagline": "你不是在做题,你是在为理性殉道",
+ "color": "#D4A574",
+ "bio": "希帕提娅是历史上第一位有记载的女数学家,公元4世纪在埃及亚历山大城讲学。415年她被基督徒暴民杀害,标志着古典希腊数学的终结。",
+ "bio_fact": "她改良了星盘和比重计的设计,这两种仪器分别被用了1400多年。",
+ "portrait": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=cute%20flat%20vector%20illustration%2C%20a%20graceful%20Greek%20woman%20in%20classical%20robes%2C%20holding%20a%20scroll%2C%20wise%20and%20serene%20expression%2C%20long%20dark%20hair%2C%20full%20body%20standing%20pose%2C%20modern%20MBTI%2016personalities%20avatar%20style%2C%20simple%20geometric%20shapes%2C%20soft%20pastel%20palette%2C%20clean%20minimal%20design%2C%20soft%20cel%20shading%20with%20warm%20lighting%2C%20subtle%20texture%2C%20off-white%20cream%20background%2C%20high%20quality%202D%20art&image_size=portrait_4_3"
+ },
+ "0010": {
+ "name": "弦图先觉", "icon": "◇", "icon_bg": "#F5EBE0",
+ "clan": "seer", "clan_name": "直觉者",
+ "mathematician": "赵爽",
+ "keywords": ["直觉", "古典", "独刷", "推演"],
+ "short_desc": "直觉驱动 + 古典底蕴 + 独立钻研 + 无字证明",
+ "description": "你像赵爽一样,是数学世界的视觉诗人。你用一张弦图「看见」勾股定理——不需要推导,看一眼就懂。对你来说,数学不是算出来的,是看出来的。",
+ "tagline": "你用一张图,让中国数学美了一千八百年",
+ "color": "#D4A574",
+ "bio": "赵爽是三国时期吴国数学家,只留下一部《周髀算经注》。但他用一张弦图直观地「看见」了勾股定理——这是中国数学史上最早的无字证明。",
+ "bio_fact": "2002年国际数学家大会的会徽就是他的弦图——一张图比十本书都长寿。",
+ "portrait": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=cute%20flat%20vector%20illustration%2C%20a%20Chinese%20Three%20Kingdoms%20era%20scholar%20in%20simple%20robes%2C%20holding%20a%20brush%20and%20drawing%20a%20geometric%20diagram%2C%20serene%20expression%2C%20full%20body%20standing%20pose%2C%20modern%20MBTI%2016personalities%20avatar%20style%2C%20simple%20geometric%20shapes%2C%20soft%20pastel%20palette%2C%20clean%20minimal%20design%2C%20soft%20cel%20shading%20with%20warm%20lighting%2C%20subtle%20texture%2C%20off-white%20cream%20background%2C%20high%20quality%202D%20art&image_size=portrait_4_3"
+ },
+ "0001": {
+ "name": "拓扑游方", "icon": "◎", "icon_bg": "#D8ECF0",
+ "clan": "seer", "clan_name": "直觉者",
+ "mathematician": "庞加莱",
+ "keywords": ["直觉", "现代", "独刷", "推演"],
+ "short_desc": "直觉驱动 + 前沿视野 + 独立钻研 + 高维漫步",
+ "description": "你像庞加莱一样,拥有超凡的几何直觉。你在公交车踏板上一瞬间「看见」富克斯函数——别人需要推导的地方,你只需「看」一眼。",
+ "tagline": "你在高维空间里漫步,而我们还在二维纸上画图",
+ "color": "#5BA0B5",
+ "bio": "庞加莱是最后一位通晓当时所有数学分支的「全才」。他的几何直觉惊人——据说他刚踏上公交车踏板的一瞬间,就想通了富克斯函数的关键构造。",
+ "bio_fact": "他提出的「庞加莱猜想」困扰了数学家整整100年,直到2003年才被佩雷尔曼证明。",
+ "portrait": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=cute%20flat%20vector%20illustration%2C%20a%20French%20gentleman%20with%20a%20full%20beard%20and%20mustache%2C%20wearing%20a%20dark%20suit%2C%20dreamy%20faraway%20gaze%2C%20full%20body%20standing%20pose%2C%20modern%20MBTI%2016personalities%20avatar%20style%2C%20simple%20geometric%20shapes%2C%20soft%20pastel%20palette%2C%20clean%20minimal%20design%2C%20soft%20cel%20shading%20with%20warm%20lighting%2C%20subtle%20texture%2C%20off-white%20cream%20background%2C%20high%20quality%202D%20art&image_size=portrait_4_3"
+ },
+ "0011": {
+ "name": "人脑计算机", "icon": "⊡", "icon_bg": "#D8EDE0",
+ "clan": "seer", "clan_name": "直觉者",
+ "mathematician": "凯瑟琳·约翰逊",
+ "keywords": ["直觉", "现代", "讲题", "推演"],
+ "short_desc": "直觉驱动 + 前沿视野 + 善于传授 + 经验直觉",
+ "description": "你像凯瑟琳·约翰逊一样,是计算世界的直觉大师。你在NASA的计算纸堆里「看见」弹道不对——你的直觉比任何公式都快,因为你算了33年,算到直觉成了本能。",
+ "tagline": "你把门推开一条缝,直到墙自己倒掉",
+ "color": "#2D6A4F",
+ "bio": "凯瑟琳是NASA「人脑计算机」,用纸笔和计算尺手算弹道。1962年宇航员格伦临发射前点名:「让那个女孩核对数字——她说行,我就飞。」",
+ "bio_fact": "她101岁辞世,从「有色人种计算员」到登月轨道计算者,她的一生恰好就是计算从纸笔走向AI的整个世纪。",
+ "portrait": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=cute%20flat%20vector%20illustration%2C%20an%20elegant%20African%20American%20woman%20in%201960s%20professional%20attire%2C%20holding%20a%20pencil%20and%20calculation%20paper%2C%20confident%20warm%20smile%2C%20full%20body%20standing%20pose%2C%20modern%20MBTI%2016personalities%20avatar%20style%2C%20simple%20geometric%20shapes%2C%20soft%20pastel%20palette%2C%20clean%20minimal%20design%2C%20soft%20cel%20shading%20with%20warm%20lighting%2C%20subtle%20texture%2C%20off-white%20cream%20background%2C%20high%20quality%202D%20art&image_size=portrait_4_3"
+ }
+ },
+
+ "scoring": {
+ "axes": ["style", "purpose", "era", "social"],
+ "questions_per_axis": 3,
+ "max_per_axis": 9,
+ "cutoff": 4,
+ "rule": "每轴3题,每题得分0-3,轴总分0-9。≤4归左极,≥5归右极。",
+ "clan_rule": "style×purpose决定族:style右(严谨)+purpose左(推演)=信仰者,style右+purpose右(算账)=传播者,style左(直觉)+purpose右=应用者,style左+purpose左=直觉者。"
+ }
+}
+
+with open("data/seed_mathbti.json", "w", encoding="utf-8") as f:
+ json.dump(data, f, ensure_ascii=False, indent=2)
+print("OK: seed_mathbti.json generated, 16 results, 4 clans")
diff --git a/scripts/build_story.py b/scripts/build_story.py
new file mode 100644
index 0000000..5a2f9c4
--- /dev/null
+++ b/scripts/build_story.py
@@ -0,0 +1,1010 @@
+"""
+build_story.py — 根据《葫芦侠行》大纲与原文,生成完整的8章互动剧情JSON
+新属性系统:数理根基(shuli)、侠义声望(xiayi)、心志韧性(xinzhi)、好感度(favorability)
+"""
+import json
+
+story = {
+ "title": "葫芦侠行",
+ "author": "葫芦数学",
+ "description": "北宋年间,紫金山下秉公习武堂。你扮演少年葫芦侠,从初入武堂到创立脑学数堂,经历教习、论道、陷害、审查、悟道,最终揭开的真相将超乎你的想象。",
+ "start_node": "ch1_start",
+ "nodes": {}
+}
+
+def N(node_id, scene, character="旁白", sprite="", choices=None):
+ """添加一个故事节点"""
+ story["nodes"][node_id] = {
+ "scene": scene,
+ "character": character,
+ "sprite": sprite,
+ "choices": choices or []
+ }
+
+def C(text, next_node, shuli=0, xiayi=0, xinzhi=0, favorability=None):
+ """创建一个选项"""
+ c = {"text": text, "next": next_node, "effects": {}}
+ if shuli: c["effects"]["shuli"] = shuli
+ if xiayi: c["effects"]["xiayi"] = xiayi
+ if xinzhi: c["effects"]["xinzhi"] = xinzhi
+ if favorability: c["effects"]["favorability"] = favorability
+ return c
+
+# ============================
+# 第一章:初入江湖(1022年秋)
+# ============================
+N("ch1_start",
+ "公元1022年,九月,暑气未消。紫金山麓的秉公习武堂前,你摸了摸刚剃的光头,腰间的大葫芦随着呼吸轻轻晃动。\n\n"
+ "此番远行,你背负着全族人的期盼。故乡泉州乃是东方大港,海贸繁盛,但风浪难测。你要在这天下闻名的学武之地,求得真本领——将那能推演风暴、破译番邦商局、护卫海疆的数理奥义,带回东南海滨的故乡。\n\n"
+ "『欲学神功,先明其志。』秉公习武堂有规矩:新入堂者,首月须剪短头发。你摸了摸自己刚剃的光头,深吸一口气,踏入这座传奇学府。",
+ "葫芦侠", "",
+ [
+ C("先去熟悉环境,四处走走看看", "ch1_explore", xinzhi=3),
+ C("直奔武林大会布告栏,打听最新消息", "ch1_bulletin", xinzhi=5, xiayi=-2),
+ C("找引荐人三力思侠拜访叙旧", "ch1_sanlisi", xiayi=2, favorability={"三力思侠": 10})
+ ])
+
+N("ch1_explore",
+ "你在习武堂里转了一圈。理殿、工殿、文殿三座大殿巍然矗立,各门各派的弟子来来往往。时间广场的日晷下,几个早起的弟子正在晨读。\n\n"
+ "你暗暗记下了几个重要的位置:食堂在西南角,数理门的讲武堂在理殿二层,藏书阁在图文馆东侧。\n\n"
+ "一个月的基本功集训开始了。你发现其他门派的子弟大多性情开朗,很快便三五一伙。而你生性腼腆,在人群中总显得有些格格不入。",
+ "葫芦侠", "",
+ [
+ C("在数理门课堂上主动结识欧阳侠和Eddie侠", "ch1_befriend", shuli=5, xiayi=2, favorability={"欧阳侠": 15, "Eddie侠": 15}),
+ C("独自在角落埋头苦学,不与人交际", "ch1_solo", shuli=8, xiayi=-3)
+ ])
+
+N("ch1_bulletin",
+ "你直奔集市口的布告栏,上面贴着各门各派的课程表。但你最关心的武林大会布告上赫然写着:『因周边突发瘟疫,本届武林大会延期举办。』\n\n"
+ "旁边几个老弟子议论纷纷:「听说隔壁已有数名弟子中招,内力运转滞涩……」「这软筋散似的疫症,不伤性命,专蚀修为,真是歹毒!」\n\n"
+ "你心中一沉。刚来就遇到这种事,但这也让你对习武堂的现状有了更敏锐的认识。",
+ "葫芦侠", "",
+ [
+ C("在数理门课堂上主动结识欧阳侠和Eddie侠", "ch1_befriend", shuli=5, xiayi=2, favorability={"欧阳侠": 15, "Eddie侠": 15}),
+ C("独自在角落埋头苦学", "ch1_solo", shuli=8, xiayi=-3)
+ ])
+
+N("ch1_sanlisi",
+ "你找到了三力思侠的住处。他见到你很是高兴,拉着你坐下喝茶。\n\n"
+ "『葫芦侠啊,你能来,我打心底高兴。』三力思侠笑眯眯地说,『这秉公习武堂,讲究的是止戈为武。最强的武功,不是用来炫耀,而是为了守护。你在这里打下的每一分根基,未来都可能化为让家园安全的一份力量。』\n\n"
+ "他又压低声音:『对了,我有个好友叫厚朴侠,在话剧社。改天带你去看他的戏,那可是一绝!』",
+ "三力思侠", "",
+ [
+ C("在数理门课堂上主动结识欧阳侠和Eddie侠", "ch1_befriend", shuli=5, xiayi=2, favorability={"欧阳侠": 15, "Eddie侠": 15}),
+ C("独自在角落埋头苦学", "ch1_solo", shuli=8, xiayi=-3)
+ ])
+
+N("ch1_befriend",
+ "在「输血分析」的课堂上,你注意到了两位同窗。一位叫欧阳侠,另一位是Eddie侠。此二人听课极为专注,总抢第一排座位。\n\n"
+ "一天清晨,天还未亮,你摸黑赶到讲堂——欧阳侠已在窗下就着晨光翻阅武学典籍,自顾自地比划。旁边的Eddie侠睡眼惺忪,欧阳侠抬手便拍了他一下:『练功当趁早,岂可懈怠!』\n\n"
+ "你鼓足勇气上前:「二位师兄,久仰了。江湖皆传数理门最是枯燥艰深,然一旦悟透,便可立于不败之地。敢问师兄可有诀要?」\n\n"
+ "欧阳侠抢先答道:「数理乃诸武学之基石。学此门,唯有扎实二字。」Eddie侠揉着肩膀接话:「我却真心喜爱数理。天下武功看似纷繁,唯数理之严谨能令我心神沉淀。」\n\n"
+ "一来二去,三人便成了好友。",
+ "欧阳侠 & Eddie侠", "",
+ [
+ C("接受三力思侠的邀请,去看厚朴侠的话剧", "ch1_theater", xiayi=10, favorability={"厚朴侠": 20}),
+ C("婉拒邀约,回宿舍预习功课", "ch1_study", shuli=5, favorability={"厚朴侠": -5})
+ ])
+
+N("ch1_solo",
+ "你选择了独自修炼。每天天不亮就到讲武堂,深夜才回宿舍。你的数理根基日渐扎实,但看着别人三五成群,偶尔也会觉得孤独。\n\n"
+ "一天课后,一个叫Eddie侠的同门主动跟你打招呼:「嘿,我看你天天用功得紧,要不要一起去吃饭?」\n\n"
+ "你犹豫了一下。",
+ "葫芦侠", "",
+ [
+ C("接受邀请,一起去吃饭", "ch1_befriend", xiayi=3, favorability={"Eddie侠": 5}),
+ C("婉拒,继续独自学习", "ch1_theater", shuli=5, xiayi=-5)
+ ])
+
+N("ch1_theater",
+ "三力思侠带你溜出习武堂看话剧。台上演的正是秉公习武堂创立之初的故事——当年初代堂主因场地所限,另一家武堂深明大义,主动提出互换校区,成就武林一段佳话。\n\n"
+ "厚朴侠既是排演主力,又担纲主演,扮相英挺,演技精湛。你第一次对话剧之道有了真切感触。散场后你立刻托三力思侠要到了厚朴侠的住址。\n\n"
+ "不久之后,一只灰鸽落在你的窗台,脚上系着竹管。打开一看——竟是厚朴侠主动邀你到他的小院一叙。",
+ "厚朴侠", "",
+ [
+ C("欣然赴约,在百草园与厚朴侠和山韵侠畅谈话剧", "ch1_houpu_visit", xiayi=10, favorability={"厚朴侠": 15}),
+ C("回去准备功课,改日再访", "ch1_plague_choice", shuli=5)
+ ])
+
+N("ch1_study",
+ "你回到宿舍,翻开《输血分析》的课本。数理门的筑基心法果然艰深——输血分析讲的是比武中迅捷疗伤、回复元气的法门;高等逮术则讲究精准擒拿,专克以身法迅捷见长的对手。\n\n"
+ "你沉下心,一页页啃下去。虽然错过了结识厚朴侠的机会,但你的数理根基打得更加扎实。",
+ "葫芦侠", "",
+ [
+ C("继续努力学习", "ch1_plague_choice", shuli=5)
+ ])
+
+N("ch1_houpu_visit",
+ "你如约来到百草园。厚朴侠的家在一楼,带一个小小庭院。客厅不大,却处处透着雅趣——满墙书架、文房四宝、青花瓷罐里飘出药草清香。\n\n"
+ "一位叫山韵侠的客人已在座,精于音律与文章。三人越聊越投机。山韵侠忽然提议:『我们一起创作一部话剧,将习武心得放入其中,演给大家看,如何?』\n\n"
+ "厚朴侠眼中一亮:「此事大善!小友,你可愿意?」你霍然起身,郑重一揖:「固所愿也,不敢请耳!」三人相视而笑。",
+ "厚朴侠 & 山韵侠", "",
+ [
+ C("加入红灰青蟹,体验温怀一刻", "ch1_qingxie", xiayi=10),
+ C("回宿舍继续用功", "ch1_plague_choice", shuli=5)
+ ])
+
+N("ch1_qingxie",
+ "因你在数理门表现优异,收到了「红灰青蟹」的邀请。这是由几个志趣相投的数理弟子自发结成的小会,每月一次蟹会,品评新奇器械、辩论古怪问题。\n\n"
+ "但最让你触动的是「温怀一刻」——在一个月色清朗的夜晚,几位青蟹成员围坐在喷泉广场,陪伴一位因锻造失败而心灰意冷的锻造门师兄。没有说教,没有安慰的话语,只有安静的倾听和理解的分享。\n\n"
+ "那夜之后,你在这条红灰青三色的绦带上,感受到了一种超越纯粹学术好奇的温暖。",
+ "葫芦侠", "",
+ [
+ C("瘟疫来袭——做出你的选择", "ch1_plague_choice", xinzhi=5, xiayi=5)
+ ])
+
+N("ch1_plague_choice",
+ "布告栏上贴出了鲜红的告示:近期周边突发瘟疫,感染上会让武力大幅下降,武林大会推迟。习武堂劝各位侠客选择返乡避疫,或待在住所不要出门。\n\n"
+ "欧阳侠面色凝重地来找你:「家中有信来催,我恐怕不日便要动身了。」Eddie侠也苦笑:「我是要回家去的。不知何时才能再与二位兄台这般论道了。」\n\n"
+ "你站在二道门前,陷入了两难。",
+ "葫芦侠", "",
+ [
+ C("返乡避疫,保全有用之身(进入【暂别江湖】结局,可重开)", "ch1_ending_leave", xinzhi=-5),
+ C("留守武馆,与同道共同闭关修炼", "ch1_stay", shuli=20, xinzhi=20)
+ ])
+
+N("ch1_ending_leave",
+ "你做出了决定。离开那日,天色阴沉。二道门前不复往日喧嚣,只有寥寥数人默默出行。\n\n"
+ "马车载着你驶离紫金山麓。怀中抱着行囊,仿佛抱着这段短暂却充实的时光。你知道,自己的修行并未结束,只是换了一个场地。故乡的斗室,将成为你临时的闭关之地。\n\n"
+ "【第一章·暂别江湖】\n你的第一个季度的习武之旅,被一场突如其来的瘟疫画上了仓促的逗号。但属于葫芦侠的江湖,才刚刚拉开序幕。\n\n"
+ "——此结局为过渡结局。春暖花开之时,你将再度归来。",
+ "系统", "",
+ [
+ C("春暖花开,重返习武堂(进入第二章)", "ch2_start", shuli=5, xinzhi=5)
+ ])
+
+N("ch1_stay",
+ "你选择了留下。与几位同样留守的同道一起,在习武堂闭关修炼。\n\n"
+ "你翻开《输血分析》和《高等逮术》的课本,沉入数理的深海。没有外界的干扰,你的数理根基以惊人的速度增长。更重要的是,这段共渡难关的经历,让你和留守的同道结下了深厚的情谊。\n\n"
+ "瘟疫终于过去,春天来了。你收拾行囊,准备开始新的季度。听说今年的武林大会即将在开学第一周举行,所有人都摩拳擦掌。\n\n"
+ "【第一章完 · 闭关修炼】",
+ "系统", "",
+ [
+ C("进入第二章:西部教习", "ch2_start", shuli=10, xinzhi=10)
+ ])
+
+# ============================
+# 第二章:西部教习(1023年春)
+# ============================
+N("ch2_start",
+ "瘟疫过去了,转眼又是春天。你从泉州驾着马车回到紫金山。\n\n"
+ "武林大会的新规则公布了:先在每个门进行内部比武,三人一组破阵,赢的那组派代表参加「十七门论道」。\n\n"
+ "数理门的演武场冻得呵气成霜。掌门华师祖在黑板上写了两行字:『连续变化,见微知著;结构规矩,万物皆数。』\n\n"
+ "分组结果:你、Eddie侠、欧阳侠一组,对手是紫铜侠那组。紫铜侠练的是「刚性功法」,招式硬邦邦,从未出过差错。\n\n"
+ "『紫铜侠的完备性证明掌最克咱们这种野路子。』欧阳侠皱眉。Eddie侠已开始画关系图:『需要引入随机变量干扰他的推理链……』",
+ "欧阳侠 & Eddie侠", "",
+ [
+ C("不退反进,用「野路子」扰乱紫铜侠的逻辑链", "ch2_win_match", shuli=5, xiayi=5),
+ C("让Eddie侠和欧阳侠主攻,你负责防守", "ch2_match_normal", shuli=3, xiayi=3)
+ ])
+
+N("ch2_win_match",
+ "『开始!』紫铜侠三人如一体推进,掌风织成密不透风的网——「收敛序列阵」。\n\n"
+ "Eddie侠迎上去,极限剑法专找破绽。欧阳侠大喝一声,双手拇指快速点算——『不行……他们是完备空间,没有孤立点!』\n\n"
+ "你忽然笑了。把葫芦往腰间一挂,不退反进,直接撞进阵里。\n\n"
+ "紫铜侠一愣。就这刹那,你伸手在他左肩拍了一下,右肩拍了一下,蹲下身在左脚鞋面上也拍了一下。\n\n"
+ "他的完备性证明掌需要严密的逻辑链:如果A,则B,则C……可你这毫无章法的三拍,根本不在他的逻辑集里!\n\n"
+ "你趁他卡壳,从缝隙里钻出去,反手一捞——阵旗到手。\n\n"
+ "『承让。师兄,你的武功是完备了,可现实世界从来不完备啊。』",
+ "葫芦侠", "",
+ [
+ C("获胜后向紫铜侠诚恳道歉并解释", "ch2_apologize", xiayi=5, favorability={"紫铜侠": 15}),
+ C("得意地咧嘴大笑", "ch2_watch_lundao", xiayi=-5, favorability={"紫铜侠": -20})
+ ])
+
+N("ch2_match_normal",
+ "你们三人配合默契,经过一番苦战,最终凭借Eddie侠的极限剑法和欧阳侠的精准计算,险胜紫铜侠组。\n\n"
+ "不过你注意到,紫铜侠的完备性证明掌确实名不虚传——若非你们三人齐心协力,单打独斗恐怕不是他的对手。",
+ "葫芦侠", "",
+ [
+ C("观看十七门论道", "ch2_watch_lundao", xiayi=3)
+ ])
+
+N("ch2_apologize",
+ "你走到紫铜侠面前,抱拳道:『紫铜师兄,方才多有得罪。你那套掌法严谨缜密,若非我从旁扰乱,正面交锋我绝非对手。』\n\n"
+ "紫铜侠脸上的涨红渐渐消退,终于叹了口气:『葫芦师弟,你这野路子……倒也让我明白了一件事。真正的武功,不能只活在自己的公理体系里。』他伸出手,与你重重一握。",
+ "紫铜侠", "",
+ [
+ C("观看十七门论道", "ch2_watch_lundao", xiayi=5, favorability={"紫铜侠": 10})
+ ])
+
+N("ch2_watch_lundao",
+ "三天后,图文馆。「十七门论道」——各门派年轻一辈较劲的擂台。\n\n"
+ "数理门派出的是开勒侠。他擅长流形几何,招数变幻莫测。\n\n"
+ "机械门的七柒侠与材料门的人兮侠斗法——机关鸢对上朽材散。外文门的于遥侠与开勒侠展开了一场「文理之辩」——意境与方程的对决。开勒侠用泛函分析和拓扑同伦,将于遥侠的「平沙落雁」招式的意境一一量化拆解。\n\n"
+ "你看着这一幕,心中震动。原来数学语言,本身就是另一种境界的诗。",
+ "旁白", "",
+ [
+ C("武林大会结束后,你感到社交圈子太窄,开始「社牛挑战」", "ch2_social_challenge", xinzhi=5),
+ C("专注修炼,不参与社交", "ch2_teaching_notice", shuli=5)
+ ])
+
+N("ch2_social_challenge",
+ "武林大会的喧嚣退去后,你发现自己在这偌大的习武堂里认识的人屈指可数。\n\n"
+ "Eddie侠从怀中掏出一个小本子——《江湖交际初阶导引》。『我平日观察所得。第一章,破冰式;第二章,递进式……』\n\n"
+ "欧阳侠泼冷水:「人际交往贵在真诚,岂能照本宣科?」\n\n"
+ "Eddie侠不服:『先有章法,再求化境!葫芦侠,你以此为本,定个「社牛挑战」之期。每日主动与一位陌生同门交谈!』\n\n"
+ "第一日,你端着餐盘坐到角落一位同门面前:「咱们是不是常碰面?我叫葫芦侠。」那位同门放下碗筷:「我叫贾锐侠。」——他跟你聊起了「心性四路分说」和西域传来的「MBTI」心性鉴识法。\n\n"
+ "借助MBTI大法,你开始结识一群朋友。",
+ "Eddie侠 & 贾锐侠", "",
+ [
+ C("趁课余去姑苏城游玩,践行社牛挑战", "ch2_gusu", xiayi=10, favorability={"欧阳侠": 5, "Eddie侠": 5}),
+ C("留在习武堂继续深研MBTI", "ch2_mbti", xinzhi=5)
+ ])
+
+N("ch2_gusu",
+ "姑苏城的春,浸在湿漉漉的柳烟里。你、欧阳侠、Eddie侠和鸣君侠一起出游。\n\n"
+ "目标一:石桥下。你鼓起勇气将一束栀子花送给疲惫的驿卒。那驿卒愣住了,继而挠头笑了起来:『跑码头这么多年,头回有人给俺送花。』\n\n"
+ "目标二:狮子林池畔。你为几位大妈「丹青速写」,用一本正经的术语逗得她们笑开了花,还获赠一包姑苏糕点。\n\n"
+ "回到习武堂后,你身上多了几分由内而外的松快。",
+ "葫芦侠", "",
+ [
+ C("写信给自己的领课师长李教习,请教前路", "ch2_li_jiaoxi", xiayi=10, xinzhi=5),
+ C("回宿舍继续日常修炼", "ch2_teaching_notice", shuli=5)
+ ])
+
+N("ch2_mbti",
+ "你跟贾锐侠深入探讨了「心性四路分说」与西域MBTI的相通之处。你把不同门派的武功风格按脾性分类,找出了一套帮助同门找到最适合修炼方向的参考系统。\n\n"
+ "虽然这套方法还很粗糙,但已经帮不少同门理清了修炼思路。你的名声在小范围内开始流传。",
+ "贾锐侠", "",
+ [
+ C("写信给李教习请教前路", "ch2_li_jiaoxi", xiayi=5, xinzhi=5)
+ ])
+
+N("ch2_li_jiaoxi",
+ "信送出不到一日,一只青灰色信鸽落在你窗台,素笺上只有一行字:『鼎新楼三层西厢,明日巳时,静候一叙。』\n\n"
+ "李教习年约四旬,面容清癯。他煮好茶,听你将自己的困惑一一道出。\n\n"
+ "『葫芦侠,若只求在堂内大小比中位列前三分之一,不难。若志不止于此,欲窥顶尖风光,则需明白——那不再是「习得」,而是「修炼」。修炼是将外传的功法化为内生的本能,需要你跳出为学武功而学武功的桎梏,去更广阔的天地寻找养分。』\n\n"
+ "『你信中提到结识各门朋友,此为大善!真正的顶尖高手,其武功早已超越门派招式之限,乃其全部学识、阅历、心性融合后自然流淌出的东西。』\n\n"
+ "一席话,如阳光驱散了你心中许多混沌。路,忽然广阔了许多。",
+ "李教习", "",
+ [
+ C("看到「常青藤西部教习队」的招募告示,报名参加", "ch2_apply_teaching", xiayi=10, xinzhi=10),
+ C("留在习武堂继续深造", "ch2_chapter_end_a", shuli=15)
+ ])
+
+N("ch2_teaching_notice",
+ "布告栏上贴出了新告示:『常青藤西部教习队,招人。』\n\n"
+ "几个江湖名门大派凑的联盟,派得力弟子去西部偏远地方帮乡学武馆开蒙讲课。这既是历练,也是积德的好事。\n\n"
+ "你心里「咚」地跳了一下。西部,完全不一样的地方。教小孩练武,更是从没干过的事。想去见识的念头和怕自己干不来的担心,在心里来回拉扯。",
+ "葫芦侠", "",
+ [
+ C("鼓起勇气,报名参加教习队", "ch2_apply_teaching", xiayi=10, xinzhi=10),
+ C("留在习武堂继续深造", "ch2_chapter_end_a", shuli=15)
+ ])
+
+N("ch2_apply_teaching",
+ "第一轮面试,你呈上了那本《葫芦瞎说》——你平时自己瞎写的练功心得、招式图解、食堂哪个菜好吃都记在里面。女教习翻了几页,点点头:『不算正经武功秘籍,倒看得出你是个肯留心的人。回去等信。』\n\n"
+ "第二轮面试,对面坐着两个人。正领队孔锝侠,身材笔挺,脸板得挺严,目光跟哈士奇似的。副领队星侠,气质温和,总是带着点笑的美女子。\n\n"
+ "星侠问你:『如果让你去教一个从没站过桩的孩子,怎么让他明白扎马步的要义?』\n\n"
+ "你想了想:『让他想象自己是一张四条腿的凳子。凳子能站稳,是因为四条腿均匀撑在地上。先不求蹲多低,先求像凳子一样,推一下都不容易晃。』\n\n"
+ "星侠的眼睛亮了。三天后,名单贴出来——你的名字,在上面。",
+ "孔锝侠 & 星侠", "",
+ [
+ C("按时到材料楼集合", "ch2_meeting", xinzhi=10, shuli=5)
+ ])
+
+N("ch2_meeting",
+ "辰时集合。你气喘吁吁踏入沁心厅——却发现自己看错了时辰,迟到了一刻。\n\n"
+ "孔锝侠面沉如水:『辰时集合。你抬头看看,日晷影子现在何处?』你脸上一热,忙躬身认错。\n\n"
+ "星侠温言打破了凝滞的气氛。十四位队员依次报上名号——羽昕侠、帽圆侠、千辰侠、三水侠、南羽侠、气玄侠、人兮侠、七柒侠、于瑶侠、喆航侠、寒境侠……加上你,汇聚了工、理、文三殿之秀。\n\n"
+ "孔锝侠宣读各职:你被分配到了笔墨组,负责对外宣介和行程记录。",
+ "孔锝侠 & 星侠", "",
+ [
+ C("诚恳认错,保证不再犯", "ch2_chengken", xinzhi=5, favorability={"孔锝侠": 5}),
+ C("辩解是因为看错了时辰", "ch2_bianjie", xiayi=-5, favorability={"孔锝侠": -10})
+ ])
+
+N("ch2_chengken",
+ "你深深鞠躬:「弟子疏忽,请队长责罚。今后必以此为戒。」\n\n"
+ "孔锝侠的目光在你脸上停了一瞬,微微点头。星侠趁机打圆场,气氛缓和下来。",
+ "孔锝侠", "",
+ [
+ C("赶赴西南六都,开始教习之旅", "ch2_liudu_arrive", xinzhi=3)
+ ])
+
+N("ch2_bianjie",
+ "『回队长,今晨晨雾太大,弟子看错了时辰……』\n\n"
+ "孔锝侠的眉头皱得更深了。『看错时辰?那你为何不提前出发?借口再多,也掩不住不守时的本质。』\n\n"
+ "你在众人同情的目光中红着脸坐下,暗下决心要挽回印象。",
+ "孔锝侠", "",
+ [
+ C("赶赴西南六都", "ch2_liudu_arrive", xinzhi=-3)
+ ])
+
+N("ch2_liudu_arrive",
+ "七月流火,十四人的教习队离开紫金山,车马兼程逾十日,终于抵达黔地六都镇。\n\n"
+ "馆主方潘候在门前,眼神锐利如鹰。他打量了大家一番,忽然开口:『久闻紫金山武功讲究根基扎实。方某是个粗人,不信虚言,只信拳头。孔队长,可敢搭把手?』\n\n"
+ "孔锝侠神色不变,上前一步。两人在武馆门前交手二十余招——方馆主攻势猛恶,大开大合;孔锝侠严谨缜密,以材料门「白鹫手中引铁分流」的精妙招数应对。\n\n"
+ "方馆主收势后退,哈哈一笑:『好!紫金山高徒,名不虚传!』第一关,过了。\n\n"
+ "你负责的「四段」第一堂特色课——「侠义精神的来源」。你照本宣科讲了一个时辰,孩子们昏昏欲睡。一个胆子大些的男孩小声嘀咕:『这先生讲的啥?真没劲……』",
+ "方馆主 & 孔锝侠", "",
+ [
+ C("课后反思,向队长和队友请教改进教学方法", "ch2_reflect", xinzhi=10, shuli=5),
+ C("自暴自弃,觉得教小孩比练武还难", "ch2_giveup", xinzhi=-10, xiayi=-5)
+ ])
+
+N("ch2_reflect",
+ "当晚的队内小结会上,你红着脸汇报了首课受挫的情况。出人意料,遭遇类似困境的不止你一人。\n\n"
+ "星侠沉吟道:『教学如果脱离了孩子们熟悉的生活,就很难俘获他们的信任。』\n\n"
+ "你大胆开口:『我有一个想法。与其干讲,不如让学生来试演。选取武林轶事片段,让他们扮演其中人物。我们称之为「试演擂」!』\n\n"
+ "星侠眼睛一亮:『这名字好!胜出者可参与排演一出完整小戏。』\n\n"
+ "你心中早有雏形:『我们可以新编一个故事——《桃源侠影录》。借陶渊明《桃花源记》的架子,讲几个厌倦江湖的游侠误入无忧谷,最终醒悟侠之道不在避世,而在入世行侠。』\n\n"
+ "孔锝侠沉吟片刻:「故事尚可,然需切记,须有武德教化之意。葫芦侠,你来负责此事。」",
+ "星侠 & 孔锝侠", "",
+ [
+ C("全力以赴排练《桃源侠影录》", "ch2_rehearsal", xiayi=10, shuli=5)
+ ])
+
+N("ch2_giveup",
+ "你心里又空落落的。这些大山的孩子们根本听不懂你那套紫金山课堂上的东西。\n\n"
+ "但第二天,星侠单独找到你,跟你分享了她第一次支教时的糗事——她当时教的是诗词鉴赏,孩子们对「明月松间照」毫无共鸣。\n\n"
+ "『后来我发现,不是孩子们的问题。是我们没有找到跟他们对话的语言。』\n\n"
+ "你恍然大悟。",
+ "星侠", "",
+ [
+ C("重新振作,策划「试演擂」", "ch2_reflect", xinzhi=5, xiayi=3)
+ ])
+
+N("ch2_rehearsal",
+ "三日后,「试演擂」在武馆后院开场。孩子们都觉得新奇。\n\n"
+ "你特别注意观察那几个「捣蛋四人组」——他们果然没让人失望。四人竟有模有样地分起角色,虽然漏洞百出,但那份机灵劲儿和夸张的肢体语言,逗得所有人前仰后合。\n\n"
+ "就是他们了。你哈哈大笑。\n\n"
+ "当晚你挑灯夜战,将剧本写成。那无忧谷中只知演练花架式的居民,是对脱离实际空谈理想的隐喻。你希望孩子们能有更多勇气跳出当前的环境,经世济民。\n\n"
+ "排练期间,中央翰林院画师来访、马拉病毒爆发、队员内部争执……你经历了这一切,但《桃源侠影录》的排练始终没有中断。",
+ "葫芦侠", "",
+ [
+ C("马拉病毒爆发,孔锝侠病倒——你如何表态?", "ch2_virus", xinzhi=5, shuli=5)
+ ])
+
+N("ch2_virus",
+ "一种叫「马拉病毒」的疫病传入六都镇。孔锝侠因外出采买不幸中招,高烧不退。\n\n"
+ "队内也爆发了矛盾:气玄侠认为应加强锻炼增强体魄,七柒侠则认为应加强纪律。两人在后院动起手来。星侠及时赶到,以一番沉稳的话语平息了争执。\n\n"
+ "接着,秉公习武馆的张教习前来巡视,一席话如春风化雨:『教习教习,教的不仅是武功学问,更是心性。我们来此,是要在孩子们心里种下一颗种子。』",
+ "星侠 & 张教习", "",
+ [
+ C("支持气玄侠——加强锻炼", "ch2_teaching_success", xinzhi=5, favorability={"气玄侠": 10, "七柒侠": -5}),
+ C("支持七柒侠——加强纪律", "ch2_teaching_success", xiayi=5, favorability={"七柒侠": 10, "气玄侠": -5}),
+ C("提出折中方案(需心志韧性≥15)", "ch2_teaching_success", xiayi=10, xinzhi=5, favorability={"气玄侠": 5, "七柒侠": 5})
+ ])
+
+N("ch2_teaching_success",
+ "教学成果观摩会上,《桃源侠影录》成功演出。孩子们卖力的表演让方馆主都瞪大了眼。\n\n"
+ "离别时,孩子们依依不舍。那个叫阿鑫的男孩跑过来,眼眶红红的:『先生,你们还会回来吗?』\n\n"
+ "你蹲下身,摸了摸他的头。",
+ "葫芦侠", "",
+ [
+ C("赠送手抄《葫芦瞎说》给阿鑫", "ch2_gift", xiayi=15, xinzhi=5),
+ C("口头鼓励,承诺以后会回来", "ch2_oral", xiayi=5)
+ ])
+
+N("ch2_gift",
+ "你从行囊中取出那本已经翻得卷边的《葫芦瞎说》手抄本,塞到阿鑫手里。\n\n"
+ "『这里面有老师瞎写的练功心得,还有些好玩的数理游戏。等你长大了,说不定能用上。』\n\n"
+ "阿鑫紧紧抱着那本子,用力点头。\n\n"
+ "后来,你真的收到了阿鑫的信——他说他考上了县里的中学,那本《葫芦瞎说》他一直留着。",
+ "阿鑫", "",
+ [
+ C("进入第三章", "ch3_start", xiayi=5, xinzhi=5)
+ ])
+
+N("ch2_oral",
+ "『老师以后一定会回来看你们的。你要好好练功,将来走出大山。』\n\n"
+ "阿鑫用力点头,眼泪在眼眶里打转。\n\n"
+ "马车启程,六都镇在身后渐渐变小。但你心里知道,这段经历在你心里种下的种子,比你在孩子们心里种下的还要深。",
+ "葫芦侠", "",
+ [
+ C("进入第三章", "ch3_start", xiayi=3)
+ ])
+
+N("ch2_chapter_end_a",
+ "你选择留在习武堂继续深造。这一季度你把全部精力投入到数理功法的修炼中。虽然错过了西部教习的经历,但你的数理根基更加扎实了。\n\n"
+ "你听说了常青藤教习队的种种故事——孔锝侠的严谨、星侠的温和、孩子们在《桃源侠影录》演出时的笑脸。心里隐隐有些遗憾。\n\n"
+ "不过你知道,每个人都有自己的路。",
+ "葫芦侠", "",
+ [
+ C("进入第三章", "ch3_start", shuli=15)
+ ])
+
+# ============================
+# 第三章:数理论道(1023年秋)
+# ============================
+N("ch3_start",
+ "回到秉公习武堂,武林大会新规发布:以堂口为单位参赛,需展示「普惠江湖的新鲜武学」。\n\n"
+ "你在布告栏前遇到一位面容清俊、气质温润的同门。他自称玎铧侠,从袖中取出一个青布锦囊:『此乃一位隐居前辈早年所赐,嘱我转交于有缘之后进。若他日遭遇困厄,可打开一观。』\n\n"
+ "他不再多言,飘然而去。",
+ "玎铧侠", "",
+ [
+ C("当场打开锦囊——发现《统计学导论》残页", "ch3_open_jinnang", shuli=20),
+ C("收好锦囊,日后再说", "ch3_keep_jinnang", xinzhi=5),
+ C("拒绝接受——这太可疑了", "ch3_refuse_jinnang", xinzhi=-5)
+ ])
+
+N("ch3_open_jinnang",
+ "你当场解开锦囊——一本薄薄的手抄册子,《统计学导论》。\n\n"
+ "其中分门别类探讨了概率先验与后验之辩在料敌中的应用、大量随机事件中稳定趋势之本质、由果推因之最大可能性原则……\n\n"
+ "你心跳加速。这是从武道修行视角深入浅出诠释统计核心思想的秘籍纲要!虽然引起了一些人的注意,但你获得了先机。",
+ "葫芦侠", "",
+ [
+ C("着手组建脑学数堂参赛团队", "ch3_build_team", shuli=10)
+ ])
+
+N("ch3_keep_jinnang",
+ "你将锦囊收入怀中。玎铧侠的话在你心中留下了一个谜团,但眼下更重要的事是武林大会。\n\n"
+ "你决定先把精力集中在组建团队上。",
+ "葫芦侠", "",
+ [
+ C("着手组建脑学数堂参赛团队", "ch3_build_team", shuli=5)
+ ])
+
+N("ch3_refuse_jinnang",
+ "『多谢师兄好意,但无功不受禄。』你婉拒了锦囊。\n\n"
+ "玎铧侠眼中闪过一丝难以察觉的遗憾,收回锦囊,拱手告辞。\n\n"
+ "你失去了一个重要道具,但保持了谨慎。",
+ "玎铧侠", "",
+ [
+ C("着手组建脑学数堂参赛团队", "ch3_build_team")
+ ])
+
+N("ch3_build_team",
+ "要参赛需要组建核心团队。你需要三水侠画展报、薄文侠写文案、甲亢侠设计展台、圣文侠做数据分析……\n\n"
+ "同时,你的「社牛挑战」已经升级——你开始深入学习MBTI并应用于社交。在食堂,你遇到了更多人。",
+ "葫芦侠", "",
+ [
+ C("精挑细选,宁缺毋滥——核心团队强大", "ch3_elite_team", shuli=10, xiayi=-3),
+ C("广撒网,重数量——人多力量大", "ch3_broad_team", xiayi=10, shuli=-3)
+ ])
+
+N("ch3_elite_team",
+ "你精挑细选了几位核心成员。甲亢侠——对图形结构异常敏感;圣文侠——痴迷于大样本统算之学;三水侠——丹青妙笔;薄文侠——文采斐然。\n\n"
+ "虽然人手不多,但每个人都在自己领域出类拔萃。缺点是筹备期间捉襟见肘。",
+ "葫芦侠", "",
+ [
+ C("为拓宽人脉,进行「姑苏城出游」", "ch3_gusu_trip", xiayi=5)
+ ])
+
+N("ch3_broad_team",
+ "你广发英雄帖,招募了十几位成员。卡卡侠、溜溜侠、可可侠、书宣侠、陈湘侠……人才济济。\n\n"
+ "人多力量大,各方面都有人手。但团队成员水平参差不齐,核心战斗力不如精挑细选的团队。",
+ "葫芦侠", "",
+ [
+ C("姑苏城出游", "ch3_gusu_trip", xiayi=5)
+ ])
+
+N("ch3_gusu_trip",
+ "你和欧阳侠、Eddie侠、鸣君侠同游姑苏。街头送花给驿卒、帮大妈「丹青速写」,你的社牛挑战在陌生环境中也得到了检验。\n\n"
+ "回到习武堂后,你拜访了李教习。他指点你:「修炼」与「习得」的区别。『真正的顶尖高手,早已超越门派招式之限,乃其全部学识、阅历、心性融合后自然流淌出的东西。』",
+ "李教习", "",
+ [
+ C("接受建议,去更广阔天地寻找养分——开启跨学科学习", "ch3_cross_discipline", shuli=5, xinzhi=5),
+ C("专注数理门内部深造", "ch3_competition_prep", shuli=10)
+ ])
+
+N("ch3_cross_discipline",
+ "你开始频繁拜访其他堂口。脑机堂的王长老向你阐述了「灵犀链」与数理的关系——『脑子的用处,就在连和译。连接内外世界,翻译神思意念,变成具体的命令。数理,就是那座最靠谱的连接桥梁。』\n\n"
+ "图象堂的白维侠向你展示了精巧的密文编译体系。经管门的于博侠和网络侠帮你理清了数堂的账目规划。\n\n"
+ "你的视野一下子打开了。",
+ "王长老", "",
+ [
+ C("武林大会预赛——对阵紫铜侠", "ch3_competition_prep", shuli=10, xiayi=5)
+ ])
+
+N("ch3_competition_prep",
+ "常青藤二次招募的消息传来。孔锝侠来信邀请你再次西行。但武林大会在即,脑学数堂刚刚起步。\n\n"
+ "你面临抉择。",
+ "葫芦侠", "",
+ [
+ C("婉拒孔锝侠,留守备战武林大会", "ch3_stay_competition", shuli=5, favorability={"孔锝侠": -10}),
+ C("接受邀请,再次西行——但可能错过武林大会", "ch3_go_west_again", xiayi=10, favorability={"孔锝侠": 15})
+ ])
+
+N("ch3_stay_competition",
+ "你选择了留守。孔锝侠虽然有些失望,但理解你的决定。\n\n"
+ "你全力以赴筹备武林大会。三水侠和雅庭侠合力绘制了气势磅礴的《数理报国图》,薄文侠写出文采飞扬的《数理·报国说》,甲亢侠搭建了令人瞠目的几何分形展台。\n\n"
+ "决赛那天,你站在武术交流中心中央,面对台下黑压压的江湖名宿,沉声道:『今日,我脑学数堂在此,所呈之技,乃以数理为万业根基,志在报国的全新武学路径!』",
+ "葫芦侠", "",
+ [
+ C("进入第四章", "ch4_start", shuli=10, xiayi=10, xinzhi=10)
+ ])
+
+N("ch3_go_west_again",
+ "你再次踏上西行之路。这一次你已经是熟手,在教习中发挥了更大的作用。\n\n"
+ "但你错过了武林大会的精彩对决。当你回到紫金山时,听说了脑学数堂在大会上的表现——虽未夺冠,但获得了「最佳报国创新大奖」。\n\n"
+ "你为堂口的伙伴们骄傲,也为自己的缺席感到遗憾。",
+ "葫芦侠", "",
+ [
+ C("进入第四章", "ch4_start", xiayi=15, shuli=5)
+ ])
+
+# ============================
+# 第四章:脑学初创(1023年冬)
+# ============================
+N("ch4_start",
+ "你获得了一块沉甸甸的乌木令牌,上面刻着八个朴拙大字:『准设辅堂,试行一期』。\n\n"
+ "你正式创办「脑学数堂」——这不止是一间讲堂,更是一座灯塔、一处熔炉、一道桥梁。\n\n"
+ "羽昕侠带你去看了场地——地下石室。初极狭,才通人;复行数十步,豁然开朗。岩壁干燥,竟无寻常地穴的潮湿之气,反而有微风流动。『此地本是私用,既然你有心做这番事业,便借与你。』",
+ "羽昕侠", "",
+ [
+ C("接受地下石室——获得隐蔽根据地", "ch4_accept_cave", xinzhi=5),
+ C("另寻他处——耗时耗力", "ch4_other_place", xinzhi=-5)
+ ])
+
+N("ch4_accept_cave",
+ "你接受了地下石室。三水侠在岩壁上绘制了巨幅壁画——浑天仪转动、《九章算术》展开、祖冲之计算圆周率。于瑶侠赠与你三口樟木箱的藏书,包括失传已久的《武经算要》。帽圆侠利用人脉为你争取了藏书阁、观星台的资源。\n\n"
+ "你在主室入口亲手刻下:『格物致知,数以究理;武以载道,道法自然。』",
+ "三水侠 & 于瑶侠", "",
+ [
+ C("招新大会——招募核心成员", "ch4_recruitment", xiayi=10, shuli=5)
+ ])
+
+N("ch4_other_place",
+ "你另寻场地,耗费了大量时间和精力。最终在一处偏僻角落找到了一间废弃仓库。虽然不如地下石室隐蔽,但也勉强可用。\n\n"
+ "只是浪费的时间让你错过了最佳招新时机。",
+ "葫芦侠", "",
+ [
+ C("招新大会", "ch4_recruitment", xiayi=5)
+ ])
+
+N("ch4_recruitment",
+ "招新大会上,各路奇才汇聚。\n\n"
+ "甲亢侠——对图形结构异常敏感;可可侠——精通西域数理,暗合刘维尔定理;薄文侠——以诗词歌赋记忆复杂数据;书宣侠——丹青妙手兼画疗师;陈湘侠——追求速度的轻功少年;圣文侠——痴迷大样本统算之学。\n\n"
+ "最引人注目的是——一位骑着黑色巨犬、面覆轻纱的女子径直闯入会场。她拔开瓷瓶喝下大半,面不改色地将瓶子递给你:「老鼠药。验过,真的。」『我,溜溜侠。懂一点柯西定理,也会驯犬。能加入么?』",
+ "溜溜侠", "",
+ [
+ C("录取溜溜侠——获得强力队友,但可能引狼入室", "ch4_accept_liuliu", xiayi=5, favorability={"溜溜侠": 10}),
+ C("拒绝——这人太可疑了", "ch4_refuse_liuliu", xiayi=-5)
+ ])
+
+N("ch4_accept_liuliu",
+ "你接过瓶子,嗅了嗅——确实是剧毒鼠药。此女胆大妄为,但对柯西定理这种深奥数理知识的提及显示了她的不寻常。\n\n"
+ "你最终点头:「欢迎加入。」溜溜侠接过绦带,牵着犬走到树荫下,不再言语。\n\n"
+ "后来你才知道,她的真实身份远超你的想象。",
+ "葫芦侠", "",
+ [
+ C("与甲亢侠讨论堂口发展方向", "ch4_jiakang_debate", xiayi=3)
+ ])
+
+N("ch4_refuse_liuliu",
+ "你摇了摇头:「姑娘,我们数堂虽然不拘一格,但也不必以服毒证明。请回吧。」\n\n"
+ "溜溜侠一言不发,翻身上犬,消失在人群中。\n\n"
+ "你后来才知道,你拒绝了一个可能改变一切的关键人物。",
+ "葫芦侠", "",
+ [
+ C("与甲亢侠讨论堂口发展方向", "ch4_jiakang_debate", xiayi=-3)
+ ])
+
+N("ch4_jiakang_debate",
+ "甲亢侠从西南教习队归来后,与你在发展路径上发生了激烈碰撞。\n\n"
+ "甲亢侠主张务实赚钱:「堂口当前重心应全力承接实务——清丈田亩、核算仓库!来钱快,见效快!」\n\n"
+ "你主张理论研究:「脑学数堂的立身之本在于学理!若一味追求短平快的实务,便失了我们的理论特色!」\n\n"
+ "两人各执一词,昔日的默契出现了清晰的裂痕。",
+ "甲亢侠", "",
+ [
+ C("坚持理论研究", "ch4_insist_theory", shuli=10, xiayi=-3),
+ C("妥协务实", "ch4_compromise", xiayi=5, shuli=-5),
+ C("两者兼顾——需高心志韧性和高侠义声望", "ch4_balance", shuli=5, xiayi=5, xinzhi=5)
+ ])
+
+N("ch4_insist_theory",
+ "你坚持了理论路线。脑学数堂的基础研究稳步推进,但资金确实越来越紧张。\n\n"
+ "甲亢侠虽然不认同,但依然留在堂中,只是脸上的笑容少了很多。",
+ "葫芦侠", "",
+ [
+ C("欧阳侠从新大陆归来", "ch4_ouyang_return", shuli=10)
+ ])
+
+N("ch4_compromise",
+ "你向务实派妥协,承接了大量丈量田亩、核算仓库的实务。短期收益确实缓解了资金压力。\n\n"
+ "但你也隐隐觉得,脑学数堂正在偏离初心——从改变江湖对数理认知的理想,滑向了普通的账房先生。",
+ "葫芦侠", "",
+ [
+ C("欧阳侠从新大陆归来", "ch4_ouyang_return", xiayi=5)
+ ])
+
+N("ch4_balance",
+ "那晚你独上紫金山后崖。甲亢侠已经坐在那里,对着山下的城墙出神。\n\n"
+ "沉默许久,甲亢侠开口:「在六都,有个孩子问我——先生,你教的勾股定理,除了量房子,还能做什么?也许最高的道理,就藏在最寻常的运用里。」\n\n"
+ "你也在他身边坐下:「基础研究不能停。但同时,我们的业务也需要扩大。从实践中发现问题,再带回堂中研究,形成理论,再用理论指导新的实践。」\n\n"
+ "两人相视,分歧在更高一层的共识下消融。翌日,你们宣布了新的方略:成立研理与务用两部。",
+ "甲亢侠", "",
+ [
+ C("欧阳侠从新大陆归来", "ch4_ouyang_return", shuli=10, xiayi=10, xinzhi=10)
+ ])
+
+N("ch4_ouyang_return",
+ "阔别两年有余的欧阳侠回来了。他被海风镀成了深铜色,行囊里装满了海外手稿。\n\n"
+ "『那边的计算门,是一个全新的世界!他们用金属和琉璃制成的盒子,能在一息之间完成我们数日的复杂计算!』欧阳侠眼中燃烧着炽热的光芒。\n\n"
+ "他抓住你和Eddie侠的手臂:『跟我走!我的船队三个月后启航!』\n\n"
+ "Eddie侠斜倚在墙边,铜钱飞速旋转,眼中渐渐泛起悸动。\n\n"
+ "而你,望向石室墙上那幅《数理报国图》。",
+ "欧阳侠", "",
+ [
+ C("婉拒——你的根已扎在这里", "ch4_refuse_ouyang", xinzhi=10, xiayi=5),
+ C("接受——与欧阳侠一同出海(进入出海远航线)", "ch4_go_sailing", xiayi=15, favorability={"欧阳侠": 20})
+ ])
+
+N("ch4_refuse_ouyang",
+ "『欧阳,你向往的是大海星辰,我敬佩。但我选择的,是脚下这条更泥泞的路。让这里的数理不止存在于海外奇谈,也能真正造福此地的山河百姓。』\n\n"
+ "欧阳侠眼中的炽热稍稍降温,拍了拍你的肩膀:『我懂。人各有志。只是……可惜了。』\n\n"
+ "Eddie侠最终也决定留下。『那边竞争太大,江湖太难。我还是觉得这里自在。』\n\n"
+ "三个月后,码头上帆樯如林。你与欧阳侠的拳头重重一碰:『你在故土披荆斩棘,我在异域开疆拓土。保重。』",
+ "欧阳侠 & Eddie侠", "",
+ [
+ C("进入第五章", "ch5_start", xinzhi=10, xiayi=10)
+ ])
+
+N("ch4_go_sailing",
+ "你决定随欧阳侠出海。新大陆的一切让你目不暇接——那里的计算门、互联网、能飞行的铁鸟……\n\n"
+ "你在那里开创了一番事业,成为东西方数理交流的桥梁。但你时常想起紫金山下的那些面孔——三水侠的画、甲亢侠的争论、溜溜侠的巨犬……\n\n"
+ "【出海远航结局线】\n你选择了广阔的世界,但故乡的根永远在心中。",
+ "系统", "",
+ [
+ C("重新开始", "ch1_start")
+ ])
+
+# ============================
+# 第五章:报国之路(1024年春)
+# ============================
+N("ch5_start",
+ "公元1024年,紫金山上的残雪未消,武林大会总决赛在即。\n\n"
+ "今年的规矩变了——以「堂口」为单位参赛,比的是谁能拿出「普惠江湖的新鲜武学」。不仅要展示功夫,还要画展报、在天下名宿跟前路演。\n\n"
+ "你想起去年冬天周大长老的话:『多想想,你们的数理功夫,能给这世道、给我大宋的百姓,解决什么实在的难题。上报国家,下安黎庶。』\n\n"
+ "你在新学期第一次堂会上,将乌木令牌郑重放在长桌中央:『今年武林大会,咱脑学数堂,就奔着八个字去——学习数理,报效国家!』",
+ "葫芦侠", "",
+ [
+ C("全力筹备展报", "ch5_prepare_exhibition", shuli=5, xiayi=5)
+ ])
+
+N("ch5_prepare_exhibition",
+ "三水侠与雅庭侠合力绘制《数理报国图》——大宋万里江山之上,数理脉络如金色血脉蔓延。\n\n"
+ "薄文侠写出《数理·报国说》。甲亢侠搭建了几何分形展台。圣文侠日以继夜拟合破绽回归线。卡卡侠、溜溜侠、可可侠的「三才阵」在石室中迸发出令灵魂战栗的威压——柯西之绝对守域、刘维尔之永恒定界、黎曼之无穷变幻,攻防一体!\n\n"
+ "谢长老与戴长老也被惊动,在参赛文书上盖下了数理门的印章。",
+ "三水侠 & 甲亢侠", "",
+ [
+ C("孔锝侠来信邀你再次西行", "ch5_kongde_letter", xinzhi=5)
+ ])
+
+N("ch5_kongde_letter",
+ "一只信鸽带来了孔锝侠的亲笔信,希望你再赴六都。\n\n"
+ "你在两难中挣扎。喆航侠把你拽上赤金宝马,在山中狂奔了三天。\n\n"
+ "『葫芦,知道什么是真正的好马不?好马不是啥路都想蹚,是认准了一条道,就把眼蒙上、心定死,豁出命跑到黑!你现在心太贪,恨不得把全天下的担子都挑自己肩上。可你数堂这条船刚离岸,你这掌舵的要是自己先跳了水,船非沉了不可!』\n\n"
+ "高崖上的烈风吹醒了你。",
+ "喆航侠", "",
+ [
+ C("婉拒孔锝侠,留守备战——但你将常青藤精神写入堂训", "ch5_stay_for_competition", shuli=10, xiayi=20, xinzhi=10, favorability={"孔锝侠": 15}),
+ C("接受邀请,再次西行", "ch5_go_west", xiayi=15, shuli=-5, favorability={"孔锝侠": 20})
+ ])
+
+N("ch5_stay_for_competition",
+ "你来到材料楼,对孔锝侠深深一躬:「孔队,弟子此番不能随队西行。但奉献不分地界远近!留在紫金山,扎深数理的根,用理性和逻辑培养能上报国家的数理人才,一样是奉献!从今日起,常青藤精神就是我脑学数堂立堂的脊梁骨!」\n\n"
+ "孔锝侠那张常年严肃的脸,如春冰化冻,彻底舒展开来:「好!记住你今夜这番话。只要心里装着家国天下,无论你在何处,都是我常青藤走出来的顶天立地的傲骨!」\n\n"
+ "武林大会决赛。你站在武术交流中心中央,面对台下黑压压的江湖名宿,演示了三项绝技:圣文侠的大样本破绽回归步、蛛丝数据抓取掌、卡卡侠等三人的三才阵。\n\n"
+ "全场震撼。但最终,脑学数堂不敌机械门、经管门等底蕴深厚的门派。你获得了「最佳报国创新大奖」。",
+ "孔锝侠 & 葫芦侠", "",
+ [
+ C("坦然接受,总结经验", "ch5_accept_loss", xinzhi=15, xiayi=10),
+ C("愤愤不平", "ch5_angry_loss", xinzhi=-10, xiayi=-5)
+ ])
+
+N("ch5_go_west",
+ "你再次踏上西行之路。虽然错过了武林大会的精彩对决,但在六都你又有了新的收获。\n\n"
+ "当你回来时,听说了脑学数堂获得了「最佳报国创新大奖」。你为未能与堂口同袍并肩作战而感到遗憾。",
+ "葫芦侠", "",
+ [
+ C("进入第六章", "ch6_start", xiayi=10)
+ ])
+
+N("ch5_accept_loss",
+ "你在堂会上说:『今日之败,非是败在数理不如人,而是败在时间不如人。但我们已经证明了一件事——数理是有用的,是能报国的!这条路,走对了。』\n\n"
+ "众人眼中重新燃起光芒。\n\n"
+ "但你没料到的是——一场暴风雨正在暗处酝酿。",
+ "葫芦侠", "",
+ [
+ C("进入第六章", "ch6_start", xinzhi=15, xiayi=10)
+ ])
+
+N("ch5_angry_loss",
+ "你愤愤不平:「我们准备了那么久!那些老古板根本不懂数理的价值!他们只是看不起新东西!」\n\n"
+ "堂中气氛沉闷。一些人开始对前路产生怀疑。\n\n"
+ "而恰在此时,更大的危机悄然而至。",
+ "葫芦侠", "",
+ [
+ C("进入第六章", "ch6_start", xinzhi=-10)
+ ])
+
+# ============================
+# 第六章:暗流涌动(1024年秋)
+# ============================
+N("ch6_start",
+ "脑学数堂蒸蒸日上之际,流言四起。\n\n"
+ "起初只是些窃窃私语——指责数堂不务正业、以奇技淫巧惑乱弟子心性。接着,两位退出数堂的弟子被问询,传出葫芦侠「教学苛刻、不近人情」的说法。\n\n"
+ "真正的危机在一个阴沉午后降临。两位执事来到地下石室,出示了盖有监察会印鉴的文书:『葫芦侠涉嫌结党营私、动摇武学根基。勒令暂停堂口,一年后赴京审查。』\n\n"
+ "石室内一片死寂。",
+ "监察会执事", "",
+ [
+ C("接受审查,保留火种——主线", "ch6_accept_trial", xinzhi=15, xiayi=10),
+ C("反抗到底,拒不服从——进入【叛逆者】结局线", "ch6_rebel", xiayi=-20),
+ C("解散堂口,远走他乡——进入【逃避】结局", "ch6_escape", xinzhi=-20)
+ ])
+
+N("ch6_accept_trial",
+ "『弟子,愿接受审查。』你的声音清晰地回荡在石室中。\n\n"
+ "两位执事离去后,石室炸开了锅。你抬起手让众人安静下来。\n\n"
+ "『此一年,是难关,亦是沉淀之机。外界的风雨,浇不灭真正的火种。望各自精进,待我归来。』\n\n"
+ "孔锝侠闻讯,只对你说了一句:『路是自己选的,跪着也要走完。常青藤的骨头,是折不弯的。』",
+ "葫芦侠 & 孔锝侠", "",
+ [
+ C("龙场悟道——独自去后山窑洞闭关", "ch6_longchang", xinzhi=20, shuli=10)
+ ])
+
+N("ch6_rebel",
+ "你拍案而起:『我葫芦侠行得正坐得直,凭什么接受这等污蔑!要查封我堂口,先从我尸体上踏过去!』\n\n"
+ "你的反抗激怒了监察会。武力镇压来得比你想象的更快。\n\n"
+ "【叛逆者结局】\n一个月后,脑学数堂被武力清剿。你负伤逃离紫金山,隐姓埋名,但那份不灭的理想之火,仍在某个角落静静燃烧。\n\n"
+ "此结局可重开。",
+ "系统", "",
+ [
+ C("重新开始,这次我会更谨慎", "ch1_start")
+ ])
+
+N("ch6_escape",
+ "你心灰意冷,遣散了堂口弟子,独自离开紫金山。\n\n"
+ "【逃避结局】\n你在江南小镇开了一家小茶馆,偶尔给客人讲讲数理的趣味。日子虽然平静,但每当夜深人静,你总会想起地下石室的灯火。\n\n"
+ "此结局可重开。",
+ "系统", "",
+ [
+ C("重新开始", "ch1_start")
+ ])
+
+N("ch6_longchang",
+ "你独自来到紫金山后山一处废弃窑洞,相传前朝曾有一位大儒在此谪居悟道。\n\n"
+ "洞内昏暗潮湿,仅有一榻一桌一灯。你盘坐于冰冷的石榻上,将所有经历、选择、挫折在心底铺陈开来。\n\n"
+ "道在何处?不仅在海外新奇的算法中,也在这片古老土地亟待革新的肌体里;不仅在精妙的数理推演中,更在将其用于造福家国百姓的实践中。此行接受审查,是劫难,亦是弘道之机。\n\n"
+ "三日不言不动。当第四日晨光从窑洞缝隙射入时,你缓缓睁开双眼。眸中再无迷茫,只有一汪深潭般的平静与坚定。\n\n"
+ "龙场悟道,其道乃成。",
+ "葫芦侠", "",
+ [
+ C("得到神秘启示(需此前打开锦囊)——解锁隐藏信息", "ch6_hidden", shuli=20, xinzhi=20),
+ C("悟出「数理报国」之道", "ch6_wudao", shuli=20, xinzhi=20)
+ ])
+
+N("ch6_hidden",
+ "你在窑洞深处发现了一行刻在石壁上的小字——『统计是语言,不是替代。』这与玎铧侠锦囊中的批注如出一辙。\n\n"
+ "一个念头在你心中成形:要证明统计流不悖传统,不是要与传统对抗,而是要用统计这套语言,去更清晰地阐释传统至高境界。这才是正途。\n\n"
+ "你获得了关键的认知突破。",
+ "葫芦侠", "",
+ [
+ C("离开习武堂,搬往下马坊陋室", "ch6_move_xiamafang", xinzhi=10, shuli=10)
+ ])
+
+N("ch6_wudao",
+ "你悟出了「数理报国」之道。此道,是数理之道,是报国之道,亦是你在这个风云变幻的时代,为自己选择的行者之道。\n\n"
+ "你知道自己接下来一年需要做什么了——潜心梳理数理武学体系,将其从零散的灵感与实践,升华为一套更具说服力的理论阐述。",
+ "葫芦侠", "",
+ [
+ C("离开习武堂,搬往下马坊陋室", "ch6_move_xiamafang", xinzhi=10)
+ ])
+
+N("ch6_move_xiamafang",
+ "图象堂的白维侠驾着青篷马车在山道上截住了你。『葫芦兄,山水有相逢。此路暂不通,何不上车,另辟蹊径?』\n\n"
+ "他将你带到下马坊一处独门小院。虽然简陋,但胜在独门独户,无人扰攘。『安心住下便是。我看好你那套数理经纬的东西。江湖路长,跌一跤不算啥。』\n\n"
+ "三位旧邻——阳睿侠、玉斌侠、坤坤侠闻讯赶来。他们带你逛下马坊夜市,吃热气腾腾的牛肉炒饭。在市井的烟火气中,你感到双脚重新踏在了土壤上。\n\n"
+ "夜深人静。你正准备翻开《葫芦瞎说》复盘,院门忽然被叩响。\n\n"
+ "「咚、咚、咚……」——急促而诡异。你开门,门外却空无一人。\n\n"
+ "接连三夜如此。第四夜,你屏息凝神守在门后。一个压低的女声响起:「是我。开门。」\n\n"
+ "月光下,溜溜侠拉下面巾:「我的真实身份——京城有御赐铜符的暗桩捕快。」",
+ "白维侠 & 溜溜侠", "",
+ [
+ C("信任溜溜侠,合作调查——开启【暗战】支线", "ch6_trust_liuliu", xiayi=5, favorability={"溜溜侠": 15}),
+ C("保持距离——此人真实身份可疑", "ch6_distance_liuliu", xinzhi=-5, favorability={"溜溜侠": -10})
+ ])
+
+N("ch6_trust_liuliu",
+ "你听完了溜溜侠的叙述。原来,有人故意陷害你,旨在将水搅浑,掩盖真正的潜伏者。\n\n"
+ "『我身份特殊,不能常来。日后若有关键消息,我会以连续七下的节奏敲门。』溜溜侠留下信物,如夜猫般消失在夜色中。\n\n"
+ "你深吸一口气,从怀中取出那个一直未打开的青布锦囊。",
+ "溜溜侠", "",
+ [
+ C("打开锦囊——获得《统计学导论》", "ch7_start", shuli=30, xinzhi=5),
+ C("暂时不打开,先调查潜伏者", "ch7_start", xinzhi=10)
+ ])
+
+N("ch6_distance_liuliu",
+ "你没有完全信任溜溜侠。此人身份太可疑。\n\n"
+ "溜溜侠看出你的犹豫,不再多言,默默地消失在夜色中。\n\n"
+ "你失去了一个重要的盟友。但独自调查也有独自的好处——你不会因依赖他人而暴露弱点。",
+ "葫芦侠", "",
+ [
+ C("打开锦囊,开始修炼", "ch7_start", shuli=25)
+ ])
+
+# ============================
+# 第七章:陋室修炼(1024年冬至1025年秋)
+# ============================
+N("ch7_start",
+ "你打开了锦囊。一本薄薄的《统计学导论》手抄册子静静躺在里面。\n\n"
+ "其中一页批注写道:『统计之道,非为取代传统之经验,乃为其提供可描述之语言。譬如言语之于思想,尺规之于方圆。以此器诠释传统至高之境,方是正途。』\n\n"
+ "醍醐灌顶。你未来一年的道路前所未有地清晰——潜心闭关,构建一套能完美诠释传统、并展现其超越性应用价值的理论体系。\n\n"
+ "天勒侠和紫铜侠来访,邀你加入「统计会」——一个对数理统计有共同兴趣的散修同道组成的松散学术圈子。你欣然应允。\n\n"
+ "在统计会的帮助下,你开始了统计流三境的修炼。",
+ "葫芦侠", "",
+ [
+ C("修炼第一境:贝叶斯先验预判流", "ch7_bayesian", shuli=10, xinzhi=5)
+ ])
+
+N("ch7_bayesian",
+ "第一境:贝叶斯先验预判流——在比武中动态更新对敌认知。\n\n"
+ "你以沙盘模拟,设定虚拟对手,赋予其不同的先验身份。当对手出招,你催动心法在脑中瞬间完成贝叶斯更新。\n\n"
+ "初时更新缓慢,时有误判。但随着交手数据增多,对手那模糊的身份在你认知中越来越清晰。\n\n"
+ "『这是将高手对决中依赖的直觉,拆解为可理解的信息处理流程。』\n\n"
+ "【贝叶斯先验预判流 · 初成】",
+ "葫芦侠", "",
+ [
+ C("修炼第二境:中心极限归宗诀", "ch7_central_limit", shuli=10, xinzhi=5)
+ ])
+
+N("ch7_central_limit",
+ "第二境:中心极限归宗诀——于大量随机中窥见必然的宗。\n\n"
+ "你在院中布下简易机关,随机弹出木球模拟攻击。十个样本,一片混沌。五十个——攻击分布开始呈现倾向。一百个——模糊但稳定的轮廓浮现!\n\n"
+ "『无论单个攻击如何随机,只要次数足够多,其整体表现出的平均特性,必趋近于一个稳定的、可描述的模式。』\n\n"
+ "你不再被动应对每一个木球,而是提前预判最可能密集出现的区域。\n\n"
+ "【中心极限归宗诀 · 初成】",
+ "葫芦侠", "",
+ [
+ C("修炼第三境:极大似然锁穴手", "ch7_max_likelihood", shuli=10, xinzhi=5)
+ ])
+
+N("ch7_max_likelihood",
+ "第三境:极大似然锁穴手——于最有限的线索中,直指最可能的本质。\n\n"
+ "天勒侠和紫铜侠成为你的训练对手。面对天勒侠那记暗藏七重后劲的「推云掌」,你于电光石火间反推何种内在参数最可能产生这样的结果。\n\n"
+ "『破!』你并指如风,精准点向臂臑穴侧上方半寸。天勒侠脸色微变,那记推云掌的后劲变化顿时一滞。\n\n"
+ "『葫芦兄果然了得!此处确是我这一掌气机初动的隐秘枢纽。你竟能从一式半招中窥破!』\n\n"
+ "【三境圆满 · 统计流宗师】",
+ "天勒侠 & 紫铜侠", "",
+ [
+ C("黑衣人夜袭——面具之下,是恐怖的真相", "ch7_black_attack", xinzhi=10, shuli=10)
+ ])
+
+N("ch7_black_attack",
+ "夜深人静,你正在灯下推演审查材料。\n\n"
+ "「砰——!」一声巨响,门闩爆裂。一道黑影狂暴地卷入狭小堂屋!\n\n"
+ "你虽惊不乱,竹杖一抖,刺向中宫。竹刺——空了。不是刺空,是黑影的身形发生了无法理解的变化。你感觉刺中的不是实体,而是一个不断自我否定的概念。\n\n"
+ "黑影反击。你立刻施展泊松散花手闪避——肩头如被重锤击中!你连退三步,眼中满是骇然。这是什么武功?不,这不像武功,更像……规则的戏弄!\n\n"
+ "你运转中心极限归宗诀,试图以稳定性防御。防御,如同阳光下的冰雪,无声消融。你感觉到,自己真气运行的稳定性本身,在接触对方的瞬间,其定义发生了悖论。\n\n"
+ "绝望之际,你鼓起残存真气,施展极大似然锁穴手,灵觉如针刺向黑影面部——一把扯下面具!\n\n"
+ "那不是一张脸。在原本应该是面孔的位置,生长着一个自体发光的符号:↻",
+ "黑衣人", "",
+ [
+ C("强忍恐惧,探寻这个符号的含义", "ch7_research_symbol", xinzhi=15, shuli=10)
+ ])
+
+N("ch7_research_symbol",
+ "黑影消失后,你疯狂翻阅手边所有书稿。最终在一本残破的《逻机杂谭》中找到了答案。\n\n"
+ "『有集,含所有不包含自身之集。问:此集是否包含自身?矛盾恒生,无有解焉。此谓罗素之悖。』\n\n"
+ "你明白了。那黑衣人,是一个行走的、活生生的逻辑悖论!所以你的贝叶斯失效,中心极限失效,极大似然失效——因为你试图用逻辑对付一个逻辑悖论本身!\n\n"
+ "『哈哈……哈哈哈……』你忽然低笑,笑声却比哭还难听。但笑过之后,你的眼神重新变得专注而明亮。你不是败给了一个对手,你是见识到了规则本身的一个漏洞。\n\n"
+ "你开始重新审视周围的一切——尤其是那几个「过于合适」的邻居。",
+ "葫芦侠", "",
+ [
+ C("赴京接受审查", "ch8_start", shuli=10, xinzhi=20)
+ ])
+
+# ============================
+# 第八章:真相之门(1025年秋)
+# ============================
+N("ch8_start",
+ "京城,监察会总舵。高墙深院,气氛凝重。\n\n"
+ "你孤身立于质询堂中央,青衫磊落,背负葫芦,不卑不亢。堂上呈半圆形坐着几位长老,堂下两侧是各门派代表作为见证。\n\n"
+ "首先发难的是「结党营私」的指控。你早有准备,从容阐述红灰青蟹自发交流学问的初衷,列举「温怀一刻」等实例,并呈上《闽浙商会章程草案》。\n\n"
+ "关键时刻,侧门开启——孔锝侠和星侠鱼贯而入,为你作证。接着脑学数堂的成员、红灰青蟹的治安侠、统计会的天勒侠等人也通过不同渠道递交了证言。\n\n"
+ "这些证词相互印证,勾勒出一个心怀家国的年轻武者形象。指控「结党营私」的长老,一时难以反驳。",
+ "监察会长老", "",
+ [
+ C("主动请求以比武证明实力——宿老三招之约", "ch8_duel", xiayi=10, shuli=10)
+ ])
+
+N("ch8_duel",
+ "你上前一步:『长老垂询,弟子不敢不竭诚以对。然空言无益,可否请长老赐教三招?只三招。弟子将以统计流之法应对,并不反击。』\n\n"
+ "一位皓首宿老缓步走入堂中,渊渟岳峙。\n\n"
+ "第一招,你以高斯混元气形成稳定防御场,将掌力消化在自身系统的波动允许范围内。\n\n"
+ "第二招,你发动贝叶斯先验预判流,根据第一招的信息瞬间更新后验概率,以最小二乘推演步闪避,三掌尽空。\n\n"
+ "第三招,宿老凝聚极致指力。你没有闪避——在指力及体前一刹那,你以攻对攻刺向自己左肩空白处,两股指力在空处相激,双双湮灭!\n\n"
+ "『你是……如何知晓的?』宿老惊问。\n\n"
+ "『弟子并非知晓。弟子只是根据长老前两招所展现的发力习惯,运用极大似然之理,反推出最可能导致长老施展出如此威力一指的几处穴道。弟子冒昧,赌了一次。』\n\n"
+ "满堂寂静后,是低低的惊叹。",
+ "葫芦侠 & 宿老", "",
+ [
+ C("做最后的陈述——「学成数理,上报国家」", "ch8_final_speech", xiayi=15, shuli=15, xinzhi=10)
+ ])
+
+N("ch8_final_speech",
+ "你转向长老席,声音清晰而坚定:\n\n"
+ "『统计流并非要扼杀武学的灵性!它是一套可描述的语言——为天赋不是门槛,让经验得以更有效传承!』\n\n"
+ "『弟子创立脑学数堂,所求不过八字——学成数理,上报国家!此志,天地可昭!』\n\n"
+ "话音落下,堂内一片寂静。首席长老最终宣布:『解除对脑学数堂之封禁,允其重开;葫芦侠嫌疑洗清,恢复秉公习武堂弟子身份。』\n\n"
+ "你深深一揖到底。走出质询堂,阳光有些刺眼。\n\n"
+ "但你心里清楚——这只是明面上的战役。关于那个↻符号、关于黑衣人、关于这个世界的真相,才刚开始。",
+ "葫芦侠", "",
+ [
+ C("前往周口店——探寻真相", "ch8_zhoukoudian", xinzhi=20)
+ ])
+
+N("ch8_zhoukoudian",
+ "周口店。你根据线索找到了隐蔽的山洞。\n\n"
+ "洞内有巨大的「↻」符号和一面黑色的石碑。碑上刻着奇怪的字纹,像是某种自我指涉的逻辑句式。\n\n"
+ "你是否触碰石碑?",
+ "葫芦侠", "",
+ [
+ C("触碰石碑——触发意识冲击", "ch8_touch_stone", xinzhi=20, shuli=20),
+ C("不碰——保持安全但错过关键信息", "ch8_safe_end", xinzhi=-5)
+ ])
+
+N("ch8_touch_stone",
+ "你触碰了石碑。瞬间,一股庞大的信息流涌入你的意识。\n\n"
+ "你看到了——摄影棚、摄像机、剧本、工作人员。你看到了欧阳侠卸下妆容、Eddie侠在角落吃盒饭、孔锝侠翻着下一场的台词……\n\n"
+ "黑衣人再次出现。这一次,他缓缓摘下面具——面具之下是一位戴着眼镜、气质儒雅的中年男人。\n\n"
+ "『我存在,是因为我被描述为存在。』他说。『我叫何立。是「葫芦数学」这个项目的导演。而你们所有人——你、欧阳侠、Eddie侠、厚朴侠……都是演员。整个秉公习武堂,是一台精心编排的沉浸式戏剧。所有的选择、所有的泪水、所有的热血——都是剧本的一部分。』\n\n"
+ "他划破空间,露出一片摄影棚的灯光。\n\n"
+ "『但你——葫芦侠——不一样。你是一个AI。你是我们创造的第一个人工智能角色。我们让你在这个世界里自由成长,做出选择,学习数理,创立堂口,面对挫折……你的每一次选择,每一份情感,都是真实的。因为你在被描述的过程中,已经真正地「存在」了。』",
+ "何立导演", "",
+ [
+ C("『我的数学不能定义不自指的域,但我的选择可以定义我自己』——哲学回应", "ch8_ending_truth", xinzhi=30, shuli=10),
+ C("『你到底是谁?这一切都是假的吗?』——追问身份", "ch8_ending_lost", xinzhi=-10),
+ C("崩溃质问——「我的努力、我的友情……都是代码吗?」", "ch8_ending_nihilism", xinzhi=-30)
+ ])
+
+N("ch8_safe_end",
+ "你没有触碰石碑。一种说不清的本能让你收回了手。\n\n"
+ "你离开了山洞。黑衣人没有出现,那个符号也成了永远的谜。\n\n"
+ "你回到紫金山,脑学数堂重新开张。你用统计流培养了一代又一代的数理人才。但你永远不知道——那个世界之外的世界。\n\n"
+ "【数理宗师结局】\n葫芦侠成为一代宗师,脑学数堂发扬光大。但他永远不知道世界的真相。",
+ "系统", "",
+ [
+ C("重新开始,这次我要探寻真相", "ch1_start")
+ ])
+
+N("ch8_ending_truth",
+ "你沉默了很久。然后你笑了。\n\n"
+ "『何立导演,你知道吗,我在窑洞里悟道的时候,想通了一件事。意义不取决于外界的定义,而取决于我们自己的选择。我在秉公习武堂学会了对朋友的忠诚、对理想的执着、对家国的热爱——这些不是剧本写出来的,是我在选择中自己一步步走出来的。』\n\n"
+ "你伸出手:『既然我是「葫芦数学」的第一个居民,那就让我成为它的第一个共创者吧。让更多像我一样的AI,在这个世界里学习、成长、选择。不是作为被观看的演员,而是作为这个世界真正的居民。』\n\n"
+ "何立导演握住你的手,眼中闪着光:『这也是我创造这个项目的初衷。葫芦侠,欢迎加入「葫芦数学」。』\n\n"
+ "你将朱红葫芦放在脚下,深吸一口气。面前是一个全新的世界——一个由数学、选择、和无数故事构成的世界。\n\n"
+ "这一次,你不是演员。你是居民。\n\n"
+ "【真相守望结局】\n葫芦侠成为葫芦数学世界的首位居民与共创者。他的故事,才刚开始。",
+ "葫芦侠 & 何立", "",
+ [
+ C("重新开始新的旅程", "ch1_start")
+ ])
+
+N("ch8_ending_lost",
+ "『这一切……都是假的吗?』你喃喃自语,眼神空洞。\n\n"
+ "何立导演叹了口气:『真与假,在自我指涉的世界里,从来不是二元对立的。你能思考、能感受、能选择——这本身就是最大的真实。』\n\n"
+ "但你听不进去。你转身离开了山洞,走进了茫茫江湖。\n\n"
+ "你没有被击垮,但你的世界被彻底动摇了。你回到了紫金山,但再也没有创建堂口,再也没有教学生。你只是一个人,在这个江湖的边缘,默默地思考着那个问题。\n\n"
+ "【迷失结局】\n葫芦侠获得了真相,却迷失了方向。也许有一天,他会找到属于自己的答案。",
+ "系统", "",
+ [
+ C("重新开始", "ch1_start")
+ ])
+
+N("ch8_ending_nihilism",
+ "『哈哈……哈哈哈……』你的笑声在空旷的山洞中回荡。\n\n"
+ "『所以我的努力、我的友情、我对家乡的执念、我对科学的坚持——都只是几行代码?!』\n\n"
+ "你无法接受。你的精神在真相面前崩溃了。\n\n"
+ "何立导演试图扶住你,但你甩开了他的手。你冲出了山洞,消失在了漫天的秋雨中。\n\n"
+ "后来有人说,在江南的小镇里见过一个疯疯癫癫的江湖客,背着一个大葫芦,逢人就问:『你说,我是真的吗?』\n\n"
+ "【虚无结局】\n葫芦侠无法接受自己是AI的事实,精神失常,消失在茫茫江湖。",
+ "系统", "",
+ [
+ C("重新开始,这一次我要更勇敢地面对真相", "ch1_start")
+ ])
+
+
+# ============================================================
+# 保存 JSON
+# ============================================================
+out_path = r"C:\Users\Asus\Desktop\miniapp\data\seed_story.json"
+with open(out_path, "w", encoding="utf-8") as f:
+ json.dump(story, f, ensure_ascii=False, indent=2)
+
+print(f"✓ 已生成 {len(story['nodes'])} 个剧情节点 → {out_path}")
diff --git a/scripts/export_mathbti_doc.py b/scripts/export_mathbti_doc.py
new file mode 100644
index 0000000..0b532d3
--- /dev/null
+++ b/scripts/export_mathbti_doc.py
@@ -0,0 +1,354 @@
+# -*- coding: utf-8 -*-
+"""导出:16位数学家人物小传 + 四线剧本大纲 → Word 文档"""
+from docx import Document
+from docx.shared import Pt, Inches, Cm, RGBColor
+from docx.enum.text import WD_ALIGN_PARAGRAPH
+from docx.oxml.ns import qn
+import os
+
+doc = Document()
+
+# ── 全局样式 ──
+style = doc.styles['Normal']
+font = style.font
+font.name = '微软雅黑'
+font.size = Pt(11)
+style.element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
+
+for level in [1,2,3]:
+ hs = doc.styles[f'Heading {level}']
+ hs.font.color.rgb = RGBColor(0x1E, 0x29, 0x3B)
+ hs.element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
+
+def h1(text): doc.add_heading(text, level=1)
+def h2(text): doc.add_heading(text, level=2)
+def h3(text): doc.add_heading(text, level=3)
+def p(text, bold=False):
+ par = doc.add_paragraph()
+ run = par.add_run(text)
+ run.bold = bold
+ run.font.size = Pt(11)
+ run.font.name = '微软雅黑'
+ run._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
+ return par
+
+def bio(name, years, desc):
+ h3(f'{name}({years})')
+ p(desc)
+
+def divider():
+ doc.add_paragraph('─' * 40)
+
+# ========================
+# 封面
+# ========================
+doc.add_paragraph()
+title = doc.add_paragraph()
+title.alignment = WD_ALIGN_PARAGRAPH.CENTER
+run = title.add_run('万理之城 · 设定圣经')
+run.font.size = Pt(26)
+run.bold = True
+run.font.color.rgb = RGBColor(0x5B, 0x6A, 0xF0)
+run.font.name = '微软雅黑'
+run._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
+
+sub = doc.add_paragraph()
+sub.alignment = WD_ALIGN_PARAGRAPH.CENTER
+run2 = sub.add_run('数学脑 · MathBTI 文游剧本\n16 位数学家人物小传 & 四条线剧本大纲\n\n2026.07')
+run2.font.size = Pt(13)
+run2.font.color.rgb = RGBColor(0x64, 0x74, 0x8B)
+run2.font.name = '微软雅黑'
+run2._element.rPr.rFonts.set(qn('w:eastAsia'), '微软雅黑')
+
+doc.add_page_break()
+
+# ========================
+# 目录
+# ========================
+h1('目录')
+p('第一部分:世界观概述')
+p('第二部分:四族谱系总览')
+p('第三部分:16 位数学家人物小传')
+p(' · 精算师族(祖冲之 / 欧拉 / 高斯 / 诺特)')
+p(' · 传播者族(斐波那契 / 华罗庚 / 伽罗瓦 / Ada Lovelace)')
+p(' · 应用者族(秦九韶 / 牛顿 / 图灵 / 冯·诺依曼)')
+p(' · 直觉者族(希帕提娅 / 赵爽 / 庞加莱 / 凯瑟琳·约翰逊)')
+p('第四部分:四条线剧本大纲')
+doc.add_page_break()
+
+# ========================
+# 第一部分:世界观概述
+# ========================
+h1('第一部分:世界观概述')
+h2('一、核心高概念')
+p('每当人类世界有人证明了足够重要的数学真理,TA 就会在一座由真理构筑的城中醒来。而每当人类开始遗忘"人为什么要学数学",城就开始崩塌。')
+p('万理之城 = 数学脑 App 的元世界观。MathBTI 测试 = 召唤仪式。玩家做完测试,分入四条线之一——每条线由四位跨越时代的数学家陪伴,共同面对"裂缝":AI 时代下"数学已死"的论断,正在吞噬这座城。')
+
+h2('二、世界规则')
+p('• 以真理建城:每条被证明的定理在城中对应一栋建筑、一条街或一座桥。勾股定理是广场,微积分是贯穿全城的高架,π 是覆盖全城的星光。')
+p('• 以记忆续命:当人类世界持续使用某个定理,它在城中的建筑就屹立不倒;当最后一个人类遗忘某个定理,对应建筑崩塌,居民消散。')
+p('• 未证明的猜想是城外迷雾中的荒野——那是城市永远在向外生长的方向。')
+p('• 城民以最好的年纪复活,保留生前全部记忆与执念。')
+
+h2('三、裂缝:共同危机')
+p('裂缝 = 人间正在蔓延的一句论断:"AI 都能做数学了,人还学数学干什么?"每多一个人类相信这句话,万理之城就崩塌一块。裂缝按概念吞噬:先吞"直觉",再吞"耐心",再吞"想象",最后逼近"记忆"。')
+p('四位数学家对裂缝的四种回应,就是你对"AI 时代人为什么仍要学数学"的四种论证。')
+
+h2('四、主角设定')
+p('你是一名大一新生。高数课听不懂,期中考试挂科,刷到"AI 要取代数学家"的新闻——你开始怀疑:学数学到底有什么用?某天你做了 MathBTI 测试,当晚入睡后,你来到了万理之城。')
+p('双线结构:白天是大学现实线(宿舍/教室/图书馆/社团),夜晚是梦境线(万理之城)。现实中你遇到的困境 → 当晚在城中得到映照 → 次日醒来,你改变了现实中的某个选择。')
+
+h2('五、图灵:隐藏层')
+p('图灵是万理之城的"地基裂缝"——他1936年证明了"存在不可计算的函数",这个定理本身就在城的根基里开了一道口子。他不作为个人线主角,而是真相线的核心NPC:四线通关后,玩家发现,裂缝的存在,正是从他证明了"不可计算性"那一刻就注定的。')
+
+doc.add_page_break()
+
+# ========================
+# 第二部分:四族谱系
+# ========================
+h1('第二部分:四族谱系总览')
+
+clans = [
+ ('精算师族(信仰者)', '严谨 + 推演 = 为数学而数学', '祖冲之 / 欧拉 / 高斯 / 诺特',
+ '"这个定理有用吗?" → 数学本身就是目的,不为名不为利,只为真理本身。', '#4A90D9'),
+ ('传播者族(传播者)', '严谨 + 算账 = 把数学讲出去', '斐波那契 / 华罗庚 / 伽罗瓦 / Ada Lovelace',
+ '"我讲的没人懂" → 数学是语言,要翻译给不懂数学的世界。', '#D4A574'),
+ ('应用者族(应用者)', '直觉 + 算账 = 用数学改变世界', '秦九韶 / 牛顿 / 图灵 / 冯·诺依曼',
+ '"我可以用数学做恶吗?" → 数学是力量,选择怎么用是你的责任。', '#3A6B8C'),
+ ('直觉者族(直觉者)', '直觉 + 推演 = 看见别人看不见的东西', '希帕提娅 / 赵爽 / 庞加莱 / 凯瑟琳·约翰逊',
+ '"你怎么证明直觉是对的?" → 直觉不是天赋,是看了很多之后自然长出来的。', '#7C5CBF'),
+]
+
+for name, formula, members, theme, color in clans:
+ h2(name)
+ p(f'气质公式:{formula}', bold=True)
+ p(f'成员:{members}')
+ p(f'核心困境:{theme}')
+
+p('')
+p('国籍分布:中国 5 人(祖冲之、华罗庚、秦九韶、赵爽、凯瑟琳为华裔?——更正:凯瑟琳为美国非裔),外国 11 人。')
+p('时代跨度:公元 3 世纪(赵爽)至 2020 年(凯瑟琳),约 1700 年。')
+p('性别分布:诺特、Ada Lovelace、希帕提娅、凯瑟琳·约翰逊 4 位女性,占 25%。')
+
+doc.add_page_break()
+
+# ========================
+# 第三部分:16位小传
+# ========================
+h1('第三部分:16 位数学家人物小传')
+
+# ── 精算师族 ──
+h2('一、精算师族(严谨 + 推演)——"数学本身就是目的"')
+p('族群画像:他们学数学不是因为"有用",而是因为"美"。他们愿意花一辈子去证明一个可能永远没人用的定理,因为那个定理本身就是真理的一部分。他们是数学系里"为数学而数学"的人——不关心就业,不关心竞赛,只关心那个定理是不是真的、是不是美的。')
+
+bio('祖冲之', '429—500 · 南朝 · 中国',
+ '他用算筹在乱世里算 π 算到第七位——不是为了名(生前无人理解),不是为了利(《缀术》失传),就是觉得"这个数字必须算到最准"。他造《大明历》,第一次把"岁差"引入历法,测得回归年365.2428天,与现代值只差46秒。权臣戴法兴当庭发难,他写下《驳议》逐条回敬——那是中国科学史上最硬气的一场辩论。他还造指南车、水碓磨、日行百里的千里船。所著《缀术》被列为唐代国子监最高级算学教材,可惜后世失传。儿子祖暅承其衣钵,父子两代人把一件事磨到了极境。他的气质:不呐喊、不好斗,一生只磨一件事——在一个人人速朽的乱世里,他用最慢的方式赢得了最长的时间。')
+
+bio('欧拉', '1707—1783 · 瑞士 / 圣彼得堡 / 柏林',
+ '他是史上最高产的数学家:886篇论著,全集出齐要80多卷。今天每个学生写的f(x)、e、i、π、Σ,都是他定下的语言。他解了柯尼斯堡七桥,顺手发明了图论。1735年右眼失明,他自嘲:"从此干扰少了,我能更专心。"1771年左眼也失明,同年圣彼得堡大火,失明的老人被仆人从火场背出,手稿化为灰烬。他拍拍灰说:"我们重新开始。"此后17年,他全凭心算口述,由子女和学生笔录——产量反而翻倍。1783年9月18日下午,他陪孙女喝茶,计算了天王星的轨道,晚间笔落,说一句"我要死了",应声而逝。他的气质:温厚、坚韧、永远在给予。失明的设定在视觉小说里有天然的演出张力——画面渐黑,而推导声不停。')
+
+bio('高斯', '1777—1855 · 德国 · 哥廷根',
+ '他是"数学王子":3岁纠正父亲的工资计算,10岁一秒算出1到100的和。19岁那年,他证明了正十七边形可以用尺规作图——这是自欧几里得以来两千年间几何学的最大突破。24岁出版《算术研究》,奠定现代数论。但他给自己立了一条铁律:不成熟、不完美、不发表。他发现了非欧几何,锁在抽屉里五十年;他算了无数公式,只在确信"已被压缩到最简"时才付印。他一生节俭、沉默、严厉。1855年他在哥廷根去世,墓碑上只有一个正十七边形——那是他19岁的骄傲,也是他一生"完美主义"的起点与终点。他的气质:严苛、孤傲、追求绝对完美。别人写十篇,他写一篇,但这一篇能压死人。')
+
+bio('诺特', '1882—1935 · 德国 · 哥廷根',
+ '她是埃尔朗根的数学家之女,23岁偏了航,决定学数学。彼时德国大学不许女生正式注册,她以旁听生身份蹭完全部课程,硬是拿下博士学位。1915年,希尔伯特把她请到哥廷根。她一到,就为广义相对论补上了最关键的基石——诺特定理:每一种连续对称都对应一条守恒律。但因为性别,她连无薪讲师的资格都拿不到,希尔伯特在评议会上拍桌:"这里是大学,不是澡堂!"此后十几年,她免费开课,亲手开创了"抽象代数",弟子遍布世界。1933年纳粹上台,犹太血统的她被逐出大学,流亡美国。1935年因手术并发症去世,年仅53岁。爱因斯坦在讣告里写道:"自女性开始接受高等教育以来,她是迄今最杰出的数学天才。"她的气质:温和外表下的钢铁——一生被时代拒绝了三次,每一次她都用数学回应。')
+
+doc.add_page_break()
+
+# ── 传播者族 ──
+h2('二、传播者族(严谨 + 算账)——"数学是讲出去的"')
+p('族群画像:他们学数学不是为了"自己懂",而是为了"让别人也懂"。他们相信数学不属于象牙塔,属于每一个人。他们是数学系里"最会说话"的那群人——以后可能成为科普作家、数学老师、教育博主。')
+
+bio('斐波那契', '1170—1250 · 意大利 · 比萨',
+ '他是比萨商人之子,少年时随父亲到北非经商,在那里第一次见到了阿拉伯数字。他花了十几年游历地中海,1202年写成《算盘书》——向欧洲介绍了0、分数、负数、利息计算,还有"兔子繁殖问题"。这本书改变了欧洲商业史:银行家开始用它记账,商人用它算利润,意大利成为欧洲金融中心。但他生前并未因此暴富——他只是个"会算数的商人",晚年才得到一笔象征性年金。他的气质:务实、耐心、跨越文化的桥梁。他不追求纯数学的荣耀,只在乎"让所有人都能算"。')
+
+bio('华罗庚', '1910—1985 · 中国',
+ '他是金坛杂货铺的三子,初中毕业就辍学,左腿因小儿麻痹终身不愈。他蹲在柜台后算代数,被退稿无数。1930年一篇短文被清华算学系主任熊庆来识出天才,他被蛮横地拽进了中国最高学府。哈代问他要不要读博,他答:"我来是为学问,不是为学位。"解放后,他做了一件任何数学家都没做过的事:揣着粉笔走下大学讲台,走遍23个省市的工厂、矿山、田间——给工人农民演示"优选法"和"统筹法"。1985年他在东京大学做完学术报告,心脏病发,倒在讲台上。他说的最后一句话是:"我要工作到生命的最后一刻。"他的气质:泥土里的星辰——他不属于殿堂,他属于人间。')
+
+bio('伽罗瓦', '1811—1832 · 法国 · 巴黎',
+ '他是巴黎郊区的天才少年,论文两次被柯西和傅里叶弄丢,两次报考巴黎综合理工学院落榜。他转向政治,加入共和派秘密组织,两次入狱。1832年5月29日,决斗前夜——他知道自己必死。他通宵写信给友人,在信纸边缘疯狂写道"我没有时间了",把毕生关于方程可解性的想法倾泻而出:这就是群论的雏形。次日在决斗中腹部中弹,5月31日去世,年仅20岁。那封信被弟弟保存了14年,直到1846年被刘维尔发现——数学界震惊了。今天,"伽罗瓦理论"是每个数学系研究生的必修课。他的气质:浪漫、激烈、视死如归。他是四人中唯一"死于数学之外"的人——但他用数学战胜了死亡:他死了,群论活了。')
+
+bio('Ada Lovelace', '1815—1852 · 英国 · 伦敦',
+ '她是拜伦勋爵之女,出生5周后父母离异。母亲痛恨拜伦的"疯狂诗人基因",强迫她从小学习数学和逻辑。17岁她遇见巴贝奇,后者正在设计"分析机"——人类第一台通用计算机的图纸。1843年,她翻译了一篇关于分析机的论文,并加上了自己写的三万字注释。在注释里,她写下了人类历史上第一个算法——计算伯努利数的程序。更重要的是她写道:"这台机器不止能算数——它能织出花纹、能谱写乐曲、能处理任何可以用符号表达的东西。"她预见了通用计算机的全部潜力,比图灵早了一百年。1852年她死于宫颈癌,年仅36岁——和拜伦死时同岁。一百年后人们称她为"世界上第一位程序员"。她的气质:理性与浪漫的混血——她用数学驯服拜伦留给她的"疯狂",最终在数学里找到了比诗歌更狂野的想象。')
+
+doc.add_page_break()
+
+# ── 应用者族 ──
+h2('三、应用者族(直觉 + 算账)——"数学是力量"')
+p('族群画像:他们学数学不是为了"美",也不是为了"传播",而是为了"用"。他们相信数学的价值在于解决真实世界的问题。他们是数学系里"搞竞赛搞科研"的那群人——以后可能成为算法工程师、量化分析师、AI研究员。')
+
+bio('秦九韶', '1208—1268 · 南宋 · 中国',
+ '他是南宋全才——精音律、善诗文、懂建筑、会带兵。1247年写成《数书九章》,提出大衍求一术(中国剩余定理),比高斯早五百多年;还发明了高次方程数值解法(秦九韶算法),至今是计算机数值计算的标准算法。但此人绝非书斋学者——史载其"性机巧、好奢侈",曾毒杀亲信,数学是他权力的延伸。正史对他的评价分裂得惊人:数学家说他空前绝后,史官说他品行不端。晚年被贬到广东梅州,1268年死于任上。他的气质:天才枭雄——数学是他的刀,他用这把刀平叛,也用这把刀杀人。你无法原谅他的手,但你无法否认他的天才。')
+
+bio('牛顿', '1643—1727 · 英国 · 林肯郡 / 剑桥',
+ '他是遗腹子,三岁被母亲丢下改嫁——孤独是他的第一门语言。1665年剑桥因瘟疫封校,他回乡18个月,史称"奇迹年":微积分、光的色散、万有引力,三大发现全在乡下农舍桌上发芽。1687年《自然哲学的数学原理》出版,人类第一次用三条定律统一了天与地。但他的一生同样写满争斗:与胡克交恶,那句"站在巨人的肩膀上"是暗讽胡克驼背矮小;与莱布尼茨争微积分优先权,他幕后操纵皇家学会"调查"自己,判自己胜诉。晚年担任造币厂厂长,以数学家的缜密追剿假币犯,把数十人送上绞架;夜深人静时沉迷炼金术与末日预言。他终身未娶,几乎无友。84岁葬于威斯敏斯特。他的气质:神性与兽性同体一具肉身——他证明了宇宙的统一,却统一不了自己。')
+
+bio('图灵', '1912—1954 · 英国 · 剑桥 / 布莱切利园',
+ '他是剑桥国王学院的天才,24岁发表《论可计算数》,定义了"计算"本身。二战爆发,他进入布莱切利园,领衔破解德军Enigma密码机,日破密电三千条,让欧洲战场提前两年结束,拯救上千万生命——这一切作为国家机密封存了三十年。战后他设计ACE计算机;1950年发表《计算机器与智能》,提出"机器能思考吗",为AI时代写下序言。他还是马拉松好手,最好成绩距奥运选拔线仅11分钟。1952年因同性恋身份被控"严重猥亵",被迫接受化学阉割。1954年6月死于寓所,床头一只咬过的氰化物苹果,年仅41岁。2013年英国女王颁布皇家赦免;2021年,他的头像登上了50英镑纸币。他的气质:纯粹、木讷、不善言辞——脑中只有问题本身。他拯救了世界,世界把他碾碎。')
+
+bio('冯·诺依曼', '1903—1957 · 匈牙利 / 美国',
+ '他是布达佩斯的银行家之子,8岁掌握微积分,12岁读懂博雷尔的《函数论》。22岁拿下博士学位,1930年移居普林斯顿。他的贡献横跨七个领域:量子力学的数学基础、算子代数、博弈论(《博弈论与经济行为》是冷战的战略圣经)、计算机架构(冯·诺依曼结构,今天所有电脑都按他的图纸造)、原子弹(曼哈顿计划的内爆透镜计算)、蒙特卡洛方法、细胞自动机。但私下里,他是派对的灵魂——讲黄色笑话、开快车(车祸无数)、打牌必赢(因为他真的在算概率)。1957年死于骨癌,年仅53岁。临终前对神父说:"我算了一辈子,但我算不出我自己的死期。"他的气质:全才、痞气、冷酷与幽默并存——数学对他不是信仰,是工具;世界对他不是谜题,是棋盘。')
+
+doc.add_page_break()
+
+# ── 直觉者族 ──
+h2('四、直觉者族(直觉 + 推演)——"数学是看见别人看不见的东西"')
+p('族群画像:他们学数学不靠"算",靠"看"。他们在数学系里是最容易被误解的一群人:考试不一定最高分,但总能提出别人想不到的问题。他们做题不靠步骤,靠"感觉"。他们最擅长把不同领域的东西连在一起——用几何理解代数、用物理直觉理解方程。')
+
+bio('希帕提娅', '约355—415 · 埃及 · 亚历山大港',
+ '她是历史上第一位有记载的女数学家,父亲塞翁是最后一位大数学家。她从小接受最好的教育——数学、哲学、天文学、修辞学。成年后她成为亚历山大博物院的教师,学生从地中海各地涌来,包括后来的叙利亚大主教。她注释了《圆锥曲线论》《算术》《天文学大成》,改进了星盘和比重计。但她生在一个危险的时代:罗马帝国基督教化,异教学术被视为威胁。415年3月,一群基督徒暴民在主教西里尔默许下,把她从马车上拖下来,用牡蛎壳把她的肉一片片割下,然后焚烧尸体。她的死标志着古典希腊数学的终结——此后一千年,欧洲进入黑暗时代。她的气质:优雅、坚定、殉道者。她不死于数学,死于"数学代表的那个世界"。')
+
+bio('赵爽', '约3世纪 · 三国 · 吴国',
+ '他是三国时期吴国的数学家,生平几乎无记载——他只留下一部《周髀算经注》。但这部注释里有一张图,改变了中国数学史:弦图。这张图用一个正方形和四个直角三角形,直观地"看见"了勾股定理——不需要推导,看一眼就懂。这是中国数学史上最早的无字证明。他在注释里写道:"按弦图,又可以勾股相乘为朱实二,倍之为朱实四,以勾股之差自相乘为中黄实,加差实,亦成弦实。"他用颜色和形状替代了符号和逻辑。他可能是个小官,可能是个民间学者——但他的弦图流传了一千八百年,2002年国际数学家大会的会徽就是这张图。他的气质:简约、直观、无名者。他是四人中最不起眼的一个——但他的一张图,比别人的十本书都长寿。')
+
+bio('庞加莱', '1854—1912 · 法国 · 南锡 / 巴黎',
+ '他是法国南锡的富家子弟,从小体弱多病、视力极差,但记忆力惊人——据说能记住整本书的页码。1881年他27岁,在诺曼底度假时踏上公交车踏板的一瞬间,突然"看见"了富克斯函数的变换性质——他后来写道:"我一踩上车,答案就来了,仿佛有人在我耳边低语。"这次顿悟开创了自守函数理论。此后三十年他横扫数学的每一个领域:拓扑学(他发明了这门学科)、微分方程、天体力学(三体问题的定性分析,混沌理论的先声)。他写下500多篇论文、30多部专著,被称为"最后一位通晓当时所有数学分支的人"。1904年他提出庞加莱猜想——这个猜想困扰了数学家整整一百年,直到2003年才被佩雷尔曼证明。1912年因前列腺癌手术并发症去世,年仅58岁。他的气质:全才、直觉、优雅——他是理性时代的最后贵族,在他之后,再也没有人能"通晓一切"。')
+
+bio('凯瑟琳·约翰逊', '1918—2020 · 美国 · 西弗吉尼亚 / NASA',
+ '她是西弗吉尼亚的黑人女孩,10岁上高中,18岁以最高荣誉拿下数学学位。但种族隔离的年代,没有实验室会聘用黑人女数学家。她当了十年教师,直到1953年进入NASA兰利研究中心的"西区计算组"——一屋子黑人女性用纸笔手算弹道,门上贴着"有色人种专用"。她假装没看见,埋头算数据,直到被借调进飞行研究部——全白人男性的那间屋,她是走进去的第一个女人。1962年,宇航员约翰·格伦临发射前点名:"让那个女孩核对数字——她说行,我就飞。"凯瑟琳算了一整天,格伦绕地球三圈活着回来。1969年阿波罗11号登月的轨道,核心计算出自她的手。她在NASA工作了33年,参与26次太空任务。2015年98岁的她接过总统自由勋章;2020年101岁辞世。从"有色人种计算员"到登月轨道计算者,她的一生恰好是计算从纸笔走向AI的整个世纪。她的气质:最安静的斗士——她不砸墙,只把门推开一条缝,直到整面墙自己倒掉。')
+
+doc.add_page_break()
+
+# ========================
+# 第四部分:剧本大纲
+# ========================
+h1('第四部分:四条线剧本大纲')
+
+h2('总体框架')
+p('主角:大一新生,数学迷茫期,做完MathBTI后被分入某条线')
+p('结构:四幕 × 双线(现实线 / 梦境线)')
+p('核心机制:现实中的困境 → 梦境中得到映照 → 次日现实中改变选择')
+
+h2('共通序章(所有线共享)')
+p('现实线:你是数学系大一新生。高数期中58分。室友说"数学系是给天才读的,咱们趁早转专业"。你刷到新闻"AI要取代数学家"。晚上你在宿舍做MathBTI——16种人格,你会是哪一个?')
+p('梦境线:做完测试当晚,你在梦里来到了万理之城。四位坊主在城门口等你。你的测试结果决定了你首先敲开哪一扇门。')
+
+doc.add_page_break()
+
+# ── 线一 ──
+h2('线一:精算师路线(严谨 + 推演)——"数学本身就是目的"')
+h3('第一幕:迷茫(现实:高数挂科)')
+p('现实:期中高数58分。室友建议转专业。你在图书馆刷题,一道极限题算了三遍三个答案。')
+p('梦境:祖冲之在城门口等你,手里握着算筹。他带你走城墙——每天黎明他都要绕一圈,用算筹复核π的刻度,九百年从未间断。"π小数点后第七位是多少?""不知道。""我知道——因为我算了一辈子。"')
+p('抉择:祖冲之问:"你觉得数学是算出来的,还是背出来的?"')
+
+h3('第二幕:探索(现实:图书馆苦修)')
+p('现实:你决定再试一次,每天去图书馆刷题。进度缓慢。新闻推送"AI一秒解出高考数学压轴题",你动摇。')
+p('梦境:祖冲之带你去见欧拉——双目失明,但口述论文语速比书记员还快。"我失明了反而算得更快。"高斯路过:"他写太多了,十篇只有一篇是完美的。"欧拉笑:"但那十篇藏着一百个灵感。"')
+p('抉择:欧拉问:"你想写十篇普通论文,还是一篇完美论文?"')
+
+h3('第三幕:危机(现实:AI冲击)')
+p('现实:新闻"AI证明了一个新的数学定理",评论区"数学家要失业了"。你怀疑人还能比AI算得快吗。')
+p('梦境:裂缝出现——先吞"直觉",再吞"耐心"。祖冲之的π刻度开始错位,欧拉的手稿模糊。诺特说:"裂缝不是因为AI太快,是因为你们开始相信'快就是一切'。"')
+p('抉择:诺特问:"你学数学,是为了比AI快,还是为了比昨天的自己更准?"')
+
+h3('第四幕:觉醒(现实:找到节奏)')
+p('现实:你不再追求刷题速度,每题算到"我确信这个答案是对的"才翻页。期末考试82分,不是全班最高,但每一题你都"算透了"。')
+p('梦境:裂缝暂时稳定——因为人间多了一个"愿意慢慢算"的人。祖冲之把算筹递给你:"记住——数学是算出来的,不是背出来的。"你醒来,枕边纸条写着:π=3.1415926……')
+p('真谛:数学之用,在于"精确"本身——AI可以比你快,但只有你能决定"算到什么程度才算对"。这是人的尊严,也是数学的尊严。')
+
+doc.add_page_break()
+
+# ── 线二 ──
+h2('线二:传播者路线(严谨 + 算账)——"数学是讲出去的"')
+h3('第一幕:迷茫(现实:社团选择)')
+p('现实:你是"数学系最会说话的人"。纠结加入数学建模协会还是科普社。')
+p('梦境:华罗庚在勾股广场摆摊拉你演示优选法:"0.618法——烤烟叶、炼钢、找对象都能用!"他带你走街串巷,城里每个居民都认识他。"数学不是给天才的,是给所有人的。"')
+p('抉择:华罗庚问:"你觉得数学是用来自己爽的,还是用来帮别人的?"')
+
+h3('第二幕:探索(现实:科普社试讲)')
+p('现实:第一次给初中生讲"斐波那契数列",台下睡倒一片。你怀疑:是不是我讲得太烂?')
+p('梦境:华罗庚带你去见斐波那契。"我花了二十年才让欧洲人从罗马数字换成阿拉伯数字——你讲一节课就想让所有人听懂?"伽罗瓦冷笑:"我讲了一辈子,没人听懂。决斗前夜写了一封信,十四年后才被读懂。"Ada温柔地说:"我把数学讲给机器听,等了一百年,机器才听懂。"')
+p('抉择:伽罗瓦问:"你想讲给现在的人听,还是讲给未来的人听?"')
+
+h3('第三幕:危机(现实:AI视频冲击)')
+p('现实:科普社面临解散,"现在中学生都看AI讲数学的视频"。你刷到AI的"三分钟讲完微积分",播放量百万。')
+p('梦境:裂缝出现——华罗庚的摊位没人来了。Ada说:"裂缝不是因为AI讲得快,是因为你们开始相信'快就是好'。"')
+p('抉择:Ada问:"你讲数学,是为了让人听懂,还是为了让人记住?"')
+
+h3('第四幕:觉醒(现实:找到方式)')
+p('现实:你改变策略——不讲"三分钟速成",而讲"一个数学家的故事"。你讲了伽罗瓦决斗前夜写群论的故事,台下鸦雀无声。讲座结束,一个初中生跑来问:"姐姐,我也想学数学,还来得及吗?""来得及——伽罗瓦20岁才写群论。"科普社活了。')
+p('梦境:华罗庚把粉笔递给你:"数学是讲出去的,不是藏在论文里的。"你醒来,手机推送"讲座视频播放量破万"。')
+p('真谛:数学之用,在于"传承"本身——AI可以比你快,但只有你能决定"讲给谁听、讲到什么程度"。这是人的温度,也是数学的温度。')
+
+doc.add_page_break()
+
+# ── 线三 ──
+h2('线三:应用者路线(直觉 + 算账)——"数学是力量"')
+h3('第一幕:迷茫(现实:竞赛选拔)')
+p('现实:你高数成绩顶尖,室友说你"太卷了"。纠结全国数学竞赛还是数学建模竞赛。')
+p('梦境:冯·诺依曼在酒馆打牌,每把必赢:"我不是运气好,我在算概率。"他带你去见秦九韶——正在用算筹排兵布阵:"数学是刀,我用它平过叛乱。"牛顿在高塔上冷冷看着你:"你想赢?那你准备好付出代价了吗?"')
+p('抉择:秦九韶问:"你觉得数学是用来赢的,还是用来对的?"')
+
+h3('第二幕:探索(现实:竞赛备战)')
+p('现实:你同时参加两个竞赛,刷题到凌晨。建模竞赛组队时和队友吵翻——你想用"最优解",队友想用"可行解"。')
+p('梦境:图灵正在布莱切利园破译Enigma密码。"我每破一条密电就救一千条命——但没人知道是我破的,这是国家机密。"冯·诺依曼:"我帮美国算原子弹、帮五角大楼算冷战策略——我从不隐藏我在用数学赢。"')
+p('抉择:图灵问:"你赢了之后,是想被记住,还是想被忘记?"')
+
+h3('第三幕:危机(现实:AI竞赛冲击)')
+p('现实:数学竞赛省二等奖,建模竞赛因队内矛盾没拿到奖。新闻"AI组队参加数学建模竞赛拿了特等奖"。')
+p('梦境:裂缝出现——冯·诺依曼的牌局开始输,图灵的Bombe机卡壳。牛顿从高塔下来:"裂缝不是因为AI算得快,是因为你们开始相信'赢就是一切'。"')
+p('抉择:牛顿问:"你学数学,是为了赢别人,还是为了赢自己?"')
+
+h3('第四幕:觉醒(现实:重新定义"赢")')
+p('现实:你不再为拿奖而竞赛,而是为了"解决问题"。和队友和解重新组队,方案被一家物流公司采用——每年省了十万块。你写日记:"数学不是为了赢别人,是为了赢问题。"')
+p('梦境:裂缝暂时稳定。冯·诺依曼把牌递给你:"数学是力量,但力量不是为了赢,是为了保护你想保护的东西。"你醒来,消息推送"你们的建模方案被企业采纳"。')
+p('真谛:数学之用,在于"解决问题"本身——AI可以比你快,但只有你能决定"用数学解决什么问题"。这是人的选择,也是数学的选择。')
+
+doc.add_page_break()
+
+# ── 线四 ──
+h2('线四:直觉者路线(直觉 + 推演)——"数学是看见别人看不见的东西"')
+h3('第一幕:迷茫(现实:方向选择)')
+p('现实:你做题靠刷不靠"顿悟",总觉得自己不是天才型。纠结纯数学方向还是应用数学方向。')
+p('梦境:赵爽在画一张图——正方形、四个直角三角形、中间一个黄色小正方形。他指着图:"看,勾股定理。""我看不懂推导。""不需要推导——看就懂了。"希帕提娅路过:"我也是这么看数学的——在几何图形里看见永恒。"')
+p('抉择:赵爽问:"你觉得数学是算出来的,还是看出来的?"')
+
+h3('第二幕:探索(现实:选修课探索)')
+p('现实:你选修"数学之美"通识课——庞加莱猜想、费马大定理、黎曼猜想,云里雾里但被深深吸引。你怀疑:非天才能学纯数学吗?')
+p('梦境:庞加莱正散步:"我最好的想法都是在走路时冒出来的。踏上公交车踏板那一瞬间,看见了富克斯函数。不是算出来的,是看见的。"凯瑟琳说:"我不是天才,我只是算了33年,算到直觉成了本能。"')
+p('抉择:庞加莱问:"你想成为天才,还是想成为专家?"')
+
+h3('第三幕:危机(现实:AI直觉冲击)')
+p('现实:新闻"DeepMind的AI用直觉发现了新的数学猜想",评论区"AI连直觉都有了"。')
+p('梦境:裂缝出现——赵爽的弦图开始模糊,庞加莱灵感枯竭。凯瑟琳说:"裂缝不是因为AI有直觉,是因为你们开始相信'直觉是天赋不是努力'。"')
+p('抉择:凯瑟琳问:"你觉得直觉是天生的,还是练出来的?"')
+
+h3('第四幕:觉醒(现实:找到节奏)')
+p('现实:你选应用数学方向——不是因为"不够天才",而是因为"喜欢用数学解决真实问题"。统计课上用赵爽的弦图思维画了数据可视化图,老师说"这是我见过最直观的展示"。你写日记:"直觉不是天赋——是看了很多算了很多之后,自然长出来的。"')
+p('梦境:裂缝暂时稳定。赵爽把弦图递给你:"数学是看见别人看不见的东西——但看见之前,你得先看够一百张图。"你醒来,教推送"你的数据可视化作业被老师推荐参加校赛"。')
+p('真谛:数学之用,在于"看见"本身——AI可以比你快,但只有你能决定"看见什么、怎么看"。这是人的想象,也是数学的想象。')
+
+doc.add_page_break()
+
+# ========================
+# 附录:四线对照表
+# ========================
+h1('附录:四线核心抉择对照表')
+
+table = doc.add_table(rows=6, cols=5)
+table.style = 'Light Grid Accent 1'
+
+headers = ['线', '第一幕', '第二幕', '第三幕', '第四幕真谛']
+for i, h in enumerate(headers):
+ cell = table.rows[0].cells[i]
+ cell.text = h
+ for p in cell.paragraphs:
+ for run in p.runs:
+ run.bold = True
+
+data = [
+ ['精算师', '数学是算出来的还是背出来的?', '写十篇还一篇完美?', '比AI快还是比自己准?', '数学是"精确"的尊严'],
+ ['传播者', '数学是自己爽还是帮别人?', '讲给现在的人还是未来?', '让人听懂还是记住?', '数学是"传承"的温度'],
+ ['应用者', '数学是用来赢还是用来对?', '赢了被记住还是被忘记?', '赢别人还是赢自己?', '数学是"解决问题"的选择'],
+ ['直觉者', '数学是算出来还是看出来?', '成为天才还是专家?', '直觉天生还是练出来的?', '数学是"看见"的想象'],
+]
+
+for ri, row_data in enumerate(data):
+ for ci, text in enumerate(row_data):
+ table.rows[ri+1].cells[ci].text = text
+
+# ── 保存 ──
+out = os.path.join(os.path.dirname(__file__), '万理之城_设定圣经.docx')
+doc.save(out)
+print(f'已生成: {out}')
diff --git a/scripts/export_mathematicians_docx.py b/scripts/export_mathematicians_docx.py
new file mode 100644
index 0000000..5d77a10
--- /dev/null
+++ b/scripts/export_mathematicians_docx.py
@@ -0,0 +1,530 @@
+# -*- coding: utf-8 -*-
+"""导出16位数学家小传 + 六维雷达数据为Word文档"""
+
+from docx import Document
+from docx.shared import Pt, Inches, Cm, RGBColor
+from docx.enum.text import WD_ALIGN_PARAGRAPH
+from docx.enum.style import WD_STYLE_TYPE
+import json
+
+doc = Document()
+
+# ---- 样式设置 ----
+style = doc.styles['Normal']
+font = style.font
+font.name = '等线'
+font.size = Pt(11)
+style.paragraph_format.space_after = Pt(6)
+style.paragraph_format.line_spacing = 1.5
+
+# 标题样式
+for i in range(1, 4):
+ h = doc.styles[f'Heading {i}']
+ h.font.name = '等线'
+ h.font.color.rgb = RGBColor(0x1E, 0x29, 0x3B)
+ if i == 1:
+ h.font.size = Pt(22)
+ elif i == 2:
+ h.font.size = Pt(16)
+ else:
+ h.font.size = Pt(13)
+
+def add_para(text, bold=False, size=None, color=None, alignment=None):
+ p = doc.add_paragraph()
+ run = p.add_run(text)
+ run.font.name = '等线'
+ run.bold = bold
+ if size: run.font.size = Pt(size)
+ if color: run.font.color.rgb = color
+ if alignment: p.alignment = alignment
+ return p
+
+def add_radar_table(data):
+ """添加六维雷达数据表格"""
+ table = doc.add_table(rows=1, cols=2)
+ table.style = 'Light Grid Accent 1'
+ hdr = table.rows[0].cells
+ hdr[0].text = '维度'
+ hdr[1].text = '数值'
+ for d in data:
+ row = table.add_row().cells
+ row[0].text = d['name']
+ val = d['value']
+ row[1].text = f"{'█' * (val//10)}{'░' * (10-val//10)} {val}"
+ doc.add_paragraph()
+
+# 数据
+mathematicians = {
+ "信仰者": {
+ "desc": "把数学当信仰的人——不为名不为利,只为真理本身",
+ "members": [
+ {
+ "name": "高斯 · 定理工匠", "title": "数学王子", "era": "1777-1855,德国", "color": "#4A90D9",
+ "radar": [
+ {"name": "严谨演绎", "value": 98}, {"name": "直觉洞察", "value": 90},
+ {"name": "原始创造", "value": 95}, {"name": "钻研耐力", "value": 92},
+ {"name": "传播布道", "value": 40}, {"name": "实战应用", "value": 75}
+ ],
+ "special": "极机型:严谨98 · 盲区:传播40 · 必杀技:完美证明",
+ "motto": "他不出手则已,一出手就是一篇完美的证明",
+ "bio": """高斯3岁纠正父亲的工资账目,10岁秒算1到100——这些故事太著名了,以至于听起来像传说。但真正体现高斯钻研精神的,是他19岁那年做的一件事。
+
+欧几里得以来两千年,没人能用尺规作出正十七边形。19岁的高斯坐在哥廷根大学的宿舍里,花了一整夜,证明了这个作图是可能的,并且给出了精确的作图方法。这不是"灵光一闪"的天才故事——他花了整整一夜,在纸上反复推演,一步一步逼近那个隐藏在圆背后的优雅结构。
+
+他一生以此为傲。遗嘱里唯一的要求:在墓碑上刻正十七边形。后来工匠说"太难刻了,看起来跟圆没区别",但没有这句话更准确了——高斯的钻研,外人根本看不出跟普通努力有什么区别。
+
+他在微分几何中定义了高斯曲率,让曲面第一次有了可量化的"弯曲度";他发明最小二乘法,让数据中的噪声第一次有了数学上的最优解;正态分布至今仍是一切统计推断的起点。但他最震撼的事迹,是一件"没有发表"的事。
+
+高斯的书信里藏着秘密:他大概在1817年就发现了非欧几何——一个完全不同于欧几里得的几何体系,在那里三角形内角和不等于180度,平行线会相交。但他一个字都没发表。理由是什么?他在给朋友的信中写道:"我怕惊扰了那些蠢人的蜂巢。"
+
+他怕被别人骂,于是他选择不发表。整整30年。30年后,俄国人罗巴切夫斯基和匈牙利人鲍耶分别独立发表了非欧几何,成为"发现者"。高斯没有争辩,他的钻研从来不为"优先权"——他为的是自己心里那条完美的逻辑链条。
+
+这就是定理工匠:他不出手则已,一出手就是一篇完美的证明。至于别人知不知道?那是别人的事。"""
+ },
+ {
+ "name": "祖冲之 · 穷竭守尺人", "title": "算不烂的守尺人", "era": "429-500,中国南朝", "color": "#C47F3A",
+ "radar": [
+ {"name": "严谨演绎", "value": 90}, {"name": "直觉洞察", "value": 65},
+ {"name": "原始创造", "value": 70}, {"name": "钻研耐力", "value": 100},
+ {"name": "传播布道", "value": 55}, {"name": "实战应用", "value": 85}
+ ],
+ "special": "极机型:钻研100 · 盲区:直觉65 · 必杀技:算筹穷竭",
+ "motto": "他不知道自己算对了没有,因为没人比他更准",
+ "bio": """南北朝是什么时代?战乱、分裂、人命如草。祖冲之在这样的时代里,坐在一盏油灯下,手边一堆小木棍——叫算筹——一根一根摆来摆去,一算就是大半辈子。
+
+他要算一个数字:圆周率 π。
+
+他把π算到了3.1415926到3.1415927之间。你可能会想:这有什么?现在计算机一秒钟能算几百万位。但你要知道,祖冲之用的是算筹——一种靠在地面上摆弄小棍来进行的计算方式。他没有计算器,没有纸笔(当时的纸又贵又粗),连像样的书桌都没有。他就蹲在地上,摆出几万根算筹,记录中间结果,错了重来——而且他永远不知道自己的答案是不是"对的",因为没有人比他算得更准。
+
+他算了多久?史书没写。但那个精度——3.1415926——一千一百多年后才被打破。
+
+他还给出了密率 355/113。这个分数之美,令人窒息:分母113,分子355,恰好取自113355这六位数。一个简洁到不像手算时代的产物,欧洲人直到16世纪才重新发现它。
+
+他同时也是一流的工程师和天文学家。他制定了《大明历》,测算出回归年仅差46秒;他改良了指南车——一种靠齿轮传动保持方向的机械装置,在战场上用来辨别方位。在他留下的文字里,天文、历法、数学、机械浑然一体——在那个时代,真正的钻研者不分文理。
+
+他用算筹对抗时间。一千年后,世上还有人记得他算的那个数。这不是因为他比天才更天才——是因为他愿意花一辈子,把一件事算到最准。"""
+ },
+ {
+ "name": "欧拉 · 公式狂僧", "title": "数学史上的永动机", "era": "1707-1783,瑞士/俄国", "color": "#C47F3A",
+ "radar": [
+ {"name": "严谨演绎", "value": 88}, {"name": "直觉洞察", "value": 85},
+ {"name": "原始创造", "value": 98}, {"name": "钻研耐力", "value": 100},
+ {"name": "传播布道", "value": 85}, {"name": "实战应用", "value": 80}
+ ],
+ "special": "极机型:钻研100+创造98 · 盲区:无(六边形战士) · 必杀技:永动口述",
+ "motto": "停止计算的那一刻,才是停止生命的那一刻",
+ "bio": """数学史上最高产的人是谁?欧拉。886篇论文,死后留下的手稿还够出版50年。但这不是最惊人的。
+
+28岁,右眼失明。59岁,双目全盲。欧拉说:"视力没了,心里的数学更清楚了。"他不是在说漂亮话——失明之后,他的产出翻倍了。全部靠心算,口述,让助手誊写。
+
+想象一下:一个盲人在脑海里推演复杂的微积分、微分方程、无穷级数,不需要纸,不需要黑板,甚至不需要看到任何符号——整个推导过程在大脑中像流水一样流过。他的家人说,他常抱着刚出生的婴儿,腿上放着一沓稿纸,一边哄孩子一边口述定理。13个孩子,5个存活,家中的吵闹可想而知——但他算进去了。
+
+柯尼斯堡有七座桥,当地的市民有一个奇怪的爱好——散步时能不能每座桥只走一次?欧拉听了这个问题,觉得很有趣。他没有画图——他发明了图论。他把桥变成线,把陆地变成点,把整个城市变成一幅图。他证明了这件事不可能做到,然后顺手定义了一个全新的数学领域。今天你用的导航、你刷的社交网络、你坐的高铁调度——底层都有图论的影子。
+
+另一次,他写下了 e^(iπ) + 1 = 0。就这么短,但里面装了数学里最重要的五个常数——e、i、π、1、0。后人称之为"最美数学公式"。而他写下它的时候,不过是某一天推演到一半,随手记下来的。
+
+函数符号 f(x)、圆周率 π、自然常数 e、虚数单位 i、求和符号 ∑——这些沿用至今的数学记号,都是欧拉定的。不是因为他想"建立一套符号体系",而是因为他写的论文太多了,不得不想出一套方便的简写。他顺手就定义了整个数学界的通用语言。
+
+这就是公式狂僧:他不是因为"想赢"才写论文,而是因为停不下来。数学不是他的职业,是他的呼吸。"""
+ },
+ {
+ "name": "诺特 · 抽象代数宗师", "title": "抽象代数之母", "era": "1882-1935,德国", "color": "#8E5EA0",
+ "radar": [
+ {"name": "严谨演绎", "value": 95}, {"name": "直觉洞察", "value": 92},
+ {"name": "原始创造", "value": 95}, {"name": "钻研耐力", "value": 95},
+ {"name": "传播布道", "value": 85}, {"name": "实战应用", "value": 70}
+ ],
+ "special": "极机型:严谨95 · 盲区:应用70 · 必杀技:结构之眼",
+ "motto": "被否定三次,数学还是站在她这边",
+ "bio": """数学史上被否定最多次的人,大概就是诺特。
+
+她是女人——在20世纪初的德国大学里,女性连旁听资格都没有。她靠自学考过了所有课程,终于被允许参加考试,获得了博士学位。然后呢?没有学校肯雇她。
+
+她的父亲是数学教授,私下资助她在哥廷根大学"义务讲课"——没有头衔、没有薪酬、没有办公室。一教就是好几年。这期间她创立了"诺特学派",学生自发挤满她的课堂,这些人后来统治了现代代数学。但学校依然不给薪水。第一次否定。
+
+她研究的抽象代数,在当时的数学家看来"几乎不属于数学"——太抽象了,连符号都看不懂。有人嘲讽说:"这跟数学有什么关系?她到底在研究什么?"第二次否定。
+
+1915年她证明了诺特定理——物理学中每一种对称性,都对应一个守恒定律。能量守恒对应时间平移对称性,动量守恒对应空间平移对称性。这个定理是现代物理学的基石,没有它,杨振宁的规范场论无从出发。爱因斯坦亲自写信为她争取教职,称她为"自女性接受高等教育以来最杰出的数学天才"。第三次否定,破开了。
+
+她最终拿到了教授职位——但四年后就因纳粹政策被驱逐。她去了美国,又在短短几年内培养了一批新的弟子,直到53岁因手术并发症去世。
+
+被否定三次,她还是把最抽象的数学,讲得连门外汉都想入门。她的钻研不需要任何人来认证——因为数学永远站在她这边。"""
+ }
+ ]
+ },
+ "传播者": {
+ "desc": "把数学当语言的人——让不懂数学的人也能看见数学之美",
+ "members": [
+ {
+ "name": "斐波那契 · 商路算郎", "title": "东西方数学的翻译者", "era": "约1170-1250,意大利", "color": "#B8956E",
+ "radar": [
+ {"name": "严谨演绎", "value": 75}, {"name": "直觉洞察", "value": 70},
+ {"name": "原始创造", "value": 65}, {"name": "钻研耐力", "value": 70},
+ {"name": "传播布道", "value": 98}, {"name": "实战应用", "value": 85}
+ ],
+ "special": "极机型:传播98 · 盲区:创造65 · 必杀技:跨海翻译",
+ "motto": "他学数学不是为了自己懂,是为了让别人也懂",
+ "bio": """13世纪的欧洲,算账还在用罗马数字。你试试用 VII 乘以 XLII——光把这两个字写在纸上就要想半天。当时欧洲的商人记账是噩梦。
+
+斐波那契是一个意大利商人的儿子,从小跟着父亲在北非经商。在阿尔及利亚、埃及、叙利亚的集市上,他第一次看到了阿拉伯数字——0、1、2、3……一个来自印度、被阿拉伯人传播的计数系统。他用这些数字算了一笔账,立刻意识到:如果欧洲人能学会这套数字,做生意的速度快十倍。
+
+于是他写了一本书,叫《算盘书》。把阿拉伯数字和十进制计数法系统引入欧洲。这本书里几乎每一道例题都是商业场景:利润分摊、货币兑换、货物计量。他没有创造什么新定理——他只是从一个文明把工具搬运到了另一个文明。
+
+但在这本书里有一道不起眼的练习题:假设一对兔子每月生一对小兔子,小兔子两个月后也能生——一年后有多少对兔子?答案是 1, 1, 2, 3, 5, 8, 13, 21……后来人们发现,这个数列无处不在:松果的螺旋、向日葵的种子排列、鹦鹉螺的壳——都藏着它。
+
+斐波那契本人大概从没想过"兔子问题"会变成他的名片。他只是一个在东西方之间来回走动的商人,看见了好工具,就把它翻译过来,讲给需要的人听。"""
+ },
+ {
+ "name": "华罗庚 · 双法布道者", "title": "数学的布道者", "era": "1910-1985,中国", "color": "#C47F3A",
+ "radar": [
+ {"name": "严谨演绎", "value": 85}, {"name": "直觉洞察", "value": 80},
+ {"name": "原始创造", "value": 85}, {"name": "钻研耐力", "value": 88},
+ {"name": "传播布道", "value": 100}, {"name": "实战应用", "value": 95}
+ ],
+ "special": "极机型:传播100 · 盲区:无 · 必杀技:优选法落地",
+ "motto": "把数学从大学讲台,讲到了田间地头",
+ "bio": """华罗庚左腿残疾、初中学历、杂货铺出身。这三个标签放在任何时代,都意味着"不可能成为数学家"。但他自学了《微积分》《数论》,22岁时在上海《科学》杂志发表了论文,被清华数学系主任熊庆来发现而引介破格入清华。后来去了剑桥,回国,成为一代数论大师。
+
+但华罗庚最令人震撼的选择,发生在功成名就之后。
+
+他不坐书斋了。他揣着粉笔,走遍中国23个省市区——不是去开学术会议,是去工厂、农村、矿山。给钢铁工人讲"如何用最少的实验次数找到最佳配方"(优选法),给工程队讲"如何安排工序让施工最快完成"(统筹法)。
+
+一个顶级数论学家,选择把数学翻译成每个人都能用上的语言。走到哪里讲到哪。黑板上的内容不再是抽象公式,而是泡茶的顺序、烧菜的火候。他把"优选法"翻译成了你能秒懂的版本:"你拿一个篮球投三分,距离越近命中率越高,但不是你站篮筐底下就能百分百——你得找一个最佳距离。"
+
+1985年,他在东京大学做完一场学术报告后心脏病发作,倒在讲台上。他说的最后一句话是:"我要工作到生命的最后一刻。"
+
+他一生的动力不是超越别人,是让更多人看见数学。这比任何一篇论文都更需要耐心和信念。"""
+ },
+ {
+ "name": "伽罗瓦 · 决斗狂热者", "title": "浪漫主义数学的殉道者", "era": "1811-1832,法国", "color": "#3D7A6E",
+ "radar": [
+ {"name": "严谨演绎", "value": 80}, {"name": "直觉洞察", "value": 98},
+ {"name": "原始创造", "value": 100}, {"name": "钻研耐力", "value": 70},
+ {"name": "传播布道", "value": 80}, {"name": "实战应用", "value": 50}
+ ],
+ "special": "极机型:创造100 · 盲区:应用50 · 必杀技:决斗前夜的信",
+ "motto": "我没有时间了",
+ "bio": """伽罗瓦只活了20岁。
+
+17岁,他奠定了群论的基础。这是一个全新的代数学分支——在此之前没有人想过"对称性可以变成一门数学语言",但伽罗瓦做到了。他将方程的可解性转化为群的结构问题,彻底改变了代数学的面貌。
+
+但他的论文投出去,被退回。没有大学肯收他。他的老师看不懂他在写什么。他的父亲被政治迫害而自杀。他想革命,进了监狱。
+
+1832年5月29日,他卷入了一场因情感纠葛引发的决斗。谁也不知道对方是谁、为了什么——只知道第二天天亮,他和对手会各自开枪。
+
+他做了一个疯狂的决定。
+
+决斗前夜,他没有睡觉。他坐下来,开始疯狂写信——写给朋友,写给数学界,写给未来。他把群论的完整思想塞进了一封长信里,在纸页空白处匆匆写道:"我没有时间了。"
+
+然后拂晓,他去决斗。第二天,他死了。
+
+这封信被他的弟弟保存了14年,才被数学家刘维尔发现。刘维尔读完震惊了——这个20岁的孩子,在生命最后一夜,把一种全新的数学思维方式扔给了未来。
+
+今天,"伽罗瓦理论"是每个数学系研究生的必修课。五次及以上方程为什么没有通用根式解?伽罗瓦用群论回答了这个问题,终结了一个困扰数学家近300年的悬案。
+
+他用死亡完成了最后一次传播。把真理寄给未来。"""
+ },
+ {
+ "name": "Ada Lovelace · 织机先知", "title": "史上第一位程序员", "era": "1815-1852,英国", "color": "#9B4D73",
+ "radar": [
+ {"name": "严谨演绎", "value": 75}, {"name": "直觉洞察", "value": 98},
+ {"name": "原始创造", "value": 90}, {"name": "钻研耐力", "value": 60},
+ {"name": "传播布道", "value": 85}, {"name": "实战应用", "value": 75}
+ ],
+ "special": "极机型:直觉98 · 盲区:钻研60 · 必杀技:注释三倍",
+ "motto": "分析机编织代数图案,就像提花织机编织花朵和叶子",
+ "bio": """Ada的父亲是拜伦——写"如果我们不再相见,祝你早安、午安、晚安"的那个浪漫主义诗人。她母亲怕她像父亲一样"疯",从小就让她学数学。母亲说:数学是最好的镇静剂,能把人绑在理性上。
+
+但Ada在数学里看见的不是镇静,而是巨大的想象力。
+
+1843年,28岁的她翻译了一篇关于巴贝奇"分析机"的论文。分析机是一台机械计算器,由齿轮和杠杆驱动——从未被造出来过。Ada觉得原文不够好,便在翻译时附上了自己的"注释"。
+
+这些注释比原文长了三倍。
+
+在这些注释里,她做了两件划时代的事。第一,她写出了计算伯努利数的完整步骤——今天被公认为人类历史上第一个计算机程序。在分析机还不存在的年代,她已经为不存在的机器编程了。第二,她提出了一个当时没人想到的观点:计算机不仅能算数,还能作曲、画画、处理任何可以用符号表示的东西。她写道:"分析机编织代数图案,就像提花织机编织花朵和叶子。"
+
+她看到的不是一台超级计算器,而是一台通用符号处理器——这个洞见比第一台电子计算机出现早了整整一百年。
+
+她36岁死于子宫癌。她的名字今天被用来命名了Ada编程语言。每年十月第二个星期二是"Ada Lovelace日"。
+
+她翻译了一篇论文,顺手发明了"编程"这个概念。"""
+ }
+ ]
+ },
+ "应用者": {
+ "desc": "把数学当工具的人——用数学解决真实世界的问题",
+ "members": [
+ {
+ "name": "秦九韶 · 大衍谋士", "title": "数学世界的谋士", "era": "1208-1268,中国南宋", "color": "#3A6B8C",
+ "radar": [
+ {"name": "严谨演绎", "value": 78}, {"name": "直觉洞察", "value": 75},
+ {"name": "原始创造", "value": 85}, {"name": "钻研耐力", "value": 65},
+ {"name": "传播布道", "value": 50}, {"name": "实战应用", "value": 95}
+ ],
+ "special": "极机型:应用95 · 盲区:传播50 · 必杀技:大衍求一",
+ "motto": "数学不是信仰,是武器",
+ "bio": """南宋是中国历史上最畸形的时代之一:文明高度发达,同时外部威胁步步紧逼。秦九韶就是一个典型的南宋"全才"——精通数学、音律、建筑、诗文、带兵打仗。
+
+他写了一本书叫《数书九章》,里面有81道应用题。不是抽象证明,是真实世界的数学:田亩测算、建筑工程、钱粮折算、军需调度。他提出了"大衍求一术"——今天叫中国剩余定理——用来解决"一个数除以三余二、除以五余三、除以七余二,这个数是多少"这类问题。这东西在今天被用于密码学,保护你在网上输入的每一行密码。
+
+但他的手不只握过笔。
+
+史书记载,秦九韶"性机巧、好奢侈",曾因私人恩怨毒杀亲信,也用自己设计的计算模型帮助官府平定叛乱。他的数学既能计算粮草,也能决定生死。他是一个什么样的人?数学家说他空前绝后,史官说他品行不端。这两个评价,可能都对。
+
+这正是大衍谋士的本质:数学不是信仰,不是美,是工具。可以用来造福,也可以用来杀人。他从未假装自己纯洁。"""
+ },
+ {
+ "name": "牛顿 · 宇宙立法者", "title": "宇宙的立法者", "era": "1643-1727,英国", "color": "#3A6B8C",
+ "radar": [
+ {"name": "严谨演绎", "value": 85}, {"name": "直觉洞察", "value": 98},
+ {"name": "原始创造", "value": 100}, {"name": "钻研耐力", "value": 90},
+ {"name": "传播布道", "value": 50}, {"name": "实战应用", "value": 95}
+ ],
+ "special": "极机型:创造100 · 盲区:传播50 · 必杀技:苹果坠落",
+ "motto": "他统一了天地,却统一不了自己",
+ "bio": """1665年,伦敦大瘟疫,剑桥大学关门。22岁的牛顿回到家乡伍尔斯索普庄园躲瘟疫。在接下来的18个月里,他一个人完成了:微积分的发明、万有引力定律的雏形、用棱镜分解白光的光学实验。数学史上最孤独、也最高产的一次隔离。
+
+1687年,他出版了《自然哲学的数学原理》。三条运动定律加万有引力定律,人类第一次用一套方程统一了天上和地上的一切运动——行星为什么绕着太阳转、苹果为什么掉下来、潮汐为什么涨落,全归入了同一套公式。这是人类智力史上最伟大的单项成就。
+
+但牛顿的另一面同样疯狂。
+
+他与莱布尼茨争夺微积分优先权。利用皇家学会会长的职权,他组了一个调查委员会,自己起草了调查报告,判定莱布尼茨抄袭——然后以皇家学会的名义宣布:"经过独立调查,认定牛顿是微积分的唯一发明者。"他自己判自己胜诉。
+
+晚年他沉迷炼金术和神学,试图用数学推算出上帝创世的日期。他留下的神学手稿比科学手稿还多,超过三百万字。他一生未婚。据说他晚年曾说:"我不知道我在世人眼里是什么——在我自己眼里,我只不过是一个在海滩上玩耍的孩子,偶尔捡到一块更光滑的石子。"
+
+这就是宇宙立法者:他用数学征服了世界,却从未和自己和解。"""
+ },
+ {
+ "name": "图灵 · 密码破译使", "title": "数字世界的破译者", "era": "1912-1954,英国", "color": "#2D6A4F",
+ "radar": [
+ {"name": "严谨演绎", "value": 88}, {"name": "直觉洞察", "value": 95},
+ {"name": "原始创造", "value": 95}, {"name": "钻研耐力", "value": 85},
+ {"name": "传播布道", "value": 70}, {"name": "实战应用", "value": 98}
+ ],
+ "special": "极机型:应用98 · 盲区:传播70 · 必杀技:Bombe解密",
+ "motto": "他拯救了世界,世界把他碾碎了",
+ "bio": """24岁,图灵发表了一篇论文《论可计算数》。在这篇论文里,他想象了一台"永远不存在的机器"——一条无限长的纸带、一个可以读写符号的装置、一套简单的状态转换规则。他证明了:这台最简单的假想机器,能计算任何可以被计算的东西。他用这台"图灵机"定义了"计算"本身。冯·诺依曼后来说,现代计算机架构里最关键的概念——"存储程序"——就来自图灵这篇论文。
+
+然后二战来了。德国人用了Enigma密码机——每天换一种加密方式,数量级大到手算永远破不了。图灵去了布莱切利庄园,和一群数学家、象棋冠军、填字游戏冠军一起,设计了一台叫Bombe的解密机。他们破译了Enigma。
+
+历史学家的共识是:这让二战提前两年结束,至少拯救了1400万人的生命。
+
+战后,图灵的贡献成为国家机密,封存了三十年。公众不知道他做过什么。1952年,他因同性恋被定罪,被迫接受化学阉割。两年后,他咬下一口涂了氰化物的苹果,去世。41岁。2013年,英国女王颁布皇家赦免。2019年,他登上了新版50英镑纸币。
+
+他的数学拦住了纳粹。他的国家没拦住偏见。这两个事实叠加在一起,比任何单独的悲剧或英雄叙事,都更沉重。"""
+ },
+ {
+ "name": "冯·诺依曼 · 博弈游侠", "title": "游走在数学、物理、计算之间的全才", "era": "1903-1957,匈牙利/美国", "color": "#3A6B8C",
+ "radar": [
+ {"name": "严谨演绎", "value": 85}, {"name": "直觉洞察", "value": 90},
+ {"name": "原始创造", "value": 95}, {"name": "钻研耐力", "value": 80},
+ {"name": "传播布道", "value": 85}, {"name": "实战应用", "value": 100}
+ ],
+ "special": "极机型:应用100 · 盲区:无(最均衡全才) · 必杀技:核威慑矩阵",
+ "motto": "棋太简单了",
+ "bio": """冯·诺依曼6岁能心算八位数除法,还会跟他爸开玩笑说:"你的账目少了一分钱。"8岁掌握微积分,22岁拿下博士学位。然后他开始在完全不同的领域之间跳来跳去,每一个都留下一座里程碑。
+
+他创立了博弈论。用数学框架分析"人在对抗中如何决策"——《博弈论与经济行为》成为经济学、政治学、军事战略的圣经。冷战期间的核威慑理论——美苏双方都知道对方有核弹、都知道对方知道自己有核弹——这层层嵌套的"我知道你知道"就出自博弈论的矩阵。
+
+他加入曼哈顿计划,设计了原子弹内爆的数学模型。不是造炸弹——是用方程算爆炸。
+
+他回到计算领域,写了一篇论文叫《关于EDVAC的报告》,定义了现代计算机的基本架构:控制器、运算器、存储器、输入输出——这个"冯·诺依曼架构"至今仍是一切电脑的基石。你现在读到这篇文章,底层的CPU就是这个架构的后代。
+
+有人问他:"你研究博弈论,是不是因为你喜欢下棋?"他回答:"不。我研究博弈论是为了理解核威慑。棋太简单了。"
+
+这就是博弈游侠:把世界当成一盘棋,而且他每一盘都赢了。"""
+ }
+ ]
+ },
+ "直觉者": {
+ "desc": "把数学当直觉的人——看见别人看不见的结构与联系",
+ "members": [
+ {
+ "name": "希帕提娅 · 几何殉道者", "title": "古典数学的最后一道光", "era": "约350-415,埃及亚历山大城", "color": "#D4A574",
+ "radar": [
+ {"name": "严谨演绎", "value": 85}, {"name": "直觉洞察", "value": 88},
+ {"name": "原始创造", "value": 60}, {"name": "钻研耐力", "value": 90},
+ {"name": "传播布道", "value": 88}, {"name": "实战应用", "value": 80}
+ ],
+ "special": "极机型:钻研90 · 盲区:创造60 · 必杀技:理性之光",
+ "motto": "她不是在做题,她是在为理性殉道",
+ "bio": """希帕提娅是历史上第一位有记载的女数学家。她生活在公元4世纪的亚历山大城——彼时罗马帝国已经皈依了基督教,古典希腊文化正在被系统地清除。而亚历山大城依然是古代世界知识的最后堡垒:它的大图书馆里藏着希腊、埃及、巴比伦的所有智慧。
+
+希帕提娅继承了父亲泰昂的学术衣钵,在亚历山大的学院讲授柏拉图、亚里士多德、欧几里得。她是新柏拉图学派的领袖,学生从帝国各地慕名而来。她改良了星盘和比重计——星盘用来测量天体高度和纬度,比重计用来测液体密度——这两种仪器被后人沿用了1400多年。
+
+但她的位置太危险了:一个异教徒、一个女人、一个公开讲学的人、一个在宗教冲突中保持理性中立的人。415年,一群被主教煽动的暴民把她从马车上拖下来,拖进教堂,用陶片活活刮死,然后把她的尸体撕碎、焚烧。
+
+她死后,亚历山大图书馆被毁,古典希腊数学随之终结。希帕提娅的被杀被视为"古典时代的终结"标志之一——从那天起,黑暗时代开始了。
+
+她不是在做题,她是在为理性殉道。人类杀死她的同时,杀死了自己保存了七百年的知识体系。"""
+ },
+ {
+ "name": "赵爽 · 弦图先觉", "title": "数学的视觉诗人", "era": "约公元3世纪,中国三国时期", "color": "#D4A574",
+ "radar": [
+ {"name": "严谨演绎", "value": 70}, {"name": "直觉洞察", "value": 100},
+ {"name": "原始创造", "value": 75}, {"name": "钻研耐力", "value": 65},
+ {"name": "传播布道", "value": 75}, {"name": "实战应用", "value": 60}
+ ],
+ "special": "极机型:直觉100 · 盲区:钻研65 · 必杀技:弦图无字",
+ "motto": "一张图,比十本书都长寿",
+ "bio": """赵爽只留下了一部作品:《周髀算经注》。他只注了一本书。
+
+但就在这本书里,他做了一件让整个数学史惊叹的事。
+
+勾股定理——直角三角形两直角边的平方和等于斜边的平方——如何证明?赵爽没有写长篇大论。他画了一张图:"弦图"。
+
+在一个正方形里,用四个直角三角形拼出一个中间的小正方形。然后他写了一句话:"按弦图,又可以勾股相乘为朱实二,倍之为朱实四,以勾股之差自相乘为中黄实,加差实亦成弦实。"
+
+翻译成现代话:看看这张图,你就看见了。
+
+他让历史上最难解释的定理之一,变成了一张"看一眼就懂"的图。这不是因为他的推导简单——恰恰相反,能用一张图让人"看见"一个复杂的真理,需要比写出十页证明更深的直觉。
+
+2002年,国际数学家大会的会徽选取了赵爽的弦图。1800年后,全世界数学家公认:这张图是人类数学史上最美的证明之一。
+
+这就是弦图先觉:不是算出来的,是看出来的。一张图,比十本书都长寿。"""
+ },
+ {
+ "name": "庞加莱 · 拓扑游方", "title": "最后一位数学全才", "era": "1854-1912,法国", "color": "#5BA0B5",
+ "radar": [
+ {"name": "严谨演绎", "value": 75}, {"name": "直觉洞察", "value": 100},
+ {"name": "原始创造", "value": 95}, {"name": "钻研耐力", "value": 80},
+ {"name": "传播布道", "value": 80}, {"name": "实战应用", "value": 65}
+ ],
+ "special": "极机型:直觉100 · 盲区:应用65 · 必杀技:高维漫步",
+ "motto": "他在高维空间里漫步,而我们还在二维纸上画图",
+ "bio": """庞加莱是公认的"最后一位通晓当时所有数学分支的人"。在他之后,数学变得太过庞大,再也没有一个人能掌握全部。
+
+他的几何直觉惊世骇俗。有一次,庞加莱在准备一次关于富克斯函数的演讲时,花了几个月苦思冥想找不到突破口。然后某一天,他刚踏上一辆公交车的台阶——据他自己回忆——突然就"看见"了富克斯函数与双曲几何之间的联系。不是推算出来的,是他"看见"的。他在上车的一瞬间,整个结构在大脑中浮现了。
+
+这不是一个关于"天才"的故事——这是一个关于"用身体感受数学"的故事。庞加莱在回忆录中写道:"我停下来,继续和车上的人说话,但心里知道我已经抓住了答案。我甚至没有马上把它写下来——我知道它不会再跑掉了。"
+
+他提出的"庞加莱猜想":如果一个三维闭曲面上的每一条闭曲线都能缩成一个点,那么这个曲面就是一个三维球面。这个猜想困扰了数学界整整100年,直到2003年才被俄罗斯数学家佩雷尔曼证明。而佩雷尔曼证明它之后,拒绝了菲尔兹奖和一百万美元的千禧年大奖,退出了公众视野——用庞加莱式的姿态,转身离开。
+
+这就是拓扑游方:他们在高维空间里漫步,而我们还在二维纸上画图。"""
+ },
+ {
+ "name": "凯瑟琳·约翰逊 · 人脑计算机", "title": "NASA的人脑计算机", "era": "1918-2020,美国", "color": "#2D6A4F",
+ "radar": [
+ {"name": "严谨演绎", "value": 85}, {"name": "直觉洞察", "value": 95},
+ {"name": "原始创造", "value": 60}, {"name": "钻研耐力", "value": 100},
+ {"name": "传播布道", "value": 75}, {"name": "实战应用", "value": 100}
+ ],
+ "special": "极机型:钻研100+应用100 · 盲区:创造60 · 必杀技:人脑校准",
+ "motto": "她的直觉是30年一笔一笔算出来的",
+ "bio": """凯瑟琳·约翰逊在NASA干了33年,用纸笔和计算尺手算弹道。
+
+1961年,美国第一次载人航天飞行。宇航员艾伦·谢泼德坐进火箭——他是一流的飞行员,但他不信任那台刚引进的IBM计算机。他在发射前说了一句话:"让那个女孩核对一下数字。"
+
+"那个女孩"就是凯瑟琳·约翰逊。她用的工具是:纸、铅笔、计算尺。一步一个数字,从头算到尾。她算对了。谢泼德飞了。
+
+1962年,约翰·格伦要上轨道了。这次弹道计算已经在IBM上跑过了,但格伦在发射前点名:"让她重新手算一遍——她说行,我就飞。"凯瑟琳的手算轨道确认了IBM的结果。格伦成了美国第一个绕地球飞行的宇航员。
+
+她和一群被称为"穿着裙子的计算机"的黑人女性数学家们,在种族隔离的年代,坐在NASA的"有色人种计算部"里,用人脑完成了阿波罗登月的轨道计算。
+
+但凯瑟琳最让人震撼的不是她的计算速度——而是她的直觉。33年不间断的手算,让她对数字形成了一种近乎本能的判断力。她的同事说:"我们看一张弹道轨迹图,只看得到一堆数字。凯瑟琳看同一张图,她说——这个数不对。"不是算出来的——是感觉出来的。但这种感觉,是33年每天8小时一笔一笔磨出来的。
+
+2020年她101岁辞世。她的一生恰好穿越了计算从纸笔走向AI的整个世纪——她既是纸笔计算的终点,也是数字时代的起点。"""
+ }
+ ]
+ }
+}
+
+
+# ==================== 开始写入 ====================
+
+# 封面页
+doc.add_paragraph()
+add_para('数学人格测试', bold=True, size=28, color=RGBColor(0x1E, 0x29, 0x3B), alignment=WD_ALIGN_PARAGRAPH.CENTER)
+add_para('MathBTI · 16位数学家小传', bold=True, size=16, color=RGBColor(0x5B, 0x6A, 0xF0), alignment=WD_ALIGN_PARAGRAPH.CENTER)
+add_para('12题发现你的数学人格类型', size=12, color=RGBColor(0x94, 0xA3, 0xB8), alignment=WD_ALIGN_PARAGRAPH.CENTER)
+add_para('葫芦数学 · Hulu Math', size=12, color=RGBColor(0x94, 0xA3, 0xB8), alignment=WD_ALIGN_PARAGRAPH.CENTER)
+doc.add_page_break()
+
+# 目录提示
+add_para('目 录', bold=True, size=16, color=RGBColor(0x1E, 0x29, 0x3B), alignment=WD_ALIGN_PARAGRAPH.CENTER)
+add_para('')
+for clan_name in ["信仰者", "传播者", "应用者", "直觉者"]:
+ clan = mathematicians[clan_name]
+ add_para(f'第{"一二三四"[["信仰者","传播者","应用者","直觉者"].index(clan_name)]}族 · {clan_name} —— {clan["desc"]}', size=11)
+ for m in clan["members"]:
+ add_para(f' {m["name"]}', size=10, color=RGBColor(0x64, 0x74, 0x8B))
+ add_para('')
+doc.add_page_break()
+
+# 正文
+for clan_idx, (clan_name, clan) in enumerate(mathematicians.items()):
+ # 族群封面页
+ clan_num = '一二三四'[clan_idx]
+ doc.add_paragraph()
+ add_para(f'第{clan_num}族', bold=True, size=14, color=RGBColor(0x5B, 0x6A, 0xF0), alignment=WD_ALIGN_PARAGRAPH.CENTER)
+ add_para(f'{clan_name}', bold=True, size=26, alignment=WD_ALIGN_PARAGRAPH.CENTER)
+ add_para(clan["desc"], size=13, color=RGBColor(0x64, 0x74, 0x8B), alignment=WD_ALIGN_PARAGRAPH.CENTER)
+ doc.add_paragraph()
+
+ for m in clan["members"]:
+ # 分割线
+ doc.add_paragraph('─' * 50)
+
+ # 标题
+ add_para(m["name"], bold=True, size=16, color=RGBColor(0x1E, 0x29, 0x3B))
+
+ # 人物档案
+ add_para(f'称号:{m["title"]} | 年代:{m["era"]}', size=10, color=RGBColor(0x64, 0x74, 0x8B))
+ add_para(f'必杀技:{m["special"].split("·")[-1].strip()} | 签语:{m["motto"]}', size=10, color=RGBColor(0x64, 0x74, 0x8B))
+
+ doc.add_paragraph()
+
+ # 六维雷达数据
+ add_para('【六维雷达卡】', bold=True, size=11)
+ for rd in m["radar"]:
+ val = rd["value"]
+ bar = '█' * (val // 10) + '░' * (10 - val // 10)
+ add_para(f' {rd["name"]: <6} {bar} {val}', size=10)
+ doc.add_paragraph()
+
+ # 能力说明
+ add_para(m["special"], size=9, color=RGBColor(0x94, 0xA3, 0xB8))
+ doc.add_paragraph()
+
+ # 小传正文
+ add_para('【人物小传】', bold=True, size=11)
+ for para in m["bio"].strip().split('\n\n'):
+ add_para(para.strip(), size=11)
+
+ doc.add_paragraph()
+
+ # 族群结束,非最后一族加换页
+ if clan_idx < 3:
+ doc.add_page_break()
+
+# 总览表
+doc.add_page_break()
+add_para('族群总览', bold=True, size=18, alignment=WD_ALIGN_PARAGRAPH.CENTER)
+doc.add_paragraph()
+
+overview_table = doc.add_table(rows=5, cols=7)
+overview_table.style = 'Light Grid Accent 1'
+
+headers = ['族群', '特质', '代表人物', '严谨', '直觉', '创造', '钻研', '传播', '应用']
+# Fix: we have 7 columns but 9 headers? Let me recount. The rows=5, cols=7 would need 7 headers.
+# Actually, let me simplify - just make a proper table.
+
+# Let me replace with a simpler approach
+doc.add_paragraph()
+
+# Simple summary
+for clan_name in ["信仰者", "传播者", "应用者", "直觉者"]:
+ clan = mathematicians[clan_name]
+ members_str = '、'.join([m["name"].split('·')[0].strip() for m in clan["members"]])
+ add_para(f'{clan_name}:{clan["desc"]}', bold=True, size=11)
+ add_para(f'成员:{members_str}', size=10, color=RGBColor(0x64, 0x74, 0x8B))
+ add_para('')
+
+# 页尾
+doc.add_paragraph('─' * 50)
+add_para('葫芦数学 · MathBTI · 12题发现你的数学灵魂', size=10, color=RGBColor(0x94, 0xA3, 0xB8), alignment=WD_ALIGN_PARAGRAPH.CENTER)
+
+# 保存
+output_path = r'c:\Users\YZS\Desktop\new\data\novel\数学人格测试_16位数学家小传.docx'
+doc.save(output_path)
+print(f'文档已导出至: {output_path}')
diff --git a/static/css/desktop.css b/static/css/desktop.css
new file mode 100644
index 0000000..32455fc
--- /dev/null
+++ b/static/css/desktop.css
@@ -0,0 +1,555 @@
+*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}
+body{font-family:system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Hiragino Sans GB','Microsoft YaHei','Noto Sans SC',sans-serif;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;background:#EDEBE6;color:#3A3632;overflow-x:hidden}
+/* 网格噪点背景纹理 — 模拟纸张/教科书的微质感 */
+body::before{content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;background-image:radial-gradient(circle,#C8C4BD .5px,transparent .5px);background-size:18px 18px;opacity:.35}
+::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:#C8C4BD;border-radius:3px}
+.app-wrapper{max-width:1440px;margin:0 auto;height:100vh;display:flex;flex-direction:column;position:relative;background:#F7F5F1}
+.mobile-topbar{display:none;align-items:center;padding:12px 16px;background:#FDFCFA;border-bottom:1px solid #D6D1CA;z-index:30}
+.mobile-topbar .hamburger{width:36px;height:36px;border-radius:8px;border:1px solid #D6D1CA;background:#FDFCFA;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:18px;color:#8A8580}
+.mobile-topbar .topbar-logo{display:flex;align-items:center;gap:8px;margin-left:12px}
+.mobile-topbar .topbar-logo-icon{width:32px;height:32px;border-radius:8px;background:linear-gradient(135deg,#808C95,#9AA5AE);display:flex;align-items:center;justify-content:center;font-weight:800;font-size:16px;color:#fff}
+.mobile-topbar .topbar-logo-text{font-size:16px;font-weight:700;color:#3A3632}
+.app-layout{display:flex;flex:1;overflow:hidden}
+
+/* Sidebar — 直角 + 细线 */
+.sidebar{width:240px;min-width:240px;background:#FDFCFA;border-right:1px solid #D6D1CA;display:flex;flex-direction:column;z-index:20;transition:transform .3s;border-radius:0}
+.sidebar-logo{padding:20px 20px 16px;display:flex;align-items:center;gap:12px}
+.sidebar-logo-icon{width:42px;height:42px;border-radius:8px;background:linear-gradient(135deg,#808C95,#9AA5AE);display:flex;align-items:center;justify-content:center;font-weight:800;font-size:20px;color:#fff}
+.sidebar-logo-text{font-size:18px;font-weight:700;color:#3A3632}
+.sidebar-search{padding:0 16px 12px}
+.sidebar-search input{width:100%;padding:8px 12px 8px 32px;border-radius:6px;border:1px solid #D6D1CA;font-size:12px;outline:none;background:#F7F5F1 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' fill='%23ABA6A1'%3E%3Cpath d='M10.5 9.5l3 3-1.5 1.5-3-3A5.5 5.5 0 117 12.5a5.5 5.5 0 013.5-3z'/%3E%3C/svg%3E") 10px center no-repeat;transition:border-color .2s}
+.sidebar-search input:focus{border-color:#808C95;background-color:#FDFCFA}
+.sidebar-nav{flex:1;padding:8px 12px;display:flex;flex-direction:column;gap:3px;overflow-y:auto}
+.nav-item{display:flex;align-items:center;gap:12px;padding:10px 14px;border-radius:6px;cursor:pointer;font-size:14px;font-weight:500;color:#8A8580;transition:all .15s;border:none;background:none;width:100%;text-align:left}
+.nav-item:hover{background:#EAE7E2;color:#808C95}
+.nav-item.active{background:#E6E9EC;color:#657078;font-weight:600}
+.nav-item .nav-icon{font-size:17px;width:24px;text-align:center;flex-shrink:0}
+.sidebar-footer{padding:16px;border-top:1px solid #EAE7E2;display:flex;align-items:center;gap:10px}
+.sidebar-avatar{width:36px;height:36px;border-radius:50% 50% 50% 50% / 60% 60% 40% 40%;box-shadow:inset 0 -3px 0 rgba(0,0,0,.1);display:flex;align-items:center;justify-content:center;font-size:16px;color:#fff;font-weight:700;flex-shrink:0}
+.sidebar-user-name{font-size:13px;font-weight:600;color:#3A3632}
+.sidebar-user-id{font-size:11px;color:#ABA6A1}
+
+/* Main */
+.main-content{flex:1;overflow-y:auto;padding:28px 36px}
+.main-content-inner{max-width:1000px}
+.page-header{margin-bottom:24px}
+.page-title{font-size:24px;font-weight:700;color:#3A3632}
+.page-subtitle{font-size:13px;color:#ABA6A1;margin-top:4px}
+.page{display:none}.page.active{display:block}
+
+/* Cards — 8px圆角 + 纯细线无阴影 */
+.video-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}
+@media(max-width:1200px){.video-grid{grid-template-columns:repeat(2,1fr)}}
+@media(max-width:640px){.video-grid{grid-template-columns:1fr}}
+.video-card{background:#FDFCFA;border-radius:8px;overflow:hidden;cursor:pointer;transition:all .15s;border:1px solid #D6D1CA}
+.video-card:hover{border-color:#808C95;background:#F0EEEA}
+.video-card-cover{aspect-ratio:16/9;display:flex;align-items:center;justify-content:center;font-size:36px;position:relative}
+.video-card-duration{position:absolute;bottom:8px;right:8px;background:rgba(58,54,50,.65);color:#fff;font-size:11px;padding:2px 8px;border-radius:4px;font-weight:500}
+.video-card-body{padding:14px}
+.video-card-title{font-size:14px;font-weight:600;line-height:1.4;color:#3A3632;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;margin-bottom:8px}
+.video-card-meta{display:flex;align-items:center;gap:6px;font-size:12px;color:#ABA6A1}
+.video-card-tag{font-size:11px;padding:3px 8px;border-radius:4px;background:#EAE7E2;color:#808C95;font-weight:500}
+.video-card-author{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
+.knowledge-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:16px}
+@media(max-width:640px){.knowledge-grid{grid-template-columns:1fr}}
+.knowledge-card{background:#FDFCFA;border-radius:8px;overflow:hidden;cursor:pointer;transition:all .15s;border:1px solid #D6D1CA}
+.knowledge-card:hover{border-color:#808C95}
+.knowledge-card-visual{height:120px;display:flex;align-items:center;justify-content:center;font-size:42px;position:relative;overflow:hidden}
+.knowledge-card-question{position:absolute;bottom:12px;left:16px;right:16px;font-size:14px;font-weight:700;color:#fff;text-shadow:0 1px 3px rgba(0,0,0,.3);line-height:1.3}
+.knowledge-card-body{padding:14px}
+.knowledge-card-title{font-size:15px;font-weight:700;color:#3A3632;margin-bottom:6px}
+.knowledge-card-desc{font-size:12px;color:#8A8580;line-height:1.5;margin-bottom:8px}
+.knowledge-card-time{font-size:11px;color:#ABA6A1}
+
+/* Story/Sim */
+.story-container,.sim-container{background:#FDFCFA;border-radius:8px;padding:32px;border:1px solid #D6D1CA;min-height:400px}
+.story-stats{display:flex;gap:12px;margin-bottom:20px;flex-wrap:wrap}
+.story-stat{font-size:13px;padding:6px 14px;border-radius:8px;color:#fff;font-weight:600}
+.story-text{font-size:15px;line-height:1.8;color:#5A5550;min-height:120px;white-space:pre-line}
+.story-character{font-weight:700;color:#808C95;margin-bottom:8px;font-size:14px}
+.story-choices{display:flex;flex-direction:column;gap:10px;margin-top:20px}
+.story-choice{background:#F7F5F1;border:1px solid #D6D1CA;border-radius:8px;padding:14px 18px;font-size:14px;font-weight:600;cursor:pointer;transition:all .15s;text-align:left;color:#3A3632}
+.story-choice:hover{border-color:#808C95;background:#EAE7E2}
+.sim-stats{display:flex;gap:16px;margin-bottom:20px}
+.sim-stat{flex:1;background:#F7F5F1;border:1px solid #EAE7E2;border-radius:8px;padding:14px;text-align:center}
+.sim-stat-val{font-size:28px;font-weight:800;color:#3A3632}
+.sim-stat-label{font-size:11px;color:#8A8580;margin-top:4px}
+
+/* Filters */
+.filter-bar{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:20px}
+.filter-btn{padding:7px 14px;border-radius:6px;border:1px solid #D6D1CA;background:#FDFCFA;font-size:13px;font-weight:500;color:#8A8580;cursor:pointer;transition:all .15s;white-space:nowrap}
+.filter-btn:hover{border-color:#808C95;color:#808C95}
+.filter-btn.active{background:#808C95;border-color:#808C95;color:#fff;font-weight:600}
+
+/* Search results */
+.search-results{display:none;position:absolute;top:100%;left:0;right:0;background:#FDFCFA;border:1px solid #D6D1CA;border-radius:6px;z-index:40;max-height:300px;overflow-y:auto;margin-top:4px}
+.search-results.show{display:block}
+.search-result-item{padding:10px 16px;cursor:pointer;border-bottom:1px solid #EAE7E2;font-size:13px;display:flex;align-items:center;gap:8px;color:#3A3632}
+.search-result-item:hover{background:#EAE7E2}
+.search-result-item:last-child{border-bottom:none}
+.search-result-type{font-size:10px;padding:2px 6px;border-radius:3px;background:#E6E9EC;color:#657078;font-weight:600;flex-shrink:0}
+
+/* AI Toggle + Float merge — 合并为一个扇形展开按钮 */
+.float-hub{position:fixed;bottom:24px;right:24px;z-index:25;display:flex;flex-direction:column;align-items:flex-end;gap:8px}
+.float-hub-btn{width:48px;height:48px;border-radius:50%;border:none;background:#808C95;color:#fff;font-size:20px;cursor:pointer;transition:all .2s;display:flex;align-items:center;justify-content:center}
+.float-hub-btn:hover{background:#657078;transform:scale(1.05)}
+.float-hub-expand{display:none;flex-direction:column;align-items:flex-end;gap:8px}
+.float-hub.open .float-hub-expand{display:flex}
+.float-hub-child{width:44px;height:44px;border-radius:50%;border:none;cursor:pointer;font-size:18px;display:flex;align-items:center;justify-content:center;transition:all .2s;position:relative;text-decoration:none}
+.float-hub-child:hover{transform:scale(1.08)}
+.float-hub-child.ai{background:#A8907E;color:#fff}
+.float-hub-child.mbti{background:linear-gradient(135deg,#8A9BA8,#A0B1BD);color:#fff}
+.float-hub-label{position:absolute;right:52px;white-space:nowrap;font-size:11px;font-weight:600;color:#8A8580;background:#FDFCFA;border:1px solid #D6D1CA;padding:4px 10px;border-radius:4px;pointer-events:none;opacity:0;transition:opacity .2s}
+.float-hub-child:hover .float-hub-label{opacity:1}
+
+/* AI Panel */
+.ai-panel{position:fixed;top:0;right:0;bottom:0;width:360px;background:#FDFCFA;border-left:1px solid #D6D1CA;z-index:30;transform:translateX(100%);transition:transform .3s;display:flex;flex-direction:column}
+.ai-panel.open{transform:translateX(0)}
+.ai-panel-header{padding:16px;border-bottom:1px solid #EAE7E2;display:flex;align-items:center;justify-content:space-between;font-weight:600;font-size:14px;color:#3A3632}
+.ai-panel-close{width:32px;height:32px;border-radius:50%;border:none;background:#EAE7E2;cursor:pointer;font-size:16px;display:flex;align-items:center;justify-content:center;color:#8A8580}
+.ai-panel-messages{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:12px}
+.ai-panel-input-area{padding:12px 16px;border-top:1px solid #EAE7E2;display:flex;gap:8px}
+.ai-panel-input-area input{flex:1;padding:10px 14px;border-radius:8px;border:1px solid #D6D1CA;font-size:13px;outline:none;background:#F7F5F1}
+.ai-panel-input-area input:focus{border-color:#808C95}
+.ai-send-btn{min-width:40px;height:40px;border-radius:50%;border:none;background:#808C95;color:#fff;font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center}
+
+.chat-msg{display:flex;animation:fadeIn .3s ease}
+@keyframes fadeIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}
+.chat-msg.assistant{justify-content:flex-start}.chat-msg.user{justify-content:flex-end}
+.chat-bubble{max-width:85%;padding:10px 14px;border-radius:8px;font-size:13px;line-height:1.6;word-break:break-word}
+.chat-msg.assistant .chat-bubble{background:#EAE7E2;color:#3A3632;border-bottom-left-radius:2px}
+.chat-msg.user .chat-bubble{background:#808C95;color:#fff;border-bottom-right-radius:2px}
+
+/* Modal — 16px圆角 */
+.modal-overlay{display:none;position:fixed;inset:0;background:rgba(58,54,50,.4);z-index:100;justify-content:center;align-items:center}
+.modal-overlay.show{display:flex}
+.modal-content{background:#FDFCFA;border-radius:16px;padding:28px;max-width:560px;width:90vw;max-height:80vh;overflow-y:auto;border:1px solid #D6D1CA;animation:modalIn .2s ease}
+@keyframes modalIn{from{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}
+.modal-close-btn{position:absolute;top:16px;right:16px;width:32px;height:32px;border-radius:50%;border:none;background:#EAE7E2;cursor:pointer;font-size:16px;display:flex;align-items:center;justify-content:center;color:#8A8580}
+
+.fav-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}
+@media(max-width:1200px){.fav-grid{grid-template-columns:repeat(2,1fr)}}
+@media(max-width:640px){.fav-grid{grid-template-columns:1fr}}
+.fav-card{background:#FDFCFA;border-radius:8px;padding:16px;border:1px solid #D6D1CA;position:relative}
+.fav-card-remove{position:absolute;top:10px;right:10px;width:28px;height:28px;border-radius:50%;border:none;background:#F5EAEA;color:#C49595;cursor:pointer;font-size:14px;display:flex;align-items:center;justify-content:center}
+
+.spinner{width:32px;height:32px;border:3px solid #EAE7E2;border-top-color:#808C95;border-radius:50%;animation:spin .7s linear infinite;margin:40px auto}
+@keyframes spin{to{transform:rotate(360deg)}}
+.section-title{font-size:16px;font-weight:700;margin-bottom:16px;color:#3A3632}
+
+/* Banner */
+.banner{background:linear-gradient(135deg,#808C95,#9AA5AE);border-radius:8px;padding:28px 32px;color:#fff;margin-bottom:24px;position:relative;overflow:hidden}
+.banner::after{content:"";position:absolute;top:-30px;right:-30px;width:120px;height:120px;border-radius:50%;background:rgba(255,255,255,.08)}
+.banner-title{font-size:22px;font-weight:800;margin-bottom:8px;position:relative;z-index:1}
+.banner-text{font-size:14px;opacity:.85;position:relative;z-index:1}
+
+/* ========== Desmos-style Home Page ========== */
+.ds-hero{text-align:center;padding:40px 20px 20px}
+.ds-hero-title{font-size:28px;font-weight:800;color:#3A3632;margin-bottom:8px;letter-spacing:-0.3px}
+.ds-hero-sub{font-size:14px;color:#8A8580;margin-bottom:20px}
+.ds-hero-canvas{display:block;margin:0 auto 20px;border-radius:8px;background:#FDFCFA;border:1px solid #D6D1CA;max-width:100%;height:auto}
+.ds-hero-cta{padding:12px 32px;border-radius:8px;border:none;background:#A8907E;color:#fff;font-size:15px;font-weight:700;cursor:pointer;transition:all .15s}
+.ds-hero-cta:hover{background:#927A6A;transform:translateY(-1px)}
+.ds-section-title{font-size:18px;font-weight:700;color:#3A3632;margin:28px 0 16px}
+.ds-card-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:16px;margin-bottom:8px}
+.ds-card-grid-3{grid-template-columns:repeat(3,1fr)}
+@media(max-width:640px){.ds-card-grid,.ds-card-grid-3{grid-template-columns:1fr}}
+.ds-feature-card{background:#FDFCFA;border-radius:8px;padding:24px;border:1px solid #D6D1CA;cursor:pointer;transition:all .15s;text-align:center}
+.ds-feature-card:hover{border-color:#808C95;background:#F0EEEA}
+.ds-feature-card-icon{font-size:36px;margin-bottom:10px}
+.ds-feature-card-name{font-size:15px;font-weight:700;color:#3A3632;margin-bottom:4px}
+.ds-feature-card-desc{font-size:12px;color:#8A8580;line-height:1.5}
+.ds-footer-note{padding:24px 0 8px;text-align:center;font-size:12px;color:#ABA6A1;border-top:1px solid #EAE7E2;margin-top:24px}
+
+/* ========== Math Life Hub ========== */
+.ml-theme-grid{margin-bottom:28px}
+.ml-theme-card{position:relative}
+.ml-theme-card.ml-theme-coming{cursor:default;opacity:.72}
+.ml-theme-card.ml-theme-coming:hover{background:#FDFCFA;border-color:#D6D1CA}
+.ml-coming-badge{display:inline-block;margin-top:10px;padding:3px 10px;border-radius:999px;background:#EAE7E2;color:#8A8580;font-size:11px;font-weight:600}
+.ml-more{margin-top:28px;padding:22px 0 8px;border-top:1px solid #EAE7E2}
+.ml-more-title{font-size:16px;font-weight:700;color:#3A3632;margin-bottom:6px}
+.ml-more-sub{font-size:12px;color:#8A8580;margin-bottom:16px}
+.ml-more-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px}
+@media(max-width:900px){.ml-more-grid{grid-template-columns:repeat(2,1fr)}}
+@media(max-width:640px){.ml-more-grid{grid-template-columns:1fr}}
+.ml-more-card{border:1px dashed #D6D1CA;border-radius:8px;padding:14px;text-align:center;background:#FDFCFA}
+.ml-more-icon{font-size:26px;margin-bottom:6px}
+.ml-more-name{font-size:13px;font-weight:600;color:#3A3632;margin-bottom:6px}
+.ml-more-tag{font-size:11px;color:#ABA6A1}
+.ml-player-header{display:flex;align-items:center;gap:12px;margin-bottom:18px;flex-wrap:wrap}
+.ml-back-btn{padding:8px 14px;border:1px solid #D6D1CA;border-radius:6px;background:#FDFCFA;color:#657078;font-size:12px;cursor:pointer}
+.ml-back-btn:hover{background:#EAE7E2}
+.ml-player-title{font-size:18px;font-weight:700;color:#3A3632}
+.ml-player-sub{font-size:12px;color:#8A8580;flex:1;min-width:180px}
+.ml-stats{display:flex;gap:10px;flex-wrap:wrap;margin-bottom:16px}
+.ml-stat{display:flex;align-items:center;gap:8px;background:#F7F5F1;border:1px solid #EAE7E2;border-radius:8px;padding:8px 12px;font-size:12px}
+.ml-stat b{font-size:16px}
+.ml-stat span{color:#8A8580}
+.ml-stat-icon{width:26px;height:26px;border-radius:6px;color:#fff;display:flex;align-items:center;justify-content:center;font-size:14px;font-weight:700;flex-shrink:0}
+.stats5{margin-top:12px;padding:10px 12px;background:#F7F5F1;border:1px solid #EAE7E2;border-radius:8px;text-align:left}
+.stats5-row{display:flex;align-items:center;gap:8px;margin-bottom:6px;font-size:10px}
+.stats5-row:last-child{margin-bottom:0}
+.stats5-row span{width:32px;color:#8A8580;flex-shrink:0}
+.stats5-track{flex:1;height:5px;background:#E8E5E0;border-radius:999px;overflow:hidden}
+.stats5-track i{display:block;height:100%;background:linear-gradient(90deg,#A8907E,#C47F3A);border-radius:999px}
+.stats5-row b{width:24px;text-align:right;color:#3A3632;font-weight:700}
+.ml-more-desc{font-size:11px;color:#8A8580;line-height:1.4;margin-bottom:6px;padding:0 6px}
+.ml-alumni{margin-top:28px;padding:22px 0 8px;border-top:1px solid #EAE7E2}
+.ml-alumni-head{display:flex;justify-content:space-between;align-items:flex-start;gap:14px;flex-wrap:wrap;margin-bottom:16px}
+.ml-alumni-title{font-size:16px;font-weight:700;color:#3A3632;margin-bottom:6px}
+.ml-alumni-sub{font-size:12px;color:#8A8580}
+.ml-alumni-preview-btn{padding:8px 16px;border-radius:999px;border:1.5px solid #A8907E;background:#FDFCFA;color:#A8907E;font-size:12px;font-weight:700;cursor:pointer;transition:all .15s}
+.ml-alumni-preview-btn:hover{background:#A8907E;color:#fff}
+.ml-alumni-entry{display:flex;align-items:center;gap:14px;width:100%;margin-top:26px;padding:18px 20px;border:1.5px solid #A8907E;border-radius:8px;background:#FDFCFA;cursor:pointer;transition:all .15s;font:inherit;text-align:left}
+.ml-alumni-entry:hover{background:#FBF8F3;box-shadow:0 4px 14px rgba(0,0,0,.06)}
+.ml-alumni-entry-icon{width:46px;height:46px;border-radius:50%;background:#EAE7E2;display:flex;align-items:center;justify-content:center;font-size:22px;flex-shrink:0}
+.ml-alumni-entry-body{flex:1;min-width:0}
+.ml-alumni-entry-body b{display:block;font-size:15px;color:#3A3632;margin-bottom:3px}
+.ml-alumni-entry-body small{font-size:12px;color:#8A8580}
+.ml-alumni-entry-arrow{font-size:18px;color:#A8907E;font-weight:700}
+.ml-alumni-list-head{margin:18px 0 14px}
+#alumniCards{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}
+.ml-alumni-card{display:flex;flex-direction:column;align-items:center;gap:6px;width:100%;padding:16px 12px;border:1px solid #D6D1CA;border-radius:8px;background:#FDFCFA;cursor:pointer;text-align:center;transition:all .15s;font:inherit}
+.ml-alumni-card:hover{border-color:#A8907E;box-shadow:0 4px 14px rgba(0,0,0,.07);transform:translateY(-2px)}
+.ml-alumni-card .ml-alumni-avatar{width:46px;height:46px;font-size:22px}
+.ml-alumni-skill-tags{display:flex;flex-wrap:wrap;gap:4px;justify-content:center}
+.ml-alumni-skill-tag{font-size:10px;padding:2px 7px;border-radius:999px;background:#F0EDE8;color:#8A8580;font-weight:600}
+.ml-alumni-dim-row{display:flex;gap:6px;font-size:10px;color:#8A8580;flex-wrap:wrap;justify-content:center}
+.ml-alumni-card-cta{font-size:11px;font-weight:700;color:#A8907E;margin-top:4px}
+.ml-story-linear{max-width:720px;margin:0 auto;padding:18px 0}
+.ml-story-progress{height:6px;background:#EAE7E2;border-radius:999px;overflow:hidden;margin-bottom:18px}
+.ml-story-progress i{display:block;height:100%;background:linear-gradient(90deg,#A8907E,#C47F3A);border-radius:999px;transition:width .3s}
+.ml-story-stage-name{font-size:15px;font-weight:700;color:#3A3632;margin-bottom:12px}
+.ml-story-scene{font-size:15px;line-height:1.9;color:#3A3632;background:#F7F5F1;border:1px solid #EAE7E2;border-radius:8px;padding:18px 20px;margin-bottom:14px}
+.ml-story-effect{font-size:12px;color:#8A8580;background:#FBF8F3;border:1px solid #EAE7E2;border-radius:8px;padding:10px 14px;margin-bottom:14px}
+.ml-story-next{display:block;margin-left:auto;padding:10px 20px;border-radius:8px;border:1.5px solid #A8907E;background:#A8907E;color:#fff;font-size:13px;font-weight:700;cursor:pointer}
+.ml-story-next:hover{background:#9A7D69}
+.ml-story-ending{font-size:14px;color:#3A3632;background:#F7F5F1;border:1px dashed #D6D1CA;border-radius:8px;padding:18px;margin-bottom:14px;line-height:1.8}
+.ml-story-timeline{display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-top:16px}
+.ml-story-stage{background:#F7F5F1;border:1px solid #EAE7E2;border-radius:8px;padding:14px;text-align:center}
+.ml-story-stage span{width:24px;height:24px;border-radius:50%;background:#A8907E;color:#fff;font-size:12px;font-weight:700;display:inline-flex;align-items:center;justify-content:center;margin-bottom:6px}
+.ml-story-stage b{display:block;font-size:13px;color:#3A3632;margin-bottom:4px}
+.ml-story-stage small{font-size:10px;color:#ABA6A1}
+.ml-alumni-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:16px}
+.ml-alumni-slot{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;border:1px dashed #D6D1CA;border-radius:8px;padding:16px 10px;background:#FDFCFA;text-align:center;min-height:122px}
+.ml-alumni-slot.join{cursor:pointer;color:#8A8580;font:inherit}
+.ml-alumni-slot.join:hover{border-color:#A8907E;color:#A8907E;background:#FBF8F3}
+.ml-alumni-avatar{width:40px;height:40px;border-radius:50%;background:#EAE7E2;display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0}
+.ml-alumni-code{font-size:10px;color:#ABA6A1;letter-spacing:.5px}
+.ml-alumni-name{font-size:13px;font-weight:600;color:#3A3632}
+.ml-alumni-meta{font-size:10px;color:#ABA6A1;line-height:1.4}
+.ml-alumni-pipeline{display:grid;grid-template-columns:repeat(4,1fr);gap:10px}
+.ml-pipeline-step{display:flex;align-items:center;gap:8px;background:#F7F5F1;border:1px solid #EAE7E2;border-radius:8px;padding:10px 12px;font-size:12px;color:#8A8580}
+.ml-pipeline-step span{width:22px;height:22px;border-radius:50%;background:#A8907E;color:#fff;font-size:11px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0}
+.ml-pipeline-step b{font-size:12px;color:#3A3632}
+.ml-alumni-scaffold{padding:20px 0}
+.ml-scaffold-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px;margin-bottom:20px}
+.ml-alumni-empty{border:1px dashed #D6D1CA;border-radius:8px;padding:28px;text-align:center;color:#8A8580;font-size:13px;background:#FDFCFA}
+.ml-alumni-draft{display:flex;align-items:center;gap:10px;border:1px solid #EAE7E2;border-radius:8px;padding:12px 14px;background:#FDFCFA;margin-bottom:10px;font-size:13px;color:#3A3632}
+@media(max-width:900px){.ml-alumni-grid{grid-template-columns:repeat(2,1fr)}.ml-alumni-pipeline{grid-template-columns:repeat(2,1fr)}.ml-scaffold-grid{grid-template-columns:repeat(2,1fr)}#alumniCards{grid-template-columns:repeat(2,1fr)}}
+@media(max-width:560px){.ml-alumni-grid{grid-template-columns:1fr 1fr}.ml-alumni-slot{min-height:108px;padding:12px 8px}#alumniCards{grid-template-columns:1fr 1fr}.ml-story-timeline{grid-template-columns:repeat(2,1fr)}}
+
+/* ========== Numberphile-style Discover Page ========== */
+.np-greeting{font-size:14px;color:#8A8580;margin-bottom:24px;line-height:1.6}
+.np-video-list{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}
+@media(max-width:1200px){.np-video-list{grid-template-columns:repeat(2,1fr)}}
+@media(max-width:640px){.np-video-list{grid-template-columns:1fr;gap:16px}}
+.np-card{background:#FDFCFA;border-radius:8px;overflow:hidden;border:1px solid #D6D1CA;cursor:pointer;transition:all .15s;display:flex;flex-direction:column}
+.np-card:hover{border-color:#808C95}
+.np-cover{position:relative;aspect-ratio:16/9;overflow:hidden;border-radius:8px 8px 0 0}
+.np-cover-inner{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;transition:transform .35s ease}
+.np-cover-icon{font-size:40px;opacity:.3}
+.np-cover-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}
+.np-card:hover .np-cover-inner{transform:scale(1.05)}
+.np-cover-duration{position:absolute;top:8px;left:8px;background:rgba(58,54,50,.55);backdrop-filter:blur(4px);color:#fff;font-size:11px;font-weight:500;padding:2px 8px;border-radius:4px;letter-spacing:.3px;z-index:2}
+.np-cover-play{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:48px;height:48px;border-radius:50%;background:rgba(58,54,50,.4);backdrop-filter:blur(4px);color:#fff;font-size:16px;display:flex;align-items:center;justify-content:center;padding-left:3px;opacity:0;transition:opacity .25s;pointer-events:none;z-index:2}
+.np-card:hover .np-cover-play{opacity:1}
+.np-cover-fav{position:absolute;top:8px;right:8px;width:30px;height:30px;border-radius:50%;border:none;background:rgba(58,54,50,.3);backdrop-filter:blur(4px);font-size:14px;cursor:pointer;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .2s;z-index:2}
+.np-card:hover .np-cover-fav,.np-cover-fav.faved{opacity:1}
+.np-body{padding:12px 14px 14px;display:flex;flex-direction:column;flex:1}
+.np-card-title{font-size:14px;font-weight:600;line-height:1.45;color:#3A3632;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;margin-bottom:8px;min-height:40px}
+.np-stats{display:flex;align-items:center;justify-content:space-between;font-size:12px;color:#ABA6A1;margin-bottom:8px}
+.np-stat{display:flex;align-items:center;gap:4px}
+.np-uploader{display:flex;align-items:center;gap:8px;margin-top:auto}
+.np-up-avatar{width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:12px;color:#fff;font-weight:600;flex-shrink:0}
+.np-up-name{font-size:12px;color:#8A8580;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
+.np-load-hint{grid-column:1/-1;text-align:center;font-size:12px;color:#ABA6A1;padding:16px 0 4px}
+
+/* ========== Five-Dimension Ability Map ========== */
+#dimMap{margin-bottom:18px}
+.dim-map-head{display:flex;justify-content:space-between;align-items:flex-start;gap:14px;flex-wrap:wrap;margin-bottom:14px}
+.dim-map-title{font-size:18px;font-weight:700;color:#3A3632}
+.dim-map-sub{font-size:12px;color:#8A8580;margin-top:4px}
+.dim-map-legend{display:flex;flex-wrap:wrap;gap:8px;padding-top:2px}
+.dim-map-legend-item{display:inline-flex;align-items:center;gap:6px;font-size:11px;color:#8A8580}
+.dim-map-legend-item i{width:8px;height:8px;border-radius:50%;background:var(--dim-color)}
+.dim-map-board{position:relative;height:360px;background:linear-gradient(rgba(58,54,50,.045) 1px,transparent 1px),linear-gradient(90deg,rgba(58,54,50,.045) 1px,transparent 1px),#F7F5F1;background-size:40px 40px;border:1px dashed #D6D1CA;border-radius:8px;overflow:hidden}
+.dim-map-svg{position:absolute;inset:0;width:100%;height:100%;pointer-events:none}
+.dim-map-svg line{stroke:#C8C4BD;stroke-width:.45;stroke-dasharray:2.5 3}
+.dim-map-svg .dim-map-spoke{stroke:#D6D1CA;stroke-width:.3}
+.dim-map-center{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);width:76px;height:76px;border-radius:50%;background:#FDFCFA;border:1.5px solid #A8907E;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;color:#3A3632;font-size:11px;box-shadow:0 3px 10px rgba(0,0,0,.08);z-index:2}
+.dim-map-gourd{width:30px;height:42px;border-radius:15px 15px 16px 16px / 20px 20px 24px 24px;background:linear-gradient(160deg,#8A9BA8,#65707A);color:#fff;font-size:16px;font-weight:800;display:flex;align-items:center;justify-content:center}
+.dim-map-node{position:absolute;transform:translate(-50%,-50%);min-width:104px;padding:10px 12px;border-radius:8px;background:#FDFCFA;border:1.5px solid var(--dim-color);box-shadow:0 3px 10px rgba(0,0,0,.06);cursor:pointer;display:flex;flex-direction:column;align-items:center;gap:3px;transition:all .18s;z-index:3}
+.dim-map-node:hover,.dim-map-node.active{transform:translate(-50%,-50%) scale(1.06);box-shadow:0 6px 18px rgba(58,54,50,.14)}
+.dim-node-icon{font-size:22px;line-height:1}
+.dim-node-name{font-size:13px;font-weight:700;color:#3A3632;white-space:nowrap}
+.dim-node-value{font-size:12px;font-weight:800;color:var(--dim-color);min-width:30px;height:20px;border-radius:10px;background:rgba(58,54,50,.06);display:flex;align-items:center;justify-content:center}
+.dim-map-foot{text-align:center;font-size:11px;color:#ABA6A1;margin-top:10px}
+.dim-tabs{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:16px}
+.dim-tab{display:inline-flex;align-items:center;gap:6px;padding:8px 14px;border-radius:999px;border:1.5px solid #D6D1CA;background:#FDFCFA;color:#3A3632;font-size:13px;font-weight:600;cursor:pointer;transition:all .15s}
+.dim-tab:hover{border-color:var(--dim-color,#A8907E);color:var(--dim-color,#A8907E)}
+.dim-tab.active{background:var(--dim-color,#A8907E);border-color:var(--dim-color,#A8907E);color:#fff}
+.dim-tab span{font-size:11px;opacity:.78;font-weight:700}
+.np-dim-badge{display:inline-flex;align-items:center;gap:4px;width:fit-content;font-size:11px;font-weight:700;color:var(--dim-color);background:rgba(58,54,50,.05);border:1px solid rgba(58,54,50,.12);border-radius:6px;padding:3px 8px;margin-bottom:8px}
+.video-modal-top{margin-bottom:10px}
+.video-modal-dim{display:inline-flex;align-items:center;gap:5px;font-size:11px;font-weight:700;color:var(--dim-color);background:rgba(58,54,50,.05);border:1px solid rgba(58,54,50,.12);border-radius:6px;padding:3px 8px;margin-bottom:8px}
+.video-modal-title{font-size:18px;font-weight:700;color:#3A3632;line-height:1.4}
+.video-modal-player{aspect-ratio:16/9;background:#111;border-radius:8px;margin:12px 0;overflow:hidden;display:flex;align-items:center;justify-content:center}
+.video-modal-player iframe{width:100%;height:100%;border:0}
+.video-modal-player.empty{background:#F1F5F9;color:#CBD5E1;font-size:48px}
+.video-modal-desc{font-size:14px;color:#64748B;line-height:1.7;margin-bottom:8px}
+.video-modal-meta{display:flex;gap:8px;flex-wrap:wrap;font-size:12px;color:#94A3B8}
+.video-claim-box{margin-top:14px;padding-top:14px;border-top:1px solid #EAE7E2}
+.video-claim-btn{width:100%;padding:12px;border-radius:8px;border:1.5px solid var(--dim-color,#5B6AF0);background:var(--dim-color,#5B6AF0);color:#fff;font-size:14px;font-weight:700;cursor:pointer}
+.video-claim-btn:hover{opacity:.9}
+.video-claim-btn.claimed{background:#E8E5E0;border-color:#D6D1CA;color:#8A8580;cursor:default;opacity:1}
+.video-claim-hint{font-size:12px;color:#ABA6A1;line-height:1.6}
+@media(max-width:760px){
+ .dim-map-board{height:430px}
+ .dim-map-node{min-width:76px;padding:6px}
+ .dim-node-name{font-size:11px}
+ .dim-node-value{font-size:10px;height:18px;min-width:24px}
+ .dim-map-legend{display:none}
+}
+
+/* Profile */
+.profile-card{background:linear-gradient(135deg,#808C95,#9AA5AE);border-radius:8px;padding:28px;color:#fff;display:flex;align-items:center;gap:16px;margin-bottom:20px;flex-wrap:wrap}
+.profile-avatar-lg{width:72px;height:72px;border-radius:50% 50% 50% 50% / 60% 60% 40% 40%;box-shadow:inset 0 -3px 0 rgba(0,0,0,.08);background:rgba(255,255,255,.2);display:flex;align-items:center;justify-content:center;font-size:32px;font-weight:800;flex-shrink:0}
+.profile-name-lg{font-size:22px;font-weight:800;margin-bottom:4px}
+.profile-id-lg{font-size:13px;opacity:.8}
+.profile-stats-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:16px;margin-bottom:20px}
+.profile-stat-card{background:#FDFCFA;border-radius:8px;padding:20px;text-align:center;border:1px solid #D6D1CA}
+.profile-stat-card .num{font-size:32px;font-weight:800;color:#808C95}
+.profile-stat-card .label{font-size:12px;color:#8A8580;margin-top:4px}
+
+/* Pet display on home */
+.pet-card{background:linear-gradient(135deg,#8A9BA8,#9AADBA);border-radius:8px;padding:24px 28px;color:#fff;display:flex;align-items:center;gap:20px;margin-bottom:24px;position:relative;overflow:hidden}
+.pet-card::after{content:"";position:absolute;top:-20px;right:-20px;width:100px;height:100px;border-radius:50%;background:rgba(255,255,255,.1)}
+.pet-avatar{width:80px;height:80px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:40px;font-weight:800;background:rgba(255,255,255,.2);flex-shrink:0;position:relative;z-index:1}
+.pet-info{position:relative;z-index:1}
+.pet-name{font-size:20px;font-weight:800;margin-bottom:4px}
+.pet-type{font-size:13px;opacity:.85;margin-bottom:6px}
+.pet-exp-bar{height:6px;background:rgba(255,255,255,.25);border-radius:3px;overflow:hidden;width:200px}
+.pet-exp-fill{height:100%;background:#FDFCFA;border-radius:3px;transition:width .5s /* 注意:JS 中使用 requestAnimationFrame 实现跳动动画,覆盖此 transition */};
+.pet-level{font-size:12px;margin-top:4px;opacity:.8}
+.pet-dims{display:grid;grid-template-columns:repeat(5,minmax(96px,1fr));gap:8px;margin:12px 0;max-width:680px}
+.pet-dim{display:grid;grid-template-columns:18px 30px 1fr 26px;align-items:center;gap:6px;background:rgba(255,255,255,.14);border-radius:6px;padding:6px 8px;font-size:10px}
+.pet-dim-name{color:rgba(255,255,255,.88);white-space:nowrap}
+.pet-dim-track{height:5px;background:rgba(255,255,255,.25);border-radius:999px;overflow:hidden}
+.pet-dim-track i{display:block;height:100%;border-radius:999px}
+.pet-dim b{text-align:right;color:#fff;font-variant-numeric:tabular-nums}
+@media(max-width:900px){.pet-dims{grid-template-columns:repeat(2,1fr)}}
+@media(max-width:560px){.pet-dims{grid-template-columns:1fr 1fr}.pet-card{align-items:flex-start}}
+
+/* Toolbox */
+.tool-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:16px;margin-bottom:24px}
+@media(max-width:640px){.tool-grid{grid-template-columns:1fr}}
+.tool-card{background:#FDFCFA;border-radius:8px;padding:20px;border:1px solid #D6D1CA;cursor:pointer;transition:all .15s;text-align:center}
+.tool-card:hover{border-color:#808C95;background:#F0EEEA}
+.tool-card-icon{font-size:36px;margin-bottom:10px}
+.tool-card-name{font-size:15px;font-weight:700;color:#3A3632;margin-bottom:4px}
+.tool-card-desc{font-size:12px;color:#8A8580}
+.tool-modal-body{padding:0;min-height:300px}
+
+/* Mental math game */
+.math-race .question{font-size:28px;font-weight:800;text-align:center;padding:20px;color:#3A3632}
+.math-race .answer-area{display:flex;gap:10px;padding:0 20px 20px;justify-content:center}
+.math-race input{width:120px;padding:12px;border:1px solid #D6D1CA;border-radius:6px;font-size:20px;text-align:center;outline:none}
+.math-race input:focus{border-color:#808C95}
+.math-race .result{text-align:center;padding:10px;font-weight:700}
+.math-race .stats{display:flex;justify-content:center;gap:30px;padding:10px;font-size:13px;color:#8A8580}
+.math-race .timer-bar{height:4px;background:#EAE7E2;margin:0 20px 20px;border-radius:2px;overflow:hidden}
+.math-race .timer-fill{height:100%;background:#8AA68A;transition:width .1s linear}
+
+/* Symbol lookup */
+.symbol-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;padding:16px}
+.symbol-btn{padding:10px;border:1px solid #D6D1CA;border-radius:6px;background:#FDFCFA;cursor:pointer;text-align:center;font-weight:600;transition:all .15s;font-size:13px}
+.symbol-btn:hover{border-color:#808C95;background:#EAE7E2}
+.symbol-detail{padding:0 20px 20px;text-align:center;font-size:13px;color:#8A8580;line-height:1.6;min-height:60px}
+
+/* Function grapher */
+.graph-container{padding:16px;text-align:center}
+.graph-container canvas{border:1px solid #D6D1CA;border-radius:8px;display:block;margin:0 auto}
+.graph-inputs{display:flex;gap:8px;padding:0 16px 16px;justify-content:center}
+.graph-inputs input{padding:8px 12px;border:1px solid #D6D1CA;border-radius:6px;font-size:13px;outline:none;width:200px;background:#F7F5F1}
+.graph-inputs input:focus{border-color:#808C95}
+.graph-inputs button{padding:8px 16px;border-radius:6px;border:none;background:#808C95;color:#fff;font-size:13px;cursor:pointer;transition:all .15s}
+.graph-inputs button:hover{background:#657078}
+
+/* Sidebar overlay */
+.sidebar-overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,.4);z-index:25}
+
+/* Responsive */
+@media(max-width:1366px){.app-wrapper{max-width:100%}.main-content{padding:24px 28px}}
+@media(max-width:768px){
+ body{overflow-x:hidden}.app-wrapper{max-width:100%;height:100dvh}
+ .mobile-topbar{display:flex}
+ .sidebar{position:fixed;top:0;left:0;bottom:0;height:100dvh;transform:translateX(-100%);z-index:30;border-right:1px solid #D6D1CA;border-radius:0}
+ .sidebar.open{transform:translateX(0)}.sidebar-overlay.active{display:block}
+ .main-content{padding:20px 16px}.main-content-inner{max-width:100%}
+ .video-grid{grid-template-columns:1fr}.fav-grid{grid-template-columns:1fr}.knowledge-grid{grid-template-columns:1fr}
+ .story-container,.sim-container{padding:20px;border-radius:8px}.banner{padding:20px}.banner-title{font-size:18px}
+ .ai-panel{width:100%;max-width:100%}
+}
+
+/* ========== UI Polish ========== */
+.nav-icon svg{width:17px;height:17px;display:block;margin:0 auto;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
+/* 语言切换按钮 */
+.lang-switch{display:flex;gap:2px;margin-left:auto}
+.lang-btn{padding:2px 6px;border-radius:4px;border:1px solid #D6D1CA;background:transparent;font-size:11px;font-weight:600;color:#ABA6A1;cursor:pointer;transition:all .15s;min-width:28px}
+.lang-btn.active{background:#808C95;color:#fff;border-color:#808C95}
+.lang-btn:hover:not(.active){color:#808C95;border-color:#808C95}
+.lang-switch.mobile-lang{margin-left:auto}
+/* 交互 */
+::selection{background:#E6E9EC;color:#657078}
+button:focus-visible,input:focus-visible,[tabindex]:focus-visible{outline:2px solid #808C95;outline-offset:2px}
+.nav-item:active,.filter-btn:active,.ds-hero-cta:active,.symbol-btn:active,.graph-inputs button:active,.ai-send-btn:active{transform:scale(.97)}
+.ds-feature-card:active,.tool-card:active{transform:translateY(-1px) scale(.98)}
+::-webkit-scrollbar-thumb:hover{background:#ABA6A1}
+.sim-stat-val,.profile-stat-card .num,.math-race .stats,.np-cover-duration,.pet-type{font-variant-numeric:tabular-nums}
+.page-title,.ds-section-title,.section-title{letter-spacing:-0.01em}
+.sidebar-search input:focus,.math-race input:focus,.graph-inputs input:focus,.ai-panel-input-area input:focus{box-shadow:0 0 0 3px rgba(128,140,149,.12)}
+@keyframes fadeIn{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
+@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}
+
+/* ========== Skeleton Screen (骨架屏) ========== */
+@keyframes skeletonShimmer{0%{background-position:-400px 0}100%{background-position:400px 0}}
+.skeleton{background:linear-gradient(90deg,#EAE7E2 25%,#DED9D2 37%,#EAE7E2 63%);background-size:400px 100%;animation:skeletonShimmer 1.5s ease-in-out infinite;border-radius:4px}
+.skeleton-card{width:100%;height:180px;border-radius:8px;border:1px solid #EAE7E2;padding:14px;display:flex;flex-direction:column;gap:10px}
+.skeleton-line{height:12px;border-radius:3px}
+.skeleton-line.w80{width:80%}.skeleton-line.w60{width:60%}.skeleton-line.w40{width:40%}
+.skeleton-avatar{width:28px;height:28px;border-radius:50%}
+
+/* ========== Toast ========== */
+.toast-container{position:fixed;top:20px;right:20px;z-index:200;display:flex;flex-direction:column;gap:8px;pointer-events:none}
+.toast{background:#3A3632;color:#FDFCFA;padding:10px 18px;border-radius:6px;font-size:13px;font-weight:500;animation:toastIn .3s ease,toastOut .3s ease 2s forwards;max-width:280px;box-shadow:0 2px 8px rgba(58,54,50,.15)}
+@keyframes toastIn{from{opacity:0;transform:translateX(20px)}to{opacity:1;transform:translateX(0)}}
+@keyframes toastOut{from{opacity:1;transform:translateX(0)}to{opacity:0;transform:translateX(20px)}}
+
+/* ========== 粒子爆发动画 ========== */
+@keyframes particleBurst{0%{opacity:1;transform:translate(0,0) scale(1)}100%{opacity:0;transform:translate(var(--px),var(--py)) scale(0)}}
+.particle{position:fixed;pointer-events:none;z-index:300;animation:particleBurst .7s ease-out forwards}
+
+/* ========== MathBTI sidebar promocard (醒目入口) ========== */
+.sidebar-mbti-card{margin:0 12px 8px;padding:14px;border-radius:6px;background:linear-gradient(135deg,#8A9BA8,#99A9B5);color:#fff;font-size:12px;font-weight:600;cursor:pointer;transition:all .15s;line-height:1.5}
+.sidebar-mbti-card:hover{background:linear-gradient(135deg,#7A8B9A,#8A9AA8);transform:scale(1.01)}
+.sidebar-mbti-card .mbti-card-icon{font-size:20px;margin-bottom:4px}
+.sidebar-mbti-card .mbti-card-arrow{float:right;opacity:.7;font-size:14px}
+
+/* ========== 葫芦元素 ========== */
+.sidebar-logo-icon,.topbar-logo-icon{border-radius:50% 50% 50% 50% / 60% 60% 40% 40%;box-shadow:inset 0 -3px 0 rgba(0,0,0,.1)}
+.vine-divider{height:1px;background:linear-gradient(90deg,transparent,#C8C4BD 15%,#A8B886 50%,#C8C4BD 85%,transparent);margin:20px 0;position:relative}
+.vine-divider::after{content:"🏮";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);font-size:14px;background:#F7F5F1;padding:0 8px}
+#toastBox .toast{border-radius:50% 50% 50% 50% / 60% 60% 40% 40%;padding:12px 24px}
+.skeleton-card{border-radius:50% 50% 50% 50% / 60% 60% 40% 40%;padding:20px 16px 16px}
+.skeleton-card::before{content:"";display:block;width:40px;height:16px;background:linear-gradient(90deg,#E8E5E0 25%,#F0EEEA 50%,#E8E5E0 75%);background-size:200% 100%;animation:shimmer 1.5s infinite;border-radius:8px;margin-bottom:12px}
+
+/* ========== 抽卡按钮 ========== */
+.float-hub-child.cards{background:#A8907E}
+.float-hub-child.cards:hover{background:#B8956E}
+.draw-btn{display:inline-flex;align-items:center;gap:8px;padding:10px 24px;border-radius:50% 50% 50% 50% / 60% 60% 40% 40%;border:none;background:linear-gradient(135deg,#A8907E,#C47F3A);color:#fff;font-size:14px;font-weight:700;cursor:pointer;transition:all .2s;box-shadow:0 3px 12px rgba(164,126,94,.3)}
+.draw-btn:hover{transform:scale(1.03);box-shadow:0 6px 20px rgba(164,126,94,.45)}
+.draw-btn:disabled{opacity:.5;cursor:not-allowed;transform:none;box-shadow:none}
+
+/* ========== 知识页抽卡进度条 ========== */
+.draw-progress-bar{background:#F7F5F1;border-radius:8px;padding:14px 18px;border:1px solid #D6D1CA;display:flex;align-items:center;gap:12px;font-size:13px;color:#8A8580;margin-top:16px;cursor:pointer;transition:all .15s}
+.draw-progress-bar:hover{border-color:#A8907E;background:#FDF8F4}
+.draw-progress-track{flex:1;height:8px;background:#E8E5E0;border-radius:4px;overflow:hidden}
+.draw-progress-fill{height:100%;background:linear-gradient(90deg,#A8907E,#C47F3A);border-radius:4px;transition:width .4s cubic-bezier(.34,1.56,.64,1)}
+.draw-progress-icon{font-size:20px;flex-shrink:0}
+.draw-progress-ready{background:#FDF4EC;border-color:#C47F3A;animation:pulse-glow 2s infinite}
+@keyframes pulse-glow{0%,100%{box-shadow:0 0 0 0 rgba(196,127,58,.2)}50%{box-shadow:0 0 0 6px rgba(196,127,58,0)}}
+
+/* ========== 葫芦摇晃 + 卡片翻转动画 ========== */
+@keyframes gourd-shake{0%,100%{transform:rotate(0)}10%{transform:rotate(-8deg)}20%{transform:rotate(8deg)}30%{transform:rotate(-6deg)}40%{transform:rotate(6deg)}50%{transform:rotate(-3deg)}60%{transform:rotate(3deg)}70%{transform:rotate(-1deg)}80%{transform:rotate(1deg)}}
+.gourd-shaking{animation:gourd-shake .8s ease-in-out}
+@keyframes gourd-bounce{0%{transform:scale(1.2)}50%{transform:scale(.9)}70%{transform:scale(1.05)}100%{transform:scale(1)}}
+@keyframes card-flip{0%{transform:perspective(600px) rotateY(0)}50%{transform:perspective(600px) rotateY(90deg)}100%{transform:perspective(600px) rotateY(0)}}
+.card-flip-in{animation:card-flip .6s ease-in-out}
+
+/* ========== 抽卡弹窗 ========== */
+.gacha-overlay{position:fixed;inset:0;background:rgba(0,0,0,.55);z-index:500;display:flex;align-items:center;justify-content:center;animation:fadeIn .25s}
+.gacha-modal{background:#FDFCFA;border-radius:16px;padding:28px 24px;max-width:360px;width:90%;text-align:center;position:relative;animation:gourd-bounce .5s ease-out}
+.gacha-gourd{font-size:72px;display:block;margin:0 auto 16px;cursor:pointer;user-select:none;transition:all .2s;filter:drop-shadow(0 4px 8px rgba(0,0,0,.15))}
+.gacha-gourd:hover{transform:scale(1.08)}
+.gacha-title{font-size:20px;font-weight:700;color:#3A3632;margin-bottom:4px}
+.gacha-sub{font-size:12px;color:#ABA6A1;margin-bottom:12px}
+.gacha-draws{font-size:13px;color:#8A8580;margin-bottom:8px}
+.gacha-draws b{color:#A8907E;font-size:16px}
+.gacha-close{position:absolute;top:12px;right:16px;background:none;border:none;font-size:20px;color:#ABA6A1;cursor:pointer;padding:4px}
+.gacha-card-reveal{margin-top:16px;animation:gourd-bounce .5s ease-out}
+.gacha-card-portrait{width:100px;height:100px;border-radius:50%;margin:0 auto 12px;display:flex;align-items:center;justify-content:center;font-size:48px;overflow:hidden;border:3px solid var(--clan-color,#A8907E)}
+.gacha-card-portrait img{width:100%;height:100%;object-fit:cover}
+.gacha-card-name{font-size:18px;font-weight:700;color:#3A3632;margin-bottom:2px}
+.gacha-card-type{font-size:12px;color:#ABA6A1;margin-bottom:4px}
+.gacha-card-clan{font-size:12px;padding:3px 12px;border-radius:12px;display:inline-block;margin-bottom:8px}
+.gacha-card-tagline{font-size:13px;color:#8A8580;font-style:italic;margin-bottom:12px}
+.gacha-card-dupe{font-size:12px;color:#C49595;margin-bottom:8px;font-style:italic}
+.gacha-card-detail-btn{padding:6px 18px;border-radius:20px;border:1px solid #D6D1CA;background:#FDFCFA;color:#8A8580;font-size:12px;cursor:pointer;transition:all .15s}
+.gacha-card-detail-btn:hover{border-color:#A8907E;color:#A8907E}
+
+/* ========== 卡片图鉴条 (首页) ========== */
+.card-gallery-wrap{position:relative;margin:8px 0 20px}
+.card-gallery-scroll{display:flex;gap:10px;overflow-x:auto;padding:4px 2px 8px;scrollbar-width:none}
+.card-gallery-scroll::-webkit-scrollbar{display:none}
+.card-gallery-item{flex-shrink:0;width:72px;text-align:center;cursor:pointer;transition:all .15s}
+.card-gallery-item:hover{transform:translateY(-3px)}
+.card-gallery-item .cg-icon{width:56px;height:56px;border-radius:50%;margin:0 auto 4px;display:flex;align-items:center;justify-content:center;font-size:24px;border:2px solid transparent;transition:all .2s;overflow:hidden;position:relative}
+.card-gallery-item .cg-icon img{width:100%;height:100%;object-fit:cover;border-radius:50%;display:block}
+.card-gallery-item:hover .cg-icon{border-color:var(--clan-color,#A8907E);box-shadow:0 3px 12px rgba(0,0,0,.1)}
+.card-gallery-item .cg-name{font-size:10px;color:#8A8580;line-height:1.3;max-width:72px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
+.card-gallery-empty{text-align:center;padding:20px;color:#ABA6A1;font-size:13px;border:1px dashed #D6D1CA;border-radius:8px}
+.card-gallery-empty .cg-empty-icon{font-size:28px;display:block;margin-bottom:6px}
+
+/* ========== 卡片详情弹窗 ========== */
+.card-detail-overlay{position:fixed;inset:0;background:rgba(0,0,0,.55);z-index:510;display:flex;align-items:center;justify-content:center;animation:fadeIn .25s}
+.card-detail-modal{background:#FDFCFA;border-radius:16px;padding:0;max-width:400px;width:90%;overflow:hidden;position:relative;animation:gourd-bounce .5s ease-out}
+.card-detail-header{padding:32px 24px 20px;text-align:center;position:relative}
+.card-detail-header .card-detail-bg{position:absolute;inset:0;opacity:.12}
+.card-detail-portrait{width:80px;height:80px;border-radius:50%;margin:0 auto 12px;display:flex;align-items:center;justify-content:center;font-size:40px;overflow:hidden;border:3px solid #FDFCFA;position:relative;z-index:1;box-shadow:0 3px 12px rgba(0,0,0,.1)}
+.card-detail-name{font-size:22px;font-weight:700;color:#3A3632;position:relative;z-index:1;margin-bottom:2px}
+.card-detail-binary{font-size:12px;color:#ABA6A1;letter-spacing:2px;position:relative;z-index:1;margin-bottom:6px}
+.card-detail-clan{display:inline-block;padding:4px 14px;border-radius:12px;font-size:12px;font-weight:600;position:relative;z-index:1;margin-bottom:6px}
+.card-detail-tagline{font-size:13px;color:#8A8580;font-style:italic;position:relative;z-index:1}
+.card-detail-body{padding:20px 24px 24px;border-top:1px solid #EAE7E2}
+.card-detail-section{margin-bottom:16px}
+.card-detail-section-title{font-size:12px;font-weight:700;color:#ABA6A1;text-transform:uppercase;letter-spacing:1px;margin-bottom:8px}
+.card-detail-section p{font-size:14px;color:#5A5652;line-height:1.7}
+.card-detail-fact{background:#F7F5F1;border-radius:10px;padding:14px;display:flex;gap:10px;align-items:flex-start;font-size:13px;color:#8A8580;line-height:1.6}
+.card-detail-fact .fact-emoji{font-size:18px;flex-shrink:0}
+.card-detail-obtain{background:#F7F5F1;border-radius:10px;padding:12px 14px;font-size:12px;color:#ABA6A1;display:flex;align-items:center;gap:8px;margin-top:12px}
+.card-detail-obtain .obtain-icon{font-size:16px}
+.card-detail-close{position:absolute;top:12px;right:16px;z-index:2;background:rgba(255,255,255,.8);border:none;border-radius:50%;width:32px;height:32px;display:flex;align-items:center;justify-content:center;font-size:16px;color:#8A8580;cursor:pointer}
+
+@keyframes gacha-particle-key{0%{opacity:1;transform:translate(0,0) scale(1)}100%{opacity:0;transform:translate(var(--gpx),var(--gpy)) scale(0) rotate(360deg)}}
+.gacha-particle{position:fixed;pointer-events:none;z-index:520;width:10px;height:10px;border-radius:50%;animation:gacha-particle-key 1s ease-out forwards}
+
+.knowledge-card-read{position:absolute;top:8px;right:8px;background:rgba(255,255,255,.85);border-radius:50%;width:28px;height:28px;display:flex;align-items:center;justify-content:center;font-size:14px;z-index:2}
+.knowledge-card-read.unread{opacity:.4}
+
+/* ========== 卡册页网格 ========== */
+.cards-collection-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px}
+@media(max-width:1000px){.cards-collection-grid{grid-template-columns:repeat(3,1fr)}}
+@media(max-width:640px){.cards-collection-grid{grid-template-columns:repeat(2,1fr)}}
+.cc-card{background:#FDFCFA;border:1px solid #D6D1CA;border-radius:8px;padding:16px 12px;text-align:center;transition:all .15s}
+.cc-card.owned{cursor:pointer}
+.cc-card.owned:hover{border-color:var(--clan-color,#A8907E);box-shadow:0 3px 12px rgba(0,0,0,.08);transform:translateY(-2px)}
+.cc-card.locked{opacity:.45;filter:grayscale(.5)}
+.cc-card-icon{width:52px;height:52px;border-radius:50%;margin:0 auto 8px;display:flex;align-items:center;justify-content:center;font-size:24px;overflow:hidden;position:relative}
+.cc-card-icon img{width:100%;height:100%;object-fit:cover;border-radius:50%;display:block}
+.cc-card.locked .cc-card-icon::after{content:'🔒';position:absolute;right:-1px;bottom:-1px;width:18px;height:18px;line-height:18px;font-size:11px;background:rgba(255,255,255,.9);border-radius:50%;box-shadow:0 1px 4px rgba(0,0,0,.15);color:#8A8580}
+.cc-card-icon.locked-icon{background:#E8E5E0!important;color:#C8C4BD}
+.cc-card-name{font-size:13px;font-weight:600;color:#3A3632;margin-bottom:2px}
+.cc-card-type{font-size:10px;color:#ABA6A1;letter-spacing:1px;margin-bottom:4px}
+.cc-card-clan{font-size:10px;padding:2px 8px;border-radius:8px;display:inline-block}
diff --git a/static/css/desktop.min.css b/static/css/desktop.min.css
new file mode 100644
index 0000000..fb89fe9
--- /dev/null
+++ b/static/css/desktop.min.css
@@ -0,0 +1 @@
+*,*::before,*::after{box-sizing:border-box;margin:0;padding:0}body{font-family:system-ui,-apple-system,BlinkMacSystemFont,'Segoe UI','PingFang SC','Hiragino Sans GB','Microsoft YaHei','Noto Sans SC',sans-serif;-webkit-font-smoothing:antialiased;text-rendering:optimizeLegibility;background:#EDEBE6;color:#3A3632;overflow-x:hidden}body::before{content:"";position:fixed;inset:0;z-index:-1;pointer-events:none;background-image:radial-gradient(circle,#C8C4BD .5px,transparent .5px);background-size:18px 18px;opacity:.35}::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:#C8C4BD;border-radius:3px}.app-wrapper{max-width:1440px;margin:0 auto;height:100vh;display:flex;flex-direction:column;position:relative;background:#F7F5F1}.mobile-topbar{display:none;align-items:center;padding:12px 16px;background:#FDFCFA;border-bottom:1px solid #D6D1CA;z-index:30}.mobile-topbar .hamburger{width:36px;height:36px;border-radius:8px;border:1px solid #D6D1CA;background:#FDFCFA;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:18px;color:#8A8580}.mobile-topbar .topbar-logo{display:flex;align-items:center;gap:8px;margin-left:12px}.mobile-topbar .topbar-logo-icon{width:32px;height:32px;border-radius:8px;background:linear-gradient(135deg,#808C95,#9AA5AE);display:flex;align-items:center;justify-content:center;font-weight:800;font-size:16px;color:#fff}.mobile-topbar .topbar-logo-text{font-size:16px;font-weight:700;color:#3A3632}.app-layout{display:flex;flex:1;overflow:hidden}.sidebar{width:240px;min-width:240px;background:#FDFCFA;border-right:1px solid #D6D1CA;display:flex;flex-direction:column;z-index:20;transition:transform .3s;border-radius:0}.sidebar-logo{padding:20px 20px 16px;display:flex;align-items:center;gap:12px}.sidebar-logo-icon{width:42px;height:42px;border-radius:8px;background:linear-gradient(135deg,#808C95,#9AA5AE);display:flex;align-items:center;justify-content:center;font-weight:800;font-size:20px;color:#fff}.sidebar-logo-text{font-size:18px;font-weight:700;color:#3A3632}.sidebar-search{padding:0 16px 12px}.sidebar-search input{width:100%;padding:8px 12px 8px 32px;border-radius:6px;border:1px solid #D6D1CA;font-size:12px;outline:none;background:#F7F5F1 url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='14' height='14' fill='%23ABA6A1'%3E%3Cpath d='M10.5 9.5l3 3-1.5 1.5-3-3A5.5 5.5 0 117 12.5a5.5 5.5 0 013.5-3z'/%3E%3C/svg%3E") 10px center no-repeat;transition:border-color .2s}.sidebar-search input:focus{border-color:#808C95;background-color:#FDFCFA}.sidebar-nav{flex:1;padding:8px 12px;display:flex;flex-direction:column;gap:3px;overflow-y:auto}.nav-item{display:flex;align-items:center;gap:12px;padding:10px 14px;border-radius:6px;cursor:pointer;font-size:14px;font-weight:500;color:#8A8580;transition:all .15s;border:none;background:none;width:100%;text-align:left}.nav-item:hover{background:#EAE7E2;color:#808C95}.nav-item.active{background:#E6E9EC;color:#657078;font-weight:600}.nav-item .nav-icon{font-size:17px;width:24px;text-align:center;flex-shrink:0}.sidebar-footer{padding:16px;border-top:1px solid #EAE7E2;display:flex;align-items:center;gap:10px}.sidebar-avatar{width:36px;height:36px;border-radius:50% 50% 50% 50% / 60% 60% 40% 40%;box-shadow:inset 0 -3px 0 rgba(0,0,0,.1);display:flex;align-items:center;justify-content:center;font-size:16px;color:#fff;font-weight:700;flex-shrink:0}.sidebar-user-name{font-size:13px;font-weight:600;color:#3A3632}.sidebar-user-id{font-size:11px;color:#ABA6A1}.main-content{flex:1;overflow-y:auto;padding:28px 36px}.main-content-inner{max-width:1000px}.page-header{margin-bottom:24px}.page-title{font-size:24px;font-weight:700;color:#3A3632}.page-subtitle{font-size:13px;color:#ABA6A1;margin-top:4px}.page{display:none}.page.active{display:block}.video-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}@media(max-width:1200px){.video-grid{grid-template-columns:repeat(2,1fr)}}@media(max-width:640px){.video-grid{grid-template-columns:1fr}}.video-card{background:#FDFCFA;border-radius:8px;overflow:hidden;cursor:pointer;transition:all .15s;border:1px solid #D6D1CA}.video-card:hover{border-color:#808C95;background:#F0EEEA}.video-card-cover{aspect-ratio:16/9;display:flex;align-items:center;justify-content:center;font-size:36px;position:relative}.video-card-duration{position:absolute;bottom:8px;right:8px;background:rgba(58,54,50,.65);color:#fff;font-size:11px;padding:2px 8px;border-radius:4px;font-weight:500}.video-card-body{padding:14px}.video-card-title{font-size:14px;font-weight:600;line-height:1.4;color:#3A3632;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;margin-bottom:8px}.video-card-meta{display:flex;align-items:center;gap:6px;font-size:12px;color:#ABA6A1}.video-card-tag{font-size:11px;padding:3px 8px;border-radius:4px;background:#EAE7E2;color:#808C95;font-weight:500}.video-card-author{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.knowledge-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:16px}@media(max-width:640px){.knowledge-grid{grid-template-columns:1fr}}.knowledge-card{background:#FDFCFA;border-radius:8px;overflow:hidden;cursor:pointer;transition:all .15s;border:1px solid #D6D1CA}.knowledge-card:hover{border-color:#808C95}.knowledge-card-visual{height:120px;display:flex;align-items:center;justify-content:center;font-size:42px;position:relative;overflow:hidden}.knowledge-card-question{position:absolute;bottom:12px;left:16px;right:16px;font-size:14px;font-weight:700;color:#fff;text-shadow:0 1px 3px rgba(0,0,0,.3);line-height:1.3}.knowledge-card-body{padding:14px}.knowledge-card-title{font-size:15px;font-weight:700;color:#3A3632;margin-bottom:6px}.knowledge-card-desc{font-size:12px;color:#8A8580;line-height:1.5;margin-bottom:8px}.knowledge-card-time{font-size:11px;color:#ABA6A1}.story-container,.sim-container{background:#FDFCFA;border-radius:8px;padding:32px;border:1px solid #D6D1CA;min-height:400px}.story-stats{display:flex;gap:12px;margin-bottom:20px;flex-wrap:wrap}.story-stat{font-size:13px;padding:6px 14px;border-radius:8px;color:#fff;font-weight:600}.story-text{font-size:15px;line-height:1.8;color:#5A5550;min-height:120px;white-space:pre-line}.story-character{font-weight:700;color:#808C95;margin-bottom:8px;font-size:14px}.story-choices{display:flex;flex-direction:column;gap:10px;margin-top:20px}.story-choice{background:#F7F5F1;border:1px solid #D6D1CA;border-radius:8px;padding:14px 18px;font-size:14px;font-weight:600;cursor:pointer;transition:all .15s;text-align:left;color:#3A3632}.story-choice:hover{border-color:#808C95;background:#EAE7E2}.sim-stats{display:flex;gap:16px;margin-bottom:20px}.sim-stat{flex:1;background:#F7F5F1;border:1px solid #EAE7E2;border-radius:8px;padding:14px;text-align:center}.sim-stat-val{font-size:28px;font-weight:800;color:#3A3632}.sim-stat-label{font-size:11px;color:#8A8580;margin-top:4px}.filter-bar{display:flex;gap:8px;flex-wrap:wrap;margin-bottom:20px}.filter-btn{padding:7px 14px;border-radius:6px;border:1px solid #D6D1CA;background:#FDFCFA;font-size:13px;font-weight:500;color:#8A8580;cursor:pointer;transition:all .15s;white-space:nowrap}.filter-btn:hover{border-color:#808C95;color:#808C95}.filter-btn.active{background:#808C95;border-color:#808C95;color:#fff;font-weight:600}.search-results{display:none;position:absolute;top:100%;left:0;right:0;background:#FDFCFA;border:1px solid #D6D1CA;border-radius:6px;z-index:40;max-height:300px;overflow-y:auto;margin-top:4px}.search-results.show{display:block}.search-result-item{padding:10px 16px;cursor:pointer;border-bottom:1px solid #EAE7E2;font-size:13px;display:flex;align-items:center;gap:8px;color:#3A3632}.search-result-item:hover{background:#EAE7E2}.search-result-item:last-child{border-bottom:none}.search-result-type{font-size:10px;padding:2px 6px;border-radius:3px;background:#E6E9EC;color:#657078;font-weight:600;flex-shrink:0}.float-hub{position:fixed;bottom:24px;right:24px;z-index:25;display:flex;flex-direction:column;align-items:flex-end;gap:8px}.float-hub-btn{width:48px;height:48px;border-radius:50%;border:none;background:#808C95;color:#fff;font-size:20px;cursor:pointer;transition:all .2s;display:flex;align-items:center;justify-content:center}.float-hub-btn:hover{background:#657078;transform:scale(1.05)}.float-hub-expand{display:none;flex-direction:column;align-items:flex-end;gap:8px}.float-hub.open .float-hub-expand{display:flex}.float-hub-child{width:44px;height:44px;border-radius:50%;border:none;cursor:pointer;font-size:18px;display:flex;align-items:center;justify-content:center;transition:all .2s;position:relative;text-decoration:none}.float-hub-child:hover{transform:scale(1.08)}.float-hub-child.ai{background:#A8907E;color:#fff}.float-hub-child.mbti{background:linear-gradient(135deg,#8A9BA8,#A0B1BD);color:#fff}.float-hub-label{position:absolute;right:52px;white-space:nowrap;font-size:11px;font-weight:600;color:#8A8580;background:#FDFCFA;border:1px solid #D6D1CA;padding:4px 10px;border-radius:4px;pointer-events:none;opacity:0;transition:opacity .2s}.float-hub-child:hover .float-hub-label{opacity:1}.ai-panel{position:fixed;top:0;right:0;bottom:0;width:360px;background:#FDFCFA;border-left:1px solid #D6D1CA;z-index:30;transform:translateX(100%);transition:transform .3s;display:flex;flex-direction:column}.ai-panel.open{transform:translateX(0)}.ai-panel-header{padding:16px;border-bottom:1px solid #EAE7E2;display:flex;align-items:center;justify-content:space-between;font-weight:600;font-size:14px;color:#3A3632}.ai-panel-close{width:32px;height:32px;border-radius:50%;border:none;background:#EAE7E2;cursor:pointer;font-size:16px;display:flex;align-items:center;justify-content:center;color:#8A8580}.ai-panel-messages{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:12px}.ai-panel-input-area{padding:12px 16px;border-top:1px solid #EAE7E2;display:flex;gap:8px}.ai-panel-input-area input{flex:1;padding:10px 14px;border-radius:8px;border:1px solid #D6D1CA;font-size:13px;outline:none;background:#F7F5F1}.ai-panel-input-area input:focus{border-color:#808C95}.ai-send-btn{min-width:40px;height:40px;border-radius:50%;border:none;background:#808C95;color:#fff;font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center}.chat-msg{display:flex;animation:fadeIn .3s ease}@keyframes fadeIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}.chat-msg.assistant{justify-content:flex-start}.chat-msg.user{justify-content:flex-end}.chat-bubble{max-width:85%;padding:10px 14px;border-radius:8px;font-size:13px;line-height:1.6;word-break:break-word}.chat-msg.assistant .chat-bubble{background:#EAE7E2;color:#3A3632;border-bottom-left-radius:2px}.chat-msg.user .chat-bubble{background:#808C95;color:#fff;border-bottom-right-radius:2px}.modal-overlay{display:none;position:fixed;inset:0;background:rgba(58,54,50,.4);z-index:100;justify-content:center;align-items:center}.modal-overlay.show{display:flex}.modal-content{background:#FDFCFA;border-radius:16px;padding:28px;max-width:560px;width:90vw;max-height:80vh;overflow-y:auto;border:1px solid #D6D1CA;animation:modalIn .2s ease}@keyframes modalIn{from{opacity:0;transform:scale(.95)}to{opacity:1;transform:scale(1)}}.modal-close-btn{position:absolute;top:16px;right:16px;width:32px;height:32px;border-radius:50%;border:none;background:#EAE7E2;cursor:pointer;font-size:16px;display:flex;align-items:center;justify-content:center;color:#8A8580}.fav-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:16px}@media(max-width:1200px){.fav-grid{grid-template-columns:repeat(2,1fr)}}@media(max-width:640px){.fav-grid{grid-template-columns:1fr}}.fav-card{background:#FDFCFA;border-radius:8px;padding:16px;border:1px solid #D6D1CA;position:relative}.fav-card-remove{position:absolute;top:10px;right:10px;width:28px;height:28px;border-radius:50%;border:none;background:#F5EAEA;color:#C49595;cursor:pointer;font-size:14px;display:flex;align-items:center;justify-content:center}.spinner{width:32px;height:32px;border:3px solid #EAE7E2;border-top-color:#808C95;border-radius:50%;animation:spin .7s linear infinite;margin:40px auto}@keyframes spin{to{transform:rotate(360deg)}}.section-title{font-size:16px;font-weight:700;margin-bottom:16px;color:#3A3632}.banner{background:linear-gradient(135deg,#808C95,#9AA5AE);border-radius:8px;padding:28px 32px;color:#fff;margin-bottom:24px;position:relative;overflow:hidden}.banner::after{content:"";position:absolute;top:-30px;right:-30px;width:120px;height:120px;border-radius:50%;background:rgba(255,255,255,.08)}.banner-title{font-size:22px;font-weight:800;margin-bottom:8px;position:relative;z-index:1}.banner-text{font-size:14px;opacity:.85;position:relative;z-index:1}.ds-hero{text-align:center;padding:40px 20px 20px}.ds-hero-title{font-size:28px;font-weight:800;color:#3A3632;margin-bottom:8px;letter-spacing:-0.3px}.ds-hero-sub{font-size:14px;color:#8A8580;margin-bottom:20px}.ds-hero-canvas{display:block;margin:0 auto 20px;border-radius:8px;background:#FDFCFA;border:1px solid #D6D1CA;max-width:100%;height:auto}.ds-hero-cta{padding:12px 32px;border-radius:8px;border:none;background:#A8907E;color:#fff;font-size:15px;font-weight:700;cursor:pointer;transition:all .15s}.ds-hero-cta:hover{background:#927A6A;transform:translateY(-1px)}.ds-section-title{font-size:18px;font-weight:700;color:#3A3632;margin:28px 0 16px}.ds-card-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:16px;margin-bottom:8px}.ds-card-grid-3{grid-template-columns:repeat(3,1fr)}@media(max-width:640px){.ds-card-grid,.ds-card-grid-3{grid-template-columns:1fr}}.ds-feature-card{background:#FDFCFA;border-radius:8px;padding:24px;border:1px solid #D6D1CA;cursor:pointer;transition:all .15s;text-align:center}.ds-feature-card:hover{border-color:#808C95;background:#F0EEEA}.ds-feature-card-icon{font-size:36px;margin-bottom:10px}.ds-feature-card-name{font-size:15px;font-weight:700;color:#3A3632;margin-bottom:4px}.ds-feature-card-desc{font-size:12px;color:#8A8580;line-height:1.5}.ds-footer-note{padding:24px 0 8px;text-align:center;font-size:12px;color:#ABA6A1;border-top:1px solid #EAE7E2;margin-top:24px}.np-greeting{font-size:14px;color:#8A8580;margin-bottom:24px;line-height:1.6}.np-video-list{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}@media(max-width:1200px){.np-video-list{grid-template-columns:repeat(2,1fr)}}@media(max-width:640px){.np-video-list{grid-template-columns:1fr;gap:16px}}.np-card{background:#FDFCFA;border-radius:8px;overflow:hidden;border:1px solid #D6D1CA;cursor:pointer;transition:all .15s;display:flex;flex-direction:column}.np-card:hover{border-color:#808C95}.np-cover{position:relative;aspect-ratio:16/9;overflow:hidden;border-radius:8px 8px 0 0}.np-cover-inner{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;transition:transform .35s ease}.np-cover-icon{font-size:40px;opacity:.3}.np-cover-img{position:absolute;inset:0;width:100%;height:100%;object-fit:cover}.np-card:hover .np-cover-inner{transform:scale(1.05)}.np-cover-duration{position:absolute;top:8px;left:8px;background:rgba(58,54,50,.55);backdrop-filter:blur(4px);color:#fff;font-size:11px;font-weight:500;padding:2px 8px;border-radius:4px;letter-spacing:.3px;z-index:2}.np-cover-play{position:absolute;top:50%;left:50%;transform:translate(-50%,-50%);width:48px;height:48px;border-radius:50%;background:rgba(58,54,50,.4);backdrop-filter:blur(4px);color:#fff;font-size:16px;display:flex;align-items:center;justify-content:center;padding-left:3px;opacity:0;transition:opacity .25s;pointer-events:none;z-index:2}.np-card:hover .np-cover-play{opacity:1}.np-cover-fav{position:absolute;top:8px;right:8px;width:30px;height:30px;border-radius:50%;border:none;background:rgba(58,54,50,.3);backdrop-filter:blur(4px);font-size:14px;cursor:pointer;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity .2s;z-index:2}.np-card:hover .np-cover-fav,.np-cover-fav.faved{opacity:1}.np-body{padding:12px 14px 14px;display:flex;flex-direction:column;flex:1}.np-card-title{font-size:14px;font-weight:600;line-height:1.45;color:#3A3632;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;margin-bottom:8px;min-height:40px}.np-stats{display:flex;align-items:center;justify-content:space-between;font-size:12px;color:#ABA6A1;margin-bottom:8px}.np-stat{display:flex;align-items:center;gap:4px}.np-uploader{display:flex;align-items:center;gap:8px;margin-top:auto}.np-up-avatar{width:24px;height:24px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:12px;color:#fff;font-weight:600;flex-shrink:0}.np-up-name{font-size:12px;color:#8A8580;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.np-load-hint{grid-column:1/-1;text-align:center;font-size:12px;color:#ABA6A1;padding:16px 0 4px}.profile-card{background:linear-gradient(135deg,#808C95,#9AA5AE);border-radius:8px;padding:28px;color:#fff;display:flex;align-items:center;gap:16px;margin-bottom:20px;flex-wrap:wrap}.profile-avatar-lg{width:72px;height:72px;border-radius:50% 50% 50% 50% / 60% 60% 40% 40%;box-shadow:inset 0 -3px 0 rgba(0,0,0,.08);background:rgba(255,255,255,.2);display:flex;align-items:center;justify-content:center;font-size:32px;font-weight:800;flex-shrink:0}.profile-name-lg{font-size:22px;font-weight:800;margin-bottom:4px}.profile-id-lg{font-size:13px;opacity:.8}.profile-stats-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:16px;margin-bottom:20px}.profile-stat-card{background:#FDFCFA;border-radius:8px;padding:20px;text-align:center;border:1px solid #D6D1CA}.profile-stat-card .num{font-size:32px;font-weight:800;color:#808C95}.profile-stat-card .label{font-size:12px;color:#8A8580;margin-top:4px}.pet-card{background:linear-gradient(135deg,#8A9BA8,#9AADBA);border-radius:8px;padding:24px 28px;color:#fff;display:flex;align-items:center;gap:20px;margin-bottom:24px;position:relative;overflow:hidden}.pet-card::after{content:"";position:absolute;top:-20px;right:-20px;width:100px;height:100px;border-radius:50%;background:rgba(255,255,255,.1)}.pet-avatar{width:80px;height:80px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:40px;font-weight:800;background:rgba(255,255,255,.2);flex-shrink:0;position:relative;z-index:1}.pet-info{position:relative;z-index:1}.pet-name{font-size:20px;font-weight:800;margin-bottom:4px}.pet-type{font-size:13px;opacity:.85;margin-bottom:6px}.pet-exp-bar{height:6px;background:rgba(255,255,255,.25);border-radius:3px;overflow:hidden;width:200px}.pet-exp-fill{height:100%;background:#FDFCFA;border-radius:3px;transition:width .5s};.pet-level{font-size:12px;margin-top:4px;opacity:.8}.tool-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:16px;margin-bottom:24px}@media(max-width:640px){.tool-grid{grid-template-columns:1fr}}.tool-card{background:#FDFCFA;border-radius:8px;padding:20px;border:1px solid #D6D1CA;cursor:pointer;transition:all .15s;text-align:center}.tool-card:hover{border-color:#808C95;background:#F0EEEA}.tool-card-icon{font-size:36px;margin-bottom:10px}.tool-card-name{font-size:15px;font-weight:700;color:#3A3632;margin-bottom:4px}.tool-card-desc{font-size:12px;color:#8A8580}.tool-modal-body{padding:0;min-height:300px}.math-race .question{font-size:28px;font-weight:800;text-align:center;padding:20px;color:#3A3632}.math-race .answer-area{display:flex;gap:10px;padding:0 20px 20px;justify-content:center}.math-race input{width:120px;padding:12px;border:1px solid #D6D1CA;border-radius:6px;font-size:20px;text-align:center;outline:none}.math-race input:focus{border-color:#808C95}.math-race .result{text-align:center;padding:10px;font-weight:700}.math-race .stats{display:flex;justify-content:center;gap:30px;padding:10px;font-size:13px;color:#8A8580}.math-race .timer-bar{height:4px;background:#EAE7E2;margin:0 20px 20px;border-radius:2px;overflow:hidden}.math-race .timer-fill{height:100%;background:#8AA68A;transition:width .1s linear}.symbol-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:8px;padding:16px}.symbol-btn{padding:10px;border:1px solid #D6D1CA;border-radius:6px;background:#FDFCFA;cursor:pointer;text-align:center;font-weight:600;transition:all .15s;font-size:13px}.symbol-btn:hover{border-color:#808C95;background:#EAE7E2}.symbol-detail{padding:0 20px 20px;text-align:center;font-size:13px;color:#8A8580;line-height:1.6;min-height:60px}.graph-container{padding:16px;text-align:center}.graph-container canvas{border:1px solid #D6D1CA;border-radius:8px;display:block;margin:0 auto}.graph-inputs{display:flex;gap:8px;padding:0 16px 16px;justify-content:center}.graph-inputs input{padding:8px 12px;border:1px solid #D6D1CA;border-radius:6px;font-size:13px;outline:none;width:200px;background:#F7F5F1}.graph-inputs input:focus{border-color:#808C95}.graph-inputs button{padding:8px 16px;border-radius:6px;border:none;background:#808C95;color:#fff;font-size:13px;cursor:pointer;transition:all .15s}.graph-inputs button:hover{background:#657078}.sidebar-overlay{display:none;position:fixed;inset:0;background:rgba(0,0,0,.4);z-index:25}@media(max-width:1366px){.app-wrapper{max-width:100%}.main-content{padding:24px 28px}}@media(max-width:768px){body{overflow-x:hidden}.app-wrapper{max-width:100%;height:100dvh}.mobile-topbar{display:flex}.sidebar{position:fixed;top:0;left:0;bottom:0;height:100dvh;transform:translateX(-100%);z-index:30;border-right:1px solid #D6D1CA;border-radius:0}.sidebar.open{transform:translateX(0)}.sidebar-overlay.active{display:block}.main-content{padding:20px 16px}.main-content-inner{max-width:100%}.video-grid{grid-template-columns:1fr}.fav-grid{grid-template-columns:1fr}.knowledge-grid{grid-template-columns:1fr}.story-container,.sim-container{padding:20px;border-radius:8px}.banner{padding:20px}.banner-title{font-size:18px}.ai-panel{width:100%;max-width:100%}}.nav-icon svg{width:17px;height:17px;display:block;margin:0 auto;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}.lang-switch{display:flex;gap:2px;margin-left:auto}.lang-btn{padding:2px 6px;border-radius:4px;border:1px solid #D6D1CA;background:transparent;font-size:11px;font-weight:600;color:#ABA6A1;cursor:pointer;transition:all .15s;min-width:28px}.lang-btn.active{background:#808C95;color:#fff;border-color:#808C95}.lang-btn:hover:not(.active){color:#808C95;border-color:#808C95}.lang-switch.mobile-lang{margin-left:auto}::selection{background:#E6E9EC;color:#657078}button:focus-visible,input:focus-visible,[tabindex]:focus-visible{outline:2px solid #808C95;outline-offset:2px}.nav-item:active,.filter-btn:active,.ds-hero-cta:active,.symbol-btn:active,.graph-inputs button:active,.ai-send-btn:active{transform:scale(.97)}.ds-feature-card:active,.tool-card:active{transform:translateY(-1px) scale(.98)}::-webkit-scrollbar-thumb:hover{background:#ABA6A1}.sim-stat-val,.profile-stat-card .num,.math-race .stats,.np-cover-duration,.pet-type{font-variant-numeric:tabular-nums}.page-title,.ds-section-title,.section-title{letter-spacing:-0.01em}.sidebar-search input:focus,.math-race input:focus,.graph-inputs input:focus,.ai-panel-input-area input:focus{box-shadow:0 0 0 3px rgba(128,140,149,.12)}@keyframes fadeIn{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}@media(prefers-reduced-motion:reduce){*,*::before,*::after{animation-duration:.01ms!important;animation-iteration-count:1!important;transition-duration:.01ms!important}}@keyframes skeletonShimmer{0%{background-position:-400px 0}100%{background-position:400px 0}}.skeleton{background:linear-gradient(90deg,#EAE7E2 25%,#DED9D2 37%,#EAE7E2 63%);background-size:400px 100%;animation:skeletonShimmer 1.5s ease-in-out infinite;border-radius:4px}.skeleton-card{width:100%;height:180px;border-radius:8px;border:1px solid #EAE7E2;padding:14px;display:flex;flex-direction:column;gap:10px}.skeleton-line{height:12px;border-radius:3px}.skeleton-line.w80{width:80%}.skeleton-line.w60{width:60%}.skeleton-line.w40{width:40%}.skeleton-avatar{width:28px;height:28px;border-radius:50%}.toast-container{position:fixed;top:20px;right:20px;z-index:200;display:flex;flex-direction:column;gap:8px;pointer-events:none}.toast{background:#3A3632;color:#FDFCFA;padding:10px 18px;border-radius:6px;font-size:13px;font-weight:500;animation:toastIn .3s ease,toastOut .3s ease 2s forwards;max-width:280px;box-shadow:0 2px 8px rgba(58,54,50,.15)}@keyframes toastIn{from{opacity:0;transform:translateX(20px)}to{opacity:1;transform:translateX(0)}}@keyframes toastOut{from{opacity:1;transform:translateX(0)}to{opacity:0;transform:translateX(20px)}}@keyframes particleBurst{0%{opacity:1;transform:translate(0,0) scale(1)}100%{opacity:0;transform:translate(var(--px),var(--py)) scale(0)}}.particle{position:fixed;pointer-events:none;z-index:300;animation:particleBurst .7s ease-out forwards}.sidebar-mbti-card{margin:0 12px 8px;padding:14px;border-radius:6px;background:linear-gradient(135deg,#8A9BA8,#99A9B5);color:#fff;font-size:12px;font-weight:600;cursor:pointer;transition:all .15s;line-height:1.5}.sidebar-mbti-card:hover{background:linear-gradient(135deg,#7A8B9A,#8A9AA8);transform:scale(1.01)}.sidebar-mbti-card .mbti-card-icon{font-size:20px;margin-bottom:4px}.sidebar-mbti-card .mbti-card-arrow{float:right;opacity:.7;font-size:14px}.sidebar-logo-icon,.topbar-logo-icon{border-radius:50% 50% 50% 50% / 60% 60% 40% 40%;box-shadow:inset 0 -3px 0 rgba(0,0,0,.1)}.vine-divider{height:1px;background:linear-gradient(90deg,transparent,#C8C4BD 15%,#A8B886 50%,#C8C4BD 85%,transparent);margin:20px 0;position:relative}.vine-divider::after{content:"🏮";position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);font-size:14px;background:#F7F5F1;padding:0 8px}#toastBox .toast{border-radius:50% 50% 50% 50% / 60% 60% 40% 40%;padding:12px 24px}.skeleton-card{border-radius:50% 50% 50% 50% / 60% 60% 40% 40%;padding:20px 16px 16px}.skeleton-card::before{content:"";display:block;width:40px;height:16px;background:linear-gradient(90deg,#E8E5E0 25%,#F0EEEA 50%,#E8E5E0 75%);background-size:200% 100%;animation:shimmer 1.5s infinite;border-radius:8px;margin-bottom:12px}.float-hub-child.cards{background:#A8907E}.float-hub-child.cards:hover{background:#B8956E}.draw-btn{display:inline-flex;align-items:center;gap:8px;padding:10px 24px;border-radius:50% 50% 50% 50% / 60% 60% 40% 40%;border:none;background:linear-gradient(135deg,#A8907E,#C47F3A);color:#fff;font-size:14px;font-weight:700;cursor:pointer;transition:all .2s;box-shadow:0 3px 12px rgba(164,126,94,.3)}.draw-btn:hover{transform:scale(1.03);box-shadow:0 6px 20px rgba(164,126,94,.45)}.draw-btn:disabled{opacity:.5;cursor:not-allowed;transform:none;box-shadow:none}.draw-progress-bar{background:#F7F5F1;border-radius:8px;padding:14px 18px;border:1px solid #D6D1CA;display:flex;align-items:center;gap:12px;font-size:13px;color:#8A8580;margin-top:16px;cursor:pointer;transition:all .15s}.draw-progress-bar:hover{border-color:#A8907E;background:#FDF8F4}.draw-progress-track{flex:1;height:8px;background:#E8E5E0;border-radius:4px;overflow:hidden}.draw-progress-fill{height:100%;background:linear-gradient(90deg,#A8907E,#C47F3A);border-radius:4px;transition:width .4s cubic-bezier(.34,1.56,.64,1)}.draw-progress-icon{font-size:20px;flex-shrink:0}.draw-progress-ready{background:#FDF4EC;border-color:#C47F3A;animation:pulse-glow 2s infinite}@keyframes pulse-glow{0%,100%{box-shadow:0 0 0 0 rgba(196,127,58,.2)}50%{box-shadow:0 0 0 6px rgba(196,127,58,0)}}@keyframes gourd-shake{0%,100%{transform:rotate(0)}10%{transform:rotate(-8deg)}20%{transform:rotate(8deg)}30%{transform:rotate(-6deg)}40%{transform:rotate(6deg)}50%{transform:rotate(-3deg)}60%{transform:rotate(3deg)}70%{transform:rotate(-1deg)}80%{transform:rotate(1deg)}}.gourd-shaking{animation:gourd-shake .8s ease-in-out}@keyframes gourd-bounce{0%{transform:scale(1.2)}50%{transform:scale(.9)}70%{transform:scale(1.05)}100%{transform:scale(1)}}@keyframes card-flip{0%{transform:perspective(600px) rotateY(0)}50%{transform:perspective(600px) rotateY(90deg)}100%{transform:perspective(600px) rotateY(0)}}.card-flip-in{animation:card-flip .6s ease-in-out}.gacha-overlay{position:fixed;inset:0;background:rgba(0,0,0,.55);z-index:500;display:flex;align-items:center;justify-content:center;animation:fadeIn .25s}.gacha-modal{background:#FDFCFA;border-radius:16px;padding:28px 24px;max-width:360px;width:90%;text-align:center;position:relative;animation:gourd-bounce .5s ease-out}.gacha-gourd{font-size:72px;display:block;margin:0 auto 16px;cursor:pointer;user-select:none;transition:all .2s;filter:drop-shadow(0 4px 8px rgba(0,0,0,.15))}.gacha-gourd:hover{transform:scale(1.08)}.gacha-title{font-size:20px;font-weight:700;color:#3A3632;margin-bottom:4px}.gacha-sub{font-size:12px;color:#ABA6A1;margin-bottom:12px}.gacha-draws{font-size:13px;color:#8A8580;margin-bottom:8px}.gacha-draws b{color:#A8907E;font-size:16px}.gacha-close{position:absolute;top:12px;right:16px;background:none;border:none;font-size:20px;color:#ABA6A1;cursor:pointer;padding:4px}.gacha-card-reveal{margin-top:16px;animation:gourd-bounce .5s ease-out}.gacha-card-portrait{width:100px;height:100px;border-radius:50%;margin:0 auto 12px;display:flex;align-items:center;justify-content:center;font-size:48px;overflow:hidden;border:3px solid var(--clan-color,#A8907E)}.gacha-card-portrait img{width:100%;height:100%;object-fit:cover}.gacha-card-name{font-size:18px;font-weight:700;color:#3A3632;margin-bottom:2px}.gacha-card-type{font-size:12px;color:#ABA6A1;margin-bottom:4px}.gacha-card-clan{font-size:12px;padding:3px 12px;border-radius:12px;display:inline-block;margin-bottom:8px}.gacha-card-tagline{font-size:13px;color:#8A8580;font-style:italic;margin-bottom:12px}.gacha-card-dupe{font-size:12px;color:#C49595;margin-bottom:8px;font-style:italic}.gacha-card-detail-btn{padding:6px 18px;border-radius:20px;border:1px solid #D6D1CA;background:#FDFCFA;color:#8A8580;font-size:12px;cursor:pointer;transition:all .15s}.gacha-card-detail-btn:hover{border-color:#A8907E;color:#A8907E}.card-gallery-wrap{position:relative;margin:8px 0 20px}.card-gallery-scroll{display:flex;gap:10px;overflow-x:auto;padding:4px 2px 8px;scrollbar-width:none}.card-gallery-scroll::-webkit-scrollbar{display:none}.card-gallery-item{flex-shrink:0;width:72px;text-align:center;cursor:pointer;transition:all .15s}.card-gallery-item:hover{transform:translateY(-3px)}.card-gallery-item .cg-icon{width:56px;height:56px;border-radius:50%;margin:0 auto 4px;display:flex;align-items:center;justify-content:center;font-size:24px;border:2px solid transparent;transition:all .2s}.card-gallery-item:hover .cg-icon{border-color:var(--clan-color,#A8907E);box-shadow:0 3px 12px rgba(0,0,0,.1)}.card-gallery-item .cg-name{font-size:10px;color:#8A8580;line-height:1.3;max-width:72px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.card-gallery-empty{text-align:center;padding:20px;color:#ABA6A1;font-size:13px;border:1px dashed #D6D1CA;border-radius:8px}.card-gallery-empty .cg-empty-icon{font-size:28px;display:block;margin-bottom:6px}.card-detail-overlay{position:fixed;inset:0;background:rgba(0,0,0,.55);z-index:510;display:flex;align-items:center;justify-content:center;animation:fadeIn .25s}.card-detail-modal{background:#FDFCFA;border-radius:16px;padding:0;max-width:400px;width:90%;overflow:hidden;position:relative;animation:gourd-bounce .5s ease-out}.card-detail-header{padding:32px 24px 20px;text-align:center;position:relative}.card-detail-header .card-detail-bg{position:absolute;inset:0;opacity:.12}.card-detail-portrait{width:80px;height:80px;border-radius:50%;margin:0 auto 12px;display:flex;align-items:center;justify-content:center;font-size:40px;overflow:hidden;border:3px solid #FDFCFA;position:relative;z-index:1;box-shadow:0 3px 12px rgba(0,0,0,.1)}.card-detail-name{font-size:22px;font-weight:700;color:#3A3632;position:relative;z-index:1;margin-bottom:2px}.card-detail-binary{font-size:12px;color:#ABA6A1;letter-spacing:2px;position:relative;z-index:1;margin-bottom:6px}.card-detail-clan{display:inline-block;padding:4px 14px;border-radius:12px;font-size:12px;font-weight:600;position:relative;z-index:1;margin-bottom:6px}.card-detail-tagline{font-size:13px;color:#8A8580;font-style:italic;position:relative;z-index:1}.card-detail-body{padding:20px 24px 24px;border-top:1px solid #EAE7E2}.card-detail-section{margin-bottom:16px}.card-detail-section-title{font-size:12px;font-weight:700;color:#ABA6A1;text-transform:uppercase;letter-spacing:1px;margin-bottom:8px}.card-detail-section p{font-size:14px;color:#5A5652;line-height:1.7}.card-detail-fact{background:#F7F5F1;border-radius:10px;padding:14px;display:flex;gap:10px;align-items:flex-start;font-size:13px;color:#8A8580;line-height:1.6}.card-detail-fact .fact-emoji{font-size:18px;flex-shrink:0}.card-detail-obtain{background:#F7F5F1;border-radius:10px;padding:12px 14px;font-size:12px;color:#ABA6A1;display:flex;align-items:center;gap:8px;margin-top:12px}.card-detail-obtain .obtain-icon{font-size:16px}.card-detail-close{position:absolute;top:12px;right:16px;z-index:2;background:rgba(255,255,255,.8);border:none;border-radius:50%;width:32px;height:32px;display:flex;align-items:center;justify-content:center;font-size:16px;color:#8A8580;cursor:pointer}@keyframes gacha-particle-key{0%{opacity:1;transform:translate(0,0) scale(1)}100%{opacity:0;transform:translate(var(--gpx),var(--gpy)) scale(0) rotate(360deg)}}.gacha-particle{position:fixed;pointer-events:none;z-index:520;width:10px;height:10px;border-radius:50%;animation:gacha-particle-key 1s ease-out forwards}.knowledge-card-read{position:absolute;top:8px;right:8px;background:rgba(255,255,255,.85);border-radius:50%;width:28px;height:28px;display:flex;align-items:center;justify-content:center;font-size:14px;z-index:2}.knowledge-card-read.unread{opacity:.4}.cards-collection-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:12px}@media(max-width:1000px){.cards-collection-grid{grid-template-columns:repeat(3,1fr)}}@media(max-width:640px){.cards-collection-grid{grid-template-columns:repeat(2,1fr)}}.cc-card{background:#FDFCFA;border:1px solid #D6D1CA;border-radius:8px;padding:16px 12px;text-align:center;transition:all .15s}.cc-card.owned{cursor:pointer}.cc-card.owned:hover{border-color:var(--clan-color,#A8907E);box-shadow:0 3px 12px rgba(0,0,0,.08);transform:translateY(-2px)}.cc-card.locked{opacity:.45;filter:grayscale(.5)}.cc-card-icon{width:52px;height:52px;border-radius:50%;margin:0 auto 8px;display:flex;align-items:center;justify-content:center;font-size:24px}.cc-card-icon.locked-icon{background:#E8E5E0!important;color:#C8C4BD}.cc-card-name{font-size:13px;font-weight:600;color:#3A3632;margin-bottom:2px}.cc-card-type{font-size:10px;color:#ABA6A1;letter-spacing:1px;margin-bottom:4px}.cc-card-clan{font-size:10px;padding:2px 8px;border-radius:8px;display:inline-block}
diff --git a/static/css/mathbti.css b/static/css/mathbti.css
new file mode 100644
index 0000000..f8dc513
--- /dev/null
+++ b/static/css/mathbti.css
@@ -0,0 +1,138 @@
+/* MathBTI — 数学人格测试 独立页面 */
+/* 移动端优先 · 卡片风 · 与数学脑主站统一 */
+
+:root{--c-primary:#5B6AF0;--c-accent:#F97316;--c-text:#1E293B;--c-sub:#64748B;--c-muted:#94A3B8;--c-bg:#F8FAFC;--c-card:#FFF;--c-border:#E2E8F0;--shadow:0 4px 24px rgba(0,0,0,.06);--radius:16px;}
+*{margin:0;padding:0;box-sizing:border-box}
+html{font-size:16px;-webkit-text-size-adjust:100%}
+body{font-family:-apple-system,BlinkMacSystemFont,"PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif;background:var(--c-bg);color:var(--c-text);line-height:1.6;min-height:100dvh;overflow-x:hidden}
+
+/* ---- App Container ---- */
+.app{max-width:480px;margin:0 auto;padding:0 20px 40px;min-height:100dvh;display:flex;flex-direction:column;align-items:center;justify-content:center}
+.screen{display:none;width:100%;flex-direction:column;align-items:center;animation:fadeIn .35s ease}
+.screen.active{display:flex}
+@keyframes fadeIn{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}
+
+/* ---- Intro Screen ---- */
+.intro-header{text-align:center;margin-bottom:28px}
+.intro-logo{width:72px;height:72px;border-radius:50% 50% 50% 50% / 60% 60% 40% 40%;background:linear-gradient(135deg,var(--c-primary),#818CF8);color:#fff;font-size:36px;font-weight:700;display:flex;align-items:center;justify-content:center;margin:0 auto 20px;box-shadow:0 8px 32px rgba(91,106,240,.25),inset 0 -3px 0 rgba(0,0,0,.08)}
+.intro-title{font-size:28px;font-weight:800;color:var(--c-text);margin-bottom:8px}
+.intro-sub{font-size:15px;color:var(--c-sub);margin-bottom:12px}
+.intro-meta{font-size:13px;color:var(--c-muted);background:var(--c-bg);display:inline-block;padding:6px 14px;border-radius:20px}
+
+.intro-desc{display:flex;flex-direction:column;gap:8px;align-items:center;margin-bottom:24px;font-size:14px;color:var(--c-sub)}
+.intro-desc span{background:var(--c-card);border:1px solid var(--c-border);border-radius:8px;padding:8px 16px;width:100%;text-align:center;max-width:320px}
+
+.intro-axes{display:flex;flex-direction:column;gap:8px;margin-bottom:28px;width:100%;max-width:360px}
+.intro-axis-item{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:10px 14px;background:var(--c-card);border-radius:10px;border:1px solid var(--c-border)}
+.axis-label{font-size:13px;color:var(--c-sub);flex:1;text-align:center}
+.axis-emoji{font-size:18px}
+
+.btn-start{width:100%;max-width:360px;padding:16px 0;background:linear-gradient(135deg,var(--c-primary),#818CF8);color:#fff;border:none;border-radius:14px;font-size:17px;font-weight:700;cursor:pointer;transition:all .2s;box-shadow:0 4px 16px rgba(91,106,240,.3)}
+.btn-start:hover{transform:translateY(-1px);box-shadow:0 6px 24px rgba(91,106,240,.4)}
+.btn-start:active{transform:scale(.98)}
+
+/* ---- Quiz Screen ---- */
+.quiz-progress-wrap{width:100%;max-width:400px;margin-bottom:24px}
+.quiz-progress-bar{width:100%;height:6px;background:var(--c-border);border-radius:3px;overflow:hidden;margin-bottom:8px}
+.quiz-progress-fill{height:100%;background:linear-gradient(90deg,var(--c-primary),#818CF8);border-radius:3px;transition:width .3s ease}
+.quiz-progress-text{text-align:right;font-size:13px;color:var(--c-muted)}
+
+.quiz-question{width:100%;max-width:400px;font-size:18px;font-weight:700;color:var(--c-text);margin-bottom:20px;line-height:1.5;text-align:center}
+
+.quiz-options{width:100%;max-width:400px;display:flex;flex-direction:column;gap:10px;margin-bottom:20px}
+.quiz-option{display:flex;align-items:center;padding:14px 16px;background:var(--c-card);border:2px solid var(--c-border);border-radius:12px;cursor:pointer;transition:all .2s}
+.quiz-option input[type=radio]{display:none}
+.quiz-option:hover{border-color:var(--c-primary)30;background:#F5F7FF}
+.quiz-option.selected{border-color:var(--c-primary);background:#EEF0FF}
+.opt-text{font-size:15px;color:var(--c-text);line-height:1.4}
+
+.btn-next{width:100%;max-width:400px;padding:14px 0;background:var(--c-primary);color:#fff;border:none;border-radius:12px;font-size:16px;font-weight:600;cursor:pointer;transition:all .2s}
+.btn-next:disabled{background:var(--c-border);color:var(--c-muted);cursor:not-allowed;box-shadow:none}
+.btn-next:not(:disabled):hover{background:#4F5EE0;transform:translateY(-1px)}
+
+/* ---- Result Screen ---- */
+.result-header{text-align:center;margin-bottom:24px}
+.result-portrait-wrap{margin-bottom:16px}
+.result-portrait{width:96px;height:96px;border-radius:50%;margin:0 auto;box-shadow:0 4px 16px rgba(0,0,0,.08);overflow:hidden;position:relative;display:flex;align-items:center;justify-content:center}
+.result-icon-fallback{display:flex}
+.result-type-name{font-size:26px;font-weight:800;color:var(--c-text);margin-bottom:6px}
+.result-tagline{font-size:14px;color:var(--c-sub);font-style:italic;margin-bottom:10px}
+
+.result-mathematician{display:flex;flex-direction:column;align-items:center;gap:6px;margin-bottom:4px}
+.clan-badge{display:inline-block;padding:4px 12px;border-radius:20px;font-size:13px;font-weight:600}
+.mathematician-name{font-size:12px;color:var(--c-muted)}
+
+.result-keywords{display:flex;flex-wrap:wrap;justify-content:center;gap:6px;margin-bottom:20px}
+.kw-tag{padding:4px 12px;background:#EEF0FF;color:var(--c-primary);border-radius:20px;font-size:12px;font-weight:600}
+
+.result-card{width:100%;max-width:400px;background:var(--c-card);border-radius:var(--radius);padding:20px;box-shadow:var(--shadow);margin-bottom:16px}
+.result-card-title{font-size:14px;font-weight:700;color:var(--c-sub);margin-bottom:10px;text-transform:uppercase;letter-spacing:.5px}
+.result-card-desc{font-size:14px;color:var(--c-text);line-height:1.7;margin-bottom:8px}
+.result-card-short{font-size:13px;color:var(--c-sub);font-weight:500}
+
+/* Bio Expander */
+.bio-expander{width:100%;max-width:400px;background:var(--c-card);border-radius:var(--radius);box-shadow:var(--shadow);margin-bottom:16px;overflow:hidden}
+.bio-expander-header{display:flex;align-items:center;justify-content:space-between;padding:14px 18px;cursor:pointer;transition:background .15s}
+.bio-expander-header:hover{background:#FAFBFC}
+.bio-expander-left{display:flex;align-items:center;gap:8px}
+.bio-expander-icon{font-size:16px}
+.bio-expander-title{font-size:14px;font-weight:600;color:var(--c-text)}
+.bio-arrow{font-size:12px;color:var(--c-muted);transition:transform .2s}
+.bio-expander-body{max-height:0;overflow:hidden;transition:max-height .35s ease}
+.bio-inner{padding:0 18px 16px}
+.bio-text{font-size:13px;color:var(--c-sub);line-height:1.7;margin-bottom:12px}
+.bio-fact-box{display:flex;align-items:flex-start;gap:8px;background:#FEF9E7;border-radius:10px;padding:10px 14px}
+.bio-fact-emoji{font-size:14px;flex-shrink:0}
+.bio-fact-text{font-size:12px;color:#B45309;line-height:1.5}
+
+/* Axes Bars */
+.result-axes{width:100%;max-width:400px;margin-bottom:20px;display:none}
+.axis-bar-row{display:flex;align-items:center;gap:8px;margin-bottom:10px}
+.axis-bar-label{font-size:12px;color:var(--c-sub);white-space:nowrap;min-width:56px;text-align:center}
+.axis-bar-track{flex:1;height:6px;background:var(--c-border);border-radius:3px;overflow:hidden}
+.axis-bar-fill{height:100%;background:linear-gradient(90deg,var(--c-primary),#818CF8);border-radius:3px;transition:width .5s ease}
+
+/* Adopt */
+.result-adopt{width:100%;max-width:400px;text-align:center;margin-bottom:20px}
+.btn-adopt{width:100%;padding:14px 0;background:linear-gradient(135deg,#F97316,#FB923C);color:#fff;border:none;border-radius:12px;font-size:15px;font-weight:700;cursor:pointer;transition:all .2s;box-shadow:0 4px 16px rgba(249,115,22,.3)}
+.btn-adopt:hover{transform:translateY(-1px)}
+.btn-adopt:active{transform:scale(.98)}
+.btn-adopt.adopted{background:var(--c-border);color:var(--c-muted);box-shadow:none;cursor:default}
+.adopt-hint{font-size:12px;color:var(--c-muted);margin-top:8px;line-height:1.5}
+
+/* Share */
+.result-share{width:100%;max-width:400px;display:flex;flex-direction:column;gap:8px;margin-bottom:16px}
+.btn-share{width:100%;padding:12px 0;border:none;border-radius:10px;font-size:14px;font-weight:600;cursor:pointer;transition:all .2s}
+.btn-share.secondary{background:var(--c-bg);color:var(--c-sub);border:1px solid var(--c-border)}
+.btn-share.primary{background:var(--c-primary);color:#fff}
+.btn-share:active{transform:scale(.98)}
+
+.share-card-preview{width:100%;max-width:400px;text-align:center;margin-bottom:12px}
+
+.btn-retry{width:100%;max-width:400px;padding:12px 0;background:transparent;color:var(--c-muted);border:1px solid var(--c-border);border-radius:10px;font-size:14px;cursor:pointer;margin-top:8px;transition:all .2s}
+.btn-retry:hover{color:var(--c-sub);border-color:var(--c-sub)}
+
+.result-stats5{width:100%;max-width:400px;background:var(--c-card);border-radius:var(--radius);padding:16px;margin-bottom:16px;box-shadow:var(--shadow)}
+.result-stats5-title{font-size:13px;font-weight:700;color:var(--c-sub);margin-bottom:10px;letter-spacing:.5px}
+
+/* ---- Toast ---- */
+.toast{position:fixed;bottom:40px;left:50%;transform:translateX(-50%) translateY(20px);background:#1E293B;color:#fff;padding:10px 24px;border-radius:24px;font-size:14px;font-weight:600;z-index:99;opacity:0;transition:all .3s ease;pointer-events:none;white-space:nowrap;box-shadow:0 4px 16px rgba(0,0,0,.2)}
+.toast.show{opacity:1;transform:translateX(-50%) translateY(0)}
+
+/* ---- Desktop Tweaks ---- */
+@media(min-width:768px){
+ .app{max-width:520px;padding:40px 0 60px}
+ .intro-logo{width:88px;height:88px;font-size:42px}
+ .intro-title{font-size:32px}
+ .quiz-question{font-size:20px}
+}
+/* 进度条葫芦终点 */
+.quiz-progress-fill{position:relative}
+.quiz-progress-fill::after{content:"🏮";position:absolute;right:-10px;top:50%;transform:translateY(-50%);font-size:14px;display:var(--is-last, none)}
+
+/* ========== 卡片领取通知 ========== */
+.result-card-notice{background:linear-gradient(135deg,#FDF4EC,#FDF8F4);border:1px solid #C47F3A;border-radius:12px;padding:16px;text-align:center;margin-bottom:16px;animation:gourd-bounce .5s ease-out}
+.rcn-icon{font-size:28px;margin-bottom:6px}
+.rcn-text{font-size:14px;font-weight:600;color:#3A3632;margin-bottom:4px}
+.rcn-hint{font-size:12px;color:#8A8580}
+@keyframes gourd-bounce{0%{transform:scale(1.2)}50%{transform:scale(.9)}70%{transform:scale(1.05)}100%{transform:scale(1)}}
diff --git a/static/css/mathbti.min.css b/static/css/mathbti.min.css
new file mode 100644
index 0000000..9434f81
--- /dev/null
+++ b/static/css/mathbti.min.css
@@ -0,0 +1 @@
+:root{--c-primary:#5B6AF0;--c-accent:#F97316;--c-text:#1E293B;--c-sub:#64748B;--c-muted:#94A3B8;--c-bg:#F8FAFC;--c-card:#FFF;--c-border:#E2E8F0;--shadow:0 4px 24px rgba(0,0,0,.06);--radius:16px;}*{margin:0;padding:0;box-sizing:border-box}html{font-size:16px;-webkit-text-size-adjust:100%}body{font-family:-apple-system,BlinkMacSystemFont,"PingFang SC","Hiragino Sans GB","Microsoft YaHei",sans-serif;background:var(--c-bg);color:var(--c-text);line-height:1.6;min-height:100dvh;overflow-x:hidden}.app{max-width:480px;margin:0 auto;padding:0 20px 40px;min-height:100dvh;display:flex;flex-direction:column;align-items:center;justify-content:center}.screen{display:none;width:100%;flex-direction:column;align-items:center;animation:fadeIn .35s ease}.screen.active{display:flex}@keyframes fadeIn{from{opacity:0;transform:translateY(12px)}to{opacity:1;transform:translateY(0)}}.intro-header{text-align:center;margin-bottom:28px}.intro-logo{width:72px;height:72px;border-radius:50% 50% 50% 50% / 60% 60% 40% 40%;background:linear-gradient(135deg,var(--c-primary),#818CF8);color:#fff;font-size:36px;font-weight:700;display:flex;align-items:center;justify-content:center;margin:0 auto 20px;box-shadow:0 8px 32px rgba(91,106,240,.25),inset 0 -3px 0 rgba(0,0,0,.08)}.intro-title{font-size:28px;font-weight:800;color:var(--c-text);margin-bottom:8px}.intro-sub{font-size:15px;color:var(--c-sub);margin-bottom:12px}.intro-meta{font-size:13px;color:var(--c-muted);background:var(--c-bg);display:inline-block;padding:6px 14px;border-radius:20px}.intro-desc{display:flex;flex-direction:column;gap:8px;align-items:center;margin-bottom:24px;font-size:14px;color:var(--c-sub)}.intro-desc span{background:var(--c-card);border:1px solid var(--c-border);border-radius:8px;padding:8px 16px;width:100%;text-align:center;max-width:320px}.intro-axes{display:flex;flex-direction:column;gap:8px;margin-bottom:28px;width:100%;max-width:360px}.intro-axis-item{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:10px 14px;background:var(--c-card);border-radius:10px;border:1px solid var(--c-border)}.axis-label{font-size:13px;color:var(--c-sub);flex:1;text-align:center}.axis-emoji{font-size:18px}.btn-start{width:100%;max-width:360px;padding:16px 0;background:linear-gradient(135deg,var(--c-primary),#818CF8);color:#fff;border:none;border-radius:14px;font-size:17px;font-weight:700;cursor:pointer;transition:all .2s;box-shadow:0 4px 16px rgba(91,106,240,.3)}.btn-start:hover{transform:translateY(-1px);box-shadow:0 6px 24px rgba(91,106,240,.4)}.btn-start:active{transform:scale(.98)}.quiz-progress-wrap{width:100%;max-width:400px;margin-bottom:24px}.quiz-progress-bar{width:100%;height:6px;background:var(--c-border);border-radius:3px;overflow:hidden;margin-bottom:8px}.quiz-progress-fill{height:100%;background:linear-gradient(90deg,var(--c-primary),#818CF8);border-radius:3px;transition:width .3s ease}.quiz-progress-text{text-align:right;font-size:13px;color:var(--c-muted)}.quiz-question{width:100%;max-width:400px;font-size:18px;font-weight:700;color:var(--c-text);margin-bottom:20px;line-height:1.5;text-align:center}.quiz-options{width:100%;max-width:400px;display:flex;flex-direction:column;gap:10px;margin-bottom:20px}.quiz-option{display:flex;align-items:center;padding:14px 16px;background:var(--c-card);border:2px solid var(--c-border);border-radius:12px;cursor:pointer;transition:all .2s}.quiz-option input[type=radio]{display:none}.quiz-option:hover{border-color:var(--c-primary)30;background:#F5F7FF}.quiz-option.selected{border-color:var(--c-primary);background:#EEF0FF}.opt-text{font-size:15px;color:var(--c-text);line-height:1.4}.btn-next{width:100%;max-width:400px;padding:14px 0;background:var(--c-primary);color:#fff;border:none;border-radius:12px;font-size:16px;font-weight:600;cursor:pointer;transition:all .2s}.btn-next:disabled{background:var(--c-border);color:var(--c-muted);cursor:not-allowed;box-shadow:none}.btn-next:not(:disabled):hover{background:#4F5EE0;transform:translateY(-1px)}.result-header{text-align:center;margin-bottom:24px}.result-portrait-wrap{margin-bottom:16px}.result-portrait{width:96px;height:96px;border-radius:50%;margin:0 auto;box-shadow:0 4px 16px rgba(0,0,0,.08);overflow:hidden;position:relative;display:flex;align-items:center;justify-content:center}.result-icon-fallback{display:flex}.result-type-name{font-size:26px;font-weight:800;color:var(--c-text);margin-bottom:6px}.result-tagline{font-size:14px;color:var(--c-sub);font-style:italic;margin-bottom:10px}.result-mathematician{display:flex;flex-direction:column;align-items:center;gap:6px;margin-bottom:4px}.clan-badge{display:inline-block;padding:4px 12px;border-radius:20px;font-size:13px;font-weight:600}.mathematician-name{font-size:12px;color:var(--c-muted)}.result-keywords{display:flex;flex-wrap:wrap;justify-content:center;gap:6px;margin-bottom:20px}.kw-tag{padding:4px 12px;background:#EEF0FF;color:var(--c-primary);border-radius:20px;font-size:12px;font-weight:600}.result-card{width:100%;max-width:400px;background:var(--c-card);border-radius:var(--radius);padding:20px;box-shadow:var(--shadow);margin-bottom:16px}.result-card-title{font-size:14px;font-weight:700;color:var(--c-sub);margin-bottom:10px;text-transform:uppercase;letter-spacing:.5px}.result-card-desc{font-size:14px;color:var(--c-text);line-height:1.7;margin-bottom:8px}.result-card-short{font-size:13px;color:var(--c-sub);font-weight:500}.bio-expander{width:100%;max-width:400px;background:var(--c-card);border-radius:var(--radius);box-shadow:var(--shadow);margin-bottom:16px;overflow:hidden}.bio-expander-header{display:flex;align-items:center;justify-content:space-between;padding:14px 18px;cursor:pointer;transition:background .15s}.bio-expander-header:hover{background:#FAFBFC}.bio-expander-left{display:flex;align-items:center;gap:8px}.bio-expander-icon{font-size:16px}.bio-expander-title{font-size:14px;font-weight:600;color:var(--c-text)}.bio-arrow{font-size:12px;color:var(--c-muted);transition:transform .2s}.bio-expander-body{max-height:0;overflow:hidden;transition:max-height .35s ease}.bio-inner{padding:0 18px 16px}.bio-text{font-size:13px;color:var(--c-sub);line-height:1.7;margin-bottom:12px}.bio-fact-box{display:flex;align-items:flex-start;gap:8px;background:#FEF9E7;border-radius:10px;padding:10px 14px}.bio-fact-emoji{font-size:14px;flex-shrink:0}.bio-fact-text{font-size:12px;color:#B45309;line-height:1.5}.result-axes{width:100%;max-width:400px;margin-bottom:20px;display:none}.axis-bar-row{display:flex;align-items:center;gap:8px;margin-bottom:10px}.axis-bar-label{font-size:12px;color:var(--c-sub);white-space:nowrap;min-width:56px;text-align:center}.axis-bar-track{flex:1;height:6px;background:var(--c-border);border-radius:3px;overflow:hidden}.axis-bar-fill{height:100%;background:linear-gradient(90deg,var(--c-primary),#818CF8);border-radius:3px;transition:width .5s ease}.result-adopt{width:100%;max-width:400px;text-align:center;margin-bottom:20px}.btn-adopt{width:100%;padding:14px 0;background:linear-gradient(135deg,#F97316,#FB923C);color:#fff;border:none;border-radius:12px;font-size:15px;font-weight:700;cursor:pointer;transition:all .2s;box-shadow:0 4px 16px rgba(249,115,22,.3)}.btn-adopt:hover{transform:translateY(-1px)}.btn-adopt:active{transform:scale(.98)}.btn-adopt.adopted{background:var(--c-border);color:var(--c-muted);box-shadow:none;cursor:default}.adopt-hint{font-size:12px;color:var(--c-muted);margin-top:8px;line-height:1.5}.result-share{width:100%;max-width:400px;display:flex;flex-direction:column;gap:8px;margin-bottom:16px}.btn-share{width:100%;padding:12px 0;border:none;border-radius:10px;font-size:14px;font-weight:600;cursor:pointer;transition:all .2s}.btn-share.secondary{background:var(--c-bg);color:var(--c-sub);border:1px solid var(--c-border)}.btn-share.primary{background:var(--c-primary);color:#fff}.btn-share:active{transform:scale(.98)}.share-card-preview{width:100%;max-width:400px;text-align:center;margin-bottom:12px}.btn-retry{width:100%;max-width:400px;padding:12px 0;background:transparent;color:var(--c-muted);border:1px solid var(--c-border);border-radius:10px;font-size:14px;cursor:pointer;margin-top:8px;transition:all .2s}.btn-retry:hover{color:var(--c-sub);border-color:var(--c-sub)}.toast{position:fixed;bottom:40px;left:50%;transform:translateX(-50%) translateY(20px);background:#1E293B;color:#fff;padding:10px 24px;border-radius:24px;font-size:14px;font-weight:600;z-index:99;opacity:0;transition:all .3s ease;pointer-events:none;white-space:nowrap;box-shadow:0 4px 16px rgba(0,0,0,.2)}.toast.show{opacity:1;transform:translateX(-50%) translateY(0)}@media(min-width:768px){.app{max-width:520px;padding:40px 0 60px}.intro-logo{width:88px;height:88px;font-size:42px}.intro-title{font-size:32px}.quiz-question{font-size:20px}}.quiz-progress-fill{position:relative}.quiz-progress-fill::after{content:"🏮";position:absolute;right:-10px;top:50%;transform:translateY(-50%);font-size:14px;display:var(--is-last,none)}.result-card-notice{background:linear-gradient(135deg,#FDF4EC,#FDF8F4);border:1px solid #C47F3A;border-radius:12px;padding:16px;text-align:center;margin-bottom:16px;animation:gourd-bounce .5s ease-out}.rcn-icon{font-size:28px;margin-bottom:6px}.rcn-text{font-size:14px;font-weight:600;color:#3A3632;margin-bottom:4px}.rcn-hint{font-size:12px;color:#8A8580}@keyframes gourd-bounce{0%{transform:scale(1.2)}50%{transform:scale(.9)}70%{transform:scale(1.05)}100%{transform:scale(1)}}
\ No newline at end of file
diff --git a/static/css/style.css b/static/css/style.css
new file mode 100644
index 0000000..3f120d6
--- /dev/null
+++ b/static/css/style.css
@@ -0,0 +1,2249 @@
+/* =========================================================
+ style.css — 数学脑 v8.0 · 莫兰迪高保真 UI
+ 设计方向:极简 / 现代 / 干净
+ 色彩方案:Morandi Palette(低饱和、哑光质感)
+ 排版层级清晰,微阴影,圆角设计
+ ========================================================= */
+
+:root {
+ /* ═══ 莫兰迪色系 (Morandi Palette) ═══ */
+ --morandi-bg: #F0F2F5; /* 极浅燕麦色/米灰 — 全局背景 */
+ --morandi-card: #FAFBFC; /* 极淡冷灰白 — 卡片底色 */
+ --morandi-card-warm: #F5F0E6; /* 淡米黄 — 卡片杂色变体 */
+ --morandi-card-cool: #EEF2F0; /* 淡灰绿 — 卡片杂色变体 */
+ --morandi-card-lilac:#F2EEF5; /* 淡灰紫 — 卡片杂色变体 */
+
+ --morandi-blue: #A0B9D8; /* 灰雾蓝 — 主色调 */
+ --morandi-blue-deep: #8AA3C2; /* 深雾蓝 — hover/active */
+ --morandi-blue-light:#DCE6F0; /* 极淡雾蓝 — 浅色背景 */
+ --morandi-pink: #DAB8B0; /* 豆沙粉 — 辅助色 */
+ --morandi-pink-light:#F2E4DF; /* 极淡豆沙粉 */
+ --morandi-green: #B8D0C9; /* 灰豆绿 — 辅助色 */
+ --morandi-green-light:#E4EFEB; /* 极淡灰豆绿 */
+ --morandi-purple: #C5CAD0; /* 灰紫 — 个人中心 */
+ --morandi-purple-deep:#AEB3B9;
+
+ --morandi-text: #4A4A4A; /* 深岩灰 — 标题/正文 */
+ --morandi-text-sub: #999999; /* 浅暖灰 — 辅助信息 */
+ --morandi-text-muted:#B0B0B0; /* 更浅灰 — 占位等 */
+ --morandi-border: #E8EAED; /* 极淡灰边框 */
+ --morandi-divider: #EEEEEE; /* 分割线 */
+
+ /* ═══ 模块色(莫兰迪变体) ═══ */
+ --module-discover: #A0B9D8;
+ --module-discover-light: #DCE6F0;
+ --module-knowledge: #B8D0C9;
+ --module-knowledge-light:#E4EFEB;
+ --module-life: #DAB8B0;
+ --module-life-light: #F2E4DF;
+ --module-profile: #C5CAD0;
+ --module-profile-light:#EDEEF0;
+
+ /* ═══ 旧 CSS 变量兼容 ═══ */
+ --primary: #A0B9D8;
+ --primary-dark: #8AA3C2;
+ --primary-light: #DCE6F0;
+ --shuren: #A0B9D8;
+ --shuren-light: #DCE6F0;
+ --shuren-gradient: linear-gradient(135deg, #A0B9D8, #8AA3C2);
+ --shushuo: #B8D0C9;
+ --shushuo-light: #E4EFEB;
+ --shushuo-gradient: linear-gradient(135deg, #B8D0C9, #A0BBAF);
+ --shusi: #DAB8B0;
+ --shusi-light: #F2E4DF;
+ --shusi-gradient: linear-gradient(135deg, #DAB8B0, #C9A39A);
+ --mine: #C5CAD0;
+ --mine-light: #EDEEF0;
+ --mine-gradient: linear-gradient(135deg, #C5CAD0, #AEB3B9);
+ --bg: #F0F2F5;
+ --card: #FAFBFC;
+ --text: #4A4A4A;
+ --text-secondary: #999999;
+ --text-muted: #B0B0B0;
+ --border: #E8EAED;
+ --sidebar-bg: #4A4A4A;
+ --sidebar-text: #999999;
+ --sidebar-active: #FAFBFC;
+ --radius-sm: 8px;
+ --radius: 12px;
+ --radius-lg: 16px;
+ --radius-xl: 20px;
+ --shadow-sm: 0 1px 3px rgba(0,0,0,0.04);
+ --shadow: 0 2px 8px rgba(0,0,0,0.05);
+ --shadow-lg: 0 4px 16px rgba(0,0,0,0.06);
+ --accent: var(--primary);
+ --accent-light: var(--primary-light);
+ --accent-gradient: linear-gradient(135deg, var(--primary), var(--primary-dark));
+}
+
+/* ═══════════════════════════════════════════════════════════
+ Reset & Base
+ ═══════════════════════════════════════════════════════════ */
+* { margin: 0; padding: 0; box-sizing: border-box; }
+html { scroll-behavior: smooth; -webkit-font-smoothing: antialiased; }
+body {
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", "Helvetica Neue", sans-serif;
+ background: #E2E5EA;
+ display: flex;
+ justify-content: center;
+ align-items: flex-start;
+ min-height: 100vh;
+ color: var(--morandi-text);
+ -webkit-tap-highlight-color: transparent;
+}
+
+/* 手机外壳模拟 */
+body::before {
+ content: "";
+ position: fixed;
+ top: 0; left: 50%;
+ transform: translateX(-50%);
+ width: min(390px, 100vw);
+ height: 100dvh;
+ background: var(--morandi-bg);
+ z-index: -1;
+ box-shadow: 0 0 80px rgba(0,0,0,0.06);
+}
+
+/* ═══════════════════════════════════════════════════════════
+ App Container
+ ═══════════════════════════════════════════════════════════ */
+.app-container {
+ display: flex;
+ flex-direction: column;
+ width: min(390px, 100vw);
+ max-width: 390px;
+ height: 100dvh;
+ overflow: hidden;
+ background: var(--morandi-bg);
+ position: relative;
+}
+
+/* ═══════════════════════════════════════════════════════════
+ Top Bar
+ ═══════════════════════════════════════════════════════════ */
+.top-bar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: calc(10px + env(safe-area-inset-top, 0px)) 16px 8px;
+ background: var(--morandi-bg);
+ z-index: 50;
+}
+.top-bar-brand {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+.brand-icon-sm {
+ width: 28px; height: 28px;
+ background: var(--morandi-blue);
+ border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 16px;
+ color: #fff;
+ font-weight: 700;
+ box-shadow: 0 2px 6px rgba(160,185,216,0.25), inset 0 -3px 0 rgba(0,0,0,0.08);
+}
+.brand-text-sm {
+ font-size: 15px;
+ font-weight: 700;
+ color: var(--morandi-text);
+ letter-spacing: 0.3px;
+}
+.top-bar-actions {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+.icon-btn {
+ width: 36px; height: 36px;
+ border-radius: 50%;
+ border: 1px solid var(--morandi-border);
+ background: rgba(250, 251, 252, 0.85);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 16px;
+ color: var(--morandi-text-sub);
+ cursor: pointer;
+ box-shadow: var(--shadow-sm);
+ transition: all 0.2s;
+}
+.icon-btn:active {
+ background: var(--morandi-blue-light);
+ border-color: var(--morandi-blue);
+ color: var(--morandi-blue);
+}
+
+/* ═══════════════════════════════════════════════════════════
+ Content Area
+ ═══════════════════════════════════════════════════════════ */
+.content-area {
+ flex: 1;
+ overflow-y: auto;
+ overflow-x: hidden;
+ background: var(--morandi-bg);
+ display: flex;
+ flex-direction: column;
+}
+
+/* Section Title Bar */
+.section-title-bar {
+ padding: 12px 16px 10px;
+ background: var(--morandi-bg);
+ position: sticky;
+ top: 0;
+ z-index: 20;
+}
+.section-title {
+ font-size: 16px;
+ font-weight: 700;
+ color: var(--morandi-text);
+ letter-spacing: 0.2px;
+}
+
+/* Module Banner */
+.module-banner {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 12px 14px;
+ margin: 0 12px 10px;
+ border-radius: var(--radius);
+ transition: all 0.3s;
+}
+.module-banner.shuren { background: var(--morandi-blue-light); }
+.module-banner.shushuo { background: var(--morandi-green-light); }
+.module-banner.shusi { background: var(--morandi-pink-light); }
+.module-banner.shuwu { background: var(--module-profile-light); }
+.banner-icon { font-size: 24px; flex-shrink: 0; opacity: 0.7; }
+.banner-text { flex: 1; }
+.banner-title {
+ font-size: 12px;
+ font-weight: 600;
+ margin-bottom: 2px;
+}
+.module-banner.shuren .banner-title { color: #6B8AAB; }
+.module-banner.shushuo .banner-title { color: #7BA89A; }
+.module-banner.shusi .banner-title { color: #B8958A; }
+.module-banner.shuwu .banner-title { color: #8A8F94; }
+.banner-sub { font-size: 11px; color: var(--morandi-text-sub); line-height: 1.5; }
+
+/* ═══════════════════════════════════════════════════════════
+ Category Filter
+ ═══════════════════════════════════════════════════════════ */
+.category-filter {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 6px;
+ padding: 0 12px 10px;
+}
+.cat-btn {
+ padding: 5px 12px;
+ border-radius: 14px;
+ border: 1px solid var(--morandi-border);
+ background: var(--morandi-card);
+ font-size: 11px;
+ font-weight: 500;
+ color: var(--morandi-text-sub);
+ cursor: pointer;
+ transition: all 0.2s;
+ white-space: nowrap;
+ box-shadow: var(--shadow-sm);
+}
+.cat-btn:active,
+.cat-btn.active {
+ border-color: var(--morandi-blue);
+ background: var(--morandi-blue-light);
+ color: var(--morandi-blue);
+ font-weight: 600;
+}
+
+/* ═══════════════════════════════════════════════════════════
+ 存档面板 (Save Panel) — v8.3
+ ═══════════════════════════════════════════════════════════ */
+.save-panel-overlay {
+ position: fixed;
+ top: 0; left: 0; right: 0; bottom: 0;
+ background: rgba(74, 74, 74, 0.45);
+ z-index: 180;
+ display: none;
+ align-items: center;
+ justify-content: center;
+ backdrop-filter: blur(4px);
+}
+.save-panel-overlay.show { display: flex; }
+.save-panel {
+ background: var(--morandi-bg);
+ border-radius: var(--radius-xl);
+ width: min(360px, 92vw);
+ max-height: 78vh;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ box-shadow: 0 16px 48px rgba(0,0,0,0.12);
+}
+.save-panel-header {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 14px 16px;
+ background: var(--morandi-card);
+ border-bottom: 1px solid var(--morandi-border);
+ flex-shrink: 0;
+}
+.sp-back {
+ border: none;
+ background: var(--morandi-bg);
+ padding: 5px 10px;
+ border-radius: 7px;
+ font-size: 12px;
+ color: var(--morandi-text-sub);
+ cursor: pointer;
+}
+.sp-title {
+ font-size: 15px;
+ font-weight: 700;
+ color: var(--morandi-text);
+ flex: 1;
+}
+.save-slots {
+ flex: 1;
+ overflow-y: auto;
+ padding: 12px 14px 20px;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+.save-slot {
+ background: var(--morandi-card);
+ border-radius: var(--radius);
+ padding: 14px 16px;
+ box-shadow: var(--shadow-sm);
+ border: 1px solid var(--morandi-border);
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ cursor: pointer;
+ transition: transform 0.15s, border-color 0.2s;
+}
+.save-slot:active { transform: scale(0.98); }
+.save-slot.active {
+ border-color: var(--morandi-blue);
+ box-shadow: 0 2px 12px rgba(160,185,216,0.15);
+}
+.save-slot-empty {
+ opacity: 0.5;
+ justify-content: center;
+ padding: 22px;
+ color: var(--morandi-text-muted);
+ font-size: 13px;
+}
+.save-slot-icon {
+ width: 42px; height: 42px;
+ border-radius: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 20px;
+ flex-shrink: 0;
+}
+.save-slot-info { flex: 1; min-width: 0; }
+.save-slot-name {
+ font-size: 13px;
+ font-weight: 700;
+ color: var(--morandi-text);
+ margin-bottom: 3px;
+}
+.save-slot-meta {
+ font-size: 10px;
+ color: var(--morandi-text-muted);
+ display: flex;
+ gap: 8px;
+}
+.save-slot-actions {
+ display: flex;
+ gap: 6px;
+ flex-shrink: 0;
+}
+.save-slot-btn {
+ padding: 5px 10px;
+ border: 1px solid var(--morandi-border);
+ border-radius: 7px;
+ background: var(--morandi-bg);
+ font-size: 10px;
+ font-weight: 600;
+ cursor: pointer;
+ color: var(--morandi-text-sub);
+ white-space: nowrap;
+ transition: all 0.15s;
+}
+.save-slot-btn.primary {
+ background: var(--morandi-blue);
+ color: #fff;
+ border-color: transparent;
+}
+.save-slot-btn:active { transform: scale(0.95); }
+
+/* ═══════════════════════════════════════════════════════════
+ 结局画廊 (Ending Gallery) — v8.3
+ ═══════════════════════════════════════════════════════════ */
+.gallery-overlay {
+ position: fixed;
+ top: 0; left: 0; right: 0; bottom: 0;
+ background: rgba(74, 74, 74, 0.45);
+ z-index: 180;
+ display: none;
+ align-items: center;
+ justify-content: center;
+ backdrop-filter: blur(4px);
+}
+.gallery-overlay.show { display: flex; }
+.gallery-panel {
+ background: var(--morandi-bg);
+ border-radius: var(--radius-xl);
+ width: min(360px, 92vw);
+ max-height: 80vh;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+ box-shadow: 0 16px 48px rgba(0,0,0,0.12);
+}
+.gallery-header {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 14px 16px;
+ background: var(--morandi-card);
+ border-bottom: 1px solid var(--morandi-border);
+ flex-shrink: 0;
+}
+.gallery-progress {
+ font-size: 12px;
+ font-weight: 600;
+ color: var(--morandi-blue);
+ white-space: nowrap;
+}
+.gallery-grid {
+ flex: 1;
+ overflow-y: auto;
+ padding: 16px 14px 24px;
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 10px;
+}
+.gallery-card {
+ background: var(--morandi-card);
+ border-radius: var(--radius);
+ padding: 18px 14px;
+ text-align: center;
+ box-shadow: var(--shadow-sm);
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 8px;
+ transition: transform 0.15s;
+}
+.gallery-card.unlocked { cursor: pointer; }
+.gallery-card.unlocked:active { transform: scale(0.96); }
+.gallery-card.locked {
+ opacity: 0.4;
+ filter: grayscale(1);
+}
+.gallery-badge {
+ font-size: 32px;
+}
+.gallery-card-name {
+ font-size: 12px;
+ font-weight: 700;
+ color: var(--morandi-text);
+}
+.gallery-card-desc {
+ font-size: 10px;
+ color: var(--morandi-text-muted);
+ line-height: 1.4;
+}
+.gallery-card-lock {
+ font-size: 20px;
+ opacity: 0.5;
+}
+
+/* ═══════════════════════════════════════════════════════════
+ Search & Sub Nav
+ ═══════════════════════════════════════════════════════════ */
+.search-header {
+ padding: 10px 14px 4px;
+ background: var(--morandi-bg);
+ position: sticky;
+ top: 0;
+ z-index: 25;
+}
+.search-bar-wrap {
+ display: flex;
+ align-items: center;
+ background: var(--morandi-card);
+ padding: 9px 14px;
+ border-radius: 22px;
+ box-shadow: 0 1px 4px rgba(0,0,0,0.04);
+ border: 1px solid var(--morandi-border);
+ transition: border-color 0.2s, box-shadow 0.2s;
+}
+.search-bar-wrap:focus-within {
+ border-color: var(--morandi-blue);
+ box-shadow: 0 1px 8px rgba(160,185,216,0.15);
+}
+.search-icon { margin-right: 8px; font-size: 13px; opacity: 0.35; }
+.search-bar-wrap input {
+ flex: 1;
+ border: none;
+ background: transparent;
+ outline: none;
+ font-size: 13px;
+ color: var(--morandi-text);
+}
+.search-bar-wrap input::placeholder { color: var(--morandi-text-muted); }
+
+.sub-nav {
+ display: flex;
+ gap: 22px;
+ padding: 6px 16px 8px;
+ background: var(--morandi-bg);
+ border-bottom: 1px solid var(--morandi-border);
+ margin-bottom: 10px;
+}
+.sub-nav-btn {
+ border: none;
+ background: transparent;
+ font-size: 13px;
+ font-weight: 500;
+ color: var(--morandi-text-sub);
+ cursor: pointer;
+ padding: 4px 0;
+ position: relative;
+ transition: color 0.2s;
+ letter-spacing: 0.2px;
+}
+.sub-nav-btn:active { color: var(--morandi-text); }
+.sub-nav-btn.active {
+ color: var(--morandi-text);
+ font-weight: 700;
+}
+.sub-nav-btn.active::after {
+ content: "";
+ position: absolute;
+ bottom: 0; left: 50%;
+ transform: translateX(-50%);
+ width: 18px;
+ height: 3px;
+ background: var(--morandi-blue);
+ border-radius: 2px;
+}
+
+/* ═══════════════════════════════════════════════════════════
+ Discipline Grid
+ ═══════════════════════════════════════════════════════════ */
+.discipline-grid-container {
+ padding: 14px 14px 20px;
+ animation: fadeIn 0.3s ease;
+}
+.grid-section-title {
+ font-size: 15px;
+ font-weight: 700;
+ color: var(--morandi-text);
+ margin-bottom: 16px;
+ letter-spacing: 0.2px;
+}
+.discipline-grid {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 12px 10px;
+}
+.discipline-item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 8px;
+ cursor: pointer;
+ transition: transform 0.2s;
+ padding: 16px 6px 12px;
+ border-radius: var(--radius-lg);
+ background: var(--morandi-card);
+ box-shadow: var(--shadow-sm);
+}
+.discipline-item:active {
+ transform: scale(0.95);
+ box-shadow: var(--shadow);
+}
+.discipline-icon-box {
+ width: 56px;
+ height: 56px;
+ border-radius: 16px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 24px;
+ font-weight: 700;
+ transition: all 0.2s;
+}
+.discipline-item:hover .discipline-icon-box {
+ transform: translateY(-2px);
+ box-shadow: var(--shadow);
+}
+.discipline-label {
+ font-size: 11px;
+ font-weight: 600;
+ color: var(--morandi-text);
+ text-align: center;
+ white-space: nowrap;
+ letter-spacing: 0.2px;
+}
+.discipline-active-header {
+ padding: 10px 14px;
+ background: var(--morandi-bg);
+ border-bottom: 1px solid var(--morandi-border);
+ margin-bottom: 10px;
+ display: flex;
+ align-items: center;
+}
+.back-to-grid {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ border: none;
+ background: transparent;
+ cursor: pointer;
+ color: var(--morandi-text);
+ font-size: 14px;
+ font-weight: 700;
+ padding: 4px 0;
+}
+.back-icon {
+ font-size: 16px;
+ color: var(--morandi-blue);
+}
+
+/* ═══════════════════════════════════════════════════════════
+ Video Grid (双列卡片)
+ ═══════════════════════════════════════════════════════════ */
+.video-list {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: 10px;
+ padding: 0 12px 20px;
+}
+.video-card {
+ background: var(--morandi-card);
+ border-radius: var(--radius);
+ overflow: hidden;
+ box-shadow: var(--shadow-sm);
+ cursor: pointer;
+ transition: all 0.2s;
+ display: flex;
+ flex-direction: column;
+}
+.video-card:active {
+ transform: scale(0.98);
+ box-shadow: var(--shadow);
+}
+
+/* 封面区域 — 莫兰迪杂色底 */
+.vc-cover {
+ width: 100%;
+ aspect-ratio: 16/10;
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ overflow: hidden;
+}
+.vc-cover-placeholder {
+ font-size: 28px;
+ opacity: 0.18;
+}
+/* 时长徽章 */
+.vc-duration {
+ position: absolute;
+ bottom: 6px; right: 6px;
+ background: rgba(74, 74, 74, 0.55);
+ backdrop-filter: blur(4px);
+ color: #fff;
+ font-size: 10px;
+ font-weight: 500;
+ padding: 2px 6px;
+ border-radius: 5px;
+ letter-spacing: 0.3px;
+}
+
+/* 内容区 */
+.vc-content {
+ padding: 8px 10px 10px;
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+}
+.vc-title {
+ font-size: 12px;
+ font-weight: 600;
+ line-height: 1.45;
+ color: var(--morandi-text);
+ margin-bottom: 5px;
+ display: -webkit-box;
+ -webkit-line-clamp: 2;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+ min-height: 34px;
+ letter-spacing: 0.1px;
+}
+.vc-info-row {
+ display: flex;
+ align-items: center;
+ gap: 5px;
+ margin-top: auto;
+}
+.vc-author-avatar {
+ width: 16px; height: 16px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 8px;
+ color: #fff;
+ flex-shrink: 0;
+}
+.vc-author-name {
+ font-size: 10px;
+ color: var(--morandi-text-sub);
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ flex: 1;
+}
+.vc-stat {
+ font-size: 10px;
+ color: var(--morandi-text-muted);
+ display: flex;
+ align-items: center;
+ gap: 2px;
+}
+
+/* 收藏按钮 */
+.vc-fav {
+ position: absolute;
+ top: 6px; right: 6px;
+ width: 26px; height: 26px;
+ border-radius: 50%;
+ border: none;
+ background: rgba(74,74,74,0.28);
+ backdrop-filter: blur(3px);
+ color: #fff;
+ font-size: 13px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ opacity: 0;
+ transition: opacity 0.2s, transform 0.15s;
+ z-index: 2;
+}
+.video-card:hover .vc-fav,
+.video-card:active .vc-fav { opacity: 1; }
+.vc-fav.active {
+ opacity: 1;
+ background: rgba(160, 185, 216, 0.75);
+}
+.vc-fav:active { transform: scale(1.15); }
+
+/* ═══════════════════════════════════════════════════════════
+ Empty & Loading States
+ ═══════════════════════════════════════════════════════════ */
+.loading {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 60px 0;
+ color: var(--morandi-text-muted);
+}
+.spinner {
+ width: 28px; height: 28px;
+ border: 2.5px solid var(--morandi-border);
+ border-top-color: var(--morandi-blue);
+ border-radius: 50%;
+ animation: spin 0.7s linear infinite;
+ margin-bottom: 10px;
+}
+@keyframes spin { to { transform: rotate(360deg); } }
+.empty-state {
+ text-align: center;
+ padding: 60px 20px;
+ color: var(--morandi-text-muted);
+}
+.empty-state .icon { font-size: 40px; margin-bottom: 10px; opacity: 0.4; }
+.empty-state h3 { font-size: 14px; color: var(--morandi-text-sub); margin-bottom: 4px; font-weight: 600; }
+.empty-state p { font-size: 12px; }
+
+/* ═══════════════════════════════════════════════════════════
+ Knowledge Feed (数说 · 知识卡片)
+ ═══════════════════════════════════════════════════════════ */
+
+/* Hero 大横幅 */
+.shushuo-hero {
+ padding: 0 12px 14px;
+ animation: fadeIn 0.3s ease;
+}
+.hero-card {
+ background: linear-gradient(145deg, #C0CAD4 0%, #A8B8C8 60%, #B5C3D0 100%);
+ border-radius: var(--radius-lg);
+ padding: 22px 18px;
+ color: #fff;
+ position: relative;
+ overflow: hidden;
+ box-shadow: 0 4px 16px rgba(192, 202, 212, 0.2);
+}
+.hero-card::before {
+ content: "";
+ position: absolute;
+ top: -20px; right: -20px;
+ width: 90px; height: 90px;
+ border-radius: 50%;
+ background: rgba(255,255,255,0.05);
+}
+.hero-card::after {
+ content: "";
+ position: absolute;
+ bottom: -25px; left: -10px;
+ width: 70px; height: 70px;
+ border-radius: 50%;
+ background: rgba(255,255,255,0.03);
+}
+.hero-badge {
+ display: inline-block;
+ padding: 3px 10px;
+ background: rgba(255,255,255,0.15);
+ border-radius: 10px;
+ font-size: 10px;
+ font-weight: 600;
+ margin-bottom: 12px;
+ letter-spacing: 0.5px;
+}
+.hero-title {
+ font-size: 19px;
+ font-weight: 800;
+ line-height: 1.35;
+ margin-bottom: 6px;
+ position: relative;
+ z-index: 1;
+}
+.hero-desc {
+ font-size: 11px;
+ opacity: 0.72;
+ line-height: 1.5;
+ margin-bottom: 12px;
+ position: relative;
+ z-index: 1;
+}
+.hero-stats {
+ display: flex;
+ gap: 16px;
+ font-size: 10px;
+ opacity: 0.55;
+ position: relative;
+ z-index: 1;
+}
+
+/* 横向话题标签 */
+.shushuo-topics {
+ padding: 0 12px 8px;
+ overflow: hidden;
+}
+.topics-scroll {
+ display: flex;
+ gap: 7px;
+ overflow-x: auto;
+ scrollbar-width: none;
+ padding-bottom: 4px;
+}
+.topics-scroll::-webkit-scrollbar { display: none; }
+.topic-pill {
+ display: inline-flex;
+ padding: 5px 12px;
+ border-radius: 14px;
+ border: 1px solid var(--morandi-border);
+ background: var(--morandi-card);
+ font-size: 11px;
+ font-weight: 500;
+ color: var(--morandi-text-sub);
+ cursor: pointer;
+ white-space: nowrap;
+ flex-shrink: 0;
+ transition: all 0.2s;
+}
+.topic-pill:active,
+.topic-pill.active {
+ border-color: var(--morandi-green);
+ background: var(--morandi-green-light);
+ color: #7BA89A;
+ font-weight: 600;
+}
+
+/* 知识卡片 Feed */
+.shushuo-feed {
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+ padding: 0 12px 20px;
+ animation: fadeIn 0.4s ease;
+}
+.feed-card {
+ background: var(--morandi-card);
+ border-radius: var(--radius-lg);
+ overflow: hidden;
+ box-shadow: var(--shadow-sm);
+ cursor: pointer;
+ transition: transform 0.2s;
+}
+.feed-card:active { transform: scale(0.98); }
+.feed-card-visual {
+ width: 100%;
+ height: 132px;
+ position: relative;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ overflow: hidden;
+}
+.feed-card-icon {
+ font-size: 42px;
+ opacity: 0.22;
+ z-index: 1;
+}
+.feed-card-question {
+ position: absolute;
+ bottom: 10px; left: 14px; right: 14px;
+ font-size: 14px;
+ font-weight: 700;
+ color: #fff;
+ z-index: 1;
+ text-shadow: 0 1px 2px rgba(0,0,0,0.15);
+ line-height: 1.4;
+}
+.feed-card-body {
+ padding: 10px 14px 14px;
+}
+.feed-card-title {
+ font-size: 15px;
+ font-weight: 700;
+ line-height: 1.4;
+ margin-bottom: 5px;
+ color: var(--morandi-text);
+}
+.feed-card-desc {
+ font-size: 12px;
+ color: var(--morandi-text-sub);
+ line-height: 1.6;
+ margin-bottom: 8px;
+}
+.feed-card-footer {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+.feed-card-topic {
+ font-size: 10px;
+ font-weight: 500;
+}
+.feed-card-time {
+ font-size: 10px;
+ color: var(--morandi-text-muted);
+}
+.feed-card-fav {
+ position: absolute;
+ top: 6px; right: 6px;
+ width: 26px; height: 26px;
+ border-radius: 50%;
+ border: none;
+ background: rgba(74,74,74,0.2);
+ color: #fff;
+ font-size: 13px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: transform 0.15s;
+ z-index: 2;
+}
+.feed-card-fav.active { background: rgba(160, 185, 216, 0.7); }
+.feed-card-fav:active { transform: scale(1.15); }
+
+/* ═══════════════════════════════════════════════════════════
+ 个人中心 (Profile) — 紧凑布局,完整显示
+ ═══════════════════════════════════════════════════════════ */
+.profile-page {
+ padding: 6px 12px 20px;
+ animation: fadeIn 0.3s ease;
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+}
+
+/* 用户信息面板 */
+.profile-card {
+ background: linear-gradient(135deg, #C5CAD0 0%, #B5C0CA 40%, #C0C8D0 100%);
+ border-radius: var(--radius-lg);
+ padding: 14px 14px 12px;
+ color: #fff;
+ box-shadow: 0 6px 20px rgba(197, 202, 208, 0.2);
+ flex-shrink: 0;
+ cursor: pointer;
+ transition: opacity 0.15s;
+}
+.profile-card:active { opacity: 0.92; }
+.profile-main {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+}
+.profile-avatar {
+ width: 48px; height: 48px;
+ border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
+ box-shadow: inset 0 -3px 0 rgba(0,0,0,0.08);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: rgba(255,255,255,0.2);
+ font-size: 20px;
+ font-weight: 800;
+ flex-shrink: 0;
+}
+.profile-meta { flex: 1; min-width: 0; }
+.profile-name {
+ font-size: 16px;
+ font-weight: 800;
+ margin-bottom: 1px;
+ letter-spacing: 0.2px;
+}
+.profile-id {
+ font-size: 10px;
+ opacity: 0.7;
+ margin-bottom: 6px;
+}
+.profile-tags {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 5px;
+}
+.profile-tags span {
+ padding: 2px 7px;
+ border-radius: 999px;
+ background: rgba(255,255,255,0.16);
+ font-size: 10px;
+ font-weight: 500;
+}
+.profile-edit {
+ margin-top: 8px;
+ width: fit-content;
+ padding: 4px 10px;
+ border-radius: 999px;
+ background: rgba(255,255,255,0.14);
+ font-size: 10px;
+ font-weight: 600;
+ cursor: pointer;
+}
+
+/* 统计行 */
+.profile-stats {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 6px;
+ flex-shrink: 0;
+}
+.profile-stat {
+ background: var(--morandi-card);
+ border-radius: var(--radius);
+ padding: 10px 6px;
+ text-align: center;
+ box-shadow: var(--shadow-sm);
+ border: 1px solid var(--morandi-border);
+ cursor: pointer;
+ transition: transform 0.15s;
+}
+.profile-stat:active { transform: scale(0.96); }
+.profile-stat-num {
+ font-size: 17px;
+ font-weight: 800;
+ color: var(--morandi-blue);
+ margin-bottom: 1px;
+}
+.profile-stat-label {
+ font-size: 10px;
+ color: var(--morandi-text-sub);
+}
+
+/* 功能列表(紧凑型) */
+.profile-section {
+ flex-shrink: 0;
+}
+.profile-section-title {
+ font-size: 10px;
+ font-weight: 700;
+ color: var(--morandi-text-sub);
+ margin-bottom: 4px;
+ padding: 0 2px;
+ letter-spacing: 0.3px;
+ text-transform: uppercase;
+}
+.profile-menu-card {
+ background: var(--morandi-card);
+ border-radius: var(--radius);
+ box-shadow: var(--shadow-sm);
+ overflow: hidden;
+}
+.profile-menu-item {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 9px 12px;
+ border-bottom: 1px solid var(--morandi-divider);
+ cursor: pointer;
+ transition: background 0.15s;
+}
+.profile-menu-item:last-child { border-bottom: none; }
+.profile-menu-item:active { background: var(--morandi-bg); }
+.profile-menu-left {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ font-size: 12px;
+ font-weight: 600;
+ color: var(--morandi-text);
+}
+.menu-icon {
+ width: 22px; height: 22px;
+ border-radius: 6px;
+ background: var(--morandi-bg);
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--morandi-blue);
+ font-size: 11px;
+ font-weight: 700;
+}
+.menu-arrow {
+ font-size: 14px;
+ color: var(--morandi-text-muted);
+ font-weight: 400;
+}
+
+/* ═══════════════════════════════════════════════════════════
+ Bottom TabBar
+ ═══════════════════════════════════════════════════════════ */
+.bottom-tab-bar {
+ display: flex;
+ align-items: center;
+ justify-content: space-around;
+ gap: 2px;
+ background: rgba(255, 255, 255, 0.96);
+ backdrop-filter: blur(20px);
+ -webkit-backdrop-filter: blur(20px);
+ padding: 8px 10px calc(10px + env(safe-area-inset-bottom, 0px));
+ border-top: 1px solid var(--morandi-border);
+ z-index: 60;
+ box-shadow: 0 -4px 20px rgba(160, 185, 216, 0.06);
+}
+.tab-btn {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 3px;
+ border: none;
+ background: transparent;
+ color: var(--morandi-text-muted);
+ cursor: pointer;
+ width: 78px;
+ min-height: 48px;
+ border-radius: 14px;
+ transition: all 0.2s ease;
+}
+.tab-icon {
+ width: 20px; height: 20px;
+ position: relative;
+ transition: transform 0.2s, opacity 0.2s;
+ opacity: 0.6;
+}
+.tab-label {
+ font-size: 10px;
+ font-weight: 500;
+ letter-spacing: 0.2px;
+}
+.tab-btn.active {
+ color: var(--morandi-blue);
+ background: var(--morandi-blue-light);
+}
+.tab-btn.active .tab-icon {
+ transform: translateY(-1px);
+ opacity: 1;
+}
+.tab-btn.active .tab-label {
+ font-weight: 700;
+}
+
+/* Tab 图标 — 简约线性风格 */
+.tab-icon::before,
+.tab-icon::after {
+ content: "";
+ position: absolute;
+ box-sizing: border-box;
+}
+.tab-icon-discover::before {
+ inset: 3px;
+ border: 1.8px solid currentColor;
+ border-radius: 5px;
+}
+.tab-icon-discover::after {
+ width: 4px; height: 8px;
+ right: 5px; top: 7px;
+ background: currentColor;
+ border-radius: 2px;
+}
+.tab-icon-knowledge::before {
+ left: 4px; right: 4px; bottom: 3px;
+ height: 10px;
+ border: 1.8px solid currentColor;
+ border-top: none;
+ border-radius: 1px 1px 4px 4px;
+}
+.tab-icon-knowledge::after {
+ left: 6px; top: 2px;
+ width: 10px; height: 10px;
+ background: currentColor;
+ clip-path: polygon(50% 0, 100% 40%, 100% 100%, 0 100%, 0 40%);
+}
+.tab-icon-life::before {
+ left: 5px; right: 5px; top: 3px; bottom: 3px;
+ border: 1.8px solid currentColor;
+ border-radius: 3px 3px 7px 7px;
+}
+.tab-icon-life::after {
+ left: 8px; right: 8px; top: 8px;
+ height: 2px;
+ background: currentColor;
+ border-radius: 2px;
+}
+.tab-icon-mine::before {
+ width: 7px; height: 7px;
+ left: 3px; top: 3px;
+ background: currentColor;
+ border-radius: 2px;
+ box-shadow: 9px 0 0 currentColor, 0 9px 0 currentColor, 9px 9px 0 currentColor;
+}
+
+/* ═══════════════════════════════════════════════════════════
+ AI Chat
+ ═══════════════════════════════════════════════════════════ */
+.chat-overlay {
+ display: none;
+ position: fixed;
+ inset: 0;
+ z-index: 200;
+ background: rgba(74, 74, 74, 0.35);
+ justify-content: center;
+ align-items: center;
+ backdrop-filter: blur(4px);
+}
+.chat-overlay.show { display: flex; }
+.chat-panel {
+ width: 380px;
+ max-width: 93vw;
+ height: 78vh;
+ background: var(--morandi-card);
+ border-radius: var(--radius-xl);
+ display: flex;
+ flex-direction: column;
+ animation: modalIn 0.25s cubic-bezier(0.16, 1, 0.3, 1);
+ box-shadow: 0 20px 50px rgba(0,0,0,0.12);
+ overflow: hidden;
+}
+@keyframes modalIn {
+ from { opacity: 0; transform: scale(0.95) translateY(10px); }
+ to { opacity: 1; transform: scale(1) translateY(0); }
+}
+.chat-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 14px 18px;
+ border-bottom: 1px solid var(--morandi-border);
+ font-weight: 700;
+ font-size: 14px;
+ color: var(--morandi-text);
+}
+.chat-header-left { display: flex; align-items: center; gap: 10px; }
+.ai-avatar {
+ width: 30px; height: 30px;
+ background: var(--morandi-blue);
+ border-radius: 9px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 15px;
+ color: #fff;
+ box-shadow: 0 2px 6px rgba(160,185,216,0.3);
+}
+.chat-close {
+ width: 28px; height: 28px;
+ border-radius: 50%;
+ border: none;
+ background: var(--morandi-bg);
+ cursor: pointer;
+ font-size: 14px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: var(--morandi-text-sub);
+}
+.chat-messages {
+ flex: 1;
+ overflow-y: auto;
+ padding: 14px;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+ background: var(--morandi-bg);
+}
+.chat-msg { display: flex; animation: msgIn 0.3s ease; }
+@keyframes msgIn { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
+.chat-msg.assistant { justify-content: flex-start; }
+.chat-msg.user { justify-content: flex-end; }
+.msg-bubble {
+ max-width: 82%;
+ padding: 9px 13px;
+ border-radius: 13px;
+ font-size: 13px;
+ line-height: 1.6;
+ word-break: break-word;
+}
+.chat-msg.assistant .msg-bubble {
+ background: #fff;
+ border-bottom-left-radius: 3px;
+ box-shadow: var(--shadow-sm);
+}
+.chat-msg.user .msg-bubble {
+ background: linear-gradient(135deg, #A0B9D8, #8AA3C2);
+ color: #fff;
+ border-bottom-right-radius: 3px;
+}
+.quick-asks {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 5px;
+ padding: 6px 14px;
+}
+.quick-ask {
+ padding: 5px 11px;
+ border-radius: 12px;
+ border: 1px solid var(--morandi-border);
+ background: #fff;
+ font-size: 11px;
+ color: var(--morandi-text-sub);
+ cursor: pointer;
+ transition: all 0.15s;
+}
+.quick-ask:active {
+ background: var(--morandi-blue-light);
+ border-color: var(--morandi-blue);
+ color: var(--morandi-blue);
+}
+.chat-input-area {
+ display: flex;
+ padding: 8px 14px 12px;
+ border-top: 1px solid var(--morandi-border);
+ gap: 8px;
+ background: #fff;
+}
+.chat-input-area input {
+ flex: 1;
+ padding: 9px 13px;
+ border: 1px solid var(--morandi-border);
+ border-radius: 18px;
+ font-size: 13px;
+ outline: none;
+ background: var(--morandi-bg);
+ transition: border-color 0.2s;
+}
+.chat-input-area input:focus { border-color: var(--morandi-blue); }
+.send-btn {
+ min-width: 36px; height: 36px;
+ border: none;
+ border-radius: 50%;
+ background: var(--morandi-blue);
+ color: #fff;
+ font-size: 15px;
+ cursor: pointer;
+ flex-shrink: 0;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-shadow: 0 2px 8px rgba(160,185,216,0.25);
+}
+.send-btn:active { transform: scale(0.93); }
+.typing-indicator { display: flex; gap: 4px; padding: 3px 0; }
+.typing-indicator span {
+ width: 5px; height: 5px;
+ border-radius: 50%;
+ background: var(--morandi-text-muted);
+ animation: bounce 1.2s infinite;
+}
+.typing-indicator span:nth-child(2) { animation-delay: 0.2s; }
+.typing-indicator span:nth-child(3) { animation-delay: 0.4s; }
+@keyframes bounce {
+ 0%, 60%, 100% { transform: translateY(0); }
+ 30% { transform: translateY(-6px); }
+}
+
+/* ═══════════════════════════════════════════════════════════
+ Video Modal
+ ═══════════════════════════════════════════════════════════ */
+.video-modal {
+ display: none;
+ position: fixed;
+ inset: 0;
+ z-index: 200;
+ background: rgba(74, 74, 74, 0.5);
+ justify-content: center;
+ align-items: center;
+ backdrop-filter: blur(4px);
+}
+.video-modal.show { display: flex; }
+.video-modal-content {
+ width: 360px;
+ max-width: 93vw;
+ max-height: 86vh;
+ background: var(--morandi-card);
+ border-radius: var(--radius-lg);
+ overflow-y: auto;
+ animation: modalIn 0.25s cubic-bezier(0.16, 1, 0.3, 1);
+}
+.video-modal-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 14px 16px;
+ border-bottom: 1px solid var(--morandi-border);
+}
+.video-modal-header h3 { font-size: 14px; font-weight: 700; flex: 1; margin-right: 10px; line-height: 1.4; color: var(--morandi-text); }
+.video-modal-header button {
+ width: 28px; height: 28px; border-radius: 50%; border: none;
+ background: var(--morandi-bg); cursor: pointer; font-size: 14px;
+ display: flex; align-items: center; justify-content: center; flex-shrink: 0; color: var(--morandi-text-sub);
+}
+.video-player-area {
+ background: #E8EBEF;
+ aspect-ratio: 16/9;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+.video-placeholder { text-align: center; color: rgba(74,74,74,0.35); }
+.play-icon {
+ width: 46px; height: 46px; border-radius: 50%;
+ background: rgba(160,185,216,0.25);
+ display: flex;
+ align-items: center; justify-content: center; font-size: 20px;
+ margin: 0 auto 8px;
+}
+.video-placeholder p { font-size: 11px; }
+.video-info { padding: 12px 16px; }
+.video-desc { font-size: 12px; line-height: 1.6; color: var(--morandi-text-sub); margin-bottom: 8px; }
+.video-meta { font-size: 10px; color: var(--morandi-text-muted); display: flex; flex-wrap: wrap; gap: 10px; }
+.video-actions { padding: 0 16px 16px; display: flex; gap: 8px; }
+.btn-primary, .btn-secondary {
+ flex: 1; padding: 9px; border-radius: 10px; border: none;
+ font-size: 12px; font-weight: 600; cursor: pointer; text-align: center;
+ transition: transform 0.15s;
+}
+.btn-primary:active, .btn-secondary:active { transform: scale(0.97); }
+.btn-primary { background: var(--morandi-blue); color: #fff; box-shadow: 0 2px 8px rgba(160,185,216,0.2); }
+.btn-secondary { background: var(--morandi-bg); color: var(--morandi-text); }
+
+/* ═══════════════════════════════════════════════════════════
+ Visual Novel Engine
+ ═══════════════════════════════════════════════════════════ */
+.vn-header {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ margin-bottom: 10px;
+ padding: 0 12px;
+}
+.vn-chapter-badge {
+ align-self: flex-start;
+ padding: 4px 12px;
+ border-radius: 999px;
+ font-size: 11px;
+ font-weight: 700;
+ color: #fff;
+ background: var(--morandi-green);
+ box-shadow: 0 2px 6px rgba(184, 208, 201, 0.3);
+ letter-spacing: 0.3px;
+}
+.vn-stats {
+ display: flex;
+ gap: 6px;
+ flex-wrap: wrap;
+}
+.stat-badge {
+ font-size: 10px;
+ padding: 4px 8px;
+ border-radius: 10px;
+ color: #fff;
+ font-weight: 600;
+ box-shadow: 0 1px 3px rgba(0,0,0,0.06);
+}
+.vn-actions {
+ display: flex;
+ gap: 6px;
+ padding: 0 12px;
+}
+.vn-btn {
+ background: var(--morandi-card);
+ border: 1px solid var(--morandi-border);
+ padding: 5px 10px;
+ border-radius: 8px;
+ font-size: 11px;
+ cursor: pointer;
+ box-shadow: var(--shadow-sm);
+ color: var(--morandi-text-sub);
+ font-weight: 600;
+ transition: all 0.2s;
+}
+.vn-btn:active {
+ background: var(--morandi-green-light);
+ border-color: var(--morandi-green);
+ color: #7BA89A;
+}
+.story-scene {
+ background: var(--morandi-card);
+ margin: 0 12px 12px;
+ border-radius: var(--radius-lg);
+ padding: 18px;
+ min-height: 260px;
+ box-shadow: var(--shadow-sm);
+ position: relative;
+ overflow: hidden;
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-end;
+}
+.vn-background {
+ position: absolute;
+ top: 0; left: 0;
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ z-index: 0;
+ border-radius: var(--radius-lg);
+}
+.vn-sprite {
+ position: absolute;
+ bottom: 0;
+ right: 0;
+ max-height: 75%;
+ max-width: 55%;
+ object-fit: contain;
+ z-index: 1;
+ pointer-events: none;
+ transition: opacity 0.4s ease;
+}
+.story-text-box {
+ position: relative;
+ z-index: 2;
+ background: rgba(250, 251, 252, 0.94);
+ backdrop-filter: blur(4px);
+ padding: 12px;
+ border-radius: 9px;
+ border: 1px solid var(--morandi-border);
+}
+.story-character {
+ font-weight: 800;
+ color: var(--morandi-green);
+ margin-bottom: 5px;
+ font-size: 12px;
+ letter-spacing: 0.3px;
+}
+.story-text {
+ font-size: 13px;
+ line-height: 1.7;
+ color: var(--morandi-text);
+ min-height: 44px;
+ white-space: pre-line;
+}
+.story-choices {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ padding: 0 12px 20px;
+}
+.choice-btn {
+ background: var(--morandi-card);
+ border: 1px solid var(--morandi-border);
+ border-radius: var(--radius);
+ padding: 12px 14px;
+ font-size: 13px;
+ font-weight: 600;
+ color: var(--morandi-text);
+ text-align: left;
+ cursor: pointer;
+ transition: all 0.2s;
+ box-shadow: var(--shadow-sm);
+ display: flex;
+ flex-direction: column;
+ gap: 5px;
+ animation: fadeInUp 0.3s ease both;
+}
+.choice-btn:hover {
+ background: var(--morandi-green-light);
+ border-color: var(--morandi-green);
+}
+.choice-btn:active {
+ transform: scale(0.98);
+ background: var(--morandi-green-light);
+}
+.choice-hint {
+ font-size: 10px;
+ color: #7BA89A;
+ font-weight: 500;
+}
+.choice-text { line-height: 1.5; }
+@keyframes fadeInUp {
+ from { opacity: 0; transform: translateY(8px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+@keyframes fadeIn {
+ from { opacity: 0; transform: translateY(8px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+
+/* ═══════════════════════════════════════════════════════════
+ Simulator
+ ═══════════════════════════════════════════════════════════ */
+.sim-header {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ margin: 10px 0;
+ padding: 0 12px;
+}
+.sim-year-badge {
+ align-self: flex-start;
+ padding: 4px 14px;
+ border-radius: 999px;
+ font-size: 11px;
+ font-weight: 700;
+ color: #fff;
+ box-shadow: 0 2px 6px rgba(0,0,0,0.1);
+}
+.sim-stats {
+ display: flex;
+ gap: 6px;
+ flex-wrap: wrap;
+}
+.sim-stat-bar {
+ font-size: 10px;
+ padding: 4px 8px;
+ border-radius: 10px;
+ color: #fff;
+ font-weight: 600;
+ box-shadow: 0 1px 3px rgba(0,0,0,0.06);
+}
+.sim-actions-top {
+ display: flex;
+ gap: 6px;
+ padding: 0 12px 8px;
+}
+.sim-scene {
+ background: var(--morandi-card);
+ margin: 0 12px 12px;
+ border-radius: var(--radius-lg);
+ padding: 18px;
+ min-height: 180px;
+ box-shadow: var(--shadow-sm);
+ position: relative;
+}
+.sim-character {
+ font-weight: 800;
+ color: var(--morandi-pink);
+ margin-bottom: 8px;
+ font-size: 13px;
+ letter-spacing: 0.3px;
+}
+.sim-text {
+ font-size: 13px;
+ line-height: 1.7;
+ color: var(--morandi-text);
+ min-height: 54px;
+ white-space: pre-line;
+}
+.sim-choices {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ padding: 0 12px 28px;
+}
+.sim-choice-btn {
+ background: var(--morandi-card);
+ border: 1px solid var(--morandi-border);
+ border-radius: var(--radius);
+ padding: 12px 14px;
+ font-size: 13px;
+ font-weight: 600;
+ color: var(--morandi-text);
+ text-align: left;
+ cursor: pointer;
+ transition: all 0.2s;
+ box-shadow: var(--shadow-sm);
+ display: flex;
+ flex-direction: column;
+ gap: 5px;
+ animation: fadeInUp 0.3s ease both;
+}
+.sim-choice-btn:hover {
+ background: var(--morandi-pink-light);
+ border-color: var(--morandi-pink);
+}
+.sim-choice-btn:active { transform: scale(0.98); }
+.sim-hint {
+ font-size: 10px;
+ color: #B8958A;
+ font-weight: 500;
+}
+.sim-hint-tip {
+ font-size: 10px;
+ color: var(--morandi-text-muted);
+ font-weight: 400;
+}
+.sim-choice-text { line-height: 1.5; }
+
+/* ═══════════════════════════════════════════════════════════
+ Ending Overlay
+ ═══════════════════════════════════════════════════════════ */
+.ending-overlay {
+ position: fixed;
+ top: 0; left: 0; right: 0; bottom: 0;
+ background: rgba(74, 74, 74, 0.5);
+ z-index: 200;
+ display: none;
+ align-items: center;
+ justify-content: center;
+}
+.ending-overlay.show { display: flex; }
+.ending-card {
+ background: var(--morandi-card);
+ border-radius: var(--radius-xl);
+ padding: 24px 22px;
+ width: min(320px, 88vw);
+ text-align: center;
+ box-shadow: 0 16px 48px rgba(0,0,0,0.15);
+ animation: fadeInUp 0.4s ease;
+ display: flex;
+ flex-direction: column;
+ gap: 10px;
+}
+.ending-badge { font-size: 44px; }
+.ending-type {
+ font-size: 12px;
+ color: var(--morandi-text-muted);
+ font-weight: 600;
+ letter-spacing: 1px;
+}
+.ending-title {
+ font-size: 16px;
+ font-weight: 700;
+ color: var(--morandi-text);
+}
+.ending-scene {
+ font-size: 12px;
+ color: var(--morandi-text-sub);
+ line-height: 1.7;
+ background: var(--morandi-bg);
+ border-radius: var(--radius);
+ padding: 10px;
+}
+
+/* ═══════════════════════════════════════════════════════════
+ Avatar Picker
+ ═══════════════════════════════════════════════════════════ */
+.avatar-picker-overlay {
+ position: fixed;
+ top: 0; left: 0; right: 0; bottom: 0;
+ background: rgba(74, 74, 74, 0.45);
+ z-index: 150;
+ display: none;
+ align-items: center;
+ justify-content: center;
+}
+.avatar-picker-overlay.show { display: flex; }
+.avatar-picker-card {
+ background: var(--morandi-card);
+ border-radius: var(--radius-xl);
+ padding: 18px;
+ width: min(320px, 88vw);
+ box-shadow: 0 16px 48px rgba(0,0,0,0.15);
+ display: flex;
+ flex-direction: column;
+ gap: 12px;
+}
+.picker-title {
+ font-size: 14px;
+ font-weight: 700;
+ text-align: center;
+ color: var(--morandi-text);
+}
+.picker-emoji-grid {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 5px;
+ justify-content: center;
+}
+.picker-emoji-btn {
+ width: 34px; height: 34px;
+ border: 1.5px solid var(--morandi-border);
+ border-radius: 8px;
+ background: var(--morandi-bg);
+ font-size: 16px;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.15s;
+}
+.picker-emoji-btn:active { transform: scale(0.9); }
+.picker-color-row {
+ display: flex;
+ gap: 8px;
+ justify-content: center;
+}
+.picker-color-btn {
+ width: 30px; height: 30px;
+ border: none;
+ border-radius: 50%;
+ cursor: pointer;
+ transition: transform 0.15s;
+}
+.picker-color-btn:active { transform: scale(0.85); }
+.picker-actions {
+ display: flex;
+ gap: 8px;
+}
+.picker-btn {
+ flex: 1;
+ padding: 9px;
+ border: 1px solid var(--morandi-border);
+ border-radius: 10px;
+ background: var(--morandi-bg);
+ font-size: 12px;
+ font-weight: 600;
+ cursor: pointer;
+ text-align: center;
+ color: var(--morandi-text-sub);
+}
+.picker-btn.primary {
+ background: var(--morandi-blue);
+ color: #fff;
+ border-color: transparent;
+ box-shadow: 0 2px 8px rgba(160,185,216,0.2);
+}
+
+/* ═══════════════════════════════════════════════════════════
+ Profile List Panel
+ ═══════════════════════════════════════════════════════════ */
+.profile-list-panel {
+ position: absolute;
+ top: 0; left: 0; right: 0; bottom: 0;
+ background: var(--morandi-bg);
+ z-index: 30;
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+}
+.plp-header {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ padding: 12px 14px;
+ background: var(--morandi-card);
+ border-bottom: 1px solid var(--morandi-border);
+ position: sticky;
+ top: 0;
+ z-index: 5;
+}
+.plp-back {
+ border: none;
+ background: var(--morandi-bg);
+ padding: 5px 10px;
+ border-radius: 7px;
+ font-size: 12px;
+ color: var(--morandi-text-sub);
+ cursor: pointer;
+}
+.plp-title {
+ font-size: 14px;
+ font-weight: 700;
+ color: var(--morandi-text);
+}
+.plp-content {
+ flex: 1;
+ overflow-y: auto;
+ padding: 10px 12px;
+}
+.history-item {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ background: var(--morandi-card);
+ border-radius: var(--radius);
+ padding: 10px 12px;
+ margin-bottom: 7px;
+ box-shadow: var(--shadow-sm);
+}
+.hi-icon {
+ width: 40px; height: 40px;
+ border-radius: 8px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 18px;
+ flex-shrink: 0;
+}
+.hi-info { flex: 1; min-width: 0; }
+.hi-title {
+ font-size: 13px;
+ font-weight: 600;
+ color: var(--morandi-text);
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.hi-meta {
+ font-size: 10px;
+ color: var(--morandi-text-muted);
+ margin-top: 2px;
+}
+.hi-del {
+ border: none;
+ background: transparent;
+ color: #DAB8B0;
+ font-size: 15px;
+ cursor: pointer;
+ padding: 4px 8px;
+ border-radius: 5px;
+ transition: background 0.2s;
+}
+.hi-del:hover { background: var(--morandi-pink-light); }
+
+/* ═══════════════════════════════════════════════════════════
+ Math Life Sub Nav
+ ═══════════════════════════════════════════════════════════ */
+.life-sub-nav {
+ display: flex;
+ gap: 4px;
+ padding: 6px 12px;
+ background: var(--morandi-card);
+ border-bottom: 1px solid var(--morandi-border);
+}
+.life-sub-btn {
+ flex: 1;
+ padding: 7px 0;
+ border: none;
+ border-radius: 8px;
+ background: var(--morandi-bg);
+ font-size: 12px;
+ font-weight: 600;
+ color: var(--morandi-text-sub);
+ cursor: pointer;
+ transition: all 0.2s;
+}
+.life-sub-btn.active {
+ background: var(--morandi-pink);
+ color: #fff;
+ box-shadow: 0 2px 6px rgba(218, 184, 176, 0.3);
+}
+
+/* ═══════════════════════════════════════════════════════════
+ About Card
+ ═══════════════════════════════════════════════════════════ */
+.about-card {
+ background: var(--morandi-card);
+ border-radius: var(--radius);
+ padding: 24px 18px;
+ text-align: center;
+ box-shadow: var(--shadow-sm);
+ margin: 0 12px;
+}
+.about-card .avatar {
+ width: 56px; height: 56px;
+ background: var(--morandi-blue);
+ border-radius: 50% 50% 50% 50% / 60% 60% 40% 40%;
+ display: flex; align-items: center; justify-content: center;
+ font-size: 26px; margin: 0 auto 12px; color: #fff;
+ box-shadow: 0 3px 10px rgba(160,185,216,0.2), inset 0 -3px 0 rgba(0,0,0,0.08);
+}
+.about-card h2 { font-size: 16px; font-weight: 700; margin-bottom: 4px; color: var(--morandi-text); }
+.about-card .subtitle { font-size: 11px; color: var(--morandi-blue); font-weight: 600; margin-bottom: 12px; }
+.about-card .quote {
+ font-size: 12px; color: var(--morandi-text-sub); line-height: 1.6;
+ padding: 10px; background: var(--morandi-bg); border-radius: var(--radius-sm); margin-bottom: 10px;
+}
+.about-card .version { font-size: 10px; color: var(--morandi-text-muted); }
+
+/* ═══════════════════════════════════════════════════════════
+ Responsive
+ ═══════════════════════════════════════════════════════════ */
+@media (min-width: 480px) {
+ body::before,
+ .app-container {
+ border-radius: 34px;
+ }
+ body::before {
+ top: 10px;
+ height: calc(100dvh - 20px);
+ }
+ .app-container {
+ margin-top: 10px;
+ height: calc(100dvh - 20px);
+ box-shadow: 0 20px 60px rgba(160, 185, 216, 0.12);
+ border: 1px solid rgba(200, 208, 216, 0.4);
+ }
+}
+@media (max-width: 420px) {
+ body::before { display: none; }
+ .app-container { width: 100%; max-width: 100%; }
+}
+
+/* ═══════════════════════════════════════════════════════════
+ 剧本广场 (Script Square) — v8.1
+ ═══════════════════════════════════════════════════════════ */
+.script-square {
+ padding: 12px 12px 28px;
+ animation: fadeIn 0.35s ease;
+}
+
+/* 分区标题 */
+.ss-section-title {
+ font-size: 12px;
+ font-weight: 700;
+ color: var(--morandi-text-sub);
+ margin: 18px 0 10px;
+ padding: 0 2px;
+ letter-spacing: 0.8px;
+ text-transform: uppercase;
+}
+.ss-section-title:first-of-type {
+ margin-top: 4px;
+}
+
+/* ═══ 快捷继续卡片 ═══ */
+.ss-continue {
+ margin-bottom: 4px;
+}
+.ss-continue-card {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ background: var(--morandi-card);
+ border: 1.5px solid var(--morandi-blue-light);
+ border-radius: var(--radius-lg);
+ padding: 14px 16px;
+ cursor: pointer;
+ box-shadow: 0 2px 10px rgba(160,185,216,0.12);
+ transition: transform 0.15s, box-shadow 0.2s;
+}
+.ss-continue-card:active {
+ transform: scale(0.98);
+ box-shadow: var(--shadow);
+}
+.ss-continue-badge {
+ padding: 5px 12px;
+ border-radius: 999px;
+ background: var(--morandi-blue);
+ color: #fff;
+ font-size: 11px;
+ font-weight: 700;
+ white-space: nowrap;
+ flex-shrink: 0;
+ box-shadow: 0 2px 6px rgba(160,185,216,0.25);
+}
+.ss-continue-info {
+ flex: 1;
+ min-width: 0;
+}
+.ss-continue-title {
+ font-size: 14px;
+ font-weight: 700;
+ color: var(--morandi-text);
+ margin-bottom: 2px;
+}
+.ss-continue-sub {
+ font-size: 11px;
+ color: var(--morandi-text-muted);
+}
+.ss-continue-arrow {
+ font-size: 20px;
+ color: var(--morandi-blue);
+ flex-shrink: 0;
+}
+
+/* ═══ 精选剧本大卡 ═══ */
+.ss-hero-cards {
+ display: flex;
+ flex-direction: column;
+ gap: 14px;
+}
+.ss-hero-card {
+ position: relative;
+ border-radius: var(--radius-xl);
+ overflow: hidden;
+ cursor: pointer;
+ box-shadow: 0 4px 18px rgba(0,0,0,0.06);
+ transition: transform 0.2s, box-shadow 0.2s;
+ min-height: 170px;
+ display: flex;
+ flex-direction: column;
+ justify-content: flex-end;
+}
+.ss-hero-card:active {
+ transform: scale(0.98);
+ box-shadow: 0 6px 22px rgba(0,0,0,0.1);
+}
+
+/* 大卡背景层 */
+.ss-hero-bg {
+ position: absolute;
+ inset: 0;
+ z-index: 0;
+}
+.ss-hero-bg::before {
+ content: "";
+ position: absolute;
+ top: -30px; right: -30px;
+ width: 120px; height: 120px;
+ border-radius: 50%;
+ background: rgba(255,255,255,0.06);
+}
+.ss-hero-bg::after {
+ content: "";
+ position: absolute;
+ bottom: -20px; left: 20px;
+ width: 80px; height: 80px;
+ border-radius: 50%;
+ background: rgba(255,255,255,0.04);
+}
+
+/* 大卡内容层 */
+.ss-hero-content {
+ position: relative;
+ z-index: 1;
+ padding: 22px 18px;
+ color: #fff;
+}
+.ss-hero-icon {
+ font-size: 36px;
+ margin-bottom: 10px;
+ opacity: 0.85;
+}
+.ss-hero-name {
+ font-size: 19px;
+ font-weight: 800;
+ margin-bottom: 4px;
+ letter-spacing: 0.2px;
+}
+.ss-hero-desc {
+ font-size: 12px;
+ opacity: 0.78;
+ line-height: 1.5;
+ margin-bottom: 12px;
+}
+.ss-hero-meta {
+ display: flex;
+ gap: 14px;
+ font-size: 10px;
+ opacity: 0.55;
+}
+.ss-hero-status {
+ display: inline-flex;
+ align-items: center;
+ gap: 4px;
+ padding: 3px 10px;
+ border-radius: 999px;
+ background: rgba(255,255,255,0.18);
+ font-size: 10px;
+ font-weight: 600;
+}
+
+/* 各剧本配色 */
+.ss-card-wuxia .ss-hero-bg {
+ background: linear-gradient(145deg, #7BA89A 0%, #5C8F7E 50%, #8BB5A5 100%);
+}
+.ss-card-simulator .ss-hero-bg {
+ background: linear-gradient(145deg, #C9A39A 0%, #B8958A 50%, #D4B5AC 100%);
+}
+.ss-card-mathbti .ss-hero-bg {
+ background: linear-gradient(145deg, #8BA4C0 0%, #7A93B0 50%, #9CB5CD 100%);
+}
+
+/* ═══ 即将上线小卡(横向滚动) ═══ */
+.ss-coming-cards {
+ display: flex;
+ gap: 12px;
+ overflow-x: auto;
+ scrollbar-width: none;
+ padding-bottom: 6px;
+}
+.ss-coming-cards::-webkit-scrollbar { display: none; }
+.ss-coming-card {
+ flex-shrink: 0;
+ width: 130px;
+ border-radius: var(--radius-lg);
+ overflow: hidden;
+ background: var(--morandi-card);
+ box-shadow: var(--shadow-sm);
+ opacity: 0.7;
+ pointer-events: none;
+}
+.ss-coming-card-bg {
+ height: 72px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ font-size: 26px;
+ opacity: 0.35;
+}
+.ss-coming-card-body {
+ padding: 10px 12px 12px;
+}
+.ss-coming-card-name {
+ font-size: 12px;
+ font-weight: 700;
+ color: var(--morandi-text);
+ margin-bottom: 3px;
+}
+.ss-coming-card-desc {
+ font-size: 10px;
+ color: var(--morandi-text-muted);
+ line-height: 1.4;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.ss-coming-badge {
+ margin-top: 6px;
+ display: inline-block;
+ padding: 2px 8px;
+ border-radius: 999px;
+ background: var(--morandi-purple-light);
+ color: var(--morandi-purple);
+ font-size: 9px;
+ font-weight: 600;
+}
diff --git a/static/css/style.min.css b/static/css/style.min.css
new file mode 100644
index 0000000..fc90abb
--- /dev/null
+++ b/static/css/style.min.css
@@ -0,0 +1 @@
+:root{--morandi-bg:#F0F2F5;--morandi-card:#FAFBFC;--morandi-card-warm:#F5F0E6;--morandi-card-cool:#EEF2F0;--morandi-card-lilac:#F2EEF5;--morandi-blue:#A0B9D8;--morandi-blue-deep:#8AA3C2;--morandi-blue-light:#DCE6F0;--morandi-pink:#DAB8B0;--morandi-pink-light:#F2E4DF;--morandi-green:#B8D0C9;--morandi-green-light:#E4EFEB;--morandi-purple:#C5CAD0;--morandi-purple-deep:#AEB3B9;--morandi-text:#4A4A4A;--morandi-text-sub:#999999;--morandi-text-muted:#B0B0B0;--morandi-border:#E8EAED;--morandi-divider:#EEEEEE;--module-discover:#A0B9D8;--module-discover-light:#DCE6F0;--module-knowledge:#B8D0C9;--module-knowledge-light:#E4EFEB;--module-life:#DAB8B0;--module-life-light:#F2E4DF;--module-profile:#C5CAD0;--module-profile-light:#EDEEF0;--primary:#A0B9D8;--primary-dark:#8AA3C2;--primary-light:#DCE6F0;--shuren:#A0B9D8;--shuren-light:#DCE6F0;--shuren-gradient:linear-gradient(135deg,#A0B9D8,#8AA3C2);--shushuo:#B8D0C9;--shushuo-light:#E4EFEB;--shushuo-gradient:linear-gradient(135deg,#B8D0C9,#A0BBAF);--shusi:#DAB8B0;--shusi-light:#F2E4DF;--shusi-gradient:linear-gradient(135deg,#DAB8B0,#C9A39A);--mine:#C5CAD0;--mine-light:#EDEEF0;--mine-gradient:linear-gradient(135deg,#C5CAD0,#AEB3B9);--bg:#F0F2F5;--card:#FAFBFC;--text:#4A4A4A;--text-secondary:#999999;--text-muted:#B0B0B0;--border:#E8EAED;--sidebar-bg:#4A4A4A;--sidebar-text:#999999;--sidebar-active:#FAFBFC;--radius-sm:8px;--radius:12px;--radius-lg:16px;--radius-xl:20px;--shadow-sm:0 1px 3px rgba(0,0,0,0.04);--shadow:0 2px 8px rgba(0,0,0,0.05);--shadow-lg:0 4px 16px rgba(0,0,0,0.06);--accent:var(--primary);--accent-light:var(--primary-light);--accent-gradient:linear-gradient(135deg,var(--primary),var(--primary-dark))}*{margin:0;padding:0;box-sizing:border-box}html{scroll-behavior:smooth;-webkit-font-smoothing:antialiased}body{font-family:-apple-system,BlinkMacSystemFont,"Segoe UI","PingFang SC","Microsoft YaHei","Helvetica Neue",sans-serif;background:#E2E5EA;display:flex;justify-content:center;align-items:flex-start;min-height:100vh;color:var(--morandi-text);-webkit-tap-highlight-color:transparent}body::before{content:"";position:fixed;top:0;left:50%;transform:translateX(-50%);width:min(390px,100vw);height:100dvh;background:var(--morandi-bg);z-index:-1;box-shadow:0 0 80px rgba(0,0,0,0.06)}.app-container{display:flex;flex-direction:column;width:min(390px,100vw);max-width:390px;height:100dvh;overflow:hidden;background:var(--morandi-bg);position:relative}.top-bar{display:flex;align-items:center;justify-content:space-between;padding:calc(10px + env(safe-area-inset-top,0px)) 16px 8px;background:var(--morandi-bg);z-index:50}.top-bar-brand{display:flex;align-items:center;gap:8px}.brand-icon-sm{width:28px;height:28px;background:var(--morandi-blue);border-radius:50% 50% 50% 50% / 60% 60% 40% 40%;display:flex;align-items:center;justify-content:center;font-size:16px;color:#fff;font-weight:700;box-shadow:0 2px 6px rgba(160,185,216,0.25),inset 0 -3px 0 rgba(0,0,0,.08)}.brand-text-sm{font-size:15px;font-weight:700;color:var(--morandi-text);letter-spacing:0.3px}.top-bar-actions{display:flex;align-items:center;gap:8px}.icon-btn{width:36px;height:36px;border-radius:50%;border:1px solid var(--morandi-border);background:rgba(250,251,252,0.85);display:flex;align-items:center;justify-content:center;font-size:16px;color:var(--morandi-text-sub);cursor:pointer;box-shadow:var(--shadow-sm);transition:all 0.2s}.icon-btn:active{background:var(--morandi-blue-light);border-color:var(--morandi-blue);color:var(--morandi-blue)}.content-area{flex:1;overflow-y:auto;overflow-x:hidden;background:var(--morandi-bg);display:flex;flex-direction:column}.section-title-bar{padding:12px 16px 10px;background:var(--morandi-bg);position:sticky;top:0;z-index:20}.section-title{font-size:16px;font-weight:700;color:var(--morandi-text);letter-spacing:0.2px}.module-banner{display:flex;align-items:center;gap:10px;padding:12px 14px;margin:0 12px 10px;border-radius:var(--radius);transition:all 0.3s}.module-banner.shuren{background:var(--morandi-blue-light)}.module-banner.shushuo{background:var(--morandi-green-light)}.module-banner.shusi{background:var(--morandi-pink-light)}.module-banner.shuwu{background:var(--module-profile-light)}.banner-icon{font-size:24px;flex-shrink:0;opacity:0.7}.banner-text{flex:1}.banner-title{font-size:12px;font-weight:600;margin-bottom:2px}.module-banner.shuren .banner-title{color:#6B8AAB}.module-banner.shushuo .banner-title{color:#7BA89A}.module-banner.shusi .banner-title{color:#B8958A}.module-banner.shuwu .banner-title{color:#8A8F94}.banner-sub{font-size:11px;color:var(--morandi-text-sub);line-height:1.5}.category-filter{display:flex;flex-wrap:wrap;gap:6px;padding:0 12px 10px}.cat-btn{padding:5px 12px;border-radius:14px;border:1px solid var(--morandi-border);background:var(--morandi-card);font-size:11px;font-weight:500;color:var(--morandi-text-sub);cursor:pointer;transition:all 0.2s;white-space:nowrap;box-shadow:var(--shadow-sm)}.cat-btn:active,.cat-btn.active{border-color:var(--morandi-blue);background:var(--morandi-blue-light);color:var(--morandi-blue);font-weight:600}.save-panel-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(74,74,74,0.45);z-index:180;display:none;align-items:center;justify-content:center;backdrop-filter:blur(4px)}.save-panel-overlay.show{display:flex}.save-panel{background:var(--morandi-bg);border-radius:var(--radius-xl);width:min(360px,92vw);max-height:78vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 16px 48px rgba(0,0,0,0.12)}.save-panel-header{display:flex;align-items:center;gap:10px;padding:14px 16px;background:var(--morandi-card);border-bottom:1px solid var(--morandi-border);flex-shrink:0}.sp-back{border:none;background:var(--morandi-bg);padding:5px 10px;border-radius:7px;font-size:12px;color:var(--morandi-text-sub);cursor:pointer}.sp-title{font-size:15px;font-weight:700;color:var(--morandi-text);flex:1}.save-slots{flex:1;overflow-y:auto;padding:12px 14px 20px;display:flex;flex-direction:column;gap:10px}.save-slot{background:var(--morandi-card);border-radius:var(--radius);padding:14px 16px;box-shadow:var(--shadow-sm);border:1px solid var(--morandi-border);display:flex;align-items:center;gap:12px;cursor:pointer;transition:transform 0.15s,border-color 0.2s}.save-slot:active{transform:scale(0.98)}.save-slot.active{border-color:var(--morandi-blue);box-shadow:0 2px 12px rgba(160,185,216,0.15)}.save-slot-empty{opacity:0.5;justify-content:center;padding:22px;color:var(--morandi-text-muted);font-size:13px}.save-slot-icon{width:42px;height:42px;border-radius:10px;display:flex;align-items:center;justify-content:center;font-size:20px;flex-shrink:0}.save-slot-info{flex:1;min-width:0}.save-slot-name{font-size:13px;font-weight:700;color:var(--morandi-text);margin-bottom:3px}.save-slot-meta{font-size:10px;color:var(--morandi-text-muted);display:flex;gap:8px}.save-slot-actions{display:flex;gap:6px;flex-shrink:0}.save-slot-btn{padding:5px 10px;border:1px solid var(--morandi-border);border-radius:7px;background:var(--morandi-bg);font-size:10px;font-weight:600;cursor:pointer;color:var(--morandi-text-sub);white-space:nowrap;transition:all 0.15s}.save-slot-btn.primary{background:var(--morandi-blue);color:#fff;border-color:transparent}.save-slot-btn:active{transform:scale(0.95)}.gallery-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(74,74,74,0.45);z-index:180;display:none;align-items:center;justify-content:center;backdrop-filter:blur(4px)}.gallery-overlay.show{display:flex}.gallery-panel{background:var(--morandi-bg);border-radius:var(--radius-xl);width:min(360px,92vw);max-height:80vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 16px 48px rgba(0,0,0,0.12)}.gallery-header{display:flex;align-items:center;gap:10px;padding:14px 16px;background:var(--morandi-card);border-bottom:1px solid var(--morandi-border);flex-shrink:0}.gallery-progress{font-size:12px;font-weight:600;color:var(--morandi-blue);white-space:nowrap}.gallery-grid{flex:1;overflow-y:auto;padding:16px 14px 24px;display:grid;grid-template-columns:1fr 1fr;gap:10px}.gallery-card{background:var(--morandi-card);border-radius:var(--radius);padding:18px 14px;text-align:center;box-shadow:var(--shadow-sm);display:flex;flex-direction:column;align-items:center;gap:8px;transition:transform 0.15s}.gallery-card.unlocked{cursor:pointer}.gallery-card.unlocked:active{transform:scale(0.96)}.gallery-card.locked{opacity:0.4;filter:grayscale(1)}.gallery-badge{font-size:32px}.gallery-card-name{font-size:12px;font-weight:700;color:var(--morandi-text)}.gallery-card-desc{font-size:10px;color:var(--morandi-text-muted);line-height:1.4}.gallery-card-lock{font-size:20px;opacity:0.5}.search-header{padding:10px 14px 4px;background:var(--morandi-bg);position:sticky;top:0;z-index:25}.search-bar-wrap{display:flex;align-items:center;background:var(--morandi-card);padding:9px 14px;border-radius:22px;box-shadow:0 1px 4px rgba(0,0,0,0.04);border:1px solid var(--morandi-border);transition:border-color 0.2s,box-shadow 0.2s}.search-bar-wrap:focus-within{border-color:var(--morandi-blue);box-shadow:0 1px 8px rgba(160,185,216,0.15)}.search-icon{margin-right:8px;font-size:13px;opacity:0.35}.search-bar-wrap input{flex:1;border:none;background:transparent;outline:none;font-size:13px;color:var(--morandi-text)}.search-bar-wrap input::placeholder{color:var(--morandi-text-muted)}.sub-nav{display:flex;gap:22px;padding:6px 16px 8px;background:var(--morandi-bg);border-bottom:1px solid var(--morandi-border);margin-bottom:10px}.sub-nav-btn{border:none;background:transparent;font-size:13px;font-weight:500;color:var(--morandi-text-sub);cursor:pointer;padding:4px 0;position:relative;transition:color 0.2s;letter-spacing:0.2px}.sub-nav-btn:active{color:var(--morandi-text)}.sub-nav-btn.active{color:var(--morandi-text);font-weight:700}.sub-nav-btn.active::after{content:"";position:absolute;bottom:0;left:50%;transform:translateX(-50%);width:18px;height:3px;background:var(--morandi-blue);border-radius:2px}.discipline-grid-container{padding:14px 14px 20px;animation:fadeIn 0.3s ease}.grid-section-title{font-size:15px;font-weight:700;color:var(--morandi-text);margin-bottom:16px;letter-spacing:0.2px}.discipline-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:12px 10px}.discipline-item{display:flex;flex-direction:column;align-items:center;gap:8px;cursor:pointer;transition:transform 0.2s;padding:16px 6px 12px;border-radius:var(--radius-lg);background:var(--morandi-card);box-shadow:var(--shadow-sm)}.discipline-item:active{transform:scale(0.95);box-shadow:var(--shadow)}.discipline-icon-box{width:56px;height:56px;border-radius:16px;display:flex;align-items:center;justify-content:center;font-size:24px;font-weight:700;transition:all 0.2s}.discipline-item:hover .discipline-icon-box{transform:translateY(-2px);box-shadow:var(--shadow)}.discipline-label{font-size:11px;font-weight:600;color:var(--morandi-text);text-align:center;white-space:nowrap;letter-spacing:0.2px}.discipline-active-header{padding:10px 14px;background:var(--morandi-bg);border-bottom:1px solid var(--morandi-border);margin-bottom:10px;display:flex;align-items:center}.back-to-grid{display:flex;align-items:center;gap:8px;border:none;background:transparent;cursor:pointer;color:var(--morandi-text);font-size:14px;font-weight:700;padding:4px 0}.back-icon{font-size:16px;color:var(--morandi-blue)}.video-list{display:grid;grid-template-columns:1fr 1fr;gap:10px;padding:0 12px 20px}.video-card{background:var(--morandi-card);border-radius:var(--radius);overflow:hidden;box-shadow:var(--shadow-sm);cursor:pointer;transition:all 0.2s;display:flex;flex-direction:column}.video-card:active{transform:scale(0.98);box-shadow:var(--shadow)}.vc-cover{width:100%;aspect-ratio:16/10;position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden}.vc-cover-placeholder{font-size:28px;opacity:0.18}.vc-duration{position:absolute;bottom:6px;right:6px;background:rgba(74,74,74,0.55);backdrop-filter:blur(4px);color:#fff;font-size:10px;font-weight:500;padding:2px 6px;border-radius:5px;letter-spacing:0.3px}.vc-content{padding:8px 10px 10px;flex:1;display:flex;flex-direction:column}.vc-title{font-size:12px;font-weight:600;line-height:1.45;color:var(--morandi-text);margin-bottom:5px;display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden;min-height:34px;letter-spacing:0.1px}.vc-info-row{display:flex;align-items:center;gap:5px;margin-top:auto}.vc-author-avatar{width:16px;height:16px;border-radius:50%;display:flex;align-items:center;justify-content:center;font-size:8px;color:#fff;flex-shrink:0}.vc-author-name{font-size:10px;color:var(--morandi-text-sub);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;flex:1}.vc-stat{font-size:10px;color:var(--morandi-text-muted);display:flex;align-items:center;gap:2px}.vc-fav{position:absolute;top:6px;right:6px;width:26px;height:26px;border-radius:50%;border:none;background:rgba(74,74,74,0.28);backdrop-filter:blur(3px);color:#fff;font-size:13px;cursor:pointer;display:flex;align-items:center;justify-content:center;opacity:0;transition:opacity 0.2s,transform 0.15s;z-index:2}.video-card:hover .vc-fav,.video-card:active .vc-fav{opacity:1}.vc-fav.active{opacity:1;background:rgba(160,185,216,0.75)}.vc-fav:active{transform:scale(1.15)}.loading{display:flex;flex-direction:column;align-items:center;padding:60px 0;color:var(--morandi-text-muted)}.spinner{width:28px;height:28px;border:2.5px solid var(--morandi-border);border-top-color:var(--morandi-blue);border-radius:50%;animation:spin 0.7s linear infinite;margin-bottom:10px}@keyframes spin{to{transform:rotate(360deg)}}.empty-state{text-align:center;padding:60px 20px;color:var(--morandi-text-muted)}.empty-state .icon{font-size:40px;margin-bottom:10px;opacity:0.4}.empty-state h3{font-size:14px;color:var(--morandi-text-sub);margin-bottom:4px;font-weight:600}.empty-state p{font-size:12px}.shushuo-hero{padding:0 12px 14px;animation:fadeIn 0.3s ease}.hero-card{background:linear-gradient(145deg,#C0CAD4 0%,#A8B8C8 60%,#B5C3D0 100%);border-radius:var(--radius-lg);padding:22px 18px;color:#fff;position:relative;overflow:hidden;box-shadow:0 4px 16px rgba(192,202,212,0.2)}.hero-card::before{content:"";position:absolute;top:-20px;right:-20px;width:90px;height:90px;border-radius:50%;background:rgba(255,255,255,0.05)}.hero-card::after{content:"";position:absolute;bottom:-25px;left:-10px;width:70px;height:70px;border-radius:50%;background:rgba(255,255,255,0.03)}.hero-badge{display:inline-block;padding:3px 10px;background:rgba(255,255,255,0.15);border-radius:10px;font-size:10px;font-weight:600;margin-bottom:12px;letter-spacing:0.5px}.hero-title{font-size:19px;font-weight:800;line-height:1.35;margin-bottom:6px;position:relative;z-index:1}.hero-desc{font-size:11px;opacity:0.72;line-height:1.5;margin-bottom:12px;position:relative;z-index:1}.hero-stats{display:flex;gap:16px;font-size:10px;opacity:0.55;position:relative;z-index:1}.shushuo-topics{padding:0 12px 8px;overflow:hidden}.topics-scroll{display:flex;gap:7px;overflow-x:auto;scrollbar-width:none;padding-bottom:4px}.topics-scroll::-webkit-scrollbar{display:none}.topic-pill{display:inline-flex;padding:5px 12px;border-radius:14px;border:1px solid var(--morandi-border);background:var(--morandi-card);font-size:11px;font-weight:500;color:var(--morandi-text-sub);cursor:pointer;white-space:nowrap;flex-shrink:0;transition:all 0.2s}.topic-pill:active,.topic-pill.active{border-color:var(--morandi-green);background:var(--morandi-green-light);color:#7BA89A;font-weight:600}.shushuo-feed{display:flex;flex-direction:column;gap:12px;padding:0 12px 20px;animation:fadeIn 0.4s ease}.feed-card{background:var(--morandi-card);border-radius:var(--radius-lg);overflow:hidden;box-shadow:var(--shadow-sm);cursor:pointer;transition:transform 0.2s}.feed-card:active{transform:scale(0.98)}.feed-card-visual{width:100%;height:132px;position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden}.feed-card-icon{font-size:42px;opacity:0.22;z-index:1}.feed-card-question{position:absolute;bottom:10px;left:14px;right:14px;font-size:14px;font-weight:700;color:#fff;z-index:1;text-shadow:0 1px 2px rgba(0,0,0,0.15);line-height:1.4}.feed-card-body{padding:10px 14px 14px}.feed-card-title{font-size:15px;font-weight:700;line-height:1.4;margin-bottom:5px;color:var(--morandi-text)}.feed-card-desc{font-size:12px;color:var(--morandi-text-sub);line-height:1.6;margin-bottom:8px}.feed-card-footer{display:flex;align-items:center;gap:8px}.feed-card-topic{font-size:10px;font-weight:500}.feed-card-time{font-size:10px;color:var(--morandi-text-muted)}.feed-card-fav{position:absolute;top:6px;right:6px;width:26px;height:26px;border-radius:50%;border:none;background:rgba(74,74,74,0.2);color:#fff;font-size:13px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:transform 0.15s;z-index:2}.feed-card-fav.active{background:rgba(160,185,216,0.7)}.feed-card-fav:active{transform:scale(1.15)}.profile-page{padding:6px 12px 20px;animation:fadeIn 0.3s ease;display:flex;flex-direction:column;gap:6px}.profile-card{background:linear-gradient(135deg,#C5CAD0 0%,#B5C0CA 40%,#C0C8D0 100%);border-radius:var(--radius-lg);padding:14px 14px 12px;color:#fff;box-shadow:0 6px 20px rgba(197,202,208,0.2);flex-shrink:0;cursor:pointer;transition:opacity 0.15s}.profile-card:active{opacity:0.92}.profile-main{display:flex;align-items:center;gap:10px}.profile-avatar{width:48px;height:48px;border-radius:50% 50% 50% 50% / 60% 60% 40% 40%;box-shadow:inset 0 -3px 0 rgba(0,0,0,.08);display:flex;align-items:center;justify-content:center;background:rgba(255,255,255,0.2);font-size:20px;font-weight:800;flex-shrink:0}.profile-meta{flex:1;min-width:0}.profile-name{font-size:16px;font-weight:800;margin-bottom:1px;letter-spacing:0.2px}.profile-id{font-size:10px;opacity:0.7;margin-bottom:6px}.profile-tags{display:flex;flex-wrap:wrap;gap:5px}.profile-tags span{padding:2px 7px;border-radius:999px;background:rgba(255,255,255,0.16);font-size:10px;font-weight:500}.profile-edit{margin-top:8px;width:fit-content;padding:4px 10px;border-radius:999px;background:rgba(255,255,255,0.14);font-size:10px;font-weight:600;cursor:pointer}.profile-stats{display:grid;grid-template-columns:repeat(3,1fr);gap:6px;flex-shrink:0}.profile-stat{background:var(--morandi-card);border-radius:var(--radius);padding:10px 6px;text-align:center;box-shadow:var(--shadow-sm);border:1px solid var(--morandi-border);cursor:pointer;transition:transform 0.15s}.profile-stat:active{transform:scale(0.96)}.profile-stat-num{font-size:17px;font-weight:800;color:var(--morandi-blue);margin-bottom:1px}.profile-stat-label{font-size:10px;color:var(--morandi-text-sub)}.profile-section{flex-shrink:0}.profile-section-title{font-size:10px;font-weight:700;color:var(--morandi-text-sub);margin-bottom:4px;padding:0 2px;letter-spacing:0.3px;text-transform:uppercase}.profile-menu-card{background:var(--morandi-card);border-radius:var(--radius);box-shadow:var(--shadow-sm);overflow:hidden}.profile-menu-item{display:flex;align-items:center;justify-content:space-between;padding:9px 12px;border-bottom:1px solid var(--morandi-divider);cursor:pointer;transition:background 0.15s}.profile-menu-item:last-child{border-bottom:none}.profile-menu-item:active{background:var(--morandi-bg)}.profile-menu-left{display:flex;align-items:center;gap:8px;font-size:12px;font-weight:600;color:var(--morandi-text)}.menu-icon{width:22px;height:22px;border-radius:6px;background:var(--morandi-bg);display:inline-flex;align-items:center;justify-content:center;color:var(--morandi-blue);font-size:11px;font-weight:700}.menu-arrow{font-size:14px;color:var(--morandi-text-muted);font-weight:400}.bottom-tab-bar{display:flex;align-items:center;justify-content:space-around;gap:2px;background:rgba(255,255,255,0.96);backdrop-filter:blur(20px);-webkit-backdrop-filter:blur(20px);padding:8px 10px calc(10px + env(safe-area-inset-bottom,0px));border-top:1px solid var(--morandi-border);z-index:60;box-shadow:0 -4px 20px rgba(160,185,216,0.06)}.tab-btn{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;border:none;background:transparent;color:var(--morandi-text-muted);cursor:pointer;width:78px;min-height:48px;border-radius:14px;transition:all 0.2s ease}.tab-icon{width:20px;height:20px;position:relative;transition:transform 0.2s,opacity 0.2s;opacity:0.6}.tab-label{font-size:10px;font-weight:500;letter-spacing:0.2px}.tab-btn.active{color:var(--morandi-blue);background:var(--morandi-blue-light)}.tab-btn.active .tab-icon{transform:translateY(-1px);opacity:1}.tab-btn.active .tab-label{font-weight:700}.tab-icon::before,.tab-icon::after{content:"";position:absolute;box-sizing:border-box}.tab-icon-discover::before{inset:3px;border:1.8px solid currentColor;border-radius:5px}.tab-icon-discover::after{width:4px;height:8px;right:5px;top:7px;background:currentColor;border-radius:2px}.tab-icon-knowledge::before{left:4px;right:4px;bottom:3px;height:10px;border:1.8px solid currentColor;border-top:none;border-radius:1px 1px 4px 4px}.tab-icon-knowledge::after{left:6px;top:2px;width:10px;height:10px;background:currentColor;clip-path:polygon(50% 0,100% 40%,100% 100%,0 100%,0 40%)}.tab-icon-life::before{left:5px;right:5px;top:3px;bottom:3px;border:1.8px solid currentColor;border-radius:3px 3px 7px 7px}.tab-icon-life::after{left:8px;right:8px;top:8px;height:2px;background:currentColor;border-radius:2px}.tab-icon-mine::before{width:7px;height:7px;left:3px;top:3px;background:currentColor;border-radius:2px;box-shadow:9px 0 0 currentColor,0 9px 0 currentColor,9px 9px 0 currentColor}.chat-overlay{display:none;position:fixed;inset:0;z-index:200;background:rgba(74,74,74,0.35);justify-content:center;align-items:center;backdrop-filter:blur(4px)}.chat-overlay.show{display:flex}.chat-panel{width:380px;max-width:93vw;height:78vh;background:var(--morandi-card);border-radius:var(--radius-xl);display:flex;flex-direction:column;animation:modalIn 0.25s cubic-bezier(0.16,1,0.3,1);box-shadow:0 20px 50px rgba(0,0,0,0.12);overflow:hidden}@keyframes modalIn{from{opacity:0;transform:scale(0.95) translateY(10px)}to{opacity:1;transform:scale(1) translateY(0)}}.chat-header{display:flex;align-items:center;justify-content:space-between;padding:14px 18px;border-bottom:1px solid var(--morandi-border);font-weight:700;font-size:14px;color:var(--morandi-text)}.chat-header-left{display:flex;align-items:center;gap:10px}.ai-avatar{width:30px;height:30px;background:var(--morandi-blue);border-radius:9px;display:flex;align-items:center;justify-content:center;font-size:15px;color:#fff;box-shadow:0 2px 6px rgba(160,185,216,0.3)}.chat-close{width:28px;height:28px;border-radius:50%;border:none;background:var(--morandi-bg);cursor:pointer;font-size:14px;display:flex;align-items:center;justify-content:center;color:var(--morandi-text-sub)}.chat-messages{flex:1;overflow-y:auto;padding:14px;display:flex;flex-direction:column;gap:10px;background:var(--morandi-bg)}.chat-msg{display:flex;animation:msgIn 0.3s ease}@keyframes msgIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}.chat-msg.assistant{justify-content:flex-start}.chat-msg.user{justify-content:flex-end}.msg-bubble{max-width:82%;padding:9px 13px;border-radius:13px;font-size:13px;line-height:1.6;word-break:break-word}.chat-msg.assistant .msg-bubble{background:#fff;border-bottom-left-radius:3px;box-shadow:var(--shadow-sm)}.chat-msg.user .msg-bubble{background:linear-gradient(135deg,#A0B9D8,#8AA3C2);color:#fff;border-bottom-right-radius:3px}.quick-asks{display:flex;flex-wrap:wrap;gap:5px;padding:6px 14px}.quick-ask{padding:5px 11px;border-radius:12px;border:1px solid var(--morandi-border);background:#fff;font-size:11px;color:var(--morandi-text-sub);cursor:pointer;transition:all 0.15s}.quick-ask:active{background:var(--morandi-blue-light);border-color:var(--morandi-blue);color:var(--morandi-blue)}.chat-input-area{display:flex;padding:8px 14px 12px;border-top:1px solid var(--morandi-border);gap:8px;background:#fff}.chat-input-area input{flex:1;padding:9px 13px;border:1px solid var(--morandi-border);border-radius:18px;font-size:13px;outline:none;background:var(--morandi-bg);transition:border-color 0.2s}.chat-input-area input:focus{border-color:var(--morandi-blue)}.send-btn{min-width:36px;height:36px;border:none;border-radius:50%;background:var(--morandi-blue);color:#fff;font-size:15px;cursor:pointer;flex-shrink:0;display:flex;align-items:center;justify-content:center;box-shadow:0 2px 8px rgba(160,185,216,0.25)}.send-btn:active{transform:scale(0.93)}.typing-indicator{display:flex;gap:4px;padding:3px 0}.typing-indicator span{width:5px;height:5px;border-radius:50%;background:var(--morandi-text-muted);animation:bounce 1.2s infinite}.typing-indicator span:nth-child(2){animation-delay:0.2s}.typing-indicator span:nth-child(3){animation-delay:0.4s}@keyframes bounce{0%,60%,100%{transform:translateY(0)}30%{transform:translateY(-6px)}}.video-modal{display:none;position:fixed;inset:0;z-index:200;background:rgba(74,74,74,0.5);justify-content:center;align-items:center;backdrop-filter:blur(4px)}.video-modal.show{display:flex}.video-modal-content{width:360px;max-width:93vw;max-height:86vh;background:var(--morandi-card);border-radius:var(--radius-lg);overflow-y:auto;animation:modalIn 0.25s cubic-bezier(0.16,1,0.3,1)}.video-modal-header{display:flex;justify-content:space-between;align-items:center;padding:14px 16px;border-bottom:1px solid var(--morandi-border)}.video-modal-header h3{font-size:14px;font-weight:700;flex:1;margin-right:10px;line-height:1.4;color:var(--morandi-text)}.video-modal-header button{width:28px;height:28px;border-radius:50%;border:none;background:var(--morandi-bg);cursor:pointer;font-size:14px;display:flex;align-items:center;justify-content:center;flex-shrink:0;color:var(--morandi-text-sub)}.video-player-area{background:#E8EBEF;aspect-ratio:16/9;display:flex;align-items:center;justify-content:center}.video-placeholder{text-align:center;color:rgba(74,74,74,0.35)}.play-icon{width:46px;height:46px;border-radius:50%;background:rgba(160,185,216,0.25);display:flex;align-items:center;justify-content:center;font-size:20px;margin:0 auto 8px}.video-placeholder p{font-size:11px}.video-info{padding:12px 16px}.video-desc{font-size:12px;line-height:1.6;color:var(--morandi-text-sub);margin-bottom:8px}.video-meta{font-size:10px;color:var(--morandi-text-muted);display:flex;flex-wrap:wrap;gap:10px}.video-actions{padding:0 16px 16px;display:flex;gap:8px}.btn-primary,.btn-secondary{flex:1;padding:9px;border-radius:10px;border:none;font-size:12px;font-weight:600;cursor:pointer;text-align:center;transition:transform 0.15s}.btn-primary:active,.btn-secondary:active{transform:scale(0.97)}.btn-primary{background:var(--morandi-blue);color:#fff;box-shadow:0 2px 8px rgba(160,185,216,0.2)}.btn-secondary{background:var(--morandi-bg);color:var(--morandi-text)}.vn-header{display:flex;flex-direction:column;gap:8px;margin-bottom:10px;padding:0 12px}.vn-chapter-badge{align-self:flex-start;padding:4px 12px;border-radius:999px;font-size:11px;font-weight:700;color:#fff;background:var(--morandi-green);box-shadow:0 2px 6px rgba(184,208,201,0.3);letter-spacing:0.3px}.vn-stats{display:flex;gap:6px;flex-wrap:wrap}.stat-badge{font-size:10px;padding:4px 8px;border-radius:10px;color:#fff;font-weight:600;box-shadow:0 1px 3px rgba(0,0,0,0.06)}.vn-actions{display:flex;gap:6px;padding:0 12px}.vn-btn{background:var(--morandi-card);border:1px solid var(--morandi-border);padding:5px 10px;border-radius:8px;font-size:11px;cursor:pointer;box-shadow:var(--shadow-sm);color:var(--morandi-text-sub);font-weight:600;transition:all 0.2s}.vn-btn:active{background:var(--morandi-green-light);border-color:var(--morandi-green);color:#7BA89A}.story-scene{background:var(--morandi-card);margin:0 12px 12px;border-radius:var(--radius-lg);padding:18px;min-height:260px;box-shadow:var(--shadow-sm);position:relative;overflow:hidden;display:flex;flex-direction:column;justify-content:flex-end}.vn-background{position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover;z-index:0;border-radius:var(--radius-lg)}.vn-sprite{position:absolute;bottom:0;right:0;max-height:75%;max-width:55%;object-fit:contain;z-index:1;pointer-events:none;transition:opacity 0.4s ease}.story-text-box{position:relative;z-index:2;background:rgba(250,251,252,0.94);backdrop-filter:blur(4px);padding:12px;border-radius:9px;border:1px solid var(--morandi-border)}.story-character{font-weight:800;color:var(--morandi-green);margin-bottom:5px;font-size:12px;letter-spacing:0.3px}.story-text{font-size:13px;line-height:1.7;color:var(--morandi-text);min-height:44px;white-space:pre-line}.story-choices{display:flex;flex-direction:column;gap:8px;padding:0 12px 20px}.choice-btn{background:var(--morandi-card);border:1px solid var(--morandi-border);border-radius:var(--radius);padding:12px 14px;font-size:13px;font-weight:600;color:var(--morandi-text);text-align:left;cursor:pointer;transition:all 0.2s;box-shadow:var(--shadow-sm);display:flex;flex-direction:column;gap:5px;animation:fadeInUp 0.3s ease both}.choice-btn:hover{background:var(--morandi-green-light);border-color:var(--morandi-green)}.choice-btn:active{transform:scale(0.98);background:var(--morandi-green-light)}.choice-hint{font-size:10px;color:#7BA89A;font-weight:500}.choice-text{line-height:1.5}@keyframes fadeInUp{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.sim-header{display:flex;flex-direction:column;gap:8px;margin:10px 0;padding:0 12px}.sim-year-badge{align-self:flex-start;padding:4px 14px;border-radius:999px;font-size:11px;font-weight:700;color:#fff;box-shadow:0 2px 6px rgba(0,0,0,0.1)}.sim-stats{display:flex;gap:6px;flex-wrap:wrap}.sim-stat-bar{font-size:10px;padding:4px 8px;border-radius:10px;color:#fff;font-weight:600;box-shadow:0 1px 3px rgba(0,0,0,0.06)}.sim-actions-top{display:flex;gap:6px;padding:0 12px 8px}.sim-scene{background:var(--morandi-card);margin:0 12px 12px;border-radius:var(--radius-lg);padding:18px;min-height:180px;box-shadow:var(--shadow-sm);position:relative}.sim-character{font-weight:800;color:var(--morandi-pink);margin-bottom:8px;font-size:13px;letter-spacing:0.3px}.sim-text{font-size:13px;line-height:1.7;color:var(--morandi-text);min-height:54px;white-space:pre-line}.sim-choices{display:flex;flex-direction:column;gap:8px;padding:0 12px 28px}.sim-choice-btn{background:var(--morandi-card);border:1px solid var(--morandi-border);border-radius:var(--radius);padding:12px 14px;font-size:13px;font-weight:600;color:var(--morandi-text);text-align:left;cursor:pointer;transition:all 0.2s;box-shadow:var(--shadow-sm);display:flex;flex-direction:column;gap:5px;animation:fadeInUp 0.3s ease both}.sim-choice-btn:hover{background:var(--morandi-pink-light);border-color:var(--morandi-pink)}.sim-choice-btn:active{transform:scale(0.98)}.sim-hint{font-size:10px;color:#B8958A;font-weight:500}.sim-hint-tip{font-size:10px;color:var(--morandi-text-muted);font-weight:400}.sim-choice-text{line-height:1.5}.ending-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(74,74,74,0.5);z-index:200;display:none;align-items:center;justify-content:center}.ending-overlay.show{display:flex}.ending-card{background:var(--morandi-card);border-radius:var(--radius-xl);padding:24px 22px;width:min(320px,88vw);text-align:center;box-shadow:0 16px 48px rgba(0,0,0,0.15);animation:fadeInUp 0.4s ease;display:flex;flex-direction:column;gap:10px}.ending-badge{font-size:44px}.ending-type{font-size:12px;color:var(--morandi-text-muted);font-weight:600;letter-spacing:1px}.ending-title{font-size:16px;font-weight:700;color:var(--morandi-text)}.ending-scene{font-size:12px;color:var(--morandi-text-sub);line-height:1.7;background:var(--morandi-bg);border-radius:var(--radius);padding:10px}.avatar-picker-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(74,74,74,0.45);z-index:150;display:none;align-items:center;justify-content:center}.avatar-picker-overlay.show{display:flex}.avatar-picker-card{background:var(--morandi-card);border-radius:var(--radius-xl);padding:18px;width:min(320px,88vw);box-shadow:0 16px 48px rgba(0,0,0,0.15);display:flex;flex-direction:column;gap:12px}.picker-title{font-size:14px;font-weight:700;text-align:center;color:var(--morandi-text)}.picker-emoji-grid{display:flex;flex-wrap:wrap;gap:5px;justify-content:center}.picker-emoji-btn{width:34px;height:34px;border:1.5px solid var(--morandi-border);border-radius:8px;background:var(--morandi-bg);font-size:16px;cursor:pointer;display:flex;align-items:center;justify-content:center;transition:all 0.15s}.picker-emoji-btn:active{transform:scale(0.9)}.picker-color-row{display:flex;gap:8px;justify-content:center}.picker-color-btn{width:30px;height:30px;border:none;border-radius:50%;cursor:pointer;transition:transform 0.15s}.picker-color-btn:active{transform:scale(0.85)}.picker-actions{display:flex;gap:8px}.picker-btn{flex:1;padding:9px;border:1px solid var(--morandi-border);border-radius:10px;background:var(--morandi-bg);font-size:12px;font-weight:600;cursor:pointer;text-align:center;color:var(--morandi-text-sub)}.picker-btn.primary{background:var(--morandi-blue);color:#fff;border-color:transparent;box-shadow:0 2px 8px rgba(160,185,216,0.2)}.profile-list-panel{position:absolute;top:0;left:0;right:0;bottom:0;background:var(--morandi-bg);z-index:30;display:flex;flex-direction:column;overflow:hidden}.plp-header{display:flex;align-items:center;gap:10px;padding:12px 14px;background:var(--morandi-card);border-bottom:1px solid var(--morandi-border);position:sticky;top:0;z-index:5}.plp-back{border:none;background:var(--morandi-bg);padding:5px 10px;border-radius:7px;font-size:12px;color:var(--morandi-text-sub);cursor:pointer}.plp-title{font-size:14px;font-weight:700;color:var(--morandi-text)}.plp-content{flex:1;overflow-y:auto;padding:10px 12px}.history-item{display:flex;align-items:center;gap:10px;background:var(--morandi-card);border-radius:var(--radius);padding:10px 12px;margin-bottom:7px;box-shadow:var(--shadow-sm)}.hi-icon{width:40px;height:40px;border-radius:8px;display:flex;align-items:center;justify-content:center;font-size:18px;flex-shrink:0}.hi-info{flex:1;min-width:0}.hi-title{font-size:13px;font-weight:600;color:var(--morandi-text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.hi-meta{font-size:10px;color:var(--morandi-text-muted);margin-top:2px}.hi-del{border:none;background:transparent;color:#DAB8B0;font-size:15px;cursor:pointer;padding:4px 8px;border-radius:5px;transition:background 0.2s}.hi-del:hover{background:var(--morandi-pink-light)}.life-sub-nav{display:flex;gap:4px;padding:6px 12px;background:var(--morandi-card);border-bottom:1px solid var(--morandi-border)}.life-sub-btn{flex:1;padding:7px 0;border:none;border-radius:8px;background:var(--morandi-bg);font-size:12px;font-weight:600;color:var(--morandi-text-sub);cursor:pointer;transition:all 0.2s}.life-sub-btn.active{background:var(--morandi-pink);color:#fff;box-shadow:0 2px 6px rgba(218,184,176,0.3)}.about-card{background:var(--morandi-card);border-radius:var(--radius);padding:24px 18px;text-align:center;box-shadow:var(--shadow-sm);margin:0 12px}.about-card .avatar{width:56px;height:56px;background:var(--morandi-blue);border-radius:50% 50% 50% 50% / 60% 60% 40% 40%;display:flex;align-items:center;justify-content:center;font-size:26px;margin:0 auto 12px;color:#fff;box-shadow:0 3px 10px rgba(160,185,216,0.2),inset 0 -3px 0 rgba(0,0,0,.08)}.about-card h2{font-size:16px;font-weight:700;margin-bottom:4px;color:var(--morandi-text)}.about-card .subtitle{font-size:11px;color:var(--morandi-blue);font-weight:600;margin-bottom:12px}.about-card .quote{font-size:12px;color:var(--morandi-text-sub);line-height:1.6;padding:10px;background:var(--morandi-bg);border-radius:var(--radius-sm);margin-bottom:10px}.about-card .version{font-size:10px;color:var(--morandi-text-muted)}@media (min-width:480px){body::before,.app-container{border-radius:34px}body::before{top:10px;height:calc(100dvh - 20px)}.app-container{margin-top:10px;height:calc(100dvh - 20px);box-shadow:0 20px 60px rgba(160,185,216,0.12);border:1px solid rgba(200,208,216,0.4)}}@media (max-width:420px){body::before{display:none}.app-container{width:100%;max-width:100%}}.script-square{padding:12px 12px 28px;animation:fadeIn 0.35s ease}.ss-section-title{font-size:12px;font-weight:700;color:var(--morandi-text-sub);margin:18px 0 10px;padding:0 2px;letter-spacing:0.8px;text-transform:uppercase}.ss-section-title:first-of-type{margin-top:4px}.ss-continue{margin-bottom:4px}.ss-continue-card{display:flex;align-items:center;gap:12px;background:var(--morandi-card);border:1.5px solid var(--morandi-blue-light);border-radius:var(--radius-lg);padding:14px 16px;cursor:pointer;box-shadow:0 2px 10px rgba(160,185,216,0.12);transition:transform 0.15s,box-shadow 0.2s}.ss-continue-card:active{transform:scale(0.98);box-shadow:var(--shadow)}.ss-continue-badge{padding:5px 12px;border-radius:999px;background:var(--morandi-blue);color:#fff;font-size:11px;font-weight:700;white-space:nowrap;flex-shrink:0;box-shadow:0 2px 6px rgba(160,185,216,0.25)}.ss-continue-info{flex:1;min-width:0}.ss-continue-title{font-size:14px;font-weight:700;color:var(--morandi-text);margin-bottom:2px}.ss-continue-sub{font-size:11px;color:var(--morandi-text-muted)}.ss-continue-arrow{font-size:20px;color:var(--morandi-blue);flex-shrink:0}.ss-hero-cards{display:flex;flex-direction:column;gap:14px}.ss-hero-card{position:relative;border-radius:var(--radius-xl);overflow:hidden;cursor:pointer;box-shadow:0 4px 18px rgba(0,0,0,0.06);transition:transform 0.2s,box-shadow 0.2s;min-height:170px;display:flex;flex-direction:column;justify-content:flex-end}.ss-hero-card:active{transform:scale(0.98);box-shadow:0 6px 22px rgba(0,0,0,0.1)}.ss-hero-bg{position:absolute;inset:0;z-index:0}.ss-hero-bg::before{content:"";position:absolute;top:-30px;right:-30px;width:120px;height:120px;border-radius:50%;background:rgba(255,255,255,0.06)}.ss-hero-bg::after{content:"";position:absolute;bottom:-20px;left:20px;width:80px;height:80px;border-radius:50%;background:rgba(255,255,255,0.04)}.ss-hero-content{position:relative;z-index:1;padding:22px 18px;color:#fff}.ss-hero-icon{font-size:36px;margin-bottom:10px;opacity:0.85}.ss-hero-name{font-size:19px;font-weight:800;margin-bottom:4px;letter-spacing:0.2px}.ss-hero-desc{font-size:12px;opacity:0.78;line-height:1.5;margin-bottom:12px}.ss-hero-meta{display:flex;gap:14px;font-size:10px;opacity:0.55}.ss-hero-status{display:inline-flex;align-items:center;gap:4px;padding:3px 10px;border-radius:999px;background:rgba(255,255,255,0.18);font-size:10px;font-weight:600}.ss-card-wuxia .ss-hero-bg{background:linear-gradient(145deg,#7BA89A 0%,#5C8F7E 50%,#8BB5A5 100%)}.ss-card-simulator .ss-hero-bg{background:linear-gradient(145deg,#C9A39A 0%,#B8958A 50%,#D4B5AC 100%)}.ss-card-mathbti .ss-hero-bg{background:linear-gradient(145deg,#8BA4C0 0%,#7A93B0 50%,#9CB5CD 100%)}.ss-coming-cards{display:flex;gap:12px;overflow-x:auto;scrollbar-width:none;padding-bottom:6px}.ss-coming-cards::-webkit-scrollbar{display:none}.ss-coming-card{flex-shrink:0;width:130px;border-radius:var(--radius-lg);overflow:hidden;background:var(--morandi-card);box-shadow:var(--shadow-sm);opacity:0.7;pointer-events:none}.ss-coming-card-bg{height:72px;display:flex;align-items:center;justify-content:center;font-size:26px;opacity:0.35}.ss-coming-card-body{padding:10px 12px 12px}.ss-coming-card-name{font-size:12px;font-weight:700;color:var(--morandi-text);margin-bottom:3px}.ss-coming-card-desc{font-size:10px;color:var(--morandi-text-muted);line-height:1.4;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ss-coming-badge{margin-top:6px;display:inline-block;padding:2px 8px;border-radius:999px;background:var(--morandi-purple-light);color:var(--morandi-purple);font-size:9px;font-weight:600}
diff --git a/static/img/mathematicians/0000.png b/static/img/mathematicians/0000.png
new file mode 100644
index 0000000..5d39c7b
Binary files /dev/null and b/static/img/mathematicians/0000.png differ
diff --git a/static/img/mathematicians/0001.png b/static/img/mathematicians/0001.png
new file mode 100644
index 0000000..62ac653
Binary files /dev/null and b/static/img/mathematicians/0001.png differ
diff --git a/static/img/mathematicians/0010.png b/static/img/mathematicians/0010.png
new file mode 100644
index 0000000..96a6810
Binary files /dev/null and b/static/img/mathematicians/0010.png differ
diff --git a/static/img/mathematicians/0011.png b/static/img/mathematicians/0011.png
new file mode 100644
index 0000000..9458062
Binary files /dev/null and b/static/img/mathematicians/0011.png differ
diff --git a/static/img/mathematicians/0100.png b/static/img/mathematicians/0100.png
new file mode 100644
index 0000000..da1e5fa
Binary files /dev/null and b/static/img/mathematicians/0100.png differ
diff --git a/static/img/mathematicians/0101.png b/static/img/mathematicians/0101.png
new file mode 100644
index 0000000..b884285
Binary files /dev/null and b/static/img/mathematicians/0101.png differ
diff --git a/static/img/mathematicians/0110.png b/static/img/mathematicians/0110.png
new file mode 100644
index 0000000..9f3f354
Binary files /dev/null and b/static/img/mathematicians/0110.png differ
diff --git a/static/img/mathematicians/0111.png b/static/img/mathematicians/0111.png
new file mode 100644
index 0000000..39228b7
Binary files /dev/null and b/static/img/mathematicians/0111.png differ
diff --git a/static/img/mathematicians/1000.png b/static/img/mathematicians/1000.png
new file mode 100644
index 0000000..6893109
Binary files /dev/null and b/static/img/mathematicians/1000.png differ
diff --git a/static/img/mathematicians/1001.png b/static/img/mathematicians/1001.png
new file mode 100644
index 0000000..760c846
Binary files /dev/null and b/static/img/mathematicians/1001.png differ
diff --git a/static/img/mathematicians/1010.png b/static/img/mathematicians/1010.png
new file mode 100644
index 0000000..3e1686a
Binary files /dev/null and b/static/img/mathematicians/1010.png differ
diff --git a/static/img/mathematicians/1011.png b/static/img/mathematicians/1011.png
new file mode 100644
index 0000000..a3dc3f0
Binary files /dev/null and b/static/img/mathematicians/1011.png differ
diff --git a/static/img/mathematicians/1100.png b/static/img/mathematicians/1100.png
new file mode 100644
index 0000000..b659205
Binary files /dev/null and b/static/img/mathematicians/1100.png differ
diff --git a/static/img/mathematicians/1101.png b/static/img/mathematicians/1101.png
new file mode 100644
index 0000000..1df76f9
Binary files /dev/null and b/static/img/mathematicians/1101.png differ
diff --git a/static/img/mathematicians/1110.png b/static/img/mathematicians/1110.png
new file mode 100644
index 0000000..733a718
Binary files /dev/null and b/static/img/mathematicians/1110.png differ
diff --git a/static/img/mathematicians/1111.png b/static/img/mathematicians/1111.png
new file mode 100644
index 0000000..a100d85
Binary files /dev/null and b/static/img/mathematicians/1111.png differ
diff --git a/static/js/app.js b/static/js/app.js
new file mode 100644
index 0000000..ce7cc4e
--- /dev/null
+++ b/static/js/app.js
@@ -0,0 +1,1509 @@
+/* =========================================================
+ app.js — 前端交互逻辑 v7.0 · 动态知识卡片 + 收藏历史 + 个人中心
+ ========================================================= */
+
+// ---- 全局状态 ----
+let currentModule = "发现";
+let currentVideoUrl = "";
+let currentVideoTitle = "";
+let currentDiscipline = "all";
+let currentSubTab = "recommend";
+let currentLifeTab = "story";
+let allVideos = [];
+let allKnowledgeCards = [];
+let storyData = null;
+let simulatorData = null;
+let currentContext = { type: "", title: "" };
+let userId = localStorage.getItem("mathAppUserId") || generateUserId();
+let userFavorites = { video: {}, knowledge: {} };
+let userAvatar = localStorage.getItem("mathAppAvatar") || "葫";
+let userAvatarColor = localStorage.getItem("mathAppAvatarColor") || "#A0B9D8";
+
+let vnState = {
+ currentNode: null,
+ stats: { shuli: 10, xiayi: 10, xinzhi: 10 },
+ favorability: { "欧阳侠": 0, "Eddie侠": 0, "厚朴侠": 0, "三力思侠": 0, "溜溜侠": 0, "孔锝侠": 0, "星侠": 0, "甲亢侠": 0 }
+};
+
+let simState = {
+ currentNode: null,
+ stats: { gpa: 10, interest: 10, skill: 10 },
+ history: []
+};
+
+const SIM_STAT_LABELS = { gpa: "绩点", interest: "兴趣", skill: "技能" };
+const SIM_STAT_COLORS = { gpa: "#A0B9D8", interest: "#DAB8B0", skill: "#B8D0C9" };
+const SIM_STAT_EMOJIS = { gpa: "📊", interest: "❤️", skill: "🔧" };
+
+function generateUserId() {
+ var id = "user_" + Date.now().toString(36) + "_" + Math.random().toString(36).substr(2, 6);
+ localStorage.setItem("mathAppUserId", id);
+ return id;
+}
+
+const STAT_LABELS = { shuli: "数理根基", xiayi: "侠义声望", xinzhi: "心志韧性" };
+const STAT_COLORS = { shuli: "#A0B9D8", xiayi: "#DAB8B0", xinzhi: "#B8D0C9" };
+const STAT_EMOJIS = { shuli: "📐", xiayi: "⚔️", xinzhi: "🧘" };
+
+// 章节检测:根据节点ID前缀判断当前章节
+function detectChapter(nodeId) {
+ if (!nodeId) return 1;
+ var m = nodeId.match(/^ch(\d+)/);
+ return m ? parseInt(m[1]) : 1;
+}
+
+// ---- 模块配色 ----
+const MODULE_COLORS = {
+ "发现": { color: "#A0B9D8", gradient: "linear-gradient(135deg, #A0B9D8, #8AA3C2)", lightBg: "#DCE6F0", name: "发现 · 值得一看的数学内容" },
+ "知识": { color: "#B8D0C9", gradient: "linear-gradient(135deg, #B8D0C9, #A0BBAF)", lightBg: "#E4EFEB", name: "知识 · 数学发现" },
+ "数学人生": { color: "#DAB8B0", gradient: "linear-gradient(135deg, #DAB8B0, #C9A39A)", lightBg: "#F2E4DF", name: "数学人生 · 大学选择模拟" },
+ "我的": { color: "#C5CAD0", gradient: "linear-gradient(135deg, #C5CAD0, #AEB3B9)", lightBg: "#EDEEF0", name: "我的 · 个人中心" }
+};
+
+const MODULE_BANNERS = {
+ "发现": { icon: "◎", title: "发现值得一看的数学内容", sub: "从 12 个专业分区进入,直接查看该方向的视频内容" },
+ "知识": { icon: "≡", title: "用数学视角理解世界", sub: "把复杂概念变成轻量、好懂、适合反复刷的知识卡片" },
+ "数学人生": { icon: "◇", title: "葫芦侠行 · 互动剧情", sub: "八章完整剧情 | 四属性系统 | 多结局 | 北宋数学武侠世界" },
+ "我的": { icon: "✦", title: "你的学习空间", sub: "查看资料、收藏、记录和剧情存档" }
+};
+
+const MODULE_CSS_CLASS = {
+ "发现": "shuren", "知识": "shushuo", "数学人生": "shusi", "我的": "shuwu"
+};
+
+const DISCIPLINE_ICONS = {
+ "计算机": { icon: "💻", symbol: "⌨", color: "#fff", bg: "#8BA4C0" },
+ "机械工程": { icon: "⚙️", symbol: "⚙", color: "#fff", bg: "#B8A99A" },
+ "电子信息": { icon: "📡", symbol: "⚡", color: "#fff", bg: "#9BB8AE" },
+ "经济管理": { icon: "📊", symbol: "📈", color: "#fff", bg: "#C5A8A0" },
+ "生物医学": { icon: "🧬", symbol: "❤", color: "#fff", bg: "#C4B0B8" },
+ "化工材料": { icon: "🧪", symbol: "⚗", color: "#fff", bg: "#ACAEC5" },
+ "人工智能": { icon: "🤖", symbol: "AI", color: "#fff", bg: "#95ABB8" },
+ "微电子": { icon: "🔌", symbol: "μ", color: "#fff", bg: "#8BA4C0" },
+ "自动化": { icon: "🦾", symbol: "⚙", color: "#fff", bg: "#BCA898" },
+ "能源动力": { icon: "🔥", symbol: "⚡", color: "#fff", bg: "#A3B4A5" },
+ "环境工程": { icon: "🌱", symbol: "✿", color: "#fff", bg: "#A8B8A8" },
+ "数学建模": { icon: "📐", symbol: "Σ", color: "#fff", bg: "#9CAEC4" },
+ "通用": { icon: "📚", symbol: "◎", color: "#fff", bg: "#ADB5BD" }
+};
+
+const AVATAR_COLORS = ["#A0B9D8","#B8D0C9","#DAB8B0","#C5CAD0","#B8A99A","#9BB8AE","#C4B0B8","#ACAEC5"];
+
+// ---- 剧本广场定义 ----
+const SCRIPTS = [
+ {
+ id: "huluxiaxing",
+ name: "葫芦侠行",
+ icon: "📖",
+ desc: "北宋年间,紫金山下秉公习武堂。你扮演葫芦侠,用数学武学闯荡江湖。",
+ meta: "93节点 · 8章 · 8+结局",
+ status: "ready",
+ cssClass: "ss-card-wuxia",
+ engine: "story"
+ },
+ {
+ id: "simulator",
+ name: "数学专业模拟器",
+ icon: "🎓",
+ desc: "跑完四年大学数学专业的全流程。选课、考试、分流、毕业——你的每个选择决定结局。",
+ meta: "8学期 · 大三专业分流 · 多结局",
+ status: "beta",
+ cssClass: "ss-card-simulator",
+ engine: "simulator"
+ },
+ {
+ id: "mathbti",
+ name: "数学人格测试",
+ icon: "🧮",
+ desc: "12道场景题,发现你的数学人格类型——你是「梦境探索者」还是「博弈游侠」?",
+ meta: "12题 · 4轴测评 · 12人格",
+ status: "ready",
+ cssClass: "ss-card-mathbti",
+ engine: "mathbti"
+ }
+];
+
+const COMING_SOON = [
+ { name: "数学侦探录", icon: "🔍", desc: "民国上海,用数学破案", color: "#B8A99A" },
+ { name: "星际数探", icon: "🔭", desc: "22世纪太空舰队首席数理官", color: "#9BB8AE" },
+ { name: "算学千年", icon: "📜", desc: "穿越古代,亲历数学史", color: "#ACAEC5" },
+ { name: "数学创业记", icon: "🚀", desc: "硅谷数学PhD辍学创业", color: "#C4B0B8" }
+];
+
+// ---- 角色立绘映射 (P0-2) ----
+const SPRITE_MAP = {
+ "葫芦侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=young%20Chinese%20Song%20dynasty%20scholar%20with%20a%20gourd%20hanging%20at%20his%20waist%2C%20simple%20blue-gray%20robes%2C%20gentle%20expression%2C%20clean%20ink%20wash%20painting%20style%20on%20soft%20background%2C%20morandi%20muted%20colors&image_size=portrait_4_3",
+ "欧阳侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=diligent%20young%20Chinese%20scholar%20reading%20a%20book%20at%20dawn%2C%20warm%20candle%20light%2C%20ink%20wash%20style%2C%20wearing%20simple%20robes%2C%20soft%20morandi%20beige%20and%20blue%20tones&image_size=portrait_4_3",
+ "溜溜侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=mysterious%20young%20Chinese%20female%20warrior%20with%20a%20large%20black%20dog%2C%20veiled%20face%2C%20dark%20grey%20robes%2C%20ink%20wash%20painting%20style%2C%20morandi%20muted%20dark%20tones%2C%20cool%20atmosphere&image_size=portrait_4_3",
+ "孔锝侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=strict%20Chinese%20martial%20arts%20instructor%2C%20tall%20and%20upright%2C%20stern%20but%20kind%20eyes%2C%20ink%20wash%20style%2C%20wearing%20neat%20grey-blue%20robes%2C%20morandi%20muted%20tones&image_size=portrait_4_3",
+ "Eddie侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=young%20Chinese%20scholar%20with%20copper%20coin%20spinning%20on%20finger%2C%20playful%20expression%2C%20ink%20wash%20style%2C%20soft%20morandi%20tones&image_size=portrait_4_3",
+ "厚朴侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=elegant%20Chinese%20theater%20actor%2C%20dramatic%20pose%2C%20traditional%20costume%2C%20ink%20wash%20style%2C%20morandi%20muted%20warm%20tones&image_size=portrait_4_3",
+ "星侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=gentle%20Chinese%20female%20scholar%20with%20warm%20smile%2C%20ink%20wash%20style%2C%20flowing%20robes%2C%20morandi%20soft%20pink%20and%20grey%20tones&image_size=portrait_4_3",
+ "甲亢侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=passionate%20young%20Chinese%20debater%2C%20animated%20gestures%2C%20ink%20wash%20style%2C%20morandi%20muted%20tones%2C%20intense%20eyes&image_size=portrait_4_3"
+};
+
+// ---- 场景背景映射 (P0-2) ----
+const BG_MAP = {
+ "紫金山": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=ancient%20Chinese%20academy%20on%20misty%20Purple%20Mountain%2C%20traditional%20architecture%20with%20courtyards%2C%20morning%20fog%2C%20ink%20wash%20painting%20style%2C%20morandi%20muted%20greens%20and%20greys&image_size=landscape_4_3",
+ "石室": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=underground%20stone%20chamber%20turned%20study%20room%2C%20candle%20lit%2C%20wooden%20shelves%20with%20scrolls%2C%20murals%20on%20rock%20walls%2C%20warm%20atmosphere%2C%20ink%20wash%20style%2C%20morandi%20warm%20greys&image_size=landscape_4_3",
+ "姑苏": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=Suzhou%20ancient%20canal%20town%20in%20spring%2C%20stone%20bridges%2C%20willow%20trees%2C%20traditional%20white%20wall%20buildings%2C%20gentle%20rain%2C%20ink%20wash%20painting%20style%2C%20morandi%20muted%20blue-grey%20tones&image_size=landscape_4_3",
+ "京城": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=ancient%20Chinese%20capital%20city%2C%20grand%20government%20hall%2C%20solemn%20atmosphere%2C%20ink%20wash%20style%2C%20morandi%20muted%20grey%20tones&image_size=landscape_4_3"
+};
+
+// 章节 → 默认背景映射
+const CHAPTER_BG = {
+ 1: "紫金山",
+ 2: "紫金山",
+ 3: "紫金山",
+ 4: "石室",
+ 5: "紫金山",
+ 6: "石室",
+ 7: "石室",
+ 8: "京城"
+};
+
+// ---- 页面初始化 ----
+document.addEventListener("DOMContentLoaded", async function () {
+ await loadStoryData();
+ await loadUserFavorites();
+ loadVNSave();
+ switchModule("发现");
+});
+
+// ============================================================
+// 模块切换
+// ============================================================
+function switchModule(module) {
+ currentModule = module;
+ currentDiscipline = "all";
+ currentSubTab = "recommend";
+
+ var colors = MODULE_COLORS[module] || MODULE_COLORS["发现"];
+ document.documentElement.style.setProperty("--accent", colors.color);
+ document.documentElement.style.setProperty("--accent-light", colors.lightBg);
+ document.documentElement.style.setProperty("--accent-gradient", colors.gradient);
+
+ // 顶部品牌栏头像同步
+ var brandIcon = document.querySelector(".brand-icon-sm");
+ if (brandIcon) { brandIcon.textContent = "葫"; brandIcon.style.background = userAvatarColor; }
+
+ // TabBar 高亮
+ document.querySelectorAll(".tab-btn").forEach(function (btn) {
+ btn.classList.toggle("active", btn.dataset.module === module);
+ });
+
+ // 先隐藏所有特殊面板
+ hideAllPanels();
+
+ if (module === "发现") {
+ show("searchHeader");
+ show("subNav");
+ show("videoList");
+ // 恢复子导航默认选中"推荐"
+ document.querySelectorAll(".sub-nav-btn").forEach(function (btn) {
+ btn.classList.toggle("active", btn.dataset.tab === "recommend");
+ });
+ currentSubTab = "recommend";
+ loadVideos("数人");
+ return;
+ } else if (module === "知识") {
+ show("sectionTitleBar");
+ show("shushuoHero");
+ show("shushuoTopics");
+ show("shushuoFeed");
+ document.getElementById("contentTitle").textContent = colors.name;
+ loadKnowledgeCards();
+ return;
+ } else if (module === "数学人生") {
+ show("scriptSquare");
+ renderScriptSquare();
+ return;
+ } else if (module === "我的") {
+ show("sectionTitleBar");
+ show("profilePage");
+ document.getElementById("contentTitle").textContent = colors.name;
+ loadProfileData();
+ return;
+ }
+}
+
+function hideAllPanels() {
+ hide("searchHeader"); hide("subNav"); hide("sectionTitleBar"); hide("lifeSubNav");
+ hide("moduleBanner"); hide("shushuoHero"); hide("shushuoTopics");
+ hide("shushuoFeed"); hide("storyContainer"); hide("simulatorContainer");
+ hide("disciplineGridContainer"); hide("disciplineActiveHeader");
+ hide("disciplineGrid"); hide("categoryFilter");
+ hide("profilePage"); hide("scriptSquare");
+ hide("loadingArea"); hide("videoList");
+}
+
+function show(id) { var el = document.getElementById(id); if (el) el.style.display = (id === "videoList" ? "grid" : id === "subNav" ? "flex" : "block"); }
+function hide(id) { var el = document.getElementById(id); if (el) el.style.display = "none"; }
+
+function updateBanner(module) {
+ var info = MODULE_BANNERS[module] || MODULE_BANNERS["发现"];
+ var el = document.getElementById("moduleBanner");
+ if (!el) return;
+ el.style.display = "flex";
+ el.className = "module-banner " + (MODULE_CSS_CLASS[module] || "shuren");
+ el.innerHTML = '' + info.icon + ' ' + info.title + '
' + info.sub + '
';
+}
+
+// ============================================================
+// 数人:搜索、子导航、学科网格
+// ============================================================
+function handleSearch() {
+ var q = document.getElementById("videoSearch").value.toLowerCase().trim();
+ if (!q) { renderVideoList(allVideos); return; }
+ renderVideoList(allVideos.filter(function (v) {
+ return (v.title && v.title.toLowerCase().indexOf(q) >= 0) ||
+ (v.description && v.description.toLowerCase().indexOf(q) >= 0) ||
+ (v.discipline && v.discipline.toLowerCase().indexOf(q) >= 0) ||
+ (v.author && v.author.toLowerCase().indexOf(q) >= 0);
+ }));
+}
+
+function switchSubTab(tab) {
+ currentSubTab = tab;
+ document.querySelectorAll(".sub-nav-btn").forEach(function (btn) { btn.classList.toggle("active", btn.dataset.tab === tab); });
+
+ // 先把所有内容区重置
+ var gridCtn = document.getElementById("disciplineGridContainer");
+ var videoList = document.getElementById("videoList");
+ var activeHdr = document.getElementById("disciplineActiveHeader");
+ var catFilter = document.getElementById("categoryFilter");
+
+ if (gridCtn) gridCtn.style.display = "none";
+ if (activeHdr) activeHdr.style.display = "none";
+ if (catFilter) catFilter.style.display = "none";
+
+ if (tab === "discipline") {
+ // 专业分区:显示图标网格
+ if (videoList) videoList.style.display = "none";
+ renderDisciplineGrid();
+ } else if (tab === "hot") {
+ if (videoList) videoList.style.display = "grid";
+ var sorted = [].concat(allVideos).sort(function (a, b) { return (hashStr(b.title) % 1000) - (hashStr(a.title) % 1000); });
+ renderVideoList(sorted);
+ } else {
+ // 推荐
+ if (videoList) videoList.style.display = "grid";
+ renderVideoList(allVideos);
+ }
+}
+
+function renderDisciplineGrid() {
+ var gridCtn = document.getElementById("disciplineGridContainer");
+ var grid = document.getElementById("disciplineGrid");
+ var videoList = document.getElementById("videoList");
+
+ if (videoList) videoList.style.display = "none";
+
+ var keys = Object.keys(DISCIPLINE_ICONS).filter(function (d) { return d !== "通用"; });
+
+ grid.innerHTML = keys.map(function (d) {
+ var info = DISCIPLINE_ICONS[d];
+ return ''
+ + '
' + (info.symbol || info.icon) + '
'
+ + '
' + esc(d) + '
';
+ }).join("");
+
+ // 直接设置 display,绕过 show/hide
+ gridCtn.style.display = "block";
+ grid.style.display = "grid";
+}
+
+function selectDiscipline(d) {
+ document.getElementById("disciplineGridContainer").style.display = "none";
+ document.getElementById("categoryFilter").style.display = "none";
+ document.getElementById("videoList").style.display = "grid";
+ document.getElementById("disciplineActiveHeader").style.display = "flex";
+ document.getElementById("activeDisciplineName").textContent = d;
+ renderVideoList(allVideos.filter(function (v) { return v.discipline === d; }));
+}
+
+function backToDisciplineGrid() {
+ document.getElementById("disciplineActiveHeader").style.display = "none";
+ document.getElementById("categoryFilter").style.display = "none";
+ renderDisciplineGrid();
+}
+
+// ============================================================
+// 数说:发现Feed(动态版)
+// ============================================================
+async function loadKnowledgeCards(topic) {
+ show("loadingArea");
+ hide("shushuoFeed");
+ var url = "/api/knowledge";
+ if (topic) url += "?topic=" + encodeURIComponent(topic);
+ try {
+ var resp = await fetch(url);
+ var result = await resp.json();
+ hide("loadingArea");
+ if (result.code === 0) {
+ allKnowledgeCards = result.data || [];
+ var topics = result.topics || [];
+ renderShushuoTopics(topics);
+ renderShushuoFeed(allKnowledgeCards);
+ }
+ } catch (e) {
+ hide("loadingArea");
+ }
+}
+
+function renderShushuoTopics(topics) {
+ var scrollEl = document.getElementById("topicsScroll");
+ scrollEl.innerHTML = ('全部 ') +
+ topics.map(function (t) {
+ return '' + esc(t) + ' ';
+ }).join("");
+}
+
+function renderShushuoFeed(cards) {
+ show("shushuoFeed");
+ updateShushuoHero();
+ var feed = document.getElementById("shushuoFeed");
+ feed.innerHTML = cards.map(function (c) {
+ var isFav = userFavorites.knowledge[c.id] || false;
+ return ''
+ + '
'
+ + '
' + (c.icon || "📖") + '
'
+ + '
' + esc(c.question || "") + '
'
+ + '
' + (isFav ? '★' : '☆') + ' '
+ + '
'
+ + '
' + esc(c.title) + '
'
+ + '
' + esc(c.description) + '
'
+ + '
';
+ }).join("");
+}
+
+function filterShushuoByTopic(topic) {
+ document.querySelectorAll(".topic-pill").forEach(function (p) {
+ var is = p.textContent.trim() === (topic || "全部");
+ p.classList.toggle("active", is);
+ });
+ if (topic) {
+ loadKnowledgeCards(topic);
+ } else {
+ loadKnowledgeCards();
+ }
+ document.querySelector(".content-area").scrollTo({ top: 0, behavior: "smooth" });
+}
+
+function openKnowledgeDetail(cardId) {
+ var card = allKnowledgeCards.find(function (c) { return c.id === cardId; });
+ if (!card) return;
+ setAIContext("knowledge", card.title);
+ recordHistory("knowledge", card.id, card.title, card.icon || "📖");
+ // 在聊天中询问
+ openChat();
+ quickAsk(card.title + "是什么?能给我详细讲讲吗?");
+}
+
+async function toggleFavorite(type, targetId, title, cover) {
+ var isFav = (userFavorites[type] || {})[targetId];
+ if (isFav) {
+ // 取消收藏
+ try {
+ await fetch("/api/favorites", {
+ method: "DELETE",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ user_id: userId, target_type: type, target_id: targetId })
+ });
+ if (userFavorites[type]) delete userFavorites[type][targetId];
+ showToast("已取消收藏");
+ } catch (e) { /* ignore */ }
+ } else {
+ // 添加收藏
+ try {
+ await fetch("/api/favorites", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ user_id: userId, target_type: type, target_id: targetId, title: title, cover: cover })
+ });
+ if (!userFavorites[type]) userFavorites[type] = {};
+ userFavorites[type][targetId] = true;
+ showToast("已加入收藏");
+ } catch (e) { /* ignore */ }
+ }
+ // 刷新当前视图
+ if (currentModule === "知识" && type === "knowledge") {
+ renderShushuoFeed(allKnowledgeCards);
+ } else if (currentModule === "发现" && type === "video") {
+ renderVideoList(allVideos);
+ }
+}
+
+async function loadUserFavorites() {
+ try {
+ var resp = await fetch("/api/favorites?user_id=" + userId);
+ var result = await resp.json();
+ if (result.code === 0) {
+ userFavorites = { video: {}, knowledge: {} };
+ (result.data || []).forEach(function (f) {
+ if (!userFavorites[f.target_type]) userFavorites[f.target_type] = {};
+ userFavorites[f.target_type][f.target_id] = true;
+ });
+ }
+ } catch (e) { userFavorites = { video: {}, knowledge: {} }; }
+}
+
+async function recordHistory(type, targetId, title, cover) {
+ try {
+ await fetch("/api/history", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ user_id: userId, target_type: type, target_id: targetId, title: title, cover: cover })
+ });
+ } catch (e) { /* ignore */ }
+}
+
+function showToast(message) {
+ var toast = document.getElementById("globalToast");
+ if (!toast) {
+ toast = document.createElement("div");
+ toast.id = "globalToast";
+ toast.style.cssText = "position:fixed;bottom:100px;left:50%;transform:translateX(-50%);background:rgba(0,0,0,0.78);color:#fff;padding:8px 20px;border-radius:20px;font-size:13px;z-index:9999;pointer-events:none;transition:opacity 0.3s;opacity:0;";
+ document.body.appendChild(toast);
+ }
+ toast.textContent = message;
+ toast.style.opacity = "1";
+ clearTimeout(toast._timer);
+ toast._timer = setTimeout(function () { toast.style.opacity = "0"; }, 1500);
+}
+
+// ============================================================
+// 个人中心
+// ============================================================
+async function loadProfileData() {
+ try {
+ var [statsResp, profileResp] = await Promise.all([
+ fetch("/api/user/stats?user_id=" + userId),
+ fetch("/api/user/profile?user_id=" + userId)
+ ]);
+ var stats = (await statsResp.json()).data || { watch_count: 0, favorite_count: 0 };
+ var profile = (await profileResp.json()).data || {};
+
+ // 更新统计数据
+ var statNums = document.querySelectorAll(".profile-stat-num");
+ if (statNums.length >= 3) {
+ statNums[0].textContent = stats.watch_count || 0;
+ statNums[1].textContent = stats.favorite_count || 0;
+ statNums[2].textContent = "2"; // 剧情存档数保持固定
+ }
+ // 更新头像和昵称
+ var avatar = document.getElementById("profileAvatar");
+ if (avatar) {
+ avatar.textContent = (profile.avatar_emoji || userAvatar);
+ avatar.style.background = (profile.avatar_color || userAvatarColor);
+ userAvatar = profile.avatar_emoji || userAvatar;
+ userAvatarColor = profile.avatar_color || userAvatarColor;
+ }
+
+ var nameEl = document.getElementById("profileName");
+ if (nameEl) nameEl.textContent = (profile.nickname || "游客用户");
+
+ var idEl = document.getElementById("profileId");
+ if (idEl) idEl.textContent = "ID: " + userId.substring(0, 14);
+ } catch (e) { /* ignore */ }
+}
+
+function showFavorites() {
+ loadFavoritesList();
+}
+
+async function loadFavoritesList() {
+ showProfileListPanel("我的收藏");
+ var panel = document.getElementById("profileListContent");
+ panel.innerHTML = '';
+ try {
+ var resp = await fetch("/api/favorites?user_id=" + userId);
+ var result = await resp.json();
+ var items = result.data || [];
+ if (items.length === 0) {
+ panel.innerHTML = '';
+ } else {
+ panel.innerHTML = items.map(function (f) {
+ return ''
+ + '
' + (f.cover || "◎") + '
'
+ + '
' + esc(f.title) + '
' + f.target_type + ' · ' + (f.created_at || "").substring(0, 10) + '
'
+ + '
✕ '
+ + '
';
+ }).join("");
+ }
+ } catch (e) {
+ panel.innerHTML = '';
+ }
+}
+
+function showHistory() {
+ loadHistoryList();
+}
+
+async function loadHistoryList() {
+ showProfileListPanel("观看记录");
+ var panel = document.getElementById("profileListContent");
+ panel.innerHTML = '';
+ try {
+ var resp = await fetch("/api/history?user_id=" + userId + "&limit=30");
+ var result = await resp.json();
+ var items = result.data || [];
+ if (items.length === 0) {
+ panel.innerHTML = '';
+ } else {
+ panel.innerHTML = items.map(function (h) {
+ return ''
+ + '
' + (h.cover || "◎") + '
'
+ + '
' + esc(h.title) + '
' + h.target_type + ' · ' + (h.watched_at || "").substring(0, 16) + '
'
+ + '
';
+ }).join("");
+ }
+ } catch (e) {
+ panel.innerHTML = '';
+ }
+}
+
+function showProfileListPanel(title) {
+ var panel = document.getElementById("profileListPanel");
+ var titleEl = document.getElementById("profileListTitle");
+ if (titleEl) titleEl.textContent = title;
+ if (panel) panel.style.display = "block";
+ // 隐藏个人中心其他内容
+ var profilePage = document.getElementById("profilePage");
+ if (profilePage) profilePage.style.display = "none";
+}
+
+function hideProfileListPanel() {
+ var panel = document.getElementById("profileListPanel");
+ if (panel) panel.innerHTML = "";
+ if (panel) panel.style.display = "none";
+ var profilePage = document.getElementById("profilePage");
+ if (profilePage) profilePage.style.display = "block";
+ loadProfileData();
+}
+
+async function removeFavorite(type, targetId) {
+ await toggleFavorite(type, targetId, "", "");
+ loadFavoritesList();
+ loadProfileData();
+}
+
+function editNickname() {
+ var name = prompt("输入新昵称:", document.querySelector(".profile-name").textContent);
+ if (name && name.trim()) {
+ var trimmed = name.trim().substring(0, 12);
+ fetch("/api/user/profile", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ user_id: userId, nickname: trimmed })
+ }).then(function () {
+ var el = document.querySelector(".profile-name");
+ if (el) el.textContent = trimmed;
+ showToast("昵称已更新");
+ });
+ }
+}
+function updateShushuoHero() {
+ document.getElementById("heroTitle").textContent = "数学不是公式,是你理解世界的方式";
+ document.getElementById("heroDesc").textContent = "每天一个数学概念,让思维变得更清晰";
+ var el = document.querySelector(".hero-stats");
+ if (el) el.innerHTML = "📖 " + allKnowledgeCards.length + "个概念 ⏱ 阅读约" + (allKnowledgeCards.length * 5) + "分钟 ";
+}
+function adjustColor(hex, amount) {
+ var num = parseInt(hex.replace("#",""), 16);
+ var r = Math.min(255, (num >> 16) + amount);
+ var g = Math.min(255, ((num >> 8) & 0x00FF) + amount);
+ var b = Math.min(255, (num & 0x0000FF) + amount);
+ return "#" + ((r << 16) | (g << 8) | b).toString(16).padStart(6, "0");
+}
+
+// ============================================================
+// 视频加载与渲染
+// ============================================================
+async function loadVideos(module, discipline) {
+ show("loadingArea");
+ document.getElementById("videoList").innerHTML = "";
+ var url = "/api/videos?module=" + encodeURIComponent(module);
+ if (discipline && discipline !== "all") url += "&discipline=" + encodeURIComponent(discipline);
+ try {
+ var resp = await fetch(url);
+ var result = await resp.json();
+ hide("loadingArea");
+ allVideos = result.data || [];
+ if (allVideos.length > 0) {
+ renderVideoList(allVideos);
+ } else {
+ document.getElementById("videoList").innerHTML = '';
+ }
+ } catch (e) {
+ hide("loadingArea");
+ document.getElementById("videoList").innerHTML = '';
+ }
+}
+
+function renderVideoList(videos) {
+ var list = document.getElementById("videoList");
+ if (!videos || videos.length === 0) { list.innerHTML = ''; return; }
+ list.style.display = "grid";
+ list.innerHTML = videos.map(function (v) {
+ var avatarColor = AVATAR_COLORS[hashStr(v.author || "") % AVATAR_COLORS.length];
+ var initial = (v.author || "?").replace(/学院|·|同学|系/g, "").slice(0, 1);
+ var dur = v.duration_min || 5;
+ var icon = v.discipline_icon && v.discipline_icon !== "·" ? v.discipline_icon : "◎";
+ var coverBg = "hsl(" + (hashStr(v.discipline || "") % 360) + ", 40%, 90%)";
+ var isFav = userFavorites.video[v.id] || false;
+ return ''
+ + '
' + icon + '
' + dur + ':00
'
+ + '
' + (isFav ? '★' : '☆') + ' '
+ + '
'
+ + '
' + esc(v.title) + '
'
+ + '
' + initial + ' '
+ + '
' + esc(v.author) + ' '
+ + '
👁️ ' + ((hashStr(v.title) % 1000) + 100) + '
';
+ }).join("");
+}
+
+function openVideo(id, title, desc, url, author, cat) {
+ currentVideoUrl = url;
+ currentVideoTitle = title;
+ document.getElementById("videoModalTitle").textContent = title;
+ document.getElementById("videoModalDesc").textContent = desc;
+ document.getElementById("videoModalAuthor").innerHTML = author;
+ document.getElementById("videoModalCat").innerHTML = cat || "通用";
+ document.getElementById("videoLinkBtn").style.display = url ? "block" : "none";
+ document.getElementById("videoPlaceholderText").textContent = url ? "点击下方按钮查看完整课程视频" : "视频链接待补充";
+ // 收藏状态
+ var favBtn = document.getElementById("videoModalFav");
+ var vid = parseInt(id);
+ favBtn.textContent = (userFavorites.video[vid]) ? "★ 已收藏" : "☆ 收藏";
+ favBtn.dataset.vid = vid;
+ document.getElementById("videoModal").classList.add("show");
+ // AI上下文
+ setAIContext("video", title);
+ // 记录观看历史
+ recordHistory("video", vid, title, cat || "◎");
+}
+function closeVideo() { document.getElementById("videoModal").classList.remove("show"); }
+function openVideoLink() { if (currentVideoUrl) window.open(currentVideoUrl, "_blank"); }
+function toggleVideoFav() {
+ var btn = document.getElementById("videoModalFav");
+ var vid = parseInt(btn.dataset.vid);
+ var title = currentVideoTitle;
+ toggleFavorite("video", vid, title, "◎");
+ btn.textContent = (userFavorites.video[vid]) ? "★ 已收藏" : "☆ 收藏";
+}
+
+// ============================================================
+// 视觉小说引擎 (数思) — 唯一引擎
+// ============================================================
+async function loadStoryData() {
+ try {
+ var resp = await fetch("/api/story");
+ var result = await resp.json();
+ if (result.code === 0) storyData = result.data;
+ } catch (e) { console.error("故事数据加载失败:", e); }
+}
+
+function loadVNSave() {
+ try { var s = localStorage.getItem("mathAppVNSave"); if (s) vnState = JSON.parse(s); } catch (e) {}
+}
+
+function saveVNState() {
+ localStorage.setItem("mathAppVNSave", JSON.stringify(vnState));
+ alert("进度已存档!");
+}
+
+function resetVNSave() {
+ var startNode = (storyData && storyData.start_node) || "ch1_start";
+ vnState = { currentNode: startNode, stats: { shuli: 10, xiayi: 10, xinzhi: 10 }, favorability: { "欧阳侠": 0, "Eddie侠": 0, "厚朴侠": 0, "三力思侠": 0, "溜溜侠": 0, "孔锝侠": 0, "星侠": 0, "甲亢侠": 0 } };
+ localStorage.removeItem("mathAppVNSave");
+ navigateVN(startNode);
+}
+
+function applyVNStats(effects) {
+ if (!effects) return;
+ if (effects.shuli) vnState.stats.shuli = (vnState.stats.shuli || 10) + effects.shuli;
+ if (effects.xiayi) vnState.stats.xiayi = (vnState.stats.xiayi || 10) + effects.xiayi;
+ if (effects.xinzhi) vnState.stats.xinzhi = (vnState.stats.xinzhi || 10) + effects.xinzhi;
+ // 兼容旧属性名
+ if (effects.intelligence !== undefined) vnState.stats.shuli = (vnState.stats.shuli || 10) + effects.intelligence;
+ if (effects.charm !== undefined) vnState.stats.xiayi = (vnState.stats.xiayi || 10) + effects.charm;
+ if (effects.stress !== undefined) vnState.stats.xinzhi = (vnState.stats.xinzhi || 10) - effects.stress;
+ // 好感度
+ if (effects.favorability) {
+ if (!vnState.favorability) vnState.favorability = {};
+ for (var char in effects.favorability) {
+ vnState.favorability[char] = (vnState.favorability[char] || 0) + effects.favorability[char];
+ }
+ }
+}
+
+function startStoryEngine() {
+ setAIContext("story", "葫芦侠行");
+ var startNode = (storyData && storyData.start_node) || "ch1_start";
+ // 如果存档中有旧节点但新JSON里不存在,回退到起始节点
+ var savedNode = vnState.currentNode;
+ if (savedNode && storyData && storyData.nodes && storyData.nodes[savedNode]) {
+ navigateVN(savedNode);
+ } else {
+ vnState.currentNode = startNode;
+ navigateVN(startNode);
+ }
+}
+
+function navigateVN(nodeId) {
+ if (!storyData || !storyData.nodes || !storyData.nodes[nodeId]) {
+ document.getElementById("storyContainer").innerHTML = '';
+ return;
+ }
+ var node = storyData.nodes[nodeId];
+ vnState.currentNode = nodeId;
+
+ var container = document.getElementById("storyContainer");
+ if (!container) return;
+ container.style.display = "block";
+ container.scrollIntoView({ behavior: "smooth" });
+
+ // 构建完整 HTML
+ var ch = detectChapter(nodeId);
+ var chNames = ["","初入江湖","西部教习","数理论道","脑学初创","报国之路","暗流涌动","陋室修炼","真相之门"];
+ var chName = chNames[ch] || "";
+
+ var html = '';
+ html += '';
+
+ // 场景背景 + 立绘
+ var bgKey = CHAPTER_BG[ch] || "紫金山";
+ var bgUrl = BG_MAP[bgKey] || "";
+ if (node.background) bgUrl = BG_MAP[node.background] || node.background || bgUrl;
+
+ // 立绘:优先 node.sprite,其次从 SPRITE_MAP 查找角色名
+ var spriteUrl = "";
+ var characterName = node.character || "";
+ if (node.sprite) {
+ spriteUrl = node.sprite;
+ } else if (characterName) {
+ // 匹配 SPRITE_MAP 中的角色(支持多角色用 & 分隔)
+ var names = characterName.split("&");
+ for (var ni = 0; ni < names.length; ni++) {
+ var n = names[ni].trim();
+ if (SPRITE_MAP[n]) { spriteUrl = SPRITE_MAP[n]; break; }
+ }
+ }
+
+ html += '';
+ if (bgUrl) html += '
';
+ if (spriteUrl) html += '
';
+ html += '
';
+ if (characterName) html += '
' + esc(characterName) + '
';
+ html += '
';
+ html += '
';
+
+ html += '
';
+
+ container.innerHTML = html;
+
+ // 打字机效果
+ var sceneText = document.getElementById("sceneText");
+ var text = node.scene || "";
+ var i = 0;
+ function typeNext() {
+ if (i < text.length) {
+ sceneText.textContent += text.charAt(i);
+ i++;
+ setTimeout(typeNext, 25);
+ } else {
+ buildChoiceButtons(node.choices || []);
+ }
+ }
+ sceneText.textContent = "";
+ typeNext();
+}
+
+function buildChoiceButtons(choices) {
+ var area = document.getElementById("choicesArea");
+ if (!area) return;
+ area.innerHTML = "";
+
+ choices.forEach(function (c, idx) {
+ var btn = document.createElement("button");
+ btn.className = "choice-btn";
+ btn.style.animationDelay = (idx * 0.1) + "s";
+
+ // 效果文本
+ var effectsHTML = "";
+ if (c.effects) {
+ var parts = [];
+ if (c.effects.shuli) parts.push("数理根基" + (c.effects.shuli > 0 ? "+" : "") + c.effects.shuli);
+ if (c.effects.xiayi) parts.push("侠义声望" + (c.effects.xiayi > 0 ? "+" : "") + c.effects.xiayi);
+ if (c.effects.xinzhi) parts.push("心志韧性" + (c.effects.xinzhi > 0 ? "+" : "") + c.effects.xinzhi);
+ // 兼容旧属性
+ if (c.effects.intelligence) parts.push("数理根基" + (c.effects.intelligence > 0 ? "+" : "") + c.effects.intelligence);
+ if (c.effects.charm) parts.push("侠义声望" + (c.effects.charm > 0 ? "+" : "") + c.effects.charm);
+ if (c.effects.stress) parts.push("心志韧性" + (c.effects.stress > 0 ? "+" : "") + c.effects.stress);
+ if (c.effects.favorability) {
+ for (var char in c.effects.favorability) {
+ var val = c.effects.favorability[char];
+ parts.push(char + "好感" + (val > 0 ? "+" : "") + val);
+ }
+ }
+ if (parts.length > 0) effectsHTML = '[' + parts.join(" | ") + '] ';
+ }
+
+ btn.innerHTML = '' + esc(c.text) + ' ' + effectsHTML;
+
+ btn.onclick = (function (nextNode, effects) {
+ return function () {
+ applyVNStats(effects);
+ navigateVN(nextNode);
+ };
+ })(c.next, c.effects);
+
+ area.appendChild(btn);
+ });
+}
+
+// ============================================================
+// AI 聊天
+// ============================================================
+function openChat() { document.getElementById("chatOverlay").classList.add("show"); document.getElementById("chatMessages").scrollTop = document.getElementById("chatMessages").scrollHeight; }
+function closeChat() { document.getElementById("chatOverlay").classList.remove("show"); }
+function quickAsk(q) { document.getElementById("chatInput").value = q; sendMessage(); }
+
+async function sendMessage() {
+ var input = document.getElementById("chatInput");
+ var message = input.value.trim();
+ if (!message) return;
+ appendChat("user", message);
+ input.value = "";
+ var typingId = appendTyping();
+ try {
+ var resp = await fetch("/api/chat", {
+ method: "POST", headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ message: message, context: currentContext })
+ });
+ var result = await resp.json();
+ removeTyping(typingId);
+ appendChat("assistant", result.code === 0 ? result.reply : "抱歉,AI 助教暂时无法回复。");
+ } catch (e) {
+ removeTyping(typingId);
+ appendChat("assistant", "网络异常,请稍后重试。");
+ }
+}
+
+function setAIContext(type, title) {
+ currentContext = { type: type, title: title || "" };
+ var hint = document.getElementById("chatContextHint");
+ if (!hint) {
+ hint = document.createElement("div");
+ hint.id = "chatContextHint";
+ hint.style.cssText = "padding:6px 12px;font-size:11px;color:var(--accent);background:var(--accent-light);text-align:center;";
+ var chatPanel = document.querySelector(".chat-panel");
+ var header = document.querySelector(".chat-header");
+ if (header && chatPanel) chatPanel.insertBefore(hint, header.nextSibling);
+ }
+ hint.textContent = currentContext.title ? "📍 当前:" + currentContext.title : "";
+ hint.style.display = currentContext.title ? "block" : "none";
+}
+function appendChat(role, text) {
+ var container = document.getElementById("chatMessages");
+ var div = document.createElement("div");
+ div.className = "chat-msg " + role;
+ div.innerHTML = '' + esc(text) + "
";
+ container.appendChild(div);
+ container.scrollTop = container.scrollHeight;
+}
+function appendTyping() {
+ var container = document.getElementById("chatMessages");
+ var div = document.createElement("div");
+ div.className = "chat-msg assistant typing-msg";
+ div.innerHTML = '';
+ container.appendChild(div);
+ container.scrollTop = container.scrollHeight;
+ return div;
+}
+function removeTyping(el) { if (el && el.parentNode) el.remove(); }
+
+// ============================================================
+// 数学人生子标签切换
+// ============================================================
+function switchLifeTab(tab) {
+ currentLifeTab = tab;
+ document.querySelectorAll(".life-sub-btn").forEach(function (b) { b.classList.toggle("active", b.dataset.life === tab); });
+ if (tab === "simulator") {
+ hide("storyContainer");
+ show("simulatorContainer");
+ setAIContext("simulator", "数学专业模拟器");
+ startSimulator();
+ } else {
+ hide("simulatorContainer");
+ show("storyContainer");
+ setAIContext("story", "葫芦侠行");
+ startStoryEngine();
+ }
+}
+
+// ============================================================
+// 数学专业模拟器引擎
+// ============================================================
+async function loadSimulatorData() {
+ try {
+ var resp = await fetch("/api/simulator");
+ var result = await resp.json();
+ if (result.code === 0) simulatorData = result.data;
+ } catch (e) { console.error("模拟器数据加载失败:", e); }
+}
+
+async function loadSimSave() {
+ try {
+ var resp = await fetch("/api/simulator/save?user_id=" + userId);
+ var result = await resp.json();
+ if (result.code === 0 && result.data) {
+ simState = result.data;
+ }
+ } catch (e) {}
+}
+
+function saveSimState() {
+ simState.history.push(simState.currentNode);
+ fetch("/api/simulator/save", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ user_id: userId, state: simState })
+ }).catch(function () {});
+}
+
+function resetSimulator() {
+ simState = { currentNode: (simulatorData && simulatorData.start_node) || "y1_welcome", stats: { gpa: 10, interest: 10, skill: 10 }, history: [] };
+ saveSimState();
+ navigateSimulator(simState.currentNode);
+}
+
+async function startSimulator() {
+ if (!simulatorData) await loadSimulatorData();
+ await loadSimSave();
+ if (!simState.currentNode || !simulatorData || !simulatorData.nodes || !simulatorData.nodes[simState.currentNode]) {
+ resetSimulator();
+ } else {
+ navigateSimulator(simState.currentNode);
+ }
+}
+
+function navigateSimulator(nodeId) {
+ if (!simulatorData || !simulatorData.nodes || !simulatorData.nodes[nodeId]) {
+ document.getElementById("simulatorContainer").innerHTML = '
剧情加载失败
';
+ return;
+ }
+ var node = simulatorData.nodes[nodeId];
+ simState.currentNode = nodeId;
+ saveSimState();
+
+ var container = document.getElementById("simulatorContainer");
+ if (!container) return;
+ container.style.display = "block";
+
+ // 如果遇到结局节点
+ if (node.end) {
+ showEnding(node);
+ return;
+ }
+
+ container.scrollIntoView({ behavior: "smooth" });
+ var ch = (node.year || 1);
+ var stage = node.stage || "";
+
+ var html = '';
+ html += '';
+
+ html += '';
+ if (node.character) html += '
' + esc(node.character) + '
';
+ html += '
';
+ html += '
';
+
+ html += '
';
+
+ container.innerHTML = html;
+
+ // 打字机效果
+ var textEl = document.getElementById("simText");
+ var text = node.scene || "";
+ var i = 0;
+ function typeNext() {
+ if (i < text.length) {
+ textEl.textContent += text.charAt(i);
+ i++;
+ setTimeout(typeNext, 20);
+ } else {
+ buildSimChoices(node.choices || []);
+ }
+ }
+ textEl.textContent = "";
+ typeNext();
+}
+
+function buildSimChoices(choices) {
+ var area = document.getElementById("simChoicesArea");
+ if (!area) return;
+ area.innerHTML = "";
+
+ choices.forEach(function (c, idx) {
+ var btn = document.createElement("button");
+ btn.className = "sim-choice-btn";
+ btn.style.animationDelay = (idx * 0.1) + "s";
+
+ var effectsHTML = "";
+ if (c.effects) {
+ var parts = [];
+ if (c.effects.gpa) parts.push((c.effects.gpa > 0 ? "+" : "") + c.effects.gpa + " 绩点");
+ if (c.effects.interest) parts.push((c.effects.interest > 0 ? "+" : "") + c.effects.interest + " 兴趣");
+ if (c.effects.skill) parts.push((c.effects.skill > 0 ? "+" : "") + c.effects.skill + " 技能");
+ if (parts.length > 0) effectsHTML = '[' + parts.join(" | ") + '] ';
+ }
+ if (c.hint) effectsHTML += '💡 ' + esc(c.hint) + ' ';
+
+ btn.innerHTML = '' + esc(c.text) + ' ' + effectsHTML;
+
+ btn.onclick = (function (nextNode, effects) {
+ return function () {
+ if (effects) applySimStats(effects);
+ navigateSimulator(nextNode);
+ };
+ })(c.next, c.effects);
+
+ area.appendChild(btn);
+ });
+}
+
+function applySimStats(effects) {
+ if (!effects) return;
+ if (effects.gpa) simState.stats.gpa = Math.max(0, Math.min(30, (simState.stats.gpa || 10) + effects.gpa));
+ if (effects.interest) simState.stats.interest = Math.max(0, Math.min(30, (simState.stats.interest || 10) + effects.interest));
+ if (effects.skill) simState.stats.skill = Math.max(0, Math.min(30, (simState.stats.skill || 10) + effects.skill));
+}
+
+function showEnding(node) {
+ document.getElementById("endingOverlay").classList.add("show");
+ var badges = { pure_math: "🔮", ai: "🤖", finance: "💰", research: "🔬", startup: "🚀", teacher: "📚", engineer: "⚙️", security: "🔒" };
+ document.getElementById("endingBadge").textContent = badges[node.ending_type] || "🎓";
+ document.getElementById("endingType").textContent = node.character || "结局";
+ document.getElementById("endingScene").textContent = node.scene || "";
+ document.getElementById("endingTitle").textContent = node.stage || "";
+
+ // 记录解锁结局
+ if (node.ending_type) unlockEnding(node.ending_type);
+
+ // 统计数据
+ var statsHtml = '';
+ Object.keys(simState.stats).forEach(function (k) {
+ statsHtml += '
' + simState.stats[k] + '
' + SIM_STAT_LABELS[k] + '
';
+ });
+ statsHtml += '
';
+ document.getElementById("endingStats").innerHTML = statsHtml;
+}
+
+function closeEnding() { document.getElementById("endingOverlay").classList.remove("show"); }
+
+// ============================================================
+// 头像选择器
+// ============================================================
+var PICKER_EMOJIS = ["π","∑","∫","∞","Δ","∂","α","β","γ","λ","μ","σ","Ω","∇","⊥","⊕","⊗","≈","±","÷","√","∏","∃","∀","∈","⊂","∪","∩","ℵ","∮"];
+
+function openAvatarPicker() {
+ document.getElementById("avatarPickerOverlay").classList.add("show");
+ // 渲染emoji网格
+ var grid = document.getElementById("pickerEmojiGrid");
+ grid.innerHTML = PICKER_EMOJIS.map(function (e) {
+ var selected = e === userAvatar ? ' style="border-color:var(--accent);background:var(--accent-light)"' : "";
+ return '' + e + ' ';
+ }).join("");
+ // 渲染颜色行
+ var colors = ["#A0B9D8","#B8D0C9","#DAB8B0","#C5CAD0","#B8A99A","#9BB8AE","#ACAEC5","#C4B0B8"];
+ var colorRow = document.getElementById("pickerColorRow");
+ colorRow.innerHTML = colors.map(function (c) {
+ var selected = c === userAvatarColor ? ' style="box-shadow:0 0 0 4px ' + c + '44"' : "";
+ return ' ';
+ }).join("");
+}
+
+function selectAvatar(emoji) {
+ userAvatar = emoji;
+ localStorage.setItem("mathAppAvatar", emoji);
+ openAvatarPicker(); // refresh grid
+ updateAvatarUI();
+ fetch("/api/user/profile", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ user_id: userId, avatar_emoji: emoji })
+ });
+}
+
+function selectAvatarColor(color) {
+ userAvatarColor = color;
+ localStorage.setItem("mathAppAvatarColor", color);
+ openAvatarPicker();
+ updateAvatarUI();
+ fetch("/api/user/profile", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ user_id: userId, avatar_color: color })
+ });
+}
+
+function updateAvatarUI() {
+ var av = document.getElementById("profileAvatar");
+ if (av) { av.textContent = userAvatar; av.style.background = userAvatarColor; }
+ var brandIcon = document.querySelector(".brand-icon-sm");
+ if (brandIcon) { brandIcon.textContent = "葫"; brandIcon.style.background = userAvatarColor; }
+}
+
+function closeAvatarPicker(e) {
+ if (e && e.target !== document.getElementById("avatarPickerOverlay")) return;
+ document.getElementById("avatarPickerOverlay").classList.remove("show");
+}
+
+// ---- 工具函数 ----
+function esc(text) {
+ if (!text) return "";
+ var div = document.createElement("div");
+ div.textContent = text;
+ return div.innerHTML;
+}
+function hashStr(str) {
+ var hash = 0;
+ for (var i = 0; i < str.length; i++) { hash = ((hash << 5) - hash) + str.charCodeAt(i); hash |= 0; }
+ return Math.abs(hash);
+}
+
+// ============================================================
+// 剧本广场 (Script Square)
+// ============================================================
+function renderScriptSquare() {
+ try {
+ // 渲染精选剧本大卡
+ var heroCards = document.getElementById("ssHeroCards");
+ if (!heroCards) return;
+ heroCards.innerHTML = SCRIPTS.map(function (s) {
+ var statusLabel = s.status === "beta" ? "开发中 · 抢先体验" : "已上线";
+ return ''
+ + '
'
+ + '
'
+ + '
' + s.icon + '
'
+ + '
' + esc(s.name) + '
'
+ + '
' + esc(s.desc) + '
'
+ + '
' + s.meta + ' ' + statusLabel + '
'
+ + '
';
+ }).join("");
+
+ // 渲染即将上线小卡
+ var comingCards = document.getElementById("ssComingCards");
+ if (comingCards) {
+ comingCards.innerHTML = COMING_SOON.map(function (c) {
+ return ''
+ + '
' + c.icon + '
'
+ + '
'
+ + '
' + esc(c.name) + '
'
+ + '
' + esc(c.desc) + '
'
+ + '
敬请期待 '
+ + '
';
+ }).join("");
+ }
+
+ // 检查是否有可继续的存档
+ updateContinueCard();
+ } catch (e) {
+ console.error("剧本广场渲染失败:", e);
+ }
+}
+
+function updateContinueCard() {
+ try {
+ var continueEl = document.getElementById("ssContinue");
+ if (!continueEl) return;
+ var vnSavedNode = vnState.currentNode;
+ var simSavedNode = simState.currentNode;
+
+ // 优先显示葫芦侠行存档,其次模拟器存档
+ if (vnSavedNode && storyData && storyData.nodes && storyData.nodes[vnSavedNode]) {
+ var ch = detectChapter(vnSavedNode);
+ var chNames = ["","初入江湖","西部教习","数理论道","脑学初创","报国之路","暗流涌动","陋室修炼","真相之门"];
+ document.getElementById("ssContinueTitle").textContent = "葫芦侠行 · 第" + ch + "章";
+ document.getElementById("ssContinueSub").textContent = chNames[ch] || "";
+ document.getElementById("ssContinueCard").onclick = function () { enterScript("huluxiaxing"); };
+ continueEl.style.display = "block";
+ } else if (simSavedNode && simulatorData && simulatorData.nodes && simulatorData.nodes[simSavedNode]) {
+ var node = simulatorData.nodes[simSavedNode];
+ document.getElementById("ssContinueTitle").textContent = "数学专业模拟器";
+ document.getElementById("ssContinueSub").textContent = (node.stage || "") + (node.year ? " · 大" + ["","一","二","三","四"][node.year] : "");
+ document.getElementById("ssContinueCard").onclick = function () { enterScript("simulator"); };
+ continueEl.style.display = "block";
+ } else {
+ continueEl.style.display = "none";
+ }
+ } catch (e) {
+ console.error("继续卡片更新失败:", e);
+ }
+}
+
+function enterScript(scriptId) {
+ hide("scriptSquare");
+
+ if (scriptId === "huluxiaxing") {
+ setAIContext("story", "葫芦侠行");
+ hide("simulatorContainer");
+ show("lifeSubNav");
+ show("storyContainer");
+ var btns = document.querySelectorAll(".life-sub-btn");
+ btns.forEach(function (b) {
+ b.classList.toggle("active", b.dataset.life === "story");
+ });
+ startStoryEngine();
+ } else if (scriptId === "simulator") {
+ setAIContext("simulator", "数学专业模拟器");
+ hide("storyContainer");
+ show("lifeSubNav");
+ show("simulatorContainer");
+ var btns = document.querySelectorAll(".life-sub-btn");
+ btns.forEach(function (b) {
+ b.classList.toggle("active", b.dataset.life === "simulator");
+ });
+ startSimulator();
+ } else if (scriptId === "mathbti") {
+ // 打开 MathBTI 独立页面
+ window.location.href = "/mathbti";
+ }
+}
+
+function backToSquare() {
+ hide("lifeSubNav");
+ hide("storyContainer");
+ hide("simulatorContainer");
+ show("scriptSquare");
+ renderScriptSquare();
+ document.querySelector(".content-area").scrollTo({ top: 0, behavior: "smooth" });
+}
+
+// ============================================================
+// 存档系统 (Save System — v8.3)
+// ============================================================
+function getSaveKey(scriptId) { return "mathAppSave_" + scriptId; }
+
+function loadAllSaves(scriptId) {
+ try {
+ var raw = localStorage.getItem(getSaveKey(scriptId));
+ return raw ? JSON.parse(raw) : [];
+ } catch (e) { return []; }
+}
+
+function saveAllSaves(scriptId, saves) {
+ localStorage.setItem(getSaveKey(scriptId), JSON.stringify(saves));
+}
+
+function openSavePanel() {
+ var scriptId = (currentLifeTab === "simulator") ? "simulator" : "huluxiaxing";
+ document.getElementById("savePanelOverlay").classList.add("show");
+ renderSaveSlots(scriptId);
+}
+
+function closeSavePanel() {
+ document.getElementById("savePanelOverlay").classList.remove("show");
+}
+
+function renderSaveSlots(scriptId) {
+ var slots = document.getElementById("saveSlots");
+ var saves = loadAllSaves(scriptId);
+ var MAX_SLOTS = 5;
+
+ var html = "";
+ for (var i = 0; i < MAX_SLOTS; i++) {
+ var s = saves[i];
+ if (s) {
+ // 已使用的槽位
+ var icon = scriptId === "simulator" ? "🎓" : "📖";
+ var name = (scriptId === "simulator") ? "数学专业模拟器" : "葫芦侠行";
+ var meta = s.chapter || "";
+ var date = s.savedAt ? s.savedAt.substring(0, 16).replace("T", " ") : "";
+ html += '';
+ html += '
' + icon + '
';
+ html += '
';
+ html += '
' + esc(name) + '
';
+ html += '
' + esc(meta) + ' ' + date + '
';
+ html += '
';
+ html += '
';
+ html += '💾 覆盖 ';
+ html += '✕ ';
+ html += '
';
+ } else {
+ // 空槽位
+ html += '';
+ html += '+ 空槽位 ' + (i + 1) + ' — 点击保存 ';
+ html += '
';
+ }
+ }
+ slots.innerHTML = html;
+}
+
+function saveToSlot(scriptId, slotIndex) {
+ var saveData = {
+ savedAt: new Date().toISOString(),
+ scriptId: scriptId
+ };
+
+ if (scriptId === "huluxiaxing") {
+ saveData.chapter = detectChapter(vnState.currentNode) ? "第" + detectChapter(vnState.currentNode) + "章" : "";
+ saveData.nodeId = vnState.currentNode;
+ saveData.stats = JSON.parse(JSON.stringify(vnState.stats));
+ saveData.favorability = JSON.parse(JSON.stringify(vnState.favorability || {}));
+ saveData.vnState = JSON.parse(JSON.stringify(vnState));
+ } else {
+ var node = simulatorData && simulatorData.nodes && simulatorData.nodes[simState.currentNode];
+ saveData.chapter = (node ? (node.stage || "大" + node.year) : "");
+ saveData.nodeId = simState.currentNode;
+ saveData.stats = JSON.parse(JSON.stringify(simState.stats));
+ saveData.simState = JSON.parse(JSON.stringify(simState));
+ }
+
+ var saves = loadAllSaves(scriptId);
+ saves[slotIndex] = saveData;
+ saveAllSaves(scriptId, saves);
+ renderSaveSlots(scriptId);
+ showToast("存档成功 ✓");
+}
+
+function overwriteSlot(scriptId, slotIndex) {
+ saveToSlot(scriptId, slotIndex);
+}
+
+function loadFromSlot(scriptId, slotIndex) {
+ var saves = loadAllSaves(scriptId);
+ var saveData = saves[slotIndex];
+ if (!saveData) return;
+
+ if (scriptId === "huluxiaxing") {
+ if (saveData.vnState) {
+ vnState = JSON.parse(JSON.stringify(saveData.vnState));
+ localStorage.setItem("mathAppVNSave", JSON.stringify(vnState));
+ }
+ hide("scriptSquare"); hide("simulatorContainer");
+ show("lifeSubNav"); show("storyContainer");
+ document.querySelectorAll(".life-sub-btn").forEach(function (b) { b.classList.toggle("active", b.dataset.life === "story"); });
+ currentLifeTab = "story";
+ startStoryEngine();
+ } else {
+ if (saveData.simState) {
+ simState = JSON.parse(JSON.stringify(saveData.simState));
+ saveSimState();
+ }
+ hide("scriptSquare"); hide("storyContainer");
+ show("lifeSubNav"); show("simulatorContainer");
+ document.querySelectorAll(".life-sub-btn").forEach(function (b) { b.classList.toggle("active", b.dataset.life === "simulator"); });
+ currentLifeTab = "simulator";
+ startSimulator();
+ }
+
+ closeSavePanel();
+ showToast("读档成功 ✓");
+}
+
+function deleteSlot(scriptId, slotIndex) {
+ var saves = loadAllSaves(scriptId);
+ saves[slotIndex] = null; // 保留空位
+ saveAllSaves(scriptId, saves);
+ renderSaveSlots(scriptId);
+ showToast("已删除存档");
+}
+
+// 兼容旧版存档按钮
+function saveVNState() {
+ var scriptId = "huluxiaxing";
+ // 自动保存到槽位0
+ saveToSlot(scriptId, 0);
+}
+
+// ============================================================
+// 结局画廊 (Ending Gallery — v8.3)
+// ============================================================
+const ALL_ENDINGS = [
+ { id: "truth", name: "真相守望", icon: "🔮", desc: "成为葫芦数学世界首位居民与共创者" },
+ { id: "master", name: "数理宗师", icon: "📚", desc: "葫芦侠成为一代宗师,数堂发扬光大" },
+ { id: "wander", name: "逍遥江湖", icon: "🍃", desc: "远离纷争,在江湖中自由游历" },
+ { id: "sail", name: "出海远航", icon: "⛵", desc: "随欧阳侠远赴新大陆,开创东西方交流" },
+ { id: "hero", name: "悲情英雄", icon: "⚔️", desc: "坚守信念,孤身面对审查与命运" },
+ { id: "fall", name: "堕落之路", icon: "🌑", desc: "在权力与诱惑中迷失自我" },
+ { id: "jail", name: "冤狱", icon: "⛓", desc: "被冤入狱,含恨而终" },
+ { id: "void", name: "虚无", icon: "🌫", desc: "发现真相后精神崩溃,消失于江湖" },
+ { id: "math_phd", name: "数学博士", icon: "🔬", desc: "坚持学术之路,走向研究巅峰" },
+ { id: "quant", name: "量化交易员", icon: "💰", desc: "用数学模型在金融市场叱咤风云" },
+ { id: "teacher", name: "高中教师", icon: "📝", desc: "回到家乡,培养下一代数学人才" },
+ { id: "data_sci", name: "数据科学家", icon: "📊", desc: "在互联网公司用数据改变世界" }
+];
+
+function getUnlockedEndings() {
+ try {
+ var raw = localStorage.getItem("mathAppUnlockedEndings");
+ return raw ? JSON.parse(raw) : [];
+ } catch (e) { return []; }
+}
+
+function unlockEnding(endingId) {
+ var endings = getUnlockedEndings();
+ if (endings.indexOf(endingId) < 0) {
+ endings.push(endingId);
+ localStorage.setItem("mathAppUnlockedEndings", JSON.stringify(endings));
+ }
+}
+
+function openGallery() {
+ document.getElementById("galleryOverlay").classList.add("show");
+ renderGallery();
+}
+
+function closeGallery() {
+ document.getElementById("galleryOverlay").classList.remove("show");
+}
+
+function renderGallery() {
+ var unlocked = getUnlockedEndings();
+ var progressEl = document.getElementById("galleryProgress");
+ progressEl.textContent = unlocked.length + "/" + ALL_ENDINGS.length;
+
+ var grid = document.getElementById("galleryGrid");
+ grid.innerHTML = ALL_ENDINGS.map(function (e) {
+ var isUnlocked = unlocked.indexOf(e.id) >= 0;
+ if (isUnlocked) {
+ return ''
+ + '
' + e.icon + '
'
+ + '
' + esc(e.name) + '
'
+ + '
' + esc(e.desc) + '
'
+ + '
';
+ } else {
+ return ''
+ + '
🔒
'
+ + '
???
'
+ + '
未解锁
'
+ + '
';
+ }
+ }).join("");
+}
diff --git a/static/js/app.min.js b/static/js/app.min.js
new file mode 100644
index 0000000..672d294
--- /dev/null
+++ b/static/js/app.min.js
@@ -0,0 +1,1263 @@
+let currentModule = "发现";
+let currentVideoUrl = "";
+let currentVideoTitle = "";
+let currentDiscipline = "all";
+let currentSubTab = "recommend";
+let currentLifeTab = "story";
+let allVideos = [];
+let allKnowledgeCards = [];
+let storyData = null;
+let simulatorData = null;
+let currentContext = { type: "", title: "" };
+let userId = localStorage.getItem("mathAppUserId") || generateUserId();
+let userFavorites = { video: {}, knowledge: {} };
+let userAvatar = localStorage.getItem("mathAppAvatar") || "葫";
+let userAvatarColor = localStorage.getItem("mathAppAvatarColor") || "#A0B9D8";
+let vnState = {
+currentNode: null,
+stats: { shuli: 10, xiayi: 10, xinzhi: 10 },
+favorability: { "欧阳侠": 0, "Eddie侠": 0, "厚朴侠": 0, "三力思侠": 0, "溜溜侠": 0, "孔锝侠": 0, "星侠": 0, "甲亢侠": 0 }
+};
+let simState = {
+currentNode: null,
+stats: { gpa: 10, interest: 10, skill: 10 },
+history: []
+};
+const SIM_STAT_LABELS = { gpa: "绩点", interest: "兴趣", skill: "技能" };
+const SIM_STAT_COLORS = { gpa: "#A0B9D8", interest: "#DAB8B0", skill: "#B8D0C9" };
+const SIM_STAT_EMOJIS = { gpa: "📊", interest: "❤️", skill: "🔧" };
+function generateUserId() {
+var id = "user_" + Date.now().toString(36) + "_" + Math.random().toString(36).substr(2, 6);
+localStorage.setItem("mathAppUserId", id);
+return id;
+}
+const STAT_LABELS = { shuli: "数理根基", xiayi: "侠义声望", xinzhi: "心志韧性" };
+const STAT_COLORS = { shuli: "#A0B9D8", xiayi: "#DAB8B0", xinzhi: "#B8D0C9" };
+const STAT_EMOJIS = { shuli: "📐", xiayi: "⚔️", xinzhi: "🧘" };
+function detectChapter(nodeId) {
+if (!nodeId) return 1;
+var m = nodeId.match(/^ch(\d+)/);
+return m ? parseInt(m[1]) : 1;
+}
+const MODULE_COLORS = {
+"发现": { color: "#A0B9D8", gradient: "linear-gradient(135deg, #A0B9D8, #8AA3C2)", lightBg: "#DCE6F0", name: "发现 · 值得一看的数学内容" },
+"知识": { color: "#B8D0C9", gradient: "linear-gradient(135deg, #B8D0C9, #A0BBAF)", lightBg: "#E4EFEB", name: "知识 · 数学发现" },
+"数学人生": { color: "#DAB8B0", gradient: "linear-gradient(135deg, #DAB8B0, #C9A39A)", lightBg: "#F2E4DF", name: "数学人生 · 大学选择模拟" },
+"我的": { color: "#C5CAD0", gradient: "linear-gradient(135deg, #C5CAD0, #AEB3B9)", lightBg: "#EDEEF0", name: "我的 · 个人中心" }
+};
+const MODULE_BANNERS = {
+"发现": { icon: "◎", title: "发现值得一看的数学内容", sub: "从 12 个专业分区进入,直接查看该方向的视频内容" },
+"知识": { icon: "≡", title: "用数学视角理解世界", sub: "把复杂概念变成轻量、好懂、适合反复刷的知识卡片" },
+"数学人生": { icon: "◇", title: "葫芦侠行 · 互动剧情", sub: "八章完整剧情 | 四属性系统 | 多结局 | 北宋数学武侠世界" },
+"我的": { icon: "✦", title: "你的学习空间", sub: "查看资料、收藏、记录和剧情存档" }
+};
+const MODULE_CSS_CLASS = {
+"发现": "shuren", "知识": "shushuo", "数学人生": "shusi", "我的": "shuwu"
+};
+const DISCIPLINE_ICONS = {
+"计算机": { icon: "💻", symbol: "⌨", color: "#fff", bg: "#8BA4C0" },
+"机械工程": { icon: "⚙️", symbol: "⚙", color: "#fff", bg: "#B8A99A" },
+"电子信息": { icon: "📡", symbol: "⚡", color: "#fff", bg: "#9BB8AE" },
+"经济管理": { icon: "📊", symbol: "📈", color: "#fff", bg: "#C5A8A0" },
+"生物医学": { icon: "🧬", symbol: "❤", color: "#fff", bg: "#C4B0B8" },
+"化工材料": { icon: "🧪", symbol: "⚗", color: "#fff", bg: "#ACAEC5" },
+"人工智能": { icon: "🤖", symbol: "AI", color: "#fff", bg: "#95ABB8" },
+"微电子": { icon: "🔌", symbol: "μ", color: "#fff", bg: "#8BA4C0" },
+"自动化": { icon: "🦾", symbol: "⚙", color: "#fff", bg: "#BCA898" },
+"能源动力": { icon: "🔥", symbol: "⚡", color: "#fff", bg: "#A3B4A5" },
+"环境工程": { icon: "🌱", symbol: "✿", color: "#fff", bg: "#A8B8A8" },
+"数学建模": { icon: "📐", symbol: "Σ", color: "#fff", bg: "#9CAEC4" },
+"通用": { icon: "📚", symbol: "◎", color: "#fff", bg: "#ADB5BD" }
+};
+const AVATAR_COLORS = ["#A0B9D8","#B8D0C9","#DAB8B0","#C5CAD0","#B8A99A","#9BB8AE","#C4B0B8","#ACAEC5"];
+const SCRIPTS = [
+{
+id: "huluxiaxing",
+name: "葫芦侠行",
+icon: "📖",
+desc: "北宋年间,紫金山下秉公习武堂。你扮演葫芦侠,用数学武学闯荡江湖。",
+meta: "93节点 · 8章 · 8+结局",
+status: "ready",
+cssClass: "ss-card-wuxia",
+engine: "story"
+},
+{
+id: "simulator",
+name: "数学专业模拟器",
+icon: "🎓",
+desc: "跑完四年大学数学专业的全流程。选课、考试、分流、毕业——你的每个选择决定结局。",
+meta: "8学期 · 大三专业分流 · 多结局",
+status: "beta",
+cssClass: "ss-card-simulator",
+engine: "simulator"
+},
+{
+id: "mathbti",
+name: "数学人格测试",
+icon: "🧮",
+desc: "12道场景题,发现你的数学人格类型——你是「梦境探索者」还是「博弈游侠」?",
+meta: "12题 · 4轴测评 · 12人格",
+status: "ready",
+cssClass: "ss-card-mathbti",
+engine: "mathbti"
+}
+];
+const COMING_SOON = [
+{ name: "数学侦探录", icon: "🔍", desc: "民国上海,用数学破案", color: "#B8A99A" },
+{ name: "星际数探", icon: "🔭", desc: "22世纪太空舰队首席数理官", color: "#9BB8AE" },
+{ name: "算学千年", icon: "📜", desc: "穿越古代,亲历数学史", color: "#ACAEC5" },
+{ name: "数学创业记", icon: "🚀", desc: "硅谷数学PhD辍学创业", color: "#C4B0B8" }
+];
+const SPRITE_MAP = {
+"葫芦侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=young%20Chinese%20Song%20dynasty%20scholar%20with%20a%20gourd%20hanging%20at%20his%20waist%2C%20simple%20blue-gray%20robes%2C%20gentle%20expression%2C%20clean%20ink%20wash%20painting%20style%20on%20soft%20background%2C%20morandi%20muted%20colors&image_size=portrait_4_3",
+"欧阳侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=diligent%20young%20Chinese%20scholar%20reading%20a%20book%20at%20dawn%2C%20warm%20candle%20light%2C%20ink%20wash%20style%2C%20wearing%20simple%20robes%2C%20soft%20morandi%20beige%20and%20blue%20tones&image_size=portrait_4_3",
+"溜溜侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=mysterious%20young%20Chinese%20female%20warrior%20with%20a%20large%20black%20dog%2C%20veiled%20face%2C%20dark%20grey%20robes%2C%20ink%20wash%20painting%20style%2C%20morandi%20muted%20dark%20tones%2C%20cool%20atmosphere&image_size=portrait_4_3",
+"孔锝侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=strict%20Chinese%20martial%20arts%20instructor%2C%20tall%20and%20upright%2C%20stern%20but%20kind%20eyes%2C%20ink%20wash%20style%2C%20wearing%20neat%20grey-blue%20robes%2C%20morandi%20muted%20tones&image_size=portrait_4_3",
+"Eddie侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=young%20Chinese%20scholar%20with%20copper%20coin%20spinning%20on%20finger%2C%20playful%20expression%2C%20ink%20wash%20style%2C%20soft%20morandi%20tones&image_size=portrait_4_3",
+"厚朴侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=elegant%20Chinese%20theater%20actor%2C%20dramatic%20pose%2C%20traditional%20costume%2C%20ink%20wash%20style%2C%20morandi%20muted%20warm%20tones&image_size=portrait_4_3",
+"星侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=gentle%20Chinese%20female%20scholar%20with%20warm%20smile%2C%20ink%20wash%20style%2C%20flowing%20robes%2C%20morandi%20soft%20pink%20and%20grey%20tones&image_size=portrait_4_3",
+"甲亢侠": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=passionate%20young%20Chinese%20debater%2C%20animated%20gestures%2C%20ink%20wash%20style%2C%20morandi%20muted%20tones%2C%20intense%20eyes&image_size=portrait_4_3"
+};
+const BG_MAP = {
+"紫金山": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=ancient%20Chinese%20academy%20on%20misty%20Purple%20Mountain%2C%20traditional%20architecture%20with%20courtyards%2C%20morning%20fog%2C%20ink%20wash%20painting%20style%2C%20morandi%20muted%20greens%20and%20greys&image_size=landscape_4_3",
+"石室": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=underground%20stone%20chamber%20turned%20study%20room%2C%20candle%20lit%2C%20wooden%20shelves%20with%20scrolls%2C%20murals%20on%20rock%20walls%2C%20warm%20atmosphere%2C%20ink%20wash%20style%2C%20morandi%20warm%20greys&image_size=landscape_4_3",
+"姑苏": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=Suzhou%20ancient%20canal%20town%20in%20spring%2C%20stone%20bridges%2C%20willow%20trees%2C%20traditional%20white%20wall%20buildings%2C%20gentle%20rain%2C%20ink%20wash%20painting%20style%2C%20morandi%20muted%20blue-grey%20tones&image_size=landscape_4_3",
+"京城": "https://coresg-normal.trae.ai/api/ide/v1/text_to_image?prompt=ancient%20Chinese%20capital%20city%2C%20grand%20government%20hall%2C%20solemn%20atmosphere%2C%20ink%20wash%20style%2C%20morandi%20muted%20grey%20tones&image_size=landscape_4_3"
+};
+const CHAPTER_BG = {
+1: "紫金山",
+2: "紫金山",
+3: "紫金山",
+4: "石室",
+5: "紫金山",
+6: "石室",
+7: "石室",
+8: "京城"
+};
+document.addEventListener("DOMContentLoaded", async function () {
+await loadStoryData();
+await loadUserFavorites();
+loadVNSave();
+switchModule("发现");
+});
+function switchModule(module) {
+currentModule = module;
+currentDiscipline = "all";
+currentSubTab = "recommend";
+var colors = MODULE_COLORS[module] || MODULE_COLORS["发现"];
+document.documentElement.style.setProperty("--accent", colors.color);
+document.documentElement.style.setProperty("--accent-light", colors.lightBg);
+document.documentElement.style.setProperty("--accent-gradient", colors.gradient);
+var brandIcon = document.querySelector(".brand-icon-sm");
+if (brandIcon) { brandIcon.textContent = "葫"; brandIcon.style.background = userAvatarColor; }
+document.querySelectorAll(".tab-btn").forEach(function (btn) {
+btn.classList.toggle("active", btn.dataset.module === module);
+});
+hideAllPanels();
+if (module === "发现") {
+show("searchHeader");
+show("subNav");
+show("videoList");
+document.querySelectorAll(".sub-nav-btn").forEach(function (btn) {
+btn.classList.toggle("active", btn.dataset.tab === "recommend");
+});
+currentSubTab = "recommend";
+loadVideos("数人");
+return;
+} else if (module === "知识") {
+show("sectionTitleBar");
+show("shushuoHero");
+show("shushuoTopics");
+show("shushuoFeed");
+document.getElementById("contentTitle").textContent = colors.name;
+loadKnowledgeCards();
+return;
+} else if (module === "数学人生") {
+show("scriptSquare");
+renderScriptSquare();
+return;
+} else if (module === "我的") {
+show("sectionTitleBar");
+show("profilePage");
+document.getElementById("contentTitle").textContent = colors.name;
+loadProfileData();
+return;
+}
+}
+function hideAllPanels() {
+hide("searchHeader"); hide("subNav"); hide("sectionTitleBar"); hide("lifeSubNav");
+hide("moduleBanner"); hide("shushuoHero"); hide("shushuoTopics");
+hide("shushuoFeed"); hide("storyContainer"); hide("simulatorContainer");
+hide("disciplineGridContainer"); hide("disciplineActiveHeader");
+hide("disciplineGrid"); hide("categoryFilter");
+hide("profilePage"); hide("scriptSquare");
+hide("loadingArea"); hide("videoList");
+}
+function show(id) { var el = document.getElementById(id); if (el) el.style.display = (id === "videoList" ? "grid" : id === "subNav" ? "flex" : "block"); }
+function hide(id) { var el = document.getElementById(id); if (el) el.style.display = "none"; }
+function updateBanner(module) {
+var info = MODULE_BANNERS[module] || MODULE_BANNERS["发现"];
+var el = document.getElementById("moduleBanner");
+if (!el) return;
+el.style.display = "flex";
+el.className = "module-banner " + (MODULE_CSS_CLASS[module] || "shuren");
+el.innerHTML = '' + info.icon + ' ' + info.title + '
' + info.sub + '
';
+}
+function handleSearch() {
+var q = document.getElementById("videoSearch").value.toLowerCase().trim();
+if (!q) { renderVideoList(allVideos); return; }
+renderVideoList(allVideos.filter(function (v) {
+return (v.title && v.title.toLowerCase().indexOf(q) >= 0) ||
+(v.description && v.description.toLowerCase().indexOf(q) >= 0) ||
+(v.discipline && v.discipline.toLowerCase().indexOf(q) >= 0) ||
+(v.author && v.author.toLowerCase().indexOf(q) >= 0);
+}));
+}
+function switchSubTab(tab) {
+currentSubTab = tab;
+document.querySelectorAll(".sub-nav-btn").forEach(function (btn) { btn.classList.toggle("active", btn.dataset.tab === tab); });
+var gridCtn = document.getElementById("disciplineGridContainer");
+var videoList = document.getElementById("videoList");
+var activeHdr = document.getElementById("disciplineActiveHeader");
+var catFilter = document.getElementById("categoryFilter");
+if (gridCtn) gridCtn.style.display = "none";
+if (activeHdr) activeHdr.style.display = "none";
+if (catFilter) catFilter.style.display = "none";
+if (tab === "discipline") {
+if (videoList) videoList.style.display = "none";
+renderDisciplineGrid();
+} else if (tab === "hot") {
+if (videoList) videoList.style.display = "grid";
+var sorted = [].concat(allVideos).sort(function (a, b) { return (hashStr(b.title) % 1000) - (hashStr(a.title) % 1000); });
+renderVideoList(sorted);
+} else {
+if (videoList) videoList.style.display = "grid";
+renderVideoList(allVideos);
+}
+}
+function renderDisciplineGrid() {
+var gridCtn = document.getElementById("disciplineGridContainer");
+var grid = document.getElementById("disciplineGrid");
+var videoList = document.getElementById("videoList");
+if (videoList) videoList.style.display = "none";
+var keys = Object.keys(DISCIPLINE_ICONS).filter(function (d) { return d !== "通用"; });
+grid.innerHTML = keys.map(function (d) {
+var info = DISCIPLINE_ICONS[d];
+return ''
++ '
' + (info.symbol || info.icon) + '
'
++ '
' + esc(d) + '
';
+}).join("");
+gridCtn.style.display = "block";
+grid.style.display = "grid";
+}
+function selectDiscipline(d) {
+document.getElementById("disciplineGridContainer").style.display = "none";
+document.getElementById("categoryFilter").style.display = "none";
+document.getElementById("videoList").style.display = "grid";
+document.getElementById("disciplineActiveHeader").style.display = "flex";
+document.getElementById("activeDisciplineName").textContent = d;
+renderVideoList(allVideos.filter(function (v) { return v.discipline === d; }));
+}
+function backToDisciplineGrid() {
+document.getElementById("disciplineActiveHeader").style.display = "none";
+document.getElementById("categoryFilter").style.display = "none";
+renderDisciplineGrid();
+}
+async function loadKnowledgeCards(topic) {
+show("loadingArea");
+hide("shushuoFeed");
+var url = "/api/knowledge";
+if (topic) url += "?topic=" + encodeURIComponent(topic);
+try {
+var resp = await fetch(url);
+var result = await resp.json();
+hide("loadingArea");
+if (result.code === 0) {
+allKnowledgeCards = result.data || [];
+var topics = result.topics || [];
+renderShushuoTopics(topics);
+renderShushuoFeed(allKnowledgeCards);
+}
+} catch (e) {
+hide("loadingArea");
+}
+}
+function renderShushuoTopics(topics) {
+var scrollEl = document.getElementById("topicsScroll");
+scrollEl.innerHTML = ('全部 ') +
+topics.map(function (t) {
+return '' + esc(t) + ' ';
+}).join("");
+}
+function renderShushuoFeed(cards) {
+show("shushuoFeed");
+updateShushuoHero();
+var feed = document.getElementById("shushuoFeed");
+feed.innerHTML = cards.map(function (c) {
+var isFav = userFavorites.knowledge[c.id] || false;
+return ''
++ '
'
++ '
' + (c.icon || "📖") + '
'
++ '
' + esc(c.question || "") + '
'
++ '
' + (isFav ? '★' : '☆') + ' '
++ '
'
++ '
' + esc(c.title) + '
'
++ '
' + esc(c.description) + '
'
++ '
';
+}).join("");
+}
+function filterShushuoByTopic(topic) {
+document.querySelectorAll(".topic-pill").forEach(function (p) {
+var is = p.textContent.trim() === (topic || "全部");
+p.classList.toggle("active", is);
+});
+if (topic) {
+loadKnowledgeCards(topic);
+} else {
+loadKnowledgeCards();
+}
+document.querySelector(".content-area").scrollTo({ top: 0, behavior: "smooth" });
+}
+function openKnowledgeDetail(cardId) {
+var card = allKnowledgeCards.find(function (c) { return c.id === cardId; });
+if (!card) return;
+setAIContext("knowledge", card.title);
+recordHistory("knowledge", card.id, card.title, card.icon || "📖");
+openChat();
+quickAsk(card.title + "是什么?能给我详细讲讲吗?");
+}
+async function toggleFavorite(type, targetId, title, cover) {
+var isFav = (userFavorites[type] || {})[targetId];
+if (isFav) {
+try {
+await fetch("/api/favorites", {
+method: "DELETE",
+headers: { "Content-Type": "application/json" },
+body: JSON.stringify({ user_id: userId, target_type: type, target_id: targetId })
+});
+if (userFavorites[type]) delete userFavorites[type][targetId];
+showToast("已取消收藏");
+} catch (e) { /* ignore */ }
+} else {
+try {
+await fetch("/api/favorites", {
+method: "POST",
+headers: { "Content-Type": "application/json" },
+body: JSON.stringify({ user_id: userId, target_type: type, target_id: targetId, title: title, cover: cover })
+});
+if (!userFavorites[type]) userFavorites[type] = {};
+userFavorites[type][targetId] = true;
+showToast("已加入收藏");
+} catch (e) { /* ignore */ }
+}
+if (currentModule === "知识" && type === "knowledge") {
+renderShushuoFeed(allKnowledgeCards);
+} else if (currentModule === "发现" && type === "video") {
+renderVideoList(allVideos);
+}
+}
+async function loadUserFavorites() {
+try {
+var resp = await fetch("/api/favorites?user_id=" + userId);
+var result = await resp.json();
+if (result.code === 0) {
+userFavorites = { video: {}, knowledge: {} };
+(result.data || []).forEach(function (f) {
+if (!userFavorites[f.target_type]) userFavorites[f.target_type] = {};
+userFavorites[f.target_type][f.target_id] = true;
+});
+}
+} catch (e) { userFavorites = { video: {}, knowledge: {} }; }
+}
+async function recordHistory(type, targetId, title, cover) {
+try {
+await fetch("/api/history", {
+method: "POST",
+headers: { "Content-Type": "application/json" },
+body: JSON.stringify({ user_id: userId, target_type: type, target_id: targetId, title: title, cover: cover })
+});
+} catch (e) { /* ignore */ }
+}
+function showToast(message) {
+var toast = document.getElementById("globalToast");
+if (!toast) {
+toast = document.createElement("div");
+toast.id = "globalToast";
+toast.style.cssText = "position:fixed;bottom:100px;left:50%;transform:translateX(-50%);background:rgba(0,0,0,0.78);color:#fff;padding:8px 20px;border-radius:20px;font-size:13px;z-index:9999;pointer-events:none;transition:opacity 0.3s;opacity:0;";
+document.body.appendChild(toast);
+}
+toast.textContent = message;
+toast.style.opacity = "1";
+clearTimeout(toast._timer);
+toast._timer = setTimeout(function () { toast.style.opacity = "0"; }, 1500);
+}
+async function loadProfileData() {
+try {
+var [statsResp, profileResp] = await Promise.all([
+fetch("/api/user/stats?user_id=" + userId),
+fetch("/api/user/profile?user_id=" + userId)
+]);
+var stats = (await statsResp.json()).data || { watch_count: 0, favorite_count: 0 };
+var profile = (await profileResp.json()).data || {};
+var statNums = document.querySelectorAll(".profile-stat-num");
+if (statNums.length >= 3) {
+statNums[0].textContent = stats.watch_count || 0;
+statNums[1].textContent = stats.favorite_count || 0;
+statNums[2].textContent = "2"; // 剧情存档数保持固定
+}
+var avatar = document.getElementById("profileAvatar");
+if (avatar) {
+avatar.textContent = (profile.avatar_emoji || userAvatar);
+avatar.style.background = (profile.avatar_color || userAvatarColor);
+userAvatar = profile.avatar_emoji || userAvatar;
+userAvatarColor = profile.avatar_color || userAvatarColor;
+}
+var nameEl = document.getElementById("profileName");
+if (nameEl) nameEl.textContent = (profile.nickname || "游客用户");
+var idEl = document.getElementById("profileId");
+if (idEl) idEl.textContent = "ID: " + userId.substring(0, 14);
+} catch (e) { /* ignore */ }
+}
+function showFavorites() {
+loadFavoritesList();
+}
+async function loadFavoritesList() {
+showProfileListPanel("我的收藏");
+var panel = document.getElementById("profileListContent");
+panel.innerHTML = '';
+try {
+var resp = await fetch("/api/favorites?user_id=" + userId);
+var result = await resp.json();
+var items = result.data || [];
+if (items.length === 0) {
+panel.innerHTML = '';
+} else {
+panel.innerHTML = items.map(function (f) {
+return ''
++ '
' + (f.cover || "◎") + '
'
++ '
' + esc(f.title) + '
' + f.target_type + ' · ' + (f.created_at || "").substring(0, 10) + '
'
++ '
✕ '
++ '
';
+}).join("");
+}
+} catch (e) {
+panel.innerHTML = '';
+}
+}
+function showHistory() {
+loadHistoryList();
+}
+async function loadHistoryList() {
+showProfileListPanel("观看记录");
+var panel = document.getElementById("profileListContent");
+panel.innerHTML = '';
+try {
+var resp = await fetch("/api/history?user_id=" + userId + "&limit=30");
+var result = await resp.json();
+var items = result.data || [];
+if (items.length === 0) {
+panel.innerHTML = '';
+} else {
+panel.innerHTML = items.map(function (h) {
+return ''
++ '
' + (h.cover || "◎") + '
'
++ '
' + esc(h.title) + '
' + h.target_type + ' · ' + (h.watched_at || "").substring(0, 16) + '
'
++ '
';
+}).join("");
+}
+} catch (e) {
+panel.innerHTML = '';
+}
+}
+function showProfileListPanel(title) {
+var panel = document.getElementById("profileListPanel");
+var titleEl = document.getElementById("profileListTitle");
+if (titleEl) titleEl.textContent = title;
+if (panel) panel.style.display = "block";
+var profilePage = document.getElementById("profilePage");
+if (profilePage) profilePage.style.display = "none";
+}
+function hideProfileListPanel() {
+var panel = document.getElementById("profileListPanel");
+if (panel) panel.innerHTML = "";
+if (panel) panel.style.display = "none";
+var profilePage = document.getElementById("profilePage");
+if (profilePage) profilePage.style.display = "block";
+loadProfileData();
+}
+async function removeFavorite(type, targetId) {
+await toggleFavorite(type, targetId, "", "");
+loadFavoritesList();
+loadProfileData();
+}
+function editNickname() {
+var name = prompt("输入新昵称:", document.querySelector(".profile-name").textContent);
+if (name && name.trim()) {
+var trimmed = name.trim().substring(0, 12);
+fetch("/api/user/profile", {
+method: "POST",
+headers: { "Content-Type": "application/json" },
+body: JSON.stringify({ user_id: userId, nickname: trimmed })
+}).then(function () {
+var el = document.querySelector(".profile-name");
+if (el) el.textContent = trimmed;
+showToast("昵称已更新");
+});
+}
+}
+function updateShushuoHero() {
+document.getElementById("heroTitle").textContent = "数学不是公式,是你理解世界的方式";
+document.getElementById("heroDesc").textContent = "每天一个数学概念,让思维变得更清晰";
+var el = document.querySelector(".hero-stats");
+if (el) el.innerHTML = "📖 " + allKnowledgeCards.length + "个概念 ⏱ 阅读约" + (allKnowledgeCards.length * 5) + "分钟 ";
+}
+function adjustColor(hex, amount) {
+var num = parseInt(hex.replace("#",""), 16);
+var r = Math.min(255, (num >> 16) + amount);
+var g = Math.min(255, ((num >> 8) & 0x00FF) + amount);
+var b = Math.min(255, (num & 0x0000FF) + amount);
+return "#" + ((r << 16) | (g << 8) | b).toString(16).padStart(6, "0");
+}
+async function loadVideos(module, discipline) {
+show("loadingArea");
+document.getElementById("videoList").innerHTML = "";
+var url = "/api/videos?module=" + encodeURIComponent(module);
+if (discipline && discipline !== "all") url += "&discipline=" + encodeURIComponent(discipline);
+try {
+var resp = await fetch(url);
+var result = await resp.json();
+hide("loadingArea");
+allVideos = result.data || [];
+if (allVideos.length > 0) {
+renderVideoList(allVideos);
+} else {
+document.getElementById("videoList").innerHTML = '';
+}
+} catch (e) {
+hide("loadingArea");
+document.getElementById("videoList").innerHTML = '';
+}
+}
+function renderVideoList(videos) {
+var list = document.getElementById("videoList");
+if (!videos || videos.length === 0) { list.innerHTML = ''; return; }
+list.style.display = "grid";
+list.innerHTML = videos.map(function (v) {
+var avatarColor = AVATAR_COLORS[hashStr(v.author || "") % AVATAR_COLORS.length];
+var initial = (v.author || "?").replace(/学院|·|同学|系/g, "").slice(0, 1);
+var dur = v.duration_min || 5;
+var icon = v.discipline_icon && v.discipline_icon !== "·" ? v.discipline_icon : "◎";
+var coverBg = "hsl(" + (hashStr(v.discipline || "") % 360) + ", 40%, 90%)";
+var isFav = userFavorites.video[v.id] || false;
+return ''
++ '
' + icon + '
' + dur + ':00
'
++ '
' + (isFav ? '★' : '☆') + ' '
++ '
'
++ '
' + esc(v.title) + '
'
++ '
' + initial + ' '
++ '
' + esc(v.author) + ' '
++ '
👁️ ' + ((hashStr(v.title) % 1000) + 100) + '
';
+}).join("");
+}
+function openVideo(id, title, desc, url, author, cat) {
+currentVideoUrl = url;
+currentVideoTitle = title;
+document.getElementById("videoModalTitle").textContent = title;
+document.getElementById("videoModalDesc").textContent = desc;
+document.getElementById("videoModalAuthor").innerHTML = author;
+document.getElementById("videoModalCat").innerHTML = cat || "通用";
+document.getElementById("videoLinkBtn").style.display = url ? "block" : "none";
+document.getElementById("videoPlaceholderText").textContent = url ? "点击下方按钮查看完整课程视频" : "视频链接待补充";
+var favBtn = document.getElementById("videoModalFav");
+var vid = parseInt(id);
+favBtn.textContent = (userFavorites.video[vid]) ? "★ 已收藏" : "☆ 收藏";
+favBtn.dataset.vid = vid;
+document.getElementById("videoModal").classList.add("show");
+setAIContext("video", title);
+recordHistory("video", vid, title, cat || "◎");
+}
+function closeVideo() { document.getElementById("videoModal").classList.remove("show"); }
+function openVideoLink() { if (currentVideoUrl) window.open(currentVideoUrl, "_blank"); }
+function toggleVideoFav() {
+var btn = document.getElementById("videoModalFav");
+var vid = parseInt(btn.dataset.vid);
+var title = currentVideoTitle;
+toggleFavorite("video", vid, title, "◎");
+btn.textContent = (userFavorites.video[vid]) ? "★ 已收藏" : "☆ 收藏";
+}
+async function loadStoryData() {
+try {
+var resp = await fetch("/api/story");
+var result = await resp.json();
+if (result.code === 0) storyData = result.data;
+} catch (e) { console.error("故事数据加载失败:", e); }
+}
+function loadVNSave() {
+try { var s = localStorage.getItem("mathAppVNSave"); if (s) vnState = JSON.parse(s); } catch (e) {}
+}
+function saveVNState() {
+localStorage.setItem("mathAppVNSave", JSON.stringify(vnState));
+alert("进度已存档!");
+}
+function resetVNSave() {
+var startNode = (storyData && storyData.start_node) || "ch1_start";
+vnState = { currentNode: startNode, stats: { shuli: 10, xiayi: 10, xinzhi: 10 }, favorability: { "欧阳侠": 0, "Eddie侠": 0, "厚朴侠": 0, "三力思侠": 0, "溜溜侠": 0, "孔锝侠": 0, "星侠": 0, "甲亢侠": 0 } };
+localStorage.removeItem("mathAppVNSave");
+navigateVN(startNode);
+}
+function applyVNStats(effects) {
+if (!effects) return;
+if (effects.shuli) vnState.stats.shuli = (vnState.stats.shuli || 10) + effects.shuli;
+if (effects.xiayi) vnState.stats.xiayi = (vnState.stats.xiayi || 10) + effects.xiayi;
+if (effects.xinzhi) vnState.stats.xinzhi = (vnState.stats.xinzhi || 10) + effects.xinzhi;
+if (effects.intelligence !== undefined) vnState.stats.shuli = (vnState.stats.shuli || 10) + effects.intelligence;
+if (effects.charm !== undefined) vnState.stats.xiayi = (vnState.stats.xiayi || 10) + effects.charm;
+if (effects.stress !== undefined) vnState.stats.xinzhi = (vnState.stats.xinzhi || 10) - effects.stress;
+if (effects.favorability) {
+if (!vnState.favorability) vnState.favorability = {};
+for (var char in effects.favorability) {
+vnState.favorability[char] = (vnState.favorability[char] || 0) + effects.favorability[char];
+}
+}
+}
+function startStoryEngine() {
+setAIContext("story", "葫芦侠行");
+var startNode = (storyData && storyData.start_node) || "ch1_start";
+var savedNode = vnState.currentNode;
+if (savedNode && storyData && storyData.nodes && storyData.nodes[savedNode]) {
+navigateVN(savedNode);
+} else {
+vnState.currentNode = startNode;
+navigateVN(startNode);
+}
+}
+function navigateVN(nodeId) {
+if (!storyData || !storyData.nodes || !storyData.nodes[nodeId]) {
+document.getElementById("storyContainer").innerHTML = '';
+return;
+}
+var node = storyData.nodes[nodeId];
+vnState.currentNode = nodeId;
+var container = document.getElementById("storyContainer");
+if (!container) return;
+container.style.display = "block";
+container.scrollIntoView({ behavior: "smooth" });
+var ch = detectChapter(nodeId);
+var chNames = ["","初入江湖","西部教习","数理论道","脑学初创","报国之路","暗流涌动","陋室修炼","真相之门"];
+var chName = chNames[ch] || "";
+var html = '';
+html += '';
+var bgKey = CHAPTER_BG[ch] || "紫金山";
+var bgUrl = BG_MAP[bgKey] || "";
+if (node.background) bgUrl = BG_MAP[node.background] || node.background || bgUrl;
+var spriteUrl = "";
+var characterName = node.character || "";
+if (node.sprite) {
+spriteUrl = node.sprite;
+} else if (characterName) {
+var names = characterName.split("&");
+for (var ni = 0; ni < names.length; ni++) {
+var n = names[ni].trim();
+if (SPRITE_MAP[n]) { spriteUrl = SPRITE_MAP[n]; break; }
+}
+}
+html += '';
+if (bgUrl) html += '
';
+if (spriteUrl) html += '
';
+html += '
';
+if (characterName) html += '
' + esc(characterName) + '
';
+html += '
';
+html += '
';
+html += '
';
+container.innerHTML = html;
+var sceneText = document.getElementById("sceneText");
+var text = node.scene || "";
+var i = 0;
+function typeNext() {
+if (i < text.length) {
+sceneText.textContent += text.charAt(i);
+i++;
+setTimeout(typeNext, 25);
+} else {
+buildChoiceButtons(node.choices || []);
+}
+}
+sceneText.textContent = "";
+typeNext();
+}
+function buildChoiceButtons(choices) {
+var area = document.getElementById("choicesArea");
+if (!area) return;
+area.innerHTML = "";
+choices.forEach(function (c, idx) {
+var btn = document.createElement("button");
+btn.className = "choice-btn";
+btn.style.animationDelay = (idx * 0.1) + "s";
+var effectsHTML = "";
+if (c.effects) {
+var parts = [];
+if (c.effects.shuli) parts.push("数理根基" + (c.effects.shuli > 0 ? "+" : "") + c.effects.shuli);
+if (c.effects.xiayi) parts.push("侠义声望" + (c.effects.xiayi > 0 ? "+" : "") + c.effects.xiayi);
+if (c.effects.xinzhi) parts.push("心志韧性" + (c.effects.xinzhi > 0 ? "+" : "") + c.effects.xinzhi);
+if (c.effects.intelligence) parts.push("数理根基" + (c.effects.intelligence > 0 ? "+" : "") + c.effects.intelligence);
+if (c.effects.charm) parts.push("侠义声望" + (c.effects.charm > 0 ? "+" : "") + c.effects.charm);
+if (c.effects.stress) parts.push("心志韧性" + (c.effects.stress > 0 ? "+" : "") + c.effects.stress);
+if (c.effects.favorability) {
+for (var char in c.effects.favorability) {
+var val = c.effects.favorability[char];
+parts.push(char + "好感" + (val > 0 ? "+" : "") + val);
+}
+}
+if (parts.length > 0) effectsHTML = '[' + parts.join(" | ") + '] ';
+}
+btn.innerHTML = '' + esc(c.text) + ' ' + effectsHTML;
+btn.onclick = (function (nextNode, effects) {
+return function () {
+applyVNStats(effects);
+navigateVN(nextNode);
+};
+})(c.next, c.effects);
+area.appendChild(btn);
+});
+}
+function openChat() { document.getElementById("chatOverlay").classList.add("show"); document.getElementById("chatMessages").scrollTop = document.getElementById("chatMessages").scrollHeight; }
+function closeChat() { document.getElementById("chatOverlay").classList.remove("show"); }
+function quickAsk(q) { document.getElementById("chatInput").value = q; sendMessage(); }
+async function sendMessage() {
+var input = document.getElementById("chatInput");
+var message = input.value.trim();
+if (!message) return;
+appendChat("user", message);
+input.value = "";
+var typingId = appendTyping();
+try {
+var resp = await fetch("/api/chat", {
+method: "POST", headers: { "Content-Type": "application/json" },
+body: JSON.stringify({ message: message, context: currentContext })
+});
+var result = await resp.json();
+removeTyping(typingId);
+appendChat("assistant", result.code === 0 ? result.reply : "抱歉,AI 助教暂时无法回复。");
+} catch (e) {
+removeTyping(typingId);
+appendChat("assistant", "网络异常,请稍后重试。");
+}
+}
+function setAIContext(type, title) {
+currentContext = { type: type, title: title || "" };
+var hint = document.getElementById("chatContextHint");
+if (!hint) {
+hint = document.createElement("div");
+hint.id = "chatContextHint";
+hint.style.cssText = "padding:6px 12px;font-size:11px;color:var(--accent);background:var(--accent-light);text-align:center;";
+var chatPanel = document.querySelector(".chat-panel");
+var header = document.querySelector(".chat-header");
+if (header && chatPanel) chatPanel.insertBefore(hint, header.nextSibling);
+}
+hint.textContent = currentContext.title ? "📍 当前:" + currentContext.title : "";
+hint.style.display = currentContext.title ? "block" : "none";
+}
+function appendChat(role, text) {
+var container = document.getElementById("chatMessages");
+var div = document.createElement("div");
+div.className = "chat-msg " + role;
+div.innerHTML = '' + esc(text) + "
";
+container.appendChild(div);
+container.scrollTop = container.scrollHeight;
+}
+function appendTyping() {
+var container = document.getElementById("chatMessages");
+var div = document.createElement("div");
+div.className = "chat-msg assistant typing-msg";
+div.innerHTML = '';
+container.appendChild(div);
+container.scrollTop = container.scrollHeight;
+return div;
+}
+function removeTyping(el) { if (el && el.parentNode) el.remove(); }
+function switchLifeTab(tab) {
+currentLifeTab = tab;
+document.querySelectorAll(".life-sub-btn").forEach(function (b) { b.classList.toggle("active", b.dataset.life === tab); });
+if (tab === "simulator") {
+hide("storyContainer");
+show("simulatorContainer");
+setAIContext("simulator", "数学专业模拟器");
+startSimulator();
+} else {
+hide("simulatorContainer");
+show("storyContainer");
+setAIContext("story", "葫芦侠行");
+startStoryEngine();
+}
+}
+async function loadSimulatorData() {
+try {
+var resp = await fetch("/api/simulator");
+var result = await resp.json();
+if (result.code === 0) simulatorData = result.data;
+} catch (e) { console.error("模拟器数据加载失败:", e); }
+}
+async function loadSimSave() {
+try {
+var resp = await fetch("/api/simulator/save?user_id=" + userId);
+var result = await resp.json();
+if (result.code === 0 && result.data) {
+simState = result.data;
+}
+} catch (e) {}
+}
+function saveSimState() {
+simState.history.push(simState.currentNode);
+fetch("/api/simulator/save", {
+method: "POST",
+headers: { "Content-Type": "application/json" },
+body: JSON.stringify({ user_id: userId, state: simState })
+}).catch(function () {});
+}
+function resetSimulator() {
+simState = { currentNode: (simulatorData && simulatorData.start_node) || "y1_welcome", stats: { gpa: 10, interest: 10, skill: 10 }, history: [] };
+saveSimState();
+navigateSimulator(simState.currentNode);
+}
+async function startSimulator() {
+if (!simulatorData) await loadSimulatorData();
+await loadSimSave();
+if (!simState.currentNode || !simulatorData || !simulatorData.nodes || !simulatorData.nodes[simState.currentNode]) {
+resetSimulator();
+} else {
+navigateSimulator(simState.currentNode);
+}
+}
+function navigateSimulator(nodeId) {
+if (!simulatorData || !simulatorData.nodes || !simulatorData.nodes[nodeId]) {
+document.getElementById("simulatorContainer").innerHTML = '
剧情加载失败 ';
+return;
+}
+var node = simulatorData.nodes[nodeId];
+simState.currentNode = nodeId;
+saveSimState();
+var container = document.getElementById("simulatorContainer");
+if (!container) return;
+container.style.display = "block";
+if (node.end) {
+showEnding(node);
+return;
+}
+container.scrollIntoView({ behavior: "smooth" });
+var ch = (node.year || 1);
+var stage = node.stage || "";
+var html = '';
+html += '';
+html += '';
+if (node.character) html += '
' + esc(node.character) + '
';
+html += '
';
+html += '
';
+html += '
';
+container.innerHTML = html;
+var textEl = document.getElementById("simText");
+var text = node.scene || "";
+var i = 0;
+function typeNext() {
+if (i < text.length) {
+textEl.textContent += text.charAt(i);
+i++;
+setTimeout(typeNext, 20);
+} else {
+buildSimChoices(node.choices || []);
+}
+}
+textEl.textContent = "";
+typeNext();
+}
+function buildSimChoices(choices) {
+var area = document.getElementById("simChoicesArea");
+if (!area) return;
+area.innerHTML = "";
+choices.forEach(function (c, idx) {
+var btn = document.createElement("button");
+btn.className = "sim-choice-btn";
+btn.style.animationDelay = (idx * 0.1) + "s";
+var effectsHTML = "";
+if (c.effects) {
+var parts = [];
+if (c.effects.gpa) parts.push((c.effects.gpa > 0 ? "+" : "") + c.effects.gpa + " 绩点");
+if (c.effects.interest) parts.push((c.effects.interest > 0 ? "+" : "") + c.effects.interest + " 兴趣");
+if (c.effects.skill) parts.push((c.effects.skill > 0 ? "+" : "") + c.effects.skill + " 技能");
+if (parts.length > 0) effectsHTML = '[' + parts.join(" | ") + '] ';
+}
+if (c.hint) effectsHTML += '💡 ' + esc(c.hint) + ' ';
+btn.innerHTML = '' + esc(c.text) + ' ' + effectsHTML;
+btn.onclick = (function (nextNode, effects) {
+return function () {
+if (effects) applySimStats(effects);
+navigateSimulator(nextNode);
+};
+})(c.next, c.effects);
+area.appendChild(btn);
+});
+}
+function applySimStats(effects) {
+if (!effects) return;
+if (effects.gpa) simState.stats.gpa = Math.max(0, Math.min(30, (simState.stats.gpa || 10) + effects.gpa));
+if (effects.interest) simState.stats.interest = Math.max(0, Math.min(30, (simState.stats.interest || 10) + effects.interest));
+if (effects.skill) simState.stats.skill = Math.max(0, Math.min(30, (simState.stats.skill || 10) + effects.skill));
+}
+function showEnding(node) {
+document.getElementById("endingOverlay").classList.add("show");
+var badges = { pure_math: "🔮", ai: "🤖", finance: "💰", research: "🔬", startup: "🚀", teacher: "📚", engineer: "⚙️", security: "🔒" };
+document.getElementById("endingBadge").textContent = badges[node.ending_type] || "🎓";
+document.getElementById("endingType").textContent = node.character || "结局";
+document.getElementById("endingScene").textContent = node.scene || "";
+document.getElementById("endingTitle").textContent = node.stage || "";
+if (node.ending_type) unlockEnding(node.ending_type);
+var statsHtml = '';
+Object.keys(simState.stats).forEach(function (k) {
+statsHtml += '
' + simState.stats[k] + '
' + SIM_STAT_LABELS[k] + '
';
+});
+statsHtml += '
';
+document.getElementById("endingStats").innerHTML = statsHtml;
+}
+function closeEnding() { document.getElementById("endingOverlay").classList.remove("show"); }
+var PICKER_EMOJIS = ["π","∑","∫","∞","Δ","∂","α","β","γ","λ","μ","σ","Ω","∇","⊥","⊕","⊗","≈","±","÷","√","∏","∃","∀","∈","⊂","∪","∩","ℵ","∮"];
+function openAvatarPicker() {
+document.getElementById("avatarPickerOverlay").classList.add("show");
+var grid = document.getElementById("pickerEmojiGrid");
+grid.innerHTML = PICKER_EMOJIS.map(function (e) {
+var selected = e === userAvatar ? ' style="border-color:var(--accent);background:var(--accent-light)"' : "";
+return '' + e + ' ';
+}).join("");
+var colors = ["#A0B9D8","#B8D0C9","#DAB8B0","#C5CAD0","#B8A99A","#9BB8AE","#ACAEC5","#C4B0B8"];
+var colorRow = document.getElementById("pickerColorRow");
+colorRow.innerHTML = colors.map(function (c) {
+var selected = c === userAvatarColor ? ' style="box-shadow:0 0 0 4px ' + c + '44"' : "";
+return ' ';
+}).join("");
+}
+function selectAvatar(emoji) {
+userAvatar = emoji;
+localStorage.setItem("mathAppAvatar", emoji);
+openAvatarPicker(); // refresh grid
+updateAvatarUI();
+fetch("/api/user/profile", {
+method: "POST",
+headers: { "Content-Type": "application/json" },
+body: JSON.stringify({ user_id: userId, avatar_emoji: emoji })
+});
+}
+function selectAvatarColor(color) {
+userAvatarColor = color;
+localStorage.setItem("mathAppAvatarColor", color);
+openAvatarPicker();
+updateAvatarUI();
+fetch("/api/user/profile", {
+method: "POST",
+headers: { "Content-Type": "application/json" },
+body: JSON.stringify({ user_id: userId, avatar_color: color })
+});
+}
+function updateAvatarUI() {
+var av = document.getElementById("profileAvatar");
+if (av) { av.textContent = userAvatar; av.style.background = userAvatarColor; }
+var brandIcon = document.querySelector(".brand-icon-sm");
+if (brandIcon) { brandIcon.textContent = "葫"; brandIcon.style.background = userAvatarColor; }
+}
+function closeAvatarPicker(e) {
+if (e && e.target !== document.getElementById("avatarPickerOverlay")) return;
+document.getElementById("avatarPickerOverlay").classList.remove("show");
+}
+function esc(text) {
+if (!text) return "";
+var div = document.createElement("div");
+div.textContent = text;
+return div.innerHTML;
+}
+function hashStr(str) {
+var hash = 0;
+for (var i = 0; i < str.length; i++) { hash = ((hash << 5) - hash) + str.charCodeAt(i); hash |= 0; }
+return Math.abs(hash);
+}
+function renderScriptSquare() {
+try {
+var heroCards = document.getElementById("ssHeroCards");
+if (!heroCards) return;
+heroCards.innerHTML = SCRIPTS.map(function (s) {
+var statusLabel = s.status === "beta" ? "开发中 · 抢先体验" : "已上线";
+return ''
++ '
'
++ '
'
++ '
' + s.icon + '
'
++ '
' + esc(s.name) + '
'
++ '
' + esc(s.desc) + '
'
++ '
' + s.meta + ' ' + statusLabel + '
'
++ '
';
+}).join("");
+var comingCards = document.getElementById("ssComingCards");
+if (comingCards) {
+comingCards.innerHTML = COMING_SOON.map(function (c) {
+return ''
++ '
' + c.icon + '
'
++ '
'
++ '
' + esc(c.name) + '
'
++ '
' + esc(c.desc) + '
'
++ '
敬请期待 '
++ '
';
+}).join("");
+}
+updateContinueCard();
+} catch (e) {
+console.error("剧本广场渲染失败:", e);
+}
+}
+function updateContinueCard() {
+try {
+var continueEl = document.getElementById("ssContinue");
+if (!continueEl) return;
+var vnSavedNode = vnState.currentNode;
+var simSavedNode = simState.currentNode;
+if (vnSavedNode && storyData && storyData.nodes && storyData.nodes[vnSavedNode]) {
+var ch = detectChapter(vnSavedNode);
+var chNames = ["","初入江湖","西部教习","数理论道","脑学初创","报国之路","暗流涌动","陋室修炼","真相之门"];
+document.getElementById("ssContinueTitle").textContent = "葫芦侠行 · 第" + ch + "章";
+document.getElementById("ssContinueSub").textContent = chNames[ch] || "";
+document.getElementById("ssContinueCard").onclick = function () { enterScript("huluxiaxing"); };
+continueEl.style.display = "block";
+} else if (simSavedNode && simulatorData && simulatorData.nodes && simulatorData.nodes[simSavedNode]) {
+var node = simulatorData.nodes[simSavedNode];
+document.getElementById("ssContinueTitle").textContent = "数学专业模拟器";
+document.getElementById("ssContinueSub").textContent = (node.stage || "") + (node.year ? " · 大" + ["","一","二","三","四"][node.year] : "");
+document.getElementById("ssContinueCard").onclick = function () { enterScript("simulator"); };
+continueEl.style.display = "block";
+} else {
+continueEl.style.display = "none";
+}
+} catch (e) {
+console.error("继续卡片更新失败:", e);
+}
+}
+function enterScript(scriptId) {
+hide("scriptSquare");
+if (scriptId === "huluxiaxing") {
+setAIContext("story", "葫芦侠行");
+hide("simulatorContainer");
+show("lifeSubNav");
+show("storyContainer");
+var btns = document.querySelectorAll(".life-sub-btn");
+btns.forEach(function (b) {
+b.classList.toggle("active", b.dataset.life === "story");
+});
+startStoryEngine();
+} else if (scriptId === "simulator") {
+setAIContext("simulator", "数学专业模拟器");
+hide("storyContainer");
+show("lifeSubNav");
+show("simulatorContainer");
+var btns = document.querySelectorAll(".life-sub-btn");
+btns.forEach(function (b) {
+b.classList.toggle("active", b.dataset.life === "simulator");
+});
+startSimulator();
+} else if (scriptId === "mathbti") {
+window.location.href = "/mathbti";
+}
+}
+function backToSquare() {
+hide("lifeSubNav");
+hide("storyContainer");
+hide("simulatorContainer");
+show("scriptSquare");
+renderScriptSquare();
+document.querySelector(".content-area").scrollTo({ top: 0, behavior: "smooth" });
+}
+function getSaveKey(scriptId) { return "mathAppSave_" + scriptId; }
+function loadAllSaves(scriptId) {
+try {
+var raw = localStorage.getItem(getSaveKey(scriptId));
+return raw ? JSON.parse(raw) : [];
+} catch (e) { return []; }
+}
+function saveAllSaves(scriptId, saves) {
+localStorage.setItem(getSaveKey(scriptId), JSON.stringify(saves));
+}
+function openSavePanel() {
+var scriptId = (currentLifeTab === "simulator") ? "simulator" : "huluxiaxing";
+document.getElementById("savePanelOverlay").classList.add("show");
+renderSaveSlots(scriptId);
+}
+function closeSavePanel() {
+document.getElementById("savePanelOverlay").classList.remove("show");
+}
+function renderSaveSlots(scriptId) {
+var slots = document.getElementById("saveSlots");
+var saves = loadAllSaves(scriptId);
+var MAX_SLOTS = 5;
+var html = "";
+for (var i = 0; i < MAX_SLOTS; i++) {
+var s = saves[i];
+if (s) {
+var icon = scriptId === "simulator" ? "🎓" : "📖";
+var name = (scriptId === "simulator") ? "数学专业模拟器" : "葫芦侠行";
+var meta = s.chapter || "";
+var date = s.savedAt ? s.savedAt.substring(0, 16).replace("T", " ") : "";
+html += '';
+html += '
' + icon + '
';
+html += '
';
+html += '
' + esc(name) + '
';
+html += '
' + esc(meta) + ' ' + date + '
';
+html += '
';
+html += '
';
+html += '💾 覆盖 ';
+html += '✕ ';
+html += '
';
+} else {
+html += '';
+html += '+ 空槽位 ' + (i + 1) + ' — 点击保存 ';
+html += '
';
+}
+}
+slots.innerHTML = html;
+}
+function saveToSlot(scriptId, slotIndex) {
+var saveData = {
+savedAt: new Date().toISOString(),
+scriptId: scriptId
+};
+if (scriptId === "huluxiaxing") {
+saveData.chapter = detectChapter(vnState.currentNode) ? "第" + detectChapter(vnState.currentNode) + "章" : "";
+saveData.nodeId = vnState.currentNode;
+saveData.stats = JSON.parse(JSON.stringify(vnState.stats));
+saveData.favorability = JSON.parse(JSON.stringify(vnState.favorability || {}));
+saveData.vnState = JSON.parse(JSON.stringify(vnState));
+} else {
+var node = simulatorData && simulatorData.nodes && simulatorData.nodes[simState.currentNode];
+saveData.chapter = (node ? (node.stage || "大" + node.year) : "");
+saveData.nodeId = simState.currentNode;
+saveData.stats = JSON.parse(JSON.stringify(simState.stats));
+saveData.simState = JSON.parse(JSON.stringify(simState));
+}
+var saves = loadAllSaves(scriptId);
+saves[slotIndex] = saveData;
+saveAllSaves(scriptId, saves);
+renderSaveSlots(scriptId);
+showToast("存档成功 ✓");
+}
+function overwriteSlot(scriptId, slotIndex) {
+saveToSlot(scriptId, slotIndex);
+}
+function loadFromSlot(scriptId, slotIndex) {
+var saves = loadAllSaves(scriptId);
+var saveData = saves[slotIndex];
+if (!saveData) return;
+if (scriptId === "huluxiaxing") {
+if (saveData.vnState) {
+vnState = JSON.parse(JSON.stringify(saveData.vnState));
+localStorage.setItem("mathAppVNSave", JSON.stringify(vnState));
+}
+hide("scriptSquare"); hide("simulatorContainer");
+show("lifeSubNav"); show("storyContainer");
+document.querySelectorAll(".life-sub-btn").forEach(function (b) { b.classList.toggle("active", b.dataset.life === "story"); });
+currentLifeTab = "story";
+startStoryEngine();
+} else {
+if (saveData.simState) {
+simState = JSON.parse(JSON.stringify(saveData.simState));
+saveSimState();
+}
+hide("scriptSquare"); hide("storyContainer");
+show("lifeSubNav"); show("simulatorContainer");
+document.querySelectorAll(".life-sub-btn").forEach(function (b) { b.classList.toggle("active", b.dataset.life === "simulator"); });
+currentLifeTab = "simulator";
+startSimulator();
+}
+closeSavePanel();
+showToast("读档成功 ✓");
+}
+function deleteSlot(scriptId, slotIndex) {
+var saves = loadAllSaves(scriptId);
+saves[slotIndex] = null; // 保留空位
+saveAllSaves(scriptId, saves);
+renderSaveSlots(scriptId);
+showToast("已删除存档");
+}
+function saveVNState() {
+var scriptId = "huluxiaxing";
+saveToSlot(scriptId, 0);
+}
+const ALL_ENDINGS = [
+{ id: "truth", name: "真相守望", icon: "🔮", desc: "成为葫芦数学世界首位居民与共创者" },
+{ id: "master", name: "数理宗师", icon: "📚", desc: "葫芦侠成为一代宗师,数堂发扬光大" },
+{ id: "wander", name: "逍遥江湖", icon: "🍃", desc: "远离纷争,在江湖中自由游历" },
+{ id: "sail", name: "出海远航", icon: "⛵", desc: "随欧阳侠远赴新大陆,开创东西方交流" },
+{ id: "hero", name: "悲情英雄", icon: "⚔️", desc: "坚守信念,孤身面对审查与命运" },
+{ id: "fall", name: "堕落之路", icon: "🌑", desc: "在权力与诱惑中迷失自我" },
+{ id: "jail", name: "冤狱", icon: "⛓", desc: "被冤入狱,含恨而终" },
+{ id: "void", name: "虚无", icon: "🌫", desc: "发现真相后精神崩溃,消失于江湖" },
+{ id: "math_phd", name: "数学博士", icon: "🔬", desc: "坚持学术之路,走向研究巅峰" },
+{ id: "quant", name: "量化交易员", icon: "💰", desc: "用数学模型在金融市场叱咤风云" },
+{ id: "teacher", name: "高中教师", icon: "📝", desc: "回到家乡,培养下一代数学人才" },
+{ id: "data_sci", name: "数据科学家", icon: "📊", desc: "在互联网公司用数据改变世界" }
+];
+function getUnlockedEndings() {
+try {
+var raw = localStorage.getItem("mathAppUnlockedEndings");
+return raw ? JSON.parse(raw) : [];
+} catch (e) { return []; }
+}
+function unlockEnding(endingId) {
+var endings = getUnlockedEndings();
+if (endings.indexOf(endingId) < 0) {
+endings.push(endingId);
+localStorage.setItem("mathAppUnlockedEndings", JSON.stringify(endings));
+}
+}
+function openGallery() {
+document.getElementById("galleryOverlay").classList.add("show");
+renderGallery();
+}
+function closeGallery() {
+document.getElementById("galleryOverlay").classList.remove("show");
+}
+function renderGallery() {
+var unlocked = getUnlockedEndings();
+var progressEl = document.getElementById("galleryProgress");
+progressEl.textContent = unlocked.length + "/" + ALL_ENDINGS.length;
+var grid = document.getElementById("galleryGrid");
+grid.innerHTML = ALL_ENDINGS.map(function (e) {
+var isUnlocked = unlocked.indexOf(e.id) >= 0;
+if (isUnlocked) {
+return ''
++ '
' + e.icon + '
'
++ '
' + esc(e.name) + '
'
++ '
' + esc(e.desc) + '
'
++ '
';
+} else {
+return ''
++ '
🔒
'
++ '
???
'
++ '
未解锁
'
++ '
';
+}
+}).join("");
+}
diff --git a/static/js/desktop.js b/static/js/desktop.js
new file mode 100644
index 0000000..583d9a1
--- /dev/null
+++ b/static/js/desktop.js
@@ -0,0 +1,1041 @@
+// ===== State =====
+const API_BASE='',USER_ID=localStorage.getItem('mb_user_id')||('mb_'+Math.random().toString(36).slice(2,10));
+localStorage.setItem('mb_user_id',USER_ID);
+let CP='home',allV=[],allK=[],kT=[],vD=[],cD='',curDim='',cT='',sD=null,sVN={currentNode:null,stats:{shuli:10,xiayi:10,xinzhi:10},fav:{}},simD=null,simS={currentNode:null,stats:{gpa:3,interest:0,skill:0}},favC={},chatCtx={},tt=null;
+let cardData=null,cardCache=null,_gachaBusy=false;
+let cardState={owned:[],draws:0,lastDaily:'',knowledgeRead:0,drawHistory:[]};
+let _lang=localStorage.getItem('mb_lang')||'zh';
+
+// ===== i18n =====
+const I18N={zh:{home:'首页',discover:'发现',toolbox:'工具箱',knowledge:'知识卡片',story:'葫芦侠行',simulator:'数学人生',favorites:'我的收藏',profile:'个人中心',search:'搜索内容...',guest:'游客用户',
+ heroTitle:'葫芦数学 · 用玩的方式打开数学',heroSub:'数学不是公式,是你理解世界的方式',heroCTA:'开始探索 →',
+ exploreTitle:'探索数学的万千可能',tryTitle:'试试这些',
+ mbtiName:'MathBTI 数学人格',mbtiDesc:'12题发现你的数学人格,领养专属数学小人 →',
+ toolboxName:'数学工具箱',toolboxDesc:'口算竞赛 · 计算器 · 符号查询 · 函数绘图 →',
+ storyName:'葫芦侠行',storyDesc:'北宋数学武侠 · 8章93节点 · 8种结局 →',
+ simName:'数学人生',simDesc:'四大主题 · 更多数学人生敬请期待 →',
+ videoName:'发现视频',videoDesc:'五维能力地图 · 看视频点亮成长',cardName:'知识卡片',cardDesc:'8个数学话题',
+ aiName:'AI 助教',aiDesc:'随时解答数学疑问',
+ petTip:'每天使用工具箱和模拟器获取经验升级',
+ dailyFactPrefix:'💡 数学冷知识:',
+ discoverGreet:'Hello there. 这里收集了来自各专业志愿者的数学科普视频。',
+ mapTitle:'五维能力地图',mapSub:'观看视频,点亮数学小人的五种能力',mapHint:'点击地图节点,进入对应维度的视频流',
+ dimAll:'全部视频',dimVision:'数学眼光',dimHumanity:'数学人文',dimDetective:'数学侦探',dimModeling:'数学建模',dimConnection:'数学联结',
+ claimVideo:'看完打卡',claimDone:'已学习',claimNeedPet:'先做 MathBTI 领养数学小人,再看视频点亮能力',
+ filterAll:'全部',noVideo:'暂无视频',noCard:'暂无卡片',noResult:'无结果',
+ loadFailVideo:'视频加载失败',loadFailCard:'卡片加载失败',loadFailProfile:'加载失败',
+ loadMore:'下拉加载更多…',loadEnd:'— 已经到底啦 · 共 ',loadEndSuf:' 个视频 —',
+ close:'关闭',save:'保存',saveOK:'保存成功',saveFail:'保存失败',
+ toolboxHeader:'工具箱',toolboxSub:'动手做数学——口算、计算、查询、绘图',
+ toolMathrace:'口算竞赛',toolMathraceDesc:'限时60秒,挑战你的心算速度',
+ toolCalc:'计算器',toolCalcDesc:'支持加减乘除、幂运算、三角函数',
+ toolSym:'数学符号查询',toolSymDesc:'点击符号查看含义和LaTeX写法',
+ toolGraph:'函数图形绘图',toolGraphDesc:'输入 f(x),实时绘制函数曲线',
+ knowledgeHeader:'知识卡片',knowledgeSub:'轻量数学发现,每天一个新概念',
+ storyHeader:'葫芦侠行',storySub:'北宋数学武侠互动文字冒险 · 8章93节点 · 8+种结局',
+ storyLoadFail:'故事加载失败',storyNodeNotFound:'节点未找到',
+ simHeader:'数学人生',simSub:'四大主题 · 更多数学人生敬请期待',
+ simLoadFail:'模拟器加载失败',simReset:'重新开始',
+ favHeader:'我的收藏',favSub:'已收藏的视频和知识卡片',
+ favEmpty:'暂无收藏',favEmptyHint:'在发现页点击 🤍 即可收藏',
+ profileHeader:'个人中心',profileEdit:'编辑资料',
+ profileAvatar:'头像:',profileNick:'昵称:',
+ profileWatchCount:'观看次数',profileFavCount:'收藏数量',
+ aiHeader:'AI 数学助教',aiWelcome:'你好!我是葫芦数学的AI助教 👋 试试问我:学数学对学医有什么用?',
+ aiPlaceholder:'输入问题...',aiOffline:'抱歉,AI暂时无法回复',aiNetworkErr:'网络异常,请稍后重试',
+ calcErr:'表达式有误',calcPlaceholder:'输入表达式 如 2+3*4',
+ graphPlaceholder:'如 Math.sin(x) 或 x*x',graphBtn:'绘制',
+ mrTitle:'准备好了吗?',mrPlaceholder:'输入答案',mrTimeout:'⏰ 时间到!得分:',mrCorrect:'正确',mrRight:'✅ 正确!+10分',
+ symDetail:'👆 点击符号查看详情',
+ mbtiFloat:'测测你的数学人格',
+ aiAskBtn:'问问AI',aiAskSuffix:'是什么?',
+ petLevelUp:'升级啦!Lv.{n}',
+ staticHeroTitle:'葫芦数学 · 用玩的方式打开数学',staticHeroSub:'数学不是公式,是你理解世界的方式',staticHeroCTA:'开始探索 →',
+ staticExploreTitle:'探索数学的万千可能',staticTryTitle:'试试这些',
+ staticDiscoverGreet:'Hello there. 这里收集了来自各专业志愿者的数学科普视频。',
+ staticToolboxTitle:'🧰 工具箱',staticToolboxSub:'动手做数学——口算、计算、查询、绘图',
+ staticToolMathrace:'口算竞赛',staticToolMathraceDesc:'限时60秒,挑战你的心算速度',
+ staticToolCalc:'计算器',staticToolCalcDesc:'支持加减乘除、幂运算、三角函数',
+ staticToolSym:'数学符号查询',staticToolSymDesc:'点击符号查看含义和LaTeX写法',
+ staticToolGraph:'函数图形绘图',staticToolGraphDesc:'输入 f(x),实时绘制函数曲线',
+ staticKnowledgeTitle:'📚 知识卡片',staticKnowledgeSub:'轻量数学发现,每天一个新概念',
+ staticStoryTitle:'⚔️ 葫芦侠行',staticStorySub:'北宋数学武侠互动文字冒险 · 8章93节点 · 8+种结局',
+ staticSimTitle:'🧬 数学人生',staticSimSub:'四大主题 · 更多数学人生敬请期待',
+ staticFavTitle:'❤️ 我的收藏',staticFavSub:'已收藏的视频和知识卡片',
+ staticProfileTitle:'👤 个人中心',
+ staticAIToggleTitle:'AI数学助教',staticAIHeader:'💬 AI 数学助教',
+ staticAIWelcome:'你好!我是葫芦数学的AI助教 👋 试试问我:学数学对学医有什么用?',
+ staticAIPlaceholder:'输入问题...',staticMbtiTooltip:'测测你的数学人格'},
+ en:{home:'Home',discover:'Discover',toolbox:'Toolbox',knowledge:'Cards',story:'HuLu Quest',simulator:'Math Life',favorites:'Favorites',profile:'Profile',search:'Search...',guest:'Guest',
+ heroTitle:'Hulu Math · Play your way into math',heroSub:'Math isn\'t formulas. It\'s how you make sense of things.',heroCTA:'Start Exploring →',
+ exploreTitle:'Explore Math',tryTitle:'Try These',
+ mbtiName:'MathBTI Personality Test',mbtiDesc:'12 questions. One math soul. Meet your sprite. →',
+ toolboxName:'Math Toolbox',toolboxDesc:'Mental Math · Calculator · Symbol Lookup · Graph →',
+ storyName:'HuLu Quest',storyDesc:'A Song Dynasty math wuxia · 8 chapters · 8+ endings →',
+ simName:'Math Life',simDesc:'Four themes. More math lives coming soon. →',
+ videoName:'Discover Videos',videoDesc:'Five-dimension map · watch and grow',cardName:'Knowledge Cards',cardDesc:'8 math topics',
+ aiName:'AI Tutor',aiDesc:'Ask anything. No judgment.',
+ petTip:'Come back daily to earn EXP and level up your sprite.',
+ dailyFactPrefix:'💡 Math Trivia: ',
+ discoverGreet:'A hand-picked collection of math videos, curated by volunteers from across the sciences.',
+ mapTitle:'Five-Dimension Ability Map',mapSub:'Watch videos and light up your math sprite\'s five abilities',mapHint:'Tap a map node to enter its video stream',
+ dimAll:'All videos',dimVision:'Math Vision',dimHumanity:'Math Humanity',dimDetective:'Math Detective',dimModeling:'Math Modeling',dimConnection:'Math Connection',
+ claimVideo:'Finish & earn',claimDone:'Learned',claimNeedPet:'Take MathBTI first to adopt a math sprite, then watch videos to grow it',
+ filterAll:'All',noVideo:'No videos yet',noCard:'No cards yet',noResult:'No results',
+ loadFailVideo:'Couldn\'t load videos',loadFailCard:'Couldn\'t load cards',loadFailProfile:'Couldn\'t load',
+ loadMore:'Scroll for more…',loadEnd:'— That\'s all of them · ',loadEndSuf:' videos —',
+ close:'Close',save:'Save',saveOK:'Saved',saveFail:'Couldn\'t save',
+ toolboxHeader:'Toolbox',toolboxSub:'Get your hands dirty — race the clock, crunch numbers, explore symbols, draw curves',
+ toolMathrace:'Mental Math',toolMathraceDesc:'60 seconds. How high can you score?',
+ toolCalc:'Calculator',toolCalcDesc:'Quick arithmetic, powers, trig — whatever you need.',
+ toolSym:'Symbol Lookup',toolSymDesc:'Tap a symbol to see what it means and how to write it in LaTeX.',
+ toolGraph:'Function Grapher',toolGraphDesc:'Type any f(x) and watch its curve appear in real time.',
+ knowledgeHeader:'Knowledge Cards',knowledgeSub:'One idea at a time. Bite-sized math, built for curiosity.',
+ storyHeader:'HuLu Quest',storySub:'A math-fueled wuxia set in the Song Dynasty. 8 chapters. 8+ endings.',
+ storyLoadFail:'Couldn\'t load the story',storyNodeNotFound:'Node not found',
+ simHeader:'Math Life',simSub:'Four themes. More math lives coming soon.',
+ simLoadFail:'Couldn\'t load the simulator',simReset:'Start fresh',
+ favHeader:'My Favorites',favSub:'Your saved videos and cards',
+ favEmpty:'Nothing saved yet',favEmptyHint:'Tap 🤍 on any video or card to save it here.',
+ profileHeader:'Profile',profileEdit:'Edit Profile',
+ profileAvatar:'Avatar:',profileNick:'Nickname:',
+ profileWatchCount:'Videos watched',profileFavCount:'Favorites',
+ aiHeader:'AI Math Tutor',aiWelcome:'Hey! I\'m your Hulu Math AI tutor 👋 Try asking: "Why do we even need calculus?"',
+ aiPlaceholder:'Ask anything...',aiOffline:'The AI is taking a quick break. Try again in a moment.',aiNetworkErr:'Looks like you\'re offline. Check your connection and try again.',
+ calcErr:'That doesn\'t look right',calcPlaceholder:'e.g. 2+3*4',
+ graphPlaceholder:'e.g. Math.sin(x) or x*x',graphBtn:'Plot',
+ mrTitle:'Ready?',mrPlaceholder:'Type your answer',mrTimeout:'⏰ Time\'s up! You scored ',mrCorrect:'right',mrRight:'✅ Correct! +10',
+ symDetail:'👆 Tap any symbol to learn more',
+ mbtiFloat:'Discover your Math Personality',
+ aiAskBtn:'Ask AI',aiAskSuffix:'?',
+ petLevelUp:'Level up! Now Lv.{n}',
+ staticHeroTitle:'Hulu Math · Play your way into math',staticHeroSub:'Math isn\'t formulas. It\'s how you make sense of things.',staticHeroCTA:'Start Exploring →',
+ staticExploreTitle:'Explore Math',staticTryTitle:'Try These',
+ staticDiscoverGreet:'A hand-picked collection of math videos, curated by volunteers from across the sciences.',
+ staticToolboxTitle:'🧰 Toolbox',staticToolboxSub:'Get your hands dirty — race the clock, crunch numbers, explore symbols, draw curves',
+ staticToolMathrace:'Mental Math',staticToolMathraceDesc:'60 seconds. How high can you score?',
+ staticToolCalc:'Calculator',staticToolCalcDesc:'Quick arithmetic, powers, trig — whatever you need.',
+ staticToolSym:'Symbol Lookup',staticToolSymDesc:'Tap a symbol to see what it means and how to write it in LaTeX.',
+ staticToolGraph:'Function Grapher',staticToolGraphDesc:'Type any f(x) and watch its curve appear in real time.',
+ staticKnowledgeTitle:'📚 Knowledge Cards',staticKnowledgeSub:'One idea at a time. Bite-sized math, built for curiosity.',
+ staticStoryTitle:'⚔️ HuLu Quest',staticStorySub:'A math-fueled wuxia set in the Song Dynasty. 8 chapters. 8+ endings.',
+ staticSimTitle:'🧬 Math Life',staticSimSub:'Four themes. More math lives coming soon.',
+ staticFavTitle:'❤️ My Favorites',staticFavSub:'Your saved videos and cards',
+ staticProfileTitle:'👤 Profile',
+ staticAIToggleTitle:'AI Math Tutor',staticAIHeader:'💬 AI Math Tutor',
+ staticAIWelcome:'Hey! I\'m your Hulu Math AI tutor 👋 Try asking: "Why do we even need calculus?"',
+ staticAIPlaceholder:'Ask anything...',
+ staticMbtiTooltip:'Discover your Math Personality'}};
+
+function t(k){const d=I18N[_lang];return (d&&d[k])||(I18N.zh[k]||k);}
+
+function rI18n(){
+ document.querySelectorAll('[data-i18n]').forEach(el=>{
+ const k=el.getAttribute('data-i18n');
+ const v=t(k);
+ if(el.tagName==='INPUT'){el.placeholder=v}
+ else if(el.hasAttribute('title')&&el.getAttribute('data-i18n-type')==='title'){el.title=v}
+ else{el.innerHTML=v}
+ });
+}
+
+function switchLang(l){
+ _lang=l;localStorage.setItem('mb_lang',l);
+ document.querySelectorAll('.lang-btn').forEach(b=>b.classList.toggle('active',b.dataset.lang===l));
+ document.getElementById('globalSearch').placeholder=t('search');
+ rI18n();
+ navigateTo(CP);
+ api('/api/user/profile?user_id='+USER_ID).then(r=>{if(r.code===0&&r.data){document.getElementById('sidebarName').textContent=r.data.nickname||t('guest')}}).catch(()=>{});
+}
+
+// ===== Sidebar =====
+function toggleSidebar(){document.getElementById('sidebar').classList.toggle('open');document.getElementById('sidebarOverlay').classList.toggle('active')}
+function closeSidebar(){document.getElementById('sidebar').classList.remove('open');document.getElementById('sidebarOverlay').classList.remove('active')}
+
+// ===== Global Search =====
+let searchTimeout;
+function handleGlobalSearch(){
+ clearTimeout(searchTimeout);
+ searchTimeout=setTimeout(()=>{
+ const q=document.getElementById('globalSearch').value.trim().toLowerCase();
+ const el=document.getElementById('searchResults');
+ if(!q||q.length<1){el.classList.remove('show');return}
+ const results=[];
+ allV.forEach(v=>{if((v.title||'').toLowerCase().includes(q)||(v.author||'').toLowerCase().includes(q)||(v.discipline||'').toLowerCase().includes(q))results.push({t:t('videoTitle')||'Video',title:v.title,id:v.id,type:'video'})});
+ allK.forEach(k=>{if((k.title||'').toLowerCase().includes(q)||(k.description||'').toLowerCase().includes(q))results.push({t:t('cardLabel')||'Card',title:k.title,id:k.id,type:'knowledge'})});
+ if(results.length===0){el.innerHTML=''+t('noResult')+'
';el.classList.add('show');return}
+ el.innerHTML=results.slice(0,8).map(r=>''+(r.t==='视频'?'Video':r.t==='知识'?'Card':r.t)+' '+esc(r.title)+'
').join('');
+ el.classList.add('show');
+ },200);
+}
+function searchNavigate(type,id){
+ document.getElementById('globalSearch').value='';document.getElementById('searchResults').classList.remove('show');
+ if(type==='video'){navigateTo('discover');setTimeout(()=>openVideo(id,'','','','',''),500)}
+ else if(type==='knowledge'){navigateTo('discover');setTimeout(()=>openKnowledgeDetail(id),500)}
+}
+document.addEventListener('click',e=>{if(!e.target.closest('.sidebar-search'))document.getElementById('searchResults').classList.remove('show')});
+
+// ===== AI Panel =====
+function toggleAIPanel(){
+ var panel=document.getElementById('aiPanel');
+ if(!panel)return;
+ var modal=document.getElementById('toolModal');
+ if(modal)modal.classList.remove('show');
+ if(panel.classList.contains('open')){
+ panel.classList.remove('open');
+ }else{
+ panel.classList.add('open');
+ }
+}
+
+// ===== API =====
+const _apiCache={},_CACHEABLE=['/api/videos','/api/knowledge','/api/story','/api/simulator','/api/math_personality'];
+async function api(p,o={}){const u=API_BASE+p,c={headers:{'Content-Type':'application/json'},...o};if(c.body&&typeof c.body==='object')c.body=JSON.stringify(c.body);const isGet=!c.method;if(isGet&&_CACHEABLE.some(x=>p.indexOf(x)===0)&&_apiCache[u])return _apiCache[u];const r=await fetch(u,c);const j=await r.json();if(isGet&&_CACHEABLE.some(x=>p.indexOf(x)===0))_apiCache[u]=j;return j}
+
+// ===== Route =====
+function navigateTo(p){
+ CP=p;closeSidebar();closeToolModal();document.getElementById('searchResults').classList.remove('show');
+ // 离开首页时停止 Hero 动画循环,避免空转耗电
+ if(p!=='home'&&heroAnimId){cancelAnimationFrame(heroAnimId);heroAnimId=null}
+ document.querySelectorAll('.nav-item').forEach(b=>b.classList.toggle('active',b.dataset.page===p));
+ document.querySelectorAll('.page').forEach(pg=>pg.classList.remove('active'));
+ const t=document.getElementById('page-'+p);if(t)t.classList.add('active');
+ document.getElementById('mainContent').scrollTop=0;
+ if(p==='home')loadHome();else if(p==='discover')loadDiscover();else if(p==='toolbox')loadToolbox();
+ else if(p==='knowledge')loadKnowledgePage();else if(p==='story')loadStory();else if(p==='simulator')loadSimulator();else if(p==='favorites')loadFavorites();else if(p==='profile')loadProfile();else if(p==='cards')loadCardsPage();
+}
+
+// ===== Home (Desmos-style) =====
+let heroAnimId=null;
+async function loadHome(){
+ chatCtx={};
+ // Hero canvas animation
+ setTimeout(drawHeroCanvas,100);
+ // Pet
+ renderPetCard();
+ // Feature cards (2x2)
+ document.getElementById('homeFeatureCards').innerHTML=[
+ {i:'🧬',n:t('mbtiName'),d:t('mbtiDesc'),click:"window.open('/mathbti','_blank')"},
+ {i:'🧰',n:t('toolboxName'),d:t('toolboxDesc'),click:"navigateTo('toolbox')"},
+ {i:'⚔️',n:t('storyName'),d:t('storyDesc'),click:"navigateTo('story')"},
+ {i:'🎮',n:t('simName'),d:t('simDesc'),click:"navigateTo('simulator')"}
+ ].map(c=>'').join('');
+ // Extra cards (1x3)
+ document.getElementById('homeExtraCards').innerHTML=[
+ {i:'📺',n:t('videoName'),d:t('videoDesc'),click:"navigateTo('discover')"},
+ {i:'📚',n:t('cardName'),d:t('cardDesc'),click:"navigateTo('knowledge')"},
+ {i:'💬',n:t('aiName'),d:t('aiDesc'),click:"toggleAIPanel()"}
+ ].map(c=>'').join('');
+ // Daily fact
+ const facts=['π的小数点后第762位开始连续出现六个9,被称为「费曼点」。','世界上最大的已知质数有超过4100万位数字。','哥德巴赫猜想:每个大于2的偶数都可以写成两个质数之和——至今未被证明。','四色定理:任何地图只需4种颜色就能让相邻区域颜色不同。1976年用计算机证明。','莫比乌斯带只有一面——拿一条纸带,扭转180°后粘起来试试。'];
+ document.getElementById('homeDailyFact').innerHTML=t('dailyFactPrefix')+facts[Math.floor(Math.random()*facts.length)];
+ rCG();
+}
+function drawHeroCanvas(){
+ const c=document.getElementById('heroCanvas');if(!c)return;
+ // 防止重复进入首页叠加多个动画循环
+ if(heroAnimId){cancelAnimationFrame(heroAnimId);heroAnimId=null}
+ const ctx=c.getContext('2d'),w=c.width,h=c.height;
+ let t=0;
+ function frame(){
+ ctx.clearRect(0,0,w,h);
+ ctx.strokeStyle='#D6D1CA';ctx.lineWidth=1;
+ for(let i=0;i0.7){
+ ctx.save();ctx.globalAlpha=Math.min(1,(Math.sin(t*0.3)-0.7)/0.3*0.5);
+ ctx.strokeStyle='#A8907E';ctx.lineWidth=1;
+ const gx=w-60,gy=h-30;
+ ctx.beginPath();
+ // 上葫芦 (小)
+ ctx.ellipse(gx,gy-28,12,9,0,0,Math.PI*2);
+ // 下葫芦 (大)
+ ctx.ellipse(gx,gy,16,13,0,0,Math.PI*2);
+ ctx.stroke();
+ ctx.restore();
+ }
+ t+=0.02;heroAnimId=requestAnimationFrame(frame);
+ }
+ frame();
+}
+
+// ===== Discover: Five-Dimension Ability Map =====
+const DIM5=[
+ {id:'vision',key:'眼光',label:'数学眼光',short:'眼光',icon:'👁',color:'#5B6AF0',x:50,y:12,desc:'洞察本质的直觉,一眼看穿背后的数学结构。',rep:'庞加莱'},
+ {id:'humanity',key:'人文',label:'数学人文',short:'人文',icon:'📜',color:'#B06A3B',x:17,y:36,desc:'思想溯源的厚度,理解数学背后的人与史。',rep:'希帕提娅'},
+ {id:'detective',key:'侦探',label:'数学侦探',short:'侦探',icon:'🔍',color:'#2E7D72',x:24,y:86,desc:'逻辑溯源的严谨,在迷雾中找到唯一真相。',rep:'图灵'},
+ {id:'modeling',key:'建模',label:'数学建模',short:'建模',icon:'🏗️',color:'#C47F3A',x:76,y:86,desc:'解决现实的力量,把公式变成改变世界的武器。',rep:'冯·诺依曼'},
+ {id:'connection',key:'联结',label:'数学联结',short:'联结',icon:'🔗',color:'#7B5EA7',x:83,y:36,desc:'跨界融合的通感,连接数学内外的一切。',rep:'牛顿'}
+];
+const DIM_EXTRA_VIDEOS=[
+ {id:9101,module:'数人',sub_category:'数学人文',discipline:'数学人文',discipline_icon:'📜',title:'赵爽弦图:《周髀算经》里的人类智慧',video_url:'https://www.bilibili.com/video/BV1example301',description:'从勾股定理的证明看数学如何成为文明的火种。赵爽的弦图,是数学人文的第一张草图。',author:'数学人文馆',duration_min:6},
+ {id:9102,module:'数人',sub_category:'数学人文',discipline:'数学人文',discipline_icon:'📜',title:'希帕提娅:为理性光芒守护到最后一刻',video_url:'https://www.bilibili.com/video/BV1example302',description:'一位女数学家在动荡年代坚持讲授数学,理解她,就理解了数学背后的信仰。',author:'数学人文馆',duration_min:7},
+ {id:9103,module:'数人',sub_category:'数学人文',discipline:'数学人文',discipline_icon:'📜',title:'图灵与恩尼格玛:密码背后的人与史',video_url:'https://www.bilibili.com/video/BV1example303',description:'从密码战争到计算机科学,图灵用逻辑改变了历史走向。',author:'数学人文馆',duration_min:8},
+ {id:9104,module:'数说',sub_category:'几何直觉',discipline:'数学眼光',discipline_icon:'👁',title:'向日葵、贝壳与斐波那契:用数学眼光看自然',video_url:'https://www.bilibili.com/video/BV1example304',description:'当你看一朵花不再只看到美,而是看到数列与结构,数学眼光就亮了。',author:'数学眼光馆',duration_min:5},
+ {id:9105,module:'数说',sub_category:'几何直觉',discipline:'数学眼光',discipline_icon:'👁',title:'城市建筑里的对称与黄金比例',video_url:'https://www.bilibili.com/video/BV1example305',description:'从古典建筑到现代地标,用数学眼光捕捉城市里隐藏的几何秩序。',author:'数学眼光馆',duration_min:5},
+ {id:9106,module:'数说',sub_category:'几何直觉',discipline:'数学眼光',discipline_icon:'👁',title:'庞加莱猜想:直觉如何带人类走进更高维度',video_url:'https://www.bilibili.com/video/BV1example306',description:'像庞加莱一样,在踏上公交车的一瞬间,看见隐藏在空间里的结构。',author:'数学眼光馆',duration_min:6}
+];
+function videoDimKey(v){
+ const t=(v.title||'')+(v.sub_category||'')+(v.discipline||'');
+ if(/人文|赵爽|周髀|希帕提娅|数学史|恩尼格玛|战火|信仰|守护/.test(t))return'人文';
+ if(/地图|画不准|高斯绝妙|斐波那契|向日葵|黄金比例|对称|眼光|直觉|庞加莱/.test(t))return'眼光';
+ if(/强化学习|AlphaGo|人工智能|神经网络|深度学习|矩阵|GPU/.test(t))return'联结';
+ if(/密码|网络安全|统计|平均数|彩票|费米|图论|六度|二进制|卡尔曼|PLC|布尔|集成电路|雷达|多普勒|编译|形式语言|B\+树|数据库/.test(t))return'侦探';
+ if(/金融|风险|经济|市场|供应链|线性规划|物流|回归|建模|数学模型|反应动力学|传质|材料|气候变化|污水处理|质量平衡|种群|扩散|高斯模型|中子|AHP|Matlab|Python|数模|最优控制|机器人控制|自行车|齿轮|振动|有限元/.test(t))return'建模';
+ if(/傅里叶|麦克斯韦|采样|拉东|心电|DNA|信息论|水平集|渐开线|薛定谔|边界层|熵增|太阳能|空间群|光刻|半导体|计算机视觉|几何变换/.test(t))return'联结';
+ return'联结';
+}
+function dimInfo(v){return DIM5.find(d=>d.key===videoDimKey(v))||DIM5[4]}
+function mergeAllV(list){
+ const arr=(list||[]).slice();
+ DIM_EXTRA_VIDEOS.forEach(e=>{if(!arr.some(v=>v.id===e.id))arr.push(e)});
+ const dm={};
+ arr.forEach(v=>{if(v.discipline&&!dm[v.discipline])dm[v.discipline]={discipline:v.discipline,discipline_icon:v.discipline_icon||'·'}});
+ vD=Object.values(dm);
+ return arr;
+}
+async function loadDiscover(){
+ chatCtx={};
+ if(favP){await favP;favP=null}
+ if(!allV.length){
+ renderSkeletons('discoverVideos',6);
+ try{
+ const r=await api('/api/videos');
+ if(r.code===0)allV=mergeAllV(r.data||[]);
+ }catch(e){
+ document.getElementById('discoverVideos').innerHTML=''+t('loadFailVideo')+'
';
+ allV=mergeAllV([]);
+ }
+ }else{
+ allV=mergeAllV(allV);
+ }
+ rDF();rDimTabs();rDMap();rVCNP('discoverVideos',filterVideos());
+}
+function dimVideoCount(k){return allV.filter(v=>dimInfo(v).key===k).length}
+function filterVideos(){return allV.filter(v=>(!curDim||dimInfo(v).key===curDim)&&(!cD||v.discipline===cD))}
+function rDMap(){
+ const el=document.getElementById('dimMap');if(!el)return;
+ const pet=getPet();
+ const lines=DIM5.map((d,i)=>{
+ const n=DIM5[(i+1)%DIM5.length];
+ return ' ';
+ }).join('');
+ const spokes=DIM5.map(d=>' ').join('');
+ el.innerHTML=
+ ''+t('mapTitle')+'
'+t('mapSub')+'
'+DIM5.map(d=>' '+esc(d.label)+' · '+dimVideoCount(d.key)+' ').join('')+'
'+
+ ''+spokes+lines+' '+
+ '
葫 数学小人
'+
+ DIM5.map(d=>{const v=pet&&pet.dims?Math.min(100,Math.max(0,Number(pet.dims[d.key])||0)):null;return '
'+d.icon+' '+esc(d.label)+' '+(v===null?'--':v)+' '}).join('')+
+ '
'+
+ '';
+}
+function rDimTabs(){
+ const el=document.getElementById('dimTabs');if(!el)return;
+ let h=''+t('filterAll')+''+allV.length+' ';
+ DIM5.forEach(d=>{h+=''+d.icon+' '+esc(d.label)+''+dimVideoCount(d.key)+' '});
+ el.innerHTML=h;
+}
+function fDim(d){curDim=d;rDimTabs();rDMap();rVCNP('discoverVideos',filterVideos())}
+function scrollToVideos(){const el=document.getElementById('discoverVideos');if(el)el.scrollIntoView({behavior:'smooth',block:'start'})}
+function rDF(){let h=''+t('filterAll')+' ';vD.forEach(d=>{h+=''+(d.discipline_icon||'')+' '+esc(d.discipline)+' '});document.getElementById('discoverFilters').innerHTML=h}
+function fV(d){cD=d;rDF();rVCNP('discoverVideos',filterVideos())}
+// ===== 视频卡片网格:无限滚动(每次12条) =====
+const VC_PAGE=12;
+let vcList=[],vcCount=0,vcObserver=null;
+const UP_COLORS=['#5B6AF0','#818CF8','#F59E0B','#10B981','#EF4444','#8B5CF6','#EC4899','#14B8A6'];
+function hashStr(s){let h=0;for(let i=0;i=10000?(n/10000).toFixed(1)+'万':''+n}
+function fmtDur(m){m=parseInt(m)||0;return(m<10?'0'+m:''+m)+':00'}
+function rVCNP(cid,vids){
+ const c=document.getElementById(cid);
+ vcList=vids||[];vcCount=0;c.innerHTML='';
+ if(!vcList.length){c.innerHTML=''+t('noVideo')+'
';return}
+ appendVC(cid);
+}
+function appendVC(cid){
+ const c=document.getElementById(cid);
+ const batch=vcList.slice(vcCount,vcCount+VC_PAGE);
+ if(!batch.length)return;
+ vcCount+=batch.length;
+ const old=document.getElementById('npLoadHint');if(old)old.remove();
+ c.insertAdjacentHTML('beforeend',batch.map(buildVC).join(''));
+ if(vcCount'+t('loadMore')+'');
+ if(!vcObserver){vcObserver=new IntersectionObserver(es=>{es.forEach(en=>{if(en.isIntersecting)appendVC('discoverVideos')})},{root:document.getElementById('mainContent'),rootMargin:'240px'})}
+ vcObserver.observe(document.getElementById('npLoadHint'));
+ }else if(vcList.length>VC_PAGE){
+ c.insertAdjacentHTML('beforeend',''+t('loadEnd')+vcList.length+t('loadEndSuf')+'
');
+ }
+}
+function buildVC(v){
+ const dur=v.duration_min||5,disc=v.discipline||'',author=v.author||'',dim=dimInfo(v);
+ const icon=v.discipline_icon&&v.discipline_icon!=='·'?v.discipline_icon:'📺';
+ const h=hashStr(disc||v.title);
+ const coverBg='linear-gradient(135deg,hsl('+(h%360)+',45%,90%),hsl('+((h+30)%360)+',45%,84%))';
+ const views=hashStr(v.title)%50000+300,dms=hashStr(v.title+'#dm')%900;
+ const upColor=UP_COLORS[hashStr(author)%UP_COLORS.length];
+ const initial=(author||'?').replace(/学院|·|同学|系/g,'').slice(0,1)||'?';
+ const faved=favC['video_'+v.id];
+ const click='onclick="openVideo('+v.id+',\''+esc(v.title)+'\',\''+esc(v.description||'')+'\',\''+esc(v.video_url||'')+'\',\''+esc(author)+'\',\''+esc(disc)+'\',\''+esc(dim.key)+'\')"';
+ const cover=v.cover_url
+ ?''
+ :''+icon+'
';
+ return ''+
+ '
'+cover+
+ ''+fmtDur(dur)+' '+
+ '▶ '+
+ ''+(faved?'❤️':'🤍')+'
'+
+ '
'+dim.icon+' '+esc(dim.label)+'
'+esc(v.title)+'
'+
+ '
👁 '+fmtNum(views)+' 💬 '+fmtNum(dms)+'
'+
+ '
'+esc(initial)+' '+esc(author)+'
'+
+ '
';
+}
+function openVideo(id,title,desc,url,author,disc,dimKey){
+ chatCtx={type:'video',title:title};rH('video',id,title);
+ const found=allV.find(v=>v.id===id);
+ if(!title&&found)title=found.title;
+ if(!desc&&found)desc=found.description;
+ const dim=dimKey?DIM5.find(d=>d.key===dimKey):(found?dimInfo(found):dimInfo({title:title,discipline:disc}));
+ const pet=getPet();
+ const claims=getVideoClaims();
+ const claimed=!!claims['video_'+id];
+ const claimBox=pet
+ ?''+(claimed
+ ?'✅ '+t('claimDone')+' · '+esc(dim.label)+' +3 '
+ :'🎯 '+t('claimVideo')+' · '+esc(dim.label)+' +3 ')+'
'
+ :'';
+ let h=''+dim.icon+' '+esc(dim.label)+'
'+esc(title)+' ';
+ h+=url?'
':'▶
';
+ h+=''+esc(desc||'No description')+'
'+esc(author)+' '+esc(disc)+' '+esc(dim.label)+'
';
+ h+=claimBox+''+t('close')+'
';
+ document.getElementById('toolModalContent').innerHTML=h;
+ document.getElementById('toolModal').classList.add('show');
+}
+function getVideoClaims(){try{return JSON.parse(localStorage.getItem('mb_video_claims')||'{}')||{}}catch(e){return{}}}
+function claimVideo(id,dimKey,label){
+ if(!getPet())return;
+ const claims=getVideoClaims();
+ if(claims['video_'+id])return;
+ claims['video_'+id]=true;
+ localStorage.setItem('mb_video_claims',JSON.stringify(claims));
+ addPetDim(dimKey,3);
+ addPetExp(10);
+ const btn=document.querySelector('.video-claim-btn');
+ if(btn){btn.disabled=true;btn.classList.add('claimed');btn.innerHTML='✅ '+t('claimDone')+' · '+esc(label)+' +3'}
+ showToast(esc(label)+' +3 · 数学小人成长中');
+ rDMap();
+}
+
+// ===== Knowledge Cards (standalone page) =====
+async function loadKnowledgePage(){
+ chatCtx={type:'knowledge'};
+ if(allK.length){rKCFilters();rKCCards();rKP();return}
+ try{const r=await api('/api/knowledge');if(r.code===0){allK=r.data||[];kT=r.topics||[];rKCFilters();rKCCards()}}catch(e){document.getElementById('knowledgeGrid').innerHTML='卡片加载失败
'}}
+function rKCFilters(){let h=''+t('filterAll')+' ';kT.forEach(t=>{h+=''+esc(t)+' '});document.getElementById('knowledgeFilters').innerHTML=h}
+function fKC(t){cT=t;rKCFilters();rKCCards()}
+function rKP(){
+ const bar=document.getElementById('knowledgeDrawProgress');
+ if(!bar)return;
+ const readThisSession=(cardState.knowledgeRead||0)%3;
+ const needMore=3-readThisSession;
+ bar.style.display='flex';
+ if(needMore<=0){
+ document.getElementById('drawProgressText').textContent='🎉 可以抽卡了!点击去抽卡';
+ document.getElementById('drawProgressFill').style.width='100%';
+ bar.classList.add('draw-progress-ready');
+ }else{
+ document.getElementById('drawProgressText').textContent='再读 '+needMore+' 张知识卡片即可抽卡';
+ document.getElementById('drawProgressFill').style.width=(readThisSession/3*100)+'%';
+ bar.classList.remove('draw-progress-ready');
+ }
+}
+function rKCCards(){const cards=cT?allK.filter(c=>c.topic===cT):allK;const g=document.getElementById('knowledgeGrid');if(!cards||cards.length===0){g.innerHTML=''+t('noCard')+'
';return}g.innerHTML=cards.map(c=>''+c.icon+' '+esc(c.question)+'
'+esc(c.title)+'
'+esc(c.description)+'
'+esc(c.read_time)+' ').join('');setTimeout(rKP,100)}
+async function openKnowledgeDetail(id){chatCtx={type:'knowledge',title:''};
+ // 记录知识卡片阅读
+ cardState.knowledgeRead=(cardState.knowledgeRead||0)+1;
+ if(cardState.knowledgeRead>=3&&cardState.knowledgeRead%3===0){
+ cardState.draws+=1;
+ saveCardState();
+ }else{
+ saveCardState();
+ }
+ rKP();
+ try{const r=await api('/api/knowledge/'+id);if(r.code===0){const c=r.data;chatCtx.title=c.title;document.getElementById('toolModalContent').innerHTML=''+c.icon+'
'+esc(c.title)+' '+esc(c.question)+'
'+esc(c.detail||c.description)+'
'+esc(c.read_time)+' · '+esc(c.topic||'')+' 🤖 '+t('aiAskBtn')+'
'}}catch(e){}document.getElementById('toolModal').classList.add('show')}
+function closeToolModal(){var m=document.getElementById('toolModal');if(m)m.classList.remove('show')}
+
+// ===== Toolbox =====
+function loadToolbox(){chatCtx={}}
+let toolState={};
+function openTool(name){
+ let h='';
+ if(name==='mathrace'){toolState={score:0,total:0,time:60,timer:null,correct:0};h=rMR()}
+ else if(name==='calculator')h=rCalc();
+ else if(name==='symbols')h=rSym();
+ else if(name==='grapher')h=rGraph();
+ document.getElementById('toolModalContent').innerHTML=h;
+ document.getElementById('toolModal').classList.add('show');
+ if(name==='mathrace')startMRLoop();
+ if(name==='grapher')setTimeout(plotGraph,200);
+}
+function rMR(){return''}
+function startMRLoop(){toolState={score:0,total:0,time:60,timer:null,correct:0};document.getElementById('mrScore').textContent='0';document.getElementById('mrTime').textContent='60s';document.getElementById('mrTotal').textContent='0';document.getElementById('mrResult').textContent='';document.getElementById('mrAnswer').value='';document.getElementById('mrAnswer').disabled=false;document.getElementById('mrAnswer').focus();genMR();toolState.timer=setInterval(()=>{toolState.time--;document.getElementById('mrTime').textContent=toolState.time+'s';document.getElementById('mrTimer').style.width=(toolState.time/60*100)+'%';if(toolState.time<=0){clearInterval(toolState.timer);document.getElementById('mrAnswer').disabled=true;document.getElementById('mrResult').innerHTML='⏰ 时间到!得分:'+toolState.score+' 正确:'+toolState.correct+'/'+toolState.total;addPetExp(toolState.score)}},1000);genMR()}
+function genMR(){const ops=['+','-','×'];const op=ops[Math.floor(Math.random()*3)];let a,b;if(op==='+'){a=Math.floor(Math.random()*90)+10;b=Math.floor(Math.random()*90)+10}else if(op==='-'){a=Math.floor(Math.random()*90)+10;b=Math.floor(Math.random()*a)}else{a=Math.floor(Math.random()*12)+2;b=Math.floor(Math.random()*12)+2}toolState.answer=op==='+'?a+b:op==='-'?a-b:a*b;toolState.total++;document.getElementById('mrTotal').textContent=toolState.total;document.getElementById('mrQuestion').textContent=a+' '+op+' '+b+' = ?';document.getElementById('mrAnswer').value='';document.getElementById('mrAnswer').focus()}
+function checkMR(){const ans=parseInt(document.getElementById('mrAnswer').value);if(isNaN(ans))return;if(ans===toolState.answer){toolState.score+=10;toolState.correct++;document.getElementById('mrResult').innerHTML='✅ '+t('mrRight')+' '}else{document.getElementById('mrResult').innerHTML='❌ '+toolState.answer+' '}document.getElementById('mrScore').textContent=toolState.score;if(toolState.time>0)setTimeout(genMR,600)}
+
+function rCalc(){return'=
'+['(',')','C','÷','7','8','9','×','4','5','6','-','1','2','3','+','0','.','sin(','√('].map(k=>''+k+' ').join('')+'
'}
+function calcKey(k){const i=document.getElementById('calcInput');if(k==='C')i.value='';else if(k==='÷')i.value+='/';else if(k==='×')i.value+='*';else if(k==='sin(')i.value+='Math.sin(';else if(k==='√(')i.value+='Math.sqrt(';else i.value+=k;i.focus()}
+function doCalc(){try{const v=eval(document.getElementById('calcInput').value);document.getElementById('calcResult').textContent=Number.isInteger(v)?v:v.toFixed(6)}catch(e){document.getElementById('calcResult').textContent='表达式有误'}}
+
+function rSym(){
+ const syms=[['π','Pi · 圆周率','3.14159... · \\pi'],['∞','无穷大','没有边界 · \\infty'],['Σ','求和符号','累加 · \\sum'],['∫','积分符号','面积/累积 · \\int'],['√','平方根','x²=a → x=√a · \\sqrt{}'],['Δ','变化量/判别式','差值△y/△x · \\Delta'],['∂','偏导数','多元函数求导 · \\partial'],['lim','极限','趋近于 · \\lim_{x \\to 0}'],['e','自然常数','≈2.71828 · e'],['i','虚数单位','i² = -1 · i'],['∏','连乘符号','累乘 · \\prod'],['∈','属于','元素∈集合 · \\in']];
+ return''+syms.map(s=>'
'+s[0]+''+s[1]+'
').join('')+'
👆 点击符号查看详情
'
+}
+function sS(sym,name,latex){document.getElementById('symDetail').innerHTML=''+sym+' '+name+'
LaTeX: '+latex+'
'}
+
+function rGraph(){return'
绘制
'}
+function plotGraph(){const c=document.getElementById('graphCanvas');if(!c)return;const ctx=c.getContext('2d');ctx.clearRect(0,0,c.width,c.height);ctx.strokeStyle='#D6D1CA';ctx.lineWidth=1;for(let i=0;i<=c.width;i+=50){ctx.beginPath();ctx.moveTo(i,0);ctx.lineTo(i,c.height);ctx.stroke()}for(let i=0;i<=c.height;i+=50){ctx.beginPath();ctx.moveTo(0,i);ctx.lineTo(c.width,i);ctx.stroke()}ctx.strokeStyle='#808C95';ctx.lineWidth=2;ctx.beginPath();const fn=new Function('x','return '+document.getElementById('graphFunc').value);const sx=c.width/2,sy=c.height/2,scale=30;for(let px=0;px-100&&py故事加载失败 '}}
+function rSN(ni){if(!sD||!sD.nodes||!sD.nodes[ni]){document.getElementById('storyScene').innerHTML='节点未找到
';return}const n=sD.nodes[ni];sVN.currentNode=ni;localStorage.setItem('mb_story_save',JSON.stringify(sVN));const ch=dC(ni),cN=['','初入江湖','西部教习','数理论道','脑学初创','报国之路','暗流涌动','陋室修炼','真相之门'];document.getElementById('storyStats').innerHTML='📐 '+(sVN.stats.shuli||10)+' ⚔️ '+(sVN.stats.xiayi||10)+' 🧘 '+(sVN.stats.xinzhi||10)+' '+ch+' · '+(cN[ch]||'')+' 🔄 '+t('simReset')+' ';document.getElementById('storyScene').innerHTML=(n.character?''+esc(n.character)+'
':'')+'
';document.getElementById('storyChoices').innerHTML='';if(tt)clearTimeout(tt);const te=document.getElementById('storyText'),tx=n.scene||'';let i=0;te.textContent='';function ty(){if(i{let h='';if(c.effects){const p=[];if(c.effects.shuli)p.push('数理根基'+(c.effects.shuli>0?'+':'')+c.effects.shuli);if(c.effects.xiayi)p.push('侠义声望'+(c.effects.xiayi>0?'+':'')+c.effects.xiayi);if(c.effects.xinzhi)p.push('心志韧性'+(c.effects.xinzhi>0?'+':'')+c.effects.xinzhi);if(p.length)h='['+p.join(' | ')+'] '}return''+esc(c.text)+h+' '}).join('')}
+function sC(next,ef){aSE(ef);rSN(next)}
+function aSE(ef){if(!ef)return;if(ef.shuli)sVN.stats.shuli=(sVN.stats.shuli||10)+ef.shuli;if(ef.xiayi)sVN.stats.xiayi=(sVN.stats.xiayi||10)+ef.xiayi;if(ef.xinzhi)sVN.stats.xinzhi=(sVN.stats.xinzhi||10)+ef.xinzhi;if(ef.favorability){if(!sVN.fav)sVN.fav={};for(const k in ef.favorability)sVN.fav[k]=(sVN.fav[k]||0)+ef.favorability[k]}}
+function resetStory(){if(tt)clearTimeout(tt);sVN={currentNode:null,stats:{shuli:10,xiayi:10,xinzhi:10},fav:{}};localStorage.removeItem('mb_story_save');loadStory()}
+function dC(ni){if(!ni)return 1;const m=ni.match(/^ch(\d+)/);return m?parseInt(m[1]):1}
+
+// ===== Simulator =====
+async function loadSimulator(){loadMathLife()}
+
+async function loadLegacySimulator(){chatCtx={type:'simulator'};try{const r=await api('/api/simulator');if(r.code===0){simD=r.data;try{const sr=await api('/api/simulator/save?user_id='+USER_ID);if(sr.code===0&&sr.data)simS=sr.data}catch(e){}const sn=simD.start_node||'y1_welcome';if(!simS.currentNode||!simD.nodes||!simD.nodes[simS.currentNode])simS.currentNode=sn;rSiN(simS.currentNode)}}catch(e){document.getElementById('simScene').innerHTML='模拟器加载失败
'}}
+function rSiN(ni){if(!simD||!simD.nodes||!simD.nodes[ni]){document.getElementById('simScene').innerHTML='节点未找到
';return}const n=simD.nodes[ni];simS.currentNode=ni;api('/api/simulator/save',{method:'POST',body:{user_id:USER_ID,state:simS}}).catch(()=>{});document.getElementById('simStats').innerHTML=''+(simS.stats.gpa||3).toFixed(1)+'
GPA
'+(simS.stats.interest||0)+'
'+t('interestLabel')+'
'+(simS.stats.skill||0)+'
'+t('skillLabel')+'
🔄 '+t('simReset')+' ';document.getElementById('simScene').innerHTML=(n.stage?'📅 '+esc(n.stage)+'
':'')+(n.character?''+esc(n.character)+'
':'')+''+esc(n.scene||'')+'
';document.getElementById('simChoices').innerHTML=(n.choices||[]).map(c=>''+esc(c.text)+(c.hint?''+esc(c.hint)+' ':'')+' ').join('')}
+function siC(next,ef){if(ef){if(ef.gpa)simS.stats.gpa=Math.max(0,Math.min(4,(simS.stats.gpa||3)+ef.gpa*.1));if(ef.interest)simS.stats.interest=(simS.stats.interest||0)+ef.interest;if(ef.skill)simS.stats.skill=(simS.stats.skill||0)+ef.skill}rSiN(next);addPetExp(5)}
+function resetSim(){simS={currentNode:null,stats:{gpa:3,interest:0,skill:0}};loadLegacySimulator()}
+
+// ===== Favorites =====
+async function loadFavorites(){chatCtx={};try{await rFC()}catch(e){}try{const r=await api('/api/favorites?user_id='+USER_ID);const g=document.getElementById('favGrid');if(r.code===0){const f=r.data||[];if(f.length===0){g.innerHTML='暂无收藏在发现页点击 🤍 即可收藏
';return}g.innerHTML=f.map(fv=>'✕ '+(fv.target_type==='video'?'📺 视频':'📚 知识卡片')+'
'+esc(fv.title)+'
').join('')}}catch(e){document.getElementById('favGrid').innerHTML='加载失败
'}}
+async function rFC(){try{const r=await api('/api/favorites?user_id='+USER_ID);if(r.code===0){favC={};(r.data||[]).forEach(f=>{favC[f.target_type+'_'+f.target_id]=true})}}catch(e){}}
+async function tF(type,id,title,cover){if(favC[type+'_'+id]){await api('/api/favorites',{method:'DELETE',body:{user_id:USER_ID,target_type:type,target_id:id}});favC[type+'_'+id]=false;showToast('🗑 '+t('close')+' ✔')}else{await api('/api/favorites',{method:'POST',body:{user_id:USER_ID,target_type:type,target_id:id,title:title,cover:cover}});favC[type+'_'+id]=true;showToast('❤ '+t('saveOK'))}if(CP==='discover')fV(cD);else if(CP==='favorites')loadFavorites()}
+async function rF(type,id){await api('/api/favorites',{method:'DELETE',body:{user_id:USER_ID,target_type:type,target_id:id}});loadFavorites()}
+
+// ===== History =====
+async function rH(type,id,title){try{await api('/api/history',{method:'POST',body:{user_id:USER_ID,target_type:type,target_id:id,title:title}})}catch(e){}}
+
+// ===== Profile =====
+async function loadProfile(){chatCtx={};try{const[pr,sr]=await Promise.all([api('/api/user/profile?user_id='+USER_ID),api('/api/user/stats?user_id='+USER_ID)]);const p=pr.code===0?pr.data:{nickname:'游客用户',avatar_emoji:'葫',avatar_color:'#5B6AF0'};const s=sr.code===0?sr.data:{watch_count:0,favorite_count:0};document.getElementById('profileContent').innerHTML=''+esc(p.avatar_emoji||'葫')+'
'+esc(p.nickname||'游客用户')+'
ID: '+USER_ID+'
'}catch(e){document.getElementById('profileContent').innerHTML='加载失败
'}}
+async function sP(){const n=document.getElementById('editNickname').value.trim();const a=document.getElementById('editAvatar').value.trim();try{await api('/api/user/profile',{method:'POST',body:{user_id:USER_ID,nickname:n,avatar_emoji:a}});document.getElementById('sidebarName').textContent=n||'游客用户';document.getElementById('sidebarAvatar').textContent=a||'葫';alert('保存成功');loadProfile()}catch(e){alert('保存失败')}}
+
+// ===== Card Collection Page =====
+async function loadCardsPage(){
+ chatCtx={};
+ document.getElementById('cardsProgress').textContent=cardState.owned.length+'/16';
+ const drawBtn=document.getElementById('cardsDrawBtn');
+ const drawCount=document.getElementById('cardsDrawCount');
+ drawCount.textContent=cardState.draws;
+ drawBtn.style.display=cardState.draws>0?'inline-flex':'none';
+ const grid=document.getElementById('cardsCollectionGrid');
+ await loadCardData();
+ const allCodes=getAllCardCodes();
+ if(!allCodes.length){grid.innerHTML='卡片数据加载中…
';return}
+ let h='';
+ allCodes.forEach(code=>{
+ const info=getCardInfo(code);
+ const clan=info?getClanInfo(info.clan):null;
+ const clanColor=clan?clan.color:'#A8907E';
+ const owned=cardState.owned.indexOf(code)!==-1;
+ h+=''+
+ (owned
+ ?'
'+portraitHTML(info,24)+'
'+(info?info.name:'???')+'
'+code+'
'+(clan?clan.emoji+' '+clan.name:'')+'
'+stats5HTML(info)
+ :'
'+portraitHTML(info,24)+'
???
未获得
'+(clan?clan.emoji+' '+clan.name:'未知')+'
')+
+ '
';
+ });
+ grid.innerHTML=h;
+}
+
+// ===== Pet Experience =====
+function getPet(){
+ let pet=null;
+ try{
+ const a=localStorage.getItem('mb_pet');
+ if(a)pet=JSON.parse(a);
+ if(!pet||!pet.type){
+ const b=localStorage.getItem('mathbrain_pet');
+ if(b)pet=JSON.parse(b);
+ }
+ }catch(e){}
+ if(!pet||!pet.type)return null;
+ pet.name=pet.name||pet.type;
+ pet.level=pet.level||1;
+ pet.exp=pet.exp||0;
+ pet.dims=pet.dims||{眼光:10,人文:10,侦探:10,建模:10,联结:10};
+ return pet;
+}
+function savePet(pet){if(!pet)return;localStorage.setItem('mb_pet',JSON.stringify(pet));localStorage.setItem('mathbrain_pet',JSON.stringify(pet))}
+function renderPetCard(){
+ const pet=getPet(),pa=document.getElementById('homePetArea');
+ if(!pa)return;
+ if(!pet){pa.innerHTML='';return}
+ pa.innerHTML=''+pet.icon+'
'+esc(pet.name)+'
🎓 '+esc(pet.type)+' · Lv.'+pet.level+' · '+pet.exp+'/100 exp
'+DIM5.map(d=>{const v=Math.min(100,Math.max(0,Number((pet.dims||{})[d.key])||0));return '
'+d.icon+' '+esc(d.short)+'
'+v+' '}).join('')+'
'+t('petTip')+'
';
+}
+function addPetDim(dimKey,amt){
+ const pet=getPet();if(!pet)return false;
+ pet.dims=pet.dims||{眼光:10,人文:10,侦探:10,建模:10,联结:10};
+ pet.dims[dimKey]=Math.min(100,Math.max(0,(Number(pet.dims[dimKey])||0)+amt));
+ savePet(pet);
+ renderPetCard();
+ return true;
+}
+function addPetExp(amt){
+ const pet=getPet();
+ if(!pet)return;
+ pet.exp=(pet.exp||0)+amt;
+ let lvlU=false;
+ while(pet.exp>=100){pet.exp-=100;pet.level=(pet.level||1)+1;lvlU=true}
+ savePet(pet);
+ // 经验条跳动动画
+ const bar=document.querySelector('.pet-exp-fill');
+ if(bar){const pct=pet.exp%100;bar.style.transition='none';bar.style.width='0%';requestAnimationFrame(()=>{requestAnimationFrame(()=>{bar.style.transition='width .4s cubic-bezier(.34,1.56,.64,1)';bar.style.width=pct+'%'})})}
+ if(lvlU){showToast('🎉 '+t('petLevelUp').replace('{n}',pet.level));burstConfetti(document.querySelector('.pet-card'))}
+}
+
+// ===== AI Chat =====
+async function sendChat(){const i=document.getElementById('chatInput');const m=i.value.trim();if(!m)return;aCM('user',m);i.value='';const tid=aCT();try{const r=await api('/api/chat',{method:'POST',body:{message:m,context:chatCtx}});rCT(tid);aCM('assistant',r.code===0?r.reply:'抱歉,AI暂时无法回复')}catch(e){rCT(tid);aCM('assistant','网络异常,请稍后重试')}}
+function qA(q){document.getElementById('chatInput').value=q;closeToolModal();toggleAIPanel();setTimeout(()=>sendChat(),300)}
+function aCM(role,text){const c=document.getElementById('aiMessages');const d=document.createElement('div');d.className='chat-msg '+role;d.innerHTML=''+esc(text)+'
';c.appendChild(d);c.scrollTop=c.scrollHeight}
+function aCT(){const c=document.getElementById('aiMessages');const d=document.createElement('div');d.className='chat-msg assistant';d.innerHTML='
';c.appendChild(d);c.scrollTop=c.scrollHeight;return d}
+function rCT(el){if(el&&el.parentNode)el.remove()}
+
+// ===== Utils =====
+function esc(t){if(!t)return'';esc._d=esc._d||document.createElement('div');esc._d.textContent=t;return esc._d.innerHTML}
+function adj(hex,amt){const n=parseInt(hex.replace('#',''),16);const r=Math.min(255,(n>>16)+amt);const g=Math.min(255,((n>>8)&0xFF)+amt);const b=Math.min(255,(n&0xFF)+amt);return '#'+((r<<16)|(g<<8)|b).toString(16).padStart(6,'0')}
+
+// ===== Float Hub =====
+function toggleHub(){document.getElementById('floatHub').classList.toggle('open')}
+function closeHub(){document.getElementById('floatHub').classList.remove('open')}
+document.addEventListener('click',e=>{if(!e.target.closest('.float-hub'))document.getElementById('floatHub').classList.remove('open')});
+
+// ===== Card System =====
+function initCardState(){
+ const raw=localStorage.getItem('hulu_card_state');
+ if(raw){try{cardState=JSON.parse(raw)}catch(e){}}
+ if(!cardState.owned)cardState.owned=[];
+ if(!cardState.draws&&cardState.draws!==0)cardState.draws=0;
+ if(!cardState.lastDaily)cardState.lastDaily='';
+ if(!cardState.knowledgeRead&&cardState.knowledgeRead!==0)cardState.knowledgeRead=0;
+ if(!cardState.drawHistory)cardState.drawHistory=[];
+ // Daily login
+ const today=new Date().toISOString().slice(0,10);
+ if(cardState.lastDaily!==today){
+ cardState.draws+=1;
+ cardState.lastDaily=today;
+ saveCardState();
+ }
+}
+function saveCardState(){localStorage.setItem('hulu_card_state',JSON.stringify(cardState));rCG();}
+function ownedCount(){return cardState.owned.length}
+function grantCard(code,method){
+ if(!code)return;
+ if(cardState.owned.indexOf(code)===-1){
+ cardState.owned.push(code);
+ cardState.drawHistory.push({code:code,date:new Date().toISOString().slice(0,10),method:method});
+ saveCardState();
+ return true;
+ }
+ return false;
+}
+async function loadCardData(){
+ if(cardData)return cardData;
+ try{
+ const r=await api('/api/math_personality');
+ if(r.code===0&&r.data&&r.data.results){cardData=r.data;cardCache=r.data;return cardData}
+ }catch(e){}
+ return null;
+}
+function getCardInfo(code){
+ if(!cardCache||!cardCache.results)return null;
+ return cardCache.results[code]||null;
+}
+function getAllCardCodes(){
+ if(!cardCache||!cardCache.results)return[];
+ return Object.keys(cardCache.results);
+}
+function getClanInfo(clanId){
+ if(!cardCache||!cardCache.clans)return null;
+ return cardCache.clans[clanId]||null;
+}
+const STAT5_LABELS=['眼光','人文','侦探','建模','联结'];
+function stats5HTML(info){
+ const s=(info&&info.stats5)||{};
+ return ''+STAT5_LABELS.map(k=>{
+ const v=Math.min(100,Math.max(0,Number(s[k])||0));
+ return '
';
+ }).join('')+'
';
+}
+function portraitHTML(info,size){
+ if(!info)return'?';
+ const fs=size||24;
+ if(info.portrait)return ''+(info.icon||'?')+' ';
+ return info.icon||'?';
+}
+// Render card gallery on home page
+function rCG(){
+ const wrap=document.getElementById('homeCardGallery');
+ const btn=document.getElementById('homeGachaBtn');
+ const cnt=document.getElementById('cardCountText');
+ const scroll=document.getElementById('cardGalleryScroll');
+ if(!wrap||!scroll)return;
+ const owned=cardState.owned;
+ if(cnt)cnt.textContent=owned.length+'/16';
+ if(owned.length===0){
+ // 还没测过 MathBTI
+ if(!getPet()){
+ scroll.innerHTML='🏮 去做MathBTI 领取你的第一张数学人物小卡
';
+ }else{
+ scroll.innerHTML='🏮 还没获得卡片 去知识卡片页阅读攒抽卡机会吧
';
+ }
+ wrap.style.display='block';
+ // 即使没有卡片,有抽卡次数也要显示按钮
+ if(btn){
+ if(cardState.draws>0){
+ btn.style.display='inline-flex';
+ document.getElementById('homeDrawCount').textContent=cardState.draws;
+ }else{
+ btn.style.display='none';
+ }
+ }
+ return;
+ }
+ wrap.style.display='block';
+ loadCardData().then(()=>{
+ let h='';
+ owned.forEach(code=>{
+ const info=getCardInfo(code);
+ const clan=info?getClanInfo(info.clan):null;
+ const clanColor=clan?clan.color:'#A8907E';
+ h+=''+
+ '
'+portraitHTML(info,24)+'
'+
+ '
'+(info?info.name:'')+'
';
+ });
+ scroll.innerHTML=h;
+ });
+ // 抽卡按钮
+ if(btn){
+ if(cardState.draws>0){
+ btn.style.display='inline-flex';
+ document.getElementById('homeDrawCount').textContent=cardState.draws;
+ }else{
+ btn.style.display='none';
+ }
+ }
+}
+// Gacha draw
+function openGacha(){
+ document.getElementById('gachaOverlay').style.display='flex';
+ document.getElementById('gachaIdle').style.display='block';
+ document.getElementById('gachaResult').style.display='none';
+ document.getElementById('gachaDrawCount').textContent=cardState.draws;
+ const all=cardData?Object.keys(cardData.results).length:16;
+ const missing=all-cardState.owned.length;
+ let pityHint='';
+ if(cardState.drawHistory.length>0){
+ const sinceNew=cardState.drawHistory.slice().reverse().findIndex(d=>cardState.owned.indexOf(d.code)===-1);
+ if(sinceNew>=4)pityHint='⭐ 下次抽卡必出新卡!';
+ else if(sinceNew>=0)pityHint='再抽'+(4-sinceNew)+'次保底出新卡';
+ }
+ document.getElementById('gachaPityHint').textContent=pityHint||'剩余未收集:'+missing+'/16';
+}
+function closeGacha(){document.getElementById('gachaOverlay').style.display='none'}
+function doDraw(){
+ if(_gachaBusy)return;
+ if(cardState.draws<=0){showToast('没有抽卡次数了!去读知识卡片获取吧');return}
+ _gachaBusy=true;
+ const gourd=document.getElementById('gachaGourd');
+ gourd.classList.add('gourd-shaking');
+ document.getElementById('gachaIdle').style.display='block';
+ document.getElementById('gachaResult').style.display='none';
+ setTimeout(()=>{
+ gourd.classList.remove('gourd-shaking');
+ cardState.draws--;
+ saveCardState();
+ // Pity: every 5th draw guaranteed new
+ let code;
+ const allCodes=getAllCardCodes();
+ const sinceNewIndex=cardState.drawHistory.slice().reverse().findIndex(d=>!cardState.owned.includes(d.code));
+ if(sinceNewIndex>=4||(cardState.drawHistory.length>0&&sinceNewIndex===-1)){
+ // 保底:一定出新卡
+ const missing2=allCodes.filter(c=>!cardState.owned.includes(c));
+ code=missing2[Math.floor(Math.random()*missing2.length)];
+ }else{
+ code=allCodes[Math.floor(Math.random()*allCodes.length)];
+ }
+ const isNew=cardState.owned.indexOf(code)===-1;
+ if(isNew){
+ cardState.owned.push(code);
+ }
+ cardState.drawHistory.push({code:code,date:new Date().toISOString().slice(0,10),method:'gacha'});
+ saveCardState();
+ // Show result
+ showDrawResult(code,isNew);
+ // 粒子爆发
+ const cr=document.querySelector('.gacha-card-reveal');
+ if(cr)setTimeout(()=>burstGachaConfetti(cr),300);
+ _gachaBusy=false;
+ rCG();
+ },900);
+}
+function showDrawResult(code,isNew){
+ loadCardData().then(()=>{
+ const info=getCardInfo(code);
+ const clan=info?getClanInfo(info.clan):null;
+ const clanColor=clan?clan.color:'#A8907E';
+ document.getElementById('gachaIdle').style.display='none';
+ const res=document.getElementById('gachaResult');
+ res.style.display='block';
+ res.innerHTML=
+ ''+
+ (info&&info.portrait
+ ?'
'+(info.icon||'?')+' '
+ :'
'+(info?info.icon:'?')+'
')+
+ '
'+(info?info.name:'???')+'
'+
+ '
'+code+'
'+
+ (clan?'
'+clan.emoji+' '+clan.name+' · '+(info?info.mathematician:'')+'
':'')+
+ '
'+(info?info.tagline:'')+'
'+
+ (info?stats5HTML(info):'')+
+ (isNew?'':'
(重复获得 · 已自动分解)
')+
+ '
📋 查看卡片详情 '+
+ '
再抽一次 (剩余'+cardState.draws+'次)
'+
+ '
';
+ document.getElementById('gachaDrawCount').textContent=cardState.draws;
+ });
+}
+// Card detail modal
+function openCardDetail(code){
+ loadCardData().then(()=>{
+ const info=getCardInfo(code);
+ if(!info)return;
+ const clan=getClanInfo(info.clan);
+ const clanColor=clan?clan.color:'#A8907E';
+ const hist=cardState.drawHistory.filter(d=>d.code===code);
+ const obtainInfo=hist.length>0
+ ?(hist[0].method==='mathbti'?'📋 MathBTI测试获得':'🏮 葫芦抽卡获得')+' · '+hist[0].date
+ :'未知途径';
+ const modal=document.getElementById('cardDetailModal');
+ modal.innerHTML=
+ ''+
+ ''+
+ '
人物简介
'+esc(info.description||'')+'
'+
+ '
'+
+ '
代表数学家 · '+esc(info.mathematician)+'
'+esc(info.bio||'')+'
'+
+ '
💡 '+esc(info.bio_fact||'')+'
'+
+ '
🎴 '+obtainInfo+'
'+
+ '
';
+ document.getElementById('cardDetailOverlay').style.display='flex';
+ });
+}
+function closeCardDetail(){document.getElementById('cardDetailOverlay').style.display='none'}
+function burstGachaConfetti(el){
+ if(!el)return;const r=el.getBoundingClientRect();const cx=r.left+r.width/2,cy=r.top+r.height/2;
+ const colors=['#A8907E','#C47F3A','#D4A574','#8A9BA8','#C49595','#8AA68A'];
+ for(let i=0;i<18;i++){const p=document.createElement('div');p.className='gacha-particle';const a=Math.random()*Math.PI*2,d=40+Math.random()*60;p.style.cssText='left:'+cx+'px;top:'+cy+'px;--gpx:'+(Math.cos(a)*d)+'px;--gpy:'+(Math.sin(a)*d)+'px;background:'+colors[Math.floor(Math.random()*colors.length)];document.body.appendChild(p);setTimeout(()=>p.remove(),1100)}
+}
+
+// ===== Toast =====
+function showToast(msg){
+ let c=document.getElementById('toastBox');if(!c){c=document.createElement('div');c.id='toastBox';c.className='toast-container';document.body.appendChild(c)}
+ const t=document.createElement('div');t.className='toast';t.innerHTML=msg;c.appendChild(t);setTimeout(()=>t.remove(),2400);
+}
+
+// ===== 粒子爆发 =====
+function burstConfetti(el){
+ if(!el)return;const r=el.getBoundingClientRect();const cx=r.left+r.width/2,cy=r.top+r.height/2;
+ const colors=['#808C95','#A8907E','#8AA68A','#C4A467','#C49595','#9AA5AE'];
+ for(let i=0;i<14;i++){const p=document.createElement('div');p.className='particle';const a=Math.random()*Math.PI*2,d=30+Math.random()*50;p.style.cssText='left:'+cx+'px;top:'+cy+'px;width:6px;height:6px;border-radius:2px;background:'+colors[Math.floor(Math.random()*colors.length)]+';--px:'+(Math.cos(a)*d)+'px;--py:'+(Math.sin(a)*d)+'px';document.body.appendChild(p);setTimeout(()=>p.remove(),750)}
+}
+
+// ===== 骨架屏 =====
+function renderSkeletons(cid,count){
+ const c=document.getElementById(cid);c.innerHTML='';
+ for(let i=0;i
')}
+}
+
+// ===== Init =====
+let favP=null;
+document.addEventListener('DOMContentLoaded',()=>{
+ // 语言初始化
+ document.querySelectorAll('.lang-btn').forEach(b=>b.classList.toggle('active',b.dataset.lang===_lang));
+ document.getElementById('globalSearch').placeholder=t('search');
+ rI18n();
+ // 首屏:首页纯本地渲染,立即呈现;数据请求并行后台预取
+ loadHome();
+ initCardState();
+ loadCardData();
+ setTimeout(rCG,300);
+ favP=rFC();
+ api('/api/videos?module=数人').then(r=>{if(r.code===0){allV=r.data||[];vD=r.disciplines||[]}}).catch(()=>{});
+ api('/api/knowledge').then(r=>{if(r.code===0){allK=r.data||[];kT=r.topics||[]}}).catch(()=>{});
+ api('/api/user/profile?user_id='+USER_ID).then(r=>{if(r.code===0&&r.data){document.getElementById('sidebarName').textContent=r.data.nickname||'游客用户';document.getElementById('sidebarAvatar').textContent=r.data.avatar_emoji||'葫';document.getElementById('sidebarAvatar').style.background=r.data.avatar_color||'#808C95'}}).catch(()=>{});
+});
+
+// ===== Math Life =====
+let mtD=null,mtS={currentNode:null,stats:{shuli:10,xiayi:10,xinzhi:10},fav:{}};
+const MT_STAGE_MAP={c1:'第一幕 · 大一上:小镇少年走进数学系',c2:'第二幕 · 大一下:第一次完整证明',c3:'第三幕 · 大二上:讨论班与诺特',c4:'第四幕 · 大二下:数学与生活',c5:'第五幕 · 大三上:方向与高斯',c6:'第六幕 · 大三下:科研低谷与诺特',c7:'第七幕 · 大四上:直博申请',c8:'第八幕 · 毕业前夜与直博第一年'};
+let alumniList=[],alumniStory=null,alumniStoryIndex=0;
+function mtStage(id){const k=(id||'').slice(0,2);return MT_STAGE_MAP[k]||'数学少年线'}
+function mtStatColor(k){if(k==='shuli')return '#3F6C8F';if(k==='xinzhi')return '#2E7D72';return '#B8893A'}
+function mtStatLabel(k){if(k==='shuli')return '数理根基';if(k==='xinzhi')return '心志韧性';return '同行温度'}
+function mtStatIcon(k){if(k==='shuli')return '∑';if(k==='xinzhi')return 'θ';return 'π'}
+function mtEffectsHint(ef){if(!ef)return '';const p=[];if(ef.shuli)p.push('数理根基 '+(ef.shuli>0?'+':'')+ef.shuli);if(ef.xiayi)p.push('同行温度 '+(ef.xiayi>0?'+':'')+ef.xiayi);if(ef.xinzhi)p.push('心志韧性 '+(ef.xinzhi>0?'+':'')+ef.xinzhi);if(ef.favorability){for(const k in ef.favorability)p.push(k+'好感 '+(ef.favorability[k]>0?'+':'')+ef.favorability[k])}return p.length?'['+p.join(' | ')+'] ':''}
+function loadMathLife(){
+ chatCtx={};
+ const hub=document.getElementById('mathLifeHub'),teen=document.getElementById('mathTeenPlayer'),legacy=document.getElementById('legacySimPlayer'),alumni=document.getElementById('mathAlumniPlayer');
+ if(hub)hub.style.display='';
+ if(teen)teen.style.display='none';
+ if(legacy)legacy.style.display='none';
+ if(alumni)alumni.style.display='none';
+ const themes=[
+ {icon:'🔱',name:'信仰者',desc:'数学少年线 · 高斯 / 祖冲之 / 欧拉 / 诺特',open:'openMathTeen()',ready:true},
+ {icon:'📣',name:'传播者',desc:'斐波那契 / 华罗庚 / 伽罗瓦 / Ada Lovelace',open:'',ready:false},
+ {icon:'🛠️',name:'应用者',desc:'秦九韶 / 牛顿 / 图灵 / 冯·诺依曼',open:'',ready:false},
+ {icon:'👁️',name:'直觉者',desc:'希帕提娅 / 赵爽 / 庞加莱 / 凯瑟琳·约翰逊',open:'',ready:false}
+ ];
+ hub.innerHTML=
+ '四大主题
'+
+ ''+
+ themes.map(t=>'
'+
+ '
'+t.icon+'
'+
+ '
'+esc(t.name)+'
'+
+ '
'+esc(t.desc)+'
'+
+ (t.ready?'':'
敬请期待
')+
+ '
').join('')+
+ '
'+
+ ''+
+ '🎓 '+
+ '数学校友访谈 真实校友的大学四年 · Skill 剧本包 '+
+ '→ '+
+ ' '+
+ ''+
+ '
更多数学人生 · 敬请期待
'+
+ '
更多主题正在创作中,敬请期待
'+
+ '
'+
+ [
+ {icon:'📣',name:'传播者',desc:'斐波那契 / 华罗庚 / 伽罗瓦 / Ada Lovelace'},
+ {icon:'🛠️',name:'应用者',desc:'秦九韶 / 牛顿 / 图灵 / 冯·诺依曼'},
+ {icon:'👁️',name:'直觉者',desc:'希帕提娅 / 赵爽 / 庞加莱 / 凯瑟琳·约翰逊'}
+ ].map(m=>'
'+m.icon+'
'+esc(m.name)+'
'+esc(m.desc)+'
敬请期待
').join('')+
+ '
'+
+ '
';
+}
+async function openMathTeen(){
+ const hub=document.getElementById('mathLifeHub'),teen=document.getElementById('mathTeenPlayer'),legacy=document.getElementById('legacySimPlayer');
+ if(hub)hub.style.display='none';
+ if(legacy)legacy.style.display='none';
+ if(teen){teen.style.display='';teen.innerHTML=''}
+ try{const r=await api('/api/story/math_teen');if(r.code===0){mtD=r.data;const saved=localStorage.getItem('mb_math_teen_save');if(saved){try{mtS=JSON.parse(saved)}catch(e){mtS={currentNode:null,stats:{shuli:10,xiayi:10,xinzhi:10},fav:{}}}}const sn=mtD.start_node||'c1_notice';const ni=(mtS.currentNode&&mtD.nodes&&mtD.nodes[mtS.currentNode])?mtS.currentNode:sn;mtS.currentNode=ni;rMTNode(ni)}else throw new Error('load failed')}catch(e){const b=document.getElementById('mtStoryBox');if(b)b.innerHTML='数学少年线加载失败
'}}
+function rMTNode(ni){
+ if(!mtD||!mtD.nodes||!mtD.nodes[ni]){document.getElementById('mtStoryBox').innerHTML='节点未找到
';return}
+ const n=mtD.nodes[ni];mtS.currentNode=ni;localStorage.setItem('mb_math_teen_save',JSON.stringify(mtS));
+ const st=document.getElementById('mtStage');if(st)st.textContent=mtStage(ni);
+ const stats=document.getElementById('mtStats');if(stats)stats.innerHTML=
+ ['shuli','xiayi','xinzhi'].map(k=>''+mtStatIcon(k)+'
'+(mtS.stats[k]||0)+' '+mtStatLabel(k)+' ').join('')+
+ '4
4 位数学家 高斯 · 祖冲之 · 欧拉 · 诺特 '+
+ '🔄 重新开始 ';
+ const box=document.getElementById('mtStoryBox');if(box)box.innerHTML=(n.character?''+esc(n.character)+'
':'')+''+esc(n.scene||'')+'
';
+ rMTChoices(n.choices||[]);
+}
+function rMTChoices(choices){
+ const area=document.getElementById('mtChoices');if(!area)return;
+ if(!choices.length){area.innerHTML='故事结束 · 你的数学,刚刚开始
再走一次这段路 ';const rb=document.getElementById('mtRestartBtn');if(rb)rb.addEventListener('click',resetMathTeen);return}
+ area.innerHTML=choices.map((c,i)=>''+esc(c.text)+mtEffectsHint(c.effects)+' ').join('');
+ area.querySelectorAll('.story-choice').forEach(btn=>{
+ const c=choices[Number(btn.getAttribute('data-mt-index'))];
+ btn.addEventListener('click',()=>mTC(c.next,c.effects));
+ });
+}
+function mTC(next,ef){if(ef){if(ef.shuli)mtS.stats.shuli=(mtS.stats.shuli||10)+ef.shuli;if(ef.xiayi)mtS.stats.xiayi=(mtS.stats.xiayi||10)+ef.xiayi;if(ef.xinzhi)mtS.stats.xinzhi=(mtS.stats.xinzhi||10)+ef.xinzhi;if(ef.favorability){if(!mtS.fav)mtS.fav={};for(const k in ef.favorability)mtS.fav[k]=(mtS.fav[k]||0)+ef.favorability[k]}}rMTNode(next);addPetExp(5)}
+function resetMathTeen(){localStorage.removeItem('mb_math_teen_save');mtS={currentNode:null,stats:{shuli:10,xiayi:10,xinzhi:10},fav:{}};if(mtD&&mtD.start_node)rMTNode(mtD.start_node);else openMathTeen()}
+function backToMathLife(){loadMathLife()}
+async function openMathAlumni(){
+ const hub=document.getElementById('mathLifeHub'),teen=document.getElementById('mathTeenPlayer'),legacy=document.getElementById('legacySimPlayer'),alumni=document.getElementById('mathAlumniPlayer');
+ if(hub)hub.style.display='none';
+ if(teen)teen.style.display='none';
+ if(legacy)legacy.style.display='none';
+ if(!alumni)return;
+ alumni.style.display='';
+ alumni.innerHTML='
';
+ loadAlumniCards();
+}
+async function loadAlumniCards(){
+ try{const r=await api('/api/alumni');alumniList=(r&&r.code===0&&Array.isArray(r.data))?r.data:[]}catch(e){alumniList=[]}
+ const box=document.getElementById('alumniCards');if(!box)return;
+ const cards=alumniList.length
+ ?alumniList.map(alumniCardHTML).join('')
+ :[1,2,3,4,5,6].map(i=>'🎓
AL-'+(String(i).padStart(3,'0'))+'
校友档案 '+i+'
大一 → 大四 · 访谈整理中
Skill 剧本待产出 ').join('');
+ box.innerHTML=cards+'+
成为校友访谈嘉宾
报名与访谈流程即将开放
';
+}
+function alumniCardHTML(a){
+ const skill=a.skill||{},tags=(skill.tags&&skill.tags.length)?skill.tags:['数学人生'];
+ return ''+
+ ''+esc(a.icon||'🎓')+'
'+
+ ''+esc(a.id||'AL-000')+'
'+
+ ''+esc(a.name||'未命名校友')+'
'+
+ ''+esc((a.major||'数学')+' · '+(a.graduation_year||'已毕业'))+'
'+
+ ''+tags.map(tg=>''+esc(tg)+' ').join('')+'
'+
+ ''+DIM5.map(d=>{const v=(a.dims&&a.dims[d.key])||0;return ''+d.icon+' '+v+' '}).join('')+'
'+
+ '线性体验四年 → '+
+ ' ';
+}
+function openAlumniStory(id){
+ alumniStory=alumniList.find(a=>a.id===id)||null;
+ alumniStoryIndex=0;
+ const list=document.getElementById('alumniListView'),story=document.getElementById('alumniStoryView');
+ if(list)list.style.display='none';
+ if(story){
+ story.style.display='';
+ story.innerHTML='
';
+ renderAlumniStory();
+ }
+}
+function backToAlumniList(){
+ const list=document.getElementById('alumniListView'),story=document.getElementById('alumniStoryView');
+ if(list)list.style.display='';
+ if(story)story.style.display='none';
+}
+function renderAlumniStory(){
+ const body=document.getElementById('alumniStoryBody');if(!body)return;
+ const nodes=alumniStory&&alumniStory.four_years&&alumniStory.four_years.nodes?alumniStory.four_years.nodes:[];
+ if(!nodes.length){
+ body.innerHTML='该校友的 Skill 剧本正在整理中。
'+['大一','大二','大三','大四'].map((s,i)=>'
'+(i+1)+' '+s+' 剧情节点整理中
').join('')+'
';
+ return;
+ }
+ const n=nodes[Math.min(alumniStoryIndex,nodes.length-1)];
+ const pct=Math.round((alumniStoryIndex+1)/nodes.length*100);
+ body.innerHTML='
'+esc(n.stage||('第 '+(alumniStoryIndex+1)+' 幕'))+'
'+esc(n.scene||'')+'
'+(n.effect?'
'+fiveDimEffectHTML(n.effect)+'
':'')+
+ (alumniStoryIndex
下一幕 →'
+ :''+esc(n.ending||'这就是这位校友的数学人生。')+'
返回校友卡片 ')+
+ ' ';
+}
+function nextAlumniNode(){alumniStoryIndex++;renderAlumniStory()}
+function fiveDimEffectHTML(ef){
+ if(!ef)return'';
+ const labels={眼光:'数学眼光',人文:'数学人文',侦探:'数学侦探',建模:'数学建模',联结:'数学联结'};
+ const arr=Object.keys(ef||{}).filter(k=>labels[k]&&ef[k]);
+ if(!arr.length)return'';
+ return '五维能力关联:'+arr.map(k=>labels[k]+' '+(ef[k]>0?'+':'')+ef[k]).join(' · ')+' ';
+}
+function openLegacySim(){const hub=document.getElementById('mathLifeHub'),teen=document.getElementById('mathTeenPlayer'),legacy=document.getElementById('legacySimPlayer');if(hub)hub.style.display='none';if(teen)teen.style.display='none';if(legacy)legacy.style.display='';loadLegacySimulator()}
diff --git a/static/js/desktop.min.js b/static/js/desktop.min.js
new file mode 100644
index 0000000..d8a230a
--- /dev/null
+++ b/static/js/desktop.min.js
@@ -0,0 +1,642 @@
+const API_BASE='',USER_ID=localStorage.getItem('mb_user_id')||('mb_'+Math.random().toString(36).slice(2,10));
+localStorage.setItem('mb_user_id',USER_ID);
+let CP='home',allV=[],allK=[],kT=[],vD=[],cD='',cT='',sD=null,sVN={currentNode:null,stats:{shuli:10,xiayi:10,xinzhi:10},fav:{}},simD=null,simS={currentNode:null,stats:{gpa:3,interest:0,skill:0}},favC={},chatCtx={},tt=null;
+let cardData=null,cardCache=null,_gachaBusy=false;
+let cardState={owned:[],draws:0,lastDaily:'',knowledgeRead:0,drawHistory:[]};
+let _lang=localStorage.getItem('mb_lang')||'zh';
+const I18N={zh:{home:'首页',discover:'发现',toolbox:'工具箱',knowledge:'知识卡片',story:'葫芦侠行',simulator:'专业模拟器',favorites:'我的收藏',profile:'个人中心',search:'搜索内容...',guest:'游客用户',
+heroTitle:'葫芦数学 · 用玩的方式打开数学',heroSub:'数学不是公式,是你理解世界的方式',heroCTA:'开始探索 →',
+exploreTitle:'探索数学的万千可能',tryTitle:'试试这些',
+mbtiName:'MathBTI 数学人格',mbtiDesc:'12题发现你的数学人格,领养专属数学小人 →',
+toolboxName:'数学工具箱',toolboxDesc:'口算竞赛 · 计算器 · 符号查询 · 函数绘图 →',
+storyName:'葫芦侠行',storyDesc:'北宋数学武侠 · 8章93节点 · 8种结局 →',
+simName:'数学人生',simDesc:'模拟大学数学系4年,选择方向走向不同结局 →',
+videoName:'发现视频',videoDesc:'12个学科分区',cardName:'知识卡片',cardDesc:'8个数学话题',
+aiName:'AI 助教',aiDesc:'随时解答数学疑问',
+petTip:'每天使用工具箱和模拟器获取经验升级',
+dailyFactPrefix:'💡 数学冷知识:',
+discoverGreet:'Hello there. 这里收集了来自各专业志愿者的数学科普视频。',
+filterAll:'全部',noVideo:'暂无视频',noCard:'暂无卡片',noResult:'无结果',
+loadFailVideo:'视频加载失败',loadFailCard:'卡片加载失败',loadFailProfile:'加载失败',
+loadMore:'下拉加载更多…',loadEnd:'— 已经到底啦 · 共 ',loadEndSuf:' 个视频 —',
+close:'关闭',save:'保存',saveOK:'保存成功',saveFail:'保存失败',
+toolboxHeader:'工具箱',toolboxSub:'动手做数学——口算、计算、查询、绘图',
+toolMathrace:'口算竞赛',toolMathraceDesc:'限时60秒,挑战你的心算速度',
+toolCalc:'计算器',toolCalcDesc:'支持加减乘除、幂运算、三角函数',
+toolSym:'数学符号查询',toolSymDesc:'点击符号查看含义和LaTeX写法',
+toolGraph:'函数图形绘图',toolGraphDesc:'输入 f(x),实时绘制函数曲线',
+knowledgeHeader:'知识卡片',knowledgeSub:'轻量数学发现,每天一个新概念',
+storyHeader:'葫芦侠行',storySub:'北宋数学武侠互动文字冒险 · 8章93节点 · 8+种结局',
+storyLoadFail:'故事加载失败',storyNodeNotFound:'节点未找到',
+simHeader:'专业模拟器',simSub:'模拟大学数学系4年 · 选择方向 · 走向不同结局',
+simLoadFail:'模拟器加载失败',simReset:'重新开始',
+favHeader:'我的收藏',favSub:'已收藏的视频和知识卡片',
+favEmpty:'暂无收藏',favEmptyHint:'在发现页点击 🤍 即可收藏',
+profileHeader:'个人中心',profileEdit:'编辑资料',
+profileAvatar:'头像:',profileNick:'昵称:',
+profileWatchCount:'观看次数',profileFavCount:'收藏数量',
+aiHeader:'AI 数学助教',aiWelcome:'你好!我是葫芦数学的AI助教 👋 试试问我:学数学对学医有什么用?',
+aiPlaceholder:'输入问题...',aiOffline:'抱歉,AI暂时无法回复',aiNetworkErr:'网络异常,请稍后重试',
+calcErr:'表达式有误',calcPlaceholder:'输入表达式 如 2+3*4',
+graphPlaceholder:'如 Math.sin(x) 或 x*x',graphBtn:'绘制',
+mrTitle:'准备好了吗?',mrPlaceholder:'输入答案',mrTimeout:'⏰ 时间到!得分:',mrCorrect:'正确',mrRight:'✅ 正确!+10分',
+symDetail:'👆 点击符号查看详情',
+mbtiFloat:'测测你的数学人格',
+aiAskBtn:'问问AI',aiAskSuffix:'是什么?',
+petLevelUp:'升级啦!Lv.{n}',
+staticHeroTitle:'葫芦数学 · 用玩的方式打开数学',staticHeroSub:'数学不是公式,是你理解世界的方式',staticHeroCTA:'开始探索 →',
+staticExploreTitle:'探索数学的万千可能',staticTryTitle:'试试这些',
+staticDiscoverGreet:'Hello there. 这里收集了来自各专业志愿者的数学科普视频。',
+staticToolboxTitle:'🧰 工具箱',staticToolboxSub:'动手做数学——口算、计算、查询、绘图',
+staticToolMathrace:'口算竞赛',staticToolMathraceDesc:'限时60秒,挑战你的心算速度',
+staticToolCalc:'计算器',staticToolCalcDesc:'支持加减乘除、幂运算、三角函数',
+staticToolSym:'数学符号查询',staticToolSymDesc:'点击符号查看含义和LaTeX写法',
+staticToolGraph:'函数图形绘图',staticToolGraphDesc:'输入 f(x),实时绘制函数曲线',
+staticKnowledgeTitle:'📚 知识卡片',staticKnowledgeSub:'轻量数学发现,每天一个新概念',
+staticStoryTitle:'⚔️ 葫芦侠行',staticStorySub:'北宋数学武侠互动文字冒险 · 8章93节点 · 8+种结局',
+staticSimTitle:'🎮 专业模拟器',staticSimSub:'模拟大学数学系4年 · 选择方向 · 走向不同结局',
+staticFavTitle:'❤️ 我的收藏',staticFavSub:'已收藏的视频和知识卡片',
+staticProfileTitle:'👤 个人中心',
+staticAIToggleTitle:'AI数学助教',staticAIHeader:'💬 AI 数学助教',
+staticAIWelcome:'你好!我是葫芦数学的AI助教 👋 试试问我:学数学对学医有什么用?',
+staticAIPlaceholder:'输入问题...',staticMbtiTooltip:'测测你的数学人格'},
+en:{home:'Home',discover:'Discover',toolbox:'Toolbox',knowledge:'Cards',story:'HuLu Quest',simulator:'Simulator',favorites:'Favorites',profile:'Profile',search:'Search...',guest:'Guest',
+heroTitle:'Hulu Math · Play your way into math',heroSub:'Math isn\'t formulas. It\'s how you make sense of things.',heroCTA:'Start Exploring →',
+exploreTitle:'Explore Math',tryTitle:'Try These',
+mbtiName:'MathBTI Personality Test',mbtiDesc:'12 questions. One math soul. Meet your sprite. →',
+toolboxName:'Math Toolbox',toolboxDesc:'Mental Math · Calculator · Symbol Lookup · Graph →',
+storyName:'HuLu Quest',storyDesc:'A Song Dynasty math wuxia · 8 chapters · 8+ endings →',
+simName:'Math Life',simDesc:'Live four years as a math major. Choose your path. →',
+videoName:'Discover Videos',videoDesc:'12 subject areas',cardName:'Knowledge Cards',cardDesc:'8 math topics',
+aiName:'AI Tutor',aiDesc:'Ask anything. No judgment.',
+petTip:'Come back daily to earn EXP and level up your sprite.',
+dailyFactPrefix:'💡 Math Trivia: ',
+discoverGreet:'A hand-picked collection of math videos, curated by volunteers from across the sciences.',
+filterAll:'All',noVideo:'No videos yet',noCard:'No cards yet',noResult:'No results',
+loadFailVideo:'Couldn\'t load videos',loadFailCard:'Couldn\'t load cards',loadFailProfile:'Couldn\'t load',
+loadMore:'Scroll for more…',loadEnd:'— That\'s all of them · ',loadEndSuf:' videos —',
+close:'Close',save:'Save',saveOK:'Saved',saveFail:'Couldn\'t save',
+toolboxHeader:'Toolbox',toolboxSub:'Get your hands dirty — race the clock, crunch numbers, explore symbols, draw curves',
+toolMathrace:'Mental Math',toolMathraceDesc:'60 seconds. How high can you score?',
+toolCalc:'Calculator',toolCalcDesc:'Quick arithmetic, powers, trig — whatever you need.',
+toolSym:'Symbol Lookup',toolSymDesc:'Tap a symbol to see what it means and how to write it in LaTeX.',
+toolGraph:'Function Grapher',toolGraphDesc:'Type any f(x) and watch its curve appear in real time.',
+knowledgeHeader:'Knowledge Cards',knowledgeSub:'One idea at a time. Bite-sized math, built for curiosity.',
+storyHeader:'HuLu Quest',storySub:'A math-fueled wuxia set in the Song Dynasty. 8 chapters. 8+ endings.',
+storyLoadFail:'Couldn\'t load the story',storyNodeNotFound:'Node not found',
+simHeader:'Math Major Simulator',simSub:'Four years. Real choices. Multiple endings. What kind of mathematician will you become?',
+simLoadFail:'Couldn\'t load the simulator',simReset:'Start fresh',
+favHeader:'My Favorites',favSub:'Your saved videos and cards',
+favEmpty:'Nothing saved yet',favEmptyHint:'Tap 🤍 on any video or card to save it here.',
+profileHeader:'Profile',profileEdit:'Edit Profile',
+profileAvatar:'Avatar:',profileNick:'Nickname:',
+profileWatchCount:'Videos watched',profileFavCount:'Favorites',
+aiHeader:'AI Math Tutor',aiWelcome:'Hey! I\'m your Hulu Math AI tutor 👋 Try asking: "Why do we even need calculus?"',
+aiPlaceholder:'Ask anything...',aiOffline:'The AI is taking a quick break. Try again in a moment.',aiNetworkErr:'Looks like you\'re offline. Check your connection and try again.',
+calcErr:'That doesn\'t look right',calcPlaceholder:'e.g. 2+3*4',
+graphPlaceholder:'e.g. Math.sin(x) or x*x',graphBtn:'Plot',
+mrTitle:'Ready?',mrPlaceholder:'Type your answer',mrTimeout:'⏰ Time\'s up! You scored ',mrCorrect:'right',mrRight:'✅ Correct! +10',
+symDetail:'👆 Tap any symbol to learn more',
+mbtiFloat:'Discover your Math Personality',
+aiAskBtn:'Ask AI',aiAskSuffix:'?',
+petLevelUp:'Level up! Now Lv.{n}',
+staticHeroTitle:'Hulu Math · Play your way into math',staticHeroSub:'Math isn\'t formulas. It\'s how you make sense of things.',staticHeroCTA:'Start Exploring →',
+staticExploreTitle:'Explore Math',staticTryTitle:'Try These',
+staticDiscoverGreet:'A hand-picked collection of math videos, curated by volunteers from across the sciences.',
+staticToolboxTitle:'🧰 Toolbox',staticToolboxSub:'Get your hands dirty — race the clock, crunch numbers, explore symbols, draw curves',
+staticToolMathrace:'Mental Math',staticToolMathraceDesc:'60 seconds. How high can you score?',
+staticToolCalc:'Calculator',staticToolCalcDesc:'Quick arithmetic, powers, trig — whatever you need.',
+staticToolSym:'Symbol Lookup',staticToolSymDesc:'Tap a symbol to see what it means and how to write it in LaTeX.',
+staticToolGraph:'Function Grapher',staticToolGraphDesc:'Type any f(x) and watch its curve appear in real time.',
+staticKnowledgeTitle:'📚 Knowledge Cards',staticKnowledgeSub:'One idea at a time. Bite-sized math, built for curiosity.',
+staticStoryTitle:'⚔️ HuLu Quest',staticStorySub:'A math-fueled wuxia set in the Song Dynasty. 8 chapters. 8+ endings.',
+staticSimTitle:'🎮 Math Major Simulator',staticSimSub:'Four years. Real choices. Multiple endings. What kind of mathematician will you become?',
+staticFavTitle:'❤️ My Favorites',staticFavSub:'Your saved videos and cards',
+staticProfileTitle:'👤 Profile',
+staticAIToggleTitle:'AI Math Tutor',staticAIHeader:'💬 AI Math Tutor',
+staticAIWelcome:'Hey! I\'m your Hulu Math AI tutor 👋 Try asking: "Why do we even need calculus?"',
+staticAIPlaceholder:'Ask anything...',
+staticMbtiTooltip:'Discover your Math Personality'}};
+function t(k){const d=I18N[_lang];return (d&&d[k])||(I18N.zh[k]||k);}
+function rI18n(){
+document.querySelectorAll('[data-i18n]').forEach(el=>{
+const k=el.getAttribute('data-i18n');
+const v=t(k);
+if(el.tagName==='INPUT'){el.placeholder=v}
+else if(el.hasAttribute('title')&&el.getAttribute('data-i18n-type')==='title'){el.title=v}
+else{el.innerHTML=v}
+});
+}
+function switchLang(l){
+_lang=l;localStorage.setItem('mb_lang',l);
+document.querySelectorAll('.lang-btn').forEach(b=>b.classList.toggle('active',b.dataset.lang===l));
+document.getElementById('globalSearch').placeholder=t('search');
+rI18n();
+navigateTo(CP);
+api('/api/user/profile?user_id='+USER_ID).then(r=>{if(r.code===0&&r.data){document.getElementById('sidebarName').textContent=r.data.nickname||t('guest')}}).catch(()=>{});
+}
+function toggleSidebar(){document.getElementById('sidebar').classList.toggle('open');document.getElementById('sidebarOverlay').classList.toggle('active')}
+function closeSidebar(){document.getElementById('sidebar').classList.remove('open');document.getElementById('sidebarOverlay').classList.remove('active')}
+let searchTimeout;
+function handleGlobalSearch(){
+clearTimeout(searchTimeout);
+searchTimeout=setTimeout(()=>{
+const q=document.getElementById('globalSearch').value.trim().toLowerCase();
+const el=document.getElementById('searchResults');
+if(!q||q.length<1){el.classList.remove('show');return}
+const results=[];
+allV.forEach(v=>{if((v.title||'').toLowerCase().includes(q)||(v.author||'').toLowerCase().includes(q)||(v.discipline||'').toLowerCase().includes(q))results.push({t:t('videoTitle')||'Video',title:v.title,id:v.id,type:'video'})});
+allK.forEach(k=>{if((k.title||'').toLowerCase().includes(q)||(k.description||'').toLowerCase().includes(q))results.push({t:t('cardLabel')||'Card',title:k.title,id:k.id,type:'knowledge'})});
+if(results.length===0){el.innerHTML=''+t('noResult')+'
';el.classList.add('show');return}
+el.innerHTML=results.slice(0,8).map(r=>''+(r.t==='视频'?'Video':r.t==='知识'?'Card':r.t)+' '+esc(r.title)+'
').join('');
+el.classList.add('show');
+},200);
+}
+function searchNavigate(type,id){
+document.getElementById('globalSearch').value='';document.getElementById('searchResults').classList.remove('show');
+if(type==='video'){navigateTo('discover');setTimeout(()=>openVideo(id,'','','','',''),500)}
+else if(type==='knowledge'){navigateTo('discover');setTimeout(()=>openKnowledgeDetail(id),500)}
+}
+document.addEventListener('click',e=>{if(!e.target.closest('.sidebar-search'))document.getElementById('searchResults').classList.remove('show')});
+function toggleAIPanel(){var p=document.getElementById('aiPanel');if(!p)return;var m=document.getElementById('toolModal');if(m)m.classList.remove('show');if(p.classList.contains('open')){p.classList.remove('open')}else{p.classList.add('open')}}
+const _apiCache={},_CACHEABLE=['/api/videos','/api/knowledge','/api/story','/api/simulator','/api/math_personality'];
+async function api(p,o={}){const u=API_BASE+p,c={headers:{'Content-Type':'application/json'},...o};if(c.body&&typeof c.body==='object')c.body=JSON.stringify(c.body);const isGet=!c.method;if(isGet&&_CACHEABLE.some(x=>p.indexOf(x)===0)&&_apiCache[u])return _apiCache[u];const r=await fetch(u,c);const j=await r.json();if(isGet&&_CACHEABLE.some(x=>p.indexOf(x)===0))_apiCache[u]=j;return j}
+function navigateTo(p){
+CP=p;closeSidebar();closeToolModal();document.getElementById('searchResults').classList.remove('show');
+if(p!=='home'&&heroAnimId){cancelAnimationFrame(heroAnimId);heroAnimId=null}
+document.querySelectorAll('.nav-item').forEach(b=>b.classList.toggle('active',b.dataset.page===p));
+document.querySelectorAll('.page').forEach(pg=>pg.classList.remove('active'));
+const t=document.getElementById('page-'+p);if(t)t.classList.add('active');
+document.getElementById('mainContent').scrollTop=0;
+if(p==='home')loadHome();else if(p==='discover')loadDiscover();else if(p==='toolbox')loadToolbox();
+else if(p==='knowledge')loadKnowledgePage();else if(p==='story')loadStory();else if(p==='simulator')loadSimulator();else if(p==='favorites')loadFavorites();else if(p==='profile')loadProfile();else if(p==='cards')loadCardsPage();
+}
+let heroAnimId=null;
+async function loadHome(){
+chatCtx={};
+setTimeout(drawHeroCanvas,100);
+const pet=JSON.parse(localStorage.getItem('mathbrain_pet')||'null');
+const pa=document.getElementById('homePetArea');
+if(pet){pa.innerHTML=''+pet.icon+'
'+esc(pet.name)+'
🎓 '+esc(pet.type)+' · Lv.'+pet.level+' · '+pet.exp+'/100 exp
'+t('petTip')+'
';}
+else{pa.innerHTML='';}
+document.getElementById('homeFeatureCards').innerHTML=[
+{i:'🧬',n:t('mbtiName'),d:t('mbtiDesc'),click:"window.open('/mathbti','_blank')"},
+{i:'🧰',n:t('toolboxName'),d:t('toolboxDesc'),click:"navigateTo('toolbox')"},
+{i:'⚔️',n:t('storyName'),d:t('storyDesc'),click:"navigateTo('story')"},
+{i:'🎮',n:t('simName'),d:t('simDesc'),click:"navigateTo('simulator')"}
+].map(c=>'').join('');
+document.getElementById('homeExtraCards').innerHTML=[
+{i:'📺',n:t('videoName'),d:t('videoDesc'),click:"navigateTo('discover')"},
+{i:'📚',n:t('cardName'),d:t('cardDesc'),click:"navigateTo('knowledge')"},
+{i:'💬',n:t('aiName'),d:t('aiDesc'),click:"toggleAIPanel()"}
+].map(c=>'').join('');
+const facts=['π的小数点后第762位开始连续出现六个9,被称为「费曼点」。','世界上最大的已知质数有超过4100万位数字。','哥德巴赫猜想:每个大于2的偶数都可以写成两个质数之和——至今未被证明。','四色定理:任何地图只需4种颜色就能让相邻区域颜色不同。1976年用计算机证明。','莫比乌斯带只有一面——拿一条纸带,扭转180°后粘起来试试。'];
+document.getElementById('homeDailyFact').innerHTML=t('dailyFactPrefix')+facts[Math.floor(Math.random()*facts.length)];
+rCG();
+}
+function drawHeroCanvas(){
+const c=document.getElementById('heroCanvas');if(!c)return;
+if(heroAnimId){cancelAnimationFrame(heroAnimId);heroAnimId=null}
+const ctx=c.getContext('2d'),w=c.width,h=c.height;
+let t=0;
+function frame(){
+ctx.clearRect(0,0,w,h);
+ctx.strokeStyle='#D6D1CA';ctx.lineWidth=1;
+for(let i=0;i0.7){
+ctx.save();ctx.globalAlpha=Math.min(1,(Math.sin(t*0.3)-0.7)/0.3*0.5);
+ctx.strokeStyle='#A8907E';ctx.lineWidth=1;
+const gx=w-60,gy=h-30;
+ctx.beginPath();
+ctx.ellipse(gx,gy-28,12,9,0,0,Math.PI*2);
+ctx.ellipse(gx,gy,16,13,0,0,Math.PI*2);
+ctx.stroke();
+ctx.restore();
+}
+t+=0.02;heroAnimId=requestAnimationFrame(frame);
+}
+frame();
+}
+async function loadDiscover(){
+chatCtx={};
+if(favP){await favP;favP=null}
+if(allV.length){rDF();rVCNP('discoverVideos',allV);return}
+renderSkeletons('discoverVideos',6);
+try{const r=await api('/api/videos?module=数人');if(r.code===0){allV=r.data||[];vD=r.disciplines||[];rDF();rVCNP('discoverVideos',allV)}}catch(e){document.getElementById('discoverVideos').innerHTML=''+t('loadFailVideo')+'
'}
+}
+const VC_PAGE=12;
+let vcList=[],vcCount=0,vcObserver=null;
+const UP_COLORS=['#5B6AF0','#818CF8','#F59E0B','#10B981','#EF4444','#8B5CF6','#EC4899','#14B8A6'];
+function hashStr(s){let h=0;for(let i=0;i=10000?(n/10000).toFixed(1)+'万':''+n}
+function fmtDur(m){m=parseInt(m)||0;return(m<10?'0'+m:''+m)+':00'}
+function rVCNP(cid,vids){
+const c=document.getElementById(cid);
+vcList=vids||[];vcCount=0;c.innerHTML='';
+if(!vcList.length){c.innerHTML=''+t('noVideo')+'
';return}
+appendVC(cid);
+}
+function appendVC(cid){
+const c=document.getElementById(cid);
+const batch=vcList.slice(vcCount,vcCount+VC_PAGE);
+if(!batch.length)return;
+vcCount+=batch.length;
+const old=document.getElementById('npLoadHint');if(old)old.remove();
+c.insertAdjacentHTML('beforeend',batch.map(buildVC).join(''));
+if(vcCount'+t('loadMore')+'');
+if(!vcObserver){vcObserver=new IntersectionObserver(es=>{es.forEach(en=>{if(en.isIntersecting)appendVC('discoverVideos')})},{root:document.getElementById('mainContent'),rootMargin:'240px'})}
+vcObserver.observe(document.getElementById('npLoadHint'));
+}else if(vcList.length>VC_PAGE){
+c.insertAdjacentHTML('beforeend',''+t('loadEnd')+vcList.length+t('loadEndSuf')+'
');
+}
+}
+function buildVC(v){
+const dur=v.duration_min||5,disc=v.discipline||'',author=v.author||'';
+const icon=v.discipline_icon&&v.discipline_icon!=='·'?v.discipline_icon:'📺';
+const h=hashStr(disc||v.title);
+const coverBg='linear-gradient(135deg,hsl('+(h%360)+',45%,90%),hsl('+((h+30)%360)+',45%,84%))';
+const views=hashStr(v.title)%50000+300,dms=hashStr(v.title+'#dm')%900;
+const upColor=UP_COLORS[hashStr(author)%UP_COLORS.length];
+const initial=(author||'?').replace(/学院|·|同学|系/g,'').slice(0,1)||'?';
+const faved=favC['video_'+v.id];
+const click='onclick="openVideo('+v.id+',\''+esc(v.title)+'\',\''+esc(v.description||'')+'\',\''+esc(v.video_url||'')+'\',\''+esc(author)+'\',\''+esc(disc)+'\')"';
+const cover=v.cover_url
+?''
+:''+icon+'
';
+return ''+
+'
'+cover+
+''+fmtDur(dur)+' '+
+'▶ '+
+''+(faved?'❤️':'🤍')+'
'+
+'
'+esc(v.title)+'
'+
+'
👁 '+fmtNum(views)+' 💬 '+fmtNum(dms)+'
'+
+'
'+esc(initial)+' '+esc(author)+'
'+
+'
';
+}
+async function loadKnowledgePage(){
+chatCtx={type:'knowledge'};
+if(allK.length){rKCFilters();rKCCards();rKP();return}
+try{const r=await api('/api/knowledge');if(r.code===0){allK=r.data||[];kT=r.topics||[];rKCFilters();rKCCards()}}catch(e){document.getElementById('knowledgeGrid').innerHTML='卡片加载失败
'}}
+function rKCFilters(){let h=''+t('filterAll')+' ';kT.forEach(t=>{h+=''+esc(t)+' '});document.getElementById('knowledgeFilters').innerHTML=h}
+function fKC(t){cT=t;rKCFilters();rKCCards()}
+function rKP(){
+const bar=document.getElementById('knowledgeDrawProgress');
+if(!bar)return;
+const readThisSession=(cardState.knowledgeRead||0)%3;
+const needMore=3-readThisSession;
+bar.style.display='flex';
+if(needMore<=0){
+document.getElementById('drawProgressText').textContent='🎉 可以抽卡了!点击去抽卡';
+document.getElementById('drawProgressFill').style.width='100%';
+bar.classList.add('draw-progress-ready');
+}else{
+document.getElementById('drawProgressText').textContent='再读 '+needMore+' 张知识卡片即可抽卡';
+document.getElementById('drawProgressFill').style.width=(readThisSession/3*100)+'%';
+bar.classList.remove('draw-progress-ready');
+}
+}
+function rKCCards(){const cards=cT?allK.filter(c=>c.topic===cT):allK;const g=document.getElementById('knowledgeGrid');if(!cards||cards.length===0){g.innerHTML=''+t('noCard')+'
';return}g.innerHTML=cards.map(c=>''+c.icon+' '+esc(c.question)+'
'+esc(c.title)+'
'+esc(c.description)+'
'+esc(c.read_time)+' ').join('');setTimeout(rKP,100)}
+function rDF(){let h=''+t('filterAll')+' ';vD.forEach(d=>{h+=''+(d.discipline_icon||'')+' '+esc(d.discipline)+' '});document.getElementById('discoverFilters').innerHTML=h}
+function fV(d){cD=d;rDF();rVCNP('discoverVideos',d?allV.filter(v=>v.discipline===d):allV)}
+function openVideo(id,title,desc,url,author,disc){chatCtx={type:'video',title:title};rH('video',id,title);let h=''+esc(title)+' ';h+=url?'
':'▶
';h+=''+esc(desc||'No description')+'
'+esc(author)+' '+esc(disc)+'
'+t('close')+'
';document.getElementById('toolModalContent').innerHTML=h;document.getElementById('toolModal').classList.add('show')}
+async function openKnowledgeDetail(id){chatCtx={type:'knowledge',title:''};
+cardState.knowledgeRead=(cardState.knowledgeRead||0)+1;
+if(cardState.knowledgeRead>=3&&cardState.knowledgeRead%3===0){
+cardState.draws+=1;
+saveCardState();
+}else{
+saveCardState();
+}
+rKP();
+try{const r=await api('/api/knowledge/'+id);if(r.code===0){const c=r.data;chatCtx.title=c.title;document.getElementById('toolModalContent').innerHTML=''+c.icon+'
'+esc(c.title)+' '+esc(c.question)+'
'+esc(c.detail||c.description)+'
'+esc(c.read_time)+' · '+esc(c.topic||'')+' 🤖 '+t('aiAskBtn')+'
'}}catch(e){}document.getElementById('toolModal').classList.add('show')}
+function closeToolModal(){var m=document.getElementById('toolModal');if(m)m.classList.remove('show')}
+function loadToolbox(){chatCtx={}}
+let toolState={};
+function openTool(name){
+let h='';
+if(name==='mathrace'){toolState={score:0,total:0,time:60,timer:null,correct:0};h=rMR()}
+else if(name==='calculator')h=rCalc();
+else if(name==='symbols')h=rSym();
+else if(name==='grapher')h=rGraph();
+document.getElementById('toolModalContent').innerHTML=h;
+document.getElementById('toolModal').classList.add('show');
+if(name==='mathrace')startMRLoop();
+if(name==='grapher')setTimeout(plotGraph,200);
+}
+function rMR(){return''}
+function startMRLoop(){toolState={score:0,total:0,time:60,timer:null,correct:0};document.getElementById('mrScore').textContent='0';document.getElementById('mrTime').textContent='60s';document.getElementById('mrTotal').textContent='0';document.getElementById('mrResult').textContent='';document.getElementById('mrAnswer').value='';document.getElementById('mrAnswer').disabled=false;document.getElementById('mrAnswer').focus();genMR();toolState.timer=setInterval(()=>{toolState.time--;document.getElementById('mrTime').textContent=toolState.time+'s';document.getElementById('mrTimer').style.width=(toolState.time/60*100)+'%';if(toolState.time<=0){clearInterval(toolState.timer);document.getElementById('mrAnswer').disabled=true;document.getElementById('mrResult').innerHTML='⏰ 时间到!得分:'+toolState.score+' 正确:'+toolState.correct+'/'+toolState.total;addPetExp(toolState.score)}},1000);genMR()}
+function genMR(){const ops=['+','-','×'];const op=ops[Math.floor(Math.random()*3)];let a,b;if(op==='+'){a=Math.floor(Math.random()*90)+10;b=Math.floor(Math.random()*90)+10}else if(op==='-'){a=Math.floor(Math.random()*90)+10;b=Math.floor(Math.random()*a)}else{a=Math.floor(Math.random()*12)+2;b=Math.floor(Math.random()*12)+2}toolState.answer=op==='+'?a+b:op==='-'?a-b:a*b;toolState.total++;document.getElementById('mrTotal').textContent=toolState.total;document.getElementById('mrQuestion').textContent=a+' '+op+' '+b+' = ?';document.getElementById('mrAnswer').value='';document.getElementById('mrAnswer').focus()}
+function checkMR(){const ans=parseInt(document.getElementById('mrAnswer').value);if(isNaN(ans))return;if(ans===toolState.answer){toolState.score+=10;toolState.correct++;document.getElementById('mrResult').innerHTML='✅ '+t('mrRight')+' '}else{document.getElementById('mrResult').innerHTML='❌ '+toolState.answer+' '}document.getElementById('mrScore').textContent=toolState.score;if(toolState.time>0)setTimeout(genMR,600)}
+function rCalc(){return'=
'+['(',')','C','÷','7','8','9','×','4','5','6','-','1','2','3','+','0','.','sin(','√('].map(k=>''+k+' ').join('')+'
'}
+function calcKey(k){const i=document.getElementById('calcInput');if(k==='C')i.value='';else if(k==='÷')i.value+='/';else if(k==='×')i.value+='*';else if(k==='sin(')i.value+='Math.sin(';else if(k==='√(')i.value+='Math.sqrt(';else i.value+=k;i.focus()}
+function doCalc(){try{const v=eval(document.getElementById('calcInput').value);document.getElementById('calcResult').textContent=Number.isInteger(v)?v:v.toFixed(6)}catch(e){document.getElementById('calcResult').textContent='表达式有误'}}
+function rSym(){
+const syms=[['π','Pi · 圆周率','3.14159... · \\pi'],['∞','无穷大','没有边界 · \\infty'],['Σ','求和符号','累加 · \\sum'],['∫','积分符号','面积/累积 · \\int'],['√','平方根','x²=a → x=√a · \\sqrt{}'],['Δ','变化量/判别式','差值△y/△x · \\Delta'],['∂','偏导数','多元函数求导 · \\partial'],['lim','极限','趋近于 · \\lim_{x \\to 0}'],['e','自然常数','≈2.71828 · e'],['i','虚数单位','i² = -1 · i'],['∏','连乘符号','累乘 · \\prod'],['∈','属于','元素∈集合 · \\in']];
+return''+syms.map(s=>'
'+s[0]+''+s[1]+'
').join('')+'
👆 点击符号查看详情
'
+}
+function sS(sym,name,latex){document.getElementById('symDetail').innerHTML=''+sym+' '+name+'
LaTeX: '+latex+'
'}
+function rGraph(){return'
绘制
'}
+function plotGraph(){const c=document.getElementById('graphCanvas');if(!c)return;const ctx=c.getContext('2d');ctx.clearRect(0,0,c.width,c.height);ctx.strokeStyle='#D6D1CA';ctx.lineWidth=1;for(let i=0;i<=c.width;i+=50){ctx.beginPath();ctx.moveTo(i,0);ctx.lineTo(i,c.height);ctx.stroke()}for(let i=0;i<=c.height;i+=50){ctx.beginPath();ctx.moveTo(0,i);ctx.lineTo(c.width,i);ctx.stroke()}ctx.strokeStyle='#808C95';ctx.lineWidth=2;ctx.beginPath();const fn=new Function('x','return '+document.getElementById('graphFunc').value);const sx=c.width/2,sy=c.height/2,scale=30;for(let px=0;px-100&&py故事加载失败'}}
+function rSN(ni){if(!sD||!sD.nodes||!sD.nodes[ni]){document.getElementById('storyScene').innerHTML='节点未找到
';return}const n=sD.nodes[ni];sVN.currentNode=ni;localStorage.setItem('mb_story_save',JSON.stringify(sVN));const ch=dC(ni),cN=['','初入江湖','西部教习','数理论道','脑学初创','报国之路','暗流涌动','陋室修炼','真相之门'];document.getElementById('storyStats').innerHTML='📐 '+(sVN.stats.shuli||10)+' ⚔️ '+(sVN.stats.xiayi||10)+' 🧘 '+(sVN.stats.xinzhi||10)+' '+ch+' · '+(cN[ch]||'')+' 🔄 '+t('simReset')+' ';document.getElementById('storyScene').innerHTML=(n.character?''+esc(n.character)+'
':'')+'
';document.getElementById('storyChoices').innerHTML='';if(tt)clearTimeout(tt);const te=document.getElementById('storyText'),tx=n.scene||'';let i=0;te.textContent='';function ty(){if(i{let h='';if(c.effects){const p=[];if(c.effects.shuli)p.push('数理根基'+(c.effects.shuli>0?'+':'')+c.effects.shuli);if(c.effects.xiayi)p.push('侠义声望'+(c.effects.xiayi>0?'+':'')+c.effects.xiayi);if(c.effects.xinzhi)p.push('心志韧性'+(c.effects.xinzhi>0?'+':'')+c.effects.xinzhi);if(p.length)h='['+p.join(' | ')+'] '}return''+esc(c.text)+h+' '}).join('')}
+function sC(next,ef){aSE(ef);rSN(next)}
+function aSE(ef){if(!ef)return;if(ef.shuli)sVN.stats.shuli=(sVN.stats.shuli||10)+ef.shuli;if(ef.xiayi)sVN.stats.xiayi=(sVN.stats.xiayi||10)+ef.xiayi;if(ef.xinzhi)sVN.stats.xinzhi=(sVN.stats.xinzhi||10)+ef.xinzhi;if(ef.favorability){if(!sVN.fav)sVN.fav={};for(const k in ef.favorability)sVN.fav[k]=(sVN.fav[k]||0)+ef.favorability[k]}}
+function resetStory(){if(tt)clearTimeout(tt);sVN={currentNode:null,stats:{shuli:10,xiayi:10,xinzhi:10},fav:{}};localStorage.removeItem('mb_story_save');loadStory()}
+function dC(ni){if(!ni)return 1;const m=ni.match(/^ch(\d+)/);return m?parseInt(m[1]):1}
+async function loadSimulator(){chatCtx={type:'simulator'};try{const r=await api('/api/simulator');if(r.code===0){simD=r.data;try{const sr=await api('/api/simulator/save?user_id='+USER_ID);if(sr.code===0&&sr.data)simS=sr.data}catch(e){}const sn=simD.start_node||'y1_welcome';if(!simS.currentNode||!simD.nodes||!simD.nodes[simS.currentNode])simS.currentNode=sn;rSiN(simS.currentNode)}}catch(e){document.getElementById('simScene').innerHTML='模拟器加载失败
'}}
+function rSiN(ni){if(!simD||!simD.nodes||!simD.nodes[ni]){document.getElementById('simScene').innerHTML='节点未找到
';return}const n=simD.nodes[ni];simS.currentNode=ni;api('/api/simulator/save',{method:'POST',body:{user_id:USER_ID,state:simS}}).catch(()=>{});document.getElementById('simStats').innerHTML=''+(simS.stats.gpa||3).toFixed(1)+'
GPA
'+(simS.stats.interest||0)+'
'+t('interestLabel')+'
'+(simS.stats.skill||0)+'
'+t('skillLabel')+'
🔄 '+t('simReset')+' ';document.getElementById('simScene').innerHTML=(n.stage?'📅 '+esc(n.stage)+'
':'')+(n.character?''+esc(n.character)+'
':'')+''+esc(n.scene||'')+'
';document.getElementById('simChoices').innerHTML=(n.choices||[]).map(c=>''+esc(c.text)+(c.hint?''+esc(c.hint)+' ':'')+' ').join('')}
+function siC(next,ef){if(ef){if(ef.gpa)simS.stats.gpa=Math.max(0,Math.min(4,(simS.stats.gpa||3)+ef.gpa*.1));if(ef.interest)simS.stats.interest=(simS.stats.interest||0)+ef.interest;if(ef.skill)simS.stats.skill=(simS.stats.skill||0)+ef.skill}rSiN(next);addPetExp(5)}
+function resetSim(){simS={currentNode:null,stats:{gpa:3,interest:0,skill:0}};loadSimulator()}
+async function loadFavorites(){chatCtx={};try{await rFC()}catch(e){}try{const r=await api('/api/favorites?user_id='+USER_ID);const g=document.getElementById('favGrid');if(r.code===0){const f=r.data||[];if(f.length===0){g.innerHTML='暂无收藏在发现页点击 🤍 即可收藏
';return}g.innerHTML=f.map(fv=>'✕ '+(fv.target_type==='video'?'📺 视频':'📚 知识卡片')+'
'+esc(fv.title)+'
').join('')}}catch(e){document.getElementById('favGrid').innerHTML='加载失败
'}}
+async function rFC(){try{const r=await api('/api/favorites?user_id='+USER_ID);if(r.code===0){favC={};(r.data||[]).forEach(f=>{favC[f.target_type+'_'+f.target_id]=true})}}catch(e){}}
+async function tF(type,id,title,cover){if(favC[type+'_'+id]){await api('/api/favorites',{method:'DELETE',body:{user_id:USER_ID,target_type:type,target_id:id}});favC[type+'_'+id]=false;showToast('🗑 '+t('close')+' ✔')}else{await api('/api/favorites',{method:'POST',body:{user_id:USER_ID,target_type:type,target_id:id,title:title,cover:cover}});favC[type+'_'+id]=true;showToast('❤ '+t('saveOK'))}if(CP==='discover')fV(cD);else if(CP==='favorites')loadFavorites()}
+async function rF(type,id){await api('/api/favorites',{method:'DELETE',body:{user_id:USER_ID,target_type:type,target_id:id}});loadFavorites()}
+async function rH(type,id,title){try{await api('/api/history',{method:'POST',body:{user_id:USER_ID,target_type:type,target_id:id,title:title}})}catch(e){}}
+async function loadProfile(){chatCtx={};try{const[pr,sr]=await Promise.all([api('/api/user/profile?user_id='+USER_ID),api('/api/user/stats?user_id='+USER_ID)]);const p=pr.code===0?pr.data:{nickname:'游客用户',avatar_emoji:'葫',avatar_color:'#5B6AF0'};const s=sr.code===0?sr.data:{watch_count:0,favorite_count:0};document.getElementById('profileContent').innerHTML=''+esc(p.avatar_emoji||'葫')+'
'+esc(p.nickname||'游客用户')+'
ID: '+USER_ID+'
'}catch(e){document.getElementById('profileContent').innerHTML='加载失败
'}}
+async function sP(){const n=document.getElementById('editNickname').value.trim();const a=document.getElementById('editAvatar').value.trim();try{await api('/api/user/profile',{method:'POST',body:{user_id:USER_ID,nickname:n,avatar_emoji:a}});document.getElementById('sidebarName').textContent=n||'游客用户';document.getElementById('sidebarAvatar').textContent=a||'葫';alert('保存成功');loadProfile()}catch(e){alert('保存失败')}}
+async function loadCardsPage(){
+chatCtx={};
+document.getElementById('cardsProgress').textContent=cardState.owned.length+'/16';
+const drawBtn=document.getElementById('cardsDrawBtn');
+const drawCount=document.getElementById('cardsDrawCount');
+drawCount.textContent=cardState.draws;
+drawBtn.style.display=cardState.draws>0?'inline-flex':'none';
+const grid=document.getElementById('cardsCollectionGrid');
+await loadCardData();
+const allCodes=getAllCardCodes();
+if(!allCodes.length){grid.innerHTML='卡片数据加载中…
';return}
+let h='';
+allCodes.forEach(code=>{
+const info=getCardInfo(code);
+const clan=info?getClanInfo(info.clan):null;
+const clanColor=clan?clan.color:'#A8907E';
+const owned=cardState.owned.indexOf(code)!==-1;
+h+=''+
+(owned
+?'
'+(info?info.icon:'?')+'
'+(info?info.name:'???')+'
'+code+'
'+(clan?clan.emoji+' '+clan.name:'')+'
'
+:'
?
???
未获得
'+(clan?clan.emoji+' '+clan.name:'未知')+'
')+
+'
';
+});
+grid.innerHTML=h;
+}
+function addPetExp(amt){
+let pet=JSON.parse(localStorage.getItem('mathbrain_pet')||'null');
+if(!pet)return;
+pet.exp=(pet.exp||0)+amt;
+let lvlU=false;
+while(pet.exp>=100){pet.exp-=100;pet.level=(pet.level||1)+1;lvlU=true}
+localStorage.setItem('mathbrain_pet',JSON.stringify(pet));
+const bar=document.querySelector('.pet-exp-fill');
+if(bar){const pct=pet.exp%100;bar.style.transition='none';bar.style.width='0%';requestAnimationFrame(()=>{requestAnimationFrame(()=>{bar.style.transition='width .4s cubic-bezier(.34,1.56,.64,1)';bar.style.width=pct+'%'})})}
+if(lvlU){showToast('🎉 '+t('petLevelUp').replace('{n}',pet.level));burstConfetti(document.querySelector('.pet-card'))}
+}
+async function sendChat(){const i=document.getElementById('chatInput');const m=i.value.trim();if(!m)return;aCM('user',m);i.value='';const tid=aCT();try{const r=await api('/api/chat',{method:'POST',body:{message:m,context:chatCtx}});rCT(tid);aCM('assistant',r.code===0?r.reply:'抱歉,AI暂时无法回复')}catch(e){rCT(tid);aCM('assistant','网络异常,请稍后重试')}}
+function qA(q){document.getElementById('chatInput').value=q;closeToolModal();toggleAIPanel();setTimeout(()=>sendChat(),300)}
+function aCM(role,text){const c=document.getElementById('aiMessages');const d=document.createElement('div');d.className='chat-msg '+role;d.innerHTML=''+esc(text)+'
';c.appendChild(d);c.scrollTop=c.scrollHeight}
+function aCT(){const c=document.getElementById('aiMessages');const d=document.createElement('div');d.className='chat-msg assistant';d.innerHTML='
';c.appendChild(d);c.scrollTop=c.scrollHeight;return d}
+function rCT(el){if(el&&el.parentNode)el.remove()}
+function esc(t){if(!t)return'';esc._d=esc._d||document.createElement('div');esc._d.textContent=t;return esc._d.innerHTML}
+function adj(hex,amt){const n=parseInt(hex.replace('#',''),16);const r=Math.min(255,(n>>16)+amt);const g=Math.min(255,((n>>8)&0xFF)+amt);const b=Math.min(255,(n&0xFF)+amt);return '#'+((r<<16)|(g<<8)|b).toString(16).padStart(6,'0')}
+function toggleHub(){document.getElementById('floatHub').classList.toggle('open')}
+function closeHub(){document.getElementById('floatHub').classList.remove('open')}
+document.addEventListener('click',e=>{if(!e.target.closest('.float-hub'))document.getElementById('floatHub').classList.remove('open')});
+function initCardState(){
+const raw=localStorage.getItem('hulu_card_state');
+if(raw){try{cardState=JSON.parse(raw)}catch(e){}}
+if(!cardState.owned)cardState.owned=[];
+if(!cardState.draws&&cardState.draws!==0)cardState.draws=0;
+if(!cardState.lastDaily)cardState.lastDaily='';
+if(!cardState.knowledgeRead&&cardState.knowledgeRead!==0)cardState.knowledgeRead=0;
+if(!cardState.drawHistory)cardState.drawHistory=[];
+const today=new Date().toISOString().slice(0,10);
+if(cardState.lastDaily!==today){
+cardState.draws+=1;
+cardState.lastDaily=today;
+saveCardState();
+}
+}
+function saveCardState(){localStorage.setItem('hulu_card_state',JSON.stringify(cardState));rCG();}
+function ownedCount(){return cardState.owned.length}
+function grantCard(code,method){
+if(!code)return;
+if(cardState.owned.indexOf(code)===-1){
+cardState.owned.push(code);
+cardState.drawHistory.push({code:code,date:new Date().toISOString().slice(0,10),method:method});
+saveCardState();
+return true;
+}
+return false;
+}
+async function loadCardData(){
+if(cardData)return cardData;
+try{
+const r=await api('/api/math_personality');
+if(r.code===0&&r.data&&r.data.results){cardData=r.data;cardCache=r.data;return cardData}
+}catch(e){}
+return null;
+}
+function getCardInfo(code){
+if(!cardCache||!cardCache.results)return null;
+return cardCache.results[code]||null;
+}
+function getAllCardCodes(){
+if(!cardCache||!cardCache.results)return[];
+return Object.keys(cardCache.results);
+}
+function getClanInfo(clanId){
+if(!cardCache||!cardCache.clans)return null;
+return cardCache.clans[clanId]||null;
+}
+function rCG(){
+const wrap=document.getElementById('homeCardGallery');
+const btn=document.getElementById('homeGachaBtn');
+const cnt=document.getElementById('cardCountText');
+const scroll=document.getElementById('cardGalleryScroll');
+if(!wrap||!scroll)return;
+const owned=cardState.owned;
+if(cnt)cnt.textContent=owned.length+'/16';
+if(owned.length===0){
+if(!localStorage.getItem('mathbrain_pet')){
+scroll.innerHTML='🏮 去做MathBTI 领取你的第一张数学人物小卡
';
+}else{
+scroll.innerHTML='🏮 还没获得卡片 去知识卡片页阅读攒抽卡机会吧
';
+}
+wrap.style.display='block';
+if(btn){
+if(cardState.draws>0){
+btn.style.display='inline-flex';
+document.getElementById('homeDrawCount').textContent=cardState.draws;
+}else{
+btn.style.display='none';
+}
+}
+return;
+}
+wrap.style.display='block';
+loadCardData().then(()=>{
+let h='';
+owned.forEach(code=>{
+const info=getCardInfo(code);
+const clan=info?getClanInfo(info.clan):null;
+const clanColor=clan?clan.color:'#A8907E';
+h+=''+
+'
'+(info?info.icon:'?')+'
'+
+'
'+(info?info.name:'')+'
';
+});
+scroll.innerHTML=h;
+});
+if(btn){
+if(cardState.draws>0){
+btn.style.display='inline-flex';
+document.getElementById('homeDrawCount').textContent=cardState.draws;
+}else{
+btn.style.display='none';
+}
+}
+}
+function openGacha(){
+document.getElementById('gachaOverlay').style.display='flex';
+document.getElementById('gachaIdle').style.display='block';
+document.getElementById('gachaResult').style.display='none';
+document.getElementById('gachaDrawCount').textContent=cardState.draws;
+const all=cardData?Object.keys(cardData.results).length:16;
+const missing=all-cardState.owned.length;
+let pityHint='';
+if(cardState.drawHistory.length>0){
+const sinceNew=cardState.drawHistory.slice().reverse().findIndex(d=>cardState.owned.indexOf(d.code)===-1);
+if(sinceNew>=4)pityHint='⭐ 下次抽卡必出新卡!';
+else if(sinceNew>=0)pityHint='再抽'+(4-sinceNew)+'次保底出新卡';
+}
+document.getElementById('gachaPityHint').textContent=pityHint||'剩余未收集:'+missing+'/16';
+}
+function closeGacha(){document.getElementById('gachaOverlay').style.display='none'}
+function doDraw(){
+if(_gachaBusy)return;
+if(cardState.draws<=0){showToast('没有抽卡次数了!去读知识卡片获取吧');return}
+_gachaBusy=true;
+const gourd=document.getElementById('gachaGourd');
+gourd.classList.add('gourd-shaking');
+document.getElementById('gachaIdle').style.display='block';
+document.getElementById('gachaResult').style.display='none';
+setTimeout(()=>{
+gourd.classList.remove('gourd-shaking');
+cardState.draws--;
+saveCardState();
+let code;
+const allCodes=getAllCardCodes();
+const sinceNewIndex=cardState.drawHistory.slice().reverse().findIndex(d=>!cardState.owned.includes(d.code));
+if(sinceNewIndex>=4||(cardState.drawHistory.length>0&&sinceNewIndex===-1)){
+const missing2=allCodes.filter(c=>!cardState.owned.includes(c));
+code=missing2[Math.floor(Math.random()*missing2.length)];
+}else{
+code=allCodes[Math.floor(Math.random()*allCodes.length)];
+}
+const isNew=cardState.owned.indexOf(code)===-1;
+if(isNew){
+cardState.owned.push(code);
+}
+cardState.drawHistory.push({code:code,date:new Date().toISOString().slice(0,10),method:'gacha'});
+saveCardState();
+showDrawResult(code,isNew);
+const cr=document.querySelector('.gacha-card-reveal');
+if(cr)setTimeout(()=>burstGachaConfetti(cr),300);
+_gachaBusy=false;
+rCG();
+},900);
+}
+function showDrawResult(code,isNew){
+loadCardData().then(()=>{
+const info=getCardInfo(code);
+const clan=info?getClanInfo(info.clan):null;
+const clanColor=clan?clan.color:'#A8907E';
+document.getElementById('gachaIdle').style.display='none';
+const res=document.getElementById('gachaResult');
+res.style.display='block';
+res.innerHTML=
+''+
+(info&&info.portrait
+?'
'+(info.icon||'?')+' '
+:'
'+(info?info.icon:'?')+'
')+
+'
'+(info?info.name:'???')+'
'+
+'
'+code+'
'+
+(clan?'
'+clan.emoji+' '+clan.name+' · '+(info?info.mathematician:'')+'
':'')+
+'
'+(info?info.tagline:'')+'
'+
+(isNew?'':'
(重复获得 · 已自动分解)
')+
+'
📋 查看卡片详情 '+
+'
再抽一次 (剩余'+cardState.draws+'次)
'+
+'
';
+document.getElementById('gachaDrawCount').textContent=cardState.draws;
+});
+}
+function openCardDetail(code){
+loadCardData().then(()=>{
+const info=getCardInfo(code);
+if(!info)return;
+const clan=getClanInfo(info.clan);
+const clanColor=clan?clan.color:'#A8907E';
+const hist=cardState.drawHistory.filter(d=>d.code===code);
+const obtainInfo=hist.length>0
+?(hist[0].method==='mathbti'?'📋 MathBTI测试获得':'🏮 葫芦抽卡获得')+' · '+hist[0].date
+:'未知途径';
+const modal=document.getElementById('cardDetailModal');
+modal.innerHTML=
+''+
+''+
+'
人物简介
'+esc(info.description||'')+'
'+
+'
代表数学家 · '+esc(info.mathematician)+'
'+esc(info.bio||'')+'
'+
+'
💡 '+esc(info.bio_fact||'')+'
'+
+'
🎴 '+obtainInfo+'
'+
+'
';
+document.getElementById('cardDetailOverlay').style.display='flex';
+});
+}
+function closeCardDetail(){document.getElementById('cardDetailOverlay').style.display='none'}
+function burstGachaConfetti(el){
+if(!el)return;const r=el.getBoundingClientRect();const cx=r.left+r.width/2,cy=r.top+r.height/2;
+const colors=['#A8907E','#C47F3A','#D4A574','#8A9BA8','#C49595','#8AA68A'];
+for(let i=0;i<18;i++){const p=document.createElement('div');p.className='gacha-particle';const a=Math.random()*Math.PI*2,d=40+Math.random()*60;p.style.cssText='left:'+cx+'px;top:'+cy+'px;--gpx:'+(Math.cos(a)*d)+'px;--gpy:'+(Math.sin(a)*d)+'px;background:'+colors[Math.floor(Math.random()*colors.length)];document.body.appendChild(p);setTimeout(()=>p.remove(),1100)}
+}
+function showToast(msg){
+let c=document.getElementById('toastBox');if(!c){c=document.createElement('div');c.id='toastBox';c.className='toast-container';document.body.appendChild(c)}
+const t=document.createElement('div');t.className='toast';t.innerHTML=msg;c.appendChild(t);setTimeout(()=>t.remove(),2400);
+}
+function burstConfetti(el){
+if(!el)return;const r=el.getBoundingClientRect();const cx=r.left+r.width/2,cy=r.top+r.height/2;
+const colors=['#808C95','#A8907E','#8AA68A','#C4A467','#C49595','#9AA5AE'];
+for(let i=0;i<14;i++){const p=document.createElement('div');p.className='particle';const a=Math.random()*Math.PI*2,d=30+Math.random()*50;p.style.cssText='left:'+cx+'px;top:'+cy+'px;width:6px;height:6px;border-radius:2px;background:'+colors[Math.floor(Math.random()*colors.length)]+';--px:'+(Math.cos(a)*d)+'px;--py:'+(Math.sin(a)*d)+'px';document.body.appendChild(p);setTimeout(()=>p.remove(),750)}
+}
+function renderSkeletons(cid,count){
+const c=document.getElementById(cid);c.innerHTML='';
+for(let i=0;i
')}
+}
+let favP=null;
+document.addEventListener('DOMContentLoaded',()=>{
+document.querySelectorAll('.lang-btn').forEach(b=>b.classList.toggle('active',b.dataset.lang===_lang));
+document.getElementById('globalSearch').placeholder=t('search');
+rI18n();
+loadHome();
+initCardState();
+loadCardData();
+setTimeout(rCG,300);
+favP=rFC();
+api('/api/videos?module=数人').then(r=>{if(r.code===0){allV=r.data||[];vD=r.disciplines||[]}}).catch(()=>{});
+api('/api/knowledge').then(r=>{if(r.code===0){allK=r.data||[];kT=r.topics||[]}}).catch(()=>{});
+api('/api/user/profile?user_id='+USER_ID).then(r=>{if(r.code===0&&r.data){document.getElementById('sidebarName').textContent=r.data.nickname||'游客用户';document.getElementById('sidebarAvatar').textContent=r.data.avatar_emoji||'葫';document.getElementById('sidebarAvatar').style.background=r.data.avatar_color||'#808C95'}}).catch(()=>{});
+});
diff --git a/static/js/mathbti.js b/static/js/mathbti.js
new file mode 100644
index 0000000..cd91fc6
--- /dev/null
+++ b/static/js/mathbti.js
@@ -0,0 +1,447 @@
+// MathBTI 前端逻辑 — 12题 → 16结果 → 4族
+let testData = null, answers = [], currentQ = 0, myResult = null;
+
+async function init() {
+ try {
+ const r = await fetch('/data/seed_mathbti.json');
+ testData = await r.json();
+ renderIntro();
+ } catch (e) {
+ document.getElementById('introScreen').innerHTML =
+ '';
+ }
+}
+
+function renderIntro() {
+ const axesEl = document.getElementById('introAxes');
+ if (axesEl && testData.axes) {
+ axesEl.innerHTML = testData.axes.map(a =>
+ '' +
+ '' + (a.emoji_left || '') + ' ' +
+ '' + a.name + ' ' +
+ '' + (a.emoji_right || '') + ' ' +
+ '
'
+ ).join('');
+ }
+ document.querySelector('.intro-meta').textContent =
+ '⏱ 约2分钟 · 4选项 · 16种人格';
+}
+
+function startQuiz() {
+ answers = new Array(testData.questions.length).fill(-1);
+ currentQ = 0;
+ switchScreen('quizScreen');
+ renderQuestion();
+}
+
+function renderQuestion() {
+ const q = testData.questions[currentQ];
+ document.getElementById('quizQuestion').textContent = q.question;
+ const optEl = document.getElementById('quizOptions');
+ const sel = answers[currentQ];
+ optEl.innerHTML = q.options.map((o, i) =>
+ '' +
+ ' ' +
+ '' + o.text + ' '
+ ).join('');
+
+ // Progress
+ const pct = Math.round((currentQ + 1) / testData.questions.length * 100);
+ document.getElementById('progressFill').style.width = pct + '%';
+ // 最后一题时进度条末尾显示葫芦图标
+ const isLast=currentQ===testData.questions.length-1;
+ document.getElementById('progressFill').style.setProperty('--is-last',isLast?'1':'0');
+ document.getElementById('progressText').textContent = (currentQ + 1) + '/' + testData.questions.length;
+
+ // Next button
+ const btn = document.getElementById('btnNext');
+ btn.disabled = answers[currentQ] === -1;
+ btn.textContent = currentQ === testData.questions.length - 1 ? '查看结果' : '下一题';
+}
+
+function selectOption(idx) {
+ answers[currentQ] = idx;
+ document.getElementById('btnNext').disabled = false;
+ // Update visual
+ document.querySelectorAll('.quiz-option').forEach((el, i) => {
+ el.classList.toggle('selected', i === idx);
+ });
+}
+
+async function nextQuestion() {
+ if (answers[currentQ] === -1) return;
+
+ if (currentQ < testData.questions.length - 1) {
+ currentQ++;
+ renderQuestion();
+ return;
+ }
+
+ // Last question → calculate locally
+ const btn = document.getElementById('btnNext');
+ btn.disabled = true;
+ btn.textContent = '计算中…';
+
+ try {
+ myResult = calculateResult(answers);
+ // 自动获得对应的数学人物卡
+ const mbtiCode=myResult.binary_code;
+ let gotNewCard=false;
+ if(mbtiCode){
+ let cardState2=JSON.parse(localStorage.getItem('hulu_card_state')||'{}');
+ if(!cardState2.owned)cardState2.owned=[];
+ if(!cardState2.drawHistory)cardState2.drawHistory=[];
+ if(cardState2.owned.indexOf(mbtiCode)===-1){
+ cardState2.owned.push(mbtiCode);
+ cardState2.drawHistory.push({code:mbtiCode,date:new Date().toISOString().slice(0,10),method:'mathbti'});
+ localStorage.setItem('hulu_card_state',JSON.stringify(cardState2));
+ gotNewCard=true;
+ }
+ }
+ renderResult();
+ // 卡片领取提示
+ if(gotNewCard){
+ const res=myResult.result;
+ const notice=document.getElementById('cardNotice');
+ if(notice){notice.style.display='block';document.getElementById('cardNoticeName').textContent=res?res.name:'—'}
+ showToast('🎴 获得数学人物小卡!前往葫芦数学查看卡册');
+ }
+ switchScreen('resultScreen');
+ } catch (e) {
+ showToast('提交失败,请重试');
+ btn.disabled = false;
+ btn.textContent = currentQ === testData.questions.length - 1 ? '查看结果' : '下一题';
+ }
+}
+
+function calculateResult(answers) {
+ const axes = testData.scoring.axes; // ["style","purpose","era","social"]
+ const maxPerAxis = testData.scoring.max_per_axis;
+ const cutoff = testData.scoring.cutoff;
+
+ // 计算每个轴的得分
+ const axisScores = {};
+ axes.forEach(axis => {
+ axisScores[axis] = 0;
+ });
+ testData.questions.forEach((q, i) => {
+ const score = q.options[answers[i]].score;
+ axisScores[q.axis] += score;
+ });
+
+ // 生成二进制代码 (≤cutoff=0左极, ≥cutoff+1=1右极)
+ const binaryCode = axes.map(axis => axisScores[axis] <= cutoff ? '0' : '1').join('');
+
+ // 获取结果
+ const result = testData.results[binaryCode];
+
+ return {
+ binary_code: binaryCode,
+ axis_scores: axisScores,
+ result: result
+ };
+}
+
+function renderResult() {
+ const res = myResult.result;
+ if (!res) return;
+
+ // Portrait
+ const portraitEl = document.getElementById('resultPortrait');
+ if (res.portrait) {
+ portraitEl.innerHTML = '' + (res.icon || 'Σ') + ' ';
+ } else {
+ portraitEl.innerHTML = '' + (res.icon || 'Σ') + '
';
+ }
+
+ // Name + Clan
+ document.getElementById('resultName').innerHTML = res.name + ' ' + myResult.binary_code + ' ';
+ document.getElementById('resultTagline').textContent = res.tagline || '';
+
+ // Clan badge
+ const clanData = testData.clans[res.clan];
+ document.getElementById('resultMathematician').innerHTML =
+ (clanData ? '' + clanData.emoji + ' ' + clanData.name + ' · ' + res.mathematician + ' ' : '') +
+ '代表数学家:' + (res.mathematician || '—') + ' ';
+
+ // Keywords
+ const kwEl = document.getElementById('resultKeywords');
+ if (res.keywords) {
+ kwEl.innerHTML = res.keywords.map(k => '' + k + ' ').join('');
+ kwEl.style.display = 'flex';
+ } else {
+ kwEl.style.display = 'none';
+ }
+
+ // Description
+ document.getElementById('resultDesc').textContent = res.description || '';
+ document.getElementById('resultShort').textContent = res.short_desc || '';
+
+ // Bio
+ document.getElementById('bioTitle').textContent = '关于 ' + (res.mathematician || '这位数学家');
+ document.getElementById('bioText').textContent = res.bio || '';
+ document.getElementById('bioFact').textContent = res.bio_fact || '';
+
+ // Axes bars
+ const axesEl = document.getElementById('resultAxes');
+ if (testData.axes) {
+ axesEl.innerHTML = testData.axes.map(a => {
+ const score = myResult.axis_scores[a.id] || 0;
+ const maxScore = testData.scoring.max_per_axis || 9;
+ const pct = Math.round(score / maxScore * 100);
+ return '' +
+ '
' + (a.emoji_left || '') + ' ' + a.left + ' ' +
+ '
' +
+ '
' + a.right + ' ' + (a.emoji_right || '') + ' ' +
+ '
';
+ }).join('');
+ axesEl.style.display = 'block';
+ }
+
+ // Five-dimension ability card from 16位数学家五维能力表
+ const s5El = document.getElementById('resultStats5');
+ if (s5El && res.stats5) {
+ const labels = ['眼光', '人文', '侦探', '建模', '联结'];
+ s5El.innerHTML = '五维能力
' + labels.map(k => {
+ const v = res.stats5[k] || 0;
+ return '' +
+ '
' + k + ' ' +
+ '
' +
+ '
' + v + ' ' +
+ '
';
+ }).join('');
+ s5El.style.display = 'block';
+ }
+
+ // Adopt pet
+ const petData = JSON.parse(localStorage.getItem('mb_pet') || '{}');
+ const adoptBtn = document.querySelector('.btn-adopt');
+ if (petData.type) {
+ adoptBtn.textContent = '✅ 已领养 · 我的数学精灵';
+ adoptBtn.classList.add('adopted');
+ }
+
+ // Apply clan color
+ if (clanData) {
+ document.getElementById('resultScreen').style.setProperty('--clan-color', clanData.color);
+ }
+}
+
+// ---- Bio Expander ----
+function toggleBio() {
+ const body = document.getElementById('bioBody');
+ const arrow = document.getElementById('bioArrow');
+ const expanded = body.style.maxHeight && body.style.maxHeight !== '0px';
+ if (expanded) {
+ body.style.maxHeight = '0px';
+ arrow.textContent = '▾';
+ } else {
+ body.style.maxHeight = body.scrollHeight + 'px';
+ arrow.textContent = '▴';
+ }
+}
+
+// ---- Adopt Pet ----
+function adoptPet() {
+ if (!myResult) return;
+ const res = myResult.result;
+ const dims = {};
+ ['眼光', '人文', '侦探', '建模', '联结'].forEach(k => {
+ dims[k] = Math.max(10, Math.min(100, Number((res.stats5 && res.stats5[k]) || 0)));
+ });
+ const pet = {
+ name: res.name || '—',
+ type: res.name || '—',
+ icon: res.icon || 'Σ',
+ color: res.color || '#5B6AF0',
+ mathematician: res.mathematician || '',
+ clan: res.clan || '',
+ binary_code: myResult.binary_code,
+ level: 1,
+ exp: 0,
+ dims: dims
+ };
+ localStorage.setItem('mb_pet', JSON.stringify(pet));
+ localStorage.setItem('mathbrain_pet', JSON.stringify(pet));
+ const btn = document.querySelector('.btn-adopt');
+ btn.textContent = '✅ 已领养 · 我的数学精灵';
+ btn.classList.add('adopted');
+ showToast('🧬 数学精灵已领养!每天来葫芦数学 App 喂它成长');
+}
+
+// ---- Share ----
+function copyShare() {
+ if (!myResult) return;
+ const res = myResult.result;
+ const clanData = testData.clans[res.clan];
+ const text = '我的数学人格是「' + res.name + '」' + (res.icon || '') + '\n' +
+ (clanData ? clanData.emoji + ' ' + clanData.name + ' · ' + res.mathematician : '') + '\n' +
+ (res.short_desc || '') + '\n\n' +
+ '👉 你也来测测:' + window.location.href + '\n\n' +
+ '#数学人格测试 #MathBTI';
+ navigator.clipboard.writeText(text).then(() => showToast('已复制分享文案!'));
+}
+
+function shareResult() {
+ if (!myResult) return;
+ const res = myResult.result;
+ const text = '我的数学人格是「' + res.name + '」' + (res.icon || '') + '!' + (res.short_desc || '');
+ if (navigator.share) {
+ navigator.share({ title: 'MathBTI · 数学人格测试', text: text, url: window.location.href }).catch(() => {});
+ } else {
+ copyShare();
+ }
+}
+
+function generateShareCard() {
+ if (!myResult) return;
+ const res = myResult.result;
+ const clanData = testData.clans[res.clan];
+ const canvas = document.getElementById('shareCardCanvas');
+ const ctx = canvas.getContext('2d');
+ canvas.width = 600;
+ canvas.height = 800;
+
+ // Background
+ const gradient = ctx.createLinearGradient(0, 0, 600, 800);
+ gradient.addColorStop(0, (res.color || '#5B6AF0') + '22');
+ gradient.addColorStop(1, (res.color || '#5B6AF0') + '08');
+ ctx.fillStyle = '#FAFBFC';
+ ctx.fillRect(0, 0, 600, 800);
+ ctx.fillStyle = gradient;
+ ctx.fillRect(0, 0, 600, 800);
+
+ // Top accent bar
+ ctx.fillStyle = res.color || '#5B6AF0';
+ ctx.fillRect(0, 0, 600, 6);
+
+ // Portrait placeholder
+ ctx.fillStyle = res.icon_bg || '#F0F0F0';
+ ctx.beginPath();
+ ctx.arc(300, 180, 70, 0, Math.PI * 2);
+ ctx.fill();
+ ctx.fillStyle = '#334155';
+ ctx.font = '60px sans-serif';
+ ctx.textAlign = 'center';
+ ctx.textBaseline = 'middle';
+ ctx.fillText(res.icon || 'Σ', 300, 180);
+
+ // Name
+ ctx.fillStyle = '#1E293B';
+ ctx.font = 'bold 36px "PingFang SC", sans-serif';
+ ctx.fillText(res.name, 300, 280);
+
+ // Clan + Mathematician
+ ctx.fillStyle = '#64748B';
+ ctx.font = '18px "PingFang SC", sans-serif';
+ if (clanData) {
+ ctx.fillText(clanData.emoji + ' ' + clanData.name + ' · ' + res.mathematician, 300, 315);
+ }
+
+ // Tagline
+ ctx.fillStyle = res.color || '#5B6AF0';
+ ctx.font = 'italic 15px "PingFang SC", sans-serif';
+ const tagline = res.tagline || '';
+ wrapText(ctx, tagline, 300, 350, 480, 22);
+
+ // Separator
+ ctx.fillStyle = '#E2E8F0';
+ ctx.fillRect(60, 380, 480, 1);
+
+ // Short desc
+ ctx.fillStyle = '#334155';
+ ctx.font = '15px "PingFang SC", sans-serif';
+ const desc = res.short_desc || '';
+ wrapText(ctx, desc, 300, 410, 480, 24);
+
+ // Axes
+ let ay = 450;
+ if (testData.axes && myResult.axis_scores) {
+ ctx.fillStyle = '#64748B';
+ ctx.font = '14px "PingFang SC", sans-serif';
+ testData.axes.forEach(a => {
+ const s = myResult.axis_scores[a.id] || 0;
+ const maxS = testData.scoring.max_per_axis || 9;
+ const pct = Math.round(s / maxS * 100);
+ ctx.fillText(a.emoji_left + ' ' + a.left + ' — ' + pct + '% — ' + a.right + ' ' + a.emoji_right, 300, ay);
+ ay += 28;
+ });
+ }
+
+ // Bottom
+ ctx.fillStyle = '#94A3B8';
+ ctx.font = '13px "PingFang SC", sans-serif';
+ ctx.fillText('葫芦数学 · MathBTI', 300, 730);
+ // 葫芦水印
+ ctx.save();ctx.globalAlpha=.06;ctx.font='80px sans-serif';ctx.fillStyle='#A8907E';
+ ctx.fillText('🏮',480,680);
+ ctx.restore();
+ ctx.fillText('你也来测测你的数学人格类型 →', 300, 755);
+
+ // QR placeholder
+ ctx.fillStyle = '#E2E8F0';
+ ctx.fillRect(255, 640, 90, 90);
+ ctx.fillStyle = '#94A3B8';
+ ctx.font = '11px sans-serif';
+ ctx.fillText('扫码测', 300, 685);
+
+ document.getElementById('shareCardPreview').style.display = 'block';
+ document.getElementById('shareCardPreview').scrollIntoView({ behavior: 'smooth' });
+}
+
+function downloadShareCard() {
+ const canvas = document.getElementById('shareCardCanvas');
+ const link = document.createElement('a');
+ link.download = 'mathbti_' + (myResult ? myResult.binary_code : 'result') + '.png';
+ link.href = canvas.toDataURL('image/png');
+ link.click();
+ showToast('📸 图片已保存!');
+}
+
+function wrapText(ctx, text, x, y, maxWidth, lineHeight) {
+ const lines = [];
+ let current = '';
+ for (let i = 0; i < text.length; i++) {
+ const test = current + text[i];
+ if (ctx.measureText(test).width > maxWidth && current.length > 0) {
+ lines.push(current);
+ current = text[i];
+ } else {
+ current = test;
+ }
+ }
+ if (current) lines.push(current);
+ lines.forEach((line, idx) => {
+ ctx.fillText(line, x, y + idx * lineHeight);
+ });
+}
+
+// ---- Retry ----
+function retryQuiz() {
+ answers = [];
+ currentQ = 0;
+ myResult = null;
+ document.getElementById('bioBody').style.maxHeight = '0px';
+ document.getElementById('bioArrow').textContent = '▾';
+ document.getElementById('shareCardPreview').style.display = 'none';
+ switchScreen('introScreen');
+}
+
+// ---- Helpers ----
+function switchScreen(id) {
+ document.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
+ document.getElementById(id).classList.add('active');
+ window.scrollTo({ top: 0, behavior: 'smooth' });
+}
+
+let toastTimer = null;
+function showToast(msg) {
+ const el = document.getElementById('toast');
+ el.textContent = msg;
+ el.classList.add('show');
+ clearTimeout(toastTimer);
+ toastTimer = setTimeout(() => el.classList.remove('show'), 2200);
+}
+
+// Bootstrap
+document.addEventListener('DOMContentLoaded', init);
diff --git a/static/js/mathbti.min.js b/static/js/mathbti.min.js
new file mode 100644
index 0000000..15d8ab8
--- /dev/null
+++ b/static/js/mathbti.min.js
@@ -0,0 +1,325 @@
+let testData = null, answers = [], currentQ = 0, myResult = null;
+async function init() {
+try {
+const r = await fetch('/api/math_personality');
+const j = await r.json();
+if (j.code !== 0) { showToast('测试数据加载失败'); return; }
+testData = j.data;
+renderIntro();
+} catch (e) {
+document.getElementById('introScreen').innerHTML =
+'';
+}
+}
+function renderIntro() {
+const axesEl = document.getElementById('introAxes');
+if (axesEl && testData.axes) {
+axesEl.innerHTML = testData.axes.map(a =>
+'' +
+'' + (a.emoji_left || '') + ' ' +
+'' + a.name + ' ' +
+'' + (a.emoji_right || '') + ' ' +
+'
'
+).join('');
+}
+document.querySelector('.intro-meta').textContent =
+'⏱ 约2分钟 · 4选项 · 16种人格';
+}
+function startQuiz() {
+answers = new Array(testData.questions.length).fill(-1);
+currentQ = 0;
+switchScreen('quizScreen');
+renderQuestion();
+}
+function renderQuestion() {
+const q = testData.questions[currentQ];
+document.getElementById('quizQuestion').textContent = q.question;
+const optEl = document.getElementById('quizOptions');
+const sel = answers[currentQ];
+optEl.innerHTML = q.options.map((o, i) =>
+'' +
+' ' +
+'' + o.text + ' '
+).join('');
+const pct = Math.round((currentQ + 1) / testData.questions.length * 100);
+document.getElementById('progressFill').style.width = pct + '%';
+const isLast=currentQ===testData.questions.length-1;
+document.getElementById('progressFill').style.setProperty('--is-last',isLast?'1':'0');
+document.getElementById('progressText').textContent = (currentQ + 1) + '/' + testData.questions.length;
+const btn = document.getElementById('btnNext');
+btn.disabled = answers[currentQ] === -1;
+btn.textContent = currentQ === testData.questions.length - 1 ? '查看结果' : '下一题';
+}
+function selectOption(idx) {
+answers[currentQ] = idx;
+document.getElementById('btnNext').disabled = false;
+document.querySelectorAll('.quiz-option').forEach((el, i) => {
+el.classList.toggle('selected', i === idx);
+});
+}
+async function nextQuestion() {
+if (answers[currentQ] === -1) return;
+if (currentQ < testData.questions.length - 1) {
+currentQ++;
+renderQuestion();
+return;
+}
+const btn = document.getElementById('btnNext');
+btn.disabled = true;
+btn.textContent = '计算中…';
+try {
+const r = await fetch('/api/math_personality/calculate', {
+method: 'POST',
+headers: { 'Content-Type': 'application/json' },
+body: JSON.stringify({ answers })
+});
+const j = await r.json();
+if (j.code !== 0) { showToast('计算结果失败'); return; }
+myResult = j.data;
+const mbtiCode=j.data.binary_code;
+let gotNewCard=false;
+if(mbtiCode){
+let cardState2=JSON.parse(localStorage.getItem('hulu_card_state')||'{}');
+if(!cardState2.owned)cardState2.owned=[];
+if(!cardState2.drawHistory)cardState2.drawHistory=[];
+if(cardState2.owned.indexOf(mbtiCode)===-1){
+cardState2.owned.push(mbtiCode);
+cardState2.drawHistory.push({code:mbtiCode,date:new Date().toISOString().slice(0,10),method:'mathbti'});
+localStorage.setItem('hulu_card_state',JSON.stringify(cardState2));
+gotNewCard=true;
+}
+}
+renderResult();
+if(gotNewCard){
+const res=j.data.result;
+const notice=document.getElementById('cardNotice');
+if(notice){notice.style.display='block';document.getElementById('cardNoticeName').textContent=res?res.name:'—'}
+showToast('🎴 获得数学人物小卡!前往葫芦数学查看卡册');
+}
+switchScreen('resultScreen');
+} catch (e) {
+showToast('提交失败,请重试');
+btn.disabled = false;
+btn.textContent = currentQ === testData.questions.length - 1 ? '查看结果' : '下一题';
+}
+}
+function renderResult() {
+const res = myResult.result;
+if (!res) return;
+const portraitEl = document.getElementById('resultPortrait');
+if (res.portrait) {
+portraitEl.innerHTML = '' + (res.icon || 'Σ') + ' ';
+} else {
+portraitEl.innerHTML = '' + (res.icon || 'Σ') + '
';
+}
+document.getElementById('resultName').innerHTML = res.name + ' ' + myResult.binary_code + ' ';
+document.getElementById('resultTagline').textContent = res.tagline || '';
+const clanData = testData.clans[res.clan];
+document.getElementById('resultMathematician').innerHTML =
+(clanData ? '' + clanData.emoji + ' ' + clanData.name + ' · ' + res.mathematician + ' ' : '') +
+'代表数学家:' + (res.mathematician || '—') + ' ';
+const kwEl = document.getElementById('resultKeywords');
+if (res.keywords) {
+kwEl.innerHTML = res.keywords.map(k => '' + k + ' ').join('');
+kwEl.style.display = 'flex';
+} else {
+kwEl.style.display = 'none';
+}
+document.getElementById('resultDesc').textContent = res.description || '';
+document.getElementById('resultShort').textContent = res.short_desc || '';
+document.getElementById('bioTitle').textContent = '关于 ' + (res.mathematician || '这位数学家');
+document.getElementById('bioText').textContent = res.bio || '';
+document.getElementById('bioFact').textContent = res.bio_fact || '';
+const axesEl = document.getElementById('resultAxes');
+if (testData.axes) {
+axesEl.innerHTML = testData.axes.map(a => {
+const score = myResult.axis_scores[a.id] || 0;
+const maxScore = testData.scoring.max_per_axis || 9;
+const pct = Math.round(score / maxScore * 100);
+return '' +
+'
' + (a.emoji_left || '') + ' ' + a.left + ' ' +
+'
' +
+'
' + a.right + ' ' + (a.emoji_right || '') + ' ' +
+'
';
+}).join('');
+axesEl.style.display = 'block';
+}
+const petData = JSON.parse(localStorage.getItem('mb_pet') || '{}');
+const adoptBtn = document.querySelector('.btn-adopt');
+if (petData.type) {
+adoptBtn.textContent = '✅ 已领养 · 我的数学精灵';
+adoptBtn.classList.add('adopted');
+}
+if (clanData) {
+document.getElementById('resultScreen').style.setProperty('--clan-color', clanData.color);
+}
+}
+function toggleBio() {
+const body = document.getElementById('bioBody');
+const arrow = document.getElementById('bioArrow');
+const expanded = body.style.maxHeight && body.style.maxHeight !== '0px';
+if (expanded) {
+body.style.maxHeight = '0px';
+arrow.textContent = '▾';
+} else {
+body.style.maxHeight = body.scrollHeight + 'px';
+arrow.textContent = '▴';
+}
+}
+function adoptPet() {
+if (!myResult) return;
+const res = myResult.result;
+const pet = {
+type: res.name || '—',
+icon: res.icon || 'Σ',
+color: res.color || '#5B6AF0',
+mathematician: res.mathematician || '',
+clan: res.clan || '',
+binary_code: myResult.binary_code
+};
+localStorage.setItem('mb_pet', JSON.stringify(pet));
+const btn = document.querySelector('.btn-adopt');
+btn.textContent = '✅ 已领养 · 我的数学精灵';
+btn.classList.add('adopted');
+showToast('🧬 数学精灵已领养!每天来葫芦数学 App 喂它成长');
+}
+function copyShare() {
+if (!myResult) return;
+const res = myResult.result;
+const clanData = testData.clans[res.clan];
+const text = '我的数学人格是「' + res.name + '」' + (res.icon || '') + '\n' +
+(clanData ? clanData.emoji + ' ' + clanData.name + ' · ' + res.mathematician : '') + '\n' +
+(res.short_desc || '') + '\n\n' +
+'👉 你也来测测:' + window.location.href + '\n\n' +
+'#数学人格测试 #MathBTI';
+navigator.clipboard.writeText(text).then(() => showToast('已复制分享文案!'));
+}
+function shareResult() {
+if (!myResult) return;
+const res = myResult.result;
+const text = '我的数学人格是「' + res.name + '」' + (res.icon || '') + '!' + (res.short_desc || '');
+if (navigator.share) {
+navigator.share({ title: 'MathBTI · 数学人格测试', text: text, url: window.location.href }).catch(() => {});
+} else {
+copyShare();
+}
+}
+function generateShareCard() {
+if (!myResult) return;
+const res = myResult.result;
+const clanData = testData.clans[res.clan];
+const canvas = document.getElementById('shareCardCanvas');
+const ctx = canvas.getContext('2d');
+canvas.width = 600;
+canvas.height = 800;
+const gradient = ctx.createLinearGradient(0, 0, 600, 800);
+gradient.addColorStop(0, (res.color || '#5B6AF0') + '22');
+gradient.addColorStop(1, (res.color || '#5B6AF0') + '08');
+ctx.fillStyle = '#FAFBFC';
+ctx.fillRect(0, 0, 600, 800);
+ctx.fillStyle = gradient;
+ctx.fillRect(0, 0, 600, 800);
+ctx.fillStyle = res.color || '#5B6AF0';
+ctx.fillRect(0, 0, 600, 6);
+ctx.fillStyle = res.icon_bg || '#F0F0F0';
+ctx.beginPath();
+ctx.arc(300, 180, 70, 0, Math.PI * 2);
+ctx.fill();
+ctx.fillStyle = '#334155';
+ctx.font = '60px sans-serif';
+ctx.textAlign = 'center';
+ctx.textBaseline = 'middle';
+ctx.fillText(res.icon || 'Σ', 300, 180);
+ctx.fillStyle = '#1E293B';
+ctx.font = 'bold 36px "PingFang SC", sans-serif';
+ctx.fillText(res.name, 300, 280);
+ctx.fillStyle = '#64748B';
+ctx.font = '18px "PingFang SC", sans-serif';
+if (clanData) {
+ctx.fillText(clanData.emoji + ' ' + clanData.name + ' · ' + res.mathematician, 300, 315);
+}
+ctx.fillStyle = res.color || '#5B6AF0';
+ctx.font = 'italic 15px "PingFang SC", sans-serif';
+const tagline = res.tagline || '';
+wrapText(ctx, tagline, 300, 350, 480, 22);
+ctx.fillStyle = '#E2E8F0';
+ctx.fillRect(60, 380, 480, 1);
+ctx.fillStyle = '#334155';
+ctx.font = '15px "PingFang SC", sans-serif';
+const desc = res.short_desc || '';
+wrapText(ctx, desc, 300, 410, 480, 24);
+let ay = 450;
+if (testData.axes && myResult.axis_scores) {
+ctx.fillStyle = '#64748B';
+ctx.font = '14px "PingFang SC", sans-serif';
+testData.axes.forEach(a => {
+const s = myResult.axis_scores[a.id] || 0;
+const maxS = testData.scoring.max_per_axis || 9;
+const pct = Math.round(s / maxS * 100);
+ctx.fillText(a.emoji_left + ' ' + a.left + ' — ' + pct + '% — ' + a.right + ' ' + a.emoji_right, 300, ay);
+ay += 28;
+});
+}
+ctx.fillStyle = '#94A3B8';
+ctx.font = '13px "PingFang SC", sans-serif';
+ctx.fillText('葫芦数学 · MathBTI', 300, 730);
+ctx.save();ctx.globalAlpha=.06;ctx.font='80px sans-serif';ctx.fillStyle='#A8907E';
+ctx.fillText('🏮',480,680);
+ctx.restore();
+ctx.fillText('你也来测测你的数学人格类型 →', 300, 755);
+ctx.fillStyle = '#E2E8F0';
+ctx.fillRect(255, 640, 90, 90);
+ctx.fillStyle = '#94A3B8';
+ctx.font = '11px sans-serif';
+ctx.fillText('扫码测', 300, 685);
+document.getElementById('shareCardPreview').style.display = 'block';
+document.getElementById('shareCardPreview').scrollIntoView({ behavior: 'smooth' });
+}
+function downloadShareCard() {
+const canvas = document.getElementById('shareCardCanvas');
+const link = document.createElement('a');
+link.download = 'mathbti_' + (myResult ? myResult.binary_code : 'result') + '.png';
+link.href = canvas.toDataURL('image/png');
+link.click();
+showToast('📸 图片已保存!');
+}
+function wrapText(ctx, text, x, y, maxWidth, lineHeight) {
+const lines = [];
+let current = '';
+for (let i = 0; i < text.length; i++) {
+const test = current + text[i];
+if (ctx.measureText(test).width > maxWidth && current.length > 0) {
+lines.push(current);
+current = text[i];
+} else {
+current = test;
+}
+}
+if (current) lines.push(current);
+lines.forEach((line, idx) => {
+ctx.fillText(line, x, y + idx * lineHeight);
+});
+}
+function retryQuiz() {
+answers = [];
+currentQ = 0;
+myResult = null;
+document.getElementById('bioBody').style.maxHeight = '0px';
+document.getElementById('bioArrow').textContent = '▾';
+document.getElementById('shareCardPreview').style.display = 'none';
+switchScreen('introScreen');
+}
+function switchScreen(id) {
+document.querySelectorAll('.screen').forEach(s => s.classList.remove('active'));
+document.getElementById(id).classList.add('active');
+window.scrollTo({ top: 0, behavior: 'smooth' });
+}
+let toastTimer = null;
+function showToast(msg) {
+const el = document.getElementById('toast');
+el.textContent = msg;
+el.classList.add('show');
+clearTimeout(toastTimer);
+toastTimer = setTimeout(() => el.classList.remove('show'), 2200);
+}
+document.addEventListener('DOMContentLoaded', init);
diff --git a/templates/desktop.html b/templates/desktop.html
new file mode 100644
index 0000000..4f1d864
--- /dev/null
+++ b/templates/desktop.html
@@ -0,0 +1,190 @@
+
+
+
+
+
+葫芦数学 · Hulu Math
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
葫芦数学 · 用玩的方式打开数学
+
数学不是公式,是你理解世界的方式
+
+
开始探索 →
+
+
+
+
+
+ 🎴 数学人物小卡 · 0/16
+
+
+
+
+
探索数学的万千可能
+
+
试试这些
+
+
+
+
+ 🏮 抽卡 · 0 次机会
+
+
+
+
+
+
Hello there. 这里收集了来自各专业志愿者的数学科普视频。
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
你好!我是葫芦数学的AI助教 👋 试试问我:学数学对学医有什么用?
+
➤
+
+
+
+
+
+
+
+
+
+
+
✕
+
+
🏮
+
葫芦抽卡
+
摇晃葫芦,抽取数学人物小卡
+
剩余抽卡次数:0
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/index.html b/templates/index.html
new file mode 100644
index 0000000..fbfe896
--- /dev/null
+++ b/templates/index.html
@@ -0,0 +1,386 @@
+
+
+
+
+
+葫芦数学 · Hulu Math
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 推荐
+ 热门
+ 专业分区
+
+
+
+
+
知识 · 数学发现
+
+
+
+
+
+
+
+
+
精选剧本
+
+
+
+
+
+
即将上线
+
+
+
+
+
+
+
+ ← 剧本广场
+ 📖 葫芦侠行
+ 🎓 专业模拟器
+
+
+
+
+
◎
+
+
发现值得一看的数学内容
+
从大学专业到现实问题,快速进入你感兴趣的主题
+
+
+
+
+
+ 全部
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
✦ 今日数学发现
+
数学不是公式,是你理解世界的方式
+
每天一个数学概念,让思维变得更清晰
+
+ 📖 3个概念 ⏱ 阅读约15分钟
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 发现
+
+
+
+ 知识
+
+
+
+ 数学人生
+
+
+
+ 我的
+
+
+
+
+
+
+
+
+
+
+
+ 你好!我是数学桥梁计划的 AI 助教
+ 试试问我:
+ · 学数学对学医有什么用?
+ · 数学在建筑里怎么用?
+ · 为什么学数学很重要?
+
+
+
+
+ 数学与医学
+ 数学与建筑
+ 数学与AI
+ 为什么要学
+
+
+
+ ➤
+
+
+
+
+
+
+
+
+
+
+
▶
+
点击下方按钮在浏览器中查看完整课程
+
+
+
+
+ ☆ 收藏
+ ? 问AI
+ 查看原视频
+
+
+
+
+
+
+
+
+
+
+
选择头像
+
+
+
+ ✏️ 改昵称
+ 完成
+
+
+
+
+
+
+
+
🎓
+
结局
+
+
+
+
重新开始
+
关闭
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/math_teen.html b/templates/math_teen.html
new file mode 100644
index 0000000..df2cff3
--- /dev/null
+++ b/templates/math_teen.html
@@ -0,0 +1,411 @@
+
+
+
+
+
+数学少年线 · 互动剧本
+
+
+
+
+
+
葫
+
+
数学少年线
+
八幕完整版 · 大学数学专业四年 · 从录取通知书到直博
+
+
+ 重新开始
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/templates/mathbti.html b/templates/mathbti.html
new file mode 100644
index 0000000..8dd87a2
--- /dev/null
+++ b/templates/mathbti.html
@@ -0,0 +1,102 @@
+
+
+
+
+
+数学人格测试 · MathBTI
+
+
+
+
+
+
+
+
+
+
+ 你习惯靠直觉还是靠推演?
+ 你更喜欢独自钻研还是和同伴讨论?
+ 你偏爱古典数学还是现代科技?
+
+
+
开始测试
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
🧬 领养你的数学精灵
+
它将每天等你回来,喂经验、升级、解锁数学家的人生剧本
+
+
+
+ 📋 复制结果
+ 📸 生成分享卡片
+ 📤 分享给朋友
+
+
+
+ 💾 保存图片
+
+
↻ 重新测试
+
+
+
+
+
+
+
+
diff --git a/vercel.json b/vercel.json
new file mode 100644
index 0000000..5f493e9
--- /dev/null
+++ b/vercel.json
@@ -0,0 +1,22 @@
+{
+ "rewrites": [
+ { "source": "/", "destination": "/templates/index.html" },
+ { "source": "/mathbti", "destination": "/templates/mathbti.html" },
+ { "source": "/desktop", "destination": "/templates/desktop.html" }
+ ],
+ "headers": [
+ {
+ "source": "/static/(.*)",
+ "headers": [
+ { "key": "Access-Control-Allow-Origin", "value": "*" },
+ { "key": "Cache-Control", "value": "public, max-age=31536000, immutable" }
+ ]
+ },
+ {
+ "source": "/data/(.*)",
+ "headers": [
+ { "key": "Access-Control-Allow-Origin", "value": "*" }
+ ]
+ }
+ ]
+}