@bongos/core 1.19.445 → 1.19.447

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 (45) hide show
  1. package/.bongos-core.json +67 -47
  2. package/docs/copy-inventory.md +354 -352
  3. package/docs/copy-registry.json +406 -388
  4. package/docs/module-api-changelog.md +4 -0
  5. package/modules/hall-ui/public/atlas.html +1 -2
  6. package/modules/hall-ui/public/collab.html +1 -2
  7. package/modules/hall-ui/public/copy-desk.css +11 -11
  8. package/modules/hall-ui/public/copy-desk.html +1 -2
  9. package/modules/hall-ui/public/diagrams.html +1 -2
  10. package/modules/hall-ui/public/drachmae.html +1 -2
  11. package/modules/hall-ui/public/gate.html +1 -2
  12. package/modules/hall-ui/public/goals.html +1 -2
  13. package/modules/hall-ui/public/government.html +1 -2
  14. package/modules/hall-ui/public/harbor.html +1 -2
  15. package/modules/hall-ui/public/idea.html +1 -2
  16. package/modules/hall-ui/public/ideas.css +6 -8
  17. package/modules/hall-ui/public/ideas.html +1 -2
  18. package/modules/hall-ui/public/index.html +3 -4
  19. package/modules/hall-ui/public/index.states.json +17 -0
  20. package/modules/hall-ui/public/modules.html +1 -2
  21. package/modules/hall-ui/public/my-projects.html +1 -2
  22. package/modules/hall-ui/public/not-ready.html +1 -2
  23. package/modules/hall-ui/public/pair.html +1 -2
  24. package/modules/hall-ui/public/people.html +1 -2
  25. package/modules/hall-ui/public/primer.html +1 -2
  26. package/modules/hall-ui/public/profile.html +1 -2
  27. package/modules/hall-ui/public/ranks.html +1 -2
  28. package/modules/hall-ui/public/roadmap.html +1 -2
  29. package/modules/hall-ui/public/scouting.html +1 -2
  30. package/modules/hall-ui/public/sessions.html +1 -2
  31. package/modules/hall-ui/public/settings.html +1 -2
  32. package/modules/hall-ui/public/shell.js +35 -2
  33. package/modules/hall-ui/public/style.css +1023 -455
  34. package/modules/hall-ui/public/task.html +1 -2
  35. package/modules/hall-ui/public/watch.css +1 -1
  36. package/modules/hall-ui/public/watch.html +1 -2
  37. package/modules/hall-ui/public/work.css +226 -100
  38. package/modules/hall-ui/public/work.html +15 -4
  39. package/modules/hall-ui/public/work.js +1 -1
  40. package/modules/hall-ui/public/work.states.json +19 -0
  41. package/package-lock.json +2 -2
  42. package/package.json +1 -1
  43. package/src/module-api.js +1 -1
  44. package/tests/hall_board_world.mjs +153 -0
  45. package/tests/hall_tokens.mjs +371 -0
@@ -1,132 +1,323 @@
1
- /* builders hall — the vanilla Cloud Bongos dashboard shell (task 1723).
2
- Clean, neutral, calm: app shell (fixed sidebar + sticky top bar + one
3
- window-scrolled content column) + a small reused component kit.
4
- Scope: docs/design/vanilla-hall-ui-redesign-scope.md.
5
-
6
- THEMING CONTRACT (do not rename): the 13 color vars + 2 font vars below are
7
- the instance-pack API — serve-internal.js themeMarkup() re-declares them from
8
- config/branding.*.json AFTER this file, so an instance's palette/fonts win.
9
- Everything else derives from them (color-mix) so any pack reskins the whole
10
- kit. The values here are the neutral vanilla defaults. */
1
+ /* builders hall — the vanilla Cloud Bongos dashboard shell (task 1723), rebuilt
2
+ into the chrome world by task 1003303 (goal 1000081).
3
+
4
+ THE WORLD. DESIGN.md at the repo root is this surface's world; the tier below
5
+ is its "Reuse: the hall and the status ledger" recipe followed literally, and
6
+ it is the tier the composition round authored and verified as
7
+ docs/design/mocks/hall/_tokens.css (task 1003302). The round's pick is v3
8
+ "the glass hall" — a 64px rail at every width, glass chrome, content on
9
+ --bg-card panels at the full radius with tone doing the separation. Record:
10
+ docs/design/hall-direction-v2.md.
11
+
12
+ THEMING CONTRACT (do not rename): the 13 color vars + 2 font vars at the head
13
+ of :root are the instance-pack API — serve-internal.js themeMarkup()
14
+ re-declares exactly these from config/branding.*.json AFTER this file, so an
15
+ instance's palette/fonts win. Their values here are branding.neutral.json's
16
+ theme.ui, byte for byte, so the served page and this sheet agree. Everything
17
+ else derives from them (var / color-mix), so any pack reskins the whole kit.
18
+
19
+ FOUR THINGS THIS TIER DOES THAT world.css DOES NOT, each a hall problem the
20
+ landing never had:
21
+
22
+ a. THE STATUS VOCABULARY IS FIXED, AND IT IS THE ONE SANCTIONED EXCEPTION TO
23
+ THE FIFTEEN RULE. DESIGN.md §Reuse: a surface that renders VERIFICATION
24
+ state — the hall's chips, CI marks — keeps its own green/amber/red and
25
+ must not adopt the world's warm accent-derived ok/warn/err. A grade that
26
+ failed must not be salmon on a page whose claim button is also salmon.
27
+ Three of the four values MOVED, and that is a measurement, not a taste:
28
+ the shipped values were tuned against the hall's retired ground
29
+ (--bg #f5f6f8), and against the world's darker paper (--bg #edecee) they
30
+ fail AA — ok 4.27:1, warn 4.13:1, info 3.85:1 and 4.36:1, and 4.19:1 on
31
+ the dark card. Re-solved here so all eight pairs clear 4.5:1 on both
32
+ grounds in both modes with ~9% of headroom. tests/hall_tokens.mjs measures
33
+ them; do not hand-tune one without re-running it.
34
+
35
+ b. THE HALL KEEPS BOTH MODES, ON ITS OWN SWITCH. The apex went dark only
36
+ (ADR 0204), so light is the hall's alone. shell.js writes
37
+ html[data-theme] — NOT the world's html[data-mode] — so the dark tier is
38
+ written twice on THAT pair: html[data-theme="dark"] and, for first paint
39
+ before the script runs, @media (prefers-color-scheme: dark)
40
+ html:not([data-theme]). Both bodies are byte-identical; the twins rule is
41
+ what keeps them so. html[data-theme="dark"] is (0,1,1) and outranks the
42
+ pack's injected :root (0,1,0), which is what lets dark win over the pack
43
+ while the instance's accent survives.
44
+
45
+ c. --line IS A WIDTH HERE, NOT A COLOUR (DESIGN.md warns about exactly this
46
+ name). The hairline's COLOUR is --rule / --rule-soft.
47
+
48
+ d. THE COMPAT ALIASES ARE LOAD-BEARING. ~450 rules across 20 page sheets
49
+ derive from --surface / --surface-2 / --border / --border-strong /
50
+ --accent-weak / --accent-text, so they are re-derived in the world's terms
51
+ rather than deleted: a page this task does not re-skin degrades into the
52
+ new palette instead of breaking. They retire page family by page family as
53
+ tasks 1003304-1003308 land.
54
+
55
+ Composition — rail/drawer breakpoints, panel padding, page layout — is NOT in
56
+ here. It lives on the shell rules below and on a page's own body, because a
57
+ body declaration beats an inherited :root value at every width and a token
58
+ tier that carries a breakpoint silently defeats its own media query. */
11
59
 
