diff --git a/app.js b/app.js
new file mode 100644
index 0000000..edbb407
--- /dev/null
+++ b/app.js
@@ -0,0 +1,561 @@
+const app = document.getElementById('app');
+const progressContainer = document.getElementById('progressContainer');
+const progressBar = document.getElementById('progressBar');
+const statusPanel = document.getElementById('statusPanel');
+const statusList = document.getElementById('statusList');
+const statusPanelClose = document.getElementById('statusPanelClose');
+const resultOverlay = document.getElementById('resultOverlay');
+const themeToggle = document.getElementById('themeToggle');
+
+let questions = [];
+
+const state = {
+ mode: null,
+ questionIndices: [],
+ currentQuestion: 0,
+ answers: {},
+ revealed: {},
+ bookmarks: new Set(),
+ completed: false,
+ customRange: null,
+ score: null
+};
+
+async function init() {
+ setupTheme();
+ try {
+ const response = await fetch('question.json');
+ questions = await response.json();
+ renderMenu();
+ } catch (error) {
+ renderError(error);
+ }
+}
+
+function setupTheme() {
+ const saved = localStorage.getItem('quiz-theme');
+ const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
+ const root = document.documentElement;
+
+ const applyTheme = (mode) => {
+ root.classList.toggle('dark', mode === 'dark');
+ themeToggle.textContent = mode === 'dark' ? '☀️' : '🌙';
+ localStorage.setItem('quiz-theme', mode);
+ };
+
+ const initial = saved || (prefersDark ? 'dark' : 'light');
+ applyTheme(initial);
+
+ themeToggle.addEventListener('click', () => {
+ const next = root.classList.contains('dark') ? 'light' : 'dark';
+ applyTheme(next);
+ });
+}
+
+function resetState() {
+ state.mode = null;
+ state.questionIndices = [];
+ state.currentQuestion = 0;
+ state.answers = {};
+ state.revealed = {};
+ state.bookmarks = new Set();
+ state.completed = false;
+ state.customRange = null;
+ state.score = null;
+}
+
+function renderMenu() {
+ resetState();
+ progressContainer.hidden = true;
+ progressBar.style.width = '0%';
+ progressBar.setAttribute('aria-valuenow', '0');
+ progressBar.setAttribute('aria-valuetext', '尚未開始作答');
+ app.innerHTML = `
+
+
+
選擇練習模式
+
題庫共 ${questions.length} 題,挑選適合自己的練習方式開始刷題吧!
+
+
+
+ `;
+
+ app.querySelectorAll('button[data-mode]').forEach((btn) => {
+ btn.addEventListener('click', () => {
+ const mode = btn.dataset.mode;
+ if (mode === 'practice') {
+ startPracticeMode();
+ } else if (mode === 'exam') {
+ startExamMode();
+ }
+ });
+ });
+
+ const customForm = document.getElementById('customForm');
+ customForm.addEventListener('submit', (event) => {
+ event.preventDefault();
+ const start = Number(document.getElementById('rangeStart').value);
+ const end = Number(document.getElementById('rangeEnd').value);
+ if (Number.isNaN(start) || Number.isNaN(end)) {
+ alert('請輸入有效的題號範圍');
+ return;
+ }
+ if (start < 1 || end < 1 || start > questions.length || end > questions.length || start > end) {
+ alert('題號範圍無效,請重新輸入。');
+ return;
+ }
+ startCustomMode(start, end);
+ });
+}
+
+function prepareSession(mode, indices, customRange = null) {
+ state.mode = mode;
+ state.questionIndices = indices;
+ state.currentQuestion = 0;
+ state.answers = {};
+ state.revealed = {};
+ state.bookmarks = new Set();
+ state.completed = false;
+ state.customRange = customRange;
+ state.score = null;
+}
+
+function startPracticeMode() {
+ const indices = questions.map((_, index) => index);
+ prepareSession('practice', indices);
+ renderQuestionView();
+}
+
+function startCustomMode(start, end) {
+ const startIndex = start - 1;
+ const endIndex = end - 1;
+ const range = [];
+ for (let i = startIndex; i <= endIndex; i += 1) {
+ range.push(i);
+ }
+ const indices = pickRandom(range, Math.min(50, range.length));
+ prepareSession('custom', indices, { start, end });
+ renderQuestionView();
+}
+
+function startExamMode() {
+ const total = Math.min(50, questions.length);
+ const pool = questions.map((_, index) => index);
+ const indices = pickRandom(pool, total);
+ prepareSession('exam', indices);
+ renderQuestionView();
+}
+
+function pickRandom(list, count) {
+ const cloned = [...list];
+ for (let i = cloned.length - 1; i > 0; i -= 1) {
+ const j = Math.floor(Math.random() * (i + 1));
+ [cloned[i], cloned[j]] = [cloned[j], cloned[i]];
+ }
+ return cloned.slice(0, count);
+}
+
+function getModeBadgeText() {
+ switch (state.mode) {
+ case 'practice':
+ return '自由參考模式';
+ case 'custom':
+ return '自訂範圍測驗';
+ case 'exam':
+ return '模擬考試模式';
+ default:
+ return '';
+ }
+}
+
+function getModeSubtext() {
+ switch (state.mode) {
+ case 'practice':
+ return `題庫總題數:${questions.length}`;
+ case 'custom':
+ if (state.customRange) {
+ return `題號範圍 ${state.customRange.start} - ${state.customRange.end},隨機抽出 ${state.questionIndices.length} 題`;
+ }
+ return '';
+ case 'exam':
+ return `整份題庫隨機抽出 ${state.questionIndices.length} 題`;
+ default:
+ return '';
+ }
+}
+
+function renderQuestionView() {
+ const total = state.questionIndices.length;
+ if (!total) {
+ app.innerHTML = '';
+ document.getElementById('backToMenu').addEventListener('click', renderMenu);
+ return;
+ }
+
+ progressContainer.hidden = false;
+ updateProgressBar();
+
+ const actualIndex = state.questionIndices[state.currentQuestion];
+ const question = questions[actualIndex];
+ const selectedOption = state.answers[actualIndex];
+ const isRevealed = state.mode === 'practice' ? Boolean(state.revealed[actualIndex]) : state.completed;
+
+ const answeredCount = state.questionIndices.filter((idx) => state.answers[idx] !== undefined).length;
+
+ app.innerHTML = `
+
+
+
+ ${question.option.map((_, idx) => renderOptionButton(question, actualIndex, idx, selectedOption, isRevealed)).join('')}
+
+ ${renderFeedback(question, actualIndex, selectedOption, isRevealed)}
+ ${renderExplanation(question, isRevealed)}
+ ${renderPs(question)}
+
+
+
+
+
+
+
+
+
+
+
+ ${state.mode !== 'practice' && !state.completed ? '
' : ''}
+
+
+
+ `;
+
+ app.querySelectorAll('.option-button').forEach((btn) => {
+ btn.addEventListener('click', () => {
+ const optionIndex = Number(btn.dataset.index);
+ handleOptionSelect(actualIndex, optionIndex);
+ });
+ });
+
+ document.getElementById('prevBtn').addEventListener('click', () => {
+ if (state.currentQuestion > 0) {
+ state.currentQuestion -= 1;
+ renderQuestionView();
+ }
+ });
+
+ document.getElementById('nextBtn').addEventListener('click', () => {
+ if (state.currentQuestion < total - 1) {
+ state.currentQuestion += 1;
+ renderQuestionView();
+ }
+ });
+
+ const jumpInput = document.getElementById('jumpInput');
+ document.getElementById('jumpBtn').addEventListener('click', () => {
+ const target = Number(jumpInput.value);
+ jumpToQuestion(target);
+ });
+
+ jumpInput.addEventListener('keydown', (event) => {
+ if (event.key === 'Enter') {
+ event.preventDefault();
+ const target = Number(jumpInput.value);
+ jumpToQuestion(target);
+ }
+ });
+
+ document.getElementById('bookmarkBtn').addEventListener('click', () => {
+ toggleBookmark(actualIndex);
+ });
+
+ document.getElementById('statusBtn').addEventListener('click', () => {
+ openStatusPanel();
+ });
+
+ document.getElementById('menuBtn').addEventListener('click', () => {
+ renderMenu();
+ });
+
+ const submitBtn = document.getElementById('submitBtn');
+ if (submitBtn) {
+ submitBtn.addEventListener('click', () => {
+ handleSubmit();
+ });
+ }
+
+ updateStatusPanel();
+}
+
+function renderOptionButton(question, actualIndex, optionIndex, selectedOption, isRevealed) {
+ const baseClass = ['option-button'];
+ const isSelected = selectedOption === optionIndex;
+ if (isSelected) baseClass.push('selected');
+
+ const isCorrect = question.answer === optionIndex;
+
+ if (isRevealed) {
+ if (isCorrect) {
+ baseClass.push('correct');
+ } else if (isSelected) {
+ baseClass.push('incorrect');
+ }
+ }
+
+ const suffix = question.optionend || '';
+ return `
+
+ `;
+}
+
+function renderFeedback(question, actualIndex, selectedOption, isRevealed) {
+ if (!isRevealed) {
+ if (state.mode === 'practice' && state.answers[actualIndex] !== undefined) {
+ const isCorrect = question.answer === selectedOption;
+ return `
${isCorrect ? '答對了!' : '答錯了,再接再厲!'} 正確答案為 ${String.fromCharCode(65 + question.answer)} 選項。
`;
+ }
+ return '';
+ }
+
+ const isCorrect = question.answer === selectedOption;
+ const message = selectedOption === undefined ? '此題未作答。' : (isCorrect ? '答對了!' : '答錯了,來看看解析。');
+ return `${message} 正確答案為 ${String.fromCharCode(65 + question.answer)} 選項。
`;
+}
+
+function renderExplanation(question, isRevealed) {
+ if (!isRevealed) return '';
+ const text = question.explain?.trim();
+ if (!text) return '';
+ return ``;
+}
+
+function renderPs(question) {
+ const note = question.ps?.trim();
+ if (!note) return '';
+ return `${formatText(note)}
`;
+}
+
+function handleOptionSelect(actualIndex, optionIndex) {
+ state.answers[actualIndex] = optionIndex;
+ if (state.mode === 'practice') {
+ state.revealed[actualIndex] = true;
+ }
+ if (state.mode !== 'practice' && state.completed) {
+ state.revealed[actualIndex] = true;
+ }
+ renderQuestionView();
+}
+
+function toggleBookmark(actualIndex) {
+ if (state.bookmarks.has(actualIndex)) {
+ state.bookmarks.delete(actualIndex);
+ } else {
+ state.bookmarks.add(actualIndex);
+ }
+ renderQuestionView();
+}
+
+function jumpToQuestion(target) {
+ const total = state.questionIndices.length;
+ if (!target || target < 1 || target > total) {
+ alert('請輸入 1 到 ' + total + ' 之間的題號');
+ return;
+ }
+ state.currentQuestion = target - 1;
+ renderQuestionView();
+}
+
+function openStatusPanel() {
+ statusPanel.classList.remove('hidden');
+ statusPanel.setAttribute('aria-hidden', 'false');
+}
+
+function closeStatusPanel() {
+ statusPanel.classList.add('hidden');
+ statusPanel.setAttribute('aria-hidden', 'true');
+}
+
+statusPanelClose.addEventListener('click', closeStatusPanel);
+statusPanel.addEventListener('click', (event) => {
+ if (event.target === statusPanel) {
+ closeStatusPanel();
+ }
+});
+
+resultOverlay.addEventListener('click', (event) => {
+ if (event.target === resultOverlay) {
+ hideResultOverlay();
+ }
+});
+
+function updateStatusPanel() {
+ const total = state.questionIndices.length;
+ statusList.innerHTML = state.questionIndices.map((idx, order) => {
+ const answered = state.answers[idx] !== undefined;
+ const bookmarked = state.bookmarks.has(idx);
+ const classes = ['status-item'];
+ if (answered) classes.push('answered');
+ if (bookmarked) classes.push('bookmarked');
+ if (order === state.currentQuestion) classes.push('current');
+ return `
+
+ ${order + 1}
+ 原題號 #${idx + 1}
+ ${bookmarked ? '★' : ''}
+ ${answered ? '✔' : '…'}
+
+ `;
+ }).join('');
+
+ statusList.querySelectorAll('.status-item').forEach((item) => {
+ item.addEventListener('click', (event) => {
+ const order = Number(event.currentTarget.dataset.order);
+ state.currentQuestion = order;
+ closeStatusPanel();
+ renderQuestionView();
+ });
+ });
+}
+
+function updateProgressBar() {
+ const total = state.questionIndices.length;
+ if (!total) {
+ progressBar.style.width = '0%';
+ return;
+ }
+ const answered = state.questionIndices.filter((idx) => state.answers[idx] !== undefined).length;
+ const percent = Math.round((answered / total) * 100);
+ progressBar.style.width = `${percent}%`;
+ progressBar.dataset.label = `已作答 ${answered}/${total}`;
+ progressBar.setAttribute('aria-valuenow', String(percent));
+ progressBar.setAttribute('aria-valuetext', `已作答 ${answered} 題,共 ${total} 題`);
+}
+
+function handleSubmit() {
+ const total = state.questionIndices.length;
+ const answered = state.questionIndices.filter((idx) => state.answers[idx] !== undefined).length;
+ if (answered < total) {
+ const proceed = confirm(`仍有 ${total - answered} 題未作答,確定要交卷嗎?`);
+ if (!proceed) {
+ return;
+ }
+ }
+
+ state.completed = true;
+ state.questionIndices.forEach((idx) => {
+ state.revealed[idx] = true;
+ });
+
+ const correct = state.questionIndices.reduce((acc, idx) => acc + (state.answers[idx] === questions[idx].answer ? 1 : 0), 0);
+ const score = Math.round((correct / total) * 100);
+ state.score = { total, correct, answered, unanswered: total - answered, score };
+
+ showResultOverlay();
+ renderQuestionView();
+}
+
+function showResultOverlay() {
+ const { total, correct, answered, unanswered, score } = state.score;
+ resultOverlay.innerHTML = `
+
+
+
測驗成績
+
恭喜完成本次${state.mode === 'exam' ? '模擬考試' : '自訂測驗'}!以下是你的作答結果:
+
+
+
+ ${score}
+ 總分
+
+
+ ${correct}
+ 答對題數
+
+
+ ${answered - correct}
+ 答錯題數
+
+
+ ${unanswered}
+ 未作答
+
+
+
+
+
+
+
+ `;
+ resultOverlay.classList.remove('hidden');
+ resultOverlay.setAttribute('aria-hidden', 'false');
+
+ document.getElementById('reviewBtn').addEventListener('click', () => {
+ hideResultOverlay();
+ });
+
+ document.getElementById('backBtn').addEventListener('click', () => {
+ hideResultOverlay();
+ renderMenu();
+ });
+
+}
+
+function hideResultOverlay() {
+ resultOverlay.classList.add('hidden');
+ resultOverlay.setAttribute('aria-hidden', 'true');
+}
+
+function renderError(error) {
+ app.innerHTML = `
+
+ 載入題庫失敗
+ 無法讀取題庫資料,請稍後再試。
+ ${error.message}
+
+ `;
+}
+
+function formatText(text = '') {
+ return text.replace(/\n/g, '
');
+}
+
+function sanitizeImagePath(path) {
+ if (!path) return '';
+ if (path.startsWith('http')) return path;
+ return path.startsWith('./') ? path : `./${path.replace(/^\/?/, '')}`;
+}
+
+init();
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..4b89df2
--- /dev/null
+++ b/index.html
@@ -0,0 +1,42 @@
+
+
+
+
+
+ 學科刷題系統
+
+
+
+
+
+
+
+
+
+
+
+ 已作答
+ 未作答
+ 已收藏
+
+
+
+
+
+
+
+
+
+
diff --git a/styles.css b/styles.css
new file mode 100644
index 0000000..121e316
--- /dev/null
+++ b/styles.css
@@ -0,0 +1,579 @@
+:root {
+ color-scheme: light dark;
+ --bg: #f8f9fb;
+ --card-bg: #ffffff;
+ --text: #1a1a1a;
+ --subtext: #555555;
+ --border: #e0e0e0;
+ --accent: #2f7dff;
+ --accent-soft: rgba(47, 125, 255, 0.12);
+ --danger: #e74c3c;
+ --success: #27ae60;
+ --shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
+}
+
+:root.dark {
+ --bg: #121417;
+ --card-bg: #1f2329;
+ --text: #f1f5f9;
+ --subtext: #cbd5f5;
+ --border: #2b3037;
+ --accent: #6d9bff;
+ --accent-soft: rgba(109, 155, 255, 0.16);
+ --danger: #ff6b6b;
+ --success: #4cd964;
+ --shadow: 0 10px 30px rgba(15, 23, 42, 0.35);
+}
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ font-family: "Noto Sans TC", "PingFang TC", "Microsoft JhengHei", sans-serif;
+ background: var(--bg);
+ color: var(--text);
+ min-height: 100vh;
+ display: flex;
+ flex-direction: column;
+}
+
+.top-bar {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 1rem clamp(1rem, 5vw, 3rem);
+ background: var(--card-bg);
+ border-bottom: 1px solid var(--border);
+ position: sticky;
+ top: 0;
+ z-index: 100;
+}
+
+.branding h1 {
+ font-size: clamp(1.25rem, 2vw + 0.8rem, 2rem);
+ margin: 0;
+}
+
+.actions {
+ display: flex;
+ gap: 0.5rem;
+ align-items: center;
+}
+
+.icon-button {
+ border: none;
+ background: transparent;
+ color: var(--text);
+ font-size: 1.25rem;
+ cursor: pointer;
+ border-radius: 50%;
+ width: 2.5rem;
+ height: 2.5rem;
+ display: grid;
+ place-items: center;
+ transition: background 0.2s ease, transform 0.2s ease;
+}
+
+.icon-button:hover,
+.icon-button:focus {
+ background: var(--accent-soft);
+ outline: none;
+ transform: translateY(-1px);
+}
+
+.progress-container {
+ height: 6px;
+ background: var(--border);
+ margin: 0 clamp(1rem, 5vw, 3rem);
+ border-radius: 999px;
+ overflow: hidden;
+}
+
+.progress-bar {
+ height: 100%;
+ background: linear-gradient(90deg, var(--accent), #6ee7b7);
+ width: 0%;
+ transition: width 0.3s ease;
+}
+
+.app {
+ flex: 1;
+ padding: clamp(1rem, 4vw, 3rem);
+ display: flex;
+ justify-content: center;
+}
+
+.card {
+ width: min(960px, 100%);
+ background: var(--card-bg);
+ border-radius: 1.25rem;
+ padding: clamp(1.5rem, 5vw, 3rem);
+ box-shadow: var(--shadow);
+ border: 1px solid var(--border);
+ display: flex;
+ flex-direction: column;
+ gap: 1.5rem;
+}
+
+.card h2 {
+ margin: 0;
+}
+
+.text-muted {
+ color: var(--subtext);
+ line-height: 1.6;
+}
+
+.menu-grid {
+ display: grid;
+ gap: 1.5rem;
+}
+
+.mode-card {
+ background: var(--card-bg);
+ border: 1px solid var(--border);
+ border-radius: 1rem;
+ padding: 1.5rem;
+ display: flex;
+ flex-direction: column;
+ gap: 0.75rem;
+ transition: transform 0.2s ease, box-shadow 0.2s ease;
+}
+
+.mode-card:hover {
+ transform: translateY(-4px);
+ box-shadow: var(--shadow);
+}
+
+.mode-card h3 {
+ margin: 0;
+}
+
+.mode-card p {
+ margin: 0;
+ color: var(--subtext);
+ line-height: 1.6;
+}
+
+.custom-form {
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.form-row {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 1rem;
+}
+
+.form-row label {
+ flex: 1 1 180px;
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+ font-weight: 600;
+}
+
+.form-row input {
+ padding: 0.6rem 0.75rem;
+ border-radius: 0.75rem;
+ border: 1px solid var(--border);
+ background: transparent;
+ color: inherit;
+}
+
+.form-row input:focus {
+ outline: 2px solid var(--accent);
+}
+
+.primary-button,
+.secondary-button,
+.ghost-button {
+ cursor: pointer;
+ border-radius: 0.75rem;
+ padding: 0.75rem 1.5rem;
+ font-size: 1rem;
+ border: none;
+ transition: transform 0.2s ease, box-shadow 0.2s ease, background 0.2s ease;
+}
+
+.primary-button {
+ background: var(--accent);
+ color: #fff;
+ box-shadow: var(--shadow);
+}
+
+.primary-button:hover,
+.primary-button:focus {
+ transform: translateY(-1px);
+}
+
+.controls button[disabled] {
+ opacity: 0.5;
+ cursor: not-allowed;
+ transform: none;
+ box-shadow: none;
+}
+
+.secondary-button {
+ background: var(--accent-soft);
+ color: var(--accent);
+}
+
+.ghost-button {
+ background: transparent;
+ border: 1px solid var(--border);
+ color: var(--text);
+}
+
+.controls {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.75rem;
+ align-items: center;
+}
+
+.controls .spacer {
+ flex: 1 1 auto;
+}
+
+.question-meta {
+ display: flex;
+ flex-direction: column;
+ gap: 0.5rem;
+}
+
+.session-badge {
+ align-self: flex-start;
+ padding: 0.35rem 0.75rem;
+ border-radius: 999px;
+ background: var(--accent-soft);
+ color: var(--accent);
+ font-size: 0.85rem;
+ font-weight: 600;
+}
+
+.session-subtext {
+ color: var(--subtext);
+ font-size: 0.9rem;
+}
+
+.question-info {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem;
+ font-size: 1rem;
+ align-items: baseline;
+}
+
+.question-info span {
+ color: var(--subtext);
+}
+
+.question-progress {
+ color: var(--subtext);
+ font-size: 0.95rem;
+}
+
+.question-title {
+ font-size: clamp(1.1rem, 1vw + 1rem, 1.4rem);
+ line-height: 1.7;
+ white-space: pre-wrap;
+}
+
+.question-image {
+ width: 100%;
+ max-height: 360px;
+ object-fit: contain;
+ border-radius: 0.75rem;
+ border: 1px solid var(--border);
+ background: #fff;
+}
+
+.options {
+ display: grid;
+ gap: 0.75rem;
+}
+
+.option-button {
+ text-align: left;
+ display: flex;
+ gap: 0.75rem;
+ align-items: flex-start;
+ border-radius: 1rem;
+ padding: 1rem;
+ border: 1px solid var(--border);
+ background: transparent;
+ color: inherit;
+ cursor: pointer;
+ transition: transform 0.15s ease, border 0.15s ease, box-shadow 0.15s ease, background 0.15s ease;
+}
+
+.option-button strong {
+ font-size: 1.1rem;
+ min-width: 2rem;
+}
+
+.option-button span {
+ flex: 1;
+}
+
+.option-button:hover,
+.option-button:focus {
+ transform: translateY(-1px);
+ box-shadow: var(--shadow);
+ outline: none;
+}
+
+.option-button.selected {
+ border-color: var(--accent);
+ background: var(--accent-soft);
+}
+
+.option-button.correct {
+ border-color: var(--success);
+ background: rgba(39, 174, 96, 0.12);
+}
+
+.option-button.incorrect {
+ border-color: var(--danger);
+ background: rgba(231, 76, 60, 0.12);
+}
+
+.feedback {
+ border-radius: 1rem;
+ padding: 1rem;
+ background: var(--accent-soft);
+ color: var(--accent);
+}
+
+.feedback.correct {
+ background: rgba(39, 174, 96, 0.12);
+ color: var(--success);
+}
+
+.feedback.incorrect {
+ background: rgba(231, 76, 60, 0.12);
+ color: var(--danger);
+}
+
+.explanation {
+ background: var(--card-bg);
+ border-radius: 1rem;
+ border: 1px solid var(--border);
+ padding: 1rem;
+ line-height: 1.6;
+}
+
+.ps-note {
+ background: var(--accent-soft);
+ color: var(--accent);
+ border-radius: 0.75rem;
+ padding: 0.75rem;
+}
+
+.status-panel {
+ position: fixed;
+ inset: 0;
+ background: rgba(15, 23, 42, 0.45);
+ display: flex;
+ justify-content: center;
+ align-items: flex-end;
+ padding: 1.5rem;
+ transition: opacity 0.2s ease;
+}
+
+.status-panel.hidden {
+ opacity: 0;
+ pointer-events: none;
+}
+
+.status-panel__content {
+ width: min(720px, 100%);
+ background: var(--card-bg);
+ border-radius: 1.25rem;
+ padding: 1.5rem;
+ border: 1px solid var(--border);
+ box-shadow: var(--shadow);
+ max-height: 85vh;
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.status-panel__header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.status-panel__legend {
+ display: flex;
+ gap: 1rem;
+ flex-wrap: wrap;
+ font-size: 0.9rem;
+}
+
+.legend-item {
+ padding: 0.25rem 0.75rem;
+ border-radius: 999px;
+ border: 1px solid var(--border);
+}
+
+.legend-answered {
+ background: rgba(39, 174, 96, 0.12);
+ color: var(--success);
+}
+
+.legend-unanswered {
+ background: rgba(231, 76, 60, 0.12);
+ color: var(--danger);
+}
+
+.legend-bookmarked {
+ background: var(--accent-soft);
+ color: var(--accent);
+}
+
+.status-panel__list {
+ display: grid;
+ gap: 0.75rem;
+ grid-template-columns: repeat(auto-fit, minmax(80px, 1fr));
+ overflow-y: auto;
+ padding-right: 0.25rem;
+}
+
+.status-item {
+ border-radius: 0.75rem;
+ border: 1px solid var(--border);
+ padding: 0.75rem;
+ display: flex;
+ flex-direction: column;
+ gap: 0.25rem;
+ cursor: pointer;
+ transition: transform 0.15s ease, box-shadow 0.15s ease, border 0.15s ease;
+ text-align: center;
+}
+
+.status-item:hover {
+ transform: translateY(-2px);
+ box-shadow: var(--shadow);
+}
+
+.status-item.current {
+ border-color: var(--accent);
+ box-shadow: var(--shadow);
+}
+
+.status-item.answered {
+ background: rgba(39, 174, 96, 0.12);
+ border-color: var(--success);
+}
+
+.status-item.bookmarked {
+ background: var(--accent-soft);
+ border-color: var(--accent);
+}
+
+.status-item .status-label {
+ font-size: 0.9rem;
+ color: var(--subtext);
+}
+
+.jump-input {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+}
+
+.jump-input input {
+ width: 80px;
+ padding: 0.5rem 0.75rem;
+ border-radius: 0.75rem;
+ border: 1px solid var(--border);
+ background: transparent;
+ color: inherit;
+}
+
+.jump-input input:focus {
+ outline: 2px solid var(--accent);
+}
+
+.result-overlay {
+ position: fixed;
+ inset: 0;
+ background: rgba(15, 23, 42, 0.6);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 1.5rem;
+}
+
+.result-overlay.hidden {
+ opacity: 0;
+ pointer-events: none;
+}
+
+.result-card {
+ background: var(--card-bg);
+ border-radius: 1.5rem;
+ padding: 2rem;
+ width: min(520px, 100%);
+ border: 1px solid var(--border);
+ box-shadow: var(--shadow);
+ display: flex;
+ flex-direction: column;
+ gap: 1.5rem;
+}
+
+.result-summary {
+ display: grid;
+ gap: 1rem;
+ grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
+}
+
+.result-box {
+ border-radius: 1rem;
+ padding: 1rem;
+ background: var(--accent-soft);
+ color: var(--accent);
+ text-align: center;
+}
+
+.result-box strong {
+ display: block;
+ font-size: 1.5rem;
+ margin-bottom: 0.25rem;
+}
+
+@media (max-width: 768px) {
+ .card {
+ padding: 1.25rem;
+ border-radius: 1rem;
+ }
+
+ .controls {
+ flex-direction: column;
+ align-items: stretch;
+ }
+
+ .jump-input {
+ width: 100%;
+ }
+
+ .jump-input input {
+ flex: 1;
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ *, *::before, *::after {
+ animation-duration: 0.001ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.001ms !important;
+ scroll-behavior: auto !important;
+ }
+}