@bongos/core 1.19.449 → 1.19.451

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.
@@ -0,0 +1,441 @@
1
+ /* room.css — the READING-ROOM archetype: a long document read end to end
2
+ (task 1003306, goal 1000081). Shared by the primer (primer.html) and the
3
+ diagrams index (diagrams.html).
4
+
5
+ THE ARCHETYPE is v3 "the glass hall", the owner's pick from the composition
6
+ round — docs/design/hall-direction-v2.md §6, mock
7
+ docs/design/mocks/hall/reading/v3.html. Reading order, top to bottom: a
8
+ progress hairline under the topbar; the room's name alone over the ground
9
+ (the shell's .page-head--room); then a GLASS table of contents beside a
10
+ --bg-card article card. The glass/card split is the composition's argument:
11
+ wayfinding is chrome and belongs to the same material as the rail and the
12
+ topbar, the document is content and belongs on a panel the ground can never
13
+ reach the ink on.
14
+
15
+ WHY IT IS ITS OWN SHEET rather than more of primer.css: the primer and the
16
+ diagrams index are the same room over different documents — one renders
17
+ markdown, one renders five figures — and the framed figure is literally the
18
+ same object on both (diagram-viewer.js upgrades `figure.diagram img,
19
+ figure.primer-figure img` with one selector). Ranks, Economy, Repo Atlas and
20
+ Modules inherit this archetype next. Page-specific shapes stay page-local:
21
+ primer.css keeps the acknowledge gate, diagrams.css keeps the legend strip.
22
+
23
+ WHY THE CLASSES ARE STILL `primer-`: two live surfaces spell them —
24
+ diagram-viewer.js's upgrade selector and tests/hall_audit.mjs's pinned
25
+ phone-overflow rule — so the sheet is named for the ROOM and the prefix is
26
+ left alone. Renaming would churn a JS selector and a pinned test to buy a
27
+ reader nothing.
28
+
29
+ THE PANEL IS THE SHELL'S. The article, the failure notes and the skeleton all
30
+ carry `.scroll` in their markup and this sheet only adds the reading padding:
31
+ v3 has exactly three card levels and a fourth is a design decision, not a
32
+ local override (modules/hall-ui/CLAUDE.md, Hard rules).
33
+
34
+ Everything below spends the world's real token names. No compat alias
35
+ (--surface / --surface-2 / --border / --border-strong / --accent-weak /
36
+ --accent-text) appears here — they exist for pages nobody has re-skinned, so
37
+ spending one is legal CSS that quietly opts this sheet back out of the world.
38
+ `--line` is a WIDTH; the hairline's colour is --rule / --rule-soft. */
39
+
40
+ /* ---- the room's composition tokens ---------------------------------------
41
+ On `body`, never in a page `:root`: the branding pack's :root is spliced at
42
+ the end of <head> at (0,1,0), so a page-local one is outranked one way and
43
+ defeats re-branding the other.
44
+ --measure is NOT redeclared. It is the shell's measured 48ch, and a `ch`
45
+ scales with the font size — so the same token renders the same ~60 characters
46
+ whether the room reads at 15px or 17px. Re-deriving it per page size is the
47
+ `ch` arithmetic that put 90 characters on this page in the first place. */
48
+ body.room {
49
+ --room-gap: var(--sp-lg);
50
+ --room-toc-w: 246px;
51
+ --room-pad: clamp(24px, 3.4vw, 58px);
52
+ --room-size: 17px;
53
+ --room-lead: 1.75;
54
+ --room-h2: clamp(24px, 2.8vw, 34px);
55
+ --room-h3: 20px;
56
+ }
57
+
58
+ /* ---- 1. the reading hairline --------------------------------------------
59
+ Sticky rather than fixed: it belongs to the content column, so it scrolls out
60
+ of the way with the head instead of hovering over the whole viewport. Under
61
+ the topbar in the z ladder (--z-progress < --z-topbar) for the same reason —
62
+ it is the article's progress, not app chrome. */
63
+ .primer-progress {
64
+ position: sticky;
65
+ top: var(--topbar-h);
66
+ z-index: var(--z-progress);
67
+ height: 3px;
68
+ background: color-mix(in srgb, var(--accent) 14%, transparent);
69
+ pointer-events: none;
70
+ }
71
+ .primer-progress__bar {
72
+ height: 100%;
73
+ width: 0;
74
+ background: var(--accent);
75
+ transition: width 200ms cubic-bezier(.2, .7, .3, 1);
76
+ }
77
+
78
+ /* ---- 2. the reading column ----------------------------------------------
79
+ ONE ceiling for the room, on the room's own body scope. --content-max (1120)
80
+ is a working surface's width; a document read at it is more empty ground than
81
+ page. The cap sits on the room's main rather than on each page's wrapper so
82
+ the primer's two columns and the diagrams index's stacked figures end at the
83
+ same edge — a reading room that changes width between its own pages reads as
84
+ two different rooms.
85
+ This is the room's own composition, not a shell override: it is scoped to the
86
+ body class a page opts into, and it sets a measure, not shell layout (the
87
+ rule tests/hall_record_world.mjs pins is `.app-content > main`, which reaches
88
+ into the shell's grid for every page at once). */
89
+ body.room #hall-main,
90
+ body.room .page-head { max-width: var(--room-max); }
91
+
92
+ .primer-layout {
93
+ display: grid;
94
+ grid-template-columns: var(--room-toc-w) minmax(0, 1fr);
95
+ gap: var(--room-gap);
96
+ align-items: start;
97
+ padding-bottom: var(--sp-md);
98
+ }
99
+ /* the article column, so the notes, the skeleton and the gate stack under the
100
+ card rather than beside the contents */
101
+ .primer-col { min-width: 0; }
102
+
103
+ /* ---- 3. the table of contents, as glass ---------------------------------- */
104
+
105
+ .primer-toc {
106
+ position: sticky;
107
+ top: calc(var(--topbar-h) + var(--sp-md));
108
+ align-self: start;
109
+ }
110
+ .primer-toc__toggle {
111
+ display: none;
112
+ width: 100%;
113
+ align-items: center;
114
+ justify-content: space-between;
115
+ gap: 8px;
116
+ min-height: 44px;
117
+ padding: 8px 16px;
118
+ background: var(--glass);
119
+ -webkit-backdrop-filter: blur(var(--glass-blur));
120
+ backdrop-filter: blur(var(--glass-blur));
121
+ border: var(--line) solid var(--glass-edge);
122
+ border-radius: var(--radius);
123
+ font-family: var(--font-body);
124
+ font-size: 14.5px;
125
+ color: var(--ink);
126
+ cursor: pointer;
127
+ }
128
+ .primer-toc__chevron { transition: transform 200ms cubic-bezier(.2, .7, .3, 1); }
129
+ .primer-toc__toggle[aria-expanded="true"] .primer-toc__chevron { transform: rotate(180deg); }
130
+ .primer-toc__inner {
131
+ background: var(--glass);
132
+ -webkit-backdrop-filter: blur(var(--glass-blur));
133
+ backdrop-filter: blur(var(--glass-blur));
134
+ border: var(--line) solid var(--glass-edge);
135
+ border-radius: var(--radius);
136
+ padding: var(--sp-md) var(--sp-sm) var(--sp-sm);
137
+ }
138
+ .primer-toc__heading {
139
+ margin: 0 0 var(--sp-sm);
140
+ padding: 0 var(--sp-xs);
141
+ font-family: var(--label-face);
142
+ font-size: var(--label-size);
143
+ font-weight: 400;
144
+ letter-spacing: var(--label-space);
145
+ text-transform: var(--label-case);
146
+ color: var(--ink-faint);
147
+ }
148
+ .primer-toc__list { list-style: none; margin: 0; padding: 0; counter-reset: toc; }
149
+ .primer-toc__list li { margin: 0; }
150
+ /* the numeral is a two-digit mono column, so a long list never re-spaces around
151
+ its tenth row the way an upper-roman VIII did */
152
+ .primer-toc__list a {
153
+ display: flex;
154
+ align-items: baseline;
155
+ gap: 11px;
156
+ min-height: 34px;
157
+ padding: 8px var(--sp-xs);
158
+ border-left: 2px solid transparent;
159
+ border-radius: var(--radius-sm);
160
+ font-size: 14px;
161
+ line-height: 1.4;
162
+ color: var(--ink-soft);
163
+ text-decoration: none;
164
+ transition: color 200ms cubic-bezier(.2, .7, .3, 1);
165
+ }
166
+ .primer-toc__list a::before {
167
+ counter-increment: toc;
168
+ content: counter(toc, decimal-leading-zero);
169
+ flex: none;
170
+ font: 400 11px/1.4 var(--font-mono);
171
+ font-variant-numeric: tabular-nums;
172
+ color: var(--ink-faint);
173
+ }
174
+ .primer-toc__list a:hover { color: var(--ink); text-decoration: none; }
175
+ .primer-toc__list a.is-active {
176
+ color: var(--ink);
177
+ background: var(--glass-strong);
178
+ border-left-color: var(--accent);
179
+ border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
180
+ }
181
+ .primer-toc__list a.is-active::before { color: var(--ink-soft); }
182
+ .primer-toc__placeholder {
183
+ padding: 8px var(--sp-xs);
184
+ font-size: 14px;
185
+ color: var(--ink-faint);
186
+ }
187
+
188
+ /* ---- 4. the article ------------------------------------------------------
189
+ The card itself is `.scroll`; this is the reading pad and the rhythm. */
190
+
191
+ .primer-article {
192
+ padding: var(--room-pad);
193
+ margin: 0;
194
+ counter-reset: sec;
195
+ font-size: var(--room-size);
196
+ line-height: var(--room-lead);
197
+ color: var(--ink);
198
+ }
199
+ /* PROSE is measured; a table, a figure and a code block may take the whole card,
200
+ and that width is the only reason the card is wider than the measure. */
201
+ .primer-article > p,
202
+ .primer-article > ul,
203
+ .primer-article > ol,
204
+ .primer-article > blockquote,
205
+ .primer-article > h2,
206
+ .primer-article > h3 { max-width: var(--measure); }
207
+
208
+ /* the document's own h1 is suppressed — the page head already names the room,
209
+ and a second h1 in <main> fails the one-header-main-h1 floor outright */
210
+ .primer-article h1 { display: none; }
211
+
212
+ .primer-article h2 {
213
+ margin: calc(var(--sp-xl) * 1.4) 0 var(--sp-md);
214
+ font-family: var(--font-display);
215
+ font-weight: 300;
216
+ font-size: var(--room-h2);
217
+ line-height: 1.06;
218
+ letter-spacing: -0.035em;
219
+ color: var(--ink);
220
+ /* anchor jumps land below the sticky top bar AND its hairline */
221
+ scroll-margin-top: calc(var(--topbar-h) + 28px);
222
+ }
223
+ /* the section number sits above the headline rather than beside it, so a long
224
+ title stays one uninterrupted shape at every width */
225
+ .primer-article h2::before {
226
+ counter-increment: sec;
227
+ content: counter(sec, decimal-leading-zero);
228
+ display: block;
229
+ margin-bottom: 14px;
230
+ font-family: var(--label-face);
231
+ font-size: var(--label-size);
232
+ letter-spacing: var(--label-space);
233
+ font-variant-numeric: tabular-nums;
234
+ color: var(--ink-faint);
235
+ }
236
+ .primer-article > h2:first-child { margin-top: 0; }
237
+ .primer-article h3 {
238
+ margin: var(--sp-lg) 0 var(--sp-xs);
239
+ font-family: var(--font-display);
240
+ font-weight: 400;
241
+ font-size: var(--room-h3);
242
+ letter-spacing: -0.02em;
243
+ color: var(--ink);
244
+ scroll-margin-top: calc(var(--topbar-h) + 28px);
245
+ }
246
+
247
+ .primer-article p { margin: 0 0 var(--sp-md); }
248
+ .primer-article p:last-child { margin-bottom: 0; }
249
+ /* the subtitle line at the very top of the document */
250
+ .primer-article > p > em:only-child { color: var(--ink-soft); }
251
+
252
+ .primer-article ul, .primer-article ol { margin: 0 0 var(--sp-lg); padding-left: 1.4em; }
253
+ .primer-article li { margin: 0 0 12px; }
254
+ .primer-article li:last-child { margin-bottom: 0; }
255
+ .primer-article li::marker { color: var(--ink-faint); }
256
+
257
+ /* 1px of vertical padding takes an inline link past the 24px floor without
258
+ costing the line its own leading (the .scroll__lede precedent, task 1003304) */
259
+ .primer-article a {
260
+ display: inline-block;
261
+ min-height: 24px;
262
+ padding: 1px 0;
263
+ color: var(--accent-ink);
264
+ text-decoration: underline;
265
+ text-underline-offset: 4px;
266
+ text-decoration-thickness: 1px;
267
+ }
268
+ .primer-article a:hover { color: var(--ink); }
269
+ .primer-article strong { font-weight: 600; color: var(--ink); }
270
+ .primer-article em { font-style: italic; }
271
+
272
+ .primer-article code {
273
+ font: 400 .85em/1.4 var(--font-mono);
274
+ background: var(--glass);
275
+ padding: 2px 6px;
276
+ border-radius: 4px;
277
+ }
278
+ .primer-article pre {
279
+ margin: 0 0 var(--sp-lg);
280
+ padding: var(--sp-md) var(--sp-lg);
281
+ overflow-x: auto;
282
+ background: var(--glass);
283
+ border: var(--line) solid var(--glass-edge);
284
+ border-radius: var(--radius);
285
+ }
286
+ .primer-article pre code {
287
+ background: none;
288
+ padding: 0;
289
+ border-radius: 0;
290
+ font-size: 13.5px;
291
+ line-height: 1.8;
292
+ color: var(--ink-soft);
293
+ }
294
+
295
+ /* blockquotes only render since task 1003306 unified the two markdown parsers —
296
+ primer.js had no blockquote rule at all and md-reader.js has always emitted
297
+ them, so the primer silently dropped every aside into a paragraph. */
298
+ .primer-article blockquote {
299
+ margin: var(--sp-lg) 0;
300
+ padding: var(--sp-md) var(--sp-lg);
301
+ background: var(--glass);
302
+ border: var(--line) solid var(--glass-edge);
303
+ border-left: 2px solid var(--rule);
304
+ border-radius: 0 var(--radius) var(--radius) 0;
305
+ color: var(--ink-soft);
306
+ }
307
+ .primer-article blockquote p { margin: 0; max-width: var(--measure); }
308
+
309
+ .primer-article hr {
310
+ border: none;
311
+ border-top: var(--line) solid var(--rule-soft);
312
+ margin: var(--sp-xl) 0;
313
+ }
314
+
315
+ .primer-article table {
316
+ width: 100%;
317
+ border-collapse: collapse;
318
+ margin: 0 0 var(--sp-lg);
319
+ font-size: 14.5px;
320
+ font-variant-numeric: tabular-nums;
321
+ }
322
+ .primer-article th, .primer-article td {
323
+ text-align: left;
324
+ vertical-align: top;
325
+ padding: 13px 16px 13px 0;
326
+ border-bottom: var(--line) solid var(--rule-soft);
327
+ }
328
+ .primer-article th {
329
+ font-family: var(--label-face);
330
+ font-size: var(--label-size);
331
+ font-weight: 400;
332
+ letter-spacing: var(--label-space);
333
+ text-transform: var(--label-case);
334
+ color: var(--ink-faint);
335
+ }
336
+ .primer-article tr:last-child td { border-bottom: 0; }
337
+
338
+ /* ---- 5. the framed figure ------------------------------------------------
339
+ ONE rule, both rooms: diagram-viewer.js upgrades `figure.diagram img,
340
+ figure.primer-figure img` into a pannable canvas, and until (or unless) the
341
+ SVG fetch succeeds the PNG stays in this frame. Glass rather than a card, so
342
+ a figure inside the article reads as inset and a figure on the diagrams index
343
+ reads as the same object. */
344
+ .primer-figure, figure.diagram {
345
+ margin: var(--sp-xl) 0;
346
+ padding: var(--sp-lg) var(--sp-md);
347
+ background: var(--glass);
348
+ border: var(--line) solid var(--glass-edge);
349
+ border-radius: var(--radius);
350
+ }
351
+ .primer-figure:last-child, figure.diagram:last-child { margin-bottom: 0; }
352
+ .primer-figure img, figure.diagram img {
353
+ display: block;
354
+ width: 100%;
355
+ max-width: 100%;
356
+ height: auto;
357
+ }
358
+ /* diagram-viewer.js inserts its canvas INSIDE the figure and hides the <img>,
359
+ so the frame has to belong to exactly one of them or the diagram ends up in
360
+ two nested boxes. The figure keeps it; the canvas gives its own up. (Its
361
+ fullscreen state re-declares both, and diagram-viewer.css loads after this
362
+ sheet on both pages, so the overlay still paints.) */
363
+ .primer-figure .dgv, figure.diagram .dgv {
364
+ background: none;
365
+ border: 0;
366
+ border-radius: var(--radius-sm);
367
+ }
368
+ .primer-figure figcaption, figure.diagram figcaption {
369
+ margin-top: 14px;
370
+ max-width: var(--measure);
371
+ font-size: 13.5px;
372
+ line-height: 1.55;
373
+ color: var(--ink-faint);
374
+ }
375
+
376
+ /* ---- 6. the states before the document ----------------------------------
377
+ Each is the same card the article is, so the page never changes shape as it
378
+ resolves — the skeleton is the article's silhouette, not a spinner. */
379
+
380
+ .primer-note, .primer-skeleton { padding: var(--room-pad); }
381
+ .primer-note__label {
382
+ margin: 0 0 14px;
383
+ font-family: var(--label-face);
384
+ font-size: var(--label-size);
385
+ letter-spacing: var(--label-space);
386
+ text-transform: var(--label-case);
387
+ color: var(--ink-faint);
388
+ }
389
+ .primer-note--error .primer-note__label { color: var(--danger); }
390
+ .primer-note__body { margin: 0 0 var(--sp-lg); max-width: var(--measure); color: var(--ink-soft); }
391
+ .primer-note__body:last-child { margin-bottom: 0; }
392
+ .primer-note__acts { display: flex; flex-wrap: wrap; gap: var(--sp-xs); margin: 0; }
393
+
394
+ .primer-sk {
395
+ display: block;
396
+ height: 10px;
397
+ border-radius: 4px;
398
+ background: var(--skeleton);
399
+ }
400
+ .primer-skeleton__h { height: 28px; width: 58%; margin-bottom: var(--sp-lg); }
401
+ .primer-skeleton__p { margin-bottom: var(--sp-lg); max-width: var(--measure); }
402
+ .primer-skeleton__p .primer-sk { margin-bottom: 15px; }
403
+ .primer-skeleton__p .primer-sk:nth-child(2) { width: 96%; }
404
+ .primer-skeleton__p .primer-sk:nth-child(3) { width: 99%; }
405
+ .primer-skeleton__p .primer-sk:nth-child(4) { width: 84%; }
406
+ .primer-skeleton__p .primer-sk:last-child { width: 46%; margin-bottom: 0; }
407
+ .primer-skeleton__rule {
408
+ height: var(--line);
409
+ margin: var(--sp-xl) 0 var(--sp-lg);
410
+ background: var(--rule-soft);
411
+ }
412
+
413
+ /* ---- 7. the room's one breakpoint ---------------------------------------
414
+ 760px, NOT the shell's 640: it is the width primer.js's matchMedia mirrors
415
+ and tests/hall_audit.mjs pins exactly. Narrowing it "for consistency" would
416
+ silently reopen the 641-760px band the table's scrollport exists to cover. */
417
+ @media (max-width: 760px) {
418
+ body.room {
419
+ --room-gap: 0px;
420
+ --room-pad: 20px;
421
+ --room-size: 16.5px;
422
+ --room-lead: 1.7;
423
+ --room-h2: 24px;
424
+ }
425
+ .primer-layout { grid-template-columns: minmax(0, 1fr); }
426
+ .primer-toc { position: static; margin-bottom: var(--sp-sm); }
427
+ .primer-toc__toggle { display: flex; }
428
+ .primer-toc__inner { display: none; margin-top: 8px; }
429
+ .primer-toc.is-open .primer-toc__inner { display: block; }
430
+ /* A markdown table's column count is fixed by its source, so its min-content
431
+ width outruns a phone (measured: 79px of page overflow on /drachmae). Give
432
+ the table its own horizontal scroller instead of letting it drag the page
433
+ sideways. `display: block` is what makes the table box a scrollport; the
434
+ rows still lay out through the anonymous table box inside it. */
435
+ .primer-article table {
436
+ display: block;
437
+ width: max-content;
438
+ max-width: 100%;
439
+ overflow-x: auto;
440
+ }
441
+ }
@@ -173,6 +173,11 @@
173
173
  --sidebar-w: var(--sidebar-w-rail);
