import React, { useEffect, useMemo, useRef, useState } from "https://esm.sh/react@18.3.1";
import { motion } from "https://esm.sh/framer-motion@11.18.2?external=react,react-dom";
import {
  BookOpen,
  Brain,
  CalendarDays,
  CheckCircle2,
  ChevronRight,
  Flame,
  Headphones,
  MessageCircleHeart,
  Mic,
  PenLine,
  Play,
  RotateCcw,
  Sparkles,
  Square,
  Timer,
  Volume2,
  Wand2,
} from "https://esm.sh/lucide-react@0.468.0?external=react";

const trainingModules = [
  {
    id: "reading",
    index: "01",
    title: "美文朗读",
    subtitle: "练语感、气息、节奏和声音感染力",
    icon: Volume2,
    contentTitle: "朗读内容",
    body: [
      "有些改变，不是从某个巨大的决定开始的，而是从一个很小的行动开始。",
      "比如，今天你愿意开口读一段话，愿意认真说出一句完整的表达，愿意听见自己的声音。",
      "表达能力的提升，并不是一夜之间发生的。它藏在每一次朗读里，藏在每一次停顿里，也藏在每一次你没有逃避表达的瞬间。",
      "从今天开始，不必害怕说得不够好。真正重要的，是你已经开始练习。",
    ],
    tips: [
      "第一遍：慢慢读，读清楚每个字。",
      "第二遍：注意停顿，让声音更自然。",
      "第三遍：带着鼓励自己的语气朗读。",
    ],
  },
  {
    id: "classic",
    index: "02",
    title: "国学经典",
    subtitle: "练沉淀、思考深度和表达底蕴",
    icon: BookOpen,
    contentTitle: "君子欲讷于言而敏于行",
    body: [
      "白话：成熟的人不急着说很多话，而是更重视行动和结果。",
      "表达应用：会表达，不等于话多。真正有力量的表达，是说得清楚、准确、有分量。",
      "今日思考：请用自己的话解释，为什么表达能力不等于“会说很多话”？",
    ],
    tips: ["先读原句，再读白话。", "最后用自己的语言复述一次。"],
  },
  {
    id: "nvc",
    index: "03",
    title: "非暴力沟通",
    subtitle: "练情商表达、换位思考和关系沟通",
    icon: MessageCircleHeart,
    contentTitle: "把指责换成清晰表达",
    body: [
      "场景：别人没有及时回复你的消息，你很不舒服。",
      "常见表达：你怎么总是不回消息？你是不是根本不在乎我？",
      "优化表达：我刚才一直没有收到回复，会有一点失落，因为我很在意这件事。你方便的时候，可以告诉我大概什么时候能回复吗？",
      "今日练习：请把“你从来都不听我说话”改成更温和的表达。",
    ],
    tips: ["表达公式：事实 + 感受 + 需要 + 请求。", "不要压抑，也不要攻击。"],
  },
  {
    id: "story",
    index: "04",
    title: "哲理小故事",
    subtitle: "练逻辑复述、故事表达和观点提炼",
    icon: Brain,
    contentTitle: "不下水的人，永远学不会游泳",
    body: [
      "有一个人想学游泳。他买了很多游泳书，看了很多教学视频，也认真记下了各种动作要领。可是很长时间过去了，他仍然不会游泳。",
      "朋友问他：“你为什么不下水试试？”他说：“我还没准备好，我怕动作不标准。”朋友说：“你不下水，永远不会知道自己哪里需要改。”",
      "表达也是一样。只看方法，不开口练习，很难真正进步。",
      "复述任务：30 秒讲清故事；1 分钟讲出启发；3 分钟结合自己的经历展开。",
    ],
    tips: ["复述时按：人物、事件、转折、启发。", "不要背原文，用自己的话讲。"],
  },
  {
    id: "tongue",
    index: "05",
    title: "标准绕口令",
    subtitle: "练吐字清晰、口腔灵活度和发音准确度",
    icon: Mic,
    contentTitle: "四与十专项练习",
    body: [
      "基础版：四是四，十是十，十四是十四，四十是四十。",
      "进阶版：要想说对四，舌头碰牙齿；要想说对十，舌头别伸直。",
      "挑战版：四十四个石狮子，蹲在四十四棵柿子树下。",
    ],
    tips: ["先慢后快，清晰比速度重要。", "每一遍都要让字头、字腹、字尾完整。"],
  },
  {
    id: "debate",
    index: "06",
    title: "观点思辨",
    subtitle: "练即兴表达、观点组织和临场发言",
    icon: Sparkles,
    contentTitle: "表达能力是天赋更重要，还是训练更重要？",
    body: [
      "支持天赋：有些人天生更外向，更敢说；有些人语言组织能力本来就强。",
      "支持训练：表达有方法，可以通过朗读、复述、即兴表达形成肌肉记忆。",
      "表达框架：我的观点是……原因有三点……举个例子……所以我认为……",
      "今日任务：围绕这个话题，完成 1 分钟即兴表达。",
    ],
    tips: ["先说观点，再说理由。", "不要追求完美，先保持完整。"],
  },
];