12
60
  :root {
13
- /* ---- pack-driven vars (the theming API) ---- */
14
- --bg: #f5f6f8;
15
- --bg-card: #ffffff;
16
- --bg-deep: #eceef2;
17
- --bg-frame: #e7e9ee;
18
-
19
- --ink: #1a1d23;
20
- --ink-soft: #5b6372;
21
- --ink-faint: #8b93a1;
22
- --rule: #d4d8e0;
23
- --rule-soft: #e6e8ee;
24
- --accent: #4f63e7;
25
- --accent-deep: #3b4dc4;
26
- --sea: #175cd3;
27
- --terracotta: #b45309;
28
-
29
- --font-display: "Inter", -apple-system, "Segoe UI", system-ui, sans-serif;
30
- --font-body: "Inter", -apple-system, "Segoe UI", system-ui, sans-serif;
31
-
32
- /* ---- derived tokens (recompute from the pack vars) ---- */
33
- --surface: var(--bg-card);
34
- --surface-2: color-mix(in srgb, var(--bg-card) 45%, var(--bg));
35
- --border: color-mix(in srgb, var(--ink) 11%, var(--bg));
36
- --border-strong: color-mix(in srgb, var(--ink) 22%, var(--bg));
37
- --accent-weak: color-mix(in srgb, var(--accent) 9%, var(--bg-card));
38
- --accent-text: color-mix(in srgb, var(--accent) 82%, var(--ink));
39
-
40
- /* semantic status colors (deliberately NOT pack-driven) */
41
- --ok: #1a7f4b;
42
- --ok-weak: color-mix(in srgb, #1a7f4b 12%, var(--bg-card));
43
- --warn: #9a6700;
44
- --warn-weak: color-mix(in srgb, #d99a00 14%, var(--bg-card));
61
+ color-scheme: light;
62
+
63
+ /* ---- the contract tier: the thirteen colours + two faces a pack overrides ---- */
64
+ --bg: #edecee;
65
+ --bg-card: #fbfafc;
66
+ --bg-deep: #e2e1e4;
67
+ --bg-frame: #f6f5f7;
68
+ --ink: #17181a;
69
+ --ink-soft: #55565a;
70
+ --ink-faint: #65656a;
71
+ --rule: #c6c6c8;
72
+ --rule-soft: #dcdbdd;
73
+ --accent: #fb9569;
74
+ --accent-deep: #a3481a;
75
+ --sea: #5b6fd0;
76
+ --terracotta: #c9512c;
77
+
78
+ --font-display: "Manrope", "Segoe UI", system-ui, -apple-system, sans-serif;
79
+ --font-body: "Manrope", "Segoe UI", system-ui, -apple-system, sans-serif;
80
+
81
+ /* ---- derived: the third face. Mono is where the hall quotes the machine —
82
+ task ids, column heads, eyebrows, a credit figure's unit. ---- */
83
+ --font-mono: "JetBrains Mono", ui-monospace, "Cascadia Mono", Consolas, monospace;
84
+
85
+ /* ---- derived: glass. The blur is a token because light glass over paper is
86
+ a tint, not a frost — light sets it to 0px and stays paper. ---- */
87
+ --glass: rgba(23, 24, 26, .05);
88
+ --glass-strong: rgba(23, 24, 26, .08);
89
+ --glass-edge: rgba(23, 24, 26, .12);
90
+ --glass-blur: 0px;
91
+
92
+ /* ---- derived: the void. The hall has no hero; the void is what a control
93
+ needs when it sits over a plate, and the seam for one. ---- */
94
+ --void: #050508;
95
+ --void-ink: #ffffff;
96
+ --void-edge: rgba(23, 24, 26, .12);
97
+
98
+ /* ---- derived: the skeleton tone. --bg-deep is the obvious choice and it is
99
+ WRONG: DESIGN.md collapses deep back to black in dark, so --bg-deep
100
+ and --bg are the same value there and a skeleton painted with it is
101
+ invisible. This mix reads in both modes and needs no dark twin,
102
+ because both of its terms already flip. ---- */
103
+ --skeleton: color-mix(in srgb, var(--ink) 10%, var(--bg-card));
104
+
105
+ /* ---- derived: the reading measure. A `ch` is the width of the font's ZERO
106
+ glyph, not a character, and Manrope's zero is much wider than its
107
+ average lowercase — primer.css's `max-width: 72ch` renders about 90
108
+ characters under a comment claiming 60-72. This token is MEASURED:
109
+ 48ch renders ~60 characters at the hall's body size. Re-measure rather
110
+ than doing the ch maths. ---- */
111
+ --measure: 48ch;
112
+
113
+ /* ---- derived: accent behaviours. The One Warm Thing Rule holds in the hall
114
+ too — the accent lights only what is pressable or live (the claim
115
+ button, the current nav item, the active tab, a focus ring). A credit
116
+ figure is data, not live, so it is ink. --accent-on is the DARK ink
117
+ that goes on an accent fill: white on this salmon fails AA. ---- */
118
+ --accent-ink: color-mix(in srgb, var(--accent) 45%, #240e00);
119
+ --accent-lift: color-mix(in srgb, var(--accent) 58%, #ffffff);
120
+ --accent-tint: color-mix(in srgb, var(--accent) 30%, #fffdf9);
121
+ --accent-on: #2a1305;
122
+ --accent-glow: 0 0 0 0 transparent;
123
+
124
+ /* ---- the status vocabulary: fixed, not pack-driven, not accent-derived.
125
+ See note (a) — these values are measured, not chosen. ---- */
126
+ --ok: #187444;
127
+ --ok-weak: color-mix(in srgb, #187444 12%, var(--bg-card));
128
+ --warn: #895c00;
129
+ --warn-weak: color-mix(in srgb, #895c00 14%, var(--bg-card));
45
130
  --danger: #b42318;
46
131
  --danger-weak: color-mix(in srgb, #b42318 10%, var(--bg-card));
47
- --info: var(--sea);
48
- --info-weak: color-mix(in srgb, var(--sea) 11%, var(--bg-card));
49
-
50
- /* layout metrics (scope §10) */
51
- --sidebar-w: 240px;
52
- --sidebar-w-rail: 64px;
53
- --topbar-h: 56px;
54
- --content-max: 1120px;
55
- --radius: 10px;
56
- --radius-sm: 8px;
57
- --shadow-pop: 0 8px 24px color-mix(in srgb, var(--ink) 14%, transparent);
58
-
59
- /* z ladder — content overlays (rank tips z-50, modals z-50, toast z-100,
132
+ /* --info is the one state that is NOT a bright colour, because it derives
133
+ from the contract's --sea rather than a chosen hex, and that makes its
134
+ own weak fill the binding pair rather than the grounds. The round
135
+ measured info against --bg and --bg-card only; measured against
136
+ --info-weak too (tests/hall_tokens.mjs), dark's 91% sea fell to 4.12:1,
137
+ so dark lightens to 80% for 5.00:1 with the ~9% headroom the rest of
138
+ the vocabulary holds. Both weak fills mix the STATE colour into the
139
+ card, which is the shape ok/warn/danger already use — mixing the raw
140
+ --sea instead is what made the two drift apart. ---- */
141
+ --info: color-mix(in srgb, var(--sea) 86%, #000000);
142
+ --info-weak: color-mix(in srgb, var(--info) 9%, var(--bg-card));
143
+
144
+ /* ---- derived: focus. The world's two-tone ring — a 2px accent-derived
145
+ outline at 3px offset inside an 8px --bg halo, so the indicator keeps
146
+ its own edge over whatever it lands on. ---- */
147
+ --focus: color-mix(in srgb, var(--accent-deep) 82%, #3a1400);
148
+
149
+ /* ---- derived: the label face flips. Mono caps read as precise on paper and
150
+ thin on black, so light takes the mono and dark takes the body face.
151
+ Take the token, never the family. ---- */
152
+ --label-face: var(--font-mono);
153
+ --label-size: 11px;
154
+ --label-space: .12em;
155
+ --label-case: uppercase;
156
+
157
+ /* ---- derived: the type halo. Only type over a plate needs one, and in light
158
+ there is nothing behind it. Never a coloured glow. ---- */
159
+ --type-halo: none;
160
+
161
+ /* ---- structure. --line is a WIDTH (note c). ---- */
162
+ --line: 1px;
163
+ --radius: 16px;
164
+ --radius-sm: 10px;
165
+ --radius-pill: 999px;
166
+
167
+ /* ---- the shell's metrics. --topbar-h stays under the kit's 80px top-bar
168
+ floor; --sidebar-w IS the rail, because v3 pins the rail at every
169
+ width and the drawer is the phone's disclosure. ---- */
170
+ --top-h: 56px;
171
+ --topbar-h: 56px;
172
+ --sidebar-w-rail: 64px;
173
+ --sidebar-w: var(--sidebar-w-rail);
174
+ --rail-open: 246px;
175
+ --content-max: 1120px;
176
+
177
+ /* ---- spacing: DESIGN.md's rhythm (6/10/14/22/34/52). The four-step
178
+ --space-* scale task 2179 named aliases onto it, so no ported rule
179
+ breaks; hairlines separate, padding does not. ---- */
180
+ --sp-hair: 6px;
181
+ --sp-xs: 10px;
182
+ --sp-sm: 14px;
183
+ --sp-md: 22px;
184
+ --sp-lg: 34px;
185
+ --sp-xl: 52px;
186
+ --space-1: var(--sp-hair);
187
+ --space-2: var(--sp-xs);
188
+ --space-3: var(--sp-sm);
189
+ --space-4: var(--sp-md);
190
+
191
+ /* ---- the compat aliases (note d) ---- */
192
+ --surface: var(--bg-card);
193
+ --surface-2: color-mix(in srgb, var(--ink) 5%, var(--bg-card));
194
+ --border: var(--rule-soft);
195
+ --border-strong: var(--rule);
196
+ --accent-weak: var(--accent-tint);
197
+ --accent-text: var(--accent-ink);
198
+ /* The world has no shadow vocabulary for UI, so this is spent only where an
199
+ element floats over a scrim (the phone drawer, the deep-link card). Pure
200
+ black with alpha is the one literal the Fifteen Rule allows, as a scrim. */
201
+ --shadow-pop: 0 18px 40px rgba(0, 0, 0, .28);
202
+
203
+ /* ---- z ladder — content overlays (rank tips z-50, modals z-50, toast z-100,
60
204
  copy-toast z-1000, deeplink z-2000, diagram fullscreen z-9999) must beat
61
- the chrome; the mobile drawer sits above chrome but below dialogs. */
205
+ the chrome; the mobile drawer sits above chrome but below dialogs. ---- */
62
206
  --z-topbar: 30;
63
207
  --z-sidebar: 40;
64
208
  --z-scrim: 140;
65
209
  --z-drawer: 150;
66
- }
67
-
68
- /* ---------- spacing scale (task 2179) ----------
69
- Four steps, deliberately matching the values the sheet had already converged
70
- on (a census found 6/10/16/20 dominant), so this NAMES the existing rhythm
71
- rather than restyling the hall. It governs the three things that decide
72
- density — card padding, card gap, section gap — and is not a mandate to
73
- rewrite every inset; chip and control padding stay as authored.
74
-
75
- Kept in its OWN block rather than added to :root above: :root is the
76
- pack-driven theming contract an instance overrides, and the program's audit
77
- requires it unchanged. Spacing is structure, not theme.
78
210
 
79
- 1 6px tight — inside a row or chip
80
- 2 10px default gap between siblings
81
- 3 16px comfortable — secondary card padding
82
- 4 20px card padding, card gap, section gap */
83
- :root {
84
- --space-1: 6px;
85
- --space-2: 10px;
86
- --space-3: 16px;
87
- --space-4: 20px;
211
+ /* ---- the hall's own plate: the declared seam, deferred by the owner
212
+ ("decide after the shell lands", 2026-08-30). The hall's plate was
213
+ never shot, so it resolves to none and the shell is a complete
214
+ composition without one — which is exactly what light does anyway.
215
+ Adding one later is a change to these three values, not a re-layout. */
216
+ --plate: none;
217
+ --plate-blend: lighten;
218
+ --plate-veil: none;
88
219
  }
89
220
 
90
221
  /* ---------- dark theme ----------
91
- Only the NEUTRALS flip; the instance pack's accent/sea/terracotta + fonts
92
- survive, and every derived token (color-mix on these vars) re-derives
93
- automatically. html[data-theme] outweighs the pack's injected :root override
94
- (specificity 0,1,1 vs 0,1,0), so dark wins over the pack's light values.
95
- shell.js owns the toggle + persistence (localStorage 'hall-theme'); the
96
- media block below covers first paint for OS-dark users before it runs. */
222
+ Only the NEUTRALS and the mode-dependent derivations flip; the pack's
223
+ accent/sea/terracotta + fonts survive (the Unpinned Accent Rule — a dark
224
+ block may pin no accent literal, or a re-themed instance cannot reach dark),
225
+ and every color-mix on them re-derives for free. Written twice, byte-
226
+ identical: the [data-theme] switch shell.js owns, and the media block that
227
+ covers first paint for OS-dark users before the script runs. */
97
228
 
98
229
  html[data-theme="dark"] {
99
230
  color-scheme: dark;
100
- --bg: #101216;
101
- --bg-card: #181b21;
102
- --bg-deep: #0c0e11;
103
- --bg-frame: #1e222a;
104
- --ink: #e7e9ee;
105
- --ink-soft: #9aa2b1;
106
- --ink-faint: #6b7280;
107
- --rule: #333945;
108
- --rule-soft: #242933;
109
- --shadow-pop: 0 8px 24px rgba(0, 0, 0, .5);
231
+ --bg: #000000;
232
+ --bg-card: #101012;
233
+ --bg-deep: #000000;
234
+ --bg-frame: #08080a;
235
+ --ink: #ffffff;
236
+ --ink-soft: #c0c0c0;
237
+ --ink-faint: #a8a8a8;
238
+ --rule: rgba(255, 255, 255, .16);
239
+ --rule-soft: rgba(255, 255, 255, .08);
240
+ --glass: rgba(255, 255, 255, .11);
241
+ --glass-strong: rgba(255, 255, 255, .17);
242
+ --glass-edge: rgba(255, 255, 255, .13);
243
+ --glass-blur: 18px;
244
+ --void: #000000;
245
+ --void-ink: var(--ink);
246
+ --void-edge: rgba(255, 255, 255, .16);
247
+ --accent-ink: var(--accent);
248
+ --accent-tint: color-mix(in srgb, var(--accent) 22%, #0b0a09);
249
+ --accent-glow: 0 8px 28px color-mix(in srgb, var(--accent) 38%, transparent);
250
+ --ok: #4ade80;
251
+ --ok-weak: color-mix(in srgb, #4ade80 14%, var(--bg-card));
252
+ --warn: #fbbf24;
253
+ --warn-weak: color-mix(in srgb, #fbbf24 14%, var(--bg-card));
254
+ --danger: #f87171;
255
+ --danger-weak: color-mix(in srgb, #f87171 14%, var(--bg-card));
256
+ --info: color-mix(in srgb, var(--sea) 80%, #ffffff);
257
+ --info-weak: color-mix(in srgb, var(--info) 14%, var(--bg-card));
258
+ --focus: var(--accent);
259
+ --label-face: var(--font-body);
260
+ --label-size: 11.5px;
261
+ --label-space: .1em;
262
+ --type-halo: 0 2px 18px rgba(0, 0, 0, .94), 0 0 54px rgba(0, 0, 0, .72);
263
+ --surface-2: color-mix(in srgb, var(--ink) 6%, var(--bg-card));
264
+ --shadow-pop: 0 18px 40px rgba(0, 0, 0, .62);
265
+ --plate-veil: linear-gradient(to bottom, rgba(0, 0, 0, .82), rgba(0, 0, 0, .93));
110
266
  }
111
267
  html[data-theme="dark"] body { background: var(--bg); }
112
268
 
113
269
  @media (prefers-color-scheme: dark) {
114
270
  html:not([data-theme]) {
115
271
  color-scheme: dark;
116
- --bg: #101216;
117
- --bg-card: #181b21;
118
- --bg-deep: #0c0e11;
119
- --bg-frame: #1e222a;
120
- --ink: #e7e9ee;
121
- --ink-soft: #9aa2b1;
122
- --ink-faint: #6b7280;
123
- --rule: #333945;
124
- --rule-soft: #242933;
125
- --shadow-pop: 0 8px 24px rgba(0, 0, 0, .5);
272
+ --bg: #000000;
273
+ --bg-card: #101012;
274
+ --bg-deep: #000000;
275
+ --bg-frame: #08080a;
276
+ --ink: #ffffff;
277
+ --ink-soft: #c0c0c0;
278
+ --ink-faint: #a8a8a8;
279
+ --rule: rgba(255, 255, 255, .16);
280
+ --rule-soft: rgba(255, 255, 255, .08);
281
+ --glass: rgba(255, 255, 255, .11);
282
+ --glass-strong: rgba(255, 255, 255, .17);
283
+ --glass-edge: rgba(255, 255, 255, .13);
284
+ --glass-blur: 18px;
285
+ --void: #000000;
286
+ --void-ink: var(--ink);
287
+ --void-edge: rgba(255, 255, 255, .16);
288
+ --accent-ink: var(--accent);
289
+ --accent-tint: color-mix(in srgb, var(--accent) 22%, #0b0a09);
290
+ --accent-glow: 0 8px 28px color-mix(in srgb, var(--accent) 38%, transparent);
291
+ --ok: #4ade80;
292
+ --ok-weak: color-mix(in srgb, #4ade80 14%, var(--bg-card));
293
+ --warn: #fbbf24;
294
+ --warn-weak: color-mix(in srgb, #fbbf24 14%, var(--bg-card));
295
+ --danger: #f87171;
296
+ --danger-weak: color-mix(in srgb, #f87171 14%, var(--bg-card));
297
+ --info: color-mix(in srgb, var(--sea) 80%, #ffffff);
298
+ --info-weak: color-mix(in srgb, var(--info) 14%, var(--bg-card));
299
+ --focus: var(--accent);
300
+ --label-face: var(--font-body);
301
+ --label-size: 11.5px;
302
+ --label-space: .1em;
303
+ --type-halo: 0 2px 18px rgba(0, 0, 0, .94), 0 0 54px rgba(0, 0, 0, .72);
304
+ --surface-2: color-mix(in srgb, var(--ink) 6%, var(--bg-card));
305
+ --shadow-pop: 0 18px 40px rgba(0, 0, 0, .62);
306
+ --plate-veil: linear-gradient(to bottom, rgba(0, 0, 0, .82), rgba(0, 0, 0, .93));
126
307
  }
127
308
  html:not([data-theme]) body { background: var(--bg); }
128
309
  }
129
310
 
311
+ /* The Kill Switch Rule (DESIGN.md). Nothing escapes it, and no motion is
312
+ authored in JavaScript, because JavaScript motion escapes it. */
313
+ @media (prefers-reduced-motion: reduce) {
314
+ *, *::before, *::after {
315
+ animation: none !important;
316
+ transition: none !important;
317
+ scroll-behavior: auto !important;
318
+ }
319
+ }
320
+
130
321
  /* ---------- table of contents (task 2166) ----------
131
322
  Section banners in file order — grep "----------" to jump:
132
323
  tokens + dark theme (ABOVE — the theming contract; never move/rename)
@@ -158,12 +349,12 @@ html {
158
349
 
159
350
  * {
160
351
  scrollbar-width: thin;
161
- scrollbar-color: var(--border-strong) transparent;
352
+ scrollbar-color: var(--rule) transparent;
162
353
  }
163
354
  ::-webkit-scrollbar { width: 10px; height: 10px; }
164
355
  ::-webkit-scrollbar-track { background: transparent; }
165
356
  ::-webkit-scrollbar-thumb {
166
- background: var(--border-strong);
357
+ background: var(--rule);
167
358
  border-radius: 5px;
168
359
  border: 2px solid transparent;
169
360
  background-clip: content-box;
@@ -182,33 +373,45 @@ body {
182
373
  }
183
374
 
184
375
  a {
185
- color: var(--accent-text);
376
+ color: var(--accent-ink);
186
377
  text-decoration: none;
187
378
  }
188
- a:hover { text-decoration: underline; text-underline-offset: 2px; }
379
+ a:hover { text-decoration: underline; text-underline-offset: 3px; }
189
380
 
190
381
  code, kbd, pre {
191
- font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
382
+ font-family: var(--font-mono);
192
383
  font-size: 0.92em;
193
384
  }
194
385
 
386
+ /* The label voice. Mono in light, the body face in dark — take the token, never
387
+ the family (DESIGN.md, The label face flips). */
388
+ .lbl {
389
+ font-family: var(--label-face);
390
+ font-size: var(--label-size);
391
+ letter-spacing: var(--label-space);
392
+ text-transform: var(--label-case);
393
+ color: var(--ink-faint);
394
+ line-height: 1.2;
395
+ }
396
+
397
+ /* Every number the ledger produced is tabular, so a live refresh cannot reflow
398
+ the composition around it. */
399
+ .num, .tabular { font-variant-numeric: tabular-nums; }
400
+
195
401
  select, input[type="text"], input[type="search"], textarea {
196
402
  font-family: var(--font-body);
197
403
  font-size: 14px;
198
404
  color: var(--ink);
199
- background: var(--surface);
200
- border: 1px solid var(--border-strong);
201
- border-radius: var(--radius-sm);
202
- padding: 6px 10px;
405
+ background: none;
406
+ border: 1px solid var(--rule);
407
+ border-radius: var(--radius-pill);
408
+ min-height: 30px;
409
+ padding: 5px 12px;
203
410
  }
411
+ select:hover, input[type="text"]:hover, input[type="search"]:hover { border-color: var(--ink-faint); }
412
+ textarea { border-radius: var(--radius-sm); }
204
413
  select:disabled { opacity: 0.55; cursor: not-allowed; }
205
- select option { background: var(--surface); color: var(--ink); }
206
-
207
- :focus-visible {
208
- outline: 2px solid var(--accent);
209
- outline-offset: 2px;
210
- border-radius: 4px;
211
- }
414
+ select option { background: var(--bg-card); color: var(--ink); }
212
415
 
213
416
  button { font-family: var(--font-body); }
214
417
 
@@ -221,48 +424,118 @@ button { font-family: var(--font-body); }
221
424
  white-space: nowrap;
222
425
  }
223
426
 
224
- /* ---------- app shell ---------- */
427
+ /* ---------- app shell ----------
428
+ v3 "the glass hall" (the owner's pick, 2026-08-30): the sidebar is the 64px
429
+ rail at EVERY width, opening on hover or focus-within over the page; the
430
+ topbar and sidebar are glass; content sits on --bg-card panels at the full
431
+ --radius with tone doing the separation and no hairline edge.
432
+
433
+ Two rules the round found the hard way and this section obeys:
434
+ - a rail label must LEAVE the layout, not fade to opacity 0. A faded label
435
+ still occupies width and pushes its own icon out of the 64px strip.
436
+ - the rail's breakpoints are composition, so they live here on the shell
437
+ rules and never in the token tier: a page that declares --sidebar-w on
438
+ `body` would beat an inherited :root value and silently defeat the query. */
225
439
 
226
440
  .app { min-height: 100vh; }
227
441
 
442
+ /* The ground and its veil. Both are tokens; the hall's plate is not shot yet,
443
+ so both resolve to none in both modes and the shell is a complete composition
444
+ without one. Adding one later is a token change, not a re-layout. */
445
+ body::before {
446
+ content: "";
447
+ position: fixed;
448
+ inset: 0;
449
+ z-index: 0;
450
+ pointer-events: none;
451
+ background-image: var(--plate);
452
+ background-size: cover;
453
+ background-position: center;
454
+ mix-blend-mode: var(--plate-blend);
455
+ }
456
+ body::after {
457
+ content: "";
458
+ position: fixed;
459
+ inset: 0;
460
+ z-index: 0;
461
+ pointer-events: none;
462
+ background-image: var(--plate-veil);
463
+ }
464
+
228
465
  .app-sidebar {
229
466
  position: fixed;
230
467
  inset: 0 auto 0 0;
231
- width: var(--sidebar-w);
468
+ width: var(--sidebar-w-rail);
232
469
  z-index: var(--z-sidebar);
233
470
  display: flex;
234
471
  flex-direction: column;
235
- background: var(--surface);
236
- border-right: 1px solid var(--border);
472
+ background: var(--glass);
473
+ border-right: 1px solid var(--glass-edge);
474
+ backdrop-filter: blur(var(--glass-blur));
475
+ -webkit-backdrop-filter: blur(var(--glass-blur));
237
476
  overflow: hidden;
238
477
  }
478
+ /* The rail opens over the content — no transition, because the world animates
479
+ transform and filter only. `:focus-within` is what makes it keyboard-reachable
480
+ at all; without it a tab into the rail lands on unlabelled icons. */
481
+ .app-sidebar:hover,
482
+ .app-sidebar:focus-within { width: var(--rail-open); }
239
483
 
240
484
  .app-sidebar__brand {
241
485
  display: flex;
242
486
  align-items: center;
487
+ justify-content: center;
243
488
  min-height: var(--topbar-h);
244
- padding: 0 16px;
245
- border-bottom: 1px solid var(--border);
489
+ padding: 0;
490
+ border-bottom: 1px solid var(--glass-edge);
246
491
  flex-shrink: 0;
247
492
  }
493
+ /* The skip link — shell.js renders it as the first focusable thing in the
494
+ document and points it at a focusable, never-ringed <main>. Off-screen until
495
+ focused, then a pill in the top-left over everything. */
496
+ .skip {
497
+ position: absolute;
498
+ left: -9999px;
499
+ top: 8px;
500
+ z-index: 200;
501
+ background: var(--bg-card);
502
+ color: var(--ink);
503
+ border: 1px solid var(--rule);
504
+ border-radius: var(--radius-pill);
505
+ padding: 11px 18px;
506
+ font-size: 13.5px;
507
+ text-decoration: none;
508
+ }
509
+ .skip:focus { left: 12px; }
510
+ main:focus { outline: none; }
511
+
248
512
  .brand-mark {
249
513
  display: flex;
250
514
  align-items: center;
515
+ justify-content: center;
251
516
  gap: 10px;
517
+ /* The label leaves the layout in the closed rail, so the dot alone would be
518
+ an 11px target — the mark still has to clear the 24px control floor. */
519
+ min-width: 32px;
520
+ min-height: 32px;
252
521
  font-family: var(--font-display);
253
- font-weight: 700;
522
+ font-weight: 500;
254
523
  font-size: 15px;
524
+ letter-spacing: -0.015em;
255
525
  color: var(--ink);
256
- min-width: 0;
257
526
  }
258
527
  .brand-mark:hover { text-decoration: none; }
528
+ /* An outlined dot, not a filled one: the accent lights only what is pressable
529
+ or live, and a wordmark is neither. */
259
530
  .brand-mark__dot {
260
- width: 10px; height: 10px;
261
- border-radius: 3px;
262
- background: var(--accent);
531
+ width: 11px; height: 11px;
532
+ border-radius: 50%;
533
+ border: 1px solid var(--ink);
534
+ background: none;
263
535
  flex-shrink: 0;
264
536
  }
265
537
  .brand-mark__name {
538
+ display: none;
266
539
  white-space: nowrap;
267
540
  overflow: hidden;
268
541
  text-overflow: ellipsis;
@@ -270,7 +543,7 @@ button { font-family: var(--font-body); }
270
543
 
271
544
  .app-sidebar__nav {
272
545
  flex: 1;
273
- padding: 12px 10px;
546
+ padding: 14px 0 22px;
274
547
  display: flex;
275
548
  flex-direction: column;
276
549
  gap: 2px;
@@ -290,28 +563,39 @@ button { font-family: var(--font-body); }
290
563
  }
291
564
  .nav-group { display: flex; flex-direction: column; gap: 2px; }
292
565
  .nav-group + .nav-group { margin-top: 14px; }
566
+ /* task 1003303: the items live in their own wrapper so the group head can carry
567
+ aria-controls (the frozen markup had aria-expanded pointing at nothing, which
568
+ fails the disclosure rule). The class deliberately does NOT start with
569
+ "nav-group" — tests/hall_nav.mjs splits the sidebar on that string to find
570
+ groups, and a wrapper that matched it would parse as a phantom sixth group. */
571
+ .nav-items { display: flex; flex-direction: column; gap: 2px; }
293
572
  .nav-group__head {
294
- font-size: 11px;
295
- font-weight: 600;
296
- letter-spacing: 0.06em;
297
- text-transform: uppercase;
298
- color: var(--ink-faint);
299
- padding: 4px 10px 2px;
300
- }
301
- .nav-group--collapsible .nav-group__head {
302
- display: flex;
573
+ display: none;
303
574
  align-items: center;
304
575
  justify-content: space-between;
576
+ gap: 8px;
305
577
  width: 100%;
578
+ min-height: 26px;
579
+ padding: 8px 22px 4px;
580
+ text-align: left;
306
581
  background: none;
307
582
  border: none;
308
583
  cursor: pointer;
309
584
  }
310
- .nav-group--collapsible .nav-group__head:hover { color: var(--ink-soft); }
585
+ .nav-group__head-label {
586
+ font-family: var(--label-face);
587
+ font-size: var(--label-size);
588
+ letter-spacing: var(--label-space);
589
+ text-transform: var(--label-case);
590
+ color: var(--ink-faint);
591
+ }
592
+ .nav-group--collapsible .nav-group__head:hover .nav-group__head-label { color: var(--ink-soft); }
311
593
  .nav-group__chevron {
312
- width: 12px;
313
- height: 12px;
594
+ width: 10px;
595
+ height: 6px;
314
596
  flex-shrink: 0;
597
+ stroke: currentColor;
598
+ color: var(--ink-faint);
315
599
  transition: transform 0.15s ease;
316
600
  }
317
601
  .nav-group--collapsed .nav-group__chevron { transform: rotate(-90deg); }
@@ -321,42 +605,47 @@ button { font-family: var(--font-body); }
321
605
  position: relative;
322
606
  display: flex;
323
607
  align-items: center;
608
+ justify-content: center;
324
609
  gap: 10px;
325
- height: 36px;
326
- padding: 0 10px;
610
+ min-height: 40px;
611
+ margin: 0 8px;
612
+ padding: 0;
327
613
  border-radius: var(--radius-sm);
328
614
  color: var(--ink-soft);
329
- font-weight: 500;
330
615
  font-size: 14px;
616
+ text-transform: lowercase;
331
617
  white-space: nowrap;
332
618
  }
333
- .nav-item:hover { background: var(--surface-2); color: var(--ink); text-decoration: none; }
334
- .nav-item--active {
335
- background: var(--accent-weak);
336
- color: var(--accent-text);
337
- }
619
+ .nav-item:hover { background: var(--glass); color: var(--ink); text-decoration: none; }
620
+ .nav-item--active { background: var(--glass-strong); color: var(--ink); }
621
+ /* The current item is the one live thing in the rail, so it takes the accent —
622
+ the lifecycle rail's lit-step grammar, one 2px bar on the leading edge. */
338
623
  .nav-item--active::before {
339
624
  content: "";
340
625
  position: absolute;
341
- left: -10px; top: 7px; bottom: 7px;
342
- width: 3px;
343
- border-radius: 0 2px 2px 0;
626
+ left: 0; top: 8px; bottom: 8px;
627
+ width: 2px;
628
+ border-radius: 1px;
344
629
  background: var(--accent);
345
630
  }
346
- .nav-icon { width: 18px; height: 18px; flex-shrink: 0; }
631
+ .nav-item__label { display: none; font-size: 14px; }
632
+ .nav-icon { width: 16px; height: 16px; flex-shrink: 0; }
347
633
 
348
634
  .app-sidebar__foot {
349
- padding: 12px 10px 14px;
350
- border-top: 1px solid var(--border);
635
+ padding: 10px 0;
636
+ border-top: 1px solid var(--glass-edge);
351
637
  display: flex;
352
638
  flex-direction: column;
639
+ align-items: center;
353
640
  gap: 8px;
354
641
  flex-shrink: 0;
355
642
  }
356
643
  .shell-user {
357
644
  display: flex;
358
645
  align-items: center;
646
+ justify-content: center;
359
647
  gap: 10px;
648
+ min-height: 36px;
360
649
  padding: 4px 6px;
361
650
  min-width: 0;
362
651
  /* task 1831: the chip is now an <a> to /settings — strip link chrome, keep the
@@ -366,15 +655,15 @@ button { font-family: var(--font-body); }
366
655
  border-radius: var(--radius-sm);
367
656
  cursor: pointer;
368
657
  }
369
- .shell-user:hover { background: var(--surface-2); text-decoration: none; }
370
- .shell-user:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
658
+ .shell-user:hover { background: var(--glass); text-decoration: none; }
371
659
  .shell-user__avatar {
372
- width: 30px; height: 30px;
660
+ width: 26px; height: 26px;
373
661
  border-radius: 50%;
374
- background: var(--accent-weak);
375
- color: var(--accent-text);
376
- font-size: 12px;
377
- font-weight: 600;
662
+ background: var(--surface-2);
663
+ border: 1px solid var(--rule);
664
+ color: var(--ink-soft);
665
+ font-size: 11px;
666
+ font-weight: 500;
378
667
  display: flex;
379
668
  align-items: center;
380
669
  justify-content: center;
@@ -382,43 +671,75 @@ button { font-family: var(--font-body); }
382
671
  flex-shrink: 0;
383
672
  }
384
673
  .shell-user__avatar img { width: 100%; height: 100%; object-fit: cover; border-radius: 50%; }
385
- .shell-user__meta { display: flex; flex-direction: column; min-width: 0; line-height: 1.3; }
674
+ .shell-user__meta { display: none; flex-direction: column; min-width: 0; line-height: 1.3; }
386
675
  .shell-user__name {
387
- font-size: 13px;
388
- font-weight: 600;
676
+ font-size: 13.5px;
389
677
  white-space: nowrap;
390
678
  overflow: hidden;
391
679
  text-overflow: ellipsis;
392
680
  }
393
- .shell-user__rank { font-size: 11.5px; color: var(--ink-faint); }
681
+ .shell-user__rank {
682
+ font-family: var(--label-face);
683
+ font-size: 10.5px;
684
+ letter-spacing: var(--label-space);
685
+ text-transform: var(--label-case);
686
+ color: var(--ink-faint);
687
+ }
394
688
  .shell-signin,
395
689
  .shell-home,
396
690
  .shell-signout {
397
691
  display: flex;
398
692
  align-items: center;
693
+ justify-content: center;
399
694
  gap: 10px;
400
- height: 34px;
401
- padding: 0 10px;
695
+ min-height: 32px;
696
+ padding: 0 8px;
402
697
  border-radius: var(--radius-sm);
403
698
  font-size: 13.5px;
404
- font-weight: 500;
405
- color: var(--ink-soft);
699
+ color: var(--ink-faint);
406
700
  }
407
701
  .shell-home { text-decoration: none; }
408
702
  .shell-signin {
409
- justify-content: center;
410
703
  background: var(--accent);
411
- color: #fff;
704
+ color: var(--accent-on);
705
+ border-radius: var(--radius-pill);
706
+ font-weight: 500;
707
+ min-height: 34px;
412
708
  }
413
- .shell-signin:hover { background: var(--accent-deep); color: #fff; text-decoration: none; }
709
+ .shell-signin:hover { background: var(--accent-lift); color: var(--accent-on); text-decoration: none; }
414
710
  .shell-home:hover,
415
- .shell-signout:hover { background: var(--surface-2); color: var(--ink); text-decoration: none; }
711
+ .shell-signout:hover { background: var(--glass); color: var(--ink); text-decoration: none; }
712
+
713
+ /* ---- the rail, open. Labels LEAVE the layout when closed and come back here;
714
+ the icon strip is what stays either way. ---- */
715
+ .app-sidebar:hover .brand-mark__name,
716
+ .app-sidebar:focus-within .brand-mark__name,
717
+ .app-sidebar:hover .nav-item__label,
718
+ .app-sidebar:focus-within .nav-item__label,
719
+ .app-sidebar:hover .shell-user__meta,
720
+ .app-sidebar:focus-within .shell-user__meta { display: block; }
721
+ .app-sidebar:hover .nav-group--collapsible .nav-group__head,
722
+ .app-sidebar:focus-within .nav-group--collapsible .nav-group__head { display: flex; }
723
+ .app-sidebar:hover .app-sidebar__brand,
724
+ .app-sidebar:focus-within .app-sidebar__brand { justify-content: flex-start; padding: 0 22px; }
725
+ .app-sidebar:hover .nav-item,
726
+ .app-sidebar:focus-within .nav-item { justify-content: flex-start; padding: 0 12px; }
727
+ .app-sidebar:hover .app-sidebar__foot,
728
+ .app-sidebar:focus-within .app-sidebar__foot { align-items: stretch; padding: 12px 16px; }
729
+ .app-sidebar:hover .shell-user,
730
+ .app-sidebar:focus-within .shell-user { justify-content: flex-start; }
731
+ .app-sidebar:hover .shell-home,
732
+ .app-sidebar:focus-within .shell-home,
733
+ .app-sidebar:hover .shell-signout,
734
+ .app-sidebar:focus-within .shell-signout { justify-content: flex-start; padding: 0 10px; }
416
735
 
417
736
  .app-body {
418
- margin-left: var(--sidebar-w);
737
+ margin-left: var(--sidebar-w-rail);
419
738
  min-height: 100vh;
420
739
  display: flex;
421
740
  flex-direction: column;
741
+ position: relative;
742
+ z-index: 1;
422
743
  }
423
744
 
424
745
  .app-topbar {
@@ -427,29 +748,36 @@ button { font-family: var(--font-body); }
427
748
  z-index: var(--z-topbar);
428
749
  display: flex;
429
750
  align-items: center;
430
- gap: 12px;
751
+ gap: 14px;
431
752
  height: var(--topbar-h);
432
- padding: 0 24px;
433
- background: color-mix(in srgb, var(--surface) 88%, transparent);
434
- backdrop-filter: blur(8px);
435
- border-bottom: 1px solid var(--border);
753
+ padding: 0 22px;
754
+ background: var(--glass);
755
+ backdrop-filter: blur(var(--glass-blur));
756
+ -webkit-backdrop-filter: blur(var(--glass-blur));
757
+ border-bottom: 1px solid var(--glass-edge);
436
758
  }
759
+ /* Sentence case, not a mono label: v3's own divergence, picked with it. */
437
760
  .topbar-title {
438
761
  margin: 0;
439
762
  font-family: var(--font-display);
440
- font-size: 16px;
441
- font-weight: 600;
442
- letter-spacing: -0.01em;
763
+ font-weight: 300;
764
+ font-size: 19px;
765
+ letter-spacing: -0.025em;
766
+ text-transform: lowercase;
767
+ color: var(--ink);
443
768
  }
444
769
  .topbar-side {
445
770
  margin-left: auto;
446
771
  display: flex;
447
772
  align-items: center;
448
- gap: 12px;
773
+ gap: 8px;
449
774
  min-width: 0;
450
775
  }
451
776
  .topbar-note {
452
- font-size: 13px;
777
+ font-family: var(--label-face);
778
+ font-size: var(--label-size);
779
+ letter-spacing: var(--label-space);
780
+ text-transform: var(--label-case);
453
781
  color: var(--ink-faint);
454
782
  white-space: nowrap;
455
783
  overflow: hidden;
@@ -461,17 +789,19 @@ button { font-family: var(--font-body); }
461
789
  display: flex;
462
790
  align-items: baseline;
463
791
  gap: 12px;
464
- padding: 10px 24px;
792
+ padding: 10px 22px;
465
793
  font-size: 13px;
466
794
  color: var(--warn);
467
795
  background: var(--warn-weak);
468
- border-bottom: 1px solid var(--border);
796
+ border-bottom: 1px solid var(--rule-soft);
469
797
  }
470
798
  .denied-notice code {
471
799
  font-size: 12px;
472
800
  }
473
801
  .denied-notice__close {
474
802
  margin-left: auto;
803
+ min-width: 24px;
804
+ min-height: 24px;
475
805
  border: 0;
476
806
  background: none;
477
807
  color: inherit;
@@ -483,31 +813,32 @@ button { font-family: var(--font-body); }
483
813
  display: none;
484
814
  align-items: center;
485
815
  justify-content: center;
486
- width: 34px; height: 34px;
487
- border: 1px solid var(--border);
488
- border-radius: var(--radius-sm);
489
- background: var(--surface);
816
+ width: 32px; height: 32px;
817
+ border: 0;
818
+ border-radius: var(--radius-pill);
819
+ background: none;
490
820
  color: var(--ink-soft);
491
821
  cursor: pointer;
492
822
  padding: 0;
493
823
  }
494
- .topbar-menu svg { width: 18px; height: 18px; }
824
+ .topbar-menu:hover { color: var(--ink); }
825
+ .topbar-menu svg { width: 16px; height: 16px; }
495
826
 
496
827
  .theme-toggle {
497
828
  display: inline-flex;
498
829
  align-items: center;
499
830
  justify-content: center;
500
831
  width: 32px; height: 32px;
501
- border: 1px solid var(--border);
502
- border-radius: var(--radius-sm);
503
- background: var(--surface);
832
+ border: 0;
833
+ border-radius: var(--radius-pill);
834
+ background: none;
504
835
  color: var(--ink-soft);
505
836
  cursor: pointer;
506
837
  padding: 0;
507
838
  flex-shrink: 0;
508
839
  }
509
- .theme-toggle:hover { background: var(--surface-2); color: var(--ink); }
510
- .theme-toggle svg { width: 17px; height: 17px; }
840
+ .theme-toggle:hover { color: var(--ink); }
841
+ .theme-toggle svg { width: 16px; height: 16px; }
511
842
 
512
843
  /* task 1475: the accessibility zoom control — sibling of the theme toggle.
513
844
  Widens to fit the "125%"/"150%" label when zoom is active. */
@@ -518,24 +849,24 @@ button { font-family: var(--font-body); }
518
849
  gap: 4px;
519
850
  min-width: 32px; height: 32px;
520
851
  padding: 0 8px;
521
- border: 1px solid var(--border);
522
- border-radius: var(--radius-sm);
523
- background: var(--surface);
852
+ border: 0;
853
+ border-radius: var(--radius-pill);
854
+ background: none;
524
855
  color: var(--ink-soft);
525
856
  cursor: pointer;
526
857
  flex-shrink: 0;
527
858
  }
528
- .zoom-toggle:hover { background: var(--surface-2); color: var(--ink); }
529
- .zoom-toggle svg { width: 17px; height: 17px; }
530
- /* Active (zoomed-in) state reads as "on" with the instance accent. */
859
+ .zoom-toggle:hover { color: var(--ink); }
860
+ .zoom-toggle svg { width: 16px; height: 16px; }
861
+ /* Active (zoomed-in) state reads as "on" — the accent, because a live control
862
+ is exactly what the accent is for. */
531
863
  .zoom-toggle--on {
532
- border-color: color-mix(in srgb, var(--accent) 45%, transparent);
533
- background: var(--accent-weak);
534
- color: var(--accent-text);
864
+ background: var(--accent-tint);
865
+ color: var(--accent-ink);
535
866
  }
536
867
  .zoom-toggle__pct {
537
868
  font-size: 12px;
538
- font-weight: 600;
869
+ font-weight: 500;
539
870
  font-variant-numeric: tabular-nums;
540
871
  line-height: 1;
541
872
  }
@@ -544,15 +875,15 @@ button { font-family: var(--font-body); }
544
875
  width: 100%;
545
876
  max-width: calc(var(--content-max) + 64px);
546
877
  margin: 0 auto;
547
- padding: 24px 32px 64px;
878
+ padding: 26px 22px 108px;
548
879
  flex: 1;
549
880
  }
550
881
 
551
- .app-views .view > .view-zone + .view-zone { margin-top: var(--space-4); }
882
+ .app-views .view > .view-zone + .view-zone { margin-top: var(--sp-md); }
552
883
  .view-zone--band {
553
884
  display: grid;
554
885
  grid-template-columns: 1fr 1fr;
555
- gap: var(--space-4);
886
+ gap: var(--sp-md);
556
887
  align-items: start;
557
888
  }
558
889
  .view-zone--band > .scroll { margin: 0; }
@@ -564,11 +895,13 @@ button { font-family: var(--font-body); }
564
895
  gap: 12px;
565
896
  padding: 18px 4px 0;
566
897
  margin-top: 24px;
567
- border-top: 1px solid var(--border);
898
+ border-top: 1px solid var(--rule-soft);
568
899
  font-size: 12.5px;
569
900
  color: var(--ink-faint);
570
901
  }
571
- .app-foot a { color: var(--ink-faint); }
902
+ /* 3px of vertical padding so a link in a row with no rhythm of its own still
903
+ clears the 24px control floor (DESIGN.md §The footer). */
904
+ .app-foot a { color: var(--ink-faint); display: inline-block; padding: 3px 0; min-height: 24px; }
572
905
  .app-foot a:hover { color: var(--ink); }
573
906
  .app-foot span { margin-right: auto; }
574
907
 
@@ -576,54 +909,67 @@ button { font-family: var(--font-body); }
576
909
  position: fixed;
577
910
  inset: 0;
578
911
  z-index: var(--z-scrim);
579
- background: color-mix(in srgb, var(--ink) 40%, transparent);
912
+ background: rgba(0, 0, 0, .55);
580
913
  }
581
914
 
582
- /* ---------- responsive shell (scope §10) ---------- */
583
-
584
- @media (max-width: 1024px) {
585
- .app-sidebar { width: var(--sidebar-w-rail); }
586
- .app-body { margin-left: var(--sidebar-w-rail); }
587
- .brand-mark__name,
588
- .nav-item__label,
589
- .nav-group__head,
590
- .shell-user__meta { display: none; }
591
- .nav-item, .shell-home, .shell-signout { justify-content: center; padding: 0; }
592
- .nav-item--active::before { left: -6px; }
593
- .shell-user { justify-content: center; padding: 2px 0; }
594
- .shell-signin { font-size: 0; padding: 0; height: 34px; }
595
- .shell-signin::after { content: "→"; font-size: 15px; }
915
+ /* Focus: the world's two-tone ring — a 2px accent-derived outline at 3px offset
916
+ inside an 8px --bg halo, so the indicator keeps its own edge over whatever it
917
+ lands on (a bare outline measured 1.1:1 over the light bar). The rail's items
918
+ ring inside themselves instead, or the halo would spill out of the 64px strip. */
919
+ :focus-visible {
920
+ outline: 2px solid var(--focus);
921
+ outline-offset: 3px;
922
+ box-shadow: 0 0 0 8px var(--bg);
923
+ border-radius: var(--radius-sm);
924
+ }
925
+ .nav-item:focus-visible,
926
+ .shell-user:focus-visible,
927
+ .shell-home:focus-visible,
928
+ .shell-signout:focus-visible {
929
+ outline-offset: -2px;
930
+ box-shadow: none;
596
931
  }
597
932
 
933
+ /* ---------- responsive shell (scope §10) ----------
934
+ The rail holds at every width (v3), so there is no 1024px collapse any more.
935
+ What is left is the phone: under 768 the rail becomes the drawer shell.js
936
+ already owns, and the hamburger appears to open it. */
937
+
598
938
  @media (max-width: 768px) {
599
939
  .app-sidebar {
600
- width: var(--sidebar-w);
940
+ width: var(--rail-open);
601
941
  transform: translateX(-100%);
602
942
  transition: transform 160ms ease;
603
943
  z-index: var(--z-drawer);
604
944
  }
945
+ /* The drawer is the rail already open — the hover expansion is off, because
946
+ there is no hover on a phone and a drawer that needed one could not open. */
947
+ .app-sidebar:hover,
948
+ .app-sidebar:focus-within { width: var(--rail-open); }
605
949
  html.drawer-open .app-sidebar { transform: translateX(0); box-shadow: var(--shadow-pop); }
606
950
  .app-body { margin-left: 0; }
607
- .brand-mark__name, .nav-item__label, .nav-group__head, .shell-user__meta { display: block; }
608
- .nav-item, .shell-home, .shell-signout { justify-content: flex-start; padding: 0 10px; }
951
+ .brand-mark__name, .nav-item__label, .shell-user__meta { display: block; }
952
+ .nav-group--collapsible .nav-group__head { display: flex; }
953
+ .app-sidebar__brand { justify-content: flex-start; padding: 0 22px; }
954
+ .nav-item, .shell-home, .shell-signout { justify-content: flex-start; padding: 0 12px; }
955
+ .app-sidebar__foot { align-items: stretch; padding: 12px 16px; }
609
956
  .shell-user { justify-content: flex-start; }
610
- .shell-signin { font-size: 13.5px; padding: 0 10px; }
611
- .shell-signin::after { content: none; }
612
957
  .topbar-menu { display: flex; }
613
958
  .topbar-note { display: none; }
614
- .app-content { padding: 16px 16px 48px; }
959
+ .app-content { padding: 18px 14px 48px; }
615
960
  .view-zone--band { grid-template-columns: 1fr; }
616
961
  }
617
962
 
618
963
  @media (max-width: 480px) {
619
- .app-content { padding: 12px 10px 40px; }
620
- }
621
-
622
- @media (prefers-reduced-motion: reduce) {
623
- * { transition: none !important; animation: none !important; scroll-behavior: auto !important; }
964
+ .app-content { padding: 14px 12px 40px; }
624
965
  }
625
966
 
626
- /* ---------- cards (the .scroll legacy name is load-bearing) ---------- */
967
+ /* ---------- cards (the .scroll legacy name is load-bearing) ----------
968
+ v3's panels: --bg-card at the full --radius, with TONE doing the separation
969
+ and no hairline edge. The world's "hairlines, not containers" rule is a
970
+ poster's rule; the owner's pick answers the container question for an
971
+ application surface with yes, and the compromise the pick makes is that a
972
+ panel is a tone, never a drawn box and never a shadow. */
627
973
 
628
974
  /* The three sanctioned card levels (task 2179). Anything that looks like a card
629
975
  is one of these — a fourth variant is a design decision, not a local override:
@@ -631,11 +977,11 @@ button { font-family: var(--font-body); }
631
977
  .scroll--quiet secondary — a card INSIDE a card, or supporting detail
632
978
  .scroll--bare none — layout-only, for stat rows and grids */
633
979
  .scroll {
634
- background: var(--surface);
635
- border: 1px solid var(--border);
980
+ background: var(--bg-card);
981
+ border: 1px solid transparent;
636
982
  border-radius: var(--radius);
637
- padding: var(--space-4);
638
- margin: 0 0 var(--space-4);
983
+ padding: var(--sp-md);
984
+ margin: 0 0 var(--sp-md);
639
985
  /* anchor jumps must clear the 56px sticky top bar */
640
986
  scroll-margin-top: calc(var(--topbar-h) + 16px);
641
987
  }
@@ -646,88 +992,144 @@ button { font-family: var(--font-body); }
646
992
  border-radius: 0;
647
993
  padding: 0;
648
994
  }
649
- /* Secondary level: recedes against a primary card via surface-2 and a quieter,
650
- smaller header. Use for supporting detail — not to fit more onto Home. */
995
+ /* Secondary level: a card inside a card recedes by tone, one step further from
996
+ the ground than its parent. Use for supporting detail — not to fit more onto
997
+ Home. */
651
998
  .scroll--quiet {
652
999
  background: var(--surface-2);
653
- padding: var(--space-3);
1000
+ padding: var(--sp-sm);
654
1001
  }
655
1002
  .scroll--quiet > .scroll__h {
656
- font-size: 13.5px;
1003
+ font-size: 14px;
657
1004
  color: var(--ink-soft);
658
1005
  }
659
1006
 
1007
+ /* Section headings take the display voice in sentence case — v3's own
1008
+ divergence from the mono label, picked with it. */
660
1009
  .scroll__h {
661
- margin: 0 0 6px;
1010
+ margin: 0 0 8px;
662
1011
  font-family: var(--font-display);
663
- font-size: 15px;
664
- font-weight: 600;
665
- letter-spacing: -0.01em;
1012
+ font-weight: 300;
1013
+ font-size: 21px;
1014
+ line-height: 1.15;
1015
+ letter-spacing: -0.03em;
1016
+ color: var(--ink);
1017
+ text-shadow: var(--type-halo);
666
1018
  }
667
1019
  .scroll__lede {
668
1020
  margin: 0 0 14px;
669
1021
  font-size: 13.5px;
670
1022
  color: var(--ink-soft);
671
- max-width: 68ch;
1023
+ max-width: var(--measure);
672
1024
  }
673
1025
  .card-foot {
674
- margin: 12px 0 0;
1026
+ margin: 14px 0 0;
675
1027
  font-size: 13px;
676
1028
  }
677
1029
 
1030
+ /* The 24px floor for links that sit in PROSE (task 1003304). A link in a lede,
1031
+ a card foot or a setup hint is a real control in a line with no vertical
1032
+ rhythm of its own, so it renders at its 17-19px line box and fails the floor
1033
+ — measured on Home and the board at every width in both modes ("Roadmap"
1034
+ 59x18, "Goals page" 69x18, "View full standing" 118x18, "primer" 37x17).
1035
+ DESIGN.md's footer rule is the remedy: go inline-block and buy the height
1036
+ back with padding, which costs the line's own leading nothing.
1037
+ THE FLOOR IS BOTH AXES, and that is the half worth writing down — "Git" came
1038
+ back 17x24, still failing, because a three-letter link is genuinely narrower
1039
+ than 24px. The horizontal padding buys the width and the equal negative
1040
+ margin gives it straight back to the layout, so the target grows and the
1041
+ sentence does not re-space around it.
1042
+ It lives here rather than in each page's sheet because these classes are the
1043
+ shell's, and the alternative is five identical copies as 1003305-1003308
1044
+ land. */
1045
+ .scroll__lede a,
1046
+ .card-foot a,
1047
+ .next-up__foot a,
1048
+ .setup__steps a,
1049
+ .setup__hint a,
1050
+ .setup__foot a {
1051
+ display: inline-block;
1052
+ min-width: 24px;
1053
+ min-height: 24px;
1054
+ padding: 3px 4px;
1055
+ margin: 0 -4px;
1056
+ line-height: 18px;
1057
+ text-align: center;
1058
+ }
1059
+
678
1060
  .pill-link,
679
1061
  #welcome-reopen a {
680
- display: inline-block;
681
- font-size: 12.5px;
682
- font-weight: 500;
1062
+ display: inline-flex;
1063
+ align-items: center;
1064
+ min-height: 28px;
1065
+ font-size: 13px;
1066
+ text-transform: lowercase;
683
1067
  color: var(--ink-soft);
684
- background: var(--surface);
685
- border: 1px solid var(--border);
686
- border-radius: 999px;
687
- padding: 4px 12px;
1068
+ background: none;
1069
+ border: 1px solid var(--rule);
1070
+ border-radius: var(--radius-pill);
1071
+ padding: 0 12px;
688
1072
  }
689
1073
  #welcome-reopen { margin: 0; }
690
- .pill-link:hover { color: var(--ink); text-decoration: none; background: var(--surface-2); }
1074
+ .pill-link:hover { color: var(--ink); text-decoration: none; border-color: var(--ink-faint); }
691
1075
 
692
- /* ---------- stat tiles (Home) ---------- */
1076
+ /* ---------- stat tiles (Home) ----------
1077
+ v3's signature: ONE glass band of figures divided by vertical hairlines, the
1078
+ owner's dark reference exactly, rather than four separate objects. The figure
1079
+ is the display face at 300 with tabular numerals — every number the ledger
1080
+ produced holds its column when it refreshes (DESIGN.md, The Tabular Rule). */
693
1081
 
694
1082
  .stat-row {
695
1083
  display: grid;
696
1084
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
697
- gap: 16px;
1085
+ gap: 0;
1086
+ background: var(--glass);
1087
+ border: 1px solid var(--glass-edge);
1088
+ border-radius: var(--radius);
1089
+ backdrop-filter: blur(var(--glass-blur));
1090
+ -webkit-backdrop-filter: blur(var(--glass-blur));
1091
+ overflow: hidden;
698
1092
  }
699
1093
  .stat-tile {
700
- background: var(--surface);
701
- border: 1px solid var(--border);
702
- border-radius: var(--radius);
703
- padding: 14px 18px;
1094
+ background: none;
1095
+ border: 0;
1096
+ border-left: 1px solid var(--glass-edge);
1097
+ border-radius: 0;
1098
+ padding: 20px 22px;
704
1099
  min-height: 88px;
705
1100
  display: flex;
706
1101
  flex-direction: column;
707
1102
  justify-content: center;
708
- gap: 2px;
1103
+ gap: 8px;
709
1104
  }
1105
+ .stat-tile:first-child { border-left: 0; }
710
1106
  .stat-tile__label {
711
- font-size: 11px;
712
- font-weight: 600;
713
- letter-spacing: 0.06em;
714
- text-transform: uppercase;
1107
+ font-family: var(--label-face);
1108
+ font-size: var(--label-size);
1109
+ letter-spacing: var(--label-space);
1110
+ text-transform: var(--label-case);
715
1111
  color: var(--ink-faint);
716
1112
  }
717
1113
  .stat-tile__num {
718
- font-size: 26px;
719
- font-weight: 650;
720
- letter-spacing: -0.02em;
1114
+ font-family: var(--font-display);
1115
+ font-size: clamp(26px, 2.6vw, 34px);
1116
+ font-weight: 300;
1117
+ letter-spacing: -0.035em;
721
1118
  font-variant-numeric: tabular-nums;
722
- line-height: 1.15;
1119
+ line-height: 1;
1120
+ color: var(--ink);
1121
+ text-shadow: var(--type-halo);
723
1122
  }
724
- .stat-tile__num--badge { font-size: 15px; }
725
- .stat-tile__sub { font-size: 12px; color: var(--ink-faint); }
1123
+ .stat-tile__num--badge { font-size: 15px; letter-spacing: -0.02em; }
1124
+ .stat-tile__sub { font-size: 12.5px; color: var(--ink-faint); }
726
1125
 
727
1126
  @media (max-width: 480px) {
728
- .stat-row { grid-template-columns: 1fr 1fr; gap: 10px; }
729
- .stat-tile { padding: 12px 14px; min-height: 72px; }
730
- .stat-tile__num { font-size: 21px; }
1127
+ .stat-row { grid-template-columns: 1fr 1fr; }
1128
+ .stat-tile { padding: 14px 16px; min-height: 72px; gap: 6px; }
1129
+ .stat-tile__num { font-size: clamp(21px, 6.6vw, 28px); }
1130
+ /* On two columns the band still divides, but now on both axes. */
1131
+ .stat-tile:nth-child(odd) { border-left: 0; }
1132
+ .stat-tile:nth-child(n + 3) { border-top: 1px solid var(--glass-edge); }
731
1133
  }
732
1134
 
733
1135
  /* ---------- Standing layout: section rail + panels ---------- */
@@ -746,15 +1148,28 @@ button { font-family: var(--font-body); }
746
1148
  gap: 2px;
747
1149
  }
748
1150
  .rail-link {
749
- display: block;
750
- padding: 6px 10px;
1151
+ display: flex;
1152
+ align-items: center;
1153
+ min-height: 28px;
1154
+ padding: 0 10px;
751
1155
  border-radius: var(--radius-sm);
752
1156
  font-size: 13px;
753
- font-weight: 500;
1157
+ text-transform: lowercase;
754
1158
  color: var(--ink-soft);
1159
+ position: relative;
1160
+ }
1161
+ .rail-link:hover { background: var(--glass); color: var(--ink); text-decoration: none; }
1162
+ /* The current section is live, so it takes the accent — the same lit-step
1163
+ grammar the rail’s nav items use. */
1164
+ .rail-link--active { background: var(--glass-strong); color: var(--ink); }
1165
+ .rail-link--active::before {
1166
+ content: "";
1167
+ position: absolute;
1168
+ left: 0; top: 6px; bottom: 6px;
1169
+ width: 2px;
1170
+ border-radius: 1px;
1171
+ background: var(--accent);
755
1172
  }
756
- .rail-link:hover { background: var(--surface-2); color: var(--ink); text-decoration: none; }
757
- .rail-link--active { background: var(--accent-weak); color: var(--accent-text); }
758
1173
 
759
1174
  /* task 2174: a Standing rail section — the scroll-spy anchor holding one or
760
1175
  more cards. The extra air between groups is what makes the grouping legible
@@ -772,11 +1187,11 @@ button { font-family: var(--font-body); }
772
1187
  overflow-x: auto;
773
1188
  gap: 6px;
774
1189
  padding: 8px 0;
775
- background: color-mix(in srgb, var(--bg) 92%, transparent);
776
- backdrop-filter: blur(6px);
1190
+ background: var(--bg);
777
1191
  }
778
- .rail-link { white-space: nowrap; border: 1px solid var(--border); background: var(--surface); border-radius: 999px; padding: 4px 12px; font-size: 12.5px; }
779
- .rail-link--active { border-color: color-mix(in srgb, var(--accent) 40%, var(--border)); }
1192
+ .rail-link { white-space: nowrap; border: 1px solid var(--rule); background: none; border-radius: var(--radius-pill); min-height: 26px; padding: 0 12px; font-size: 12.5px; }
1193
+ .rail-link--active { border-color: var(--accent); background: none; }
1194
+ .rail-link--active::before { content: none; }
780
1195
  }
781
1196
 
782
1197
  /* ---------- legacy page chrome compat (pre-shell pages) ---------- */
@@ -871,28 +1286,30 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
871
1286
  .ledger th {
872
1287
  position: sticky;
873
1288
  top: var(--topbar-h);
874
- background: var(--surface);
1289
+ background: var(--bg-card);
875
1290
  text-align: left;
876
- font-size: 11px;
877
- font-weight: 600;
878
- letter-spacing: 0.06em;
879
- text-transform: uppercase;
1291
+ font-family: var(--label-face);
1292
+ font-size: var(--label-size);
1293
+ font-weight: 400;
1294
+ letter-spacing: var(--label-space);
1295
+ text-transform: var(--label-case);
880
1296
  color: var(--ink-faint);
881
- padding: 8px 12px;
882
- border-bottom: 1px solid var(--border-strong);
1297
+ padding: 0 12px 8px 0;
1298
+ border-bottom: 1px solid var(--rule);
883
1299
  z-index: 1;
884
1300
  }
885
1301
  .ledger td {
886
- padding: 12px;
1302
+ padding: 14px 12px 14px 0;
887
1303
  border-bottom: 1px solid var(--rule-soft);
888
1304
  vertical-align: top;
889
1305
  }
1306
+ .ledger th:last-child, .ledger td:last-child { padding-right: 0; }
890
1307
  .ledger tr:last-child td { border-bottom: none; }
891
- .ledger tbody tr:hover td { background: var(--surface-2); }
1308
+ .ledger tbody tr:hover td { background: var(--glass); }
892
1309
  /* task 1467: a builder name that links to their profile page reads as plain
893
1310
  text in the dense leaderboard/roster, revealing itself as a link on hover. */
894
1311
  .lb-name { color: inherit; }
895
- .lb-name:hover { color: var(--accent-text); text-decoration: underline; text-underline-offset: 2px; }
1312
+ .lb-name:hover { color: var(--accent-ink); text-decoration: underline; text-underline-offset: 3px; }
896
1313
  .ledger__empty {
897
1314
  color: var(--ink-faint);
898
1315
  font-size: 13px;
@@ -956,16 +1373,17 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
956
1373
  .filter {
957
1374
  display: flex;
958
1375
  flex-direction: column;
959
- gap: 3px;
960
- font-size: 11px;
961
- font-weight: 600;
962
- letter-spacing: 0.06em;
963
- text-transform: uppercase;
1376
+ gap: 4px;
1377
+ font-family: var(--label-face);
1378
+ font-size: var(--label-size);
1379
+ letter-spacing: var(--label-space);
1380
+ text-transform: var(--label-case);
964
1381
  color: var(--ink-faint);
965
1382
  }
966
1383
  .filter select {
967
1384
  min-width: 150px;
968
- height: 32px;
1385
+ min-height: 32px;
1386
+ font-family: var(--font-body);
969
1387
  padding: 4px 8px;
970
1388
  font-size: 13.5px;
971
1389
  text-transform: none;
@@ -976,7 +1394,8 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
976
1394
  selects it sits alongside. */
977
1395
  .filter input[type="search"] {
978
1396
  min-width: 200px;
979
- height: 32px;
1397
+ min-height: 32px;
1398
+ font-family: var(--font-body);
980
1399
  padding: 4px 8px;
981
1400
  font-size: 13.5px;
982
1401
  text-transform: none;
@@ -999,24 +1418,51 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
999
1418
  gap: 12px;
1000
1419
  margin-top: 12px;
1001
1420
  }
1002
- .pager__range { font-size: 12.5px; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
1421
+ .pager__range { font-family: var(--font-mono); font-size: 12px; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
1003
1422
 
1423
+ /* Pill or nothing (DESIGN.md, Shapes). A ghost button is a hairline pill; the
1424
+ one filled shape on a page is the accent door, and only where the action is
1425
+ the live thing. 28px of box plus the row’s own air clears the 24px floor. */
1004
1426
  .btn-ghost {
1005
1427
  display: inline-flex;
1006
1428
  align-items: center;
1429
+ justify-content: center;
1007
1430
  gap: 6px;
1008
- height: 30px;
1009
- padding: 0 12px;
1431
+ min-height: 30px;
1432
+ min-width: 56px;
1433
+ padding: 0 14px;
1010
1434
  font-size: 13px;
1011
- font-weight: 500;
1435
+ text-transform: lowercase;
1012
1436
  color: var(--ink-soft);
1013
- background: var(--surface);
1014
- border: 1px solid var(--border-strong);
1015
- border-radius: var(--radius-sm);
1437
+ background: none;
1438
+ border: 1px solid var(--rule);
1439
+ border-radius: var(--radius-pill);
1440
+ cursor: pointer;
1441
+ }
1442
+ .btn-ghost:hover:not(:disabled) { border-color: var(--ink-faint); color: var(--ink); text-decoration: none; }
1443
+ .btn-ghost:disabled { color: var(--ink-faint); border-color: var(--rule-soft); cursor: default; }
1444
+
1445
+ /* The accent door: the page’s one primary action. --accent-on is the dark ink
1446
+ that goes on the salmon; white on it fails AA and is banned. */
1447
+ .btn-accent {
1448
+ display: inline-flex;
1449
+ align-items: center;
1450
+ justify-content: center;
1451
+ gap: 6px;
1452
+ min-height: 34px;
1453
+ min-width: 74px;
1454
+ padding: 0 16px;
1455
+ font-size: 13.5px;
1456
+ font-weight: 500;
1457
+ text-transform: lowercase;
1458
+ color: var(--accent-on);
1459
+ background: var(--accent);
1460
+ border: 0;
1461
+ border-radius: var(--radius-pill);
1016
1462
  cursor: pointer;
1017
1463
  }
1018
- .btn-ghost:hover:not(:disabled) { background: var(--surface-2); color: var(--ink); text-decoration: none; }
1019
- .btn-ghost:disabled { opacity: 0.45; cursor: not-allowed; }
1464
+ .btn-accent:hover:not(:disabled) { background: var(--accent-lift); color: var(--accent-on); text-decoration: none; }
1465
+ .btn-accent:disabled { background: var(--surface-2); color: var(--ink-faint); cursor: default; }
1020
1466
 
1021
1467
  /* ---------- profile / standing ---------- */
1022
1468
 
@@ -1139,7 +1585,7 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
1139
1585
  .conn-reqs { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }
1140
1586
  .conn-req {
1141
1587
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
1142
- padding: 8px 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface-1);
1588
+ padding: 8px 10px; border: 1px solid var(--border); border-radius: var(--radius-sm); background: var(--surface-2);
1143
1589
  }
1144
1590
  .conn-req__note { flex: 1 1 100%; margin: 0; font-size: 13px; color: var(--ink-soft); }
1145
1591
  .conn-req__state { font-size: 13px; color: var(--ink-faint); }
@@ -1153,10 +1599,14 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
1153
1599
 
1154
1600
  /* ---------- rank badges + tooltip ---------- */
1155
1601
 
1602
+ /* 24px, not the old 20 (task 1003304). The badge is not itself a control, but
1603
+ .rank-badge-stretch below covers it edge to edge and IS one — so the badge's
1604
+ height is the link's height, and at 20 it failed the floor on every page that
1605
+ renders a rank. Only the box grew; the type and the colours are untouched. */
1156
1606
  .rank-badge {
1157
1607
  display: inline-flex;
1158
1608
  align-items: center;
1159
- height: 20px;
1609
+ height: 24px;
1160
1610
  padding: 0 9px;
1161
1611
  border-radius: 999px;
1162
1612
  font-size: 11.5px;
@@ -1429,25 +1879,36 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
1429
1879
 
1430
1880
  .setup { margin-top: 12px; }
1431
1881
  .setup__intro { font-size: 13px; color: var(--ink-soft); }
1432
- .setup__toggle { display: inline-flex; border: 1px solid var(--border-strong); border-radius: var(--radius-sm); overflow: hidden; margin: 8px 0 12px; }
1882
+ .setup__toggle { display: flex; border: 0; border-bottom: 1px solid var(--rule); margin: 8px 0 12px; flex-wrap: wrap; }
1883
+ /* An open tab is the live thing in the strip, so it is what the accent lights:
1884
+ a 2px underline sitting on the strip’s own hairline. */
1433
1885
  .setup__tab {
1434
- border: none;
1435
- background: var(--surface);
1886
+ display: inline-flex;
1887
+ align-items: baseline;
1888
+ gap: 8px;
1889
+ min-height: 38px;
1890
+ border: 0;
1891
+ border-bottom: 2px solid transparent;
1892
+ margin-bottom: -1px;
1893
+ background: none;
1436
1894
  color: var(--ink-soft);
1437
- font-size: 13px;
1438
- font-weight: 500;
1439
- padding: 6px 14px;
1895
+ font-size: 14px;
1896
+ text-transform: lowercase;
1897
+ padding: 0 16px;
1440
1898
  cursor: pointer;
1441
1899
  }
1442
- .setup__tab + .setup__tab { border-left: 1px solid var(--border-strong); }
1443
- .setup__tab[aria-selected="true"] { background: var(--accent-weak); color: var(--accent-text); }
1444
- .setup__tab-tag { font-size: 10.5px; margin-left: 6px; color: var(--ink-faint); }
1445
- .setup__tab-tag--rec { color: var(--ok); font-weight: 600; }
1900
+ .setup__tab:first-child { padding-left: 0; }
1901
+ .setup__tab:hover { color: var(--ink); }
1902
+ .setup__tab[aria-selected="true"] { color: var(--ink); border-bottom-color: var(--accent); }
1903
+ .setup__tab-tag { font-family: var(--label-face); font-size: 10.5px; letter-spacing: var(--label-space); text-transform: var(--label-case); margin-left: 6px; color: var(--ink-faint); }
1904
+ .setup__tab-tag--rec { color: var(--ok); }
1446
1905
  .setup__pane { display: none; }
1447
1906
  .setup[data-config="desktop"] .setup__pane--desktop { display: block; }
1448
1907
  .setup[data-config="online"] .setup__pane--online { display: block; }
1449
1908
  .setup__steps { margin: 8px 0; padding-left: 20px; font-size: 13.5px; }
1450
1909
  .setup__steps li { margin: 6px 0; }
1910
+ /* The setup panel's prose links ("Git", "Claude Code", "primer") take the
1911
+ shared prose-link floor rule in the cards section above (task 1003304). */
1451
1912
  .setup__steps code {
1452
1913
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
1453
1914
  font-size: 12px;
@@ -1531,46 +1992,67 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
1531
1992
  .setup__copy:hover { background: var(--surface-2); }
1532
1993
  .setup__copy--copied { background: var(--ok-weak); color: var(--ok); border-color: color-mix(in srgb, var(--ok) 35%, transparent); }
1533
1994
  .setup__getcode { margin: 10px 0; }
1534
- .setup__getcode-summary { cursor: pointer; font-weight: 600; font-size: 13px; }
1995
+ /* A <summary> is a control, so it owes the 24px floor like any other; it
1996
+ measured 20px tall (task 1003304). The height is bought with padding rather
1997
+ than a display change, because a <summary> is display:list-item by default
1998
+ and flex or block would silently eat its disclosure marker. */
1999
+ .setup__getcode-summary {
2000
+ cursor: pointer;
2001
+ font-weight: 600;
2002
+ font-size: 13px;
2003
+ min-height: 24px;
2004
+ padding: 3px 0;
2005
+ line-height: 18px;
2006
+ }
1535
2007
  .setup__getcode-lede { font-size: 12.5px; color: var(--ink-soft); }
1536
2008
  .setup__getcode-body { margin-top: 8px; }
1537
2009
 
1538
2010
  /* ---------- landing (signed-out) ---------- */
1539
2011
 
1540
- .landing { max-width: 560px; }
2012
+ .landing { max-width: var(--measure); }
1541
2013
  .landing__lede { font-size: 14px; color: var(--ink-soft); }
2014
+ /* A link inside landing prose is still a control: 3px of vertical padding is
2015
+ what clears the 24px floor in a line that has no rhythm of its own
2016
+ (DESIGN.md §The footer applies the same rule to its links). */
2017
+ .landing p a:not(.landing__btn) {
2018
+ display: inline-block;
2019
+ padding: 3px 0;
2020
+ min-height: 24px;
2021
+ line-height: 18px;
2022
+ }
1542
2023
  .landing__cta { margin: 14px 0; }
1543
2024
  .landing__btn {
1544
2025
  display: inline-flex;
1545
2026
  align-items: center;
1546
2027
  justify-content: center;
1547
- height: 36px;
1548
- padding: 0 18px;
1549
- border-radius: var(--radius-sm);
1550
- border: 1px solid var(--accent);
2028
+ min-height: 46px;
2029
+ padding: 0 22px;
2030
+ border-radius: var(--radius-pill);
2031
+ border: 0;
1551
2032
  background: var(--accent);
1552
- color: #fff;
1553
- font-size: 13.5px;
1554
- font-weight: 600;
2033
+ color: var(--accent-on);
2034
+ font-size: 14px;
2035
+ font-weight: 500;
2036
+ text-transform: lowercase;
1555
2037
  cursor: pointer;
1556
2038
  }
1557
- .landing__btn:hover { background: var(--accent-deep); text-decoration: none; color: #fff; }
2039
+ .landing__btn:hover { background: var(--accent-lift); text-decoration: none; color: var(--accent-on); }
1558
2040
  .landing__note { font-size: 12.5px; color: var(--ink-faint); }
1559
2041
  .landing__error { color: var(--danger); }
1560
2042
  .landing__error-h { color: var(--danger); margin: 0 0 4px; font-size: 14px; }
1561
2043
  .landing__request {
1562
- border: 1px solid var(--border);
2044
+ border: 0;
1563
2045
  border-radius: var(--radius);
1564
2046
  background: var(--surface-2);
1565
- padding: 16px 18px;
2047
+ padding: 18px 20px;
1566
2048
  margin: 16px 0;
1567
2049
  }
1568
- .landing__request-h { margin: 0 0 4px; font-size: 14px; font-weight: 650; }
2050
+ .landing__request-h { margin: 0 0 6px; font-family: var(--font-display); font-size: 18px; font-weight: 300; letter-spacing: -0.025em; }
1569
2051
  .landing__request-lede { margin: 0 0 12px; font-size: 13px; color: var(--ink-soft); }
1570
2052
  .landing__request-lede--soft { color: var(--ink-faint); }
1571
2053
  .landing__form { display: flex; flex-direction: column; gap: 10px; }
1572
- .landing__field { display: flex; flex-direction: column; gap: 3px; font-size: 12.5px; font-weight: 500; color: var(--ink-soft); }
1573
- .landing__field input { background: var(--surface); }
2054
+ .landing__field { display: flex; flex-direction: column; gap: 4px; font-family: var(--label-face); font-size: var(--label-size); letter-spacing: var(--label-space); text-transform: var(--label-case); color: var(--ink-faint); }
2055
+ .landing__field input { font-family: var(--font-body); font-size: 14px; text-transform: none; letter-spacing: normal; }
1574
2056
  .landing__req-mark { color: var(--danger); }
1575
2057
  .landing__opt { color: var(--ink-faint); font-weight: 400; }
1576
2058
  .landing__form-hint { font-size: 12.5px; color: var(--ink-faint); margin: 0; }
@@ -1613,32 +2095,87 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
1613
2095
  .goal-mine__ver { font-size: 12px; color: var(--ink-faint); }
1614
2096
  .goal-mine__foot { font-size: 13px; margin: 10px 0 0; }
1615
2097
 
1616
- /* ---------- work-board task cards (work page) ---------- */
2098
+ /* ---------- work-board task cards (work page) ----------
2099
+ Re-skinned into the chrome world by task 1003304 (goal 1000081). In v3 "the
2100
+ glass hall" a claimable task IS the panel — --bg-card at the full --radius,
2101
+ tone separating it from the ground, no drawn edge at rest — which is why
2102
+ #claim-scroll in work.html is .scroll--bare: a .scroll around these would be
2103
+ a card inside a card. The dense LISTS on the other three tabs keep their
2104
+ panel and take v1's bare-ledger row inside it (direction §6). */
1617
2105
 
1618
- .task-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 10px; }
2106
+ .task-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 12px; }
1619
2107
  .task {
1620
2108
  display: grid;
1621
2109
  grid-template-columns: auto minmax(0, 1fr) auto auto;
1622
- gap: 4px 14px;
1623
- align-items: center;
1624
- background: var(--surface);
1625
- border: 1px solid var(--border);
2110
+ gap: 0 18px;
2111
+ align-items: start;
2112
+ background: var(--bg-card);
2113
+ /* The edge is transparent at rest and spends --glass-edge on hover: v3
2114
+ separates by tone, and a drawn border on every row rebuilds the boxed
2115
+ stack the pick removed. The 1px is always there so nothing reflows. */
2116
+ border: var(--line) solid transparent;
1626
2117
  border-radius: var(--radius);
1627
- padding: 12px 16px;
1628
- }
1629
- .task__id { color: var(--ink-faint); font-variant-numeric: tabular-nums; font-size: 13px; display: flex; align-items: center; gap: 6px; }
1630
- /* task 1759: the work-board multi-select checkbox, hosted in the id cell so no
1631
- grid column needs to shift. Sized up slightly from a native default checkbox
1632
- so it's an easy target next to the small id text. */
1633
- .task__select { width: 15px; height: 15px; cursor: pointer; accent-color: var(--accent); flex: none; }
1634
- .task--selected { border-color: var(--accent); background: var(--accent-weak); }
1635
- /* task 1869: the explicit open-detail hitbox — clicking the card body now
1636
- toggles selection, so this small icon button is the only way in from a
1637
- claimable card to the task-detail overlay. */
2118
+ padding: 17px 22px;
2119
+ }
2120
+ .task:hover { border-color: var(--glass-edge); }
2121
+ .task__id { display: flex; align-items: center; gap: 2px; padding-top: 2px; }
2122
+ .task__num {
2123
+ font-family: var(--font-mono);
2124
+ font-size: 12.5px;
2125
+ font-variant-numeric: tabular-nums;
2126
+ color: var(--ink-faint);
2127
+ }
2128
+ /* task 1759, re-cut for the 24px floor (task 1003304): the hit area is 24px and
2129
+ the DRAWN box is 14px inside it. The old 15px input was the control itself,
2130
+ so it failed the floor at every width — painting the box with ::before is
2131
+ what lets the target grow without the checkbox growing with it. */
2132
+ .task__select {
2133
+ appearance: none;
2134
+ -webkit-appearance: none;
2135
+ position: relative;
2136
+ width: 24px;
2137
+ height: 24px;
2138
+ margin: 0;
2139
+ flex: none;
2140
+ cursor: pointer;
2141
+ }
2142
+ .task__select::before {
2143
+ content: "";
2144
+ position: absolute;
2145
+ left: 5px;
2146
+ top: 5px;
2147
+ width: 14px;
2148
+ height: 14px;
2149
+ border: var(--line) solid var(--rule);
2150
+ border-radius: 3px;
2151
+ }
2152
+ .task__select:checked::before { background: var(--accent); border-color: var(--accent); }
2153
+ .task__select:checked::after {
2154
+ content: "";
2155
+ position: absolute;
2156
+ left: 9px;
2157
+ top: 8px;
2158
+ width: 4px;
2159
+ height: 8px;
2160
+ border: solid var(--accent-on);
2161
+ border-width: 0 1.5px 1.5px 0;
2162
+ transform: rotate(42deg);
2163
+ }
2164
+ /* Selection is NOT hover. The mock draws both with --glass-edge, which reads
2165
+ fine on a mock with one selected row and not at all on a board whose whole
2166
+ point is selecting several for the bulk bar — so selection keeps an accent
2167
+ signal (a selected row is live, which is what the accent is for).
2168
+ The EDGE carries it, not a fill: --accent-tint is a 30% mix in light, and
2169
+ three selected rows painted with it took over the page (looked at, 1280w
2170
+ light). The checked box is already an accent fill on every selected row;
2171
+ a second one per row is what made it shout. */
2172
+ .task--selected { border-color: var(--accent); }
2173
+ /* task 1869: the explicit open-detail hitbox — clicking the card body toggles
2174
+ selection, so this is the only way from a claimable card into the overlay. */
1638
2175
  .task__open {
1639
2176
  flex: none;
1640
- width: 18px;
1641
- height: 18px;
2177
+ width: 26px;
2178
+ height: 26px;
1642
2179
  display: inline-flex;
1643
2180
  align-items: center;
1644
2181
  justify-content: center;
@@ -1650,41 +2187,68 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
1650
2187
  cursor: pointer;
1651
2188
  border-radius: var(--radius-sm);
1652
2189
  }
1653
- .task__open:hover { color: var(--ink); background: var(--surface-2); }
1654
- .task__body { min-width: 0; }
1655
- .task__title { font-weight: 600; font-size: 13.5px; margin: 0; }
1656
- .task__meta { display: flex; flex-wrap: wrap; gap: 4px 10px; font-size: 12px; color: var(--ink-faint); margin-top: 2px; }
2190
+ .task__open:hover { color: var(--ink); background: var(--glass); }
2191
+ .task__body { min-width: 0; padding-top: 2px; }
2192
+ .task__title { font-weight: 400; font-size: 14.5px; line-height: 1.4; margin: 0 0 7px; color: var(--ink); }
2193
+ .task__meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 8px; font-size: 12px; color: var(--ink-faint); margin-top: 0; }
2194
+ /* The label voice, outlined — a tag is a fact about the task, never a fill. */
1657
2195
  .task__tag {
1658
2196
  display: inline-flex;
1659
2197
  align-items: center;
1660
- height: 18px;
1661
- padding: 0 8px;
1662
- border-radius: 999px;
1663
- background: var(--surface-2);
1664
- border: 1px solid var(--border);
1665
- font-size: 11px;
1666
- font-weight: 500;
2198
+ padding: 2px 8px;
2199
+ border-radius: var(--radius-pill);
2200
+ background: none;
2201
+ border: var(--line) solid var(--rule);
2202
+ font-family: var(--label-face);
2203
+ font-size: 10.5px;
2204
+ letter-spacing: var(--label-space);
2205
+ text-transform: var(--label-case);
1667
2206
  color: var(--ink-soft);
1668
2207
  }
1669
- .task__credits { text-align: right; }
1670
- .task__credits-num { font-weight: 650; font-variant-numeric: tabular-nums; font-size: 15px; }
1671
- .task__credits-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-faint); }
2208
+ .task__credits { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; padding-top: 2px; }
2209
+ /* A credit figure is DATA, not live — the display face in ink, never accent
2210
+ (DESIGN.md, The One Warm Thing Rule + The Tabular Rule). */
2211
+ .task__credits-num {
2212
+ font-family: var(--font-display);
2213
+ font-weight: 300;
2214
+ font-size: 26px;
2215
+ line-height: 1;
2216
+ letter-spacing: -0.03em;
2217
+ font-variant-numeric: tabular-nums;
2218
+ color: var(--ink);
2219
+ }
2220
+ .task__credits-label {
2221
+ font-family: var(--label-face);
2222
+ font-size: 10px;
2223
+ letter-spacing: var(--label-space);
2224
+ text-transform: var(--label-case);
2225
+ color: var(--ink-faint);
2226
+ }
1672
2227
  .task__copy {
1673
- border: 1px solid var(--border-strong);
1674
- background: var(--surface);
1675
- color: var(--ink-soft);
1676
- border-radius: var(--radius-sm);
2228
+ display: inline-flex;
2229
+ align-items: center;
2230
+ justify-content: center;
2231
+ width: 32px;
2232
+ height: 32px;
2233
+ border: var(--line) solid var(--rule);
2234
+ background: none;
2235
+ color: var(--ink-faint);
2236
+ border-radius: var(--radius-pill);
1677
2237
  font-size: 12.5px;
1678
- padding: 4px 10px;
2238
+ padding: 0;
1679
2239
  cursor: pointer;
1680
2240
  }
1681
- .task__copy:hover { background: var(--surface-2); }
2241
+ .task__copy:hover { color: var(--ink); border-color: var(--ink-faint); }
2242
+ /* The status vocabulary, not the accent: "copied" is a verification state. */
1682
2243
  .task__copy[data-copied="1"] { background: var(--ok-weak); color: var(--ok); border-color: color-mix(in srgb, var(--ok) 35%, transparent); }
1683
- .task__copy-icon { margin-right: 4px; }
2244
+ .task__copy-icon { margin-right: 0; }
1684
2245
 
1685
2246
  @media (max-width: 640px) {
1686
- .task { grid-template-columns: auto minmax(0, 1fr); }
1687
- .task__credits { grid-column: 2; text-align: left; }
2247
+ .task { grid-template-columns: minmax(0, 1fr) auto; gap: 6px 14px; padding: 14px 16px; }
2248
+ .task__id { grid-column: 1; }
2249
+ .task__credits { grid-column: 2; grid-row: 1; flex-direction: row; align-items: baseline; gap: 6px; }
2250
+ .task__credits-num { font-size: 20px; }
2251
+ .task__body { grid-column: 1 / -1; }
1688
2252
  }
1689
2253
 
1690
2254
  /* ---------- ranks page ---------- */
@@ -1748,10 +2312,10 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
1748
2312
  transform: translate(-50%, 12px);
1749
2313
  z-index: 100;
1750
2314
  background: var(--ink);
1751
- color: var(--bg-card);
2315
+ color: var(--bg);
1752
2316
  font-size: 13.5px;
1753
- padding: 10px 18px;
1754
- border-radius: var(--radius);
2317
+ padding: 11px 20px;
2318
+ border-radius: var(--radius-pill);
1755
2319
  box-shadow: var(--shadow-pop);
1756
2320
  opacity: 0;
1757
2321
  visibility: hidden;
@@ -1782,7 +2346,7 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
1782
2346
  position: fixed;
1783
2347
  inset: 0;
1784
2348
  z-index: 2000;
1785
- background: color-mix(in srgb, var(--ink) 45%, transparent);
2349
+ background: rgba(0, 0, 0, .55);
1786
2350
  display: flex;
1787
2351
  align-items: center;
1788
2352
  justify-content: center;
@@ -1790,8 +2354,8 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
1790
2354
  }
1791
2355
  .deeplink-card__panel {
1792
2356
  position: relative;
1793
- background: var(--surface);
1794
- border: 1px solid var(--border);
2357
+ background: var(--bg-card);
2358
+ border: 1px solid var(--rule);
1795
2359
  border-radius: var(--radius);
1796
2360
  box-shadow: var(--shadow-pop);
1797
2361
  max-width: 560px;
@@ -1804,15 +2368,15 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
1804
2368
  position: absolute;
1805
2369
  top: 10px; right: 10px;
1806
2370
  width: 28px; height: 28px;
1807
- border: 1px solid var(--border);
1808
- border-radius: var(--radius-sm);
1809
- background: var(--surface);
2371
+ border: 1px solid var(--rule);
2372
+ border-radius: var(--radius-pill);
2373
+ background: none;
1810
2374
  color: var(--ink-soft);
1811
2375
  font-size: 16px;
1812
2376
  line-height: 1;
1813
2377
  cursor: pointer;
1814
2378
  }
1815
- .deeplink-card__close:hover { background: var(--surface-2); }
2379
+ .deeplink-card__close:hover { border-color: var(--ink-faint); color: var(--ink); }
1816
2380
  .deeplink-card__kind {
1817
2381
  font-size: 11px;
1818
2382
  font-weight: 600;
@@ -1821,7 +2385,7 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
1821
2385
  color: var(--ink-faint);
1822
2386
  margin: 0 0 4px;
1823
2387
  }
1824
- .deeplink-card__title { font-size: 16px; font-weight: 650; margin: 0 0 8px; }
2388
+ .deeplink-card__title { font-family: var(--font-display); font-size: 21px; font-weight: 300; letter-spacing: -0.03em; margin: 0 0 8px; }
1825
2389
  .deeplink-card__meta { display: flex; flex-wrap: wrap; gap: 4px 14px; font-size: 12.5px; color: var(--ink-soft); margin: 0 0 10px; }
1826
2390
  .deeplink-card__summary { font-size: 13.5px; margin: 0 0 10px; }
1827
2391
  .deeplink-card__note { font-size: 12.5px; color: var(--ink-faint); margin: 0; }
@@ -1954,10 +2518,21 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
1954
2518
  invitations to accept + join-requests to approve. Matches the home widgets'
1955
2519
  card language (.scroll host); each row is body-left / actions-right. */
1956
2520
  .ginbox { list-style: none; margin: 0 0 4px; padding: 0; }
2521
+ /* Home's sub-heading voice — the label token, not a hardcoded uppercase (task
2522
+ 1003304). Taking the token is what makes it flip: --label-face resolves to
2523
+ the mono in light and back to the body face in dark, because mono caps read
2524
+ as precise on paper and thin on black. All four users of this class
2525
+ (hall-widgets, goal-inbox, profile-nudge, builders) are Home surfaces. */
1957
2526
  .ginbox__sec-h {
1958
- margin: 14px 0 6px; font-size: 12px; font-weight: 600;
1959
- text-transform: uppercase; letter-spacing: 0.04em; color: var(--ink-faint);
2527
+ margin: 22px 0 10px;
2528
+ font-family: var(--label-face);
2529
+ font-size: var(--label-size);
2530
+ font-weight: 400;
2531
+ letter-spacing: var(--label-space);
2532
+ text-transform: var(--label-case);
2533
+ color: var(--ink-faint);
1960
2534
  }
2535
+ .ginbox__sec-h:first-child { margin-top: 0; }
1961
2536
  .ginbox__item {
1962
2537
  display: flex; align-items: flex-start; gap: 12px;
1963
2538
  padding: 9px 0; border-top: 1px solid var(--rule-soft);
@@ -1995,40 +2570,47 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
1995
2570
  /* filter-bar disclosure ("Filters" button) + removable active-filter chips */
1996
2571
  .filters__more-count { margin-left: 6px; }
1997
2572
  .filters--overflow {
1998
- padding: 10px 12px;
1999
- border: 1px dashed var(--border-strong);
2573
+ padding: 12px 14px;
2574
+ border: 0;
2000
2575
  border-radius: var(--radius-sm);
2001
2576
  background: var(--surface-2);
2002
2577
  margin-bottom: 14px;
2003
2578
  }
2004
2579
  .filter-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 12px; }
2580
+ /* An active filter IS live, so the chip is the one place in a filter bar the
2581
+ accent belongs. */
2005
2582
  .chip {
2006
- display: inline-flex; align-items: center; gap: 6px;
2007
- padding: 3px 10px;
2583
+ display: inline-flex; align-items: center; justify-content: center; gap: 6px;
2584
+ min-height: 26px;
2585
+ padding: 0 12px;
2008
2586
  font-size: 12.5px;
2009
- color: var(--accent-text);
2010
- background: var(--accent-weak);
2011
- border: 1px solid color-mix(in srgb, var(--accent) 30%, var(--border));
2012
- border-radius: 999px;
2587
+ text-transform: lowercase;
2588
+ color: var(--accent-ink);
2589
+ background: var(--accent-tint);
2590
+ border: 0;
2591
+ border-radius: var(--radius-pill);
2013
2592
  cursor: pointer;
2014
2593
  }
2015
- .chip:hover { background: color-mix(in srgb, var(--accent) 16%, var(--bg-card)); }
2594
+ .chip:hover { color: var(--ink); }
2016
2595
 
2017
2596
  /* tab strip — the .setup__tab idiom in the .setup__toggle frame, promoted */
2597
+ /* A tab strip is hairlines, not a segmented box: one rule under the row and a
2598
+ 2px accent underline on the open tab, the lit-step grammar again. */
2018
2599
  .kit-tabs {
2019
- display: inline-flex;
2600
+ display: flex;
2020
2601
  max-width: 100%;
2021
2602
  overflow-x: auto;
2022
- border: 1px solid var(--border-strong);
2023
- border-radius: var(--radius-sm);
2603
+ border: 0;
2604
+ border-bottom: 1px solid var(--rule);
2605
+ border-radius: 0;
2024
2606
  margin: 0 0 14px;
2025
2607
  }
2026
2608
  .kit-tabs .setup__tab { white-space: nowrap; }
2027
2609
  .kit-tabs .setup__tab .count-pill { margin-left: 6px; }
2028
2610
 
2029
2611
  /* empty state — one short line + at most one action link */
2030
- .empty-state { padding: 6px 0; }
2031
- .empty-state p { margin: 0; font-size: 13px; color: var(--ink-faint); }
2612
+ .empty-state { padding: 8px 0; }
2613
+ .empty-state p { margin: 0; font-size: 13.5px; color: var(--ink-soft); }
2032
2614
  .empty-state__action { display: inline-block; margin-top: 6px; font-size: 13px; }
2033
2615
 
2034
2616
  /* skeleton shimmer — standard-height placeholder rows while data loads, so
@@ -2038,22 +2620,10 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
2038
2620
  width: 60%;
2039
2621
  min-width: 80px;
2040
2622
  height: 12px;
2041
- border-radius: 6px;
2042
- background: linear-gradient(90deg,
2043
- color-mix(in srgb, var(--ink) 7%, var(--bg-card)) 25%,
2044
- color-mix(in srgb, var(--ink) 13%, var(--bg-card)) 45%,
2045
- color-mix(in srgb, var(--ink) 7%, var(--bg-card)) 65%);
2046
- background-size: 200% 100%;
2047
- animation: kit-skel-shimmer 1.2s ease-in-out infinite;
2623
+ border-radius: 2px;
2624
+ background: var(--skeleton);
2048
2625
  }
2049
2626
  .kit-skel-row td { border-bottom: 1px solid var(--rule-soft); }
2050
- @keyframes kit-skel-shimmer {
2051
- from { background-position: 120% 0; }
2052
- to { background-position: -80% 0; }
2053
- }
2054
- @media (prefers-reduced-motion: reduce) {
2055
- .kit-skel { animation: none; }
2056
- }
2057
2627
 
2058
2628
  /* collapsible card body — the Work-backlog <details> idiom, generalized */
2059
2629
  .kit-collapse__summary {
@@ -2065,47 +2635,45 @@ main [id] { scroll-margin-top: calc(var(--topbar-h) + 16px); }
2065
2635
  .kit-collapse__summary .scroll__h { display: inline; margin: 0; }
2066
2636
  .kit-collapse__body { margin-top: 12px; }
2067
2637
 
2068
- /* ---- Home dashboard (task 2169) ---- */
2638
+ /* ---- Home dashboard (task 2169; re-skinned into the world by task 1003304) ---- */
2069
2639
  /* Stat tiles are links now — inherit ink, reveal affordance on hover. */
2070
2640
  a.stat-tile { color: inherit; }
2071
- a.stat-tile:hover {
2072
- text-decoration: none;
2073
- border-color: color-mix(in srgb, var(--accent) 35%, var(--border));
2074
- background: color-mix(in srgb, var(--accent) 3%, var(--surface));
2075
- }
2641
+ a.stat-tile:hover { text-decoration: none; background: var(--glass); }
2642
+ a.stat-tile:hover .stat-tile__sub { color: var(--ink-soft); }
2076
2643
  /* The over-budget "More" row — one quiet line, not a card. */
2077
2644
  .home-more { margin: 0; font-size: 13px; color: var(--ink-soft); }
2078
2645
 
2079
- /* task 2184: two-column main zone on wide screens — Home reads as a
2080
- dashboard, not a stack. The attention inbox (and the More row) span both
2081
- columns; the home-main cards sit side by side. Mirrors the
2082
- .view-zone--band grid idiom (cards drop their own margins inside a grid). */
2646
+ /* task 2184's two-column main zone, RETIRED by task 1003304. The zone holds
2647
+ the attention inbox, the More row and the overflow — all three of which
2648
+ already spanned both columns — plus ONE card, Your work. So the second
2649
+ column was always empty and the "dashboard, not a stack" it was reaching for
2650
+ is what #zone-home-band above actually delivers (Standing beside Goals).
2651
+ v3 agrees: its board mock puts the two-up in a .band and runs
2652
+ "Needs your attention" and "Your work" full width as sheets. One column
2653
+ here, and the dead half of the page goes away. */
2083
2654
  #zone-home-main { display: grid; grid-template-columns: minmax(0, 1fr); gap: 20px; align-items: start; }
2084
2655
  #zone-home-main > .scroll { margin: 0; }
2085
- @media (min-width: 1024px) {
2086
- #zone-home-main { grid-template-columns: repeat(2, minmax(0, 1fr)); }
2087
- #zone-home-main > #attention-scroll,
2088
- #zone-home-main > #home-more-row,
2089
- #zone-home-main > #zone-home-overflow { grid-column: 1 / -1; }
2090
- }
2091
2656
 
2092
2657
  /* task 2184: the "Next up" slice in the Your-work card — the page's answer to
2093
- "what's my next action". Quiet rows, accented title, one foot link. */
2094
- .next-up { list-style: none; margin: 0 0 4px; padding: 0; display: flex; flex-direction: column; gap: 8px; }
2658
+ "what's my next action". v1's bare ledger row inside v3's panel (direction
2659
+ §6): a hairline and the row's own air, never a filled tile — a fill here was
2660
+ a card inside a card, and it was the last site in the hall spending
2661
+ --surface-2 as a raised surface. The first row drops its rule and its top
2662
+ padding so the list starts flush against the panel's air. */
2663
+ .next-up { list-style: none; margin: 0 0 4px; padding: 0; display: flex; flex-direction: column; gap: 0; }
2095
2664
  .next-up__item {
2096
2665
  display: flex;
2097
2666
  flex-direction: column;
2098
2667
  gap: 2px;
2099
- padding: 9px 12px;
2100
- border: 1px solid var(--border);
2101
- border-left: 3px solid color-mix(in srgb, var(--accent) 55%, var(--border));
2102
- border-radius: var(--radius-sm);
2103
- background: var(--surface-2);
2668
+ padding: 14px 0;
2669
+ border-top: var(--line) solid var(--rule-soft);
2670
+ background: none;
2104
2671
  }
2105
- .next-up__title { font-weight: 600; font-size: 13.5px; color: var(--ink); }
2106
- .next-up__title:hover { color: var(--accent-text); }
2107
- .next-up__meta { font-size: 12px; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
2108
- .next-up__foot { margin: 0 0 14px; font-size: 13px; }
2672
+ .next-up__item:first-child { border-top: 0; padding-top: 0; }
2673
+ .next-up__title { font-weight: 400; font-size: 14px; color: var(--ink); }
2674
+ .next-up__title:hover { color: var(--accent-ink); }
2675
+ .next-up__meta { font-family: var(--font-mono); font-size: 11.5px; color: var(--ink-faint); font-variant-numeric: tabular-nums; }
2676
+ .next-up__foot { margin: 14px 0 18px; font-size: 13px; }
2109
2677
 
2110
2678
  /* ---------- jump palette (task 2178) ----------
2111
2679
  The top-bar slot + the Ctrl/Cmd-K overlay. Styled from existing tokens only —