@zhouchangui/math-ati 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -0
- package/bin/math-ati.js +168 -6
- package/dist/assets/{index-Bk2WFPoL.css → index-CGZslJ0a.css} +1 -1
- package/dist/assets/{index-BYFoutza.js → index-CGfjl7nO.js} +8 -8
- package/dist/index.html +2 -2
- package/package.json +3 -1
- package/server/agentClient.js +32 -12
- package/server/fileStore.js +77 -28
- package/server/index.js +5 -3
- package/server/knowledgeExtractor.js +135 -12
- package/server/pdfSubmissionGrader.js +1 -1
- package/server/practiceService.js +6 -4
- package/templates/workspace/data/knowledge_points.json +0 -1264
package/AGENTS.md
CHANGED
|
@@ -77,6 +77,7 @@ Chapter-scoped data:
|
|
|
77
77
|
- `data/chapters/<chapter-id>/chapter.json`: chapter metadata.
|
|
78
78
|
- `data/chapters/<chapter-id>/knowledge/knowledge.json`: chapter knowledge-point dictionary.
|
|
79
79
|
- `data/chapters/<chapter-id>/knowledge/knowledge.md`: readable knowledge document.
|
|
80
|
+
- `data/chapters/<chapter-id>/source_pages/`: copied curriculum note images plus `manifest.json`; the manifest is a derived index and may be rebuilt from the local images when empty or stale.
|
|
80
81
|
- `data/chapters/<chapter-id>/mastery.json`: chapter knowledge-point mastery state.
|
|
81
82
|
- `data/chapters/<chapter-id>/abilities.json`: configured chapter ability assessment state.
|
|
82
83
|
- `data/chapters/<chapter-id>/mistakes.json`: chapter mistakes.
|
package/bin/math-ati.js
CHANGED
|
@@ -1,24 +1,32 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { spawn } from 'node:child_process';
|
|
2
|
+
import { execFile, spawn } from 'node:child_process';
|
|
3
3
|
import { constants } from 'node:fs';
|
|
4
4
|
import { access, cp, mkdir, readdir, readFile, writeFile } from 'node:fs/promises';
|
|
5
|
+
import { createRequire } from 'node:module';
|
|
5
6
|
import os from 'node:os';
|
|
6
7
|
import path from 'node:path';
|
|
8
|
+
import { promisify } from 'node:util';
|
|
7
9
|
import { fileURLToPath } from 'node:url';
|
|
8
10
|
|
|
9
11
|
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
10
12
|
const templateRoot = path.join(packageRoot, 'templates', 'workspace');
|
|
13
|
+
const requireFromCli = createRequire(import.meta.url);
|
|
14
|
+
const execFileAsync = promisify(execFile);
|
|
15
|
+
const packageJson = JSON.parse(await readFile(path.join(packageRoot, 'package.json'), 'utf8'));
|
|
16
|
+
const DEFAULT_CURRICULUM_ID = 'rj-7a';
|
|
17
|
+
const DEFAULT_CURRICULUM_PACKAGE = '@zhouchangui/math-ati-curriculum-rj-7a';
|
|
18
|
+
const DEFAULT_CURRICULUM_PACKAGE_SPEC = `${DEFAULT_CURRICULUM_PACKAGE}@${packageJson.version}`;
|
|
11
19
|
|
|
12
20
|
function printHelp() {
|
|
13
21
|
console.log(`math-ati
|
|
14
22
|
|
|
15
23
|
Usage:
|
|
16
|
-
math-ati init [workspace]
|
|
24
|
+
math-ati init [workspace] [--curriculum-package <spec>] [--curriculum-dir <dir>]
|
|
17
25
|
math-ati start [workspace] [--open] [--port 4173] [--data-dir <dir>] [--env-file <file>]
|
|
18
26
|
math-ati doctor [workspace] [--data-dir <dir>] [--env-file <file>]
|
|
19
27
|
|
|
20
28
|
Commands:
|
|
21
|
-
init Create a local workspace
|
|
29
|
+
init Create a local workspace and copy curriculum resources.
|
|
22
30
|
start Start the local API and production web UI.
|
|
23
31
|
doctor Print package, workspace, and configuration status.
|
|
24
32
|
`);
|
|
@@ -58,13 +66,12 @@ async function ensureWorkspaceTemplate() {
|
|
|
58
66
|
throw new Error(`workspace_template_missing:${templateRoot}`);
|
|
59
67
|
}
|
|
60
68
|
|
|
61
|
-
async function
|
|
62
|
-
await ensureWorkspaceTemplate();
|
|
69
|
+
async function copyTreeSafely(sourceRoot, targetDir, options) {
|
|
63
70
|
await mkdir(targetDir, { recursive: true });
|
|
64
71
|
const stack = [''];
|
|
65
72
|
while (stack.length) {
|
|
66
73
|
const rel = stack.pop();
|
|
67
|
-
const sourceDir = path.join(
|
|
74
|
+
const sourceDir = path.join(sourceRoot, rel);
|
|
68
75
|
const targetSubdir = path.join(targetDir, rel);
|
|
69
76
|
await mkdir(targetSubdir, { recursive: true });
|
|
70
77
|
const entries = await readdir(sourceDir, { withFileTypes: true });
|
|
@@ -81,10 +88,141 @@ async function copyTemplateSafely(targetDir, options) {
|
|
|
81
88
|
}
|
|
82
89
|
}
|
|
83
90
|
|
|
91
|
+
async function copyTemplateSafely(targetDir, options) {
|
|
92
|
+
await ensureWorkspaceTemplate();
|
|
93
|
+
await copyTreeSafely(templateRoot, targetDir, options);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function packageNameFromSpec(spec) {
|
|
97
|
+
if (!spec || spec.startsWith('file:') || spec.startsWith('.') || spec.startsWith('/')) {
|
|
98
|
+
return DEFAULT_CURRICULUM_PACKAGE;
|
|
99
|
+
}
|
|
100
|
+
if (spec.startsWith('@')) {
|
|
101
|
+
const slashIndex = spec.indexOf('/');
|
|
102
|
+
const versionIndex = spec.indexOf('@', slashIndex);
|
|
103
|
+
return versionIndex > 0 ? spec.slice(0, versionIndex) : spec;
|
|
104
|
+
}
|
|
105
|
+
const versionIndex = spec.indexOf('@');
|
|
106
|
+
return versionIndex > 0 ? spec.slice(0, versionIndex) : spec;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
async function curriculumDataRootFromPackageRoot(curriculumPackageRoot) {
|
|
110
|
+
const candidates = [
|
|
111
|
+
path.join(curriculumPackageRoot, 'curriculum', DEFAULT_CURRICULUM_ID, 'data'),
|
|
112
|
+
path.join(curriculumPackageRoot, 'data'),
|
|
113
|
+
curriculumPackageRoot
|
|
114
|
+
];
|
|
115
|
+
for (const candidate of candidates) {
|
|
116
|
+
if (await exists(path.join(candidate, 'global', 'chapters.json'))) {
|
|
117
|
+
return candidate;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
async function curriculumDataRootFromDir(dir) {
|
|
124
|
+
const resolved = path.resolve(dir);
|
|
125
|
+
const candidates = [
|
|
126
|
+
path.join(resolved, 'curriculum', DEFAULT_CURRICULUM_ID, 'data'),
|
|
127
|
+
path.join(resolved, 'data'),
|
|
128
|
+
resolved
|
|
129
|
+
];
|
|
130
|
+
for (const candidate of candidates) {
|
|
131
|
+
if (await exists(path.join(candidate, 'global', 'chapters.json'))) {
|
|
132
|
+
return {
|
|
133
|
+
dataRoot: candidate,
|
|
134
|
+
source: resolved,
|
|
135
|
+
packageName: null,
|
|
136
|
+
installed: false
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
throw new Error(`curriculum_data_missing:${resolved}`);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function localCurriculumPackageRoot(packageName) {
|
|
144
|
+
const directNodeModules = path.join(packageRoot, 'node_modules', ...packageName.split('/'));
|
|
145
|
+
if (await exists(path.join(directNodeModules, 'package.json'))) return directNodeModules;
|
|
146
|
+
try {
|
|
147
|
+
return path.dirname(requireFromCli.resolve(`${packageName}/package.json`));
|
|
148
|
+
} catch {
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function curriculumCacheRoot() {
|
|
154
|
+
return path.join(os.homedir(), '.math-ati', 'curriculum-cache', DEFAULT_CURRICULUM_ID);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
async function installCurriculumPackage(spec, packageName) {
|
|
158
|
+
const cacheRoot = curriculumCacheRoot();
|
|
159
|
+
await mkdir(cacheRoot, { recursive: true });
|
|
160
|
+
await execFileAsync('npm', [
|
|
161
|
+
'install',
|
|
162
|
+
'--prefix',
|
|
163
|
+
cacheRoot,
|
|
164
|
+
'--omit=dev',
|
|
165
|
+
'--no-audit',
|
|
166
|
+
'--no-fund',
|
|
167
|
+
spec
|
|
168
|
+
], {
|
|
169
|
+
maxBuffer: 1024 * 1024 * 20
|
|
170
|
+
});
|
|
171
|
+
const packageRootFromCache = path.join(cacheRoot, 'node_modules', ...packageName.split('/'));
|
|
172
|
+
if (!(await exists(path.join(packageRootFromCache, 'package.json')))) {
|
|
173
|
+
throw new Error(`curriculum_package_install_failed:${spec}`);
|
|
174
|
+
}
|
|
175
|
+
return packageRootFromCache;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
async function resolveCurriculumDataRoot(flags) {
|
|
179
|
+
if (flags['curriculum-dir'] || process.env.MATH_ATI_CURRICULUM_DIR) {
|
|
180
|
+
return curriculumDataRootFromDir(flags['curriculum-dir'] || process.env.MATH_ATI_CURRICULUM_DIR);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const spec = flags['curriculum-package']
|
|
184
|
+
|| process.env.MATH_ATI_CURRICULUM_PACKAGE_SPEC
|
|
185
|
+
|| DEFAULT_CURRICULUM_PACKAGE_SPEC;
|
|
186
|
+
const packageName = packageNameFromSpec(spec);
|
|
187
|
+
let curriculumPackageRoot = await localCurriculumPackageRoot(packageName);
|
|
188
|
+
let installed = false;
|
|
189
|
+
if (!curriculumPackageRoot || spec !== packageName) {
|
|
190
|
+
curriculumPackageRoot = await installCurriculumPackage(spec, packageName);
|
|
191
|
+
installed = true;
|
|
192
|
+
}
|
|
193
|
+
const dataRoot = await curriculumDataRootFromPackageRoot(curriculumPackageRoot);
|
|
194
|
+
if (!dataRoot) throw new Error(`curriculum_data_missing:${curriculumPackageRoot}`);
|
|
195
|
+
return {
|
|
196
|
+
dataRoot,
|
|
197
|
+
source: curriculumPackageRoot,
|
|
198
|
+
packageName,
|
|
199
|
+
installed
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
async function writeCurriculumReceipt(workspaceDir, curriculum) {
|
|
204
|
+
await writeFile(
|
|
205
|
+
path.join(workspaceDir, 'data', 'global', 'curriculum.json'),
|
|
206
|
+
`${JSON.stringify({
|
|
207
|
+
curriculumId: DEFAULT_CURRICULUM_ID,
|
|
208
|
+
packageName: curriculum.packageName,
|
|
209
|
+
source: curriculum.source,
|
|
210
|
+
installedAt: new Date().toISOString()
|
|
211
|
+
}, null, 2)}\n`,
|
|
212
|
+
'utf8'
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
|
|
84
216
|
async function initWorkspace(argv) {
|
|
85
217
|
const { args, flags } = parseArgs(argv);
|
|
86
218
|
const workspaceDir = path.resolve(args[0] || process.cwd());
|
|
219
|
+
const hadData = await exists(path.join(workspaceDir, 'data', 'global', 'chapters.json'));
|
|
87
220
|
await copyTemplateSafely(workspaceDir, { force: Boolean(flags.force) });
|
|
221
|
+
const curriculum = await resolveCurriculumDataRoot(flags);
|
|
222
|
+
await copyTreeSafely(curriculum.dataRoot, path.join(workspaceDir, 'data'), {
|
|
223
|
+
force: Boolean(flags.force) || !hadData
|
|
224
|
+
});
|
|
225
|
+
await writeCurriculumReceipt(workspaceDir, curriculum);
|
|
88
226
|
const envLocal = path.join(workspaceDir, '.env.local');
|
|
89
227
|
const envExample = path.join(workspaceDir, '.env.local.example');
|
|
90
228
|
if (!(await exists(envLocal)) && (await exists(envExample))) {
|
|
@@ -92,6 +230,8 @@ async function initWorkspace(argv) {
|
|
|
92
230
|
}
|
|
93
231
|
console.log(`[math-ati.init] workspace=${workspaceDir}`);
|
|
94
232
|
console.log(`[math-ati.init] data=${path.join(workspaceDir, 'data')}`);
|
|
233
|
+
console.log(`[math-ati.init] curriculum=${curriculum.packageName || curriculum.source}`);
|
|
234
|
+
console.log(`[math-ati.init] curriculumSource=${curriculum.source}`);
|
|
95
235
|
console.log('[math-ati.init] edit .env.local, then run: math-ati start --open');
|
|
96
236
|
}
|
|
97
237
|
|
|
@@ -157,12 +297,34 @@ async function doctor(argv) {
|
|
|
157
297
|
const dataDir = dataDirFromArgs(args, flags);
|
|
158
298
|
const envLocal = envFileFromArgs(args, flags);
|
|
159
299
|
const distIndex = path.join(packageRoot, 'dist', 'index.html');
|
|
300
|
+
const chapterDataDir = path.join(dataDir, 'chapters');
|
|
301
|
+
let chapterDirectoryCount = 0;
|
|
302
|
+
let sourcePageCount = 0;
|
|
303
|
+
try {
|
|
304
|
+
const entries = await readdir(chapterDataDir, { withFileTypes: true });
|
|
305
|
+
const chapterDirs = entries.filter((entry) => entry.isDirectory());
|
|
306
|
+
chapterDirectoryCount = chapterDirs.length;
|
|
307
|
+
for (const entry of chapterDirs) {
|
|
308
|
+
const pagesDir = path.join(chapterDataDir, entry.name, 'source_pages');
|
|
309
|
+
try {
|
|
310
|
+
const pages = await readdir(pagesDir);
|
|
311
|
+
sourcePageCount += pages.filter((file) => /\.(png|jpe?g|webp)$/i.test(file)).length;
|
|
312
|
+
} catch {
|
|
313
|
+
// Missing source pages are reported through the count.
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
} catch {
|
|
317
|
+
// Missing data is reported by hasData.
|
|
318
|
+
}
|
|
160
319
|
console.log(JSON.stringify({
|
|
161
320
|
packageRoot,
|
|
162
321
|
dataDir,
|
|
163
322
|
hasData: await exists(path.join(dataDir, 'global', 'chapters.json')),
|
|
164
323
|
hasEnvLocal: await exists(envLocal),
|
|
165
324
|
hasBuiltUi: await exists(distIndex),
|
|
325
|
+
curriculumReceipt: await exists(path.join(dataDir, 'global', 'curriculum.json')),
|
|
326
|
+
chapterDirectoryCount,
|
|
327
|
+
sourcePageCount,
|
|
166
328
|
node: process.version
|
|
167
329
|
}, null, 2));
|
|
168
330
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
:root{color:#17201c;background:#eef2ef;font-family:Inter,PingFang SC,Microsoft YaHei,Arial,sans-serif;font-size:16px;--surface: #fffdf8;--surface-muted: #f5f7f2;--surface-cool: #edf4f0;--border: #d4ddd5;--border-strong: #b9c6be;--text-soft: #5f6c64;--text-muted: #738077;--green: #1f6455;--green-dark: #16483d;--green-soft: #dcece5;--rust: #91442f;--rust-soft: #f4ded1;--yellow-soft: #ece7bd;--danger: #872d23;--danger-soft: #f8d8d2}*{box-sizing:border-box}body{margin:0}button,input,textarea,select{font:inherit}button{min-height:38px;border:1px solid var(--border-strong);border-radius:6px;background:var(--surface);color:#17201c;padding:0 12px;display:inline-flex;align-items:center;justify-content:center;gap:8px;cursor:pointer;transition:background .14s ease,border-color .14s ease,color .14s ease,box-shadow .14s ease}.button-link,.entry-action-button{min-height:38px;border:1px solid var(--border-strong);border-radius:6px;color:#17201c;text-decoration:none;display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:0 12px}button.primary{color:#fff;background:var(--green);border-color:var(--green)}button:hover:not(:disabled),.button-link:hover,.entry-action-button:hover{border-color:var(--green);background:var(--surface-cool)}button.primary:hover:not(:disabled){background:var(--green-dark);border-color:var(--green-dark)}button:focus-visible,.button-link:focus-visible,.entry-action-button:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible{outline:2px solid rgba(31,100,85,.28);outline-offset:2px}button:disabled,input:disabled,textarea:disabled,select:disabled{opacity:.55;cursor:not-allowed}input,textarea,select{width:100%;border:1px solid var(--border-strong);border-radius:6px;background:#fff;color:#17201c;min-height:38px;padding:8px 10px}textarea{min-height:72px;resize:vertical}h1,h2,h3,h4,p{margin:0}h1{font-size:32px;line-height:1.15}h2{font-size:22px}h3{font-size:18px}h4{font-size:15px}.app-shell{max-width:1480px;margin:0 auto;padding:24px}.app-header{display:flex;justify-content:space-between;align-items:center;gap:18px;margin-bottom:18px}.app-title-button{min-height:0;border:0;background:transparent;padding:0;color:inherit;justify-content:flex-start;text-align:left}.app-title-button:hover:not(:disabled){background:transparent;color:var(--green)}.profile-open-button{flex:0 0 auto}.settings-menu-wrap{position:relative;flex:0 0 auto}.settings-menu{position:absolute;top:calc(100% + 8px);right:0;z-index:40;min-width:150px;display:grid;gap:4px;border:1px solid var(--border-strong);border-radius:8px;background:var(--surface);box-shadow:0 18px 42px #16221d2e;padding:6px}.settings-menu button{width:100%;justify-content:flex-start;border-color:transparent;background:transparent}.settings-menu button:hover{border-color:var(--border);background:var(--surface-cool)}.app-header p,.workflow-head p,.section-heading p,.card-note{color:var(--text-muted);margin-top:5px}.eyebrow{color:var(--rust);font-size:12px;text-transform:uppercase;letter-spacing:0;font-weight:700}.toast{background:var(--green-dark);color:#fff;border-radius:6px;padding:10px 12px;margin-bottom:14px}.workspace{display:grid;grid-template-columns:292px minmax(0,1fr);gap:18px;align-items:start}.nav-collapsed .workspace{grid-template-columns:64px minmax(0,1fr)}.chapter-nav{position:sticky;top:16px;height:calc(100vh - 48px);overflow:hidden;border:1px solid var(--border);border-radius:8px;background:var(--surface);display:flex;flex-direction:column;box-shadow:0 10px 24px #24312a0f}.chapter-nav-head{min-height:54px;padding:10px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;gap:8px}.chapter-nav-list{padding:8px;overflow:auto;display:grid;gap:5px}.chapter-nav-item{width:100%;justify-content:flex-start;min-height:42px;padding:6px 8px;background:transparent;border-color:transparent}.chapter-nav.collapsed .chapter-nav-head,.chapter-nav.collapsed .chapter-nav-item{justify-content:center}.chapter-nav-item.selected{border-color:var(--green);background:var(--green-soft)}.chapter-index{width:24px;flex:0 0 auto;color:var(--rust);font-weight:700;font-size:12px}.chapter-name{flex:1;min-width:0;text-align:left;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.chapter-status,.flow-status{flex:0 0 auto;border-radius:999px;background:#e4e9e2;color:#536057;padding:3px 8px;font-size:12px;white-space:nowrap}.chapter-status.in_progress,.flow-status.generated,.flow-status.previewed,.flow-status.printed,.flow-status.submitted,.flow-status.pdf_received{background:#f2d9c8;color:#7f3a23}.chapter-status.review_due,.flow-status.graded,.flow-status.needs_review{background:var(--yellow-soft);color:#645600}.chapter-status.mastered,.flow-status.archived{background:#cfe8da;color:var(--green)}.flow-status.failed,.flow-status.failed_generate,.flow-status.failed_grade{background:var(--danger-soft);color:var(--danger)}.flow-status.available{background:var(--green-soft);color:var(--green)}.flow-status.missing{background:#fff6d7;color:#7a5a0f}.flow-status.missing_source,.flow-status.unavailable{background:#e7ece8;color:var(--text-muted)}.icon-button{width:38px;padding:0}.chapter-workspace{min-width:0}.panel{background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:18px;margin-bottom:16px;box-shadow:0 10px 24px #24312a0d}.chapter-summary{display:grid;grid-template-columns:minmax(250px,1fr) minmax(400px,1.2fr);gap:16px;align-items:end}.stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px}.stats-grid.compact{grid-template-columns:repeat(5,minmax(0,1fr))}.stat-tile{border:1px solid var(--border);border-radius:6px;background:var(--surface-muted);padding:12px}.stat-tile span{color:var(--text-muted);font-size:13px}.stat-tile strong{display:block;margin-top:6px;font-size:22px}.knowledge-map-panel{display:grid;gap:16px}.knowledge-map-head{display:flex;justify-content:space-between;gap:16px;align-items:start}.knowledge-view-tabs{display:inline-flex;gap:4px;padding:4px;border:1px solid var(--border);border-radius:8px;background:var(--surface-muted)}.knowledge-view-tabs button{min-height:34px;border-color:transparent;background:transparent;white-space:nowrap}.knowledge-view-tabs button.selected{color:var(--green);border-color:var(--green);background:#fff;font-weight:700}.knowledge-map-summary{display:flex;flex-wrap:wrap;gap:10px;color:var(--text-soft);font-size:13px}.knowledge-map-summary span{display:inline-flex;align-items:center;gap:6px}.knowledge-filter-group{display:inline-flex;gap:4px;padding:2px;border:1px solid var(--border);border-radius:999px;background:var(--surface-muted)}.knowledge-filter-group button{min-height:26px;border-radius:999px;border-color:transparent;background:transparent;padding:0 10px;font-size:12px}.knowledge-filter-group button.selected{color:#fff;background:var(--green);border-color:var(--green)}.legend-dot{width:10px;height:10px;border:1px solid var(--border-strong);border-radius:999px;background:#fff}.legend-dot.mastered{background:#bfe1cf;border-color:var(--green)}.legend-dot.needs_review{background:var(--yellow-soft);border-color:#c6b85d}.knowledge-wall{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:10px}.knowledge-card{min-height:132px;display:grid;align-content:space-between;gap:10px;border:1px solid var(--border);border-radius:8px;padding:12px;background:#fff}.knowledge-card.mastered{border-color:#94bca8;background:#edf7f1}.knowledge-card.needs_review{border-color:#d7cb76;background:#fff9d9}.knowledge-card.uncovered{background:#fff}.knowledge-card strong{display:block;line-height:1.35}.knowledge-card span{color:var(--text-muted);font-size:12px}.knowledge-card p{color:var(--text-soft);font-size:13px;line-height:1.55}.knowledge-card-foot{display:flex;justify-content:space-between;gap:8px;align-items:center}.knowledge-card-foot small{color:var(--text-muted)}.knowledge-market-layout{display:block}.knowledge-market-board{position:relative;display:grid;grid-template-columns:repeat(auto-fill,minmax(82px,1fr));grid-auto-flow:dense;gap:1px;padding:4px;border:0;border-radius:8px;background:#20342e}.knowledge-market-tile{--tile-bg: #fffdf8;--tile-fg: #17201c;--tile-muted: #627167;position:relative;min-height:54px;display:grid;place-items:center;gap:2px;padding:6px;border-radius:3px;text-align:center;color:var(--tile-fg);background:var(--tile-bg);border:0;box-shadow:none}.knowledge-market-tile:hover:not(:disabled){z-index:40;background:var(--tile-bg);border-color:transparent;filter:brightness(.96)}.knowledge-market-tile:focus-visible{z-index:40;outline:2px solid rgba(31,100,85,.42);outline-offset:1px}.knowledge-market-tile strong{width:100%;font-size:12.5px;line-height:1.22;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}.knowledge-hover-card span{color:var(--tile-muted);font-size:11px;line-height:1.1}.knowledge-market-tile.mastered{--tile-bg: #1f9d5b;--tile-fg: #ffffff;--tile-muted: rgba(255, 255, 255, .82)}.knowledge-market-tile.needs_review{--tile-bg: #f3dc61;--tile-fg: #17201c;--tile-muted: #5d612f}.knowledge-market-tile.review-low{--tile-bg: #f8e986;--tile-muted: #667044}.knowledge-market-tile.review-mid{--tile-bg: #efd347;--tile-muted: #5b5f2b}.knowledge-market-tile.review-high{--tile-bg: #d49a22;--tile-muted: #443a17}.knowledge-market-tile.uncovered{--tile-bg: #fffdf8;--tile-fg: #17201c;--tile-muted: #738077}.knowledge-hover-card{position:absolute;left:0;top:calc(100% + 6px);z-index:80;width:min(320px,70vw);display:none;gap:7px;border:1px solid var(--border-strong);border-radius:8px;background:#fffdf8;color:#17201c;box-shadow:0 18px 46px #16221d42;padding:12px;text-align:left}.knowledge-market-tile:hover .knowledge-hover-card,.knowledge-market-tile:focus-visible .knowledge-hover-card{display:grid}.knowledge-hover-card strong{font-size:15px;line-height:1.35;display:block}.knowledge-hover-card em{color:var(--text-soft);font-style:normal;font-size:13px;line-height:1.55}.knowledge-hover-card>span{color:var(--text-muted);font-size:12px}.knowledge-image-board{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:12px}.knowledge-page-card{display:grid;grid-template-columns:110px minmax(0,1fr);gap:12px;border:1px solid var(--border);border-radius:8px;background:#fff;padding:10px}.knowledge-page-card img{width:100%;height:150px;object-fit:cover;object-position:top;border:1px solid var(--border);border-radius:6px;background:var(--surface-muted)}.knowledge-page-card strong{display:block;margin-bottom:3px}.knowledge-page-card p{color:var(--text-muted);font-size:13px;margin-bottom:8px}.page-point-list{display:flex;flex-wrap:wrap;gap:6px}.point-pill{border:1px solid var(--border-strong);border-radius:999px;background:#fff;padding:4px 8px;color:var(--text-soft);font-size:12px}.point-pill.mastered{border-color:#94bca8;background:#edf7f1;color:var(--green)}.point-pill.needs_review{border-color:#d7cb76;background:#fff9d9;color:#645600}.flow-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:10px}.active-paper-panel,.result-panel{display:grid;gap:14px;align-content:start}.workflow-entry-card{width:100%;min-height:72px;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;justify-content:stretch;text-align:left;padding:12px 14px;gap:14px;background:#fff;border:1px solid var(--border-strong);border-radius:6px}.workflow-entry-card.disabled{background:#f1f4f0;border-color:var(--border)}.workflow-head,.section-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.section-heading{justify-content:flex-start;margin-bottom:12px}.section-heading svg{color:var(--green);margin-top:1px}.subtle-icon-button{width:34px;min-height:34px;margin-left:auto;padding:0;border-color:transparent;background:transparent;color:var(--text-muted)}.subtle-icon-button:hover:not(:disabled){border-color:var(--border);background:var(--surface-cool);color:var(--green)}.overview-actions{display:grid;gap:12px}.practice-entry-strip{display:grid;gap:10px;padding:14px}.practice-entry-strip .section-heading{margin-bottom:0}.practice-entry-strip .workflow-entry-card{min-height:60px;padding:12px;background:#fbfdf9}.practice-entry-strip .workflow-entry-card>div:first-child strong{font-size:15px}.practice-entry-strip .workflow-entry-card small{display:none}.practice-overview-progress{display:grid;gap:12px;border:1px solid var(--border);border-radius:6px;background:var(--surface-muted);padding:14px}.progress-copy{display:flex;align-items:baseline;justify-content:space-between;gap:12px}.progress-copy span,.workflow-entry-card small{color:var(--text-muted);font-size:13px}.progress-copy strong{font-size:18px}.progress-track{height:8px;overflow:hidden;border-radius:999px;background:#dfe7e1}.progress-track span{display:block;height:100%;border-radius:inherit;background:var(--green)}.progress-stat-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px}.progress-stat-grid .stat-tile{padding:10px}.progress-stat-grid .stat-tile strong{font-size:20px}.practice-stat-line{display:flex;flex-wrap:wrap;gap:8px}.practice-stat-line span{border:1px solid var(--border);border-radius:999px;background:#fff;color:var(--text-soft);font-size:12px;padding:5px 9px}.workflow-entry-card>div:first-child{min-width:0;display:grid;gap:3px}.workflow-entry-card>div:first-child strong{font-size:16px}.entry-action-cluster{min-width:max-content;display:flex;align-items:center;justify-content:flex-end;gap:10px}.entry-action-button{min-width:104px;color:#fff;background:var(--green);border-color:var(--green);font-weight:700;white-space:nowrap;padding:0 12px 0 14px;box-shadow:0 4px 10px #1f645524;transition:background .14s ease,border-color .14s ease,color .14s ease,box-shadow .14s ease,transform .14s ease}.entry-action-button svg{margin-right:-3px}.entry-action-button:hover{color:#fff;background:var(--green-dark);border-color:var(--green-dark);box-shadow:0 6px 14px #1f645533;transform:translateY(-1px)}.entry-action-button.disabled{color:var(--text-muted);background:#e7ece8;border-color:var(--border);box-shadow:none;pointer-events:none}.extract-workspace{display:grid;gap:16px;scroll-margin-top:16px}.extract-compact-panel{display:flex;flex-wrap:wrap;align-items:center;gap:10px;padding:12px 14px}.extract-hero-panel{display:flex;align-items:center;gap:14px}.extract-hero-panel p,.extract-status-panel p,.extract-source-panel p{color:var(--text-muted);margin-top:5px}.extract-status-panel,.extract-source-panel{display:grid;gap:14px}.extract-profile-summary{display:flex;flex-wrap:wrap;gap:8px}.extract-profile-summary span{border:1px solid var(--border);border-radius:999px;background:#fff;color:var(--text-soft);font-size:12px;padding:5px 9px}.extract-actions{display:flex;flex-wrap:wrap;align-items:center;gap:10px}.extract-source-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:10px}.extract-source-grid a{display:grid;gap:7px;color:inherit;text-decoration:none;border:1px solid var(--border);border-radius:8px;background:var(--surface-muted);padding:8px}.extract-source-grid img{width:100%;aspect-ratio:3 / 4;object-fit:cover;object-position:top;border:1px solid var(--border);border-radius:6px;background:#fff}.extract-source-grid span{overflow:hidden;color:var(--text-soft);font-size:12px;white-space:nowrap;text-overflow:ellipsis}.extract-profile-dialog{width:min(720px,calc(100vw - 40px))}.extract-profile-fields{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}.extract-focus-list{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}.extract-focus-list>.field-label{flex:0 0 100%}.check-pill,.check-row{display:inline-flex;align-items:center;gap:7px;border:1px solid var(--border);border-radius:999px;background:#fff;padding:7px 10px;color:#17201c;font-size:13px}.check-row{width:fit-content;margin-top:12px;border-radius:6px}.check-pill input,.check-row input{width:auto;min-height:0}.destructive-note{margin-top:12px;border:1px solid #d8aaa2;border-radius:6px;background:#fff7f5;color:var(--danger);padding:10px 12px;font-size:13px;line-height:1.55}.practice-workspace{display:grid;gap:16px}.workspace-title-panel{display:flex;align-items:center;gap:14px;min-height:96px}.workspace-title-panel button{flex:0 0 auto}.workspace-title-panel p{color:var(--text-muted);margin-top:5px}.paper-workspace-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(300px,360px);gap:18px;align-items:start}.paper-main-column,.practice-side-rail{display:grid;gap:16px;align-content:start}.practice-side-rail{position:sticky;top:18px}.paper-toolbar{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.paper-toolbar p{color:var(--text-muted);margin-top:5px}.paper-toolbar-side{display:grid;gap:10px;justify-items:end;flex:0 0 auto}.paper-mode-switch{display:inline-grid;grid-template-columns:repeat(2,minmax(72px,1fr));gap:4px;padding:4px;border:1px solid var(--border);border-radius:8px;background:var(--surface-muted)}.paper-mode-switch button{min-height:32px;border-color:transparent;background:transparent;padding:0 10px;white-space:nowrap}.paper-mode-switch button.selected{border-color:var(--green);background:#fff;color:var(--green);font-weight:700}.paper-preview-frame{height:min(78vh,900px);min-height:620px;border:1px solid var(--border);border-radius:8px;background:#f8faf7;overflow:hidden}.paper-preview-frame iframe{width:100%;height:100%;border:0;display:block;background:#fff}.create-practice-panel,.workflow-controls{display:grid;gap:12px}.workflow-controls{grid-template-columns:minmax(320px,1fr) minmax(150px,210px);align-items:end;border:1px solid var(--border);border-radius:8px;background:var(--surface-muted);padding:12px}label{display:grid;gap:6px;color:#4b574f;font-size:13px}.field-hint{color:var(--text-muted);font-size:12px}.duration-field{display:grid;gap:6px}.field-label{color:#4b574f;font-size:13px;font-weight:700}.duration-options{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}.duration-option{min-height:40px;padding:0 8px;white-space:nowrap;background:#fff}.duration-option.selected{border-color:var(--green);background:var(--green-soft);color:var(--green);font-weight:700}.first-round-progress{display:grid;gap:8px;padding:11px 12px;border:1px solid var(--border);border-radius:8px;background:var(--surface-muted)}.first-round-progress .progress-copy strong{font-size:16px}.first-round-progress p{color:var(--text-muted);font-size:12px;line-height:1.5}.practice-links{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}.paper-action-bar{display:grid;grid-template-columns:minmax(280px,1.35fr) minmax(150px,1fr) minmax(170px,1fr);gap:8px}.paper-page-switch{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);gap:6px;align-items:center;min-height:38px}.paper-page-switch button{min-height:38px;padding:0 10px}.paper-page-switch span{color:var(--text-muted);font-size:13px;font-weight:700;white-space:nowrap}.upload-strip{display:grid;gap:10px;padding:12px;border:1px solid var(--border);border-radius:8px;background:#fff}.upload-strip p{color:var(--text-muted);font-size:13px;margin-top:3px}.upload-strip .button-link{justify-self:start}.upload-file-actions{display:flex;flex-wrap:wrap;gap:8px}.upload-strip .danger-subtle{justify-self:start;color:var(--danger);border-color:#d8aaa2;background:#fff7f5}.upload-strip .danger-subtle:hover:not(:disabled){color:var(--danger);border-color:var(--danger);background:var(--danger-soft)}.full-width{width:100%}.card-note{border-left:3px solid var(--border-strong);padding-left:10px;font-size:13px}.job-line{display:grid;gap:8px;border:1px solid var(--border);border-radius:6px;background:var(--surface-muted);padding:9px 10px;color:#4b574f}.job-line-current{display:flex;align-items:center;gap:8px;min-width:0}.job-line-current span{min-width:0;overflow-wrap:anywhere}.job-events{display:grid;gap:5px;margin:0;padding-left:24px;color:#6c746c;font-size:12px;line-height:1.45}.job-events li{overflow-wrap:anywhere}.job-line.failed{background:#fff0ed;color:var(--danger)}.spin{animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.result-button,.result-actions{justify-self:start}.result-actions{display:flex;flex-wrap:wrap;gap:8px}.modal-backdrop{position:fixed;inset:0;z-index:50;display:grid;place-items:center;background:#17201c5c;padding:24px}.modal-panel{width:min(920px,100%);max-height:min(820px,calc(100vh - 48px));overflow:auto;border:1px solid var(--border-strong);border-radius:8px;background:var(--surface);box-shadow:0 24px 70px #15251f47;padding:16px}.modal-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:12px}.modal-panel .result-panel{border:0;box-shadow:none;padding:0}.profile-dialog{width:min(720px,100%);display:grid;gap:12px}.settings-dialog{width:min(680px,100%);display:grid;gap:12px}.profile-form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.profile-dialog label,.settings-dialog label{display:grid;gap:6px}.profile-dialog textarea{min-height:82px}.inline-check{display:flex!important;grid-template-columns:none;align-items:center;gap:8px;color:var(--text-muted)}.inline-check input{width:auto}.settings-status,.settings-path,.form-error{border:1px solid var(--border);border-radius:6px;padding:9px 10px;font-size:14px}.settings-status.configured{background:var(--green-soft);color:var(--green-dark)}.settings-status.missing,.form-error{background:var(--peach);color:var(--rust)}.settings-path{background:var(--surface-muted);color:var(--text-muted);word-break:break-all}.modal-actions{display:flex;justify-content:flex-end;gap:8px;padding-top:4px}.artifact-links{margin-top:12px;border:1px solid var(--border);border-radius:6px;background:var(--surface-muted);padding:10px}.artifact-links h4{margin-bottom:8px}.artifact-links div{display:flex;flex-wrap:wrap;gap:8px}.artifact-links a{border:1px solid var(--border-strong);border-radius:999px;color:var(--green);background:var(--surface);padding:5px 10px;text-decoration:none;font-size:13px}.history-list{display:grid;gap:8px}.history-select-row{width:100%;display:grid;grid-template-columns:auto minmax(0,1fr);gap:8px 10px;justify-content:stretch;text-align:left;border:1px solid var(--border);background:var(--surface-muted);padding:10px;min-height:62px}.history-select-row.selected{border-color:var(--green);background:var(--green-soft)}.history-select-row .flow-status{align-self:start}.history-select-row strong,.history-select-row span,.history-select-row small{min-width:0}.history-select-row strong,.history-select-row div>span{display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.history-select-row small{grid-column:2;color:var(--text-muted)}.history-row{display:grid;grid-template-columns:minmax(0,1fr) auto auto auto;gap:8px;align-items:center;border:1px solid var(--border);border-radius:6px;background:var(--surface-muted);padding:10px}.history-row strong,.history-row span{display:block}.history-row div>span{color:var(--text-muted);overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.empty-state{border:1px dashed var(--border-strong);border-radius:6px;color:var(--text-muted);padding:18px;background:var(--surface-muted)}.grading-table{display:grid;gap:8px;margin-top:12px}.grading-row{display:grid;grid-template-columns:60px 90px minmax(0,1fr);gap:10px;align-items:start;border:1px solid var(--border);border-radius:6px;background:var(--surface-muted);padding:10px;text-align:left;width:100%}.grading-row p{color:#4b574f}.grading-row.selected{border-color:var(--green);background:var(--green-soft)}.grading-detail-card{display:grid;gap:8px;margin-top:12px;border:1px solid var(--border-strong);border-radius:8px;background:#fff;padding:12px}.grading-detail-card p{color:#4b574f}.weak-points{margin-top:14px;display:flex;flex-wrap:wrap;gap:8px;align-items:center}.weak-points h4{width:100%}.weak-points span{border-radius:999px;background:var(--rust-soft);color:#7f3a23;padding:4px 9px;font-size:12px}.weak-points p{color:var(--text-muted)}@media(max-width:1080px){.workspace,.nav-collapsed .workspace,.chapter-summary,.flow-grid,.paper-workspace-grid{grid-template-columns:1fr}.practice-side-rail{position:static}.chapter-nav{position:static;height:auto;max-height:320px}.chapter-nav.collapsed{max-height:54px}.chapter-nav.collapsed .chapter-nav-list{display:none}}@media(max-width:760px){.app-shell{padding:14px}.app-header,.workflow-head,.paper-toolbar,.workspace-title-panel{display:grid}.stats-grid,.stats-grid.compact,.practice-links,.paper-action-bar,.workflow-controls,.history-row,.profile-form-grid,.grading-row,.progress-copy,.workflow-entry-card{grid-template-columns:1fr}.progress-stat-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.progress-copy{display:grid}.history-select-row small{grid-column:1}}
|
|
1
|
+
:root{color:#17201c;background:#eef2ef;font-family:Inter,PingFang SC,Microsoft YaHei,Arial,sans-serif;font-size:16px;--surface: #fffdf8;--surface-muted: #f5f7f2;--surface-cool: #edf4f0;--border: #d4ddd5;--border-strong: #b9c6be;--text-soft: #5f6c64;--text-muted: #738077;--green: #1f6455;--green-dark: #16483d;--green-soft: #dcece5;--rust: #91442f;--rust-soft: #f4ded1;--yellow-soft: #ece7bd;--danger: #872d23;--danger-soft: #f8d8d2}*{box-sizing:border-box}body{margin:0}button,input,textarea,select{font:inherit}button{min-height:38px;border:1px solid var(--border-strong);border-radius:6px;background:var(--surface);color:#17201c;padding:0 12px;display:inline-flex;align-items:center;justify-content:center;gap:8px;cursor:pointer;transition:background .14s ease,border-color .14s ease,color .14s ease,box-shadow .14s ease}.button-link,.entry-action-button{min-height:38px;border:1px solid var(--border-strong);border-radius:6px;color:#17201c;text-decoration:none;display:inline-flex;align-items:center;justify-content:center;gap:8px;padding:0 12px}button.primary{color:#fff;background:var(--green);border-color:var(--green)}button:hover:not(:disabled),.button-link:hover,.entry-action-button:hover{border-color:var(--green);background:var(--surface-cool)}button.primary:hover:not(:disabled){background:var(--green-dark);border-color:var(--green-dark)}button:focus-visible,.button-link:focus-visible,.entry-action-button:focus-visible,input:focus-visible,textarea:focus-visible,select:focus-visible{outline:2px solid rgba(31,100,85,.28);outline-offset:2px}button:disabled,input:disabled,textarea:disabled,select:disabled{opacity:.55;cursor:not-allowed}input,textarea,select{width:100%;border:1px solid var(--border-strong);border-radius:6px;background:#fff;color:#17201c;min-height:38px;padding:8px 10px}textarea{min-height:72px;resize:vertical}h1,h2,h3,h4,p{margin:0}h1{font-size:32px;line-height:1.15}h2{font-size:22px}h3{font-size:18px}h4{font-size:15px}.app-shell{max-width:1480px;margin:0 auto;padding:24px}.app-header{display:flex;justify-content:space-between;align-items:center;gap:18px;margin-bottom:18px}.app-title-button{min-height:0;border:0;background:transparent;padding:0;color:inherit;justify-content:flex-start;text-align:left}.app-title-button:hover:not(:disabled){background:transparent;color:var(--green)}.profile-open-button{flex:0 0 auto}.settings-menu-wrap{position:relative;flex:0 0 auto}.settings-menu{position:absolute;top:calc(100% + 8px);right:0;z-index:40;min-width:150px;display:grid;gap:4px;border:1px solid var(--border-strong);border-radius:8px;background:var(--surface);box-shadow:0 18px 42px #16221d2e;padding:6px}.settings-menu button{width:100%;justify-content:flex-start;border-color:transparent;background:transparent}.settings-menu button:hover{border-color:var(--border);background:var(--surface-cool)}.app-header p,.workflow-head p,.section-heading p,.card-note{color:var(--text-muted);margin-top:5px}.eyebrow{color:var(--rust);font-size:12px;text-transform:uppercase;letter-spacing:0;font-weight:700}.toast{background:var(--green-dark);color:#fff;border-radius:6px;padding:10px 12px;margin-bottom:14px}.workspace{display:grid;grid-template-columns:292px minmax(0,1fr);gap:18px;align-items:start}.nav-collapsed .workspace{grid-template-columns:64px minmax(0,1fr)}.chapter-nav{position:sticky;top:16px;height:calc(100vh - 48px);overflow:hidden;border:1px solid var(--border);border-radius:8px;background:var(--surface);display:flex;flex-direction:column;box-shadow:0 10px 24px #24312a0f}.chapter-nav-head{min-height:54px;padding:10px;border-bottom:1px solid var(--border);display:flex;align-items:center;justify-content:space-between;gap:8px}.chapter-nav-list{padding:8px;overflow:auto;display:grid;gap:5px}.chapter-nav-item{width:100%;justify-content:flex-start;min-height:42px;padding:6px 8px;background:transparent;border-color:transparent}.chapter-nav.collapsed .chapter-nav-head,.chapter-nav.collapsed .chapter-nav-item{justify-content:center}.chapter-nav-item.selected{border-color:var(--green);background:var(--green-soft)}.chapter-index{width:24px;flex:0 0 auto;color:var(--rust);font-weight:700;font-size:12px}.chapter-name{flex:1;min-width:0;text-align:left;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.chapter-status,.flow-status{flex:0 0 auto;border-radius:999px;background:#e4e9e2;color:#536057;padding:3px 8px;font-size:12px;white-space:nowrap}.chapter-status.in_progress,.flow-status.generated,.flow-status.previewed,.flow-status.printed,.flow-status.submitted,.flow-status.pdf_received{background:#f2d9c8;color:#7f3a23}.chapter-status.review_due,.flow-status.graded,.flow-status.needs_review{background:var(--yellow-soft);color:#645600}.chapter-status.mastered,.flow-status.archived{background:#cfe8da;color:var(--green)}.flow-status.failed,.flow-status.failed_generate,.flow-status.failed_grade{background:var(--danger-soft);color:var(--danger)}.flow-status.available{background:var(--green-soft);color:var(--green)}.flow-status.missing{background:#fff6d7;color:#7a5a0f}.flow-status.missing_source,.flow-status.unavailable{background:#e7ece8;color:var(--text-muted)}.icon-button{width:38px;padding:0}.chapter-workspace{min-width:0}.panel{background:var(--surface);border:1px solid var(--border);border-radius:8px;padding:18px;margin-bottom:16px;box-shadow:0 10px 24px #24312a0d}.chapter-summary{display:grid;grid-template-columns:minmax(250px,1fr) minmax(400px,1.2fr);gap:16px;align-items:end}.stats-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:10px}.stats-grid.compact{grid-template-columns:repeat(5,minmax(0,1fr))}.stat-tile{border:1px solid var(--border);border-radius:6px;background:var(--surface-muted);padding:12px}.stat-tile span{color:var(--text-muted);font-size:13px}.stat-tile strong{display:block;margin-top:6px;font-size:22px}.knowledge-map-panel{display:grid;gap:16px}.knowledge-map-head{display:flex;justify-content:space-between;gap:16px;align-items:start}.knowledge-view-tabs{display:inline-flex;gap:4px;padding:4px;border:1px solid var(--border);border-radius:8px;background:var(--surface-muted)}.knowledge-view-tabs button{min-height:34px;border-color:transparent;background:transparent;white-space:nowrap}.knowledge-view-tabs button.selected{color:var(--green);border-color:var(--green);background:#fff;font-weight:700}.knowledge-map-summary{display:flex;flex-wrap:wrap;gap:10px;color:var(--text-soft);font-size:13px}.knowledge-map-summary span{display:inline-flex;align-items:center;gap:6px}.knowledge-filter-group{display:inline-flex;gap:4px;padding:2px;border:1px solid var(--border);border-radius:999px;background:var(--surface-muted)}.knowledge-filter-group button{min-height:26px;border-radius:999px;border-color:transparent;background:transparent;padding:0 10px;font-size:12px}.knowledge-filter-group button.selected{color:#fff;background:var(--green);border-color:var(--green)}.legend-dot{width:10px;height:10px;border:1px solid var(--border-strong);border-radius:999px;background:#fff}.legend-dot.mastered{background:#bfe1cf;border-color:var(--green)}.legend-dot.needs_review{background:var(--yellow-soft);border-color:#c6b85d}.knowledge-wall{display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,1fr));gap:10px}.knowledge-card{min-height:132px;display:grid;align-content:space-between;gap:10px;border:1px solid var(--border);border-radius:8px;padding:12px;background:#fff}.knowledge-card.mastered{border-color:#94bca8;background:#edf7f1}.knowledge-card.needs_review{border-color:#d7cb76;background:#fff9d9}.knowledge-card.uncovered{background:#fff}.knowledge-card strong{display:block;line-height:1.35}.knowledge-card span{color:var(--text-muted);font-size:12px}.knowledge-card p{color:var(--text-soft);font-size:13px;line-height:1.55}.knowledge-card-foot{display:flex;justify-content:space-between;gap:8px;align-items:center}.knowledge-card-foot small{color:var(--text-muted)}.knowledge-market-layout{display:block}.knowledge-market-board{position:relative;display:grid;grid-template-columns:repeat(auto-fill,minmax(82px,1fr));grid-auto-flow:dense;gap:1px;padding:4px;border:0;border-radius:8px;background:#20342e}.knowledge-market-tile{--tile-bg: #fffdf8;--tile-fg: #17201c;--tile-muted: #627167;position:relative;min-height:54px;display:grid;place-items:center;gap:2px;padding:6px;border-radius:3px;text-align:center;color:var(--tile-fg);background:var(--tile-bg);border:0;box-shadow:none}.knowledge-market-tile:hover:not(:disabled){z-index:40;background:var(--tile-bg);border-color:transparent;filter:brightness(.96)}.knowledge-market-tile:focus-visible{z-index:40;outline:2px solid rgba(31,100,85,.42);outline-offset:1px}.knowledge-market-tile strong{width:100%;font-size:12.5px;line-height:1.22;display:-webkit-box;-webkit-line-clamp:3;-webkit-box-orient:vertical;overflow:hidden}.knowledge-hover-card span{color:var(--tile-muted);font-size:11px;line-height:1.1}.knowledge-market-tile.mastered{--tile-bg: #1f9d5b;--tile-fg: #ffffff;--tile-muted: rgba(255, 255, 255, .82)}.knowledge-market-tile.needs_review{--tile-bg: #f3dc61;--tile-fg: #17201c;--tile-muted: #5d612f}.knowledge-market-tile.review-low{--tile-bg: #f8e986;--tile-muted: #667044}.knowledge-market-tile.review-mid{--tile-bg: #efd347;--tile-muted: #5b5f2b}.knowledge-market-tile.review-high{--tile-bg: #d49a22;--tile-muted: #443a17}.knowledge-market-tile.uncovered{--tile-bg: #fffdf8;--tile-fg: #17201c;--tile-muted: #738077}.knowledge-hover-card{position:absolute;left:0;top:calc(100% + 6px);z-index:80;width:min(320px,70vw);display:none;gap:7px;border:1px solid var(--border-strong);border-radius:8px;background:#fffdf8;color:#17201c;box-shadow:0 18px 46px #16221d42;padding:12px;text-align:left}.knowledge-market-tile:hover .knowledge-hover-card,.knowledge-market-tile:focus-visible .knowledge-hover-card{display:grid}.knowledge-hover-card strong{font-size:15px;line-height:1.35;display:block}.knowledge-hover-card em{color:var(--text-soft);font-style:normal;font-size:13px;line-height:1.55}.knowledge-hover-card>span{color:var(--text-muted);font-size:12px}.knowledge-image-board{display:grid;grid-template-columns:repeat(auto-fill,minmax(300px,1fr));gap:12px}.knowledge-page-card{display:grid;grid-template-columns:110px minmax(0,1fr);gap:12px;border:1px solid var(--border);border-radius:8px;background:#fff;padding:10px}.knowledge-page-card img{width:100%;height:150px;object-fit:cover;object-position:top;border:1px solid var(--border);border-radius:6px;background:var(--surface-muted)}.knowledge-page-card strong{display:block;margin-bottom:3px}.knowledge-page-card p{color:var(--text-muted);font-size:13px;margin-bottom:8px}.page-point-list{display:flex;flex-wrap:wrap;gap:6px}.point-pill{border:1px solid var(--border-strong);border-radius:999px;background:#fff;padding:4px 8px;color:var(--text-soft);font-size:12px}.point-pill.mastered{border-color:#94bca8;background:#edf7f1;color:var(--green)}.point-pill.needs_review{border-color:#d7cb76;background:#fff9d9;color:#645600}.flow-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(210px,1fr));gap:10px}.active-paper-panel,.result-panel{display:grid;gap:14px;align-content:start}.workflow-entry-card{width:100%;min-height:72px;display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;justify-content:stretch;text-align:left;padding:12px 14px;gap:14px;background:#fff;border:1px solid var(--border-strong);border-radius:6px}.workflow-entry-card.disabled{background:#f1f4f0;border-color:var(--border)}.workflow-head,.section-heading{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.section-heading{justify-content:flex-start;margin-bottom:12px}.section-heading svg{color:var(--green);margin-top:1px}.subtle-icon-button{width:34px;min-height:34px;margin-left:auto;padding:0;border-color:transparent;background:transparent;color:var(--text-muted)}.subtle-icon-button:hover:not(:disabled){border-color:var(--border);background:var(--surface-cool);color:var(--green)}.overview-actions{display:grid;gap:12px}.practice-entry-strip{display:grid;gap:10px;padding:14px}.practice-entry-strip .section-heading{margin-bottom:0}.practice-entry-strip .workflow-entry-card{min-height:60px;padding:12px;background:#fbfdf9}.practice-entry-strip .workflow-entry-card>div:first-child strong{font-size:15px}.practice-entry-strip .workflow-entry-card small{display:none}.practice-overview-progress{display:grid;gap:12px;border:1px solid var(--border);border-radius:6px;background:var(--surface-muted);padding:14px}.progress-copy{display:flex;align-items:baseline;justify-content:space-between;gap:12px}.progress-copy span,.workflow-entry-card small{color:var(--text-muted);font-size:13px}.progress-copy strong{font-size:18px}.progress-track{height:8px;overflow:hidden;border-radius:999px;background:#dfe7e1}.progress-track span{display:block;height:100%;border-radius:inherit;background:var(--green)}.progress-stat-grid{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:10px}.progress-stat-grid .stat-tile{padding:10px}.progress-stat-grid .stat-tile strong{font-size:20px}.practice-stat-line{display:flex;flex-wrap:wrap;gap:8px}.practice-stat-line span{border:1px solid var(--border);border-radius:999px;background:#fff;color:var(--text-soft);font-size:12px;padding:5px 9px}.workflow-entry-card>div:first-child{min-width:0;display:grid;gap:3px}.workflow-entry-card>div:first-child strong{font-size:16px}.entry-action-cluster{min-width:max-content;display:flex;align-items:center;justify-content:flex-end;gap:10px}.entry-action-button{min-width:104px;color:#fff;background:var(--green);border-color:var(--green);font-weight:700;white-space:nowrap;padding:0 12px 0 14px;box-shadow:0 4px 10px #1f645524;transition:background .14s ease,border-color .14s ease,color .14s ease,box-shadow .14s ease,transform .14s ease}.entry-action-button svg{margin-right:-3px}.entry-action-button:hover{color:#fff;background:var(--green-dark);border-color:var(--green-dark);box-shadow:0 6px 14px #1f645533;transform:translateY(-1px)}.entry-action-button.disabled{color:var(--text-muted);background:#e7ece8;border-color:var(--border);box-shadow:none;pointer-events:none}.extract-workspace{display:grid;gap:16px;scroll-margin-top:16px}.extract-compact-panel{display:grid;grid-template-columns:minmax(0,1fr) auto;align-items:center;gap:12px;padding:12px 14px}.extract-init-copy{min-width:0;display:grid;gap:3px}.extract-init-copy strong{font-size:15px}.extract-init-copy span{color:var(--text-muted);font-size:13px;line-height:1.4}.extract-init-actions{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:8px}.extract-init-chip{border:1px solid var(--border);border-radius:999px;background:#fff;color:var(--text-soft);font-size:12px;line-height:1;padding:6px 8px;white-space:nowrap}.extract-hero-panel{display:flex;align-items:center;gap:14px}.extract-hero-panel p,.extract-status-panel p,.extract-source-panel p{color:var(--text-muted);margin-top:5px}.extract-status-panel,.extract-source-panel{display:grid;gap:14px}.extract-profile-summary{display:flex;flex-wrap:wrap;gap:8px}.extract-profile-summary span{border:1px solid var(--border);border-radius:999px;background:#fff;color:var(--text-soft);font-size:12px;padding:5px 9px}.extract-actions{display:flex;flex-wrap:wrap;align-items:center;gap:10px}.extract-source-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(140px,1fr));gap:10px}.extract-source-grid a{display:grid;gap:7px;color:inherit;text-decoration:none;border:1px solid var(--border);border-radius:8px;background:var(--surface-muted);padding:8px}.extract-source-grid img{width:100%;aspect-ratio:3 / 4;object-fit:cover;object-position:top;border:1px solid var(--border);border-radius:6px;background:#fff}.extract-source-grid span{overflow:hidden;color:var(--text-soft);font-size:12px;white-space:nowrap;text-overflow:ellipsis}.extract-profile-dialog{width:min(720px,calc(100vw - 40px))}.extract-profile-fields{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:12px}.extract-focus-list{display:flex;flex-wrap:wrap;gap:8px;margin-top:12px}.extract-focus-list>.field-label{flex:0 0 100%}.check-pill,.check-row{display:inline-flex;align-items:center;gap:7px;border:1px solid var(--border);border-radius:999px;background:#fff;padding:7px 10px;color:#17201c;font-size:13px}.check-row{width:fit-content;margin-top:12px;border-radius:6px}.check-pill input,.check-row input{width:auto;min-height:0}.destructive-note{margin-top:12px;border:1px solid #d8aaa2;border-radius:6px;background:#fff7f5;color:var(--danger);padding:10px 12px;font-size:13px;line-height:1.55}.practice-workspace{display:grid;gap:16px}.workspace-title-panel{display:flex;align-items:center;gap:14px;min-height:96px}.workspace-title-panel button{flex:0 0 auto}.workspace-title-panel p{color:var(--text-muted);margin-top:5px}.paper-workspace-grid{display:grid;grid-template-columns:minmax(0,1fr) minmax(300px,360px);gap:18px;align-items:start}.paper-main-column,.practice-side-rail{display:grid;gap:16px;align-content:start}.practice-side-rail{position:sticky;top:18px}.paper-toolbar{display:flex;align-items:flex-start;justify-content:space-between;gap:12px}.paper-toolbar p{color:var(--text-muted);margin-top:5px}.paper-toolbar-side{display:grid;gap:10px;justify-items:end;flex:0 0 auto}.paper-mode-switch{display:inline-grid;grid-template-columns:repeat(2,minmax(72px,1fr));gap:4px;padding:4px;border:1px solid var(--border);border-radius:8px;background:var(--surface-muted)}.paper-mode-switch button{min-height:32px;border-color:transparent;background:transparent;padding:0 10px;white-space:nowrap}.paper-mode-switch button.selected{border-color:var(--green);background:#fff;color:var(--green);font-weight:700}.paper-preview-frame{height:min(78vh,900px);min-height:620px;border:1px solid var(--border);border-radius:8px;background:#f8faf7;overflow:hidden}.paper-preview-frame iframe{width:100%;height:100%;border:0;display:block;background:#fff}.create-practice-panel,.workflow-controls{display:grid;gap:12px}.workflow-controls{grid-template-columns:minmax(320px,1fr) minmax(150px,210px);align-items:end;border:1px solid var(--border);border-radius:8px;background:var(--surface-muted);padding:12px}label{display:grid;gap:6px;color:#4b574f;font-size:13px}.field-hint{color:var(--text-muted);font-size:12px}.duration-field{display:grid;gap:6px}.field-label{color:#4b574f;font-size:13px;font-weight:700}.duration-options{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px}.duration-option{min-height:40px;padding:0 8px;white-space:nowrap;background:#fff}.duration-option.selected{border-color:var(--green);background:var(--green-soft);color:var(--green);font-weight:700}.first-round-progress{display:grid;gap:8px;padding:11px 12px;border:1px solid var(--border);border-radius:8px;background:var(--surface-muted)}.first-round-progress .progress-copy strong{font-size:16px}.first-round-progress p{color:var(--text-muted);font-size:12px;line-height:1.5}.practice-links{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}.paper-action-bar{display:grid;grid-template-columns:minmax(280px,1.35fr) minmax(150px,1fr) minmax(170px,1fr);gap:8px}.paper-page-switch{display:grid;grid-template-columns:minmax(0,1fr) auto minmax(0,1fr);gap:6px;align-items:center;min-height:38px}.paper-page-switch button{min-height:38px;padding:0 10px}.paper-page-switch span{color:var(--text-muted);font-size:13px;font-weight:700;white-space:nowrap}.upload-strip{display:grid;gap:10px;padding:12px;border:1px solid var(--border);border-radius:8px;background:#fff}.upload-strip p{color:var(--text-muted);font-size:13px;margin-top:3px}.upload-strip .button-link{justify-self:start}.upload-file-actions{display:flex;flex-wrap:wrap;gap:8px}.upload-strip .danger-subtle{justify-self:start;color:var(--danger);border-color:#d8aaa2;background:#fff7f5}.upload-strip .danger-subtle:hover:not(:disabled){color:var(--danger);border-color:var(--danger);background:var(--danger-soft)}.full-width{width:100%}.card-note{border-left:3px solid var(--border-strong);padding-left:10px;font-size:13px}.job-line{display:grid;gap:8px;grid-column:1 / -1;border:1px solid var(--border);border-radius:6px;background:var(--surface-muted);padding:9px 10px;color:#4b574f}.job-line-current{display:flex;align-items:center;gap:8px;min-width:0}.job-line-current span{min-width:0;overflow-wrap:anywhere}.job-progress-track{height:6px;overflow:hidden;border-radius:999px;background:#dfe7e1}.job-progress-track span{display:block;height:100%;border-radius:inherit;background:var(--green);transition:width .22s ease}.job-events{display:grid;gap:5px;margin:0;padding-left:24px;color:#6c746c;font-size:12px;line-height:1.45}.job-events li{overflow-wrap:anywhere}.job-line.failed{background:#fff0ed;color:var(--danger)}.job-line.failed .job-progress-track span{background:var(--danger)}.job-failure-hint{border-top:1px solid #ecc5bf;color:#8f3326;font-size:12px;line-height:1.45;padding-top:8px}.spin{animation:spin 1s linear infinite}@keyframes spin{to{transform:rotate(360deg)}}.result-button,.result-actions{justify-self:start}.result-actions{display:flex;flex-wrap:wrap;gap:8px}.modal-backdrop{position:fixed;inset:0;z-index:50;display:grid;place-items:center;background:#17201c5c;padding:24px}.modal-panel{width:min(920px,100%);max-height:min(820px,calc(100vh - 48px));overflow:auto;border:1px solid var(--border-strong);border-radius:8px;background:var(--surface);box-shadow:0 24px 70px #15251f47;padding:16px}.modal-head{display:flex;align-items:flex-start;justify-content:space-between;gap:16px;margin-bottom:12px}.modal-panel .result-panel{border:0;box-shadow:none;padding:0}.profile-dialog{width:min(720px,100%);display:grid;gap:12px}.settings-dialog{width:min(680px,100%);display:grid;gap:12px}.profile-form-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:10px}.profile-dialog label,.settings-dialog label{display:grid;gap:6px}.profile-dialog textarea{min-height:82px}.inline-check{display:flex!important;grid-template-columns:none;align-items:center;gap:8px;color:var(--text-muted)}.inline-check input{width:auto}.settings-status,.settings-path,.form-error{border:1px solid var(--border);border-radius:6px;padding:9px 10px;font-size:14px}.settings-status.configured{background:var(--green-soft);color:var(--green-dark)}.settings-status.missing,.form-error{background:var(--peach);color:var(--rust)}.settings-path{background:var(--surface-muted);color:var(--text-muted);word-break:break-all}.modal-actions{display:flex;justify-content:flex-end;gap:8px;padding-top:4px}.artifact-links{margin-top:12px;border:1px solid var(--border);border-radius:6px;background:var(--surface-muted);padding:10px}.artifact-links h4{margin-bottom:8px}.artifact-links div{display:flex;flex-wrap:wrap;gap:8px}.artifact-links a{border:1px solid var(--border-strong);border-radius:999px;color:var(--green);background:var(--surface);padding:5px 10px;text-decoration:none;font-size:13px}.history-list{display:grid;gap:8px}.history-select-row{width:100%;display:grid;grid-template-columns:auto minmax(0,1fr);gap:8px 10px;justify-content:stretch;text-align:left;border:1px solid var(--border);background:var(--surface-muted);padding:10px;min-height:62px}.history-select-row.selected{border-color:var(--green);background:var(--green-soft)}.history-select-row .flow-status{align-self:start}.history-select-row strong,.history-select-row span,.history-select-row small{min-width:0}.history-select-row strong,.history-select-row div>span{display:block;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.history-select-row small{grid-column:2;color:var(--text-muted)}.history-row{display:grid;grid-template-columns:minmax(0,1fr) auto auto auto;gap:8px;align-items:center;border:1px solid var(--border);border-radius:6px;background:var(--surface-muted);padding:10px}.history-row strong,.history-row span{display:block}.history-row div>span{color:var(--text-muted);overflow:hidden;white-space:nowrap;text-overflow:ellipsis}.empty-state{border:1px dashed var(--border-strong);border-radius:6px;color:var(--text-muted);padding:18px;background:var(--surface-muted)}.grading-table{display:grid;gap:8px;margin-top:12px}.grading-row{display:grid;grid-template-columns:60px 90px minmax(0,1fr);gap:10px;align-items:start;border:1px solid var(--border);border-radius:6px;background:var(--surface-muted);padding:10px;text-align:left;width:100%}.grading-row p{color:#4b574f}.grading-row.selected{border-color:var(--green);background:var(--green-soft)}.grading-detail-card{display:grid;gap:8px;margin-top:12px;border:1px solid var(--border-strong);border-radius:8px;background:#fff;padding:12px}.grading-detail-card p{color:#4b574f}.weak-points{margin-top:14px;display:flex;flex-wrap:wrap;gap:8px;align-items:center}.weak-points h4{width:100%}.weak-points span{border-radius:999px;background:var(--rust-soft);color:#7f3a23;padding:4px 9px;font-size:12px}.weak-points p{color:var(--text-muted)}@media(max-width:1080px){.workspace,.nav-collapsed .workspace,.chapter-summary,.flow-grid,.paper-workspace-grid{grid-template-columns:1fr}.practice-side-rail{position:static}.chapter-nav{position:static;height:auto;max-height:320px}.chapter-nav.collapsed{max-height:54px}.chapter-nav.collapsed .chapter-nav-list{display:none}}@media(max-width:760px){.app-shell{padding:14px}.app-header,.workflow-head,.paper-toolbar,.workspace-title-panel{display:grid}.stats-grid,.stats-grid.compact,.practice-links,.paper-action-bar,.workflow-controls,.history-row,.profile-form-grid,.grading-row,.progress-copy,.extract-compact-panel,.workflow-entry-card{grid-template-columns:1fr}.extract-init-actions{justify-content:flex-start}.progress-stat-grid{grid-template-columns:repeat(2,minmax(0,1fr))}.progress-copy{display:grid}.history-select-row small{grid-column:1}}
|