@nanocollective/roster 0.1.0-alpha.5 → 0.1.0-alpha.50

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 (102) hide show
  1. package/README.md +70 -84
  2. package/dist/cli.js +4833 -2752
  3. package/docs/README.md +9 -6
  4. package/docs/agents.md +24 -20
  5. package/docs/architecture.md +13 -5
  6. package/docs/charters/analyst.md +65 -0
  7. package/docs/charters/cmo.md +69 -0
  8. package/docs/charters/community.md +63 -0
  9. package/docs/charters/cto.md +71 -0
  10. package/docs/charters/designer.md +65 -0
  11. package/docs/charters/devops.md +65 -0
  12. package/docs/charters/pm.md +70 -0
  13. package/docs/charters/qa.md +65 -0
  14. package/docs/charters/support.md +60 -0
  15. package/docs/charters/writer.md +63 -0
  16. package/docs/commands.md +93 -7
  17. package/docs/concepts.md +61 -14
  18. package/docs/cost.md +36 -1
  19. package/docs/developing.md +16 -21
  20. package/docs/doctor-codes.md +10 -2
  21. package/docs/export.md +2 -0
  22. package/docs/extending.md +2 -2
  23. package/docs/getting-started.md +128 -78
  24. package/docs/images/brain.jpg +0 -0
  25. package/docs/images/org.jpg +0 -0
  26. package/docs/images/prompt.jpg +0 -0
  27. package/docs/images/setup-org.jpg +0 -0
  28. package/docs/images/setup-plan.jpg +0 -0
  29. package/docs/images/staff.jpg +0 -0
  30. package/docs/manual-steps.md +94 -123
  31. package/docs/memory.md +21 -3
  32. package/docs/org-yaml.md +40 -2
  33. package/docs/portal.md +165 -48
  34. package/docs/prompts.md +31 -4
  35. package/docs/security.md +37 -5
  36. package/docs/session-workflow.md +63 -17
  37. package/docs/staff-yaml.md +30 -3
  38. package/docs/troubleshooting.md +8 -8
  39. package/docs/upgrading.md +9 -3
  40. package/docs/writing-a-charter.md +28 -0
  41. package/package.json +18 -20
  42. package/templates/brain/.github/workflows/%%STAFF%%-daily.yaml +26 -1
  43. package/templates/brain/.github/workflows/%%STAFF%%-mention.yaml +41 -7
  44. package/templates/brain/CHARTER.md +3 -3
  45. package/templates/brain/README.md +1 -0
  46. package/templates/brain/log/decisions.md +3 -0
  47. package/templates/brain/staff.yaml +4 -1
  48. package/templates/brain/strategy/ideas.md +7 -0
  49. package/templates/briefs/priorities.md +46 -0
  50. package/templates/ops/.github/workflows/session.yaml +236 -15
  51. package/templates/ops/agents.mjs +7 -3
  52. package/templates/ops/compose.mjs +31 -3
  53. package/templates/ops/inflight.mjs +157 -0
  54. package/templates/ops/org/operating.md +43 -4
  55. package/templates/ops/org/voice.md +9 -0
  56. package/templates/ops/prompts/_inflight.md +14 -0
  57. package/templates/ops/prompts/_paths.md +2 -1
  58. package/templates/ops/prompts/daily.md +37 -9
  59. package/templates/ops/prompts/mention.md +21 -0
  60. package/templates/ops/run-record.mjs +146 -0
  61. package/templates/portal/css/base.css +167 -73
  62. package/templates/portal/css/brain.css +23 -20
  63. package/templates/portal/css/diff.css +10 -9
  64. package/templates/portal/css/graph.css +12 -7
  65. package/templates/portal/css/health.css +13 -11
  66. package/templates/portal/css/home.css +93 -0
  67. package/templates/portal/css/inbox.css +45 -25
  68. package/templates/portal/css/layout.css +90 -46
  69. package/templates/portal/css/markdown.css +36 -14
  70. package/templates/portal/css/runs.css +13 -0
  71. package/templates/portal/css/setup.css +116 -34
  72. package/templates/portal/index.html +21 -9
  73. package/templates/portal/js/api.js +44 -4
  74. package/templates/portal/js/app.js +94 -9
  75. package/templates/portal/js/dialog.js +83 -0
  76. package/templates/portal/js/homesort.js +174 -0
  77. package/templates/portal/js/icons.js +37 -0
  78. package/templates/portal/js/inflight.js +18 -0
  79. package/templates/portal/js/md.js +5 -2
  80. package/templates/portal/js/mdedit.js +84 -0
  81. package/templates/portal/js/readiness.js +70 -0
  82. package/templates/portal/js/refresh.js +10 -2
  83. package/templates/portal/js/state.js +11 -5
  84. package/templates/portal/js/views/app.js +24 -7
  85. package/templates/portal/js/views/brain.js +1 -1
  86. package/templates/portal/js/views/checklist.js +10 -4
  87. package/templates/portal/js/views/credential.js +84 -0
  88. package/templates/portal/js/views/graph.js +1 -1
  89. package/templates/portal/js/views/health.js +17 -4
  90. package/templates/portal/js/views/hire.js +593 -0
  91. package/templates/portal/js/views/home.js +546 -0
  92. package/templates/portal/js/views/inbox.js +226 -70
  93. package/templates/portal/js/views/org.js +46 -106
  94. package/templates/portal/js/views/orgedit.js +234 -0
  95. package/templates/portal/js/views/paste.js +87 -21
  96. package/templates/portal/js/views/prompt.js +11 -4
  97. package/templates/portal/js/views/repos.js +20 -15
  98. package/templates/portal/js/views/runonce.js +94 -0
  99. package/templates/portal/js/views/runs.js +170 -0
  100. package/templates/portal/js/views/setup.js +261 -75
  101. package/templates/portal/js/views/staff.js +170 -243
  102. package/templates/portal/js/views/todo.js +62 -0
