create-pmos 0.133.0 → 0.135.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 (212) hide show
  1. package/package.json +1 -1
  2. package/payload/KIT-VERSION +1 -1
  3. package/payload/planning/evals/eval-calibration.md +1 -1
  4. package/payload/planning/okrs/health-budgets.md +1 -1
  5. package/payload/state/discoveries.md +1 -1
  6. package/workspace/bin/notes-replay.js +235 -0
  7. package/workspace/c3-briefing-test.js +130 -0
  8. package/workspace/c3-foundation-test.js +159 -0
  9. package/workspace/c3-ib-items-test.js +105 -0
  10. package/workspace/c3-ib-more-test.js +120 -0
  11. package/workspace/c3-ib-triage-backlog-test.js +119 -0
  12. package/workspace/c3-ini-harness.js +66 -0
  13. package/workspace/c3-initiatives-test.js +140 -0
  14. package/workspace/c3-one-home-test.js +14 -1
  15. package/workspace/c3-reports-test.js +57 -18
  16. package/workspace/c3-settings-test.js +139 -0
  17. package/workspace/c3-work-test.js +42 -10
  18. package/workspace/fixtures/one-home-baseline.json +1 -9
  19. package/workspace/guide/briefing-cards.md +15 -3
  20. package/workspace/guide/briefing.md +12 -8
  21. package/workspace/guide/calendar-popup.md +2 -2
  22. package/workspace/guide/console-calendar.md +39 -10
  23. package/workspace/guide/index.md +1 -0
  24. package/workspace/guide/initiative-boards.md +65 -0
  25. package/workspace/guide/jira.md +3 -1
  26. package/workspace/guide/layout.md +2 -0
  27. package/workspace/guide/my-work.md +5 -3
  28. package/workspace/guide/notes.md +35 -2
  29. package/workspace/guide/reports-dashboards.md +29 -5
  30. package/workspace/guide/settings.md +68 -0
  31. package/workspace/guide/welcome.md +10 -10
  32. package/workspace/lib/calendar-cli.js +3 -2
  33. package/workspace/lib/calendar-layers.js +21 -10
  34. package/workspace/lib/calendar-tidy.js +230 -0
  35. package/workspace/lib/engine/boards.js +542 -0
  36. package/workspace/lib/engine/breakdown-ops.js +180 -0
  37. package/workspace/lib/engine/calendar-import.js +84 -0
  38. package/workspace/lib/engine/calendar-model.js +185 -0
  39. package/workspace/lib/engine/card-state.js +92 -0
  40. package/workspace/lib/engine/derived/derive.js +92 -0
  41. package/workspace/lib/engine/derived/plan.js +3 -0
  42. package/workspace/lib/engine/derived/trust.js +3 -0
  43. package/workspace/lib/engine/doc-health.js +142 -0
  44. package/workspace/lib/engine/doc-sheet.js +75 -0
  45. package/workspace/lib/engine/intake.js +78 -0
  46. package/workspace/lib/engine/outbox.js +86 -0
  47. package/workspace/lib/engine/plan-forecast.js +342 -0
  48. package/workspace/lib/engine/promises.js +29 -0
  49. package/workspace/lib/engine/records/index.js +114 -0
  50. package/workspace/lib/engine/releases.js +96 -0
  51. package/workspace/lib/engine/replies.js +99 -0
  52. package/workspace/lib/engine/reply-contract/reply-contract.js +372 -0
  53. package/workspace/lib/engine/report-families.js +175 -0
  54. package/workspace/lib/engine/roadmap-ops.js +150 -0
  55. package/workspace/lib/engine/sessions-list.js +27 -0
  56. package/workspace/lib/engine/signal-rules.js +95 -0
  57. package/workspace/lib/engine/signals/cards.js +57 -0
  58. package/workspace/lib/engine/signals/detectors.js +370 -0
  59. package/workspace/lib/engine/signals/facts-cli.js +8 -0
  60. package/workspace/lib/engine/signals/facts.js +197 -0
  61. package/workspace/lib/engine/signals/index.js +33 -0
  62. package/workspace/lib/engine/signals/kinds.js +110 -0
  63. package/workspace/lib/engine/signals/layout-paths.js +44 -0
  64. package/workspace/lib/engine/signals/options.js +24 -0
  65. package/workspace/lib/engine/signals/score.js +72 -0
  66. package/workspace/lib/engine/signals/select.js +47 -0
  67. package/workspace/lib/engine/step-planner.js +65 -0
  68. package/workspace/lib/engine/trust/claim-rules.json +93 -0
  69. package/workspace/lib/engine/trust/import-calendar.js +21 -0
  70. package/workspace/lib/engine/trust/import-guard.js +36 -0
  71. package/workspace/lib/engine/trust/ledger.js +53 -0
  72. package/workspace/lib/engine/trust/outbox.js +45 -0
  73. package/workspace/lib/engine/trust/plan.js +5 -0
  74. package/workspace/lib/engine/trust/trust.js +21 -0
  75. package/workspace/lib/engine/truth-table.js +57 -0
  76. package/workspace/lib/engine/workspace-lib.js +24 -0
  77. package/workspace/lib/factory-join.js +1 -1
  78. package/workspace/lib/facts-async.js +4 -4
  79. package/workspace/lib/headless.js +95 -0
  80. package/workspace/lib/import-overlay.js +2 -1
  81. package/workspace/lib/jira-status.js +30 -3
  82. package/workspace/lib/notes-audio.js +28 -9
  83. package/workspace/lib/notes-capture.js +136 -0
  84. package/workspace/lib/notes-final.js +38 -13
  85. package/workspace/lib/notes-live-guard.js +117 -0
  86. package/workspace/lib/notes-replay.js +125 -0
  87. package/workspace/lib/notes-screen.js +180 -0
  88. package/workspace/lib/notes-whisper.js +139 -16
  89. package/workspace/lib/notes.js +4 -2
  90. package/workspace/lib/outbox-store.js +10 -6
  91. package/workspace/lib/pmos-verbs.js +3 -1
  92. package/workspace/lib/proactive-cli.js +7 -0
  93. package/workspace/lib/proactive.js +15 -2
  94. package/workspace/lib/repo-script.js +36 -0
  95. package/workspace/lib/report-dashboards.js +5 -2
  96. package/workspace/lib/report-frame.js +122 -0
  97. package/workspace/lib/report-schedules.js +113 -0
  98. package/workspace/lib/route-modules.js +9 -2
  99. package/workspace/lib/routes-boards.js +734 -0
  100. package/workspace/lib/routes-brief-page.js +13 -4
  101. package/workspace/lib/routes-briefing.js +22 -11
  102. package/workspace/lib/routes-c3-init.js +4 -3
  103. package/workspace/lib/routes-c3-life.js +2 -1
  104. package/workspace/lib/routes-c3-reports.js +96 -13
  105. package/workspace/lib/routes-c3-strategy.js +4 -3
  106. package/workspace/lib/routes-c3-timeline.js +3 -2
  107. package/workspace/lib/routes-calendar-import.js +3 -2
  108. package/workspace/lib/routes-calendar-views.js +9 -4
  109. package/workspace/lib/routes-doc-health.js +2 -1
  110. package/workspace/lib/routes-doc-sheet.js +2 -1
  111. package/workspace/lib/routes-forecast.js +2 -1
  112. package/workspace/lib/routes-hooks.js +3 -2
  113. package/workspace/lib/routes-initiatives.js +2 -1
  114. package/workspace/lib/routes-intake.js +2 -1
  115. package/workspace/lib/routes-notes.js +113 -15
  116. package/workspace/lib/routes-outbox.js +23 -3
  117. package/workspace/lib/routes-pm-items.js +19 -6
  118. package/workspace/lib/routes-records.js +3 -2
  119. package/workspace/lib/routes-releases.js +2 -1
  120. package/workspace/lib/routes-report-facts.js +4 -3
  121. package/workspace/lib/routes-report-families.js +2 -1
  122. package/workspace/lib/routes-roadmap-ops.js +5 -4
  123. package/workspace/lib/routes-runs.js +6 -5
  124. package/workspace/lib/routes-settings.js +51 -0
  125. package/workspace/lib/routes-signals.js +3 -2
  126. package/workspace/lib/routes-stage-tools.js +220 -0
  127. package/workspace/lib/run-answers.js +2 -1
  128. package/workspace/lib/settings-store.js +226 -0
  129. package/workspace/lib/stage-tools.js +208 -0
  130. package/workspace/lib/vendor/scripts/signals/layout-paths.js +2 -1
  131. package/workspace/lib/vendor/scripts/workspace-lib.js +24 -0
  132. package/workspace/lib/work-evidence.js +73 -0
  133. package/workspace/package-lock.json +2 -2
  134. package/workspace/package.json +1 -1
  135. package/workspace/public/pm/briefing-deck.js +63 -17
  136. package/workspace/public/pm/briefing-model.js +36 -7
  137. package/workspace/public/pm/c3-ambient.js +100 -0
  138. package/workspace/public/pm/c3-brief-needs.js +26 -6
  139. package/workspace/public/pm/c3-brief-pulse.js +12 -4
  140. package/workspace/public/pm/c3-brief-today.js +56 -21
  141. package/workspace/public/pm/c3-brief.js +2 -1
  142. package/workspace/public/pm/c3-calendar.js +161 -42
  143. package/workspace/public/pm/c3-calpop.js +5 -5
  144. package/workspace/public/pm/c3-demo-boards.js +649 -0
  145. package/workspace/public/pm/c3-demo-brief-page.js +1 -1
  146. package/workspace/public/pm/c3-demo-cal4.js +3 -2
  147. package/workspace/public/pm/c3-demo-cal5.js +52 -0
  148. package/workspace/public/pm/c3-demo-data.js +17 -27
  149. package/workspace/public/pm/c3-demo-rep3.js +46 -5
  150. package/workspace/public/pm/c3-demo.js +2 -1
  151. package/workspace/public/pm/c3-dock.js +1 -1
  152. package/workspace/public/pm/c3-ib-docs.js +80 -0
  153. package/workspace/public/pm/c3-ib-initiatives.js +134 -0
  154. package/workspace/public/pm/c3-ib-items.js +201 -0
  155. package/workspace/public/pm/c3-ib-releases.js +51 -0
  156. package/workspace/public/pm/c3-ib-sprints.js +55 -0
  157. package/workspace/public/pm/c3-ib-triage.js +178 -0
  158. package/workspace/public/pm/c3-ii-tabs.js +240 -0
  159. package/workspace/public/pm/c3-ini-records.js +197 -0
  160. package/workspace/public/pm/c3-ini-timeline.js +278 -0
  161. package/workspace/public/pm/c3-ini-ui.js +229 -0
  162. package/workspace/public/pm/c3-ini-work.js +139 -0
  163. package/workspace/public/pm/c3-initiatives.css +218 -0
  164. package/workspace/public/pm/c3-initiatives.js +741 -0
  165. package/workspace/public/pm/c3-plan.js +2 -2
  166. package/workspace/public/pm/c3-reports.css +47 -47
  167. package/workspace/public/pm/c3-reports.js +223 -185
  168. package/workspace/public/pm/c3-settings.css +74 -0
  169. package/workspace/public/pm/c3-settings.js +703 -0
  170. package/workspace/public/pm/c3-shell.js +102 -36
  171. package/workspace/public/pm/c3-terminal-icons.js +2 -2
  172. package/workspace/public/pm/c3-terminal.js +2 -2
  173. package/workspace/public/pm/calendar-views.js +26 -13
  174. package/workspace/public/pm/components.css +319 -213
  175. package/workspace/public/pm/console3.css +118 -23
  176. package/workspace/public/pm/dest-notes.js +26 -4
  177. package/workspace/public/pm/notes-screen-capture.js +165 -0
  178. package/workspace/public/pm/notes-screen-view.js +98 -0
  179. package/workspace/public/pm/panel-trees.js +5 -2
  180. package/workspace/public/pm/pm-work-view.js +233 -146
  181. package/workspace/public/pm/shell-bell.js +3 -2
  182. package/workspace/public/pm/shell-recording.js +148 -17
  183. package/workspace/public/pm/tokens.css +2 -2
  184. package/workspace/public/pm/view-notes.js +53 -11
  185. package/workspace/public/pm/welcome.js +197 -89
  186. package/workspace/public/welcome.html +6 -4
  187. package/workspace/server.js +105 -6
  188. package/workspace/skills/day-check.skill +1 -1
  189. package/workspace/skills/jira-sync.skill +6 -4
  190. package/workspace/skills/morning-brief.skill +1 -1
  191. package/workspace/test-files.json +14 -3
  192. package/workspace/test-lists/c3-ib-items.json +9 -0
  193. package/workspace/test-lists/c3-ib-more.json +9 -0
  194. package/workspace/test-lists/c3-ib-triage-backlog.json +9 -0
  195. package/workspace/test-lists/c3-initiative-boards.json +6 -0
  196. package/workspace/test-lists/calendar-tidy-up.json +6 -0
  197. package/workspace/test-lists/console3-briefing.json +9 -0
  198. package/workspace/test-lists/console3-foundation.json +9 -0
  199. package/workspace/test-lists/console3-reports.json +3 -3
  200. package/workspace/test-lists/meeting-notes.json +12 -4
  201. package/workspace/test-lists/workspace-headless-jobs.json +5 -0
  202. package/workspace/test-lists/workspace-package-script-closure.json +6 -0
  203. package/workspace/lib/vendor/workspace/lib/actor.js +0 -91
  204. package/workspace/lib/vendor/workspace/lib/board-cache.js +0 -156
  205. package/workspace/lib/vendor/workspace/lib/console-state.js +0 -295
  206. package/workspace/lib/vendor/workspace/lib/factory-join.js +0 -158
  207. package/workspace/lib/vendor/workspace/lib/git-info.js +0 -306
  208. package/workspace/lib/vendor/workspace/lib/plan-items.js +0 -922
  209. package/workspace/lib/vendor/workspace/lib/plan-tree.js +0 -419
  210. package/workspace/lib/vendor/workspace/lib/review-gate.js +0 -39
  211. package/workspace/lib/vendor/workspace/lib/sink.js +0 -321
  212. package/workspace/lib/vendor/workspace/lib/state-root.js +0 -587
