<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>数学少年线 · 互动剧本</title>
<style>
:root{
  --ink:#182430;
  --ink-2:#263947;
  --paper:#F7F3EA;
  --paper-2:#FFFDF8;
  --teal:#2E7D72;
  --gold:#B8893A;
  --blue:#3F6C8F;
  --red:#A64A44;
  --muted:#7C8790;
  --line:#D8D2C4;
  --shadow:0 18px 50px rgba(24,36,48,.16);
}
*{box-sizing:border-box;margin:0;padding:0}
body{
  min-height:100vh;
  background:
    radial-gradient(circle at 15% 10%, rgba(62,108,143,.16), transparent 32%),
    radial-gradient(circle at 85% 20%, rgba(46,125,114,.14), transparent 30%),
    linear-gradient(160deg,#E9E4D8 0%,#F4F0E7 45%,#DDE5E4 100%);
  color:var(--ink);
  font-family:"PingFang SC","Microsoft YaHei","Segoe UI",sans-serif;
  display:flex;
  align-items:center;
  justify-content:center;
  padding:22px;
}
.shell{
  width:100%;
  max-width:860px;
  background:var(--paper-2);
  border:1px solid var(--line);
  border-radius:18px;
  box-shadow:var(--shadow);
  overflow:hidden;
}
.topbar{
  display:flex;
  align-items:center;
  gap:14px;
  padding:18px 22px;
  background:linear-gradient(120deg,var(--ink) 0%,var(--ink-2) 100%);
  color:#F7F3EA;
}
.mark{
  width:46px;
  height:46px;
  border-radius:50% 50% 50% 50% / 60% 60% 40% 40%;
  display:flex;
  align-items:center;
  justify-content:center;
  font-size:24px;
  font-weight:700;
  background:linear-gradient(140deg,var(--teal),var(--blue));
  color:#fff;
  box-shadow:0 8px 20px rgba(0,0,0,.22);
  flex:none;
}
.topbar h1{font-size:21px;font-weight:700;letter-spacing:.5px}
.topbar p{font-size:12px;color:#C9D4DA;margin-top:4px;line-height:1.5}
.top-actions{margin-left:auto;display:flex;gap:8px}
.top-actions button{
  border:1px solid rgba(255,255,255,.24);
  background:rgba(255,255,255,.08);
  color:#F7F3EA;
  padding:8px 13px;
  border-radius:10px;
  font-size:12px;
  cursor:pointer;
  transition:.2s;
}
.top-actions button:hover{background:rgba(255,255,255,.18)}
.stats{
  display:flex;
  gap:10px;
  padding:14px 22px;
  border-bottom:1px solid var(--line);
  background:var(--paper);
  flex-wrap:wrap;
}
.stat{
  flex:1 1 130px;
  min-width:130px;
  border:1px solid var(--line);
  border-radius:12px;
  padding:10px 12px;
  background:#fff;
  display:flex;
  align-items:center;
  gap:9px;
}
.stat-ico{
  width:30px;
  height:30px;
  border-radius:9px;
  display:flex;
  align-items:center;
  justify-content:center;
  font-size:14px;
  font-weight:700;
  color:#fff;
  flex:none;
}
.stat b{display:block;font-size:15px;line-height:1.2}
.stat span{font-size:11px;color:var(--muted)}
.stage{
  padding:22px 22px 0;
  display:flex;
  align-items:center;
  gap:12px;
  font-size:12px;
  color:var(--muted);
}
.stage-line{height:1px;flex:1;background:var(--line)}
.stage-tag{
  color:var(--teal);
  font-weight:700;
  border:1px solid rgba(46,125,114,.35);
  background:rgba(46,125,114,.08);
  padding:4px 10px;
  border-radius:999px;
  letter-spacing:1px;
}
.reader{
  padding:18px 24px 26px;
  min-height:300px;
}
.speaker{
  display:inline-flex;
  align-items:center;
  gap:8px;
  margin-bottom:14px;
  color:var(--ink-2);
  font-size:13px;
  font-weight:700;
}
.speaker::before{
  content:"";
  width:8px;
  height:8px;
  border-radius:50%;
  background:var(--gold);
}
.scene-text{
  font-size:17px;
  line-height:1.95;
  white-space:pre-wrap;
  color:var(--ink);
  min-height:180px;
}
.choices{
  display:flex;
  flex-direction:column;
  gap:10px;
  padding:0 24px 26px;
}
.choice{
  display:block;
  width:100%;
  text-align:left;
  border:1px solid var(--line);
  border-left:4px solid var(--teal);
  background:#fff;
  color:var(--ink);
  border-radius:12px;
  padding:14px 16px;
  font-size:15px;
  line-height:1.5;
  cursor:pointer;
  transition:.18s;
}
.choice:hover{
  transform:translateY(-2px);
  border-color:var(--teal);
  box-shadow:0 8px 20px rgba(46,125,114,.14);
}
.choice small{
  display:block;
  margin-top:5px;
  color:var(--muted);
  font-size:11px;
}
.end-note{
  text-align:center;
  padding:10px 0 4px;
  color:var(--muted);
  font-size:13px;
}
.footer{
  padding:12px 22px;
  border-top:1px solid var(--line);
  background:var(--paper);
  color:var(--muted);
  font-size:11px;
  text-align:center;
}
@media (max-width:640px){
  body{padding:12px}
  .shell{border-radius:14px}
  .topbar{padding:14px 16px}
  .stats{padding:12px 14px}
  .stat{flex-basis:calc(50% - 6px);min-width:0}
  .reader{padding:16px 16px 20px}
  .choices{padding:0 16px 20px}
  .scene-text{font-size:15px}
}
</style>
</head>
<body>
<div class="shell">
  <div class="topbar">
    <div class="mark">葫</div>
    <div>
      <h1>数学少年线</h1>
      <p>八幕完整版 · 大学数学专业四年 · 从录取通知书到直博</p>
    </div>
    <div class="top-actions">
      <button onclick="resetGame()">重新开始</button>
    </div>
  </div>
  <div class="stats" id="statsBar"></div>
  <div class="stage" id="stageBar"></div>
  <div class="reader">
    <div class="speaker" id="speaker">旁白</div>
    <div class="scene-text" id="sceneText">正在加载剧本……</div>
  </div>
  <div class="choices" id="choicesArea"></div>
  <div class="footer">数学少年线 · 完整互动剧本 v2 · 85 节点 / 107 选项</div>
</div>

<script>
const SAVE_KEY = 'math_teen_story_save';
let storyData = null;
let state = {
  currentNode: null,
  stats: { shuli: 10, xiayi: 10, xinzhi: 10 },
  fav: {},
  history: []
};
let typeTimer = null;

function esc(s){
  return String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;').replace(/"/g,'&quot;').replace(/'/g,'&#39;');
}

function stageOf(id){
  const map = {
    c1: '第一幕 · 大一上：小镇少年走进数学系',
    c2: '第二幕 · 大一下：第一次完整证明',
    c3: '第三幕 · 大二上：讨论班与诺特',
    c4: '第四幕 · 大二下：数学与生活',
    c5: '第五幕 · 大三上：方向与高斯',
    c6: '第六幕 · 大三下：科研低谷与诺特',
    c7: '第七幕 · 大四上：直博申请',
    c8: '第八幕 · 毕业前夜与直博第一年'
  };
  const key = (id || '').slice(0,2);
  return map[key] || '数学少年线';
}

function statColor(k){
  if(k==='shuli') return '#3F6C8F';
  if(k==='xinzhi') return '#2E7D72';
  return '#B8893A';
}

function statLabel(k){
  if(k==='shuli') return '数理根基';
  if(k==='xinzhi') return '心志韧性';
  return '同行温度';
}

function statIcon(k){
  if(k==='shuli') return '∑';
  if(k==='xinzhi') return 'θ';
  return 'π';
}

function renderStats(){
  const keys = ['shuli','xiayi','xinzhi'];
  document.getElementById('statsBar').innerHTML = keys.map(k => {
    const v = state.stats[k] || 0;
    return '<div class="stat"><div class="stat-ico" style="background:'+statColor(k)+'">'+statIcon(k)+'</div><div><b>'+v+'</b><span>'+statLabel(k)+'</span></div></div>';
  }).join('') + '<div class="stat" style="flex-basis:120px"><div class="stat-ico" style="background:#A64A44">4</div><div><b>4 位数学家</b><span>高斯 · 祖冲之 · 欧拉 · 诺特</span></div></div>';
}

function renderStage(){
  document.getElementById('stageBar').innerHTML = '<span class="stage-tag">'+esc(stageOf(state.currentNode))+'</span><span class="stage-line"></span><span>'+esc(storyData.title)+'</span>';
}

function renderNode(){
  if(!storyData || !storyData.nodes) return;
  const node = storyData.nodes[state.currentNode];
  if(!node){
    document.getElementById('sceneText').textContent = '节点未找到，请重新开始。';
    return;
  }
  if(typeTimer) clearTimeout(typeTimer);
  renderStats();
  renderStage();
  document.getElementById('speaker').textContent = node.character || '旁白';
  const textEl = document.getElementById('sceneText');
  const full = node.scene || '';
  textEl.textContent = '';
  let i = 0;
  function type(){
    if(i < full.length){
      textEl.textContent += full.charAt(i);
      i++;
      typeTimer = setTimeout(type, 16);
    } else {
      renderChoices(node.choices || []);
    }
  }
  type();
  saveState();
}

function renderChoices(choices){
  const area = document.getElementById('choicesArea');
  if(!choices.length){
    area.innerHTML = '<div class="end-note">故事结束 · 你的数学，刚刚开始</div><button class="choice" onclick="resetGame()">再走一次这段路</button>';
    return;
  }
  area.innerHTML = choices.map((c, idx) => {
    const effect = c.effects || {};
    const parts = [];
    if(effect.shuli) parts.push('数理根基 '+(effect.shuli>0?'+':'')+effect.shuli);
    if(effect.xinzhi) parts.push('心志韧性 '+(effect.xinzhi>0?'+':'')+effect.xinzhi);
    if(effect.xiayi) parts.push('同行温度 '+(effect.xiayi>0?'+':'')+effect.xiayi);
    const hint = parts.length ? '<small>'+parts.join(' · ')+'</small>' : '';
    return '<button class="choice" onclick="choose(\''+esc(c.next)+'\', '+idx+')">'+esc(c.text)+hint+'</button>';
  }).join('');
}

function choose(next, idx){
  if(typeTimer) clearTimeout(typeTimer);
  const node = storyData.nodes[state.currentNode];
  const choice = (node.choices || [])[idx];
  if(!choice) return;
  const effects = choice.effects || {};
  Object.keys(effects).forEach(k => {
    if(k === 'favorability'){
      Object.keys(effects[k] || {}).forEach(name => {
        state.fav[name] = (state.fav[name] || 0) + effects[k][name];
      });
    } else {
      state.stats[k] = (state.stats[k] || 0) + effects[k];
    }
  });
  state.history.push(state.currentNode);
  state.currentNode = next;
  renderNode();
}

function saveState(){
  try { localStorage.setItem(SAVE_KEY, JSON.stringify(state)); } catch(e){}
}

function loadState(){
  try {
    const raw = localStorage.getItem(SAVE_KEY);
    if(raw){
      const saved = JSON.parse(raw);
      if(saved && saved.currentNode && storyData && storyData.nodes[saved.currentNode]){
        state = Object.assign(state, saved);
        return true;
      }
    }
  } catch(e){}
  return false;
}

function resetGame(){
  if(typeTimer) clearTimeout(typeTimer);
  state = {
    currentNode: storyData ? storyData.start_node : null,
    stats: { shuli: 10, xiayi: 10, xinzhi: 10 },
    fav: {},
    history: []
  };
  try { localStorage.removeItem(SAVE_KEY); } catch(e){}
  if(storyData) renderNode();
}

async function boot(){
  try {
    const resp = await fetch('/api/story/math_teen');
    const json = await resp.json();
    if(json.code !== 0) throw new Error(json.msg || '加载失败');
    storyData = json.data;
    if(!loadState()){
      state.currentNode = storyData.start_node;
    }
    renderNode();
  } catch(e){
    document.getElementById('sceneText').textContent = '剧本加载失败，请先启动 Flask 服务后访问本页。';
  }
}

boot();
</script>
</body>
</html>
