free-coding-models 0.5.0 → 0.5.4

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 (145) hide show
  1. package/README.md +40 -6
  2. package/bin/free-coding-models.js +10 -0
  3. package/changelog/v0.5.1.md +24 -0
  4. package/changelog/v0.5.4.md +24 -0
  5. package/package.json +11 -3
  6. package/sources.js +2 -0
  7. package/src/core/router-daemon.js +166 -1
  8. package/src/core/utils.js +2 -0
  9. package/src/tui/cli-help.js +2 -0
  10. package/src/tui/key-handler.js +1 -1
  11. package/src/tui/render-table.js +1 -1
  12. package/web/README.md +8 -5
  13. package/web/assets/providers/aistudio/aistudio-text.svg +1 -0
  14. package/web/assets/providers/aistudio/aistudio.svg +1 -0
  15. package/web/assets/providers/cerebras/cerebras-brand-color.svg +1 -0
  16. package/web/assets/providers/cerebras/cerebras-brand.svg +1 -0
  17. package/web/assets/providers/cerebras/cerebras-color.svg +1 -0
  18. package/web/assets/providers/cerebras/cerebras-text.svg +1 -0
  19. package/web/assets/providers/cerebras/cerebras.svg +1 -0
  20. package/web/assets/providers/cloudflare/cloudflare-color.svg +1 -0
  21. package/web/assets/providers/cloudflare/cloudflare-text.svg +1 -0
  22. package/web/assets/providers/cloudflare/cloudflare.svg +1 -0
  23. package/web/assets/providers/gemini/gemini-color.svg +1 -0
  24. package/web/assets/providers/gemini/gemini-text.svg +1 -0
  25. package/web/assets/providers/gemini/gemini.svg +1 -0
  26. package/web/assets/providers/github/github-text.svg +1 -0
  27. package/web/assets/providers/github/github.svg +1 -0
  28. package/web/assets/providers/groq/groq-text.svg +1 -0
  29. package/web/assets/providers/groq/groq.svg +1 -0
  30. package/web/assets/providers/mistral/mistral-color.svg +1 -0
  31. package/web/assets/providers/mistral/mistral-text.svg +1 -0
  32. package/web/assets/providers/mistral/mistral.svg +1 -0
  33. package/web/assets/providers/nvidia/nvidia-color.svg +1 -0
  34. package/web/assets/providers/nvidia/nvidia-text.svg +1 -0
  35. package/web/assets/providers/nvidia/nvidia.svg +1 -0
  36. package/web/assets/providers/opencode/opencode-text.svg +1 -0
  37. package/web/assets/providers/opencode/opencode.svg +1 -0
  38. package/web/assets/providers/openrouter/openrouter (1).svg +1 -0
  39. package/web/assets/providers/openrouter/openrouter-text.svg +1 -0
  40. package/web/assets/providers/openrouter/openrouter.svg +1 -0
  41. package/web/assets/providers/ovhcloud/ovhcloud.svg +12 -0
  42. package/web/assets/providers/qwen/qwen-color.svg +1 -0
  43. package/web/assets/providers/qwen/qwen-text.svg +1 -0
  44. package/web/assets/providers/qwen/qwen.svg +1 -0
  45. package/web/assets/providers/sambanova/sambanova-color.svg +1 -0
  46. package/web/assets/providers/sambanova/sambanova-text.svg +1 -0
  47. package/web/assets/providers/sambanova/sambanova.svg +1 -0
  48. package/web/assets/providers/scalewaylogo/ScalewayLogo.svg +13 -0
  49. package/web/assets/providers/zai/zai-text.svg +1 -0
  50. package/web/assets/providers/zai/zai.svg +1 -0
  51. package/web/dist/assets/index-BoWmUveV.js +39 -0
  52. package/web/dist/assets/index-BrpHevg4.css +1 -0
  53. package/web/dist/favicon.ico +0 -0
  54. package/web/dist/favicons/apple-touch-icon.png +0 -0
  55. package/web/dist/favicons/browserconfig.xml +12 -0
  56. package/web/dist/favicons/favicon-16x16.png +0 -0
  57. package/web/dist/favicons/favicon-192x192.png +0 -0
  58. package/web/dist/favicons/favicon-32x32.png +0 -0
  59. package/web/dist/favicons/favicon-48x48.png +0 -0
  60. package/web/dist/favicons/favicon-512x512.png +0 -0
  61. package/web/dist/favicons/favicon-96x96.png +0 -0
  62. package/web/dist/favicons/favicon.ico +0 -0
  63. package/web/dist/favicons/mstile-150x150.png +0 -0
  64. package/web/dist/favicons/mstile-310x150.png +0 -0
  65. package/web/dist/favicons/mstile-310x310.png +0 -0
  66. package/web/dist/favicons/mstile-512x512.png +0 -0
  67. package/web/dist/favicons/mstile-70x70.png +0 -0
  68. package/web/dist/favicons/site.webmanifest +25 -0
  69. package/web/dist/index.html +42 -6
  70. package/web/index.html +40 -4
  71. package/web/package.json +11 -0
  72. package/web/public/favicon.ico +0 -0
  73. package/web/public/favicons/apple-touch-icon.png +0 -0
  74. package/web/public/favicons/browserconfig.xml +12 -0
  75. package/web/public/favicons/favicon-16x16.png +0 -0
  76. package/web/public/favicons/favicon-192x192.png +0 -0
  77. package/web/public/favicons/favicon-32x32.png +0 -0
  78. package/web/public/favicons/favicon-48x48.png +0 -0
  79. package/web/public/favicons/favicon-512x512.png +0 -0
  80. package/web/public/favicons/favicon-96x96.png +0 -0
  81. package/web/public/favicons/favicon.ico +0 -0
  82. package/web/public/favicons/mstile-150x150.png +0 -0
  83. package/web/public/favicons/mstile-310x150.png +0 -0
  84. package/web/public/favicons/mstile-310x310.png +0 -0
  85. package/web/public/favicons/mstile-512x512.png +0 -0
  86. package/web/public/favicons/mstile-70x70.png +0 -0
  87. package/web/public/favicons/site.webmanifest +25 -0
  88. package/web/server.js +676 -211
  89. package/web/src/App.jsx +233 -71
  90. package/web/src/components/analytics/AnalyticsView.jsx +40 -6
  91. package/web/src/components/analytics/AnalyticsView.module.css +4 -0
  92. package/web/src/components/atoms/AILatencyCell.jsx +38 -0
  93. package/web/src/components/atoms/AILatencyCell.module.css +51 -0
  94. package/web/src/components/atoms/HealthCell.jsx +138 -0
  95. package/web/src/components/atoms/HealthCell.module.css +56 -0
  96. package/web/src/components/atoms/LastPingCell.jsx +35 -0
  97. package/web/src/components/atoms/LastPingCell.module.css +45 -0
  98. package/web/src/components/atoms/MoodCell.jsx +25 -0
  99. package/web/src/components/atoms/MoodCell.module.css +6 -0
  100. package/web/src/components/atoms/NoKeyIcon.jsx +65 -0
  101. package/web/src/components/atoms/NoKeyIcon.module.css +30 -0
  102. package/web/src/components/atoms/ProviderLogo.jsx +188 -0
  103. package/web/src/components/atoms/ProviderLogo.module.css +103 -0
  104. package/web/src/components/atoms/RankCell.jsx +9 -0
  105. package/web/src/components/atoms/RankCell.module.css +9 -0
  106. package/web/src/components/atoms/Sparkline.jsx +21 -2
  107. package/web/src/components/atoms/StabilityCell.module.css +5 -0
  108. package/web/src/components/atoms/StatusDot.module.css +7 -0
  109. package/web/src/components/atoms/TPSCell.jsx +36 -0
  110. package/web/src/components/atoms/TPSCell.module.css +44 -0
  111. package/web/src/components/atoms/TierBadge.module.css +11 -0
  112. package/web/src/components/atoms/Toast.module.css +6 -0
  113. package/web/src/components/atoms/VerdictBadge.jsx +30 -7
  114. package/web/src/components/atoms/VerdictBadge.module.css +36 -15
  115. package/web/src/components/dashboard/DetailPanel.jsx +63 -2
  116. package/web/src/components/dashboard/DetailPanel.module.css +130 -0
  117. package/web/src/components/dashboard/ExportModal.jsx +9 -4
  118. package/web/src/components/dashboard/FilterBar.jsx +228 -33
  119. package/web/src/components/dashboard/FilterBar.module.css +187 -22
  120. package/web/src/components/dashboard/ModelTable.jsx +500 -54
  121. package/web/src/components/dashboard/ModelTable.module.css +318 -31
  122. package/web/src/components/layout/Footer.jsx +1 -1
  123. package/web/src/components/layout/Header.jsx +171 -9
  124. package/web/src/components/layout/Header.module.css +223 -25
  125. package/web/src/components/palette/CommandPalette.jsx +146 -0
  126. package/web/src/components/palette/CommandPalette.module.css +121 -0
  127. package/web/src/components/settings/SettingsView.jsx +24 -6
  128. package/web/src/components/settings/SettingsView.module.css +0 -1
  129. package/web/src/global.css +118 -84
  130. package/web/src/hooks/useColumnSizing.js +211 -0
  131. package/web/src/hooks/useFavorites.js +150 -0
  132. package/web/src/hooks/useFilter.js +262 -37
  133. package/web/src/hooks/useSSE.js +33 -9
  134. package/web/src/hooks/useSocket.js +200 -0
  135. package/web/src/hooks/useTheme.js +72 -16
  136. package/web/src/hooks/useUrlState.js +93 -0
  137. package/web/vite.config.js +41 -0
  138. package/web/dist/assets/index-CGN-0_A0.css +0 -1
  139. package/web/dist/assets/index-CvMUM9Jr.js +0 -11
  140. package/web/src/components/dashboard/StatsBar.jsx +0 -40
  141. package/web/src/components/dashboard/StatsBar.module.css +0 -28
  142. package/web/src/components/layout/Sidebar.jsx +0 -42
  143. package/web/src/components/layout/Sidebar.module.css +0 -76
  144. package/web/src/components/map/MapView.jsx +0 -17
  145. package/web/src/components/map/MapView.module.css +0 -25
