ltcai 2.2.7 → 3.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.
Files changed (122) hide show
  1. package/README.md +72 -34
  2. package/docs/CHANGELOG.md +119 -0
  3. package/docs/V3_BACKEND_ARCHITECTURE.md +138 -0
  4. package/docs/V3_FRONTEND.md +139 -0
  5. package/knowledge_graph.py +649 -21
  6. package/latticeai/__init__.py +1 -1
  7. package/latticeai/api/admin.py +47 -0
  8. package/latticeai/api/agents.py +54 -31
  9. package/latticeai/api/auth.py +5 -2
  10. package/latticeai/api/chat.py +10 -2
  11. package/latticeai/api/search.py +240 -0
  12. package/latticeai/api/static_routes.py +11 -2
  13. package/latticeai/core/config.py +18 -0
  14. package/latticeai/core/embedding_providers.py +625 -0
  15. package/latticeai/core/local_embeddings.py +86 -0
  16. package/latticeai/core/workspace_os.py +1 -1
  17. package/latticeai/server_app.py +65 -1
  18. package/latticeai/services/agent_runtime.py +245 -0
  19. package/latticeai/services/search_service.py +346 -0
  20. package/package.json +13 -6
  21. package/scripts/build_v3_assets.mjs +164 -0
  22. package/scripts/capture/README.md +28 -0
  23. package/scripts/capture/capture_enterprise.js +8 -0
  24. package/scripts/capture/capture_graph.js +8 -0
  25. package/scripts/capture/capture_onboarding.js +8 -0
  26. package/scripts/capture/capture_page.js +43 -0
  27. package/scripts/capture/capture_release_media.js +125 -0
  28. package/scripts/capture/capture_skills.js +8 -0
  29. package/scripts/capture/capture_workspace.js +8 -0
  30. package/scripts/generate_diagrams.py +513 -0
  31. package/scripts/lint_v3.mjs +33 -0
  32. package/scripts/release-0.3.1.sh +105 -0
  33. package/scripts/take_screenshots.js +69 -0
  34. package/scripts/validate_release_artifacts.py +167 -0
  35. package/static/account.html +9 -9
  36. package/static/activity.html +4 -4
  37. package/static/admin.html +8 -8
  38. package/static/agents.html +4 -4
  39. package/static/chat.html +10 -10
  40. package/static/css/reference/account.css +137 -1
  41. package/static/css/reference/chat.css +31 -37
  42. package/static/css/responsive.css +42 -0
  43. package/static/css/tokens.5a595671.css +260 -0
  44. package/static/css/tokens.css +125 -130
  45. package/static/graph.html +9 -9
  46. package/static/manifest.json +3 -3
  47. package/static/plugins.html +4 -4
  48. package/static/scripts/account.js +4 -4
  49. package/static/scripts/chat.js +40 -8
  50. package/static/scripts/workspace.js +78 -0
  51. package/static/sw.js +3 -1
  52. package/static/v3/asset-manifest.json +47 -0
  53. package/static/v3/css/lattice.base.css +128 -0
  54. package/static/v3/css/lattice.base.e4cdd05d.css +128 -0
  55. package/static/v3/css/lattice.components.011e988b.css +447 -0
  56. package/static/v3/css/lattice.components.css +447 -0
  57. package/static/v3/css/lattice.shell.4920f42d.css +407 -0
  58. package/static/v3/css/lattice.shell.css +407 -0
  59. package/static/v3/css/lattice.tokens.c597ff81.css +132 -0
  60. package/static/v3/css/lattice.tokens.css +132 -0
  61. package/static/v3/css/lattice.views.3ee19d4e.css +277 -0
  62. package/static/v3/css/lattice.views.css +277 -0
  63. package/static/v3/index.html +69 -0
  64. package/static/v3/js/app.46fb61d9.js +26 -0
  65. package/static/v3/js/app.js +26 -0
  66. package/static/v3/js/core/api.22a41d42.js +344 -0
  67. package/static/v3/js/core/api.js +344 -0
  68. package/static/v3/js/core/components.4c83e0a9.js +222 -0
  69. package/static/v3/js/core/components.js +222 -0
  70. package/static/v3/js/core/dom.a2773eb0.js +148 -0
  71. package/static/v3/js/core/dom.js +148 -0
  72. package/static/v3/js/core/router.584570f2.js +37 -0
  73. package/static/v3/js/core/router.js +37 -0
  74. package/static/v3/js/core/routes.f935dd50.js +78 -0
  75. package/static/v3/js/core/routes.js +78 -0
  76. package/static/v3/js/core/shell.1b6199d6.js +363 -0
  77. package/static/v3/js/core/shell.js +363 -0
  78. package/static/v3/js/core/store.34ebd5e6.js +113 -0
  79. package/static/v3/js/core/store.js +113 -0
  80. package/static/v3/js/views/admin-audit.660a1fb1.js +185 -0
  81. package/static/v3/js/views/admin-audit.js +185 -0
  82. package/static/v3/js/views/admin-permissions.a7ae5f09.js +177 -0
  83. package/static/v3/js/views/admin-permissions.js +177 -0
  84. package/static/v3/js/views/admin-policies.3658fd86.js +102 -0
  85. package/static/v3/js/views/admin-policies.js +102 -0
  86. package/static/v3/js/views/admin-private-vpc.7d342d36.js +135 -0
  87. package/static/v3/js/views/admin-private-vpc.js +135 -0
  88. package/static/v3/js/views/admin-security.07c66b72.js +180 -0
  89. package/static/v3/js/views/admin-security.js +180 -0
  90. package/static/v3/js/views/admin-users.03bac88c.js +168 -0
  91. package/static/v3/js/views/admin-users.js +168 -0
  92. package/static/v3/js/views/agents.14e48bdd.js +193 -0
  93. package/static/v3/js/views/agents.js +193 -0
  94. package/static/v3/js/views/chat.718144ce.js +449 -0
  95. package/static/v3/js/views/chat.js +449 -0
  96. package/static/v3/js/views/files.4935197e.js +186 -0
  97. package/static/v3/js/views/files.js +186 -0
  98. package/static/v3/js/views/home.cdde3b32.js +119 -0
  99. package/static/v3/js/views/home.js +119 -0
  100. package/static/v3/js/views/hybrid-search.b22b97e0.js +195 -0
  101. package/static/v3/js/views/hybrid-search.js +195 -0
  102. package/static/v3/js/views/knowledge-graph.a14ea7e7.js +237 -0
  103. package/static/v3/js/views/knowledge-graph.js +237 -0
  104. package/static/v3/js/views/models.a1ffa147.js +256 -0
  105. package/static/v3/js/views/models.js +256 -0
  106. package/static/v3/js/views/my-computer.1b2ff621.js +237 -0
  107. package/static/v3/js/views/my-computer.js +237 -0
  108. package/static/v3/js/views/pipeline.c522f1ce.js +157 -0
  109. package/static/v3/js/views/pipeline.js +157 -0
  110. package/static/v3/js/views/settings.4f777210.js +250 -0
  111. package/static/v3/js/views/settings.js +250 -0
  112. package/static/workflows.html +4 -4
  113. package/static/workspace.css +340 -2
  114. package/static/workspace.html +43 -24
  115. package/docs/images/tmp_frames/frame_00.png +0 -0
  116. package/docs/images/tmp_frames/frame_01.png +0 -0
  117. package/docs/images/tmp_frames/frame_02.png +0 -0
  118. package/docs/images/tmp_frames/frame_03.png +0 -0
  119. package/docs/images/tmp_frames/hero_00.png +0 -0
  120. package/docs/images/tmp_frames/hero_01.png +0 -0
  121. package/docs/images/tmp_frames/hero_02.png +0 -0
  122. package/docs/images/tmp_frames/hero_03.png +0 -0