const days = [
  "开口与节奏",
  "停顿与重音",
  "吐字清晰",
  "语气自然",
  "复述入门",
  "一分钟表达",
  "第一周复盘",
  "表达顺序",
  "三点表达法",
  "故事结构",
  "观点提炼",
  "信息压缩",
  "清晰回答",
  "第二周复盘",
  "表达感受",
  "提出请求",
  "回应不同意见",
  "冲突表达",
  "拒绝表达",
  "赞美表达",
  "第三周复盘",
  "即兴观点",
  "举例能力",
  "反方思考",
  "说服表达",
  "故事化表达",
  "表达感染力",
  "三分钟表达",
  "模拟演讲",
  "最终展示",
];

function classNames(...classes) {
  return classes.filter(Boolean).join(" ");
}

function ProgressRing({ value }) {
  const radius = 38;
  const circumference = 2 * Math.PI * radius;
  const offset = circumference - (value / 100) * circumference;

  return (
    <div className="relative h-24 w-24">
      <svg className="h-24 w-24 -rotate-90" viewBox="0 0 100 100">
        <circle
          cx="50"
          cy="50"
          r={radius}
          stroke="rgba(255,255,255,0.12)"
          strokeWidth="8"
          fill="none"
        />
        <circle
          cx="50"
          cy="50"
          r={radius}
          stroke="currentColor"
          strokeWidth="8"
          fill="none"
          strokeLinecap="round"
          strokeDasharray={circumference}
          strokeDashoffset={offset}
          className="text-[#D6B56D] transition-all duration-700"
        />
      </svg>
      <div className="absolute inset-0 flex items-center justify-center text-lg font-semibold text-[#F3EFE4]">
        {value}%
      </div>
    </div>
  );
}