@@ -1,27 +1,83 @@
1
1
  /**
2
2
  * @file web/src/hooks/useTheme.js
3
- * @description React hook for dark/light theme toggle.
4
- * 📖 Persists theme on <html data-theme="dark|light">. Reads initial from DOM.
5
- * → useTheme
3
+ * @description React hook for tri-state theme cycle (auto / dark / light) M1 parity.
4
+ * 📖 Mirrors the TUI's `G` key behavior:
5
+ * 📖 auto system preference (resolved once at mount)
6
+ * 📖 dark → explicit dark
7
+ * 📖 light → explicit light
8
+ * 📖 Persists the choice on <html data-theme="auto|dark|light">. When set to
9
+ * 📖 `auto`, the hook resolves the actual rendered theme and writes it back to
10
+ * 📖 <html data-theme="..."> via resolveSystemTheme() (light/dark) so the
11
+ * 📖 rest of the CSS keeps working without any "auto" branch in stylesheets.
12
+ *
13
+ * @functions
14
+ * → useTheme() — { theme, resolvedTheme, setTheme, cycle }
6
15
  */
7
16
  import { useState, useCallback, useEffect } from 'react'
8
17
 
18
+ export const THEME_CYCLE = ['auto', 'dark', 'light']
19
+
20
+ // 📖 Read the OS-level dark/light preference. Falls back to dark on browsers
21
+ // 📖 that don't support matchMedia (very rare; SSR snapshots, etc.).
22
+ function resolveSystemTheme() {
23
+ if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return 'dark'
24
+ try {
25
+ return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'
26
+ } catch {
27
+ return 'dark'
28
+ }
29
+ }
30
+
31
+ function readInitialTheme() {
32
+ if (typeof document === 'undefined') return 'auto'
33
+ const attr = document.documentElement.getAttribute('data-theme')
34
+ if (attr === 'auto' || attr === 'dark' || attr === 'light') return attr
35
+ return 'auto'
36
+ }
37
+
9
38
  export function useTheme() {
10
- const [theme, setTheme] = useState(() => {
11
- return document.documentElement.getAttribute('data-theme') || 'dark'
12
- })
13
-
14
- const toggle = useCallback(() => {
15
- setTheme((prev) => {
16
- const next = prev === 'dark' ? 'light' : 'dark'
17
- document.documentElement.setAttribute('data-theme', next)
18
- return next
19
- })
39
+ const [theme, setThemeState] = useState(readInitialTheme)
40
+ const [systemTheme, setSystemTheme] = useState(resolveSystemTheme)
41
+
42
+ // 📖 Listen for OS theme changes while the user is in "auto" mode.
43
+ useEffect(() => {
44
+ if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return
45
+ const mq = window.matchMedia('(prefers-color-scheme: dark)')
46
+ const handler = (e) => setSystemTheme(e.matches ? 'dark' : 'light')
47
+ if (typeof mq.addEventListener === 'function') {
48
+ mq.addEventListener('change', handler)
49
+ return () => mq.removeEventListener('change', handler)
50
+ }
51
+ // 📖 Safari < 14 fallback
52
+ if (typeof mq.addListener === 'function') {
53
+ mq.addListener(handler)
54
+ return () => mq.removeListener(handler)
55
+ }
20
56
  }, [])
21
57
 
58
+ // 📖 Apply the resolved theme to <html data-theme="..."> on every change.
59
+ // 📖 We write the *resolved* value (light / dark) so existing CSS keeps working.
22
60
  useEffect(() => {
23
- document.documentElement.setAttribute('data-theme', theme)
24
- }, [theme])
61
+ const resolved = theme === 'auto' ? systemTheme : theme
62
+ document.documentElement.setAttribute('data-theme', resolved)
63
+ // 📖 Also stamp the user-chosen mode so other parts of the app can read it
64
+ // 📖 (e.g. to render the right "auto" badge in the theme button).
65
+ document.documentElement.setAttribute('data-theme-preference', theme)
66
+ }, [theme, systemTheme])
67
+
68
+ // 📖 Cycle through auto → dark → light → auto, matching the TUI's `G` key.
69
+ const cycle = useCallback(() => {
70
+ setThemeState((prev) => {
71
+ const idx = THEME_CYCLE.indexOf(prev)
72
+ return THEME_CYCLE[(idx + 1) % THEME_CYCLE.length]
73
+ })
74
+ }, [])
75
+
76
+ const setTheme = useCallback((next) => {
77
+ if (THEME_CYCLE.includes(next)) setThemeState(next)
78
+ }, [])
79
+
80
+ const resolvedTheme = theme === 'auto' ? systemTheme : theme
25
81
 
26
- return { theme, toggle }
82
+ return { theme, resolvedTheme, setTheme, cycle }
27
83
  }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * @file web/src/hooks/useUrlState.js
