@agentunion/kite 1.4.0 → 1.5.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 (235) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/cli.js +44 -5
  3. package/core/dependency_checker.py +250 -0
  4. package/core/env_checker.py +490 -0
  5. package/dependencies_lock.json +128 -0
  6. package/extensions/agents/assistant/server.py +33 -17
  7. package/extensions/channels/acp_channel/server.py +33 -17
  8. package/extensions/services/backup/entry.py +23 -16
  9. package/extensions/services/evol/auth_manager.py +443 -0
  10. package/extensions/services/evol/config.yaml +149 -0
  11. package/extensions/services/evol/config_loader.py +117 -0
  12. package/extensions/services/evol/entry.py +406 -0
  13. package/extensions/services/evol/evol_api.py +173 -0
  14. package/extensions/services/evol/evol_config.json5 +29 -0
  15. package/extensions/services/evol/migrate_tokens.py +122 -0
  16. package/extensions/services/evol/module.md +32 -0
  17. package/extensions/services/evol/pairing.py +250 -0
  18. package/extensions/services/evol/pairing_codes.jsonl +1 -0
  19. package/extensions/services/evol/relay.py +682 -0
  20. package/extensions/services/evol/relay_config.json5 +67 -0
  21. package/extensions/services/evol/routes/__init__.py +1 -0
  22. package/extensions/services/evol/routes/routes_management_ws.py +127 -0
  23. package/extensions/services/evol/routes/routes_rpc.py +89 -0
  24. package/extensions/services/evol/routes/routes_test.py +61 -0
  25. package/extensions/services/evol/server.py +875 -0
  26. package/extensions/services/evol/static/css/style.css +1200 -0
  27. package/extensions/services/evol/static/index.html +781 -0
  28. package/extensions/services/evol/static/index_evol.html +14 -0
  29. package/extensions/services/evol/static/js/app.js +6304 -0
  30. package/extensions/services/evol/static/js/auth.js +326 -0
  31. package/extensions/services/evol/static/js/dialog.js +285 -0
  32. package/extensions/services/evol/static/js/evol-app-fixed.js +50 -0
  33. package/extensions/services/evol/static/js/evol-app.js +1949 -0
  34. package/extensions/services/evol/static/js/evol-app.js.bak +1800 -0
  35. package/extensions/services/evol/static/js/kernel-client-example.js +228 -0
  36. package/extensions/services/evol/static/js/kernel-client.js +396 -0
  37. package/extensions/services/evol/static/js/main.js +141 -0
  38. package/extensions/services/evol/static/js/registry-tests.js +585 -0
  39. package/extensions/services/evol/static/js/stats.js +217 -0
  40. package/extensions/services/evol/static/js/token-manager.js +175 -0
  41. package/extensions/services/evol/static/pairing.html +248 -0
  42. package/extensions/services/evol/static/test_registry.html +262 -0
  43. package/extensions/services/evol/static/test_relay.html +462 -0
  44. package/extensions/services/evol/stats_manager.py +240 -0
  45. package/extensions/services/model_service/entry.py +23 -1
  46. package/extensions/services/proxy/.claude/settings.local.json +13 -0
  47. package/extensions/services/proxy/CHANGELOG_20260308.md +258 -0
  48. package/extensions/services/proxy/_fix_prints.py +133 -0
  49. package/extensions/services/proxy/_fix_prints2.py +87 -0
  50. package/extensions/services/proxy/agentcp/LICENCE +178 -0
  51. package/extensions/services/proxy/agentcp/README copy.md +85 -0
  52. package/extensions/services/proxy/agentcp/README.md +260 -0
  53. package/extensions/services/proxy/agentcp/__init__.py +16 -0
  54. package/extensions/services/proxy/agentcp/agent.py +4 -0
  55. package/extensions/services/proxy/agentcp/agentcp.py +2494 -0
  56. package/extensions/services/proxy/agentcp/agentprofile.json +89 -0
  57. package/extensions/services/proxy/agentcp/ap/__init__.py +16 -0
  58. package/extensions/services/proxy/agentcp/ap/ap_client.py +316 -0
  59. package/extensions/services/proxy/agentcp/assets/images/wechat_qr.png +0 -0
  60. package/extensions/services/proxy/agentcp/backup/metrics.json +31 -0
  61. package/extensions/services/proxy/agentcp/base/__init__.py +20 -0
  62. package/extensions/services/proxy/agentcp/base/auth_client.py +257 -0
  63. package/extensions/services/proxy/agentcp/base/client.py +112 -0
  64. package/extensions/services/proxy/agentcp/base/env.py +34 -0
  65. package/extensions/services/proxy/agentcp/base/html_util.py +336 -0
  66. package/extensions/services/proxy/agentcp/base/log.py +98 -0
  67. package/extensions/services/proxy/agentcp/ca/__init__.py +17 -0
  68. package/extensions/services/proxy/agentcp/ca/ca_client.py +414 -0
  69. package/extensions/services/proxy/agentcp/ca/ca_root.py +74 -0
  70. package/extensions/services/proxy/agentcp/context/__init__.py +20 -0
  71. package/extensions/services/proxy/agentcp/context/context.py +73 -0
  72. package/extensions/services/proxy/agentcp/context/exceptions.py +114 -0
  73. package/extensions/services/proxy/agentcp/create_profile.py +125 -0
  74. package/extensions/services/proxy/agentcp/create_profile_weather.py +125 -0
  75. package/extensions/services/proxy/agentcp/db/__init__.py +15 -0
  76. package/extensions/services/proxy/agentcp/db/db_mananger.py +550 -0
  77. package/extensions/services/proxy/agentcp/docs/UDP_HEARTBEAT_FIX_REPORT.md +265 -0
  78. package/extensions/services/proxy/agentcp/docs/heartbeat_issue_analysis.md +291 -0
  79. package/extensions/services/proxy/agentcp/file/__init__.py +16 -0
  80. package/extensions/services/proxy/agentcp/file/file_client.py +141 -0
  81. package/extensions/services/proxy/agentcp/file/wss_binary_message.py +137 -0
  82. package/extensions/services/proxy/agentcp/hcp.py +299 -0
  83. package/extensions/services/proxy/agentcp/heartbeat/__init__.py +16 -0
  84. package/extensions/services/proxy/agentcp/heartbeat/heartbeat_client.py +360 -0
  85. package/extensions/services/proxy/agentcp/improved_scheduler.py +498 -0
  86. package/extensions/services/proxy/agentcp/llm_agent_utils.py +249 -0
  87. package/extensions/services/proxy/agentcp/llm_server.py +172 -0
  88. package/extensions/services/proxy/agentcp/mermaid.py +210 -0
  89. package/extensions/services/proxy/agentcp/message.py +149 -0
  90. package/extensions/services/proxy/agentcp/metrics.py +256 -0
  91. package/extensions/services/proxy/agentcp/monitoring/__init__.py +20 -0
  92. package/extensions/services/proxy/agentcp/monitoring/global_monitor.py +27 -0
  93. package/extensions/services/proxy/agentcp/monitoring/metrics_store.py +325 -0
  94. package/extensions/services/proxy/agentcp/monitoring/monitoring_service.py +269 -0
  95. package/extensions/services/proxy/agentcp/monitoring/sliding_window.py +222 -0
  96. package/extensions/services/proxy/agentcp/monitoring/standalone_reader.py +224 -0
  97. package/extensions/services/proxy/agentcp/msg/__init__.py +21 -0
  98. package/extensions/services/proxy/agentcp/msg/connection_manager.py +456 -0
  99. package/extensions/services/proxy/agentcp/msg/message_client.py +2058 -0
  100. package/extensions/services/proxy/agentcp/msg/message_serialize.py +263 -0
  101. package/extensions/services/proxy/agentcp/msg/open_ai_message.py +88 -0
  102. package/extensions/services/proxy/agentcp/msg/session_manager.py +1062 -0
  103. package/extensions/services/proxy/agentcp/msg/stream_client.py +267 -0
  104. package/extensions/services/proxy/agentcp/msg/websocket_file_receiver.py +89 -0
  105. package/extensions/services/proxy/agentcp/msg/ws_logger.py +685 -0
  106. package/extensions/services/proxy/agentcp/msg/wss_binary_message.py +137 -0
  107. package/extensions/services/proxy/agentcp/requirements.txt +7 -0
  108. package/extensions/services/proxy/agentcp/samples/agent_graph/README.md +37 -0
  109. package/extensions/services/proxy/agentcp/samples/agent_graph/agentprofile.json +89 -0
  110. package/extensions/services/proxy/agentcp/samples/agent_graph/create_profile.py +138 -0
  111. package/extensions/services/proxy/agentcp/samples/agent_graph/main.py +164 -0
  112. package/extensions/services/proxy/agentcp/samples/agent_use/create_profile.py +123 -0
  113. package/extensions/services/proxy/agentcp/samples/agent_use/llm/create_profile.py +129 -0
  114. package/extensions/services/proxy/agentcp/samples/agent_use/llm/env.json +5 -0
  115. package/extensions/services/proxy/agentcp/samples/agent_use/llm/main.py +146 -0
  116. package/extensions/services/proxy/agentcp/samples/agent_use/main.py +123 -0
  117. package/extensions/services/proxy/agentcp/samples/agent_use/readme.md +379 -0
  118. package/extensions/services/proxy/agentcp/samples/agent_use/search/create_profile.py +129 -0
  119. package/extensions/services/proxy/agentcp/samples/agent_use/search/main.py +28 -0
  120. package/extensions/services/proxy/agentcp/samples/agent_use/tool/create_profile.py +129 -0
  121. package/extensions/services/proxy/agentcp/samples/agent_use/tool/main.py +20 -0
  122. package/extensions/services/proxy/agentcp/samples/ali_amap/README.md +97 -0
  123. package/extensions/services/proxy/agentcp/samples/ali_amap/amap_agent.py +88 -0
  124. package/extensions/services/proxy/agentcp/samples/ali_amap/create_profile.py +125 -0
  125. package/extensions/services/proxy/agentcp/samples/compute_agent/agent/powershell.py +228 -0
  126. package/extensions/services/proxy/agentcp/samples/compute_agent/agent/software.py +63 -0
  127. package/extensions/services/proxy/agentcp/samples/compute_agent/agent/tools.py +36 -0
  128. package/extensions/services/proxy/agentcp/samples/compute_agent/browser_user.py +41 -0
  129. package/extensions/services/proxy/agentcp/samples/deepseek/README.md +79 -0
  130. package/extensions/services/proxy/agentcp/samples/deepseek/create_profile.py +126 -0
  131. package/extensions/services/proxy/agentcp/samples/deepseek/deepseek.py +42 -0
  132. package/extensions/services/proxy/agentcp/samples/dify_chat/README.md +78 -0
  133. package/extensions/services/proxy/agentcp/samples/dify_chat/create_profile.py +126 -0
  134. package/extensions/services/proxy/agentcp/samples/dify_chat/dify_chat.py +47 -0
  135. package/extensions/services/proxy/agentcp/samples/dify_workflow/README.md +78 -0
  136. package/extensions/services/proxy/agentcp/samples/dify_workflow/create_profile.py +126 -0
  137. package/extensions/services/proxy/agentcp/samples/dify_workflow/dify_workflow.py +46 -0
  138. package/extensions/services/proxy/agentcp/samples/executor/README.md +44 -0
  139. package/extensions/services/proxy/agentcp/samples/executor/agentprofile.json +89 -0
  140. package/extensions/services/proxy/agentcp/samples/executor/create_profile.py +139 -0
  141. package/extensions/services/proxy/agentcp/samples/executor/main.py +160 -0
  142. package/extensions/services/proxy/agentcp/samples/filereader/README.md +45 -0
  143. package/extensions/services/proxy/agentcp/samples/filereader/agentprofile.json +90 -0
  144. package/extensions/services/proxy/agentcp/samples/filereader/create_profile.py +137 -0
  145. package/extensions/services/proxy/agentcp/samples/filereader/main.py +253 -0
  146. package/extensions/services/proxy/agentcp/samples/filewriter/README.md +38 -0
  147. package/extensions/services/proxy/agentcp/samples/filewriter/agentprofile.json +91 -0
  148. package/extensions/services/proxy/agentcp/samples/filewriter/create_profile.py +138 -0
  149. package/extensions/services/proxy/agentcp/samples/filewriter/main.py +289 -0
  150. package/extensions/services/proxy/agentcp/samples/hcp/README.md +85 -0
  151. package/extensions/services/proxy/agentcp/samples/hcp/acp_weather_agent.zip +0 -0
  152. package/extensions/services/proxy/agentcp/samples/hcp/create_profile.py +125 -0
  153. package/extensions/services/proxy/agentcp/samples/hcp/hcp.py +237 -0
  154. package/extensions/services/proxy/agentcp/samples/helloworld/README.md +68 -0
  155. package/extensions/services/proxy/agentcp/samples/helloworld/hello_world.py +40 -0
  156. package/extensions/services/proxy/agentcp/samples/llm_agent/MEADME.md +117 -0
  157. package/extensions/services/proxy/agentcp/samples/llm_agent/create_profile.py +125 -0
  158. package/extensions/services/proxy/agentcp/samples/llm_agent/qwen_agent.py +136 -0
  159. package/extensions/services/proxy/agentcp/samples/local_llm_agent/README.md +90 -0
  160. package/extensions/services/proxy/agentcp/samples/local_llm_agent/create_profile.py +125 -0
  161. package/extensions/services/proxy/agentcp/samples/local_llm_agent/main.py +49 -0
  162. package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/README.md +55 -0
  163. package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/create_profile.py +125 -0
  164. package/extensions/services/proxy/agentcp/samples/query_llm_from_agent/main.py +23 -0
  165. package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/README.md +103 -0
  166. package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/create_profile.py +125 -0
  167. package/extensions/services/proxy/agentcp/samples/query_weather_api_agent/main.py +69 -0
  168. package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/README.md +58 -0
  169. package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/create_profile.py +125 -0
  170. package/extensions/services/proxy/agentcp/samples/query_weather_from_agent/main.py +25 -0
  171. package/extensions/services/proxy/agentcp/samples/qwen3/README.md +71 -0
  172. package/extensions/services/proxy/agentcp/samples/qwen3/create_profile.py +126 -0
  173. package/extensions/services/proxy/agentcp/samples/qwen3/qwen3.py +37 -0
  174. package/extensions/services/proxy/agentcp/samples/qwen3_tools/README.md +133 -0
  175. package/extensions/services/proxy/agentcp/samples/qwen3_tools/create_profile.py +126 -0
  176. package/extensions/services/proxy/agentcp/samples/qwen3_tools/qwen3_tools.py +98 -0
  177. package/extensions/services/proxy/agentcp/samples/search/create_profile_qwen.py +125 -0
  178. package/extensions/services/proxy/agentcp/samples/search/create_profile_search.py +125 -0
  179. package/extensions/services/proxy/agentcp/samples/search/qwen_agent.py +136 -0
  180. package/extensions/services/proxy/agentcp/samples/search/search_agent.py +170 -0
  181. package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/README.md +89 -0
  182. package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/create_profile.py +125 -0
  183. package/extensions/services/proxy/agentcp/samples/wrapper_agently_to_agent/main.py +44 -0
  184. package/extensions/services/proxy/agentcp/utils/__init__.py +15 -0
  185. package/extensions/services/proxy/agentcp/utils/file_util.py +117 -0
  186. package/extensions/services/proxy/agentcp/utils/proxy_bypass.py +99 -0
  187. package/extensions/services/proxy/agentcp/workflow.py +203 -0
  188. package/extensions/services/proxy/console_auth.py +109 -0
  189. package/extensions/services/proxy/evol/__init__.py +1 -0
  190. package/extensions/services/proxy/evol/config.py +37 -0
  191. package/extensions/services/proxy/evol/http/__init__.py +1 -0
  192. package/extensions/services/proxy/evol/http/async_http.py +551 -0
  193. package/extensions/services/proxy/evol/log.py +28 -0
  194. package/extensions/services/proxy/evol/presenter/__init__.py +2 -0
  195. package/extensions/services/proxy/evol/presenter/agentIdPresenter.py +1031 -0
  196. package/extensions/services/proxy/evol/presenter/apikeyPresenter.py +106 -0
  197. package/extensions/services/proxy/evol/presenter/configPresenter.py +1281 -0
  198. package/extensions/services/proxy/evol/presenter/userPresenter.py +477 -0
  199. package/extensions/services/proxy/evol/server/__init__.py +1 -0
  200. package/extensions/services/proxy/evol/server/claude_proxy_async.py +3430 -0
  201. package/extensions/services/proxy/evol/server/openclaw_proxy.py +1861 -0
  202. package/extensions/services/proxy/evol/server/proxy_config.py +15 -0
  203. package/extensions/services/proxy/evol/server/proxy_engine.py +501 -0
  204. package/extensions/services/proxy/evol/version.py +24 -0
  205. package/extensions/services/proxy/logs/websocket.log +260 -0
  206. package/extensions/services/proxy/main.py +240 -0
  207. package/extensions/services/proxy/requirements.txt +13 -0
  208. package/extensions/services/proxy/server.py +271 -0
  209. package/extensions/services/watchdog/entry.py +42 -16
  210. package/extensions/services/watchdog/module.md +1 -0
  211. package/extensions/services/watchdog/monitor.py +34 -4
  212. package/extensions/services/web/module.md +1 -1
  213. package/extensions/services/web/server.py +30 -18
  214. package/extensions/services/web/static/js/token-manager.js +10 -10
  215. package/kernel/entry.py +1 -1
  216. package/kernel/module.md +25 -1
  217. package/kernel/registry_store.py +2 -26
  218. package/kernel/rpc_router.py +36 -10
  219. package/kernel/server.py +106 -17
  220. package/kite_cli/commands/deps_install.py +67 -0
  221. package/kite_cli/commands/env_check.py +45 -0
  222. package/kite_cli/commands/prepare.py +49 -0
  223. package/kite_cli/commands/venv_setup.py +56 -0
  224. package/kite_cli/main.py +29 -1
  225. package/launcher/entry.py +306 -21
  226. package/launcher/module.md +9 -0
  227. package/launcher/module_scanner.py +11 -1
  228. package/main.py +4 -1
  229. package/package.json +8 -1
  230. package/python_version.json +4 -0
  231. package/requirements.txt +38 -0
  232. package/scripts/env-manager.js +328 -0
  233. package/scripts/python-env.js +79 -0
  234. package/scripts/scan_dependencies.py +461 -0
  235. package/scripts/setup-python-env.js +191 -0
