@bongos/core 1.23.18 → 1.23.20

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 (60) hide show
  1. package/.bongos-core.json +134 -49
  2. package/clients/bongos-client/README.md +1 -1
  3. package/clients/bongos-client/bongos-client.global.js +4 -0
  4. package/clients/bongos-client/index.cjs +4 -0
  5. package/clients/bongos-client/index.d.ts +4 -0
  6. package/clients/bongos-client/index.mjs +4 -0
  7. package/docs/api/openapi.json +43 -1
  8. package/docs/api-reference.md +4 -2
  9. package/docs/copy-inventory.md +992 -935
  10. package/docs/copy-registry.json +1897 -1313
  11. package/docs/module-api-changelog.md +4 -0
  12. package/docs/page-inventory.json +12 -2
  13. package/docs/page-readings.json +616 -576
  14. package/modules/platform-identity/builder-of-the-week.js +61 -24
  15. package/modules/platform-identity/community-crowns.js +176 -0
  16. package/modules/platform-identity/community-leaderboard.js +4 -1
  17. package/modules/platform-identity/community-movers.js +152 -0
  18. package/modules/platform-identity/migrations/platform_identity_033_period_features.sql +43 -0
  19. package/modules/platform-identity/routes/community-leaderboard.js +45 -3
  20. package/modules/public-landing/public/account.html +3 -2
  21. package/modules/public-landing/public/community-crowns-first.states.json +15 -0
  22. package/modules/public-landing/public/community-crowns-new-year.states.json +15 -0
  23. package/modules/public-landing/public/community-crowns-none.states.json +15 -0
  24. package/modules/public-landing/public/community-empty.states.json +1 -1
  25. package/modules/public-landing/public/community-movers-none.states.json +15 -0
  26. package/modules/public-landing/public/community-owner-two.states.json +2 -2
  27. package/modules/public-landing/public/community-owner.states.json +1 -1
  28. package/modules/public-landing/public/community-week-none.states.json +1 -1
  29. package/modules/public-landing/public/community.html +188 -22
  30. package/modules/public-landing/public/community.probes.json +20 -3
  31. package/modules/public-landing/public/community.states.json +8 -6
  32. package/modules/public-landing/public/connections.html +3 -2
  33. package/modules/public-landing/public/contact.html +3 -2
  34. package/modules/public-landing/public/guild.html +3 -2
  35. package/modules/public-landing/public/index.html +3 -2
  36. package/modules/public-landing/public/invitations.html +3 -2
  37. package/modules/public-landing/public/privacy.html +3 -2
  38. package/modules/public-landing/public/projects.html +3 -2
  39. package/modules/public-landing/public/terms.html +3 -2
  40. package/modules/ui-design/kit/fixtures/community-crowns-first.json +6 -0
  41. package/modules/ui-design/kit/fixtures/community-crowns-new-year.json +6 -0
  42. package/modules/ui-design/kit/fixtures/community-crowns-none.json +6 -0
  43. package/modules/ui-design/kit/fixtures/community-crowns.json +6 -0
  44. package/modules/ui-design/kit/fixtures/community-movers-none.json +6 -0
  45. package/modules/ui-design/kit/fixtures/community-movers.json +12 -0
  46. package/modules/ui-design/kit/serve.js +22 -0
  47. package/package-lock.json +2 -2
  48. package/package.json +1 -1
  49. package/release-notes.json +12 -0
  50. package/scripts/bongos/run-unit-tests.js +8 -0
  51. package/src/module-api.js +1 -1
  52. package/src/platform-server.js +4 -3
  53. package/tests/community_crowns.mjs +369 -0
  54. package/tests/community_crowns_db.mjs +164 -0
  55. package/tests/community_movers.mjs +280 -0
  56. package/tests/community_movers_db.mjs +147 -0
  57. package/tests/hub_account_page.mjs +2 -2
  58. package/tests/hub_community_page.mjs +213 -29
  59. package/tests/landing_legal_pages.mjs +2 -2
  60. package/tests/privacy_matrix.mjs +107 -9
@@ -0,0 +1,15 @@
1
+ {
2
+ "_": "The community home in early January 2027 (task 1004902; fixtures/community-crowns-new-year.json): 2026 crowned for good and named under the new year so far; history began before 2027, so no 'since'.",
3
+ "page": "/community",
4
+ "surface": "public-landing",
5
+ "modes": ["dark"],
6
+ "modeQuery": false,
7
+ "stub": { "fixtures": { "community-crowns": "community-crowns-new-year" } },
8
+ "ignoreRequests": ["/api/(gds|bongos)/me$", "/my-projects", "/me/guild-", "/scouting$"],
9
+ "ignoreConsole": ["/api/(gds|bongos)/me", "/scouting", "\\b40[13]\\b", "Unauthorized", "Forbidden"],
10
+ "ignoreControls": [".vh"],
11
+ "states": {
12
+ "out-home-new-year": { "auth": false, "actions": [["wait", 400]], "expect": { "visible": ["#home", "#homeCrowns", "#crownYearLast"], "hidden": ["#homeFailed"], "text": [["#crownMonthWhen", "December 2026"], ["#crownYearWhen", "2027 so far"], ["#crownYear .crownName", "Lin Wei"], ["#crownYearLast", "2026: Maximiliana van der Berghe-Lindqvist, 11,250 credits"]] } }
13
+ },
14
+ "reducedMotion": { "auth": false, "actions": [["wait", 400]] }
15
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "_": "The community home with no one to show (task 1004902; fixtures/community-crowns-none.json): nobody qualified, or each winner hides their numbers — the two look the same (spec D8). Each card says so in one line.",
3
+ "page": "/community",
4
+ "surface": "public-landing",
5
+ "modes": ["dark"],
6
+ "modeQuery": false,
7
+ "stub": { "fixtures": { "community-crowns": "community-crowns-none" } },
8
+ "ignoreRequests": ["/api/(gds|bongos)/me$", "/my-projects", "/me/guild-", "/scouting$"],
9
+ "ignoreConsole": ["/api/(gds|bongos)/me", "/scouting", "\\b40[13]\\b", "Unauthorized", "Forbidden"],
10
+ "ignoreControls": [".vh"],
11
+ "states": {
12
+ "out-home-none": { "auth": false, "actions": [["wait", 400]], "expect": { "visible": ["#home", "#homeCrowns", "#crownWeek .commMsg", "#crownMonth .commMsg", "#crownYear .commMsg"], "hidden": ["#homeFailed", "#crownYearLast"], "count": [["#homeCrowns .crownName", 0]], "text": [["#crownWeek .commMsg", "No builder of the week for 26 Oct – 1 Nov 2026."], ["#crownMonth .commMsg", "No builder of the month for October 2026."], ["#crownYear .commMsg", "No builder of 2026 yet."]] } }
13
+ },
14
+ "reducedMotion": { "auth": false, "actions": [["wait", 400]] }
15
+ }
@@ -9,7 +9,7 @@
9
9
  "ignoreConsole": ["/api/(gds|bongos)/me", "/scouting", "\\b40[13]\\b", "Unauthorized", "Forbidden"],
10
10
  "ignoreControls": [".vh"],