function TimerBox() {
  const [seconds, setSeconds] = useState(60);
  const [running, setRunning] = useState(false);

  useEffect(() => {
    if (!running || seconds <= 0) return undefined;

    const timer = setTimeout(() => {
      setSeconds((current) => current - 1);
    }, 1000);

    return () => clearTimeout(timer);
  }, [running, seconds]);

  const mm = String(Math.floor(seconds / 60)).padStart(2, "0");
  const ss = String(seconds % 60).padStart(2, "0");

  return (
    <div className="rounded-3xl border border-white/10 bg-white/[0.04] p-5 shadow-2xl shadow-black/20">
      <div className="mb-4 flex items-center gap-2 text-sm text-[#B7B0A2]">
        <Timer className="h-4 w-4" />
        朗读 / 即兴表达计时器
      </div>

      <div className="mb-5 text-5xl font-semibold tracking-tight text-[#F3EFE4]">
        {mm}:{ss}
      </div>

      <div className="flex flex-wrap gap-3">
        <button
          type="button"
          onClick={() => setRunning((current) => !current)}
          className="inline-flex items-center gap-2 rounded-full bg-[#D6B56D] px-5 py-2.5 text-sm font-semibold text-[#10231E] transition hover:scale-[1.02]"
        >
          <Play className="h-4 w-4" />
          {running ? "暂停" : "开始"}
        </button>

        <button
          type="button"
          onClick={() => {
            setRunning(false);
            setSeconds(60);
          }}
          className="inline-flex items-center gap-2 rounded-full border border-white/10 px-5 py-2.5 text-sm font-medium text-[#F3EFE4] transition hover:bg-white/10"
        >
          <RotateCcw className="h-4 w-4" />
          重置
        </button>

        {[30, 60, 180].map((item) => (
          <button
            type="button"
            key={item}
            onClick={() => {
              setRunning(false);
              setSeconds(item);
            }}
            className="rounded-full bg-white/[0.06] px-4 py-2 text-sm text-[#B7B0A2] transition hover:bg-white/10 hover:text-[#F3EFE4]"
          >
            {item < 60 ? `${item}秒` : `${item / 60}分钟`}
          </button>
        ))}
      </div>
    </div>
  );
}

const selfReviewItems = [
  {
    id: "speed",
    label: "语速偏快",
    advice: "下一次先把语速降下来，每句话之间留半秒停顿。清楚比快更重要。",
  },
  {
    id: "pause",
    label: "停顿太少",
    advice: "把内容按意思分成几段，在关键词前后主动停顿，让听的人更容易跟上。",
  },
  {
    id: "logic",
    label: "逻辑不够清楚",
    advice: "开头先说一句核心观点，再用“第一、第二、第三”展开，最后用一句话收尾。",
  },
  {
    id: "detail",
    label: "例子不够具体",
    advice: "每次表达至少加一个具体场景，例如“今天朗读时，我在第二段开始卡住”。",
  },
  {
    id: "tone",
    label: "语气不够自然",
    advice: "不要像背稿，试着用跟朋友解释的语气说出来，让表达更松弛。",
  },
  {
    id: "confidence",
    label: "声音不够稳定",
    advice: "录音前先深呼吸一次，第一句话慢一点、稳一点，帮自己进入状态。",
  },
  {
    id: "ending",
    label: "结尾没有总结",
    advice: "结尾加一句“所以我今天最大的收获是……”，让表达有完成感。",
  },
  {
    id: "filler",
    label: "口头禅较多",
    advice: "听回放时数一数“嗯、然后、就是”，下一次把口头禅换成短暂停顿。",
  },
];