174
174
  --rail-open: 246px;
175
175
  --content-max: 1120px;
176
+ /* the READING ceiling (task 1003306). A reading room is narrower than a
177
+ working surface: past ~1010px the article card is more empty ground than
178
+ page and reads as a void, so the room's head and its two columns share one
179
+ cap rather than each carrying a literal. */
180
+ --room-max: 1010px;
176
181
  /* the RIGHT rail — the room a page reserves beside its main column for a
177
182
  fixed side panel (the ideas composer's meter). Named here so the reserve
178
183
  and the panel are measured from one value; see `.app-content--railed`. */
@@ -206,12 +211,18 @@
206
211
  --shadow-pop: 0 18px 40px rgba(0, 0, 0, .28);
207
212
 
208
213
  /* ---- z ladder — content overlays (rank tips z-50, modals z-50, toast z-100,
209
- copy-toast z-1000, deeplink z-2000, diagram fullscreen z-9999) must beat
210
- the chrome; the mobile drawer sits above chrome but below dialogs. ---- */
214
+ copy-toast z-1000, deeplink z-2000) must beat the chrome; the mobile drawer
215
+ sits above chrome but below dialogs. The two ends belong to the reading
216
+ rooms and were hand-written numbers until task 1003306 named them: the
217
+ reading hairline sits UNDER the topbar so it reads as the article's
218
+ progress and not as app chrome, and the diagram's fullscreen canvas is the
219
+ top of the whole ladder because it covers the shell entirely. ---- */
220
+ --z-progress: 25;
211
221
  --z-topbar: 30;
