@bongos/core 1.20.74 → 1.20.75

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/.bongos-core.json +45 -40
  2. package/clients/bongos-client/README.md +1 -1
  3. package/clients/bongos-client/bongos-client.global.js +2 -0
  4. package/clients/bongos-client/index.cjs +2 -0
  5. package/clients/bongos-client/index.d.ts +4 -0
  6. package/clients/bongos-client/index.mjs +2 -0
  7. package/docs/api/openapi.json +79 -3
  8. package/docs/api-reference.md +3 -2
  9. package/docs/copy-inventory.md +447 -373
  10. package/docs/copy-registry.json +1157 -468
  11. package/docs/module-api-changelog.md +2 -0
  12. package/docs/page-inventory.json +6 -1
  13. package/docs/page-readings.json +1044 -650
  14. package/modules/provisioning/routes/provisioning.js +35 -0
  15. package/modules/public-landing/public/assets/cosmos.css +1 -1
  16. package/modules/public-landing/public/projects.html +589 -63
  17. package/modules/public-landing/public/projects.probes.json +8 -5
  18. package/modules/public-landing/public/projects.states.json +19 -14
  19. package/modules/ui-design/kit/lib.js +4 -2
  20. package/modules/ui-design/kit/serve.js +5 -0
  21. package/package-lock.json +2 -2
  22. package/package.json +1 -1
  23. package/release-notes.json +6 -0
  24. package/src/module-api.js +1 -1
  25. package/tests/projects_hub.mjs +9 -5
  26. package/tests/projects_hub_app_status.mjs +1 -1
  27. package/tests/projects_hub_app_step.mjs +10 -10
  28. package/tests/projects_hub_look.mjs +7 -7
  29. package/tests/projects_hub_module_picker.mjs +9 -9
  30. package/tests/projects_hub_pre_uat.mjs +2 -1
  31. package/tests/wizard_demo.mjs +1 -1
  32. package/tests/wizard_draft_resume.mjs +1 -1
  33. package/tests/wizard_front_door.mjs +11 -7
  34. package/tests/wizard_intent_resume.mjs +9 -9
  35. package/tests/wizard_physics.mjs +264 -0
@@ -167,6 +167,54 @@ h1[tabindex="-1"]:focus:not(:focus-visible){outline:none;}
167
167
  .forkStage{max-width:300px;}
168
168
  .forkRoutes{margin-top:32px;}
169
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;}
180
+ .physCells .tpill small{display:block;font-size:13.5px;font-weight:400;color:var(--ink-soft);line-height:1.4;}
181
+ .physCells .tpill[disabled]{opacity:.45;cursor:default;}
182
+ .physWhy{font-size:14px;line-height:1.5;color:var(--ink-soft);max-width:60ch;margin:0 0 14px;}
183
+ .physExisting{margin:4px 0 14px;padding:0;border:0;}
184
+ .physExisting legend{font-size:15px;color:var(--ink);margin:0 0 6px;padding:0;}
185
+ .physCheck{display:flex;align-items:flex-start;gap:12px;min-height:24px;padding:10px 0;border-top:1px solid var(--rule-soft);font-size:15px;color:var(--ink);cursor:pointer;}
186
+ .physCheck input[type=checkbox]{width:18px;height:18px;margin:2px 0 0;flex:none;accent-color:var(--accent);}
187
+ .physCheck small{display:block;font-size:13.5px;color:var(--ink-soft);}
188
+ .physMain{margin-left:auto;display:inline-flex;align-items:center;gap:6px;font-family:var(--label-face);font-size:var(--label-size);letter-spacing:var(--label-space);text-transform:uppercase;color:var(--ink-faint);}
189
+ .physMain input{width:16px;height:16px;margin:0;accent-color:var(--accent);}
190
+ /* the two pickers in the renderSetup field grammar: a hairline under, 44px tall */
191
+ .physRow2 select{display:block;width:100%;min-height:44px;font-family:var(--font-body);font-size:16px;color:var(--ink);background:none;border:0;border-bottom:1px solid var(--rule);border-radius:0;padding:8px 0;outline:none;}
192
+ .physRow2 select:focus{border-bottom-color:var(--ink);}
193
+ .physRow2 select option{background:var(--bg);color:var(--ink);}
194
+ .physRow2{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:0 16px;}
195
+ .physNote{margin:6px 0 14px;padding:14px 0 0;border-top:1px solid var(--rule);max-width:62ch;}
196
+ .physNoteH{font-size:15px;color:var(--ink);margin:0 0 8px;}
197
+ .physTiers{display:grid;grid-template-columns:120px minmax(0,1fr);gap:8px 16px;margin:0 0 10px;font-size:14.5px;line-height:1.45;}
198
+ .physTiers 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:2px;}
199
+ .physTiers dd{margin:0;color:var(--ink);}
200
+ .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
+ .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
+ @media (max-width:840px){ .physRow2{grid-template-columns:1fr;} }
170
218
  /* under 520 the two doors cannot sit side by side at 48px, so they stack —
171
219
  still two equal doors in one object */
