lepus 0.2.2 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/Gemfile.lock +3 -3
  4. data/README.md +104 -9
  5. data/bin/web-demo +592 -0
  6. data/docs/brand/BRAND.md +67 -0
  7. data/docs/brand/apple-touch-icon.png +0 -0
  8. data/docs/brand/favicon-16.png +0 -0
  9. data/docs/brand/favicon-32.png +0 -0
  10. data/docs/brand/favicon.ico +0 -0
  11. data/docs/brand/favicon.svg +1 -0
  12. data/docs/brand/icon-512.png +0 -0
  13. data/docs/brand/lockup-lepus-mono.svg +1 -0
  14. data/docs/brand/lockup-lepus-on-dark.svg +1 -0
  15. data/docs/brand/lockup-lepus-on-light.svg +1 -0
  16. data/docs/brand/lockup-lepus-saga-mono.svg +1 -0
  17. data/docs/brand/lockup-lepus-saga-on-dark.svg +1 -0
  18. data/docs/brand/lockup-lepus-saga-on-light.svg +1 -0
  19. data/docs/brand/lockup-lepus-saga.svg +1 -0
  20. data/docs/brand/lockup-lepus.svg +1 -0
  21. data/docs/brand/mark-mono.svg +1 -0
  22. data/docs/brand/mark-on-dark.svg +1 -0
  23. data/docs/brand/mark-on-light.svg +1 -0
  24. data/docs/brand/mark-small-mono.svg +1 -0
  25. data/docs/brand/mark-small-on-dark.svg +1 -0
  26. data/docs/brand/mark-small-on-light.svg +1 -0
  27. data/docs/brand/mark-small.svg +1 -0
  28. data/docs/brand/mark.svg +1 -0
  29. data/docs/configuration.md +1 -0
  30. data/docs/consumers.md +39 -5
  31. data/docs/getting-started.md +1 -1
  32. data/docs/images/lepus-web.png +0 -0
  33. data/docs/middleware.md +1 -1
  34. data/docs/producers.md +41 -0
  35. data/docs/testing.md +67 -48
  36. data/docs/web.md +39 -13
  37. data/gemfiles/Gemfile.rails-5.2.lock +2 -2
  38. data/gemfiles/Gemfile.rails-6.1.lock +4 -4
  39. data/gemfiles/Gemfile.rails-7.2.lock +1 -1
  40. data/gemfiles/Gemfile.rails-8.0.lock +1 -1
  41. data/lib/lepus/configuration.rb +6 -0
  42. data/lib/lepus/consumer.rb +34 -0
  43. data/lib/lepus/consumers/config.rb +8 -7
  44. data/lib/lepus/consumers/stats.rb +15 -2
  45. data/lib/lepus/delay.rb +180 -0
  46. data/lib/lepus/primitive/string.rb +2 -12
  47. data/lib/lepus/producer.rb +6 -1
  48. data/lib/lepus/producers/definition.rb +3 -1
  49. data/lib/lepus/publisher.rb +29 -3
  50. data/lib/lepus/testing/rspec_matchers.rb +20 -1
  51. data/lib/lepus/testing.rb +2 -0
  52. data/lib/lepus/version.rb +1 -1
  53. data/lib/lepus/web/api.rb +45 -0
  54. data/lib/lepus/web/app.rb +19 -16
  55. data/lib/lepus/web/management_api.rb +125 -2
  56. data/lib/lepus/web/topology.rb +341 -0
  57. data/lib/lepus/web.rb +17 -18
  58. data/lib/lepus.rb +1 -0
  59. data/web/assets/app.css +862 -0
  60. data/web/assets/apple-touch-icon.png +0 -0
  61. data/web/assets/favicon-32.png +0 -0
  62. data/web/assets/favicon.svg +1 -0
  63. data/web/assets/fonts/overpass/OFL.txt +93 -0
  64. data/web/assets/fonts/overpass/overpass-latin-wght.woff2 +0 -0
  65. data/web/assets/js/api.js +27 -0
  66. data/web/assets/js/dom.js +95 -0
  67. data/web/assets/js/format.js +63 -0
  68. data/web/assets/js/live.js +74 -0
  69. data/web/assets/js/main.js +156 -0
  70. data/web/assets/js/map.js +606 -0
  71. data/web/assets/js/model.js +262 -0
  72. data/web/assets/js/rates.js +161 -0
  73. data/web/assets/js/theme.js +8 -0
  74. data/web/assets/js/views/connections.js +61 -0
  75. data/web/assets/js/views/overview.js +229 -0
  76. data/web/assets/js/views/processes.js +101 -0
  77. data/web/assets/js/views/queues.js +456 -0
  78. data/web/index.html +58 -153
  79. data/web/sw.js +14 -143
  80. metadata +47 -12
  81. data/test_offline.html +0 -189
  82. data/web/assets/css/styles.css +0 -635
  83. data/web/assets/js/app.js +0 -6
  84. data/web/assets/js/bootstrap.js +0 -20
  85. data/web/assets/js/controllers/connection_controller.js +0 -44
  86. data/web/assets/js/controllers/dashboard_controller.js +0 -499
  87. data/web/assets/js/controllers/queue_controller.js +0 -17
  88. data/web/assets/js/controllers/theme_controller.js +0 -31
  89. data/web/assets/js/offline-manager.js +0 -233
  90. data/web/assets/js/service-worker-manager.js +0 -65
