projectkeeper 0.0.0-stage → 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 (276) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/LICENSE +21 -0
  3. package/README.md +283 -2
  4. package/README.zh-CN.md +285 -0
  5. package/THIRD_PARTY_NOTICES.md +53 -0
  6. package/bin/pk.mjs +6 -0
  7. package/bin/postinstall.mjs +5 -0
  8. package/bin/strip-types.mjs +28 -0
  9. package/package.json +68 -4
  10. package/patches/README.md +111 -0
  11. package/patches/pi-ai-0.85.0.ts +122 -0
  12. package/patches/pi-ai-0.87.1.ts +122 -0
  13. package/scripts/apply-pi-streaming-patch.ts +86 -0
  14. package/skills/clerk/coverage/SKILL.md +35 -0
  15. package/skills/clerk/cross-check/SKILL.md +118 -0
  16. package/skills/clerk/deepen-dispatch/SKILL.md +69 -0
  17. package/skills/clerk/lane-follow-up/SKILL.md +94 -0
  18. package/skills/clerk/lane-plan/SKILL.md +107 -0
  19. package/skills/clerk/lane-slot/SKILL.md +113 -0
  20. package/skills/clerk/lane-topic/SKILL.md +116 -0
  21. package/skills/clerk/orientation/SKILL.md +160 -0
  22. package/skills/clerk/reconcile/SKILL.md +77 -0
  23. package/skills/clerk/skeleton-dispatch/SKILL.md +43 -0
  24. package/skills/clerk/spot-check/SKILL.md +52 -0
  25. package/skills/clerk/synthesis/SKILL.md +65 -0
  26. package/skills/table-follow-up/SKILL.md +42 -0
  27. package/src/cli.ts +319 -0
  28. package/src/codemap/README.md +13 -0
  29. package/src/codemap/engines.ts +102 -0
  30. package/src/codemap/facts.ts +504 -0
  31. package/src/context/agent-entry.ts +232 -0
  32. package/src/context/ask.ts +97 -0
  33. package/src/context/assemble.ts +1058 -0
  34. package/src/context/code-source.ts +103 -0
  35. package/src/context/k-briefs.ts +114 -0
  36. package/src/context/lineage.ts +100 -0
  37. package/src/context/object-brief.ts +300 -0
  38. package/src/context/owner-words.ts +191 -0
  39. package/src/context/pack-fixture.ts +342 -0
  40. package/src/context/project-rules.ts +160 -0
  41. package/src/intake/intake.ts +392 -0
  42. package/src/intake/material-rules.ts +166 -0
  43. package/src/intake/removal.ts +316 -0
  44. package/src/intake/skipped.ts +72 -0
  45. package/src/keeper/adjustment.ts +565 -0
  46. package/src/keeper/bounds/boundary.ts +509 -0
  47. package/src/keeper/bounds/command.ts +1665 -0
  48. package/src/keeper/bounds/paths.ts +164 -0
  49. package/src/keeper/bounds/reads.ts +718 -0
  50. package/src/keeper/bounds/shell-write-guard.ts +521 -0
  51. package/src/keeper/capabilities.ts +136 -0
  52. package/src/keeper/clerk-steps.ts +151 -0
  53. package/src/keeper/clerk-tools.ts +1424 -0
  54. package/src/keeper/conversation.ts +253 -0
  55. package/src/keeper/evidence.ts +662 -0
  56. package/src/keeper/fake-provider.ts +132 -0
  57. package/src/keeper/held.ts +15 -0
  58. package/src/keeper/history-tools.ts +163 -0
  59. package/src/keeper/jobs/answering.ts +53 -0
  60. package/src/keeper/key-check.ts +105 -0
  61. package/src/keeper/ledger-tools.ts +295 -0
  62. package/src/keeper/link-check.ts +185 -0
  63. package/src/keeper/merge.ts +260 -0
  64. package/src/keeper/note-origin.ts +56 -0
  65. package/src/keeper/numbers-check.ts +102 -0
  66. package/src/keeper/open-in-pi.ts +57 -0
  67. package/src/keeper/organize/absence.ts +266 -0
  68. package/src/keeper/organize/carried-on.ts +214 -0
  69. package/src/keeper/organize/clerk-coverage.ts +293 -0
  70. package/src/keeper/organize/clerk-prompts.ts +132 -0
  71. package/src/keeper/organize/clerk.ts +1395 -0
  72. package/src/keeper/organize/coverage-gate.ts +19 -0
  73. package/src/keeper/organize/coverage-tools.ts +765 -0
  74. package/src/keeper/organize/entries.ts +153 -0
  75. package/src/keeper/organize/entry-gate.ts +289 -0
  76. package/src/keeper/organize/fill-tools.ts +718 -0
  77. package/src/keeper/organize/follow-up.ts +280 -0
  78. package/src/keeper/organize/generation-check.ts +504 -0
  79. package/src/keeper/organize/graph.ts +230 -0
  80. package/src/keeper/organize/lane-tools.ts +289 -0
  81. package/src/keeper/organize/looked-tools.ts +54 -0
  82. package/src/keeper/organize/materials.ts +243 -0
  83. package/src/keeper/organize/owner-lines.ts +493 -0
  84. package/src/keeper/organize/place-tools.ts +216 -0
  85. package/src/keeper/organize/placement-inference.ts +1045 -0
  86. package/src/keeper/organize/placement-time.ts +320 -0
  87. package/src/keeper/organize/placing.ts +821 -0
  88. package/src/keeper/organize/positioning.ts +33 -0
  89. package/src/keeper/organize/readiness.ts +47 -0
  90. package/src/keeper/organize/reading.ts +401 -0
  91. package/src/keeper/organize/round-blocks.ts +65 -0
  92. package/src/keeper/organize/round-news.ts +164 -0
  93. package/src/keeper/organize/round-open.ts +586 -0
  94. package/src/keeper/organize/schedule-state.ts +39 -0
  95. package/src/keeper/organize/schedule.ts +137 -0
  96. package/src/keeper/organize/service.ts +409 -0
  97. package/src/keeper/organize/skills.ts +116 -0
  98. package/src/keeper/organize/slots.ts +85 -0
  99. package/src/keeper/organize/stage-gate.ts +175 -0
  100. package/src/keeper/organize/stage-tools.ts +471 -0
  101. package/src/keeper/organize/standing-notes.ts +141 -0
  102. package/src/keeper/organize/table-follow-up.ts +330 -0
  103. package/src/keeper/organize/takeover-clerk.ts +832 -0
  104. package/src/keeper/organize/takeover-state.ts +103 -0
  105. package/src/keeper/organize/takeover.ts +216 -0
  106. package/src/keeper/organize/update-pending.ts +535 -0
  107. package/src/keeper/organize/workbench-placement.ts +92 -0
  108. package/src/keeper/own-keys.ts +114 -0
  109. package/src/keeper/owner-text.ts +165 -0
  110. package/src/keeper/project-folder.ts +334 -0
  111. package/src/keeper/prompts.ts +101 -0
  112. package/src/keeper/references.ts +177 -0
  113. package/src/keeper/relook/compose.ts +120 -0
  114. package/src/keeper/relook/prompts.ts +36 -0
  115. package/src/keeper/roles.ts +259 -0
  116. package/src/keeper/round-tools.ts +227 -0
  117. package/src/keeper/route.ts +148 -0
  118. package/src/keeper/rules.ts +97 -0
  119. package/src/keeper/runtime.ts +1858 -0
  120. package/src/keeper/scope-tools.ts +194 -0
  121. package/src/keeper/sendback-text.ts +83 -0
  122. package/src/keeper/step-timing.ts +207 -0
  123. package/src/keeper/testing/clerk-script.ts +212 -0
  124. package/src/keeper/testing/streaming-arguments.ts +57 -0
  125. package/src/keeper/tools.ts +2340 -0
  126. package/src/keeper/utterance-tools.ts +64 -0
  127. package/src/keeper/verbatim.ts +397 -0
  128. package/src/ledger/adapters.ts +186 -0
  129. package/src/ledger/arrangements.ts +257 -0
  130. package/src/ledger/blame.ts +58 -0
  131. package/src/ledger/code-engine-child.ts +69 -0
  132. package/src/ledger/code-engine.ts +548 -0
  133. package/src/ledger/code.ts +607 -0
  134. package/src/ledger/docs.ts +294 -0
  135. package/src/ledger/git-read.ts +321 -0
  136. package/src/ledger/index.ts +1850 -0
  137. package/src/ledger/item-range.ts +16 -0
  138. package/src/ledger/lines.ts +328 -0
  139. package/src/ledger/numbering.ts +269 -0
  140. package/src/ledger/ranges.ts +74 -0
  141. package/src/ledger/rebuild-worker.ts +15 -0
  142. package/src/ledger/rebuild.ts +387 -0
  143. package/src/ledger/repo-scan.ts +181 -0
  144. package/src/ledger/schema.ts +450 -0
  145. package/src/ledger/sessions.ts +134 -0
  146. package/src/ledger/text-scan.ts +466 -0
  147. package/src/ledger/time.ts +185 -0
  148. package/src/ledger/ts-symbols.ts +180 -0
  149. package/src/ledger/views.ts +377 -0
  150. package/src/mcp/server.ts +127 -0
  151. package/src/mcp/tools.ts +187 -0
  152. package/src/model/ids.ts +23 -0
  153. package/src/model/k-types.ts +912 -0
  154. package/src/model/time.ts +85 -0
  155. package/src/model/types.ts +1382 -0
  156. package/src/model/views-k.ts +707 -0
  157. package/src/model/vocab.ts +269 -0
  158. package/src/process/analysis.ts +79 -0
  159. package/src/process/breakpoint-candidates.ts +383 -0
  160. package/src/process/breakpoints.ts +746 -0
  161. package/src/process/claim-check.ts +295 -0
  162. package/src/process/delivery-links.ts +52 -0
  163. package/src/process/facts.ts +487 -0
  164. package/src/process/generations.ts +138 -0
  165. package/src/process/index.ts +243 -0
  166. package/src/process/integration.ts +128 -0
  167. package/src/process/placement.ts +107 -0
  168. package/src/process/plan.ts +276 -0
  169. package/src/process/sendbacks.ts +244 -0
  170. package/src/process/text.ts +185 -0
  171. package/src/process/units.ts +543 -0
  172. package/src/process/unnumbered.ts +431 -0
  173. package/src/process/work.ts +884 -0
  174. package/src/scope/candidates.ts +86 -0
  175. package/src/scope/decisions.ts +229 -0
  176. package/src/scope/discover.ts +741 -0
  177. package/src/scope/ignore.ts +126 -0
  178. package/src/scope/ignored-place.ts +93 -0
  179. package/src/scope/skip.ts +161 -0
  180. package/src/scope/toolchain.ts +180 -0
  181. package/src/scope/worktree.ts +187 -0
  182. package/src/server/api.ts +549 -0
  183. package/src/server/app.ts +595 -0
  184. package/src/server/drafts-api.ts +170 -0
  185. package/src/server/folders-api.ts +243 -0
  186. package/src/server/graph-view.ts +639 -0
  187. package/src/server/http.ts +224 -0
  188. package/src/server/k-agent-api.ts +56 -0
  189. package/src/server/k-rounds-d99.ts +91 -0
  190. package/src/server/k-views.ts +405 -0
  191. package/src/server/keeper-page.ts +446 -0
  192. package/src/server/keys-api.ts +239 -0
  193. package/src/server/project-folder-api.ts +104 -0
  194. package/src/server/vendor.ts +14 -0
  195. package/src/server/workbench-content.ts +561 -0
  196. package/src/sources/anchor.ts +129 -0
  197. package/src/sources/files.ts +267 -0
  198. package/src/sources/gitobs.ts +41 -0
  199. package/src/sources/history.ts +231 -0
  200. package/src/sources/scan-worker.ts +10 -0
  201. package/src/sources/sessions/locate.ts +292 -0
  202. package/src/sources/sessions/read.ts +346 -0
  203. package/src/sources/sessions/scope.ts +42 -0
  204. package/src/sources/sessions/utterances.ts +223 -0
  205. package/src/sources/watch.ts +215 -0
  206. package/src/store/json-file.ts +50 -0
  207. package/src/store/paths.ts +39 -0
  208. package/src/store/project-store.ts +295 -0
  209. package/src/store/versions.ts +172 -0
  210. package/src/store/workspace.ts +216 -0
  211. package/src/table/check.ts +333 -0
  212. package/src/table/follow-up-tools.ts +30 -0
  213. package/src/table/follow-up.ts +344 -0
  214. package/src/table/import.ts +610 -0
  215. package/src/table/project-home.ts +61 -0
  216. package/src/table/sessions.ts +168 -0
  217. package/src/table/takeover.ts +483 -0
  218. package/src/util/git-check.ts +29 -0
  219. package/src/util/git.ts +411 -0
  220. package/src/util/own-ts.ts +15 -0
  221. package/src/util/paths.ts +225 -0
  222. package/template/FILLING-IN.md +148 -0
  223. package/template/examples/papertrail.json +747 -0
  224. package/template/projectkeeper.schema.json +1001 -0
  225. package/ui/app.js +1135 -0
  226. package/ui/failures.js +82 -0
  227. package/ui/flyout.js +120 -0
  228. package/ui/folder-chooser.js +264 -0
  229. package/ui/glossary.js +156 -0
  230. package/ui/graph-fit.js +157 -0
  231. package/ui/graph-material.js +61 -0
  232. package/ui/graph-palette.js +109 -0
  233. package/ui/graph-tools.js +62 -0
  234. package/ui/graph.js +1712 -0
  235. package/ui/index.html +31 -0
  236. package/ui/k/code-view.js +230 -0
  237. package/ui/k/draft-view.js +93 -0
  238. package/ui/k/k-views.css +224 -0
  239. package/ui/k/round-tree.js +450 -0
  240. package/ui/k/scope-k.js +159 -0
  241. package/ui/k-data.js +118 -0
  242. package/ui/k-fold.js +106 -0
  243. package/ui/k-process.css +302 -0
  244. package/ui/k-process.js +934 -0
  245. package/ui/keeper-activity.js +175 -0
  246. package/ui/keeper-view.js +801 -0
  247. package/ui/keeper.css +101 -0
  248. package/ui/key-order.js +62 -0
  249. package/ui/list-fold.js +67 -0
  250. package/ui/markdown.d.ts +21 -0
  251. package/ui/markdown.js +472 -0
  252. package/ui/placement.d.ts +92 -0
  253. package/ui/placement.js +445 -0
  254. package/ui/popover-place.d.ts +16 -0
  255. package/ui/popover-place.js +89 -0
  256. package/ui/popover.js +198 -0
  257. package/ui/project-folder.js +37 -0
  258. package/ui/shell-state.js +40 -0
  259. package/ui/strip-rows.js +171 -0
  260. package/ui/styles.css +492 -0
  261. package/ui/theme-pick.js +36 -0
  262. package/ui/theme.js +74 -0
  263. package/ui/themes/LICENSE.md +13 -0
  264. package/ui/themes/README.md +44 -0
  265. package/ui/themes/TOKENS.md +291 -0
  266. package/ui/themes/a1/board.css +1557 -0
  267. package/ui/themes/a1/pk.css +392 -0
  268. package/ui/themes/b1/board.css +1163 -0
  269. package/ui/themes/b1/pk.css +218 -0
  270. package/ui/themes/d2/board.css +518 -0
  271. package/ui/themes/d2/pk.css +243 -0
  272. package/ui/themes/guard.css +73 -0
  273. package/ui/themes/i1/board.css +776 -0
  274. package/ui/themes/i1/pk.css +223 -0
  275. package/ui/themes/manifest.json +13 -0
  276. package/ui/views.js +2796 -0