172
220
  @media (max-width:520px){
@@ -1243,15 +1291,16 @@ summary{min-height:24px;padding:3px 0;}
1243
1291
  <div class="wrap viewWrap">
1244
1292
  <div class="card wizFront">
1245
1293
  <ol class="steps" aria-label="Creation steps">
1246
- <li class="now" data-step="3" aria-current="step"><span class="snum">1</span><span class="slbl">Name</span></li>
1247
- <li data-step="4"><span class="snum">2</span><span class="slbl">Type</span></li>
1248
- <li data-step="5"><span class="snum">3</span><span class="slbl">About</span></li>
1249
- <li data-step="6"><span class="snum">4</span><span class="slbl">Modules</span></li>
1250
- <li data-step="7"><span class="snum">5</span><span class="slbl">Home</span></li>
1251
- <li data-step="8"><span class="snum">6</span><span class="slbl">Address</span></li>
1252
- <li data-step="9"><span class="snum">7</span><span class="slbl">App</span></li>
1253
- <li data-step="10"><span class="snum">8</span><span class="slbl">Look</span></li>
1254
- <li data-step="11"><span class="snum">9</span><span class="slbl">Create</span></li>
1294
+ <li class="now" data-step="3" aria-current="step"><span class="snum">1</span><span class="slbl">Physics</span></li>
1295
+ <li data-step="4"><span class="snum">2</span><span class="slbl">Name</span></li>
1296
+ <li data-step="5"><span class="snum">3</span><span class="slbl">Type</span></li>
1297
+ <li data-step="6"><span class="snum">4</span><span class="slbl">About</span></li>
1298
+ <li data-step="7"><span class="snum">5</span><span class="slbl">Modules</span></li>
1299
+ <li data-step="8"><span class="snum">6</span><span class="slbl">Home</span></li>
1300
+ <li data-step="9"><span class="snum">7</span><span class="slbl">Address</span></li>
1301
+ <li data-step="10"><span class="snum">8</span><span class="slbl">App</span></li>
1302
+ <li data-step="11"><span class="snum">9</span><span class="slbl">Look</span></li>
1303
+ <li data-step="12"><span class="snum">10</span><span class="slbl">Create</span></li>
1255
1304
  </ol>
1256
1305
 
1257
1306
  <div class="createSplit">
@@ -1433,12 +1482,156 @@ summary{min-height:24px;padding:3px 0;}
1433
1482
  </div>
1434
1483
  </section>
1435
1484
 
1436
- <!-- step 3 · name (the hub's original step-1 form, now the wizard's) -->
1485
+ <!-- step 3 · planet physics, questions 1 to 4 (task 1004418, BV2.PS07; the canvas's
1486
+ Autofill, State, StateExisting, NotPossible, Geography, Ages, Entity and
1487
+ EntityExisting frames, docs/design/project-startup-direction.md). ONE rail step
1488
+ with its own inner sequence, so the wizard's numbering moved by one and the
1489
+ canvas's seven-question rail lives inside the panel (questions 5 to 7 join it in
1490
+ BV2.PS11). Left: the question, its choices and the doors. Right: the field plate
1491
+ with the planet forming from the world's own plates as the answers come in.
1492
+ Every answer rides the create request's `detail` (planet-physics.js); the stop
1493
+ screen and the soft-limit table come from POST /provisioning/physics-check, the
1494
+ same screen and table the create runs. The age tiers are described, never
1495
+ claimed as enforced (spec D6). -->
1496
+ <section class="panel" id="panel-physics" aria-labelledby="phys-h0">
1497
+ <div class="forkBody physBody">
1498
+ <div>
1499
+ <p class="physCrumb">planet creation · part 1 of 2 · planet physics</p>
1500
+ <ol class="physRail" id="physRail" aria-label="Planet physics questions">
1501
+ <li data-q="1">state</li><li data-q="2">geography</li><li data-q="3">ages</li><li data-q="4">entity</li><li>builders</li><li>rewards</li><li>framing</li>
1502
+ </ol>
1503
+ <form data-next="4" id="physForm">
1504
+ <div class="physQ" data-q="0">
1505
+ <h1 id="phys-h0">Start from your account, <em>or from scratch?</em></h1>
1506
+ <p class="lede">Seven questions set your planet’s physics: the rules it will live by. We can fill in what your account already knows from your last project, and you check every answer before it counts.</p>
1507
+ <div class="typeCards physCells" role="group" aria-label="How to start">
1508
+ <button type="button" class="tpill" data-phys-start="account" aria-pressed="false" id="physFromAccount">Auto-fill from my account</button>
1509
+ <button type="button" class="tpill" data-phys-start="scratch" aria-pressed="false">Fill it in myself</button>
1510
+ </div>
1511
+ <p class="physWhy" id="physAccountWhy" hidden></p>
1512
+ </div>
1513
+
1514
+ <div class="physQ" data-q="1" hidden>
1515
+ <h1 id="phys-h1">Is your planet new, <em>or already started?</em></h1>
1516
+ <div class="typeCards physCells" role="group" aria-label="Your project’s state">
1517
+ <button type="button" class="tpill" data-phys="project_state" data-val="new" aria-pressed="false">New <small>Starting from zero.</small></button>
1518
+ <button type="button" class="tpill" data-phys="project_state" data-val="existing" aria-pressed="false">Existing <small>My project has already been started.</small></button>
1519
+ </div>
1520
+ <fieldset class="physExisting" id="physExisting" data-limit="existing_work" hidden>
1521
+ <legend>What already exists? Pick all that apply, then your main one.</legend>
1522
+ <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>
1523
+ <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>
1524
+ <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>
1525
+ </fieldset>
1526
+ <p class="physWhy" data-why="existing_work" hidden></p>
1527
+ <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>
1528
+ <textarea id="physDesc" rows="3" maxlength="600" placeholder="A co-op farming game where the seasons never repeat."></textarea>
1529
+ </label>
1530
+ </div>
1531
+
1532
+ <div class="physQ" data-q="2" hidden>
1533
+ <h1 id="phys-h2">Where is <em>everyone?</em></h1>
1534
+ <p class="lede">Where you are, and where your builders will be, decides which payout and legal options we can offer. You can change it later.</p>
1535
+ <div class="physRow2">
1536
+ <label class="wizField"><span>Your country</span><select id="physCountry"><option value="">Choose…</option></select></label>
1537
+ <label class="wizField" id="physRegionField" hidden><span>State or province</span><select id="physRegion"><option value="">Choose…</option></select></label>
1538
+ </div>
1539
+ <div class="wizField"><span id="physGeoLbl">Where do you expect builders to be?</span>
1540
+ <div class="typeCards physCells" role="group" aria-labelledby="physGeoLbl">
1541
+ <button type="button" class="tpill" data-phys="builder_geography" data-val="us-only" aria-pressed="false">US only</button>
1542
+ <button type="button" class="tpill" data-phys="builder_geography" data-val="us-eu" aria-pressed="false">US and EU</button>
1543
+ <button type="button" class="tpill" data-phys="builder_geography" data-val="global" aria-pressed="false">Global</button>
1544
+ <button type="button" class="tpill" data-phys="builder_geography" data-val="not-sure" aria-pressed="false">Not sure yet</button>
1545
+ </div>
1546
+ </div>
1547
+ </div>
1548
+
1549
+ <div class="physQ" data-q="3" hidden>
1550
+ <h1 id="phys-h3">Will anyone under 18 <em>be building?</em></h1>
1551
+ <div class="typeCards physCells" role="group" aria-label="Builders under 18">
1552
+ <button type="button" class="tpill" data-phys="minors_on_board" data-val="false" aria-pressed="false">No <small>Everyone building is an adult.</small></button>
1553
+ <button type="button" class="tpill" data-phys="minors_on_board" data-val="true" aria-pressed="false">Yes <small>Some builders are under 18.</small></button>
1554
+ </div>
1555
+ <div class="physNote" id="physAges" hidden>
1556
+ <p class="physNoteH">The age tiers young builders work under</p>
1557
+ <dl class="physTiers">
1558
+ <dt>13 to 15</dt><dd>Non-monetary tracks only</dd>
1559
+ <dt>16 to 17</dt><dd>Paid work, with a guardian’s co-signature and a work-permit attestation</dd>
1560
+ <dt>18 and over</dt><dd>Full access</dd>
1561
+ </dl>
1562
+ <p class="finePrint">Each young builder’s guardian signs for them, and their payouts go to an account the guardian controls. These are the rules your project is set up to follow; the platform does not check ages for you yet.</p>
1563
+ </div>
1564
+ </div>
1565
+
1566
+ <div class="physQ" data-q="4" hidden>
1567
+ <h1 id="phys-h4">What <em>are you?</em></h1>
1568
+ <div id="physIntent">
1569
+ <p class="lede">Your planet is new, so there’s nothing to sign yet. Tell us roughly where you’re heading. You’ll make the binding choice later, once you know what you’re building.</p>
1570
+ <div class="typeCards physCells" role="group" aria-label="Where you are heading">
1571
+ <button type="button" class="tpill" data-phys="entity" data-val="intent-company" aria-pressed="false">Probably a company</button>
1572
+ <button type="button" class="tpill" data-phys="entity" data-val="intent-nonprofit" aria-pressed="false">Probably a nonprofit</button>
1573
+ <button type="button" class="tpill" data-phys="entity" data-val="intent-solo" aria-pressed="false">Just me for now</button>
1574
+ <button type="button" class="tpill" data-phys="entity" data-val="intent-unsure" aria-pressed="false">Don’t know yet</button>
1575
+ </div>
1576
+ <button type="button" class="physLink" id="physHaveEntity">I already have a legal entity</button>
1577
+ </div>
1578
+ <div id="physForms" hidden>
1579
+ <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>
1580
+ <p class="physGrp">Organization</p>
1581
+ <div class="typeCards physCells" role="group" aria-label="Organization">
1582
+ <button type="button" class="tpill" data-phys="entity" data-val="nonprofit" aria-pressed="false">Nonprofit</button>
1583
+ <button type="button" class="tpill" data-phys="entity" data-val="for-profit" aria-pressed="false">For-profit company</button>
1584
+ <button type="button" class="tpill" data-phys="entity" data-val="corporate-division" aria-pressed="false">Division of a corporation</button>
1585
+ <button type="button" class="tpill" data-phys="entity" data-val="research" aria-pressed="false">Research entity</button>
1586
+ </div>
1587
+ <p class="physGrp">Group</p>
1588
+ <div class="typeCards physCells" role="group" aria-label="Group">
1589
+ <button type="button" class="tpill" data-phys="entity" data-val="university-project" aria-pressed="false">University project</button>
1590
+ <button type="button" class="tpill" data-phys="entity" data-val="social-group" aria-pressed="false">Social group</button>
1591
+ </div>
1592
+ <p class="physGrp">Individual</p>
1593
+ <div class="typeCards physCells" role="group" aria-label="Individual">
1594
+ <button type="button" class="tpill" data-phys="entity" data-val="single-owner-llc" aria-pressed="false">Single-owner LLC</button>
1595
+ <button type="button" class="tpill" data-phys="entity" data-val="private-individual" aria-pressed="false">Private individual</button>
1596
+ </div>
1597
+ <button type="button" class="physLink" id="physNoEntity">We don’t have one yet</button>
1598
+ </div>
1599
+ </div>
1600
+
1601
+ <div class="physQ" data-q="stop" hidden>
1602
+ <h1 id="phys-hstop">This can’t be built <em>on <span data-brand-product-inline>Cloud Bongos</span></em></h1>
1603
+ <p class="lede" id="physStopMsg"></p>
1604
+ <div class="nav">
1605
+ <button type="button" class="btn ghost" id="physStartOver">Start over</button>
1606
+ <a class="btn" href="/contact">Contact us</a>
1607
+ </div>
1608
+ </div>
1609
+
1610
+ <div class="hint" id="h-phys">&nbsp;</div>
1611
+ <div class="nav" id="physNav">
1612
+ <button type="button" class="btn ghost" id="physBack">← Back</button>
1613
+ <button type="submit" class="btn" id="physNext">Continue →</button>
1614
+ </div>
1615
+ </form>
1616
+ </div>
1617
+ <div class="forkStage physStage" id="physStage" aria-hidden="true">
1618
+ <span class="forkOrbit"></span>
1619
+ <span class="forkOrbit forkOrbitDash"></span>
1620
+ <span class="physGuard" id="physGuard" hidden></span>
1621
+ <img class="forkSeed physPlanet" id="physPlanet" src="/assets/world/r2-chrome-blob-one-cut.webp" alt="">
1622
+ <p class="physCap"><span id="physCapLbl">planet physics</span> · <span id="physCapN">0 of 7</span></p>
1623
+ <div class="physFoot"><p class="physCallout" id="physCallout">physics · the rules it lives by · each answer shapes your planet here</p>
1624
+ <p class="physCoords" id="physCoords"></p></div>
1625
+ </div>
1626
+ </div>
1627
+ </section>
1628
+
1629
+ <!-- step 4 · name (the hub's original step-1 form, now the wizard's) -->
1437
1630
  <section class="panel" id="panel-name" aria-labelledby="name-h">