@@ -0,0 +1,93 @@
1
+ /* Home: what needs you, who is working, your requests, closed today. */
2
+
3
+ .home{max-width:820px}
4
+ /* Secondary text on Home is read, not scanned for numbers: the readable face and colour, not
5
+ the faint monospace the rest of the portal uses for metadata. */
6
+ .home .meta{font:12.5px/1.5 var(--sans);color:var(--ink-dim)}
7
+ .home h2{font:600 13px var(--sans);color:var(--ink-dim);text-transform:none;letter-spacing:0;
8
+ margin:26px 0 10px;display:flex;align-items:center;gap:8px}
9
+ .home h2 .pill{font-size:10.5px}
10
+ .homeempty{color:var(--ink-faint);font-size:13px;margin:0 0 8px}
11
+
12
+ .homeaskcard{display:flex;flex-direction:column;gap:10px}
13
+ .homeask,.homeanswer{width:100%;box-sizing:border-box;resize:vertical;min-height:44px;
14
+ font:13px/1.45 var(--sans);color:var(--ink);background:var(--surface-2);
15
+ border:1px solid transparent;border-radius:8px;padding:8px 10px}
16
+ .homeask:focus,.homeanswer:focus{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
17
+ .homeanswer{margin-top:10px}
18
+ .homewho{font:500 12.5px var(--sans);color:var(--ink);background:var(--surface-2);border:0;
19
+ border-radius:8px;padding:5px 8px}
20
+
21
+ .homecard{padding:14px 16px;cursor:pointer;display:grid;grid-template-columns:1fr auto;
22
+ align-items:center;gap:10px 18px}
23
+ .homeabout{min-width:0}
24
+ .homecard:hover{border-color:var(--border-strong)}
25
+ .homecard:focus-visible{outline:none;border-color:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}
26
+ .homehead{display:flex;align-items:baseline;gap:8px}
27
+ .homehead .chip{flex:none}
28
+ .homeopen{all:unset;cursor:pointer;font:600 14px/1.35 var(--sans);color:var(--ink);letter-spacing:-.01em}
29
+ .homeopen:hover{text-decoration:underline}
30
+ .homeopen:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:4px}
31
+ .homeabout > .meta{margin-top:4px}
32
+ .homedue{margin:8px 0 0;font-size:12.5px;color:var(--ink-dim)}
33
+ .homedue.late{color:var(--warn)}
34
+ .homeacts{justify-content:flex-end;flex-wrap:nowrap}
35
+ .homeacts .meta{white-space:normal;text-align:right;max-width:220px}
36
+ @media (max-width:640px){
37
+ .homecard{grid-template-columns:1fr}
38
+ .homeacts{justify-content:flex-start;flex-wrap:wrap}
39
+ .homeacts .meta{text-align:left}
40
+ }
41
+
42
+ .homeworking{display:flex;flex-direction:column;padding:4px 16px}
43
+ .homerun{display:grid;grid-template-columns:minmax(150px,220px) auto 1fr auto;align-items:center;
44
+ gap:12px;padding:12px 0;font-size:13.5px}
45
+ .homerun + .homerun{border-top:1px solid var(--line)}
46
+ .homewho2{display:inline-flex;align-items:center;gap:8px;min-width:0}
47
+ .homewho2 b{font-weight:600;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
48
+ .homewho2 .ic{--ic:15px;color:var(--ink-dim);flex:none}
49
+ .homestate{justify-self:start}
50
+ .homestate.ok::before{content:"";width:6px;height:6px;border-radius:50%;background:currentColor;
51
+ margin-right:6px;animation:homepulse 1.6s var(--ease) infinite}
52
+ @keyframes homepulse{50%{opacity:.3}}
53
+ @media (prefers-reduced-motion:reduce){.homestate.ok::before{animation:none}}
54
+ .homerunwhat{color:var(--ink);line-height:1.45;min-width:0}
55
+ .homerunsub{color:var(--ink-dim);font-size:12.5px;margin-top:2px}
56
+ .homerunlog .ghbtn{min-height:28px;padding:3px 12px}
57
+ @media (max-width:640px){
58
+ .homerun{grid-template-columns:1fr auto;grid-template-areas:"who state" "what what" "log log"}
59
+ .homewho2{grid-area:who}.homestate{grid-area:state;justify-self:end}
60
+ .homerunwhat{grid-area:what}.homerunlog{grid-area:log}
61
+ }
62
+
63
+ .homerest{margin-top:26px}
64
+ .homecard.gone,.homeline.gone{opacity:0;transform:translateX(12px);
65
+ transition:opacity .2s var(--ease),transform .2s var(--ease)}
66
+
67
+ /* Activity you have not seen, as Trackers marks it: a blue edge and a bold title. Opening the
68
+ thread marks it read, here and on GitHub. */
69
+ .homecard.unread{box-shadow:inset 3px 0 0 var(--blue)}
70
+ .homeline.unread{box-shadow:inset 3px 0 0 var(--blue);margin:0 -16px;padding-left:13px;padding-right:16px}
71
+ .unread .homeopen,.unread .homerowtitle{font-weight:700}
72
+ .unread .homeopen::before,.unread .homerowtitle::before{content:"";display:inline-block;width:7px;height:7px;border-radius:50%;
73
+ background:var(--blue);margin:0 7px 1px 0;vertical-align:middle}
74
+
75
+ .homereports{display:flex;flex-direction:column;gap:14px;padding:14px 16px}
76
+ .homereport{display:grid;grid-template-columns:190px 1fr;gap:12px;font-size:13px;line-height:1.5}
77
+ .homereport p{margin:0 0 3px;color:var(--ink-dim)}
78
+ .homereport p:first-child{color:var(--ink)}
79
+ .homereport .homewho2{align-items:baseline;flex-wrap:wrap;min-width:0}
80
+ @media (max-width:640px){.homereport{grid-template-columns:1fr;gap:4px}}
81
+
82
+ @media (max-width:640px){
83
+ .homehead{flex-wrap:wrap}
84
+ }
85
+
86
+ /* Rows in a list card (Unread, Your requests, Closed today) that open a thread. */
87
+ .homeline{display:grid;grid-template-columns:auto 1fr auto;align-items:center;gap:12px;
88
+ padding:12px 0;font-size:13.5px;cursor:pointer}
89
+ .homeline + .homeline{border-top:1px solid var(--line)}
90
+ .homeline .homestate{min-width:64px;justify-content:center}
91
+ .homeline:hover .homerowtitle{text-decoration:underline}
92
+ .homeline:focus-visible{outline:2px solid var(--accent);outline-offset:2px;border-radius:6px}
93
+ .homerowtitle{font-weight:600;color:var(--ink)}
@@ -5,15 +5,16 @@
5
5
  the inbox hard to scan. The line is dropped either side of the selected row, whose own
6
6
  background is already doing the separating. */
7
7
  .irow{display:block;width:100%;text-align:left;background:none;border:0;cursor:pointer;
8
- padding:11px 10px;border-radius:7px;color:var(--ink-dim);
9
- border-top:1px solid transparent}
8
+ padding:11px 12px;border-radius:var(--r);color:var(--ink-dim);
9
+ border-top:1px solid transparent;transition:background .12s var(--ease)}
10
10
  .irow + .irow{border-top-color:var(--line)}
