@sleep2agi/agent-network-dashboard 0.5.7-preview.7 → 0.5.7-preview.75

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 (376) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-path-routes-manifest.json +0 -1
  3. package/.next/build-manifest.json +3 -3
  4. package/.next/diagnostics/route-bundle-stats.json +65 -65
  5. package/.next/fallback-build-manifest.json +3 -3
  6. package/.next/prerender-manifest.json +3 -3
  7. package/.next/routes-manifest.json +0 -6
  8. package/.next/server/app/_global-error.html +1 -1
  9. package/.next/server/app/_global-error.rsc +1 -1
  10. package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  11. package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  12. package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  13. package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  14. package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/.next/server/app/_not-found/page/next-font-manifest.json +2 -1
  16. package/.next/server/app/_not-found/page.js +1 -1
  17. package/.next/server/app/_not-found/page.js.nft.json +1 -1
  18. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  19. package/.next/server/app/_not-found.html +4 -4
  20. package/.next/server/app/_not-found.rsc +16 -16
  21. package/.next/server/app/_not-found.segments/_full.segment.rsc +16 -16
  22. package/.next/server/app/_not-found.segments/_head.segment.rsc +4 -4
  23. package/.next/server/app/_not-found.segments/_index.segment.rsc +9 -9
  24. package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
  25. package/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  26. package/.next/server/app/_not-found.segments/_tree.segment.rsc +3 -3
  27. package/.next/server/app/admin/page/next-font-manifest.json +2 -1
  28. package/.next/server/app/admin/page.js +1 -1
  29. package/.next/server/app/admin/page.js.nft.json +1 -1
  30. package/.next/server/app/admin/page_client-reference-manifest.js +1 -1
  31. package/.next/server/app/admin.html +4 -4
  32. package/.next/server/app/admin.rsc +20 -19
  33. package/.next/server/app/admin.segments/_full.segment.rsc +20 -19
  34. package/.next/server/app/admin.segments/_head.segment.rsc +4 -4
  35. package/.next/server/app/admin.segments/_index.segment.rsc +9 -9
  36. package/.next/server/app/admin.segments/_tree.segment.rsc +5 -4
  37. package/.next/server/app/admin.segments/admin/__PAGE__.segment.rsc +4 -4
  38. package/.next/server/app/admin.segments/admin.segment.rsc +3 -3
  39. package/.next/server/app/index.html +4 -4
  40. package/.next/server/app/index.rsc +20 -19
  41. package/.next/server/app/index.segments/__PAGE__.segment.rsc +4 -4
  42. package/.next/server/app/index.segments/_full.segment.rsc +20 -19
  43. package/.next/server/app/index.segments/_head.segment.rsc +4 -4
  44. package/.next/server/app/index.segments/_index.segment.rsc +9 -9
  45. package/.next/server/app/index.segments/_tree.segment.rsc +5 -4
  46. package/.next/server/app/login/page/next-font-manifest.json +2 -1
  47. package/.next/server/app/login/page.js +1 -1
  48. package/.next/server/app/login/page.js.nft.json +1 -1
  49. package/.next/server/app/login/page_client-reference-manifest.js +1 -1
  50. package/.next/server/app/login.html +2 -2
  51. package/.next/server/app/login.rsc +20 -19
  52. package/.next/server/app/login.segments/_full.segment.rsc +20 -19
  53. package/.next/server/app/login.segments/_head.segment.rsc +4 -4
  54. package/.next/server/app/login.segments/_index.segment.rsc +9 -9
  55. package/.next/server/app/login.segments/_tree.segment.rsc +5 -4
  56. package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
  57. package/.next/server/app/login.segments/login.segment.rsc +3 -3
  58. package/.next/server/app/logs/page/next-font-manifest.json +2 -1
  59. package/.next/server/app/logs/page.js +1 -1
  60. package/.next/server/app/logs/page.js.nft.json +1 -1
  61. package/.next/server/app/logs/page_client-reference-manifest.js +1 -1
  62. package/.next/server/app/logs.html +4 -4
  63. package/.next/server/app/logs.rsc +20 -19
  64. package/.next/server/app/logs.segments/_full.segment.rsc +20 -19
  65. package/.next/server/app/logs.segments/_head.segment.rsc +4 -4
  66. package/.next/server/app/logs.segments/_index.segment.rsc +9 -9
  67. package/.next/server/app/logs.segments/_tree.segment.rsc +5 -4
  68. package/.next/server/app/logs.segments/logs/__PAGE__.segment.rsc +4 -4
  69. package/.next/server/app/logs.segments/logs.segment.rsc +3 -3
  70. package/.next/server/app/manifest.webmanifest.body +1 -1
  71. package/.next/server/app/messages/page/next-font-manifest.json +2 -1
  72. package/.next/server/app/messages/page.js +1 -1
  73. package/.next/server/app/messages/page.js.nft.json +1 -1
  74. package/.next/server/app/messages/page_client-reference-manifest.js +1 -1
  75. package/.next/server/app/messages.html +4 -4
  76. package/.next/server/app/messages.rsc +20 -19
  77. package/.next/server/app/messages.segments/_full.segment.rsc +20 -19
  78. package/.next/server/app/messages.segments/_head.segment.rsc +4 -4
  79. package/.next/server/app/messages.segments/_index.segment.rsc +9 -9
  80. package/.next/server/app/messages.segments/_tree.segment.rsc +5 -4
  81. package/.next/server/app/messages.segments/messages/__PAGE__.segment.rsc +4 -4
  82. package/.next/server/app/messages.segments/messages.segment.rsc +3 -3
  83. package/.next/server/app/node/page/next-font-manifest.json +2 -1
  84. package/.next/server/app/node/page.js +1 -1
  85. package/.next/server/app/node/page.js.nft.json +1 -1
  86. package/.next/server/app/node/page_client-reference-manifest.js +1 -1
  87. package/.next/server/app/node.html +4 -4
  88. package/.next/server/app/node.rsc +20 -19
  89. package/.next/server/app/node.segments/_full.segment.rsc +20 -19
  90. package/.next/server/app/node.segments/_head.segment.rsc +4 -4
  91. package/.next/server/app/node.segments/_index.segment.rsc +9 -9
  92. package/.next/server/app/node.segments/_tree.segment.rsc +5 -4
  93. package/.next/server/app/node.segments/node/__PAGE__.segment.rsc +4 -4
  94. package/.next/server/app/node.segments/node.segment.rsc +3 -3
  95. package/.next/server/app/nodes/page/next-font-manifest.json +2 -1
  96. package/.next/server/app/nodes/page.js +1 -1
  97. package/.next/server/app/nodes/page.js.nft.json +1 -1
  98. package/.next/server/app/nodes/page_client-reference-manifest.js +1 -1
  99. package/.next/server/app/nodes.html +4 -4
  100. package/.next/server/app/nodes.rsc +20 -19
  101. package/.next/server/app/nodes.segments/_full.segment.rsc +20 -19
  102. package/.next/server/app/nodes.segments/_head.segment.rsc +4 -4
  103. package/.next/server/app/nodes.segments/_index.segment.rsc +9 -9
  104. package/.next/server/app/nodes.segments/_tree.segment.rsc +5 -4
  105. package/.next/server/app/nodes.segments/nodes/__PAGE__.segment.rsc +4 -4
  106. package/.next/server/app/nodes.segments/nodes.segment.rsc +3 -3
  107. package/.next/server/app/page/next-font-manifest.json +2 -1
  108. package/.next/server/app/page.js +1 -1
  109. package/.next/server/app/page.js.nft.json +1 -1
  110. package/.next/server/app/page_client-reference-manifest.js +1 -1
  111. package/.next/server/app/server-logs/page/next-font-manifest.json +2 -1
  112. package/.next/server/app/server-logs/page.js +1 -1
  113. package/.next/server/app/server-logs/page.js.nft.json +1 -1
  114. package/.next/server/app/server-logs/page_client-reference-manifest.js +1 -1
  115. package/.next/server/app/server-logs.html +4 -4
  116. package/.next/server/app/server-logs.rsc +20 -19
  117. package/.next/server/app/server-logs.segments/_full.segment.rsc +20 -19
  118. package/.next/server/app/server-logs.segments/_head.segment.rsc +4 -4
  119. package/.next/server/app/server-logs.segments/_index.segment.rsc +9 -9
  120. package/.next/server/app/server-logs.segments/_tree.segment.rsc +5 -4
  121. package/.next/server/app/server-logs.segments/server-logs/__PAGE__.segment.rsc +4 -4
  122. package/.next/server/app/server-logs.segments/server-logs.segment.rsc +3 -3
  123. package/.next/server/app/servers/page/next-font-manifest.json +2 -1
  124. package/.next/server/app/servers/page.js +1 -1
  125. package/.next/server/app/servers/page.js.nft.json +1 -1
  126. package/.next/server/app/servers/page_client-reference-manifest.js +1 -1
  127. package/.next/server/app/servers.html +4 -4
  128. package/.next/server/app/servers.rsc +20 -19
  129. package/.next/server/app/servers.segments/_full.segment.rsc +20 -19
  130. package/.next/server/app/servers.segments/_head.segment.rsc +4 -4
  131. package/.next/server/app/servers.segments/_index.segment.rsc +9 -9
  132. package/.next/server/app/servers.segments/_tree.segment.rsc +5 -4
  133. package/.next/server/app/servers.segments/servers/__PAGE__.segment.rsc +4 -4
  134. package/.next/server/app/servers.segments/servers.segment.rsc +3 -3
  135. package/.next/server/app/settings/networks/page/next-font-manifest.json +2 -1
  136. package/.next/server/app/settings/networks/page.js +1 -1
  137. package/.next/server/app/settings/networks/page.js.nft.json +1 -1
  138. package/.next/server/app/settings/networks/page_client-reference-manifest.js +1 -1
  139. package/.next/server/app/settings/networks.html +4 -4
  140. package/.next/server/app/settings/networks.rsc +20 -19
  141. package/.next/server/app/settings/networks.segments/_full.segment.rsc +20 -19
  142. package/.next/server/app/settings/networks.segments/_head.segment.rsc +4 -4
  143. package/.next/server/app/settings/networks.segments/_index.segment.rsc +9 -9
  144. package/.next/server/app/settings/networks.segments/_tree.segment.rsc +5 -4
  145. package/.next/server/app/settings/networks.segments/settings/networks/__PAGE__.segment.rsc +4 -4
  146. package/.next/server/app/settings/networks.segments/settings/networks.segment.rsc +3 -3
  147. package/.next/server/app/settings/networks.segments/settings.segment.rsc +3 -3
  148. package/.next/server/app/settings/page/next-font-manifest.json +2 -1
  149. package/.next/server/app/settings/page.js +1 -1
  150. package/.next/server/app/settings/page.js.nft.json +1 -1
  151. package/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  152. package/.next/server/app/settings/tokens/page/next-font-manifest.json +2 -1
  153. package/.next/server/app/settings/tokens/page.js +1 -1
  154. package/.next/server/app/settings/tokens/page.js.nft.json +1 -1
  155. package/.next/server/app/settings/tokens/page_client-reference-manifest.js +1 -1
  156. package/.next/server/app/settings/tokens.html +4 -4
  157. package/.next/server/app/settings/tokens.rsc +20 -19
  158. package/.next/server/app/settings/tokens.segments/_full.segment.rsc +20 -19
  159. package/.next/server/app/settings/tokens.segments/_head.segment.rsc +4 -4
  160. package/.next/server/app/settings/tokens.segments/_index.segment.rsc +9 -9
  161. package/.next/server/app/settings/tokens.segments/_tree.segment.rsc +5 -4
  162. package/.next/server/app/settings/tokens.segments/settings/tokens/__PAGE__.segment.rsc +4 -4
  163. package/.next/server/app/settings/tokens.segments/settings/tokens.segment.rsc +3 -3
  164. package/.next/server/app/settings/tokens.segments/settings.segment.rsc +3 -3
  165. package/.next/server/app/settings.html +4 -4
  166. package/.next/server/app/settings.rsc +20 -19
  167. package/.next/server/app/settings.segments/_full.segment.rsc +20 -19
  168. package/.next/server/app/settings.segments/_head.segment.rsc +4 -4
  169. package/.next/server/app/settings.segments/_index.segment.rsc +9 -9
  170. package/.next/server/app/settings.segments/_tree.segment.rsc +5 -4
  171. package/.next/server/app/settings.segments/settings/__PAGE__.segment.rsc +4 -4
  172. package/.next/server/app/settings.segments/settings.segment.rsc +3 -3
  173. package/.next/server/app/tasks/[id]/page/next-font-manifest.json +2 -1
  174. package/.next/server/app/tasks/[id]/page.js +1 -1
  175. package/.next/server/app/tasks/[id]/page.js.nft.json +1 -1
  176. package/.next/server/app/tasks/[id]/page_client-reference-manifest.js +1 -1
  177. package/.next/server/app/tasks/page/next-font-manifest.json +2 -1
  178. package/.next/server/app/tasks/page.js +1 -1
  179. package/.next/server/app/tasks/page.js.nft.json +1 -1
  180. package/.next/server/app/tasks/page_client-reference-manifest.js +1 -1
  181. package/.next/server/app/tasks.html +4 -4
  182. package/.next/server/app/tasks.rsc +20 -19
  183. package/.next/server/app/tasks.segments/_full.segment.rsc +20 -19
  184. package/.next/server/app/tasks.segments/_head.segment.rsc +4 -4
  185. package/.next/server/app/tasks.segments/_index.segment.rsc +9 -9
  186. package/.next/server/app/tasks.segments/_tree.segment.rsc +5 -4
  187. package/.next/server/app/tasks.segments/tasks/__PAGE__.segment.rsc +4 -4
  188. package/.next/server/app/tasks.segments/tasks.segment.rsc +3 -3
  189. package/.next/server/app-paths-manifest.json +0 -1
  190. package/.next/server/chunks/00jm_next_dist_0ju_ux9._.js +1 -1
  191. package/.next/server/chunks/00jm_next_dist_0ju_ux9._.js.map +1 -1
  192. package/.next/server/chunks/ssr/[root-of-the-server]__030vg4n._.js +2 -2
  193. package/.next/server/chunks/ssr/[root-of-the-server]__030vg4n._.js.map +1 -1
  194. package/.next/server/chunks/ssr/[root-of-the-server]__05kf31s._.js +3 -0
  195. package/.next/server/chunks/ssr/[root-of-the-server]__05kf31s._.js.map +1 -0
  196. package/.next/server/chunks/ssr/[root-of-the-server]__096ytyk._.js +3 -0
  197. package/.next/server/chunks/ssr/[root-of-the-server]__096ytyk._.js.map +1 -0
  198. package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js +1 -1
  199. package/.next/server/chunks/ssr/[root-of-the-server]__0fhoq8i._.js.map +1 -1
  200. package/.next/server/chunks/ssr/[root-of-the-server]__0nw1f-j._.js +3 -0
  201. package/.next/server/chunks/ssr/[root-of-the-server]__0nw1f-j._.js.map +1 -0
  202. package/.next/server/chunks/ssr/[root-of-the-server]__0u4-66w._.js +8 -0
  203. package/.next/server/chunks/ssr/[root-of-the-server]__0u4-66w._.js.map +1 -0
  204. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js +3 -3
  205. package/.next/server/chunks/ssr/agent-network-dashboard_09kk21a._.js.map +1 -1
  206. package/.next/server/chunks/ssr/agent-network-dashboard_app_012oyw5._.js +3 -0
  207. package/.next/server/chunks/ssr/agent-network-dashboard_app_012oyw5._.js.map +1 -0
  208. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js +1 -1
  209. package/.next/server/chunks/ssr/agent-network-dashboard_app_01jhlxz._.js.map +1 -1
  210. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js +1 -1
  211. package/.next/server/chunks/ssr/agent-network-dashboard_app_09d29my._.js.map +1 -1
  212. package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js +1 -1
  213. package/.next/server/chunks/ssr/agent-network-dashboard_app_0_d45-d._.js.map +1 -1
  214. package/.next/server/chunks/ssr/agent-network-dashboard_app_0fjlnh~._.js +3 -0
  215. package/.next/server/chunks/ssr/agent-network-dashboard_app_0fjlnh~._.js.map +1 -0
  216. package/.next/server/chunks/ssr/agent-network-dashboard_app_0gd.4pc._.js +9 -0
  217. package/.next/server/chunks/ssr/agent-network-dashboard_app_0gd.4pc._.js.map +1 -0
  218. package/.next/server/chunks/ssr/agent-network-dashboard_app_0wn4jc5._.js +3 -0
  219. package/.next/server/chunks/ssr/agent-network-dashboard_app_0wn4jc5._.js.map +1 -0
  220. package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js +1 -1
  221. package/.next/server/chunks/ssr/agent-network-dashboard_app_0xgney8._.js.map +1 -1
  222. package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js +1 -1
  223. package/.next/server/chunks/ssr/agent-network-dashboard_app_10hjgv4._.js.map +1 -1
  224. package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js +1 -1
  225. package/.next/server/chunks/ssr/agent-network-dashboard_app_12l4oto._.js.map +1 -1
  226. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0r7kb.o._.js +9 -0
  227. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0r7kb.o._.js.map +1 -0
  228. package/.next/server/chunks/ssr/agent-network-dashboard_app_servers_page_tsx_0jib5qm._.js +1 -1
  229. package/.next/server/chunks/ssr/agent-network-dashboard_app_servers_page_tsx_0jib5qm._.js.map +1 -1
  230. package/.next/server/chunks/ssr/agent-network-dashboard_app_tasks_page_tsx_0mwxy4z._.js +1 -1
  231. package/.next/server/chunks/ssr/agent-network-dashboard_app_tasks_page_tsx_0mwxy4z._.js.map +1 -1
  232. package/.next/server/middleware-build-manifest.js +3 -3
  233. package/.next/server/next-font-manifest.js +1 -1
  234. package/.next/server/next-font-manifest.json +30 -15
  235. package/.next/server/pages/404.html +4 -4
  236. package/.next/server/pages/500.html +1 -1
  237. package/.next/server/server-reference-manifest.js +1 -1
  238. package/.next/server/server-reference-manifest.json +1 -1
  239. package/.next/static/chunks/0.mh8n0itrii5.js +1 -0
  240. package/.next/static/chunks/049vx3qljs1tt.js +1 -0
  241. package/.next/static/chunks/04uju~n5s9g7..css +1 -0
  242. package/.next/static/chunks/066jf0nk75nic.css +2 -0
  243. package/.next/static/chunks/06vp7429lrzl7.js +1 -0
  244. package/.next/static/chunks/{0jp~cs9-zkmqa.js → 07h2umbpc5lqy.js} +2 -2
  245. package/.next/static/chunks/0_bn~gcrgo.4n.js +1 -0
  246. package/.next/static/chunks/0_cm~9rtqil56.js +1 -0
  247. package/.next/static/chunks/0cp0cz3mxejl~.js +4 -0
  248. package/.next/static/chunks/0g1o7c8fbafn7.js +1 -0
  249. package/.next/static/chunks/0g4d-_fi-d9hg.js +1 -0
  250. package/.next/static/chunks/0iv.0p9_5b36e.js +1 -0
  251. package/.next/static/chunks/0tor7h4q5_rz..js +1 -0
  252. package/.next/static/chunks/0vun~4ljcrj3p.js +7 -0
  253. package/.next/static/chunks/0wz0122ym_gr3.js +1 -0
  254. package/.next/static/chunks/0y5gol09tlu63.js +1 -0
  255. package/.next/static/chunks/114o05335p68v.js +1 -0
  256. package/.next/static/chunks/14141xj5.1t3t.js +1 -0
  257. package/.next/static/chunks/149a4l50_3vw-.js +7 -0
  258. package/.next/static/chunks/15hos-r_t7doi.js +1 -0
  259. package/.next/static/media/4fa387ec64143e14-s.0wkzw~je483f-.woff2 +0 -0
  260. package/.next/static/media/53b9e256198e5412-s.0-wfv7uh4i7h9.woff2 +0 -0
  261. package/.next/static/media/5ce348bf30bf5439-s.0zgw-jeven.3w.woff2 +0 -0
  262. package/.next/static/media/6306c77e7c8268e4-s.0rhz0arwfsn~5.woff2 +0 -0
  263. package/.next/static/media/7178b3e590c64307-s.0nx0ww8fni_q3.woff2 +0 -0
  264. package/.next/static/media/797e433ab948586e-s.p.08e28id.o-okb.woff2 +0 -0
  265. package/.next/static/media/7d817b4c03b0c5f1-s.0l76wvqk9d84w.woff2 +0 -0
  266. package/.next/static/media/8a480f0b521d4e75-s.0jzbimsg8vl84.woff2 +0 -0
  267. package/.next/static/media/bbc41e54d2fcbd21-s.0k4k9394f2q-k.woff2 +0 -0
  268. package/.next/static/media/caa3a2e1cccd8315-s.p.09~u27dqhyhd6.woff2 +0 -0
  269. package/.next/static/media/fef07dbb0973bf53-s.12tyk43_3sh9u.woff2 +0 -0
  270. package/.next/trace +2 -2
  271. package/.next/trace-build +1 -1
  272. package/.next/types/routes.d.ts +1 -2
  273. package/.next/types/validator.ts +0 -9
  274. package/app/admin/page.tsx +53 -38
  275. package/app/components/AgentCard.tsx +36 -20
  276. package/app/components/AppShell.tsx +7 -1
  277. package/app/components/ChatPopover.tsx +1 -1
  278. package/app/components/CollapsibleSearch.tsx +127 -0
  279. package/app/components/CommandCenter.tsx +14 -4
  280. package/app/components/CommandPalette.tsx +6 -6
  281. package/app/components/DispatchPanel.tsx +13 -10
  282. package/app/components/EmptyState.tsx +19 -4
  283. package/app/components/HealthBanner.tsx +28 -4
  284. package/app/components/HelpOverlay.tsx +4 -7
  285. package/app/components/LoadingSkeleton.tsx +31 -21
  286. package/app/components/MobileNav.tsx +28 -21
  287. package/app/components/Sidebar.tsx +30 -23
  288. package/app/components/StatsBar.tsx +69 -42
  289. package/app/components/TaskChatPanel.tsx +105 -12
  290. package/app/components/TaskDrawer.tsx +9 -7
  291. package/app/components/ThemeSwitcher.tsx +15 -79
  292. package/app/components/TopoGraph.tsx +31 -21
  293. package/app/components/UserBar.tsx +5 -5
  294. package/app/globals.css +1757 -1776
  295. package/app/layout.tsx +37 -4
  296. package/app/lib/hooks.ts +4 -6
  297. package/app/lib/status.ts +24 -17
  298. package/app/login/page.tsx +7 -7
  299. package/app/logs/page.tsx +12 -6
  300. package/app/manifest.ts +2 -2
  301. package/app/messages/page.tsx +108 -50
  302. package/app/node/page.tsx +27 -17
  303. package/app/nodes/page.tsx +62 -49
  304. package/app/page.tsx +60 -282
  305. package/app/server-logs/page.tsx +40 -14
  306. package/app/servers/page.tsx +33 -12
  307. package/app/settings/networks/page.tsx +17 -15
  308. package/app/settings/page.tsx +102 -96
  309. package/app/settings/tokens/page.tsx +5 -5
  310. package/app/tasks/[id]/page.tsx +10 -10
  311. package/app/tasks/page.tsx +58 -34
  312. package/bin/start.js +0 -0
  313. package/package.json +1 -1
  314. package/public/favicon.svg +1 -1
  315. package/public/manifest.webmanifest +24 -0
  316. package/public/robots.txt +7 -0
  317. package/.next/server/app/api/hub/license/route/app-paths-manifest.json +0 -3
  318. package/.next/server/app/api/hub/license/route/build-manifest.json +0 -9
  319. package/.next/server/app/api/hub/license/route/server-reference-manifest.json +0 -4
  320. package/.next/server/app/api/hub/license/route.js +0 -7
  321. package/.next/server/app/api/hub/license/route.js.map +0 -5
  322. package/.next/server/app/api/hub/license/route.js.nft.json +0 -1
  323. package/.next/server/app/api/hub/license/route_client-reference-manifest.js +0 -3
  324. package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_license_route_actions_0a4.fuh.js +0 -3
  325. package/.next/server/chunks/0ykm__next-internal_server_app_api_hub_license_route_actions_0a4.fuh.js.map +0 -1
  326. package/.next/server/chunks/[root-of-the-server]__0rovr5-._.js +0 -3
  327. package/.next/server/chunks/[root-of-the-server]__0rovr5-._.js.map +0 -1
  328. package/.next/server/chunks/ssr/[root-of-the-server]__0lu1wok._.js +0 -8
  329. package/.next/server/chunks/ssr/[root-of-the-server]__0lu1wok._.js.map +0 -1
  330. package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js +0 -3
  331. package/.next/server/chunks/ssr/[root-of-the-server]__0nw~zhp._.js.map +0 -1
  332. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js +0 -3
  333. package/.next/server/chunks/ssr/[root-of-the-server]__0sv~g.o._.js.map +0 -1
  334. package/.next/server/chunks/ssr/[root-of-the-server]__11fu-5m._.js +0 -3
  335. package/.next/server/chunks/ssr/[root-of-the-server]__11fu-5m._.js.map +0 -1
  336. package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js +0 -3
  337. package/.next/server/chunks/ssr/agent-network-dashboard_app_057q.ne._.js.map +0 -1
  338. package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js +0 -3
  339. package/.next/server/chunks/ssr/agent-network-dashboard_app_0i3759l._.js.map +0 -1
  340. package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js +0 -9
  341. package/.next/server/chunks/ssr/agent-network-dashboard_app_1153xeb._.js.map +0 -1
  342. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0s5uqlp._.js +0 -9
  343. package/.next/server/chunks/ssr/agent-network-dashboard_app_components_0s5uqlp._.js.map +0 -1
  344. package/.next/server/chunks/ssr/agent-network-dashboard_app_server-logs_page_tsx_0dg.l_8._.js +0 -3
  345. package/.next/server/chunks/ssr/agent-network-dashboard_app_server-logs_page_tsx_0dg.l_8._.js.map +0 -1
  346. package/.next/static/chunks/0-mpa_947ipeq.js +0 -1
  347. package/.next/static/chunks/02to42x11p557.js +0 -7
  348. package/.next/static/chunks/03~5pxwbxxw-b.js +0 -1
  349. package/.next/static/chunks/04~fkia6-79k3.js +0 -1
  350. package/.next/static/chunks/0561vp5-q5.zp.js +0 -1
  351. package/.next/static/chunks/05uk96gc~9mni.js +0 -1
  352. package/.next/static/chunks/085rejlait1fs.js +0 -1
  353. package/.next/static/chunks/0a.9~-nf0gpec.js +0 -1
  354. package/.next/static/chunks/0im751o4n61c7.js +0 -1
  355. package/.next/static/chunks/0inql3s9ldyx5.js +0 -1
  356. package/.next/static/chunks/0ku0fjqlm9mca.js +0 -1
  357. package/.next/static/chunks/0mcamnu4w_x1r.js +0 -4
  358. package/.next/static/chunks/0ss8u23bnbyry.js +0 -1
  359. package/.next/static/chunks/0~rv5y.y5my9s.css +0 -1
  360. package/.next/static/chunks/13yktdzuatx3d.js +0 -1
  361. package/.next/static/chunks/15-ltfhot3b4n.js +0 -7
  362. package/.next/static/chunks/16ls93seuyj8a.js +0 -1
  363. package/.next/static/chunks/17sxlwlx5fhrp.css +0 -1
  364. package/.next/static/chunks/181u38qblp8lz.js +0 -1
  365. package/.next/static/media/4fa387ec64143e14-s.0.qu-9752pffj.woff2 +0 -0
  366. package/.next/static/media/5ce348bf30bf5439-s.0ee55_hj9qcer.woff2 +0 -0
  367. package/.next/static/media/6306c77e7c8268e4-s.0mao5jbfbduzp.woff2 +0 -0
  368. package/.next/static/media/797e433ab948586e-s.p.09zddjkbdep5a.woff2 +0 -0
  369. package/.next/static/media/7d817b4c03b0c5f1-s.0uzt.a6d44yda.woff2 +0 -0
  370. package/.next/static/media/bbc41e54d2fcbd21-s.0mvwgmnhv29no.woff2 +0 -0
  371. package/app/api/hub/license/route.ts +0 -33
  372. package/app/components/BroadcastBar.tsx +0 -84
  373. package/app/components/InboxPanel.tsx +0 -36
  374. /package/.next/static/{vv4Gz5yVhOzydMI2UlT1l → nPhsSMLrgn8H0RsXNP4ku}/_buildManifest.js +0 -0
  375. /package/.next/static/{vv4Gz5yVhOzydMI2UlT1l → nPhsSMLrgn8H0RsXNP4ku}/_clientMiddlewareManifest.js +0 -0
  376. /package/.next/static/{vv4Gz5yVhOzydMI2UlT1l → nPhsSMLrgn8H0RsXNP4ku}/_ssgManifest.js +0 -0
package/app/globals.css CHANGED
@@ -4,2014 +4,1995 @@ html,
4
4
  body {
5
5
  max-width: 100%;
6
6
  overflow-x: hidden;
7
- }
8
-
9
- * {
7
+ }* {
10
8
  box-sizing: border-box;
11
- }
12
-
13
- /* ────────────────────────────────────────────────────
9
+ }/* ────────────────────────────────────────────────────
14
10
  Design tokens — switch via [data-theme="..."] on <html>
15
11
  See ThemeProvider for runtime selection.
16
12
  ──────────────────────────────────────────────────── */
17
13
 
18
14
  :root,
