@bongos/core 1.21.4 → 1.21.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.bongos-core.json +118 -63
- package/clients/bongos-client/README.md +1 -1
- package/clients/bongos-client/bongos-client.global.js +2 -0
- package/clients/bongos-client/index.cjs +2 -0
- package/clients/bongos-client/index.d.ts +2 -0
- package/clients/bongos-client/index.mjs +2 -0
- package/docs/adr/0362-the-governor-docket-is-collected-from-contributed-sources.md +34 -0
- package/docs/adr/README.md +1 -0
- package/docs/api/openapi.json +35 -1
- package/docs/api-reference.md +3 -2
- package/docs/copy-inventory.md +389 -518
- package/docs/copy-registry.json +880 -2143
- package/docs/module-api-changelog.md +2 -0
- package/docs/page-inventory.json +17 -25
- package/docs/page-readings.json +1008 -1246
- package/modules/autonomy/governor-docket.js +101 -0
- package/modules/autonomy/routes/autonomy.js +5 -0
- package/modules/discord/board-broadcast.js +3 -1
- package/modules/discord/craft-broadcast.js +107 -0
- package/modules/discord/routes/discord.js +10 -0
- package/modules/discord/ship-broadcast.js +30 -2
- package/modules/economy/reward.js +3 -0
- package/modules/government/board.js +20 -5
- package/modules/government/docket-vocab.js +82 -0
- package/modules/government/docket.js +458 -0
- package/modules/government/routes/government.js +34 -0
- package/modules/hall-ui/public/genesis-home.js +9 -10
- package/modules/ideas/governor-docket.js +93 -0
- package/modules/ideas/routes/blockers.js +5 -0
- package/modules/lifecycle/governor-docket.js +192 -0
- package/modules/lifecycle/routes/gate-approvals.js +78 -33
- package/modules/lifecycle/routes/lifecycle.js +3 -0
- package/modules/onboarding/routes/access-requests.js +68 -27
- package/modules/public-landing/public/assets/cosmos.css +6 -50
- package/modules/public-landing/public/projects.html +1170 -1659
- package/modules/public-landing/public/projects.probes.json +18 -18
- package/modules/public-landing/public/projects.states.json +23 -31
- package/modules/security/routes/reports.js +39 -0
- package/package-lock.json +2 -2
- package/package.json +1 -1
- package/release-notes.json +14 -0
- package/src/bongos/module-rechecks.js +77 -0
- package/src/bongos/route-rank-check.js +3 -0
- package/src/bongos/routes/core-update.js +41 -0
- package/src/bongos/routes/modules.js +17 -0
- package/src/module-api.js +1 -1
- package/tests/discord_craft_broadcast.mjs +162 -0
- package/tests/genesis_home.mjs +1 -1
- package/tests/government_routes.mjs +3 -1
- package/tests/governor_docket.mjs +552 -0
- package/tests/onboard.mjs +10 -4
- package/tests/platform_boot.mjs +1 -1
- package/tests/projects_hub.mjs +196 -89
- package/tests/projects_hub_app_status.mjs +3 -1
- package/tests/projects_hub_app_step.mjs +72 -48
- package/tests/projects_hub_dns_ready.mjs +10 -2
- package/tests/projects_hub_look.mjs +32 -22
- package/tests/projects_hub_module_picker.mjs +112 -62
- package/tests/projects_hub_pre_uat.mjs +48 -41
- package/tests/public_landing_projects.mjs +12 -6
- package/tests/wizard_demo.mjs +37 -11
- package/tests/wizard_draft_resume.mjs +34 -22
- package/tests/wizard_front_door.mjs +173 -103
- package/tests/wizard_intent_resume.mjs +28 -25
- package/tests/wizard_physics.mjs +94 -76
- package/tests/wizard_physics_more.mjs +409 -277
- package/tests/wizard_six_screens.mjs +126 -0
|
@@ -71,31 +71,19 @@
|
|
|
71
71
|
#view-manage .pubVerdict{font-size:15px;font-weight:500;line-height:1.55;color:var(--ink-soft);margin-bottom:14px;}
|
|
72
72
|
#view-manage .pubVerdict b{font-weight:700;color:var(--ink);}
|
|
73
73
|
|
|
74
|
-
/* The bongo pair on the New-project view. Component-scoped custom properties,
|
|
75
|
-
never :root (C1). The drum head is SKIN — using --bg-card would turn it black
|
|
76
|
-
in dark mode — and --drum-shadow is ink in light but PLAIN BLACK in dark,
|
|
77
|
-
because --ink is near-white there and an ink-filled ellipse paints a glowing
|
|
78
|
-
puddle under the drums instead of a shadow. Same move cosmos.css already
|
|
79
|
-
makes for --shadow-card. Written twice so an explicit choice and the OS
|
|
80
|
-
preference both land. */
|
|
81
|
-
.bongos{display:block;width:100%;height:auto;overflow:visible;
|
|
82
|
-
--skin-hi:#fffdf7;--skin-mid:#f4e6cd;--skin-lo:#cdb896;--drum-shadow:var(--ink);}
|
|
83
|
-
:root[data-mode="dark"] .bongos{--skin-hi:#fdf7ea;--skin-mid:#efe1c8;--skin-lo:#c3ae90;--drum-shadow:#000;}
|
|
84
|
-
@media (prefers-color-scheme:dark){:root:not([data-mode="light"]) .bongos{--skin-hi:#fdf7ea;--skin-mid:#efe1c8;--skin-lo:#c3ae90;--drum-shadow:#000;}}
|
|
85
|
-
|
|
86
74
|
/* ── the create wizard (task 1002763, absorbed from /create), in the world
|
|
87
75
|
(task 1003346; direction record §15, the pick: v1 the rail above) ──────
|
|
88
|
-
Tokens, the reset, the sky and the shared vocabulary (.card, .btn
|
|
76
|
+
Tokens, the reset, the sky and the shared vocabulary (.card, .btn)
|
|
89
77
|
come from cosmos.css, which draws the wizard's FRAME in the world's grammar
|
|
90
|
-
by scope (#view-new: no card ground,
|
|
91
|
-
|
|
78
|
+
by scope (#view-new: no card ground, no progress rail since task 1004504,
|
|
79
|
+
the doors as pills); what lands here is the wizard’s own structure —
|
|
92
80
|
fields on one bottom hairline, the choices as cells of one band, the review
|
|
93
81
|
as a ledger, the human steps as a numbered ledger, the standup strip as the
|
|
94
|
-
same
|
|
82
|
+
same strip. No literal color anywhere: every surface is a cosmos token or a
|
|
95
83
|
color-mix over one (C1/C2/C4); no plate, no box shadow, no glow — halos are
|
|
96
84
|
outlines. Naming: the wizard's field wrapper is .wizField — cosmos owns
|
|
97
|
-
.field (the orb map) — and its done checklist is .humanSteps
|
|
98
|
-
|
|
85
|
+
.field (the orb map) — and its done checklist is .humanSteps. Its inputs are
|
|
86
|
+
scoped to the view so the glass
|
|
99
87
|
search pills (#mapQ, #mineQ) keep their own look. */
|
|
100
88
|
.panel{display:none;}
|
|
101
89
|
.panel.on{display:block;animation:fadeStep .3s ease both;}
|
|
@@ -103,80 +91,33 @@
|
|
|
103
91
|
/* show() moves focus to each panel's h1 for keyboard/SR context — keep the
|
|
104
92
|
focus, lose the ring (a black rectangle around a heading; owner walk) */
|
|
105
93
|
h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
106
|
-
/* the done step needs the full width — the split collapses and the bongo
|
|
107
|
-
pair steps aside for the checklist */
|
|
108
|
-
.createSplit.wizDone{display:block;}
|
|
109
|
-
.createSplit.wizDone .bongoCell{display:none;}
|
|
110
|
-
/* the front door (task 1004417, BV2.PS05): two screens before the numbered
|
|
111
|
-
steps. The rail is the steps', so it steps aside for both; the fork takes the
|
|
112
|
-
whole card like the done panel, the bongo pair giving way to the seed. */
|
|
113
|
-
#view-new .card.wizFront .steps{display:none;}
|
|
114
|
-
.createSplit.wizFork{display:block;}
|
|
115
|
-
.createSplit.wizFork .bongoCell{display:none;}
|
|
116
94
|
/* the one tick: a hairline row, the box in the accent, the whole row the target */
|
|
117
95
|
.termsAck{display:flex;gap:14px;align-items:flex-start;cursor:pointer;max-width:62ch;margin:6px 0 0;padding:16px 0 10px;border-top:1px solid var(--rule);}
|
|
118
96
|
.termsAck input{width:20px;height:20px;margin:2px 0 0;flex:none;accent-color:var(--accent);}
|
|
119
97
|
.termsAck span{font-size:16px;line-height:1.5;color:var(--ink);}
|
|
120
|
-
.termsRead{display:inline-block;
|
|
98
|
+
.termsRead{display:inline-block;min-height:24px;line-height:24px;color:var(--ink);text-decoration:underline;text-underline-offset:3px;}
|
|
121
99
|
/* the door pill (DESIGN.md): two equal doors joined into one object — one grid
|
|
122
100
|
of two equal tracks inside one hairline pill; the demo door is transparent at
|
|
123
101
|
rest, the accent only on the one that goes on */
|
|
124
102
|
.doorPill{display:inline-grid;grid-template-columns:1fr 1fr;gap:6px;padding:7px;margin:6px 0 16px;border-radius:999px;border:1px solid var(--rule);background:color-mix(in srgb,var(--bg) 55%,transparent);}
|
|
125
103
|
#view-new .doorPill .btn.ghost{border-color:transparent;}
|
|
126
104
|
#view-new .doorPill .btn.ghost:hover{border-color:var(--rule);}
|
|
127
|
-
/* the demo route (task 1004420):
|
|
128
|
-
field grammar, the fit check as
|
|
129
|
-
dashed ring on the planet for the time box (the canvas's "when it closes, the demo ends") */
|
|
105
|
+
/* the demo route (task 1004420): one screen inside the fork — steppers on the wizard's
|
|
106
|
+
field grammar, the fit check as one quiet line, two teammates */
|
|
130
107
|
.demoSteppers{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 24px;}
|
|
131
108
|
.demoStep{display:flex;align-items:center;gap:12px;margin-top:6px;}
|
|
132
109
|
.demoStep output{min-width:2ch;text-align:center;font-family:var(--font-display);font-weight:300;font-size:28px;color:var(--ink);}
|
|
133
110
|
.demoStep .btn{min-width:40px;min-height:40px;justify-content:center;}
|
|
134
|
-
.
|
|
135
|
-
.demoFitLine{font-size:15.5px;line-height:1.5;color:var(--ink);margin:8px 0 12px;}
|
|
136
|
-
.demoFitTry{font-size:14px;color:var(--ink-soft);margin:0 0 4px;}
|
|
137
|
-
.demoTry{margin:0 0 6px;padding-left:18px;font-size:14.5px;line-height:1.6;color:var(--ink);}
|
|
138
|
-
.demoLedger{display:grid;grid-template-columns:150px minmax(0,1fr);gap:0;margin:6px 0 18px;border-top:1px solid var(--rule);}
|
|
139
|
-
.demoLedger dt,.demoLedger dd{margin:0;padding:11px 0;border-bottom:1px solid var(--rule-soft);}
|
|
140
|
-
.demoLedger dt{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);text-transform:uppercase;color:var(--ink-faint);padding-top:14px;}
|
|
141
|
-
.demoLedger dd{font-size:15.5px;color:var(--ink);}
|
|
142
|
-
.demoLedger dd small{display:block;font-size:13.5px;color:var(--ink-soft);margin-top:2px;}
|
|
111
|
+
.demoFitLine{font-size:14.5px;line-height:1.5;color:var(--ink-soft);margin:0 0 14px;max-width:62ch;}
|
|
143
112
|
.demoInvites{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 24px;}
|
|
144
|
-
.demoRing{position:absolute;inset:4%;border:1px dashed color-mix(in srgb,var(--accent) 55%,transparent);border-radius:50%;}
|
|
145
113
|
.demoKeep{border-top:1px solid var(--rule);padding-top:14px;margin:6px 0 10px;}
|
|
146
114
|
.demoKeepH{font-family:var(--font-display);font-weight:300;font-size:24px;letter-spacing:-.02em;text-transform:lowercase;color:var(--ink);margin:0 0 10px;}
|
|
147
115
|
.demoKeepCells{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 24px;}
|
|
148
116
|
.demoClock{font-size:15.5px;color:var(--ink);margin:0 0 10px;}
|
|
149
117
|
.demoSoonWarn{color:var(--warn);font-weight:600;}
|
|
150
|
-
.
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
dashed one, the chrome blob cut out on top — nothing drawn but hairlines */
|
|
154
|
-
.forkBody{display:grid;grid-template-columns:minmax(0,1fr) minmax(240px,380px);gap:48px;align-items:center;}
|
|
155
|
-
.forkStage{position:relative;aspect-ratio:1/1;border-radius:16px;overflow:hidden;background:var(--sky-bg) var(--field-plate) center/cover no-repeat;}
|
|
156
|
-
.forkOrbit{position:absolute;inset:20%;border:1px solid var(--rule);border-radius:50%;}
|
|
157
|
-
.forkOrbit.forkOrbitDash{inset:9%;border-style:dashed;border-color:var(--rule-soft);}
|
|
158
|
-
.forkSeed{position:absolute;left:50%;top:50%;width:44%;height:auto;transform:translate(-50%,-50%);}
|
|
159
|
-
/* the two routes on one hairline band */
|
|
160
|
-
.forkRoutes{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 48px;margin-top:40px;}
|
|
161
|
-
.forkRoutes>div{border-top:1px solid var(--rule);padding-top:18px;}
|
|
162
|
-
.routeLbl{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);text-transform:uppercase;color:var(--ink-faint);margin:0;}
|
|
163
|
-
.routeT{font-family:var(--font-display);font-weight:300;font-size:26px;letter-spacing:-.02em;line-height:1.1;text-transform:lowercase;color:var(--ink);margin:8px 0 0;}
|
|
164
|
-
.routeD{font-size:14.5px;line-height:1.5;color:var(--ink-soft);margin:8px 0 0;max-width:52ch;}
|
|
165
|
-
@media (max-width:840px){
|
|
166
|
-
.forkBody,.forkRoutes{grid-template-columns:1fr;gap:28px;}
|
|
167
|
-
.forkStage{max-width:300px;}
|
|
168
|
-
.forkRoutes{margin-top:32px;}
|
|
169
|
-
}
|
|
170
|
-
/* planet physics (task 1004418, BV2.PS07): the fork's layout, its stage forming a
|
|
171
|
-
planet as the answers come in. The canvas's seven-question rail is a quiet
|
|
172
|
-
label-face row inside the panel; the wizard's own rail stays above it. Choices
|
|
173
|
-
are the wizard's hairline pills; the planet grows by a step size per answer. */
|
|
174
|
-
.physCrumb{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);text-transform:uppercase;color:var(--ink-faint);margin:0 0 10px;}
|
|
175
|
-
.physRail{list-style:none;display:flex;flex-wrap:wrap;gap:4px 16px;margin:0 0 22px;padding:0;font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);text-transform:uppercase;color:var(--ink-faint);}
|
|
176
|
-
.physRail li.now{color:var(--ink);}
|
|
177
|
-
.physRail li.done{color:var(--ink-soft);}
|
|
178
|
-
.physRail li.now::before{content:"";display:inline-block;width:6px;height:6px;margin-right:6px;border-radius:50%;background:var(--accent);vertical-align:2px;}
|
|
179
|
-
.physQ h1 em{font-style:italic;}
|
|
118
|
+
@media (max-width:640px){ .demoSteppers,.demoInvites,.demoKeepCells{grid-template-columns:1fr;} }
|
|
119
|
+
/* planet physics (task 1004418, BV2.PS07; the quick checks and the review rows since
|
|
120
|
+
task 1004504): the wizard's hairline pills, the pickers, the age tiers and the suite. */
|
|
180
121
|
.physCells .tpill small{display:block;font-size:13.5px;font-weight:400;color:var(--ink-soft);line-height:1.4;}
|
|
181
122
|
.physCells .tpill[disabled]{opacity:.45;cursor:default;}
|
|
182
123
|
.physWhy{font-size:14px;line-height:1.5;color:var(--ink-soft);max-width:60ch;margin:0 0 14px;}
|
|
@@ -199,26 +140,9 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
199
140
|
.physTiers dd{margin:0;color:var(--ink);}
|
|
200
141
|
.physGrp{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);text-transform:uppercase;color:var(--ink-faint);margin:12px 0 4px;}
|
|
201
142
|
.physLink{background:none;border:0;padding:3px 0;min-height:24px;font:inherit;font-size:14px;color:var(--ink);text-decoration:underline;text-underline-offset:3px;cursor:pointer;}
|
|
202
|
-
.physStage .physPlanet{transition:width .6s ease;}
|
|
203
|
-
.physStage .physPlanet[data-size="1"]{width:22%;}
|
|
204
|
-
.physStage .physPlanet[data-size="2"]{width:28%;}
|
|
205
|
-
.physStage .physPlanet[data-size="3"]{width:34%;}
|
|
206
|
-
.physStage .physPlanet[data-size="4"]{width:40%;}
|
|
207
|
-
.physStage .physPlanet[data-size="5"]{width:46%;}
|
|
208
|
-
.physStage .physPlanet[data-size="6"]{width:50%;}
|
|
209
|
-
.physGuard{position:absolute;inset:14%;border:1px solid color-mix(in srgb,var(--accent) 55%,transparent);border-radius:50%;outline:1px dashed color-mix(in srgb,var(--accent) 30%,transparent);outline-offset:6px;}
|
|
210
|
-
.physStage.physStopped{opacity:.6;}
|
|
211
|
-
.physCap,.physFoot{position:absolute;left:16px;right:16px;margin:0;}
|
|
212
|
-
.physFoot{bottom:14px;display:grid;gap:6px;}
|
|
213
|
-
.physCap,.physCallout,.physCoords{margin:0;font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);text-transform:uppercase;color:var(--ink-soft);}
|
|
214
|
-
.physCap{top:14px;}
|
|
215
|
-
.physCallout{color:var(--ink);text-transform:none;letter-spacing:0;font-family:var(--font-body);font-size:13px;}
|
|
216
|
-
.physCoords{color:var(--ink-faint);}
|
|
217
143
|
@media (max-width:840px){ .physRow2{grid-template-columns:1fr;} }
|
|
218
|
-
/* questions 5 to 7
|
|
219
|
-
|
|
220
|
-
two-column grid; the growth guess is a ledger of three months over a range. */
|
|
221
|
-
.physRail li.skipped,#view-new .steps li.skipped .slbl{text-decoration:line-through;text-decoration-color:var(--ink-faint);}
|
|
144
|
+
/* questions 5 to 7 (task 1004419, BV2.PS11): the suite a two-column grid, the growth
|
|
145
|
+
guess one line over a range. */
|
|
222
146
|
.typeCards.physOpts{grid-template-columns:1fr;}
|
|
223
147
|
.physOpts .tpill,.physSuiteGrid .tpill{align-items:flex-start;border-left:0;padding-left:0;border-top:1px solid var(--rule-soft);}
|
|
224
148
|
.physOpts .tpill:first-child{border-top:0;}
|
|
@@ -228,46 +152,144 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
228
152
|
.typeCards.physSuiteGrid{grid-template-columns:repeat(2,minmax(0,1fr));gap:0 16px;}
|
|
229
153
|
.physSuiteGrid .tpill:nth-child(-n+2){border-top:0;}
|
|
230
154
|
.physWhyList{margin:0 0 14px;padding:0 0 0 18px;font-size:14px;line-height:1.5;color:var(--ink-soft);max-width:62ch;}
|
|
231
|
-
.physGuess{margin:12px 0 10px;padding:14px 0 0;border-top:1px solid var(--rule);max-width:62ch;}
|
|
232
|
-
.physGuessLine{font-size:15px;line-height:1.5;color:var(--ink);margin:0 0 12px;}
|
|
233
|
-
.physMonths{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));grid-template-rows:auto auto;grid-auto-flow:column;gap:2px 16px;margin:0 0 6px;padding-top:10px;border-top:1px solid var(--rule-soft);}
|
|
234
|
-
.physMonths dt{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);text-transform:uppercase;color:var(--ink-faint);}
|
|
235
|
-
.physMonths dd{margin:0;font-family:var(--font-display);font-weight:300;font-size:30px;line-height:1.1;color:var(--ink);font-variant-numeric:tabular-nums;}
|
|
236
155
|
.physAdjust input[type=range]{display:block;width:100%;min-height:24px;margin:6px 0 0;accent-color:var(--accent);background:none;}
|
|
237
|
-
.physLedger dd{display:flex;align-items:flex-start;gap:12px;}
|
|
238
|
-
.physLedger dd>span{flex:1;min-width:0;}
|
|
239
|
-
.physLedger .physLink{flex:none;}
|
|
240
156
|
@media (max-width:520px){ .typeCards.physSuiteGrid{grid-template-columns:1fr;} .physSuiteGrid .tpill:nth-child(2){border-top:1px solid var(--rule-soft);} }
|
|
241
|
-
/* the
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
.
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
.
|
|
248
|
-
.
|
|
249
|
-
.
|
|
250
|
-
|
|
251
|
-
.
|
|
252
|
-
.
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
157
|
+
/* ── the night room (task 1004504; the design of record docs/design/mocks/combined-onboarding/)
|
|
158
|
+
The studio's lofi room (modules/hall-ui/public/studio.css) behind the whole wizard: a
|
|
159
|
+
window with its moon, stars and city lights, the floor, the lamp and its glow, a mug and
|
|
160
|
+
a plant, and a soft grain. Its colours are the room's own, but each is a mix of the
|
|
161
|
+
fifteen (sea for the night, accent for the lights) with black or white, so a re-branded
|
|
162
|
+
pack re-lights the room and no literal colour is spent (C1/C2). Component-scoped tokens on
|
|
163
|
+
the room itself — never :root. Fixed under the top bar, clipped, and decoration only;
|
|
164
|
+
the words column sits above it and the window never slides under it. The chrome seed in
|
|
165
|
+
the window grows a little on each screen (show() writes data-screen). The only motion is
|
|
166
|
+
a slow twinkle, the lamp's breathe and the seed's float, and the kill switch stops all
|
|
167
|
+
three: the rest frame is the whole composition. ── */
|
|
168
|
+
#view-new .wizRoom{
|
|
169
|
+
--rm-room:color-mix(in srgb,var(--sea) 4%,#000);
|
|
170
|
+
--rm-frame:color-mix(in srgb,var(--sea) 9%,#000);
|
|
171
|
+
--rm-night:color-mix(in srgb,var(--sea) 15%,#000);
|
|
172
|
+
--rm-moon:color-mix(in srgb,var(--accent) 12%,#fff);
|
|
173
|
+
--rm-halo:color-mix(in srgb,var(--accent) 12%,rgba(255,255,255,.16));
|
|
174
|
+
--rm-star:color-mix(in srgb,var(--sea) 14%,#fff);
|
|
175
|
+
--rm-tower-1:color-mix(in srgb,var(--sea) 8%,#000);
|
|
176
|
+
--rm-tower-2:color-mix(in srgb,var(--sea) 10%,#000);
|
|
177
|
+
--rm-tower-3:color-mix(in srgb,var(--sea) 7%,#000);
|
|
178
|
+
--rm-floor:color-mix(in srgb,var(--sea) 5%,#000);
|
|
179
|
+
--rm-edge:rgba(255,255,255,.06);
|
|
180
|
+
--rm-glow:color-mix(in srgb,var(--accent) 22%,transparent);
|
|
181
|
+
--rm-stem:color-mix(in srgb,var(--sea) 12%,#000);
|
|
182
|
+
--rm-shade:color-mix(in srgb,var(--sea) 16%,#000);
|
|
183
|
+
--rm-mug:color-mix(in srgb,var(--sea) 11%,#000);
|
|
184
|
+
--rm-leaf-1:color-mix(in srgb,var(--sea) 20%,#000);
|
|
185
|
+
--rm-leaf-2:color-mix(in srgb,var(--sea) 26%,#000);
|
|
186
|
+
--seed:.45;
|
|
187
|
+
position:fixed;left:0;right:0;top:var(--top-h,64px);bottom:0;z-index:0;overflow:hidden;pointer-events:none;
|
|
188
|
+
background:var(--rm-room);
|
|
189
|
+
}
|
|
190
|
+
#view-new .wizRoom>*,#view-new .rmWindow>*{position:absolute;}
|
|
191
|
+
#view-new .rmWindow{left:max(calc(50% + 80px),700px);top:56px;width:520px;height:520px;box-sizing:border-box;border:14px solid var(--rm-frame);border-radius:6px;background:var(--rm-night);overflow:hidden;}
|
|
192
|
+
#view-new .rmMoon{left:350px;top:60px;width:56px;height:56px;border-radius:50%;background:var(--rm-moon);box-shadow:0 0 60px 12px var(--rm-halo);}
|
|
193
|
+
#view-new .rmStar{border-radius:50%;background:var(--rm-star);}
|
|
194
|
+
#view-new .rmS1{left:70px;top:70px;width:3px;height:3px;}
|
|
195
|
+
#view-new .rmS2{left:180px;top:38px;width:2px;height:2px;}
|
|
196
|
+
#view-new .rmS3{left:250px;top:118px;width:3px;height:3px;opacity:.7;}
|
|
197
|
+
#view-new .rmS4{left:440px;top:168px;width:2px;height:2px;}
|
|
198
|
+
#view-new .rmS5{left:120px;top:186px;width:2px;height:2px;opacity:.6;}
|
|
199
|
+
#view-new .rmTower{bottom:0;}
|
|
200
|
+
#view-new .rmT1{left:0;width:110px;height:210px;background:var(--rm-tower-1);}
|
|
201
|
+
#view-new .rmT2{left:100px;width:86px;height:290px;background:var(--rm-tower-2);}
|
|
202
|
+
#view-new .rmT3{left:186px;width:130px;height:180px;background:var(--rm-tower-3);}
|
|
203
|
+
#view-new .rmT4{left:306px;width:76px;height:250px;background:var(--rm-tower-2);}
|
|
204
|
+
#view-new .rmT5{left:372px;width:140px;height:160px;background:var(--rm-tower-1);}
|
|
205
|
+
#view-new .rmLight{width:6px;height:8px;background:var(--accent);}
|
|
206
|
+
#view-new .rmL1{left:124px;bottom:232px;opacity:.7;}
|
|
207
|
+
#view-new .rmL2{left:148px;bottom:196px;opacity:.5;}
|
|
208
|
+
#view-new .rmL3{left:28px;bottom:140px;opacity:.55;}
|
|
209
|
+
#view-new .rmL4{left:326px;bottom:204px;opacity:.65;}
|
|
210
|
+
#view-new .rmL5{left:430px;bottom:100px;opacity:.55;}
|
|
211
|
+
#view-new .rmL6{left:232px;bottom:110px;opacity:.45;}
|
|
212
|
+
#view-new .rmMullion{left:246px;top:0;height:100%;border-left:12px solid var(--rm-frame);}
|
|
213
|
+
#view-new .rmFloor{left:0;right:0;top:660px;bottom:0;background:var(--rm-floor);border-top:1px solid var(--rm-edge);}
|
|
214
|
+
#view-new .rmGlow{left:calc(50% + 420px);top:430px;width:300px;height:300px;border-radius:50%;background:radial-gradient(circle,var(--rm-glow) 0%,transparent 65%);}
|
|
215
|
+
#view-new .rmStem{left:calc(50% + 550px);top:520px;width:6px;height:140px;background:var(--rm-stem);}
|
|
216
|
+
#view-new .rmShade{left:calc(50% + 506px);top:500px;width:94px;height:46px;border-radius:48px 48px 6px 6px;background:var(--rm-shade);}
|
|
217
|
+
#view-new .rmMug{left:calc(50% + 330px);top:610px;width:46px;height:52px;border-radius:0 0 10px 10px;background:var(--rm-mug);}
|
|
218
|
+
#view-new .rmLeaf{border-radius:50% 50% 0 0;}
|
|
219
|
+
#view-new .rmF1{left:calc(50% + 170px);top:570px;width:64px;height:90px;background:var(--rm-leaf-1);}
|
|
220
|
+
#view-new .rmF2{left:calc(50% + 210px);top:550px;width:50px;height:110px;background:var(--rm-leaf-2);}
|
|
221
|
+
/* the seed: the world's chrome blob, hanging in the window's sky */
|
|
222
|
+
#view-new .rmSeed{left:120px;top:120px;width:120px;height:120px;transform:scale(var(--seed));transform-origin:50% 50%;}
|
|
223
|
+
#view-new .rmSeed img{display:block;width:100%;height:100%;object-fit:contain;filter:drop-shadow(0 0 22px var(--rm-glow));}
|
|
224
|
+
#view-new .wizRoom[data-screen="signin"]{--seed:.3;}
|
|
225
|
+
#view-new .wizRoom[data-screen="fork"]{--seed:.45;}
|
|
226
|
+
#view-new .wizRoom[data-screen="what"]{--seed:.55;}
|
|
227
|
+
#view-new .wizRoom[data-screen="checks"]{--seed:.65;}
|
|
228
|
+
#view-new .wizRoom[data-screen="review"]{--seed:.75;}
|
|
229
|
+
#view-new .wizRoom[data-screen="look"]{--seed:.85;}
|
|
230
|
+
#view-new .wizRoom[data-screen="done"]{--seed:1.1;}
|
|
231
|
+
/* soft film grain over the room — the lofi texture, no colour of its own */
|
|
232
|
+
#view-new .rmGrain{inset:0;opacity:.07;mix-blend-mode:overlay;background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.9' numOctaves='2' stitchTiles='stitch'/></filter><rect width='100%' height='100%' filter='url(%23n)'/></svg>");}
|
|
267
233
|
@media (prefers-reduced-motion:no-preference){
|
|
268
|
-
.
|
|
269
|
-
.
|
|
270
|
-
|
|
234
|
+
#view-new .rmSeed{transition:transform 1.2s ease;}
|
|
235
|
+
#view-new .rmStar{animation:rmTwinkle 7s ease-in-out infinite;}
|
|
236
|
+
#view-new .rmS2{animation-delay:-2s;}
|
|
237
|
+
#view-new .rmS3{animation-delay:-4s;}
|
|
238
|
+
#view-new .rmS4{animation-delay:-1s;}
|
|
239
|
+
#view-new .rmS5{animation-delay:-5s;}
|
|
240
|
+
#view-new .rmGlow{animation:rmBreathe 9s ease-in-out infinite;}
|
|
241
|
+
#view-new .rmSeed img{animation:rmFloat 8s ease-in-out infinite;}
|
|
242
|
+
@keyframes rmTwinkle{0%,100%{opacity:.9}50%{opacity:.35}}
|
|
243
|
+
@keyframes rmBreathe{0%,100%{opacity:1}50%{opacity:.7}}
|
|
244
|
+
@keyframes rmFloat{0%,100%{transform:translateY(0)}50%{transform:translateY(-6px)}}
|
|
245
|
+
}
|
|
246
|
+
/* under 900 the window steps back and dims behind the words; the lamp, mug and plant go */
|
|
247
|
+
@media (max-width:900px){
|
|
248
|
+
#view-new .rmWindow{left:auto;right:-140px;top:24px;opacity:.35;}
|
|
249
|
+
#view-new .rmGlow,#view-new .rmStem,#view-new .rmShade,#view-new .rmMug,#view-new .rmLeaf{display:none;}
|
|
250
|
+
#view-new .rmFloor{top:auto;height:80px;}
|
|
251
|
+
}
|
|
252
|
+
/* the words column above the room, held to the mockup's measure so the window never
|
|
253
|
+
slides under it; headings in the mockup's sentence case */
|
|
254
|
+
#view-new .wrap{position:relative;z-index:1;min-height:calc(100vh - var(--top-h,64px) - 120px);}
|
|
255
|
+
#view-new .panel{max-width:600px;}
|
|
256
|
+
.wizTop{display:flex;align-items:center;gap:16px;min-height:44px;margin:0 0 28px;max-width:600px;}
|
|
257
|
+
.wizEyebrow{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);text-transform:uppercase;color:var(--ink-faint);}
|
|
258
|
+
.wizBack{margin-left:auto;min-height:40px;padding:0 16px;border-radius:999px;border:1px solid var(--glass-edge);background:var(--glass);color:var(--ink-soft);font:inherit;font-size:13px;cursor:pointer;}
|
|
259
|
+
.wizBack:hover{color:var(--ink);}
|
|
260
|
+
.wizBack[hidden]{display:none;}
|
|
261
|
+
.demoTag{display:inline-block;margin-left:12px;padding:3px 10px;border-radius:999px;font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);text-transform:uppercase;color:var(--accent);border:1px solid color-mix(in srgb,var(--accent) 55%,transparent);vertical-align:middle;}
|
|
262
|
+
.demoTag[hidden]{display:none;}
|
|
263
|
+
.fillOffer{margin:0 0 12px;}
|
|
264
|
+
.wizRecap{margin:22px 0 0;font-size:15px;line-height:1.5;color:var(--ink-soft);}
|
|
265
|
+
#view-new .doorPill .btn[disabled]{opacity:.35;cursor:default;}
|
|
266
|
+
/* the review: one hairline ledger of rows. Each row is its label, its value with one
|
|
267
|
+
plain line under it, and two actions — Looks right (the accent, softly) and Change.
|
|
268
|
+
A confirmed row settles to a quiet tick; an open row shows the old step's own controls
|
|
269
|
+
under its value, and Continue below stays locked until every row is confirmed. */
|
|
270
|
+
.setRows{margin:24px 0 0;border-top:1px solid var(--rule);}
|
|
271
|
+
.setRow{display:grid;grid-template-columns:120px minmax(0,1fr) auto;gap:0 16px;align-items:start;padding:16px 0;border-bottom:1px solid var(--rule);}
|
|
272
|
+
.setRow[hidden]{display:none;}
|
|
273
|
+
.setRow .k{font-size:13px;line-height:22px;color:var(--ink-faint);padding-top:2px;}
|
|
274
|
+
.setRow .v{min-width:0;}
|
|
275
|
+
.setRow .rowV{display:block;font-size:15.5px;line-height:1.45;color:var(--ink);overflow-wrap:anywhere;}
|
|
276
|
+
.setRow .rowS{display:block;margin-top:3px;font-size:13px;line-height:1.45;color:var(--ink-faint);overflow-wrap:anywhere;}
|
|
277
|
+
.setRow .rowS:empty{display:none;}
|
|
278
|
+
.setRow .acts{display:flex;gap:6px;flex:none;}
|
|
279
|
+
.setRow .acts button{min-height:34px;padding:0 14px;border-radius:999px;font:inherit;font-size:13px;white-space:nowrap;cursor:pointer;}
|
|
280
|
+
.setRow [data-row-ok]{border:1px solid color-mix(in srgb,var(--accent) 55%,transparent);background:color-mix(in srgb,var(--accent) 14%,transparent);color:var(--ink);}
|
|
281
|
+
.setRow [data-row-chg]{border:1px solid var(--glass-edge);background:var(--glass);color:var(--ink-soft);}
|
|
282
|
+
.setRow [data-row-chg]:hover{color:var(--ink);}
|
|
283
|
+
.setRow [data-row-chg][hidden]{display:none;}
|
|
284
|
+
.setRow.done .rowV{color:var(--ink-soft);}
|
|
285
|
+
.setRow.done [data-row-ok]{border-color:transparent;background:none;color:var(--accent);cursor:default;}
|
|
286
|
+
.setRow.done [data-row-ok]::before{content:"✓ ";}
|
|
287
|
+
.setRow.open [data-row-ok]{display:none;}
|
|
288
|
+
.setRow .opts{margin-top:14px;}
|
|
289
|
+
.setRow .opts .rowUse{margin-top:6px;}
|
|
290
|
+
@media (max-width:560px){
|
|
291
|
+
.setRow{grid-template-columns:minmax(0,1fr);gap:6px 0;}
|
|
292
|
+
.setRow .k{padding-top:0;}
|
|
271
293
|
}
|
|
272
294
|
/* under 520 the two doors cannot sit side by side at 48px, so they stack —
|
|
273
295
|
still two equal doors in one object */
|
|
@@ -343,13 +365,6 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
|
|
|
343
365
|
|
|
344
366
|
/* the review: a ledger of what is about to be filed — label · value rows on
|
|
345
367
|
soft hairlines, the last row closed */
|
|
346
|
-
.review{list-style:none;display:grid;grid-template-columns:172px 1fr;gap:0 22px;border:0;border-radius:0;overflow:visible;margin:0 0 6px;}
|
|
347
|
-
.review li{display:contents;}
|
|
348
|
-
.review .k,.review .v{border-top:1px solid var(--rule-soft);padding:13px 0;min-width:0;}
|
|
349
|
-
.review li:last-child .k,.review li:last-child .v{border-bottom:1px solid var(--rule-soft);}
|
|
350
|
-
.review .k{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);text-transform:uppercase;font-weight:400;color:var(--ink-faint);padding-top:16px;}
|
|
351
|
-
.review .v{font-size:15px;font-weight:400;line-height:1.5;color:var(--ink);text-align:left;overflow-wrap:anywhere;}
|
|
352
|
-
.review .v code{font-family:var(--font-mono);font-size:13px;background:none;border-radius:0;padding:0;color:var(--ink-soft);}
|
|
353
368
|
|
|
354
369
|
/* ── done / human steps: the tick leaves (the rail says filed), the title
|
|
355
370
|
stands in the display voice, the steps only you can play are a numbered
|
|
@@ -470,11 +485,9 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
470
485
|
.addrConfirm details .inner{margin-top:6px;}
|
|
471
486
|
.addrOpt{display:flex;align-items:flex-start;gap:12px;margin-top:14px;font-size:14px;font-weight:400;color:var(--warn);line-height:1.5;cursor:pointer;}
|
|
472
487
|
.addrOpt input{margin-top:4px;accent-color:var(--accent);}
|
|
473
|
-
/* the app
|
|
488
|
+
/* the app row (task 1004351, ADR 0345): one row per place the app
|
|
474
489
|
can run. A radio per row so the choice is native; "soon" is a row that is shown but
|
|
475
490
|
cannot be picked yet, and says so in words rather than by colour alone. */
|
|
476
|
-
.appHall{font-size:14.5px;font-weight:400;color:var(--ink-soft);line-height:1.5;max-width:62ch;margin:0 0 18px;}
|
|
477
|
-
.appHall b{color:var(--ink);font-weight:500;}
|
|
478
491
|
.appOpts{border:0;padding:0;margin:0 0 14px;border-top:1px solid var(--rule);}
|
|
479
492
|
.appOpts legend{font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);text-transform:uppercase;font-weight:400;color:var(--ink-faint);padding:0;margin:0 0 10px;}
|
|
480
493
|
.appOpt{display:flex;align-items:flex-start;gap:12px;min-height:24px;padding:14px 0;border-bottom:1px solid var(--rule);cursor:pointer;}
|
|
@@ -623,10 +636,6 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
623
636
|
.typeCards{grid-template-columns:1fr;}
|
|
624
637
|
.tpill{border-left:0;padding-left:0;border-top:1px solid var(--rule-soft);}
|
|
625
638
|
.tpill:first-child{border-top:0;}
|
|
626
|
-
.review{grid-template-columns:1fr;gap:0;}
|
|
627
|
-
.review .k{padding-bottom:0;}
|
|
628
|
-
.review .v{border-top:0;padding-top:6px;}
|
|
629
|
-
.review li:last-child .k{border-bottom:0;}
|
|
630
639
|
}
|
|
631
640
|
|
|
632
641
|
</style>
|
|
@@ -1342,50 +1351,53 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1342
1351
|
The terms panel carries a static `on` so a no-script visitor still reads
|
|
1343
1352
|
step 1 rather than an empty card. -->
|
|
1344
1353
|
<section id="view-new" hidden>
|
|
1354
|
+
<!-- the night room (task 1004504, the design of record docs/design/mocks/combined-onboarding/):
|
|
1355
|
+
the studio's lofi room behind the whole wizard, in place of the nebula plate, the
|
|
1356
|
+
forming planet and the bongo pair. Every colour is a mix of the fifteen (no page
|
|
1357
|
+
:root, no literal); the chrome seed hangs in the window and grows a little on each
|
|
1358
|
+
screen (show() writes data-screen); the only motion is a slow twinkle, the lamp's
|
|
1359
|
+
breathe and the seed's float, all inside the kill switch. Decoration only. -->
|
|
1360
|
+
<div class="wizRoom" id="wizRoom" data-screen="fork" aria-hidden="true">
|
|
1361
|
+
<div class="rmWindow">
|
|
1362
|
+
<span class="rmMoon"></span>
|
|
1363
|
+
<span class="rmStar rmS1"></span><span class="rmStar rmS2"></span><span class="rmStar rmS3"></span><span class="rmStar rmS4"></span><span class="rmStar rmS5"></span>
|
|
1364
|
+
<span class="rmSeed"><img src="/assets/world/r2-chrome-blob-one-cut.webp" alt=""></span>
|
|
1365
|
+
<span class="rmTower rmT1"></span><span class="rmTower rmT2"></span><span class="rmTower rmT3"></span><span class="rmTower rmT4"></span><span class="rmTower rmT5"></span>
|
|
1366
|
+
<span class="rmLight rmL1"></span><span class="rmLight rmL2"></span><span class="rmLight rmL3"></span><span class="rmLight rmL4"></span><span class="rmLight rmL5"></span><span class="rmLight rmL6"></span>
|
|
1367
|
+
<span class="rmMullion"></span>
|
|
1368
|
+
</div>
|
|
1369
|
+
<span class="rmFloor"></span>
|
|
1370
|
+
<span class="rmGlow"></span><span class="rmStem"></span><span class="rmShade"></span>
|
|
1371
|
+
<span class="rmMug"></span><span class="rmLeaf rmF1"></span><span class="rmLeaf rmF2"></span>
|
|
1372
|
+
<span class="rmGrain"></span>
|
|
1373
|
+
</div>
|
|
1345
1374
|
<div class="wrap viewWrap">
|
|
1346
|
-
<div class="card
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
<
|
|
1351
|
-
<
|
|
1352
|
-
|
|
1353
|
-
<li data-step="8"><span class="snum">6</span><span class="slbl">Home</span></li>
|
|
1354
|
-
<li data-step="9"><span class="snum">7</span><span class="slbl">Address</span></li>
|
|
1355
|
-
<li data-step="10"><span class="snum">8</span><span class="slbl">App</span></li>
|
|
1356
|
-
<li data-step="11"><span class="snum">9</span><span class="slbl">Look</span></li>
|
|
1357
|
-
<li data-step="12"><span class="snum">10</span><span class="slbl">Create</span></li>
|
|
1358
|
-
</ol>
|
|
1359
|
-
|
|
1375
|
+
<div class="card">
|
|
1376
|
+
<!-- no progress rail (owner, 2026-10-01): the eyebrow names where you are and one
|
|
1377
|
+
Back walks the screens backwards -->
|
|
1378
|
+
<div class="wizTop">
|
|
1379
|
+
<span class="wizEyebrow" id="wizEyebrow">New project</span>
|
|
1380
|
+
<button type="button" class="wizBack" id="wizBack" hidden>← Back</button>
|
|
1381
|
+
</div>
|
|
1360
1382
|
<div class="createSplit">
|
|
1361
1383
|
<div>
|
|
1362
|
-
<!--
|
|
1384
|
+
<!-- 0 · sign in -->
|
|
1363
1385
|
<section class="panel" id="panel-gate" aria-labelledby="gate-h">
|
|
1364
|
-
<h1 id="gate-h">
|
|
1365
|
-
<p class="lede">
|
|
1386
|
+
<h1 id="gate-h">Make something.</h1>
|
|
1387
|
+
<p class="lede">Sign in with GitHub to start your project.</p>
|
|
1366
1388
|
<!-- The wizard's own sign-in opts into repo scope (read:user + repo, ADR 0155) up
|
|
1367
1389
|
front (&repos=1) — anyone here intends to create a project, which needs repo
|
|
1368
|
-
access, so we ask once at sign-in instead of a second "Connect" step
|
|
1390
|
+
access, so we ask once at sign-in instead of a second "Connect" step later.
|
|
1369
1391
|
Hall + CLI sign-in stay identity-only (ADR 0143 least-privilege default). -->
|
|
1370
1392
|
<a class="btn big" id="wizSignin" href="/api/bongos/auth/web/start?return=/projects%3Fview%3Dnew&repos=1">
|
|
1371
1393
|
<svg class="gh" viewBox="0 0 16 16" aria-hidden="true"><path d="M8 0C3.58 0 0 3.58 0 8c0 3.54 2.29 6.53 5.47 7.59.4.07.55-.17.55-.38 0-.19-.01-.82-.01-1.49-2.01.37-2.53-.49-2.69-.94-.09-.23-.48-.94-.82-1.13-.28-.15-.68-.52-.01-.53.63-.01 1.08.58 1.23.82.72 1.21 1.87.87 2.33.66.07-.52.28-.87.51-1.07-1.78-.2-3.64-.89-3.64-3.95 0-.87.31-1.59.82-2.15-.08-.2-.36-1.02.08-2.12 0 0 .67-.21 2.2.82.64-.18 1.32-.27 2-.27s1.36.09 2 .27c1.53-1.04 2.2-.82 2.2-.82.44 1.1.16 1.92.08 2.12.51.56.82 1.27.82 2.15 0 3.07-1.87 3.75-3.65 3.95.29.25.54.73.54 1.48 0 1.07-.01 1.93-.01 2.2 0 .21.15.46.55.38A8.01 8.01 0 0 0 16 8c0-4.42-3.58-8-8-8z"/></svg>
|
|
1372
|
-
|
|
1394
|
+
Continue with GitHub
|
|
1373
1395
|
</a>
|
|
1374
1396
|
<!-- Informed consent BEFORE GitHub's own wording (ADR 0155): the scope the
|
|
1375
1397
|
button asks for reads as "all your repositories" on GitHub's screen, so
|
|
1376
|
-
the reason is stated here, on the path every founder actually takes
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
<!-- Trust links (task 1003502, superseding task 1002705's license-only fallback).
|
|
1380
|
-
TWO links, because the sentence makes two claims: "open source" → the CODE,
|
|
1381
|
-
"(AGPL-3.0)" → the licence it is under. The code half was impossible until
|
|
1382
|
-
2026-09-13 — the old private-repo URL 404ed, both npm scopes were private,
|
|
1383
|
-
and the ADR 0099 mirror is dormant until ~2026-11, which left the licence
|
|
1384
|
-
text as the only live artifact. Task 1003872 / ADR 0283 published
|
|
1385
|
-
@bongos/core publicly, so the claim now resolves to the platform itself
|
|
1386
|
-
(npm renders a README + a browsable Code tab; verified by a tokenless
|
|
1387
|
-
`npm pack` from a clean HOME, the ADR 0283 standard). Not @cloudbongos/cli:
|
|
1388
|
-
that is the 32-file generated client, a slice of the thing being claimed. -->
|
|
1398
|
+
the reason is stated here, on the path every founder actually takes. -->
|
|
1399
|
+
<p class="finePrint" id="wizScopeNote">Signing in asks GitHub for repository access. GitHub calls this access to <em>all</em> your repositories, because it is the only option that can write to a private repo. We use it only for your project’s one repo. Nothing else is touched.</p>
|
|
1400
|
+
<!-- Trust links (task 1003502): "open source" → the code, "(AGPL-3.0)" → its licence. -->
|
|
1389
1401
|
<p class="finePrint">Free while we find our feet. Your project’s repo and server are yours —
|
|
1390
1402
|
<span data-brand-product-inline>Cloud Bongos</span> is <a href="https://www.npmjs.com/package/@bongos/core" target="_blank" rel="noopener">open source</a>
|
|
1391
1403
|
(<a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener">AGPL-3.0</a>)
|
|
@@ -1393,735 +1405,513 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
1393
1405
|
<div class="alert" id="wizGateAlert" role="alert"></div>
|
|
1394
1406
|
</section>
|
|
1395
1407
|
|
|
1396
|
-
<!--
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
<section class="panel on" id="panel-terms" aria-labelledby="terms-h">
|
|
1408
|
+
<!-- 1 · demo or real project, with the terms as its one tick (tasks 1004417 and
|
|
1409
|
+
1004504). The tick rides the create as `terms_acknowledged` and the SERVER
|
|
1410
|
+
records which terms version it was (modules/provisioning/terms-ack.js), so
|
|
1411
|
+
this page never names one. Both doors wait for it. The demo opens one screen
|
|
1412
|
+
in place (task 1004420), then joins the ordinary flow marked "demo". The panel
|
|
1413
|
+
carries a static `on` so a no-script visitor reads it rather than an empty card. -->
|
|
1414
|
+
<section class="panel on" id="panel-fork" aria-labelledby="fork-h">
|
|
1404
1415
|
<!-- The standup already being watched, kept one click away (task 1003503).
|
|
1405
|
-
A deliberate "New project" click opens THIS fresh step 1 (the front door since task 1004417); the previous
|
|
1406
|
-
project's done panel is reachable here instead of taking the panel over.
|
|
1407
1416
|
Baked markup, hidden until enter() has a record to name. -->
|
|
1408
1417
|
<p class="resumeOffer" id="wizResume" hidden>still watching <strong id="wizResumeName">your project</strong> — <a href="#" id="wizResumeGo">resume it</a></p>
|
|
1409
|
-
<
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1418
|
+
<div id="forkIntro">
|
|
1419
|
+
<h1 id="fork-h">You’re about to start a digital planet.</h1>
|
|
1420
|
+
<p class="lede">Try a small demo first, or start your real project.</p>
|
|
1421
|
+
<form data-next="2">
|
|
1422
|
+
<label class="termsAck">
|
|
1423
|
+
<input type="checkbox" id="termsAck" aria-describedby="h-terms">
|
|
1424
|
+
<span>I can sign for this project, and I accept the <a class="termsRead" href="/terms" target="_blank" rel="noopener">terms</a>.</span>
|
|
1425
|
+
</label>
|
|
1426
|
+
<div class="hint" id="h-terms"> </div>
|
|
1427
|
+
<div class="doorPill" role="group" aria-label="How to begin">
|
|
1428
|
+
<button type="button" class="btn big ghost" id="wizDemo" aria-controls="demoSetup" aria-expanded="false" disabled>Try the demo</button>
|
|
1429
|
+
<button type="submit" class="btn big" id="wizStart" disabled>Start my project</button>
|
|
1430
|
+
</div>
|
|
1431
|
+
</form>
|
|
1432
|
+
</div>
|
|
1433
|
+
<!-- the demo route (task 1004420, BV2.PS09): one screen inside the fork, then
|
|
1434
|
+
the ordinary flow from "What are you making?": a demo is a normal project
|
|
1435
|
+
marked "demo" (D9). The fit check is a copy of modules/provisioning/demo.js
|
|
1436
|
+
fitCheck, held equal by tests/wizard_demo.mjs. -->
|
|
1437
|
+
<div id="demoSetup" class="demoView" hidden>
|
|
1438
|
+
<h1 id="demo-h1">Try a demo.</h1>
|
|
1439
|
+
<p class="lede">A small, time-boxed project to learn by doing. You can keep it at the end.</p>
|
|
1440
|
+
<div class="demoSteppers">
|
|
1441
|
+
<div class="wizField"><span id="demoPeopleLbl">People, you included</span>
|
|
1442
|
+
<div class="demoStep" role="group" aria-labelledby="demoPeopleLbl">
|
|
1443
|
+
<button type="button" class="btn small ghost" data-demo-step="people" data-by="-1" aria-label="One fewer person">−</button>
|
|
1444
|
+
<output id="demoPeople" aria-live="polite">3</output>
|
|
1445
|
+
<button type="button" class="btn small ghost" data-demo-step="people" data-by="1" aria-label="One more person">+</button>
|
|
1446
|
+
</div>
|
|
1447
|
+
</div>
|
|
1448
|
+
<div class="wizField"><span id="demoHoursLbl">Hours each</span>
|
|
1449
|
+
<div class="demoStep" role="group" aria-labelledby="demoHoursLbl">
|
|
1450
|
+
<button type="button" class="btn small ghost" data-demo-step="hours" data-by="-1" aria-label="One hour less">−</button>
|
|
1451
|
+
<output id="demoHours" aria-live="polite">4</output>
|
|
1452
|
+
<button type="button" class="btn small ghost" data-demo-step="hours" data-by="1" aria-label="One hour more">+</button>
|
|
1453
|
+
</div>
|
|
1454
|
+
</div>
|
|
1455
|
+
</div>
|
|
1456
|
+
<label class="wizField"><span>Your idea</span>
|
|
1457
|
+
<input type="text" id="demoIdea" maxlength="600" placeholder="A voting app for movie night" autocomplete="off">
|
|
1416
1458
|
</label>
|
|
1417
|
-
<
|
|
1418
|
-
<div class="
|
|
1459
|
+
<p class="hint demoFitLine" aria-live="polite"><span id="demoFitSum">3 people × 4 hours = 12 builder-hours.</span> <span id="demoFitVerdict">Enough for a small version. Trim it to one shared list, voting and a winner reveal; accounts and notifications will not fit.</span></p>
|
|
1460
|
+
<div class="demoInvites" id="demoInvites">
|
|
1461
|
+
<label class="wizField"><span>Invite by GitHub handle</span>
|
|
1462
|
+
<input type="text" id="demoInvite1" placeholder="teammate’s handle" autocomplete="off" autocapitalize="off" spellcheck="false" maxlength="39">
|
|
1463
|
+
</label>
|
|
1464
|
+
<label class="wizField"><span>And one more</span>
|
|
1465
|
+
<input type="text" id="demoInvite2" placeholder="teammate’s handle" autocomplete="off" autocapitalize="off" spellcheck="false" maxlength="39">
|
|
1466
|
+
</label>
|
|
1467
|
+
</div>
|
|
1468
|
+
<p class="finePrint">An unkept demo goes offline after 30 days. We warn you first, and it can come back.</p>
|
|
1419
1469
|
<div class="nav">
|
|
1420
1470
|
<span></span>
|
|
1421
|
-
<button type="
|
|
1471
|
+
<button type="button" class="btn" id="demoStart">Continue</button>
|
|
1472
|
+
</div>
|
|
1473
|
+
</div>
|
|
1474
|
+
</section>
|
|
1475
|
+
|
|
1476
|
+
<!-- 2 · what are you making: the name, the kind (task 1002772 — a declaration,
|
|
1477
|
+
closed vocabulary, Not sure the escape hatch) and the optional sentence that
|
|
1478
|
+
puts the project on the public map (ADR 0182 D4). -->
|
|
1479
|
+
<section class="panel" id="panel-what" aria-labelledby="what-h">
|
|
1480
|
+
<h1 id="what-h">What are you making?<span class="demoTag" id="demoTag" hidden>demo</span></h1>
|
|
1481
|
+
<form id="newForm" data-next="3">
|
|
1482
|
+
<label class="wizField"><span>Name</span>
|
|
1483
|
+
<input type="text" id="newName" placeholder="Moonbase" autocomplete="off" maxlength="80" aria-label="Project name">
|
|
1484
|
+
<div class="hint" id="h-name"> </div>
|
|
1485
|
+
</label>
|
|
1486
|
+
<div class="wizField"><span id="typeLbl">Kind</span>
|
|
1487
|
+
<div class="typeCards" id="typeCards" role="group" aria-labelledby="typeLbl">
|
|
1488
|
+
<button type="button" class="tpill" data-type="game" aria-pressed="false">Game</button>
|
|
1489
|
+
<button type="button" class="tpill" data-type="research" aria-pressed="false">Research</button>
|
|
1490
|
+
<button type="button" class="tpill" data-type="business" aria-pressed="false">Business</button>
|
|
1491
|
+
<button type="button" class="tpill" data-type="non-profit" aria-pressed="false">Non-profit</button>
|
|
1492
|
+
<button type="button" class="tpill" data-type="not-sure" aria-pressed="false">Not sure</button>
|
|
1493
|
+
</div>
|
|
1494
|
+
<div class="hint" id="h-projtype"> </div>
|
|
1495
|
+
</div>
|
|
1496
|
+
<label class="wizField"><span>In a sentence <small>(optional — puts it on the public map)</small></span>
|
|
1497
|
+
<input type="text" id="f-desc" maxlength="600" placeholder="A co-op space game built by its players" autocomplete="off">
|
|
1498
|
+
</label>
|
|
1499
|
+
<div class="nav">
|
|
1500
|
+
<span></span>
|
|
1501
|
+
<button type="submit" class="btn">Continue</button>
|
|
1422
1502
|
</div>
|
|
1423
1503
|
</form>
|
|
1424
1504
|
</section>
|
|
1425
1505
|
|
|
1426
|
-
<!--
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
<section class="panel" id="panel-
|
|
1432
|
-
<div
|
|
1433
|
-
<
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
</div>
|
|
1442
|
-
<div class="nav">
|
|
1443
|
-
<button type="button" class="btn ghost" data-back="1">← Back</button>
|
|
1444
|
-
<span></span>
|
|
1445
|
-
</div>
|
|
1446
|
-
</form>
|
|
1506
|
+
<!-- 3 · a few quick checks: the planet-physics answers that change something
|
|
1507
|
+
(tasks 1004418 and 1004419), under planet-physics.js's own keys. Continue asks
|
|
1508
|
+
POST /provisioning/physics-check — the same screen and soft-limit table the
|
|
1509
|
+
create runs — and a stop ends here, before anything exists. The age tiers are
|
|
1510
|
+
described, never claimed as enforced (spec D6). A demo skips this screen. -->
|
|
1511
|
+
<section class="panel" id="panel-checks" aria-labelledby="checks-h">
|
|
1512
|
+
<div id="checksMain">
|
|
1513
|
+
<h1 id="checks-h">A few quick checks.</h1>
|
|
1514
|
+
<p class="lede">These decide which legal and payout options we can offer.</p>
|
|
1515
|
+
<!-- auto-fill: the answers this account gave on its most recent project -->
|
|
1516
|
+
<p class="fillOffer" id="physFillOffer" hidden><button type="button" class="linklike" id="physFromAccount">Fill these in from my last project</button></p>
|
|
1517
|
+
<form data-next="4" id="checksForm">
|
|
1518
|
+
<div class="physRow2">
|
|
1519
|
+
<label class="wizField"><span>Where are you?</span><select id="physCountry"><option value="">Choose…</option></select></label>
|
|
1520
|
+
<label class="wizField" id="physRegionField" hidden><span>State or province</span><select id="physRegion"><option value="">Choose…</option></select></label>
|
|
1447
1521
|
</div>
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
<p class="lede">A small, time-boxed project to learn the platform by doing. Everyone uses their own AI plan.</p>
|
|
1455
|
-
<div class="demoSteppers">
|
|
1456
|
-
<div class="wizField"><span id="demoPeopleLbl">People, you included</span>
|
|
1457
|
-
<div class="demoStep" role="group" aria-labelledby="demoPeopleLbl">
|
|
1458
|
-
<button type="button" class="btn small ghost" data-demo-step="people" data-by="-1" aria-label="One fewer person">−</button>
|
|
1459
|
-
<output id="demoPeople" aria-live="polite">3</output>
|
|
1460
|
-
<button type="button" class="btn small ghost" data-demo-step="people" data-by="1" aria-label="One more person">+</button>
|
|
1461
|
-
</div>
|
|
1462
|
-
</div>
|
|
1463
|
-
<div class="wizField"><span id="demoHoursLbl">Hours each</span>
|
|
1464
|
-
<div class="demoStep" role="group" aria-labelledby="demoHoursLbl">
|
|
1465
|
-
<button type="button" class="btn small ghost" data-demo-step="hours" data-by="-1" aria-label="One hour less">−</button>
|
|
1466
|
-
<output id="demoHours" aria-live="polite">4</output>
|
|
1467
|
-
<button type="button" class="btn small ghost" data-demo-step="hours" data-by="1" aria-label="One hour more">+</button>
|
|
1468
|
-
</div>
|
|
1469
|
-
</div>
|
|
1522
|
+
<div class="wizField"><span id="physGeoLbl">Where will builders be?</span>
|
|
1523
|
+
<div class="typeCards physCells" role="group" aria-labelledby="physGeoLbl">
|
|
1524
|
+
<button type="button" class="tpill" data-phys="builder_geography" data-val="us-only" aria-pressed="false">US only</button>
|
|
1525
|
+
<button type="button" class="tpill" data-phys="builder_geography" data-val="us-eu" aria-pressed="false">US and EU</button>
|
|
1526
|
+
<button type="button" class="tpill" data-phys="builder_geography" data-val="global" aria-pressed="false">Anywhere</button>
|
|
1527
|
+
<button type="button" class="tpill" data-phys="builder_geography" data-val="not-sure" aria-pressed="false">Not sure</button>
|
|
1470
1528
|
</div>
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
<
|
|
1476
|
-
<p class="demoFitLine"><span id="demoFitSum">3 people × 4 hours = 12 builder-hours.</span> <span id="demoFitVerdict">Enough for a small version. Trim it to one shared list, voting and a winner reveal; accounts and notifications will not fit.</span></p>
|
|
1477
|
-
<p class="demoFitTry">What you’ll get to try</p>
|
|
1478
|
-
<ul class="demoTry">
|
|
1479
|
-
<li>Claim a task and ship it</li>
|
|
1480
|
-
<li>See your work graded</li>
|
|
1481
|
-
<li>Put an idea to a vote in the Board Room</li>
|
|
1482
|
-
<li>Watch your rank grow with shipped work</li>
|
|
1483
|
-
</ul>
|
|
1484
|
-
</div>
|
|
1485
|
-
<div class="nav">
|
|
1486
|
-
<button type="button" class="btn ghost" id="demoBack1">← Back</button>
|
|
1487
|
-
<button type="button" class="btn" id="demoNext1">See the settings</button>
|
|
1529
|
+
</div>
|
|
1530
|
+
<div class="wizField"><span id="physAgeLbl">Will anyone under 18 build?</span>
|
|
1531
|
+
<div class="typeCards physCells" role="group" aria-labelledby="physAgeLbl">
|
|
1532
|
+
<button type="button" class="tpill" data-phys="minors_on_board" data-val="false" aria-pressed="false">No</button>
|
|
1533
|
+
<button type="button" class="tpill" data-phys="minors_on_board" data-val="true" aria-pressed="false">Yes</button>
|
|
1488
1534
|
</div>
|
|
1489
1535
|
</div>
|
|
1490
|
-
<div
|
|
1491
|
-
<
|
|
1492
|
-
<
|
|
1493
|
-
|
|
1494
|
-
<dt>
|
|
1495
|
-
<dt>
|
|
1496
|
-
<dt>Government</dt><dd>Monarchy / BDFL<small>One leader decides: you make the calls.</small></dd>
|
|
1497
|
-
<dt>Rewards</dt><dd>None<small>It’s a demo, for learning.</small></dd>
|
|
1498
|
-
<dt>Framing</dt><dd>All managed for you</dd>
|
|
1499
|
-
<dt>Who can see it</dt><dd>Only your team</dd>
|
|
1500
|
-
<dt>AI usage</dt><dd>Everyone uses their own AI plan</dd>
|
|
1536
|
+
<div class="physNote" id="physAges" hidden>
|
|
1537
|
+
<p class="physNoteH">The age tiers young builders work under</p>
|
|
1538
|
+
<dl class="physTiers">
|
|
1539
|
+
<dt>13 to 15</dt><dd>Non-monetary tracks only</dd>
|
|
1540
|
+
<dt>16 to 17</dt><dd>Paid work, with a guardian’s co-signature and a work-permit attestation</dd>
|
|
1541
|
+
<dt>18 and over</dt><dd>Full access</dd>
|
|
1501
1542
|
</dl>
|
|
1502
|
-
<
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
<
|
|
1507
|
-
|
|
1508
|
-
</
|
|
1543
|
+
<p class="finePrint">A guardian signs for each young builder and controls their payouts. The platform does not check ages for you yet.</p>
|
|
1544
|
+
</div>
|
|
1545
|
+
<div class="wizField" id="physIntent"><span id="physWhoLbl">Who’s behind it?</span>
|
|
1546
|
+
<div class="typeCards physCells" role="group" aria-labelledby="physWhoLbl">
|
|
1547
|
+
<button type="button" class="tpill" data-phys="entity" data-val="intent-solo" aria-pressed="false">Just me</button>
|
|
1548
|
+
<button type="button" class="tpill" data-phys="entity" data-val="intent-company" aria-pressed="false">A company</button>
|
|
1549
|
+
<button type="button" class="tpill" data-phys="entity" data-val="intent-nonprofit" aria-pressed="false">A nonprofit</button>
|
|
1550
|
+
<button type="button" class="tpill" data-phys="entity" data-val="intent-unsure" aria-pressed="false">Not sure yet</button>
|
|
1509
1551
|
</div>
|
|
1510
|
-
<
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1552
|
+
<button type="button" class="physLink" id="physHaveEntity">We already have a legal entity</button>
|
|
1553
|
+
</div>
|
|
1554
|
+
<div class="wizField" id="physForms" hidden><span>Your legal entity</span>
|
|
1555
|
+
<p class="physGrp">Organization</p>
|
|
1556
|
+
<div class="typeCards physCells" role="group" aria-label="Organization">
|
|
1557
|
+
<button type="button" class="tpill" data-phys="entity" data-val="nonprofit" aria-pressed="false">Nonprofit</button>
|
|
1558
|
+
<button type="button" class="tpill" data-phys="entity" data-val="for-profit" aria-pressed="false">For-profit company</button>
|
|
1559
|
+
<button type="button" class="tpill" data-phys="entity" data-val="corporate-division" aria-pressed="false">Division of a corporation</button>
|
|
1560
|
+
<button type="button" class="tpill" data-phys="entity" data-val="research" aria-pressed="false">Research entity</button>
|
|
1561
|
+
</div>
|
|
1562
|
+
<p class="physGrp">Group</p>
|
|
1563
|
+
<div class="typeCards physCells" role="group" aria-label="Group">
|
|
1564
|
+
<button type="button" class="tpill" data-phys="entity" data-val="university-project" aria-pressed="false">University project</button>
|
|
1565
|
+
<button type="button" class="tpill" data-phys="entity" data-val="social-group" aria-pressed="false">Social group</button>
|
|
1566
|
+
</div>
|
|
1567
|
+
<p class="physGrp">Individual</p>
|
|
1568
|
+
<div class="typeCards physCells" role="group" aria-label="Individual">
|
|
1569
|
+
<button type="button" class="tpill" data-phys="entity" data-val="single-owner-llc" aria-pressed="false">Single-owner LLC</button>
|
|
1570
|
+
<button type="button" class="tpill" data-phys="entity" data-val="private-individual" aria-pressed="false">Private individual</button>
|
|
1514
1571
|
</div>
|
|
1572
|
+
<button type="button" class="physLink" id="physNoEntity">We don’t have one yet</button>
|
|
1515
1573
|
</div>
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
<p class="demoStageCap" id="demoStageCap" hidden>time box · <span id="demoStageHours">12</span> builder-hours · when the dashed ring closes, the demo asks whether to keep your planet</p>
|
|
1523
|
-
</div>
|
|
1574
|
+
<div class="hint" id="h-phys"> </div>
|
|
1575
|
+
<div class="nav">
|
|
1576
|
+
<span></span>
|
|
1577
|
+
<button type="submit" class="btn" id="physNext">Continue</button>
|
|
1578
|
+
</div>
|
|
1579
|
+
</form>
|
|
1524
1580
|
</div>
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
<p class="routeLbl">Route 2 · planet creation</p>
|
|
1533
|
-
<p class="routeT">Create your planet</p>
|
|
1534
|
-
<p class="routeD">Answer a few questions about your project, choose where its code and its app live, and it stands up as yours.</p>
|
|
1581
|
+
<!-- the screening stop: nothing is created -->
|
|
1582
|
+
<div id="checksStop" hidden>
|
|
1583
|
+
<h1 id="phys-hstop">This can’t be built <em>on <span data-brand-product-inline>Cloud Bongos</span></em></h1>
|
|
1584
|
+
<p class="lede" id="physStopMsg"></p>
|
|
1585
|
+
<div class="nav">
|
|
1586
|
+
<button type="button" class="btn ghost" id="physStartOver">Start over</button>
|
|
1587
|
+
<a class="btn" href="/contact">Contact us</a>
|
|
1535
1588
|
</div>
|
|
1536
1589
|
</div>
|
|
1537
1590
|
</section>
|
|
1538
1591
|
|
|
1539
|
-
<!--
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
<
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
<
|
|
1592
|
+
<!-- 4 · check what we set up: every prefilled answer is a row the founder
|
|
1593
|
+
confirms ("Looks right") or changes in place. Change opens the step's own
|
|
1594
|
+
controls, moved here whole, so every handler and id is the one it always was;
|
|
1595
|
+
"Use this" checks the row the way its old step did and confirms it. Continue
|
|
1596
|
+
stays locked until every row that applies is confirmed. -->
|
|
1597
|
+
<section class="panel" id="panel-review" aria-labelledby="rev-h">
|
|
1598
|
+
<h1 id="rev-h">Check what we set up.</h1>
|
|
1599
|
+
<p class="lede">We filled these in from your answers. Confirm each one, or change it.</p>
|
|
1600
|
+
<form data-next="5" id="reviewForm">
|
|
1601
|
+
<div class="setRows" id="reviewRows">
|
|
1602
|
+
|
|
1603
|
+
<div class="setRow" data-row="code">
|
|
1604
|
+
<span class="k">Code</span>
|
|
1605
|
+
<div class="v"><span class="rowV" data-row-v></span><small class="rowS" data-row-s></small>
|
|
1606
|
+
<div class="opts" hidden>
|
|
1607
|
+
<div class="typeCards physCells" role="group" aria-label="Your project’s state">
|
|
1608
|
+
<button type="button" class="tpill" data-phys="project_state" data-val="new" aria-pressed="false">Starting from zero</button>
|
|
1609
|
+
<button type="button" class="tpill" data-phys="project_state" data-val="existing" aria-pressed="false">Already started</button>
|
|
1610
|
+
</div>
|
|
1611
|
+
<fieldset class="physExisting" id="physExisting" data-limit="existing_work" hidden>
|
|
1612
|
+
<legend>What already exists? Pick all that apply, then your main one.</legend>
|
|
1613
|
+
<label class="physCheck"><input type="checkbox" data-work="code"> <span>Code files</span> <span class="physMain"><input type="radio" name="physMain" value="code" aria-label="Code files is the main one"> main</span></label>
|
|
1614
|
+
<label class="physCheck"><input type="checkbox" data-work="digital-files"> <span>Digital files <small>Word documents, spreadsheets, other software.</small></span> <span class="physMain"><input type="radio" name="physMain" value="digital-files" aria-label="Digital files is the main one"> main</span></label>
|
|
1615
|
+
<label class="physCheck"><input type="checkbox" data-work="physical-work"> <span>Physical files or work <small>You’ll describe it later, in words or a document.</small></span> <span class="physMain"><input type="radio" name="physMain" value="physical-work" aria-label="Physical files or work is the main one"> main</span></label>
|
|
1616
|
+
</fieldset>
|
|
1617
|
+
<p class="physWhy" data-why="existing_work" hidden></p>
|
|
1618
|
+
<div class="choices" role="group" aria-label="Starting point">
|
|
1619
|
+
<button type="button" class="choice" id="c-green" aria-pressed="true">
|
|
1620
|
+
<b>A new GitHub repo</b>
|
|
1621
|
+
<p>An empty <strong>public</strong> repo, set up for you.</p>
|
|
1622
|
+
</button>
|
|
1623
|
+
<button type="button" class="choice" id="c-adopt" aria-pressed="false">
|
|
1624
|
+
<b>A repo I already have</b>
|
|
1625
|
+
<p>Public or private, we never change it.</p>
|
|
1626
|
+
</button>
|
|
1627
|
+
</div>
|
|
1628
|
+
<div class="reassure" id="adoptNote">Nothing is overwritten. You see any conflicts first, and your open issues become tasks.</div>
|
|
1629
|
+
<!-- GitHub-integrated repo picker (adopt) + auto-create (fresh) — task 1002099.
|
|
1630
|
+
Without repo access we fall back to the manual owner/repo inputs
|
|
1631
|
+
(#manualHome), which stay authoritative. -->
|
|
1632
|
+
<div id="ghConnect" class="reassure">
|
|
1633
|
+
<p id="ghConnectMsg"> </p>
|
|
1634
|
+
<button type="button" class="btn" id="ghConnectBtn">Allow repo access</button>
|
|
1635
|
+
<button type="button" class="linklike" id="ghManualToggle">or enter it manually</button>
|
|
1636
|
+
</div>
|
|
1637
|
+
<div id="ghBusy" class="hint" hidden>Reading your GitHub repositories…</div>
|
|
1638
|
+
<div id="ghAdopt" class="wizField" hidden>
|
|
1639
|
+
<span>Pick one of your repositories</span>
|
|
1640
|
+
<input type="text" id="ghSearch" placeholder="Search your repos…" autocomplete="off" aria-label="Search your repositories">
|
|
1641
|
+
<div id="ghList" class="ghlist" role="group" aria-label="Your repositories"></div>
|
|
1642
|
+
<div class="hint" id="ghAdoptHint"> </div>
|
|
1643
|
+
<!-- visibility stated on EVERY repo path (task 1002721) -->
|
|
1644
|
+
<div class="hint">We never change a repo’s visibility — public stays public, private stays private.</div>
|
|
1645
|
+
</div>
|
|
1646
|
+
<div id="ghFresh" class="wizField" hidden>
|
|
1647
|
+
<span>New repository name</span>
|
|
1648
|
+
<input type="text" id="ghNewName" placeholder="moonbase" autocomplete="off" maxlength="100" aria-label="New repository name">
|
|
1649
|
+
<!-- visibility is stated on EVERY repo path (task 1002721); updateFreshPreview()
|
|
1650
|
+
replaces this floor with the same claim plus the resolved repo URL -->
|
|
1651
|
+
<div class="hint" id="ghFreshHint">We’ll create an empty <strong>public</strong> repo — projects here are open by design. Want private code? Create the repo yourself on GitHub and “adopt” it instead — a private repository gets its own read-only deploy key automatically.</div>
|
|
1652
|
+
</div>
|
|
1653
|
+
<div class="row2" id="manualHome">
|
|
1654
|
+
<label class="wizField"><span>GitHub owner <small>(user or org)</small></span>
|
|
1655
|
+
<input type="text" id="f-owner" placeholder="acme" autocomplete="off" maxlength="60">
|
|
1656
|
+
</label>
|
|
1657
|
+
<label class="wizField"><span>Repository name</span>
|
|
1658
|
+
<input type="text" id="f-repo" placeholder="moonbase" autocomplete="off" maxlength="120">
|
|
1659
|
+
</label>
|
|
1660
|
+
<!-- visibility stated BEFORE GitHub's own radio is the first place it appears (task 1002721) -->
|
|
1661
|
+
<div class="hint" style="grid-column:1 / -1;">Creating the repo yourself on GitHub? Public or private is your choice there — both work here, and we never change it.</div>
|
|
1662
|
+
<!-- the way back from the manual fallback (task 1003683) -->
|
|
1663
|
+
<button type="button" class="linklike" id="ghManualBack" style="grid-column:1 / -1;" hidden>Use my GitHub repositories instead</button>
|
|
1664
|
+
</div>
|
|
1665
|
+
<div class="hint" id="h-home"> </div>
|
|
1666
|
+
<button type="button" class="btn small rowUse" data-row-use>Use this</button>
|
|
1576
1667
|
</div>
|
|
1577
|
-
<fieldset class="physExisting" id="physExisting" data-limit="existing_work" hidden>
|
|
1578
|
-
<legend>What already exists? Pick all that apply, then your main one.</legend>
|
|
1579
|
-
<label class="physCheck"><input type="checkbox" data-work="code"> <span>Code files</span> <span class="physMain"><input type="radio" name="physMain" value="code" aria-label="Code files is the main one"> main</span></label>
|
|
1580
|
-
<label class="physCheck"><input type="checkbox" data-work="digital-files"> <span>Digital files <small>Word documents, spreadsheets, other software.</small></span> <span class="physMain"><input type="radio" name="physMain" value="digital-files" aria-label="Digital files is the main one"> main</span></label>
|
|
1581
|
-
<label class="physCheck"><input type="checkbox" data-work="physical-work"> <span>Physical files or work <small>You’ll describe it in words, or upload a document, during genesis.</small></span> <span class="physMain"><input type="radio" name="physMain" value="physical-work" aria-label="Physical files or work is the main one"> main</span></label>
|
|
1582
|
-
</fieldset>
|
|
1583
|
-
<p class="physWhy" data-why="existing_work" hidden></p>
|
|
1584
|
-
<label class="wizField"><span>What is your project, and what do you want <span data-brand-product-inline>Cloud Bongos</span> to do for you?</span>
|
|
1585
|
-
<textarea id="physDesc" rows="3" maxlength="600" placeholder="A co-op farming game where the seasons never repeat."></textarea>
|
|
1586
|
-
</label>
|
|
1587
1668
|
</div>
|
|
1669
|
+
<span class="acts"><button type="button" class="rowOk" data-row-ok>Looks right</button><button type="button" class="rowChg" data-row-chg aria-expanded="false">Change</button></span>
|
|
1670
|
+
</div>
|
|
1588
1671
|
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
<
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
<
|
|
1672
|
+
<!-- the address (tasks 1002704, 1002898 N8, PS19): the assigned address is
|
|
1673
|
+
stated, and both alternatives sit under it. It is shown and confirmed on
|
|
1674
|
+
every path, the demo's too — nothing is created before it is chosen. -->
|
|
1675
|
+
<div class="setRow" data-row="address">
|
|
1676
|
+
<span class="k">Address</span>
|
|
1677
|
+
<div class="v"><span class="rowV" data-row-v></span><small class="rowS" data-row-s></small>
|
|
1678
|
+
<div class="opts" hidden>
|
|
1679
|
+
<label class="wizField"><span>Project handle</span>
|
|
1680
|
+
<input type="text" id="f-slug" placeholder="moonbase" autocomplete="off" maxlength="40" spellcheck="false">
|
|
1681
|
+
<div class="hint" id="h-slug"> </div>
|
|
1682
|
+
</label>
|
|
1683
|
+
<div class="addrConfirm" id="addrConfirm">
|
|
1684
|
+
<div class="addrBig" id="addrAssigned"></div>
|
|
1685
|
+
<p id="addrAssignedNote">Yours once it’s set up. You can add your own domain any time.</p>
|
|
1686
|
+
<details id="addrCustom">
|
|
1687
|
+
<summary>A domain I own</summary>
|
|
1688
|
+
<div class="inner">
|
|
1689
|
+
<label class="wizField"><span>Domain you own</span>
|
|
1690
|
+
<input type="text" id="f-domain" placeholder="moonbase.example.com" autocomplete="off" maxlength="253" spellcheck="false">
|
|
1691
|
+
<div class="hint" id="h-domain"> </div>
|
|
1692
|
+
</label>
|
|
1693
|
+
</div>
|
|
1694
|
+
</details>
|
|
1695
|
+
<label class="addrOpt"><input type="checkbox" id="f-noaddr">
|
|
1696
|
+
<span>No address yet. Sign-in can’t be switched on until it has one.</span>
|
|
1697
|
+
</label>
|
|
1602
1698
|
</div>
|
|
1699
|
+
<button type="button" class="btn small rowUse" data-row-use>Use this</button>
|
|
1603
1700
|
</div>
|
|
1604
1701
|
</div>
|
|
1702
|
+
<span class="acts"><button type="button" class="rowOk" data-row-ok>Looks right</button><button type="button" class="rowChg" data-row-chg aria-expanded="false">Change</button></span>
|
|
1703
|
+
</div>
|
|
1605
1704
|
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
<div class="
|
|
1613
|
-
<
|
|
1614
|
-
|
|
1615
|
-
<
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1705
|
+
<!-- where the app runs (tasks 1004351, 1004352; ADR 0345): the hall is ours to
|
|
1706
|
+
host, so this row is only the owner's APP. "Decide later" is the default.
|
|
1707
|
+
The Render key lives in a variable only: never the draft, a URL or a log. -->
|
|
1708
|
+
<div class="setRow" data-row="app">
|
|
1709
|
+
<span class="k">App hosting</span>
|
|
1710
|
+
<div class="v"><span class="rowV" data-row-v></span><small class="rowS" data-row-s></small>
|
|
1711
|
+
<div class="opts" hidden>
|
|
1712
|
+
<fieldset class="appOpts" id="appOpts">
|
|
1713
|
+
<legend>Where your app runs</legend>
|
|
1714
|
+
<label class="appOpt">
|
|
1715
|
+
<input type="radio" name="appPlatform" value="later" checked>
|
|
1716
|
+
<span><b>Decide later</b>
|
|
1717
|
+
<span>There’s no app to host until your builders write one.</span></span>
|
|
1718
|
+
</label>
|
|
1719
|
+
<label class="appOpt" id="appOptRender">
|
|
1720
|
+
<input type="radio" name="appPlatform" value="render" aria-describedby="appRenderWhat">
|
|
1721
|
+
<span><b>Render</b>
|
|
1722
|
+
<span id="appRenderWhat">We set it up on your own Render account. Render bills you directly.</span></span>
|
|
1723
|
+
</label>
|
|
1724
|
+
</fieldset>
|
|
1725
|
+
<p class="renderWhy" id="renderLater" role="status" hidden>Your new repo has no app yet, so there is nothing for Render to run. Once your builders write it, set it up from your project’s page. Nothing is created on Render now.</p>
|
|
1726
|
+
<div class="renderSetup" id="renderSetup" hidden>
|
|
1727
|
+
<p class="renderWhy">Your Render key is used once, to create your app on your own Render account, and then thrown away — it is not kept, not even in this browser. Render bills you directly. A key can act on your whole Render account, so only paste it here. <a href="https://dashboard.render.com/u/settings#api-keys" target="_blank" rel="noopener noreferrer">Create a key in Render</a></p>
|
|
1728
|
+
<div class="renderKeyRow">
|
|
1729
|
+
<label class="wizField"><span>Render API key</span>
|
|
1730
|
+
<input type="password" id="f-renderKey" autocomplete="off" spellcheck="false" maxlength="200" placeholder="rnd_…" aria-describedby="h-renderKey">
|
|
1731
|
+
</label>
|
|
1732
|
+
<button type="button" class="btn ghost small" id="renderCheck">Check key</button>
|
|
1733
|
+
</div>
|
|
1734
|
+
<div class="hint" id="h-renderKey" role="status"> </div>
|
|
1735
|
+
<fieldset class="appOpts" id="renderWs" hidden>
|
|
1736
|
+
<legend>Which Render workspace</legend>
|
|
1737
|
+
<div id="renderWsList"></div>
|
|
1738
|
+
</fieldset>
|
|
1739
|
+
<fieldset class="appOpts" id="renderSvc" hidden>
|
|
1740
|
+
<legend>A new app, or one you already run</legend>
|
|
1741
|
+
<div id="renderSvcList"></div>
|
|
1742
|
+
</fieldset>
|
|
1743
|
+
<div class="renderNew" id="renderNew" hidden>
|
|
1744
|
+
<label class="wizField"><span>Your app is built with</span>
|
|
1745
|
+
<select id="f-renderRuntime">
|
|
1746
|
+
<option value="node">Node.js</option><option value="python">Python</option><option value="ruby">Ruby</option>
|
|
1747
|
+
<option value="go">Go</option><option value="rust">Rust</option><option value="elixir">Elixir</option>
|
|
1748
|
+
<option value="docker">Docker — use my repository’s Dockerfile</option>
|
|
1749
|
+
</select>
|
|
1750
|
+
</label>
|
|
1751
|
+
<label class="wizField" id="renderBuildWrap"><span>Build command <small>— Render needs one; the usual one is filled in</small></span>
|
|
1752
|
+
<input type="text" id="f-renderBuild" autocomplete="off" spellcheck="false" maxlength="500" placeholder="for example: npm ci">
|
|
1753
|
+
</label>
|
|
1754
|
+
<label class="wizField" id="renderStartWrap"><span>Start command</span>
|
|
1755
|
+
<input type="text" id="f-renderStart" autocomplete="off" spellcheck="false" maxlength="500" placeholder="for example: npm start">
|
|
1756
|
+
</label>
|
|
1757
|
+
<label class="wizField"><span>Plan</span>
|
|
1758
|
+
<select id="f-renderPlan"></select>
|
|
1759
|
+
</label>
|
|
1760
|
+
<label class="addrOpt"><input type="checkbox" id="f-renderDb">
|
|
1761
|
+
<span>My app needs a database (Render Postgres)</span>
|
|
1762
|
+
</label>
|
|
1763
|
+
<label class="wizField" id="renderDbPlanWrap" hidden><span>Database plan</span>
|
|
1764
|
+
<select id="f-renderDbPlan"></select>
|
|
1765
|
+
</label>
|
|
1766
|
+
</div>
|
|
1767
|
+
<div class="hint" id="h-render" role="status"> </div>
|
|
1768
|
+
</div>
|
|
1769
|
+
<button type="button" class="btn small rowUse" data-row-use>Use this</button>
|
|
1620
1770
|
</div>
|
|
1621
1771
|
</div>
|
|
1772
|
+
<span class="acts"><button type="button" class="rowOk" data-row-ok>Looks right</button><button type="button" class="rowChg" data-row-chg aria-expanded="false">Change</button></span>
|
|
1773
|
+
</div>
|
|
1622
1774
|
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
<div id="physForms" hidden>
|
|
1636
|
-
<p class="lede">Pick the legal entity behind your project. The laws that apply, and the ways you can reward builders, depend on it.</p>
|
|
1637
|
-
<p class="physGrp">Organization</p>
|
|
1638
|
-
<div class="typeCards physCells" role="group" aria-label="Organization">
|
|
1639
|
-
<button type="button" class="tpill" data-phys="entity" data-val="nonprofit" aria-pressed="false">Nonprofit</button>
|
|
1640
|
-
<button type="button" class="tpill" data-phys="entity" data-val="for-profit" aria-pressed="false">For-profit company</button>
|
|
1641
|
-
<button type="button" class="tpill" data-phys="entity" data-val="corporate-division" aria-pressed="false">Division of a corporation</button>
|
|
1642
|
-
<button type="button" class="tpill" data-phys="entity" data-val="research" aria-pressed="false">Research entity</button>
|
|
1643
|
-
</div>
|
|
1644
|
-
<p class="physGrp">Group</p>
|
|
1645
|
-
<div class="typeCards physCells" role="group" aria-label="Group">
|
|
1646
|
-
<button type="button" class="tpill" data-phys="entity" data-val="university-project" aria-pressed="false">University project</button>
|
|
1647
|
-
<button type="button" class="tpill" data-phys="entity" data-val="social-group" aria-pressed="false">Social group</button>
|
|
1775
|
+
<!-- the extras (tasks 1002339, 1002340): the type's suggested set until the
|
|
1776
|
+
owner says otherwise; the server resolves the same bundle from the same
|
|
1777
|
+
declaration (ADR 0237), so accepting and changing nothing are one answer.
|
|
1778
|
+
The always-on core is stated as a clause, never as dead toggles. -->
|
|
1779
|
+
<div class="setRow" data-row="extras">
|
|
1780
|
+
<span class="k">Extras</span>
|
|
1781
|
+
<div class="v"><span class="rowV" data-row-v></span><small class="rowS" data-row-s></small>
|
|
1782
|
+
<div class="opts" hidden>
|
|
1783
|
+
<div class="wizField"><span id="modLbl">Extras for this project</span>
|
|
1784
|
+
<input type="text" id="modQ" placeholder="Search extras…" autocomplete="off" aria-label="Search the extras by name" aria-describedby="modFilterNote">
|
|
1785
|
+
<div class="hint" id="modFilterNote" aria-live="polite"></div>
|
|
1786
|
+
<div class="typeCards" id="modCards" role="group" aria-labelledby="modLbl"></div>
|
|
1648
1787
|
</div>
|
|
1649
|
-
<
|
|
1650
|
-
<div class="
|
|
1651
|
-
|
|
1652
|
-
<button type="button" class="tpill" data-phys="entity" data-val="private-individual" aria-pressed="false">Private individual</button>
|
|
1653
|
-
</div>
|
|
1654
|
-
<button type="button" class="physLink" id="physNoEntity">We don’t have one yet</button>
|
|
1788
|
+
<div class="modWhy" id="modWhy" aria-live="polite"></div>
|
|
1789
|
+
<div class="reassure" id="modCoreNote"></div>
|
|
1790
|
+
<button type="button" class="btn small rowUse" data-row-use>Use this</button>
|
|
1655
1791
|
</div>
|
|
1656
1792
|
</div>
|
|
1793
|
+
<span class="acts"><button type="button" class="rowOk" data-row-ok>Looks right</button><button type="button" class="rowChg" data-row-chg aria-expanded="false">Change</button></span>
|
|
1794
|
+
</div>
|
|
1657
1795
|
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
<div class="
|
|
1665
|
-
<
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
<div id="physTeam" hidden>
|
|
1669
|
-
<div class="wizField"><span id="physStartLbl"><span data-voice="planet">Builders at the start, you included</span><span data-voice="pro" hidden>Contributors at the start</span></span>
|
|
1670
|
-
<div class="demoStep" role="group" aria-labelledby="physStartLbl">
|
|
1671
|
-
<button type="button" class="btn small ghost" data-phys-count="-1" aria-label="One fewer">−</button>
|
|
1672
|
-
<output id="physStartN" aria-live="polite">2</output>
|
|
1673
|
-
<button type="button" class="btn small ghost" data-phys-count="1" aria-label="One more">+</button>
|
|
1674
|
-
</div>
|
|
1796
|
+
<!-- team size (question 5, task 1004419): one person, or a team with a count
|
|
1797
|
+
and the month-three guess the founder corrects; who can join a team is the
|
|
1798
|
+
team shape the extras' preselect reads (task 1002341) -->
|
|
1799
|
+
<div class="setRow" data-row="team">
|
|
1800
|
+
<span class="k">Team size</span>
|
|
1801
|
+
<div class="v"><span class="rowV" data-row-v></span><small class="rowS" data-row-s></small>
|
|
1802
|
+
<div class="opts" hidden>
|
|
1803
|
+
<div class="typeCards physCells" role="group" aria-label="One or many">
|
|
1804
|
+
<button type="button" class="tpill" data-phys-solo="one" aria-pressed="false">One person</button>
|
|
1805
|
+
<button type="button" class="tpill" data-phys-solo="many" aria-pressed="false">A team</button>
|
|
1675
1806
|
</div>
|
|
1676
|
-
<div
|
|
1677
|
-
<
|
|
1807
|
+
<div id="physTeam" hidden>
|
|
1808
|
+
<div class="wizField"><span id="physStartLbl">People at the start, you included</span>
|
|
1809
|
+
<div class="demoStep" role="group" aria-labelledby="physStartLbl">
|
|
1810
|
+
<button type="button" class="btn small ghost" data-phys-count="-1" aria-label="One fewer">−</button>
|
|
1811
|
+
<output id="physStartN" aria-live="polite">2</output>
|
|
1812
|
+
<button type="button" class="btn small ghost" data-phys-count="1" aria-label="One more">+</button>
|
|
1813
|
+
</div>
|
|
1814
|
+
</div>
|
|
1678
1815
|
<p class="physGuessLine" id="physGuessLine" aria-live="polite"></p>
|
|
1679
|
-
<dl class="physMonths">
|
|
1680
|
-
<dt>Month 1</dt><dd id="physM1">2</dd>
|
|
1681
|
-
<dt>Month 2</dt><dd id="physM2">4</dd>
|
|
1682
|
-
<dt>Month 3</dt><dd id="physM3">5</dd>
|
|
1683
|
-
</dl>
|
|
1684
1816
|
<label class="wizField physAdjust"><span>Adjust month three</span>
|
|
1685
1817
|
<input type="range" id="physExpected" min="2" max="30" step="1" value="5">
|
|
1686
1818
|
</label>
|
|
1819
|
+
<div class="wizField"><span id="teamLbl">Who can join?</span>
|
|
1820
|
+
<div class="typeCards" id="teamCards" role="group" aria-labelledby="teamLbl">
|
|
1821
|
+
<button type="button" class="tpill" data-team="small-team" aria-pressed="false">People I invite</button>
|
|
1822
|
+
<button type="button" class="tpill" data-team="community" aria-pressed="false">Anyone can apply</button>
|
|
1823
|
+
</div>
|
|
1824
|
+
</div>
|
|
1687
1825
|
</div>
|
|
1826
|
+
<button type="button" class="btn small rowUse" data-row-use>Use this</button>
|
|
1688
1827
|
</div>
|
|
1689
1828
|
</div>
|
|
1829
|
+
<span class="acts"><button type="button" class="rowOk" data-row-ok>Looks right</button><button type="button" class="rowChg" data-row-chg aria-expanded="false">Change</button></span>
|
|
1830
|
+
</div>
|
|
1690
1831
|
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
<
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
<div class="physQ" data-q="7" hidden>
|
|
1704
|
-
<h1 id="phys-h7">How much should <em><span data-brand-product-inline>Cloud Bongos</span> run for you?</em></h1>
|
|
1705
|
-
<p class="lede">Not binding. It just speeds up your setup.</p>
|
|
1706
|
-
<div class="typeCards physCells physOpts" role="group" aria-label="How much is run for you">
|
|
1707
|
-
<button type="button" class="tpill" data-phys="framing" data-val="managed" aria-pressed="false"><span>A · All managed <small>The full suite, run for you.</small></span></button>
|
|
1708
|
-
<button type="button" class="tpill" data-phys="framing" data-val="hybrid" aria-pressed="false"><span>B · Hybrid <small>Choose what you want from the full suite.</small></span></button>
|
|
1709
|
-
<button type="button" class="tpill" data-phys="framing" data-val="local" aria-pressed="false"><span>C · Local or internal <small>Choose from the suite, and keep your data on your own machines.</small></span></button>
|
|
1710
|
-
<button type="button" class="tpill" data-phys="framing" data-val="open-source" aria-pressed="false"><span>D · Open source, local <small>Just the free core. Build on your own machines and pay only for what you add.</small></span></button>
|
|
1711
|
-
</div>
|
|
1712
|
-
<p class="physWhy" data-why="services" hidden></p>
|
|
1713
|
-
<fieldset class="physSuite" id="physSuite" hidden>
|
|
1714
|
-
<legend id="physSuiteLbl">Your hybrid suite</legend>
|
|
1715
|
-
<div class="typeCards physCells physSuiteGrid">
|
|
1716
|
-
<button type="button" class="tpill" data-phys="services" data-val="hosting" aria-pressed="false">Hosting</button>
|
|
1717
|
-
<button type="button" class="tpill" data-phys="services" data-val="labor-payment" aria-pressed="false">Labor payment and HR</button>
|
|
1718
|
-
<button type="button" class="tpill" data-phys="services" data-val="starter-kit" aria-pressed="false">Starting module kit</button>
|
|
1719
|
-
<button type="button" class="tpill" data-phys="services" data-val="marketplace-buy" aria-pressed="false">Module marketplace: buy</button>
|
|
1720
|
-
<button type="button" class="tpill" data-phys="services" data-val="marketplace-sell" aria-pressed="false">Module marketplace: sell</button>
|
|
1721
|
-
<button type="button" class="tpill" data-phys="services" data-val="guild-access" aria-pressed="false">Builder guild access</button>
|
|
1722
|
-
<button type="button" class="tpill" data-phys="services" data-val="guild-create" aria-pressed="false">Create a builder guild</button>
|
|
1723
|
-
<button type="button" class="tpill" data-phys="services" data-val="company-creation" aria-pressed="false">Company creation</button>
|
|
1724
|
-
<button type="button" class="tpill" data-phys="services" data-val="cbaas-consulting" aria-pressed="false">Expert consulting</button>
|
|
1725
|
-
<button type="button" class="tpill" data-phys="services" data-val="public-marketing" aria-pressed="false">Public marketed project</button>
|
|
1832
|
+
<!-- rewards (question 6): intent only, never asked of a founder building
|
|
1833
|
+
alone (the table hides it, and this row goes with it) -->
|
|
1834
|
+
<div class="setRow" data-row="rewards">
|
|
1835
|
+
<span class="k">Rewards</span>
|
|
1836
|
+
<div class="v"><span class="rowV" data-row-v></span><small class="rowS" data-row-s></small>
|
|
1837
|
+
<div class="opts" hidden>
|
|
1838
|
+
<div class="typeCards physCells" role="group" aria-label="Rewards being considered">
|
|
1839
|
+
<button type="button" class="tpill" data-phys="reward_intents" data-val="non-monetary" aria-pressed="false">Learning and fun</button>
|
|
1840
|
+
<button type="button" class="tpill" data-phys="reward_intents" data-val="cash" aria-pressed="false">Cash for work</button>
|
|
1841
|
+
<button type="button" class="tpill" data-phys="reward_intents" data-val="equity" aria-pressed="false">Equity</button>
|
|
1842
|
+
<button type="button" class="tpill" data-phys="reward_intents" data-val="future-payouts" aria-pressed="false">Future payouts</button>
|
|
1726
1843
|
</div>
|
|
1727
|
-
<ul class="physWhyList" data-why-list="
|
|
1728
|
-
|
|
1729
|
-
</div>
|
|
1730
|
-
|
|
1731
|
-
<div class="physQ" data-q="sum" hidden>
|
|
1732
|
-
<h1 id="phys-hsum"><span data-voice="planet">Your planet’s <em>physics</em></span><span data-voice="pro" hidden>Your project’s <em>profile</em></span></h1>
|
|
1733
|
-
<p class="lede"><span data-voice="planet">This is what shapes your planet. Change any answer before it’s born.</span><span data-voice="pro" hidden>This is what shapes your project. Change any answer before it is created.</span></p>
|
|
1734
|
-
<dl class="demoLedger physLedger" id="physSummary"></dl>
|
|
1735
|
-
</div>
|
|
1736
|
-
|
|
1737
|
-
<div class="physQ" data-q="stop" hidden>
|
|
1738
|
-
<h1 id="phys-hstop">This can’t be built <em>on <span data-brand-product-inline>Cloud Bongos</span></em></h1>
|
|
1739
|
-
<p class="lede" id="physStopMsg"></p>
|
|
1740
|
-
<div class="nav">
|
|
1741
|
-
<button type="button" class="btn ghost" id="physStartOver">Start over</button>
|
|
1742
|
-
<a class="btn" href="/contact">Contact us</a>
|
|
1844
|
+
<ul class="physWhyList" data-why-list="reward_intents" hidden></ul>
|
|
1845
|
+
<button type="button" class="btn small rowUse" data-row-use>Use this</button>
|
|
1743
1846
|
</div>
|
|
1744
1847
|
</div>
|
|
1745
|
-
|
|
1746
|
-
<div class="hint" id="h-phys"> </div>
|
|
1747
|
-
<div class="nav" id="physNav">
|
|
1748
|
-
<button type="button" class="btn ghost" id="physBack">← Back</button>
|
|
1749
|
-
<button type="submit" class="btn" id="physNext">Continue →</button>
|
|
1750
|
-
</div>
|
|
1751
|
-
</form>
|
|
1752
|
-
</div>
|
|
1753
|
-
<div class="forkStage physStage" id="physStage" aria-hidden="true">
|
|
1754
|
-
<span class="forkOrbit"></span>
|
|
1755
|
-
<span class="forkOrbit forkOrbitDash"></span>
|
|
1756
|
-
<!-- task 1004419: rings for the rewards being considered, moons for the
|
|
1757
|
-
builders (the faint ones on the dashed orbit are the expected growth), and
|
|
1758
|
-
the planet's orbit around the platform's mark, set by the framing -->
|
|
1759
|
-
<span class="physBelt" data-ring="1" hidden></span><span class="physBelt" data-ring="2" hidden></span><span class="physBelt" data-ring="3" hidden></span><span class="physBelt" data-ring="4" hidden></span>
|
|
1760
|
-
<span class="physGhosts" id="physGhosts" hidden><img src="/assets/world/r3-frost-bead-cut.webp" alt=""><img src="/assets/world/r3-frost-bead-cut.webp" alt=""><img src="/assets/world/r3-frost-bead-cut.webp" alt=""><img src="/assets/world/r3-frost-bead-cut.webp" alt=""></span>
|
|
1761
|
-
<span class="physMoons" id="physMoons" hidden><img src="/assets/world/r3-frost-bead-cut.webp" alt=""><img src="/assets/world/r3-frost-bead-cut.webp" alt=""><img src="/assets/world/r3-frost-bead-cut.webp" alt=""><img src="/assets/world/r3-frost-bead-cut.webp" alt=""><img src="/assets/world/r3-frost-bead-cut.webp" alt=""><img src="/assets/world/r3-frost-bead-cut.webp" alt=""></span>
|
|
1762
|
-
<span class="physTether" id="physTether" hidden></span>
|
|
1763
|
-
<span class="physGuard" id="physGuard" hidden></span>
|
|
1764
|
-
<img class="forkSeed physPlanet" id="physPlanet" src="/assets/world/r2-chrome-blob-one-cut.webp" alt="">
|
|
1765
|
-
<img class="physSun" id="physSun" src="/assets/world/r2-bongos-chrome-a-cut.webp" alt="" hidden>
|
|
1766
|
-
<p class="physCap"><span id="physCapLbl">planet physics</span> · <span id="physCapN">0 of 7</span></p>
|
|
1767
|
-
<div class="physFoot"><p class="physCallout" id="physCallout">physics · the rules it lives by · each answer shapes your planet here</p>
|
|
1768
|
-
<p class="physCoords" id="physCoords"></p></div>
|
|
1769
|
-
</div>
|
|
1770
|
-
</div>
|
|
1771
|
-
</section>
|
|
1772
|
-
|
|
1773
|
-
<!-- step 4 · name (the hub's original step-1 form, now the wizard's) -->
|
|
1774
|
-
<section class="panel" id="panel-name" aria-labelledby="name-h">
|
|
1775
|
-
<h1 id="name-h">Start a project</h1>
|
|
1776
|
-
<p class="lede">Your own project: its own repository, its own home on the web, and a hall of AI builders
|
|
1777
|
-
who do the building. You set the direction; agents lay down the code.</p>
|
|
1778
|
-
<form id="newForm" data-next="5">
|
|
1779
|
-
<label class="wizField"><span>Project name</span>
|
|
1780
|
-
<input type="text" id="newName" placeholder="What should it be called?" autocomplete="off" maxlength="80" aria-label="Project name">
|
|
1781
|
-
<div class="hint" id="h-name"> </div>
|
|
1782
|
-
</label>
|
|
1783
|
-
<div class="nav">
|
|
1784
|
-
<span></span>
|
|
1785
|
-
<button type="submit" class="btn">Continue →</button>
|
|
1786
|
-
</div>
|
|
1787
|
-
</form>
|
|
1788
|
-
<p class="finePrint">Free while we find our feet. Your project’s repo and server are yours —
|
|
1789
|
-
<span data-brand-product-inline>Cloud Bongos</span> is <a href="https://www.npmjs.com/package/@bongos/core" target="_blank" rel="noopener">open source</a>
|
|
1790
|
-
(<a href="https://www.gnu.org/licenses/agpl-3.0.html" target="_blank" rel="noopener">AGPL-3.0</a>)
|
|
1791
|
-
and capture-resistant by design.</p>
|
|
1792
|
-
</section>
|
|
1793
|
-
|
|
1794
|
-
<!-- step 2 · type (task 1002772) — a DECLARATION, closed vocabulary
|
|
1795
|
-
(owner decision 2026-08-13): Game / Research / Business /
|
|
1796
|
-
Non-Profit / Not Sure, selectable buttons with a themed
|
|
1797
|
-
description card. Not Sure is the escape hatch — nobody is
|
|
1798
|
-
forced into a wrong answer, and it can change later. -->
|
|
1799
|
-
<section class="panel" id="panel-type" aria-labelledby="type-h">
|
|
1800
|
-
<h1 id="type-h">What kind of project is it?</h1>
|
|
1801
|
-
<p class="lede">Pick the shape that fits best — it’s how the project is listed on the map, and you can change it later from the project’s own page. Not sure is a fine answer.</p>
|
|
1802
|
-
<form data-next="6">
|
|
1803
|
-
<div class="typeCards" id="typeCards" role="group" aria-label="Project type">
|
|
1804
|
-
<button type="button" class="tpill" data-type="game" aria-pressed="false">Game</button>
|
|
1805
|
-
<button type="button" class="tpill" data-type="research" aria-pressed="false">Research</button>
|
|
1806
|
-
<button type="button" class="tpill" data-type="business" aria-pressed="false">Business</button>
|
|
1807
|
-
<button type="button" class="tpill" data-type="non-profit" aria-pressed="false">Non-Profit</button>
|
|
1808
|
-
<button type="button" class="tpill" data-type="not-sure" aria-pressed="false">Not Sure</button>
|
|
1809
|
-
</div>
|
|
1810
|
-
<div class="typeCard" id="typeCard" hidden aria-live="polite">
|
|
1811
|
-
<b id="typeCardName"></b>
|
|
1812
|
-
<p id="typeCardDesc"></p>
|
|
1813
|
-
</div>
|
|
1814
|
-
<!-- pre-filled from the planet-physics answers (task 1004419), and says so -->
|
|
1815
|
-
<p class="physWhy" id="typePrefill" hidden></p>
|
|
1816
|
-
<div class="hint" id="h-projtype"> </div>
|
|
1817
|
-
<div class="nav">
|
|
1818
|
-
<button type="button" class="btn ghost" data-back="4">← Back</button>
|
|
1819
|
-
<button type="submit" class="btn">Next →</button>
|
|
1820
|
-
</div>
|
|
1821
|
-
</form>
|
|
1822
|
-
</section>
|
|
1823
|
-
|
|
1824
|
-
<!-- step 3 · about (task 1002334) — the OPTIONAL project-detail step.
|
|
1825
|
-
The one panel in the wizard that can be walked past without an
|
|
1826
|
-
answer, so the skip is a real button of its own, sitting beside
|
|
1827
|
-
Next rather than hidden as "leave it blank and press Next" — an
|
|
1828
|
-
optional step whose escape hatch has to be inferred is not
|
|
1829
|
-
optional in practice. What skipping costs is stated ON the panel
|
|
1830
|
-
(ADR 0182 D4: no description ⇒ the project is off the public map
|
|
1831
|
-
until it has one), because a consequence discovered afterwards is
|
|
1832
|
-
not a choice the owner made. -->
|
|
1833
|
-
<section class="panel" id="panel-detail" aria-labelledby="detail-h">
|
|
1834
|
-
<h1 id="detail-h">Tell us about it</h1>
|
|
1835
|
-
<p class="lede">Optional — skip it and nothing about your project changes. Answering shapes what we set up for you, and a description is what puts the project on the public map.</p>
|
|
1836
|
-
<form data-next="7">
|
|
1837
|
-
<label class="wizField"><span>What are you building? <small>(a sentence or two)</small></span>
|
|
1838
|
-
<textarea id="f-desc" rows="3" maxlength="600" placeholder="A co-op farming game where the seasons never repeat."></textarea>
|
|
1839
|
-
</label>
|
|
1840
|
-
<div class="wizField"><span id="teamLbl">Who’s building it?</span>
|
|
1841
|
-
<div class="typeCards" id="teamCards" role="group" aria-labelledby="teamLbl">
|
|
1842
|
-
<button type="button" class="tpill" data-team="solo" aria-pressed="false">Just me</button>
|
|
1843
|
-
<button type="button" class="tpill" data-team="small-team" aria-pressed="false">A small team</button>
|
|
1844
|
-
<button type="button" class="tpill" data-team="community" aria-pressed="false">An open community</button>
|
|
1848
|
+
<span class="acts"><button type="button" class="rowOk" data-row-ok>Looks right</button><button type="button" class="rowChg" data-row-chg aria-expanded="false">Change</button></span>
|
|
1845
1849
|
</div>
|
|
1846
|
-
</div>
|
|
1847
|
-
<div class="reassure" id="detailSkipNote"></div>
|
|
1848
|
-
<div class="nav">
|
|
1849
|
-
<button type="button" class="btn ghost" data-back="5">← Back</button>
|
|
1850
|
-
<span class="navPair">
|
|
1851
|
-
<button type="button" class="btn ghost" id="skipDetail">Skip for now</button>
|
|
1852
|
-
<button type="submit" class="btn">Next →</button>
|
|
1853
|
-
</span>
|
|
1854
|
-
</div>
|
|
1855
|
-
</form>
|
|
1856
|
-
</section>
|
|
1857
|
-
|
|
1858
|
-
<!-- step 4 · modules (task 1002339, BV1.R20) — the picker. Skippable
|
|
1859
|
-
like About, and for the same reason: an owner who has met none of
|
|
1860
|
-
these modules should not have to audit a list to get a project.
|
|
1861
|
-
What Skip means here is spelled out on the panel and is TRUE by
|
|
1862
|
-
construction — the request omits the field, and the server resolves
|
|
1863
|
-
the same starter bundle this panel preselected (ADR 0237), so the
|
|
1864
|
-
set is the one they were shown either way.
|
|
1865
|
-
|
|
1866
|
-
The always-on core is stated as a clause, never as a row of dead
|
|
1867
|
-
toggles: sixteen disabled controls read as choices the owner failed
|
|
1868
|
-
to make, when in fact they were never choices. -->
|
|
1869
|
-
<section class="panel" id="panel-modules" aria-labelledby="mod-h">
|
|
1870
|
-
<h1 id="mod-h">What it comes with</h1>
|
|
1871
|
-
<p class="lede">Optional — skip it and you get the suggested set below, shaped by what you just told us. These are extras on top of everything every project already has, and you can switch any of them on or off later.</p>
|
|
1872
|
-
<form data-next="8">
|
|
1873
|
-
<div class="wizField"><span id="modLbl">Extras for this project</span>
|
|
1874
|
-
<!-- the catalog search (task 1002340, BV1.R21). The wizard's own
|
|
1875
|
-
search idiom (#ghSearch): a plain field on one hairline, no
|
|
1876
|
-
new class and no icon. It filters the band below and NOTHING
|
|
1877
|
-
else — what is switched on is unaffected by what is on
|
|
1878
|
-
screen, and the line under it counts the ones it is hiding
|
|
1879
|
-
(ADR 0245). -->
|
|
1880
|
-
<input type="text" id="modQ" placeholder="Search extras…" autocomplete="off" aria-label="Search the extras by name" aria-describedby="modFilterNote">
|
|
1881
|
-
<div class="hint" id="modFilterNote" aria-live="polite"></div>
|
|
1882
|
-
<div class="typeCards" id="modCards" role="group" aria-labelledby="modLbl"></div>
|
|
1883
|
-
</div>
|
|
1884
|
-
<div class="modWhy" id="modWhy" aria-live="polite"></div>
|
|
1885
|
-
<div class="reassure" id="modCoreNote"></div>
|
|
1886
|
-
<div class="hint" id="h-modules"> </div>
|
|
1887
|
-
<div class="nav">
|
|
1888
|
-
<button type="button" class="btn ghost" data-back="6">← Back</button>
|
|
1889
|
-
<span class="navPair">
|
|
1890
|
-
<button type="button" class="btn ghost" id="skipModules">Skip for now</button>
|
|
1891
|
-
<button type="submit" class="btn">Next →</button>
|
|
1892
|
-
</span>
|
|
1893
|
-
</div>
|
|
1894
|
-
</form>
|
|
1895
|
-
</section>
|
|
1896
1850
|
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
<button type="button" class="btn" id="ghConnectBtn">Allow repo access</button>
|
|
1928
|
-
<button type="button" class="linklike" id="ghManualToggle">or enter it manually</button>
|
|
1929
|
-
</div>
|
|
1930
|
-
<div id="ghBusy" class="hint" hidden>Reading your GitHub repositories…</div>
|
|
1931
|
-
<div id="ghAdopt" class="wizField" hidden>
|
|
1932
|
-
<span>Pick one of your repositories</span>
|
|
1933
|
-
<input type="text" id="ghSearch" placeholder="Search your repos…" autocomplete="off" aria-label="Search your repositories">
|
|
1934
|
-
<div id="ghList" class="ghlist" role="group" aria-label="Your repositories"></div>
|
|
1935
|
-
<div class="hint" id="ghAdoptHint"> </div>
|
|
1936
|
-
<!-- visibility stated on EVERY repo path (task 1002721) — the picker
|
|
1937
|
-
marks private repos, and this says what adoption does about it -->
|
|
1938
|
-
<div class="hint">We never change a repo’s visibility — public stays public, private stays private.</div>
|
|
1939
|
-
</div>
|
|
1940
|
-
<div id="ghFresh" class="wizField" hidden>
|
|
1941
|
-
<span>New repository name</span>
|
|
1942
|
-
<input type="text" id="ghNewName" placeholder="mercury" autocomplete="off" maxlength="100" aria-label="New repository name">
|
|
1943
|
-
<!-- visibility is stated on EVERY repo path (task 1002721) — this baked
|
|
1944
|
-
line is the floor; updateFreshPreview() replaces it with the same
|
|
1945
|
-
claim plus the resolved repo URL once a name is typed -->
|
|
1946
|
-
<div class="hint" id="ghFreshHint">We’ll create an empty <strong>public</strong> repo — projects here are open by design. Want private code? Create the repo yourself on GitHub and “adopt” it instead — a private repository gets its own read-only deploy key automatically.</div>
|
|
1947
|
-
</div>
|
|
1948
|
-
|
|
1949
|
-
<div class="row2" id="manualHome">
|
|
1950
|
-
<label class="wizField"><span>GitHub owner <small>(user or org)</small></span>
|
|
1951
|
-
<input type="text" id="f-owner" placeholder="acme" autocomplete="off" maxlength="60">
|
|
1952
|
-
</label>
|
|
1953
|
-
<label class="wizField"><span>Repository name</span>
|
|
1954
|
-
<input type="text" id="f-repo" placeholder="mercury" autocomplete="off" maxlength="120">
|
|
1955
|
-
</label>
|
|
1956
|
-
<!-- visibility stated BEFORE GitHub's own radio is the first place it
|
|
1957
|
-
appears (task 1002721, owner firsthand in the QA walk) -->
|
|
1958
|
-
<div class="hint" style="grid-column:1 / -1;">Creating the repo yourself on GitHub? Public or private is your choice there — both work here, and we never change it.</div>
|
|
1959
|
-
<!-- the way back from the manual fallback (task 1003683). Choosing "or enter
|
|
1960
|
-
it manually" used to hide the connect offer for good: #ghConnect renders
|
|
1961
|
-
only while repoAccess is 'need-connect', and nothing ever set it back, so
|
|
1962
|
-
a full reload was the only route to the button. Stays hidden when the
|
|
1963
|
-
fallback was FORCED (repoAccess 'unavailable'), where connecting cannot
|
|
1964
|
-
help and the offer would be a lie. -->
|
|
1965
|
-
<button type="button" class="linklike" id="ghManualBack" style="grid-column:1 / -1;" hidden>Use my GitHub repositories instead</button>
|
|
1966
|
-
</div>
|
|
1967
|
-
<div class="hint" id="h-home"> </div>
|
|
1968
|
-
<div class="nav">
|
|
1969
|
-
<button type="button" class="btn ghost" data-back="7">← Back</button>
|
|
1970
|
-
<button type="submit" class="btn">Next →</button>
|
|
1971
|
-
</div>
|
|
1972
|
-
</form>
|
|
1973
|
-
</section>
|
|
1974
|
-
|
|
1975
|
-
<!-- step 6 · address — a CONFIRMATION, not a form (task 1002704: every project
|
|
1976
|
-
gets its slug subdomain assigned up front). The domainless default was a
|
|
1977
|
-
proven dead-end (QA walk, task 1002702).
|
|
1978
|
-
Three choices, all legible without opening anything (task 1002898 N8):
|
|
1979
|
-
the assigned address is stated, and BOTH alternatives sit under it — the
|
|
1980
|
-
bring-your-own domain behind its disclosure, the no-address opt-out in the
|
|
1981
|
-
open. The opt-out used to live INSIDE the "domain I own" fold, i.e. the
|
|
1982
|
-
choice for NO address was hidden under a fold labelled the opposite
|
|
1983
|
-
choice; the owner read the step as "weird" and never found it. -->
|
|
1984
|
-
<section class="panel" id="panel-address" aria-labelledby="addr-h">
|
|
1985
|
-
<h1 id="addr-h">Its address in the sky</h1>
|
|
1986
|
-
<p class="lede">The handle names your project on the platform — and its address comes with it. Once it stands up, a few GitHub steps (the next screen walks you through them) switch sign-in on.</p>
|
|
1987
|
-
<form data-next="10">
|
|
1988
|
-
<label class="wizField"><span>Project handle</span>
|
|
1989
|
-
<input type="text" id="f-slug" placeholder="mercury" autocomplete="off" maxlength="40" spellcheck="false">
|
|
1990
|
-
<div class="hint" id="h-slug"> </div>
|
|
1991
|
-
</label>
|
|
1992
|
-
<div class="addrConfirm" id="addrConfirm">
|
|
1993
|
-
<div class="addrBig" id="addrAssigned"></div>
|
|
1994
|
-
<p id="addrAssignedNote">Yours from the moment it stands up. You can point a domain you own at it any time.</p>
|
|
1995
|
-
<details id="addrCustom">
|
|
1996
|
-
<summary>Use a domain I own instead</summary>
|
|
1997
|
-
<div class="inner">
|
|
1998
|
-
<label class="wizField"><span>Domain you own</span>
|
|
1999
|
-
<input type="text" id="f-domain" placeholder="mercury.example.com" autocomplete="off" maxlength="253" spellcheck="false">
|
|
2000
|
-
<div class="hint" id="h-domain"> </div>
|
|
2001
|
-
</label>
|
|
1851
|
+
<!-- how much we run (question 7): a hybrid or local framing opens its suite -->
|
|
1852
|
+
<div class="setRow" data-row="run">
|
|
1853
|
+
<span class="k">We run</span>
|
|
1854
|
+
<div class="v"><span class="rowV" data-row-v></span><small class="rowS" data-row-s></small>
|
|
1855
|
+
<div class="opts" hidden>
|
|
1856
|
+
<div class="typeCards physCells" role="group" aria-label="How much is run for you">
|
|
1857
|
+
<button type="button" class="tpill" data-phys="framing" data-val="managed" aria-pressed="false">Everything, for you</button>
|
|
1858
|
+
<button type="button" class="tpill" data-phys="framing" data-val="hybrid" aria-pressed="false">Some of it</button>
|
|
1859
|
+
<button type="button" class="tpill" data-phys="framing" data-val="local" aria-pressed="false">On my machines</button>
|
|
1860
|
+
<button type="button" class="tpill" data-phys="framing" data-val="open-source" aria-pressed="false">Free core only</button>
|
|
1861
|
+
</div>
|
|
1862
|
+
<p class="physWhy" data-why="services" hidden></p>
|
|
1863
|
+
<fieldset class="physSuite" id="physSuite" hidden>
|
|
1864
|
+
<legend id="physSuiteLbl">Your hybrid suite</legend>
|
|
1865
|
+
<div class="typeCards physCells physSuiteGrid">
|
|
1866
|
+
<button type="button" class="tpill" data-phys="services" data-val="hosting" aria-pressed="false">Hosting</button>
|
|
1867
|
+
<button type="button" class="tpill" data-phys="services" data-val="labor-payment" aria-pressed="false">Labor payment and HR</button>
|
|
1868
|
+
<button type="button" class="tpill" data-phys="services" data-val="starter-kit" aria-pressed="false">Starting module kit</button>
|
|
1869
|
+
<button type="button" class="tpill" data-phys="services" data-val="marketplace-buy" aria-pressed="false">Module marketplace: buy</button>
|
|
1870
|
+
<button type="button" class="tpill" data-phys="services" data-val="marketplace-sell" aria-pressed="false">Module marketplace: sell</button>
|
|
1871
|
+
<button type="button" class="tpill" data-phys="services" data-val="guild-access" aria-pressed="false">Builder guild access</button>
|
|
1872
|
+
<button type="button" class="tpill" data-phys="services" data-val="guild-create" aria-pressed="false">Create a builder guild</button>
|
|
1873
|
+
<button type="button" class="tpill" data-phys="services" data-val="company-creation" aria-pressed="false">Company creation</button>
|
|
1874
|
+
<button type="button" class="tpill" data-phys="services" data-val="cbaas-consulting" aria-pressed="false">Expert consulting</button>
|
|
1875
|
+
<button type="button" class="tpill" data-phys="services" data-val="public-marketing" aria-pressed="false">Public marketed project</button>
|
|
1876
|
+
</div>
|
|
1877
|
+
<ul class="physWhyList" data-why-list="services" hidden></ul>
|
|
1878
|
+
</fieldset>
|
|
1879
|
+
<button type="button" class="btn small rowUse" data-row-use>Use this</button>
|
|
1880
|
+
</div>
|
|
2002
1881
|
</div>
|
|
2003
|
-
|
|
2004
|
-
<label class="addrOpt"><input type="checkbox" id="f-noaddr">
|
|
2005
|
-
<span>Start without an address — possible, but sign-in on your project can’t be set up until it has one.</span>
|
|
2006
|
-
</label>
|
|
2007
|
-
</div>
|
|
2008
|
-
<div class="nav">
|
|
2009
|
-
<button type="button" class="btn ghost" data-back="8">← Back</button>
|
|
2010
|
-
<button type="submit" class="btn">Next →</button>
|
|
2011
|
-
</div>
|
|
2012
|
-
</form>
|
|
2013
|
-
</section>
|
|
2014
|
-
|
|
2015
|
-
<!-- step 7 · where the app runs (task 1004351, ADR 0345). The hall is ours to
|
|
2016
|
-
host, so this step is only about the owner's APP. "Decide later" is the
|
|
2017
|
-
owner's ruling and is always the default. Render (task 1004352) asks for
|
|
2018
|
-
the owner's key, checks it, and makes them CHOOSE the workspace — a
|
|
2019
|
-
Render key reaches every workspace its user is in, so nothing is
|
|
2020
|
-
pre-picked. The key lives in a variable only: never in the draft, a
|
|
2021
|
-
URL, a log or any browser store. -->
|
|
2022
|
-
<section class="panel" id="panel-app" aria-labelledby="app-h">
|
|
2023
|
-
<h1 id="app-h">Where your app will run</h1>
|
|
2024
|
-
<p class="appHall"><b>We host your Builders' hall</b> — the place where the project gets built — at the address you just chose. There is nothing for you to set up.</p>
|
|
2025
|
-
<p class="lede">Your app is the thing the project builds. Choose where it should run, or decide later.</p>
|
|
2026
|
-
<form data-next="11">
|
|
2027
|
-
<fieldset class="appOpts" id="appOpts">
|
|
2028
|
-
<legend>Where your app runs</legend>
|
|
2029
|
-
<label class="appOpt" id="appOptRender">
|
|
2030
|
-
<input type="radio" name="appPlatform" value="render" aria-describedby="appRenderWhat">
|
|
2031
|
-
<span><b>Render</b>
|
|
2032
|
-
<span id="appRenderWhat">We set your app up on your own Render account. You lend us your Render key once, and Render bills you directly.</span></span>
|
|
2033
|
-
</label>
|
|
2034
|
-
<label class="appOpt">
|
|
2035
|
-
<input type="radio" name="appPlatform" value="later" checked>
|
|
2036
|
-
<span><b>Decide later</b>
|
|
2037
|
-
<span>Create the project now. You can choose where your app runs afterwards.</span></span>
|
|
2038
|
-
</label>
|
|
2039
|
-
</fieldset>
|
|
2040
|
-
<p class="renderWhy" id="renderLater" role="status" hidden>Your new repository has no app in it yet, so there is nothing for Render to run today. Once your builders have written the app, set it up on Render from your project’s page — it takes a minute, and asks for your Render key once. Nothing is created on Render now.</p>
|
|
2041
|
-
<div class="renderSetup" id="renderSetup" hidden>
|
|
2042
|
-
<p class="renderWhy">Your Render key is used once, to create your app on your own Render account, and then thrown away — it is not kept, not even in this browser. Render bills you directly. A key can act on your whole Render account, so only paste it here. <a href="https://dashboard.render.com/u/settings#api-keys" target="_blank" rel="noopener noreferrer">Create a key in Render</a></p>
|
|
2043
|
-
<div class="renderKeyRow">
|
|
2044
|
-
<label class="wizField"><span>Render API key</span>
|
|
2045
|
-
<input type="password" id="f-renderKey" autocomplete="off" spellcheck="false" maxlength="200" placeholder="rnd_…" aria-describedby="h-renderKey">
|
|
2046
|
-
</label>
|
|
2047
|
-
<button type="button" class="btn ghost small" id="renderCheck">Check key</button>
|
|
1882
|
+
<span class="acts"><button type="button" class="rowOk" data-row-ok>Looks right</button><button type="button" class="rowChg" data-row-chg aria-expanded="false">Change</button></span>
|
|
2048
1883
|
</div>
|
|
2049
|
-
<div class="hint" id="h-renderKey" role="status"> </div>
|
|
2050
|
-
<fieldset class="appOpts" id="renderWs" hidden>
|
|
2051
|
-
<legend>Which Render workspace</legend>
|
|
2052
|
-
<div id="renderWsList"></div>
|
|
2053
|
-
</fieldset>
|
|
2054
|
-
<fieldset class="appOpts" id="renderSvc" hidden>
|
|
2055
|
-
<legend>A new app, or one you already run</legend>
|
|
2056
|
-
<div id="renderSvcList"></div>
|
|
2057
|
-
</fieldset>
|
|
2058
|
-
<div class="renderNew" id="renderNew" hidden>
|
|
2059
|
-
<label class="wizField"><span>Your app is built with</span>
|
|
2060
|
-
<select id="f-renderRuntime">
|
|
2061
|
-
<option value="node">Node.js</option><option value="python">Python</option><option value="ruby">Ruby</option>
|
|
2062
|
-
<option value="go">Go</option><option value="rust">Rust</option><option value="elixir">Elixir</option>
|
|
2063
|
-
<option value="docker">Docker — use my repository’s Dockerfile</option>
|
|
2064
|
-
</select>
|
|
2065
|
-
</label>
|
|
2066
|
-
<label class="wizField" id="renderBuildWrap"><span>Build command <small>— Render needs one; the usual one is filled in</small></span>
|
|
2067
|
-
<input type="text" id="f-renderBuild" autocomplete="off" spellcheck="false" maxlength="500" placeholder="for example: npm ci">
|
|
2068
|
-
</label>
|
|
2069
|
-
<label class="wizField" id="renderStartWrap"><span>Start command</span>
|
|
2070
|
-
<input type="text" id="f-renderStart" autocomplete="off" spellcheck="false" maxlength="500" placeholder="for example: npm start">
|
|
2071
|
-
</label>
|
|
2072
|
-
<label class="wizField"><span>Plan</span>
|
|
2073
|
-
<select id="f-renderPlan"></select>
|
|
2074
|
-
</label>
|
|
2075
|
-
<label class="addrOpt"><input type="checkbox" id="f-renderDb">
|
|
2076
|
-
<span>My app needs a database (Render Postgres)</span>
|
|
2077
|
-
</label>
|
|
2078
|
-
<label class="wizField" id="renderDbPlanWrap" hidden><span>Database plan</span>
|
|
2079
|
-
<select id="f-renderDbPlan"></select>
|
|
2080
|
-
</label>
|
|
2081
|
-
</div>
|
|
2082
|
-
<div class="hint" id="h-render" role="status"> </div>
|
|
2083
1884
|
</div>
|
|
1885
|
+
<div class="hint" id="h-review"> </div>
|
|
2084
1886
|
<div class="nav">
|
|
2085
|
-
<
|
|
2086
|
-
<button type="submit" class="btn">
|
|
1887
|
+
<span></span>
|
|
1888
|
+
<button type="submit" class="btn" id="reviewGo" disabled>Confirm 7 more</button>
|
|
2087
1889
|
</div>
|
|
2088
1890
|
</form>
|
|
2089
1891
|
</section>
|
|
2090
1892
|
|
|
2091
|
-
<!--
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
1893
|
+
<!-- 5 · a look, then Create (task 1004421, ADR 0358): colours and a logo so builders
|
|
1894
|
+
in several projects always know which hall they are in. lookKit.buildLookPicker
|
|
1895
|
+
fills it — the same picker the manage page's "Its look" uses — and the choice is
|
|
1896
|
+
sent right after Create (sendLook). -->
|
|
2095
1897
|
<section class="panel" id="panel-look" aria-labelledby="look-h">
|
|
2096
|
-
<h1 id="look-h">Give it a look
|
|
2097
|
-
<p class="lede">
|
|
2098
|
-
<
|
|
2099
|
-
|
|
2100
|
-
<div class="nav">
|
|
2101
|
-
<button type="button" class="btn ghost" data-back="10">← Back</button>
|
|
2102
|
-
<button type="submit" class="btn">Next →</button>
|
|
2103
|
-
</div>
|
|
2104
|
-
</form>
|
|
2105
|
-
</section>
|
|
2106
|
-
|
|
2107
|
-
<!-- step 9 · review + create -->
|
|
2108
|
-
<section class="panel" id="panel-review" aria-labelledby="rev-h">
|
|
2109
|
-
<h1 id="rev-h">Ready to roll</h1>
|
|
2110
|
-
<p class="lede">One tap files the request and the platform starts standing your project up — server, database, address. Here's the beat you're about to drop:</p>
|
|
2111
|
-
<ul class="review" id="reviewList"></ul>
|
|
1898
|
+
<h1 id="look-h">Give it a look.</h1>
|
|
1899
|
+
<p class="lede">So builders always know which project they’re in.</p>
|
|
1900
|
+
<div class="lookPick" id="wizLook"></div>
|
|
1901
|
+
<p class="wizRecap" id="wizRecap"></p>
|
|
2112
1902
|
<div class="alert" id="createAlert" role="alert"></div>
|
|
2113
1903
|
<div class="nav">
|
|
2114
|
-
<
|
|
1904
|
+
<span></span>
|
|
2115
1905
|
<button type="button" class="btn big" id="doCreate">Create my project</button>
|
|
2116
1906
|
</div>
|
|
2117
|
-
<p class="finePrint">
|
|
1907
|
+
<p class="finePrint">Right after, you’ll get the few steps only you can do.</p>
|
|
2118
1908
|
</section>
|
|
2119
1909
|
|
|
2120
|
-
<!-- done · the human steps -->
|
|
1910
|
+
<!-- 6 · done · the human steps -->
|
|
2121
1911
|
<section class="panel" id="panel-done" aria-labelledby="done-h">
|
|
2122
1912
|
<div class="doneHead">
|
|
2123
1913
|
<span class="tick" aria-hidden="true"><svg width="22" height="22" viewBox="0 0 22 22" fill="none"><path d="M4 11.5 9 16.5 18 6" stroke="currentColor" stroke-width="3" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
|
|
2124
|
-
<h1 id="done-h">
|
|
1914
|
+
<h1 id="done-h">Setting up your project</h1>
|
|
2125
1915
|
</div>
|
|
2126
1916
|
<p class="lede" id="doneLede"></p>
|
|
2127
1917
|
<p class="hint" id="doneRender" role="status" hidden></p>
|
|
@@ -2130,16 +1920,13 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
2130
1920
|
<!-- the STABLE live region: the strip inside is rebuilt every 5s tick,
|
|
2131
1921
|
and a live region that is itself replaced never announces -->
|
|
2132
1922
|
<div id="liveProgress" role="status" aria-live="polite"></div>
|
|
2133
|
-
<!-- the invite step (task 1002346, BV1.R27) — OPTIONAL, and it stands
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
create (ADR 0246), so before the Create step there is nothing to invite
|
|
2137
|
-
anyone TO. The whole reasoning is ADR 0249. Hidden until the created
|
|
2138
|
-
record carries an id and a slug; the script is the only thing that
|
|
1923
|
+
<!-- the invite step (task 1002346, BV1.R27; ADR 0249) — OPTIONAL, and it stands
|
|
1924
|
+
HERE because an invite needs a project that already exists. Hidden until the
|
|
1925
|
+
created record carries an id and a slug; the script is the only thing that
|
|
2139
1926
|
shows it. -->
|
|
2140
1927
|
<div class="inviteBox" id="wizInvite" hidden>
|
|
2141
1928
|
<h2 class="stepsHead">Invite builders</h2>
|
|
2142
|
-
<p class="finePrint">Optional
|
|
1929
|
+
<p class="finePrint">Optional. Invite people by GitHub username. The invite comes from your project’s hall, and you can invite anyone later.</p>
|
|
2143
1930
|
<div class="attach">
|
|
2144
1931
|
<label class="vh" for="wizInviteWho">GitHub username</label>
|
|
2145
1932
|
<input type="text" id="wizInviteWho" placeholder="GitHub username" autocomplete="off" autocapitalize="off" spellcheck="false" maxlength="200">
|
|
@@ -2157,76 +1944,12 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
2157
1944
|
<p class="samePath" id="wizInviteLater"></p>
|
|
2158
1945
|
<h2 class="stepsHead" id="doneStepsHead">Your steps — the parts only you can play</h2>
|
|
2159
1946
|
<ol class="humanSteps" id="humanSteps"></ol>
|
|
2160
|
-
<!-- the
|
|
2161
|
-
|
|
1947
|
+
<!-- the extras row was a default (task 1002342, BV1.R23) — one line saying
|
|
1948
|
+
where they live afterwards, so accepting them is never a dead end -->
|
|
2162
1949
|
<p class="samePath" id="doneModules"></p>
|
|
2163
|
-
<p class="samePath">These are the same
|
|
1950
|
+
<p class="samePath">These steps are the same however a project is created.</p>
|
|
2164
1951
|
</section>
|
|
2165
1952
|
</div>
|
|
2166
|
-
|
|
2167
|
-
<!-- the bongo pair — one mark on every surface (owner, 2026-08-13).
|
|
2168
|
-
The drum head is SKIN, never --bg-card, or it turns black in dark
|
|
2169
|
-
mode; --drum-shadow is ink in light and plain black in dark, the
|
|
2170
|
-
same move cosmos.css already makes for --shadow-card. -->
|
|
2171
|
-
<div class="bongoCell">
|
|
2172
|
-
<svg class="bongos" viewBox="0 0 440 320" role="img" aria-label="A pair of bongo drums">
|
|
2173
|
-
<defs>
|
|
2174
|
-
<linearGradient id="shellBig" x1="0" y1="0" x2="1" y2="0.35">
|
|
2175
|
-
<stop offset="0%" stop-color="var(--accent-deep)"/><stop offset="16%" stop-color="var(--accent)"/>
|
|
2176
|
-
<stop offset="48%" stop-color="var(--accent)"/><stop offset="86%" stop-color="var(--accent-deep)"/>
|
|
2177
|
-
<stop offset="100%" stop-color="var(--accent-deep)"/>
|
|
2178
|
-
</linearGradient>
|
|
2179
|
-
<linearGradient id="shellSml" x1="0" y1="0" x2="1" y2="0.35">
|
|
2180
|
-
<stop offset="0%" stop-color="var(--accent-deep)"/><stop offset="18%" stop-color="var(--terracotta)"/>
|
|
2181
|
-
<stop offset="55%" stop-color="var(--terracotta)"/><stop offset="100%" stop-color="var(--accent-deep)"/>
|
|
2182
|
-
</linearGradient>
|
|
2183
|
-
<radialGradient id="skin" cx="0.36" cy="0.26" r="0.9">
|
|
2184
|
-
<stop offset="0%" stop-color="var(--skin-hi)"/><stop offset="58%" stop-color="var(--skin-mid)"/>
|
|
2185
|
-
<stop offset="100%" stop-color="var(--skin-lo)"/>
|
|
2186
|
-
</radialGradient>
|
|
2187
|
-
<radialGradient id="halo" cx="0.5" cy="0.5" r="0.5">
|
|
2188
|
-
<stop offset="0%" stop-color="var(--accent)" stop-opacity="0.30"/>
|
|
2189
|
-
<stop offset="48%" stop-color="var(--accent)" stop-opacity="0.13"/>
|
|
2190
|
-
<stop offset="100%" stop-color="var(--accent)" stop-opacity="0"/>
|
|
2191
|
-
</radialGradient>
|
|
2192
|
-
<filter id="soft" x="-60%" y="-160%" width="220%" height="420%"><feGaussianBlur stdDeviation="9"/></filter>
|
|
2193
|
-
</defs>
|
|
2194
|
-
<ellipse class="glow" cx="222" cy="170" rx="215" ry="140" fill="url(#halo)"/>
|
|
2195
|
-
<ellipse class="bshadow" cx="220" cy="286" rx="142" ry="14" fill="var(--drum-shadow)" opacity=".22" filter="url(#soft)"/>
|
|
2196
|
-
<path d="M 214,196 C 232,188 250,188 264,196 L 258,232 C 244,225 230,225 219,232 Z" fill="var(--accent-deep)"/>
|
|
2197
|
-
<path d="M 214,196 C 232,188 250,188 264,196 L 262,205 C 247,197 231,197 216,205 Z" fill="var(--ink)" opacity=".22"/>
|
|
2198
|
-
<g class="drumBig">
|
|
2199
|
-
<path d="M 74,118 C 76,166 90,228 98,266 A 52,17 0 0 0 202,266 C 210,228 224,166 226,118 Z" fill="url(#shellBig)"/>
|
|
2200
|
-
<path d="M 172,124 C 174,176 186,232 192,268 A 52,17 0 0 0 202,266 C 210,228 224,166 226,118 Z" fill="var(--ink)" opacity=".17"/>
|
|
2201
|
-
<path d="M 92,126 C 94,174 104,230 110,266 A 52,17 0 0 1 100,267 C 92,230 78,172 76,124 Z" fill="#fff" opacity=".20"/>
|
|
2202
|
-
<ellipse cx="150" cy="121" rx="80" ry="29" fill="var(--accent-deep)"/>
|
|
2203
|
-
<ellipse cx="150" cy="117" rx="80" ry="29" fill="var(--accent)"/>
|
|
2204
|
-
<ellipse cx="150" cy="117" rx="80" ry="29" fill="#fff" opacity=".13"/>
|
|
2205
|
-
<ellipse cx="150" cy="118" rx="70" ry="24" fill="url(#skin)"/>
|
|
2206
|
-
<ellipse cx="150" cy="118" rx="70" ry="24" fill="none" stroke="var(--ink)" stroke-opacity=".09" stroke-width="1.4"/>
|
|
2207
|
-
<ellipse cx="150" cy="118" rx="55" ry="18" fill="none" stroke="var(--ink)" stroke-opacity=".045" stroke-width="1"/>
|
|
2208
|
-
<g fill="var(--accent-deep)">
|
|
2209
|
-
<path d="M 78,138 l 9,0 -1.5,20 -6,0 z"/><path d="M 120,148 l 9,0 -1,21 -7,0 z"/>
|
|
2210
|
-
<path d="M 172,148 l 9,0 -1,21 -7,0 z"/><path d="M 214,138 l 9,0 -1.5,20 -6,0 z"/>
|
|
2211
|
-
</g>
|
|
2212
|
-
</g>
|
|
2213
|
-
<g class="drumSml">
|
|
2214
|
-
<path d="M 240,134 C 242,178 252,230 258,266 A 40,13 0 0 0 338,266 C 344,230 354,178 356,134 Z" fill="url(#shellSml)"/>
|
|
2215
|
-
<path d="M 314,140 C 316,186 324,234 328,268 A 40,13 0 0 0 338,266 C 344,230 354,178 356,134 Z" fill="var(--ink)" opacity=".19"/>
|
|
2216
|
-
<path d="M 254,140 C 256,182 264,232 268,266 A 40,13 0 0 1 260,267 C 254,232 244,182 242,138 Z" fill="#fff" opacity=".17"/>
|
|
2217
|
-
<ellipse cx="296" cy="137" rx="60" ry="22" fill="var(--accent-deep)"/>
|
|
2218
|
-
<ellipse cx="296" cy="133" rx="60" ry="22" fill="var(--terracotta)"/>
|
|
2219
|
-
<ellipse cx="296" cy="133" rx="60" ry="22" fill="#fff" opacity=".13"/>
|
|
2220
|
-
<ellipse cx="296" cy="134" rx="51" ry="18" fill="url(#skin)"/>
|
|
2221
|
-
<ellipse cx="296" cy="134" rx="51" ry="18" fill="none" stroke="var(--ink)" stroke-opacity=".09" stroke-width="1.4"/>
|
|
2222
|
-
<ellipse cx="296" cy="134" rx="39" ry="13" fill="none" stroke="var(--ink)" stroke-opacity=".045" stroke-width="1"/>
|
|
2223
|
-
<g fill="var(--accent-deep)">
|
|
2224
|
-
<path d="M 243,152 l 8,0 -1,17 -6,0 z"/><path d="M 276,160 l 8,0 -1,18 -6,0 z"/>
|
|
2225
|
-
<path d="M 312,160 l 8,0 -1,18 -6,0 z"/><path d="M 345,152 l 8,0 -1,17 -6,0 z"/>
|
|
2226
|
-
</g>
|
|
2227
|
-
</g>
|
|
2228
|
-
</svg>
|
|
2229
|
-
</div>
|
|
2230
1953
|
</div>
|
|
2231
1954
|
</div>
|
|
2232
1955
|
|
|
@@ -4772,11 +4495,14 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
4772
4495
|
and replaying it would land on the wrong panel with the rail already
|
|
4773
4496
|
marking the skipped ones done. Bump this key on EVERY renumbering
|
|
4774
4497
|
(-v6: the front door's two screens took steps 1 and 2, task 1004417;
|
|
4775
|
-
-v7: the Look step took 10, so review is 11 and done 12, task 1004421
|
|
4498
|
+
-v7: the Look step took 10, so review is 11 and done 12, task 1004421;
|
|
4499
|
+
-v9: the six blended screens, task 1004504). */
|
|
4776
4500
|
/* `termsAck` is the front door's one tick (task 1004417). It is the founder's
|
|
4777
4501
|
own answer, so it lives in the draft like every other one and survives the
|
|
4778
4502
|
GitHub round-trip; the server, not this page, names the terms version. */
|
|
4779
|
-
|
|
4503
|
+
/* `confirmed` is the review screen's own answer (task 1004504): which prefilled rows
|
|
4504
|
+
the founder has looked at and accepted or changed. Create waits for all of them. */
|
|
4505
|
+
var DRAFT_KEY = 'cb-create-draft-v9';
|
|
4780
4506
|
var CREATED_KEY = 'cb-created-instance';
|
|
4781
4507
|
/* `description` + `teamShape` are the optional About step (task 1002334) —
|
|
4782
4508
|
both legitimately stay empty, and empty is what the create request omits. */
|
|
@@ -4785,7 +4511,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
4785
4511
|
request omits the field), [] = the owner switched them all off, [...] =
|
|
4786
4512
|
what they picked. Never seed it with the preselect — that would store an
|
|
4787
4513
|
answer nobody gave. */
|
|
4788
|
-
var state = { step: 0, name: '', projType: null, description: '', teamShape: null, modules: null, mode: 'greenfield', owner: '', ownerTouched: false, repo: '', repoTouched: false, slug: '', slugTouched: false, domain: '', noAddress: false, appPlatform: 'later', physics: null, demo: null, look: null, termsAck: false };
|
|
4514
|
+
var state = { step: 0, name: '', projType: null, description: '', teamShape: null, modules: null, mode: 'greenfield', owner: '', ownerTouched: false, repo: '', repoTouched: false, slug: '', slugTouched: false, domain: '', noAddress: false, appPlatform: 'later', physics: null, demo: null, look: null, termsAck: false, confirmed: {} };
|
|
4789
4515
|
try { Object.assign(state, JSON.parse(sessionStorage.getItem(DRAFT_KEY) || '{}')); } catch (e) {}
|
|
4790
4516
|
var authKnown = false; /* flips true when the boot's /me settles, either way */
|
|
4791
4517
|
|
|
@@ -4799,7 +4525,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
4799
4525
|
state.name = handedName.trim().slice(0, 80);
|
|
4800
4526
|
state.slug = deriveSlug(state.name); state.slugTouched = false;
|
|
4801
4527
|
state.repo = deriveRepo(state.name); state.repoTouched = false;
|
|
4802
|
-
state.step = state.step >= 1 && state.step <=
|
|
4528
|
+
state.step = state.step >= 1 && state.step <= 5 ? state.step : 1;
|
|
4803
4529
|
try { sessionStorage.setItem(DRAFT_KEY, JSON.stringify(state)); } catch (e) {}
|
|
4804
4530
|
}
|
|
4805
4531
|
} catch (e) {}
|
|
@@ -4923,13 +4649,13 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
4923
4649
|
if (avail === 'taken' || avail === 'yours' || avail === 'reserved') { var hs = $('h-slug'); hs.innerHTML = ' '; hs.className = 'hint'; }
|
|
4924
4650
|
if (custom) {
|
|
4925
4651
|
big.innerHTML = '<code>' + esc(custom) + '</code> — your own domain, wired up for you.';
|
|
4926
|
-
note.textContent = '
|
|
4652
|
+
note.textContent = 'You point it here after setup; your projects page walks you through it.';
|
|
4927
4653
|
} else if (state.noAddress) {
|
|
4928
4654
|
big.textContent = 'Starting without an address.';
|
|
4929
4655
|
note.textContent = 'Sign-in on your project can’t be set up until it has one — add it any time from your projects page.';
|
|
4930
4656
|
} else if (assigned && avail === 'free') {
|
|
4931
4657
|
big.innerHTML = '<code>' + esc(assigned) + '</code> — yours from the moment it stands up.';
|
|
4932
|
-
note.textContent = '
|
|
4658
|
+
note.textContent = 'Sign-in switches on after the GitHub steps. You can add your own domain any time.';
|
|
4933
4659
|
} else if (assigned && avail === 'taken') {
|
|
4934
4660
|
big.innerHTML = '<code>' + esc(assigned) + '</code> — already taken.';
|
|
4935
4661
|
note.textContent = 'Another project on the platform holds that handle. Pick a different one.';
|
|
@@ -4991,115 +4717,89 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
4991
4717
|
}
|
|
4992
4718
|
|
|
4993
4719
|
/* ── step navigation ──
|
|
4994
|
-
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
|
|
4999
|
-
|
|
5000
|
-
|
|
5001
|
-
|
|
5002
|
-
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
/* a step the planet-physics answers skip (task 1004419); still reachable */
|
|
5013
|
-
li.classList.toggle('skipped', step < 13 && physStepSkipped(n));
|
|
5014
|
-
if (now) li.setAttribute('aria-current', 'step'); else li.removeAttribute('aria-current');
|
|
5015
|
-
/* a step already answered is a way BACK (task 1003246): only those take
|
|
5016
|
-
the button role, the tab stop and the data-goto the delegated handler
|
|
5017
|
-
below reads. The done panel offers none — the project is filed, and
|
|
5018
|
-
there is nothing behind it to change. */
|
|
5019
|
-
if (step < 13 && n < cur) {
|
|
5020
|
-
li.setAttribute('role', 'button');
|
|
5021
|
-
li.setAttribute('tabindex', '0');
|
|
5022
|
-
li.setAttribute('data-goto', String(n));
|
|
5023
|
-
} else {
|
|
5024
|
-
li.removeAttribute('role');
|
|
5025
|
-
li.removeAttribute('tabindex');
|
|
5026
|
-
li.removeAttribute('data-goto');
|
|
5027
|
-
}
|
|
5028
|
-
});
|
|
4720
|
+
The six blended screens (task 1004504, the design of record
|
|
4721
|
+
docs/design/mocks/combined-onboarding/): gate 0 · fork 1 (demo or real, the terms
|
|
4722
|
+
as its one tick) · what 2 (name, kind, a sentence) · checks 3 (the planet-physics
|
|
4723
|
+
answers that change something) · review 4 (every prefilled answer, confirmed row by
|
|
4724
|
+
row) · look 5 (then Create) · done 6. No rail: the eyebrow names the screen and one
|
|
4725
|
+
Back walks backwards. A demo skips the checks — its answers are the demo's own. */
|
|
4726
|
+
var panels = { 0: 'panel-gate', 1: 'panel-fork', 2: 'panel-what', 3: 'panel-checks', 4: 'panel-review', 5: 'panel-look', 6: 'panel-done' };
|
|
4727
|
+
var SCREEN = { 0: 'signin', 1: 'fork', 2: 'what', 3: 'checks', 4: 'review', 5: 'look', 6: 'done' };
|
|
4728
|
+
function eyebrowFor(step) {
|
|
4729
|
+
if (step === 0) return 'Sign in';
|
|
4730
|
+
if (step === 6) return 'Founding';
|
|
4731
|
+
if (step === 1 && !$('demoSetup').hidden) return 'Demo';
|
|
4732
|
+
return state.demo && step > 1 ? 'New demo' : 'New project';
|
|
4733
|
+
}
|
|
4734
|
+
/* where Back goes from each screen: the demo's own screen first, then the one before */
|
|
4735
|
+
function prevStep(step) {
|
|
4736
|
+
if (step === 4 && state.demo) return 2;
|
|
4737
|
+
return step - 1;
|
|
5029
4738
|
}
|
|
5030
4739
|
function show(step, moveFocus) {
|
|
5031
4740
|
state.step = step;
|
|
5032
4741
|
Object.keys(panels).forEach(function (k) {
|
|
5033
4742
|
$(panels[k]).classList.toggle('on', Number(k) === step);
|
|
5034
4743
|
});
|
|
5035
|
-
paintRail(step);
|
|
5036
4744
|
saveDraft();
|
|
5037
|
-
/* the
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
root.querySelector('.createSplit').classList.toggle('wizFork', step === 2 || step === 3);
|
|
4745
|
+
/* the room's seed grows a little on each screen (CSS reads data-screen) */
|
|
4746
|
+
$('wizRoom').setAttribute('data-screen', SCREEN[step]);
|
|
4747
|
+
$('wizEyebrow').textContent = eyebrowFor(step);
|
|
4748
|
+
$('wizBack').hidden = step === 0 || step === 6 || (step === 1 && $('demoSetup').hidden);
|
|
4749
|
+
$('demoTag').hidden = !state.demo;
|
|
5043
4750
|
/* focus moves ONLY on a deliberate step (the hub rule setView already
|
|
5044
4751
|
applies to this view: never on boot, never on a background settle) */
|
|
5045
4752
|
if (moveFocus) {
|
|
5046
4753
|
var h = $(panels[step]).querySelector('h1');
|
|
5047
4754
|
if (h) { h.setAttribute('tabindex', '-1'); h.focus({ preventScroll: false }); }
|
|
5048
4755
|
}
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
if (step ===
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
if (step ===
|
|
5067
|
-
if (step ===
|
|
5068
|
-
}
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
if (railEl) {
|
|
5075
|
-
railEl.addEventListener('click', function (ev) {
|
|
5076
|
-
var li = ev.target.closest && ev.target.closest('li[data-goto]');
|
|
5077
|
-
if (li) show(Number(li.getAttribute('data-goto')), true);
|
|
5078
|
-
});
|
|
5079
|
-
railEl.addEventListener('keydown', function (ev) {
|
|
5080
|
-
if (ev.key !== 'Enter' && ev.key !== ' ' && ev.key !== 'Spacebar') return;
|
|
5081
|
-
var li = ev.target.closest && ev.target.closest('li[data-goto]');
|
|
5082
|
-
if (!li) return;
|
|
5083
|
-
ev.preventDefault();
|
|
5084
|
-
show(Number(li.getAttribute('data-goto')), true);
|
|
5085
|
-
});
|
|
5086
|
-
}
|
|
4756
|
+
if (step === 1) paintDoors();
|
|
4757
|
+
if (step === 3) { physLoadAccountOnce(); paintChecks(); }
|
|
4758
|
+
/* the review hosts the old steps' own controls, so it repaints them on entry.
|
|
4759
|
+
The repo probe is authenticated — never fire it before /me settles (a pre-auth
|
|
4760
|
+
probe 401s for strangers and could seed the draft's owner with a placeholder) */
|
|
4761
|
+
if (step === 4) {
|
|
4762
|
+
physDefaults();
|
|
4763
|
+
renderModules();
|
|
4764
|
+
syncHome();
|
|
4765
|
+
if (authKnown) ensureRepoAccess();
|
|
4766
|
+
/* re-check on every entry: a handle free ten minutes ago may not be now (N10) */
|
|
4767
|
+
checkSlugAvailable(); renderAddrPanel();
|
|
4768
|
+
syncAppStep(); syncRenderSetup();
|
|
4769
|
+
paintSetup();
|
|
4770
|
+
}
|
|
4771
|
+
/* a resumed draft can land PAST the review with the repo probe never run — Create
|
|
4772
|
+
would then file a repo it promised to make but never did */
|
|
4773
|
+
else if (step > 4 && step < 6 && authKnown && repoAccess === null) ensureRepoAccess();
|
|
4774
|
+
if (step === 5) { renderLook(); renderRecap(); }
|
|
4775
|
+
}
|
|
4776
|
+
$('wizBack').addEventListener('click', function () {
|
|
4777
|
+
if (state.step === 1 && !$('demoSetup').hidden) { demoShow('intro'); $('wizDemo').focus(); return; }
|
|
4778
|
+
if (state.step === 3 && !$('checksStop').hidden) { checksShow('main'); return; }
|
|
4779
|
+
if (state.step > 1 && state.step < 6) show(prevStep(state.step), true);
|
|
4780
|
+
});
|
|
5087
4781
|
|
|
5088
|
-
/* ── the front door (
|
|
5089
|
-
|
|
4782
|
+
/* ── the front door (tasks 1004417 and 1004504) ── the tick mirrors into the draft,
|
|
4783
|
+
and both doors wait for it: nothing past the fork until the terms are accepted */
|
|
4784
|
+
function paintDoors() {
|
|
4785
|
+
$('wizDemo').disabled = !state.termsAck;
|
|
4786
|
+
$('wizStart').disabled = !state.termsAck;
|
|
4787
|
+
}
|
|
5090
4788
|
$('termsAck').checked = !!state.termsAck;
|
|
5091
4789
|
$('termsAck').addEventListener('change', function () {
|
|
5092
4790
|
state.termsAck = !!this.checked;
|
|
5093
|
-
if (state.termsAck) { $('h-terms').textContent = '
|
|
4791
|
+
if (state.termsAck) { $('h-terms').textContent = ' '; $('h-terms').className = 'hint'; }
|
|
4792
|
+
paintDoors();
|
|
5094
4793
|
saveDraft();
|
|
5095
4794
|
});
|
|
4795
|
+
paintDoors();
|
|
5096
4796
|
/* ── the demo route (task 1004420, BV2.PS09) ──
|
|
5097
|
-
"Try the demo" opens
|
|
5098
|
-
idea,
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
is soft (D3). The limits and the fit sentences are copies of
|
|
4797
|
+
"Try the demo" opens one screen inside the fork: the people, the hours each, the
|
|
4798
|
+
idea, a first read of what fits, and up to two teammates. Continue goes on through
|
|
4799
|
+
the ordinary flow from "What are you making?": a demo is a normal project marked
|
|
4800
|
+
"demo" (D9), so the create carries `demo` and the server applies the demo's picked
|
|
4801
|
+
answers and makes it private to the team — which is why it skips the checks. The
|
|
4802
|
+
time box is soft (D3). The limits and the fit sentences are copies of
|
|
5103
4803
|
modules/provisioning/demo.js, held equal by tests/wizard_demo.mjs. */
|
|
5104
4804
|
var DEMO_LIMITS = { people: [1, 12], hours: [1, 40] };
|
|
5105
4805
|
var demoDraft = state.demo
|
|
@@ -5125,32 +4825,26 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
5125
4825
|
$('demoHours').textContent = String(demoDraft.hours);
|
|
5126
4826
|
$('demoFitSum').textContent = demoDraft.people + (demoDraft.people === 1 ? ' person' : ' people') + ' × ' + demoDraft.hours + (demoDraft.hours === 1 ? ' hour' : ' hours') + ' = ' + fit.total + ' builder-hours.';
|
|
5127
4827
|
$('demoFitVerdict').textContent = fit.sentence;
|
|
5128
|
-
$('demoLPeople').textContent = String(demoDraft.people);
|
|
5129
4828
|
var others = demoDraft.people - 1;
|
|
5130
|
-
$('demoLPeopleSub').textContent = others === 0 ? 'Just you' : others === 1 ? 'You, plus the person you invite below' : others === 2 ? 'You, plus the two people you invite below' : 'You, plus ' + others + ' people: invite two below, and the rest from your hall';
|
|
5131
|
-
$('demoLHours').textContent = demoDraft.hours + (demoDraft.hours === 1 ? ' hour each' : ' hours each');
|
|
5132
|
-
$('demoLTotal').textContent = fit.total + ' builder-hours in total. When it runs out you are asked whether to keep your planet; your work is never stopped.';
|
|
5133
|
-
$('demoStageHours').textContent = String(fit.total);
|
|
5134
4829
|
$('demoInvites').hidden = others === 0;
|
|
5135
4830
|
$('demoInvite2').closest('label').hidden = others < 2;
|
|
5136
4831
|
}
|
|
5137
4832
|
function demoShow(which) {
|
|
5138
4833
|
$('forkIntro').hidden = which !== 'intro';
|
|
5139
4834
|
$('demoSetup').hidden = which !== 'setup';
|
|
5140
|
-
$('demoReady').hidden = which !== 'ready';
|
|
5141
|
-
$('demoRing').hidden = which === 'intro';
|
|
5142
|
-
$('demoStageCap').hidden = which === 'intro';
|
|
5143
4835
|
$('wizDemo').setAttribute('aria-expanded', which === 'intro' ? 'false' : 'true');
|
|
4836
|
+
$('wizEyebrow').textContent = eyebrowFor(state.step);
|
|
4837
|
+
$('wizBack').hidden = which === 'intro';
|
|
5144
4838
|
if (which !== 'intro') {
|
|
5145
4839
|
paintDemo();
|
|
5146
|
-
var h = $(
|
|
4840
|
+
var h = $('demo-h1');
|
|
5147
4841
|
h.setAttribute('tabindex', '-1'); h.focus({ preventScroll: false });
|
|
5148
4842
|
}
|
|
5149
4843
|
}
|
|
5150
4844
|
$('demoIdea').value = demoDraft.idea;
|
|
5151
4845
|
$('demoInvite1').value = demoDraft.invites[0] || '';
|
|
5152
4846
|
$('demoInvite2').value = demoDraft.invites[1] || '';
|
|
5153
|
-
$('wizDemo').addEventListener('click', function () { demoShow('setup'); });
|
|
4847
|
+
$('wizDemo').addEventListener('click', function () { if (state.termsAck) demoShow('setup'); });
|
|
5154
4848
|
root.querySelectorAll('[data-demo-step]').forEach(function (b) {
|
|
5155
4849
|
b.addEventListener('click', function () {
|
|
5156
4850
|
var key = b.getAttribute('data-demo-step');
|
|
@@ -5161,9 +4855,6 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
5161
4855
|
});
|
|
5162
4856
|
});
|
|
5163
4857
|
$('demoIdea').addEventListener('input', function () { demoDraft.idea = this.value; });
|
|
5164
|
-
$('demoNext1').addEventListener('click', function () { demoShow('ready'); });
|
|
5165
|
-
$('demoBack1').addEventListener('click', function () { demoShow('intro'); $('wizDemo').focus(); });
|
|
5166
|
-
$('demoBack2').addEventListener('click', function () { demoShow('setup'); });
|
|
5167
4858
|
$('demoStart').addEventListener('click', function () {
|
|
5168
4859
|
var invites = [demoHandle($('demoInvite1').value), demoHandle($('demoInvite2').value)]
|
|
5169
4860
|
.filter(function (h, i, all) { return h && all.indexOf(h) === i; })
|
|
@@ -5176,9 +4867,10 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
5176
4867
|
$('f-desc').value = state.description;
|
|
5177
4868
|
}
|
|
5178
4869
|
state.teamShape = demoDraft.people > 1 ? 'small-team' : 'solo';
|
|
4870
|
+
/* the demo's own rows are the demo's answers, so the review asks again */
|
|
4871
|
+
state.confirmed = {};
|
|
5179
4872
|
saveDraft();
|
|
5180
|
-
|
|
5181
|
-
show(4, true);
|
|
4873
|
+
show(2, true);
|
|
5182
4874
|
});
|
|
5183
4875
|
/* the done panel's invite step names the demo's teammates (task 1004420): the hub
|
|
5184
4876
|
holds no invite of its own, so the founder sends each from the project's own box */
|
|
@@ -5220,6 +4912,8 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
5220
4912
|
var h = $('h-name');
|
|
5221
4913
|
if (state.slug) { h.innerHTML = 'its handle on the platform: <code>' + esc(state.slug) + '</code>'; h.className = 'hint'; }
|
|
5222
4914
|
else { h.innerHTML = ' '; }
|
|
4915
|
+
/* the repo and the address follow the name, so the review asks about them again */
|
|
4916
|
+
unconfirm(['code', 'address']);
|
|
5223
4917
|
saveDraft();
|
|
5224
4918
|
});
|
|
5225
4919
|
if (state.name && state.slug) $('h-name').innerHTML = 'its handle on the platform: <code>' + esc(state.slug) + '</code>';
|
|
@@ -5231,53 +4925,35 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
5231
4925
|
$('adoptNote').classList.toggle('on', mode === 'adopt');
|
|
5232
4926
|
saveDraft();
|
|
5233
4927
|
syncHome();
|
|
5234
|
-
|
|
4928
|
+
if (typeof paintSetup === 'function') paintSetup();
|
|
5235
4929
|
}
|
|
5236
4930
|
$('c-green').addEventListener('click', function () { setMode('greenfield'); });
|
|
5237
4931
|
$('c-adopt').addEventListener('click', function () { setMode('adopt'); });
|
|
5238
4932
|
setMode(state.mode);
|
|
5239
4933
|
|
|
5240
|
-
/* ── the
|
|
5241
|
-
description card, themed per type via --type-acc (component-scoped, C1) */
|
|
4934
|
+
/* ── the kind (task 1002772) ── a declaration: the pill is the answer */
|
|
5242
4935
|
function paintType(t) {
|
|
5243
4936
|
root.querySelectorAll('#typeCards .tpill').forEach(function (b) {
|
|
5244
4937
|
b.setAttribute('aria-pressed', String(b.getAttribute('data-type') === t));
|
|
5245
4938
|
});
|
|
5246
|
-
var card = $('typeCard');
|
|
5247
|
-
var m = t && TYPE_META[t];
|
|
5248
|
-
if (m) {
|
|
5249
|
-
card.hidden = false;
|
|
5250
|
-
card.style.setProperty('--type-acc', m.acc);
|
|
5251
|
-
$('typeCardName').textContent = m.label;
|
|
5252
|
-
$('typeCardDesc').textContent = m.desc;
|
|
5253
|
-
} else {
|
|
5254
|
-
card.hidden = true;
|
|
5255
|
-
}
|
|
5256
4939
|
}
|
|
5257
4940
|
root.querySelectorAll('#typeCards .tpill').forEach(function (b) {
|
|
5258
4941
|
b.addEventListener('click', function () {
|
|
5259
4942
|
state.projType = b.getAttribute('data-type');
|
|
5260
4943
|
$('h-projtype').innerHTML = ' '; $('h-projtype').className = 'hint';
|
|
5261
4944
|
paintType(state.projType);
|
|
5262
|
-
|
|
4945
|
+
/* the suggested extras follow the kind, so the review asks about them again */
|
|
4946
|
+
unconfirm(['extras']);
|
|
5263
4947
|
saveDraft();
|
|
5264
4948
|
});
|
|
5265
4949
|
});
|
|
5266
4950
|
paintType(state.projType || null);
|
|
5267
4951
|
|
|
5268
|
-
/* ── the
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
the panel — it flips between "here's what you'll get" and "here's what
|
|
5274
|
-
skipping costs", so pressing Skip is an informed choice rather than a
|
|
5275
|
-
discovery made three screens later. */
|
|
5276
|
-
var TEAM_META = {
|
|
5277
|
-
'solo': 'Just you. We keep the setup light and skip the joining machinery.',
|
|
5278
|
-
'small-team': 'A handful of people you know. We suggest a setup built around invites.',
|
|
5279
|
-
'community': 'Open to whoever turns up. We suggest a setup built for people applying to join.'
|
|
5280
|
-
};
|
|
4952
|
+
/* ── the sentence and the team shape (task 1002334) ──
|
|
4953
|
+
The sentence is optional and half the publish gate (ADR 0182 D4): it is what puts
|
|
4954
|
+
the project on the public map, which the field's own label says. The team shape is
|
|
4955
|
+
what the extras' preselect reads: "solo" follows a team of one, and a team says who
|
|
4956
|
+
can join (people invited, or anyone applying) on the review's Team size row. */
|
|
5281
4957
|
function teamLabel(t) {
|
|
5282
4958
|
var b = t && root.querySelector('#teamCards .tpill[data-team="' + t + '"]');
|
|
5283
4959
|
return b ? b.textContent : '';
|
|
@@ -5287,42 +4963,21 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
5287
4963
|
b.setAttribute('aria-pressed', String(b.getAttribute('data-team') === t));
|
|
5288
4964
|
});
|
|
5289
4965
|
}
|
|
5290
|
-
function paintDetail() {
|
|
5291
|
-
var note = $('detailSkipNote');
|
|
5292
|
-
var told = String(state.description || '').trim();
|
|
5293
|
-
var parts = [];
|
|
5294
|
-
if (!told) {
|
|
5295
|
-
parts.push('No description yet — your project still stands up, it just stays off the public map until it has one. You can add it any time from its page.');
|
|
5296
|
-
} else {
|
|
5297
|
-
parts.push('That description is what puts your project on the public map.');
|
|
5298
|
-
}
|
|
5299
|
-
if (state.teamShape && TEAM_META[state.teamShape]) parts.push(TEAM_META[state.teamShape]);
|
|
5300
|
-
note.textContent = parts.join(' ');
|
|
5301
|
-
note.classList.add('on');
|
|
5302
|
-
}
|
|
5303
4966
|
$('f-desc').value = state.description || '';
|
|
5304
4967
|
$('f-desc').addEventListener('input', function () {
|
|
5305
4968
|
state.description = this.value.slice(0, 600);
|
|
5306
|
-
paintDetail();
|
|
5307
4969
|
saveDraft();
|
|
5308
4970
|
});
|
|
5309
4971
|
root.querySelectorAll('#teamCards .tpill').forEach(function (b) {
|
|
5310
4972
|
b.addEventListener('click', function () {
|
|
5311
|
-
|
|
5312
|
-
not answer", which an optional question has to keep offering */
|
|
5313
|
-
var pick = b.getAttribute('data-team');
|
|
5314
|
-
state.teamShape = state.teamShape === pick ? null : pick;
|
|
4973
|
+
state.teamShape = b.getAttribute('data-team');
|
|
5315
4974
|
paintTeam(state.teamShape);
|
|
5316
|
-
|
|
4975
|
+
unconfirm(['extras']);
|
|
5317
4976
|
saveDraft();
|
|
4977
|
+
paintSetup();
|
|
5318
4978
|
});
|
|
5319
4979
|
});
|
|
5320
4980
|
paintTeam(state.teamShape || null);
|
|
5321
|
-
paintDetail();
|
|
5322
|
-
/* Skip = go on with whatever is (or isn't) filled in. It deliberately does
|
|
5323
|
-
NOT erase an answer already given: a visitor who typed a description and
|
|
5324
|
-
then pressed Skip meant "I'm done here", not "throw that away". */
|
|
5325
|
-
$('skipDetail').addEventListener('click', function () { show(7, true); });
|
|
5326
4981
|
|
|
5327
4982
|
/* ── the module picker (task 1002339, BV1.R20) ──
|
|
5328
4983
|
ONE public read gives the panel both halves it needs: `optional` is the
|
|
@@ -5365,8 +5020,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
5365
5020
|
exact dishonesty the About row's "off the public map" clause exists to
|
|
5366
5021
|
prevent, one screen before Create. */
|
|
5367
5022
|
function repaintForBundles() {
|
|
5368
|
-
if (state.step ===
|
|
5369
|
-
else if (state.step === 12) renderReview();
|
|
5023
|
+
if (state.step === 4) { renderModules(); paintSetup(); }
|
|
5370
5024
|
}
|
|
5371
5025
|
/* The row for the declared type, or null before the read lands / for a type
|
|
5372
5026
|
the table has no row for (including no type at all). */
|
|
@@ -5527,7 +5181,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
5527
5181
|
why.classList.toggle('on', reasons.length > 0);
|
|
5528
5182
|
var core = (starterBundles.core || []).length;
|
|
5529
5183
|
note.textContent = core
|
|
5530
|
-
? 'Every project also
|
|
5184
|
+
? 'Every project also has the same ' + core + ' core parts, always on: the platform, its work tracking and the builders’ hall.'
|
|
5531
5185
|
: '';
|
|
5532
5186
|
note.classList.toggle('on', core > 0);
|
|
5533
5187
|
}
|
|
@@ -5553,12 +5207,6 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
5553
5207
|
moduleQuery = (ev && ev.target && ev.target.value) || '';
|
|
5554
5208
|
renderModules();
|
|
5555
5209
|
});
|
|
5556
|
-
/* Next off this panel CONFIRMS what is on screen — the owner looked at the
|
|
5557
|
-
set and accepted it, which is a different fact from never being asked. */
|
|
5558
|
-
root.querySelector('#panel-modules form').addEventListener('submit', function () {
|
|
5559
|
-
state.modules = pickedModules();
|
|
5560
|
-
saveDraft();
|
|
5561
|
-
});
|
|
5562
5210
|
/* What the review ledger names. Three cases, and the third is the one worth
|
|
5563
5211
|
writing down: with nothing said and the catalog not (yet) loaded we do not
|
|
5564
5212
|
know the module NAMES, but we do know the owner will get the type's
|
|
@@ -5575,14 +5223,9 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
5575
5223
|
: 'The extras you picked';
|
|
5576
5224
|
}
|
|
5577
5225
|
var picked = pickedModules();
|
|
5578
|
-
return picked.length ?
|
|
5226
|
+
return picked.length ? picked.map(moduleLabel).join(', ') : 'None — just the core';
|
|
5579
5227
|
}
|
|
5580
5228
|
|
|
5581
|
-
/* Skip = walk past without answering. It does NOT erase a toggle already
|
|
5582
|
-
made (the About step's rule), and with nothing said it leaves state.modules
|
|
5583
|
-
null so the server applies the bundle this panel was showing. */
|
|
5584
|
-
$('skipModules').addEventListener('click', function () { show(8, true); });
|
|
5585
|
-
|
|
5586
5229
|
/* ── GitHub-integrated repo picker (adopt) + auto-create (fresh) — task 1002099 / ADR 0143.
|
|
5587
5230
|
Progressive enhancement: probe GET /github/repos once we reach the Home step.
|
|
5588
5231
|
200 → we hold repo access → show the picker (adopt) / name field (fresh);
|
|
@@ -5759,7 +5402,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
5759
5402
|
var h = $('h-domain');
|
|
5760
5403
|
if (!state.domain) { h.innerHTML = ' '; h.className = 'hint'; this.classList.remove('bad'); }
|
|
5761
5404
|
else if (looksLikeDomain(state.domain)) { h.textContent = 'Looks like a domain.'; h.className = 'hint ok'; this.classList.remove('bad'); }
|
|
5762
|
-
else { h.textContent = 'That doesn’t look like a domain yet (e.g.
|
|
5405
|
+
else { h.textContent = 'That doesn’t look like a domain yet (e.g. moonbase.example.com).'; h.className = 'hint err'; this.classList.add('bad'); }
|
|
5763
5406
|
renderAddrPanel();
|
|
5764
5407
|
saveDraft();
|
|
5765
5408
|
});
|
|
@@ -5769,10 +5412,10 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
5769
5412
|
saveDraft();
|
|
5770
5413
|
});
|
|
5771
5414
|
|
|
5772
|
-
/* ── planet physics
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5415
|
+
/* ── planet physics (tasks 1004418 and 1004419, BV2.PS07 + PS11; regrouped by task 1004504) ──
|
|
5416
|
+
Geography, ages and entity are the quick checks (screen 3); the project's state,
|
|
5417
|
+
the builders, the rewards and the framing are rows of the review (screen 4), each
|
|
5418
|
+
prefilled and confirmed there. The answers live in state.physics.answers under
|
|
5776
5419
|
planet-physics.js's own keys, so the create sends them as `detail` untouched.
|
|
5777
5420
|
The vocabularies below are COPIES of that file's (a page cannot require a
|
|
5778
5421
|
module), held equal by tests/wizard_physics.mjs and wizard_physics_more.mjs. */
|
|
@@ -5803,32 +5446,14 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
5803
5446
|
'CA-ON': 'Ontario', 'CA-PE': 'Prince Edward Island', 'CA-QC': 'Quebec', 'CA-SK': 'Saskatchewan', 'CA-YT': 'Yukon'
|
|
5804
5447
|
};
|
|
5805
5448
|
var PHYS_ENTITY_FORMS = ['nonprofit', 'for-profit', 'corporate-division', 'research', 'university-project', 'social-group', 'single-owner-llc', 'private-individual'];
|
|
5806
|
-
/*
|
|
5807
|
-
art direction): frost for one person, chrome for a group, gold for an
|
|
5808
|
-
organization, obsidian for a nonprofit. */
|
|
5809
|
-
var PHYS_MATERIAL = {
|
|
5810
|
-
'intent-solo': 'frost', 'single-owner-llc': 'frost', 'private-individual': 'frost',
|
|
5811
|
-
'university-project': 'chrome', 'social-group': 'chrome', 'intent-unsure': 'chrome',
|
|
5812
|
-
'intent-company': 'gold', 'for-profit': 'gold', 'corporate-division': 'gold', 'research': 'gold',
|
|
5813
|
-
'intent-nonprofit': 'obsidian', 'nonprofit': 'obsidian'
|
|
5814
|
-
};
|
|
5815
|
-
var PHYS_CORE_WORD = { frost: 'individual', chrome: 'group', gold: 'organization', obsidian: 'nonprofit' };
|
|
5816
|
-
var PHYS_GEO_WORD = { 'us-only': 'us only', 'us-eu': 'us + eu', 'global': 'global', 'not-sure': 'not sure yet' };
|
|
5817
|
-
/* Questions 5 to 7 (task 1004419). The voice (spec D5): a formed organization
|
|
5818
|
-
reads the plain professional copy; individuals, groups and every "not yet" read
|
|
5819
|
-
the planet's. The lists are planet-physics.js's, in its order. */
|
|
5820
|
-
var PHYS_ORGS = ['nonprofit', 'for-profit', 'corporate-division', 'research'];
|
|
5449
|
+
/* Questions 5 to 7 (task 1004419). The lists are planet-physics.js's, in its order. */
|
|
5821
5450
|
var PHYS_REWARDS = ['non-monetary', 'cash', 'equity', 'future-payouts'];
|
|
5822
5451
|
var PHYS_FRAMINGS = ['managed', 'hybrid', 'local', 'open-source'];
|
|
5823
5452
|
var PHYS_SERVICES = ['hosting', 'labor-payment', 'starter-kit', 'marketplace-buy', 'marketplace-sell', 'guild-access', 'guild-create', 'company-creation', 'cbaas-consulting', 'public-marketing'];
|
|
5824
5453
|
var PHYS_MANY = ['reward_intents', 'services'];
|
|
5825
5454
|
/* the suite a hybrid or local framing starts from (the canvas's Framing frame) */
|
|
5826
5455
|
var PHYS_SUITE_START = { hybrid: ['hosting', 'starter-kit', 'marketplace-buy', 'guild-access'], local: ['starter-kit', 'marketplace-buy', 'guild-access'] };
|
|
5827
|
-
/* the
|
|
5828
|
-
hybrid the middle, local far, and open source a system of its own */
|
|
5829
|
-
var PHYS_ORBIT = { managed: 'near', hybrid: 'mid', local: 'far', 'open-source': 'own' };
|
|
5830
|
-
var PHYS_ORBIT_WORD = { managed: 'a close orbit around the platform', hybrid: 'a middle orbit around the platform', local: 'a far orbit around the platform', 'open-source': 'a system of its own' };
|
|
5831
|
-
/* what the summary says for each answer */
|
|
5456
|
+
/* what the review and the reasons say for each answer */
|
|
5832
5457
|
var PHYS_SAY = {
|
|
5833
5458
|
project_state: { 'new': 'New', existing: 'Existing' },
|
|
5834
5459
|
builder_geography: { 'us-only': 'Builders in the US only', 'us-eu': 'Builders in the US and EU', global: 'Builders anywhere', 'not-sure': 'Not sure yet where builders will be' },
|
|
@@ -5837,7 +5462,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
5837
5462
|
nonprofit: 'Nonprofit', 'for-profit': 'For-profit company', 'corporate-division': 'Division of a corporation', research: 'Research entity',
|
|
5838
5463
|
'university-project': 'University project', 'social-group': 'Social group', 'single-owner-llc': 'Single-owner LLC', 'private-individual': 'Private individual'
|
|
5839
5464
|
},
|
|
5840
|
-
reward_intents: { 'non-monetary': '
|
|
5465
|
+
reward_intents: { 'non-monetary': 'Learning and fun', cash: 'Cash for work', equity: 'Equity', 'future-payouts': 'Future payouts' },
|
|
5841
5466
|
framing: { managed: 'All managed', hybrid: 'Hybrid', local: 'Local or internal', 'open-source': 'Open source, local' },
|
|
5842
5467
|
services: {
|
|
5843
5468
|
hosting: 'Hosting', 'labor-payment': 'Labor payment and HR', 'starter-kit': 'Starting module kit', 'marketplace-buy': 'Module marketplace: buy',
|
|
@@ -5845,16 +5470,10 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
5845
5470
|
'company-creation': 'Company creation', 'cbaas-consulting': 'Expert consulting', 'public-marketing': 'Public marketed project'
|
|
5846
5471
|
}
|
|
5847
5472
|
};
|
|
5848
|
-
/* the project type the entity suggests for the Type step (a pre-fill the founder sees and can change) */
|
|
5849
|
-
var PHYS_TYPE = {
|
|
5850
|
-
'intent-company': 'business', 'for-profit': 'business', 'corporate-division': 'business', 'single-owner-llc': 'business',
|
|
5851
|
-
'intent-nonprofit': 'non-profit', nonprofit: 'non-profit', research: 'research', 'university-project': 'research'
|
|
5852
|
-
};
|
|
5853
|
-
var PHYS_LAST = 7;
|
|
5854
5473
|
var physLimits = [];
|
|
5855
5474
|
|
|
5856
5475
|
function phys() {
|
|
5857
|
-
if (!state.physics || typeof state.physics !== 'object') state.physics = {
|
|
5476
|
+
if (!state.physics || typeof state.physics !== 'object') state.physics = { answers: {} };
|
|
5858
5477
|
if (!state.physics.answers || typeof state.physics.answers !== 'object') state.physics.answers = {};
|
|
5859
5478
|
return state.physics;
|
|
5860
5479
|
}
|
|
@@ -5879,11 +5498,11 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
5879
5498
|
if ((a.framing === 'hybrid' || a.framing === 'local') && Array.isArray(a.services)) {
|
|
5880
5499
|
out.services = PHYS_SERVICES.filter(function (s) { return a.services.indexOf(s) !== -1; });
|
|
5881
5500
|
}
|
|
5882
|
-
/* the voice
|
|
5883
|
-
|
|
5501
|
+
/* the voice (spec D5) is the plain one for every founder since task 1004504;
|
|
5502
|
+
still stored, so settings can switch it later */
|
|
5503
|
+
if (a.entity) out.voice = 'professional';
|
|
5884
5504
|
return out;
|
|
5885
5505
|
}
|
|
5886
|
-
function physVoice() { return PHYS_ORGS.indexOf(phys().answers.entity) !== -1 ? 'professional' : 'planet'; }
|
|
5887
5506
|
function physSolo() { var a = phys().answers; return a.builders_at_start === 1 && a.builders_expected === 1; }
|
|
5888
5507
|
function physHidden(option) { return physLimits.some(function (l) { return l && l.option === option && l.effect === 'hide'; }); }
|
|
5889
5508
|
/* a question the answers skip: rewards, when the table hides them (building alone) */
|
|
@@ -5901,88 +5520,34 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
5901
5520
|
if (q === 7) return !!a.framing;
|
|
5902
5521
|
return false;
|
|
5903
5522
|
}
|
|
5904
|
-
/*
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
function
|
|
5908
|
-
var p = phys();
|
|
5909
|
-
for (var n = q + 1; n <= PHYS_LAST; n++) {
|
|
5910
|
-
if (physSkips(n)) continue;
|
|
5911
|
-
if (!p.fromSum || !physAnswered(n)) return n;
|
|
5912
|
-
}
|
|
5913
|
-
return 'sum';
|
|
5914
|
-
}
|
|
5915
|
-
function physPrevQ(q) {
|
|
5916
|
-
var from = q === 'sum' ? PHYS_LAST + 1 : q;
|
|
5917
|
-
for (var n = from - 1; n >= 1; n--) if (!physSkips(n)) return n;
|
|
5918
|
-
return 0;
|
|
5919
|
-
}
|
|
5920
|
-
|
|
5921
|
-
/* ── the technical steps, pre-filled from the answers (task 1004419) ──
|
|
5922
|
-
What the answers suggest, applied only where the founder has not chosen for
|
|
5923
|
-
themselves (the field still holds its default, or what we filled last time), so
|
|
5924
|
-
walking back to change an answer moves the suggestion and never their choice. */
|
|
5925
|
-
var PHYS_PREFILL_DEFAULT = { projType: null, teamShape: null, mode: 'greenfield' };
|
|
5926
|
-
function physPrefillFor() {
|
|
5927
|
-
var a = phys().answers, out = {};
|
|
5928
|
-
if (PHYS_TYPE[a.entity]) out.projType = PHYS_TYPE[a.entity];
|
|
5929
|
-
if (physSolo()) out.teamShape = 'solo';
|
|
5930
|
-
if (a.project_state) out.mode = a.project_state === 'existing' && Array.isArray(a.existing_work) && a.existing_work.indexOf('code') !== -1 ? 'adopt' : 'greenfield';
|
|
5931
|
-
return out;
|
|
5932
|
-
}
|
|
5933
|
-
function physPrefill() {
|
|
5934
|
-
var want = physPrefillFor(), was = state.prefilled || {}, now = {};
|
|
5935
|
-
Object.keys(PHYS_PREFILL_DEFAULT).forEach(function (k) {
|
|
5936
|
-
var cur = state[k] === undefined ? PHYS_PREFILL_DEFAULT[k] : state[k];
|
|
5937
|
-
var untouched = cur === PHYS_PREFILL_DEFAULT[k] || (was[k] !== undefined && cur === was[k]);
|
|
5938
|
-
if (!untouched) return;
|
|
5939
|
-
if (want[k] !== undefined) { state[k] = want[k]; now[k] = want[k]; } else state[k] = PHYS_PREFILL_DEFAULT[k];
|
|
5940
|
-
});
|
|
5941
|
-
state.prefilled = now;
|
|
5942
|
-
/* a step the answers skip keeps its default answer */
|
|
5943
|
-
if (physStepSkipped(10)) state.appPlatform = 'later';
|
|
5944
|
-
if (typeof setMode === 'function') setMode(state.mode);
|
|
5945
|
-
if (typeof paintType === 'function') paintType(state.projType || null);
|
|
5946
|
-
if (typeof paintTeam === 'function') paintTeam(state.teamShape || null);
|
|
5947
|
-
saveDraft();
|
|
5948
|
-
return now;
|
|
5949
|
-
}
|
|
5950
|
-
/* The technical steps that do not apply to these answers: a planet built on its
|
|
5951
|
-
own machines (framing local, or open source) has no app platform to pick. A demo
|
|
5952
|
-
is all managed, whatever an abandoned walk through the questions left behind. */
|
|
5953
|
-
function physStepSkipped(n) {
|
|
5523
|
+
/* A planet built on its own machines (framing local, or open source) has no app
|
|
5524
|
+
platform to pick, so the review's App hosting row only says so. A demo is all
|
|
5525
|
+
managed, whatever an abandoned walk through the questions left behind. */
|
|
5526
|
+
function appSkipped() {
|
|
5954
5527
|
if (state.demo) return false;
|
|
5955
5528
|
var a = (state.physics && state.physics.answers) || {};
|
|
5956
|
-
return
|
|
5957
|
-
}
|
|
5958
|
-
/* the wizard's Next and Back step over a skipped step; the rail still reaches it */
|
|
5959
|
-
function physPastSkips(n, dir) {
|
|
5960
|
-
while (n > 0 && n < 13 && physStepSkipped(n)) n += dir;
|
|
5961
|
-
return n;
|
|
5962
|
-
}
|
|
5963
|
-
/* the line on the Type and Home steps saying the answer came from the questions */
|
|
5964
|
-
function physPaintPrefillNotes() {
|
|
5965
|
-
if (!PHYS_SAY) return; /* setMode runs once at boot, above where these are set */
|
|
5966
|
-
var pf = state.prefilled || {}, a = (state.physics && state.physics.answers) || {};
|
|
5967
|
-
var whose = PHYS_ORGS.indexOf(a.entity) !== -1 ? 'your project' : 'your planet';
|
|
5968
|
-
var t = $('typePrefill');
|
|
5969
|
-
var tOn = !!pf.projType && state.projType === pf.projType && !!PHYS_SAY.entity[a.entity];
|
|
5970
|
-
t.hidden = !tOn;
|
|
5971
|
-
t.textContent = tOn ? 'Picked from your answer “' + PHYS_SAY.entity[a.entity] + '”. Change it if another fits better.' : '';
|
|
5972
|
-
var h = $('homePrefill');
|
|
5973
|
-
var hOn = !!pf.mode && state.mode === pf.mode;
|
|
5974
|
-
h.hidden = !hOn;
|
|
5975
|
-
h.textContent = !hOn ? '' : pf.mode === 'adopt'
|
|
5976
|
-
? 'Adopt is picked because ' + whose + ' already has code. Choose Start fresh if it should begin again.'
|
|
5977
|
-
: a.project_state === 'existing'
|
|
5978
|
-
? 'Start fresh is picked because what ' + whose + ' has so far is not code.'
|
|
5979
|
-
: 'Start fresh is picked because ' + whose + ' is new.';
|
|
5529
|
+
return a.framing === 'local' || a.framing === 'open-source';
|
|
5980
5530
|
}
|
|
5531
|
+
/* What the create sends as `detail`. A demo sends only its team shape: the server
|
|
5532
|
+
applies the demo's own picked answers (task 1004420), as it always has. */
|
|
5981
5533
|
function createDetail() {
|
|
5982
|
-
var d = physDetail();
|
|
5534
|
+
var d = state.demo ? {} : physDetail();
|
|
5983
5535
|
if (state.teamShape) d.team_shape = state.teamShape;
|
|
5984
5536
|
return Object.keys(d).length ? d : undefined;
|
|
5985
5537
|
}
|
|
5538
|
+
/* The review's prefilled answers, written once on the way in and never over a
|
|
5539
|
+
founder's own: a new project, all managed, one person. Each still has to be
|
|
5540
|
+
confirmed on its row before Create — confirming is the answer. */
|
|
5541
|
+
function physDefaults() {
|
|
5542
|
+
var a = phys().answers;
|
|
5543
|
+
if (!a.project_state) a.project_state = 'new';
|
|
5544
|
+
if (!a.framing) a.framing = 'managed';
|
|
5545
|
+
if (!Number.isInteger(a.builders_at_start)) { a.builders_at_start = 1; a.builders_expected = 1; }
|
|
5546
|
+
if (physSolo()) state.teamShape = 'solo';
|
|
5547
|
+
else if (!state.teamShape || state.teamShape === 'solo') state.teamShape = 'small-team';
|
|
5548
|
+
if (state.mode === 'greenfield' && a.project_state === 'existing' && Array.isArray(a.existing_work) && a.existing_work.indexOf('code') !== -1 && !(state.confirmed || {}).code) setMode('adopt');
|
|
5549
|
+
saveDraft();
|
|
5550
|
+
}
|
|
5986
5551
|
|
|
5987
5552
|
function physFillSelects() {
|
|
5988
5553
|
var c = $('physCountry'), r = $('physRegion');
|
|
@@ -6003,100 +5568,30 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6003
5568
|
r.value = a.home_region || '';
|
|
6004
5569
|
}
|
|
6005
5570
|
|
|
6006
|
-
/* The stage: the planet forming from the world's own plates. A seed for a new
|
|
6007
|
-
planet, a formed one for an existing; the core material once the entity is
|
|
6008
|
-
known; a protective ring when young builders are on board. */
|
|
6009
|
-
function physPaintStage() {
|
|
6010
|
-
var p = phys(), a = p.answers;
|
|
6011
|
-
var mat = PHYS_MATERIAL[a.entity] || null;
|
|
6012
|
-
var src = mat && mat !== 'chrome' ? '/assets/world/r3-' + mat + '-blob-cut.webp'
|
|
6013
|
-
: a.project_state === 'existing' ? '/assets/world/r2-chrome-blobs-set-cut.webp'
|
|
6014
|
-
: '/assets/world/r2-chrome-blob-one-cut.webp';
|
|
6015
|
-
var img = $('physPlanet');
|
|
6016
|
-
if (img.getAttribute('src') !== src) img.setAttribute('src', src);
|
|
6017
|
-
var size = p.q === 'stop' ? 0 : p.q === 'sum' ? 6
|
|
6018
|
-
: Math.min(6, Math.max(1, Math.min(typeof p.q === 'number' ? p.q : 0, PHYS_LAST) + (a.project_state === 'existing' ? 2 : 1)));
|
|
6019
|
-
img.setAttribute('data-size', String(size));
|
|
6020
|
-
img.hidden = p.q === 'stop';
|
|
6021
|
-
$('physGuard').hidden = a.minors_on_board !== true || p.q === 'stop';
|
|
6022
|
-
$('physStage').classList.toggle('physStopped', p.q === 'stop');
|
|
6023
|
-
var pro = physVoice() === 'professional';
|
|
6024
|
-
$('physCapLbl').textContent = pro ? 'project profile' : 'planet physics';
|
|
6025
|
-
$('physCapN').textContent = p.q === 'stop' ? 'stopped' : p.q === 'sum' ? 'complete' : (typeof p.q === 'number' ? p.q : 0) + ' of 7';
|
|
6026
|
-
/* task 1004419: moons for the builders now (beads of the core's material), faint
|
|
6027
|
-
ones on the dashed orbit for the expected growth, a ring per reward being
|
|
6028
|
-
considered, and the orbit around the platform's mark the framing sets */
|
|
6029
|
-
var live = p.q !== 'stop';
|
|
6030
|
-
var at = live && Number.isInteger(a.builders_at_start) ? a.builders_at_start : 0;
|
|
6031
|
-
var exp = live && Number.isInteger(a.builders_expected) ? a.builders_expected : 0;
|
|
6032
|
-
var bead = '/assets/world/r3-' + (mat && mat !== 'chrome' ? mat : 'frost') + '-bead-cut.webp';
|
|
6033
|
-
var nMoons = Math.min(at, 6), nGhosts = Math.min(Math.max(exp - at, 0), 4);
|
|
6034
|
-
root.querySelectorAll('#physMoons img, #physGhosts img').forEach(function (m) { if (m.getAttribute('src') !== bead) m.setAttribute('src', bead); });
|
|
6035
|
-
root.querySelectorAll('#physMoons img').forEach(function (m, i) { m.hidden = i >= nMoons; });
|
|
6036
|
-
root.querySelectorAll('#physGhosts img').forEach(function (m, i) { m.hidden = i >= nGhosts; });
|
|
6037
|
-
$('physMoons').hidden = nMoons === 0;
|
|
6038
|
-
$('physGhosts').hidden = nGhosts === 0;
|
|
6039
|
-
var rewards = live && Array.isArray(a.reward_intents) && !physSolo() && !physHidden('reward_intents') ? a.reward_intents : [];
|
|
6040
|
-
root.querySelectorAll('#physStage .physBelt').forEach(function (b, i) { b.hidden = i >= rewards.length; });
|
|
6041
|
-
var orbit = live && a.framing ? PHYS_ORBIT[a.framing] : 'none';
|
|
6042
|
-
$('physStage').setAttribute('data-orbit', orbit);
|
|
6043
|
-
$('physSun').hidden = orbit === 'none' || orbit === 'own';
|
|
6044
|
-
$('physTether').hidden = orbit === 'none' || orbit === 'own';
|
|
6045
|
-
var rewardWords = rewards.map(function (r) { return PHYS_SAY.reward_intents[r].toLowerCase(); }).join(', ');
|
|
6046
|
-
var call = pro ? 'profile · each answer shapes your project here' : 'physics · the rules it lives by · each answer shapes your planet here';
|
|
6047
|
-
if (p.q === 'stop') call = 'no planet forms · stopped at state · nothing was created';
|
|
6048
|
-
else if (p.q === 'sum') call = pro ? 'profile · seven answers · ready for setup' : 'physics · seven answers, one planet · ready for its coordinates';
|
|
6049
|
-
else if (p.q === 7 && a.framing) call = pro
|
|
6050
|
-
? 'framing · ' + PHYS_SAY.framing[a.framing].toLowerCase()
|
|
6051
|
-
: 'orbit · ' + PHYS_SAY.framing[a.framing].toLowerCase() + ' · ' + PHYS_ORBIT_WORD[a.framing];
|
|
6052
|
-
else if (p.q === 6 && rewards.length) call = pro
|
|
6053
|
-
? 'rewards · ' + rewards.length + ' being considered · ' + rewardWords
|
|
6054
|
-
: 'rings · ' + rewards.length + (rewards.length === 1 ? ' current of value · ' : ' currents of value · ') + rewardWords;
|
|
6055
|
-
else if (p.q === 5 && at) call = physSolo()
|
|
6056
|
-
? (pro ? 'team · one person' : 'moons · just you · one moon')
|
|
6057
|
-
: (pro ? 'team · ' + at + (at === 1 ? ' contributor' : ' contributors') + ' · estimated ' + exp + ' by month three'
|
|
6058
|
-
: 'moons · ' + at + (at === 1 ? ' builder now' : ' builders now') + ' · the dashed orbit holds about ' + exp + ' by month three');
|
|
6059
|
-
else if (p.q === 4 && mat) call = 'core · ' + PHYS_CORE_WORD[mat] + ' · a ' + mat + ' core';
|
|
6060
|
-
else if (p.q === 3 && a.minors_on_board === true) call = 'protective ring · under 18s on board · the age tiers apply';
|
|
6061
|
-
else if (p.q === 2 && a.home_country) call = 'coordinates · ' + (a.home_region || a.home_country).toLowerCase() + (a.builder_geography ? ' · builders ' + PHYS_GEO_WORD[a.builder_geography] : '');
|
|
6062
|
-
else if (p.q === 1 && a.project_state) call = a.project_state === 'existing' ? 'state · existing · terrain already formed' : 'state · new · a seed: nothing built yet';
|
|
6063
|
-
$('physCallout').textContent = call;
|
|
6064
|
-
var coords = [];
|
|
6065
|
-
if (a.project_state) coords.push('state ' + a.project_state);
|
|
6066
|
-
if (a.home_country) coords.push('home ' + (a.home_region || a.home_country).toLowerCase());
|
|
6067
|
-
if (a.builder_geography) coords.push('builders ' + PHYS_GEO_WORD[a.builder_geography]);
|
|
6068
|
-
if (a.minors_on_board !== undefined && a.minors_on_board !== null) coords.push(a.minors_on_board ? 'ages 13+' : 'ages 18+');
|
|
6069
|
-
if (mat) coords.push('core ' + PHYS_CORE_WORD[mat]);
|
|
6070
|
-
if (at) coords.push((pro ? 'team ' : 'builders ') + (physSolo() ? '1' : at + ' → ' + exp));
|
|
6071
|
-
if (rewards.length) coords.push('rewards ' + rewards.map(function (r) { return r === 'future-payouts' ? 'future' : r; }).join(', '));
|
|
6072
|
-
if (live && a.framing) coords.push((pro ? 'framing ' : 'orbit ') + a.framing);
|
|
6073
|
-
$('physCoords').textContent = coords.join(' · ');
|
|
6074
|
-
}
|
|
6075
|
-
|
|
6076
5571
|
/* The soft-limit table's verdicts (spec D6): an option it hides is hidden and
|
|
6077
5572
|
one it greys is disabled, each with its reason beside it. */
|
|
6078
5573
|
/* Task 1004419 widened it to the list questions: a member the table hides is
|
|
6079
5574
|
hidden and one it greys disabled, and either leaves the founder's list (it can
|
|
6080
5575
|
no longer be chosen); a member's reasons gather once each in its question's
|
|
6081
5576
|
why list, naming the options they are about. A whole question it hides, the
|
|
6082
|
-
|
|
5577
|
+
review hides with its row (physSkips) or hides in place (data-limit). */
|
|
6083
5578
|
function physApplyLimits() {
|
|
6084
5579
|
var a = phys().answers;
|
|
6085
|
-
root.querySelectorAll('#
|
|
6086
|
-
root.querySelectorAll('#
|
|
6087
|
-
root.querySelectorAll('#
|
|
5580
|
+
root.querySelectorAll('#view-new [data-why]').forEach(function (el) { el.hidden = true; el.textContent = ''; });
|
|
5581
|
+
root.querySelectorAll('#view-new [data-limit-grey]').forEach(function (el) { el.removeAttribute('data-limit-grey'); el.disabled = false; el.title = ''; });
|
|
5582
|
+
root.querySelectorAll('#view-new [data-limit-hide]').forEach(function (el) { el.removeAttribute('data-limit-hide'); el.hidden = false; });
|
|
6088
5583
|
var lists = {};
|
|
6089
5584
|
physLimits.forEach(function (l) {
|
|
6090
5585
|
var key = String((l && l.option) || '');
|
|
6091
5586
|
var parts = key.split(':');
|
|
6092
|
-
var why = root.querySelector('#
|
|
5587
|
+
var why = root.querySelector('#view-new [data-why="' + key + '"]');
|
|
6093
5588
|
if (why && l.reason) { why.textContent = why.textContent ? why.textContent + ' ' + l.reason : l.reason; why.hidden = false; }
|
|
6094
5589
|
if (!parts[1]) {
|
|
6095
|
-
if (l.effect === 'hide') root.querySelectorAll('#
|
|
5590
|
+
if (l.effect === 'hide') root.querySelectorAll('#view-new [data-limit="' + key + '"]').forEach(function (el) { el.hidden = true; });
|
|
6096
5591
|
return;
|
|
6097
5592
|
}
|
|
6098
5593
|
/* a note is offered as it is, with its reason in the list below */
|
|
6099
|
-
root.querySelectorAll('#
|
|
5594
|
+
root.querySelectorAll('#view-new [data-phys="' + parts[0] + '"][data-val="' + parts[1] + '"]').forEach(function (b) {
|
|
6100
5595
|
if (l.effect === 'grey') { b.disabled = true; b.setAttribute('data-limit-grey', '1'); b.title = l.reason || ''; }
|
|
6101
5596
|
if (l.effect === 'hide') { b.hidden = true; b.setAttribute('data-limit-hide', '1'); }
|
|
6102
5597
|
});
|
|
@@ -6107,7 +5602,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6107
5602
|
var byReason = lists[parts[0]] = lists[parts[0]] || {};
|
|
6108
5603
|
(byReason[l.reason] = byReason[l.reason] || []).push(PHYS_SAY[parts[0]][parts[1]] || parts[1]);
|
|
6109
5604
|
});
|
|
6110
|
-
root.querySelectorAll('#
|
|
5605
|
+
root.querySelectorAll('#view-new [data-why-list]').forEach(function (ul) {
|
|
6111
5606
|
while (ul.firstChild) ul.removeChild(ul.firstChild);
|
|
6112
5607
|
var byReason = lists[ul.getAttribute('data-why-list')] || {};
|
|
6113
5608
|
Object.keys(byReason).forEach(function (reason) {
|
|
@@ -6119,59 +5614,37 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6119
5614
|
});
|
|
6120
5615
|
}
|
|
6121
5616
|
|
|
6122
|
-
|
|
6123
|
-
|
|
6124
|
-
|
|
6125
|
-
|
|
6126
|
-
});
|
|
6127
|
-
root.querySelectorAll('#physRail li').forEach(function (li) {
|
|
6128
|
-
var n = Number(li.getAttribute('data-q'));
|
|
6129
|
-
li.classList.toggle('now', n === p.q);
|
|
6130
|
-
li.classList.toggle('done', !!n && (p.q === 'sum' || (typeof p.q === 'number' && n < p.q)) && !physSkips(n));
|
|
6131
|
-
li.classList.toggle('skipped', physSkips(n));
|
|
6132
|
-
});
|
|
6133
|
-
$('physRail').hidden = p.q === 'stop' || p.q === 0;
|
|
6134
|
-
$('physNav').hidden = p.q === 'stop';
|
|
6135
|
-
physPaintVoice();
|
|
6136
|
-
root.querySelectorAll('[data-phys-start]').forEach(function (b) { b.setAttribute('aria-pressed', String(b.getAttribute('data-phys-start') === p.start)); });
|
|
6137
|
-
root.querySelectorAll('#panel-physics [data-phys]').forEach(function (b) {
|
|
5617
|
+
/* every [data-phys] pill, pressed or not, from the answers — on both screens */
|
|
5618
|
+
function physPaintPills() {
|
|
5619
|
+
var a = phys().answers;
|
|
5620
|
+
root.querySelectorAll('#view-new [data-phys]').forEach(function (b) {
|
|
6138
5621
|
var v = a[b.getAttribute('data-phys')], val = b.getAttribute('data-val');
|
|
6139
5622
|
var on = Array.isArray(v) ? v.indexOf(val) !== -1 : v !== undefined && v !== null && String(v) === val;
|
|
6140
5623
|
b.setAttribute('aria-pressed', String(on));
|
|
6141
5624
|
});
|
|
6142
|
-
|
|
6143
|
-
|
|
6144
|
-
|
|
6145
|
-
|
|
6146
|
-
|
|
6147
|
-
|
|
6148
|
-
|
|
6149
|
-
var existing = a.project_state === 'existing';
|
|
6150
|
-
$('physExisting').hidden = !existing;
|
|
6151
|
-
var works = Array.isArray(a.existing_work) ? a.existing_work : [];
|
|
6152
|
-
root.querySelectorAll('#physExisting [data-work]').forEach(function (cb) { cb.checked = works.indexOf(cb.getAttribute('data-work')) !== -1; });
|
|
6153
|
-
root.querySelectorAll('#physExisting input[name="physMain"]').forEach(function (r) {
|
|
6154
|
-
r.checked = a.main_work === r.value;
|
|
6155
|
-
r.disabled = works.indexOf(r.value) === -1;
|
|
6156
|
-
});
|
|
6157
|
-
$('physDesc').value = state.description || '';
|
|
5625
|
+
}
|
|
5626
|
+
/* screen 3, the quick checks: country and region, where builders will be, anyone
|
|
5627
|
+
under 18 (the tiers shown for a yes), and who is behind it (the full list of legal
|
|
5628
|
+
entities for someone who already has one) */
|
|
5629
|
+
function paintChecks() {
|
|
5630
|
+
var p = phys(), a = p.answers;
|
|
5631
|
+
physPaintPills();
|
|
6158
5632
|
$('physAges').hidden = a.minors_on_board !== true;
|
|
6159
|
-
var forms =
|
|
5633
|
+
var forms = PHYS_ENTITY_FORMS.indexOf(a.entity) !== -1 || p.entityForms === true;
|
|
6160
5634
|
$('physForms').hidden = !forms;
|
|
6161
5635
|
$('physIntent').hidden = forms;
|
|
6162
|
-
if (p.q === 0 && !physAccountLoaded) { physAccountLoaded = true; physLoadAccount(); }
|
|
6163
5636
|
physFillSelects();
|
|
6164
5637
|
physApplyLimits();
|
|
6165
|
-
physPaintStage();
|
|
6166
5638
|
}
|
|
6167
|
-
|
|
6168
|
-
|
|
6169
|
-
|
|
6170
|
-
|
|
6171
|
-
|
|
5639
|
+
function checksShow(which) {
|
|
5640
|
+
$('checksMain').hidden = which !== 'main';
|
|
5641
|
+
$('checksStop').hidden = which !== 'stop';
|
|
5642
|
+
$('wizBack').hidden = false;
|
|
5643
|
+
var h = which === 'stop' ? $('phys-hstop') : $('checks-h');
|
|
5644
|
+
h.setAttribute('tabindex', '-1'); h.focus({ preventScroll: false });
|
|
6172
5645
|
}
|
|
6173
5646
|
|
|
6174
|
-
/*
|
|
5647
|
+
/* the Team size row's controls: one or many, the start count, and the month-three guess */
|
|
6175
5648
|
function physPaintTeam() {
|
|
6176
5649
|
var a = phys().answers, many = Number.isInteger(a.builders_at_start) && !physSolo();
|
|
6177
5650
|
root.querySelectorAll('[data-phys-solo]').forEach(function (b) {
|
|
@@ -6179,85 +5652,23 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6179
5652
|
b.setAttribute('aria-pressed', String(on));
|
|
6180
5653
|
});
|
|
6181
5654
|
$('physTeam').hidden = !many;
|
|
5655
|
+
paintTeam(state.teamShape || null);
|
|
6182
5656
|
if (!many) return;
|
|
6183
5657
|
var start = a.builders_at_start, exp = a.builders_expected;
|
|
6184
|
-
var pro = physVoice() === 'professional';
|
|
6185
5658
|
$('physStartN').textContent = String(start);
|
|
6186
|
-
$('physM1').textContent = String(start);
|
|
6187
|
-
$('physM2').textContent = String(Math.round(Math.sqrt(start * exp)));
|
|
6188
|
-
$('physM3').textContent = String(exp);
|
|
6189
5659
|
var range = $('physExpected');
|
|
6190
5660
|
range.setAttribute('min', String(Math.max(2, start)));
|
|
6191
5661
|
range.setAttribute('max', String(Math.min(100000, Math.max(30, physGuess(start) * 3, exp))));
|
|
6192
5662
|
range.value = String(exp);
|
|
6193
|
-
range.setAttribute('aria-valuetext', exp +
|
|
6194
|
-
$('physGuessLine').textContent =
|
|
6195
|
-
? 'Based on your project, we estimate ' + exp + ' contributors by month three. Adjust it if you know better.'
|
|
6196
|
-
: 'From what you’re building, we’d expect about ' + exp + ' builders by month three. Nobody can estimate builder growth cold, so we guess first and you correct.';
|
|
5663
|
+
range.setAttribute('aria-valuetext', exp + ' people by month three');
|
|
5664
|
+
$('physGuessLine').textContent = 'We guess about ' + exp + ' people by month three. Adjust it if you know better.';
|
|
6197
5665
|
}
|
|
6198
5666
|
|
|
6199
|
-
/* the summary: every answer, each with a way back to its question */
|
|
6200
5667
|
function physCountryName(code) {
|
|
6201
5668
|
try { return new Intl.DisplayNames(['en'], { type: 'region' }).of(code) || code; } catch (e) { return code; }
|
|
6202
5669
|
}
|
|
6203
|
-
function physSummaryRows() {
|
|
6204
|
-
var a = phys().answers, pro = physVoice() === 'professional';
|
|
6205
|
-
var rows = [];
|
|
6206
|
-
var told = String(state.description || '').trim();
|
|
6207
|
-
rows.push({ q: 1, k: 'State', v: PHYS_SAY.project_state[a.project_state] || 'Not answered', s: told });
|
|
6208
|
-
rows.push({ q: 2, k: 'Geography', v: a.home_country ? physCountryName(a.home_country) + (a.home_region && PHYS_REGIONS[a.home_region] ? ' (' + PHYS_REGIONS[a.home_region] + ')' : '') : 'Not answered',
|
|
6209
|
-
s: PHYS_SAY.builder_geography[a.builder_geography] || '' });
|
|
6210
|
-
rows.push({ q: 3, k: 'Under 18', v: a.minors_on_board === true ? 'Yes' : a.minors_on_board === false ? 'No' : 'Not answered',
|
|
6211
|
-
s: a.minors_on_board === true ? 'The age tiers apply, and a guardian signs for each young builder' : a.minors_on_board === false ? 'Everyone building is an adult' : '' });
|
|
6212
|
-
rows.push({ q: 4, k: pro ? 'Entity' : 'You are', v: PHYS_SAY.entity[a.entity] || 'Not answered',
|
|
6213
|
-
s: PHYS_ENTITY_FORMS.indexOf(a.entity) !== -1 ? 'An entity already formed' : a.entity ? 'The binding choice comes later' : '' });
|
|
6214
|
-
var solo = physSolo();
|
|
6215
|
-
rows.push({ q: 5, k: pro ? 'Team' : 'Builders', v: solo ? 'Just you' : Number.isInteger(a.builders_at_start) ? a.builders_at_start + ' now' : 'Not answered',
|
|
6216
|
-
s: solo ? (pro ? 'A single contributor' : 'Building solo') : Number.isInteger(a.builders_expected) ? 'About ' + a.builders_expected + ' by month three' : '' });
|
|
6217
|
-
var hiddenWhy = physLimits.filter(function (l) { return l && l.option === 'reward_intents' && l.effect === 'hide'; })[0];
|
|
6218
|
-
var rewards = Array.isArray(a.reward_intents) ? a.reward_intents : [];
|
|
6219
|
-
if (physSkips(6)) rows.push({ q: 5, k: 'Rewards', v: 'Not asked', s: hiddenWhy ? hiddenWhy.reason : '' });
|
|
6220
|
-
else rows.push({ q: 6, k: 'Rewards', v: rewards.length ? PHYS_REWARDS.filter(function (r) { return rewards.indexOf(r) !== -1; }).map(function (r) { return PHYS_SAY.reward_intents[r]; }).join(', ') : 'Not answered',
|
|
6221
|
-
s: rewards.length ? 'Intent only' : '' });
|
|
6222
|
-
var services = Array.isArray(a.services) ? a.services : [];
|
|
6223
|
-
rows.push({ q: 7, k: 'Framing', v: PHYS_SAY.framing[a.framing] || 'Not answered',
|
|
6224
|
-
s: a.framing === 'managed' ? 'The whole suite, run for you'
|
|
6225
|
-
: a.framing === 'open-source' ? 'The free core, on your own machines'
|
|
6226
|
-
: (a.framing === 'hybrid' || a.framing === 'local') ? (services.length ? PHYS_SERVICES.filter(function (x) { return services.indexOf(x) !== -1; }).map(function (x) { return PHYS_SAY.services[x]; }).join(', ') : 'No services picked yet') : '' });
|
|
6227
|
-
return rows;
|
|
6228
|
-
}
|
|
6229
|
-
function physPaintSummary() {
|
|
6230
|
-
var dl = $('physSummary');
|
|
6231
|
-
while (dl.firstChild) dl.removeChild(dl.firstChild);
|
|
6232
|
-
physSummaryRows().forEach(function (r) {
|
|
6233
|
-
var dt = document.createElement('dt');
|
|
6234
|
-
dt.textContent = r.k;
|
|
6235
|
-
var dd = document.createElement('dd');
|
|
6236
|
-
var body = document.createElement('span');
|
|
6237
|
-
body.textContent = r.v;
|
|
6238
|
-
if (r.s) { var sm = document.createElement('small'); sm.textContent = r.s; body.appendChild(sm); }
|
|
6239
|
-
var change = document.createElement('button');
|
|
6240
|
-
change.type = 'button';
|
|
6241
|
-
change.className = 'physLink';
|
|
6242
|
-
change.textContent = 'change';
|
|
6243
|
-
change.setAttribute('data-phys-goto', String(r.q));
|
|
6244
|
-
change.setAttribute('aria-label', 'Change ' + r.k.toLowerCase());
|
|
6245
|
-
change.addEventListener('click', function () { physChange(r.q); });
|
|
6246
|
-
dd.appendChild(body);
|
|
6247
|
-
dd.appendChild(change);
|
|
6248
|
-
dl.appendChild(dt);
|
|
6249
|
-
dl.appendChild(dd);
|
|
6250
|
-
});
|
|
6251
|
-
}
|
|
6252
|
-
/* "change": back to that question; Continue from it returns to the summary */
|
|
6253
|
-
function physChange(q) {
|
|
6254
|
-
phys().fromSum = true;
|
|
6255
|
-
physHint('');
|
|
6256
|
-
physGo(q);
|
|
6257
|
-
}
|
|
6258
|
-
|
|
6259
5670
|
function physHint(msg) {
|
|
6260
|
-
$('h-phys').textContent = msg || '
|
|
5671
|
+
$('h-phys').textContent = msg || ' ';
|
|
6261
5672
|
$('h-phys').className = msg ? 'hint err' : 'hint';
|
|
6262
5673
|
}
|
|
6263
5674
|
function physCheck() {
|
|
@@ -6266,101 +5677,50 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6266
5677
|
body: JSON.stringify({ description: String(state.description || '').trim() || undefined, detail: physDetail() })
|
|
6267
5678
|
}).then(function (r) { return r.ok ? r.json() : null; }).catch(function () { return null; });
|
|
6268
5679
|
}
|
|
6269
|
-
|
|
6270
|
-
|
|
6271
|
-
|
|
6272
|
-
|
|
6273
|
-
|
|
6274
|
-
|
|
6275
|
-
|
|
5680
|
+
/* What the table hides follows the team and the framing, so a change to either on
|
|
5681
|
+
the review asks it again and repaints the rows it touches. */
|
|
5682
|
+
function physRecheck() {
|
|
5683
|
+
physCheck().then(function (res) {
|
|
5684
|
+
if (!res || !res.soft_limits) return;
|
|
5685
|
+
physLimits = res.soft_limits.limits || [];
|
|
5686
|
+
physApplyLimits();
|
|
5687
|
+
paintSetup();
|
|
5688
|
+
});
|
|
6276
5689
|
}
|
|
6277
5690
|
|
|
6278
|
-
/* Continue
|
|
6279
|
-
|
|
5691
|
+
/* Continue on the quick checks: every answer given, then the server's screen — the
|
|
5692
|
+
one the create runs — with the description from screen 2. A stop ends here,
|
|
5693
|
+
before anything exists; otherwise its soft limits shape the review. */
|
|
6280
5694
|
var physBusy = false;
|
|
6281
|
-
function
|
|
6282
|
-
var
|
|
5695
|
+
function checksAdvance() {
|
|
5696
|
+
var a = phys().answers;
|
|
6283
5697
|
physHint('');
|
|
6284
|
-
if (
|
|
6285
|
-
if (
|
|
6286
|
-
|
|
6287
|
-
|
|
6288
|
-
|
|
6289
|
-
|
|
6290
|
-
|
|
6291
|
-
|
|
6292
|
-
|
|
6293
|
-
|
|
6294
|
-
|
|
6295
|
-
physBusy = false;
|
|
6296
|
-
if (res && res.stop) { $('physStopMsg').textContent = res.message || ''; physGo('stop'); return; }
|
|
6297
|
-
physLimits = (res && res.soft_limits && res.soft_limits.limits) || [];
|
|
6298
|
-
physGo(physNextQ(1));
|
|
6299
|
-
});
|
|
6300
|
-
return false;
|
|
6301
|
-
}
|
|
6302
|
-
if (p.q === 2) {
|
|
6303
|
-
if (!a.home_country) { physHint('Pick your country.'); return false; }
|
|
6304
|
-
if (!a.builder_geography) { physHint('Say where you expect builders to be. Not sure yet is a fine answer.'); return false; }
|
|
6305
|
-
physGo(physNextQ(2)); return false;
|
|
6306
|
-
}
|
|
6307
|
-
if (p.q === 3) {
|
|
6308
|
-
if (a.minors_on_board !== true && a.minors_on_board !== false) { physHint('Answer yes or no to go on.'); return false; }
|
|
6309
|
-
physGo(physNextQ(3)); return false;
|
|
6310
|
-
}
|
|
6311
|
-
/* from question 4 on, the table is asked again on every Continue: what it hides
|
|
6312
|
-
or greys on the next screens depends on the answers just given */
|
|
6313
|
-
if (p.q >= 4 && p.q <= PHYS_LAST) {
|
|
6314
|
-
if (!physAnswered(p.q)) {
|
|
6315
|
-
physHint(p.q === 4 ? 'Pick the one that fits best. Don’t know yet is a fine answer.'
|
|
6316
|
-
: p.q === 5 ? 'Say one or many to go on.'
|
|
6317
|
-
: p.q === 6 ? 'Pick at least one. Non-monetary is a fine answer.'
|
|
6318
|
-
: 'Pick how much should be run for you to go on.');
|
|
6319
|
-
return false;
|
|
6320
|
-
}
|
|
6321
|
-
if (physBusy) return false;
|
|
6322
|
-
physBusy = true;
|
|
6323
|
-
var from = p.q;
|
|
6324
|
-
physCheck().then(function (res) {
|
|
6325
|
-
physBusy = false;
|
|
6326
|
-
if (res && res.soft_limits) physLimits = res.soft_limits.limits || [];
|
|
6327
|
-
physGo(physNextQ(from));
|
|
6328
|
-
});
|
|
6329
|
-
return false;
|
|
6330
|
-
}
|
|
6331
|
-
/* the summary's Create my planet: the answers pre-fill the technical steps,
|
|
6332
|
-
and the wizard moves on to the first of them, the name */
|
|
6333
|
-
if (p.q === 'sum') {
|
|
6334
|
-
physPrefill();
|
|
5698
|
+
if (!a.home_country) { physHint('Pick your country.'); return false; }
|
|
5699
|
+
if (!a.builder_geography) { physHint('Say where builders will be. Not sure is a fine answer.'); return false; }
|
|
5700
|
+
if (a.minors_on_board !== true && a.minors_on_board !== false) { physHint('Answer yes or no for builders under 18.'); return false; }
|
|
5701
|
+
if (!a.entity) { physHint('Say who’s behind it. Not sure yet is a fine answer.'); return false; }
|
|
5702
|
+
if (physBusy) return false;
|
|
5703
|
+
physBusy = true;
|
|
5704
|
+
physDefaults();
|
|
5705
|
+
physCheck().then(function (res) {
|
|
5706
|
+
physBusy = false;
|
|
5707
|
+
if (res && res.stop) { $('physStopMsg').textContent = res.message || ''; checksShow('stop'); return; }
|
|
5708
|
+
physLimits = (res && res.soft_limits && res.soft_limits.limits) || [];
|
|
6335
5709
|
show(4, true);
|
|
6336
|
-
|
|
6337
|
-
}
|
|
5710
|
+
});
|
|
6338
5711
|
return false;
|
|
6339
5712
|
}
|
|
6340
5713
|
|
|
6341
|
-
$('physBack').addEventListener('click', function () {
|
|
6342
|
-
var p = phys();
|
|
6343
|
-
physHint('');
|
|
6344
|
-
p.fromSum = false;
|
|
6345
|
-
if (p.q === 'sum' || (typeof p.q === 'number' && p.q > 0)) physGo(physPrevQ(p.q)); else show(2, true);
|
|
6346
|
-
});
|
|
6347
5714
|
$('physStartOver').addEventListener('click', function () {
|
|
6348
5715
|
state.description = '';
|
|
6349
5716
|
if ($('f-desc')) $('f-desc').value = '';
|
|
6350
5717
|
phys().answers = {};
|
|
6351
|
-
|
|
6352
|
-
|
|
6353
|
-
|
|
6354
|
-
|
|
6355
|
-
var p = phys();
|
|
6356
|
-
p.start = b.getAttribute('data-phys-start');
|
|
6357
|
-
if (p.start === 'account' && physAccountAnswers) {
|
|
6358
|
-
Object.keys(physAccountAnswers).forEach(function (k) { p.answers[k] = physAccountAnswers[k]; });
|
|
6359
|
-
}
|
|
6360
|
-
saveDraft(); paintPhysics();
|
|
6361
|
-
});
|
|
5718
|
+
state.confirmed = {};
|
|
5719
|
+
saveDraft();
|
|
5720
|
+
show(2, true);
|
|
5721
|
+
checksShow('main');
|
|
6362
5722
|
});
|
|
6363
|
-
root.querySelectorAll('#
|
|
5723
|
+
root.querySelectorAll('#view-new [data-phys]').forEach(function (b) {
|
|
6364
5724
|
b.addEventListener('click', function () {
|
|
6365
5725
|
var k = b.getAttribute('data-phys'), raw = b.getAttribute('data-val');
|
|
6366
5726
|
var v = raw === 'true' ? true : raw === 'false' ? false : raw;
|
|
@@ -6377,7 +5737,9 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6377
5737
|
}
|
|
6378
5738
|
if (k === 'project_state' && a[k] !== 'existing') { delete a.existing_work; delete a.main_work; }
|
|
6379
5739
|
if (k === 'framing') physFramingChanged();
|
|
6380
|
-
physHint(''); saveDraft();
|
|
5740
|
+
physHint(''); saveDraft();
|
|
5741
|
+
paintChecks();
|
|
5742
|
+
paintSetup();
|
|
6381
5743
|
});
|
|
6382
5744
|
});
|
|
6383
5745
|
/* A hybrid or local framing starts from the canvas's suite until the founder picks
|
|
@@ -6385,22 +5747,22 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6385
5747
|
function physFramingChanged() {
|
|
6386
5748
|
var p = phys(), a = p.answers;
|
|
6387
5749
|
if ((a.framing === 'hybrid' || a.framing === 'local') && !p.suiteTouched) a.services = PHYS_SUITE_START[a.framing].slice();
|
|
6388
|
-
|
|
6389
|
-
|
|
6390
|
-
physLimits = res.soft_limits.limits || [];
|
|
6391
|
-
paintPhysics();
|
|
6392
|
-
});
|
|
5750
|
+
unconfirm(['app']);
|
|
5751
|
+
physRecheck();
|
|
6393
5752
|
}
|
|
6394
|
-
/*
|
|
5753
|
+
/* the Team size row's controls: one or many, the start count, the guess */
|
|
6395
5754
|
root.querySelectorAll('[data-phys-solo]').forEach(function (b) {
|
|
6396
5755
|
b.addEventListener('click', function () {
|
|
6397
5756
|
var p = phys(), a = p.answers;
|
|
6398
5757
|
if (b.getAttribute('data-phys-solo') === 'one') {
|
|
6399
5758
|
a.builders_at_start = 1; a.builders_expected = 1; p.guessTouched = false;
|
|
5759
|
+
state.teamShape = 'solo';
|
|
6400
5760
|
} else if (physSolo() || !Number.isInteger(a.builders_at_start)) {
|
|
6401
5761
|
a.builders_at_start = 2; a.builders_expected = physGuess(2); p.guessTouched = false;
|
|
5762
|
+
state.teamShape = 'small-team';
|
|
6402
5763
|
}
|
|
6403
|
-
|
|
5764
|
+
unconfirm(['rewards', 'extras']);
|
|
5765
|
+
saveDraft(); paintSetup(); physRecheck();
|
|
6404
5766
|
});
|
|
6405
5767
|
});
|
|
6406
5768
|
root.querySelectorAll('[data-phys-count]').forEach(function (b) {
|
|
@@ -6412,7 +5774,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6412
5774
|
expects company, or it would read as building alone */
|
|
6413
5775
|
if (!p.guessTouched) a.builders_expected = physGuess(start);
|
|
6414
5776
|
a.builders_expected = Math.max(a.builders_expected || 0, start, 2);
|
|
6415
|
-
saveDraft();
|
|
5777
|
+
saveDraft(); paintSetup();
|
|
6416
5778
|
});
|
|
6417
5779
|
});
|
|
6418
5780
|
$('physExpected').addEventListener('input', function () {
|
|
@@ -6420,7 +5782,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6420
5782
|
if (!Number.isFinite(n)) return;
|
|
6421
5783
|
a.builders_expected = Math.max(n, a.builders_at_start || 1, 2);
|
|
6422
5784
|
p.guessTouched = true;
|
|
6423
|
-
saveDraft();
|
|
5785
|
+
saveDraft(); paintSetup();
|
|
6424
5786
|
});
|
|
6425
5787
|
root.querySelectorAll('#physExisting [data-work]').forEach(function (cb) {
|
|
6426
5788
|
cb.addEventListener('change', function () {
|
|
@@ -6431,35 +5793,30 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6431
5793
|
a.existing_work = list;
|
|
6432
5794
|
if (list.length === 1) a.main_work = list[0];
|
|
6433
5795
|
if (list.indexOf(a.main_work) === -1) a.main_work = list.length ? list[0] : null;
|
|
6434
|
-
saveDraft();
|
|
5796
|
+
saveDraft(); paintSetup();
|
|
6435
5797
|
});
|
|
6436
5798
|
});
|
|
6437
5799
|
root.querySelectorAll('#physExisting input[name="physMain"]').forEach(function (r) {
|
|
6438
|
-
r.addEventListener('change', function () { if (r.checked) { phys().answers.main_work = r.value; saveDraft();
|
|
6439
|
-
});
|
|
6440
|
-
$('physDesc').addEventListener('input', function () {
|
|
6441
|
-
state.description = $('physDesc').value;
|
|
6442
|
-
if ($('f-desc')) $('f-desc').value = state.description;
|
|
6443
|
-
saveDraft();
|
|
5800
|
+
r.addEventListener('change', function () { if (r.checked) { phys().answers.main_work = r.value; saveDraft(); paintSetup(); } });
|
|
6444
5801
|
});
|
|
6445
5802
|
$('physCountry').addEventListener('change', function () {
|
|
6446
5803
|
var a = phys().answers;
|
|
6447
5804
|
a.home_country = $('physCountry').value || null;
|
|
6448
5805
|
if (a.home_region && a.home_region.indexOf((a.home_country || '') + '-') !== 0) a.home_region = null;
|
|
6449
|
-
saveDraft();
|
|
5806
|
+
physHint(''); saveDraft(); paintChecks();
|
|
6450
5807
|
});
|
|
6451
|
-
$('physRegion').addEventListener('change', function () { phys().answers.home_region = $('physRegion').value || null; saveDraft();
|
|
6452
|
-
$('physHaveEntity').addEventListener('click', function () { phys().entityForms = true; saveDraft();
|
|
5808
|
+
$('physRegion').addEventListener('change', function () { phys().answers.home_region = $('physRegion').value || null; saveDraft(); });
|
|
5809
|
+
$('physHaveEntity').addEventListener('click', function () { phys().entityForms = true; saveDraft(); paintChecks(); });
|
|
6453
5810
|
$('physNoEntity').addEventListener('click', function () {
|
|
6454
5811
|
var p = phys();
|
|
6455
5812
|
p.entityForms = false;
|
|
6456
5813
|
if (PHYS_ENTITY_FORMS.indexOf(p.answers.entity) !== -1) p.answers.entity = null;
|
|
6457
|
-
saveDraft();
|
|
5814
|
+
saveDraft(); paintChecks();
|
|
6458
5815
|
});
|
|
6459
5816
|
|
|
6460
5817
|
/* Auto-fill: the answers this account already gave on its most recent project —
|
|
6461
|
-
the only details the platform holds about a founder. With none, the
|
|
6462
|
-
|
|
5818
|
+
the only details the platform holds about a founder. With none, the offer stays
|
|
5819
|
+
hidden. The state of the project is never copied: it is this project's. */
|
|
6463
5820
|
var physAccountAnswers = null;
|
|
6464
5821
|
var physAccountLoaded = false;
|
|
6465
5822
|
function physLoadAccount() {
|
|
@@ -6475,101 +5832,114 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6475
5832
|
if (withDetail.detail[k] !== undefined && withDetail.detail[k] !== null) pick[k] = withDetail.detail[k];
|
|
6476
5833
|
});
|
|
6477
5834
|
physAccountAnswers = Object.keys(pick).length ? pick : null;
|
|
6478
|
-
$('
|
|
6479
|
-
var why = $('physAccountWhy');
|
|
6480
|
-
why.hidden = !!physAccountAnswers;
|
|
6481
|
-
why.textContent = physAccountAnswers ? '' : 'Nothing to fill from yet: your account has no other project with these answers.';
|
|
5835
|
+
$('physFillOffer').hidden = !physAccountAnswers;
|
|
6482
5836
|
})
|
|
6483
5837
|
.catch(function () {});
|
|
6484
5838
|
}
|
|
5839
|
+
function physLoadAccountOnce() {
|
|
5840
|
+
if (physAccountLoaded || !authKnown) return;
|
|
5841
|
+
physAccountLoaded = true;
|
|
5842
|
+
physLoadAccount();
|
|
5843
|
+
}
|
|
5844
|
+
$('physFromAccount').addEventListener('click', function () {
|
|
5845
|
+
if (!physAccountAnswers) return;
|
|
5846
|
+
var a = phys().answers;
|
|
5847
|
+
Object.keys(physAccountAnswers).forEach(function (k) { a[k] = physAccountAnswers[k]; });
|
|
5848
|
+
physHint(''); saveDraft(); paintChecks();
|
|
5849
|
+
});
|
|
6485
5850
|
|
|
6486
|
-
/*
|
|
5851
|
+
/* ── checks the old steps made on Next, now made by their rows ── */
|
|
5852
|
+
function validateCode() {
|
|
5853
|
+
var a = phys().answers;
|
|
5854
|
+
var homeErr = function (msg) { $('h-home').textContent = msg; $('h-home').className = 'hint err'; return false; };
|
|
5855
|
+
if (a.project_state === 'existing') {
|
|
5856
|
+
var works = Array.isArray(a.existing_work) ? a.existing_work : [];
|
|
5857
|
+
if (!works.length) return homeErr('Pick what already exists, then your main one.');
|
|
5858
|
+
if (works.indexOf(a.main_work) === -1) return homeErr('Mark which of them is your main one.');
|
|
5859
|
+
}
|
|
5860
|
+
if (repoAccess === 'need-connect') return homeErr('Allow GitHub repo access to pick or create your repository — or choose “enter it manually”.');
|
|
5861
|
+
if (repoAccess === true && state.mode !== 'adopt') {
|
|
5862
|
+
/* Fresh with access: just the new repo NAME (owner is the signed-in account). */
|
|
5863
|
+
if (!validRepoName(state.repo)) return homeErr('Name the new repository (letters, numbers, and . _ -).');
|
|
5864
|
+
} else {
|
|
5865
|
+
/* Adopt (picker or manual) and the manual fallback: need a concrete owner + repo. */
|
|
5866
|
+
if (!state.owner || !state.repo) {
|
|
5867
|
+
return homeErr(state.mode === 'adopt' && repoAccess === true ? 'Pick one of your repositories.' : 'Both owner and repository name are needed.');
|
|
5868
|
+
}
|
|
5869
|
+
}
|
|
5870
|
+
$('h-home').innerHTML = ' '; $('h-home').className = 'hint';
|
|
5871
|
+
return true;
|
|
5872
|
+
}
|
|
5873
|
+
function validateAddress() {
|
|
5874
|
+
if (!validSlug(state.slug)) {
|
|
5875
|
+
$('h-slug').textContent = '2–40 lowercase letters, numbers, or dashes; no leading/trailing/double dash.';
|
|
5876
|
+
$('h-slug').className = 'hint err'; $('f-slug').classList.add('bad');
|
|
5877
|
+
return false;
|
|
5878
|
+
}
|
|
5879
|
+
if (state.domain && !looksLikeDomain(state.domain)) {
|
|
5880
|
+
$('addrCustom').open = true;
|
|
5881
|
+
var hd0 = $('h-domain');
|
|
5882
|
+
hd0.textContent = 'That doesn’t look like a domain yet (e.g. moonbase.example.com) — fix it, or clear the field to use the address above.';
|
|
5883
|
+
hd0.className = 'hint err';
|
|
5884
|
+
return false;
|
|
5885
|
+
}
|
|
5886
|
+
/* A handle the server has already said is not free would only 409 at Create —
|
|
5887
|
+
stop here instead (task 1002898 N10). ONLY a definite negative blocks:
|
|
5888
|
+
'unknown' and 'checking' must never gate it, because the check is advisory
|
|
5889
|
+
and the 409 remains the authority. A chosen domain or the opt-out settles the
|
|
5890
|
+
address another way, so the block does not apply there. */
|
|
5891
|
+
if (!state.domain && !state.noAddress) {
|
|
5892
|
+
var av = slugAvailState();
|
|
5893
|
+
if (av === 'taken' || av === 'yours' || av === 'reserved') {
|
|
5894
|
+
$('h-slug').textContent = av === 'taken'
|
|
5895
|
+
? 'That handle is already taken — pick a different one.'
|
|
5896
|
+
: av === 'yours'
|
|
5897
|
+
? 'You already have a project with that handle — pick a different one.'
|
|
5898
|
+
: 'That handle is reserved by the platform — pick a different one.';
|
|
5899
|
+
$('h-slug').className = 'hint err'; $('f-slug').classList.add('bad');
|
|
5900
|
+
return false;
|
|
5901
|
+
}
|
|
5902
|
+
}
|
|
5903
|
+
/* no auto-assigned address (dotless host): going domainless must be an
|
|
5904
|
+
EXPLICIT choice — a domain, or the opt-out box. Never silent. */
|
|
5905
|
+
if (!resolvedDomain() && !state.noAddress) {
|
|
5906
|
+
$('addrCustom').open = true;
|
|
5907
|
+
var hd = $('h-domain');
|
|
5908
|
+
hd.textContent = 'Add a domain you own — or tick “no address yet” to go without one.';
|
|
5909
|
+
hd.className = 'hint err';
|
|
5910
|
+
return false;
|
|
5911
|
+
}
|
|
5912
|
+
return true;
|
|
5913
|
+
}
|
|
5914
|
+
|
|
5915
|
+
/* per-screen validation on Continue */
|
|
6487
5916
|
function validateStep(step) {
|
|
6488
|
-
|
|
6489
|
-
|
|
6490
|
-
and only the last one moves the wizard on, so this never says yes itself */
|
|
6491
|
-
if (step === 3) return physAdvance();
|
|
6492
|
-
/* the fork's own door is "Begin planet creation": a real project, not the demo */
|
|
6493
|
-
if (step === 2) { state.demo = null; return true; }
|
|
6494
|
-
/* the front door's one tick (task 1004417): nothing past it until it is given */
|
|
5917
|
+
/* the front door's one tick (task 1004417): nothing past it until it is given.
|
|
5918
|
+
The fork's own submit door is "Start my project": a real project, not the demo. */
|
|
6495
5919
|
if (step === 1) {
|
|
6496
5920
|
if (!state.termsAck) {
|
|
6497
|
-
$('h-terms').textContent = 'Tick the box to go on. It’s how your project records that you
|
|
5921
|
+
$('h-terms').textContent = 'Tick the box to go on. It’s how your project records that you accepted the terms.';
|
|
6498
5922
|
$('h-terms').className = 'hint err';
|
|
6499
5923
|
return false;
|
|
6500
5924
|
}
|
|
5925
|
+
if (state.demo) { state.demo = null; state.confirmed = {}; }
|
|
6501
5926
|
return true;
|
|
6502
5927
|
}
|
|
6503
|
-
if (step ===
|
|
5928
|
+
if (step === 2) {
|
|
6504
5929
|
if (!state.name.trim()) { $('h-name').textContent = 'Give it a name to start.'; $('h-name').className = 'hint err'; return false; }
|
|
6505
|
-
return true;
|
|
6506
|
-
}
|
|
6507
|
-
if (step === 5) {
|
|
6508
5930
|
if (!state.projType) {
|
|
6509
|
-
$('h-projtype').textContent = 'Pick one — Not
|
|
5931
|
+
$('h-projtype').textContent = 'Pick one — Not sure is a fine answer.';
|
|
6510
5932
|
$('h-projtype').className = 'hint err';
|
|
6511
5933
|
return false;
|
|
6512
5934
|
}
|
|
6513
5935
|
return true;
|
|
6514
5936
|
}
|
|
6515
|
-
/*
|
|
6516
|
-
|
|
6517
|
-
|
|
6518
|
-
if (step ===
|
|
6519
|
-
var
|
|
6520
|
-
if (
|
|
6521
|
-
if (repoAccess === true && state.mode !== 'adopt') {
|
|
6522
|
-
/* Fresh with access: just the new repo NAME (owner is the signed-in account). */
|
|
6523
|
-
if (!validRepoName(state.repo)) return homeErr('Name the new repository (letters, numbers, and . _ -).');
|
|
6524
|
-
} else {
|
|
6525
|
-
/* Adopt (picker or manual) and the manual fallback: need a concrete owner + repo. */
|
|
6526
|
-
if (!state.owner || !state.repo) {
|
|
6527
|
-
return homeErr(state.mode === 'adopt' && repoAccess === true ? 'Pick one of your repositories.' : 'Both owner and repository name are needed.');
|
|
6528
|
-
}
|
|
6529
|
-
}
|
|
6530
|
-
$('h-home').innerHTML = ' '; $('h-home').className = 'hint';
|
|
6531
|
-
return true;
|
|
6532
|
-
}
|
|
6533
|
-
if (step === 9) {
|
|
6534
|
-
if (!validSlug(state.slug)) {
|
|
6535
|
-
$('h-slug').textContent = '2–40 lowercase letters, numbers, or dashes; no leading/trailing/double dash.';
|
|
6536
|
-
$('h-slug').className = 'hint err'; $('f-slug').classList.add('bad');
|
|
6537
|
-
return false;
|
|
6538
|
-
}
|
|
6539
|
-
if (state.domain && !looksLikeDomain(state.domain)) {
|
|
6540
|
-
$('addrCustom').open = true;
|
|
6541
|
-
var hd0 = $('h-domain');
|
|
6542
|
-
hd0.textContent = 'That doesn’t look like a domain yet (e.g. mercury.example.com) — fix it, or clear the field to use the address above.';
|
|
6543
|
-
hd0.className = 'hint err';
|
|
6544
|
-
$('f-domain').focus();
|
|
6545
|
-
return false;
|
|
6546
|
-
}
|
|
6547
|
-
/* A handle the server has already said is not free would only 409 at Create —
|
|
6548
|
-
stop here instead (task 1002898 N10). ONLY a definite negative blocks:
|
|
6549
|
-
'unknown' and 'checking' must never gate the step, because the check is
|
|
6550
|
-
advisory and the 409 remains the authority. A chosen domain or the opt-out
|
|
6551
|
-
settles the address another way, so the block does not apply there. */
|
|
6552
|
-
if (!state.domain && !state.noAddress) {
|
|
6553
|
-
var av = slugAvailState();
|
|
6554
|
-
if (av === 'taken' || av === 'yours' || av === 'reserved') {
|
|
6555
|
-
$('h-slug').textContent = av === 'taken'
|
|
6556
|
-
? 'That handle is already taken — pick a different one.'
|
|
6557
|
-
: av === 'yours'
|
|
6558
|
-
? 'You already have a project with that handle — pick a different one.'
|
|
6559
|
-
: 'That handle is reserved by the platform — pick a different one.';
|
|
6560
|
-
$('h-slug').className = 'hint err'; $('f-slug').classList.add('bad');
|
|
6561
|
-
return false;
|
|
6562
|
-
}
|
|
6563
|
-
}
|
|
6564
|
-
/* no auto-assigned address (dotless host): going domainless must be an
|
|
6565
|
-
EXPLICIT choice — a domain, or the opt-out box. Never silent. */
|
|
6566
|
-
if (!resolvedDomain() && !state.noAddress) {
|
|
6567
|
-
$('addrCustom').open = true;
|
|
6568
|
-
var hd = $('h-domain');
|
|
6569
|
-
hd.textContent = 'Add a domain you own — or tick “start without an address” to go without one.';
|
|
6570
|
-
hd.className = 'hint err';
|
|
6571
|
-
return false;
|
|
6572
|
-
}
|
|
5937
|
+
/* the quick checks walk to the review themselves, after the server's screen */
|
|
5938
|
+
if (step === 3) return checksAdvance();
|
|
5939
|
+
/* the review: every row that applies confirmed, each still true */
|
|
5940
|
+
if (step === 4) {
|
|
5941
|
+
var open = unconfirmedRows();
|
|
5942
|
+
if (open.length) { openRow(open[0]); return false; }
|
|
6573
5943
|
return true;
|
|
6574
5944
|
}
|
|
6575
5945
|
return true;
|
|
@@ -6579,44 +5949,16 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6579
5949
|
form.addEventListener('submit', function (ev) {
|
|
6580
5950
|
ev.preventDefault();
|
|
6581
5951
|
var next = Number(form.getAttribute('data-next'));
|
|
6582
|
-
|
|
6583
|
-
|
|
5952
|
+
if (!validateStep(next - 1)) return;
|
|
5953
|
+
/* a demo's answers are the demo's own, so it skips the quick checks */
|
|
5954
|
+
if (next === 3 && state.demo) { physDefaults(); show(4, true); return; }
|
|
5955
|
+
show(next, true);
|
|
6584
5956
|
});
|
|
6585
5957
|
});
|
|
6586
|
-
root.querySelectorAll('[data-back]').forEach(function (btn) {
|
|
6587
|
-
btn.addEventListener('click', function () { show(physPastSkips(Number(btn.getAttribute('data-back')), -1), true); });
|
|
6588
|
-
});
|
|
6589
5958
|
|
|
6590
|
-
/* ── review ── */
|
|
6591
|
-
/* the app step (task 1004351): a native radio group, so the draft only mirrors it.
|
|
6592
|
-
An older draft has no appPlatform, and a value this page does not offer (a
|
|
6593
|
-
platform still marked soon, or one that was removed) reads as 'later' — the
|
|
6594
|
-
review must never name a platform the owner could not actually pick. */
|
|
6595
|
-
function appPlatformChoice() {
|
|
6596
|
-
var want = state.appPlatform;
|
|
6597
|
-
var pick = Array.prototype.filter.call(root.querySelectorAll('#appOpts input[name=appPlatform]'), function (i) {
|
|
6598
|
-
return i.value === want && !i.disabled;
|
|
6599
|
-
})[0];
|
|
6600
|
-
return pick ? pick.value : 'later';
|
|
6601
|
-
}
|
|
6602
|
-
function syncAppStep() {
|
|
6603
|
-
var pick = appPlatformChoice();
|
|
6604
|
-
root.querySelectorAll('#appOpts input[name=appPlatform]').forEach(function (i) { i.checked = i.value === pick; });
|
|
6605
|
-
}
|
|
6606
|
-
function appRow() {
|
|
6607
|
-
/* the step the framing skipped (task 1004419) */
|
|
6608
|
-
if (physStepSkipped(10)) return 'Runs on your own machines, as your framing says — you can choose a platform later from the project’s page';
|
|
6609
|
-
if (appPlatformChoice() !== 'render') return 'Not chosen yet — you can decide later';
|
|
6610
|
-
if (renderLater()) return 'Render — set up from your project’s page once your app has code. Nothing is created on Render now.';
|
|
6611
|
-
var ws =esc(state.renderOwnerName || state.renderOwnerId || '');
|
|
6612
|
-
if (state.renderMode === 'adopt') return 'Render, workspace ' + ws + ' — link <code>' + esc(state.renderServiceName || state.renderServiceId || '') + '</code>, changing nothing on it';
|
|
6613
|
-
var plan = planLabel(RENDER_WEB_PLANS, state.renderPlan || 'starter');
|
|
6614
|
-
var db = state.renderDb ? ', with a database (' + planLabel(RENDER_DB_PLANS, state.renderDbPlan || 'basic_256mb') + ')' : ', no database';
|
|
6615
|
-
return 'Render, workspace ' + ws + ' — a new app (' + esc(runtimeLabel(state.renderRuntime || 'node')) + ', ' + plan + ')' + db + '. Render bills you directly.';
|
|
6616
|
-
}
|
|
6617
5959
|
var appOptsEl = root.querySelector('#appOpts');
|
|
6618
5960
|
if (appOptsEl) appOptsEl.addEventListener('change', function (ev) {
|
|
6619
|
-
if (ev.target && ev.target.name === 'appPlatform' && !ev.target.disabled) { state.appPlatform = ev.target.value; saveDraft(); syncRenderSetup(); }
|
|
5961
|
+
if (ev.target && ev.target.name === 'appPlatform' && !ev.target.disabled) { state.appPlatform = ev.target.value; saveDraft(); syncRenderSetup(); paintSetup(); }
|
|
6620
5962
|
});
|
|
6621
5963
|
|
|
6622
5964
|
/* ── Render (task 1004352, ADR 0348) ──
|
|
@@ -6629,8 +5971,9 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6629
5971
|
fresh one, Render ran the repo's `npm start`, which is the hall, and timed out.
|
|
6630
5972
|
Choosing Render for a fresh repo is still an answer: the step says when and where
|
|
6631
5973
|
it happens, and nothing is sent to Render at Create. */
|
|
6632
|
-
|
|
6633
|
-
function
|
|
5974
|
+
/* a framing that runs on the founder's own machines rules Render out, whatever was picked before */
|
|
5975
|
+
function renderNow() { return !appSkipped() && appPlatformChoice() === 'render' && state.mode === 'adopt'; }
|
|
5976
|
+
function renderLater() { return !appSkipped() && appPlatformChoice() === 'render' && state.mode !== 'adopt'; }
|
|
6634
5977
|
var rc = renderConnect($, state, {
|
|
6635
5978
|
save: saveDraft,
|
|
6636
5979
|
repo: function () { return String(state.owner || '') + '/' + String(state.repo || ''); },
|
|
@@ -6670,7 +6013,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6670
6013
|
: 'Once the project is up, we create your app on your Render account. Your key is used once, then thrown away.';
|
|
6671
6014
|
/* then the app's own progress, from the same read the project's page uses (task 1004353) */
|
|
6672
6015
|
var box = $('doneApp');
|
|
6673
|
-
if (box) watchApp(instanceId, box, function () { return state.step ===
|
|
6016
|
+
if (box) watchApp(instanceId, box, function () { return state.step === 6; }, 5000);
|
|
6674
6017
|
});
|
|
6675
6018
|
}).catch(function (err) {
|
|
6676
6019
|
note.className = 'hint err';
|
|
@@ -6694,14 +6037,6 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6694
6037
|
function () { return state.name; });
|
|
6695
6038
|
} else lookPicker.repaint();
|
|
6696
6039
|
}
|
|
6697
|
-
function lookRow() {
|
|
6698
|
-
var l = state.look || defaultLook();
|
|
6699
|
-
var name = l.look === 'custom'
|
|
6700
|
-
? 'Your own — ' + ((lookKit.lookAdjustAccent(l.accent) || {}).hex || '')
|
|
6701
|
-
: (lookKit.LOOKS[l.look] || lookKit.LOOKS['chrome-world']).title;
|
|
6702
|
-
return esc(name) + ', ' + (l.nightTint === false ? 'pure black at night' : 'a tinted night') +
|
|
6703
|
-
(l.logo ? ', with your logo' : ', no logo');
|
|
6704
|
-
}
|
|
6705
6040
|
/* Send the look a moment after Create, like the Render key: the project exists, so a
|
|
6706
6041
|
refusal here is the LOOK's, and the done panel says so in plain words. */
|
|
6707
6042
|
function sendLookSetup(instanceId) {
|
|
@@ -6714,46 +6049,214 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6714
6049
|
note.textContent = 'Your project is being created, but its look was not saved: ' + err.message + ' You can choose it again on the project’s page.';
|
|
6715
6050
|
});
|
|
6716
6051
|
}
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
|
|
6052
|
+
|
|
6053
|
+
/* ── the review: check what we set up (task 1004504) ── */
|
|
6054
|
+
/* the app row (task 1004351): a native radio group, so the draft only mirrors it.
|
|
6055
|
+
An older draft has no appPlatform, and a value this page does not offer (a
|
|
6056
|
+
platform still marked soon, or one that was removed) reads as 'later' — the
|
|
6057
|
+
review must never name a platform the owner could not actually pick. */
|
|
6058
|
+
function appPlatformChoice() {
|
|
6059
|
+
var want = state.appPlatform;
|
|
6060
|
+
var pick = Array.prototype.filter.call(root.querySelectorAll('#appOpts input[name=appPlatform]'), function (i) {
|
|
6061
|
+
return i.value === want && !i.disabled;
|
|
6062
|
+
})[0];
|
|
6063
|
+
return pick ? pick.value : 'later';
|
|
6064
|
+
}
|
|
6065
|
+
function syncAppStep() {
|
|
6066
|
+
var pick = appPlatformChoice();
|
|
6067
|
+
root.querySelectorAll('#appOpts input[name=appPlatform]').forEach(function (i) { i.checked = i.value === pick; });
|
|
6068
|
+
}
|
|
6069
|
+
/* what the App hosting row says under its value, in plain text */
|
|
6070
|
+
function appRowDetail() {
|
|
6071
|
+
if (appPlatformChoice() !== 'render') return 'We host your builders’ hall. There’s no app to host until your builders write one.';
|
|
6072
|
+
if (renderLater()) return 'Set up from your project’s page once your app has code. Nothing is created on Render now.';
|
|
6073
|
+
var ws = state.renderOwnerName || state.renderOwnerId || '';
|
|
6074
|
+
if (state.renderMode === 'adopt') return 'Workspace ' + ws + ' — link ' + (state.renderServiceName || state.renderServiceId || '') + ', changing nothing on it.';
|
|
6075
|
+
var plan = planLabel(RENDER_WEB_PLANS, state.renderPlan || 'starter');
|
|
6076
|
+
var db = state.renderDb ? ', with a database (' + planLabel(RENDER_DB_PLANS, state.renderDbPlan || 'basic_256mb') + ')' : ', no database';
|
|
6077
|
+
return 'Workspace ' + ws + ' — a new app (' + runtimeLabel(state.renderRuntime || 'node') + ', ' + plan + ')' + db + '. Render bills you directly.';
|
|
6078
|
+
}
|
|
6079
|
+
|
|
6080
|
+
var ROWS = ['code', 'address', 'app', 'extras', 'team', 'rewards', 'run'];
|
|
6081
|
+
function rowEl(key) { return root.querySelector('#reviewRows .setRow[data-row="' + key + '"]'); }
|
|
6082
|
+
function confirmedRows() {
|
|
6083
|
+
if (!state.confirmed || typeof state.confirmed !== 'object') state.confirmed = {};
|
|
6084
|
+
return state.confirmed;
|
|
6085
|
+
}
|
|
6086
|
+
/* a row the answers make not apply: rewards for someone building alone (the table
|
|
6087
|
+
hides the question) */
|
|
6088
|
+
function rowApplies(key) { return !(key === 'rewards' && physSkips(6)); }
|
|
6089
|
+
/* a row whose value is fixed, so it is confirmed but never changed here: the demo's
|
|
6090
|
+
own answers, and the app platform a local or open-source framing rules out */
|
|
6091
|
+
function rowFixed(key) {
|
|
6092
|
+
if (state.demo && (key === 'team' || key === 'rewards' || key === 'run')) return true;
|
|
6093
|
+
return key === 'app' && appSkipped();
|
|
6094
|
+
}
|
|
6095
|
+
function unconfirmedRows() {
|
|
6096
|
+
var c = confirmedRows();
|
|
6097
|
+
return ROWS.filter(function (k) { return rowApplies(k) && !c[k]; });
|
|
6098
|
+
}
|
|
6099
|
+
function unconfirm(keys) {
|
|
6100
|
+
var c = confirmedRows();
|
|
6101
|
+
keys.forEach(function (k) { delete c[k]; });
|
|
6102
|
+
if (state.step === 4) paintSetup();
|
|
6103
|
+
}
|
|
6104
|
+
/* the checks a row's own old step made on Next — a row cannot be confirmed past them */
|
|
6105
|
+
function rowValid(key) {
|
|
6106
|
+
if (key === 'code') return validateCode();
|
|
6107
|
+
if (key === 'address') return validateAddress();
|
|
6108
|
+
if (key === 'app') return rowFixed('app') || validateRender();
|
|
6109
|
+
if (key === 'team') return physAnswered(5);
|
|
6110
|
+
if (key === 'run') return !!phys().answers.framing;
|
|
6111
|
+
return true;
|
|
6112
|
+
}
|
|
6113
|
+
function openRow(key) {
|
|
6114
|
+
var row = rowEl(key);
|
|
6115
|
+
if (!row || rowFixed(key)) return;
|
|
6116
|
+
row.classList.add('open');
|
|
6117
|
+
row.querySelector('.opts').hidden = false;
|
|
6118
|
+
row.querySelector('[data-row-chg]').setAttribute('aria-expanded', 'true');
|
|
6119
|
+
if (key === 'address') { checkSlugAvailable(); renderAddrPanel(); }
|
|
6120
|
+
if (key === 'extras') renderModules();
|
|
6121
|
+
if (key === 'code') syncHome();
|
|
6122
|
+
if (key === 'app') { syncAppStep(); syncRenderSetup(); }
|
|
6123
|
+
}
|
|
6124
|
+
function closeRow(key) {
|
|
6125
|
+
var row = rowEl(key);
|
|
6126
|
+
row.classList.remove('open');
|
|
6127
|
+
row.querySelector('.opts').hidden = true;
|
|
6128
|
+
row.querySelector('[data-row-chg]').setAttribute('aria-expanded', 'false');
|
|
6129
|
+
}
|
|
6130
|
+
/* Looks right, or Use this: the row's own checks first — a row that fails opens
|
|
6131
|
+
with its reason showing — then it is the founder's answer. The extras row turns
|
|
6132
|
+
"the suggested set" into an answer given, a different fact from never asked. */
|
|
6133
|
+
function confirmRow(key) {
|
|
6134
|
+
if (!rowValid(key)) { openRow(key); return false; }
|
|
6135
|
+
/* only once the catalog is here: without it the suggested set is unknown, and an
|
|
6136
|
+
empty answer would switch off the very bundle the server applies to no answer */
|
|
6137
|
+
if (key === 'extras' && starterBundles) state.modules = pickedModules();
|
|
6138
|
+
confirmedRows()[key] = true;
|
|
6139
|
+
closeRow(key);
|
|
6140
|
+
saveDraft();
|
|
6141
|
+
paintSetup();
|
|
6142
|
+
var next = unconfirmedRows()[0];
|
|
6143
|
+
var go = next ? rowEl(next).querySelector('[data-row-ok]') : $('reviewGo');
|
|
6144
|
+
if (go && !go.disabled) go.focus({ preventScroll: true });
|
|
6145
|
+
return true;
|
|
6146
|
+
}
|
|
6147
|
+
$('reviewRows').addEventListener('click', function (ev) {
|
|
6148
|
+
var t = ev.target.closest && ev.target.closest('[data-row-ok], [data-row-chg], [data-row-use]');
|
|
6149
|
+
if (!t) return;
|
|
6150
|
+
var key = t.closest('.setRow').getAttribute('data-row');
|
|
6151
|
+
if (t.hasAttribute('data-row-chg')) {
|
|
6152
|
+
if (rowEl(key).classList.contains('open')) closeRow(key);
|
|
6153
|
+
else { delete confirmedRows()[key]; openRow(key); paintSetup(); }
|
|
6154
|
+
return;
|
|
6155
|
+
}
|
|
6156
|
+
confirmRow(key);
|
|
6157
|
+
});
|
|
6158
|
+
|
|
6159
|
+
/* what each row says: its value and one plain line under it */
|
|
6160
|
+
function rowText(key) {
|
|
6161
|
+
var a = phys().answers;
|
|
6162
|
+
if (key === 'code') {
|
|
6163
|
+
var adopt = state.mode === 'adopt';
|
|
6164
|
+
var where = state.owner && state.repo ? 'github.com/' + state.owner + '/' + state.repo : adopt ? 'Pick the repo to adopt.' : '';
|
|
6165
|
+
return { v: adopt ? 'A repo I already have' : 'A new GitHub repo', s: where + (a.project_state === 'existing' ? (where ? ' · ' : '') + 'already started' : '') };
|
|
6166
|
+
}
|
|
6167
|
+
if (key === 'address') {
|
|
6168
|
+
var custom = state.domain && looksLikeDomain(state.domain);
|
|
6169
|
+
var av = !custom && !state.noAddress ? slugAvailState() : 'unknown';
|
|
6170
|
+
if (state.noAddress && !custom) return { v: 'No address yet', s: 'Sign-in can’t be switched on until it has one.' };
|
|
6171
|
+
/* a dotless host (local dev) assigns nothing, so the row asks for a domain or the opt-out */
|
|
6172
|
+
if (!resolvedDomain()) return { v: 'No address assigned here', s: 'Add a domain you own, or go without one for now.' };
|
|
6173
|
+
return {
|
|
6174
|
+
v: resolvedDomain() || 'Pick a handle',
|
|
6175
|
+
s: custom ? 'Your own domain. Your projects page walks you through pointing it here.'
|
|
6176
|
+
: av === 'taken' ? 'Already taken — change the handle.'
|
|
6177
|
+
: av === 'yours' ? 'You already have that one — change the handle.'
|
|
6178
|
+
: av === 'reserved' ? 'Reserved by the platform — change the handle.'
|
|
6179
|
+
: 'Yours once it’s set up. You can add your own domain any time.'
|
|
6180
|
+
};
|
|
6181
|
+
}
|
|
6182
|
+
if (key === 'app') {
|
|
6183
|
+
if (appSkipped()) return { v: 'Runs on your own machines', s: 'As your answer says. Choose a platform later from the project’s page.' };
|
|
6184
|
+
return { v: appPlatformChoice() === 'render' ? 'Render' : 'Decide later', s: appRowDetail() };
|
|
6185
|
+
}
|
|
6186
|
+
if (key === 'extras') {
|
|
6187
|
+
var picked = pickedModules();
|
|
6188
|
+
var lead = !starterBundles
|
|
6189
|
+
? (state.modules === null ? 'The suggested set' : 'The extras you picked')
|
|
6190
|
+
: state.modules === null
|
|
6191
|
+
? picked.length + ' suggested' + (typeLabel(state.projType) ? ' for a ' + typeLabel(state.projType).toLowerCase() : '')
|
|
6192
|
+
: picked.length ? picked.length + ' picked' : 'None — just the core';
|
|
6193
|
+
return { v: lead, s: starterBundles ? extrasRow() : '' };
|
|
6194
|
+
}
|
|
6195
|
+
if (key === 'team') {
|
|
6196
|
+
if (state.demo) return { v: state.demo.people + (state.demo.people === 1 ? ' person' : ' people'), s: state.demo.hours_each + (state.demo.hours_each === 1 ? ' hour' : ' hours') + ' each. When the time box runs out, we ask if you want to keep it.' };
|
|
6197
|
+
if (physSolo()) return { v: 'One person', s: 'Just you.' };
|
|
6198
|
+
return { v: a.builders_at_start + ' people now', s: 'About ' + a.builders_expected + ' by month three · ' + (state.teamShape === 'community' ? 'anyone can apply' : 'people you invite') + '.' };
|
|
6199
|
+
}
|
|
6200
|
+
if (key === 'rewards') {
|
|
6201
|
+
if (state.demo) return { v: 'None', s: 'It’s a demo, for learning.' };
|
|
6202
|
+
var r = Array.isArray(a.reward_intents) ? a.reward_intents : [];
|
|
6203
|
+
return r.length
|
|
6204
|
+
? { v: PHYS_REWARDS.filter(function (x) { return r.indexOf(x) !== -1; }).map(function (x) { return PHYS_SAY.reward_intents[x]; }).join(', '), s: 'Intent only. You set the real terms later.' }
|
|
6205
|
+
: { v: 'Not decided yet', s: 'Intent only. You set the real terms later.' };
|
|
6206
|
+
}
|
|
6207
|
+
if (key === 'run') {
|
|
6208
|
+
if (state.demo) return { v: 'Everything, for you', s: 'A demo is all managed and private to your team.' };
|
|
6209
|
+
var services = Array.isArray(a.services) ? a.services : [];
|
|
6210
|
+
var named = PHYS_SERVICES.filter(function (x) { return services.indexOf(x) !== -1; }).map(function (x) { return PHYS_SAY.services[x]; }).join(', ');
|
|
6211
|
+
if (a.framing === 'hybrid') return { v: 'Some of it', s: named || 'Pick what you want from the suite.' };
|
|
6212
|
+
if (a.framing === 'local') return { v: 'On my machines', s: named || 'Pick what you want from the suite.' };
|
|
6213
|
+
if (a.framing === 'open-source') return { v: 'Free core only', s: 'The free core, on your own machines.' };
|
|
6214
|
+
return { v: 'Everything, for you', s: 'Hosting, payouts and the starter kit, managed.' };
|
|
6215
|
+
}
|
|
6216
|
+
return { v: '', s: '' };
|
|
6217
|
+
}
|
|
6218
|
+
/* paint every row from the draft, and the locked Continue under them */
|
|
6219
|
+
function paintSetup() {
|
|
6220
|
+
if (!ROWS) return; /* boot: setMode() runs before this block has run */
|
|
6221
|
+
var c = confirmedRows();
|
|
6222
|
+
var suite = phys().answers.framing === 'hybrid' || phys().answers.framing === 'local';
|
|
6223
|
+
$('physSuite').hidden = !suite;
|
|
6224
|
+
$('physSuiteLbl').textContent = phys().answers.framing === 'local' ? 'Your local suite' : 'Your hybrid suite';
|
|
6225
|
+
var a = phys().answers, works = Array.isArray(a.existing_work) ? a.existing_work : [];
|
|
6226
|
+
$('physExisting').hidden = a.project_state !== 'existing';
|
|
6227
|
+
root.querySelectorAll('#physExisting [data-work]').forEach(function (cb) { cb.checked = works.indexOf(cb.getAttribute('data-work')) !== -1; });
|
|
6228
|
+
root.querySelectorAll('#physExisting input[name="physMain"]').forEach(function (r) {
|
|
6229
|
+
r.checked = a.main_work === r.value;
|
|
6230
|
+
r.disabled = works.indexOf(r.value) === -1;
|
|
6231
|
+
});
|
|
6232
|
+
physPaintPills();
|
|
6233
|
+
physPaintTeam();
|
|
6234
|
+
physApplyLimits();
|
|
6235
|
+
ROWS.forEach(function (key) {
|
|
6236
|
+
var row = rowEl(key), t = rowText(key);
|
|
6237
|
+
row.hidden = !rowApplies(key);
|
|
6238
|
+
row.classList.toggle('done', !!c[key]);
|
|
6239
|
+
row.querySelector('[data-row-v]').textContent = t.v;
|
|
6240
|
+
row.querySelector('[data-row-s]').textContent = t.s;
|
|
6241
|
+
var ok = row.querySelector('[data-row-ok]');
|
|
6242
|
+
ok.textContent = c[key] ? 'Confirmed' : 'Looks right';
|
|
6243
|
+
ok.disabled = !!c[key];
|
|
6244
|
+
row.querySelector('[data-row-chg]').hidden = rowFixed(key);
|
|
6245
|
+
});
|
|
6246
|
+
var left = unconfirmedRows().length;
|
|
6247
|
+
var go = $('reviewGo');
|
|
6248
|
+
go.disabled = left > 0;
|
|
6249
|
+
go.textContent = left ? 'Confirm ' + left + ' more' : 'Continue';
|
|
6250
|
+
}
|
|
6251
|
+
/* screen 5's one line: what Create is about to make */
|
|
6252
|
+
function renderRecap() {
|
|
6721
6253
|
var cb = $('doCreate');
|
|
6254
|
+
/* every road back to this screen gets a live button — the create path disables
|
|
6255
|
+
it and only the failure branch used to restore it */
|
|
6722
6256
|
cb.disabled = false; cb.textContent = 'Create my project';
|
|
6723
|
-
var
|
|
6724
|
-
|
|
6725
|
-
|
|
6726
|
-
/* the About step is optional, so its row states the CONSEQUENCE of the
|
|
6727
|
-
skip rather than a bare em-dash — the review is the last screen before
|
|
6728
|
-
Create, and "off the map" should never be a surprise (task 1002334) */
|
|
6729
|
-
['About', String(state.description || '').trim()
|
|
6730
|
-
? esc(String(state.description).trim())
|
|
6731
|
-
: '<em style="font-weight:600;color:var(--warn)">not said — off the public map until you add it</em>'],
|
|
6732
|
-
['Who’s building it', teamLabel(state.teamShape) || 'Not said'],
|
|
6733
|
-
/* the picker is optional too, so this row shows what the project will
|
|
6734
|
-
ACTUALLY come with — the owner's picks, or the preselect they walked
|
|
6735
|
-
past — never a blank that a Create is about to turn into a surprise */
|
|
6736
|
-
['Extras', extrasRow()],
|
|
6737
|
-
['Starting from', state.mode === 'adopt' ? 'A repo you already have' : 'A fresh, empty repo'],
|
|
6738
|
-
['Repository', '<code>' + esc(state.owner + '/' + state.repo) + '</code>'],
|
|
6739
|
-
['Handle', '<code>' + esc(state.slug) + '</code>'],
|
|
6740
|
-
['Address', resolvedDomain()
|
|
6741
|
-
? '<code>' + esc(resolvedDomain()) + '</code>'
|
|
6742
|
-
: '<em style="font-weight:600;color:var(--warn)">none — sign-in stays locked until it has one</em>'],
|
|
6743
|
-
/* The wizard creates hosting_shape 'cloud-host', which since ADR 0345 means the
|
|
6744
|
-
HALL is hosted by us; where the owner's APP runs is its own row (task 1004351). */
|
|
6745
|
-
['Builders\' hall', 'Hosted by us, on the platform’s current release'],
|
|
6746
|
-
['App', appRow()],
|
|
6747
|
-
['Look', lookRow()]
|
|
6748
|
-
];
|
|
6749
|
-
/* the demo route (task 1004420): the review says it is a demo, and what that means */
|
|
6750
|
-
if (state.demo) {
|
|
6751
|
-
rows.unshift(['Demo', esc(state.demo.people + (state.demo.people === 1 ? ' person' : ' people') + ', ' + state.demo.hours_each + ' hours each.') +
|
|
6752
|
-
' Private to your team, no rewards, all managed. When the time box runs out you are asked whether to keep your planet.']);
|
|
6753
|
-
}
|
|
6754
|
-
$('reviewList').innerHTML = rows.map(function (r) {
|
|
6755
|
-
return '<li><span class="k">' + r[0] + '</span><span class="v">' + r[1] + '</span></li>';
|
|
6756
|
-
}).join('');
|
|
6257
|
+
var bits = [state.name || state.slug, typeLabel(state.projType) || '', state.mode === 'adopt' ? 'your repo' : 'new repo', resolvedDomain() || 'no address yet'];
|
|
6258
|
+
if (state.demo) bits.push('demo');
|
|
6259
|
+
$('wizRecap').textContent = bits.filter(Boolean).join(' · ');
|
|
6757
6260
|
}
|
|
6758
6261
|
|
|
6759
6262
|
/* ── create ── */
|
|
@@ -6833,6 +6336,8 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6833
6336
|
box) — send the founder back to it rather than file an unacknowledged
|
|
6834
6337
|
project (task 1004417) */
|
|
6835
6338
|
if (!state.termsAck) { show(1, true); validateStep(1); return; }
|
|
6339
|
+
/* nothing is created before every row of the review is confirmed */
|
|
6340
|
+
if (unconfirmedRows().length) { show(4, true); validateStep(4); return; }
|
|
6836
6341
|
btn.disabled = true; btn.textContent = 'Raising it into the sky…';
|
|
6837
6342
|
ensureFreshRepo().then(function () {
|
|
6838
6343
|
return fetch(API + '/provisioning/instances', {
|
|
@@ -6899,7 +6404,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6899
6404
|
sessionStorage.removeItem(DRAFT_KEY);
|
|
6900
6405
|
} catch (e) {}
|
|
6901
6406
|
renderDone();
|
|
6902
|
-
show(
|
|
6407
|
+
show(6, true);
|
|
6903
6408
|
sendRenderSetup(data.instance && data.instance.id); // the key rides the new project's own setup run (task 1004352)
|
|
6904
6409
|
sendLookSetup(data.instance && data.instance.id); // the look + logo, like the Render key: right after Create (task 1004421)
|
|
6905
6410
|
startProgress(data.instance && data.instance.id);
|
|
@@ -6909,8 +6414,11 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
6909
6414
|
if (err && err.gotoHome) {
|
|
6910
6415
|
/* A repo-creation problem (name taken / reconnect) — bounce back to the Home
|
|
6911
6416
|
step with the reason, so the user fixes it where the repo is chosen. */
|
|
6912
|
-
show(
|
|
6913
|
-
|
|
6417
|
+
show(4, true);
|
|
6418
|
+
delete confirmedRows().code;
|
|
6419
|
+
openRow('code');
|
|
6420
|
+
paintSetup();
|
|
6421
|
+
$('h-home').textContent = err.homeMsg || 'Please check the repository again.';
|
|
6914
6422
|
$('h-home').className = 'hint err';
|
|
6915
6423
|
return;
|
|
6916
6424
|
}
|
|
@@ -7134,9 +6642,10 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
7134
6642
|
var adopt = state.mode === 'adopt';
|
|
7135
6643
|
$('doneRender').hidden = true; // sendRenderSetup shows it again when Render is the choice
|
|
7136
6644
|
$('doneApp').hidden = true; $('doneApp').textContent = '';
|
|
7137
|
-
$('
|
|
7138
|
-
|
|
7139
|
-
|
|
6645
|
+
$('done-h').textContent = 'Setting up ' + (state.name || state.slug || 'your project');
|
|
6646
|
+
$('doneLede').innerHTML = 'We’re setting up <strong>' + esc(state.name || state.slug) + '</strong>' +
|
|
6647
|
+
(adopt ? ' on your existing repo' : '') +
|
|
6648
|
+
': its server, database and address. Watch it here, or any time on <a href="?view=mine">your projects page</a>.';
|
|
7140
6649
|
/* the picker's later half (task 1002342, BV1.R23). The modules step is
|
|
7141
6650
|
skippable, so this panel names where it lives afterwards rather than
|
|
7142
6651
|
leaving an owner to go looking for it. */
|
|
@@ -7401,6 +6910,8 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
7401
6910
|
return false;
|
|
7402
6911
|
}
|
|
7403
6912
|
if (inst.last_verified_up) {
|
|
6913
|
+
/* the alive moment (task 1004504): said only once the project has answered */
|
|
6914
|
+
$('done-h').textContent = (state.name || state.slug || 'Your project') + ' is alive.';
|
|
7404
6915
|
setProgress(progressMarkup(-1, 3,
|
|
7405
6916
|
'Your project is answering at ' + link + '. Finish the steps below and the hall is yours.', 'isLive', flow));
|
|
7406
6917
|
return true;
|
|
@@ -7485,7 +6996,6 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
7485
6996
|
5s tick never clobbers an open dropdown or mid-flight button) */
|
|
7486
6997
|
if (inst && (inst.status !== live.instStatus || addressReady(inst) !== live.addrReady)) {
|
|
7487
6998
|
live.instStatus = inst.status; live.addrReady = addressReady(inst); renderSteps();
|
|
7488
|
-
if (state.step === 13) paintRail(13); /* the rail finishes only when the project answers */
|
|
7489
6999
|
}
|
|
7490
7000
|
var terminal = renderProgress(inst);
|
|
7491
7001
|
if (!terminal) pollTimer = setTimeout(tick, 5000);
|
|
@@ -7525,9 +7035,10 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
7525
7035
|
is three-valued and null is its "nothing said" sentinel; leaving the key out
|
|
7526
7036
|
left it UNDEFINED, which is not null — so pickedModules() reached .slice()
|
|
7527
7037
|
on it and the second project's Modules step threw. */
|
|
7528
|
-
state = { step: 1, name: '', projType: null, description: '', teamShape: null, modules: null, mode: 'greenfield', owner: '', ownerTouched: false, repo: '', repoTouched: false, slug: '', slugTouched: false, domain: '', noAddress: false, appPlatform: 'later', physics: null, demo: null, look: null, termsAck: false };
|
|
7038
|
+
state = { step: 1, name: '', projType: null, description: '', teamShape: null, modules: null, mode: 'greenfield', owner: '', ownerTouched: false, repo: '', repoTouched: false, slug: '', slugTouched: false, domain: '', noAddress: false, appPlatform: 'later', physics: null, demo: null, look: null, termsAck: false, confirmed: {} };
|
|
7529
7039
|
$('newName').value = ''; $('f-owner').value = ''; $('f-repo').value = ''; $('f-slug').value = ''; $('f-domain').value = '';
|
|
7530
7040
|
$('f-noaddr').checked = false; $('f-desc').value = '';
|
|
7041
|
+
$('termsAck').checked = false; paintDoors(); demoShow('intro'); checksShow('main');
|
|
7531
7042
|
/* Re-seed the owner from the signed-in account for the next project. */
|
|
7532
7043
|
if (me && me.github_login) { state.owner = me.github_login; $('f-owner').value = state.owner; }
|
|
7533
7044
|
$('h-name').innerHTML = ' ';
|
|
@@ -7594,17 +7105,17 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
7594
7105
|
another project" the only way out.
|
|
7595
7106
|
|
|
7596
7107
|
The reset fires only when there is a SECOND DRAFT to lose, and the draft's own
|
|
7597
|
-
step is what says so. A landed create clears DRAFT_KEY — but show(
|
|
7108
|
+
step is what says so. A landed create clears DRAFT_KEY — but show(6) then
|
|
7598
7109
|
persists the done panel's own position straight back into it, so "is there a
|
|
7599
7110
|
draft?" is always yes and would have skipped the reset forever, leaving the
|
|
7600
|
-
finished project
|
|
7111
|
+
finished project’s name sitting in the fresh form. Only steps 1–5 are wizard
|
|
7601
7112
|
steps: anything else is the done panel's bookkeeping, not a project someone
|
|
7602
7113
|
is part-way through typing. */
|
|
7603
7114
|
function enterCreated(created, focus) {
|
|
7604
7115
|
if (focus && !resumeIntent) {
|
|
7605
7116
|
var draft = null;
|
|
7606
7117
|
try { draft = JSON.parse(sessionStorage.getItem(DRAFT_KEY) || 'null'); } catch (e) {}
|
|
7607
|
-
var at = draft && draft.step >= 1 && draft.step <=
|
|
7118
|
+
var at = draft && draft.step >= 1 && draft.step <= 5 ? draft.step : 0;
|
|
7608
7119
|
if (!at) { resetForNewProject(); at = 1; }
|
|
7609
7120
|
paintResumeOffer(created);
|
|
7610
7121
|
/* step 1's own focus target is its one control, the terms tick, not the
|
|
@@ -7628,7 +7139,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
7628
7139
|
manifestState = created.manifest_state; manifestPending = true;
|
|
7629
7140
|
}
|
|
7630
7141
|
renderDone();
|
|
7631
|
-
show(
|
|
7142
|
+
show(6, !!focus);
|
|
7632
7143
|
startProgress(created.id);
|
|
7633
7144
|
resumePolls();
|
|
7634
7145
|
}
|
|
@@ -7642,10 +7153,10 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
7642
7153
|
var created = authKnown ? createdRecord() : null;
|
|
7643
7154
|
if (created && created.id) { enterCreated(created, !!focus); return; }
|
|
7644
7155
|
paintResumeOffer(null);
|
|
7645
|
-
/* before identity settles, never render past step 1 (the
|
|
7646
|
-
|
|
7156
|
+
/* before identity settles, never render past step 1 (the fork and its terms
|
|
7157
|
+
tick, since task 1004504): deeper steps carry authenticated side effects (the Home-step repo
|
|
7647
7158
|
probe), and onAuth() re-enters to the real draft step the moment /me answers */
|
|
7648
|
-
var step = authKnown && state.step >= 1 && state.step <=
|
|
7159
|
+
var step = authKnown && state.step >= 1 && state.step <= 5 ? state.step : 1;
|
|
7649
7160
|
if (step === 1) {
|
|
7650
7161
|
/* Pre-auth, step 1 is a PLACEHOLDER, not where the visitor is — but show()
|
|
7651
7162
|
persists whatever step it renders, so it used to overwrite the draft's real
|
|
@@ -7654,7 +7165,7 @@ summary{min-height:24px;padding:3px 0;}
|
|
|
7654
7165
|
step back after rendering the placeholder, in memory AND in the draft. */
|
|
7655
7166
|
var resumeStep = state.step;
|
|
7656
7167
|
show(1, false);
|
|
7657
|
-
if (!authKnown && resumeStep >= 1 && resumeStep <=
|
|
7168
|
+
if (!authKnown && resumeStep >= 1 && resumeStep <= 5) { state.step = resumeStep; saveDraft(); }
|
|
7658
7169
|
if (focus) $('termsAck').focus();
|
|
7659
7170
|
} else {
|
|
7660
7171
|
show(step, !!focus);
|