@bongos/core 1.19.484 → 1.19.485

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.
@@ -14,6 +14,9 @@
14
14
  <link rel="preconnect" href="https://fonts.googleapis.com">
15
15
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
16
16
  <link rel="stylesheet" href="/builders/style.css?v=2026-07-02-shell1">
17
+ <!-- The PANEL archetype (task 1003308) before the page's own sheet. -->
18
+ <link rel="stylesheet" href="/builders/panel.css">
19
+ <link rel="stylesheet" href="/builders/profile.css">
17
20
  </head>
18
21
  <body data-page="profile" data-page-title="Profile">
19
22
 
@@ -23,7 +26,11 @@
23
26
  <div class="app-body">
24
27
  <header class="app-topbar" id="app-topbar"></header>
25
28
  <div class="app-content">
26
- <div class="page-head">
29
+ <!-- The page's ONE h1 (one-header-main-h1), in the family's voice; the
30
+ sub-line is profile.js's — "<name> — <rank> · N works shipped". -->
31
+ <div class="page-head page-head--room page-head--panel">
32
+ <p class="page-head__eyebrow">the page everyone else reads</p>
33
+ <h1 id="profile-h1" class="page-head__h">profile</h1>
27
34
  <p class="page-head__sub" id="profile-sub">Loading profile…</p>
28
35
  </div>
29
36
 
@@ -31,7 +38,7 @@
31
38
  <!-- Standing — avatar, name, rank badge, member-since. Filled by profile.js. -->
32
39
  <section class="scroll" id="standing-scroll" aria-labelledby="standing-h">
33
40
  <h2 id="standing-h" class="sr-only">Standing</h2>
34
- <div class="profile" id="profile-head">
41
+ <div class="profile profile--ident" id="profile-head">
35
42
  <div class="profile__placeholder">…</div>
36
43
  </div>
37
44
  </section>
@@ -92,19 +99,19 @@
92
99
  <div class="profile__placeholder" id="activity-placeholder">…</div>
93
100
 
94
101
  <div id="activity-tasks-panel" hidden>
95
- <h3 class="scroll__h">Works completed</h3>
102
+ <h3 class="profile-act__h">Works completed</h3>
96
103
  <div id="activity-tasks"></div>
97
104
  </div>
98
105
 
99
106
  <div id="activity-goals-panel" hidden>
100
- <h3 class="scroll__h">Goals created</h3>
107
+ <h3 class="profile-act__h">Goals created</h3>
101
108
  <div id="activity-goals"></div>
102
109
  </div>
103
110
 
104
111
  <!-- The idea trail. Revealed only in the craft views where filing an
105
112
  idea is the work being shown (all + ideator) — see profile.js. -->
106
113
  <div id="activity-ideas-panel" hidden>
107
- <h3 class="scroll__h">Ideas filed, and what became of them</h3>
114
+ <h3 class="profile-act__h">Ideas filed, and what became of them</h3>
108
115
  <p class="scroll__lede">Every idea this builder captured — promoted, discarded, merged, or still open.</p>
109
116
  <div id="activity-ideas"></div>
110
117
  </div>
