@bongos/core 1.19.461 → 1.19.463

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 (51) hide show
  1. package/.bongos-core.json +145 -40
  2. package/.claude/skills/design/SKILL.md +2 -1
  3. package/docs/adr/0221-the-impeccable-method-is-one-skill-with-sub-command-playbooks.md +36 -0
  4. package/docs/adr/README.md +1 -0
  5. package/docs/copy-inventory.md +122 -125
  6. package/docs/copy-registry.json +306 -322
  7. package/docs/file-map.md +2 -2
  8. package/docs/module-api-changelog.md +4 -0
  9. package/modules/hall-ui/public/goals-lib.js +10 -6
  10. package/modules/hall-ui/public/goals-page.js +27 -12
  11. package/modules/hall-ui/public/goals-render.js +83 -52
  12. package/modules/hall-ui/public/goals.css +129 -120
  13. package/modules/hall-ui/public/goals.html +29 -11
  14. package/modules/hall-ui/public/goals.states.json +71 -3
  15. package/modules/hall-ui/public/people.css +27 -0
  16. package/modules/hall-ui/public/people.html +15 -7
  17. package/modules/hall-ui/public/people.js +111 -22
  18. package/modules/hall-ui/public/people.states.json +31 -1
  19. package/modules/hall-ui/public/scouting.html +30 -17
  20. package/modules/hall-ui/public/scouting.states.json +7 -2
  21. package/modules/hall-ui/public/sessions.css +18 -15
  22. package/modules/hall-ui/public/sessions.html +27 -22
  23. package/modules/hall-ui/public/sessions.js +32 -13
  24. package/modules/hall-ui/public/sessions.states.json +3 -2
  25. package/modules/ui-design/module.json +1 -1
  26. package/modules/ui-design/skills/README.md +4 -3
  27. package/modules/ui-design/skills/impeccable/SKILL.md +143 -0
  28. package/modules/ui-design/skills/impeccable/reference/adapt.md +25 -0
  29. package/modules/ui-design/skills/impeccable/reference/animate.md +27 -0
  30. package/modules/ui-design/skills/impeccable/reference/audit.md +19 -0
  31. package/modules/ui-design/skills/impeccable/reference/bolder.md +17 -0
  32. package/modules/ui-design/skills/impeccable/reference/clarify.md +25 -0
  33. package/modules/ui-design/skills/impeccable/reference/colorize.md +22 -0
  34. package/modules/ui-design/skills/impeccable/reference/critique.md +15 -0
  35. package/modules/ui-design/skills/impeccable/reference/distill.md +26 -0
  36. package/modules/ui-design/skills/impeccable/reference/document.md +25 -0
  37. package/modules/ui-design/skills/impeccable/reference/extract.md +20 -0
  38. package/modules/ui-design/skills/impeccable/reference/harden.md +21 -0
  39. package/modules/ui-design/skills/impeccable/reference/layout.md +20 -0
  40. package/modules/ui-design/skills/impeccable/reference/onboard.md +22 -0
  41. package/modules/ui-design/skills/impeccable/reference/optimize.md +22 -0
  42. package/modules/ui-design/skills/impeccable/reference/polish.md +22 -0
  43. package/modules/ui-design/skills/impeccable/reference/quieter.md +17 -0
  44. package/modules/ui-design/skills/impeccable/reference/shape.md +23 -0
  45. package/modules/ui-design/skills/impeccable/reference/typeset.md +24 -0
  46. package/package-lock.json +2 -2
  47. package/package.json +1 -1
  48. package/src/module-api.js +1 -1
  49. package/tests/hall_roster_world.mjs +287 -0
  50. package/tests/ui_design_skills.mjs +41 -0
  51. package/modules/hall-ui/public/scouting.js +0 -73
@@ -25,47 +25,42 @@
25
25
  The ch unit is the zero glyph's width, not a character — the body measure is
26
26
  --measure (48ch, measured at ~60 characters), never fresh ch arithmetic. */
27
27
 
28
- /* ---- goal-card grid (the CARDS view) ----
29
- task 2171 density pass: minmax 300→230 (four across at desktop width),
30
- tighter gaps/padding/type. Retired by task 1002439's grouped rollup, REVIVED
31
- by task 1003136 as the list|cards toggle's card view: renderList nests
32
- id-less .goal-grid wrappers per group, which this rule styles — the
33
- #goal-grid override further down only flattens the OUTER container. */
34
- .goal-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 10px; }
28
+ /* ---- the CARDS view: the row folded, not a second design ----
29
+ task 1003470: the rows|cards toggle stays — the archetype keeps it, because
30
+ a card wall is the better read when someone scans for a NAME rather than for
31
+ what can move — but the card is the row's compact sibling and nothing else:
32
+ the same title with the id inside it, the same pills and chips, the same
33
+ roll line, the same right-column state, from ONE fact source
34
+ (goalRenderFacts; pinned by tests/goals_card_view.mjs). A card inside the
35
+ list panel draws no box — a --bg-card tile on a --bg-card panel is the
36
+ card-in-a-card v3 removed — so the grid paints the hairline BETWEEN its rows
37
+ (a 1px row gap over the rule tone, each cell on the panel's own ground) and
38
+ nothing above the first or below the last. renderList nests id-less
39
+ .goal-grid wrappers per group, which this rule styles; the #goal-grid
40
+ override further down only flattens the OUTER container. */
41
+ .goal-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); gap: 1px 0; background: var(--rule-soft); --ring: 36px; }
35
42
  .goal-card {
36
- display: flex; flex-direction: column; gap: 7px;
37
- background: var(--bg-card); border: var(--line) solid transparent;
38
- border-radius: var(--radius); padding: 10px 12px;
43
+ display: flex; flex-direction: column; gap: 10px;
44
+ padding: 18px 32px 20px 0;
45
+ background: var(--bg-card);
39
46
  color: var(--ink);
47
+ min-width: 0;
40
48
  }