11
11
  "states": {
12
- "out-empty": { "auth": false, "actions": [["wait", 400]], "expect": { "visible": ["#boardEmpty", "#boardWindow"], "hidden": ["#boardFailed", "#boardLoading", "#find", "#recruit"], "count": [["#boardList .commRow", 0]], "text": [["#boardEmpty", "No builders on the leaderboard yet."]] } }
12
+ "out-empty": { "auth": false, "url": "/community/leaderboard", "actions": [["wait", 400]], "expect": { "visible": ["#boardEmpty", "#boardWindow"], "hidden": ["#boardFailed", "#boardLoading", "#find", "#recruit"], "count": [["#boardList .commRow", 0]], "text": [["#boardEmpty", "No builders on the leaderboard yet."]] } }
13
13
  },
14
14
  "reducedMotion": { "auth": false, "actions": [["wait", 400]] }
15
15
  }
@@ -0,0 +1,15 @@
1
+ {
2
+ "_": "The community home with no movers to show (task 1002983; fixtures/community-movers-none.json): the row under the crowns stands with one line in each card.",
3
+ "page": "/community",
4
+ "surface": "public-landing",
5
+ "modes": ["dark"],
6
+ "modeQuery": false,
7
+ "stub": { "fixtures": { "community-movers": "community-movers-none" } },
8
+ "ignoreRequests": ["/api/(gds|bongos)/me$", "/my-projects", "/me/guild-", "/scouting$"],
9
+ "ignoreConsole": ["/api/(gds|bongos)/me", "/scouting", "\\b40[13]\\b", "Unauthorized", "Forbidden"],
10
+ "ignoreControls": [".vh"],
11
+ "states": {
12
+ "out-home-movers-none": { "auth": false, "actions": [["wait", 400]], "expect": { "visible": ["#homeMovers", "#moveRising .commMsg", "#moveNone", "#moversRule"], "count": [["#moveProjects .commRow", 0]], "text": [["#moveRising .commMsg", "No rising builder in the last 30 days."], ["#moveNone", "No public project shipped a work in the last 30 days."]] } }
13
+ },
14
+ "reducedMotion": { "auth": false, "actions": [["wait", 400]] }
15
+ }
@@ -9,8 +9,8 @@
9
9
  "ignoreConsole": ["/api/(gds|bongos)/me", "/scouting", "\\b40[13]\\b", "Unauthorized", "Forbidden"],
10
10
  "ignoreControls": [".vh"],
11
11
  "states": {
12
- "in-owner-two": { "auth": true, "actions": [["wait", 600]], "expect": { "visible": ["#boardFor", "#boardForPick"], "value": [["#boardForPick", "0"]], "count": [["#boardForPick option", 2], ["#boardList a.commRecruit", 6]], "href": [["#boardList .commRow:first-child .commRecruit", "https://mercury.example.test/builders/watch?invite=priyanair"]] } },
13
- "in-owner-two-picked": { "auth": true, "actions": [["wait", 600], ["select", "#boardForPick", "1"], ["wait", 200]], "expect": { "value": [["#boardForPick", "1"]], "href": [["#boardList .commRow:first-child .commRecruit", "https://tidepool.example.test/builders/watch?invite=priyanair"]], "attr": [["#boardList .commRow:first-child .commRecruit", "aria-label", "Recruit Priya Nair to Tidepool (opens in a new tab)"]] } }
12
+ "in-owner-two": { "auth": true, "url": "/community/leaderboard", "actions": [["wait", 600]], "expect": { "visible": ["#boardFor", "#boardForPick"], "value": [["#boardForPick", "0"]], "count": [["#boardForPick option", 2], ["#boardList a.commRecruit", 6]], "href": [["#boardList .commRow:first-child .commRecruit", "https://mercury.example.test/builders/watch?invite=priyanair"]] } },
13
+ "in-owner-two-picked": { "auth": true, "url": "/community/leaderboard", "actions": [["wait", 600], ["select", "#boardForPick", "1"], ["wait", 200]], "expect": { "value": [["#boardForPick", "1"]], "href": [["#boardList .commRow:first-child .commRecruit", "https://tidepool.example.test/builders/watch?invite=priyanair"]], "attr": [["#boardList .commRow:first-child .commRecruit", "aria-label", "Recruit Priya Nair to Tidepool (opens in a new tab)"]] } }
14
14
  },
15
15
  "reducedMotion": { "auth": true, "actions": [["wait", 600]] }
16
16
  }
@@ -9,7 +9,7 @@
9
9
  "ignoreConsole": ["/api/(gds|bongos)/me", "/scouting", "\\b40[13]\\b", "Unauthorized", "Forbidden"],
10
10
  "ignoreControls": [".vh"],
11
11
  "states": {
12
- "in-owner": { "auth": true, "actions": [["wait", 600]], "expect": { "visible": ["#board", "#boardFor", "#who"], "hidden": ["#find", "#recruit", "#boardFailed"], "count": [["#boardList .commRow", 6], ["#boardList a.commRecruit", 6]], "text": [["#boardFor", "Recruit opens Mercury’s hall with their name filled in, and you send the invite there."], ["#boardList .commRow:first-child .commRecruit", "Recruit"]], "href": [["#boardList .commRow:first-child .commRecruit", "https://mercury.example.test/builders/watch?invite=priyanair"]] } },
12
+ "in-owner": { "auth": true, "url": "/community/leaderboard", "actions": [["wait", 600]], "expect": { "visible": ["#board", "#boardFor", "#who"], "hidden": ["#find", "#recruit", "#boardFailed"], "count": [["#boardList .commRow", 6], ["#boardList a.commRecruit", 6]], "text": [["#boardFor", "Recruit opens Mercury’s hall with their name filled in, and you send the invite there."], ["#boardList .commRow:first-child .commRecruit", "Recruit"]], "href": [["#boardList .commRow:first-child .commRecruit", "https://mercury.example.test/builders/watch?invite=priyanair"]] } },
13
13
  "in-owner-find": { "auth": true, "url": "/community/find", "actions": [["wait", 600], ["fill", "#findQ", "pri"], ["click", "#findBtn"], ["wait", 300]], "expect": { "visible": ["#find", "#findFor"], "hidden": ["#board", "#recruit"], "count": [["#findList .mineRow", 2], ["#findList .mineRow__actions a.commRecruit", 2]], "href": [["#findList .mineRow:first-child .commRecruit", "https://mercury.example.test/builders/watch?invite=priyanair"]] } }
14
14
  },
15
15
  "reducedMotion": { "auth": true, "actions": [["wait", 600]] }
@@ -10,7 +10,7 @@
10
10
  "ignoreControls": [".vh"],
11
11
  "states": {
12
12
  "out-week-none": { "auth": false, "url": "/community/week", "actions": [["wait", 400]], "expect": { "visible": ["#week", "#weekNone", "#weekRuleH"], "hidden": ["#weekCrown", "#weekLoading", "#weekFailed", "#pastH"], "count": [["#weekPast .commRow", 0]], "text": [["#weekNone", "No builder of the week for 19–25 Oct 2026."]] } },
13
- "out-board-no-crown": { "auth": false, "actions": [["wait", 400]], "expect": { "visible": ["#board", "#boardList"], "hidden": ["#boardCrown"] } }
13
+ "out-board-no-crown": { "auth": false, "url": "/community/leaderboard", "actions": [["wait", 400]], "expect": { "visible": ["#board", "#boardList"], "hidden": ["#boardCrown"] } }
14
14
  },
15
15
  "reducedMotion": { "auth": false, "actions": [["wait", 400]] }
16
16
  }
@@ -5,15 +5,19 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1">
6
6
  <!-- THE COMMUNITY PAGES (task 1003363; ADR 0217 D1, ADR 0337 D1/D3/D4.1, the community
7
7
  spec docs/specs/bongos-v1-community-tab.md D1/D3/D4/D8). Who is building, across every