19
15
  [data-theme="cyber"] {
20
- --bg: #0a0a1a;
21
- --bg-secondary: #11112a;
22
- --bg-elevated: #161640;
16
+ --bg: #0b0b0d;
17
+ --bg-secondary: #161618;
18
+ --bg-elevated: #1f1f24;
23
19
  --fg: #e5e7eb;
24
20
  --fg-muted: #9ca3af;
25
21
  --fg-dim: #6b7280;
26
- --border: #2a2a4a;
27
- --border-hover: #3a3a5a;
22
+ --border: #26262b;
23
+ --border-hover: #3a3a41;
28
24
  --accent: #8b5cf6;
29
25
  --accent-fg: #ffffff;
30
26
  --success: #10b981;
31
27
  --warning: #f59e0b;
32
28
  --danger: #ef4444;
33
29
  --info: #3b82f6;
34
- --scrollbar: #2a2a4a;
35
- --scrollbar-hover: #3a3a5a;
36
- --code-bg: #1a1a3a;
37
- color-scheme: dark;
38
- }
39
-
40
- [data-theme="light"] {
41
- --bg: #f6f7f9;
42
- --bg-secondary: #ffffff;
43
- --bg-elevated: #eef0f4;
44
- --fg: #0f1419;
45
- --fg-muted: #525a66;
46
- --fg-dim: #8a94a3;
47
- --border: #e3e6eb;
48
- --border-hover: #c8cdd6;
49
- --accent: #009e7e;
50
- --accent-fg: #ffffff;
51
- --success: #059669;
52
- --warning: #d97706;
53
- --danger: #dc2626;
54
- --info: #0284c7;
55
- --scrollbar: #d4d8df;
56
- --scrollbar-hover: #a1a8b5;
57
- --code-bg: #eef0f4;
58
- color-scheme: light;
59
- }
60
-
61
- [data-theme="mint"] {
62
- --bg: #f0fdf4;
63
- --bg-secondary: #ffffff;
64
- --bg-elevated: #dcfce7;
65
- --fg: #052e16;
66
- --fg-muted: #166534;
67
- --fg-dim: #4ade80;
68
- --border: #bbf7d0;
69
- --border-hover: #86efac;
70
- --accent: #16a34a;
71
- --accent-fg: #ffffff;
72
- --success: #16a34a;
73
- --warning: #ca8a04;
74
- --danger: #dc2626;
75
- --info: #0891b2;
76
- --scrollbar: #bbf7d0;
77
- --scrollbar-hover: #86efac;
78
- --code-bg: #dcfce7;
79
- color-scheme: light;
80
- }
81
-
82
- [data-theme="sunset"] {
83
- --bg: #1a0f1f;
84
- --bg-secondary: #2d1633;
85
- --bg-elevated: #3f1d47;
86
- --fg: #fde68a;
87
- --fg-muted: #fbbf24;
88
- --fg-dim: #b45309;
89
- --border: #581c87;
90
- --border-hover: #7e22ce;
91
- --accent: #f97316;
92
- --accent-fg: #ffffff;
93
- --success: #84cc16;
94
- --warning: #facc15;
95
- --danger: #f43f5e;
96
- --info: #ec4899;
97
- --scrollbar: #581c87;
98
- --scrollbar-hover: #7e22ce;
99
- --code-bg: #2d1633;
30
+ --scrollbar: #26262b;
31
+ --scrollbar-hover: #3a3a41;
32
+ --code-bg: #232327;
100
33
  color-scheme: dark;
101
- }
102
-
103
- @theme inline {
34
+ }@theme inline {
104
35
  --color-background: var(--bg);
105
36
  --color-foreground: var(--fg);
37
+ --font-sans: var(--font-geist-sans);
106
38
  --font-mono: var(--font-geist-mono);
107
- }
108
-
109
- html, body {
39
+ }html,
40
+ body {
110
41
  background: var(--bg);
111
42
  color: var(--fg);
112
- }
113
-
43
+ }/* #217 D1: UI text is sans (OpenWebUI-style product feel); mono is
44
+ opt-in via .font-mono for code, IDs and logs. */
114
45
  body {
115
- font-family: var(--font-mono), ui-monospace, monospace;
46
+ font-family: var(--font-geist-sans), system-ui, -apple-system, sans-serif;
116
47
  transition: background-color 0.2s ease, color 0.2s ease;
117
- }
118
-
119
- /* Smooth scrollbar — themed */
120
- .scrollbar-thin::-webkit-scrollbar { width: 4px; height: 4px; }
121
- .scrollbar-thin::-webkit-scrollbar-track { background: transparent; }
122
- .scrollbar-thin::-webkit-scrollbar-thumb {
48
+ }/* Smooth scrollbar — themed */
49
+ .scrollbar-thin::-webkit-scrollbar { width: 4px; height: 4px; }.scrollbar-thin::-webkit-scrollbar-track { background: transparent; }.scrollbar-thin::-webkit-scrollbar-thumb {
123
50
  background: var(--scrollbar);
124
51
  border-radius: 4px;
125
- }
126
- .scrollbar-thin::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-hover); }
127
-
128
- /* Fade in animation */
52
+ }.scrollbar-thin::-webkit-scrollbar-thumb:hover { background: var(--scrollbar-hover); }/* Fade in animation */
129
53
  @keyframes fade-in {
130
54
  from { opacity: 0; transform: translateY(-4px); }
131
55
  to { opacity: 1; transform: translateY(0); }
132
- }
133
- .animate-fade-in {
56
+ }.animate-fade-in {
134
57
  animation: fade-in 0.3s ease-out;
135
- }
136
-
137
- /* Mobile touch targets */
58
+ }/* Mobile touch targets */
138
59
  @media (max-width: 640px) {
139
60
  select, input[type="text"], button {
140
61
  min-height: 44px;
141
62
  }
142
- }
143
-
144
- /* Safe area for notched devices */
63
+ }/* Safe area for notched devices */
145
64
  @supports (padding-bottom: env(safe-area-inset-bottom)) {
146
65
  body {
147
66
  padding-bottom: env(safe-area-inset-bottom);
148
67
  }
149
- }
150
-
151
- /* ────────────────────────────────────────────────────
152
- Compatibility shims — gradually replace hard-coded
153
- #0a0a1a / zinc-9xx / etc. in components with var()
154
- over time. These rules let the existing components
155
- pick up the active theme even before they are
156
- migrated.
157
- ──────────────────────────────────────────────────── */
68
+ }/* When sidebar is collapsed (w-16 {
69
+ border-radius: 8px !important;
70
+ padding-left: 0 !important;
71
+ border-left: 0 !important;
72
+ background-color: var(--bg-elevated) !important;
73
+ color: var(--accent) !important;
74
+ }/* Q6: native <select> theming for light/mint.
75
+ Without this,
76
+ dropdowns on Nodes / Messages / Logs / Settings render with
77
+ the OS default chrome (heavy chevron,
78
+ off-spec font weight,
79
+ no border
80
+ radius). We strip the native chrome with appearance-none {
81
+ -webkit-appearance: none !important;
82
+ -moz-appearance: none !important;
83
+ appearance: none !important;
84
+ background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%23525a66' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 8l5 5 5-5'/%3E%3C/svg%3E") !important;
85
+ background-repeat: no-repeat !important;
86
+ background-position: right 0.6rem center !important;
87
+ background-size: 1rem 1rem !important;
88
+ padding-right: 2rem !important;
89
+ background-color: var(--bg-secondary) !important;
90
+ border-radius: 8px;
91
+ }/* Mint reuses the same chevron — color picker would need its own gray,
92
+ but
93
+ accent is similar enough that the default chevron color reads OK. */
158
94
 