41
- /* Hover lifts the tone; the transparent edge is what focus lands on. */
42
- a.goal-card:hover { text-decoration: none; background: var(--glass); }
43
- .goal-card__head { display: flex; align-items: flex-start; gap: 9px; }
44
- /* task 1003136: the task-1760 drag handle/priority/dragging rules that lived
45
- here are deleted with wireDragReorder (see goals-page.js) — the revived
46
- cards deliberately carry no drag affordance. */
47
- /* task 1761: a goal blocked by an unachieved prerequisite is dimmed + hue-shifted. */
48
- .goal-card--blocked { opacity: 0.62; border-style: dashed; }
49
- .goal-card--blocked:hover { opacity: 0.85; }
49
+ .goal-card__head { display: flex; align-items: flex-start; gap: 12px; }
50
+ .goal-card__head .gr-title { font-size: 15px; }
51
+ .goal-card__title { flex: 1; min-width: 0; }
52
+ .goal-card__foot { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 12px; margin-top: auto; }
53
+ /* task 1761: a goal blocked by an unachieved prerequisite (the detail view). */
50
54
  .goal-deps { color: var(--ink-faint); }
51
55
  .goal-deps--blocked { color: var(--warn); font-weight: 600; }
52
- .goal-card__title { font-weight: 650; font-size: 13px; line-height: 1.35; flex: 1; min-width: 0; }
56
+ /* The detail head's meta line and its mono id (task 2163). */
53
57
  .goal-card__meta { display: flex; flex-wrap: wrap; gap: 4px 6px; align-items: center; font-size: 11.5px; color: var(--ink-faint); }
54
- /* task 2163 — the real goal id, shown as a monospace #NNN token so it reads as
55
- an identifier and never gets confused with the priority ordinal on the left. */
56
58
  .goal-card__id, .goal-detail__id {
57
59
  font-family: var(--font-mono);
58
60
  font-variant-numeric: tabular-nums; font-weight: 600; color: var(--ink-faint);
59
61
  }
60
62
  .goal-card__id { padding: 0 5px; border: var(--line) solid var(--rule-soft); border-radius: var(--radius-sm); }
61
63
  .goal-detail__id { font-size: 0.72em; vertical-align: 0.12em; margin-right: 4px; }
62
- .goal-card__foot { display: flex; flex-wrap: wrap; gap: 4px 10px; font-size: 11.5px; color: var(--ink-soft); }
63
- .goal-card__foot strong { font-variant-numeric: tabular-nums; }
64
- /* boot skeleton — goal-card-shaped shimmer placeholders (kit .kit-skel bars)
65
- so the grid doesn't jump when data lands. */
66
- .goal-card--skel { pointer-events: none; }
67
- .goal-card--skel .kit-skel { width: 80%; }
68
- .goal-card--skel .kit-skel + .kit-skel { width: 55%; }
69
64
 
70
65
  /* ---- detail view ---- */
71
66
  .goal-detail__head { display: flex; align-items: flex-start; gap: 20px; flex-wrap: wrap; }
@@ -228,117 +223,131 @@ a.goal-card:hover { text-decoration: none; background: var(--glass); }
228
223
  .conflict__paths { font-family: var(--font-mono); font-size: 11.5px; color: var(--ink-soft); margin-top: 4px; word-break: break-all; }
229
224
  .conflict-note { font-size: 12px; color: var(--ink-faint); margin: 10px 0 0; }
230
225
 