@@ -0,0 +1,15 @@
1
+ {
2
+ "_": "The hall's PROFILE page through modules/ui-design/kit/render.js (task 1003308). The identity band, the shelf and the three activity ledgers only exist once a profile has LOADED, so the floors run against the hall-preview harness (see settings.states.json for the two commands; pass --base-in/--base-out at it). `out` is a real state here rather than a redirect: a signed-out visitor with no ?id= gets the empty state, not a bounce — under the harness's --fixture-me the /me read still answers, so `out` renders the own profile there and the empty state only under the kit's own stub.",
3
+ "page": "/profile",
4
+ "surface": "hall-ui",
5
+ "stub": { "prefix": "/builders/" },
6
+ "modeQuery": false,
7
+ "ignoreRequests": ["/api/(gds|bongos)/"],
8
+ "ignoreConsole": ["/api/(gds|bongos)/", "\b401\b", "\b404\b", "Unauthorized", "Not Found", "load failed"],
9
+ "states": {
10
+ "own": { "auth": true, "actions": [["wait", 900]], "expect": { "visible": ["#profile-h1", "#profile-head", "#numbers-body", "#works-table", "#activity-tabs"] } },
11
+ "other": { "auth": true, "url": "/profile?id=1000025", "actions": [["wait", 900]], "expect": { "visible": ["#profile-head", "#ach-grid"] } },
12
+ "out": { "auth": false, "actions": [["wait", 600]], "expect": { "visible": ["#profile-h1", "#profile-head"] } }
13
+ },
14
+ "reducedMotion": { "auth": true, "actions": [["wait", 900]] }
15
+ }
@@ -1,14 +1,18 @@
1
- /* settings.css — page-local styles for Settings (task 2172; extracted from
2
- settings.html's old inline <style> per the hall placement rules). Components
3
- layered on the shared design system (style.css) — the rail (.section-rail /
4
- .rail-link) and buttons derive from the shared tokens so instance theme
5
- packs reskin this page too. */
1
+ /* settings.css — what Settings has and the rest of the panel family does not
2
+ (task 1003308, goal 1000081; the archetype is panel.css). The grouped layout
3
+ behind the section rail, the group head, the token ladder, the voices grid,
4
+ the volume line and the image-key banner. Every colour is a `var()` of the
5
+ world's tier; the one literal pair is the Discord mark, named below.
6
6
 
7
- /* ---- grouped layout: left sub-nav rail + one visible group (task 1723) ---- */
7
+ The rail itself (.section-rail / .rail-link) is the shell's, built by
8
+ OTBKit.makeSectionRail in switch mode; this sheet only widens its column to
9
+ the mock's and keeps one group visible at a time. */
10
+
11
+ /* ---- grouped layout: left rail + one visible group ------------------------ */
8
12
  .settings-layout {
9
13
  display: grid;
10
- grid-template-columns: 180px minmax(0, 1fr);
11
- gap: 20px;
14
+ grid-template-columns: 206px minmax(0, 1fr);
15
+ gap: 30px;
12
16
  align-items: start;
13
17
  }
14
18
  .settings-layout[hidden] { display: none !important; }
@@ -24,357 +28,136 @@
24
28
  .settings-groups[data-active="sound"] > [data-group="sound"],
25
29
  .settings-groups[data-active="extensions"] > [data-group="extensions"] { display: block; }
26
30
  .settings-groups > [data-group][hidden] { display: none !important; }
31
+ .settings-groups .scroll { margin: 0 0 var(--sp-sm); }
32
+ .settings-groups .scroll__lede { font-size: 14.5px; }
33
+ /* the rail's items at the mock's height: the shell's 28px rows are Standing's
34
+ density, and a settings group is a destination, not a scroll-spy stop */
35
+ #settings-rail .rail-link { min-height: 38px; padding: 0 14px; font-size: 14.5px; text-transform: none; border-radius: var(--radius-pill); }
36
+ #settings-rail .rail-link--active::before { left: 0; top: 7px; bottom: 7px; }
27
37
 
28
- /* task 2172 density pass — standard panel anatomy (title → one-line purpose →
29
- control → save-semantics note) in a tighter card: less padding + stacking
30
- margin than the default .scroll, ledes clamped to one visual line's worth. */
31
- .settings-groups .scroll { padding: 16px 18px; margin: 0 0 12px; }
32
- .settings-groups .scroll__lede { font-size: 13px; margin-bottom: 10px; }
38
+ /* the group head: the group's name in the display voice over a hairline, with
39
+ its own address in the mono face beside it (a machine's word keeps its case) */
40
+ .grp__head {
41
+ display: flex;
42
+ align-items: baseline;
43
+ justify-content: space-between;
44
+ gap: var(--sp-sm);
45
+ margin: 0 0 var(--sp-sm);
46
+ padding-bottom: var(--sp-xs);
47
+ border-bottom: var(--line) solid var(--glass-edge);
48
+ }
49
+ .grp__h { margin: 0; font-family: var(--font-display); font-weight: 300; font-size: clamp(22px, 2.1vw, 29px); line-height: 1.1; letter-spacing: -.03em; color: var(--ink); }
50
+ .grp__eyebrow { font-family: var(--font-mono); font-size: 11px; letter-spacing: 0; text-transform: none; color: var(--ink-faint); }
33
51
 
