ltcai 3.3.0 → 3.4.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.
Files changed (75) hide show
  1. package/README.md +85 -66
  2. package/docs/CHANGELOG.md +36 -0
  3. package/docs/architecture.md +2 -1
  4. package/docs/assets/v3.4.0/agent-run.png +0 -0
  5. package/docs/assets/v3.4.0/agents.png +0 -0
  6. package/docs/assets/v3.4.0/before/chat-before.png +0 -0
  7. package/docs/assets/v3.4.0/before/files-before.png +0 -0
  8. package/docs/assets/v3.4.0/chat.png +0 -0
  9. package/docs/assets/v3.4.0/connect-folder.png +0 -0
  10. package/docs/assets/v3.4.0/files.png +0 -0
  11. package/docs/assets/v3.4.0/home.png +0 -0
  12. package/docs/assets/v3.4.0/hooks-dispatch.png +0 -0
  13. package/docs/assets/v3.4.0/knowledge-graph.png +0 -0
  14. package/docs/assets/v3.4.0/local-agent.png +0 -0
  15. package/docs/assets/v3.4.0/memory.png +0 -0
  16. package/docs/assets/v3.4.0/settings.png +0 -0
  17. package/docs/assets/v3.4.0/vision-input.png +0 -0
  18. package/docs/assets/v3.4.0/workflows.png +0 -0
  19. package/knowledge_graph.py +45 -0
  20. package/knowledge_graph_api.py +10 -0
  21. package/latticeai/__init__.py +1 -1
  22. package/latticeai/api/agents.py +3 -0
  23. package/latticeai/api/hooks.py +39 -0
  24. package/latticeai/api/local_files.py +41 -0
  25. package/latticeai/api/models.py +36 -1
  26. package/latticeai/api/tools.py +16 -1
  27. package/latticeai/api/workflow_designer.py +2 -1
  28. package/latticeai/core/hooks.py +398 -2
  29. package/latticeai/core/marketplace.py +1 -1
  30. package/latticeai/core/multi_agent.py +1 -1
  31. package/latticeai/core/workflow_engine.py +21 -1
  32. package/latticeai/core/workspace_os.py +1 -1
  33. package/latticeai/server_app.py +40 -0
  34. package/latticeai/services/agent_runtime.py +46 -1
  35. package/latticeai/services/upload_service.py +17 -0
  36. package/package.json +1 -1
  37. package/scripts/capture/capture_v340.js +88 -0
  38. package/static/css/{tokens.8b8e31bd.css → tokens.3ba22e37.css} +109 -109
  39. package/static/css/tokens.css +109 -109
  40. package/static/v3/asset-manifest.json +24 -24
  41. package/static/v3/css/{lattice.components.011e988b.css → lattice.components.9b49d614.css} +57 -32
  42. package/static/v3/css/lattice.components.css +57 -32
  43. package/static/v3/css/{lattice.shell.4920f42d.css → lattice.shell.6ceea7c8.css} +75 -31
  44. package/static/v3/css/lattice.shell.css +75 -31
  45. package/static/v3/css/lattice.tokens.css +13 -13
  46. package/static/v3/css/{lattice.tokens.c597ff81.css → lattice.tokens.e7018963.css} +13 -13
  47. package/static/v3/css/{lattice.views.1d326beb.css → lattice.views.22f69117.css} +93 -15
  48. package/static/v3/css/lattice.views.css +93 -15
  49. package/static/v3/js/{app.cf5bb712.js → app.c4acfdd8.js} +1 -1
  50. package/static/v3/js/core/{api.113660c5.js → api.12b568ad.js} +67 -0
  51. package/static/v3/js/core/api.js +67 -0
  52. package/static/v3/js/core/{components.4c83e0a9.js → components.35f02e4c.js} +8 -0
  53. package/static/v3/js/core/components.js +8 -0
  54. package/static/v3/js/core/{routes.07ad6696.js → routes.d214b399.js} +16 -12
  55. package/static/v3/js/core/routes.js +16 -12
  56. package/static/v3/js/core/{shell.9e707234.js → shell.80a6ad82.js} +37 -9
  57. package/static/v3/js/core/shell.js +34 -6
  58. package/static/v3/js/views/agents.014d0b74.js +541 -0
  59. package/static/v3/js/views/agents.js +305 -57
  60. package/static/v3/js/views/{chat.c48fd9e2.js → chat.e6dd7dd0.js} +161 -9
  61. package/static/v3/js/views/chat.js +161 -9
  62. package/static/v3/js/views/files.adad14c1.js +365 -0
  63. package/static/v3/js/views/files.js +212 -79
  64. package/static/v3/js/views/home.24f8b8ae.js +200 -0
  65. package/static/v3/js/views/home.js +96 -15
  66. package/static/v3/js/views/hooks.13845954.js +215 -0
  67. package/static/v3/js/views/hooks.js +117 -1
  68. package/static/v3/js/views/{my-computer.1b2ff621.js → my-computer.c3ef5283.js} +224 -1
  69. package/static/v3/js/views/my-computer.js +224 -1
  70. package/static/v3/js/views/{settings.c7b0cc05.js → settings.8631fa5e.js} +54 -0
  71. package/static/v3/js/views/settings.js +54 -0
  72. package/static/v3/js/views/agents.c373d48c.js +0 -293
  73. package/static/v3/js/views/files.8464634a.js +0 -232
  74. package/static/v3/js/views/home.cdde3b32.js +0 -119
  75. package/static/v3/js/views/hooks.f3edebca.js +0 -99
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ltcai",
3
- "version": "3.3.0",
3
+ "version": "3.4.0",
4
4
  "description": "Lattice AI v3 local-first AI workspace platform with knowledge graph, vector index, hybrid search, agents, and workspace modes.",
