/* ============================================================
   晟算科技 · 太空场景引擎 v3 —— 电影级真实感
   真实月面(NASA/SSS) + 真实星野(银河) + ACES调色 + 泛光
   撕裂伤口 + 碎石量级谱 + 尘埃云
   模块加载(import map), 模式: journey / orbit
   ============================================================ */

import * as THREE from "three";
import { EffectComposer } from "three/addons/postprocessing/EffectComposer.js";
import { RenderPass } from "three/addons/postprocessing/RenderPass.js";
import { UnrealBloomPass } from "three/addons/postprocessing/UnrealBloomPass.js";

if (!window.SPACE_MODE) {
  window.SPACE_MODE = document.body.getAttribute("data-space") || "journey";
}

(function () {
  "use strict";

  var isMobile = window.innerWidth < 768 || "ontouchstart" in window;
  var reducedMotion = window.matchMedia("(prefers-reduced-motion: reduced)").matches;
  var bootEl = document.getElementById("spaceBoot");
  var canvasHost = document.getElementById("spaceCanvas");
  var isJourney = window.SPACE_MODE === "journey";

  function fallback() {
    if (bootEl) bootEl.remove();
    document.body.classList.add("space-fallback", "space-ready");
  }

  var testC = document.createElement("canvas");
  if (!(testC.getContext("webgl") || testC.getContext("experimental-webgl"))) { fallback(); return; }

  /* ============================================================
     工具
     ============================================================ */
  function glowTexture(inner, mid, size) {
    size = size || 256;
    var c = document.createElement("canvas");
    c.width = c.height = size;
    var x = c.getContext("2d");
    var g = x.createRadialGradient(size/2, size/2, 0, size/2, size/2, size/2);
    g.addColorStop(0, inner);
    g.addColorStop(0.25, inner.replace(/[\d.]+\)$/, "0.5)"));
    g.addColorStop(0.55, mid.replace(/[\d.]+\)$/, "0.18)"));
    g.addColorStop(0.8, mid.replace(/[\d.]+\)$/, "0.05)"));
    g.addColorStop(1, "rgba(0,0,0,0)");
    x.fillStyle = g;
    x.fillRect(0, 0, size, size);
    return new THREE.CanvasTexture(c);
  }

  // Three.js SphereGeometry 的标准 UV 映射
  function uvToSphere(u, v, r) {
    var phi = 2 * Math.PI * u;
    var theta = Math.PI * v;
    return new THREE.Vector3(
      -r * Math.cos(phi) * Math.sin(theta),
      r * Math.cos(theta),
      r * Math.sin(phi) * Math.sin(theta)
    );
  }

  // 冲击点: 朝向 +x+z (首页相机可见侧)
  var IMPACT = { u: 0.334, v: 0.47 };

  /* ============================================================
     场景与渲染器(ACES电影调色)
     ============================================================ */
  var scene = new THREE.Scene();
  var camera = new THREE.PerspectiveCamera(55, innerWidth / innerHeight, 0.1, 3000);
  var rig = new THREE.Group();
  rig.add(camera);
  scene.add(rig);

  var renderer;
  try {
    renderer = new THREE.WebGLRenderer({ antialias: true, alpha: true, powerPreference: "high-performance" });
  } catch (e) { fallback(); return; }
  renderer.setPixelRatio(Math.min(devicePixelRatio, isMobile ? 1.2 : 1.6));
  renderer.setSize(innerWidth, innerHeight);
  renderer.toneMapping = THREE.ACESFilmicToneMapping;   // 电影级色调映射
  renderer.toneMappingExposure = 1.15;
  renderer.outputColorSpace = THREE.SRGBColorSpace;
  canvasHost.appendChild(renderer.domElement);

  /* ---------- 泛光(只有高亮物体发光: 太阳/裂缝/代码粒子) ---------- */
  var composer = null, bloomPass = null;
  if (!isMobile) {
    composer = new EffectComposer(renderer);
    composer.addPass(new RenderPass(scene, camera));
    bloomPass = new UnrealBloomPass(new THREE.Vector2(innerWidth, innerHeight), 0.85, 0.55, 0.78);
    composer.addPass(bloomPass);
  }

  /* ============================================================
     资产加载(真实纹理)
     ============================================================ */
  var texLoader = new THREE.TextureLoader();
  var loadState = { moon: false, sky: false };

  /* ---------- 真实星野天球(银河横贯) ---------- */
  var skyMesh = null;
  texLoader.load("images/starfield.jpg", function (tex) {
    tex.colorSpace = THREE.SRGBColorSpace;
    tex.mapping = THREE.EquirectangularReflectionMapping;
    var mat = new THREE.MeshBasicMaterial({ map: tex, side: THREE.BackSide, depthWrite: false });
    mat.fog = false;
    skyMesh = new THREE.Mesh(new THREE.SphereGeometry(1200, 48, 32), mat);
    skyMesh.rotation.y = 1.35; // 让银河带横贯可见半球
    skyMesh.rotation.x = 0.3;
    scene.add(skyMesh);
    loadState.sky = true;
  }, undefined, function () { loadState.sky = true; });

  /* ---------- 近景尘埃(保留视差最快层) ---------- */
  function makeDustLayer(count, spread) {
    var geo = new THREE.BufferGeometry();
    var pos = new Float32Array(count * 3);
    var col = new Float32Array(count * 3);
    for (var i = 0; i < count; i++) {
      pos[i*3] = (Math.random()-0.5) * spread;
      pos[i*3+1] = (Math.random()-0.5) * spread;
      pos[i*3+2] = (Math.random()-0.5) * spread;
      var b = 0.5 + Math.random() * 0.5;
      col[i*3] = b * 0.85; col[i*3+1] = b * 0.95; col[i*3+2] = b;
    }
    geo.setAttribute("position", new THREE.BufferAttribute(pos, 3));
    geo.setAttribute("color", new THREE.BufferAttribute(col, 3));
    var mat = new THREE.PointsMaterial({
      size: 1.1, vertexColors: true, transparent: true, opacity: 0.55,
      sizeAttenuation: true, depthWrite: false
    });
    return new THREE.Points(geo, mat);
  }
  var nearDust = makeDustLayer(isMobile ? 100 : 220, 200);
  rig.add(nearDust);

  /* ---------- 太阳: 小而烈 + 泛光 + 横向光纹 ---------- */
  var sunGroup = new THREE.Group();
  // 亮度>1的核心,推过泛光阈值
  var sunCore = new THREE.Mesh(
    new THREE.SphereGeometry(5, 24, 24),
    new THREE.MeshBasicMaterial({ color: new THREE.Color(2.6, 2.3, 1.9) })
  );
  sunGroup.add(sunCore);
  // 柔和光晕(克制)
  var sunHalo = new THREE.Sprite(new THREE.SpriteMaterial({
    map: glowTexture("rgba(255,232,190,0.85)", "rgba(255,170,90,0.6)"),
    transparent: true, opacity: 0.4, blending: THREE.AdditiveBlending, depthWrite: false
  }));
  sunHalo.scale.set(46, 46, 1);
  sunGroup.add(sunHalo);
  // 变形宽银幕横向光纹
  var streakC = document.createElement("canvas");
  streakC.width = 256; streakC.height = 8;
  var sx = streakC.getContext("2d");
  var sg = sx.createLinearGradient(0, 0, 256, 0);
  sg.addColorStop(0, "rgba(255,215,160,0)");
  sg.addColorStop(0.5, "rgba(255,235,200,0.85)");
  sg.addColorStop(1, "rgba(255,215,160,0)");
  sx.fillStyle = sg; sx.fillRect(0, 0, 256, 8);
  var streakTex = new THREE.CanvasTexture(streakC);
  var sunStreak = new THREE.Sprite(new THREE.SpriteMaterial({
    map: streakTex, transparent: true, opacity: 0.55,
    blending: THREE.AdditiveBlending, depthWrite: false
  }));
  sunStreak.scale.set(90, 1.6, 1);
  sunGroup.add(sunStreak);
  sunGroup.position.set(-150, 70, -260);
  scene.add(sunGroup);

  /* ============================================================
     月亮: 真实纹理 + 撕裂伤口 + 发光裂缝
     ============================================================ */
  var moonGroup = new THREE.Group();
  var MOON_R = 10;
  var moonMat = new THREE.MeshStandardMaterial({
    color: 0xc9ccd2,                 // 冷调青灰(提亮度,纹理可见)
    roughness: 0.98, metalness: 0.0,
    bumpScale: 0.9                   // 更强环形山起伏
  });
  var moon = null;

  // 发光裂缝纹理(emissiveMap): 冲击点+放射裂纹+断面代码
  function makeCrackEmissive() {
    var W = 1024, H = 512;
    var c = document.createElement("canvas");
    c.width = W; c.height = H;
    var x = c.getContext("2d");
    x.fillStyle = "#000"; x.fillRect(0, 0, W, H);
    var ix = IMPACT.u * W, iy = IMPACT.v * H;

    // 断面发光代码结构
    x.save();
    x.beginPath(); x.arc(ix, iy, 58, 0, 7); x.clip();
    x.strokeStyle = "rgba(90,255,195,0.5)"; x.lineWidth = 1;
    for (var g = -58; g <= 58; g += 9) {
      x.beginPath(); x.moveTo(ix + g, iy - 58); x.lineTo(ix + g, iy + 58); x.stroke();
      x.beginPath(); x.moveTo(ix - 58, iy + g); x.lineTo(ix + 58, iy + g); x.stroke();
    }
    var chars = "01{}<>/=;#*";
    x.font = "9px monospace"; x.textBaseline = "top";
    for (var i = 0; i < 55; i++) {
      x.fillStyle = "rgba(160,255,215," + (0.4 + Math.random() * 0.6) + ")";
      x.fillText(chars[Math.floor(Math.random() * chars.length)], ix - 52 + Math.random() * 104, iy - 52 + Math.random() * 104);
    }
    x.restore();

    // 冲击核心辉光
    var core = x.createRadialGradient(ix, iy, 0, ix, iy, 80);
    core.addColorStop(0, "rgba(200,255,230,1)");
    core.addColorStop(0.3, "rgba(120,255,205,0.5)");
    core.addColorStop(1, "rgba(80,230,175,0)");
    x.fillStyle = core; x.beginPath(); x.arc(ix, iy, 80, 0, 7); x.fill();

    // 放射裂纹(宽淡→窄亮)
    x.lineCap = "round"; x.lineJoin = "round";
    for (var c2 = 0; c2 < 6; c2++) {
      var ang = (c2 / 6) * Math.PI * 2 + (Math.random() - 0.5) * 0.8;
      var px = ix, py = iy;
      var pts = [[px, py]];
      var len = 0.16 + Math.random() * 0.2;
      var steps = 6 + Math.floor(Math.random() * 4);
      for (var s = 0; s < steps; s++) {
        ang += (Math.random() - 0.5) * 0.8;
        px += Math.cos(ang) * (len * W / steps);
        py += Math.sin(ang) * (len * W / steps) * 0.55;
        pts.push([px, py]);
      }
      [[4, "rgba(70,220,170,0.06)"], [2, "rgba(100,240,190,0.12)"], [0.9, "rgba(200,255,235,0.75)"]].forEach(function (ps) {
        x.strokeStyle = ps[1]; x.lineWidth = ps[0];
        x.beginPath(); x.moveTo(pts[0][0], pts[0][1]);
        for (var k = 1; k < pts.length; k++) x.lineTo(pts[k][0], pts[k][1]);
        x.stroke();
      });
    }
    return new THREE.CanvasTexture(c);
  }

  // 顶点撕裂: 把伤口区域向内撕成参差轮廓
  function tearGeometry(geo, woundDir) {
    var pos = geo.attributes.position;
    var v = new THREE.Vector3();
    for (var i = 0; i < pos.count; i++) {
      v.fromBufferAttribute(pos, i).normalize();
      var ang = v.angleTo(woundDir);
      if (ang < 0.45) {
        var fall = 1 - ang / 0.45;
        // 高频噪声 → 锯齿而非圆坑
        var n = 0.5 + 0.5 * Math.sin(v.x * 13.1) * Math.sin(v.y * 9.7) * Math.sin(v.z * 11.3)
              + 0.25 * Math.sin(v.x * 31.7 + v.y * 23.9);
        n = Math.max(0, Math.min(1, n));
        var depth = fall * (0.5 + 3.4 * n * (0.35 + 0.65 * fall));
        if (ang > 0.28) depth += (Math.random() - 0.5) * 0.55; // 撕裂缘参差
        var newR = MOON_R - Math.max(0, depth);
        pos.setXYZ(i, v.x * newR, v.y * newR, v.z * newR);
      }
    }
    geo.computeVertexNormals();
  }

  var woundDirLocal = uvToSphere(IMPACT.u, IMPACT.v, 1).normalize();
  var woundPoint = uvToSphere(IMPACT.u, IMPACT.v, MOON_R * 0.98);

  texLoader.load("images/moon_color.jpg", function (colorTex) {
    colorTex.colorSpace = THREE.SRGBColorSpace;
    moonMat.map = colorTex;
    moonMat.emissiveMap = makeCrackEmissive();
    moonMat.emissive = new THREE.Color(0x9fffd8);
    moonMat.emissiveIntensity = 2.1;

    // 亮度转凹凸(NASA位移图下载失败时的运行时方案,效果扎实)
    var img = colorTex.image;
    var bc = document.createElement("canvas");
    bc.width = 1024; bc.height = 512;
    var bx = bc.getContext("2d");
    bx.drawImage(img, 0, 0, 1024, 512);
    var id = bx.getImageData(0, 0, 1024, 512);
    var d = id.data;
    for (var i = 0; i < d.length; i += 4) {
      var lum = d[i] * 0.299 + d[i+1] * 0.587 + d[i+2] * 0.114;
      lum = Math.max(0, Math.min(255, (lum - 110) * 1.7 + 100));
      d[i] = d[i+1] = d[i+2] = lum;
    }
    bx.putImageData(id, 0, 0);
    moonMat.bumpMap = new THREE.CanvasTexture(bc);

    var geo = new THREE.SphereGeometry(MOON_R, isMobile ? 64 : 96, isMobile ? 48 : 72);
    tearGeometry(geo, woundDirLocal);
    moon = new THREE.Mesh(geo, moonMat);
    moonGroup.add(moon);
    loadState.moon = true;
  }, undefined, function () { loadState.moon = true; });

  // 月缘柔光(青灰,克制——别洗白纹理)
  var moonHalo = new THREE.Sprite(new THREE.SpriteMaterial({
    map: glowTexture("rgba(175,200,195,0.5)", "rgba(160,190,185,0.4)"),
    transparent: true, opacity: 0.16, blending: THREE.AdditiveBlending, depthWrite: false
  }));
  moonHalo.scale.set(26, 26, 1);
  moonGroup.add(moonHalo);

  // 伤口溢出辉光(活着的事件)
  var woundGlow = new THREE.Sprite(new THREE.SpriteMaterial({
    map: glowTexture("rgba(160,255,220,0.9)", "rgba(90,255,195,0.55)"),
    transparent: true, opacity: 0.8, blending: THREE.AdditiveBlending, depthWrite: false
  }));
  woundGlow.scale.set(11, 11, 1);
  woundGlow.position.copy(woundPoint).multiplyScalar(1.04);
  moonGroup.add(woundGlow);

  /* ---------- 大碎片(2块,顶点扰动,真月面) ---------- */
  var fragments = [];
  var fragSpecs = [
    { size: 2.4, scatter: new THREE.Vector3(8.5, 4.5, 4),  home: new THREE.Vector3(6.8, 3.6, 2.8),  spin: 0.16, period: 14 },
    { size: 1.6, scatter: new THREE.Vector3(-6.5, -5, 5),  home: new THREE.Vector3(-5, -3.8, 3.8),  spin: -0.24, period: 9 }
  ];
  fragSpecs.forEach(function (spec) {
    var geo = new THREE.IcosahedronGeometry(spec.size, 2);
    var pa = geo.getAttribute("position");
    var vv = new THREE.Vector3();
    for (var i = 0; i < pa.count; i++) {
      vv.fromBufferAttribute(pa, i).normalize();
      var j = 0.7 + Math.random() * 0.55;
      pa.setXYZ(i, vv.x * spec.size * j, vv.y * spec.size * j, vv.z * spec.size * j);
    }
    geo.computeVertexNormals();
    var mesh = new THREE.Mesh(geo, moonMat);
    mesh.position.copy(spec.scatter);
    mesh.rotation.set(Math.random() * 3, Math.random() * 3, Math.random() * 3);
    moonGroup.add(mesh);
    fragments.push({ mesh: mesh, spec: spec, phase: Math.random() * Math.PI * 2 });
  });

  /* ---------- 碎石量级谱(InstancedMesh, 从伤口拖出) ---------- */
  var DEBRIS_N = isMobile ? 90 : 220;
  var debrisData = [];
  var debrisMesh = null;
  (function buildDebris() {
    var rockGeo = new THREE.IcosahedronGeometry(1, 0);
    var rp = rockGeo.getAttribute("position");
    for (var i = 0; i < rp.count; i++) {
      var j = 0.65 + Math.random() * 0.7;
      rp.setXYZ(i, rp.getX(i) * j, rp.getY(i) * j, rp.getZ(i) * j);
    }
    rockGeo.computeVertexNormals();
    debrisMesh = new THREE.InstancedMesh(rockGeo, moonMat, DEBRIS_N);
    var tangent = new THREE.Vector3().crossVectors(woundDirLocal, new THREE.Vector3(0, 1, 0)).normalize();
    var bitan = new THREE.Vector3().crossVectors(woundDirLocal, tangent).normalize();
    var dummy = new THREE.Object3D();
    for (var d = 0; d < DEBRIS_N; d++) {
      var along = (Math.random() - 0.3) * 48;              // 沿切线拖带(更宽)
      var out = 3 + Math.random() * 15;                    // 向外飞散距离(不贴月)
      var pos = woundPoint.clone()
        .add(tangent.clone().multiplyScalar(along))
        .add(woundDirLocal.clone().multiplyScalar(out))
        .add(bitan.clone().multiplyScalar((Math.random() - 0.5) * 5));
      var scale = 0.06 + Math.pow(Math.random(), 2.2) * 0.55; // 大量小颗,少数大颗
      var vel = woundDirLocal.clone().multiplyScalar(0.1 + Math.random() * 0.25)
        .add(tangent.clone().multiplyScalar((Math.random() - 0.5) * 0.1));
      var axis = new THREE.Vector3(Math.random()-0.5, Math.random()-0.5, Math.random()-0.5).normalize();
      debrisData.push({
        pos: pos, scale: scale, vel: vel, axis: axis,
        rotSpeed: (Math.random() - 0.5) * 0.6, phase: Math.random() * Math.PI * 2,
        wobble: 0.3 + Math.random() * 0.7, period: 8 + Math.random() * 12
      });
      dummy.position.copy(pos);
      dummy.scale.setScalar(scale);
      dummy.updateMatrix();
      debrisMesh.setMatrixAt(d, dummy.matrix);
    }
    debrisMesh.instanceMatrix.needsUpdate = true;
    moonGroup.add(debrisMesh);
  })();

  /* ---------- 尘埃云(烟尘,参考图灵魂) ---------- */
  var dustClouds = [];
  var tangent2 = new THREE.Vector3().crossVectors(woundDirLocal, new THREE.Vector3(0, 1, 0)).normalize();
  for (var dc = 0; dc < (isMobile ? 3 : 5); dc++) {
    var dustTex = glowTexture("rgba(150,175,165,0.55)", "rgba(140,170,160,0.35)", 256);
    var dust = new THREE.Sprite(new THREE.SpriteMaterial({
      map: dustTex, transparent: true, opacity: 0.05 + Math.random() * 0.06,
      blending: THREE.NormalBlending, depthWrite: false
    }));
    var sc2 = 22 + Math.random() * 26;
    dust.scale.set(sc2 * 1.6, sc2, 1);
    var along2 = 4 + dc * 7 + Math.random() * 4;
    dust.position.copy(woundPoint)
      .add(tangent2.clone().multiplyScalar(along2))
      .add(woundDirLocal.clone().multiplyScalar(2 + Math.random() * 4));
    moonGroup.add(dust);
    dustClouds.push({ sp: dust, base: dust.position.clone(), phase: Math.random() * Math.PI * 2, along: along2 });
  }

  // 修复光缝
  var seams = [];
  var seamTex = glowTexture("rgba(160,255,220,1)", "rgba(90,255,195,0.6)", 64);
  fragSpecs.forEach(function (spec) {
    var seam = new THREE.Sprite(new THREE.SpriteMaterial({
      map: seamTex, color: 0x9fffd8, transparent: true, opacity: 0,
      blending: THREE.AdditiveBlending, depthWrite: false
    }));
    seam.scale.set(spec.size * 3.2, spec.size * 1.2, 1);
    seam.position.copy(spec.home).multiplyScalar(0.94);
    moonGroup.add(seam);
    seams.push(seam);
  });

  moonGroup.position.set(6, 1, -26);
  scene.add(moonGroup);

  /* ---------- 代码泄漏粒子 ---------- */
  var codeChars = "01{}<>/=;#*$fx";
  var charCache = {};
  function charTex(ch) {
    if (!charCache[ch]) {
      var s = 48, c = document.createElement("canvas");
      c.width = c.height = s;
      var x = c.getContext("2d");
      x.font = "bold 30px monospace"; x.textAlign = "center"; x.textBaseline = "middle";
      x.shadowColor = "#9fffd8"; x.shadowBlur = 9; x.fillStyle = "#d6ffee";
      x.fillText(ch, s / 2, s / 2);
      charCache[ch] = new THREE.CanvasTexture(c);
    }
    return charCache[ch];
  }
  var LEAK_N = isMobile ? 20 : 40;
  var leaks = [];
  var leakBase = woundPoint.clone().add(moonGroup.position);
  for (var li = 0; li < LEAK_N; li++) {
    var sp = new THREE.Sprite(new THREE.SpriteMaterial({
      map: charTex(codeChars[Math.floor(Math.random() * codeChars.length)]),
      transparent: true, opacity: 0, depthWrite: false, blending: THREE.AdditiveBlending
    }));
    var sc3 = 0.4 + Math.random() * 0.55;
    sp.scale.set(sc3, sc3, 1);
    var emit = leakBase.clone().add(new THREE.Vector3((Math.random()-0.5)*3, (Math.random()-0.5)*3, (Math.random()-0.5)*3));
    sp.position.copy(emit);
    scene.add(sp);
    leaks.push({
      sp: sp, life: Math.random(), speed: 0.08 + Math.random() * 0.14,
      drift: woundDirLocal.clone().multiplyScalar(0.4 + Math.random() * 0.5)
        .add(new THREE.Vector3((Math.random()-0.5)*0.4, (Math.random()-0.5)*0.4, (Math.random()-0.5)*0.3)),
      origin: emit
    });
  }

  /* ---------- 光照(前左上方主光: 月面左亮右暗,环形山显形) ---------- */
  var sunLight = new THREE.DirectionalLight(0xffe3c0, 2.8);
  sunLight.position.set(-114, 71, 118);   // 前左上,照亮相机可见半球的左侧
  scene.add(sunLight);
  var fillCold = new THREE.DirectionalLight(0x5a6fc8, 0.55);  // 右前方冷补光,碎石不死黑
  fillCold.position.set(120, -30, 100);
  scene.add(fillCold);
  scene.add(new THREE.AmbientLight(0x2a3452, 0.6));
  var rimLight = new THREE.DirectionalLight(0x8899ff, 0.35);
  rimLight.position.set(80, -40, 60);
  scene.add(rimLight);

  /* ============================================================
     相机编排(V6行为: 呼吸漂移 + 鼠标惯性 + 滚动驱动)
     ============================================================ */
  var mouseX = 0, mouseY = 0;
  if (!isMobile) {
    document.addEventListener("mousemove", function (e) {
      mouseX = (e.clientX / innerWidth - 0.5) * 2;
      mouseY = (e.clientY / innerHeight - 0.5) * 2;
    });
  }
  function scrollProgress() {
    var h = document.documentElement.scrollHeight - innerHeight;
    return h > 0 ? Math.min(Math.max(scrollY / h, 0), 1) : 0;
  }
  function easeInOut(t) { return t < 0.5 ? 2 * t * t : 1 - Math.pow(-2 * t + 2, 2) / 2; }
  function lerp(a, b, t) { return a + (b - a) * t; }

  var warpT = reducedMotion ? 1 : 0;
  var clock = new THREE.Clock();
  var bootDone = false;

  /* ---------- BOOT ---------- */
  var bootLines = [
    { t: "> 接入深空观测网络 ………… ", ok: "[OK]" },
    { t: "> 望远镜阵列对焦 …………… ", ok: "[OK]" },
    { t: "> 扫描目标: MOON.SYS ……… ", ok: "[FATAL]" },
    { t: "> 检测到结构性碎裂 ………… ", ok: "[FRAGS]" },
    { t: "> 启动修复协议 ……………… ", ok: "[GO]" }
  ];
  var bootBody = document.getElementById("spaceBootBody");
  var bootBar = document.getElementById("spaceBootBar");
  function runBoot() {
    if (!bootEl || reducedMotion) { finishBoot(); return; }
    var i = 0;
    (function next() {
      if (i < bootLines.length) {
        var line = document.createElement("div");
        line.className = "terminal-line";
        var isFatal = bootLines[i].ok.indexOf("FATAL") >= 0 || bootLines[i].ok.indexOf("FRAG") >= 0;
        line.innerHTML = '<span class="terminal-prompt">' + bootLines[i].t + '</span><span class="terminal-text ' + (isFatal ? "log-error" : "ok") + '">' + bootLines[i].ok + "</span>";
        if (bootBody) bootBody.appendChild(line);
        if (bootBar) bootBar.style.width = ((i + 1) / bootLines.length * 100) + "%";
        i++;
        setTimeout(next, 340);
      } else setTimeout(finishBoot, 420);
    })();
  }
  function finishBoot() {
    if (bootEl) {
      bootEl.classList.add("hide");
      setTimeout(function () { if (bootEl.parentNode) bootEl.parentNode.removeChild(bootEl); }, 900);
    }
    bootDone = true;
    document.body.classList.add("space-ready");
  }
  runBoot();
  setTimeout(finishBoot, 4500);

  /* ============================================================
     主循环
     ============================================================ */
  var dummy = new THREE.Object3D();
  var tmpQ = new THREE.Quaternion();

  function animate() {
    requestAnimationFrame(animate);
    var t = clock.getElapsedTime();
    var p = isJourney ? scrollProgress() : 0;
    var ep = easeInOut(p);

    // warp入场
    if (warpT < 1) {
      warpT = Math.min(1, warpT + 0.008);
      var w = 1 - Math.pow(1 - warpT, 3);
      camera.position.z = lerp(220, isJourney ? 46 : 42, w);
      if (skyMesh) skyMesh.rotation.y += 0.004 * (1 - warpT);
    } else if (isJourney) {
      camera.position.z = lerp(46, 26.5, ep);
      camera.position.y = lerp(0, 3, ep);
    } else {
      camera.position.z = 42;
    }

    // 呼吸式失重漂移
    rig.position.x = Math.sin(t * 0.24) * 0.55;
    rig.position.y = Math.cos(t * 0.19) * 0.45;
    rig.rotation.z = Math.sin(t * 0.1) * 0.008;

    // 鼠标惯性跟随
    camera.rotation.y += ((-mouseX * 0.09) - camera.rotation.y) * 0.045;
    camera.rotation.x += ((-mouseY * 0.06) - camera.rotation.x) * 0.045;

    // 月亮缓慢自转 + 呼吸
    if (moon) moon.rotation.y += 0.0006;
    moonGroup.position.y = 1 + Math.sin(t * 0.33) * 0.3;
    moonGroup.rotation.z = Math.sin(t * 0.11) * 0.015;

    // 伤口辉光脉动
    woundGlow.material.opacity = 0.65 + Math.sin(t * 1.6) * 0.2;

    // 修复进度(V6时序)
    var repair = isJourney ? Math.min(Math.max((p - 0.42) / 0.46, 0), 1) * 0.85 : 0.3;
    var rt = repair / 0.85;

    // 大碎片: 无衰减漂移,各自周期相位
    fragments.forEach(function (f) {
      var target = f.spec.scatter.clone().lerp(f.spec.home, rt * rt);
      var amp = 0.5 * (1 - rt);
      var w1 = (t / f.spec.period) * Math.PI * 2 + f.phase;
      f.mesh.position.set(
        target.x + Math.sin(w1) * amp,
        target.y + Math.sin(w1 * 0.83 + 1.2) * amp * 0.8,
        target.z + Math.cos(w1 * 0.71) * amp * 0.7
      );
      f.mesh.rotation.x += 0.0016 * f.spec.spin * (1 - rt * 0.8);
      f.mesh.rotation.y += 0.0022 * f.spec.spin * (1 - rt * 0.8);
    });

    // 碎石场: 各自向外漂移+翻滚+不同步摆动(持续离散中)
    if (debrisMesh) {
      for (var d = 0; d < DEBRIS_N; d++) {
        var dd = debrisData[d];
        dd.pos.add(dd.vel.clone().multiplyScalar(0.016 * (1 - rt * 0.6)));
        var wob = (t / dd.period) * Math.PI * 2 + dd.phase;
        dummy.position.set(
          dd.pos.x + Math.sin(wob) * dd.wobble * 0.3,
          dd.pos.y + Math.sin(wob * 0.83) * dd.wobble * 0.25,
          dd.pos.z + Math.cos(wob * 0.71) * dd.wobble * 0.25
        );
        tmpQ.setFromAxisAngle(dd.axis, t * dd.rotSpeed + dd.phase);
        dummy.quaternion.copy(tmpQ);
        dummy.scale.setScalar(dd.scale);
        dummy.updateMatrix();
        debrisMesh.setMatrixAt(d, dummy.matrix);
      }
      debrisMesh.instanceMatrix.needsUpdate = true;
    }

    // 尘埃云缓慢流动
    dustClouds.forEach(function (dc2, i) {
      dc2.sp.position.x = dc2.base.x + Math.sin(t * 0.1 + dc2.phase) * 1.2 + t * 0.05 * (i % 2 ? 1 : -0.7);
      dc2.sp.position.y = dc2.base.y + Math.cos(t * 0.08 + dc2.phase) * 0.9;
      dc2.sp.material.opacity = 0.05 + 0.03 * Math.sin(t * 0.23 + dc2.phase) + 0.04;
    });

    // 光缝
    seams.forEach(function (s, i) {
      s.material.opacity = rt * 0.9 + Math.sin(t * 2.2 + i * 1.7) * 0.06 * rt;
    });

    // 代码泄漏
    var leakRate = 1 - rt * 0.55;
    leaks.forEach(function (L) {
      L.life += L.speed * 0.016;
      if (L.life > 1) {
        L.life = 0;
        L.sp.position.copy(L.origin);
        L.sp.material.map = charTex(codeChars[Math.floor(Math.random() * codeChars.length)]);
      }
      L.sp.position.add(L.drift.clone().multiplyScalar(0.016));
      L.sp.material.opacity = Math.sin(L.life * Math.PI) * 0.8 * leakRate;
    });

    // 太阳呼吸 + 光纹微闪
    sunHalo.material.opacity = 0.36 + Math.sin(t * 0.7) * 0.06;
    sunStreak.material.opacity = 0.45 + Math.sin(t * 1.1) * 0.12;

    // 星野极缓旋转 + 近尘埃视差
    if (skyMesh) skyMesh.rotation.y += 0.00008;
    nearDust.rotation.y += 0.0003;

    if (composer) composer.render();
    else renderer.render(scene, camera);
  }
  animate();

  addEventListener("resize", function () {
    camera.aspect = innerWidth / innerHeight;
    camera.updateProjectionMatrix();
    renderer.setSize(innerWidth, innerHeight);
    if (composer) composer.setSize(innerWidth, innerHeight);
  });
})();