package/ui/styles.css ADDED
@@ -0,0 +1,492 @@
1
+ :root{--pk-keeper-w:420px;--pk-dock-h:60px;--pk-top:52px;--pk-gap:8px;--pk-pill-w:190px}
2
+ *{box-sizing:border-box}html,body{height:100%}body{margin:0;background:var(--pk-ground,#141413);color:var(--pk-text,#e7e7df);font:var(--pk-font-body,13px/1.55 var(--pk-font-ui,'Segoe UI Variable Text','Segoe UI','Microsoft YaHei',sans-serif))}
3
+ button,input,textarea,select{font:inherit}button{color:inherit;cursor:pointer;background:none;border:0}button:disabled{cursor:not-allowed;opacity:.5}
4
+ a{color:var(--pk-blue,#9fbdd5);text-decoration:none}a:hover{text-decoration:underline}
5
+ button:focus-visible,a:focus-visible,input:focus-visible,select:focus-visible,textarea:focus-visible,summary:focus-visible,[tabindex]:focus-visible{outline:2px solid var(--pk-accent,#e3b76f);outline-offset:2px}
6
+ h1,h2,h3,h4,p{margin:0}h1{font-size:22px;font-weight:600;letter-spacing:-.4px}h2{font-size:16px;font-weight:600}h3{font-size:13px;font-weight:600}h4{font-size:12px;font-weight:600;color:var(--pk-text-muted,#a5a59b)}
7
+ small{font-size:11px;color:var(--pk-text-muted,#a5a59b)}.muted{color:var(--pk-text-muted,#a5a59b)}.faint{color:var(--pk-text-faint,#85857b)}.mono{font:11px/1.5 var(--pk-font-mono,'Cascadia Code',Consolas,monospace)}code{font:11px/1.5 var(--pk-font-mono,'Cascadia Code',Consolas,monospace);background:var(--pk-code-bg,var(--pk-raised,#242420));padding:1px 4px;border-radius:var(--pk-radius-xs,3px)}
8
+ .btn{display:inline-flex;gap:6px;align-items:center;justify-content:center;min-height:30px;padding:5px 10px;border:1px solid var(--pk-control-border,var(--pk-line,#35352f));border-radius:var(--pk-control-radius,var(--pk-radius,6px));font-weight:500;white-space:nowrap;background:var(--pk-control-bg,var(--pk-surface,#1c1c1a));background-image:var(--pk-control-texture,none);box-shadow:var(--pk-control-shadow,none)}.btn:hover:not(:disabled){background:var(--pk-control-hover-bg,var(--pk-raised,#242420));border-color:var(--pk-control-hover-border,var(--pk-control-border,var(--pk-line,#35352f)))}
9
+ .btn.primary{color:var(--pk-on-accent,#211e16);background:var(--pk-accent,#e3b76f);border-color:var(--pk-accent,#e3b76f)}.btn.primary:hover:not(:disabled){background:var(--pk-accent-hi,#edc98f)}.btn.small{font-size:12px;min-height:26px;padding:3px 8px}.btn.danger{color:var(--pk-red,#dca6a0)}.btn.active{border-color:var(--pk-accent,#e3b76f);color:var(--pk-accent,#e3b76f);background:var(--pk-accent-bg,#322c20)}
10
+ .text-btn{display:inline-flex;align-items:center;gap:5px;padding:2px 0;color:var(--pk-blue,#9fbdd5);text-align:left}.text-btn:hover{color:var(--pk-text,#e7e7df)}
11
+ .tag{display:inline-flex;align-items:center;gap:4px;white-space:nowrap;font-size:11px;border-radius:var(--pk-radius-sm,4px);padding:1px 6px;background:var(--pk-raised,#242420);color:var(--pk-text-muted,#a5a59b);vertical-align:middle}.tag.amber{background:var(--pk-accent-bg,#322c20);color:var(--pk-accent,#e3b76f)}.tag.green{background:var(--pk-green-bg,#253129);color:var(--pk-green,#a3c5ac)}.tag.blue{background:var(--pk-blue-bg,#252e36);color:var(--pk-blue,#9fbdd5)}.tag.red{background:var(--pk-red-bg,#3a2a2a);color:var(--pk-red,#dca6a0)}.tag.purple{background:var(--pk-purple-bg,#2e2838);color:var(--pk-purple,#c4b0dc)}.tag.strike{text-decoration:line-through}
12
+ .dot{width:7px;height:7px;border-radius:50%;display:inline-block;background:var(--pk-text-faint,#85857b)}.dot.green{background:var(--pk-green,#a3c5ac)}.dot.amber{background:var(--pk-accent,#e3b76f)}.dot.red{background:var(--pk-red,#dca6a0)}.dot.blue{background:var(--pk-blue,#9fbdd5)}
13
+ .row{display:flex;align-items:center;gap:10px}.spread{justify-content:space-between}.wrap{flex-wrap:wrap}.stack{display:flex;flex-direction:column;gap:10px}.grow{flex:1;min-width:0}
14
+ .input{width:100%;background:var(--pk-well-bg,var(--pk-surface,#1c1c1a));color:var(--pk-text,#e7e7df);border:1px solid var(--pk-well-border,var(--pk-line,#35352f));border-radius:var(--pk-control-radius,var(--pk-radius-sm,5px));padding:7px 10px;box-shadow:var(--pk-well-shadow,none)}select.input{padding:6px 8px}textarea.input{resize:vertical;min-height:70px}
15
+ .field{display:flex;flex-direction:column;gap:5px}.field label{font-size:11px;color:var(--pk-text-muted,#a5a59b)}
16
+ kbd{font:10px var(--pk-font-ui,'Segoe UI Variable Text','Segoe UI','Microsoft YaHei',sans-serif);color:var(--pk-text-faint,#85857b);border:1px solid var(--pk-line,#35352f);border-radius:var(--pk-radius-xs,3px);padding:0 4px}
17
+
18
+ /* ── shell ── */
19
+ /* Floating panels with gaps between them (D100; Spec §6.1): the top bar across, the rail and the main panel under it,
20
+ the drawer under the main panel. The layout of the panels themselves is at the end of this file ("D100 · panels"). */
21
+ .shell{display:grid;grid-template-columns:var(--pk-rail-w,230px) minmax(0,1fr);grid-template-rows:auto minmax(0,1fr) auto;grid-template-areas:"top top" "rail main" "rail drawer";gap:var(--pk-gap);padding:var(--pk-gap);height:100dvh;min-height:480px}
22
+ .rail{background:var(--pk-rail,#191918);border-right:1px solid var(--pk-line-soft,#2b2b26);display:flex;flex-direction:column;min-height:0;overflow:auto;scrollbar-width:thin}
23
+ .brand{height:52px;display:flex;align-items:center;gap:9px;padding:0 16px;font-size:14px;font-weight:650;flex-shrink:0}.brand-mark{width:22px;height:22px;color:var(--pk-accent,#e3b76f)}
24
+ .rail-section{padding:10px 12px 4px;font-size:10.5px;letter-spacing:.4px;text-transform:uppercase;color:var(--pk-text-faint,#85857b)}
25
+ .project-picker{margin:0 10px 8px;display:flex;gap:6px}.project-picker select{flex:1}
26
+ nav.views{padding:0 8px;display:grid;gap:2px}.nav-item{display:flex;align-items:center;gap:8px;text-align:left;padding:7px 10px;border-radius:var(--pk-radius-sm,5px);color:var(--pk-text-muted,#a5a59b);width:100%}.nav-item:hover{background:var(--pk-raised,#242420)}.nav-item.active{background:var(--pk-accent-soft,#302b22);color:var(--pk-accent,#e3b76f)}.nav-item .count{margin-left:auto;font:11px var(--pk-font-mono,'Cascadia Code',Consolas,monospace);color:var(--pk-text-faint,#85857b)}.nav-item.active .count{color:var(--pk-accent,#e3b76f)}
27
+ /* Which of the two the rail is showing, said in the corner rather than left to be worked out (owner 2026-09-17). */
28
+ .outline-head{display:flex;align-items:center;justify-content:space-between;gap:6px}
29
+ .outline-modes{display:inline-flex;border:1px solid var(--pk-control-border,var(--pk-line,#35352f));border-radius:var(--pk-radius-sm,4px);overflow:hidden;text-transform:none;letter-spacing:0}
30
+ .outline-modes button{padding:1px 6px;font-size:10px;color:var(--pk-text-faint,#85857b);background:var(--pk-control-bg,var(--pk-surface,#1c1c1a))}
31
+ .outline-modes button:hover{color:var(--pk-text,#e7e7df)}
32
+ .outline-modes button.active{background:var(--pk-accent-bg,#322c20);color:var(--pk-accent,#e3b76f)}
33
+ .outline{padding:4px 8px 8px;font-size:12px;display:flex;flex-direction:column}.outline .ol-area{padding:4px 10px;border-radius:var(--pk-radius-sm,4px);color:var(--pk-text,#e7e7df);cursor:pointer;display:flex;justify-content:space-between;gap:8px;width:100%;text-align:left}.outline .ol-area>span{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.outline .ol-area:hover,.outline .ol-work:hover{background:var(--pk-raised,#242420)}.outline .ol-area.sel,.outline .ol-work.sel{color:var(--pk-accent,#e3b76f)}.outline .ol-work{display:block;width:100%;text-align:left;padding:3px 10px 3px 22px;color:var(--pk-text-muted,#a5a59b);cursor:pointer;border-radius:var(--pk-radius-sm,4px);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.outline .empty{padding:4px 10px;color:var(--pk-text-faint,#85857b);font-size:11px}
34
+ /* The Keeper's status at the foot of the rail. It lists what is running; the cap keeps a burst of parallel jobs
35
+ from pushing the rail's own content off screen, and the full list is in its title and in Keeper activity. */
36
+ .rail-foot{margin-top:auto;padding:10px}
37
+ /* The views sit at the top of the rail and the project's structure scrolls under them by itself (owner 2026-09-30), so
38
+ the way back to the Project graph is always in sight. */
39
+ .rail>nav.views{flex:none;margin-bottom:4px}
40
+ .rail>.outline{flex:1 1 auto;min-height:96px;overflow:auto;scrollbar-width:thin}
41
+ .keeper-entry{display:flex;align-items:flex-start;gap:8px;width:100%;text-align:left;padding:9px 10px;border:1px solid var(--pk-card-border,var(--pk-line,#35352f));border-radius:var(--pk-card-radius,var(--pk-radius,6px));background:var(--pk-card-bg,var(--pk-surface,#1c1c1a));background-image:var(--pk-card-texture,none);box-shadow:var(--pk-card-shadow,none)}
42
+ .keeper-entry .pi-mark{font-family:var(--pk-font-display,Georgia,serif);font-size:20px;line-height:1.1;color:var(--pk-accent,#e3b76f)}
43
+ .keeper-entry .kw{flex:1;min-width:0}
44
+ .keeper-entry .kw b{display:block;font-weight:600;font-size:12px}
45
+ .keeper-entry small{display:block;max-height:4.8em;overflow:hidden}
46
+
47
+ /* The Keeper's dock, bottom right: Follow up, then the robot (owner 2026-09-17, after WorkflowKeeper's). */
48
+ .keeper-dock{position:fixed;right:var(--pk-gap);bottom:var(--pk-gap);z-index:30}
49
+ .dock-pill{display:flex;align-items:stretch;height:44px;border-radius:var(--pk-radius-pill,22px);overflow:hidden;background:var(--pk-control-bg,var(--pk-surface,#1c1c1a));background-image:var(--pk-control-texture,none);border:1px solid var(--pk-accent,#e3b76f);box-shadow:var(--pk-shadow-raised,0 4px 16px #0006)}
50
+ .dock-follow{display:inline-flex;align-items:center;gap:6px;padding:0 15px;font-weight:600;font-size:13px;color:var(--pk-text,#e7e7df)}
51
+ .dock-follow:hover:not(:disabled){background:var(--pk-raised,#242420)}.dock-follow:disabled{opacity:.45}
52
+ .dock-sep{width:1px;background:var(--pk-line,#35352f);margin:9px 0}
53
+ .dock-robot{display:inline-flex;align-items:center;gap:6px;padding:0 15px;font-size:13px;color:var(--pk-text,#e7e7df)}
54
+ .dock-robot:hover{background:var(--pk-raised,#242420)}.dock-robot.on{background:var(--pk-accent-bg,#322c20);color:var(--pk-accent,#e3b76f)}
55
+ .dock-robot svg{width:22px;height:22px;fill:none;stroke:var(--pk-accent,#e3b76f);stroke-width:1.7;stroke-linecap:round}
56
+ .workbench{min-width:0;display:flex;flex-direction:column;min-height:0}
57
+ .topbar{min-height:52px;height:auto;flex-shrink:0;border-bottom:1px solid var(--pk-line-soft,#2b2b26);display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:6px 10px;padding:7px 16px}
58
+ .topbar-main{display:flex;align-items:center;gap:10px;min-width:0;flex:1 1 620px}.crumb{display:flex;align-items:center;gap:8px;color:var(--pk-text-muted,#a5a59b);min-width:120px;max-width:min(34vw,440px);font-size:12px;flex:0 1 auto}.crumb strong{font-weight:500;color:var(--pk-text,#e7e7df);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.crumb .sep{color:var(--pk-text-faint,#85857b)}
59
+ .coverage-pill{display:inline-flex;align-items:center;gap:8px;border:1px solid var(--pk-control-border,var(--pk-line,#35352f));border-radius:var(--pk-control-radius,var(--pk-radius-sm,5px));padding:4px 9px;font-size:11px;color:var(--pk-text-muted,#a5a59b);background:var(--pk-control-bg,var(--pk-surface,#1c1c1a));background-image:var(--pk-control-texture,none);box-shadow:var(--pk-control-shadow,none)}.coverage-pill b{font-weight:500;color:var(--pk-text,#e7e7df)}.coverage-pill>span{white-space:nowrap}
60
+ .top-actions{display:flex;align-items:center;justify-content:flex-end;flex-wrap:wrap;gap:6px 8px;min-width:0}.top-view-tools{display:flex;align-items:center;min-width:0}.top-view-tools[hidden]{display:none}
61
+ /* The theme picker (Spec §6.16): a control of the top bar, drawn like the other controls; the choice lives in this browser. */
62
+ .theme-pick{display:inline-flex;align-items:center;gap:6px;font-size:12px;color:var(--pk-text-muted,#a5a59b);white-space:nowrap}.theme-pick select{min-height:26px;max-width:124px;padding:3px 6px;font-size:12px;text-overflow:ellipsis;color:var(--pk-text,#e7e7df);background:var(--pk-control-bg,var(--pk-surface,#1c1c1a));background-image:var(--pk-control-texture,none);border:1px solid var(--pk-control-border,var(--pk-line,#35352f));border-radius:var(--pk-control-radius,var(--pk-radius-sm,5px));box-shadow:var(--pk-control-shadow,none)}.theme-pick select:hover{background:var(--pk-control-hover-bg,var(--pk-raised,#242420))}.search-trigger{display:flex;gap:14px;align-items:center;color:var(--pk-text-muted,#a5a59b);border:1px solid var(--pk-control-border,var(--pk-line,#35352f));border-radius:var(--pk-control-radius,var(--pk-radius-sm,5px));padding:4px 8px;font-size:12px;background:var(--pk-control-bg,var(--pk-surface,#1c1c1a));background-image:var(--pk-control-texture,none);box-shadow:var(--pk-control-shadow,none)}
63
+ /* The main view alone (D67): an object's details open in the popover beside it and the right belongs to the Keeper.
64
+ Every view ends above the pill's band; the graph view keeps the corner clear inside its bottom strip instead. */
65
+ .body{display:flex;flex-direction:column;min-height:0;min-width:0;flex:1;position:relative}
66
+ main{flex:1;min-height:0;overflow:auto;min-width:0;padding:18px 22px 28px;scrollbar-width:thin;scrollbar-color:var(--pk-line-strong,#4b4b42) transparent;display:flex;flex-direction:column;gap:14px}
67
+ main.graph-main{padding:0;gap:0}
68
+ .page-head{display:flex;gap:16px;align-items:flex-start;justify-content:space-between}.page-head .sub{margin-top:4px;color:var(--pk-text-muted,#a5a59b);font-size:12px;max-width:70ch}
69
+ .section{border:1px solid var(--pk-section-border,var(--pk-line-soft,#2b2b26));border-radius:var(--pk-section-radius,var(--pk-radius,6px));background:var(--pk-section-bg,var(--pk-surface,#1c1c1a));box-shadow:var(--pk-section-shadow,none)}.section>header{padding:9px 12px;border-bottom:1px solid var(--pk-head-border,var(--pk-line-soft,#2b2b26));background:var(--pk-head-bg,transparent);background-image:var(--pk-head-texture,none);box-shadow:var(--pk-head-shadow,none);color:var(--pk-head-text,var(--pk-text,#e7e7df));border-radius:var(--pk-section-radius,var(--pk-radius,6px)) var(--pk-section-radius,var(--pk-radius,6px)) 0 0;display:flex;justify-content:space-between;align-items:center;gap:8px;font-size:12px;font-weight:600}.section>.content{padding:10px 12px;display:flex;flex-direction:column;gap:8px;font-size:12px}
70
+ .kv{display:grid;grid-template-columns:110px minmax(0,1fr);gap:4px 10px;font-size:12px}.kv dt{color:var(--pk-text-faint,#85857b)}.kv dd{margin:0;overflow-wrap:anywhere}
71
+ details.fold{border:1px solid var(--pk-section-border,var(--pk-line-soft,#2b2b26));border-radius:var(--pk-section-radius,var(--pk-radius-sm,5px));background:var(--pk-section-bg,var(--pk-surface,#1c1c1a))}details.fold>summary{cursor:pointer;padding:7px 10px;font-size:12px;color:var(--pk-text-muted,#a5a59b);list-style:none;display:flex;align-items:center;gap:6px}details.fold>summary::before{content:'▸';font-size:10px;color:var(--pk-text-faint,#85857b)}details.fold[open]>summary::before{content:'▾'}details.fold>.content{padding:0 10px 10px;font-size:12px}
72
+ .empty{padding:44px 20px;text-align:center;color:var(--pk-text-muted,#a5a59b);max-width:64ch;margin:0 auto}.empty h2{color:var(--pk-text,#e7e7df);margin-bottom:8px;font-size:17px}.empty p{margin-bottom:14px;font-size:12px}
73
+ table.list{width:100%;border-collapse:collapse;text-align:left;font-size:12px}table.list th{font-size:11px;font-weight:400;color:var(--pk-text-faint,#85857b);padding:8px;border-bottom:1px solid var(--pk-line,#35352f)}table.list td{padding:9px 8px;border-bottom:1px solid var(--pk-line-soft,#2b2b26);vertical-align:top;overflow-wrap:anywhere}table.list tr.clickable{cursor:pointer}table.list tr.clickable:hover td{background:var(--pk-surface,#1c1c1a)}table.list tr.sel td{background:var(--pk-sel-bg,#1f1f1b);box-shadow:inset 3px 0 0 var(--pk-accent,#e3b76f)}
74
+ .scope-item{display:grid;grid-template-columns:130px 170px minmax(0,1fr) auto;gap:10px;padding:9px 12px;border-bottom:1px solid var(--pk-line-soft,#2b2b26);font-size:12px;align-items:start}.scope-item:last-child{border-bottom:0}.scope-item .path{overflow-wrap:anywhere;color:var(--pk-text,#e7e7df)}.scope-item .reason{color:var(--pk-text-muted,#a5a59b);margin-top:3px}.scope-item.excluded .path{color:var(--pk-text-faint,#85857b)}
75
+ .toolbar{display:flex;align-items:center;justify-content:space-between;gap:10px;flex-wrap:wrap}.segmented{display:inline-flex;padding:3px;border:1px solid var(--pk-control-border,var(--pk-line,#35352f));border-radius:var(--pk-control-radius,var(--pk-radius,6px));background:var(--pk-control-bg,var(--pk-surface,#1c1c1a));background-image:var(--pk-control-texture,none);box-shadow:var(--pk-control-shadow,none)}.segmented button{padding:3px 9px;color:var(--pk-text-muted,#a5a59b);font-size:12px;border-radius:var(--pk-radius-xs,3px)}.segmented button.active{background:var(--pk-control-on,#34332d);color:var(--pk-control-on-text,var(--pk-text,#e7e7df));box-shadow:var(--pk-control-on-shadow,none)}
76
+ .note-card{border:1px solid var(--pk-card-border,var(--pk-line-soft,#2b2b26));border-radius:var(--pk-card-radius,var(--pk-radius,6px));background:var(--pk-card-bg,var(--pk-surface,#1c1c1a));background-image:var(--pk-card-texture,none);box-shadow:var(--pk-card-shadow,none);color:var(--pk-card-text,var(--pk-text,#e7e7df));padding:10px 12px;display:flex;flex-direction:column;gap:6px;cursor:pointer;width:100%;text-align:left;font-size:13px}.note-card>.row{width:100%}.note-card:hover{border-color:var(--pk-card-hover-border,var(--pk-line,#35352f))}.note-card.sel{border-color:var(--pk-accent,#e3b76f)}.note-card .preview{color:var(--pk-text-muted,#a5a59b);font-size:12px}.note-body h4{margin:10px 0 4px;color:var(--pk-accent,#e3b76f)}.note-body p,.note-body li{font-size:12px;line-height:1.75}.note-body ul{margin:0;padding-left:18px}
77
+ .timeline{padding-left:20px;position:relative;display:flex;flex-direction:column;gap:18px}.timeline:before{content:'';position:absolute;left:5px;top:6px;bottom:6px;width:1px;background:var(--pk-line,#35352f)}.tl-event{position:relative}.tl-event:before{content:'';position:absolute;width:9px;height:9px;border:2px solid var(--pk-ground,#141413);background:var(--pk-text-faint,#85857b);border-radius:50%;left:-19px;top:5px;box-shadow:0 0 0 1px var(--pk-line,#35352f)}.tl-event.sel:before{background:var(--pk-accent,#e3b76f)}.tl-event time{font-size:11px;color:var(--pk-text-muted,#a5a59b)}.tl-event h3{margin:3px 0 4px}.diff{display:grid;grid-template-columns:1fr 1fr;gap:8px;margin-top:6px}.diff>div{padding:8px 10px;border:1px solid var(--pk-line,#35352f);border-radius:var(--pk-radius-sm,5px);font-size:12px}.diff .old{background:var(--pk-red-bg,#23201e)}.diff .old del{color:var(--pk-red-ln,#b8a59d)}.diff .new{background:var(--pk-green-bg,#202820);border-color:var(--pk-green-ln,#374237)}.diff small{display:block;margin-bottom:4px}
78
+ .strip{border-top:1px solid var(--pk-line-soft,#2b2b26);background:var(--pk-rail,#191918);display:grid;grid-template-columns:repeat(3,minmax(0,1fr));overflow:hidden}.strip>section{border-right:1px solid var(--pk-line-soft,#2b2b26);display:flex;flex-direction:column;min-width:0;min-height:0}.strip header{padding:8px 12px;font-size:11px;font-weight:600;color:var(--pk-head-text,var(--pk-text-muted,#a5a59b));display:flex;justify-content:space-between;align-items:center;border-bottom:1px solid var(--pk-head-border,var(--pk-line-soft,#2b2b26));background:var(--pk-head-bg,transparent);background-image:var(--pk-head-texture,none);box-shadow:var(--pk-head-shadow,none)}.strip .items{overflow:auto;padding:6px 8px;display:flex;flex-direction:column;gap:4px;scrollbar-width:thin}.strip .item{padding:6px 8px;border-radius:var(--pk-radius-sm,4px);font-size:12px;cursor:pointer;display:flex;flex-direction:column;gap:2px}.strip .item:hover{background:var(--pk-raised,#242420)}.strip .item.sel{background:var(--pk-accent-soft,#302b22)}.strip .item .meta{font-size:10.5px;color:var(--pk-text-faint,#85857b)}.strip .none{padding:8px;font-size:11px;color:var(--pk-text-faint,#85857b)}
79
+ .graph-wrap{position:relative;flex:1;min-height:0;display:flex;flex-direction:column;overflow:hidden}.graph-tools{display:flex;gap:6px;align-items:center;flex-wrap:wrap;font-size:12px}.top-view-tools .graph-tools{padding:0;border:0;background:none;min-height:0}.top-view-tools .segmented{padding:2px}.top-view-tools .segmented button{padding:2px 7px}.top-view-tools .btn.small,.top-view-tools .read-mark{min-height:26px;padding:2px 7px}#cy{flex:1;min-height:140px;background:var(--pk-graph-bg,var(--pk-ground,#141413))}.legend{display:flex;gap:10px;flex-wrap:wrap;font-size:10.5px;color:var(--pk-text-muted,#a5a59b);padding:6px 12px;border-top:1px solid var(--pk-line-soft,#2b2b26);background:var(--pk-rail,#191918)}.legend span{display:inline-flex;align-items:center;gap:4px}.legend .legend-col{color:var(--pk-text,#e7e7df);font-weight:600}.legend i{width:10px;height:10px;display:inline-block;border-radius:var(--pk-radius-xs,2px)}
80
+ .graph-empty{position:absolute;inset:0;display:flex;align-items:center;justify-content:center;pointer-events:none}.graph-empty>div{pointer-events:auto}
81
+ .keeper-panel{position:fixed;display:none;flex-direction:column;overflow:hidden;outline:0}.keeper-panel.open{display:flex}.keeper-inner{display:flex;flex-direction:column;flex:1;min-height:0;min-width:0}
82
+ .keeper-panel.floating{left:0;top:0;z-index:45;width:min(560px,calc(100vw - 32px));height:min(72vh,680px);min-width:320px;min-height:260px;max-width:calc(100vw - 16px);max-height:calc(100dvh - 16px);background:var(--pk-card-bg,#191917);background-image:var(--pk-card-texture,none);border:1px solid var(--pk-card-border,#4c4c40);border-radius:var(--pk-radius-panel,10px);resize:both;box-shadow:var(--pk-shadow-window,0 28px 80px #000b,0 1px 0 #ffffff0d inset)}.keeper-head{padding:12px 14px;border-bottom:1px solid var(--pk-card-line,var(--pk-line-soft,#2b2b26));display:flex;flex-direction:column;gap:6px}.keeper-head .title{display:flex;justify-content:space-between;align-items:center;font-weight:600}.keeper-head .ctx{font-size:11px;color:var(--pk-text-muted,#a5a59b);display:flex;gap:6px;align-items:center;min-width:0}.keeper-head .ctx strong{font-weight:400;color:var(--pk-text,#e7e7df);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
83
+ .keeper-stream{flex:1;overflow:auto;padding:14px;display:flex;flex-direction:column;gap:12px;scrollbar-width:thin}.msg{font-size:12px;line-height:1.75}.msg.user{align-self:flex-end;background:var(--pk-msg-user-bg,var(--pk-raised,#242420));border:1px solid var(--pk-msg-user-border,var(--pk-line,#35352f));border-radius:var(--pk-radius,6px);padding:7px 10px;white-space:pre-wrap;max-width:92%}.msg.keeper{white-space:pre-wrap}.msg.system{color:var(--pk-text-faint,#85857b);font-size:11px}.msg .cites{display:flex;gap:5px;flex-wrap:wrap;margin-top:6px}.cite{font-size:10px;border:1px solid var(--pk-line,#35352f);padding:2px 6px;border-radius:var(--pk-radius-sm,4px);color:var(--pk-text-muted,#a5a59b);cursor:pointer}.cite:hover{color:var(--pk-blue,#9fbdd5)}
84
+ .composer{border-top:1px solid var(--pk-card-line,var(--pk-line-soft,#2b2b26));padding:10px}.composer textarea{width:100%;min-height:64px;background:var(--pk-well-bg,#22221e);border:1px solid var(--pk-well-border,#424237);border-radius:var(--pk-radius,6px);box-shadow:var(--pk-well-shadow,none);color:var(--pk-text,#e7e7df);padding:8px;resize:none}.composer .foot{display:flex;justify-content:space-between;align-items:center;margin-top:6px;font-size:10.5px;color:var(--pk-text-faint,#85857b);gap:8px}
85
+ dialog{color:var(--pk-card-text,var(--pk-text,#e7e7df));background:var(--pk-card-bg,#1e1e1b);background-image:var(--pk-card-texture,none);border:1px solid var(--pk-card-border,#49493d);border-radius:var(--pk-radius-panel,9px);padding:0;width:min(760px,calc(100vw - 32px));max-height:88dvh;box-shadow:var(--pk-shadow-window,0 24px 90px #0008)}dialog::backdrop{background:var(--pk-scrim,#0009);backdrop-filter:blur(3px)}.dialog-head{padding:14px 18px;border-bottom:1px solid var(--pk-card-line,var(--pk-line,#35352f));display:flex;justify-content:space-between;align-items:center;gap:16px}.dialog-head h2{font-size:15px}.dialog-body{padding:16px 18px;overflow:auto;max-height:calc(88dvh - 64px);display:flex;flex-direction:column;gap:12px}.dialog-foot{display:flex;gap:8px;justify-content:flex-end}
86
+ .source-quote{background:var(--pk-well-bg,#171715);padding:10px 0;border:1px solid var(--pk-well-border,var(--pk-line-soft,#2b2b26));border-radius:var(--pk-radius-sm,5px);box-shadow:var(--pk-well-shadow,none);font:12px/1.8 var(--pk-font-ui,'Segoe UI Variable Text','Segoe UI','Microsoft YaHei',sans-serif);max-height:50vh;overflow:auto}.src-line{display:grid;grid-template-columns:48px minmax(0,1fr);padding:0 12px 0 0}.src-line .no{color:var(--pk-text-faint,#75756c);font:10px/2.15 var(--pk-font-mono,'Cascadia Code',Consolas,monospace);text-align:right;padding-right:12px;user-select:none}.src-line .tx{white-space:pre-wrap;overflow-wrap:anywhere}
87
+ .search-results{display:flex;flex-direction:column;gap:2px}.search-result{padding:8px 10px;text-align:left;border-bottom:1px solid var(--pk-line-soft,#2b2b26);display:flex;gap:10px;align-items:center;width:100%}.search-result:hover{background:var(--pk-raised,#242420)}.search-result small{margin-left:auto;white-space:nowrap}
88
+ #toast{position:fixed;left:50%;bottom:22px;transform:translateX(-50%);max-width:calc(100vw - 40px);padding:9px 14px;border:1px solid var(--pk-line-strong,#505044);background:var(--pk-raised,#303027);color:var(--pk-text,#e7e7df);border-radius:var(--pk-radius,7px);font-size:12px;box-shadow:var(--pk-shadow-raised,0 5px 20px #0004);opacity:0;pointer-events:none;transition:opacity .15s;z-index:20}#toast.show{opacity:1}
89
+ .mobile-nav{display:none}
90
+ @media(max-width:1100px){:root{--pk-rail-w:200px}}
91
+ @media(max-width:700px){.shell{grid-template-columns:minmax(0,1fr)}.rail{display:none}.mobile-nav{display:flex;gap:2px;overflow:auto;border-bottom:1px solid var(--pk-line,#35352f);padding:4px 8px;flex-shrink:0}.mobile-nav button{font-size:11px;white-space:nowrap;padding:6px 8px;border-radius:var(--pk-radius-sm,4px);color:var(--pk-text-muted,#a5a59b)}.mobile-nav button.active{background:var(--pk-accent-bg,#322c20);color:var(--pk-accent,#e3b76f)}.topbar{padding:6px 10px;gap:6px}.topbar-main{flex-basis:100%;flex-wrap:wrap;gap:5px}.crumb{max-width:100%;flex:1 1 100%}.top-view-tools{width:100%}.top-actions{margin-left:auto}.coverage-pill{display:none}.search-trigger span{display:none}.theme-pick>span{display:none}.theme-pick select{max-width:92px}main{padding:14px 12px}.scope-item{grid-template-columns:1fr;gap:3px}.strip,.strip.two,.strip.three,.strip.four{grid-template-columns:1fr;max-height:40vh;overflow:auto;padding-right:0}.strip>section{border-right:0;border-bottom:1px solid var(--pk-line-soft,#2b2b26)}.keeper-panel.floating{width:calc(100vw - 16px);resize:none}.diff{grid-template-columns:1fr}}
92
+ @media(prefers-reduced-motion:reduce){*{transition:none!important}}
93
+
94
+ /* ── graph workbench (B4) ── */
95
+ .strip.two{grid-template-columns:repeat(2,minmax(0,1fr))}
96
+ .strip.three{grid-template-columns:repeat(3,minmax(0,1fr))}
97
+ .strip.four{grid-template-columns:repeat(4,minmax(0,1fr))}
98
+ .legend .sep{color:var(--pk-line,#35352f)}.legend i.line{width:16px;height:2px;border-radius:0}.legend i.line.dashed{background:repeating-linear-gradient(90deg,currentColor 0 4px,transparent 4px 7px)}.legend i.dashedbox{border:1px dashed var(--pk-text-muted,#a5a59b);background:transparent}.legend i.redbox{border:1.5px solid var(--pk-red,#dca6a0);background:transparent}
99
+ .list-wrap{display:flex;flex-direction:column;flex:1;min-height:0;overflow:auto}.list-wrap .goals{display:flex;gap:6px;flex-wrap:wrap;padding:8px 12px}.list-wrap table.list{margin:0 12px 12px;width:calc(100% - 24px)}table.list.three td{width:33%}.text-btn.sel{color:var(--pk-accent,#e3b76f)}.ref-fold{margin-top:4px;font-size:11.5px}.ref-fold summary{cursor:pointer;color:var(--pk-text-faint,#85857b)}.ref-fold summary:hover{color:var(--pk-text-muted,#a5a59b)}.ref-fold>div{padding:2px 0 2px 12px}
100
+ .quote{margin:8px 0;padding:6px 10px;border-left:2px solid var(--pk-accent,#e3b76f);color:var(--pk-text-muted,#a5a59b);font-style:italic}
101
+ ul.stmts{margin:0;padding-left:16px}ul.stmts li{margin:3px 0;line-height:1.7}
102
+ .red{color:var(--pk-red,#dca6a0)}
103
+ @media(max-width:700px){.strip.four{grid-template-columns:1fr}.graph-tools{gap:5px;padding:6px 8px}.list-wrap table.list{margin:0 4px}}
104
+
105
+ /* Vocabulary window */
106
+ dialog.wide{width:min(1180px,calc(100vw - 32px))}dialog.wide .dialog-body{max-height:calc(88dvh - 64px)}.dialog-body h3{font-size:13px;margin-top:6px}table.glossary{width:100%;border-collapse:collapse;font-size:12px;line-height:1.45}table.glossary th{text-align:left;font-size:11px;color:var(--pk-text-muted,#a5a59b);padding:4px 8px;border-bottom:1px solid var(--pk-line,#35352f)}table.glossary td{vertical-align:top;padding:7px 8px;border-top:1px solid var(--pk-line-soft,#2b2b26)}table.glossary .zh{display:block;color:var(--pk-text-muted,#a5a59b);margin-top:3px}table.glossary .also{color:var(--pk-text-muted,#a5a59b)}table.glossary .chip{display:inline-flex;align-items:center;gap:6px;font-weight:600;white-space:nowrap}table.glossary .chip i{width:10px;height:10px;border-radius:var(--pk-radius-xs,2px);display:inline-block}table.glossary code{font:11px var(--pk-font-mono,'Cascadia Code',Consolas,monospace);color:var(--pk-text,#e7e7df)}.dialog-body p .zh{display:block;color:var(--pk-text-faint,#85857b);margin-top:2px}
107
+ .dialog-body .lang-toggle{align-self:flex-end}
108
+
109
+ /* Graph legend shapes and stars (owner 2026-09-17) */
110
+ /* The corner marks, by name, wherever the page writes one (the legend, the List): the same colours the picture uses. */
111
+ .mark-star{color:var(--pk-graph-mark-star,#f3ead2)}.mark-flag{color:var(--pk-graph-mark-flag,#e3b76f)}.mark-note{color:var(--pk-graph-mark-note,#e7e7df)}.mark-ask{color:var(--pk-graph-mark-ask,#e3b76f)}.mark-pending{color:var(--pk-graph-mark-pending,#9fbdd5)}
112
+ .shape-icon { display: inline-flex; vertical-align: -2px; color: var(--pk-text-muted,#a5a59b); }
113
+ .legend .star-legend, .star { color: var(--pk-star, #ffd54a); }
114
+ .star-note { font-size: 11px; }
115
+
116
+ /* Compare (D45): the two ends, the five counts and one difference at a time, above the graph. */
117
+ .compare-bar{display:flex;flex-direction:column;gap:6px;padding:8px 12px;border-bottom:1px solid var(--pk-line-soft,#2b2b26);background:var(--pk-rail,#191918);font-size:12px}
118
+ .compare-bar .input.small{width:auto;padding:3px 6px;font-size:11px}
119
+
120
+ /* ── walkthrough of a change follow-up (§4.2) ── */
121
+ dialog.wide{width:min(1000px,calc(100vw - 32px))}
122
+ .walk-root{display:flex;flex-direction:column;gap:10px}.walk-title{font-size:15px;margin-bottom:4px}
123
+ .walk-bar{border-top:1px solid var(--pk-line-soft,#2b2b26);padding-top:8px}
124
+ .walk{display:grid;grid-template-columns:minmax(0,5fr) minmax(0,7fr);gap:12px;align-items:start}
125
+ .walk-spine{max-height:44dvh;overflow:auto;border:1px solid var(--pk-line,#35352f);border-radius:var(--pk-radius,6px);scrollbar-width:thin}
126
+ .walk-item{display:block;width:100%;text-align:left;padding:7px 10px;border:0;border-bottom:1px solid var(--pk-line-soft,#2b2b26);color:var(--pk-text,#e7e7df);background:none;cursor:pointer;font:inherit}
127
+ .walk-item:hover{background:var(--pk-surface,#1c1c1a)}.walk-item.on{background:var(--pk-accent-bg,#322c20);box-shadow:inset 3px 0 0 var(--pk-accent,#e3b76f)}
128
+ .walk-name{font-size:12.5px;margin-top:3px}.walk-why{font-size:11.5px;color:var(--pk-text-muted,#a5a59b);display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
129
+ .walk-step{border:1px solid var(--pk-line,#35352f);border-radius:var(--pk-radius,6px);padding:12px 14px;display:flex;flex-direction:column;gap:8px}.walk-step h3{font-size:14px}
130
+ .tag.origin{border-style:dashed}
131
+ @media(max-width:700px){.walk{grid-template-columns:1fr}.walk-spine{max-height:30dvh}}
132
+
133
+ /* ── readability self-check (D46): the mark is in the control row, this is the panel behind it (S3, below) ── */
134
+ .read-flag{color:var(--pk-text-faint,#85857b)}.read-flag.alert{color:var(--pk-accent,#e3b76f)}.read-body{display:flex;flex-direction:column;gap:10px}
135
+ .read-issue{display:flex;flex-direction:column;gap:4px}.read-issue .row{gap:6px}
136
+
137
+ /* ── the Keeper's window drops in from the top (owner, 2026-09-18) ── */
138
+ .keeper-panel.floating .keeper-head{cursor:grab;user-select:none;touch-action:none}
139
+ .keeper-panel.floating.dragging{box-shadow:var(--pk-shadow-window,0 36px 100px #000c);opacity:.97}.keeper-panel.floating.dragging .keeper-head{cursor:grabbing}
140
+ /* Falls with gravity (accelerating), overshoots below its place as if on an elastic string, and bobs to rest. */
141
+ .keeper-panel.floating.dropping{animation:pk-drop .82s both;transform-origin:50% 0}
142
+ @keyframes pk-drop{
143
+ 0%{transform:translateY(calc(-1 * var(--pk-fall,800px))) rotate(-1.4deg);animation-timing-function:cubic-bezier(.47,0,.86,.36)}
144
+ 56%{transform:translateY(22px) rotate(.4deg) scaleY(1.018);animation-timing-function:cubic-bezier(.2,.65,.4,1)}
145
+ 72%{transform:translateY(-9px) rotate(-.2deg) scaleY(.994);animation-timing-function:cubic-bezier(.5,0,.8,.45)}
146
+ 84%{transform:translateY(4px) scaleY(1.004);animation-timing-function:cubic-bezier(.2,.65,.4,1)}
147
+ 93%{transform:translateY(-1.5px)}
148
+ 100%{transform:none}
149
+ }
150
+ @media(prefers-reduced-motion:reduce){.keeper-panel.floating.dropping{animation:pk-fade .15s both}}
151
+ @keyframes pk-fade{from{opacity:0}to{opacity:1}}
152
+
153
+ /* ── Graph as tidied (D53–D55, CKC-09 v2.5) ── */
154
+ /* The legend is one line of what the picture shows, with counts; More opens what each item means (WorkflowKeeper). */
155
+ .legend{flex-wrap:nowrap;align-items:center;gap:10px;white-space:nowrap}
156
+ .legend .lg-items{display:flex;align-items:center;gap:4px 14px;flex:1;min-width:0;overflow:hidden}
157
+ .legend .sep{width:1px;height:12px;background:var(--pk-line,#35352f);display:inline-block;flex:none}
158
+ .legend .lg-item{flex:none}
159
+ .legend .lg-item{display:inline-flex;align-items:center;gap:5px}
160
+ .legend .lg-icon{display:inline-flex}
161
+ .legend .lg-mark{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;border-radius:50%;border:1.2px solid;font-size:10px;font-weight:700;background:var(--pk-surface,#1c1c1a)}
162
+ .legend .lg-count{font-size:10px;font-weight:600;color:var(--pk-text-muted,#a5a59b);border:1px solid var(--pk-line-strong,#4a4a43);border-radius:var(--pk-radius-pill,8px);padding:0 5px;background:var(--pk-surface,#1c1c1a)}
163
+ .legend .lg-desc{display:none;color:var(--pk-text-faint,#85857b)}
164
+ .legend .lg-more{flex:none;font-size:11px}
165
+ .legend.expanded{align-items:flex-start;white-space:normal}
166
+ .legend.expanded .lg-items{flex-wrap:wrap;gap:6px 18px}
167
+ .legend.expanded .lg-item{max-width:520px;align-items:baseline}
168
+ .legend.expanded .lg-desc{display:inline}
169
+ /* The full name and the meaning of the marks, while an object is pointed at. */
170
+ .graph-tip{position:fixed;z-index:40;pointer-events:none;max-width:340px;background:var(--pk-card-bg,#10100f);border:1px solid var(--pk-card-border,var(--pk-line,#35352f));border-radius:var(--pk-card-radius,var(--pk-radius,6px));padding:6px 9px;font-size:12px;line-height:1.5;color:var(--pk-card-text,var(--pk-text,#e7e7df));box-shadow:var(--pk-shadow-raised,0 8px 24px #0006)}
171
+ .graph-tip[hidden]{display:none}
172
+ /* List: the Observed reality cell begins with the same count as the graph's folder (D55). */
173
+ table.list td.obs .obs-sum{font-size:11.5px;color:var(--pk-text-muted,#a5a59b);margin-bottom:4px}
174
+ table.list td.obs .obs-sum b{color:var(--pk-text,#e7e7df)}
175
+ .list-wrap .tag.sel{outline:1px solid var(--pk-accent,#e3b76f)}.list-wrap button.tag{cursor:pointer}
176
+ .list-wrap tr.sel>td:first-child{box-shadow:inset 2px 0 0 var(--pk-accent,#e3b76f)}
177
+ /* Arriving in List from the graph, a folder, search or a link: the target is picked out for a moment. */
178
+ @keyframes pk-flash{0%{background:var(--pk-flash,#4a3f25)}100%{background:transparent}}
179
+ .flash{animation:pk-flash 1.6s ease-out}
180
+ /* Spec v2.8 content (CKC-09 AC-32, AC-33; CKC-21 AC-11): the owner's words above the List's areas, a project rule's row
181
+ in Project scope, and what a change removed. Only the existing colours and sizes are used. */
182
+ .list-wrap .words-fold{flex:1 1 100%;margin-top:0}.list-wrap .words-fold summary{font-weight:600;color:var(--pk-text-muted,#a5a59b)}
183
+ .rule-group{display:flex;flex-direction:column;gap:6px}.rule-group>h4{margin:6px 0 0}
184
+ .rule-row{border:1px solid var(--pk-well-border,var(--pk-line-soft,#2b2b26));border-radius:var(--pk-radius-sm,5px);padding:8px 10px;display:flex;flex-direction:column;gap:4px;background:var(--pk-well-bg,var(--pk-rail,#191918))}.rule-row.replaced{background:transparent}.rule-row .quote{margin:2px 0}
185
+ .strip .removed-line s,.tl-event s{color:var(--pk-text-faint,#85857b)}
186
+
187
+ /* ══ S1 · details popover, full details, the Keeper docked on the right, Markdown in answers (D67; Spec §6.1, §6.4,
188
+ §6.8, §6.15). Colours come only through the variables above, so the theme batch changes values, not rules. ══ */
189
+
190
+ /* ── The details popover: beside the object, inside the main view, over everything but a dialog ── */
191
+ .popover{position:fixed;z-index:40;left:0;top:0;width:min(384px,calc(100vw - 16px));max-height:min(78dvh,640px);display:flex;flex-direction:column;background:var(--pk-card-bg,var(--pk-surface,#1c1c1a));background-image:var(--pk-card-texture,none);color:var(--pk-card-text,var(--pk-text,#e7e7df));border:1px solid var(--pk-card-border,var(--pk-line,#35352f));border-top:2px solid var(--pk-accent,#e3b76f);border-radius:var(--pk-card-radius,var(--pk-radius-panel,8px));box-shadow:var(--pk-card-shadow,var(--pk-shadow-float,0 14px 40px #0009));font-size:12px;outline:0}
192
+ .popover[hidden]{display:none}.popover:focus,.popover:focus-visible,.keeper-panel:focus-visible{outline:0}
193
+ .popover-content{display:flex;flex-direction:column;min-height:0;flex:1}
194
+ .popover-head{display:flex;gap:8px;align-items:flex-start;padding:10px 10px 8px 14px;border-bottom:1px solid var(--pk-card-line,var(--pk-line-soft,#2b2b26));flex:none}
195
+ .popover-title{font-size:14px;font-weight:600;line-height:1.35;overflow-wrap:anywhere}
196
+ .popover-sub{font:10.5px/1.5 var(--pk-font-mono,'Cascadia Code',Consolas,monospace);letter-spacing:.03em;color:var(--pk-text-faint,#85857b);margin-top:1px}
197
+ .popover-back{font-size:11px;margin-bottom:2px;max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;display:block}
198
+ .popover-close{flex:none;width:24px;height:24px;border-radius:var(--pk-radius-pill,12px);font-size:16px;line-height:1;color:var(--pk-text-muted,#a5a59b);border:1px solid var(--pk-control-border,var(--pk-line,#35352f))}.popover-close:hover{color:var(--pk-text,#e7e7df);background:var(--pk-raised,#242420)}
199
+ .popover-body{padding:10px 14px 12px;display:flex;flex-direction:column;gap:10px;overflow:auto;min-height:0;scrollbar-width:thin}
200
+ .popover-sentence{line-height:1.65;display:-webkit-box;-webkit-line-clamp:6;-webkit-box-orient:vertical;overflow:hidden}
201
+ /* The body scrolls as a whole; no part of it shrinks. The summary is clipped (overflow hidden) and so could shrink to
202
+ nothing in the column when the history under it was long — D1's popover showed no summary at all (owner 2026-09-30). */
203
+ .popover-body>*{flex-shrink:0}
204
+ /* What it is, first: its validity always said, as a tag of its own; where it sits, one line under the summary. */
205
+ .status-tag{font-weight:600}.status-tag .text-btn{font-weight:600;color:inherit;text-decoration:underline dotted}
206
+ .place-line{font-size:12px;line-height:1.55;color:var(--pk-text-muted,#a5a59b)}
207
+ .place-line .place-lead{font-size:10.5px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--pk-text-faint,#85857b);margin-right:2px}
208
+ .place-line.place-product .place-lead,.place-line.place-none .place-lead{color:var(--pk-accent,#e3b76f)}
209
+ .full-sentence{line-height:1.65;margin:0}
210
+ .popover-notes{display:flex;flex-direction:column;gap:6px}.popover-notes h4,.popover-body h4{font-size:10.5px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--pk-text-faint,#85857b)}
211
+ .popover .note-card{padding:8px 10px;gap:4px;background:var(--pk-raised,#242420);font-size:12px}.popover .note-card .preview{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;overflow:hidden}
212
+ .popover-actions{gap:6px}
213
+ .popover-foot{flex:none;display:flex;gap:10px;align-items:flex-start;padding:9px 14px 10px;border-top:1px solid var(--pk-card-line,var(--pk-line-soft,#2b2b26));background:var(--pk-card-foot-bg,var(--pk-rail,#191918));border-radius:0 0 var(--pk-card-radius,var(--pk-radius-panel,8px)) var(--pk-card-radius,var(--pk-radius-panel,8px))}
214
+ .popover-scrim{position:fixed;inset:0;z-index:39;background:var(--pk-scrim,#0009)}.popover-scrim[hidden]{display:none}
215
+ /* Narrow window: the popover is an overlay along the bottom (Spec §6.15). */
216
+ .popover.sheet{left:8px!important;right:8px;top:auto!important;bottom:8px;width:auto!important;max-height:72dvh!important}
217
+
218
+ /* ── Full details: `Details` in the dialog ── */
219
+ dialog.details-dialog{width:min(820px,calc(100vw - 32px))}
220
+ .dialog-head h2{flex:1;min-width:0;overflow-wrap:anywhere}
221
+ .full-details{display:flex;flex-direction:column;gap:14px;font-size:12.5px}
222
+ .full-head{display:flex;flex-direction:column;gap:6px}
223
+ .full-section{display:flex;flex-direction:column;gap:6px;scroll-margin-top:8px;border-radius:var(--pk-radius-sm,5px)}
224
+ .full-section>h4,.full-section h4{margin:0}.full-section p{line-height:1.7}
225
+ .full-details ul{margin:0;padding-left:18px}.full-details li{line-height:1.7}
226
+ /* What an object still lacks, and how it follows the changes that reached it (Spec §5.5, §6.4): in the popover a block
227
+ like the others, in the full details its `Propagation` part. Colours are the surface's own (card or dialog). */
228
+ .lack{display:flex;flex-direction:column;gap:1px;font-size:12px;line-height:1.6}
229
+ .lack-what{overflow-wrap:anywhere}
230
+ .lack-from{padding-left:10px;overflow-wrap:anywhere}
231
+ .prop-part{display:flex;flex-direction:column;gap:5px}
232
+ .prop-record{font-size:12px;line-height:1.6;overflow-wrap:anywhere}
233
+ .judged-in{font-size:11.5px;line-height:1.55}
234
+
235
+ /* ── The Keeper conversation docked on the right (WorkflowKeeper's D81): the main view and the pill give way by the
236
+ panel's real width, so nothing is covered; narrow windows have no room to give, and it lies over the main view ── */
237
+ .keeper-panel.docked{top:calc(var(--pk-top) + var(--pk-gap));right:var(--pk-gap);bottom:var(--pk-gap);z-index:32;width:var(--pk-keeper-w);min-width:320px;max-width:calc(100vw - var(--pk-rail-w,230px) - 380px);background:var(--pk-card-bg,var(--pk-rail,#191918));background-image:var(--pk-card-texture,none);border:1px solid var(--pk-card-border,var(--pk-line,#35352f));border-radius:var(--pk-radius-panel,10px);box-shadow:var(--pk-section-shadow,0 6px 22px #0005)}
238
+ .keeper-resizer{position:absolute;left:0;top:0;bottom:0;width:7px;cursor:col-resize;z-index:2;touch-action:none}.keeper-resizer::after{content:'';position:absolute;left:0;top:0;bottom:0;width:1px;background:transparent}
239
+ .keeper-resizer:hover::after,.keeper-resizer:focus-visible::after,.keeper-panel.sizing .keeper-resizer::after{background:var(--pk-accent,#e3b76f);width:2px}
240
+ .keeper-panel.floating .keeper-resizer{display:none}
241
+ .keeper-panel.sizing{user-select:none}
242
+ .keeper-who{display:inline-flex;align-items:baseline;gap:6px;min-width:0;flex-wrap:wrap}.keeper-who .pi-mark{font-family:var(--pk-font-display,Georgia,serif);font-size:17px;line-height:1;color:var(--pk-accent,#e3b76f)}
243
+ .keeper-actions{gap:6px;flex:none}
244
+ .keeper-head .title{gap:8px;align-items:flex-start;flex-wrap:wrap}.keeper-head-right{display:flex;align-items:flex-start;justify-content:flex-end;gap:10px;min-width:0;margin-left:auto}.keeper-telemetry{display:flex;align-items:flex-start;gap:10px;min-width:0;font-weight:400}.keeper-telemetry-item{display:flex;flex-direction:column;align-items:flex-end;min-width:0;line-height:1.25}.keeper-telemetry-item small{font-size:9.5px;color:var(--pk-text-faint,#85857b)}.keeper-context-usage,.keeper-model-name{max-width:140px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px;color:var(--pk-text-muted,#a5a59b);font-variant-numeric:tabular-nums}
245
+ .keeper-stream{outline-offset:-2px}
246
+ @media(min-width:901px){
247
+ body.keeper-docked .main-panel{margin-right:calc(var(--pk-keeper-w) + var(--pk-gap))}
248
+ body.keeper-docked .drawer{margin-right:calc(var(--pk-keeper-w) + var(--pk-pill-w) + 2 * var(--pk-gap))}
249
+ body.keeper-docked .keeper-dock{right:calc(var(--pk-keeper-w) + 2 * var(--pk-gap))}
250
+ }
251
+ @media(max-width:900px){
252
+ .keeper-panel.docked{width:min(440px,calc(100vw - 2 * var(--pk-gap)));max-width:calc(100vw - 2 * var(--pk-gap));min-width:0;box-shadow:var(--pk-shadow-float,0 14px 40px #0009)}
253
+ .keeper-panel.docked .keeper-resizer{display:none}
254
+ }
255
+ @media(max-width:700px){.keeper-panel.docked{left:var(--pk-gap);width:auto}}
256
+
257
+ /* ── Answers drawn as Markdown (CKC-10 AC-21). The answer and the owner's own words take the strongest text colour
258
+ there is, the same as everything the owner is meant to read (WorkflowKeeper's D87) ── */
259
+ .msg.keeper,.msg.keeper .text,.msg.user{color:var(--pk-text-strong,var(--pk-text,#e7e7df))}
260
+ .msg.keeper{white-space:normal}
261
+ .md{white-space:normal;overflow-wrap:anywhere;line-height:1.7}
262
+ .md>:first-child{margin-top:0}.md>:last-child{margin-bottom:0}
263
+ .md p{margin:0 0 7px}
264
+ .md h1,.md h2,.md h3,.md h4,.md h5,.md h6{margin:10px 0 4px;font-size:12.5px;font-weight:700;line-height:1.4;color:inherit;letter-spacing:0;text-transform:none}.md h1{font-size:15px}.md h2{font-size:14px}.md h3{font-size:13px}
265
+ .md ul,.md ol{margin:0 0 7px;padding-left:20px}.md li{margin:1px 0}.md li>p{margin:0}.md li>ul,.md li>ol{margin:1px 0 2px}
266
+ .md code{font:11.5px/1.5 var(--pk-font-mono,'Cascadia Code',Consolas,monospace);padding:0 4px;border-radius:var(--pk-radius-xs,3px);color:inherit;background:var(--pk-well-bg,var(--pk-ground,#141413));border:1px solid var(--pk-well-border,var(--pk-line-soft,#2b2b26))}
267
+ .md pre{margin:4px 0 8px;padding:7px 9px;border-radius:var(--pk-radius,6px);overflow-x:auto;background:var(--pk-well-bg,var(--pk-ground,#141413));border:1px solid var(--pk-well-border,var(--pk-line-soft,#2b2b26))}.md pre code{padding:0;border:0;background:none;white-space:pre;overflow-wrap:normal}
268
+ .md blockquote{margin:0 0 7px;padding-left:9px;border-left:2px solid var(--pk-line,#35352f);color:var(--pk-text-muted,#a5a59b)}
269
+ .md hr{border:0;border-top:1px solid var(--pk-line,#35352f);margin:9px 0}
270
+ .md .md-table{overflow-x:auto;margin:0 0 8px}.md table{border-collapse:collapse;font-size:11.5px}
271
+ .md th,.md td{border:1px solid var(--pk-line,#35352f);padding:3px 7px;text-align:left;vertical-align:top;overflow-wrap:break-word}.md th{font-weight:700;background:var(--pk-raised,#242420)}.md td code,.md th code{overflow-wrap:anywhere}
272
+ .md strong{font-weight:700;color:inherit}
273
+ .md .md-link{color:var(--pk-blue,#9fbdd5);text-decoration:underline;text-underline-offset:2px}.md .md-nolink{text-decoration:underline dotted;text-underline-offset:2px;cursor:help}
274
+ .md .cite,button.cite{font:10px/1.6 var(--pk-font-ui,'Segoe UI Variable Text','Segoe UI','Microsoft YaHei',sans-serif);background:none;vertical-align:1px;margin:0 1px}
275
+ .msg.keeper.streaming .md>:is(p,h1,h2,h3,h4,h5,h6,li):last-child::after{content:'\258D';opacity:.55;margin-left:2px}
276
+ /* A selection must not change the top bar's height and move the graph or List. The title holds the full name. */
277
+ #focus-crumb{min-width:0;display:block;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
278
+
279
+ /* ══ S3 · one row of controls; the panels that float from it; the graph's own buttons on its corner (D68; Spec §6.1,
280
+ §6.3 "控件放在哪"; CKC-09 AC-37). Colours come only through the variables at the top. ══ */
281
+ .graph-tools{flex:none;min-height:49px}
282
+ .graph-tools .btn.small{min-height:30px}
283
+ .top-view-tools .graph-tools{min-height:0}.top-view-tools .graph-tools .btn.small{min-height:26px}
284
+ /* A narrow window: the row takes the width it has and wraps onto a second line instead of running past the window.
285
+ Wider than the window, it made a phone page zoom out, so the conversation overlay no longer covered it (QC AY B12). */
286
+ @media(max-width:700px){.top-view-tools .graph-tools{flex:1 1 100%;min-width:0;max-width:100%}}
287
+ .filter-btn{font-variant-numeric:tabular-nums}
288
+ /* The readability mark: always there, quiet at 0, the accent once something is hard to read (D46 is not said less). */
289
+ .read-mark{display:inline-flex;align-items:center;min-height:30px;padding:3px 9px;border:1px solid transparent;border-radius:var(--pk-control-radius,var(--pk-radius,6px));font-size:12px;font-variant-numeric:tabular-nums;color:var(--pk-text-faint,#85857b);white-space:nowrap}
290
+ .read-mark:hover{color:var(--pk-text,#e7e7df);border-color:var(--pk-line,#35352f)}
291
+ .read-mark.alert{color:var(--pk-accent,#e3b76f);border-color:var(--pk-accent,#e3b76f);background:var(--pk-accent-bg,#322c20);font-weight:600}
292
+ .read-mark[aria-expanded="true"],.filter-btn[aria-expanded="true"]{border-color:var(--pk-text-muted,#a5a59b)}
293
+ /* The graph and, over its top right corner, what is done to the picture itself. It takes no row; a view the graph
294
+ chooses keeps the band under it clear of objects (graph-fit.js `padTop`). In a narrow main view it wraps. */
295
+ .graph-stage{position:relative;flex:1;min-height:140px;display:flex;flex-direction:column}
296
+ .graph-stage #cy{min-height:0}
297
+ .graph-float{position:absolute;top:8px;right:10px;z-index:5;display:flex;flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:4px;max-width:calc(100% - 20px);padding:4px;border:1px solid var(--pk-control-border,var(--pk-line,#35352f));border-radius:var(--pk-radius-pill,16px);background:var(--pk-control-bg,var(--pk-rail,#191918));background-image:var(--pk-control-texture,none);box-shadow:var(--pk-control-shadow,none)}
298
+ .graph-float .btn.small{min-height:24px;padding:2px 9px;border-radius:var(--pk-radius-pill,12px);font-size:11.5px;font-weight:400;color:var(--pk-text-muted,#a5a59b)}
299
+ .graph-float .btn.small:hover:not(:disabled),.graph-float .btn.small.active{color:var(--pk-text,#e7e7df)}.graph-float .btn.small.active{color:var(--pk-accent,#e3b76f)}
300
+ .graph-float .segmented{padding:2px;border-radius:var(--pk-radius-pill,12px)}.graph-float .segmented button{padding:1px 8px;font-size:11.5px;border-radius:var(--pk-radius-pill,10px)}
301
+ /* A panel of the control row: it floats, so nothing under it moves; flyout.js keeps it inside the main view. */
302
+ .flyout{position:fixed;z-index:36;left:0;top:0;width:min(340px,calc(100vw - 16px));max-height:min(74dvh,600px);overflow:auto;display:flex;flex-direction:column;gap:10px;padding:12px 14px;background:var(--pk-card-bg,var(--pk-surface,#1c1c1a));background-image:var(--pk-card-texture,none);color:var(--pk-card-text,var(--pk-text,#e7e7df));border:1px solid var(--pk-card-border,var(--pk-line,#35352f));border-radius:var(--pk-card-radius,var(--pk-radius-panel,8px));box-shadow:var(--pk-card-shadow,var(--pk-shadow-float,0 14px 40px #0009));font-size:12px;outline:0;scrollbar-width:thin}
303
+ .flyout[hidden]{display:none}
304
+ .flyout h4{font-size:10.5px;font-weight:600;text-transform:uppercase;letter-spacing:.06em;color:var(--pk-text-faint,#85857b)}
305
+ .flyout .input.small{padding:4px 6px;font-size:12px}
306
+ .flyout .chk{display:inline-flex;align-items:center;gap:6px;white-space:nowrap}.flyout .chk input{margin:0}
307
+ .filter-grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px 10px}
308
+ .filter-ticks{gap:16px}
309
+ .filter-show{display:flex;flex-direction:column;align-items:flex-start;gap:8px;padding-top:10px;border-top:1px solid var(--pk-card-line,var(--pk-line-soft,#2b2b26))}
310
+ .flyout-foot{display:flex;justify-content:flex-end;padding-top:10px;border-top:1px solid var(--pk-card-line,var(--pk-line-soft,#2b2b26))}
311
+ .flyout.read-panel{width:min(440px,calc(100vw - 16px))}
312
+ .read-head{display:flex;flex-wrap:wrap;align-items:center;gap:6px}
313
+
314
+ /* ══ S3 · the bottom strip: one line per item, a row opens in place, the height is the owner's (D68; Spec §6.2; CKC-09
315
+ AC-38; after WorkflowKeeper's D55). A row is the one-line form of the card the next batch gives every list: four
316
+ slots — mark · one sentence · object · time — and the part that opens. Only the colours above are used. ══ */
317
+ .strip{position:relative;flex:none;height:var(--pk-strip-h,260px);min-height:120px;max-height:calc(100% - 240px)}
318
+ /* Its top edge is dragged (or moved with the arrow keys); it takes no height of its own. */
319
+ .strip-resizer{flex:none;height:0;position:relative;z-index:6;touch-action:none;outline:0}
320
+ .strip-resizer::before{content:'';position:absolute;left:0;right:0;top:-4px;height:8px;cursor:row-resize}
321
+ .strip-resizer::after{content:'';position:absolute;left:0;right:0;top:-1px;height:2px;background:transparent}
322
+ .strip-resizer:hover::after,.strip-resizer:focus-visible::after,.strip-resizer.sizing::after{background:var(--pk-accent,#e3b76f)}
323
+ /* The pill at the bottom right lies over the strip's corner. No column is kept empty for it (S1 kept one): the strip
324
+ takes the whole width, and only a column the pill stands over ends its list above it (views.js fitStripToDock). */
325
+ .strip>section.over-dock .items{margin-bottom:var(--pk-dock-h)}
326
+ /* The header: the name and the count, one line in every column. Under `Notes (attention)`, why the list is shorter
327
+ than the notes (D50) is a card that opens on hover or focus of the header (owner 2026-09-22: the two lines under
328
+ the name made that header taller than its neighbours; the reason belongs in a hover box). */
329
+ .strip header{display:block;padding:6px 12px;position:relative;z-index:7}
330
+ .strip header[tabindex]{cursor:default;outline:0}
331
+ .strip-title{display:flex;justify-content:space-between;align-items:baseline;gap:10px}.strip-title>span{white-space:nowrap}
332
+ .strip-title small{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-weight:400}
333
+ .strip-why{display:none;position:absolute;left:8px;top:calc(100% - 2px);width:max-content;max-width:min(460px,calc(100vw - 32px));padding:7px 10px 8px;flex-direction:column;gap:2px;font-size:11px;font-weight:400;line-height:1.5;text-shadow:none;color:var(--pk-card-text,var(--pk-text,#e7e7df));background:var(--pk-card-bg,var(--pk-surface,#1c1c1a));background-image:var(--pk-card-texture,none);border:1px solid var(--pk-card-border,var(--pk-line,#35352f));border-radius:var(--pk-card-radius,var(--pk-radius-panel,8px));box-shadow:var(--pk-card-shadow,var(--pk-shadow-float,0 14px 40px #0009))}
334
+ .strip-why small{font-size:10.5px;color:var(--pk-text-muted,#a5a59b);margin-top:3px}
335
+ .strip header:hover .strip-why,.strip header:focus-within .strip-why{display:flex}
336
+ /* A row. */
337
+ .strip .items{padding:4px 6px;gap:1px}
338
+ .strip-row{flex:none;border-radius:var(--pk-radius-sm,4px)}
339
+ .strip .item{flex-direction:row;align-items:center;gap:8px;min-height:26px;padding:3px 8px;white-space:nowrap}
340
+ .row-marks{display:inline-flex;gap:3px;flex:none}
341
+ .mk{display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;border:1px solid var(--pk-line,#35352f);border-radius:var(--pk-radius-sm,4px);font-size:10.5px;font-weight:700;line-height:1;color:var(--pk-text-muted,#a5a59b)}
342
+ .mk-decide{background:var(--pk-accent,#e3b76f);border-color:var(--pk-accent,#e3b76f);color:var(--pk-on-accent,#141413)}
343
+ .mk-discuss{border-color:var(--pk-blue,#9fbdd5);color:var(--pk-blue,#9fbdd5)}
344
+ .mk-info,.mk-plain{color:var(--pk-text-faint,#85857b)}
345
+ .mk-origin{border-style:dashed;font-weight:600}
346
+ .mk-work{background:var(--pk-raised,#242420);font-weight:600}
347
+ .mk-good{border-color:var(--pk-green,#a3c5ac);color:var(--pk-green,#a3c5ac)}
348
+ .mk-bad{border-color:var(--pk-red,#dca6a0);color:var(--pk-red,#dca6a0)}
349
+ .mk-wait{border-color:var(--pk-accent,#e3b76f);color:var(--pk-accent,#e3b76f)}
350
+ .row-text{flex:1 1 0;min-width:0;overflow:hidden;text-overflow:ellipsis}
351
+ .row-object{flex:0 1 auto;min-width:0;max-width:32%;overflow:hidden;text-overflow:ellipsis;font-size:11px;color:var(--pk-text-muted,#a5a59b)}
352
+ .row-time{flex:none;font-size:10.5px;color:var(--pk-text-faint,#85857b);font-variant-numeric:tabular-nums}
353
+ /* The sentence comes first: in a narrow column the object gives way to it, then the time (both stay in the hover and
354
+ in the part that opens). A column is narrow with three of them beside the docked Keeper. */
355
+ .strip>section{container-type:inline-size}
356
+ @container (max-width:420px){.row-object{max-width:24%}}
357
+ @container (max-width:290px){.row-object{display:none}}
358
+ @container (max-width:230px){.row-time{display:none}}
359
+ /* The part that opens, in place. */
360
+ .strip-row.open{background:var(--pk-card-bg,var(--pk-surface,#1c1c1a));background-image:var(--pk-card-texture,none);color:var(--pk-card-text,var(--pk-text,#e7e7df));box-shadow:inset 2px 0 0 var(--pk-accent,#e3b76f),var(--pk-card-shadow,0 0 0 0 transparent)}
361
+ .strip-row.open>.item{background:var(--pk-card-head-bg,var(--pk-raised,#242420));border-radius:var(--pk-radius-sm,4px)}
362
+ .row-more{display:flex;flex-direction:column;gap:7px;padding:8px 10px 10px 12px;font-size:12px;white-space:normal;overflow-wrap:anywhere}
363
+ .more-title{font-size:12.5px;font-weight:600;line-height:1.45}
364
+ .more-text{line-height:1.65}
365
+ .more-state,.more-objects,.more-actions,.on-line{gap:6px}
366
+ .more-items{display:flex;flex-direction:column;gap:3px}
367
+ .more-foot{display:flex;flex-wrap:wrap;gap:8px 10px;align-items:center;padding-top:8px;border-top:1px solid var(--pk-card-line,var(--pk-line-soft,#2b2b26))}
368
+ .more-foot>.more-actions{flex:1 1 auto}
369
+ /* The last line of the part that opens — its actions and Details together — stays in sight at the foot of the column
370
+ while a tall expansion is scrolled, so the buttons are never below the fold without a sign of them (V16). */
371
+ .row-more>:last-child{position:sticky;bottom:0;background:var(--pk-card-bg,var(--pk-surface,#1c1c1a));padding-bottom:2px}
372
+ /* One reading surface for all columns. It rises over the index instead of squeezing a long note into one cell. */
373
+ .strip.has-sheet{overflow:visible}.strip-sheet{position:absolute;left:0;right:0;bottom:0;height:clamp(260px,42dvh,460px);max-height:calc(100dvh - var(--pk-top) - 90px);z-index:9;display:flex;flex-direction:column;min-width:0;color:var(--pk-card-text,var(--pk-text,#e7e7df));background:var(--pk-card-bg,var(--pk-surface,#1c1c1a));background-image:var(--pk-card-texture,none);border-top:2px solid var(--pk-accent,#e3b76f);box-shadow:var(--pk-card-shadow,var(--pk-shadow-float,0 -12px 34px #0008));outline:0}.strip-sheet.opening{animation:pk-sheet-up .22s cubic-bezier(.2,.75,.3,1) both}@keyframes pk-sheet-up{from{transform:translateY(100%)}to{transform:none}}
374
+ .strip-sheet-head{display:flex;align-items:flex-start;justify-content:space-between;gap:14px;padding:9px 12px 8px 16px;border-bottom:1px solid var(--pk-card-line,var(--pk-line-soft,#2b2b26));background:var(--pk-card-head-bg,var(--pk-raised,#242420));flex:none}.strip-sheet-heading{display:flex;align-items:baseline;gap:8px;min-width:0}.strip-sheet-heading strong{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:13px}.strip-sheet-heading small{max-width:28%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.strip-sheet-close{flex:none}
375
+ .strip-sheet-body{flex:1;min-height:0;overflow:auto;margin-bottom:var(--pk-dock-h);padding:12px 18px;gap:9px;scrollbar-width:thin}.strip-sheet-body>.more-title{font-size:15px;line-height:1.4}.strip-sheet-body>.more-text{width:100%;font-size:12.5px}.strip-sheet-body>.more-foot{position:static;bottom:auto;z-index:auto;margin-top:2px;padding:8px 0 4px;background:var(--pk-card-bg,var(--pk-surface,#1c1c1a))}
376
+ /* A narrow window: the columns are stacked into one list that scrolls as a whole, under the graph and above the pill's
377
+ band. (The rule of the first narrow block was meant to do this; the column rules that follow it in the file undid it.) */
378
+ @media(max-width:700px){.strip,.strip.two,.strip.three{grid-template-columns:minmax(0,1fr);height:auto;min-height:0;max-height:40vh;overflow:auto}.strip.has-sheet{height:40vh;min-height:220px;overflow:hidden}.strip.has-sheet>.strip-sheet{height:100%;max-height:none}.strip>section{border-right:0;min-height:auto}.strip .items{overflow:visible}.strip-resizer{display:none}.row-object{max-width:30%}.strip-sheet-head{padding-left:10px}.strip-sheet-heading small{display:none}.strip-sheet-body{padding-left:12px;padding-right:12px}}
379
+ @media(prefers-reduced-motion:reduce){.strip-sheet.opening{animation:none}}
380
+
381
+ /* ══ S3 · the outline shows a name in full: a second line when one is not enough, the whole name on hover (D68; Spec
382
+ §6.1; CKC-09 AC-41). The name is in a span, because a button itself cannot be clamped to lines. ══ */
383
+ .outline .ol-area{align-items:flex-start}
384
+ .outline .ol-work{white-space:normal;overflow:visible}
385
+ .outline .ol-area>span,.outline .ol-work>span{display:-webkit-box;-webkit-line-clamp:2;-webkit-box-orient:vertical;min-width:0;overflow:hidden;white-space:normal;text-overflow:clip;overflow-wrap:anywhere;line-height:1.4}
386
+ .outline .ol-area>small{flex:none;line-height:1.55}
387
+
388
+ /* ══ QC AY package C · a Follow up result's news (Spec §3.8, §6.2; CKC-07 AC-27, CKC-24 AC-15): by kind, each entry
389
+ what it is (a press goes there), where it is and what changed for it; the top bar's "nothing new". The entries take
390
+ the ink of the surface they are drawn on (the reading sheet, the result's dialog), so they read on every theme. ══ */
391
+ .round-news{display:flex;flex-direction:column;gap:9px}
392
+ .news-kind>h4{margin:0 0 3px;color:inherit;opacity:.8}
393
+ .news-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:4px}
394
+ .news-list>li{display:flex;flex-wrap:wrap;align-items:baseline;gap:2px 8px;line-height:1.5}
395
+ .news-list .news-go{color:inherit;text-decoration:underline;text-decoration-color:var(--pk-accent,#e3b76f);text-underline-offset:2px;overflow-wrap:anywhere}
396
+ .news-list .news-go:hover{color:var(--pk-accent,#e3b76f)}
397
+ .news-pos,.news-why{color:inherit;opacity:.72;font-size:11px}
398
+ .news-why{flex-basis:100%}
399
+ .round-news-counts{gap:6px}
400
+ .coverage-pill.with-last{display:inline-grid;justify-items:start;row-gap:1px;padding-top:3px;padding-bottom:3px}@media(max-width:700px){.coverage-pill.with-last{display:none}}.coverage-pill>.cov-parts{display:inline-flex;align-items:center;gap:8px}.coverage-pill>.cov-last-round{color:var(--pk-text,#e7e7df)}
401
+
402
+ /* ══ D100 · panels: the workbench as floating panels with gaps between them, all made from one card (Spec §6.1, §6.16;
403
+ CKC-09 AC-38, AC-43, AC-44, AC-45). The top bar, the rail, the main panel and the drawer are the section level of the
404
+ ladder (ground < section < card < control): the theme's panel colour, its section border, radius and shadow; the
405
+ Keeper conversation is the card, as it was. Only the tokens above are used, so every theme keeps working. ══ */
406
+ .panel{background:var(--pk-rail,#191918);border:1px solid var(--pk-section-border,var(--pk-line-soft,#2b2b26));border-radius:var(--pk-radius-panel,10px);box-shadow:var(--pk-section-shadow,0 6px 22px #0005);min-width:0;min-height:0}
407
+ .topbar{grid-area:top;min-height:48px;padding:6px 10px 6px 12px}
408
+ .mobile-nav{grid-area:nav}
409
+ .rail{grid-area:rail;overflow-x:hidden}
410
+ .main-panel{grid-area:main;position:relative;z-index:1;display:flex;flex-direction:column;overflow:hidden}
411
+ .drawer{grid-area:drawer;position:relative;z-index:2;display:flex;flex-direction:column;min-height:44px;margin-right:calc(var(--pk-pill-w) + var(--pk-gap))}
412
+ /* The brand leads the top bar, so the rail can fold without losing it. */
413
+ .topbar .brand{height:auto;padding:0 4px 0 0;flex:none}.topbar .brand-name{white-space:nowrap}
414
+ /* The head of the rail: `Workspace` and the fold. Folded, the rail keeps the fold, the view icons and the status dot. */
415
+ .rail-head{display:flex;align-items:center;justify-content:space-between;gap:6px;padding:6px 8px 2px 0;flex:none}
416
+ .rail-head .rail-section{padding-top:4px}
417
+ .rail-fold,.panel-max{flex:none;display:inline-flex;align-items:center;justify-content:center;width:28px;height:26px;border:1px solid var(--pk-control-border,var(--pk-line,#35352f));border-radius:var(--pk-control-radius,var(--pk-radius-sm,5px));background:var(--pk-control-bg,var(--pk-surface,#1c1c1a));background-image:var(--pk-control-texture,none);box-shadow:var(--pk-control-shadow,none);color:var(--pk-text-muted,#a5a59b);font-size:14px;line-height:1}
418
+ .rail-fold:hover,.panel-max:hover{color:var(--pk-text,#e7e7df);background:var(--pk-control-hover-bg,var(--pk-raised,#242420));border-color:var(--pk-control-hover-border,var(--pk-control-border,var(--pk-line,#35352f)))}
419
+ .panel-max svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
420
+ .panel-max[aria-pressed="true"]{color:var(--pk-accent,#e3b76f);border-color:var(--pk-accent,#e3b76f)}
421
+ .nav-icon{display:inline-flex;width:16px;height:16px;flex:none}.nav-icon svg{width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:1.5;stroke-linecap:round;stroke-linejoin:round}
422
+ .brand-mark{display:inline-flex}.brand-mark svg{width:22px;height:22px}
423
+ .topbar .topbar-main{flex:1 1 300px}
424
+ .nav-label{min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
425
+ body.rail-folded{--pk-rail-w:52px}
426
+ body.rail-folded .rail-full,body.rail-folded .nav-label,body.rail-folded .nav-item .count,body.rail-folded .keeper-entry .kw{display:none}
427
+ body.rail-folded .rail-head{justify-content:center;padding:8px 0 6px}
428
+ body.rail-folded nav.views{padding:0 6px;gap:4px}
429
+ body.rail-folded .nav-item{justify-content:center;padding:8px 0}
430
+ body.rail-folded .rail-foot{padding:8px 6px}
431
+ body.rail-folded .keeper-entry{justify-content:center;padding:8px 0;position:relative}
432
+ body.rail-folded .keeper-entry::after{content:'';position:absolute;right:6px;top:6px;width:7px;height:7px;border-radius:50%;background:var(--pk-text-faint,#85857b)}
433
+ body.rail-folded .keeper-entry:has(.dot.green)::after{background:var(--pk-green,#a3c5ac)}body.rail-folded .keeper-entry:has(.dot.amber)::after{background:var(--pk-accent,#e3b76f)}body.rail-folded .keeper-entry:has(.dot.red)::after{background:var(--pk-red,#dca6a0)}body.rail-folded .keeper-entry:has(.dot.blue)::after{background:var(--pk-blue,#9fbdd5)}
434
+ .shell{transition:grid-template-columns .18s ease}
435
+ /* The header of the main panel: the view's one row of controls (or its name), and the button that fills the window. */
436
+ .panel-head{display:flex;align-items:center;gap:8px;min-height:44px;padding:6px 8px 6px 12px;border-bottom:1px solid var(--pk-head-border,var(--pk-line-soft,#2b2b26));flex:none}
437
+ .panel-head .top-view-tools{flex:1;min-width:0}
438
+ .panel-title{flex:1;min-width:0;font-size:12px;font-weight:600;color:var(--pk-text-muted,#a5a59b);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
439
+ .main-panel>.body{flex:1}
440
+ /* The drawer: one line, as tall as the pill beside it, until it is pulled up; then the columns, as tall as dragged. */
441
+ .drawer.empty{visibility:hidden;border-color:transparent;box-shadow:none;background:none}
442
+ .drawer-bar{display:flex;align-items:center;gap:10px;min-height:42px;padding:0 10px 0 4px;flex:none}
443
+ .drawer-toggle{flex:1;min-width:0;display:flex;align-items:center;gap:8px;padding:6px 8px;border-radius:var(--pk-radius-sm,5px);text-align:left;font-size:12px;color:var(--pk-text-muted,#a5a59b);white-space:nowrap;overflow:hidden}
444
+ .drawer-toggle:hover{background:var(--pk-raised,#242420);color:var(--pk-text,#e7e7df)}
445
+ .drawer-toggle b{font-weight:600;color:var(--pk-text,#e7e7df);font-variant-numeric:tabular-nums}
446
+ .drawer-caret{flex:none;width:14px;color:var(--pk-accent,#e3b76f);font-size:12px}
447
+ .drawer-part{min-width:0;overflow:hidden;text-overflow:ellipsis}.drawer-part.since{color:var(--pk-accent,#e3b76f)}
448
+ .drawer-sep{color:var(--pk-text-faint,#85857b)}
449
+ .attn-count{flex:none;display:inline-flex;align-items:center;gap:4px;min-height:24px;padding:1px 8px;border:1px solid var(--pk-accent,#e3b76f);border-radius:var(--pk-radius-pill,12px);font-size:11.5px;color:var(--pk-accent,#e3b76f);background:var(--pk-accent-soft,#302b22);white-space:nowrap;font-variant-numeric:tabular-nums}
450
+ .attn-count:hover{background:var(--pk-accent-bg,#322c20)}
451
+ .attn-count.active{color:var(--pk-on-accent,#211e16);background:var(--pk-accent,#e3b76f)}
452
+ .attn-count .attn-mark{font-size:11px;line-height:1}
453
+ .strip-counts{display:inline-flex;align-items:baseline;gap:8px;min-width:0}
454
+ .drawer .strip{display:none;border-top:1px solid var(--pk-line-soft,#2b2b26);border-radius:0 0 var(--pk-radius-panel,10px) var(--pk-radius-panel,10px);max-height:none}
455
+ .drawer.open .strip{display:grid}
456
+ .drawer .strip-resizer{display:none}.drawer.open .strip-resizer{display:block}
457
+ .drawer .strip-sheet-body{margin-bottom:0}
458
+ .drawer .strip-sheet{border-radius:var(--pk-radius-panel,10px) var(--pk-radius-panel,10px) 0 0}
459
+ /* Filling the window: the main panel is laid over the workbench, which steps aside; the pill keeps its band at the
460
+ bottom, and the docked Keeper still takes the right, from the top this time (the top bar has stepped aside). */
461
+ body.maximised .main-panel{position:fixed;left:var(--pk-gap);top:var(--pk-gap);right:var(--pk-gap);bottom:var(--pk-dock-h);z-index:28;margin-right:0}
462
+ body.maximised .shell>.topbar,body.maximised .shell>.rail,body.maximised .shell>.drawer,body.maximised .shell>.mobile-nav{visibility:hidden}
463
+ body.maximised .keeper-panel.docked{top:var(--pk-gap)}
464
+ @media(min-width:901px){body.maximised.keeper-docked .main-panel{right:calc(var(--pk-keeper-w) + 2 * var(--pk-gap));margin-right:0}}
465
+ /* A narrow window (Spec §6.15): the rail is the navigation at the top, the panels stack, and the drawer lies full width
466
+ over the band of the pill. */
467
+ @media(max-width:700px){
468
+ :root{--pk-gap:6px}
469
+ .shell{grid-template-rows:auto auto minmax(0,1fr) auto;grid-template-areas:"top" "nav" "main" "drawer"}
470
+ .mobile-nav{border:1px solid var(--pk-section-border,var(--pk-line-soft,#2b2b26));border-radius:var(--pk-radius-panel,10px);background:var(--pk-rail,#191918)}
471
+ .topbar .brand-name{display:none}
472
+ .drawer,body.keeper-docked .drawer{margin-right:0;margin-bottom:calc(var(--pk-dock-h) - var(--pk-gap))}
473
+ .panel-head{flex-wrap:wrap}
474
+ .drawer-part{display:inline-flex;gap:4px;min-width:0}.dp-name{min-width:0;max-width:6.5em;overflow:hidden;text-overflow:ellipsis}
475
+ .drawer.open .strip{max-height:30vh}
476
+ }
477
+ @media(prefers-reduced-motion:reduce){.shell{transition:none}}
478
+ /* What intake could not take in: the count beside the coverage pill opens the list, one row a file (CKC-07 AC-10). */
479
+ .cov-failed{cursor:pointer;flex:none;align-self:center}
480
+ .cov-failed:hover{border-color:var(--pk-red,#dca6a0)}
481
+ .failed-list .failed-item{display:flex;flex-direction:column;gap:3px;padding:8px 10px;border:1px solid var(--pk-line-soft,#2b2b26);border-radius:var(--pk-radius-sm,5px);background:var(--pk-well-bg,var(--pk-surface,#1c1c1a))}
482
+ .failed-list .failed-item .mono{overflow-wrap:anywhere}
483
+ /* ── The folder chooser: a panel under the field a location is typed in (folder-chooser.js). The field stays a field;
484
+ the panel is a list of folders to walk, in the dialog's own controls. ── */
485
+ .fc{display:flex;flex-direction:column;gap:8px;padding:10px;border:1px solid var(--pk-well-border,var(--pk-line,#35352f));border-radius:var(--pk-radius,6px)}.fc[hidden]{display:none}
486
+ .fc-bar{display:flex;gap:6px;align-items:center}.fc-bar .fc-path{flex:1;min-width:0;padding:5px 8px}.fc-sub{justify-content:space-between;align-items:flex-start;gap:10px}
487
+ .fc-starts{display:flex;flex-wrap:wrap;gap:4px;min-width:0}.fc-hidden{display:inline-flex;align-items:center;gap:5px;min-height:26px;font-size:11px;color:var(--pk-text-muted,#a5a59b);white-space:nowrap;cursor:pointer}
488
+ .fc-list{display:flex;flex-direction:column;height:232px;overflow:auto;background:var(--pk-well-bg,var(--pk-surface,#1c1c1a));border:1px solid var(--pk-well-border,var(--pk-line-soft,#2b2b26));border-radius:var(--pk-radius-sm,5px);box-shadow:var(--pk-well-shadow,none)}
489
+ .fc-row{display:flex;flex:none;gap:8px;align-items:center;width:100%;padding:5px 9px;text-align:left;border-bottom:1px solid var(--pk-line-soft,#2b2b26)}.fc-row:hover,.fc-row:focus-visible{background:var(--pk-raised,#242420)}.fc-row:focus-visible{outline-offset:-2px}
490
+ .fc-icon{display:inline-flex;flex:none;color:var(--pk-text-muted,#a5a59b)}.fc-name{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fc-row.dim .fc-name,.fc-row.dim .fc-icon{color:var(--pk-text-faint,#85857b)}
491
+ .fc-empty{padding:14px 12px}.fc-more:empty{display:none}
492
+ .fc-foot{display:flex;gap:8px;align-items:center;justify-content:flex-end;flex-wrap:wrap}.fc-here{flex:1;min-width:0;display:flex;gap:6px;align-items:center;font-size:12px}.fc-here-name{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
@@ -0,0 +1,36 @@
1
+ // Which theme opens, and which files that is (Spec §6.16; CKC-09 AC-40). Pure: the choice kept in this browser and
2
+ // the manifest go in, an id and two hrefs come out; theme.js does the DOM. Tested by src/ui/theme-pick.test.ts.
3
+
4
+ /** The factory look: ProjectKeeper's own dark skin, no theme file at all. Kept as the empty string, like WorkflowKeeper. */
5
+ export const FACTORY = '';
6
+ /** Where the choice is kept: this browser's, a personal preference, never a project asset. */
7
+ export const THEME_KEY = 'pk.theme';
8
+
9
+ const isFolder = (id) => /^[a-z0-9][a-z0-9_-]*$/i.test(String(id));
10
+
11
+ /**
12
+ * The theme to open: what this browser remembers when the manifest still lists it (the factory look counts as
13
+ * remembered); else the manifest's default; else the factory look. Without a manifest there is nothing to load.
14
+ */
15
+ export function resolveTheme(saved, manifest) {
16
+ const ids = new Set((manifest?.themes ?? []).map((t) => t.id));
17
+ if (saved === FACTORY) return FACTORY;
18
+ if (typeof saved === 'string' && ids.has(saved)) return saved;
19
+ return manifest && ids.has(manifest.default) ? manifest.default : FACTORY;
20
+ }
21
+
22
+ /** The two files of a theme: the frozen board and ProjectKeeper's own layer for it. The factory look is no file. */
23
+ export function themeHrefs(id) {
24
+ if (id === FACTORY || !isFolder(id)) return { board: '', pk: '' };
25
+ return { board: `/themes/${id}/board.css`, pk: `/themes/${id}/pk.css` };
26
+ }
27
+
28
+ /** What the picker offers: the factory look first, then the manifest's themes in its order, by their names. */
29
+ export function themeOptions(manifest) {
30
+ return [{ id: FACTORY, name: 'Factory' }, ...(manifest?.themes ?? []).map((t) => ({ id: t.id, name: t.name }))];
31
+ }
32
+
33
+ /** A theme's name for a message; an unknown id is shown as itself. */
34
+ export function themeName(id, manifest) {
35
+ return themeOptions(manifest).find((o) => o.id === id)?.name ?? String(id);
36
+ }