diff --git a/web/css/style.css b/web/css/style.css
new file mode 100644
index 0000000..a6f8273
--- /dev/null
+++ b/web/css/style.css
@@ -0,0 +1,1108 @@
+/* =============================================
+ NiuGuide - 仿 LeetCode 深色主题样式
+ ============================================= */
+
+/* ========= CSS 变量(深色主题) ========= */
+:root {
+ --bg-primary: #1a1a2e;
+ --bg-secondary: #16213e;
+ --bg-card: #1e2748;
+ --bg-card-hover: #253255;
+ --bg-nav: #111827;
+ --bg-input: #1e293b;
+
+ --text-primary: #e2e8f0;
+ --text-secondary: #94a3b8;
+ --text-muted: #64748b;
+
+ --accent-blue: #3b82f6;
+ --accent-blue-hover: #2563eb;
+ --accent-green: #22c55e;
+ --accent-yellow: #f59e0b;
+ --accent-red: #ef4444;
+ --accent-purple: #8b5cf6;
+
+ --border-color: #2d3748;
+ --border-radius: 8px;
+ --border-radius-lg: 12px;
+
+ --shadow: 0 4px 6px -1px rgba(0,0,0,0.3);
+ --shadow-lg: 0 10px 25px rgba(0,0,0,0.4);
+
+ --sidebar-width: 240px;
+ --header-height: 56px;
+ --mobile-header-height: 50px;
+ --mobile-tabs-height: 56px;
+
+ --font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
+}
+
+/* ========= 重置 & 基础 ========= */
+*, *::before, *::after {
+ margin: 0;
+ padding: 0;
+ box-sizing: border-box;
+}
+
+html {
+ font-size: 16px;
+ -webkit-font-smoothing: antialiased;
+}
+
+body {
+ font-family: var(--font-family);
+ background: var(--bg-primary);
+ color: var(--text-primary);
+ line-height: 1.6;
+ min-height: 100vh;
+}
+
+a { color: var(--accent-blue); text-decoration: none; }
+a:hover { color: var(--accent-blue-hover); }
+
+/* 滚动条 */
+::-webkit-scrollbar { width: 6px; }
+::-webkit-scrollbar-track { background: transparent; }
+::-webkit-scrollbar-thumb { background: var(--border-color); border-radius: 3px; }
+::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }
+
+/* ========= 按钮 ========= */
+.btn {
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+ gap: 6px;
+ padding: 8px 16px;
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius);
+ font-size: 14px;
+ font-family: var(--font-family);
+ cursor: pointer;
+ transition: all 0.2s;
+ background: var(--bg-card);
+ color: var(--text-primary);
+}
+.btn:hover { background: var(--bg-card-hover); }
+.btn:disabled { opacity: 0.5; cursor: not-allowed; }
+
+.btn-primary {
+ background: var(--accent-blue);
+ color: #fff;
+ border-color: var(--accent-blue);
+}
+.btn-primary:hover { background: var(--accent-blue-hover); }
+
+.btn-danger {
+ background: var(--accent-red);
+ color: #fff;
+ border-color: var(--accent-red);
+}
+.btn-danger:hover { opacity: 0.9; }
+
+.btn-success {
+ background: var(--accent-green);
+ color: #fff;
+ border-color: var(--accent-green);
+}
+
+.btn-text {
+ background: transparent;
+ border: none;
+ color: var(--text-secondary);
+ padding: 6px 10px;
+}
+.btn-text:hover { color: var(--text-primary); background: rgba(255,255,255,0.05); }
+
+.btn-sm { padding: 4px 10px; font-size: 13px; }
+.btn-lg { padding: 12px 24px; font-size: 16px; }
+
+.btn-icon {
+ width: 36px;
+ height: 36px;
+ padding: 0;
+ border-radius: 50%;
+ display: inline-flex;
+ align-items: center;
+ justify-content: center;
+}
+
+/* ========= 输入框 ========= */
+input, textarea, select {
+ background: var(--bg-input);
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius);
+ color: var(--text-primary);
+ padding: 10px 14px;
+ font-size: 14px;
+ font-family: var(--font-family);
+ width: 100%;
+ transition: border-color 0.2s;
+ outline: none;
+}
+input:focus, textarea:focus, select:focus {
+ border-color: var(--accent-blue);
+}
+textarea { resize: vertical; min-height: 100px; }
+select { cursor: pointer; }
+select option { background: var(--bg-card); }
+
+/* ========= 布局 ========= */
+.app-layout {
+ display: flex;
+ min-height: 100vh;
+}
+
+/* 侧边栏 */
+.sidebar {
+ width: var(--sidebar-width);
+ background: var(--bg-nav);
+ border-right: 1px solid var(--border-color);
+ display: flex;
+ flex-direction: column;
+ position: fixed;
+ top: 0;
+ left: 0;
+ height: 100vh;
+ z-index: 100;
+ transition: transform 0.3s;
+}
+
+.sidebar-logo {
+ padding: 20px;
+ font-size: 20px;
+ font-weight: 700;
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ border-bottom: 1px solid var(--border-color);
+}
+.sidebar-logo i { color: var(--accent-blue); }
+
+.sidebar-nav {
+ flex: 1;
+ overflow-y: auto;
+ padding: 12px 0;
+}
+
+.nav-item {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 10px 20px;
+ color: var(--text-secondary);
+ font-size: 14px;
+ transition: all 0.2s;
+ cursor: pointer;
+ border-left: 3px solid transparent;
+}
+.nav-item:hover {
+ color: var(--text-primary);
+ background: rgba(255,255,255,0.03);
+}
+.nav-item.active {
+ color: var(--accent-blue);
+ background: rgba(59,130,246,0.08);
+ border-left-color: var(--accent-blue);
+}
+.nav-item i { width: 20px; text-align: center; font-size: 16px; }
+
+.nav-divider {
+ height: 1px;
+ background: var(--border-color);
+ margin: 8px 16px;
+}
+
+.sidebar-footer {
+ padding: 12px 16px;
+ border-top: 1px solid var(--border-color);
+}
+
+/* 隐藏状态(登录页使用) */
+.hidden { display: none !important; }
+
+/* 登录页全宽 */
+.main-wrapper.full-width {
+ margin-left: 0;
+}
+
+/* 主内容区 */
+.main-wrapper {
+ flex: 1;
+ margin-left: var(--sidebar-width);
+ min-height: 100vh;
+}
+
+.main-content {
+ padding: 24px;
+ padding-top: calc(var(--header-height) + 24px);
+ max-width: 1200px;
+ width: 100%;
+}
+
+
+/* 桌面顶部栏 */
+.desktop-header {
+ height: var(--header-height);
+ background: var(--bg-primary);
+ border-bottom: 1px solid var(--border-color);
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0 24px;
+ position: fixed;
+ top: 0;
+ right: 0;
+ left: var(--sidebar-width);
+ z-index: 50;
+}
+.header-breadcrumb { font-size: 16px; font-weight: 500; }
+.header-user {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ color: var(--text-secondary);
+ font-size: 14px;
+}
+.header-user i { font-size: 24px; }
+
+/* 移动端头部 */
+.mobile-header {
+ display: none;
+ height: var(--mobile-header-height);
+ background: var(--bg-nav);
+ border-bottom: 1px solid var(--border-color);
+ align-items: center;
+ justify-content: space-between;
+ padding: 0 12px;
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ z-index: 100;
+}
+.header-title { font-weight: 600; font-size: 16px; }
+
+/* 移动端 Tab */
+.mobile-tabs {
+ display: none;
+ height: var(--mobile-tabs-height);
+ background: var(--bg-nav);
+ border-top: 1px solid var(--border-color);
+ position: fixed;
+ bottom: 0;
+ left: 0;
+ right: 0;
+ z-index: 100;
+ justify-content: space-around;
+ align-items: center;
+}
+.tab-item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ gap: 2px;
+ padding: 6px 12px;
+ color: var(--text-muted);
+ cursor: pointer;
+ font-size: 11px;
+ transition: color 0.2s;
+}
+.tab-item i { font-size: 20px; }
+.tab-item.active { color: var(--accent-blue); }
+.tab-item:hover { color: var(--text-secondary); }
+
+/* ========= 卡片 ========= */
+.card {
+ background: var(--bg-card);
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius-lg);
+ padding: 20px;
+ transition: all 0.2s;
+}
+.card:hover { border-color: rgba(59,130,246,0.3); }
+.card-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 16px;
+}
+.card-title { font-size: 16px; font-weight: 600; }
+
+/* ========= 统计卡片 ========= */
+.stats-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
+ gap: 16px;
+ margin-bottom: 24px;
+}
+.stat-card {
+ background: var(--bg-card);
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius-lg);
+ padding: 20px;
+ text-align: center;
+}
+.stat-icon {
+ width: 48px;
+ height: 48px;
+ border-radius: 12px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin: 0 auto 12px;
+ font-size: 20px;
+}
+.stat-value {
+ font-size: 28px;
+ font-weight: 700;
+ margin-bottom: 4px;
+}
+.stat-label {
+ color: var(--text-muted);
+ font-size: 13px;
+}
+
+/* ========= 页面容器 ========= */
+.page-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 24px;
+ flex-wrap: wrap;
+ gap: 12px;
+}
+.page-title { font-size: 22px; font-weight: 700; }
+.page-subtitle { color: var(--text-secondary); font-size: 14px; margin-top: 4px; }
+
+/* ========= 搜索栏 ========= */
+.search-bar {
+ display: flex;
+ gap: 12px;
+ align-items: center;
+ margin-bottom: 20px;
+ flex-wrap: wrap;
+}
+.search-bar input {
+ flex: 1;
+ min-width: 200px;
+}
+.search-bar select {
+ width: auto;
+ min-width: 120px;
+}
+
+/* ========= 分类网格 ========= */
+.category-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
+ gap: 16px;
+}
+.category-card {
+ background: var(--bg-card);
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius-lg);
+ padding: 24px 16px;
+ text-align: center;
+ cursor: pointer;
+ transition: all 0.2s;
+}
+.category-card:hover {
+ border-color: var(--accent-blue);
+ transform: translateY(-2px);
+ box-shadow: var(--shadow);
+}
+.category-card i {
+ font-size: 32px;
+ color: var(--accent-blue);
+ margin-bottom: 12px;
+}
+.category-card .name {
+ font-size: 15px;
+ font-weight: 500;
+ margin-bottom: 6px;
+}
+.category-card .count {
+ color: var(--text-muted);
+ font-size: 13px;
+}
+
+/* ========= 题目列表 ========= */
+.question-list { display: flex; flex-direction: column; gap: 8px; }
+.question-item {
+ background: var(--bg-card);
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius);
+ padding: 14px 18px;
+ cursor: pointer;
+ transition: all 0.2s;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 12px;
+}
+.question-item:hover {
+ border-color: var(--accent-blue);
+ background: var(--bg-card-hover);
+}
+.question-item .q-left {
+ flex: 1;
+ min-width: 0;
+}
+.question-item .q-title {
+ font-size: 15px;
+ font-weight: 500;
+ margin-bottom: 4px;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+.question-item .q-meta {
+ display: flex;
+ gap: 10px;
+ font-size: 12px;
+ color: var(--text-muted);
+ flex-wrap: wrap;
+}
+.question-item .q-right {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ flex-shrink: 0;
+}
+.tag {
+ display: inline-block;
+ padding: 2px 8px;
+ border-radius: 4px;
+ font-size: 12px;
+ background: rgba(59,130,246,0.1);
+ color: var(--accent-blue);
+}
+.tag-green { background: rgba(34,197,94,0.1); color: var(--accent-green); }
+.tag-yellow { background: rgba(245,158,11,0.1); color: var(--accent-yellow); }
+.tag-red { background: rgba(239,68,68,0.1); color: var(--accent-red); }
+.tag-gray { background: rgba(100,116,139,0.15); color: var(--text-muted); }
+
+/* ========= 分页 ========= */
+.pagination {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ gap: 6px;
+ margin-top: 20px;
+}
+.page-btn {
+ padding: 6px 12px;
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius);
+ background: var(--bg-card);
+ color: var(--text-secondary);
+ cursor: pointer;
+ font-size: 13px;
+ transition: all 0.2s;
+}
+.page-btn:hover { background: var(--bg-card-hover); }
+.page-btn.active {
+ background: var(--accent-blue);
+ color: #fff;
+ border-color: var(--accent-blue);
+}
+.page-btn:disabled { opacity: 0.4; cursor: not-allowed; }
+
+/* ========= 题目详情 ========= */
+.question-detail { max-width: 800px; }
+.detail-header { margin-bottom: 24px; }
+.detail-title { font-size: 24px; font-weight: 700; margin-bottom: 12px; }
+.detail-tags { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
+
+.section-block {
+ margin-bottom: 24px;
+}
+.section-title {
+ font-size: 16px;
+ font-weight: 600;
+ margin-bottom: 12px;
+ padding-bottom: 8px;
+ border-bottom: 1px solid var(--border-color);
+}
+.section-content {
+ font-size: 15px;
+ line-height: 1.8;
+ white-space: pre-wrap;
+ color: var(--text-secondary);
+}
+
+/* 掌握状态选择 */
+.mastery-selector {
+ display: flex;
+ gap: 8px;
+ flex-wrap: wrap;
+}
+.mastery-btn {
+ padding: 8px 16px;
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius);
+ background: var(--bg-card);
+ color: var(--text-secondary);
+ cursor: pointer;
+ font-size: 13px;
+ transition: all 0.2s;
+}
+.mastery-btn:hover { border-color: var(--accent-blue); }
+.mastery-btn.active { border-color: currentColor; background: rgba(255,255,255,0.05); color: var(--text-primary); }
+
+/* ========= 评论 ========= */
+.comment-item {
+ padding: 14px 0;
+ border-bottom: 1px solid var(--border-color);
+}
+.comment-item:last-child { border-bottom: none; }
+.comment-header {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ margin-bottom: 8px;
+ font-size: 13px;
+}
+.comment-avatar {
+ width: 28px;
+ height: 28px;
+ border-radius: 50%;
+ background: var(--accent-blue);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: #fff;
+ font-size: 12px;
+ font-weight: 600;
+}
+.comment-user { font-weight: 500; color: var(--text-primary); }
+.comment-time { color: var(--text-muted); }
+.comment-content { font-size: 14px; color: var(--text-secondary); line-height: 1.6; }
+
+/* ========= 表单 ========= */
+.form-group {
+ margin-bottom: 16px;
+}
+.form-label {
+ display: block;
+ font-size: 14px;
+ font-weight: 500;
+ margin-bottom: 6px;
+ color: var(--text-secondary);
+}
+.form-actions {
+ display: flex;
+ gap: 12px;
+ margin-top: 24px;
+}
+
+/* ========= 登录页面 ========= */
+.login-page {
+ min-height: 100vh;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ background: var(--bg-primary);
+ padding: 20px;
+}
+.login-box {
+ background: var(--bg-card);
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius-lg);
+ padding: 40px;
+ width: 100%;
+ max-width: 420px;
+ box-shadow: var(--shadow-lg);
+}
+.login-logo {
+ text-align: center;
+ margin-bottom: 32px;
+}
+.login-logo i {
+ font-size: 48px;
+ color: var(--accent-blue);
+}
+.login-logo h1 {
+ font-size: 24px;
+ font-weight: 700;
+ margin-top: 12px;
+}
+.login-logo p {
+ color: var(--text-muted);
+ font-size: 14px;
+ margin-top: 4px;
+}
+.login-tabs {
+ display: flex;
+ margin-bottom: 24px;
+ border-bottom: 1px solid var(--border-color);
+}
+.login-tab {
+ flex: 1;
+ padding: 10px;
+ text-align: center;
+ cursor: pointer;
+ color: var(--text-muted);
+ font-size: 15px;
+ font-weight: 500;
+ border-bottom: 2px solid transparent;
+ transition: all 0.2s;
+}
+.login-tab.active {
+ color: var(--accent-blue);
+ border-bottom-color: var(--accent-blue);
+}
+
+/* ========= Toast ========= */
+#toast-container {
+ position: fixed;
+ top: 20px;
+ right: 20px;
+ z-index: 9999;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+.toast {
+ padding: 12px 20px;
+ border-radius: var(--border-radius);
+ font-size: 14px;
+ color: #fff;
+ transform: translateX(120%);
+ transition: transform 0.3s;
+ max-width: 360px;
+}
+.toast.show { transform: translateX(0); }
+.toast-info { background: var(--accent-blue); }
+.toast-success { background: var(--accent-green); }
+.toast-error { background: var(--accent-red); }
+
+/* ========= Modal ========= */
+.modal-overlay {
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0,0,0,0.6);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ z-index: 1000;
+ opacity: 0;
+ transition: opacity 0.3s;
+ padding: 20px;
+}
+.modal-overlay.show { opacity: 1; }
+.modal-box {
+ background: var(--bg-card);
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius-lg);
+ padding: 28px;
+ max-width: 480px;
+ width: 100%;
+}
+.modal-box p { font-size: 15px; margin-bottom: 20px; color: var(--text-secondary); }
+.modal-actions { display: flex; gap: 12px; justify-content: flex-end; }
+.modal-title { font-size: 18px; font-weight: 600; margin-bottom: 16px; }
+
+/* ========= Spinner ========= */
+.spinner {
+ display: inline-block;
+ width: 16px;
+ height: 16px;
+ border: 2px solid transparent;
+ border-top-color: currentColor;
+ border-radius: 50%;
+ animation: spin 0.6s linear infinite;
+}
+.spinner-lg {
+ display: block;
+ width: 40px;
+ height: 40px;
+ border: 3px solid var(--border-color);
+ border-top-color: var(--accent-blue);
+ border-radius: 50%;
+ animation: spin 0.6s linear infinite;
+ margin: 40px auto;
+}
+@keyframes spin { to { transform: rotate(360deg); } }
+
+.page-loading, .page-error {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 60px 20px;
+ color: var(--text-muted);
+}
+.page-error i { font-size: 48px; color: var(--accent-red); margin-bottom: 16px; }
+.page-error p { margin-bottom: 16px; }
+
+/* ========= 空状态 ========= */
+.empty-state {
+ text-align: center;
+ padding: 60px 20px;
+ color: var(--text-muted);
+}
+.empty-state i { font-size: 48px; margin-bottom: 16px; opacity: 0.5; }
+.empty-state p { font-size: 15px; }
+
+/* ========= 排行榜 ========= */
+.rank-item {
+ display: flex;
+ align-items: center;
+ gap: 16px;
+ padding: 12px 16px;
+ background: var(--bg-card);
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius);
+ margin-bottom: 8px;
+}
+.rank-num {
+ width: 32px;
+ text-align: center;
+ font-weight: 700;
+ font-size: 16px;
+ color: var(--text-muted);
+}
+.rank-num.top1 { color: #f59e0b; }
+.rank-num.top2 { color: #94a3b8; }
+.rank-num.top3 { color: #b45309; }
+.rank-avatar {
+ width: 36px;
+ height: 36px;
+ border-radius: 50%;
+ background: var(--accent-blue);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: #fff;
+ font-size: 14px;
+ font-weight: 600;
+}
+.rank-info { flex: 1; }
+.rank-name { font-weight: 500; font-size: 14px; }
+.rank-value { color: var(--text-muted); font-size: 13px; }
+
+/* ========= 面经卡 ========= */
+.exp-grid {
+ display: grid;
+ grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
+ gap: 16px;
+}
+.exp-card {
+ background: var(--bg-card);
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius-lg);
+ padding: 20px;
+ cursor: pointer;
+ transition: all 0.2s;
+}
+.exp-card:hover { border-color: var(--accent-blue); }
+.exp-company {
+ font-size: 16px;
+ font-weight: 600;
+ margin-bottom: 6px;
+ display: flex;
+ align-items: center;
+ gap: 8px;
+}
+.exp-meta {
+ display: flex;
+ gap: 12px;
+ flex-wrap: wrap;
+ font-size: 13px;
+ color: var(--text-muted);
+ margin-bottom: 10px;
+}
+.exp-content {
+ font-size: 14px;
+ color: var(--text-secondary);
+ line-height: 1.6;
+ display: -webkit-box;
+ -webkit-line-clamp: 3;
+ -webkit-box-orient: vertical;
+ overflow: hidden;
+}
+.exp-footer {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-top: 12px;
+ font-size: 13px;
+ color: var(--text-muted);
+}
+.exp-actions { display: flex; gap: 12px; }
+.exp-actions i { cursor: pointer; }
+.exp-actions i:hover { color: var(--accent-blue); }
+.exp-actions .liked { color: var(--accent-red); }
+
+/* ========= 论坛帖子 ========= */
+.forum-item {
+ background: var(--bg-card);
+ border: 1px solid var(--border-color);
+ border-radius: var(--border-radius);
+ padding: 16px 20px;
+ cursor: pointer;
+ transition: all 0.2s;
+ margin-bottom: 8px;
+}
+.forum-item:hover { border-color: var(--accent-blue); }
+.forum-title {
+ font-size: 16px;
+ font-weight: 500;
+ margin-bottom: 8px;
+}
+.forum-meta {
+ display: flex;
+ gap: 16px;
+ font-size: 13px;
+ color: var(--text-muted);
+ flex-wrap: wrap;
+}
+.forum-stats {
+ display: flex;
+ gap: 16px;
+ font-size: 13px;
+ color: var(--text-muted);
+}
+
+/* ========= 面试模式 ========= */
+.interview-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ margin-bottom: 24px;
+ flex-wrap: wrap;
+ gap: 12px;
+}
+.interview-progress {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+.progress-bar {
+ width: 200px;
+ height: 6px;
+ background: var(--bg-input);
+ border-radius: 3px;
+ overflow: hidden;
+}
+.progress-fill {
+ height: 100%;
+ background: var(--accent-blue);
+ border-radius: 3px;
+ transition: width 0.3s;
+}
+
+/* ========= 热力图 ========= */
+.heatmap-wrapper {
+ overflow-x: auto;
+ padding: 10px 0;
+}
+.heatmap-grid {
+ display: grid;
+ grid-template-columns: repeat(7, 1fr);
+ gap: 3px;
+ max-width: 350px;
+}
+.heatmap-cell {
+ aspect-ratio: 1;
+ min-width: 14px;
+ min-height: 14px;
+ border-radius: 2px;
+ background: var(--bg-card);
+}
+
+.heatmap-cell.l0 { background: var(--bg-input); }
+.heatmap-cell.l1 { background: rgba(34,197,94,0.2); }
+.heatmap-cell.l2 { background: rgba(34,197,94,0.4); }
+.heatmap-cell.l3 { background: rgba(34,197,94,0.6); }
+.heatmap-cell.l4 { background: rgba(34,197,94,0.8); }
+
+/* ========= Tab 切换 ========= */
+.tabs {
+ display: flex;
+ gap: 0;
+ margin-bottom: 20px;
+ border-bottom: 1px solid var(--border-color);
+}
+.tab {
+ padding: 10px 20px;
+ cursor: pointer;
+ color: var(--text-muted);
+ font-size: 14px;
+ font-weight: 500;
+ border-bottom: 2px solid transparent;
+ transition: all 0.2s;
+}
+.tab:hover { color: var(--text-secondary); }
+.tab.active {
+ color: var(--accent-blue);
+ border-bottom-color: var(--accent-blue);
+}
+
+/* ========= 评论区 ========= */
+.comment-form {
+ display: flex;
+ gap: 12px;
+ margin-bottom: 16px;
+}
+.comment-form textarea { flex: 1; min-height: 60px; }
+
+/* ========= 响应式 ========= */
+/* 平板:小于 1024px */
+@media (max-width: 1024px) {
+ .main-content { padding: 20px; padding-top: calc(var(--header-height) + 20px); }
+}
+
+/* 手机:小于 768px */
+@media (max-width: 768px) {
+ .sidebar {
+ transform: translateX(-100%);
+ }
+ .sidebar.open {
+ transform: translateX(0);
+ }
+
+ .main-wrapper {
+ margin-left: 0;
+ padding-bottom: var(--mobile-tabs-height);
+ }
+
+ .desktop-header { display: none; }
+ .mobile-header { display: flex; }
+ .mobile-tabs { display: flex; }
+
+ .main-content {
+ padding: 16px;
+ padding-top: calc(var(--mobile-header-height) + 16px);
+ }
+
+ .stats-grid {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ .category-grid {
+ grid-template-columns: repeat(2, 1fr);
+ }
+
+ .exp-grid {
+ grid-template-columns: 1fr;
+ }
+
+ .page-header {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+
+ .search-bar {
+ flex-direction: column;
+ }
+ .search-bar input { min-width: 100%; }
+
+ .login-box { padding: 28px 20px; }
+
+ .detail-title { font-size: 20px; }
+
+ .progress-bar { width: 120px; }
+}
+
+/* 小手机:小于 480px */
+@media (max-width: 480px) {
+ .stats-grid {
+ grid-template-columns: repeat(2, 1fr);
+ gap: 10px;
+ }
+ .stat-card { padding: 14px; }
+ .stat-value { font-size: 22px; }
+
+ .question-item { padding: 12px 14px; }
+}
+
+/* ========= 工具类 ========= */
+.flex { display: flex; }
+.flex-col { flex-direction: column; }
+.flex-wrap { flex-wrap: wrap; }
+.items-center { align-items: center; }
+.justify-between { justify-content: space-between; }
+.gap-2 { gap: 8px; }
+.gap-3 { gap: 12px; }
+.gap-4 { gap: 16px; }
+.mb-2 { margin-bottom: 8px; }
+.mb-3 { margin-bottom: 12px; }
+.mb-4 { margin-bottom: 16px; }
+.mt-4 { margin-top: 16px; }
+.text-center { text-align: center; }
+.text-muted { color: var(--text-muted); }
+.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
+.w-full { width: 100%; }
+
+
+/* ========= 答案隐藏(点击展示) ========= */
+.answer-hidden {
+ color: transparent;
+ text-shadow: 0 0 10px var(--text-muted);
+ cursor: pointer;
+ user-select: none;
+ position: relative;
+ transition: all 0.3s;
+ padding-bottom: 36px;
+}
+.answer-hidden::after {
+ content: '👆 点击显示答案';
+ position: absolute;
+ bottom: 8px;
+ left: 50%;
+ transform: translateX(-50%);
+ color: var(--accent-blue);
+ font-size: 13px;
+ text-shadow: none;
+ z-index: 2;
+ opacity: 0.8;
+ transition: opacity 0.2s;
+ white-space: nowrap;
+}
+.answer-hidden:hover::after {
+ opacity: 1;
+}
+.answer-hidden.revealed {
+ color: var(--text-secondary);
+ text-shadow: none;
+ cursor: auto;
+ user-select: auto;
+ padding-bottom: 0;
+}
+.answer-hidden.revealed::after {
+ display: none;
+}
+
+
+
+/* ========= 过渡动画 ========= */
+.fade-in {
+
+ animation: fadeIn 0.3s ease;
+}
+@keyframes fadeIn {
+ from { opacity: 0; transform: translateY(10px); }
+ to { opacity: 1; transform: translateY(0); }
+}
+
+/* ========= 移动端侧栏遮罩 ========= */
+.sidebar-overlay {
+ display: none;
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ background: rgba(0,0,0,0.5);
+ z-index: 99;
+}
+.sidebar-overlay.show { display: block; }
+
+@media (min-width: 769px) {
+ .sidebar-overlay { display: none !important; }
+}
diff --git a/web/index.html b/web/index.html
new file mode 100644
index 0000000..88641ca
--- /dev/null
+++ b/web/index.html
@@ -0,0 +1,49 @@
+
+
+
+
+
+ NiuGuide - Java 面试指南
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/web/js/api.js b/web/js/api.js
new file mode 100644
index 0000000..8807a60
--- /dev/null
+++ b/web/js/api.js
@@ -0,0 +1,142 @@
+/**
+ * 统一 API 请求封装
+ * 自动携带 Token(Authorization 头),适配 Sa-Token 认证
+ * 统一错误处理
+ */
+const API = {
+ /**
+ * 从 localStorage 获取 token
+ */
+ getToken() {
+ return localStorage.getItem('token');
+ },
+
+ /**
+ * 保存 token
+ */
+ setToken(token) {
+ localStorage.setItem('token', token);
+ },
+
+ /**
+ * 清除 token
+ */
+ clearToken() {
+ localStorage.removeItem('token');
+ },
+
+ /**
+ * 是否已登录(有 token)
+ */
+ isLoggedIn() {
+ return !!this.getToken();
+ },
+
+ /**
+ * 核心请求方法
+ * @param {string} method - HTTP 方法
+ * @param {string} url - 请求路径(相对于 API_BASE)
+ * @param {object} [body] - 请求体(可选)
+ * @param {object} [params] - URL 查询参数(可选)
+ * @returns {Promise