@@ -0,0 +1,862 @@
1
+ @font-face {
2
+ font-family: "Overpass";
3
+ src: url("fonts/overpass/overpass-latin-wght.woff2") format("woff2");
4
+ font-weight: 100 900;
5
+ font-style: normal;
6
+ font-display: swap;
7
+ }
8
+
9
+ :root {
10
+ --paper: #f3f5f8;
11
+ --raise: #ffffff;
12
+ --ink: #1a2233;
13
+ --ink-2: #485267;
14
+ --ink-3: #5f6a80;
15
+ --rule: #d9dee7;
16
+ --rule-soft: #e6e9ef;
17
+ --line: #1a2233;
18
+ --halo: #f3f5f8;
19
+ --token: #f2a30f;
20
+ --token-text: #8a5200;
21
+ --stop: #d3263a;
22
+ --stop-text: #b01e30;
23
+ --stop-soft: #fbe4e6;
24
+ --back: #6a4cf0;
25
+ --back-text: #5536d6;
26
+ --wait: #138496;
27
+ --wait-text: #0c6a78;
28
+ --done: #6f7a90;
29
+ --done-fill: #c9cfda;
30
+ --focus: #2f5bff;
31
+ --font: "Overpass", system-ui, sans-serif;
32
+ --gutter: clamp(24px, 2.6vw, 72px);
33
+ --measure: 72ch;
34
+ color-scheme: light;
35
+ }
36
+
37
+ @media (prefers-color-scheme: dark) {
38
+ :root:not([data-theme="light"]) {
39
+ --paper: #0e1522;
40
+ --raise: #141c2c;
41
+ --ink: #e3e9f3;
42
+ --ink-2: #a9b4c8;
43
+ --ink-3: #8e9ab1;
44
+ --rule: #26314a;
45
+ --rule-soft: #1c2538;
46
+ --line: #c3cde0;
47
+ --halo: #0e1522;
48
+ --token: #ffb21a;
49
+ --token-text: #ffc552;
50
+ --stop: #ff5468;
51
+ --stop-text: #ff7d8c;
52
+ --stop-soft: #3a1823;
53
+ --back: #9d89ff;
54
+ --back-text: #b3a3ff;
55
+ --wait: #3cc4d4;
56
+ --wait-text: #74d9e5;
57
+ --wait: #3cc4d4;
58
+ --wait-text: #74d9e5;
59
+ --done: #7d8aa6;
60
+ --done-fill: #34405a;
61
+ --focus: #7d9bff;
62
+ color-scheme: dark;
63
+ }
64
+ }
65
+
66
+ :root[data-theme="dark"] {
67
+ --paper: #0e1522;
68
+ --raise: #141c2c;
69
+ --ink: #e3e9f3;
70
+ --ink-2: #a9b4c8;
71
+ --ink-3: #8e9ab1;
72
+ --rule: #26314a;
73
+ --rule-soft: #1c2538;
74
+ --line: #c3cde0;
75
+ --halo: #0e1522;
76
+ --token: #ffb21a;
77
+ --token-text: #ffc552;
78
+ --stop: #ff5468;
79
+ --stop-text: #ff7d8c;
80
+ --stop-soft: #3a1823;
81
+ --back: #9d89ff;
82
+ --back-text: #b3a3ff;
83
+ --wait: #3cc4d4;
84
+ --wait-text: #74d9e5;
85
+ --done: #7d8aa6;
86
+ --done-fill: #34405a;
87
+ --focus: #7d9bff;
88
+ color-scheme: dark;
89
+ }
90
+
91
+ *, *::before, *::after { box-sizing: border-box; }
92
+
93
+ [hidden] { display: none !important; }
94
+
95
+ html { -webkit-text-size-adjust: 100%; }
96
+
97
+ body {
98
+ margin: 0;
99
+ background: var(--paper);
100
+ color: var(--ink);
101
+ font-family: var(--font);
102
+ font-size: 15px;
103
+ line-height: 1.5;
104
+ font-variant-numeric: tabular-nums;
105
+ overflow-x: hidden;
106
+ }
107
+
108
+ a { color: inherit; }
109
+ button, input, select { font: inherit; color: inherit; }
110
+
111
+ :focus-visible {
112
+ outline: 2px solid var(--focus);
113
+ outline-offset: 2px;
114
+ border-radius: 3px;
115
+ }
116
+
117
+ .skip {
118
+ position: absolute;
119
+ left: 8px;
120
+ top: -48px;
121
+ padding: 8px 12px;
122
+ background: var(--ink);
123
+ color: var(--paper);
124
+ z-index: 10;
125
+ }
126
+ .skip:focus { top: 8px; }
127
+
128
+ .visually-hidden {
129
+ position: absolute;
130
+ width: 1px;
131
+ height: 1px;
132
+ overflow: hidden;
133
+ clip-path: inset(50%);
134
+ white-space: nowrap;
135
+ }
136
+
137
+ /* Top bar */
138
+
139
+ .bar {
140
+ display: flex;
141
+ align-items: center;
142
+ gap: 32px;
143
+ padding: 18px var(--gutter);
144
+ }
145
+
146
+ .brand {
147
+ display: flex;
148
+ align-items: center;
149
+ gap: 10px;
150
+ text-decoration: none;
151
+ font-weight: 700;
152
+ font-size: 16px;
153
+ letter-spacing: -0.01em;
154
+ }
155
+ .brand-l { fill: none; stroke: var(--ink); stroke-width: 8; stroke-linecap: round; stroke-linejoin: round; }
156
+ .brand-dot { fill: var(--token); }
157
+
158
+ .nav { display: flex; gap: 4px; }
159
+ .nav a {
160
+ text-decoration: none;
161
+ color: var(--ink-2);
162
+ padding: 6px 12px;
163
+ border-radius: 999px;
164
+ font-weight: 500;
165
+ }
166
+ .nav a:hover { color: var(--ink); }
167
+ .nav a[aria-current] {
168
+ color: var(--ink);
169
+ background: var(--rule-soft);
170
+ font-weight: 650;
171
+ }
172
+
173
+ .bar-tools { margin-left: auto; display: flex; align-items: center; gap: 12px; }
174
+
175
+ .poll { position: relative; }
176
+ .live {
177
+ display: inline-flex;
178
+ align-items: center;
179
+ gap: 8px;
180
+ height: 34px;
181
+ border: 1px solid var(--rule);
182
+ background: none;
183
+ padding: 0 10px 0 12px;
184
+ border-radius: 999px;
185
+ color: var(--ink-2);
186
+ cursor: pointer;
187
+ font-weight: 600;
188
+ white-space: nowrap;
189
+ }
190
+ .live:hover { color: var(--ink); background: var(--rule-soft); }
191
+ .live[aria-expanded="true"] { background: var(--rule-soft); color: var(--ink); }
192
+ .live-text { color: var(--ink); }
193
+ .live-rate { color: var(--ink-3); font-weight: 550; }
194
+ .live-rate:empty { display: none; }
195
+ .live-caret { fill: none; stroke: currentColor; stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round; color: var(--ink-3); }
196
+ .live-dot {
197
+ width: 8px;
198
+ height: 8px;
199
+ border-radius: 50%;
200
+ background: var(--token);
201
+ box-shadow: 0 0 0 0 var(--token);
202
+ animation: breathe 2.4s ease-out infinite;
203
+ }
204
+ .poll.is-paused .live-dot { background: none; box-shadow: inset 0 0 0 2px var(--ink-3); animation: none; }
205
+ .poll.is-paused .live-text { color: var(--ink-2); }
206
+
207
+ .poll-menu {
208
+ position: absolute;
209
+ right: 0;
210
+ top: calc(100% + 6px);
211
+ z-index: 20;
212
+ width: 236px;
213
+ padding: 12px;
214
+ border-radius: 12px;
215
+ background: var(--raise);
216
+ border: 1px solid var(--rule);
217
+ box-shadow: 0 12px 32px -12px rgb(10 16 30 / 0.35);
218
+ }
219
+ .poll-menu fieldset { border: 0; margin: 0; padding: 0; min-width: 0; }
220
+ .poll-menu legend { padding: 0 6px 6px; color: var(--ink-3); font-size: 12.5px; font-weight: 650; letter-spacing: 0.02em; }
221
+ .poll-options { display: grid; gap: 2px; }
222
+ .poll-option {
223
+ display: flex;
224
+ align-items: center;
225
+ gap: 10px;
226
+ padding: 7px 8px;
227
+ border-radius: 8px;
228
+ cursor: pointer;
229
+ color: var(--ink);
230
+ font-weight: 550;
231
+ }
232
+ .poll-option:hover { background: var(--rule-soft); }
233
+ .poll-option input { accent-color: var(--ink); margin: 0; width: 16px; height: 16px; }
234
+ .poll-option:has(input:checked) { background: var(--rule-soft); font-weight: 700; }
235
+ .poll-option:has(input:focus-visible) { outline: 2px solid var(--focus); outline-offset: -2px; }
236
+ .poll-option input:focus-visible { outline: none; }
237
+ .poll-default { margin-left: auto; color: var(--ink-3); font-size: 12px; font-weight: 600; }
238
+ .poll-note { margin: 8px 6px 0; color: var(--ink-3); font-size: 12.5px; line-height: 1.4; }
239
+
240
+ @keyframes breathe {
241
+ 0% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--token) 60%, transparent); }
242
+ 70%, 100% { box-shadow: 0 0 0 7px color-mix(in srgb, var(--token) 0%, transparent); }
243
+ }
244
+
245
+ .theme {
246
+ display: inline-flex;
247
+ padding: 3px;
248
+ border-radius: 999px;
249
+ background: var(--rule-soft);
250
+ }
251
+ .theme button {
252
+ display: grid;
253
+ place-items: center;
254
+ width: 30px;
255
+ height: 28px;
256
+ border: 0;
257
+ border-radius: 999px;
258
+ background: none;
259
+ color: var(--ink-3);
260
+ cursor: pointer;
261
+ }
262
+ .theme button:hover { color: var(--ink); }
263
+ .theme button[aria-pressed="true"] { background: var(--raise); color: var(--ink); }
264
+ .theme svg { fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linecap: round; }
265
+ .theme svg .fill { fill: currentColor; stroke: none; }
266
+
267
+ /* Page */
268
+
269
+ .view {
270
+ padding: 8px var(--gutter) 72px;
271
+ }
272
+ .view:focus { outline: none; }
273
+
274
+ .loading, .empty { color: var(--ink-3); }
275
+
276
+ .error {
277
+ color: var(--stop-text);
278
+ font-weight: 600;
279
+ }
280
+
281
+ /* Overview headline, which is also the map's key */
282
+
283
+ .range-row { display: flex; align-items: center; gap: 12px; margin: 0 0 12px; }
284
+ .range-label { color: var(--ink-3); font-size: 13.5px; font-weight: 600; }
285
+ .headline {
286
+ margin: 28px 0 14px;
287
+ font-size: clamp(26px, 3vw, 56px);
288
+ line-height: 1.12;
289
+ font-weight: 300;
290
+ letter-spacing: -0.025em;
291
+ max-width: 34em;
292
+ text-wrap: balance;
293
+ }
294
+ .headline .n { font-weight: 700; letter-spacing: -0.03em; }
295
+ .headline .sym { display: inline-block; vertical-align: 0.1em; margin: 0 0.16em 0 0; }
296
+ .headline .sym svg { display: block; width: 0.7em; height: 0.7em; overflow: visible; }
297
+ .headline .quiet { color: var(--ink-3); }
298
+ .headline .part { white-space: nowrap; }
299
+ .headline .window { color: var(--ink-2); }
300
+
301
+ .legend {
302
+ list-style: none;
303
+ margin: 22px 0 10px;
304
+ padding: 16px 0 0;
305
+ border-top: 1px solid var(--rule);
306
+ display: grid;
307
+ grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
308
+ gap: 10px 32px;
309
+ color: var(--ink-2);
310
+ font-size: 13.5px;
311
+ line-height: 1.4;
312
+ }
313
+ .legend li { display: flex; gap: 10px; align-items: flex-start; }
314
+ .legend .sym { flex: none; margin-top: 1px; }
315
+ .legend .sym svg { display: block; width: 18px; height: 18px; overflow: visible; }
316
+ .legend b { color: var(--ink); font-weight: 650; }
317
+ .legend-hint { margin: 0 0 22px; color: var(--ink-3); font-size: 13px; max-width: var(--measure); }
318
+
319
+ .sym-token { fill: var(--token); stroke: var(--line); stroke-width: 2; }
320
+ .sym-idle { fill: var(--paper); stroke: var(--token); stroke-width: 3; }
321
+ .sym-stop { fill: var(--stop); }
322
+ .sym-back { fill: var(--back); }
323
+ .sym-wait circle { fill: none; stroke: var(--wait); stroke-width: 2.4; }
324
+ .sym-wait path { fill: none; stroke: var(--wait); stroke-width: 2.4; stroke-linecap: round; stroke-linejoin: round; }
325
+
326
+ .sym-error { fill: var(--stop); }
327
+
328
+ .status-note {
329
+ margin: 0 0 20px;
330
+ padding: 10px 14px;
331
+ border-radius: 8px;
332
+ max-width: var(--measure);
333
+ background: var(--stop-soft);
334
+ color: var(--stop-text);
335
+ font-weight: 600;
336
+ overflow-wrap: anywhere;
337
+ }
338
+
339
+ /* The map */
340
+
341
+ .map { position: relative; margin: 0 -8px; }
342
+ .map svg { display: block; width: 100%; height: auto; overflow: visible; }
343
+
344
+ .m-line { stroke: var(--line); stroke-width: 6; stroke-linecap: round; fill: none; }
345
+ .m-line.is-quiet { stroke: var(--rule); stroke-width: 4; }
346
+ .m-line.is-future { stroke: var(--rule); }
347
+ .m-rail { stroke: var(--back); stroke-width: 2; stroke-dasharray: 1 5; stroke-linecap: round; fill: none; opacity: 0.75; }
348
+ .m-rail.is-active { stroke-width: 2.5; opacity: 1; }
349
+ .m-spur { stroke: var(--stop); stroke-width: 2; stroke-linecap: round; fill: none; opacity: 0.7; }
350
+ .m-cap { fill: var(--paper); stroke: var(--line); stroke-width: 3; }
351
+ .m-cap.is-quiet { stroke: var(--rule); }
352
+ .m-cap-label { fill: var(--ink-2); font-size: 12.5px; font-weight: 600; }
353
+ .m-cap-num { fill: var(--ink); font-size: 20px; font-weight: 700; letter-spacing: -0.02em; }
354
+
355
+ .m-ex { fill: var(--ink); font-size: 16px; font-weight: 700; letter-spacing: -0.01em; }
356
+ .m-ex.is-quiet { fill: var(--ink-2); font-weight: 600; }
357
+ .m-meta { fill: var(--ink-3); font-size: 12.5px; font-weight: 500; }
358
+ .m-meta .m-meta-n { fill: var(--ink-2); font-weight: 650; }
359
+ .m-keys { fill: var(--ink-3); font-size: 11.5px; font-weight: 500; }
360
+
361
+ .m-station circle.m-dot { fill: var(--paper); stroke: var(--line); stroke-width: 3; transition: r 160ms ease; }
362
+ .m-row { cursor: pointer; text-decoration: none; }
363
+ .m-row .m-hit { fill: transparent; }
364
+ .m-row.is-flash .m-hit { fill: color-mix(in srgb, var(--token) 22%, transparent); }
365
+ @media (prefers-reduced-motion: no-preference) { .m-row .m-hit { transition: fill 1s ease-out; } }
366
+ .m-row:focus { outline: none; }
367
+ .m-row:focus-visible .m-hit { stroke: var(--focus); stroke-width: 2; }
368
+ .m-row:hover .m-station circle.m-dot, .m-row:focus-visible .m-station circle.m-dot { r: 9; }
369
+ .m-row:hover .m-name { text-decoration: underline; text-underline-offset: 3px; }
370
+ .m-station.is-quiet circle.m-dot { stroke: var(--rule); }
371
+ .m-station.is-backlog circle.m-dot { stroke: var(--token); stroke-width: 3.5; }
372
+ .m-station.is-stopped circle.m-dot { stroke: var(--stop); stroke-width: 3.5; }
373
+ .m-station.is-idle circle.m-dot { stroke: var(--ink-3); stroke-dasharray: 3 3; }
374
+ .m-halo { fill: var(--stop); opacity: 0; }
375
+ .m-station.is-stopped .m-halo { opacity: 0.16; }
376
+ .m-name { fill: var(--ink); font-size: 13px; font-weight: 650; }
377
+ .m-name .m-sub { fill: var(--ink-3); font-weight: 550; }
378
+ .m-name .m-sub-stop { fill: var(--stop-text); font-weight: 650; }
379
+ .m-station.is-quiet .m-name { fill: var(--ink-2); font-weight: 550; }
380
+ .m-consumer { fill: var(--ink-2); font-size: 12.5px; font-weight: 600; }
381
+ .m-consumer.is-missing { fill: var(--ink-3); font-weight: 500; font-style: italic; }
382
+ .m-consumer .m-sub { fill: var(--ink-3); font-weight: 500; }
383
+ .m-consumer.is-stopped { fill: var(--stop-text); font-weight: 700; }
384
+
385
+ .m-backlog { fill: color-mix(in srgb, var(--token) 16%, var(--paper)); stroke: var(--token); stroke-width: 2; }
386
+ .m-backlog-n { fill: var(--token-text); font-size: 12px; font-weight: 750; }
387
+ .m-retry { fill: var(--raise); stroke: var(--back); stroke-width: 1.5; }
388
+ .m-retry-n { fill: var(--back-text); font-size: 11px; font-weight: 750; }
389
+ .m-wait-box { fill: color-mix(in srgb, var(--wait) 10%, var(--raise)); stroke: var(--wait); stroke-width: 1.5; }
390
+ .m-wait-n { fill: var(--wait-text); font-size: 11px; font-weight: 750; }
391
+ .m-meta .m-wait-n { font-size: 12.5px; }
392
+ .m-wait-hook { stroke: var(--wait); stroke-width: 1.5; stroke-dasharray: 2 3; stroke-linecap: round; fill: none; }
393
+ .m-clock circle { fill: none; stroke: var(--wait-text); stroke-width: 1.4; }
394
+ .m-clock path { fill: none; stroke: var(--wait-text); stroke-width: 1.4; stroke-linecap: round; stroke-linejoin: round; }
395
+ .m-block { fill: var(--stop); }
396
+ .m-block-n { fill: var(--stop-text); font-size: 12px; font-weight: 700; }
397
+
398
+ .m-more circle.m-dot { fill: var(--raise); stroke: var(--rule); stroke-width: 2; }
399
+ .m-more .m-more-box { fill: var(--raise); stroke: var(--rule); stroke-width: 1.5; }
400
+ .m-more:hover .m-more-box, .m-more:focus-visible .m-more-box { stroke: var(--ink-3); }
401
+ .m-more .m-more-n { fill: var(--ink-2); font-size: 12px; font-weight: 650; }
402
+ .m-more { cursor: pointer; }
403
+ .m-more:focus { outline: none; }
404
+ .m-more:focus-visible .m-more-box { stroke: var(--focus); stroke-width: 2; }
405
+
406
+ .m-tip {
407
+ position: absolute;
408
+ z-index: 5;
409
+ display: grid;
410
+ gap: 2px;
411
+ max-width: 340px;
412
+ padding: 8px 12px;
413
+ border-radius: 8px;
414
+ background: var(--ink);
415
+ color: var(--paper);
416
+ font-size: 13px;
417
+ line-height: 1.35;
418
+ pointer-events: none;
419
+ box-shadow: 0 8px 24px -10px rgb(10 16 30 / 0.45);
420
+ }
421
+ .m-tip b { font-weight: 700; overflow-wrap: anywhere; }
422
+ .m-tip span { opacity: 0.86; }
423
+
424
+ .t-token { fill: var(--token); stroke: var(--halo); stroke-width: 2; }
425
+ .t-token.is-back { fill: var(--back); }
426
+ .t-token.is-stop { fill: var(--stop); }
427
+ .g-trail-move { stop-color: var(--token); }
428
+ .g-trail-back { stop-color: var(--back); }
429
+ .g-trail-stop { stop-color: var(--stop); }
430
+
431
+ .map-foot { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 6px 24px; margin-top: 10px; }
432
+ .map-note { margin: 0; color: var(--ink-3); font-size: 13px; max-width: var(--measure); }
433
+ .map-empty { margin: 24px 0; padding: 28px; border: 1px dashed var(--rule); border-radius: 12px; color: var(--ink-2); max-width: var(--measure); }
434
+
435
+ .data-table { font-size: 13.5px; }
436
+ .data-table summary { color: var(--ink-2); font-weight: 600; cursor: pointer; width: max-content; }
437
+ .data-table[open] { flex-basis: 100%; }
438
+ .data-scroll { max-height: 360px; overflow: auto; margin-top: 10px; border: 1px solid var(--rule); border-radius: 8px; }
439
+ .data-table table { border-collapse: collapse; width: 100%; }
440
+ .data-table th, .data-table td { padding: 6px 14px; text-align: right; border-bottom: 1px solid var(--rule-soft); white-space: nowrap; }
441
+ .data-table th:first-child, .data-table td.l { text-align: left; }
442
+ .data-table tbody th { font-weight: 550; text-align: left; }
443
+ .data-table thead th { position: sticky; top: 0; background: var(--paper); color: var(--ink-3); font-weight: 600; }
444
+
445
+ /* Rates */
446
+
447
+ .through {
448
+ margin-top: 36px;
449
+ padding-top: 28px;
450
+ border-top: 1px solid var(--rule);
451
+ }
452
+ .through-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 8px 24px; margin-bottom: 4px; }
453
+ .through-head .section-note { margin-bottom: 0; }
454
+ .tp { display: grid; grid-template-columns: minmax(0, 1fr); }
455
+ .tp-totals { display: flex; flex-wrap: wrap; gap: 6px 22px; margin: 14px 0 4px; font-size: 13px; }
456
+ .tp-totals div { display: flex; align-items: center; gap: 6px; }
457
+ .tp-totals dt, .tp-totals dd { line-height: 1.2; }
458
+ .tp-totals dt { color: var(--ink-3); display: flex; align-items: center; gap: 6px; }
459
+ .tp-totals dd { margin: 0; color: var(--ink); font-weight: 700; }
460
+ .tp-totals i { width: 10px; height: 10px; border-radius: 2px; display: inline-block; align-self: center; }
461
+ .tp-totals .k-publish { width: 14px; height: 0; border-radius: 0; border-top: 2px solid var(--line); }
462
+ .tp-totals .k-deliver { width: 14px; height: 0; border-radius: 0; border-top: 2px dashed var(--token); }
463
+ .tp-totals .k-ack { background: var(--done-fill); border-top: 2px solid var(--done); }
464
+ .tp-totals .k-redeliver { background: var(--back); }
465
+ .tp-chart svg { display: block; width: 100%; height: auto; overflow: visible; touch-action: pan-y; }
466
+ .p-ack { fill: var(--done-fill); }
467
+ .p-ack-edge { fill: none; stroke: var(--done); stroke-width: 1.5; stroke-linejoin: round; }
468
+ .p-redeliver { fill: var(--back); opacity: 0.85; }
469
+ .p-publish { fill: none; stroke: var(--line); stroke-width: 1.75; stroke-linejoin: round; }
470
+ .p-deliver { fill: none; stroke: var(--token); stroke-width: 2; stroke-dasharray: 5 4; stroke-linejoin: round; }
471
+ .p-axis { fill: var(--ink-3); font-size: 11.5px; }
472
+ .p-rule { stroke: var(--rule); stroke-width: 1; }
473
+ .p-grid { stroke: var(--rule-soft); stroke-width: 1; }
474
+ .p-tick { stroke: var(--rule); stroke-width: 1; }
475
+ .p-cursor { stroke: var(--ink-3); stroke-width: 1; }
476
+ .p-hit { fill: transparent; cursor: crosshair; }
477
+ .tp-foot {
478
+ display: flex;
479
+ flex-wrap: wrap;
480
+ align-items: baseline;
481
+ justify-content: space-between;
482
+ gap: 4px 24px;
483
+ margin-top: 6px;
484
+ }
485
+ .tp-readout { margin: 0; color: var(--ink-3); font-size: 13px; min-height: 20px; }
486
+ .tp-readout b { color: var(--ink); font-weight: 650; }
487
+ .tp-empty { margin: 18px 0 6px; color: var(--ink-3); }
488
+ .tp-foot .data-table summary { margin-left: auto; }
489
+ .tp-foot .data-table[open] summary { margin-left: 0; }
490
+
491
+ .seg { display: inline-flex; flex-wrap: wrap; gap: 2px; padding: 3px; border-radius: 999px; background: var(--rule-soft); }
492
+ .seg button {
493
+ white-space: nowrap;
494
+ border: 0;
495
+ background: none;
496
+ border-radius: 999px;
497
+ padding: 5px 13px;
498
+ color: var(--ink-2);
499
+ font-weight: 550;
500
+ cursor: pointer;
501
+ }
502
+ .seg button:hover { color: var(--ink); }
503
+ .seg button[aria-pressed="true"] { background: var(--raise); color: var(--ink); font-weight: 650; }
504
+
505
+ /* Below the map */
506
+
507
+ .page-section {
508
+ margin-top: 40px;
509
+ padding-top: 28px;
510
+ border-top: 1px solid var(--rule);
511
+ scroll-margin-top: 16px;
512
+ min-width: 0;
513
+ }
514
+
515
+ .section-title {
516
+ margin: 0 0 4px;
517
+ font-size: 18px;
518
+ font-weight: 700;
519
+ letter-spacing: -0.01em;
520
+ }
521
+ .section-note { margin: 0 0 16px; color: var(--ink-3); font-size: 13.5px; max-width: var(--measure); }
522
+ .section-head { display: flex; flex-wrap: wrap; align-items: flex-end; justify-content: space-between; gap: 10px 24px; margin-bottom: 14px; }
523
+ .section-head .section-note { margin-bottom: 0; }
524
+
525
+ /* Processes */
526
+
527
+ .procs { list-style: none; margin: 0; padding: 0; }
528
+ .procs > li + li { border-top: 1px solid var(--rule); margin-top: 6px; padding-top: 6px; }
529
+ .sup { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 12px; padding: 10px 4px 6px; }
530
+ .sup-name { font-weight: 700; font-size: 16px; overflow-wrap: anywhere; }
531
+ .sup-meta { color: var(--ink-3); font-size: 13px; }
532
+ .workers { list-style: none; margin: 0; padding: 0 0 6px; }
533
+ .worker { padding: 8px 4px 8px 22px; position: relative; }
534
+ .worker + .worker { border-top: 1px solid var(--rule-soft); }
535
+ .worker::before {
536
+ content: "";
537
+ position: absolute;
538
+ left: 4px;
539
+ top: 14px;
540
+ width: 10px;
541
+ height: 10px;
542
+ border-radius: 50%;
543
+ border: 2.5px solid var(--line);
544
+ background: var(--line);
545
+ }
546
+ .worker.is-late::before { border-color: var(--token); background: var(--paper); }
547
+ .worker.is-stale::before { border-color: var(--stop); background: var(--stop); }
548
+ .worker-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 12px; }
549
+ .worker-name { font-weight: 650; overflow-wrap: anywhere; }
550
+ .worker-meta { color: var(--ink-3); font-size: 13px; }
551
+ .worker-meta .is-late { color: var(--token-text); font-weight: 650; }
552
+ .worker-meta .is-stale { color: var(--stop-text); font-weight: 650; }
553
+ .cons { list-style: none; margin: 6px 0 0; padding: 0; display: grid; gap: 4px; }
554
+ .con {
555
+ display: grid;
556
+ grid-template-columns: minmax(0, 1fr) auto;
557
+ gap: 0 16px;
558
+ font-size: 13.5px;
559
+ }
560
+ .con-name { overflow-wrap: anywhere; }
561
+ .con-name b { font-weight: 600; }
562
+ .con-name span { color: var(--ink-3); }
563
+ .con-counts { display: flex; gap: 12px; justify-content: flex-end; color: var(--ink-2); white-space: nowrap; }
564
+ .con-counts .is-rejected { color: var(--back-text); font-weight: 650; }
565
+ .con-counts .is-errored { color: var(--stop-text); font-weight: 700; }
566
+ .con-counts .is-delayed { color: var(--wait-text); font-weight: 650; }
567
+
568
+ /* Queues */
569
+
570
+ .filter {
571
+ height: 34px;
572
+ min-width: 0;
573
+ width: 220px;
574
+ max-width: 100%;
575
+ border-radius: 999px;
576
+ border: 1px solid var(--rule);
577
+ background: var(--paper);
578
+ padding: 0 14px;
579
+ color: var(--ink);
580
+ }
581
+ .filter::placeholder { color: var(--ink-3); }
582
+
583
+ .rows { width: 100%; border-collapse: collapse; }
584
+ .rows th {
585
+ text-align: right;
586
+ font-weight: 600;
587
+ color: var(--ink-3);
588
+ font-size: 13px;
589
+ padding: 0 0 8px 10px;
590
+ border-bottom: 1px solid var(--rule);
591
+ white-space: nowrap;
592
+ }
593
+ .rows th:first-child { text-align: left; padding-left: 4px; }
594
+ .rows td { padding: 9px 0 9px 10px; border-bottom: 1px solid var(--rule-soft); vertical-align: baseline; text-align: right; white-space: nowrap; }
595
+ .rows td:first-child { text-align: left; padding-left: 4px; white-space: normal; overflow-wrap: anywhere; }
596
+ .rows tbody tr:hover td { background: var(--rule-soft); }
597
+ .sort {
598
+ border: 0;
599
+ background: none;
600
+ padding: 0;
601
+ color: inherit;
602
+ font-weight: inherit;
603
+ cursor: pointer;
604
+ display: inline-flex;
605
+ align-items: center;
606
+ gap: 4px;
607
+ }
608
+ .sort:hover { color: var(--ink); }
609
+ th[aria-sort] .sort { color: var(--ink); }
610
+ .sort svg { fill: currentColor; opacity: 0; }
611
+ th[aria-sort] .sort svg { opacity: 1; }
612
+ th[aria-sort="ascending"] .sort svg { transform: rotate(180deg); }
613
+ .q-table th:first-child { width: 34%; }
614
+ .q-table th:last-child, .q-table td:last-child { padding-right: 8px; }
615
+ .q-table tbody + tbody .q-group > * { padding-top: 22px; }
616
+ .q-table .q-group > * { padding: 12px 0 8px 10px; border-bottom: 1px solid var(--rule); vertical-align: baseline; }
617
+ .q-table .q-group th { text-align: left; padding-left: 4px; font-weight: inherit; color: inherit; font-size: inherit; white-space: normal; }
618
+ .q-table .q-group td { color: var(--ink-2); font-weight: 650; font-size: 13.5px; }
619
+ .rows tbody tr.q-group:hover > * { background: none; }
620
+ .q-ex { font-weight: 750; font-size: 15.5px; letter-spacing: -0.01em; overflow-wrap: anywhere; margin-right: 10px; }
621
+ .q-ex-meta { color: var(--ink-3); font-size: 13px; font-weight: 500; }
622
+ .q-head { display: flex; align-items: baseline; gap: 6px; }
623
+ .q-id { min-width: 0; }
624
+ .q-toggle {
625
+ flex: none;
626
+ display: inline-grid;
627
+ place-items: center;
628
+ width: 24px;
629
+ height: 22px;
630
+ align-self: flex-start;
631
+ border: 0;
632
+ border-radius: 6px;
633
+ background: none;
634
+ padding: 0;
635
+ color: var(--ink-2);
636
+ cursor: pointer;
637
+ }
638
+ button.q-toggle:hover { background: var(--rule); color: var(--ink); }
639
+ .q-chevron { fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; transition: transform 140ms ease; }
640
+ [aria-expanded="true"] > .q-chevron { transform: rotate(90deg); }
641
+ .q-title { display: inline-flex; align-items: center; gap: 2px; max-width: 100%; }
642
+ .q-name { font-weight: 650; color: var(--ink); overflow-wrap: anywhere; }
643
+ button.q-name { border: 0; background: none; padding: 0; font: inherit; font-weight: 650; text-align: left; cursor: pointer; }
644
+ button.q-name:hover { text-decoration: underline; text-underline-offset: 3px; }
645
+ .q-map {
646
+ flex: none;
647
+ display: inline-grid;
648
+ place-items: center;
649
+ width: 24px;
650
+ height: 22px;
651
+ border: 0;
652
+ border-radius: 6px;
653
+ background: none;
654
+ padding: 0;
655
+ color: var(--ink-3);
656
+ cursor: pointer;
657
+ opacity: 0;
658
+ }
659
+ .q-row:hover .q-map, .q-map:focus-visible { opacity: 1; }
660
+ .q-map:hover { background: var(--rule); color: var(--ink); }
661
+ .q-pin { fill: none; stroke: currentColor; stroke-width: 1.4; stroke-linejoin: round; }
662
+ @media (hover: none) {
663
+ .q-map { opacity: 1; }
664
+ .q-map, .q-toggle { position: relative; }
665
+ .q-map::after, button.q-toggle::after { content: ""; position: absolute; inset: -11px -10px; }
666
+ }
667
+ .q-app { display: block; color: var(--ink-3); font-size: 12.5px; }
668
+ .q-row:focus { outline: none; }
669
+ .q-row:focus-visible > td { box-shadow: inset 0 2px 0 var(--focus), inset 0 -2px 0 var(--focus); }
670
+ .q-row:focus-visible > td:first-child { box-shadow: inset 2px 2px 0 var(--focus), inset 0 -2px 0 var(--focus); }
671
+ .q-row:focus-visible > td:last-child { box-shadow: inset -2px 2px 0 var(--focus), inset 0 -2px 0 var(--focus); }
672
+ .rows tbody tr.is-picked td { background: color-mix(in srgb, var(--token) 22%, transparent); }
673
+ .rows tbody tr.is-fading td { transition: background-color 1s ease-out; }
674
+ .q-row.is-idle td, .q-row.is-idle .q-name { color: var(--ink-3); }
675
+ .q-row.is-idle .q-name { font-weight: 550; }
676
+ .q-table .q-child td { padding-top: 6px; padding-bottom: 6px; font-size: 13.5px; background: color-mix(in srgb, var(--rule-soft) 45%, transparent); }
677
+ .q-table .q-child td:first-child { padding-left: 34px; }
678
+ .q-kid { overflow-wrap: anywhere; color: var(--ink-2); font-weight: 600; }
679
+ .q-role {
680
+ display: inline-block;
681
+ padding: 1px 7px 0;
682
+ border-radius: 999px;
683
+ font-size: 11.5px;
684
+ font-weight: 700;
685
+ letter-spacing: 0.02em;
686
+ vertical-align: 1px;
687
+ border: 1px solid currentColor;
688
+ }
689
+ .q-role.is-retry { color: var(--back-text); }
690
+ .q-role.is-error { color: var(--stop-text); }
691
+ .q-role.is-delay { color: var(--wait-text); }
692
+ .q-table .q-fold td { padding: 4px 0 4px 4px; text-align: left; }
693
+ .q-more {
694
+ display: inline-flex;
695
+ align-items: center;
696
+ gap: 6px;
697
+ min-height: 32px;
698
+ padding: 0 10px 0 6px;
699
+ border: 0;
700
+ border-radius: 999px;
701
+ background: none;
702
+ color: var(--ink-2);
703
+ font-weight: 600;
704
+ font-size: 13.5px;
705
+ cursor: pointer;
706
+ }
707
+ .q-more:hover { background: var(--rule-soft); color: var(--ink); }
708
+ .q-zero { color: var(--ink-3); }
709
+ .q-ready.is-backlog { color: var(--token-text); font-weight: 700; }
710
+ .q-retry.is-on { color: var(--back-text); font-weight: 650; }
711
+ .q-error.is-on { color: var(--stop-text); font-weight: 700; }
712
+ .q-wait { color: var(--wait-text); font-weight: 650; }
713
+ .q-wait.is-after { margin-left: 0.2em; }
714
+ .q-cons.is-none { color: var(--stop-text); font-weight: 650; }
715
+ .q-trend { color: var(--token-text); font-size: 12px; font-weight: 650; padding-left: 4px; }
716
+
717
+ .list-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 20px; margin-top: 12px; }
718
+ .list-foot p { margin: 0; color: var(--ink-3); font-size: 13px; }
719
+
720
+ /* Connections */
721
+
722
+ .rows.is-quiet { font-size: 13.5px; color: var(--ink-2); }
723
+ .rows.is-quiet td:first-child { color: var(--ink); }
724
+ .c-state.is-blocked, .c-state.is-blocking { color: var(--stop-text); font-weight: 650; }
725
+ .c-self { color: var(--ink-3); font-size: 12.5px; padding-left: 6px; }
726
+
727
+ .empty { color: var(--ink-3); }
728
+
729
+ /* Buttons */
730
+
731
+ .btn {
732
+ display: inline-flex;
733
+ align-items: center;
734
+ gap: 8px;
735
+ height: 38px;
736
+ padding: 0 16px;
737
+ border-radius: 999px;
738
+ border: 1.5px solid var(--ink);
739
+ background: var(--ink);
740
+ color: var(--paper);
741
+ font-weight: 650;
742
+ cursor: pointer;
743
+ text-decoration: none;
744
+ white-space: nowrap;
745
+ }
746
+ .btn:hover { background: color-mix(in srgb, var(--ink) 86%, var(--paper)); }
747
+ .btn-quiet { background: none; color: var(--ink); border-color: var(--rule); }
748
+ .btn-quiet:hover { background: var(--rule-soft); }
749
+ .btn.btn-small { height: 32px; padding: 0 14px; font-size: 14px; }
750
+
751
+ .inline-link {
752
+ border: 0;
753
+ background: none;
754
+ padding: 0;
755
+ color: var(--ink);
756
+ font-weight: 600;
757
+ text-decoration: underline;
758
+ text-underline-offset: 3px;
759
+ cursor: pointer;
760
+ }
761
+
762
+ @media (min-width: 1300px) {
763
+ .procs { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0 56px; align-items: start; }
764
+ .procs > li:nth-child(-n + 2) { border-top: 0; margin-top: 0; padding-top: 0; }
765
+ .procs > li + li { margin-top: 6px; }
766
+ }
767
+
768
+ @media (max-width: 1099px) {
769
+ :root { --gutter: 24px; }
770
+ .q-ex-meta { display: block; }
771
+ }
772
+
773
+ @media (max-width: 899px) {
774
+ .q-table, .q-table tbody, .q-table td, .q-table th { display: block; }
775
+ .q-table thead { display: block; }
776
+ .q-table thead tr {
777
+ display: flex;
778
+ flex-wrap: wrap;
779
+ align-items: center;
780
+ gap: 0 16px;
781
+ padding: 0 4px 6px;
782
+ border-bottom: 1px solid var(--rule);
783
+ }
784
+ .q-table thead tr::before { content: "Sort by"; color: var(--ink-3); font-size: 13px; }
785
+ .q-table thead th { padding: 0; border: 0; }
786
+ .q-table .sort { min-height: 32px; }
787
+ .q-table th:first-child { width: auto; }
788
+ .q-table tbody tr { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 14px; padding: 10px 4px; border-bottom: 1px solid var(--rule-soft); }
789
+ .q-table td, .q-table td:first-child, .q-table td:last-child { padding: 0; border: 0; text-align: left; white-space: normal; font-size: 13.5px; }
790
+ .q-table td[data-label]::before { content: attr(data-label) " "; color: var(--ink-3); font-weight: 500; }
791
+ .q-table td:empty { display: none; }
792
+ .q-table td.is-still { display: none; }
793
+ .rows tbody tr.q-row:hover td { background: none; }
794
+ .q-table .q-group { padding: 18px 4px 8px; border-bottom: 1px solid var(--rule); }
795
+ .q-table tbody:first-of-type .q-group { padding-top: 12px; }
796
+ .q-table .q-group > *, .q-table tbody + tbody .q-group > * { padding: 0; border: 0; font-size: 13px; }
797
+ .q-table .q-group th { flex: 1 1 100%; padding: 0 0 2px; }
798
+ .q-row td:first-child { flex: 1 1 100%; padding-bottom: 2px; font-size: 15px; }
799
+ .q-table tr.q-child { padding-left: 34px; background: color-mix(in srgb, var(--rule-soft) 45%, transparent); }
800
+ .q-table .q-child td, .q-table .q-child td:first-child { padding: 0; background: none; font-size: 13px; }
801
+ .q-child .q-toggle { display: none; }
802
+ .q-row:focus-visible { outline: 2px solid var(--focus); outline-offset: -2px; }
803
+ .q-row:focus-visible > td, .q-row:focus-visible > td:first-child, .q-row:focus-visible > td:last-child { box-shadow: none; }
804
+ .rows tbody tr.q-row.is-picked { background: color-mix(in srgb, var(--token) 22%, transparent); }
805
+ .rows tbody tr.q-row.is-fading { transition: background-color 1s ease-out; }
806
+ .rows tbody tr.q-row.is-picked td { background: none; }
807
+ .q-table tr.q-fold { padding: 4px 0; }
808
+ }
809
+
810
+ @media (max-width: 699px) {
811
+ :root { --gutter: 16px; }
812
+ .headline { margin-top: 12px; }
813
+ .headline .part { white-space: normal; }
814
+ .bar { gap: 12px; flex-wrap: wrap; padding-top: 12px; padding-bottom: 8px; }
815
+ .nav { order: 3; width: 100%; margin: 0 -12px; overflow-x: auto; }
816
+ .poll:not(.is-paused) .live-text { display: none; }
817
+ .live { padding: 0 8px 0 10px; gap: 6px; }
818
+ .poll-menu { width: min(236px, calc(100vw - 32px)); }
819
+ .map { margin: 0; }
820
+ .tp-totals { gap: 4px 16px; }
821
+ .filter { width: 100%; }
822
+ .section-head > * { flex: 1 1 100%; }
823
+ .con { grid-template-columns: minmax(0, 1fr); }
824
+ .con-counts { justify-content: flex-start; flex-wrap: wrap; gap: 2px 12px; }
825
+ .rows thead { display: none; }
826
+ .rows, .rows tbody, .rows tr, .rows td { display: block; }
827
+ .rows tr {
828
+ display: grid;
829
+ grid-template-columns: repeat(3, minmax(0, 1fr));
830
+ gap: 2px 12px;
831
+ padding: 10px 4px;
832
+ border-bottom: 1px solid var(--rule-soft);
833
+ }
834
+ .rows td { padding: 0; border: 0; text-align: left; white-space: normal; font-size: 13.5px; }
835
+ .rows td:first-child { grid-column: 1 / -1; padding: 0 0 4px; font-size: 15px; }
836
+ .rows td[data-label]::before { content: attr(data-label) " "; color: var(--ink-3); font-weight: 500; }
837
+ .rows tbody tr:hover td { background: none; }
838
+ .rows-sortable thead { display: block; }
839
+ .rows-sortable thead tr {
840
+ display: flex;
841
+ flex-wrap: wrap;
842
+ align-items: center;
843
+ gap: 0 16px;
844
+ padding: 0 4px 6px;
845
+ border-bottom: 1px solid var(--rule);
846
+ }
847
+ .rows-sortable thead tr::before { content: "Sort by"; color: var(--ink-3); font-size: 13px; }
848
+ .rows-sortable th { display: block; padding: 0; border: 0; }
849
+ .rows-sortable .sort { min-height: 32px; }
850
+ }
851
+
852
+ @media (min-width: 1700px) {
853
+ .procs { column-gap: 88px; }
854
+ }
855
+
856
+ @media (prefers-reduced-motion: reduce) {
857
+ .live-dot { animation: none; }
858
+ .m-station circle.m-dot { transition: none; }
859
+ .q-chevron { transition: none; }
860
+ }
861
+ .con-more { margin-top: 4px; font-size: 13px; }
862
+ .con-more summary { color: var(--ink-2); font-weight: 600; cursor: pointer; width: max-content; }