34
- @media (max-width: 768px) {
52
+ @media (max-width: 860px) {
35
53
  /* Collapse to one column; the shared .section-rail ≤768px rules turn the
36
- rail into a sticky horizontal chip strip just under the top bar. */
37
- .settings-layout { grid-template-columns: minmax(0, 1fr); }
54
+ rail into a horizontal chip strip just under the top bar. */
55
+ .settings-layout { grid-template-columns: minmax(0, 1fr); gap: var(--sp-md); }
38
56
  }
39
-
40
- .settings-status {
41
- font-size: 13px;
42
- color: var(--ink-soft);
43
- min-height: 1.2em;
44
- margin-top: 8px;
45
- }
46
- .section-note {
47
- color: var(--ink-faint);
48
- font-size: 12.5px;
49
- margin-top: 10px;
50
- margin-bottom: 0;
57
+ @media (max-width: 520px) {
58
+ .grp__head { flex-direction: column; align-items: flex-start; gap: 4px; }
51
59
  }
52
60
 
53
- /* ---- Voices (per-skill model picker) ---- */
54
- .voices-scroll { /* keep many rows compact within a bounded, scrollable box */ }
55
- .voices-wrap {
56
- max-height: 460px;
57
- overflow-y: auto;
58
- margin-top: 6px;
59
- border: 1px solid var(--border);
61
+ /* ---- the token ladder (CLI access) ------------------------------------------
62
+ A 64-character token cannot be a line of prose, so it is quoted: one tonal
63
+ step down from the panel, the machine's words receding to the soft ink so the
64
+ secret itself is the only ink on the line. */
65
+ .cli-result { flex-basis: 100%; margin-top: var(--sp-xs); }
66
+ .cli-result__say { margin: 0 0 var(--sp-xs); font-size: 14.5px; color: var(--ink); }
67
+ .quote {
68
+ background: var(--glass);
69
+ border: var(--line) solid var(--glass-edge);
60
70
  border-radius: var(--radius-sm);
71
+ padding: var(--sp-xs) var(--sp-sm) var(--sp-hair);
61
72
  }
62
- table.voices {
63
- width: 100%;
64
- border-collapse: collapse;
73
+ .quote__eyebrow { margin: 0 0 var(--sp-hair); font-family: var(--label-face); font-size: var(--label-size); letter-spacing: var(--label-space); text-transform: var(--label-case); color: var(--ink-faint); }
74
+ .quote__pre {
75
+ margin: 0;
76
+ font-family: var(--font-mono);
65
77
  font-size: 13px;
78
+ line-height: 1.6;
66
79
  color: var(--ink);
80
+ white-space: pre-wrap;
81
+ overflow-wrap: anywhere;
82
+ word-break: break-all;
67
83
  }
68
- table.voices th, table.voices td {
69
- padding: 7px 12px;
70
- text-align: left;
71
- border-bottom: 1px solid var(--rule-soft);
72
- vertical-align: middle;
73
- }
74
- table.voices th {
75
- /* sticks inside .voices-wrap's own scroller, not the window — top:0 is
76
- relative to the wrap, so no --topbar-h offset needed here */
77
- position: sticky;
78
- top: 0;
79
- z-index: 1;
80
- background: var(--surface);
81
- font-size: 11px;
82
- font-weight: 600;
83
- letter-spacing: 0.06em;
84
- text-transform: uppercase;
85
- color: var(--ink-faint);
86
- border-bottom: 1px solid var(--border-strong);
87
- }
88
- table.voices tr:last-child td { border-bottom: none; }
89
- table.voices tbody tr:hover td { background: var(--surface-2); }
90
- .voices__skill code {
91
- font-size: 12.5px;
92
- color: var(--info);
93
- }
94
- .voices__default {
95
- font-size: 12.5px;
96
- color: var(--ink-soft);
97
- }
98
- select.voices__select { min-width: 140px; }
99
- .voices-empty, .craft-empty {
100
- color: var(--ink-faint);
101
- font-style: italic;
102
- font-size: 13px;
103
- padding: 10px 0;
104
- }
105
-
106
- /* ---- Sounds (per-sound on/off toggles) ---- */
107
- .sound-rows { margin-top: 8px; }
108
- .sound-row {
109
- display: flex;
110
- align-items: center;
111
- justify-content: space-between;
112
- gap: 16px;
113
- padding: 9px 0;
114
- border-bottom: 1px solid var(--rule-soft);
115
- }
116
- .sound-row:last-child { border-bottom: none; }
117
- .sound-row__text { min-width: 0; }
118
- .sound-row__label {
119
- font-size: 13.5px;
120
- color: var(--ink);
121
- font-weight: 600;
122
- }
123
- .sound-row__desc {
124
- font-size: 12.5px;
125
- color: var(--ink-soft);
126
- margin-top: 2px;
127
- }
128
- button.sound-toggle {
129
- font-family: var(--font-body);
130
- font-size: 12.5px;
131
- font-weight: 600;
132
- padding: 5px 16px;
133
- min-width: 72px;
134
- border-radius: 999px;
135
- border: 1px solid var(--border-strong);
136
- background: var(--surface);
137
- color: var(--ink-soft);
138
- cursor: pointer;
139
- flex-shrink: 0;
140
- }
141
- button.sound-toggle[data-on="1"] {
142
- background: var(--accent);
143
- border-color: var(--accent);
144
- color: #fff;
145
- }
146
- button.sound-toggle:disabled { opacity: 0.5; cursor: default; }
147
- .sound-volume {
148
- display: flex;
149
- align-items: center;
150
- gap: 14px;
151
- padding: 4px 0 12px;
152
- border-bottom: 1px solid var(--border);
153
- margin-bottom: 4px;
154
- }
155
- .sound-volume__label {
156
- font-size: 11px;
157
- font-weight: 600;
158
- text-transform: uppercase;
159
- letter-spacing: 0.06em;
160
- color: var(--ink-faint);
161
- flex-shrink: 0;
162
- }
163
- .sound-volume input[type="range"] {
164
- flex: 1;
165
- accent-color: var(--accent);
166
- cursor: pointer;
167
- }
168
- .sound-volume__pct {
169
- font-size: 13px;
170
- color: var(--ink);
171
- min-width: 3.2em;
172
- text-align: right;
173
- font-variant-numeric: tabular-nums;
174
- }
175
-
176
- /* ---- Event sounds (per-event variant dropdowns, task 582) ---- */
177
- .event-sound-rows { margin-top: 8px; }
178
- .event-sound-row {
179
- display: flex;
180
- align-items: center;
181
- justify-content: space-between;
182
- gap: 16px;
183
- padding: 9px 0;
184
- border-bottom: 1px solid var(--rule-soft);
185
- }
186
- .event-sound-row:last-child { border-bottom: none; }
187
- .event-sound-row__text { flex: 1; min-width: 0; }
188
- .event-sound-row__label {
189
- font-size: 13.5px;
190
- color: var(--ink);
191
- font-weight: 600;
192
- }
193
- .event-sound-row__desc {
194
- font-size: 12.5px;
195
- color: var(--ink-soft);
196
- margin-top: 2px;
197
- }
198
- .event-sound-row__controls {
199
- display: flex;
200
- align-items: center;
201
- gap: 8px;
202
- flex-shrink: 0;
203
- }
204
- .event-sound-select { cursor: pointer; }
205
- .event-sound-preview {
206
- font-family: var(--font-body);
207
- font-size: 12px;
208
- font-weight: 600;
209
- padding: 5px 12px;
210
- border-radius: 999px;
211
- border: 1px solid var(--border-strong);
212
- background: transparent;
213
- color: var(--ink-soft);
214
- cursor: pointer;
215
- }
216
- .event-sound-preview:active { opacity: 0.7; }
217
-
218
- /* ---- Disciplines (preference chips) ---- */
219
- .craft-chips {
84
+ .quote__foot {
220
85
  display: flex;
221
86
  flex-wrap: wrap;
222
- gap: 10px;
223
- margin-top: 8px;
224
- }
225
- .craft-chip {
226
- display: inline-flex;
227
- align-items: center;
228
- gap: 8px;
229
- font-size: 13.5px;
230
- color: var(--ink);
231
- background: var(--surface);
232
- border: 1px solid var(--border-strong);
233
- border-radius: 999px;
234
- padding: 6px 14px;
235
- cursor: pointer;
236
- user-select: none;
237
- text-transform: capitalize;
238
- }
239
- .craft-chip:hover { background: var(--surface-2); }
240
- .craft-chip input { cursor: pointer; }
241
- .craft-chip[data-on="1"] {
242
- background: var(--accent);
243
- border-color: var(--accent);
244
- color: #fff;
245
- }
246
- .craft-actions {
247
- display: flex;
248
87
  align-items: center;
249
- gap: 14px;
250
- margin-top: 14px;
251
- flex-wrap: wrap;
252
- }
253
-
254
- /* ---- Buttons (Save / card actions) ---- */
255
- button.craft-save, .settings-btn {
256
- font-family: var(--font-body);
257
- font-size: 13px;
258
- font-weight: 600;
259
- padding: 7px 16px;
260
- border: 1px solid var(--accent);
261
- background: var(--surface);
262
- color: var(--accent-text);
263
- border-radius: var(--radius-sm);
264
- cursor: pointer;
265
- display: inline-block;
266
- text-decoration: none;
267
- }
268
- button.craft-save:hover:not(:disabled), .settings-btn:hover:not(:disabled) {
269
- background: var(--accent);
270
- color: #fff;
271
- text-decoration: none;
272
- }
273
- button.craft-save:disabled, .settings-btn:disabled { opacity: 0.5; cursor: default; }
274
- /* Discord brand button — keeps its blurple so it's recognisable. */
275
- .settings-btn--discord {
276
- background: #5865F2;
277
- border-color: #4853c4;
278
- color: #fff;
88
+ gap: var(--sp-xs);
89
+ margin-top: var(--sp-xs);
90
+ padding-top: var(--sp-hair);
91
+ border-top: var(--line) solid var(--glass-edge);
279
92
  }
280
- .settings-btn--discord:hover { background: #4853c4; color: #fff; }
281
- .settings-btn--quiet {
282
- border-color: var(--border-strong);
283
- color: var(--ink-soft);
284
- }
285
- .settings-btn--quiet:hover:not(:disabled) { background: var(--surface-2); color: var(--ink); }
286
93
 
287
- /* ---- Display name (single editable field, task 1669) ---- */
288
- .display-name-row {
289
- display: flex;
290
- flex-wrap: wrap;
291
- align-items: center;
292
- gap: 12px;
293
- margin-top: 8px;
294
- }
295
- input.display-name-input {
296
- flex: 1 1 280px;
297
- min-width: 0;
298
- }
94
+ /* ---- active sessions --------------------------------------------------------
95
+ The name is a run with its pill and the agent beside it, then the when-line
96
+ underneath; a row of the family otherwise. */
97
+ .sessions-row__main > strong { display: inline; }
98
+ .sessions-row__agent { display: inline; margin-left: 8px; }
99
+ .sessions-row__when { display: block; }
299
100
 
300
- /* ---- Connections + CLI access cards ---- */
301
- .settings-card {
302
- margin-top: 8px;
303
- display: flex;
304
- flex-wrap: wrap;
305
- align-items: center;
306
- gap: 12px;
307
- }
308
- .settings-card__hint { color: var(--ink-soft); font-size: 13px; }
309
- .settings-card__ok { font-weight: 600; color: var(--ok); }
310
- .cli-result { flex-basis: 100%; margin-top: 4px; }
311
- .cli-result pre {
312
- background: var(--bg-deep);
313
- color: var(--ink);
314
- padding: 10px 12px;
315
- border: 1px solid var(--border);
316
- border-radius: var(--radius-sm);
317
- overflow-x: auto;
318
- font-size: 12.5px;
319
- margin: 6px 0;
101
+ /* ---- Voices (per-skill model picker) ------------------------------------- */
102
+ .voices-wrap { margin-top: var(--sp-sm); overflow-x: auto; }
103
+ table.voices { width: 100%; border-collapse: collapse; text-align: left; font-size: 14px; color: var(--ink); }
104
+ table.voices th, table.voices td {
105
+ padding: var(--sp-xs) var(--sp-xs) var(--sp-xs) 0;
106
+ vertical-align: middle;
107
+ border-top: var(--line) solid var(--rule-soft);
108
+ }
109
+ table.voices thead th {
110
+ padding-top: 0;
111
+ border-top: 0;
112
+ border-bottom: var(--line) solid var(--rule);
113
+ font-family: var(--label-face);
114
+ font-size: var(--label-size);
115
+ font-weight: 400;
116
+ letter-spacing: var(--label-space);
117
+ text-transform: var(--label-case);
118
+ color: var(--ink-faint);
119
+ white-space: nowrap;
320
120
  }
121
+ .voices__skill { white-space: nowrap; }
122
+ .voices__skill code { font-family: var(--font-mono); font-size: 12.5px; color: var(--ink-soft); }
123
+ .voices__default { margin-left: 0; font-family: var(--font-body); font-size: 14px; letter-spacing: 0; text-transform: none; color: var(--ink-soft); }
124
+ td.voices__choice { width: 1%; white-space: nowrap; }
125
+ select.voices__select { min-width: 140px; }
126
+ .voices-empty, .craft-empty { padding: var(--sp-xs) 0; font-size: 13.5px; color: var(--ink-faint); }
321
127
 
322
- /* ---- Active sessions (task 1003341) — one row per live session, the current
323
- one badged so nobody ends the page they are reading. ---- */
324
- .sessions-row {
325
- display: flex;
326
- flex-wrap: wrap;
327
- align-items: center;
328
- justify-content: space-between;
329
- gap: 10px;
330
- padding: 10px 0;
331
- border-bottom: 1px solid var(--border);
332
- }
333
- .sessions-row:last-child { border-bottom: 0; }
334
- .sessions-row__main { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; min-width: 0; }
335
- .sessions-row__agent,
336
- .sessions-row__when { color: var(--ink-soft); font-size: 13px; }
337
- .sessions-row__agent { overflow-wrap: anywhere; }
338
- .sessions-row__when { flex-basis: 100%; }
339
- .sessions-row__badge {
340
- font-size: 12px;
341
- padding: 1px 7px;
342
- border: 1px solid var(--border);
343
- border-radius: var(--radius-sm);
344
- color: var(--ink-soft);
345
- }
128
+ /* ---- Sounds: the volume line ------------------------------------------- */
129
+ .sound-volume { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-xs); margin-bottom: var(--sp-xs); }
130
+ .sound-volume .field-label { margin: 0; }
131
+ .sound-volume__pct { min-width: 3.2em; font-size: 13px; color: var(--ink-faint); text-align: right; font-variant-numeric: tabular-nums; }
346
132
 
347
- /* ---- Image key (task 1013) — the "we cover it" / "action needed" banner
348
- uses the shared semantic status tokens. ---- */
133
+ /* ---- Image key: the covered / action-needed banner ------------------------ */
349
134
  .artkey-banner {
350
- border-radius: var(--radius-sm);
351
- padding: 10px 14px;
352
- font-size: 13px;
135
+ margin: 0 0 var(--sp-sm);
136
+ padding: var(--sp-xs) var(--sp-sm);
137
+ font-size: 14px;
353
138
  line-height: 1.45;
354
- border: 1px solid var(--border);
355
- margin-bottom: 10px;
356
- }
357
- .artkey-banner--covered {
358
- background: var(--ok-weak);
359
- border-color: color-mix(in srgb, var(--ok) 45%, var(--border));
360
- color: var(--ok);
361
- }
362
- .artkey-banner--action {
139
+ color: var(--ink);
140
+ border-left: 2px solid var(--warn);
363
141
  background: var(--warn-weak);
364
- border-color: color-mix(in srgb, var(--warn) 45%, var(--border));
365
- color: var(--warn);
366
- }
367
- .artkey-entry-row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; flex-basis: 100%; }
368
- .artkey-input {
369
- flex: 1 1 280px;
370
- min-width: 0;
371
- padding: 7px 10px;
372
- border: 1px solid var(--border-strong);
373
142
  border-radius: var(--radius-sm);
374
- background: var(--surface);
375
- color: var(--ink);
376
- font-family: ui-monospace, "SF Mono", Menlo, monospace;
377
- font-size: 13px;
378
143
  }
379
- .artkey-actions { display: flex; gap: 10px; flex-wrap: wrap; }
144
+ .artkey-banner--covered { border-left-color: var(--ok); background: var(--ok-weak); }
145
+ .artkey-banner strong { font-weight: 500; }
380
146
  #artkey-entry { flex-basis: 100%; }
147
+ /* the entry renders inside the card's flex row, so its label takes the row */
148
+ #artkey-card > .field-label, #artkey-entry > .field-label { flex: 1 1 100%; margin-top: 0; }
149
+ #artkey-entry[hidden] { display: none; }
150
+ .artkey-entry-row { flex-basis: 100%; }
151
+
152
+ /* ---- Discord -----------------------------------------------------------------
153
+ THIRD-PARTY MARK, KEPT ON PURPOSE. Discord's blurple is Discord's, not the
154
+ world's: the button is recognisable as theirs only in their colour, and the
155
+ link opens their consent screen. These two literals are the one sanctioned
156
+ exception in this sheet — a future literal sweep must leave them alone. */
157
+ .settings-btn--discord { background: #5865F2; border-color: #4853c4; color: #ffffff; }
158
+ .settings-btn--discord:hover:not(:disabled) { background: #4853c4; border-color: #4853c4; color: #ffffff; }
159
+
160
+ /* ---- dev box ------------------------------------------------------------- */
161
+ /* the box panel is the shell's (.setup__box, box-panel.js); inside a settings
162
+ panel it sits on the glass rather than a second card */
163
+ .settings-groups .setup__box { margin: var(--sp-sm) 0 0; background: var(--glass); border-color: var(--glass-edge); border-radius: var(--radius-sm); }