8
- project, as four sub-pages of the bar's Community menu (owner, 2026-10-27), all served
9
- from this one file: /community is the leaderboard, /community/week the builder of the
10
- week (task 1002981, spec D5), /community/find the builder search, /community/recruiters
11
- the builders who hide their numbers but are open to recruiters.
8
+ project, as five sub-pages of the bar's Community menu (owner, 2026-10-27), all served
9
+ from this one file: /community is the community home (task 1004902: the builders of the
10
+ week, month and year, under them the rising builder and the projects on the move (task
11
+ 1002983), and a door to each page), /community/leaderboard the leaderboard,
12
+ /community/week the builder of the week (task 1002981, spec D5), /community/find the
13
+ builder search, /community/recruiters the builders who hide their numbers but are open
14
+ to recruiters.
12
15
  The script shows the section the path names and marks it current in the menu.
13
- VIEWING IS UNGATED (ADR 0217 D1): the leaderboard, the builder of the week and the search
14
- are the same for every viewer, signed in or not. All three reads are public,
16
+ VIEWING IS UNGATED (ADR 0217 D1): the home, the leaderboard, the builder of the week and
17
+ the search are the same for every viewer, signed in or not. All five reads are public,
15
18
  viewer-independent routes (modules/platform-identity/routes/community-leaderboard.js,
16
- which also serves the builder of the week, and community-search.js),
19
+ which also serves the builder of the week, the crowns and the movers, and
20
+ community-search.js),
17
21
  which decide WHO appears through account-visibility.js; these pages add no rule of
18
22
  their own. The one viewer-relative page is the recruiter list: drawn only from a 200 of
19
23
  GET /scouting, whose door (a project owner, or project.curate) is the server's, never
@@ -27,7 +31,7 @@
27
31
  platform-identity is on. noindex until the SEO goal decides (owner, 2026-10-27).
28
32
  Server-stamped like the footer doors: {{…}} tokens filled by landingPage(). -->
29
33
  <title>{{productName}} — Community</title>
30
- <meta name="description" content="Who is building on {{productName}}: the leaderboard across every project, and a search for builders by name.">
34
+ <meta name="description" content="Who is building on {{productName}}: the builders of the week, month and year, the leaderboard across every project, and a search for builders by name.">
31
35
  <meta name="robots" content="noindex">
32
36
  <link rel="preconnect" href="https://fonts.googleapis.com">
33
37
  <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
@@ -110,6 +114,33 @@
110
114
  .commSub{margin:24px 0 4px;font-family:var(--font-body);font-size:14.5px;font-weight:500;color:var(--ink);}
111
115
  .weekRows .commRow{grid-template-columns:6.5rem 36px minmax(0,1fr) auto;}
112
116
  .weekRows .commPlace{font-family:var(--label-face);}
