rvlib-shipkit 0.38.0 → 1.0.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 (252) hide show
  1. package/CHANGELOG.md +371 -16
  2. package/README.md +26 -14
  3. package/package.json +2 -2
  4. package/src/assets/gen/comfyts-gen.ts +27 -10
  5. package/src/assets/gen/generate-roll.ts +1 -1
  6. package/src/assets/kit-config.ts +6 -6
  7. package/src/assets/run-assets.ts +14 -10
  8. package/src/assets/server/studio-server.ts +6 -5
  9. package/src/check.ts +343 -0
  10. package/src/{rules/SK001-package-json-shape.ts → checks/SK001-package-json-shape.check.ts} +44 -46
  11. package/src/checks/SK002-contracts-fulfillness.check.ts +24 -0
  12. package/src/checks/SK003-claude-md-shape.check.ts +66 -0
  13. package/src/checks/SK004-gitignore-policy.check.ts +309 -0
  14. package/src/{rules/SK005-rvlib-versions.ts → checks/SK005-rvlib-versions.check.ts} +46 -32
  15. package/src/{rules/SK006-npm-publish-safety.ts → checks/SK006-npm-publish-safety.check.ts} +25 -24
  16. package/src/{rules/SK007-github-visibility.ts → checks/SK007-github-visibility.check.ts} +20 -14
  17. package/src/{rules/SK008-reviewer-agent.ts → checks/SK008-reviewer-agent.check.ts} +29 -14
  18. package/src/{rules/SK009-commit-ai-tells.ts → checks/SK009-commit-ai-tells.check.ts} +24 -21
  19. package/src/checks/SK010-undeclared-unit.check.ts +111 -0
  20. package/src/{rules/SK011-sibling-unit-ranges.ts → checks/SK011-sibling-unit-ranges.check.ts} +21 -20
  21. package/src/{rules/SK012-config-barrel-import.ts → checks/SK012-config-barrel-import.check.ts} +18 -23
  22. package/src/{rules/SK013-one-way-in-policy.ts → checks/SK013-one-way-in-policy.check.ts} +58 -50
  23. package/src/{rules/SK014-prose-ste.ts → checks/SK014-prose-ste.check.ts} +27 -13
  24. package/src/{rules/SK015-nested-repo.ts → checks/SK015-nested-repo.check.ts} +15 -12
  25. package/src/checks/SK016-legacy-check-shapes.check.ts +182 -0
  26. package/src/checks/SK017-vscode-hub-task.check.ts +197 -0
  27. package/src/checks/SK018-test-files-run.check.ts +28 -0
  28. package/src/checks/SK019-tests-run-in-units.check.ts +76 -0
  29. package/src/checks/SK020-self-spawning-check.check.ts +147 -0
  30. package/src/checks/contracts.check.ts +20 -0
  31. package/src/cli/cmd_Asks.ts +3 -2
  32. package/src/cli/cmd_Assets.ts +2 -2
  33. package/src/cli/cmd_Bump.ts +1 -1
  34. package/src/cli/cmd_Check.ts +6 -6
  35. package/src/cli/cmd_Checks.ts +95 -0
  36. package/src/cli/cmd_Ci.ts +48 -13
  37. package/src/cli/cmd_Commands.ts +2 -6
  38. package/src/cli/cmd_ConfigKeys.ts +69 -0
  39. package/src/cli/cmd_Contracts.ts +1 -1
  40. package/src/cli/cmd_Fleet.ts +6 -2
  41. package/src/cli/cmd_Hooks.ts +1 -1
  42. package/src/cli/cmd_Keys.ts +43 -18
  43. package/src/cli/cmd_Mv.ts +2 -2
  44. package/src/cli/cmd_Prose.ts +10 -1
  45. package/src/cli/cmd_Releases.ts +2 -2
  46. package/src/cli/cmd_Serve.ts +1 -1
  47. package/src/cli/cmd_Start.ts +6 -6
  48. package/src/cli/cmd_Status.ts +25 -0
  49. package/src/cli/cmd_Tui.ts +2 -2
  50. package/src/cli.ts +7 -4
  51. package/src/collections/actions.ts +4 -4
  52. package/src/collections/checks.ts +258 -0
  53. package/src/collections/collection-types.ts +254 -1
  54. package/src/collections/comfy.ts +23 -0
  55. package/src/collections/md-body.ts +14 -0
  56. package/src/collections/pings.ts +38 -12
  57. package/src/collections/posts.ts +19 -3
  58. package/src/collections/providers.ts +198 -91
  59. package/src/collections/reflections.ts +41 -0
  60. package/src/collections/state-rules.ts +73 -0
  61. package/src/collections/test-time.ts +288 -0
  62. package/src/collections/tests.ts +181 -0
  63. package/src/collections/wikis.ts +66 -0
  64. package/src/comfy/comfy-panel.ts +292 -0
  65. package/src/contracts/apply-contracts.ts +17 -12
  66. package/src/contracts/contract-types.ts +16 -4
  67. package/src/contracts/contracts-engine.ts +78 -42
  68. package/src/contracts/manifests/discord.contract.ts +6 -6
  69. package/src/contracts/manifests/identity.contract.ts +3 -3
  70. package/src/contracts/manifests/itch.contract.ts +12 -15
  71. package/src/contracts/manifests/social.contract.ts +5 -5
  72. package/src/contracts/manifests/steam.contract.ts +16 -16
  73. package/src/contracts/run-contracts.ts +4 -3
  74. package/src/core/attach.ts +96 -0
  75. package/src/core/canonical-scripts.ts +25 -0
  76. package/src/core/check-ctx.ts +204 -14
  77. package/src/core/check-file-names.ts +112 -0
  78. package/src/core/check-pass.ts +84 -0
  79. package/src/core/check-runner.ts +796 -0
  80. package/src/core/config-keys.ts +256 -0
  81. package/src/core/config-rewrite.ts +77 -0
  82. package/src/core/discover-checks.ts +166 -90
  83. package/src/core/discover-legacy-checks.ts +26 -0
  84. package/src/core/doc-block.ts +13 -0
  85. package/src/core/floor-resolve.ts +264 -0
  86. package/src/core/floor.ts +20 -137
  87. package/src/core/git-bin.ts +24 -0
  88. package/src/core/git-root.ts +108 -0
  89. package/src/core/instance.ts +195 -0
  90. package/src/core/jobs.ts +118 -0
  91. package/src/core/layout.ts +52 -0
  92. package/src/core/legacy-shapes.ts +207 -0
  93. package/src/core/lib-root.ts +5 -0
  94. package/src/core/locked-write.ts +110 -39
  95. package/src/core/machine-commit.ts +5 -4
  96. package/src/core/migrate-layout.ts +557 -0
  97. package/src/core/motherhome.ts +2 -8
  98. package/src/core/nesting.ts +4 -12
  99. package/src/core/one-way-in-policy.ts +73 -17
  100. package/src/core/outcome-words.ts +45 -0
  101. package/src/core/package-manager.ts +86 -0
  102. package/src/core/parallel.ts +21 -0
  103. package/src/core/process-tree.ts +44 -0
  104. package/src/core/raw-access.ts +93 -0
  105. package/src/core/read-set.ts +152 -0
  106. package/src/core/run-check.ts +155 -213
  107. package/src/core/run-ci.ts +153 -189
  108. package/src/core/run-fix.ts +96 -0
  109. package/src/core/run-fixers.ts +53 -30
  110. package/src/core/self-update.ts +9 -3
  111. package/src/core/slots.ts +83 -0
  112. package/src/core/spawn-timeout.ts +34 -3
  113. package/src/core/when-block.ts +401 -0
  114. package/src/core/when.ts +135 -0
  115. package/src/core/write-status.ts +5 -2
  116. package/src/hooks/leak-guard.ts +10 -8
  117. package/src/hooks/one-way-in-guard.ts +13 -5
  118. package/src/hooks/root-identity.ts +2 -1
  119. package/src/hooks/run-hooks.ts +35 -23
  120. package/src/init-repo.ts +39 -23
  121. package/src/ledger/check-stats.ts +155 -0
  122. package/src/ledger/check-table.ts +179 -0
  123. package/src/ledger/db.ts +255 -0
  124. package/src/ledger/gate-checks.ts +433 -0
  125. package/src/ledger/git-key.ts +116 -77
  126. package/src/ledger/git-scan.ts +96 -35
  127. package/src/ledger/ledger.ts +116 -42
  128. package/src/ledger/live-runs.ts +111 -0
  129. package/src/ledger/live-timeline.ts +97 -0
  130. package/src/ledger/repo-view.ts +73 -0
  131. package/src/ledger/run-log.ts +448 -0
  132. package/src/ledger/run-timeline.ts +173 -0
  133. package/src/ledger/store.ts +23 -12
  134. package/src/ledger/suite-log.ts +161 -0
  135. package/src/ledger/toolchain-identity.ts +7 -1
  136. package/src/ledger/world-cache.ts +111 -0
  137. package/src/mv/mv-md.ts +16 -6
  138. package/src/problems/extract.ts +182 -0
  139. package/src/problems/problem-board.ts +37 -0
  140. package/src/problems/problem-line.ts +77 -0
  141. package/src/prose/prose-view.ts +90 -0
  142. package/src/prose/run-prose.ts +60 -19
  143. package/src/prose/ste.ts +140 -24
  144. package/src/reflections/reflections-queue.ts +81 -0
  145. package/src/release/git-facts.ts +15 -9
  146. package/src/release/run-bump.ts +6 -5
  147. package/src/release/run-release.ts +25 -11
  148. package/src/release/run-releases.ts +2 -2
  149. package/src/release/unit-gate.ts +7 -54
  150. package/src/release/unit-resolve.ts +282 -0
  151. package/src/release/units.ts +33 -217
  152. package/src/repo-config.ts +155 -85
  153. package/src/rules/rule.ts +36 -96
  154. package/src/rules/untrack-generated.ts +5 -4
  155. package/src/scaffold/files/vscode-tasks.json +10 -31
  156. package/src/scaffold/jsonc-spans.ts +113 -0
  157. package/src/scaffold/project-scaffold.ts +16 -43
  158. package/src/scan.ts +98 -0
  159. package/src/serve/gate-trigger.ts +148 -0
  160. package/src/serve/health-schedule.ts +102 -0
  161. package/src/serve/hub-app/chart-view.ts +179 -0
  162. package/src/serve/hub-app/check-view.tsx +345 -0
  163. package/src/serve/hub-app/check-words.ts +164 -0
  164. package/src/serve/hub-app/checks-svc.ts +280 -0
  165. package/src/serve/hub-app/clock-svc.ts +33 -0
  166. package/src/serve/hub-app/col-card.tsx +23 -39
  167. package/src/serve/hub-app/cols-svc.ts +44 -20
  168. package/src/serve/hub-app/comfy-svc.ts +171 -0
  169. package/src/serve/hub-app/comfy-view.tsx +81 -0
  170. package/src/serve/hub-app/dur.ts +41 -0
  171. package/src/serve/hub-app/hash-route.ts +121 -0
  172. package/src/serve/hub-app/header-bar.tsx +117 -0
  173. package/src/serve/hub-app/health-svc.ts +23 -28
  174. package/src/serve/hub-app/help.tsx +29 -0
  175. package/src/serve/hub-app/hub-app.tsx +122 -119
  176. package/src/serve/hub-app/hub-st.ts +115 -42
  177. package/src/serve/hub-app/hub-types.ts +117 -2
  178. package/src/serve/hub-app/hub.css +534 -192
  179. package/src/serve/hub-app/index.html +2 -0
  180. package/src/serve/hub-app/live-svc.ts +174 -0
  181. package/src/serve/hub-app/md.ts +15 -0
  182. package/src/serve/hub-app/media-svc.ts +4 -1
  183. package/src/serve/hub-app/media-view.tsx +15 -0
  184. package/src/serve/hub-app/next-run.ts +68 -0
  185. package/src/serve/hub-app/project-gutter.css +65 -0
  186. package/src/serve/hub-app/project-gutter.tsx +128 -0
  187. package/src/serve/hub-app/projects-svc.ts +226 -0
  188. package/src/serve/hub-app/prose-hits-st.ts +124 -0
  189. package/src/serve/hub-app/prose-hits-view.tsx +176 -0
  190. package/src/serve/hub-app/prose-hits.css +21 -0
  191. package/src/serve/hub-app/run-strip.ts +47 -0
  192. package/src/serve/hub-app/runs-tabs.ts +24 -0
  193. package/src/serve/hub-app/test-view.tsx +107 -0
  194. package/src/serve/hub-app/tests-svc.ts +62 -0
  195. package/src/serve/hub-app/tests-view.tsx +238 -0
  196. package/src/serve/hub-app/time-ago.tsx +34 -0
  197. package/src/serve/hub-app/timeline-chart.tsx +274 -0
  198. package/src/serve/hub-app/timeline-geometry.ts +314 -0
  199. package/src/serve/hub-app/timeline-svc.ts +62 -0
  200. package/src/serve/hub-app/timeline-view.tsx +245 -0
  201. package/src/serve/hub-app/tree-build.ts +410 -0
  202. package/src/serve/hub-app/tree-row.tsx +42 -0
  203. package/src/serve/hub-app/tree-svc.ts +174 -0
  204. package/src/serve/hub-app/wikis-view.tsx +53 -0
  205. package/src/serve/other-repos.ts +79 -0
  206. package/src/serve/project-gutter-route.ts +350 -0
  207. package/src/serve/project-gutter.ts +449 -0
  208. package/src/serve/run-serve.ts +245 -16
  209. package/src/serve/same-origin.ts +14 -0
  210. package/src/serve/serve-hub.ts +213 -18
  211. package/src/shipkit.ts +19 -12
  212. package/src/smoke/run-smoke.ts +3 -2
  213. package/src/start-repo.ts +75 -36
  214. package/src/suite/judge-suites.ts +216 -18
  215. package/src/suite/kept-timings.ts +58 -0
  216. package/src/suite/runner-shapes.ts +88 -0
  217. package/src/suite/suite-cli.ts +147 -24
  218. package/src/suite/suite-cpu-preload.ts +92 -0
  219. package/src/suite/test-units.ts +243 -0
  220. package/src/ts/config.ts +4 -69
  221. package/src/ts/jsonc.ts +69 -0
  222. package/src/ts/lex.ts +143 -0
  223. package/src/ts/rename.ts +73 -10
  224. package/src/ts/source-graph.ts +28 -8
  225. package/src/ts/tsconfig-projects.ts +6 -97
  226. package/src/ts/tsconfig-resolve.ts +108 -0
  227. package/src/tui/build-steps-panel.ts +61 -0
  228. package/src/tui/checks-panel.ts +24 -16
  229. package/src/tui/run-tui.ts +2 -2
  230. package/src/tui/tui-panel.ts +5 -4
  231. package/src/wiki/graph-colors.ts +68 -0
  232. package/src/wiki/graph-mount.ts +7 -9
  233. package/src/wiki/graph-page.ts +42 -25
  234. package/src/wiki/graph-positions.ts +105 -0
  235. package/src/wiki/run-wiki.ts +1 -1
  236. package/src/wiki/serve-graph.ts +33 -3
  237. package/src/cli/cmd_Rules.ts +0 -51
  238. package/src/cli/cmd_Units.ts +0 -82
  239. package/src/collections/graphs.ts +0 -32
  240. package/src/contracts/contracts-once.ts +0 -43
  241. package/src/core/check-suite.ts +0 -302
  242. package/src/core/repair-contract-dirs.ts +0 -136
  243. package/src/core/task-runner.ts +0 -339
  244. package/src/ledger/gate-units.ts +0 -138
  245. package/src/ledger/unit-table.ts +0 -105
  246. package/src/rules/SK002-contracts-fulfillness.ts +0 -24
  247. package/src/rules/SK003-claude-md-shape.ts +0 -70
  248. package/src/rules/SK004-gitignore-policy.ts +0 -238
  249. package/src/rules/SK010-undeclared-release-unit.ts +0 -127
  250. package/src/rules/rule-registry.ts +0 -64
  251. package/src/rules/run-fix.ts +0 -72
  252. package/src/tui/tasks-panel.ts +0 -60