@@ -1,14 +1,15 @@
1
1
  /* pm/pm-work-view.js — the Work view of My work, built on the console3 mockup's own markup and CSS (initiative
2
2
  * planning-flows-and-agent-sessions). The mockup (prototype/pmos-console-handoff/mockups/console3.html) is the UI: the rows
3
3
  * (.nrow with .rv, .ki, .nb, .tt, .chips, .xp), the lens bar (.calbar, .seg), the add line (.c3add, .chips2), the case file
4
- * (.case, .c3jf, .c3steps), the jobs card (.card.sect) and the swipe gesture are the mockup's; the styles are pm/console3.css
4
+ * (.case, .c3jf, .c3steps), the Focus card (.card.sect) and the swipe gesture are the mockup's; the styles are pm/console3.css
5
5
  * (see pm/console3-lift.md). This file keeps the real data calls and draws what they return, nothing else.
6
6
  *
7
7
  * mountPmWork(host, ctx) draws once per host; disposePmWork(host) removes it.
8
8
  * Views (initiative console3-work-page, planning/prd/console3-work-page-prd.md): Inbox, Today, Upcoming, Waiting, Someday and Review.
9
9
  * The SERVER places every item and answers `work`, `review`, `focus` and `counts`; this file draws them and never copies an item from
10
- * one view to another. Today opens with Focus (up to three jobs, each with the reason: Accept, Edit title, Not today). Upcoming groups
11
- * the next 7 days by day, then Later. Review is a weekly pass, one item at a time (Keep, Change, Drop). An item done outside PMOS
10
+ * one view to another. The right column of every view is Focus (up to three jobs, each with the reason: Accept, Edit, Not today; a row opens the
11
+ * job's sheet) (initiative console3-work-parity). Today is Overdue, Due today and Flagged. Upcoming is Next 7 days by day, then a Later card. Review
12
+ * also carries the agent's drafts section that Briefing used to show. Review is a weekly pass, one item at a time (Keep, Change, Drop). An item done outside PMOS
12
13
  * (`kind: offline`) shows the person and offers Done with an optional outcome, Not yet and Drop, and "Looks done?" with Confirm when
13
14
  * it carries `evidence`; PMOS never closes it by itself.
14
15
  * Routes (lib/routes-pm-items.js). Every write is a page press through pmPost:
@@ -22,7 +23,7 @@
22
23
  * drawn disabled ("no channel connected"); nothing is sent.
23
24
  * In demo mode (/c3.html?demo=1, window.__c3demo) the answers of the writes are laid over the fixtures in memory (`applyOv`) and placed
24
25
  * with work-place.js, the server's rule; the agent run of a row with a suggested move is a mock that only walks the steps and shows a
25
- * labelled draft. The deck is ctx.plan, a node the page builds. The microphone is drawn disabled.
26
+ * labelled draft. ctx.go(tab) opens another tab. The microphone is drawn disabled.
26
27
  *
27
28
  * The helpers c3ic, c3ring, ensureC3Css and c3Phone are shared with briefing-deck.js and calendar-views.js. */
28
29
  import { el } from './dom.js';
@@ -32,13 +33,16 @@ import { renderCase } from './c3-case.js';
32
33
  import { makeXp, attachExpand } from './c3-expand.js';
33
34
  import { attachSwipe } from './c3-swipe.js';
34
35
  import { toast } from './c3-toast.js';
36
+ import { openSheet } from './c3-sheet.js';
37
+ import { onBriefing, loadBriefing } from './shell-ticker.js';
38
+ import { reviewSection } from './c3-brief.js';
35
39
  import { VIEWS, REVIEW_DAYS, workPlace, arrives, buildWork, addIso, daysBetween, untouchedDays } from './work-place.js';
36
40
 
37
41
  /* ── shared with the Briefing and Calendar views: the stylesheet, the icon sprite, the ring, the phone class ───────── */
38
42
 
39
43
  const SVG_NS = 'http://www.w3.org/2000/svg';
40
44
  /* The mockup's icon sprite (console3.html lines 970 to 1026, ids renamed c3i-*, plus the c3 layer's edit and trash). */