117
+ /* the community home (task 1004902): the week's, month's and year's crowns side by side in
118
+ the week page's pieces, then a door to each community page */
119
+ .homeCrowns{list-style:none;margin:6px 0 4px;padding:0;display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:0 clamp(20px,3vw,40px);}
120
+ .homeCard{display:flex;flex-direction:column;align-items:flex-start;gap:14px;min-width:0;padding:20px 0 24px;border-top:1px solid var(--rule-soft);}
121
+ .homeCard .commSub{margin:0;}
122
+ .homeBody{display:flex;flex-direction:column;gap:12px;min-width:0;}
123
+ .homeCard .crownWho .connAv{width:48px;height:48px;}
124
+ .homeCard .crownName{font-size:clamp(20px,2.2vw,24px);}
125
+ .homeCard .crownNums{text-align:left;}
126
+ .homeCard .commMsg{margin:0;}
127
+ .homeLast{margin:0;font-size:13.5px;line-height:1.5;color:var(--ink-soft);}
128
+ .homeLast strong{font-weight:500;color:var(--ink);}
129
+ .homeDoors{list-style:none;display:flex;flex-wrap:wrap;gap:10px 12px;margin:10px 0 0;padding:0;}
130
+ .homeDoors .btn{text-transform:none;}
131
+ /* the row under the crowns (task 1002983): the rising builder in the first column, the
132
+ projects on the move across the other two as board rows with no face */
133
+ .homeMovers{margin-top:14px;}
134
+ .homeMovers .homeWide{grid-column:span 2;align-items:stretch;}
135
+ .moveRows{margin:0;}
136
+ .moveRows .commRow{grid-template-columns:3ch minmax(0,1fr) auto;padding:10px 0;}
137
+ .moveRows a{display:inline-block;min-height:24px;padding:3px 0;line-height:18px;color:var(--ink);
138
+ text-decoration:underline;text-decoration-color:var(--accent);text-underline-offset:4px;}
139
+ .moveRows a:focus-visible{outline:2px solid var(--focus);outline-offset:3px;}
140
+ @media (max-width:760px){
141
+ .homeCrowns{grid-template-columns:minmax(0,1fr);}
142
+ .homeMovers .homeWide{grid-column:auto;}
143
+ }
113
144
  @media (max-width:480px){
114
145
  .commCrown{grid-template-columns:minmax(0,1fr);}
115
146
  .crownNums{text-align:left;}
@@ -154,7 +185,8 @@
154
185
  <div class="menuBand" id="menuCommunity" data-menu="community" hidden>
155
186
  <div class="bandIn">
156
187
  <ul class="bandRow">
157
- <li class="bandCell"><a class="bandT" href="/community">Leaderboard</a><span class="bandC">who earned the most, across every project</span></li>
188
+ <li class="bandCell"><a class="bandT" href="/community">Community home</a><span class="bandC">the builders of the week, month and year</span></li>
189
+ <li class="bandCell"><a class="bandT" href="/community/leaderboard">Leaderboard</a><span class="bandC">who earned the most, across every project</span></li>
158
190
  <li class="bandCell"><a class="bandT" href="/community/week">Builder of the week</a><span class="bandC">the top earner of each week, and how it is chosen</span></li>
159
191
  <li class="bandCell"><a class="bandT" href="/community/find">Find a builder</a><span class="bandC">anyone building here, by name or role</span></li>
160
192
  <li class="bandCell"><a class="bandT" href="/community/recruiters">Open to recruiters</a><span class="bandC">who is open to recruiting, for project owners</span></li>
@@ -169,7 +201,7 @@
169
201
  <li class="bandCell"><a class="bandT" href="/projects?view=map&amp;sub=table">Table</a><span class="bandC">the same projects as rows</span></li>
170
202
  <li class="bandCell" id="mineCell"><a class="bandT" href="/projects?view=mine">Your projects</a><span class="bandC">the ones you own</span></li>
171
203
  <li class="bandCell"><a class="bandT" href="/projects?view=new">New project</a><span class="bandC">five questions and a home</span></li>
172
- <li class="bandCell bandFold"><a class="bandT" href="/community">Leaderboard</a><span class="bandC">who earned the most, across every project</span></li><li class="bandCell bandFold"><a class="bandT" href="/community/week">Builder of the week</a><span class="bandC">the top earner of each week, and how it is chosen</span></li><li class="bandCell bandFold"><a class="bandT" href="/community/find">Find a builder</a><span class="bandC">anyone building here, by name or role</span></li><li class="bandCell bandFold"><a class="bandT" href="/community/recruiters">Open to recruiters</a><span class="bandC">who is open to recruiting, for project owners</span></li><li class="bandCell bandFold knowCell" hidden><a class="bandT" href="/connections#connSuggest">Builders you may know</a><span class="bandC">connections or projects in common</span></li>
204
+ <li class="bandCell bandFold"><a class="bandT" href="/community">Community home</a><span class="bandC">the builders of the week, month and year</span></li><li class="bandCell bandFold"><a class="bandT" href="/community/leaderboard">Leaderboard</a><span class="bandC">who earned the most, across every project</span></li><li class="bandCell bandFold"><a class="bandT" href="/community/week">Builder of the week</a><span class="bandC">the top earner of each week, and how it is chosen</span></li><li class="bandCell bandFold"><a class="bandT" href="/community/find">Find a builder</a><span class="bandC">anyone building here, by name or role</span></li><li class="bandCell bandFold"><a class="bandT" href="/community/recruiters">Open to recruiters</a><span class="bandC">who is open to recruiting, for project owners</span></li><li class="bandCell bandFold knowCell" hidden><a class="bandT" href="/connections#connSuggest">Builders you may know</a><span class="bandC">connections or projects in common</span></li>
173
205
  </ul>
174
206
  </div>
175
207
  </div>
@@ -188,13 +220,46 @@
188
220
  <main class="acct" id="comm">
189
221
  <h1 class="vh">Community</h1>
190
222
 
191
- <!-- FOUR SUB-PAGES, ONE FILE: every section is real markup; the script un-hides the one
192
- the path names (/community, /community/week, /community/find, /community/recruiters). -->
223
+ <!-- FIVE SUB-PAGES, ONE FILE: every section is real markup; the script un-hides the one
224
+ the path names (/community, /community/leaderboard, /community/week, /community/find,
225
+ /community/recruiters). -->
193
226
  <div class="mineExtra">
227
+ <!-- THE COMMUNITY HOME (task 1004902; owner, 2026-10-27): the builders of the week, the
228
+ month and the year from GET /community/crowns (community-crowns.js decides and
229
+ records them), the rule in plain words, then a door to each community page. The
230
+ same for every viewer but "Builders you may know", shown signed in like its menu item. -->
231
+ <section id="home" data-view="home" aria-labelledby="homeH">
232
+ <h2 id="homeH">Top builders</h2>
233
+ <p class="commMsg" id="homeLoading" role="status">Reading the top builders…</p>
234
+ <p class="projMsg err" id="homeFailed" role="alert" hidden></p>
235
+ <ul class="homeCrowns" id="homeCrowns" hidden>
236
+ <li class="homeCard"><h3 class="commSub">Builder of the week</h3><p class="crownWhen" id="crownWeekWhen"></p><div class="homeBody" id="crownWeek"></div></li>
237
+ <li class="homeCard"><h3 class="commSub">Builder of the month</h3><p class="crownWhen" id="crownMonthWhen"></p><div class="homeBody" id="crownMonth"></div></li>
238
+ <li class="homeCard"><h3 class="commSub">Builder of the year</h3><p class="crownWhen" id="crownYearWhen"></p><div class="homeBody" id="crownYear"></div><p class="homeLast" id="crownYearLast" hidden></p></li>
239
+ </ul>
240
+ <p class="commNote" id="homeRule">Each is the builder who earned the most {{currency}} across every project in that time, among the builders who shipped at least one work in it. A tie goes to whoever shipped more works, then to whoever earned more karma. Builders who hide their numbers or keep their account private are not counted. The week and the month are decided once, when they end; the year shows the leader so far, and is decided on 1 January.</p>
241
+ <!-- THE ROW UNDER THE CROWNS (task 1002983; owner, 2026-10-28): the rising builder and
242
+ the projects on the move, the last 30 days, from GET /community/movers
243
+ (community-movers.js decides both). Its own read: a failed one drops only this row. -->
244
+ <ul class="homeCrowns homeMovers" id="homeMovers" hidden>
245
+ <li class="homeCard"><h3 class="commSub">Rising builder</h3><p class="crownWhen">The last 30 days</p><div class="homeBody" id="moveRising"></div></li>
246
+ <li class="homeCard homeWide"><h3 class="commSub">Projects on the move</h3><p class="crownWhen">The last 30 days</p><ol class="commRows moveRows" id="moveProjects"></ol><p class="commMsg" id="moveNone" hidden>No public project shipped a work in the last 30 days.</p></li>
247
+ </ul>
248
+ <p class="commNote" id="moversRule" hidden>The rising builder is the one who earned the most {{currency}} in the last 30 days, among the builders who had earned under 100 {{currency}} before them and shipped at least one work in them. Projects on the move are the public projects that shipped the most works in the last 30 days, then earned the most {{currency}}. Builders who hide their numbers or keep their account private are not counted in either.</p>
249
+ <h3 class="commSub" id="doorsH">Community pages</h3>
250
+ <ul class="homeDoors" aria-labelledby="doorsH">
251
+ <li><a class="btn ghost" href="/community/leaderboard">Leaderboard</a></li>
252
+ <li><a class="btn ghost" href="/community/week">Builder of the week</a></li>
253
+ <li><a class="btn ghost" href="/community/find">Find a builder</a></li>
254
+ <li><a class="btn ghost" href="/community/recruiters">Open to recruiters</a></li>
255
+ <li class="knowCell" hidden><a class="btn ghost" href="/connections#connSuggest">Builders you may know</a></li>
256
+ </ul>
257
+ </section>
258
+
194
259
  <!-- THE LEADERBOARD (spec D3): GET /community/leaderboard, one window and one craft
195
260
  at a time. The server ranks; this page draws its rows in the order and with the
196
261
  `place` it was given, and every count it prints is the length of a list. -->
197
- <section id="board" data-view="leaderboard" aria-labelledby="boardH">
262
+ <section id="board" data-view="leaderboard" aria-labelledby="boardH" hidden>
198
263
  <h2 id="boardH">Leaderboard</h2>
199
264
  <p class="commCrownLine" id="boardCrown" hidden></p>
200
265
  <div class="commFilters">
@@ -236,7 +301,7 @@
236
301
  <p class="commMsg" id="weekNone" hidden></p>
237
302
  <h3 class="commSub" id="weekRuleH">How it is chosen</h3>
238
303
  <p class="commNote">The builder who earned the most {{currency}} across every project in one week, Monday to Sunday (UTC), among the builders who shipped at least one work that week. A tie goes to whoever shipped more works, then to whoever earned more karma. Builders who hide their numbers or keep their account private are not counted.</p>
239
- <p class="commNote">Each week is decided once, the first time anyone opens this page after it ends, and it does not change after that. A project reports a builder’s numbers when they sign in or ship there, so a week counts work when it was reported, not when it was done.</p>
304
+ <p class="commNote">Each week is decided once, the first time a community page asks for it after it ends, and it does not change after that. A project reports a builder’s numbers when they sign in or ship there, so a week counts work when it was reported, not when it was done.</p>
240
305
  <h3 class="commSub" id="pastH" hidden>Past weeks</h3>
241
306
  <ol class="commRows weekRows" id="weekPast" aria-labelledby="pastH"></ol>
242
307
  </section>
@@ -304,10 +369,11 @@
304
369
  var QUERY_MIN = 2;
305
370
  var QUERY_MAX = 100;
306
371
  var CRAFT_WORD = { engineer: 'engineer', artist: 'artist', ideator: 'ideator', ui: 'designer' };
307
- // The four sub-pages this file is served as (platform-server.js's /community routes);
308
- // the path picks one, and its menu items are marked current.
372
+ // The five sub-pages this file is served as (platform-server.js's /community routes);
373
+ // the path picks one, and its menu items are marked current. The home adds no title.
309
374
  var VIEWS = {
310
- leaderboard: { path: '/community', section: 'board', title: 'Leaderboard' },
375
+ home: { path: '/community', section: 'home', title: '' },
376
+ leaderboard: { path: '/community/leaderboard', section: 'board', title: 'Leaderboard' },
311
377
  week: { path: '/community/week', section: 'week', title: 'Builder of the week' },
312
378
  find: { path: '/community/find', section: 'find', title: 'Find a builder' },
313
379
  recruiters: { path: '/community/recruiters', section: 'recruit', title: 'Open to recruiters' },
@@ -354,8 +420,8 @@
354
420
  return what + ' could not be read. Reload the page to try again.';
355
421
  }
356
422
  function viewOf(pathname) {
357
- var m = String(pathname || '').replace(/\/+$/, '').match(/^\/community(?:\/(week|find|recruiters))?$/);
358
- return m ? (m[1] || 'leaderboard') : 'leaderboard';
423
+ var m = String(pathname || '').replace(/\/+$/, '').match(/^\/community(?:\/(leaderboard|week|find|recruiters))?$/);
424
+ return m ? (m[1] || 'home') : 'home';
359
425
  }
360
426
 
361
427
  // ── one builder: the avatar, then the name over the @handle ─────────────────
@@ -523,6 +589,11 @@
523
589
  var box = $('weekCrown');
524
590
  box.textContent = '';
525
591
  if (span) box.appendChild(el('p', 'crownWhen', span));
592
+ fillCrown(box, w);
593
+ box.hidden = false;
594
+ }
595
+ // the winner, their credits and works: the week page's crown and each home card
596
+ function fillCrown(box, w) {
526
597
  var who = el('div', 'crownWho');
527
598
  who.appendChild(face(w));
528
599
  var main = el('div', 'mineRow__main');
@@ -535,7 +606,6 @@
535
606
  nums.appendChild(stat(num(w.credits).toLocaleString(), UNIT));
536
607
  nums.appendChild(stat(String(ships), ships === 1 ? 'work shipped' : 'works shipped'));
537
608
  box.appendChild(nums);
538
- box.hidden = false;
539
609
  }