@@ -1,123 +1,214 @@
1
- /* house palette — matches the graph family. Plain CSS on purpose: a
2
- published lib must not impose bunfig plugins (tailwind) on consumers. */
3
- :root{--bg:#141210;--panel:#1e1a15;--panel2:#262019;--line:#332b22;--ink:#f3ebe0;--dim:#a89a88;
4
- --accent:#e88c28;--gold:#ffcf5c;--teal:#58c0b0;--red:#e05252;--blue:#6ca0e8;--chk1:#221d17;--chk2:#1a1611}
1
+ /* THE TOKENS: the one place a colour is picked. Plain CSS on purpose, a
2
+ published lib must not impose bunfig plugins (tailwind) on consumers.
3
+ Graphite chrome, one cool accent for selection, focus and the primary
4
+ action, and red/orange/green reserved for STATE. No rule outside this
5
+ block writes a colour, and tests/hub-css-invariants.test.ts fails one
6
+ that does. */
7
+ :root{--bg:#0d0e10;--panel:#141619;--panel2:#1b1e22;--line:#282c31;--ink:#e2e4e7;--dim:#8d9299;
8
+ --accent:#6b8fb3;--sel:#1a2430;--chk1:#16181b;--chk2:#121417;
9
+ /* state, and nothing else: red broken · orange taste pending · green measured passing */
10
+ --red:#d2605c;--warn:#cf9a4e;--ok:#6faa86;--blue:#6f9bd1;
11
+ /* the colourless ones: scrims, shadows, masks and the ruler hairlines */
12
+ --scrim:rgba(0,0,0,.6);--shadow:rgba(0,0,0,.62);--solid:#000;
13
+ --hi1:rgba(255,255,255,.28);--hi2:rgba(255,255,255,.16);--hi3:rgba(255,255,255,.07);
14
+ --ground:#1b2521;--groundline:#2a3630;--tag:#eef2f6;
15
+ /* the KIND hues: a row's type, never its state (tree-build TYPE_MARK) */
16
+ --k1:#8ca8c7;--k2:#a892c9;--k3:#6fb0b8;--k4:#c792b4;--k5:#9198a1;--k6:#8f95cf;
17
+ --wash:rgba(107,143,179,.15);--wash2:rgba(107,143,179,.11);--hair:rgba(107,143,179,.5);
18
+ --errbg:#2a1a1a;--errline:#4a2c2c;--errink:#e3bebe;
19
+ /* the ONE spacing scale: every padding, margin and gap in this sheet is a
20
+ value from it or 0, and tests/hub-css-invariants.test.ts fails the rest */
21
+ --s0:2px;--s1:4px;--s2:8px;--s3:12px;--s4:16px}
5
22
  *{box-sizing:border-box}html,body,#root{height:100%}
6
23
  body{margin:0;background:var(--bg);color:var(--ink);font:14px/1.5 system-ui,sans-serif;overflow:hidden}
7
- .app{display:grid;grid-template-columns:46px 290px 1fr;height:100vh}
24
+ /* the page is a flex ROW: a column mounted before .appcol sits left of the
25
+ header and everything under it, and .appcol stacks the header, the live
26
+ strip and the body */
27
+ .app{display:flex;height:100vh}
28
+ .appcol{flex:1;min-width:0;display:flex;flex-direction:column}
29
+ .appbody{flex:1;min-height:0;display:grid;grid-template-columns:330px 1fr}
8
30
 
9
- /* ── icon rail: jump-to-section, never a navigation ── */
10
- .skrail{width:46px;height:100vh;display:flex;flex-direction:column;align-items:center;padding-top:10px;gap:4px;
11
- background:#17140f;border-right:1px solid var(--line)}
12
- .skrail .ric{display:flex;align-items:center;justify-content:center;width:36px;height:36px;border-radius:9px;
13
- font-size:17px;cursor:pointer;filter:grayscale(35%);opacity:.75;position:relative;user-select:none}
14
- .skrail .ric:hover{background:var(--panel2);filter:none;opacity:1}
15
- .skrail .ric i{position:absolute;top:4px;right:4px;width:8px;height:8px;border-radius:50%;background:var(--red)}
31
+ /* ── the header: repo name, the core pages, the two runs and the run state ── */
32
+ .hdr{display:flex;gap:var(--s2);align-items:center;height:36px;flex:none;padding:0 var(--s3);border-bottom:1px solid var(--line);background:var(--panel)}
33
+ .hdr .sp{flex:1}
34
+ .hname{font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--ink);font-weight:600;margin-right:var(--s2);white-space:nowrap}
35
+ .hnav{display:flex;gap:var(--s1);overflow:hidden}
36
+ .hnav button{border-color:transparent;color:var(--dim);border-radius:0;border-bottom:1px solid transparent;white-space:nowrap}
37
+ .hnav button:hover{color:var(--ink);border-color:transparent;border-bottom-color:var(--line)}
38
+ .hnav button.on{color:var(--accent);border-bottom-color:var(--accent)}
39
+ /* the run state holds its width whatever it says, so a run never moves the buttons beside it */
40
+ .hstate{display:inline-flex;gap:var(--s1);align-items:center;width:260px;min-width:260px;flex:none;justify-content:flex-end;font-size:11.5px;color:var(--dim);overflow:hidden}
41
+ .hstate.on{color:var(--ink)}
42
+ .hstate .htext{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
43
+
44
+ /* ── the live strip: the one run chart. Folded, one thin row of ONE height
45
+ whatever runs; unfolded, the chart at the height its lanes hold, set
46
+ inline from a held value that grows at once and shrinks only after a
47
+ while, so it never jumps every second. The strip clips nothing itself, so
48
+ its (?) opens below it over the page; the chart box clips the chart ── */
49
+ .live{display:flex;flex-direction:column;height:30px;flex:none;border-bottom:1px solid var(--line);background:var(--bg);font-size:11px;color:var(--dim)}
50
+ .live .hbub{top:calc(100% + 6px);bottom:auto;z-index:30}
51
+ .lrow{display:flex;gap:var(--s2);align-items:center;height:29px;flex:none;padding:0 var(--s3)}
52
+ .lrow .sp{flex:1}
53
+ .lfold{display:inline-flex;gap:var(--s1);align-items:center;border-color:transparent;color:var(--dim);padding:var(--s0) var(--s1)}
54
+ .lfold .sk{font-size:9px;opacity:.55}
55
+ .lwho{width:180px;flex:none;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:var(--ink)}
56
+ .live.idle .lwho{color:var(--dim)}
57
+ .lwho i{font-style:normal;color:var(--dim);margin-left:var(--s1)}
58
+ .lwho b{font-weight:500;color:var(--accent);margin-left:var(--s1)}
59
+ .lchart{flex:1;min-width:0;height:21px;border:1px solid var(--line);border-radius:3px;overflow:hidden}
60
+ .lopen{flex:1;min-height:0;overflow-y:auto;padding:0 var(--s3);scrollbar-width:thin;scrollbar-color:var(--line) transparent}
61
+ .lnote,.lmore{flex:none;color:var(--warn);white-space:nowrap}
62
+ .lbusy{flex:none;white-space:nowrap}
16
63
 
17
64
  /* ── sidebar ── */
18
- aside{border-right:1px solid var(--line);background:var(--panel);overflow-y:auto;height:100vh}
19
- .shead{position:sticky;top:0;background:var(--panel);padding:12px 14px;border-bottom:1px solid var(--line);
65
+ aside{border-right:1px solid var(--line);background:var(--panel);overflow-y:auto;min-height:0}
66
+ .shead{position:sticky;top:0;background:var(--panel);padding:var(--s3);border-bottom:1px solid var(--line);
20
67
  font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--dim);z-index:2;display:flex;justify-content:space-between;align-items:center}
21
- /* segmented severity filter — all · needs · red */
22
- .seg{display:inline-flex;border:1px solid var(--line);border-radius:7px;overflow:hidden;flex:none}
23
- .seg button{background:var(--bg);color:var(--dim);border:0;border-radius:0;padding:2px 8px;font-size:10px;font-weight:600;
24
- letter-spacing:.06em;text-transform:uppercase;cursor:pointer}
68
+ /* segmented severity filter — all · needs · red. A thin border and an
69
+ underline on the live one: no fill, no pill */
70
+ .seg{display:inline-flex;border:1px solid var(--line);border-radius:3px;overflow:hidden;flex:none}
71
+ .seg button{background:transparent;color:var(--dim);border:0;border-bottom:1px solid transparent;border-radius:0;
72
+ padding:var(--s0) var(--s2);font-size:11px;font-weight:500;text-transform:lowercase;letter-spacing:0;cursor:pointer}
25
73
  .seg button+button{border-left:1px solid var(--line)}
26
74
  .seg button:hover{color:var(--ink)}
27
- .seg button.on{background:var(--accent);color:#1a1206}
28
- .ssearch{padding:8px 12px}
29
- .ssearch input{width:100%;background:var(--bg);border:1px solid var(--line);border-radius:8px;color:var(--ink);
30
- padding:6px 10px;font-size:12.5px;outline:none}
75
+ .seg button.on{color:var(--accent);border-bottom-color:var(--accent)}
76
+ .seg button .sk{margin-left:var(--s1);font-size:9px;opacity:.55}
77
+ .swhen{padding:0 var(--s3) var(--s2);font-size:11px;color:var(--dim)}
78
+ .ssearch{padding:var(--s2) var(--s3)}
79
+ .ssearch input{width:100%;background:var(--bg);border:1px solid var(--line);border-radius:4px;color:var(--ink);
80
+ padding:var(--s1) var(--s2);font-size:12.5px;outline:none}
31
81
  .ssearch input:focus{border-color:var(--accent)}
32
- /* section header — hairline top border, tight letterspacing; media cats
33
- and collection sections share this treatment EXACTLY */
34
- .gname{padding:9px 14px 4px;font-size:11px;letter-spacing:.09em;text-transform:uppercase;color:var(--accent);
35
- border-top:1px solid var(--line);display:flex;justify-content:space-between;align-items:center;scroll-margin-top:46px}
36
- .gname .cn{cursor:pointer}.gname .cn:hover{color:var(--gold)}
37
- .gname .cnt{font-size:11px;color:var(--dim);letter-spacing:0;text-align:right;min-width:18px}
38
- .warnline{color:var(--red);text-transform:none;letter-spacing:0}
82
+ /* ── the resource tree: ONE row shape, a fixed grid. The indent rides in
83
+ the FIRST column and the label eats the slack, so every right side slot
84
+ lines up down the whole column whatever the depth. Colour is on the
85
+ label, and the glyph slot repeats the state as one character: no dots,
86
+ no prose, no dash separators ── */
87
+ .tree{padding:var(--s1) 0 var(--s4);outline:none}
88
+ .tree:focus-visible{box-shadow:inset 2px 0 0 var(--accent)}
89
+ .trow{display:grid;grid-template-columns:calc(var(--d,0)*12px + 18px) 14px minmax(0,1fr) 58px 46px 14px;
90
+ align-items:center;gap:var(--s1);padding:0 var(--s2);height:20px;line-height:20px;cursor:pointer;white-space:nowrap}
91
+ .trow:hover{background:var(--panel2)}
92
+ .trow.on{background:var(--sel);box-shadow:inset 2px 0 0 var(--accent)}
93
+ /* the fold marker: an 11px chevron with the whole first column as its hit
94
+ target, and it turns rather than swapping for another glyph */
95
+ .tw{display:flex;align-items:center;justify-content:flex-end;height:20px;color:var(--dim);user-select:none}
96
+ .trow:hover .tw{color:var(--ink)}
97
+ .cvt{transition:transform .12s ease;transform-origin:50% 50%;flex:none}
98
+ .cvt.open{transform:rotate(90deg)}
99
+ /* WHAT a row is: one glyph, one hue per kind. The hue is the kind's
100
+ identity and carries no state, which is why it is its own slot */
101
+ .ty{font-size:11px;text-align:center;user-select:none;color:var(--dim)}
102
+ .ty.k1{color:var(--k1)}
103
+ .ty.k2{color:var(--k2)}
104
+ .ty.k3{color:var(--k3)}
105
+ .ty.k4{color:var(--k4)}
106
+ .ty.k5{color:var(--k5)}
107
+ .ty.k6{color:var(--k6)}
108
+ .tl{font-size:12.5px;overflow:hidden;text-overflow:ellipsis}
109
+ .tl.red{color:var(--red)}
110
+ .tl.warn{color:var(--warn)}
111
+ .tl.ok{color:var(--ok)}
112
+ .tl.idle{color:var(--ink)}
113
+ .tl.quiet{color:var(--dim)}
114
+ /* a check's id: a small dim tag before its title, never the loud part */
115
+ .tid{font:9.5px ui-monospace,Menlo,monospace;color:var(--dim);margin-right:var(--s1);letter-spacing:.02em}
116
+ /* the state glyph: ONE glyph per state, the same everywhere. Blocking
117
+ versus advisory is a muted tone, never a word */
118
+ .tg{font-size:10px;text-align:center;color:var(--dim);user-select:none}
119
+ .tg.red{color:var(--red)}
120
+ .tg.warn{color:var(--warn)}
121
+ .tg.ok{color:var(--ok)}
122
+ .tg.adv{color:var(--warn);opacity:.75}
123
+ .tg.run{color:var(--accent);animation:pulse 1.1s ease-in-out infinite}
124
+ /* right aligned, tabular figures, ONE unit (ms) and one tone table, so a
125
+ column of durations compares by eye (hub-app/dur.ts owns both) */
126
+ .tdur{font:10.5px ui-monospace,Menlo,monospace;font-variant-numeric:tabular-nums;text-align:right;color:var(--dim);overflow:hidden}
127
+ .tdur.fast,.dnum.fast{color:var(--ok)}
128
+ .tdur.plain,.dnum.plain{color:var(--ink)}
129
+ .tdur.slow,.dnum.slow{color:var(--warn)}
130
+ .tdur.bad,.dnum.bad{color:var(--red)}
131
+ .tnote{font-size:10px;text-align:right;color:var(--dim);overflow:hidden;text-overflow:ellipsis}
132
+ .tnote b{font-weight:600;color:var(--red)}
133
+ .stabs{display:flex;gap:var(--s2);align-items:center;padding:var(--s1) var(--s3);border-bottom:1px solid var(--line)}
134
+ .stabs .sp{flex:1}
135
+ .stabs .cnt{font-size:11px;color:var(--dim)}
136
+ .catgen{display:inline-flex;gap:var(--s1)}
137
+ .catgen button{padding:var(--s0) var(--s2);font-size:11px;font-weight:700}
138
+ .tempty{padding:var(--s2) var(--s3);font-size:12px;color:var(--dim);white-space:pre-wrap}
139
+ .warnline{color:var(--red)}
39
140
  .fold{cursor:pointer;color:var(--dim);font-size:15px;display:inline-block;width:22px;text-align:center;
40
- padding:2px 3px;margin:-2px 0;border-radius:6px;user-select:none}
141
+ padding:var(--s0);border-radius:3px;user-select:none}
41
142
  .fold:hover{color:var(--ink);background:var(--panel2)}
42
- .catgen button{background:var(--panel2);color:var(--dim);border:1px solid var(--line);border-radius:6px;
43
- padding:1px 6px;font-size:11px;font-weight:700;margin-left:3px;cursor:pointer}
44
- .catgen button:hover{color:var(--ink);border-color:var(--accent)}
45
- /* items sit one step in from their header, on a subtle left guide line;
46
- the full row hovers, the selected row keeps the accent bar */
47
- .arow{position:relative;display:flex;align-items:center;gap:8px;padding:3px 12px 3px 24px;cursor:pointer;
48
- border-left:3px solid transparent}
49
- .arow::before{content:'';position:absolute;left:15px;top:0;bottom:0;width:1px;background:var(--line)}
50
- .arow:hover{background:var(--panel2)}
51
- .arow.sel{background:rgba(232,140,40,.18);border-left-color:var(--accent)}
52
- .arow.sel::before{background:transparent}
53
- .arow .lb{flex:1;min-width:0}.arow .lb b{display:block;font-size:13px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-weight:600}
54
- .arow .lb span{font-size:11px;color:var(--dim);display:block;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
55
- .dot{width:8px;height:8px;border-radius:50%;flex:none;background:var(--line)}
56
- .dot.done{background:var(--gold)}.dot.some{background:var(--accent)}
57
- .dot.miss{background:#e5484d}.dot.stale{background:#f5a524}
58
- .cdot{width:8px;height:8px;border-radius:50%;flex:none;background:var(--line)}
59
- .cdot.red{background:var(--red)}.cdot.yellow{background:var(--gold)}
143
+ .spin{width:12px;height:12px;border:2px solid var(--line);border-top-color:var(--accent);border-radius:50%;animation:rot 1s linear infinite;flex:none}
60
144
 
61
145
  /* ── main column ── */
62
- main{display:flex;flex-direction:column;height:100vh;overflow:hidden}
146
+ main{display:flex;flex-direction:column;min-height:0;overflow:hidden}
63
147
  /* topbar: the prompt is its OWN full-width row (folded by default to one
64
148
  ellipsized line; expanded = small mono, auto-grows to its content,
65
149
  scrolls only past ~14 lines), then ONE slim toolbar row */
66
- .bar{display:flex;flex-direction:column;gap:8px;padding:10px 16px;border-bottom:1px solid var(--line);background:var(--panel)}
150
+ .bar{display:flex;flex-direction:column;gap:var(--s2);padding:var(--s2) var(--s4);border-bottom:1px solid var(--line);background:var(--panel)}
67
151
  textarea{width:100%;min-height:64px;background:var(--bg);color:var(--ink);border:1px solid var(--line);
68
- border-radius:9px;padding:9px 11px;font:12px/1.5 ui-monospace,Menlo,monospace;resize:vertical}
69
- .promptrow{display:flex;gap:6px;align-items:flex-start}
70
- .promptrow .fold{flex:none;background:var(--panel2);color:var(--dim);border:1px solid var(--line);border-radius:9px;
71
- padding:6px 9px;font-size:11px;line-height:1.5}
72
- .bar .prompt{flex:1;min-height:36px;max-height:252px;overflow-y:auto;resize:none;font:11px/1.5 ui-monospace,Menlo,monospace;padding:7px 10px}
152
+ border-radius:3px;padding:var(--s2) var(--s3);font:12px/1.5 ui-monospace,Menlo,monospace;resize:vertical}
153
+ .promptrow{display:flex;gap:var(--s1);align-items:flex-start}
154
+ .promptrow .fold{flex:none;background:var(--panel2);color:var(--dim);border:1px solid var(--line);border-radius:3px;
155
+ padding:var(--s1) var(--s2);font-size:11px;line-height:1.5}
156
+ .bar .prompt{flex:1;min-height:36px;max-height:252px;overflow-y:auto;resize:none;font:11px/1.5 ui-monospace,Menlo,monospace;padding:var(--s2)}
73
157
  .bar .prompt.folded{min-height:0;max-height:none;overflow:hidden;white-space:nowrap;text-overflow:ellipsis;cursor:pointer;
74
- color:var(--dim);background:var(--bg);border:1px solid var(--line);border-radius:9px}
75
- .toolbar{display:flex;gap:8px;align-items:center;min-height:30px}
76
- .toolbar button{padding:5px 11px;font-size:12px}
158
+ color:var(--dim);background:var(--bg);border:1px solid var(--line);border-radius:3px}
159
+ .toolbar{display:flex;gap:var(--s2);align-items:center;min-height:30px}
160
+ .toolbar button{padding:var(--s1) var(--s3);font-size:12px}
77
161
  .toolbar .sp{flex:1}
78
- .toolbar .xn{display:flex;gap:3px;align-items:center;font-size:12px;color:var(--dim)}
79
- .toolbar .xn select{padding:4px 5px;font-size:12px}
80
- button{background:var(--accent);color:#1a1206;border:0;border-radius:9px;padding:8px 13px;font-weight:650;cursor:pointer}
81
- button.ghost{background:var(--panel2);color:var(--ink);border:1px solid var(--line)}
82
- button:disabled{opacity:.45;cursor:default}
83
- select,input[type=number]{background:var(--bg);color:var(--ink);border:1px solid var(--line);border-radius:8px;padding:7px}
162
+ .toolbar .xn{display:flex;gap:var(--s0);align-items:center;font-size:12px;color:var(--dim)}
163
+ .toolbar .xn select{padding:var(--s1);font-size:12px}
164
+ /* BUTTONS: small, flat, one pixel border on the panel colour, a 12px
165
+ lowercase label. The accent is a border and an ink colour on the primary
166
+ action, never a fill, and nothing here is a big rounded rectangle */
167
+ button{background:transparent;color:var(--ink);border:1px solid var(--line);border-radius:3px;
168
+ padding:var(--s0) var(--s2);font:500 12px/1.5 system-ui,sans-serif;cursor:pointer}
169
+ button:hover{border-color:var(--accent)}
170
+ button.primary{color:var(--accent);border-color:var(--accent)}
171
+ button.ghost{color:var(--dim)}
172
+ button.ghost:hover{color:var(--ink)}
173
+ button:disabled{opacity:.4;cursor:default;border-color:var(--line);color:var(--dim)}
174
+ select,input[type=number]{background:var(--bg);color:var(--ink);border:1px solid var(--line);border-radius:4px;padding:var(--s2)}
84
175
  .status{font-size:12px;color:var(--dim);text-align:right;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;max-width:46vw;flex:none}
85
- .spin{width:12px;height:12px;border:2px solid var(--line);border-top-color:var(--gold);border-radius:50%;animation:rot 1s linear infinite;flex:none}
86
176
  @keyframes rot{to{transform:rotate(360deg)}}
177
+ @keyframes pulse{0%,100%{opacity:1}50%{opacity:.25}}
87
178
  .progbar{display:block;width:120px;height:4px;background:var(--panel2);border-radius:2px;overflow:hidden;flex:none}
88
- .progbar i{display:block;height:100%;width:0;background:var(--gold);transition:width .3s}
89
- .detnote{font-size:11px;color:var(--teal);line-height:1.25}
90
- .jrow{display:flex;gap:8px;margin-top:6px;align-items:center}
179
+ .progbar i{display:block;height:100%;width:0;background:var(--accent);transition:width .3s}
180
+ .detnote{font-size:11px;color:var(--ok);line-height:1.25}
181
+ .jrow{display:flex;gap:var(--s2);margin-top:var(--s1);align-items:center}
91
182
  .jmsg{font-size:12px;color:var(--dim)}
92
- kbd{background:var(--panel2);border:1px solid var(--line);border-radius:4px;padding:0 5px;font-size:11px}
93
- .help{font-size:11px;color:var(--dim);padding:4px 16px;border-top:1px solid var(--line)}
94
- .errs{background:#3a1717;border-bottom:1px solid #5a2222;color:#f0c0c0;font:12px/1.5 ui-monospace,Menlo,monospace;
95
- padding:6px 16px;white-space:pre-wrap}
183
+ kbd{background:var(--panel2);border:1px solid var(--line);border-radius:4px;padding:0 var(--s1);font-size:11px}
184
+ .help{font-size:11px;color:var(--dim);padding:var(--s1) var(--s4);border-top:1px solid var(--line)}
185
+ .errs{background:var(--errbg);border-bottom:1px solid var(--errline);color:var(--errink);font:12px/1.5 ui-monospace,Menlo,monospace;
186
+ padding:var(--s1) var(--s4);white-space:pre-wrap}
96
187
 
97
188
  /* ── per-asset info chips ── */
98
- .info{flex-wrap:wrap;gap:6px;align-items:center;padding:6px 16px;border-bottom:1px solid var(--line);background:var(--panel2)}
99
- .info .chip{display:inline-flex;align-items:center;gap:5px;background:var(--bg);border:1px solid var(--line);border-radius:6px;
100
- padding:2px 8px;font-size:11px;color:var(--ink);max-width:360px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
189
+ .info{flex-wrap:wrap;gap:var(--s1);align-items:center;padding:var(--s1) var(--s4);border-bottom:1px solid var(--line);background:var(--panel2)}
190
+ .info .chip{display:inline-flex;align-items:center;gap:var(--s1);background:var(--bg);border:1px solid var(--line);border-radius:3px;
191
+ padding:var(--s0) var(--s2);font-size:11px;color:var(--ink);max-width:360px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
101
192
  .info .chip b{color:var(--accent);font-weight:650;letter-spacing:.05em;text-transform:uppercase;font-size:10px}
102
- .info .chip.warn{border-color:var(--red);color:#f0c0c0}.info .chip.warn b{color:var(--red)}
193
+ .info .chip.warn{border-color:var(--red);color:var(--errink)}.info .chip.warn b{color:var(--red)}
103
194
  .info .chip code{font:11px ui-monospace,Menlo,monospace;color:var(--dim)}
104
195
 
105
196
  /* ── stage ── */
106
- .stage{position:relative;flex:1;min-height:0;display:flex;align-items:center;justify-content:center;padding:18px;overflow:hidden;
197
+ .stage{position:relative;flex:1;min-height:0;display:flex;align-items:center;justify-content:center;padding:var(--s4);overflow:hidden;
107
198
  background:conic-gradient(var(--chk1) 90deg,var(--chk2) 0 180deg,var(--chk1) 0 270deg,var(--chk2) 0) 0/34px 34px}
108
- .stage img.roll{max-width:100%;max-height:100%;object-fit:contain;filter:drop-shadow(0 6px 20px #0008);z-index:5;position:relative}
199
+ .stage img.roll{max-width:100%;max-height:100%;object-fit:contain;filter:drop-shadow(0 6px 20px var(--shadow));z-index:5;position:relative}
109
200
  .stage .ph,.stagewrap .ph{color:var(--dim)}
110
- .stagewrap{flex:1;min-height:0;overflow-y:auto;display:flex;align-items:flex-start;justify-content:center;padding:18px}
111
- .stageaud{display:flex;flex-direction:column;align-items:center;gap:14px;font-size:48px;padding:40px}
201
+ .stagewrap{flex:1;min-height:0;overflow-y:auto;display:flex;align-items:flex-start;justify-content:center;padding:var(--s4)}
202
+ .stageaud{display:flex;flex-direction:column;align-items:center;gap:var(--s3);font-size:48px;padding:var(--s4)}
112
203
  .stageaud audio{width:min(480px,80vw)}
113
204
 
114
205
  /* ── geometry overlay ── */
115
- #ov{position:absolute;border:2px solid var(--gold);border-radius:50%;background:rgba(255,207,92,.14);cursor:move;z-index:10}
116
- #ov .hdl{position:absolute;right:-7px;top:-7px;width:14px;height:14px;background:var(--gold);border-radius:3px;cursor:nesw-resize}
117
- #ov .arrow{position:absolute;left:0;right:0;top:50%;border-top:1px dashed var(--gold);opacity:.8}
118
- #ov .sz{position:absolute;left:50%;top:50%;transform:translate(-50%,-120%);font-size:11px;color:var(--gold);
119
- background:#000a;padding:0 5px;border-radius:4px;white-space:nowrap}
120
- .plumb{position:absolute;width:0;border-left:1px dashed rgba(255,207,92,.5);top:-2000px;bottom:-2000px;z-index:9}
206
+ #ov{position:absolute;border:2px solid var(--accent);border-radius:50%;background:var(--wash);cursor:move;z-index:10}
207
+ #ov .hdl{position:absolute;right:-7px;top:-7px;width:14px;height:14px;background:var(--accent);border-radius:3px;cursor:nesw-resize}
208
+ #ov .arrow{position:absolute;left:0;right:0;top:50%;border-top:1px dashed var(--accent);opacity:.8}
209
+ #ov .sz{position:absolute;left:50%;top:50%;transform:translate(-50%,-120%);font-size:11px;color:var(--accent);
210
+ background:var(--scrim);padding:0 var(--s1);border-radius:4px;white-space:nowrap}
211
+ .plumb{position:absolute;width:0;border-left:1px dashed var(--hair);top:-2000px;bottom:-2000px;z-index:9}
121
212
  /* leg DRAG boxes all ride at 10, above the sprite (5) and the mask host
122
213
  (.ovlhost 6) — every leg grabbable even with a mask canvas on. depth is
123
214
  the PILLS' job, never theirs */
@@ -125,202 +216,453 @@ kbd{background:var(--panel2);border:1px solid var(--line);border-radius:4px;padd
125
216
  .leg.far{border-color:var(--blue)}
126
217
  .leg.near{border-color:var(--red)}
127
218
  .leg .hip{position:absolute;top:-3px;left:50%;transform:translateX(-50%);width:6px;height:6px;border-radius:50%;background:currentColor}
128
- .leg .foot{position:absolute;bottom:-4px;left:50%;transform:translateX(-50%);width:12px;height:7px;border-radius:50%;background:var(--gold)}
219
+ .leg .foot{position:absolute;bottom:-4px;left:50%;transform:translateX(-50%);width:12px;height:7px;border-radius:50%;background:var(--accent)}
129
220
  .leg .lh{position:absolute;right:-6px;top:-6px;width:11px;height:11px;background:currentColor;border-radius:3px;cursor:nesw-resize}
130
- .leg .tag{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);font-size:10px;font-weight:700;color:#eaf2ff;
131
- text-shadow:0 1px 2px #000c;pointer-events:none;white-space:nowrap}
132
- .leg.near .tag{color:#ffecec}
221
+ .leg .tag{position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);font-size:10px;font-weight:700;color:var(--tag);
222
+ text-shadow:0 1px 2px var(--solid);pointer-events:none;white-space:nowrap}
133
223
  /* resting-pose pill preview, game z-order sandwich: far pills (3) behind
134
224
  the sprite (5), near pills (7) over it — never intercept a drag */
135
- .pill{position:absolute;border-radius:999px;z-index:3;opacity:.9;pointer-events:none}
225
+ .pill{position:absolute;border-radius:3px;z-index:3;opacity:.9;pointer-events:none}
136
226
  .pill.near{z-index:7}
137
- #rectbar{position:absolute;left:14px;bottom:14px;z-index:11;gap:8px;align-items:center;
138
- background:#000b;border:1px solid var(--line);border-radius:10px;padding:7px 10px;font-size:12px}
139
- #rectbar input[type=number]{width:64px;padding:4px}
227
+ #rectbar{position:absolute;left:14px;bottom:14px;z-index:11;gap:var(--s2);align-items:center;
228
+ background:var(--scrim);border:1px solid var(--line);border-radius:5px;padding:var(--s2);font-size:12px}
229
+ #rectbar input[type=number]{width:64px;padding:var(--s1)}
140
230
  #rectbar .ro{color:var(--dim);font:11px ui-monospace,Menlo,monospace}
141
- #rectbar label{display:flex;gap:6px;align-items:center}
231
+ #rectbar label{display:flex;gap:var(--s1);align-items:center}
142
232
 
143
233
  /* ── category grid + lineup ── */
144
- #catgrid{flex:1;min-height:0;overflow-y:auto;padding:14px}
145
- .cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:12px}
146
- .card{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:8px;cursor:pointer}
234
+ #catgrid{flex:1;min-height:0;overflow-y:auto;padding:var(--s3)}
235
+ .cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:var(--s3)}
236
+ .card{background:var(--panel);border:1px solid var(--line);border-radius:5px;padding:var(--s2);cursor:pointer}
147
237
  .card:hover{border-color:var(--accent)}
148
- .card .th{position:relative;aspect-ratio:1;border-radius:8px;overflow:hidden;
238
+ .card .th{position:relative;aspect-ratio:1;border-radius:4px;overflow:hidden;
149
239
  background:conic-gradient(var(--chk1) 90deg,var(--chk2) 0 180deg,var(--chk1) 0 270deg,var(--chk2) 0) 0/14px 14px}
150
240
  .card .th img{width:100%;height:100%;object-fit:contain}
151
- .card .th .ovmini{position:absolute;border:1.5px solid var(--gold);border-radius:50%;background:rgba(255,207,92,.12)}
241
+ .card .th .ovmini{position:absolute;border:1.5px solid var(--accent);border-radius:50%;background:var(--wash2)}
152
242
  .card .th .spin{position:absolute;top:8px;right:8px}
153
- .card .nm{font-size:12px;margin:6px 0 4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:pointer}
154
- .card .nm:hover{color:var(--gold)}
155
- .card .row{display:flex;gap:6px;align-items:center;font-size:11px;color:var(--dim)}
156
- .card .row input{width:58px;padding:3px}
157
- .card .aud{aspect-ratio:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;font-size:28px}
243
+ .card .nm{font-size:12px;margin:var(--s1) 0 var(--s1);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:pointer}
244
+ .card .nm:hover{color:var(--accent)}
245
+ .card .row{display:flex;gap:var(--s1);align-items:center;font-size:11px;color:var(--dim)}
246
+ .card .row input{width:58px;padding:var(--s0)}
247
+ .card .aud{aspect-ratio:1;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--s2);font-size:28px}
158
248
  .card .aud audio{width:92%;height:24px}
159
- #allbar{display:flex;gap:8px;align-items:center;margin:0 0 12px;font-size:12px;color:var(--dim)}
160
- #allbar label{display:flex;gap:6px;align-items:center}
161
- #mixsec{margin-top:16px}
249
+ #allbar{display:flex;gap:var(--s2);align-items:center;margin:0 0 var(--s3);font-size:12px;color:var(--dim)}
250
+ #allbar label{display:flex;gap:var(--s1);align-items:center}
251
+ #mixsec{margin-top:var(--s4)}
162
252
  #mixbox{position:relative;height:420px;overflow-x:auto;overflow-y:hidden;background:var(--panel);
163
- border:1px solid var(--line);border-radius:10px}
253
+ border:1px solid var(--line);border-radius:5px}
164
254
  #mixinner{position:relative;height:100%}
165
- #mixground{position:absolute;left:0;right:0;bottom:0;height:44px;background:#1d2b1a;border-top:1px solid #2c4527}
255
+ #mixground{position:absolute;left:0;right:0;bottom:0;height:44px;background:var(--ground);border-top:1px solid var(--groundline)}
166
256
  .mixsprite{position:absolute;image-rendering:auto;pointer-events:none}
167
257
  .mixlabel{position:absolute;font-size:10px;color:var(--dim);text-align:center;transform:translateX(-50%)}
168
258
  .ruler{position:absolute;left:0;right:0;bottom:44px;top:0;pointer-events:none;
169
259
  background:
170
- repeating-linear-gradient(to top,rgba(255,255,255,.28) 0 1px,transparent 1px 240px),
171
- repeating-linear-gradient(to top,rgba(255,255,255,.16) 0 1px,transparent 1px 120px),
172
- repeating-linear-gradient(to top,rgba(255,255,255,.07) 0 1px,transparent 1px 24px)}
260
+ repeating-linear-gradient(to top,var(--hi1) 0 1px,transparent 1px 240px),
261
+ repeating-linear-gradient(to top,var(--hi2) 0 1px,transparent 1px 120px),
262
+ repeating-linear-gradient(to top,var(--hi3) 0 1px,transparent 1px 24px)}
173
263
  .rlabel{position:absolute;left:4px;font-size:9px;color:var(--dim)}
174
264
  .rlabel.b{font-weight:700;color:var(--ink)}
175
265
 
176
266
  /* ── roll vignettes (shared by the variants bar + category cards) ── */
177
- .vig{position:relative;height:100%;aspect-ratio:1;flex:none;border:2px solid var(--line);border-radius:10px;overflow:hidden;cursor:pointer;
267
+ .vig{position:relative;height:100%;aspect-ratio:1;flex:none;border:2px solid var(--line);border-radius:5px;overflow:hidden;cursor:pointer;
178
268
  background:conic-gradient(var(--chk1) 90deg,var(--chk2) 0 180deg,var(--chk1) 0 270deg,var(--chk2) 0) 0/12px 12px}
179
269
  .vig img{width:100%;height:100%;object-fit:contain}
180
270
  .vig.active{border-color:var(--ink)}
181
- .vig.picked{border-color:var(--gold);box-shadow:0 0 0 2px var(--gold) inset}
182
- .vig.alt{border-color:var(--teal)}
183
- .vig .st{position:absolute;top:4px;right:6px;font-size:16px;filter:drop-shadow(0 1px 2px #000)}
184
- .vig .btns{position:absolute;bottom:4px;left:4px;right:4px;display:flex;gap:4px;justify-content:center;opacity:0;transition:opacity .15s}
271
+ .vig.picked{border-color:var(--accent);box-shadow:0 0 0 2px var(--accent) inset}
272
+ .vig.alt{border-color:var(--ok)}
273
+ .vig .st{position:absolute;top:4px;right:6px;font-size:16px;filter:drop-shadow(0 1px 2px var(--solid))}
274
+ .vig .btns{position:absolute;bottom:4px;left:4px;right:4px;display:flex;gap:var(--s1);justify-content:center;opacity:0;transition:opacity .15s}
185
275
  .vig:hover .btns{opacity:1}
186
- .vig .btns button{padding:2px 7px;font-size:11px;border-radius:6px}
187
- .vig .btns .b-alt{background:var(--teal);color:#06342c}
188
- .vig .btns .b-del{background:var(--red);color:#fff}
189
- .vig.aud{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;font-size:20px}
276
+ .vig .btns button{padding:var(--s0) var(--s2);font-size:11px;border-radius:3px}
277
+ .vig .btns .b-alt{color:var(--ok);border-color:var(--ok)}
278
+ .vig .btns .b-del{color:var(--red);border-color:var(--red)}
279
+ .vig.aud{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--s1);font-size:20px}
190
280
  .vig.aud audio{width:100%;height:24px}
191
- .vig.gen{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:8px;color:var(--dim);font-size:11px}
281
+ .vig.gen{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--s2);color:var(--dim);font-size:11px}
192
282
 
193
283
  /* ── tools bar + center row (stage column + variants side bar) ── */
194
- .toolsbar{display:flex;gap:6px;align-items:center;padding:7px 16px;border-bottom:1px solid var(--line);background:var(--panel)}
195
- .toolsbar .tab{background:transparent;color:var(--dim);border:0;border-bottom:2px solid transparent;border-radius:0;padding:4px 10px;font-size:12.5px;font-weight:650}
284
+ .toolsbar{display:flex;gap:var(--s1);align-items:center;padding:var(--s2) var(--s4);border-bottom:1px solid var(--line);background:var(--panel)}
285
+ .toolsbar .tab{background:transparent;color:var(--dim);border:0;border-bottom:2px solid transparent;border-radius:0;padding:var(--s1) var(--s2);font-size:12.5px;font-weight:650}
196
286
  .toolsbar .tab.on{color:var(--ink);border-bottom-color:var(--accent)}
197
- .toolsbar .tdiv{width:1px;align-self:stretch;background:var(--line);margin:0 8px}
198
- .toolsbar .tlab{font-size:11px;color:var(--dim);text-transform:uppercase;letter-spacing:.06em;margin-right:4px}
199
- .toolsbar .chip{background:var(--panel2);color:var(--dim);border:1px solid var(--line);border-radius:999px;padding:4px 13px;font-size:12px;font-weight:600}
287
+ .toolsbar .tdiv{width:1px;align-self:stretch;background:var(--line);margin:0 var(--s2)}
288
+ .toolsbar .tlab{font-size:11px;color:var(--dim);text-transform:uppercase;letter-spacing:.06em;margin-right:var(--s1)}
289
+ .toolsbar .chip{background:var(--panel2);color:var(--dim);border:1px solid var(--line);border-radius:3px;padding:var(--s1) var(--s3);font-size:12px;font-weight:600}
200
290
  .toolsbar .chip.on{color:var(--ink);background:var(--bg);border-color:var(--accent)}
201
291
  .toolsbar .sp{flex:1}
202
- .toolsbar .ghost{font-size:12px;padding:4px 11px}
292
+ .toolsbar .ghost{font-size:12px;padding:var(--s1) var(--s3)}
203
293
  .centerrow{display:flex;flex:1;min-height:0}
204
294
  .ovlhost{position:absolute;z-index:6;pointer-events:none}
205
295
  .fpanel{position:absolute;z-index:12;min-width:230px;max-width:520px;background:var(--panel);border:1px solid var(--line);
206
- border-radius:10px;box-shadow:0 10px 30px #000a;display:flex;flex-direction:column}
207
- .fhead{display:flex;align-items:center;justify-content:space-between;gap:8px;padding:4px 9px;border-bottom:1px solid var(--line);
296
+ border-radius:5px;box-shadow:0 10px 30px var(--shadow);display:flex;flex-direction:column}
297
+ .fhead{display:flex;align-items:center;justify-content:space-between;gap:var(--s2);padding:var(--s1) var(--s2);border-bottom:1px solid var(--line);
208
298
  cursor:grab;font-size:10.5px;color:var(--dim);text-transform:uppercase;letter-spacing:.06em;user-select:none}
209
- .fhead .x{background:transparent;color:var(--dim);border:0;padding:0 2px;font-size:12px}
299
+ .fhead .x{background:transparent;color:var(--dim);border:0;padding:0 var(--s0);font-size:12px}
210
300
  .fhead .x:hover{color:var(--ink)}
211
- .fbody{padding:6px 9px;max-height:46vh;overflow:auto}
301
+ .fbody{padding:var(--s1) var(--s2);max-height:46vh;overflow:auto}
212
302
  /* panel content inherits the hub's chunky default button — inside a card
213
303
  everything is DENSE, repo panel scripts get this for free */
214
- .fbody button{padding:3px 9px;font-size:11.5px;font-weight:600;border-radius:6px}
215
- .fbody input:not([type=color]),.fbody select{padding:3px 6px;font-size:11.5px}
304
+ .fbody button{padding:var(--s0) var(--s2);font-size:11.5px;font-weight:600;border-radius:3px}
305
+ .fbody input:not([type=color]),.fbody select{padding:var(--s0) var(--s1);font-size:11.5px}
216
306
  .fbody label{font-size:11.5px}
217
307
  .fbody .jsonta{width:100%;min-height:160px}
218
308
  .vbar{flex:none;border-left:1px solid var(--line);background:var(--panel);display:flex;min-height:0;position:relative}
219
- .vbar.closed{width:26px;flex-direction:column;align-items:center;justify-content:center;gap:6px;cursor:pointer;color:var(--dim);font-size:11px}
220
- .vbar .vcount{background:var(--panel2);border:1px solid var(--line);border-radius:999px;padding:2px 6px;font-size:10px}
309
+ .vbar.closed{width:26px;flex-direction:column;align-items:center;justify-content:center;gap:var(--s1);cursor:pointer;color:var(--dim);font-size:11px}
310
+ .vbar .vcount{background:var(--panel2);border:1px solid var(--line);border-radius:3px;padding:var(--s0) var(--s1);font-size:10px}
221
311
  .vhandle{position:absolute;left:0;top:0;bottom:0;width:6px;cursor:col-resize;z-index:3}
222
312
  .vhandle:hover{background:var(--accent);opacity:.4}
223
- .vscroll{flex:1;overflow-y:auto;display:flex;flex-direction:column;gap:8px;padding:10px 8px 10px 12px}
313
+ .vscroll{flex:1;overflow-y:auto;display:flex;flex-direction:column;gap:var(--s2);padding:var(--s2) var(--s2) var(--s2) var(--s3)}
224
314
  .vbar .vig{width:100%;height:auto}
225
- .vbar .ph{color:var(--dim);font-size:12px;padding:8px}
315
+ .vbar .ph{color:var(--dim);font-size:12px;padding:var(--s2)}
226
316
 
227
317
  /* ── asset remark row ── */
228
- .assetrem{flex:none;border-top:1px solid var(--line);background:var(--panel);padding:8px 12px}
229
- .assetrem .row{display:flex;gap:10px;align-items:center}
230
- .assetrem input{flex:1;background:var(--bg);border:1px solid var(--line);border-radius:8px;color:var(--ink);padding:7px 10px;font-size:12.5px}
231
- .assetrem .rem{color:var(--teal);border-left:2px solid var(--teal);padding-left:10px;font-size:12.5px;margin:0 0 8px;white-space:pre-wrap}
318
+ .assetrem{flex:none;border-top:1px solid var(--line);background:var(--panel);padding:var(--s2) var(--s3)}
319
+ .assetrem .row{display:flex;gap:var(--s2);align-items:center}
320
+ .assetrem input{flex:1;background:var(--bg);border:1px solid var(--line);border-radius:4px;color:var(--ink);padding:var(--s2);font-size:12.5px}
321
+ .assetrem .rem{color:var(--ok);border-left:2px solid var(--ok);padding-left:var(--s2);font-size:12.5px;margin:0 0 var(--s2);white-space:pre-wrap}
232
322
 
233
323
  /* ── collection item card + status card ── */
234
- .colcard{max-width:760px;width:100%;background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:18px 22px;text-align:left}
235
- .colcard h3{margin:0 0 10px;font-size:15px;display:flex;gap:10px;align-items:baseline}
324
+ .colcard{max-width:760px;width:100%;background:var(--panel);border:1px solid var(--line);border-radius:3px;padding:var(--s4);text-align:left}
325
+ .colcard h3{margin:0 0 var(--s2);font-size:15px;display:flex;gap:var(--s2);align-items:baseline}
326
+ .colcard h3 .sp{flex:1}
236
327
  .colcard .truth{margin-left:auto;font:11px ui-monospace,Menlo,monospace;color:var(--dim)}
237
- .colcard .rem{color:var(--teal);border-left:2px solid var(--teal);padding-left:10px;font-size:12.5px;margin:8px 0;white-space:pre-wrap}
238
- .colcard .row{display:flex;gap:10px;align-items:center;margin-top:10px;flex-wrap:wrap}
239
- .colcard input{flex:1;min-width:220px;background:var(--bg);border:1px solid var(--line);border-radius:8px;color:var(--ink);padding:7px 10px;font-size:12.5px}
240
- .colcard code{display:block;background:var(--bg);border:1px solid var(--line);border-radius:8px;padding:8px 10px;font:12px ui-monospace,Menlo,monospace;white-space:pre-wrap;max-height:200px;overflow-y:auto;margin-top:8px}
241
- .colcard .cand{background:var(--bg);border:1px solid var(--line);border-radius:9px;padding:9px 12px;margin-top:8px}
328
+ .colcard .rem{color:var(--ok);border-left:2px solid var(--ok);padding-left:var(--s2);font-size:12.5px;margin:var(--s2) 0;white-space:pre-wrap}
329
+ .colcard .row{display:flex;gap:var(--s2);align-items:center;margin-top:var(--s2);flex-wrap:wrap}
330
+ .colcard input{flex:1;min-width:220px;background:var(--bg);border:1px solid var(--line);border-radius:4px;color:var(--ink);padding:var(--s2);font-size:12.5px}
331
+ .colcard code{display:block;background:var(--bg);border:1px solid var(--line);border-radius:4px;padding:var(--s2);font:12px ui-monospace,Menlo,monospace;white-space:pre-wrap;max-height:200px;overflow-y:auto;margin-top:var(--s2)}
332
+ .colcard .cand{background:var(--bg);border:1px solid var(--line);border-radius:3px;padding:var(--s2) var(--s3);margin-top:var(--s2)}
242
333
  .colcard .candhint{color:var(--dim);font-size:12.5px;white-space:pre-wrap}
243
- .colcard .err,.assetrem .err{color:var(--red);margin-top:8px;font-size:12.5px}
334
+ .colcard .err,.assetrem .err{color:var(--red);margin-top:var(--s2);font-size:12.5px}
244
335
  .colcard a{color:var(--accent)}
245
336
 
246
- .back{cursor:pointer;color:var(--dim);font-size:12px}
247
- .back:hover{color:var(--ink)}
248
- .colcard .back{margin-bottom:8px}
337
+ /* ONE GATE CHECK's page: small widgets on a responsive grid, each answering
338
+ one question and carrying the `(?)` for its own words. The label and value
339
+ table it replaces made every fact the same size and the same shape */
340
+ .checkcard{max-width:1000px}
341
+ /* the runs page: invocations on the left, one lane per check with its bar at
342
+ its real start and end, the three lists on the right. A bar wears the tones
343
+ of the check page's bar row, so red, warn, green and a grey replay mean the
344
+ same thing on both */
345
+ .tlcard{max-width:1400px}
346
+ /* the horizontal axis over the whole record: one svg, scrolled sideways,
347
+ zoomed by the wheel */
348
+ .tlax{overflow-x:auto;overflow-y:hidden;scrollbar-width:none;cursor:grab}
349
+ .tlax.dragging{cursor:grabbing}
350
+ .tlax::-webkit-scrollbar{display:none}
351
+ /* the zoom control floats over the chart's top right corner: it shows while
352
+ the pointer is on the chart, and stays while the chart is zoomed */
353
+ .tlchart{position:relative}
354
+ .tlfloat{position:absolute;top:var(--s1);right:var(--s1);z-index:4;display:flex;gap:var(--s1);align-items:center}
355
+ .tlzoom{background:var(--panel);opacity:0;transition:opacity .15s}
356
+ .tlchart:hover .tlzoom,.tlchart.zoomed .tlzoom{opacity:1}
357
+ /* the chart stopped following: one pill says so and takes it back to now */
358
+ .tllive{background:var(--panel);color:var(--accent);border-color:var(--accent)}
359
+ .tlax.compact{overflow:hidden;height:100%;padding-top:var(--s0)}
360
+ .tlaxin{position:relative}
361
+ .tlsvg{display:block}
362
+ .tlband{fill:var(--wash2);stroke:var(--line)}
363
+ .tlband.red{stroke:var(--red)}
364
+ .tlbandlbl{fill:var(--dim);font:10px ui-monospace,Menlo,monospace}
365
+ .tlb{fill:var(--dim);cursor:pointer}
366
+ .tlb:hover{stroke:var(--ink)}
367
+ .tlb.fast{fill:var(--ok)}
368
+ .tlb.plain{fill:var(--accent)}
369
+ .tlb.slow,.tlb.warn{fill:var(--warn)}
370
+ .tlb.bad,.tlb.red{fill:var(--red)}
371
+ .tlb.rep{fill:var(--line)}
372
+ .tlb.run{fill:var(--accent);animation:pulse 1.6s ease-in-out infinite}
373
+ .tlb.repo{fill:var(--k6)}
374
+ .tlband.run{stroke:var(--accent)}
375
+ .tlconc{fill:var(--hair)}
376
+ .tlconc.over{fill:var(--warn)}
377
+ .tlcap{stroke:var(--dim);stroke-dasharray:3 3}
378
+ .tlcaplbl{fill:var(--dim);font:9.5px ui-monospace,Menlo,monospace}
379
+ .tltick line{stroke:var(--line)}
380
+ .tltick text,.tlgap text{fill:var(--dim);font:9.5px ui-monospace,Menlo,monospace}
381
+ /* a folded idle stretch: a neutral slice the full height, apart from the
382
+ bars and the concurrency band, its duration inside */
383
+ .tlgap rect{fill:var(--hi3);stroke:var(--line);stroke-dasharray:2 3}
384
+ .tlgap.live rect{stroke:var(--accent)}
385
+ .tlgap.live text{fill:var(--ink)}
386
+ .tlgap text{font-size:10px;pointer-events:none}
387
+ .tlnow{stroke:var(--accent)}
388
+ /* fixed to the viewport, placed from the pointer: every box of the strip
389
+ clips its overflow, and a tooltip inside them is cut to a sliver */
390
+ .tlaxtip{position:fixed;z-index:30;display:flex;flex-direction:column;gap:0;padding:var(--s1) var(--s2);pointer-events:none;
391
+ background:var(--panel);border:1px solid var(--line);border-radius:3px;box-shadow:0 2px 12px var(--shadow);font-size:11px;white-space:nowrap}
392
+ .tlaxtip .dim{color:var(--dim)}
393
+ /* the runs page: one tab bar, then one list. Every line on a grid of fixed
394
+ columns (.rrun, .rtest, .rslow), 24px high, tabular numerals, numbers
395
+ right aligned, so ages, times and counts read down a column. A check line
396
+ shares its run's columns, its id spanning the three left of the time, and
397
+ its word sits in the ran or the cached column */
398
+ .rtabs{display:flex;gap:var(--s3);align-items:center;height:32px;border-bottom:1px solid var(--line)}
399
+ .rtabs .sp{flex:1}
400
+ .rtab{display:inline-flex;gap:var(--s1);align-items:center;height:32px;background:transparent;border:0;border-bottom:2px solid transparent;border-radius:0;padding:0 var(--s1);color:var(--dim);font-size:12.5px}
401
+ .rtab:hover{color:var(--ink)}
402
+ .rtab.on{color:var(--ink);border-bottom-color:var(--accent)}
403
+ .rbadge{min-width:18px;padding:0 var(--s1);border-radius:8px;font-size:10.5px;line-height:16px;text-align:center;font-variant-numeric:tabular-nums;background:var(--panel2);color:var(--dim)}
404
+ .rbadge.red{background:var(--errbg);color:var(--red)}
405
+ .rbadge.warn{color:var(--warn)}
406
+ .rlist{margin-top:var(--s2);font-size:12px;font-variant-numeric:tabular-nums}
407
+ .rline,.rhead{display:grid;gap:var(--s2);align-items:center;height:24px;padding:0 var(--s1);white-space:nowrap}
408
+ .rline{cursor:pointer}
409
+ .rline>*,.rhead>*{min-width:0;overflow:hidden;text-overflow:ellipsis}
410
+ .rline:hover{background:var(--panel2)}
411
+ .rline.red{box-shadow:inset 2px 0 0 var(--red)}
412
+ .rhead{font-size:10.5px;letter-spacing:.06em;text-transform:uppercase;color:var(--dim);text-align:right}
413
+ .rrun,.rchild{grid-template-columns:12px 64px 72px minmax(0,1fr) 88px 64px 88px 48px}
414
+ .rtest{grid-template-columns:152px minmax(0,1fr) 88px 144px 64px}
415
+ /* a test file page: its runs, when, verdict, time against its budget, tier */
416
+ .rtrun{grid-template-columns:64px minmax(0,1fr) 88px 88px;cursor:default}
417
+ .rtrun .rid{display:flex;gap:var(--s2);align-items:center}
418
+ /* the pages of the test groups: one line per file (where the time goes), one
419
+ per test check (the tests page), the failing files with their failure line */
420
+ .rtime{grid-template-columns:minmax(0,1fr) 88px 72px 128px 72px 40px 88px}
421
+ .rcheckt{grid-template-columns:152px 88px 48px 56px minmax(0,1fr) 72px}
422
+ .rhead .tleft{text-align:left}
423
+ .tshare{display:grid;grid-template-columns:minmax(0,1fr) 36px;gap:var(--s1);align-items:center}
424
+ .ttrack{display:block;height:6px;border-radius:1px;overflow:hidden;background:var(--panel2)}
425
+ .ttrack i{display:block;height:100%;background:var(--accent)}
426
+ .tfail{cursor:pointer}
427
+ .tfail:hover{background:var(--panel2)}
428
+ .tfline{grid-template-columns:minmax(0,1fr) 88px 64px}
429
+ .tfline.wc{grid-template-columns:152px minmax(0,1fr) 88px 64px}
430
+ .tfwhy{font:11px/1.5 ui-monospace,Menlo,monospace;color:var(--red);padding:0 var(--s1) var(--s1) var(--s4);white-space:pre-wrap;word-break:break-word}
431
+ .tfwhy.dim{color:var(--dim)}
432
+ .tsum{margin-bottom:var(--s2);font-size:12.5px}
433
+ .tsline{display:flex;flex-wrap:wrap;gap:var(--s3);align-items:baseline}
434
+ .tshape,.tstop{margin-top:var(--s1)}
435
+ .tshape.tail,.tshape.volume{color:var(--warn)}
436
+ /* the runner's wall cut into its phases, so time spent outside the files shows */
437
+ .tpbar{display:flex;height:10px;border-radius:2px;overflow:hidden;background:var(--panel2);margin-bottom:var(--s2)}
438
+ .tpbar i{display:block;height:100%}
439
+ .tpbar .p-setup{background:var(--dim)}
440
+ .tpbar .p-par{background:var(--accent)}
441
+ .tpbar .p-ser{background:var(--k6)}
442
+ .tpbar .p-alone{background:var(--warn)}
443
+ .tpbar .p-host{background:var(--line)}
444
+ .rslow{grid-template-columns:minmax(0,1fr) 88px 88px 144px}
445
+ .rran,.rcached{text-align:right}
446
+ .rran{color:var(--accent)}
447
+ .rcached{color:var(--dim)}
448
+ .rran.zero,.rcached.zero{opacity:.4}
449
+ .rcaret{color:var(--dim)}
450
+ .rwho{border:1px solid var(--line);border-radius:3px;padding:0 var(--s1);font-size:10px;line-height:16px;text-align:center;text-transform:uppercase;letter-spacing:.04em;color:var(--dim)}
451
+ .rchild .rid{grid-column:2/5;display:flex;gap:var(--s2);align-items:center;padding-left:var(--s4)}
452
+ .rchild .rname{min-width:0;overflow:hidden;text-overflow:ellipsis}
453
+ /* an unfolded run's checks scroll in a box of their own, so a fifty check
454
+ gate never pushes the other runs off the page */
455
+ .rkids{max-height:16rem;overflow-y:auto;margin-bottom:var(--s1);border-radius:3px;box-shadow:inset 0 0 0 1px var(--line);background:var(--bg)}
456
+ .rtag{font:11px ui-monospace,Menlo,monospace;overflow:hidden;text-overflow:ellipsis}
457
+ .rtag.dim,.rnum.dim{color:var(--dim)}
458
+ .rnum{text-align:right;font:12px ui-monospace,Menlo,monospace}
459
+ .rnum.fast{color:var(--ok)}
460
+ .rnum.slow{color:var(--warn)}
461
+ .rnum.bad{color:var(--red)}
462
+ .rred,.rflag.red{color:var(--red)}
463
+ .rflag.warn{color:var(--warn)}
464
+ .rempty{height:24px;line-height:24px;color:var(--dim)}
465
+ /* it holds its width: a label growing from `run it now` to `running` used
466
+ to resize the header under the pointer */
467
+ .urun{min-width:104px;display:inline-flex;gap:var(--s1);align-items:center;justify-content:center}
468
+ /* a titled card and its label and value facts: the runs page lists and the
469
+ project card read these */
470
+ .wcard{background:var(--bg);border:1px solid var(--line);border-radius:3px;padding:var(--s3);min-width:0}
471
+ .wcard.wide{grid-column:1/-1}
472
+ .wtitle{display:flex;gap:var(--s1);align-items:center;font-size:11px;text-transform:uppercase;letter-spacing:.08em;
473
+ color:var(--dim);margin-bottom:var(--s2)}
474
+ .wbody{font-size:12.5px;min-width:0}
475
+ .wfact{display:flex;gap:var(--s2);align-items:baseline;padding:var(--s1) 0;border-top:1px solid var(--line);min-width:0}
476
+ .wk{flex:0 0 88px;color:var(--dim);font-size:11px;display:flex;gap:var(--s0);align-items:center}
477
+ .wv{flex:1;min-width:0;word-break:break-word}
478
+ .wcard code{display:inline;background:var(--panel2);border:1px solid var(--line);border-radius:3px;
479
+ padding:0 var(--s1);margin:0;font:11.5px ui-monospace,Menlo,monospace;max-height:none;white-space:normal}
480
+ .wcard .dim{color:var(--dim)}
481
+ .wcard .md{min-height:120px;max-height:320px;overflow-y:auto}
482
+ /* the head: the id as a small tag, then the title, then the run button */
483
+ .chead{display:flex;gap:var(--s2);align-items:baseline;flex-wrap:wrap}
484
+ .chead .sp{flex:1}
485
+ .checkcard h3.ctitle{margin:0;font-size:15px;font-weight:600;display:inline;min-width:0;overflow-wrap:anywhere}
486
+ /* ONE grid of facts: a dim label left, the value right, two per line on a
487
+ wide screen and one on a narrow one. No fact sits in a box of its own */
488
+ .cfacts{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));column-gap:var(--s4);margin-top:var(--s2)}
489
+ @media (max-width:900px){.cfacts{grid-template-columns:minmax(0,1fr)}}
490
+ .cf{display:grid;grid-template-columns:88px minmax(0,1fr);gap:var(--s2);align-items:baseline;padding:var(--s1) 0;
491
+ border-top:1px solid var(--line);min-width:0;font-size:12.5px}
492
+ .cf.wide{grid-column:1/-1}
493
+ .ck{color:var(--dim);font-size:11px;display:inline-flex;gap:var(--s0);align-items:center}
494
+ .cv{min-width:0}
495
+ .cblock{display:block;margin-top:var(--s1)}
496
+ .cwhen{display:inline-flex;gap:var(--s1);align-items:center;flex-wrap:wrap}
497
+ .ctimes{display:flex;gap:var(--s3);flex-wrap:wrap;align-items:baseline}
498
+ .ctimes .ck{margin-right:var(--s1)}
499
+ .cinputs summary{cursor:pointer}
500
+ /* the command: copyable monospace, wrapping only between tokens */
501
+ .ccmd{margin-top:var(--s2)}
502
+ .checkcard code{display:inline;background:var(--panel2);border:1px solid var(--line);border-radius:3px;
503
+ padding:0 var(--s1);margin:0;font:11.5px ui-monospace,Menlo,monospace;max-height:none;overflow:visible;white-space:normal}
504
+ .checkcard .cargv{user-select:all;padding:var(--s0) var(--s2);line-height:1.7}
505
+ .tok{white-space:nowrap}
506
+ a.tok{color:var(--accent)}
507
+ .ccopy{margin-left:var(--s2);font-size:10px;padding:0 var(--s2)}
508
+ .cscript{margin-top:var(--s1)}
509
+ /* a path never breaks mid token: it truncates, and its tooltip is the whole */
510
+ .checkcard .cpath{display:inline-block;max-width:100%;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;vertical-align:bottom}
511
+ .checkcard .dim{color:var(--dim)}
512
+ /* what is not a fact: the output and the slow suites, titled, unboxed */
513
+ .csec{margin-top:var(--s4)}
514
+ .cstitle{display:flex;gap:var(--s1);align-items:center;font-size:11px;text-transform:uppercase;letter-spacing:.08em;
515
+ color:var(--dim);margin-bottom:var(--s2)}
516
+ .wline{margin-bottom:var(--s1);word-break:break-word}
517
+ /* the check's own sentence, under the title: it answers "what is this row",
518
+ and the grid below it is detail. Rendered as markdown, so a code span and a
519
+ link in a description read as one */
520
+ .usays{margin:var(--s2) 0 0 0;font-size:13px;line-height:1.55}
521
+ .usays p{margin:0}
522
+ /* one wire line, with its prefix and its indentation kept: the pre holds the
523
+ shape, the inline markdown inside it makes a pointer clickable */
524
+ .wlogline{white-space:pre-wrap;word-break:break-word}
525
+ .wlogline code{background:var(--panel2);border-radius:3px;padding:0 2px}
526
+ /* one number, in the one unit, in the one tone (hub-app/dur.ts) */
527
+ .dnum{font:12px ui-monospace,Menlo,monospace;font-variant-numeric:tabular-nums}
528
+ /* the last runs, oldest left: an average hides a check creeping up, and the
529
+ ledger cannot even see the reds */
530
+ .bars{display:flex;gap:var(--s0);align-items:flex-end;height:34px;margin-top:var(--s2)}
531
+ .bars .bar{flex:1;min-width:3px;border-radius:1px;background:var(--dim);display:block}
532
+ .bars .bar.fast{background:var(--ok)}
533
+ .bars .bar.plain{background:var(--accent)}
534
+ .bars .bar.slow{background:var(--warn)}
535
+ .bars .bar.bad,.bars .bar.red{background:var(--red)}
536
+ .bars .bar.warn{background:var(--warn)}
537
+ .bars .bar.rep{background:var(--line)}
538
+ /* the lines of the run in flight, then the lines of the last one. A floor
539
+ height, so a streaming run never jumps the card it sits in */
540
+ .wlog{margin:0;min-height:120px;max-height:320px;overflow-y:auto;background:var(--panel2);border:1px solid var(--line);
541
+ border-radius:3px;padding:var(--s2);font:11.5px/1.5 ui-monospace,Menlo,monospace;color:var(--ink);white-space:pre-wrap}
542
+ /* THE HELP: a `(?)` that says its one sentence at once, on hover and on
543
+ focus. No delay, no library, no state */
544
+ .hlp{display:inline-flex;align-items:center;justify-content:center;width:13px;height:13px;flex:none;
545
+ border:1px solid var(--line);border-radius:50%;color:var(--dim);font-size:9px;line-height:1;cursor:help;
546
+ position:relative;outline:none;user-select:none}
547
+ .hlp:hover,.hlp:focus{color:var(--accent);border-color:var(--accent)}
548
+ .hbub{display:none;position:absolute;left:50%;bottom:calc(100% + 6px);transform:translateX(-50%);width:max-content;
549
+ max-width:260px;background:var(--panel2);border:1px solid var(--line);border-radius:3px;box-shadow:0 2px 10px var(--shadow);
550
+ padding:var(--s1) var(--s2);font:11.5px/1.4 system-ui,sans-serif;text-transform:none;letter-spacing:0;
551
+ color:var(--ink);white-space:normal;z-index:9}
552
+ .hlp:hover .hbub,.hlp:focus .hbub{display:block}
553
+ /* the type legend: every glyph of the tree with its word, in one bubble */
554
+ .legend{display:flex;flex-direction:column;gap:var(--s1)}
555
+ .legend .lrow{display:flex;gap:var(--s2);align-items:baseline;white-space:normal}
556
+ .legend .lrow i{font-style:normal;width:12px;text-align:center;flex:none}
557
+ /* the switch: one select named by effect, and the reason that makes `never`
558
+ sayable. It writes repo.config.ts, so the note under it is the file waiting
559
+ for a commit */
560
+ .wsel{min-width:150px}
561
+ .winputs{display:flex;flex-wrap:wrap;gap:var(--s1);margin-top:var(--s1)}
562
+ /* an age that moves: the one clock ticks, and only these repaint */
563
+ .ago{color:var(--dim);font-size:11.5px}
564
+ .wclear{background:var(--bg);color:var(--dim);border:1px solid var(--line);border-radius:3px;
565
+ padding:var(--s0) var(--s2);font-size:10px;text-transform:uppercase;letter-spacing:.06em;cursor:pointer}
566
+ .wclear:hover{color:var(--ink)}
567
+ .wreason{width:100%;background:var(--bg);border:1px solid var(--line);border-radius:4px;color:var(--ink);
568
+ padding:var(--s1) var(--s2);font-size:12.5px;outline:none;margin-top:var(--s2)}
569
+ .wreason:focus{border-color:var(--accent)}
570
+ .wsuite{border-top:1px solid var(--line);padding:var(--s2) 0}
571
+ .wfile{display:flex;gap:var(--s2);align-items:center}
572
+ .wform{display:flex;flex-direction:column;gap:var(--s2);margin-top:var(--s2)}
573
+ .wform input{background:var(--bg);border:1px solid var(--line);border-radius:4px;color:var(--ink);
574
+ padding:var(--s1) var(--s2);font-size:12.5px;outline:none}
575
+ .wform input:focus{border-color:var(--accent)}
249
576
 
250
577
  /* ── collection view (one card per instance) ── */
251
- .colgrid{flex:1;min-height:0;overflow-y:auto;padding:14px 16px}
578
+ .colgrid{flex:1;min-height:0;overflow-y:auto;padding:var(--s3) var(--s4)}
252
579
  .cghead{font-size:12px;letter-spacing:.14em;text-transform:uppercase;color:var(--accent);
253
- display:flex;gap:10px;align-items:baseline;margin:0 0 12px}
580
+ display:flex;gap:var(--s2);align-items:baseline;margin:0 0 var(--s3)}
254
581
  .cghead .cnt{font-size:11px;color:var(--dim);letter-spacing:0;text-transform:none}
255
- .cgcards{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:12px}
256
- .cgcard{background:var(--panel);border:1px solid var(--line);border-radius:10px;padding:10px 12px;cursor:pointer;min-width:0}
582
+ .cgcards{display:grid;grid-template-columns:repeat(auto-fill,minmax(240px,1fr));gap:var(--s3)}
583
+ .cgcard{background:var(--panel);border:1px solid var(--line);border-radius:5px;padding:var(--s2) var(--s3);cursor:pointer;min-width:0}
257
584
  .cgcard:hover{border-color:var(--accent)}
258
- .cgcard .cgtop{display:flex;gap:8px;align-items:center;min-width:0}
585
+ .cgcard .cgtop{display:flex;gap:var(--s2);align-items:center;min-width:0}
259
586
  .cgcard .cgtop b{font-size:13px;font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
260
- .cgcard .truth{display:block;font:11px ui-monospace,Menlo,monospace;color:var(--dim);margin-top:4px;
587
+ .cgcard .truth{display:block;font:11px ui-monospace,Menlo,monospace;color:var(--dim);margin-top:var(--s1);
261
588
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
262
- .cgcard .cgrem{color:var(--teal);border-left:2px solid var(--teal);padding-left:8px;font-size:12px;margin-top:6px;
589
+ .cgcard .cgrem{color:var(--ok);border-left:2px solid var(--ok);padding-left:var(--s2);font-size:12px;margin-top:var(--s1);
263
590
  white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
264
591
 
265
592
  /* ── graph item view (the graph itself fills the stage) ── */
266
- .graphview{flex:1;min-height:0;display:flex;flex-direction:column;gap:10px;padding:12px 16px 16px}
267
- .gvhead{display:flex;gap:10px;align-items:center;font-size:13px;min-width:0}
593
+ .graphview{flex:1;min-height:0;display:flex;flex-direction:column;gap:var(--s2);padding:var(--s3) var(--s4) var(--s4)}
594
+ .gvhead{display:flex;gap:var(--s2);align-items:center;font-size:13px;min-width:0}
268
595
  .gvhead .gvtext{white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
269
- .graphview iframe{flex:1;min-height:0;width:100%;border:1px solid var(--line);border-radius:12px;background:var(--bg)}
596
+ .graphview iframe{flex:1;min-height:0;width:100%;border:1px solid var(--line);border-radius:3px;background:var(--bg)}
597
+
598
+ /* ── comfy item view (comfy-ts's own panel fills the stage) ── */
599
+ .comfyview{flex:1;min-height:0;display:flex;flex-direction:column;gap:var(--s2);padding:var(--s3) var(--s4) var(--s4)}
600
+ .comfyview iframe{flex:1;min-height:0;width:100%;border:1px solid var(--line);border-radius:3px;background:var(--bg)}
601
+ .comfyview .gvhead button.ghost{padding:var(--s0) var(--s2);font-size:12px}
602
+ .comfyview .err{color:var(--red);font-size:12.5px}
603
+ .comfyview .ph{flex:1;display:flex;align-items:center;justify-content:center}
604
+ /* the same panel as a popup over an asset page, so the asset stays behind it */
605
+ .cfov{position:fixed;inset:0;background:var(--scrim);z-index:100;display:flex;align-items:center;justify-content:center;padding:4vh 3vw}
606
+ .cfbox{width:min(1200px,94vw);height:88vh;background:var(--panel);border:1px solid var(--line);border-radius:3px;
607
+ box-shadow:0 12px 40px var(--shadow);display:flex;flex-direction:column;gap:var(--s2);padding:var(--s3) var(--s3) var(--s3)}
608
+ .cfbox iframe{flex:1;min-height:0;width:100%;border:1px solid var(--line);border-radius:5px;background:var(--bg)}
609
+ .cfbox .gvhead button.ghost{padding:var(--s0) var(--s2);font-size:12px}
610
+ .cfbox .err{color:var(--red);font-size:12.5px}
611
+ .cfbox .ph{flex:1;display:flex;align-items:center;justify-content:center}
270
612
 
271
613
  /* ── md output (truth-file card bodies — marked + sanitize allowlist) ── */
272
614
  .md{font-size:12px;line-height:1.5;color:var(--ink)}
273
- .md table{border-collapse:collapse;margin:6px 0;font-size:11.5px;max-width:100%}
274
- .md th,.md td{border:1px solid var(--line);padding:3px 8px;text-align:left;vertical-align:top}
615
+ .md table{border-collapse:collapse;margin:var(--s1) 0;font-size:11.5px;max-width:100%}
616
+ .md th,.md td{border:1px solid var(--line);padding:var(--s0) var(--s2);text-align:left;vertical-align:top}
275
617
  .md th{color:var(--accent);font-weight:650;font-size:10.5px;letter-spacing:.05em;text-transform:uppercase;background:var(--panel2)}
276
- .md blockquote{margin:6px 0;padding:2px 10px;border-left:2px solid var(--line);color:var(--dim)}
277
- .md hr{border:0;border-top:1px solid var(--line);margin:8px 0}
278
- .md h1,.md h2,.md h3{font-size:12.5px;margin:8px 0 4px;color:var(--accent);letter-spacing:.02em}
618
+ .md blockquote{margin:var(--s1) 0;padding:var(--s0) var(--s2);border-left:2px solid var(--line);color:var(--dim)}
619
+ .md hr{border:0;border-top:1px solid var(--line);margin:var(--s2) 0}
620
+ .md h1,.md h2,.md h3{font-size:12.5px;margin:var(--s2) 0 var(--s1);color:var(--accent);letter-spacing:.02em}
279
621
  .md h1{font-size:13.5px}
280
- .md p{margin:4px 0}
281
- .md ul{margin:4px 0;padding-left:18px}
282
- .md li{margin:2px 0}
283
- .md code{display:inline;background:var(--bg);border:1px solid var(--line);border-radius:4px;padding:0 4px;
622
+ .md p{margin:var(--s1) 0}
623
+ .md ul{margin:var(--s1) 0;padding-left:var(--s4)}
624
+ .md li{margin:var(--s0) 0}
625
+ .md code{display:inline;background:var(--bg);border:1px solid var(--line);border-radius:4px;padding:0 var(--s1);
284
626
  font:11px ui-monospace,Menlo,monospace;color:var(--ink);white-space:pre-wrap;max-height:none;margin:0}
285
- .md pre{background:var(--bg);border:1px solid var(--line);border-radius:8px;padding:8px 10px;margin:6px 0;
627
+ .md pre{background:var(--bg);border:1px solid var(--line);border-radius:4px;padding:var(--s2);margin:var(--s1) 0;
286
628
  overflow-x:auto;font:11px/1.5 ui-monospace,Menlo,monospace;white-space:pre}
287
629
  .md pre code{background:none;border:0;padding:0}
288
630
  .md a{color:var(--accent)}
289
- .md.clamp{max-height:180px;overflow:hidden;margin-top:6px;
290
- -webkit-mask-image:linear-gradient(#000 65%,transparent);mask-image:linear-gradient(#000 65%,transparent)}
631
+ .md.clamp{max-height:180px;overflow:hidden;margin-top:var(--s1);
632
+ -webkit-mask-image:linear-gradient(var(--solid) 65%,transparent);mask-image:linear-gradient(var(--solid) 65%,transparent)}
291
633
 
292
634
  /* ── secret (cred) cards ── */
293
635
  .credname{font:600 13px ui-monospace,Menlo,monospace}
294
- .creddesc{font-size:12px;color:var(--dim);margin-top:4px}
295
- .credrow{display:flex;gap:8px;align-items:center;margin-top:8px}
296
- .stchip{font-size:10px;border-radius:5px;padding:1px 7px;letter-spacing:.08em;text-transform:uppercase;
636
+ .creddesc{font-size:12px;color:var(--dim);margin-top:var(--s1)}
637
+ .credrow{display:flex;gap:var(--s2);align-items:center;margin-top:var(--s2)}
638
+ .stchip{font-size:10px;border-radius:5px;padding:var(--s0) var(--s2);letter-spacing:.08em;text-transform:uppercase;
297
639
  border:1px solid;flex:none;font-weight:600}
298
- .stchip.ok{color:var(--teal);border-color:var(--teal)}
640
+ .stchip.ok{color:var(--ok);border-color:var(--ok)}
299
641
  .stchip.miss{color:var(--red);border-color:var(--red)}
300
642
  a.mint{color:var(--accent);font-size:12px;text-decoration:none}
301
643
  a.mint:hover{text-decoration:underline}
302
- .colcard h3 .stchip,.colcard h3 a.mint{margin-left:8px}
644
+ .colcard h3 .stchip,.colcard h3 a.mint{margin-left:var(--s2)}
303
645
 
304
646
  /* ── cmd+j palette ── */
305
- .palov{position:fixed;inset:0;background:#0009;z-index:100;display:flex;justify-content:center;align-items:flex-start;
647
+ .palov{position:fixed;inset:0;background:var(--scrim);z-index:100;display:flex;justify-content:center;align-items:flex-start;
306
648
  padding-top:14vh}
307
- .pal{width:min(560px,90vw);background:var(--panel);border:1px solid var(--line);border-radius:12px;
308
- box-shadow:0 12px 40px #000a;overflow:hidden}
649
+ .pal{width:min(560px,90vw);background:var(--panel);border:1px solid var(--line);border-radius:3px;
650
+ box-shadow:0 12px 40px var(--shadow);overflow:hidden}
309
651
  .pal input{width:100%;background:var(--bg);border:0;border-bottom:1px solid var(--line);color:var(--ink);
310
- padding:12px 14px;font-size:14px;outline:none}
311
- .prows{max-height:50vh;overflow-y:auto;padding:6px}
312
- .prow{display:flex;align-items:center;gap:10px;padding:7px 10px;border-radius:8px;cursor:pointer;font-size:13px}
652
+ padding:var(--s3);font-size:14px;outline:none}
653
+ .prows{max-height:50vh;overflow-y:auto;padding:var(--s1)}
654
+ .prow{display:flex;align-items:center;gap:var(--s2);padding:var(--s2);border-radius:4px;cursor:pointer;font-size:13px}
313
655
  .prow.on{background:var(--panel2)}
314
656
  .prow.none{color:var(--dim);cursor:default}
315
657
  .prow .plabel{flex:1;min-width:0;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
316
658
  .prow .pchip{font-size:10px;color:var(--dim);background:var(--bg);border:1px solid var(--line);border-radius:5px;
317
- padding:1px 6px;text-transform:uppercase;letter-spacing:.08em;flex:none}
659
+ padding:var(--s0) var(--s1);text-transform:uppercase;letter-spacing:.08em;flex:none}
318
660
 
319
661
  .wrap{max-width:900px;width:100%}
320
662
  .wrap h1{font-size:20px;margin:0}
321
- .wrap h1 small{color:var(--dim);font-weight:400;font-size:13px;margin-left:10px}
322
- .hbar{display:flex;align-items:center;gap:12px;margin:18px 0 12px}
663
+ .wrap h1 small{color:var(--dim);font-weight:400;font-size:13px;margin-left:var(--s2)}
664
+ .hbar{display:flex;align-items:center;gap:var(--s3);margin:var(--s4) 0 var(--s3)}
323
665
  .note{font-size:12px;color:var(--dim)}
324
- pre{background:var(--panel);border:1px solid var(--line);border-radius:12px;padding:16px;overflow-x:auto;
666
+ pre{background:var(--panel);border:1px solid var(--line);border-radius:3px;padding:var(--s4);overflow-x:auto;
325
667
  font:12.5px/1.55 ui-monospace,Menlo,monospace;white-space:pre-wrap}
326
- .ok{color:var(--teal)}.warn{color:var(--gold)}.fail{color:var(--red)}.dim{color:var(--dim)}
668
+ .ok{color:var(--ok)}.warn{color:var(--warn)}.fail{color:var(--red)}.dim{color:var(--dim)}