create-pmos 0.123.0 → 0.125.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 (127) hide show
  1. package/package.json +1 -1
  2. package/payload/KIT-VERSION +2 -2
  3. package/payload/planning/evals/eval-calibration.md +1 -1
  4. package/payload/planning/okrs/health-budgets.md +1 -1
  5. package/payload/scripts/nine-stages.js +81 -0
  6. package/payload/scripts/workflow-state.js +2 -0
  7. package/payload/state/discoveries.md +1 -1
  8. package/payload/state/log.md +1 -1
  9. package/workspace/c3-one-home-test.js +131 -0
  10. package/workspace/fixtures/initiatives-api/list.json +171 -3
  11. package/workspace/fixtures/initiatives-api/roadmap.json +228 -4
  12. package/workspace/fixtures/initiatives-api/sheet.json +57 -1
  13. package/workspace/fixtures/one-home-baseline.json +83 -0
  14. package/workspace/guide/layout.md +2 -0
  15. package/workspace/guide/notes.md +1 -0
  16. package/workspace/lib/engine/nine-stages.js +81 -0
  17. package/workspace/lib/engine/workflow-state.js +2 -0
  18. package/workspace/lib/facts-async.js +51 -0
  19. package/workspace/lib/item-steps.js +52 -0
  20. package/workspace/lib/jira-status.js +8 -3
  21. package/workspace/lib/jobs-to-be-done.js +52 -0
  22. package/workspace/lib/outbox-store.js +87 -0
  23. package/workspace/lib/pm-items.js +115 -0
  24. package/workspace/lib/route-modules.js +21 -0
  25. package/workspace/lib/routes-briefing.js +100 -0
  26. package/workspace/lib/routes-c3-init.js +53 -0
  27. package/workspace/lib/routes-c3-life.js +119 -0
  28. package/workspace/lib/routes-c3-strategy.js +169 -0
  29. package/workspace/lib/routes-c3-timeline.js +191 -0
  30. package/workspace/lib/routes-calendar-import.js +60 -0
  31. package/workspace/lib/routes-calendar-views.js +61 -0
  32. package/workspace/lib/routes-doc-health.js +79 -0
  33. package/workspace/lib/routes-doc-sheet.js +169 -0
  34. package/workspace/lib/routes-forecast.js +85 -0
  35. package/workspace/lib/routes-hooks.js +121 -0
  36. package/workspace/lib/routes-initiatives.js +3 -0
  37. package/workspace/lib/routes-intake.js +24 -0
  38. package/workspace/lib/routes-outbox.js +97 -0
  39. package/workspace/lib/routes-pm-items.js +216 -0
  40. package/workspace/lib/routes-records.js +61 -0
  41. package/workspace/lib/routes-releases.js +129 -0
  42. package/workspace/lib/routes-report-facts.js +84 -0
  43. package/workspace/lib/routes-report-families.js +32 -0
  44. package/workspace/lib/routes-roadmap-ops.js +141 -0
  45. package/workspace/lib/routes-runs.js +119 -0
  46. package/workspace/lib/routes-signals.js +50 -0
  47. package/workspace/lib/run-answers.js +69 -0
  48. package/workspace/package-lock.json +2 -2
  49. package/workspace/package.json +1 -1
  50. package/workspace/public/c3.html +26 -0
  51. package/workspace/public/pm/briefing-deck.js +415 -0
  52. package/workspace/public/pm/briefing-model.js +176 -0
  53. package/workspace/public/pm/c3-breakdown.js +240 -0
  54. package/workspace/public/pm/c3-brief.js +278 -0
  55. package/workspace/public/pm/c3-cal-strategy.js +528 -0
  56. package/workspace/public/pm/c3-case.js +173 -0
  57. package/workspace/public/pm/c3-demo-brief-ix.js +350 -0
  58. package/workspace/public/pm/c3-demo-cal-ix.js +74 -0
  59. package/workspace/public/pm/c3-demo-cal2.js +66 -0
  60. package/workspace/public/pm/c3-demo-cal3.js +173 -0
  61. package/workspace/public/pm/c3-demo-data.js +560 -0
  62. package/workspace/public/pm/c3-demo-init.js +373 -0
  63. package/workspace/public/pm/c3-demo-life.js +77 -0
  64. package/workspace/public/pm/c3-demo-misc2.js +46 -0
  65. package/workspace/public/pm/c3-demo-plan2.js +30 -0
  66. package/workspace/public/pm/c3-demo-rep-ix.js +126 -0
  67. package/workspace/public/pm/c3-demo-rep2.js +92 -0
  68. package/workspace/public/pm/c3-demo-term.js +153 -0
  69. package/workspace/public/pm/c3-demo-time.js +49 -0
  70. package/workspace/public/pm/c3-demo-work-ix.js +80 -0
  71. package/workspace/public/pm/c3-demo-work2.js +13 -0
  72. package/workspace/public/pm/c3-demo.js +113 -0
  73. package/workspace/public/pm/c3-doc-sheet.js +314 -0
  74. package/workspace/public/pm/c3-dock.js +180 -0
  75. package/workspace/public/pm/c3-docs.js +290 -0
  76. package/workspace/public/pm/c3-expand.js +96 -0
  77. package/workspace/public/pm/c3-init-extras.js +170 -0
  78. package/workspace/public/pm/c3-lifecycle-panels.js +476 -0
  79. package/workspace/public/pm/c3-misc-panels.js +89 -0
  80. package/workspace/public/pm/c3-plan-panels.js +163 -0
  81. package/workspace/public/pm/c3-plan.js +192 -0
  82. package/workspace/public/pm/c3-roadmap.js +249 -0
  83. package/workspace/public/pm/c3-sheet.js +107 -0
  84. package/workspace/public/pm/c3-shell.js +637 -0
  85. package/workspace/public/pm/c3-signals.js +196 -0
  86. package/workspace/public/pm/c3-strategy-panels.js +45 -0
  87. package/workspace/public/pm/c3-swipe.js +138 -0
  88. package/workspace/public/pm/c3-terminal-data.js +26 -0
  89. package/workspace/public/pm/c3-terminal-edge.js +91 -0
  90. package/workspace/public/pm/c3-terminal-icons.js +41 -0
  91. package/workspace/public/pm/c3-terminal-results.js +141 -0
  92. package/workspace/public/pm/c3-terminal.js +52 -0
  93. package/workspace/public/pm/c3-tickets.js +163 -0
  94. package/workspace/public/pm/c3-timeline.js +201 -0
  95. package/workspace/public/pm/c3-toast.js +16 -0
  96. package/workspace/public/pm/calendar-views.js +1015 -0
  97. package/workspace/public/pm/console3-lift.md +220 -0
  98. package/workspace/public/pm/console3.css +1480 -0
  99. package/workspace/public/pm/dest-initiatives.js +16 -3
  100. package/workspace/public/pm/dest-mywork.js +8 -1
  101. package/workspace/public/pm/dest-notes.js +25 -3
  102. package/workspace/public/pm/dest-reports.js +47 -1
  103. package/workspace/public/pm/dest-today.js +55 -1
  104. package/workspace/public/pm/doc-health-panel.js +110 -0
  105. package/workspace/public/pm/doc-rail.js +9 -3
  106. package/workspace/public/pm/lifecycle-lens-model.js +228 -0
  107. package/workspace/public/pm/lifecycle-lens.js +261 -0
  108. package/workspace/public/pm/new-initiative-sheet.js +116 -21
  109. package/workspace/public/pm/outbox-panel.js +125 -0
  110. package/workspace/public/pm/pm-work-view.js +919 -0
  111. package/workspace/public/pm/releases-view.js +248 -0
  112. package/workspace/public/pm/report-families-view.js +503 -0
  113. package/workspace/public/pm/run-card.js +244 -0
  114. package/workspace/public/pm/sessions-panel.js +149 -0
  115. package/workspace/public/pm/shell-bell.js +162 -0
  116. package/workspace/public/pm/shell-model.js +83 -0
  117. package/workspace/public/pm/shell-phone.js +124 -0
  118. package/workspace/public/pm/shell-readaloud.js +53 -0
  119. package/workspace/public/pm/shell-recording.js +110 -0
  120. package/workspace/public/pm/shell-ticker.js +90 -0
  121. package/workspace/public/pm/slide-confirm.js +163 -0
  122. package/workspace/public/pm/timeline-forecast-view.js +164 -0
  123. package/workspace/public/pm/today-day.js +8 -2
  124. package/workspace/public/pm/tokens.css +1 -0
  125. package/workspace/server.js +5 -2
  126. package/workspace/test-files.json +8 -0
  127. package/workspace/test-lists/browser-suites-green.json +5 -0