41
- const SPRITE = '<symbol id="c3i-sun" viewBox="0 0 24 24"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></symbol><symbol id="c3i-moon" viewBox="0 0 24 24"><path d="M20 14a8 8 0 11-10-10 6 6 0 0010 10z"/></symbol><symbol id="c3i-cal" viewBox="0 0 24 24"><rect x="4" y="5" width="16" height="15" rx="2"/><path d="M4 10h16M8 3v4M16 3v4"/></symbol><symbol id="c3i-inbox" viewBox="0 0 24 24"><path d="M4 13l2-8h12l2 8M4 13v6h16v-6M4 13h5l1 2h4l1-2h5"/></symbol><symbol id="c3i-todo" viewBox="0 0 24 24"><rect x="4" y="4" width="16" height="16" rx="3"/><path d="M8 12l3 3 5-6"/></symbol><symbol id="c3i-init" viewBox="0 0 24 24"><path d="M12 3l9 5-9 5-9-5 9-5zM3 13l9 5 9-5"/></symbol><symbol id="c3i-report" viewBox="0 0 24 24"><path d="M5 20V10M12 20V4M19 20v-7"/></symbol><symbol id="c3i-notes" viewBox="0 0 24 24"><path d="M6 3h9l4 4v14H6zM14 3v5h5M9 13h7M9 17h5"/></symbol><symbol id="c3i-user" viewBox="0 0 24 24"><circle cx="12" cy="8" r="4"/><path d="M4 21c0-4 4-6 8-6s8 2 8 6"/></symbol><symbol id="c3i-doc" viewBox="0 0 24 24"><path d="M6 3h9l4 4v14H6zM14 3v5h5"/></symbol><symbol id="c3i-sheet" viewBox="0 0 24 24"><rect x="4" y="4" width="16" height="16" rx="2"/><path d="M4 10h16M4 15h16M10 4v16"/></symbol><symbol id="c3i-img" viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="16" rx="2"/><circle cx="9" cy="10" r="2"/><path d="M21 16l-5-5-9 9"/></symbol><symbol id="c3i-pdf" viewBox="0 0 24 24"><path d="M6 3h9l4 4v14H6zM14 3v5h5M9 15h6M9 18h4"/></symbol><symbol id="c3i-term" viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M7 9l3 3-3 3M13 15h4"/></symbol><symbol id="c3i-branch" viewBox="0 0 24 24"><circle cx="6" cy="5" r="2"/><circle cx="6" cy="19" r="2"/><circle cx="18" cy="9" r="2"/><path d="M6 7v10M18 11c0 4-6 3-12 6"/></symbol><symbol id="c3i-repo" viewBox="0 0 24 24"><path d="M5 4h11a3 3 0 013 3v13H8a3 3 0 01-3-3zM5 17a3 3 0 013-3h11"/></symbol><symbol id="c3i-flag" viewBox="0 0 24 24"><path d="M6 21V4M6 5h11l-2 4 2 4H6"/></symbol><symbol id="c3i-clock" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></symbol><symbol id="c3i-alert" viewBox="0 0 24 24"><path d="M12 3l10 18H2zM12 10v5M12 18v.5"/></symbol><symbol id="c3i-check" viewBox="0 0 24 24"><path d="M5 12l5 5 9-10"/></symbol><symbol id="c3i-x" viewBox="0 0 24 24"><path d="M6 6l12 12M18 6L6 18"/></symbol><symbol id="c3i-eye" viewBox="0 0 24 24"><path d="M2 12s4-7 10-7 10 7 10 7-4 7-10 7S2 12 2 12z"/><circle cx="12" cy="12" r="3"/></symbol><symbol id="c3i-undo" viewBox="0 0 24 24"><path d="M4 12a8 8 0 108-8M4 4v6h6"/></symbol><symbol id="c3i-out" viewBox="0 0 24 24"><path d="M5 12h14M13 6l6 6-6 6"/></symbol><symbol id="c3i-shield" viewBox="0 0 24 24"><path d="M12 3l8 3v6c0 5-3.5 8-8 9-4.5-1-8-4-8-9V6z"/></symbol><symbol id="c3i-mic" viewBox="0 0 24 24"><rect x="9" y="3" width="6" height="11" rx="3"/><path d="M5 11a7 7 0 0014 0M12 18v3"/></symbol><symbol id="c3i-clip" viewBox="0 0 24 24"><rect x="6" y="4" width="12" height="17" rx="2"/><path d="M9 4h6v3H9zM9 12h6M9 16h4"/></symbol><symbol id="c3i-search" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="M21 21l-5-5"/></symbol><symbol id="c3i-phone" viewBox="0 0 24 24"><rect x="7" y="2" width="10" height="20" rx="2"/><path d="M11 18h2"/></symbol><symbol id="c3i-cloud" viewBox="0 0 24 24"><path d="M7 18a4 4 0 010-8 5 5 0 019.6-1A4.5 4.5 0 0117 18z"/></symbol><symbol id="c3i-bolt" viewBox="0 0 24 24"><path d="M13 2L4 14h7l-1 8 9-12h-7z"/></symbol><symbol id="c3i-plus" viewBox="0 0 24 24"><path d="M12 5v14M5 12h14"/></symbol><symbol id="c3i-scale" viewBox="0 0 24 24"><path d="M12 3v18M5 7h14M5 7l-3 7a3 3 0 006 0zM19 7l-3 7a3 3 0 006 0z"/></symbol><symbol id="c3i-reply" viewBox="0 0 24 24"><path d="M9 7L4 12l5 5M4 12h10a6 6 0 016 6"/></symbol><symbol id="c3i-info" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 11v6M12 7.5v.5"/></symbol><symbol id="c3i-wait" viewBox="0 0 24 24"><path d="M7 3h10M7 21h10M8 3c0 5 8 5 8 9s-8 4-8 9"/></symbol><symbol id="c3i-up" viewBox="0 0 24 24"><path d="M7 11v9H4v-9zM7 11l4-8c2 0 3 1 3 3v3h5a2 2 0 012 2l-1 6a2 2 0 01-2 2H7"/></symbol><symbol id="c3i-cols" viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M9 4v16M15 4v16"/></symbol><symbol id="c3i-fold" viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M15 4v16"/></symbol><symbol id="c3i-spark" viewBox="0 0 24 24"><path d="M12 3l2 5 5 2-5 2-2 5-2-5-5-2 5-2z"/></symbol><symbol id="c3i-target" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="5"/><circle cx="12" cy="12" r="1"/></symbol><symbol id="c3i-filter" viewBox="0 0 24 24"><path d="M3 5h18l-7 8v6l-4-2v-4z"/></symbol><symbol id="c3i-mail" viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="M3 7l9 6 9-6"/></symbol><symbol id="c3i-lock" viewBox="0 0 24 24"><rect x="5" y="11" width="14" height="10" rx="2"/><path d="M8 11V8a4 4 0 018 0v3"/></symbol><symbol id="c3i-play" viewBox="0 0 24 24"><path d="M7 4l13 8-13 8z"/></symbol><symbol id="c3i-stop" viewBox="0 0 24 24"><rect x="6" y="6" width="12" height="12" rx="1"/></symbol><symbol id="i-pause" viewBox="0 0 24 24"><path d="M8 5v14M16 5v14"/></symbol><symbol id="c3i-prev" viewBox="0 0 24 24"><path d="M15 6l-6 6 6 6"/></symbol><symbol id="c3i-next" viewBox="0 0 24 24"><path d="M9 6l6 6-6 6"/></symbol><symbol id="c3i-tree" viewBox="0 0 24 24"><path d="M5 4h7v4H5zM13 10h7v4h-7zM13 17h7v4h-7zM8 8v11h5M8 12h5"/></symbol><symbol id="c3i-org" viewBox="0 0 24 24"><path d="M9 3h6v5H9zM3 16h6v5H3zM15 16h6v5h-6zM12 8v4M6 16v-4h12v4"/></symbol><symbol id="c3i-drop" viewBox="0 0 24 24"><path d="M12 3c4 5 6 8 6 11a6 6 0 01-12 0c0-3 2-6 6-11z"/></symbol><symbol id="c3i-more" viewBox="0 0 24 24"><path d="M5 12h.01M12 12h.01M19 12h.01" stroke-width="3"/></symbol><symbol id="c3i-zin" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="M21 21l-5-5M8 11h6M11 8v6"/></symbol><symbol id="c3i-zout" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="M21 21l-5-5M8 11h6"/></symbol><symbol id="c3i-bell" viewBox="0 0 24 24"><path d="M6 17v-6a6 6 0 0112 0v6l2 2H4zM10 21h4"/></symbol><symbol id="c3i-vol" viewBox="0 0 24 24"><path d="M4 9v6h4l5 4V5L8 9zM16.5 8.5a5 5 0 010 7M19 6a8.5 8.5 0 010 12"/></symbol><symbol id="c3i-menu" viewBox="0 0 24 24"><path d="M4 7h16M4 12h16M4 17h16"/></symbol><symbol id="c3i-edit" viewBox="0 0 24 24"><path d="M4 20h4L19 9l-4-4L4 16v4zM13.5 6.5l4 4"/></symbol><symbol id="c3i-trash" viewBox="0 0 24 24"><path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13"/></symbol>';
45
+ const SPRITE = '<symbol id="c3i-sun" viewBox="0 0 24 24"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></symbol><symbol id="c3i-star" viewBox="0 0 24 24"><path d="M12 3l2.6 5.6 6.1.7-4.5 4.2 1.2 6L12 16.5 6.6 19.5l1.2-6L3.3 9.3l6.1-.7z"/></symbol><symbol id="c3i-moon" viewBox="0 0 24 24"><path d="M20 14a8 8 0 11-10-10 6 6 0 0010 10z"/></symbol><symbol id="c3i-cal" viewBox="0 0 24 24"><rect x="4" y="5" width="16" height="15" rx="2"/><path d="M4 10h16M8 3v4M16 3v4"/></symbol><symbol id="c3i-inbox" viewBox="0 0 24 24"><path d="M4 13l2-8h12l2 8M4 13v6h16v-6M4 13h5l1 2h4l1-2h5"/></symbol><symbol id="c3i-todo" viewBox="0 0 24 24"><rect x="4" y="4" width="16" height="16" rx="3"/><path d="M8 12l3 3 5-6"/></symbol><symbol id="c3i-init" viewBox="0 0 24 24"><path d="M12 3l9 5-9 5-9-5 9-5zM3 13l9 5 9-5"/></symbol><symbol id="c3i-report" viewBox="0 0 24 24"><path d="M5 20V10M12 20V4M19 20v-7"/></symbol><symbol id="c3i-notes" viewBox="0 0 24 24"><path d="M6 3h9l4 4v14H6zM14 3v5h5M9 13h7M9 17h5"/></symbol><symbol id="c3i-user" viewBox="0 0 24 24"><circle cx="12" cy="8" r="4"/><path d="M4 21c0-4 4-6 8-6s8 2 8 6"/></symbol><symbol id="c3i-doc" viewBox="0 0 24 24"><path d="M6 3h9l4 4v14H6zM14 3v5h5"/></symbol><symbol id="c3i-sheet" viewBox="0 0 24 24"><rect x="4" y="4" width="16" height="16" rx="2"/><path d="M4 10h16M4 15h16M10 4v16"/></symbol><symbol id="c3i-img" viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="16" rx="2"/><circle cx="9" cy="10" r="2"/><path d="M21 16l-5-5-9 9"/></symbol><symbol id="c3i-pdf" viewBox="0 0 24 24"><path d="M6 3h9l4 4v14H6zM14 3v5h5M9 15h6M9 18h4"/></symbol><symbol id="c3i-term" viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M7 9l3 3-3 3M13 15h4"/></symbol><symbol id="c3i-branch" viewBox="0 0 24 24"><circle cx="6" cy="5" r="2"/><circle cx="6" cy="19" r="2"/><circle cx="18" cy="9" r="2"/><path d="M6 7v10M18 11c0 4-6 3-12 6"/></symbol><symbol id="c3i-repo" viewBox="0 0 24 24"><path d="M5 4h11a3 3 0 013 3v13H8a3 3 0 01-3-3zM5 17a3 3 0 013-3h11"/></symbol><symbol id="c3i-flag" viewBox="0 0 24 24"><path d="M6 21V4M6 5h11l-2 4 2 4H6"/></symbol><symbol id="c3i-clock" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/></symbol><symbol id="c3i-alert" viewBox="0 0 24 24"><path d="M12 3l10 18H2zM12 10v5M12 18v.5"/></symbol><symbol id="c3i-check" viewBox="0 0 24 24"><path d="M5 12l5 5 9-10"/></symbol><symbol id="c3i-x" viewBox="0 0 24 24"><path d="M6 6l12 12M18 6L6 18"/></symbol><symbol id="c3i-eye" viewBox="0 0 24 24"><path d="M2 12s4-7 10-7 10 7 10 7-4 7-10 7S2 12 2 12z"/><circle cx="12" cy="12" r="3"/></symbol><symbol id="c3i-undo" viewBox="0 0 24 24"><path d="M4 12a8 8 0 108-8M4 4v6h6"/></symbol><symbol id="c3i-out" viewBox="0 0 24 24"><path d="M5 12h14M13 6l6 6-6 6"/></symbol><symbol id="c3i-shield" viewBox="0 0 24 24"><path d="M12 3l8 3v6c0 5-3.5 8-8 9-4.5-1-8-4-8-9V6z"/></symbol><symbol id="c3i-mic" viewBox="0 0 24 24"><rect x="9" y="3" width="6" height="11" rx="3"/><path d="M5 11a7 7 0 0014 0M12 18v3"/></symbol><symbol id="c3i-clip" viewBox="0 0 24 24"><rect x="6" y="4" width="12" height="17" rx="2"/><path d="M9 4h6v3H9zM9 12h6M9 16h4"/></symbol><symbol id="c3i-search" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="M21 21l-5-5"/></symbol><symbol id="c3i-phone" viewBox="0 0 24 24"><rect x="7" y="2" width="10" height="20" rx="2"/><path d="M11 18h2"/></symbol><symbol id="c3i-cloud" viewBox="0 0 24 24"><path d="M7 18a4 4 0 010-8 5 5 0 019.6-1A4.5 4.5 0 0117 18z"/></symbol><symbol id="c3i-bolt" viewBox="0 0 24 24"><path d="M13 2L4 14h7l-1 8 9-12h-7z"/></symbol><symbol id="c3i-plus" viewBox="0 0 24 24"><path d="M12 5v14M5 12h14"/></symbol><symbol id="c3i-scale" viewBox="0 0 24 24"><path d="M12 3v18M5 7h14M5 7l-3 7a3 3 0 006 0zM19 7l-3 7a3 3 0 006 0z"/></symbol><symbol id="c3i-reply" viewBox="0 0 24 24"><path d="M9 7L4 12l5 5M4 12h10a6 6 0 016 6"/></symbol><symbol id="c3i-info" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><path d="M12 11v6M12 7.5v.5"/></symbol><symbol id="c3i-wait" viewBox="0 0 24 24"><path d="M7 3h10M7 21h10M8 3c0 5 8 5 8 9s-8 4-8 9"/></symbol><symbol id="c3i-up" viewBox="0 0 24 24"><path d="M7 11v9H4v-9zM7 11l4-8c2 0 3 1 3 3v3h5a2 2 0 012 2l-1 6a2 2 0 01-2 2H7"/></symbol><symbol id="c3i-cols" viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M9 4v16M15 4v16"/></symbol><symbol id="c3i-fold" viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="16" rx="2"/><path d="M15 4v16"/></symbol><symbol id="c3i-spark" viewBox="0 0 24 24"><path d="M12 3l2 5 5 2-5 2-2 5-2-5-5-2 5-2z"/></symbol><symbol id="c3i-target" viewBox="0 0 24 24"><circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="5"/><circle cx="12" cy="12" r="1"/></symbol><symbol id="c3i-filter" viewBox="0 0 24 24"><path d="M3 5h18l-7 8v6l-4-2v-4z"/></symbol><symbol id="c3i-mail" viewBox="0 0 24 24"><rect x="3" y="5" width="18" height="14" rx="2"/><path d="M3 7l9 6 9-6"/></symbol><symbol id="c3i-lock" viewBox="0 0 24 24"><rect x="5" y="11" width="14" height="10" rx="2"/><path d="M8 11V8a4 4 0 018 0v3"/></symbol><symbol id="c3i-play" viewBox="0 0 24 24"><path d="M7 4l13 8-13 8z"/></symbol><symbol id="c3i-stop" viewBox="0 0 24 24"><rect x="6" y="6" width="12" height="12" rx="1"/></symbol><symbol id="i-pause" viewBox="0 0 24 24"><path d="M8 5v14M16 5v14"/></symbol><symbol id="c3i-prev" viewBox="0 0 24 24"><path d="M15 6l-6 6 6 6"/></symbol><symbol id="c3i-next" viewBox="0 0 24 24"><path d="M9 6l6 6-6 6"/></symbol><symbol id="c3i-tree" viewBox="0 0 24 24"><path d="M5 4h7v4H5zM13 10h7v4h-7zM13 17h7v4h-7zM8 8v11h5M8 12h5"/></symbol><symbol id="c3i-org" viewBox="0 0 24 24"><path d="M9 3h6v5H9zM3 16h6v5H3zM15 16h6v5h-6zM12 8v4M6 16v-4h12v4"/></symbol><symbol id="c3i-drop" viewBox="0 0 24 24"><path d="M12 3c4 5 6 8 6 11a6 6 0 01-12 0c0-3 2-6 6-11z"/></symbol><symbol id="c3i-more" viewBox="0 0 24 24"><path d="M5 12h.01M12 12h.01M19 12h.01" stroke-width="3"/></symbol><symbol id="c3i-zin" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="M21 21l-5-5M8 11h6M11 8v6"/></symbol><symbol id="c3i-zout" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="M21 21l-5-5M8 11h6"/></symbol><symbol id="c3i-bell" viewBox="0 0 24 24"><path d="M6 17v-6a6 6 0 0112 0v6l2 2H4zM10 21h4"/></symbol><symbol id="c3i-vol" viewBox="0 0 24 24"><path d="M4 9v6h4l5 4V5L8 9zM16.5 8.5a5 5 0 010 7M19 6a8.5 8.5 0 010 12"/></symbol><symbol id="c3i-menu" viewBox="0 0 24 24"><path d="M4 7h16M4 12h16M4 17h16"/></symbol><symbol id="c3i-edit" viewBox="0 0 24 24"><path d="M4 20h4L19 9l-4-4L4 16v4zM13.5 6.5l4 4"/></symbol><symbol id="c3i-trash" viewBox="0 0 24 24"><path d="M4 7h16M9 7V4h6v3M6 7l1 13h10l1-13"/></symbol>';
42
46
 
43
47
  /** Link console3.css once, from this module's own folder (the same way primitives.js links components.css). */