function RecordingReviewBox({ currentModule, note }) {
  const recorderRef = useRef(null);
  const chunksRef = useRef([]);
  const [isRecording, setIsRecording] = useState(false);
  const [audioBlob, setAudioBlob] = useState(null);
  const [audioUrl, setAudioUrl] = useState("");
  const [selectedIssues, setSelectedIssues] = useState([]);
  const [score, setScore] = useState(75);
  const [status, setStatus] = useState("");
  const [error, setError] = useState("");
  const [review, setReview] = useState("");

  useEffect(() => {
    if (!audioBlob) {
      setAudioUrl("");
      return undefined;
    }

    const url = URL.createObjectURL(audioBlob);
    setAudioUrl(url);
    return () => URL.revokeObjectURL(url);
  }, [audioBlob]);

  const toggleIssue = (id) => {
    setSelectedIssues((current) =>
      current.includes(id) ? current.filter((item) => item !== id) : [...current, id]
    );
  };

  const startRecording = async () => {
    setError("");
    setStatus("");
    setReview("");

    if (!navigator.mediaDevices?.getUserMedia) {
      setError("当前浏览器不支持录音，请换用新版 Chrome、Edge 或 Safari。");
      return;
    }

    try {
      const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
      const recorder = new MediaRecorder(stream);
      chunksRef.current = [];

      recorder.ondataavailable = (event) => {
        if (event.data.size > 0) chunksRef.current.push(event.data);
      };

      recorder.onstop = () => {
        const blob = new Blob(chunksRef.current, { type: recorder.mimeType || "audio/webm" });
        setAudioBlob(blob);
        stream.getTracks().forEach((track) => track.stop());
      };

      recorderRef.current = recorder;
      recorder.start();
      setIsRecording(true);
      setStatus("正在录音。说完后点击停止，再回放并勾选这次发现的问题。");
    } catch (recordingError) {
      setError("无法开启麦克风，请确认浏览器已经允许录音权限。");
    }
  };

  const stopRecording = () => {
    recorderRef.current?.stop();
    recorderRef.current = null;
    setIsRecording(false);
    setStatus("录音已保存。建议先回放一遍，再完成自评。");
  };

  const createSelfReview = () => {
    setError("");

    if (!audioBlob) {
      setError("请先完成一段录音。");
      return;
    }

    if (selectedIssues.length === 0) {
      setError("请至少勾选一个本次想改进的问题。");
      return;
    }

    const picked = selfReviewItems.filter((item) => selectedIssues.includes(item.id));
    const nextReview = [
      `本次自评分：${score}/100`,
      "",
      `训练板块：${currentModule.title}`,
      "",
      "本次发现的问题：",
      ...picked.map((item) => `- ${item.label}`),
      "",
      "下一次改进建议：",
      ...picked.map((item, index) => `${index + 1}. ${item.advice}`),
      "",
      "复练任务：",
      `请围绕“${currentModule.contentTitle}”重新录一遍，这次重点只改 1-2 个问题，不追求一次全改完。`,
      note ? `\n你的今日笔记：${note}` : "",
    ].join("\n");

    setReview(nextReview);
    setStatus("自评已生成。建议照着建议重录一次，再比较两段录音。");

    const history = JSON.parse(localStorage.getItem("speech-self-review-history") || "[]");
    localStorage.setItem(
      "speech-self-review-history",
      JSON.stringify([
        {
          id: Date.now(),
          moduleTitle: currentModule.title,
          score,
          issues: picked.map((item) => item.label),
          review: nextReview,
          createdAt: new Date().toISOString(),
        },
        ...history,
      ].slice(0, 20))
    );
  };

  return (
    <div className="rounded-3xl border border-white/10 bg-white/[0.05] p-5 shadow-2xl shadow-black/20">
      <div className="mb-4 flex items-center gap-2 text-sm text-[#D6B56D]">
        <Wand2 className="h-4 w-4" />
        录音与本地自评
      </div>

      <div className="mb-4 flex flex-wrap gap-3">
        <span className="inline-flex items-center gap-2 rounded-2xl border border-white/10 px-4 py-3 text-sm text-[#B7B0A2]">
          <Headphones className="h-4 w-4" />
          不消耗 API
        </span>
        <span className="inline-flex items-center rounded-2xl border border-white/10 px-4 py-3 text-sm text-[#B7B0A2]">
          录音只保存在当前浏览器
        </span>
      </div>

      <div className="flex flex-wrap gap-3">
        <button
          type="button"
          onClick={isRecording ? stopRecording : startRecording}
          className="inline-flex items-center gap-2 rounded-full bg-[#D6B56D] px-5 py-2.5 text-sm font-semibold text-[#10231E] transition hover:scale-[1.02]"
        >
          {isRecording ? <Square className="h-4 w-4" /> : <Mic className="h-4 w-4" />}
          {isRecording ? "停止录音" : "开始录音"}
        </button>

        <button
          type="button"
          onClick={createSelfReview}
          disabled={isRecording || !audioBlob}
          className="inline-flex items-center gap-2 rounded-full border border-white/10 px-5 py-2.5 text-sm font-medium text-[#F3EFE4] transition hover:bg-white/10 disabled:cursor-not-allowed disabled:opacity-45"
        >
          <CheckCircle2 className="h-4 w-4" />
          生成自评建议
        </button>
      </div>

      {audioUrl && (
        <audio controls src={audioUrl} className="mt-4 w-full" />
      )}

      <div className="mt-4 rounded-2xl bg-black/15 p-4">
        <div className="mb-3 flex flex-wrap items-center justify-between gap-3">
          <span className="text-sm font-semibold text-[#F3EFE4]">本次自评分：{score}</span>
          <input
            type="range"
            min="40"
            max="100"
            value={score}
            onChange={(event) => setScore(Number(event.target.value))}
            className="w-40 accent-[#D6B56D]"
          />
        </div>

        <div className="grid gap-2 sm:grid-cols-2">
          {selfReviewItems.map((item) => {
            const checked = selectedIssues.includes(item.id);
            return (
              <button
                type="button"
                key={item.id}
                onClick={() => toggleIssue(item.id)}
                className={`rounded-2xl border px-3 py-2 text-left text-sm leading-6 transition ${
                  checked
                    ? "border-[#D6B56D]/70 bg-[#D6B56D]/15 text-[#F3EFE4]"
                    : "border-white/10 bg-white/[0.04] text-[#B7B0A2] hover:bg-white/10"
                }`}
              >
                {item.label}
              </button>
            );
          })}
        </div>
      </div>

      {status && <p className="mt-4 rounded-2xl bg-white/[0.06] p-4 text-sm leading-7 text-[#B7B0A2]">{status}</p>}
      {error && <p className="mt-4 rounded-2xl border border-red-400/20 bg-red-400/10 p-4 text-sm leading-7 text-red-100">{error}</p>}

      {review && (
        <div className="mt-4 grid gap-4">
          <div className="rounded-2xl bg-[#D6B56D]/10 p-4">
            <div className="mb-2 text-sm font-semibold text-[#F3EFE4]">本地自评建议</div>
            <p className="whitespace-pre-wrap text-sm leading-7 text-[#F3EFE4]">{review}</p>
          </div>
        </div>
      )}
    </div>
  );
}