@@ -0,0 +1,74 @@
1
+ /* pm/c3-demo-cal-ix.js — DEMO fixtures for the meeting card, the Before / During / After stage and the initiative case files of the Calendar
2
+ * and the Briefing's middle column on /c3.html?demo=1. The content is the console3 mockup's own (console3.html: MW line 2364, MEETC line 2580,
3
+ * prepPack line 1563, the pulse rows line 1448). c3-demo.js imports this file for its side effect: it sets window.__c3calix, which
4
+ * calendar-views.js reads when it is present. Real mode never loads this file, so a real calendar shows only what the calendar memory holds.
5
+ * Keys are the base event id (m1 to m5; a demo event id is `m4@2026-10-08`) and the initiative ids of the calendar's gantt rows.
6
+ * Nothing here reaches the server. `outward: true` on an option means it would tell a person: the slide stays disabled (no channel connected). */
7
+
8
+ const fx = (icon, text, tone) => ({ icon, text, tone: tone || '' });
9
+
10
+ const MEET = {
11
+ m1: { init: 'Search relevance', att: ['S', 'D'], prep: 'ready', line: 'Routine. Keeps Search relevance unblocked.', why: 'Routine. The async summary covers it, so skipping gives back 30 minutes.',
12
+ prepRows: [['spark', 'Agenda', 'Proposed: blockers · next steps', true], ['clock', 'Changed since last time', 'EN-418 still blocked on platform', true], ['scale', 'Leave with', 'No decision needed', true], ['doc', 'Read first', 'Nothing new', true], ['user', 'People', 'Sam, Dara', true], ['out', 'Pre-read to attendees', 'Drafted. Goes out only when you send it.', true]],
13
+ case: { id: 'meet:m1', tc: 'var(--success)', sit: 'Standup is routine this week. Sam posts an async summary at 09:25 and nothing in it needs you.', conf: 71, cta: 'Slide to skip',
14
+ chg: [['check', 'The summary covers the 3 open tickets', 'Slack', 'ok'], ['clock', 'Gives back 30 minutes of focus time', 'Calendar', 'ok']],
15
+ opts: [{ id: 'skip', l: 'Skip and read the summary', rec: true, msg: 'Skipped. The summary lands in your list at 09:25.', fx: [['clock', 'Back 30 minutes', 'ok']] }, { id: 'att', l: 'Attend as usual', msg: 'Kept.', fx: [['user', 'You can answer EN-418 live']] }], unsure: 'Sam may raise the EN-418 block live.' } },
16
+ m2: { init: 'Checkout v2', att: ['D', 'L'], prep: 'ready', line: 'Checkout v2: 2 PRD comments and 3 blockers to clear before the 14:00 decision.', why: 'PRD comments fell from 5 to 2 since yesterday. It feeds the 14:00 decision.',
17
+ prepRows: [['spark', 'Agenda', 'Proposed: Scope cut · blockers · next steps', true], ['clock', 'Changed since last time', 'PRD comments 5 to 2 · Conversion down 0.2 pts · New blocker: a11y slot', true], ['scale', 'Leave with', 'Scope cut', true],
18
+ ['doc', 'Read first', 'checkout-v2-prd.md · customer-call-oct6.pdf · whiteboard-flow.jpg · forecast-q4.xlsx', true], ['user', 'People', 'Dara owes 1 · Lee owes 1', true], ['out', 'Pre-read to attendees', 'Drafted. Goes out only when you send it.', false]],
19
+ case: { id: 'meet:m2', tc: 'var(--acc)', sit: 'Prep is ready. Two PRD comments and three blockers are the agenda, and the scope cut is the ask.', conf: 84, cta: 'Slide to confirm',
20
+ chg: [['check', 'Agenda and pre-read are ready', 'Agent', 'ok'], ['clock', 'PRD comments fell from 5 to 2', 'Confluence'], ['scale', 'The ask: cut to 3 flows', 'your call']],
21
+ opts: [{ id: 'att', l: 'Attend and lead with the cut', rec: true, msg: 'Kept. The pre-read goes out when you send it.', fx: [['check', 'Gets the 14:00 decision early', 'ok']] },
22
+ { id: 'short', l: 'Shorten to 15 minutes', outward: true, msg: 'Shortened to 15 minutes.', fx: [['clock', 'Saves 15 minutes', 'ok'], ['alert', 'Drops the blocker review', 'warn']] },
23
+ { id: 'written', l: 'Send Dara the ask in writing', outward: true, msg: 'Skipped.', fx: [['clock', 'Saves 45 minutes', 'ok'], ['alert', 'Loses the live debate', 'warn']] }] } },
24
+ m3: { init: 'Returns experience', att: ['M', 'L'], prep: 'review', line: 'Returns discovery: interview 3 of 5. Question 6 needs your wording.', why: 'Interview 3 of 5 tests the label hypothesis. Question 6 needs your wording.',
25
+ prepRows: [['spark', 'Agenda', 'Proposed: interview guide · question 6', true], ['clock', 'Changed since last time', '2 of 3 calls mention the support route', true], ['scale', 'Leave with', 'Park or continue Returns', true],
26
+ ['doc', 'Read first', 'interview-guide.md', true], ['user', 'People', 'Marta confirmed the slot', true], ['out', 'Pre-read to attendees', 'Drafted. Goes out only when you send it.', false]],
27
+ case: { id: 'meet:m3', tc: 'var(--purple)', sit: 'The only meeting that tests a hypothesis. The guide is drafted, and Question 6 needs your wording before 13:00.', conf: 88, cta: 'Slide to confirm',
28
+ chg: [['notes', '2 of 3 calls mention the support route', 'Notes'], ['eye', 'The guide needs your look', 'Review', 'warn'], ['user', 'Marta confirmed the slot', 'Gmail', 'ok']],
29
+ opts: [{ id: 'att', l: 'Attend and approve the guide first', rec: true, msg: 'Kept. Guide review is first in your list.', fx: [['check', 'Tests the third hypothesis', 'ok']] },
30
+ { id: 'move', l: 'Move to tomorrow', outward: true, msg: 'Move requested.', fx: [['alert', 'Loses the 5-interview window', 'bad']] }] } },
31
+ m4: { init: 'Checkout v2', att: ['D', 'L', 'S'], prep: 'blocked', line: 'Checkout v2: the sprint cannot start until scope is decided. The 24 Oct date depends on it.', why: 'The sprint cannot start until scope is decided. The 24 Oct date depends on it.',
32
+ prepRows: [['spark', 'Agenda', 'Proposed: Scope cut · blockers · next steps', true], ['clock', 'Changed since last time', 'PRD comments 5 to 2 · Conversion down 0.2 pts · New blocker: a11y slot', true], ['scale', 'Leave with', 'Scope cut', true],
33
+ ['doc', 'Read first', 'checkout-v2-prd.md · customer-call-oct6.pdf · whiteboard-flow.jpg · forecast-q4.xlsx', true], ['user', 'People', 'Dara owes 1 · Lee owes 1', true], ['out', 'Pre-read to attendees', 'Drafted. Goes out only when you send it.', false]],
34
+ case: { id: 'meet:m4', tc: 'var(--destructive)', sit: 'Blocked on your scope decision. If it slips past 14:00, the sprint plans the wrong scope.', conf: 78, cta: 'Slide to keep the slot',
35
+ chg: [['alert', 'Waiting on your scope decision', 'Decision', 'bad'], ['user', 'Dara, Lee and Sam are invited', 'Calendar'], ['clock', 'You are free again at 15:30', 'Calendar']],
36
+ opts: [{ id: 'att', l: 'Keep 14:00 and decide before', rec: true, msg: 'Kept. The decision is first in your list.', fx: [['check', 'Planning starts on time', 'ok']] },
37
+ { id: 'move', l: 'Move to 15:30 if undecided', outward: true, msg: 'Move to 15:30 queued if you have not decided by 13:30.', fx: [['clock', '90 more minutes to decide', 'warn']] }], ign: 'The sprint starts without scope.' } },
38
+ m5: { init: 'Checkout v2', att: ['P', 'D'], prep: 'ready', line: 'You report the date to leadership. The status draft waits for the decision.', why: 'You report the date to leadership. SSO is a milestone worth leading with.',
39
+ prepRows: [['spark', 'Agenda', 'Proposed: date · SSO · risks', true], ['clock', 'Changed since last time', 'SSO beta live with 3 customers', true], ['scale', 'Leave with', 'The date, said plainly', true], ['doc', 'Read first', 'status-draft.md', true], ['user', 'People', 'Priya', true], ['out', 'Pre-read to attendees', 'Drafted. Goes out only when you send it.', false]],
40
+ case: { id: 'meet:m5', tc: 'var(--success)', sit: 'You report the date to leadership. The status draft waits for the scope decision, and SSO is worth leading with.', conf: 80, cta: 'Slide to confirm',
41
+ chg: [['report', 'The status draft is in Review', 'Review', 'warn'], ['flag', 'SSO beta live with 3 customers', 'Jira', 'ok'], ['alert', '24 Oct is conditional until 14:00', 'Decision', 'warn']],
42
+ opts: [{ id: 'att', l: 'Attend and lead with SSO', rec: true, msg: 'Kept. SSO leads the status draft.', fx: [['check', 'Opens with the win', 'ok']] },
43
+ { id: 'written', l: 'Send the update in writing', outward: true, msg: 'Skipped.', fx: [['clock', 'Saves 30 minutes', 'ok'], ['alert', 'No live questions', 'warn']] }] } },
44
+ };
45
+
46
+ /* the initiative case files behind a pulse row, a gantt row or a dot (the mockup opens the initiative's detail; here it is a sheet) */
47
+ const INIT = {
48
+ checkout: { tc: 'var(--destructive)', sit: 'Forecast Oct 31 against promised Oct 24: 7 days late on the full scope. Dara needs the scope decision before Sprint planning at 14:00.', conf: 82, cta: 'Slide to decide',
49
+ chg: [['alert', 'Forecast Oct 31, promised Oct 24 (+7 days)', 'Forecast', 'bad'], ['alert', '3 blockers on the full scope', 'Jira', 'warn'], ['clock', 'Sprint planning is at 14:00', 'Calendar']],
50
+ opts: [{ id: 'cut', l: 'Cut to 3 flows and hold 24 Oct', rec: true, msg: 'Decision logged. Follow-up drafts are in Review.', fx: [['check', '24 Oct holds', 'ok'], ['alert', '2 flows move to v2.1', 'warn']] }, { id: 'full', l: 'Keep the full scope, move to 31 Oct', outward: true, msg: 'Queued.', fx: [['alert', 'Leadership is told the date moved', 'bad']] }],
51
+ unsure: 'The a11y audit slot may still move.', ign: 'The sprint starts without scope.', go: { action: 'tab', key: 'init', label: 'Open Initiatives' } },
52
+ sso: { tc: 'var(--warning)', sit: '3 of 8 release checks are open and the release is 6 days away. The date holds if the checks close by Monday.', conf: 74, cta: 'Slide to confirm',
53
+ chg: [['clock', '3 of 8 release checks open', 'Jira', 'warn'], ['check', 'Beta is live with 3 customers', 'Jira', 'ok']],
54
+ opts: [{ id: 'hold', l: 'Hold 14 Oct and watch the checks', rec: true, msg: 'Kept. The checks are on your Monday list.', fx: [['check', 'No change to the promise', 'ok']] }, { id: 'tell', l: 'Warn Marta the date may move', outward: true, msg: 'Queued.', fx: [['user', 'Marta hears it early']] }],
55
+ go: { action: 'tab', key: 'init', label: 'Open Initiatives' } },
56
+ search: { tc: 'var(--warning)', sit: 'EN-418 has been blocked on the platform team with no reply since Tuesday. Search relevance is at risk, not late.', conf: 69, cta: 'Slide to confirm',
57
+ chg: [['alert', 'EN-418 blocked, no reply since Tue', 'Jira', 'warn'], ['up', 'Relevance is 28% faster on the index so far', 'Report', 'ok']],
58
+ opts: [{ id: 'ask', l: 'Ask the platform lead for a date', rec: true, outward: true, msg: 'Queued.', fx: [['user', 'A reply by Friday']] }, { id: 'wait', l: 'Wait until Monday', msg: 'Kept.', fx: [['clock', 'Costs 2 more days', 'warn']] }],
59
+ go: { action: 'tab', key: 'init', label: 'Open Initiatives' } },
60
+ returns: { tc: 'var(--success)', sit: 'Learning is on schedule. Interview 5 on 13 Oct decides park or continue, and the stop rule has a review date.', conf: 80, cta: 'Slide to confirm',
61
+ chg: [['check', 'Interview 3 of 5 is booked', 'Calendar', 'ok'], ['notes', '2 of 3 calls mention the support route', 'Notes']],
62
+ opts: [{ id: 'keep', l: 'Keep the plan', rec: true, msg: 'Kept.', fx: [['check', 'Interview 5 decides', 'ok']] }],
63
+ go: { action: 'tab', key: 'init', label: 'Open Initiatives' } },
64
+ onb: { tc: 'var(--muted-foreground)', sit: 'No movement in 9 days. The stop rule found no signals. Continue or park?', conf: 60, cta: 'Slide to confirm',
65
+ chg: [['moon', 'No change in 9 days', 'Repository', 'warn'], ['info', 'No KR or health budget is named', 'Anchor', 'bad']],
66
+ opts: [{ id: 'park', l: 'Park it until Q1', rec: true, msg: 'Parked. Nothing outside changed.', fx: [['moon', 'Frees the next review', 'ok']] }, { id: 'go', l: 'Continue and name an anchor', msg: 'Kept.', fx: [['alert', 'Needs an anchor first', 'warn']] }],
67
+ ign: 'It stays unanchored and is rejected at the gate.', go: { action: 'tab', key: 'init', label: 'Open Initiatives' } },
68
+ };
69
+
70
+ /* the year pane's dots name a bet; this maps the name to the initiative whose case file the dot opens */
71
+ const LINK = { 'Checkout v2': 'checkout', 'Search relevance': 'search', 'Returns discovery': 'returns', 'Continue or park?': 'onb', 'SSO release, SCIM': 'sso' };
72
+
73
+ window.__c3calix = { MEET, INIT, LINK };
74
+ export { MEET, INIT, LINK, fx };
@@ -0,0 +1,66 @@
1
+ /* pm/c3-demo-cal2.js — DEMO fixtures for the Calendar's After notes, the Week time grid and the "Progress and timing" card on
2
+ * /c3.html?demo=1. The content is the console3 mockup's own: DECS and ACT (console3.html lines 1492 and 1493), notesView (1494),
3
+ * MT and MS (1517 and 1518: the meetings and the markers of each day), commits() (2705: the dated promises and tasks) and PCT (2703).
4
+ * c3-demo.js imports this file for its side effect: it sets window.__c3cal2, which calendar-views.js reads when it is present.
5
+ * Real mode never loads this file, so a real calendar draws only what the calendar memory holds. Nothing here reaches the server. */
6
+
7
+ const MT = { // Oct 2026, by day of the month: [start, end, title]
8
+ 5: [[9.5, 10, 'Standup'], [11, 12, 'Roadmap review'], [14, 15.5, 'Design critique']],
9
+ 6: [[9.5, 10, 'Standup'], [13, 14, 'Customer call'], [15, 16, '1:1 Dara'], [16, 17, 'Discovery sync']],
10
+ 7: [[9.5, 10, 'Standup'], [10, 12, 'Checkout workshop'], [14, 15, 'Leadership prep'], [15.5, 16.5, 'Sales sync']],
11
+ 9: [[9.5, 10, 'Standup'], [11, 12, 'Design review'], [14, 15, '1:1 Priya']],
12
+ 12: [[9.5, 10, 'Standup'], [10.5, 11.5, 'SSO freeze'], [14, 15, 'Interview 4'], [15.5, 16, '1:1 Lee']],
13
+ 13: [[9.5, 10, 'Standup'], [13, 14, 'Interview 5'], [15, 16, 'Roadmap sync']],
14
+ 14: [[9.5, 10, 'Standup'], [10, 11, 'SSO release check'], [14, 15, 'Leadership prep']],
15
+ 15: [[9.5, 10, 'Standup'], [11, 12, 'Design review']],
16
+ 16: [[9.5, 10, 'Standup'], [12, 13, 'Leadership weekly'], [14, 15, '1:1 Dara']],
17
+ };
18
+ /* Thursday 8 Oct is the page's own day: the five meetings of the mockup's S.meetings (id, start, end, title) */
19
+ const TODAY_MEET = [['m1', 9.5, 10, 'Standup'], ['m2', 10.5, 11.25, 'Checkout sync'], ['m3', 13, 14, 'Customer interview'], ['m4', 14, 15, 'Sprint planning'], ['m5', 16.5, 17, 'Leadership update']];
20
+ const MS = { // markers by date: [kind, title]
21
+ '2026-10-09': [['copy', 'Copy freeze']], '2026-10-12': [['promise', 'SSO date to Marta']], '2026-10-13': [['learn', 'Interview 5: review the stop rule']],
22
+ '2026-10-14': [['ship', 'SSO release']], '2026-10-16': [['report', 'Leadership weekly']], '2026-10-21': [['risk', 'A11y slot, if scope is cut']],
23
+ '2026-10-23': [['report', 'Leadership weekly']], '2026-10-24': [['promise', 'Checkout v2 promised']], '2026-10-28': [['risk', 'A11y slot, full scope']],
24
+ '2026-10-30': [['report', 'Leadership weekly']], '2026-10-31': [['forecast', 'Checkout v2, full scope']], '2026-11-06': [['promise', 'Search relevance promised']],
25
+ };
26
+ const ROUTINE = /^(Standup|1:1|Sync$)/;
27
+
28
+ /** The meetings and markers of one day, as the mockup's dayMeetings and marks draw them. Weekends are empty. */
29
+ function day(date) {
30
+ const [y, m, d] = date.split('-').map(Number);
31
+ const w = new Date(Date.UTC(y, m - 1, d)).getUTCDay();
32
+ const mk = MS[date] || [];
33
+ if (w === 0 || w === 6) return { ms: [], mk };
34
+ if (date === '2026-10-08') return { ms: TODAY_MEET.map(([id, s, e, t]) => ({ id: id + '@' + date, s, e, t, hot: id === 'm4', rt: t === 'Standup' })), mk };
35
+ const a = (m === 10 && MT[d]) || [[9.5, 10, 'Standup'], [14, 15, 'Sync']];
36
+ return { ms: a.map(([s, e, t]) => ({ s, e, t, rt: ROUTINE.test(t) })), mk };
37
+ }
38
+
39
+ const NOTES = {
40
+ title: 'Checkout workshop', when: 'Wed 7 Oct · 41 min', who: ['Dara', 'Lee', 'Sam'],
41
+ diff: 'live capture says "cut to three flows"; final note says "hold 24 Oct"',
42
+ decisions: [{ id: 'x1', t: 'Defer saved cards to v2.1', rej: 'Keep in v2' }, { id: 'x2', t: 'Hold the 24 Oct date until the scope cut is decided', rej: 'Move the date now' }],
43
+ actions: [{ id: 'a1', t: 'Send the revised scope to design', o: 'You', d: 'Fri' }, { id: 'a2', t: 'Book the a11y audit before 28 Oct', o: '', d: 'Fri' },
44
+ { id: 'a3', t: 'Confirm sandbox keys with the vendor', o: 'Dara', d: 'Fri' }, { id: 'a4', t: 'Share returns insights with sales', o: '', d: 'next Tue' }],
45
+ owners: ['', 'You', 'Dara', 'Lee', 'Marta'], dues: ['Fri', 'next Tue', 'no date'],
46
+ told: ['Dara', 'Lee', 'Sam'], notTold: ['Priya'], flag: '24 Oct is conditional',
47
+ live: 'Dara: the keys are the real blocker, not the scope.\nYou: then we cut to three flows and keep 24 Oct.\n[highlight] decision: cut to three flows\n[caught] action: Dara confirms sandbox keys',
48
+ };
49
+
50
+ /* the mockup's commits() tasks (line 2705): dates are 2026 and days are absolute, so they hold when the page's own day is 8 Oct */
51
+ const TASKS = [
52
+ { id: 't-keys', n: 'Sandbox keys from the vendor', due: '2026-10-06', st: 'over', late: 2, why: 'Asked Tue. No reply in 2 days. Blocks 24 Oct.' },
53
+ { id: 't-num', n: '1 Oct numbers to Priya', due: '2026-10-08', st: 'ok', why: 'Priya asked at 07:58. Due 10:30.' },
54
+ { id: 't-wk', n: 'Weekly status to leadership', due: '2026-10-09', st: 'risk', why: 'The draft waits for the 14:00 decision.' },
55
+ { id: 't-sso', n: 'SSO date to Marta', due: '2026-10-12', st: 'ok', why: "Promised after Monday's freeze." },
56
+ { id: 't-int5', n: 'Interview 5: review the stop rule', due: '2026-10-13', st: 'ok', why: 'Decides whether returns discovery continues.' },
57
+ { id: 't-a11y', n: 'A11y audit slot', due: '2026-10-21', st: 'risk', why: 'No slot before 28 Oct unless scope is cut.' },
58
+ { id: 't-prd', n: 'PRD v5 sent to Dara', due: '2026-10-07', st: 'done', why: 'Sent Wed.' },
59
+ { id: 't-ssob', n: 'SSO beta live', due: '2026-10-06', st: 'done', why: '3 customers, 0 incidents.' },
60
+ { id: 't-srch', n: 'Search fix merged', due: '2026-10-06', st: 'done', why: 'p95 420 to 300 ms.' },
61
+ ];
62
+ const PCT = { checkout: 0.5, returns: 0.4, sso: 0.75, search: 0.38, onb: 0.2 }; // the mockup's PCT (line 2703)
63
+
64
+ const STAT = { sso: 'risk' }; // the mockup's amber SSO (its rag), where the shared demo row says on time
65
+
66
+ window.__c3cal2 = { day, NOTES, TASKS, PCT, STAT };
@@ -0,0 +1,173 @@
1
+ /* pm/c3-demo-cal3.js — DEMO fixtures for the Calendar's Quarter and Year panes on /c3.html?demo=1 (c3-cal-strategy.js reads them).
2
+ * The content is the console3 mockup's own (console3.html: NSA/nsPlan line 2824, KRS 2857, SIGS 2861 and 3713, SIGC 2872 and 3714, SYNM 2887,
3
+ * MATP 2892, BETS 2910, stratCase 2919, CALC 2999 to 3015, calCards 3143, commits 2706). It answers GET /api/c3-strategy in the shape of
4
+ * workspace/lib/routes-c3-strategy.js, plus the demo-only extras the real route has no source for: `head` (the lens line), `deck` (the six cards of each
5
+ * zoom), `delivery` (the commitments of progSVG, initiatives and tasks), `direction` (the case file of the direction card) and `maturity`.
6
+ *
7
+ * Wiring: c3-demo.js imports this file for its side effect (one line). The import runs before c3-demo.js reads window.fetch, so the fetch wrapper
8
+ * installed here sits UNDER c3-demo.js's own: a path this file does not answer falls through to whatever was there. Real mode never loads it. */
9
+
10
+ const MNS = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
11
+ const mlab = (i) => MNS[(i + 9) % 12] + " '" + (25 + Math.floor((i + 9) / 12));
12
+ const key = (i) => { const t = 2025 * 12 + 9 + i; return Math.floor(t / 12) + '-' + String((t % 12) + 1).padStart(2, '0'); };
13
+ const NSA = [9.4, 9.8, 10.2, 10.5, 10.9, 11.2, 11.5, 11.7, 11.9, 12.1, 12.15, 12.28, 12.4];
14
+ const plan = (i) => (i <= 12 ? 9.4 + (12.9 - 9.4) * (i / 12) : 12.9 + (18 - 12.9) * ((i - 12) / 11));
15
+ const slope = (NSA[12] - NSA[9]) / 3;
16
+ const proj = (i) => NSA[12] + slope * (i - 12);
17
+ const cross = (f) => { for (let i = 13; i <= 23; i++) if (proj(i) < plan(i) * f) return i; return -1; };
18
+ const nsVar = () => Math.round((NSA[12] / plan(12) - 1) * 100);
19
+ const LEAVE = mlab(cross(0.92)); const PIVOT = mlab(cross(0.85)); const VAR = Math.abs(nsVar());
20
+
21
+ const NORTH = { label: 'Weekly active accounts', unit: 'k', now: key(12), by: '2027-09-30',
22
+ axis: { quarter: { min: 11, max: 16, ticks: [12, 14, 16] }, year: { min: 8, max: 19, ticks: [10, 14, 18] } },
23
+ points: Array.from({ length: 24 }, (_, i) => ({ at: key(i), actual: i <= 12 ? NSA[i] : null, plan: Math.round(plan(i) * 100) / 100, proj: i >= 12 ? Math.round(proj(i) * 100) / 100 : null })) };
24
+
25
+ const OUTCOMES = [
26
+ { id: 'kr-conv', name: 'Conversion', pillar: 'Convert', unit: '%', start: 3.5, target: 4.0, now: 3.1, expected: 3.75, forecast: 2.9, status: 'off', lead: 'Down 6 weeks in a row. The address step loses 45%.', by: '2026-12-31', pivot: false, n_readings: 7 },
27
+ { id: 'kr-ent', name: 'Enterprise design partners', pillar: 'Enterprise', unit: '', start: 0, target: 5, now: 3, expected: 3, forecast: 5, status: 'ok', lead: 'Two more are asking for SCIM early.', by: '2026-12-31', pivot: false, n_readings: 7 },
28
+ { id: 'kr-ret', name: 'Retention, day 30', pillar: 'Retain', unit: '%', start: 40, target: 45, now: 41.5, expected: 42.9, forecast: 43, status: 'drift', lead: 'Flat. Label printing is the top pain.', by: '2026-12-31', pivot: false, n_readings: 7 },
29
+ { id: 'kr-disc', name: 'Returns discovery decided', pillar: 'Retain', unit: ' of 5', start: 0, target: 5, now: 2, expected: 2.5, forecast: 5, status: 'ok', lead: 'Interview 5 on 13 Oct decides park or continue.', by: '2026-12-31', pivot: false, n_readings: 3 },
30
+ ];
31
+
32
+ /* ── cases: the mockup's SIGC, stratCase and CALC, in the shape c3-case.js reads ── */
33
+ const O = (id, l, rec, msg, fx) => ({ id, l, rec: rec ? 1 : 0, msg, fx: fx || [] });
34
+ const CASE = {
35
+ conv: { sit: 'At this slope conversion ends the quarter at 2.9% against 4.0%. The pivot line is 3.0% for two weeks.', conf: 71, cta: 'Slide to confirm',
36
+ chg: [['clock', 'Down 6 weeks in a row', 'Analytics', 'bad'], ['alert', 'Cart to address loses 45%', 'funnel', 'bad'], ['scale', 'Scope change may be the cause', 'Jira', 'warn']],
37
+ opts: [O('x', 'Run the address-step experiment, freeze scope', 1, "Experiment brief is drafting. Scope freeze goes into today's decision.", [['check', 'Tests the cause before pivoting', 'ok'], ['clock', 'Result in 2 weeks']]),
38
+ O('w', 'Wait two more weeks', 0, 'Watching. The agent re-checks in 2 weeks.', [['alert', 'Reaches the pivot line unprepared', 'warn']]),
39
+ O('p', 'Pivot the checkout bet now', 0, 'Pivot review requested for the Quarterly review.', [['alert', 'Trigger not crossed yet', 'bad']])], unsure: 'One quarter of data. A seasonal dip is not ruled out.' },
40
+ ret: { sit: 'Retention has not moved all quarter, while the clearest customer pain sits in returns.', conf: 66, cta: 'Slide to confirm',
41
+ chg: [['clock', 'Day 30 at 41.5% against 45%', 'Analytics', 'warn'], ['notes', '3 of 3 calls name label printing', 'Notes', 'ok'], ['cal', 'Interview 5 on 13 Oct', 'Calendar']],
42
+ opts: [O('f', 'Pull Returns forward a quarter if interview 5 agrees', 1, 'Conditional change drafted for the Q1 plan.', [['check', 'Moves effort to the strongest signal', 'ok']]), O('k', 'Keep the plan', 0, 'Plan unchanged.', [['clock', 'Returns waits until Q1', 'warn']])] },
43
+ slip: { sit: 'The sandbox keys and the accessibility slot sit on the critical path. Cutting scope pulls the slot to 21 Oct and holds the date.', conf: 78, cta: 'Slide to confirm',
44
+ chg: [['clock', 'Keys 2 days late, slot not before 28 Oct', 'Jira', 'bad'], ['scale', 'Cut to 3 flows frees the 21 Oct slot', 'Calendar', 'ok'], ['out', 'Leadership was told 24 Oct', 'Slack', 'warn']],
45
+ opts: [O('c', 'Cut to 3 flows and hold 24 Oct', 1, 'Scenario set to the cut. The timeline and the Calendar follow.', [['check', 'Holds 24 Oct', 'ok']]), O('w', 'Warn leadership now with a new date', 0, 'Draft to Priya is in Review.', [['out', '31 Oct, with the cause', 'ok']]), O('h', 'Hold and watch', 0, 'No change.', [['alert', 'Crosses the 5-day line', 'warn']])], unsure: 'The vendor may deliver the keys sooner.' },
46
+ churn: { sit: 'Three scope changes in four weeks make any date a guess.', conf: 74, cta: 'Slide to confirm', chg: [['scale', '3 changes in 4 weeks', 'Jira', 'warn'], ['doc', 'PRD at v5', 'Confluence']],
47
+ opts: [O('z', "Freeze scope after today's decision", 1, 'Scope freeze added to the decision follow-ups.', [['check', 'Makes the date believable', 'ok']]), O('o', 'Stay flexible', 0, 'No freeze.', [['alert', 'Date stays a guess', 'warn']])] },
48
+ dep: { sit: 'The same vendor delay holds up two bets, so it is a portfolio risk and not a task risk.', conf: 82, cta: 'Slide to confirm', chg: [['alert', 'Sandbox keys 2 days overdue', 'Jira', 'bad'], ['init', 'Blocks Checkout v2 and SSO billing', 'Initiatives', 'warn']],
49
+ opts: [O('e', 'Escalate to the vendor lead today', 1, 'Escalation drafted for you to send.', [['check', 'Unblocks both bets', 'ok']]), O('w', 'Wait until Friday', 0, 'Waiting.', [['clock', 'Crosses 5 days', 'bad']])] },
50
+ rel: { sit: 'Dates have lost trust. Ranges tell leadership the truth about uncertainty.', conf: 69, cta: 'Slide to confirm', chg: [['report', '62% of dated promises kept', 'Reports', 'warn'], ['out', 'Next update to leadership: Fri 16 Oct', 'Calendar']],
51
+ opts: [O('r', 'Switch to ranges for estimates', 1, 'Ranges will be used in the next update.', [['check', 'Rebuilds trust', 'ok']]), O('d', 'Keep dates', 0, 'No change.', [['alert', 'Risks another miss', 'warn']])] },
52
+ gate: { sit: 'Build is far ahead of the evidence. A rubric draft exists but has no weights from you.', conf: 80, cta: 'Slide to confirm', chg: [['shield', 'Spec and rubric exist as drafts', 'gates', 'warn'], ['alert', 'No quality evidence yet', 'CI', 'warn']],
53
+ opts: [O('w', 'Set the rubric weights today', 1, 'Weights opened in Review.', [['check', 'Review can score the build', 'ok']]), O('l', 'Let the agent propose weights', 0, 'Agent proposal drafted.', [['clock', 'You still confirm them']])] },
54
+ ent: { sit: 'Customers are pulling Enterprise forward faster than the plan assumes.', conf: 77, cta: 'Slide to confirm', chg: [['user', '3 design partners live', 'Jira', 'ok'], ['mail', '2 asked for SCIM early', 'Gmail', 'ok'], ['target', '5 partners by Dec is the goal', 'OKR']],
55
+ opts: [O('q', 'Add Enterprise as a Q1 bet', 1, 'Q1 bet drafted for the Quarterly review.', [['check', 'Follows the pull', 'ok'], ['alert', 'Needs capacity from Retain', 'warn']]), O('h', 'Hold the plan', 0, 'No change.', [['alert', 'May lose the window', 'warn']])] },
56
+ };
57
+ const SIGNALS = [
58
+ { id: 'conv', sev: 'drift', t: 'Conversion has fallen six weeks in a row', ev: '3.5% to 3.1%. The address step loses 45% of carts.', thr: 'Below 3.0% for 2 weeks: pivot the checkout bet', ttt: 'about 2 weeks', sp: [3.5, 3.4, 3.4, 3.3, 3.3, 3.2, 3.1] },
59
+ { id: 'ret', sev: 'drift', t: 'Retention is flat while the top pain is returns', ev: 'Day 30 at 41.5% against 45%. 3 of 3 calls name label printing.', thr: 'No lift after interview 5: pull Returns forward', ttt: '5 days', sp: [41, 41.2, 41.4, 41.3, 41.5, 41.4, 41.5] },
60
+ { id: 'slip', sev: 'drift', t: 'Checkout v2 critical path is 7 days late', ev: 'Sandbox keys and the a11y slot move the ship date from 24 to 31 Oct.', thr: 'More than 5 days late: warn leadership', ttt: 'tripped', sp: [0, 0, 2, 4, 7, 7] },
61
+ { id: 'churn', sev: 'watch', t: 'Scope keeps changing on Checkout v2', ev: 'Scope changed 3 times in 4 weeks.', thr: 'A fourth change: re-baseline the plan', ttt: 'next change', sp: [1, 1, 2, 2, 3, 3, 3] },
62
+ { id: 'dep', sev: 'watch', t: 'One vendor blocks two bets', ev: 'Sandbox keys hold up Checkout v2 and SSO billing.', thr: 'More than 5 days overdue: escalate to the sponsor', ttt: '3 days', sp: [0, 0, 0, 1, 1, 2, 2] },
63
+ { id: 'rel', sev: 'watch', t: 'Dated promises are slipping more often', ev: '62% of dated promises kept this quarter against 80%.', thr: 'Below 55%: give ranges, not dates', ttt: '2 to 3 weeks', sp: [85, 80, 78, 72, 68, 65, 62] },
64
+ { id: 'gate', sev: 'watch', t: 'Search relevance has no quality evidence yet', ev: 'Build is 70% done. Quality, review and acceptance are all open.', thr: 'Build without a rubric: block the merge', ttt: '3 days', sp: [0, 0, 0, 1, 1, 2] },
65
+ { id: 'ent', sev: 'up', t: 'Enterprise pull is stronger than planned', ev: '3 design partners live. 2 more asked for SCIM early.', thr: '5 partners by Dec: make Enterprise a Q1 bet', ttt: 'on course', sp: [0, 0, 1, 1, 2, 3, 3] },
66
+ ].map((s) => ({ ...s, case: CASE[s.id], source: 'demo' }));
67
+
68
+ const SYNERGY = { unit: 'pillars',
69
+ columns: [{ id: 'convert', n: 'Convert', icon: 'target' }, { id: 'retain', n: 'Retain', icon: 'repo' }, { id: 'onboard', n: 'Onboard', icon: 'user' }, { id: 'enterprise', n: 'Enterprise', icon: 'shield' }],
70
+ rows: [{ id: 'checkout', title: 'Checkout v2', weights: [3, 0, 1, 0], parked: false, initiative: 'checkout' }, { id: 'returns', title: 'Returns experience', weights: [1, 3, 0, 0], parked: false, initiative: 'returns' },
71
+ { id: 'sso', title: 'SSO', weights: [1, 0, 0, 3], parked: false, initiative: 'sso' }, { id: 'search', title: 'Search relevance', weights: [1, 2, 0, 0], parked: false, initiative: 'search' },
72
+ { id: 'onb', title: 'Self-serve onboarding', weights: [0, 0, 3, 0], parked: true, initiative: 'onb' }],
73
+ notes: [{ icon: 'plus', tone: 'ok', text: 'SSO and Checkout v2 share billing code. Build them together and save about 2 weeks.' }, { icon: 'alert', tone: 'bad', text: 'Returns and Search relevance both need the platform team in November. Capacity clash.' },
74
+ { icon: 'out', tone: '', text: 'Returns findings feed the Q1 onboarding design.' }, { icon: 'user', tone: 'warn', text: 'Dara carries 4 items across 3 bets.' }] };
75
+
76
+ const MATURITY = { stages: ['Discover', 'Prove', 'Repeat', 'Scale', 'Lead'], target_label: "Sep '27", pillars: [
77
+ { n: 'Convert', now: 2, tgt: 4, proj: 3, ev: '3 of 7 criteria met', nx: 'Conversion at 3.8% or more for 4 weeks' }, { n: 'Retain', now: 2, tgt: 3, proj: 3, ev: '2 of 6 criteria met', nx: 'Returns contact rate at 0.5' },
78
+ { n: 'Onboard', now: 1, tgt: 2, proj: 1, ev: '0 of 5 criteria met', nx: 'a revived onboarding bet' }, { n: 'Enterprise', now: 2, tgt: 3, proj: 3, ev: '4 of 6 criteria met', nx: '5 design partners live' }] };
79
+
80
+ const TIMELINE = { quarters: ["Q4 '26", "Q1 '27", "Q2 '27", "Q3 '27"],
81
+ rows: [{ id: 'checkout', title: 'Checkout v2', sub: 'Convert', cells: ['c', 'c', '', ''], st: 'late', initiative: 'checkout' }, { id: 'search', title: 'Search relevance', sub: 'Convert', cells: ['c', '', '', ''], st: 'risk', initiative: 'search' },
82
+ { id: 'returns', title: 'Returns experience', sub: 'Retain', cells: ['c', 'c', '', ''], st: 'ok', initiative: 'returns' }, { id: 'sso', title: 'SSO and SCIM', sub: 'Enterprise', cells: ['c', 'c', 'c', ''], st: 'ok', initiative: 'sso' },
83
+ { id: 'onb', title: 'Self-serve onboarding', sub: 'Onboard', cells: ['', 'p', '', ''], st: 'park', initiative: 'onb' }, { id: 'audit', title: 'Enterprise audit logs', sub: 'Enterprise', cells: ['', 'a', '', ''], st: 'prop', initiative: null }],
84
+ checkpoints: [{ q: 0, f: 0.62, label: 'Returns on 13 Oct' }, { q: 0, f: 0.95, label: 'Quarterly review on 20 Dec' }, { q: 1, f: 0.7, label: 'Mid-quarter check in Feb' }] };
85
+
86
+ /* the commitments of the mockup's progSVG: initiatives (promised and forecast) and the PM's own tasks, as days of the quarter from 1 Oct */
87
+ const dn = (n) => new Date(Date.UTC(2026, 9, n)).toISOString().slice(0, 10);
88
+ const INIT = (id, n, start, due, fc, pct, st, why) => ({ id, n, kind: 'init', start: dn(start), due: dn(due), fc: dn(fc), pct, st, why, go: id });
89
+ const TASK = (id, n, due, st, why, late) => ({ id, n, kind: 'task', due: dn(due), st, why, late: late || 0 });
90
+ const DELIVERY = { rows: [
91
+ INIT('checkout', 'Checkout v2', 1, 24, 31, 0.5, 'late', 'Sandbox keys and the a11y slot move the ship date.'), INIT('returns', 'Returns experience', 1, 20, 20, 0.4, 'ok', 'Discovery, interview 3 of 5.'),
92
+ INIT('sso', 'SSO', 1, 14, 14, 0.75, 'risk', 'Three release gates still open.'), INIT('onb', 'Self-serve onboarding', 1, -1, -1, 0.2, 'park', 'No movement in 9 days. Continue or park?'), INIT('search', 'Search relevance', 1, 37, 37, 0.38, 'risk', 'EN-418 blocked on platform, no reply since Tue.'),
93
+ TASK('t-keys', 'Sandbox keys from the vendor', 6, 'over', 'Asked Tue. No reply in 2 days. Blocks 24 Oct.', 2), TASK('t-num', '1 Oct numbers to Priya', 8, 'ok', 'Priya asked at 07:58. Due 10:30.'), TASK('t-wk', 'Weekly status to leadership', 9, 'risk', 'The draft waits for the 14:00 decision.'),
94
+ TASK('t-sso', 'SSO date to Marta', 12, 'ok', "Promised after Monday's freeze."), TASK('t-int5', 'Interview 5: review the stop rule', 13, 'ok', 'Decides whether returns discovery continues.'), TASK('t-a11y', 'A11y audit slot', 21, 'risk', 'No slot before 28 Oct unless scope is cut.'),
95
+ TASK('t-prd', 'PRD v5 sent to Dara', 7, 'done', 'Sent Wed.'), TASK('t-ssob', 'SSO beta live', 6, 'done', '3 customers, 0 incidents.'), TASK('t-srch', 'Search fix merged', 6, 'done', 'p95 420 to 300 ms.')] };
96
+
97
+ /* ── the direction card's case files (stratCase) and the other deck cards' ── */
98
+ const DIR_Q = { tc: 'var(--warning)', sit: 'Conversion is off course and retention is flat, but the pivot line is not crossed. Fix the cause before changing the bet.', conf: 76, cta: 'Slide to adjust course',
99
+ chg: [['clock', 'Conversion lands at 2.9% against 4.0%', 'forecast', 'bad'], ['up', 'Enterprise is ahead: 3 of 5 partners', 'OKR', 'ok'], ['alert', '2 signals drifting, 3 on watch', 'signals', 'warn']],
100
+ opts: [O('adjust', 'Adjust: freeze scope, run the experiment, prepare Returns', 1, 'Course adjusted. Three follow-ups are in Review.', [['check', 'Tests the cause before pivoting', 'ok'], ['clock', 'Pivot review stays on 20 Dec']]),
101
+ O('persevere', 'Persevere: hold course', 0, 'Holding course. Signals stay on watch.', [['alert', 'Conversion keeps sliding', 'bad']]), O('pivot', 'Pivot the Convert bet', 0, 'Pivot review requested.', [['alert', 'Trigger is about 2 weeks away', 'bad']])],
102
+ unsure: 'One quarter of data.', ign: 'The slope reaches the pivot line in March.' };
103
+ const DIR_Y = { tc: 'var(--warning)', sit: 'The north star is ' + VAR + '% behind plan and the gap is widening. At this slope it leaves tolerance in ' + LEAVE + ' and reaches the pivot line in ' + PIVOT + '.', conf: 72, cta: 'Slide to rebalance',
104
+ chg: [['clock', 'Growth slowed to 0.8% a month, plan needs 3%', 'Analytics', 'bad'], ['up', 'Enterprise pull is the strongest upside', 'Signals', 'ok'], ['flag', 'Convert is one stage short of target', 'Maturity', 'warn']],
105
+ opts: [O('adjust', 'Adjust: shift effort to Enterprise and Retain', 1, 'Rebalance drafted for the Quarterly review.', [['check', 'Follows the strongest evidence', 'ok'], ['alert', 'Convert slows', 'warn']]),
106
+ O('persevere', 'Persevere: hold the plan', 0, 'Holding the plan.', [['alert', 'Leaves tolerance in ' + LEAVE, 'bad']]), O('pivot', 'Pivot: Enterprise-first strategy', 0, 'Pivot study requested.', [['alert', 'Too little evidence yet', 'warn']])],
107
+ unsure: 'Two quarters of Enterprise data.', ign: 'Pivot line reached in ' + PIVOT + '.' };
108
+ const K = (sit, conf, chg, opts, cta) => ({ sit, conf, cta: cta || 'Slide to confirm', chg, opts });
109
+ const CALC = {
110
+ qkr: K('Two outcomes are on course. Conversion is off course and retention is drifting, so the quarter needs a recovery plan, not a new target.', 74,
111
+ [['clock', 'Conversion lands at 2.9% against 4.0%', 'forecast', 'bad'], ['alert', 'Retention lands at 43% against 45%', 'forecast', 'warn'], ['check', 'Enterprise partners and discovery on course', 'OKR', 'ok']],
112
+ [O('rec', 'Keep the targets and add a recovery plan', 1, 'Recovery plan drafted for Conversion and Retention.', [['check', 'Keeps the ambition honest', 'ok']]), O('rb', 'Re-baseline Conversion to 3.4%', 0, 'Re-baseline requested.', [['alert', 'Hides the problem', 'bad']]), O('drop', 'Drop the Retention outcome', 0, 'Drop requested.', [['alert', 'Loses a pillar', 'bad']])], 'Slide to plan recovery'),
113
+ qsyn: K('Two bets share billing code and two others both need the platform team in November. Sequence them to save time and avoid a clash.', 73,
114
+ [['plus', 'SSO and Checkout v2 share billing code', 'Synergy', 'ok'], ['alert', 'Returns and Search relevance both need the platform team in Nov', 'Capacity', 'bad']],
115
+ [O('seq', 'Build the billing code once and move Search to December', 1, 'Sequence proposed for the Quarterly review.', [['check', 'Saves about 2 weeks', 'ok'], ['clock', 'Search slips a month', 'warn']]), O('as', 'Keep the plan', 0, 'No change.', [['alert', 'Clash in November', 'bad']])], 'Slide to sequence'),
116
+ qns: K('The north star is ' + VAR + '% behind plan and grows about 0.8% a month. Plan needs 3%.', 72,
117
+ [['clock', 'Leaves the tolerance band in ' + LEAVE, 'projection', 'warn'], ['alert', 'Reaches the pivot line in ' + PIVOT, 'projection', 'bad']],
118
+ [O('path', 'Draft a recovery path that closes half the gap by March', 1, 'Recovery path drafted.', [['check', 'Gives the review something to decide', 'ok']]), O('tgt', 'Revisit the plan target', 0, 'Review requested.', [['alert', 'Moves the goal, not the work', 'warn']]), O('hold', 'Hold', 0, 'Holding.', [['alert', 'Gap keeps widening', 'bad']])], 'Slide to draft'),
119
+ ymat: K('Two pillars are on course for September and two are one stage short. Convert and Onboard decide whether the year lands.', 70,
120
+ [['flag', 'Convert is one stage short', 'Maturity', 'warn'], ['moon', 'Onboard has no active bet', 'Maturity', 'bad'], ['check', 'Retain and Enterprise are on course', 'Maturity', 'ok']],
121
+ [O('b', 'Revive an Onboard bet in Q2 and focus Convert on the checkout fix', 1, 'Plan change drafted for the Quarterly review.', [['check', 'Closes both gaps', 'ok']]), O('k', 'Keep the plan', 0, 'No change.', [['alert', 'Two pillars stay short', 'warn']])], 'Slide to plan'),
122
+ ybets: K('Every bet has a date to persevere or pivot. The next one is Returns on 13 Oct, then the Quarterly review on 20 Dec.', 78,
123
+ [['flag', 'Returns review on 13 Oct', 'Checkpoint'], ['flag', 'Quarterly review on 20 Dec', 'Checkpoint'], ['spark', 'An agent-proposed Enterprise audit logs bet for Q1', 'Proposal', 'ok']],
124
+ [O('p', 'Add the proposed Enterprise audit logs bet to Q1', 1, 'Bet added to the Q1 draft.', [['check', 'Follows the strongest pull', 'ok']]), O('n', 'Decline the proposal', 0, 'Declined.', [['moon', 'Stays in the log']])], 'Slide to add'),
125
+ yopp: K('Customer pull for Enterprise is stronger than any other signal. An Enterprise-first strategy is worth a short study before the pivot line.', 62,
126
+ [['user', '3 design partners live, 2 asking early', 'Signals', 'ok'], ['flag', 'Enterprise is on course for September', 'Maturity', 'ok'], ['alert', 'Needs capacity from Retain and Convert', 'Capacity', 'warn']],
127
+ [O('st', 'Run a 6-week Enterprise-first study', 1, 'Study scoped as a draft brief.', [['check', 'Tests the pivot cheaply', 'ok']]), O('no', 'Stay with the four pillars', 0, 'No change.', [['clock', 'May miss the window', 'warn']])], 'Slide to scope the study'),
128
+ };
129
+
130
+ const FACTS_SIG = [['clock', '3 drifting'], ['eye', '4 on watch'], ['up', '1 upside']];
131
+ const CONV_NUM = { kind: 'num', big: '2.9', unit: '%', delta: 'lands here at this slope', tone: 'var(--destructive)', arr: [3.5, 3.4, 3.4, 3.3, 3.3, 3.2, 3.1], target: 4 };
132
+ const DECK = {
133
+ quarter: [
134
+ { k: 'qdir', ei: 'target', el: 'Quarter direction', h: 'Adjust course early', sub: '2 of 4 outcomes on course. Conversion is drifting. Enterprise is ahead.', hero: { kind: 'ns' }, facts: FACTS_SIG, case: DIR_Q, dir: true },
135
+ { k: 'qkr', ei: 'target', el: 'Outcomes', h: '2 of 4 outcomes on course', sub: 'Conversion is off course. Retention is drifting.', hero: { kind: 'krs' }, facts: [['flag', 'Quarterly review 20 Dec']], case: CALC.qkr },
136
+ { k: 'qsig', tag: 'critical', h: 'Conversion has fallen six weeks in a row', sub: 'Below 3.0% for 2 weeks is the pivot trigger.', hero: CONV_NUM, facts: [['clock', 'About 2 weeks to the trigger'], ['alert', 'Cart to address loses 45%']], case: CASE.conv },
137
+ { k: 'qsyn', tag: 'opp', h: 'Build SSO and Checkout billing together', sub: 'Saves about 2 weeks. Returns and Search clash on the platform team.', hero: { kind: 'rows', rows: [['plus', 'SSO and Checkout share billing code', 'ok', '+2 wk'], ['alert', 'Returns and Search need the platform team', 'bad', 'clash']] }, facts: [['user', 'Dara carries 4 items']], case: CALC.qsyn },
138
+ { k: 'qwin', tag: 'big', h: 'Enterprise is ahead: 3 of 5 partners', sub: 'Two more asked for SCIM early.', hero: { kind: 'ring', done: 3, total: 5, cap: '3 of 5 partners', sub: '2 more asking' }, facts: [['flag', 'First year-one milestone']], case: CASE.ent },
139
+ { k: 'qns', ei: 'up', el: 'North star', h: 'North star is ' + VAR + '% behind plan', sub: 'Leaves tolerance in ' + LEAVE + ', pivot line in ' + PIVOT + '.', hero: { kind: 'ns' }, facts: [['clock', '0.8% a month, plan needs 3%']], case: CALC.qns },
140
+ ],
141
+ year: [
142
+ { k: 'ydir', ei: 'target', el: 'Year direction', h: 'North star is ' + VAR + '% behind and widening', sub: 'It leaves tolerance in ' + LEAVE + ' and reaches the pivot line in ' + PIVOT + '.', hero: { kind: 'ns' }, facts: FACTS_SIG, case: DIR_Y, dir: true },
143
+ { k: 'ymat', ei: 'flag', el: 'Maturity', h: 'Two pillars one stage short', sub: 'Convert and Onboard decide whether the year lands.', hero: { kind: 'ladder' }, facts: [['flag', 'Target: September 2027']], case: CALC.ymat },
144
+ { k: 'yns', ei: 'up', el: 'North star', h: 'The gap is widening: ' + nsVar() + '% against plan', sub: 'Plan needs 3% a month. We are at 0.8%.', hero: { kind: 'ns' }, facts: [['alert', 'Pivot line in ' + PIVOT]], case: CALC.qns },
145
+ { k: 'ybets', ei: 'flag', el: 'Bets and checkpoints', h: 'Every bet has a date to persevere or pivot', sub: 'Next: Returns on 13 Oct, then the Quarterly review on 20 Dec.', hero: { kind: 'rows', rows: [['flag', 'Returns review', '', '13 Oct'], ['flag', 'Quarterly review', '', '20 Dec'], ['spark', 'Enterprise audit logs proposed', 'ok', 'agent']] }, facts: [['target', '6 bets, 4 pillars']], case: CALC.ybets },
146
+ { k: 'ysig', tag: 'critical', h: 'Conversion is the earliest warning', sub: 'Six weeks down. The pivot trigger is 2 weeks below 3.0%.', hero: { ...CONV_NUM, big: '3.1', delta: 'falling six weeks' }, facts: [['clock', '2 weeks to trigger']], case: CASE.conv },
147
+ { k: 'yopp', tag: 'opp', h: 'An Enterprise-first strategy deserves a study', sub: 'Customer pull is the strongest signal we have.', hero: { kind: 'rows', rows: [['user', '3 design partners live', 'ok'], ['mail', '2 asking for SCIM early', 'ok'], ['alert', 'Needs capacity from Retain', 'warn']] }, facts: [['spark', '6-week study']], case: CALC.yopp },
148
+ ],
149
+ };
150
+
151
+ export const STRATEGY = {
152
+ ok: true, today: '2026-10-08', demo: true, north: NORTH, outcomes: OUTCOMES, signals: SIGNALS, synergy: SYNERGY, timeline: TIMELINE, maturity: MATURITY, delivery: DELIVERY, deck: DECK,
153
+ head: { quarter: { i: 'target', h: 'Q4 is drifting: adjust early, before the pivot line', s: '2 of 4 outcomes on course. Conversion is off course and retention is flat. Enterprise is ahead.', ring: [2, 4] },
154
+ year: { i: 'flag', h: 'North star ' + VAR + '% behind plan, gap widening', s: 'Leaves tolerance in ' + LEAVE + ', reaches the pivot line in ' + PIVOT + ". 2 of 4 pillars on course for Sep '27.", ring: [2, 4] } },
155
+ direction: { state: 'adjust', next: 'Quarterly review, 20 Dec' },
156
+ sources: { kr_metrics: 4, bets: 6, hypotheses: 3, signal_rules: 8 },
157
+ };
158
+
159
+ /* the answer for GET /api/c3-strategy; chained under c3-demo.js's fetch wrapper (see the header) */
160
+ const realFetch = typeof window !== 'undefined' && window.fetch ? window.fetch.bind(window) : null;
161
+ if (realFetch) {
162
+ window.fetch = (input, init) => {
163
+ try {
164
+ const raw = typeof input === 'string' ? input : input instanceof URL ? input.href : input && input.url;
165
+ const method = String((init && init.method) || (input && input.method) || 'GET').toUpperCase();
166
+ const u = new URL(raw, location.origin);
167
+ if (method === 'GET' && u.origin === location.origin && u.pathname === '/api/c3-strategy') {
168
+ return Promise.resolve(new Response(JSON.stringify(STRATEGY), { status: 200, headers: { 'content-type': 'application/json' } }));
169
+ }
170
+ } catch { /* an unreadable request goes on as it is */ }
171
+ return realFetch(input, init);
172
+ };
173
+ }