labtasker-webui 0.4.0__tar.gz → 0.5.0__tar.gz

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 (57) hide show
  1. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/PKG-INFO +13 -3
  2. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/README.md +12 -2
  3. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/docs/reference/specification.md +57 -6
  4. labtasker_webui-0.5.0/frontend/dist/assets/index-BzQ_Vgpz.css +1 -0
  5. labtasker_webui-0.5.0/frontend/dist/assets/index-ytN9gsNN.js +57 -0
  6. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/frontend/dist/index.html +2 -2
  7. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/frontend/package-lock.json +32 -32
  8. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/frontend/package.json +6 -6
  9. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/pyproject.toml +1 -1
  10. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/src/labtasker_webui/__init__.py +1 -1
  11. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/src/labtasker_webui/profile.py +1 -0
  12. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/src/labtasker_webui/schemas.py +3 -0
  13. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/tests/test_observations.py +38 -0
  14. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/tests/test_upstream_source.py +22 -3
  15. labtasker_webui-0.4.0/frontend/dist/assets/index-BLS-SDvc.js +0 -56
  16. labtasker_webui-0.4.0/frontend/dist/assets/index-V-48iAs9.css +0 -1
  17. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/.agents/skills/release/SKILL.md +0 -0
  18. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/.agents/skills/ui-change/SKILL.md +0 -0
  19. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/.agents/sops/development.md +0 -0
  20. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/.agents/sops/qa.md +0 -0
  21. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/.agents/sops/releasing.md +0 -0
  22. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/.gitignore +0 -0
  23. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/AGENTS.md +0 -0
  24. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/CONTRIBUTING.md +0 -0
  25. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/LICENSE +0 -0
  26. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/assets/screenshot.png +0 -0
  27. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/docs/releases/v0.2.0.md +0 -0
  28. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/docs/releases/v0.2.1.md +0 -0
  29. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/docs/releases/v0.4.0.md +0 -0
  30. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/frontend/dist/labtasker-icon.png +0 -0
  31. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/scripts/check_package.py +0 -0
  32. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/scripts/check_versions.py +0 -0
  33. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/src/labtasker_webui/__main__.py +0 -0
  34. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/src/labtasker_webui/app.py +0 -0
  35. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/src/labtasker_webui/cli.py +0 -0
  36. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/src/labtasker_webui/compatibility.py +0 -0
  37. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/src/labtasker_webui/config.py +0 -0
  38. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/src/labtasker_webui/local.py +0 -0
  39. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/src/labtasker_webui/operations.py +0 -0
  40. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/src/labtasker_webui/py.typed +0 -0
  41. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/src/labtasker_webui/security.py +0 -0
  42. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/src/labtasker_webui/sessions.py +0 -0
  43. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/src/labtasker_webui/upstream.py +0 -0
  44. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/tests/fixture_server.py +0 -0
  45. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/tests/packaging_smoke.py +0 -0
  46. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/tests/test_app.py +0 -0
  47. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/tests/test_compatibility.py +0 -0
  48. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/tests/test_config.py +0 -0
  49. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/tests/test_custom_columns.py +0 -0
  50. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/tests/test_local.py +0 -0
  51. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/tests/test_name_fuzzy.py +0 -0
  52. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/tests/test_operations.py +0 -0
  53. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/tests/test_profile.py +0 -0
  54. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/tests/test_real_server.py +0 -0
  55. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/tests/test_release_versions.py +0 -0
  56. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/tests/test_sessions.py +0 -0
  57. {labtasker_webui-0.4.0 → labtasker_webui-0.5.0}/tests/test_upstream.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: labtasker-webui
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: A focused web interface for Labtasker v2
5
5
  Project-URL: Homepage, https://github.com/luocfprime/labtasker-webui
6
6
  Project-URL: Repository, https://github.com/luocfprime/labtasker-webui
@@ -36,8 +36,10 @@ The key features are:
36
36
  - **Routes and Workers:** Collapse route navigation when you need more table space.
37
37
  Inspect Idle/Busy Worker counts and last-seen reports; include inactive routes when
38
38
  reviewing historical work. Resize Worker columns or double-click them to fit loaded
39
- content. These features require Server 2.2 or later; Worker reports require Workers
40
- that support observations.
39
+ content. Open a Worker to inspect its metadata and latest telemetry, filter those
40
+ JSON fields, or add selected `metadata.*` and `telemetry.*` paths as columns when
41
+ the Server supplies them. These features require Server 2.2 or later; Worker reports
42
+ require Workers that support observations.
41
43
  - **Flexible result exploration:** Filter Task fields and nested values, or add
42
44
  custom columns such as `args.test_num` and `result.succ_rate`. Resize, reorder,
43
45
  and double-click columns to fit their contents.
@@ -129,6 +131,14 @@ remain user-defined. A running Task without a valid `completed / total` ratio
129
131
  shows `—`; non-running rows leave the Progress column blank. The Progress column
130
132
  starts compact and can be resized like the other data columns.
131
133
 
134
+ Hover or focus a determinate ring to see its exact values, execution Duration,
135
+ ETA and reported data as a compact JSON tree. A numeric `eta` in the progress
136
+ object means seconds remaining when that snapshot was reported; an ISO timestamp
137
+ means the estimated finish time. Without a valid reported `eta`, the WebUI shows
138
+ a coarse linear estimate based on the completed ratio and elapsed time at the
139
+ latest progress report. This display-only fallback is not stored, filterable or
140
+ available as a column; reported `progress.eta` remains ordinary Task data.
141
+
132
142
  ## Project profiles
133
143
 
134
144
  On loopback binds, the WebUI remembers settings in its working directory:
@@ -17,8 +17,10 @@ The key features are:
17
17
  - **Routes and Workers:** Collapse route navigation when you need more table space.
18
18
  Inspect Idle/Busy Worker counts and last-seen reports; include inactive routes when
19
19
  reviewing historical work. Resize Worker columns or double-click them to fit loaded
20
- content. These features require Server 2.2 or later; Worker reports require Workers
21
- that support observations.
20
+ content. Open a Worker to inspect its metadata and latest telemetry, filter those
21
+ JSON fields, or add selected `metadata.*` and `telemetry.*` paths as columns when
22
+ the Server supplies them. These features require Server 2.2 or later; Worker reports
23
+ require Workers that support observations.
22
24
  - **Flexible result exploration:** Filter Task fields and nested values, or add
23
25
  custom columns such as `args.test_num` and `result.succ_rate`. Resize, reorder,
24
26
  and double-click columns to fit their contents.
@@ -110,6 +112,14 @@ remain user-defined. A running Task without a valid `completed / total` ratio
110
112
  shows `—`; non-running rows leave the Progress column blank. The Progress column
111
113
  starts compact and can be resized like the other data columns.
112
114
 
115
+ Hover or focus a determinate ring to see its exact values, execution Duration,
116
+ ETA and reported data as a compact JSON tree. A numeric `eta` in the progress
117
+ object means seconds remaining when that snapshot was reported; an ISO timestamp
118
+ means the estimated finish time. Without a valid reported `eta`, the WebUI shows
119
+ a coarse linear estimate based on the completed ratio and elapsed time at the
120
+ latest progress report. This display-only fallback is not stored, filterable or
121
+ available as a column; reported `progress.eta` remains ordinary Task data.
122
+
113
123
  ## Project profiles
114
124
 
115
125
  On loopback binds, the WebUI remembers settings in its working directory:
@@ -118,12 +118,35 @@ loop. The Status column has a 104 px minimum so every status badge remains fully
118
118
  visible, including when an older saved layout requested a narrower width.
119
119
 
