@bongos/core 1.19.456 → 1.19.458
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.
- package/.bongos-core.json +40 -30
- package/docs/copy-inventory.md +160 -177
- package/docs/copy-registry.json +445 -606
- package/docs/file-map.md +1 -1
- package/docs/module-api-changelog.md +4 -0
- package/modules/hall-ui/public/gate.css +20 -88
- package/modules/hall-ui/public/gate.html +24 -12
- package/modules/hall-ui/public/gate.js +77 -60
- package/modules/hall-ui/public/gate.states.json +19 -1
- package/modules/hall-ui/public/government.css +96 -154
- package/modules/hall-ui/public/government.html +80 -52
- package/modules/hall-ui/public/government.js +227 -136
- package/modules/hall-ui/public/government.states.json +71 -1
- package/modules/hall-ui/public/harbor.css +9 -120
- package/modules/hall-ui/public/harbor.html +23 -11
- package/modules/hall-ui/public/harbor.js +109 -83
- package/modules/hall-ui/public/harbor.states.json +19 -1
- package/modules/hall-ui/public/oversight.css +296 -0
- package/modules/hall-ui/public/watch.css +56 -170
- package/modules/hall-ui/public/watch.html +48 -37
- package/modules/hall-ui/public/watch.js +363 -298
- package/modules/hall-ui/public/watch.states.json +48 -1
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/src/module-api.js +1 -1
- package/tests/hall_oversight_world.mjs +118 -0
|
@@ -0,0 +1,296 @@
|
|
|
1
|
+
/* oversight.css — the oversight family's archetype (task 1003469, goal
|
|
2
|
+
1000081): what a oversight ROW is in the chrome world, and the pieces the
|
|
3
|
+
four pages (government, watch, harbor, gate) share around it — the figure
|
|
4
|
+
band inside a panel, the group head, the inline form, the grant toggle, the
|
|
5
|
+
confirm modal. The third family sheet after record.css and room.css: a page
|
|
6
|
+
sheet holds only what one page has and the others do not.
|
|
7
|
+
|
|
8
|
+
Every panel is the shell's .scroll; every heading its .scroll__h; every state
|
|
9
|
+
pill its .fact-pill; every table its .ledger. This sheet adds nothing the
|
|
10
|
+
shell already says, and spends no colour the fifteen do not resolve. */
|
|
11
|
+
|
|
12
|
+
/* ---- the figure band, inside a panel ---------------------------------------
|
|
13
|
+
The record family's band (.rec-figs), restated for this family: hairline
|
|
14
|
+
columns, the figure in the display face at 300, the label above it. A glass
|
|
15
|
+
.stat-row is the TOP-of-page strip (Watch's attention row, Harbor and Gate's
|
|
16
|
+
summaries); this is the figure row a panel carries under its own heading. */
|
|
17
|
+
.ov-figs {
|
|
18
|
+
display: grid;
|
|
19
|
+
grid-template-columns: repeat(auto-fit, minmax(132px, 1fr));
|
|
20
|
+
gap: 0;
|
|
21
|
+
margin: 0 0 var(--sp-sm);
|
|
22
|
+
padding: 0 0 var(--sp-sm);
|
|
23
|
+
border-bottom: var(--line) solid var(--rule-soft);
|
|
24
|
+
}
|
|
25
|
+
.ov-fig { padding: 4px 18px 0; border-left: var(--line) solid var(--rule-soft); min-width: 0; }
|
|
26
|
+
.ov-fig:first-child { border-left: 0; padding-left: 0; }
|
|
27
|
+
.ov-fig__l {
|
|
28
|
+
display: block;
|
|
29
|
+
font-family: var(--label-face);
|
|
30
|
+
font-size: var(--label-size);
|
|
31
|
+
letter-spacing: var(--label-space);
|
|
32
|
+
text-transform: var(--label-case);
|
|
33
|
+
color: var(--ink-faint);
|
|
34
|
+
margin-bottom: 8px;
|
|
35
|
+
white-space: nowrap;
|
|
36
|
+
overflow: hidden;
|
|
37
|
+
text-overflow: ellipsis;
|
|
38
|
+
}
|
|
39
|
+
.ov-fig__v {
|
|
40
|
+
display: block;
|
|
41
|
+
font-family: var(--font-display);
|
|
42
|
+
font-weight: 300;
|
|
43
|
+
font-size: clamp(22px, 2.2vw, 30px);
|
|
44
|
+
line-height: 1;
|
|
45
|
+
letter-spacing: -0.03em;
|
|
46
|
+
color: var(--ink);
|
|
47
|
+
font-variant-numeric: tabular-nums;
|
|
48
|
+
}
|
|
49
|
+
.ov-fig__s { display: block; margin-top: 6px; font-size: 12px; color: var(--ink-faint); }
|
|
50
|
+
@media (max-width: 480px) {
|
|
51
|
+
.ov-figs { grid-template-columns: 1fr 1fr; row-gap: 16px; }
|
|
52
|
+
.ov-fig:nth-child(odd) { border-left: 0; padding-left: 0; }
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* ---- the group head ---------------------------------------------------------
|
|
56
|
+
A named run of rows inside a panel (the mock's .group__head): the name in the
|
|
57
|
+
label voice, the count in mono, one full rule under. The first row after a
|
|
58
|
+
head drops its own hairline, since the head already drew one. */
|
|
59
|
+
.ov-group { margin: var(--sp-md) 0 0; }
|
|
60
|
+
.ov-group:first-child, .scroll__lede + .ov-group, .ov-figs + .ov-group { margin-top: 0; }
|
|
61
|
+
.ov-group__head {
|
|
62
|
+
display: flex;
|
|
63
|
+
align-items: baseline;
|
|
64
|
+
gap: 12px;
|
|
65
|
+
padding: 0 0 10px;
|
|
66
|
+
border-bottom: var(--line) solid var(--rule);
|
|
67
|
+
}
|
|
68
|
+
.ov-group__name {
|
|
69
|
+
font-family: var(--label-face);
|
|
70
|
+
font-size: var(--label-size);
|
|
71
|
+
letter-spacing: var(--label-space);
|
|
72
|
+
text-transform: var(--label-case);
|
|
73
|
+
color: var(--ink);
|
|
74
|
+
}
|
|
75
|
+
.ov-group__n { font-family: var(--font-mono); font-size: 12px; font-variant-numeric: tabular-nums; color: var(--ink-faint); }
|
|
76
|
+
.ov-group__note { margin-left: auto; font-size: 12.5px; color: var(--ink-faint); text-align: right; }
|
|
77
|
+
|
|
78
|
+
/* ---- the oversight row -----------------------------------------------------
|
|
79
|
+
The goals row (.gr-row) at this family's density: one hairline on top, the
|
|
80
|
+
title at 15px/500, an optional one-line sub, a facts line of pills, chips and
|
|
81
|
+
plain facts, and the actions on the right. The rule sits on top so the first
|
|
82
|
+
row starts flush against the panel, or against the group head that already
|
|
83
|
+
drew its rule. Two columns; under 640px the right column drops beneath. */
|
|
84
|
+
.ov-rows { margin: 0; padding: 0; list-style: none; }
|
|
85
|
+
.ov-row {
|
|
86
|
+
display: grid;
|
|
87
|
+
grid-template-columns: minmax(0, 1fr) auto;
|
|
88
|
+
gap: 10px 24px;
|
|
89
|
+
align-items: start;
|
|
90
|
+
padding: 14px 0;
|
|
91
|
+
border-top: var(--line) solid var(--rule-soft);
|
|
92
|
+
}
|
|
93
|
+
.ov-rows > .ov-row:first-child, .ov-group__head + .ov-rows > .ov-row:first-child, .ov-group__head + .ov-row { border-top: 0; }
|
|
94
|
+
.ov-row--lead { grid-template-columns: auto minmax(0, 1fr) auto; }
|
|
95
|
+
/* A dense row: a permission, a voter, a machine principal — sixty of them in
|
|
96
|
+
one panel is the ordinary case, so the air tightens. */
|
|
97
|
+
.ov-row--dense { padding: 10px 0; }
|
|
98
|
+
/* The parts are divs in a list row and spans in a button row (a button holds
|
|
99
|
+
phrasing content only), so each part states its own display. */
|
|
100
|
+
.ov-row__main { display: block; min-width: 0; }
|
|
101
|
+
.ov-row__title {
|
|
102
|
+
display: inline-block;
|
|
103
|
+
font-size: 15px;
|
|
104
|
+
font-weight: 500;
|
|
105
|
+
letter-spacing: -0.01em;
|
|
106
|
+
line-height: 1.35;
|
|
107
|
+
color: var(--ink);
|
|
108
|
+
text-decoration: none;
|
|
109
|
+
overflow-wrap: anywhere;
|
|
110
|
+
}
|
|
111
|
+
/* A title that is a link is a control and owes the 24px floor on both axes:
|
|
112
|
+
the padding buys the box, the equal negative margin hands the space back. */
|
|
113
|
+
a.ov-row__title { min-height: 24px; min-width: 24px; padding: 2px 4px; margin: -2px -4px; }
|
|
114
|
+
a.ov-row__title:hover { color: var(--accent-ink); text-decoration: underline; text-underline-offset: 3px; }
|
|
115
|
+
.ov-row__title .ov-mono { margin-left: 8px; }
|
|
116
|
+
.ov-row__sub { display: block; margin: 4px 0 0; font-size: 13.5px; line-height: 1.5; color: var(--ink-soft); max-width: var(--measure); overflow-wrap: anywhere; }
|
|
117
|
+
.ov-row__facts { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; margin: 10px 0 0; }
|
|
118
|
+
.ov-row__sub + .ov-row__facts { margin-top: 8px; }
|
|
119
|
+
.ov-row__right { display: flex; flex-direction: column; align-items: flex-end; gap: 8px; text-align: right; }
|
|
120
|
+
.ov-row__actions { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; }
|
|
121
|
+
.ov-row__when { font-size: 12.5px; color: var(--ink-faint); font-variant-numeric: tabular-nums; white-space: nowrap; }
|
|
122
|
+
@media (max-width: 640px) {
|
|
123
|
+
.ov-row { grid-template-columns: minmax(0, 1fr); }
|
|
124
|
+
.ov-row--lead { grid-template-columns: auto minmax(0, 1fr); }
|
|
125
|
+
.ov-row--lead > .ov-row__right { grid-column: 2; }
|
|
126
|
+
.ov-row__right { align-items: flex-start; text-align: left; }
|
|
127
|
+
.ov-row__actions { justify-content: flex-start; }
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/* A row that is itself the control — the rank list, where picking a row is the
|
|
131
|
+
page's one selection. The world marks a current thing with its tint (the
|
|
132
|
+
ranks ladder's --current card), never with a coloured bar. */
|
|
133
|
+
button.ov-row {
|
|
134
|
+
width: 100%;
|
|
135
|
+
text-align: left;
|
|
136
|
+
font: inherit;
|
|
137
|
+
color: inherit;
|
|
138
|
+
background: none;
|
|
139
|
+
border-left: 0; border-right: 0; border-bottom: 0;
|
|
140
|
+
border-radius: 0;
|
|
141
|
+
cursor: pointer;
|
|
142
|
+
}
|
|
143
|
+
button.ov-row:hover { background: var(--glass); }
|
|
144
|
+
button.ov-row[aria-pressed="true"] { background: var(--accent-tint); border-top-color: transparent; border-radius: var(--radius-sm); }
|
|
145
|
+
button.ov-row[aria-pressed="true"] + button.ov-row { border-top-color: transparent; }
|
|
146
|
+
button.ov-row[aria-pressed="true"] .ov-row__title { color: var(--accent-ink); }
|
|
147
|
+
.ov-rows--select button.ov-row { padding-left: 12px; padding-right: 12px; margin: 0 -12px; width: calc(100% + 24px); }
|
|
148
|
+
|
|
149
|
+
/* ---- the top-of-page summary strip ----------------------------------------------
|
|
150
|
+
The shell's glass .stat-row, given the air a panel would have under it. */
|
|
151
|
+
.ov-summary { margin: 0 0 var(--sp-md); }
|
|
152
|
+
|
|
153
|
+
/* ---- plain facts and the machine's words ------------------------------------ */
|
|
154
|
+
.ov-fact { font-size: 12.5px; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
|
|
155
|
+
.ov-fact b { color: var(--ink-soft); font-weight: 500; }
|
|
156
|
+
/* A link inside a fact line is a control at a 12.5px line box: the padding buys
|
|
157
|
+
the 24px floor on both axes and the equal negative margin hands it back. */
|
|
158
|
+
.ov-fact a { display: inline-block; color: var(--accent-ink); min-height: 24px; min-width: 24px; padding: 3px 4px; margin: -3px -4px; line-height: 18px; }
|
|
159
|
+
.ov-mono { font-family: var(--font-mono); font-size: 12px; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
|
|
160
|
+
.ov-key { font-family: var(--font-mono); font-size: 12.5px; color: var(--ink); overflow-wrap: anywhere; }
|
|
161
|
+
.ov-key--faint { color: var(--ink-faint); }
|
|
162
|
+
.ov-danger { color: var(--danger); }
|
|
163
|
+
.ov-ok { color: var(--ok); }
|
|
164
|
+
|
|
165
|
+
/* Detail lines under a row: a reason per line (the gate's holds), or a key and
|
|
166
|
+
a value (the harbor's make and cost). Hairlines between, none on the first. */
|
|
167
|
+
.ov-lines { list-style: none; margin: 10px 0 0; padding: 0; max-width: 72ch; }
|
|
168
|
+
.ov-lines li { display: flex; gap: 12px; align-items: baseline; padding: 6px 0; border-top: var(--line) solid var(--rule-soft); font-size: 13px; line-height: 1.45; color: var(--ink-soft); }
|
|
169
|
+
.ov-lines li:first-child { border-top: 0; padding-top: 0; }
|
|
170
|
+
.ov-lines .ov-key { flex: none; max-width: 46%; }
|
|
171
|
+
.ov-lines .ov-lines__v { margin-left: auto; text-align: right; color: var(--ink); font-variant-numeric: tabular-nums; }
|
|
172
|
+
/* On a phone the key takes the line and the value or reason drops under it:
|
|
173
|
+
three narrow columns at 390px read as a table nobody laid out. */
|
|
174
|
+
@media (max-width: 640px) {
|
|
175
|
+
.ov-lines li { flex-wrap: wrap; gap: 2px 10px; }
|
|
176
|
+
.ov-lines .ov-key { max-width: 100%; }
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/* Prose inside a row or a panel — the idea a board votes on, set as prose and
|
|
180
|
+
never as config rows. Each part is a label over a paragraph. */
|
|
181
|
+
.ov-prose { margin: 14px 0 0; max-width: var(--measure); }
|
|
182
|
+
.ov-prose__l {
|
|
183
|
+
margin: 14px 0 4px;
|
|
184
|
+
font-family: var(--label-face);
|
|
185
|
+
font-size: var(--label-size);
|
|
186
|
+
letter-spacing: var(--label-space);
|
|
187
|
+
text-transform: var(--label-case);
|
|
188
|
+
color: var(--ink-faint);
|
|
189
|
+
}
|
|
190
|
+
.ov-prose__l:first-child { margin-top: 0; }
|
|
191
|
+
.ov-prose__p { margin: 0; font-size: 14px; line-height: 1.55; color: var(--ink); white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
192
|
+
|
|
193
|
+
/* ---- the inline form ---------------------------------------------------------
|
|
194
|
+
Label over field, fields in one wrapping row, the button on the baseline. The
|
|
195
|
+
controls themselves are the shell's (hairline pill inputs); this only lays
|
|
196
|
+
them out. A form that explains itself does so in one line under the row. */
|
|
197
|
+
.ov-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 10px 14px; margin: 0 0 var(--sp-sm); }
|
|
198
|
+
.ov-field { display: flex; flex-direction: column; gap: 6px; flex: 1 1 180px; min-width: 0; }
|
|
199
|
+
.ov-field--sm { flex: 0 1 140px; }
|
|
200
|
+
.ov-field--wide { flex: 1 1 100%; }
|
|
201
|
+
.ov-field__l {
|
|
202
|
+
font-family: var(--label-face);
|
|
203
|
+
font-size: 10.5px;
|
|
204
|
+
letter-spacing: var(--label-space);
|
|
205
|
+
text-transform: var(--label-case);
|
|
206
|
+
color: var(--ink-faint);
|
|
207
|
+
}
|
|
208
|
+
/* The shell styles text and search inputs; a number input (the builder id) would
|
|
209
|
+
otherwise render at the browser's 21px and fail the 24px floor, so the field
|
|
210
|
+
restates the shell's hairline pill for every control it holds. */
|
|
211
|
+
.ov-field input, .ov-field select, .ov-field textarea {
|
|
212
|
+
width: 100%;
|
|
213
|
+
box-sizing: border-box;
|
|
214
|
+
min-height: 30px;
|
|
215
|
+
padding: 5px 12px;
|
|
216
|
+
font-family: var(--font-body);
|
|
217
|
+
font-size: 14px;
|
|
218
|
+
color: var(--ink);
|
|
219
|
+
background: none;
|
|
220
|
+
border: var(--line) solid var(--rule);
|
|
221
|
+
border-radius: var(--radius-pill);
|
|
222
|
+
}
|
|
223
|
+
.ov-field textarea { border-radius: var(--radius-sm); }
|
|
224
|
+
.ov-field input:hover, .ov-field select:hover, .ov-field textarea:hover { border-color: var(--ink-faint); }
|
|
225
|
+
.ov-field input::placeholder, .ov-field textarea::placeholder { color: var(--ink-faint); }
|
|
226
|
+
/* The one line under the row. A max-width here would cap the flex basis and let
|
|
227
|
+
the line ride beside the button, so the line takes the full row instead. */
|
|
228
|
+
.ov-form__why { flex: 1 1 100%; min-width: 100%; margin: 0; font-size: 12.5px; color: var(--ink-faint); }
|
|
229
|
+
|
|
230
|
+
/* ---- the grant toggle ---------------------------------------------------------
|
|
231
|
+
Held is a hairline pill in ink; not held is the same pill in the faint ink.
|
|
232
|
+
The accent arrives only on hover, because forty granted permissions painted
|
|
233
|
+
salmon were the loudest thing on the page and none of them was live. */
|
|
234
|
+
.ov-toggle {
|
|
235
|
+
display: inline-flex;
|
|
236
|
+
align-items: center;
|
|
237
|
+
justify-content: center;
|
|
238
|
+
min-height: 28px;
|
|
239
|
+
min-width: 64px;
|
|
240
|
+
padding: 0 12px;
|
|
241
|
+
font: inherit;
|
|
242
|
+
font-size: 12.5px;
|
|
243
|
+
color: var(--ink-faint);
|
|
244
|
+
background: none;
|
|
245
|
+
border: var(--line) solid var(--rule);
|
|
246
|
+
border-radius: var(--radius-pill);
|
|
247
|
+
cursor: pointer;
|
|
248
|
+
white-space: nowrap;
|
|
249
|
+
}
|
|
250
|
+
.ov-toggle[aria-pressed="true"] { color: var(--ink); border-color: var(--ink-soft); }
|
|
251
|
+
.ov-toggle:hover:not(:disabled) { color: var(--accent-ink); border-color: var(--accent); }
|
|
252
|
+
.ov-toggle:disabled { opacity: 0.55; cursor: default; }
|
|
253
|
+
|
|
254
|
+
/* A ghost pill whose action is destructive: the danger ink on the same hairline
|
|
255
|
+
shape, so Close, Dismiss, Deny and Offboard read as the same kind of control
|
|
256
|
+
as their neighbours and still warn. */
|
|
257
|
+
.btn-ghost--danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 45%, var(--rule)); }
|
|
258
|
+
.btn-ghost--danger:hover:not(:disabled) { color: var(--danger); border-color: var(--danger); }
|
|
259
|
+
|
|
260
|
+
/* ---- the foot line ----------------------------------------------------------- */
|
|
261
|
+
.ov-foot { margin: 14px 0 0; font-size: 12.5px; color: var(--ink-faint); max-width: var(--measure); }
|
|
262
|
+
/* A link in the foot line is a control at a 12.5px line box: the padding buys
|
|
263
|
+
the 24px floor and the equal negative margin hands the space back. */
|
|
264
|
+
.ov-foot a { display: inline-block; color: var(--accent-ink); min-height: 24px; min-width: 24px; padding: 3px 4px; margin: -3px -4px; line-height: 18px; }
|
|
265
|
+
|
|
266
|
+
/* ---- the confirm modal ----------------------------------------------------------
|
|
267
|
+
Harbor and Gate share one confirm dialog. A --bg-card panel closed by one rule
|
|
268
|
+
hairline and no shadow (the hub's project card is the reference), on the
|
|
269
|
+
shell's --z-modal layer, over a scrim mixed from the ink. */
|
|
270
|
+
.ov-modal-bg {
|
|
271
|
+
position: fixed;
|
|
272
|
+
inset: 0;
|
|
273
|
+
z-index: var(--z-modal);
|
|
274
|
+
display: flex;
|
|
275
|
+
align-items: center;
|
|
276
|
+
justify-content: center;
|
|
277
|
+
padding: 20px;
|
|
278
|
+
background: color-mix(in srgb, var(--ink) 40%, transparent);
|
|
279
|
+
}
|
|
280
|
+
.ov-modal {
|
|
281
|
+
width: 100%;
|
|
282
|
+
max-width: 480px;
|
|
283
|
+
padding: 22px 24px 24px;
|
|
284
|
+
background: var(--bg-card);
|
|
285
|
+
border: var(--line) solid var(--rule);
|
|
286
|
+
border-radius: var(--radius);
|
|
287
|
+
}
|
|
288
|
+
.ov-modal .scroll__h { margin-bottom: 10px; }
|
|
289
|
+
.ov-modal p { margin: 0 0 12px; font-size: 13.5px; line-height: 1.5; color: var(--ink-soft); }
|
|
290
|
+
.ov-modal p strong, .ov-modal p b { color: var(--ink); font-weight: 500; }
|
|
291
|
+
.ov-modal p:last-of-type { margin-bottom: 0; }
|
|
292
|
+
.ov-modal label:not(.ov-modal__check) { display: block; margin: 14px 0 6px; font-family: var(--label-face); font-size: 10.5px; letter-spacing: var(--label-space); text-transform: var(--label-case); color: var(--ink-faint); }
|
|
293
|
+
.ov-modal input[type="text"], .ov-modal textarea { width: 100%; box-sizing: border-box; }
|
|
294
|
+
.ov-modal__check { display: flex; align-items: flex-start; gap: 8px; margin: 14px 0 0; font-size: 13px; color: var(--ink); }
|
|
295
|
+
.ov-modal__check input { margin-top: 3px; width: 16px; height: 16px; }
|
|
296
|
+
.ov-modal__actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 20px; }
|
|
@@ -1,189 +1,75 @@
|
|
|
1
|
-
/* watch.css — page-local styles for Watch (task
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
/* watch.css — page-local styles for Watch (task 1003469 rebuild; first
|
|
2
|
+
extracted from the page's inline <style> by task 2173). Every list on the page
|
|
3
|
+
is the kit's .ledger, every figure row the oversight family's .ov-figs, every
|
|
4
|
+
sub-section an .ov-group head (oversight.css). What stays here is what only
|
|
5
|
+
Watch has: the severity vocabulary, the engagement-report reader, and the
|
|
6
|
+
grade trend. Class names are load-bearing (watch.js writes them). */
|
|
6
7
|
|
|
7
|
-
/*
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
.
|
|
11
|
-
.
|
|
8
|
+
/* Severity / status pills. Base: the shell's .fact-pill (task 1003307). A
|
|
9
|
+
severity is a state, so it is a hairline in its own colour rather than a
|
|
10
|
+
filled chip — four filled severities in one row was the loudest thing here. */
|
|
11
|
+
.badge--sev-critical { color: var(--danger); }
|
|
12
|
+
.badge--sev-high { color: var(--warn); }
|
|
13
|
+
.badge--sev-medium { color: var(--info); }
|
|
14
|
+
.badge--sev-low { color: var(--ink-faint); }
|
|
15
|
+
.badge--status { color: var(--ink-faint); }
|
|
12
16
|
|
|
13
|
-
/*
|
|
14
|
-
|
|
17
|
+
/* Numeric cells in a kit ledger read right-aligned and tabular; a cell that
|
|
18
|
+
warns takes the danger ink. */
|
|
19
|
+
.ledger th.num, .ledger td.num { text-align: right; font-variant-numeric: tabular-nums; }
|
|
20
|
+
.ledger td.num.warn { color: var(--danger); font-weight: 500; }
|
|
21
|
+
/* Row actions inside a ledger cell: ghost pills in a row. */
|
|
22
|
+
.watch-actions { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
|
|
15
23
|
|
|
16
|
-
.
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
margin-top: 6px;
|
|
21
|
-
}
|
|
22
|
-
.watch-stat {
|
|
23
|
-
background: var(--bg-card);
|
|
24
|
-
border: var(--line) solid var(--rule-soft);
|
|
25
|
-
border-radius: var(--radius-sm);
|
|
26
|
-
padding: 12px 14px;
|
|
27
|
-
text-align: center;
|
|
28
|
-
}
|
|
29
|
-
.watch-stat__num {
|
|
30
|
-
font-family: var(--font-display);
|
|
31
|
-
font-size: 1.5rem;
|
|
32
|
-
font-weight: 650;
|
|
33
|
-
color: var(--ink);
|
|
34
|
-
line-height: 1.2;
|
|
35
|
-
font-variant-numeric: tabular-nums;
|
|
36
|
-
}
|
|
37
|
-
.watch-stat__label {
|
|
38
|
-
font-size: 11px;
|
|
39
|
-
font-weight: 600;
|
|
40
|
-
color: var(--ink-faint);
|
|
41
|
-
text-transform: uppercase;
|
|
42
|
-
letter-spacing: 0.06em;
|
|
43
|
-
margin-top: 4px;
|
|
44
|
-
}
|
|
24
|
+
/* A sealed or failed section says so in place. Three states, three sentences:
|
|
25
|
+
"nothing here" is the kit's empty state, this is "you may not see this" and
|
|
26
|
+
"could not load". */
|
|
27
|
+
.watch-unavailable { margin: 0; padding: 8px 0; font-size: 13.5px; line-height: 1.5; color: var(--ink-soft); max-width: var(--measure); }
|
|
45
28
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
font-
|
|
29
|
+
/* The engagement-report reader: exploit text, escaped into a pre and scrolled
|
|
30
|
+
in its own frame, never injected as HTML (threat-model §6.3). */
|
|
31
|
+
.watch-doc__h {
|
|
32
|
+
margin: var(--sp-sm) 0 6px;
|
|
33
|
+
font-family: var(--label-face);
|
|
34
|
+
font-size: var(--label-size);
|
|
35
|
+
font-weight: 400;
|
|
36
|
+
letter-spacing: var(--label-space);
|
|
37
|
+
text-transform: var(--label-case);
|
|
51
38
|
color: var(--ink);
|
|
52
39
|
}
|
|
53
|
-
table.watch th, table.watch td {
|
|
54
|
-
padding: 6px 10px;
|
|
55
|
-
text-align: left;
|
|
56
|
-
border-bottom: var(--line) solid var(--rule-soft);
|
|
57
|
-
vertical-align: top;
|
|
58
|
-
}
|
|
59
|
-
table.watch th {
|
|
60
|
-
font-size: 11px;
|
|
61
|
-
font-weight: 600;
|
|
62
|
-
text-transform: uppercase;
|
|
63
|
-
letter-spacing: 0.06em;
|
|
64
|
-
color: var(--ink-faint);
|
|
65
|
-
border-bottom: var(--line) solid var(--rule);
|
|
66
|
-
white-space: nowrap;
|
|
67
|
-
}
|
|
68
|
-
table.watch td.num { text-align: right; font-variant-numeric: tabular-nums; }
|
|
69
|
-
table.watch td.warn { color: var(--danger); font-weight: 600; }
|
|
70
|
-
table.watch tr:hover td { background: var(--glass); }
|
|
71
|
-
/* the kit ledger (audit log) shares the same numeric-cell affordances */
|
|
72
|
-
.ledger td.num { text-align: right; font-variant-numeric: tabular-nums; }
|
|
73
|
-
.ledger td.num.warn { color: var(--danger); font-weight: 600; }
|
|
74
|
-
.watch-mono { font-family: var(--font-mono); font-size: 0.84rem; color: var(--info); }
|
|
75
|
-
.watch-empty { color: var(--ink-faint); font-size: 13px; padding: 10px 0; }
|
|
76
|
-
/* .watch-subh is .watch-sub-h without the second hyphen, and watch.js emits
|
|
77
|
-
both (:400/832/857 vs :638/640). The base rule below already groups them, so
|
|
78
|
-
the two spellings LOOK the same — the only real divergence was :first-of-type
|
|
79
|
-
below, which matched one spelling and not the other, so a section whose first
|
|
80
|
-
sub-heading happened to be spelled the other way carried 16px of top margin
|
|
81
|
-
instead of 8. Grouped there too (task 1003307). */
|
|
82
|
-
.watch-note { color: var(--ink-faint); font-size: 12.5px; margin-top: 10px; }
|
|
83
|
-
.watch-rank { font-size: 12px; color: var(--ink-faint); }
|
|
84
40
|
.watch-doc {
|
|
41
|
+
margin: 0;
|
|
42
|
+
padding: 12px 14px;
|
|
43
|
+
max-height: 60vh;
|
|
44
|
+
overflow-y: auto;
|
|
85
45
|
white-space: pre-wrap;
|
|
86
46
|
word-break: break-word;
|
|
87
47
|
font-family: var(--font-mono);
|
|
88
|
-
font-size: 0.8rem;
|
|
89
|
-
line-height: 1.5;
|
|
90
|
-
max-height: 60vh;
|
|
91
|
-
overflow-y: auto;
|
|
92
|
-
padding: 12px;
|
|
93
|
-
margin-top: 8px;
|
|
94
|
-
background: var(--glass);
|
|
95
|
-
border: var(--line) solid var(--rule-soft);
|
|
96
|
-
border-radius: var(--radius-sm);
|
|
97
|
-
}
|
|
98
|
-
.watch-sub-h, .watch-subh {
|
|
99
|
-
font-family: var(--font-display);
|
|
100
|
-
font-size: 14px;
|
|
101
|
-
font-weight: 600;
|
|
102
|
-
color: var(--ink);
|
|
103
|
-
margin: 16px 0 4px;
|
|
104
|
-
}
|
|
105
|
-
.watch-sub-h:first-of-type, .watch-subh:first-of-type { margin-top: 8px; }
|
|
106
|
-
|
|
107
|
-
/* Severity / status badges. */
|
|
108
|
-
/* Base: the shell's .fact-pill (task 1003307). A severity is a state, so it is a
|
|
109
|
-
hairline in its own colour rather than a filled chip — four filled severities
|
|
110
|
-
stacked in one table row was the loudest thing on the Watch board. */
|
|
111
|
-
.badge--sev-critical { color: var(--danger); }
|
|
112
|
-
.badge--sev-high { color: var(--warn); }
|
|
113
|
-
.badge--sev-medium { color: var(--info); }
|
|
114
|
-
.badge--sev-low { color: var(--ink-faint); }
|
|
115
|
-
.badge--status { color: var(--ink-faint); }
|
|
116
|
-
|
|
117
|
-
/* Row action buttons + selects. */
|
|
118
|
-
.watch-actions { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
|
|
119
|
-
.watch-btn {
|
|
120
|
-
font-family: var(--font-body);
|
|
121
48
|
font-size: 12.5px;
|
|
122
|
-
|
|
123
|
-
padding: 4px 12px;
|
|
124
|
-
border: var(--line) solid var(--rule);
|
|
125
|
-
background: var(--bg-card);
|
|
126
|
-
color: var(--ink-soft);
|
|
127
|
-
border-radius: var(--radius-sm);
|
|
128
|
-
cursor: pointer;
|
|
129
|
-
}
|
|
130
|
-
.watch-btn:hover:not(:disabled) { background: var(--glass); color: var(--ink); }
|
|
131
|
-
.watch-btn:disabled { opacity: 0.45; cursor: default; }
|
|
132
|
-
.watch-btn--danger { color: var(--danger); border-color: color-mix(in srgb, var(--danger) 40%, var(--rule)); }
|
|
133
|
-
.watch-select {
|
|
134
|
-
font-family: var(--font-body);
|
|
135
|
-
font-size: 13px;
|
|
136
|
-
padding: 4px 6px;
|
|
137
|
-
border: var(--line) solid var(--rule);
|
|
138
|
-
background: var(--bg-card);
|
|
49
|
+
line-height: 1.5;
|
|
139
50
|
color: var(--ink);
|
|
51
|
+
background: var(--glass);
|
|
140
52
|
border-radius: var(--radius-sm);
|
|
141
53
|
}
|
|
142
54
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
align-items: flex-start; justify-content: space-between;
|
|
148
|
-
}
|
|
149
|
-
.watch-req__who { font-weight: 600; color: var(--ink); }
|
|
150
|
-
.watch-req__meta { color: var(--ink-faint); font-size: 12.5px; margin-top: 2px; }
|
|
151
|
-
.watch-req__note { color: var(--ink); font-size: 13.5px; margin-top: 6px; max-width: 56ch; }
|
|
152
|
-
|
|
153
|
-
.watch-unavailable { color: var(--ink-faint); font-size: 13px; padding: 8px 0; }
|
|
154
|
-
|
|
155
|
-
.watch-trend { display: flex; flex-direction: column; gap: 4px; margin-top: 8px; }
|
|
55
|
+
/* The 14-day grade trend: a day, a bar, then the count and score. The bar is a
|
|
56
|
+
measurement, not a control, so it is ink on a hairline track and never the
|
|
57
|
+
accent. */
|
|
58
|
+
.watch-trend { display: flex; flex-direction: column; margin-top: 4px; }
|
|
156
59
|
.watch-trend__row {
|
|
157
|
-
display: grid;
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
background: var(--glass); border-radius: 3px; height: 12px; overflow: hidden;
|
|
163
|
-
border: var(--line) solid var(--rule-soft);
|
|
164
|
-
}
|
|
165
|
-
.watch-trend__bar-fill { background: var(--accent); height: 100%; display: block; }
|
|
166
|
-
.watch-trend__val { text-align: right; font-variant-numeric: tabular-nums; color: var(--ink-soft); }
|
|
167
|
-
|
|
168
|
-
.watch-list { list-style: none; padding: 0; margin: 8px 0 0; }
|
|
169
|
-
.watch-list li {
|
|
170
|
-
display: flex; justify-content: space-between; gap: 12px;
|
|
171
|
-
padding: 5px 0; border-bottom: var(--line) solid var(--rule-soft);
|
|
60
|
+
display: grid;
|
|
61
|
+
grid-template-columns: 64px minmax(0, 1fr) 96px;
|
|
62
|
+
gap: 12px;
|
|
63
|
+
align-items: center;
|
|
64
|
+
min-height: 28px;
|
|
172
65
|
font-size: 13px;
|
|
66
|
+
color: var(--ink-soft);
|
|
67
|
+
font-variant-numeric: tabular-nums;
|
|
173
68
|
}
|
|
174
|
-
.watch-
|
|
175
|
-
.watch-
|
|
176
|
-
|
|
177
|
-
.watch-foot-link {
|
|
178
|
-
display: inline-block; margin-top: 12px;
|
|
179
|
-
font-size: 13px; font-weight: 500;
|
|
180
|
-
color: var(--accent-ink);
|
|
181
|
-
}
|
|
69
|
+
.watch-trend__bar-track { height: 6px; overflow: hidden; border-radius: var(--radius-pill); background: var(--rule-soft); }
|
|
70
|
+
.watch-trend__bar-fill { display: block; height: 100%; background: var(--ink-soft); }
|
|
71
|
+
.watch-trend__val { text-align: right; color: var(--ink); }
|
|
182
72
|
|
|
183
|
-
/*
|
|
184
|
-
|
|
185
|
-
.
|
|
186
|
-
.watch-invite__label { display: block; font-weight: 600; margin-bottom: 6px; }
|
|
187
|
-
.watch-invite__row { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
|
|
188
|
-
.watch-invite__row input[type="text"] { flex: 1 1 200px; min-width: 0; }
|
|
189
|
-
.watch-invite__why { margin: 8px 0 0; font-size: 13px; opacity: .8; }
|
|
73
|
+
/* A link inside a ledger cell (a session's tasks) is a control at a 13.5px line
|
|
74
|
+
box: the padding buys the 24px floor and the negative margin hands it back. */
|
|
75
|
+
.ledger td a:not(.lb-name) { display: inline-block; min-height: 24px; padding: 3px 0; margin: -3px 0; line-height: 18px; }
|