11
11
  .irow[aria-current="true"],.irow[aria-current="true"] + .irow{border-top-color:transparent}
12
- .irow:hover{background:var(--panel-2)}
12
+ .irow:hover{background:var(--hover)}
13
+ .irow:hover + .irow,.irow:hover{border-top-color:transparent}
13
14
  .irow:focus{outline:none}
14
- .irow:focus-visible{outline:2px solid var(--accent-dim);outline-offset:-2px}
15
- .irow[aria-current="true"]{background:var(--panel-2);box-shadow:inset 2px 0 0 var(--accent)}
16
- .ititle{font:13.5px/1.4 var(--sans);color:var(--ink);display:-webkit-box;-webkit-line-clamp:2;
15
+ .irow:focus-visible{box-shadow:inset 0 0 0 1px var(--accent-dim)}
16
+ .irow[aria-current="true"]{background:var(--accent-soft)}
17
+ .ititle{font:500 13.5px/1.4 var(--sans);letter-spacing:-.005em;color:var(--ink);display:-webkit-box;-webkit-line-clamp:2;
17
18
  -webkit-box-orient:vertical;overflow:hidden}
18
19
  .irow[aria-current="true"] .ititle{color:var(--accent)}
19
20
  /* The rest of the row follows the title. The project label kept the flat grey it has when
@@ -25,7 +26,7 @@
25
26
  .dot{display:inline-block;width:6px;height:6px;border-radius:50%;background:var(--warn);
26
27
  margin-right:6px;vertical-align:2px}
27
28
  .imeta{display:flex;gap:6px;align-items:center;flex-wrap:wrap;margin-top:5px}
28
- .imeta .repo{font:10px var(--sans);letter-spacing:.03em;text-transform:uppercase;
29
+ .imeta .repo{font:600 10px var(--sans);letter-spacing:.06em;text-transform:uppercase;
29
30
  color:var(--ink-faint);border-right:1px solid var(--line);padding-right:7px}
30
31
  .imeta .num{font:10.5px var(--mono);color:var(--ink-faint)}
31
32
  .imeta .when{font:10.5px var(--sans);color:var(--ink-faint);margin-left:auto}
@@ -48,7 +49,7 @@
48
49
 
49
50
  /* ---------- thread ---------- */
50
51
  .thead{border-bottom:1px solid var(--line);padding-bottom:16px}
51
- .thead h3{margin:8px 0 0;font:600 17px/1.35 var(--sans);letter-spacing:-.012em;color:var(--ink)}
52
+ .thead h3{margin:8px 0 0;font:600 19px/1.3 var(--sans);letter-spacing:-.02em;color:var(--ink)}
52
53
  /* The same fact as the chip, said in full, once, at the top of the thread it applies to.
53
54
  Tight to the title and a clear gap above the buttons: it is a statement about this pull
54
55
  request, not a caption on the controls. At an even 10/16 it read as the latter. */
@@ -56,16 +57,25 @@
56
57
  /* A bottom margin here would collapse against the row's own 16px and change nothing, so the
57
58
  extra room is put on the row. General sibling, not adjacent: labels can sit between them. */
58
59
  .tconflict ~ .tacts{margin-top:24px}
59
- .tlabels{margin-top:10px}
60
- .cmt{border-top:1px solid var(--line);padding:16px 0}
61
- .cmt.first{border-top:0}
60
+ .tmetalabels{display:inline-flex;gap:6px;margin-left:10px;vertical-align:middle;font-family:var(--sans)}
61
+ /* A comment is a card with a header strip, so each message has a clear start and end. */
62
+ .cmt{border:1px solid var(--line);border-radius:var(--r-lg);margin:14px 0;background:var(--panel);overflow:hidden}
63
+ .cmt.human{border-color:color-mix(in srgb,var(--accent) 40%,var(--line))}
64
+ .chead{display:flex;align-items:center;gap:8px;padding:9px 14px;background:var(--panel-2);
65
+ border-bottom:1px solid var(--line);font:13px var(--sans);color:var(--ink-dim)}
66
+ .cmt.human .chead{background:color-mix(in srgb,var(--accent) 8%,var(--panel-2))}
67
+ .chead .ic{--ic:14px;color:var(--ink-faint)}
68
+ .chead b{color:var(--ink);font-weight:600}
69
+ .chead .clogin{font:11.5px var(--mono);color:var(--ink-faint)}
70
+ .chead .cwhen{margin-left:auto;font-size:12px;color:var(--ink-faint);white-space:nowrap}
71
+ .cbodywrap{padding:12px 16px 14px}
62
72
  /* The header already draws a line under itself. Whatever the reversed thread puts first must
63
73
  not draw a second one right under it. */
64
74
  .lead{border-top:0}
65
75
  /* Reactions. Small, under the comment they belong to, the way GitHub puts them — an agent's
66
76
  👀 is an acknowledgement and it should be readable at a glance rather than counted. */
67
77
  .reacts{display:flex;gap:6px;flex-wrap:wrap;margin-top:10px}
68
- .react{font:11px var(--sans);border:1px solid var(--line);border-radius:20px;padding:2px 9px;
78
+ .react{font:11px var(--sans);border:1px solid var(--line);border-radius:999px;padding:2px 9px;
69
79
  color:var(--ink-dim);background:var(--panel-2);cursor:default}
70
80
  /* Reply, close and merge, beside the title rather than under the last comment. A thread is
71
81
  read newest-first and answered from the top, so nothing here is worth scrolling to. */
@@ -79,10 +89,11 @@
79
89
  rather than three places to navigate to. */
80
90
  .tabs{display:flex;gap:2px;border-bottom:1px solid var(--line);margin:2px 0 0}
81
91
  .tab{background:none;border:0;border-bottom:2px solid transparent;cursor:pointer;
82
- font:12.5px var(--sans);color:var(--ink-faint);padding:9px 12px;margin-bottom:-1px}
92
+ font:500 12.5px var(--sans);color:var(--ink-faint);padding:9px 12px;margin-bottom:-1px;
93
+ transition:color .15s var(--ease),border-color .15s var(--ease)}
83
94
  .tab:hover{color:var(--ink)}
84
95
  .tab[aria-current="true"]{color:var(--ink);border-bottom-color:var(--accent)}
85
- .tab:focus-visible{outline:2px solid var(--accent-dim);outline-offset:-2px}
96
+ .tab:focus-visible{box-shadow:inset 0 0 0 1px var(--accent-dim);border-radius:6px 6px 0 0}
86
97
 
87
98
  .prcommit{display:flex;gap:10px;align-items:baseline;padding:8px 0;
88
99
  border-top:1px solid var(--line);font-size:12.5px}
@@ -101,9 +112,9 @@
101
112
  .labelpick .chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:6px}
