diff --git a/app.js b/app.js
new file mode 100644
index 0000000..0d73a11
--- /dev/null
+++ b/app.js
@@ -0,0 +1,475 @@
+const state = {
+ allQuestions: [],
+ mode: null,
+ questionSet: [],
+ answers: [],
+ bookmarks: new Set(),
+ currentIndex: 0,
+ revealAnswers: false,
+ finished: false,
+ showImmediate: false
+};
+
+const elements = {
+ questionCount: document.getElementById('questionCount'),
+ menuView: document.getElementById('menuView'),
+ questionView: document.getElementById('questionView'),
+ resultView: document.getElementById('resultView'),
+ progressBar: document.getElementById('progressBar'),
+ modeLabel: document.getElementById('modeLabel'),
+ questionPosition: document.getElementById('questionPosition'),
+ originalIndex: document.getElementById('originalIndex'),
+ questionText: document.getElementById('questionText'),
+ questionImageContainer: document.getElementById('questionImageContainer'),
+ optionsList: document.getElementById('optionsList'),
+ feedbackPanel: document.getElementById('feedbackPanel'),
+ feedbackMessage: document.getElementById('feedbackMessage'),
+ explanation: document.getElementById('explanation'),
+ psInfo: document.getElementById('psInfo'),
+ prevQuestion: document.getElementById('prevQuestion'),
+ nextQuestion: document.getElementById('nextQuestion'),
+ finishTest: document.getElementById('finishTest'),
+ jumpForm: document.getElementById('jumpForm'),
+ jumpInput: document.getElementById('jumpInput'),
+ jumpTotal: document.getElementById('jumpTotal'),
+ statusToggle: document.getElementById('statusToggle'),
+ bookmarkToggle: document.getElementById('bookmarkToggle'),
+ themeToggle: document.getElementById('themeToggle'),
+ backToMenu: document.getElementById('backToMenu'),
+ statusPanel: document.getElementById('statusPanel'),
+ statusClose: document.getElementById('statusClose'),
+ statusList: document.getElementById('statusList'),
+ rangeModal: document.getElementById('rangeModal'),
+ rangeClose: document.getElementById('rangeClose'),
+ rangeCancel: document.getElementById('rangeCancel'),
+ rangeForm: document.getElementById('rangeForm'),
+ rangeStart: document.getElementById('rangeStart'),
+ rangeEnd: document.getElementById('rangeEnd'),
+ resultScore: document.getElementById('resultScore'),
+ resultCorrect: document.getElementById('resultCorrect'),
+ resultIncorrect: document.getElementById('resultIncorrect'),
+ resultUnanswered: document.getElementById('resultUnanswered'),
+ reviewButton: document.getElementById('reviewButton'),
+ resultBackToMenu: document.getElementById('resultBackToMenu')
+};
+
+const statusItemTemplate = document.getElementById('statusItemTemplate');
+
+async function init() {
+ try {
+ const response = await fetch('question.json');
+ if (!response.ok) throw new Error('無法載入題庫');
+ const data = await response.json();
+ state.allQuestions = data.map((question, index) => ({
+ ...question,
+ originalIndex: index
+ }));
+ elements.questionCount.textContent = `題庫共 ${state.allQuestions.length} 題`;
+ } catch (error) {
+ elements.menuView.innerHTML = `
載入題庫發生錯誤:${error.message}
`;
+ }
+}
+
+function setView(view) {
+ elements.menuView.classList.toggle('hidden', view !== 'menu');
+ elements.questionView.classList.toggle('hidden', view !== 'question');
+ elements.resultView.classList.toggle('hidden', view !== 'result');
+ elements.backToMenu.classList.toggle('hidden', view === 'menu');
+}
+
+function resetState() {
+ state.mode = null;
+ state.questionSet = [];
+ state.answers = [];
+ state.bookmarks.clear();
+ state.currentIndex = 0;
+ state.revealAnswers = false;
+ state.finished = false;
+ state.showImmediate = false;
+}
+
+function startPracticeMode() {
+ if (!state.allQuestions.length) return;
+ resetState();
+ state.mode = 'practice';
+ state.questionSet = state.allQuestions.map((q) => ({ ...q }));
+ state.answers = Array(state.questionSet.length).fill(null);
+ state.showImmediate = true;
+ setupQuestionView();
+}
+
+function startExamMode(limitRange = null) {
+ if (!state.allQuestions.length) return;
+ resetState();
+ if (limitRange) {
+ const [start, end] = limitRange;
+ const available = state.allQuestions.filter((q) => {
+ const index = q.originalIndex + 1;
+ return index >= start && index <= end;
+ });
+ state.questionSet = sampleQuestions(available, 50);
+ } else {
+ state.questionSet = sampleQuestions(state.allQuestions, 50);
+ }
+ if (!state.questionSet.length) {
+ alert('選定範圍內沒有題目,請重新選擇。');
+ return;
+ }
+ state.answers = Array(state.questionSet.length).fill(null);
+ state.mode = limitRange ? 'range' : 'exam';
+ setupQuestionView();
+}
+
+function sampleQuestions(source, amount) {
+ const copy = [...source];
+ const result = [];
+ const max = Math.min(amount, copy.length);
+ for (let i = copy.length - 1; i > 0; i--) {
+ const j = Math.floor(Math.random() * (i + 1));
+ [copy[i], copy[j]] = [copy[j], copy[i]];
+ }
+ for (let k = 0; k < max; k++) {
+ result.push({ ...copy[k] });
+ }
+ return result;
+}
+
+function setupQuestionView() {
+ elements.modeLabel.textContent = getModeLabel();
+ elements.jumpTotal.textContent = state.questionSet.length;
+ elements.jumpInput.setAttribute('max', state.questionSet.length);
+ updateQuestionDisplay();
+ updateProgress();
+ updateBookmarkButton();
+ updateFinishButton();
+ setView('question');
+}
+
+function getModeLabel() {
+ switch (state.mode) {
+ case 'practice':
+ return '自由參考模式';
+ case 'range':
+ return '自訂範圍測驗';
+ case 'exam':
+ return '模擬考試模式';
+ default:
+ return '';
+ }
+}
+
+function updateQuestionDisplay() {
+ const question = state.questionSet[state.currentIndex];
+ if (!question) return;
+
+ elements.questionPosition.textContent = `第 ${state.currentIndex + 1} / ${state.questionSet.length} 題`;
+ elements.originalIndex.textContent = `原題號:${question.originalIndex + 1}`;
+ elements.questionText.textContent = question.question;
+
+ elements.questionImageContainer.innerHTML = '';
+ if (question.questionimage) {
+ const img = document.createElement('img');
+ img.src = `image/${question.questionimage}`;
+ img.alt = '題目圖片';
+ elements.questionImageContainer.appendChild(img);
+ }
+
+ elements.optionsList.innerHTML = '';
+ question.option.forEach((text, optionIndex) => {
+ const li = document.createElement('li');
+ li.className = 'option';
+ li.setAttribute('tabindex', '0');
+ li.dataset.index = optionIndex;
+ const optionContent = `${text}${question.optionend ?? ''}`;
+ li.textContent = optionContent;
+
+ const selected = state.answers[state.currentIndex];
+ if (selected === optionIndex) {
+ li.classList.add('selected');
+ }
+
+ if (shouldRevealAnswers()) {
+ if (optionIndex === question.answer) {
+ li.classList.add('correct');
+ } else if (selected === optionIndex && selected !== question.answer) {
+ li.classList.add('incorrect');
+ }
+ }
+
+ li.addEventListener('click', () => selectOption(optionIndex));
+ li.addEventListener('keypress', (event) => {
+ if (event.key === 'Enter' || event.key === ' ') {
+ event.preventDefault();
+ selectOption(optionIndex);
+ }
+ });
+ elements.optionsList.appendChild(li);
+ });
+
+ updateFeedback(question);
+ updateNavigatorButtons();
+ updateStatusPanel();
+}
+
+function selectOption(optionIndex) {
+ if (state.finished && !state.showImmediate) {
+ return;
+ }
+ state.answers[state.currentIndex] = optionIndex;
+ updateProgress();
+ updateQuestionDisplay();
+}
+
+function shouldRevealAnswers(index = state.currentIndex) {
+ if (state.showImmediate) {
+ return state.answers[index] !== null;
+ }
+ return state.revealAnswers;
+}
+
+function updateFeedback(question) {
+ const selected = state.answers[state.currentIndex];
+ const show = shouldRevealAnswers();
+ if (show && selected !== null) {
+ elements.feedbackPanel.classList.remove('hidden');
+ const correct = selected === question.answer;
+ elements.feedbackMessage.textContent = correct ? '答對了!' : '答錯了。';
+ elements.feedbackMessage.style.color = correct ? '#22c55e' : '#ef4444';
+ elements.explanation.textContent = question.explain ? `解析:${question.explain}` : '此題沒有提供解析。';
+ elements.psInfo.textContent = question.ps ? `備註:${question.ps}` : '';
+ } else if (show && state.finished && selected === null) {
+ elements.feedbackPanel.classList.remove('hidden');
+ elements.feedbackMessage.textContent = '此題尚未作答。';
+ elements.feedbackMessage.style.color = '#f59e0b';
+ elements.explanation.textContent = question.explain ? `解析:${question.explain}` : '此題沒有提供解析。';
+ elements.psInfo.textContent = question.ps ? `備註:${question.ps}` : '';
+ } else if (show) {
+ elements.feedbackPanel.classList.remove('hidden');
+ elements.feedbackMessage.textContent = '請先作答以查看解析。';
+ elements.feedbackMessage.style.color = '#f97316';
+ elements.explanation.textContent = '';
+ elements.psInfo.textContent = '';
+ } else {
+ elements.feedbackPanel.classList.add('hidden');
+ elements.feedbackMessage.textContent = '';
+ elements.explanation.textContent = '';
+ elements.psInfo.textContent = '';
+ }
+}
+
+function updateNavigatorButtons() {
+ elements.prevQuestion.disabled = state.currentIndex === 0;
+ elements.nextQuestion.disabled = state.currentIndex === state.questionSet.length - 1;
+}
+
+function updateProgress() {
+ const answeredCount = state.answers.filter((ans) => ans !== null).length;
+ const total = state.questionSet.length || 1;
+ const progress = Math.round((answeredCount / total) * 100);
+ elements.progressBar.style.width = `${progress}%`;
+}
+
+function goToQuestion(index) {
+ if (index < 0 || index >= state.questionSet.length) return;
+ state.currentIndex = index;
+ updateQuestionDisplay();
+ updateBookmarkButton();
+}
+
+function updateBookmarkButton() {
+ if (state.bookmarks.has(state.currentIndex)) {
+ elements.bookmarkToggle.textContent = '移除書籤';
+ } else {
+ elements.bookmarkToggle.textContent = '加入書籤';
+ }
+}
+
+function toggleBookmark() {
+ if (state.bookmarks.has(state.currentIndex)) {
+ state.bookmarks.delete(state.currentIndex);
+ } else {
+ state.bookmarks.add(state.currentIndex);
+ }
+ updateBookmarkButton();
+ updateStatusPanel();
+}
+
+function updateStatusPanel() {
+ if (elements.statusPanel.classList.contains('hidden')) return;
+ renderStatusList();
+}
+
+function renderStatusList() {
+ elements.statusList.innerHTML = '';
+ state.questionSet.forEach((question, index) => {
+ const item = statusItemTemplate.content.firstElementChild.cloneNode(true);
+ item.querySelector('.status-number').textContent = index + 1;
+ const icons = [];
+ if (state.answers[index] !== null) icons.push('✔');
+ if (state.bookmarks.has(index)) icons.push('★');
+ item.querySelector('.status-icons').textContent = icons.join(' ');
+ item.classList.add(state.answers[index] !== null ? 'answered' : 'unanswered');
+ if (state.bookmarks.has(index)) item.classList.add('bookmarked');
+ if (index === state.currentIndex) item.classList.add('current');
+ item.addEventListener('click', () => {
+ goToQuestion(index);
+ closeStatusPanel();
+ });
+ elements.statusList.appendChild(item);
+ });
+}
+
+function openStatusPanel() {
+ renderStatusList();
+ elements.statusPanel.classList.remove('hidden');
+}
+
+function closeStatusPanel() {
+ elements.statusPanel.classList.add('hidden');
+}
+
+function updateFinishButton() {
+ const shouldShow = state.mode === 'exam' || state.mode === 'range';
+ elements.finishTest.classList.toggle('hidden', !shouldShow || state.finished);
+}
+
+function finishTest() {
+ if (state.finished) return;
+ state.finished = true;
+ state.revealAnswers = true;
+ const { correct, incorrect, unanswered } = calculateResult();
+ const score = Math.round((correct / state.questionSet.length) * 100);
+ elements.resultScore.textContent = `${score} 分`;
+ elements.resultCorrect.textContent = `${correct}`;
+ elements.resultIncorrect.textContent = `${incorrect}`;
+ elements.resultUnanswered.textContent = `${unanswered}`;
+ updateFinishButton();
+ setView('result');
+}
+
+function calculateResult() {
+ let correct = 0;
+ let incorrect = 0;
+ let unanswered = 0;
+ state.questionSet.forEach((question, index) => {
+ const answer = state.answers[index];
+ if (answer === null) {
+ unanswered += 1;
+ } else if (answer === question.answer) {
+ correct += 1;
+ } else {
+ incorrect += 1;
+ }
+ });
+ return { correct, incorrect, unanswered };
+}
+
+function reviewQuestions() {
+ setView('question');
+ updateQuestionDisplay();
+ updateBookmarkButton();
+ updateProgress();
+}
+
+function handleThemeToggle() {
+ const body = document.body;
+ const current = body.getAttribute('data-theme');
+ const next = current === 'light' ? 'dark' : 'light';
+ body.setAttribute('data-theme', next);
+ elements.themeToggle.textContent = next === 'light' ? '🌞 亮色模式' : '🌙 暗色模式';
+}
+
+function returnToMenu() {
+ resetState();
+ closeStatusPanel();
+ elements.rangeModal.classList.add('hidden');
+ setView('menu');
+}
+
+function handleJump(event) {
+ event.preventDefault();
+ const value = Number(elements.jumpInput.value);
+ if (!Number.isFinite(value)) return;
+ goToQuestion(value - 1);
+ elements.jumpInput.value = '';
+}
+
+document.getElementById('practiceMode').addEventListener('click', startPracticeMode);
+document.getElementById('rangeMode').addEventListener('click', () => {
+ elements.rangeModal.classList.remove('hidden');
+});
+document.getElementById('examMode').addEventListener('click', () => startExamMode());
+
+elements.prevQuestion.addEventListener('click', () => goToQuestion(state.currentIndex - 1));
+elements.nextQuestion.addEventListener('click', () => goToQuestion(state.currentIndex + 1));
+elements.finishTest.addEventListener('click', finishTest);
+
+elements.jumpForm.addEventListener('submit', handleJump);
+
+elements.statusToggle.addEventListener('click', openStatusPanel);
+elements.statusClose.addEventListener('click', closeStatusPanel);
+
+elements.statusPanel.addEventListener('click', (event) => {
+ if (event.target === elements.statusPanel) {
+ closeStatusPanel();
+ }
+});
+
+elements.bookmarkToggle.addEventListener('click', toggleBookmark);
+
+elements.themeToggle.addEventListener('click', handleThemeToggle);
+
+elements.backToMenu.addEventListener('click', () => {
+ if (state.mode === 'exam' || state.mode === 'range') {
+ const confirmed = confirm('離開將結束此次測驗,是否確定?');
+ if (!confirmed) return;
+ }
+ returnToMenu();
+});
+
+// Range modal controls
+elements.rangeClose.addEventListener('click', () => elements.rangeModal.classList.add('hidden'));
+elements.rangeCancel.addEventListener('click', () => elements.rangeModal.classList.add('hidden'));
+elements.rangeModal.addEventListener('click', (event) => {
+ if (event.target === elements.rangeModal) {
+ elements.rangeModal.classList.add('hidden');
+ }
+});
+
+elements.rangeForm.addEventListener('submit', (event) => {
+ event.preventDefault();
+ const start = Number(elements.rangeStart.value);
+ const end = Number(elements.rangeEnd.value);
+ if (!Number.isFinite(start) || !Number.isFinite(end) || start <= 0 || end <= 0) {
+ alert('請輸入有效的題號範圍。');
+ return;
+ }
+ if (start > end) {
+ alert('起始題號不可大於結束題號。');
+ return;
+ }
+ elements.rangeModal.classList.add('hidden');
+ startExamMode([start, end]);
+ elements.rangeStart.value = '';
+ elements.rangeEnd.value = '';
+});
+
+elements.reviewButton.addEventListener('click', reviewQuestions);
+
+elements.resultBackToMenu.addEventListener('click', returnToMenu);
+
+document.addEventListener('keydown', (event) => {
+ if (event.key === 'Escape') {
+ closeStatusPanel();
+ elements.rangeModal.classList.add('hidden');
+ }
+});
+
+window.addEventListener('beforeunload', (event) => {
+ if (!state.finished && (state.mode === 'exam' || state.mode === 'range')) {
+ event.preventDefault();
+ event.returnValue = '';
+ }
+});
+
+init();
diff --git a/index.html b/index.html
new file mode 100644
index 0000000..ddee65d
--- /dev/null
+++ b/index.html
@@ -0,0 +1,142 @@
+
+
+
+
+
+ 學科刷題系統
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 測驗成績
+
+
0得分
+
0答對
+
0答錯
+
0未答
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/styles.css b/styles.css
new file mode 100644
index 0000000..657b3c5
--- /dev/null
+++ b/styles.css
@@ -0,0 +1,547 @@
+:root {
+ --background: #f5f7fb;
+ --card-background: #ffffff;
+ --surface: #e8ecf5;
+ --text-primary: #1f2937;
+ --text-secondary: #4b5563;
+ --accent: #2563eb;
+ --accent-hover: #1d4ed8;
+ --border: #d1d5db;
+ --shadow: 0 18px 40px rgba(15, 23, 42, 0.12);
+}
+
+body[data-theme="dark"] {
+ --background: #0f172a;
+ --card-background: #1e293b;
+ --surface: #1e293b;
+ --text-primary: #f8fafc;
+ --text-secondary: #cbd5f5;
+ --accent: #60a5fa;
+ --accent-hover: #3b82f6;
+ --border: #334155;
+ --shadow: 0 20px 45px rgba(15, 23, 42, 0.4);
+}
+
+* {
+ box-sizing: border-box;
+}
+
+body {
+ margin: 0;
+ min-height: 100vh;
+ background: var(--background);
+ font-family: 'Noto Sans TC', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
+ color: var(--text-primary);
+ display: flex;
+ flex-direction: column;
+}
+
+.app-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 1.25rem clamp(1rem, 4vw, 2.5rem);
+ background: transparent;
+}
+
+.header-left {
+ display: flex;
+ align-items: center;
+ gap: 1rem;
+}
+
+.app-title {
+ margin: 0;
+ font-size: clamp(1.25rem, 3vw, 1.75rem);
+ font-weight: 700;
+}
+
+.question-count {
+ font-size: 0.95rem;
+ color: var(--text-secondary);
+}
+
+.header-actions {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+}
+
+button {
+ font-family: inherit;
+ border: none;
+ border-radius: 999px;
+ cursor: pointer;
+ font-weight: 600;
+ transition: transform 0.15s ease, box-shadow 0.15s ease, background-color 0.2s ease;
+}
+
+button:focus-visible {
+ outline: 3px solid var(--accent);
+ outline-offset: 2px;
+}
+
+button.primary {
+ padding: 0.65rem 1.5rem;
+ background: var(--accent);
+ color: #fff;
+ box-shadow: 0 6px 16px rgba(37, 99, 235, 0.25);
+}
+
+button.primary:hover {
+ background: var(--accent-hover);
+ transform: translateY(-1px);
+}
+
+button.secondary {
+ padding: 0.55rem 1.25rem;
+ background: var(--surface);
+ color: var(--text-primary);
+ border: 1px solid var(--border);
+}
+
+button.secondary:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 4px 10px rgba(15, 23, 42, 0.15);
+}
+
+button.icon-button {
+ background: transparent;
+ border: none;
+ color: var(--text-primary);
+ padding: 0.25rem;
+ font-size: 1.2rem;
+}
+
+button.icon-button:hover {
+ color: var(--accent);
+}
+
+.app-main {
+ flex: 1;
+ width: min(1100px, 95vw);
+ margin: 0 auto clamp(1rem, 3vw, 2.5rem);
+ display: flex;
+ justify-content: center;
+ align-items: flex-start;
+}
+
+.card {
+ background: var(--card-background);
+ border-radius: 24px;
+ padding: clamp(1rem, 5vw, 2.5rem);
+ width: 100%;
+ box-shadow: var(--shadow);
+ transition: background-color 0.3s ease, color 0.3s ease;
+}
+
+.hidden {
+ display: none !important;
+}
+
+.menu-actions {
+ display: grid;
+ gap: 1rem;
+ margin: 1.5rem 0;
+}
+
+.menu-subtitle {
+ color: var(--text-secondary);
+}
+
+.menu-info ul {
+ padding-left: 1rem;
+ color: var(--text-secondary);
+ line-height: 1.7;
+}
+
+.progress-wrapper {
+ width: 100%;
+ height: 8px;
+ background: var(--surface);
+ border-radius: 999px;
+ overflow: hidden;
+ margin-bottom: 1.5rem;
+}
+
+.progress-bar {
+ height: 100%;
+ width: 0%;
+ background: linear-gradient(90deg, var(--accent), var(--accent-hover));
+ transition: width 0.25s ease;
+}
+
+.question-header {
+ display: flex;
+ justify-content: space-between;
+ gap: 1rem;
+ flex-wrap: wrap;
+ align-items: center;
+ margin-bottom: 1rem;
+}
+
+.question-meta {
+ display: flex;
+ flex-wrap: wrap;
+ gap: 0.5rem 1rem;
+ align-items: center;
+}
+
+.mode-label {
+ padding: 0.25rem 0.75rem;
+ border-radius: 999px;
+ background: rgba(37, 99, 235, 0.12);
+ color: var(--accent);
+ font-weight: 600;
+}
+
+.question-position,
+.question-original {
+ color: var(--text-secondary);
+ font-size: 0.95rem;
+}
+
+.question-text {
+ margin-top: 0;
+ font-size: clamp(1.1rem, 3vw, 1.35rem);
+ line-height: 1.6;
+}
+
+.question-image img {
+ max-width: min(100%, 480px);
+ width: 100%;
+ border-radius: 16px;
+ margin: 1rem 0;
+ box-shadow: 0 8px 24px rgba(15, 23, 42, 0.25);
+}
+
+.options {
+ list-style: none;
+ margin: 0;
+ padding: 0;
+ display: grid;
+ gap: 0.75rem;
+}
+
+.option {
+ padding: 0.85rem 1rem;
+ border-radius: 18px;
+ border: 1px solid var(--border);
+ background: var(--surface);
+ color: var(--text-primary);
+ cursor: pointer;
+ transition: border 0.2s ease, background-color 0.2s ease, transform 0.15s ease;
+}
+
+.option:hover {
+ transform: translateY(-1px);
+ border-color: var(--accent);
+}
+
+.option.selected {
+ border-color: var(--accent);
+ background: rgba(37, 99, 235, 0.16);
+}
+
+.option.correct {
+ border-color: #22c55e;
+ background: rgba(34, 197, 94, 0.18);
+}
+
+.option.incorrect {
+ border-color: #ef4444;
+ background: rgba(239, 68, 68, 0.18);
+}
+
+.feedback {
+ margin-top: 1.5rem;
+ border-radius: 16px;
+ padding: 1rem 1.25rem;
+ border: 1px solid var(--border);
+ background: var(--surface);
+}
+
+.feedback p {
+ margin: 0 0 0.75rem;
+ font-weight: 600;
+}
+
+.explanation,
+.ps {
+ color: var(--text-secondary);
+ line-height: 1.6;
+ white-space: pre-line;
+}
+
+.question-footer {
+ margin-top: 2rem;
+ display: flex;
+ flex-wrap: wrap;
+ gap: 1rem;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.navigator {
+ display: flex;
+ gap: 0.75rem;
+}
+
+.jump-form {
+ display: flex;
+ align-items: center;
+ gap: 0.5rem;
+ flex-wrap: wrap;
+ color: var(--text-secondary);
+}
+
+.jump-form input {
+ width: 4rem;
+ padding: 0.4rem;
+ border-radius: 12px;
+ border: 1px solid var(--border);
+ background: var(--card-background);
+ color: var(--text-primary);
+}
+
+.status-panel {
+ position: fixed;
+ inset: 0;
+ background: rgba(15, 23, 42, 0.45);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 1.5rem;
+ z-index: 20;
+}
+
+.status-panel-content {
+ background: var(--card-background);
+ padding: 1.5rem;
+ border-radius: 20px;
+ width: min(90vw, 520px);
+ box-shadow: var(--shadow);
+ display: flex;
+ flex-direction: column;
+ gap: 1rem;
+}
+
+.status-panel-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+}
+
+.status-list {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(64px, 1fr));
+ gap: 0.75rem;
+ max-height: 60vh;
+ overflow-y: auto;
+ padding-right: 0.25rem;
+}
+
+.status-item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 0.25rem;
+ padding: 0.75rem 0.5rem;
+ border-radius: 16px;
+ background: var(--surface);
+ border: 1px solid transparent;
+ color: var(--text-secondary);
+ font-weight: 600;
+}
+
+.status-item.current {
+ box-shadow: 0 0 0 2px rgba(37, 99, 235, 0.35);
+ border-color: var(--accent);
+ color: var(--text-primary);
+}
+
+.status-item.answered {
+ border-color: rgba(34, 197, 94, 0.6);
+ color: var(--text-primary);
+}
+
+.status-item.unanswered {
+ border-color: rgba(148, 163, 184, 0.6);
+}
+
+.status-item.bookmarked {
+ box-shadow: 0 0 0 2px rgba(234, 179, 8, 0.3);
+}
+
+.status-number {
+ font-size: 1rem;
+}
+
+.status-icons {
+ font-size: 0.85rem;
+ color: var(--text-secondary);
+}
+
+.status-legend {
+ display: flex;
+ gap: 1rem;
+ color: var(--text-secondary);
+ font-size: 0.9rem;
+}
+
+.status-legend .legend::before {
+ content: '';
+ display: inline-block;
+ width: 10px;
+ height: 10px;
+ border-radius: 50%;
+ margin-right: 0.35rem;
+}
+
+.status-legend .answered::before {
+ background: #22c55e;
+}
+
+.status-legend .unanswered::before {
+ background: #94a3b8;
+}
+
+.status-legend .bookmarked::before {
+ background: #facc15;
+}
+
+.modal {
+ position: fixed;
+ inset: 0;
+ background: rgba(15, 23, 42, 0.45);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 30;
+ padding: 1.5rem;
+}
+
+.modal-content {
+ width: min(92vw, 420px);
+ background: var(--card-background);
+ border-radius: 20px;
+ box-shadow: var(--shadow);
+ display: flex;
+ flex-direction: column;
+}
+
+.modal-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 1.25rem 1.5rem 0.75rem;
+}
+
+.modal-body {
+ display: flex;
+ flex-direction: column;
+ gap: 0.85rem;
+ padding: 0 1.5rem 1.5rem;
+}
+
+.field {
+ display: flex;
+ flex-direction: column;
+ gap: 0.4rem;
+ color: var(--text-secondary);
+}
+
+.field input {
+ padding: 0.6rem 0.75rem;
+ border-radius: 12px;
+ border: 1px solid var(--border);
+ background: var(--surface);
+ color: var(--text-primary);
+}
+
+.modal-tip {
+ color: var(--text-secondary);
+ font-size: 0.9rem;
+ margin-top: 0.5rem;
+}
+
+.modal-actions {
+ display: flex;
+ justify-content: flex-end;
+ gap: 0.75rem;
+}
+
+.error {
+ color: #ef4444;
+ background: rgba(239, 68, 68, 0.1);
+ border: 1px solid rgba(239, 68, 68, 0.35);
+ padding: 1rem 1.25rem;
+ border-radius: 16px;
+ line-height: 1.6;
+ text-align: center;
+}
+
+.result-summary {
+ display: grid;
+ grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
+ gap: 1rem;
+ margin: 1.5rem 0;
+}
+
+.result-number {
+ display: block;
+ font-size: clamp(1.8rem, 5vw, 2.6rem);
+ font-weight: 700;
+ color: var(--accent);
+}
+
+.result-label {
+ color: var(--text-secondary);
+}
+
+@media (max-width: 768px) {
+ .app-header {
+ flex-direction: column;
+ align-items: flex-start;
+ gap: 0.75rem;
+ }
+
+ .header-actions {
+ align-self: stretch;
+ justify-content: space-between;
+ }
+
+ .question-footer {
+ flex-direction: column;
+ align-items: stretch;
+ }
+
+ .navigator {
+ justify-content: space-between;
+ }
+
+ .jump-form {
+ width: 100%;
+ justify-content: space-between;
+ }
+}
+
+@media (max-width: 480px) {
+ button.primary,
+ button.secondary {
+ width: 100%;
+ }
+
+ .options {
+ gap: 0.6rem;
+ }
+
+ .option {
+ font-size: 0.95rem;
+ }
+
+ .status-list {
+ grid-template-columns: repeat(auto-fill, minmax(56px, 1fr));
+ }
+}