231
- /* Filtered-count line under the kit filter bar (list view). */
232
- .goals-count-line { margin: -4px 0 12px; font-size: 12.5px; color: var(--ink-faint); }
226
+ /* The count line + the view toggle (list view): one line under the filter
227
+ bar. rows|cards is a VIEW, not a filter — it left the filter bar in task
228
+ 1003470 for the archetype's segmented control (mock .viewtoggle). The pressed
229
+ side takes the tint the world gives a current thing (the ranks ladder, the
230
+ oversight select row), never the accent fill: a view is not a live door. */
231
+ .goals-line { display: flex; align-items: center; justify-content: space-between; flex-wrap: wrap; gap: 10px 20px; margin: 0 0 6px; }
232
+ .goals-count-line { margin: 0; font-family: var(--font-mono); font-size: 12.5px; font-variant-numeric: tabular-nums; color: var(--ink-faint); }
233
+ .viewtoggle { display: inline-flex; align-items: center; gap: 3px; padding: 3px; border: var(--line) solid var(--rule-soft); border-radius: var(--radius-pill); }
234
+ .viewtoggle__btn {
235
+ min-height: 26px; min-width: 60px; padding: 0 14px;
236
+ font: inherit; font-size: 13px; text-transform: lowercase; color: var(--ink-faint);
237
+ background: none; border: 0; border-radius: var(--radius-pill); cursor: pointer;
238
+ }
239
+ .viewtoggle__btn:hover { color: var(--ink-soft); }
240
+ .viewtoggle__btn[aria-pressed="true"] { color: var(--accent-ink); background: var(--accent-tint); }
233
241
 
234
242
  /* ============================================================
235
- task 1002439 — Goals "what's next" rollup (grouped list view).
236
- Replaces the paginated card grid: a task-readiness summary, then
237
- goals grouped Active / Blocked / Achieved. Each row rolls its
238
- tasks up into ready/in-progress/blocked; the task→task dependency
239
- graph itself lives on the Tasks page. Uses the shared style.css tokens.
243
+ The list — task 1002439's grouped rollup, redesigned in task 1003470 (goal
244
+ 1000081). ONE panel: the shell's .scroll (.goals-panel) holds the filter bar
245
+ under a hairline, the count line with the view toggle, the readiness band
246
+ (the family's .ov-figs) and the groups under .ov-group__head. The rows are
247
+ the archetype's (mock .goal): ring / content / right, air not edges, the
248
+ hairline on TOP so the first row after a head starts flush. Before this the
249
+ page was a bar of twelve controls on the ground over one --bg-card panel PER
250
+ GROUP, the readiness as three small dotted numbers, and the group heads on
251
+ the ground between panels.
240
252
  ============================================================ */
241
- /* The OUTER container renders grouped full-width blocks (summary + section
242
- heads), so flip it to normal block flow. Id-selected on purpose: the cards
243
- view (task 1003136) nests id-less .goal-grid wrappers per group, which keep
244
- the real card grid above. */
245
- #goal-grid.goal-grid { display: block; gap: 0; }
246
- .gr-summary { display: flex; flex-wrap: wrap; gap: 22px; margin: 6px 0 20px; }
247
- .gr-sm { display: flex; align-items: baseline; gap: 8px; font-size: 13px; color: var(--ink-soft); }
248
- .gr-sm b { font-size: 20px; font-weight: 700; letter-spacing: -.02em; color: var(--ink); font-variant-numeric: tabular-nums; }
249
- .gr-sm__dot { width: 8px; height: 8px; border-radius: 50%; align-self: center; }
253
+ /* The OUTER container renders full-width blocks, so it is block flow.
254
+ Id-selected on purpose: the cards view nests id-less .goal-grid wrappers per
255
+ group, which keep the real grid above. */
256
+ #goal-grid.goal-grid { display: block; gap: 0; background: none; }
257
+ /* The filter bar closes on a hairline so the figures under it read as the
258
+ panel's content, not as more controls. The kit's overflow row and its chips
259
+ sit inside the same rule. */
260
+ #goals-filters { margin: 0 0 18px; padding: 0 0 6px; border-bottom: var(--line) solid var(--rule-soft); }
261
+ #goals-filters .filters { margin-bottom: 10px; }
250
262
 