212
222
  --z-sidebar: 40;
213
223
  --z-scrim: 140;
214
224
  --z-drawer: 150;
225
+ --z-diagram-full: 9999;
215
226
 
216
227
  /* ---- the hall's own plate: the declared seam, deferred by the owner
217
228
  ("decide after the shell lands", 2026-08-30). The hall's plate was
@@ -1270,6 +1281,53 @@ main:focus { outline: none; }
1270
1281
  }
1271
1282
  .page-head--board .page-head__sub { font-size: 14px; max-width: var(--measure); }
1272
1283
 
1284
+ /* The READING-ROOM page head (task 1003306, mock docs/design/mocks/hall/reading/v3.html).
1285
+ The board's head is quiet BECAUSE the board is a working surface and the page's
1286
+ name is the least interesting thing on it. A reading room is the opposite: the
1287
+ document IS the page, so its name is the largest thing in the first viewport,
1288
+ lowercase in the display face over the ground, with the label voice spent on a
1289
+ one-word eyebrow above it. Two voices, one per archetype — the divergence is
1290
+ the owner's pick, not drift.
1291
+ The sub takes --measure because it is body prose. The h1 does NOT: --measure is
1292
+ the BODY measure, and spending it on a display line caps it near 950px and lets
1293
+ the title run the column's width (tests/hall_record_world.mjs §8). A heading's
1294
+ cap is the room's own --room-max, which is a layout ceiling, not a measure.
1295
+ The modifier carries the VOICE only. The room's WIDTH belongs to room.css,
1296
+ which caps the head and the column it heads from one value — so a page that
1297
+ takes the voice without loading the archetype (Ranks) keeps the shell's width
1298
+ for both, and never ends up with a 1010px head over 1120px cards. */
1299
+ .page-head--room { padding: var(--sp-xl) 0 var(--sp-lg); margin: 0; }
1300
+ .page-head--room .page-head__eyebrow {
1301
+ margin: 0 0 14px;
1302
+ font-family: var(--label-face);
1303
+ font-size: var(--label-size);
1304
+ font-weight: 400;
1305
+ letter-spacing: var(--label-space);
1306
+ text-transform: var(--label-case);
1307
+ color: var(--ink-faint);
1308
+ }
1309
+ .page-head--room .page-head__h {
1310
+ margin: 0;
1311
+ font-family: var(--font-display);
1312
+ font-weight: 300;
1313
+ font-size: clamp(34px, 4.6vw, 58px);
1314
+ line-height: 1.02;
1315
+ letter-spacing: -0.042em;
1316
+ text-transform: lowercase;
1317
+ color: var(--ink);
1318
+ text-shadow: var(--type-halo);
1319
+ }
1320
+ .page-head--room .page-head__sub {
1321
+ margin: var(--sp-md) 0 0;
1322
+ max-width: var(--measure);
1323
+ font-size: 16px;
1324
+ color: var(--ink-soft);
1325
+ }
1326
+ @media (max-width: 760px) {
1327
+ .page-head--room { padding-top: var(--sp-lg); }
1328
+ .page-head--room .page-head__h { font-size: 34px; }
1329
+ }
1330
+
1273
1331
  .hall-nav {
1274
1332
  position: sticky;
1275
1333
  top: var(--topbar-h);
@@ -2345,7 +2403,17 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
2345
2403
  .task__body { grid-column: 1 / -1; }
2346
2404
  }
