create-pmos 0.134.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 (209) 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 +16 -1
  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-capture.js +9 -1
  83. package/workspace/lib/notes-final.js +38 -13
  84. package/workspace/lib/notes-replay.js +125 -0
  85. package/workspace/lib/notes-screen.js +180 -0
  86. package/workspace/lib/notes-whisper.js +10 -3
  87. package/workspace/lib/outbox-store.js +10 -6
  88. package/workspace/lib/pmos-verbs.js +3 -1
  89. package/workspace/lib/proactive-cli.js +7 -0
  90. package/workspace/lib/proactive.js +15 -2
  91. package/workspace/lib/repo-script.js +36 -0
  92. package/workspace/lib/report-dashboards.js +5 -2
  93. package/workspace/lib/report-frame.js +122 -0
  94. package/workspace/lib/report-schedules.js +113 -0
  95. package/workspace/lib/route-modules.js +9 -2
  96. package/workspace/lib/routes-boards.js +734 -0
  97. package/workspace/lib/routes-brief-page.js +13 -4
  98. package/workspace/lib/routes-briefing.js +22 -11
  99. package/workspace/lib/routes-c3-init.js +4 -3
  100. package/workspace/lib/routes-c3-life.js +2 -1
  101. package/workspace/lib/routes-c3-reports.js +96 -13
  102. package/workspace/lib/routes-c3-strategy.js +4 -3
  103. package/workspace/lib/routes-c3-timeline.js +3 -2
  104. package/workspace/lib/routes-calendar-import.js +3 -2
  105. package/workspace/lib/routes-calendar-views.js +9 -4
  106. package/workspace/lib/routes-doc-health.js +2 -1
  107. package/workspace/lib/routes-doc-sheet.js +2 -1
  108. package/workspace/lib/routes-forecast.js +2 -1
  109. package/workspace/lib/routes-hooks.js +3 -2
  110. package/workspace/lib/routes-initiatives.js +2 -1
  111. package/workspace/lib/routes-intake.js +2 -1
  112. package/workspace/lib/routes-notes.js +55 -9
  113. package/workspace/lib/routes-outbox.js +23 -3
  114. package/workspace/lib/routes-pm-items.js +19 -6
  115. package/workspace/lib/routes-records.js +3 -2
  116. package/workspace/lib/routes-releases.js +2 -1
  117. package/workspace/lib/routes-report-facts.js +4 -3
  118. package/workspace/lib/routes-report-families.js +2 -1
  119. package/workspace/lib/routes-roadmap-ops.js +5 -4
  120. package/workspace/lib/routes-runs.js +6 -5
  121. package/workspace/lib/routes-settings.js +51 -0
  122. package/workspace/lib/routes-signals.js +3 -2
  123. package/workspace/lib/routes-stage-tools.js +220 -0
  124. package/workspace/lib/run-answers.js +2 -1
  125. package/workspace/lib/settings-store.js +226 -0
  126. package/workspace/lib/stage-tools.js +208 -0
  127. package/workspace/lib/vendor/scripts/signals/layout-paths.js +2 -1
  128. package/workspace/lib/vendor/scripts/workspace-lib.js +24 -0
  129. package/workspace/lib/work-evidence.js +73 -0
  130. package/workspace/package-lock.json +2 -2
  131. package/workspace/package.json +1 -1
  132. package/workspace/public/pm/briefing-deck.js +63 -17
  133. package/workspace/public/pm/briefing-model.js +36 -7
  134. package/workspace/public/pm/c3-ambient.js +100 -0
  135. package/workspace/public/pm/c3-brief-needs.js +26 -6
  136. package/workspace/public/pm/c3-brief-pulse.js +12 -4
  137. package/workspace/public/pm/c3-brief-today.js +56 -21
  138. package/workspace/public/pm/c3-brief.js +2 -1
  139. package/workspace/public/pm/c3-calendar.js +161 -42
  140. package/workspace/public/pm/c3-calpop.js +5 -5
  141. package/workspace/public/pm/c3-demo-boards.js +649 -0
  142. package/workspace/public/pm/c3-demo-brief-page.js +1 -1
  143. package/workspace/public/pm/c3-demo-cal4.js +3 -2
  144. package/workspace/public/pm/c3-demo-cal5.js +52 -0
  145. package/workspace/public/pm/c3-demo-data.js +17 -27
  146. package/workspace/public/pm/c3-demo-rep3.js +46 -5
  147. package/workspace/public/pm/c3-demo.js +2 -1
  148. package/workspace/public/pm/c3-dock.js +1 -1
  149. package/workspace/public/pm/c3-ib-docs.js +80 -0
  150. package/workspace/public/pm/c3-ib-initiatives.js +134 -0
  151. package/workspace/public/pm/c3-ib-items.js +201 -0
  152. package/workspace/public/pm/c3-ib-releases.js +51 -0
  153. package/workspace/public/pm/c3-ib-sprints.js +55 -0
  154. package/workspace/public/pm/c3-ib-triage.js +178 -0
  155. package/workspace/public/pm/c3-ii-tabs.js +240 -0
  156. package/workspace/public/pm/c3-ini-records.js +197 -0
  157. package/workspace/public/pm/c3-ini-timeline.js +278 -0
  158. package/workspace/public/pm/c3-ini-ui.js +229 -0
  159. package/workspace/public/pm/c3-ini-work.js +139 -0
  160. package/workspace/public/pm/c3-initiatives.css +218 -0
  161. package/workspace/public/pm/c3-initiatives.js +741 -0
  162. package/workspace/public/pm/c3-plan.js +2 -2
  163. package/workspace/public/pm/c3-reports.css +47 -47
  164. package/workspace/public/pm/c3-reports.js +223 -185
  165. package/workspace/public/pm/c3-settings.css +74 -0
  166. package/workspace/public/pm/c3-settings.js +703 -0
  167. package/workspace/public/pm/c3-shell.js +102 -36
  168. package/workspace/public/pm/c3-terminal-icons.js +2 -2
  169. package/workspace/public/pm/c3-terminal.js +2 -2
  170. package/workspace/public/pm/calendar-views.js +1 -1
  171. package/workspace/public/pm/components.css +319 -213
  172. package/workspace/public/pm/console3.css +99 -23
  173. package/workspace/public/pm/dest-notes.js +26 -4
  174. package/workspace/public/pm/notes-screen-capture.js +165 -0
  175. package/workspace/public/pm/notes-screen-view.js +98 -0
  176. package/workspace/public/pm/panel-trees.js +5 -2
  177. package/workspace/public/pm/pm-work-view.js +233 -146
  178. package/workspace/public/pm/shell-bell.js +3 -2
  179. package/workspace/public/pm/shell-recording.js +29 -8
  180. package/workspace/public/pm/tokens.css +2 -2
  181. package/workspace/public/pm/view-notes.js +46 -9
  182. package/workspace/public/pm/welcome.js +197 -89
  183. package/workspace/public/welcome.html +6 -4
  184. package/workspace/server.js +105 -6
  185. package/workspace/skills/day-check.skill +1 -1
  186. package/workspace/skills/jira-sync.skill +6 -4
  187. package/workspace/skills/morning-brief.skill +1 -1
  188. package/workspace/test-files.json +12 -3
  189. package/workspace/test-lists/c3-ib-items.json +9 -0
  190. package/workspace/test-lists/c3-ib-more.json +9 -0
  191. package/workspace/test-lists/c3-ib-triage-backlog.json +9 -0
  192. package/workspace/test-lists/c3-initiative-boards.json +6 -0
  193. package/workspace/test-lists/calendar-tidy-up.json +6 -0
  194. package/workspace/test-lists/console3-briefing.json +9 -0
  195. package/workspace/test-lists/console3-foundation.json +9 -0
  196. package/workspace/test-lists/console3-reports.json +3 -3
  197. package/workspace/test-lists/meeting-notes.json +10 -4
  198. package/workspace/test-lists/workspace-headless-jobs.json +5 -0
  199. package/workspace/test-lists/workspace-package-script-closure.json +6 -0
  200. package/workspace/lib/vendor/workspace/lib/actor.js +0 -91
  201. package/workspace/lib/vendor/workspace/lib/board-cache.js +0 -156
  202. package/workspace/lib/vendor/workspace/lib/console-state.js +0 -295
  203. package/workspace/lib/vendor/workspace/lib/factory-join.js +0 -158
  204. package/workspace/lib/vendor/workspace/lib/git-info.js +0 -306
  205. package/workspace/lib/vendor/workspace/lib/plan-items.js +0 -922
  206. package/workspace/lib/vendor/workspace/lib/plan-tree.js +0 -419
  207. package/workspace/lib/vendor/workspace/lib/review-gate.js +0 -39
  208. package/workspace/lib/vendor/workspace/lib/sink.js +0 -321
  209. package/workspace/lib/vendor/workspace/lib/state-root.js +0 -587