1438
1631
  <h1 id="name-h">Start a project</h1>
1439
1632
  <p class="lede">Your own project: its own repository, its own home on the web, and a hall of AI builders
1440
1633
  who do the building. You set the direction; agents lay down the code.</p>
1441
- <form id="newForm" data-next="4">
1634
+ <form id="newForm" data-next="5">
1442
1635
  <label class="wizField"><span>Project name</span>
1443
1636
  <input type="text" id="newName" placeholder="What should it be called?" autocomplete="off" maxlength="80" aria-label="Project name">
1444
1637
  <div class="hint" id="h-name">&nbsp;</div>
@@ -1462,7 +1655,7 @@ summary{min-height:24px;padding:3px 0;}
1462
1655
  <section class="panel" id="panel-type" aria-labelledby="type-h">
1463
1656
  <h1 id="type-h">What kind of project is it?</h1>
1464
1657
  <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>
1465
- <form data-next="5">
1658
+ <form data-next="6">
1466
1659
  <div class="typeCards" id="typeCards" role="group" aria-label="Project type">
1467
1660
  <button type="button" class="tpill" data-type="game" aria-pressed="false">Game</button>
1468
1661
  <button type="button" class="tpill" data-type="research" aria-pressed="false">Research</button>
@@ -1476,7 +1669,7 @@ summary{min-height:24px;padding:3px 0;}
1476
1669
  </div>
1477
1670
  <div class="hint" id="h-projtype">&nbsp;</div>
1478
1671
  <div class="nav">
1479
- <button type="button" class="btn ghost" data-back="3">← Back</button>
1672
+ <button type="button" class="btn ghost" data-back="4">← Back</button>
1480
1673
  <button type="submit" class="btn">Next →</button>
1481
1674
  </div>
1482
1675
  </form>
@@ -1494,7 +1687,7 @@ summary{min-height:24px;padding:3px 0;}
1494
1687
  <section class="panel" id="panel-detail" aria-labelledby="detail-h">
1495
1688
  <h1 id="detail-h">Tell us about it</h1>
1496
1689
  <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>
1497
- <form data-next="6">
1690
+ <form data-next="7">
1498
1691
  <label class="wizField"><span>What are you building? <small>(a sentence or two)</small></span>
1499
1692
  <textarea id="f-desc" rows="3" maxlength="600" placeholder="A co-op farming game where the seasons never repeat."></textarea>
1500
1693
  </label>
@@ -1507,7 +1700,7 @@ summary{min-height:24px;padding:3px 0;}
1507
1700
  </div>
1508
1701
  <div class="reassure" id="detailSkipNote"></div>
1509
1702
  <div class="nav">
1510
- <button type="button" class="btn ghost" data-back="4">← Back</button>
1703
+ <button type="button" class="btn ghost" data-back="5">← Back</button>
1511
1704
  <span class="navPair">
1512
1705
  <button type="button" class="btn ghost" id="skipDetail">Skip for now</button>
1513
1706
  <button type="submit" class="btn">Next →</button>
@@ -1530,7 +1723,7 @@ summary{min-height:24px;padding:3px 0;}
1530
1723
  <section class="panel" id="panel-modules" aria-labelledby="mod-h">
1531
1724
  <h1 id="mod-h">What it comes with</h1>
1532
1725
  <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>
1533
- <form data-next="7">
1726
+ <form data-next="8">
1534
1727
  <div class="wizField"><span id="modLbl">Extras for this project</span>
1535
1728
  <!-- the catalog search (task 1002340, BV1.R21). The wizard's own
1536
1729
  search idiom (#ghSearch): a plain field on one hairline, no
@@ -1546,7 +1739,7 @@ summary{min-height:24px;padding:3px 0;}
1546
1739
  <div class="reassure" id="modCoreNote"></div>
1547
1740
  <div class="hint" id="h-modules">&nbsp;</div>
1548
1741
  <div class="nav">
1549
- <button type="button" class="btn ghost" data-back="5">← Back</button>
1742
+ <button type="button" class="btn ghost" data-back="6">← Back</button>
1550
1743
  <span class="navPair">
1551
1744
  <button type="button" class="btn ghost" id="skipModules">Skip for now</button>
1552
1745
  <button type="submit" class="btn">Next →</button>
@@ -1559,7 +1752,7 @@ summary{min-height:24px;padding:3px 0;}
1559
1752
  <section class="panel" id="panel-home" aria-labelledby="home-h">
1560
1753
  <h1 id="home-h">Where the code lives</h1>
1561
1754
  <p class="lede">Every project is a real GitHub repository that you own. Start from a clean slate, or bring a repo you already have.</p>
1562
- <form data-next="8">
1755
+ <form data-next="9">
1563
1756
  <div class="choices" role="group" aria-label="Starting point">
1564
1757
  <button type="button" class="choice" id="c-green" aria-pressed="true">
1565
1758
  <!-- token-built, not platform orange — public-landing/ ships wholesale to
@@ -1625,7 +1818,7 @@ summary{min-height:24px;padding:3px 0;}
1625
1818
  </div>
1626
1819
  <div class="hint" id="h-home">&nbsp;</div>
1627
1820
  <div class="nav">
1628
- <button type="button" class="btn ghost" data-back="6">← Back</button>
1821
+ <button type="button" class="btn ghost" data-back="7">← Back</button>
1629
1822
  <button type="submit" class="btn">Next →</button>
1630
1823
  </div>
1631
1824
  </form>
@@ -1643,7 +1836,7 @@ summary{min-height:24px;padding:3px 0;}
1643
1836
  <section class="panel" id="panel-address" aria-labelledby="addr-h">
1644
1837
  <h1 id="addr-h">Its address in the sky</h1>
1645
1838
  <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>
1646
- <form data-next="9">
1839
+ <form data-next="10">
1647
1840
  <label class="wizField"><span>Project handle</span>
1648
1841
  <input type="text" id="f-slug" placeholder="mercury" autocomplete="off" maxlength="40" spellcheck="false">
1649
1842
  <div class="hint" id="h-slug">&nbsp;</div>
@@ -1665,7 +1858,7 @@ summary{min-height:24px;padding:3px 0;}
1665
1858
  </label>
1666
1859
  </div>
1667
1860
  <div class="nav">
1668
- <button type="button" class="btn ghost" data-back="7">← Back</button>
1861
+ <button type="button" class="btn ghost" data-back="8">← Back</button>
1669
1862
  <button type="submit" class="btn">Next →</button>
1670
1863
  </div>
1671
1864
  </form>
@@ -1682,7 +1875,7 @@ summary{min-height:24px;padding:3px 0;}
1682
1875
  <h1 id="app-h">Where your app will run</h1>
1683
1876
  <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>
1684
1877
  <p class="lede">Your app is the thing the project builds. Choose where it should run, or decide later.</p>
1685
- <form data-next="10">
1878
+ <form data-next="11">
1686
1879
  <fieldset class="appOpts" id="appOpts">
1687
1880
  <legend>Where your app runs</legend>
1688
1881
  <label class="appOpt" id="appOptRender">
@@ -1741,7 +1934,7 @@ summary{min-height:24px;padding:3px 0;}
1741
1934
  <div class="hint" id="h-render" role="status">&nbsp;</div>
1742
1935
  </div>
1743
1936
  <div class="nav">
1744
- <button type="button" class="btn ghost" data-back="8">← Back</button>
1937
+ <button type="button" class="btn ghost" data-back="9">← Back</button>
1745
1938
  <button type="submit" class="btn">Next →</button>
1746
1939
  </div>
1747
1940
  </form>
@@ -1754,10 +1947,10 @@ summary{min-height:24px;padding:3px 0;}
1754
1947
  <section class="panel" id="panel-look" aria-labelledby="look-h">
1755
1948
  <h1 id="look-h">Give it a look</h1>
1756
1949
  <p class="lede">Colours for light and dark mode, and a logo, so builders who work in several projects always know which hall they’re in.</p>
1757
- <form data-next="11">
1950
+ <form data-next="12">
1758
1951
  <div class="lookPick" id="wizLook"></div>
1759
1952
  <div class="nav">
1760
- <button type="button" class="btn ghost" data-back="9">← Back</button>
1953
+ <button type="button" class="btn ghost" data-back="10">← Back</button>
1761
1954
  <button type="submit" class="btn">Next →</button>
1762
1955
  </div>
1763
1956
  </form>
@@ -1770,7 +1963,7 @@ summary{min-height:24px;padding:3px 0;}
1770
1963
  <ul class="review" id="reviewList"></ul>
1771
1964
  <div class="alert" id="createAlert" role="alert"></div>
1772
1965
  <div class="nav">
1773
- <button type="button" class="btn ghost" data-back="10">← Back</button>
1966
+ <button type="button" class="btn ghost" data-back="11">← Back</button>
1774
1967
  <button type="button" class="btn big" id="doCreate">Create my project</button>
1775
1968
  </div>
1776
1969
  <p class="finePrint">This files the request; the control plane does the real standup and nothing touches servers from this page. You'll get the few steps only you can do right after.</p>
@@ -4424,7 +4617,7 @@ summary{min-height:24px;padding:3px 0;}
4424
4617
  /* `termsAck` is the front door's one tick (task 1004417). It is the founder's
4425
4618
  own answer, so it lives in the draft like every other one and survives the
4426
4619
  GitHub round-trip; the server, not this page, names the terms version. */
4427
- var DRAFT_KEY = 'cb-create-draft-v7';
4620
+ var DRAFT_KEY = 'cb-create-draft-v8';
4428
4621
  var CREATED_KEY = 'cb-created-instance';
4429
4622
  /* `description` + `teamShape` are the optional About step (task 1002334) —
4430
4623
  both legitimately stay empty, and empty is what the create request omits. */
@@ -4433,7 +4626,7 @@ summary{min-height:24px;padding:3px 0;}
4433
4626
  request omits the field), [] = the owner switched them all off, [...] =