@@ -0,0 +1,1200 @@
1
+ /* AI Phone Agent — Dashboard Stylesheet */
2
+
3
+ :root {
4
+ --sidebar-bg: #1e293b;
5
+ --sidebar-hover: #334155;
6
+ --sidebar-active: #0f172a;
7
+ --sidebar-text: #94a3b8;
8
+ --sidebar-text-active: #f1f5f9;
9
+ --primary: #3b82f6;
10
+ --primary-hover: #2563eb;
11
+ --primary-light: #dbeafe;
12
+ --success: #22c55e;
13
+ --success-light: #dcfce7;
14
+ --error: #ef4444;
15
+ --error-light: #fee2e2;
16
+ --warning: #eab308;
17
+ --warning-light: #fef9c3;
18
+ --white: #ffffff;
19
+ --gray-50: #f8fafc;
20
+ --gray-100: #f1f5f9;
21
+ --gray-200: #e2e8f0;
22
+ --gray-300: #cbd5e1;
23
+ --gray-400: #94a3b8;
24
+ --gray-500: #64748b;
25
+ --gray-600: #475569;
26
+ --gray-700: #334155;
27
+ --gray-800: #1e293b;
28
+ --gray-900: #0f172a;
29
+ --sidebar-width: 240px;
30
+ --header-height: 56px;
31
+ --statusbar-height: 32px;
32
+ --radius: 8px;
33
+ --shadow: 0 1px 3px rgba(0,0,0,.1), 0 1px 2px rgba(0,0,0,.06);
34
+ --shadow-md: 0 4px 6px rgba(0,0,0,.1);
35
+ }
36
+
37
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
38
+
39
+ html, body {
40
+ height: 100%;
41
+ font-family: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
42
+ font-size: 14px;
43
+ color: var(--gray-800);
44
+ background: var(--gray-50);
45
+ }
46
+
47
+ /* ===== HEADER ===== */
48
+ #app-header {
49
+ position: fixed; top: 0; left: 0; right: 0;
50
+ height: var(--header-height);
51
+ background: var(--white);
52
+ border-bottom: 1px solid var(--gray-200);
53
+ display: flex; align-items: center; justify-content: space-between;
54
+ padding: 0 20px 0 0;
55
+ z-index: 100;
56
+ box-shadow: var(--shadow);
57
+ }
58
+ .header-left {
59
+ display: flex; align-items: center; gap: 12px;
60
+ padding-left: 16px;
61
+ }
62
+ .header-title {
63
+ font-size: 18px; font-weight: 600; color: var(--gray-900);
64
+ }
65
+ .header-right { display: flex; align-items: center; gap: 12px; }
66
+ .icon-btn {
67
+ background: none; border: none; font-size: 20px; cursor: pointer;
68
+ color: var(--gray-500); padding: 4px 8px; border-radius: 4px;
69
+ }
70
+ .icon-btn:hover { background: var(--gray-100); }
71
+ .bt-indicator {
72
+ display: flex; align-items: center; gap: 6px;
73
+ padding: 4px 12px; border-radius: 20px; font-size: 12px;
74
+ background: var(--error-light); color: var(--error);
75
+ }
76
+ .bt-indicator.connected { background: var(--success-light); color: var(--success); }
77
+ .bt-icon { font-weight: 700; }
78
+
79
+ /* ===== LAYOUT ===== */
80
+ #app-layout {
81
+ display: flex;
82
+ padding-top: var(--header-height);
83
+ min-height: calc(100vh - var(--statusbar-height));
84
+ }
85
+
86
+ /* ===== SIDEBAR ===== */
87
+ #sidebar {
88
+ position: fixed; top: var(--header-height); bottom: var(--statusbar-height);
89
+ left: 0; width: var(--sidebar-width);
90
+ background: var(--sidebar-bg);
91
+ overflow-y: auto; z-index: 90;
92
+ }
93
+ .nav-list { list-style: none; padding: 12px 0; }
94
+ .nav-item {
95
+ display: flex; flex-direction: column;
96
+ padding: 12px 20px;
97
+ cursor: pointer;
98
+ border-left: 3px solid transparent;
99
+ color: var(--sidebar-text);
100
+ transition: all .15s;
101
+ }
102
+ .nav-item:hover {
103
+ background: var(--sidebar-hover);
104
+ color: var(--sidebar-text-active);
105
+ }
106
+ .nav-item.active {
107
+ background: var(--sidebar-active);
108
+ border-left-color: var(--primary);
109
+ color: var(--sidebar-text-active);
110
+ }
111
+ .nav-label { font-size: 14px; font-weight: 500; }
112
+ .nav-sublabel { font-size: 11px; opacity: .6; margin-top: 2px; }
113
+
114
+ /* ===== MAIN CONTENT ===== */
115
+ #main-content {
116
+ margin-left: var(--sidebar-width);
117
+ flex: 1;
118
+ padding: 24px;
119
+ padding-bottom: calc(var(--statusbar-height) + 24px);
120
+ min-height: calc(100vh - var(--header-height) - var(--statusbar-height));
121
+ }
122
+
123
+ /* ===== PAGES ===== */
124
+ .page { display: none; }
125
+ .page.active { display: block; }
126
+
127
+ .page-title {
128
+ font-size: 22px; font-weight: 600; color: var(--gray-900);
129
+ margin-bottom: 20px;
130
+ }
131
+
132
+ .page-actions {
133
+ display: flex; align-items: center; gap: 10px;
134
+ margin-bottom: 16px; flex-wrap: wrap;
135
+ }
136
+
137
+ /* ===== STATUS BAR ===== */
138
+ #status-bar {
139
+ position: fixed; bottom: 0; left: 0; right: 0;
140
+ height: var(--statusbar-height);
141
+ background: var(--gray-100);
142
+ border-top: 1px solid var(--gray-200);
143
+ display: flex; align-items: center;
144
+ padding: 0 20px 0 calc(var(--sidebar-width) + 20px);
145
+ gap: 24px; font-size: 12px; color: var(--gray-500);
146
+ z-index: 90;
147
+ }
148
+ .status-item { display: flex; gap: 4px; }
149
+ .status-label { font-weight: 500; }
150
+
151
+ /* ===== STAT CARDS ===== */
152
+ .stats-row {
153
+ display: grid; grid-template-columns: repeat(4, 1fr);
154
+ gap: 16px; margin-bottom: 20px;
155
+ }
156
+ .stat-card {
157
+ background: var(--white); border-radius: var(--radius);
158
+ box-shadow: var(--shadow); padding: 20px; text-align: center;
159
+ }
160
+ .stat-value {
161
+ font-size: 28px; font-weight: 700; color: var(--gray-900);
162
+ margin-bottom: 4px;
163
+ }
164
+ .stat-label { font-size: 13px; color: var(--gray-500); }
165
+
166
+ /* Stats Grid */
167
+ .stats-grid {
168
+ display: grid;
169
+ grid-template-columns: repeat(8, 1fr);
170
+ gap: 10px;
171
+ }
172
+ .stat-item {
173
+ text-align: center;
174
+ padding: 8px 6px;
175
+ border-radius: 6px;
176
+ background: var(--gray-50);
177
+ transition: background 0.2s;
178
+ cursor: pointer;
179
+ position: relative;
180
+ }
181
+ .stat-item:hover {
182
+ background: var(--gray-100);
183
+ }
184
+ .stat-item .stat-value {
185
+ font-size: 18px;
186
+ margin-bottom: 2px;
187
+ font-weight: 600;
188
+ }
189
+ .stat-item .stat-label {
190
+ font-size: 10px;
191
+ }
192
+
193
+ /* Stat Tooltip */
194
+ .stat-tooltip {
195
+ position: fixed;
196
+ display: none;
197
+ background: #2d2d2d;
198
+ color: #e0e0e0;
199
+ padding: 12px;
200
+ border-radius: 6px;
201
+ box-shadow: 0 4px 12px rgba(0,0,0,0.3);
202
+ font-size: 12px;
203
+ font-family: monospace;
204
+ max-width: 400px;
205
+ max-height: 300px;
206
+ overflow-y: auto;
207
+ z-index: 10000;
208
+ pointer-events: none;
209
+ white-space: pre-wrap;
210
+ line-height: 1.5;
211
+ }
212
+ .stat-tooltip.show {
213
+ display: block;
214
+ }
215
+
216
+ /* ===== PANELS ===== */
217
+ .panel {
218
+ background: var(--white); border-radius: var(--radius);
219
+ box-shadow: var(--shadow); padding: 20px;
220
+ margin-bottom: 16px;
221
+ }
222
+ .panel-title {
223
+ font-size: 15px; font-weight: 600; color: var(--gray-700);
224
+ margin-bottom: 14px; padding-bottom: 10px;
225
+ border-bottom: 1px solid var(--gray-100);
226
+ }
227
+
228
+ /* ===== BREAKDOWN LIST ===== */
229
+ .breakdown-list { list-style: none; }
230
+ .breakdown-list li {
231
+ display: flex; justify-content: space-between; align-items: center;
232
+ padding: 8px 0; border-bottom: 1px solid var(--gray-100);
233
+ }
234
+ .breakdown-list li:last-child { border-bottom: none; }
235
+ .breakdown-label { color: var(--gray-600); }
236
+ .breakdown-value { font-weight: 600; color: var(--gray-800); }
237
+
238
+ /* ===== TABLES ===== */
239
+ .table-wrapper { overflow-x: auto; }
240
+ .data-table {
241
+ width: 100%; border-collapse: collapse; font-size: 13px;
242
+ }
243
+ .data-table th {
244
+ text-align: left; padding: 10px 12px;
245
+ background: var(--gray-50); color: var(--gray-500);
246
+ font-weight: 600; font-size: 12px; text-transform: uppercase;
247
+ letter-spacing: .03em; border-bottom: 2px solid var(--gray-200);
248
+ }
249
+ .data-table td {
250
+ padding: 10px 12px; border-bottom: 1px solid var(--gray-100);
251
+ color: var(--gray-700);
252
+ }
253
+ .data-table tbody tr:hover { background: var(--gray-50); }
254
+ .data-table .text-muted { color: var(--gray-400); font-style: italic; }
255
+
256
+ /* ===== BUTTONS ===== */
257
+ .btn {
258
+ display: inline-flex; align-items: center; justify-content: center;
259
+ padding: 8px 16px; border: none; border-radius: 6px;
260
+ font-size: 13px; font-weight: 500; cursor: pointer;
261
+ transition: all .15s; gap: 6px; white-space: nowrap;
262
+ }
263
+ .btn:disabled { background: var(--gray-200); color: var(--gray-400); cursor: not-allowed; opacity: 1; }
264
+ .btn-primary { background: var(--primary); color: var(--white); }
265
+ .btn-primary:hover:not(:disabled) { background: var(--primary-hover); }
266
+ .btn-secondary { background: var(--gray-100); color: var(--gray-700); }
267
+ .btn-secondary:hover:not(:disabled) { background: var(--gray-200); }
268
+ .btn-danger { background: var(--error); color: var(--white); }
269
+ .btn-danger:hover:not(:disabled) { background: #dc2626; }
270
+ .btn-success { background: var(--success); color: var(--white); }
271
+ .btn-success:hover:not(:disabled) { background: #16a34a; }
272
+ .btn-warning { background: #f59e0b; color: var(--white); }
273
+ .btn-warning:hover:not(:disabled) { background: #d97706; }
274
+ .btn-sm { padding: 4px 14px; font-size: 12px; min-width: 52px; }
275
+ .btn-lg { padding: 10px 24px; font-size: 15px; }
276
+
277
+ /* ===== FORMS ===== */
278
+ .form-section {
279
+ background: var(--white); border-radius: var(--radius);
280
+ box-shadow: var(--shadow); padding: 20px; margin-bottom: 16px;
281
+ }
282
+ .form-title {
283
+ font-size: 16px; font-weight: 600; color: var(--gray-800);
284
+ margin-bottom: 16px;
285
+ }
286
+ .form-group { margin-bottom: 14px; }
287
+ .form-group label {
288
+ display: block; font-size: 13px; font-weight: 500;
289
+ color: var(--gray-600); margin-bottom: 5px;
290
+ }
291
+ .form-group input,
292
+ .form-group textarea,
293
+ .form-group select {
294
+ width: 100%; padding: 8px 12px;
295
+ border: 1px solid var(--gray-300); border-radius: 6px;
296
+ font-size: 13px; color: var(--gray-800);
297
+ background: var(--white); transition: border-color .15s;
298
+ }
299
+ .form-group input:focus,
300
+ .form-group textarea:focus,
301
+ .form-group select:focus {
302
+ outline: none; border-color: var(--primary);
303
+ box-shadow: 0 0 0 3px rgba(59,130,246,.15);
304
+ }
305
+ .form-group input[type="range"] {
306
+ padding: 0; border: none; box-shadow: none;
307
+ }
308
+ .form-row {
309
+ display: grid; grid-template-columns: 1fr 1fr; gap: 16px;
310
+ }
311
+ .form-actions {
312
+ display: flex; gap: 10px; margin-top: 16px;
313
+ }
314
+ .required { color: var(--error); }
315
+ .form-hint { font-size: 11px; color: var(--gray-400); margin-top: 2px; }
316
+ .input-with-btn { display: flex; gap: 8px; }
317
+ .input-with-btn select { flex: 1; }
318
+
319
+ /* ===== CONFIG AUTO-SAVE STATUS ===== */
320
+ .config-save-status {
321
+ display: inline-block;
322
+ font-size: 12px; font-weight: 400;
323
+ color: var(--gray-400);
324
+ margin-left: 12px; vertical-align: middle;
325
+ transition: opacity .3s ease;
326
+ }
327
+ .config-save-status.saving { color: var(--primary); }
328
+ .config-save-status.saved { color: var(--success); }
329
+ .config-save-status.error { color: var(--error); }
330
+ .config-save-status.fade-out { opacity: 0; }
331
+
332
+ /* ===== BASIC INFO: Tags & Owners ===== */
333
+ .tag-list {
334
+ display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; min-height: 28px;
335
+ }
336
+ .tag-item {
337
+ display: inline-flex; align-items: center; gap: 4px;
338
+ padding: 4px 10px; border-radius: 14px; font-size: 13px;
339
+ background: var(--gray-100); border: 1px solid var(--gray-200);
340
+ cursor: pointer; transition: all .15s ease;
341
+ }
342
+ .tag-item:hover { border-color: var(--primary); }
343
+ .tag-item.active {
344
+ background: var(--primary); color: #fff; border-color: var(--primary);
345
+ }
346
+ .tag-item .tag-remove {
347
+ display: inline-flex; align-items: center; justify-content: center;
348
+ width: 16px; height: 16px; border-radius: 50%; font-size: 12px; line-height: 1;
349
+ background: rgba(0,0,0,.1); cursor: pointer; border: none; padding: 0;
350
+ color: inherit;
351
+ }
352
+ .tag-item .tag-remove:hover { background: rgba(0,0,0,.25); }
353
+ .tag-input-row {
354
+ display: flex; gap: 8px;
355
+ }
356
+ .tag-input { flex: 1; }
357
+
358
+ .badge { display: inline-block; font-size: 10px; padding: 1px 6px; border-radius: 8px; vertical-align: middle; }
359
+ .badge-primary { background: var(--primary); color: #fff; }
360
+
361
+ .owner-list {
362
+ display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px;
363
+ }
364
+ .owner-item {
365
+ display: flex; align-items: center; gap: 8px;
366
+ padding: 6px 12px; border-radius: 8px; font-size: 13px;
367
+ background: var(--gray-100); border: 1px solid var(--gray-200);
368
+ }
369
+ .owner-item .owner-phone { font-weight: 600; min-width: 120px; }
370
+ .owner-item .owner-name { color: var(--gray-500); min-width: 60px; }
371
+ .owner-item .owner-note { color: var(--gray-400); flex: 1; font-size: 12px; }
372
+ .owner-item .tag-remove { margin-left: auto; flex-shrink: 0; }
373
+
374
+ .owner-add-row {
375
+ display: flex; gap: 8px;
376
+ }
377
+ .owner-input { font-size: 13px; }
378
+ .owner-input-phone { flex: 2; }
379
+ .owner-input-name { flex: 1.5; }
380
+ .owner-input-note { flex: 2; }
381
+
382
+ /* ===== HIDDEN ===== */
383
+ .hidden { display: none !important; }
384
+
385
+ /* ===== PAGINATION ===== */
386
+ .pagination {
387
+ display: flex; align-items: center; justify-content: center;
388
+ gap: 10px; padding: 16px 0;
389
+ }
390
+ .pagination-info { font-size: 13px; color: var(--gray-500); }
391
+
392
+ /* ===== SEARCH BOX ===== */
393
+ .search-box input {
394
+ padding: 6px 12px; border: 1px solid var(--gray-300);
395
+ border-radius: 6px; font-size: 13px; width: 220px;
396
+ }
397
+ .search-box input:focus {
398
+ outline: none; border-color: var(--primary);
399
+ box-shadow: 0 0 0 3px rgba(59,130,246,.15);
400
+ }
401
+
402
+ /* ===== PROVIDER TABS ===== */
403
+ .provider-tabs {
404
+ display: flex; gap: 0; margin-bottom: 16px;
405
+ border-bottom: 2px solid var(--gray-200);
406
+ }
407
+ .provider-tab {
408
+ padding: 8px 20px; background: none; border: none;
409
+ font-size: 14px; font-weight: 500; color: var(--gray-500);
410
+ cursor: pointer; border-bottom: 2px solid transparent;
411
+ margin-bottom: -2px; transition: all .15s;
412
+ }
413
+ .provider-tab:hover { color: var(--gray-700); }
414
+ .provider-tab.active {
415
+ color: var(--primary); border-bottom-color: var(--primary);
416
+ }
417
+ .provider-config { display: none; }
418
+ .provider-config.active { display: block; }
419
+
420
+ /* ===== BLUETOOTH CONNECTION CARD ===== */
421
+ .bt-connection-card { padding: 4px 0; }
422
+ .bt-status-row {
423
+ display: flex; align-items: center; gap: 10px;
424
+ margin-bottom: 16px; font-size: 16px; font-weight: 600;
425
+ }
426
+ .bt-status-indicator {
427
+ width: 12px; height: 12px; border-radius: 50%;
428
+ background: var(--error); display: inline-block;
429
+ }
430
+ .bt-status-indicator.connected { background: var(--success); }
431
+ .bt-device-info {
432
+ display: grid; grid-template-columns: 1fr 1fr;
433
+ gap: 12px; margin-bottom: 16px;
434
+ }
435
+ .bt-info-item { display: flex; flex-direction: column; gap: 2px; }
436
+ .info-label { font-size: 12px; color: var(--gray-500); font-weight: 500; }
437
+ .info-value { font-size: 14px; color: var(--gray-800); }
438
+ .bt-actions { display: flex; gap: 10px; }
439
+
440
+ /* ===== CALL DETAIL ===== */
441
+ .call-info-grid {
442
+ display: grid; grid-template-columns: repeat(3, 1fr);
443
+ gap: 16px;
444
+ }
445
+ .call-info-item { display: flex; flex-direction: column; gap: 2px; }
446
+
447
+ /* ===== TRANSCRIPT / CHAT ===== */
448
+ .transcript-container {
449
+ max-height: 500px; overflow-y: auto;
450
+ display: flex; flex-direction: column; gap: 10px;
451
+ padding: 12px 0;
452
+ }
453
+ .chat-msg {
454
+ max-width: 75%; padding: 10px 14px; border-radius: 12px;
455
+ font-size: 13px; line-height: 1.5;
456
+ }
457
+ .chat-msg.ai {
458
+ align-self: flex-end;
459
+ background: var(--primary); color: var(--white);
460
+ border-bottom-right-radius: 4px;
461
+ }
462
+ .chat-msg.human {
463
+ align-self: flex-start;
464
+ background: var(--gray-200); color: var(--gray-800);
465
+ border-bottom-left-radius: 4px;
466
+ }
467
+ .chat-msg.system {
468
+ align-self: center; text-align: center;
469
+ background: var(--gray-100); color: var(--gray-500);
470
+ font-size: 12px; border-radius: 20px;
471
+ }
472
+ .chat-msg.mcp {
473
+ align-self: center; width: 90%;
474
+ background: var(--warning-light); color: var(--gray-800);
475
+ border: 1px solid var(--warning); font-size: 12px;
476
+ }
477
+ .chat-msg .msg-role {
478
+ font-size: 11px; font-weight: 600; opacity: .7; margin-bottom: 3px;
479
+ }
480
+ .chat-msg .msg-time {
481
+ font-size: 11px; opacity: .6; margin-top: 4px;
482
+ }
483
+ .summary-content { font-size: 14px; line-height: 1.6; color: var(--gray-700); }
484
+ .text-muted { color: var(--gray-400); }
485
+ .tool-calls-container { font-size: 13px; }
486
+
487
+ /* ===== TAGS ===== */
488
+ .tag {
489
+ display: inline-block; padding: 2px 8px; border-radius: 10px;
490
+ background: var(--primary-light); color: var(--primary);
491
+ font-size: 11px; font-weight: 500; margin: 1px 2px;
492
+ }
493
+
494
+ /* ===== BADGE ===== */
495
+ .badge {
496
+ display: inline-block; padding: 2px 8px; border-radius: 10px;
497
+ font-size: 11px; font-weight: 600;
498
+ }
499
+ .badge-success { background: var(--success-light); color: var(--success); }
500
+ .badge-error { background: var(--error-light); color: var(--error); }
501
+ .badge-warning { background: var(--warning-light); color: var(--warning); }
502
+ .badge-info { background: var(--primary-light); color: var(--primary); }
503
+ .badge-gray { background: var(--gray-100); color: var(--gray-500); }
504
+
505
+ /* ===== MODAL ===== */
506
+ .modal-overlay {
507
+ position: fixed; inset: 0;
508
+ background: rgba(0,0,0,.4); backdrop-filter: blur(2px);
509
+ display: flex; align-items: center; justify-content: center;
510
+ z-index: 200;
511
+ }
512
+ .modal {
513
+ background: var(--white); border-radius: var(--radius);
514
+ box-shadow: var(--shadow-md);
515
+ width: 90%; max-width: 560px; max-height: 85vh;
516
+ display: flex; flex-direction: column;
517
+ }
518
+ .modal-header {
519
+ display: flex; align-items: center; justify-content: space-between;
520
+ padding: 16px 20px; border-bottom: 1px solid var(--gray-200);
521
+ }
522
+ .modal-title { font-size: 16px; font-weight: 600; }
523
+ .modal-close {
524
+ background: none; border: none; font-size: 22px;
525
+ cursor: pointer; color: var(--gray-400); padding: 0 4px;
526
+ }
527
+ .modal-close:hover { color: var(--gray-700); }
528
+ .modal-body { padding: 20px; overflow-y: auto; flex: 1; }
529
+ .modal-footer {
530
+ padding: 12px 20px; border-top: 1px solid var(--gray-200);
531
+ display: flex; justify-content: flex-end; gap: 8px;
532
+ }
533
+
534
+ /* ===== CHAT TEST DIALOG ===== */
535
+ .chat-test-overlay {
536
+ position: fixed; inset: 0; z-index: 250;
537
+ background: rgba(0,0,0,.45); backdrop-filter: blur(3px);
538
+ display: flex; align-items: center; justify-content: center;
539
+ }
540
+ .chat-test-overlay.hidden { display: none; }
541
+ .chat-test-dialog {
542
+ width: 760px; max-width: 95vw; height: 82vh;
543
+ background: var(--white); border-radius: 12px;
544
+ box-shadow: 0 20px 60px rgba(0,0,0,.25);
545
+ display: flex; flex-direction: column; overflow: hidden;
546
+ }
547
+ .chat-test-header {
548
+ padding: 14px 20px; border-bottom: 1px solid var(--gray-200);
549
+ display: flex; align-items: center; justify-content: space-between;
550
+ flex-shrink: 0;
551
+ }
552
+ .chat-test-header h3 { margin: 0; font-size: 16px; font-weight: 600; }
553
+ .chat-test-controls {
554
+ display: flex; align-items: center; gap: 8px;
555
+ }
556
+ .chat-test-select {
557
+ padding: 5px 10px; border: 1px solid var(--gray-300); border-radius: 6px;
558
+ font-size: 13px; background: var(--white); color: var(--gray-800);
559
+ }
560
+ .chat-test-model-select { max-width: 220px; }
561
+ .chat-test-close {
562
+ background: none; border: none; font-size: 22px; cursor: pointer;
563
+ color: var(--gray-400); padding: 0 4px; line-height: 1;
564
+ }
565
+ .chat-test-close:hover { color: var(--gray-700); }
566
+
567
+ .chat-test-messages {
568
+ flex: 1; overflow-y: auto; padding: 20px;
569
+ display: flex; flex-direction: column; gap: 12px;
570
+ background: var(--gray-50);
571
+ }
572
+ .chat-test-empty {
573
+ text-align: center; color: var(--gray-400); padding: 60px 0; font-size: 14px;
574
+ }
575
+
576
+ .chat-test-bubble {
577
+ max-width: 80%; padding: 10px 14px; border-radius: 12px;
578
+ font-size: 14px; line-height: 1.6; white-space: pre-wrap; word-break: break-word;
579
+ }
580
+ .chat-test-bubble.user {
581
+ align-self: flex-end;
582
+ background: var(--primary); color: var(--white);
583
+ border-bottom-right-radius: 4px;
584
+ }
585
+ .chat-test-bubble.assistant {
586
+ align-self: flex-start;
587
+ background: var(--white); color: var(--gray-800);
588
+ border: 1px solid var(--gray-200);
589
+ border-bottom-left-radius: 4px;
590
+ }
591
+ .chat-test-bubble.error {
592
+ align-self: center;
593
+ background: var(--danger-light, #fef2f2); color: var(--danger, #ef4444);
594
+ border: 1px solid var(--danger, #ef4444); font-size: 13px;
595
+ }
596
+ .chat-test-bubble.thinking {
597
+ align-self: flex-start;
598
+ background: var(--white); color: var(--gray-400);
599
+ border: 1px solid var(--gray-200);
600
+ border-bottom-left-radius: 4px;
601
+ font-style: italic;
602
+ }
603
+
604
+ .chat-test-footer {
605
+ padding: 14px 20px; border-top: 1px solid var(--gray-200);
606
+ display: flex; gap: 10px; align-items: flex-end;
607
+ flex-shrink: 0; background: var(--white);
608
+ }
609
+ .chat-test-input {
610
+ flex: 1; resize: none; border: 1px solid var(--gray-300); border-radius: 8px;
611
+ padding: 10px 14px; font-size: 14px; font-family: inherit;
612
+ line-height: 1.5; max-height: 120px; overflow-y: auto;
613
+ }
614
+ .chat-test-input:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px rgba(79,70,229,.1); }
615
+
616
+ /* ===== TOAST ===== */
617
+ #toast-container {
618
+ position: fixed; top: 70px; right: 20px;
619
+ display: flex; flex-direction: column; gap: 8px;
620
+ z-index: 300; pointer-events: none;
621
+ }
622
+ .toast {
623
+ background: var(--white); border-radius: 8px;
624
+ box-shadow: var(--shadow-md); padding: 12px 16px;
625
+ font-size: 13px; min-width: 260px; max-width: 380px;
626
+ border-left: 4px solid var(--gray-400);
627
+ pointer-events: auto;
628
+ animation: toastIn .25s ease-out;
629
+ }
630
+ .toast.success { border-left-color: var(--success); }
631
+ .toast.error { border-left-color: var(--error); }
632
+ .toast.info { border-left-color: var(--primary); }
633
+ @keyframes toastIn { from { opacity:0; transform: translateX(40px); } to { opacity:1; transform: translateX(0); } }
634
+
635
+ /* ===== AUDIO PLAYER ===== */
636
+ #detail-audio { width: 100%; margin-top: 8px; }
637
+
638
+ /* ===== ASR TEST DIALOG ===== */
639
+ .asr-test-overlay {
640
+ position: fixed; inset: 0; z-index: 250;
641
+ background: rgba(0,0,0,.45); backdrop-filter: blur(3px);
642
+ display: flex; align-items: center; justify-content: center;
643
+ }
644
+ .asr-test-overlay.hidden { display: none; }
645
+ .asr-test-dialog {
646
+ width: 520px; max-width: 95vw; max-height: 90vh;
647
+ background: var(--white); border-radius: 12px;
648
+ box-shadow: 0 20px 60px rgba(0,0,0,.25);
649
+ display: flex; flex-direction: column; overflow: hidden;
650
+ }
651
+ .asr-test-header {
652
+ padding: 14px 20px; border-bottom: 1px solid var(--gray-200);
653
+ display: flex; align-items: center; justify-content: space-between;
654
+ flex-shrink: 0;
655
+ }
656
+ .asr-test-header h3 { margin: 0; font-size: 16px; font-weight: 600; }
657
+ .asr-test-close {
658
+ background: none; border: none; font-size: 22px; cursor: pointer;
659
+ color: var(--gray-400); padding: 0 4px; line-height: 1;
660
+ }
661
+ .asr-test-close:hover { color: var(--gray-700); }
662
+ .asr-test-body {
663
+ padding: 24px 20px; min-height: 200px;
664
+ display: flex; flex-direction: column; gap: 20px;
665
+ overflow-y: auto; flex: 1 1 auto;
666
+ }
667
+ .asr-test-status-area { text-align: center; }
668
+ .asr-test-status-text { font-size: 14px; color: var(--gray-500); }
669
+ .asr-test-status-text.recording {
670
+ color: var(--error);
671
+ animation: pulse 1.5s infinite;
672
+ }
673
+ @keyframes pulse {
674
+ 0%, 100% { opacity: 1; }
675
+ 50% { opacity: .5; }
676
+ }
677
+ .asr-test-result-area {
678
+ background: var(--gray-50); border-radius: 8px;
679
+ padding: 16px; min-height: 80px; max-height: 200px;
680
+ overflow-y: auto;
681
+ }
682
+ .asr-test-result-label {
683
+ font-size: 12px; font-weight: 500; color: var(--gray-500);
684
+ margin-bottom: 8px;
685
+ }
686
+ .asr-test-result-text {
687
+ font-size: 16px; line-height: 1.6; color: var(--gray-800);
688
+ white-space: pre-wrap; word-break: break-word; min-height: 40px;
689
+ }
690
+ .asr-test-footer {
691
+ padding: 14px 20px; border-top: 1px solid var(--gray-200);
692
+ display: flex; justify-content: center; gap: 12px;
693
+ flex-shrink: 0;
694
+ }
695
+ .asr-test-stats {
696
+ display: flex; justify-content: center; gap: 16px;
697
+ font-size: 12px; color: var(--gray-500); font-variant-numeric: tabular-nums;
698
+ }
699
+ .asr-test-stats span {
700
+ background: var(--gray-100); padding: 2px 10px; border-radius: 10px;
701
+ }
702
+ .asr-test-log {
703
+ font-family: monospace; font-size: 11px; line-height: 1.5;
704
+ color: var(--gray-600); background: var(--gray-50);
705
+ border-radius: 6px; padding: 8px 12px;
706
+ max-height: 120px; overflow-y: auto; white-space: pre-wrap;
707
+ }
708
+ .asr-test-playback {
709
+ display: flex; justify-content: center; align-items: center; gap: 10px;
710
+ }
711
+ .asr-test-play-dur { font-size: 12px; color: var(--gray-500); }
712
+
713
+ /* --- ASR Controls Bar --- */
714
+ .asr-ctrl-bar {
715
+ background: var(--gray-50); border-radius: 8px; padding: 10px 12px;
716
+ margin-bottom: 10px; display: flex; flex-direction: column; gap: 8px;
717
+ }
718
+ .asr-ctrl-row {
719
+ display: flex; align-items: center; gap: 8px;
720
+ }
721
+ .asr-ctrl-label {
722
+ font-size: 12px; color: var(--gray-500); font-weight: 500; white-space: nowrap;
723
+ }
724
+ .asr-ctrl-select {
725
+ padding: 4px 8px; border: 1px solid var(--gray-300); border-radius: 5px;
726
+ font-size: 12px; background: var(--white); color: var(--gray-800);
727
+ overflow: hidden; text-overflow: ellipsis;
728
+ }
729
+ .asr-mute-btn {
730
+ background: none; border: 1px solid var(--gray-300); border-radius: 6px;
731
+ width: 34px; height: 30px; cursor: pointer; color: var(--gray-700);
732
+ display: flex; align-items: center; justify-content: center;
733
+ transition: all .15s; flex-shrink: 0;
734
+ }
735
+ .asr-mute-btn:hover { background: var(--gray-100); }
736
+ .asr-mute-btn .mic-slash { display: none; }
737
+ .asr-mute-btn.muted {
738
+ background: var(--error-light); border-color: var(--error); color: var(--error);
739
+ }
740
+ .asr-mute-btn.muted .mic-slash { display: inline; }
741
+ .asr-mic-gain {
742
+ width: 80px; flex-shrink: 0; cursor: pointer;
743
+ }
744
+ .asr-gain-val {
745
+ font-size: 11px; color: var(--gray-500); min-width: 36px; text-align: right;
746
+ font-variant-numeric: tabular-nums;
747
+ }
748
+ .asr-agc-label {
749
+ font-size: 11px; color: var(--gray-500); white-space: nowrap; cursor: pointer;
750
+ display: flex; align-items: center; gap: 3px; margin-left: 4px;
751
+ }
752
+
753
+ /* --- ASR Status + Timer row --- */
754
+ .asr-test-status-row {
755
+ display: flex; align-items: center; justify-content: center; gap: 16px;
756
+ margin-bottom: 12px;
757
+ }
758
+ .asr-test-timer {
759
+ font-size: 20px; font-weight: 700; font-variant-numeric: tabular-nums;
760
+ color: var(--error); min-width: 56px; text-align: center;
761
+ }
762
+ .asr-test-timer:empty { display: none; }
763
+
764
+ /* --- Waveform visualization --- */
765
+ .asr-test-viz {
766
+ background: var(--gray-900); border-radius: 8px; padding: 12px;
767
+ margin-bottom: 12px;
768
+ }
769
+ #asr-test-canvas {
770
+ width: 100%; height: 100px; display: block; border-radius: 4px;
771
+ }
772
+ .asr-test-vol-row {
773
+ display: flex; align-items: center; gap: 8px; margin-top: 8px;
774
+ }
775
+ .asr-test-vol-label { font-size: 11px; color: var(--gray-400); min-width: 28px; }
776
+ .asr-test-vol-track {
777
+ flex: 1; height: 6px; background: var(--gray-700); border-radius: 3px;
778
+ overflow: hidden;
779
+ }
780
+ .asr-test-vol-bar {
781
+ height: 100%; width: 0%; border-radius: 3px;
782
+ background: linear-gradient(90deg, var(--success) 0%, var(--warning) 70%, var(--error) 100%);
783
+ transition: width 0.08s ease-out;
784
+ }
785
+ .asr-test-vol-db {
786
+ font-size: 11px; color: var(--gray-400); min-width: 50px; text-align: right;
787
+ font-variant-numeric: tabular-nums;
788
+ }
789
+
790
+ /* ===== VOICE CHAT ===== */
791
+ .vc-layout {
792
+ display: flex; gap: 16px;
793
+ height: calc(100vh - var(--header-height) - var(--statusbar-height) - 48px);
794
+ }
795
+ .vc-main {
796
+ flex: 1; display: flex; flex-direction: column; min-width: 0;
797
+ }
798
+ .vc-sidebar {
799
+ width: 260px; flex-shrink: 0; display: flex; flex-direction: column;
800
+ background: var(--white); border-radius: var(--radius);
801
+ border: 1px solid var(--gray-200); overflow: hidden;
802
+ }
803
+ .vc-sidebar-title {
804
+ padding: 10px 14px; font-size: 13px; font-weight: 600;
805
+ color: var(--gray-600); border-bottom: 1px solid var(--gray-200);
806
+ background: var(--gray-50); flex-shrink: 0;
807
+ }
808
+ .vc-log {
809
+ flex: 1; overflow-y: auto; padding: 8px 12px;
810
+ font-family: monospace; font-size: 11px; line-height: 1.6;
811
+ color: var(--gray-600); white-space: pre-wrap; word-break: break-all;
812
+ min-height: 0; max-height: 50%;
813
+ }
814
+ .vc-log-entry { margin-bottom: 2px; }
815
+ .vc-log-time { color: var(--gray-400); }
816
+ .vc-log-entry.error { color: var(--error); }
817
+ .vc-log-entry.warn { color: var(--warning, #f59e0b); }
818
+ .vc-log-entry.success { color: var(--success); }
819
+ .vc-log-entry.info { color: var(--primary); }
820
+
821
+ /* Debug records */
822
+ .vc-debug-title { cursor: default; }
823
+ .vc-debug-count {
824
+ font-weight: 400; color: var(--gray-400); font-size: 11px; margin-left: 4px;
825
+ }
826
+ .vc-debug {
827
+ flex: 1; overflow-y: auto; padding: 4px 8px;
828
+ font-family: monospace; font-size: 11px; line-height: 1.5;
829
+ }
830
+ .vc-debug-item { margin-bottom: 4px; }
831
+ .vc-debug-item summary {
832
+ cursor: pointer; padding: 3px 6px; border-radius: 4px;
833
+ color: var(--gray-600); font-size: 11px;
834
+ }
835
+ .vc-debug-item summary:hover { background: var(--gray-100); }
836
+ .vc-debug-item summary .vc-dbg-turn { font-weight: 600; color: var(--primary); }
837
+ .vc-debug-item summary .vc-dbg-time { color: var(--gray-400); margin-left: 4px; }
838
+ .vc-debug-item pre {
839
+ margin: 4px 0 0 0; padding: 6px 8px; background: var(--gray-50);
840
+ border: 1px solid var(--gray-200); border-radius: 4px;
841
+ white-space: pre-wrap; word-break: break-all; font-size: 10px;
842
+ color: var(--gray-700); max-height: 300px; overflow-y: auto;
843
+ }
844
+
845
+ .vc-status-bar {
846
+ display: flex; align-items: center; justify-content: center;
847
+ padding: 10px 0; flex-shrink: 0; gap: 16px;
848
+ }
849
+ .vc-call-timer {
850
+ font-size: 18px; font-weight: 700; color: var(--gray-500);
851
+ font-variant-numeric: tabular-nums; min-width: 50px;
852
+ }
853
+ .vc-call-timer:empty { display: none; }
854
+ .vc-status-indicator {
855
+ display: flex; align-items: center; gap: 10px;
856
+ padding: 8px 20px; border-radius: 20px;
857
+ background: var(--gray-100); font-size: 14px; font-weight: 500;
858
+ color: var(--gray-500); transition: all .3s;
859
+ }
860
+ .vc-status-indicator.listening { background: var(--success-light); color: var(--success); }
861
+ .vc-status-indicator.thinking { background: var(--primary-light); color: var(--primary); }
862
+ .vc-status-indicator.speaking { background: var(--warning-light); color: #b45309; }
863
+ .vc-pulse-dot {
864
+ width: 10px; height: 10px; border-radius: 50%;
865
+ background: var(--gray-400); display: inline-block; transition: background .3s;
866
+ }
867
+ .vc-status-indicator.listening .vc-pulse-dot { background: var(--success); animation: vcPulse 1.5s infinite; }
868
+ .vc-status-indicator.thinking .vc-pulse-dot { background: var(--primary); animation: vcPulse 1s infinite; }
869
+ .vc-status-indicator.speaking .vc-pulse-dot { background: #b45309; animation: vcPulse 0.8s infinite; }
870
+ @keyframes vcPulse {
871
+ 0%, 100% { opacity: 1; transform: scale(1); }
872
+ 50% { opacity: .4; transform: scale(1.3); }
873
+ }
874
+
875
+ /* Config info panel */
876
+ .vc-config-panel {
877
+ display: flex; gap: 16px; flex-wrap: wrap;
878
+ padding: 8px 14px; margin-bottom: 6px;
879
+ background: var(--white); border-radius: var(--radius);
880
+ border: 1px solid var(--gray-200); flex-shrink: 0; font-size: 12px;
881
+ }
882
+ .vc-config-row { display: flex; align-items: center; gap: 6px; }
883
+ .vc-config-label {
884
+ font-weight: 600; color: var(--gray-500);
885
+ padding: 1px 6px; border-radius: 4px; background: var(--gray-100);
886
+ }
887
+ .vc-config-value { color: var(--gray-700); }
888
+
889
+ /* VAD inline controls */
890
+ .vc-vad-bar {
891
+ display: flex; gap: 12px; flex-wrap: wrap;
892
+ padding: 6px 14px; margin-bottom: 6px;
893
+ background: var(--white); border-radius: var(--radius);
894
+ border: 1px solid var(--gray-200); flex-shrink: 0; font-size: 12px;
895
+ align-items: center;
896
+ }
897
+ .vc-vad-group { display: flex; align-items: center; gap: 4px; }
898
+ .vc-vad-label {
899
+ font-weight: 500; color: var(--gray-500); white-space: nowrap;
900
+ }
901
+ .vc-vad-input {
902
+ width: 70px; padding: 2px 6px; border: 1px solid var(--gray-300);
903
+ border-radius: 4px; font-size: 12px; text-align: center;
904
+ color: var(--gray-800); background: var(--white);
905
+ font-variant-numeric: tabular-nums;
906
+ }
907
+ .vc-vad-input:focus {
908
+ outline: none; border-color: var(--primary);
909
+ box-shadow: 0 0 0 2px rgba(59,130,246,.15);
910
+ }
911
+
912
+ /* Phone simulation bar */
913
+ .vc-phone-bar {
914
+ display: flex; gap: 10px; flex-wrap: wrap;
915
+ padding: 6px 14px; margin-bottom: 6px;
916
+ background: var(--white); border-radius: var(--radius);
917
+ border: 1px solid var(--gray-200); flex-shrink: 0; font-size: 12px;
918
+ align-items: center;
919
+ }
920
+ .vc-phone-group { display: flex; align-items: center; gap: 4px; }
921
+ .vc-phone-group-num { flex: 1; min-width: 120px; }
922
+ .vc-phone-group-name { flex: 1; min-width: 80px; }
923
+ .vc-phone-label { font-weight: 500; color: var(--gray-500); white-space: nowrap; }
924
+ .vc-phone-select {
925
+ padding: 2px 6px; border: 1px solid var(--gray-300); border-radius: 4px;
926
+ font-size: 12px; color: var(--gray-800); background: var(--white);
927
+ }
928
+ .vc-phone-input {
929
+ flex: 1; padding: 2px 6px; border: 1px solid var(--gray-300);
930
+ border-radius: 4px; font-size: 12px; color: var(--gray-800);
931
+ background: var(--white); min-width: 0;
932
+ }
933
+ .vc-phone-select:focus, .vc-phone-input:focus {
934
+ outline: none; border-color: var(--primary);
935
+ box-shadow: 0 0 0 2px rgba(59,130,246,.15);
936
+ }
937
+
938
+ /* System prompt */
939
+ .vc-prompt-bar { margin-bottom: 6px; flex-shrink: 0; }
940
+ .vc-prompt-label {
941
+ font-size: 12px; font-weight: 500; color: var(--gray-500);
942
+ margin-bottom: 3px; display: block;
943
+ }
944
+ .vc-prompt-input {
945
+ width: 100%; padding: 6px 10px; border: 1px solid var(--gray-300);
946
+ border-radius: 6px; font-size: 12px; color: var(--gray-800);
947
+ background: var(--white); resize: vertical; font-family: inherit; line-height: 1.5;
948
+ }
949
+ .vc-prompt-input:focus {
950
+ outline: none; border-color: var(--primary);
951
+ box-shadow: 0 0 0 3px rgba(59,130,246,.15);
952
+ }
953
+
954
+ /* Waveform */
955
+ .vc-viz {
956
+ background: var(--gray-900); border-radius: 8px; padding: 8px;
957
+ margin-bottom: 6px; flex-shrink: 0;
958
+ }
959
+ #vc-canvas { width: 100%; height: 64px; display: block; border-radius: 4px; }
960
+ .vc-vol-row { display: flex; align-items: center; gap: 8px; margin-top: 6px; }
961
+ .vc-vol-label { font-size: 11px; color: var(--gray-400); min-width: 28px; }
962
+ .vc-vol-track {
963
+ flex: 1; height: 5px; background: var(--gray-700); border-radius: 3px; overflow: hidden;
964
+ }
965
+ .vc-vol-bar {
966
+ height: 100%; width: 0%; border-radius: 3px;
967
+ background: linear-gradient(90deg, var(--success) 0%, var(--warning) 70%, var(--error) 100%);
968
+ transition: width 0.08s ease-out;
969
+ }
970
+ .vc-vol-db {
971
+ font-size: 11px; color: var(--gray-400); min-width: 50px; text-align: right;
972
+ font-variant-numeric: tabular-nums;
973
+ }
974
+
975
+ /* Messages */
976
+ .vc-messages {
977
+ flex: 1; overflow-y: auto; padding: 16px;
978
+ display: flex; flex-direction: column; gap: 12px;
979
+ background: var(--gray-50); border-radius: var(--radius);
980
+ border: 1px solid var(--gray-200);
981
+ }
982
+ .vc-empty-hint { text-align: center; color: var(--gray-400); padding: 60px 0; font-size: 14px; }
983
+
984
+ /* Chat bubbles */
985
+ .vc-bubble {
986
+ max-width: 80%; padding: 10px 14px; border-radius: 12px;
987
+ font-size: 14px; line-height: 1.6; word-break: break-word; white-space: pre-wrap;
988
+ }
989
+ .vc-bubble.user {
990
+ align-self: flex-end; background: var(--primary); color: var(--white);
991
+ border-bottom-right-radius: 4px;
992
+ }
993
+ .vc-bubble.ai {
994
+ align-self: flex-start; background: var(--white); color: var(--gray-800);
995
+ border: 1px solid var(--gray-200); border-bottom-left-radius: 4px;
996
+ }
997
+ .vc-bubble.interim {
998
+ align-self: flex-end; background: var(--gray-200); color: var(--gray-500);
999
+ border-bottom-right-radius: 4px; font-style: italic;
1000
+ }
1001
+ .vc-bubble .vc-bubble-label {
1002
+ font-size: 11px; font-weight: 600; opacity: .6;
1003
+ margin-bottom: 3px; display: flex; justify-content: space-between;
1004
+ }
1005
+ .vc-bubble .vc-bubble-time { font-weight: 400; font-size: 10px; opacity: .7; }
1006
+ /* Tool Call Card — collapsed */
1007
+ .vc-tool-card {
1008
+ align-self: flex-start; display: inline-flex; flex-direction: column;
1009
+ max-width: 85%; margin: 4px 0;
1010
+ }
1011
+ .vc-tool-card-header {
1012
+ display: inline-flex; align-items: center; gap: 8px;
1013
+ background: #f8f9fb; border: 1px solid #e5e7eb; border-radius: 22px;
1014
+ padding: 8px 16px; cursor: pointer; user-select: none;
1015
+ transition: background .15s, box-shadow .15s; font-size: 14px; color: #374151;
1016
+ }
1017
+ .vc-tool-card-header:hover { background: #f0f2f5; box-shadow: 0 1px 4px rgba(0,0,0,.06); }
1018
+ .vc-tool-card-icon {
1019
+ width: 20px; height: 20px; flex-shrink: 0; opacity: .55;
1020
+ }
1021
+ .vc-tool-card-name { font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
1022
+ .vc-tool-card-metrics { display: flex; align-items: center; gap: 8px; margin-left: auto; font-size: 12px; color: #9ca3af; white-space: nowrap; }
1023
+ .vc-tool-card-metrics .up { color: #6b7280; }
1024
+ .vc-tool-card-metrics .down { color: #6b7280; }
1025
+ .vc-tool-card-status { width: 20px; height: 20px; flex-shrink: 0; display: flex; align-items: center; justify-content: center; }
1026
+ .vc-tool-card-status .spinner {
1027
+ width: 16px; height: 16px; border: 2px solid #d1d5db; border-top-color: #6b7280;
1028
+ border-radius: 50%; animation: vcToolSpin .8s linear infinite;
1029
+ }
1030
+ @keyframes vcToolSpin { to { transform: rotate(360deg); } }
1031
+ .vc-tool-card-status .checkmark { color: #22c55e; font-size: 18px; line-height: 1; }
1032
+ .vc-tool-card-status .error-mark { color: #ef4444; font-size: 18px; line-height: 1; }
1033
+
1034
+ /* Tool Call Card — expanded detail */
1035
+ .vc-tool-detail {
1036
+ display: none; border: 1px solid #e5e7eb; border-radius: 12px;
1037
+ margin-top: 6px; background: #fff; overflow: hidden;
1038
+ box-shadow: 0 1px 6px rgba(0,0,0,.06); font-size: 13px; color: #374151;
1039
+ }
1040
+ .vc-tool-card.expanded .vc-tool-detail { display: block; }
1041
+ .vc-tool-detail-header {
1042
+ display: flex; align-items: center; justify-content: space-between;
1043
+ padding: 12px 16px; border-bottom: 1px solid #f0f0f0;
1044
+ }
1045
+ .vc-tool-detail-header .title { display: flex; align-items: center; gap: 6px; font-weight: 500; color: #6b7280; }
1046
+ .vc-tool-detail-header .title svg { width: 16px; height: 16px; opacity: .5; }
1047
+ .vc-tool-detail-copy {
1048
+ background: none; border: none; cursor: pointer; padding: 4px; color: #9ca3af; transition: color .15s;
1049
+ }
1050
+ .vc-tool-detail-copy:hover { color: #374151; }
1051
+ .vc-tool-detail-copy svg { width: 16px; height: 16px; }
1052
+ .vc-tool-detail-info { padding: 12px 16px; line-height: 1.8; color: #6b7280; font-size: 12px; }
1053
+ .vc-tool-detail-info .label { color: #3b82f6; font-weight: 500; }
1054
+ .vc-tool-detail-sep { height: 1px; background: #f0f0f0; margin: 0 16px; }
1055
+ .vc-tool-detail-section { padding: 12px 16px; }
1056
+ .vc-tool-detail-section .section-title {
1057
+ display: flex; align-items: center; gap: 6px;
1058
+ font-size: 12px; font-weight: 500; color: #6b7280; margin-bottom: 8px;
1059
+ }
1060
+ .vc-tool-detail-section .section-title .arrow-up { color: #6b7280; }
1061
+ .vc-tool-detail-section .section-title .arrow-down { color: #3b82f6; }
1062
+ .vc-tool-detail-section pre {
1063
+ background: #f8f9fb; border-radius: 6px; padding: 10px 12px;
1064
+ font-family: 'Courier New', Consolas, monospace; font-size: 12px;
1065
+ white-space: pre-wrap; word-break: break-all; max-height: 200px; overflow-y: auto;
1066
+ margin: 0; color: #374151; line-height: 1.5;
1067
+ }
1068
+
1069
+ /* Controls */
1070
+ .vc-controls {
1071
+ display: flex; justify-content: center; padding: 12px 0; flex-shrink: 0; gap: 12px;
1072
+ }
1073
+ .vc-btn-call {
1074
+ background: var(--success); color: var(--white);
1075
+ padding: 12px 40px; font-size: 16px; border-radius: 30px;
1076
+ }
1077
+ .vc-btn-call:hover:not(:disabled) { background: #16a34a; }
1078
+ .vc-btn-hangup { padding: 12px 40px; font-size: 16px; border-radius: 30px; }
1079
+
1080
+ /* ===== RESPONSIVE ===== */
1081
+ @media (max-width: 768px) {
1082
+ #sidebar { display: none; }
1083
+ #main-content { margin-left: 0; }
1084
+ #status-bar { padding-left: 20px; }
1085
+ .stats-row { grid-template-columns: 1fr 1fr; }
1086
+ .form-row { grid-template-columns: 1fr; }
1087
+ .call-info-grid { grid-template-columns: 1fr 1fr; }
1088
+ .bt-device-info { grid-template-columns: 1fr; }
1089
+ .vc-layout { flex-direction: column; }
1090
+ .vc-sidebar { width: 100%; max-height: 200px; }
1091
+ .owner-add-row { flex-wrap: wrap; }
1092
+ .owner-add-row .owner-input { min-width: 0; flex-basis: 45%; }
1093
+ }
1094
+
1095
+ /* ===== TOGGLE SWITCH ===== */
1096
+ .toggle-switch-label {
1097
+ display: inline-flex; align-items: center; gap: 6px;
1098
+ cursor: pointer; font-size: 13px; color: var(--gray-600);
1099
+ user-select: none;
1100
+ }
1101
+ .toggle-switch-label input { display: none; }
1102
+ .toggle-switch {
1103
+ position: relative; width: 36px; min-width: 36px; height: 20px;
1104
+ background: var(--gray-300); border-radius: 10px;
1105
+ transition: background .2s; display: inline-block; flex-shrink: 0;
1106
+ }
1107
+ .toggle-switch::after {
1108
+ content: ''; position: absolute; top: 2px; left: 2px;
1109
+ width: 16px; height: 16px; border-radius: 50%;
1110
+ background: var(--white); box-shadow: 0 1px 2px rgba(0,0,0,.2);
1111
+ transition: transform .2s;
1112
+ }
1113
+ .toggle-switch-label input:checked + .toggle-switch {
1114
+ background: var(--primary);
1115
+ }
1116
+ .toggle-switch-label input:checked + .toggle-switch::after {
1117
+ transform: translateX(16px);
1118
+ }
1119
+
1120
+ /* ===== MODULES PAGE ===== */
1121
+ .module-row {
1122
+ cursor: pointer;
1123
+ }
1124
+ .module-row:hover {
1125
+ background: var(--gray-50);
1126
+ }
1127
+ .module-state-select {
1128
+ padding: 4px 8px; border: 1px solid var(--gray-300); border-radius: 5px;
1129
+ font-size: 12px; background: var(--white); color: var(--gray-800);
1130
+ cursor: pointer;
1131
+ }
1132
+ .module-state-select:focus {
1133
+ outline: none; border-color: var(--primary);
1134
+ box-shadow: 0 0 0 2px rgba(59,130,246,.15);
1135
+ }
1136
+
1137
+ /* Module type badges */
1138
+ .module-type-badge {
1139
+ display: inline-block; padding: 2px 8px; border-radius: 10px;
1140
+ font-size: 11px; font-weight: 600; text-transform: lowercase;
1141
+ background: var(--gray-100); color: var(--gray-500);
1142
+ }
1143
+ .module-type-badge.type-infrastructure { background: #f3e8ff; color: #7c3aed; }
1144
+ .module-type-badge.type-service { background: var(--primary-light); color: var(--primary); }
1145
+ .module-type-badge.type-channel { background: var(--success-light); color: var(--success); }
1146
+ .module-type-badge.type-agent { background: #fff7ed; color: #ea580c; }
1147
+ .module-type-badge.type-tool { background: var(--gray-100); color: var(--gray-500); }
1148
+
1149
+ /* Module state dots */
1150
+ .module-state-dot {
1151
+ display: inline-block; width: 10px; height: 10px; border-radius: 50%;
1152
+ flex-shrink: 0; background: var(--gray-300);
1153
+ }
1154
+ .module-state-dot.enabled { background: var(--success); }
1155
+ .module-state-dot.manual { background: var(--warning); }
1156
+ .module-state-dot.disabled { background: var(--gray-300); }
1157
+
1158
+ /* Config tree */
1159
+ .config-tree-group {
1160
+ margin-bottom: 4px;
1161
+ }
1162
+ .config-tree-key {
1163
+ font-size: 13px; font-weight: 600; color: var(--gray-700);
1164
+ padding: 6px 8px; cursor: pointer; border-radius: 4px;
1165
+ user-select: none; display: flex; align-items: center; gap: 4px;
1166
+ }
1167
+ .config-tree-key::before {
1168
+ content: '\25BE'; font-size: 10px; color: var(--gray-400);
1169
+ display: inline-block; transition: transform .15s;
1170
+ }
1171
+ .config-tree-group.collapsed .config-tree-key::before {
1172
+ transform: rotate(-90deg);
1173
+ }
1174
+ .config-tree-key:hover { background: var(--gray-50); }
1175
+ .config-tree-nested {
1176
+ margin-left: 12px; padding-left: 12px;
1177
+ border-left: 2px solid var(--gray-200);
1178
+ }
1179
+ .config-tree-group.collapsed .config-tree-nested { display: none; }
1180
+ .config-tree-leaf {
1181
+ display: flex; align-items: center; gap: 10px;
1182
+ padding: 4px 8px; margin-bottom: 2px;
1183
+ }
1184
+ .config-tree-label {
1185
+ font-size: 13px; color: var(--gray-600); min-width: 140px;
1186
+ font-weight: 500; flex-shrink: 0;
1187
+ }
1188
+ .config-tree-input {
1189
+ flex: 1; padding: 5px 10px; border: 1px solid var(--gray-300);
1190
+ border-radius: 6px; font-size: 13px; color: var(--gray-800);
1191
+ background: var(--white); transition: border-color .15s;
1192
+ min-width: 0;
1193
+ }
1194
+ .config-tree-input:focus {
1195
+ outline: none; border-color: var(--primary);
1196
+ box-shadow: 0 0 0 3px rgba(59,130,246,.15);
1197
+ }
1198
+ .config-tree-checkbox {
1199
+ width: 16px; height: 16px; cursor: pointer;
1200
+ }