dsh-mobile-plugin 0.5.28 → 0.5.29

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 (350) hide show
  1. package/extensions/catalog.json +10 -1
  2. package/extensions/magpie/agentcompact.js +104 -0
  3. package/extensions/magpie/app.css +4902 -0
  4. package/extensions/magpie/app.js +24474 -0
  5. package/extensions/magpie/autobackup.js +185 -0
  6. package/extensions/magpie/autoresetown.js +151 -0
  7. package/extensions/magpie/balfound.js +38 -0
  8. package/extensions/magpie/boot.js +23 -0
  9. package/extensions/magpie/cfgblocked.js +39 -0
  10. package/extensions/magpie/checkinnow.js +111 -0
  11. package/extensions/magpie/claudemode.js +92 -0
  12. package/extensions/magpie/compat.js +41 -0
  13. package/extensions/magpie/context.css +433 -0
  14. package/extensions/magpie/context.js +1576 -0
  15. package/extensions/magpie/ctxtext.js +223 -0
  16. package/extensions/magpie/fonts.js +42 -0
  17. package/extensions/magpie/i18n.js +18942 -0
  18. package/extensions/magpie/icons/ai21.svg +1 -0
  19. package/extensions/magpie/icons/ai302-color.svg +1 -0
  20. package/extensions/magpie/icons/ai360-color.svg +1 -0
  21. package/extensions/magpie/icons/aihubmix-color.svg +1 -0
  22. package/extensions/magpie/icons/aionui.svg +1 -0
  23. package/extensions/magpie/icons/air.svg +4 -0
  24. package/extensions/magpie/icons/alibaba-color.svg +1 -0
  25. package/extensions/magpie/icons/alibabacloud-color.svg +1 -0
  26. package/extensions/magpie/icons/alma.png +0 -0
  27. package/extensions/magpie/icons/anthropic.svg +1 -0
  28. package/extensions/magpie/icons/antigravity-color.svg +1 -0
  29. package/extensions/magpie/icons/anyscale-color.svg +1 -0
  30. package/extensions/magpie/icons/app-alma.png +0 -0
  31. package/extensions/magpie/icons/app-cc-switch.png +0 -0
  32. package/extensions/magpie/icons/apple-touch-icon.png +0 -0
  33. package/extensions/magpie/icons/aside.svg +28 -0
  34. package/extensions/magpie/icons/astrbot.svg +1 -0
  35. package/extensions/magpie/icons/atomcode.png +0 -0
  36. package/extensions/magpie/icons/aws.svg +1 -0
  37. package/extensions/magpie/icons/azure-color.svg +1 -0
  38. package/extensions/magpie/icons/azureai-color.svg +1 -0
  39. package/extensions/magpie/icons/baichuan-color.svg +1 -0
  40. package/extensions/magpie/icons/baidu-color.svg +1 -0
  41. package/extensions/magpie/icons/baiducloud-color.svg +1 -0
  42. package/extensions/magpie/icons/bailian-color.svg +1 -0
  43. package/extensions/magpie/icons/baseten.svg +1 -0
  44. package/extensions/magpie/icons/bedrock-color.svg +1 -0
  45. package/extensions/magpie/icons/burncloud-color.svg +1 -0
  46. package/extensions/magpie/icons/bytedance-color.svg +1 -0
  47. package/extensions/magpie/icons/cerebras.svg +1 -0
  48. package/extensions/magpie/icons/chatglm-color.svg +1 -0
  49. package/extensions/magpie/icons/cherryin-color.svg +1 -0
  50. package/extensions/magpie/icons/chutes.svg +1 -0
  51. package/extensions/magpie/icons/cindy.png +0 -0
  52. package/extensions/magpie/icons/claude-color.svg +1 -0
  53. package/extensions/magpie/icons/claudecode-color.svg +1 -0
  54. package/extensions/magpie/icons/cline.svg +1 -0
  55. package/extensions/magpie/icons/cloudflare-color.svg +1 -0
  56. package/extensions/magpie/icons/codebuddy-color.svg +1 -0
  57. package/extensions/magpie/icons/codex-color.svg +1 -0
  58. package/extensions/magpie/icons/cohere-color.svg +1 -0
  59. package/extensions/magpie/icons/cometapi-color.svg +1 -0
  60. package/extensions/magpie/icons/commandcode.svg +1 -0
  61. package/extensions/magpie/icons/copilot-color.svg +1 -0
  62. package/extensions/magpie/icons/coze.svg +1 -0
  63. package/extensions/magpie/icons/crush.png +0 -0
  64. package/extensions/magpie/icons/curl.svg +1 -0
  65. package/extensions/magpie/icons/cursor.svg +1 -0
  66. package/extensions/magpie/icons/deepinfra-color.svg +1 -0
  67. package/extensions/magpie/icons/deepmind-color.svg +1 -0
  68. package/extensions/magpie/icons/deepseek-color.svg +1 -0
  69. package/extensions/magpie/icons/deveco-color.svg +12 -0
  70. package/extensions/magpie/icons/devin.svg +1 -0
  71. package/extensions/magpie/icons/doubao-color.svg +1 -0
  72. package/extensions/magpie/icons/empryo-color.svg +1 -0
  73. package/extensions/magpie/icons/factory.svg +1 -0
  74. package/extensions/magpie/icons/favicon.svg +1 -0
  75. package/extensions/magpie/icons/featherless-color.svg +1 -0
  76. package/extensions/magpie/icons/fireworks-color.svg +1 -0
  77. package/extensions/magpie/icons/friendli.svg +1 -0
  78. package/extensions/magpie/icons/fx.svg +1 -0
  79. package/extensions/magpie/icons/gemini-color.svg +1 -0
  80. package/extensions/magpie/icons/geminicli-color.svg +1 -0
  81. package/extensions/magpie/icons/gemma-color.svg +1 -0
  82. package/extensions/magpie/icons/giteeai.svg +1 -0
  83. package/extensions/magpie/icons/github.svg +1 -0
  84. package/extensions/magpie/icons/githubcopilot.svg +1 -0
  85. package/extensions/magpie/icons/glama.svg +1 -0
  86. package/extensions/magpie/icons/gmicloud.svg +1 -0
  87. package/extensions/magpie/icons/google-color.svg +1 -0
  88. package/extensions/magpie/icons/googlecloud-color.svg +1 -0
  89. package/extensions/magpie/icons/goose.svg +1 -0
  90. package/extensions/magpie/icons/grok.svg +1 -0
  91. package/extensions/magpie/icons/groq.svg +1 -0
  92. package/extensions/magpie/icons/hanako.png +0 -0
  93. package/extensions/magpie/icons/hermes.svg +1 -0
  94. package/extensions/magpie/icons/huawei-color.svg +1 -0
  95. package/extensions/magpie/icons/huaweicloud-color.svg +1 -0
  96. package/extensions/magpie/icons/huggingface-color.svg +1 -0
  97. package/extensions/magpie/icons/hunyuan-color.svg +1 -0
  98. package/extensions/magpie/icons/hyperbolic-color.svg +1 -0
  99. package/extensions/magpie/icons/icon-512.png +0 -0
  100. package/extensions/magpie/icons/iflytekcloud-color.svg +1 -0
  101. package/extensions/magpie/icons/inference.svg +1 -0
  102. package/extensions/magpie/icons/infinigence-color.svg +1 -0
  103. package/extensions/magpie/icons/internlm-color.svg +1 -0
  104. package/extensions/magpie/icons/jcode.svg +1 -0
  105. package/extensions/magpie/icons/jina.svg +1 -0
  106. package/extensions/magpie/icons/kilo.svg +1 -0
  107. package/extensions/magpie/icons/kilocode.svg +1 -0
  108. package/extensions/magpie/icons/kimi-code-desktop.png +0 -0
  109. package/extensions/magpie/icons/kimi.svg +1 -0
  110. package/extensions/magpie/icons/kiro-color.svg +1 -0
  111. package/extensions/magpie/icons/kling-color.svg +1 -0
  112. package/extensions/magpie/icons/lambda.svg +1 -0
  113. package/extensions/magpie/icons/leptonai-color.svg +1 -0
  114. package/extensions/magpie/icons/liquid.svg +1 -0
  115. package/extensions/magpie/icons/liveagent-color.svg +1 -0
  116. package/extensions/magpie/icons/llmapi.svg +1 -0
  117. package/extensions/magpie/icons/lmstudio.svg +1 -0
  118. package/extensions/magpie/icons/lody.png +0 -0
  119. package/extensions/magpie/icons/longcat.svg +1 -0
  120. package/extensions/magpie/icons/magpie.svg +1 -0
  121. package/extensions/magpie/icons/meta-color.svg +1 -0
  122. package/extensions/magpie/icons/metaai-color.svg +1 -0
  123. package/extensions/magpie/icons/microsoft-color.svg +1 -0
  124. package/extensions/magpie/icons/mimocode.svg +1 -0
  125. package/extensions/magpie/icons/minimax-color.svg +1 -0
  126. package/extensions/magpie/icons/mistermorph-color.svg +1 -0
  127. package/extensions/magpie/icons/mistral-color.svg +1 -0
  128. package/extensions/magpie/icons/mlx-serve.png +0 -0
  129. package/extensions/magpie/icons/modelscope-color.svg +1 -0
  130. package/extensions/magpie/icons/monica-color.svg +1 -0
  131. package/extensions/magpie/icons/moonshot.svg +1 -0
  132. package/extensions/magpie/icons/morph-color.svg +1 -0
  133. package/extensions/magpie/icons/mothx.png +0 -0
  134. package/extensions/magpie/icons/nebius.svg +1 -0
  135. package/extensions/magpie/icons/newapi-color.svg +1 -0
  136. package/extensions/magpie/icons/nousresearch.svg +1 -0
  137. package/extensions/magpie/icons/nova-color.svg +1 -0
  138. package/extensions/magpie/icons/novita-color.svg +1 -0
  139. package/extensions/magpie/icons/nvidia-color.svg +1 -0
  140. package/extensions/magpie/icons/ollama.svg +1 -0
  141. package/extensions/magpie/icons/omlx.svg +1 -0
  142. package/extensions/magpie/icons/omo.svg +1 -0
  143. package/extensions/magpie/icons/omp.svg +1 -0
  144. package/extensions/magpie/icons/openai.svg +1 -0
  145. package/extensions/magpie/icons/openchamber.svg +11 -0
  146. package/extensions/magpie/icons/openclaw.svg +1 -0
  147. package/extensions/magpie/icons/opencode.svg +1 -0
  148. package/extensions/magpie/icons/openmausbot.svg +1 -0
  149. package/extensions/magpie/icons/openrouter.svg +1 -0
  150. package/extensions/magpie/icons/openwebui.svg +1 -0
  151. package/extensions/magpie/icons/parasail.svg +1 -0
  152. package/extensions/magpie/icons/penguin-color.svg +1 -0
  153. package/extensions/magpie/icons/perplexity-color.svg +1 -0
  154. package/extensions/magpie/icons/phala-color.svg +1 -0
  155. package/extensions/magpie/icons/pi.svg +1 -0
  156. package/extensions/magpie/icons/pipellm-color.svg +5 -0
  157. package/extensions/magpie/icons/poe.svg +1 -0
  158. package/extensions/magpie/icons/ppio-color.svg +1 -0
  159. package/extensions/magpie/icons/prime-agent.svg +1 -0
  160. package/extensions/magpie/icons/qiniu-color.svg +1 -0
  161. package/extensions/magpie/icons/qoder.svg +1 -0
  162. package/extensions/magpie/icons/qwen-color.svg +1 -0
  163. package/extensions/magpie/icons/qwenpaw.svg +1 -0
  164. package/extensions/magpie/icons/raycast.svg +1 -0
  165. package/extensions/magpie/icons/reasonix-color.svg +48 -0
  166. package/extensions/magpie/icons/reka.svg +1 -0
  167. package/extensions/magpie/icons/replicate.svg +1 -0
  168. package/extensions/magpie/icons/roocode.svg +1 -0
  169. package/extensions/magpie/icons/rtk.png +0 -0
  170. package/extensions/magpie/icons/sambanova-color.svg +1 -0
  171. package/extensions/magpie/icons/sensenova-color.svg +1 -0
  172. package/extensions/magpie/icons/siliconcloud-color.svg +1 -0
  173. package/extensions/magpie/icons/snow.png +0 -0
  174. package/extensions/magpie/icons/snowflake-color.svg +1 -0
  175. package/extensions/magpie/icons/sophnet-color.svg +1 -0
  176. package/extensions/magpie/icons/spark-color.svg +1 -0
  177. package/extensions/magpie/icons/stepfun-color.svg +1 -0
  178. package/extensions/magpie/icons/straico-color.svg +1 -0
  179. package/extensions/magpie/icons/submodel-color.svg +1 -0
  180. package/extensions/magpie/icons/t3code.svg +4 -0
  181. package/extensions/magpie/icons/targon-color.svg +1 -0
  182. package/extensions/magpie/icons/tencent-color.svg +1 -0
  183. package/extensions/magpie/icons/tencentcloud-color.svg +1 -0
  184. package/extensions/magpie/icons/tiangong-color.svg +1 -0
  185. package/extensions/magpie/icons/together-color.svg +1 -0
  186. package/extensions/magpie/icons/trae.svg +1 -0
  187. package/extensions/magpie/icons/typesafe.png +0 -0
  188. package/extensions/magpie/icons/upstage-color.svg +1 -0
  189. package/extensions/magpie/icons/venice-color.svg +1 -0
  190. package/extensions/magpie/icons/vercel.svg +1 -0
  191. package/extensions/magpie/icons/vertexai-color.svg +1 -0
  192. package/extensions/magpie/icons/vllm-color.svg +1 -0
  193. package/extensions/magpie/icons/volcengine-color.svg +1 -0
  194. package/extensions/magpie/icons/voyage-color.svg +1 -0
  195. package/extensions/magpie/icons/vscode.svg +1 -0
  196. package/extensions/magpie/icons/wenxin-color.svg +1 -0
  197. package/extensions/magpie/icons/windsurf.svg +1 -0
  198. package/extensions/magpie/icons/workbuddy-color.svg +1 -0
  199. package/extensions/magpie/icons/workersai-color.svg +1 -0
  200. package/extensions/magpie/icons/xai.svg +1 -0
  201. package/extensions/magpie/icons/xiaomimimo.svg +1 -0
  202. package/extensions/magpie/icons/xinference-color.svg +1 -0
  203. package/extensions/magpie/icons/yi.svg +1 -0
  204. package/extensions/magpie/icons/yuanbao-color.svg +1 -0
  205. package/extensions/magpie/icons/yylx.svg +5 -0
  206. package/extensions/magpie/icons/zai.svg +1 -0
  207. package/extensions/magpie/icons/zcode.png +0 -0
  208. package/extensions/magpie/icons/zed.svg +1 -0
  209. package/extensions/magpie/icons/zenmux.svg +1 -0
  210. package/extensions/magpie/icons/zeroone.svg +1 -0
  211. package/extensions/magpie/icons/zhipu-color.svg +1 -0
  212. package/extensions/magpie/icons/zoo-code.png +0 -0
  213. package/extensions/magpie/index.html +572 -0
  214. package/extensions/magpie/kernel.js +274 -0
  215. package/extensions/magpie/keyspend.js +63 -0
  216. package/extensions/magpie/layout.js +26 -0
  217. package/extensions/magpie/manifest.json +37 -0
  218. package/extensions/magpie/navlamp.js +47 -0
  219. package/extensions/magpie/omarchy.css +72 -0
  220. package/extensions/magpie/omarchy.js +46 -0
  221. package/extensions/magpie/passerrors.js +21 -0
  222. package/extensions/magpie/pluginpages.js +405 -0
  223. package/extensions/magpie/plugins/gateway/client.js +1194 -0
  224. package/extensions/magpie/plugins/gateway/keys.js +972 -0
  225. package/extensions/magpie/plugins/gateway/settings.js +459 -0
  226. package/extensions/magpie/plugins/library/client.js +6788 -0
  227. package/extensions/magpie/plugins/library/i18n.js +3228 -0
  228. package/extensions/magpie/plugins/library/library.css +848 -0
  229. package/extensions/magpie/plugins/otel/i18n.js +48 -0
  230. package/extensions/magpie/plugins/otel/page.js +189 -0
  231. package/extensions/magpie/plugins/redact/client.js +46 -0
  232. package/extensions/magpie/plugins/redact/demo.js +461 -0
  233. package/extensions/magpie/plugins/redact/i18n.js +432 -0
  234. package/extensions/magpie/plugins/redact/page.js +985 -0
  235. package/extensions/magpie/plugins/redact/redact.css +385 -0
  236. package/extensions/magpie/plugins/redact/settings.js +248 -0
  237. package/extensions/magpie/plugins/redact/xq_dark.jpg +0 -0
  238. package/extensions/magpie/plugins/redact/xq_dark.webm +0 -0
  239. package/extensions/magpie/plugins/redact/xq_light.jpg +0 -0
  240. package/extensions/magpie/plugins/redact/xq_light.webm +0 -0
  241. package/extensions/magpie/plugins/routing/client.js +8593 -0
  242. package/extensions/magpie/plugins/routing/i18n.js +2696 -0
  243. package/extensions/magpie/plugins/routing/routing.css +1111 -0
  244. package/extensions/magpie/plugins/search/settings.js +460 -0
  245. package/extensions/magpie/plugins/sessions/client.js +1622 -0
  246. package/extensions/magpie/plugins/sessions/i18n.js +360 -0
  247. package/extensions/magpie/plugins/sessions/sessions.css +181 -0
  248. package/extensions/magpie/plugins/sessions/settings.js +100 -0
  249. package/extensions/magpie/plugins/usage/client.js +150 -0
  250. package/extensions/magpie/plugins/usage/i18n.js +984 -0
  251. package/extensions/magpie/plugins/usage/page.js +4307 -0
  252. package/extensions/magpie/plugins.css +319 -0
  253. package/extensions/magpie/plugins.js +1926 -0
  254. package/extensions/magpie/profilerename.js +27 -0
  255. package/extensions/magpie/purposes.js +2 -0
  256. package/extensions/magpie/quitask.js +79 -0
  257. package/extensions/magpie/quotaall.js +142 -0
  258. package/extensions/magpie/ratewindows.js +123 -0
  259. package/extensions/magpie/registry.js +87 -0
  260. package/extensions/magpie/resetask.js +137 -0
  261. package/extensions/magpie/ruleedit.js +53 -0
  262. package/extensions/magpie/scrollends.js +91 -0
  263. package/extensions/magpie/settingshints.js +75 -0
  264. package/extensions/magpie/signsheets.js +86 -0
  265. package/extensions/magpie/start.js +26 -0
  266. package/extensions/magpie/syncfiles.js +216 -0
  267. package/extensions/magpie/theme.js +29 -0
  268. package/extensions/magpie/traywindows.js +41 -0
  269. package/extensions/magpie/webfonts.js +130 -0
  270. package/extensions/magpie/whatsnew.js +252 -0
  271. package/extensions/magpie.zip +0 -0
  272. package/knowledge/apps/baijiahao.md +30 -0
  273. package/knowledge/apps/bilibili.md +1 -0
  274. package/knowledge/apps/dedao.md +33 -0
  275. package/knowledge/apps/feishu.md +35 -0
  276. package/knowledge/apps/gmail.md +33 -0
  277. package/knowledge/apps/jike.md +31 -0
  278. package/knowledge/apps/kuaishou.md +34 -0
  279. package/knowledge/apps/linkedin.md +33 -0
  280. package/knowledge/apps/qqmail.md +30 -0
  281. package/knowledge/apps/telegram.md +27 -0
  282. package/knowledge/apps/toutiao.md +32 -0
  283. package/knowledge/apps/uber-eats.md +26 -0
  284. package/knowledge/apps/uber.md +27 -0
  285. package/knowledge/apps/whatsapp.md +27 -0
  286. package/knowledge/apps/x.md +32 -0
  287. package/knowledge/apps/xiaohongshu.md +1 -0
  288. package/knowledge/apps/zhihu.md +1 -0
  289. package/knowledge/sites/bilibili.com/SITE.md +50 -0
  290. package/knowledge/sites/bilibili.com/references/view-fields.md +17 -0
  291. package/knowledge/sites/github.com/SITE.md +51 -0
  292. package/knowledge/sites/github.com/references/repo-fields.md +13 -0
  293. package/knowledge/sites/x.com/SITE.md +47 -0
  294. package/knowledge/sites/xiaohongshu.com/SITE.md +48 -0
  295. package/knowledge/sites/xiaohongshu.com/references/note-fields.md +18 -0
  296. package/knowledge/sites/youtube.com/SITE.md +49 -0
  297. package/knowledge/sites/youtube.com/references/innertube-fields.md +14 -0
  298. package/knowledge/sites/zhihu.com/SITE.md +47 -0
  299. package/knowledge/sites/zhihu.com/references/api-fields.md +21 -0
  300. package/lib/mobile/extension-catalog.js +41 -1
  301. package/lib/mobile/extension-data.js +224 -0
  302. package/lib/mobile/extension-dev.js +3 -1
  303. package/lib/mobile/file-ops.js +252 -0
  304. package/lib/mobile/history-derive.js +80 -4
  305. package/lib/mobile/host-bridge/endpoints.js +48 -6
  306. package/lib/mobile/host-bridge/event-synthesis.js +33 -6
  307. package/lib/mobile/jobs-control.js +118 -0
  308. package/lib/mobile/memory.js +635 -0
  309. package/lib/mobile/model-badges.js +77 -0
  310. package/lib/mobile/pet-tools.js +50 -4
  311. package/lib/mobile/pets.js +214 -18
  312. package/lib/mobile/phone/access.js +85 -0
  313. package/lib/mobile/phone/evidence.js +228 -0
  314. package/lib/mobile/phone/gate.js +290 -0
  315. package/lib/mobile/phone/grounding.js +314 -0
  316. package/lib/mobile/phone/guard.js +465 -0
  317. package/lib/mobile/phone/irreversible.js +88 -8
  318. package/lib/mobile/phone/knowledge-tools.js +665 -0
  319. package/lib/mobile/phone/learn.js +329 -0
  320. package/lib/mobile/phone/redact.js +237 -0
  321. package/lib/mobile/phone/risk.js +95 -0
  322. package/lib/mobile/phone/routes.js +28 -1
  323. package/lib/mobile/phone/service.js +23 -0
  324. package/lib/mobile/phone/site-commands.js +271 -0
  325. package/lib/mobile/phone/step-summary.js +201 -0
  326. package/lib/mobile/phone/stop-summary.js +235 -0
  327. package/lib/mobile/phone/tool-groups.js +125 -0
  328. package/lib/mobile/phone/tools.js +589 -0
  329. package/lib/mobile/phone/untrusted.js +65 -0
  330. package/lib/mobile/phone-knowledge.js +20 -4
  331. package/lib/mobile/schedule-edit.js +500 -0
  332. package/lib/mobile/schedule.js +5 -0
  333. package/lib/mobile/send-continuation.js +52 -0
  334. package/lib/mobile/session-models.js +27 -31
  335. package/lib/mobile/session-send.js +12 -12
  336. package/lib/mobile/skill-import.js +338 -0
  337. package/lib/mobile/skill-store.js +524 -0
  338. package/lib/routes/mobile-api.js +42 -0
  339. package/package.json +7 -6
  340. package/skills/skill-authoring/SKILL.md +46 -0
  341. package/skills/skill-authoring/references/examples.md +82 -0
  342. package/skills/skill-authoring/references/frontmatter-and-fields.md +46 -0
  343. package/skills/skill-authoring/scripts/check_skill.py +167 -0
  344. package/skills/skill-authoring/scripts/manifest.json +3 -0
  345. package/knowledge/sites/bilibili.com.md +0 -27
  346. package/knowledge/sites/github.com.md +0 -27
  347. package/knowledge/sites/x.com.md +0 -28
  348. package/knowledge/sites/xiaohongshu.com.md +0 -27
  349. package/knowledge/sites/youtube.com.md +0 -28
  350. package/knowledge/sites/zhihu.com.md +0 -27