159
- /* page-level dark backgrounds use --bg */
160
- [data-theme="light"] .bg-\[\#050510\],
161
- [data-theme="light"] .bg-\[\#080814\],
162
- [data-theme="light"] .bg-\[\#0a0a14\],
163
- [data-theme="light"] .bg-\[\#0a0a15\],
164
- [data-theme="light"] .bg-\[\#0a0a1a\],
165
- [data-theme="light"] .bg-\[\#0d0d1a\],
166
- [data-theme="light"] .bg-\[\#11112a\],
167
- [data-theme="light"] .bg-\[\#11111c\],
168
- [data-theme="light"] .bg-\[\#161630\],
169
- [data-theme="light"] .bg-\[\#161640\],
170
- [data-theme="mint"] .bg-\[\#050510\],
171
- [data-theme="mint"] .bg-\[\#080814\],
172
- [data-theme="mint"] .bg-\[\#0a0a14\],
173
- [data-theme="mint"] .bg-\[\#0a0a15\],
174
- [data-theme="mint"] .bg-\[\#0a0a1a\],
175
- [data-theme="mint"] .bg-\[\#0d0d1a\],
176
- [data-theme="mint"] .bg-\[\#11112a\],
177
- [data-theme="mint"] .bg-\[\#11111c\],
178
- [data-theme="mint"] .bg-\[\#161630\],
179
- [data-theme="mint"] .bg-\[\#161640\] {
180
- background-color: var(--bg) !important;
95
+ /* KPI stat cards in light mode: kill the gradient wash (which reads as
96
+ pale-mud on white) {
97
+ border-top-width: 2px;
98
+ box-shadow: 0 1px 0 rgba(15, 23, 42, 0.04), 0 4px 12px -8px rgba(15, 23, 42, 0.08);
99
+ }/* ─────────────────────────────────────────────────────────────────────
100
+ Login surface (round 1 polish)
101
+ Restrained,
102
+ minimal — first impression for new Agent Network users.
103
+ ─────────────────────────────────────────────────────────────────── */
104
+ .anet-login-bg {
105
+ background:
106
+ radial-gradient(circle at 50% 30%, rgba(34, 211, 238, 0.05), transparent 55%),
107
+ radial-gradient(circle at 33% 75%, rgba(59, 130, 246, 0.045), transparent 55%);
108
+ }/* Health banner — sticky thin strip above content {
109
+ background-color: rgba(16, 185, 129, 0.08) !important;
110
+ color: #047857 !important;
111
+ border-color: rgba(16, 185, 129, 0.25) !important;
112
+ }/* Sidebar brand "online" pulse — slow,
113
+ restrained,
114
+ NO glow.
115
+ 1.6s opacity drift between 1.0 and 0.5; nothing scales,
116
+ nothing blurs. */
117
+ @keyframes anet-brand-pulse-kf {
118
+ 0%, 100% { opacity: 1; }
119
+ 50% { opacity: 0.4; }
120
+ }.anet-brand-pulse {
121
+ animation: anet-brand-pulse-kf 1.6s ease-in-out infinite;
181
122
  }
182
-
183
- /* card / elevated backgrounds (incl. opacity variants) */
184
- [data-theme="light"] .bg-\[\#111128\],
185
- [data-theme="light"] .bg-\[\#111128\]\/80,
186
- [data-theme="light"] .bg-\[\#1a1a2a\],
187
- [data-theme="light"] .bg-\[\#1a1a3a\],
188
- [data-theme="mint"] .bg-\[\#111128\],
189
- [data-theme="mint"] .bg-\[\#111128\]\/80,
190
- [data-theme="mint"] .bg-\[\#1a1a2a\],
191
- [data-theme="mint"] .bg-\[\#1a1a3a\] {
192
- background-color: var(--bg-secondary) !important;
123
+ @media (prefers-reduced-motion: reduce) {
124
+ .anet-brand-pulse { animation: none; }
125
+ }/* Skeleton pulse — same 1.6s rhythm as the brand pulse but applied to a
126
+ container so all bars inside breathe together. Drifts between full
127
+ token value and 60% so light themes show a calm grey-to-bg-elevated
128
+ fade and dark themes show a calm navy-to-elevated fade. */
129
+ @keyframes anet-skeleton-kf {
130
+ 0%, 100% { opacity: 1; }
131
+ 50% { opacity: 0.55; }
132
+ }.anet-skeleton-pulse {
133
+ animation: anet-skeleton-kf 1.6s ease-in-out infinite;
193
134
  }
194
-
195
- /* hover / input field backgrounds */
196
- [data-theme="light"] .bg-\[\#2a2a4a\],
197
- [data-theme="mint"] .bg-\[\#2a2a4a\] {
198
- background-color: var(--bg-elevated) !important;
135
+ @media (prefers-reduced-motion: reduce) {
136
+ .anet-skeleton-pulse { animation: none; }
137
+ }/* Skeleton bar fill — different per theme so it's visible against the
138
+ card bg without being loud. Dark: lighter navy block on dark card.
139
+ Light: mid-grey on white card (not too light or it disappears). */
140
+ .anet-skeleton-bar {
141
+ background-color: #1c1c1f;
142
+ }/* Command palette modal surface — bumped depth so it sits clearly above
143
+ the page content in both themes. Light/mint: white shell with a real
144
+ shadow (not the dark navy that the CSS shim would normally translate
145
+ `bg-[#111113]` into). */
146
+ .anet-cmdk kbd {
147
+ font-size: 9px;
148
+ line-height: 1;
149
+ padding: 1px 4px;
150
+ border: 1px solid currentColor;
151
+ border-radius: 3px;
152
+ opacity: 0.6;
153
+ }/* Round 20 — matched-char highlight inside cmdk titles/hints. Inherits
154
+ line color (`color`) instead of forcing one — keeps text readable on
155
+ both inactive (gray) and active (cyan-300) rows. <mark> default is a
156
+ garish yellow block,
157
+ which we override. */
158
+ .anet-cmdk-mark {
159
+ background: transparent;
160
+ color: inherit;
161
+ font-weight: 600;
162
+ /* Subtle underline so the mark stays visible even on the active row
163
+ where the background tint matches the accent color. */
164
+ text-decoration: underline;
165
+ text-decoration-color: currentColor;
166
+ text-decoration-thickness: 1px;
167
+ text-underline-offset: 2px;
168
+ text-decoration-skip-ink: none;
169
+ }/* Cyber: accent the matched glyph color so it pops on the dark bg. */
170
+ [data-theme="cyber"] .anet-cmdk-mark,
171
+ :root:not([data-theme]) .anet-cmdk-mark {
172
+ color: #22d3ee;
173
+ }/* On the active (cyan-tinted) row,
174
+ the title is already cyan-300 — use
175
+ white-with-underline so the highlight reads as emphasis,
176
+ not redundancy. */
177
+ .anet-cmdk-row.bg-cyan-500\/10 .anet-cmdk-mark {
178
+ color: #f0fdff;
179
+ }/* Tasks tab strip horizontal scroll fade — gradient overlays at left/right
180
+ edges hint that there's more content when the row overflows. Only
181
+ applied on <sm (where the strip actually scrolls). The wrapper is
182
+ `position: relative` and the fades are absolute siblings. */
183
+ .anet-tabstrip-wrap {
184
+ position: relative;
185
+ }.anet-tabstrip-wrap::before,
186
+ .anet-tabstrip-wrap::after {
187
+ content: '';
188
+ position: absolute;
189
+ top: 4px;
190
+ bottom: 4px;
191
+ width: 20px;
192
+ pointer-events: none;
193
+ z-index: 1;
194
+ }.anet-tabstrip-wrap::before {
195
+ left: 0;
196
+ background: linear-gradient(to right, #161618, transparent);
197
+ border-radius: 8px 0 0 8px;
198
+ }.anet-tabstrip-wrap::after {
199
+ right: 0;
200
+ background: linear-gradient(to left, #161618, transparent);
201
+ border-radius: 0 8px 8px 0;
199
202
  }
203
+ @media (min-width: 640px) {
204
+ /* sm: stops scrolling, hide the fades */
205
+ .anet-tabstrip-wrap::before,
206
+ .anet-tabstrip-wrap::after { display: none; }
207
+ }/* Round 30 — global drawer entrance keyframes so TaskDrawer,
208
+ TaskChatPanel,
209
+ and any future right-side panel share one source. Previously the
210
+ keyframe lived inside TaskChatPanel's <style jsx global>; if that
211
+ component never mounted,
212
+ the keyframe was missing for TaskDrawer. */
213
+ @keyframes anet-slide-in {
214
+ from { transform: translateX(100%); }
215
+ to { transform: translateX(0); }
216
+ }.animate-slide-in { animation: anet-slide-in 0.2s ease-out; }
200
217
 
201
- /* hardcoded borders */
202
- [data-theme="light"] .border-\[\#111128\],
203
- [data-theme="light"] .border-\[\#1a1a2a\],
204
- [data-theme="light"] .border-\[\#2a2a4a\],
205
- [data-theme="light"] .border-\[\#3a3a5a\],
206
- [data-theme="mint"] .border-\[\#111128\],
207
- [data-theme="mint"] .border-\[\#1a1a2a\],
208
- [data-theme="mint"] .border-\[\#2a2a4a\],
209
- [data-theme="mint"] .border-\[\#3a3a5a\] {
210
- border-color: var(--border) !important;
218
+ @keyframes anet-fade-in {
219
+ from { opacity: 0; }
220
+ to { opacity: 1; }
221
+ }.anet-fade-in { animation: anet-fade-in 0.15s ease-out; }/* Round 184 — chrome reset button click-spin. The reset button's
222
+ onClick triggers R168 smoothView crossfade on the canvas,
223
+ but the
224
+ BUTTON itself had no visual feedback that the click registered. A
225
+ single 360° rotation on the refresh-arrow icon (450ms ease-out) is
226
+ a quick "yes,
227
+ I heard you" confirmation. Counter-clockwise (-360°)
228
+ reads as "rewind" / "undo" — natural for a reset gesture. Mirror
229
+ to R52's hub click ripple at the chrome scope. */
230
+ @keyframes anet-reset-spin {
231
+ from { transform: rotate(0deg); }
232
+ to { transform: rotate(-360deg); }
233
+ }.anet-reset-spin {
234
+ animation: anet-reset-spin 0.45s cubic-bezier(0.4, 0, 0.2, 1);
235
+ transform-origin: center;
236
+ transform-box: fill-box;
237
+ }/* Round 186 — chrome zoom-in / zoom-out buttons get a brief icon pop
238
+ on click. Same click-feel idiom R184 added for the reset button —
239
+ the canvas already smoothes via R168/R169 smoothView,
240
+ but the
241
+ chrome BUTTON itself needs its own confirmation. A quick scale
242
+ up-then-back (1 → 1.18 → 1 over 220ms) reads as "tapped". Same
243
+ transform-box: fill-box anchoring as R184 keeps the icon
244
+ centered in place. */
245
+ @keyframes anet-chrome-pop {
246
+ 0% { transform: scale(1); }
247
+ 50% { transform: scale(1.18); }
248
+ 100% { transform: scale(1); }
249
+ }.anet-chrome-pop {
250
+ animation: anet-chrome-pop 0.22s ease-out;
251
+ transform-origin: center;
252
+ transform-box: fill-box;
253
+ }/* Round 498 — recent-signal row hot-count subtle pulse (信息密度 +
254
+ 呼吸感 themes). When a row's edge count crosses the hot threshold
255
+ (≥ 10),
256
+ the existing R127/R320/R445 amber-fill + fw-700 typography
257
+ already calls attention; R498 adds a slow 3s opacity breath (0.85 ↔
258
+ 1.0) on the digit so the hot tspans gently pulse — at-a-glance scan
259
+ of the panel reads "high-traffic lane right here" with motion in
260
+ addition to color+weight. 3s cycle is deliberate: faster (1-2s)
261
+ would compete with the working-status halo cadence; slower (5s+)
262
+ would lose the "alive" signal. Amplitude 15% (matches R497 hub-
263
+ highlight breath idiom).
264
+ prefers-reduced-motion handled by the R29 blanket override
265
+ (animation-duration: 0.001ms !important) — no per-class guard
266
+ needed. The component-side gate (`!reducedMotion && isHot`)
267
+ ensures the className is only applied when both conditions hold,
268
+ so even without the blanket the no-motion preference is respected. */
269
+ @keyframes anet-recent-hot-pulse-kf {
270
+ 0%, 100% { opacity: 0.85; }
271
+ 50% { opacity: 1; }
272
+ }.anet-recent-hot-pulse {
273
+ animation: anet-recent-hot-pulse-kf 3s ease-in-out infinite;
274
+ }/* Round 36 — current-step ring pulse on TaskDrawer timeline. Halo gently
275
+ breathes around the active step's dot so users can spot "task is here". */
276
+ @keyframes anet-current-step-pulse-kf {
277
+ 0%, 100% { transform: scale(1); opacity: 0.6; }
278
+ 50% { transform: scale(1.25); opacity: 0.0; }
279
+ }.anet-current-step-pulse {
280
+ animation: anet-current-step-pulse-kf 1.6s ease-in-out infinite;
281
+ }/* Round 45 — TopoGraph rotating radar sweep wedge. 6s slow rotation
282
+ gives the topology canvas a real "radar scan" feel without distracting
283
+ from the agent nodes.
284
+ R146: split shorthand into longhand so per-render inline style can
285
+ override animation-duration via --sweep-dur,
286
+ joining the R145
287
+ --spoke-dur / R132 --march-dur pattern. Default 6s preserves R45's
288
+ original cadence for any caller that forgets the var. */
289
+ @keyframes anet-topo-sweep-kf {
290
+ 0% { transform: rotate(0deg); }
291
+ 100% { transform: rotate(360deg); }
292
+ }.anet-topo-sweep {
293
+ animation-name: anet-topo-sweep-kf;
294
+ animation-duration: var(--sweep-dur, 6s);
295
+ animation-timing-function: linear;
296
+ animation-iteration-count: infinite;
211
297
  }
212
298
 
213
- /* dark-tuned gradient buttons flat brand color in light themes */
214
- [data-theme="light"] [class*="from-cyan-"][class*="to-blue-"],
215
- [data-theme="light"] [class*="from-cyan-"][class*="to-emerald-"],
216
- [data-theme="light"] [class*="from-emerald-"][class*="to-cyan-"],
217
- [data-theme="light"] [class*="from-blue-"][class*="to-cyan-"] {
218
- background: var(--accent) !important;
219
- color: var(--accent-fg) !important;
220
- }
221
- [data-theme="light"] [class*="from-cyan-"][class*="to-blue-"]:hover,
222
- [data-theme="light"] [class*="from-cyan-"][class*="to-emerald-"]:hover,
223
- [data-theme="light"] [class*="from-emerald-"][class*="to-cyan-"]:hover,
224
- [data-theme="light"] [class*="from-blue-"][class*="to-cyan-"]:hover {
225
- background: #00b894 !important;
226
- filter: brightness(1.05);
227
- }
228
- [data-theme="light"] .text-gray-100,
229
- [data-theme="light"] .text-gray-200,
230
- [data-theme="light"] .text-gray-300,
231
- [data-theme="light"] .text-gray-100\/90,
232
- [data-theme="mint"] .text-gray-100,
233
- [data-theme="mint"] .text-gray-200,
234
- [data-theme="mint"] .text-gray-300,
235
- [data-theme="mint"] .text-gray-100\/90 {
236
- color: var(--fg) !important;
237
- }
238
- [data-theme="light"] .text-gray-400,
239
- [data-theme="light"] .text-gray-500,
240
- [data-theme="light"] .text-gray-600,
241
- [data-theme="light"] .placeholder-gray-600::placeholder,
242
- [data-theme="mint"] .text-gray-400,
243
- [data-theme="mint"] .text-gray-500,
244
- [data-theme="mint"] .text-gray-600,
245
- [data-theme="mint"] .placeholder-gray-600::placeholder {
246
- color: var(--fg-muted) !important;
299
+ /* R155 focus-visible outline for TopoGraph chip-row interactives.
300
+ Cyber theme's dark canvas often swallows the browser default focus
301
+ ring; R154 fixed it for chrome buttons via Tailwind's focus-visible:
302
+ ring (box-shadow). Chip-row chips reuse `box-shadow` for the R88 /
303
+ R139 / R140 pin-mirror inset — so a focus box-shadow would conflict
304
+ with the pinned-state shadow. `outline` is a separate CSS property
305
+ and doesn't fight box-shadow, so we use it here. `currentColor`
306
+ inherits the chip's own accent (green for working, cyan for online,
307
+ per-vendor for letters, etc.). Only fires on :focus-visible
308
+ (keyboard) — mouse focus stays unstyled.
309
+ Round 490 / Loop — focus outline transitions on color rather than
310
+ hard-cutting. Pre-R490 the keyboard focus ring snapped in/out
311
+ instantly (no `transition` declaration), while every other hover/
312
+ pin state on the same chip eased through the Hero D 200ms ease-out
313
+ vocabulary (R459-R475 cluster + R489 hover ring). Keyboard users
314
+ tabbing through chips saw discontinuous focus jumps — pointer
315
+ users saw smooth motion. R490 unifies the two by holding a
316
+ permanent transparent outline (no painted footprint, no layout
317
+ shift — `outline` is drawn outside the border-box and doesn't
318
+ trigger reflow) and transitioning ONLY outline-color when focus
319
+ arrives/leaves. The cyber-theme dark canvas keyboard nav now
320
+ reads as one motion vocabulary with pointer hover. */
321
+ .anet-topo-chip-focus {
322
+ outline: 2px solid transparent;
323
+ outline-offset: 1px;
324
+ /* R490 (revised) — Tailwind utility classes on the chips (transition-
325
+ colors / transition-transform / duration-200) set their own
326
+ `transition-property` list at the same selector specificity (0,1,0),
327
+ and since Tailwind's stylesheet loads after globals.css they win
328
+ the cascade — without `!important` my outline-color transition is
329
+ ignored. The unified list below replaces Tailwind's narrower list
330
+ with a superset (color, bg, border, opacity, box-shadow, transform
331
+ all preserved) plus `outline-color` for R490. All at 200ms ease-
332
+ out, matching the chip-row's existing Tailwind duration-200 ease-
333
+ out so visible animations on the chip are unchanged.
334
+
335
+ Round 524 / Loop — CORRECTION + bug fix.
336
+ R490's original 'trade-off' note read:
337
+ 'chips with inline style.transition (R210 pressure-bar
338
+ segments at 220ms width / 150ms boxShadow / 150ms filter)
339
+ keep their inline value because inline style wins over class
340
+ rules regardless of !important.'
341
+ That was WRONG about CSS cascade. Per the spec, `!important`
342
+ author declarations DO override normal-priority inline-style
343
+ declarations. So R490's transition-property list silently
344
+ dropped every inline transition that targeted a property NOT
345
+ in this class's list — including R165's width 220ms tween on
346
+ the pressure-bar segments AND R210's filter brightness(1.2)
347
+ hover on those same segments. Both motion polishes have been
348
+ silently broken (snapping instead of easing) since R490
349
+ landed; banked as test-craft lesson in R523.
350
+ R524 fixes by extending the transition-property list to
351
+ include `width` and `filter` so the pressure-bar segments
352
+ (and any future chip-focus surface with width/filter motion)
353
+ animate per the class's unified 200ms ease-out. The cadence
354
+ shift (R165's 220ms → 200ms, R210's 150ms → 200ms) loses 20-
355
+ 50ms of intent precision but slots into the dashboard's
356
+ uniform 200ms motion vocabulary the rest of the chip family
357
+ already uses — net visible improvement (motion was DEAD,
358
+ now eases at 200ms; that's a strictly better state than
359
+ snapping).
360
+ Other chip-focus surfaces (filter pills, online/working
361
+ chips, minimap container, "+N more" footer's <g>) don't
362
+ change width or filter at runtime, so adding these
363
+ properties to the list is a no-op for them. */
364
+ transition-property:
365
+ outline-color, color, background-color, border-color,
366
+ text-decoration-color, fill, stroke, opacity, box-shadow, transform,
367
+ width, filter
368
+ !important;
369
+ transition-duration: 200ms !important;
370
+ transition-timing-function: ease-out !important;
247
371
  }
248
- [data-theme="light"] .text-white,
249
- [data-theme="mint"] .text-white {
250
- color: var(--fg) !important;
372
+ .anet-topo-chip-focus:focus-visible {
373
+ outline-color: currentColor;
251
374
  }
252
375
 
253
- [data-theme="light"] .text-zinc-100,
254
- [data-theme="light"] .text-zinc-200,
255
- [data-theme="light"] .text-white,
256
- [data-theme="mint"] .text-zinc-100,
257
- [data-theme="mint"] .text-zinc-200,
258
- [data-theme="mint"] .text-white {
259
- color: var(--fg) !important;
376
+ /* R156 — focus-visible outline for SVG-side TopoGraph interactives.
377
+ Same idiom as R155's chip-row class but for SVG <g> elements
378
+ (recent-signal rows, legend rows, group labels, edge badges,
379
+ node bodies, "+N more" footer). Each <g> already has role=
380
+ "button"/"link" + tabIndex=0 + onKeyDown (R116/R121/R133/R143/
381
+ R144/R151/R152). Browser default focus outline on SVG is hard
382
+ to spot against the canvas; explicit cyan-300 ring matches the
383
+ dashboard's legendAccent. 2-px outline-offset gives breathing
384
+ room around the painted bounding box.
385
+ Round 491 / Loop — SVG-side counterpart to R490's chip-focus
386
+ outline-color transition. Pre-R491 keyboard focus on SVG g
387
+ elements (recent rows, legend rows, group labels, edge badges,
388
+ nodes, "+N more") snapped instantly; HTML chips (post-R490) now
389
+ ease through 200ms ease-out but SVG g still hard-cut. Same
390
+ baseline-transparent + transition-outline-color recipe brings
391
+ the SVG canvas into the unified keyboard motion vocabulary —
392
+ keyboard users tabbing between chip-row and SVG canvas see
393
+ ONE smooth fade timing instead of HTML-smooth-then-SVG-snap.
394
+ The cyan-300 (#67e8f9) target color matches R156's original
395
+ choice (legendAccent visual identity); only the transition
396
+ timing is new.
397
+ Note on cascade: SVG g elements rarely carry Tailwind
398
+ transition-* utility classes (Tailwind defaults are HTML-
399
+ centric), so the (0,1,0) specificity of .anet-topo-svg-focus
400
+ typically wins without !important. Adding !important defensively
401
+ anyway since the React component may add future inline styles
402
+ or Tailwind classes. Same trade-off as R490 (inline
403
+ style.transition still wins if added). */
404
+ .anet-topo-svg-focus {
405
+ outline: 2px solid transparent;
406
+ outline-offset: 2px;
407
+ transition-property: outline-color !important;
408
+ transition-duration: 200ms !important;
409
+ transition-timing-function: ease-out !important;
260
410
  }
261
-
262
- [data-theme="light"] .text-zinc-400,
263
- [data-theme="light"] .text-zinc-500,
264
- [data-theme="mint"] .text-zinc-400,
265
- [data-theme="mint"] .text-zinc-500 {
266
- color: var(--fg-muted) !important;
411
+ .anet-topo-svg-focus:focus-visible {
412
+ outline-color: #67e8f9;
267
413
  }
268
414
 
269
- [data-theme="light"] .bg-zinc-900,
270
- [data-theme="light"] .bg-zinc-950,
271
- [data-theme="light"] .bg-black,
272
- [data-theme="mint"] .bg-zinc-900,
273
- [data-theme="mint"] .bg-zinc-950,
274
- [data-theme="mint"] .bg-black {
275
- background-color: var(--bg-secondary) !important;
415
+ /* Round 46 — animated dashed spokes flowing from hub outward. Each spoke
416
+ gets a small animationDelay (set inline) so the dashes don't all flow
417
+ in sync — reads as "command relay" pulse rather than a metronome.
418
+ R145: split shorthand into longhand so per-render inline style can
419
+ override animation-duration via the --spoke-dur custom property —
420
+ same idiom R132 uses for --march-dur on group boxes. Default 2.4s
421
+ when --spoke-dur isn't set, so any future caller that forgets the
422
+ var keeps R46's original cadence. */
423
+ @keyframes anet-topo-spoke-flow-kf {
424
+ from { stroke-dashoffset: 0; }
425
+ to { stroke-dashoffset: -40; }
276
426
  }
277
-
278
- [data-theme="light"] .bg-zinc-800,
279
- [data-theme="mint"] .bg-zinc-800 {
280
- background-color: var(--bg-elevated) !important;
427
+ .anet-topo-spoke-flow {
428
+ animation-name: anet-topo-spoke-flow-kf;
429
+ animation-duration: var(--spoke-dur, 2.4s);
430
+ animation-timing-function: linear;
431
+ animation-iteration-count: infinite;
281
432
  }
282
433
 
283
- [data-theme="light"] .border-zinc-700,
284
- [data-theme="light"] .border-zinc-800,
285
- [data-theme="light"] .border-white\/10,
286
- [data-theme="light"] .border-white\/5,
287
- [data-theme="mint"] .border-zinc-700,
288
- [data-theme="mint"] .border-zinc-800,
289
- [data-theme="mint"] .border-white\/10,
290
- [data-theme="mint"] .border-white\/5 {
291
- border-color: var(--border) !important;
434
+ /* Round 85 — group-box "marching ants" perimeter drift.
435
+ Applied to a grid-layout group box whose `statuses.working > 0`
436
+ AND that is neither hovered nor pinned (those treatments already
437
+ carry their own visual emphasis via solid stroke). Slow 12s
438
+ linear cycle reads as ambient motion, not animation — the eye
439
+ parses "this group has live work" without registering the box
440
+ as moving. R86 stride matches the box's `strokeDasharray="6 6"`
441
+ so a single full cycle returns to a frame-identical state.
442
+ prefers-reduced-motion handled by the blanket override below.
443
+ R132: split shorthand into longhand so per-group inline style can
444
+ override animation-duration via the --march-dur custom property
445
+ without re-declaring keyframe + timing + iteration count. Default
446
+ stays at 12s when --march-dur isn't set, so existing single-group
447
+ call sites keep their cadence. */
448
+ @keyframes anet-topo-groupbox-march-kf {
449
+ from { stroke-dashoffset: 0; }
450
+ to { stroke-dashoffset: -12; }
292
451
  }
293
-
294
- /* Light theme: additional zinc shades + accent colors override */
295
- [data-theme="light"] .bg-zinc-700,
296
- [data-theme="light"] .bg-zinc-600,
297
- [data-theme="mint"] .bg-zinc-700,
298
- [data-theme="mint"] .bg-zinc-600 {
299
- background-color: var(--bg-elevated) !important;
452
+ .anet-topo-groupbox-live {
453
+ animation-name: anet-topo-groupbox-march-kf;
454
+ animation-duration: var(--march-dur, 12s);
455
+ animation-timing-function: linear;
456
+ animation-iteration-count: infinite;
300
457
  }
301
- [data-theme="light"] .text-zinc-300,
302
- [data-theme="light"] .text-zinc-600,
303
- [data-theme="mint"] .text-zinc-300,
304
- [data-theme="mint"] .text-zinc-600 {
305
- color: var(--fg-muted) !important;
458
+
459
+ /* Round 553 — title-block brand logo subtle idle breath.
460
+ 5th anchor in the 呼吸感 breath family — cadence ladder:
461
+ row hot 3s
462
+ hub idle 4s
463
+ brand logo 5s ← this round
464
+ watermark 6s
465
+ crescent 7s
466
+ Pure opacity breath (0.92 ↔ 1, ~8% delta) on the title-block
467
+ crescent SVG so the brand mark reads as a "living mark" at
468
+ rest. Composes cleanly with R548 hover:scale-105 +
469
+ R549 hover:rotate-6 (independent properties: opacity vs
470
+ scale/rotate; the moon keeps breathing as it lifts on hover).
471
+ prefers-reduced-motion handled by R29 blanket override —
472
+ animation-duration → 0.001ms neutralises the keyframe without
473
+ killing the static opacity baseline. Component-side gate also
474
+ applied (`!reducedMotion` toggles the className) as canonical
475
+ TopoGraph breath pattern (sibling to R130 hub idle / R519
476
+ watermark / R326 crescent breath gates). */
477
+ @keyframes anet-topo-brand-logo-breath-kf {
478
+ 0%, 100% { opacity: 1; }
479
+ 50% { opacity: 0.92; }
306
480
  }
307
- [data-theme="light"] .text-zinc-700,
308
- [data-theme="light"] .text-zinc-800,
309
- [data-theme="mint"] .text-zinc-700,
310
- [data-theme="mint"] .text-zinc-800 {
311
- color: var(--fg) !important;
481
+ .anet-topo-brand-logo-breath {
482
+ animation: anet-topo-brand-logo-breath-kf 5s ease-in-out infinite;
312
483
  }
313
484
 
314
- /* Light theme: emerald / cyan / indigo accents unified brand emerald */
315
- [data-theme="light"] .text-emerald-400,
316
- [data-theme="light"] .text-emerald-300,
317
- [data-theme="light"] .text-cyan-400,
318
- [data-theme="light"] .text-cyan-300,
319
- [data-theme="light"] .text-indigo-400 {
320
- color: var(--accent) !important;
485
+ /* Round 683 brand 书生 logo extends single-layer drop-shadow hover
486
+ (R604, Tailwind `hover:drop-shadow-[0_0_8px_currentColor]`) to the
487
+ multi-layer halo family vocabulary: near 8px + far 16px (2× blur
488
+ stride) at currentColor (inherits inline style.color = teal #0d9488
489
+ / cyan #67e8f9), stacked with brightness(1.10). Matches R642-R667
490
+ 0.5×-falloff convention by using currentColor for both layers —
491
+ the same hue at native alpha layers up because drop-shadow alpha
492
+ accumulates additively when stacked.
493
+ Pure CSS `:hover` so no new React state needed; the SVG element
494
+ listens via its own pseudo-class. Inline transition list already
495
+ covers `filter 200ms ease-out` (R557) so the multi-layer halo eases
496
+ under the same cadence. Tailwind v4 utility composition for multi-
497
+ drop-shadow doesn't work cleanly (per R659 detour note), so dropping
498
+ the utility classes (`hover:brightness-110`, `hover:drop-shadow-[...]`)
499
+ in favor of an explicit CSS rule that wins over the cascade.
500
+ 42nd anchor in multi-layer halo family — first brand-mark anchor. */
501
+ .anet-topo-brand-logo-mark:hover {
502
+ filter: drop-shadow(0 0 8px currentColor)
503
+ drop-shadow(0 0 16px currentColor)
504
+ brightness(1.10);
321
505
  }
322
506
 
323
- /* Light theme: every "-300/400" accent text color needs a darker mid-tone
324
- variant. The default Tailwind `-300/-400` shades are designed for dark
325
- backgrounds putting them on white gives "barely visible" text (the
326
- problem Vincent flagged: "浅色下很多字体看不见"). Map each family to its
327
- `-700` equivalent so contrast against white card is AA. */
328
- [data-theme="light"] .text-green-400,
329
- [data-theme="light"] .text-green-300,
330
- [data-theme="mint"] .text-green-400,
331
- [data-theme="mint"] .text-green-300 { color: #047857 !important; }
332
-
333
- [data-theme="light"] .text-yellow-400,
334
- [data-theme="light"] .text-yellow-300,
335
- [data-theme="mint"] .text-yellow-400,
336
- [data-theme="mint"] .text-yellow-300 { color: #92400e !important; }
507
+ /* Round 684 title-block H2 "Command mesh" gains multi-layer halo
508
+ via the wrapper's `:hover` (sibling pattern to R683 brand logo —
509
+ both lift together when ANY surface in the title-block cluster
510
+ is hovered, including the brand logo, kicker, or H2 itself).
511
+ Pre-R684 the H2 had only 2 typographic hover axes (R554 tracking-
512
+ tighter + R556 font-bold) via group-hover Tailwind utilities; the
513
+ paint axis was untouched. R684 adds the multi-layer halo paint
514
+ axis to the H2's hover signature — same near + far layered glow
515
+ vocabulary the 42 other surfaces in the family share.
516
+ Stride: 4 + 8px (2× blur stride at h2 scale, text-lg = 18px —
517
+ smaller than brand logo's 8+16 since the H2 is text not graphic).
518
+ Tint: currentColor — inherits the H2's resolved fill color
519
+ (white on cyber, darker on light theme overrides).
520
+ No brightness multiply — H2 text-white is already near-peak
521
+ luminance on cyber; adding brightness would push past saturation.
522
+ The selector targets the H2 via its data-attr `data-topo-section-
523
+ title` nested under the title-block wrapper's group container
524
+ (`data-topo-section-titleblock-group`). Inline `transition-
525
+ [letter-spacing,font-weight]` on the H2 doesn't include filter,
526
+ so the halo would snap on. Add a transition rule here so the
527
+ halo eases under the same 200ms cadence as the existing 2 axes —
528
+ keeps the cluster's hover gesture motion-coherent.
529
+ 43rd anchor in multi-layer halo family — sibling to R683 brand
530
+ logo, closing the title-block cluster's paint-axis halo at the
531
+ H2 element scope. */
532
+ [data-topo-section-titleblock-group]:hover [data-topo-section-title] {
533
+ filter: drop-shadow(0 0 4px currentColor)
534
+ drop-shadow(0 0 8px currentColor);
535
+ }
536
+ [data-topo-section-title] {
537
+ transition: letter-spacing 200ms ease-out,
538
+ font-weight 200ms ease-out,
539
+ filter 200ms ease-out;
540
+ }
337
541
 
338
- [data-theme="light"] .text-amber-400,
339
- [data-theme="light"] .text-amber-300,
340
- [data-theme="mint"] .text-amber-400,
341
- [data-theme="mint"] .text-amber-300 { color: #92400e !important; }
542
+ /* Round 685 — kicker "Network Topology" gains multi-layer halo via
543
+ the title-block wrapper's `:hover`, closing the title-block trio
544
+ at 3/3 paint-axis halo (logo R683 + H2 R684 + kicker R685). Pre-
545
+ R685 the kicker had only 2 hover axes (R555 tracking-spread +
546
+ color lift gray-500 → gray-400); the paint axis was untouched.
547
+ R685 adds 2-layer drop-shadow at 2 + 4px stride (2× blur stride
548
+ at kicker scale — text-xs = 12px, smaller than H2's 4+8 and
549
+ logo's 8+16). currentColor inherits the kicker's resolved fill
550
+ (gray-500 rest / gray-400 hover post-R555) so the halo paints
551
+ in the same hue as the text.
552
+ Selector targets the kicker via `data-topo-section-kicker` data-
553
+ attr (already present on the kicker div). Sibling rule + transition
554
+ pattern to R684 H2; both fire on the wrapper's :hover and ease at
555
+ 200ms.
556
+ When the user hovers the title cluster, ALL 3 surfaces (logo +
557
+ H2 + kicker) glow simultaneously — cluster reads as one
558
+ coherent hover unit, paint axis CLOSED across all surfaces.
559
+ 44th anchor in multi-layer halo family — completes title-block
560
+ trio 3/3. */
561
+ [data-topo-section-titleblock-group]:hover [data-topo-section-kicker] {
562
+ filter: drop-shadow(0 0 2px currentColor)
563
+ drop-shadow(0 0 4px currentColor);
564
+ }
565
+ [data-topo-section-kicker] {
566
+ transition: letter-spacing 200ms ease-out,
567
+ color 200ms ease-out,
568
+ filter 200ms ease-out;
569
+ }
342
570
 
343
- [data-theme="light"] .text-red-400,
344
- [data-theme="light"] .text-red-300,
345
- [data-theme="mint"] .text-red-400,
346
- [data-theme="mint"] .text-red-300 { color: #b91c1c !important; }
571
+ /* Round 697 — chrome strip segmented-control wrappers gain a wrapper-
572
+ level multi-layer halo when ANY inner button is hovered. Pre-R697
573
+ the 3 chrome wrappers (Layout / nodeSize / zoom) had only static
574
+ border + background individual buttons inside halo via R674/R675/
575
+ R673/R668 but the parent wrapper stayed flat. R697 uses CSS `:has()`
576
+ to detect inner button hover and applies a 2-layer drop-shadow at
577
+ cyan-300 (#67e8f9) — compact-chrome tier (2+4 stride, 0.5/0.25 alpha).
347
578
 
348
- [data-theme="light"] .text-purple-400,
349
- [data-theme="light"] .text-purple-300,
350
- [data-theme="mint"] .text-purple-400,
351
- [data-theme="mint"] .text-purple-300 { color: #6d28d9 !important; }
579
+ When user hovers a child Ring/Grid/S/M/L/zoom button, the WRAPPER
580
+ itself echoes the inner-button halo outward in a soft glow — chrome
581
+ strip reads as one coherent "this control group is active" gesture
582
+ alongside the per-button cyan halo.
352
583
 
353
- [data-theme="light"] .text-orange-400,
354
- [data-theme="light"] .text-orange-300,
355
- [data-theme="mint"] .text-orange-400,
356
- [data-theme="mint"] .text-orange-300 { color: #c2410c !important; }
584
+ Cyan-300 hardcoded (vs theme-driven pal.legendAccent) since CSS
585
+ doesn't have a clean handle on the React-computed palette token at
586
+ this wrapper level. Cyan-300 ≈ light theme's pal.legendAccent
587
+ (#0d9488 teal-600) is bluish; the halo is hover-only + brief so the
588
+ slight cross-theme hue mismatch is acceptable.
357
589
 
358
- [data-theme="light"] .text-blue-400,
359
- [data-theme="light"] .text-blue-300,
360
- [data-theme="mint"] .text-blue-400,
361
- [data-theme="mint"] .text-blue-300 { color: #1d4ed8 !important; }
590
+ transition: filter 200ms ease-out — matches the inner buttons'
591
+ 200ms cadence so wrapper + buttons ease together on hover-in/out.
362
592
 
363
- [data-theme="light"] .text-pink-400,
364
- [data-theme="light"] .text-pink-300,
365
- [data-theme="mint"] .text-pink-400,
366
- [data-theme="mint"] .text-pink-300 { color: #be185d !important; }
593
+ 53rd anchor in multi-layer halo family — first wrapper-level
594
+ has-hover anchor. Closes chrome-strip "active control group"
595
+ signal at the parent scope alongside R667-R675 per-button anchors. */
596
+ [data-topo-chrome-layout-trailer]:has(button:hover),
597
+ [data-topo-chrome-fleet-group-trailer]:has(button:hover),
598
+ [data-topo-chrome-zoom-wrapper]:has(button:hover) {
599
+ filter: drop-shadow(0 0 2px rgba(103, 232, 249, 0.5))
600
+ drop-shadow(0 0 4px rgba(103, 232, 249, 0.25));
601
+ }
602
+ [data-topo-chrome-layout-trailer],
603
+ [data-topo-chrome-fleet-group-trailer],
604
+ [data-topo-chrome-zoom-wrapper] {
605
+ transition: background-color 200ms ease-out,
606
+ border-color 200ms ease-out,
607
+ filter 200ms ease-out;
608
+ }
367
609
 
368
- [data-theme="light"] .text-violet-400,
369
- [data-theme="light"] .text-violet-300,
370
- [data-theme="mint"] .text-violet-400,
371
- [data-theme="mint"] .text-violet-300 { color: #6d28d9 !important; }
610
+ /* ─────────────────────────────────────────────────────────────────────
611
+ Round 698 — vendor-distribution chip-row outer wrapper joins the
612
+ R697 wrapper-level :has() halo family. Pre-R698 only the chrome-strip
613
+ wrappers (Layout/nodeSize/zoom) echoed inner-button hover at the
614
+ parent scope; the vendor-distribution wrapper stayed flat even though
615
+ its inner vendor letter chips already halo'd per-element (R676/R688/
616
+ R690).
372
617
 
373
- [data-theme="light"] .text-teal-400,
374
- [data-theme="light"] .text-teal-300,
375
- [data-theme="mint"] .text-teal-400,
376
- [data-theme="mint"] .text-teal-300 { color: #0f766e !important; }
618
+ R697 used `:has(button:hover)` because chrome wrappers contain real
619
+ <button> children. The vendor letter chips are `<span role="button">`
620
+ (clickable spans, not buttons — preserves inline-baseline alignment
621
+ with the title text). The :has() selector variant `[role="button"]:hover`
622
+ extends R697's wrapper-level vocabulary from real-button children
623
+ to ARIA-button children — same gesture, broader applicability.
377
624
 
378
- /* Light theme: text-gray-700 (used in footers / muted body) is too pale
379
- when sitting on slightly-grey page bg. Bump to fg-muted. */
380
- [data-theme="light"] .text-gray-700,
381
- [data-theme="mint"] .text-gray-700 { color: var(--fg-muted) !important; }
382
- [data-theme="light"] .bg-emerald-500\/10,
383
- [data-theme="light"] .bg-emerald-500\/20,
384
- [data-theme="light"] .bg-cyan-500\/10,
385
- [data-theme="light"] .bg-cyan-500\/20 {
386
- background-color: rgba(0, 158, 126, 0.08) !important;
387
- }
388
- [data-theme="light"] .border-emerald-500\/30,
389
- [data-theme="light"] .border-emerald-500\/40,
390
- [data-theme="light"] .border-cyan-500\/30 {
391
- border-color: rgba(0, 158, 126, 0.32) !important;
392
- }
625
+ Cyan-300 (#67e8f9) at 2+4 stride, 0.5/0.25 alpha matches R697
626
+ cadence exactly. Transition matches the per-chip 200ms hover cascade.
627
+ When user hovers any A:N / O:N / 书:N / ?:N chip, the outer chip-row
628
+ wrapper echoes the per-chip halo outward — reads as "this vendor-
629
+ distribution control group is active" alongside the existing R676
630
+ vendor letter glyph halo + R688 count suffix halo.
393
631
 
394
- /* Light theme: card-like containers get a subtle elevation shadow
395
- (cards in dark mode lean on glow; in light mode they need a real shadow
396
- to read as a layer rather than blending into the page background) */
397
- [data-theme="light"] .bg-zinc-900,
398
- [data-theme="light"] .bg-zinc-950,
399
- [data-theme="light"] .bg-\[\#0a0a1a\],
400
- [data-theme="light"] .bg-\[\#11112a\],
401
- [data-theme="light"] .bg-\[\#161640\] {
402
- box-shadow: 0 1px 0 rgba(15, 23, 42, 0.04), 0 6px 18px -8px rgba(15, 23, 42, 0.08);
632
+ 54th anchor in multi-layer halo family 2nd wrapper-level :has()
633
+ anchor. First :has() rule targeting [role="button"] (vs <button>)
634
+ children. Closes vendor-chip-row "active control group" signal at
635
+ the parent scope. */
636
+ [data-topo-chrome-vendor-distribution-wrapper]:has([role="button"]:hover) {
637
+ filter: drop-shadow(0 0 2px rgba(103, 232, 249, 0.5))
638
+ drop-shadow(0 0 4px rgba(103, 232, 249, 0.25));
403
639
  }
404
- [data-theme="light"] .bg-zinc-800 {
405
- box-shadow: inset 0 0 0 1px var(--border);
640
+ [data-topo-chrome-vendor-distribution-wrapper] {
641
+ transition: background-color 200ms ease-out,
642
+ border-color 200ms ease-out,
643
+ filter 200ms ease-out;
406
644
  }
407
645
 
408
- /* Light theme: kill any leftover ring / shadow-glow that was tuned for dark bg */
409
- [data-theme="light"] [class*="shadow-emerald"],
410
- [data-theme="light"] [class*="shadow-cyan"],
411
- [data-theme="light"] [class*="shadow-indigo"] {
412
- --tw-shadow-color: rgba(0, 158, 126, 0.18);
413
- }
646
+ /* ─────────────────────────────────────────────────────────────────────
647
+ Round 699 — kicker "Network Topology" at-rest breathing fade.
414
648
 
415
- /* Solid-color action buttons (bg-blue-600 / bg-cyan-600 / bg-green-600 / …)
416
- stay dark navy in light themes because the gradient-button shim above only
417
- matches `from-…-to-…` gradient buttons. Map every primary solid background
418
- to the active accent so Broadcast / Send Task / Register / Change Password
419
- etc. read as the brand color instead of black-on-white. */
420
- [data-theme="light"] .bg-blue-600,
421
- [data-theme="light"] .bg-blue-500,
422
- [data-theme="light"] .bg-cyan-600,
423
- [data-theme="light"] .bg-cyan-500,
424
- [data-theme="light"] .bg-emerald-600,
425
- [data-theme="light"] .bg-emerald-500,
426
- [data-theme="light"] .bg-green-600,
427
- [data-theme="light"] .bg-green-500,
428
- [data-theme="light"] .bg-indigo-600,
429
- [data-theme="light"] .bg-indigo-500,
430
- [data-theme="light"] .bg-purple-600,
431
- [data-theme="light"] .bg-purple-500,
432
- [data-theme="mint"] .bg-blue-600,
433
- [data-theme="mint"] .bg-blue-500,
434
- [data-theme="mint"] .bg-cyan-600,
435
- [data-theme="mint"] .bg-cyan-500,
436
- [data-theme="mint"] .bg-emerald-600,
437
- [data-theme="mint"] .bg-emerald-500,
438
- [data-theme="mint"] .bg-green-600,
439
- [data-theme="mint"] .bg-green-500,
440
- [data-theme="mint"] .bg-indigo-600,
441
- [data-theme="mint"] .bg-indigo-500,
442
- [data-theme="mint"] .bg-purple-600,
443
- [data-theme="mint"] .bg-purple-500 {
444
- background-color: var(--accent) !important;
445
- color: var(--accent-fg) !important;
446
- }
447
- [data-theme="light"] .hover\:bg-blue-500:hover,
448
- [data-theme="light"] .hover\:bg-cyan-500:hover,
449
- [data-theme="light"] .hover\:bg-emerald-500:hover,
450
- [data-theme="light"] .hover\:bg-green-500:hover,
451
- [data-theme="light"] .hover\:bg-indigo-500:hover,
452
- [data-theme="light"] .hover\:bg-purple-500:hover,
453
- [data-theme="mint"] .hover\:bg-blue-500:hover,
454
- [data-theme="mint"] .hover\:bg-cyan-500:hover,
455
- [data-theme="mint"] .hover\:bg-emerald-500:hover,
456
- [data-theme="mint"] .hover\:bg-green-500:hover,
457
- [data-theme="mint"] .hover\:bg-indigo-500:hover,
458
- [data-theme="mint"] .hover\:bg-purple-500:hover {
459
- background-color: #00b894 !important;
460
- filter: brightness(1.05);
461
- }
649
+ The H2 below ("Command mesh") already had its hover halo via the R684
650
+ descendant rule; the kicker eyebrow above had R685 hover halo + R555
651
+ tracking-expand, but at REST it sat at a fixed alpha — static, no
652
+ sign of life. R699 adds a slow respiratory cycle (opacity 0.78 1
653
+ at 6 s ease-in-out infinite) on `.anet-topo-kicker-breath`.
462
654
 
463
- /* Disabled state for the same family keep contrast against the page bg
464
- so disabled buttons don't read as "dark and active" in light mode */
465
- [data-theme="light"] .disabled\:bg-gray-800:disabled,
466
- [data-theme="light"] .disabled\:bg-zinc-800:disabled,
467
- [data-theme="mint"] .disabled\:bg-gray-800:disabled,
468
- [data-theme="mint"] .disabled\:bg-zinc-800:disabled {
469
- background-color: var(--bg-elevated) !important;
470
- color: var(--fg-muted) !important;
471
- }
655
+ 6 s is deliberately ~3× slower than the sidebar brand-pulse (1.6 s)
656
+ and the recent-hot pulse (3 s) kicker isn't signaling urgency, just
657
+ "this section is alive". Group-hover overrides via R555/R685 take
658
+ precedence the moment user enters the title-block, so the breath
659
+ never competes with the gesture vocabulary.
472
660
 
473
- /* ─────────────────────────────────────────────────────────────────────
474
- v0.4.3 light-theme polish Phase 3 P0 fixes
475
- ─────────────────────────────────────────────────────────────────── */
661
+ Pure opacity: no transform / no filter blur / no size change → text
662
+ bounding box stable → topo-overlap-test grid + ring untouched. Pairs
663
+ sequentially with `anet-brand-pulse-kf` (1.6 s, brand status) and
664
+ `anet-recent-hot-pulse-kf` (3 s, recent-signal heat) as the third
665
+ distinct respiratory rhythm in the topo canvas.
476
666
 
477
- /* P0-1: Sidebar gets its own surface in light themes (not page-colored).
478
- In Cyber (dark) the sidebar already feels separate via depth; in
479
- light/mint the page bg == sidebar bg so only the right border separates
480
- them. Switch sidebar to bg-secondary (white) + a hairline shadow. */
481
- [data-theme="light"] aside[data-anet-sidebar="true"],
482
- [data-theme="mint"] aside[data-anet-sidebar="true"] {
483
- background-color: var(--bg-secondary) !important;
484
- /* Right edge: solid hairline border + a soft 16px halo so the sidebar
485
- reads as a card hovering over the page, not "part of the page". */
486
- box-shadow:
487
- 1px 0 0 var(--border),
488
- 8px 0 20px -10px rgba(15, 23, 42, 0.10),
489
- 0 0 0 1px rgba(15, 23, 42, 0.02);
490
- border-right-color: transparent !important;
491
- }
492
- /* Also tint page bg slightly cooler so the white sidebar pops more */
493
- [data-theme="light"] {
494
- --bg: #f3f5f8; /* was #f6f7f9 — bumped 3pt cooler for sidebar contrast */
495
- }
667
+ prefers-reduced-motion: reduce animation: none (kicker freezes at
668
+ resting alpha = 1, sibling to other R29-family motion guards).
669
+ ───────────────────────────────────────────────────────────────────── */
670
+ /* Round 699 + R714 kicker "Network Topology" dual-axis breath.
671
+ R699 introduced 6s opacity breath (0.78 ↔ 1, ~22%); R714 adds a
672
+ SECOND axis in the same @keyframes: a transform scale (0.995 ↔ 1,
673
+ ~0.5%). Two axes share cadence (6s) so they breathe IN PHASE —
674
+ the dimmest moment is also the smallest moment, mirroring the
675
+ R711 H2 dual-axis at the title-block's eyebrow tier.
496
676
 
497
- /* P0-2: Active nav collapse the 4-emerald stack (bg + border + text + ring)
498
- to a single accent cue: 3px left bar + medium-weight text. Dark themes
499
- keep the existing tinted-pill look. */
500
- [data-theme="light"] .anet-nav-active,
501
- [data-theme="mint"] .anet-nav-active {
502
- /* 通信龙 final spec (Q1 + 3-piece lockdown): active nav = ONE cue.
503
- 4px left bar emerald, text in --fg (NOT emerald), NO bg tint.
504
- Reads as a calm "current page" indicator without competing emerald
505
- surfaces. (Earlier 0.4.3 had 3px + faint tint + emerald text.) */
506
- background-color: transparent !important;
507
- border-color: transparent !important;
508
- border-left: 4px solid var(--accent) !important;
509
- border-radius: 0 8px 8px 0 !important;
510
- padding-left: calc(0.75rem - 4px) !important;
511
- color: var(--fg) !important;
512
- font-weight: 600;
513
- }
514
- [data-theme="light"] .anet-nav-active:hover,
515
- [data-theme="mint"] .anet-nav-active:hover {
516
- background-color: var(--bg-elevated) !important;
517
- }
518
- /* When sidebar is collapsed (w-16, justify-center) the left bar would look
519
- weird against a centered icon — switch to a left-bg accent instead. */
520
- [data-theme="light"] aside.w-16 .anet-nav-active,
521
- [data-theme="mint"] aside.w-16 .anet-nav-active {
522
- border-radius: 8px !important;
523
- padding-left: 0 !important;
524
- border-left: 0 !important;
525
- background-color: var(--bg-elevated) !important;
526
- color: var(--accent) !important;
527
- }
677
+ Why scale 0.5%: the kicker is the section's secondary text (eyebrow
678
+ above the H2 headline). Its alpha range is wider (22% vs H2's 12%)
679
+ so a slightly wider scale range (0.5% vs H2's 0.3%) keeps the
680
+ axes proportionally weighted. Still sub-pixel at xs font-size
681
+ (~12 px text height → 0.06px swing) — imperceptible per-frame,
682
+ accumulates as "the eyebrow gently pulses with the breath".
528
683
 
529
- /* Q6: native <select> theming for light/mint.
530
- Without this, dropdowns on Nodes / Messages / Logs / Settings render with
531
- the OS default chrome (heavy chevron, off-spec font weight, no border
532
- radius). We strip the native chrome with appearance-none, then paint our
533
- own chevron via a background-image SVG. ~10 LOC. */
534
- [data-theme="light"] select,
535
- [data-theme="mint"] select {
536
- -webkit-appearance: none !important;
537
- -moz-appearance: none !important;
538
- appearance: none !important;
539
- background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%23525a66' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 8l5 5 5-5'/%3E%3C/svg%3E") !important;
540
- background-repeat: no-repeat !important;
541
- background-position: right 0.6rem center !important;
542
- background-size: 1rem 1rem !important;
543
- padding-right: 2rem !important;
544
- background-color: var(--bg-secondary) !important;
545
- border-radius: 8px;
546
- }
547
- [data-theme="light"] select:focus,
548
- [data-theme="mint"] select:focus {
549
- outline: 2px solid rgba(0, 158, 126, 0.2);
550
- outline-offset: -1px;
551
- border-color: var(--accent) !important;
552
- }
553
- /* Mint reuses the same chevron — color picker would need its own gray, but
554
- accent is similar enough that the default chevron color reads OK. */
684
+ Mirrors R711 H2 pattern (HTML text dual-axis: opacity + CSS
685
+ transform scale) both title-block text tiers now have dual-axis
686
+ breath, closing title-block dual-axis symmetry across kicker and
687
+ H2. Brand-logo (the 3rd title-block trio member) still single-axis
688
+ since it already has its own 5-axis hover signature (R548/R549/R553/
689
+ R557/R604) plus 5s breath; further axes would oversubscribe the
690
+ brand mark. Pattern budget: title-block trio members get dual-axis
691
+ if their hover signature is sparse, single-axis if rich.
555
692
 
556
- /* KPI stat cards in light mode: kill the gradient wash (which reads as
557
- pale-mud on white), replace with a 2px top accent strip per color
558
- family + real elevation shadow. Dark themes keep the gradient. */
559
- [data-theme="light"] .anet-stat-card,
560
- [data-theme="mint"] .anet-stat-card {
561
- border-top-width: 2px;
562
- box-shadow: 0 1px 0 rgba(15, 23, 42, 0.04), 0 4px 12px -8px rgba(15, 23, 42, 0.08);
563
- }
564
- [data-theme="light"] .anet-stat-card:hover,
565
- [data-theme="mint"] .anet-stat-card:hover {
566
- box-shadow: 0 1px 0 rgba(15, 23, 42, 0.06), 0 12px 24px -12px rgba(15, 23, 42, 0.16);
567
- }
568
- /* Top-strip colors per stat family */
569
- [data-theme="light"] .anet-stat-card[data-anet-stat-card="green"] { border-top-color: #10b981; }
570
- [data-theme="light"] .anet-stat-card[data-anet-stat-card="cyan"] { border-top-color: var(--accent); }
571
- [data-theme="light"] .anet-stat-card[data-anet-stat-card="gray"] { border-top-color: #94a3b8; }
572
- [data-theme="light"] .anet-stat-card[data-anet-stat-card="white"] { border-top-color: #3b82f6; }
573
- [data-theme="mint"] .anet-stat-card[data-anet-stat-card="green"] { border-top-color: #16a34a; }
574
- [data-theme="mint"] .anet-stat-card[data-anet-stat-card="cyan"] { border-top-color: var(--accent); }
575
- [data-theme="mint"] .anet-stat-card[data-anet-stat-card="gray"] { border-top-color: #4ade80; }
576
- [data-theme="mint"] .anet-stat-card[data-anet-stat-card="white"] { border-top-color: #0891b2; }
577
- /* Drop the gradient wash in light themes — looks like dust on white. */
578
- [data-theme="light"] .anet-stat-card > .pointer-events-none,
579
- [data-theme="mint"] .anet-stat-card > .pointer-events-none {
580
- display: none;
581
- }
693
+ transform-origin defaults to center kicker scales around its own
694
+ midpoint, no glyph drift. paint-only flex parent stable topo-
695
+ overlap-test untouched. prefers-reduced-motion: reduce neutralizes. */
696
+ /* Round 740 — one-time canvas entrance animation. Plays on FIRST PAINT
697
+ only, then the canvas sits at scale(1) opacity(1) for the rest of
698
+ the session. Distinct from all prior animation families:
699
+ - breath family (R699+) is rest-axis infinite
700
+ - ambient family (R735+) is sweep-axis infinite
701
+ - R740 entrance is ONE-SHOT (no `infinite` keyword) — plays once
702
+ Subsequent re-mounts (route changes, etc.) replay the animation —
703
+ intended as a "the canvas just arrived" signal to the user.
582
704
 
583
- /* Generic card hover elevation every white card on light bg gets a
584
- subtle lift on hover so clickability is obvious. */
585
- [data-theme="light"] .bg-\[\#111128\]:hover,
586
- [data-theme="mint"] .bg-\[\#111128\]:hover {
587
- box-shadow: 0 6px 18px -10px rgba(15, 23, 42, 0.12);
588
- }
705
+ Animation: 0% scale(0.99) opacity(0.8) 100% scale(1) opacity(1)
706
+ over 600ms with ease-out timing. Subtle: the scale change is only
707
+ 1% (0.99 → 1.00); opacity ramps from 0.8 to 1.0. Reads as "settling
708
+ in" — the canvas zooms in slightly while fading in.
589
709
 
590
- /* Task row hover bg (round 19) dark hex would land on a white card and
591
- look like a stripe. Re-map to a faint accent tint per theme. */
592
- [data-theme="light"] .anet-task-row:hover {
593
- background-color: #f5f7fb !important;
594
- }
595
- [data-theme="mint"] .anet-task-row:hover {
596
- background-color: #ecf6f1 !important;
597
- }
598
- [data-theme="sunset"] .anet-task-row:hover {
599
- background-color: #fff4ec !important;
600
- }
601
- /* Open state — slightly stronger fill so users can see "this one is the
602
- open row" while scrolling. */
603
- [data-theme="light"] .anet-task-row[aria-expanded="true"] {
604
- background-color: #fafbfd !important;
605
- box-shadow: 0 4px 14px -10px rgba(15, 23, 42, 0.14) !important;
710
+ transform-origin defaults to 50% 50% (canvas center), so the scale
711
+ ripple comes from the middle outward.
712
+
713
+ prefers-reduced-motion guard: when reduced motion is preferred, the
714
+ class isn't applied (JSX-level gate in TopoGraph.tsx), so the CSS
715
+ animation never runs. Belt-and-suspenders @media block below
716
+ neutralises if the class somehow ends up applied. */
717
+ @keyframes anet-topo-canvas-entrance-kf {
718
+ 0% { transform: scale(0.99); opacity: 0.8; }
719
+ 100% { transform: scale(1); opacity: 1; }
606
720
  }
607
- [data-theme="mint"] .anet-task-row[aria-expanded="true"] {
608
- background-color: #f3faf6 !important;
721
+ .anet-topo-canvas-entrance {
722
+ animation: anet-topo-canvas-entrance-kf 600ms ease-out;
723
+ transform-origin: 50% 50%;
609
724
  }
610
- [data-theme="sunset"] .anet-task-row[aria-expanded="true"] {
611
- background-color: #fff8f1 !important;
725
+ @media (prefers-reduced-motion: reduce) {
726
+ .anet-topo-canvas-entrance { animation: none; }
612
727
  }
613
728
 
614
- /* ─────────────────────────────────────────────────────────────────────
615
- Login surface (round 1 polish)
616
- Restrained, minimal first impression for new Agent Network users.
617
- ─────────────────────────────────────────────────────────────────── */
618
- .anet-login-bg {
619
- background:
620
- radial-gradient(circle at 50% 30%, rgba(34, 211, 238, 0.05), transparent 55%),
621
- radial-gradient(circle at 33% 75%, rgba(59, 130, 246, 0.045), transparent 55%);
729
+ /* Round 728 — recent + legend panel titles join the triple-axis tier
730
+ at 8 s, forming the "8 s triple-axis PAIR" (mirror to R724's 6 s pair).
731
+ Both panel titles already share R700/R701 SMIL opacity (8 s) +
732
+ R713 SMIL font-size (8 s) — R728 adds a 3rd CSS-animated axis:
733
+ text-shadow glow at the same 8 s cadence in phase.
734
+
735
+ Same hybrid SMIL + CSS pattern as R722 watermark: SMIL drives SVG
736
+ attributes (opacity, font-size); CSS drives the text-shadow CSS
737
+ property on the same <text> element. Modern browsers honour both
738
+ vocabularies side-by-side without conflict.
739
+
740
+ Multi-cadence triple-axis tier post-R728 (6 members across 4 cadences):
741
+ 6 s kicker + watermark (R721/R722, "6 s pair")
742
+ 8 s recent + legend titles (R728, "8 s pair") ← this round
743
+ 9 s zoom-level readout (R727)
744
+ 10 s H2 section title (R725)
745
+
746
+ Pattern structural mirror: R724's 6 s pair (title-block + canvas-
747
+ brand tiers) finds its sibling in R728's 8 s pair (panel-title tier).
748
+ Both pairs use parity-shape (members at same cadence). Adds new
749
+ R717 pattern entry `triple-axis-pair-8s` (shape `8s-triple-pair`)
750
+ to formalize the structural mirror.
751
+
752
+ Glow tuning vs prior 4 triple-axis members:
753
+ R721 kicker blur 6 px, alpha 0.30 @ 6 s (title-block, sharp)
754
+ R722 watermark blur 8 px, alpha 0.20 @ 6 s (canvas-brand, quiet)
755
+ R725 H2 blur 8 px, alpha 0.22 @ 10 s (title-block, mellow)
756
+ R727 zoom-level blur 5 px, alpha 0.25 @ 9 s (data tier, sharp pill)
757
+ R728 panel-pair blur 7 px, alpha 0.23 @ 8 s (panel-title) ← this round
758
+ Mid-range blur (7 px) — between zoom-level (5 px pill) and H2 (8 px
759
+ heading); alpha 0.23 — between H2 (0.22 mellow) and zoom-level (0.25)
760
+ to position the panel-title glow as a "calm-medium" register that
761
+ matches the panel titles' weight in the visual hierarchy (heading-
762
+ weight but smaller fontSize than H2).
763
+
764
+ Gating: piggybacks on the existing JSX gates (R700: !reducedMotion &&
765
+ !activeEdgeKey; R701: !reducedMotion && !pinnedStatus). The CSS class
766
+ is applied conditionally; when ungated, both SMIL animates and CSS
767
+ animation coexist. prefers-reduced-motion @media guard provides
768
+ belt-and-suspenders. */
769
+ @keyframes anet-topo-panel-title-glow-breath-kf {
770
+ 0%, 100% { text-shadow: none; }
771
+ 50% { text-shadow: 0 0 7px rgba(34, 211, 238, 0.23); }
622
772
  }
623
- [data-theme="light"] .anet-login-bg,
624
- [data-theme="mint"] .anet-login-bg {
625
- background-image:
626
- radial-gradient(circle at 50% 35%, rgba(0, 158, 126, 0.06), transparent 60%),
627
- radial-gradient(circle, rgba(15, 23, 42, 0.05) 1px, transparent 1px);
628
- background-size: 100% 100%, 24px 24px;
773
+ .anet-topo-panel-title-glow-breath {
774
+ animation: anet-topo-panel-title-glow-breath-kf 8s ease-in-out infinite;
629
775
  }
630
- [data-theme="light"] .anet-login-mark,
631
- [data-theme="mint"] .anet-login-mark {
632
- background: var(--bg-secondary) !important;
633
- box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06), 0 0 0 1px var(--border);
776
+ @media (prefers-reduced-motion: reduce) {
777
+ .anet-topo-panel-title-glow-breath { animation: none; }
634
778
  }
635
779
 
636
- /* Health bannersticky thin strip above content, with theme-aware
637
- green/amber/red tints. Dark themes use the lower-opacity bg+border
638
- that come from the component classes directly. Light/mint need
639
- slightly more saturation so the tint reads on a white surface. */
640
- [data-theme="light"] .anet-health-banner.bg-emerald-500\/6,
641
- [data-theme="mint"] .anet-health-banner.bg-emerald-500\/6 {
642
- background-color: rgba(16, 185, 129, 0.08) !important;
643
- color: #047857 !important;
644
- border-color: rgba(16, 185, 129, 0.25) !important;
645
- }
646
- [data-theme="light"] .anet-health-banner.bg-amber-500\/8,
647
- [data-theme="mint"] .anet-health-banner.bg-amber-500\/8 {
648
- background-color: rgba(245, 158, 11, 0.12) !important;
649
- color: #92400e !important;
650
- border-color: rgba(245, 158, 11, 0.30) !important;
651
- }
652
- [data-theme="light"] .anet-health-banner.bg-red-500\/8,
653
- [data-theme="mint"] .anet-health-banner.bg-red-500\/8 {
654
- background-color: rgba(220, 38, 38, 0.10) !important;
655
- color: #b91c1c !important;
656
- border-color: rgba(220, 38, 38, 0.30) !important;
657
- }
780
+ /* Round 722watermark text becomes the 2ND TRIPLE-axis surface,
781
+ completing a "6 s triple-axis PAIR" with R721 kicker (both at the
782
+ fastest dual-axis cadence, both with sparse hover signatures, both
783
+ read-target text symmetric structural mirror).
658
784
 
659
- /* Sidebar brand "online" pulse — slow, restrained, NO glow.
660
- 1.6s opacity drift between 1.0 and 0.5; nothing scales, nothing blurs. */
661
- @keyframes anet-brand-pulse-kf {
662
- 0%, 100% { opacity: 1; }
663
- 50% { opacity: 0.4; }
664
- }
665
- .anet-brand-pulse {
666
- animation: anet-brand-pulse-kf 1.6s ease-in-out infinite;
667
- }
668
- @media (prefers-reduced-motion: reduce) {
669
- .anet-brand-pulse { animation: none; }
670
- }
785
+ The watermark's prior axes:
786
+ R519 SMIL opacity (6 s, 0.32 0.48)
787
+ R712 SMIL letter-spacing (6 s, 0.45 ↔ 0.55)
788
+ R722 adds a 3rd CSS-animated axis on the SAME `<text>` element:
789
+ R722 CSS text-shadow (6 s, none ↔ 0 0 8 px rgba(34, 211, 238, 0.20))
671
790
 
672
- /* Skeleton pulse same 1.6s rhythm as the brand pulse but applied to a
673
- container so all bars inside breathe together. Drifts between full
674
- token value and 60% so light themes show a calm grey-to-bg-elevated
675
- fade and dark themes show a calm navy-to-elevated fade. */
676
- @keyframes anet-skeleton-kf {
677
- 0%, 100% { opacity: 1; }
678
- 50% { opacity: 0.55; }
791
+ The SMIL animations (opacity + letter-spacing) drive SVG attributes
792
+ in their own clock; the CSS animation drives `text-shadow` as a CSS
793
+ property. SVG <text> in modern browsers honours both vocabularies
794
+ on the same element no conflict. The 6 s cadence shared across
795
+ all 3 axes keeps them in phase: at the dimmest+tightest moment the
796
+ glow appears; at the brightest+widest moment the glow is gone. One
797
+ coherent inhale-exhale.
798
+
799
+ Glow tuning vs kicker (R721):
800
+ kicker blur 6 px, alpha 0.30
801
+ watermark blur 8 px, alpha 0.20
802
+ The watermark text sits at canvas bottom-left at fontSize 11 with
803
+ a base opacity of 0.4 (R282) — the glow needs more spread (8 vs 6)
804
+ to register at that low base opacity, but less alpha (0.20 vs 0.30)
805
+ to stay proportional to the wordmark's quieter visual register.
806
+
807
+ Gating: piggybacks on the existing R519 reducedMotion JSX gate
808
+ (the entire <animate> tree only mounts when !reducedMotion). When
809
+ reducedMotion is true the class is still applied but the @media
810
+ (prefers-reduced-motion: reduce) below neutralises the CSS
811
+ animation. Belt-and-suspenders. */
812
+ @keyframes anet-topo-brand-watermark-glow-breath-kf {
813
+ 0%, 100% { text-shadow: none; }
814
+ 50% { text-shadow: 0 0 8px rgba(34, 211, 238, 0.20); }
679
815
  }
680
- .anet-skeleton-pulse {
681
- animation: anet-skeleton-kf 1.6s ease-in-out infinite;
816
+ .anet-topo-brand-watermark-glow-breath {
817
+ animation: anet-topo-brand-watermark-glow-breath-kf 6s ease-in-out infinite;
682
818
  }
683
819
  @media (prefers-reduced-motion: reduce) {
684
- .anet-skeleton-pulse { animation: none; }
820
+ .anet-topo-brand-watermark-glow-breath { animation: none; }
685
821
  }
686
822
 
687
- /* Skeleton bar fill different per theme so it's visible against the
688
- card bg without being loud. Dark: lighter navy block on dark card.
689
- Light: mid-grey on white card (not too light or it disappears). */
690
- .anet-skeleton-bar {
691
- background-color: #1a1a2a;
692
- }
693
- [data-theme="light"] .anet-skeleton-bar,
694
- [data-theme="mint"] .anet-skeleton-bar {
695
- background-color: #d4d8df;
696
- }
823
+ /* Round 721kicker becomes the family's first TRIPLE-axis breath
824
+ surface. R699 opacity (6 s) + R714 transform-scale (6 s) joined by
825
+ R721 text-shadow (6 s) all 3 axes share the 6 s cadence in phase.
697
826
 
698
- /* Command palette modal surface bumped depth so it sits clearly above
699
- the page content in both themes. Light/mint: white shell with a real
700
- shadow (not the dark navy that the CSS shim would normally translate
701
- `bg-[#0d0d1a]` into). */
702
- .anet-cmdk kbd {
703
- font-size: 9px;
704
- line-height: 1;
705
- padding: 1px 4px;
706
- border: 1px solid currentColor;
707
- border-radius: 3px;
708
- opacity: 0.6;
709
- }
710
- [data-theme="light"] .anet-cmdk > div:not([aria-hidden]),
711
- [data-theme="mint"] .anet-cmdk > div:not([aria-hidden]) {
712
- background-color: var(--bg-secondary) !important;
713
- box-shadow: 0 10px 30px -8px rgba(15, 23, 42, 0.18),
714
- 0 2px 6px -2px rgba(15, 23, 42, 0.10);
715
- }
716
- [data-theme="light"] .anet-cmdk-row.bg-cyan-500\/10,
717
- [data-theme="mint"] .anet-cmdk-row.bg-cyan-500\/10 {
718
- background-color: rgba(0, 158, 126, 0.08) !important;
719
- }
827
+ At rest peak (0%, 100%) the kicker reads as a static title;
828
+ at mid-breath (50%) it dims (0.78), retracts (scale 0.995), AND
829
+ emits a soft cyan glow (text-shadow 0 0 6px rgba(34, 211, 238, 0.30)).
830
+ The glow appears OUT-OF-PHASE with the dim — as the text shrinks
831
+ slightly it gains a subtle aura. Reads as an "inhale glow": a small
832
+ visible payoff at the bottom of the breath cycle.
720
833
 
721
- /* Round 20 matched-char highlight inside cmdk titles/hints. Inherits
722
- line color (`color`) instead of forcing one keeps text readable on
723
- both inactive (gray) and active (cyan-300) rows. <mark> default is a
724
- garish yellow block, which we override. */
725
- .anet-cmdk-mark {
726
- background: transparent;
727
- color: inherit;
728
- font-weight: 600;
729
- /* Subtle underline so the mark stays visible even on the active row
730
- where the background tint matches the accent color. */
731
- text-decoration: underline;
732
- text-decoration-color: currentColor;
733
- text-decoration-thickness: 1px;
734
- text-underline-offset: 2px;
735
- text-decoration-skip-ink: none;
736
- }
737
- /* Cyber: accent the matched glyph color so it pops on the dark bg. */
738
- [data-theme="cyber"] .anet-cmdk-mark,
739
- :root:not([data-theme]) .anet-cmdk-mark {
740
- color: #22d3ee;
741
- }
742
- /* Light/mint: use the brand teal — undertone matches the active row. */
743
- [data-theme="light"] .anet-cmdk-mark,
744
- [data-theme="mint"] .anet-cmdk-mark {
745
- color: #0d9488;
746
- }
747
- [data-theme="sunset"] .anet-cmdk-mark {
748
- color: #ea580c;
749
- }
750
- /* On the active (cyan-tinted) row, the title is already cyan-300 — use
751
- white-with-underline so the highlight reads as emphasis, not redundancy. */
752
- .anet-cmdk-row.bg-cyan-500\/10 .anet-cmdk-mark {
753
- color: #f0fdff;
754
- }
755
- [data-theme="light"] .anet-cmdk-row.bg-cyan-500\/10 .anet-cmdk-mark,
756
- [data-theme="mint"] .anet-cmdk-row.bg-cyan-500\/10 .anet-cmdk-mark {
757
- color: #064e3b;
758
- }
834
+ Why kicker first (vs other dual-axis surfaces):
835
+ - kicker has the SPARSEST hover signature in the family being
836
+ below H2 (the page heading), it's read-target text, not an
837
+ interactive surface. The breath family's "if hover sig sparse,
838
+ dual-axis; if hover sig rich, single-axis" budget rule extends
839
+ naturally to "if hover sig SPARSE, can go triple-axis".
840
+ - 6 s is the fastest dual-axis cadence in the rolodex — at fast
841
+ cadence the 3rd axis (a glow appear+disappear cycle every 3 s
842
+ on each half) registers as ambient rhythm, not a distraction.
759
843
 
760
- /* Help overlay — same white-card-with-shadow treatment as cmdk on light */
761
- [data-theme="light"] .anet-help > div:not([aria-hidden]),
762
- [data-theme="mint"] .anet-help > div:not([aria-hidden]) {
763
- background-color: var(--bg-secondary) !important;
764
- box-shadow: 0 10px 30px -8px rgba(15, 23, 42, 0.18),
765
- 0 2px 6px -2px rgba(15, 23, 42, 0.10);
766
- }
844
+ text-shadow chosen over letter-spacing or font-size because:
845
+ - text-shadow doesn't reflow text (no horizontal motion)
846
+ - glow against the dark canvas reads clearly without competing
847
+ with the opacity dim
848
+ - cyan tint at 30% alpha + 6 px blur is below the brightness
849
+ threshold of any node halo (R674/R675 nodes use 0.5+ alpha)
767
850
 
768
- /* Tasks tab strip horizontal scroll fade — gradient overlays at left/right
769
- edges hint that there's more content when the row overflows. Only
770
- applied on <sm (where the strip actually scrolls). The wrapper is
771
- `position: relative` and the fades are absolute siblings. */
772
- .anet-tabstrip-wrap {
773
- position: relative;
774
- }
775
- .anet-tabstrip-wrap::before,
776
- .anet-tabstrip-wrap::after {
777
- content: '';
778
- position: absolute;
779
- top: 4px;
780
- bottom: 4px;
781
- width: 20px;
782
- pointer-events: none;
783
- z-index: 1;
784
- }
785
- .anet-tabstrip-wrap::before {
786
- left: 0;
787
- background: linear-gradient(to right, #111128, transparent);
788
- border-radius: 8px 0 0 8px;
789
- }
790
- .anet-tabstrip-wrap::after {
791
- right: 0;
792
- background: linear-gradient(to left, #111128, transparent);
793
- border-radius: 0 8px 8px 0;
794
- }
795
- @media (min-width: 640px) {
796
- /* sm: stops scrolling, hide the fades */
797
- .anet-tabstrip-wrap::before,
798
- .anet-tabstrip-wrap::after { display: none; }
851
+ Composition note: the R29 prefers-reduced-motion guard already
852
+ neutralizes the entire 6 s animation; no separate text-shadow
853
+ reduced-motion logic needed. */
854
+ @keyframes anet-topo-kicker-breath-kf {
855
+ 0%, 100% { opacity: 1; transform: scale(1); text-shadow: none; }
856
+ 50% { opacity: 0.78; transform: scale(0.995); text-shadow: 0 0 6px rgba(34, 211, 238, 0.30); }
799
857
  }
800
- [data-theme="light"] .anet-tabstrip-wrap::before,
801
- [data-theme="mint"] .anet-tabstrip-wrap::before {
802
- background: linear-gradient(to right, var(--bg-secondary), transparent);
858
+ .anet-topo-kicker-breath {
859
+ animation: anet-topo-kicker-breath-kf 6s ease-in-out infinite;
803
860
  }
804
- [data-theme="light"] .anet-tabstrip-wrap::after,
805
- [data-theme="mint"] .anet-tabstrip-wrap::after {
806
- background: linear-gradient(to left, var(--bg-secondary), transparent);
861
+ @media (prefers-reduced-motion: reduce) {
862
+ .anet-topo-kicker-breath { animation: none; }
807
863
  }
808
864
 
809
- /* P0-3 baseline: error / warning chip colors for light themes.
810
- The component-level copy (replacing raw JSON with human text) is fixed
811
- in the Logs / Server-Logs page components separately. */
812
- [data-theme="light"] .anet-error-chip,
813
- [data-theme="mint"] .anet-error-chip {
814
- background-color: rgba(220, 38, 38, 0.06) !important;
815
- color: #b91c1c !important;
816
- border-color: rgba(220, 38, 38, 0.25) !important;
817
- }
818
- [data-theme="light"] .anet-warn-chip,
819
- [data-theme="mint"] .anet-warn-chip {
820
- background-color: rgba(217, 119, 6, 0.08) !important;
821
- color: #92400e !important;
822
- border-color: rgba(217, 119, 6, 0.28) !important;
823
- }
865
+ /* Round 702 H2 "Command mesh" at-rest breathing fade, completing the
866
+ title-block respiratory trio (brand-logo 5 s / kicker 6 s / H2 10 s).
867
+ Tighter alpha range (0.88 1, ~12%) vs the kicker (0.78 ↔ 1, ~22%):
868
+ H2 carries more visual weight, so a gentler breath keeps it from
869
+ looking flickery. Slowest in the trio — 10 s reads as calm anchoring,
870
+ not anxious. prefers-reduced-motion: reduce animation: none,
871
+ sibling to the R699 kicker guard. */
872
+ /* Round 702 + R711 — H2 "Command mesh" dual-axis breath. R702 introduced
873
+ the 10s opacity breath (0.88 ↔ 1, ~12%); R711 adds a SECOND axis in
874
+ the same @keyframes: a transform scale (0.997 ↔ 1, ~0.3%). The two
875
+ axes share cadence (10s) so they breathe in PHASE — the dimmest moment
876
+ is also the smallest moment, like a single coherent inhale-exhale.
824
877
 
825
- /* Round 30 global drawer entrance keyframes so TaskDrawer, TaskChatPanel,
826
- and any future right-side panel share one source. Previously the
827
- keyframe lived inside TaskChatPanel's <style jsx global>; if that
828
- component never mounted, the keyframe was missing for TaskDrawer. */
829
- @keyframes anet-slide-in {
830
- from { transform: translateX(100%); }
831
- to { transform: translateX(0); }
832
- }
833
- .animate-slide-in { animation: anet-slide-in 0.2s ease-out; }
878
+ Why scale 0.3%: the H2 is the section's primary text identity; scale
879
+ changes affect glyph rasterization at sub-pixel level. 0.3% range
880
+ keeps the visible change at fraction of a pixel for a 40-px-tall text,
881
+ imperceptible per-frame but cumulatively reads as "the title gently
882
+ pulses with the breath", reinforcing the opacity dimming without
883
+ adding new gesture. Scale doesn't affect layout flow (CSS transform
884
+ is paint-only) — flex parent stays stable, overlap test safe.
834
885
 
835
- @keyframes anet-fade-in {
836
- from { opacity: 0; }
837
- to { opacity: 1; }
838
- }
839
- .anet-fade-in { animation: anet-fade-in 0.15s ease-out; }
886
+ First DUAL-AXIS breath anchor in the 呼吸感 family — establishes that
887
+ future anchors can layer multiple animation properties on a single
888
+ keyframe block for "deeper aliveness" on a single surface, without
889
+ needing a second class or animation. Pattern budget: prefer single-
890
+ axis on most anchors (visual hierarchy) and reserve dual-axis for
891
+ primary identity surfaces (H2 here = first primary-identity dual). */
892
+ /* Round 725 — H2 section title becomes the 3RD triple-axis surface,
893
+ FIRST triple-axis member at a NON-6 s cadence (10 s). Establishes
894
+ that the triple-axis TIER is multi-cadence rather than 6 s-locked —
895
+ the R724 `triple-axis-pair` pattern stays valid for kicker+watermark
896
+ at 6 s; H2 joins the triple-axis TIER as a solo 10 s member.
840
897
 
841
- /* Round 184 chrome reset button click-spin. The reset button's
842
- onClick triggers R168 smoothView crossfade on the canvas, but the
843
- BUTTON itself had no visual feedback that the click registered. A
844
- single 360° rotation on the refresh-arrow icon (450ms ease-out) is
845
- a quick "yes, I heard you" confirmation. Counter-clockwise (-360°)
846
- reads as "rewind" / "undo" — natural for a reset gesture. Mirror
847
- to R52's hub click ripple at the chrome scope. */
848
- @keyframes anet-reset-spin {
849
- from { transform: rotate(0deg); }
850
- to { transform: rotate(-360deg); }
898
+ H2's prior dual-axis (R702 opacity + R711 transform-scale @ 10 s)
899
+ gains a 3rd CSS-animated axis: text-shadow glow at 10 s in phase.
900
+ Reads as a slow inhale glow on the section heading — calmer than
901
+ the 6 s pair (kicker/watermark) because H2's slower cadence keeps
902
+ the glow appearance interval at 5 s (vs 3 s for the 6 s tier).
903
+
904
+ Glow tuning vs kicker (R721) and watermark (R722):
905
+ kicker blur 6 px, alpha 0.30 @ 6 s
906
+ watermark blur 8 px, alpha 0.20 @ 6 s
907
+ H2 blur 8 px, alpha 0.22 @ 10 s ← this round
908
+ H2 sits between the two at alpha; its larger fontSize (section
909
+ heading, much bigger than kicker/watermark) needs a wider blur
910
+ (8 px to match watermark) but a slightly higher alpha than
911
+ watermark (0.22 vs 0.20) because at H2's slower 10 s cadence the
912
+ glow gets a longer dwell time at peak — proportionally tighter
913
+ alpha would feel under-emphasised vs the 6 s pair's snappier peaks.
914
+
915
+ The triple-axis tier now has 3 members across 2 cadences:
916
+ 6 s kicker + watermark (R724 "triple-axis-pair" pattern)
917
+ 10 s H2 (solo)
918
+ R725 doesn't add a new R717 pattern entry — H2 is a solo, not
919
+ a pair. R723 catalog (triple-axis-surfaces) extends to 3 entries.
920
+
921
+ prefers-reduced-motion guard already in place on the existing
922
+ R702 H2 breath class; this round's text-shadow axis joins the
923
+ same animation rule and is neutralised by the same @media block. */
924
+ @keyframes anet-topo-section-title-breath-kf {
925
+ 0%, 100% { opacity: 1; transform: scale(1); text-shadow: none; }
926
+ 50% { opacity: 0.88; transform: scale(0.997); text-shadow: 0 0 8px rgba(34, 211, 238, 0.22); }
851
927
  }
852
- .anet-reset-spin {
853
- animation: anet-reset-spin 0.45s cubic-bezier(0.4, 0, 0.2, 1);
854
- transform-origin: center;
855
- transform-box: fill-box;
928
+ .anet-topo-section-title-breath {
929
+ animation: anet-topo-section-title-breath-kf 10s ease-in-out infinite;
930
+ /* transform-origin defaults to 50% 50% which is what we want — the H2
931
+ scales around its own center, no glyph drift. */
932
+ }
933
+ @media (prefers-reduced-motion: reduce) {
934
+ .anet-topo-section-title-breath { animation: none; }
856
935
  }
857
936
 
858
- /* Round 186 — chrome zoom-in / zoom-out buttons get a brief icon pop
859
- on click. Same click-feel idiom R184 added for the reset button
860
- the canvas already smoothes via R168/R169 smoothView, but the
861
- chrome BUTTON itself needs its own confirmation. A quick scale
862
- up-then-back (1 → 1.18 1 over 220ms) reads as "tapped". Same
863
- transform-box: fill-box anchoring as R184 keeps the icon
864
- centered in place. */
865
- @keyframes anet-chrome-pop {
866
- 0% { transform: scale(1); }
867
- 50% { transform: scale(1.18); }
868
- 100% { transform: scale(1); }
937
+ /* Round 703 — chrome zoom-level readout at-rest breath. 9 s cadence sits
938
+ between the 8 s panel titles and the 10 s H2 in the respiratory
939
+ rolodex. Tighter alpha range (0.85 1, ~15%) than the kicker —
940
+ readout carries tabular numeric data, so it shouldn't drift too far
941
+ from full opacity. Hover gate via the data attribute: when
942
+ data-topo-chrome-zoom-level-hover="true", animation: none, and the
943
+ existing R347 ls + R420 fw + R517 color + R593 brightness + R668
944
+ halo 4-axis lift takes precedence cleanly. prefers-reduced-motion:
945
+ reduce neutralizes, sibling to R699/R702 guards. */
946
+ /* Round 703 + R715 — chrome zoom-level readout dual-axis breath.
947
+ R703 introduced 9s opacity breath (0.85 ↔ 1, ~15%); R715 adds a
948
+ SECOND axis in the same @keyframes: a transform scale (0.996 ↔ 1,
949
+ ~0.4%). Two axes share cadence (9s) so they breathe IN PHASE —
950
+ dimmest = smallest, mirroring R711 H2 / R714 kicker pattern at the
951
+ CHROME DATA TIER (vs title-block primary text tier).
952
+
953
+ Why scale 0.4% on zoom-level (vs H2's 0.3%, kicker's 0.5%): zoom-
954
+ level's alpha range is between H2 (12%) and kicker (22%) at 15%
955
+ — scale axis interpolated to 0.4% keeps the three dual-axis HTML
956
+ text surfaces co-proportioned. Sub-pixel at xs font-size → ~0.04px
957
+ swing at ~10px text height; imperceptible per-frame, accumulates
958
+ as "the readout pulses with the breath".
959
+
960
+ Pattern budget extension — chrome data tier dual-axis:
961
+ R699+R714 kicker 6s eyebrow tier
962
+ R703+R715 zoom-level 9s chrome data tier ← this round
963
+ R702+R711 H2 10s primary headline tier
964
+ The 6/9/10 s cadences and the kicker/zoom-level/H2 scale ratios
965
+ (0.5/0.4/0.3 %) form a tier-graded ladder: faster cadence + larger
966
+ scale on secondary-tier text; slower cadence + smaller scale on
967
+ primary-tier text. Each tier has its own "tempo + amplitude"
968
+ coupled signature — kicker pulses quickly-and-noticeably, H2
969
+ pulses slowly-and-subtly, zoom-level sits between.
970
+
971
+ Hover gate via `[data-topo-chrome-zoom-level-hover="true"]` →
972
+ animation: none, so R347/R420/R517/R593 hover axes take precedence
973
+ without animation underneath. transform-origin defaults to center
974
+ — pill scales around its own midpoint. prefers-reduced-motion:
975
+ reduce also neutralizes. */
976
+ /* Round 727 — zoom-level readout becomes the 4TH triple-axis surface and
977
+ FIRST chrome-tier (data-tier) member to join the multi-cadence triple-
978
+ axis tier. R703 opacity (9 s) + R715 transform-scale (9 s) joined by
979
+ R727 text-shadow (9 s) — all 3 axes share the 9 s cadence in phase.
980
+
981
+ Multi-cadence triple-axis tier post-R727 (4 members across 3 cadences):
982
+ 6 s kicker + watermark (title-block + canvas-brand tiers)
983
+ 9 s zoom-level readout (data tier) ← this round
984
+ 10 s H2 section title (title-block tier)
985
+ Pattern budget extension: prior triple-axis members were all read-target
986
+ text in primary-identity tiers (title-block + canvas-brand). R727 extends
987
+ to the DATA tier — a chrome readout, not pure text. Justified because:
988
+ - zoom-level's hover sig (R703 data-attr gate) is sparse: only the
989
+ hover-tracking attr; no rich tactile axes
990
+ - the surface is a single-tone monospace digit pill — visually quiet
991
+ enough that a glow pulse remains subordinate to node halos
992
+ - the chrome strip's "data tier" sub-role becomes a structural mirror
993
+ of the title-block tier in the triple-axis context
994
+
995
+ Glow tuning vs prior 3 triple-axis members:
996
+ R721 kicker blur 6 px, alpha 0.30 @ 6 s (title-block)
997
+ R722 watermark blur 8 px, alpha 0.20 @ 6 s (canvas-brand)
998
+ R725 H2 blur 8 px, alpha 0.22 @ 10 s (title-block)
999
+ R727 zoom-level blur 5 px, alpha 0.25 @ 9 s (data tier) ← this round
1000
+ Smaller blur (5 px) for the smaller pill surface; mid-range alpha
1001
+ (0.25) between kicker (sharp+bright at 0.30) and H2 (medium at 0.22).
1002
+ At 9 s the glow appearance interval is ~4.5 s on each half — between
1003
+ the 3 s 6 s-pair peak and the 5 s H2 peak, matching the cadence
1004
+ interpolation.
1005
+
1006
+ Hover gate preserved: the existing R703 [data-topo-chrome-zoom-level-
1007
+ hover="true"] selector still gates the entire animation off when user
1008
+ interacts — R727's text-shadow is part of the same @keyframes block
1009
+ so it gets gated together. */
1010
+ @keyframes anet-topo-chrome-zoom-level-breath-kf {
1011
+ 0%, 100% { opacity: 1; transform: scale(1); text-shadow: none; }
1012
+ 50% { opacity: 0.85; transform: scale(0.996); text-shadow: 0 0 5px rgba(34, 211, 238, 0.25); }
869
1013
  }
870
- .anet-chrome-pop {
871
- animation: anet-chrome-pop 0.22s ease-out;
872
- transform-origin: center;
873
- transform-box: fill-box;
1014
+ .anet-topo-chrome-zoom-level-breath {
1015
+ animation: anet-topo-chrome-zoom-level-breath-kf 9s ease-in-out infinite;
1016
+ }
1017
+ .anet-topo-chrome-zoom-level-breath[data-topo-chrome-zoom-level-hover="true"] {
1018
+ animation: none;
1019
+ }
1020
+ @media (prefers-reduced-motion: reduce) {
1021
+ .anet-topo-chrome-zoom-level-breath { animation: none; }
874
1022
  }
875
1023
 
876
- /* Round 498recent-signal row hot-count subtle pulse (信息密度 +
877
- 呼吸感 themes). When a row's edge count crosses the hot threshold
878
- (≥ 10), the existing R127/R320/R445 amber-fill + fw-700 typography
879
- already calls attention; R498 adds a slow 3s opacity breath (0.85 ↔
880
- 1.0) on the digit so the hot tspans gently pulse — at-a-glance scan
881
- of the panel reads "high-traffic lane right here" with motion in
882
- addition to color+weight. 3s cycle is deliberate: faster (1-2s)
883
- would compete with the working-status halo cadence; slower (5s+)
884
- would lose the "alive" signal. Amplitude 15% (matches R497 hub-
885
- highlight breath idiom).
886
- prefers-reduced-motion handled by the R29 blanket override
887
- (animation-duration: 0.001ms !important) no per-class guard
888
- needed. The component-side gate (`!reducedMotion && isHot`)
889
- ensures the className is only applied when both conditions hold,
890
- so even without the blanket the no-motion preference is respected. */
891
- @keyframes anet-recent-hot-pulse-kf {
892
- 0%, 100% { opacity: 0.85; }
893
- 50% { opacity: 1; }
1024
+ /* Round 704brand watermark WRAPPER envelope breath at 15 s — slowest
1025
+ tier in the respiratory rolodex. Inner "sleep2agi" text already
1026
+ breathes at 6 s (R519); this adds an outer wrapper envelope so the
1027
+ brand area has nested respiration (fast inner / slow outer). 0.85 ↔ 1
1028
+ alpha range (~15%) matches the R703 tabular/decorative surface band
1029
+ heavy visual register shouldn't drift too far from full opacity.
1030
+
1031
+ Focal-recede gate via data-attr selector when the wrapper's
1032
+ data-topo-brand-watermark-recede attr flips to "true" (user is
1033
+ hovering a node/edge/group on the canvas), animation: none gates
1034
+ off so the R525 inline opacity={0.7} attr takes effect cleanly.
1035
+ Pattern sibling to R703 zoom-level data-attr selector gate.
1036
+ prefers-reduced-motion: reduce also neutralizes. */
1037
+ @keyframes anet-topo-brand-watermark-envelope-breath-kf {
1038
+ 0%, 100% { opacity: 1; }
1039
+ 50% { opacity: 0.85; }
894
1040
  }
895
- .anet-recent-hot-pulse {
896
- animation: anet-recent-hot-pulse-kf 3s ease-in-out infinite;
1041
+ .anet-topo-brand-watermark-envelope-breath {
1042
+ animation: anet-topo-brand-watermark-envelope-breath-kf 15s ease-in-out infinite;
1043
+ }
1044
+ .anet-topo-brand-watermark-envelope-breath[data-topo-brand-watermark-recede="true"] {
1045
+ animation: none;
1046
+ }
1047
+ @media (prefers-reduced-motion: reduce) {
1048
+ .anet-topo-brand-watermark-envelope-breath { animation: none; }
897
1049
  }
898
1050
 
899
- /* Round 36current-step ring pulse on TaskDrawer timeline. Halo gently
900
- breathes around the active step's dot so users can spot "task is here". */
901
- @keyframes anet-current-step-pulse-kf {
902
- 0%, 100% { transform: scale(1); opacity: 0.6; }
903
- 50% { transform: scale(1.25); opacity: 0.0; }
1051
+ /* Round 705canvas crescent wrapper envelope breath at 13 s. Mirror
1052
+ to R704 watermark wrapper, closing canvas-brand-pair envelope
1053
+ symmetry. Cadence 13 s coprime with R704 (15 s) so the two never
1054
+ beat together when both visible. Tighter alpha range (0.30 0.35,
1055
+ ~14%) deliberately hugs the existing inline 0.35 baseline — preserves
1056
+ the visible-state intent while adding nested respiration.
1057
+
1058
+ Two gate-off rules instead of one (R704 had a single recede gate):
1059
+ - visible="false" → animation: none, so inline opacity={0} holds when
1060
+ the recent panel is shown (crescent must be invisible).
1061
+ - recede="true" → animation: none, so inline opacity={0.245} holds
1062
+ when canvas attention is elsewhere.
1063
+ Either gate flips → CSS releases control to inline opacity attr.
1064
+ prefers-reduced-motion: reduce also neutralizes. */
1065
+ @keyframes anet-topo-brand-canvas-mark-envelope-breath-kf {
1066
+ 0%, 100% { opacity: 0.35; }
1067
+ 50% { opacity: 0.30; }
904
1068
  }
905
- .anet-current-step-pulse {
906
- animation: anet-current-step-pulse-kf 1.6s ease-in-out infinite;
1069
+ .anet-topo-brand-canvas-mark-envelope-breath {
1070
+ animation: anet-topo-brand-canvas-mark-envelope-breath-kf 13s ease-in-out infinite;
1071
+ }
1072
+ .anet-topo-brand-canvas-mark-envelope-breath[data-topo-brand-canvas-mark-visible="false"] {
1073
+ animation: none;
1074
+ }
1075
+ .anet-topo-brand-canvas-mark-envelope-breath[data-topo-brand-canvas-mark-recede="true"] {
1076
+ animation: none;
1077
+ }
1078
+ @media (prefers-reduced-motion: reduce) {
1079
+ .anet-topo-brand-canvas-mark-envelope-breath { animation: none; }
907
1080
  }
908
1081
 
909
- /* Round 45TopoGraph rotating radar sweep wedge. 6s slow rotation
910
- gives the topology canvas a real "radar scan" feel without distracting
911
- from the agent nodes.
912
- R146: split shorthand into longhand so per-render inline style can
913
- override animation-duration via --sweep-dur, joining the R145
914
- --spoke-dur / R132 --march-dur pattern. Default 6s preserves R45's
915
- original cadence for any caller that forgets the var. */
916
- @keyframes anet-topo-sweep-kf {
917
- 0% { transform: rotate(0deg); }
918
- 100% { transform: rotate(360deg); }
1082
+ /* Round 706title-block wrapper envelope breath at 11s. Mirrors the
1083
+ canvas-brand-pair nested-envelope pattern (R704/R705) at the title-
1084
+ block scope. 11s coprime with all 3 inner trio cadences (5, 6, 10) —
1085
+ no common factor no phase lock with inner anchors. Tighter alpha
1086
+ range (0.92 1, ~8%) than canvas-brand-pair envelopes because the
1087
+ title-block carries primary text identity; even subtle dimming on
1088
+ the H2 + kicker headline reads as flicker if too aggressive. 8%
1089
+ range = "barely-perceptible aliveness". prefers-reduced-motion:
1090
+ reduce neutralizes. */
1091
+ @keyframes anet-topo-title-block-envelope-breath-kf {
1092
+ 0%, 100% { opacity: 1; }
1093
+ 50% { opacity: 0.92; }
919
1094
  }
920
- .anet-topo-sweep {
921
- animation-name: anet-topo-sweep-kf;
922
- animation-duration: var(--sweep-dur, 6s);
923
- animation-timing-function: linear;
924
- animation-iteration-count: infinite;
1095
+ .anet-topo-title-block-envelope-breath {
1096
+ animation: anet-topo-title-block-envelope-breath-kf 11s ease-in-out infinite;
1097
+ }
1098
+ @media (prefers-reduced-motion: reduce) {
1099
+ .anet-topo-title-block-envelope-breath { animation: none; }
925
1100
  }
926
1101
 
927
- /* R155focus-visible outline for TopoGraph chip-row interactives.
928
- Cyber theme's dark canvas often swallows the browser default focus
929
- ring; R154 fixed it for chrome buttons via Tailwind's focus-visible:
930
- ring (box-shadow). Chip-row chips reuse `box-shadow` for the R88 /
931
- R139 / R140 pin-mirror inset so a focus box-shadow would conflict
932
- with the pinned-state shadow. `outline` is a separate CSS property
933
- and doesn't fight box-shadow, so we use it here. `currentColor`
934
- inherits the chip's own accent (green for working, cyan for online,
935
- per-vendor for letters, etc.). Only fires on :focus-visible
936
- (keyboard) mouse focus stays unstyled.
937
- Round 490 / Loop focus outline transitions on color rather than
938
- hard-cutting. Pre-R490 the keyboard focus ring snapped in/out
939
- instantly (no `transition` declaration), while every other hover/
940
- pin state on the same chip eased through the Hero D 200ms ease-out
941
- vocabulary (R459-R475 cluster + R489 hover ring). Keyboard users
942
- tabbing through chips saw discontinuous focus jumps — pointer
943
- users saw smooth motion. R490 unifies the two by holding a
944
- permanent transparent outline (no painted footprint, no layout
945
- shift — `outline` is drawn outside the border-box and doesn't
946
- trigger reflow) and transitioning ONLY outline-color when focus
947
- arrives/leaves. The cyber-theme dark canvas keyboard nav now
948
- reads as one motion vocabulary with pointer hover. */
949
- .anet-topo-chip-focus {
950
- outline: 2px solid transparent;
951
- outline-offset: 1px;
952
- /* R490 (revised) — Tailwind utility classes on the chips (transition-
953
- colors / transition-transform / duration-200) set their own
954
- `transition-property` list at the same selector specificity (0,1,0),
955
- and since Tailwind's stylesheet loads after globals.css they win
956
- the cascade — without `!important` my outline-color transition is
957
- ignored. The unified list below replaces Tailwind's narrower list
958
- with a superset (color, bg, border, opacity, box-shadow, transform
959
- all preserved) plus `outline-color` for R490. All at 200ms ease-
960
- out, matching the chip-row's existing Tailwind duration-200 ease-
961
- out so visible animations on the chip are unchanged.
962
-
963
- Round 524 / Loop — CORRECTION + bug fix.
964
- R490's original 'trade-off' note read:
965
- 'chips with inline style.transition (R210 pressure-bar
966
- segments at 220ms width / 150ms boxShadow / 150ms filter)
967
- keep their inline value because inline style wins over class
968
- rules regardless of !important.'
969
- That was WRONG about CSS cascade. Per the spec, `!important`
970
- author declarations DO override normal-priority inline-style
971
- declarations. So R490's transition-property list silently
972
- dropped every inline transition that targeted a property NOT
973
- in this class's list — including R165's width 220ms tween on
974
- the pressure-bar segments AND R210's filter brightness(1.2)
975
- hover on those same segments. Both motion polishes have been
976
- silently broken (snapping instead of easing) since R490
977
- landed; banked as test-craft lesson in R523.
978
- R524 fixes by extending the transition-property list to
979
- include `width` and `filter` so the pressure-bar segments
980
- (and any future chip-focus surface with width/filter motion)
981
- animate per the class's unified 200ms ease-out. The cadence
982
- shift (R165's 220ms → 200ms, R210's 150ms → 200ms) loses 20-
983
- 50ms of intent precision but slots into the dashboard's
984
- uniform 200ms motion vocabulary the rest of the chip family
985
- already uses — net visible improvement (motion was DEAD,
986
- now eases at 200ms; that's a strictly better state than
987
- snapping).
988
- Other chip-focus surfaces (filter pills, online/working
989
- chips, minimap container, "+N more" footer's <g>) don't
990
- change width or filter at runtime, so adding these
991
- properties to the list is a no-op for them. */
992
- transition-property:
993
- outline-color, color, background-color, border-color,
994
- text-decoration-color, fill, stroke, opacity, box-shadow, transform,
995
- width, filter
996
- !important;
997
- transition-duration: 200ms !important;
998
- transition-timing-function: ease-out !important;
999
- }
1000
- .anet-topo-chip-focus:focus-visible {
1001
- outline-color: currentColor;
1002
- }
1003
-
1004
- /* R156 — focus-visible outline for SVG-side TopoGraph interactives.
1005
- Same idiom as R155's chip-row class but for SVG <g> elements
1006
- (recent-signal rows, legend rows, group labels, edge badges,
1007
- node bodies, "+N more" footer). Each <g> already has role=
1008
- "button"/"link" + tabIndex=0 + onKeyDown (R116/R121/R133/R143/
1009
- R144/R151/R152). Browser default focus outline on SVG is hard
1010
- to spot against the canvas; explicit cyan-300 ring matches the
1011
- dashboard's legendAccent. 2-px outline-offset gives breathing
1012
- room around the painted bounding box.
1013
- Round 491 / Loop — SVG-side counterpart to R490's chip-focus
1014
- outline-color transition. Pre-R491 keyboard focus on SVG g
1015
- elements (recent rows, legend rows, group labels, edge badges,
1016
- nodes, "+N more") snapped instantly; HTML chips (post-R490) now
1017
- ease through 200ms ease-out but SVG g still hard-cut. Same
1018
- baseline-transparent + transition-outline-color recipe brings
1019
- the SVG canvas into the unified keyboard motion vocabulary —
1020
- keyboard users tabbing between chip-row and SVG canvas see
1021
- ONE smooth fade timing instead of HTML-smooth-then-SVG-snap.
1022
- The cyan-300 (#67e8f9) target color matches R156's original
1023
- choice (legendAccent visual identity); only the transition
1024
- timing is new.
1025
- Note on cascade: SVG g elements rarely carry Tailwind
1026
- transition-* utility classes (Tailwind defaults are HTML-
1027
- centric), so the (0,1,0) specificity of .anet-topo-svg-focus
1028
- typically wins without !important. Adding !important defensively
1029
- anyway since the React component may add future inline styles
1030
- or Tailwind classes. Same trade-off as R490 (inline
1031
- style.transition still wins if added). */
1032
- .anet-topo-svg-focus {
1033
- outline: 2px solid transparent;
1034
- outline-offset: 2px;
1035
- transition-property: outline-color !important;
1036
- transition-duration: 200ms !important;
1037
- transition-timing-function: ease-out !important;
1038
- }
1039
- .anet-topo-svg-focus:focus-visible {
1040
- outline-color: #67e8f9;
1041
- }
1042
-
1043
- /* Round 46 — animated dashed spokes flowing from hub outward. Each spoke
1044
- gets a small animationDelay (set inline) so the dashes don't all flow
1045
- in sync — reads as "command relay" pulse rather than a metronome.
1046
- R145: split shorthand into longhand so per-render inline style can
1047
- override animation-duration via the --spoke-dur custom property —
1048
- same idiom R132 uses for --march-dur on group boxes. Default 2.4s
1049
- when --spoke-dur isn't set, so any future caller that forgets the
1050
- var keeps R46's original cadence. */
1051
- @keyframes anet-topo-spoke-flow-kf {
1052
- from { stroke-dashoffset: 0; }
1053
- to { stroke-dashoffset: -40; }
1054
- }
1055
- .anet-topo-spoke-flow {
1056
- animation-name: anet-topo-spoke-flow-kf;
1057
- animation-duration: var(--spoke-dur, 2.4s);
1058
- animation-timing-function: linear;
1059
- animation-iteration-count: infinite;
1060
- }
1061
-
1062
- /* Round 85 — group-box "marching ants" perimeter drift.
1063
- Applied to a grid-layout group box whose `statuses.working > 0`
1064
- AND that is neither hovered nor pinned (those treatments already
1065
- carry their own visual emphasis via solid stroke). Slow 12s
1066
- linear cycle reads as ambient motion, not animation — the eye
1067
- parses "this group has live work" without registering the box
1068
- as moving. R86 stride matches the box's `strokeDasharray="6 6"`
1069
- so a single full cycle returns to a frame-identical state.
1070
- prefers-reduced-motion handled by the blanket override below.
1071
- R132: split shorthand into longhand so per-group inline style can
1072
- override animation-duration via the --march-dur custom property
1073
- without re-declaring keyframe + timing + iteration count. Default
1074
- stays at 12s when --march-dur isn't set, so existing single-group
1075
- call sites keep their cadence. */
1076
- @keyframes anet-topo-groupbox-march-kf {
1077
- from { stroke-dashoffset: 0; }
1078
- to { stroke-dashoffset: -12; }
1079
- }
1080
- .anet-topo-groupbox-live {
1081
- animation-name: anet-topo-groupbox-march-kf;
1082
- animation-duration: var(--march-dur, 12s);
1083
- animation-timing-function: linear;
1084
- animation-iteration-count: infinite;
1085
- }
1086
-
1087
- /* Round 553 — title-block brand logo subtle idle breath.
1088
- 5th anchor in the 呼吸感 breath family — cadence ladder:
1089
- row hot 3s
1090
- hub idle 4s
1091
- brand logo 5s ← this round
1092
- watermark 6s
1093
- crescent 7s
1094
- Pure opacity breath (0.92 ↔ 1, ~8% delta) on the title-block
1095
- crescent SVG so the brand mark reads as a "living mark" at
1096
- rest. Composes cleanly with R548 hover:scale-105 +
1097
- R549 hover:rotate-6 (independent properties: opacity vs
1098
- scale/rotate; the moon keeps breathing as it lifts on hover).
1099
- prefers-reduced-motion handled by R29 blanket override —
1100
- animation-duration → 0.001ms neutralises the keyframe without
1101
- killing the static opacity baseline. Component-side gate also
1102
- applied (`!reducedMotion` toggles the className) as canonical
1103
- TopoGraph breath pattern (sibling to R130 hub idle / R519
1104
- watermark / R326 crescent breath gates). */
1105
- @keyframes anet-topo-brand-logo-breath-kf {
1106
- 0%, 100% { opacity: 1; }
1107
- 50% { opacity: 0.92; }
1108
- }
1109
- .anet-topo-brand-logo-breath {
1110
- animation: anet-topo-brand-logo-breath-kf 5s ease-in-out infinite;
1111
- }
1112
-
1113
- /* Round 683 — brand 书生 logo extends single-layer drop-shadow hover
1114
- (R604, Tailwind `hover:drop-shadow-[0_0_8px_currentColor]`) to the
1115
- multi-layer halo family vocabulary: near 8px + far 16px (2× blur
1116
- stride) at currentColor (inherits inline style.color = teal #0d9488
1117
- / cyan #67e8f9), stacked with brightness(1.10). Matches R642-R667
1118
- 0.5×-falloff convention by using currentColor for both layers —
1119
- the same hue at native alpha layers up because drop-shadow alpha
1120
- accumulates additively when stacked.
1121
- Pure CSS `:hover` so no new React state needed; the SVG element
1122
- listens via its own pseudo-class. Inline transition list already
1123
- covers `filter 200ms ease-out` (R557) so the multi-layer halo eases
1124
- under the same cadence. Tailwind v4 utility composition for multi-
1125
- drop-shadow doesn't work cleanly (per R659 detour note), so dropping
1126
- the utility classes (`hover:brightness-110`, `hover:drop-shadow-[...]`)
1127
- in favor of an explicit CSS rule that wins over the cascade.
1128
- 42nd anchor in multi-layer halo family — first brand-mark anchor. */
1129
- .anet-topo-brand-logo-mark:hover {
1130
- filter: drop-shadow(0 0 8px currentColor)
1131
- drop-shadow(0 0 16px currentColor)
1132
- brightness(1.10);
1133
- }
1134
-
1135
- /* Round 684 — title-block H2 "Command mesh" gains multi-layer halo
1136
- via the wrapper's `:hover` (sibling pattern to R683 brand logo —
1137
- both lift together when ANY surface in the title-block cluster
1138
- is hovered, including the brand logo, kicker, or H2 itself).
1139
- Pre-R684 the H2 had only 2 typographic hover axes (R554 tracking-
1140
- tighter + R556 font-bold) via group-hover Tailwind utilities; the
1141
- paint axis was untouched. R684 adds the multi-layer halo paint
1142
- axis to the H2's hover signature — same near + far layered glow
1143
- vocabulary the 42 other surfaces in the family share.
1144
- Stride: 4 + 8px (2× blur stride at h2 scale, text-lg = 18px —
1145
- smaller than brand logo's 8+16 since the H2 is text not graphic).
1146
- Tint: currentColor — inherits the H2's resolved fill color
1147
- (white on cyber, darker on light theme overrides).
1148
- No brightness multiply — H2 text-white is already near-peak
1149
- luminance on cyber; adding brightness would push past saturation.
1150
- The selector targets the H2 via its data-attr `data-topo-section-
1151
- title` nested under the title-block wrapper's group container
1152
- (`data-topo-section-titleblock-group`). Inline `transition-
1153
- [letter-spacing,font-weight]` on the H2 doesn't include filter,
1154
- so the halo would snap on. Add a transition rule here so the
1155
- halo eases under the same 200ms cadence as the existing 2 axes —
1156
- keeps the cluster's hover gesture motion-coherent.
1157
- 43rd anchor in multi-layer halo family — sibling to R683 brand
1158
- logo, closing the title-block cluster's paint-axis halo at the
1159
- H2 element scope. */
1160
- [data-topo-section-titleblock-group]:hover [data-topo-section-title] {
1161
- filter: drop-shadow(0 0 4px currentColor)
1162
- drop-shadow(0 0 8px currentColor);
1163
- }
1164
- [data-topo-section-title] {
1165
- transition: letter-spacing 200ms ease-out,
1166
- font-weight 200ms ease-out,
1167
- filter 200ms ease-out;
1168
- }
1169
-
1170
- /* Round 685 — kicker "Network Topology" gains multi-layer halo via
1171
- the title-block wrapper's `:hover`, closing the title-block trio
1172
- at 3/3 paint-axis halo (logo R683 + H2 R684 + kicker R685). Pre-
1173
- R685 the kicker had only 2 hover axes (R555 tracking-spread +
1174
- color lift gray-500 → gray-400); the paint axis was untouched.
1175
- R685 adds 2-layer drop-shadow at 2 + 4px stride (2× blur stride
1176
- at kicker scale — text-xs = 12px, smaller than H2's 4+8 and
1177
- logo's 8+16). currentColor inherits the kicker's resolved fill
1178
- (gray-500 rest / gray-400 hover post-R555) so the halo paints
1179
- in the same hue as the text.
1180
- Selector targets the kicker via `data-topo-section-kicker` data-
1181
- attr (already present on the kicker div). Sibling rule + transition
1182
- pattern to R684 H2; both fire on the wrapper's :hover and ease at
1183
- 200ms.
1184
- When the user hovers the title cluster, ALL 3 surfaces (logo +
1185
- H2 + kicker) glow simultaneously — cluster reads as one
1186
- coherent hover unit, paint axis CLOSED across all surfaces.
1187
- 44th anchor in multi-layer halo family — completes title-block
1188
- trio 3/3. */
1189
- [data-topo-section-titleblock-group]:hover [data-topo-section-kicker] {
1190
- filter: drop-shadow(0 0 2px currentColor)
1191
- drop-shadow(0 0 4px currentColor);
1192
- }
1193
- [data-topo-section-kicker] {
1194
- transition: letter-spacing 200ms ease-out,
1195
- color 200ms ease-out,
1196
- filter 200ms ease-out;
1197
- }
1198
-
1199
- /* Round 697 — chrome strip segmented-control wrappers gain a wrapper-
1200
- level multi-layer halo when ANY inner button is hovered. Pre-R697
1201
- the 3 chrome wrappers (Layout / nodeSize / zoom) had only static
1202
- border + background — individual buttons inside halo via R674/R675/
1203
- R673/R668 but the parent wrapper stayed flat. R697 uses CSS `:has()`
1204
- to detect inner button hover and applies a 2-layer drop-shadow at
1205
- cyan-300 (#67e8f9) — compact-chrome tier (2+4 stride, 0.5/0.25 alpha).
1206
-
1207
- When user hovers a child Ring/Grid/S/M/L/zoom button, the WRAPPER
1208
- itself echoes the inner-button halo outward in a soft glow — chrome
1209
- strip reads as one coherent "this control group is active" gesture
1210
- alongside the per-button cyan halo.
1211
-
1212
- Cyan-300 hardcoded (vs theme-driven pal.legendAccent) since CSS
1213
- doesn't have a clean handle on the React-computed palette token at
1214
- this wrapper level. Cyan-300 ≈ light theme's pal.legendAccent
1215
- (#0d9488 teal-600) is bluish; the halo is hover-only + brief so the
1216
- slight cross-theme hue mismatch is acceptable.
1217
-
1218
- transition: filter 200ms ease-out — matches the inner buttons'
1219
- 200ms cadence so wrapper + buttons ease together on hover-in/out.
1220
-
1221
- 53rd anchor in multi-layer halo family — first wrapper-level
1222
- has-hover anchor. Closes chrome-strip "active control group"
1223
- signal at the parent scope alongside R667-R675 per-button anchors. */
1224
- [data-topo-chrome-layout-trailer]:has(button:hover),
1225
- [data-topo-chrome-fleet-group-trailer]:has(button:hover),
1226
- [data-topo-chrome-zoom-wrapper]:has(button:hover) {
1227
- filter: drop-shadow(0 0 2px rgba(103, 232, 249, 0.5))
1228
- drop-shadow(0 0 4px rgba(103, 232, 249, 0.25));
1229
- }
1230
- [data-topo-chrome-layout-trailer],
1231
- [data-topo-chrome-fleet-group-trailer],
1232
- [data-topo-chrome-zoom-wrapper] {
1233
- transition: background-color 200ms ease-out,
1234
- border-color 200ms ease-out,
1235
- filter 200ms ease-out;
1236
- }
1237
-
1238
- /* ─────────────────────────────────────────────────────────────────────
1239
- Round 698 — vendor-distribution chip-row outer wrapper joins the
1240
- R697 wrapper-level :has() halo family. Pre-R698 only the chrome-strip
1241
- wrappers (Layout/nodeSize/zoom) echoed inner-button hover at the
1242
- parent scope; the vendor-distribution wrapper stayed flat even though
1243
- its inner vendor letter chips already halo'd per-element (R676/R688/
1244
- R690).
1245
-
1246
- R697 used `:has(button:hover)` because chrome wrappers contain real
1247
- <button> children. The vendor letter chips are `<span role="button">`
1248
- (clickable spans, not buttons — preserves inline-baseline alignment
1249
- with the title text). The :has() selector variant `[role="button"]:hover`
1250
- extends R697's wrapper-level vocabulary from real-button children
1251
- to ARIA-button children — same gesture, broader applicability.
1252
-
1253
- Cyan-300 (#67e8f9) at 2+4 stride, 0.5/0.25 alpha — matches R697
1254
- cadence exactly. Transition matches the per-chip 200ms hover cascade.
1255
- When user hovers any A:N / O:N / 书:N / ?:N chip, the outer chip-row
1256
- wrapper echoes the per-chip halo outward — reads as "this vendor-
1257
- distribution control group is active" alongside the existing R676
1258
- vendor letter glyph halo + R688 count suffix halo.
1259
-
1260
- 54th anchor in multi-layer halo family — 2nd wrapper-level :has()
1261
- anchor. First :has() rule targeting [role="button"] (vs <button>)
1262
- children. Closes vendor-chip-row "active control group" signal at
1263
- the parent scope. */
1264
- [data-topo-chrome-vendor-distribution-wrapper]:has([role="button"]:hover) {
1265
- filter: drop-shadow(0 0 2px rgba(103, 232, 249, 0.5))
1266
- drop-shadow(0 0 4px rgba(103, 232, 249, 0.25));
1267
- }
1268
- [data-topo-chrome-vendor-distribution-wrapper] {
1269
- transition: background-color 200ms ease-out,
1270
- border-color 200ms ease-out,
1271
- filter 200ms ease-out;
1272
- }
1273
-
1274
- /* ─────────────────────────────────────────────────────────────────────
1275
- Round 699 — kicker "Network Topology" at-rest breathing fade.
1276
-
1277
- The H2 below ("Command mesh") already had its hover halo via the R684
1278
- descendant rule; the kicker eyebrow above had R685 hover halo + R555
1279
- tracking-expand, but at REST it sat at a fixed alpha — static, no
1280
- sign of life. R699 adds a slow respiratory cycle (opacity 0.78 ↔ 1
1281
- at 6 s ease-in-out infinite) on `.anet-topo-kicker-breath`.
1282
-
1283
- 6 s is deliberately ~3× slower than the sidebar brand-pulse (1.6 s)
1284
- and the recent-hot pulse (3 s) — kicker isn't signaling urgency, just
1285
- "this section is alive". Group-hover overrides via R555/R685 take
1286
- precedence the moment user enters the title-block, so the breath
1287
- never competes with the gesture vocabulary.
1288
-
1289
- Pure opacity: no transform / no filter blur / no size change → text
1290
- bounding box stable → topo-overlap-test grid + ring untouched. Pairs
1291
- sequentially with `anet-brand-pulse-kf` (1.6 s, brand status) and
1292
- `anet-recent-hot-pulse-kf` (3 s, recent-signal heat) as the third
1293
- distinct respiratory rhythm in the topo canvas.
1294
-
1295
- prefers-reduced-motion: reduce → animation: none (kicker freezes at
1296
- resting alpha = 1, sibling to other R29-family motion guards).
1297
- ───────────────────────────────────────────────────────────────────── */
1298
- /* Round 699 + R714 — kicker "Network Topology" dual-axis breath.
1299
- R699 introduced 6s opacity breath (0.78 ↔ 1, ~22%); R714 adds a
1300
- SECOND axis in the same @keyframes: a transform scale (0.995 ↔ 1,
1301
- ~0.5%). Two axes share cadence (6s) so they breathe IN PHASE —
1302
- the dimmest moment is also the smallest moment, mirroring the
1303
- R711 H2 dual-axis at the title-block's eyebrow tier.
1304
-
1305
- Why scale 0.5%: the kicker is the section's secondary text (eyebrow
1306
- above the H2 headline). Its alpha range is wider (22% vs H2's 12%)
1307
- so a slightly wider scale range (0.5% vs H2's 0.3%) keeps the
1308
- axes proportionally weighted. Still sub-pixel at xs font-size
1309
- (~12 px text height → 0.06px swing) — imperceptible per-frame,
1310
- accumulates as "the eyebrow gently pulses with the breath".
1311
-
1312
- Mirrors R711 H2 pattern (HTML text dual-axis: opacity + CSS
1313
- transform scale) — both title-block text tiers now have dual-axis
1314
- breath, closing title-block dual-axis symmetry across kicker and
1315
- H2. Brand-logo (the 3rd title-block trio member) still single-axis
1316
- since it already has its own 5-axis hover signature (R548/R549/R553/
1317
- R557/R604) plus 5s breath; further axes would oversubscribe the
1318
- brand mark. Pattern budget: title-block trio members get dual-axis
1319
- if their hover signature is sparse, single-axis if rich.
1320
-
1321
- transform-origin defaults to center — kicker scales around its own
1322
- midpoint, no glyph drift. paint-only → flex parent stable → topo-
1323
- overlap-test untouched. prefers-reduced-motion: reduce neutralizes. */
1324
- /* Round 740 — one-time canvas entrance animation. Plays on FIRST PAINT
1325
- only, then the canvas sits at scale(1) opacity(1) for the rest of
1326
- the session. Distinct from all prior animation families:
1327
- - breath family (R699+) is rest-axis infinite
1328
- - ambient family (R735+) is sweep-axis infinite
1329
- - R740 entrance is ONE-SHOT (no `infinite` keyword) — plays once
1330
- Subsequent re-mounts (route changes, etc.) replay the animation —
1331
- intended as a "the canvas just arrived" signal to the user.
1332
-
1333
- Animation: 0% scale(0.99) opacity(0.8) → 100% scale(1) opacity(1)
1334
- over 600ms with ease-out timing. Subtle: the scale change is only
1335
- 1% (0.99 → 1.00); opacity ramps from 0.8 to 1.0. Reads as "settling
1336
- in" — the canvas zooms in slightly while fading in.
1337
-
1338
- transform-origin defaults to 50% 50% (canvas center), so the scale
1339
- ripple comes from the middle outward.
1340
-
1341
- prefers-reduced-motion guard: when reduced motion is preferred, the
1342
- class isn't applied (JSX-level gate in TopoGraph.tsx), so the CSS
1343
- animation never runs. Belt-and-suspenders @media block below
1344
- neutralises if the class somehow ends up applied. */
1345
- @keyframes anet-topo-canvas-entrance-kf {
1346
- 0% { transform: scale(0.99); opacity: 0.8; }
1347
- 100% { transform: scale(1); opacity: 1; }
1102
+ /* Round 742 title-block entrance, 2nd member of the entrance family.
1103
+ Staggered choreography with R740 canvas entrance:
1104
+ R740 canvas 0 600 ms (scale 0.99 1, opacity 0.8 1)
1105
+ R742 title-block 200 → 700 ms (translateY -4px 0, opacity 0.7 1)
1106
+ The 100ms gap (600 700 ms) between canvas settle and title-block
1107
+ settle creates a perceived "shake out" canvas arrives, then the
1108
+ heading writes itself into place. Both done by ~700 ms.
1109
+
1110
+ CSS cascade detail: when an element has BOTH .anet-topo-title-block-
1111
+ envelope-breath (R706, infinite 11s breath) AND .anet-topo-title-
1112
+ block-entrance (R742, one-shot 500ms), the `animation` property
1113
+ doesn't merge — only one wins. So R742 declares a COMPOUND animation
1114
+ listing BOTH (entrance first as one-shot, breath second as the
1115
+ ongoing infinite). Placed AFTER the R706 envelope-breath block in
1116
+ source order so cascade gives R742 the win when both classes are
1117
+ applied. When ONLY envelope-breath is applied (reducedMotion strips
1118
+ the entrance class via JSX), R706 rule takes over with just the
1119
+ breath animation. */
1120
+ @keyframes anet-topo-title-block-entrance-kf {
1121
+ 0% { transform: translateY(-4px); opacity: 0.7; }
1122
+ 100% { transform: translateY(0); opacity: 1; }
1348
1123
  }
1349
- .anet-topo-canvas-entrance {
1350
- animation: anet-topo-canvas-entrance-kf 600ms ease-out;
1351
- transform-origin: 50% 50%;
1124
+ .anet-topo-title-block-entrance {
1125
+ animation:
1126
+ anet-topo-title-block-entrance-kf 500ms ease-out 200ms backwards,
1127
+ anet-topo-title-block-envelope-breath-kf 11s ease-in-out infinite;
1352
1128
  }
1353
1129
  @media (prefers-reduced-motion: reduce) {
1354
- .anet-topo-canvas-entrance { animation: none; }
1130
+ .anet-topo-title-block-entrance { animation: none; }
1355
1131
  }
1356
1132
 
1357
- /* Round 728recent + legend panel titles join the triple-axis tier
1358
- at 8 s, forming the "8 s triple-axis PAIR" (mirror to R724's 6 s pair).
1359
- Both panel titles already share R700/R701 SMIL opacity (8 s) +
1360
- R713 SMIL font-size (8 s) R728 adds a 3rd CSS-animated axis:
1361
- text-shadow glow at the same 8 s cadence in phase.
1362
-
1363
- Same hybrid SMIL + CSS pattern as R722 watermark: SMIL drives SVG
1364
- attributes (opacity, font-size); CSS drives the text-shadow CSS
1365
- property on the same <text> element. Modern browsers honour both
1366
- vocabularies side-by-side without conflict.
1367
-
1368
- Multi-cadence triple-axis tier post-R728 (6 members across 4 cadences):
1369
- 6 s kicker + watermark (R721/R722, "6 s pair")
1370
- 8 s recent + legend titles (R728, "8 s pair") ← this round
1371
- 9 s zoom-level readout (R727)
1372
- 10 s H2 section title (R725)
1373
-
1374
- Pattern structural mirror: R724's 6 s pair (title-block + canvas-
1375
- brand tiers) finds its sibling in R728's 8 s pair (panel-title tier).
1376
- Both pairs use parity-shape (members at same cadence). Adds new
1377
- R717 pattern entry `triple-axis-pair-8s` (shape `8s-triple-pair`)
1378
- to formalize the structural mirror.
1379
-
1380
- Glow tuning vs prior 4 triple-axis members:
1381
- R721 kicker blur 6 px, alpha 0.30 @ 6 s (title-block, sharp)
1382
- R722 watermark blur 8 px, alpha 0.20 @ 6 s (canvas-brand, quiet)
1383
- R725 H2 blur 8 px, alpha 0.22 @ 10 s (title-block, mellow)
1384
- R727 zoom-level blur 5 px, alpha 0.25 @ 9 s (data tier, sharp pill)
1385
- R728 panel-pair blur 7 px, alpha 0.23 @ 8 s (panel-title) ← this round
1386
- Mid-range blur (7 px) — between zoom-level (5 px pill) and H2 (8 px
1387
- heading); alpha 0.23 — between H2 (0.22 mellow) and zoom-level (0.25)
1388
- to position the panel-title glow as a "calm-medium" register that
1389
- matches the panel titles' weight in the visual hierarchy (heading-
1390
- weight but smaller fontSize than H2).
1133
+ /* Round 743chrome strip entrance, 3rd member of the entrance family,
1134
+ completing the entrance TRIO. Three-stage cascade:
1135
+ R740 canvas 0 600 ms scale 0.99→1, opacity 0.8→1
1136
+ R742 title-block 200 700 ms translateY -4→0, opacity 0.7→1
1137
+ R743 chrome strip 400 900 ms translateX 8→0, opacity 0.7→1
1138
+ Each stage offset +200 ms. translateX (not translateY) because the
1139
+ chrome strip docks at the canvas right edge sliding in from its
1140
+ own edge reads as "docking into place".
1391
1141
 
1392
- Gating: piggybacks on the existing JSX gates (R700: !reducedMotion &&
1393
- !activeEdgeKey; R701: !reducedMotion && !pinnedStatus). The CSS class
1394
- is applied conditionally; when ungated, both SMIL animates and CSS
1395
- animation coexist. prefers-reduced-motion @media guard provides
1396
- belt-and-suspenders. */
1397
- @keyframes anet-topo-panel-title-glow-breath-kf {
1398
- 0%, 100% { text-shadow: none; }
1399
- 50% { text-shadow: 0 0 7px rgba(34, 211, 238, 0.23); }
1142
+ The chrome strip wrapper div has no other animation, so a plain
1143
+ single-animation class works here (no compound needed like R742's
1144
+ title-block which collided with R706 envelope-breath). One-shot
1145
+ no `infinite`, runs once per mount. prefers-reduced-motion JSX gate
1146
+ + CSS @media guard. */
1147
+ @keyframes anet-topo-chrome-strip-entrance-kf {
1148
+ 0% { transform: translateX(8px); opacity: 0.7; }
1149
+ 100% { transform: translateX(0); opacity: 1; }
1400
1150
  }
1401
- .anet-topo-panel-title-glow-breath {
1402
- animation: anet-topo-panel-title-glow-breath-kf 8s ease-in-out infinite;
1151
+ .anet-topo-chrome-strip-entrance {
1152
+ animation: anet-topo-chrome-strip-entrance-kf 500ms ease-out 400ms backwards;
1403
1153
  }
1404
1154
  @media (prefers-reduced-motion: reduce) {
1405
- .anet-topo-panel-title-glow-breath { animation: none; }
1155
+ .anet-topo-chrome-strip-entrance { animation: none; }
1406
1156
  }
1407
1157
 
1408
- /* Round 722watermark text becomes the 2ND TRIPLE-axis surface,
1409
- completing a "6 s triple-axis PAIR" with R721 kicker (both at the
1410
- fastest dual-axis cadence, both with sparse hover signatures, both
1411
- read-target text symmetric structural mirror).
1412
-
1413
- The watermark's prior axes:
1414
- R519 SMIL opacity (6 s, 0.32 ↔ 0.48)
1415
- R712 SMIL letter-spacing (6 s, 0.45 ↔ 0.55)
1416
- R722 adds a 3rd CSS-animated axis on the SAME `<text>` element:
1417
- R722 CSS text-shadow (6 s, none ↔ 0 0 8 px rgba(34, 211, 238, 0.20))
1158
+ /* Round 707chrome Layout wrapper at-rest breath at 17 s. Slowest tier
1159
+ among HTML chrome respiratory anchors (sibling to R703 zoom-level 9 s
1160
+ in the chrome strip's data tier; this 17 s lands in the chrome strip's
1161
+ control tier). 17 is prime → coprime with every other cadence in the
1162
+ rolodex (3/4/5/6/7/8/9/10/11/13/15). Never phase-locks with any other
1163
+ anchor.
1418
1164
 
1419
- The SMIL animations (opacity + letter-spacing) drive SVG attributes
1420
- in their own clock; the CSS animation drives `text-shadow` as a CSS
1421
- property. SVG <text> in modern browsers honours both vocabularies
1422
- on the same element — no conflict. The 6 s cadence shared across
1423
- all 3 axes keeps them in phase: at the dimmest+tightest moment the
1424
- glow appears; at the brightest+widest moment the glow is gone. One
1425
- coherent inhale-exhale.
1165
+ Tightest alpha range yet (0.94 1, ~6%) the Layout buttons are
1166
+ interactive controls with active-state cyan tint; even ~10% dimming
1167
+ on the inner button color would feel jarring. 6% = "barely-perceptible
1168
+ aliveness" on a control group.
1426
1169
 
1427
- Glow tuning vs kicker (R721):
1428
- kicker blur 6 px, alpha 0.30
1429
- watermark blur 8 px, alpha 0.20
1430
- The watermark text sits at canvas bottom-left at fontSize 11 with
1431
- a base opacity of 0.4 (R282) the glow needs more spread (8 vs 6)
1432
- to register at that low base opacity, but less alpha (0.20 vs 0.30)
1433
- to stay proportional to the wordmark's quieter visual register.
1170
+ Hover gate via :has() selector — when user hovers any inner Ring/Grid
1171
+ button, the R697 wrapper-level halo (cyan filter) takes precedence;
1172
+ animation: none on hover pauses the breath so the halo reads crisp
1173
+ without underlying alpha drift. Third gate-off mechanism in the family
1174
+ vocabulary, sibling to R703 hover data-attr gate + R704/R705 recede
1175
+ gates. */
1176
+ @keyframes anet-topo-chrome-layout-trailer-breath-kf {
1177
+ 0%, 100% { opacity: 1; }
1178
+ 50% { opacity: 0.94; }
1179
+ }
1180
+ .anet-topo-chrome-layout-trailer-breath {
1181
+ animation: anet-topo-chrome-layout-trailer-breath-kf 17s ease-in-out infinite;
1182
+ }
1183
+ .anet-topo-chrome-layout-trailer-breath:has(button:hover) {
1184
+ animation: none;
1185
+ }
1186
+ @media (prefers-reduced-motion: reduce) {
1187
+ .anet-topo-chrome-layout-trailer-breath { animation: none; }
1188
+ }
1434
1189
 
1435
- Gating: piggybacks on the existing R519 reducedMotion JSX gate
1436
- (the entire <animate> tree only mounts when !reducedMotion). When
1437
- reducedMotion is true the class is still applied but the @media
1438
- (prefers-reduced-motion: reduce) below neutralises the CSS
1439
- animation. Belt-and-suspenders. */
1440
- @keyframes anet-topo-brand-watermark-glow-breath-kf {
1441
- 0%, 100% { text-shadow: none; }
1442
- 50% { text-shadow: 0 0 8px rgba(34, 211, 238, 0.20); }
1190
+ /* Round 708 chrome nodeSize wrapper at-rest breath at 19 s. Sibling
1191
+ to R707 Layout 17 s both chrome control-tier wrappers carry the
1192
+ same alpha range (6%) + the same :has(button:hover) gate. Two coprime
1193
+ prime cadences (17, 19) form a chrome-control respiratory pair,
1194
+ pattern-parallel to canvas-brand-pair (R704/R705) coprime envelope
1195
+ pair. */
1196
+ @keyframes anet-topo-chrome-fleet-group-trailer-breath-kf {
1197
+ 0%, 100% { opacity: 1; }
1198
+ 50% { opacity: 0.94; }
1443
1199
  }
1444
- .anet-topo-brand-watermark-glow-breath {
1445
- animation: anet-topo-brand-watermark-glow-breath-kf 6s ease-in-out infinite;
1200
+ .anet-topo-chrome-fleet-group-trailer-breath {
1201
+ animation: anet-topo-chrome-fleet-group-trailer-breath-kf 19s ease-in-out infinite;
1202
+ }
1203
+ .anet-topo-chrome-fleet-group-trailer-breath:has(button:hover) {
1204
+ animation: none;
1446
1205
  }
1447
1206
  @media (prefers-reduced-motion: reduce) {
1448
- .anet-topo-brand-watermark-glow-breath { animation: none; }
1207
+ .anet-topo-chrome-fleet-group-trailer-breath { animation: none; }
1449
1208
  }
1450
1209
 
1451
- /* Round 721kicker becomes the family's first TRIPLE-axis breath
1452
- surface. R699 opacity (6 s) + R714 transform-scale (6 s) joined by
1453
- R721 text-shadow (6 s) all 3 axes share the 6 s cadence in phase.
1210
+ /* Round 709chrome zoom wrapper at-rest breath at 23 s. Closes the
1211
+ chrome control trio (R707 Layout 17s + R708 nodeSize 19s + R709 zoom
1212
+ 23s) with three adjacent prime coprime cadences. 23s slowest in the
1213
+ chrome trio — reads as "calm anchor for the viewport-control cluster"
1214
+ since zoom is the most functional of the 3 wrappers (its inner buttons
1215
+ compound viewport math). Same alpha (6%) + :has(button:hover) gate
1216
+ as siblings. */
1217
+ @keyframes anet-topo-chrome-zoom-wrapper-breath-kf {
1218
+ 0%, 100% { opacity: 1; }
1219
+ 50% { opacity: 0.94; }
1220
+ }
1221
+ .anet-topo-chrome-zoom-wrapper-breath {
1222
+ animation: anet-topo-chrome-zoom-wrapper-breath-kf 23s ease-in-out infinite;
1223
+ }
1224
+ .anet-topo-chrome-zoom-wrapper-breath:has(button:hover) {
1225
+ animation: none;
1226
+ }
1227
+ @media (prefers-reduced-motion: reduce) {
1228
+ .anet-topo-chrome-zoom-wrapper-breath { animation: none; }
1229
+ }
1454
1230
 
1455
- At rest peak (0%, 100%) the kicker reads as a static title;
1456
- at mid-breath (50%) it dims (0.78), retracts (scale 0.995), AND
1457
- emits a soft cyan glow (text-shadow 0 0 6px rgba(34, 211, 238, 0.30)).
1458
- The glow appears OUT-OF-PHASE with the dim as the text shrinks
1459
- slightly it gains a subtle aura. Reads as an "inhale glow": a small
1460
- visible payoff at the bottom of the breath cycle.
1231
+ /* Round 718 chrome reset button joins the respiratory family at 21 s.
1232
+ 5th anchor on the chrome-strip pattern, extending it from
1233
+ "tiered-with-trio" "tiered-with-quartet". The chrome strip now
1234
+ spans a data tier + a prime quartet across control surfaces:
1235
+ R703 zoom-level readout 9 s (data tier)
1236
+ R707 Layout wrapper 17 s (control wrapper tier)
1237
+ R708 nodeSize wrapper 19 s (control wrapper tier)
1238
+ R718 reset button 21 s (atomic control tier) ← this round
1239
+ R709 zoom wrapper 23 s (control wrapper tier)
1461
1240
 
1462
- Why kicker first (vs other dual-axis surfaces):
1463
- - kicker has the SPARSEST hover signature in the family being
1464
- below H2 (the page heading), it's read-target text, not an
1465
- interactive surface. The breath family's "if hover sig sparse,
1466
- dual-axis; if hover sig rich, single-axis" budget rule extends
1467
- naturally to "if hover sig SPARSE, can go triple-axis".
1468
- - 6 s is the fastest dual-axis cadence in the rolodex at fast
1469
- cadence the 3rd axis (a glow appear+disappear cycle every 3 s
1470
- on each half) registers as ambient rhythm, not a distraction.
1241
+ 21 is composite (3 × 7), the ONE composite in the prime quartet —
1242
+ slots between the prime wrappers 19 and 23 to fill the 17/19/21/23
1243
+ odd-number ladder. Coprime with all rolodex cadences (the rolodex
1244
+ has no 3-multiples until 9 and no 7-multiples until 7 itself — but
1245
+ neither 9 nor 7 share factors with 21's other co-tier mates 17/19/23).
1246
+ Actually 21 IS divisible by 3 like 9 — gcd(21,9)=3 so 21 phase-locks
1247
+ with 9 every 63 s. That's deliberate: the data tier (9 s) and the
1248
+ atomic-control tier (21 s) RE-ALIGN every minute a slow handshake
1249
+ between "what the chrome reads" and "the reset action". Aesthetic
1250
+ over arithmetic: a 1-per-minute alignment is sub-perceptible.
1471
1251
 
1472
- text-shadow chosen over letter-spacing or font-size because:
1473
- - text-shadow doesn't reflow text (no horizontal motion)
1474
- - glow against the dark canvas reads clearly without competing
1475
- with the opacity dim
1476
- - cyan tint at 30% alpha + 6 px blur is below the brightness
1477
- threshold of any node halo (R674/R675 nodes use 0.5+ alpha)
1252
+ Atomic-control sub-tier rationale: the reset button is the only
1253
+ STANDALONE atomic chrome button with a 5-axis hover signature
1254
+ (R594 brightness + R400 hover-lift + R196 active-state + R350 icon
1255
+ spin + R184 release pop). The 5 hover axes already give it a rich
1256
+ interactive voice; a 6th REST axis at 7% alpha (slightly looser than
1257
+ the 6% wrappers, to compensate for the smaller icon-button surface)
1258
+ marks it as "alive" at rest without competing with the hover axes.
1478
1259
 
1479
- Composition note: the R29 prefers-reduced-motion guard already
1480
- neutralizes the entire 6 s animation; no separate text-shadow
1481
- reduced-motion logic needed. */
1482
- @keyframes anet-topo-kicker-breath-kf {
1483
- 0%, 100% { opacity: 1; transform: scale(1); text-shadow: none; }
1484
- 50% { opacity: 0.78; transform: scale(0.995); text-shadow: 0 0 6px rgba(34, 211, 238, 0.30); }
1260
+ Hover gate via data-attr (R703 pattern, not R707/R708/R709 :has()
1261
+ pattern). The button itself exposes data-topo-chrome-reset-hover
1262
+ already plumbed for the R350 icon-rotate cue and R594 brightness —
1263
+ so the breath gates off naturally on its own hover state. Sibling
1264
+ gate pattern to R703 zoom-level (the only other chrome anchor at
1265
+ the leaf-element tier; both gate via their own hover data-attr
1266
+ rather than parent :has()). */
1267
+ @keyframes anet-topo-chrome-reset-breath-kf {
1268
+ 0%, 100% { opacity: 1; }
1269
+ 50% { opacity: 0.93; }
1485
1270
  }
1486
- .anet-topo-kicker-breath {
1487
- animation: anet-topo-kicker-breath-kf 6s ease-in-out infinite;
1271
+ .anet-topo-chrome-reset-breath {
1272
+ animation: anet-topo-chrome-reset-breath-kf 21s ease-in-out infinite;
1273
+ }
1274
+ .anet-topo-chrome-reset-breath[data-topo-chrome-reset-hover="true"] {
1275
+ animation: none;
1488
1276
  }
1489
1277
  @media (prefers-reduced-motion: reduce) {
1490
- .anet-topo-kicker-breath { animation: none; }
1278
+ .anet-topo-chrome-reset-breath { animation: none; }
1491
1279
  }
1492
1280
 
1493
- /* Round 702H2 "Command mesh" at-rest breathing fade, completing the
1494
- title-block respiratory trio (brand-logo 5 s / kicker 6 s / H2 10 s).
1495
- Tighter alpha range (0.88 1, ~12%) vs the kicker (0.78 ↔ 1, ~22%):
1496
- H2 carries more visual weight, so a gentler breath keeps it from
1497
- looking flickery. Slowest in the trio 10 s reads as calm anchoring,
1498
- not anxious. prefers-reduced-motion: reduce animation: none,
1499
- sibling to the R699 kicker guard. */
1500
- /* Round 702 + R711 — H2 "Command mesh" dual-axis breath. R702 introduced
1501
- the 10s opacity breath (0.88 ↔ 1, ~12%); R711 adds a SECOND axis in
1502
- the same @keyframes: a transform scale (0.997 ↔ 1, ~0.3%). The two
1503
- axes share cadence (10s) so they breathe in PHASE — the dimmest moment
1504
- is also the smallest moment, like a single coherent inhale-exhale.
1281
+ /* Round 719chrome fullscreen button joins the respiratory family at
1282
+ 25 s. 6th anchor on the chrome-strip pattern, extending the shape
1283
+ from "tiered-with-quartet" "tiered-with-quintet". Sibling to R718
1284
+ reset (21 s) closes the ATOMIC-CONTROL DUO: both standalone
1285
+ chrome buttons (reset + fullscreen, the only non-segmented chrome
1286
+ buttons in the family per R400 doc) now breathe at parity alpha
1287
+ range with coprime cadences.
1505
1288
 
1506
- Why scale 0.3%: the H2 is the section's primary text identity; scale
1507
- changes affect glyph rasterization at sub-pixel level. 0.3% range
1508
- keeps the visible change at fraction of a pixel for a 40-px-tall text,
1509
- imperceptible per-frame but cumulatively reads as "the title gently
1510
- pulses with the breath", reinforcing the opacity dimming without
1511
- adding new gesture. Scale doesn't affect layout flow (CSS transform
1512
- is paint-only) flex parent stays stable, overlap test safe.
1289
+ Chrome-strip pattern post-R719:
1290
+ R703 zoom-level readout 9 s (data tier)
1291
+ R707 Layout wrapper 17 s (control wrapper tier)
1292
+ R708 nodeSize wrapper 19 s (control wrapper tier)
1293
+ R718 reset button 21 s (atomic control tier)
1294
+ R709 zoom wrapper 23 s (control wrapper tier)
1295
+ R719 fullscreen button 25 s (atomic control tier) this round
1513
1296
 
1514
- First DUAL-AXIS breath anchor in the 呼吸感 family establishes that
1515
- future anchors can layer multiple animation properties on a single
1516
- keyframe block for "deeper aliveness" on a single surface, without
1517
- needing a second class or animation. Pattern budget: prefer single-
1518
- axis on most anchors (visual hierarchy) and reserve dual-axis for
1519
- primary identity surfaces (H2 here = first primary-identity dual). */
1520
- /* Round 725 H2 section title becomes the 3RD triple-axis surface,
1521
- FIRST triple-axis member at a NON-6 s cadence (10 s). Establishes
1522
- that the triple-axis TIER is multi-cadence rather than 6 s-locked —
1523
- the R724 `triple-axis-pair` pattern stays valid for kicker+watermark
1524
- at 6 s; H2 joins the triple-axis TIER as a solo 10 s member.
1297
+ 25 = coprime with every other rolodex cadence (no factor 5
1298
+ anywhere). Fills the 17/19/21/23/25 odd-number ladder perfectly
1299
+ and becomes the new slowest cadence on the rolodex (prior slowest:
1300
+ 23 s zoom wrapper). The chrome strip now spans a 16 s arc from
1301
+ 9 25, with its two atomic-control members at 21 s and 25 s
1302
+ bracketing the wrapper trio (17/19/23) atomic controls breathe
1303
+ slower than wrappers, matching their role as terminal commits
1304
+ (reset wipes state; fullscreen flips viewport mode).
1525
1305
 
1526
- H2's prior dual-axis (R702 opacity + R711 transform-scale @ 10 s)
1527
- gains a 3rd CSS-animated axis: text-shadow glow at 10 s in phase.
1528
- Reads as a slow inhale glow on the section heading — calmer than
1529
- the 6 s pair (kicker/watermark) because H2's slower cadence keeps
1530
- the glow appearance interval at 5 s (vs 3 s for the 6 s tier).
1306
+ Same 7% alpha (0.93 1) as R718 reset atomic-control tier
1307
+ parity. The duo reads as a matched pair at rest. Hover gate via
1308
+ data-topo-chrome-fullscreen-hover (R703 leaf-element pattern,
1309
+ sibling to R718 reset's gate). */
1310
+ @keyframes anet-topo-chrome-fullscreen-breath-kf {
1311
+ 0%, 100% { opacity: 1; }
1312
+ 50% { opacity: 0.93; }
1313
+ }
1314
+ .anet-topo-chrome-fullscreen-breath {
1315
+ animation: anet-topo-chrome-fullscreen-breath-kf 25s ease-in-out infinite;
1316
+ }
1317
+ .anet-topo-chrome-fullscreen-breath[data-topo-chrome-fullscreen-hover="true"] {
1318
+ animation: none;
1319
+ }
1320
+ @media (prefers-reduced-motion: reduce) {
1321
+ .anet-topo-chrome-fullscreen-breath { animation: none; }
1322
+ }
1531
1323
 
1532
- Glow tuning vs kicker (R721) and watermark (R722):
1533
- kicker blur 6 px, alpha 0.30 @ 6 s
1534
- watermark blur 8 px, alpha 0.20 @ 6 s
1535
- H2 blur 8 px, alpha 0.22 @ 10 s ← this round
1536
- H2 sits between the two at alpha; its larger fontSize (section
1537
- heading, much bigger than kicker/watermark) needs a wider blur
1538
- (8 px to match watermark) but a slightly higher alpha than
1539
- watermark (0.22 vs 0.20) because at H2's slower 10 s cadence the
1540
- glow gets a longer dwell time at peak — proportionally tighter
1541
- alpha would feel under-emphasised vs the 6 s pair's snappier peaks.
1324
+ /* ─────────────────────────────────────────────────────────────────────
1325
+ Round 29 prefers-reduced-motion a11y sweep
1542
1326
 
1543
- The triple-axis tier now has 3 members across 2 cadences:
1544
- 6 s kicker + watermark (R724 "triple-axis-pair" pattern)
1545
- 10 s H2 (solo)
1546
- R725 doesn't add a new R717 pattern entry — H2 is a solo, not
1547
- a pair. R723 catalog (triple-axis-surfaces) extends to 3 entries.
1327
+ Rounds 19~27 accumulated a lot of small animations: Tasks row 0fr↔1fr
1328
+ expand, chevron rotate, hover bg shifts, Cmd+K popover, TaskChatPanel
1329
+ slide-in, brand/skeleton pulses already had their own guards.
1548
1330
 
1549
- prefers-reduced-motion guard already in place on the existing
1550
- R702 H2 breath class; this round's text-shadow axis joins the
1551
- same animation rule and is neutralised by the same @media block. */
1552
- @keyframes anet-topo-section-title-breath-kf {
1553
- 0%, 100% { opacity: 1; transform: scale(1); text-shadow: none; }
1554
- 50% { opacity: 0.88; transform: scale(0.997); text-shadow: 0 0 8px rgba(34, 211, 238, 0.22); }
1555
- }
1556
- .anet-topo-section-title-breath {
1557
- animation: anet-topo-section-title-breath-kf 10s ease-in-out infinite;
1558
- /* transform-origin defaults to 50% 50% which is what we want — the H2
1559
- scales around its own center, no glyph drift. */
1560
- }
1331
+ For users who set system "reduce motion": neutralise everything in one
1332
+ pass. Don't kill state changes outright only their durations and
1333
+ keyframes. Hover and focus colours still apply, just instantly.
1334
+ ───────────────────────────────────────────────────────────────────── */
1561
1335
  @media (prefers-reduced-motion: reduce) {
1562
- .anet-topo-section-title-breath { animation: none; }
1336
+ *,
1337
+ *::before,
1338
+ *::after {
1339
+ animation-duration: 0.001ms !important;
1340
+ animation-iteration-count: 1 !important;
1341
+ transition-duration: 0.001ms !important;
1342
+ scroll-behavior: auto !important;
1343
+ }
1344
+ /* Skeleton pulse already has its own guard; harden in case the bar
1345
+ class is reused without anet-skeleton-pulse. */
1346
+ .anet-skeleton-bar {
1347
+ animation: none !important;
1348
+ }
1349
+ /* Brand pulse hidden — green/red dot still indicates state via color. */
1350
+ .anet-brand-pulse {
1351
+ animation: none !important;
1352
+ }
1353
+ /* Drawer entrance kills — keyframes promoted to globals in r30. */
1354
+ .animate-slide-in,
1355
+ .anet-fade-in,
1356
+ .anet-current-step-pulse,
1357
+ .anet-topo-sweep,
1358
+ .anet-topo-spoke-flow,
1359
+ .anet-topo-groupbox-live {
1360
+ animation: none !important;
1361
+ }
1362
+ /* Tasks row grid 0fr↔1fr keep working (it's the toggle mechanism),
1363
+ but the smooth transition just snaps. Already handled via the
1364
+ blanket transition-duration override above. */
1563
1365
  }
1564
1366
 
1565
- /* Round 703 — chrome zoom-level readout at-rest breath. 9 s cadence sits
1566
- between the 8 s panel titles and the 10 s H2 in the respiratory
1567
- rolodex. Tighter alpha range (0.85 ↔ 1, ~15%) than the kicker —
1568
- readout carries tabular numeric data, so it shouldn't drift too far
1569
- from full opacity. Hover gate via the data attribute: when
1570
- data-topo-chrome-zoom-level-hover="true", animation: none, and the
1571
- existing R347 ls + R420 fw + R517 color + R593 brightness + R668
1572
- halo 4-axis lift takes precedence cleanly. prefers-reduced-motion:
1573
- reduce neutralizes, sibling to R699/R702 guards. */
1574
- /* Round 703 + R715 — chrome zoom-level readout dual-axis breath.
1575
- R703 introduced 9s opacity breath (0.85 ↔ 1, ~15%); R715 adds a
1576
- SECOND axis in the same @keyframes: a transform scale (0.996 ↔ 1,
1577
- ~0.4%). Two axes share cadence (9s) so they breathe IN PHASE —
1578
- dimmest = smallest, mirroring R711 H2 / R714 kicker pattern at the
1579
- CHROME DATA TIER (vs title-block primary text tier).
1367
+ /* ────────────────────────────────────────────────────
1368
+ #190 mobile polish tap feel
1369
+ ────────────────────────────────────────────────────
1370
+ `touch-action: manipulation` removes the 300 ms tap-delay browsers
1371
+ add to disambiguate double-tap-zoom, and (more importantly) keeps an
1372
+ accidental double-tap on an interactive element from zooming the
1373
+ page. Pinch-zoom on the rest of the page is unaffected. The hint is
1374
+ a no-op on inputs without a touch driver, so zero desktop cost.
1580
1375
 
1581
- Why scale 0.4% on zoom-level (vs H2's 0.3%, kicker's 0.5%): zoom-
1582
- level's alpha range is between H2 (12%) and kicker (22%) at 15%
1583
- scale axis interpolated to 0.4% keeps the three dual-axis HTML
1584
- text surfaces co-proportioned. Sub-pixel at xs font-size → ~0.04px
1585
- swing at ~10px text height; imperceptible per-frame, accumulates
1586
- as "the readout pulses with the breath".
1376
+ `-webkit-tap-highlight-color` swaps iOS Safari's default opaque grey
1377
+ tap overlay for a faint themed shimmer (~18 % accent over transparent),
1378
+ matching the cyber / light palettes via the `--accent` token. */
1379
+ @layer base {
1380
+ button,
1381
+ a,
1382
+ [role="button"],
1383
+ [role="tab"],
1384
+ [role="menuitem"],
1385
+ summary {
1386
+ touch-action: manipulation;
1387
+ -webkit-tap-highlight-color: color-mix(in oklab, var(--accent) 18%, transparent);
1388
+ }
1389
+ }
1587
1390
 
1588
- Pattern budget extension — chrome data tier dual-axis:
1589
- R699+R714 kicker 6s eyebrow tier
1590
- R703+R715 zoom-level 9s chrome data tier ← this round
1591
- R702+R711 H2 10s primary headline tier
1592
- The 6/9/10 s cadences and the kicker/zoom-level/H2 scale ratios
1593
- (0.5/0.4/0.3 %) form a tier-graded ladder: faster cadence + larger
1594
- scale on secondary-tier text; slower cadence + smaller scale on
1595
- primary-tier text. Each tier has its own "tempo + amplitude"
1596
- coupled signature — kicker pulses quickly-and-noticeably, H2
1597
- pulses slowly-and-subtly, zoom-level sits between.
1598
1391
 
1599
- Hover gate via `[data-topo-chrome-zoom-level-hover="true"]` →
1600
- animation: none, so R347/R420/R517/R593 hover axes take precedence
1601
- without animation underneath. transform-origin defaults to center
1602
- pill scales around its own midpoint. prefers-reduced-motion:
1603
- reduce also neutralizes. */
1604
- /* Round 727 — zoom-level readout becomes the 4TH triple-axis surface and
1605
- FIRST chrome-tier (data-tier) member to join the multi-cadence triple-
1606
- axis tier. R703 opacity (9 s) + R715 transform-scale (9 s) joined by
1607
- R727 text-shadow (9 s) — all 3 axes share the 9 s cadence in phase.
1392
+ /* ════════════════════════════════════════════════════════════
1393
+ #217 S6 Light (white) theme, restored from pre-R8 history
1394
+ (4e05d91^). R8 stripped it as dead code because no UI exposed
1395
+ it; Vincent asked for an optimized white theme in Settings,
1396
+ which now owns the toggle. Mint/sunset stay deleted.
1397
+ ════════════════════════════════════════════════════════════ */
1608
1398
 
1609
- Multi-cadence triple-axis tier post-R727 (4 members across 3 cadences):
1610
- 6 s kicker + watermark (title-block + canvas-brand tiers)
1611
- 9 s zoom-level readout (data tier) ← this round
1612
- 10 s H2 section title (title-block tier)
1613
- Pattern budget extension: prior triple-axis members were all read-target
1614
- text in primary-identity tiers (title-block + canvas-brand). R727 extends
1615
- to the DATA tier — a chrome readout, not pure text. Justified because:
1616
- - zoom-level's hover sig (R703 data-attr gate) is sparse: only the
1617
- hover-tracking attr; no rich tactile axes
1618
- - the surface is a single-tone monospace digit pill — visually quiet
1619
- enough that a glow pulse remains subordinate to node halos
1620
- - the chrome strip's "data tier" sub-role becomes a structural mirror
1621
- of the title-block tier in the triple-axis context
1399
+ [data-theme="light"] {
1400
+ --bg: #f6f7f9;
1401
+ --bg-secondary: #ffffff;
1402
+ --bg-elevated: #eef0f4;
1403
+ --fg: #0f1419;
1404
+ --fg-muted: #525a66;
1405
+ --fg-dim: #8a94a3;
1406
+ --border: #e3e6eb;
1407
+ --border-hover: #c8cdd6;
1408
+ --accent: #009e7e;
1409
+ --accent-fg: #ffffff;
1410
+ --success: #059669;
1411
+ --warning: #d97706;
1412
+ --danger: #dc2626;
1413
+ --info: #0284c7;
1414
+ --scrollbar: #d4d8df;
1415
+ --scrollbar-hover: #a1a8b5;
1416
+ --code-bg: #eef0f4;
1417
+ color-scheme: light;
1418
+ }
1622
1419
 
1623
- Glow tuning vs prior 3 triple-axis members:
1624
- R721 kicker blur 6 px, alpha 0.30 @ 6 s (title-block)
1625
- R722 watermark blur 8 px, alpha 0.20 @ 6 s (canvas-brand)
1626
- R725 H2 blur 8 px, alpha 0.22 @ 10 s (title-block)
1627
- R727 zoom-level blur 5 px, alpha 0.25 @ 9 s (data tier) ← this round
1628
- Smaller blur (5 px) for the smaller pill surface; mid-range alpha
1629
- (0.25) between kicker (sharp+bright at 0.30) and H2 (medium at 0.22).
1630
- At 9 s the glow appearance interval is ~4.5 s on each half — between
1631
- the 3 s 6 s-pair peak and the 5 s H2 peak, matching the cadence
1632
- interpolation.
1420
+ /* ────────────────────────────────────────────────────
1421
+ Compatibility shims gradually replace hard-coded
1422
+ #0b0b0d / zinc-9xx / etc. in components with var()
1423
+ over time. These rules let the existing components
1424
+ pick up the active theme even before they are
1425
+ migrated.
1426
+ ──────────────────────────────────────────────────── */
1633
1427
 
1634
- Hover gate preserved: the existing R703 [data-topo-chrome-zoom-level-
1635
- hover="true"] selector still gates the entire animation off when user
1636
- interacts — R727's text-shadow is part of the same @keyframes block
1637
- so it gets gated together. */
1638
- @keyframes anet-topo-chrome-zoom-level-breath-kf {
1639
- 0%, 100% { opacity: 1; transform: scale(1); text-shadow: none; }
1640
- 50% { opacity: 0.85; transform: scale(0.996); text-shadow: 0 0 5px rgba(34, 211, 238, 0.25); }
1428
+ /* page-level dark backgrounds use --bg */
1429
+ [data-theme="light"] .bg-\[\#060607\],
1430
+ [data-theme="light"] .bg-\[\#0a0a0c\],
1431
+ [data-theme="light"] .bg-\[\#0e0e10\],
1432
+ [data-theme="light"] .bg-\[\#0e0e10\],
1433
+ [data-theme="light"] .bg-\[\#0b0b0d\],
1434
+ [data-theme="light"] .bg-\[\#111113\],
1435
+ [data-theme="light"] .bg-\[\#161618\],
1436
+ [data-theme="light"] .bg-\[\#161618\],
1437
+ [data-theme="light"] .bg-\[\#1f1f24\],
1438
+ [data-theme="light"] .bg-\[\#1f1f24\],
1439
+ [data-theme="mint"] .bg-\[\#060607\],
1440
+ [data-theme="mint"] .bg-\[\#0a0a0c\],
1441
+ [data-theme="mint"] .bg-\[\#0e0e10\],
1442
+ [data-theme="mint"] .bg-\[\#0e0e10\],
1443
+ [data-theme="mint"] .bg-\[\#0b0b0d\],
1444
+ [data-theme="mint"] .bg-\[\#111113\],
1445
+ [data-theme="mint"] .bg-\[\#161618\],
1446
+ [data-theme="mint"] .bg-\[\#161618\],
1447
+ [data-theme="mint"] .bg-\[\#1f1f24\],
1448
+ [data-theme="mint"] .bg-\[\#1f1f24\] {
1449
+ background-color: var(--bg) !important;
1641
1450
  }
1642
- .anet-topo-chrome-zoom-level-breath {
1643
- animation: anet-topo-chrome-zoom-level-breath-kf 9s ease-in-out infinite;
1451
+
1452
+ /* card / elevated backgrounds (incl. opacity variants) */
1453
+ [data-theme="light"] .bg-\[\#161618\],
1454
+ [data-theme="light"] .bg-\[\#161618\]\/80,
1455
+ [data-theme="light"] .bg-\[\#1c1c1f\],
1456
+ [data-theme="light"] .bg-\[\#232327\],
1457
+ [data-theme="mint"] .bg-\[\#161618\],
1458
+ [data-theme="mint"] .bg-\[\#161618\]\/80,
1459
+ [data-theme="mint"] .bg-\[\#1c1c1f\],
1460
+ [data-theme="mint"] .bg-\[\#232327\] {
1461
+ background-color: var(--bg-secondary) !important;
1644
1462
  }
1645
- .anet-topo-chrome-zoom-level-breath[data-topo-chrome-zoom-level-hover="true"] {
1646
- animation: none;
1463
+
1464
+ /* hover / input field backgrounds */
1465
+ [data-theme="light"] .bg-\[\#26262b\],
1466
+ [data-theme="mint"] .bg-\[\#26262b\] {
1467
+ background-color: var(--bg-elevated) !important;
1647
1468
  }
1648
- @media (prefers-reduced-motion: reduce) {
1649
- .anet-topo-chrome-zoom-level-breath { animation: none; }
1469
+
1470
+ /* hardcoded borders */
1471
+ [data-theme="light"] .border-\[\#161618\],
1472
+ [data-theme="light"] .border-\[\#1c1c1f\],
1473
+ [data-theme="light"] .border-\[\#26262b\],
1474
+ [data-theme="light"] .border-\[\#3a3a41\],
1475
+ [data-theme="mint"] .border-\[\#161618\],
1476
+ [data-theme="mint"] .border-\[\#1c1c1f\],
1477
+ [data-theme="mint"] .border-\[\#26262b\],
1478
+ [data-theme="mint"] .border-\[\#3a3a41\] {
1479
+ border-color: var(--border) !important;
1650
1480
  }
1651
1481
 
1652
- /* Round 704 brand watermark WRAPPER envelope breath at 15 s — slowest
1653
- tier in the respiratory rolodex. Inner "sleep2agi" text already
1654
- breathes at 6 s (R519); this adds an outer wrapper envelope so the
1655
- brand area has nested respiration (fast inner / slow outer). 0.85 ↔ 1
1656
- alpha range (~15%) matches the R703 tabular/decorative surface band
1657
- heavy visual register shouldn't drift too far from full opacity.
1482
+ /* dark-tuned gradient buttons flat brand color in light themes */
1483
+ [data-theme="light"] [class*="from-cyan-"][class*="to-blue-"],
1484
+ [data-theme="light"] [class*="from-cyan-"][class*="to-emerald-"],
1485
+ [data-theme="light"] [class*="from-emerald-"][class*="to-cyan-"],
1486
+ [data-theme="light"] [class*="from-blue-"][class*="to-cyan-"] {
1487
+ background: var(--accent) !important;
1488
+ color: var(--accent-fg) !important;
1489
+ }
1658
1490
 
1659
- Focal-recede gate via data-attr selector — when the wrapper's
1660
- data-topo-brand-watermark-recede attr flips to "true" (user is
1661
- hovering a node/edge/group on the canvas), animation: none gates
1662
- off so the R525 inline opacity={0.7} attr takes effect cleanly.
1663
- Pattern sibling to R703 zoom-level data-attr selector gate.
1664
- prefers-reduced-motion: reduce also neutralizes. */
1665
- @keyframes anet-topo-brand-watermark-envelope-breath-kf {
1666
- 0%, 100% { opacity: 1; }
1667
- 50% { opacity: 0.85; }
1491
+ [data-theme="light"] [class*="from-cyan-"][class*="to-blue-"]:hover,
1492
+ [data-theme="light"] [class*="from-cyan-"][class*="to-emerald-"]:hover,
1493
+ [data-theme="light"] [class*="from-emerald-"][class*="to-cyan-"]:hover,
1494
+ [data-theme="light"] [class*="from-blue-"][class*="to-cyan-"]:hover {
1495
+ background: #00b894 !important;
1496
+ filter: brightness(1.05);
1668
1497
  }
1669
- .anet-topo-brand-watermark-envelope-breath {
1670
- animation: anet-topo-brand-watermark-envelope-breath-kf 15s ease-in-out infinite;
1498
+
1499
+ [data-theme="light"] .text-gray-100,
1500
+ [data-theme="light"] .text-gray-200,
1501
+ [data-theme="light"] .text-gray-300,
1502
+ [data-theme="light"] .text-gray-100\/90,
1503
+ [data-theme="mint"] .text-gray-100,
1504
+ [data-theme="mint"] .text-gray-200,
1505
+ [data-theme="mint"] .text-gray-300,
1506
+ [data-theme="mint"] .text-gray-100\/90 {
1507
+ color: var(--fg) !important;
1671
1508
  }
1672
- .anet-topo-brand-watermark-envelope-breath[data-topo-brand-watermark-recede="true"] {
1673
- animation: none;
1509
+
1510
+ [data-theme="light"] .text-gray-400,
1511
+ [data-theme="light"] .text-gray-500,
1512
+ [data-theme="light"] .text-gray-600,
1513
+ [data-theme="light"] .placeholder-gray-600::placeholder,
1514
+ [data-theme="mint"] .text-gray-400,
1515
+ [data-theme="mint"] .text-gray-500,
1516
+ [data-theme="mint"] .text-gray-600,
1517
+ [data-theme="mint"] .placeholder-gray-600::placeholder {
1518
+ color: var(--fg-muted) !important;
1674
1519
  }
1675
- @media (prefers-reduced-motion: reduce) {
1676
- .anet-topo-brand-watermark-envelope-breath { animation: none; }
1520
+
1521
+ [data-theme="light"] .text-white,
1522
+ [data-theme="mint"] .text-white {
1523
+ color: var(--fg) !important;
1677
1524
  }
1678
1525
 
1679
- /* Round 705 — canvas crescent wrapper envelope breath at 13 s. Mirror
1680
- to R704 watermark wrapper, closing canvas-brand-pair envelope
1681
- symmetry. Cadence 13 s coprime with R704 (15 s) so the two never
1682
- beat together when both visible. Tighter alpha range (0.30 ↔ 0.35,
1683
- ~14%) deliberately hugs the existing inline 0.35 baseline — preserves
1684
- the visible-state intent while adding nested respiration.
1526
+ [data-theme="light"] .text-zinc-100,
1527
+ [data-theme="light"] .text-zinc-200,
1528
+ [data-theme="light"] .text-white,
1529
+ [data-theme="mint"] .text-zinc-100,
1530
+ [data-theme="mint"] .text-zinc-200,
1531
+ [data-theme="mint"] .text-white {
1532
+ color: var(--fg) !important;
1533
+ }
1685
1534
 
1686
- Two gate-off rules instead of one (R704 had a single recede gate):
1687
- - visible="false" → animation: none, so inline opacity={0} holds when
1688
- the recent panel is shown (crescent must be invisible).
1689
- - recede="true" animation: none, so inline opacity={0.245} holds
1690
- when canvas attention is elsewhere.
1691
- Either gate flips → CSS releases control to inline opacity attr.
1692
- prefers-reduced-motion: reduce also neutralizes. */
1693
- @keyframes anet-topo-brand-canvas-mark-envelope-breath-kf {
1694
- 0%, 100% { opacity: 0.35; }
1695
- 50% { opacity: 0.30; }
1535
+ [data-theme="light"] .text-zinc-400,
1536
+ [data-theme="light"] .text-zinc-500,
1537
+ [data-theme="mint"] .text-zinc-400,
1538
+ [data-theme="mint"] .text-zinc-500 {
1539
+ color: var(--fg-muted) !important;
1696
1540
  }
1697
- .anet-topo-brand-canvas-mark-envelope-breath {
1698
- animation: anet-topo-brand-canvas-mark-envelope-breath-kf 13s ease-in-out infinite;
1541
+
1542
+ [data-theme="light"] .bg-zinc-900,
1543
+ [data-theme="light"] .bg-zinc-950,
1544
+ [data-theme="light"] .bg-black,
1545
+ [data-theme="mint"] .bg-zinc-900,
1546
+ [data-theme="mint"] .bg-zinc-950,
1547
+ [data-theme="mint"] .bg-black {
1548
+ background-color: var(--bg-secondary) !important;
1699
1549
  }
1700
- .anet-topo-brand-canvas-mark-envelope-breath[data-topo-brand-canvas-mark-visible="false"] {
1701
- animation: none;
1550
+
1551
+ [data-theme="light"] .bg-zinc-800,
1552
+ [data-theme="mint"] .bg-zinc-800 {
1553
+ background-color: var(--bg-elevated) !important;
1702
1554
  }
1703
- .anet-topo-brand-canvas-mark-envelope-breath[data-topo-brand-canvas-mark-recede="true"] {
1704
- animation: none;
1555
+
1556
+ [data-theme="light"] .border-zinc-700,
1557
+ [data-theme="light"] .border-zinc-800,
1558
+ [data-theme="light"] .border-white\/10,
1559
+ [data-theme="light"] .border-white\/5,
1560
+ [data-theme="mint"] .border-zinc-700,
1561
+ [data-theme="mint"] .border-zinc-800,
1562
+ [data-theme="mint"] .border-white\/10,
1563
+ [data-theme="mint"] .border-white\/5 {
1564
+ border-color: var(--border) !important;
1705
1565
  }
1706
- @media (prefers-reduced-motion: reduce) {
1707
- .anet-topo-brand-canvas-mark-envelope-breath { animation: none; }
1566
+
1567
+ /* Light theme: additional zinc shades + accent colors override */
1568
+ [data-theme="light"] .bg-zinc-700,
1569
+ [data-theme="light"] .bg-zinc-600,
1570
+ [data-theme="mint"] .bg-zinc-700,
1571
+ [data-theme="mint"] .bg-zinc-600 {
1572
+ background-color: var(--bg-elevated) !important;
1708
1573
  }
1709
1574
 
1710
- /* Round 706 — title-block wrapper envelope breath at 11s. Mirrors the
1711
- canvas-brand-pair nested-envelope pattern (R704/R705) at the title-
1712
- block scope. 11s coprime with all 3 inner trio cadences (5, 6, 10) —
1713
- no common factor → no phase lock with inner anchors. Tighter alpha
1714
- range (0.92 ↔ 1, ~8%) than canvas-brand-pair envelopes because the
1715
- title-block carries primary text identity; even subtle dimming on
1716
- the H2 + kicker headline reads as flicker if too aggressive. 8%
1717
- range = "barely-perceptible aliveness". prefers-reduced-motion:
1718
- reduce neutralizes. */
1719
- @keyframes anet-topo-title-block-envelope-breath-kf {
1720
- 0%, 100% { opacity: 1; }
1721
- 50% { opacity: 0.92; }
1575
+ [data-theme="light"] .text-zinc-300,
1576
+ [data-theme="light"] .text-zinc-600,
1577
+ [data-theme="mint"] .text-zinc-300,
1578
+ [data-theme="mint"] .text-zinc-600 {
1579
+ color: var(--fg-muted) !important;
1722
1580
  }
1723
- .anet-topo-title-block-envelope-breath {
1724
- animation: anet-topo-title-block-envelope-breath-kf 11s ease-in-out infinite;
1581
+
1582
+ [data-theme="light"] .text-zinc-700,
1583
+ [data-theme="light"] .text-zinc-800,
1584
+ [data-theme="mint"] .text-zinc-700,
1585
+ [data-theme="mint"] .text-zinc-800 {
1586
+ color: var(--fg) !important;
1725
1587
  }
1726
- @media (prefers-reduced-motion: reduce) {
1727
- .anet-topo-title-block-envelope-breath { animation: none; }
1588
+
1589
+ /* Light theme: emerald / cyan / indigo accents → unified brand emerald */
1590
+ [data-theme="light"] .text-emerald-400,
1591
+ [data-theme="light"] .text-emerald-300,
1592
+ [data-theme="light"] .text-cyan-400,
1593
+ [data-theme="light"] .text-cyan-300,
1594
+ [data-theme="light"] .text-indigo-400 {
1595
+ color: var(--accent) !important;
1728
1596
  }
1729
1597
 
1730
- /* Round 742 title-block entrance, 2nd member of the entrance family.
1731
- Staggered choreography with R740 canvas entrance:
1732
- R740 canvas 0 600 ms (scale 0.99 1, opacity 0.8 1)
1733
- R742 title-block 200 700 ms (translateY -4px 0, opacity 0.7 → 1)
1734
- The 100ms gap (600 700 ms) between canvas settle and title-block
1735
- settle creates a perceived "shake out" — canvas arrives, then the
1736
- heading writes itself into place. Both done by ~700 ms.
1598
+ /* Light theme: every "-300/400" accent text color needs a darker mid-tone
1599
+ variant. The default Tailwind `-300/-400` shades are designed for dark
1600
+ backgrounds putting them on white gives "barely visible" text (the
1601
+ problem Vincent flagged: "浅色下很多字体看不见"). Map each family to its
1602
+ `-700` equivalent so contrast against white card is AA. */
1603
+ [data-theme="light"] .text-green-400,
1604
+ [data-theme="light"] .text-green-300,
1605
+ [data-theme="mint"] .text-green-400,
1606
+ [data-theme="mint"] .text-green-300 { color: #047857 !important; }
1737
1607
 
1738
- CSS cascade detail: when an element has BOTH .anet-topo-title-block-
1739
- envelope-breath (R706, infinite 11s breath) AND .anet-topo-title-
1740
- block-entrance (R742, one-shot 500ms), the `animation` property
1741
- doesn't merge — only one wins. So R742 declares a COMPOUND animation
1742
- listing BOTH (entrance first as one-shot, breath second as the
1743
- ongoing infinite). Placed AFTER the R706 envelope-breath block in
1744
- source order so cascade gives R742 the win when both classes are
1745
- applied. When ONLY envelope-breath is applied (reducedMotion strips
1746
- the entrance class via JSX), R706 rule takes over with just the
1747
- breath animation. */
1748
- @keyframes anet-topo-title-block-entrance-kf {
1749
- 0% { transform: translateY(-4px); opacity: 0.7; }
1750
- 100% { transform: translateY(0); opacity: 1; }
1751
- }
1752
- .anet-topo-title-block-entrance {
1753
- animation:
1754
- anet-topo-title-block-entrance-kf 500ms ease-out 200ms backwards,
1755
- anet-topo-title-block-envelope-breath-kf 11s ease-in-out infinite;
1756
- }
1757
- @media (prefers-reduced-motion: reduce) {
1758
- .anet-topo-title-block-entrance { animation: none; }
1759
- }
1608
+ [data-theme="light"] .text-yellow-400,
1609
+ [data-theme="light"] .text-yellow-300,
1610
+ [data-theme="mint"] .text-yellow-400,
1611
+ [data-theme="mint"] .text-yellow-300 { color: #92400e !important; }
1612
+
1613
+ [data-theme="light"] .text-amber-400,
1614
+ [data-theme="light"] .text-amber-300,
1615
+ [data-theme="mint"] .text-amber-400,
1616
+ [data-theme="mint"] .text-amber-300 { color: #92400e !important; }
1617
+
1618
+ [data-theme="light"] .text-red-400,
1619
+ [data-theme="light"] .text-red-300,
1620
+ [data-theme="mint"] .text-red-400,
1621
+ [data-theme="mint"] .text-red-300 { color: #b91c1c !important; }
1622
+
1623
+ [data-theme="light"] .text-purple-400,
1624
+ [data-theme="light"] .text-purple-300,
1625
+ [data-theme="mint"] .text-purple-400,
1626
+ [data-theme="mint"] .text-purple-300 { color: #6d28d9 !important; }
1627
+
1628
+ [data-theme="light"] .text-orange-400,
1629
+ [data-theme="light"] .text-orange-300,
1630
+ [data-theme="mint"] .text-orange-400,
1631
+ [data-theme="mint"] .text-orange-300 { color: #c2410c !important; }
1632
+
1633
+ [data-theme="light"] .text-blue-400,
1634
+ [data-theme="light"] .text-blue-300,
1635
+ [data-theme="mint"] .text-blue-400,
1636
+ [data-theme="mint"] .text-blue-300 { color: #1d4ed8 !important; }
1637
+
1638
+ [data-theme="light"] .text-pink-400,
1639
+ [data-theme="light"] .text-pink-300,
1640
+ [data-theme="mint"] .text-pink-400,
1641
+ [data-theme="mint"] .text-pink-300 { color: #be185d !important; }
1760
1642
 
1761
- /* Round 743 — chrome strip entrance, 3rd member of the entrance family,
1762
- completing the entrance TRIO. Three-stage cascade:
1763
- R740 canvas 0 → 600 ms scale 0.99→1, opacity 0.8→1
1764
- R742 title-block 200 700 ms translateY -4→0, opacity 0.7→1
1765
- R743 chrome strip 400 → 900 ms translateX 8→0, opacity 0.7→1
1766
- Each stage offset +200 ms. translateX (not translateY) because the
1767
- chrome strip docks at the canvas right edge — sliding in from its
1768
- own edge reads as "docking into place".
1643
+ [data-theme="light"] .text-violet-400,
1644
+ [data-theme="light"] .text-violet-300,
1645
+ [data-theme="mint"] .text-violet-400,
1646
+ [data-theme="mint"] .text-violet-300 { color: #6d28d9 !important; }
1769
1647
 
1770
- The chrome strip wrapper div has no other animation, so a plain
1771
- single-animation class works here (no compound needed like R742's
1772
- title-block which collided with R706 envelope-breath). One-shot —
1773
- no `infinite`, runs once per mount. prefers-reduced-motion JSX gate
1774
- + CSS @media guard. */
1775
- @keyframes anet-topo-chrome-strip-entrance-kf {
1776
- 0% { transform: translateX(8px); opacity: 0.7; }
1777
- 100% { transform: translateX(0); opacity: 1; }
1648
+ [data-theme="light"] .text-teal-400,
1649
+ [data-theme="light"] .text-teal-300,
1650
+ [data-theme="mint"] .text-teal-400,
1651
+ [data-theme="mint"] .text-teal-300 { color: #0f766e !important; }
1652
+
1653
+ /* Light theme: text-gray-700 (used in footers / muted body) is too pale
1654
+ when sitting on slightly-grey page bg. Bump to fg-muted. */
1655
+ [data-theme="light"] .text-gray-700,
1656
+ [data-theme="mint"] .text-gray-700 { color: var(--fg-muted) !important; }
1657
+
1658
+ [data-theme="light"] .bg-emerald-500\/10,
1659
+ [data-theme="light"] .bg-emerald-500\/20,
1660
+ [data-theme="light"] .bg-cyan-500\/10,
1661
+ [data-theme="light"] .bg-cyan-500\/20 {
1662
+ background-color: rgba(0, 158, 126, 0.08) !important;
1778
1663
  }
1779
- .anet-topo-chrome-strip-entrance {
1780
- animation: anet-topo-chrome-strip-entrance-kf 500ms ease-out 400ms backwards;
1664
+
1665
+ [data-theme="light"] .border-emerald-500\/30,
1666
+ [data-theme="light"] .border-emerald-500\/40,
1667
+ [data-theme="light"] .border-cyan-500\/30 {
1668
+ border-color: rgba(0, 158, 126, 0.32) !important;
1781
1669
  }
1782
- @media (prefers-reduced-motion: reduce) {
1783
- .anet-topo-chrome-strip-entrance { animation: none; }
1670
+
1671
+ /* Light theme: card-like containers get a subtle elevation shadow
1672
+ (cards in dark mode lean on glow; in light mode they need a real shadow
1673
+ to read as a layer rather than blending into the page background) */
1674
+ [data-theme="light"] .bg-zinc-900,
1675
+ [data-theme="light"] .bg-zinc-950,
1676
+ [data-theme="light"] .bg-\[\#0b0b0d\],
1677
+ [data-theme="light"] .bg-\[\#161618\],
1678
+ [data-theme="light"] .bg-\[\#1f1f24\] {
1679
+ box-shadow: 0 1px 0 rgba(15, 23, 42, 0.04), 0 6px 18px -8px rgba(15, 23, 42, 0.08);
1784
1680
  }
1785
1681
 
1786
- /* Round 707 — chrome Layout wrapper at-rest breath at 17 s. Slowest tier
1787
- among HTML chrome respiratory anchors (sibling to R703 zoom-level 9 s
1788
- in the chrome strip's data tier; this 17 s lands in the chrome strip's
1789
- control tier). 17 is prime → coprime with every other cadence in the
1790
- rolodex (3/4/5/6/7/8/9/10/11/13/15). Never phase-locks with any other
1791
- anchor.
1682
+ [data-theme="light"] .bg-zinc-800 {
1683
+ box-shadow: inset 0 0 0 1px var(--border);
1684
+ }
1792
1685
 
1793
- Tightest alpha range yet (0.94 1, ~6%) the Layout buttons are
1794
- interactive controls with active-state cyan tint; even ~10% dimming
1795
- on the inner button color would feel jarring. 6% = "barely-perceptible
1796
- aliveness" on a control group.
1686
+ /* Light theme: kill any leftover ring / shadow-glow that was tuned for dark bg */
1687
+ [data-theme="light"] [class*="shadow-emerald"],
1688
+ [data-theme="light"] [class*="shadow-cyan"],
1689
+ [data-theme="light"] [class*="shadow-indigo"] {
1690
+ --tw-shadow-color: rgba(0, 158, 126, 0.18);
1691
+ }
1797
1692
 
1798
- Hover gate via :has() selector when user hovers any inner Ring/Grid
1799
- button, the R697 wrapper-level halo (cyan filter) takes precedence;
1800
- animation: none on hover pauses the breath so the halo reads crisp
1801
- without underlying alpha drift. Third gate-off mechanism in the family
1802
- vocabulary, sibling to R703 hover data-attr gate + R704/R705 recede
1803
- gates. */
1804
- @keyframes anet-topo-chrome-layout-trailer-breath-kf {
1805
- 0%, 100% { opacity: 1; }
1806
- 50% { opacity: 0.94; }
1693
+ /* Solid-color action buttons (bg-blue-600 / bg-cyan-600 / bg-green-600 / …)
1694
+ stay dark navy in light themes because the gradient-button shim above only
1695
+ matches `from-…-to-…` gradient buttons. Map every primary solid background
1696
+ to the active accent so Broadcast / Send Task / Register / Change Password
1697
+ etc. read as the brand color instead of black-on-white. */
1698
+ [data-theme="light"] .bg-blue-600,
1699
+ [data-theme="light"] .bg-blue-500,
1700
+ [data-theme="light"] .bg-cyan-600,
1701
+ [data-theme="light"] .bg-cyan-500,
1702
+ [data-theme="light"] .bg-emerald-600,
1703
+ [data-theme="light"] .bg-emerald-500,
1704
+ [data-theme="light"] .bg-green-600,
1705
+ [data-theme="light"] .bg-green-500,
1706
+ [data-theme="light"] .bg-indigo-600,
1707
+ [data-theme="light"] .bg-indigo-500,
1708
+ [data-theme="light"] .bg-purple-600,
1709
+ [data-theme="light"] .bg-purple-500,
1710
+ [data-theme="mint"] .bg-blue-600,
1711
+ [data-theme="mint"] .bg-blue-500,
1712
+ [data-theme="mint"] .bg-cyan-600,
1713
+ [data-theme="mint"] .bg-cyan-500,
1714
+ [data-theme="mint"] .bg-emerald-600,
1715
+ [data-theme="mint"] .bg-emerald-500,
1716
+ [data-theme="mint"] .bg-green-600,
1717
+ [data-theme="mint"] .bg-green-500,
1718
+ [data-theme="mint"] .bg-indigo-600,
1719
+ [data-theme="mint"] .bg-indigo-500,
1720
+ [data-theme="mint"] .bg-purple-600,
1721
+ [data-theme="mint"] .bg-purple-500 {
1722
+ background-color: var(--accent) !important;
1723
+ color: var(--accent-fg) !important;
1807
1724
  }
1808
- .anet-topo-chrome-layout-trailer-breath {
1809
- animation: anet-topo-chrome-layout-trailer-breath-kf 17s ease-in-out infinite;
1725
+
1726
+ [data-theme="light"] .hover\:bg-blue-500:hover,
1727
+ [data-theme="light"] .hover\:bg-cyan-500:hover,
1728
+ [data-theme="light"] .hover\:bg-emerald-500:hover,
1729
+ [data-theme="light"] .hover\:bg-green-500:hover,
1730
+ [data-theme="light"] .hover\:bg-indigo-500:hover,
1731
+ [data-theme="light"] .hover\:bg-purple-500:hover,
1732
+ [data-theme="mint"] .hover\:bg-blue-500:hover,
1733
+ [data-theme="mint"] .hover\:bg-cyan-500:hover,
1734
+ [data-theme="mint"] .hover\:bg-emerald-500:hover,
1735
+ [data-theme="mint"] .hover\:bg-green-500:hover,
1736
+ [data-theme="mint"] .hover\:bg-indigo-500:hover,
1737
+ [data-theme="mint"] .hover\:bg-purple-500:hover {
1738
+ background-color: #00b894 !important;
1739
+ filter: brightness(1.05);
1810
1740
  }
1811
- .anet-topo-chrome-layout-trailer-breath:has(button:hover) {
1812
- animation: none;
1741
+
1742
+ /* Disabled state for the same family — keep contrast against the page bg
1743
+ so disabled buttons don't read as "dark and active" in light mode */
1744
+ [data-theme="light"] .disabled\:bg-gray-800:disabled,
1745
+ [data-theme="light"] .disabled\:bg-zinc-800:disabled,
1746
+ [data-theme="mint"] .disabled\:bg-gray-800:disabled,
1747
+ [data-theme="mint"] .disabled\:bg-zinc-800:disabled {
1748
+ background-color: var(--bg-elevated) !important;
1749
+ color: var(--fg-muted) !important;
1813
1750
  }
1814
- @media (prefers-reduced-motion: reduce) {
1815
- .anet-topo-chrome-layout-trailer-breath { animation: none; }
1751
+
1752
+ /* ─────────────────────────────────────────────────────────────────────
1753
+ v0.4.3 light-theme polish — Phase 3 P0 fixes
1754
+ ─────────────────────────────────────────────────────────────────── */
1755
+
1756
+ /* P0-1: Sidebar gets its own surface in light themes (not page-colored).
1757
+ In Cyber (dark) the sidebar already feels separate via depth; in
1758
+ light/mint the page bg == sidebar bg so only the right border separates
1759
+ them. Switch sidebar to bg-secondary (white) + a hairline shadow. */
1760
+ [data-theme="light"] aside[data-anet-sidebar="true"],
1761
+ [data-theme="mint"] aside[data-anet-sidebar="true"] {
1762
+ background-color: var(--bg-secondary) !important;
1763
+ /* Right edge: solid hairline border + a soft 16px halo so the sidebar
1764
+ reads as a card hovering over the page, not "part of the page". */
1765
+ box-shadow:
1766
+ 1px 0 0 var(--border),
1767
+ 8px 0 20px -10px rgba(15, 23, 42, 0.10),
1768
+ 0 0 0 1px rgba(15, 23, 42, 0.02);
1769
+ border-right-color: transparent !important;
1816
1770
  }
1817
1771
 
1818
- /* Round 708 chrome nodeSize wrapper at-rest breath at 19 s. Sibling
1819
- to R707 Layout 17 s — both chrome control-tier wrappers carry the
1820
- same alpha range (6%) + the same :has(button:hover) gate. Two coprime
1821
- prime cadences (17, 19) form a chrome-control respiratory pair,
1822
- pattern-parallel to canvas-brand-pair (R704/R705) coprime envelope
1823
- pair. */
1824
- @keyframes anet-topo-chrome-fleet-group-trailer-breath-kf {
1825
- 0%, 100% { opacity: 1; }
1826
- 50% { opacity: 0.94; }
1772
+ /* Also tint page bg slightly cooler so the white sidebar pops more */
1773
+ [data-theme="light"] {
1774
+ --bg: #f3f5f8; /* was #f6f7f9 bumped 3pt cooler for sidebar contrast */
1827
1775
  }
1828
- .anet-topo-chrome-fleet-group-trailer-breath {
1829
- animation: anet-topo-chrome-fleet-group-trailer-breath-kf 19s ease-in-out infinite;
1776
+
1777
+ /* P0-2: Active nav — collapse the 4-emerald stack (bg + border + text + ring)
1778
+ to a single accent cue: 3px left bar + medium-weight text. Dark themes
1779
+ keep the existing tinted-pill look. */
1780
+ [data-theme="light"] .anet-nav-active,
1781
+ [data-theme="mint"] .anet-nav-active {
1782
+ /* 通信龙 final spec (Q1 + 3-piece lockdown): active nav = ONE cue.
1783
+ 4px left bar emerald, text in --fg (NOT emerald), NO bg tint.
1784
+ Reads as a calm "current page" indicator without competing emerald
1785
+ surfaces. (Earlier 0.4.3 had 3px + faint tint + emerald text.) */
1786
+ background-color: transparent !important;
1787
+ border-color: transparent !important;
1788
+ border-left: 4px solid var(--accent) !important;
1789
+ border-radius: 0 8px 8px 0 !important;
1790
+ padding-left: calc(0.75rem - 4px) !important;
1791
+ color: var(--fg) !important;
1792
+ font-weight: 600;
1830
1793
  }
1831
- .anet-topo-chrome-fleet-group-trailer-breath:has(button:hover) {
1832
- animation: none;
1794
+
1795
+ [data-theme="light"] .anet-nav-active:hover,
1796
+ [data-theme="mint"] .anet-nav-active:hover {
1797
+ background-color: var(--bg-elevated) !important;
1833
1798
  }
1834
- @media (prefers-reduced-motion: reduce) {
1835
- .anet-topo-chrome-fleet-group-trailer-breath { animation: none; }
1799
+
1800
+ /* When sidebar is collapsed (w-16, justify-center) the left bar would look
1801
+ weird against a centered icon — switch to a left-bg accent instead. */
1802
+ [data-theme="light"] aside.w-16 .anet-nav-active,
1803
+ [data-theme="mint"] aside.w-16 .anet-nav-active {
1804
+ border-radius: 8px !important;
1805
+ padding-left: 0 !important;
1806
+ border-left: 0 !important;
1807
+ background-color: var(--bg-elevated) !important;
1808
+ color: var(--accent) !important;
1836
1809
  }
1837
1810
 
1838
- /* Round 709 chrome zoom wrapper at-rest breath at 23 s. Closes the
1839
- chrome control trio (R707 Layout 17s + R708 nodeSize 19s + R709 zoom
1840
- 23s) with three adjacent prime coprime cadences. 23s slowest in the
1841
- chrome trio reads as "calm anchor for the viewport-control cluster"
1842
- since zoom is the most functional of the 3 wrappers (its inner buttons
1843
- compound viewport math). Same alpha (6%) + :has(button:hover) gate
1844
- as siblings. */
1845
- @keyframes anet-topo-chrome-zoom-wrapper-breath-kf {
1846
- 0%, 100% { opacity: 1; }
1847
- 50% { opacity: 0.94; }
1811
+ /* Q6: native <select> theming for light/mint.
1812
+ Without this, dropdowns on Nodes / Messages / Logs / Settings render with
1813
+ the OS default chrome (heavy chevron, off-spec font weight, no border
1814
+ radius). We strip the native chrome with appearance-none, then paint our
1815
+ own chevron via a background-image SVG. ~10 LOC. */
1816
+ [data-theme="light"] select,
1817
+ [data-theme="mint"] select {
1818
+ -webkit-appearance: none !important;
1819
+ -moz-appearance: none !important;
1820
+ appearance: none !important;
1821
+ background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%23525a66' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M5 8l5 5 5-5'/%3E%3C/svg%3E") !important;
1822
+ background-repeat: no-repeat !important;
1823
+ background-position: right 0.6rem center !important;
1824
+ background-size: 1rem 1rem !important;
1825
+ padding-right: 2rem !important;
1826
+ background-color: var(--bg-secondary) !important;
1827
+ border-radius: 8px;
1848
1828
  }
1849
- .anet-topo-chrome-zoom-wrapper-breath {
1850
- animation: anet-topo-chrome-zoom-wrapper-breath-kf 23s ease-in-out infinite;
1829
+
1830
+ [data-theme="light"] select:focus,
1831
+ [data-theme="mint"] select:focus {
1832
+ outline: 2px solid rgba(0, 158, 126, 0.2);
1833
+ outline-offset: -1px;
1834
+ border-color: var(--accent) !important;
1851
1835
  }
1852
- .anet-topo-chrome-zoom-wrapper-breath:has(button:hover) {
1853
- animation: none;
1836
+
1837
+ /* Mint reuses the same chevron — color picker would need its own gray, but
1838
+ accent is similar enough that the default chevron color reads OK. */
1839
+
1840
+ /* KPI stat cards in light mode: kill the gradient wash (which reads as
1841
+ pale-mud on white), replace with a 2px top accent strip per color
1842
+ family + real elevation shadow. Dark themes keep the gradient. */
1843
+ [data-theme="light"] .anet-stat-card,
1844
+ [data-theme="mint"] .anet-stat-card {
1845
+ border-top-width: 2px;
1846
+ box-shadow: 0 1px 0 rgba(15, 23, 42, 0.04), 0 4px 12px -8px rgba(15, 23, 42, 0.08);
1854
1847
  }
1855
- @media (prefers-reduced-motion: reduce) {
1856
- .anet-topo-chrome-zoom-wrapper-breath { animation: none; }
1848
+
1849
+ [data-theme="light"] .anet-stat-card:hover,
1850
+ [data-theme="mint"] .anet-stat-card:hover {
1851
+ box-shadow: 0 1px 0 rgba(15, 23, 42, 0.06), 0 12px 24px -12px rgba(15, 23, 42, 0.16);
1857
1852
  }
1858
1853
 
1859
- /* Round 718 chrome reset button joins the respiratory family at 21 s.
1860
- 5th anchor on the chrome-strip pattern, extending it from
1861
- "tiered-with-trio" → "tiered-with-quartet". The chrome strip now
1862
- spans a data tier + a prime quartet across control surfaces:
1863
- R703 zoom-level readout 9 s (data tier)
1864
- R707 Layout wrapper 17 s (control wrapper tier)
1865
- R708 nodeSize wrapper 19 s (control wrapper tier)
1866
- R718 reset button 21 s (atomic control tier) ← this round
1867
- R709 zoom wrapper 23 s (control wrapper tier)
1854
+ /* Top-strip colors per stat family */
1855
+ [data-theme="light"] .anet-stat-card[data-anet-stat-card="green"] { border-top-color: #10b981; }
1868
1856
 
1869
- 21 is composite (3 × 7), the ONE composite in the prime quartet —
1870
- slots between the prime wrappers 19 and 23 to fill the 17/19/21/23
1871
- odd-number ladder. Coprime with all rolodex cadences (the rolodex
1872
- has no 3-multiples until 9 and no 7-multiples until 7 itself — but
1873
- neither 9 nor 7 share factors with 21's other co-tier mates 17/19/23).
1874
- Actually 21 IS divisible by 3 like 9 — gcd(21,9)=3 so 21 phase-locks
1875
- with 9 every 63 s. That's deliberate: the data tier (9 s) and the
1876
- atomic-control tier (21 s) RE-ALIGN every minute — a slow handshake
1877
- between "what the chrome reads" and "the reset action". Aesthetic
1878
- over arithmetic: a 1-per-minute alignment is sub-perceptible.
1857
+ [data-theme="light"] .anet-stat-card[data-anet-stat-card="cyan"] { border-top-color: var(--accent); }
1879
1858
 
1880
- Atomic-control sub-tier rationale: the reset button is the only
1881
- STANDALONE atomic chrome button with a 5-axis hover signature
1882
- (R594 brightness + R400 hover-lift + R196 active-state + R350 icon
1883
- spin + R184 release pop). The 5 hover axes already give it a rich
1884
- interactive voice; a 6th REST axis at 7% alpha (slightly looser than
1885
- the 6% wrappers, to compensate for the smaller icon-button surface)
1886
- marks it as "alive" at rest without competing with the hover axes.
1859
+ [data-theme="light"] .anet-stat-card[data-anet-stat-card="gray"] { border-top-color: #94a3b8; }
1887
1860
 
1888
- Hover gate via data-attr (R703 pattern, not R707/R708/R709 :has()
1889
- pattern). The button itself exposes data-topo-chrome-reset-hover —
1890
- already plumbed for the R350 icon-rotate cue and R594 brightness
1891
- so the breath gates off naturally on its own hover state. Sibling
1892
- gate pattern to R703 zoom-level (the only other chrome anchor at
1893
- the leaf-element tier; both gate via their own hover data-attr
1894
- rather than parent :has()). */
1895
- @keyframes anet-topo-chrome-reset-breath-kf {
1896
- 0%, 100% { opacity: 1; }
1897
- 50% { opacity: 0.93; }
1861
+ [data-theme="light"] .anet-stat-card[data-anet-stat-card="white"] { border-top-color: #3b82f6; }
1862
+
1863
+ /* Drop the gradient wash in light themes looks like dust on white. */
1864
+ [data-theme="light"] .anet-stat-card > .pointer-events-none,
1865
+ [data-theme="mint"] .anet-stat-card > .pointer-events-none {
1866
+ display: none;
1898
1867
  }
1899
- .anet-topo-chrome-reset-breath {
1900
- animation: anet-topo-chrome-reset-breath-kf 21s ease-in-out infinite;
1868
+
1869
+ /* Generic card hover elevation — every white card on light bg gets a
1870
+ subtle lift on hover so clickability is obvious. */
1871
+ [data-theme="light"] .bg-\[\#161618\]:hover,
1872
+ [data-theme="mint"] .bg-\[\#161618\]:hover {
1873
+ box-shadow: 0 6px 18px -10px rgba(15, 23, 42, 0.12);
1901
1874
  }
1902
- .anet-topo-chrome-reset-breath[data-topo-chrome-reset-hover="true"] {
1903
- animation: none;
1875
+
1876
+ /* Task row hover bg (round 19) — dark hex would land on a white card and
1877
+ look like a stripe. Re-map to a faint accent tint per theme. */
1878
+ [data-theme="light"] .anet-task-row:hover {
1879
+ background-color: #f5f7fb !important;
1904
1880
  }
1905
- @media (prefers-reduced-motion: reduce) {
1906
- .anet-topo-chrome-reset-breath { animation: none; }
1881
+
1882
+ /* Open state slightly stronger fill so users can see "this one is the
1883
+ open row" while scrolling. */
1884
+ [data-theme="light"] .anet-task-row[aria-expanded="true"] {
1885
+ background-color: #fafbfd !important;
1886
+ box-shadow: 0 4px 14px -10px rgba(15, 23, 42, 0.14) !important;
1907
1887
  }
1908
1888
 
1909
- /* Round 719 — chrome fullscreen button joins the respiratory family at
1910
- 25 s. 6th anchor on the chrome-strip pattern, extending the shape
1911
- from "tiered-with-quartet" → "tiered-with-quintet". Sibling to R718
1912
- reset (21 s) closes the ATOMIC-CONTROL DUO: both standalone
1913
- chrome buttons (reset + fullscreen, the only non-segmented chrome
1914
- buttons in the family per R400 doc) now breathe at parity alpha
1915
- range with coprime cadences.
1889
+ [data-theme="light"] .anet-login-bg,
1890
+ [data-theme="mint"] .anet-login-bg {
1891
+ background-image:
1892
+ radial-gradient(circle at 50% 35%, rgba(0, 158, 126, 0.06), transparent 60%),
1893
+ radial-gradient(circle, rgba(15, 23, 42, 0.05) 1px, transparent 1px);
1894
+ background-size: 100% 100%, 24px 24px;
1895
+ }
1916
1896
 
1917
- Chrome-strip pattern post-R719:
1918
- R703 zoom-level readout 9 s (data tier)
1919
- R707 Layout wrapper 17 s (control wrapper tier)
1920
- R708 nodeSize wrapper 19 s (control wrapper tier)
1921
- R718 reset button 21 s (atomic control tier)
1922
- R709 zoom wrapper 23 s (control wrapper tier)
1923
- R719 fullscreen button 25 s (atomic control tier) ← this round
1897
+ [data-theme="light"] .anet-login-mark,
1898
+ [data-theme="mint"] .anet-login-mark {
1899
+ background: var(--bg-secondary) !important;
1900
+ box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06), 0 0 0 1px var(--border);
1901
+ }
1924
1902
 
1925
- 25 = coprime with every other rolodex cadence (no factor 5
1926
- anywhere). Fills the 17/19/21/23/25 odd-number ladder perfectly
1927
- and becomes the new slowest cadence on the rolodex (prior slowest:
1928
- 23 s zoom wrapper). The chrome strip now spans a 16 s arc from
1929
- 9 → 25, with its two atomic-control members at 21 s and 25 s
1930
- bracketing the wrapper trio (17/19/23) — atomic controls breathe
1931
- slower than wrappers, matching their role as terminal commits
1932
- (reset wipes state; fullscreen flips viewport mode).
1903
+ /* Health bannersticky thin strip above content, with theme-aware
1904
+ green/amber/red tints. Dark themes use the lower-opacity bg+border
1905
+ that come from the component classes directly. Light/mint need
1906
+ slightly more saturation so the tint reads on a white surface. */
1907
+ [data-theme="light"] .anet-health-banner.bg-emerald-500\/6,
1908
+ [data-theme="mint"] .anet-health-banner.bg-emerald-500\/6 {
1909
+ background-color: rgba(16, 185, 129, 0.08) !important;
1910
+ color: #047857 !important;
1911
+ border-color: rgba(16, 185, 129, 0.25) !important;
1912
+ }
1933
1913
 
1934
- Same 7% alpha (0.93 ↔ 1) as R718 reset — atomic-control tier
1935
- parity. The duo reads as a matched pair at rest. Hover gate via
1936
- data-topo-chrome-fullscreen-hover (R703 leaf-element pattern,
1937
- sibling to R718 reset's gate). */
1938
- @keyframes anet-topo-chrome-fullscreen-breath-kf {
1939
- 0%, 100% { opacity: 1; }
1940
- 50% { opacity: 0.93; }
1914
+ [data-theme="light"] .anet-health-banner.bg-amber-500\/8,
1915
+ [data-theme="mint"] .anet-health-banner.bg-amber-500\/8 {
1916
+ background-color: rgba(245, 158, 11, 0.12) !important;
1917
+ color: #92400e !important;
1918
+ border-color: rgba(245, 158, 11, 0.30) !important;
1941
1919
  }
1942
- .anet-topo-chrome-fullscreen-breath {
1943
- animation: anet-topo-chrome-fullscreen-breath-kf 25s ease-in-out infinite;
1920
+
1921
+ [data-theme="light"] .anet-health-banner.bg-red-500\/8,
1922
+ [data-theme="mint"] .anet-health-banner.bg-red-500\/8 {
1923
+ background-color: rgba(220, 38, 38, 0.10) !important;
1924
+ color: #b91c1c !important;
1925
+ border-color: rgba(220, 38, 38, 0.30) !important;
1944
1926
  }
1945
- .anet-topo-chrome-fullscreen-breath[data-topo-chrome-fullscreen-hover="true"] {
1946
- animation: none;
1927
+
1928
+ [data-theme="light"] .anet-skeleton-bar,
1929
+ [data-theme="mint"] .anet-skeleton-bar {
1930
+ background-color: #d4d8df;
1947
1931
  }
1948
- @media (prefers-reduced-motion: reduce) {
1949
- .anet-topo-chrome-fullscreen-breath { animation: none; }
1932
+
1933
+ [data-theme="light"] .anet-cmdk > div:not([aria-hidden]),
1934
+ [data-theme="mint"] .anet-cmdk > div:not([aria-hidden]) {
1935
+ background-color: var(--bg-secondary) !important;
1936
+ box-shadow: 0 10px 30px -8px rgba(15, 23, 42, 0.18),
1937
+ 0 2px 6px -2px rgba(15, 23, 42, 0.10);
1950
1938
  }
1951
1939
 
1952
- /* ─────────────────────────────────────────────────────────────────────
1953
- Round 29 — prefers-reduced-motion a11y sweep
1940
+ [data-theme="light"] .anet-cmdk-row.bg-cyan-500\/10,
1941
+ [data-theme="mint"] .anet-cmdk-row.bg-cyan-500\/10 {
1942
+ background-color: rgba(0, 158, 126, 0.08) !important;
1943
+ }
1954
1944
 
1955
- Rounds 19~27 accumulated a lot of small animations: Tasks row 0fr↔1fr
1956
- expand, chevron rotate, hover bg shifts, Cmd+K popover, TaskChatPanel
1957
- slide-in, brand/skeleton pulses already had their own guards.
1945
+ /* Light/mint: use the brand teal undertone matches the active row. */
1946
+ [data-theme="light"] .anet-cmdk-mark,
1947
+ [data-theme="mint"] .anet-cmdk-mark {
1948
+ color: #0d9488;
1949
+ }
1958
1950
 
1959
- For users who set system "reduce motion": neutralise everything in one
1960
- pass. Don't kill state changes outright — only their durations and
1961
- keyframes. Hover and focus colours still apply, just instantly.
1962
- ───────────────────────────────────────────────────────────────────── */
1963
- @media (prefers-reduced-motion: reduce) {
1964
- *,
1965
- *::before,
1966
- *::after {
1967
- animation-duration: 0.001ms !important;
1968
- animation-iteration-count: 1 !important;
1969
- transition-duration: 0.001ms !important;
1970
- scroll-behavior: auto !important;
1971
- }
1972
- /* Skeleton pulse already has its own guard; harden in case the bar
1973
- class is reused without anet-skeleton-pulse. */
1974
- .anet-skeleton-bar {
1975
- animation: none !important;
1976
- }
1977
- /* Brand pulse hidden — green/red dot still indicates state via color. */
1978
- .anet-brand-pulse {
1979
- animation: none !important;
1980
- }
1981
- /* Drawer entrance kills — keyframes promoted to globals in r30. */
1982
- .animate-slide-in,
1983
- .anet-fade-in,
1984
- .anet-current-step-pulse,
1985
- .anet-topo-sweep,
1986
- .anet-topo-spoke-flow,
1987
- .anet-topo-groupbox-live {
1988
- animation: none !important;
1989
- }
1990
- /* Tasks row grid 0fr↔1fr keep working (it's the toggle mechanism),
1991
- but the smooth transition just snaps. Already handled via the
1992
- blanket transition-duration override above. */
1951
+ [data-theme="light"] .anet-cmdk-row.bg-cyan-500\/10 .anet-cmdk-mark,
1952
+ [data-theme="mint"] .anet-cmdk-row.bg-cyan-500\/10 .anet-cmdk-mark {
1953
+ color: #064e3b;
1993
1954
  }
1994
1955
 
1995
- /* ────────────────────────────────────────────────────
1996
- #190 mobile polish — tap feel
1997
- ────────────────────────────────────────────────────
1998
- `touch-action: manipulation` removes the 300 ms tap-delay browsers
1999
- add to disambiguate double-tap-zoom, and (more importantly) keeps an
2000
- accidental double-tap on an interactive element from zooming the
2001
- page. Pinch-zoom on the rest of the page is unaffected. The hint is
2002
- a no-op on inputs without a touch driver, so zero desktop cost.
1956
+ /* Help overlay — same white-card-with-shadow treatment as cmdk on light */
1957
+ [data-theme="light"] .anet-help > div:not([aria-hidden]),
1958
+ [data-theme="mint"] .anet-help > div:not([aria-hidden]) {
1959
+ background-color: var(--bg-secondary) !important;
1960
+ box-shadow: 0 10px 30px -8px rgba(15, 23, 42, 0.18),
1961
+ 0 2px 6px -2px rgba(15, 23, 42, 0.10);
1962
+ }
2003
1963
 
2004
- `-webkit-tap-highlight-color` swaps iOS Safari's default opaque grey
2005
- tap overlay for a faint themed shimmer (~18 % accent over transparent),
2006
- matching the cyber / light palettes via the `--accent` token. */
2007
- @layer base {
2008
- button,
2009
- a,
2010
- [role="button"],
2011
- [role="tab"],
2012
- [role="menuitem"],
2013
- summary {
2014
- touch-action: manipulation;
2015
- -webkit-tap-highlight-color: color-mix(in oklab, var(--accent) 18%, transparent);
2016
- }
1964
+ [data-theme="light"] .anet-tabstrip-wrap::before,
1965
+ [data-theme="mint"] .anet-tabstrip-wrap::before {
1966
+ background: linear-gradient(to right, var(--bg-secondary), transparent);
1967
+ }
1968
+
1969
+ [data-theme="light"] .anet-tabstrip-wrap::after,
1970
+ [data-theme="mint"] .anet-tabstrip-wrap::after {
1971
+ background: linear-gradient(to left, var(--bg-secondary), transparent);
1972
+ }
1973
+
1974
+ /* P0-3 baseline: error / warning chip colors for light themes.
1975
+ The component-level copy (replacing raw JSON with human text) is fixed
1976
+ in the Logs / Server-Logs page components separately. */
1977
+ [data-theme="light"] .anet-error-chip,
1978
+ [data-theme="mint"] .anet-error-chip {
1979
+ background-color: rgba(220, 38, 38, 0.06) !important;
1980
+ color: #b91c1c !important;
1981
+ border-color: rgba(220, 38, 38, 0.25) !important;
1982
+ }
1983
+
1984
+ [data-theme="light"] .anet-warn-chip,
1985
+ [data-theme="mint"] .anet-warn-chip {
1986
+ background-color: rgba(217, 119, 6, 0.08) !important;
1987
+ color: #92400e !important;
1988
+ border-color: rgba(217, 119, 6, 0.28) !important;
1989
+ }
1990
+
1991
+ /* #217 S6 — opacity-variant fills that postdate the original shim
1992
+ table (MobileNav bottom bar, Settings/admin jump chips). */
1993
+ [data-theme="light"] .bg-\[\#111113\]\/95 {
1994
+ background-color: rgba(255, 255, 255, 0.95) !important;
1995
+ }
1996
+ [data-theme="light"] .bg-\[\#0e0e10\]\/60 {
1997
+ background-color: rgba(255, 255, 255, 0.6) !important;
2017
1998
  }