2347
2405
 
2348
- /* ---------- ranks page ---------- */
2406
+ /* ---------- ranks page ----------
2407
+ Re-skinned into the chrome world by task 1003306 (goal 1000081). Ranks is a
2408
+ reading room by archetype — a document about the ladder — so it takes the
2409
+ room's page head (.page-head--room) and the room's voice on its labels, but
2410
+ not room.css: it has no article, no table of contents and no figures, and a
2411
+ sheet loaded for one max-width is a request spent on nothing.
2412
+ The rank card is `.scroll--quiet`'s level expressed as a list row: a tone one
2413
+ step further from the ground than the panel it sits in, with the edge
2414
+ reserved as transparent so a modifier can draw one without the base having
2415
+ to. The compat aliases are retired here; the badges above still spend them,
2416
+ because they render on every page in the hall and belong to 1003308. */
2349
2417
 
2350
2418
  .ranks-standing {
2351
2419
  display: grid;
@@ -2354,47 +2422,51 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
2354
2422
  align-items: center;
2355
2423
  }
2356
2424
  .ranks-standing__badge { display: flex; }
2357
- .ranks-standing__title { font-weight: 650; font-size: 15px; }
2358
- .ranks-standing__blurb { grid-column: 1 / -1; font-size: 13.5px; color: var(--ink-soft); }
2425
+ .ranks-standing__title { font-family: var(--font-display); font-weight: 300; font-size: 19px; letter-spacing: -0.02em; }
2426
+ .ranks-standing__blurb { grid-column: 1 / -1; font-size: 13.5px; color: var(--ink-soft); max-width: var(--measure); }
2359
2427
  .ranks-standing__next { grid-column: 1 / -1; font-size: 13px; color: var(--ink-faint); }