540
610
  function pastRow(p) {
541
611
  var li = el('li', 'commRow');
@@ -570,6 +640,101 @@
570
640
  });
571
641
  }
572
642
 
643
+ // ── the community home: the builders of the week, month and year (task 1004902) ──
644
+ // community-crowns.js decides and records each; this draws what it was sent.
645
+ var LONG_MONTHS = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
646
+ function utcDay(iso) {
647
+ var d = new Date(String(iso) + 'T00:00:00Z');
648
+ return /^\d{4}-\d{2}-\d{2}$/.test(String(iso)) && isFinite(d.getTime()) ? d : null;
649
+ }
650
+ function monthLabel(iso) {
651
+ var d = utcDay(iso);
652
+ return d ? LONG_MONTHS[d.getUTCMonth()] + ' ' + d.getUTCFullYear() : '';
653
+ }
654
+ function dayLabel(iso) {
655
+ var d = utcDay(iso);
656
+ return d ? d.getUTCDate() + ' ' + MONTHS[d.getUTCMonth()] + ' ' + d.getUTCFullYear() : '';
657
+ }
658
+ function homeCard(id, when, w, none) {
659
+ $(id + 'When').textContent = when;
660
+ var box = $(id);
661
+ box.textContent = '';
662
+ if (w) fillCrown(box, w);
663
+ else box.appendChild(el('p', 'commMsg', none));
664
+ }
665
+ function loadHome() {
666
+ return read('/community/crowns').then(function (r) {
667
+ $('homeLoading').hidden = true;
668
+ if (!r.ok) {
669
+ $('homeFailed').textContent = failText(r.status, 'The top builders');
670
+ $('homeFailed').hidden = false;
671
+ return;
672
+ }
673
+ var week = r.data.week || {};
674
+ var month = r.data.month || {};
675
+ var year = r.data.year || {};
676
+ var span = weekSpan(week.start, true);
677
+ homeCard('crownWeek', span, week.winner, span ? 'No builder of the week for ' + span + '.' : 'No builder of the week yet.');
678
+ var mon = monthLabel(month.start);
679
+ homeCard('crownMonth', mon, month.winner, month.first_crown
680
+ ? 'The first builder of the month is named on ' + dayLabel(month.first_crown) + '.'
681
+ : (mon ? 'No builder of the month for ' + mon + '.' : 'No builder of the month yet.'));
682
+ var y = utcDay(year.start);
683
+ var yr = y ? String(y.getUTCFullYear()) : '';
684
+ var since = year.since && year.since !== year.start ? dayLabel(year.since) : '';
685
+ homeCard('crownYear', yr ? yr + ' so far' + (since ? ', since ' + since : '') : '', year.winner, yr ? 'No builder of ' + yr + ' yet.' : 'No builder of the year yet.');
686
+ var last = year.last && year.last.winner;
687
+ var lastYear = year.last && utcDay(year.last.start);
688
+ var line = $('crownYearLast');
689
+ line.textContent = '';
690
+ line.hidden = !(last && lastYear);
691
+ if (last && lastYear) {
692
+ line.appendChild(el('span', null, lastYear.getUTCFullYear() + ': '));
693
+ line.appendChild(el('strong', null, last.display_name || last.handle || last.github_login || 'Builder'));
694
+ line.appendChild(el('span', null, ', ' + num(last.credits).toLocaleString() + ' ' + UNIT));
695
+ }
696
+ $('homeCrowns').hidden = false;
697
+ });
698
+ }
699
+
700
+ // ── the row under the crowns: the rising builder and the projects on the move ──
701
+ // (task 1002983) community-movers.js decides both; a failed read leaves the row down.
702
+ function moveRow(p, i) {
703
+ var li = el('li', 'commRow');
704
+ li.appendChild(el('span', 'commPlace', String(i + 1)));
705
+ var main = el('div', 'mineRow__main');
706
+ var name = String(p.name || 'A project');
707
+ var href = safeHttpUrl(p.origin);
708
+ if (href) {
709
+ var a = el('a', 'mineRow__name', name);
710
+ a.setAttribute('href', href);
711
+ main.appendChild(a);
712
+ } else main.appendChild(el('div', 'mineRow__name', name));
713
+ main.appendChild(el('p', 'mineRow__hint', num(p.credits).toLocaleString() + ' ' + UNIT + ' earned'));
714
+ li.appendChild(main);
715
+ var works = el('div', 'commCredits');
716
+ var n = num(p.tasks_shipped);
717
+ works.appendChild(el('span', 'commNum', String(n)));
718
+ works.appendChild(el('span', 'commUnit', n === 1 ? 'work shipped' : 'works shipped'));
719
+ li.appendChild(works);
720
+ return li;
721
+ }
722
+ function loadMovers() {
723
+ return read('/community/movers').then(function (r) {
724
+ if (!r.ok) return;
725
+ var box = $('moveRising');
726
+ box.textContent = '';
727
+ if (r.data.rising) fillCrown(box, r.data.rising);
728
+ else box.appendChild(el('p', 'commMsg', 'No rising builder in the last 30 days.'));
729
+ var list = $('moveProjects');
730
+ list.textContent = '';
731
+ arr(r.data.projects).forEach(function (p, i) { if (p) list.appendChild(moveRow(p, i)); });
732
+ $('moveNone').hidden = list.children.length > 0;
733
+ $('homeMovers').hidden = false;
734
+ $('moversRule').hidden = false;
735
+ });
736
+ }
737
+
573
738
  // ── the search ──────────────────────────────────────────────────────────────
