plutonium 0.62.2 → 0.64.0

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 (256) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/skills/plutonium/SKILL.md +44 -0
  3. data/.claude/skills/plutonium-app/SKILL.md +3 -3
  4. data/.claude/skills/plutonium-async-interactions/SKILL.md +191 -0
  5. data/.claude/skills/plutonium-auth/SKILL.md +36 -0
  6. data/.claude/skills/plutonium-behavior/SKILL.md +121 -24
  7. data/.claude/skills/plutonium-kanban/SKILL.md +17 -3
  8. data/.claude/skills/plutonium-resource/SKILL.md +259 -12
  9. data/.claude/skills/plutonium-tenancy/SKILL.md +32 -3
  10. data/.claude/skills/plutonium-ui/SKILL.md +115 -14
  11. data/.claude/skills/plutonium-wizard/SKILL.md +73 -4
  12. data/.standard.yml +1 -1
  13. data/CHANGELOG.md +73 -0
  14. data/CLAUDE.md +87 -0
  15. data/Rakefile +34 -0
  16. data/SECURITY.md +1 -1
  17. data/app/assets/plutonium.css +1 -1
  18. data/app/assets/plutonium.js +685 -102
  19. data/app/assets/plutonium.js.map +4 -4
  20. data/app/assets/plutonium.min.js +53 -53
  21. data/app/assets/plutonium.min.js.map +4 -4
  22. data/app/views/rodauth/_login_form.html.erb +13 -0
  23. data/db/migrate/async_interactions/20260817000001_create_plutonium_async_runs.rb +170 -0
  24. data/docs/.vitepress/config.ts +81 -3
  25. data/docs/.vitepress/theme/blog.data.ts +44 -0
  26. data/docs/.vitepress/theme/components/BlogIndex.vue +87 -0
  27. data/docs/.vitepress/theme/components/BlogMeta.vue +47 -0
  28. data/docs/.vitepress/theme/components/HomeFeatureTour.vue +293 -0
  29. data/docs/.vitepress/theme/components/HomeHero.vue +3 -3
  30. data/docs/.vitepress/theme/components/HomeInTheBox.vue +8 -0
  31. data/docs/.vitepress/theme/components/HomeStopWriting.vue +1 -0
  32. data/docs/.vitepress/theme/components/HomeWhyPlutonium.vue +84 -0
  33. data/docs/.vitepress/theme/index.ts +8 -4
  34. data/docs/blog/association-inputs-post-signed-ids.md +70 -0
  35. data/docs/blog/fix-the-model-not-the-policy.md +122 -0
  36. data/docs/blog/fractional-ordering-runs-out-of-room.md +67 -0
  37. data/docs/blog/half-finished-forms-are-pii.md +76 -0
  38. data/docs/blog/index.md +13 -0
  39. data/docs/blog/interactions-are-presentation-objects.md +152 -0
  40. data/docs/blog/introducing-plutonium.md +253 -0
  41. data/docs/blog/jobs-are-not-permission-snapshots.md +100 -0
  42. data/docs/blog/plutonium-and-ai-agents.md +48 -0
  43. data/docs/blog/realtime-is-one-line-and-four-dependencies.md +70 -0
  44. data/docs/blog/two-forms-one-dom-id.md +69 -0
  45. data/docs/blog/whats-new-async-kanban-wizards.md +130 -0
  46. data/docs/getting-started/tutorial/04-authorization.md +12 -3
  47. data/docs/getting-started/tutorial/06-nested-resources.md +3 -1
  48. data/docs/getting-started/tutorial/07-author-portal.md +2 -2
  49. data/docs/guides/authentication.md +73 -0
  50. data/docs/guides/authorization.md +2 -0
  51. data/docs/guides/creating-packages.md +5 -3
  52. data/docs/guides/custom-actions.md +74 -12
  53. data/docs/guides/customizing-ui.md +9 -2
  54. data/docs/guides/index.md +1 -0
  55. data/docs/guides/kanban.md +7 -5
  56. data/docs/guides/nested-resources.md +11 -1
  57. data/docs/guides/performance.md +104 -0
  58. data/docs/guides/user-invites.md +1 -1
  59. data/docs/guides/wizards.md +9 -1
  60. data/docs/index.md +3 -3
  61. data/docs/public/images/home/tour-actions.png +0 -0
  62. data/docs/public/images/home/tour-async.png +0 -0
  63. data/docs/public/images/home/tour-kanban.png +0 -0
  64. data/docs/public/images/home/tour-tenancy.png +0 -0
  65. data/docs/public/images/home/tour-wizard.png +0 -0
  66. data/docs/public/images/reference/async-progress-page.png +0 -0
  67. data/docs/public/images/reference/async-running-banner.png +0 -0
  68. data/docs/public/templates/experimental.rb +34 -0
  69. data/docs/public/templates/pluton8.rb +14 -0
  70. data/docs/reference/app/portals.md +15 -3
  71. data/docs/reference/auth/accounts.md +19 -0
  72. data/docs/reference/behavior/async-interactions.md +295 -0
  73. data/docs/reference/behavior/controllers.md +17 -4
  74. data/docs/reference/behavior/index.md +7 -1
  75. data/docs/reference/behavior/interactions.md +152 -22
  76. data/docs/reference/configuration.md +5 -0
  77. data/docs/reference/index.md +1 -0
  78. data/docs/reference/kanban/dsl.md +7 -4
  79. data/docs/reference/kanban/index.md +1 -1
  80. data/docs/reference/kanban/positioning.md +26 -4
  81. data/docs/reference/positioning.md +568 -0
  82. data/docs/reference/resource/actions.md +97 -4
  83. data/docs/reference/resource/definition.md +181 -9
  84. data/docs/reference/tenancy/invites.md +1 -1
  85. data/docs/reference/tenancy/nested-resources.md +60 -2
  86. data/docs/reference/ui/assets.md +4 -0
  87. data/docs/reference/ui/components.md +57 -4
  88. data/docs/reference/ui/displays.md +20 -10
  89. data/docs/reference/ui/index.md +1 -1
  90. data/docs/reference/wizard/dsl.md +33 -0
  91. data/docs/reference/wizard/storage-config.md +1 -0
  92. data/docs/superpowers/plans/2026-07-16-homepage-depth-upgrade.md +624 -0
  93. data/docs/superpowers/plans/2026-07-16-homepage-depth-upgrade.md.tasks.json +32 -0
  94. data/docs/superpowers/plans/2026-07-31-positioned-drag-and-drop.md +1787 -0
  95. data/docs/superpowers/plans/2026-07-31-positioned-drag-and-drop.md.tasks.json +91 -0
  96. data/docs/superpowers/plans/2026-08-17-async-interactions.md +1414 -0
  97. data/docs/superpowers/plans/2026-08-17-async-interactions.md.tasks.json +66 -0
  98. data/docs/superpowers/specs/2026-07-16-homepage-depth-upgrade-design.md +111 -0
  99. data/docs/superpowers/specs/2026-07-17-action-html-attributes-design.md +124 -0
  100. data/docs/superpowers/specs/2026-07-31-positioned-drag-and-drop-design.md +506 -0
  101. data/docs/superpowers/specs/2026-08-17-async-interactions-design.md +185 -0
  102. data/gemfiles/postgres.gemfile.lock +85 -85
  103. data/gemfiles/rails_7.gemfile.lock +323 -141
  104. data/gemfiles/rails_8.0.gemfile.lock +126 -116
  105. data/gemfiles/rails_8.1.gemfile.lock +127 -117
  106. data/lib/generators/pu/async_interactions/install_generator.rb +111 -0
  107. data/lib/generators/pu/async_interactions/templates/app/controllers/async_runs_controller.rb.tt +15 -0
  108. data/lib/generators/pu/core/typespec/typespec_generator.rb +7 -4
  109. data/lib/generators/pu/invites/install_generator.rb +3 -3
  110. data/lib/generators/pu/invites/templates/packages/invites/app/views/layouts/invites/invitation.html.erb.tt +2 -2
  111. data/lib/generators/pu/lib/plutonium_generators/concerns/mounts_engines.rb +47 -2
  112. data/lib/generators/pu/lib/plutonium_generators/concerns/resource_registration.rb +41 -0
  113. data/lib/generators/pu/lite/litestream/litestream_generator.rb +1 -1
  114. data/lib/generators/pu/lite/solid_queue/solid_queue_generator.rb +1 -1
  115. data/lib/generators/pu/res/conn/conn_generator.rb +19 -39
  116. data/lib/generators/pu/res/conn/templates/app/controllers/resource_controller.rb.tt +4 -0
  117. data/lib/generators/pu/rodauth/templates/app/rodauth/account_rodauth_plugin.rb.tt +15 -6
  118. data/lib/generators/pu/rodauth/templates/app/rodauth/rodauth_plugin.rb.tt +7 -0
  119. data/lib/generators/pu/saas/welcome/templates/app/views/layouts/welcome.html.erb.tt +2 -2
  120. data/lib/generators/pu/wizards/install_generator.rb +78 -0
  121. data/lib/plutonium/action/base.rb +71 -9
  122. data/lib/plutonium/action/interactive.rb +9 -0
  123. data/lib/plutonium/attachments.rb +254 -0
  124. data/lib/plutonium/configuration.rb +82 -1
  125. data/lib/plutonium/core/controller.rb +50 -7
  126. data/lib/plutonium/core/controllers/authorizable.rb +16 -0
  127. data/lib/plutonium/core/controllers/entity_scoping.rb +12 -2
  128. data/lib/plutonium/definition/base.rb +51 -0
  129. data/lib/plutonium/definition/display_layout.rb +112 -0
  130. data/lib/plutonium/definition/index_views.rb +8 -7
  131. data/lib/plutonium/definition/input_aliases.rb +38 -0
  132. data/lib/plutonium/definition/page_widths.rb +65 -0
  133. data/lib/plutonium/definition/positioning.rb +126 -0
  134. data/lib/plutonium/definition/sorting.rb +17 -2
  135. data/lib/plutonium/helpers/turbo_helper.rb +7 -0
  136. data/lib/plutonium/interaction/README.md +61 -24
  137. data/lib/plutonium/interaction/async/configuration.rb +38 -0
  138. data/lib/plutonium/interaction/async/context.rb +419 -0
  139. data/lib/plutonium/interaction/async/executor.rb +422 -0
  140. data/lib/plutonium/interaction/async/job.rb +80 -0
  141. data/lib/plutonium/interaction/async/reap_job.rb +81 -0
  142. data/lib/plutonium/interaction/async/run.rb +394 -0
  143. data/lib/plutonium/interaction/async/run_definition.rb +155 -0
  144. data/lib/plutonium/interaction/async/run_policy.rb +86 -0
  145. data/lib/plutonium/interaction/base.rb +34 -7
  146. data/lib/plutonium/interaction/concerns/dispatchable.rb +518 -0
  147. data/lib/plutonium/interaction/concerns/scoping.rb +70 -9
  148. data/lib/plutonium/interaction/response/redirect.rb +11 -3
  149. data/lib/plutonium/kanban/board.rb +14 -0
  150. data/lib/plutonium/kanban/column.rb +4 -2
  151. data/lib/plutonium/kanban/dsl.rb +4 -1
  152. data/lib/plutonium/kanban/grouping.rb +9 -22
  153. data/lib/plutonium/kanban/positioning.rb +5 -65
  154. data/lib/plutonium/positioning/config.rb +94 -0
  155. data/lib/plutonium/positioning/model.rb +128 -0
  156. data/lib/plutonium/positioning.rb +25 -86
  157. data/lib/plutonium/railtie.rb +1 -0
  158. data/lib/plutonium/resource/controller.rb +118 -38
  159. data/lib/plutonium/resource/controllers/crud_actions/index_action.rb +32 -2
  160. data/lib/plutonium/resource/controllers/crud_actions.rb +30 -2
  161. data/lib/plutonium/resource/controllers/eager_loading.rb +87 -0
  162. data/lib/plutonium/resource/controllers/export_csv.rb +10 -1
  163. data/lib/plutonium/resource/controllers/kanban_actions.rb +53 -14
  164. data/lib/plutonium/resource/controllers/position_actions.rb +390 -0
  165. data/lib/plutonium/resource/controllers/presentable.rb +19 -13
  166. data/lib/plutonium/resource/controllers/queryable.rb +5 -1
  167. data/lib/plutonium/resource/controllers/wizard_actions.rb +21 -0
  168. data/lib/plutonium/resource/policy.rb +33 -0
  169. data/lib/plutonium/resource/query_object.rb +36 -0
  170. data/lib/plutonium/routing/mapper_extensions.rb +100 -8
  171. data/lib/plutonium/routing/route_set_extensions.rb +15 -1
  172. data/lib/plutonium/routing/wizard_registration.rb +4 -0
  173. data/lib/plutonium/testing/resource_policy.rb +6 -2
  174. data/lib/plutonium/ui/action_button.rb +12 -7
  175. data/lib/plutonium/ui/actions_dropdown.rb +1 -1
  176. data/lib/plutonium/ui/block.rb +21 -1
  177. data/lib/plutonium/ui/breadcrumbs.rb +187 -55
  178. data/lib/plutonium/ui/component/methods.rb +5 -0
  179. data/lib/plutonium/ui/component/positionable.rb +112 -0
  180. data/lib/plutonium/ui/component/resolves_tags.rb +57 -0
  181. data/lib/plutonium/ui/component/section.rb +185 -0
  182. data/lib/plutonium/ui/display/base.rb +13 -1
  183. data/lib/plutonium/ui/display/components/formatted_value.rb +26 -0
  184. data/lib/plutonium/ui/display/components/section.rb +18 -0
  185. data/lib/plutonium/ui/display/resource.rb +141 -22
  186. data/lib/plutonium/ui/display/theme.rb +20 -1
  187. data/lib/plutonium/ui/export_button.rb +1 -1
  188. data/lib/plutonium/ui/form/base.rb +8 -7
  189. data/lib/plutonium/ui/form/components/intl_tel_input.rb +1 -1
  190. data/lib/plutonium/ui/form/components/section.rb +7 -62
  191. data/lib/plutonium/ui/form/components/uppy.rb +12 -1
  192. data/lib/plutonium/ui/form/concerns/renders_nested_resource_fields.rb +16 -3
  193. data/lib/plutonium/ui/form/concerns/renders_structured_inputs.rb +5 -1
  194. data/lib/plutonium/ui/form/query.rb +2 -4
  195. data/lib/plutonium/ui/form/resource.rb +92 -15
  196. data/lib/plutonium/ui/form/theme.rb +17 -0
  197. data/lib/plutonium/ui/form/wizard.rb +25 -1
  198. data/lib/plutonium/ui/grid/card.rb +79 -11
  199. data/lib/plutonium/ui/grid/resource.rb +47 -5
  200. data/lib/plutonium/ui/interaction/async/run_progress.rb +227 -0
  201. data/lib/plutonium/ui/interaction/async/running_banner.rb +65 -0
  202. data/lib/plutonium/ui/kanban/card.rb +2 -1
  203. data/lib/plutonium/ui/kanban/column.rb +12 -6
  204. data/lib/plutonium/ui/kanban/resource.rb +6 -7
  205. data/lib/plutonium/ui/layout/base.rb +10 -3
  206. data/lib/plutonium/ui/nav_grid_menu.rb +1 -0
  207. data/lib/plutonium/ui/page/base.rb +19 -0
  208. data/lib/plutonium/ui/page/edit.rb +4 -1
  209. data/lib/plutonium/ui/page/index.rb +69 -18
  210. data/lib/plutonium/ui/page/interactive_action.rb +5 -1
  211. data/lib/plutonium/ui/page/new.rb +4 -1
  212. data/lib/plutonium/ui/page/show.rb +27 -10
  213. data/lib/plutonium/ui/page/wizard.rb +10 -1
  214. data/lib/plutonium/ui/page/wizard_chooser.rb +36 -11
  215. data/lib/plutonium/ui/page_width.rb +58 -0
  216. data/lib/plutonium/ui/table/base.rb +34 -1
  217. data/lib/plutonium/ui/table/components/attachment.rb +1 -1
  218. data/lib/plutonium/ui/table/components/bulk_actions_toolbar.rb +32 -8
  219. data/lib/plutonium/ui/table/components/drag_handle.rb +120 -0
  220. data/lib/plutonium/ui/table/components/filter_form.rb +1 -4
  221. data/lib/plutonium/ui/table/components/filter_pills.rb +1 -1
  222. data/lib/plutonium/ui/table/components/row_actions_dropdown.rb +1 -1
  223. data/lib/plutonium/ui/table/resource.rb +50 -5
  224. data/lib/plutonium/ui/table/theme.rb +59 -2
  225. data/lib/plutonium/ui/wizard/review.rb +4 -2
  226. data/lib/plutonium/ui/wizard/summary_display.rb +42 -14
  227. data/lib/plutonium/version.rb +1 -1
  228. data/lib/plutonium/wizard/attachments.rb +32 -197
  229. data/lib/plutonium/wizard/base.rb +6 -1
  230. data/lib/plutonium/wizard/configuration.rb +12 -0
  231. data/lib/plutonium/wizard/controller.rb +14 -0
  232. data/lib/plutonium/wizard/driving.rb +99 -15
  233. data/lib/plutonium/wizard/dsl.rb +23 -0
  234. data/lib/plutonium/wizard/resume.rb +127 -49
  235. data/lib/plutonium/wizard/runner.rb +46 -2
  236. data/lib/plutonium/wizard/step_adapter.rb +1 -1
  237. data/lib/plutonium/wizard/sweep_job.rb +16 -0
  238. data/lib/plutonium.rb +21 -0
  239. data/lib/rodauth/features/session_isolation.rb +92 -0
  240. data/lib/rodauth/plugins.rb +1 -0
  241. data/package.json +2 -1
  242. data/plutonium.gemspec +20 -20
  243. data/src/css/components.css +89 -1
  244. data/src/css/slim_select.css +20 -0
  245. data/src/js/controllers/breadcrumbs_controller.js +112 -0
  246. data/src/js/controllers/bulk_actions_controller.js +10 -2
  247. data/src/js/controllers/kanban_controller.js +30 -21
  248. data/src/js/controllers/positioned_controller.js +452 -0
  249. data/src/js/controllers/register_controllers.js +6 -0
  250. data/src/js/controllers/resource_drop_down_controller.js +5 -0
  251. data/src/js/controllers/run_progress_controller.js +73 -0
  252. data/src/js/drag/sortable.js +186 -0
  253. data/yarn.lock +108 -63
  254. metadata +101 -22
  255. data/docs/.vitepress/theme/components/HomeAudienceSplit.vue +0 -53
  256. data/docs/.vitepress/theme/components/HomePillars.vue +0 -42
