@leonarto/spec-embryo 0.1.0
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/README.md +156 -0
- package/package.json +48 -0
- package/src/backends/base.ts +18 -0
- package/src/backends/deterministic.ts +105 -0
- package/src/backends/index.ts +26 -0
- package/src/backends/prompt.ts +169 -0
- package/src/backends/subprocess.ts +198 -0
- package/src/cli.ts +111 -0
- package/src/commands/agents.ts +16 -0
- package/src/commands/current.ts +95 -0
- package/src/commands/doctor.ts +12 -0
- package/src/commands/handoff.ts +64 -0
- package/src/commands/init.ts +101 -0
- package/src/commands/reshape.ts +20 -0
- package/src/commands/resume.ts +19 -0
- package/src/commands/spec.ts +108 -0
- package/src/commands/status.ts +98 -0
- package/src/commands/task.ts +190 -0
- package/src/commands/ui.ts +35 -0
- package/src/domain.ts +357 -0
- package/src/engine.ts +290 -0
- package/src/frontmatter.ts +83 -0
- package/src/index.ts +75 -0
- package/src/paths.ts +32 -0
- package/src/repository.ts +807 -0
- package/src/services/adoption.ts +169 -0
- package/src/services/agents.ts +191 -0
- package/src/services/dashboard.ts +776 -0
- package/src/services/details.ts +453 -0
- package/src/services/doctor.ts +452 -0
- package/src/services/layout.ts +420 -0
- package/src/services/spec-answer-evaluation.ts +103 -0
- package/src/services/spec-import.ts +217 -0
- package/src/services/spec-questions.ts +343 -0
- package/src/services/ui.ts +34 -0
- package/src/storage.ts +57 -0
- package/src/templates.ts +270 -0
- package/tsconfig.json +17 -0
- package/web/package.json +24 -0
- package/web/src/app.css +83 -0
- package/web/src/app.d.ts +6 -0
- package/web/src/app.html +11 -0
- package/web/src/lib/components/AnalysisFilters.svelte +293 -0
- package/web/src/lib/components/DocumentBody.svelte +100 -0
- package/web/src/lib/components/MultiSelectDropdown.svelte +280 -0
- package/web/src/lib/components/SelectDropdown.svelte +265 -0
- package/web/src/lib/server/project-root.ts +34 -0
- package/web/src/lib/task-board.ts +20 -0
- package/web/src/routes/+layout.server.ts +57 -0
- package/web/src/routes/+layout.svelte +421 -0
- package/web/src/routes/+layout.ts +1 -0
- package/web/src/routes/+page.svelte +530 -0
- package/web/src/routes/specs/+page.svelte +416 -0
- package/web/src/routes/specs/[specId]/+page.server.ts +81 -0
- package/web/src/routes/specs/[specId]/+page.svelte +675 -0
- package/web/src/routes/tasks/+page.svelte +341 -0
- package/web/src/routes/tasks/[taskId]/+page.server.ts +12 -0
- package/web/src/routes/tasks/[taskId]/+page.svelte +431 -0
- package/web/src/routes/timeline/+page.svelte +1093 -0
- package/web/svelte.config.js +10 -0
- package/web/tsconfig.json +9 -0
- package/web/vite.config.ts +11 -0
|
@@ -0,0 +1,675 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { ArrowLeft, CircleCheckBig, Gauge, NotebookText, Orbit, Pause, Sparkles, Target } from "@lucide/svelte";
|
|
3
|
+
import DocumentBody from "../../../lib/components/DocumentBody.svelte";
|
|
4
|
+
import type { SpecDetailResult } from "../../../../../src/services/details.ts";
|
|
5
|
+
|
|
6
|
+
let { data, form } = $props();
|
|
7
|
+
const detail = $derived(data.detail as SpecDetailResult);
|
|
8
|
+
const actionResult = $derived(
|
|
9
|
+
form as
|
|
10
|
+
| {
|
|
11
|
+
action?: string;
|
|
12
|
+
error?: string;
|
|
13
|
+
success?: boolean;
|
|
14
|
+
savedCount?: number;
|
|
15
|
+
answeredQuestions?: string[];
|
|
16
|
+
evaluation?: {
|
|
17
|
+
status: "needs-more-input" | "ready-for-follow-up";
|
|
18
|
+
summary: string;
|
|
19
|
+
nextActions: string[];
|
|
20
|
+
bundle: {
|
|
21
|
+
recommended_prompt: string;
|
|
22
|
+
expected_output_format: {
|
|
23
|
+
sections: string[];
|
|
24
|
+
propose_new_questions: boolean;
|
|
25
|
+
propose_follow_up_tasks: boolean;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
| undefined,
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
const statusIcons = {
|
|
34
|
+
proposed: Sparkles,
|
|
35
|
+
active: Orbit,
|
|
36
|
+
paused: Pause,
|
|
37
|
+
done: CircleCheckBig,
|
|
38
|
+
archived: NotebookText,
|
|
39
|
+
} as const;
|
|
40
|
+
</script>
|
|
41
|
+
|
|
42
|
+
<svelte:head>
|
|
43
|
+
<title>{detail.found ? `${detail.spec.id} · ${detail.spec.title}` : `Missing spec · ${detail.id}`}</title>
|
|
44
|
+
</svelte:head>
|
|
45
|
+
|
|
46
|
+
{#if !detail.found}
|
|
47
|
+
<section class="missing-shell">
|
|
48
|
+
<a class="back-link" href="/specs">
|
|
49
|
+
<ArrowLeft size={14} strokeWidth={1.9} />
|
|
50
|
+
Back to Specs
|
|
51
|
+
</a>
|
|
52
|
+
<p class="eyebrow">Deterministic fallback</p>
|
|
53
|
+
<h2>Spec {detail.id} was not found</h2>
|
|
54
|
+
<p class="intro">The requested spec does not exist in the current managed memory. Pick one of the known IDs below.</p>
|
|
55
|
+
<div class="chip-row">
|
|
56
|
+
{#each detail.availableIds as specId}
|
|
57
|
+
<a class="chip" href={`/specs/${specId}`}>{specId}</a>
|
|
58
|
+
{/each}
|
|
59
|
+
</div>
|
|
60
|
+
</section>
|
|
61
|
+
{:else}
|
|
62
|
+
{@const StatusIcon = statusIcons[detail.spec.status as keyof typeof statusIcons] ?? NotebookText}
|
|
63
|
+
<section class="detail-shell">
|
|
64
|
+
<div class="detail-header">
|
|
65
|
+
<div class="header-copy">
|
|
66
|
+
<a class="back-link" href="/specs">
|
|
67
|
+
<ArrowLeft size={14} strokeWidth={1.9} />
|
|
68
|
+
Back to Specs
|
|
69
|
+
</a>
|
|
70
|
+
<p class="eyebrow">Spec detail</p>
|
|
71
|
+
<div class="title-row">
|
|
72
|
+
<div>
|
|
73
|
+
<p class="item-id">{detail.spec.id}</p>
|
|
74
|
+
<h2>{detail.spec.title}</h2>
|
|
75
|
+
</div>
|
|
76
|
+
<span class="status-pill">
|
|
77
|
+
<StatusIcon size={14} strokeWidth={1.9} />
|
|
78
|
+
{detail.spec.status}
|
|
79
|
+
</span>
|
|
80
|
+
</div>
|
|
81
|
+
<p class="intro">{detail.spec.summary}</p>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<div class="meta-panel">
|
|
85
|
+
<div class="meta-grid">
|
|
86
|
+
<div>
|
|
87
|
+
<span>Linked tasks</span>
|
|
88
|
+
<strong>{detail.linkedTasks.length}</strong>
|
|
89
|
+
</div>
|
|
90
|
+
<div>
|
|
91
|
+
<span>Completion</span>
|
|
92
|
+
<strong>{Math.round(detail.progress.completionRatio * 100)}%</strong>
|
|
93
|
+
</div>
|
|
94
|
+
<div>
|
|
95
|
+
<span>Current focus</span>
|
|
96
|
+
<strong>{detail.currentState.isActiveSpec ? "Active" : "Inactive"}</strong>
|
|
97
|
+
</div>
|
|
98
|
+
</div>
|
|
99
|
+
<div class="chip-row">
|
|
100
|
+
{#each detail.spec.tags as tag}
|
|
101
|
+
<span class="chip">{tag}</span>
|
|
102
|
+
{/each}
|
|
103
|
+
{#if detail.spec.owner}
|
|
104
|
+
<span class="chip">owner {detail.spec.owner}</span>
|
|
105
|
+
{/if}
|
|
106
|
+
{#if detail.spec.updatedAt}
|
|
107
|
+
<span class="chip">updated {detail.spec.updatedAt.slice(0, 10)}</span>
|
|
108
|
+
{/if}
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
</div>
|
|
112
|
+
|
|
113
|
+
<section class="detail-grid">
|
|
114
|
+
<article class="panel">
|
|
115
|
+
<div class="panel-head">
|
|
116
|
+
<div class="panel-title">
|
|
117
|
+
<Gauge size={15} strokeWidth={1.9} />
|
|
118
|
+
<h3>Progress and linked tasks</h3>
|
|
119
|
+
</div>
|
|
120
|
+
<div class="progress-line">
|
|
121
|
+
<div class="progress-bar" style={`width:${Math.max(detail.progress.completionRatio * 100, 5)}%`}></div>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
|
|
125
|
+
<div class="progress-grid">
|
|
126
|
+
<span>done {detail.progress.done}</span>
|
|
127
|
+
<span>review {detail.progress.review}</span>
|
|
128
|
+
<span>in progress {detail.progress.inProgress}</span>
|
|
129
|
+
<span>blocked {detail.progress.blocked}</span>
|
|
130
|
+
<span>todo {detail.progress.todo}</span>
|
|
131
|
+
</div>
|
|
132
|
+
|
|
133
|
+
{#if detail.linkedTasks.length === 0}
|
|
134
|
+
<p class="empty">This spec has no linked tasks yet.</p>
|
|
135
|
+
{:else}
|
|
136
|
+
<div class="group-stack">
|
|
137
|
+
{#each detail.linkedTaskGroups as group}
|
|
138
|
+
<section class="task-group">
|
|
139
|
+
<div class="group-head">
|
|
140
|
+
<p class="linked-id">{group.title}</p>
|
|
141
|
+
<span class="mini-pill">{group.tasks.length}</span>
|
|
142
|
+
</div>
|
|
143
|
+
<div class="linked-list">
|
|
144
|
+
{#each group.tasks as task}
|
|
145
|
+
<a class="linked-card" href={`/tasks/${task.id}`}>
|
|
146
|
+
<div class="linked-head">
|
|
147
|
+
<div>
|
|
148
|
+
<p class="linked-id">{task.id}</p>
|
|
149
|
+
<h4>{task.title}</h4>
|
|
150
|
+
</div>
|
|
151
|
+
<span class="mini-pill">{task.statusLabel}</span>
|
|
152
|
+
</div>
|
|
153
|
+
<p>{task.summary}</p>
|
|
154
|
+
<div class="chip-row">
|
|
155
|
+
<span class="chip">p{task.priority}</span>
|
|
156
|
+
<span class="chip">{task.effort}u</span>
|
|
157
|
+
{#if task.archivedAt}
|
|
158
|
+
<span class="chip chip-warning">archived</span>
|
|
159
|
+
{/if}
|
|
160
|
+
</div>
|
|
161
|
+
</a>
|
|
162
|
+
{/each}
|
|
163
|
+
</div>
|
|
164
|
+
</section>
|
|
165
|
+
{/each}
|
|
166
|
+
</div>
|
|
167
|
+
{/if}
|
|
168
|
+
</article>
|
|
169
|
+
|
|
170
|
+
<article class="panel">
|
|
171
|
+
<div class="panel-head">
|
|
172
|
+
<div class="panel-title">
|
|
173
|
+
<Target size={15} strokeWidth={1.9} />
|
|
174
|
+
<h3>Current state relevance</h3>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
<div class="state-stack">
|
|
178
|
+
<div class="state-card">
|
|
179
|
+
<span>Focus</span>
|
|
180
|
+
<strong>{detail.currentState.focus}</strong>
|
|
181
|
+
</div>
|
|
182
|
+
<div class="chip-row">
|
|
183
|
+
<span class={`chip ${detail.currentState.isActiveSpec ? "chip-accent" : ""}`}>
|
|
184
|
+
{detail.currentState.isActiveSpec ? "Active in current state" : "Not active in current state"}
|
|
185
|
+
</span>
|
|
186
|
+
</div>
|
|
187
|
+
</div>
|
|
188
|
+
|
|
189
|
+
<div class="panel-head">
|
|
190
|
+
<div class="panel-title">
|
|
191
|
+
<NotebookText size={15} strokeWidth={1.9} />
|
|
192
|
+
<h3>Related handoffs</h3>
|
|
193
|
+
</div>
|
|
194
|
+
</div>
|
|
195
|
+
{#if detail.relatedHandoffs.length === 0}
|
|
196
|
+
<p class="empty">No related handoffs were found for this spec yet.</p>
|
|
197
|
+
{:else}
|
|
198
|
+
<div class="handoff-list">
|
|
199
|
+
{#each detail.relatedHandoffs as handoff}
|
|
200
|
+
<div class="state-card">
|
|
201
|
+
<p class="linked-id">{handoff.id}</p>
|
|
202
|
+
<strong>{handoff.title}</strong>
|
|
203
|
+
<p>{handoff.summary}</p>
|
|
204
|
+
<small>{handoff.createdAt}</small>
|
|
205
|
+
</div>
|
|
206
|
+
{/each}
|
|
207
|
+
</div>
|
|
208
|
+
{/if}
|
|
209
|
+
</article>
|
|
210
|
+
</section>
|
|
211
|
+
|
|
212
|
+
<section class="detail-grid detail-grid-secondary">
|
|
213
|
+
<article class="panel">
|
|
214
|
+
<div class="panel-head">
|
|
215
|
+
<div class="panel-title">
|
|
216
|
+
<Sparkles size={15} strokeWidth={1.9} />
|
|
217
|
+
<h3>Answer open questions</h3>
|
|
218
|
+
</div>
|
|
219
|
+
</div>
|
|
220
|
+
|
|
221
|
+
{#if detail.openQuestions.length === 0}
|
|
222
|
+
<p class="empty">This spec does not have open questions right now.</p>
|
|
223
|
+
{:else}
|
|
224
|
+
<form class="question-form" method="POST" action="?/answerOpenQuestions">
|
|
225
|
+
<div class="question-list">
|
|
226
|
+
{#each detail.openQuestions as question}
|
|
227
|
+
<label class="question-card">
|
|
228
|
+
<input type="hidden" name="question" value={question.question} />
|
|
229
|
+
<div class="question-head">
|
|
230
|
+
<p class="linked-id">{question.id}</p>
|
|
231
|
+
<span class="mini-pill">open</span>
|
|
232
|
+
</div>
|
|
233
|
+
<strong>{question.question}</strong>
|
|
234
|
+
<textarea
|
|
235
|
+
name="answer"
|
|
236
|
+
rows="4"
|
|
237
|
+
placeholder="Add the durable answer that should be written back into the spec."
|
|
238
|
+
></textarea>
|
|
239
|
+
</label>
|
|
240
|
+
{/each}
|
|
241
|
+
</div>
|
|
242
|
+
|
|
243
|
+
<div class="question-actions">
|
|
244
|
+
<p class="helper-copy">You can answer one or many questions here. Empty answers are ignored on save.</p>
|
|
245
|
+
<button class="primary-button" type="submit">Save answered questions</button>
|
|
246
|
+
</div>
|
|
247
|
+
</form>
|
|
248
|
+
{/if}
|
|
249
|
+
|
|
250
|
+
{#if actionResult?.action === "answerOpenQuestions" && actionResult.error}
|
|
251
|
+
<div class="feedback-card feedback-error">
|
|
252
|
+
<p class="linked-id">save error</p>
|
|
253
|
+
<strong>{actionResult.error}</strong>
|
|
254
|
+
</div>
|
|
255
|
+
{/if}
|
|
256
|
+
|
|
257
|
+
{#if actionResult?.action === "answerOpenQuestions" && actionResult.success}
|
|
258
|
+
<div class="feedback-stack">
|
|
259
|
+
<div class="feedback-card feedback-success">
|
|
260
|
+
<p class="linked-id">saved</p>
|
|
261
|
+
<strong>{actionResult.savedCount} answer{actionResult.savedCount === 1 ? "" : "s"} recorded</strong>
|
|
262
|
+
{#if actionResult.answeredQuestions && actionResult.answeredQuestions.length > 0}
|
|
263
|
+
<div class="chip-row">
|
|
264
|
+
{#each actionResult.answeredQuestions as question}
|
|
265
|
+
<span class="chip">{question}</span>
|
|
266
|
+
{/each}
|
|
267
|
+
</div>
|
|
268
|
+
{/if}
|
|
269
|
+
</div>
|
|
270
|
+
|
|
271
|
+
{#if actionResult.evaluation}
|
|
272
|
+
<div class="feedback-card">
|
|
273
|
+
<div class="panel-head">
|
|
274
|
+
<div class="panel-title">
|
|
275
|
+
<Target size={15} strokeWidth={1.9} />
|
|
276
|
+
<h3>Continuation guidance</h3>
|
|
277
|
+
</div>
|
|
278
|
+
<span class={`mini-pill ${actionResult.evaluation.status === "ready-for-follow-up" ? "mini-pill-success" : "mini-pill-warm"}`}>
|
|
279
|
+
{actionResult.evaluation.status === "ready-for-follow-up" ? "ready" : "needs more input"}
|
|
280
|
+
</span>
|
|
281
|
+
</div>
|
|
282
|
+
<p>{actionResult.evaluation.summary}</p>
|
|
283
|
+
<ul class="plain-list">
|
|
284
|
+
{#each actionResult.evaluation.nextActions as action}
|
|
285
|
+
<li>{action}</li>
|
|
286
|
+
{/each}
|
|
287
|
+
</ul>
|
|
288
|
+
<div class="prompt-block">
|
|
289
|
+
<p class="linked-id">agent evaluation prompt</p>
|
|
290
|
+
<pre>{actionResult.evaluation.bundle.recommended_prompt}</pre>
|
|
291
|
+
</div>
|
|
292
|
+
</div>
|
|
293
|
+
{/if}
|
|
294
|
+
</div>
|
|
295
|
+
{/if}
|
|
296
|
+
</article>
|
|
297
|
+
|
|
298
|
+
<article class="panel">
|
|
299
|
+
<div class="panel-head">
|
|
300
|
+
<div class="panel-title">
|
|
301
|
+
<NotebookText size={15} strokeWidth={1.9} />
|
|
302
|
+
<h3>Resolved decisions</h3>
|
|
303
|
+
</div>
|
|
304
|
+
<span class="mini-pill">{detail.resolvedDecisions.length}</span>
|
|
305
|
+
</div>
|
|
306
|
+
|
|
307
|
+
{#if detail.resolvedDecisions.length === 0}
|
|
308
|
+
<p class="empty">This spec does not have resolved decisions recorded yet.</p>
|
|
309
|
+
{:else}
|
|
310
|
+
<div class="decision-list">
|
|
311
|
+
{#each detail.resolvedDecisions as decision}
|
|
312
|
+
<div class="decision-card">
|
|
313
|
+
<p class="linked-id">{decision.id}</p>
|
|
314
|
+
{#if decision.question}
|
|
315
|
+
<strong>{decision.question}</strong>
|
|
316
|
+
{/if}
|
|
317
|
+
<p>{decision.answer}</p>
|
|
318
|
+
</div>
|
|
319
|
+
{/each}
|
|
320
|
+
</div>
|
|
321
|
+
{/if}
|
|
322
|
+
</article>
|
|
323
|
+
</section>
|
|
324
|
+
|
|
325
|
+
<article class="panel panel-body">
|
|
326
|
+
<div class="panel-head">
|
|
327
|
+
<div class="panel-title">
|
|
328
|
+
<NotebookText size={15} strokeWidth={1.9} />
|
|
329
|
+
<h3>Source narrative</h3>
|
|
330
|
+
</div>
|
|
331
|
+
</div>
|
|
332
|
+
<DocumentBody sections={detail.bodySections} emptyLabel="This spec does not have narrative sections yet." />
|
|
333
|
+
</article>
|
|
334
|
+
</section>
|
|
335
|
+
{/if}
|
|
336
|
+
|
|
337
|
+
<style>
|
|
338
|
+
.eyebrow,
|
|
339
|
+
.item-id,
|
|
340
|
+
.linked-id {
|
|
341
|
+
margin: 0 0 0.32rem;
|
|
342
|
+
text-transform: uppercase;
|
|
343
|
+
letter-spacing: 0.14em;
|
|
344
|
+
font-size: 0.7rem;
|
|
345
|
+
color: var(--muted);
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
h2,
|
|
349
|
+
h3,
|
|
350
|
+
h4,
|
|
351
|
+
p {
|
|
352
|
+
margin: 0;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
h2,
|
|
356
|
+
h3,
|
|
357
|
+
h4 {
|
|
358
|
+
font-family: var(--display-font);
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
h2 {
|
|
362
|
+
font-size: clamp(1.8rem, 2.4vw, 2.6rem);
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
h3 {
|
|
366
|
+
font-size: 1.1rem;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
h4 {
|
|
370
|
+
font-size: 1rem;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
.intro,
|
|
374
|
+
.panel p,
|
|
375
|
+
.panel small,
|
|
376
|
+
.empty {
|
|
377
|
+
color: var(--muted);
|
|
378
|
+
line-height: 1.5;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
.detail-shell,
|
|
382
|
+
.missing-shell,
|
|
383
|
+
.detail-grid,
|
|
384
|
+
.meta-panel,
|
|
385
|
+
.linked-list,
|
|
386
|
+
.group-stack,
|
|
387
|
+
.handoff-list,
|
|
388
|
+
.state-stack {
|
|
389
|
+
display: grid;
|
|
390
|
+
gap: 1rem;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
.detail-header {
|
|
394
|
+
display: grid;
|
|
395
|
+
grid-template-columns: minmax(0, 1.7fr) minmax(18rem, 0.9fr);
|
|
396
|
+
gap: 1rem;
|
|
397
|
+
margin-bottom: 1rem;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
.header-copy {
|
|
401
|
+
display: grid;
|
|
402
|
+
gap: 0.7rem;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
.back-link,
|
|
406
|
+
.linked-card {
|
|
407
|
+
text-decoration: none;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
.back-link {
|
|
411
|
+
display: inline-flex;
|
|
412
|
+
align-items: center;
|
|
413
|
+
gap: 0.42rem;
|
|
414
|
+
width: fit-content;
|
|
415
|
+
color: var(--muted-soft);
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
.title-row,
|
|
419
|
+
.linked-head,
|
|
420
|
+
.panel-head,
|
|
421
|
+
.panel-title,
|
|
422
|
+
.chip-row,
|
|
423
|
+
.meta-grid {
|
|
424
|
+
display: flex;
|
|
425
|
+
gap: 0.6rem;
|
|
426
|
+
flex-wrap: wrap;
|
|
427
|
+
align-items: center;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
.title-row,
|
|
431
|
+
.linked-head,
|
|
432
|
+
.meta-grid {
|
|
433
|
+
justify-content: space-between;
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
.meta-panel,
|
|
437
|
+
.panel {
|
|
438
|
+
padding: 1rem;
|
|
439
|
+
border-radius: var(--radius-xl);
|
|
440
|
+
border: 1px solid var(--line);
|
|
441
|
+
background: var(--panel);
|
|
442
|
+
box-shadow: var(--shadow);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
.meta-grid div {
|
|
446
|
+
display: grid;
|
|
447
|
+
gap: 0.18rem;
|
|
448
|
+
min-width: 8rem;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
.meta-grid span {
|
|
452
|
+
font-size: 0.76rem;
|
|
453
|
+
color: var(--muted);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
.meta-grid strong {
|
|
457
|
+
font-family: var(--display-font);
|
|
458
|
+
font-size: 1.3rem;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
.status-pill,
|
|
462
|
+
.chip,
|
|
463
|
+
.mini-pill {
|
|
464
|
+
display: inline-flex;
|
|
465
|
+
align-items: center;
|
|
466
|
+
gap: 0.32rem;
|
|
467
|
+
border-radius: 999px;
|
|
468
|
+
padding: 0.34rem 0.7rem;
|
|
469
|
+
border: 1px solid var(--line);
|
|
470
|
+
background: rgba(255, 255, 255, 0.88);
|
|
471
|
+
box-shadow: var(--shadow-soft);
|
|
472
|
+
font-size: 0.77rem;
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
.chip-accent {
|
|
476
|
+
background: rgba(15, 141, 96, 0.12);
|
|
477
|
+
border-color: rgba(15, 141, 96, 0.22);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
.chip-warning {
|
|
481
|
+
background: rgba(143, 84, 33, 0.12);
|
|
482
|
+
border-color: rgba(143, 84, 33, 0.22);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
.detail-grid {
|
|
486
|
+
grid-template-columns: minmax(0, 1.3fr) minmax(18rem, 0.85fr);
|
|
487
|
+
margin-bottom: 1rem;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
.detail-grid-secondary {
|
|
491
|
+
margin-bottom: 1rem;
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
.panel {
|
|
495
|
+
gap: 0.9rem;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
.progress-line {
|
|
499
|
+
width: 7rem;
|
|
500
|
+
height: 0.6rem;
|
|
501
|
+
border-radius: 999px;
|
|
502
|
+
overflow: hidden;
|
|
503
|
+
background: rgba(24, 22, 18, 0.08);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
.progress-bar {
|
|
507
|
+
height: 100%;
|
|
508
|
+
border-radius: 999px;
|
|
509
|
+
background: linear-gradient(90deg, rgba(15, 141, 96, 0.82), rgba(15, 141, 96, 0.34));
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
.progress-grid {
|
|
513
|
+
display: flex;
|
|
514
|
+
flex-wrap: wrap;
|
|
515
|
+
gap: 0.45rem;
|
|
516
|
+
color: var(--muted);
|
|
517
|
+
font-size: 0.8rem;
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
.linked-card,
|
|
521
|
+
.state-card {
|
|
522
|
+
display: grid;
|
|
523
|
+
gap: 0.65rem;
|
|
524
|
+
padding: 0.9rem;
|
|
525
|
+
border-radius: var(--radius-lg);
|
|
526
|
+
border: 1px solid var(--line);
|
|
527
|
+
background: rgba(255, 255, 255, 0.82);
|
|
528
|
+
box-shadow: var(--shadow-soft);
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
.question-form,
|
|
532
|
+
.question-list,
|
|
533
|
+
.feedback-stack,
|
|
534
|
+
.decision-list {
|
|
535
|
+
display: grid;
|
|
536
|
+
gap: 0.8rem;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
.question-card,
|
|
540
|
+
.decision-card,
|
|
541
|
+
.feedback-card {
|
|
542
|
+
display: grid;
|
|
543
|
+
gap: 0.55rem;
|
|
544
|
+
padding: 0.9rem;
|
|
545
|
+
border-radius: var(--radius-lg);
|
|
546
|
+
border: 1px solid rgba(119, 103, 77, 0.16);
|
|
547
|
+
background: rgba(255, 251, 244, 0.82);
|
|
548
|
+
box-shadow: var(--shadow-soft);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
.task-group {
|
|
552
|
+
display: grid;
|
|
553
|
+
gap: 0.72rem;
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
.group-head {
|
|
557
|
+
display: flex;
|
|
558
|
+
justify-content: space-between;
|
|
559
|
+
gap: 0.6rem;
|
|
560
|
+
align-items: center;
|
|
561
|
+
}
|
|
562
|
+
|
|
563
|
+
.linked-card:hover {
|
|
564
|
+
border-color: rgba(15, 141, 96, 0.18);
|
|
565
|
+
transform: translateY(-1px);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
.state-card span {
|
|
569
|
+
font-size: 0.76rem;
|
|
570
|
+
color: var(--muted);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
.state-card strong {
|
|
574
|
+
line-height: 1.35;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
.question-head,
|
|
578
|
+
.question-actions {
|
|
579
|
+
display: flex;
|
|
580
|
+
gap: 0.7rem;
|
|
581
|
+
align-items: center;
|
|
582
|
+
justify-content: space-between;
|
|
583
|
+
flex-wrap: wrap;
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
.helper-copy {
|
|
587
|
+
font-size: 0.85rem;
|
|
588
|
+
color: var(--muted);
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
textarea {
|
|
592
|
+
width: 100%;
|
|
593
|
+
min-height: 7rem;
|
|
594
|
+
resize: vertical;
|
|
595
|
+
box-sizing: border-box;
|
|
596
|
+
padding: 0.85rem 0.95rem;
|
|
597
|
+
border-radius: 1rem;
|
|
598
|
+
border: 1px solid rgba(119, 103, 77, 0.22);
|
|
599
|
+
background: rgba(255, 255, 255, 0.82);
|
|
600
|
+
color: inherit;
|
|
601
|
+
font: inherit;
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
textarea:focus {
|
|
605
|
+
outline: 2px solid rgba(15, 141, 96, 0.2);
|
|
606
|
+
border-color: rgba(15, 141, 96, 0.4);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
.primary-button {
|
|
610
|
+
border: 0;
|
|
611
|
+
border-radius: 999px;
|
|
612
|
+
padding: 0.72rem 1rem;
|
|
613
|
+
background: linear-gradient(135deg, rgba(18, 114, 88, 0.96), rgba(31, 153, 119, 0.96));
|
|
614
|
+
color: white;
|
|
615
|
+
font: inherit;
|
|
616
|
+
font-weight: 600;
|
|
617
|
+
cursor: pointer;
|
|
618
|
+
box-shadow: 0 14px 28px rgba(15, 141, 96, 0.18);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
.feedback-error {
|
|
622
|
+
border-color: rgba(161, 78, 56, 0.2);
|
|
623
|
+
background: rgba(255, 241, 236, 0.9);
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
.feedback-success {
|
|
627
|
+
border-color: rgba(15, 141, 96, 0.2);
|
|
628
|
+
background: rgba(241, 250, 244, 0.92);
|
|
629
|
+
}
|
|
630
|
+
|
|
631
|
+
.plain-list {
|
|
632
|
+
margin: 0;
|
|
633
|
+
padding-left: 1rem;
|
|
634
|
+
color: var(--muted);
|
|
635
|
+
display: grid;
|
|
636
|
+
gap: 0.4rem;
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
.prompt-block {
|
|
640
|
+
display: grid;
|
|
641
|
+
gap: 0.45rem;
|
|
642
|
+
padding: 0.8rem;
|
|
643
|
+
border-radius: 1rem;
|
|
644
|
+
background: rgba(255, 255, 255, 0.75);
|
|
645
|
+
border: 1px solid rgba(119, 103, 77, 0.14);
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
pre {
|
|
649
|
+
margin: 0;
|
|
650
|
+
white-space: pre-wrap;
|
|
651
|
+
word-break: break-word;
|
|
652
|
+
font-family: var(--mono-font);
|
|
653
|
+
font-size: 0.8rem;
|
|
654
|
+
line-height: 1.45;
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
.mini-pill-success {
|
|
658
|
+
color: rgba(15, 102, 69, 1);
|
|
659
|
+
border-color: rgba(15, 141, 96, 0.24);
|
|
660
|
+
background: rgba(15, 141, 96, 0.08);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
.mini-pill-warm {
|
|
664
|
+
color: rgba(143, 84, 33, 1);
|
|
665
|
+
border-color: rgba(143, 84, 33, 0.24);
|
|
666
|
+
background: rgba(143, 84, 33, 0.08);
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
@media (max-width: 980px) {
|
|
670
|
+
.detail-header,
|
|
671
|
+
.detail-grid {
|
|
672
|
+
grid-template-columns: 1fr;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
</style>
|