574
739
  function say(text, kind) {
575
740
  var s = $('findStatus');
@@ -696,10 +861,11 @@
696
861
  Array.prototype.forEach.call(document.querySelectorAll('a[href="' + view.path + '"]'), function (a) {
697
862
  a.setAttribute('aria-current', 'page');
698
863
  });
699
- document.title = document.title + ' · ' + view.title;
864
+ if (view.title) document.title = document.title + ' · ' + view.title;
700
865
  $('signinTop').setAttribute('href', '/api/bongos/auth/web/start?return=' + view.path + '&repos=1');
701
866
  if (VIEW === 'leaderboard') { loadBoard(); read('/community/builder-of-the-week').then(crownLine); }
702
- if (VIEW === 'week') loadWeek();
867
+ if (VIEW === 'home') { loadHome(); loadMovers(); }
868
+ else if (VIEW === 'week') loadWeek();
703
869
  else if (VIEW === 'recruiters') bar.ready.then(function (me) { if (me) loadRecruit(); else showRecruit('recruitDoor'); });
704
870
  else bar.ready.then(function (me) { if (me) loadOwned(me, $(VIEW === 'find' ? 'findFor' : 'boardFor')); });
705
871
  })();
@@ -4,23 +4,39 @@
4
4
  "surface": "public-landing",