@@ -19,10 +19,7 @@
19
19
  inset: 0;
20
20
  pointer-events: none;
21
21
  z-index: 0;
22
- background:
23
- radial-gradient(circle at 82% 12%, var(--border), transparent 30%),
24
- radial-gradient(circle at 10% 80%, rgba(180,160,255,0.16), transparent 35%),
25
- linear-gradient(180deg, var(--bg) 0%, var(--surface-2) 52%, var(--surface) 100%);
22
+ background: var(--app-bg);
26
23
  }
27
24
 
28
25
  .bg-shapes {
@@ -2872,14 +2869,14 @@
2872
2869
  color: var(--accent);
2873
2870
  }
2874
2871
 
2875
- /* Lattice AI workspace — light lavender theme. */
2872
+ /* Lattice AI workspace — product shell theme. */
2876
2873
  .app-layout {
2877
2874
  --bg: var(--bg);
2878
2875
  --surface: var(--surface);
2879
2876
  --surface-2: var(--surface-2);
2880
2877
  --surface-3: var(--surface-3);
2881
2878
  --accent: var(--accent);
2882
- --accent-2: var(--accent-2);
2879
+ --accent-2: var(--lt-accent-2, #0f9f8f);
2883
2880
  --accent-3: var(--accent-3);
2884
2881
  --accent-pink: var(--accent-pink);
2885
2882
  --accent-soft: var(--accent-soft);
@@ -4220,7 +4217,7 @@
4220
4217
  }
4221
4218
 
4222
4219
  /* =========================================================
4223
- Soft Lavender Theme (4/10) — full override for .app-layout
4220
+ Workspace product shell — full override for .app-layout
4224
4221
  ========================================================= */
4225
4222
  body.lattice-ref-chat .app-layout {
4226
4223
  --bg: var(--bg);
@@ -4228,7 +4225,7 @@
4228
4225
  --surface-2: var(--surface-2);
4229
4226
  --surface-3: var(--surface-3);
4230
4227
  --accent: var(--accent);
4231
- --accent-2: #ffb84d;
4228
+ --accent-2: var(--lt-accent-2, #0f9f8f);
4232
4229
  --accent-3: var(--accent-2);
4233
4230
  --accent-soft: var(--accent-soft);
4234
4231
  --text: var(--text);
@@ -4238,10 +4235,7 @@
4238
4235
  --border-strong: var(--border-strong);
4239
4236
  --shadow: var(--shadow);
4240
4237
  --shadow-sm: var(--shadow-sm);
4241
- background:
4242
- radial-gradient(circle at 82% 12%, var(--border), transparent 30%),
4243
- radial-gradient(circle at 10% 80%, rgba(180,160,255,0.16), transparent 35%),
4244
- linear-gradient(180deg, var(--bg) 0%, var(--surface-2) 52%, var(--surface) 100%);
4238
+ background: var(--app-bg);
4245
4239
  }
4246
4240
  body.lattice-ref-chat .app-layout::before,
4247
4241
  body.lattice-ref-chat .app-layout::after {
@@ -4251,15 +4245,15 @@
4251
4245
  /* Sidebar */
4252
4246
  body.lattice-ref-chat .app-layout .sidebar {
4253
4247
  background: var(--sidebar);
4254
- border-right: 1px solid rgba(111,66,232,0.13);
4255
- box-shadow: 12px 0 46px rgba(88,72,150,0.08);
4248
+ border-right: 1px solid var(--line);
4249
+ box-shadow: none;
4256
4250
  }
4257
4251
 
4258
4252
  /* Chat header */
4259
4253
  body.lattice-ref-chat .app-layout .chat-header {
4260
4254
  background: var(--sidebar);
4261
- border-bottom: 1px solid rgba(111,66,232,0.11);
4262
- box-shadow: 0 2px 12px rgba(88,72,150,0.06);
4255
+ border-bottom: 1px solid var(--line);
4256
+ box-shadow: none;
4263
4257
  backdrop-filter: blur(14px);
4264
4258
  }
4265
4259
 
@@ -4306,28 +4300,28 @@
4306
4300
  /* 입력창 영역 */
4307
4301
  body.lattice-ref-chat .app-layout .input-area {
4308
4302
  background: linear-gradient(0deg,
4309
- rgba(243,237,255,0.99) 0%,
4310
- rgba(243,237,255,0.86) 64%,
4303
+ color-mix(in srgb, var(--bg) 98%, transparent) 0%,
4304
+ color-mix(in srgb, var(--bg) 82%, transparent) 64%,
4311
4305
  transparent 100%);
4312
4306
  }
4313
4307
  body.lattice-ref-chat .app-layout .input-box {
4314
4308
  background: var(--input);
4315
- border: 1px solid rgba(111,66,232,0.17);
4316
- box-shadow: 0 8px 32px rgba(88,72,150,0.10);
4309
+ border: 1px solid var(--line);
4310
+ box-shadow: var(--shadow-sm);
4317
4311
  }
4318
4312
  body.lattice-ref-chat .app-layout .input-box:focus-within {
4319
- border-color: rgba(111,66,232,0.44);
4320
- box-shadow: 0 8px 32px rgba(88,72,150,0.10), 0 0 0 3px rgba(111,66,232,0.09);
4313
+ border-color: var(--accent);
4314
+ box-shadow: var(--shadow-sm), 0 0 0 3px var(--accent-soft);
4321
4315
  }
4322
4316
 
4323
4317
  /* 전송 버튼 */
4324
4318
  body.lattice-ref-chat .app-layout .send-btn {
4325
4319
  background: linear-gradient(135deg, var(--accent), var(--accent-2));
4326
4320
  color: #ffffff;
4327
- box-shadow: 0 8px 20px rgba(111,66,232,0.28);
4321
+ box-shadow: 0 8px 20px color-mix(in srgb, var(--accent) 24%, transparent);
4328
4322
  }
4329
4323
  body.lattice-ref-chat .app-layout .send-btn:hover {
4330
- box-shadow: 0 10px 26px rgba(111,66,232,0.38);
4324
+ box-shadow: 0 10px 26px color-mix(in srgb, var(--accent) 34%, transparent);
4331
4325
  opacity: 0.92;
4332
4326
  }
4333
4327
 
@@ -4339,17 +4333,17 @@
4339
4333
  box-shadow: 0 2px 10px rgba(88,72,150,0.08);
4340
4334
  }
4341
4335
  body.lattice-ref-chat .app-layout .empty-chip:hover {
4342
- background: rgba(111,66,232,0.08);
4336
+ background: var(--accent-soft);
4343
4337
  border-color: var(--border-strong);
4344
4338
  }
4345
4339
  body.lattice-ref-chat .app-layout .ops-card {
4346
4340
  background: var(--card);
4347
- border-color: rgba(111,66,232,0.13);
4348
- box-shadow: 0 6px 22px rgba(88,72,150,0.08);
4341
+ border-color: var(--line);
4342
+ box-shadow: var(--shadow-sm);
4349
4343
  }
4350
4344
  body.lattice-ref-chat .app-layout .ops-card.primary {
4351
- background: linear-gradient(135deg, rgba(111,66,232,0.10), var(--card) 58%);
4352
- border-color: rgba(111,66,232,0.22);
4345
+ background: linear-gradient(135deg, var(--accent-soft), var(--card) 58%);
4346
+ border-color: var(--border-strong);
4353
4347
  }
4354
4348
 
4355
4349
  /* 배지/버튼 */
@@ -4371,8 +4365,8 @@
4371
4365
  body.lattice-ref-chat .app-layout .status-btn,
4372
4366
  body.lattice-ref-chat .app-layout .admin-btn,
4373
4367
  body.lattice-ref-chat .app-layout .setup-wizard-sidebar-btn {
4374
- background: rgba(111,66,232,0.10);
4375
- border-color: rgba(111,66,232,0.22);
4368
+ background: var(--accent-soft);
4369
+ border-color: var(--border-strong);
4376
4370
  color: var(--accent);
4377
4371
  box-shadow: none;
4378
4372
  }
@@ -4388,7 +4382,7 @@
4388
4382
  color: var(--text);
4389
4383
  -webkit-text-fill-color: var(--text);
4390
4384
  background: var(--accent-soft);
4391
- border-color: rgba(111,66,232,0.24);
4385
+ border-color: var(--border-strong);
4392
4386
  }
4393
4387
  /* 채팅 기록 목록도 검정 */
4394
4388
  body.lattice-ref-chat .app-layout .history-item {
@@ -4408,20 +4402,20 @@
4408
4402
  /* 사이드바 검색 입력 */
4409
4403
  body.lattice-ref-chat .app-layout .sidebar-search input {
4410
4404
  color: var(--text);
4411
- background: rgba(111,66,232,0.06);
4412
- border-color: rgba(111,66,232,0.16);
4405
+ background: var(--input);
4406
+ border-color: var(--line);
4413
4407
  }
4414
4408
  body.lattice-ref-chat .app-layout .sidebar-search input::placeholder {
4415
4409
  color: var(--faint);
4416
4410
  -webkit-text-fill-color: var(--faint);
4417
4411
  }
4418
- /* 상단 모드 세그멘트 배경도 라벤더로 */
4412
+ /* 상단 모드 세그멘트 */
4419
4413
  body.lattice-ref-chat .app-layout .mode-segmented {
4420
4414
  background: var(--surface-2);
4421
- border-color: rgba(111,66,232,0.16);
4415
+ border-color: var(--line);
4422
4416
  }
4423
4417
  body.lattice-ref-chat .app-layout .mode-segmented button:not(.active) {
4424
- color: #3d3860;
4418
+ color: var(--muted);
4425
4419
  }
4426
4420
 
4427
4421
  /* ── 홈 뷰: 채팅 전용 사이드바 섹션 숨기기 ── */
@@ -817,3 +817,45 @@ body.lattice-ref-chat .app-layout #user-input:focus {
817
817
  width: 100%;
818
818
  }
819
819
  }