4434
4627
  what they picked. Never seed it with the preselect — that would store an
4435
4628
  answer nobody gave. */
4436
- 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', demo: null, look: null, termsAck: false };
4629
+ 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 };
4437
4630
  try { Object.assign(state, JSON.parse(sessionStorage.getItem(DRAFT_KEY) || '{}')); } catch (e) {}
4438
4631
  var authKnown = false; /* flips true when the boot's /me settles, either way */
4439
4632
 
@@ -4447,7 +4640,7 @@ summary{min-height:24px;padding:3px 0;}
4447
4640
  state.name = handedName.trim().slice(0, 80);
4448
4641
  state.slug = deriveSlug(state.name); state.slugTouched = false;
4449
4642
  state.repo = deriveRepo(state.name); state.repoTouched = false;
4450
- state.step = state.step >= 1 && state.step <= 11 ? state.step : 1;
4643
+ state.step = state.step >= 1 && state.step <= 12 ? state.step : 1;
4451
4644
  try { sessionStorage.setItem(DRAFT_KEY, JSON.stringify(state)); } catch (e) {}
4452
4645
  }
4453
4646
  } catch (e) {}
@@ -4644,14 +4837,14 @@ summary{min-height:24px;padding:3px 0;}
4644
4837
  review 11 · done 12 (the Look step joined at 10, task 1004421). The rail numbers
4645
4838
  3-11 as its 1-9 and steps aside on 1 and 2; done marks it all. About, Modules and
4646
4839
  Look are the steps Next can pass with nothing changed — Look has a default. */
4647
- var panels = { 0: 'panel-gate', 1: 'panel-terms', 2: 'panel-fork', 3: 'panel-name', 4: 'panel-type', 5: 'panel-detail', 6: 'panel-modules', 7: 'panel-home', 8: 'panel-address', 9: 'panel-app', 10: 'panel-look', 11: 'panel-review', 12: 'panel-done' };
4840
+ var panels = { 0: 'panel-gate', 1: 'panel-terms', 2: 'panel-fork', 3: 'panel-physics', 4: 'panel-name', 5: 'panel-type', 6: 'panel-detail', 7: 'panel-modules', 8: 'panel-home', 9: 'panel-address', 10: 'panel-app', 11: 'panel-look', 12: 'panel-review', 13: 'panel-done' };
4648
4841
  /* the rail at the done panel (task 1003244): "done" is the Create step
4649
4842
  still running — the marker stays on 5 until the project actually
4650
4843
  answers, and only then does the whole rail read finished. aria-current
4651
4844
  tells a screen reader the same thing. */