102
113
  .chip.pick{background:none;cursor:pointer;padding:3px 9px;font-size:11px;color:var(--ink-dim)}
103
114
  .chip.pick:hover{border-color:var(--ink-faint);color:var(--ink)}
104
- .chip.pick:focus-visible{outline:2px solid var(--accent-dim);outline-offset:1px}
105
- .chip.pick[aria-pressed="true"]{background:color-mix(in srgb,var(--accent) 16%,transparent);
106
- border-color:var(--accent-dim);color:var(--ink)}
115
+ .chip.pick:focus-visible{box-shadow:0 0 0 3px var(--accent-soft);border-color:var(--accent)}
116
+ .chip.pick[aria-pressed="true"]{background:var(--accent-soft);
117
+ border-color:var(--accent-dim);color:var(--accent)}
107
118
 
108
119
  /* ---------- the @ list ----------
109
120
  A mention is the mechanism: a staff member's workflow gates on their handle, so a misspelled
@@ -113,12 +124,12 @@
113
124
  The height is capped well under a dialog's, because it is placed inside one and a dialog
114
125
  scrolls its own box: anything taller is not long, it is cut off. */
115
126
  .mentions{position:absolute;z-index:30;min-width:280px;max-width:min(420px,90%);max-height:196px;
116
- overflow-y:auto;background:var(--panel);border:1px solid var(--line);border-radius:8px;
117
- padding:4px;box-shadow:0 14px 34px rgba(0,0,0,.28)}
127
+ overflow-y:auto;background:var(--bg-elev);border:1px solid var(--border-strong);border-radius:var(--r);
128
+ padding:4px;box-shadow:var(--shadow-lg)}
118
129
  .mrow{display:grid;grid-template-columns:auto 1fr;gap:2px 8px;width:100%;text-align:left;
119
130
  background:none;border:0;border-radius:6px;padding:6px 8px;cursor:pointer;font:inherit}
120
- .mrow:hover{background:var(--panel-2)}
121
- .mrow[aria-selected="true"]{background:var(--panel-2);box-shadow:inset 2px 0 0 var(--accent)}
131
+ .mrow:hover{background:var(--hover)}
132
+ .mrow[aria-selected="true"]{background:var(--accent-soft)}
122
133
  .mrow .mat{font:12px var(--mono);color:var(--accent)}
123
134
  .mrow .mname{font:12.5px var(--sans);color:var(--ink);overflow:hidden;text-overflow:ellipsis;
124
135
  white-space:nowrap}