@@ -0,0 +1,624 @@
1
+ # Homepage Depth & Proof Upgrade Implementation Plan
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers-extended-cc:subagent-driven-development (recommended) or superpowers-extended-cc:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** Make the VitePress homepage sell Plutonium's depth — a new interactive feature tour (Kanban / Wizards / Actions / Multi-tenancy) with real code + screenshots, a merged "Why Plutonium" band, and copy weaves across existing sections.
6
+
7
+ **Architecture:** Two new Vue components (`HomeFeatureTour.vue`, `HomeWhyPlutonium.vue`) in the VitePress theme, registered in `index.ts`, composed in `docs/index.md`. `HomePillars.vue` and `HomeAudienceSplit.vue` are deleted (merged into `HomeWhyPlutonium`). Three existing components get copy-only edits. Screenshots are reused from `docs/public/images/guides/` — no new assets.
8
+
9
+ **Tech Stack:** VitePress + Vue 3 SFCs (`<script setup>`), `@tabler/icons-vue`, existing `pu-*` design tokens in `custom.css`. No new dependencies.
10
+
11
+ **User Verification:** YES — the user visually signs off the finished homepage (desktop + mobile) before the work is called done (Task 4).
12
+
13
+ **Spec:** `docs/superpowers/specs/2026-07-16-homepage-depth-upgrade-design.md`
14
+
15
+ ---
16
+
17
+ ## Context for the implementer
18
+
19
+ - Site source lives in `docs/`. Homepage is `docs/index.md`, which just stacks globally-registered components. Components live in `docs/.vitepress/theme/components/`, registered in `docs/.vitepress/theme/index.ts`.
20
+ - Design tokens (`--pu-accent: #d33`, `--pu-bg-dark`, `--pu-section*` classes, `.pu-term`) are in `docs/.vitepress/theme/custom.css` (~line 420+). Dark mode works by `.dark` overriding the `--pu-*` vars — always use the vars, never hardcode light-mode colors (hardcoding the dark terminal palette `#161b22`/`#30363d` is fine; it's constant in both modes, see `hw-browser-bar--term` in `HomeWalkthrough.vue`).
21
+ - Images are referenced with `withBase("/images/...")` (see `HomeWalkthrough.vue`). Class `pu-zoomable` opts an image into medium-zoom.
22
+ - Internal links in components are written with the site base included: `/plutonium-core/guides/kanban` (see `HomeInTheBox.vue`).
23
+ - Build/verify commands run from the repo root: `yarn docs:build` (includes dead-link check), `yarn docs:dev` (localhost:5173).
24
+ - There is no JS test infrastructure for the docs site. Verification = successful build + visual pass. TDD does not apply to these tasks.
25
+
26
+ ---
27
+
28
+ ### Task 1: Create `HomeFeatureTour.vue` and mount it
29
+
30
+ **Goal:** New interactive tour section — desktop feature rail / mobile accordion — showing DSL code + real screenshot for Kanban, Wizards, Actions, Multi-tenancy.
31
+
32
+ **Files:**
33
+ - Create: `docs/.vitepress/theme/components/HomeFeatureTour.vue`
34
+ - Modify: `docs/.vitepress/theme/index.ts` (import + register)
35
+ - Modify: `docs/index.md` (insert `<HomeFeatureTour />` after `<HomeWalkthrough />`)
36
+
37
+ **Acceptance Criteria:**
38
+ - [ ] Desktop (>768px): vertical rail on the left listing 4 features with one-line hooks; clicking swaps the right panel; active item has accent left border
39
+ - [ ] Mobile (≤768px): same DOM reflows to an accordion — all 4 heads visible, one open at a time
40
+ - [ ] Each panel: file caption bar → syntax-highlighted DSL snippet → guide screenshot (zoomable) → policy callout + "Read the guide →" link
41
+ - [ ] SSR/no-JS renders the first feature (Kanban) expanded
42
+ - [ ] Heads are `<button>`s with `aria-expanded`/`aria-controls`
43
+ - [ ] `yarn docs:build` passes
44
+
45
+ **Verify:** `yarn docs:build` → exits 0, no dead links. Then visual check in `yarn docs:dev`.
46
+
47
+ **Steps:**
48
+
49
+ - [ ] **Step 1: Write the component**
50
+
51
+ Create `docs/.vitepress/theme/components/HomeFeatureTour.vue`:
52
+
53
+ ```vue
54
+ <template>
55
+ <section class="pu-section">
56
+ <div class="pu-section-inner">
57
+ <h2 class="pu-section-title">More than CRUD.</h2>
58
+ <p class="ft-sub">
59
+ Four features other frameworks make you build yourself.
60
+ All declarative. All policy-aware.
61
+ </p>
62
+
63
+ <div class="ft-grid">
64
+ <template v-for="f in features" :key="f.id">
65
+ <button
66
+ class="ft-head"
67
+ :class="{ 'ft-head--on': selected === f.id }"
68
+ :aria-expanded="selected === f.id"
69
+ :aria-controls="`ft-panel-${f.id}`"
70
+ @click="selected = f.id"
71
+ >
72
+ <span class="ft-head-text">
73
+ <b>{{ f.name }}</b>
74
+ <small>{{ f.hook }}</small>
75
+ </span>
76
+ <IconChevronDown class="ft-chev" :size="16" :stroke-width="2" />
77
+ </button>
78
+
79
+ <div v-if="selected === f.id" :id="`ft-panel-${f.id}`" class="ft-body">
80
+ <div class="ft-caption">{{ f.file }}</div>
81
+ <pre class="ft-code" v-html="f.code"></pre>
82
+ <img :src="withBase(f.shot)" :alt="f.alt" class="ft-shot pu-zoomable" />
83
+ <div class="ft-foot">
84
+ <span class="ft-policy">
85
+ <IconShieldCheck :size="14" :stroke-width="2" /> {{ f.policy }}
86
+ </span>
87
+ <a class="ft-link" :href="f.link">Read the guide →</a>
88
+ </div>
89
+ </div>
90
+ </template>
91
+ </div>
92
+ </div>
93
+ </section>
94
+ </template>
95
+
96
+ <script setup>
97
+ import { ref } from "vue"
98
+ import { withBase } from "vitepress"
99
+ import { IconChevronDown, IconShieldCheck } from "@tabler/icons-vue"
100
+
101
+ const features = [
102
+ {
103
+ id: "kanban",
104
+ name: "Kanban boards",
105
+ hook: "Drag-drop boards from one block",
106
+ file: "app/definitions/task_definition.rb",
107
+ code: `<span class="m">kanban</span> <span class="k">do</span>
108
+ <span class="m">column</span> <span class="s">:todo</span>, scope: -&gt; { where(status: <span class="s">"todo"</span>) }, role: <span class="s">:backlog</span>
109
+ <span class="m">column</span> <span class="s">:doing</span>, on_enter: -&gt;(r) { r.update!(status: <span class="s">"doing"</span>) }, wip: <span class="n">3</span>
110
+ <span class="m">column</span> <span class="s">:done</span>, on_enter: <span class="s">:mark_done!</span>, role: <span class="s">:done</span>
111
+ <span class="k">end</span>`,
112
+ shot: "/images/guides/kanban-board.png",
113
+ alt: "Kanban board with drag-and-drop columns, WIP limits, and quick-add",
114
+ policy: "Columns lock and drags are rejected server-side when kanban_move? says no.",
115
+ link: "/plutonium-core/guides/kanban",
116
+ },
117
+ {
118
+ id: "wizards",
119
+ name: "Wizards",
120
+ hook: "Multi-step flows with branching & resume",
121
+ file: "app/wizards/company_onboarding_wizard.rb",
122
+ code: `<span class="k">class</span> CompanyOnboardingWizard <span class="k">&lt;</span> Plutonium::Wizard::Base
123
+ <span class="m">step</span> <span class="s">:company</span>, label: <span class="s">"Company details"</span> <span class="k">do</span>
124
+ <span class="m">attribute</span> <span class="s">:name</span>, <span class="s">:string</span>
125
+ <span class="m">input</span> <span class="s">:name</span>
126
+ <span class="m">validates</span> <span class="s">:name</span>, presence: <span class="k">true</span>
127
+ <span class="k">end</span>
128
+
129
+ <span class="m">step</span> <span class="s">:plan</span>, label: <span class="s">"Plan"</span> <span class="k">do</span>
130
+ <span class="m">attribute</span> <span class="s">:plan</span>, <span class="s">:string</span>
131
+ <span class="m">input</span> <span class="s">:plan</span>, as: <span class="s">:radio_buttons</span>, choices: <span class="s">%w[free pro]</span>
132
+ <span class="k">end</span>
133
+
134
+ <span class="m">review</span> label: <span class="s">"Review &amp; submit"</span>
135
+
136
+ <span class="k">def</span> <span class="f">execute</span>
137
+ company = Company.create!(name: data.company.name, plan: data.plan.plan)
138
+ succeed(company).with_message(<span class="s">"You're all set!"</span>)
139
+ <span class="k">end</span>
140
+ <span class="k">end</span>`,
141
+ shot: "/images/guides/wizards-step.png",
142
+ alt: "Wizard step form with progress indicator",
143
+ policy: "Steps validate per-screen; the built-in review step gates the finish.",
144
+ link: "/plutonium-core/guides/wizards",
145
+ },
146
+ {
147
+ id: "actions",
148
+ name: "Actions & interactions",
149
+ hook: "Business logic with auto-generated UI",
150
+ file: "app/definitions/post_definition.rb",
151
+ code: `<span class="k">class</span> PostDefinition <span class="k">&lt;</span> ResourceDefinition
152
+ <span class="m">action</span> <span class="s">:publish</span>, interaction: PublishPostInteraction
153
+ <span class="k">end</span>
154
+
155
+ <span class="c"># app/policies/post_policy.rb</span>
156
+ <span class="k">class</span> PostPolicy <span class="k">&lt;</span> ResourcePolicy
157
+ <span class="k">def</span> <span class="f">publish?</span> = update? &amp;&amp; record.draft?
158
+ <span class="k">end</span>`,
159
+ shot: "/images/guides/custom-actions-bulk.png",
160
+ alt: "Bulk action running against selected table rows",
161
+ policy: "No publish? policy method, no button — it disappears, it doesn't disable.",
162
+ link: "/plutonium-core/guides/custom-actions",
163
+ },
164
+ {
165
+ id: "tenancy",
166
+ name: "Multi-tenancy & nesting",
167
+ hook: "Scoping, invites, nested resources",
168
+ file: "packages/customer_portal/lib/engine.rb",
169
+ code: `<span class="k">class</span> CustomerPortal::Engine <span class="k">&lt;</span> Rails::Engine
170
+ <span class="k">include</span> Plutonium::Portal::Engine
171
+
172
+ config.after_initialize <span class="k">do</span>
173
+ <span class="m">scope_to_entity</span> Organization, strategy: <span class="s">:path</span>
174
+ <span class="k">end</span>
175
+ <span class="k">end</span>
176
+
177
+ <span class="c"># → /customer/42/posts — every query scoped to org 42</span>`,
178
+ shot: "/images/guides/multi-tenancy-dashboard.png",
179
+ alt: "Tenant-scoped portal dashboard",
180
+ policy: "Every query flows through the entity scope — no default_scope hacks.",
181
+ link: "/plutonium-core/guides/multi-tenancy",
182
+ },
183
+ ]
184
+
185
+ const selected = ref(features[0].id)
186
+ </script>
187
+
188
+ <style scoped>
189
+ .ft-sub { color: var(--pu-text-muted); font-size: 15px; margin: -16px 0 28px; }
190
+
191
+ .ft-grid {
192
+ display: grid;
193
+ grid-template-columns: 260px 1fr;
194
+ grid-template-rows: repeat(3, auto) 1fr;
195
+ column-gap: 28px;
196
+ align-items: start;
197
+ }
198
+ .ft-head {
199
+ grid-column: 1;
200
+ display: flex; align-items: center; justify-content: space-between; gap: 8px;
201
+ width: 100%; text-align: left; cursor: pointer;
202
+ background: transparent; font-family: inherit;
203
+ border: 0; border-left: 3px solid transparent;
204
+ border-bottom: 1px solid var(--pu-border-soft);
205
+ padding: 14px 16px;
206
+ transition: border-color 0.15s ease, background 0.15s ease;
207
+ }
208
+ .ft-head:hover { background: var(--pu-bg-band); }
209
+ .ft-head--on { border-left-color: var(--pu-accent); background: var(--pu-bg-band); }
210
+ .ft-head-text { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
211
+ .ft-head b { font-size: 14.5px; font-weight: 600; color: var(--pu-text); }
212
+ .ft-head--on b { color: var(--pu-accent); }
213
+ .ft-head small { font-size: 12px; color: var(--pu-text-faint); }
214
+ .ft-chev { color: var(--pu-text-faint); flex-shrink: 0; display: none; }
215
+
216
+ .ft-body { grid-column: 2; grid-row: 1 / -1; min-width: 0; }
217
+ .ft-caption {
218
+ font-size: 11px; letter-spacing: 0.05em;
219
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
220
+ color: #8b949e; background: #161b22;
221
+ border: 1px solid #30363d; border-bottom: 0;
222
+ border-radius: 8px 8px 0 0; padding: 8px 14px;
223
+ }
224
+ .ft-code {
225
+ background: var(--pu-bg-dark); color: var(--pu-term-text);
226
+ border-radius: 0 0 8px 8px; margin: 0; padding: 14px 16px;
227
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
228
+ font-size: 12.5px; line-height: 1.6; overflow-x: auto; white-space: pre;
229
+ }
230
+ .ft-code :deep(.k) { color: #ff7b72; }
231
+ .ft-code :deep(.s) { color: #a5d6ff; }
232
+ .ft-code :deep(.m) { color: #d2a8ff; }
233
+ .ft-code :deep(.f) { color: #d2a8ff; }
234
+ .ft-code :deep(.n) { color: #79c0ff; }
235
+ .ft-code :deep(.c) { color: #8b949e; }
236
+
237
+ .ft-shot {
238
+ display: block; width: 100%; height: auto; margin-top: 14px;
239
+ border: 1px solid var(--pu-border-soft); border-radius: 8px;
240
+ }
241
+ .ft-foot {
242
+ display: flex; align-items: center; justify-content: space-between;
243
+ gap: 12px; flex-wrap: wrap; margin-top: 12px;
244
+ }
245
+ .ft-policy {
246
+ display: inline-flex; align-items: center; gap: 6px;
247
+ font-size: 12.5px; color: var(--pu-text-muted);
248
+ }
249
+ .ft-policy svg { color: var(--pu-success-fg); flex-shrink: 0; }
250
+ .ft-link {
251
+ font-size: 13px; font-weight: 500; color: var(--pu-accent);
252
+ text-decoration: none; white-space: nowrap;
253
+ }
254
+ .ft-link:hover { text-decoration: underline; }
255
+
256
+ @media (max-width: 768px) {
257
+ .ft-grid { display: block; }
258
+ .ft-head { border: 1px solid var(--pu-border-soft); border-left-width: 3px; border-radius: 6px; margin-bottom: 8px; }
259
+ .ft-chev { display: block; transition: transform 0.15s ease; }
260
+ .ft-head--on .ft-chev { transform: rotate(180deg); }
261
+ .ft-body { margin: 4px 0 16px; }
262
+ }
263
+ </style>
264
+ ```
265
+
266
+ - [ ] **Step 2: Register the component**
267
+
268
+ In `docs/.vitepress/theme/index.ts`, add the import after the `HomeWalkthrough` import and the registration after the `HomeWalkthrough` registration:
269
+
270
+ ```ts
271
+ import HomeFeatureTour from "./components/HomeFeatureTour.vue"
272
+ ```
273
+
274
+ ```ts
275
+ app.component("HomeFeatureTour", HomeFeatureTour)
276
+ ```
277
+
278
+ - [ ] **Step 3: Mount on the homepage**
279
+
280
+ In `docs/index.md`, insert after `<HomeWalkthrough />`:
281
+
282
+ ```md
283
+ <HomeFeatureTour />
284
+ ```
285
+
286
+ (Section order after this task: Hero, StopWriting, Pillars, Walkthrough, **FeatureTour**, AudienceSplit, InTheBox, Cta. Task 2 finishes the reordering.)
287
+
288
+ - [ ] **Step 4: Build**
289
+
290
+ Run: `yarn docs:build`
291
+ Expected: build succeeds, no dead-link errors.
292
+
293
+ - [ ] **Step 5: Visual smoke check**
294
+
295
+ Run `yarn docs:dev`, open `http://localhost:5173/plutonium-core/`. Check: rail renders with Kanban expanded; clicking Wizards/Actions/Tenancy swaps the panel; at ≤768px width the section becomes an accordion; all four screenshots load; guide links navigate.
296
+
297
+ - [ ] **Step 6: Commit**
298
+
299
+ ```bash
300
+ git add docs/.vitepress/theme/components/HomeFeatureTour.vue docs/.vitepress/theme/index.ts docs/index.md
301
+ git commit -m "feat(docs): add homepage feature tour section"
302
+ ```
303
+
304
+ ---
305
+
306
+ ### Task 2: Create `HomeWhyPlutonium.vue`, delete `HomePillars` + `HomeAudienceSplit`
307
+
308
+ **Goal:** One compact band replaces two sections: four pillar cards on top, two-audience footer row below.
309
+
310
+ **Files:**
311
+ - Create: `docs/.vitepress/theme/components/HomeWhyPlutonium.vue`
312
+ - Delete: `docs/.vitepress/theme/components/HomePillars.vue`
313
+ - Delete: `docs/.vitepress/theme/components/HomeAudienceSplit.vue`
314
+ - Modify: `docs/.vitepress/theme/index.ts`
315
+ - Modify: `docs/index.md`
316
+
317
+ **Acceptance Criteria:**
318
+ - [ ] Single `pu-section--band` section titled "Built on Rails. Wired for shipping."
319
+ - [ ] Four compact pillar cards (Convention / It's just Rails / Multi-tenant ready / AI-readable) with the same icons and copy as the old `HomePillars`
320
+ - [ ] Footer row: "For Rails developers" and "For founders & teams", each with its existing lede + exactly 2 bullets (per spec)
321
+ - [ ] `HomePillars.vue` and `HomeAudienceSplit.vue` deleted; no references remain (`grep` clean)
322
+ - [ ] Homepage section order is: Hero, StopWriting, Walkthrough, FeatureTour, WhyPlutonium, InTheBox, Cta
323
+ - [ ] `yarn docs:build` passes
324
+
325
+ **Verify:** `yarn docs:build` → exits 0. `grep -rn "HomePillars\|HomeAudienceSplit" docs --include="*.ts" --include="*.md" --include="*.vue" -l` (excluding `docs/.vitepress/dist` and `docs/superpowers`) → no matches.
326
+
327
+ **Steps:**
328
+
329
+ - [ ] **Step 1: Write the component**
330
+
331
+ Create `docs/.vitepress/theme/components/HomeWhyPlutonium.vue`:
332
+
333
+ ```vue
334
+ <template>
335
+ <section class="pu-section pu-section--band">
336
+ <div class="pu-section-inner">
337
+ <h2 class="pu-section-title">Built on Rails. Wired for shipping.</h2>
338
+
339
+ <div class="wp-grid">
340
+ <div class="wp-card" v-for="p in pillars" :key="p.name">
341
+ <component :is="p.icon" class="wp-icon" :size="20" :stroke-width="1.75" />
342
+ <div class="wp-name">{{ p.name }}</div>
343
+ <div class="wp-desc">{{ p.desc }}</div>
344
+ </div>
345
+ </div>
346
+
347
+ <div class="wp-audiences">
348
+ <div class="wp-aud">
349
+ <div class="wp-aud-head">For Rails developers</div>
350
+ <p class="wp-aud-lede">The missing layer between Rails and the apps you keep building.</p>
351
+ <ul class="wp-aud-list">
352
+ <li><IconArrowRight class="wp-arr" :size="16" :stroke-width="2.25" /><span>Convention extended to CRUD, policies, and portals</span></li>
353
+ <li><IconArrowRight class="wp-arr" :size="16" :stroke-width="2.25" /><span>Generated code lives in your repo — edit anything</span></li>
354
+ </ul>
355
+ </div>
356
+ <div class="wp-aud wp-aud--right">
357
+ <div class="wp-aud-head">For founders &amp; teams</div>
358
+ <p class="wp-aud-lede">Skip the SaaS template debate. Plutonium turns Rails into a SaaS toolkit.</p>
359
+ <ul class="wp-aud-list">
360
+ <li><IconArrowRight class="wp-arr" :size="16" :stroke-width="2.25" /><span>Admin panel, signup, and invites on day one</span></li>
361
+ <li><IconArrowRight class="wp-arr" :size="16" :stroke-width="2.25" /><span>Multi-tenant scoping when you need it</span></li>
362
+ </ul>
363
+ </div>
364
+ </div>
365
+ </div>
366
+ </section>
367
+ </template>
368
+
369
+ <script setup>
370
+ import { IconRoute, IconCode, IconBuildingSkyscraper, IconRobot, IconArrowRight } from "@tabler/icons-vue"
371
+
372
+ const pillars = [
373
+ { icon: IconRoute, name: "Convention over configuration",
374
+ desc: "Extended to resources, policies, portals, and tenancy — not just routes and views." },
375
+ { icon: IconCode, name: "It's just Rails",
376
+ desc: 'Generated code lives in your repo. Edit it, override it, delete it. The “magic” is regular Ruby mixins you can read.' },
377
+ { icon: IconBuildingSkyscraper, name: "Multi-tenant ready",
378
+ desc: "Path or domain tenancy. Scoped relations. Invites and memberships out of the box." },
379
+ { icon: IconRobot, name: "AI-readable",
380
+ desc: "Predictable file layout and naming. Built-in skills teach AI assistants the patterns." },
381
+ ]
382
+ </script>
383
+
384
+ <style scoped>
385
+ .wp-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
386
+ .wp-card {
387
+ padding: 14px 16px; border: 1px solid var(--pu-border-soft); border-radius: 8px;
388
+ background: var(--pu-bg-light);
389
+ }
390
+ .wp-icon { color: var(--pu-accent); margin-bottom: 8px; display: block; }
391
+ .wp-name { font-weight: 600; font-size: 14px; color: var(--pu-text); margin-bottom: 4px; line-height: 1.25; }
392
+ .wp-desc { font-size: 12.5px; color: var(--pu-text-muted); line-height: 1.5; }
393
+
394
+ .wp-audiences {
395
+ display: grid; grid-template-columns: 1fr 1fr; gap: 28px;
396
+ margin-top: 28px; padding-top: 24px; border-top: 1px solid var(--pu-border);
397
+ }
398
+ .wp-aud--right { border-left: 1px solid var(--pu-border); padding-left: 28px; }
399
+ .wp-aud-head {
400
+ font-size: 11px; text-transform: uppercase; letter-spacing: 0.1em;
401
+ color: var(--pu-accent); font-weight: 600; margin-bottom: 6px;
402
+ }
403
+ .wp-aud-lede {
404
+ font-size: 16px; line-height: 1.35; font-weight: 500; color: var(--pu-text);
405
+ margin: 0 0 10px; letter-spacing: -0.01em;
406
+ }
407
+ .wp-aud-list { list-style: none; padding: 0; margin: 0; font-size: 14px; line-height: 1.6; color: var(--pu-text-muted); }
408
+ .wp-aud-list li { display: flex; gap: 10px; align-items: flex-start; padding: 4px 0; }
409
+ .wp-arr { color: var(--pu-accent); flex-shrink: 0; margin-top: 3px; }
410
+
411
+ @media (max-width: 900px) { .wp-grid { grid-template-columns: repeat(2, 1fr); } }
412
+ @media (max-width: 768px) {
413
+ .wp-audiences { grid-template-columns: 1fr; }
414
+ .wp-aud--right { border-left: none; padding-left: 0; border-top: 1px solid var(--pu-border); padding-top: 20px; }
415
+ }
416
+ @media (max-width: 480px) { .wp-grid { grid-template-columns: 1fr; } }
417
+ </style>
418
+ ```
419
+
420
+ - [ ] **Step 2: Swap registrations**
421
+
422
+ In `docs/.vitepress/theme/index.ts`:
423
+ - Remove the `HomePillars` and `HomeAudienceSplit` import lines and their `app.component(...)` lines.
424
+ - Add:
425
+
426
+ ```ts
427
+ import HomeWhyPlutonium from "./components/HomeWhyPlutonium.vue"
428
+ ```
429
+
430
+ ```ts
431
+ app.component("HomeWhyPlutonium", HomeWhyPlutonium)
432
+ ```
433
+
434
+ - [ ] **Step 3: Update the homepage and delete old components**
435
+
436
+ `docs/index.md` component stack becomes exactly:
437
+
438
+ ```md
439
+ <HomeHero />
440
+
441
+ <HomeStopWriting />
442
+
443
+ <HomeWalkthrough />
444
+
445
+ <HomeFeatureTour />
446
+
447
+ <HomeWhyPlutonium />
448
+
449
+ <HomeInTheBox />
450
+
451
+ <HomeCta />
452
+ ```
453
+
454
+ Then:
455
+
456
+ ```bash
457
+ git rm docs/.vitepress/theme/components/HomePillars.vue docs/.vitepress/theme/components/HomeAudienceSplit.vue
458
+ ```
459
+
460
+ - [ ] **Step 4: Build + reference check**
461
+
462
+ Run: `yarn docs:build`
463
+ Expected: exits 0.
464
+
465
+ Run: `grep -rn "HomePillars\|HomeAudienceSplit" docs/.vitepress/theme docs/index.md`
466
+ Expected: no output.
467
+
468
+ - [ ] **Step 5: Commit**
469
+
470
+ ```bash
471
+ git add docs/.vitepress/theme/components/HomeWhyPlutonium.vue docs/.vitepress/theme/index.ts docs/index.md
472
+ git commit -m "feat(docs): merge pillars and audience split into Why Plutonium band"
473
+ ```
474
+
475
+ ---
476
+
477
+ ### Task 3: Copy weaves — hero, stop-writing, in-the-box
478
+
479
+ **Goal:** Make the four tour features discoverable in the hero, the scaffold comparison, and the categorized links.
480
+
481
+ **Files:**
482
+ - Modify: `docs/.vitepress/theme/components/HomeHero.vue` (pillars line)
483
+ - Modify: `docs/.vitepress/theme/components/HomeStopWriting.vue` (win stats)
484
+ - Modify: `docs/.vitepress/theme/components/HomeInTheBox.vue` (Workflows category)
485
+
486
+ **Acceptance Criteria:**
487
+ - [ ] Hero pillars line includes **Wizards.** and **Kanban.**
488
+ - [ ] Stop-writing Plutonium column shows `+ Actions` after `+ Bulk actions`
489
+ - [ ] In-the-box has a "Workflows" category (second position) with Wizards / Kanban boards / Interactions linking to their guides
490
+ - [ ] `yarn docs:build` passes
491
+
492
+ **Verify:** `yarn docs:build` → exits 0; visual check of the three sections.
493
+
494
+ **Steps:**
495
+
496
+ - [ ] **Step 1: Hero pillars line**
497
+
498
+ In `docs/.vitepress/theme/components/HomeHero.vue`, replace:
499
+
500
+ ```html
501
+ <p class="home-hero-pillars">
502
+ <b>CRUD.</b> <b>Auth.</b> <b>Authorization.</b> <b>Multi-tenancy.</b>
503
+ <b>Admin portals.</b> <b>Search, filters, bulk actions.</b>
504
+ All generated. All customizable. All Rails.
505
+ </p>
506
+ ```
507
+
508
+ with:
509
+
510
+ ```html
511
+ <p class="home-hero-pillars">
512
+ <b>CRUD.</b> <b>Auth.</b> <b>Authorization.</b> <b>Multi-tenancy.</b>
513
+ <b>Wizards.</b> <b>Kanban.</b> <b>Admin portals.</b>
514
+ <b>Search, filters, bulk actions.</b>
515
+ All generated. All customizable. All Rails.
516
+ </p>
517
+ ```
518
+
519
+ - [ ] **Step 2: Stop-writing stats**
520
+
521
+ In `docs/.vitepress/theme/components/HomeStopWriting.vue`, replace:
522
+
523
+ ```html
524
+ <div class="hsw-stats hsw-stats--win">
525
+ <span><b>Full CRUD</b></span>
526
+ <span><b>+ Search</b></span>
527
+ <span><b>+ Filters</b></span>
528
+ <span><b>+ Bulk actions</b></span>
529
+ </div>
530
+ ```
531
+
532
+ with:
533
+
534
+ ```html
535
+ <div class="hsw-stats hsw-stats--win">
536
+ <span><b>Full CRUD</b></span>
537
+ <span><b>+ Search</b></span>
538
+ <span><b>+ Filters</b></span>
539
+ <span><b>+ Bulk actions</b></span>
540
+ <span><b>+ Actions</b></span>
541
+ </div>
542
+ ```
543
+
544
+ - [ ] **Step 3: In-the-box Workflows category**
545
+
546
+ In `docs/.vitepress/theme/components/HomeInTheBox.vue`, insert into the `cats` array **between** the "Resources" and "App structure" entries:
547
+
548
+ ```js
549
+ { name: "Workflows", items: [
550
+ { name: "Wizards", desc: "Multi-step flows with branching & resume",
551
+ link: "/plutonium-core/guides/wizards" },
552
+ { name: "Kanban boards", desc: "Drag-drop boards from one block",
553
+ link: "/plutonium-core/guides/kanban" },
554
+ { name: "Interactions", desc: "Business logic with auto-generated UI",
555
+ link: "/plutonium-core/guides/custom-actions" },
556
+ ]},
557
+ ```
558
+
559
+ - [ ] **Step 4: Build**
560
+
561
+ Run: `yarn docs:build`
562
+ Expected: exits 0, no dead links.
563
+
564
+ - [ ] **Step 5: Commit**
565
+
566
+ ```bash
567
+ git add docs/.vitepress/theme/components/HomeHero.vue docs/.vitepress/theme/components/HomeStopWriting.vue docs/.vitepress/theme/components/HomeInTheBox.vue
568
+ git commit -m "feat(docs): weave wizards, kanban, and actions into homepage copy"
569
+ ```
570
+
571
+ ---
572
+
573
+ ### Task 4: Full visual pass + user sign-off
574
+
575
+ **Goal:** Verify the finished homepage at desktop and mobile widths and get the user's approval.
576
+
577
+ **Files:** none (verification only)
578
+
579
+ **Acceptance Criteria:**
580
+ - [ ] `yarn docs:build` passes on the final state
581
+ - [ ] Desktop pass: section order Hero → StopWriting → Walkthrough → FeatureTour → WhyPlutonium → InTheBox → CTA; tour rail interaction works; dark mode looks right (toggle the theme)
582
+ - [ ] Mobile pass (≤768px): tour is an accordion, all sections reflow, no horizontal scroll
583
+ - [ ] All four tour screenshots load and zoom; all guide links resolve
584
+ - [ ] User confirms the page sells the depth
585
+
586
+ **Verify:** `yarn docs:build` → exits 0; then manual pass in `yarn docs:dev`.
587
+
588
+ **Steps:**
589
+
590
+ - [ ] **Step 1: Build final state**
591
+
592
+ Run: `yarn docs:build`
593
+ Expected: exits 0.
594
+
595
+ - [ ] **Step 2: Manual pass**
596
+
597
+ Run `yarn docs:dev`; check every acceptance criterion above at 1280px and 375px widths, in light and dark mode.
598
+
599
+ - [ ] **Step 3: User verification**
600
+
601
+ **User Verification Required:**
602
+ Before marking this task complete, you MUST call AskUserQuestion:
603
+
604
+ ```yaml
605
+ AskUserQuestion:
606
+ question: "The homepage upgrade is done — feature tour, merged Why Plutonium band, copy weaves. Does it sell the depth the way you wanted?"
607
+ header: "Verification"
608
+ options:
609
+ - label: "Approved"
610
+ description: "Page sells the product — work is complete"
611
+ - label: "Needs changes"
612
+ description: "Something's off — describe what, and it gets reworked"
613
+ ```
614
+
615
+ **If the user selects "Needs changes":** the task is NOT complete. Rework, then re-verify with AskUserQuestion again.
616
+
617
+ ---
618
+
619
+ ## Self-Review
620
+
621
+ - **Spec coverage:** Tour section (Task 1), merged band + deletions (Task 2), three copy weaves (Task 3), visual/mobile/build verification (Tasks 1–4). Out-of-scope items untouched. ✓
622
+ - **Placeholders:** none — every code step contains full code. ✓
623
+ - **Type consistency:** component names `HomeFeatureTour`/`HomeWhyPlutonium` consistent across create/register/mount steps; CSS class prefixes `ft-`/`wp-` self-contained. ✓
624
+ - **Verification requirement:** YES → Task 4 carries `requiresUserVerification: true` with the standard block. ✓
@@ -0,0 +1,32 @@
1
+ {
2
+ "planPath": "docs/superpowers/plans/2026-07-16-homepage-depth-upgrade.md",
3
+ "tasks": [
4
+ {
5
+ "id": 6,
6
+ "subject": "Task 1: Create HomeFeatureTour.vue and mount it",
7
+ "status": "completed",
8
+ "description": "**Goal:** New interactive tour section — desktop feature rail / mobile accordion — showing DSL code + real screenshot for Kanban, Wizards, Actions, Multi-tenancy.\n\n**Files:** Create docs/.vitepress/theme/components/HomeFeatureTour.vue; Modify docs/.vitepress/theme/index.ts, docs/index.md\n\n**Verify:** yarn docs:build\n\nFull code in plan Task 1.\n\n```json:metadata\n{\"files\": [\"docs/.vitepress/theme/components/HomeFeatureTour.vue\", \"docs/.vitepress/theme/index.ts\", \"docs/index.md\"], \"verifyCommand\": \"yarn docs:build\", \"acceptanceCriteria\": [\"desktop rail swaps panels\", \"mobile accordion\", \"panel shows code+shot+policy+link\", \"build passes\"], \"requiresUserVerification\": false}\n```"
9
+ },
10
+ {
11
+ "id": 7,
12
+ "subject": "Task 2: Create HomeWhyPlutonium.vue, delete HomePillars + HomeAudienceSplit",
13
+ "status": "completed",
14
+ "blockedBy": [6],
15
+ "description": "**Goal:** One compact band replaces two sections: four pillar cards + two-audience footer row.\n\n**Files:** Create docs/.vitepress/theme/components/HomeWhyPlutonium.vue; Delete HomePillars.vue, HomeAudienceSplit.vue; Modify index.ts, docs/index.md\n\n**Verify:** yarn docs:build && ! grep -rn 'HomePillars\\|HomeAudienceSplit' docs/.vitepress/theme docs/index.md\n\nFull code in plan Task 2.\n\n```json:metadata\n{\"files\": [\"docs/.vitepress/theme/components/HomeWhyPlutonium.vue\", \"docs/.vitepress/theme/index.ts\", \"docs/index.md\"], \"verifyCommand\": \"yarn docs:build\", \"acceptanceCriteria\": [\"merged band renders\", \"old components deleted\", \"section order correct\", \"build passes\"], \"requiresUserVerification\": false}\n```"
16
+ },
17
+ {
18
+ "id": 8,
19
+ "subject": "Task 3: Copy weaves — hero, stop-writing, in-the-box",
20
+ "status": "completed",
21
+ "description": "**Goal:** Weave Wizards/Kanban/Actions into HomeHero pillars line, HomeStopWriting stats, and a new Workflows category in HomeInTheBox.\n\n**Verify:** yarn docs:build\n\nExact before/after snippets in plan Task 3.\n\n```json:metadata\n{\"files\": [\"docs/.vitepress/theme/components/HomeHero.vue\", \"docs/.vitepress/theme/components/HomeStopWriting.vue\", \"docs/.vitepress/theme/components/HomeInTheBox.vue\"], \"verifyCommand\": \"yarn docs:build\", \"acceptanceCriteria\": [\"hero mentions wizards+kanban\", \"+ Actions stat\", \"Workflows category added\", \"build passes\"], \"requiresUserVerification\": false}\n```"
22
+ },
23
+ {
24
+ "id": 9,
25
+ "subject": "Task 4: Full visual pass + user sign-off",
26
+ "status": "completed",
27
+ "blockedBy": [6, 7, 8],
28
+ "description": "**Goal:** Final desktop+mobile, light+dark visual pass; user approves via AskUserQuestion (see plan Task 4 for the exact question block).\n\n```json:metadata\n{\"files\": [], \"verifyCommand\": \"yarn docs:build\", \"acceptanceCriteria\": [\"build passes\", \"desktop+mobile visual pass\", \"user approves\"], \"requiresUserVerification\": true, \"userVerificationPrompt\": \"Does the upgraded homepage sell the depth the way you wanted?\"}\n```"
29
+ }
30
+ ],
31
+ "lastUpdated": "2026-07-16"
32
+ }