2360
2428
  @media (max-width: 540px) { .ranks-standing { grid-template-columns: minmax(0, 1fr); } }
2361
2429
 
2362
2430
  .ranks-ladder { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
2363
2431
  .rank-card {
2364
- background: var(--surface);
2365
- border: 1px solid var(--border);
2432
+ background: var(--glass);
2433
+ border: var(--line) solid transparent;
2366
2434
  border-radius: var(--radius);
2367
- padding: 14px 16px;
2435
+ padding: var(--sp-sm) 16px;
2368
2436
  }
2369
- .rank-card--current { border-color: color-mix(in srgb, var(--accent) 45%, var(--border)); background: var(--accent-weak); }
2370
- .rank-card--next { border-style: dashed; }
2437
+ /* the reader's own rung is the one warm thing on the ladder */
2438
+ .rank-card--current { background: var(--accent-tint); border-color: color-mix(in srgb, var(--accent) 45%, transparent); }
2439
+ .rank-card--next { border-style: dashed; border-color: var(--rule); }
2371
2440
  .rank-card--sealed { opacity: 0.7; }
2372
2441
  .rank-card--placeholder { color: var(--ink-faint); }
2373
2442
  .rank-card__head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
2374
- .rank-card__title { font-weight: 650; font-size: 14px; }
2443
+ .rank-card__title { font-family: var(--font-display); font-weight: 400; font-size: 16px; letter-spacing: -0.02em; }
2375
2444
  .rank-card__status {
2376
2445
  margin-left: auto;
2377
- font-size: 11px;
2378
- font-weight: 600;
2379
- letter-spacing: 0.05em;
2380
- text-transform: uppercase;
2446
+ font-family: var(--label-face);
2447
+ font-size: var(--label-size);
2448
+ font-weight: 400;
2449
+ letter-spacing: var(--label-space);
2450
+ text-transform: var(--label-case);
2381
2451
  }
2382
- .rank-card__status--current { color: var(--accent-text); }
2452
+ .rank-card__status--current { color: var(--accent-ink); }
2383
2453
  .rank-card__status--next { color: var(--info); }
2384
2454
  .rank-card__status--sealed { color: var(--ink-faint); }
2385
- .rank-card__blurb { font-size: 13px; color: var(--ink-soft); margin: 6px 0; }
2455
+ .rank-card__blurb { font-size: 13px; color: var(--ink-soft); margin: 6px 0; max-width: var(--measure); }
2386
2456
  .rank-card__blurb--sealed { font-style: italic; }
2387
2457
  .rank-card__unlocks-label {
2388
- font-size: 11px;
2389
- font-weight: 600;
2390
- text-transform: uppercase;
2391
- letter-spacing: 0.06em;
2458
+ font-family: var(--label-face);
2459
+ font-size: var(--label-size);
2460
+ font-weight: 400;
2461
+ letter-spacing: var(--label-space);
2462
+ text-transform: var(--label-case);
2392
2463
  color: var(--ink-faint);
2393
- margin: 8px 0 4px;
2464
+ margin: 10px 0 4px;
2394
2465
  }
2395
- .rank-card__list { margin: 0; padding-left: 18px; font-size: 13px; color: var(--ink-soft); }
2466
+ .rank-card__list { margin: 0; padding-left: 18px; font-size: 13px; color: var(--ink-soft); max-width: var(--measure); }
2396
2467
  .rank-card__list li { margin: 2px 0; }
2397
- .ranks-rise__body { font-size: 13.5px; }
2468
+ .rank-card__list li::marker { color: var(--ink-faint); }
2469
+ .ranks-rise__body { font-size: 13.5px; max-width: var(--measure); }
2398
2470
  .ranks-rise__body p { margin: 8px 0; }
2399
2471
 
2400
2472
  /* ---------- toast + copy toast + deep-link overlay ---------- */
package/package-lock.json CHANGED
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "name": "@bongos/core",
3
- "version": "1.19.449",
3
+ "version": "1.19.451",
4
4
  "lockfileVersion": 3,
5
5
  "requires": true,
6
6
  "packages": {
7
7
  "": {
8
8
  "name": "@bongos/core",
9
- "version": "1.19.449",
9
+ "version": "1.19.451",
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.449",
3
+ "version": "1.19.451",
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",