PTA Timer

PTA平台题目计时和倒计时工具 - GreasyFork/Tampermonkey 版

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

ستحتاج إلى تثبيت إضافة مثل Stylus لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتتمكن من تثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

ستحتاج إلى تثبيت إضافة لإدارة أنماط المستخدم لتثبيت هذا النمط.

(لدي بالفعل مثبت أنماط للمستخدم، دعني أقم بتثبيته!)

// ==UserScript==
// @name         PTA Timer
// @namespace    https://github.com/Withnoidea/PATTimer
// @version      2.0.0
// @description  PTA平台题目计时和倒计时工具 - GreasyFork/Tampermonkey 版
// @author       Withnoidea
// @license      MIT
// @match        https://pintia.cn/*
// @icon         https://cdn.jsdelivr.net/gh/Withnoidea/images/icon.png
// @icon16       https://cdn.jsdelivr.net/gh/Withnoidea/images/icon16.png
// @icon48       https://cdn.jsdelivr.net/gh/Withnoidea/images/icon48.png
// @icon128      https://cdn.jsdelivr.net/gh/Withnoidea/images/icon128.png
// @grant        GM_addStyle
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_deleteValue
// @grant        GM_listValues
// @grant        GM_notification
// @grant        GM_registerMenuCommand
// @run-at       document-idle
// ==/UserScript==