44
48
  export function ensureC3Css() {
@@ -106,37 +110,44 @@ const API = '/api/pm-items';
106
110
  const JOBS = '/api/pm-jobs';
107
111
  /* The six views (initiative console3-work-page, PRD R1): five places an open item can be, and the weekly Review pass. */
108
112
  const LENSES = [['inbox', 'Inbox'], ['today', 'Today'], ['upcoming', 'Upcoming'], ['waiting', 'Waiting'], ['someday', 'Someday'], ['review', 'Review']];
109
- const WLI = { inbox: 'inbox', today: 'sun', upcoming: 'cal', waiting: 'wait', someday: 'moon', review: 'eye' };
113
+ const WLI = { inbox: 'inbox', today: 'sun', upcoming: 'cal', waiting: 'wait', someday: 'moon', review: 'check' };
110
114
  const WHO = ['you', 'agent-drafts', 'agent-does'];
111
115
  const OUTWARD = /\b(send|sends|sending|email|emails|message|messages|tell|post|reply to)\b/i; // mirrors lib/item-steps.js
112
116
  const RESOLVES_TIP = 'Your work. The team task is separate: close it in its initiative.';
113
117
 
114
118
  export const COPY = Object.freeze({
115
119
  addPlaceholder: 'Add work: type or speak',
116
- addLabel: 'Add work', addBtn: 'Add', hint: 'Try: Send Dana the new date by Fri !1',
120
+ addLabel: 'Add work', addBtn: 'Add', hint: 'Try: Send Dewi the new date by Fri',
117
121
  addsTo: 'adds to', noJob: 'no job yet', waitingOn: (w) => 'waiting on ' + w,
118
122
  empty: 'Nothing here.', noJobGroup: 'No job yet', jobs: 'Jobs to be done', noJobs: 'No jobs yet.',
119
123
  addJob: 'Add a job', addJobPlaceholder: 'when I plan a release, I want honest dates, so Dana can trust them',
120
124
  groupJob: 'Group by job to be done', groupTime: 'Group by time', loading: 'Reading…',
121
125
  micOff: 'Speak is off on this line: on-device dictation runs in Notes, in the main app. Type the item here.',
122
126
  jobsNote: 'The count is every item in the job, across all views. Tap a job to edit or delete it.',
123
- legend: ['open', 'done', 'migrated', 'scheduled'], readError: 'Your items could not be read.', saveError: 'Nothing was saved.',
127
+ legend: ['open', 'done'], readError: 'Your items could not be read.', saveError: 'Nothing was saved.',
124
128
  removed: 'Removed.', undo: 'Undo', restored: 'Restored.', tooLate: 'Too late to undo.', drop: 'Drop',
125
129
  reassigned: (n) => (n === 1 ? '1 item now has no job.' : `${n} items now have no job.`),
126
130
  progress: (d, t) => `${d} of ${t} done`,
127
131
  jobLine: (j) => `When ${j.when}, I want ${j.want}, so ${j.so}.`,
132
+ /* The header of each view (the mockup's INTRO): a bold line and a dim line. */
128
133
  lens: {
129
- inbox: (n) => `${n} to decide`, today: (n) => `${n} for today`, upcoming: (n) => `${n} in the days ahead`,
130
- waiting: (n) => `${n} waiting on others`, someday: (n) => `${n} parked in Someday`, review: (n) => `${n} to review this week`,
134
+ inbox: 'Decide when or whether', today: 'Overdue, due today and flagged', upcoming: 'The next 7 days, then anything later',
135
+ waiting: 'Things you handed to someone', someday: 'Parked on purpose', review: 'A weekly pass over items without a date',
131
136
  },
132
137
  lensSub: {
133
- inbox: 'Captured, not decided. Give each one a day, a Not before date, a person to wait on, or park it in Someday.',
134
- today: 'Overdue, due today and flagged. Focus says which jobs matter most today.',
135
- upcoming: 'The next 7 days by day, then Later. A Not before date keeps an item out of Today until that day.',
136
- waiting: 'Someone else must act first.',
137
- someday: 'Parked on purpose. Nothing here asks for attention until the weekly Review.',
138
- review: 'Items in Inbox, Waiting or Someday that nobody touched for 7 days, most stale first. Keep, change or drop each one.',
138
+ inbox: 'Nothing here has a date. Tap Decide to give each item a day, park it or drop it.',
139
+ today: 'Focus says which jobs matter most today.',
140
+ upcoming: 'A "not before" date keeps an item out of Today until it arrives.',
141
+ waiting: 'You expect these back. Nudge when one goes quiet.',
142
+ someday: 'Nothing here asks for attention until the weekly Review.',
143
+ review: 'Items in Inbox, Waiting or Someday that nobody touched for 7 days, most stale first. One at a time: keep it, change it, or drop it.',
139
144
  },
145
+ sect: { inbox: 'Inbox', waiting: 'Waiting', someday: 'Someday', overdue: 'Overdue', dueToday: 'Due today', flagged: 'Flagged', next7: 'Next 7 days', later: 'Later' },
146
+ decisions: (n) => ` ${n} decision${n === 1 ? '' : 's'} wait${n === 1 ? 's' : ''} in Briefing, Needs you.`, openBriefing: 'Open Briefing',
147
+ inboxNote: 'New asks for initiatives are triaged in', initiatives: 'Initiatives', inboxNoteEnd: '. This inbox is only for your own work.',
148
+ clear: 'Clear', beyond: 'beyond 7 days', nothingLater: 'Nothing beyond 7 days.', tomorrowHead: 'Tomorrow',
149
+ decide: 'Decide', nudge: 'Nudge', doneBtn: 'Done',
150
+ nudged: (w) => `Nudge to ${w} drafted. No channel is connected, so nothing is sent.`,
140
151
  emptyView: {
141
152
  inbox: 'Inbox is empty. Capture anything that crosses your mind.', today: 'Nothing due, overdue or flagged. Pick from Upcoming when you are ready.',
142
153
  upcoming: 'Nothing dated in the days ahead.', waiting: 'Nobody owes you anything right now.', someday: 'Nothing parked.',
@@ -144,17 +155,19 @@ export const COPY = Object.freeze({
144
155
  done: 'Done', doneToast: 'Marked done.', steps: 'Steps', noSteps: 'No steps yet.', addStep: 'Add a step',
145
156
  who: { you: 'You', 'agent-drafts': 'Agent drafts', 'agent-does': 'Agent does' },
146
157
  whoOutward: 'Sends to a person: the agent drafts, you slide to send.', options: 'Options', plan: 'Plan the steps',
147
- /* Focus (PRD R4) */
148
- focus: 'Focus', focusSub: 'Up to three jobs for today, each with the reason.', focusNone: 'No focus suggested for today. Everything below still counts.',
149
- focusPinned: 'Focus today', focusSuggested: 'Suggested', accept: 'Accept', editTitle: 'Edit title', notToday: 'Not today', unpin: 'Unpin', save: 'Save', cancel: 'Cancel',
150
- why: (w) => 'Why: ' + w, focusAccepted: 'Pinned to Today.', focusDismissed: 'Not today. It does not come back today.', focusCleared: 'Unpinned.',
158
+ /* Focus (PRD R4; console3-work-parity R2) */
159
+ focus: 'Focus: jobs to be done', focusNone: 'No focus for today. Everything below still counts.',
160
+ focusPinned: 'Focus today', accept: 'Accept', editTitle: 'Edit title', edit: 'Edit', notToday: 'Not today', unpin: 'Unpin', save: 'Save', cancel: 'Cancel',
161
+ why: (w) => 'Why: ' + w, focusAccepted: 'Added to Focus today', focusDismissed: 'Not today. PMOS will suggest it again tomorrow.', focusCleared: 'Unpinned.',
162
+ markedNot: (n) => `${n} marked not today.`, bringBack2: 'Bring back', broughtBack: 'Brought back.', newJob: 'New job', jobSheet: 'Job to be done',
151
163
  /* offline work and the shared actions (PRD R6) */
152
164
  offline: 'outside PMOS', offlineNote: (p) => 'Happens outside PMOS' + (p ? ', with ' + p : '') + '. Tell PMOS how it went.',
153
165
  outcome: 'What came out of it (optional)', notYet: 'Not yet:', laterToday: 'Later today', tomorrow: 'Tomorrow', nextWeek: 'Next week',
154
166
  looksDone: 'Looks done?', confirm: 'Confirm done', flag: 'Flag', unflag: 'Unflag', notBefore: 'Not before', someday: 'Someday', bringBack: 'Bring back',
155
167
  waitingField: 'Waiting on', waitingWho: 'Who?', stopWaiting: 'Stop waiting', flagged: 'Flagged',
156
- notBeforeChip: (d) => 'Not before ' + d, advances: (t) => 'Advances: ' + t, moved: (n) => (n === 1 ? 'Moved once' : `Moved ${n} times`),
157
- overdue: (n) => (n === 1 ? '1 day overdue' : `${n} days overdue`),
168
+ notBeforeChip: (d) => 'Not before ' + d, advances: (t) => 'Advances team task ' + t, moved: (n) => `Moved ${n}×`,
169
+ overdue: (n) => (n === 1 ? 'Overdue since yesterday' : `${n} days overdue`), dueToday: 'Due today', dueOn: (d) => 'Due ' + d,
170
+ waitingSince: (w, since, ago) => `Waiting for ${w}` + (since ? ` since ${since}` : '') + (ago ? ` (${ago})` : ''), repeats: (r) => 'Repeats ' + String(r).toLowerCase(),
158
171
  /* Review (PRD R7) */
159
172
  reviewHead: 'Weekly review', keep: 'Keep', change: 'Change', next: 'Done changing, next', reviewDone: 'Review done',
160
173
  reviewDoneText: (n) => `You looked at ${n} item${n === 1 ? '' : 's'}. Anything you changed or dropped has moved to its new view.`,
@@ -195,6 +208,7 @@ function applyOv(base, ov) {
195
208
  d.focus = {
196
209
  accepted: [...(F.accepted || []).filter((a) => !st(a.key)), ...[...ov.focus.values()].filter((x) => x.state === 'acc').map((x) => x.f)],
197
210
  suggestions: (F.suggestions || []).filter((s) => (!st(s.key) || st(s.key).state === 'clear') && live(s)).slice(0, 3),
211
+ dismissed: [...new Set([...(F.dismissed || []), ...[...ov.focus.entries()].filter(([, x]) => x.state === 'no').map(([k]) => k)])].filter((k) => !st(k) || st(k).state === 'no'),
198
212
  };
199
213
  const jobs = [...(d.jobs || []), ...ov.jobsAdded].filter((j) => !ov.jobsGone.has(j.id)).map((j) => ({ ...j, ...(ov.jobEdits.get(j.id) || {}) }));
200
214
  jobs.forEach((j) => {
@@ -269,9 +283,9 @@ export function mountPmWork(host, ctx) {
269
283
  if (inst) return;
270
284
  if (ctx && ctx.FIXTURE) return;
271
285
  ensureC3Css();
272
- inst = { host, ctx: ctx || {}, root: el('div', { class: 'c3 calwrap', 'data-pm-work': '' }), data: null, base: null, ov: newOv(), lens: 'today', group: false,
273
- opens: new Set(), picks: new Map(), editing: new Set(), planning: new Set(), runs: new Map(), trays: new Set(), cases: new Set(), timers: new Set(), hidden: new Set(), jobNew: null, jobForm: false,
274
- focEdit: new Set(), rv: { ids: null, handled: new Set(), changing: false } };
286
+ inst = { host, ctx: ctx || {}, root: el('div', { class: 'c3 calwrap wk', 'data-pm-work': '' }), data: null, base: null, ov: newOv(), lens: 'today',
287
+ opens: new Set(), picks: new Map(), editing: new Set(), planning: new Set(), runs: new Map(), trays: new Set(), cases: new Set(), timers: new Set(), hidden: new Set(),
288
+ focEdit: new Set(), rv: { ids: null, handled: new Set(), changing: false }, decisions: 0, drafts: null, draftsSig: '', draftsHost: el('div', { 'data-pm-review-drafts': '' }) };
275
289
  inst.body = el('div', { class: 'calbody pmw-body' });
276
290
  inst.bar = el('div', { class: 'calbar' });
277
291
  inst.addBox = addLine(inst);
@@ -279,7 +293,9 @@ export function mountPmWork(host, ctx) {
279
293
  inst.stopPhone = c3Phone(inst.root);
280
294
  insts.set(host, inst);
281
295
  host.appendChild(inst.root);
282
- if (inst.ctx.onLens) inst.ctx.onLens(inst.lens); // the page's deck follows the view the page opens on
296
+ if (inst.ctx.onLens) inst.ctx.onLens(inst.lens);
297
+ inst.stopBrief = onBriefing((b) => briefRead(inst, b)); // the decisions that wait in Briefing, and the drafts the Review view carries
298
+ loadBriefing();
283
299
  load(inst);
284
300
  }
285
301
 
@@ -287,6 +303,7 @@ export function disposePmWork(host) {
287
303
  const inst = insts.get(host);
288
304
  if (!inst) return;
289
305
  if (inst.stopPhone) inst.stopPhone();
306
+ if (inst.stopBrief) inst.stopBrief();
290
307
  disposeCases(inst);
291
308
  inst.timers.forEach((t) => clearTimeout(t));
292
309
  inst.root.remove();
@@ -435,7 +452,7 @@ function addLine(inst) {
435
452
  const r = await press(inst, API + '/add', { line }, { quiet: true });
436
453
  if (!r) return;
437
454
  input.value = ''; chips.replaceChildren(...previewChips(inst, ''));
438
- inst.lens = workPlace(o, before); if (inst.ctx.onLens) inst.ctx.onLens(inst.lens); inst.group = false;
455
+ inst.lens = workPlace(o, before); if (inst.ctx.onLens) inst.ctx.onLens(inst.lens);
439
456
  if (r.item) inst.opens.add(r.item.id);
440
457
  say({ text: 'Added to ' + labelOf(inst.lens), timeout: r.undo_ms || 6000, actionLabel: COPY.undo, onAction: async () => { if (r.item) await press(inst, API + '/remove', { id: r.item.id }, { quiet: true }); await load(inst); } });
441
458
  await load(inst);
@@ -741,7 +758,7 @@ function swipeSpec(inst, item, lens) {
741
758
  function expandRow(inst, row, key, build, onClose) {
742
759
  const xp = makeXp();
743
760
  const xi = xp.firstChild;
744
- attachExpand(row, xp, {
761
+ row.__exp = attachExpand(row, xp, {
745
762
  key, initial: inst.opens.has(key),
746
763
  onToggle: (on) => {
747
764
  if (on) {
@@ -761,49 +778,73 @@ const KIND_ICON = { reply: 'reply', decide: 'scale', review: 'eye', do: 'check'
761
778
  /* The mockup's time-left bar (.fuse) with its text, for an item that carries `left` ({ text, pct, tone }). */
762
779
  const fuseBar = (l) => { const c = l.tone === 'warn' ? 'var(--warning)' : 'var(--acc)'; return el('div', { class: 'chips2', style: 'margin-top:6px;gap:8px' }, el('div', { class: 'fuse' }, el('i', { style: 'width:' + Math.round(l.pct * 100) + '%;background:' + c })), el('span', { class: 'mono', style: 'font-size:var(--fs-1);color:' + c }, l.text)); };
763
780
 
781
+ /* "yesterday", "7 days ago": how long ago a day was (the mockup's ago()). */
782
+ const ago = (from, today) => { const n = daysBetween(from, today); return n <= 0 ? 'today' : n === 1 ? 'yesterday' : n + ' days ago'; };
783
+
784
+ /* The due chip of a row (the mockup's dueChip): a tag for overdue and for today, a calendar chip for a later day. */
785
+ function dueChip(inst, item) {
786
+ if (!item.due) return null;
787
+ const today = inst.data.today; const due = String(item.due).slice(0, 10);
788
+ if (due < today) return el('span', { class: 'tag cal3-tone-bad', 'data-pm-due': 'overdue' }, COPY.overdue(daysBetween(due, today)));
789
+ if (due === today) return el('span', { class: 'tag cal3-tone-warn', 'data-pm-due': 'today' }, COPY.dueToday + (item.time ? ' ' + item.time : ''));
790
+ return el('span', { class: 'chip2', 'data-pm-due': 'later' }, c3ic('cal', 'sm'), COPY.dueOn(dayLabel(due)));
791
+ }
792
+
764
793
  /* The chips the Work page adds to a row: outside PMOS and the person, Not before, Flagged, how often it moved, Looks done, the team task it advances. */
765
- function workChips(inst, item) {
794
+ function workChips(inst, item, o = {}) {
766
795
  const today = inst.data.today; const c = [];
767
796
  if (item.kind === 'offline') c.push(el('span', { class: 'tag wk-off', 'data-pm-offline-tag': item.id }, COPY.offline), el('span', { class: 'chip2' }, c3ic('user', 'sm'), item.person || 'someone'));
768
- if (item.defer && item.defer > today) c.push(el('span', { class: 'tag wk-nb', 'data-pm-notbefore': item.id }, COPY.notBeforeChip(dayLabel(item.defer))));
769
- if (item.flagged) c.push(el('span', { class: 'chip2 wk-fl', 'data-pm-flagged': item.id }, c3ic('flag', 'sm'), COPY.flagged));
770
- if (item.due && String(item.due).slice(0, 10) < today) c.push(el('span', { class: 'st bad' }, c3ic('alert', 'sm'), COPY.overdue(daysBetween(item.due, today))));
797
+ if (item.defer && item.defer > today) c.push(el('span', { class: 'tag wk-nb cal3-up', 'data-pm-notbefore': item.id }, COPY.notBeforeChip(dayLabel(item.defer))));
798
+ if (item.flagged && !o.noFlag) c.push(el('span', { class: 'tag cal3-up', 'data-pm-flagged': item.id }, COPY.flagged));
799
+ if (item.recurring) c.push(el('span', { class: 'chip2' }, c3ic('undo', 'sm'), COPY.repeats(item.recurring)));
771
800
  if (item.not_yet_count) c.push(el('span', { class: 'tag' }, COPY.moved(item.not_yet_count)));
772
801
  if (item.kind === 'offline' && item.evidence) c.push(el('span', { class: 'st warn', 'data-pm-looks-done': item.id }, c3ic('eye', 'sm'), COPY.looksDone));
773
802
  if (item.resolves) c.push(el('span', { class: 'chip2', title: RESOLVES_TIP, 'data-pm-resolves': item.resolves }, c3ic('init', 'sm'), COPY.advances(resolvesTitle(inst, item))));
774
803
  return c;
775
804
  }
776
805
 
777
- /* The mockup's workRow(): li.nrow.wrow > .rv.l, .rv.r (made by the swipe), .ki, .nb (.tt, .chips, .xp > .xi), and the .sg glyphs (line 3258). */
778
- function itemRow(inst, item, lens) {
806
+ /* The mockup's workRow(): li.nrow.wrow > .rv.l, .rv.r (made by the swipe), .ki, .nb (.tt, .chips, .xp > .xi), the .acts button and the .sg glyphs (line 3258). */
807
+ function itemRow(inst, item, lens, o = {}) {
779
808
  const j = item.job && jobById(inst, item.job);
809
+ const today = inst.data.today;
780
810
  const chips = [];
781
811
  /* An item with `mock_row` (the /c3.html demo mode's mockup rows) is drawn as the mockup draws a work row: agent move, effort, time, job, source, initiative. */
782
812
  const mock = item.mock_row === true;
783
813
  const rs = inst.runs.get(item.id);
784
814
  const agent = mock && item.agent_move && (item.ix || {}).move;
815
+ const overdue = !!item.due && String(item.due).slice(0, 10) < today;
785
816
  if (rs && rs.phase === 'work') chips.push(el('span', { class: 'st c3strip', 'data-sxs': item.id }, el('span', { class: 'c3spin' }, c3ic('spark', 'sm')), rs.steps[Math.min(rs.i, rs.steps.length - 1)] + ' · ' + (Math.min(rs.i, rs.steps.length - 1) + 1) + ' of ' + rs.steps.length));
786
817
  else if (rs && rs.phase === 'done') chips.push(el('span', { class: 'st ok' }, c3ic('check', 'sm'), 'agent: result ready'));
787
818
  else if (inst.planning.has(item.id)) chips.push(el('span', { class: 'st' }, el('span', { class: 'c3spin' }, c3ic('spark', 'sm')), 'planning the steps'));
819
+ if (item.waiting_on) {
820
+ const since = item.waiting_since ? String(item.waiting_since).slice(0, 10) : '';
821
+ chips.push(el('span', { class: 'chip2', 'data-pm-waiting-chip': item.id }, c3ic('wait', 'sm'), COPY.waitingSince(item.waiting_on, since ? dayLabel(since) : '', since ? ago(since, today) : '')));
822
+ }
823
+ chips.push(dueChip(inst, item));
824
+ chips.push(...workChips(inst, item, { noFlag: o.noFlag }));
788
825
  if (mock && item.agent_move && !(rs && rs.phase === 'done')) chips.push(el('span', { class: 'chip2', style: 'gap:4px', title: 'Swipe right to let the agent act' }, c3ic('spark', 'sm'), 'agent move'));
789
826
  if (mock && item.mins) chips.push(el('span', { class: 'chip2', title: 'Effort' }, c3ic('clock', 'sm'), item.mins <= 2 ? 'under 2 min' : item.mins + ' min'));
790
- if (mock && item.time) chips.push(el('span', { class: 'chip2' }, c3ic('cal', 'sm'), item.time));
791
- if (item.due) chips.push(el('span', { class: 'chip2' }, c3ic('cal', 'sm'), item.due));
792
827
  if (item.priority) chips.push(el('span', { class: 'fl ' + item.priority.toLowerCase() }, item.priority));
793
- if (item.waiting_on) chips.push(el('span', { class: 'chip2' }, c3ic('wait', 'sm'), COPY.waitingOn(item.waiting_on)));
794
- chips.push(...workChips(inst, item));
795
828
  if ((item.steps || []).length) { const dn = item.steps.filter((s) => s.done).length; const ag = item.steps.filter((s) => s.who !== 'you' && !s.done).length; chips.push(el('span', { class: 'chip2', title: 'Steps' }, c3ic('tree', 'sm'), dn + ' of ' + item.steps.length + ' steps' + (ag ? ' · agent can do ' + ag : ''))); }
796
829
  if (j) chips.push(el('span', { class: 'chip2 jc', title: j.want }, c3ic('target', 'sm'), mock && j.mock_name ? j.mock_name : j.want));
797
830
  if (mock && item.src) chips.push(el('span', { class: 'tag' }, item.src));
798
831
  if (mock && item.init) chips.push(el('span', { class: 'chip2' }, c3ic('init', 'sm'), item.init.length > 16 ? item.init.slice(0, 15) + '…' : item.init));
799
832
  const title = el('span', { class: 'tt', 'data-pm-title': item.id }, item.title);
800
833
  const spec = swipeSpec(inst, item, lens);
801
- const recL = itemCase(inst, item, lens).model.opts.find((o) => o.rec);
834
+ const recL = itemCase(inst, item, lens).model.opts.find((q) => q.rec);
802
835
  const recSide = agent ? 'r' : (recL && /^(drop|keep|wait|defer|someday)/.test(recL.id) ? 'l' : 'r');
803
- const icon = mock ? (KIND_ICON[item.kind] || 'check') : item.kind === 'offline' ? 'user' : item.flagged ? 'flag' : item.priority === 'P1' ? 'alert' : 'todo';
836
+ /* the row icon (the mockup): user for work outside PMOS, flag when flagged, wait in Waiting, check otherwise */
837
+ const icon = mock ? (KIND_ICON[item.kind] || 'check') : item.kind === 'offline' ? 'user' : item.flagged ? 'flag' : lens === 'waiting' ? 'wait' : 'check';
838
+ const hot = overdue || item.flagged || item.priority === 'P1';
839
+ /* the right side (mockup W8): Decide in Inbox, Nudge in Waiting, nothing in Someday, a Done check everywhere else */
840
+ let right = null;
841
+ if (lens === 'inbox') right = button({ label: COPY.decide, variant: 'ghost', size: 'sm', icon: 'next', attrs: { 'data-pm-decide': item.id }, onClick: (e) => { e.stopPropagation(); if (row.__exp) row.__exp.toggle(); } });
842
+ else if (lens === 'waiting') right = c3ib(COPY.nudge, 'out', { 'data-pm-nudge': item.id, onClick: (e) => { e.stopPropagation(); say({ text: COPY.nudged(item.waiting_on || 'them'), timeout: 4000 }); } }, 'sm');
843
+ else if (lens !== 'someday') right = c3ib(COPY.doneBtn, 'check', { 'data-pm-done': item.id, onClick: (e) => { e.stopPropagation(); doneAct(inst, item, row, 'Done.'); } }, 'sm');
804
844
  const row = el('li', { class: 'nrow wrow', 'data-row': '', 'data-id': item.id, 'data-w': '1', 'data-pm-item': item.id, 'data-lens': lens },
805
- el('span', { class: 'ki' + (item.priority === 'P1' || item.flagged ? ' hot' : ''), title: item.kind === 'offline' ? 'Happens outside PMOS' : item.priority || '' }, c3ic(icon)),
806
- el('div', { class: 'nb' }, title, mock && item.left ? fuseBar(item.left) : null, chips.length ? el('div', { class: 'chips' }, chips) : null),
845
+ el('span', { class: 'ki' + (hot ? ' hot' : ''), title: item.kind === 'offline' ? 'Happens outside PMOS' : item.priority || '' }, c3ic(icon)),
846
+ el('div', { class: 'nb' }, title, mock && item.left ? fuseBar(item.left) : null, chips.filter(Boolean).length ? el('div', { class: 'chips' }, chips.filter(Boolean)) : null),
847
+ right ? el('div', { class: 'acts' }, right) : el('span'),
807
848
  el('span', { class: 'sg', title: agent ? 'Drag right: the agent acts. Drag left: reply.' : 'Drag right: ' + spec.right.label + '. Drag left: ' + spec.left.label + '.' },
808
849
  el('span', { class: 'g l' + (recSide === 'l' ? ' y' : '') }, c3ic(spec.left.icon)), el('span', { class: 'g r' + (recSide === 'r' ? ' y' : '') }, c3ic(spec.right.icon))));
809
850
  const xp = expandRow(inst, row, item.id, () => caseFile(inst, item, lens));
@@ -825,52 +866,105 @@ function itemRow(inst, item, lens) {
825
866
  return row;
826
867
  }
827
868
 
828
- /* ── Focus: up to three jobs for today, with the reason (PRD R4) ──────────────────────────────────────────────────── */
829
-
830
- function focusCard(inst) {
831
- const F = inst.data.focus || { suggestions: [], accepted: [] };
832
- const acc = F.accepted || []; const sug = (F.suggestions || []).slice(0, 3);
833
- const one = (f, pinned) => {
834
- const editing = inst.focEdit.has(f.key);
835
- const input = editing ? el('input', { type: 'text', 'aria-label': COPY.editTitle, 'data-pm-focus-input': f.key, autocomplete: 'off' }) : null;
836
- if (input) input.value = f.title;
837
- const save = () => focusAccept(inst, { ...f, title: (input && input.value.trim()) || f.title });
838
- const cancel = () => { inst.focEdit.delete(f.key); draw(inst); };
839
- const edit = () => { inst.focEdit.add(f.key); draw(inst); later(inst, () => { const i = inst.root.querySelector('[data-pm-focus-input="' + f.key + '"]'); if (i) i.focus(); }, 30); };
840
- if (input) input.addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.isComposing) save(); else if (e.key === 'Escape') cancel(); });
841
- const job = f.job && jobById(inst, f.job);
842
- const refs = (f.item_ids || []).map((id) => itemById(inst, id)).filter((i) => i && !i.done && i.title !== f.title); // a one-item job does not list itself
843
- const btns = editing ? [pill(COPY.save, 'check', { class: 'pill pri', 'data-pm-focus-save': f.key, onclick: save }), pill(COPY.cancel, null, { 'data-pm-focus-cancel': f.key, onclick: cancel })]
844
- : pinned ? [pill(COPY.editTitle, 'edit', { 'data-pm-focus-edit': f.key, onclick: edit }), pill(COPY.unpin, 'x', { 'data-pm-focus-clear': f.key, onclick: () => focusClear(inst, f) })]
845
- : [pill(COPY.accept, 'check', { class: 'pill pri', 'data-pm-focus-accept': f.key, onclick: () => focusAccept(inst, f) }), pill(COPY.editTitle, 'edit', { 'data-pm-focus-edit': f.key, onclick: edit }),
846
- pill(COPY.notToday, 'moon', { 'data-pm-focus-dismiss': f.key, onclick: () => focusDismiss(inst, f) })];
847
- return el('div', { class: 'wkfc' + (pinned ? ' on' : ''), 'data-pm-focus-card': f.key, 'data-pm-focus-state': pinned ? 'accepted' : 'suggested' },
848
- el('div', { class: 'chips2' }, el('span', { class: 'tag' + (pinned ? ' wk-on' : '') }, pinned ? COPY.focusPinned : COPY.focusSuggested),
849
- job ? el('span', { class: 'chip2' }, c3ic('target', 'sm'), job.mock_name || job.want) : null),
850
- editing ? el('span', { class: 'form' }, input) : el('b', { class: 'wkfc-t', 'data-pm-focus-title': f.key }, f.title),
851
- f.why ? el('p', { class: 'wkfc-why' }, COPY.why(f.why)) : null,
852
- refs.length ? el('ul', { class: 'wkfc-r', role: 'list' }, refs.map((i) => el('li', null, c3ic('next', 'sm'), el('span', null, i.title)))) : null,
853
- el('div', { class: 'wkq' }, btns));
869
+ /* ── Focus: the right-hand column of every view (console3-work-parity R2, R3; Flow today removed by console3-deck-flow-fixes) ── */
870
+
871
+ /* One Focus row (the mockup's focusHTML row): target icon, title, progress, the reason, the open items, and Accept / Edit / Not today. A row that
872
+ * has a job opens the job's sheet (R3): the whole row, or its job chip for the keyboard. */
873
+ function focusRow(inst, f, pinned) {
874
+ const editing = inst.focEdit.has(f.key);
875
+ const input = editing ? el('input', { type: 'text', 'aria-label': COPY.editTitle, 'data-pm-focus-input': f.key, autocomplete: 'off' }) : null;
876
+ if (input) input.value = f.title;
877
+ const save = () => focusAccept(inst, { ...f, title: (input && input.value.trim()) || f.title });
878
+ const cancel = () => { inst.focEdit.delete(f.key); draw(inst); };
879
+ const edit = () => { inst.focEdit.add(f.key); draw(inst); later(inst, () => { const i = inst.root.querySelector('[data-pm-focus-input="' + f.key + '"]'); if (i) i.focus(); }, 30); };
880
+ if (input) input.addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.isComposing) save(); else if (e.key === 'Escape') cancel(); });
881
+ const job = f.job && jobById(inst, f.job);
882
+ const refs = (f.item_ids || []).map((id) => itemById(inst, id)).filter((i) => i && !i.done && i.title !== f.title); // a one-item job does not list itself
883
+ const total = job ? job.total || 0 : (f.item_ids || []).length; const done = job ? job.done || 0 : 0;
884
+ const btns = editing ? [pill(COPY.save, null, { class: 'pill', 'data-pm-focus-save': f.key, onclick: save }), pill(COPY.cancel, null, { 'data-pm-focus-cancel': f.key, onclick: cancel })]
885
+ : pinned ? [pill(COPY.edit, null, { 'data-pm-focus-edit': f.key, onclick: edit }), pill(COPY.unpin, null, { 'data-pm-focus-clear': f.key, onclick: () => focusClear(inst, f) }),
886
+ pill(COPY.notToday, null, { 'data-pm-focus-dismiss': f.key, onclick: () => focusDismiss(inst, f) })]
887
+ : [pill(COPY.accept, 'check', { class: 'pill', 'data-pm-focus-accept': f.key, onclick: () => focusAccept(inst, f) }), pill(COPY.edit, null, { 'data-pm-focus-edit': f.key, onclick: edit }),
888
+ pill(COPY.notToday, null, { 'data-pm-focus-dismiss': f.key, onclick: () => focusDismiss(inst, f) })];
889
+ const pfill = el('i'); pfill.style.setProperty('--wl-w', (total ? Math.round((done / total) * 100) : 0) + '%');
890
+ const openJob = () => openJobSheet(inst, job);
891
+ const li = el('li', { class: 'nrow wkfr', 'data-pm-focus-card': f.key, 'data-pm-focus-state': pinned ? 'accepted' : 'suggested', 'data-row': '' },
892
+ el('span', { class: 'ki' + (pinned ? ' hot' : '') }, c3ic('target')),
893
+ el('div', { class: 'nb' },
894
+ editing ? el('span', { class: 'form' }, input) : el('span', { class: 'tt', title: f.why || '', 'data-pm-focus-title': f.key }, f.title),
895
+ el('div', { class: 'chips' }, el('div', { class: 'pbar wkpb' }, pfill),
896
+ el('span', { class: 'mono dim wk-s' }, COPY.progress(done, total)),
897
+ pinned ? el('span', { class: 'tag cal3-up wk-on' }, COPY.focusPinned) : null,
898
+ job ? pill(job.mock_name || job.want, 'target', { class: 'pill wkjob', title: COPY.jobSheet, 'data-pm-focus-job': job.id, onclick: (e) => { e.stopPropagation(); openJob(); } }) : null),
899
+ f.why ? el('div', { class: 'dim wk-s wkfc-why' }, COPY.why(f.why)) : null,
900
+ refs.length ? el('div', { class: 'chips' }, refs.map((i) => el('span', { class: 'chip2' }, i.title))) : null,
901
+ el('div', { class: 'chips2 wkq wkfr-b' }, btns)),
902
+ el('span'));
903
+ return li;
904
+ }
905
+
906
+ function focusSection(inst) {
907
+ const F = inst.data.focus || { suggestions: [], accepted: [], dismissed: [] };
908
+ const acc = F.accepted || []; const sug = (F.suggestions || []).slice(0, 3); const hidden = F.dismissed || [];
909
+ const n = acc.length + sug.length;
910
+ const restore = async () => {
911
+ for (const k of hidden) await press(inst, API + '/focus/clear', { key: k }, { quiet: true });
912
+ say({ text: COPY.broughtBack, timeout: 2500 });
913
+ await load(inst);
854
914
  };
915
+ const foot = hidden.length ? el('div', { class: 'fold', 'data-pm-focus-hidden': '' }, COPY.markedNot(hidden.length), ' ', pill(COPY.bringBack2, null, { 'data-pm-focus-restore': '', onclick: restore })) : null;
916
+ const add = c3ib(COPY.newJob, 'plus', { 'data-pm-job-add-toggle': '', onClick: () => openNewJob(inst) }, 'sm');
855
917
  return el('section', { class: 'card sect wkfoc', 'data-pm-focus': '' },
856
- el('div', { class: 'sh2' }, el('h2', null, c3ic('target', 'sm'), COPY.focus), el('span', { class: 'dim wk-s' }, COPY.focusSub)),
857
- acc.length || sug.length ? el('div', { class: 'wkfg' }, ...acc.map((f) => one(f, true)), ...sug.map((f) => one(f, false))) : el('div', { class: 'dim mono wk-s', 'data-pm-focus-none': '' }, COPY.focusNone));
918
+ el('div', { class: 'sh2' }, el('h2', null, c3ic('target', 'sm'), COPY.focus, ' ', el('span', { class: 'bdg g' }, String(n))), el('span', { class: 'r' }, add)),
919
+ n ? el('ul', { class: 'list', role: 'list' }, ...acc.map((f) => focusRow(inst, f, true)), ...sug.map((f) => focusRow(inst, f, false)))
920
+ : el('div', { class: 'fold', 'data-pm-focus-none': '' }, c3ic('check', 'sm'), COPY.focusNone),
921
+ foot);
858
922
  }
859
923
 
860
- /* ── Upcoming: the next 7 days by day, then Later (PRD R5) ─────────────────────────────────────────────────────────── */
924
+ /* ── Upcoming: Next 7 days by day, then a Later card (console3-work-parity R6) ──────────────────────────────────────── */
925
+
926
+ const legendEl = () => el('span', { class: 'fl2', 'data-pm-legend': '' }, COPY.legend.map((w, k) => el('span', null, ['• ', '× '][k] + w)));
861
927
 
862
928
  function upcomingMain(inst, list) {
863
929
  const today = inst.data.today;
864
- const block = (key, label, sub, its) => el('div', { class: 'wkday', 'data-pm-day': key },
865
- el('h3', null, label, sub ? el('span', { class: 'dim' }, sub) : null, its.length ? el('span', { class: 'bdg g' }, String(its.length)) : null),
866
- its.length ? el('ul', { class: 'list', role: 'list' }, its.map((i) => itemRow(inst, i, 'upcoming'))) : el('div', { class: 'wkday-none' }, 'Nothing'));
867
930
  const at = (i) => { const a = arrives(i, today); return a ? Math.max(1, daysBetween(today, a)) : 99; };
931
+ const near = list.filter((i) => at(i) <= 7); const far = list.filter((i) => at(i) > 7);
868
932
  const days = [];
869
- for (let n = 1; n <= 7; n++) { const d = addIso(today, n); days.push(block(d, n === 1 ? 'Tomorrow' : dayLabel(d).slice(0, 3), n === 1 ? dayLabel(d).slice(4) : dayLabel(d).slice(4), list.filter((i) => at(i) === n))); }
870
- days.push(block('later', 'Later', 'beyond 7 days', list.filter((i) => at(i) > 7)));
871
- const sec = sect(el('div', { class: 'sh2' }, el('h2', null, c3ic('cal', 'sm'), labelOf('upcoming'), ' ', el('span', { class: 'bdg g' }, String(list.length)))), el('div', { class: 'wkdays' }, days));
872
- sec.setAttribute('data-pm-list', 'upcoming');
873
- return sec;
933
+ for (let n = 1; n <= 7; n++) {
934
+ const d = addIso(today, n); const its = near.filter((i) => at(i) === n);
935
+ days.push(el('div', { class: 'wkday', 'data-pm-day': d },
936
+ el('h3', { class: 'wkdayh' }, n === 1 ? COPY.tomorrowHead + ' ' : null, el('span', { class: 'dim' }, dayLabel(d))),
937
+ its.length ? el('ul', { class: 'list', role: 'list' }, its.map((i) => itemRow(inst, i, 'upcoming'))) : el('div', { class: 'dim wkday-none' }, COPY.clear)));
938
+ }
939
+ const nextSec = sect(el('div', { class: 'sh2' }, el('h2', null, c3ic('cal', 'sm'), COPY.sect.next7, ' ', el('span', { class: 'bdg g' }, String(near.length))), legendEl()), el('div', { class: 'wkdays' }, days));
940
+ nextSec.setAttribute('data-pm-list', 'upcoming');
941
+ const laterSec = sect(el('div', { class: 'sh2' }, el('h2', null, c3ic('moon', 'sm'), COPY.sect.later, ' ', el('span', { class: 'bdg g' }, String(far.length))), el('span', { class: 'dim wk-s' }, COPY.beyond)),
942
+ far.length ? el('ul', { class: 'list', role: 'list' }, far.map((i) => itemRow(inst, i, 'upcoming'))) : el('ul', { class: 'list' }, el('li', { class: 'fold' }, c3ic('check', 'sm'), COPY.nothingLater)));
943
+ laterSec.setAttribute('data-pm-day', 'later');
944
+ return [nextSec, laterSec];
945
+ }
946
+
947
+ /* ── Today: Overdue, Due today and Flagged, each its own card (console3-work-parity R5) ─────────────────────────────── */
948
+
949
+ function todayMain(inst, list) {
950
+ const today = inst.data.today; const dayOf = (i) => (i.due ? String(i.due).slice(0, 10) : '');
951
+ const byDue = (a, b) => (dayOf(a) < dayOf(b) ? -1 : dayOf(a) > dayOf(b) ? 1 : String(a.time || '') < String(b.time || '') ? -1 : String(a.time || '') > String(b.time || '') ? 1 : 0);
952
+ const over = list.filter((i) => dayOf(i) && dayOf(i) < today).sort(byDue);
953
+ const due = list.filter((i) => dayOf(i) === today).sort(byDue);
954
+ const flag = list.filter((i) => !over.includes(i) && !due.includes(i));
955
+ if (!list.length) {
956
+ const sec = sect(el('div', { class: 'sh2' }, el('h2', null, c3ic('sun', 'sm'), labelOf('today'), ' ', el('span', { class: 'bdg g' }, '0'))), el('ul', { class: 'list' }, el('li', { class: 'fold' }, c3ic('check', 'sm'), COPY.emptyView.today)));
957
+ sec.setAttribute('data-pm-list', 'today');
958
+ return [sec];
959
+ }
960
+ let first = true;
961
+ return [['overdue', 'alert', COPY.sect.overdue, over], ['due', 'sun', COPY.sect.dueToday, due], ['flagged', 'flag', COPY.sect.flagged, flag]].filter((g) => g[3].length).map(([key, ic, title, its]) => {
962
+ const sec = sect(el('div', { class: 'sh2' }, el('h2', null, c3ic(ic, 'sm'), title, ' ', el('span', { class: 'bdg g' }, String(its.length))), first ? legendEl() : null),
963
+ el('ul', { class: 'list', role: 'list' }, its.map((i) => itemRow(inst, i, 'today', { noFlag: key === 'flagged' }))));
964
+ first = false;
965
+ sec.setAttribute('data-pm-list', 'today'); sec.setAttribute('data-pm-sect', key);
966
+ return sec;
967
+ });
874
968
  }
875
969
 
876
970
  /* ── Review: a weekly pass, one item at a time (PRD R7) ────────────────────────────────────────────────────────────── */
@@ -932,18 +1026,18 @@ function jobAll(inst, job) {
932
1026
  }
933
1027
 
934
1028
  /* The job's case file (mockup C3.cases.job, line 4660): the words, the count, Keep / Delete and move / Delete and leave, slide to confirm. */
935
- function jobCase(inst, j) {
1029
+ function jobCase(inst, j, close) {
936
1030
  const its = (inst.data.items || []).filter((i) => i.job === j.id);
937
1031
  const done = its.filter((i) => i.done).length;
938
1032
  const others = (inst.data.jobs || []).filter((q) => q.id !== j.id); const near = others[0];
939
1033
  const nm = (q) => q.mock_name || q.want;
940
- const del = (reassign) => async () => { const r = await press(inst, JOBS + '/delete', { id: j.id, ...(reassign ? { reassign_to: reassign } : {}) }); if (r) say({ text: COPY.reassigned(r.reassigned || 0), timeout: 4000 }); };
1034
+ const del = (reassign) => async () => { const r = await press(inst, JOBS + '/delete', { id: j.id, ...(reassign ? { reassign_to: reassign } : {}) }); if (r) { say({ text: COPY.reassigned(r.reassigned || 0), timeout: 4000 }); close(); } };
941
1035
  const model = { id: 'job:' + j.id, sit: COPY.jobLine(j), conf: 90, cta: 'Slide to keep it',
942
1036
  chg: [['target', done + ' of ' + its.length + ' done', 'count across every view', 'ok'], ['info', 'Change the words below. They save at once and you can undo for 7 seconds.', '']],
943
1037
  opts: [{ id: 'keep', l: 'Keep it', rec: 1, fx: [fx('check', 'Nothing changes', 'ok')] },
944
1038
  { id: 'mv', l: near ? 'Delete it and move its ' + its.length + ' item' + (its.length === 1 ? '' : 's') + ' to "' + nm(near) + '"' : 'Delete it', rec: 0, fx: [fx('trash', 'The job is gone', 'warn'), fx('target', its.length + ' items change job', 'warn')] },
945
1039
  { id: 'nj', l: 'Delete it and leave its items without a job', rec: 0, fx: [fx('trash', 'The job is gone', 'warn'), fx('alert', 'The items have no job', '')] }] };
946
- const run = { keep: () => say({ text: 'Kept.', timeout: 2500 }), mv: del(near && near.id), nj: del(null) };
1040
+ const run = { keep: () => { say({ text: 'Kept.', timeout: 2500 }); close(); }, mv: del(near && near.id), nj: del(null) };
947
1041
  const root = renderCase(model, { onConfirm: ({ option }) => { if (run[option.id]) run[option.id](); } });
948
1042
  root.setAttribute('data-pm-case', j.id);
949
1043
  root.querySelectorAll('.opt .rk').forEach((n) => { n.replaceChildren(c3ic('spark', 'sm'), 'Suggested'); n.removeAttribute('title'); }); // a job carries no confidence figure: never show a made-up percentage
@@ -954,9 +1048,9 @@ function jobCase(inst, j) {
954
1048
  }
955
1049
 
956
1050
  /* The "+ New job" draft (mockup c3jdraft, line 4678): one line in, the agent-style draft of the four fields out, then "Slide to create". */
957
- function jobNewCase(inst) {
958
- const x = inst.jobNew.line || 'keep the week calm';
959
- const d = inst.jobNew.draft;
1051
+ function jobNewCase(inst, st, close) {
1052
+ const x = st.line || 'keep the week calm';
1053
+ const d = st.draft;
960
1054
  const inputs = ['when', 'want', 'so'].map((k) => el('input', { type: 'text', 'aria-label': k === 'when' ? 'When' : k === 'want' ? 'I want' : 'So', 'data-pm-job-new': k, value: d[k], autocomplete: 'off' }));
961
1055
  const val = (k) => inputs[['when', 'want', 'so'].indexOf(k)].value.trim();
962
1056
  const demo = DEMO(); // demo: the mockup's draft also names the job (its "Job" field); the server has no name field
@@ -965,53 +1059,48 @@ function jobNewCase(inst) {
965
1059
  chg: [['spark', 'Drafted by the agent', 'assumption', 'warn']],
966
1060
  opts: [{ id: 'mk', l: 'Create the job', rec: 1, fx: [fx('target', 'Items can now be filed under it', 'ok')] }, { id: 'no', l: 'Not now', rec: 0, fx: [] }] };
967
1061
  const root = renderCase(model, { onConfirm: async ({ option }) => {
968
- if (option.id === 'no') { inst.jobNew = null; say({ text: 'Dropped the draft.', timeout: 2500 }); draw(inst); return; }
1062
+ if (option.id === 'no') { say({ text: 'Dropped the draft.', timeout: 2500 }); close(); return; }
969
1063
  const line = 'when ' + val('when') + ', I want ' + val('want') + ', so ' + val('so');
970
1064
  const r = await press(inst, JOBS + '/add', nameIn ? { line, name: nameIn.value.trim() } : { line });
971
- if (r) { inst.jobNew = null; say({ text: 'Job created. Add work to it by saying which job it serves.', timeout: 3000 }); await load(inst); }
1065
+ if (r) { say({ text: 'Job created. Add work to it by saying which job it serves.', timeout: 3000 }); close(); await load(inst); }
972
1066
  } });
973
1067
  if (!demo) {
974
1068
  root.querySelectorAll('.opt .rk').forEach((n) => { n.replaceChildren(c3ic('spark', 'sm'), 'Suggested'); n.removeAttribute('title'); });
975
1069
  root.querySelectorAll('.opt .conf').forEach((n) => n.remove());
976
1070
  }
977
1071
  root.querySelector('.sit').after(el('div', { class: 'c3jf' }, nameIn ? field('Job', nameIn) : null, ...['when', 'want', 'so'].map((k, i) => field(k === 'when' ? 'When' : k === 'want' ? 'I want' : 'So', inputs[i]))));
978
- inst.cases.add(root);
979
1072
  return root;
980
1073
  }
981
1074
 
982
- /* The mockup's jobsCard() (line 4667): section.card.sect, .sh2 with the title and the + at the right, a list of .nrow with a .pbar and a case file. */
983
- function jobsCard(inst) {
984
- const js = inst.data.jobs || [];
985
- const rows = js.map((j) => {
986
- const pct = j.total ? Math.round((j.done / j.total) * 100) : 0;
987
- const row = el('li', { class: 'nrow', 'data-row': '', 'data-pm-job': j.id },
988
- el('span', { class: 'ki' }, c3ic('target')),
989
- el('div', { class: 'nb' }, el('span', { class: 'tt', title: COPY.jobLine(j) }, j.mock_name || j.want.charAt(0).toUpperCase() + j.want.slice(1)),
990
- el('div', { class: 'chips' }, el('div', { class: 'pbar', style: 'width:90px' }, el('i', { style: 'width:' + pct + '%' })),
991
- el('span', { class: 'mono dim', style: small }, COPY.progress(j.done, j.total)))));
992
- row.querySelector('.nb').append(expandRow(inst, row, j.id, () => jobCase(inst, j)));
993
- return row;
994
- });
995
- const lines = [];
996
- if (inst.jobNew) {
997
- if (inst.jobNew.draft) lines.push(el('li', { class: 'c3jnew' }, jobNewCase(inst)));
998
- else {
999
- const input = el('input', { type: 'text', placeholder: 'What are you trying to get done? One line', 'aria-label': 'New job', autocomplete: 'off', 'data-pm-add': JOBS + '/add' });
1000
- const go = () => { const x = (input.value || '').trim() || 'keep the week calm'; inst.jobNew = { line: x, draft: { when: 'it comes up', want: 'a clear way to ' + x.toLowerCase().replace(/^to /, '').replace(/\.$/, ''), so: 'the outcome is not left to chance' } }; draw(inst); later(inst, () => { const i = inst.root.querySelector('[data-pm-job-new="name"], [data-pm-job-new="when"]'); if (i) i.focus(); }, 30); };
1001
- input.addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.isComposing) go(); });
1002
- lines.push(el('li', { class: 'c3jnew' }, el('div', { class: 'form' }, input, c3ib('Draft it', 'spark', { 'data-pm-job-draft': '', onClick: go }, 'pri'), c3ib('Cancel', 'x', { 'data-pm-job-cancel': '', onClick: () => { inst.jobNew = null; draw(inst); } }, 'sm'))));
1003
- }
1004
- }
1005
- const plus = c3ib('New job', 'plus', { 'aria-expanded': String(!!inst.jobNew), 'data-pm-job-add-toggle': '', onClick: () => { inst.jobNew = inst.jobNew ? null : {}; draw(inst); later(inst, () => { const i = inst.root.querySelector('[aria-label="New job"]'); if (i) i.focus(); }, 30); } }, 'sm');
1006
- return el('section', { class: 'card sect', 'data-pm-jobs': '' },
1007
- el('div', { class: 'sh2' }, el('h2', null, c3ic('target', 'sm'), COPY.jobs), el('span', { class: 'r' }, plus)), // no count: the mockup's title has none
1008
- js.length || lines.length ? el('ul', { class: 'list', role: 'list' }, ...lines, ...rows) : el('div', { class: 'dim mono', style: small }, COPY.noJobs),
1009
- el('div', { class: 'dim', style: small }, COPY.jobsNote));
1075
+ /* The job's sheet (console3-work-parity R3): a Focus row opens it. The case file lives in the sheet, so a redraw of the page does not dispose it. */
1076
+ function openJobSheet(inst, job) {
1077
+ let h = null;
1078
+ const body = jobCase(inst, job, () => h && h.close());
1079
+ h = openSheet({ title: COPY.jobSheet, icon: 'target', body, onClose: () => { try { body.c3case.dispose(); } catch { /* already gone */ } } });
1080
+ }
1081
+
1082
+ /* The New job button of the Focus section: one line in, the draft of the three fields out, then "Slide to create", all in a sheet. */
1083
+ function openNewJob(inst) {
1084
+ let h = null; let case2 = null;
1085
+ const holder = el('div', { 'data-pm-job-new-sheet': '' });
1086
+ const close = () => h && h.close();
1087
+ const input = el('input', { type: 'text', placeholder: 'What are you trying to get done? One line', 'aria-label': 'New job', autocomplete: 'off', 'data-pm-add': JOBS + '/add' });
1088
+ const go = () => {
1089
+ const x = (input.value || '').trim() || 'keep the week calm';
1090
+ const st = { line: x, draft: { when: 'it comes up', want: 'a clear way to ' + x.toLowerCase().replace(/^to /, '').replace(/\.$/, ''), so: 'the outcome is not left to chance' } };
1091
+ case2 = jobNewCase(inst, st, close);
1092
+ holder.replaceChildren(case2);
1093
+ later(inst, () => { const i = holder.querySelector('[data-pm-job-new="name"], [data-pm-job-new="when"]'); if (i) i.focus(); }, 30);
1094
+ };
1095
+ input.addEventListener('keydown', (e) => { if (e.key === 'Enter' && !e.isComposing) go(); });
1096
+ holder.append(el('div', { class: 'form' }, input, c3ib('Draft it', 'spark', { 'data-pm-job-draft': '', onClick: go }, 'pri')));
1097
+ h = openSheet({ title: COPY.newJob, icon: 'target', body: holder, onClose: () => { try { if (case2) case2.c3case.dispose(); } catch { /* already gone */ } } });
1098
+ later(inst, () => input.focus(), 30);
1010
1099
  }
1011
1100
 
1012
1101
  /* ── the page ────────────────────────────────────────────────────────────────────────────────────────────────────── */
1013
1102
 
1014
- /* The mockup's workHead() calbar: the .seg view bar with a count on every view, and the group toggle as an .ib (line 3281). */
1103
+ /* The mockup's workHead() calbar: the .seg view bar with a count on every view (line 3281). */
1015
1104
  function lensBar(inst) {
1016
1105
  const seg = el('div', { class: 'seg', role: 'group', 'aria-label': 'Work view', 'data-pm-lenses': '' },
1017
1106
  LENSES.map(([id, label]) => {
@@ -1023,16 +1112,7 @@ function lensBar(inst) {
1023
1112
  badge.setAttribute('data-pm-count', id);
1024
1113
  return b;
1025
1114
  }));
1026
- const grp = c3ib(inst.group ? COPY.groupTime : COPY.groupJob, 'target', { 'aria-pressed': String(inst.group), 'data-pm-group': '', disabled: inst.lens === 'review' }, 'sm');
1027
- grp.addEventListener('click', () => { inst.group = !inst.group; draw(inst); });
1028
- return [seg, el('div', { class: 'chips2' }, grp)];
1029
- }
1030
-
1031
- /* The ring at the right of Today's header: done today of done today plus what is still in Today. */
1032
- function todayRing(inst) {
1033
- const d = inst.data;
1034
- const doneToday = (d.items || []).filter((i) => i.done && String(i.done_at || '').slice(0, 10) === d.today).length;
1035
- return c3ring(doneToday, doneToday + shown(inst, 'today').length, 44);
1115
+ return [seg];
1036
1116
  }
1037
1117
 
1038
1118
  /* A .card.sect with an .sh2 title row and a ul.list of rows (line 3262). */
@@ -1040,6 +1120,18 @@ function sect(head, rows) { return el('section', { class: 'card sect' }, head, r
1040
1120
 
1041
1121
  const FOCUS_ATTRS = ['data-pm-edit', 'data-pm-step-add', 'data-pm-job-edit', 'data-pm-agent-reply', 'data-pm-job-new', 'data-pm-outcome', 'data-pm-wait-on', 'data-pm-focus-input'];
1042
1122
 
1123
+ /* What the briefing tells this page: how many decisions wait in Briefing (the header says so), and the drafts section that Review carries (decision 1). */
1124
+ function briefRead(inst, b) {
1125
+ const decide = ((b && b.cards) || []).filter((c) => c && (c.move || c.tag === 'Critical'));
1126
+ const n = b && Array.isArray(b.needs) && b.needs.length ? b.needs.length : decide.length;
1127
+ const rv = b && b.review ? b.review : null;
1128
+ const sig = JSON.stringify([n, rv]);
1129
+ if (sig === inst.briefSig) return;
1130
+ inst.briefSig = sig; inst.decisions = n;
1131
+ inst.draftsHost.replaceChildren(...(rv ? [reviewSection(rv)] : []));
1132
+ if (inst.data && insts.get(inst.host) === inst) draw(inst);
1133
+ }
1134
+
1043
1135
  function draw(inst) {
1044
1136
  if (inst.lens !== inst.prevLens) { if (inst.lens === 'review') inst.rv.ids = null; inst.prevLens = inst.lens; } // a fresh Review pass each time the view is opened
1045
1137
  /* keep the focus: a field the PM is typing in, or the row they were on, is found again after the redraw */
@@ -1055,33 +1147,28 @@ function draw(inst) {
1055
1147
  if (!d) { inst.body.replaceChildren(el('p', { class: 'dim mono', style: small }, COPY.loading)); return; }
1056
1148
  const lens = inst.lens;
1057
1149
  const list = lens === 'review' ? [] : shown(inst, lens);
1150
+ const go = inst.ctx.go;
1058
1151
  const lh = el('div', { class: 'lh', 'data-pm-lens-head': lens },
1059
1152
  el('span', { class: 'ki' }, c3ic(WLI[lens])),
1060
- el('div', { class: 'wk-min' }, el('b', null, COPY.lens[lens](countOf(inst, lens))), el('div', { class: 'dim' }, COPY.lensSub[lens])),
1061
- lens === 'today' ? todayRing(inst) : null);
1153
+ el('div', { class: 'wk-min' }, el('b', null, COPY.lens[lens]),
1154
+ el('div', { class: 'dim' }, COPY.lensSub[lens] + (inst.decisions ? COPY.decisions(inst.decisions) : ''),
1155
+ inst.decisions && go ? [' ', pill(COPY.openBriefing, 'next', { 'data-pm-open-briefing': '', onclick: () => go('brief') })] : null)));
1062
1156
  const empty = el('li', { class: 'fold' }, c3ic('check', 'sm'), COPY.emptyView[lens] || COPY.empty);
1063
1157
  let main;
1064
- if (lens === 'review') main = [reviewMain(inst)];
1065
- else if (inst.group) {
1066
- const groups = (d.jobs || []).map((j) => [j, list.filter((i) => i.job === j.id)])
1067
- .concat([[null, list.filter((i) => !i.job || !jobById(inst, i.job))]]).filter((g) => g[1].length);
1068
- main = groups.length ? groups.map(([j, its]) => {
1069
- const sec = sect(el('div', { class: 'sh2' }, el('h2', null, c3ic('target', 'sm'), j ? (j.mock_name || j.want) : COPY.noJobGroup),
1070
- j ? el('span', { class: 'chips2 dim mono', style: small }, COPY.progress(j.done, j.total)) : null),
1071
- el('ul', { class: 'list', role: 'list' }, its.map((i) => itemRow(inst, i, lens))));
1072
- sec.setAttribute('data-pm-group-card', j ? j.id : '');
1073
- return sec;
1074
- }) : [sect(el('div', { class: 'sh2' }, el('h2', null, c3ic(WLI[lens], 'sm'), labelOf(lens))), el('ul', { class: 'list' }, empty))];
1075
- } else if (lens === 'upcoming') main = [upcomingMain(inst, list)];
1158
+ if (lens === 'review') main = [reviewMain(inst), inst.draftsHost];
1159
+ else if (lens === 'upcoming') main = upcomingMain(inst, list);
1160
+ else if (lens === 'today') main = todayMain(inst, list);
1076
1161
  else {
1077
- const sec = sect(el('div', { class: 'sh2' }, el('h2', null, c3ic(WLI[lens], 'sm'), labelOf(lens), ' ', el('span', { class: 'bdg g' }, String(list.length))),
1078
- el('span', { class: 'fl2', 'data-pm-legend': '' }, COPY.legend.map((w, k) => el('span', null, ['• ', '× ', '> ', '< '][k] + w)))),
1162
+ const sec = sect(el('div', { class: 'sh2' }, el('h2', null, c3ic(WLI[lens], 'sm'), COPY.sect[lens], ' ', el('span', { class: 'bdg g' }, String(list.length))), legendEl()),
1079
1163
  el('ul', { class: 'list', role: 'list' }, list.length ? list.map((i) => itemRow(inst, i, lens)) : empty));
1080
1164
  sec.setAttribute('data-pm-list', lens);
1081
1165
  main = [sec];
1166
+ if (lens === 'inbox') {
1167
+ const to = go ? pill(COPY.initiatives, 'next', { 'data-pm-go-init': '', onclick: () => go('init') }) : COPY.initiatives;
1168
+ main.push(el('div', { class: 'fold', 'data-pm-inbox-note': '' }, COPY.inboxNote + ' ', to, COPY.inboxNoteEnd));
1169
+ }
1082
1170
  }
1083
- if (lens === 'today') main.unshift(focusCard(inst));
1084
- /* The mockup's vWork() body: .calbody > .lh + .brief.sbb > .bcols > .bcol.l (the list) and .bcol.r (the jobs card). */
1085
- inst.body.replaceChildren(lh, el('div', { class: 'brief sbb' }, el('div', { class: 'bcols' }, el('div', { class: 'bcol l' }, inst.ctx.plan || null, ...main), el('div', { class: 'bcol r' }, jobsCard(inst)))));
1171
+ /* The mockup's vWork() body: .calbody > .lh + .brief.sbb > .bcols > .bcol.l (the view) and .bcol.r (Focus). The PM removed the mockup's Flow today card (console3-deck-flow-fixes). */
1172
+ inst.body.replaceChildren(lh, el('div', { class: 'brief sbb' }, el('div', { class: 'bcols' }, el('div', { class: 'bcol l' }, ...main), el('div', { class: 'bcol r' }, focusSection(inst)))));
1086
1173
  if (keep) { const n = inst.root.querySelector(keep.sel); if (n && n.focus) { try { n.focus({ preventScroll: true }); } catch { /* not focusable */ } } }
1087
1174
  }