@@ -0,0 +1,139 @@
1
+ /* pm/c3-ini-work.js — the model the Initiatives page shares across its tabs (mockup v5, 34-initiatives.js): the workflow templates an
2
+ * initiative can run, the stage tools, and the work items (Jira items and PMOS tasks) as rows, sheets and timeline rows. It reads the
3
+ * server only through the ctx it is handed (c3-initiatives.js makeCtx), and writes only the PMOS-task status (POST
4
+ * /api/boards/tasks/state) from the item sheet. Jira items are read-only everywhere: status, dates and owner are Jira's.
5
+ *
6
+ * TEMPLATES · isBuildStage · templateBuilds · offered(board) · wfOf(row, board) · catOf(wf, stage) · tplName(row, board) · kindOf(row, board)
7
+ * TOOL_META { id: [label, icon] } the server's stage tools (scripts/boards.js TOOLS)
8
+ * KIND_IC · STATUSES · srcTag(w) · wkStatus(w) · workRow(ctx, w, o) · openWorkSheet(ctx, w, all, onChanged)
9
+ * workTlRows(ctx, items, onChanged) · iniTlRow(ctx, ini, kids, board) · iniTip(ini, board) rows for c3-ini-timeline.js
10
+ * loadItems(ctx) · dropItems(ctx) GET /api/boards/items, kept for a few seconds per board */
11
+ import { el } from './dom.js';
12
+ import { icon, chip, tag, st, note, kv, nrow, sect, list, lk, dateChip, ownerChip, ownerText, day, daysBetween, trunc, progChip, pill, plural } from './c3-ini-ui.js';
13
+
14
+ const S = (name, cat) => ({ name, cat });
15
+ /** The workflow templates (scripts/boards.js INI_TEMPLATES): name, kind, use and stages. */
16
+ export const TEMPLATES = Object.freeze({
17
+ product: { name: 'Product', kind: 'product', use: 'Product work that ships: discovery through to evaluating what shipped.', stages: [S('Discover', 'Discover'), S('Plan', 'Plan'), S('Build & Deliver', 'Do'), S('Monitor & Evaluate', 'Learn')] },
18
+ fix: { name: 'Quick fix', kind: 'product', use: 'A small, known change: plan it, build it, ship it, check it.', stages: [S('Plan', 'Plan'), S('Build & Deliver', 'Do'), S('Evaluate', 'Learn')] },
19
+ research: { name: 'Research only', kind: 'product', use: 'Learn before committing. Nothing gets built in this initiative.', stages: [S('Discover', 'Discover'), S('Plan', 'Plan'), S('Evaluate', 'Learn')] },
20
+ content: { name: 'Content and launch', kind: 'project', use: 'Pages, campaigns and launches that a team or an agency produces.', stages: [S('Discover', 'Discover'), S('Plan', 'Plan'), S('Produce', 'Do'), S('Launch', 'Do'), S('Evaluate', 'Learn')] },
21
+ project: { name: 'Project', kind: 'project', use: 'Someone who only manages a project: plan it, run it, close it.', stages: [S('Plan', 'Plan'), S('Execute', 'Do'), S('Close', 'Learn')] },
22
+ simple: { name: 'Simple', kind: 'simple', use: 'A plain to-do flow for small or personal work.', stages: [S('To do', 'Plan'), S('Doing', 'Do'), S('Done', 'Learn')] },
23
+ });
24
+ export const BOARD_TEMPLATES = ['product', 'project', 'simple'];
25
+ export const isBuildStage = (s) => /^(build|deliver)/i.test(String((s && s.name) || s || ''));
26
+ export const templateBuilds = (k) => !!TEMPLATES[k] && TEMPLATES[k].stages.some(isBuildStage);
27
+ /** The templates the board offers new and existing initiatives (never a Build one when Build and deliver is off). */
28
+ export const offered = (b) => (Array.isArray(b && b.offer) && b.offer.length ? b.offer : Object.keys(TEMPLATES)).filter((k) => TEMPLATES[k] && (b.build !== false || !templateBuilds(k)));
29
+ export const wfOf = (row, b) => (row && Array.isArray(row.workflow) && row.workflow.length ? row.workflow : (row && row.template && TEMPLATES[row.template] ? TEMPLATES[row.template].stages : (b && b.workflow) || []));
30
+ export const catOf = (wf, stage) => ((wf || []).find((s) => s.name === stage) || {}).cat || 'Plan';
31
+ const sameNames = (a, b) => a.length === b.length && a.every((s, k) => s.name === b[k].name);
32
+ /** "Research only", "Product (edited)", or the board's own workflow ("Product, the board's"). */
33
+ export function tplName(row, b) {
34
+ const t = row && row.template && TEMPLATES[row.template];
35
+ if (t) return t.name + (row.workflow && row.workflow.length && !sameNames(t.stages, row.workflow) ? ' (edited)' : '');
36
+ if (b && b.self) return 'PMOS ladder';
37
+ const bt = TEMPLATES[b && b.template];
38
+ return bt ? bt.name + (b.templateEdited ? ' (edited)' : '') : 'Custom';
39
+ }
40
+ export const kindOf = (row, b) => (row && row.template && TEMPLATES[row.template] ? TEMPLATES[row.template].kind : (TEMPLATES[b && b.template] || TEMPLATES.product).kind);
41
+
42
+ /** The stage tools of the server (scripts/boards.js TOOLS): label and icon. */
43
+ export const TOOL_META = {
44
+ ideas: ['Ideas', 'spark'], hypotheses: ['Hypotheses', 'flag'], notes: ['Notes', 'notes'], prd: ['PRD', 'doc'], rubric: ['Done-criteria', 'check'],
45
+ breakdown: ['Breakdown', 'tree'], placement: ['Roadmap place', 'cal'], tickets: ['Tickets', 'check'], releases: ['Releases', 'out'],
46
+ measurements: ['Measurements', 'up'], evaluation: ['Evaluation', 'scale'], history: ['History', 'undo'], brief: ['Brief', 'doc'], timeline: ['Timeline', 'cal'],
47
+ };
48
+ export const toolLabel = (t) => (t && t.label) || (TOOL_META[t && t.id] || [String((t && t.id) || '')])[0];
49
+ export const toolIcon = (id) => (TOOL_META[id] || [0, 'doc'])[1];
50
+
51
+ /* ── work items ── */
52
+ export const KIND_IC = { Epic: 'bolt', Story: 'doc', Task: 'check', Bug: 'alert' };
53
+ export const STATUSES = ['To do', 'In progress', 'In review', 'Done'];
54
+ export const srcTag = (w) => (w.src === 'jira' ? el('span', { class: 'tag' }, icon('jira', 'sm'), 'Jira') : el('span', { class: 'tag' }, 'PMOS'));
55
+ export const wkStatus = (w) => (w.blocked ? st('bad', 'alert', 'Blocked') : w.status === 'Done' ? st('ok', 'check', 'Done') : w.status === 'To do' ? chip('To do') : tag(w.status));
56
+
57
+ const CACHE = new Map();
58
+ /** GET /api/boards/items for the ctx's board: { ok, items, initiatives, build, jira, error }. Kept for 5 s. */
59
+ export async function loadItems(ctx, force) {
60
+ const key = ctx.ws + '|' + ctx.board.id;
61
+ const hit = CACHE.get(key);
62
+ if (!force && hit && Date.now() - hit.at < 5000) return hit.p;
63
+ const p = ctx.api.get('/api/boards/items', { board: ctx.board.id }).then((r) => (r && r.ok && !r.pending
64
+ ? { ok: true, items: r.items || [], initiatives: r.initiatives || [], build: !!r.build, jira: r.jira || null, error: '' }
65
+ : { ok: false, items: [], initiatives: [], build: !!ctx.board.build, jira: ctx.board.jira || null, error: r && r.pending ? 'the repository is still being read' : (r && r.error) || 'no answer' }));
66
+ CACHE.set(key, { at: Date.now(), p });
67
+ return p;
68
+ }
69
+ export const dropItems = (ctx) => { if (ctx && ctx.board) CACHE.delete(ctx.ws + '|' + ctx.board.id); else CACHE.clear(); };
70
+
71
+ /** One work item as a console3 row. o: { all, ini (show the initiative link), epic (show the epic chip, default true), sub, right, onChanged }. */
72
+ export function workRow(ctx, w, o = {}) {
73
+ const ep = w.epic && o.all && o.epic !== false ? o.all.find((x) => x.uid === w.epic) : null;
74
+ const late = w.status !== 'Done' && w.end && daysBetween(ctx.today, w.end) < 0;
75
+ const chips = [w.key ? chipM(w.key) : null, chip(icon(KIND_IC[w.kind] || 'check', 'sm'), w.kind), srcTag(w), wkStatus(w), ownerChip(w.owner),
76
+ w.end ? dateChip(w.kind === 'Epic' ? w.start : null, w.end, late) : null,
77
+ ep ? chip(icon('bolt', 'sm'), trunc(ep.title, 32)) : null,
78
+ o.ini && w.ini ? lk({ onclick: () => ctx.openInitiative(w.ini), 'data-ini-open': w.ini }, icon('init', 'sm'), w.ini) : null];
79
+ return nrow({ ic: w.blocked ? 'alert' : KIND_IC[w.kind] || 'check', hot: w.status === 'In progress' || w.status === 'In review', tone: w.blocked ? 'bad' : '',
80
+ title: w.title, onOpen: () => openWorkSheet(ctx, w, o.all || [w], o.onChanged), openLabel: 'Open ' + (w.key ? w.key + ' ' : '') + w.title, chips,
81
+ right: [w.src === 'jira' ? el('span', { class: 'chip2', title: 'Status and dates come from Jira' }, icon('lock', 'sm')) : null, ...(o.right || [])],
82
+ attrs: { 'data-ini-item': w.uid, 'data-src': w.src }, cls: o.sub ? 'ini-sub-row' : '' });
83
+ }
84
+ const chipM = (t) => el('span', { class: 'chip2 mono' }, t);
85
+
86
+ /** The sheet of one work item: what it is, its dates and progress, its items when it is an epic; a PMOS task can change status here. */
87
+ export function openWorkSheet(ctx, w, all, onChanged) {
88
+ const kids = (all || []).filter((x) => x.epic && x.epic === w.uid);
89
+ const ini = w.ini;
90
+ const body = el('div', { class: 'ini-form', 'data-ini': 'work-sheet' });
91
+ const sh = ctx.openSheet({ title: (w.key ? w.key + ' ' : '') + w.title, icon: KIND_IC[w.kind] || 'check', body });
92
+ async function setState(s) {
93
+ const r = await ctx.api.post('/api/boards/tasks/state', { board: ctx.board.id, task: w.ref, state: s });
94
+ if (!r || !r.ok) { ctx.toast({ text: 'The status did not change: ' + ((r && r.error) || 'no answer') + '.', tone: 'error' }); return; }
95
+ sh.close(); dropItems(ctx);
96
+ ctx.toast('"' + trunc(w.title, 40) + '" moved to ' + s + '.');
97
+ if (onChanged) onChanged(r.item);
98
+ }
99
+ body.replaceChildren(
100
+ el('div', { class: 'chips2' }, srcTag(w), chip(icon(KIND_IC[w.kind] || 'check', 'sm'), w.kind), wkStatus(w), ini ? lk({ onclick: () => { sh.close(); ctx.openInitiative(ini); } }, icon('init', 'sm'), ini) : null),
101
+ kv([['Owner', ownerText(w.owner)], ['Dates', w.start || w.end ? (day(w.start) || 'No start') + ' to ' + (day(w.end) || 'no end') : 'No dates yet'], ['Progress', progChip(w.progress || 0, w.status === 'Done' ? 'ok' : 'acc')]]),
102
+ kids.length ? sect({ ic: 'bolt', title: 'In this epic', count: kids.length }, list(kids.map((k) => workRow(ctx, k, { all, epic: false, onChanged })))) : null,
103
+ w.src === 'jira'
104
+ ? el('div', { class: 'fold' }, icon('lock', 'sm'), el('b', null, 'Mirrored from Jira, read-only.'), ' Status, dates and owner are Jira\'s. Change them in Jira and PMOS mirrors them.')
105
+ : w.kind === 'Epic' ? note('A PMOS epic groups PMOS tasks. Drag its tasks on the Timeline to change their dates.')
106
+ : el('div', { class: 'ini-field' }, el('span', { class: 'dim mono ini-lbl' }, 'Move to'),
107
+ el('div', { class: 'seg ini-seg', role: 'group', 'aria-label': 'Status' }, ...STATUSES.map((s) => pill({ class: '', 'aria-pressed': String(w.status === s), 'data-ini-state': s, disabled: w.status === s, onclick: () => setState(s) }, s)))));
108
+ }
109
+
110
+ /* ── timeline rows ── */
111
+ export const iniTip = (i, b) => ({ t: i.id + ' ' + i.title, p: Math.round((i.progress || 0) * 100) + '%', st: (i.stage || 'No stage') + ' · ' + (i.status || 'no status'), o: i.owner || 'No owner',
112
+ src: 'Initiative · ' + tplName(i, b) + ' workflow', hint: 'Click the row to show its epics' });
113
+ const toneOfIni = (i) => (i.status === 'late' ? 'bad' : i.status === 'at risk' ? 'warn' : i.status === 'done' ? 'ok' : 'acc');
114
+ export const iniTlRow = (ctx, i, kids, b, cls) => ({ uid: i.id, label: i.title, key: i.id, start: i.start, end: i.end, progress: i.progress || 0, tone: toneOfIni(i), cls: cls || 'ini',
115
+ open: () => ctx.openInitiative(i.id, 'overview', b && b.id), tip: iniTip(i, b || ctx.board), kids });
116
+ /** Epic > item rows of a set of items; items with no epic of the set come after. A PMOS task can be dragged; a Jira item is locked. */
117
+ export function workTlRows(ctx, items, onChanged) {
118
+ const wr = (w) => ({ uid: w.uid, label: w.title, key: w.key, src: w.src, start: w.start, end: w.end, progress: w.progress || 0, cls: w.kind === 'Epic' ? 'ep' : 'it',
119
+ tone: w.blocked ? 'bad' : w.status === 'Done' ? 'ok' : w.src === 'pmos' ? 'pm' : 'acc', drag: w.src === 'pmos' && w.kind !== 'Epic' && w.ref ? w.ref : null, lock: w.src === 'jira' ? w.key : null,
120
+ open: () => openWorkSheet(ctx, w, items, onChanged),
121
+ tip: { t: (w.key ? w.key + ' ' : '') + w.title, p: Math.round((w.progress || 0) * 100) + '%', st: w.blocked ? 'Blocked' : w.status, o: w.owner || 'No owner', jira: w.src === 'jira',
122
+ src: w.src === 'jira' ? 'Jira ' + w.kind.toLowerCase() + ' · read-only' : 'PMOS ' + w.kind.toLowerCase(), hint: w.src === 'jira' ? 'Dates come from Jira' : w.kind === 'Epic' ? 'Click to show its tasks' : 'Drag to reschedule' } });
123
+ const by = (a, c) => String(a.start || '9').localeCompare(String(c.start || '9'));
124
+ const epics = items.filter((w) => w.kind === 'Epic');
125
+ const loose = items.filter((w) => w.kind !== 'Epic' && !(w.epic && epics.some((e) => e.uid === w.epic)));
126
+ return [...epics.map((e) => ({ ...wr(e), kids: items.filter((w) => w.epic === e.uid).sort(by).map(wr) })), ...loose.sort(by).map(wr)];
127
+ }
128
+ /** Reschedule a PMOS task by whole days (a drag or an arrow key on the Timeline), with an Undo in the toast. */
129
+ export async function rescheduleTask(ctx, taskId, days, onDone) {
130
+ const r = await ctx.api.post('/api/boards/tasks/reschedule', { board: ctx.board.id, task: taskId, days });
131
+ dropItems(ctx);
132
+ if (!r || !r.ok) { ctx.toast({ text: 'The task was not rescheduled: ' + ((r && r.error) || 'no answer') + '.', tone: 'error' }); onDone(null); return; }
133
+ const it = r.item || {};
134
+ ctx.toast({ text: 'Rescheduled "' + trunc(it.title || '', 40) + '" to ' + day(it.start) + ' to ' + day(it.end) + '.', actionLabel: 'Undo', timeout: 8000,
135
+ onAction: async () => { await ctx.api.post('/api/boards/tasks/reschedule', { board: ctx.board.id, task: taskId, days: -days }); dropItems(ctx); onDone(null, true); } });
136
+ onDone(it);
137
+ }
138
+ export const lockedToast = (ctx, key) => ctx.toast(key + ' is a Jira item. Its dates and status come from Jira; change them there and PMOS mirrors them.');
139
+ export { plural };
@@ -0,0 +1,218 @@
1
+ /* pm/c3-initiatives.css — the few rules console3.css has no class for, on the Initiatives page (c3-initiatives.js and its tab
2
+ * modules). The page draws with console3's own classes (.calwrap, .calbar, .lh, .seg, .brief.sbb, .card.sect, .nrow, .chips2,
3
+ * .chip2, .st, .tag, .pill, .chgl, .hbs > .hbr, .fold, .pgl); the rules here are the mockup's 34-initiatives.css under the `ini-`
4
+ * prefix, held to the R28 rules: corners 0, 6px or 8px (50% on a dot), font sizes 11, 12, 14, 16 or 20px or a --fs token, colours
5
+ * from the tokens only, and every :hover inside @media (hover: hover). Run-time geometry comes in as --in-… custom properties.
6
+ *
7
+ * Category colours: Discover var(--foreground), Plan var(--ring), Do var(--acc), Learn a mix of the accent and the muted foreground.
8
+ * Tones (.ini-t-…): acc, ok, warn (var(--warning)), bad (var(--destructive)), pm (a PMOS task, var(--ring)) and muted. */
9
+
10
+ /* ── a primitive button drawn as a console3 control: take the primitive's own box off, the console3 class draws it ── */
11
+ :where(.c3) .ini-b{height:auto;min-height:0;padding:0;border:0;background:transparent;font:inherit;color:inherit;line-height:inherit;white-space:normal;text-align:left;justify-content:flex-start;gap:6px}
12
+ .c3 .seg .ini-b,.c3 .ini-seg .ini-b{white-space:nowrap;line-height:1.2}
13
+ .c3 .ini-b.pill,.c3 .ini-b.chip2{white-space:nowrap}
14
+ .c3 .ini-b.tt{display:block;justify-content:flex-start;white-space:normal}
15
+ .c3 .ini-b:disabled{opacity:.45;cursor:default}
16
+
17
+ /* ── colours: category (data-cat) and tone ── */
18
+ .c3 .ini-c{--ini-c:var(--ring)}
19
+ .c3 .ini-c[data-cat="Discover"]{--ini-c:var(--foreground)}
20
+ .c3 .ini-c[data-cat="Plan"]{--ini-c:var(--ring)}
21
+ .c3 .ini-c[data-cat="Do"]{--ini-c:var(--acc)}
22
+ .c3 .ini-c[data-cat="Learn"]{--ini-c:color-mix(in oklab,var(--acc) 40%,var(--muted-foreground))}
23
+ .c3 .ini-t-acc{--c:var(--acc)}
24
+ .c3 .ini-t-ok{--c:color-mix(in oklab,var(--acc) 55%,var(--foreground))}
25
+ .c3 .ini-t-warn{--c:var(--warning)}
26
+ .c3 .ini-t-bad{--c:var(--destructive)}
27
+ .c3 .ini-t-pm{--c:var(--ring)}
28
+ .c3 .ini-t-muted{--c:var(--muted-foreground)}
29
+ .c3 .ini-k-bad{color:var(--destructive)}
30
+ .c3 .ini-k-warn{color:var(--warning)}
31
+ .c3 .ini-k-ok{color:var(--acc)}
32
+ .c3 .ini-late{color:var(--destructive)}
33
+
34
+ /* ── small parts ── */
35
+ .c3 .ini-sw{display:inline-block;width:8px;height:8px;border-radius:0;flex:none;background:var(--ini-c)}
36
+ .c3 .ini-note{font-size:var(--fs-2);line-height:1.45}
37
+ .c3 .ini-lbl{font-size:var(--fs-1);letter-spacing:.04em}
38
+ .c3 .ini-meta{font-size:var(--fs-2)}
39
+ .c3 .ini-sub{text-transform:none;letter-spacing:0;font-weight:400}
40
+ .c3 .ini-lhm{min-width:0}
41
+ .c3 .ini-seg{overflow-x:auto;max-width:100%;scrollbar-width:thin}
42
+ .c3 .ini-seg .bdg{margin-left:2px}
43
+ .c3 .ini-stack{display:flex;gap:2px;height:8px;border-radius:6px;overflow:hidden;background:var(--muted);margin-top:8px}
44
+ .c3 .ini-stack i{display:block;height:100%;flex:var(--in-f,1) 1 0;background:var(--ini-c)}
45
+ .c3 .ini-steps{gap:3px}
46
+ .c3 .ini-steps>span{margin-left:3px}
47
+ .c3 .ini-steps .ini-dot{display:inline-block;width:6px;height:6px;border-radius:50%;border:1px solid var(--ini-c);flex:none}
48
+ .c3 .ini-steps .ini-dot.d{background:var(--ini-c);opacity:.55}
49
+ .c3 .ini-steps .ini-dot.c{background:var(--ini-c);box-shadow:0 0 0 2px var(--accw)}
50
+ .c3 .ini-mini{display:inline-block;width:44px;height:4px;border-radius:6px;background:var(--muted);overflow:hidden;vertical-align:middle;flex:none}
51
+ .c3 .ini-mini u{display:block;height:100%;width:var(--in-w,0);background:var(--c);text-decoration:none}
52
+ .c3 .hbr .tk u{width:var(--in-w,2%)}
53
+ .c3 .hbr.ini-c{--c:var(--ini-c)}
54
+ .c3 .ini-hbb{display:grid;gap:4px;width:100%;color:inherit;font:inherit;cursor:pointer}
55
+ .c3 .ini-kv{display:grid;grid-template-columns:max-content minmax(0,1fr);gap:6px 14px;margin:0;font-size:var(--fs-3)}
56
+ .c3 .ini-kv dt{color:var(--muted-foreground);font:var(--fs-2) var(--mono)}
57
+ .c3 .ini-kv dd{margin:0;min-width:0;overflow-wrap:anywhere}
58
+ .c3 .ini-num{width:84px;flex:none}
59
+ .c3 .ini-up,.c3 .ini-down{display:inline-flex}
60
+ .c3 .ini-up{transform:rotate(-90deg)}
61
+ .c3 .ini-down{transform:rotate(90deg)}
62
+ .c3 .ini-sub-row{padding-left:var(--sp-5)}
63
+ .c3 .ini-pager{justify-content:flex-end}
64
+ .c3 .ini-chk{display:flex;gap:8px;align-items:flex-start;font-size:var(--fs-3);cursor:pointer}
65
+ .c3 .ini-chk input{margin-top:2px;flex:none}
66
+ .c3 .ini-chk.done span{color:var(--muted-foreground);text-decoration:line-through}
67
+ .c3 .ini-grow{flex:1 1 auto}
68
+ .c3 .ini-field{display:grid;gap:6px}
69
+ .c3 .ini-form{display:grid;gap:var(--sp-3)}
70
+ .c3 .ini-form textarea,.c3 .ini-form input[type=text]{width:100%}
71
+ .c3 .ini-flash{outline:2px solid var(--acc);outline-offset:2px}
72
+ .c3 .ini-pre{margin:0;white-space:pre-line}
73
+ .c3 .ini-confirm{display:grid;gap:8px}
74
+ .c3 .ini-err{color:var(--destructive);font-size:var(--fs-2)}
75
+
76
+ /* console3 draws a row in a briefing column with two tracks; a row here with actions keeps its third */
77
+ .c3 .ini .brief .nrow:has(>.acts>*){grid-template-columns:auto minmax(0,1fr) auto}
78
+ .c3 .ini-col{display:grid;gap:var(--sp-3);min-width:0}
79
+ .c3 .ini-p{margin:0}
80
+ .c3 .ini-text{display:grid}
81
+ .c3 .ini-text textarea{width:100%;min-height:72px;resize:vertical}
82
+ .c3 .ini-checks{display:grid;gap:6px}
83
+
84
+ /* ── the sprint burndown (stage records) ── */
85
+ .c3 .ini-burn{display:block;width:100%;max-width:420px;height:auto}
86
+ .c3 .ini-burn .ax{stroke:var(--border)}
87
+ .c3 .ini-burn .ideal{stroke:var(--muted-foreground);stroke-dasharray:3 3;fill:none}
88
+ .c3 .ini-burn .act{stroke:var(--acc);stroke-width:2;fill:none;stroke-linejoin:round}
89
+ .c3 .ini-burn .dot{fill:var(--acc)}
90
+ .c3 .ini-burn text{fill:var(--muted-foreground);font:var(--fs-1) var(--mono)}
91
+
92
+ /* ── the popover menu ── */
93
+ .c3 .ini-menu{position:fixed;left:var(--in-x,0);top:var(--in-y,0);z-index:60;width:min(300px,calc(100vw - 16px));max-height:min(420px,calc(100vh - 80px));overflow:auto;display:grid;gap:2px;padding:6px;border:1px solid var(--border);border-radius:8px;background:var(--card);box-shadow:var(--sh2)}
94
+ .c3 .ini-mlbl{padding:6px 8px 2px;font:var(--fs-1) var(--mono);color:var(--muted-foreground);letter-spacing:.04em}
95
+ .c3 .ini-mi{display:flex;align-items:center;gap:8px;width:100%;padding:6px 8px;border-radius:6px;font-size:var(--fs-3);color:var(--foreground)}
96
+ .c3 .ini-mi[aria-current="true"]{color:var(--muted-foreground)}
97
+
98
+ /* ── the kanban (Board tab) ── */
99
+ .c3 .ini-kb{display:grid;grid-template-columns:repeat(4,minmax(220px,1fr));gap:var(--sp-3);overflow-x:auto;align-items:start;padding-bottom:var(--sp-2)}
100
+ .c3 .ini-kb .nrow{grid-template-columns:auto minmax(0,1fr) auto;gap:8px}
101
+ .c3 .ini-kb .nrow .nb{padding-right:0}
102
+ .c3 .ini-kb .nrow.ini-dragging{opacity:.4}
103
+ .c3 [data-ini-col].over,.c3 [data-wf-row].over{outline:2px dashed var(--acc);outline-offset:-2px}
104
+ .c3 .ini-grip{cursor:grab}
105
+
106
+ /* ══ the timeline (Portfolio, Board, Initiative) ══ */
107
+ .c3 .ini-tl{position:relative;--lw:240px;display:grid;gap:var(--sp-2);min-width:0}
108
+ .c3 .ini-tlbar{display:flex;flex-wrap:wrap;gap:var(--sp-2);align-items:center;justify-content:space-between}
109
+ .c3 .ini-tlsc{overflow-x:auto;overflow-y:hidden;border:1px solid var(--border);border-radius:8px;background:var(--card);overscroll-behavior-x:contain;scrollbar-width:thin}
110
+ .c3 .ini-tlin{position:relative;width:calc(var(--lw) + var(--in-days) * var(--in-pd) * 1px);min-width:100%;padding-bottom:6px}
111
+ .c3 .ini-tl.panning .ini-tlsc{cursor:grabbing;user-select:none}
112
+ .c3 .ini-tl.dragging,.c3 .ini-tl.dragging *{cursor:grabbing;user-select:none}
113
+ .c3 .ini-tlgrid{position:absolute;inset:0 0 0 var(--lw);pointer-events:none}
114
+ .c3 .ini-tlgrid i{position:absolute;top:0;bottom:0;left:calc(var(--in-d0) * var(--in-pd) * 1px);width:1px;background:var(--border);opacity:.45}
115
+ .c3 .ini-tlgrid i.mj{opacity:1}
116
+ .c3 .ini-tlnow{position:absolute;top:0;bottom:0;left:calc(var(--lw) + var(--in-d0) * var(--in-pd) * 1px);width:2px;margin-left:-1px;background:var(--foreground);box-shadow:0 0 0 3px var(--accw);z-index:1;pointer-events:none}
117
+ .c3 .ini-tlnow b{position:absolute;top:19px;left:50%;transform:translateX(-50%);font:600 var(--fs-1)/1 var(--mono);padding:3px 5px;border-radius:6px;background:var(--foreground);color:var(--background);white-space:nowrap}
118
+ .c3 .ini-tlr{position:relative;display:grid;grid-template-columns:var(--lw) minmax(0,1fr);height:24px;align-items:stretch}
119
+ .c3 .ini-tll{position:sticky;left:0;z-index:2;display:flex;align-items:center;gap:4px;min-width:0;padding-right:8px;padding-left:calc(6px + var(--in-dep,0) * 14px);background:var(--card);border-right:1px solid var(--border);font:var(--fs-1)/1 var(--mono);color:var(--foreground)}
120
+ .c3 .ini-tltr{position:relative;min-width:0}
121
+ .c3 .ini-tlr.hl .ini-tltr{background:color-mix(in oklab,var(--acc) 7%,transparent)}
122
+ .c3 .ini-tlr.hl .ini-tll{background:color-mix(in oklab,var(--acc) 7%,var(--card))}
123
+ .c3 .ini-tlr.hl .ini-tlname{color:var(--acc)}
124
+ .c3 .ini-tlr.grp{height:28px}
125
+ .c3 .ini-tlr.grp .ini-tll{font-weight:600}
126
+ .c3 .ini-tlr.grp:not(:first-of-type){border-top:1px solid var(--border)}
127
+ .c3 .ini-tlr.ini .ini-tll{font-weight:500}
128
+ .c3 .ini-tlr.it .ini-tll{color:var(--muted-foreground)}
129
+ .c3 .ini-tlr.mrk{border-top:1px dashed var(--border);margin-top:4px}
130
+ .c3 .ini-tlhd{height:34px;border-bottom:1px solid var(--border)}
131
+ .c3 .ini-tlhd .ini-tll{padding-left:8px;text-transform:uppercase;letter-spacing:.06em}
132
+ .c3 .ini-tlt{position:absolute;left:calc(var(--in-d0) * var(--in-pd) * 1px);bottom:4px;padding-left:3px;font:var(--fs-1)/1 var(--mono);color:var(--muted-foreground);white-space:nowrap}
133
+ .c3 .ini-tlt.mj{top:5px;bottom:auto;font-weight:600;color:var(--foreground)}
134
+ .c3 .ini-tl.zooming .ini-tlt:not(.mj){opacity:0}
135
+ .c3 .ini-tlc,.c3 .ini-tlo{display:inline-grid;place-items:center;width:18px;height:18px;flex:none;border-radius:6px;color:var(--muted-foreground);cursor:pointer}
136
+ .c3 .ini-tlc svg{transition:transform .22s ease}
137
+ .c3 .ini-tlc[aria-expanded="true"] svg{transform:rotate(90deg)}
138
+ .c3 .ini-tlo{margin-left:auto;opacity:0}
139
+ .c3 .ini-tlr.hl .ini-tlo,.c3 .ini-tlo:focus-visible{opacity:1}
140
+ .c3 .ini-tlc0{width:18px;flex:none}
141
+ .c3 .ini-tlx{display:flex;align-items:center;gap:5px;min-width:0;flex:1 1 auto;cursor:pointer;font:inherit;color:inherit}
142
+ .c3 span.ini-tlx{cursor:default}
143
+ .c3 .ini-tlkey{flex:none;color:var(--muted-foreground)}
144
+ .c3 .ini-tlsrc{flex:none;font-size:var(--fs-1);letter-spacing:.04em;padding:0 3px;border-radius:0;color:var(--muted-foreground);background:var(--muted)}
145
+ .c3 .ini-tlsrc.j{display:inline-grid;place-items:center;padding:0 1px;color:var(--acc);background:color-mix(in oklab,var(--acc) 12%,transparent)}
146
+ .c3 .ini-tlsrc svg{width:11px;height:11px}
147
+ .c3 .ini-tlname{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}
148
+ .c3 .ini-tlb{--bh:8px;position:absolute;top:calc(50% - var(--bh) / 2);height:var(--bh);left:calc(var(--in-d0) * var(--in-pd) * 1px);width:max(4px,calc((var(--in-d1) - var(--in-d0)) * var(--in-pd) * 1px));border-radius:6px;background:color-mix(in oklab,var(--c) 26%,transparent);box-shadow:inset 0 0 0 1px color-mix(in oklab,var(--c) 45%,transparent);overflow:hidden;cursor:pointer;transform-origin:left center;outline:none;transition:box-shadow .15s,filter .15s;z-index:1}
149
+ .c3 .ini-tlb>i{position:absolute;inset:0 auto 0 0;width:var(--in-w,0);background:var(--c);border-radius:0}
150
+ .c3 .ini-tlb.ini{--bh:10px}
151
+ .c3 .ini-tlb.grp{--bh:4px}
152
+ .c3 .ini-tlb.it{--bh:6px}
153
+ .c3 .ini-tlb.pm{cursor:grab;background-image:repeating-linear-gradient(135deg,transparent 0 4px,color-mix(in oklab,var(--c) 18%,transparent) 4px 6px)}
154
+ .c3 .ini-tlb.jl{overflow:visible}
155
+ .c3 .ini-tlb .lk{position:absolute;left:calc(100% + 3px);top:50%;transform:translateY(-50%);display:inline-grid;color:var(--muted-foreground);opacity:0;transition:opacity .15s}
156
+ .c3 .ini-tlb .lk svg{width:10px;height:10px}
157
+ .c3 .ini-tlr.hl .ini-tlb .lk,.c3 .ini-tlb.nope .lk{opacity:1}
158
+ .c3 .ini-tlr.hl .ini-tlb,.c3 .ini-tlb:focus-visible{box-shadow:inset 0 0 0 1px var(--c),0 0 0 3px color-mix(in oklab,var(--c) 22%,transparent)}
159
+ .c3 .ini-tlb.drag{cursor:grabbing;box-shadow:inset 0 0 0 1px var(--c),0 0 0 3px var(--accw),var(--sh2);z-index:4}
160
+ .c3 .ini-tlb.nope{animation:ini-shake .36s ease}
161
+ .c3 .ini-tl.zooming .ini-tlb{transition:none}
162
+ .c3 .ini-tlm{position:absolute;top:50%;left:calc(var(--in-d0) * var(--in-pd) * 1px);width:9px;height:9px;margin:-5px 0 0 -5px;border:1.5px solid var(--acc);border-radius:0;background:var(--card);cursor:pointer;z-index:1;transition:transform .15s}
163
+ .c3 .ini-tlm.release{transform:rotate(45deg)}
164
+ .c3 .ini-tlm.milestone{border-color:var(--warning)}
165
+ .c3 .ini-tlk{display:grid;grid-template-rows:0fr;transition:grid-template-rows .28s cubic-bezier(.2,.7,.2,1)}
166
+ .c3 .ini-tlk>div{min-height:0;overflow-y:clip;overflow-x:visible;opacity:0;transition:opacity .2s}
167
+ .c3 .ini-tlk.open{grid-template-rows:1fr}
168
+ .c3 .ini-tlk.open>div{opacity:1}
169
+ .c3 .ini-tlk.grow .ini-tlb{animation:ini-grow .42s cubic-bezier(.2,.7,.2,1) both;animation-delay:calc(var(--in-j,0) * 28ms + 80ms)}
170
+ .c3 .ini-tl.anim .ini-tlb{animation:ini-grow .6s cubic-bezier(.2,.7,.2,1) both;animation-delay:calc(var(--in-i,0) * 18ms + 60ms)}
171
+ .c3 .ini-tl.anim .ini-tlnow{animation:ini-fade .5s .2s both}
172
+ @keyframes ini-grow{from{transform:scaleX(0);opacity:0}40%{opacity:1}to{transform:scaleX(1);opacity:1}}
173
+ @keyframes ini-fade{from{opacity:0}to{opacity:1}}
174
+ @keyframes ini-shake{0%,100%{translate:0}20%{translate:-4px}40%{translate:4px}60%{translate:-3px}80%{translate:2px}}
175
+ .c3 .ini-tip{position:absolute;left:var(--in-x,0);top:var(--in-y,0);z-index:20;min-width:200px;max-width:280px;display:grid;gap:3px;padding:8px 10px;border:1px solid var(--border);border-radius:8px;background:var(--card);box-shadow:var(--sh2);font:var(--fs-1)/1.4 var(--mono);color:var(--foreground);pointer-events:none;opacity:0;translate:0 4px;transition:opacity .12s,translate .12s}
176
+ .c3 .ini-tip.on{opacity:1;translate:0 0}
177
+ .c3 .ini-tip b{font:600 var(--fs-2)/1.3 var(--sans)}
178
+ .c3 .ini-tip>div{display:flex;align-items:center;gap:5px}
179
+ .c3 .ini-tip svg{width:11px;height:11px;flex:none;color:var(--muted-foreground)}
180
+ .c3 .ini-tip .dim{color:var(--muted-foreground)}
181
+ .c3 .ini-tlmv{padding:0 4px;border-radius:0;background:var(--accw);color:var(--acc);font-weight:600}
182
+ .c3 .ini-pgl .ini-fg{color:var(--foreground)}
183
+ .c3 .ini-tlkeyi{display:inline-block;flex:none;vertical-align:middle;margin-right:4px}
184
+ .c3 .ini-tlkeyi.now{width:2px;height:11px;background:var(--foreground)}
185
+ .c3 .ini-tlkeyi.bar{width:14px;height:5px;border-radius:6px;background:var(--acc)}
186
+ .c3 .ini-tlkeyi.pm{width:14px;height:6px;border-radius:6px;background:color-mix(in oklab,var(--ring) 40%,transparent);box-shadow:inset 0 0 0 1px var(--ring)}
187
+ .c3 .ini-tlkeyi.rel{width:7px;height:7px;border:1.5px solid var(--acc);transform:rotate(45deg)}
188
+ .c3 .ini-tlkeyi.ms{width:8px;height:8px;border:1.5px solid var(--warning)}
189
+
190
+ /* ── hover (a pointer that can hover) ── */
191
+ @media (hover:hover){
192
+ .c3 .ini-mi:hover{background:var(--muted)}
193
+ .c3 .ini-tlr:not(.ini-tlhd):hover .ini-tltr{background:color-mix(in oklab,var(--acc) 7%,transparent)}
194
+ .c3 .ini-tlr:not(.ini-tlhd):hover .ini-tll{background:color-mix(in oklab,var(--acc) 7%,var(--card))}
195
+ .c3 .ini-tlc:hover,.c3 .ini-tlo:hover{background:var(--muted);color:var(--foreground)}
196
+ .c3 .ini-tlr:hover .ini-tlo{opacity:1}
197
+ .c3 .ini-tlx:hover .ini-tlname{color:var(--acc)}
198
+ .c3 span.ini-tlx:hover .ini-tlname{color:inherit}
199
+ .c3 .ini-tlr:hover .ini-tlb .lk{opacity:1}
200
+ .c3 .ini-tlr:hover .ini-tlb{box-shadow:inset 0 0 0 1px var(--c),0 0 0 3px color-mix(in oklab,var(--c) 22%,transparent)}
201
+ .c3 .ini-tlm.release:hover{transform:rotate(45deg) scale(1.35);background:var(--acc)}
202
+ .c3 .ini-tlm.milestone:hover{transform:scale(1.35);background:var(--warning)}
203
+ .c3 .ini-hbb:hover .l span{color:var(--acc)}
204
+ }
205
+
206
+ /* ── touch: a bigger target for the page's own controls ── */
207
+ @media (pointer:coarse){
208
+ .c3 .ini-b.pill,.c3 .ini-seg .ini-b,.c3 .ini-mi{min-height:44px}
209
+ .c3 .ini-b.chip2.lk{min-height:32px}
210
+ }
211
+
212
+ @media (max-width:760px){
213
+ .c3 .ini-tl{--lw:132px}
214
+ .c3 .ini-tlkey{display:none}
215
+ }
216
+ @media (prefers-reduced-motion:reduce){
217
+ .c3 .ini-tl *,.c3 .ini-tip{animation:none;transition:none}
218
+ }