5
5
  "probes": [
6
6
  {
7
- "name": "community signed out: the leaderboard for everyone; Community the current section, Leaderboard the current item",
7
+ "name": "community home signed out: the three crowns and a door to each page; Community home the current item",
8
8
  "auth": false,
9
9
  "width": 1440,
10
10
  "steps": [["wait", 400], ["click", "#commTrig"], ["wait", 150]],
11
+ "expect": {
12
+ "open": "#menuCommunity",
13
+ "visible": ["#homeCrowns", "#signinTop"],
14
+ "hidden": ["#board", "#find", "#recruit", ".homeDoors .knowCell"],
15
+ "count": [["#homeCrowns .crownName", 3], [".homeDoors li:not([hidden])", 4], ["#menuCommunity a[aria-current=\"page\"]", 1]],
16
+ "attr": [["#commTrig", "aria-current", "true"]],
17
+ "href": [["#menuCommunity a[aria-current=\"page\"]", "/community"], [".homeDoors li:first-child a", "/community/leaderboard"], ["#signinTop", "/api/bongos/auth/web/start?return=/community&repos=1"]]
18
+ }
19
+ },
20
+ {
21
+ "name": "community leaderboard signed out: the board for everyone; Community the current section, Leaderboard the current item",
22
+ "auth": false,
23
+ "width": 1440,
24
+ "url": "/community/leaderboard",
25
+ "steps": [["wait", 400], ["click", "#commTrig"], ["wait", 150]],
11
26
  "expect": {
12
27
  "open": "#menuCommunity",
13
28
  "visible": ["#boardList", "#signinTop"],
14
- "hidden": ["#find", "#recruit"],
29
+ "hidden": ["#home", "#find", "#recruit"],
15
30
  "count": [["#boardList .commRow", 6], ["#menuCommunity a[aria-current=\"page\"]", 1]],
16
31
  "attr": [["#commTrig", "aria-current", "true"]],
17
- "href": [["#menuCommunity a[aria-current=\"page\"]", "/community"], ["#signinTop", "/api/bongos/auth/web/start?return=/community&repos=1"]]
32
+ "href": [["#menuCommunity a[aria-current=\"page\"]", "/community/leaderboard"], ["#signinTop", "/api/bongos/auth/web/start?return=/community/leaderboard&repos=1"]]
18
33
  }
19
34
  },
20
35
  {
21
36
  "name": "community: the 7-day window from the dropdown re-reads the board",
22
37
  "auth": false,
23
38
  "width": 1440,
39
+ "url": "/community/leaderboard",
24
40
  "steps": [["wait", 400], ["select", "#boardWindow", "7d"], ["wait", 300]],
25
41
  "expect": {
26
42
  "count": [["#boardList .commRow", 2]],
@@ -32,6 +48,7 @@
32
48
  "name": "community: a craft filters the board and renumbers nothing the server did not",
33
49
  "auth": false,
34
50
  "width": 390,
51
+ "url": "/community/leaderboard",
35
52
  "steps": [["wait", 400], ["click", "#craftTabs [data-craft=\"engineer\"]"], ["wait", 300]],
36
53
  "expect": {
37
54
  "count": [["#boardList .commRow", 3]],
@@ -8,11 +8,13 @@
8
8
  "ignoreConsole": ["/api/(gds|bongos)/me", "/scouting", "\\b40[13]\\b", "Unauthorized", "Forbidden"],
9
9
  "ignoreControls": [".vh"],
10
10
  "states": {
11
- "out": { "auth": false, "actions": [["wait", 400]], "expect": { "visible": ["#board", "#boardList", "#boardWindow", "#signinTop", "#projTrig", "#boardCrown"], "hidden": ["#week", "#find", "#recruit", "#who", "#boardLoading", "#boardFailed", "#boardEmpty"], "href": [["#boardCrown a", "/community/week"]], "count": [["#boardList .commRow", 6]], "value": [["#boardWindow", "all"]], "attr": [["#craftTabs [data-craft=\"\"]", "aria-pressed", "true"], ["#commTrig", "aria-current", "true"], ["#menuCommunity a[href=\"/community\"]", "aria-current", "page"]], "text": [["#boardList .commRow:first-child .mineRow__name", "Priya Nair"], ["#boardList .commRow:first-child .commNum", "18,420"], ["#boardList .commRow:first-child .mineRow__hint", "212 works shipped · 64 karma"], ["#boardNote", "Credits earned across every project, since each builder started."], ["#boardCrown", "Builder of the week: Ada Okafor, 1,240 credits, 19–25 Oct"]] } },
12
- "out-30d": { "auth": false, "actions": [["wait", 400], ["select", "#boardWindow", "30d"], ["wait", 300]], "expect": { "count": [["#boardList .commRow", 3]], "value": [["#boardWindow", "30d"]], "text": [["#boardList .commRow:first-child .mineRow__name", "Ada Okafor"], ["#boardNote", "Credits earned in the last 30 days, across every project."]] } },
13
- "out-artists": { "auth": false, "actions": [["wait", 400], ["click", "#craftTabs [data-craft=\"artist\"]"], ["wait", 300]], "expect": { "count": [["#boardList .commRow", 1]], "text": [["#boardList .commRow:first-child .mineRow__name", "Ada Okafor"], ["#boardList .commRow:first-child .commPlace", "1"]] } },
14
- "out-community-band": { "auth": false, "actions": [["wait", 400], ["clickIfVisible", "#commTrig"], ["wait", 300]], "expect": { "hidden": ["#menuCommunity .knowCell"], "count": [["#menuCommunity .bandCell:not([hidden])", 4]], "attr": [["#menuCommunity a[href=\"/community\"]", "aria-current", "page"]] } },
15
- "in-community-band": { "auth": true, "actions": [["wait", 400], ["clickIfVisible", "#commTrig"], ["wait", 300]], "expect": { "count": [["#menuCommunity .bandCell:not([hidden])", 5]], "href": [["#menuCommunity .knowCell a", "/connections#connSuggest"]] } },
11
+ "out": { "auth": false, "actions": [["wait", 400]], "expect": { "visible": ["#home", "#homeCrowns", "#crownWeek .crownName", "#crownMonth .crownName", "#crownYear .crownName", "#homeRule", "#signinTop", "#homeMovers", "#moveRising .crownName", "#moversRule"], "hidden": ["#board", "#week", "#find", "#recruit", "#who", "#homeLoading", "#homeFailed", "#crownYearLast", ".homeDoors .knowCell", "#moveNone"], "count": [[".homeDoors li:not([hidden])", 4], ["#moveProjects .commRow", 5]], "href": [[".homeDoors li:first-child a", "/community/leaderboard"], ["#moveProjects .commRow:first-child a", "https://tidepool.example/"]], "attr": [["#commTrig", "aria-current", "true"], ["#menuCommunity a[href=\"/community\"]", "aria-current", "page"]], "text": [["#crownWeekWhen", "26 Oct – 1 Nov 2026"], ["#crownWeek .crownName", "Ada Okafor"], ["#crownMonthWhen", "October 2026"], ["#crownMonth .crownName", "Priya Nair"], ["#crownMonth .crownNums div:first-child .commNum", "6,120"], ["#crownYearWhen", "2026 so far, since 27 Sep 2026"], ["#crownYear .crownName", "Maximiliana van der Berghe-Lindqvist"], ["#moveRising .crownName", "Kofi Mensah"], ["#moveProjects .commRow:first-child .commNum", "23"], ["#moveProjects .commRow:first-child .mineRow__hint", "1,840 credits earned"]] } },
12
+ "in": { "auth": true, "actions": [["wait", 600]], "expect": { "visible": ["#home", "#homeCrowns", "#who", ".homeDoors .knowCell"], "hidden": ["#board", "#signinTop", "#homeFailed"], "count": [[".homeDoors li:not([hidden])", 5]], "href": [[".homeDoors .knowCell a", "/connections#connSuggest"]] } },
13
+ "out-board": { "auth": false, "url": "/community/leaderboard", "actions": [["wait", 400]], "expect": { "visible": ["#board", "#boardList", "#boardWindow", "#signinTop", "#projTrig", "#boardCrown"], "hidden": ["#home", "#week", "#find", "#recruit", "#who", "#boardLoading", "#boardFailed", "#boardEmpty"], "href": [["#boardCrown a", "/community/week"]], "count": [["#boardList .commRow", 6]], "value": [["#boardWindow", "all"]], "attr": [["#craftTabs [data-craft=\"\"]", "aria-pressed", "true"], ["#commTrig", "aria-current", "true"], ["#menuCommunity a[href=\"/community/leaderboard\"]", "aria-current", "page"]], "text": [["#boardList .commRow:first-child .mineRow__name", "Priya Nair"], ["#boardList .commRow:first-child .commNum", "18,420"], ["#boardList .commRow:first-child .mineRow__hint", "212 works shipped · 64 karma"], ["#boardNote", "Credits earned across every project, since each builder started."], ["#boardCrown", "Builder of the week: Ada Okafor, 1,240 credits, 19–25 Oct"]] } },
14
+ "out-30d": { "auth": false, "url": "/community/leaderboard", "actions": [["wait", 400], ["select", "#boardWindow", "30d"], ["wait", 300]], "expect": { "count": [["#boardList .commRow", 3]], "value": [["#boardWindow", "30d"]], "text": [["#boardList .commRow:first-child .mineRow__name", "Ada Okafor"], ["#boardNote", "Credits earned in the last 30 days, across every project."]] } },
15
+ "out-artists": { "auth": false, "url": "/community/leaderboard", "actions": [["wait", 400], ["click", "#craftTabs [data-craft=\"artist\"]"], ["wait", 300]], "expect": { "count": [["#boardList .commRow", 1]], "text": [["#boardList .commRow:first-child .mineRow__name", "Ada Okafor"], ["#boardList .commRow:first-child .commPlace", "1"]] } },
16
+ "out-community-band": { "auth": false, "actions": [["wait", 400], ["clickIfVisible", "#commTrig"], ["wait", 300]], "expect": { "hidden": ["#menuCommunity .knowCell"], "count": [["#menuCommunity .bandCell:not([hidden])", 5]], "attr": [["#menuCommunity a[href=\"/community\"]", "aria-current", "page"]] } },
17
+ "in-community-band": { "auth": true, "actions": [["wait", 400], ["clickIfVisible", "#commTrig"], ["wait", 300]], "expect": { "count": [["#menuCommunity .bandCell:not([hidden])", 6]], "href": [["#menuCommunity .knowCell a", "/connections#connSuggest"]] } },
16
18
  "in-projects-band": { "auth": true, "actions": [["wait", 400], ["click", "#projTrig"], ["wait", 300]], "expect": { "visible": ["#menuProjects"], "count": [["#menuProjects .knowCell:not([hidden])", 1]] } },
17
19
  "out-projects-band": { "auth": false, "actions": [["wait", 400], ["click", "#projTrig"], ["wait", 300]], "expect": { "visible": ["#menuProjects"], "attr": [["#menuProjects a[href=\"/community\"]", "aria-current", "page"]] } },
18
20
  "out-week": { "auth": false, "url": "/community/week", "actions": [["wait", 400]], "expect": { "visible": ["#week", "#weekCrown", "#pastH", "#weekPast"], "hidden": ["#board", "#find", "#recruit", "#weekLoading", "#weekFailed", "#weekNone"], "count": [["#weekPast .commRow", 4]], "attr": [["#menuCommunity a[href=\"/community/week\"]", "aria-current", "page"]], "text": [["#weekCrown .crownWhen", "19–25 Oct 2026"], ["#weekCrown .crownName", "Ada Okafor"], ["#weekCrown .crownNums div:first-child .commNum", "1,240"], ["#weekCrown .crownNums div:last-child .commUnit", "works shipped"], ["#weekPast .commRow:first-child .commPlace", "12–18 Oct"], ["#weekPast .commRow:nth-child(3) .commPlace", "28 Sep – 4 Oct"]] } },
@@ -21,7 +23,7 @@
21
23
  "out-find-search": { "auth": false, "url": "/community/find", "actions": [["wait", 400], ["fill", "#findQ", "pri"], ["click", "#findBtn"], ["wait", 300]], "expect": { "count": [["#findList .mineRow", 2]], "text": [["#findStatus", "2 builders found."], ["#findList .mineRow:first-child .mineRow__hint", "engineer · designer"]] } },
22
24
  "out-recruiters": { "auth": false, "url": "/community/recruiters", "actions": [["wait", 400]], "expect": { "visible": ["#recruit", "#recruitDoor", "#recruitSignin"], "hidden": ["#board", "#find", "#recruitLoading", "#recruitClosed"], "count": [["#recruitList .mineRow", 0]], "attr": [["#menuCommunity a[href=\"/community/recruiters\"]", "aria-current", "page"]] } },
23
25
  "in-recruiters": { "auth": true, "url": "/community/recruiters", "actions": [["wait", 600]], "expect": { "visible": ["#recruit", "#recruitClosed", "#who"], "hidden": ["#recruitDoor", "#recruitLoading", "#recruitFailed"], "count": [["#recruitList .mineRow", 0]] } },
24
- "in": { "auth": true, "actions": [["wait", 600]], "expect": { "visible": ["#board", "#who"], "hidden": ["#find", "#recruit", "#signinTop", "#boardFailed"], "count": [["#boardList .commRow", 6]] } }
26
+ "in-board": { "auth": true, "url": "/community/leaderboard", "actions": [["wait", 600]], "expect": { "visible": ["#board", "#who"], "hidden": ["#find", "#recruit", "#signinTop", "#boardFailed"], "count": [["#boardList .commRow", 6]] } }
25
27
  },
26
28
  "reducedMotion": { "auth": false, "actions": [["wait", 400]] }
27
29
  }
@@ -90,7 +90,8 @@
90
90
  <div class="menuBand" id="menuCommunity" data-menu="community" hidden>
91
91
  <div class="bandIn">
92
92
  <ul class="bandRow">
93
- <li class="bandCell"><a class="bandT" href="/community">Leaderboard</a><span class="bandC">who earned the most, across every project</span></li>
93
+ <li class="bandCell"><a class="bandT" href="/community">Community home</a><span class="bandC">the builders of the week, month and year</span></li>
94
+ <li class="bandCell"><a class="bandT" href="/community/leaderboard">Leaderboard</a><span class="bandC">who earned the most, across every project</span></li>
94
95
  <li class="bandCell"><a class="bandT" href="/community/week">Builder of the week</a><span class="bandC">the top earner of each week, and how it is chosen</span></li>
95
96
  <li class="bandCell"><a class="bandT" href="/community/find">Find a builder</a><span class="bandC">anyone building here, by name or role</span></li>
96
97
  <li class="bandCell"><a class="bandT" href="/community/recruiters">Open to recruiters</a><span class="bandC">who is open to recruiting, for project owners</span></li>
@@ -105,7 +106,7 @@
105
106
  <li class="bandCell"><a class="bandT" href="/projects?view=map&amp;sub=table">Table</a><span class="bandC">the same projects as rows</span></li>
106
107
  <li class="bandCell" id="mineCell"><a class="bandT" href="/projects?view=mine">Your projects</a><span class="bandC">the ones you own</span></li>
107
108
  <li class="bandCell"><a class="bandT" href="/projects?view=new">New project</a><span class="bandC">five questions and a home</span></li>
108
- <li class="bandCell bandFold"><a class="bandT" href="/community">Leaderboard</a><span class="bandC">who earned the most, across every project</span></li><li class="bandCell bandFold"><a class="bandT" href="/community/week">Builder of the week</a><span class="bandC">the top earner of each week, and how it is chosen</span></li><li class="bandCell bandFold"><a class="bandT" href="/community/find">Find a builder</a><span class="bandC">anyone building here, by name or role</span></li><li class="bandCell bandFold"><a class="bandT" href="/community/recruiters">Open to recruiters</a><span class="bandC">who is open to recruiting, for project owners</span></li><li class="bandCell bandFold knowCell" hidden><a class="bandT" href="/connections#connSuggest">Builders you may know</a><span class="bandC">connections or projects in common</span></li>
109
+ <li class="bandCell bandFold"><a class="bandT" href="/community">Community home</a><span class="bandC">the builders of the week, month and year</span></li><li class="bandCell bandFold"><a class="bandT" href="/community/leaderboard">Leaderboard</a><span class="bandC">who earned the most, across every project</span></li><li class="bandCell bandFold"><a class="bandT" href="/community/week">Builder of the week</a><span class="bandC">the top earner of each week, and how it is chosen</span></li><li class="bandCell bandFold"><a class="bandT" href="/community/find">Find a builder</a><span class="bandC">anyone building here, by name or role</span></li><li class="bandCell bandFold"><a class="bandT" href="/community/recruiters">Open to recruiters</a><span class="bandC">who is open to recruiting, for project owners</span></li><li class="bandCell bandFold knowCell" hidden><a class="bandT" href="/connections#connSuggest">Builders you may know</a><span class="bandC">connections or projects in common</span></li>
109
110
  </ul>
110
111
  </div>
111
112
  </div>
@@ -70,7 +70,8 @@
70
70
  <!--apex-only:start--><div class="menuBand" id="menuCommunity" data-menu="community" hidden>
71
71
  <div class="bandIn">
72
72
  <ul class="bandRow">
73
- <li class="bandCell"><a class="bandT" href="/community">Leaderboard</a><span class="bandC">who earned the most, across every project</span></li>
73
+ <li class="bandCell"><a class="bandT" href="/community">Community home</a><span class="bandC">the builders of the week, month and year</span></li>
74
+ <li class="bandCell"><a class="bandT" href="/community/leaderboard">Leaderboard</a><span class="bandC">who earned the most, across every project</span></li>
74
75
  <li class="bandCell"><a class="bandT" href="/community/week">Builder of the week</a><span class="bandC">the top earner of each week, and how it is chosen</span></li>
75
76
  <li class="bandCell"><a class="bandT" href="/community/find">Find a builder</a><span class="bandC">anyone building here, by name or role</span></li>
76
77
  <li class="bandCell"><a class="bandT" href="/community/recruiters">Open to recruiters</a><span class="bandC">who is open to recruiting, for project owners</span></li>
@@ -85,7 +86,7 @@
85
86
  <li class="bandCell"><a class="bandT" href="/projects?view=map&amp;sub=table">Table</a><span class="bandC">the same projects as rows</span></li>
86
87
  <li class="bandCell" id="mineCell"><a class="bandT" href="/projects?view=mine">Your projects</a><span class="bandC">the ones you own</span></li>
87
88
  <li class="bandCell"><a class="bandT" href="/projects?view=new">New project</a><span class="bandC">five questions and a home</span></li>
88
- <!--apex-only:start--><li class="bandCell bandFold"><a class="bandT" href="/community">Leaderboard</a><span class="bandC">who earned the most, across every project</span></li><li class="bandCell bandFold"><a class="bandT" href="/community/week">Builder of the week</a><span class="bandC">the top earner of each week, and how it is chosen</span></li><li class="bandCell bandFold"><a class="bandT" href="/community/find">Find a builder</a><span class="bandC">anyone building here, by name or role</span></li><li class="bandCell bandFold"><a class="bandT" href="/community/recruiters">Open to recruiters</a><span class="bandC">who is open to recruiting, for project owners</span></li><li class="bandCell bandFold knowCell" hidden><a class="bandT" href="/connections#connSuggest">Builders you may know</a><span class="bandC">connections or projects in common</span></li><!--apex-only:end-->
89
+ <!--apex-only:start--><li class="bandCell bandFold"><a class="bandT" href="/community">Community home</a><span class="bandC">the builders of the week, month and year</span></li><li class="bandCell bandFold"><a class="bandT" href="/community/leaderboard">Leaderboard</a><span class="bandC">who earned the most, across every project</span></li><li class="bandCell bandFold"><a class="bandT" href="/community/week">Builder of the week</a><span class="bandC">the top earner of each week, and how it is chosen</span></li><li class="bandCell bandFold"><a class="bandT" href="/community/find">Find a builder</a><span class="bandC">anyone building here, by name or role</span></li><li class="bandCell bandFold"><a class="bandT" href="/community/recruiters">Open to recruiters</a><span class="bandC">who is open to recruiting, for project owners</span></li><li class="bandCell bandFold knowCell" hidden><a class="bandT" href="/connections#connSuggest">Builders you may know</a><span class="bandC">connections or projects in common</span></li><!--apex-only:end-->
89
90
  </ul>
90
91
  </div>
91
92
  </div>