251
- /* The group head sits on a --rule hairline (the archetype's .group__head): a
252
- heavier line than the rows' --rule-soft, so the groups read as the outer
253
- division and the rows as the inner one. */
254
- .gr-sec { display: flex; align-items: center; gap: 12px; margin: 26px 0 0; padding: 0 0 10px; border-bottom: var(--line) solid var(--rule); }
255
- /* The section name is the label voice — take the tokens, never the family:
256
- --label-face is mono in light and the body face in dark, because mono caps
257
- read as precise on paper and as thin and technical on black. */
258
- .gr-sec b {
259
- font-family: var(--label-face); font-size: var(--label-size); font-weight: 400;
260
- letter-spacing: var(--label-space); text-transform: var(--label-case);
261
- color: var(--ink-faint);
262
- }
263
- .gr-sec__n { font-size: 12px; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
263
+ /* The readiness band: the family figure band with a dot in the label — the
264
+ same dots the roll line under every row uses, so the band and the rows are
265
+ one language. */
266
+ .gr-figs { margin-top: 18px; }
267
+ .gr-figs + .ov-group { margin-top: var(--sp-md); }
268
+ .gr-mark { display: inline-block; width: 7px; height: 7px; border-radius: 50%; margin-right: 8px; vertical-align: 1px; flex: none; }
269
+ .gr-mark--ready { background: var(--ok); }
270
+ .gr-mark--active { background: var(--info); }
271
+ .gr-mark--blocked { background: var(--warn); }
272
+ .gr-mark--backlog { background: var(--ink-faint); }
264
273
 
265
- /* The rollup list is one panel, not a stack of bordered rows: --bg-card at the
266
- full radius with a transparent edge, and the rows' own hairlines doing the
267
- dividing. The padding is the panel's, so the first and last rows breathe
268
- against it rather than against their own borders. */
269
- .gr-list { border: var(--line) solid transparent; border-radius: var(--radius); background: var(--bg-card); padding: 4px 0; overflow: hidden; }
270
- /* The archetype's row (mock .goal): the air is the loosest of the three
271
- versions, the columns are ring / content / right, and the rule sits on TOP so
272
- the first row can drop it and start flush against the panel's own padding. */
273
- .gr-row { display: grid; grid-template-columns: auto minmax(0, 1fr) auto; gap: 24px; align-items: start; padding: 20px 22px; border-top: var(--line) solid var(--rule-soft); }
274
- .gr-row:first-child { border-top: 0; }
275
- .gr-row:hover { background: var(--glass); }
276
- .gr-ringcol { flex: none; }
277
- .gr-crit-none { display: inline-grid; place-items: center; width: 44px; height: 44px; border-radius: 50%; border: 2px solid var(--rule); color: var(--ink-faint); font-size: 13px; }
274
+ /* The rows. --ring is the ring column's width; the ring svg takes it. */
275
+ .gr-list { --ring: 44px; margin: 0; }
276
+ .gr-row { display: grid; grid-template-columns: var(--ring) minmax(0, 1fr) auto; gap: 24px; align-items: start; padding: 20px 0; border-top: var(--line) solid var(--rule-soft); }
277
+ .ov-group__head + .gr-list > .gr-row:first-child { border-top: 0; }
278
+ .gr-ringcol { width: var(--ring); flex: none; }
279
+ .gr-ringcol .goal-ring { width: var(--ring); height: var(--ring); display: block; }
280
+ /* The ring's figure is the machine's word: mono, quiet. */
281
+ .gr-ringcol .goal-ring__label { font-family: var(--font-mono); font-weight: 400; font-size: 11px; fill: var(--ink-soft); }
282
+ /* No done-when criteria yet: the empty track, and nothing drawn in it. */
283
+ .gr-crit-none { display: block; width: var(--ring); height: var(--ring); border-radius: 50%; border: 3px solid var(--rule-soft); box-sizing: border-box; }
278
284
  .gr-main { min-width: 0; }
279
- /* The goal's name is the way into the goal, so it is a control and owes the
280
- 24px floor on BOTH axes. Its 15px/1.3 line box measured 20px tall (the kit's
281
- controls-24px finding, 26 rows at 1440); the padding buys the height and the
282
- equal negative margin hands the space back so the row does not re-space. */
283
- /* The name at the archetype's weight: 17px/500, not 15px/650. A heavy small
284
- title reads as a label; this reads as the thing itself. It is also the way
285
- into the goal, so it owes the 24px floor on both axes — the padding buys the
286
- height and the equal negative margin hands the space back. */
285
+ /* The name at the archetype's weight: 17px/500, not a heavy label. It is the
286
+ way into the goal, so it owes the 24px floor on both axes — the padding buys
287
+ the height and the equal negative margin hands the space back. */
287
288
  .gr-title {
288
289
  display: inline-block; font-size: 17px; font-weight: 500; letter-spacing: -.014em;
289
290
  color: var(--ink); line-height: 1.3; text-decoration: none;
290
- min-height: 24px; min-width: 24px; padding: 3px 4px; margin: 0 -4px;
291
+ min-height: 24px; min-width: 24px; padding: 3px 4px; margin: -3px -4px;
292
+ overflow-wrap: anywhere;
291
293
  }
292
294
  .gr-title:hover { color: var(--accent-ink); text-decoration: underline; text-underline-offset: 3px; }
293
- /* The id rides INSIDE the title rather than on the meta line below it, so every
294
- row's name starts at the same x and the id never competes for the second line
295
- the subtitle now wants. */
296
- .gr-title__id { font-family: var(--font-mono); font-size: 12px; font-variant-numeric: tabular-nums; color: var(--ink-faint); margin-right: 9px; }
297
-
298
- /* The goal's own one-line explanation. Task 1003224 made this a tooltip; task
299
- 1003307 gives it the line back on the owner's call, because 35 of 72 goals
300
- carry one and a tooltip does not exist on a touch screen. Empty goals render
301
- no element at all, so a row without a subtitle simply closes up. */
295
+ /* The id rides INSIDE the title, mono and quiet, so every row's name starts at
296
+ the same x and the id never competes for the subtitle's line. */
297
+ .gr-title__id { font-family: var(--font-mono); font-size: 12px; font-weight: 400; font-variant-numeric: tabular-nums; color: var(--ink-faint); margin-right: 9px; }
298
+ /* The goal's own one-line explanation: 35 of 72 goals carry one, and a tooltip
299
+ does not exist on a touch screen (task 1003307, on the owner's call). */
302
300
  .gr-sub { margin: 5px 0 0; color: var(--ink-soft); font-size: 14px; line-height: 1.5; max-width: var(--measure); }
303
-
304
- /* The fact line: the state, then what the goal is scoped to. The pill and the
305
- chip themselves are the shell's .fact-pill / .fact-chip — this row only says
306
- how they sit together. */
301
+ /* The fact line: the state pill, the scope chips, the version chip and the
302
+ category chip — every label a goal carries, in one run. The pill and the
303
+ chips are the shell's; this only says how they sit together. */
307
304
  .gr-facts { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px; margin: 12px 0 0; }
308
- /* What is left of the old meta run-on: the sort stamp, and only while a date
309
- sort is active. The id moved into the title, the version became a chip and the
310
- status became a pill. */
311
- .gr-meta { font-size: 12px; color: var(--ink-faint); margin-top: 8px; font-variant-numeric: tabular-nums; }
305
+ .gr-facts .gr-cat { margin-left: 0; }
306
+ /* The roll line: what the goal's tasks are doing, only the non-zero buckets. */
312
307
  .gr-roll { display: flex; flex-wrap: wrap; gap: 8px 18px; margin-top: 12px; }
313
- .gr-d { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
314
- .gr-d i { width: 7px; height: 7px; border-radius: 50%; }
308
+ .gr-d { display: inline-flex; align-items: center; gap: 8px; font-size: 13px; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
309
+ .gr-d .gr-mark { margin-right: 0; }
315
310
  .gr-d b { color: var(--ink); font-weight: 600; }
316
- .gr-right { text-align: right; min-width: 150px; max-width: 244px; display: flex; flex-direction: column; align-items: flex-end; gap: 10px; }
317
- .gr-next { display: flex; flex-direction: column; align-items: flex-end; gap: 8px; max-width: 230px; }
318
- .gr-next__t { font-size: 12.5px; color: var(--ink-soft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 230px; }
319
- .gr-claim { background: var(--accent); color: var(--accent-on); border-radius: var(--radius-sm); padding: 6px 14px; font-size: 12.5px; font-weight: 650; white-space: nowrap; }
320
- .gr-claim:hover { background: var(--accent-deep); }
321
- .gr-drill { font-size: 12.5px; color: var(--ink-faint); display: inline-flex; align-items: center; min-height: 24px; gap: 5px; }
322
- .gr-drill:hover { color: var(--accent-ink); }
323
- .gr-met { font-size: 12.5px; font-weight: 600; color: var(--ok); }
324
- .gr-quiet { font-size: 12.5px; color: var(--ink-faint); }
311
+ /* The right column is ONE thing: the next action, or a one-line state. The
312
+ accent lands only on "claim next", the row's live door; the other states are
313
+ ink, and the blocked door is a ghost pill. Under it, the sort's date while a
314
+ date sort is on. */
315
+ .gr-right { display: flex; flex-direction: column; align-items: flex-end; gap: 10px; text-align: right; max-width: 244px; }
316
+ .gr-next { display: flex; flex-direction: column; align-items: flex-end; gap: 8px; max-width: 244px; }
317
+ .gr-next__t { font-size: 13px; color: var(--ink-soft); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 244px; }
318
+ .gr-state { display: inline-flex; align-items: center; gap: 7px; font-size: 13px; color: var(--ink-soft); text-align: left; }
319
+ .gr-state--ok { color: var(--ok); }
320
+ .gr-state__mark { width: 14px; height: 14px; flex: none; stroke: currentColor; fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
321
+ .gr-quiet { font-size: 13px; color: var(--ink-faint); }
322
+ .gr-when { font-size: 12.5px; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
325
323
 
326
- .gr-pills { display: flex; flex-wrap: wrap; gap: 8px; }
327
- .gr-pill { display: inline-flex; align-items: center; gap: 8px; background: var(--bg-card); border: var(--line) solid var(--rule); border-radius: var(--radius-pill); padding: 6px 13px 6px 8px; color: var(--ink-soft); font-size: 12.5px; min-height: 24px; }
328
- .gr-pill:hover { background: var(--glass); }
329
- .gr-pill b { color: var(--ink); font-weight: 600; }
330
- .gr-pill__ok { width: 17px; height: 17px; border-radius: 50%; background: var(--ok-weak); color: var(--ok); display: grid; place-items: center; font-size: 10px; font-weight: 800; }
331
- .gr-pill__id { color: var(--ink-faint); font-variant-numeric: tabular-nums; }
324
+ /* Achieved and archived goals are compact pills in BOTH views (mock .pill): a
325
+ hairline pill with the check drawn, the name at 500, the id in mono. */
326
+ .gr-pills { display: flex; flex-wrap: wrap; gap: 9px; margin: 18px 0 0; }
327
+ .gr-pill { display: inline-flex; align-items: center; gap: 10px; min-height: 34px; padding: 0 15px; border: var(--line) solid var(--rule); border-radius: var(--radius-pill); color: var(--ink-soft); font-size: 13px; text-decoration: none; }
328
+ .gr-pill:hover { border-color: var(--ink-faint); color: var(--ink); text-decoration: none; }
329
+ .gr-pill b { color: var(--ink); font-weight: 500; }
330
+ .gr-pill__mark { width: 16px; height: 16px; flex: none; stroke: var(--ok); fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
331
+ .gr-pill__id { font-family: var(--font-mono); font-size: 12px; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
332
332
  /* task 1002965 — the date an achieved/archived pill was ordered on, shown only
333
- while a date sort is active (the pill's counterpart to .gr-meta). */
333
+ while a date sort is active (the pill's counterpart to .gr-when). */
334
334
  .gr-pill__when { color: var(--ink-faint); font-variant-numeric: tabular-nums; }
335
- .gr-pills--muted { opacity: .6; }
335
+ .gr-pills--muted .gr-pill { color: var(--ink-faint); }
336
+ .gr-pills--muted .gr-pill__mark { stroke: var(--ink-faint); }
337
+
338
+ /* Boot skeleton: row-shaped, in the skeleton tone (--bg-deep is black in dark). */
339
+ .gr-row--skel { pointer-events: none; }
340
+ .gr-skel__ring { display: block; width: var(--ring); height: var(--ring); border-radius: 50%; background: var(--skeleton); }
341
+ .gr-row--skel .kit-skel { display: block; width: 60%; }
342
+ .gr-row--skel .kit-skel + .kit-skel { width: 40%; margin-top: 10px; }
336
343
 
337
344
  @media (max-width: 640px) {
338
- .gr-row { grid-template-columns: auto 1fr; row-gap: 11px; }
339
- .gr-right { grid-column: 1 / -1; align-items: flex-start; text-align: left; }
340
- .gr-next { align-items: flex-start; }
341
- .gr-next__t { max-width: 100%; }
345
+ .gr-list { --ring: 36px; }
346
+ .gr-row { grid-template-columns: var(--ring) minmax(0, 1fr); gap: 14px 16px; padding: 18px 0; }
347
+ .gr-right, .gr-next { grid-column: 1 / -1; align-items: flex-start; text-align: left; max-width: 100%; }
348
+ .gr-next__t { max-width: 100%; white-space: normal; }
349
+ .goal-grid { grid-template-columns: minmax(0, 1fr); }
350
+ .goal-card { padding-right: 0; }
342
351
  }
343
352
 
344
353
  /* ── task 1003224: name, subtitle, and Claude's text at three weights ────────
@@ -9,6 +9,9 @@
9
9
  <link rel="preconnect" href="https://fonts.googleapis.com">
10
10
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
11
  <link rel="stylesheet" href="/builders/style.css?v=2026-07-03-goals1">
12
+ <!-- task 1003470: the family sheet the list's figure band and group heads
13
+ come from (.ov-figs, .ov-group__head), linked before the page's own. -->
14
+ <link rel="stylesheet" href="/builders/oversight.css">
12
15
  <!-- Page-local styles (task 2171) — extracted from the old inline <style>
13
16
  block per the hall placement rules; shared pager/filter/tab/count-pill
14
17
  styles now live in style.css's hall-kit section. -->
@@ -42,19 +45,34 @@
42
45
  </div>
43
46
 
44
47
  <main id="hall-main">
45
- <!-- LIST view -->
48
+ <!-- LIST view — ONE panel (task 1003470): the filter bar, the count line
49
+ with the rows|cards toggle, the readiness band and the groups all sit
50
+ on the shell's .scroll. It used to be a bar of twelve controls on the
51
+ ground over one panel PER GROUP. -->
46
52
  <section id="goals-list-view" aria-labelledby="goals-list-h">
47
53
  <h2 id="goals-list-h" class="sr-only">All goals</h2>
48
- <!-- task 2171: the filter bar is built by OTBKit.makeFilterBar —
49
- Version / Status / Membership, all primary (no overflow). Control
50
- ids are unchanged (f-goal-version, f-goal-status, f-goal-mine)
51
- so goals-page.js reads stay stable. -->
52
- <div id="goals-filters"></div>
53
- <p class="goals-count-line" id="goals-count" aria-live="polite"></p>
54
- <div class="goal-grid" id="goal-grid"></div>
55
- <!-- task 2171: the grid's pager bar is stamped here by OTBKit
56
- (pagerBarHtml('goals') — same ids, same otb.goals.perPage pref). -->
57
- <div id="goals-pager-mount"></div>
54
+ <div class="scroll goals-panel">
55
+ <!-- task 2171: the filter bar is built by OTBKit.makeFilterBar —
56
+ Version / Status / Sort in the open, the other five behind the
57
+ kit's Filters disclosure (task 1003470). Control ids are
58
+ unchanged so goals-page.js reads stay stable. -->
59
+ <div id="goals-filters"></div>
60
+ <div class="goals-line">
61
+ <p class="goals-count-line" id="goals-count" aria-live="polite"></p>
62
+ <!-- rows|cards is a view, not a filter. The hidden input is what
63
+ renderList reads (#f-goal-view) and what the saved pref restores;
64
+ the two buttons write it. -->
65
+ <div class="viewtoggle" role="group" aria-label="View" id="goals-view">
66
+ <input type="hidden" id="f-goal-view" value="list">
67
+ <button type="button" class="viewtoggle__btn" data-view="list" aria-pressed="true">rows</button>
68
+ <button type="button" class="viewtoggle__btn" data-view="cards" aria-pressed="false">cards</button>
69
+ </div>
70
+ </div>
71
+ <div class="goal-grid" id="goal-grid"></div>
72
+ <!-- task 2171: the grid's pager bar is stamped here by OTBKit
73
+ (pagerBarHtml('goals') — same ids, same otb.goals.perPage pref). -->
74
+ <div id="goals-pager-mount"></div>
75
+ </div>
58
76
  </section>
59
77
 
60
78
  <!-- DETAIL view (hash-routed: #/goal/N) -->
@@ -1,12 +1,12 @@
1
1
  {
2
- "_": "The hall's Goals list + goal detail through modules/ui-design/kit/render.js (task 1003307). Goals is the LIST ARCHETYPE the governance-and-lists re-skin applies across thirteen pages, so its floors are measured first and the rest are held against them. Populated look: node scripts/hall-preview/server.js --port 4620 --fixture-me, then --base-in/--base-out at that origin.",
2
+ "_": "The hall's Goals list + goal detail through modules/ui-design/kit/render.js (task 1003307; redesigned in task 1003470). Goals is the LIST ARCHETYPE the governance-and-lists re-skin applies across thirteen pages, so its floors are measured first and the rest are held against them. Populated look: node scripts/hall-preview/server.js --port 4620 --fixture-me, then --base-in/--base-out at that origin (the harness carries goals, goals/rollups and work-categories since task 1003470). `filters` opens the kit's Filters disclosure (the five lenses that left the open bar); `cards` flips the rows|cards toggle on the count line.",
3
3
  "page": "/builders/goals",
4
4
  "surface": "hall-ui",
5
5
  "stub": {
6
6
  "prefix": "/builders/"
7
7
  },
8
8
  "modeQuery": false,
9
- "_feeds": "The kit's own stub carries no goal feed, so against it the page renders its EMPTY and SKELETON states — which is deliberate: an empty board is a state the floors must hold in. Against the hall-preview harness the same states render populated. The feed 404s are declared here, never hidden. work-categories and goals/rollups have no checked-in fixture, so the harness answers 404: declared here, not hidden.",
9
+ "_feeds": "The kit's own stub carries no goal feed, so against it the page renders its EMPTY and SKELETON states — which is deliberate: an empty board is a state the floors must hold in. Against the hall-preview harness the same states render populated. The feed 404s are declared here, never hidden.",
10
10
  "ignoreRequests": [
11
11
  "/api/(gds|bongos)/me$",
12
12
  "/api/(gds|bongos)/me/",
@@ -36,13 +36,81 @@
36
36
  "expect": {
37
37
  "visible": [
38
38
  "#goals-list-view",
39
- "#goals-filters"
39
+ "#goals-filters",
40
+ "#goals-view"
40
41
  ],
41
42
  "hidden": [
42
43
  "#goal-detail-view"
43
44
  ]
44
45
  }
45
46
  },
47
+ "filters": {
48
+ "auth": true,
49
+ "actions": [
50
+ [
51
+ "wait",
52
+ 600
53
+ ],
54
+ [
55
+ "click",
56
+ "#goals-filters .filters__more"
57
+ ],
58
+ [
59
+ "wait",
60
+ 150
61
+ ]
62
+ ],
63
+ "expect": {
64
+ "visible": [
65
+ "#goals-filters .filters--overflow",
66
+ "#f-goal-category"
67
+ ],
68
+ "attr": [
69
+ [
70
+ "#goals-filters .filters__more",
71
+ "aria-expanded",
72
+ "true"
73
+ ]
74
+ ]
75
+ }
76
+ },
77
+ "cards": {
78
+ "auth": true,
79
+ "actions": [
80
+ [
81
+ "wait",
82
+ 600
83
+ ],
84
+ [
85
+ "click",
86
+ "#goals-view [data-view=\"cards\"]"
87
+ ],
88
+ [
89
+ "wait",
90
+ 200
91
+ ]
92
+ ],
93
+ "expect": {
94
+ "attr": [
95
+ [
96
+ "#goals-view [data-view=\"cards\"]",
97
+ "aria-pressed",
98
+ "true"
99
+ ],
100
+ [
101
+ "#goals-view [data-view=\"list\"]",
102
+ "aria-pressed",
103
+ "false"
104
+ ]
105
+ ],
106
+ "value": [
107
+ [
108
+ "#f-goal-view",
109
+ "cards"
110
+ ]
111
+ ]
112
+ }
113
+ },
46
114
  "detail": {
47
115
  "auth": true,
48
116
  "url": "/builders/goals#/goal/1000081",
@@ -0,0 +1,27 @@
1
+ /* people.css — page-local styles for the cross-project builder directory
2
+ (task 1003470, goal 1000081), served at /people and at its legacy door
3
+ /scouting, which loads the same page. Only the identity cell lives here: the
4
+ ledger, the filter bar, the pager, the chips and the empty state are the
5
+ shell's and the kit's. Tokens are the world's real names — no compat alias. */
6
+
7
+ /* The who cell: avatar, then the name over the login. A row that carries an
8
+ avatar centres its figures on it, so the numbers line up with the name. */
9
+ #people-body .ledger td { vertical-align: middle; }
10
+ .dir-who { display: inline-flex; align-items: center; gap: 11px; min-width: 0; }
11
+ .dir-avatar {
12
+ width: 30px; height: 30px; border-radius: 50%; flex: none;
13
+ object-fit: cover; background: var(--skeleton);
14
+ }
15
+ /* No avatar: the initial on the glass, never a broken image or a blank disc. */
16
+ .dir-avatar--initial {
17
+ display: inline-grid; place-items: center;
18
+ font-size: 12.5px; font-weight: 500; color: var(--ink-soft);
19
+ background: var(--glass-strong);
20
+ }
21
+ /* The name column keeps room for a login: on a phone the ledger scrolls in its
22
+ card (the shell's strip) rather than breaking a handle across two lines. */
23
+ .dir-who__t { display: flex; flex-direction: column; gap: 1px; min-width: 132px; }
24
+ .dir-name { font-weight: 500; color: var(--ink); overflow-wrap: anywhere; }
25
+ .dir-login { font-family: var(--font-mono); font-size: 11.5px; color: var(--ink-faint); }
26
+ .dir-crafts { display: inline-flex; flex-wrap: wrap; gap: 6px; }
27
+ .dir-none { color: var(--ink-faint); }
@@ -9,6 +9,9 @@
9
9
  <link rel="preconnect" href="https://fonts.googleapis.com">
10
10
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
11
11
  <link rel="stylesheet" href="/builders/style.css?v=2026-07-02-shell1">
12
+ <!-- task 1003470: the identity cell (avatar, name, login) is the one thing the
13
+ directory adds to the shell's ledger. -->
14
+ <link rel="stylesheet" href="/builders/people.css">
12
15
  </head>
13
16
  <body data-page="people" data-page-title="People">
14
17
 
@@ -18,19 +21,20 @@
18
21
  <div class="app-body">
19
22
  <header class="app-topbar" id="app-topbar"></header>
20
23
  <div class="app-content">
21
- <!-- task 1003307: the board page head. People shipped with NO <h1> at all
22
- (one-header-main-h1 measured 0 on every render), so the re-skin
23
- carries the structural fix rather than porting the hole. The heading
24
- is the LABEL voice, not a display line: on a working surface the
25
- page's own name is the least interesting thing on it. -->
24
+ <!-- The board page head (task 1003307): the label voice, because on a
25
+ working surface the page's own name is the least interesting thing on
26
+ it. The sub is rewritten by people.js once the directory has loaded. -->
26
27
  <div class="page-head page-head--board">
27
28
  <h1 class="page-head__h">People</h1>
28
- <p class="page-head__sub">Everyone building across hosted projects, ranked by track record. Descriptive only — cross-project numbers never carry authority here.</p>
29
+ <p class="page-head__sub" id="people-sub">Everyone building across hosted projects, ranked by track record. Descriptive only — cross-project numbers never carry authority here.</p>
29
30
  </div>
30
31
 
31
32
  <main id="hall-main">
33
+ <!-- task 1003470: ONE directory. /people and its legacy door /scouting
34
+ render this same page from the same script — the same
35
+ /api/gds/scouting rollup used to be drawn three different ways. -->
32
36
  <section class="scroll" id="people-scroll" aria-labelledby="people-h">
33
- <h2 id="people-h" class="scroll__h">Builder directory</h2>
37
+ <h2 id="people-h" class="scroll__h">Builder directory<span class="count-pill" id="people-count" hidden></span></h2>
34
38
  <div id="people-filters"></div>
35
39
  <div id="people-body">
36
40
  <div class="profile__placeholder">Loading the directory…</div>
@@ -50,6 +54,10 @@
50
54
  <script src="/builders/shell.js?v=2026-07-02-shell1"></script>
51
55
  <!-- jump palette (Ctrl/Cmd-K). Optional by design: without it the shell's slot stays hidden. -->
52
56
  <script src="/builders/palette.js" defer></script>
57
+ <!-- shared frontend toolkit (window.OTB — rank labels, number formats) + the
58
+ branding pack's currency label, loaded BEFORE people.js. -->
59
+ <script src="/builders/dom-utils.js?v=2026-06-14-domutils"></script>
60
+ <script src="/builders/brand-apply.js"></script>
53
61
  <script src="/builders/hall-kit.js"></script>
54
62
  <script src="/builders/people.js"></script>
55
63
  </body>