
(() => {
  const $ = (sel) => document.querySelector(sel);

  const screens = {
    start: $("#screen-start"),
    option: $("#screen-option"),
    quiz: $("#screen-quiz"),
    result: $("#screen-result"),
    gallery: $("#screen-gallery"),
    confession: $("#screen-confession"),
    review: $("#screen-review"),
  };

  const btnStart = $("#btnStart");
  const btnOption = $("#btnOption");
  const btnExit = $("#btnExit");
  const bgmEl = document.getElementById("bgm");

  const btnGallery = $("#btnGallery");
  const btnResetProgress = $("#btnResetProgress");
  const btnOptBack = $("#btnOptBack");
  const galleryList = $("#galleryList");
  const btnGalleryBack = $("#btnGalleryBack");

  const progress = $("#progress");
  const scoreEl = $("#score");
  const gaugeText = $("#gaugeText");
  const gaugeFill = $("#gaugeFill");
  const questionEl = $("#question");
  const questionModeLabel = $("#questionModeLabel");
  const choicesEl = $("#choices");
  const btnQuit = $("#btnQuit");

  const resultタイトルへ = $("#resultタイトルへ");
  const resultText = $("#resultText");
  const storyHint = $("#storyHint");
  const btnReplay = $("#btnReplay");
  const btnToタイトルへ = $("#btnToタイトルへ");
  const btnReviewMistakes = $("#btnReviewMistakes");
  const confessionText = $("#confessionText");
  const btnConfess = $("#btnConfess");
  const btnHold = $("#btnHold");
  const reviewList = $("#reviewList");
  const reviewSummary = $("#reviewSummary");
  const btnReviewBack = $("#btnReviewBack");

  const popup = $("#popup");
  const popupVideo = $("#popupVideo");
  const popupClose = $("#popupClose");
  const toast = $("#toast");

  const popupCard = popup.querySelector(".popup-card");
  const popupImg = document.createElement("img");
  popupImg.id = "popupImg";
  popupImg.alt = "popup image";
  popupImg.style.width = "100%";
  popupImg.style.height = "auto";
  popupImg.style.maxHeight = "calc(80vh - 52px)";
  popupImg.style.borderRadius = "12px";
  popupImg.style.display = "none";
  popupCard.appendChild(popupImg);

  const playOverlay = document.createElement("button");
  playOverlay.type = "button";
  playOverlay.textContent = "▶ 再生";
  playOverlay.style.position = "absolute";
  playOverlay.style.left = "50%";
  playOverlay.style.top = "50%";
  playOverlay.style.transform = "translate(-50%, -50%)";
  playOverlay.style.padding = "14px 18px";
  playOverlay.style.fontSize = "18px";
  playOverlay.style.borderRadius = "14px";
  playOverlay.style.border = "0";
  playOverlay.style.cursor = "pointer";
  playOverlay.style.boxShadow = "0 10px 24px rgba(0,0,0,.35)";
  playOverlay.style.display = "none";
  playOverlay.style.zIndex = "5";
  popupCard.style.position = "relative";
  popupCard.appendChild(playOverlay);

  const introNext = document.createElement("button");
  introNext.textContent = "次へ";
  introNext.style.position = "absolute";
  introNext.style.right = "18px";
  introNext.style.bottom = "18px";
  introNext.style.padding = "10px 14px";
  introNext.style.border = "none";
  introNext.style.borderRadius = "12px";
  introNext.style.background = "rgba(0,0,0,0.65)";
  introNext.style.color = "#fff";
  introNext.style.fontWeight = "700";
  introNext.style.cursor = "pointer";
  introNext.style.display = "none";
  popupCard.appendChild(introNext);

  const introCaption = document.createElement("div");
  introCaption.id = "introCaption";
  introCaption.style.position = "absolute";
  introCaption.style.left = "50%";
  introCaption.style.bottom = "18px";
  introCaption.style.transform = "translateX(-50%)";
  introCaption.style.width = "min(92%, 920px)";
  introCaption.style.padding = "14px 18px";
  introCaption.style.borderRadius = "14px";
  introCaption.style.background = "rgba(0,0,0,0.62)";
  introCaption.style.color = "#fff";
  introCaption.style.fontSize = "clamp(16px, 2.2vw, 28px)";
  introCaption.style.fontWeight = "700";
  introCaption.style.lineHeight = "1.6";
  introCaption.style.textAlign = "center";
  introCaption.style.textShadow = "0 2px 8px rgba(0,0,0,0.55)";
  introCaption.style.display = "none";
  introCaption.style.pointerEvents = "none";
  popupCard.appendChild(introCaption);

  let popupNextCallback = null;
  let bgmWasPlayingBeforePopup = false;
  let bgmEnabled = true;
  let bgmStartedOnce = false;
  const TOTAL_SESSIONS = 1;
  const PASS_LINE = 7;
  const TARGET_GAUGE = 80;
  const IS_TRIAL_VERSION = true;

  const state = {
    questions: [],
    order: [],
    idx: 0,
    correct: 0,
    locked: false,
    currentCorrectIndex: 0,
    progressData: {
      sessionNo: 1,
      passedSessions: 0,
      gauge: 0,
      streak: 0,
      happyClears: 0,
      pendingSuccess: false
    },
    selectedMode: "500",
    finalQuestionIndex: null,
    currentPackedChoices: [],
    mistakes: [],
  };


  const getModeFromUrl = () => {
    try {
      if (window.CELINE_MODE === "700") return "700";
      if (window.CELINE_MODE === "500") return "500";
      const file = (location.pathname || "").toLowerCase();
      if (file.endsWith("index_700.html")) return "700";
      if (file.endsWith("index_500.html")) return "500";
      const params = new URLSearchParams(location.search || "");
      const m = params.get("mode");
      return m === "700" ? "700" : "500";
    } catch (e) {
      return "500";
    }
  };

  const progressKey = () => `sekuhara_programming_progress_trial_${state.selectedMode}`;
  const modeLabel = () => "Excel 初級";

  const initMode = () => {
    state.selectedMode = getModeFromUrl();
    const note = document.getElementById("modeNote");
    if (note) {
      note.textContent = `Excel補習トレーニング / 4択10問 / 現在: ${modeLabel()}`;
    }
    document.title = `背久原パソコン教室 ${modeLabel()}`;
  };

  const showToast = (msg) => {
    if (!toast) return;
    toast.textContent = msg;
    toast.classList.remove("hidden");
    clearTimeout(showToast._t);
    showToast._t = setTimeout(() => toast.classList.add("hidden"), 1200);
  };

  const shuffle = (arr) => {
    for (let i = arr.length - 1; i > 0; i--) {
      const j = Math.floor(Math.random() * (i + 1));
      [arr[i], arr[j]] = [arr[j], arr[i]];
    }
    return arr;
  };


  const escapeHtml = (value) => String(value ?? "")
    .replace(/&/g, "&amp;")
    .replace(/</g, "&lt;")
    .replace(/>/g, "&gt;")
    .replace(/"/g, "&quot;")
    .replace(/'/g, "&#39;");

  const buildExplanation = (question, correctText, userAnswer = "") => {
    const q = String(question || "");

    const rules = [
      [/SUMIFS|複数条件.*合計/, "SUMIFS は複数条件で合計したいときに便利です。条件の列と合計列を取り違えないのがコツです。"],
      [/COUNTIFS|複数条件.*件数/, "COUNTIFS は複数条件の件数集計に使います。条件範囲と条件の対応をそろえて考えます。"],
      [/SUMIF|条件.*合計/, "SUMIF は 1 つの条件で合計したいときの基本です。条件範囲と合計範囲を確認します。"],
      [/COUNTIF|条件.*個数|条件.*件数/, "COUNTIF は条件に合う件数を数える関数です。完全一致か部分一致かを意識すると安定します。"],
      [/VLOOKUP|XLOOKUP|HLOOKUP|検索/, "検索系の問題は『何を探し、どこから返すか』を先に整理すると解きやすくなります。"],
      [/SUM|合計/, "SUM は合計の基本関数です。まず範囲が正しいかを見るのが大切です。"],
      [/AVERAGE|平均/, "AVERAGE は平均を出す関数です。合計ではなく平均になっているかを確認します。"],
      [/MAX|最大/, "MAX は最大値、MIN は最小値です。似た関数名の取り違えに注意します。"],
      [/MIN|最小/, "MIN は最小値を返します。最大値の MAX とセットで覚えると安定します。"],
      [/LEFT|RIGHT|MID|文字/, "文字列関数は『左・右・途中』のどれかを落ち着いて見分けると正解しやすいです。"],
      [/LEN|文字数/, "LEN は文字数を数える関数です。見た目ではなく何文字あるかを考えます。"],
      [/TRIM|空白/, "TRIM は前後や連続した余分な空白整理でよく使います。名簿や一覧の整形で定番です。"],
      [/CLEAN|制御文字|改行/, "CLEAN は見えない制御文字の整理に向く関数です。貼り付けデータの掃除で役立ちます。"],
      [/TODAY|NOW|日付|時刻/, "日付時刻系は『今日だけ』か『今の日時』かの違いを押さえると解けます。"],
      [/ROUNDUP|ROUNDDOWN|ROUND|切り上げ|切り捨て|四捨五入/, "ROUND 系は四捨五入・切り上げ・切り捨ての違いを見分けるのがポイントです。"],
      [/Ctrl|Alt|F[0-9]|ショートカット/, "ショートカット問題は実務でよく使う基本操作から覚えると得点しやすいです。"],
      [/フィルター|絞り込/, "フィルターは表示を絞る機能です。データ自体を削除するわけではありません。"],
      [/ソート|並び替え/, "ソートは順序を並べ替える操作です。表全体を対象にする意識が大切です。"],
      [/条件付き書式/, "条件付き書式は条件に合うセルを目立たせる機能です。期限管理や重複確認で役立ちます。"],
      [/入力規則|プルダウン/, "入力規則は入力ミス防止に有効です。選択肢を限定したいときに便利です。"],
      [/絶対参照|相対参照|\$/, "参照問題は『コピー後にずれてよいか、固定したいか』で考えると整理しやすいです。"],
      [/エラー|#DIV\/0!|#REF!|#N\/A|#VALUE!|#NAME\?/, "エラー系は原因をイメージすると覚えやすいです。0 割り、参照切れ、名前違いなどを区別します。"],
      [/ピボット/, "ピボットテーブルは大量データの集計で便利です。月別や担当別の集計でよく使います。"],
      [/印刷|改ページ|タイトル行/, "印刷系は『どこまで印刷するか』『見出しをどう出すか』を意識して考えます。"]
    ];

    let reason = "";
    for (const [pattern, explanation] of rules) {
      if (pattern.test(q)) {
        reason = explanation;
        break;
      }
    }
    if (!reason) {
      reason = "Excel は関数名・参照・操作名を一つずつ整理すると正解に届きやすくなります。";
    }
    const wrongLine = userAnswer
      ? `あなたが選んだ「${userAnswer}」では目的に合わないので、今回は「${correctText}」が正解です。`
      : `今回は「${correctText}」が正解です。`;
    return `正解は「${correctText}」です。${reason} ${wrongLine}`;
  };

  const getRankLabel = () => {
    const gauge = state.progressData.gauge || 0;
    if (gauge >= 90) return '達人';
    if (gauge >= 75) return '上級';
    if (gauge >= 50) return '中級';
    if (gauge >= 25) return '初級';
    return '見習い';
  };

  const renderMistakeReview = () => {
    if (!reviewList || !reviewSummary) return;
    const count = state.mistakes.length;
    reviewSummary.textContent = count
      ? `今回のミスは ${count} 問です。正解と短い解説だけ確認できるようにしました。`
      : '今回はミスがありません。とても良い出来です。';
    reviewList.innerHTML = '';
    if (!count) {
      const item = document.createElement('div');
      item.className = 'review-item';
      item.innerHTML = '<div class="review-expl">全問正解、または復習が必要な問題はありません。</div>';
      reviewList.appendChild(item);
      return;
    }
    state.mistakes.forEach((m, idx) => {
      const item = document.createElement('article');
      item.className = 'review-item';
      item.innerHTML = `
        <div class="review-no">${idx + 1}</div>
        <div class="review-question">${escapeHtml(m.q)}</div>
        <div class="review-row"><span>あなたの答え</span><strong class="review-wrong">${escapeHtml(m.userAnswer)}</strong></div>
        <div class="review-row"><span>正解</span><strong class="review-correct">${escapeHtml(m.correctAnswer)}</strong></div>
        <div class="review-expl">${escapeHtml(m.explanation)}</div>
      `;
      reviewList.appendChild(item);
    });
  };

  const loadQuestions = async () => {
    const selected = state.selectedMode === "700" ? window.CELINE_QUESTIONS_700 : window.CELINE_QUESTIONS_500;
    if (Array.isArray(selected) && selected.length) {
      state.questions = selected;
    } else if (Array.isArray(window.CELINE_QUESTIONS) && window.CELINE_QUESTIONS.length) {
      state.questions = window.CELINE_QUESTIONS;
    }
    if (location.protocol !== "file:") {
      try {
        const fileName = state.selectedMode === "700" ? "assets/questions_700.json" : "assets/questions_500.json";
        const res = await fetch(fileName, { cache: "no-store" });
        const data = await res.json();
        if (Array.isArray(data) && data.length) state.questions = data;
      } catch (e) {}
    }
    if (!Array.isArray(state.questions) || !state.questions.length) {
      throw new Error("questions load error");
    }
  };

  const loadProgress = () => {
    if (IS_TRIAL_VERSION) {
      resetProgress();
      return;
    }
    try {
      const raw = localStorage.getItem(progressKey());
      if (!raw) return;
      const p = JSON.parse(raw);
      if (typeof p.sessionNo === "number") state.progressData.sessionNo = Math.min(TOTAL_SESSIONS, Math.max(1, Math.floor(p.sessionNo)));
      if (typeof p.passedSessions === "number") state.progressData.passedSessions = Math.min(TOTAL_SESSIONS, Math.max(0, Math.floor(p.passedSessions)));
      if (typeof p.gauge === "number") state.progressData.gauge = Math.min(100, Math.max(0, Math.floor(p.gauge)));
      if (typeof p.streak === "number") state.progressData.streak = Math.max(0, Math.floor(p.streak));
      if (typeof p.happyClears === "number") state.progressData.happyClears = Math.max(0, Math.floor(p.happyClears));
    } catch (e) {}
  };

  const saveProgress = () => {
    try {
      localStorage.setItem(progressKey(), JSON.stringify(state.progressData));
    } catch (e) {}
  };

  const resetProgress = () => {
    state.progressData = { sessionNo: 1, passedSessions: 0, gauge: 0, streak: 0, happyClears: 0,
      pendingSuccess: false };
    try {
      localStorage.removeItem("celine_date_progress_gauge");
      localStorage.removeItem("celine_programming_progress_python");
      localStorage.removeItem("celine_programming_progress_java");
    } catch (e) {}
    saveProgress();
  };

  const renderGallery = () => {
    galleryList.innerHTML = "";
    const item = document.createElement("div");
    item.className = "hint";
    item.textContent = "体験版では成果記録は視聴できません。続きは製品版でお楽しみください。";
    galleryList.appendChild(item);
  };

  const setScreen = (name) => {
    Object.values(screens).forEach(s => s && s.classList.remove("active"));
    screens[name] && screens[name].classList.add("active");
  };

  const applyBgm = async (forcePlay = false) => {
    if (!bgmEl || !bgmEnabled) return;
    bgmEl.volume = 0.25;
    bgmEl.loop = true;
    bgmEl.preload = "auto";
    bgmEl.defaultMuted = false;
    bgmEl.muted = false;
    try {
      if (!bgmStartedOnce) bgmEl.load();
    } catch (e) {}
    if (!forcePlay) return;
    try {
      await bgmEl.play();
      bgmStartedOnce = true;
      bgmEl.muted = false;
    } catch (e) {
      showToast("BGMはStart後にもう一度押すと始まる場合があります");
    }
  };

  const getQuizSideCandidates = () => {
    const stage = Math.min(4, Math.max(1, Math.floor((state.progressData.sessionNo - 1) / 3) + 1));
    return [
      `assets/quiz_side_${stage}.png`,
      `assets/quiz_side_${stage}.jpg`,
      "assets/quiz_side.png",
      "assets/quiz_side.jpg"
    ];
  };

  const findQuizImagePanel = () => document.querySelector(".quiz-image");

  const tryLoadImage = (src) => new Promise((resolve, reject) => {
    const img = new Image();
    img.onload = () => resolve(src);
    img.onerror = () => reject(new Error("fail"));
    img.src = src + "?v=" + Date.now();
  });

  const setupQuizSideImage = async () => {
    const panel = findQuizImagePanel();
    if (!panel) return;
    let ok = null;
    for (const src of getQuizSideCandidates()) {
      try { await tryLoadImage(src); ok = src; break; } catch (e) {}
    }
    if (!ok) return;
    panel.style.backgroundImage = `url("${ok}?v=${Date.now()}")`;
    panel.style.backgroundSize = "cover";
    panel.style.backgroundPosition = "center";
    panel.style.backgroundRepeat = "no-repeat";
  };

  const showPopupImage = (imgSrc) => {
    popupVideo.pause();
    popupVideo.style.display = "none";
    popupImg.src = imgSrc;
    popupImg.style.display = "block";
    popup.classList.remove("hidden");
  };

  const showPopupImageStep = (imageItem, onDone) => {
    const item = typeof imageItem === "string" ? { src: imageItem, caption: "" } : (imageItem || {});
    try { popupVideo.pause(); } catch (e) {}
    popupVideo.style.display = "none";
    popupImg.src = item.src || "";
    popupImg.style.display = "block";
    playOverlay.style.display = "none";
    introCaption.textContent = item.caption || "";
    introCaption.style.display = item.caption ? "block" : "none";
    introNext.style.display = "inline-block";
    popupNextCallback = onDone || null;
    popup.classList.remove("hidden");
  };

  const showIntroSequence = (images, onDone) => {
    const seq = Array.isArray(images) ? images.filter(Boolean) : [];
    let idx = 0;
    const next = () => {
      if (idx >= seq.length) {
        onDone && onDone();
        return;
      }
      showPopupImageStep(seq[idx], () => {
        idx += 1;
        next();
      });
    };
    next();
  };

  introNext.addEventListener("click", () => {
    const cb = popupNextCallback;
    popupNextCallback = null;
    closeVideoPopup(false);
    cb && cb();
  });

  const showPlayOverlay = (onClick) => {
    playOverlay.style.display = "block";
    const handler = () => {
      playOverlay.style.display = "none";
      playOverlay.removeEventListener("click", handler);
      onClick && onClick();
    };
    playOverlay.addEventListener("click", handler);
  };

  const showPopupVideo = (videoSrc, fallbackImgSrc) => {
    popupImg.style.display = "none";
    popupVideo.style.display = "block";
    popupVideo.pause();
    popupVideo.removeAttribute("src");
    popupVideo.load();
    popupVideo.src = videoSrc;
    popup.classList.remove("hidden");
    try {
      bgmWasPlayingBeforePopup = !!(bgmEl && !bgmEl.paused && !bgmEl.ended);
      if (bgmEl) bgmEl.pause();
    } catch (e) {}
    const onError = () => {
      popupVideo.removeEventListener("error", onError);
      showPopupImage(fallbackImgSrc);
    };
    popupVideo.addEventListener("error", onError);
    popupVideo.play().catch(() => {
      showToast("動画は再生ボタンで開始できます");
      showPlayOverlay(() => popupVideo.play().catch(() => {}));
    });
  };

  const closeVideoPopup = (resumeBgm = true) => {
    popup.classList.add("hidden");
    try { popupVideo.pause(); } catch (e) {}
    try { playOverlay.style.display = "none"; } catch (e) {}
    try { introNext.style.display = "none"; } catch (e) {}
    try { introCaption.style.display = "none"; introCaption.textContent = ""; } catch (e) {}
    popupNextCallback = null;
    if (resumeBgm && bgmEnabled) applyBgm(true);
    bgmWasPlayingBeforePopup = false;
  };

  const startSession = () => {
    state.order = shuffle([...Array(state.questions.length).keys()]).slice(0, 10);
    state.idx = 0;
    state.correct = 0;
    state.locked = false;
    state.mistakes = [];
    renderQuestion();
    setScreen("quiz");
    setTimeout(setupQuizSideImage, 50);
  };

  const renderHeader = () => {
    if (questionModeLabel) questionModeLabel.textContent = `${modeLabel()} / 補習 ${state.progressData.sessionNo}`;
    if (progress) progress.textContent = `補習 ${state.progressData.sessionNo} / ${TOTAL_SESSIONS}　問題 ${state.idx + 1} / 10`;
    if (scoreEl) scoreEl.textContent = `今回 ${state.correct} / 10　通算合格 ${state.progressData.passedSessions} / ${TOTAL_SESSIONS}`;
    if (gaugeText) gaugeText.textContent = `理解度ゲージ (${modeLabel()}) / ${getRankLabel()}`;
    if (gaugeFill) gaugeFill.style.width = `${state.progressData.gauge}%`;
  };

  const renderQuestion = () => {
    renderHeader();
    const qIndex = state.order[state.idx];
    const q = state.questions[qIndex];
    if (questionEl) questionEl.textContent = q.q;
    const packed = q.choices.map((t, i) => ({ t, isCorrect: i === q.a }));
    shuffle(packed);
    state.currentPackedChoices = packed;
    state.currentCorrectIndex = packed.findIndex(x => x.isCorrect);
    choicesEl.innerHTML = "";
    packed.forEach((item, i) => {
      const b = document.createElement("button");
      b.className = "choice";
      b.type = "button";
      b.textContent = item.t;
      b.addEventListener("click", () => onAnswer(i));
      choicesEl.appendChild(b);
    });
    setTimeout(setupQuizSideImage, 10);
  };

  const gainGauge = (correct) => {
    if (correct) {
      state.progressData.streak += 1;
      const add = 1;
      state.progressData.gauge = Math.min(100, state.progressData.gauge + add);
      return add;
    } else {
      state.progressData.streak = 0;
      const add = 0;
      state.progressData.gauge = Math.min(100, state.progressData.gauge + add);
      return add;
    }
  };

  const onAnswer = (choiceIndex) => {
    if (state.locked) return;
    state.locked = true;
    const buttons = [...choicesEl.querySelectorAll(".choice")];
    buttons.forEach((b, i) => {
      if (i === state.currentCorrectIndex) b.classList.add("correct");
      if (i === choiceIndex && i !== state.currentCorrectIndex) b.classList.add("wrong");
      b.disabled = true;
    });
    const correct = (choiceIndex === state.currentCorrectIndex);
    if (correct) state.correct += 1;
    else {
      const qIndex = state.order[state.idx];
      const q = state.questions[qIndex];
      const correctAnswer = state.currentPackedChoices[state.currentCorrectIndex]?.t || "";
      const userAnswer = state.currentPackedChoices[choiceIndex]?.t || "";
      state.mistakes.push({
        q: q.q,
        userAnswer,
        correctAnswer,
        explanation: buildExplanation(q.q, correctAnswer, userAnswer)
      });
    }
    const gained = gainGauge(correct);
    renderHeader();
    showToast(`${correct ? "正解" : "不正解"}　学力 +${gained}`);
    saveProgress();

    setTimeout(() => {
      state.locked = false;
      state.idx += 1;
      if (state.idx >= 10) finishSession();
      else renderQuestion();
    }, 650);
  };

  const getSessionResultImage = (passed) => passed ? "assets/day_pass.png" : "assets/day_fail.png";

  const getFinalEnding = () => {
    const totalCorrect = state.progressData.gauge || 0;
    const passed = state.progressData.passedSessions || 0;

    if (totalCorrect >= 85 && passed >= 9) {
      return {
        key: "best",
        title: "最良エンド",
        text: `総正解数は ${totalCorrect} / 100、合格日数は ${passed} / 10。\n10日間を通して高水準を維持し、最後まで安定して結果を積み上げた。`,
        hint: "努力も実力も十分。最高の結末に届いた。",
        image: "assets/ending_best.png",
        video: "assets/ending_best.mp4",
        happy: true
      };
    }
    if (totalCorrect >= 70 && passed >= 7) {
      return {
        key: "good",
        title: "良エンド",
        text: `総正解数は ${totalCorrect} / 100、合格日数は ${passed} / 10。\n必要な基準をしっかり越え、10日間の補習をきちんとやり切った。`,
        hint: "十分に良い結果。ここから先にも期待できる。",
        image: "assets/ending_good.png",
        video: "assets/ending_good.mp4",
        happy: false
      };
    }
    if (totalCorrect >= 60) {
      return {
        key: "bitter",
        title: "ほろ苦いエンド",
        text: `総正解数は ${totalCorrect} / 100、合格日数は ${passed} / 10。\n実力は見せたが、日ごとの安定感が足りず、あと一歩届かなかった。`,
        hint: "惜しい結果。もう少し安定すれば上の結末が見える。",
        image: "assets/ending_bitter.png",
        video: "assets/ending_bitter.mp4",
        happy: false
      };
    }
    return {
      key: "bad",
      title: "残念エンド",
      text: `総正解数は ${totalCorrect} / 100、合格日数は ${passed} / 10。\n今回は基礎固めが足りず、全体として苦しい10日間になった。`,
      hint: "もう一度最初から積み上げれば、結果は必ず変わる。",
      image: "assets/ending_bad.png",
      video: "assets/ending_bad.mp4",
      happy: false
    };
  };

  const finishSession = () => {
    const passed = state.correct >= PASS_LINE;
    state.progressData.passedSessions = passed ? 1 : 0;
    state.progressData.sessionNo = 1;
    saveProgress();

    if (resultタイトルへ) resultタイトルへ.textContent = passed ? "体験版 合格" : "体験版 終了";
    if (resultText) {
      resultText.textContent = passed
        ? `今回の体験版は ${state.correct} / 10 で合格。製品版ではさらに続きのレッスンと追加イベントが待っています。`
        : `今回の体験版は ${state.correct} / 10。製品版では続きのレッスンに挑戦できます。`;
    }
    if (storyHint) {
      storyHint.textContent = passed
        ? "ご褒美動画は1本のみ収録しています。続きは製品版でご確認ください。"
        : "お仕置き動画は1本のみ収録しています。続きは製品版でご確認ください。";
    }
    renderMistakeReview();
    if (btnReviewMistakes) {
      btnReviewMistakes.style.display = state.mistakes.length ? "inline-flex" : "none";
    }
    setScreen("result");

    const clip = passed ? "assets/reward_3.mp4" : "assets/punish.mp4";
    showPopupImageStep(getSessionResultImage(passed), () => showPopupVideo(clip, "assets/start.png"));

    btnReplay.textContent = "タイトルへ戻る";
    btnReplay.onclick = () => {
      resetProgress();
      setScreen("start");
    };
  };

  const finishConfession = () => {
    const ending = getFinalEnding();
    if (ending.happy) {
      state.progressData.happyClears += 1;
      saveProgress();
    }
    if (resultタイトルへ) resultタイトルへ.textContent = ending.title;
    if (resultText) resultText.textContent = ending.text;
    if (storyHint) storyHint.textContent = ending.hint;
    setScreen("result");
    showPopupImageStep(ending.image, () => showPopupVideo(ending.video, ending.image));
    btnReplay.textContent = "最初からやり直す";
    btnReplay.onclick = () => {
      resetProgress();
      startSession();
    };
  };


  const showConfessionPause = () => {
    if (confessionText) {
      confessionText.textContent = `10日間のレッスンはすべて終了しました。
ここまでの総正解数は ${state.progressData.gauge} / 100、合格日数は ${state.progressData.passedSessions} / 10 です。

最終結果は4種類です。
・最良エンド：総正解85以上 かつ 合格9日以上
・良エンド：総正解70以上 かつ 合格7日以上
・ほろ苦いエンド：総正解60以上
・残念エンド：総正解60未満

この積み上げが、そのまま最後の結果になります。`;
    }
    setScreen("confession");
  };


  btnStart && btnStart.addEventListener("click", async () => {
    bgmEnabled = true;
    if (bgmEl) {
      bgmEl.muted = false;
      bgmEl.defaultMuted = false;
      bgmEl.volume = 0.25;
    }
    await applyBgm(true);
    showIntroSequence([
      { src: "assets/intro_shop.png", caption: "" },
      "assets/intro_1.png",
      "assets/intro_2.png",
      "assets/intro_3.png"
    ], async () => {
      await applyBgm(true);
      if (state.progressData.sessionNo > 1 || state.progressData.passedSessions > 0 || state.progressData.gauge > 0) {
        if (confirm(`前回の${modeLabel()}データがあります。\n補習 ${state.progressData.sessionNo} / ${TOTAL_SESSIONS}\n理解度 ${state.progressData.gauge} / 100 から再開しますか？\n「キャンセル」で最初からやり直します。`)) {
          startSession();
        } else {
          resetProgress();
          startSession();
        }
      } else {
        startSession();
      }
    });
  });

  btnOption && btnOption.addEventListener("click", () => setScreen("option"));
  btnExit && btnExit.addEventListener("click", () => showToast("終了する場合はタブを閉じてください"));
  btnGallery && btnGallery.addEventListener("click", () => { renderGallery(); setScreen("gallery"); });
  btnResetProgress && btnResetProgress.addEventListener("click", () => {
    const ok = confirm("解放データと進行データをリセットします。よろしいですか？");
    if (!ok) return;
    resetProgress();
    showToast("リセットしました");
  });
  btnOptBack && btnOptBack.addEventListener("click", () => setScreen("start"));
  btnGalleryBack && btnGalleryBack.addEventListener("click", () => setScreen("option"));
  btnQuit && btnQuit.addEventListener("click", () => setScreen("start"));
  btnToタイトルへ && btnToタイトルへ.addEventListener("click", () => setScreen("start"));
  btnReviewMistakes && btnReviewMistakes.addEventListener("click", () => { renderMistakeReview(); setScreen("review"); });
  btnReviewBack && btnReviewBack.addEventListener("click", () => setScreen("result"));


  btnConfess && btnConfess.addEventListener("click", () => {
    if (IS_TRIAL_VERSION) {
      setScreen("start");
      return;
    }
    finishConfession();
  });

  popupClose && popupClose.addEventListener("click", closeVideoPopup);
  const popupBackdrop = popup ? popup.querySelector(".popup-backdrop") : null;
  popupBackdrop && popupBackdrop.addEventListener("click", closeVideoPopup);
  popupVideo && popupVideo.addEventListener("ended", () => closeVideoPopup());

  document.addEventListener("pointerdown", () => {
    if (bgmEnabled && bgmEl && popup.classList.contains("hidden")) {
      bgmEl.muted = false;
      if (bgmEl.paused) applyBgm(true);
    }
  }, { passive: true });

  (async () => {
    initMode();
    loadProgress();
    await applyBgm(false);
    try {
      await loadQuestions();
    } catch (e) {
      alert("問題データの読み込みに失敗しました。");
    }
    setTimeout(setupQuizSideImage, 100);
    window.addEventListener("resize", () => setTimeout(setupQuizSideImage, 50));
  })();
})();
