@swarmvaultai/engine 0.7.31 → 0.8.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/LICENSE +21 -0
- package/dist/hooks/claude.js +0 -0
- package/dist/hooks/copilot.js +0 -0
- package/dist/hooks/gemini.js +0 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +145 -12
- package/dist/viewer/assets/index-CRcCxyS8.css +1 -0
- package/dist/viewer/assets/index-QQ74kUX8.js +368 -0
- package/dist/viewer/index.html +2 -2
- package/dist/viewer/lib.d.ts +32 -1
- package/dist/viewer/lib.js +55 -0
- package/package.json +8 -9
- package/dist/viewer/assets/index-CwkhOTfH.js +0 -332
- package/dist/viewer/assets/index-DRAglPyY.css +0 -1
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 SwarmVault
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/dist/hooks/claude.js
CHANGED
|
File without changes
|
package/dist/hooks/copilot.js
CHANGED
|
File without changes
|
package/dist/hooks/gemini.js
CHANGED
|
File without changes
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -21344,18 +21344,52 @@ async function rejectApproval(rootDir, approvalId, targets = []) {
|
|
|
21344
21344
|
}
|
|
21345
21345
|
async function listCandidates(rootDir) {
|
|
21346
21346
|
const pages = await listPages(rootDir);
|
|
21347
|
-
|
|
21347
|
+
const candidates = pages.filter(
|
|
21348
21348
|
(page) => page.status === "candidate" && (page.kind === "concept" || page.kind === "entity")
|
|
21349
|
-
)
|
|
21350
|
-
|
|
21351
|
-
|
|
21352
|
-
|
|
21353
|
-
|
|
21354
|
-
|
|
21355
|
-
|
|
21356
|
-
|
|
21357
|
-
|
|
21358
|
-
|
|
21349
|
+
);
|
|
21350
|
+
let scoreLookup = null;
|
|
21351
|
+
try {
|
|
21352
|
+
const { config, paths } = await loadVaultConfig(rootDir);
|
|
21353
|
+
const promotionConfig = resolvePromotionConfig(config);
|
|
21354
|
+
const graph = await readJsonFile(paths.graphPath);
|
|
21355
|
+
if (graph) {
|
|
21356
|
+
const compileState = await readJsonFile(paths.compileStatePath) ?? emptyCompileState();
|
|
21357
|
+
const now = Date.now();
|
|
21358
|
+
const decisions = candidates.map(
|
|
21359
|
+
(page) => evaluateCandidateForPromotion(page, graph, compileState.candidateHistory, promotionConfig, now)
|
|
21360
|
+
);
|
|
21361
|
+
scoreLookup = new Map(
|
|
21362
|
+
decisions.map((decision) => [
|
|
21363
|
+
decision.pageId,
|
|
21364
|
+
{
|
|
21365
|
+
score: decision.score,
|
|
21366
|
+
breakdown: Object.fromEntries(decision.gates.map((gate) => [gate.gate, gate.value]))
|
|
21367
|
+
}
|
|
21368
|
+
])
|
|
21369
|
+
);
|
|
21370
|
+
}
|
|
21371
|
+
} catch {
|
|
21372
|
+
scoreLookup = null;
|
|
21373
|
+
}
|
|
21374
|
+
return candidates.map((page) => {
|
|
21375
|
+
const scored = scoreLookup?.get(page.id);
|
|
21376
|
+
return {
|
|
21377
|
+
pageId: page.id,
|
|
21378
|
+
title: page.title,
|
|
21379
|
+
kind: page.kind,
|
|
21380
|
+
path: page.path,
|
|
21381
|
+
activePath: candidateActivePath(page),
|
|
21382
|
+
sourceIds: page.sourceIds,
|
|
21383
|
+
createdAt: page.createdAt,
|
|
21384
|
+
updatedAt: page.updatedAt,
|
|
21385
|
+
...scored ? { score: scored.score, scoreBreakdown: scored.breakdown } : {}
|
|
21386
|
+
};
|
|
21387
|
+
}).sort((left, right) => {
|
|
21388
|
+
const ls = left.score ?? -1;
|
|
21389
|
+
const rs = right.score ?? -1;
|
|
21390
|
+
if (ls !== rs) return rs - ls;
|
|
21391
|
+
return left.title.localeCompare(right.title);
|
|
21392
|
+
});
|
|
21359
21393
|
}
|
|
21360
21394
|
function resolveCandidateTarget(pages, target) {
|
|
21361
21395
|
const candidate = pages.find((page) => page.status === "candidate" && (page.id === target || page.path === target));
|
|
@@ -23318,7 +23352,7 @@ async function getWatchStatus(rootDir) {
|
|
|
23318
23352
|
}
|
|
23319
23353
|
|
|
23320
23354
|
// src/mcp.ts
|
|
23321
|
-
var SERVER_VERSION = "0.
|
|
23355
|
+
var SERVER_VERSION = "0.8.0";
|
|
23322
23356
|
async function createMcpServer(rootDir) {
|
|
23323
23357
|
const server = new McpServer({
|
|
23324
23358
|
name: "swarmvault",
|
|
@@ -25889,6 +25923,8 @@ async function deleteManagedSource(rootDir, id) {
|
|
|
25889
25923
|
|
|
25890
25924
|
// src/viewer.ts
|
|
25891
25925
|
import { execFile as execFile2 } from "child_process";
|
|
25926
|
+
import { randomUUID } from "crypto";
|
|
25927
|
+
import { EventEmitter } from "events";
|
|
25892
25928
|
import fs23 from "fs/promises";
|
|
25893
25929
|
import http from "http";
|
|
25894
25930
|
import path28 from "path";
|
|
@@ -26015,6 +26051,33 @@ function buildViewerGraphArtifact(graph, options = {}) {
|
|
|
26015
26051
|
}
|
|
26016
26052
|
|
|
26017
26053
|
// src/viewer.ts
|
|
26054
|
+
var ViewerEventBus = class extends EventEmitter {
|
|
26055
|
+
publish(event) {
|
|
26056
|
+
const enriched = {
|
|
26057
|
+
id: event.id ?? randomUUID(),
|
|
26058
|
+
timestamp: event.timestamp ?? (/* @__PURE__ */ new Date()).toISOString(),
|
|
26059
|
+
type: event.type,
|
|
26060
|
+
level: event.level,
|
|
26061
|
+
message: event.message,
|
|
26062
|
+
meta: event.meta
|
|
26063
|
+
};
|
|
26064
|
+
this.emit("event", enriched);
|
|
26065
|
+
return enriched;
|
|
26066
|
+
}
|
|
26067
|
+
};
|
|
26068
|
+
var viewerEventBus = new ViewerEventBus();
|
|
26069
|
+
viewerEventBus.setMaxListeners(64);
|
|
26070
|
+
function toViewerLintFindings(findings) {
|
|
26071
|
+
const detectedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
26072
|
+
return findings.map((finding, index) => ({
|
|
26073
|
+
id: `${finding.code}:${index}`,
|
|
26074
|
+
severity: finding.severity,
|
|
26075
|
+
category: finding.code,
|
|
26076
|
+
message: finding.message,
|
|
26077
|
+
pagePath: finding.pagePath,
|
|
26078
|
+
detectedAt
|
|
26079
|
+
}));
|
|
26080
|
+
}
|
|
26018
26081
|
var execFileAsync2 = promisify2(execFile2);
|
|
26019
26082
|
async function isReadableFile(absolutePath) {
|
|
26020
26083
|
try {
|
|
@@ -26262,6 +26325,76 @@ async function startGraphServer(rootDir, port, options = {}) {
|
|
|
26262
26325
|
response.end(JSON.stringify(result));
|
|
26263
26326
|
return;
|
|
26264
26327
|
}
|
|
26328
|
+
if (url.pathname === "/api/lint") {
|
|
26329
|
+
try {
|
|
26330
|
+
const findings = await lintVault(rootDir);
|
|
26331
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
26332
|
+
response.end(JSON.stringify(toViewerLintFindings(findings)));
|
|
26333
|
+
} catch (error) {
|
|
26334
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
26335
|
+
response.end(JSON.stringify([]));
|
|
26336
|
+
console.warn(`[viewer] /api/lint failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
26337
|
+
}
|
|
26338
|
+
return;
|
|
26339
|
+
}
|
|
26340
|
+
if (url.pathname === "/api/workspace") {
|
|
26341
|
+
const reportPath = path28.join(paths.wikiDir, "graph", "report.json");
|
|
26342
|
+
const [graphRaw, reportRaw, approvalsRaw, candidatesRaw, watchStatusRaw, lintRaw] = await Promise.all([
|
|
26343
|
+
readJsonFile(paths.graphPath).catch(() => null),
|
|
26344
|
+
readJsonFile(reportPath).catch(() => null),
|
|
26345
|
+
listApprovals(rootDir).catch(() => []),
|
|
26346
|
+
listCandidates(rootDir).catch(() => []),
|
|
26347
|
+
getWatchStatus(rootDir).catch(() => ({ generatedAt: "", watchedRepoRoots: [], pendingSemanticRefresh: [] })),
|
|
26348
|
+
lintVault(rootDir).catch(() => [])
|
|
26349
|
+
]);
|
|
26350
|
+
const viewerGraph = graphRaw ? buildViewerGraphArtifact(graphRaw, { report: reportRaw, full: options.full ?? false }) : null;
|
|
26351
|
+
response.writeHead(200, { "content-type": "application/json" });
|
|
26352
|
+
response.end(
|
|
26353
|
+
JSON.stringify({
|
|
26354
|
+
graph: viewerGraph,
|
|
26355
|
+
graphReport: reportRaw ?? null,
|
|
26356
|
+
approvals: approvalsRaw,
|
|
26357
|
+
candidates: candidatesRaw,
|
|
26358
|
+
watchStatus: watchStatusRaw,
|
|
26359
|
+
lintFindings: toViewerLintFindings(lintRaw)
|
|
26360
|
+
})
|
|
26361
|
+
);
|
|
26362
|
+
return;
|
|
26363
|
+
}
|
|
26364
|
+
if (url.pathname === "/api/events") {
|
|
26365
|
+
response.writeHead(200, {
|
|
26366
|
+
"content-type": "text/event-stream",
|
|
26367
|
+
"cache-control": "no-cache, no-transform",
|
|
26368
|
+
connection: "keep-alive",
|
|
26369
|
+
"x-accel-buffering": "no"
|
|
26370
|
+
});
|
|
26371
|
+
const send = (event) => {
|
|
26372
|
+
response.write(`id: ${event.id}
|
|
26373
|
+
`);
|
|
26374
|
+
response.write(`data: ${JSON.stringify(event)}
|
|
26375
|
+
|
|
26376
|
+
`);
|
|
26377
|
+
};
|
|
26378
|
+
send({
|
|
26379
|
+
id: randomUUID(),
|
|
26380
|
+
type: "connected",
|
|
26381
|
+
level: "info",
|
|
26382
|
+
message: "Activity stream connected.",
|
|
26383
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
26384
|
+
});
|
|
26385
|
+
const listener = (event) => send(event);
|
|
26386
|
+
viewerEventBus.on("event", listener);
|
|
26387
|
+
const heartbeat = setInterval(() => {
|
|
26388
|
+
response.write(`: keepalive ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
26389
|
+
|
|
26390
|
+
`);
|
|
26391
|
+
}, 25e3);
|
|
26392
|
+
request.on("close", () => {
|
|
26393
|
+
clearInterval(heartbeat);
|
|
26394
|
+
viewerEventBus.off("event", listener);
|
|
26395
|
+
});
|
|
26396
|
+
return;
|
|
26397
|
+
}
|
|
26265
26398
|
if (url.pathname === "/api/clip" && request.method === "POST") {
|
|
26266
26399
|
const body = await readJsonBody(request);
|
|
26267
26400
|
const clipUrl = typeof body.url === "string" ? body.url.trim() : "";
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
:root,:root[data-theme=dark]{--c-bg-base: #020617;--c-bg-surface: #0c1222;--c-bg-elevated: rgba(15, 23, 42, .88);--c-bg-inset: rgba(2, 6, 23, .82);--c-bg-input: #070d1a;--c-bg-canvas-grid: rgba(148, 163, 184, .045);--c-bg-canvas-glow: rgba(14, 165, 233, .025);--c-border: rgba(148, 163, 184, .1);--c-border-subtle: rgba(148, 163, 184, .06);--c-border-focus: rgba(125, 211, 252, .45);--c-border-focus-strong: #38bdf8;--c-border-danger: rgba(248, 113, 113, .24);--c-border-warning: rgba(251, 191, 36, .2);--c-text-primary: #e2e8f0;--c-text-secondary: #94a3b8;--c-text-muted: #94a3b8;--c-text-accent: #7dd3fc;--c-text-error: #f87171;--c-text-warning: #fbbf24;--c-text-success: #34d399;--c-accent-bg: rgba(14, 165, 233, .08);--c-accent-bg-strong: rgba(14, 165, 233, .18);--c-danger-bg: rgba(127, 29, 29, .18);--c-warning-bg: rgba(251, 191, 36, .1);--c-success-bg: rgba(34, 197, 94, .1);--c-mark-bg: rgba(125, 211, 252, .18);--c-overlay: rgba(2, 6, 23, .78);--c-shadow-elevated: 0 20px 50px -10px rgba(0, 0, 0, .6);color-scheme:dark}:root[data-theme=light]{--c-bg-base: #f8fafc;--c-bg-surface: #ffffff;--c-bg-elevated: #ffffff;--c-bg-inset: #f1f5f9;--c-bg-input: #ffffff;--c-bg-canvas-grid: rgba(15, 23, 42, .07);--c-bg-canvas-glow: rgba(14, 165, 233, .06);--c-border: rgba(15, 23, 42, .1);--c-border-subtle: rgba(15, 23, 42, .06);--c-border-focus: rgba(14, 116, 144, .55);--c-border-focus-strong: #0284c7;--c-border-danger: rgba(220, 38, 38, .3);--c-border-warning: rgba(202, 138, 4, .3);--c-text-primary: #0f172a;--c-text-secondary: #475569;--c-text-muted: #64748b;--c-text-accent: #0369a1;--c-text-error: #b91c1c;--c-text-warning: #a16207;--c-text-success: #047857;--c-accent-bg: rgba(14, 165, 233, .1);--c-accent-bg-strong: rgba(14, 165, 233, .2);--c-danger-bg: rgba(254, 226, 226, .65);--c-warning-bg: rgba(254, 243, 199, .6);--c-success-bg: rgba(220, 252, 231, .6);--c-mark-bg: rgba(14, 165, 233, .22);--c-overlay: rgba(15, 23, 42, .55);--c-shadow-elevated: 0 10px 30px -10px rgba(15, 23, 42, .3);color-scheme:light}@media(prefers-color-scheme:light){:root:not([data-theme]){--c-bg-base: #f8fafc;--c-bg-surface: #ffffff;--c-bg-elevated: #ffffff;--c-bg-inset: #f1f5f9;--c-bg-input: #ffffff;--c-bg-canvas-grid: rgba(15, 23, 42, .07);--c-bg-canvas-glow: rgba(14, 165, 233, .06);--c-border: rgba(15, 23, 42, .1);--c-border-subtle: rgba(15, 23, 42, .06);--c-border-focus: rgba(14, 116, 144, .55);--c-border-focus-strong: #0284c7;--c-border-danger: rgba(220, 38, 38, .3);--c-border-warning: rgba(202, 138, 4, .3);--c-text-primary: #0f172a;--c-text-secondary: #475569;--c-text-muted: #64748b;--c-text-accent: #0369a1;--c-text-error: #b91c1c;--c-text-warning: #a16207;--c-text-success: #047857;--c-accent-bg: rgba(14, 165, 233, .1);--c-accent-bg-strong: rgba(14, 165, 233, .2);--c-danger-bg: rgba(254, 226, 226, .65);--c-warning-bg: rgba(254, 243, 199, .6);--c-success-bg: rgba(220, 252, 231, .6);--c-mark-bg: rgba(14, 165, 233, .22);--c-overlay: rgba(15, 23, 42, .55);--c-shadow-elevated: 0 10px 30px -10px rgba(15, 23, 42, .3);color-scheme:light}}:root{--font-sans: "Inter", "Avenir Next", "Segoe UI", system-ui, sans-serif;--font-mono: "IBM Plex Mono", "SF Mono", "Fira Code", monospace;--text-2xs: .6875rem;--text-xs: .75rem;--text-sm: .8125rem;--text-base: .9375rem;--text-lg: 1.0625rem;--text-xl: 1.25rem;--sp-1: 4px;--sp-2: 8px;--sp-3: 12px;--sp-4: 16px;--sp-5: 20px;--sp-6: 24px;--radius-sm: 4px;--radius-md: 6px;--radius-lg: 10px;--sidebar-width: 240px;--rail-width: 400px;--bar-height: 42px;--z-overlay: 30;--z-drawer: 40;--z-modal: 50;font-family:var(--font-sans);background:var(--c-bg-base);color:var(--c-text-primary)}*,*:before,*:after{box-sizing:border-box}body{margin:0;height:100vh;overflow:hidden;font-size:var(--text-sm);line-height:1.5;-webkit-font-smoothing:antialiased;background:var(--c-bg-base);color:var(--c-text-primary)}#root{height:100vh;overflow:hidden}:focus-visible{outline:2px solid var(--c-border-focus-strong);outline-offset:2px;border-radius:var(--radius-sm)}button:focus-visible,a:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible,[role=button]:focus-visible,[role=tab]:focus-visible,[role=option]:focus-visible{outline:2px solid var(--c-border-focus-strong);outline-offset:2px}.app-shell{display:grid;grid-template-columns:var(--sidebar-width) minmax(0,1fr) var(--rail-width);grid-template-rows:var(--bar-height) minmax(0,1fr);grid-template-areas:"bar bar bar" "sidebar center rail";height:100vh;overflow:hidden}.app-bar{grid-area:bar;display:flex;align-items:center;gap:var(--sp-3);padding:0 var(--sp-5);background:var(--c-bg-surface);border-bottom:1px solid var(--c-border);z-index:10}.app-bar-title{font-size:var(--text-sm);font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--c-text-accent)}.app-bar-subtitle{font-size:var(--text-xs);color:var(--c-text-muted);letter-spacing:.04em}.app-bar-spacer{flex:1}.app-bar-actions{display:flex;gap:var(--sp-2);align-items:center}.app-bar-icon-btn{all:unset;display:inline-flex;align-items:center;justify-content:center;height:28px;min-width:28px;padding:0 var(--sp-2);border-radius:var(--radius-sm);border:1px solid var(--c-border);color:var(--c-text-secondary);cursor:pointer;font-size:var(--text-xs);background:transparent;transition:border-color .12s,color .12s,background .12s}.app-bar-icon-btn:hover{border-color:var(--c-border-focus);color:var(--c-text-primary);background:var(--c-accent-bg)}.drawer-trigger{display:none}.sidebar{grid-area:sidebar;overflow-y:auto;padding:var(--sp-3);background:var(--c-bg-surface);border-right:1px solid var(--c-border);display:flex;flex-direction:column;gap:var(--sp-3);scrollbar-width:thin;scrollbar-color:var(--c-text-muted) transparent}.sidebar-section{display:flex;flex-direction:column;gap:var(--sp-2)}.sidebar-heading{font-size:var(--text-2xs);font-weight:600;text-transform:uppercase;letter-spacing:.1em;color:var(--c-text-muted);padding:var(--sp-1) 0;border-bottom:1px solid var(--c-border-subtle);margin-bottom:var(--sp-1)}.sidebar-section-toggle{all:unset;display:flex;align-items:center;gap:var(--sp-2);width:100%;cursor:pointer;font-size:var(--text-2xs);font-weight:600;text-transform:uppercase;letter-spacing:.1em;color:var(--c-text-muted);padding:var(--sp-1) 0;border-bottom:1px solid var(--c-border-subtle);margin-bottom:var(--sp-1);transition:color .15s}.sidebar-section-toggle:hover{color:var(--c-text-secondary)}.sidebar-section-toggle:before{content:"▸";font-size:.6em;transition:transform .2s ease;display:inline-block}.sidebar-section-toggle.is-expanded:before{transform:rotate(90deg)}.sidebar-section-toggle .filter-badge{margin-left:auto;font-family:var(--font-mono);font-size:var(--text-2xs);color:var(--c-text-accent);font-weight:500}.sidebar-section-body{display:grid;gap:var(--sp-2);max-height:0;overflow:hidden;transition:max-height .25s ease,opacity .2s ease;opacity:0}.sidebar-section-body.is-expanded{max-height:800px;opacity:1}.filter-group{display:flex;flex-direction:column;gap:3px}.filter-label{font-size:var(--text-2xs);text-transform:uppercase;letter-spacing:.06em;color:var(--c-text-muted)}.center-area{grid-area:center;display:grid;grid-template-rows:auto minmax(0,1fr) auto;overflow:hidden}.stats-strip{display:flex;gap:var(--sp-2);padding:var(--sp-2) var(--sp-3);border-bottom:1px solid var(--c-border-subtle);background:var(--c-bg-surface);align-items:center}.stats-group{display:flex;gap:var(--sp-3);align-items:baseline}.stats-divider{width:1px;height:14px;background:var(--c-border);align-self:center;flex-shrink:0}.stat{display:flex;align-items:baseline;gap:var(--sp-1)}.stat-label{font-size:var(--text-2xs);color:var(--c-text-muted);text-transform:uppercase;letter-spacing:.06em}.stat-value{font-family:var(--font-mono);font-size:var(--text-sm);color:var(--c-text-primary);font-weight:500}.overview-banner{display:flex;align-items:center;gap:var(--sp-2);padding:var(--sp-2) var(--sp-3);border-bottom:1px solid var(--c-border-subtle);background:var(--c-warning-bg);color:var(--c-text-secondary);font-size:var(--text-xs);letter-spacing:.02em}.overview-banner code{font-family:var(--font-mono);font-size:inherit;color:var(--c-text-warning)}.canvas-wrap{position:relative;min-height:0;display:flex;flex-direction:column}.canvas{flex:1;min-height:0;background:radial-gradient(ellipse at 50% 40%,var(--c-bg-canvas-glow),transparent 65%),radial-gradient(circle at 1px 1px,var(--c-bg-canvas-grid) 1px,transparent 0),var(--c-bg-base);background-size:100% 100%,28px 28px,100% 100%}.canvas-loading{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--sp-3)}.loading-text{font-size:var(--text-base);color:var(--c-text-muted);letter-spacing:.06em;font-weight:400;animation:pulse-fade 2s ease-in-out infinite}@keyframes pulse-fade{0%,to{opacity:.35}50%{opacity:.85}}.canvas-empty{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--sp-2);color:var(--c-text-muted)}.canvas-empty-icon{font-size:2rem;opacity:.4}.canvas-toolbar{position:absolute;top:var(--sp-2);left:var(--sp-2);display:flex;flex-wrap:wrap;gap:var(--sp-2);z-index:5;background:var(--c-bg-elevated);border:1px solid var(--c-border);border-radius:var(--radius-md);padding:var(--sp-1) var(--sp-2);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);align-items:center;font-size:var(--text-xs)}.canvas-toolbar-group{display:flex;align-items:center;gap:var(--sp-1);padding-right:var(--sp-2);border-right:1px solid var(--c-border-subtle)}.canvas-toolbar-group:last-child{border-right:none;padding-right:0}.canvas-toolbar select.input{height:24px;padding:0 var(--sp-2);font-size:var(--text-xs);width:auto;min-width:110px}.canvas-toolbar .btn{height:24px;padding:0 var(--sp-2);font-size:var(--text-xs)}.canvas-legend{position:absolute;bottom:var(--sp-2);left:var(--sp-2);display:flex;flex-direction:column;gap:var(--sp-1);background:var(--c-bg-elevated);border:1px solid var(--c-border);border-radius:var(--radius-md);padding:var(--sp-2);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);z-index:5;max-width:220px}.canvas-legend-heading{font-size:var(--text-2xs);text-transform:uppercase;letter-spacing:.06em;color:var(--c-text-muted);margin-bottom:var(--sp-1)}.canvas-legend-group{display:flex;flex-direction:column;gap:2px}.legend-row{display:flex;align-items:center;gap:var(--sp-2);font-size:var(--text-xs);color:var(--c-text-secondary)}.legend-swatch{width:12px;height:12px;border-radius:2px;display:inline-block}.legend-swatch.shape-diamond{transform:rotate(45deg);width:9px;height:9px}.legend-swatch.shape-hex{clip-path:polygon(25% 0%,75% 0%,100% 50%,75% 100%,25% 100%,0% 50%)}.legend-swatch.shape-round{border-radius:50%}.legend-swatch.line-dashed{border-bottom:1.5px dashed currentColor;height:0;width:18px}.legend-swatch.line-solid{border-bottom:1.5px solid currentColor;height:0;width:18px}.legend-toggle{position:absolute;bottom:var(--sp-2);right:var(--sp-2);z-index:5}.canvas-minimap{position:absolute;top:var(--sp-2);right:var(--sp-2);width:180px;height:120px;background:var(--c-bg-inset);border:1px solid var(--c-border);border-radius:var(--radius-md);overflow:hidden;z-index:5;cursor:crosshair}.canvas-minimap-viewport{position:absolute;border:1.5px solid var(--c-border-focus-strong);background:#38bdf814;pointer-events:none}.report-tabs{border-top:1px solid var(--c-border);background:var(--c-bg-surface);max-height:360px;overflow:hidden}.report-tabs .tabs{border:none;border-radius:0;background:transparent}.report-tabs .tab-panel{max-height:310px;overflow-y:auto}.report-tabs-empty{padding:var(--sp-3);text-align:center;color:var(--c-text-muted);font-size:var(--text-sm)}.report-stats{display:flex;gap:var(--sp-4);font-size:var(--text-sm);color:var(--c-text-secondary);flex-wrap:wrap}.report-stats strong{font-family:var(--font-mono);color:var(--c-text-primary);margin-left:var(--sp-1)}.surprise-row{display:flex;align-items:center;gap:var(--sp-2);flex-wrap:wrap}.detail-rail{grid-area:rail;overflow-y:auto;padding:var(--sp-3);background:var(--c-bg-surface);border-left:1px solid var(--c-border);display:flex;flex-direction:column;gap:var(--sp-3);scrollbar-width:thin;scrollbar-color:var(--c-text-muted) transparent}.tabs{border-radius:var(--radius-lg);border:1px solid var(--c-border);background:var(--c-bg-elevated);overflow:hidden}.tab-bar{display:flex;border-bottom:1px solid var(--c-border);padding:0 var(--sp-1);gap:0;flex-wrap:wrap}.tab-btn{all:unset;padding:var(--sp-2) var(--sp-3);font-size:var(--text-xs);text-transform:uppercase;letter-spacing:.06em;color:var(--c-text-muted);cursor:pointer;border-bottom:2px solid transparent;transition:color .12s,border-color .12s;white-space:nowrap}.tab-btn.is-active{color:var(--c-text-primary);border-bottom-color:var(--c-text-accent)}.tab-btn:hover:not(.is-active){color:var(--c-text-secondary)}.tab-btn:focus-visible{outline:2px solid var(--c-border-focus-strong);outline-offset:-2px;border-radius:var(--radius-sm)}.tab-count{margin-left:var(--sp-1);font-family:var(--font-mono);font-size:var(--text-2xs);color:var(--c-text-accent)}.tab-panel{padding:var(--sp-3);max-height:320px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--c-text-muted) transparent}.panel{background:var(--c-bg-elevated);border:1px solid var(--c-border);border-radius:var(--radius-lg);padding:var(--sp-3);transition:border-color .15s}.panel-heading{margin:0 0 var(--sp-2);font-size:var(--text-base);font-weight:700;text-transform:none;letter-spacing:.01em;color:var(--c-text-primary);display:flex;align-items:baseline;gap:var(--sp-2)}.panel-heading .panel-meta{font-size:var(--text-xs);font-weight:400;color:var(--c-text-muted);text-transform:none;letter-spacing:0}.page-panel{min-height:120px}.card-list{display:grid;gap:var(--sp-2)}.card{background:var(--c-bg-inset);border:1px solid var(--c-border);border-radius:var(--radius-md);padding:var(--sp-2) var(--sp-3);display:flex;flex-direction:column;gap:4px}.card.is-selected{border-color:var(--c-border-focus-strong);background:var(--c-accent-bg-strong)}.card>.input+.input,.card>.input+.btn,.card>.btn+.input{margin-top:2px}.card-warning{border-color:var(--c-border-warning)}.card-title{font-size:var(--text-sm);font-weight:600;color:var(--c-text-primary);margin:0;line-height:1.3}.card-row{display:flex;align-items:center;gap:var(--sp-2)}.result-card{all:unset;display:flex;flex-direction:column;gap:3px;background:var(--c-bg-inset);border:1px solid var(--c-border);border-radius:var(--radius-md);padding:var(--sp-2) var(--sp-3);cursor:pointer;transition:border-color .12s,background .12s}.result-card:hover{border-color:var(--c-border-focus);background:var(--c-accent-bg)}.result-card.is-active{border-color:var(--c-border-focus);background:var(--c-accent-bg-strong)}.input{background:var(--c-bg-input);color:var(--c-text-primary);border:1px solid var(--c-border);border-radius:var(--radius-sm);padding:6px var(--sp-2);font-family:var(--font-sans);font-size:var(--text-sm);width:100%;outline:none;transition:border-color .12s}.input:focus{border-color:var(--c-border-focus)}.input::placeholder{color:var(--c-text-muted)}select.input{appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M0 0l5 6 5-6z' fill='currentColor'/%3E%3C/svg%3E");background-repeat:no-repeat;background-position:right 8px center;padding-right:24px;text-overflow:ellipsis;color:var(--c-text-primary)}.btn{all:unset;display:inline-flex;align-items:center;gap:var(--sp-1);height:28px;padding:0 var(--sp-3);font-size:var(--text-xs);font-family:var(--font-sans);border-radius:var(--radius-sm);cursor:pointer;border:1px solid var(--c-border);background:transparent;color:var(--c-text-primary);transition:border-color .12s,background .12s;white-space:nowrap}.btn:hover{border-color:var(--c-border-focus);background:var(--c-accent-bg)}.btn:focus-visible{outline:2px solid var(--c-border-focus-strong);outline-offset:1px}.btn:disabled{opacity:.35;cursor:default;pointer-events:none}.btn-primary{border-color:var(--c-border-focus-strong);background:var(--c-accent-bg-strong);color:var(--c-text-primary)}.btn-danger{border-color:var(--c-border-danger);background:var(--c-danger-bg);color:var(--c-text-error)}.btn-ghost{border:none;background:transparent;color:var(--c-text-accent);padding:0 var(--sp-1);height:auto;font-size:var(--text-xs)}.btn-ghost:hover{text-decoration:underline;border:none;background:transparent}.btn-icon{width:24px;padding:0;justify-content:center}.action-row{display:flex;flex-wrap:wrap;gap:var(--sp-2);margin-top:var(--sp-1);align-items:center}.action-row-end{margin-left:auto}.chip-row{display:flex;flex-wrap:wrap;gap:var(--sp-1);margin-top:var(--sp-1)}.chip{display:inline-flex;align-items:center;gap:var(--sp-1);padding:2px var(--sp-2);font-size:var(--text-2xs);font-family:var(--font-mono);background:var(--c-bg-inset);border:1px solid var(--c-border);color:var(--c-text-secondary);border-radius:999px;letter-spacing:0}.chip-tag{background:var(--c-accent-bg);border-color:var(--c-border-focus);color:var(--c-text-accent);cursor:pointer}.chip-tag.is-active{background:var(--c-accent-bg-strong)}.chip-static{cursor:default}.chip-danger{border-color:var(--c-border-danger);background:var(--c-danger-bg);color:var(--c-text-error)}.chip-warning{border-color:var(--c-border-warning);background:var(--c-warning-bg);color:var(--c-text-warning)}.chip-success{border-color:#22c55e66;background:var(--c-success-bg);color:var(--c-text-success)}.label{font-size:var(--text-2xs);text-transform:uppercase;letter-spacing:.06em;color:var(--c-text-muted);font-weight:500}.meta-grid{display:grid;grid-template-columns:auto 1fr;gap:3px var(--sp-3);font-size:var(--text-xs);align-items:baseline}.meta-label{color:var(--c-text-muted);text-transform:uppercase;letter-spacing:.04em;font-size:var(--text-2xs);white-space:nowrap}.meta-value{color:var(--c-text-primary);overflow-wrap:break-word;word-break:break-word}.meta-value-truncate{max-width:260px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:inline-block}.meta-value.mono,.text-mono{font-family:var(--font-mono)}.linked-section{margin-top:var(--sp-2);padding-top:var(--sp-2);border-top:1px solid var(--c-border-subtle)}.asset-preview{display:grid;gap:var(--sp-2);margin-top:var(--sp-2)}.asset-card{margin:0;display:grid;gap:var(--sp-1)}.asset-card img{width:100%;max-height:200px;object-fit:contain;border-radius:var(--radius-md);border:1px solid var(--c-border);background:var(--c-bg-inset)}.asset-card figcaption{color:var(--c-text-muted);font-size:var(--text-2xs)}.markdown-content{margin-top:var(--sp-3);padding:var(--sp-3);border-radius:var(--radius-md);background:var(--c-bg-inset);border:1px solid var(--c-border);color:var(--c-text-primary);font-size:var(--text-sm);line-height:1.6;overflow-wrap:break-word;word-break:break-word}.markdown-content>*:first-child{margin-top:0}.markdown-content>*:last-child{margin-bottom:0}.markdown-content h1,.markdown-content h2,.markdown-content h3,.markdown-content h4{margin:1.2em 0 .5em;line-height:1.25;color:var(--c-text-primary);font-weight:600}.markdown-content h1{font-size:var(--text-xl)}.markdown-content h2{font-size:var(--text-lg)}.markdown-content h3{font-size:var(--text-base)}.markdown-content h4{font-size:var(--text-sm)}.markdown-content p{margin:.5em 0}.markdown-content a{color:var(--c-text-accent);text-decoration:underline;text-decoration-color:#7dd3fc59}.markdown-content a:hover{text-decoration-color:currentColor}.markdown-content ul,.markdown-content ol{margin:.5em 0;padding-left:1.4em}.markdown-content li{margin:.15em 0}.markdown-content blockquote{margin:.7em 0;padding:.4em var(--sp-3);border-left:3px solid var(--c-border-focus);color:var(--c-text-secondary);background:var(--c-bg-elevated);border-radius:0 var(--radius-sm) var(--radius-sm) 0}.markdown-content code{font-family:var(--font-mono);font-size:.9em;background:var(--c-bg-elevated);border:1px solid var(--c-border-subtle);border-radius:var(--radius-sm);padding:0 4px}.markdown-content pre{background:var(--c-bg-base);border:1px solid var(--c-border);border-radius:var(--radius-md);padding:var(--sp-3);overflow-x:auto;margin:.7em 0;font-size:.85em;line-height:1.5}.markdown-content pre code{background:transparent;border:none;padding:0;font-size:inherit}.markdown-content table{width:100%;border-collapse:collapse;margin:.7em 0;font-size:var(--text-xs)}.markdown-content th,.markdown-content td{border:1px solid var(--c-border);padding:4px 8px;text-align:left}.markdown-content th{background:var(--c-bg-elevated);font-weight:600}.markdown-content hr{border:none;border-top:1px solid var(--c-border);margin:1em 0}.markdown-content img{max-width:100%;height:auto;border-radius:var(--radius-md)}.markdown-content .hljs{color:var(--c-text-primary);background:transparent}.markdown-content .hljs-comment,.markdown-content .hljs-quote{color:var(--c-text-muted);font-style:italic}.markdown-content .hljs-keyword,.markdown-content .hljs-selector-tag,.markdown-content .hljs-built_in,.markdown-content .hljs-tag{color:#c084fc}.markdown-content .hljs-string,.markdown-content .hljs-attr,.markdown-content .hljs-doctag{color:var(--c-text-success)}.markdown-content .hljs-title,.markdown-content .hljs-name,.markdown-content .hljs-section{color:var(--c-text-accent);font-weight:600}.markdown-content .hljs-number,.markdown-content .hljs-literal{color:var(--c-text-warning)}.markdown-content .hljs-variable,.markdown-content .hljs-template-variable,.markdown-content .hljs-symbol{color:#fbbf24}.markdown-content .hljs-type,.markdown-content .hljs-class{color:#38bdf8}.markdown-content .hljs-meta{color:var(--c-text-muted)}.markdown-content .hljs-deletion{background:var(--c-danger-bg);color:var(--c-text-error)}.markdown-content .hljs-addition{background:var(--c-success-bg);color:var(--c-text-success)}.content-pre{margin:var(--sp-2) 0 0;white-space:pre-wrap;overflow:auto;max-height:340px;padding:var(--sp-3);border-radius:var(--radius-md);background:var(--c-bg-inset);border:1px solid var(--c-border);font-family:var(--font-mono);font-size:var(--text-xs);color:var(--c-text-secondary);line-height:1.55;scrollbar-width:thin;scrollbar-color:var(--c-text-muted) transparent}.content-truncation-note{margin-top:var(--sp-1);font-size:var(--text-2xs);color:var(--c-text-muted);font-style:italic}.empty-state{display:flex;flex-direction:column;align-items:center;justify-content:center;padding:var(--sp-5) var(--sp-3);color:var(--c-text-muted);text-align:center;min-height:80px;gap:var(--sp-2)}.empty-state-icon{font-size:1.5rem;opacity:.4;line-height:1}mark{background:var(--c-mark-bg);color:var(--c-text-accent);border-radius:2px;padding:0 2px}.search-controls{display:flex;align-items:center;gap:var(--sp-2);margin-bottom:var(--sp-2);flex-wrap:wrap}.search-syntax-help{background:var(--c-bg-inset);border:1px solid var(--c-border);border-radius:var(--radius-md);padding:var(--sp-2);font-size:var(--text-xs);color:var(--c-text-secondary)}.search-syntax-help code{background:var(--c-bg-elevated);padding:1px 4px;border-radius:var(--radius-sm)}.recent-searches{display:flex;flex-wrap:wrap;gap:var(--sp-1);margin-top:var(--sp-2)}.text-sm{font-size:var(--text-sm)}.text-xs{font-size:var(--text-xs)}.text-muted{color:var(--c-text-muted)}.text-secondary{color:var(--c-text-secondary)}.text-accent{color:var(--c-text-accent)}.text-success{color:var(--c-text-success)}.text-warning{color:var(--c-text-warning);font-size:var(--text-xs)}.text-error{color:var(--c-text-error);font-size:var(--text-xs)}code{font-family:var(--font-mono);font-size:var(--text-xs)}p,h3,h4{margin:0}a{color:var(--c-text-accent);text-decoration:none}a:hover{text-decoration:underline}.bulk-toolbar{display:flex;align-items:center;gap:var(--sp-2);padding:var(--sp-2);margin-bottom:var(--sp-2);background:var(--c-accent-bg);border:1px solid var(--c-border-focus);border-radius:var(--radius-md);font-size:var(--text-xs)}.bulk-toolbar-count{color:var(--c-text-accent);font-family:var(--font-mono);font-weight:600}.list-filter-bar{display:flex;flex-wrap:wrap;gap:var(--sp-2);margin-bottom:var(--sp-2);align-items:center}.list-filter-bar .input{flex:1;min-width:120px}.checkbox-cell{display:inline-flex;align-items:center;gap:var(--sp-1);cursor:pointer}.checkbox-cell input{cursor:pointer;accent-color:var(--c-border-focus-strong)}.activity-feed{display:flex;flex-direction:column;gap:var(--sp-2)}.activity-item{display:grid;grid-template-columns:auto 1fr;gap:var(--sp-2);align-items:start;font-size:var(--text-xs);padding:var(--sp-1) var(--sp-2);border-left:2px solid var(--c-border)}.activity-item.activity-success{border-left-color:var(--c-text-success)}.activity-item.activity-warning{border-left-color:var(--c-text-warning)}.activity-item.activity-error{border-left-color:var(--c-text-error)}.activity-item.activity-info{border-left-color:var(--c-text-accent)}.activity-time{font-family:var(--font-mono);color:var(--c-text-muted);font-size:var(--text-2xs);white-space:nowrap}.activity-message{color:var(--c-text-primary);overflow-wrap:break-word}.activity-meta{color:var(--c-text-muted);font-size:var(--text-2xs);margin-top:2px}.activity-status-pill{display:inline-flex;align-items:center;gap:4px;padding:2px 8px;border-radius:999px;font-size:var(--text-2xs);border:1px solid var(--c-border)}.activity-status-pill.is-live{border-color:#22c55e66;color:var(--c-text-success)}.activity-status-pill.is-live:before{content:"";width:6px;height:6px;border-radius:50%;background:var(--c-text-success);animation:pulse-fade 1.6s ease-in-out infinite}.activity-status-pill.is-offline{color:var(--c-text-muted)}.lint-finding{padding:var(--sp-2);border-left:3px solid var(--c-border);background:var(--c-bg-inset);border-radius:0 var(--radius-md) var(--radius-md) 0;font-size:var(--text-xs);display:flex;flex-direction:column;gap:4px}.lint-finding.is-error{border-left-color:var(--c-text-error)}.lint-finding.is-warning{border-left-color:var(--c-text-warning)}.lint-finding.is-info{border-left-color:var(--c-text-accent)}.palette-overlay{position:fixed;inset:0;background:var(--c-overlay);display:flex;justify-content:center;align-items:flex-start;padding-top:12vh;z-index:var(--z-modal);-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.palette{width:min(560px,92vw);max-height:70vh;background:var(--c-bg-elevated);border:1px solid var(--c-border);border-radius:var(--radius-lg);box-shadow:var(--c-shadow-elevated);display:flex;flex-direction:column;overflow:hidden}.palette-input{border:none;border-bottom:1px solid var(--c-border);padding:var(--sp-3) var(--sp-4);font-size:var(--text-base);background:transparent;color:var(--c-text-primary);outline:none}.palette-list{overflow-y:auto;padding:var(--sp-1);display:flex;flex-direction:column;gap:2px}.palette-item{all:unset;display:flex;align-items:center;gap:var(--sp-2);padding:var(--sp-2) var(--sp-3);border-radius:var(--radius-sm);font-size:var(--text-sm);cursor:pointer;color:var(--c-text-primary)}.palette-item.is-active,.palette-item:hover{background:var(--c-accent-bg)}.palette-item-shortcut{margin-left:auto;font-family:var(--font-mono);font-size:var(--text-2xs);color:var(--c-text-muted);border:1px solid var(--c-border);padding:1px 6px;border-radius:var(--radius-sm)}.palette-item-section{font-size:var(--text-2xs);color:var(--c-text-muted);text-transform:uppercase;letter-spacing:.06em;padding:var(--sp-2) var(--sp-3) var(--sp-1)}.palette-empty{padding:var(--sp-4);text-align:center;color:var(--c-text-muted);font-size:var(--text-sm)}.help-modal{width:min(640px,92vw);background:var(--c-bg-elevated);border:1px solid var(--c-border);border-radius:var(--radius-lg);box-shadow:var(--c-shadow-elevated);padding:var(--sp-4)}.shortcut-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--sp-2) var(--sp-4);margin-top:var(--sp-3)}.shortcut-row{display:flex;align-items:center;justify-content:space-between;padding:var(--sp-1) 0;border-bottom:1px dashed var(--c-border-subtle)}.kbd{display:inline-flex;align-items:center;gap:2px;font-family:var(--font-mono);font-size:var(--text-2xs);background:var(--c-bg-inset);border:1px solid var(--c-border);border-radius:var(--radius-sm);padding:1px 6px;color:var(--c-text-secondary)}.drawer-backdrop{position:fixed;inset:0;background:var(--c-overlay);z-index:var(--z-overlay);-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:none}.drawer-backdrop.is-open{display:block}.diff-view{margin-top:var(--sp-3);border:1px solid var(--c-border);border-radius:var(--radius-md);padding:var(--sp-2);background:var(--c-bg-inset)}.diff-toolbar{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--sp-2);gap:var(--sp-2);flex-wrap:wrap}.diff-frontmatter{width:100%;margin-bottom:var(--sp-2);border-collapse:collapse;font-size:var(--text-xs)}.diff-frontmatter th,.diff-frontmatter td{text-align:left;padding:4px 8px;border-bottom:1px solid var(--c-border-subtle);vertical-align:top}.diff-frontmatter tr.is-protected{background:var(--c-danger-bg)}.label-danger{color:var(--c-text-error);margin-left:4px}.diff-hunk{margin-bottom:var(--sp-2)}.diff-hunk-header{color:var(--c-text-muted);padding:2px 4px}.diff-unified,.diff-column{font-family:var(--font-mono);font-size:12px;line-height:1.5;margin:0;padding:4px 0;overflow-x:auto;background:var(--c-bg-base);max-height:320px;overflow-y:auto}.diff-split{display:grid;grid-template-columns:1fr 1fr;gap:4px}.diff-line{display:flex;gap:8px;padding:0 8px;white-space:pre}.diff-marker{width:1ch;display:inline-block;flex-shrink:0}.diff-add{background:#16a34a2e;color:var(--c-text-success)}.diff-remove{background:#dc26262e;color:var(--c-text-error)}.diff-context{color:var(--c-text-secondary)}.btn.is-active{background:var(--c-accent-bg-strong);border-color:var(--c-border-focus-strong);color:var(--c-text-primary)}.theme-toggle-options{display:inline-flex;border:1px solid var(--c-border);border-radius:var(--radius-sm);overflow:hidden}.theme-toggle-options button{all:unset;padding:4px 8px;font-size:var(--text-2xs);cursor:pointer;color:var(--c-text-secondary);border-right:1px solid var(--c-border)}.theme-toggle-options button:last-child{border-right:none}.theme-toggle-options button:hover{color:var(--c-text-primary)}.theme-toggle-options button.is-active{background:var(--c-accent-bg-strong);color:var(--c-text-primary)}.undo-toast{position:fixed;bottom:var(--sp-4);left:50%;transform:translate(-50%);background:var(--c-bg-elevated);border:1px solid var(--c-border-focus);border-radius:var(--radius-md);padding:var(--sp-2) var(--sp-3);display:flex;align-items:center;gap:var(--sp-3);box-shadow:var(--c-shadow-elevated);z-index:var(--z-modal);font-size:var(--text-sm)}@media(max-width:1100px){.app-shell{grid-template-columns:1fr;grid-template-rows:var(--bar-height) minmax(0,1fr);grid-template-areas:"bar" "center";height:100vh}.drawer-trigger{display:inline-flex}.sidebar,.detail-rail{position:fixed;top:var(--bar-height);bottom:0;width:min(320px,90vw);z-index:var(--z-drawer);transform:translate(-100%);transition:transform .22s ease;box-shadow:var(--c-shadow-elevated)}.sidebar{left:0;border-right:1px solid var(--c-border)}.detail-rail{right:0;left:auto;transform:translate(100%);border-left:1px solid var(--c-border)}.sidebar.is-open,.detail-rail.is-open{transform:translate(0)}.center-area{grid-area:center}.stats-strip{flex-wrap:wrap;gap:var(--sp-2)}.canvas-toolbar{flex-wrap:wrap}}@media(max-width:600px){.sidebar,.detail-rail{width:100vw;box-shadow:none}.canvas-minimap{display:none}.canvas-legend{max-width:160px}.canvas-toolbar{left:var(--sp-1);right:var(--sp-1);top:var(--sp-1)}}@media(prefers-reduced-motion:reduce){*,*:before,*:after{animation-duration:.01ms!important;transition-duration:.01ms!important}}
|