120
120
  Hovering or focusing the ring opens a viewport-contained summary with the exact
121
- completed and total values, progress attempt, Server report time and a generic
122
- JSON preview. Clicking pins the same summary until an outside interaction or
123
- Escape. Clicking inside the summary does not open the Task drawer. Escape works
124
- from the summary's contents and returns focus to the ring. Task row keyboard
125
- navigation only handles keys on the row itself; Space on its checkbox toggles
126
- selection without opening details. The UI assigns no meaning to other progress keys. Task details show a
121
+ completed and total values, current execution Duration, ETA, progress attempt,
122
+ Server report time and a compact JSON tree of the reported object. The tree
123
+ reuses the Task drawer's nested rendering, expands its first two levels, omits
124
+ raw and per-node copy controls, provides one visible whole-object Copy action,
125
+ keeps the root disclosure inset from both edges, and scrolls within a bounded height. Clicking pins
126
+ the summary as a non-modal dialog so its tree can be operated until an outside
127
+ interaction or Escape. Clicking inside the summary does not open the Task
128
+ drawer. Escape works from the summary's contents and returns focus to the ring.
129
+
130
+ ETA is summary-only derived UI state: it adds no column, Task field, JSON value,
131
+ filter, sort key, custom path, profile setting or saved-view state. A finite
132
+ nonnegative numeric `progress.eta` is interpreted as seconds remaining at
133
+ `progress_updated_at`; an ISO timestamp string is interpreted as the estimated
134
+ finish time. Either valid reported form takes precedence and remains ordinary
135
+ Worker-reported JSON, so `progress.eta` stays visible in the tree and available
136
+ to upstream `progress.*` filtering. An invalid or absent reported ETA falls back
137
+ to linear extrapolation only when the current attempt has valid timestamps and
138
+ `0 < completed < total`: the average rate uses
139
+ `progress_updated_at - started_at`, never the current browser time. The resulting
140
+ finish time is anchored at `progress_updated_at`; the browser clock only counts
141
+ down from that fixed estimate while the summary is open. Derived values are
142
+ coarsened to minute-scale display and marked approximate. Zero completion shows
143
+ Calculating, completed equal to total shows Finishing, a passed estimate shows
144
+ Overdue, and invalid inputs show an em dash.
145
+
146
+ Task row keyboard navigation only handles keys on the row itself; Space on its
147
+ checkbox toggles selection without opening details. Apart from the display
148
+ conventions for `completed`, `total` and `eta`, the UI assigns no meaning to other
149
+ progress keys. Task details show a
127
150
  Progress JSON section immediately before Result whenever progress is non-null,
128
151
  including for terminal Tasks whose last snapshot was retained. Its heading
129
152
  shows the Server-owned attempt and report time. Null progress adds no details
@@ -246,6 +269,34 @@ default widths are 240, 100, 200, 240 and 210 px respectively. Pointer dragging
246
269
  Left/Right keyboard adjustment change only the chosen column; double-click fits that
247
270
  column to loaded content. Widths persist per connection and Queue.
248
271
 
272
+ Worker observations may include strict JSON-object `metadata`, nullable strict
273
+ JSON-object `telemetry`, and nullable Server-owned `telemetry_updated_at`. Older
274
+ Servers that omit these fields remain readable as empty metadata and absent
275
+ telemetry. The five existing Worker columns remain the default; telemetry and
276
+ metadata do not add default columns or acquire WebUI-defined metric semantics.
277
+ The Worker Columns control can hide and reorder built-in columns and add explicit
278
+ `metadata.*` or `telemetry.*` JSON paths. Missing paths remain blank. Worker column
279
+ visibility, custom paths, order and widths persist per connection and Queue,
280
+ independently of Task columns and named Task views.
281
+
282
+ The Worker toolbar supports the existing Worker status selector plus an applied
283
+ advanced expression. The expression is combined with the selected route and status,
284
+ stored in browser history as Worker-only state, and accepts the upstream Worker
285
+ filter contract, including `metadata.*` and `telemetry.*` paths. Text applies on
286
+ Enter or leaving the input group; Apply remains a fallback and IME composition is
287
+ respected.
288
+
289
+ Opening a Worker row displays a resizable details drawer containing its observation
290
+ fields, Metadata and Telemetry JSON trees, telemetry report time, and raw observation.
291
+ It reuses the Task drawer's Tree/Raw and whole-object Copy behaviors. The drawer is
292
+ restorable through browser history by querying the active Worker list with the exact
293
+ Worker ID; when the observation has expired, it reports that absence rather than
294
+ inventing retained history. Opening the associated Task replaces the visible drawer,
295
+ and Back restores the Worker drawer while the observation remains active. Closing a
296
+ drawer restores focus to its originating row or link. Drawer headers constrain long
297
+ titles so the fixed-size close button remains fully inside the right padding at every
298
+ supported viewport.
299
+
249
300
  Change opens connection settings without discarding the current workspace. Back to
250
301
  workspace restores it; a successful connection change clears the query cache.
251
302
  Back to workspace is disabled while a connection change is pending. If the change
