@bongos/core 1.19.440 → 1.19.442
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 +71 -31
- package/clients/bongos-client/index.d.ts +1 -2
- package/docs/api/openapi.json +3 -20
- package/docs/api-reference.md +1 -1
- package/docs/copy-inventory.md +180 -173
- package/docs/copy-registry.json +300 -226
- package/docs/design/mocks/hub/mine.html +89 -1
- package/docs/design/projects-hub-direction-v2.md +59 -0
- package/docs/module-api-changelog.md +4 -0
- package/modules/platform-identity/routes/projects.js +2 -1
- package/modules/provisioning/provisioning.js +11 -2
- package/modules/provisioning/routes/provisioning.js +10 -5
- package/modules/ui-design/kit/fixtures/featured-hub.json +1 -1
- package/modules/ui-design/kit/serve.js +29 -4
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/public-landing/assets/cosmos.css +89 -65
- package/public-landing/assets/world/r4-card-chrome.jpg +0 -0
- package/public-landing/assets/world/r4-card-chrome.jpg.json +14 -0
- package/public-landing/assets/world/r4-card-deepfield.jpg +0 -0
- package/public-landing/assets/world/r4-card-deepfield.jpg.json +14 -0
- package/public-landing/assets/world/r4-card-dust.jpg +0 -0
- package/public-landing/assets/world/r4-card-dust.jpg.json +14 -0
- package/public-landing/assets/world/r4-card-nebula.jpg +0 -0
- package/public-landing/assets/world/r4-card-nebula.jpg.json +14 -0
- package/public-landing/projects-floor.states.json +28 -1
- package/public-landing/projects.html +201 -69
- package/public-landing/projects.probes.json +16 -2
- package/public-landing/projects.states.json +3 -1
- package/src/module-api.js +1 -1
- package/tests/projects_hub.mjs +173 -65
- package/tests/projects_hub_pre_uat.mjs +11 -9
- package/tests/provisioning_settings.mjs +49 -4
|
@@ -84,6 +84,36 @@ body{
|
|
|
84
84
|
.door.danger{border-color:color-mix(in srgb,var(--err) 45%,transparent);color:var(--err);}
|
|
85
85
|
.door.danger:hover{border-color:var(--err);}
|
|
86
86
|
|
|
87
|
+
/* ── the every-project table, in the ledger row's grammar (task 1003356): the
|
|
88
|
+
SAME tracks the mine row lays (72px planet, then 1.1fr 1.3fr 1fr 232px), the
|
|
89
|
+
facts as label-face lines, the module list where the mine row draws its rail,
|
|
90
|
+
and at most two doors. No header row, no cell borders: the sort left the
|
|
91
|
+
column head and became a hairline pill group, the mine view's own control. ── */
|
|
92
|
+
.tblSort{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:16px 0 8px;}
|
|
93
|
+
.tblSort .sLbl{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);text-transform:uppercase;color:var(--ink-faint);margin-right:6px;}
|
|
94
|
+
.tblSort .dir{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);}
|
|
95
|
+
.trows{list-style:none;margin:8px 0 0;padding:0;}
|
|
96
|
+
.trow{display:grid;grid-template-columns:72px minmax(0,1.1fr) minmax(0,1.3fr) minmax(0,1fr) 232px;grid-template-areas:"orb who facts runs doors" "orb who tag runs doors";gap:0 22px;align-items:center;padding:22px 0;border-top:1px solid var(--rule-soft);}
|
|
97
|
+
.trow>.worldOrb{grid-area:orb;}
|
|
98
|
+
.trow .tWho{grid-area:who;}
|
|
99
|
+
.trow .facts{grid-area:facts;align-self:end;}
|
|
100
|
+
.trow .ptag{grid-area:tag;align-self:start;margin:5px 0 0;font-size:13.5px;font-weight:400;line-height:1.45;color:var(--ink-soft);}
|
|
101
|
+
.trow .runs{grid-area:runs;}
|
|
102
|
+
.trow .doors{grid-area:doors;}
|
|
103
|
+
.trows .trow:last-child{border-bottom:1px solid var(--rule-soft);}
|
|
104
|
+
.tWho{display:grid;gap:8px;justify-items:start;align-content:center;min-width:0;}
|
|
105
|
+
.tWho .mname{font-family:var(--font-body);font-weight:500;font-size:17px;letter-spacing:-.015em;color:var(--ink);}
|
|
106
|
+
.tWho .chip{white-space:normal;line-height:1.3;}
|
|
107
|
+
.trow .runs{display:grid;gap:6px;min-width:0;}
|
|
108
|
+
.trow .runs .rLbl{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);text-transform:uppercase;color:var(--ink-faint);}
|
|
109
|
+
.trow .tags{display:flex;gap:6px;flex-wrap:wrap;}
|
|
110
|
+
.trow .tags span{font-family:var(--font-mono);font-size:10.5px;padding:2px 8px;border-radius:999px;border:1px solid var(--rule);color:var(--ink-faint);white-space:nowrap;}
|
|
111
|
+
.trow .pill{display:inline-block;font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);text-transform:uppercase;padding:3px 9px;border-radius:999px;border:1px solid var(--rule);color:var(--ink-faint);}
|
|
112
|
+
.trow .typeTag{font-family:var(--font-mono);font-size:10.5px;font-weight:500;color:var(--ink-soft);border:1px solid var(--rule);border-radius:999px;padding:2px 9px;}
|
|
113
|
+
.rowHall{display:inline-flex;align-items:center;justify-content:center;width:40px;height:40px;border:1px solid var(--rule);border-radius:var(--radius-pill);color:var(--ink-soft);}
|
|
114
|
+
.rowHall svg{width:20px;height:20px;fill:none;stroke:currentColor;stroke-width:1.4;}
|
|
115
|
+
.rowHall:hover{border-color:var(--ink-faint);color:var(--ink);}
|
|
116
|
+
|
|
87
117
|
/* ── v2 · the poster row: the planet large, the name in the display voice, the rail full width ── */
|
|
88
118
|
.prow{display:grid;grid-template-columns:132px 1fr auto;gap:0 28px;align-items:start;padding:30px 0;border-top:1px solid var(--rule-soft);}
|
|
89
119
|
.prow .pname{font-family:var(--font-display);font-weight:300;font-size:30px;letter-spacing:-.035em;line-height:1.04;text-transform:lowercase;color:var(--ink);margin:0 0 8px;}
|
|
@@ -146,6 +176,7 @@ body[data-state="list"] section[data-state="list"],
|
|
|
146
176
|
body[data-state="cards"] section[data-state="cards"],
|
|
147
177
|
body[data-state="manage"] section[data-state="manage"],
|
|
148
178
|
body[data-state="empty"] section[data-state="empty"]{display:block;}
|
|
179
|
+
body[data-state="table"] section[data-state="table"]{display:block;}
|
|
149
180
|
.vh{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;}
|
|
150
181
|
.skip{position:absolute;left:12px;top:-60px;background:var(--bg-card);color:var(--ink);padding:11px 18px;border-radius:999px;}
|
|
151
182
|
.skip:focus{top:12px;}
|
|
@@ -171,6 +202,9 @@ a:focus-visible,button:focus-visible,label:focus-within{outline:2px solid var(--
|
|
|
171
202
|
.choice label{border-left:0;padding-left:0;border-top:1px solid var(--rule-soft);}
|
|
172
203
|
.choice label:first-child{border-top:0;}
|
|
173
204
|
.planetRow{grid-template-columns:72px 1fr;}
|
|
205
|
+
.trow{grid-template-columns:56px 1fr;gap:14px 16px;padding:20px 0;}
|
|
206
|
+
.trow .worldOrb{--d:56px;}
|
|
207
|
+
.trow{grid-template-areas:"orb who" "tag tag" "facts facts" "runs runs" "doors doors";}
|
|
174
208
|
}
|
|
175
209
|
</style>
|
|
176
210
|
</head>
|
|
@@ -181,6 +215,8 @@ a:focus-visible,button:focus-visible,label:focus-within{outline:2px solid var(--
|
|
|
181
215
|
?state=cards the three row treatments side by side: v1 the ledger, v2 the poster row, v3 the shelf
|
|
182
216
|
?state=manage the manage page: the ledger of facts, the rail, the doors, hairline sections, the planet slot
|
|
183
217
|
?state=empty the mine view with no projects: a band of the Projects band's grammar
|
|
218
|
+
?state=table the every-project table (task 1003356) in the SAME ledger grammar: the masthead,
|
|
219
|
+
the sort as hairline pills, one row per project on the mine row's tracks
|
|
184
220
|
-->
|
|
185
221
|
<a class="skip" href="#main">Skip to content</a>
|
|
186
222
|
<header class="bar">Cloud Bongos</header>
|
|
@@ -352,6 +388,58 @@ a:focus-visible,button:focus-visible,label:focus-within{outline:2px solid var(--
|
|
|
352
388
|
</div>
|
|
353
389
|
</section>
|
|
354
390
|
|
|
391
|
+
<section data-state="table">
|
|
392
|
+
<div class="view">
|
|
393
|
+
<div class="tblMast">
|
|
394
|
+
<h2 class="tblTitle">every project</h2>
|
|
395
|
+
<span class="mastObj" aria-hidden="true"></span>
|
|
396
|
+
</div>
|
|
397
|
+
<div class="tblSort" role="group" aria-label="Sort the projects">
|
|
398
|
+
<span class="sLbl">sort</span>
|
|
399
|
+
<button type="button" class="hpill" aria-pressed="true">name <span class="dir">a to z</span></button>
|
|
400
|
+
<button type="button" class="hpill" aria-pressed="false">builders</button>
|
|
401
|
+
</div>
|
|
402
|
+
<ul class="trows">
|
|
403
|
+
<li class="trow">
|
|
404
|
+
<span class="worldOrb" data-shape="pair" data-material="chrome" data-style="plain" style="--acc:var(--accent);--d:64px"><span class="disc"></span></span>
|
|
405
|
+
<div class="tWho"><span class="mname">Cloud Bongos</span><span class="pill">this platform</span><span class="chip calm"><span class="cdot"></span>not checked</span></div>
|
|
406
|
+
<span class="ptag">the AI-first build platform this map is drawn on.</span><div class="facts"><span class="fact">9 builders</span><span class="fact">30+ shipped in 30 days</span><span class="fact">listed 12 jul 2026</span></div>
|
|
407
|
+
<div class="runs"><span class="rLbl">runs</span><span class="tags"><span>core</span></span></div>
|
|
408
|
+
<div class="doors"><a class="door" href="#">view</a><a class="rowHall" href="#" aria-label="Builders' hall" title="Builders' hall"><svg viewBox="0 0 20 20" aria-hidden="true"><path d="M2.8 7.4 10 3.5l7.2 3.9"/><path d="M5.2 8.8v5.6M10 8.8v5.6M14.8 8.8v5.6"/><path d="M3.4 16.3h13.2"/></svg></a></div>
|
|
409
|
+
</li>
|
|
410
|
+
<li class="trow">
|
|
411
|
+
<span class="worldOrb" data-shape="bead" data-material="chrome" data-style="wash" style="--acc:var(--terracotta);--d:64px"><span class="disc"></span></span>
|
|
412
|
+
<div class="tWho"><span class="mname">Off the Boats</span><span class="pill">you build here</span><span class="chip calm"><span class="cdot"></span>not checked</span></div>
|
|
413
|
+
<span class="ptag">A singular shop, and a broader videogame in a persistent ancient Greek world.</span><div class="facts"><span class="fact"><span class="typeTag">Game</span></span><span class="fact">11 builders</span><span class="fact">0 shipped in 30 days</span><span class="fact">listed 15 jul 2026</span></div>
|
|
414
|
+
<div class="runs"><span class="rLbl">runs</span><span class="tags"><span>art-pipeline</span><span>character-anim</span><span>demo</span><span>+1</span></span></div>
|
|
415
|
+
<div class="doors"><a class="door" href="#">view</a><a class="rowHall" href="#" aria-label="Builders' hall" title="Builders' hall"><svg viewBox="0 0 20 20" aria-hidden="true"><path d="M2.8 7.4 10 3.5l7.2 3.9"/><path d="M5.2 8.8v5.6M10 8.8v5.6M14.8 8.8v5.6"/><path d="M3.4 16.3h13.2"/></svg></a></div>
|
|
416
|
+
</li>
|
|
417
|
+
<li class="trow">
|
|
418
|
+
<span class="worldOrb" data-shape="pair" data-material="obsidian" data-style="tint" style="--acc:var(--sea);--d:64px"><span class="disc"></span></span>
|
|
419
|
+
<div class="tWho"><span class="mname">Demo</span><span class="pill">yours</span><span class="chip live"><span class="cdot"></span>live <span class="rec">checked 3m ago</span></span></div>
|
|
420
|
+
<span class="ptag">A live demo instance on Cloud Bongos.</span><div class="facts"><span class="fact">type not declared</span><span class="fact">builders not known</span><span class="fact">listed 14 jul 2026</span></div>
|
|
421
|
+
<div class="runs"><span class="rLbl">runs</span><span class="fact">not known</span></div>
|
|
422
|
+
<div class="doors"><a class="door" href="#">view</a></div>
|
|
423
|
+
</li>
|
|
424
|
+
<li class="trow">
|
|
425
|
+
<span class="worldOrb" data-shape="blob" data-material="frost" data-style="wash" style="--acc:var(--gold-soft);--d:64px"><span class="disc"></span></span>
|
|
426
|
+
<div class="tWho"><span class="mname">Emersonian Circles</span><span class="chip calm"><span class="cdot"></span>not checked</span></div>
|
|
427
|
+
<span class="ptag">An independent project built on Cloud Bongos.</span><div class="facts"><span class="fact">type not declared</span><span class="fact">1 builder</span><span class="fact">30+ shipped in 30 days</span><span class="fact">listed 25 jul 2026</span></div>
|
|
428
|
+
<div class="runs"><span class="rLbl">runs</span><span class="tags"><span>core</span></span></div>
|
|
429
|
+
<div class="doors"><a class="door" href="#">view</a></div>
|
|
430
|
+
</li>
|
|
431
|
+
<li class="trow">
|
|
432
|
+
<span class="worldOrb" data-shape="blob" data-material="chrome" data-style="tint" style="--acc:var(--sun-lit);--d:64px"><span class="disc"></span></span>
|
|
433
|
+
<div class="tWho"><span class="mname">Tidewater</span><span class="chip calm"><span class="cdot"></span>not checked</span></div>
|
|
434
|
+
<span class="ptag">A project that has declared nothing yet.</span><div class="facts"><span class="fact">type not declared</span><span class="fact">builders not known</span><span class="fact">listed 20 jul 2026</span></div>
|
|
435
|
+
<div class="runs"><span class="rLbl">runs</span><span class="fact">not known</span></div>
|
|
436
|
+
<div class="doors"><a class="door" href="#">view</a></div>
|
|
437
|
+
</li>
|
|
438
|
+
</ul>
|
|
439
|
+
<p class="vLede" role="status" style="margin-top:22px">Sorted by name, A to Z.</p>
|
|
440
|
+
</div>
|
|
441
|
+
</section>
|
|
442
|
+
|
|
355
443
|
<section data-state="empty">
|
|
356
444
|
<div class="view">
|
|
357
445
|
<div class="vHead"><h2 class="vTitle">your projects</h2></div>
|
|
@@ -369,7 +457,7 @@ a:focus-visible,button:focus-visible,label:focus-within{outline:2px solid var(--
|
|
|
369
457
|
No motion in script; nothing is built here. */
|
|
370
458
|
(function () {
|
|
371
459
|
var state = new URLSearchParams(location.search || location.hash.slice(1) || (window.__Q || '')).get('state') || 'list';
|
|
372
|
-
if (['list', 'cards', 'manage', 'empty'].indexOf(state) === -1) state = 'list';
|
|
460
|
+
if (['list', 'cards', 'manage', 'empty', 'table'].indexOf(state) === -1) state = 'list';
|
|
373
461
|
document.body.setAttribute('data-state', state);
|
|
374
462
|
})();
|
|
375
463
|
</script>
|
|
@@ -276,3 +276,62 @@ Three mastheads, rendered dark and light at 1440 / 390 / 320 (ALL CLEAN) and mea
|
|
|
276
276
|
**Retargeted.** `tests/projects_hub.mjs` (`renderManage` reads `addressRow(inst, true)`; the delete gate reads off `renderDelete`) and `tests/projects_hub_pre_uat.mjs` (the restart door's one home, the restart sender, the one remaining confirm, the field's move and its section, the F19 and correction doors, the minute tick, the manifest snag's link, the announcements, the no-address door). The copy registry regenerated.
|
|
277
277
|
|
|
278
278
|
**Flagged to the copy desk, not changed here.** The manage page's own site door still reads "Open site →" while the row now reads "View" — each reads naturally in place (the row pairs two doors, the page sits among verbs), so neither was churned. `stripMarkup`'s queued-flow notes still say "this card updates when it starts" on a page that is not a card. And a prose pointer inside a row's address line inherits the mono face meant for the address itself — true before this build too, and left alone rather than churned.
|
|
279
|
+
|
|
280
|
+
## 18. The every-project table takes the Your-projects row: the table is gone, the ledger stands (task 1003356)
|
|
281
|
+
|
|
282
|
+
**Date:** 2026-08-30 · the owner, after the Your-projects makeover (task 1003345) and the table's fixed tracks (task 1003348): *"also the your projects page has a better design than the every project table."* This is the every-project half. The masthead the owner picked in §9.4 (T1, the ledger, "every project") is untouched; what changed is everything under it.
|
|
283
|
+
|
|
284
|
+
**The round.** One reading, so no pick page (the round's own rule: a pick page only where two readings compete). A `?state=table` variant on `docs/design/mocks/hub/mine.html` — the sheet the Your-projects round already used, so the two views could be read side by side in one file — `check-mock` **ALL RULES PASS**, shot through the kit's stub at 1440 / 390 / 320 dark, **ALL CLEAN**. Two things the render caught before any page file was touched:
|
|
285
|
+
|
|
286
|
+
- The row's name and its pill were being clipped at 14ch and then hidden outright under 900px. `cosmos.css:593` and `:672` own a `.who` for the landing's face rows, and the mock's row had reached for the same name. Renamed `.tWho`. (The mine mock's `?state=list` has the same latent collision from its own round; the *shipped* mine view does not — it renders `.projHead` — so the mock is left as the record it is.)
|
|
287
|
+
- The tagline had no home in the first reading, and dropping it would have taken the only sentence saying what a project *is* out of the browse list. It went to the mine row's second line — the area the mine row gives its address — which is what made the two rows the same grammar rather than a family resemblance.
|
|
288
|
+
|
|
289
|
+
**The markup call, and why.** The task left the `<table>` to the builder: *"the `<table>` markup, its header row and cell borders go, or stay as the accessible structure under the row's look if the sort's a11y needs it."* **The table is gone.** Three reasons, in order of weight:
|
|
290
|
+
|
|
291
|
+
1. Once every fact is a self-describing label-face line stacked inside one track, the rows share no columns — so there is nothing for a header row to head, and `aria-sort` (valid only on a `columnheader`) has no host. A row is a record now, not a set of cells on shared axes, and the honest markup for a list of records is a list.
|
|
292
|
+
2. A `<table>` whose `<tr>` is `display:grid` loses its table role in every engine. Keeping the element would have meant re-declaring `role="table" / "row" / "cell"` by hand — markup wearing a role the layout had already taken off it.
|
|
293
|
+
3. The sort's accessibility is *better* off the header, not worse: two `aria-pressed` toggles in a `role="group"` — the mine view's own filter-pill pattern, already shipped and already keyboard-reachable — plus a `role="status"` sentence under the list (`#mapSorted`, "Sorted by name, a to z.") that states the live order for a reader who never saw the press. Pressing the pressed pill reverses, exactly as the column header did.
|
|
294
|
+
|
|
295
|
+
**What was built.** `#mapRows` is a `<ul class="tbl">` of `<li class="tblRow">`, one grid, five tracks — `72px minmax(0,1.1fr) minmax(0,1.3fr) minmax(0,1fr) 232px` — and two area rows, `"orb who facts runs doors" / "orb who tag runs doors"`: the same five tracks `#view-mine .proj` + `.projBody` compose, with **runs where the mine row draws its rail** and **the tagline where it puts its address**. The planet is the project's own (`planetAttrs(templateOf(p))` + `accentOf(p)`, the owner's choice first — task 1003347), decorative, since the name beside it is the row's door. The facts are `factLines()`: the declared type through `typeHtml`, builders, shipped in 30 days, the Listed date. At most two doors: `view` as the mine row's hairline pill, and the hall — member-gated and address-gated as before, now standing at the door's 40px rather than riding a text link's cell as a 28px glyph. Hairlines above each row, the last one closing; a row's hover is its hairline taking ink. The platform's own row keeps its static apex-only markup and its ids, and now paints its own shipped-in-30-days figure from the feed it was already reading — the callout §9.4's T3 flagged and never wired.
|
|
296
|
+
|
|
297
|
+
**The truths that had to survive the move, and how.** The columns drew a distinction the ledger had to keep in words:
|
|
298
|
+
|
|
299
|
+
| Fact | Was | Is |
|
|
300
|
+
|---|---|---|
|
|
301
|
+
| builders, null | `—` in the figure voice | `builders not known` |
|
|
302
|
+
| builders, 0 | `0` | `0 builders` — still a real answer, not an absence |
|
|
303
|
+
| type, not declared | `—` | `type not declared` (through the same `typeHtml`) |
|
|
304
|
+
| runs, null vs `[]` | `—` vs `core` | unchanged — under an explicit `runs` label, where a dash is the value of a labelled field rather than a sentence standing on its own |
|
|
305
|
+
| liveness, unprobed | `• —` | `• not checked` beside the name (v1's §5 proposal, taken now that the chip sits where a reader looks) |
|
|
306
|
+
| shipped / listed | never a column | rendered only where there is an answer — the poster card's own rule, so nothing new is claimed |
|
|
307
|
+
|
|
308
|
+
**Kept.** The masthead and its object; the sort keys and the comparator (unknown builders still sort to the bottom in both directions); the search in the bar; the platform's row pinned first, apex-only; an owned row's name as a manage anchor and its plain click routing to manage; the row-click-opens-the-card contract; `#sub-table-body`'s scroll as the floor under a very long row.
|
|
309
|
+
|
|
310
|
+
**The kit.** `render.js --page /projects` **ALL CLEAN** (13 states + a new `in-table-by-builders`), `--states projects-empty` and `--states projects-floor` ALL CLEAN, `probe.js --page /projects` **45/45 ALL PASS** (43 + two new: the pills carry the order and the sentence states it; pressing the pressed one reverses). `tests/projects_hub.mjs` 134/134, `projects_hub_pre_uat.mjs` 56/56, `<redacted>.mjs` 34/34, `fitness.js` all checks passed, the copy registry regenerated.
|
|
311
|
+
|
|
312
|
+
**Retargeted.** Every table pin: the self row's extraction (`<li class="tblRow" id="selfRow">`), the generated-row template, the six-cell parity test (now "every row lays the same five parts", which reads the planet expression too, so dropping the planet cannot pass), the column-source test (now fact-source), the fixed-tracks test (now asserts the two views' tracks line for line and that **no table rule outlived the table**), the `—`/0 pin, the Runs pin, `.rowHall`'s box, the placeholder-href guard, and `projects_hub_pre_uat.mjs`'s keyboard-sort test. One test is new: the sort control's own contract.
|
|
313
|
+
|
|
314
|
+
**Flagged to the copy desk, not changed here.** `typeHtml`'s "not declared" now speaks in the row's voice but is still the formatter the create wizard's review reads through `typeLabel`; the two are not in conflict, but a copy pass over the row's four fact lines as a set would be worth one sitting.
|
|
315
|
+
|
|
316
|
+
## 19. The card's backdrop: a plate set the owner picks, beside the planet (task 1003352)
|
|
317
|
+
|
|
318
|
+
**Date:** 2026-08-30 · deferred out of the planet round (§16) when the owner said, on that review, *"this could also house project card backdrop customization."* It waited because a card plate set is an art round of its own and the free image coverage had ended; the owner confirmed the spend for this session.
|
|
319
|
+
|
|
320
|
+
**The art round.** Four **5:2** bands — the shape a poster card's banner actually is. Round 3's four space plates are 16:9 field grounds; a 5:2 crop of one puts its density wherever it happens to fall, so the round generated for the card's own composition instead: the interest centre-right, the left third and both lower corners near-black, because the card puts its disc and name lower-left and its two figure callouts upper-left and lower-right.
|
|
321
|
+
|
|
322
|
+
| Key | What it is |
|
|
323
|
+
|---|---|
|
|
324
|
+
| `nebula` | a violet-and-bronze nebula wash, sparse stars — the field's own language in the card's shape |
|
|
325
|
+
| `dust` | warm bronze dust lanes, very low contrast; a haze rather than a shape |
|
|
326
|
+
| `deepfield` | a deep field of very distant galaxies on near-black; the quietest of the four |
|
|
327
|
+
| `chrome` | a sweeping ribbon of liquid chrome — the world's own material with no object in it |
|
|
328
|
+
|
|
329
|
+
**Two things worth recording about the round.** `nano_banana_pro` was asked first (the model round 3 used); after twenty-five minutes not one of its four jobs had left `waiting`, while the same four prompts on `soul_location` returned in fifteen seconds — a stuck provider queue, not a bad request. And the nebula came back too dark to read at the card's 560px: a **gamma** lift (1.75) raises the wash without crushing the black the disc and the name sit on, which is what a contrast lift did instead when it was tried first. Both are in the sidecars, so the plates can be remade.
|
|
330
|
+
|
|
331
|
+
**The key is the planet's kind, not a new kind.** `card_backdrop` joins `PLANET_VOCAB` rather than getting a vocabulary of its own, because everything true of the planet is true of it: a closed list, no default, the platform's to draw, pushed to no instance, restarting nothing. That one line is the whole server change — `planetChoice()` loops the vocab, the settings route builds both its validator and its `settingKeys` from it, and `publicInstance()` spreads it — so the key reaches the row, the own-scoped read and the public wire with no new branch. Two edits beyond it: `planetChoicesForHosts` now admits a host that chose **only** a backdrop, and `/projects/featured` carries the third key.
|
|
332
|
+
|
|
333
|
+
**On the page.** `openCard` reads `backdropPlate(p.card_backdrop) || safeHttpUrl(p.art_url)` — the owner's plate stands in front of the project's own picture, and a key this platform does not draw resolves to `''` rather than a broken `src`. The picker is **its own section beside the planet**, not a control inside it: the owner's words were "beside the planet", and one heading is how a project is drawn *everywhere* while the other is one surface's backdrop, so folding them together would have made both headings lie. It is the planet section's twin otherwise — one band of hairline cells (five: "its own picture" and the four plates), the preview at the planet preview's width as a 5:2 band, one save door, the settings PATCH, the receipt the page repaints from. The preview's caption speaks for the ROW, so a pick that is ahead of the row reads **"not saved yet"** rather than "nothing chosen" under a plate the owner can already see.
|
|
334
|
+
|
|
335
|
+
**What the grade caught, and the class it closed.** The first round shipped `card_backdrop` into `PLANET_VOCAB` and stopped there — and the settings route's `validateOrRespond` schema was a **hand-written field list**, not the vocabulary. Strict mode rejects any field a schema does not declare, so `settingKeys` would have copied the key that the validator had already 400'd: every real save from the manage page would have failed. Nothing caught it, because the kit's stub accepted any body key, so 47 probes and a clean render all passed against a request the server refuses. Both halves are fixed rather than patched: the route's schema is now **derived from the vocabularies** (`vocabSchema(SETTINGS_VOCAB)` + `vocabSchema(PLANET_VOCAB)`), so a key can no longer exist in one and not the other; the stub validates against the same closed lists (copied — a kit module may not import core — and pinned to their source by a test); and `provisioning_settings.mjs` now PATCHes the **real** router with `card_backdrop`, refuses an off-vocabulary one, and walks every declared key of both vocabularies asserting a 200, which is the guard that would have failed on the first round.
|
|
336
|
+
|
|
337
|
+
**The kit.** The read path renders where the hub's own catalog is served — `projects-floor.states.json` is the sibling allowed to alias a fixture, so `featured-hub.json` gives Example a `card_backdrop` and the new `card-backdrop` state pins the plate's `src` on the card. `render.js --page /projects` **ALL CLEAN** (with `in-manage-card`), the two sibling states files ALL CLEAN, `probe.js` **47/47** (two new: the pick saves with the route's no-restart receipt; handing it back restores the project's own picture). `projects_hub` 138/138, `projects_hub_pre_uat` 56/56, `<redacted>` 34/34, `provisioning_settings` 26/26, `provisioning_settings_env` 12/12, `validate-design --strict` valid, `fitness` all checks passed, the copy registry regenerated. DESIGN.md carries the round-4 table.
|
|
@@ -1317,5 +1317,9 @@ is load-bearing: the script throws rather than guess if it is missing, and
|
|
|
1317
1317
|
landed since 1.19.438 with no explicit bump. run 33290633434. (task 1002620)
|
|
1318
1318
|
1.19.440 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1319
1319
|
landed since 1.19.439 with no explicit bump. run 33321131006. (task 1002620)
|
|
1320
|
+
1.19.441 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1321
|
+
landed since 1.19.440 with no explicit bump. run 33323478836. (task 1002620)
|
|
1322
|
+
1.19.442 — CI auto-patch (publish-on-merge, ADR 0161): carrier for merges
|
|
1323
|
+
landed since 1.19.441 with no explicit bump. run 33326781131. (task 1002620)
|
|
1320
1324
|
---------------------------------------------------------------------------
|
|
1321
1325
|
```
|
|
@@ -148,7 +148,8 @@ module.exports = function projectsRoutes() {
|
|
|
148
148
|
// the wire always carries both keys: null = the platform's own pick
|
|
149
149
|
const planetOf = (p) => {
|
|
150
150
|
const c = (planets && planets[hostOfOrigin(p.origin)]) || {};
|
|
151
|
-
return { planet_template: c.planet_template || null, planet_accent: c.planet_accent || null
|
|
151
|
+
return { planet_template: c.planet_template || null, planet_accent: c.planet_accent || null,
|
|
152
|
+
card_backdrop: c.card_backdrop || null };
|
|
152
153
|
};
|
|
153
154
|
// The enrichment's keys (builders, tags, owner, owner_avatar, faces,
|
|
154
155
|
// shipped_30d, shipped_30d_capped, hall_origin) never collide with the
|
|
@@ -142,7 +142,16 @@ const PLANET_TEMPLATES = Object.freeze([
|
|
|
142
142
|
...PLANET_SHAPES.flatMap((s) => PLANET_MATERIALS.flatMap((m) => ['tint', 'wash', 'plain'].map((y) => `${s}-${m}-${y}`))),
|
|
143
143
|
]);
|
|
144
144
|
const PLANET_ACCENTS = Object.freeze(['sea', 'terracotta', 'gold-soft', 'sun-lit', 'accent']);
|
|
145
|
-
|
|
145
|
+
// The card's backdrop (task 1003352) is the same KIND of key as the planet, so it
|
|
146
|
+
// rides the same vocab: a closed list of platform-drawn plates, null = the
|
|
147
|
+
// project's own art (or none). It is not a policy either — nothing is pushed to
|
|
148
|
+
// the instance and nothing restarts; the card simply reads the row.
|
|
149
|
+
const CARD_BACKDROPS = Object.freeze(['nebula', 'dust', 'deepfield', 'chrome']);
|
|
150
|
+
// Named PLANET_VOCAB for the keys it began with; what it actually holds is every
|
|
151
|
+
// key the PLATFORM draws a project with — no default, no env transport, no restart.
|
|
152
|
+
const PLANET_VOCAB = Object.freeze({
|
|
153
|
+
planet_template: PLANET_TEMPLATES, planet_accent: PLANET_ACCENTS, card_backdrop: CARD_BACKDROPS,
|
|
154
|
+
});
|
|
146
155
|
|
|
147
156
|
// The optional project-detail interview (BV1.R15, task 1002334) — the "tell us
|
|
148
157
|
// about your project" step every creation form offers and every owner may skip.
|
|
@@ -1088,7 +1097,7 @@ async function planetChoicesForHosts(db, hosts) {
|
|
|
1088
1097
|
for (const r of rows) {
|
|
1089
1098
|
if (out[r.host]) continue;
|
|
1090
1099
|
const choice = planetChoice(r);
|
|
1091
|
-
if (
|
|
1100
|
+
if (Object.values(choice).some(Boolean)) out[r.host] = choice;
|
|
1092
1101
|
}
|
|
1093
1102
|
return out;
|
|
1094
1103
|
}
|
|
@@ -796,12 +796,17 @@ module.exports = function provisioningRoutes() {
|
|
|
796
796
|
router.patch('/provisioning/instances/:id/settings', auth.requireBuilder, async (req, res) => {
|
|
797
797
|
const id = parseId(req, res); // (req, res) — parseId sends 400 bad_id + returns null on failure
|
|
798
798
|
if (!id) return;
|
|
799
|
+
// The schema IS the vocabularies, not a copy of them (task 1003352): strict
|
|
800
|
+
// mode rejects any field the schema does not declare, so a key added to a
|
|
801
|
+
// vocabulary but forgotten here reaches settingKeys and then 400s on every
|
|
802
|
+
// real save — which is exactly what card_backdrop did until the grader caught
|
|
803
|
+
// it. Deriving both from the same source makes that drift unrepresentable.
|
|
804
|
+
// The platform-drawn keys are nullable on purpose: null hands the pick back.
|
|
805
|
+
const vocabSchema = (vocab) => Object.fromEntries(
|
|
806
|
+
Object.entries(vocab).map(([key, values]) => [key, { type: 'string', enum: values }]));
|
|
799
807
|
if (validateOrRespond(req, res, {
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
// the planet (task 1003347): nullable on purpose — null hands the pick back to the platform
|
|
803
|
-
planet_template: { type: 'string', enum: provisioning.PLANET_VOCAB.planet_template },
|
|
804
|
-
planet_accent: { type: 'string', enum: provisioning.PLANET_VOCAB.planet_accent },
|
|
808
|
+
...vocabSchema(provisioning.SETTINGS_VOCAB),
|
|
809
|
+
...vocabSchema(provisioning.PLANET_VOCAB),
|
|
805
810
|
})) return;
|
|
806
811
|
const settingKeys = [...Object.keys(provisioning.SETTINGS_VOCAB), ...Object.keys(provisioning.PLANET_VOCAB)];
|
|
807
812
|
const patch = {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"_":"The hub's catalog (task 1003345): the landing's featured.json plus Tidewater, an all-null row nobody owns — the card-floor state opens ITS card, because Demo is owned by the signed-in stub (provisioning-instances.json) and an owned orb's plain click opens the manage view, never the card. Served under the name \"featured\" by projects.states.json's stub.fixtures alias; the landing keeps featured.json.","projects":[{"origin":"https://amazonprimea.com","name":"Off the Boats","tagline":"A singular shop, and a broader videogame in a persistent ancient Greek world that collides with modern day commerce & products.","art_url":"/assets/world/r2-chrome-blob-one.jpg","type":"game","template":"bead-wash","planet_template":null,"planet_accent":null,"created_at":"2026-07-15T22:12:57.732Z","builders":11,"tags":["art-pipeline","character-anim","demo","game"],"owner":"lars589","owner_avatar":"https://avatars.githubusercontent.com/u/283020391?v=4","faces":["https://avatars.githubusercontent.com/u/283020391?v=4","https://avatars.githubusercontent.com/u/64553469?v=4","https://avatars.githubusercontent.com/u/290184380?v=4","https://avatars.githubusercontent.com/u/295990394?v=4","https://avatars.githubusercontent.com/u/35337514?v=4"],"shipped_30d":0,"shipped_30d_capped":false,"hall_origin":"https://builders.amazonprimea.com"},{"origin":"https://demo.cloudbongos.com","name":"Demo","tagline":"A live demo instance on Cloud Bongos.","art_url":null,"type":null,"template":"pair-gold-wash","planet_template":"pair-obsidian-tint","planet_accent":"sea","created_at":"2026-07-14T19:16:38.160Z","builders":null,"tags":null,"owner":null,"owner_avatar":null,"faces":null,"shipped_30d":null,"shipped_30d_capped":null,"hall_origin":null},{"origin":"https://emersonian-circles.cloudbongos.com","name":"Emersonian Circles","tagline":"An independent project built on Cloud Bongos.","art_url":null,"type":null,"template":null,"planet_template":null,"planet_accent":null,"created_at":"2026-07-25T02:21:55.641Z","builders":1,"tags":[],"owner":"lars589","owner_avatar":"https://avatars.githubusercontent.com/u/283020391?v=4","faces":[],"shipped_30d":30,"shipped_30d_capped":true,"hall_origin":null},{"origin":"https://tidewater.cloudbongos.com","name":"Tidewater","tagline":"A project that has declared nothing yet.","art_url":null,"type":null,"template":null,"planet_template":null,"planet_accent":null,"created_at":"2026-07-20T08:00:00.000Z","builders":null,"tags":null,"owner":null,"owner_avatar":null,"faces":null,"shipped_30d":null,"shipped_30d_capped":null,"hall_origin":null}]}
|
|
1
|
+
{"_":"The hub's catalog (task 1003345): the landing's featured.json plus Tidewater, an all-null row nobody owns — the card-floor state opens ITS card, because Demo is owned by the signed-in stub (provisioning-instances.json) and an owned orb's plain click opens the manage view, never the card. Served under the name \"featured\" by projects.states.json's stub.fixtures alias; the landing keeps featured.json.","projects":[{"origin":"https://amazonprimea.com","name":"Off the Boats","tagline":"A singular shop, and a broader videogame in a persistent ancient Greek world that collides with modern day commerce & products.","art_url":"/assets/world/r2-chrome-blob-one.jpg","type":"game","template":"bead-wash","planet_template":null,"planet_accent":null,"card_backdrop":"nebula","created_at":"2026-07-15T22:12:57.732Z","builders":11,"tags":["art-pipeline","character-anim","demo","game"],"owner":"lars589","owner_avatar":"https://avatars.githubusercontent.com/u/283020391?v=4","faces":["https://avatars.githubusercontent.com/u/283020391?v=4","https://avatars.githubusercontent.com/u/64553469?v=4","https://avatars.githubusercontent.com/u/290184380?v=4","https://avatars.githubusercontent.com/u/295990394?v=4","https://avatars.githubusercontent.com/u/35337514?v=4"],"shipped_30d":0,"shipped_30d_capped":false,"hall_origin":"https://builders.amazonprimea.com"},{"origin":"https://demo.cloudbongos.com","name":"Demo","tagline":"A live demo instance on Cloud Bongos.","art_url":null,"type":null,"template":"pair-gold-wash","planet_template":"pair-obsidian-tint","planet_accent":"sea","created_at":"2026-07-14T19:16:38.160Z","builders":null,"tags":null,"owner":null,"owner_avatar":null,"faces":null,"shipped_30d":null,"shipped_30d_capped":null,"hall_origin":null},{"origin":"https://emersonian-circles.cloudbongos.com","name":"Emersonian Circles","tagline":"An independent project built on Cloud Bongos.","art_url":null,"type":null,"template":null,"planet_template":null,"planet_accent":null,"created_at":"2026-07-25T02:21:55.641Z","builders":1,"tags":[],"owner":"lars589","owner_avatar":"https://avatars.githubusercontent.com/u/283020391?v=4","faces":[],"shipped_30d":30,"shipped_30d_capped":true,"hall_origin":null},{"origin":"https://tidewater.cloudbongos.com","name":"Tidewater","tagline":"A project that has declared nothing yet.","art_url":null,"type":null,"template":null,"planet_template":null,"planet_accent":null,"created_at":"2026-07-20T08:00:00.000Z","builders":null,"tags":null,"owner":null,"owner_avatar":null,"faces":null,"shipped_30d":null,"shipped_30d_capped":null,"hall_origin":null}]}
|
|
@@ -137,15 +137,40 @@ const ASKED = new Map();
|
|
|
137
137
|
// the run — the page repaints from the row it re-reads, so the row must carry the choice; a
|
|
138
138
|
// PATCH with only planet keys gets the route's no-restart receipt, a policy key the push
|
|
139
139
|
const PLANETS = new Map();
|
|
140
|
-
const PLANET_KEYS = ['planet_template', 'planet_accent'];
|
|
141
|
-
const NO_PLANET = { planet_template: null, planet_accent: null };
|
|
140
|
+
const PLANET_KEYS = ['planet_template', 'planet_accent', 'card_backdrop'];
|
|
141
|
+
const NO_PLANET = { planet_template: null, planet_accent: null, card_backdrop: null };
|
|
142
142
|
function readBody(req, cb) {
|
|
143
143
|
let d = '';
|
|
144
144
|
req.on('data', (c) => { d += c; });
|
|
145
145
|
req.on('end', () => { let b = {}; try { b = JSON.parse(d || '{}'); } catch { b = {}; } cb(b); });
|
|
146
146
|
}
|
|
147
|
+
// The real route validates STRICTLY against its vocabularies (an undeclared key
|
|
148
|
+
// or an off-vocabulary value is a 400). The stub mirrored none of that, so the
|
|
149
|
+
// probes stayed green on a body the server would have refused — which is how a
|
|
150
|
+
// missing card_backdrop entry in the route's schema shipped past a full probe
|
|
151
|
+
// run (task 1003352, grade round 2). The lists are copied on purpose: a kit
|
|
152
|
+
// module may not import core. They are small, closed, and pinned by a test.
|
|
153
|
+
const STUB_VOCAB = {
|
|
154
|
+
platform_visibility: ['public', 'gated'],
|
|
155
|
+
joinability: ['open', 'apply', 'invite_only'],
|
|
156
|
+
planet_template: null, // the template set is long; shape is checked, not membership
|
|
157
|
+
planet_accent: ['sea', 'terracotta', 'gold-soft', 'sun-lit', 'accent'],
|
|
158
|
+
card_backdrop: ['nebula', 'dust', 'deepfield', 'chrome'],
|
|
159
|
+
};
|
|
160
|
+
function settingsRejection(body) {
|
|
161
|
+
for (const [key, value] of Object.entries(body)) {
|
|
162
|
+
if (!(key in STUB_VOCAB)) return `unknown field: ${key}`;
|
|
163
|
+
if (value === null) continue; // null hands a pick back
|
|
164
|
+
if (typeof value !== 'string') return `${key} must be a string`;
|
|
165
|
+
const allowed = STUB_VOCAB[key];
|
|
166
|
+
if (allowed && !allowed.includes(value)) return `${key} is not one of: ${allowed.join(', ')}`;
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
147
170
|
function settingsPatch(res, id, body) {
|
|
148
|
-
|
|
171
|
+
const bad = settingsRejection(body);
|
|
172
|
+
if (bad) return json(res, 400, { error: { code: 'validation_failed', message: bad } });
|
|
173
|
+
if (PLANET_KEYS.some((k) => k in body)) PLANETS.set(String(id), Object.fromEntries(PLANET_KEYS.map((k) => [k, body[k] || null])));
|
|
149
174
|
const policy = ['platform_visibility', 'joinability'].some((k) => k in body);
|
|
150
175
|
const push = policy ? FLOW_RECEIPTS.settings.push
|
|
151
176
|
: { queued: false, restart: false, message: 'Saved. The sky, your list and the card draw it this way from here; nothing on your project restarts for it.' };
|
|
@@ -154,7 +179,7 @@ function settingsPatch(res, id, body) {
|
|
|
154
179
|
// the row carries its planet: a saved pick for the run, else the fixture's own (or null)
|
|
155
180
|
function withPlanet(inst) {
|
|
156
181
|
const saved = PLANETS.get(String(inst.id));
|
|
157
|
-
return { ...inst,
|
|
182
|
+
return { ...inst, ...Object.fromEntries(PLANET_KEYS.map((k) => [k, inst[k] || null])), ...(saved || {}) };
|
|
158
183
|
}
|
|
159
184
|
function flowReceipt(rest, method) {
|
|
160
185
|
if (method === 'POST' && rest === '/provisioning/instances') return FLOW_RECEIPTS.provision;
|
package/package-lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.442",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@bongos/core",
|
|
9
|
-
"version": "1.19.
|
|
9
|
+
"version": "1.19.442",
|
|
10
10
|
"license": "AGPL-3.0-or-later",
|
|
11
11
|
"dependencies": {
|
|
12
12
|
"express": "^4.21.2",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bongos/core",
|
|
3
|
-
"version": "1.19.
|
|
3
|
+
"version": "1.19.442",
|
|
4
4
|
"description": "Cloud Bongos — the AI-first build platform core (GDS + platform surfaces + module system), installed as a versioned dependency (ADR 0108).",
|
|
5
5
|
"license": "AGPL-3.0-or-later",
|
|
6
6
|
"main": "src/platform-server.js",
|
|
@@ -862,73 +862,88 @@ p.sec-p{font-size:var(--fs-lede);font-weight:500;color:var(--ink-soft);line-heig
|
|
|
862
862
|
background-repeat:no-repeat;background-size:calc(var(--dia) * 6.89655) auto;
|
|
863
863
|
background-position:calc(var(--dia) * -5.58621) calc(var(--dia) * -2.14014);filter:var(--mast-shadow);}
|
|
864
864
|
|
|
865
|
-
|
|
866
|
-
.
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
.
|
|
870
|
-
.
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
.tbl
|
|
876
|
-
|
|
865
|
+
/* ── THE LEDGER ROW (task 1003356; the owner: "the your projects page has a
|
|
866
|
+
better design than the every project table"). The table is gone — not
|
|
867
|
+
restyled: once every fact is a self-describing label-face line inside the
|
|
868
|
+
row, the rows share no columns to head, and a <table> whose <tr> is a grid
|
|
869
|
+
loses its table role in every engine anyway. So the rows are a LIST, the
|
|
870
|
+
sort left the column head for a pill group (.tblSort), and the whole view
|
|
871
|
+
takes the Your-projects row's grammar (#view-mine .proj, direction §14.1):
|
|
872
|
+
the SAME five tracks, the planet as the row's object, the facts stacked,
|
|
873
|
+
the tagline where the mine row puts its address, the module list where the
|
|
874
|
+
mine row draws its rail, and at most two doors. ── */
|
|
875
|
+
.tbl{list-style:none;margin:0;padding:0;}
|
|
876
|
+
.tbl .tblRow{display:grid;
|
|
877
|
+
grid-template-columns:72px minmax(0,1.1fr) minmax(0,1.3fr) minmax(0,1fr) 232px;
|
|
878
|
+
grid-template-areas:"orb who facts runs doors" "orb who tag runs doors";
|
|
879
|
+
gap:0 22px;align-items:center;padding:22px 0;
|
|
880
|
+
border-top:1px solid var(--rule-soft);transition:border-color .2s cubic-bezier(.2,.7,.3,1);}
|
|
881
|
+
.tbl .tblRow:last-child{border-bottom:1px solid var(--rule-soft);}
|
|
877
882
|
/* the whole row opens the project card, so it must look like it does: the
|
|
878
|
-
hairline
|
|
879
|
-
.tbl
|
|
880
|
-
.tbl
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
.tbl .
|
|
883
|
+
hairline above it takes ink, as a band cell's does */
|
|
884
|
+
.tbl .tblRow{cursor:pointer;}
|
|
885
|
+
.tbl .tblRow:hover{border-top-color:var(--ink-faint);}
|
|
886
|
+
/* the planet, docked: the sky's object, relative instead of absolute, and the
|
|
887
|
+
row's decoration — the name beside it is the accessible door (#view-mine's rule) */
|
|
888
|
+
.tbl .worldOrb{grid-area:orb;position:relative;left:auto;top:auto;transform:none;display:block;
|
|
889
|
+
cursor:inherit;opacity:1;pointer-events:none;}
|
|
890
|
+
.tbl .worldOrb .disc{display:block;margin:0;}
|
|
891
|
+
.tbl .tWho{grid-area:who;display:grid;gap:8px;justify-items:start;align-content:center;min-width:0;}
|
|
892
|
+
.tbl .ptitle{font-family:var(--font-body);font-weight:500;font-size:17px;letter-spacing:-.015em;color:var(--ink);text-decoration:none;}
|
|
893
|
+
.tbl a.ptitle{display:inline-flex;align-items:center;min-height:24px;}
|
|
884
894
|
.tbl a.ptitle:hover{text-decoration:underline;}
|
|
885
|
-
.tbl .
|
|
886
|
-
|
|
887
|
-
|
|
895
|
+
.tbl .tWho .chip{white-space:normal;line-height:1.3;}
|
|
896
|
+
/* the facts: label-face lines, one fact each, the mine row's .projMeta */
|
|
897
|
+
.tbl .facts{grid-area:facts;align-self:end;display:grid;gap:5px;min-width:0;
|
|
898
|
+
font-family:var(--label-face);font-size:var(--label-size);font-weight:400;
|
|
899
|
+
letter-spacing:var(--label-space);text-transform:uppercase;color:var(--ink-faint);}
|
|
900
|
+
/* the tagline, where the mine row puts its address: the honest clause in the body face */
|
|
901
|
+
.tbl .ptag{grid-area:tag;align-self:start;margin:5px 0 0;font-size:13.5px;font-weight:400;
|
|
902
|
+
line-height:1.45;color:var(--ink-soft);}
|
|
903
|
+
/* Runs, where the mine row draws its rail: the module list under its own label */
|
|
904
|
+
.tbl .runs{grid-area:runs;display:grid;gap:6px;min-width:0;font-size:13.5px;color:var(--ink-soft);}
|
|
905
|
+
.tbl .rLbl{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);
|
|
906
|
+
text-transform:uppercase;color:var(--ink-faint);}
|
|
888
907
|
.tbl .tags{display:flex;gap:6px;flex-wrap:wrap;}
|
|
889
908
|
.tbl .tags span{font-family:var(--font-mono);font-size:10.5px;padding:2px 8px;border-radius:999px;
|
|
890
909
|
border:1px solid var(--rule);color:var(--ink-faint);white-space:nowrap;}
|
|
891
910
|
.tbl .pill{display:inline-block;font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);
|
|
892
911
|
text-transform:uppercase;padding:3px 9px;border-radius:999px;border:1px solid var(--rule);color:var(--ink-faint);}
|
|
893
|
-
/* the
|
|
894
|
-
|
|
895
|
-
.
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
.
|
|
901
|
-
/*
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
.
|
|
905
|
-
|
|
906
|
-
.
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
right-aligned column's header is right-aligned too. Below the table's minimum
|
|
914
|
-
the wrap scrolls (#sub-table-body), never the page. ── */
|
|
915
|
-
.tbl{table-layout:fixed;min-width:860px;}
|
|
916
|
-
.tbl th.colName{padding-left:22px;}
|
|
917
|
-
.tbl th.right{text-align:right;}
|
|
918
|
-
.tbl th.colType{width:88px;}
|
|
919
|
-
.tbl th.colBuilders{width:96px;}
|
|
920
|
-
.tbl th.colTags{width:168px;}
|
|
921
|
-
.tbl th.colStatus{width:224px;}
|
|
922
|
-
.tbl th.colGo{width:112px;}
|
|
912
|
+
/* the doors: hairline pills, lowercase, right of the row — the mine row's
|
|
913
|
+
.btn.small.ghost, with the hall glyph as the second (task 1003356) */
|
|
914
|
+
.tbl .rowDoors{grid-area:doors;display:flex;gap:8px;flex-wrap:wrap;justify-content:flex-end;align-items:center;}
|
|
915
|
+
.rowGo{display:inline-flex;align-items:center;justify-content:center;min-height:40px;padding:0 18px;
|
|
916
|
+
font-family:var(--font-body);font-weight:400;font-size:14.5px;text-transform:lowercase;
|
|
917
|
+
color:var(--ink);background:none;border:1px solid var(--rule);border-radius:var(--radius-pill);
|
|
918
|
+
text-decoration:none;white-space:nowrap;transition:border-color .15s ease;}
|
|
919
|
+
.rowGo:hover{border-color:var(--ink-faint);color:var(--ink);}
|
|
920
|
+
/* ── the sort, off the column head (task 1003356): the mine view's filter
|
|
921
|
+
pills — hairline, lowercase, the pressed one inverted to ink ── */
|
|
922
|
+
.tblSort{display:flex;align-items:center;gap:8px;flex-wrap:wrap;margin:0 0 8px;}
|
|
923
|
+
.tblSort .sortLbl{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);
|
|
924
|
+
text-transform:uppercase;color:var(--ink-faint);margin-right:6px;}
|
|
925
|
+
.tblSort button{display:inline-flex;align-items:center;gap:8px;min-height:36px;padding:0 15px;
|
|
926
|
+
font-family:var(--font-body);font-weight:400;font-size:14.5px;text-transform:lowercase;
|
|
927
|
+
color:var(--ink-soft);background:none;border:1px solid var(--rule);border-radius:var(--radius-pill);cursor:pointer;}
|
|
928
|
+
.tblSort button:hover{border-color:var(--ink-faint);color:var(--ink);}
|
|
929
|
+
.tblSort button[aria-pressed="true"]{background:var(--ink);border-color:var(--ink);color:var(--bg);}
|
|
930
|
+
.tblSort button:focus-visible{outline:2px solid var(--accent);outline-offset:2px;}
|
|
931
|
+
.tblSort .dir{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);}
|
|
923
932
|
/* the declared-type tag (task 1002772) — its own vocabulary: not the relationship
|
|
924
933
|
.pill, not the liveness .chip. Two different facts never wear the same chrome. */
|
|
925
934
|
.typeTag{font-family:var(--font-mono);font-size:10.5px;font-weight:500;color:var(--ink-soft);border:1px solid var(--rule);border-radius:999px;padding:2px 9px;white-space:nowrap;}
|
|
926
|
-
@media (max-width:780px){.tbl .colTags,.tbl .colType{display:none;}.tbl{min-width:600px;}}
|
|
927
935
|
@media (max-width:900px){
|
|
928
936
|
.tblMast{gap:16px;padding:22px 0 20px;}
|
|
929
937
|
.tblTitle{font-size:clamp(28px,7.4vw,34px);}
|
|
930
938
|
.mastObj{--dia:clamp(72px,18vw,96px);}
|
|
931
|
-
|
|
939
|
+
/* a phone stacks the row the way the Your-projects row stacks: the planet
|
|
940
|
+
and the name together, then everything else full width */
|
|
941
|
+
.tbl .tblRow{grid-template-columns:56px minmax(0,1fr);
|
|
942
|
+
grid-template-areas:"orb who" "tag tag" "facts facts" "runs runs" "doors doors";
|
|
943
|
+
gap:14px 16px;padding:20px 0;}
|
|
944
|
+
.tbl .tblRow .worldOrb{--d:56px;}
|
|
945
|
+
.tbl .facts,.tbl .ptag,.tbl .runs{align-self:auto;}
|
|
946
|
+
.tbl .rowDoors{justify-content:flex-start;}
|
|
932
947
|
}
|
|
933
948
|
|
|
934
949
|
/* ── the living sky (task 1002762): camera, spotlight, hover card ──
|
|
@@ -1071,10 +1086,13 @@ p.sec-p{font-size:var(--fs-lede);font-weight:500;color:var(--ink-soft);line-heig
|
|
|
1071
1086
|
.projCard .cardHall:hover{text-decoration-color:var(--accent-deep);}
|
|
1072
1087
|
.cardHall svg,.rowHall svg{width:17px;height:17px;fill:none;stroke:currentColor;stroke-width:1.5;
|
|
1073
1088
|
stroke-linecap:round;stroke-linejoin:round;}
|
|
1074
|
-
/* the
|
|
1075
|
-
.
|
|
1076
|
-
|
|
1077
|
-
.rowHall
|
|
1089
|
+
/* the row's second door (task 1003356): a member's hall, only where the hall is
|
|
1090
|
+
elsewhere. It stands beside "view" in the row's door slot, so it takes the
|
|
1091
|
+
door's hairline pill at the same 40px height rather than a glyph's 28px. */
|
|
1092
|
+
.rowHall{display:inline-flex;align-items:center;justify-content:center;width:44px;height:40px;
|
|
1093
|
+
color:var(--ink-soft);border:1px solid var(--rule);border-radius:var(--radius-pill);
|
|
1094
|
+
transition:border-color .15s ease,color .15s ease;}
|
|
1095
|
+
.rowHall:hover{border-color:var(--ink-faint);color:var(--ink);}
|
|
1078
1096
|
@media (max-width:640px){
|
|
1079
1097
|
.projCard h3{font-size:26px;padding:0 20px;}
|
|
1080
1098
|
.projCard .cardTag,.projCard .cardRel,.projCard .cardFaces,.projCard .cardFigs,.projCard .cardMeta,
|
|
@@ -1148,12 +1166,9 @@ p.sec-p{font-size:var(--fs-lede);font-weight:500;color:var(--ink-soft);line-heig
|
|
|
1148
1166
|
.mineBar .search .count{display:none;} /* the "All 16" pill already carries the number */
|
|
1149
1167
|
}
|
|
1150
1168
|
/* the carried surfaces: only the hit-area floors the world asks for (direction
|
|
1151
|
-
record §5, travelling with the pick) — the footer links
|
|
1152
|
-
|
|
1153
|
-
on a phone instead of pushing the page sideways */
|
|
1169
|
+
record §5, travelling with the pick) — the footer links; the sort pills carry
|
|
1170
|
+
their own 36px. The wrap keeps its scroll as the floor under a very long row. */
|
|
1154
1171
|
#sub-table-body{overflow-x:auto;}
|
|
1155
|
-
.tbl th button{min-height:24px;display:inline-flex;align-items:center;}
|
|
1156
|
-
.tbl a.ptitle{display:inline-flex;align-items:center;min-height:24px;}
|
|
1157
1172
|
.foot a,.finePrint a{display:inline-block;padding:3px 0;min-height:24px;}
|
|
1158
1173
|
/* ═══════════════════════════════════════════════════════════════════════
|
|
1159
1174
|
YOUR PROJECTS + THE MANAGE PAGE, IN THE WORLD (task 1003345; direction
|
|
@@ -1163,9 +1178,10 @@ p.sec-p{font-size:var(--fs-lede);font-weight:500;color:var(--ink-soft);line-heig
|
|
|
1163
1178
|
structure, the project's own planet as the row's object, the strip drawn
|
|
1164
1179
|
as the lifecycle rail, the doors as hairline pills with the accent only on
|
|
1165
1180
|
the one that acts. One set of FIXED tracks across every row, so the list
|
|
1166
|
-
reads as one ledger —
|
|
1167
|
-
in its `.tbl`
|
|
1168
|
-
1003348
|
|
1181
|
+
reads as one ledger — and since task 1003356 the every-project table lays
|
|
1182
|
+
the SAME five tracks in its `.tbl .tblRow` rule, so the two views are one
|
|
1183
|
+
grammar (task 1003348 aligned them; both halves are pinned together in
|
|
1184
|
+
tests/projects_hub.mjs). Dark only
|
|
1169
1185
|
(ADR 0204); every colour a token; halos are outlines, never box-shadow.
|
|
1170
1186
|
═══════════════════════════════════════════════════════════════════════ */
|
|
1171
1187
|
#view-mine .wrap,#view-manage .wrap{max-width:1180px;}
|
|
@@ -1361,6 +1377,14 @@ p.sec-p{font-size:var(--fs-lede);font-weight:500;color:var(--ink-soft);line-heig
|
|
|
1361
1377
|
#view-manage .pickPreview{display:grid;gap:12px;justify-items:center;padding-top:6px;}
|
|
1362
1378
|
#view-manage .pickWords{margin:0;font-size:14px;font-weight:400;line-height:1.45;color:var(--ink);text-align:center;max-width:168px;}
|
|
1363
1379
|
#view-manage .pickWords small{display:block;font-size:12.5px;color:var(--ink-soft);}
|
|
1380
|
+
/* the card's backdrop preview (task 1003352): the poster's own 5:2 band at the
|
|
1381
|
+
width the planet preview stands at, so the two sections read as one column.
|
|
1382
|
+
No ground of its own — "its own picture" is an empty hairline frame, which is
|
|
1383
|
+
the honest drawing of a project that may have no art at all. */
|
|
1384
|
+
#view-manage .cardBand{display:block;width:168px;aspect-ratio:5/2;background-size:cover;
|
|
1385
|
+
background-position:center;background-repeat:no-repeat;background-color:var(--bg);
|
|
1386
|
+
border:1px solid var(--rule-soft);}
|
|
1387
|
+
#view-manage .cardBand[data-own]{border-style:dashed;}
|
|
1364
1388
|
#view-manage .pickBands{display:grid;border-bottom:1px solid var(--rule);}
|
|
1365
1389
|
#view-manage .pickBand{display:grid;grid-template-columns:88px 1fr;gap:0 18px;align-items:center;border-top:1px solid var(--rule);}
|
|
1366
1390
|
#view-manage .pickLabel{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);text-transform:uppercase;color:var(--ink-faint);padding:14px 0;}
|
|
Binary file
|