function App() {
  const [completedModules, setCompletedModules] = useState(["reading", "classic"]);
  const [activeModule, setActiveModule] = useState(trainingModules[0].id);
  const [note, setNote] = useState("");
  const [dayCompleted, setDayCompleted] = useState(false);

  const progress = useMemo(
    () => Math.round((completedModules.length / trainingModules.length) * 100),
    [completedModules]
  );

  const currentModule =
    trainingModules.find((item) => item.id === activeModule) || trainingModules[0];

  const toggleModule = (id) => {
    setCompletedModules((prev) =>
      prev.includes(id) ? prev.filter((item) => item !== id) : [...prev, id]
    );
  };

  return (
    <div className="min-h-screen bg-[#10231E] text-[#F3EFE4]">
      <div className="pointer-events-none fixed inset-0 overflow-hidden">
        <div className="absolute -left-28 top-0 h-96 w-96 rounded-full bg-[#D6B56D]/20 blur-3xl" />
        <div className="absolute right-0 top-40 h-[520px] w-[520px] rounded-full bg-emerald-400/10 blur-3xl" />
        <div className="absolute bottom-0 left-1/3 h-80 w-80 rounded-full bg-white/5 blur-3xl" />
      </div>

      <header className="relative mx-auto flex max-w-7xl items-center justify-between px-5 py-6 md:px-8">
        <div className="flex items-center gap-3">
          <div className="flex h-10 w-10 items-center justify-center rounded-2xl bg-[#D6B56D] text-[#10231E] shadow-lg shadow-black/20">
            <Mic className="h-5 w-5" />
          </div>
          <div>
            <div className="text-base font-semibold tracking-wide">表达力训练营</div>
            <div className="text-xs text-[#B7B0A2]">30 Days Speaking Practice</div>
          </div>
        </div>

        <nav className="hidden items-center gap-7 text-sm text-[#B7B0A2] md:flex">
          <a href="#plan" className="hover:text-[#F3EFE4]">
            训练计划
          </a>
          <a href="#today" className="hover:text-[#F3EFE4]">
            今日训练
          </a>
          <a href="#checkin" className="hover:text-[#F3EFE4]">
            打卡复盘
          </a>
        </nav>
      </header>

      <main className="relative">
        <section className="mx-auto grid max-w-7xl items-center gap-10 px-5 pb-16 pt-10 md:grid-cols-[1.05fr_0.95fr] md:px-8 md:pb-24 md:pt-20">
          <motion.div
            initial={{ opacity: 0, y: 18 }}
            animate={{ opacity: 1, y: 0 }}
            transition={{ duration: 0.7 }}
          >
            <div className="mb-6 inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.05] px-4 py-2 text-sm text-[#D6B56D]">
              <Flame className="h-4 w-4" />
              每天 20 分钟 · 固定 6 大训练板块
            </div>

            <h1 className="max-w-3xl text-5xl font-semibold leading-tight tracking-tight md:text-7xl">
              30 天表达能力
              <span className="block text-[#D6B56D]">提升计划</span>
            </h1>

            <p className="mt-7 max-w-2xl text-lg leading-9 text-[#D8D2C6] md:text-xl">
              打开网页，跟着朗读、经典、沟通、复述、绕口令和思辨一步步开口练。
              不是看知识，而是真正训练表达肌肉。
            </p>

            <div className="mt-9 flex flex-wrap gap-4">
              <a
                href="#today"
                className="inline-flex items-center gap-2 rounded-full bg-[#D6B56D] px-7 py-4 text-base font-semibold text-[#10231E] shadow-2xl shadow-black/20 transition hover:scale-[1.02]"
              >
                开始 Day 01
                <ChevronRight className="h-5 w-5" />
              </a>
              <a
                href="#plan"
                className="inline-flex items-center gap-2 rounded-full border border-white/10 bg-white/[0.04] px-7 py-4 text-base font-semibold text-[#F3EFE4] transition hover:bg-white/10"
              >
                查看 30 天计划
              </a>
            </div>
          </motion.div>

          <motion.div
            initial={{ opacity: 0, scale: 0.96 }}
            animate={{ opacity: 1, scale: 1 }}
            transition={{ duration: 0.7, delay: 0.1 }}
            className="rounded-[2rem] border border-white/10 bg-white/[0.06] p-5 shadow-2xl shadow-black/30 backdrop-blur"
          >
            <div className="rounded-[1.5rem] bg-[#F7F2E8] p-6 text-[#10231E]">
              <div className="mb-6 flex items-center justify-between">
                <div>
                  <div className="text-sm font-medium text-[#8B5E3C]">今日训练</div>
                  <div className="mt-1 text-2xl font-semibold">Day 01｜开口与节奏</div>
                </div>
                <ProgressRing value={progress} />
              </div>

              <div className="space-y-3">
                {trainingModules.map((item) => {
                  const Icon = item.icon;
                  const done = completedModules.includes(item.id);

                  return (
                    <div key={item.id} className="flex items-center gap-3 rounded-2xl bg-white p-4 shadow-sm">
                      <div className="flex h-11 w-11 shrink-0 items-center justify-center rounded-xl bg-[#10231E]/90 text-[#D6B56D]">
                        <Icon className="h-5 w-5" />
                      </div>

                      <div className="min-w-0 flex-1">
                        <div className="font-semibold">{item.title}</div>
                        <div className="truncate text-sm text-[#7A7168]">{item.subtitle}</div>
                      </div>

                      <CheckCircle2
                        className={classNames("h-5 w-5", done ? "text-emerald-600" : "text-stone-300")}
                      />
                    </div>
                  );
                })}
              </div>
            </div>
          </motion.div>
        </section>

        <section className="mx-auto max-w-7xl px-5 py-10 md:px-8" id="plan">
          <div className="mb-7 flex flex-col justify-between gap-4 md:flex-row md:items-end">
            <div>
              <div className="mb-3 inline-flex items-center gap-2 rounded-full bg-white/[0.05] px-4 py-2 text-sm text-[#D6B56D]">
                <CalendarDays className="h-4 w-4" />
                训练路径
              </div>
              <h2 className="text-3xl font-semibold md:text-4xl">30 天进度总览</h2>
            </div>
            <p className="max-w-xl text-sm leading-7 text-[#B7B0A2]">
              每 7 天一次阶段复盘，从“敢开口”逐步过渡到“会表达、有逻辑、有气场”。
            </p>
          </div>

          <div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-5">
            {days.map((title, index) => {
              const finished = index < 3;
              const current = index === 0;

              return (
                <button
                  type="button"
                  key={title}
                  className={classNames(
                    "rounded-2xl border p-4 text-left transition hover:-translate-y-0.5 hover:bg-white/[0.08]",
                    current ? "border-[#D6B56D]/60 bg-[#D6B56D]/10" : "border-white/10 bg-white/[0.04]"
                  )}
                >
                  <div className="mb-3 flex items-center justify-between">
                    <span className="text-sm text-[#D6B56D]">Day {String(index + 1).padStart(2, "0")}</span>
                    {finished && <CheckCircle2 className="h-4 w-4 text-emerald-400" />}
                  </div>
                  <div className="font-medium text-[#F3EFE4]">{title}</div>
                </button>
              );
            })}
          </div>
        </section>

        <section className="mx-auto grid max-w-7xl gap-6 px-5 py-14 md:grid-cols-[340px_1fr] md:px-8" id="today">
          <aside className="h-fit rounded-[2rem] border border-white/10 bg-white/[0.05] p-5 backdrop-blur md:sticky md:top-6">
            <div className="mb-5">
              <div className="text-sm text-[#D6B56D]">Day 01</div>
              <h2 className="mt-1 text-2xl font-semibold">让表达从敢开口开始</h2>
              <p className="mt-3 text-sm leading-7 text-[#B7B0A2]">
                今日重点不是说得完美，而是稳定、清晰、完整地开口。
              </p>
            </div>

            <div className="mb-5 flex items-center gap-5 rounded-3xl bg-black/15 p-4">
              <ProgressRing value={progress} />
              <div>
                <div className="text-2xl font-semibold">{completedModules.length}/6</div>
                <div className="mt-1 text-sm text-[#B7B0A2]">已完成板块</div>
              </div>
            </div>

            <div className="space-y-2">
              {trainingModules.map((item) => {
                const Icon = item.icon;
                const done = completedModules.includes(item.id);
                const active = activeModule === item.id;

                return (
                  <button
                    type="button"
                    key={item.id}
                    onClick={() => setActiveModule(item.id)}
                    className={classNames(
                      "flex w-full items-center gap-3 rounded-2xl p-3 text-left transition",
                      active ? "bg-[#D6B56D] text-[#10231E]" : "bg-white/[0.04] text-[#F3EFE4] hover:bg-white/[0.08]"
                    )}
                  >
                    <Icon className="h-5 w-5 shrink-0" />
                    <div className="min-w-0 flex-1">
                      <div className="font-medium">
                        {item.index}. {item.title}
                      </div>
                      <div className={classNames("truncate text-xs", active ? "text-[#10231E]/70" : "text-[#B7B0A2]")}>
                        {item.subtitle}
                      </div>
                    </div>
                    <CheckCircle2 className={classNames("h-5 w-5 shrink-0", done ? "opacity-100" : "opacity-25")} />
                  </button>
                );
              })}
            </div>
          </aside>

          <div className="space-y-6">
            <motion.article
              key={currentModule.id}
              initial={{ opacity: 0, y: 14 }}
              animate={{ opacity: 1, y: 0 }}
              transition={{ duration: 0.35 }}
              className="rounded-[2rem] border border-white/10 bg-[#F7F2E8] p-6 text-[#10231E] shadow-2xl shadow-black/20 md:p-9"
            >
              <div className="mb-8 flex flex-col justify-between gap-5 md:flex-row md:items-start">
                <div>
                  <div className="mb-3 inline-flex rounded-full bg-[#10231E] px-4 py-2 text-sm font-medium text-[#D6B56D]">
                    板块 {currentModule.index}
                  </div>
                  <h3 className="text-3xl font-semibold tracking-tight md:text-4xl">{currentModule.title}</h3>
                  <p className="mt-3 text-[#7A7168]">{currentModule.subtitle}</p>
                </div>

                <button
                  type="button"
                  onClick={() => toggleModule(currentModule.id)}
                  className={classNames(
                    "inline-flex items-center justify-center gap-2 rounded-full px-6 py-3 text-sm font-semibold transition",
                    completedModules.includes(currentModule.id)
                      ? "bg-emerald-700 text-white"
                      : "bg-[#10231E] text-[#F3EFE4] hover:scale-[1.02]"
                  )}
                >
                  <CheckCircle2 className="h-5 w-5" />
                  {completedModules.includes(currentModule.id) ? "已完成" : "完成本模块"}
                </button>
              </div>

              <div className="rounded-[1.5rem] bg-white p-6 shadow-sm md:p-8">
                <div className="mb-5 text-sm font-semibold tracking-widest text-[#8B5E3C]">
                  {currentModule.contentTitle}
                </div>
                <div className="space-y-6 text-xl leading-[2.05] tracking-wide text-[#2B2B2B] md:text-2xl">
                  {currentModule.body.map((paragraph) => (
                    <p key={paragraph}>{paragraph}</p>
                  ))}
                </div>
              </div>

              <div className="mt-6 grid gap-4 md:grid-cols-2">
                {currentModule.tips.map((tip) => (
                  <div key={tip} className="rounded-2xl bg-[#10231E]/5 p-4 text-sm leading-7 text-[#5E554D]">
                    {tip}
                  </div>
                ))}
              </div>
            </motion.article>

            <div className="grid gap-6 lg:grid-cols-[0.9fr_1.1fr]" id="checkin">
              <div className="grid gap-6">
                <TimerBox />
                <RecordingReviewBox currentModule={currentModule} note={note} />
              </div>

              <div className="rounded-3xl border border-white/10 bg-white/[0.05] p-5 shadow-2xl shadow-black/20">
                <div className="mb-4 flex items-center gap-2 text-sm text-[#D6B56D]">
                  <PenLine className="h-4 w-4" />
                  今日表达笔记
                </div>

                <textarea
                  value={note}
                  onChange={(event) => setNote(event.target.value)}
                  placeholder="写下今天最打动你的一句话，或者记录你练习时遇到的卡点……"
                  className="min-h-40 w-full resize-none rounded-2xl border border-white/10 bg-black/15 p-4 text-base leading-7 text-[#F3EFE4] outline-none placeholder:text-[#B7B0A2]/60 focus:border-[#D6B56D]/50"
                />

                <div className="mt-4 flex flex-wrap items-center justify-between gap-3">
                  <span className="text-sm text-[#B7B0A2]">建议每天写 1 句话，形成表达复盘记录。</span>
                  <button
                    type="button"
                    onClick={() => setDayCompleted(true)}
                    className="rounded-full bg-[#D6B56D] px-6 py-3 text-sm font-semibold text-[#10231E] transition hover:scale-[1.02]"
                  >
                    完成今日打卡
                  </button>
                </div>

                {dayCompleted && (
                  <motion.div
                    initial={{ opacity: 0, y: 10 }}
                    animate={{ opacity: 1, y: 0 }}
                    className="mt-4 rounded-2xl border border-emerald-400/20 bg-emerald-400/10 p-4 text-sm leading-7 text-emerald-100"
                  >
                    你已完成 Day 01。表达力的提升，从每一次开口开始。
                  </motion.div>
                )}
              </div>
            </div>
          </div>
        </section>
      </main>
    </div>
  );
}


import ReactDOM from "https://esm.sh/react-dom@18.3.1/client";

ReactDOM.createRoot(document.getElementById("root")).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