820
+
821
+ /* Product workspace shell: this file loads last, so keep the mobile rail fluid. */
822
+ @media (max-width: 860px) {
823
+ body.workspace-page .workspace-shell {
824
+ grid-template-columns: 1fr;
825
+ min-width: 0;
826
+ width: 100%;
827
+ }
828
+
829
+ body.workspace-page .workspace-rail {
830
+ height: auto;
831
+ min-height: 0;
832
+ min-width: 0;
833
+ max-width: 100vw;
834
+ position: relative;
835
+ width: 100%;
836
+ }
837
+
838
+ body.workspace-page main {
839
+ min-width: 0;
840
+ width: 100%;
841
+ }
842
+
843
+ body.workspace-page .workspace-rail nav,
844
+ body.workspace-page .rail-links {
845
+ min-width: 0;
846
+ width: 100%;
847
+ }
848
+
849
+ body.workspace-page .workspace-rail a span {
850
+ min-width: 0;
851
+ overflow: hidden;
852
+ text-overflow: ellipsis;
853
+ }
854
+ }
855
+
856
+ @media (max-width: 520px) {
857
+ body.workspace-page .workspace-rail nav,
858
+ body.workspace-page .rail-links {
859
+ grid-template-columns: 1fr;
860
+ }
861
+ }
@@ -0,0 +1,260 @@
1
+ /* ============================================================================
2
+ * Lattice AI — Design Tokens (Single Source of Truth) v3.1.0
3
+ *
4
+ * 이 파일이 색·면·테두리·그림자·포커스의 단일 출처다.
5
+ * :root → 라이트 테마 값
6
+ * :root[data-lt-theme="dark"] → 동일 토큰명의 다크 값
7
+ * @media (prefers-color-scheme: dark) → 사용자가 명시 선택 안 했을 때 OS 추종
8
+ *
9
+ * 컴포넌트 CSS 는 항상 var(--token) 만 사용한다. 따라서 이 파일의 값만 바꾸면
10
+ * 전체 UI 가 자연스럽게 전환된다. (컴포넌트별 다크 오버라이드 / !important 금지)
11
+ *
12
+ * 토큰 카테고리(요청 범위): background · surface · card · sidebar · modal · table ·
13
+ * graph · text · muted-text · border · accent · success · warning · danger ·
14
+ * input · overlay · shadow · focus-ring
15
+ * ========================================================================== */
16
+
17
+ :root {
18
+ color-scheme: light;
19
+
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;
31
+
32
+ /* ╔══════════════════════════════════════════════════════════════════════╗
33
+ ║ SEMANTIC TOKENS — LIGHT (neutral workspace palette) ║
34
+ ╚══════════════════════════════════════════════════════════════════════╝ */
35
+
36
+ /* background / surfaces */
37
+ --bg: #f6f8f7;
38
+ --bg-soft: #eef3f2;
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);
46
+ --modal: rgba(255, 255, 255, 0.98);
47
+ --table: rgba(255, 255, 255, 0.78);
48
+ --input: rgba(255, 255, 255, 0.86);
49
+ --overlay: rgba(15, 23, 42, 0.44);
50
+ --overlay-scrim: var(--overlay);
51
+ --app-bg:
52
+ linear-gradient(180deg, #f8faf9 0%, #eef4f2 54%, #f7faf9 100%);
53
+
54
+ /* text */
55
+ --text: #24223d;
56
+ --muted: #475569; /* muted-text */
57
+ --faint: #70818f;
58
+ --text-muted: var(--muted);
59
+
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);
65
+
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;
73
+
74
+ /* status */
75
+ --success: #0f8f6d;
76
+ --warning: #a16207;
77
+ --danger: #c2410c;
78
+
79
+ /* graph */
80
+ --graph-bg: #f8faf9;
81
+ --graph-grid: rgba(12, 92, 115, 0.08);
82
+ --graph-pill: rgba(255, 255, 255, 0.92);
83
+
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);
87
+
88
+ /* focus ring */
89
+ --focus-ring: rgba(37, 99, 235, 0.55);
90
+
91
+ /* ── 구조 토큰(색 아님) ─────────────────────────────────────────────── */
92
+ --radius: 14px;
93
+ --radius-sm: 8px;
94
+ --content-width: 900px;
95
+
96
+ /* ── 레거시/별칭 — 코드가 참조하는 모든 변수를 의미 토큰으로 매핑 ──────── */
97
+ --ink: var(--text);
98
+ --blue: var(--accent);
99
+ --green: var(--success);
100
+ --amber: var(--warning);
101
+ --red: var(--danger);
102
+ --pink: var(--accent-pink);
103
+ --ok: var(--success);
104
+ --warn: var(--warning);
105
+ --err: var(--danger);
106
+ --panel: var(--surface);
107
+ --panel-2: var(--surface-2);
108
+ --panel-3: var(--surface-3);
109
+ --panel-strong: var(--surface);
110
+ --glow-green: 0 0 34px rgba(15, 159, 143, 0.14);
111
+ --glow-blue: 0 0 34px rgba(37, 99, 235, 0.14);
112
+ /* PPT reference 스킨 별칭 */
113
+ --ref-purple: var(--accent);
114
+ --ref-purple-2: var(--accent-2);
115
+ --ref-indigo: #2563eb;
116
+ --ref-ink: var(--text);
117
+ --ref-muted: var(--muted);
118
+ --ref-faint: var(--faint);
119
+ --ref-line: var(--border);
120
+ --ref-soft: var(--surface);
121
+ --ref-card: var(--card);
122
+ --ref-shadow: var(--shadow);
123
+
124
+ /* ── --lt-* 별칭 (workspace.css / platform.css 가 참조) — LIGHT ──────────
125
+ 순환참조를 피하려 구체값으로 둔다(소비자가 --bg 등을 --lt-* 로 재정의해도 안전). */
126
+ --lt-bg: #f6f8f7;
127
+ --lt-surface: #ffffff;
128
+ --lt-surface-2: #f0f5f4;
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);
137
+
138
+ /* ── 모션 / 타이포 ─────────────────────────────────────────────────── */
139
+ --lt-motion-ease: cubic-bezier(0.22, 1, 0.36, 1);
140
+ }
141
+
142
+ /* ╔════════════════════════════════════════════════════════════════════════╗
143
+ ║ SEMANTIC TOKENS — DARK (값만 교체, 같은 토큰명) ║
144
+ ╚════════════════════════════════════════════════════════════════════════╝ */
145
+ :root[data-lt-theme="dark"] {
146
+ color-scheme: dark;
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);
158
+ --table: rgba(255, 255, 255, 0.045);
159
+ --input: rgba(255, 255, 255, 0.065);
160
+ --overlay: rgba(0, 0, 0, 0.64);
161
+ --overlay-scrim: var(--overlay);
162
+ --app-bg:
163
+ linear-gradient(180deg, #101418 0%, #151d22 54%, #101418 100%);
164
+
165
+ --text: #edf3f2;
166
+ --muted: #b8c5c2;
167
+ --faint: #849894;
168
+ --text-muted: var(--muted);
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);
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;
181
+
182
+ --success: #34d399;
183
+ --warning: #fbbf24;
184
+ --danger: #fb923c;
185
+
186
+ --graph-bg: #0f1518;
187
+ --graph-grid: rgba(94, 234, 212, 0.08);
188
+ --graph-pill: rgba(27, 35, 40, 0.92);
189
+
190
+ --shadow: 0 18px 54px rgba(0, 0, 0, 0.50);
191
+ --shadow-sm: 0 8px 24px rgba(0, 0, 0, 0.42);
192
+
193
+ --focus-ring: rgba(125, 211, 252, 0.70);
194
+
195
+ --lt-bg: #101418;
196
+ --lt-surface: #1b2328;
197
+ --lt-surface-2: #202b31;
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;
205
+ --lt-shadow-md: 0 18px 54px rgba(0, 0, 0, 0.50);
206
+
207
+ --ref-indigo: #93c5fd;
208
+ --glow-green: 0 0 34px rgba(94, 234, 212, 0.18);
209
+ --glow-blue: 0 0 34px rgba(125, 211, 252, 0.18);
210
+ }
211
+
212
+ /* OS 다크 추종 — 사용자가 명시적으로 라이트를 고르지 않은 경우에만 */
213
+ @media (prefers-color-scheme: dark) {
214
+ :root:not([data-lt-theme="light"]):not([data-lt-theme="dark"]) {
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);
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);
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;
236
+ --lt-shadow-md: 0 18px 54px rgba(0,0,0,0.50);
237
+ }
238
+ }
239
+
240
+ /* ── 전역 폴리시 ───────────────────────────────────────────────────────── */
241
+ ::selection { background: var(--accent-soft); color: var(--text); }
242
+
243
+ :focus-visible {
244
+ outline: 2px solid var(--focus-ring);
245
+ outline-offset: 2px;
246
+ }
247
+
248
+ ::-webkit-scrollbar { width: 6px; height: 6px; }
249
+ ::-webkit-scrollbar-track { background: transparent; }
250
+ ::-webkit-scrollbar-thumb { background: var(--accent-soft); border-radius: 99px; }
251
+ ::-webkit-scrollbar-thumb:hover { background: var(--border-strong); }
252
+
253
+ @media (prefers-reduced-motion: reduce) {
254
+ *, *::before, *::after {
255
+ animation-duration: 0.001ms;
256
+ animation-iteration-count: 1;
257
+ transition-duration: 0.001ms;
258
+ scroll-behavior: auto;
259
+ }
260
+ }