@@ -0,0 +1 @@
1
+ :root{--font-ui:-apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", "Microsoft YaHei", sans-serif;--font-code:ui-monospace, "SFMono-Regular", Menlo, Consolas, monospace;--text-small:11px;--text-body:12px;--text-title:22px;--header-height:40px;--control-height:32px;font-family:var(--font-ui);font-size:var(--text-body);color:#202631;font-synthesis:none;--blue:#1769d2;--line:#dfe3e8;--muted:#687385;--panel:#fff;--red:#c93636;background:#f8f9fb;font-weight:400;line-height:1.5}*{box-sizing:border-box}#toast{overflow-wrap:anywhere;color:#344054;max-width:min(420px,100vw - 24px);font-size:var(--text-body);opacity:0;pointer-events:none;background:#fff;border:1px solid #cbd8e8;border-radius:7px;padding:11px 14px;transition:all .16s;position:relative;transform:translateY(8px);box-shadow:0 10px 30px #20395624}#toast:not(.visible){border:0;width:0;height:0;padding:0;position:absolute;overflow:hidden}#toast.visible{opacity:1;transform:none}body{min-width:320px;margin:0}button,input,select{font:inherit;letter-spacing:inherit}input,select{font-weight:400}h1,h2,h3,strong,b{font-weight:600}button{cursor:pointer;white-space:nowrap}:where(button,input,select,summary,a,[tabindex]):focus-visible{outline:2px solid var(--blue);outline-offset:3px}.top{height:var(--header-height);border-bottom:1px solid var(--line);z-index:8;background:#fff;justify-content:space-between;align-items:center;padding:0 20px;display:flex;position:sticky;top:0}.brand{align-items:center;gap:8px;font-size:17px;line-height:24px;display:flex}.logo{object-fit:contain;flex-shrink:0;width:24px;height:24px;display:block}.server{font-size:var(--text-body);color:var(--muted);align-items:center;gap:8px;display:flex}.live{background:#2c9a67;border-radius:50%;width:8px;height:8px}.page,.workspace{max-width:none;margin:auto;padding:14px 20px 16px}.page-head,.workspace-head{justify-content:space-between;align-items:flex-start;gap:16px;margin-bottom:18px;display:flex}.eyebrow{letter-spacing:.13em;color:#6d788a;font-size:11px;font-weight:600}.page h1,.workspace h1{font-size:var(--text-title);letter-spacing:-.025em;margin:5px 0 4px;font-weight:600;line-height:1.25}.page-head p,.workspace-head p{color:var(--muted);font-size:var(--text-body);margin:0}.primary,.secondary,.toolbar button,.pager button,.actions button,.modal-actions button{color:#303846;min-height:var(--control-height);font-weight:500;font-size:var(--text-body);background:#fff;border:1px solid #cdd3da;border-radius:6px;padding:0 14px;line-height:1.25}.primary,.toolbar button.primary{background:var(--blue);border-color:var(--blue);color:#fff}.toolbar button.danger-link:not(:disabled){color:var(--red)}.secondary{flex-shrink:0;align-items:center;gap:8px;display:flex}.compact{min-height:var(--control-height)}.link,.danger-link{color:var(--blue);font-size:var(--text-body);background:0 0;border:0;padding:3px}.danger-link{color:var(--red)}button:disabled{opacity:.45;cursor:default}.spinner{border:2px solid;border-right-color:#0000;border-radius:50%;width:14px;height:14px;animation:.8s linear infinite spin;display:inline-block}@keyframes spin{to{transform:rotate(360deg)}}.loading{text-align:center;color:var(--muted);justify-content:center;gap:10px;padding:50px;display:flex}.queue-grid{grid-template-columns:repeat(auto-fill,minmax(min(100%,430px),1fr));gap:18px;display:grid}.queue-card{border:1px solid var(--line);cursor:pointer;background:#fff;border-radius:8px;padding:16px;transition:all .15s}.queue-card:hover,.queue-card:focus{border-color:#94b9e8;box-shadow:0 4px 18px #182a4410}.queue-title{justify-content:space-between;align-items:flex-start;margin-bottom:14px;display:flex}.queue-title h2{letter-spacing:-.015em;margin:0 0 3px;font-size:18px;line-height:1.35}.queue-title span{color:var(--muted);font-size:var(--text-small)}.queue-title .arrow{color:#1769d2;font-size:19px}.status-row{border:1px solid #e4e7eb;border-radius:6px;grid-template-columns:repeat(5,1fr);display:grid;overflow:hidden}.status-row button{text-align:left;background:#fbfcfd;border:0;border-right:1px solid #e4e7eb;padding:9px 8px}.status-row button:last-child{border:0}.status-row i{border-radius:50%;width:6px;height:6px;margin-right:5px;display:inline-block}.status-row b{font-size:var(--text-body)}.status-row span{color:var(--muted);margin:3px 0 0 11px;font-size:11px;display:block}.pending{--state:#c18a18}.running{--state:#1776df}.succeeded{--state:#23855b}.failed{--state:#d04545}.cancelled{--state:#87919f}.status-row i,.badge i{background:var(--state)}.progress{background:#edf0f3;border-radius:5px;height:5px;margin:18px 0 12px;overflow:hidden}.progress span{background:#4b98e9;height:100%;display:block}.queue-foot{font-size:var(--text-small);color:var(--muted);flex-direction:column;align-items:stretch;gap:12px;min-height:25px;display:flex}.queue-metrics{font-variant-numeric:tabular-nums;justify-content:space-between;gap:14px;display:flex}.recent-task{text-overflow:ellipsis;white-space:nowrap;max-width:100%;overflow:hidden}.badge{color:#3e4755;text-transform:capitalize;white-space:nowrap;background:#f2f4f6;border-radius:12px;align-items:center;gap:6px;padding:3px 8px;font-size:11px;display:inline-flex}.badge i{border-radius:50%;width:6px;height:6px}.duration{white-space:nowrap;font-variant-numeric:tabular-nums;align-items:center;gap:5px;display:inline-flex}.duration i{background:#2c9a67;border-radius:50%;width:6px;height:6px}.empty{text-align:center;color:var(--muted);padding:70px 20px}.empty h2{color:#333b47;margin:10px 0 4px;font-size:18px}.empty p{font-size:var(--text-body);margin:0}.empty-icon{color:#9aa3af;font-size:28px}.connect{background:linear-gradient(115deg,#f6f9fd,#fff);place-items:center;min-height:100vh;display:grid}.connect-card{border:1px solid var(--line);background:#fff;border-radius:10px;width:min(450px,100vw - 32px);padding:32px;box-shadow:0 18px 50px #1d365318}.connect-copy{margin:40px 0 25px}.connect-copy h1{font-size:var(--text-title);letter-spacing:-.025em;margin:7px 0;line-height:1.25}.connect-copy p,.privacy{color:var(--muted);font-size:var(--text-body);line-height:1.55}.connect form label{font-size:var(--text-body);margin:16px 0;font-weight:500;display:block}.connect label span{float:right;color:#8b94a2;font-weight:400}.connect input{font-size:var(--text-body);border:1px solid #ccd3dc;border-radius:6px;width:100%;height:40px;margin-top:7px;padding:0 11px;font-weight:400;display:block}.connect form .primary{width:100%;margin-top:8px}.privacy{text-align:center;margin:20px 20px 0}.error,.stale{color:#9b3030;font-size:var(--text-small);background:#fff1f1;border:1px solid #f0cccc;border-radius:5px;margin:10px 0;padding:10px 12px}.field-error{color:#a23838;font-size:var(--text-small);margin:-6px 10px 12px}.stale{flex-wrap:wrap;align-items:center;gap:8px;display:flex}.stale>div{overflow-wrap:anywhere;flex:280px;min-width:0}.stale button{color:inherit;background:0 0;border:1px solid;border-radius:5px;flex:none;padding:5px 9px}.stale{color:#7f651a;background:#fff8e5;border-color:#eadca7}.splash{place-content:center;justify-items:center;gap:25px;height:100vh;display:grid}.crumb{font-size:var(--text-body);color:var(--muted);align-items:center;gap:10px;margin-bottom:8px;display:flex}.crumb button{color:var(--blue);background:0 0;border:0;padding:0}.workspace-head{margin-bottom:12px}.stats{border:1px solid var(--line);background:#fff;border-radius:7px;grid-template-columns:repeat(5,1fr);margin-bottom:14px;display:grid}.stats button{border:0;border-right:1px solid var(--line);text-align:left;background:0 0;padding:8px 12px}.stats button:last-child{border:0}.stats span{color:var(--muted);text-transform:capitalize;font-size:11px}.stats strong{font-variant-numeric:tabular-nums;margin-top:2px;font-size:18px;line-height:1.3;display:block}.toolbar{border:1px solid var(--line);background:#fff;border-radius:7px;flex-wrap:wrap;align-items:center;gap:8px;margin-bottom:12px;padding:10px;display:flex}.toolbar input:not([type=checkbox]),.toolbar select{height:var(--control-height);font-size:var(--text-body);background:#fff;border:1px solid #ccd3dc;border-radius:5px;padding:0 10px}.toolbar .filter{flex:180px;min-width:0}.column-menu{flex:none;position:relative}.column-menu summary,.column-menu>button{height:var(--control-height);color:#303846;font-size:var(--text-body);cursor:pointer;background:#fff;border:1px solid #ccd3dc;border-radius:5px;align-items:center;padding:0 10px;list-style:none;display:flex}.column-menu>div{z-index:20;border:1px solid var(--line);background:#fff;border-radius:6px;width:176px;max-height:min(280px,40dvh);padding:4px;position:fixed;top:calc(100% + 6px);left:0;overflow-y:auto;box-shadow:0 8px 22px #1d36531c}.column-menu summary,.column-menu>button{gap:12px}.column-menu summary::-webkit-details-marker{display:none}.column-menu summary:after,.column-menu>button:after{content:"";border-bottom:1.5px solid;border-right:1.5px solid;width:6px;height:6px;transform:translateY(-2px)rotate(45deg)}.column-menu[open] summary,.column-menu>button[aria-expanded=true]{background:#eceef1}.column-menu input[type=checkbox]{width:14px;height:14px;accent-color:var(--blue);flex:0 0 14px;margin:0;padding:0}.column-menu label:hover,.column-menu label:focus-within{background:#f3f5f7}.column-menu label{cursor:pointer;color:#303846;min-height:32px;font-size:var(--text-body);border-radius:3px;align-items:center;gap:10px;padding:6px 10px;display:flex}.selection{min-height:45px;font-size:var(--text-body);background:#f0f7ff;border:1px solid #a9caef;border-radius:6px;align-items:center;gap:10px;margin-bottom:10px;padding:0 14px;display:flex}.selection-summary{flex-direction:column;min-width:0;display:flex}.selection-summary small{color:var(--red);overflow-wrap:anywhere}.selection-actions{flex-wrap:wrap;align-items:center;gap:8px;margin-left:auto;display:flex}.selection-action-hint{display:inline-flex}.priority-editor{border-left:1px solid #cbdcf0;align-items:center;gap:4px;padding-left:8px;display:inline-flex}.priority-editor input{color:#242a34;width:82px;height:28px;font:inherit;font-variant-numeric:tabular-nums;background:#fff;border:1px solid #b9cce3;border-radius:4px;padding:0 7px}.table-wrap{border:1px solid var(--line);background:#fff;border-radius:7px;min-height:260px;overflow:auto}table{border-collapse:collapse;width:100%;line-height:1.35;font-size:var(--text-body)}th{text-align:left;color:#657083;border-bottom:1px solid var(--line);white-space:nowrap;background:#f7f8fa;padding:8px 10px;font-weight:500}td{color:#4c5665;white-space:nowrap;border-bottom:1px solid #edf0f2;padding:5px 10px}tbody tr{cursor:pointer}tbody tr:hover,tbody tr.chosen{background:#f4f8fd}td b{color:#2b3440;font-size:var(--text-body);margin-bottom:1px;display:block}td code{display:block}td code,.drawer code{color:var(--muted);font-family:var(--font-code);font-size:11px;font-weight:400;line-height:1.5}td[data-column-id=progress],th[data-column-id=progress]{text-align:center;padding-left:0;padding-right:0}.task-progress{place-items:center;display:inline-grid}.progress-trigger{width:24px;height:24px;color:var(--blue);background:0 0;border:0;border-radius:50%;place-items:center;padding:0;display:grid}.progress-trigger:hover{background:#1769d20a}.progress-ring{place-items:center;width:14px;height:14px;display:grid;position:relative}.progress-ring svg{width:100%;height:100%;position:absolute;inset:0;transform:rotate(-90deg)}.progress-ring circle{fill:none;stroke-width:5.14286px}.progress-ring-track{stroke:#e6e9ed}.progress-ring-value{stroke:color-mix(in srgb, currentColor 82%, #fff);stroke-linecap:round;transition:stroke-dasharray .25s}.progress-unavailable{color:var(--muted)}.progress-popover{z-index:10000;color:#263140;text-align:left;white-space:normal;background:#fff;border:1px solid #d5dbe3;border-radius:7px;padding:12px;position:fixed;overflow:auto;box-shadow:0 8px 24px #18223026}.progress-popover>strong{color:var(--blue);font-variant-numeric:tabular-nums;margin-bottom:8px;font-size:15px;display:block}.progress-popover dl{grid-template-columns:78px 1fr;gap:5px 10px;margin:0 0 10px;display:grid}.progress-popover dt{color:var(--muted)}.progress-popover dd{margin:0}.progress-popover .duration,.progress-popover .eta{white-space:nowrap}.progress-data-title{color:var(--muted);font-size:var(--text-small);justify-content:space-between;align-items:center;margin-bottom:5px;font-weight:600;display:flex}.progress-data-title .link{font-size:inherit;font-weight:400}.json-tree.progress-json-tree{background:#f8fafc;border:1px solid #e4e7eb;border-radius:5px;max-height:220px;margin:0;padding:10px 16px 12px 20px;font-size:11px;overflow:auto}.chips{gap:3px;display:flex}.chips span{background:#f8f9fa;border:1px solid #dce1e7;border-radius:4px;padding:2px 5px}.pager{color:var(--muted);font-size:var(--text-small);justify-content:space-between;align-items:center;margin-top:13px;display:flex}.pager button{margin-left:7px}.drawer{right:0;top:var(--header-height);z-index:7;background:#fff;border-left:1px solid #ccd3da;flex-direction:column;width:min(600px,100vw);display:flex;position:fixed;bottom:0;box-shadow:-12px 0 35px #253b581a}.drawer-resize{z-index:2;cursor:col-resize;outline:none;width:10px;position:absolute;inset:0 auto 0 -5px}.drawer-resize:hover,.drawer-resize:focus-visible{border-left:2px solid var(--blue)}.drawer-head{border-bottom:1px solid var(--line);justify-content:space-between;align-items:flex-start;gap:12px;padding:20px 24px;display:flex}.drawer-head>div{overflow-wrap:anywhere;flex:auto;min-width:0}.drawer-head h2{letter-spacing:-.015em;margin:5px 0 3px;font-size:20px;line-height:1.3}.close{color:#687385;background:0 0;border:0;border-radius:6px;flex:0 0 32px;width:32px;height:32px;margin-left:auto;padding:0;position:relative}.close:before,.close:after{content:"";background:currentColor;border-radius:999px;width:14px;height:1.5px;position:absolute;top:15px;left:9px}.close:before{transform:rotate(45deg)}.close:after{transform:rotate(-45deg)}.close:hover{color:#394454;background:#f3f5f7}.close:active{background:#e9edf1}.drawer-body{padding:20px 24px 60px;overflow:auto}.task-summary{justify-content:space-between;align-items:center;margin-bottom:15px;display:flex}.actions{gap:7px;display:flex}.detail-section{border-top:1px solid var(--line);padding:18px 0}.detail-section h3{font-size:var(--text-body);margin:0 0 13px}.detail-section dl{font-size:var(--text-body);grid-template-columns:110px 1fr;gap:9px;margin:0;display:grid}.detail-section dt{color:var(--muted)}.detail-section dd{margin:0}.section-title{align-items:center;display:flex}.section-heading{align-items:baseline;gap:8px;min-width:0;margin-right:auto;display:flex}.section-heading h3{margin:0}.section-meta{color:var(--muted);font-size:var(--text-small)}@media (prefers-reduced-motion:reduce){.progress-ring-value{transition:none}}.detail-section pre{max-height:300px;font:12px/1.6 var(--font-code);background:#f5f7f9;border:1px solid #e4e7eb;border-radius:6px;padding:13px;overflow:auto}.json-tree{max-height:420px;font:12px/1.6 var(--font-code);background:#f8fafc;border:1px solid #e4e7eb;border-radius:6px;padding:10px 12px;overflow:auto}.json-node summary{cursor:pointer;color:#4b5665;list-style-position:outside}.json-node>.json-copy,.json-leaf button{visibility:hidden;color:var(--blue);font:inherit;background:0 0;border:0;margin-left:8px;padding:0}.json-node:hover>.json-copy,.json-node:focus-within>.json-copy,.json-leaf:hover button,.json-leaf:focus-within button{visibility:visible}.json-node{position:relative}.json-node>.json-copy{position:absolute;top:0;right:0}.json-children{border-left:1px solid #dce2e9;margin-left:7px;padding-left:16px}.json-key{color:#7652a2}.json-key:after{content:":"}.json-kind,.json-empty,.json-null{color:#87919f}.json-string{color:#27704e;overflow-wrap:anywhere}.json-scalar{color:#1769d2}.json-leaf{min-height:20px}.json-leaf a{color:var(--blue);overflow-wrap:anywhere}.failure{color:#9f3333}.modal-back{z-index:20;background:#26314066;place-items:center;display:grid;position:fixed;inset:0}.modal{z-index:21;background:#fff;border-radius:9px;width:min(460px,100vw - 32px);max-height:calc(100dvh - 32px);padding:26px;position:fixed;top:50%;left:50%;overflow:auto;transform:translate(-50%,-50%);box-shadow:0 24px 70px #1119}.batch-modal{width:min(620px,100vw - 32px)}.delete-scope{border:1px solid var(--line);font-size:var(--text-small);background:#f8f9fb;border-radius:6px;grid-template-columns:78px 1fr;gap:7px 12px;margin:18px 0;padding:12px;display:grid}.delete-scope dt{color:var(--muted)}.delete-scope dd{overflow-wrap:anywhere;margin:0}.operation-counts{color:var(--muted);gap:18px;display:flex}.failure-list{max-height:180px;overflow:auto}.failure-list .error{grid-template-columns:150px 1fr;gap:8px;display:grid}.modal h2{margin:8px 0;font-size:20px}.modal p{font-size:var(--text-body);color:var(--muted);line-height:1.55}.danger-mark{width:34px;height:var(--control-height);color:var(--red);background:#fff0f0;border-radius:50%;place-items:center;font-weight:700;display:grid}.modal-actions{justify-content:flex-end;gap:8px;margin-top:22px;display:flex}.danger{color:#fff!important;background:#c93636!important;border-color:#c93636!important}.operation{margin-top:20px}.operation>div:first-child{font-size:var(--text-small);justify-content:space-between;display:flex}.operation .progress{margin-top:8px}@media (width<=900px){body{min-width:0}.page,.workspace{padding:16px 18px}.queue-grid{grid-template-columns:1fr}.drawer{z-index:10;top:0;width:100%!important}.drawer-resize{display:none}.top{padding:0 18px}.status-row,.stats{grid-template-columns:repeat(5,minmax(65px,1fr));overflow:auto}.toolbar{flex-wrap:wrap}.toolbar .filter{flex-basis:100%}.table-wrap{overflow:auto}}@media (width<=600px){.top{height:auto;min-height:var(--header-height);flex-wrap:wrap;gap:10px;padding:8px 18px}.server{overflow-wrap:anywhere;flex-wrap:wrap;min-width:0}.live{flex:0 0 8px}.queue-card{min-width:0;padding:16px}.status-row,.stats{grid-template-columns:repeat(5,minmax(0,1fr))}.status-row button,.stats button{text-align:center;min-width:0;padding:10px 4px}.status-row span{margin-left:0}.stats span{font-size:11px}.toolbar{grid-template-columns:repeat(2,minmax(0,1fr));display:grid}.toolbar>*{width:100%;min-width:0}.toolbar .filter{grid-column:1/-1}.column-menu summary{justify-content:center}.column-menu>div{left:auto;right:0}.pager,.selection,.task-summary{flex-wrap:wrap;gap:12px;height:auto}.selection{padding:12px}.pager>div{margin-left:auto}.drawer-head,.drawer-body{padding-left:18px;padding-right:18px}.drawer-head>div{overflow-wrap:anywhere;min-width:0}.close{flex-shrink:0}.detail-section dd{overflow-wrap:anywhere;min-width:0}}@media (prefers-reduced-motion:reduce){*,:before,:after{transition-duration:.01ms!important;animation-duration:.01ms!important}}.workspace{height:calc(100dvh - var(--header-height));flex-direction:column;min-height:560px;display:flex}.workspace>*{flex-shrink:0}.workspace .table-wrap{flex:auto;min-height:180px}.table-wrap th{z-index:1;position:sticky;top:0}.task-name{text-overflow:ellipsis;max-width:clamp(240px,26vw,520px);overflow:hidden}@media (width<=900px){.workspace{height:auto;min-height:calc(100dvh - var(--header-height))}.workspace .table-wrap{flex:none;max-height:65dvh}}.list-summary{color:var(--muted);font-size:var(--text-small);margin-top:8px}.load-more{min-height:1px;color:var(--muted);font-size:var(--text-small);justify-content:center;align-items:center;gap:8px;display:flex}.load-more:not(:empty){padding:12px}.toolbar>input,.toolbar>select,.toolbar>button,.column-menu>summary{box-sizing:border-box;height:var(--control-height);min-height:var(--control-height);font-family:var(--font-ui);font-size:var(--text-body);vertical-align:middle;border:1px solid #ccd3dc;border-radius:5px;margin:0;padding:0 10px;line-height:18px}.toolbar>button,.column-menu>summary{justify-content:center;align-items:center;display:inline-flex}.toolbar>select{appearance:none;background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath d='m3 4.5 3 3 3-3' fill='none' stroke='%23515c6c' stroke-width='1.5'/%3E%3C/svg%3E");background-position:right 9px center;background-repeat:no-repeat;padding-right:28px}.column-menu>summary:after{width:5px;height:5px}td{line-height:16px}td code{line-height:14px}@media (pointer:coarse){.toolbar{--control-height:36px}.column-menu label{min-height:36px}}.virtual-table{table-layout:fixed}.virtual-table tbody tr:not(.virtual-spacer){height:42px}.virtual-table td{text-overflow:ellipsis;overflow:hidden}.virtual-table th[data-column-id=select],.virtual-table td[data-column-id=select]{box-shadow:1px 0 var(--line);position:sticky;left:0}.virtual-table th[data-column-id=select]{z-index:3}.virtual-table td[data-column-id=select]{z-index:1;background:#fff}.virtual-table tbody tr:hover td[data-column-id=select],.virtual-table tbody tr.chosen td[data-column-id=select]{background:#f4f8fd}.virtual-table .task-name{max-width:100%}.virtual-table .chips{overflow:hidden}.virtual-table td code{text-overflow:ellipsis;overflow:hidden}.virtual-spacer td{border:0;padding:0}.column-resizer{cursor:col-resize;touch-action:none;z-index:2;width:7px;position:absolute;inset:0 0 0 auto}.column-resizer:hover,.column-resizer:focus-visible{background:#1769d244}.instant-tooltip{z-index:10000;pointer-events:none;color:#263140;white-space:pre-wrap;overflow-wrap:anywhere;background:#fff;border:1px solid #d5dbe3;border-radius:5px;max-width:min(640px,100vw - 16px);max-height:calc(100vh - 16px);padding:7px 10px;font:12px/1.5 -apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;position:fixed;overflow:hidden;box-shadow:0 3px 12px #18223026}.priority-value{white-space:nowrap;font-variant-numeric:tabular-nums;align-items:baseline;gap:4px;display:inline-flex}.priority-direction{color:#657083}.virtual-table .table-filler,.worker-table .table-filler{border-left:0;border-right:0;padding:0}.ui-select{flex:none;position:relative}.toolbar .ui-select>button{width:100%;height:var(--control-height);min-height:var(--control-height);font:inherit;color:#202631;background:#fff;border:1px solid #ccd4df;border-radius:5px;justify-content:space-between;align-items:center;gap:14px;padding:0 10px;font-weight:400;display:flex}.ui-select-menu{z-index:100;border:1px solid var(--line);width:max-content;min-width:100%;font-size:var(--text-body);background:#fff;border-radius:5px;padding:4px;position:absolute;top:calc(100% + 4px);left:0;box-shadow:0 3px 12px #18223020}.ui-select-menu [role=option]{cursor:pointer;border-radius:3px;justify-content:space-between;align-items:center;gap:20px;min-height:28px;padding:4px 8px;display:flex}.ui-select-menu [role=option].highlighted{background:#edf3fb}.ui-select-menu [aria-selected=true]{color:#1769cc}.priority-direction.positive{color:#c93636}.priority-direction.negative{color:#2275c9}.filter-input{position:relative}.toolbar .filter-input>input{width:100%;padding-right:32px}.toolbar .filter-help-button{color:#687385;background:0 0;border:0;border-radius:4px;width:22px;height:22px;min-height:0;padding:0;font-size:12px;position:absolute;top:5px;right:5px}.toolbar .filter-help-button:hover,.toolbar .filter-help-button[aria-expanded=true]{color:#1769d2;background:#edf3fb}.filter-help{z-index:110;border:1px solid var(--line);background:#fff;border-radius:6px;width:min(460px,100vw - 48px);padding:14px;font-size:12px;line-height:1.5;position:absolute;top:calc(100% + 6px);left:0;box-shadow:0 4px 16px #18223020}.filter-help p{margin:8px 0 0}.filter-help code{font-size:11px}.filter-examples{gap:3px;margin:10px 0;display:grid}.toolbar .filter-examples button{text-align:left;background:#f8f9fb;border:0;border-radius:3px;flex-wrap:wrap;justify-content:space-between;gap:4px 12px;width:100%;height:auto;min-height:30px;padding:5px 7px;font-size:12px;font-weight:400;display:flex}.toolbar .filter-examples button:hover{background:#edf3fb}.column-menu>div{width:370px;max-width:calc(100vw - 24px);max-height:min(520px,65dvh)}.column-list{max-height:360px;overflow-y:auto}.column-choice{align-items:center;gap:3px;display:flex}.column-menu .column-choice label{flex:1;min-width:0;padding:5px 6px}.column-choice label span{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.toolbar .column-choice button{background:0 0;border:0;width:24px;height:26px;min-height:0;padding:0}.custom-column-form{border-top:1px solid var(--line);margin-top:6px;padding:10px 6px 4px}.column-menu .custom-column-form label{padding:0 0 6px}.custom-column-form>div{gap:6px;display:flex}.toolbar .custom-column-form input{width:100%;min-width:0}.custom-column-form small{color:var(--muted);margin-top:6px;display:block}.column-drag-handle{cursor:grab;color:var(--muted);touch-action:none}.column-drag-handle:active{cursor:grabbing}.column-choice.drop-target{box-shadow:inset 0 2px var(--blue);background:0 0}.column-menu .column-choice.drop-target label{background:0 0}.virtual-table th{-webkit-user-select:none;user-select:none}.filter-help{max-height:min(650px,70vh);overflow-y:auto}.save-filter,.saved-filter-row{gap:6px;display:flex}.toolbar .save-filter input{flex:1;width:0;min-width:0}.toolbar .saved-filter-row>button:last-child{flex:none;width:26px}.queue-navigation{justify-content:space-between;align-items:center;gap:12px;margin-bottom:12px;display:flex}.queue-navigation .crumb{min-width:0;margin:0}.queue-navigation .crumb b{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.connection-mode{border:1px solid var(--line);border-radius:4px;padding:0 6px;font-size:11px;line-height:20px}.connection-address{text-overflow:ellipsis;white-space:nowrap;max-width:min(50vw,650px);overflow:hidden}.queue-navigation-actions,.views{align-items:center;gap:6px;display:flex}.views{position:relative}.views .ui-select{width:180px}.views button,.views input{border:1px solid var(--line);color:#303846;background:#fff;border-radius:5px;min-height:32px;padding:0 10px}.views .ui-select>button{justify-content:space-between;align-items:center;gap:8px;width:100%;display:flex}.views .ui-select>button span{text-overflow:ellipsis;overflow:hidden}.views-menu{border:1px solid var(--line);z-index:40;background:#fff;border-radius:6px;width:180px;padding:12px;position:absolute;top:calc(100% + 6px);right:0;box-shadow:0 4px 16px #15223814}.views-menu p{color:var(--muted);margin:6px 0 10px;font-size:12px}.views-menu input{box-sizing:border-box;width:100%}.view-actions{grid-template-columns:1fr 1fr;gap:6px;margin-top:8px;display:grid}@media (width<=600px){.queue-navigation{flex-wrap:wrap}.views-menu{width:min(180px,80vw);left:0;right:auto}}.connect .ui-select>button{border:1px solid var(--line);background:#fff;border-radius:5px;justify-content:space-between;align-items:center;width:100%;min-height:32px;padding:0 10px;display:flex}.connect label small{color:var(--muted);margin-top:6px;font-weight:400;display:block}.view-selector{position:relative}.select-value{align-items:center;gap:6px;min-width:0;display:inline-flex}.select-value-text{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.ui-select>button>svg{flex-shrink:0}.view-dirty{background:var(--blue);border-radius:50%;flex:0 0 6px;width:6px;height:6px;display:inline-block}.views .view-save{color:var(--blue);border-color:#a6c6ed}.views .select-action{border:0;border-top:1px solid var(--line);text-align:left;width:100%;color:var(--blue);border-radius:0;margin-top:4px;padding:6px 8px;display:block}.views-menu{width:180px;padding:4px}.views-menu button{text-align:left;border:0;border-radius:3px;width:100%;font-weight:400;display:block}.views-menu button:not(:disabled):hover,.views-menu button:focus-visible{background:#edf3fb}.views-menu [role=separator]{border-top:1px solid var(--line);margin:4px}.views-menu .view-delete{color:var(--red)}.view-dialog{width:min(380px,100vw - 32px);padding:20px}.view-dialog h2{overflow-wrap:anywhere;font-size:17px}.view-dialog label{margin:16px 0 6px;display:block}.view-dialog input{border:1px solid var(--line);border-radius:5px;width:100%;height:34px;padding:0 10px}.view-dialog .view-name-error{color:var(--red);margin:6px 0}.stats .pending{--count-color:#956600}.stats .running{--count-color:#1768bd}.stats .succeeded{--count-color:#23855b}.stats .failed{--count-color:#c93636}.stats .cancelled{--count-color:#687385}.stats span:before{content:"";background:var(--state);vertical-align:1px;border-radius:50%;width:6px;height:6px;margin-right:6px;display:inline-block}.stats strong{color:var(--count-color)}.stats button:hover{background:color-mix(in srgb, var(--state) 5%, white)}.stats button.active{box-shadow:inset 0 -2px var(--state);background:color-mix(in srgb, var(--state) 9%, white)}.profile-save-error{pointer-events:auto;color:#624719;background:#fff9ed;border:1px solid #e3ba6c;border-radius:6px;align-items:center;gap:12px;width:min(420px,100vw - 24px);padding:10px 12px;font-size:13px;display:flex}.ui-select-menu,.filter-help,.views-menu{overscroll-behavior:contain;overflow-wrap:anywhere;position:fixed;overflow-y:auto}.ui-select-menu [role=option]>span:first-child{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.ui-select-menu [role=option]>span:last-child{flex-shrink:0}.server-version-warning{color:#674d0a;background:#fff8e6;border:1px solid #d7b665;border-radius:6px;align-items:baseline;gap:12px;margin:12px 0;padding:10px 14px;font-size:13px;display:flex}.server-version-warning span{overflow-wrap:anywhere;min-width:0}.server-version-warning button{color:inherit;flex-shrink:0}[hidden]{display:none!important}.queue-workspace{flex:1;grid-template-columns:200px minmax(0,1fr);gap:18px;min-height:0;display:grid}.queue-workspace.routes-collapsed{grid-template-columns:minmax(0,1fr)}.workspace-content,.task-workspace,.workers-panel{flex-direction:column;flex:1;min-width:0;min-height:0;display:flex}.route-sidebar{border-right:1px solid var(--line);flex-direction:column;min-height:0;padding-right:12px;display:flex}.route-sidebar-head{justify-content:space-between;align-items:center;height:34px;display:flex}.route-list{flex:1;min-height:0;overflow:auto}.route-item{text-align:left;background:0 0;border:0;flex-direction:column;gap:4px;width:100%;margin-bottom:3px;padding:9px 10px;display:flex}.route-item>span,.active-route{text-overflow:ellipsis;white-space:nowrap;max-width:100%;overflow:hidden}.route-item small{color:var(--muted);font-size:11px}.route-item.active{color:#245987;background:#edf3fa}.inactive-routes{align-items:center;gap:6px;padding:12px 0;font-size:11px;display:flex}.workspace-tabs{border-bottom:1px solid var(--line);align-items:center;gap:6px;margin-bottom:12px;padding-bottom:7px;display:flex}.workspace-tabs button{background:0 0;border:0}.workspace-tabs button[aria-pressed=true]{color:#245987;background:#eaf0f7}.active-route{color:var(--muted);margin-left:8px}.stats,.worker-stats{background:0 0;border:0;flex-wrap:wrap;gap:6px 18px;margin-bottom:12px;display:flex}.stats button,.worker-stats button{text-align:left;background:0 0;border:0;align-items:baseline;gap:8px;padding:5px 7px;display:inline-flex}.stats strong,.worker-stats strong{font-variant-numeric:tabular-nums;margin:0;font-size:27px;font-weight:600;line-height:1.25;display:inline}.stats span,.worker-stats button{color:var(--muted);font-size:12px}.stats span:before{content:none}.worker-stats strong{color:#28313c}.worker-stats button[aria-pressed=true]{background:#edf3fa}.worker-toolbar{color:var(--muted);align-items:center;gap:12px;margin-bottom:10px;display:flex}.worker-table table{table-layout:fixed}.worker-table td>code,.worker-route,.worker-table td>.link{text-overflow:ellipsis;white-space:nowrap;max-width:100%;display:block;overflow:hidden}.worker-table td{padding:10px;overflow:hidden}.worker-table td small{white-space:normal;overflow-wrap:anywhere;margin-top:4px;display:block}.worker-idle{color:#23855b;background:#edf8f1}.worker-busy{color:#1768bd;background:#edf4ff}.observation-delayed{color:#956600}@media (width<=900px){.queue-workspace{grid-template-columns:160px minmax(0,1fr);gap:10px}.stats,.worker-stats{gap:4px}.stats strong,.worker-stats strong{font-size:23px}.route-list{max-height:65dvh}}@media (width<=600px){.queue-workspace{grid-template-columns:minmax(0,1fr)}.route-sidebar{border-right:0;border-bottom:1px solid var(--line);padding:0}.route-list{max-height:160px}}:root{--text-body:13px;color:#242a34;--blue:#245cc7;--muted:#626d7c;--line:#e3e7ec;background:#fff}.task-name{font-weight:500}.queue-workspace{grid-template-columns:200px minmax(0,1fr);transition:grid-template-columns .14s,gap .14s}.queue-workspace.routes-collapsed{grid-template-columns:0 minmax(0,1fr);gap:0}.route-rail{min-width:0;min-height:0;display:flex;overflow:hidden}.routes-collapsed .route-rail{visibility:hidden}.route-collapse{border:1px solid var(--line);cursor:pointer;background:#fff;border-radius:5px;justify-content:center;align-items:center;width:28px;height:28px;font-size:20px;display:inline-flex}.route-item{border:1px solid #0000;border-radius:6px;transition:background .12s,border-color .12s}.route-item:hover{background:#f5f7fa}.route-item.active{color:#245cc7;background:#edf3ff;border-color:#b7c9ed}.route-name{align-items:center;gap:7px;display:flex}.route-dot{vertical-align:1px;background:#9aa3af;border-radius:50%;flex:0 0 6px;width:6px;height:6px;margin-right:5px;display:inline-block}.route-name .route-dot{margin:0}.route-busy{background:#2674d1}.route-idle{background:#23855b}.route-waiting{background:#ab7917}.route-unknown{background:0 0;box-shadow:inset 0 0 0 1px #9aa3af}.workspace-tabs{gap:12px;padding-bottom:0}.workspace-tabs>button{color:#242a34;border-bottom:2px solid #0000;border-radius:0;padding:8px 13px;font-size:14px;transition:color .12s,background .12s}.workspace-tabs>button:hover{background:#f5f7fa}.workspace-tabs>button[aria-pressed=true]{color:#245cc7;background:0 0;border-bottom-color:#245cc7}.workspace-tabs>.secondary{border:1px solid var(--line);border-radius:5px;align-items:center;gap:7px;margin-bottom:5px;margin-right:4px;padding:5px 9px;font-size:12px;display:inline-flex}.stats{gap:8px 18px;padding:2px 0}.stats .all{--state:#626d7c;--count-color:#242a34}.stats button,.worker-stats button{cursor:pointer;border-radius:6px;padding:6px 9px;transition:background .12s,box-shadow .12s}.stats button.active,.worker-stats button[aria-pressed=true]{box-shadow:none;background:#f0f3f7}.worker-toolbar .ui-select>button{height:var(--control-height);color:#242a34;background:#fff;border:1px solid #ccd4df;border-radius:5px;justify-content:space-between;align-items:center;gap:16px;padding:0 10px;display:flex}@media (width<=900px){.queue-workspace{grid-template-columns:160px minmax(0,1fr)}.route-rail .route-sidebar{width:160px;min-width:160px}}@media (width<=600px){.queue-workspace,.queue-workspace.routes-collapsed{grid-template-columns:minmax(0,1fr)}.routes-collapsed .route-rail{display:none}.route-rail .route-sidebar{width:100%;min-width:0}}@media (prefers-reduced-motion:reduce){.queue-workspace,.route-item,.workspace-tabs>button,.stats button,.worker-stats button{transition:none}}.toolbar{background:0 0;border:0;border-radius:0;padding:0}.table-wrap{border:0;border-radius:0}.virtual-table .task-name{color:#245cc7;font-weight:500}.badge{border-radius:4px;padding:2px 7px;font-size:12px}.badge.pending{color:#8b6016;background:#fbf4e5}.badge.running{color:#245cc7;background:#edf3ff}.badge.succeeded{color:#16734b;background:#edf8f2}.badge.failed{color:#ac4242;background:#fceeee}.badge.cancelled{color:#626d7c;background:#f3f5f7}.badge i{display:none}.worker-stats .worker-idle-count strong{color:#23855b}.worker-stats .worker-busy-count strong{color:#2674d1}.queue-workspace{--route-width:clamp(160px, var(--route-preferred-width,200px), 40vw)}.queue-workspace:not(.routes-collapsed){grid-template-columns:var(--route-width) minmax(0, 1fr)}.route-rail{position:relative}.route-rail .route-sidebar{width:var(--route-width);min-width:var(--route-width)}.route-resizer{cursor:col-resize;touch-action:none;z-index:2;width:8px;position:absolute;top:0;bottom:0;right:0}.route-resizer:after{content:"";background:0 0;width:2px;position:absolute;top:0;bottom:0;right:0}.route-resizer:hover:after,.route-resizer:focus-visible:after,.resizing-routes .route-resizer:after{background:var(--blue)}.queue-workspace.resizing-routes{-webkit-user-select:none;user-select:none;cursor:col-resize;transition:none}@media (width<=600px){.queue-workspace:not(.routes-collapsed){grid-template-columns:minmax(0,1fr)}.route-rail .route-sidebar{width:100%;min-width:0}.route-resizer{display:none}}.toolbar .task-name-filter{flex:0 240px;width:240px;min-width:210px;max-width:100%}.queue-navigation{margin-bottom:12px}.crumb,.route-sidebar-head,.workspace-tabs>button{font-size:13px}.crumb b,.route-sidebar-head strong,.workspace-tabs>button{font-weight:500}.route-sidebar-head,.workspace-tabs{border-bottom:1px solid var(--line);height:36px;min-height:36px;margin-bottom:10px}.route-sidebar-head{padding:0 0 0 9px}.workspace-tabs>button{height:36px;padding:6px 12px}.route-item{gap:3px;padding:8px 9px}.route-item.active{background:#edf3ff;border-color:#0000}.stats,.worker-stats{gap:6px 16px}.stats strong,.worker-stats strong{font-size:24px;font-weight:500}.stats span,.worker-stats button{font-size:12px}.stats button,.worker-stats button{padding:6px 8px}.stats button.active{background:color-mix(in srgb, var(--count-color) 9%, white)}.stats button.active span{color:var(--count-color)}.stats .all{--count-color:#242a34}.worker-stats button[aria-pressed=true]{color:#242a34;background:#f0f2f4}.worker-stats .worker-idle-count[aria-pressed=true]{color:#16734b;background:#edf8f2}.worker-stats .worker-busy-count[aria-pressed=true]{color:#245cc7;background:#edf3ff}.virtual-table th,.worker-table th{font-size:12px;font-weight:500}@media (width<=600px){.toolbar .task-name-filter{flex-basis:100%;width:100%;min-width:0}}.queue-location{align-items:center;gap:14px;min-width:0;display:flex}.routes-toggle{height:28px;font-size:var(--text-body);color:var(--muted);cursor:pointer;background:0 0;border:1px solid #0000;border-radius:4px;flex:none;align-items:center;gap:6px;padding:0 8px;font-weight:400;display:inline-flex}.routes-toggle svg{flex-shrink:0;width:18px;height:18px}.routes-toggle:hover,.routes-toggle:focus-visible{color:#242a34;background:#f5f7fa}.routes-toggle[aria-expanded=true]{color:#242a34}.route-all{justify-content:center;height:36px;margin-bottom:10px}.queue-location .crumb{min-width:0}@media (width<=600px){.queue-location{gap:8px;max-width:100%}}.queue-grid{grid-template-columns:repeat(auto-fill,minmax(min(100%,460px),460px))}.queue-card .queue-title{align-items:center;gap:12px;margin-bottom:12px}.queue-title h2{overflow-wrap:anywhere;margin:0;font-size:16px;font-weight:500}.queue-completion{color:var(--muted);flex-shrink:0;font-size:12px}.queue-card .status-row{border:0;border-radius:0;flex-wrap:wrap;justify-content:space-between;gap:4px 0;margin:0 -4px 10px;display:flex;overflow:visible}.queue-card .status-row button{text-align:left;cursor:pointer;background:0 0;border:0;border-radius:5px;align-items:baseline;gap:4px;min-width:0;padding:6px 4px;transition:background .12s;display:inline-flex}.queue-card .status-row button:hover{background:color-mix(in srgb, var(--state) 7%, white)}.queue-card .status-row b{color:var(--state);font-variant-numeric:tabular-nums;font-size:18px;font-weight:500;line-height:1.3}.queue-card .status-row span{text-transform:capitalize;margin:0;font-size:11px;display:inline}.queue-distribution{background:#eef0f3;border-radius:2px;height:4px;display:flex;overflow:hidden}.queue-distribution>span{background:var(--state);flex-shrink:0;height:100%;box-shadow:inset -1px 0 #fff}.queue-card .queue-metrics{justify-content:space-between;margin-top:8px}.queue-card .queue-foot{margin-top:0}.queue-card .status-row button:focus-visible{outline:2px solid var(--state);outline-offset:3px}.queue-card:hover{box-shadow:none;border-color:var(--line)}.queue-card:focus-visible{outline:2px solid var(--blue);outline-offset:3px}.inactive-routes{color:var(--muted);cursor:pointer;justify-content:space-between;gap:10px;padding:10px 8px;font-size:12px;line-height:20px;position:relative}.inactive-routes input{opacity:0;width:1px;height:1px;position:absolute}.inactive-switch{background:#c9cfd8;border-radius:8px;flex:0 0 26px;height:16px;transition:background .12s;position:relative}.inactive-switch:after{content:"";background:#fff;border-radius:50%;width:12px;height:12px;transition:transform .12s;position:absolute;top:2px;left:2px}.inactive-routes input:checked+.inactive-switch{background:var(--blue)}.inactive-routes input:checked+.inactive-switch:after{transform:translate(10px)}.inactive-routes input:focus-visible+.inactive-switch{outline:2px solid var(--blue);outline-offset:3px}@media (prefers-reduced-motion:reduce){.inactive-switch,.inactive-switch:after{transition:none}}.queue-progress-label{color:var(--muted);justify-content:space-between;align-items:baseline;margin:2px 0 7px;font-size:11px;display:flex}.queue-progress-label strong{font-variant-numeric:tabular-nums;font-size:20px;font-weight:500}.queue-worker-summary{color:var(--muted);align-items:baseline;gap:14px;margin-top:12px;font-size:12px;display:flex}.queue-worker-summary b{font-variant-numeric:tabular-nums;font-weight:500}.queue-worker-idle{color:#16734b}.queue-worker-busy{color:#245cc7}.queue-card{padding:20px}.queue-card .queue-title{margin-bottom:20px}.queue-card .status-row{grid-template-columns:repeat(5,minmax(0,1fr));gap:4px;margin:0 -5px 18px;display:grid}.queue-card .status-row button{flex-direction:column;align-items:flex-start;gap:4px;padding:5px;display:flex}.queue-card .status-row b{letter-spacing:-.4px;font-size:24px;line-height:1.2}.queue-card .status-row span{font-size:10px}.queue-progress-label{margin:8px 0 0;font-size:11px}.queue-progress-label strong{font-size:11px}.queue-observation-summary{border-top:1px solid #eff1f4;flex-wrap:wrap;gap:10px 20px;margin-top:18px;padding-top:12px;display:flex}.queue-observation-summary .queue-worker-summary,.queue-route-summary{color:var(--muted);gap:8px;margin:0;font-size:11px;display:flex}.queue-route-summary b{font-weight:500}.queue-route-waiting{color:#ad7a17}@media (width<=380px){.queue-card{padding:16px}.queue-card .status-row b{font-size:21px}.queue-card .status-row{gap:0}}#corner-notifications{max-width:calc(100vw - 24px);max-height:calc(100dvh - var(--header-height) - 24px);overscroll-behavior:contain;z-index:19;pointer-events:none;flex-direction:column;align-items:flex-end;gap:8px;display:flex;position:fixed;bottom:12px;right:12px;overflow:auto}.observation-notices{pointer-events:none;gap:8px;width:min(360px,100vw - 24px);display:grid}.observation-notice{color:#624719;overflow-wrap:anywhere;pointer-events:auto;background:#fff9ed;border:1px solid #e3ba6c;border-radius:8px;padding:12px 14px;box-shadow:0 4px 16px #20395624}.observation-notice p{margin:4px 0 0;font-size:11px}.observation-notice-actions{justify-content:flex-end;gap:16px;margin-top:8px;display:flex}.observation-notice-actions .link{color:inherit}.observation-notices:empty{display:none}#corner-notifications>*{flex-shrink:0}