3
+ * @description URL deep-linking hook — the Web's answer to TUI CLI flags (--tier, --sort, etc.).
4
+ * 📖 M1 ships read-only hydration: on mount, parses the query string and hydrates the Web view.
5
+ * 📖 M2 will add write-back (push filter/sort/view changes back to the URL via history.replaceState).
6
+ *
7
+ * 📖 Supported query params (M1 = read-only):
8
+ * 📖 ?tier=S+|S|A+|A|A-|B+|B|C|all — sets the tier filter
9
+ * 📖 ?status=up|down|pending|all — sets the health/status filter
10
+ * 📖 ?provider=<providerKey>|all — sets the provider filter
11
+ * 📖 ?verdict=<verdict>|all — sets the verdict filter
12
+ * 📖 ?health=<health>|all — sets the health filter
13
+ * 📖 ?sort=<col>&dir=asc|desc — sets the sort column + direction
14
+ * 📖 ?view=dashboard|settings|analytics — sets the active view
15
+ * 📖 ?q=<text> — sets the search query
16
+ * 📖 ?tier=S&sort=verdict&dir=asc — compose freely; the same URL is shareable
17
+ *
18
+ * @functions
19
+ * → useUrlState({ currentView, setCurrentView, filterState }) — hydrates Web state from URL
20
+ *
21
+ * @see ideas/tui-web-feature-parity.md §5.4 — full CLI-flag ↔ URL-param mapping
22
+ */
23
+ import { useEffect } from 'react'
24
+
25
+ // 📖 Valid enum values for cycle-style params. Keys here determine which params
26
+ // 📖 get accepted silently vs ignored. Anything not in the list is dropped.
27
+ const VALID_TIERS = new Set(['S+', 'S', 'A+', 'A', 'A-', 'B+', 'B', 'C', 'all'])
28
+ const VALID_STATUS = new Set(['up', 'down', 'pending', 'all'])
29
+ const VALID_SORTS = new Set([
30
+ 'mood', 'idx', 'tier', 'sweScore', 'ctx', 'label', 'origin',
31
+ 'latestPing', 'avg', 'condition', 'verdict', 'stability', 'uptime',
32
+ 'aiLatency', 'tps', 'trend',
33
+ ])
34
+ const VALID_VIEWS = new Set(['dashboard', 'settings', 'analytics'])
35
+ const VALID_DIRS = new Set(['asc', 'desc'])
36
+
37
+ /**
38
+ * 📖 parseUrlParams: extract a normalized set of params from the current URL.
39
+ * 📖 Returns a flat object with camelCase keys, only including recognized params.
40
+ * @returns {{
41
+ * tier: string|null, status: string|null, provider: string|null,
42
+ * verdict: string|null, health: string|null,
43
+ * sort: string|null, dir: string|null, view: string|null, q: string,
44
+ * }}
45
+ */
46
+ function parseUrlParams() {
47
+ if (typeof window === 'undefined') return null
48
+ const params = new URLSearchParams(window.location.search)
49
+ const out = {}
50
+ if (params.has('tier') && VALID_TIERS.has(params.get('tier'))) out.tier = params.get('tier')
51
+ if (params.has('status') && VALID_STATUS.has(params.get('status'))) out.status = params.get('status')
52
+ if (params.has('provider')) out.provider = params.get('provider')
53
+ if (params.has('verdict')) out.verdict = params.get('verdict')
54
+ if (params.has('health')) out.health = params.get('health')
55
+ if (params.has('sort') && VALID_SORTS.has(params.get('sort'))) out.sort = params.get('sort')
56
+ if (params.has('dir') && VALID_DIRS.has(params.get('dir'))) out.dir = params.get('dir')
57
+ if (params.has('view') && VALID_VIEWS.has(params.get('view'))) out.view = params.get('view')
58
+ if (params.has('q')) out.q = params.get('q')
59
+ return out
60
+ }
61
+
62
+ /**
63
+ * 📖 useUrlState: hydrate the Web view from the current URL on mount.
64
+ * 📖 M1 = read-only. Pass setters; the hook calls them once on mount with
65
+ * 📖 whatever the URL declares, then leaves the URL alone.
66
+ *
67
+ * 📖 The hook is intentionally non-intrusive: if the URL has no params, it does
68
+ * 📖 nothing. The caller is free to call the setters independently — the URL
69
+ * 📖 simply becomes the *initial* state.
70
+ *
71
+ * @param {{
72
+ * currentView: string,
73
+ * setCurrentView: (view: string) => void,
74
+ * filterState: object | null, // wired in M2
75
+ * }} opts
76
+ */
77
+ export function useUrlState({ currentView, setCurrentView, filterState }) {
78
+ useEffect(() => {
79
+ const params = parseUrlParams()
80
+ if (!params) return
81
+
82
+ if (params.view && params.view !== currentView) {
83
+ setCurrentView(params.view)
84
+ }
85
+
86
+ // 📖 Filter hydration is wired through the filterState prop in M2. M1 just
87
+ // 📖 sets the view; the rest of the URL params act as documentation for
88
+ // 📖 the user and get the write-back loop fully closed in M2.
89
+ }, [currentView, setCurrentView, filterState])
90
+ }
91
+
92
+ // 📖 Re-export the parser so M2's write-back path can use the same validator.
93
+ export { parseUrlParams, VALID_TIERS, VALID_STATUS, VALID_SORTS, VALID_VIEWS, VALID_DIRS }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * @file web/vite.config.js
3
+ * @description Vite config with proxy for backend API + Socket.IO (with WebSocket upgrade).
4
+ */
5
+ import { defineConfig } from 'vite'
6
+ import react from '@vitejs/plugin-react'
7
+ import { readFileSync } from 'node:fs'
8
+ import { fileURLToPath } from 'node:url'
9
+
10
+ const __dirname = fileURLToPath(new URL('.', import.meta.url))
11
+ const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf8'))
12
+
13
+ export default defineConfig({
14
+ plugins: [react()],
15
+ root: 'web',
16
+ build: {
17
+ outDir: 'dist',
18
+ emptyOutDir: true,
19
+ },
20
+ server: {
21
+ port: 5173,
22
+ proxy: {
23
+ '/api': {
24
+ target: 'http://localhost:3333',
25
+ changeOrigin: true,
26
+ },
27
+ '/socket.io': {
28
+ target: 'http://localhost:3333',
29
+ changeOrigin: true,
30
+ ws: true, // ← enable WebSocket upgrade
31
+ },
32
+ '/v1': {
33
+ target: 'http://localhost:3333',
34
+ changeOrigin: true,
35
+ },
36
+ },
37
+ },
38
+ define: {
39
+ __APP_VERSION__: JSON.stringify(pkg.version),
40
+ },
41
+ })
@@ -1 +0,0 @@
1
- ._header_1nuxz_1{z-index:100;background:var(--color-bg-card);-webkit-backdrop-filter:blur(20px)saturate(1.5);border-bottom:1px solid var(--color-border);justify-content:space-between;align-items:center;gap:20px;height:60px;padding:0 24px;display:flex;position:sticky;top:0}._left_1nuxz_16{align-items:center;gap:12px;display:flex}._center_1nuxz_17{flex:1;max-width:480px}._right_1nuxz_18{align-items:center;gap:10px;display:flex}._logo_1nuxz_20{align-items:center;gap:8px;display:flex}._logoIcon_1nuxz_21{font-size:22px}._logoText_1nuxz_22{background:linear-gradient(135deg, var(--color-accent), #06b6d4);-webkit-text-fill-color:transparent;-webkit-background-clip:text;background-clip:text;font-size:16px;font-weight:700}._version_1nuxz_27{font-size:11px;font-weight:500;font-family:var(--font-mono);color:var(--color-text-dim);background:var(--color-accent-dim);border:1px solid var(--color-border);border-radius:999px;padding:2px 8px}._searchBar_1nuxz_33{background:var(--color-surface);border:1px solid var(--color-border);border-radius:10px;align-items:center;gap:8px;height:36px;padding:0 12px;transition:border-color .15s,box-shadow .15s;display:flex}._searchBar_1nuxz_33:focus-within{border-color:var(--color-accent);box-shadow:0 0 0 3px var(--color-accent-glow)}._searchIcon_1nuxz_43{flex-shrink:0;font-size:14px}._searchInput_1nuxz_44{color:var(--color-text);font-size:13px;font-family:var(--font-sans);background:0 0;border:none;outline:none;flex:1}._searchInput_1nuxz_44::placeholder{color:var(--color-text-dim)}._kbd_1nuxz_49{font-size:10px;font-family:var(--font-mono);color:var(--color-text-dim);background:var(--color-bg);border:1px solid var(--color-border);border-radius:4px;flex-shrink:0;padding:2px 6px}._iconBtn_1nuxz_55{border:1px solid var(--color-border);background:var(--color-surface);color:var(--color-text);cursor:pointer;border-radius:6px;justify-content:center;align-items:center;padding:6px 8px;font-size:16px;transition:all .15s;display:inline-flex}._iconBtn_1nuxz_55:hover{background:var(--color-bg-hover);border-color:var(--color-border-hover)}._primaryBtn_1nuxz_62{border:1px solid var(--color-accent);background:var(--color-accent);color:#000;cursor:pointer;font-size:13px;font-weight:600;font-family:var(--font-sans);border-radius:6px;align-items:center;gap:6px;padding:6px 14px;transition:all .15s;display:inline-flex}._primaryBtn_1nuxz_62:hover{background:var(--color-accent-hover)}@media (width<=768px){._center_1nuxz_17{display:none}}._sidebar_16fei_1{z-index:200;background:var(--color-bg-elevated);border-right:1px solid var(--color-border);flex-direction:column;width:64px;padding:12px 0;transition:width .25s cubic-bezier(.16,1,.3,1);display:flex;position:fixed;top:0;bottom:0;left:0;overflow:hidden}._sidebar_16fei_1:hover{width:200px}._logo_16fei_16{white-space:nowrap;align-items:center;gap:10px;margin-bottom:16px;padding:8px 18px;display:flex;overflow:hidden}._logoIcon_16fei_25{flex-shrink:0;font-size:24px}._logoText_16fei_26{background:linear-gradient(135deg, var(--color-accent), #06b6d4);-webkit-text-fill-color:transparent;opacity:0;-webkit-background-clip:text;background-clip:text;font-size:16px;font-weight:800;transition:opacity .25s cubic-bezier(.16,1,.3,1)}._sidebar_16fei_1:hover ._logoText_16fei_26{opacity:1}._nav_16fei_38{flex-direction:column;flex:1;gap:4px;display:flex}._bottom_16fei_39{flex-direction:column;gap:4px;display:flex}._navItem_16fei_41{color:var(--color-text-muted);cursor:pointer;white-space:nowrap;font-family:var(--font-sans);background:0 0;border:none;align-items:center;gap:12px;padding:10px 20px;font-size:13px;font-weight:500;transition:all .15s cubic-bezier(.16,1,.3,1);display:flex;overflow:hidden}._navItem_16fei_41:hover{color:var(--color-text);background:var(--color-bg-hover)}._navIcon_16fei_61{text-align:center;flex-shrink:0;width:20px;font-size:16px}._navLabel_16fei_67{opacity:0;transition:opacity .25s cubic-bezier(.16,1,.3,1)}._sidebar_16fei_1:hover ._navLabel_16fei_67{opacity:1}._active_16fei_72{border-right:3px solid var(--color-accent);color:var(--color-accent)!important;background:var(--color-accent-dim)!important}._footer_1uw54_1{z-index:1;border-top:1px solid var(--color-border);color:var(--color-text-dim);justify-content:space-between;align-items:center;padding:16px 24px;font-size:12px;display:flex;position:relative}._left_1uw54_8 a,._right_1uw54_8 a{color:var(--color-text-dim);text-decoration:none}._left_1uw54_8 a:hover,._right_1uw54_8 a:hover{color:var(--color-accent)}._right_1uw54_8{gap:16px;display:flex}._statsBar_1q3n1_1{z-index:1;grid-template-columns:repeat(5,1fr);gap:14px;padding:20px 24px 0;display:grid;position:relative}._card_1q3n1_6{background:var(--color-bg-card);-webkit-backdrop-filter:blur(12px);border:1px solid var(--color-border);border-radius:16px;align-items:center;gap:12px;padding:16px 18px;transition:all .25s cubic-bezier(.16,1,.3,1);display:flex}._card_1q3n1_6:hover{border-color:var(--color-border-hover);box-shadow:var(--shadow-md);transform:translateY(-2px)}._icon_1q3n1_21{font-size:28px}._body_1q3n1_22{flex:1}._value_1q3n1_23{font-size:22px;font-weight:800;font-family:var(--font-mono);line-height:1}._label_1q3n1_24{color:var(--color-text-muted);text-transform:uppercase;letter-spacing:.5px;margin-top:2px;font-size:11px;font-weight:500}@media (width<=1024px){._statsBar_1q3n1_1{grid-template-columns:repeat(3,1fr)}}@media (width<=768px){._statsBar_1q3n1_1{grid-template-columns:repeat(2,1fr)}}@media (width<=480px){._statsBar_1q3n1_1{grid-template-columns:1fr}}._filters_jhonm_1{z-index:1;flex-wrap:wrap;align-items:center;gap:20px;padding:16px 24px;display:flex;position:relative}._group_jhonm_6{align-items:center;gap:8px;display:flex}._filterLabel_jhonm_7{color:var(--color-text-muted);text-transform:uppercase;letter-spacing:.5px;font-size:11px;font-weight:600}._tierRow_jhonm_11{gap:4px;display:flex}._tierBtn_jhonm_12{font-size:12px;font-weight:600;font-family:var(--font-mono);border:1px solid var(--color-border);background:var(--color-surface);color:var(--color-text-muted);cursor:pointer;border-radius:6px;padding:4px 10px;transition:all .15s}._tierBtn_jhonm_12:hover{background:var(--color-bg-hover);color:var(--color-text)}._active_jhonm_19{font-weight:700;background:var(--color-accent)!important;color:#000!important;border-color:var(--color-accent)!important}._providerSelect_jhonm_23{background:var(--color-surface);color:var(--color-text);border:1px solid var(--color-border);font-size:12px;font-family:var(--font-sans);cursor:pointer;border-radius:6px;outline:none;padding:5px 10px}._providerSelect_jhonm_23:focus{border-color:var(--color-accent)}._spacer_jhonm_30{flex:1}._live_jhonm_31{color:var(--color-accent);text-transform:uppercase;letter-spacing:1px;align-items:center;gap:6px;font-size:11px;font-weight:700;display:flex}._liveDot_jhonm_36{background:var(--color-accent);border-radius:50%;width:8px;height:8px;animation:2s ease-in-out infinite _pulseDot_jhonm_1}@keyframes _pulseDot_jhonm_1{0%,to{box-shadow:0 0 0 0 var(--color-accent-glow)}50%{box-shadow:0 0 0 6px #0000}}._badge_1vsmk_1{text-align:center;border-radius:4px;min-width:32px;padding:2px 8px;font-family:JetBrains Mono,monospace;font-size:11px;font-weight:700;display:inline-block}._tier_splus_1vsmk_11{color:gold;background:#ffd70026;border:1px solid #ffd7004d}._tier_s_1vsmk_11{color:#ff8c00;background:#ff8c001f;border:1px solid #ff8c0040}._tier_aplus_1vsmk_13{color:#00c8ff;background:#00c8ff1a;border:1px solid #00c8ff33}._tier_a_1vsmk_13{color:#3ddc84;background:#3ddc841a;border:1px solid #3ddc8433}._tier_aminus_1vsmk_15{color:#7ecf7e;background:#7ecf7e1a;border:1px solid #7ecf7e33}._tier_bplus_1vsmk_16{color:#a8a8c8;background:#a8a8c81a;border:1px solid #a8a8c826}._tier_b_1vsmk_16{color:#808098;background:#8080981a;border:1px solid #80809826}._tier_c_1vsmk_18{color:#606078;background:#6060781a;border:1px solid #60607826}._dot_xhoha_1{vertical-align:middle;border-radius:50%;width:8px;height:8px;margin-right:6px;display:inline-block}._up_xhoha_9{background:#0f8;box-shadow:0 0 6px #0f86}._down_xhoha_10,._timeout_xhoha_11{background:#f44}._pending_xhoha_12{background:#555570;animation:1.5s ease-in-out infinite _pulse_xhoha_1}@keyframes _pulse_xhoha_1{0%,to{box-shadow:0 0 #76b90040}50%{box-shadow:0 0 0 6px #0000}}._badge_j5mmw_1{text-transform:uppercase;letter-spacing:.5px;border-radius:999px;padding:2px 8px;font-size:10px;font-weight:700;display:inline-block}._perfect_j5mmw_10{color:#0f8;background:#00ff881f;border:1px solid #00ff8840}._normal_j5mmw_11{color:#76b900;background:#76b9001f;border:1px solid #76b90040}._slow_j5mmw_12{color:#fa0;background:#ffaa001f;border:1px solid #ffaa0040}._spiky_j5mmw_13{color:#f60;background:#ff66001f;border:1px solid #ff660040}._veryslow_j5mmw_14{color:#f44;background:#ff44441f;border:1px solid #f443}._overloaded_j5mmw_15{color:#f22;background:#ff22221f;border:1px solid #f223}._unstable_j5mmw_16{color:#c00;background:#cc00001f;border:1px solid #c003}._notactive_j5mmw_17{color:#555570;background:#5555701f;border:1px solid #55557026}._pending_j5mmw_18{color:#444460;background:#4444601a;border:1px solid #44446026}._ratelimited_j5mmw_19{color:#fa0;background:#ffaa0026;border:1px solid #ffaa004d}._cell_1qs4d_1{align-items:center;gap:6px;display:flex}._bar_1qs4d_2{background:var(--color-surface);border-radius:3px;width:48px;height:6px;overflow:hidden}._fill_1qs4d_3{border-radius:3px;height:100%;transition:width .25s cubic-bezier(.16,1,.3,1)}._high_1qs4d_4{background:linear-gradient(90deg,#0f8,#76b900)}._mid_1qs4d_5{background:linear-gradient(90deg,#fa0,#ff8c00)}._low_1qs4d_6{background:linear-gradient(90deg,#f44,#c00)}._value_1qs4d_7{font-family:JetBrains Mono,monospace;font-size:12px;font-weight:600}._none_1qs4d_8{color:var(--color-text-dim)}._container_8mrfy_1{background:var(--color-bg-card);-webkit-backdrop-filter:blur(12px)saturate(1.5);border:1px solid var(--color-border);border-radius:16px;margin:16px 0;overflow:hidden}._table_8mrfy_10{border-collapse:collapse;width:100%;font-size:13px}._th_8mrfy_11{text-transform:uppercase;letter-spacing:.8px;color:var(--color-text-muted);background:var(--color-bg-elevated);border-bottom:1px solid var(--color-border);white-space:nowrap;-webkit-user-select:none;user-select:none;cursor:default;padding:12px 14px;font-size:11px;font-weight:700}._th_8mrfy_11:hover{color:var(--color-accent)}._table_8mrfy_10 td{border-bottom:1px solid var(--color-border);white-space:nowrap;padding:10px 14px}._table_8mrfy_10 tbody tr{cursor:pointer;transition:background .15s}._table_8mrfy_10 tbody tr:hover{background:var(--color-bg-hover)}._tdRank_8mrfy_20{text-align:center;font-family:var(--font-mono);color:var(--color-text-dim);font-weight:600}._rank1_8mrfy_21 td:first-child{border-left:3px solid gold}._rank2_8mrfy_22 td:first-child{border-left:3px solid silver}._rank3_8mrfy_23 td:first-child{border-left:3px solid #cd7f32}._modelCell_8mrfy_24{flex-direction:column;display:flex}._modelName_8mrfy_25{align-items:center;gap:4px;font-size:13px;font-weight:600;display:flex}._modelId_8mrfy_26{font-family:var(--font-mono);color:var(--color-text-dim);margin-top:1px;font-size:10px}._noKey_8mrfy_27{color:#fa0;background:#ffaa001f;border:1px solid #fa03;border-radius:3px;margin-left:6px;padding:1px 6px;font-size:10px;font-weight:600}._providerPill_8mrfy_31{background:var(--color-accent-dim);color:var(--color-text-muted);border:1px solid var(--color-border);border-radius:999px;padding:2px 8px;font-size:11px;font-weight:500;display:inline-block}._swe_8mrfy_35{font-family:var(--font-mono);font-weight:600}._sweHigh_8mrfy_36{color:gold}._sweMid_8mrfy_37{color:#3ddc84}._sweLow_8mrfy_38{color:var(--color-text-dim)}._ctx_8mrfy_39{font-family:var(--font-mono);color:var(--color-text-muted);font-size:12px}._ping_8mrfy_40{font-family:var(--font-mono);font-weight:600}._pingFast_8mrfy_41{color:#0f8}._pingMedium_8mrfy_42{color:#fa0}._pingSlow_8mrfy_43{color:#f44}._pingNone_8mrfy_44{color:var(--color-text-dim)}._uptime_8mrfy_45{font-family:var(--font-mono);font-size:12px}._empty_8mrfy_46{text-align:center;color:var(--color-text-muted);padding:60px 0}._panel_uzr3l_1{z-index:300;background:var(--color-bg-elevated);border-left:1px solid var(--color-border);width:min(420px,90vw);box-shadow:var(--shadow-lg);transition:transform var(--transition-medium);position:fixed;top:0;bottom:0;right:0;overflow-y:auto;transform:translate(100%)}._header_uzr3l_16{border-bottom:1px solid var(--color-border);justify-content:space-between;align-items:center;padding:20px 24px;display:flex}._title_uzr3l_24{margin:0;font-size:16px;font-weight:700}._closeBtn_uzr3l_30{color:var(--color-text-muted);cursor:pointer;background:0 0;border:none;font-size:22px;line-height:1}._closeBtn_uzr3l_30:hover{color:var(--color-text)}._body_uzr3l_43{padding:24px}._stat_uzr3l_47{border-bottom:1px solid var(--color-border);justify-content:space-between;align-items:center;padding:10px 0;display:flex}._statLabel_uzr3l_55{color:var(--color-text-muted);text-transform:uppercase;letter-spacing:.5px;font-size:12px;font-weight:600}._statValue_uzr3l_63,._ping_uzr3l_68{font-family:var(--font-mono);font-weight:600}._pingFast_uzr3l_73{color:#0f8}._pingMedium_uzr3l_74{color:#fa0}._pingSlow_uzr3l_75{color:#f44}._pingNone_uzr3l_76{color:var(--color-text-dim)}._chart_uzr3l_78{background:var(--color-surface);border-radius:var(--radius-md);border:1px solid var(--color-border);margin:20px 0;padding:16px}._chartTitle_uzr3l_86{color:var(--color-text-muted);text-transform:uppercase;letter-spacing:.8px;margin-bottom:12px;font-size:11px;font-weight:700}._chartEmpty_uzr3l_95{color:var(--color-text-dim);text-align:center;padding:20px}._overlay_1e3f5_1{z-index:1000;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px);background:#0009;justify-content:center;align-items:center;animation:.2s ease-out _fadeIn_1e3f5_1;display:flex;position:fixed;inset:0}._overlay_1e3f5_1[hidden]{display:none}@keyframes _fadeIn_1e3f5_1{0%{opacity:0}to{opacity:1}}._modal_1e3f5_18{background:var(--color-bg-elevated);border:1px solid var(--color-border);border-radius:var(--radius-xl);width:min(500px,90vw);max-height:80vh;box-shadow:var(--shadow-lg);animation:_modalSlideIn_1e3f5_1 .3s var(--ease-out);overflow-y:auto}@keyframes _modalSlideIn_1e3f5_1{0%{opacity:0;transform:translateY(20px)scale(.96)}to{opacity:1;transform:translateY(0)scale(1)}}._modalHeader_1e3f5_33{border-bottom:1px solid var(--color-border);justify-content:space-between;align-items:center;padding:20px 24px;display:flex}._modalTitle_1e3f5_40{margin:0;font-size:18px;font-weight:700}._modalClose_1e3f5_45{color:var(--color-text-muted);cursor:pointer;background:0 0;border:none;padding:0;font-size:24px;line-height:1}._modalClose_1e3f5_45:hover{color:var(--color-text)}._modalBody_1e3f5_57{padding:24px}._options_1e3f5_60{flex-direction:column;gap:10px;display:flex}._option_1e3f5_60{border:1px solid var(--color-border);border-radius:var(--radius-md);background:var(--color-surface);cursor:pointer;transition:all var(--transition-fast);text-align:left;font-family:var(--font-sans);color:var(--color-text);align-items:center;gap:14px;width:100%;padding:14px 18px;display:flex}._option_1e3f5_60:hover{border-color:var(--color-accent);background:var(--color-accent-dim)}._optionIcon_1e3f5_84{flex-shrink:0;font-size:24px}._optionLabel_1e3f5_89{font-size:14px;font-weight:600;display:block}._optionDesc_1e3f5_94{color:var(--color-text-muted);margin-top:2px;font-size:11px;display:block}._page_17r6q_1{max-width:900px;padding:24px}._pageHeader_17r6q_6{margin-bottom:24px}._pageTitle_17r6q_10{margin:0 0 6px;font-size:24px;font-weight:800}._pageSubtitle_17r6q_16{color:var(--color-text-muted);margin:0;font-size:13px;line-height:1.5}._pageSubtitle_17r6q_16 code{font-family:var(--font-mono);background:var(--color-surface);border-radius:4px;padding:2px 6px;font-size:12px}._loading_17r6q_31{text-align:center;color:var(--color-text-muted);padding:60px 0}._toolbar_17r6q_37{flex-wrap:wrap;align-items:center;gap:12px;margin-bottom:20px;display:flex}._toolbarSearch_17r6q_45{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-md);flex:1;align-items:center;gap:8px;min-width:200px;height:36px;padding:0 12px;display:flex}._toolbarSearch_17r6q_45 svg{color:var(--color-text-dim);flex-shrink:0}._toolbarSearch_17r6q_45 input{color:var(--color-text);font-size:13px;font-family:var(--font-sans);background:0 0;border:none;outline:none;flex:1}._toolbarSearch_17r6q_45 input::placeholder{color:var(--color-text-dim)}._toolbarActions_17r6q_77{gap:8px;display:flex}._toolbarBtn_17r6q_82{border:1px solid var(--color-border);border-radius:var(--radius-sm);cursor:pointer;background:var(--color-surface);color:var(--color-text);transition:all var(--transition-fast);font-size:13px;font-weight:500;font-family:var(--font-sans);align-items:center;gap:6px;padding:6px 14px;display:inline-flex}._toolbarBtn_17r6q_82:hover{background:var(--color-bg-hover);border-color:var(--color-border-hover)}._providers_17r6q_103{flex-direction:column;gap:12px;display:flex}._card_17r6q_109{background:var(--color-bg-card);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border:1px solid var(--color-border);border-radius:var(--radius-lg);transition:border-color var(--transition-fast);overflow:hidden}._card_17r6q_109:hover{border-color:var(--color-border-hover)}._cardHeader_17r6q_122{cursor:pointer;transition:background var(--transition-fast);align-items:center;gap:12px;padding:16px 20px;display:flex}._cardHeader_17r6q_122:hover{background:var(--color-bg-hover)}._cardIcon_17r6q_135{border-radius:var(--radius-sm);background:var(--color-accent-dim);flex-shrink:0;justify-content:center;align-items:center;width:36px;height:36px;font-size:18px;display:flex}._cardInfo_17r6q_147{flex:1;min-width:0}._cardName_17r6q_152{font-size:14px;font-weight:700}._cardMeta_17r6q_157{color:var(--color-text-muted);margin-top:2px;font-size:11px}._cardStatus_17r6q_163{border-radius:999px;flex-shrink:0;padding:3px 10px;font-size:11px;font-weight:700}._statusConfigured_17r6q_171{background:var(--color-success-dim);color:var(--color-success)}._statusMissing_17r6q_176{background:var(--color-warning-dim);color:var(--color-warning)}._toggleIcon_17r6q_181{color:var(--color-text-dim);transition:transform var(--transition-fast);flex-shrink:0;font-size:18px}._toggleIconExpanded_17r6q_188{transform:rotate(180deg)}._cardBody_17r6q_192{max-height:0;transition:max-height var(--transition-medium);border-top:0 solid #0000;overflow:hidden}._cardExpanded_17r6q_199 ._cardBody_17r6q_192{border-top:1px solid var(--color-border);max-height:500px}._cardContent_17r6q_204{padding:16px 20px}._keyGroup_17r6q_208{margin-bottom:12px}._keyLabel_17r6q_212{color:var(--color-text-muted);text-transform:uppercase;letter-spacing:.5px;margin-bottom:6px;font-size:11px;font-weight:600;display:block}._keyDisplay_17r6q_222{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-sm);align-items:center;gap:8px;margin-bottom:8px;padding:8px 12px;display:flex}._keyDisplayValue_17r6q_233{font-family:var(--font-mono);color:var(--color-text-muted);word-break:break-all;flex:1;font-size:13px}._keyDisplayActions_17r6q_241{flex-shrink:0;gap:4px;display:flex}._actionBtn_17r6q_247{border:1px solid var(--color-border);border-radius:var(--radius-sm);background:var(--color-surface);color:var(--color-text);cursor:pointer;transition:all var(--transition-fast);justify-content:center;align-items:center;padding:4px 8px;font-size:12px;display:inline-flex}._actionBtn_17r6q_247:hover{background:var(--color-bg-hover)}._actionBtnDanger_17r6q_265{background:var(--color-danger-dim);color:var(--color-danger);border-color:#f443}._actionBtnDanger_17r6q_265:hover{background:#f443}._keyInputRow_17r6q_275{align-items:center;gap:8px;display:flex}._keyInput_17r6q_275{background:var(--color-surface);border:1px solid var(--color-border);border-radius:var(--radius-sm);font-size:13px;font-family:var(--font-mono);color:var(--color-text);transition:border-color var(--transition-fast);outline:none;flex:1;padding:8px 12px}._keyInput_17r6q_275:focus{border-color:var(--color-accent)}._keyInput_17r6q_275::placeholder{color:var(--color-text-dim)}._saveBtn_17r6q_302{border-radius:var(--radius-sm);background:var(--color-success-dim);color:var(--color-success);cursor:pointer;font-size:13px;font-weight:600;font-family:var(--font-sans);transition:all var(--transition-fast);border:1px solid #0f83;align-items:center;gap:6px;padding:8px 14px;display:inline-flex}._saveBtn_17r6q_302:hover{background:#00ff8826}._enabledRow_17r6q_322{border-top:1px solid var(--color-border);justify-content:space-between;align-items:center;margin-top:12px;padding-top:12px;display:flex}._enabledLabel_17r6q_331{color:var(--color-text-muted);font-size:12px;font-weight:500}._toggleSwitch_17r6q_337{cursor:pointer;width:44px;height:24px;position:relative}._toggleSwitch_17r6q_337 input{display:none}._toggleSlider_17r6q_348{background:var(--color-surface);border:1px solid var(--color-border);transition:all var(--transition-fast);border-radius:12px;position:absolute;inset:0}._toggleSlider_17r6q_348:after{content:"";background:var(--color-text-dim);width:16px;height:16px;transition:all var(--transition-fast);border-radius:50%;position:absolute;top:3px;left:3px}._toggleSwitch_17r6q_337 input:checked+._toggleSlider_17r6q_348{background:var(--color-accent-dim);border-color:var(--color-accent)}._toggleSwitch_17r6q_337 input:checked+._toggleSlider_17r6q_348:after{background:var(--color-accent);transform:translate(20px)}._page_1sl6l_1{padding:24px}._pageHeader_1sl6l_5{margin-bottom:24px}._pageTitle_1sl6l_9{margin:0 0 6px;font-size:24px;font-weight:800}._pageSubtitle_1sl6l_15{color:var(--color-text-muted);margin:0;font-size:13px}._grid_1sl6l_21{grid-template-columns:repeat(2,1fr);gap:16px;display:grid}._card_1sl6l_27{background:var(--color-bg-card);-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);border:1px solid var(--color-border);border-radius:var(--radius-lg);padding:20px;overflow:hidden}._cardWide_1sl6l_36{grid-column:span 2}._cardTitle_1sl6l_40{margin:0 0 16px;font-size:14px;font-weight:700}._empty_1sl6l_49{color:var(--color-text-dim);text-align:center;padding:20px 0}._healthItem_1sl6l_55{border-bottom:1px solid var(--color-border);align-items:center;gap:12px;padding:8px 0;display:flex}._healthItem_1sl6l_55:last-child{border-bottom:none}._healthName_1sl6l_67{flex-shrink:0;width:120px;font-size:12px;font-weight:600}._healthBar_1sl6l_74{background:var(--color-surface);border-radius:4px;flex:1;height:8px;overflow:hidden}._healthFill_1sl6l_82{background:linear-gradient(90deg, var(--color-accent), var(--color-success));height:100%;transition:width var(--transition-medium);border-radius:4px}._healthPct_1sl6l_89{text-align:right;width:40px;font-family:var(--font-mono);font-size:12px;font-weight:600}._pctFast_1sl6l_97{color:#0f8}._pctMedium_1sl6l_98{color:#fa0}._pctSlow_1sl6l_99{color:#f44}._leaderItem_1sl6l_101{border-bottom:1px solid var(--color-border);align-items:center;gap:10px;padding:8px 0;display:flex}._leaderItem_1sl6l_101:last-child{border-bottom:none}._leaderRank_1sl6l_113{background:var(--color-surface);width:28px;height:28px;color:var(--color-text-dim);border-radius:50%;flex-shrink:0;justify-content:center;align-items:center;font-size:12px;font-weight:700;display:flex}._rank1_1sl6l_127{color:gold;background:#ffd70026}._rank2_1sl6l_128{color:silver;background:#c0c0c026}._rank3_1sl6l_129{color:#cd7f32;background:#cd7f3226}._leaderName_1sl6l_131{text-overflow:ellipsis;white-space:nowrap;flex:1;min-width:0;font-size:12px;font-weight:600;overflow:hidden}._leaderLatency_1sl6l_141{font-family:var(--font-mono);color:var(--color-success);flex-shrink:0;font-size:12px;font-weight:600}._tierItem_1sl6l_149{align-items:center;gap:10px;padding:6px 0;display:flex}._tierBadge_1sl6l_156{flex-shrink:0;width:40px}._tierBar_1sl6l_161{background:var(--color-surface);border-radius:5px;flex:1;height:10px;overflow:hidden}._tierFill_1sl6l_169{height:100%;transition:width var(--transition-medium);border-radius:5px}._tierCount_1sl6l_175{text-align:right;width:30px;font-family:var(--font-mono);color:var(--color-text-muted);font-size:12px}@media (width<=1024px){._grid_1sl6l_21{grid-template-columns:1fr}._cardWide_1sl6l_36{grid-column:span 1}}._container_156te_1{width:100%;padding:2rem}._wip_156te_6{text-align:center;flex-direction:column;justify-content:center;align-items:center;min-height:60vh;display:flex}._wip_156te_6 h1{color:var(--text-primary);margin-bottom:1rem;font-size:2rem;font-weight:700}._wip_156te_6 p{color:var(--text-secondary);font-size:1.125rem}._container_18l8h_1{z-index:10000;pointer-events:none;flex-direction:column;gap:8px;display:flex;position:fixed;top:20px;right:20px}._toast_tvk0q_1{pointer-events:auto;border-radius:var(--radius-md,10px);background:var(--color-bg-elevated);border:1px solid var(--color-border);box-shadow:var(--shadow-lg);align-items:center;gap:10px;min-width:280px;max-width:420px;padding:12px 18px;font-size:13px;font-weight:500;animation:.3s cubic-bezier(.16,1,.3,1) _slideIn_tvk0q_1;display:flex}._success_tvk0q_17{border-left:4px solid #0f8}._error_tvk0q_18{border-left:4px solid #f44}._warning_tvk0q_19{border-left:4px solid #fa0}._info_tvk0q_20{border-left:4px solid #06b6d4}._icon_tvk0q_21{flex-shrink:0;font-size:18px}._message_tvk0q_22{flex:1}._close_tvk0q_23{color:var(--color-text-dim);cursor:pointer;background:0 0;border:none;padding:0;font-size:16px;line-height:1}._exiting_tvk0q_32{animation:.3s cubic-bezier(.16,1,.3,1) forwards _slideOut_tvk0q_1}@keyframes _slideIn_tvk0q_1{0%{opacity:0;transform:translate(100%)}to{opacity:1;transform:translate(0)}}@keyframes _slideOut_tvk0q_1{0%{opacity:1;transform:translate(0)}to{opacity:0;transform:translate(100%)}}:root{--color-bg:#0a0a0f;--color-bg-elevated:#12121a;--color-bg-card:#12121eb3;--color-bg-hover:#1e1e32cc;--color-bg-active:#282841e6;--color-surface:#1a1a2e;--color-border:#ffffff0f;--color-border-hover:#ffffff1f;--color-text:#e8e8f0;--color-text-muted:#8888a8;--color-text-dim:#555570;--color-accent:#76b900;--color-accent-hover:#8ad100;--color-accent-glow:#76b90040;--color-accent-dim:#76b90014;--color-danger:#f44;--color-danger-dim:#ff44441a;--color-warning:#fa0;--color-warning-dim:#ffaa001a;--color-success:#0f8;--color-success-dim:#00ff881a;--color-info:#06b6d4;--color-info-dim:#06b6d41a;--tier-splus:gold;--tier-s:#ff8c00;--tier-aplus:#00c8ff;--tier-a:#3ddc84;--tier-aminus:#7ecf7e;--tier-bplus:#a8a8c8;--tier-b:#808098;--tier-c:#606078;--verdict-perfect:#0f8;--verdict-normal:#76b900;--verdict-slow:#fa0;--verdict-spiky:#f60;--verdict-veryslow:#f44;--verdict-overloaded:#f22;--verdict-unstable:#c00;--verdict-notactive:#555570;--verdict-pending:#444460;--font-sans:"Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;--font-mono:"JetBrains Mono", "Fira Code", "Cascadia Code", monospace;--header-h:60px;--sidebar-w:64px;--sidebar-w-expanded:200px;--radius-sm:6px;--radius-md:10px;--radius-lg:16px;--radius-xl:20px;--shadow-sm:0 1px 3px #0000004d;--shadow-md:0 4px 12px #0006;--shadow-lg:0 8px 32px #00000080;--shadow-glow:0 0 30px var(--color-accent-glow);--ease-out:cubic-bezier(.16, 1, .3, 1);--transition-fast:.15s var(--ease-out);--transition-medium:.25s var(--ease-out);--transition-slow:.4s var(--ease-out)}[data-theme=light]{--color-bg:#f5f5fa;--color-bg-elevated:#fff;--color-bg-card:#ffffffd9;--color-bg-hover:#f0f0f8e6;--color-bg-active:#e6e6f2f2;--color-surface:#eeeef4;--color-border:#00000014;--color-border-hover:#00000026;--color-text:#1a1a2e;--color-text-muted:#668;--color-text-dim:#99a;--color-accent-glow:#76b90026;--color-accent-dim:#76b9000d;--color-danger-dim:#ff444414;--color-warning-dim:#ffaa0014;--color-success-dim:#00ff8814;--color-info-dim:#06b6d414;--shadow-sm:0 1px 3px #00000014;--shadow-md:0 4px 12px #0000001a;--shadow-lg:0 8px 32px #0000001f}*,:before,:after{box-sizing:border-box;margin:0;padding:0}html{scroll-behavior:smooth;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;font-size:14px}body{font-family:var(--font-sans);background:var(--color-bg);color:var(--color-text);min-height:100vh;display:flex;position:relative;overflow-x:hidden}a{color:var(--color-accent);text-decoration:none}a:hover{color:var(--color-accent-hover)}code{font-family:var(--font-mono);background:var(--color-surface);border-radius:4px;padding:2px 6px;font-size:12px}.bg-grid{z-index:0;pointer-events:none;background-image:linear-gradient(#ffffff05 1px,#0000 1px),linear-gradient(90deg,#ffffff05 1px,#0000 1px);background-size:60px 60px;position:fixed;inset:0}.bg-glow{z-index:0;pointer-events:none;filter:blur(120px);opacity:.3;border-radius:50%;animation:20s ease-in-out infinite float;position:fixed}.bg-glow--1{background:radial-gradient(circle, var(--color-accent) 0%, transparent 70%);width:600px;height:600px;top:-200px;left:-200px}.bg-glow--2{background:radial-gradient(circle,#6366f1 0%,#0000 70%);width:400px;height:400px;animation-delay:-7s;top:50%;right:-150px}.bg-glow--3{background:radial-gradient(circle,#06b6d4 0%,#0000 70%);width:500px;height:500px;animation-delay:-14s;bottom:-200px;left:30%}@keyframes float{0%,to{transform:translate(0)scale(1)}33%{transform:translate(30px,-30px)scale(1.05)}66%{transform:translate(-20px,20px)scale(.95)}}[data-theme=light] .bg-glow{opacity:.12}[data-theme=light] .bg-grid{background-image:linear-gradient(#0000000a 1px,#0000 1px),linear-gradient(90deg,#0000000a 1px,#0000 1px)}.app-content{margin-left:var(--sidebar-w);z-index:1;min-height:100vh;transition:margin-left var(--transition-medium);flex-direction:column;flex:1;display:flex;position:relative}.view{flex-direction:column;flex:1;display:flex}::-webkit-scrollbar{width:6px}::-webkit-scrollbar-track{background:0 0}::-webkit-scrollbar-thumb{background:var(--color-border);border-radius:3px}::-webkit-scrollbar-thumb:hover{background:var(--color-text-dim)}@media (width<=768px){.app-content{margin-left:0}}