4652
4845
  function paintRail(step) {
4653
- var landed = step === 12 && !!(live && live.instStatus === 'active');
4654
- var cur = step === 12 ? 11 : step;
4846
+ var landed = step === 13 && !!(live && live.instStatus === 'active');
4847
+ var cur = step === 13 ? 12 : step;
4655
4848
  root.querySelectorAll('.steps li').forEach(function (li) {
4656
4849
  var n = Number(li.getAttribute('data-step'));
4657
4850
  var now = !landed && n === cur;
@@ -4662,7 +4855,7 @@ summary{min-height:24px;padding:3px 0;}
4662
4855
  the button role, the tab stop and the data-goto the delegated handler
4663
4856
  below reads. The done panel offers none — the project is filed, and
4664
4857
  there is nothing behind it to change. */
4665
- if (step < 12 && n < cur) {
4858
+ if (step < 13 && n < cur) {
4666
4859
  li.setAttribute('role', 'button');
4667
4860
  li.setAttribute('tabindex', '0');
4668
4861
  li.setAttribute('data-goto', String(n));
@@ -4681,11 +4874,11 @@ summary{min-height:24px;padding:3px 0;}
4681
4874
  paintRail(step);
4682
4875
  saveDraft();
4683
4876
  /* the done step takes the whole card — the bongo pair steps aside */
4684
- root.querySelector('.createSplit').classList.toggle('wizDone', step === 12);
4877
+ root.querySelector('.createSplit').classList.toggle('wizDone', step === 13);
4685
4878
  /* the front door (task 1004417): no rail on its two screens, and the fork
4686
4879
  takes the whole card for the seed on its plate */
4687
4880
  root.querySelector('.card').classList.toggle('wizFront', step === 1 || step === 2);
4688
- root.querySelector('.createSplit').classList.toggle('wizFork', step === 2);
4881
+ root.querySelector('.createSplit').classList.toggle('wizFork', step === 2 || step === 3);
4689
4882
  /* focus moves ONLY on a deliberate step (the hub rule setView already
4690
4883
  applies to this view: never on boot, never on a background settle) */
4691
4884
  if (moveFocus) {
@@ -4697,18 +4890,19 @@ summary{min-height:24px;padding:3px 0;}
4697
4890
  draft's owner with myLogin()'s placeholder) */
4698
4891
  /* the picker repaints on entry, not once at boot: the owner can walk back
4699
4892
  to the Type step and change the answer the preselect is keyed to */
4700
- if (step === 6) renderModules();
4701
- if (step === 7) { syncHome(); if (authKnown) ensureRepoAccess(); }
4893
+ if (step === 3) paintPhysics();
4894
+ if (step === 7) renderModules();
4895
+ if (step === 8) { syncHome(); if (authKnown) ensureRepoAccess(); }
4702
4896
  /* a resumed draft can land PAST the Home step with the probe never run —
4703
4897
  Create would then file a repo it promised to make but never did */
4704
- else if (step > 7 && step < 12 && authKnown && repoAccess === null) ensureRepoAccess();
4898
+ else if (step > 8 && step < 13 && authKnown && repoAccess === null) ensureRepoAccess();
4705
4899
  /* re-check on every entry, not just on typing: a resumed draft arrives here with
4706
4900
  a handle nobody has asked the server about, and a handle free ten minutes ago
4707
4901
  may not be now (task 1002898 N10) */
4708
- if (step === 8) { checkSlugAvailable(); renderAddrPanel(); }
4709
- if (step === 9) { syncAppStep(); syncRenderSetup(); }
4710
- if (step === 10) renderLook();
4711
- if (step === 11) renderReview();
4902
+ if (step === 9) { checkSlugAvailable(); renderAddrPanel(); }
4903
+ if (step === 10) { syncAppStep(); syncRenderSetup(); }
4904
+ if (step === 11) renderLook();
4905
+ if (step === 12) renderReview();
4712
4906
  }
4713
4907
 
4714
4908
  /* One delegated pair on the rail, so paintRail can add and drop data-goto
@@ -4821,7 +5015,8 @@ summary{min-height:24px;padding:3px 0;}
4821
5015
  }
4822
5016
  state.teamShape = demoDraft.people > 1 ? 'small-team' : 'solo';
4823
5017
  saveDraft();
4824
- show(3, true);
5018
+ /* the name step: a demo skips planet physics (task 1004418) */
5019
+ show(4, true);
4825
5020
  });
4826
5021
  /* the done panel's invite step names the demo's teammates (task 1004420): the hub
4827
5022
  holds no invite of its own, so the founder sends each from the project's own box */
@@ -4963,7 +5158,7 @@ summary{min-height:24px;padding:3px 0;}
4963
5158
  /* Skip = go on with whatever is (or isn't) filled in. It deliberately does
4964
5159
  NOT erase an answer already given: a visitor who typed a description and
4965
5160
  then pressed Skip meant "I'm done here", not "throw that away". */
4966
- $('skipDetail').addEventListener('click', function () { show(6, true); });
5161
+ $('skipDetail').addEventListener('click', function () { show(7, true); });
4967
5162
 
4968
5163
  /* ── the module picker (task 1002339, BV1.R20) ──
4969
5164
  ONE public read gives the panel both halves it needs: `optional` is the
@@ -5006,8 +5201,8 @@ summary{min-height:24px;padding:3px 0;}
5006
5201
  exact dishonesty the About row's "off the public map" clause exists to
5007
5202
  prevent, one screen before Create. */
5008
5203
  function repaintForBundles() {
5009
- if (state.step === 6) renderModules();
5010
- else if (state.step === 11) renderReview();
5204
+ if (state.step === 7) renderModules();
5205
+ else if (state.step === 12) renderReview();
5011
5206
  }
5012
5207
  /* The row for the declared type, or null before the read lands / for a type
5013
5208
  the table has no row for (including no type at all). */
@@ -5222,7 +5417,7 @@ summary{min-height:24px;padding:3px 0;}
5222
5417
  /* Skip = walk past without answering. It does NOT erase a toggle already
5223
5418
  made (the About step's rule), and with nothing said it leaves state.modules
5224
5419
  null so the server applies the bundle this panel was showing. */
5225
- $('skipModules').addEventListener('click', function () { show(7, true); });
5420
+ $('skipModules').addEventListener('click', function () { show(8, true); });
5226
5421
 
5227
5422
  /* ── GitHub-integrated repo picker (adopt) + auto-create (fresh) — task 1002099 / ADR 0143.
5228
5423
  Progressive enhancement: probe GET /github/repos once we reach the Home step.
@@ -5410,9 +5605,340 @@ summary{min-height:24px;padding:3px 0;}
5410
5605
  saveDraft();
5411
5606
  });
5412
5607
 
5608
+ /* ── planet physics, questions 1 to 4 (task 1004418, BV2.PS07) ──
5609
+ One wizard step with its own inner sequence: 0 account-or-scratch, 1 state,
5610
+ 2 geography, 3 ages, 4 entity, and the stop screen. The answers live in
5611
+ state.physics.answers under planet-physics.js's own keys, so the create sends
5612
+ them as `detail` untouched. The vocabularies below are COPIES of that file's
5613
+ (a page cannot require a module), held equal by tests/wizard_physics.mjs. */
5614
+ var PHYS_COUNTRIES = ('AD AE AF AG AI AL AM AO AQ AR AS AT AU AW AX AZ BA BB BD BE BF BG BH BI BJ BL BM BN BO BQ ' +
5615
+ 'BR BS BT BV BW BY BZ CA CC CD CF CG CH CI CK CL CM CN CO CR CU CV CW CX CY CZ DE DJ DK DM ' +
5616
+ 'DO DZ EC EE EG EH ER ES ET FI FJ FK FM FO FR GA GB GD GE GF GG GH GI GL GM GN GP GQ GR GS ' +
5617
+ 'GT GU GW GY HK HM HN HR HT HU ID IE IL IM IN IO IQ IR IS IT JE JM JO JP KE KG KH KI KM KN ' +
5618
+ 'KP KR KW KY KZ LA LB LC LI LK LR LS LT LU LV LY MA MC MD ME MF MG MH MK ML MM MN MO MP MQ ' +
5619
+ 'MR MS MT MU MV MW MX MY MZ NA NC NE NF NG NI NL NO NP NR NU NZ OM PA PE PF PG PH PK PL PM ' +
5620
+ 'PN PR PS PT PW PY QA RE RO RS RU RW SA SB SC SD SE SG SH SI SJ SK SL SM SN SO SR SS ST SV ' +
5621
+ 'SX SY SZ TC TD TF TG TH TJ TK TL TM TN TO TR TT TV TW TZ UA UG UM US UY UZ VA VC VE VG VI ' +
5622
+ 'VN VU WF WS YE YT ZA ZM ZW').split(' ');
5623
+ var PHYS_REGIONS = {
5624
+ 'US-AL': 'Alabama', 'US-AK': 'Alaska', 'US-AZ': 'Arizona', 'US-AR': 'Arkansas', 'US-CA': 'California',
5625
+ 'US-CO': 'Colorado', 'US-CT': 'Connecticut', 'US-DE': 'Delaware', 'US-FL': 'Florida', 'US-GA': 'Georgia',
5626
+ 'US-HI': 'Hawaii', 'US-ID': 'Idaho', 'US-IL': 'Illinois', 'US-IN': 'Indiana', 'US-IA': 'Iowa',
5627
+ 'US-KS': 'Kansas', 'US-KY': 'Kentucky', 'US-LA': 'Louisiana', 'US-ME': 'Maine', 'US-MD': 'Maryland',
5628
+ 'US-MA': 'Massachusetts', 'US-MI': 'Michigan', 'US-MN': 'Minnesota', 'US-MS': 'Mississippi', 'US-MO': 'Missouri',
5629
+ 'US-MT': 'Montana', 'US-NE': 'Nebraska', 'US-NV': 'Nevada', 'US-NH': 'New Hampshire', 'US-NJ': 'New Jersey',
5630
+ 'US-NM': 'New Mexico', 'US-NY': 'New York', 'US-NC': 'North Carolina', 'US-ND': 'North Dakota', 'US-OH': 'Ohio',
5631
+ 'US-OK': 'Oklahoma', 'US-OR': 'Oregon', 'US-PA': 'Pennsylvania', 'US-RI': 'Rhode Island', 'US-SC': 'South Carolina',
5632
+ 'US-SD': 'South Dakota', 'US-TN': 'Tennessee', 'US-TX': 'Texas', 'US-UT': 'Utah', 'US-VT': 'Vermont',
5633
+ 'US-VA': 'Virginia', 'US-WA': 'Washington', 'US-WV': 'West Virginia', 'US-WI': 'Wisconsin', 'US-WY': 'Wyoming',
5634
+ 'US-DC': 'District of Columbia', 'US-AS': 'American Samoa', 'US-GU': 'Guam', 'US-MP': 'Northern Mariana Islands',
5635
+ 'US-PR': 'Puerto Rico', 'US-UM': 'U.S. Minor Outlying Islands', 'US-VI': 'U.S. Virgin Islands',
5636
+ 'CA-AB': 'Alberta', 'CA-BC': 'British Columbia', 'CA-MB': 'Manitoba', 'CA-NB': 'New Brunswick',
5637
+ 'CA-NL': 'Newfoundland and Labrador', 'CA-NS': 'Nova Scotia', 'CA-NT': 'Northwest Territories', 'CA-NU': 'Nunavut',
5638
+ 'CA-ON': 'Ontario', 'CA-PE': 'Prince Edward Island', 'CA-QC': 'Quebec', 'CA-SK': 'Saskatchewan', 'CA-YT': 'Yukon'
5639
+ };
5640
+ var PHYS_ENTITY_FORMS = ['nonprofit', 'for-profit', 'corporate-division', 'research', 'university-project', 'social-group', 'single-owner-llc', 'private-individual'];
5641
+ /* The core material the entity gives the planet (the direction doc's placeholder
5642
+ art direction): frost for one person, chrome for a group, gold for an
5643
+ organization, obsidian for a nonprofit. */
5644
+ var PHYS_MATERIAL = {
5645
+ 'intent-solo': 'frost', 'single-owner-llc': 'frost', 'private-individual': 'frost',
5646
+ 'university-project': 'chrome', 'social-group': 'chrome', 'intent-unsure': 'chrome',
5647
+ 'intent-company': 'gold', 'for-profit': 'gold', 'corporate-division': 'gold', 'research': 'gold',
5648
+ 'intent-nonprofit': 'obsidian', 'nonprofit': 'obsidian'
5649
+ };
5650
+ var PHYS_CORE_WORD = { frost: 'individual', chrome: 'group', gold: 'organization', obsidian: 'nonprofit' };
5651
+ var PHYS_GEO_WORD = { 'us-only': 'us only', 'us-eu': 'us + eu', 'global': 'global', 'not-sure': 'not sure yet' };
5652
+ var PHYS_LAST = 4;
5653
+ var physLimits = [];
5654
+
5655
+ function phys() {
5656
+ if (!state.physics || typeof state.physics !== 'object') state.physics = { q: 0, start: null, answers: {} };
5657
+ if (!state.physics.answers || typeof state.physics.answers !== 'object') state.physics.answers = {};
5658
+ return state.physics;
5659
+ }
5660
+ /* The answers as the create sends them: only what was answered, and the existing
5661
+ work only for an existing planet (the soft-limit table hides it for a new one). */
5662
+ function physDetail() {
5663
+ var a = phys().answers, out = {};
5664
+ ['project_state', 'home_country', 'home_region', 'builder_geography', 'minors_on_board', 'entity'].forEach(function (k) {
5665
+ if (a[k] !== undefined && a[k] !== null && a[k] !== '') out[k] = a[k];
5666
+ });
5667
+ if (a.project_state === 'existing' && Array.isArray(a.existing_work) && a.existing_work.length) {
5668
+ out.existing_work = a.existing_work.slice();
5669
+ if (a.main_work && a.existing_work.indexOf(a.main_work) !== -1) out.main_work = a.main_work;
5670
+ }
5671
+ return out;
5672
+ }
5673
+ function createDetail() {
5674
+ var d = physDetail();
5675
+ if (state.teamShape) d.team_shape = state.teamShape;
5676
+ return Object.keys(d).length ? d : undefined;
5677
+ }
5678
+
5679
+ function physFillSelects() {
5680
+ var c = $('physCountry'), r = $('physRegion');
5681
+ if (c.options.length === 1) {
5682
+ var names = null;
5683
+ try { names = new Intl.DisplayNames(['en'], { type: 'region' }); } catch (e) {}
5684
+ PHYS_COUNTRIES.map(function (code) { return { code: code, name: (names && names.of(code)) || code }; })
5685
+ .sort(function (x, y) { return x.name.localeCompare(y.name); })
5686
+ .forEach(function (o) { var opt = document.createElement('option'); opt.value = o.code; opt.textContent = o.name; c.appendChild(opt); });
5687
+ }
5688
+ var a = phys().answers, country = a.home_country || '';
5689
+ c.value = country;
5690
+ while (r.options.length > 1) r.removeChild(r.lastChild);
5691
+ Object.keys(PHYS_REGIONS).filter(function (k) { return k.indexOf(country + '-') === 0; }).forEach(function (k) {
5692
+ var opt = document.createElement('option'); opt.value = k; opt.textContent = PHYS_REGIONS[k]; r.appendChild(opt);
5693
+ });
5694
+ $('physRegionField').hidden = r.options.length === 1;
5695
+ r.value = a.home_region || '';
5696
+ }
5697
+
5698
+ /* The stage: the planet forming from the world's own plates. A seed for a new
5699
+ planet, a formed one for an existing; the core material once the entity is
5700
+ known; a protective ring when young builders are on board. */
5701
+ function physPaintStage() {
5702
+ var p = phys(), a = p.answers;
5703
+ var mat = PHYS_MATERIAL[a.entity] || null;
5704
+ var src = mat && mat !== 'chrome' ? '/assets/world/r3-' + mat + '-blob-cut.webp'
5705
+ : a.project_state === 'existing' ? '/assets/world/r2-chrome-blobs-set-cut.webp'
5706
+ : '/assets/world/r2-chrome-blob-one-cut.webp';
5707
+ var img = $('physPlanet');
5708
+ if (img.getAttribute('src') !== src) img.setAttribute('src', src);
5709
+ img.setAttribute('data-size', String(p.q === 'stop' ? 0 : Math.max(1, Math.min(p.q, PHYS_LAST) + (a.project_state === 'existing' ? 2 : 1))));
5710
+ img.hidden = p.q === 'stop';
5711
+ $('physGuard').hidden = a.minors_on_board !== true;
5712
+ $('physStage').classList.toggle('physStopped', p.q === 'stop');
5713
+ $('physCapN').textContent = p.q === 'stop' ? 'stopped' : (typeof p.q === 'number' ? p.q : 0) + ' of 7';
5714
+ var call = 'physics · the rules it lives by · each answer shapes your planet here';
5715
+ if (p.q === 'stop') call = 'no planet forms · stopped at state · nothing was created';
5716
+ else if (p.q === 4 && mat) call = 'core · ' + PHYS_CORE_WORD[mat] + ' · a ' + mat + ' core';
5717
+ else if (p.q === 3 && a.minors_on_board === true) call = 'protective ring · under 18s on board · the age tiers apply';
5718
+ 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] : '');
5719
+ 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';
5720
+ $('physCallout').textContent = call;
5721
+ var coords = [];
5722
+ if (a.project_state) coords.push('state ' + a.project_state);
5723
+ if (a.home_country) coords.push('home ' + (a.home_region || a.home_country).toLowerCase());
5724
+ if (a.builder_geography) coords.push('builders ' + PHYS_GEO_WORD[a.builder_geography]);
5725
+ if (a.minors_on_board !== undefined && a.minors_on_board !== null) coords.push(a.minors_on_board ? 'ages 13+' : 'ages 18+');
5726
+ if (mat) coords.push('core ' + PHYS_CORE_WORD[mat]);
5727
+ $('physCoords').textContent = coords.join(' · ');
5728
+ }
5729
+
5730
+ /* The soft-limit table's verdicts (spec D6): an option it hides is hidden and
5731
+ one it greys is disabled, each with its reason beside it. */
5732
+ function physApplyLimits() {
5733
+ root.querySelectorAll('#panel-physics [data-why]').forEach(function (el) { el.hidden = true; el.textContent = ''; });
5734
+ root.querySelectorAll('#panel-physics [data-limit-grey]').forEach(function (el) { el.removeAttribute('data-limit-grey'); el.disabled = false; });
5735
+ physLimits.forEach(function (l) {
5736
+ var key = String(l.option || '');
5737
+ var why = root.querySelector('#panel-physics [data-why="' + key + '"]');
5738
+ if (why && l.reason) { why.textContent = l.reason; why.hidden = false; }
5739
+ if (l.effect === 'grey') {
5740
+ var parts = key.split(':');
5741
+ root.querySelectorAll('#panel-physics [data-phys="' + parts[0] + '"][data-val="' + (parts[1] || '') + '"]').forEach(function (b) {
5742
+ b.disabled = true; b.setAttribute('data-limit-grey', '1'); b.title = l.reason || '';
5743
+ });
5744
+ }
5745
+ });
5746
+ }
5747
+
5748
+ function paintPhysics() {
5749
+ var p = phys(), a = p.answers;
5750
+ root.querySelectorAll('#panel-physics .physQ').forEach(function (el) {
5751
+ el.hidden = String(el.getAttribute('data-q')) !== String(p.q);
5752
+ });
5753
+ root.querySelectorAll('#physRail li').forEach(function (li) {
5754
+ var n = Number(li.getAttribute('data-q'));
5755
+ li.classList.toggle('now', n === p.q);
5756
+ li.classList.toggle('done', !!n && typeof p.q === 'number' && n < p.q);
5757
+ });
5758
+ $('physRail').hidden = p.q === 'stop' || p.q === 0;
5759
+ $('physNav').hidden = p.q === 'stop';
5760
+ root.querySelectorAll('[data-phys-start]').forEach(function (b) { b.setAttribute('aria-pressed', String(b.getAttribute('data-phys-start') === p.start)); });
5761
+ root.querySelectorAll('#panel-physics [data-phys]').forEach(function (b) {
5762
+ var v = a[b.getAttribute('data-phys')];
5763
+ b.setAttribute('aria-pressed', String(v !== undefined && v !== null && String(v) === b.getAttribute('data-val')));
5764
+ });
5765
+ var existing = a.project_state === 'existing';
5766
+ $('physExisting').hidden = !existing;
5767
+ var works = Array.isArray(a.existing_work) ? a.existing_work : [];
5768
+ root.querySelectorAll('#physExisting [data-work]').forEach(function (cb) { cb.checked = works.indexOf(cb.getAttribute('data-work')) !== -1; });
5769
+ root.querySelectorAll('#physExisting input[name="physMain"]').forEach(function (r) {
5770
+ r.checked = a.main_work === r.value;
5771
+ r.disabled = works.indexOf(r.value) === -1;
5772
+ });
5773
+ $('physDesc').value = state.description || '';
5774
+ $('physAges').hidden = a.minors_on_board !== true;
5775
+ var forms = existing || PHYS_ENTITY_FORMS.indexOf(a.entity) !== -1 || p.entityForms === true;
5776
+ $('physForms').hidden = !forms;
5777
+ $('physIntent').hidden = forms;
5778
+ if (p.q === 0 && !physAccountLoaded) { physAccountLoaded = true; physLoadAccount(); }
5779
+ physFillSelects();
5780
+ physApplyLimits();
5781
+ physPaintStage();
5782
+ }
5783
+
5784
+ function physHint(msg) {
5785
+ $('h-phys').textContent = msg || ' ';
5786
+ $('h-phys').className = msg ? 'hint err' : 'hint';
5787
+ }
5788
+ function physCheck() {
5789
+ return fetch(API + '/provisioning/physics-check', {
5790
+ method: 'POST', credentials: 'same-origin', headers: { 'Content-Type': 'application/json' },
5791
+ body: JSON.stringify({ description: String(state.description || '').trim() || undefined, detail: physDetail() })
5792
+ }).then(function (r) { return r.ok ? r.json() : null; }).catch(function () { return null; });
5793
+ }
5794
+ function physGo(q) {
5795
+ phys().q = q;
5796
+ saveDraft();
5797
+ paintPhysics();
5798
+ var h = root.querySelector('#panel-physics .physQ[data-q="' + q + '"] h1');
5799
+ if (h) { h.setAttribute('tabindex', '-1'); h.focus({ preventScroll: false }); }
5800
+ }
5801
+
5802
+ /* Continue: check this question, then move on. Question 1 asks the server's
5803
+ screen first — a description it stops ends here, before anything exists. */
5804
+ var physBusy = false;
5805
+ function physAdvance() {
5806
+ var p = phys(), a = p.answers;
5807
+ physHint('');
5808
+ if (p.q === 0) { if (!p.start) p.start = 'scratch'; physGo(1); return false; }
5809
+ if (p.q === 1) {
5810
+ if (!a.project_state) { physHint('Pick new or existing to go on.'); return false; }
5811
+ if (a.project_state === 'existing') {
5812
+ var works = Array.isArray(a.existing_work) ? a.existing_work : [];
5813
+ if (!works.length) { physHint('Pick what already exists, then your main one.'); return false; }
5814
+ if (works.indexOf(a.main_work) === -1) { physHint('Mark which of them is your main one.'); return false; }
5815
+ }
5816
+ if (physBusy) return false;
5817
+ physBusy = true;
5818
+ physCheck().then(function (res) {
5819
+ physBusy = false;
5820
+ if (res && res.stop) { $('physStopMsg').textContent = res.message || ''; physGo('stop'); return; }
5821
+ physLimits = (res && res.soft_limits && res.soft_limits.limits) || [];
5822
+ physGo(2);
5823
+ });
5824
+ return false;
5825
+ }
5826
+ if (p.q === 2) {
5827
+ if (!a.home_country) { physHint('Pick your country.'); return false; }
5828
+ if (!a.builder_geography) { physHint('Say where you expect builders to be. Not sure yet is a fine answer.'); return false; }
5829
+ physGo(3); return false;
5830
+ }
5831
+ if (p.q === 3) {
5832
+ if (a.minors_on_board !== true && a.minors_on_board !== false) { physHint('Answer yes or no to go on.'); return false; }
5833
+ physGo(4); return false;
5834
+ }
5835
+ if (p.q === 4) {
5836
+ if (!a.entity) { physHint('Pick the one that fits best. Don’t know yet is a fine answer.'); return false; }
5837
+ physCheck().then(function (res) { physLimits = (res && res.soft_limits && res.soft_limits.limits) || []; });
5838
+ show(4, true);
5839
+ return false;
5840
+ }
5841
+ return false;
5842
+ }
5843
+
5844
+ $('physBack').addEventListener('click', function () {
5845
+ var p = phys();
5846
+ physHint('');
5847
+ if (typeof p.q === 'number' && p.q > 0) physGo(p.q - 1); else show(2, true);
5848
+ });
5849
+ $('physStartOver').addEventListener('click', function () {
5850
+ state.description = '';
5851
+ if ($('f-desc')) $('f-desc').value = '';
5852
+ phys().answers = {};
5853
+ physGo(1);
5854
+ });
5855
+ root.querySelectorAll('[data-phys-start]').forEach(function (b) {
5856
+ b.addEventListener('click', function () {
5857
+ var p = phys();
5858
+ p.start = b.getAttribute('data-phys-start');
5859
+ if (p.start === 'account' && physAccountAnswers) {
5860
+ Object.keys(physAccountAnswers).forEach(function (k) { p.answers[k] = physAccountAnswers[k]; });
5861
+ }
5862
+ saveDraft(); paintPhysics();
5863
+ });
5864
+ });
5865
+ root.querySelectorAll('#panel-physics [data-phys]').forEach(function (b) {
5866
+ b.addEventListener('click', function () {
5867
+ var k = b.getAttribute('data-phys'), raw = b.getAttribute('data-val');
5868
+ var v = raw === 'true' ? true : raw === 'false' ? false : raw;
5869
+ var a = phys().answers;
5870
+ a[k] = a[k] === v ? null : v; /* a second click un-says it, like the team pills */
5871
+ if (k === 'project_state' && a[k] !== 'existing') { delete a.existing_work; delete a.main_work; }
5872
+ physHint(''); saveDraft(); paintPhysics();
5873
+ });
5874
+ });
5875
+ root.querySelectorAll('#physExisting [data-work]').forEach(function (cb) {
5876
+ cb.addEventListener('change', function () {
5877
+ var a = phys().answers, w = cb.getAttribute('data-work');
5878
+ var list = Array.isArray(a.existing_work) ? a.existing_work.slice() : [];
5879
+ if (cb.checked && list.indexOf(w) === -1) list.push(w);
5880
+ if (!cb.checked) list = list.filter(function (x) { return x !== w; });
5881
+ a.existing_work = list;
5882
+ if (list.length === 1) a.main_work = list[0];
5883
+ if (list.indexOf(a.main_work) === -1) a.main_work = list.length ? list[0] : null;
5884
+ saveDraft(); paintPhysics();
5885
+ });
5886
+ });
5887
+ root.querySelectorAll('#physExisting input[name="physMain"]').forEach(function (r) {
5888
+ r.addEventListener('change', function () { if (r.checked) { phys().answers.main_work = r.value; saveDraft(); paintPhysics(); } });
5889
+ });
5890
+ $('physDesc').addEventListener('input', function () {
5891
+ state.description = $('physDesc').value;
5892
+ if ($('f-desc')) $('f-desc').value = state.description;
5893
+ saveDraft();
5894
+ });
5895
+ $('physCountry').addEventListener('change', function () {
5896
+ var a = phys().answers;
5897
+ a.home_country = $('physCountry').value || null;
5898
+ if (a.home_region && a.home_region.indexOf((a.home_country || '') + '-') !== 0) a.home_region = null;
5899
+ saveDraft(); paintPhysics();
5900
+ });
5901
+ $('physRegion').addEventListener('change', function () { phys().answers.home_region = $('physRegion').value || null; saveDraft(); physPaintStage(); });
5902
+ $('physHaveEntity').addEventListener('click', function () { phys().entityForms = true; saveDraft(); paintPhysics(); });
5903
+ $('physNoEntity').addEventListener('click', function () {
5904
+ var p = phys();
5905
+ p.entityForms = false;
5906
+ if (PHYS_ENTITY_FORMS.indexOf(p.answers.entity) !== -1) p.answers.entity = null;
5907
+ saveDraft(); paintPhysics();
5908
+ });
5909
+
5910
+ /* Auto-fill: the answers this account already gave on its most recent project —
5911
+ the only details the platform holds about a founder. With none, the door says so
5912
+ and stays shut. The state of the planet is never copied: it is this project's. */
5913
+ var physAccountAnswers = null;
5914
+ var physAccountLoaded = false;
5915
+ function physLoadAccount() {
5916
+ return fetch(API + '/provisioning/instances', { credentials: 'same-origin' })
5917
+ .then(function (r) { return r.ok ? r.json() : null; })
5918
+ .then(function (d) {
5919
+ var list = (d && (d.instances || d)) || [];
5920
+ if (!Array.isArray(list)) list = [];
5921
+ var withDetail = list.filter(function (i) { return i && i.detail && typeof i.detail === 'object'; })
5922
+ .sort(function (x, y) { return String(y.created_at || '').localeCompare(String(x.created_at || '')); })[0];
5923
+ var pick = {};
5924
+ if (withDetail) ['home_country', 'home_region', 'builder_geography', 'minors_on_board', 'entity'].forEach(function (k) {
5925
+ if (withDetail.detail[k] !== undefined && withDetail.detail[k] !== null) pick[k] = withDetail.detail[k];
5926
+ });
5927
+ physAccountAnswers = Object.keys(pick).length ? pick : null;
5928
+ $('physFromAccount').disabled = !physAccountAnswers;
5929
+ var why = $('physAccountWhy');
5930
+ why.hidden = !!physAccountAnswers;
5931
+ why.textContent = physAccountAnswers ? '' : 'Nothing to fill from yet: your account has no other project with these answers.';
5932
+ })
5933
+ .catch(function () {});
5934
+ }
5935
+
5413
5936
  /* per-panel validation on Next */