(function () {
  'use strict';

  const ICONS = {
    main: 'https://cdn.jsdelivr.net/gh/Withnoidea/images/icon.png',
    icon16: 'https://cdn.jsdelivr.net/gh/Withnoidea/images/icon16.png',
    icon48: 'https://cdn.jsdelivr.net/gh/Withnoidea/images/icon48.png',
    icon128: 'https://cdn.jsdelivr.net/gh/Withnoidea/images/icon128.png'
  };

  const CONTENT_CSS = "/* ============================================\n   PTA Timer - Content Script Styles\n   Ultra-Compact Floating Timer Widget\n   Focus Precision Design System\n   ============================================ */\n\n@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@500;600&display=swap');\n\n/* Timer Widget Container */\n#pta-timer-display {\n    position: fixed;\n    top: 16px;\n    right: 16px;\n    width: auto;\n    min-width: 340px;\n    height: 44px;\n    background: rgba(255, 255, 255, 0.85);\n    backdrop-filter: blur(20px);\n    -webkit-backdrop-filter: blur(20px);\n    border: 1px solid rgba(255, 255, 255, 0.5);\n    border-radius: 10px;\n    box-shadow: 0 2px 12px rgba(0, 0, 0, 0.06);\n    font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n    z-index: 9999;\n    display: flex;\n    align-items: center;\n    overflow: hidden;\n    animation: slideIn 0.3s cubic-bezier(0.4, 0, 0.2, 1);\n    transition: all 0.2s ease;\n}\n\n#pta-timer-display:hover {\n    box-shadow: 0 4px 16px rgba(0, 0, 0, 0.1);\n}\n\n/* Dragging states */\n#pta-timer-display:not(.fixed) {\n    cursor: move;\n    user-select: none;\n}\n\n#pta-timer-display:not(.fixed):active {\n    opacity: 0.85;\n    transform: scale(1.01);\n    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);\n}\n\n/* Fixed state */\n#pta-timer-display.fixed {\n    cursor: default;\n    user-select: auto;\n    border: 1.5px solid rgba(37, 99, 235, 0.3);\n}\n\n@keyframes slideIn {\n    from {\n        transform: translateY(-10px);\n        opacity: 0;\n    }\n    to {\n        transform: translateY(0);\n        opacity: 1;\n    }\n}\n\n/* Left Branding Section */\n.timer-brand {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n    padding: 0 12px;\n    height: 100%;\n    border-right: 1px solid rgba(0, 0, 0, 0.05);\n}\n\n.timer-brand-info {\n    display: flex;\n    flex-direction: column;\n}\n\n.timer-brand-row {\n    display: flex;\n    align-items: center;\n    gap: 4px;\n}\n\n.timer-status-dot {\n    width: 5px;\n    height: 5px;\n    border-radius: 50%;\n    background: #006c49;\n    box-shadow: 0 0 4px rgba(0, 108, 73, 0.4);\n}\n\n.timer-status-dot.paused {\n    background: #d97706;\n    box-shadow: 0 0 4px rgba(217, 119, 6, 0.4);\n}\n\n.timer-brand-label {\n    font-size: 10px;\n    font-weight: 700;\n    color: rgba(21, 28, 39, 0.8);\n    text-transform: uppercase;\n    letter-spacing: 0.08em;\n}\n\n.timer-exam-badge {\n    font-size: 8px;\n    font-weight: 700;\n    background: rgba(207, 44, 48, 0.85);\n    color: #fff;\n    padding: 1px 4px;\n    border-radius: 3px;\n    text-transform: uppercase;\n    letter-spacing: 0.02em;\n    line-height: 1;\n}\n\n.timer-problem-id {\n    display: flex;\n    align-items: center;\n    gap: 4px;\n    opacity: 0.5;\n}\n\n.timer-problem-id span {\n    font-size: 8px;\n    text-transform: uppercase;\n    letter-spacing: -0.02em;\n    color: #434655;\n}\n\n.timer-problem-mode {\n    font-size: 8px;\n    text-transform: uppercase;\n    letter-spacing: -0.02em;\n    color: #006c49;\n    font-weight: 500;\n}\n\n/* Central Display */\n.timer-center {\n    display: flex;\n    align-items: center;\n    flex: 1;\n    padding: 0 12px;\n    gap: 12px;\n    height: 100%;\n    position: relative;\n}\n\n.timer-time-display {\n    font-family: 'JetBrains Mono', monospace;\n    font-size: 18px;\n    font-weight: 600;\n    color: #004ac6;\n    font-variant-numeric: tabular-nums;\n    letter-spacing: -0.01em;\n    line-height: 1;\n}\n\n.timer-time-display.warning {\n    color: #d97706;\n}\n\n.timer-time-display.danger {\n    color: #ab0b1c;\n    animation: timerPulse 1.5s ease-in-out infinite;\n}\n\n@keyframes timerPulse {\n    0%, 100% { opacity: 1; }\n    50% { opacity: 0.5; }\n}\n\n/* Divider */\n.timer-divider {\n    width: 1px;\n    height: 20px;\n    background: rgba(0, 0, 0, 0.05);\n}\n\n/* Controls Cluster */\n.timer-controls-cluster {\n    display: flex;\n    align-items: center;\n    gap: 4px;\n}\n\n.timer-ctrl-btn {\n    width: 28px;\n    height: 28px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    border: none;\n    border-radius: 6px;\n    cursor: pointer;\n    transition: all 0.15s ease;\n    padding: 0;\n}\n\n.timer-ctrl-btn:active {\n    transform: scale(0.9);\n}\n\n.timer-ctrl-btn .material-symbols-outlined {\n    font-size: 16px;\n    font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 20;\n}\n\n/* Play/Pause - Primary */\n.timer-ctrl-btn.btn-play-pause {\n    background: #004ac6;\n    color: white;\n    box-shadow: 0 1px 3px rgba(0, 74, 198, 0.3);\n}\n\n.timer-ctrl-btn.btn-play-pause:hover {\n    background: #2563eb;\n}\n\n.timer-ctrl-btn.btn-play-pause .material-symbols-outlined {\n    font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 20;\n}\n\n/* Reset */\n.timer-ctrl-btn.btn-reset {\n    background: transparent;\n    color: #434655;\n}\n\n.timer-ctrl-btn.btn-reset:hover {\n    background: rgba(0, 0, 0, 0.05);\n}\n\n/* Stop/Countdown */\n.timer-ctrl-btn.btn-countdown {\n    background: transparent;\n    color: #434655;\n}\n\n.timer-ctrl-btn.btn-countdown:hover {\n    color: #006c49;\n    background: rgba(108, 248, 187, 0.1);\n}\n\n/* Progress Bar */\n.timer-progress-bar {\n    position: absolute;\n    bottom: 0;\n    left: 0;\n    right: 0;\n    height: 2px;\n    background: rgba(0, 0, 0, 0.05);\n}\n\n.timer-progress-fill {\n    height: 100%;\n    background: #4edea3;\n    box-shadow: 0 0 4px rgba(78, 222, 163, 0.3);\n    transition: width 1s linear;\n    width: 0%;\n}\n\n.timer-progress-fill.warning {\n    background: #d97706;\n    box-shadow: 0 0 4px rgba(217, 119, 6, 0.3);\n}\n\n.timer-progress-fill.danger {\n    background: #ab0b1c;\n    box-shadow: 0 0 4px rgba(171, 11, 28, 0.3);\n}\n\n/* Right Actions */\n.timer-actions {\n    display: flex;\n    align-items: center;\n    gap: 2px;\n    padding: 0 8px;\n    height: 100%;\n    border-left: 1px solid rgba(0, 0, 0, 0.05);\n}\n\n.timer-action-btn {\n    width: 28px;\n    height: 28px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    border: none;\n    background: transparent;\n    border-radius: 6px;\n    cursor: pointer;\n    color: rgba(67, 70, 85, 0.5);\n    transition: all 0.15s ease;\n    padding: 0;\n}\n\n.timer-action-btn:hover {\n    color: #151c27;\n    background: rgba(0, 0, 0, 0.05);\n}\n\n.timer-action-btn:active {\n    transform: scale(0.9);\n}\n\n.timer-action-btn .material-symbols-outlined {\n    font-size: 14px;\n    font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 20;\n}\n\n.timer-action-btn.pinned {\n    color: #004ac6;\n}\n\n.timer-action-btn.pinned .material-symbols-outlined {\n    font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 20;\n}\n\n/* Countdown Dialog Overlay */\n.timer-countdown-dialog {\n    position: fixed;\n    top: 0;\n    left: 0;\n    right: 0;\n    bottom: 0;\n    background: rgba(0, 0, 0, 0.3);\n    backdrop-filter: blur(4px);\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    z-index: 10001;\n    animation: fadeIn 0.2s ease;\n}\n\n@keyframes fadeIn {\n    from { opacity: 0; }\n    to { opacity: 1; }\n}\n\n.timer-countdown-panel {\n    background: white;\n    border-radius: 12px;\n    padding: 20px;\n    width: 280px;\n    box-shadow: 0 16px 48px rgba(0, 0, 0, 0.15);\n    animation: scaleIn 0.2s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n@keyframes scaleIn {\n    from { transform: scale(0.95); opacity: 0; }\n    to { transform: scale(1); opacity: 1; }\n}\n\n.timer-countdown-panel h3 {\n    font-size: 14px;\n    font-weight: 600;\n    color: #151c27;\n    margin-bottom: 12px;\n}\n\n.timer-countdown-panel input {\n    width: 100%;\n    padding: 10px 12px;\n    border: 1.5px solid #c3c6d7;\n    border-radius: 8px;\n    font-family: 'JetBrains Mono', monospace;\n    font-size: 16px;\n    font-weight: 500;\n    color: #151c27;\n    outline: none;\n    margin-bottom: 12px;\n    transition: border-color 0.2s;\n}\n\n.timer-countdown-panel input:focus {\n    border-color: #004ac6;\n    box-shadow: 0 0 0 3px rgba(0, 74, 198, 0.1);\n}\n\n.timer-countdown-panel input::placeholder {\n    color: #737686;\n    font-family: 'Inter', sans-serif;\n    font-weight: 400;\n    font-size: 13px;\n}\n\n.timer-countdown-actions {\n    display: flex;\n    gap: 8px;\n}\n\n.timer-countdown-actions button {\n    flex: 1;\n    padding: 10px;\n    border-radius: 8px;\n    font-family: 'Inter', sans-serif;\n    font-size: 13px;\n    font-weight: 600;\n    cursor: pointer;\n    transition: all 0.15s ease;\n    border: none;\n}\n\n.timer-countdown-actions button:active {\n    transform: scale(0.97);\n}\n\n.timer-countdown-actions .btn-confirm {\n    background: #004ac6;\n    color: white;\n}\n\n.timer-countdown-actions .btn-confirm:hover {\n    background: #2563eb;\n}\n\n.timer-countdown-actions .btn-cancel {\n    background: #f0f3ff;\n    color: #434655;\n    border: 1px solid #c3c6d7;\n}\n\n.timer-countdown-actions .btn-cancel:hover {\n    background: #e2e8f8;\n}\n\n.timer-finished-toast {\n    position: fixed;\n    top: 72px;\n    right: 16px;\n    z-index: 10002;\n    animation: timerFinishedSlideIn 0.28s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n.timer-finished-toast.closing {\n    animation: timerFinishedSlideOut 0.18s ease forwards;\n}\n\n.timer-finished-card {\n    min-width: 300px;\n    max-width: 360px;\n    display: flex;\n    align-items: center;\n    gap: 12px;\n    padding: 14px;\n    background: rgba(255, 255, 255, 0.94);\n    border: 1px solid rgba(195, 198, 215, 0.55);\n    border-left: 4px solid #ab0b1c;\n    border-radius: 14px;\n    box-shadow: 0 18px 48px rgba(21, 28, 39, 0.18);\n    backdrop-filter: blur(18px);\n    -webkit-backdrop-filter: blur(18px);\n}\n\n.timer-finished-card.warning {\n    border-left-color: #d97706;\n}\n\n.timer-finished-icon {\n    width: 42px;\n    height: 42px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    flex-shrink: 0;\n    border-radius: 12px;\n    background: linear-gradient(135deg, rgba(171, 11, 28, 0.14), rgba(207, 44, 48, 0.22));\n    color: #ab0b1c;\n}\n\n.timer-finished-icon .material-symbols-outlined {\n    font-size: 24px;\n    font-variation-settings: 'FILL' 1, 'wght' 500, 'GRAD' 0, 'opsz' 24;\n}\n\n.timer-finished-copy {\n    min-width: 0;\n    flex: 1;\n}\n\n.timer-finished-title {\n    font-size: 14px;\n    font-weight: 800;\n    color: #151c27;\n    letter-spacing: -0.02em;\n}\n\n.timer-finished-message {\n    margin-top: 3px;\n    font-size: 12px;\n    line-height: 1.45;\n    color: #434655;\n}\n\n.timer-finished-close {\n    width: 28px;\n    height: 28px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    flex-shrink: 0;\n    border: none;\n    border-radius: 8px;\n    background: transparent;\n    color: rgba(67, 70, 85, 0.65);\n    cursor: pointer;\n    transition: all 0.15s ease;\n}\n\n.timer-finished-close:hover {\n    background: rgba(171, 11, 28, 0.08);\n    color: #ab0b1c;\n}\n\n.timer-finished-close:active {\n    transform: scale(0.92);\n}\n\n.timer-finished-close .material-symbols-outlined {\n    font-size: 18px;\n}\n\n@keyframes timerFinishedSlideIn {\n    from {\n        transform: translateX(24px) scale(0.98);\n        opacity: 0;\n    }\n    to {\n        transform: translateX(0) scale(1);\n        opacity: 1;\n    }\n}\n\n@keyframes timerFinishedSlideOut {\n    to {\n        transform: translateX(24px) scale(0.98);\n        opacity: 0;\n    }\n}\n\n.pta-problem-highlight-pending {\n    background: rgba(124, 58, 237, 0.22) !important;\n    border-color: rgba(124, 58, 237, 0.58) !important;\n    box-shadow: 0 0 0 2px rgba(124, 58, 237, 0.34), 0 8px 20px rgba(88, 28, 135, 0.14) !important;\n    outline: none !important;\n    border-radius: 8px;\n    color: #5b21b6 !important;\n    transition: background 0.2s ease, box-shadow 0.2s ease;\n}\n\n.pta-problem-highlight-pending :is(a, button, div, span, svg, path) {\n    border-color: rgba(124, 58, 237, 0.58) !important;\n    color: #5b21b6 !important;\n    stroke: #7c3aed !important;\n}\n\n.pta-problem-highlight-pending :is(a, button, div)[class*=\"success\"],\n.pta-problem-highlight-pending :is(a, button, div)[class*=\"accepted\"],\n.pta-problem-highlight-pending :is(a, button, div)[class*=\"checked\"] {\n    background: rgba(124, 58, 237, 0.12) !important;\n}\n\n.pta-problem-highlight-solved {\n    background: rgba(245, 158, 11, 0.30) !important;\n    border-color: rgba(217, 119, 6, 0.58) !important;\n    box-shadow: 0 0 0 2px rgba(217, 119, 6, 0.42), 0 8px 20px rgba(146, 64, 14, 0.16) !important;\n    outline: none !important;\n    border-radius: 8px;\n    color: #92400e !important;\n    transition: background 0.2s ease, box-shadow 0.2s ease;\n}\n\n.pta-problem-highlight-solved :is(a, button, div, span, svg, path) {\n    border-color: rgba(217, 119, 6, 0.58) !important;\n    color: #92400e !important;\n    stroke: #d97706 !important;\n}\n\n.pta-problem-highlight-solved :is(a, button, div)[class*=\"success\"],\n.pta-problem-highlight-solved :is(a, button, div)[class*=\"accepted\"],\n.pta-problem-highlight-solved :is(a, button, div)[class*=\"checked\"] {\n    background: rgba(245, 158, 11, 0.16) !important;\n}\n\n/* Responsive */\n@media (max-width: 768px) {\n    #pta-timer-display {\n        min-width: 300px;\n        top: 8px;\n        right: 8px;\n        height: 40px;\n    }\n\n    .timer-time-display {\n        font-size: 16px;\n    }\n\n    .timer-ctrl-btn {\n        width: 26px;\n        height: 26px;\n    }\n}\n\n.timer-ctrl-btn.btn-panel {\n    background: transparent;\n    color: #434655;\n}\n.timer-ctrl-btn.btn-panel:hover {\n    color: #004ac6;\n    background: rgba(0, 74, 198, 0.08);\n}\n";
  const PANEL_CSS = "\n@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@500;600&family=Material+Symbols+Outlined:opsz,wght,FILL,[email protected],100..700,0..1,-50..200&display=swap');\n:host {\n  position: fixed;\n  inset: 0;\n  z-index: 2147483646;\n  display: none;\n  font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n}\n:host(.visible) { display: block; }\n.pta-panel-backdrop {\n  position: absolute;\n  inset: 0;\n  background: rgba(15, 23, 42, 0.28);\n  backdrop-filter: blur(2px);\n  -webkit-backdrop-filter: blur(2px);\n}\n.pta-panel-shell {\n  position: absolute;\n  top: 72px;\n  right: 24px;\n  width: 360px;\n  height: 580px;\n  max-width: calc(100vw - 32px);\n  max-height: calc(100vh - 96px);\n  border-radius: 18px;\n  overflow: hidden;\n  background: #fff;\n  box-shadow: 0 24px 80px rgba(15, 23, 42, 0.28);\n}\n.pta-panel-close {\n  position: absolute;\n  top: 7px;\n  right: 8px;\n  z-index: 60;\n  width: 30px;\n  height: 30px;\n  display: flex;\n  align-items: center;\n  justify-content: center;\n  border: none;\n  border-radius: 10px;\n  background: rgba(240, 243, 255, 0.88);\n  color: #434655;\n  cursor: pointer;\n  transition: all 0.15s ease;\n}\n.pta-panel-close:hover { background: rgba(226, 232, 248, 1); color: #004ac6; }\n.pta-panel-close .material-symbols-outlined { font-size: 18px; }\n@media (max-width: 480px) {\n  .pta-panel-shell { top: 56px; right: 8px; left: 8px; width: auto; height: min(580px, calc(100vh - 72px)); }\n}\n/* PTA Timer - Focus Precision Design System - All Views */\n:host {\n    --surface: #f9f9ff;\n    --surface-dim: #d3daea;\n    --surface-container-lowest: #ffffff;\n    --surface-container-low: #f0f3ff;\n    --surface-container: #e7eefe;\n    --surface-container-high: #e2e8f8;\n    --surface-container-highest: #dce2f3;\n    --on-surface: #151c27;\n    --on-surface-variant: #434655;\n    --outline: #737686;\n    --outline-variant: #c3c6d7;\n    --primary: #004ac6;\n    --primary-container: #2563eb;\n    --on-primary: #ffffff;\n    --on-primary-container: #eeefff;\n    --secondary: #006c49;\n    --secondary-container: #6cf8bb;\n    --on-secondary-container: #00714d;\n    --tertiary: #ab0b1c;\n    --tertiary-container: #cf2c30;\n    --on-tertiary-container: #ffecea;\n    --error: #ba1a1a;\n    --error-container: #ffdad6;\n    --on-error-container: #93000a;\n    --radius-sm: 0.25rem;\n    --radius-default: 0.5rem;\n    --radius-md: 0.75rem;\n    --radius-lg: 1rem;\n    --radius-xl: 1.5rem;\n    --radius-full: 9999px;\n    --container-padding: 16px;\n    --element-gap: 8px;\n    --section-margin: 12px;\n}\n\n* { margin: 0; padding: 0; box-sizing: border-box; }\n.plugin-container {\n    width: 360px;\n    height: 580px;\n    margin: 0;\n    overflow: hidden;\n    font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;\n    background: transparent;\n    color: var(--on-surface);\n    -webkit-font-smoothing: antialiased;\n}\n.material-symbols-outlined {\n    font-family: 'Material Symbols Outlined';\n    font-weight: normal;\n    font-style: normal;\n    font-size: 20px;\n    line-height: 1;\n    letter-spacing: normal;\n    text-transform: none;\n    display: inline-block;\n    white-space: nowrap;\n    word-wrap: normal;\n    direction: ltr;\n    font-feature-settings: 'liga';\n    -webkit-font-feature-settings: 'liga';\n    -webkit-font-smoothing: antialiased;\n    font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;\n    vertical-align: middle;\n    width: 1em;\n    overflow: hidden;\n    flex-shrink: 0;\n}\n.plugin-container {\n    width: 100%;\n    height: 100%;\n    position: relative;\n}\n.view {\n    display: flex;\n    flex-direction: column;\n    height: 100%;\n    background: var(--surface-container-lowest);\n    border: 1px solid rgba(195, 198, 215, 0.3);\n    overflow: hidden;\n    position: absolute;\n    top: 0; left: 0; right: 0; bottom: 0;\n}\n\n/* TopAppBar */\n.top-app-bar {\n    display: flex;\n    justify-content: space-between;\n    align-items: center;\n    padding: 8px 56px 8px var(--container-padding);\n    background: rgba(255, 255, 255, 0.8);\n    backdrop-filter: blur(12px);\n    border-bottom: 1px solid rgba(195, 198, 215, 0.2);\n    z-index: 10;\n    flex-shrink: 0;\n}\n.top-app-bar.compact {\n    padding: 6px 12px;\n    border-bottom: 1px solid rgba(195, 198, 215, 0.1);\n}\n.app-bar-left {\n    display: flex;\n    align-items: center;\n    gap: 8px;\n}\n.app-bar-right {\n    display: flex;\n    align-items: center;\n    gap: 4px;\n}\n.app-title {\n    font-size: 14px;\n    font-weight: 700;\n    color: var(--on-surface);\n}\n.app-title-sm {\n    font-size: 13px;\n    font-weight: 700;\n    color: var(--on-surface);\n}\n.exam-badge {\n    background: rgba(207, 44, 48, 0.8);\n    color: #fff;\n    font-size: 9px;\n    font-weight: 700;\n    padding: 2px 5px;\n    border-radius: var(--radius-sm);\n    text-transform: uppercase;\n}\n.icon-btn {\n    width: 32px; height: 32px;\n    display: flex; align-items: center; justify-content: center;\n    border: none; background: transparent;\n    border-radius: var(--radius-default);\n    cursor: pointer; color: var(--on-surface-variant);\n    transition: all 0.15s ease;\n}\n.icon-btn:hover { background: rgba(226, 232, 248, 0.5); color: var(--primary); }\n.icon-btn:active { transform: scale(0.95); }\n.icon-btn .material-symbols-outlined { font-size: 20px; }\n.icon-btn-sm {\n    width: 26px; height: 26px;\n    display: flex; align-items: center; justify-content: center;\n    border: none; background: transparent;\n    border-radius: var(--radius-sm);\n    cursor: pointer; color: var(--on-surface-variant);\n    transition: all 0.15s ease;\n}\n.icon-btn-sm:hover { background: rgba(226, 232, 248, 0.5); }\n\n/* View Content */\n.view-content {\n    flex: 1;\n    display: flex;\n    flex-direction: column;\n    overflow-y: auto;\n    padding: var(--container-padding);\n    gap: var(--section-margin);\n}\n.view-content::-webkit-scrollbar { width: 4px; }\n.view-content::-webkit-scrollbar-track { background: transparent; }\n.view-content::-webkit-scrollbar-thumb { background: var(--surface-container-highest); border-radius: 4px; }\n\n/* Problem Header */\n.problem-header {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n}\n.problem-info { min-width: 0; flex: 1; }\n.problem-subtitle {\n    font-size: 11px; font-weight: 500;\n    color: var(--on-surface-variant); margin-bottom: 2px;\n}\n.problem-title {\n    font-size: 14px; font-weight: 600;\n    color: var(--on-surface);\n    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n}\n\n/* Timer Display */\n.timer-display-section {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    padding: 12px 16px;\n    background: var(--surface-container-low);\n    border: 1px solid rgba(0, 74, 198, 0.05);\n    border-radius: var(--radius-md);\n}\n.timer-display-wrapper { display: flex; flex-direction: column; }\n.timer-label {\n    font-size: 10px; font-weight: 500;\n    color: var(--on-surface-variant);\n    text-transform: uppercase; letter-spacing: 0.03em;\n    margin-bottom: 2px;\n}\n.timer-value {\n    font-family: 'JetBrains Mono', monospace;\n    font-size: 28px; font-weight: 600;\n    color: var(--on-surface);\n    line-height: 1.2; letter-spacing: -0.02em;\n    font-variant-numeric: tabular-nums;\n    transition: color 0.3s ease;\n}\n.timer-value.warning { color: #d97706; }\n.timer-value.danger { color: var(--tertiary); animation: pulse 1.5s ease-in-out infinite; }\n@keyframes pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.6; } }\n.timer-controls { display: flex; gap: 6px; }\n.control-btn {\n    width: 36px; height: 36px;\n    display: flex; align-items: center; justify-content: center;\n    border: 1px solid rgba(195, 198, 215, 0.3);\n    background: rgba(255, 255, 255, 0.8);\n    border-radius: var(--radius-default);\n    cursor: pointer; color: var(--on-surface-variant);\n    transition: all 0.15s ease;\n}\n.control-btn:hover { background: var(--surface-container-high); }\n.control-btn:active { transform: scale(0.9); }\n.control-btn .material-symbols-outlined { font-size: 18px; }\n.countdown-btn { color: var(--secondary); }\n.countdown-btn:hover { background: rgba(108, 248, 187, 0.15); }\n\n/* Countdown Input */\n.countdown-input-section {\n    padding: 12px;\n    background: var(--surface-container-low);\n    border: 1px solid rgba(195, 198, 215, 0.3);\n    border-radius: var(--radius-default);\n}\n.countdown-input-row { display: flex; gap: 8px; align-items: center; }\n.countdown-input {\n    flex: 1; padding: 8px 12px;\n    border: 1px solid var(--outline-variant);\n    border-radius: var(--radius-default);\n    background: var(--surface-container-lowest);\n    color: var(--on-surface);\n    font-family: 'JetBrains Mono', monospace;\n    font-size: 14px; font-weight: 500; outline: none;\n    transition: border-color 0.2s ease;\n}\n.countdown-input:focus { border-color: var(--primary); box-shadow: 0 0 0 2px rgba(0, 74, 198, 0.1); }\n.countdown-input::placeholder { color: var(--outline); font-family: 'Inter', sans-serif; font-weight: 400; }\n.btn-primary-sm {\n    padding: 8px 14px;\n    background: var(--primary); color: var(--on-primary);\n    border: none; border-radius: var(--radius-default);\n    font-family: 'Inter', sans-serif; font-size: 12px; font-weight: 600;\n    cursor: pointer; transition: all 0.15s ease; white-space: nowrap;\n}\n.btn-primary-sm:hover { background: var(--primary-container); }\n.btn-primary-sm:active { transform: scale(0.95); }\n.btn-cancel-sm {\n    padding: 8px 12px;\n    background: transparent; color: var(--on-surface-variant);\n    border: 1px solid var(--outline-variant);\n    border-radius: var(--radius-default);\n    font-family: 'Inter', sans-serif; font-size: 12px; font-weight: 500;\n    cursor: pointer; transition: all 0.15s ease; white-space: nowrap;\n}\n.btn-cancel-sm:hover { background: var(--surface-container-high); }\n\n/* Sessions List */\n.quick-stats { flex: 1; display: flex; flex-direction: column; min-height: 0; }\n.stats-header { margin-bottom: 8px; }\n.stats-label {\n    font-size: 10px; font-weight: 700;\n    color: var(--on-surface-variant);\n    text-transform: uppercase; letter-spacing: 0.05em;\n}\n.sessions-list {\n    flex: 1; overflow-y: auto;\n    display: flex; flex-direction: column; gap: 6px;\n}\n.sessions-list::-webkit-scrollbar { width: 3px; }\n.sessions-list::-webkit-scrollbar-thumb { background: var(--surface-container-highest); border-radius: 3px; }\n.session-item {\n    display: flex; align-items: center; justify-content: space-between;\n    padding: 10px 12px;\n    background: var(--surface-container-low);\n    border: 1px solid transparent;\n    border-radius: var(--radius-default);\n    transition: all 0.15s ease;\n}\n.session-item:hover { border-color: rgba(0, 74, 198, 0.15); background: var(--surface-container-high); }\n.session-item-left { display: flex; align-items: center; gap: 10px; min-width: 0; }\n.session-number {\n    width: 28px; height: 28px;\n    border-radius: var(--radius-default);\n    background: var(--surface-container-high);\n    display: flex; align-items: center; justify-content: center;\n    font-family: 'JetBrains Mono', monospace;\n    font-size: 11px; font-weight: 600; color: var(--primary); flex-shrink: 0;\n}\n.session-info { min-width: 0; }\n.session-problem-id {\n    font-size: 12px; font-weight: 600; color: var(--on-surface);\n    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n}\n.session-date { font-size: 10px; color: var(--on-surface-variant); margin-top: 1px; }\n.session-item-right {\n    display: flex; align-items: center; gap: 8px; flex-shrink: 0;\n}\n.session-time {\n    font-family: 'JetBrains Mono', monospace;\n    font-size: 12px; font-weight: 500; color: var(--primary); white-space: nowrap;\n}\n.session-delete-btn {\n    width: 26px; height: 26px;\n    display: flex; align-items: center; justify-content: center;\n    border: none; border-radius: var(--radius-sm);\n    background: transparent; color: var(--on-surface-variant);\n    cursor: pointer; opacity: 0;\n    transition: all 0.15s ease;\n}\n.session-item:hover .session-delete-btn { opacity: 1; }\n.session-delete-btn:hover { background: var(--error-container); color: var(--on-error-container); }\n.session-delete-btn .material-symbols-outlined { font-size: 16px; }\n.empty-state {\n    display: flex; flex-direction: column;\n    align-items: center; justify-content: center;\n    padding: 24px; gap: 8px;\n}\n.icon-empty { font-size: 32px !important; color: var(--outline-variant); }\n.empty-text { font-size: 12px; color: var(--outline); }\n\n.history-content {\n    gap: 14px;\n    background:\n        radial-gradient(circle at top right, rgba(37, 99, 235, 0.12), transparent 34%),\n        var(--surface-container-lowest);\n}\n.history-hero {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    padding: 18px;\n    background: linear-gradient(135deg, var(--primary), var(--primary-container));\n    border-radius: var(--radius-xl);\n    color: var(--on-primary);\n    box-shadow: 0 12px 28px rgba(0, 74, 198, 0.18);\n}\n.history-eyebrow {\n    font-size: 11px;\n    font-weight: 700;\n    letter-spacing: 0.08em;\n    text-transform: uppercase;\n    opacity: 0.82;\n}\n.history-title {\n    margin-top: 4px;\n    font-size: 24px;\n    font-weight: 800;\n    letter-spacing: -0.04em;\n}\n.history-hero-icon {\n    width: 44px;\n    height: 44px;\n    display: flex !important;\n    align-items: center;\n    justify-content: center;\n    border-radius: var(--radius-lg);\n    background: rgba(255, 255, 255, 0.16);\n    font-size: 26px !important;\n}\n.history-panel {\n    padding: 12px;\n    background: rgba(255, 255, 255, 0.72);\n    border: 1px solid rgba(195, 198, 215, 0.28);\n    border-radius: var(--radius-xl);\n    box-shadow: 0 10px 30px rgba(21, 28, 39, 0.05);\n}\n.history-stats-header {\n    display: flex;\n    align-items: center;\n    justify-content: space-between;\n    margin-bottom: 10px;\n}\n.stats-hint {\n    font-size: 10px;\n    color: rgba(67, 70, 85, 0.66);\n}\n.history-panel .sessions-list {\n    gap: 8px;\n}\n.history-panel .session-item {\n    padding: 12px;\n    background: rgba(240, 243, 255, 0.72);\n    border-color: rgba(195, 198, 215, 0.22);\n    border-radius: var(--radius-lg);\n}\n.history-panel .session-item:hover {\n    transform: translateY(-1px);\n    box-shadow: 0 8px 18px rgba(21, 28, 39, 0.06);\n}\n.history-panel .session-delete-btn {\n    opacity: 1;\n    background: rgba(255, 255, 255, 0.64);\n}\n.confirm-overlay {\n    position: absolute;\n    inset: 0;\n    z-index: 50;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    padding: 20px;\n    background: rgba(21, 28, 39, 0.36);\n    backdrop-filter: blur(8px);\n    opacity: 0;\n    pointer-events: none;\n    transition: opacity 0.18s ease;\n}\n.confirm-overlay.visible {\n    opacity: 1;\n    pointer-events: auto;\n}\n.confirm-dialog {\n    width: 100%;\n    padding: 22px;\n    background: var(--surface-container-lowest);\n    border: 1px solid rgba(195, 198, 215, 0.55);\n    border-radius: var(--radius-xl);\n    box-shadow: 0 24px 60px rgba(21, 28, 39, 0.22);\n    transform: translateY(8px) scale(0.98);\n    transition: transform 0.18s ease;\n}\n.confirm-overlay.visible .confirm-dialog {\n    transform: translateY(0) scale(1);\n}\n.confirm-icon {\n    width: 48px;\n    height: 48px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    margin-bottom: 14px;\n    border-radius: var(--radius-lg);\n    background: var(--error-container);\n    color: var(--on-error-container);\n}\n.confirm-icon .material-symbols-outlined {\n    font-size: 26px;\n}\n.confirm-title {\n    font-size: 18px;\n    font-weight: 800;\n    color: var(--on-surface);\n    letter-spacing: -0.03em;\n}\n.confirm-message {\n    margin-top: 6px;\n    font-size: 12px;\n    line-height: 1.5;\n    color: var(--on-surface-variant);\n}\n.confirm-actions {\n    display: grid;\n    grid-template-columns: 1fr 1fr;\n    gap: 10px;\n    margin-top: 18px;\n}\n.confirm-btn {\n    height: 40px;\n    border: none;\n    border-radius: var(--radius-lg);\n    font-family: 'Inter', sans-serif;\n    font-size: 13px;\n    font-weight: 700;\n    cursor: pointer;\n    transition: all 0.15s ease;\n}\n.confirm-btn:active {\n    transform: scale(0.97);\n}\n.confirm-cancel {\n    background: var(--surface-container-low);\n    color: var(--on-surface-variant);\n}\n.confirm-cancel:hover {\n    background: var(--surface-container-high);\n}\n.confirm-delete {\n    background: var(--error);\n    color: #fff;\n    box-shadow: 0 8px 16px rgba(186, 26, 26, 0.18);\n}\n.confirm-delete:hover {\n    filter: brightness(1.06);\n}\n\n/* Footer */\n.app-footer {\n    display: flex; align-items: center; justify-content: space-between;\n    padding: 6px var(--container-padding);\n    background: rgba(231, 238, 254, 0.3);\n    border-top: 1px solid rgba(195, 198, 215, 0.15);\n    flex-shrink: 0;\n}\n.app-footer.compact { padding: 4px 12px; }\n.footer-status { display: flex; align-items: center; gap: 6px; }\n.status-dot {\n    width: 6px; height: 6px; border-radius: 50%;\n    background: var(--outline-variant); transition: background 0.3s ease;\n}\n.status-dot.connected { background: var(--secondary); box-shadow: 0 0 4px rgba(0, 108, 73, 0.4); }\n.status-text { font-size: 10px; color: var(--on-surface-variant); }\n.version-text { font-size: 10px; color: rgba(67, 70, 85, 0.6); font-style: italic; }\n\n/* ============================================\n   VIEW: Exam List\n   ============================================ */\n.exam-list-content { padding-bottom: 0; }\n.context-header { margin-bottom: 12px; }\n.context-title {\n    font-size: 11px; font-weight: 800;\n    color: var(--on-surface-variant);\n    text-transform: uppercase; letter-spacing: 0.08em;\n    margin-bottom: 8px;\n}\n.search-bar {\n    display: flex; align-items: center;\n    background: var(--surface-container-low);\n    border: 1px solid rgba(195, 198, 215, 0.5);\n    border-radius: var(--radius-default);\n    padding: 8px 12px; gap: 8px;\n}\n.search-icon { font-size: 18px !important; color: var(--on-surface-variant); }\n.search-input {\n    flex: 1; border: none; background: transparent;\n    font-size: 12px; color: var(--on-surface); outline: none;\n    font-family: 'Inter', sans-serif;\n}\n.search-input::placeholder { color: var(--outline); }\n.btn-current-page-problems {\n    width: 100%;\n    margin-top: 10px;\n    padding: 10px 12px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    gap: 8px;\n    border: 1px solid rgba(0, 74, 198, 0.18);\n    border-radius: var(--radius-lg);\n    background: rgba(0, 74, 198, 0.08);\n    color: var(--primary);\n    font-family: 'Inter', sans-serif;\n    font-size: 13px;\n    font-weight: 800;\n    cursor: pointer;\n    transition: all 0.15s ease;\n}\n.btn-current-page-problems:hover {\n    background: rgba(0, 74, 198, 0.13);\n    border-color: rgba(0, 74, 198, 0.32);\n}\n.btn-current-page-problems:active {\n    transform: scale(0.98);\n}\n.btn-current-page-problems .material-symbols-outlined {\n    font-size: 18px;\n}\n.exam-list-scroll {\n    flex: 1; overflow-y: auto;\n    display: flex; flex-direction: column; gap: 8px;\n    padding-bottom: 8px;\n}\n.exam-list-scroll::-webkit-scrollbar { width: 4px; }\n.exam-list-scroll::-webkit-scrollbar-thumb { background: var(--surface-container-highest); border-radius: 4px; }\n.exam-list-item {\n    display: flex; align-items: center; justify-content: space-between;\n    padding: 12px;\n    background: var(--surface-container-low);\n    border: 1px solid transparent;\n    border-radius: var(--radius-default);\n    cursor: pointer;\n    transition: all 0.2s ease;\n}\n.exam-list-item:hover {\n    border-color: rgba(0, 74, 198, 0.2);\n    background: rgba(226, 232, 248, 0.5);\n}\n.exam-list-item.active-exam {\n    border-color: rgba(0, 74, 198, 0.34);\n    background: rgba(0, 74, 198, 0.08);\n}\n.exam-list-item-left { display: flex; align-items: center; gap: 12px; min-width: 0; }\n.exam-list-icon {\n    width: 32px; height: 32px;\n    border-radius: var(--radius-sm);\n    background: rgba(0, 74, 198, 0.1);\n    display: flex; align-items: center; justify-content: center;\n    color: var(--primary);\n    flex-shrink: 0;\n}\n.exam-list-icon .material-symbols-outlined { font-size: 20px; }\n.exam-list-copy { display: flex; flex-direction: column; min-width: 0; }\n.exam-list-name { font-size: 14px; font-weight: 500; color: var(--on-surface); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.exam-list-subtitle { margin-top: 2px; font-size: 10px; font-weight: 700; color: var(--primary); }\n.exam-list-item-right { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }\n.exam-list-count {\n    background: var(--secondary-container);\n    color: var(--on-secondary-container);\n    padding: 2px 8px; border-radius: var(--radius-full);\n    font-size: 11px; font-weight: 600; white-space: nowrap;\n}\n.exam-list-delete-btn {\n    width: 28px;\n    height: 28px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    border: none;\n    border-radius: var(--radius-full);\n    background: var(--error-container);\n    color: var(--on-error-container);\n    cursor: pointer;\n    transition: all 0.15s ease;\n}\n.exam-list-delete-btn:hover { filter: brightness(0.98); }\n.exam-list-delete-btn:active { transform: scale(0.92); }\n.exam-list-delete-btn .material-symbols-outlined { font-size: 17px; }\n.exam-list-chevron { font-size: 18px !important; color: var(--outline); transition: color 0.2s; }\n.exam-list-item:hover .exam-list-chevron { color: var(--primary); }\n.exam-list-footer {\n    padding: 16px;\n    background: var(--surface-container-lowest);\n    border-top: 1px solid rgba(195, 198, 215, 0.2);\n    flex-shrink: 0;\n}\n.btn-return-timer {\n    width: 100%; padding: 12px 16px;\n    background: var(--primary); color: var(--on-primary);\n    border: none; border-radius: var(--radius-lg);\n    font-family: 'Inter', sans-serif; font-size: 14px; font-weight: 600;\n    cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px;\n    transition: all 0.15s ease; box-shadow: 0 2px 8px rgba(0, 74, 198, 0.2);\n}\n.btn-return-timer:hover { background: var(--primary-container); }\n.btn-return-timer:active { transform: scale(0.98); }\n.btn-return-timer .material-symbols-outlined { font-size: 20px; }\n\n/* ============================================\n   VIEW: Exam Problem List\n   ============================================ */\n.exam-problems-layout {\n    display: flex; flex: 1; overflow: hidden;\n}\n.exam-problems-main {\n    flex: 1; display: flex; flex-direction: column;\n    padding: var(--container-padding);\n    overflow-y: auto;\n    background:\n        radial-gradient(circle at top left, rgba(37, 99, 235, 0.08), transparent 36%),\n        var(--surface-container-lowest);\n}\n.exam-problems-main::-webkit-scrollbar { width: 4px; }\n.exam-problems-main::-webkit-scrollbar-thumb { background: var(--surface-container-highest); border-radius: 4px; }\n.exam-problems-back { margin-bottom: var(--section-margin); }\n.btn-text-primary {\n    display: flex; align-items: center; gap: 4px;\n    background: none; border: none;\n    color: var(--primary); font-size: 14px; font-weight: 600;\n    font-family: 'Inter', sans-serif;\n    cursor: pointer; padding: 0;\n}\n.btn-text-primary:hover { text-decoration: underline; }\n.btn-text-primary .material-symbols-outlined { font-size: 18px; }\n.exam-title-section { margin-bottom: 20px; }\n.exam-title {\n    font-size: 20px; font-weight: 700;\n    color: var(--on-surface); line-height: 1.2; letter-spacing: -0.01em;\n}\n.exam-meta { display: flex; align-items: center; gap: 8px; margin-top: 6px; }\n.exam-status-badge {\n    background: var(--secondary-container);\n    color: var(--on-secondary-container);\n    padding: 2px 8px; border-radius: var(--radius-full);\n    font-size: 11px; font-weight: 500;\n}\n.exam-duration {\n    display: flex; align-items: center; gap: 4px;\n    font-size: 11px; color: var(--on-surface-variant);\n}\n.problem-list {\n    display: flex; flex-direction: column; gap: 10px;\n    flex: 1;\n}\n.problem-list-item {\n    display: flex; align-items: center; justify-content: space-between;\n    padding: 12px;\n    background: var(--surface-container-low);\n    border: 1px solid rgba(195, 198, 215, 0.3);\n    border-radius: var(--radius-lg);\n    transition: all 0.2s ease;\n}\n.problem-list-item:hover { border-color: rgba(0, 74, 198, 0.5); }\n.problem-list-item-left { display: flex; align-items: center; gap: 12px; min-width: 0; }\n.problem-list-number {\n    width: 40px; height: 40px;\n    border-radius: var(--radius-default);\n    background: var(--surface-container-high);\n    display: flex; align-items: center; justify-content: center;\n    font-family: 'JetBrains Mono', monospace;\n    font-size: 13px; font-weight: 600; color: var(--primary); flex-shrink: 0;\n}\n.problem-list-info { min-width: 0; }\n.problem-list-name {\n    font-size: 13px; font-weight: 600; color: var(--on-surface);\n    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n}\n.problem-list-meta { font-size: 11px; color: var(--on-surface-variant); margin-top: 3px; }\n.problem-list-start-btn {\n    padding: 6px 16px;\n    background: var(--primary); color: var(--on-primary);\n    border: none; border-radius: var(--radius-full);\n    font-family: 'Inter', sans-serif; font-size: 13px; font-weight: 600;\n    cursor: pointer; transition: all 0.15s ease;\n    box-shadow: 0 1px 3px rgba(0, 74, 198, 0.2);\n    white-space: nowrap;\n}\n.problem-list-start-btn:hover { background: var(--primary-container); }\n.problem-list-start-btn:active { transform: scale(0.95); }\n.problem-list-start-btn:disabled {\n    opacity: 0.45;\n    cursor: not-allowed;\n    box-shadow: none;\n}\n.btn-danger-sm {\n    padding: 6px 12px;\n    background: var(--error-container);\n    color: var(--on-error-container);\n    border: none;\n    border-radius: var(--radius-full);\n    font-family: 'Inter', sans-serif;\n    font-size: 12px;\n    font-weight: 800;\n    cursor: pointer;\n    transition: all 0.15s ease;\n    white-space: nowrap;\n}\n.btn-danger-sm:hover {\n    filter: brightness(0.98);\n}\n.btn-danger-sm:active {\n    transform: scale(0.95);\n}\n.custom-exam-toolbar {\n    flex-direction: column;\n    align-items: stretch;\n    gap: 8px;\n    margin-bottom: 12px;\n    padding: 10px 12px;\n    background: rgba(240, 243, 255, 0.78);\n    border: 1px solid rgba(0, 74, 198, 0.16);\n    border-radius: var(--radius-lg);\n}\n.custom-exam-count {\n    font-size: 12px;\n    font-weight: 800;\n    color: var(--primary);\n    white-space: nowrap;\n}\n.custom-exam-actions {\n    display: flex;\n    align-items: center;\n    gap: 6px;\n    flex-wrap: wrap;\n    width: 100%;\n}\n.custom-exam-actions .btn-text-primary,\n.custom-exam-actions .problem-list-start-btn,\n.custom-exam-actions .btn-danger-sm {\n    min-height: 30px;\n}\n.custom-exam-actions .problem-list-start-btn,\n.custom-exam-actions .btn-danger-sm {\n    flex: 1 1 76px;\n    padding-inline: 10px;\n}\n.custom-exam-duration-input {\n    flex: 1 1 82px;\n    min-width: 0;\n    height: 30px;\n    padding: 0 9px;\n    border: 1px solid rgba(124, 58, 237, 0.32);\n    border-radius: var(--radius-full);\n    background: rgba(255, 255, 255, 0.78);\n    color: var(--on-surface);\n    font-family: 'JetBrains Mono', monospace;\n    font-size: 12px;\n    font-weight: 700;\n    outline: none;\n    transition: all 0.15s ease;\n}\n.custom-exam-duration-input:focus {\n    border-color: rgba(124, 58, 237, 0.72);\n    box-shadow: 0 0 0 3px rgba(124, 58, 237, 0.12);\n}\n.custom-exam-duration-input::placeholder {\n    color: rgba(67, 70, 85, 0.62);\n    font-family: 'Inter', sans-serif;\n    font-weight: 700;\n}\n.problem-list-item.selected,\n.problem-list-item.pending {\n    background: rgba(124, 58, 237, 0.14);\n    border-color: rgba(124, 58, 237, 0.42);\n    box-shadow: 0 8px 18px rgba(88, 28, 135, 0.10);\n}\n.problem-list-item.solved {\n    background: rgba(245, 158, 11, 0.18);\n    border-color: rgba(217, 119, 6, 0.42);\n    box-shadow: 0 8px 18px rgba(146, 64, 14, 0.10);\n}\n.problem-status-badge {\n    display: inline-flex;\n    align-items: center;\n    padding: 2px 7px;\n    border-radius: var(--radius-full);\n    font-size: 10px;\n    font-weight: 800;\n    line-height: 1.2;\n    white-space: nowrap;\n}\n.problem-status-badge.blue {\n    background: rgba(124, 58, 237, 0.16);\n    color: #5b21b6;\n}\n.problem-status-badge.green {\n    background: rgba(245, 158, 11, 0.22);\n    color: #92400e;\n}\n.progress-stats {\n    display: flex; align-items: center;\n    padding: 12px;\n    background: rgba(226, 232, 248, 0.5);\n    border-radius: var(--radius-default);\n    margin-top: auto; padding-top: 20px;\n    gap: 12px;\n}\n.progress-info { display: flex; flex-direction: column; }\n.progress-label {\n    font-size: 10px; font-weight: 500;\n    color: var(--on-surface-variant);\n    text-transform: uppercase; letter-spacing: 0.05em;\n}\n.progress-value {\n    font-family: 'JetBrains Mono', monospace;\n    font-size: 13px; font-weight: 500; color: var(--primary);\n}\n.progress-bar-container {\n    flex: 1; height: 4px;\n    background: var(--outline-variant);\n    border-radius: var(--radius-full); overflow: hidden;\n}\n.progress-bar-fill {\n    height: 100%; background: var(--secondary);\n    border-radius: var(--radius-full);\n    transition: width 0.3s ease; width: 0%;\n}\n.progress-percent { font-size: 12px; color: var(--on-surface-variant); font-weight: 500; }\n\n/* ============================================\n   VIEW: Exam Mode (Minimalist Compact)\n   ============================================ */\n.global-timer-bar {\n    display: flex; align-items: center; justify-content: space-between;\n    padding: 4px 12px;\n    background: rgba(0, 74, 198, 0.05);\n    border-bottom: 1px solid rgba(195, 198, 215, 0.2);\n    flex-shrink: 0;\n}\n.global-timer-left { display: flex; align-items: center; gap: 6px; }\n.global-timer-label {\n    font-size: 11px; font-weight: 500;\n    color: var(--on-surface-variant);\n    text-transform: uppercase; letter-spacing: 0.05em;\n}\n.global-timer-right { display: flex; align-items: center; gap: 12px; }\n.global-timer-value {\n    font-family: 'JetBrains Mono', monospace;\n    font-size: 13px; font-weight: 700;\n    color: var(--primary); font-variant-numeric: tabular-nums;\n}\n.global-progress-bar {\n    width: 64px; height: 4px;\n    background: var(--surface-container-highest);\n    border-radius: var(--radius-full); overflow: hidden;\n}\n.global-progress-fill {\n    height: 100%; background: var(--primary);\n    border-radius: var(--radius-full);\n    transition: width 1s linear; width: 0%;\n}\n.exam-mode-content {\n    flex: 1; display: flex; flex-direction: column;\n    padding: 12px; gap: 12px;\n}\n.exam-problem-header {\n    display: flex; align-items: center; justify-content: space-between;\n}\n.exam-problem-info { min-width: 0; }\n.exam-problem-index {\n    font-size: 11px; font-weight: 500;\n    color: var(--on-surface-variant);\n}\n.exam-problem-name {\n    font-size: 13px; font-weight: 600;\n    color: var(--on-surface);\n    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n    line-height: 1.3;\n}\n.exam-problem-difficulty {\n    background: rgba(108, 248, 187, 0.6);\n    color: var(--on-secondary-container);\n    padding: 3px 8px; border-radius: var(--radius-sm);\n    font-size: 10px; font-weight: 600; white-space: nowrap;\n}\n.exam-timer-display {\n    display: flex; align-items: center; justify-content: space-between;\n    padding: 10px 12px;\n    background: rgba(240, 243, 255, 0.5);\n    border: 1px solid rgba(0, 74, 198, 0.05);\n    border-radius: var(--radius-default);\n}\n.exam-timer-info { display: flex; flex-direction: column; }\n.exam-timer-label {\n    font-size: 10px; font-weight: 500;\n    color: var(--on-surface-variant);\n    text-transform: uppercase; letter-spacing: -0.02em;\n}\n.exam-timer-value {\n    font-family: 'JetBrains Mono', monospace;\n    font-size: 24px; font-weight: 600;\n    color: var(--on-surface); line-height: 1;\n    font-variant-numeric: tabular-nums;\n    letter-spacing: -0.05em;\n}\n.exam-timer-value.warning { color: #d97706; }\n.exam-timer-value.danger { color: var(--tertiary); animation: pulse 1.5s ease-in-out infinite; }\n.exam-timer-controls { display: flex; gap: 6px; }\n.exam-ctrl-btn {\n    width: 32px; height: 32px;\n    border: 1px solid rgba(195, 198, 215, 0.3);\n    background: rgba(255, 255, 255, 0.8);\n    border-radius: var(--radius-default);\n    display: flex; align-items: center; justify-content: center;\n    cursor: pointer; color: var(--on-surface-variant);\n    transition: all 0.15s ease;\n}\n.exam-ctrl-btn:hover { background: var(--surface-container-high); }\n.exam-ctrl-btn:active { transform: scale(0.9); }\n.exam-ctrl-btn .material-symbols-outlined { font-size: 18px; }\n.exam-nav-controls { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }\n.exam-nav-btn {\n    display: flex; align-items: center; justify-content: center; gap: 4px;\n    padding: 8px;\n    border: 1px solid rgba(195, 198, 215, 0.3);\n    background: rgba(255, 255, 255, 0.4);\n    border-radius: var(--radius-default);\n    font-family: 'Inter', sans-serif; font-size: 12px; font-weight: 600;\n    color: var(--on-surface-variant);\n    cursor: pointer; transition: all 0.15s ease;\n}\n.exam-nav-btn:hover { background: rgba(226, 232, 248, 0.6); }\n.exam-nav-btn:active { transform: scale(0.95); }\n.exam-submit-btn {\n    width: 100%; padding: 10px;\n    background: rgba(0, 74, 198, 0.9); color: var(--on-primary);\n    border: none; border-radius: var(--radius-default);\n    font-family: 'Inter', sans-serif; font-size: 13px; font-weight: 600;\n    cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px;\n    transition: all 0.15s ease;\n    box-shadow: 0 1px 4px rgba(0, 74, 198, 0.2);\n}\n.exam-submit-btn:hover { filter: brightness(1.1); }\n.exam-submit-btn:active { transform: scale(0.98); }\n.exam-cancel-btn {\n    width: 100%; padding: 10px;\n    background: var(--error-container); color: var(--on-error-container);\n    border: none; border-radius: var(--radius-default);\n    font-family: 'Inter', sans-serif; font-size: 13px; font-weight: 700;\n    cursor: pointer; display: flex; align-items: center; justify-content: center; gap: 8px;\n    transition: all 0.15s ease;\n}\n.exam-cancel-btn:hover { filter: brightness(0.98); }\n.exam-cancel-btn:active { transform: scale(0.98); }\n\n/* ============================================\n   VIEW: Settings\n   ============================================ */\n.settings-content {\n    display: flex; flex-direction: column; gap: 20px;\n}\n.settings-group {\n    display: flex; flex-direction: column; gap: 4px;\n}\n.setting-item {\n    display: flex; align-items: center; justify-content: space-between;\n    padding: 12px;\n    background: var(--surface-container-low);\n    border-radius: var(--radius-default);\n    border: 1px solid rgba(195, 198, 215, 0.2);\n}\n.setting-info { display: flex; flex-direction: column; gap: 2px; }\n.setting-label { font-size: 13px; font-weight: 500; color: var(--on-surface); }\n.setting-desc { font-size: 11px; color: var(--on-surface-variant); }\n.toggle {\n    position: relative; display: inline-block;\n    width: 40px; height: 22px;\n}\n.toggle input { opacity: 0; width: 0; height: 0; }\n.toggle-slider {\n    position: absolute; cursor: pointer;\n    top: 0; left: 0; right: 0; bottom: 0;\n    background: var(--outline-variant);\n    border-radius: var(--radius-full);\n    transition: 0.3s;\n}\n.toggle-slider::before {\n    content: ''; position: absolute;\n    height: 16px; width: 16px;\n    left: 3px; bottom: 3px;\n    background: white; border-radius: 50%;\n    transition: 0.3s;\n}\n.toggle input:checked + .toggle-slider { background: var(--primary); }\n.toggle input:checked + .toggle-slider::before { transform: translateX(18px); }\n.btn-danger {\n    display: flex; align-items: center; justify-content: center; gap: 8px;\n    padding: 12px;\n    background: var(--error-container); color: var(--on-error-container);\n    border: none; border-radius: var(--radius-default);\n    font-family: 'Inter', sans-serif; font-size: 13px; font-weight: 600;\n    cursor: pointer; transition: all 0.15s ease;\n}\n.btn-danger:hover { opacity: 0.9; }\n.btn-danger:active { transform: scale(0.98); }\n.btn-danger .material-symbols-outlined { font-size: 18px; }\n";
  const PANEL_HTML = "<div class=\"plugin-container\">\n\n        <!-- ============================================\n             VIEW: History (Default)\n             ============================================ -->\n        <div class=\"view\" id=\"view-timer\">\n            <!-- TopAppBar -->\n            <header class=\"top-app-bar\">\n                <div class=\"app-bar-left\">\n                    <span class=\"app-title\">PTA Timer</span>\n                </div>\n                <div class=\"app-bar-right\">\n                    <button class=\"icon-btn\" id=\"nav-exams-btn\" title=\"考试列表\">\n                        <span class=\"material-symbols-outlined\">assignment</span>\n                    </button>\n                    <button class=\"icon-btn\" id=\"pin-btn\" title=\"固定\">\n                        <span class=\"material-symbols-outlined\">push_pin</span>\n                    </button>\n                    <button class=\"icon-btn\" id=\"settings-btn\" title=\"设置\">\n                        <span class=\"material-symbols-outlined\">settings</span>\n                    </button>\n                </div>\n            </header>\n\n            <!-- Main Content -->\n            <main class=\"view-content history-content\">\n                <section class=\"history-hero\">\n                    <div>\n                        <p class=\"history-eyebrow\">学习历史</p>\n                        <h1 class=\"history-title\">历史记录</h1>\n                    </div>\n                    <span class=\"history-hero-icon material-symbols-outlined\">history</span>\n                </section>\n\n                <!-- Sessions List -->\n                <div class=\"quick-stats history-panel\">\n                    <div class=\"stats-header history-stats-header\">\n                        <span class=\"stats-label\">做题记录</span>\n                        <span class=\"stats-hint\">按时间倒序排列</span>\n                    </div>\n                    <div class=\"sessions-list\" id=\"sessions-list\">\n                        <div class=\"empty-state\">\n                            <span class=\"material-symbols-outlined icon-empty\">history</span>\n                            <span class=\"empty-text\">暂无记录</span>\n                        </div>\n                    </div>\n                </div>\n            </main>\n\n            <!-- Footer -->\n            <footer class=\"app-footer\">\n                <div class=\"footer-status\">\n                    <div class=\"status-dot\" id=\"status-dot\"></div>\n                    <span class=\"status-text\" id=\"status-text\">未连接</span>\n                </div>\n                <span class=\"version-text\">v2.0.0</span>\n            </footer>\n        </div>\n\n        <!-- ============================================\n             VIEW: Exam List\n             ============================================ -->\n        <div class=\"view\" id=\"view-exam-list\" style=\"display: none;\">\n            <header class=\"top-app-bar\">\n                <div class=\"app-bar-left\">\n                    <button class=\"icon-btn\" id=\"exam-list-back-btn\">\n                        <span class=\"material-symbols-outlined\">arrow_back</span>\n                    </button>\n                    <span class=\"app-title\">PTA Timer</span>\n                </div>\n                <div class=\"app-bar-right\">\n                    <button class=\"icon-btn\" title=\"固定\">\n                        <span class=\"material-symbols-outlined\">push_pin</span>\n                    </button>\n                </div>\n            </header>\n\n            <main class=\"view-content exam-list-content\">\n                <!-- Contextual Header -->\n                <div class=\"context-header\">\n                    <h1 class=\"context-title\">考试列表</h1>\n                    <div class=\"search-bar\">\n                        <span class=\"material-symbols-outlined search-icon\">search</span>\n                        <input type=\"text\" class=\"search-input\" id=\"exam-search-input\" placeholder=\"搜索考试...\">\n                    </div>\n                    <button class=\"btn-current-page-problems\" id=\"load-current-page-problems-btn\">\n                        <span class=\"material-symbols-outlined\">playlist_add_check</span>\n                        从当前页面选择题目\n                    </button>\n                </div>\n\n                <!-- Scrollable Exam List -->\n                <div class=\"exam-list-scroll\" id=\"exam-list-scroll\">\n                    <!-- Items will be rendered by JS -->\n                </div>\n            </main>\n\n            <!-- Footer: Return to Timer -->\n            <footer class=\"exam-list-footer\">\n                <button class=\"btn-return-timer\" id=\"return-timer-btn\">\n                    <span class=\"material-symbols-outlined\">history</span>\n                    返回历史记录\n                </button>\n            </footer>\n        </div>\n\n        <!-- ============================================\n             VIEW: Exam Problem List\n             ============================================ -->\n        <div class=\"view\" id=\"view-exam-problems\" style=\"display: none;\">\n            <header class=\"top-app-bar\">\n                <div class=\"app-bar-left\">\n                    <span class=\"app-title\">PTA Timer</span>\n                </div>\n                <div class=\"app-bar-right\">\n                    <button class=\"icon-btn\" title=\"固定\">\n                        <span class=\"material-symbols-outlined\">push_pin</span>\n                    </button>\n                </div>\n            </header>\n\n            <div class=\"exam-problems-layout\">\n                <!-- Main Canvas -->\n                <main class=\"exam-problems-main\">\n                    <!-- Back to Exams -->\n                    <div class=\"exam-problems-back\">\n                        <button class=\"btn-text-primary\" id=\"back-to-exams-btn\">\n                            <span class=\"material-symbols-outlined\">arrow_back</span>\n                            返回考试列表\n                        </button>\n                    </div>\n\n                    <!-- Exam Title -->\n                    <div class=\"exam-title-section\">\n                        <h1 class=\"exam-title\" id=\"exam-title\">考试名称</h1>\n                        <div class=\"exam-meta\">\n                            <span class=\"exam-status-badge\" id=\"exam-status-badge\">进行中</span>\n                            <span class=\"exam-duration\">\n                                <span class=\"material-symbols-outlined\" style=\"font-size: 14px;\">schedule</span>\n                                <span id=\"exam-duration-text\">180 min</span>\n                            </span>\n                        </div>\n                    </div>\n\n                    <div class=\"custom-exam-toolbar\" id=\"custom-exam-toolbar\" style=\"display: none;\">\n                        <span class=\"custom-exam-count\" id=\"custom-exam-count\">已选择 0 题</span>\n                        <div class=\"custom-exam-actions\">\n                            <input class=\"custom-exam-duration-input\" id=\"custom-exam-duration-input\" type=\"number\" min=\"1\" max=\"999\" placeholder=\"限时/分钟\">\n                            <button class=\"btn-text-primary\" id=\"select-all-problems-btn\">全选</button>\n                            <button class=\"problem-list-start-btn\" id=\"create-custom-exam-btn\">创建考试</button>\n                            <button class=\"btn-danger-sm\" id=\"delete-custom-exam-btn\" style=\"display: none;\">删除考试</button>\n                        </div>\n                    </div>\n\n                    <!-- Problem List -->\n                    <div class=\"problem-list\" id=\"problem-list\">\n                        <!-- Items rendered by JS -->\n                    </div>\n\n                    <!-- Progress Stats -->\n                    <div class=\"progress-stats\">\n                        <div class=\"progress-info\">\n                            <span class=\"progress-label\">总进度</span>\n                            <span class=\"progress-value\" id=\"progress-value\">0 / 0 pts</span>\n                        </div>\n                        <div class=\"progress-bar-container\">\n                            <div class=\"progress-bar-fill\" id=\"progress-bar-fill\"></div>\n                        </div>\n                        <span class=\"progress-percent\" id=\"progress-percent\">0%</span>\n                    </div>\n                </main>\n            </div>\n        </div>\n\n        <!-- ============================================\n             VIEW: Exam Mode (Minimalist Compact)\n             ============================================ -->\n        <div class=\"view\" id=\"view-exam-mode\" style=\"display: none;\">\n            <!-- Global Exam Timer Bar -->\n            <div class=\"global-timer-bar\">\n                <div class=\"global-timer-left\">\n                    <span class=\"material-symbols-outlined\" style=\"font-size: 14px; color: var(--primary);\">schedule</span>\n                    <span class=\"global-timer-label\">全局</span>\n                </div>\n                <div class=\"global-timer-right\">\n                    <span class=\"global-timer-value\" id=\"exam-global-timer\">00:00:00</span>\n                    <div class=\"global-progress-bar\">\n                        <div class=\"global-progress-fill\" id=\"exam-global-progress\"></div>\n                    </div>\n                </div>\n            </div>\n\n            <!-- TopAppBar -->\n            <header class=\"top-app-bar compact\">\n                <div class=\"app-bar-left\">\n                    <span class=\"app-title-sm\">PTA</span>\n                    <span class=\"exam-badge\">Exam</span>\n                </div>\n                <div class=\"app-bar-right\">\n                    <button class=\"icon-btn-sm\" title=\"固定\">\n                        <span class=\"material-symbols-outlined\" style=\"font-size: 16px;\">push_pin</span>\n                    </button>\n                    <button class=\"icon-btn-sm\" id=\"exam-mode-close-btn\" title=\"关闭\">\n                        <span class=\"material-symbols-outlined\" style=\"font-size: 16px;\">close</span>\n                    </button>\n                </div>\n            </header>\n\n            <!-- Main Content -->\n            <main class=\"exam-mode-content\">\n                <!-- Current Problem Header -->\n                <div class=\"exam-problem-header\">\n                    <div class=\"exam-problem-info\">\n                        <p class=\"exam-problem-index\" id=\"exam-problem-index\">题目 1/5</p>\n                        <h3 class=\"exam-problem-name\" id=\"exam-problem-name\">当前题目</h3>\n                    </div>\n                    <div class=\"exam-problem-difficulty\" id=\"exam-problem-difficulty\">Med</div>\n                </div>\n\n                <!-- Problem Timer -->\n                <div class=\"exam-timer-display\">\n                    <div class=\"exam-timer-info\">\n                        <p class=\"exam-timer-label\">考试剩余</p>\n                        <div class=\"exam-timer-value\" id=\"exam-timer-value\">00:00:00</div>\n                    </div>\n                    <div class=\"exam-timer-controls\">\n                        <button class=\"exam-ctrl-btn\" id=\"exam-play-pause-btn\" title=\"暂停\">\n                            <span class=\"material-symbols-outlined\">pause</span>\n                        </button>\n                        <button class=\"exam-ctrl-btn\" id=\"exam-reset-btn\" title=\"重置\">\n                            <span class=\"material-symbols-outlined\">history</span>\n                        </button>\n                    </div>\n                </div>\n\n                <!-- Navigation Controls -->\n                <div class=\"exam-nav-controls\">\n                    <button class=\"exam-nav-btn\" id=\"exam-prev-btn\">\n                        <span class=\"material-symbols-outlined\" style=\"font-size: 16px;\">chevron_left</span>\n                        上一题\n                    </button>\n                    <button class=\"exam-nav-btn\" id=\"exam-next-btn\">\n                        下一题\n                        <span class=\"material-symbols-outlined\" style=\"font-size: 16px;\">chevron_right</span>\n                    </button>\n                </div>\n\n                <!-- Submit Button -->\n                <button class=\"exam-submit-btn\" id=\"exam-submit-btn\">\n                    <span class=\"material-symbols-outlined\" style=\"font-size: 18px; font-variation-settings: 'FILL' 1;\">task_alt</span>\n                    提交考试\n                </button>\n                <button class=\"exam-cancel-btn\" id=\"exam-cancel-btn\">\n                    <span class=\"material-symbols-outlined\" style=\"font-size: 18px;\">close</span>\n                    取消考试\n                </button>\n            </main>\n\n            <!-- Footer -->\n            <footer class=\"app-footer compact\">\n                <div class=\"footer-status\">\n                    <div class=\"status-dot connected\"></div>\n                    <span class=\"status-text\">已连接</span>\n                </div>\n                <span class=\"version-text\">v2.0.0</span>\n            </footer>\n        </div>\n\n        <!-- ============================================\n             VIEW: Settings\n             ============================================ -->\n        <div class=\"view\" id=\"view-settings\" style=\"display: none;\">\n            <header class=\"top-app-bar\">\n                <div class=\"app-bar-left\">\n                    <button class=\"icon-btn\" id=\"settings-back-btn\">\n                        <span class=\"material-symbols-outlined\">arrow_back</span>\n                    </button>\n                    <span class=\"app-title\">设置</span>\n                </div>\n                <div class=\"app-bar-right\"></div>\n            </header>\n\n            <main class=\"view-content\">\n                <div class=\"settings-content\">\n                    <div class=\"settings-group\">\n                        <div class=\"setting-item\">\n                            <div class=\"setting-info\">\n                                <span class=\"setting-label\">自动开始计时</span>\n                                <span class=\"setting-desc\">进入题目页面时自动计时</span>\n                            </div>\n                            <label class=\"toggle\">\n                                <input type=\"checkbox\" id=\"auto-start-toggle\" checked>\n                                <span class=\"toggle-slider\"></span>\n                            </label>\n                        </div>\n                        <div class=\"setting-item\">\n                            <div class=\"setting-info\">\n                                <span class=\"setting-label\">倒计时提醒</span>\n                                <span class=\"setting-desc\">剩余时间不足时发出提醒</span>\n                            </div>\n                            <label class=\"toggle\">\n                                <input type=\"checkbox\" id=\"notification-toggle\" checked>\n                                <span class=\"toggle-slider\"></span>\n                            </label>\n                        </div>\n                    </div>\n                    <div class=\"settings-group\">\n                        <button class=\"btn-danger\" id=\"clear-data-btn\">\n                            <span class=\"material-symbols-outlined\">delete</span>\n                            清除所有数据\n                        </button>\n                    </div>\n                </div>\n            </main>\n        </div>\n\n        <div class=\"confirm-overlay\" id=\"delete-confirm-overlay\" aria-hidden=\"true\">\n            <div class=\"confirm-dialog\" role=\"dialog\" aria-modal=\"true\" aria-labelledby=\"delete-confirm-title\">\n                <div class=\"confirm-icon\">\n                    <span class=\"material-symbols-outlined\">delete</span>\n                </div>\n                <div class=\"confirm-copy\">\n                    <h2 class=\"confirm-title\" id=\"delete-confirm-title\">删除这条记录?</h2>\n                    <p class=\"confirm-message\">删除后无法恢复,确定要继续吗?</p>\n                </div>\n                <div class=\"confirm-actions\">\n                    <button class=\"confirm-btn confirm-cancel\" id=\"delete-confirm-cancel\">取消</button>\n                    <button class=\"confirm-btn confirm-delete\" id=\"delete-confirm-ok\">删除</button>\n                </div>\n            </div>\n        </div>\n    </div>";

  let activeContext = 'runtime';
  let panelHost = null;
  let panelShadow = null;
  let panelInitialized = false;
  let panelDocument = null;
  let initializePanelApp = null;

  function callSoon(callback, value) {
    if (typeof callback !== 'function') return;
    setTimeout(() => callback(value), 0);
  }

  function cloneValue(value) {
    if (value === undefined) return undefined;
    try {
      return JSON.parse(JSON.stringify(value));
    } catch (error) {
      return value;
    }
  }

  function hasGM(name) {
    return typeof globalThis[name] === 'function';
  }

  function storageKey(key) {
    return `ptaTimer.${key}`;
  }

  function readValue(key) {
    if (hasGM('GM_getValue')) return cloneValue(GM_getValue(key));
    const raw = localStorage.getItem(storageKey(key));
    if (raw === null) return undefined;
    try { return JSON.parse(raw); } catch (error) { return raw; }
  }

  function writeValue(key, value) {
    if (hasGM('GM_setValue')) {
      GM_setValue(key, cloneValue(value));
      return;
    }
    localStorage.setItem(storageKey(key), JSON.stringify(value));
  }

  function deleteValue(key) {
    if (hasGM('GM_deleteValue')) {
      GM_deleteValue(key);
      return;
    }
    localStorage.removeItem(storageKey(key));
  }

  function listValues() {
    if (hasGM('GM_listValues')) return GM_listValues();
    const prefix = 'ptaTimer.';
    return Object.keys(localStorage)
      .filter((key) => key.startsWith(prefix))
      .map((key) => key.slice(prefix.length));
  }

  function normalizeKeys(keys) {
    if (Array.isArray(keys)) return keys;
    if (typeof keys === 'string') return [keys];
    if (keys && typeof keys === 'object') return Object.keys(keys);
    return listValues();
  }

  const storageListeners = new Set();
  const runtimeListeners = [];
  const contentListeners = [];

  function emitStorageChanges(changes) {
    if (!Object.keys(changes).length) return;
    storageListeners.forEach((listener) => {
      try { listener(changes, 'local'); } catch (error) { console.error('[PTA Timer] storage listener failed', error); }
    });
  }

  function dispatchMessage(listeners, request, callback) {
    if (!listeners.length) {
      compat.runtime.lastError = { message: 'No receiving end' };
      callSoon(callback);
      setTimeout(() => { compat.runtime.lastError = null; }, 0);
      return;
    }

    let responded = false;
    let asyncResponse = false;
    compat.runtime.lastError = null;

    const sendResponse = (response) => {
      if (responded) return;
      responded = true;
      compat.runtime.lastError = null;
      callSoon(callback, response);
    };

    for (const listener of [...listeners]) {
      try {
        const result = listener(request, { tab: { id: 1, url: location.href } }, sendResponse);
        if (result === true) asyncResponse = true;
        if (responded) return;
      } catch (error) {
        compat.runtime.lastError = { message: error.message };
        console.error('[PTA Timer] message listener failed', error);
      }
    }

    if (!asyncResponse && !responded) callSoon(callback);
  }

  const compat = {
    storage: {
      local: {
        get(keys, callback) {
          const result = {};
          if (keys == null) {
            listValues().forEach((key) => { result[key] = readValue(key); });
          } else if (typeof keys === 'string') {
            result[keys] = readValue(keys);
          } else if (Array.isArray(keys)) {
            keys.forEach((key) => { result[key] = readValue(key); });
          } else if (typeof keys === 'object') {
            Object.keys(keys).forEach((key) => {
              const value = readValue(key);
              result[key] = value === undefined ? keys[key] : value;
            });
          }
          callSoon(callback, result);
        },
        set(items, callback) {
          const changes = {};
          Object.entries(items || {}).forEach(([key, value]) => {
            const oldValue = readValue(key);
            writeValue(key, value);
            changes[key] = { oldValue, newValue: cloneValue(value) };
          });
          callSoon(callback);
          setTimeout(() => emitStorageChanges(changes), 0);
        },
        remove(keys, callback) {
          const changes = {};
          normalizeKeys(keys).forEach((key) => {
            const oldValue = readValue(key);
            deleteValue(key);
            changes[key] = { oldValue, newValue: undefined };
          });
          callSoon(callback);
          setTimeout(() => emitStorageChanges(changes), 0);
        },
        clear(callback) {
          const changes = {};
          listValues().forEach((key) => {
            const oldValue = readValue(key);
            deleteValue(key);
            changes[key] = { oldValue, newValue: undefined };
          });
          callSoon(callback);
          setTimeout(() => emitStorageChanges(changes), 0);
        }
      },
      onChanged: {
        addListener(listener) { storageListeners.add(listener); },
        removeListener(listener) { storageListeners.delete(listener); }
      }
    },
    runtime: {
      lastError: null,
      onMessage: {
        addListener(listener) {
          if (activeContext === 'content') contentListeners.push(listener);
          else runtimeListeners.push(listener);
        }
      },
      onStartup: { addListener() {} },
      onInstalled: { addListener() {} },
      sendMessage(request, callback) { dispatchMessage(runtimeListeners, request, callback); },
      getURL(path) {
        if (/icon48\.png$/.test(path)) return ICONS.icon48;
        if (/icon128\.png$/.test(path)) return ICONS.icon128;
        if (/icon16\.png$/.test(path)) return ICONS.icon16;
        return ICONS.main;
      }
    },
    tabs: {
      query(_queryInfo, callback) { callSoon(callback, [{ id: 1, active: true, url: location.href }]); },
      sendMessage(_tabId, request, callback) { dispatchMessage(contentListeners, request, callback); },
      update(_tabId, updateProperties, callback) {
        if (updateProperties && updateProperties.url) location.assign(updateProperties.url);
        callSoon(callback, { id: 1, url: updateProperties?.url || location.href });
      }
    },
    notifications: {
      create(options, callback) {
        notify(options?.title || 'PTA Timer', options?.message || options?.text || '');
        callSoon(callback, String(Date.now()));
      }
    }
  };

  function notify(title, text) {
    if (hasGM('GM_notification')) {
      GM_notification({ title, text, image: ICONS.icon48, timeout: 6000 });
      return;
    }
    if ('Notification' in window && Notification.permission === 'granted') {
      new Notification(title, { body: text, icon: ICONS.icon48 });
    }
  }

  function injectFonts() {
    if (document.querySelector('link[data-pta-timer-fonts]')) return;
    const link = document.createElement('link');
    link.dataset.ptaTimerFonts = 'true';
    link.rel = 'stylesheet';
    link.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=JetBrains+Mono:wght@500;600&family=Material+Symbols+Outlined:wght,[email protected],0..1&display=swap';
    document.head.appendChild(link);
  }

  function addStyle(css) {
    if (hasGM('GM_addStyle')) {
      GM_addStyle(css);
      return;
    }
    const style = document.createElement('style');
    style.textContent = css;
    document.head.appendChild(style);
  }

  const ICON_FALLBACKS = {
    assignment: '▣',
    history: '↺',
    close: '×',
    push_pin: '⌖',
    settings: '⚙',
    arrow_back: '←',
    playlist_add_check: '☑',
    search: '⌕',
    schedule: '◷',
    pause: 'Ⅱ',
    play_arrow: '▶',
    replay: '↻',
    timer: '◴',
    dashboard: '▦',
    chevron_left: '‹',
    chevron_right: '›',
    task_alt: '✓',
    check: '✓',
    delete: '×',
    calendar_today: '□',
    timer_off: '◼'
  };

  function renderMaterialIcon(icon) {
    const current = icon.textContent.trim();
    const previousName = icon.dataset.icon;
    const previousGlyph = previousName ? ICON_FALLBACKS[previousName] || previousName : '';
    const name = current && current !== previousGlyph ? current : previousName;
    if (!name) return;

    const glyph = ICON_FALLBACKS[name] || name;
    icon.dataset.icon = name;
    icon.setAttribute('aria-hidden', 'true');
    if (icon.textContent !== glyph) icon.textContent = glyph;
  }

  function withIconFallbacks(html) {
    return html.replace(/<span([^>]*class="[^"]*\bmaterial-symbols-outlined\b[^"]*"[^>]*)>([a-z0-9_]+)<\/span>/g, (match, attrs, name) => {
      const glyph = ICON_FALLBACKS[name];
      if (!glyph) return match;
      const nextAttrs = /\sdata-icon=/.test(attrs) ? attrs : `${attrs} data-icon="${name}"`;
      return `<span${nextAttrs}>${glyph}</span>`;
    });
  }

  function renderMaterialIcons(root = document) {
    root.querySelectorAll?.('.material-symbols-outlined').forEach(renderMaterialIcon);
  }

  function watchMaterialIcons(root) {
    renderMaterialIcons(root);
    const observer = new MutationObserver((mutations) => {
      mutations.forEach((mutation) => {
        if (mutation.type === 'characterData') {
          const parent = mutation.target.parentElement;
          const icon = parent?.classList?.contains('material-symbols-outlined') ? parent : parent?.closest?.('.material-symbols-outlined');
          if (icon) renderMaterialIcon(icon);
          return;
        }

        const target = mutation.target;
        if (target?.classList?.contains?.('material-symbols-outlined')) {
          renderMaterialIcon(target);
        }

        mutation.addedNodes.forEach((node) => {
          if (node.nodeType !== 1) return;
          if (node.classList?.contains('material-symbols-outlined')) renderMaterialIcon(node);
          renderMaterialIcons(node);
        });
      });
    });
    observer.observe(root, { childList: true, subtree: true, characterData: true });
  }

  function refreshIconFallbacks() {
    renderMaterialIcons(document);
    if (panelShadow) renderMaterialIcons(panelShadow);
  }

  function refreshIconFallbacksSoon() {
    refreshIconFallbacks();
    [50, 150, 350, 800, 1500].forEach((delay) => setTimeout(refreshIconFallbacks, delay));
  }

  function runModule(context, factory) {
    const previous = activeContext;
    activeContext = context;
    try { factory(); } finally { activeContext = previous; }
  }

  function ensurePanel() {
    if (panelHost) return panelHost;

    panelHost = document.createElement('div');
    panelHost.id = 'pta-timer-panel-host';
    panelShadow = panelHost.attachShadow({ mode: 'open' });
    panelShadow.innerHTML = withIconFallbacks(`
      <style>${PANEL_CSS}</style>
      <div class="pta-panel-backdrop" data-panel-close></div>
      <div class="pta-panel-shell" role="dialog" aria-modal="true" aria-label="PTA Timer">
        <button class="pta-panel-close" id="pta-panel-close" title="关闭">
          <span class="material-symbols-outlined">close</span>
        </button>
        ${PANEL_HTML}
      </div>
    `);
    document.body.appendChild(panelHost);

    panelDocument = {
      getElementById: (id) => panelShadow.getElementById(id),
      querySelector: (selector) => panelShadow.querySelector(selector),
      querySelectorAll: (selector) => panelShadow.querySelectorAll(selector),
      createElement: (...args) => document.createElement(...args),
      addEventListener: (...args) => document.addEventListener(...args),
      removeEventListener: (...args) => document.removeEventListener(...args)
    };

    panelShadow.querySelector('[data-panel-close]').addEventListener('click', closePanel);
    panelShadow.getElementById('pta-panel-close').addEventListener('click', closePanel);
    panelShadow.querySelectorAll('[title="固定"]').forEach((button) => { button.style.display = 'none'; });
    watchMaterialIcons(panelShadow);

    if (!panelInitialized && typeof initializePanelApp === 'function') {
      panelInitialized = true;
      initializePanelApp();
      refreshIconFallbacksSoon();
    }

    return panelHost;
  }

  function openPanel() {
    ensurePanel();
    panelHost.classList.add('visible');
    refreshIconFallbacksSoon();
  }

  function closePanel() {
    if (panelHost) panelHost.classList.remove('visible');
  }

  function installPanelLauncher() {
    if (!document.body || document.getElementById('pta-timer-launcher')) return;
    const launcher = document.createElement('button');
    launcher.id = 'pta-timer-launcher';
    launcher.type = 'button';
    launcher.title = '打开 PTA Timer 面板';
    launcher.textContent = 'PTA';
    launcher.addEventListener('click', openPanel);
    document.body.appendChild(launcher);
  }

  function registerMenuCommand() {
    if (hasGM('GM_registerMenuCommand')) {
      GM_registerMenuCommand('打开 PTA Timer 面板', openPanel);
    }
  }

  window.PTATimerUserscript = { openPanel, closePanel };

  injectFonts();
  addStyle(CONTENT_CSS + `
#pta-timer-launcher {
  position: fixed;
  right: 16px;
  bottom: 18px;
  z-index: 9998;
  width: 44px;
  height: 44px;
  border: 1px solid rgba(255,255,255,0.55);
  border-radius: 14px;
  background: rgba(0, 74, 198, 0.92);
  color: #fff;
  box-shadow: 0 12px 30px rgba(0, 74, 198, 0.24);
  font: 800 11px 'Inter', -apple-system, BlinkMacSystemFont, sans-serif;
  letter-spacing: 0.08em;
  cursor: pointer;
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
}
#pta-timer-launcher:hover { background: #2563eb; transform: translateY(-1px); }
`);
  registerMenuCommand();
  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', installPanelLauncher, { once: true });
  } else {
    installPanelLauncher();
  }

  runModule('background', function () {
// PTA Timer - Background Service Worker
// Manages timer state and session persistence

let timerData = {
  isRunning: false,
  isPaused: false,
  startTime: null,
  pauseStartTime: null,
  pausedDuration: 0,
  currentSession: null,
  totalTime: 0,
  sessions: []
};

let storageLoaded = false;
let storageLoadInProgress = false;
let pendingLoadCallbacks = [];

// Message handler
compat.runtime.onMessage.addListener((request, sender, sendResponse) => {
  loadStoredData(() => {
    normalizeTimerData();

    switch (request.action) {
      case 'startTimer':
        startTimer(request.problemId, request.problemName);
        sendResponse({ success: true });
        break;
      case 'stopTimer':
        stopTimer(false);
        sendResponse({ success: true });
        break;
      case 'completeTimer':
        stopTimer(true);
        sendResponse({ success: true });
        break;
      case 'togglePauseTimer':
        sendResponse(togglePauseTimer());
        break;
      case 'deleteSession':
        deleteSession(request.sessionIndex);
        sendResponse({ success: true });
        break;
      case 'startCustomExam':
        startCustomExam(request.exam, () => sendResponse({ success: true }));
        break;
      case 'getActiveCustomExam':
        getActiveCustomExam((exam) => sendResponse({ success: true, exam: exam }));
        break;
      case 'setCurrentExamProblem':
        setCurrentExamProblem(request.problemIndex, () => sendResponse({ success: true }));
        break;
      case 'markExamProblemSolved':
        markExamProblemSolved(request.problemId, () => sendResponse({ success: true }));
        break;
      case 'finishCustomExam':
        finishCustomExam(() => sendResponse({ success: true }));
        break;
      case 'clearAllData':
        clearAllData(() => sendResponse({ success: true }));
        break;
      case 'getTimerData':
        sendResponse(timerData);
        break;
      case 'setCountdown':
        setCountdown(request.duration);
        sendResponse({ success: true });
        break;
      case 'getCountdown':
        sendResponse({ countdown: countdownData });
        break;
      default:
        sendResponse({ success: false });
    }
  });
  return true; // Keep message channel open for async responses
});

function loadStoredData(callback) {
  if (storageLoaded) {
    callback();
    return;
  }

  pendingLoadCallbacks.push(callback);
  if (storageLoadInProgress) {
    return;
  }

  storageLoadInProgress = true;
  compat.storage.local.get(['timerData', 'countdownData'], (result) => {
    if (result.timerData) {
      timerData = { ...timerData, ...result.timerData };
    }
    if (result.countdownData) {
      countdownData = result.countdownData;
      if (countdownData.isRunning) {
        startCountdownLoop();
      }
    }
    storageLoaded = true;
    storageLoadInProgress = false;

    const callbacks = pendingLoadCallbacks;
    pendingLoadCallbacks = [];
    callbacks.forEach((pendingCallback) => pendingCallback());
  });
}

function normalizeTimerData() {
  timerData.sessions = Array.isArray(timerData.sessions) ? timerData.sessions : [];
  timerData.isPaused = Boolean(timerData.isPaused);
  timerData.pausedDuration = Number(timerData.pausedDuration) || 0;
  timerData.pauseStartTime = timerData.pauseStartTime || null;
  timerData.sessions.forEach((session) => {
    if (!session.problemName) {
      session.problemName = session.problemId || '未知题目';
    }
  });
  if (timerData.currentSession && !timerData.currentSession.problemName) {
    timerData.currentSession.problemName = timerData.currentSession.problemId || '计时中...';
  }
}

function startTimer(problemId, problemName) {
  if (timerData.isRunning && timerData.currentSession?.problemId === problemId) {
    if (problemName && timerData.currentSession.problemName !== problemName) {
      timerData.currentSession.problemName = problemName;
      compat.storage.local.set({ timerData: timerData });
    }
    return;
  }

  if (timerData.isRunning) {
    stopTimer(false);
  }

  timerData.isRunning = true;
  timerData.isPaused = false;
  timerData.startTime = Date.now();
  timerData.pauseStartTime = null;
  timerData.pausedDuration = 0;
  timerData.currentSession = {
    problemId: problemId,
    problemName: problemName || problemId,
    startTime: timerData.startTime,
    endTime: null
  };
  compat.storage.local.set({ timerData: timerData });
}

function stopTimer(saveSession) {
  if (timerData.isRunning && timerData.currentSession) {
    const endTime = Date.now();
    const pausedDuration = timerData.pausedDuration +
      (timerData.isPaused && timerData.pauseStartTime ? endTime - timerData.pauseStartTime : 0);

    const completedSession = {
      ...timerData.currentSession,
      endTime: endTime,
      duration: Math.max(0, endTime - timerData.currentSession.startTime - pausedDuration)
    };

    timerData.isRunning = false;
    timerData.isPaused = false;

    if (saveSession) {
      timerData.sessions.push(completedSession);
      timerData.totalTime += completedSession.duration;
    }

    timerData.currentSession = null;
    timerData.startTime = null;
    timerData.pauseStartTime = null;
    timerData.pausedDuration = 0;

    compat.storage.local.set({ timerData: timerData });
  }
}

function togglePauseTimer() {
  if (!timerData.isRunning || !timerData.currentSession) {
    return { success: false, timerData: timerData };
  }

  if (timerData.isPaused) {
    timerData.pausedDuration += Date.now() - timerData.pauseStartTime;
    timerData.pauseStartTime = null;
    timerData.isPaused = false;
  } else {
    timerData.pauseStartTime = Date.now();
    timerData.isPaused = true;
  }

  compat.storage.local.set({ timerData: timerData });
  return { success: true, timerData: timerData };
}

function deleteSession(sessionIndex) {
  if (!Number.isInteger(sessionIndex) || !timerData.sessions[sessionIndex]) {
    return;
  }

  const [session] = timerData.sessions.splice(sessionIndex, 1);
  timerData.totalTime = Math.max(0, timerData.totalTime - (session.duration || 0));
  compat.storage.local.set({ timerData: timerData });
}

function startCustomExam(exam, callback) {
  if (!exam) {
    callback();
    return;
  }

  const activeExam = {
    ...exam,
    currentProblemIndex: exam.currentProblemIndex || 0,
    problems: Array.isArray(exam.problems) ? exam.problems : []
  };

  compat.storage.local.remove(['customExamSelectionPreview'], () => {
    compat.storage.local.set({
      activeCustomExam: activeExam,
      isExamMode: true,
      currentExam: activeExam,
      currentProblemIndex: activeExam.currentProblemIndex,
      examStartTime: activeExam.startedAt,
      examDuration: activeExam.duration
    }, callback);
  });
}

function getActiveCustomExam(callback) {
  compat.storage.local.get(['activeCustomExam'], (result) => {
    callback(result.activeCustomExam || null);
  });
}

function setCurrentExamProblem(problemIndex, callback) {
  compat.storage.local.get(['activeCustomExam', 'currentExam'], (result) => {
    const index = Number(problemIndex) || 0;
    const updates = { currentProblemIndex: index };

    if (result.activeCustomExam) {
      updates.activeCustomExam = { ...result.activeCustomExam, currentProblemIndex: index };
    }
    if (result.currentExam?.mode === 'custom') {
      updates.currentExam = { ...result.currentExam, currentProblemIndex: index };
    }

    compat.storage.local.set(updates, callback);
  });
}

function markExamProblemSolved(problemId, callback) {
  if (!problemId) {
    callback();
    return;
  }

  compat.storage.local.get(['activeCustomExam', 'currentExam'], (result) => {
    if (!result.activeCustomExam?.problems) {
      callback();
      return;
    }

    const solvedAt = Date.now();
    const latestSession = [...timerData.sessions].reverse().find((session) => session.problemId === problemId);
    const updateProblems = (problems) => problems.map((problem) => {
      if (problem.id !== problemId) {
        return problem;
      }
      return {
        ...problem,
        status: 'solved',
        solvedAt: solvedAt,
        duration: latestSession?.duration || problem.duration || 0
      };
    });

    const activeCustomExam = {
      ...result.activeCustomExam,
      problems: updateProblems(result.activeCustomExam.problems)
    };
    const updates = { activeCustomExam: activeCustomExam };

    if (result.currentExam?.mode === 'custom') {
      updates.currentExam = {
        ...result.currentExam,
        problems: updateProblems(result.currentExam.problems || [])
      };
    }

    compat.storage.local.set(updates, callback);
  });
}

function finishCustomExam(callback) {
  compat.storage.local.remove([
    'activeCustomExam',
    'customExamSelectionPreview',
    'isExamMode',
    'currentExam',
    'currentProblemIndex',
    'examStartTime',
    'examDuration'
  ], callback);
}

function clearAllData(callback) {
  compat.storage.local.clear(() => {
    timerData = {
      isRunning: false,
      isPaused: false,
      startTime: null,
      pauseStartTime: null,
      pausedDuration: 0,
      currentSession: null,
      totalTime: 0,
      sessions: []
    };
    countdownData = {
      isRunning: false,
      duration: 0,
      remaining: 0,
      startTime: null
    };
    storageLoaded = true;
    callback();
  });
}

// Countdown
let countdownData = {
  isRunning: false,
  duration: 0,
  remaining: 0,
  startTime: null
};

function setCountdown(duration) {
  countdownData.duration = duration * 60 * 1000;
  countdownData.remaining = countdownData.duration;
  countdownData.startTime = Date.now();
  countdownData.isRunning = true;

  startCountdownLoop();
  compat.storage.local.set({ countdownData: countdownData });
}

function startCountdownLoop() {
  if (countdownData.isRunning) {
    const elapsed = Date.now() - countdownData.startTime;
    countdownData.remaining = Math.max(0, countdownData.duration - elapsed);

    if (countdownData.remaining <= 0) {
      countdownData.isRunning = false;
      compat.notifications.create({
        type: 'basic',
        iconUrl: 'icons/icon48.png',
        title: 'PTA Timer',
        message: '倒计时结束!'
      });
    } else {
      setTimeout(startCountdownLoop, 1000);
    }

    compat.storage.local.set({ countdownData: countdownData });
  }
}

// Load persisted data on startup
compat.runtime.onStartup.addListener(() => {
  compat.storage.local.get(['timerData', 'countdownData'], (result) => {
    if (result.timerData) {
      timerData = result.timerData;
    }
    if (result.countdownData) {
      countdownData = result.countdownData;
      if (countdownData.isRunning) {
        startCountdownLoop();
      }
    }
  });
});

// Also load on install/update
compat.runtime.onInstalled.addListener(() => {
  compat.storage.local.get(['timerData'], (result) => {
    if (result.timerData) {
      timerData = result.timerData;
    }
  });
});

  });

  runModule('popup', function () {
// PTA Timer - Popup Script
// Multi-View: Timer, Exam List, Exam Problems, Exam Mode, Settings

initializePanelApp = function initPanelApp() {
  const document = panelDocument;
  // ============================================
  // State
  // ============================================
  let currentView = 'timer';
  let countdownVisible = false;
  let examData = []; // Loaded exam sessions
  let currentExam = null; // Currently selected exam
  let activeCustomExam = null;
  let currentProblemIndex = 0; // Current problem in exam mode
  let pendingConfirmAction = null;
  let selectedProblemIds = new Set();
  let customExamSelectionMode = false;

  // ============================================
  // Views
  // ============================================
  const views = {
    timer: document.getElementById('view-timer'),
    examList: document.getElementById('view-exam-list'),
    examProblems: document.getElementById('view-exam-problems'),
    examMode: document.getElementById('view-exam-mode'),
    settings: document.getElementById('view-settings')
  };

  // ============================================
  // DOM References - Timer View
  // ============================================
  const timerValue = document.getElementById('timer-value');
  const playPauseBtn = document.getElementById('play-pause-btn');
  const resetBtn = document.getElementById('reset-btn');
  const countdownBtn = document.getElementById('countdown-btn');
  const countdownInputSection = document.getElementById('countdown-input-section');
  const countdownInput = document.getElementById('countdown-input');
  const startCountdownBtn = document.getElementById('start-countdown-btn');
  const cancelCountdownBtn = document.getElementById('cancel-countdown-btn');
  const sessionsList = document.getElementById('sessions-list');
  const statusDot = document.getElementById('status-dot');
  const statusText = document.getElementById('status-text');
  const navExamsBtn = document.getElementById('nav-exams-btn');
  const settingsBtn = document.getElementById('settings-btn');
  const confirmOverlay = document.getElementById('delete-confirm-overlay');
  const confirmCancel = document.getElementById('delete-confirm-cancel');
  const confirmOk = document.getElementById('delete-confirm-ok');
  const confirmTitle = document.getElementById('delete-confirm-title');
  const confirmMessage = document.querySelector('.confirm-message');
  const confirmIcon = document.querySelector('.confirm-icon .material-symbols-outlined');

  // DOM References - Exam List View
  const examListBackBtn = document.getElementById('exam-list-back-btn');
  const examSearchInput = document.getElementById('exam-search-input');
  const examListScroll = document.getElementById('exam-list-scroll');
  const returnTimerBtn = document.getElementById('return-timer-btn');
  const loadCurrentPageProblemsBtn = document.getElementById('load-current-page-problems-btn');

  // DOM References - Exam Problems View
  const backToExamsBtn = document.getElementById('back-to-exams-btn');
  const examTitle = document.getElementById('exam-title');
  const examStatusBadge = document.getElementById('exam-status-badge');
  const examDurationText = document.getElementById('exam-duration-text');
  const examDurationMeta = examDurationText?.closest('.exam-duration');
  const problemList = document.getElementById('problem-list');
  const customExamToolbar = document.getElementById('custom-exam-toolbar');
  const customExamCount = document.getElementById('custom-exam-count');
  const customExamDurationInput = document.getElementById('custom-exam-duration-input');
  const selectAllProblemsBtn = document.getElementById('select-all-problems-btn');
  const createCustomExamBtn = document.getElementById('create-custom-exam-btn');
  const deleteCustomExamBtn = document.getElementById('delete-custom-exam-btn');
  const progressValue = document.getElementById('progress-value');
  const progressBarFill = document.getElementById('progress-bar-fill');
  const progressPercent = document.getElementById('progress-percent');

  // DOM References - Exam Mode View
  const examGlobalTimer = document.getElementById('exam-global-timer');
  const examGlobalProgress = document.getElementById('exam-global-progress');
  const examProblemIndex = document.getElementById('exam-problem-index');
  const examProblemName = document.getElementById('exam-problem-name');
  const examProblemDifficulty = document.getElementById('exam-problem-difficulty');
  const examTimerValue = document.getElementById('exam-timer-value');
  const examPlayPauseBtn = document.getElementById('exam-play-pause-btn');
  const examResetBtn = document.getElementById('exam-reset-btn');
  const examPrevBtn = document.getElementById('exam-prev-btn');
  const examNextBtn = document.getElementById('exam-next-btn');
  const examSubmitBtn = document.getElementById('exam-submit-btn');
  const examCancelBtn = document.getElementById('exam-cancel-btn');
  const examModeCloseBtn = document.getElementById('exam-mode-close-btn');

  // DOM References - Settings View
  const settingsBackBtn = document.getElementById('settings-back-btn');
  const clearDataBtn = document.getElementById('clear-data-btn');
  const autoStartToggle = document.getElementById('auto-start-toggle');
  const notificationToggle = document.getElementById('notification-toggle');

  // ============================================
  // Initialize
  // ============================================
  loadSettings();
  loadExamData();
  updateTimerDisplay();
  setInterval(updateTimerDisplay, 1000);
  checkExamMode();

  // ============================================
  // Navigation Events
  // ============================================
  navExamsBtn.addEventListener('click', () => switchView('examList'));
  settingsBtn.addEventListener('click', () => switchView('settings'));
  examListBackBtn.addEventListener('click', () => switchView('timer'));
  returnTimerBtn.addEventListener('click', () => switchView('timer'));
  loadCurrentPageProblemsBtn.addEventListener('click', loadCurrentPageProblems);
  backToExamsBtn.addEventListener('click', () => switchView('examList'));
  settingsBackBtn.addEventListener('click', () => switchView('timer'));
  examModeCloseBtn.addEventListener('click', () => switchView('examList'));

  // Timer View Events
  playPauseBtn?.addEventListener('click', togglePlayPause);
  resetBtn?.addEventListener('click', resetTimer);
  countdownBtn?.addEventListener('click', toggleCountdownInput);
  startCountdownBtn?.addEventListener('click', startCountdown);
  cancelCountdownBtn?.addEventListener('click', () => {
    countdownInputSection.style.display = 'none';
  });
  countdownInput?.addEventListener('keydown', (e) => {
    if (e.key === 'Enter') startCountdown();
  });

  // Exam Mode Events
  examPlayPauseBtn.addEventListener('click', togglePlayPause);
  examResetBtn.addEventListener('click', resetTimer);
  examPrevBtn.addEventListener('click', () => navigateProblem(-1));
  examNextBtn.addEventListener('click', () => navigateProblem(1));
  examSubmitBtn.addEventListener('click', submitExam);
  examCancelBtn.addEventListener('click', confirmDeleteCustomExam);

  // Settings Events
  autoStartToggle.addEventListener('change', saveSettings);
  notificationToggle.addEventListener('change', saveSettings);
  clearDataBtn.addEventListener('click', clearData);
  confirmCancel.addEventListener('click', closeConfirm);
  confirmOk.addEventListener('click', confirmPendingAction);
  confirmOverlay.addEventListener('click', (e) => {
    if (e.target === confirmOverlay) closeConfirm();
  });

  selectAllProblemsBtn.addEventListener('click', toggleSelectAllProblems);
  createCustomExamBtn.addEventListener('click', confirmCreateCustomExam);
  deleteCustomExamBtn.addEventListener('click', confirmDeleteCustomExam);

  // Search
  examSearchInput.addEventListener('input', renderExamList);

  compat.storage.onChanged.addListener((changes, areaName) => {
    if (areaName === 'local' && changes.activeCustomExam) {
      activeCustomExam = changes.activeCustomExam.newValue || null;
      currentExam = activeCustomExam || currentExam;
      if (currentView === 'examList') {
        renderExamList();
      }
      if (currentView === 'examProblems') {
        renderExamProblems();
      }
    }
  });

  // ============================================
  // Settings
  // ============================================
  function loadSettings() {
    compat.storage.local.get(['settings'], (result) => {
      const settings = {
        autoStart: result.settings?.autoStart !== false,
        notifications: result.settings?.notifications !== false
      };
      autoStartToggle.checked = settings.autoStart;
      notificationToggle.checked = settings.notifications;
    });
  }

  function saveSettings() {
    compat.storage.local.set({
      settings: {
        autoStart: autoStartToggle.checked,
        notifications: notificationToggle.checked
      }
    });
  }

  // ============================================
  // View Switching
  // ============================================
  function switchView(view) {
    currentView = view;
    Object.values(views).forEach(v => v.style.display = 'none');

    switch (view) {
      case 'timer':
        views.timer.style.display = 'flex';
        break;
      case 'examList':
        views.examList.style.display = 'flex';
        renderExamList();
        break;
      case 'examProblems':
        views.examProblems.style.display = 'flex';
        renderExamProblems();
        break;
      case 'examMode':
        views.examMode.style.display = 'flex';
        updateExamModeDisplay();
        break;
      case 'settings':
        views.settings.style.display = 'flex';
        break;
    }
  }

  // ============================================
  // Check if we should show exam mode
  // ============================================
  function checkExamMode() {
    compat.storage.local.get(['activeCustomExam', 'isExamMode', 'currentExam', 'currentProblemIndex'], (result) => {
      activeCustomExam = result.activeCustomExam || null;
      const storedExam = activeCustomExam || result.currentExam;
      if (result.isExamMode && storedExam) {
        currentExam = storedExam;
        currentProblemIndex = storedExam.currentProblemIndex || result.currentProblemIndex || 0;
        switchView('examMode');
      }
    });
  }

  // ============================================
  // Timer Display Update
  // ============================================
  function updateTimerDisplay() {
    compat.runtime.sendMessage({ action: 'getTimerData' }, (timerData) => {
      if (compat.runtime.lastError || !timerData) {
        statusDot.classList.remove('connected');
        statusText.textContent = '未连接';
        return;
      }

      statusDot.classList.add('connected');
      statusText.textContent = '已连接';

      if (timerData.isRunning && timerData.currentSession) {
        const currentTime = getElapsedTime(timerData);
        const timeStr = formatTime(currentTime);
        if (timerValue) timerValue.textContent = timeStr;
        updateExamProblemTimer(timerData);
        updatePlayPauseIcons(timerData.isPaused);
      } else if (currentView === 'examMode') {
        updateExamProblemTimer(timerData);
        updatePlayPauseIcons(false);
      } else if (currentView === 'timer') {
        if (timerValue) {
          timerValue.textContent = '00:00:00';
          timerValue.classList.remove('warning', 'danger');
        }
        updatePlayPauseIcons(false);
      }

      updateSessionsList(timerData.sessions || []);
    });

    updateCountdownDisplay();
    if (currentView === 'examMode') updateExamGlobalTimer();
  }

  function updateExamProblemTimer(timerData) {
    if (!examTimerValue) return;

    compat.storage.local.get(['examStartTime', 'examDuration'], (result) => {
      const elapsed = result.examStartTime ? Date.now() - result.examStartTime : 0;
      const duration = Number(result.examDuration) || Number(currentExam?.duration) || 0;
      const remaining = duration > 0 ? Math.max(0, duration - elapsed) : 0;
      const minutes = Math.floor(remaining / 60000);

      examTimerValue.textContent = formatTime(remaining);
      examTimerValue.classList.remove('warning', 'danger');
      if (duration > 0 && minutes <= 5) {
        examTimerValue.classList.add('danger');
      } else if (duration > 0 && minutes <= 10) {
        examTimerValue.classList.add('warning');
      }
    });
  }

  // ============================================
  // Countdown Display
  // ============================================
  function updateCountdownDisplay() {
    compat.storage.local.get(['countdownData'], (result) => {
      if (result.countdownData && result.countdownData.isRunning) {
        const countdown = result.countdownData;
        const elapsed = Date.now() - countdown.startTime;
        const remaining = Math.max(0, countdown.duration - elapsed);

        if (remaining > 0) {
          const timeStr = formatCountdownTime(remaining);
          if (timerValue) timerValue.textContent = timeStr;
          if (examTimerValue) examTimerValue.textContent = timeStr;

          const minutes = Math.floor(remaining / 60000);
          timerValue?.classList.remove('warning', 'danger');
          if (examTimerValue) examTimerValue.classList.remove('warning', 'danger');
          if (minutes <= 5) {
            timerValue?.classList.add('danger');
            if (examTimerValue) examTimerValue.classList.add('danger');
          } else if (minutes <= 10) {
            timerValue?.classList.add('warning');
            if (examTimerValue) examTimerValue.classList.add('warning');
          }
        } else {
          timerValue?.classList.remove('warning', 'danger');
          if (examTimerValue) examTimerValue.classList.remove('warning', 'danger');
        }
      }
    });
  }

  // ============================================
  // Exam Global Timer
  // ============================================
  function updateExamGlobalTimer() {
    compat.storage.local.get(['examStartTime', 'examDuration'], (result) => {
      if (result.examStartTime && result.examDuration) {
        const elapsed = Date.now() - result.examStartTime;
        const remaining = Math.max(0, result.examDuration - elapsed);
        examGlobalTimer.textContent = formatTime(remaining);
        const progress = ((result.examDuration - remaining) / result.examDuration) * 100;
        examGlobalProgress.style.width = Math.min(100, progress) + '%';
      } else if (result.examStartTime) {
        const elapsed = Date.now() - result.examStartTime;
        examGlobalTimer.textContent = formatTime(elapsed);
      }
    });
  }

  // ============================================
  // Exam Mode Display
  // ============================================
  function updateExamModeDisplay() {
    if (!currentExam || !currentExam.problems) return;
    const prob = currentExam.problems[currentProblemIndex];
    if (prob) {
      examProblemIndex.textContent = `题目 ${currentProblemIndex + 1}/${currentExam.problems.length}`;
      examProblemName.textContent = prob.name || prob.id || '未知题目';
      examProblemDifficulty.textContent = prob.difficulty || '';
    }
  }

  // ============================================
  // Timer Controls
  // ============================================
  function togglePlayPause() {
    compat.tabs.query({ active: true, currentWindow: true }, function (tabs) {
      if (!tabs[0]) return;

      compat.tabs.sendMessage(tabs[0].id, { action: 'togglePlayPause' }, () => {
        if (compat.runtime.lastError) {
          compat.runtime.sendMessage({ action: 'togglePauseTimer' }, (response) => {
            if (compat.runtime.lastError || !response?.success) return;
            updatePlayPauseIcons(response.timerData.isPaused);
            updateTimerDisplay();
          });
          return;
        }

        setTimeout(updateTimerDisplay, 120);
      });
    });
  }

  function resetTimer() {
    if (confirm('确定要重置当前计时吗?')) {
      compat.tabs.query({ active: true, currentWindow: true }, function (tabs) {
        if (tabs[0]) {
          compat.tabs.sendMessage(tabs[0].id, { action: 'resetTimer' }, () => {
            if (compat.runtime.lastError) {
              compat.runtime.sendMessage({ action: 'stopTimer' });
              compat.storage.local.remove(['countdownData']);
            }
            if (timerValue) {
              timerValue.textContent = '00:00:00';
              timerValue.classList.remove('warning', 'danger');
            }
            if (examTimerValue) {
              examTimerValue.textContent = '00:00:00';
              examTimerValue.classList.remove('warning', 'danger');
            }
            updatePlayPauseIcons(false);
            setTimeout(updateTimerDisplay, 100);
          });
        }
      });
    }
  }

  function toggleCountdownInput() {
    if (!countdownInputSection || !countdownInput) return;
    countdownVisible = !countdownVisible;
    countdownInputSection.style.display = countdownVisible ? 'block' : 'none';
    if (countdownVisible) countdownInput.focus();
  }

  function startCountdown() {
    if (!countdownInput || !countdownInputSection) return;
    const duration = parseInt(countdownInput.value);
    if (duration && duration > 0) {
      compat.tabs.query({ active: true, currentWindow: true }, function (tabs) {
        if (tabs[0]) {
          compat.tabs.sendMessage(tabs[0].id, {
            action: 'startCountdown',
            duration: duration
          });
        }
      });
      countdownInput.value = '';
      countdownInputSection.style.display = 'none';
      countdownVisible = false;
    } else {
      countdownInput.style.borderColor = 'var(--error)';
      setTimeout(() => { countdownInput.style.borderColor = ''; }, 1500);
    }
  }

  // ============================================
  // Exam Navigation
  // ============================================
  function navigateProblem(direction) {
    if (!currentExam || !currentExam.problems) return;
    const newIndex = currentProblemIndex + direction;
    if (newIndex >= 0 && newIndex < currentExam.problems.length) {
      currentProblemIndex = newIndex;
      compat.runtime.sendMessage({ action: 'setCurrentExamProblem', problemIndex: currentProblemIndex });
      updateExamModeDisplay();

      const prob = currentExam.problems[currentProblemIndex];
      if (prob && prob.url) {
        compat.tabs.query({ active: true, currentWindow: true }, function (tabs) {
          if (tabs[0]) {
            compat.tabs.update(tabs[0].id, { url: prob.url });
          }
        });
      }
    }
  }

  function submitExam() {
    showConfirm({
      icon: 'task_alt',
      title: '提交考试?',
      message: '提交后将结束当前自定义考试,题目完成状态会保留到做题记录中。',
      confirmText: '提交',
      onConfirm: () => {
        compat.runtime.sendMessage({ action: 'finishCustomExam' }, () => {
          activeCustomExam = null;
          currentExam = null;
          currentProblemIndex = 0;
          switchView('timer');
          refreshActiveTabHighlights();
        });
      }
    });
  }

  // ============================================
  // Exam Data Management
  // ============================================
  function loadExamData() {
    compat.storage.local.get(['activeCustomExam'], (result) => {
      activeCustomExam = result.activeCustomExam || null;
      examData = [];
      compat.storage.local.remove(['examSessions']);
      if (currentView === 'examList') renderExamList();
    });
  }

  // ============================================
  // Current Page Problem Selection
  // ============================================
  function loadCurrentPageProblems() {
    compat.tabs.query({ active: true, currentWindow: true }, (tabs) => {
      if (!tabs[0]) return;

      compat.tabs.sendMessage(tabs[0].id, { action: 'getCurrentPageProblems' }, (response) => {
        if (compat.runtime.lastError || !response?.problems?.length) {
          showConfirm({
            icon: 'info',
            title: '未找到题目列表',
            message: '请先打开 PTA 的题目列表或考试题目列表页面,再从当前页面选择题目。',
            confirmText: '知道了',
            onConfirm: () => {}
          });
          return;
        }

        compat.storage.local.remove(['customExamSelectionPreview'], refreshActiveTabHighlights);
        customExamSelectionMode = true;
        currentExam = {
          id: `selection-${Date.now()}`,
          name: '当前页面题目',
          mode: 'selection',
          date: new Date().toISOString(),
          duration: response.problems.length * 30 * 60000,
          problems: response.problems.map((problem) => ({
            ...problem,
            status: 'pending',
            selected: false,
            solvedAt: null
          }))
        };
        selectedProblemIds = new Set();
        switchView('examProblems');
      });
    });
  }

  function getProblemKey(problem, index) {
    return problem.id || String(index);
  }

  function toggleProblemSelection(problem, index) {
    const key = getProblemKey(problem, index);
    if (selectedProblemIds.has(key)) {
      selectedProblemIds.delete(key);
    } else {
      selectedProblemIds.add(key);
    }
    syncSelectionPreview();
    renderExamProblems();
  }

  function toggleSelectAllProblems() {
    if (!currentExam?.problems) return;

    if (selectedProblemIds.size === currentExam.problems.length) {
      selectedProblemIds.clear();
    } else {
      selectedProblemIds = new Set(currentExam.problems.map((problem, index) => getProblemKey(problem, index)));
    }
    syncSelectionPreview();
    renderExamProblems();
  }

  function syncSelectionPreview() {
    if (!customExamSelectionMode || !currentExam?.problems) return;

    const selectedProblems = currentExam.problems
      .filter((problem, index) => selectedProblemIds.has(getProblemKey(problem, index)))
      .map((problem) => ({ ...problem, status: 'pending' }));

    if (selectedProblems.length === 0) {
      compat.storage.local.remove(['customExamSelectionPreview'], refreshActiveTabHighlights);
      return;
    }

    compat.storage.local.set({
      customExamSelectionPreview: {
        id: 'selection-preview',
        mode: 'selection-preview',
        problems: selectedProblems
      }
    }, refreshActiveTabHighlights);
  }

  function confirmCreateCustomExam() {
    if (!selectedProblemIds.size) return;

    const durationMinutes = getCustomExamDurationMinutes();
    customExamDurationInput.value = String(durationMinutes);
    showConfirm({
      icon: 'timer',
      title: '创建自定义考试?',
      message: `将使用选中的 ${selectedProblemIds.size} 道题创建一场 ${durationMinutes} 分钟的计时考试。`,
      confirmText: '开始',
      onConfirm: createCustomExamFromSelection
    });
  }

  function getCustomExamDurationMinutes() {
    const fallback = selectedProblemIds.size * 30;
    const value = Number(customExamDurationInput.value);
    const minutes = Number.isFinite(value) && value > 0 ? value : fallback;
    return Math.max(1, Math.min(999, Math.round(minutes)));
  }

  function confirmDeleteCustomExam() {
    showConfirm({
      icon: 'delete',
      title: customExamSelectionMode ? '取消当前选择?' : '删除当前考试?',
      message: customExamSelectionMode
        ? '将退出当前页面题目选择,并清除网站题目列表上的选中状态。'
        : '将删除当前自定义考试设置,并清除网站题目列表上的蓝色/绿色状态。',
      confirmText: customExamSelectionMode ? '取消选择' : '删除',
      onConfirm: deleteCustomExam
    });
  }

  function deleteCustomExam() {
    const finishDelete = () => {
      customExamSelectionMode = false;
      activeCustomExam = null;
      selectedProblemIds.clear();
      currentExam = null;
      currentProblemIndex = 0;
      switchView('examList');
      refreshActiveTabHighlights();
    };

    if (customExamSelectionMode) {
      compat.storage.local.remove(['customExamSelectionPreview'], finishDelete);
      return;
    }

    compat.runtime.sendMessage({ action: 'finishCustomExam' }, finishDelete);
  }

  function createCustomExamFromSelection() {
    const durationMinutes = getCustomExamDurationMinutes();
    const selectedProblems = currentExam.problems
      .filter((problem, index) => selectedProblemIds.has(getProblemKey(problem, index)))
      .map((problem) => ({
        ...problem,
        status: problem.status === 'solved' ? 'solved' : 'pending',
        selected: true,
        solvedAt: problem.solvedAt || null
      }));

    currentExam = {
      id: `custom-${Date.now()}`,
      name: `自定义考试 · ${selectedProblems.length} 题`,
      mode: 'custom',
      startedAt: Date.now(),
      duration: durationMinutes * 60000,
      currentProblemIndex: 0,
      problems: selectedProblems
    };
    customExamSelectionMode = false;
    selectedProblemIds.clear();
    startExamMode(0);
  }

  // ============================================
  // Render Exam List
  // ============================================
  function renderExamList() {
    const query = (examSearchInput.value || '').toLowerCase();
    const exams = activeCustomExam ? [activeCustomExam] : examData;
    const filtered = exams.filter(e => e.name.toLowerCase().includes(query));

    if (filtered.length === 0) {
      examListScroll.innerHTML = `
        <div class="empty-state">
          <span class="material-symbols-outlined icon-empty">assignment</span>
          <span class="empty-text">${query ? '未找到匹配的考试' : '暂无考试记录'}</span>
        </div>
      `;
      return;
    }

    examListScroll.innerHTML = '';
    filtered.forEach((exam) => {
      const isActive = activeCustomExam?.id === exam.id;
      const item = document.createElement('div');
      item.className = `exam-list-item${isActive ? ' active-exam' : ''}`;
      item.innerHTML = `
        <div class="exam-list-item-left">
          <div class="exam-list-icon">
            <span class="material-symbols-outlined">${isActive ? 'timer' : 'calendar_today'}</span>
          </div>
          <div class="exam-list-copy">
            <span class="exam-list-name">${exam.name}</span>
            ${isActive ? '<span class="exam-list-subtitle">进行中的考试</span>' : ''}
          </div>
        </div>
        <div class="exam-list-item-right">
          <span class="exam-list-count">${exam.problems.length} 题</span>
          ${isActive ? '<button class="exam-list-delete-btn" title="取消考试"><span class="material-symbols-outlined">close</span></button>' : ''}
          <span class="material-symbols-outlined exam-list-chevron">chevron_right</span>
        </div>
      `;
      item.addEventListener('click', () => {
        currentExam = exam;
        currentProblemIndex = exam.currentProblemIndex || 0;
        switchView(isActive ? 'examMode' : 'examProblems');
      });
      item.querySelector('.exam-list-delete-btn')?.addEventListener('click', (e) => {
        e.stopPropagation();
        currentExam = exam;
        confirmDeleteCustomExam();
      });
      examListScroll.appendChild(item);
    });
  }

  // ============================================
  // Render Exam Problems
  // ============================================
  function renderExamProblems() {
    if (!currentExam) return;

    const isCustomExam = currentExam.mode === 'custom';
    examTitle.textContent = currentExam.name;
    examDurationText.textContent = currentExam.duration
      ? Math.round(currentExam.duration / 60000) + ' min'
      : '未知';
    examStatusBadge.textContent = customExamSelectionMode ? '选择题目' : isCustomExam ? '进行中' : '已完成';
    if (examDurationMeta) examDurationMeta.style.display = customExamSelectionMode || isCustomExam ? 'none' : '';
    customExamToolbar.style.display = (customExamSelectionMode || isCustomExam) ? 'flex' : 'none';
    customExamDurationInput.style.display = customExamSelectionMode ? '' : 'none';
    selectAllProblemsBtn.style.display = customExamSelectionMode ? '' : 'none';
    createCustomExamBtn.style.display = customExamSelectionMode ? '' : 'none';
    deleteCustomExamBtn.style.display = isCustomExam ? '' : 'none';
    deleteCustomExamBtn.textContent = '删除考试';

    if (customExamSelectionMode) {
      customExamCount.textContent = `已选择 ${selectedProblemIds.size} 题`;
      selectAllProblemsBtn.textContent = selectedProblemIds.size === currentExam.problems.length ? '取消全选' : '全选';
      createCustomExamBtn.disabled = selectedProblemIds.size === 0;
    } else if (isCustomExam) {
      const solvedCount = currentExam.problems.filter((problem) => problem.status === 'solved').length;
      customExamCount.textContent = `已完成 ${solvedCount}/${currentExam.problems.length} 题`;
    }

    problemList.innerHTML = '';
    const totalProblems = currentExam.problems.length;
    const solvedCount = currentExam.problems.filter((problem) => problem.status === 'solved').length;

    currentExam.problems.forEach((prob, index) => {
      const key = getProblemKey(prob, index);
      const selected = selectedProblemIds.has(key);
      const solved = prob.status === 'solved' || Boolean(prob.duration);
      const item = document.createElement('div');
      item.className = 'problem-list-item';
      if (customExamSelectionMode && selected) item.classList.add('selected');
      if (isCustomExam && !solved) item.classList.add('pending');
      if (solved) item.classList.add('solved');

      const statusText = solved ? '已完成' : (customExamSelectionMode && selected) || isCustomExam ? '待完成' : '';
      const statusClass = solved ? 'green' : statusText ? 'blue' : '';
      const actionText = customExamSelectionMode ? (selected ? '已选' : '选择') : '开始';

      item.innerHTML = `
        <div class="problem-list-item-left">
          <div class="problem-list-number">${String(prob.displayIndex || index + 1).padStart(2, '0')}</div>
          <div class="problem-list-info">
            <div class="problem-list-name">${prob.name || prob.id || '题目 ' + (index + 1)}</div>
            <div class="problem-list-meta">
              <span>${prob.duration ? formatTime(prob.duration) : (prob.id || '未计时')}</span>
              ${statusText ? `<span class="problem-status-badge ${statusClass}">${statusText}</span>` : ''}
            </div>
          </div>
        </div>
        <button class="problem-list-start-btn" data-index="${index}">${actionText}</button>
      `;

      item.addEventListener('click', () => {
        if (customExamSelectionMode) toggleProblemSelection(prob, index);
      });
      item.querySelector('.problem-list-start-btn').addEventListener('click', (e) => {
        e.stopPropagation();
        if (customExamSelectionMode) {
          toggleProblemSelection(prob, index);
        } else {
          startExamMode(index);
        }
      });
      problemList.appendChild(item);
    });

    progressValue.textContent = `${solvedCount} / ${totalProblems} 题`;
    progressBarFill.style.width = totalProblems > 0 ? (solvedCount / totalProblems * 100) + '%' : '0%';
    progressPercent.textContent = totalProblems > 0 ? Math.round(solvedCount / totalProblems * 100) + '%' : '0%';
  }

  // ============================================
  // Start Exam Mode
  // ============================================
  function startExamMode(problemIndex) {
    currentProblemIndex = problemIndex;
    currentExam.currentProblemIndex = currentProblemIndex;

    const startExam = () => {
      switchView('examMode');
      navigateToCurrentProblem();
      refreshActiveTabHighlights();
    };

    if (currentExam.mode === 'custom') {
      compat.runtime.sendMessage({ action: 'startCustomExam', exam: currentExam }, startExam);
      return;
    }

    compat.storage.local.set({
      isExamMode: true,
      currentExam: currentExam,
      currentProblemIndex: currentProblemIndex,
      examStartTime: Date.now(),
      examDuration: currentExam.duration || (currentExam.problems.length * 30 * 60000)
    }, startExam);
  }

  function navigateToCurrentProblem() {
    const prob = currentExam?.problems?.[currentProblemIndex];
    if (!prob?.url) return;

    compat.tabs.query({ active: true, currentWindow: true }, function (tabs) {
      if (tabs[0]) {
        compat.tabs.update(tabs[0].id, { url: prob.url });
      }
    });
  }

  function refreshActiveTabHighlights() {
    compat.tabs.query({ active: true, currentWindow: true }, function (tabs) {
      if (tabs[0]) {
        compat.tabs.sendMessage(tabs[0].id, { action: 'refreshProblemHighlights' });
      }
    });
  }

  // ============================================
  // Sessions List
  // ============================================
  function updateSessionsList(sessions) {
    if (!sessions || sessions.length === 0) {
      sessionsList.innerHTML = `
        <div class="empty-state">
          <span class="material-symbols-outlined icon-empty">history</span>
          <span class="empty-text">暂无记录</span>
        </div>
      `;
      return;
    }

    const recentSessions = sessions
      .map((session, sessionIndex) => ({ session, sessionIndex }))
      .reverse();
    sessionsList.innerHTML = '';
    recentSessions.forEach(({ session, sessionIndex }, index) => {
      const item = document.createElement('div');
      item.className = 'session-item';

      const left = document.createElement('div');
      left.className = 'session-item-left';

      const number = document.createElement('div');
      number.className = 'session-number';
      number.textContent = String(recentSessions.length - index).padStart(2, '0');

      const info = document.createElement('div');
      info.className = 'session-info';

      const name = document.createElement('div');
      name.className = 'session-problem-id';
      name.textContent = session.problemName || session.problemId || '未知题目';

      const date = document.createElement('div');
      date.className = 'session-date';
      date.textContent = `${formatSessionDate(session.endTime)} · ${session.problemId || '未知 ID'}`;

      const right = document.createElement('div');
      right.className = 'session-item-right';

      const time = document.createElement('span');
      time.className = 'session-time';
      time.textContent = formatTime(session.duration);

      const deleteBtn = document.createElement('button');
      deleteBtn.className = 'session-delete-btn';
      deleteBtn.title = '删除记录';
      deleteBtn.innerHTML = '<span class="material-symbols-outlined">delete</span>';
      deleteBtn.addEventListener('click', (e) => {
        e.stopPropagation();
        deleteSession(sessionIndex);
      });

      info.appendChild(name);
      info.appendChild(date);
      left.appendChild(number);
      left.appendChild(info);
      right.appendChild(time);
      right.appendChild(deleteBtn);
      item.appendChild(left);
      item.appendChild(right);
      sessionsList.appendChild(item);
    });
  }

  // ============================================
  // Clear Data
  // ============================================
  function clearData() {
    showConfirm({
      icon: 'delete_forever',
      title: '清除所有数据?',
      message: '将删除所有做题记录、倒计时和当前计时状态,此操作无法恢复。',
      confirmText: '清除',
      onConfirm: performClearData
    });
  }

  function performClearData() {
    compat.runtime.sendMessage({ action: 'clearAllData' }, () => {
      examData = [];
      currentExam = null;
      currentProblemIndex = 0;
      compat.tabs.query({ active: true, currentWindow: true }, (tabs) => {
        if (tabs[0]) {
          compat.tabs.sendMessage(tabs[0].id, { action: 'resetTimer' }, () => {
            updateTimerDisplay();
          });
        } else {
          updateTimerDisplay();
        }
      });
      switchView('timer');
    });
  }

  function deleteSession(sessionIndex) {
    showConfirm({
      icon: 'delete',
      title: '删除这条记录?',
      message: '删除后无法恢复,确定要继续吗?',
      confirmText: '删除',
      onConfirm: () => {
        compat.runtime.sendMessage({ action: 'deleteSession', sessionIndex: sessionIndex }, () => {
          if (compat.runtime.lastError) return;
          updateTimerDisplay();
        });
      }
    });
  }

  function showConfirm({ icon, title, message, confirmText, onConfirm }) {
    pendingConfirmAction = onConfirm;
    confirmIcon.textContent = icon;
    confirmTitle.textContent = title;
    confirmMessage.textContent = message;
    confirmOk.textContent = confirmText;
    confirmOverlay.classList.add('visible');
    confirmOverlay.setAttribute('aria-hidden', 'false');
    confirmOk.focus();
  }

  function closeConfirm() {
    pendingConfirmAction = null;
    confirmOverlay.classList.remove('visible');
    confirmOverlay.setAttribute('aria-hidden', 'true');
  }

  function confirmPendingAction() {
    const action = pendingConfirmAction;
    closeConfirm();
    if (action) action();
  }

  // ============================================
  // Utility Functions
  // ============================================
  function getElapsedTime(timerData) {
    if (!timerData?.currentSession) return 0;
    const now = timerData.isPaused && timerData.pauseStartTime ? timerData.pauseStartTime : Date.now();
    return Math.max(0, now - timerData.currentSession.startTime - (timerData.pausedDuration || 0));
  }

  function updatePlayPauseIcons(paused) {
    const icon = playPauseBtn?.querySelector('.material-symbols-outlined');
    const examIcon = examPlayPauseBtn?.querySelector('.material-symbols-outlined');
    if (icon) icon.textContent = paused ? 'play_arrow' : 'pause';
    if (examIcon) examIcon.textContent = paused ? 'play_arrow' : 'pause';
    if (playPauseBtn) playPauseBtn.title = paused ? '继续' : '暂停';
    if (examPlayPauseBtn) examPlayPauseBtn.title = paused ? '继续' : '暂停';
  }

  function formatTime(ms) {
    if (!ms || ms < 0) return '00:00:00';
    const seconds = Math.floor(ms / 1000);
    const minutes = Math.floor(seconds / 60);
    const hours = Math.floor(minutes / 60);
    return `${hours.toString().padStart(2, '0')}:${(minutes % 60).toString().padStart(2, '0')}:${(seconds % 60).toString().padStart(2, '0')}`;
  }

  function formatCountdownTime(ms) {
    const totalSeconds = Math.floor(ms / 1000);
    const minutes = Math.floor(totalSeconds / 60);
    const seconds = totalSeconds % 60;
    if (minutes >= 60) {
      const hours = Math.floor(minutes / 60);
      return `${hours.toString().padStart(2, '0')}:${(minutes % 60).toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
    }
    return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
  }

  function formatSessionDate(timestamp) {
    if (!timestamp) return '';
    const date = new Date(timestamp);
    const month = date.getMonth() + 1;
    const day = date.getDate();
    const hours = date.getHours().toString().padStart(2, '0');
    const mins = date.getMinutes().toString().padStart(2, '0');
    return `${month}/${day} ${hours}:${mins}`;
  }
};

  });

  runModule('content', function () {
// PTA Timer - Content Script
// Ultra-Compact Floating Timer Widget
// Focus Precision Design System v2.0

let timerDisplay = null;
let currentProblemId = null;
let isFixed = false;
let isPaused = false;
let countdownData = null;
let countdownPauseTime = 0;
let countdownWarningShown = false;
let timerPauseTime = 0;
let timerPauseOffset = 0;
let isDragging = false;
let dragOffset = { x: 0, y: 0 };
let isExamMode = false; // 考试模式标志
let hasActiveCustomExam = false;
let examStartTime = null; // 考试开始时间
let timerCompleted = false;
let lastDetectedUrl = null;
let routeObserver = null;
let routeCheckInterval = null;
let routeDetectionTimer = null;
let submissionResultObserver = null;
let submissionResultCheckTimer = null;
let timerDisplayUpdateTimer = null;
let problemListObserver = null;
let problemListRefreshTimer = null;
let settings = {
  autoStart: true,
  notifications: true
};

// ============================================
// Initialization
// ============================================
function init() {
  compat.storage.local.get(['timerFixed', 'timerPosition', 'activeCustomExam', 'isExamMode', 'settings'], (result) => {
    if (result.timerFixed !== undefined) {
      isFixed = result.timerFixed;
    }
    settings = {
      autoStart: result.settings?.autoStart !== false,
      notifications: result.settings?.notifications !== false
    };
    hasActiveCustomExam = Boolean(result.activeCustomExam);
    isExamMode = hasActiveCustomExam || Boolean(result.isExamMode);

    detectProblemPage();

    setTimeout(() => {
      if (timerDisplay && result.timerPosition && !isFixed) {
        timerDisplay.style.left = result.timerPosition.x + 'px';
        timerDisplay.style.top = result.timerPosition.y + 'px';
        timerDisplay.style.right = 'auto';
      }
    }, 100);
  });

  isPaused = false;
  timerPauseTime = 0;
  timerPauseOffset = 0;
  countdownPauseTime = 0;
  countdownWarningShown = false;

  startRouteDetection();
  startProblemListWatcher();
  scheduleProblemListRefresh();
}

// ============================================
// Message Listener
// ============================================
compat.runtime.onMessage.addListener((request, sender, sendResponse) => {
  if (request.action === 'startCountdown') {
    startCountdown(request.duration);
    sendResponse({ success: true });
  } else if (request.action === 'togglePlayPause') {
    togglePlayPause();
    sendResponse({ success: true });
  } else if (request.action === 'syncPauseState') {
    syncPauseState(request.isPaused);
    sendResponse({ success: true });
  } else if (request.action === 'resetTimer') {
    resetTimer();
    sendResponse({ success: true });
  } else if (request.action === 'getCurrentPageProblems') {
    sendResponse({ success: true, problems: collectCurrentPageProblems() });
  } else if (request.action === 'refreshProblemHighlights') {
    scheduleProblemListRefresh();
    sendResponse({ success: true });
  }
});

// ============================================
// Page Detection
// ============================================
function detectProblemPage() {
  const url = window.location.href;
  lastDetectedUrl = url;

  // 检测考试模式:URL 包含 /exam/ 或 /exams/
  const wasExamMode = isExamMode;
  isExamMode = hasActiveCustomExam || /\/exams?\//.test(url);

  // 考试模式状态变化时,保存到 storage 供 popup 读取
  if (isExamMode !== wasExamMode) {
    if (isExamMode) {
      compat.storage.local.set({ isExamMode: true });
    } else {
      compat.storage.local.get(['activeCustomExam'], (result) => {
        if (!result.activeCustomExam) {
          compat.storage.local.set({ isExamMode: false });
        }
      });
    }

    if (isExamMode && !examStartTime) {
      // 进入考试模式,记录考试开始时间
      compat.storage.local.get(['examStartTime'], (result) => {
        if (result.examStartTime) {
          examStartTime = result.examStartTime;
        } else {
          examStartTime = Date.now();
          compat.storage.local.set({ examStartTime: examStartTime });
        }
      });
    }
  }

  const problemId = extractProblemId(url);
  scheduleProblemListRefresh();

  if (problemId) {
    if (problemId !== currentProblemId) {
      if (currentProblemId) {
        stopProblemTimer();
      }
      currentProblemId = problemId;
      timerCompleted = false;
      resetLocalTimerState();
      if (settings.autoStart) {
        startProblemTimer(problemId);
      }
      createTimerDisplay();
    }

    startSubmissionResultWatcher();
    checkSubmissionResult();
  } else {
    stopSubmissionResultWatcher();
    if (currentProblemId) {
      stopProblemTimer();
      currentProblemId = null;
      timerCompleted = false;
    }
    if (timerDisplay) {
      timerDisplay.style.display = 'none';
    }
  }
}

function extractProblemId(url) {
  const parsedUrl = new URL(url);
  const problemSetProblemId = parsedUrl.searchParams.get('problemSetProblemId');
  if (problemSetProblemId) {
    return problemSetProblemId;
  }

  if (/\/exams?\//.test(parsedUrl.pathname)) {
    return null;
  }

  const pathMatch = parsedUrl.pathname.match(/\/problems\/([^\/\?]+)/);
  return pathMatch ? pathMatch[1] : null;
}

function startRouteDetection() {
  if (!routeObserver) {
    routeObserver = new MutationObserver(scheduleRouteDetection);
    routeObserver.observe(document.body, {
      childList: true
    });
  }

  if (!routeCheckInterval) {
    routeCheckInterval = setInterval(scheduleRouteDetection, 1000);
  }
}

function scheduleRouteDetection() {
  if (routeDetectionTimer || window.location.href === lastDetectedUrl) {
    return;
  }

  routeDetectionTimer = setTimeout(() => {
    routeDetectionTimer = null;
    if (window.location.href !== lastDetectedUrl) {
      detectProblemPage();
    }
  }, 200);
}

function startSubmissionResultWatcher() {
  if (submissionResultObserver || timerCompleted) {
    return;
  }

  submissionResultObserver = new MutationObserver(scheduleSubmissionResultCheck);
  submissionResultObserver.observe(document.body, {
    childList: true,
    subtree: true
  });
}

function stopSubmissionResultWatcher() {
  if (submissionResultObserver) {
    submissionResultObserver.disconnect();
    submissionResultObserver = null;
  }

  if (submissionResultCheckTimer) {
    clearTimeout(submissionResultCheckTimer);
    submissionResultCheckTimer = null;
  }
}

function scheduleSubmissionResultCheck() {
  if (submissionResultCheckTimer || timerCompleted) {
    return;
  }

  submissionResultCheckTimer = setTimeout(() => {
    submissionResultCheckTimer = null;
    checkSubmissionResult();
  }, 500);
}

function checkSubmissionResult() {
  if (timerCompleted || !isFullAcceptedSubmissionVisible()) {
    return;
  }

  timerCompleted = true;
  stopSubmissionResultWatcher();
  completeProblemTimer();
  markTimerAsCompleted();
}

function isFullAcceptedSubmissionVisible() {
  const modalBodies = document.querySelectorAll('[data-e2e="modal-body"]');
  for (const body of modalBodies) {
    const modal = body.closest('[data-e2e="modal-mask"], .pc-modal') || body;
    const title = modal.querySelector('.title_H10HL, [class*="title_"]');
    if (!title || !title.textContent.includes('提交结果')) {
      continue;
    }

    const text = body.textContent;
    if (!text.includes('答案正确')) {
      continue;
    }

    const scoreMatch = text.match(/分数\s*(\d+(?:\.\d+)?)\s*\/\s*(\d+(?:\.\d+)?)/);
    if (scoreMatch && Number(scoreMatch[1]) === Number(scoreMatch[2])) {
      return true;
    }
  }

  return false;
}

function resetLocalTimerState() {
  isPaused = false;
  timerPauseTime = 0;
  timerPauseOffset = 0;
  countdownData = null;
  countdownPauseTime = 0;
  countdownWarningShown = false;
  compat.storage.local.remove(['countdownData']);
}

function markTimerAsCompleted() {
  isPaused = true;
  countdownData = null;
  compat.storage.local.remove(['countdownData']);

  const btn = document.getElementById('pta-play-pause');
  const icon = btn && btn.querySelector('.material-symbols-outlined');
  const statusDot = document.getElementById('pta-status-dot');
  const modeLabel = document.getElementById('pta-mode-label');

  if (icon) icon.textContent = 'check';
  if (btn) btn.title = '已完成';
  if (statusDot) statusDot.classList.add('paused');
  if (modeLabel) {
    modeLabel.textContent = 'DONE';
    modeLabel.style.color = '#16a34a';
  }
}

// ============================================
// Problem List Collection & Highlighting
// ============================================
function startProblemListWatcher() {
  if (problemListObserver || !document.body) {
    return;
  }

  problemListObserver = new MutationObserver(scheduleProblemListRefresh);
  problemListObserver.observe(document.body, {
    childList: true,
    subtree: true
  });
}

function scheduleProblemListRefresh() {
  if (problemListRefreshTimer) {
    return;
  }

  problemListRefreshTimer = setTimeout(() => {
    problemListRefreshTimer = null;
    refreshProblemListHighlights();
  }, 400);
}

function collectCurrentPageProblems() {
  return collectCurrentPageProblemEntries().map(({ id, name, displayIndex, url }) => ({ id, name, displayIndex, url }));
}

function collectCurrentPageProblemEntries() {
  const entries = [];
  const seen = new Set();
  const links = document.querySelectorAll('a[href*="problemSetProblemId="], a[href*="/problems/"]');

  links.forEach((anchor) => {
    if (anchor.closest('#pta-timer-display, .timer-countdown-dialog, .timer-finished-toast')) {
      return;
    }

    const url = new URL(anchor.href, window.location.href).href;
    const id = extractProblemId(url);
    if (!id || seen.has(id)) {
      return;
    }

    const target = getProblemListItemTarget(anchor);
    seen.add(id);
    entries.push({
      id: id,
      name: getProblemNameFromListItem(anchor, target, id),
      displayIndex: getProblemDisplayIndex(anchor),
      url: url,
      target: target
    });
  });

  return entries;
}

function getProblemListItemTarget(anchor) {
  const listTarget = anchor.closest('td, li, tr, [role="row"], [role="listitem"]');
  if (listTarget) {
    return listTarget;
  }
  return anchor;
}

function getProblemDisplayIndex(anchor) {
  const text = anchor.textContent.trim().replace(/\s+/g, ' ');
  return /^\d+$/.test(text) ? text : '';
}

function getProblemNameFromListItem(anchor, target, fallback) {
  const anchorText = anchor.textContent.trim().replace(/\s+/g, ' ');
  if (anchorText && anchorText.length <= 40) {
    return /^\d+$/.test(anchorText) ? `题目 ${anchorText}` : anchorText;
  }

  const title = target?.querySelector('h1, h2, h3, [class*="title"], [class*="Title"], [class*="name"], [class*="Name"]');
  const text = (title?.textContent || target?.textContent || '').trim().replace(/\s+/g, ' ');
  if (text && text.length <= 60 && !/^编程题\s*\d+\s*\/\s*\d+$/.test(text)) {
    return text;
  }
  return fallback;
}

function refreshProblemListHighlights() {
  document.querySelectorAll('.pta-problem-highlight-pending, .pta-problem-highlight-solved').forEach((element) => {
    element.classList.remove('pta-problem-highlight-pending', 'pta-problem-highlight-solved');
  });

  compat.storage.local.get(['activeCustomExam', 'customExamSelectionPreview'], (result) => {
    const exam = result.activeCustomExam || result.customExamSelectionPreview;
    if (!exam?.problems) {
      return;
    }

    const statusById = new Map(exam.problems.map((problem) => [problem.id, problem.status]));
    collectCurrentPageProblemEntries().forEach(({ id, target }) => {
      const status = statusById.get(id);
      if (!status || !target) {
        return;
      }
      target.classList.add(status === 'solved' ? 'pta-problem-highlight-solved' : 'pta-problem-highlight-pending');
    });
  });
}

compat.storage.onChanged.addListener((changes, areaName) => {
  if (areaName !== 'local') {
    return;
  }

  if (changes.settings) {
    settings = {
      autoStart: changes.settings.newValue?.autoStart !== false,
      notifications: changes.settings.newValue?.notifications !== false
    };
  }

  if (changes.activeCustomExam) {
    hasActiveCustomExam = Boolean(changes.activeCustomExam.newValue);
    isExamMode = hasActiveCustomExam || /\/exams?\//.test(window.location.href);
  }

  if (changes.activeCustomExam || changes.customExamSelectionPreview) {
    scheduleProblemListRefresh();
  }
});

// ============================================
// Timer Control
// ============================================
function startProblemTimer(problemId) {
  compat.runtime.sendMessage({
    action: 'startTimer',
    problemId: problemId,
    problemName: getProblemName()
  });
}

function stopProblemTimer() {
  compat.runtime.sendMessage({
    action: 'stopTimer'
  });
}

function completeProblemTimer() {
  compat.runtime.sendMessage({
    action: 'completeTimer'
  }, () => {
    if (currentProblemId) {
      compat.runtime.sendMessage({
        action: 'markExamProblemSolved',
        problemId: currentProblemId
      }, () => {
        scheduleProblemListRefresh();
      });
    }
  });
}

function getProblemName() {
  const title = document.querySelector('.text-darkest.font-bold.text-lg, h1, [class*="problem"] h1');
  const text = title?.textContent?.trim();
  return text || currentProblemId || '未知题目';
}

function getElapsedTime(timerData) {
  if (!timerData?.currentSession) return 0;
  const now = timerData.isPaused && timerData.pauseStartTime ? timerData.pauseStartTime : Date.now();
  return Math.max(0, now - timerData.currentSession.startTime - (timerData.pausedDuration || 0));
}

function renderPlayPauseState(paused) {
  const btn = document.getElementById('pta-play-pause');
  const icon = btn && btn.querySelector('.material-symbols-outlined');
  const statusDot = document.getElementById('pta-status-dot');
  const modeLabel = document.getElementById('pta-mode-label');

  isPaused = paused;
  if (icon) icon.textContent = paused ? 'play_arrow' : 'pause';
  if (btn) btn.title = paused ? '继续' : '暂停';
  if (statusDot) statusDot.classList.toggle('paused', paused);
  if (modeLabel && !timerCompleted) {
    modeLabel.textContent = isExamMode ? 'EXAM' : paused ? 'PAUSED' : (countdownData && countdownData.isRunning ? 'COUNTDOWN' : 'FOCUS');
    modeLabel.style.color = isExamMode ? '#cf2c30' : paused ? '#d97706' : '';
  }
}

// ============================================
// UI Creation - Ultra Compact Widget
// ============================================
function createTimerDisplay() {
  if (timerDisplay) {
    timerDisplay.remove();
  }

  // Load Material Symbols font
  if (!document.querySelector('link[href*="Material+Symbols+Outlined"]')) {
    const fontLink = document.createElement('link');
    fontLink.rel = 'stylesheet';
    fontLink.href = 'https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,[email protected],0..1&display=swap';
    document.head.appendChild(fontLink);
  }

  // Load Inter & JetBrains Mono
  if (!document.querySelector('link[href*="JetBrains+Mono"]')) {
    const fontLink = document.createElement('link');
    fontLink.rel = 'stylesheet';
    fontLink.href = 'https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@500;600&display=swap';
    document.head.appendChild(fontLink);
  }

  timerDisplay = document.createElement('div');
  timerDisplay.id = 'pta-timer-display';

  const shortId = currentProblemId ? currentProblemId.substring(0, 7) : '---';
  const examBadgeHtml = isExamMode ? '<span class="timer-exam-badge">EXAM</span>' : '';

  timerDisplay.innerHTML = `
    <!-- Left Branding -->
    <div class="timer-brand">
      <div class="timer-brand-info">
        <div class="timer-brand-row">
          <div class="timer-status-dot" id="pta-status-dot"></div>
          <span class="timer-brand-label">PTA</span>
          ${examBadgeHtml}
        </div>
        <div class="timer-problem-id">
          <span id="pta-problem-label">${shortId}</span>
          <span>•</span>
          <span class="timer-problem-mode" id="pta-mode-label">${isExamMode ? 'EXAM' : 'FOCUS'}</span>
        </div>
      </div>
    </div>

    <!-- Central Display -->
    <div class="timer-center">
      <span class="timer-time-display" id="pta-current-time">00:00:00</span>
      <div class="timer-divider"></div>
      <div class="timer-controls-cluster">
        <button class="timer-ctrl-btn btn-play-pause" id="pta-play-pause" title="暂停">
          <span class="material-symbols-outlined">pause</span>
        </button>
        <button class="timer-ctrl-btn btn-reset" id="pta-reset" title="重置">
          <span class="material-symbols-outlined">replay</span>
        </button>
        <button class="timer-ctrl-btn btn-countdown" id="pta-countdown" title="倒计时">
          <span class="material-symbols-outlined">timer</span>
        </button>
        <button class="timer-ctrl-btn btn-panel" id="pta-open-panel" title="打开面板">
          <span class="material-symbols-outlined">dashboard</span>
        </button>
      </div>
      <div class="timer-progress-bar">
        <div class="timer-progress-fill" id="pta-progress-fill"></div>
      </div>
    </div>

    <!-- Right Actions -->
    <div class="timer-actions">
      <button class="timer-action-btn ${isFixed ? 'pinned' : ''}" id="pta-fix-toggle" title="${isFixed ? '取消固定' : '固定'}">
        <span class="material-symbols-outlined">push_pin</span>
      </button>
      <button class="timer-action-btn" id="pta-timer-close" title="关闭">
        <span class="material-symbols-outlined">close</span>
      </button>
    </div>
  `;

  document.body.appendChild(timerDisplay);
  renderMaterialIcons(timerDisplay);

  // Apply fixed state
  if (isFixed) {
    timerDisplay.classList.add('fixed');
  } else {
    addDragFunctionality();
  }

  // Bind events
  bindEvents();

  // Start display update loop
  if (timerDisplayUpdateTimer) {
    clearTimeout(timerDisplayUpdateTimer);
    timerDisplayUpdateTimer = null;
  }
  updateTimerDisplay();
}

// ============================================
// Event Binding
// ============================================
function bindEvents() {
  document.getElementById('pta-timer-close').addEventListener('click', (e) => {
    e.stopPropagation();
    timerDisplay.style.display = 'none';
  });

  document.getElementById('pta-fix-toggle').addEventListener('click', (e) => {
    e.stopPropagation();
    toggleFix();
  });

  document.getElementById('pta-play-pause').addEventListener('click', (e) => {
    e.stopPropagation();
    togglePlayPause();
  });

  document.getElementById('pta-reset').addEventListener('click', (e) => {
    e.stopPropagation();
    resetTimer();
  });

  document.getElementById('pta-countdown').addEventListener('click', (e) => {
    e.stopPropagation();
    showCountdownDialog();
  });

  const panelButton = document.getElementById('pta-open-panel');
  if (panelButton) {
    panelButton.addEventListener('click', (e) => {
      e.stopPropagation();
      openPanel();
    });
  }
}

// ============================================
// Drag Functionality
// ============================================
function addDragFunctionality() {
  if (!timerDisplay) return;

  timerDisplay.addEventListener('mousedown', (e) => {
    if (isFixed) return;
    if (e.target.tagName === 'BUTTON' || e.target.closest('button')) return;

    isDragging = true;
    const rect = timerDisplay.getBoundingClientRect();
    dragOffset.x = e.clientX - rect.left;
    dragOffset.y = e.clientY - rect.top;
    timerDisplay.style.transition = 'none';
  });

  document.addEventListener('mousemove', (e) => {
    if (!isDragging || isFixed) return;

    const x = e.clientX - dragOffset.x;
    const y = e.clientY - dragOffset.y;
    const maxX = window.innerWidth - timerDisplay.offsetWidth;
    const maxY = window.innerHeight - timerDisplay.offsetHeight;

    timerDisplay.style.left = Math.max(0, Math.min(x, maxX)) + 'px';
    timerDisplay.style.top = Math.max(0, Math.min(y, maxY)) + 'px';
    timerDisplay.style.right = 'auto';
  });

  document.addEventListener('mouseup', () => {
    if (isDragging) {
      isDragging = false;
      timerDisplay.style.transition = '';

      const rect = timerDisplay.getBoundingClientRect();
      compat.storage.local.set({
        timerPosition: { x: rect.left, y: rect.top }
      });
    }
  });
}

// ============================================
// Toggle Fix
// ============================================
function toggleFix() {
  isFixed = !isFixed;
  const fixBtn = document.getElementById('pta-fix-toggle');

  if (isFixed) {
    fixBtn.classList.add('pinned');
    fixBtn.title = '取消固定';
    timerDisplay.classList.add('fixed');
  } else {
    fixBtn.classList.remove('pinned');
    fixBtn.title = '固定';
    timerDisplay.classList.remove('fixed');
    addDragFunctionality();
  }

  compat.storage.local.set({ timerFixed: isFixed });
}

// ============================================
// Play/Pause
// ============================================
function togglePlayPause() {
  if (timerCompleted) return;

  const toggleActiveTimer = () => {
    compat.runtime.sendMessage({ action: 'togglePauseTimer' }, (response) => {
      if (compat.runtime.lastError || !response?.success) return;
      syncPauseState(response.timerData.isPaused);
    });
  };

  compat.runtime.sendMessage({ action: 'getTimerData' }, (timerData) => {
    if (compat.runtime.lastError) return;

    if ((!timerData?.isRunning || !timerData.currentSession) && currentProblemId) {
      startProblemTimer(currentProblemId);
      syncPauseState(false);
      return;
    }

    toggleActiveTimer();
  });
}

function syncPauseState(paused) {
  renderPlayPauseState(paused);

  if (paused) {
    if (countdownData && countdownData.isRunning) {
      countdownPauseTime = Date.now();
    }
  } else if (countdownData && countdownData.isRunning && countdownPauseTime > 0) {
    const pauseDuration = Date.now() - countdownPauseTime;
    countdownData.startTime += pauseDuration;
    countdownPauseTime = 0;
    compat.storage.local.set({ countdownData: countdownData });
  }
}

// ============================================
// Reset Timer
// ============================================
function resetTimer() {
  compat.runtime.sendMessage({ action: 'stopTimer' });

  timerCompleted = false;
  startSubmissionResultWatcher();
  isPaused = false;
  timerPauseTime = 0;
  timerPauseOffset = 0;
  countdownData = null;
  countdownPauseTime = 0;
  countdownWarningShown = false;
  compat.storage.local.remove(['countdownData']);

  const btn = document.getElementById('pta-play-pause');
  const icon = btn.querySelector('.material-symbols-outlined');
  icon.textContent = 'pause';
  btn.title = '暂停';

  const statusDot = document.getElementById('pta-status-dot');
  statusDot.classList.remove('paused');

  const modeLabel = document.getElementById('pta-mode-label');
  modeLabel.textContent = 'FOCUS';
  modeLabel.style.color = '';

  const timeDisplay = document.getElementById('pta-current-time');
  timeDisplay.textContent = '00:00:00';
  timeDisplay.classList.remove('warning', 'danger');

  const progressFill = document.getElementById('pta-progress-fill');
  progressFill.style.width = '0%';
  progressFill.classList.remove('warning', 'danger');

  if (currentProblemId) {
    startProblemTimer(currentProblemId);
  }

  scheduleProblemListRefresh();
}

// ============================================
// Countdown Dialog
// ============================================
function showCountdownDialog() {
  // Remove existing dialog
  const existing = document.querySelector('.timer-countdown-dialog');
  if (existing) existing.remove();

  const dialog = document.createElement('div');
  dialog.className = 'timer-countdown-dialog';
  dialog.innerHTML = `
    <div class="timer-countdown-panel">
      <h3>设置倒计时</h3>
      <input type="number" id="pta-countdown-input" placeholder="输入分钟数" min="1" max="999" autofocus>
      <div class="timer-countdown-actions">
        <button class="btn-cancel" id="pta-countdown-cancel">取消</button>
        <button class="btn-confirm" id="pta-countdown-confirm">开始</button>
      </div>
    </div>
  `;

  document.body.appendChild(dialog);
  renderMaterialIcons(dialog);

  const input = document.getElementById('pta-countdown-input');
  const confirmBtn = document.getElementById('pta-countdown-confirm');
  const cancelBtn = document.getElementById('pta-countdown-cancel');

  setTimeout(() => input.focus(), 100);

  confirmBtn.addEventListener('click', () => {
    const value = parseInt(input.value);
    if (value && value > 0) {
      startCountdown(value);
      dialog.remove();
    } else {
      input.style.borderColor = '#ab0b1c';
      setTimeout(() => { input.style.borderColor = ''; }, 1500);
    }
  });

  cancelBtn.addEventListener('click', () => dialog.remove());

  dialog.addEventListener('click', (e) => {
    if (e.target === dialog) dialog.remove();
  });

  input.addEventListener('keydown', (e) => {
    if (e.key === 'Enter') confirmBtn.click();
    if (e.key === 'Escape') dialog.remove();
  });
}

// ============================================
// Countdown Logic
// ============================================
function startCountdown(minutes) {
  countdownData = {
    duration: minutes * 60 * 1000,
    startTime: Date.now(),
    isRunning: true
  };

  isPaused = false;
  countdownPauseTime = 0;
  countdownWarningShown = false;
  timerPauseTime = 0;
  timerPauseOffset = 0;

  const btn = document.getElementById('pta-play-pause');
  const icon = btn.querySelector('.material-symbols-outlined');
  icon.textContent = 'pause';
  btn.title = '暂停';

  const modeLabel = document.getElementById('pta-mode-label');
  modeLabel.textContent = 'COUNTDOWN';

  compat.storage.local.set({ countdownData: countdownData });
}

// ============================================
// Countdown Reminders
// ============================================
function showCountdownWarningToast(remaining) {
  document.querySelector('.timer-warning-toast')?.remove();

  const toast = document.createElement('div');
  toast.className = 'timer-finished-toast timer-warning-toast';
  toast.innerHTML = `
    <div class="timer-finished-card warning">
      <div class="timer-finished-icon">
        <span class="material-symbols-outlined">timer</span>
      </div>
      <div class="timer-finished-copy">
        <div class="timer-finished-title">倒计时提醒</div>
        <div class="timer-finished-message">剩余 ${formatCountdownTime(remaining)},请注意时间。</div>
      </div>
      <button class="timer-finished-close" type="button" aria-label="关闭提醒">
        <span class="material-symbols-outlined">close</span>
      </button>
    </div>
  `;

  document.body.appendChild(toast);
  renderMaterialIcons(toast);

  const closeToast = () => {
    toast.classList.add('closing');
    setTimeout(() => toast.remove(), 180);
  };

  toast.querySelector('.timer-finished-close').addEventListener('click', closeToast);
  setTimeout(closeToast, 6000);
}

function showCountdownWarningIfNeeded(remaining) {
  if (!settings.notifications || countdownWarningShown || isPaused || remaining <= 0) {
    return;
  }

  const warningThreshold = Math.min(5 * 60 * 1000, countdownData.duration / 2);
  if (remaining > warningThreshold) {
    return;
  }

  countdownWarningShown = true;
  showCountdownWarningToast(remaining);

  if (Notification.permission === 'granted') {
    new Notification('PTA Timer', {
      body: `倒计时剩余 ${formatCountdownTime(remaining)}`,
      icon: compat.runtime.getURL('icons/icon48.png')
    });
  }
}

// ============================================
// Countdown Finished Toast
// ============================================
function showCountdownFinishedToast() {
  document.querySelector('.timer-finished-toast')?.remove();

  const toast = document.createElement('div');
  toast.className = 'timer-finished-toast';
  toast.innerHTML = `
    <div class="timer-finished-card">
      <div class="timer-finished-icon">
        <span class="material-symbols-outlined">timer_off</span>
      </div>
      <div class="timer-finished-copy">
        <div class="timer-finished-title">倒计时结束</div>
        <div class="timer-finished-message">时间到,记得检查当前题目状态。</div>
      </div>
      <button class="timer-finished-close" type="button" aria-label="关闭提醒">
        <span class="material-symbols-outlined">close</span>
      </button>
    </div>
  `;

  document.body.appendChild(toast);
  renderMaterialIcons(toast);

  const closeToast = () => {
    toast.classList.add('closing');
    setTimeout(() => toast.remove(), 180);
  };

  toast.querySelector('.timer-finished-close').addEventListener('click', closeToast);
  setTimeout(closeToast, 8000);
}

// ============================================
// Display Update Loop
// ============================================
function updateTimerDisplay() {
  if (!timerDisplay || timerDisplay.style.display === 'none') {
    timerDisplayUpdateTimer = setTimeout(updateTimerDisplay, 1000);
    return;
  }

  if (isExamMode) {
    compat.storage.local.get(['examStartTime', 'examDuration'], (result) => {
      if (result.examStartTime && Number(result.examDuration) > 0) {
        updateExamCountdownDisplay(result);
        syncCurrentProblemTimerState();
        return;
      }

      isExamMode = false;
      if (!/\/exams?\//.test(window.location.href)) {
        compat.storage.local.set({ isExamMode: false });
      }
      updateProblemTimerDisplay();
    });
  } else {
    updateProblemTimerDisplay();
  }

  timerDisplayUpdateTimer = setTimeout(updateTimerDisplay, 1000);
}

function updateProblemTimerDisplay() {
  updateCountdownDisplay();

  if (countdownData && countdownData.isRunning) {
    return;
  }

  compat.runtime.sendMessage({ action: 'getTimerData' }, (timerData) => {
    if (compat.runtime.lastError || !timerData) return;
    if (timerData.isRunning && timerData.currentSession) {
      syncCurrentProblemName(timerData);

      const timeEl = document.getElementById('pta-current-time');
      if (timeEl) {
        timeEl.textContent = formatTime(getElapsedTime(timerData));
      }
      renderPlayPauseState(timerData.isPaused);
      return;
    }

    if (currentProblemId && !timerCompleted && settings.autoStart) {
      startProblemTimer(currentProblemId);
      renderPlayPauseState(false);
      return;
    }

    renderPlayPauseState(true);
  });
}

function syncCurrentProblemTimerState() {
  compat.runtime.sendMessage({ action: 'getTimerData' }, (timerData) => {
    if (compat.runtime.lastError || !timerData || timerCompleted) return;
    if (timerData.isRunning && timerData.currentSession) {
      syncCurrentProblemName(timerData);
    }
    renderPlayPauseState(timerData.isPaused);
  });
}

function syncCurrentProblemName(timerData) {
  const problemName = getProblemName();
  if (problemName && problemName !== currentProblemId && problemName !== timerData.currentSession.problemName) {
    compat.runtime.sendMessage({
      action: 'startTimer',
      problemId: currentProblemId,
      problemName: problemName
    });
  }
}

function updateExamCountdownDisplay(examTiming) {
  const timeDisplay = document.getElementById('pta-current-time');
  const progressFill = document.getElementById('pta-progress-fill');
  const elapsed = Date.now() - examTiming.examStartTime;
  const duration = Number(examTiming.examDuration) || 0;
  const remaining = Math.max(0, duration - elapsed);
  const minutes = Math.floor(remaining / 60000);

  if (timeDisplay) {
    timeDisplay.textContent = formatTime(remaining);
    timeDisplay.classList.remove('warning', 'danger');
    if (minutes <= 5) {
      timeDisplay.classList.add('danger');
    } else if (minutes <= 10) {
      timeDisplay.classList.add('warning');
    }
  }

  if (progressFill) {
    progressFill.style.width = Math.min(100, elapsed / duration * 100) + '%';
    progressFill.classList.remove('warning', 'danger');
    if (minutes <= 5) {
      progressFill.classList.add('danger');
    } else if (minutes <= 10) {
      progressFill.classList.add('warning');
    }
  }
}

function updateCountdownDisplay() {
  if (!countdownData || !countdownData.isRunning) {
    compat.storage.local.get(['countdownData'], (result) => {
      if (result.countdownData && result.countdownData.isRunning) {
        countdownData = result.countdownData;
        updateCountdownDisplay();
      }
    });
    return;
  }

  let elapsed;
  if (isPaused && countdownPauseTime > 0) {
    elapsed = countdownPauseTime - countdownData.startTime;
  } else {
    elapsed = Date.now() - countdownData.startTime;
  }

  const remaining = Math.max(0, countdownData.duration - elapsed);
  const timeDisplay = document.getElementById('pta-current-time');
  const progressFill = document.getElementById('pta-progress-fill');

  showCountdownWarningIfNeeded(remaining);

  if (remaining <= 0) {
    countdownData.isRunning = false;

    showCountdownFinishedToast();

    if (settings.notifications && Notification.permission === 'granted') {
      new Notification('PTA Timer', {
        body: '倒计时结束!',
        icon: compat.runtime.getURL('icons/icon48.png')
      });
    }

    compat.storage.local.remove(['countdownData']);
    isPaused = false;
    countdownPauseTime = 0;

    if (timeDisplay) {
      timeDisplay.classList.remove('warning', 'danger');
    }
    if (progressFill) {
      progressFill.style.width = '100%';
      progressFill.classList.remove('warning', 'danger');
    }

    const modeLabel = document.getElementById('pta-mode-label');
    if (modeLabel) modeLabel.textContent = 'FOCUS';
  } else {
    if (timeDisplay) {
      timeDisplay.textContent = formatCountdownTime(remaining);

      const minutes = Math.floor(remaining / 60000);
      timeDisplay.classList.remove('warning', 'danger');
      if (minutes <= 5) {
        timeDisplay.classList.add('danger');
      } else if (minutes <= 10) {
        timeDisplay.classList.add('warning');
      }
    }

    // Update progress bar
    if (progressFill) {
      const progress = ((countdownData.duration - remaining) / countdownData.duration) * 100;
      progressFill.style.width = progress + '%';

      const minutes = Math.floor(remaining / 60000);
      progressFill.classList.remove('warning', 'danger');
      if (minutes <= 5) {
        progressFill.classList.add('danger');
      } else if (minutes <= 10) {
        progressFill.classList.add('warning');
      }
    }
  }
}

// ============================================
// Utility Functions
// ============================================
function formatCountdownTime(ms) {
  const totalSeconds = Math.floor(ms / 1000);
  const minutes = Math.floor(totalSeconds / 60);
  const seconds = totalSeconds % 60;
  if (minutes >= 60) {
    const hours = Math.floor(minutes / 60);
    return `${hours.toString().padStart(2, '0')}:${(minutes % 60).toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
  }
  return `${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
}

function formatTime(ms) {
  if (!ms || ms < 0) return '00:00:00';
  const seconds = Math.floor(ms / 1000);
  const minutes = Math.floor(seconds / 60);
  const hours = Math.floor(minutes / 60);
  return `${hours.toString().padStart(2, '0')}:${(minutes % 60).toString().padStart(2, '0')}:${(seconds % 60).toString().padStart(2, '0')}`;
}

// ============================================
// Bootstrap
// ============================================
if (document.readyState === 'loading') {
  document.addEventListener('DOMContentLoaded', init);
} else {
  init();
}

  });

})();