@@ -136,7 +147,8 @@ textarea.dropping{border-color:var(--accent);background:color-mix(in srgb,var(--
136
147
  /* ---------- timeline events ----------
137
148
  Not comments, so not comment-shaped: a marker, a line of prose, and whatever it points at.
138
149
  These carry the half of a thread that is agents referring to each other's work. */
139
- .tev{display:flex;gap:10px;align-items:flex-start;padding:9px 0 9px 2px;
150
+ .tev{display:flex;gap:10px;align-items:flex-start;padding:6px 0 6px 12px;margin-left:18px;
151
+ border-left:2px solid var(--line);
140
152
  font:12.5px/1.5 var(--sans);color:var(--ink-dim)}
141
153
  .tev .tico{flex:none;display:inline-flex;align-items:center;justify-content:center;
142
154
  width:16px;height:19px;color:var(--ink-faint);--ic:14px}
@@ -156,9 +168,10 @@ textarea.dropping{border-color:var(--accent);background:color-mix(in srgb,var(--
156
168
  /* An item this thread points at, as a row you can open here rather than a link that throws
157
169
  you out to GitHub. Open and closed read at a glance, which is the whole reason to show it. */
158
170
  .xref{display:flex;gap:8px;align-items:baseline;width:100%;text-align:left;background:none;
159
- border:1px solid var(--line);border-radius:7px;padding:6px 10px;margin-top:5px;
160
- cursor:pointer;color:var(--ink-dim);font:12.5px var(--sans)}
161
- .xref:hover{border-color:var(--accent-dim);background:var(--panel-2);color:var(--ink)}
171
+ border:1px solid var(--line);border-radius:var(--r);padding:7px 11px;margin-top:6px;
172
+ cursor:pointer;color:var(--ink-dim);font:12.5px var(--sans);background:var(--bg-elev);
173
+ transition:border-color .15s var(--ease),background .15s var(--ease),color .15s var(--ease)}
174
+ .xref:hover{border-color:var(--border-strong);background:var(--surface-2);color:var(--ink)}
162
175
  .xref .st{flex:none;display:inline-flex;--ic:14px;position:relative;top:2px}
163
176
  .xref .st.open{color:var(--accent)} .xref .st.closed{color:var(--derived)}
164
177
  .xref .st.merged{color:var(--derived)} .xref .st.draft{color:var(--ink-faint)}
@@ -177,3 +190,10 @@ textarea.dropping{border-color:var(--accent);background:color-mix(in srgb,var(--
177
190
  .tevmore[aria-expanded="true"] .caret{transform:none}
178
191
  .tevquiet{border-left:1px solid var(--line);margin-left:9px;padding-left:14px}
179
192
  .tevquiet .tev{padding:5px 0;font-size:12px}
193
+
194
+ /* Unread, from GitHub's notifications: a bar on the left and a heavier title. Cleared on open. */
195
+ .irow.unread{box-shadow:inset 3px 0 0 var(--blue);background:color-mix(in srgb,var(--blue) 6%,transparent)}
196
+ .irow.unread .ititle{font-weight:700;color:var(--ink)}
197
+
198
+ /* A little room between rows, so unread cards and the selected row do not run together. */
199
+ .irow{margin:3px 0}
@@ -1,86 +1,104 @@
1
1
  /* The page frame: sidebar, main column, and the tree-beside-viewer split every screen uses. */
2
2
 
3
- .app{display:grid;grid-template-columns:246px 1fr;min-height:100vh}
3
+ .app{display:grid;grid-template-columns:252px 1fr;min-height:100vh}
4
4
  /* ---------- sidebar ---------- */
5
- .side{background:var(--panel);border-right:1px solid var(--line);padding:20px 0 14px;
6
- position:sticky;top:0;height:100vh;overflow-y:auto;display:flex;flex-direction:column}
7
- .brand{padding:0 20px 18px;font:600 15px var(--sans);letter-spacing:-.01em}
5
+ .side{background:var(--side);border-right:1px solid var(--line);padding:16px 10px 12px;
6
+ position:sticky;top:0;height:100vh;overflow-y:auto;display:flex;flex-direction:column;
7
+ gap:1px}
8
+ .brand{padding:4px 10px 16px;font:600 15px/1.2 var(--sans);letter-spacing:-.02em;
9
+ display:grid;grid-template-columns:auto 1fr;column-gap:10px;align-items:center}
8
10
  /* The tab icon, in the corner it belongs in. Drawn rather than fetched so it takes the
9
11
  theme's accent and costs no request. */
10
- .brand .mark{display:inline-block;width:9px;height:9px;border-radius:50%;
11
- background:var(--accent);margin-right:7px;vertical-align:1px}
12
+ .brand .mark{grid-row:1/3;display:inline-block;width:24px;height:24px;border-radius:7px;
13
+ background:var(--accent-fill)}
12
14
  .brand small{display:block;color:var(--ink-faint);font-weight:400;font-size:11.5px;
13
- letter-spacing:.02em;margin-top:2px;margin-left:16px}
14
- .refreshall{display:flex;align-items:center;gap:8px;width:calc(100% - 24px);margin:0 12px 4px;
15
- background:var(--panel-2);border:1px solid var(--line);color:var(--ink-faint);
16
- border-radius:7px;padding:7px 10px;cursor:pointer;font:11px var(--sans);text-align:left}
17
- .refreshall:hover{color:var(--ink);border-color:var(--accent-dim)}
15
+ letter-spacing:0;margin-top:2px;overflow:hidden;text-overflow:ellipsis;
16
+ white-space:nowrap}
17
+ .refreshall{display:flex;align-items:center;gap:8px;width:100%;margin:0 0 12px;
18
+ background:var(--hover);border:1px solid transparent;color:var(--ink-dim);
19
+ border-radius:8px;padding:6px 10px;cursor:pointer;font:11.5px var(--sans);text-align:left;
20
+ transition:color .15s var(--ease),background .15s var(--ease)}
21
+ .refreshall:hover{color:var(--ink);background:var(--active)}
18
22
  .refreshall .sync{display:inline-flex;--ic:13px;flex:none}
19
23
  .refreshall.spin .sync{animation:spin .9s linear infinite}
20
24
 
21
- .sect{padding:14px 20px 6px;font:600 10.5px var(--sans);letter-spacing:.09em;
25
+ .sect{padding:16px 10px 6px;font:600 11px var(--sans);letter-spacing:.08em;
22
26
  text-transform:uppercase;color:var(--ink-faint)}
23
27
  /* The Staff heading is also the fold for the list under it, so it is a button — and has to
24
- go on reading as a heading rather than as a control. The caret and the 20px left padding
28
+ go on reading as a heading rather than as a control. The caret and the left padding
25
29
  are the staff rows' own, so the two carets line up in one column. */
26
30
  .sectfold{display:flex;align-items:center;gap:7px;width:100%;background:none;border:0;
27
- cursor:pointer;text-align:left;font:600 10.5px var(--sans);letter-spacing:.09em;
28
- text-transform:uppercase;color:var(--ink-faint)}
31
+ border-radius:6px;cursor:pointer;text-align:left;font:600 11px var(--sans);letter-spacing:.08em;
32
+ text-transform:uppercase;color:var(--ink-faint);margin-top:12px;
33
+ transition:color .15s var(--ease)}
29
34
  .sectfold:hover{color:var(--ink-dim)}
30
- .sectfold .caret{display:inline-flex;--ic:12px;flex:none;transition:transform .12s ease}
35
+ .sectfold .caret{display:inline-flex;--ic:12px;flex:none;transition:transform .15s var(--ease)}
31
36
  .sectfold[aria-expanded="false"] .caret{transform:rotate(-90deg)}
32
37
  /* The count is a number, not a heading: mono, and none of the heading's letterspacing. */
33
- .sectfold .n{font:10.5px var(--mono);letter-spacing:0;text-transform:none;color:var(--ink-faint)}
38
+ .sectfold .n{font:10.5px var(--mono);letter-spacing:0;text-transform:none;color:var(--ink-faint);
39
+ font-variant-numeric:tabular-nums}
34
40
  /* Flex, not block with a float. The icons are block-level SVGs and the trailing count has to
35
41
  sit hard right; `float` does neither of those inside a flex row, and did neither of them
36
42
  outside one once the icons arrived. */
37
- .nav{display:flex;align-items:center;width:100%;text-align:left;background:none;border:0;
38
- color:var(--ink-dim);font:14px var(--sans);padding:7px 20px;cursor:pointer;
39
- border-left:2px solid transparent}
40
- .nav:hover{color:var(--ink);background:var(--panel-2)}
41
- .nav[aria-current="true"]{color:var(--ink);border-left-color:var(--accent);background:var(--panel-2)}
42
- .nav .ic{color:var(--ink-faint);margin-right:9px;--ic:15px;flex:none}
43
- .nav .n{margin-left:auto;padding-left:10px;color:var(--ink-faint);font:11px var(--mono)}
44
- .nav.top{margin-bottom:2px}
43
+ .nav{position:relative;display:flex;align-items:center;width:100%;text-align:left;background:none;
44
+ border:0;border-radius:7px;color:var(--ink-dim);font:500 13.5px var(--sans);padding:6px 10px;
45
+ cursor:pointer;min-height:32px;
46
+ transition:background .15s var(--ease),color .15s var(--ease)}
47
+ .nav:hover{color:var(--ink);background:var(--hover)}
48
+ .nav:focus-visible{box-shadow:0 0 0 2px var(--accent-soft),inset 0 0 0 1px var(--accent-dim)}
49
+ .nav[aria-current="true"]{color:var(--ink);background:var(--sel)}
50
+ .nav .ic{color:var(--ink-faint);margin-right:10px;--ic:15px;flex:none;
51
+ transition:color .15s var(--ease)}
52
+ .nav:hover .ic{color:var(--ink-dim)}
53
+ .nav[aria-current="true"] .ic{color:var(--accent)}
54
+ .nav .n{margin-left:auto;padding-left:10px;color:var(--ink-faint);font:500 10.5px var(--mono);
55
+ font-variant-numeric:tabular-nums}
56
+ .nav .n:not(:empty):not(.wait){padding:1px 7px;border-radius:999px;margin-left:auto;
57
+ background:var(--sel);color:var(--ink-dim);min-width:20px;
58
+ text-align:center}
59
+ .nav .n.wait{margin-left:auto}
60
+ .nav.top{margin-bottom:0}
45
61
 
46
62
  /* Staff are rows that unfold into their own views, rather than a dropdown plus a list you
47
63
  have to connect for yourself. The nesting is the hierarchy, said out loud. */
48
- .staffrow{display:flex;align-items:center;gap:7px;font:13.5px var(--sans);color:var(--ink);
49
- padding-right:14px}
50
- .staffrow .caret{color:var(--ink-faint);--ic:12px;flex:none;transition:transform .12s ease}
64
+ .staffrow{display:flex;align-items:center;gap:7px;font:500 13px var(--sans);color:var(--ink);
65
+ padding-right:10px}
66
+ .staffrow .caret{color:var(--ink-faint);--ic:12px;flex:none;transition:transform .15s var(--ease)}
51
67
  .staffrow[aria-expanded="false"] .caret{transform:rotate(-90deg)}
52
68
  .staffrow .t{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
53
69
  .staffrow .hh{font:10.5px var(--mono);color:var(--ink-faint);flex:none}
54
70
  .staffrow[aria-selected="true"]{color:var(--ink)}
55
- .staffviews{padding-bottom:4px}
56
- /* `subnav`, not `sub`: `.sub` is the page subtitle and carries a 24px bottom margin, which
71
+ .staffviews{position:relative;padding:2px 0 6px;margin-left:12px}
72
+ /* `subnav`, not `sub`: `.sub` is the page subtitle and carries a bottom margin, which
57
73
  these rows inherited as a gap you could park a car in. */
58
- .nav.subnav{padding:5px 20px 5px 40px;font-size:13px}
74
+ .nav.subnav{padding:4px 10px 4px 16px;font-size:12.5px;min-height:28px}
59
75
  .nav.subnav .n{font-size:10px}
60
- .sidefoot{margin-top:auto;padding-top:14px}
76
+ .sidefoot{margin-top:auto;padding-top:14px;border-top:1px solid var(--line)}
61
77
  .sidefoot .nav{font-size:12.5px;color:var(--ink-faint)}
62
78
  .sidefoot .nav:hover{color:var(--ink-dim)}
63
79
 
64
80
  /* ---------- main ---------- */
65
- main{padding:28px 34px 80px;max-width:1180px;overflow-x:hidden}
81
+ /* clip, not hidden: hidden makes main a scroll container, and nothing inside it can be sticky. */
82
+ main{padding:36px 44px 96px;max-width:1220px;overflow-x:clip}
66
83
 
67
84
  /* ---------- tree beside viewer ---------- */
68
85
  .split{display:grid;grid-template-columns:314px 1fr;gap:16px;align-items:start}
69
86
  @media (max-width:900px){.split{grid-template-columns:1fr}}
70
- .tree{background:var(--panel);border:1px solid var(--line);border-radius:9px;padding:9px;
71
- max-height:74vh;overflow:auto}
72
- .tdir{font:600 11px var(--mono);color:var(--ink-faint);padding:8px 8px 4px;letter-spacing:.04em}
73
- .tdir.first{padding-top:2px}
87
+ /* The list beside a viewer stays in view while a long file or thread scrolls. */
88
+ .tree{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);padding:6px;
89
+ max-height:calc(100vh - 40px);overflow:auto;box-shadow:var(--shadow-sm);position:sticky;top:20px}
90
+ .tdir{font:600 11px var(--mono);color:var(--ink-faint);padding:9px 8px 4px;letter-spacing:.02em}
91
+ .tdir.first{padding-top:3px}
74
92
  .tfile{display:flex;align-items:baseline;gap:10px;width:100%;text-align:left;background:none;
75
- border:0;color:var(--ink-dim);font:12.5px var(--mono);padding:4px 8px;border-radius:5px;
76
- cursor:pointer}
77
- .tfile:hover{background:var(--panel-2);color:var(--ink)}
78
- .tfile[aria-current="true"]{background:var(--panel-2);color:var(--accent)}
93
+ border:0;color:var(--ink-dim);font:12.5px var(--mono);padding:5px 8px;border-radius:6px;
94
+ cursor:pointer;transition:background .12s var(--ease),color .12s var(--ease)}
95
+ .tfile:hover{background:var(--hover);color:var(--ink)}
96
+ .tfile[aria-current="true"]{background:var(--accent-soft);color:var(--accent)}
79
97
  .tfile span.t{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
80
98
  .tfile i{color:var(--ink-faint);font-style:normal;font-size:10.5px;flex:none;
81
99
  font-variant-numeric:tabular-nums}
82
100
  .tfile:focus{outline:none}
83
- .tfile:focus-visible{outline:2px solid var(--accent-dim);outline-offset:-2px}
101
+ .tfile:focus-visible{box-shadow:inset 0 0 0 1px var(--accent-dim)}
84
102
 
85
103
  /* A search result: the page, and the lines the words were actually found in. Without the lines
86
104
  this is a filter on titles, which is the thing that was not good enough. */
@@ -93,8 +111,9 @@ main{padding:28px 34px 80px;max-width:1180px;overflow-x:hidden}
93
111
  .thit .hitline em{font-style:normal;color:var(--ink);background:color-mix(in srgb,var(--accent) 22%,transparent);
94
112
  border-radius:3px;padding:0 2px}
95
113
 
96
- .viewer{background:var(--panel);border:1px solid var(--line);border-radius:9px;padding:20px 22px;
97
- min-height:340px;max-height:74vh;overflow:auto}
114
+ /* No height cap: a file grows the page, so there is one scrollbar, not one inside another. */
115
+ .viewer{background:var(--panel);border:1px solid var(--line);border-radius:var(--r-lg);padding:22px 26px;
116
+ min-height:340px;min-width:0;box-shadow:var(--shadow-sm)}
98
117
  .viewer pre{margin:0;white-space:pre-wrap;word-break:break-word;font:12.5px/1.65 var(--mono);color:var(--ink-dim)}
99
118
  /* Source keeps its own line breaks and scrolls, rather than wrapping into soup. */
100
119
  .viewer pre.code{white-space:pre;word-break:normal;overflow-x:auto}
@@ -105,12 +124,14 @@ main{padding:28px 34px 80px;max-width:1180px;overflow-x:hidden}
105
124
  .viewer audio.player{background:none}
106
125
  .viewer table{border-collapse:collapse;font:12px var(--mono);width:100%}
107
126
  .viewer td,.viewer th{border:1px solid var(--line);padding:4px 8px;text-align:left;white-space:nowrap}
108
- .viewer th{position:sticky;top:0;background:var(--panel-2);color:var(--ink)}
127
+ .viewer th{position:sticky;top:0;background:var(--panel-2);color:var(--ink);font-weight:600}
109
128
 
110
129
  .gallery{display:grid;gap:10px;grid-template-columns:repeat(auto-fill,minmax(150px,1fr))}
111
130
  .gallery figure{margin:0;position:relative}
112
131
  .gallery img,.gallery video{width:100%;aspect-ratio:4/3;object-fit:contain;background:var(--panel-2);
113
- border-radius:6px;border:1px solid var(--line);cursor:pointer;display:block}
132
+ border-radius:var(--r);border:1px solid var(--line);cursor:pointer;display:block;
133
+ transition:border-color .15s var(--ease),transform .15s var(--ease)}
134
+ .gallery img:hover,.gallery video:hover{border-color:var(--border-strong)}
114
135
  /* A still frame and a photograph look identical in a grid, so the video says which it is. */
115
136
  .playmark{position:absolute;top:8px;right:8px;display:inline-flex;padding:4px;border-radius:50%;
116
137
  background:color-mix(in srgb,#000 55%,transparent);color:#fff;--ic:12px;pointer-events:none}
@@ -129,3 +150,26 @@ main{padding:28px 34px 80px;max-width:1180px;overflow-x:hidden}
129
150
  .side{position:static;height:auto;border-right:none;border-bottom:1px solid var(--line)}
130
151
  .sidefoot{margin-top:0}
131
152
  }
153
+
154
+ /* A staff member who cannot run yet (readiness.js). */
155
+ .readydot{width:7px;height:7px;border-radius:50%;background:var(--warn);flex:none;margin-left:4px}
156
+ .staffcard.notready{border-color:color-mix(in srgb,var(--warn) 45%,var(--line))}
157
+ .hirecard{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:6px;min-height:120px;
158
+ border:1.5px dashed var(--border-strong);border-radius:var(--r-lg);background:none;color:var(--ink-dim);
159
+ font:600 14px var(--sans);cursor:pointer}
160
+ .hirecard:hover{color:var(--ink);border-color:var(--accent)}
161
+ .hirecard::before{content:"+";font-size:22px;line-height:1}
162
+
163
+ /* The inbox toolbar: one height, one line. */
164
+ .inboxbar{align-items:center}
165
+ .inboxbar > *{height:34px;box-sizing:border-box}
166
+ .inboxbar .modes{margin:0;padding:3px;align-items:stretch}
167
+ .inboxbar .mode{display:flex;align-items:center}
168
+ .inboxbar .iconbtn{width:34px;display:grid;place-items:center}
169
+ .inboxbar .ghbtn{display:flex;align-items:center}
170
+ /* Staff and issue icons in the sidebar. */
171
+ .nav.inboxlink{display:flex;align-items:center;gap:8px}
172
+ .nav.inboxlink .ic,.staffrow .ic{--ic:14px;color:var(--ink-faint);flex:none}
173
+ .nav.inboxlink[aria-current="true"] .ic{color:var(--accent)}
174
+ /* The unread count matches the unread rows. Empty, it stays grey like every other badge. */
175
+ #unreadcount:not(:empty):not(.wait){background:var(--blue);color:#fff}
@@ -1,34 +1,37 @@
1
1
  /* One renderer, two sizes: .cbody is a comment in a thread, .doc is a file being read.
2
2
  Everything shared lives on .md so a fix reaches both. */
3
3
 
4
- .md{line-height:1.65}
4
+ .md{line-height:1.7}
5
5
  .md>:first-child{margin-top:0}
6
6
  .md p{margin:0 0 10px}
7
7
  .md h1,.md h2,.md h3,.md h4,.md h5,.md h6{color:var(--ink);font-weight:600;line-height:1.3;
8
- margin:22px 0 8px;letter-spacing:-.012em}
9
- .md h1{font-size:20px} .md h2{font-size:17px} .md h3{font-size:15px}
8
+ margin:26px 0 10px;letter-spacing:-.02em}
9
+ .md h1{font-size:22px} .md h2{font-size:17.5px} .md h3{font-size:15px}
10
10
  .md h4,.md h5,.md h6{font-size:13.5px;color:var(--ink-dim)}
11
- .md h1,.md h2{padding-bottom:6px;border-bottom:1px solid var(--line)}
11
+ .md h1,.md h2{padding-bottom:8px;border-bottom:1px solid var(--line)}
12
12
  .md ul,.md ol{margin:0 0 10px;padding-left:20px}
13
13
  .md li{margin:3px 0}
14
14
  .md li>ul,.md li>ol{margin:4px 0 0}
15
15
  .md ul.tasks{list-style:none;padding-left:1px}
16
16
  .md .box{color:var(--ink-faint);margin-right:6px;display:inline-flex;--ic:14px;
17
17
  vertical-align:-3px}
18
- .md code{font:12px var(--mono);color:var(--accent);background:var(--panel-2);
19
- padding:1px 4px;border-radius:4px;word-break:break-word}
20
- .md pre{background:var(--panel-2);border:1px solid var(--line);border-radius:7px;
21
- padding:11px 13px;font:12px/1.62 var(--mono);margin:10px 0;color:var(--ink-dim);
18
+ .md code{font:12px var(--mono);color:var(--ink);background:color-mix(in srgb,var(--ink) 7%,transparent);
19
+ padding:1px 5px;border-radius:5px;word-break:break-word}
20
+ .md pre{background:var(--surface-2);border:0;border-radius:var(--r);
21
+ padding:13px 15px;font:12px/1.62 var(--mono);margin:10px 0;color:var(--ink-dim);
22
22
  overflow-x:auto;white-space:pre}
23
- .md pre code{background:none;padding:0;color:inherit;font-size:inherit}
24
- .md a{color:var(--ruled)}
25
- .md blockquote{margin:10px 0;padding:2px 0 2px 12px;border-left:2px solid var(--line);color:var(--ink-dim)}
23
+ .md pre code{background:none;border:0;padding:0;color:inherit;font-size:inherit}
24
+ .md a{color:var(--accent);text-decoration:underline;text-decoration-color:var(--accent-dim);
25
+ text-underline-offset:3px;text-decoration-thickness:1px}
26
+ .md a:hover{text-decoration-color:var(--accent)}
27
+ .md blockquote{margin:12px 0;padding:2px 0 2px 14px;border-left:3px solid var(--line);color:var(--ink-dim)}
26
28
  .md hr{border:0;border-top:1px solid var(--line);margin:16px 0}
27
29
  .md img{max-width:100%;border-radius:6px;background:var(--panel-2)}
28
30
  .md .ctable{overflow-x:auto;margin:10px 0;max-width:100%}
29
31
  .md table{border-collapse:collapse;font-size:12.5px;width:100%}
30
- .md th,.md td{border:1px solid var(--line);padding:6px 9px;text-align:left;vertical-align:top}
31
- .md th{background:var(--panel-2);color:var(--ink);font-weight:600;white-space:nowrap}
32
+ .md th,.md td{border:1px solid var(--line);padding:7px 10px;text-align:left;vertical-align:top}
33
+ .md th{background:var(--panel-2);color:var(--ink);font-weight:600;white-space:nowrap;
34
+ font-size:11.5px;letter-spacing:.02em}
32
35
  .md td{color:var(--ink-dim)}
33
36
  .md td b,.md td strong{color:var(--ink)}
34
37
  .cbody{margin-top:8px;font-size:13.5px;max-width:74ch}
@@ -70,5 +73,24 @@ pre.yaml{color:var(--ink)}
70
73
  Bordered and inset so it reads as a figure rather than the page bleeding into another one:
71
74
  these are pictures of this same UI, and without a frame the two run together. */
72
75
  .md img{display:block;max-width:100%;height:auto;margin:14px 0;
73
- border:1px solid var(--line);border-radius:8px}
76
+ border:1px solid var(--line);border-radius:var(--r);box-shadow:var(--shadow)}
74
77
  .md img+em,.md p>img+em{display:block}
78
+
79
+ /* ---------- highlighted editors (mdedit.js) ---------- */
80
+
81
+ .hled{position:relative}
82
+ .hled .hlback{position:absolute;inset:0;margin:0;pointer-events:none;overflow:hidden;
83
+ white-space:pre-wrap;overflow-wrap:break-word;word-break:normal;color:var(--ink);
84
+ border-color:transparent !important;background:transparent !important}
85
+ .hled .hlfront{position:relative;color:transparent;background:transparent;caret-color:var(--ink);
86
+ -webkit-text-fill-color:transparent}
87
+ .hled .hlfront::selection{background:var(--accent-soft)}
88
+ .mdh{color:var(--ruled);font-weight:700}
89
+ .mdb{font-weight:700}
90
+ .mdi{font-style:italic}
91
+ .mdc,.mdcode,.mdfence{color:var(--derived)}
92
+ .mdq{color:var(--ink-dim);font-style:italic}
93
+ .mdl{color:var(--accent)}
94
+ .mdk{color:var(--accent)}
95
+ .mdu{color:var(--ink-faint)}
96
+ .mdt{color:var(--ruled)}
@@ -0,0 +1,13 @@
1
+ /* The Runs screen: a spend strip, then one table per staff member. */
2
+
3
+ .runsum{margin-bottom:6px}
4
+ .hsect i.warn{color:var(--warn)}
5
+ .runtable{border-collapse:collapse;width:100%;margin-top:8px;font:12px var(--mono);
6
+ font-variant-numeric:tabular-nums}
7
+ .runtable th{text-align:left;font:600 10.5px var(--sans);letter-spacing:.06em;text-transform:uppercase;
8
+ color:var(--ink-faint);padding:6px 10px 6px 0;border-bottom:1px solid var(--line)}
9
+ .runtable td{padding:7px 10px 7px 0;border-bottom:1px solid var(--line);color:var(--ink-dim);
10
+ white-space:nowrap}
11
+ .runtable td:last-child,.runtable th:last-child{text-align:right;padding-right:0}
12
+ /* The table is wider than a phone; it scrolls inside its section, never the page. */
13
+ .hbody.runs{overflow-x:auto}