5414
5937
  function validateStep(step) {
5415
- if (step === 9) return validateRender();
5938
+ if (step === 10) return validateRender();
5939
+ /* planet physics (task 1004418): Continue walks the questions inside the step,
5940
+ and only the last one moves the wizard on, so this never says yes itself */
5941
+ if (step === 3) return physAdvance();
5416
5942
  /* the fork's own door is "Begin planet creation": a real project, not the demo */
5417
5943
  if (step === 2) { state.demo = null; return true; }
5418
5944
  /* the front door's one tick (task 1004417): nothing past it until it is given */
@@ -5424,11 +5950,11 @@ summary{min-height:24px;padding:3px 0;}
5424
5950
  }
5425
5951
  return true;
5426
5952
  }
5427
- if (step === 3) {
5953
+ if (step === 4) {
5428
5954
  if (!state.name.trim()) { $('h-name').textContent = 'Give it a name to start.'; $('h-name').className = 'hint err'; return false; }
5429
5955
  return true;
5430
5956
  }
5431
- if (step === 4) {
5957
+ if (step === 5) {
5432
5958
  if (!state.projType) {
5433
5959
  $('h-projtype').textContent = 'Pick one — Not Sure is a fine answer.';
5434
5960
  $('h-projtype').className = 'hint err';
@@ -5436,10 +5962,10 @@ summary{min-height:24px;padding:3px 0;}
5436
5962
  }
5437
5963
  return true;
5438
5964
  }
5439
- /* steps 5 (About) and 6 (Modules) have no clause: they are the optional
5965
+ /* steps 6 (About) and 7 (Modules) have no clause: they are the optional
5440
5966
  ones, and Next past an empty answer is the whole point (tasks 1002334,
5441
5967
  1002339). Step 2 (the fork) has none either: its doors are the answer. */
5442
- if (step === 7) {
5968
+ if (step === 8) {
5443
5969
  var homeErr = function (msg) { $('h-home').textContent = msg; $('h-home').className = 'hint err'; return false; };
5444
5970
  if (repoAccess === 'need-connect') return homeErr('Allow GitHub repo access to pick or create your repository — or choose “enter it manually”.');
5445
5971
  if (repoAccess === true && state.mode !== 'adopt') {
@@ -5454,7 +5980,7 @@ summary{min-height:24px;padding:3px 0;}
5454
5980
  $('h-home').innerHTML = '&nbsp;'; $('h-home').className = 'hint';
5455
5981
  return true;
5456
5982
  }
5457
- if (step === 8) {
5983
+ if (step === 9) {
5458
5984
  if (!validSlug(state.slug)) {
5459
5985
  $('h-slug').textContent = '2–40 lowercase letters, numbers, or dashes; no leading/trailing/double dash.';
5460
5986
  $('h-slug').className = 'hint err'; $('f-slug').classList.add('bad');
@@ -5591,7 +6117,7 @@ summary{min-height:24px;padding:3px 0;}
5591
6117
  : 'Once the project is up, we create your app on your Render account. Your key is used once, then thrown away.';
5592
6118
  /* then the app's own progress, from the same read the project's page uses (task 1004353) */
5593
6119
  var box = $('doneApp');
5594
- if (box) watchApp(instanceId, box, function () { return state.step === 12; }, 5000);
6120
+ if (box) watchApp(instanceId, box, function () { return state.step === 13; }, 5000);
5595
6121
  });
5596
6122
  }).catch(function (err) {
5597
6123
  note.className = 'hint err';
@@ -5766,7 +6292,7 @@ summary{min-height:24px;padding:3px 0;}
5766
6292
  because absent means SKIPPED and an empty string would read as an
5767
6293
  answered-but-blank description to the publish gate */
5768
6294
  description: String(state.description || '').trim() || undefined,
5769
- detail: state.teamShape ? { team_shape: state.teamShape } : undefined,
6295
+ detail: createDetail(),
5770
6296
  /* the picker's answer (task 1002339) — omitted while unanswered, and
5771
6297
  the server then applies the SAME starter bundle the panel showed,
5772
6298
  so a skip and an accept land the same modules */
@@ -5820,7 +6346,7 @@ summary{min-height:24px;padding:3px 0;}
5820
6346
  sessionStorage.removeItem(DRAFT_KEY);
5821
6347
  } catch (e) {}
5822
6348
  renderDone();
5823
- show(12, true);
6349
+ show(13, true);
5824
6350
  sendRenderSetup(data.instance && data.instance.id); // the key rides the new project's own setup run (task 1004352)
5825
6351
  sendLookSetup(data.instance && data.instance.id); // the look + logo, like the Render key: right after Create (task 1004421)
5826
6352
  startProgress(data.instance && data.instance.id);
@@ -5830,7 +6356,7 @@ summary{min-height:24px;padding:3px 0;}
5830
6356
  if (err && err.gotoHome) {
5831
6357
  /* A repo-creation problem (name taken / reconnect) — bounce back to the Home
5832
6358
  step with the reason, so the user fixes it where the repo is chosen. */
5833
- show(7, true);
6359
+ show(8, true);
5834
6360
  $('h-home').textContent = err.homeMsg || 'Please revisit the repository step.';
5835
6361
  $('h-home').className = 'hint err';
5836
6362
  return;
@@ -6398,7 +6924,7 @@ summary{min-height:24px;padding:3px 0;}
6398
6924
  5s tick never clobbers an open dropdown or mid-flight button) */
6399
6925
  if (inst && inst.status !== live.instStatus) {
6400
6926
  live.instStatus = inst.status; renderSteps();
6401
- if (state.step === 12) paintRail(12); /* the rail finishes only when the project answers */
6927
+ if (state.step === 13) paintRail(13); /* the rail finishes only when the project answers */
6402
6928
  }
6403
6929
  var terminal = renderProgress(inst);
6404
6930
  if (!terminal) pollTimer = setTimeout(tick, 5000);
@@ -6438,7 +6964,7 @@ summary{min-height:24px;padding:3px 0;}
6438
6964
  is three-valued and null is its "nothing said" sentinel; leaving the key out
6439
6965
  left it UNDEFINED, which is not null — so pickedModules() reached .slice()
6440
6966
  on it and the second project's Modules step threw. */
6441
- 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', demo: null, look: null, termsAck: false };
6967
+ 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 };
6442
6968
  $('newName').value = ''; $('f-owner').value = ''; $('f-repo').value = ''; $('f-slug').value = ''; $('f-domain').value = '';
6443
6969
  $('f-noaddr').checked = false; $('f-desc').value = '';
6444
6970
  /* Re-seed the owner from the signed-in account for the next project. */
@@ -6517,7 +7043,7 @@ summary{min-height:24px;padding:3px 0;}
6517
7043
  if (focus && !resumeIntent) {
6518
7044
  var draft = null;
6519
7045
  try { draft = JSON.parse(sessionStorage.getItem(DRAFT_KEY) || 'null'); } catch (e) {}
6520
- var at = draft && draft.step >= 1 && draft.step <= 11 ? draft.step : 0;
7046
+ var at = draft && draft.step >= 1 && draft.step <= 12 ? draft.step : 0;
6521
7047
  if (!at) { resetForNewProject(); at = 1; }
6522
7048
  paintResumeOffer(created);
6523
7049
  /* step 1's own focus target is its one control, the terms tick, not the
@@ -6541,7 +7067,7 @@ summary{min-height:24px;padding:3px 0;}
6541
7067
  manifestState = created.manifest_state; manifestPending = true;
6542
7068
  }
6543
7069
  renderDone();
6544
- show(12, !!focus);
7070
+ show(13, !!focus);
6545
7071
  startProgress(created.id);
6546
7072
  resumePolls();
6547
7073
  }
@@ -6558,7 +7084,7 @@ summary{min-height:24px;padding:3px 0;}
6558
7084
  /* before identity settles, never render past step 1 (the terms, since task
6559
7085
  1004417): deeper steps carry authenticated side effects (the Home-step repo
6560
7086
  probe), and onAuth() re-enters to the real draft step the moment /me answers */
6561
- var step = authKnown && state.step >= 1 && state.step <= 11 ? state.step : 1;
7087
+ var step = authKnown && state.step >= 1 && state.step <= 12 ? state.step : 1;
6562
7088
  if (step === 1) {
6563
7089
  /* Pre-auth, step 1 is a PLACEHOLDER, not where the visitor is — but show()
6564
7090
  persists whatever step it renders, so it used to overwrite the draft's real
@@ -6567,7 +7093,7 @@ summary{min-height:24px;padding:3px 0;}
6567
7093
  step back after rendering the placeholder, in memory AND in the draft. */
6568
7094
  var resumeStep = state.step;
6569
7095
  show(1, false);
6570
- if (!authKnown && resumeStep >= 1 && resumeStep <= 11) { state.step = resumeStep; saveDraft(); }
7096
+ if (!authKnown && resumeStep >= 1 && resumeStep <= 12) { state.step = resumeStep; saveDraft(); }
6571
7097
  if (focus) $('termsAck').focus();
6572
7098
  } else {
6573
7099
  show(step, !!focus);