@@ -0,0 +1,1111 @@
1
+ /* Routing, live: the Gateway view's stage for what routing does with each
2
+ request (client.js). The same stage as the site's Model Routing, only
3
+ played by the gateway's own trace. */
4
+ .rt { background: var(--card); border: 1px solid var(--line); border-radius: 14px; padding: 14px 16px 12px; margin: 0 0 18px; }
5
+ .rt-top { display: flex; flex-direction: column; gap: 3px; }
6
+ .rt-what { font-size: 13px; color: var(--muted); }
7
+ .rt-what b { color: var(--fg); font-weight: 600; }
8
+ .rt-mode { margin: 0; font-size: 12.5px; color: var(--fg-2); line-height: 1.45; }
9
+ /* the agents' and magpie's columns at least 124px, and as wide as their
10
+ words up to 15% of the stage: at 124px a full-screen window cut an agent
11
+ to "Claude Desk…" while its keys' rows ran on across the rest (refeccd
12
+ on Discord) */
13
+ .rt-stage { position: relative; display: grid; grid-template-columns: fit-content(max(124px, 15%)) fit-content(max(124px, 15%)) minmax(0, 1fr); column-gap: 56px; align-items: center; padding: 22px 2px; }
14
+ .rt-stage > :is(.rt-srcs, .rt-hub) { min-width: 124px; }
15
+ .rt-wires { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
16
+ .rt-wires path { fill: none; stroke: var(--line); stroke-width: 1.5; transition: opacity .3s; }
17
+ .rt-wires path.live { stroke: var(--agent, var(--muted)); stroke-dasharray: 3 5; animation: rtflow .7s linear infinite; }
18
+ /* lit: the way the request shown went, over; only a request in flight flows (#2386) */
19
+ .rt-wires path.lit { stroke: var(--agent, var(--muted)); }
20
+ .rt-wires path.rest { stroke-dasharray: 2 6; opacity: .45; }
21
+ /* a flowing wire is drawn by its copy in .rt-flows, a layer of its own, so
22
+ the wires that don't move aren't drawn again each frame (#3164) */
23
+ .rt-wires path.flown { visibility: hidden; animation: none; }
24
+ .rt-flows { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; will-change: transform; }
25
+ .rt-flows path { fill: none; stroke: var(--agent, var(--muted)); stroke-width: 1.5; stroke-dasharray: 3 5; animation: rtflow .7s linear infinite; }
26
+ @keyframes rtflow { to { stroke-dashoffset: -8; } }
27
+ /* the magpies: one carries each request out, another brings its answer
28
+ back — the dot at the tip of its beak. They fly along the wires and
29
+ under the cards, through magpie's box rather than over its words: over
30
+ them, with two agents sending at once one sat on the hub's address and
31
+ another on a member's row (penershu on X). Each flight starts and ends
32
+ a dot clear of the card at either end of its wire (pose). */
33
+ .rt-sky { position: absolute; inset: 0; z-index: 0; width: 100%; height: 100%; overflow: visible; pointer-events: none; }
34
+ /* each agent's requests and answers fly in its colour: a request filled
35
+ with it, an answer ringed with it, a failure ringed red */
36
+ .rt-sky .pkt { fill: var(--agent, var(--fg)); filter: drop-shadow(0 0 3px var(--agent, var(--muted))); }
37
+ .rt-sky .pkt.back { fill: var(--card); stroke: var(--agent, var(--fg)); stroke-width: 2.25; }
38
+ .rt-sky .pkt.back.err { fill: var(--agent, var(--card)); stroke: var(--red); }
39
+ .rt-sky path.via { fill: none; stroke: none; }
40
+ .rt-sky .pkt.held { animation: rtheld 1.2s ease-in-out infinite; }
41
+ @keyframes rtheld { 50% { opacity: .45; } }
42
+ .rt-flier { color: var(--fg); }
43
+ .rt-flier .body, .rt-flier .tail, .rt-flier .wing path:first-child, .rt-flier .wing.far { fill: currentColor; }
44
+ .rt-flier .wing.far { opacity: .45; }
45
+ .rt-flier .belly { fill: var(--card); }
46
+ .rt-flier .wing .bar { fill: none; stroke: var(--card); stroke-width: 1.3; stroke-linecap: round; }
47
+ /* the wings beat from the shoulder: down hard, up easy */
48
+ .rt-flier .wing { transform-box: fill-box; transform-origin: 50% 100%; animation: rtflap .42s cubic-bezier(.45, 0, .3, 1) infinite; }
49
+ .rt-flier .wing.far { animation-delay: -.03s; }
50
+ @keyframes rtflap { 0%, 100% { transform: scaleY(1); } 55% { transform: scaleY(-.62) skewX(-8deg); } }
51
+ .rt-flier .rt-lift { animation: rtcome .3s ease-out; transition: transform .45s cubic-bezier(.3, 0, .2, 1), opacity .45s; }
52
+ .rt-flier.away .rt-lift { transform: translate(14px, -16px) scale(.8); opacity: 0; }
53
+ @keyframes rtcome { from { transform: translate(-6px, -10px) scale(.85); opacity: 0; } }
54
+ .rt-srcs { position: relative; z-index: 1; min-width: 0; display: grid; gap: 10px; }
55
+ .rt-srcs.many .rt-node { padding: 8px 8px 7px; }
56
+ .rt-src b::before { content: ""; display: inline-block; width: 7px; height: 7px; margin: 0 6px 1px 0; border-radius: 50%; background: var(--agent); vertical-align: middle; }
57
+ .rt-srcs.many .rt-node .rt-ic .ic, .rt-srcs.many .rt-node .rt-ic .ic img, .rt-srcs.many .rt-node .rt-ic .ic svg { width: 20px; height: 20px; }
58
+ .rt-node { position: relative; z-index: 1; min-width: 0; display: flex; flex-direction: column; align-items: center; gap: 2px; padding: 12px 8px 10px; background: var(--card); border: 1px solid var(--line); border-radius: 12px; text-align: center; }
59
+ .rt-node .rt-ic .ic, .rt-node .rt-bird { width: 26px; height: 26px; margin-bottom: 3px; }
60
+ .rt-node .rt-ic .ic img, .rt-node .rt-ic .ic svg { width: 26px; height: 26px; }
61
+ .rt-node b { font-size: 13px; font-weight: 600; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
62
+ .rt-node small { font-size: 11px; color: var(--muted); max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
63
+ .rt-hub { border-color: var(--fg); box-shadow: 0 0 0 4px var(--card-2); }
64
+ .rt-hub i { font-style: normal; font-size: 10.5px; font-weight: 560; margin-top: 5px; padding: 1px 8px; border-radius: 999px; background: var(--fg); color: var(--card); }
65
+ .rt-hub.tick, .rt-src.tick { animation: rttick .4s ease-out; }
66
+ @keyframes rttick { 40% { transform: scale(1.04); } }
67
+ /* one column as wide as the list: a row out of sight is as wide as WebKit
68
+ last drew it (content-visibility), and in a column as wide as its rows
69
+ "No request yet" held the page as wide as a wider window had it, a
70
+ scrollbar under it (young02036 on Discord) */
71
+ .rt-accts { position: relative; z-index: 1; list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: minmax(0, 1fr); gap: 7px; min-width: 0; }
72
+ /* a row's name, plan and model as wide as they are, and what it says the
73
+ rest: a long status (an intent smart routing picked it for) is what is
74
+ cut short, never the model, which says what answered (yi_time on
75
+ Discord). It keeps at least min(14em, 40%) of the row, where the name
76
+ and plan are cut first (b below) */
77
+ .rt-accts li { position: relative; display: grid; grid-template-columns: 10px minmax(0, max-content) minmax(min(14em, 40%), 1fr); align-items: center; column-gap: 10px; row-gap: 6px; padding: 9px 12px; background: var(--card); border: 1px solid var(--line); border-radius: 10px; font-size: 13px; transition: opacity .3s, border-color .3s, transform .5s cubic-bezier(.2, .8, .2, 1); }
78
+ .rt-accts li { margin-left: calc(var(--depth, 0) * 18px); }
79
+ /* a key off screen is not laid out or painted (#3164): with hundreds of
80
+ keys open, WebKit painted every row again on each frame a magpie flew.
81
+ One not drawn yet is as tall as a key drawn is (--rt-row, client.js's
82
+ layout). A row a failure tagged keeps its tag up over its top edge,
83
+ which this would cut, so it is drawn whole until the tag has faded
84
+ (lift, client.js) */
85
+ .rt-accts li:not(.lift) { content-visibility: auto; contain-intrinsic-size: auto var(--rt-row, 38px); }
86
+ /* a run of keys far from sight, out of the list (client.js place, #3164) */
87
+ .rt-accts li.rt-gap { display: block; padding: 0; border: 0; background: none; content-visibility: visible; contain: strict; pointer-events: none; }
88
+ /* a group in the group: a heading its models hang from, set in under it */
89
+ .rt-accts li.rt-sub { display: flex; align-items: center; gap: 7px; padding: 5px 10px; font-size: 12px; color: var(--muted); background: var(--card-2); border-style: dashed; }
90
+ .rt-accts li.rt-sub.on { border-style: solid; border-color: var(--fg-2); color: var(--fg); }
91
+ .rt-accts li.rt-sub svg { flex: none; color: var(--fg-2); }
92
+ .rt-accts li.rt-sub code.mdl { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
93
+ .rt-accts li.rt-sub i { margin-left: auto; font-style: normal; font-size: 10.5px; font-weight: 560; padding: 1px 8px; border-radius: 999px; border: 1px solid var(--line); color: var(--fg-2); white-space: nowrap; }
94
+ /* it folds from its heading (Aiirobyte on Discord): folded, it says what
95
+ it routes to together on a line of its own */
96
+ .rt-accts li.rt-sub { flex-wrap: wrap; row-gap: 4px; cursor: pointer; user-select: none; }
97
+ .rt-accts li.rt-sub .n { white-space: nowrap; }
98
+ .rt-accts li.rt-sub .n:empty, .rt-accts li.rt-sub em:empty, .rt-accts li.rt-sub i:empty { display: none; }
99
+ /* a route's heading, when more than one route is on the stage: its provider's logo */
100
+ .rt-accts li.rt-sub > .ic { flex: none; }
101
+ .rt-accts li.rt-sub .chev { display: inline-flex; color: var(--muted); transition: transform .2s; }
102
+ .rt-accts li.rt-sub .chev svg { color: inherit; }
103
+ .rt-accts li.rt-sub:hover .chev { color: var(--fg); }
104
+ .rt-accts li.rt-sub.open .chev { transform: rotate(180deg); }
105
+ .rt-accts li.rt-sub em { flex: 1 0 100%; min-width: 0; white-space: normal; padding-left: 21px; }
106
+ .rt-accts li.rt-sub:focus { outline: none; }
107
+ .rt-accts li.rt-sub:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 30%, transparent); }
108
+ .rt-accts li.idle { display: block; color: var(--muted); border-style: dashed; text-align: center; }
109
+ .rt-accts b { font-weight: 560; color: var(--fg); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
110
+ /* a row's and a fold's head: the name and the plan give up their room
111
+ before the model's chip and its tags do, each cut short on its own */
112
+ .rt-accts li:not(.rt-sub) > b { display: flex; align-items: baseline; min-width: 0; }
113
+ .rt-accts li:not(.rt-sub) > b > * { flex: none; }
114
+ .rt-accts li:not(.rt-sub) > b > span:not(.ic) { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
115
+ .rt-accts li:not(.rt-sub) > b > span.who { flex-shrink: 100; } /* the name first, the plan after it */
116
+ .rt-accts li:not(.rt-sub) > b > span:not(.ic):not(.who):not(:empty) { margin-left: .3em; }
117
+ .rt-accts li:not(.rt-sub) > b > .ic { align-self: center; }
118
+ .rt-accts b > span:not(.ic) { font-weight: 450; color: var(--muted); }
119
+ .rt-accts b > span.who { color: var(--fg); font-weight: 560; }
120
+ .rt-accts b .ic { display: inline-flex; width: 16px; height: 16px; margin-right: 7px; vertical-align: -3px; }
121
+ .rt-accts b .ic img, .rt-accts b .ic svg { width: 16px; height: 16px; }
122
+ .rt-accts b .fb { margin-left: 6px; font-size: 10.5px; font-weight: 560; padding: 0 6px; border-radius: 999px; border: 1px solid var(--line); color: var(--muted); }
123
+ .rt-accts b code.mdl, :is(.rt-req, .rt-probe) code, .rt-act code.mdl { font: var(--code-style, normal) 500 11px/1.5 var(--code-font, var(--code)); font-stretch: var(--code-stretch, 100%); color: var(--fg-2); background: var(--card-2); border-radius: 5px; padding: 0 5px; }
124
+ .rt-accts b code.mdl { margin-left: 7px; font-weight: 450; }
125
+ .rt-accts em { font-style: normal; font-size: 12px; color: var(--muted); white-space: nowrap; font-variant-numeric: tabular-nums; }
126
+ /* what a row says has at most its column: a long one (an intent smart
127
+ routing picked it for) is cut short, its name kept, never run off the
128
+ row's edge (penershu on X) */
129
+ .rt-accts li > em { min-width: 0; overflow: hidden; text-overflow: ellipsis; text-align: right; }
130
+ /* the session an account answers (connor_yang on Discord): one line cut
131
+ short, also where a narrow stage lets the status wrap */
132
+ .rt-accts em .ses { display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom; }
133
+ .rt-accts .bar { grid-column: 2 / 4; height: 3px; border-radius: 2px; background: var(--card-2); overflow: hidden; }
134
+ .rt-accts .bar i { display: block; height: 100%; width: 0; border-radius: 2px; background: var(--muted); transition: width .4s; }
135
+ .rt-accts li.nobar .bar { display: none; }
136
+ .rt-accts .dot { width: 8px; height: 8px; border-radius: 50%; border: 1.5px solid var(--muted); }
137
+ .rt-accts li.on { border-color: var(--fg); }
138
+ .rt-accts li.on em { color: var(--fg); }
139
+ .rt-accts li.on .bar i { background: var(--fg); }
140
+ .rt-accts li.on .dot { background: var(--fg); border-color: var(--fg); }
141
+ /* A live dot's pulse: a disk 6px wider all round grows from the dot and
142
+ fades. It moves by transform and opacity, on the compositor: as a
143
+ box-shadow it repainted the page every frame while requests were in
144
+ flight, and 64 of them kept a core half busy (unscientific9). A muted
145
+ dot's disk goes behind it, in a parent that isolates it; a filled one's
146
+ is the dot's own colour, so it may lie on top. */
147
+ .rt-accts li.now .dot, .rt-livedot.on, :is(.rt-req, .rt-probe).wait .to i, .ps-dst.wait > i { position: relative; }
148
+ .rt-accts li.now .dot::after, .rt-livedot.on::after, :is(.rt-req, .rt-probe).wait .to i::after, .ps-dst.wait > i::after, .rt-steps li.wait::after {
149
+ content: ""; position: absolute; inset: 0; border-radius: 50%; background: color-mix(in srgb, var(--fg) 40%, transparent); opacity: 0; pointer-events: none;
150
+ animation: rtlive 1.8s infinite;
151
+ }
152
+ .rt-accts li.now .dot::after { inset: -1.5px; animation-name: rtlive-dot; }
153
+ :is(.rt-req, .rt-probe).wait .to, .ps-dst.wait, .rt-steps li.wait { isolation: isolate; }
154
+ :is(.rt-req, .rt-probe).wait .to i::after, .ps-dst.wait > i::after, .rt-steps li.wait::after { z-index: -1; }
155
+ .rt-steps li.wait::after { inset: auto; left: 5px; top: .5em; width: 6px; height: 6px; }
156
+ /* 6px each side: a 6px dot grows to 18px, the 8px account dot to 20px */
157
+ @keyframes rtlive { 0% { transform: scale(1); opacity: 1; } 70%, 100% { transform: scale(3); opacity: 0; } }
158
+ @keyframes rtlive-dot { 0% { transform: scale(1); opacity: 1; } 70%, 100% { transform: scale(2.5); opacity: 0; } }
159
+ .rt-accts li.low em { color: var(--fg-2); }
160
+ .rt-accts li.rest { border-style: dashed; }
161
+ /* a reply still out with no content for 15s: the dot stops pulsing */
162
+ .rt-accts li.quiet .dot { background: var(--amber); border-color: var(--amber); }
163
+ .rt-accts li.quiet .dot::after { animation: none; }
164
+ .rt-accts li.rest > :not(.tag) { opacity: .5; }
165
+ .rt-accts li.rest em { color: var(--red); opacity: .85; }
166
+ .rt-accts li.left { border-style: dotted; }
167
+ .rt-accts li.left > :not(.tag) { opacity: .45; }
168
+ .rt-accts li.hit { animation: rthit .45s; }
169
+ .rt-accts li.new { animation: rtin .4s ease-out; }
170
+ @keyframes rthit { 20% { translate: -5px; } 40% { translate: 4px; } 60% { translate: -2px; } 80% { translate: 1px; } }
171
+ .rt-accts .tag { position: absolute; right: 10px; top: -10px; padding: 1px 7px; border-radius: 6px; background: var(--red); color: #fff; font: var(--code-style, normal) 600 10.5px/1.5 var(--code-font, var(--code)); font-stretch: var(--code-stretch, 100%); opacity: 0; transform: translateY(4px) scale(.9); transition: opacity .25s, transform .25s; pointer-events: none; }
172
+ .rt-accts li.tagged .tag { opacity: 1; transform: none; }
173
+ /* a provider's keys for one model, folded into one row (#1319): it opens
174
+ to head them, as a group in the group heads its models */
175
+ .rt-accts li.rt-fold { grid-template-columns: 10px minmax(0, max-content) minmax(min(14em, 40%), 1fr) auto; cursor: pointer; user-select: none; }
176
+ .rt-accts li.rt-fold em { white-space: normal; text-align: right; }
177
+ .rt-accts li.rt-fold .chev { display: inline-flex; color: var(--muted); transition: transform .2s; }
178
+ .rt-accts li.rt-fold:hover .chev { color: var(--fg); }
179
+ .rt-accts li.rt-fold.open { background: var(--card-2); }
180
+ .rt-accts li.rt-fold.open .chev { transform: rotate(180deg); }
181
+ .rt-accts li.rt-fold:focus { outline: none; }
182
+ .rt-accts li.rt-fold:focus-visible { box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 30%, transparent); }
183
+ .rt-foot { display: flex; align-items: center; justify-content: space-between; gap: 10px 20px; flex-wrap: wrap; padding-top: 10px; border-top: 1px solid var(--line); }
184
+ .rt-cap { flex: 1 1 20em; margin: 0; min-height: 3em; display: flex; align-items: center; font-size: 13px; line-height: 1.45; color: var(--fg); }
185
+ .rt-cap.in { animation: rtin .4s ease-out; }
186
+ @keyframes rtin { from { opacity: 0; transform: translateY(4px); } }
187
+ .rt-stats { display: flex; gap: 18px; font-size: 11px; color: var(--muted); }
188
+ .rt-stats > span { display: flex; flex-direction: column; }
189
+ .rt-stats b { font-size: 19px; line-height: 1.2; font-weight: 650; letter-spacing: -.02em; color: var(--fg); font-variant-numeric: tabular-nums; }
190
+ .rt-log { padding-top: 10px; margin-top: 10px; border-top: 1px solid var(--line); }
191
+ /* each session's context window, over the card, while several send at
192
+ once (tjhhhh on Discord): a quiet block each, the one the card tells of
193
+ lit as a picked request is */
194
+ .rt-ctx-sess { display: grid; grid-template-columns: repeat(auto-fill, minmax(min(100%, 180px), 1fr)); gap: 8px; margin-top: 12px; }
195
+ .rt-ctx-sess[hidden] { display: none; }
196
+ /* a block is a grid, not a flex column: WebKit up to Safari 17 (macOS 14)
197
+ puts a flex <button>'s children in a box of its own as wide as the
198
+ longest line, so a long name ran past its block over the next ones
199
+ (tz_188 on Discord); a grid button has no such box */
200
+ .rt-ctx-s { display: grid; grid-template-columns: minmax(0, 1fr); align-content: start; gap: 4px; min-width: 0; padding: 8px 10px; font: inherit; font-size: 12px; text-align: left; color: var(--fg-2); background: transparent; border: 1px solid var(--line); border-radius: 8px; cursor: pointer; }
201
+ .rt-ctx-s[aria-pressed="true"] { background: var(--card-2); color: var(--fg); }
202
+ .rt-ctx-s .nm { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
203
+ .rt-ctx-s .sub { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
204
+ .rt-ctx-s .ctx-stack { display: flex; max-width: none; width: 100%; height: 4px; border-radius: 2px; }
205
+ .rt-log-head { display: flex; flex-wrap: wrap; align-items: center; row-gap: 2px; font-size: 12px; font-weight: 600; color: var(--fg); }
206
+ .rt-log-head .grow { flex: 1; }
207
+ /* a request's request and response, opened from the story's head */
208
+ .rt-bodies { margin-top: 8px; border: 1px solid var(--line-2); border-radius: 8px; overflow: hidden; background: var(--card); }
209
+ .rt-bodies[hidden] { display: none; }
210
+ .rt-bodies > .call-details { border-top: 0; }
211
+ .rt-bodies > .call-archive { border: 0; }
212
+ .rt-bodies > .none, .rt-bodies-gone { margin: 0; padding: 10px 12px; font-size: 11.5px; font-weight: 400; color: var(--muted); }
213
+ .rt-bodies > .call-archive-err { padding: 10px 12px 4px; }
214
+ .rt-bodies > .call-archive-err + button.text { margin: 0 12px 10px; }
215
+ .rt-steps { list-style: none; margin: 6px 0 0; padding: 0; display: grid; align-content: start; gap: 5px; counter-reset: rt; max-height: 200px; overflow-y: auto; }
216
+ .rt-steps li { position: relative; padding-left: 20px; font-size: 12.5px; line-height: 1.45; color: var(--fg-2); }
217
+ .rt-steps li::before { content: ""; position: absolute; left: 5px; top: .5em; width: 6px; height: 6px; border-radius: 50%; background: var(--muted); }
218
+ .rt-steps li.why { color: var(--fg); }
219
+ .rt-steps li.why::before { background: var(--fg); }
220
+ .rt-steps li.aside { color: var(--muted); }
221
+ .rt-steps li.said { font-family: var(--code-font, var(--code)); font-size: 11.5px; overflow-wrap: anywhere; white-space: pre-wrap; }
222
+ :where(html[data-fonts]) .rt-steps li.said { font-style: var(--code-style, inherit); font-weight: var(--code-weight, inherit); font-stretch: var(--code-stretch, 100%); }
223
+ .rt-stats > span.rt-errs:not([aria-disabled="true"]) { cursor: pointer; }
224
+ .rt-stats > span.rt-errs:not([aria-disabled="true"]):hover b { color: var(--red); }
225
+ .rt-steps li.aside::before { background: none; border: 1px solid var(--muted); width: 4px; height: 4px; }
226
+ .rt-steps li.ok::before { background: var(--green); }
227
+ .rt-steps li.bad::before { background: var(--red); -webkit-mask: var(--x-mark) center / contain no-repeat; mask: var(--x-mark) center / contain no-repeat; border-radius: 0; transform: scale(1.5); }
228
+
229
+ /* where a try's time went: one stacked bar, magpie's, the network's and
230
+ the vendor's told apart by a swatch, each part named with its time */
231
+ .rt-time { display: grid; gap: 5px; max-width: 560px; }
232
+ .rt-time-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 12px; color: var(--fg-2); }
233
+ .rt-time-who { display: inline-flex; align-items: center; gap: 5px; font-variant-numeric: tabular-nums; }
234
+ .rt-time-who::before, .rt-time-parts li::before { content: ""; flex: none; width: 7px; height: 7px; border-radius: 2px; background: var(--rt-time); }
235
+ .rt-time-bar { display: flex; height: 6px; border-radius: 3px; overflow: hidden; background: var(--line); gap: 1px; }
236
+ .rt-time-bar span { min-width: 2px; background: var(--rt-time); }
237
+ .rt-time-parts { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 2px 14px; }
238
+ .rt-time-parts li { display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px; }
239
+ .rt-time-parts .v { color: var(--fg-2); font-variant-numeric: tabular-nums; }
240
+ .rt-time .magpie { --rt-time: var(--accent); }
241
+ .rt-time .net { --rt-time: var(--amber); }
242
+ .rt-time .vendor { --rt-time: var(--fg-2); }
243
+ .rt-off { padding: 18px 4px; text-align: center; color: var(--muted); font-size: 13px; }
244
+ @media (prefers-reduced-motion: reduce) {
245
+ .rt-wires path.live, .rt-flows path, .rt-sky .pkt.held, .rt-accts li.new, .rt-accts li.hit, .rt-hub.tick, .rt-src.tick, .rt-cap.in, .masking .pii { animation: none; }
246
+ .rt-accts li.now .dot::after, .rt-steps li.wait::after { animation: none; }
247
+ .rt-accts li { transition: none; }
248
+ }
249
+ /* The narrow and wide layouts below go by a box's own width, as container
250
+ queries would: client.js sets max560 on a box 560px wide or less, min1150
251
+ on one 1150px or wider. Not container queries: in WebKit they kept the
252
+ page from scrolling to its end (#1249). :where() keeps each rule as
253
+ strong as it was inside its query. */
254
+ /* a narrow list: what a row says goes under its name */
255
+ .rt-accts:where(.max460) li { grid-template-columns: 10px minmax(0, 1fr); }
256
+ .rt-accts:where(.max460) em { grid-column: 2; white-space: normal; }
257
+ .rt-accts:where(.max460) .bar { grid-column: 2; }
258
+ .rt-accts:where(.max460) li.rt-fold { grid-template-columns: 10px minmax(0, 1fr) auto; }
259
+ .rt-accts:where(.max460) li.rt-fold em { grid-row: 2; text-align: left; }
260
+ .rt-accts:where(.max460) li.rt-fold .chev { grid-column: 3; grid-row: 1; }
261
+ /* narrow (the tray panel): magpie and the agent side by side, the
262
+ accounts under them, on a lane down their left */
263
+ :where(.rt.max560) .rt-stage { grid-template-columns: 1fr 1fr; column-gap: 28px; row-gap: 26px; padding: 16px 0; }
264
+ :where(.rt.max560) .rt-stage > :is(.rt-srcs, .rt-hub) { min-width: 0; }
265
+ :where(.rt.max560) .rt-accts { grid-column: 1 / -1; padding-left: 22px; }
266
+ .rt-accts:where(.max560) li { grid-template-columns: 10px minmax(0, 1fr); }
267
+ .rt-accts:where(.max560) em { grid-column: 2; white-space: normal; }
268
+ .rt-accts:where(.max560) .bar { grid-column: 2; }
269
+ /* under the name, what a row says starts where the name does */
270
+ .rt-accts:where(.max460, .max560) li > em { text-align: left; }
271
+ /* ★ 本地改动(不是上游):窄屏上「名字 + 套餐 + 模型」这一行换行,名字独占一行。
272
+ 上游那套窄屏规则只把**状态**(`em`)挪到第二行,名字与模型、套餐仍挤在同一行里,
273
+ 而 `span.who` 是 `flex-shrink: 100`(宽屏上这是对的:状态被截短、名字保住)—— 到了
274
+ 手机宽度,`b` 那一行只剩两百来像素,模型标签先占住,名字就被压成「1..」这样两三个字。
275
+ 实测 390px 宽的机型上 `wnluo2@gmail.com` 的可用宽度只有 10px。所以窄屏另立一条:
276
+ `b` 允许换行、`who` 占满第一行(`1 1 100%`,不再参与收缩),模型标签与套餐掉到第二行。 */
277
+ .rt-accts:where(.max460, .max560) li:not(.rt-sub) > b { flex-wrap: wrap; row-gap: 3px; }
278
+ .rt-accts:where(.max460, .max560) li:not(.rt-sub) > b > span.who { flex: 1 1 100%; min-width: 0; }
279
+ .rt-accts:where(.max460, .max560) li:not(.rt-sub) > b > code.mdl { margin-left: 0; }
280
+ :where(.rt.max560) .rt-stats { gap: 14px; }
281
+ /* the Routing view */
282
+ /* the Routing view, and the Routing groups view beside it */
283
+ :is(#view-routing, #view-groups) .row-head { margin: 14px 0 6px; padding: 0 2px; gap: 0; }
284
+ :is(#view-routing, #view-groups) .row-head .note { font-size: 11px; color: var(--faint); margin-left: 8px; }
285
+ #view-routing .row-head .rt-livedot + .note { margin-left: 8px; }
286
+ .rt-gnew { display: inline-flex; align-items: center; gap: 4px; }
287
+ /* the groups page's toolbar: the filter first, then how many */
288
+ .rt-gsec > .row-head:first-child .rt-gfilter { flex: 0 1 300px; width: auto; margin: 0; }
289
+ .rt-gsec > .row-head:first-child .rt-gcount { margin-left: 10px; white-space: nowrap; }
290
+ .rt-gsec > .row-head:first-child .rt-gfilter + .rt-gcount { margin-left: 10px; }
291
+ /* the settings for every group, a card under the list */
292
+ .rt-gset > :is(.rt-gfound, .rt-gnames) { padding: 12px 14px; }
293
+ .rt-gset > .rt-gnames { border-top: 1px solid var(--line-2); }
294
+ /* an email, hidden: stand-in letters of its shape, softly blurred —
295
+ coming in out of focus when asked for */
296
+ .pii { filter: blur(2.2px); opacity: .5; user-select: none; }
297
+ .masking .pii { animation: rtpii .35s ease-out backwards; }
298
+ @keyframes rtpii { from { filter: blur(0); opacity: 1; } }
299
+ #view-routing .rt { margin: 0; }
300
+ .rt-more { padding-bottom: 12px; }
301
+ .rt-cols { display: grid; grid-template-columns: minmax(0, 1fr); column-gap: 16px; }
302
+ /* side by side, the accounts end where the requests do and scroll like
303
+ them, not run on down the page beside an empty column: of no height of
304
+ their own, they take what the requests give the row, and no less than a
305
+ few accounts'. Only where the requests' column still keeps each request
306
+ on one line (wider than 760px, see below): narrower, one column of
307
+ one-line requests reads better than two with each request on three
308
+ (#860, huoranxuanyuan). Where the page is 1150px wide or more. Kept to
309
+ 28%, no more than 360px: in a page kept to 1200px the accounts had room
310
+ to spare while the requests' models were cut (#1926, hibaliqya-yonma) */
311
+ :where(.rt-more.min1150) .rt-cols { grid-template-columns: minmax(0, 1fr) clamp(260px, 28%, 360px); }
312
+ :where(.rt-more.min1150) .rt-cols:not(.solo) .rt-col:last-child { display: flex; flex-direction: column; }
313
+ :where(.rt-more.min1150) .rt-cols:not(.solo) .rt-acts { flex: 1 1 auto; height: 0; min-height: 216px; overflow-y: auto; overscroll-behavior: contain; }
314
+ /* each column sized by its own width: a request's row lays itself out by
315
+ the list's width, not the page's (#273: in two columns of ~470px the
316
+ rows kept to one line, and the reasoning was drawn over the numbers) */
317
+ .rt-col { min-width: 0; }
318
+ /* the model that answered keeps room of its own (at least 14em, or 30%
319
+ of a narrower list) and the metrics wrap in theirs, up to 40%: an auto
320
+ column took the widest row's metrics for every row, and the model went
321
+ to "· hi…" (#1926, hibaliqya-yonma) */
322
+ /* Each row has columns of its own, as wide as the measuring grid's (a
323
+ hidden .rt-sizer holding one probe of each row's shape) found its own
324
+ (--rt-cols, set by the page); until it is measured, a row sizes its
325
+ own. A row was a subgrid of the list: Safari 16 (macOS 13) recursed in
326
+ sizing the list's fit-content() columns through it, and its WebContent
327
+ process crashed and reloaded the page, up to 93 times a minute under
328
+ traffic (#3731, gpboyer2). */
329
+ .rt-reqs { --rt-tpl: auto fit-content(32%) minmax(min(14em, 30%), 1fr) fit-content(40%); padding: 4px; max-height: 420px; overflow-y: auto; display: flex; flex-direction: column; }
330
+ .rt-reqs > * { flex: none; }
331
+ .rt-req { display: grid; grid-template-columns: var(--rt-cols, var(--rt-tpl)); align-items: center; column-gap: 12px; padding: 6px 8px; font: inherit; font-size: 12px; text-align: left; color: var(--fg-2); background: none; border: 1px solid transparent; border-radius: 8px; cursor: pointer; }
332
+ .rt-req:hover { background: var(--card-2); }
333
+ /* a long list draws only the rows near its sight, between gaps as tall as
334
+ the rest; a probe is a row unlike any drawn, there for the columns'
335
+ widths only (#2524) */
336
+ .rt-reqs.win { overflow-anchor: none; }
337
+ /* the measuring grid: as wide as a row's content, with its gaps, and no
338
+ height. Each probe's cells are in the columns a row's are (a layout's
339
+ placing them wins over these), in whatever row: only the columns are
340
+ measured */
341
+ .rt-sizer { display: grid; grid-template-columns: var(--rt-tpl); column-gap: 12px; padding-inline: 9px; height: 0; overflow: hidden; visibility: hidden; pointer-events: none; font-size: 12px; }
342
+ .rt-sizer > .rt-probe { display: contents; }
343
+ /* a probe is measured, never seen: its dot doesn't pulse */
344
+ .rt-sizer *, .rt-sizer *::before, .rt-sizer *::after { animation: none !important; transition: none !important; }
345
+ :where(.rt-sizer) .at { grid-column: 1; }
346
+ :where(.rt-sizer) .asked { grid-column: 2; }
347
+ :where(.rt-sizer) .to { grid-column: 3; }
348
+ :where(.rt-sizer) .meta { grid-column: 4; }
349
+ .rt-req[aria-pressed="true"] { background: var(--card-2); border-color: var(--line); color: var(--fg); }
350
+ :is(.rt-req, .rt-probe) .at { font-variant-numeric: tabular-nums; color: var(--muted); }
351
+ :is(.rt-req, .rt-probe) .asked { display: flex; align-items: center; gap: 6px; min-width: 0; }
352
+ :is(.rt-req, .rt-probe) .asked i.ag { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--agent); }
353
+ :is(.rt-req, .rt-probe) .asked .ic, :is(.rt-req, .rt-probe) .asked .ic img, :is(.rt-req, .rt-probe) .asked .ic svg { width: 14px; height: 14px; flex: none; }
354
+ :is(.rt-req, .rt-probe) .m, :is(.rt-req, .rt-probe) .to span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
355
+ /* the waiting dot's 6px glow fits inside the clip: 6px on its left,
356
+ 1px more above and below, given back outside so nothing moves
357
+ (#2553, dumplings) */
358
+ :is(.rt-req, .rt-probe) .to { display: flex; align-items: center; gap: 6px; min-width: 0; overflow: hidden; color: var(--fg); padding: 1px 0 1px 6px; margin: -1px 0 -1px -6px; }
359
+ :is(.rt-req, .rt-probe) .to .said { flex-shrink: 100000; min-width: min(9em, 60%); }
360
+ :is(.rt-req, .rt-probe) .to .said.split { display: flex; }
361
+ :is(.rt-req, .rt-probe) .to .said .who { flex: 0 1000 auto; min-width: 2.5em; }
362
+ :is(.rt-req, .rt-probe) .to .said .mdl { flex: none; max-width: calc(100% - 2.5em); white-space: pre; }
363
+ /* on a phone the account keeps a few letters, not a sliver of one (40px,
364
+ where phone-rows.test.cjs calls a text cut to a sliver) */
365
+ @media (max-width: 600px) {
366
+ :is(.rt-req, .rt-probe) .to .said .who { min-width: 40px; }
367
+ :is(.rt-req, .rt-probe) .to .said .mdl { max-width: calc(100% - 40px); }
368
+ }
369
+ :is(.rt-req, .rt-probe) .to > .ic { width: 14px; height: 14px; }
370
+ :is(.rt-req, .rt-probe) .to i { flex: none; width: 6px; height: 6px; border-radius: 50%; background: var(--green); }
371
+ :is(.rt-req, .rt-probe).moved .to i { background: var(--accent); }
372
+ :is(.rt-req, .rt-probe).bad .to { color: var(--red); }
373
+ :is(.rt-req, .rt-probe).bad .to i { background: var(--red); -webkit-mask: var(--x-mark) center / contain no-repeat; mask: var(--x-mark) center / contain no-repeat; border-radius: 0; transform: scale(1.5); }
374
+ :is(.rt-req, .rt-probe).wait .to i { background: var(--muted); }
375
+ /* one its agent canceled: neither an answer's dot nor a failure's mark */
376
+ .rt-req.gone .to i { background: var(--muted); }
377
+ /* the reasoning keeps to its own box: what it was asked at is cut first,
378
+ then what it was sent at, never drawn over the numbers beside it (#435) */
379
+ :is(.rt-req, .rt-probe) .to .ef { flex: 0 1 auto; display: flex; min-width: 0; margin-left: -2px; color: var(--muted); }
380
+ :is(.rt-req, .rt-probe) .ef::before { content: "· "; flex: none; white-space: pre; }
381
+ :is(.rt-req, .rt-probe) .to .ef span { min-width: 0; white-space: pre; }
382
+ :is(.rt-req, .rt-probe) .to .ef .was { flex-shrink: 100000; }
383
+ :is(.rt-req, .rt-probe) .to .ef.picked { color: var(--fg-2); }
384
+ :is(.rt-req, .rt-probe) .ef .was { opacity: .75; }
385
+ /* what a call was for (a Codex guardian review, a title…): a quiet grey tag by the model */
386
+ :is(.rt-req, .rt-probe) .asked .kind, .rp-req .kind, .rt-steps li .kind { flex: none; font-size: 10.5px; line-height: 16px; padding: 0 5px; border-radius: 5px; color: var(--muted); background: var(--card-2); border: 1px solid var(--line); white-space: nowrap; }
387
+ .rt-steps li .kind { display: inline-block; vertical-align: 1px; }
388
+ .rt-steps li .rt-named { white-space: nowrap; }
389
+ .rt-steps li .rt-named > .ic { width: 14px; height: 14px; margin-right: 4px; vertical-align: -2px; }
390
+ /* in a narrow row the model keeps its room first: the tag gives way, to an ellipsis */
391
+ :is(.rt-req, .rt-probe) .asked .kind { flex: 0 100 auto; min-width: 3.2em; max-width: 9em; overflow: hidden; text-overflow: ellipsis; }
392
+ :is(.rt-req, .rt-probe) .asked.kinded .m { flex: none; max-width: calc(100% - 3.2em - 40px); }
393
+ :is(.rt-req, .rt-probe) .to .swap, .rt-steps li .swap { font-size: 10.5px; line-height: 16px; padding: 0 5px; border-radius: 5px; color: var(--amber); background: var(--amber-soft); white-space: nowrap; }
394
+ :is(.rt-req, .rt-probe) .to .swap { flex: none; max-width: 70%; }
395
+ :is(.rt-req, .rt-probe) .to .routed, .pr-to .routed { flex: none; max-width: 45%; overflow: hidden; text-overflow: ellipsis; font-size: 10.5px; line-height: 16px; color: var(--muted); white-space: nowrap; }
396
+ .rt-steps li.swap::before { background: var(--amber); }
397
+ /* short of room, where it went gives way down to the provider's name, then this does
398
+ (.upstreamed, set with the tag: Safari before 15.4 has no :has()) */
399
+ :is(.rt-req, .rt-probe) .to.upstreamed .said { min-width: min(10em, 45%); }
400
+ :is(.rt-req, .rt-probe) .to .upstream, .pr-to .upstream { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; font-size: 10.5px; line-height: 16px; color: var(--muted); white-space: nowrap; }
401
+ :is(.rt-req, .rt-probe) .meta { display: flex; align-items: baseline; justify-content: flex-end; flex-wrap: wrap; gap: 2px 0; color: var(--muted); font-variant-numeric: tabular-nums; }
402
+ .rt-metric { display: inline-flex; align-items: baseline; gap: 4px; white-space: nowrap; min-width: 0; }
403
+ .rt-metric .k { color: var(--muted); }
404
+ .rt-metric .v { font-variant-numeric: tabular-nums; }
405
+ :is(.rt-req, .rt-probe) .tokens, .rt-session .rt-token-total { cursor: help; }
406
+ :is(.rt-req, .rt-probe) .meta .retry-count { white-space: nowrap; }
407
+ :is(.rt-req, .rt-probe) .meta > * + .rt-metric::before { content: "·"; margin: 0 2px; }
408
+ :is(.rt-req, .rt-probe) .meta .cost-metric { margin-left: 10px; color: var(--fg-2); }
409
+ :is(.rt-req, .rt-probe) .meta .cost-metric::before { display: none; }
410
+ :is(.rt-req, .rt-probe) .meta[hidden] { display: none; }
411
+ .rt-session { grid-column: 1 / -1; display: flex; align-items: center; flex-wrap: wrap; gap: 4px 8px; min-width: 0; padding: 9px 8px; margin-top: 4px; font: inherit; font-size: 12px; text-align: left; color: var(--fg-2); background: var(--card-2); border: 1px solid var(--line); border-radius: 8px; }
412
+ button.rt-session { cursor: pointer; }
413
+ .rt-session .arrow { flex: none; width: 10px; color: var(--muted); }
414
+ .rt-session .nm { flex: 1 1 160px; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 500; }
415
+ .rt-session .dir { flex: 0 1 auto; min-width: 0; max-width: 40%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); }
416
+ .rt-session .dir[hidden] { display: none; }
417
+ .rt-session .summary { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 10px; color: var(--muted); font-variant-numeric: tabular-nums; }
418
+ .rt-session > .cost { flex: none; margin-left: auto; font-variant-numeric: tabular-nums; }
419
+ .rt-session > .cost[hidden] { display: none; }
420
+ .rt-filters { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; min-width: 0; }
421
+ .rt-filters > .rt-days { flex: 1; min-width: 0; }
422
+ .rt-group-by { flex: none; }
423
+ /* Purpose is a list tool in its heading; the bar below chooses day and view. */
424
+ #view-routing .rt-req-head { height: auto; min-height: 26px; flex-wrap: wrap; }
425
+ .rt-purpose-tools { flex: none; display: inline-flex; align-items: center; margin-left: 8px; border-radius: 6px; }
426
+ .rt-purpose-tools.set { background: var(--pill); }
427
+ .rt-purpose { display: inline-flex; align-items: center; gap: 3px; max-width: 180px; height: 24px; }
428
+ .rt-purpose span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
429
+ .rt-purpose svg { flex: none; opacity: .7; transition: transform .15s var(--ease); }
430
+ .rt-purpose.open svg { transform: rotate(180deg); }
431
+ .rt-metric-pick { display: inline-flex; align-items: center; gap: 3px; height: 24px; margin-left: 6px; }
432
+ .rt-metric-pick.open svg { transform: rotate(180deg); }
433
+ .rt-fold-all { display: inline-flex; align-items: center; justify-content: center; width: 24px; height: 24px; padding: 0; margin-left: 2px; }
434
+ .pop.proto-menu.rt-metric-menu { width: 190px; max-width: calc(100vw - 16px); background: var(--card); }
435
+ .rt-metric-menu .pm-item { align-items: center; }
436
+ .rt-metric-menu .pm-tick { height: 13px; width: 13px; border: 1px solid var(--line); border-radius: 3px; }
437
+ .rt-metric-menu .on .pm-tick { border-color: var(--muted); }
438
+ .rt-metric-menu .pm-note { display: none; }
439
+ .rt-purpose-tools.set .rt-purpose { color: var(--fg-2); }
440
+ .rt-purpose-clear { display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 24px; padding: 0; }
441
+ .pop.proto-menu.rt-purpose-menu { width: 190px; max-width: calc(100vw - 16px); background: var(--card); }
442
+ .rt-purpose-menu .pm-head, .rt-purpose-menu .pm-note { display: none; }
443
+ .rt-purpose-menu .pm-item { align-items: center; padding: 7px 8px; }
444
+ .rt-purpose-menu .pm-tick { height: 13px; width: 13px; border: 1px solid var(--line); border-radius: 3px; }
445
+ .rt-purpose-menu .on .pm-tick { border-color: var(--muted); }
446
+ .rt-purpose-menu .pm-name { font-weight: 400; overflow-wrap: anywhere; }
447
+ #view-routing :where(.rt-col.max440) .rt-req-head .note { flex: 1 1 100%; order: 1; margin: 2px 0 0; }
448
+ :where(.rt-col.max440) .rt-purpose { max-width: 150px; }
449
+ /* Keep the compact row on wide lists. In a narrow column the destination
450
+ and the metrics go under the model; individual metrics stay whole. */
451
+ /* Where a request's numbers don't fit their column on one line (set by
452
+ metaFit), they go on a line of their own under where it went, which keeps
453
+ its room (#2288, xishuchkui-code). */
454
+ :where(.rt-col.meta-under:not(.max760)) .rt-reqs { --rt-tpl: auto fit-content(45%) minmax(0, 1fr); }
455
+ :where(.rt-col.meta-under:not(.max760)) .rt-req { row-gap: 2px; }
456
+ :where(.rt-col.meta-under:not(.max760)) :is(.rt-req, .rt-probe) .meta { grid-column: 2 / -1; grid-row: 2; justify-content: flex-start; }
457
+ :where(.rt-col.max760) .rt-reqs { --rt-tpl: auto minmax(0, 1fr); }
458
+ :where(.rt-col.max760) .rt-req { row-gap: 2px; }
459
+ :where(.rt-col.max760) :is(.rt-req, .rt-probe) .asked { grid-column: 2; grid-row: 1; }
460
+ :where(.rt-col.max760) :is(.rt-req, .rt-probe) .to { grid-column: 2; grid-row: 2; }
461
+ :where(.rt-col.max760) :is(.rt-req, .rt-probe) .meta { grid-column: 2; grid-row: 3; justify-content: flex-start; }
462
+ .rt-acts { padding: 4px 0; }
463
+ /* under the requests, as wide as the page, the accounts and keys flow
464
+ in columns, each provider's heading over its own (in one block with
465
+ its first row, .rt-lead): one to a line, or a heading across each
466
+ line, a page of many providers ran on far below the requests (Jiabubu
467
+ on Discord, twice). Beside the requests (min1150) the column holds one.
468
+ A column is wide enough for an account's name, plan and Try now on one
469
+ line (danzi002 on Discord), two of them still on a phone */
470
+ :where(.rt-more:not(.min1150)) .rt-acts, .rt-cols.solo .rt-acts { columns: min(240px, 40vw); column-gap: 0; }
471
+ :where(.rt-more:not(.min1150)) .rt-acts > *, .rt-cols.solo .rt-acts > * { break-inside: avoid; }
472
+ .rt-cols.solo { grid-template-columns: minmax(0, 1fr); }
473
+ .rt-reqs.none { display: block; background: none; border-color: transparent; }
474
+ .rt-reqs.none .empty-state { padding: 14px 12px 18px; font-size: 12px; line-height: 1.6; }
475
+ .rt-reqs.none .empty-state b { font-size: 13px; }
476
+ .rt-prov { display: flex; align-items: center; gap: 7px; padding: 8px 12px 4px; font-size: 12px; }
477
+ .rt-prov .ic, .rt-prov .ic img, .rt-prov .ic svg { width: 14px; height: 14px; }
478
+ .rt-prov > span:last-child { margin-left: auto; font-size: 10.5px; color: var(--muted); border: 1px solid var(--line); border-radius: 999px; padding: 0 7px; }
479
+ .rt-acts > :not(:first-child) > .rt-prov:first-child { border-top: 1px solid var(--line); margin-top: 4px; padding-top: 10px; }
480
+ /* a routing named where it can be changed: a way to where it is set */
481
+ .rt-go { cursor: pointer; }
482
+ .rt-go::after { content: " ›"; }
483
+ /* :focus-visible in rules of its own: Safari before 15.4 drops a list
484
+ holding it whole, hover included */
485
+ .rt-prov > span.rt-go:hover { color: var(--fg); border-color: var(--fg); }
486
+ .rt-prov > span.rt-go:focus-visible { color: var(--fg); border-color: var(--fg); }
487
+ .rt-hub i.rt-go:hover { box-shadow: 0 0 0 3px var(--card-2), 0 0 0 4px var(--fg); }
488
+ .rt-hub i.rt-go:focus-visible { box-shadow: 0 0 0 3px var(--card-2), 0 0 0 4px var(--fg); }
489
+ .rt-go:focus-visible { outline: none; }
490
+ .rt-found { animation: rtfound 1.4s ease-out; }
491
+ @keyframes rtfound { 0%, 40% { box-shadow: inset 0 0 0 2px var(--fg); } 100% { box-shadow: inset 0 0 0 2px transparent; } }
492
+ @media (prefers-reduced-motion: reduce) { .rt-found { animation: none; } }
493
+ /* an account or key: one cell each, alike (danzi002 on Discord: "显示的好
494
+ 乱"). Its name, plan and Try now on one line, a dot before it for how it
495
+ is; under it what is left or when it is back, how it fared on hover.
496
+ Red only for what the reader has to do (.act) */
497
+ .rt-act { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 1px 10px; padding: 6px 12px 9px 33px; font-size: 12px; line-height: 18px; }
498
+ .rt-act > * { grid-column: 1 / -1; min-width: 0; }
499
+ .rt-act > .nm { grid-column: 1; grid-row: 1; }
500
+ .rt-act > .fix { grid-column: 2; grid-row: 1; }
501
+ /* Try now no taller than the name beside it (WebKit's button is): the
502
+ cell keeps its height when the rest is lifted */
503
+ .rt-act:not(.act) > .fix { height: 18px; align-items: center; }
504
+ /* what the reader has to do has a line of its own, its words in full */
505
+ .rt-act.act > .fix { grid-column: 1 / -1; grid-row: auto; flex-wrap: wrap; white-space: normal; }
506
+ .rt-act .nm { display: flex; align-items: center; gap: 6px; min-width: 0; }
507
+ .rt-act .nm b { font-weight: 560; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
508
+ .rt-act .nm span { color: var(--muted); white-space: nowrap; }
509
+ /* narrow, the plan gives way before the name does */
510
+ .rt-act:not(.rt-keys) .nm span { flex: 0 1000 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
511
+ .rt-act .nm .dot { position: absolute; left: 19px; top: 12px; width: 6px; height: 6px; border-radius: 50%; background: color-mix(in srgb, var(--muted) 45%, transparent); }
512
+ .rt-act.on .nm .dot { background: var(--green); }
513
+ .rt-act.rest .nm .dot { background: var(--amber); }
514
+ .rt-act.act .nm .dot { background: var(--red); }
515
+ /* in full, a second line if it must: when it is back is what it says (#1016) */
516
+ .rt-act .st { color: var(--fg-2); overflow-wrap: anywhere; }
517
+ .rt-act .st:empty { display: none; }
518
+ .rt-act .st.rest, .rt-act .st.left { color: var(--muted); }
519
+ .rt-act.act .st.rest { color: var(--red); }
520
+ /* the accounts' allowances read again by hand (#1834), beside the heading */
521
+ .rt-read { display: inline-flex; align-items: center; justify-content: center; flex: none; width: 20px; height: 20px; margin: -3px 0 -3px 6px; padding: 0; border: 0; border-radius: 5px; background: none; color: var(--muted); cursor: pointer; transition: background .12s, color .12s; }
522
+ .rt-read[hidden] { display: none; }
523
+ .rt-read:hover { color: var(--fg); background: var(--pill-hover); }
524
+ .rt-read:focus-visible { color: var(--fg); background: var(--pill-hover); outline: none; }
525
+ .rt-read svg { flex: none; }
526
+ .rt-read.busy { cursor: default; color: var(--fg-2); }
527
+ .rt-read.busy svg > * { transform-box: view-box; transform-origin: 50% 50%; animation: spin .8s linear infinite; }
528
+ @media (prefers-reduced-motion: reduce) { .rt-read.busy svg > * { animation: none; } }
529
+ /* a subscription account's windows (#1833), one line where it has no
530
+ figure of its own, the rest of it on hover */
531
+ .rt-act .wins { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--fg-2); font-size: 11px; font-variant-numeric: tabular-nums; }
532
+ .rt-act .wins span + span { margin-left: 12px; }
533
+ .rt-act .tally { display: flex; gap: 0 10px; overflow: hidden; white-space: nowrap; color: var(--muted); font-size: 11px; font-variant-numeric: tabular-nums; }
534
+ .rt-act .tally > span { flex: none; }
535
+ .rt-act .tally > span:last-child { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
536
+ /* with a line of its own, how it fares is on hover; one to fix says it */
537
+ .rt-act.said > .tally { display: none; }
538
+ .rt-act.act .tally .bad { color: var(--red); }
539
+ .rt-act .fix { display: flex; gap: 10px; font-size: 11px; white-space: nowrap; }
540
+ .rt-act .fix button.link { color: var(--muted); }
541
+ .rt-act .fix button.link:hover { color: var(--fg); }
542
+ .rt-act .fix button.link:focus-visible { color: var(--fg); }
543
+ .rt-act .fix button.rt-signin, .rt-act.act .fix button.link { color: var(--red); }
544
+ /* under the cell, in its bottom padding: every cell keeps its height. In
545
+ the flow, not absolute: Chromium put an absolute bar of a row in a
546
+ column's unbroken block (.rt-lead) over the row above it */
547
+ .rt-act .bar { height: 2px; margin: 2px 0 -4px; border-radius: 2px; background: var(--card-2); overflow: hidden; }
548
+ .rt-act:has(> .bar) { padding-bottom: 8px; }
549
+ .rt-act .bar i { display: block; height: 100%; border-radius: 2px; background: var(--muted); }
550
+ /* a provider's keys summed up in one row (#1319), opened to each */
551
+ .rt-act.rt-keys { cursor: pointer; user-select: none; }
552
+ .rt-act.rt-keys .chev { display: inline-flex; align-self: center; margin-left: auto; color: var(--muted); transition: transform .2s; }
553
+ .rt-act.rt-keys:hover .chev { color: var(--fg); }
554
+ .rt-act.rt-keys.open .chev { transform: rotate(180deg); }
555
+ .rt-act.rt-keys:focus { outline: none; }
556
+ .rt-act.rt-keys:focus-visible { box-shadow: inset 0 0 0 2px color-mix(in srgb, var(--accent) 30%, transparent); }
557
+ .rt-livedot { width: 6px; height: 6px; margin-left: 7px; border-radius: 50%; background: var(--green); --fg: var(--green); }
558
+ /* it pulses while a request is under way only (#2827) */
559
+ @media (prefers-reduced-motion: reduce) { .rt-livedot.on::after { animation: none; } }
560
+ /* routing groups, and the providers routing over several of their own */
561
+ .rt-gsec { display: grid; grid-template-columns: minmax(0, 1fr); gap: 6px; }
562
+ .rt-gsec .row-head .note { margin-right: 6px; }
563
+ /* the groups filtered by name and model, beside Select and New group */
564
+ .rt-gsec .rt-gfilter { width: 190px; min-width: 0; flex: 0 1 190px; height: 24px; margin: 0 6px 0 4px; }
565
+ .rt-gsec .rt-pools { margin-bottom: 0; }
566
+ .rt-gsec .row-head + .list + .row-head { margin-top: 8px; }
567
+ .rt-groups > *, .rt-pools > * { border-top: 1px solid var(--line-2); }
568
+ .rt-groups > :first-child, .rt-pools > :first-child { border-top: 0; }
569
+ .rt-group { display: flex; align-items: center; gap: 12px; padding: 10px 12px 10px 14px; cursor: pointer; transition: background .12s; }
570
+ .rt-group:hover { background: var(--card-2); }
571
+ .rt-group.off { opacity: .6; }
572
+ /* switched off as a whole: its switch, its "switched off" tag and its
573
+ grey logos say so, and its words stay readable (jiabubu on Discord: at
574
+ half opacity they were too faint to read), the name a step quieter than
575
+ an on group's and not dimmed again by .off */
576
+ .rt-group.disabled { opacity: 1; }
577
+ .rt-group.disabled .nm b { color: var(--fg-2); font-weight: 500; }
578
+ .rt-group.disabled > .ics, .rt-group.disabled > .rt-ghandle { filter: grayscale(1); opacity: .6; }
579
+ .rt-group .rt-gon { flex: none; transform: scale(.85); }
580
+ .rt-group .ics { display: flex; gap: 3px; flex: none; min-width: 18px; }
581
+ .rt-group .ics .ic { width: 16px; height: 16px; }
582
+ /* a group's logos are its handle (#779), as an agent row's logo is: the
583
+ grip in the row's left margin is drawn only while the row is under the
584
+ pointer, dragged, or its handle has the keyboard or its menu open —
585
+ always there it is clutter, as on the Agents page. .ag-handle is in the
586
+ selector so it outweighs body.window .ag-handle, whose 28px held a stack
587
+ of logos in, spilling it over the card's left edge onto the name (ARNO
588
+ on Discord) */
589
+ .rt-group .rt-ghandle.ag-handle { position: relative; align-items: center; justify-content: center; width: auto; min-width: 24px; height: 24px; padding: 0 3px; }
590
+ .rt-group .rt-ghandle::before { content: ""; position: absolute; left: -7px; top: 50%; width: 5px; height: 11px; margin-top: -5.5px; background: radial-gradient(circle, var(--muted) .9px, transparent 1.2px) 0 0 / 3.5px 3.75px; opacity: 0; transition: opacity .15s; }
591
+ .rt-group:is(:hover, .dragging) .rt-ghandle::before, .rt-group .rt-ghandle.open::before { opacity: 1; }
592
+ /* apart: Safari before 15.4 drops a whole selector list with :focus-visible in it */
593
+ .rt-group .rt-ghandle:focus-visible::before { opacity: 1; }
594
+ /* while a row is dragged the others slide out of its way */
595
+ .rt-groups.sorting { user-select: none; -webkit-user-select: none; }
596
+ .rt-groups.sorting > .rt-group { transition: transform .22s cubic-bezier(.22, 1, .36, 1); }
597
+ .rt-groups.sorting .rt-ghandle { cursor: grabbing; }
598
+ .rt-groups > .rt-group.dragging { position: relative; z-index: 2; background: var(--card); box-shadow: 0 6px 18px rgba(20, 22, 30, .12), 0 0 0 1px var(--line); border-radius: 8px; transition: none; }
599
+ @media (prefers-reduced-motion: reduce) { .rt-groups.sorting > .rt-group { transition: none; } }
600
+ .rt-group .main { flex: 1; min-width: 0; }
601
+ .rt-group .nm { display: flex; align-items: center; gap: 8px; min-width: 0; font-size: 13px; }
602
+ .rt-group .nm b { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
603
+ .rt-group .nm code.mdl { font: var(--code-style, normal) 500 11px/1.5 var(--code-font, var(--code)); font-stretch: var(--code-stretch, 100%); color: var(--fg-2); background: var(--card-2); border-radius: 5px; padding: 0 5px; white-space: nowrap; }
604
+ .rt-group .nm small.auto { font-size: 11px; color: var(--faint); white-space: nowrap; }
605
+ /* the check a group just saved shows by its name (savedMark) */
606
+ .rt-group .nm .rt-gsaved { display: inline-grid; place-items: center; width: 16px; height: 16px; flex: none; border-radius: 50%; background: var(--accent); color: #fff; }
607
+ /* Save, while the gateway saves: a small spinner, as iOS's */
608
+ .rt-gedit button.primary.busy { opacity: .75; display: inline-flex; align-items: center; gap: 6px; }
609
+ .rt-gedit button.primary.busy::before { content: ""; width: 10px; height: 10px; border-radius: 50%; border: 1.6px solid currentColor; border-right-color: transparent; animation: spin .7s linear infinite; }
610
+ @media (prefers-reduced-motion: reduce) { .rt-gedit button.primary.busy::before { animation-duration: 2s; } }
611
+ /* what a group's card and its editor say can be selected and copied
612
+ (jiabubu on Discord): its name and id, its models, its patterns, what
613
+ is left of its accounts; a drag across them selects, a click still
614
+ opens the editor (groupRow). Its controls stay controls. */
615
+ .rt-groups:not(.sorting) > .rt-group :is(.nm b, .nm code.mdl, .mem),
616
+ .rt-gedit .fallback:not(.rt-rules) > .fbl:not(.sorting) > .fbrow > .n, .rt-gaccts > .fbrow > .n { user-select: text; -webkit-user-select: text; }
617
+ .rt-group .mem { margin-top: 2px; font-size: 11.5px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
618
+ /* a pattern the group finds models by, and how many it matches now */
619
+ .rt-group .mem.pat code, .rt-pats code { font: var(--code-style, normal) 500 11px/1.5 var(--code-font, var(--code)); font-stretch: var(--code-stretch, 100%); color: var(--fg-2); }
620
+ .rt-group .mem.pat.none, .rt-pats .fbrow.none small { color: var(--amber); }
621
+ /* a classifier that failed (#1929): a dot, never a stripe */
622
+ .rt-group .mem.clsfail { color: var(--amber); }
623
+ .rt-group .mem.clsfail .dot { display: inline-block; width: 6px; height: 6px; border-radius: 50%; background: currentColor; margin-right: 6px; vertical-align: 1px; }
624
+ .rt-patadd { display: flex; gap: 6px; align-items: center; }
625
+ .rt-pats .fbl:not(:empty) + .rt-patadd { margin-top: 4px; }
626
+ .rt-patadd input { flex: 1; min-width: 0; }
627
+ /* the tags take up to half the row, onto more lines rather than pushing
628
+ Edit and the switch out of it (young02036 on Discord) */
629
+ .rt-group .tags { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 4px; flex: 0 1 auto; min-width: 0; max-width: 50%; }
630
+ .rt-group .tag { max-width: 100%; overflow: hidden; text-overflow: ellipsis; font-size: 11px; line-height: 18px; padding: 0 8px; border-radius: 999px; box-shadow: inset 0 0 0 1px var(--line); color: var(--muted); white-space: nowrap; }
631
+ .rt-group .tag.bad { color: var(--red); }
632
+ .rt-group .tag.idle { opacity: .55; }
633
+ /* the routing tag is a pop-up button for the group's routings (#3450) */
634
+ .rt-group button.tag.rt-groute { display: inline-flex; align-items: center; gap: 3px; height: auto; margin: 0; padding: 0 6px 0 8px; border: 0; background: transparent; font: inherit; font-size: 11px; line-height: 18px; color: var(--muted); cursor: pointer; transition: color .12s, background .12s; }
635
+ .rt-group button.tag.rt-groute svg { flex: none; opacity: .7; }
636
+ .rt-group button.tag.rt-groute:hover, .rt-group button.tag.rt-groute.open { color: var(--fg); background: var(--card-2); }
637
+ .rt-group button.tag.rt-groute:focus-visible { outline: none; box-shadow: inset 0 0 0 1px var(--accent), 0 0 0 3px color-mix(in srgb, var(--accent) 30%, transparent); }
638
+ /* a manual group's members on its card: the one picked, marked by a dot */
639
+ .rt-picks { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 5px; }
640
+ .rt-pick { display: inline-flex; align-items: center; gap: 5px; max-width: 100%; min-width: 0; height: 22px; padding: 0 8px 0 6px; border: 1px solid var(--line); border-radius: 999px; background: transparent; color: var(--muted); font: inherit; font-size: 11.5px; cursor: pointer; transition: background .12s, color .12s, border-color .12s; }
641
+ .rt-pick:hover { color: var(--fg); border-color: var(--muted); background: var(--card); }
642
+ .rt-pick .dot { width: 7px; height: 7px; flex: none; border-radius: 50%; box-shadow: inset 0 0 0 1.5px var(--faint); }
643
+ .rt-pick .ic, .rt-pick .ic-stack { width: 13px; height: 13px; flex: none; }
644
+ .rt-pick .n { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
645
+ .rt-pick small { color: var(--faint); font-size: 10.5px; white-space: nowrap; }
646
+ .rt-pick.on { color: var(--fg); border-color: var(--accent); background: color-mix(in srgb, var(--accent) 10%, transparent); cursor: default; }
647
+ .rt-pick.on .dot { background: var(--accent); box-shadow: none; }
648
+ .rt-pick.off { opacity: .55; }
649
+ .rt-pick:focus-visible { outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 30%, transparent); }
650
+ .rt-gnone { padding: 14px; font-size: 12px; line-height: 1.5; }
651
+ /* the routing groups switched off: where they were, a line of why and the
652
+ way back on */
653
+ .rt-goff { display: grid; gap: 6px; }
654
+ .rt-goffbox { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; padding: 12px 14px; }
655
+ .rt-goffwhy { flex: 1 1 260px; min-width: 0; font-size: 12px; line-height: 1.5; color: var(--fg-2); }
656
+ .rt-goffon { flex: none; }
657
+ .rt-gfound, .rt-gnames { display: flex; align-items: center; gap: 12px; padding: 2px 4px 4px; }
658
+ .rt-gfound .txt, .rt-gnames .txt { flex: 1; min-width: 0; display: grid; gap: 2px; }
659
+ .rt-gfound b, .rt-gnames b { font-size: 12.5px; font-weight: 600; }
660
+ .rt-gfound small, .rt-gnames small { font-size: 11.5px; line-height: 1.45; color: var(--muted); }
661
+ /* the names setting's three choices go under its words when it is narrow */
662
+ .rt-gnames { flex-wrap: wrap; }
663
+ /* the providers kept out of found groups: a text button under the
664
+ switch's words, its menu the app's */
665
+ .rt-gfound .rt-skip-pick { justify-self: start; display: inline-flex; align-items: center; gap: 3px; max-width: 100%; height: 22px; margin-left: -7px; font-size: 11.5px; }
666
+ .rt-skip-pick span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
667
+ .rt-skip-pick svg { flex: none; opacity: .7; transition: transform .15s var(--ease); }
668
+ .rt-skip-pick.open svg { transform: rotate(180deg); }
669
+ .pop.proto-menu.rt-skip-menu { width: 230px; max-width: calc(100vw - 16px); background: var(--card); }
670
+ .rt-gnames .txt { flex-basis: 220px; }
671
+ .rt-ghidden { display: flex; align-items: center; padding: 4px 12px 4px 8px; }
672
+ .rt-ghidden .rt-gremoved { display: inline-flex; align-items: center; gap: 4px; font-size: 11.5px; color: var(--faint); }
673
+ .rt-ghidden .rt-gremoved:hover, .rt-ghidden .rt-gremoved.open { color: var(--fg-2); }
674
+ .rt-gsel { display: flex; align-items: center; gap: 10px; padding: 8px 12px 8px 14px; }
675
+ .rt-group.rt-gpick { cursor: default; }
676
+ .rt-group.rt-gpick.on { background: var(--card-2); }
677
+ .rt-gedit { cursor: default; scroll-margin-top: 12px; }
678
+ /* the group being edited reads as selected among the others: a soft tint
679
+ and an accent ring, as a list marks its selected row; its Save and
680
+ Cancel stay at the bottom of the window while its rows scroll by, at a
681
+ push button's size (Jiabubu on Discord). The list clips instead of
682
+ hiding its overflow, so the bar can stick. */
683
+ .list.rt-groups { overflow: hidden; overflow: clip; }
684
+ .rt-groups > .rt-gedit { --ged-bg: color-mix(in srgb, var(--accent) 4%, var(--card)); background: var(--ged-bg); box-shadow: inset 0 0 0 1.5px color-mix(in srgb, var(--accent) 60%, transparent); }
685
+ .rt-groups > .rt-gedit:first-child { border-radius: 11px 11px 0 0; }
686
+ .rt-groups > .rt-gedit:last-child { border-radius: 0 0 11px 11px; }
687
+ .rt-groups > .rt-gedit:only-child { border-radius: 11px; }
688
+ .rt-groups > .rt-gedit + * { border-top-color: transparent; }
689
+ /* the editor's Cancel and Add stay in sight, the bar's ground down to the
690
+ window's edge: the view's own foot padding (.view, body.window and the
691
+ phone page's in app.css) under it had shown as a gap (Jiabubu on Discord) */
692
+ .rt-gedit > .bar { position: sticky; bottom: calc(-1 * var(--vfoot, 4px)); z-index: 3; background: var(--ged-bg, var(--card)); padding-bottom: calc(10px + var(--safe, 0px)); margin-bottom: calc(-10px - var(--safe, 0px)); }
693
+ body.window .rt-gedit > .bar { --vfoot: 8px; }
694
+ @media (max-width: 760px), (pointer: coarse) and (max-width: 1024px) { body.web .rt-gedit > .bar { --vfoot: max(12px, env(safe-area-inset-bottom)); --safe: env(safe-area-inset-bottom, 0px); } }
695
+ .rt-gedit > .bar > button.text:not(.danger) { height: 28px; padding: 0 12px; border-radius: 7px; font-size: 13px; }
696
+ .rt-gedit .ehead.folds { cursor: pointer; }
697
+ .rt-gedit .ehead.folds:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
698
+ .rt-gedit .segs { max-width: 100%; }
699
+ /* its hint as close under the choices as every other row's: 4px */
700
+ .rt-gedit .ctx-wrap { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; }
701
+ .rt-gedit .ctx-wrap .hint { flex-basis: 100%; margin-top: 0; }
702
+ .rt-gedit .gsuffix { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; }
703
+ .rt-gedit .gsuffix .hint { overflow-wrap: anywhere; }
704
+ .rt-gedit .ctx-wrap input.ctx-in { width: 96px; flex: none; }
705
+ .rt-gedit .ctx-wrap input.first-in { width: 64px; }
706
+ .rt-gedit .ctx-wrap .first-unit { margin-left: -2px; color: var(--muted); font-size: 12px; }
707
+ .rt-gedit .ctx-wrap .first-unit[hidden] { display: none; }
708
+ .rt-pool { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; padding: 10px 12px 10px 14px; }
709
+ .rt-pool .nm { flex: 1 1 12em; display: flex; align-items: center; gap: 8px; min-width: 0; font-size: 13px; }
710
+ .rt-pool .nm .ic { width: 16px; height: 16px; }
711
+ .rt-pool .nm b { font-weight: 600; }
712
+ .rt-pool .nm span:last-child { font-size: 12px; color: var(--muted); }
713
+ /* a label column and a control column, so Routing's and Stays' segments
714
+ start at one edge whatever their labels' widths */
715
+ .rt-pool .ctl { display: grid; grid-template-columns: auto minmax(0, 1fr); align-items: center; justify-items: start; gap: 6px 8px; min-width: 0; max-width: 100%; }
716
+ .rt-pool .segs { max-width: 100%; flex-wrap: wrap; }
717
+ .rt-pool .lab { display: contents; }
718
+ .rt-pool .lab small { font-size: 11px; color: var(--muted); }
719
+ /* narrow, a row keeps only its routing, which switches from here */
720
+ @media (max-width: 560px) { body:not(.web) .rt-group .tags > :not(.rt-groute) { display: none; } }
721
+ @media (max-width: 760px), (pointer: coarse) and (max-width: 1024px) {
722
+ body.web .rt-group {
723
+ display: grid; grid-template-columns: auto minmax(0, 1fr) auto; /* the logos as wide as they are */
724
+ gap: 6px 8px; padding: 12px 12px 12px 14px; /* the grip's room, as wide */
725
+ }
726
+ body.web .rt-group .tags { justify-content: flex-start; max-width: none; grid-column: 2 / -1; grid-row: 2; }
727
+ body.web .rt-group > .text { grid-column: 3; grid-row: 1; }
728
+ body.web .rt-group .nm { flex-wrap: wrap; }
729
+ body.web .rt-group .mem { white-space: normal; overflow-wrap: anywhere; }
730
+ body.web .rt-pick { height: auto; min-height: 44px; padding: 8px; font-size: 12.5px; }
731
+ body.web .rt-pick { flex-wrap: wrap; }
732
+ body.web .rt-gedit { grid-template-columns: minmax(0, 1fr); gap: 6px; padding: 12px; }
733
+ body.web .rt-gedit label { text-align: left; padding-top: 8px; }
734
+ body.web .rt-gedit .segs { flex-wrap: wrap; }
735
+ body.web .rt-gedit .fbrow { flex-wrap: wrap; justify-content: normal; }
736
+ body.web .rt-gedit .fbrow .n { flex: 1 1 calc(100% - 60px); white-space: normal; overflow-wrap: anywhere; }
737
+ body.web .rt-gedit .fbrow > .grow { display: none; }
738
+ body.web .rt-gedit .fbrow .text { min-width: 44px; }
739
+ body.web .rt-cond { height: auto; min-height: 44px; }
740
+ body.web .rt-gadd { min-height: 44px; }
741
+ body.web .rt-pool .ctl { grid-template-columns: minmax(0, 1fr); }
742
+ body.web .rt-pool .lab small { padding-top: 6px; }
743
+ body.web .rt-stats { flex-wrap: wrap; gap: 10px 16px; }
744
+ /* the upstream a request went to takes a line of its own on a phone,
745
+ so the reasoning beside the provider isn't cut to two letters */
746
+ body.web :is(.rt-req, .rt-probe) .to.upstreamed { flex-wrap: wrap; row-gap: 0; }
747
+ body.web :is(.rt-req, .rt-probe) .to.upstreamed .said { flex: 1 1 0; min-width: 0; }
748
+ body.web :is(.rt-req, .rt-probe) .to .upstream { flex-basis: 100%; padding-left: 12px; }
749
+ body.web #view-routing > .row-head .note { flex: 1 1 100%; order: 1; margin: 0; }
750
+ }
751
+ .rt-gsec .segs .opt { white-space: nowrap; }
752
+ /* the groups, in a page 520px wide or less */
753
+ :where(.rt-gpage.max520) .rt-gsec .row-head .note { display: none; }
754
+ :where(.rt-gpage.max520) .rt-gsec .rt-gfilter { flex: 1 1 auto; width: auto; }
755
+ :where(.rt-gpage.max520) .rt-gedit { grid-template-columns: minmax(0, 1fr); gap: 4px; }
756
+ :where(.rt-gpage.max520) .rt-gedit label { text-align: left; padding-top: 8px; }
757
+ :where(.rt-gpage.max520) .rt-gedit .segs .opt { padding-left: 9px; padding-right: 9px; }
758
+ /* the models and patterns take the column's width and no more: a grid's
759
+ auto track grows to its widest row's content, so a long model name with
760
+ its chips pushed the rows past the editor's edge, cut off there, and
761
+ the patterns below no longer lined up with them. Tight, the name gives
762
+ way (it ellipsizes, to 8em), and tighter still (German's longer words)
763
+ the chips go on a line of their own, right-aligned, rather than past
764
+ the edge; none of them is ever cut */
765
+ .rt-gedit .fallback, .rt-gedit .fallback:not(.rt-rules) .fbl { grid-template-columns: minmax(0, 1fr); }
766
+ /* in :where, so a phone's own row (body.web, above) still wins */
767
+ :where(.rt-gedit .fallback:not(.rt-rules)) .fbrow { flex-wrap: wrap; justify-content: flex-end; row-gap: 2px; }
768
+ :where(.rt-gedit .fallback:not(.rt-rules)) .fbrow .n { flex: 1 1 8em; }
769
+ .rt-gedit .fbrow .n { display: flex; align-items: baseline; gap: 6px; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
770
+ .rt-gedit .fbrow .n > * { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
771
+ .rt-gedit .fbrow .n > small { flex: 0 100 auto; } /* gives way before the name */
772
+ .rt-gedit .fbrow .n small { color: var(--muted); font-size: 11.5px; }
773
+ .rt-gedit .fbrow.off .n { color: var(--muted); text-decoration: line-through; }
774
+ .rt-gedit .fbrow .rt-mon { transform: scale(.85); }
775
+ /* a named member's number is its handle: the grip beside it shows on
776
+ hover, dragged or from the keyboard only, as on the groups' cards */
777
+ .rt-gedit .fbrow .rt-mhandle.ag-handle { position: relative; width: 18px; height: 22px; margin: 0 -2px; font: inherit; color: var(--muted); font-variant-numeric: tabular-nums; }
778
+ .rt-gedit .fbrow .rt-mhandle::before { content: ""; position: absolute; left: -7px; top: 50%; width: 5px; height: 11px; margin-top: -5.5px; background: radial-gradient(circle, var(--muted) .9px, transparent 1.2px) 0 0 / 3.5px 3.75px; opacity: 0; transition: opacity .15s; }
779
+ .rt-gedit .fbrow:is(:hover, .dragging) .rt-mhandle::before { opacity: 1; }
780
+ /* apart: Safari before 15.4 drops a whole selector list with :focus-visible in it */
781
+ .rt-gedit .fbrow .rt-mhandle:focus-visible::before { opacity: 1; }
782
+ .rt-gedit .fbl.sorting { user-select: none; -webkit-user-select: none; }
783
+ .rt-gedit .fbl.sorting > .fbrow { transition: transform .22s cubic-bezier(.22, 1, .36, 1); }
784
+ .rt-gedit .fbl.sorting .rt-mhandle { cursor: grabbing; }
785
+ .rt-gedit .fbl > .fbrow.dragging { position: relative; z-index: 2; background: var(--card); box-shadow: 0 6px 18px rgba(20, 22, 30, .12), 0 0 0 1px var(--line); border-radius: 8px; transition: none; }
786
+ @media (prefers-reduced-motion: reduce) { .rt-gedit .fbl.sorting > .fbrow { transition: none; } }
787
+ .rt-gedit .fbrow.muted > :not(.rt-mon):not(.text) { opacity: .45; }
788
+ /* a member's own reasoning, beside its Up and Remove */
789
+ .rt-gedit .fbrow .rt-fixed { height: 24px; padding: 0 9px; font-size: 11.5px; flex: none; }
790
+ /* a member's weight under By weight (#1910), edited in place */
791
+ .rt-gedit .fbrow .rt-weight { font-variant-numeric: tabular-nums; }
792
+ .rt-gedit .fbrow .rt-weight-in { width: 64px; flex: none; height: 24px; font-size: 12px; padding: 0 6px; }
793
+ /* a group a member of a group is: it takes no reasoning of its own (its
794
+ models reason as it says, provider.SaveGroup), so its chip names the group
795
+ it follows and opens that group's card instead. It takes the room it needs
796
+ and gives it back when the row is tight, rather than widening it: the
797
+ desktop app's member row doesn't wrap (only body.web's does) */
798
+ .rt-gedit .fbrow .rt-inner { flex: 0 1 auto; min-width: 0; }
799
+ .rt-gedit .fbrow .rt-inner > span { overflow: hidden; text-overflow: ellipsis; }
800
+ /* a group's account choice (Group.Accounts): a long account name ellipsizes */
801
+ .rt-gedit .fbrow .rt-gacct { flex: 0 1 auto; min-width: 0; }
802
+ .rt-gedit .fbrow .rt-gacct > span { overflow: hidden; text-overflow: ellipsis; }
803
+ .rt-gedit > .rt-gacc[hidden] { display: none; }
804
+ .rt-gadd {
805
+ justify-self: start; display: inline-flex; align-items: center; gap: 6px;
806
+ height: 28px; padding: 0 11px 0 9px; border-radius: 8px;
807
+ border: 1px dashed var(--line); background: transparent; color: var(--muted);
808
+ font: inherit; font-size: 12.5px; cursor: pointer;
809
+ transition: color .15s, border-color .15s, background .15s;
810
+ }
811
+ .rt-gadd:hover, .rt-gadd.open { color: var(--fg); border-color: var(--muted); background: var(--card); }
812
+ .rt-gadd svg { flex: none; }
813
+ /* Add a rule, and beside it a rule by what the message asks for */
814
+ .rt-gadds { display: flex; flex-wrap: wrap; gap: 6px; }
815
+ .rt-gadds > [hidden] { display: none; }
816
+ /* Add a model, and beside it Sort by weight while it would move something */
817
+ .rt-madds { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
818
+ .rt-madds > [hidden] { display: none; }
819
+ /* a group's rules, each one line read as a sentence: the conditions it
820
+ has, → and the model it sends that turn to first; its acts under ⋯ */
821
+ .rt-rules .fbl:not(:empty) + .rt-gadds { margin-top: 2px; }
822
+ /* every rule's → and model start at one x: the number, the conditions
823
+ (as wide as the widest rule's, to at most half the width before they
824
+ wrap), what it does, the ⋯. Each rule is a grid of its own, its
825
+ conditions at least --rt-condw wide, the widest rule's conditions as
826
+ measured (fitConds). The rules were a subgrid of the list once, the way
827
+ Safari 16 crashed sizing the requests (#3731, #3955) */
828
+ .rt-rules .fbl { display: flex; flex-direction: column; }
829
+ .rt-rule { display: grid; grid-template-columns: 16px fit-content(50%) minmax(0, 1fr) auto; align-items: start; column-gap: 8px; padding: 7px 0; border-top: 1px solid var(--line); font-size: 12.5px; }
830
+ .rt-rule > .rt-line.when { min-width: var(--rt-condw, 0); }
831
+ .rt-rule:first-child { border-top: 0; padding-top: 2px; }
832
+ .rt-rule > .i { line-height: 26px; color: var(--muted); font-size: 11.5px; font-variant-numeric: tabular-nums; text-align: right; }
833
+ /* a rule's number is its handle, as a model's is: the grip beside it
834
+ shows on hover, dragged or from the keyboard only */
835
+ .rt-rule > .rt-rhandle.ag-handle { position: relative; width: 16px; height: 26px; padding: 0; margin: 0; font: inherit; font-size: 11.5px; line-height: 26px; color: var(--muted); font-variant-numeric: tabular-nums; text-align: right; }
836
+ .rt-rule > .rt-rhandle::before { content: ""; position: absolute; left: -5px; top: 50%; width: 5px; height: 11px; margin-top: -5.5px; background: radial-gradient(circle, var(--muted) .9px, transparent 1.2px) 0 0 / 3.5px 3.75px; opacity: 0; transition: opacity .15s; }
837
+ .rt-rule:is(:hover, .dragging) > .rt-rhandle::before { opacity: 1; }
838
+ /* apart: Safari before 15.4 drops a whole selector list with :focus-visible in it */
839
+ .rt-rule > .rt-rhandle:focus-visible::before { opacity: 1; }
840
+ .rt-rules > .fbl.sorting { user-select: none; -webkit-user-select: none; }
841
+ .rt-rules > .fbl.sorting > .rt-rule { transition: transform .22s cubic-bezier(.22, 1, .36, 1); }
842
+ .rt-rules > .fbl.sorting .rt-rhandle { cursor: grabbing; }
843
+ .rt-rules > .fbl > .rt-rule.dragging { position: relative; z-index: 2; background: var(--card); box-shadow: 0 6px 18px rgba(20, 22, 30, .12), 0 0 0 1px var(--line); border-radius: 8px; transition: none; }
844
+ @media (prefers-reduced-motion: reduce) { .rt-rules > .fbl.sorting > .rt-rule { transition: none; } }
845
+ .rt-line { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; min-width: 0; }
846
+ /* on a narrow page what it does goes under the conditions */
847
+ @media (max-width: 640px) {
848
+ .rt-rule { grid-template-columns: 16px minmax(0, 1fr) auto; row-gap: 6px; }
849
+ .rt-rule > .rt-line.when { min-width: 0; }
850
+ .rt-rule > .rt-then { grid-column: 2; grid-row: 2; }
851
+ .rt-rule > .rt-rmore { grid-column: 3; grid-row: 1; }
852
+ }
853
+ .rt-arrow { color: var(--muted); font-size: 12px; padding: 0 1px; }
854
+ /* a set condition, and the × on its corner that takes it off: shown on
855
+ hover or focus, always where nothing hovers */
856
+ .rt-c { position: relative; display: inline-flex; align-items: center; gap: 6px; min-width: 0; max-width: 100%; }
857
+ .rt-cx {
858
+ position: absolute; top: -5px; right: -5px; z-index: 1; display: grid; place-items: center;
859
+ width: 16px; height: 16px; padding: 0; border-radius: 50%; border: 1px solid var(--line);
860
+ background: var(--card); color: var(--muted); cursor: pointer;
861
+ opacity: 0; pointer-events: none; transition: opacity .12s, color .12s;
862
+ }
863
+ .rt-cx:hover { color: var(--fg); border-color: var(--muted); }
864
+ .rt-c:hover > .rt-cx, .rt-c:focus-within > .rt-cx { opacity: 1; pointer-events: auto; }
865
+ @media (hover: none) { .rt-cx { opacity: 1; pointer-events: auto; } }
866
+ /* the quiet + that adds a condition (in words while the rule has none),
867
+ and the one that adds the rule's next model */
868
+ .rt-addc, .rt-adduse {
869
+ display: inline-grid; grid-auto-flow: column; place-items: center; gap: 5px;
870
+ width: 24px; height: 24px; padding: 0; border-radius: 999px; border: 1px solid transparent;
871
+ background: transparent; color: var(--muted); font: inherit; font-size: 12px; cursor: pointer;
872
+ transition: color .15s, border-color .15s, background .15s;
873
+ }
874
+ .rt-addc.wide { width: auto; height: 26px; padding: 0 10px 0 8px; border: 1px dashed var(--line); }
875
+ .rt-addc:hover, .rt-addc.open, .rt-addc:focus-visible, .rt-adduse:hover, .rt-adduse.open, .rt-adduse:focus-visible { color: var(--fg); background: var(--card-2); }
876
+ .rt-addc.wide:hover, .rt-addc.wide.open { border-color: var(--muted); }
877
+ /* the rule's ⋯, always drawn, in the icons' quiet grey: it is the only
878
+ way to move or remove a rule, and drawn on hover alone nobody found it
879
+ (ppBruce on Discord: "a rule added has no delete button") */
880
+ .rt-rule > .rt-rmore { align-self: start; }
881
+ .rt-rule > .rt-rmore.open { background: var(--pill); color: var(--fg); }
882
+ /* why Save refused the rule, under it until it has a condition */
883
+ .rt-rule > .rt-rneed { grid-column: 2 / -1; color: var(--amber, var(--muted)); font-size: 11.5px; }
884
+ .rt-rule > .rt-rneed[hidden] { display: none; }
885
+ /* how a part works, under an ⓘ beside it */
886
+ .rt-info { display: inline-grid; place-items: center; width: 22px; height: 22px; border-radius: 50%; color: var(--muted); cursor: help; outline: none; }
887
+ .rt-info:hover, .rt-info:focus-visible { color: var(--fg); }
888
+ .rt-info:focus-visible { box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 40%, transparent); }
889
+ .rt-info[hidden] { display: none; }
890
+ .rt-gadds { align-items: center; }
891
+ .rt-pickrow { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
892
+ .rt-cond {
893
+ display: inline-flex; align-items: center; gap: 5px; height: 26px; padding: 0 10px; border-radius: 999px;
894
+ border: 1px dashed var(--line); background: transparent; color: var(--muted);
895
+ font: inherit; font-size: 12px; cursor: pointer; white-space: nowrap; max-width: 100%;
896
+ transition: color .15s, border-color .15s, background .15s;
897
+ }
898
+ .rt-cond:hover { color: var(--fg); border-color: var(--muted); }
899
+ .rt-cond.on { border-style: solid; color: var(--fg); background: var(--card-2); }
900
+ /* a chip in a Settings row too, where .row .ic makes icons 18–22px (duanckham on X) */
901
+ .rt-cond .ic, .row .rt-cond .ic, body.window .row .rt-cond .ic { width: 14px; height: 14px; flex: none; }
902
+ .rt-cond > span { overflow: hidden; text-overflow: ellipsis; }
903
+ /* a group's logos overlap in discs whose rings sit outside them: clipped, the first lost its edge */
904
+ .rt-cond > .ic-stack { overflow: visible; }
905
+ .rt-cond.tk { cursor: text; }
906
+ .rt-cond.in { cursor: text; }
907
+ .rt-cond.fd { cursor: text; }
908
+ .rt-cond.fd input, .editor .rt-cond.fd input[type="text"] { width: 24ch; }
909
+ .rt-cond.xf { cursor: text; }
910
+ .rt-cond.xf input, .editor .rt-cond.xf input[type="text"] { width: 24ch; }
911
+ /* a narrow line squeezes what was typed (it scrolls in its box), never
912
+ the word that says what the box is */
913
+ .rt-cond.in > span:not(.rt-grow), .rt-cond.fd > span, .rt-cond.xf > span { flex: none; }
914
+ .rt-cond.fd > input, .rt-cond.xf > input { min-width: 0; flex: 0 1 auto; }
915
+ /* the intent, a sentence, shown whole: its box as wide as the words and
916
+ onto more lines where the line ends. A hidden copy of the words, in the
917
+ same cell and font, gives the box its size; the pill rounds less once
918
+ it is more than one line */
919
+ .rt-cond.in { height: auto; min-height: 26px; padding-top: 2px; padding-bottom: 2px; border-radius: 13px; white-space: normal; }
920
+ .rt-cond.in > span:first-child { align-self: flex-start; line-height: 20px; }
921
+ .rt-cond > .rt-grow { align-self: flex-start; display: inline-grid; flex: 0 1 auto; min-width: min(6ch, 100%); max-width: 100%; overflow: visible; }
922
+ .rt-grow::after, .rt-grow > textarea, .editor .rt-grow > textarea {
923
+ grid-area: 1 / 1; box-sizing: border-box; margin: 0; padding: 0 2px; border: 0; min-width: 0;
924
+ font: inherit; line-height: 20px; white-space: pre-wrap; overflow-wrap: anywhere;
925
+ }
926
+ .rt-grow::after { content: attr(data-v) " "; visibility: hidden; }
927
+ .rt-grow > textarea, .editor .rt-grow > textarea {
928
+ width: 100%; height: 100%; min-height: 20px; resize: none; overflow: hidden;
929
+ background: transparent; color: inherit; outline: none; box-shadow: none; border-radius: 0;
930
+ }
931
+ @media (max-width: 760px), (pointer: coarse) and (max-width: 1024px) {
932
+ /* a phone's page sets its boxes at 16px; a rule's fields keep the line's
933
+ own size (iOS gets maximum-scale=1 from layout.js, so focus doesn't zoom) */
934
+ body.web .rt-cond input:not([type="range"]):not([type="checkbox"]):not([type="radio"]),
935
+ body.web .rt-grow, body.web .rt-grow > textarea { font-size: inherit; }
936
+ body.web .rt-cond.in { padding-top: 11px; padding-bottom: 11px; } /* the first line in the middle of the 44px pill */
937
+ body.web .rt-grow > textarea { min-height: 0; }
938
+ }
939
+ /* a rule's hours: two times in the pill, typed; one that isn't a time is marked */
940
+ .rt-cond.tm { cursor: text; }
941
+ .rt-cond.tm input.clock, .editor .rt-cond.tm input.clock[type="text"] { width: 5.5ch; text-align: center; }
942
+ .rt-cond.tm.bad { border-style: solid; border-color: var(--amber, var(--muted)); }
943
+ /* the time zone a rule's hours are in, when the server's clock is another */
944
+ .rt-zone { font-size: 12px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
945
+ .rt-cond[hidden] { display: none; }
946
+ .rt-classifier { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; }
947
+ .rt-classifier[hidden] { display: none; }
948
+ .rt-classifier .w { color: var(--muted); font-size: 12px; }
949
+ .rt-classifier .hint { flex-basis: 100%; }
950
+ /* the effort's classifier offering to pick the model too: a line of its
951
+ own, wrapping rather than running past a narrow window */
952
+ .rt-gadds .rt-try { white-space: normal; text-align: left; color: var(--accent); }
953
+ .rt-classifier .rt-cls-more { margin-left: -7px; white-space: normal; text-align: left; color: var(--accent); }
954
+ /* the levels a group names for agents (#295): one toggle each */
955
+ .rt-levels { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 6px; margin-top: 6px; }
956
+ .rt-levels[hidden] { display: none; }
957
+ /* the field is the pill: the editor's boxed input (its border and focus ring) stays off it */
958
+ .rt-cond input, .editor .rt-cond input[type="text"], .editor .rt-cond input[type="number"], .editor .rt-cond input:focus { box-shadow: none; border-radius: 0; }
959
+ .editor .rt-cond input:focus-visible { box-shadow: none; border-radius: 0; }
960
+ .rt-cond:focus-within { border-style: solid; border-color: var(--accent); color: var(--fg); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent); }
961
+ .rt-cond input, .editor .rt-cond input[type="text"], .editor .rt-cond input[type="number"] { width: 7ch; height: 20px; padding: 0 2px; border: 0; background: transparent; color: inherit; font: inherit; font-variant-numeric: tabular-nums; outline: none; -moz-appearance: textfield; }
962
+ .rt-cond input::-webkit-outer-spin-button, .rt-cond input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
963
+ .rt-rwarn { color: var(--amber, var(--muted)); font-size: 11.5px; }
964
+ .rt-rwarn:empty { display: none; }
965
+
966
+ /* a replay: the time it is replaying, what is in flight then, and a track */
967
+ .rt-replay { margin: 10px 0 2px; padding: 8px 10px 10px; border: 1px solid var(--line); border-radius: 10px; display: grid; gap: 6px; }
968
+ .rp-top { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
969
+ .rp-tag { font-size: 11px; font-weight: 600; color: var(--muted); display: inline-flex; align-items: center; gap: 6px; }
970
+ .rp-tag::before { content: ""; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); animation: rppulse 1.2s ease-in-out infinite; }
971
+ @keyframes rppulse { 50% { opacity: .3; } }
972
+ .rp-clock { font-size: 22px; font-weight: 650; letter-spacing: -.02em; font-variant-numeric: tabular-nums; color: var(--fg); }
973
+ .rp-day { font-size: 11px; color: var(--muted); }
974
+ .rp-skip { font-size: 11px; color: var(--accent); }
975
+ .rp-top .grow { flex: 1; }
976
+ .rp-what { display: grid; gap: 3px; min-height: 20px; }
977
+ .rp-req { display: flex; align-items: center; gap: 6px; font-size: 12px; color: var(--muted); min-width: 0; cursor: pointer; }
978
+ .rp-req b { color: var(--fg); font-weight: 600; }
979
+ .rp-req.on .rp-n { color: var(--fg); }
980
+ .rp-req i.ag { flex: none; width: 8px; height: 8px; border-radius: 50%; background: var(--agent); }
981
+ .rp-req .rp-to { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
982
+ .rp-req .rp-at { margin-left: auto; flex: none; font-variant-numeric: tabular-nums; color: var(--faint); }
983
+ .rp-n { flex: none; font-variant-numeric: tabular-nums; }
984
+ .rp-wait { font-size: 12px; color: var(--faint); }
985
+ .rp-track { position: relative; height: 16px; cursor: pointer; }
986
+ .rp-track::before { content: ""; position: absolute; left: 0; right: 0; top: 7px; height: 2px; border-radius: 1px; background: var(--line); }
987
+ .rp-mark { position: absolute; top: 4px; width: 3px; height: 8px; margin-left: -1.5px; border-radius: 1.5px; background: var(--agent); opacity: .45; transition: opacity .2s, transform .2s; }
988
+ .rp-mark.on { opacity: 1; transform: scaleY(1.5); }
989
+ .rp-head { position: absolute; top: 1px; width: 2px; height: 14px; margin-left: -1px; border-radius: 1px; background: var(--fg); pointer-events: none; }
990
+ @media (max-width: 520px) { .rp-req .rp-at { display: none; } }
991
+
992
+ /* the days the routing history keeps: live, then each day, newest first */
993
+ .rt-days, .rt-group-by { display: flex; gap: 4px; overflow-x: auto; padding: 0 2px 6px; scrollbar-width: none; }
994
+ .rt-days::-webkit-scrollbar, .rt-group-by::-webkit-scrollbar { display: none; }
995
+ .rt-day { flex: none; display: inline-flex; align-items: baseline; gap: 5px; font-size: 12px; color: var(--muted); padding: 3px 9px; border: 1px solid var(--line); border-radius: 999px; background: none; white-space: nowrap; }
996
+ .rt-day small { font-size: 10.5px; color: var(--faint); font-variant-numeric: tabular-nums; }
997
+ .rt-day:hover { color: var(--fg); }
998
+ .rt-day.on { color: var(--fg); border-color: var(--fg); }
999
+ /* the days that fit, the rest under the last pill (#961) */
1000
+ .rt-days { overflow-x: hidden; }
1001
+ .rt-days .rt-day[hidden] { display: none; }
1002
+ .rt-day-more svg { flex: none; align-self: center; opacity: .7; transition: transform .15s var(--ease); }
1003
+ .rt-day-more.open svg { transform: rotate(180deg); }
1004
+
1005
+ /* the tray panel's Routing tab: a small stage, then the gateway's latest
1006
+ requests, two lines each */
1007
+ .pr-stage { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) 40px minmax(0, 1fr); column-gap: 38px; align-items: center; height: 104px; padding: 0 10px; border-radius: 11px; background: var(--card); box-shadow: var(--card-edge); overflow: hidden; }
1008
+ .ps-wires, .ps-sky { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; overflow: visible; }
1009
+ .ps-wire { fill: none; stroke: var(--line); stroke-width: 1.5; stroke-linecap: round; transition: stroke .25s; }
1010
+ .ps-wire.on { stroke: color-mix(in srgb, var(--agent, var(--accent)) 55%, transparent); }
1011
+ .ps-col { position: relative; z-index: 1; display: flex; flex-direction: column; gap: 6px; min-width: 0; }
1012
+ .ps-from { align-items: flex-end; }
1013
+ .ps-to { align-items: flex-start; }
1014
+ .ps-node { display: flex; align-items: center; gap: 5px; max-width: 100%; min-width: 0; height: 22px; padding: 0 8px; border-radius: 7px; font-size: 11.5px; color: var(--fg); background: var(--card-2); box-shadow: inset 0 0 0 1px var(--line); }
1015
+ .ps-node .ic, .ps-node .ic img, .ps-node .ic svg { width: 13px; height: 13px; flex: none; }
1016
+ .ps-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 550; }
1017
+ .ps-name small { margin-left: 4px; font-size: 10.5px; font-weight: 400; color: var(--muted); }
1018
+ .ps-dst > i { flex: none; width: 6px; height: 6px; border-radius: 50%; background: var(--green); }
1019
+ .ps-dst.bad > i { background: var(--red); -webkit-mask: var(--x-mark) center / contain no-repeat; mask: var(--x-mark) center / contain no-repeat; border-radius: 0; transform: scale(1.5); }
1020
+ .ps-dst.wait > i { background: var(--muted); }
1021
+ .ps-hub { position: relative; z-index: 1; display: grid; place-items: center; width: 40px; height: 40px; border-radius: 50%; color: var(--fg); background: var(--card); box-shadow: inset 0 0 0 1px var(--line), 0 1px 3px rgba(0, 0, 0, .08); }
1022
+ .ps-hub svg { width: 24px; height: 24px; }
1023
+ .ps-hub.busy { animation: ps-ring 1.8s ease-out infinite; }
1024
+ @keyframes ps-ring { 0% { box-shadow: inset 0 0 0 1px var(--line), 0 0 0 0 color-mix(in srgb, var(--fg) 16%, transparent); } 70%, 100% { box-shadow: inset 0 0 0 1px var(--line), 0 0 0 7px transparent; } }
1025
+ .ps-dot { fill: var(--agent, var(--accent)); filter: drop-shadow(0 0 3px color-mix(in srgb, var(--agent, var(--accent)) 60%, transparent)); transition: fill .2s, opacity .3s; }
1026
+ .ps-dot.ok { fill: var(--green); filter: drop-shadow(0 0 3px color-mix(in srgb, var(--green) 60%, transparent)); }
1027
+ .ps-dot.bad { fill: var(--red); filter: drop-shadow(0 0 3px color-mix(in srgb, var(--red) 60%, transparent)); }
1028
+ .ps-dot.wait { animation: ps-wait 1.2s ease-in-out infinite; }
1029
+ .ps-dot.gone { opacity: 0; }
1030
+ @keyframes ps-wait { 50% { opacity: .45; } }
1031
+ .panel-routing { flex: none; display: flex; flex-direction: column; gap: 8px; padding: 2px 2px 6px; }
1032
+ .pr-head { display: flex; align-items: center; gap: 8px; min-height: 22px; padding: 0 2px 0 4px; font-size: 12px; color: var(--muted); }
1033
+ .pr-head .rt-livedot { margin-left: 0; flex: none; }
1034
+ .pr-today { font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
1035
+ .pr-today b { color: var(--fg); font-weight: 600; }
1036
+ .pr-head .text { flex: none; font-size: 12px; }
1037
+ .skeleton.pr-sk { display: inline-block; width: 150px; height: 9px; border-radius: 4px; vertical-align: middle; }
1038
+ /* the stage and the requests to come, their own size, as skeletons */
1039
+ .skeleton.pr-ghost { border-radius: 11px; }
1040
+ .pr-ghost > * { visibility: hidden; }
1041
+ /* a Manual group's model, picked from the tray (#2489) */
1042
+ .pr-picks { display: flex; flex-direction: column; padding: 4px; border-radius: 11px; background: var(--card); box-shadow: var(--card-edge); }
1043
+ .pr-pick { display: flex; align-items: center; gap: 10px; min-height: 30px; padding: 2px 4px 2px 8px; font-size: 12px; }
1044
+ .pr-gname { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--fg); font-weight: 550; }
1045
+ .pr-pick .sess-pick { flex: 0 1 auto; min-width: 0; max-width: 62%; }
1046
+ .pr-none { margin: 0; padding: 12px; border-radius: 11px; background: var(--card); box-shadow: var(--card-edge); font-size: 12px; line-height: 1.55; color: var(--muted); }
1047
+ .pr-none b { display: block; color: var(--fg); font-weight: 550; }
1048
+ .pr-list { display: flex; flex-direction: column; padding: 4px; border-radius: 11px; background: var(--card); box-shadow: var(--card-edge); }
1049
+ .pr-req { display: grid; grid-template-columns: minmax(0, 1fr) auto; align-items: center; gap: 2px 10px; padding: 6px 8px; border-radius: 8px; font: inherit; font-size: 12px; text-align: left; color: var(--fg-2); background: none; border: 0; cursor: pointer; }
1050
+ .pr-req:hover { background: var(--card-2); }
1051
+ .pr-a, .pr-to { display: flex; align-items: center; gap: 6px; min-width: 0; }
1052
+ .pr-a .ic, .pr-a .ic img, .pr-a .ic svg { width: 14px; height: 14px; flex: none; }
1053
+ .pr-who { flex: none; color: var(--fg); font-weight: 550; }
1054
+ .pr-a .m { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font: var(--code-style, normal) 500 11px/1.5 var(--code-font, var(--code)); font-stretch: var(--code-stretch, 100%); color: var(--fg-2); background: var(--card-2); border-radius: 5px; padding: 0 5px; }
1055
+ .pr-a .kind { flex: 0 100 auto; min-width: 3.2em; max-width: 8em; overflow: hidden; text-overflow: ellipsis; font-size: 10.5px; line-height: 16px; padding: 0 5px; border-radius: 5px; color: var(--muted); background: var(--card-2); border: 1px solid var(--line); white-space: nowrap; }
1056
+ .pr-req .at { justify-self: end; font-variant-numeric: tabular-nums; color: var(--muted); font-size: 11px; }
1057
+ .pr-to { padding-left: 20px; gap: 5px; color: var(--fg-2); }
1058
+ .pr-to > i { flex: none; width: 14px; margin-left: -20px; margin-right: 1px; text-align: center; font-style: normal; font-size: 11px; color: var(--faint); }
1059
+ .pr-req.moved .pr-to > i { color: var(--accent); }
1060
+ .pr-req.bad .pr-to { color: var(--red); }
1061
+ .pr-req.bad .pr-to > i { color: var(--red); }
1062
+ .pr-req.wait .pr-to > i { color: var(--fg-2); animation: ps-wait 1.2s ease-in-out infinite; }
1063
+ .pr-where { min-width: 0; flex: 0 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1064
+ .pr-m { flex: 0 2 auto; min-width: 2em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--muted); }
1065
+ .pr-m::before { content: "· "; }
1066
+ .pr-to .swap { flex: none; max-width: 45%; overflow: hidden; text-overflow: ellipsis; font-size: 10.5px; line-height: 16px; padding: 0 5px; border-radius: 5px; color: var(--amber); background: var(--amber-soft); white-space: nowrap; }
1067
+ .pr-line { grid-column: 1 / -1; display: flex; flex-wrap: wrap; align-items: center; gap: 2px 10px; min-width: 0; }
1068
+ .pr-line .pr-to { flex: 1 1 auto; }
1069
+ .pr-req .meta { flex: none; max-width: 100%; margin-left: auto; overflow: hidden; text-overflow: ellipsis; color: var(--muted); font-size: 11px; font-variant-numeric: tabular-nums; white-space: nowrap; text-align: right; }
1070
+ @media (prefers-reduced-motion: reduce) { .pr-req.wait .pr-to > i, .ps-hub.busy, .ps-dot.wait { animation: none; } .ps-dst.wait > i::after { animation: none; } }
1071
+ @supports not (grid-template-columns: subgrid) {
1072
+ @media (max-width: 700px) {
1073
+ .rt-reqs { --rt-tpl: auto minmax(0, 1fr); }
1074
+ .rt-req { row-gap: 2px; }
1075
+ :is(.rt-req, .rt-probe) .asked { grid-column: 2; grid-row: 1; }
1076
+ :is(.rt-req, .rt-probe) .to { grid-column: 2; grid-row: 2; }
1077
+ :is(.rt-req, .rt-probe) .meta { grid-column: 2; grid-row: 3; justify-content: flex-start; }
1078
+ }
1079
+ }
1080
+ /* templates: groups for common needs made of the user's own models, each
1081
+ added in a click (provider.Templates) */
1082
+ .rt-gtpl { padding: 10px 12px 12px; display: grid; gap: 8px; }
1083
+ .rt-gtpl-head { font-size: 12px; color: var(--muted); }
1084
+ /* Import a group (laincatx on X): the text pasted, then what came of it */
1085
+ .rt-gimport { padding: 10px 12px 12px; display: grid; gap: 8px; min-width: 0; border-bottom: 1px solid var(--line); }
1086
+ .rt-gimport-text { width: 100%; box-sizing: border-box; min-height: 72px; padding: 7px 9px; border-radius: 7px; border: 1px solid var(--line); background: var(--card); color: var(--fg); font-family: var(--code-font, var(--code)); font-size: 12px; line-height: 1.45; resize: vertical; outline: none; overflow-wrap: anywhere; }
1087
+ .rt-gimport-text:focus { border-color: var(--accent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--accent) 22%, transparent); }
1088
+ .rt-gimport .acts { display: flex; gap: 8px; justify-content: flex-end; flex-wrap: wrap; }
1089
+ .rt-gimport-done { font-size: 12.5px; font-weight: 600; }
1090
+ .rt-gimport-missing, .rt-gimport-note { font-size: 12px; line-height: 1.45; color: var(--muted); overflow-wrap: anywhere; }
1091
+ .rt-gimport-missing { color: var(--danger, #d66); }
1092
+ .rt-gtpl-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 8px; }
1093
+ .rt-gtpl-card { display: flex; flex-direction: column; gap: 4px; min-width: 0; padding: 10px 12px; border: 1px solid var(--line); border-radius: 10px; }
1094
+ .rt-gtpl-card .hd { display: flex; align-items: center; gap: 8px; min-width: 0; }
1095
+ .rt-gtpl-card .hd b { flex: 1; min-width: 0; font-size: 12.5px; font-weight: 600; line-height: 1.4; overflow-wrap: anywhere; }
1096
+ .rt-gtpl-card .hd .ic, .rt-gtpl-card .hd .ic-stack { flex: none; }
1097
+ .rt-gtpl-card .desc { font-size: 11.5px; line-height: 1.45; color: var(--muted); }
1098
+ .rt-gtpl-card .mem { font-size: 11.5px; color: var(--fg-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1099
+ .rt-gtpl-card .mem.why { color: var(--faint); white-space: normal; }
1100
+ .rt-gtpl-card.off b { color: var(--muted); }
1101
+ .rt-gtpl-card .acts { display: flex; align-items: center; gap: 8px; margin-top: auto; padding-top: 4px; }
1102
+ .rt-gtplbtn { display: inline-flex; align-items: center; gap: 4px; }
1103
+ .rt-log-head .mask-badge { margin-left: 8px; }
1104
+ /* under Accounts and keys: Show N more, and the way to each account's
1105
+ allowances in full on the Usage page (ferock4669, iyou00 on Discord) */
1106
+ .rt-acts-foot { display: flex; align-items: center; gap: 12px; padding: 2px 12px 4px 33px; font-size: 11px; }
1107
+ .rt-acts-foot[hidden] { display: none; }
1108
+ .rt-acts-foot .grow { flex: 1; }
1109
+ .rt-acts-foot button.text { font-size: 11px; color: var(--muted); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1110
+ .rt-acts-foot button.text:hover { color: var(--fg); }
1111
+ .rt-acts-foot button.text[hidden] { display: none; }