5
5
  "homepage": "https://github.com/TaeSooPark-PTS/LatticeAI#readme",
6
6
  "repository": {
@@ -0,0 +1,88 @@
1
+ #!/usr/bin/env node
2
+ /*
3
+ * Capture v3.4.0 /app view screenshots into docs/assets/v3.4.0/.
4
+ *
5
+ * Drives the real SPA (built hashed assets) against the visual mock server, so
6
+ * every screenshot is the genuine v3.4.0 frontend rendering real view code with
7
+ * representative-but-honest mock data. Live-model output (VLM inference, agent
8
+ * LLM text) is NOT simulated; those remain runtime-pending per the release notes.
9
+ * Run `npm run build:assets` first so the manifest points at the new code.
10
+ *
11
+ * node scripts/capture/capture_v340.js
12
+ * Env: LTCAI_CAPTURE_BASE_URL (default http://127.0.0.1:4927 — the mock server)
13
+ */
14
+ const fs = require("fs");
15
+ const path = require("path");
16
+
17
+ async function loadPlaywright() {
18
+ try { return require("@playwright/test"); } catch (_) { return require("playwright"); }
19
+ }
20
+
21
+ const ROOT = path.resolve(__dirname, "..", "..");
22
+ const OUT = path.join(ROOT, "docs", "assets", "v3.4.0");
23
+ const BASE = process.env.LTCAI_CAPTURE_BASE_URL || "http://127.0.0.1:4927";
24
+
25
+ // { route, file, [action] }. action: "agent-run" clicks Run and waits for logs;
26
+ // "scroll-bottom" scrolls the view to reveal lower panels before the shot.
27
+ const SHOTS = [
28
+ { route: "home", file: "home.png" },
29
+ { route: "chat", file: "chat.png" },
30
+ { route: "chat", file: "vision-input.png" },
31
+ { route: "files", file: "files.png" },
32
+ { route: "files", file: "connect-folder.png", action: "scroll-bottom" },
33
+ { route: "knowledge-graph", file: "knowledge-graph.png" },
34
+ { route: "memory", file: "memory.png" },
35
+ { route: "agents", file: "agents.png" },
36
+ { route: "agents", file: "agent-run.png", action: "agent-run" },
37
+ { route: "workflows", file: "workflows.png" },
38
+ { route: "settings", file: "settings.png" },
39
+ { route: "my-computer", file: "local-agent.png" },
40
+ { route: "hooks", file: "hooks-dispatch.png", action: "scroll-bottom" },
41
+ ];
42
+
43
+ async function main() {
44
+ fs.mkdirSync(OUT, { recursive: true });
45
+ const { chromium } = await loadPlaywright();
46
+ const browser = await chromium.launch({ headless: true });
47
+ const context = await browser.newContext({ viewport: { width: 1480, height: 940 }, deviceScaleFactor: 2 });
48
+ await context.addInitScript(() => {
49
+ localStorage.setItem("lt-theme", "light");
50
+ localStorage.setItem("ltcai_mode", "admin");
51
+ localStorage.setItem("ltcai_user_email", "demo@lattice.local");
52
+ localStorage.setItem("ltcai_is_admin", "true");
53
+ });
54
+ const page = await context.newPage();
55
+
56
+ for (const shot of SHOTS) {
57
+ // Fresh full-page load per shot — avoids transient cross-view overlap that a
58
+ // same-page hash change can leave behind, so headers render crisp.
59
+ await page.goto(new URL("/app#/" + shot.route, BASE).toString(), { waitUntil: "domcontentloaded", timeout: 30000 });
60
+ await page.evaluate((mode) => document.documentElement.setAttribute("data-lt-theme", mode), "light");
61
+ await page.waitForSelector("#app .lt3-vhead, #app .lt3-chat", { timeout: 15000 }).catch(() => {});
62
+ await page.waitForTimeout(1800); // let the view hydrate fully (crisp, not mid-load)
63
+
64
+ if (shot.action === "agent-run") {
65
+ // Fill the goal and trigger a real run; wait for the logs/timeline to render.
66
+ const ta = page.locator("#app textarea").first();
67
+ await ta.fill("Summarize this week's release work and propose next steps.").catch(() => {});
68
+ const runBtn = page.getByRole("button", { name: /run agents/i }).first();
69
+ await runBtn.click({ timeout: 4000 }).catch(() => {});
70
+ await page.waitForTimeout(1500);
71
+ await page.evaluate(() => { const a = document.querySelector(".lt3-view"); if (a) a.scrollTop = 0; });
72
+ } else if (shot.action === "scroll-bottom") {
73
+ await page.evaluate(() => {
74
+ const sc = document.querySelector(".lt3-view") || document.scrollingElement;
75
+ if (sc) sc.scrollTop = sc.scrollHeight;
76
+ });
77
+ await page.waitForTimeout(700);
78
+ }
79
+
80
+ const out = path.join(OUT, shot.file);
81
+ await page.screenshot({ path: out, fullPage: false });
82
+ console.log(out);
83
+ }
84
+
85
+ await browser.close();
86
+ }
87
+
88
+ main().catch((e) => { console.error(e); process.exit(1); });
@@ -1,5 +1,5 @@
1
1
  /* ============================================================================
2
- * Lattice AI — Design Tokens (Single Source of Truth) v3.3.0
2
+ * Lattice AI — Design Tokens (Single Source of Truth) v3.3.1
3
3
  *
4
4
  * 이 파일이 색·면·테두리·그림자·포커스의 단일 출처다.
5
5
  * :root → 라이트 테마 값
@@ -18,75 +18,75 @@
18
18
  color-scheme: light;
19
19
 
20
20
  /* ── Brand raw scale (불변, 테마 무관 기준점) ───────────────────────────── */
21
- --lt-color-primary-100: #dbeafe;
22
- --lt-color-primary-200: #bfdbfe;
23
- --lt-color-primary-400: #60a5fa;
24
- --lt-color-primary-600: #2563eb;
25
- --lt-color-primary-800: #1e3a8a;
26
- --lt-color-ink-900: #111827;
27
- --lt-color-accent-cyan: #0891b2;
28
- --lt-color-accent-green: #0f9f8f;
29
- --lt-color-accent-amber: #b7791f;
30
- --lt-color-accent-pink: #b83280;
21
+ --lt-color-primary-100: #dfe7ff;
22
+ --lt-color-primary-200: #c8d6ff;
23
+ --lt-color-primary-400: #7f9cff;
24
+ --lt-color-primary-600: #3b63f4;
25
+ --lt-color-primary-800: #203a8d;
26
+ --lt-color-ink-900: #141923;
27
+ --lt-color-accent-cyan: #0c9ca8;
28
+ --lt-color-accent-green: #0e9f91;
29
+ --lt-color-accent-amber: #c77d16;
30
+ --lt-color-accent-pink: #c33fb3;
31
31
 
32
32
  /* ╔══════════════════════════════════════════════════════════════════════╗
33
33
  ║ SEMANTIC TOKENS — LIGHT (neutral workspace palette) ║
34
34
  ╚══════════════════════════════════════════════════════════════════════╝ */
35
35
 
36
36
  /* background / surfaces */
37
- --bg: #f6f8f7;
38
- --bg-soft: #eef3f2;
37
+ --bg: #f5f7fb;
38
+ --bg-soft: #edf1f7;
39
39
  --surface: #ffffff;
40
- --surface-2: #f0f5f4;
41
- --surface-3: #e4ecea;
42
- --surface-muted: #edf3f2;
43
- --surface-elevated: rgba(255, 255, 255, 0.94);
44
- --card: rgba(255, 255, 255, 0.92);
45
- --sidebar: rgba(255, 255, 255, 0.94);
40
+ --surface-2: #eef2f7;
41
+ --surface-3: #e1e7ef;
42
+ --surface-muted: #e9eef6;
43
+ --surface-elevated: rgba(255, 255, 255, 0.96);
44
+ --card: rgba(255, 255, 255, 0.94);
45
+ --sidebar: rgba(249, 251, 255, 0.96);
46
46
  --modal: rgba(255, 255, 255, 0.98);
47
47
  --table: rgba(255, 255, 255, 0.78);
48
48
  --input: rgba(255, 255, 255, 0.86);
49
- --overlay: rgba(15, 23, 42, 0.44);
49
+ --overlay: rgba(20, 25, 35, 0.48);
50
50
  --overlay-scrim: var(--overlay);
51
51
  --app-bg:
52
- linear-gradient(180deg, #f8faf9 0%, #eef4f2 54%, #f7faf9 100%);
52
+ linear-gradient(180deg, #f8faff 0%, #eef3fa 48%, #f5f7fb 100%);
53
53
 
54
54
  /* text */
55
- --text: #24223d;
56
- --muted: #475569; /* muted-text */
57
- --faint: #70818f;
55
+ --text: #1e2430;
56
+ --muted: #526071; /* muted-text */
57
+ --faint: #7a8798;
58
58
  --text-muted: var(--muted);
59
59
 
60
60
  /* border / line */
61
- --border: rgba(15, 23, 42, 0.12);
62
- --border-strong: rgba(15, 23, 42, 0.22);
63
- --line: rgba(15, 23, 42, 0.10);
64
- --line-strong: rgba(15, 23, 42, 0.18);
61
+ --border: rgba(30, 36, 48, 0.12);
62
+ --border-strong: rgba(30, 36, 48, 0.22);
63
+ --line: rgba(30, 36, 48, 0.09);
64
+ --line-strong: rgba(30, 36, 48, 0.18);
65
65
 
66
66
  /* accent */
67
- --accent: #2563eb;
68
- --accent-2: #0f9f8f;
69
- --accent-3: #b7791f;
70
- --accent-pink: #b83280;
71
- --accent-soft: rgba(37, 99, 235, 0.10);
72
- --accent-deep: #1d4ed8;
67
+ --accent: #3b63f4;
68
+ --accent-2: #0e9f91;
69
+ --accent-3: #c77d16;
70
+ --accent-pink: #c33fb3;
71
+ --accent-soft: rgba(59, 99, 244, 0.11);
72
+ --accent-deep: #2f4fd0;
73
73
 
74
74
  /* status */
75
- --success: #0f8f6d;
76
- --warning: #a16207;
77
- --danger: #c2410c;
75
+ --success: #0d8f70;
76
+ --warning: #a45f07;
77
+ --danger: #bf3e2f;
78
78
 
79
79
  /* graph */
80
- --graph-bg: #f8faf9;
81
- --graph-grid: rgba(12, 92, 115, 0.08);
80
+ --graph-bg: #f8faff;
81
+ --graph-grid: rgba(59, 99, 244, 0.09);
82
82
  --graph-pill: rgba(255, 255, 255, 0.92);
83
83
 
84
84
  /* shadow */
85
- --shadow: 0 18px 50px rgba(15, 23, 42, 0.10);
86
- --shadow-sm: 0 8px 22px rgba(15, 23, 42, 0.08);
85
+ --shadow: 0 18px 48px rgba(30, 36, 48, 0.11);
86
+ --shadow-sm: 0 8px 20px rgba(30, 36, 48, 0.08);
87
87
 
88
88
  /* focus ring */
89
- --focus-ring: rgba(37, 99, 235, 0.55);
89
+ --focus-ring: rgba(59, 99, 244, 0.56);
90
90
 
91
91
  /* ── 구조 토큰(색 아님) ─────────────────────────────────────────────── */
92
92
  --radius: 14px;
@@ -123,17 +123,17 @@
123
123
 
124
124
  /* ── --lt-* 별칭 (workspace.css / platform.css 가 참조) — LIGHT ──────────
125
125
  순환참조를 피하려 구체값으로 둔다(소비자가 --bg 등을 --lt-* 로 재정의해도 안전). */
126
- --lt-bg: #f6f8f7;
126
+ --lt-bg: #f5f7fb;
127
127
  --lt-surface: #ffffff;
128
- --lt-surface-2: #f0f5f4;
128
+ --lt-surface-2: #eef2f7;
129
129
  --lt-input: rgba(255, 255, 255, 0.86);
130
- --lt-ink: #24223d;
131
- --lt-ink-soft: #475569;
132
- --lt-muted: #70818f;
133
- --lt-line: rgba(15, 23, 42, 0.12);
134
- --lt-accent: #2563eb;
135
- --lt-accent-2: #0f9f8f;
136
- --lt-shadow-md: 0 18px 50px rgba(15, 23, 42, 0.10);
130
+ --lt-ink: #1e2430;
131
+ --lt-ink-soft: #526071;
132
+ --lt-muted: #7a8798;
133
+ --lt-line: rgba(30, 36, 48, 0.12);
134
+ --lt-accent: #3b63f4;
135
+ --lt-accent-2: #0e9f91;
136
+ --lt-shadow-md: 0 18px 48px rgba(30, 36, 48, 0.11);
137
137
 
138
138
  /* ── 모션 / 타이포 ─────────────────────────────────────────────────── */
139
139
  --lt-motion-ease: cubic-bezier(0.22, 1, 0.36, 1);
@@ -145,63 +145,63 @@
145
145
  :root[data-lt-theme="dark"] {
146
146
  color-scheme: dark;
147
147
 
148
- --bg: #101418;
149
- --bg-soft: #151b20;
150
- --surface: #1b2328;
151
- --surface-2: #202b31;
152
- --surface-3: #29363d;
153
- --surface-muted: #151d22;
154
- --surface-elevated: #253139;
155
- --card: rgba(255, 255, 255, 0.055);
156
- --sidebar: #11191d;
157
- --modal: rgba(27, 35, 40, 0.98);
148
+ --bg: #0d1017;
149
+ --bg-soft: #111722;
150
+ --surface: #151b26;
151
+ --surface-2: #1b2330;
152
+ --surface-3: #263142;
153
+ --surface-muted: #111925;
154
+ --surface-elevated: #202a38;
155
+ --card: rgba(255, 255, 255, 0.06);
156
+ --sidebar: #0f141d;
157
+ --modal: rgba(21, 27, 38, 0.98);
158
158
  --table: rgba(255, 255, 255, 0.045);
159
159
  --input: rgba(255, 255, 255, 0.065);
160
- --overlay: rgba(0, 0, 0, 0.64);
160
+ --overlay: rgba(0, 0, 0, 0.66);
161
161
  --overlay-scrim: var(--overlay);
162
162
  --app-bg:
163
- linear-gradient(180deg, #101418 0%, #151d22 54%, #101418 100%);
163
+ linear-gradient(180deg, #0d1017 0%, #121a27 50%, #0d1017 100%);
164
164
 
165
- --text: #edf3f2;
166
- --muted: #b8c5c2;
167
- --faint: #849894;
165
+ --text: #eef3fb;
166
+ --muted: #b8c3d2;
167
+ --faint: #8290a3;
168
168
  --text-muted: var(--muted);
169
169
 
170
- --border: rgba(196, 213, 208, 0.18);
171
- --border-strong: rgba(196, 213, 208, 0.30);
172
- --line: rgba(196, 213, 208, 0.14);
173
- --line-strong: rgba(196, 213, 208, 0.24);
170
+ --border: rgba(207, 217, 231, 0.16);
171
+ --border-strong: rgba(207, 217, 231, 0.28);
172
+ --line: rgba(207, 217, 231, 0.12);
173
+ --line-strong: rgba(207, 217, 231, 0.22);
174
174
 
175
- --accent: #0284c7;
176
- --accent-2: #0f766e;
177
- --accent-3: #facc15;
178
- --accent-pink: #f0abfc;
179
- --accent-soft: rgba(2, 132, 199, 0.20);
180
- --accent-deep: #0369a1;
175
+ --accent: #7f9cff;
176
+ --accent-2: #4dd8c2;
177
+ --accent-3: #f2bc57;
178
+ --accent-pink: #f47ac2;
179
+ --accent-soft: rgba(127, 156, 255, 0.18);
180
+ --accent-deep: #a8bbff;
181
181
 
182
182
  --success: #34d399;
183
- --warning: #fbbf24;
184
- --danger: #fb923c;
183
+ --warning: #f2bc57;
184
+ --danger: #ff8a72;
185
185
 
186
- --graph-bg: #0f1518;
187
- --graph-grid: rgba(94, 234, 212, 0.08);
188
- --graph-pill: rgba(27, 35, 40, 0.92);
186
+ --graph-bg: #0f141d;
187
+ --graph-grid: rgba(127, 156, 255, 0.10);
188
+ --graph-pill: rgba(21, 27, 38, 0.92);
189
189
 
190
190
  --shadow: 0 18px 54px rgba(0, 0, 0, 0.50);
191
191
  --shadow-sm: 0 8px 24px rgba(0, 0, 0, 0.42);
192
192
 
193
- --focus-ring: rgba(125, 211, 252, 0.70);
193
+ --focus-ring: rgba(127, 156, 255, 0.70);
194
194
 
195
- --lt-bg: #101418;
196
- --lt-surface: #1b2328;
197
- --lt-surface-2: #202b31;
195
+ --lt-bg: #0d1017;
196
+ --lt-surface: #151b26;
197
+ --lt-surface-2: #1b2330;
198
198
  --lt-input: rgba(255, 255, 255, 0.065);
199
- --lt-ink: #edf3f2;
200
- --lt-ink-soft: #b8c5c2;
201
- --lt-muted: #849894;
202
- --lt-line: rgba(196, 213, 208, 0.18);
203
- --lt-accent: #0284c7;
204
- --lt-accent-2: #0f766e;
199
+ --lt-ink: #eef3fb;
200
+ --lt-ink-soft: #b8c3d2;
201
+ --lt-muted: #8290a3;
202
+ --lt-line: rgba(207, 217, 231, 0.16);
203
+ --lt-accent: #7f9cff;
204
+ --lt-accent-2: #4dd8c2;
205
205
  --lt-shadow-md: 0 18px 54px rgba(0, 0, 0, 0.50);
206
206
 
207
207
  --ref-indigo: #93c5fd;
@@ -213,26 +213,26 @@
213
213
  @media (prefers-color-scheme: dark) {
214
214
  :root:not([data-lt-theme="light"]):not([data-lt-theme="dark"]) {
215
215
  color-scheme: dark;
216
- --bg: #101418; --bg-soft: #151b20;
217
- --surface: #1b2328; --surface-2: #202b31; --surface-3: #29363d;
218
- --surface-muted: #151d22; --surface-elevated: #253139;
219
- --card: rgba(255,255,255,0.055); --sidebar: #11191d;
220
- --modal: rgba(27,35,40,0.98); --table: rgba(255,255,255,0.045);
221
- --input: rgba(255,255,255,0.065); --overlay: rgba(0,0,0,0.64); --overlay-scrim: var(--overlay);
216
+ --bg: #0d1017; --bg-soft: #111722;
217
+ --surface: #151b26; --surface-2: #1b2330; --surface-3: #263142;
218
+ --surface-muted: #111925; --surface-elevated: #202a38;
219
+ --card: rgba(255,255,255,0.06); --sidebar: #0f141d;
220
+ --modal: rgba(21,27,38,0.98); --table: rgba(255,255,255,0.045);
221
+ --input: rgba(255,255,255,0.065); --overlay: rgba(0,0,0,0.66); --overlay-scrim: var(--overlay);
222
222
  --app-bg:
223
- linear-gradient(180deg, #101418 0%, #151d22 54%, #101418 100%);
224
- --text: #edf3f2; --muted: #b8c5c2; --faint: #849894; --text-muted: var(--muted);
225
- --border: rgba(196,213,208,0.18); --border-strong: rgba(196,213,208,0.30);
226
- --line: rgba(196,213,208,0.14); --line-strong: rgba(196,213,208,0.24);
227
- --accent: #0284c7; --accent-2: #0f766e; --accent-3: #facc15;
228
- --accent-pink: #f0abfc; --accent-soft: rgba(2,132,199,0.20); --accent-deep: #0369a1;
229
- --success: #34d399; --warning: #fbbf24; --danger: #fb923c;
230
- --graph-bg: #0f1518; --graph-grid: rgba(94,234,212,0.08); --graph-pill: rgba(27,35,40,0.92);
223
+ linear-gradient(180deg, #0d1017 0%, #121a27 50%, #0d1017 100%);
224
+ --text: #eef3fb; --muted: #b8c3d2; --faint: #8290a3; --text-muted: var(--muted);
225
+ --border: rgba(207,217,231,0.16); --border-strong: rgba(207,217,231,0.28);
226
+ --line: rgba(207,217,231,0.12); --line-strong: rgba(207,217,231,0.22);
227
+ --accent: #7f9cff; --accent-2: #4dd8c2; --accent-3: #f2bc57;
228
+ --accent-pink: #f47ac2; --accent-soft: rgba(127,156,255,0.18); --accent-deep: #a8bbff;
229
+ --success: #34d399; --warning: #f2bc57; --danger: #ff8a72;
230
+ --graph-bg: #0f141d; --graph-grid: rgba(127,156,255,0.10); --graph-pill: rgba(21,27,38,0.92);
231
231
  --shadow: 0 18px 54px rgba(0,0,0,0.50); --shadow-sm: 0 8px 24px rgba(0,0,0,0.42);
232
- --focus-ring: rgba(125,211,252,0.70);
233
- --lt-bg: #101418; --lt-surface: #1b2328; --lt-surface-2: #202b31; --lt-input: rgba(255,255,255,0.065);
234
- --lt-ink: #edf3f2; --lt-ink-soft: #b8c5c2; --lt-muted: #849894;
235
- --lt-line: rgba(196,213,208,0.18); --lt-accent: #0284c7; --lt-accent-2: #0f766e;
232
+ --focus-ring: rgba(127,156,255,0.70);
233
+ --lt-bg: #0d1017; --lt-surface: #151b26; --lt-surface-2: #1b2330; --lt-input: rgba(255,255,255,0.065);
234
+ --lt-ink: #eef3fb; --lt-ink-soft: #b8c3d2; --lt-muted: #8290a3;
235
+ --lt-line: rgba(207,217,231,0.16); --lt-accent: #7f9cff; --lt-accent-2: #4dd8c2;
236
236
  --lt-shadow-md: 0 18px 54px rgba(0,0,0,0.50);
237
237
  }
238
238
  }