@mj-biz-apps/contracts-ng 0.2.0 → 0.4.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 (38) hide show
  1. package/dist/lib/form-panels/contract-form.panels.d.ts +111 -0
  2. package/dist/lib/form-panels/contract-form.panels.d.ts.map +1 -0
  3. package/dist/lib/form-panels/contract-form.panels.js +1002 -0
  4. package/dist/lib/form-panels/contract-form.panels.js.map +1 -0
  5. package/dist/lib/form-panels/contract-form.policy.d.ts +5 -0
  6. package/dist/lib/form-panels/contract-form.policy.d.ts.map +1 -0
  7. package/dist/lib/form-panels/contract-form.policy.js +33 -0
  8. package/dist/lib/form-panels/contract-form.policy.js.map +1 -0
  9. package/dist/lib/form-panels/contract.panels.d.ts +55 -3
  10. package/dist/lib/form-panels/contract.panels.d.ts.map +1 -1
  11. package/dist/lib/form-panels/contract.panels.js +619 -362
  12. package/dist/lib/form-panels/contract.panels.js.map +1 -1
  13. package/dist/lib/form-panels/organization.panels.d.ts +4 -13
  14. package/dist/lib/form-panels/organization.panels.d.ts.map +1 -1
  15. package/dist/lib/form-panels/organization.panels.js +54 -62
  16. package/dist/lib/form-panels/organization.panels.js.map +1 -1
  17. package/dist/lib/form-panels/person.panels.d.ts +14 -0
  18. package/dist/lib/form-panels/person.panels.d.ts.map +1 -0
  19. package/dist/lib/form-panels/person.panels.js +108 -0
  20. package/dist/lib/form-panels/person.panels.js.map +1 -0
  21. package/dist/lib/form-panels/supersede.panel.d.ts +29 -3
  22. package/dist/lib/form-panels/supersede.panel.d.ts.map +1 -1
  23. package/dist/lib/form-panels/supersede.panel.js +81 -23
  24. package/dist/lib/form-panels/supersede.panel.js.map +1 -1
  25. package/dist/lib/pages/contracts-dashboard.page.d.ts +37 -17
  26. package/dist/lib/pages/contracts-dashboard.page.d.ts.map +1 -1
  27. package/dist/lib/pages/contracts-dashboard.page.js +365 -63
  28. package/dist/lib/pages/contracts-dashboard.page.js.map +1 -1
  29. package/dist/lib/sections/section-shell.component.js +2 -2
  30. package/dist/public-api.d.ts +3 -1
  31. package/dist/public-api.d.ts.map +1 -1
  32. package/dist/public-api.js +15 -4
  33. package/dist/public-api.js.map +1 -1
  34. package/package.json +2 -3
  35. package/dist/lib/form-panels/record-files.panel.d.ts +0 -151
  36. package/dist/lib/form-panels/record-files.panel.d.ts.map +0 -1
  37. package/dist/lib/form-panels/record-files.panel.js +0 -1046
  38. package/dist/lib/form-panels/record-files.panel.js.map +0 -1
@@ -14,7 +14,7 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
14
14
  *
15
15
  * 1. **The panels were not `<mj-collapsible-panel>`s.** They were hand-rolled `<div class="card">`s, so
16
16
  * they had none of MJ's panel chrome — hence text with no card behind it, cards touching each other,
17
- * and content overflowing. The one panel that looked right (Documents) was the one panel built on
17
+ * and content overflowing. The panels that looked right were the ones built on
18
18
  * the real component.
19
19
  * 2a. **`contributionKey` MUST EQUAL the panel's own `SectionKey`, and must DIFFER from
20
20
  * `replacesSectionKey`.** This is the rule that took longest to find, because breaking it fails
@@ -42,84 +42,219 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
42
42
  import { ChangeDetectorRef, Component, ViewEncapsulation, inject } from '@angular/core';
43
43
  import { CommonModule } from '@angular/common';
44
44
  import { FormsModule } from '@angular/forms';
45
+ import { CompositeKey } from '@memberjunction/core';
46
+ import { UserInfoEngine } from '@memberjunction/core-entities';
45
47
  import { RegisterClassEx } from '@memberjunction/global';
46
48
  import { BaseFormPanel, BaseFormsModule } from '@memberjunction/ng-base-forms';
47
49
  import { HierarchyTreeComponent } from '@memberjunction/ng-hierarchy-tree';
48
- import { NavigationService } from '@memberjunction/ng-shared';
49
- import { MJC_ENTITIES } from '../data/entity-names';
50
+ import { MJC_ENTITIES, MJC_FOREIGN_ENTITIES } from '../data/entity-names';
50
51
  import * as i0 from "@angular/core";
51
- import * as i1 from "@angular/common";
52
- import * as i2 from "@angular/forms";
53
- import * as i3 from "@memberjunction/ng-base-forms";
54
- function MJCContractHeroPanel_Conditional_10_Template(rf, ctx) { if (rf & 1) {
55
- i0.ɵɵdomElementStart(0, "span", 8);
52
+ import * as i1 from "@memberjunction/ng-base-forms";
53
+ import * as i2 from "@angular/common";
54
+ import * as i3 from "@angular/forms";
55
+ function MJCContractHeroPanel_Conditional_4_Template(rf, ctx) { if (rf & 1) {
56
+ i0.ɵɵelement(0, "span", 4);
57
+ } if (rf & 2) {
58
+ const ctx_r0 = i0.ɵɵnextContext();
59
+ i0.ɵɵproperty("title", ctx_r0.State);
60
+ i0.ɵɵattribute("data-tone", ctx_r0.StatusTone);
61
+ } }
62
+ function MJCContractHeroPanel_Conditional_9_Template(rf, ctx) { if (rf & 1) {
63
+ i0.ɵɵelementStart(0, "div", 8);
56
64
  i0.ɵɵtext(1);
57
- i0.ɵɵdomElementEnd();
65
+ i0.ɵɵelementEnd();
66
+ } if (rf & 2) {
67
+ const ctx_r0 = i0.ɵɵnextContext();
68
+ i0.ɵɵadvance();
69
+ i0.ɵɵtextInterpolate(ctx_r0.Record.ContractNumber);
70
+ } }
71
+ function MJCContractHeroPanel_Conditional_16_Template(rf, ctx) { if (rf & 1) {
72
+ i0.ɵɵelementStart(0, "span", 12);
73
+ i0.ɵɵtext(1);
74
+ i0.ɵɵelementEnd();
58
75
  } if (rf & 2) {
59
76
  const ctx_r0 = i0.ɵɵnextContext();
60
77
  i0.ɵɵadvance();
61
78
  i0.ɵɵtextInterpolate(ctx_r0.TypeName);
62
79
  } }
63
- function MJCContractHeroPanel_Conditional_11_Template(rf, ctx) { if (rf & 1) {
64
- i0.ɵɵdomElementStart(0, "span", 9);
65
- i0.ɵɵdomElement(1, "i", 19);
80
+ function MJCContractHeroPanel_Conditional_17_Template(rf, ctx) { if (rf & 1) {
81
+ i0.ɵɵelementStart(0, "span", 13);
82
+ i0.ɵɵelement(1, "i", 16);
66
83
  i0.ɵɵtext(2, " Agreement modified ");
67
- i0.ɵɵdomElementEnd();
84
+ i0.ɵɵelementEnd();
68
85
  } }
69
- function MJCContractHeroPanel_Conditional_12_Template(rf, ctx) { if (rf & 1) {
70
- i0.ɵɵdomElementStart(0, "span", 10);
71
- i0.ɵɵdomElement(1, "i", 20);
86
+ function MJCContractHeroPanel_Conditional_18_Template(rf, ctx) { if (rf & 1) {
87
+ i0.ɵɵelementStart(0, "span", 14);
88
+ i0.ɵɵelement(1, "i", 17);
72
89
  i0.ɵɵtext(2, " Awaiting document ");
73
- i0.ɵɵdomElementEnd();
90
+ i0.ɵɵelementEnd();
74
91
  } }
75
- function MJCContractHeroPanel_Conditional_24_Template(rf, ctx) { if (rf & 1) {
76
- i0.ɵɵdomElementStart(0, "span");
77
- i0.ɵɵtext(1, "Contact: ");
78
- i0.ɵɵdomElementStart(2, "strong");
79
- i0.ɵɵtext(3);
80
- i0.ɵɵdomElementEnd()();
92
+ function MJCContractHeroPanel_Conditional_21_Conditional_9_Template(rf, ctx) { if (rf & 1) {
93
+ const _r2 = i0.ɵɵgetCurrentView();
94
+ i0.ɵɵelementStart(0, "button", 26);
95
+ i0.ɵɵlistener("click", function MJCContractHeroPanel_Conditional_21_Conditional_9_Template_button_click_0_listener($event) { i0.ɵɵrestoreView(_r2); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.OpenCustomer($event)); });
96
+ i0.ɵɵtext(1);
97
+ i0.ɵɵelementEnd();
81
98
  } if (rf & 2) {
82
- const ctx_r0 = i0.ɵɵnextContext();
83
- i0.ɵɵadvance(3);
84
- i0.ɵɵtextInterpolate(ctx_r0.ContactName);
99
+ const ctx_r0 = i0.ɵɵnextContext(2);
100
+ i0.ɵɵadvance();
101
+ i0.ɵɵtextInterpolate(ctx_r0.CustomerName);
85
102
  } }
86
- function MJCContractHeroPanel_Conditional_32_Template(rf, ctx) { if (rf & 1) {
87
- i0.ɵɵdomElementStart(0, "span", 17);
88
- i0.ɵɵtext(1, "not signed yet");
89
- i0.ɵɵdomElementEnd();
103
+ function MJCContractHeroPanel_Conditional_21_Conditional_10_Template(rf, ctx) { if (rf & 1) {
104
+ i0.ɵɵelementStart(0, "span", 21);
105
+ i0.ɵɵtext(1);
106
+ i0.ɵɵelementEnd();
107
+ } if (rf & 2) {
108
+ const ctx_r0 = i0.ɵɵnextContext(2);
109
+ i0.ɵɵadvance();
110
+ i0.ɵɵtextInterpolate(ctx_r0.CustomerName || "\u2014");
90
111
  } }
91
- function MJCContractHeroPanel_Conditional_45_Template(rf, ctx) { if (rf & 1) {
92
- i0.ɵɵdomElementStart(0, "span", 17);
112
+ function MJCContractHeroPanel_Conditional_21_Conditional_14_Template(rf, ctx) { if (rf & 1) {
113
+ const _r3 = i0.ɵɵgetCurrentView();
114
+ i0.ɵɵelementStart(0, "button", 26);
115
+ i0.ɵɵlistener("click", function MJCContractHeroPanel_Conditional_21_Conditional_14_Template_button_click_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.OpenContact($event)); });
93
116
  i0.ɵɵtext(1);
94
- i0.ɵɵdomElementEnd();
117
+ i0.ɵɵelementEnd();
95
118
  } if (rf & 2) {
96
- const ctx_r0 = i0.ɵɵnextContext();
119
+ const ctx_r0 = i0.ɵɵnextContext(2);
120
+ i0.ɵɵadvance();
121
+ i0.ɵɵtextInterpolate(ctx_r0.ContactName);
122
+ } }
123
+ function MJCContractHeroPanel_Conditional_21_Conditional_15_Template(rf, ctx) { if (rf & 1) {
124
+ i0.ɵɵelementStart(0, "span", 21);
125
+ i0.ɵɵtext(1);
126
+ i0.ɵɵelementEnd();
127
+ } if (rf & 2) {
128
+ const ctx_r0 = i0.ɵɵnextContext(2);
129
+ i0.ɵɵadvance();
130
+ i0.ɵɵtextInterpolate(ctx_r0.ContactName || "\u2014");
131
+ } }
132
+ function MJCContractHeroPanel_Conditional_21_Conditional_39_Template(rf, ctx) { if (rf & 1) {
133
+ const _r4 = i0.ɵɵgetCurrentView();
134
+ i0.ɵɵelementStart(0, "div", 19)(1, "span", 20);
135
+ i0.ɵɵtext(2);
136
+ i0.ɵɵelementEnd();
137
+ i0.ɵɵelementStart(3, "button", 26);
138
+ i0.ɵɵlistener("click", function MJCContractHeroPanel_Conditional_21_Conditional_39_Template_button_click_3_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r0 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r0.OpenSource($event)); });
139
+ i0.ɵɵtext(4);
140
+ i0.ɵɵelementEnd()();
141
+ } if (rf & 2) {
142
+ const ctx_r0 = i0.ɵɵnextContext(2);
143
+ i0.ɵɵadvance(2);
144
+ i0.ɵɵtextInterpolate(ctx_r0.SourceLabel);
145
+ i0.ɵɵadvance();
146
+ i0.ɵɵattribute("aria-label", "Open " + (ctx_r0.SourceName || "the source record"));
97
147
  i0.ɵɵadvance();
148
+ i0.ɵɵtextInterpolate1(" ", ctx_r0.SourceName || "Open", " ");
149
+ } }
150
+ function MJCContractHeroPanel_Conditional_21_Conditional_40_Template(rf, ctx) { if (rf & 1) {
151
+ i0.ɵɵelementStart(0, "div", 23)(1, "span", 20);
152
+ i0.ɵɵtext(2, "Term");
153
+ i0.ɵɵelementEnd();
154
+ i0.ɵɵelementStart(3, "span", 27);
155
+ i0.ɵɵtext(4);
156
+ i0.ɵɵelementEnd()();
157
+ } if (rf & 2) {
158
+ const ctx_r0 = i0.ɵɵnextContext(2);
159
+ i0.ɵɵadvance(4);
98
160
  i0.ɵɵtextInterpolate(ctx_r0.EndsInText);
99
161
  } }
100
- function MJCContractHeroPanel_Conditional_51_Template(rf, ctx) { if (rf & 1) {
101
- i0.ɵɵdomElementStart(0, "span", 17);
102
- i0.ɵɵtext(1, "no standard terms referenced");
103
- i0.ɵɵdomElementEnd();
162
+ function MJCContractHeroPanel_Conditional_21_Conditional_41_Template(rf, ctx) { if (rf & 1) {
163
+ i0.ɵɵelementStart(0, "div", 24)(1, "div", 28);
164
+ i0.ɵɵelement(2, "mj-form-field", 29);
165
+ i0.ɵɵelementEnd()();
166
+ } if (rf & 2) {
167
+ const ctx_r0 = i0.ɵɵnextContext(2);
168
+ i0.ɵɵadvance(2);
169
+ i0.ɵɵproperty("Record", ctx_r0.Record)("ShowLabel", true)("EditMode", ctx_r0.EditMode)("FormContext", ctx_r0.FormContext);
104
170
  } }
105
- function MJCContractHeroPanel_Conditional_57_Template(rf, ctx) { if (rf & 1) {
106
- i0.ɵɵdomElementStart(0, "span", 17);
107
- i0.ɵɵtext(1, "entered directly");
108
- i0.ɵɵdomElementEnd();
171
+ function MJCContractHeroPanel_Conditional_21_Conditional_42_Template(rf, ctx) { if (rf & 1) {
172
+ i0.ɵɵelementStart(0, "div", 25);
173
+ i0.ɵɵtext(1, "Contract number is assigned on save.");
174
+ i0.ɵɵelementEnd();
109
175
  } }
110
- function MJCContractHeroPanel_Conditional_58_Template(rf, ctx) { if (rf & 1) {
111
- i0.ɵɵdomElementStart(0, "div", 18);
112
- i0.ɵɵtext(1, " The contract number is minted on first save, from a counter taken under a lock \u2014 so it cannot collide with another contract created at the same moment. ");
113
- i0.ɵɵdomElementEnd();
176
+ function MJCContractHeroPanel_Conditional_21_Template(rf, ctx) { if (rf & 1) {
177
+ i0.ɵɵelementStart(0, "div", 18)(1, "div", 19)(2, "span", 20);
178
+ i0.ɵɵtext(3, "Company");
179
+ i0.ɵɵelementEnd();
180
+ i0.ɵɵelementStart(4, "span", 21);
181
+ i0.ɵɵtext(5);
182
+ i0.ɵɵelementEnd()();
183
+ i0.ɵɵelementStart(6, "div", 19)(7, "span", 20);
184
+ i0.ɵɵtext(8, "Customer");
185
+ i0.ɵɵelementEnd();
186
+ i0.ɵɵconditionalCreate(9, MJCContractHeroPanel_Conditional_21_Conditional_9_Template, 2, 1, "button", 22)(10, MJCContractHeroPanel_Conditional_21_Conditional_10_Template, 2, 1, "span", 21);
187
+ i0.ɵɵelementEnd();
188
+ i0.ɵɵelementStart(11, "div", 19)(12, "span", 20);
189
+ i0.ɵɵtext(13, "Contact");
190
+ i0.ɵɵelementEnd();
191
+ i0.ɵɵconditionalCreate(14, MJCContractHeroPanel_Conditional_21_Conditional_14_Template, 2, 1, "button", 22)(15, MJCContractHeroPanel_Conditional_21_Conditional_15_Template, 2, 1, "span", 21);
192
+ i0.ɵɵelementEnd();
193
+ i0.ɵɵelementStart(16, "div", 19)(17, "span", 20);
194
+ i0.ɵɵtext(18, "Executed");
195
+ i0.ɵɵelementEnd();
196
+ i0.ɵɵelementStart(19, "span", 21);
197
+ i0.ɵɵtext(20);
198
+ i0.ɵɵpipe(21, "date");
199
+ i0.ɵɵelementEnd()();
200
+ i0.ɵɵelementStart(22, "div", 19)(23, "span", 20);
201
+ i0.ɵɵtext(24, "Effective");
202
+ i0.ɵɵelementEnd();
203
+ i0.ɵɵelementStart(25, "span", 21);
204
+ i0.ɵɵtext(26);
205
+ i0.ɵɵpipe(27, "date");
206
+ i0.ɵɵelementEnd()();
207
+ i0.ɵɵelementStart(28, "div", 19)(29, "span", 20);
208
+ i0.ɵɵtext(30, "Term ends");
209
+ i0.ɵɵelementEnd();
210
+ i0.ɵɵelementStart(31, "span", 21);
211
+ i0.ɵɵtext(32);
212
+ i0.ɵɵpipe(33, "date");
213
+ i0.ɵɵelementEnd()();
214
+ i0.ɵɵelementStart(34, "div", 19)(35, "span", 20);
215
+ i0.ɵɵtext(36, "Agreement");
216
+ i0.ɵɵelementEnd();
217
+ i0.ɵɵelementStart(37, "span", 21);
218
+ i0.ɵɵtext(38);
219
+ i0.ɵɵelementEnd()();
220
+ i0.ɵɵconditionalCreate(39, MJCContractHeroPanel_Conditional_21_Conditional_39_Template, 5, 3, "div", 19);
221
+ i0.ɵɵelementEnd();
222
+ i0.ɵɵconditionalCreate(40, MJCContractHeroPanel_Conditional_21_Conditional_40_Template, 5, 1, "div", 23);
223
+ i0.ɵɵconditionalCreate(41, MJCContractHeroPanel_Conditional_21_Conditional_41_Template, 3, 4, "div", 24);
224
+ i0.ɵɵconditionalCreate(42, MJCContractHeroPanel_Conditional_21_Conditional_42_Template, 2, 0, "div", 25);
225
+ } if (rf & 2) {
226
+ const ctx_r0 = i0.ɵɵnextContext();
227
+ i0.ɵɵadvance(5);
228
+ i0.ɵɵtextInterpolate(ctx_r0.CompanyName || "\u2014");
229
+ i0.ɵɵadvance(4);
230
+ i0.ɵɵconditional(ctx_r0.Record.CustomerOrganizationID && ctx_r0.CustomerName ? 9 : 10);
231
+ i0.ɵɵadvance(5);
232
+ i0.ɵɵconditional(ctx_r0.Record.PrimaryContactPersonID && ctx_r0.ContactName ? 14 : 15);
233
+ i0.ɵɵadvance(6);
234
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(21, 11, ctx_r0.Record.ExecutedDate, "d MMM y") || "\u2014");
235
+ i0.ɵɵadvance(6);
236
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(27, 14, ctx_r0.Record.EffectiveDate, "d MMM y") || "\u2014");
237
+ i0.ɵɵadvance(6);
238
+ i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(33, 17, ctx_r0.Record.EndDate, "d MMM y") || "\u2014");
239
+ i0.ɵɵadvance(6);
240
+ i0.ɵɵtextInterpolate(ctx_r0.TemplateName || "\u2014");
241
+ i0.ɵɵadvance();
242
+ i0.ɵɵconditional(ctx_r0.HasSource ? 39 : -1);
243
+ i0.ɵɵadvance();
244
+ i0.ɵɵconditional(ctx_r0.DaysToEnd !== null ? 40 : -1);
245
+ i0.ɵɵadvance();
246
+ i0.ɵɵconditional(ctx_r0.EditMode ? 41 : -1);
247
+ i0.ɵɵadvance();
248
+ i0.ɵɵconditional(!ctx_r0.Record.ContractNumber ? 42 : -1);
114
249
  } }
115
250
  function MJCContractRenewalPanel_Conditional_8_Template(rf, ctx) { if (rf & 1) {
116
251
  const _r1 = i0.ɵɵgetCurrentView();
117
- i0.ɵɵelementStart(0, "select", 13);
252
+ i0.ɵɵelementStart(0, "select", 12);
118
253
  i0.ɵɵlistener("ngModelChange", function MJCContractRenewalPanel_Conditional_8_Template_select_ngModelChange_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.Set("AutoRenew", $event)); });
119
- i0.ɵɵelementStart(1, "option", 14);
254
+ i0.ɵɵelementStart(1, "option", 13);
120
255
  i0.ɵɵtext(2, "Yes");
121
256
  i0.ɵɵelementEnd();
122
- i0.ɵɵelementStart(3, "option", 14);
257
+ i0.ɵɵelementStart(3, "option", 13);
123
258
  i0.ɵɵtext(4, "No");
124
259
  i0.ɵɵelementEnd()();
125
260
  } if (rf & 2) {
@@ -139,31 +274,26 @@ function MJCContractRenewalPanel_Conditional_9_Template(rf, ctx) { if (rf & 1) {
139
274
  i0.ɵɵadvance();
140
275
  i0.ɵɵtextInterpolate(ctx_r1.Record.AutoRenew ? "Yes" : "No");
141
276
  } }
142
- function MJCContractRenewalPanel_Conditional_10_Template(rf, ctx) { if (rf & 1) {
143
- i0.ɵɵelementStart(0, "div", 7);
144
- i0.ɵɵtext(1, "someone must act for this to continue");
145
- i0.ɵɵelementEnd();
146
- } }
147
- function MJCContractRenewalPanel_Conditional_14_Template(rf, ctx) { if (rf & 1) {
277
+ function MJCContractRenewalPanel_Conditional_13_Template(rf, ctx) { if (rf & 1) {
148
278
  const _r3 = i0.ɵɵgetCurrentView();
149
- i0.ɵɵelementStart(0, "input", 15);
150
- i0.ɵɵlistener("ngModelChange", function MJCContractRenewalPanel_Conditional_14_Template_input_ngModelChange_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.Set("RenewalNoticeDays", $event)); });
279
+ i0.ɵɵelementStart(0, "input", 14);
280
+ i0.ɵɵlistener("ngModelChange", function MJCContractRenewalPanel_Conditional_13_Template_input_ngModelChange_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.Set("RenewalNoticeDays", $event)); });
151
281
  i0.ɵɵelementEnd();
152
282
  } if (rf & 2) {
153
283
  const ctx_r1 = i0.ɵɵnextContext();
154
284
  i0.ɵɵproperty("ngModel", ctx_r1.Record.RenewalNoticeDays);
155
285
  } }
156
- function MJCContractRenewalPanel_Conditional_15_Template(rf, ctx) { if (rf & 1) {
286
+ function MJCContractRenewalPanel_Conditional_14_Template(rf, ctx) { if (rf & 1) {
157
287
  i0.ɵɵelementStart(0, "div", 6);
158
288
  i0.ɵɵtext(1);
159
289
  i0.ɵɵelementEnd();
160
290
  } if (rf & 2) {
161
291
  const ctx_r1 = i0.ɵɵnextContext();
162
292
  i0.ɵɵadvance();
163
- i0.ɵɵtextInterpolate(ctx_r1.Record.RenewalNoticeDays ? ctx_r1.Record.RenewalNoticeDays + " days" : "\u2014");
293
+ i0.ɵɵtextInterpolate(ctx_r1.Days(ctx_r1.Record.RenewalNoticeDays));
164
294
  } }
165
- function MJCContractRenewalPanel_Conditional_16_Template(rf, ctx) { if (rf & 1) {
166
- i0.ɵɵelementStart(0, "div", 7);
295
+ function MJCContractRenewalPanel_Conditional_17_Template(rf, ctx) { if (rf & 1) {
296
+ i0.ɵɵelementStart(0, "div", 8);
167
297
  i0.ɵɵtext(1);
168
298
  i0.ɵɵpipe(2, "date");
169
299
  i0.ɵɵelementEnd();
@@ -172,55 +302,55 @@ function MJCContractRenewalPanel_Conditional_16_Template(rf, ctx) { if (rf & 1)
172
302
  i0.ɵɵadvance();
173
303
  i0.ɵɵtextInterpolate1("deadline: ", i0.ɵɵpipeBind2(2, 1, ctx_r1.NoticeDeadline, "d MMM y"));
174
304
  } }
175
- function MJCContractRenewalPanel_Conditional_20_Template(rf, ctx) { if (rf & 1) {
305
+ function MJCContractRenewalPanel_Conditional_21_Template(rf, ctx) { if (rf & 1) {
176
306
  const _r4 = i0.ɵɵgetCurrentView();
177
- i0.ɵɵelementStart(0, "input", 16);
178
- i0.ɵɵlistener("ngModelChange", function MJCContractRenewalPanel_Conditional_20_Template_input_ngModelChange_0_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.Set("CancellationWindowDays", $event)); });
307
+ i0.ɵɵelementStart(0, "input", 15);
308
+ i0.ɵɵlistener("ngModelChange", function MJCContractRenewalPanel_Conditional_21_Template_input_ngModelChange_0_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.Set("CancellationWindowDays", $event)); });
179
309
  i0.ɵɵelementEnd();
180
310
  } if (rf & 2) {
181
311
  const ctx_r1 = i0.ɵɵnextContext();
182
312
  i0.ɵɵproperty("ngModel", ctx_r1.Record.CancellationWindowDays);
183
313
  } }
184
- function MJCContractRenewalPanel_Conditional_21_Template(rf, ctx) { if (rf & 1) {
314
+ function MJCContractRenewalPanel_Conditional_22_Template(rf, ctx) { if (rf & 1) {
185
315
  i0.ɵɵelementStart(0, "div", 6);
186
316
  i0.ɵɵtext(1);
187
317
  i0.ɵɵelementEnd();
188
318
  } if (rf & 2) {
189
319
  const ctx_r1 = i0.ɵɵnextContext();
190
320
  i0.ɵɵadvance();
191
- i0.ɵɵtextInterpolate(ctx_r1.Record.CancellationWindowDays ? ctx_r1.Record.CancellationWindowDays + " days" : "\u2014");
321
+ i0.ɵɵtextInterpolate(ctx_r1.Days(ctx_r1.Record.CancellationWindowDays));
192
322
  } }
193
- function MJCContractRenewalPanel_Conditional_22_Template(rf, ctx) { if (rf & 1) {
194
- i0.ɵɵelementStart(0, "div", 7);
323
+ function MJCContractRenewalPanel_Conditional_25_Template(rf, ctx) { if (rf & 1) {
324
+ i0.ɵɵelementStart(0, "div", 8);
195
325
  i0.ɵɵtext(1, "the window is open now");
196
326
  i0.ɵɵelementEnd();
197
327
  } }
198
- function MJCContractRenewalPanel_Conditional_26_Template(rf, ctx) { if (rf & 1) {
328
+ function MJCContractRenewalPanel_Conditional_29_Template(rf, ctx) { if (rf & 1) {
199
329
  const _r5 = i0.ɵɵgetCurrentView();
200
- i0.ɵɵelementStart(0, "input", 17);
201
- i0.ɵɵlistener("ngModelChange", function MJCContractRenewalPanel_Conditional_26_Template_input_ngModelChange_0_listener($event) { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.Set("AnnualIncreasePercent", $event)); });
330
+ i0.ɵɵelementStart(0, "input", 16);
331
+ i0.ɵɵlistener("ngModelChange", function MJCContractRenewalPanel_Conditional_29_Template_input_ngModelChange_0_listener($event) { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.Set("AnnualIncreasePercent", $event)); });
202
332
  i0.ɵɵelementEnd();
203
333
  } if (rf & 2) {
204
334
  const ctx_r1 = i0.ɵɵnextContext();
205
335
  i0.ɵɵproperty("ngModel", ctx_r1.Record.AnnualIncreasePercent);
206
336
  } }
207
- function MJCContractRenewalPanel_Conditional_27_Template(rf, ctx) { if (rf & 1) {
337
+ function MJCContractRenewalPanel_Conditional_30_Template(rf, ctx) { if (rf & 1) {
208
338
  i0.ɵɵelementStart(0, "div", 6);
209
339
  i0.ɵɵtext(1);
210
340
  i0.ɵɵelementEnd();
211
341
  } if (rf & 2) {
212
342
  const ctx_r1 = i0.ɵɵnextContext();
213
343
  i0.ɵɵadvance();
214
- i0.ɵɵtextInterpolate(ctx_r1.Record.AnnualIncreasePercent !== null ? ctx_r1.Record.AnnualIncreasePercent + "%" : "\u2014");
344
+ i0.ɵɵtextInterpolate(ctx_r1.Percent(ctx_r1.Record.AnnualIncreasePercent));
215
345
  } }
216
- function MJCContractRenewalPanel_Conditional_30_Template(rf, ctx) { if (rf & 1) {
217
- i0.ɵɵelementStart(0, "div", 12);
218
- i0.ɵɵtext(1, " No renewal terms recorded. If the agreement states any, recording them is what puts this contract on the renewals watchlist. ");
346
+ function MJCContractRenewalPanel_Conditional_31_Template(rf, ctx) { if (rf & 1) {
347
+ i0.ɵɵelementStart(0, "div", 11);
348
+ i0.ɵɵtext(1, "No renewal terms recorded.");
219
349
  i0.ɵɵelementEnd();
220
350
  } }
221
351
  function MJCContractDatesPanel_Conditional_6_Template(rf, ctx) { if (rf & 1) {
222
352
  const _r1 = i0.ɵɵgetCurrentView();
223
- i0.ɵɵelementStart(0, "input", 12);
353
+ i0.ɵɵelementStart(0, "input", 11);
224
354
  i0.ɵɵlistener("ngModelChange", function MJCContractDatesPanel_Conditional_6_Template_input_ngModelChange_0_listener($event) { i0.ɵɵrestoreView(_r1); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.SetDate("ExecutedDate", $event)); });
225
355
  i0.ɵɵelementEnd();
226
356
  } if (rf & 2) {
@@ -237,16 +367,16 @@ function MJCContractDatesPanel_Conditional_7_Template(rf, ctx) { if (rf & 1) {
237
367
  i0.ɵɵadvance();
238
368
  i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(2, 1, ctx_r1.Record.ExecutedDate, "d MMM y") || "\u2014");
239
369
  } }
240
- function MJCContractDatesPanel_Conditional_13_Template(rf, ctx) { if (rf & 1) {
370
+ function MJCContractDatesPanel_Conditional_11_Template(rf, ctx) { if (rf & 1) {
241
371
  const _r3 = i0.ɵɵgetCurrentView();
242
- i0.ɵɵelementStart(0, "input", 13);
243
- i0.ɵɵlistener("ngModelChange", function MJCContractDatesPanel_Conditional_13_Template_input_ngModelChange_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.SetDate("EffectiveDate", $event)); });
372
+ i0.ɵɵelementStart(0, "input", 12);
373
+ i0.ɵɵlistener("ngModelChange", function MJCContractDatesPanel_Conditional_11_Template_input_ngModelChange_0_listener($event) { i0.ɵɵrestoreView(_r3); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.SetDate("EffectiveDate", $event)); });
244
374
  i0.ɵɵelementEnd();
245
375
  } if (rf & 2) {
246
376
  const ctx_r1 = i0.ɵɵnextContext();
247
377
  i0.ɵɵproperty("ngModel", ctx_r1.AsInput(ctx_r1.Record.EffectiveDate));
248
378
  } }
249
- function MJCContractDatesPanel_Conditional_14_Template(rf, ctx) { if (rf & 1) {
379
+ function MJCContractDatesPanel_Conditional_12_Template(rf, ctx) { if (rf & 1) {
250
380
  i0.ɵɵelementStart(0, "div", 5);
251
381
  i0.ɵɵtext(1);
252
382
  i0.ɵɵpipe(2, "date");
@@ -256,16 +386,16 @@ function MJCContractDatesPanel_Conditional_14_Template(rf, ctx) { if (rf & 1) {
256
386
  i0.ɵɵadvance();
257
387
  i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(2, 1, ctx_r1.Record.EffectiveDate, "d MMM y") || "\u2014");
258
388
  } }
259
- function MJCContractDatesPanel_Conditional_18_Template(rf, ctx) { if (rf & 1) {
389
+ function MJCContractDatesPanel_Conditional_16_Template(rf, ctx) { if (rf & 1) {
260
390
  const _r4 = i0.ɵɵgetCurrentView();
261
- i0.ɵɵelementStart(0, "input", 14);
262
- i0.ɵɵlistener("ngModelChange", function MJCContractDatesPanel_Conditional_18_Template_input_ngModelChange_0_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.SetDate("EndDate", $event)); });
391
+ i0.ɵɵelementStart(0, "input", 13);
392
+ i0.ɵɵlistener("ngModelChange", function MJCContractDatesPanel_Conditional_16_Template_input_ngModelChange_0_listener($event) { i0.ɵɵrestoreView(_r4); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.SetDate("EndDate", $event)); });
263
393
  i0.ɵɵelementEnd();
264
394
  } if (rf & 2) {
265
395
  const ctx_r1 = i0.ɵɵnextContext();
266
396
  i0.ɵɵproperty("ngModel", ctx_r1.AsInput(ctx_r1.Record.EndDate));
267
397
  } }
268
- function MJCContractDatesPanel_Conditional_19_Template(rf, ctx) { if (rf & 1) {
398
+ function MJCContractDatesPanel_Conditional_17_Template(rf, ctx) { if (rf & 1) {
269
399
  i0.ɵɵelementStart(0, "div", 5);
270
400
  i0.ɵɵtext(1);
271
401
  i0.ɵɵpipe(2, "date");
@@ -275,25 +405,16 @@ function MJCContractDatesPanel_Conditional_19_Template(rf, ctx) { if (rf & 1) {
275
405
  i0.ɵɵadvance();
276
406
  i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(2, 1, ctx_r1.Record.EndDate, "d MMM y") || "\u2014");
277
407
  } }
278
- function MJCContractDatesPanel_Conditional_20_Template(rf, ctx) { if (rf & 1) {
279
- i0.ɵɵelementStart(0, "div", 6);
280
- i0.ɵɵtext(1);
281
- i0.ɵɵelementEnd();
282
- } if (rf & 2) {
283
- const ctx_r1 = i0.ɵɵnextContext();
284
- i0.ɵɵadvance();
285
- i0.ɵɵtextInterpolate(ctx_r1.EndsInText);
286
- } }
287
- function MJCContractDatesPanel_Conditional_24_Template(rf, ctx) { if (rf & 1) {
408
+ function MJCContractDatesPanel_Conditional_21_Template(rf, ctx) { if (rf & 1) {
288
409
  const _r5 = i0.ɵɵgetCurrentView();
289
- i0.ɵɵelementStart(0, "input", 15);
290
- i0.ɵɵlistener("ngModelChange", function MJCContractDatesPanel_Conditional_24_Template_input_ngModelChange_0_listener($event) { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.SetDate("TerminatedDate", $event)); });
410
+ i0.ɵɵelementStart(0, "input", 14);
411
+ i0.ɵɵlistener("ngModelChange", function MJCContractDatesPanel_Conditional_21_Template_input_ngModelChange_0_listener($event) { i0.ɵɵrestoreView(_r5); const ctx_r1 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r1.SetDate("TerminatedDate", $event)); });
291
412
  i0.ɵɵelementEnd();
292
413
  } if (rf & 2) {
293
414
  const ctx_r1 = i0.ɵɵnextContext();
294
415
  i0.ɵɵproperty("ngModel", ctx_r1.AsInput(ctx_r1.Record.TerminatedDate));
295
416
  } }
296
- function MJCContractDatesPanel_Conditional_25_Template(rf, ctx) { if (rf & 1) {
417
+ function MJCContractDatesPanel_Conditional_22_Template(rf, ctx) { if (rf & 1) {
297
418
  i0.ɵɵelementStart(0, "div", 5);
298
419
  i0.ɵɵtext(1);
299
420
  i0.ɵɵpipe(2, "date");
@@ -328,9 +449,10 @@ function MJCContractLineagePanel_Conditional_2_Template(rf, ctx) { if (rf & 1) {
328
449
  } }
329
450
  function MJCContractLineagePanel_Conditional_4_Template(rf, ctx) { if (rf & 1) {
330
451
  i0.ɵɵelementStart(0, "div", 3);
331
- i0.ɵɵtext(1, " A standalone agreement \u2014 nothing above it, no change orders, and nothing has replaced it. ");
452
+ i0.ɵɵtext(1, " No parent contract, change orders, or superseding contracts. ");
332
453
  i0.ɵɵelementEnd();
333
454
  } }
455
+ const COLLAPSE_SETTING = 'mj.identityHeader.collapsed.contract';
334
456
  /** Chip variant per lifecycle state — colour carries meaning, so each state gets a considered one. */
335
457
  function chipClassFor(state) {
336
458
  switch (state) {
@@ -346,6 +468,23 @@ function chipClassFor(state) {
346
468
  * 1 · Hero — persistent identity, above the rail
347
469
  * ──────────────────────────────────────────────────────────────────────────── */
348
470
  let MJCContractHeroPanel = class MJCContractHeroPanel extends BaseFormPanel {
471
+ cdr = inject(ChangeDetectorRef);
472
+ Collapsed = false;
473
+ ngOnInit() {
474
+ const raw = UserInfoEngine.Instance.GetSetting(COLLAPSE_SETTING);
475
+ if (raw) {
476
+ try {
477
+ this.Collapsed = JSON.parse(raw) === true;
478
+ }
479
+ catch {
480
+ this.Collapsed = false;
481
+ }
482
+ }
483
+ }
484
+ ToggleCollapsed() {
485
+ this.Collapsed = !this.Collapsed;
486
+ UserInfoEngine.Instance.SetSettingDebounced(COLLAPSE_SETTING, JSON.stringify(this.Collapsed));
487
+ }
349
488
  /**
350
489
  * The human name for this agreement: its description, falling back to the number.
351
490
  *
@@ -381,9 +520,129 @@ let MJCContractHeroPanel = class MJCContractHeroPanel extends BaseFormPanel {
381
520
  get CompanyName() { return this.Record?.Company ?? ''; }
382
521
  get ContactName() { return this.Record?.PrimaryContactPerson ?? ''; }
383
522
  get TemplateName() { return this.Record?.ContractTemplate ?? ''; }
384
- get CreatingEntityName() { return this.Record?.CreatingEntity ?? ''; }
523
+ /* ── Source record — the thing that created this contract (issue #28 item 1) ──────────────
524
+ *
525
+ * WHAT THE STAT USED TO CLAIM. Labelled "Created from", it rendered `CreatingEntity` — the name of
526
+ * an ENTITY, not a record — so a contract raised from a Close-Won deal read "Deals" and one
527
+ * entered by hand read "Entered directly". Neither told the reader WHICH deal, and the second
528
+ * spends a stat on the absence of a fact.
529
+ *
530
+ * `CreatingEntityID` / `CreatingRecordID` is a POLYMORPHIC pair, so nothing here may assume Deals.
531
+ * Today `bizapps-sales` is the only writer (`LiveContractsSeam`, on Close-Won) and always stores
532
+ * Deals, but the column does not say so: the entity is resolved from the id, the label takes that
533
+ * entity's own singular name, and an Order would read "Source Order" with no edit here.
534
+ *
535
+ * NAVIGATION GOES THROUGH THIS PANEL'S OWN `open()`, the helper next added for Customer and
536
+ * Contact. It handles ctrl/cmd-click for a new tab and routes through `OnFormNavigate` rather
537
+ * than `NavigationService`, and matching it matters more than the primary-key lookup an earlier
538
+ * version of this did — `CompositeKey.FromID` is the convention every link in this file uses.
539
+ */
540
+ /** Resolved once per record, lazily, on the first template read. */
541
+ sourceFor = null;
542
+ SourceLabel = 'Source record';
543
+ SourceName = '';
544
+ /** Set when the provenance names a record that does not exist — the stat hides rather than link. */
545
+ SourceMissing = false;
546
+ /**
547
+ * Whether this contract records what created it.
548
+ *
549
+ * Doubles as the load trigger — `BaseFormPanel` has no lifecycle hook and the slot host sets
550
+ * `Record` before view init, so the first template read is the earliest reliable moment. Keyed on
551
+ * the record so navigating the form to another contract reloads instead of showing the previous
552
+ * one's deal.
553
+ */
554
+ get HasSource() {
555
+ const entityID = this.Record?.CreatingEntityID;
556
+ const recordID = this.Record?.CreatingRecordID;
557
+ if (!entityID || !recordID)
558
+ return false;
559
+ const key = `${this.Record?.ID}:${entityID}:${recordID}`;
560
+ if (this.sourceFor !== key) {
561
+ this.sourceFor = key;
562
+ this.SourceMissing = false;
563
+ void this.loadSource(key);
564
+ }
565
+ return !this.SourceMissing;
566
+ }
567
+ /** The entity named by `CreatingEntityID`, or null when the id names nothing this user can see. */
568
+ sourceEntity() {
569
+ const id = this.Record?.CreatingEntityID;
570
+ return (id ? this.FormComponent?.ProviderToUse?.Entities?.find((e) => e.ID === id) : undefined) ?? null;
571
+ }
572
+ /**
573
+ * Label from the entity, name from the record.
574
+ *
575
+ * `BaseTableDisplayName` rather than `DisplayName`: the entity is plural ("Deals") and the stat
576
+ * labels one record, so the singular base table is the honest word — and it is what produces
577
+ * "Source Deal" and "Source Order" with no de-pluralising guess.
578
+ */
579
+ async loadSource(key) {
580
+ const entity = this.sourceEntity();
581
+ if (!entity)
582
+ return;
583
+ this.SourceLabel = `Source ${entity.BaseTableDisplayName}`;
584
+ const nameField = entity.NameField?.Name;
585
+ const recordID = String(this.Record?.CreatingRecordID ?? '');
586
+ if (!nameField || !recordID) {
587
+ this.cdr.detectChanges();
588
+ return;
589
+ }
590
+ try {
591
+ const { ScopedRunView } = await import('../data/provider');
592
+ const rv = ScopedRunView(this.FormComponent?.ProviderToUse);
593
+ const pkField = entity.PrimaryKeys?.[0]?.Name ?? 'ID';
594
+ const r = await rv.RunView({
595
+ EntityName: entity.Name,
596
+ Fields: [nameField],
597
+ ExtraFilter: `${pkField} = '${recordID.replace(/'/g, "''")}'`,
598
+ ResultType: 'simple',
599
+ });
600
+ // Guard against a slower read for a PREVIOUS record landing after the form moved on.
601
+ if (this.sourceFor !== key)
602
+ return;
603
+ const row = r?.Success ? r.Results?.[0] : undefined;
604
+ if (r?.Success && !row) {
605
+ // READ SUCCEEDED AND MATCHED NOTHING: the provenance names a row that is not there,
606
+ // so there is nothing to open and the stat hides itself. This is the case the
607
+ // fallback below used to swallow — CTR-000026 carries a hand-typed pair whose record
608
+ // id is not even a UUID, and the stat rendered an "Open" button that navigated
609
+ // nowhere. A link that cannot work is worse than an absent stat: it invites a click
610
+ // and spends the reader's trust.
611
+ this.SourceMissing = true;
612
+ this.SourceName = '';
613
+ return;
614
+ }
615
+ this.SourceMissing = false;
616
+ this.SourceName = row ? String(row[nameField] ?? '') : '';
617
+ }
618
+ catch {
619
+ // A THROWN read is the different case, and the fallback is still right for it: the record
620
+ // may well exist and simply be unreadable by this user, so the link stays and the stat
621
+ // says "Open". Conflating the two is what made the dead button look deliberate.
622
+ this.SourceMissing = false;
623
+ this.SourceName = '';
624
+ }
625
+ finally {
626
+ this.cdr.detectChanges();
627
+ }
628
+ }
629
+ /** Open the record that created this contract, through the same helper as the other links. */
630
+ OpenSource(event) {
631
+ const entity = this.sourceEntity();
632
+ if (!entity)
633
+ return;
634
+ this.open(event, entity.Name, this.Record?.CreatingRecordID);
635
+ }
385
636
  get IsAwaitingDocument() { return this.Record?.IsAwaitingDocument === true; }
386
637
  get DaysToEnd() { return this.Record?.DaysToEnd ?? null; }
638
+ get StatusTone() {
639
+ switch (this.State) {
640
+ case 'Active':
641
+ case 'Executed': return 'success';
642
+ case 'Terminated': return 'warning';
643
+ default: return 'muted';
644
+ }
645
+ }
387
646
  /** "in 16 months" reads better than "in 487 days" past a couple of months — the mockup's phrasing. */
388
647
  get EndsInText() {
389
648
  const d = this.DaysToEnd;
@@ -398,121 +657,76 @@ let MJCContractHeroPanel = class MJCContractHeroPanel extends BaseFormPanel {
398
657
  const months = Math.round(d / 30);
399
658
  return `in ${months} month${months === 1 ? '' : 's'}`;
400
659
  }
660
+ OpenCustomer(event) {
661
+ this.open(event, MJC_FOREIGN_ENTITIES.Organization, this.Record?.CustomerOrganizationID);
662
+ }
663
+ OpenContact(event) {
664
+ this.open(event, MJC_FOREIGN_ENTITIES.Person, this.Record?.PrimaryContactPersonID);
665
+ }
666
+ open(event, entity, id) {
667
+ if (!id)
668
+ return;
669
+ event.preventDefault();
670
+ this.FormComponent.OnFormNavigate({
671
+ Kind: 'record',
672
+ EntityName: entity,
673
+ PrimaryKey: CompositeKey.FromID(id),
674
+ OpenInNewTab: event.ctrlKey || event.metaKey,
675
+ });
676
+ }
401
677
  static ɵfac = /*@__PURE__*/ (() => { let ɵMJCContractHeroPanel_BaseFactory; return function MJCContractHeroPanel_Factory(__ngFactoryType__) { return (ɵMJCContractHeroPanel_BaseFactory || (ɵMJCContractHeroPanel_BaseFactory = i0.ɵɵgetInheritedFactory(MJCContractHeroPanel)))(__ngFactoryType__ || MJCContractHeroPanel); }; })();
402
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJCContractHeroPanel, selectors: [["mjc-contract-hero-panel"]], features: [i0.ɵɵInheritDefinitionFeature], decls: 59, vars: 30, consts: [[1, "mjc-hero"], [1, "mjc-hero__identity"], [1, "mjc-hero__avatar"], ["aria-hidden", "true", 1, "fa-solid", "fa-file-signature"], [1, "mjc-hero__copy"], [1, "mjc-hero__title-row"], [1, "mjc-hero__title"], [1, "mjc-chip"], [1, "mjc-chip", "mjc-chip--info"], ["title", "This contract deviates from the standard agreement \u2014 read the paper", 1, "mjc-chip", "mjc-chip--warn"], ["title", "This contract type expects an executed document and none is attached", 1, "mjc-chip", "mjc-chip--warn"], [1, "mjc-hero__meta"], [1, "mjc-mono"], [1, "mjc-hero__stats"], [1, "mjc-stat"], [1, "mjc-stat__label"], [1, "mjc-stat__value"], [1, "mjc-stat__sub"], [1, "mjc-flag"], ["aria-hidden", "true", 1, "fa-solid", "fa-triangle-exclamation"], ["aria-hidden", "true", 1, "fa-solid", "fa-file-circle-question"]], template: function MJCContractHeroPanel_Template(rf, ctx) { if (rf & 1) {
403
- i0.ɵɵdomElementStart(0, "div", 0)(1, "div", 1)(2, "div", 2);
404
- i0.ɵɵdomElement(3, "i", 3);
405
- i0.ɵɵdomElementEnd();
406
- i0.ɵɵdomElementStart(4, "div", 4)(5, "div", 5)(6, "h1", 6);
407
- i0.ɵɵtext(7);
408
- i0.ɵɵdomElementEnd();
409
- i0.ɵɵdomElementStart(8, "span", 7);
410
- i0.ɵɵtext(9);
411
- i0.ɵɵdomElementEnd();
412
- i0.ɵɵconditionalCreate(10, MJCContractHeroPanel_Conditional_10_Template, 2, 1, "span", 8);
413
- i0.ɵɵconditionalCreate(11, MJCContractHeroPanel_Conditional_11_Template, 3, 0, "span", 9);
414
- i0.ɵɵconditionalCreate(12, MJCContractHeroPanel_Conditional_12_Template, 3, 0, "span", 10);
415
- i0.ɵɵdomElementEnd();
416
- i0.ɵɵdomElementStart(13, "div", 11)(14, "span", 12);
678
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJCContractHeroPanel, selectors: [["mjc-contract-hero-panel"]], features: [i0.ɵɵInheritDefinitionFeature], decls: 22, vars: 16, consts: [[1, "mjc-hero"], [1, "mjc-hero__identity"], ["aria-hidden", "true", 1, "mjc-hero__avatar"], [1, "fa-solid", "fa-file-signature"], [1, "mjc-hero__presence", 3, "title"], [1, "mjc-hero__copy"], [1, "mjc-hero__title-row"], [1, "mjc-hero__title"], [1, "mjc-hero__aka"], [1, "mjc-hero__badges"], [1, "mjc-hero__entity-chip"], [1, "mjc-chip"], [1, "mjc-chip", "mjc-chip--info"], ["title", "Standard agreement was modified", 1, "mjc-chip", "mjc-chip--warn"], ["title", "Executed agreement not attached", 1, "mjc-chip", "mjc-chip--warn"], ["type", "button", 1, "mjc-hero__toggle", 3, "click", "title"], ["aria-hidden", "true", 1, "fa-solid", "fa-triangle-exclamation"], ["aria-hidden", "true", 1, "fa-solid", "fa-file-circle-question"], [1, "mjc-hero__summary"], [1, "mjc-hero__stat"], [1, "mjc-hero__stat-label"], [1, "mjc-hero__stat-val"], ["type", "button", 1, "mjc-hero__stat-val", "is-link"], [1, "mjc-hero__next"], [1, "mjc-hero__edit"], [1, "mjc-flag"], ["type", "button", 1, "mjc-hero__stat-val", "is-link", 3, "click"], [1, "mjc-hero__next-val"], [1, "mjc-hero__field"], ["FieldName", "Description", "Type", "textarea", 3, "Record", "ShowLabel", "EditMode", "FormContext"]], template: function MJCContractHeroPanel_Template(rf, ctx) { if (rf & 1) {
679
+ i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div", 2);
680
+ i0.ɵɵelement(3, "i", 3);
681
+ i0.ɵɵconditionalCreate(4, MJCContractHeroPanel_Conditional_4_Template, 1, 2, "span", 4);
682
+ i0.ɵɵelementEnd();
683
+ i0.ɵɵelementStart(5, "div", 5)(6, "div", 6)(7, "h1", 7);
684
+ i0.ɵɵtext(8);
685
+ i0.ɵɵelementEnd()();
686
+ i0.ɵɵconditionalCreate(9, MJCContractHeroPanel_Conditional_9_Template, 2, 1, "div", 8);
687
+ i0.ɵɵelementStart(10, "div", 9)(11, "span", 10);
688
+ i0.ɵɵelement(12, "i", 3);
689
+ i0.ɵɵtext(13, " Contract");
690
+ i0.ɵɵelementEnd();
691
+ i0.ɵɵelementStart(14, "span", 11);
417
692
  i0.ɵɵtext(15);
418
- i0.ɵɵdomElementEnd();
419
- i0.ɵɵdomElementStart(16, "span");
420
- i0.ɵɵtext(17, "Customer: ");
421
- i0.ɵɵdomElementStart(18, "strong");
422
- i0.ɵɵtext(19);
423
- i0.ɵɵdomElementEnd()();
424
- i0.ɵɵdomElementStart(20, "span");
425
- i0.ɵɵtext(21, "Selling: ");
426
- i0.ɵɵdomElementStart(22, "strong");
427
- i0.ɵɵtext(23);
428
- i0.ɵɵdomElementEnd()();
429
- i0.ɵɵconditionalCreate(24, MJCContractHeroPanel_Conditional_24_Template, 4, 1, "span");
430
- i0.ɵɵdomElementEnd()()();
431
- i0.ɵɵdomElementStart(25, "div", 13)(26, "div", 14)(27, "span", 15);
432
- i0.ɵɵtext(28, "Executed");
433
- i0.ɵɵdomElementEnd();
434
- i0.ɵɵdomElementStart(29, "span", 16);
435
- i0.ɵɵtext(30);
436
- i0.ɵɵpipe(31, "date");
437
- i0.ɵɵdomElementEnd();
438
- i0.ɵɵconditionalCreate(32, MJCContractHeroPanel_Conditional_32_Template, 2, 0, "span", 17);
439
- i0.ɵɵdomElementEnd();
440
- i0.ɵɵdomElementStart(33, "div", 14)(34, "span", 15);
441
- i0.ɵɵtext(35, "Effective");
442
- i0.ɵɵdomElementEnd();
443
- i0.ɵɵdomElementStart(36, "span", 16);
444
- i0.ɵɵtext(37);
445
- i0.ɵɵpipe(38, "date");
446
- i0.ɵɵdomElementEnd()();
447
- i0.ɵɵdomElementStart(39, "div", 14)(40, "span", 15);
448
- i0.ɵɵtext(41, "Term ends");
449
- i0.ɵɵdomElementEnd();
450
- i0.ɵɵdomElementStart(42, "span", 16);
451
- i0.ɵɵtext(43);
452
- i0.ɵɵpipe(44, "date");
453
- i0.ɵɵdomElementEnd();
454
- i0.ɵɵconditionalCreate(45, MJCContractHeroPanel_Conditional_45_Template, 2, 1, "span", 17);
455
- i0.ɵɵdomElementEnd();
456
- i0.ɵɵdomElementStart(46, "div", 14)(47, "span", 15);
457
- i0.ɵɵtext(48, "Agreement");
458
- i0.ɵɵdomElementEnd();
459
- i0.ɵɵdomElementStart(49, "span", 16);
460
- i0.ɵɵtext(50);
461
- i0.ɵɵdomElementEnd();
462
- i0.ɵɵconditionalCreate(51, MJCContractHeroPanel_Conditional_51_Template, 2, 0, "span", 17);
463
- i0.ɵɵdomElementEnd();
464
- i0.ɵɵdomElementStart(52, "div", 14)(53, "span", 15);
465
- i0.ɵɵtext(54, "Created by");
466
- i0.ɵɵdomElementEnd();
467
- i0.ɵɵdomElementStart(55, "span", 16);
468
- i0.ɵɵtext(56);
469
- i0.ɵɵdomElementEnd();
470
- i0.ɵɵconditionalCreate(57, MJCContractHeroPanel_Conditional_57_Template, 2, 0, "span", 17);
471
- i0.ɵɵdomElementEnd()();
472
- i0.ɵɵconditionalCreate(58, MJCContractHeroPanel_Conditional_58_Template, 2, 0, "div", 18);
473
- i0.ɵɵdomElementEnd();
693
+ i0.ɵɵelementEnd();
694
+ i0.ɵɵconditionalCreate(16, MJCContractHeroPanel_Conditional_16_Template, 2, 1, "span", 12);
695
+ i0.ɵɵconditionalCreate(17, MJCContractHeroPanel_Conditional_17_Template, 3, 0, "span", 13);
696
+ i0.ɵɵconditionalCreate(18, MJCContractHeroPanel_Conditional_18_Template, 3, 0, "span", 14);
697
+ i0.ɵɵelementEnd()();
698
+ i0.ɵɵelementStart(19, "button", 15);
699
+ i0.ɵɵlistener("click", function MJCContractHeroPanel_Template_button_click_19_listener() { return ctx.ToggleCollapsed(); });
700
+ i0.ɵɵelement(20, "i");
701
+ i0.ɵɵelementEnd()();
702
+ i0.ɵɵconditionalCreate(21, MJCContractHeroPanel_Conditional_21_Template, 43, 20);
703
+ i0.ɵɵelementEnd();
474
704
  } if (rf & 2) {
475
- i0.ɵɵadvance(7);
705
+ i0.ɵɵclassProp("mjc-hero--collapsed", ctx.Collapsed);
706
+ i0.ɵɵadvance(4);
707
+ i0.ɵɵconditional(!ctx.EditMode ? 4 : -1);
708
+ i0.ɵɵadvance(4);
476
709
  i0.ɵɵtextInterpolate(ctx.Title);
477
710
  i0.ɵɵadvance();
711
+ i0.ɵɵconditional(ctx.Record.ContractNumber && !ctx.Collapsed ? 9 : -1);
712
+ i0.ɵɵadvance(5);
478
713
  i0.ɵɵclassMap("mjc-chip " + ctx.StateChipClass);
479
714
  i0.ɵɵadvance();
480
715
  i0.ɵɵtextInterpolate(ctx.State);
481
716
  i0.ɵɵadvance();
482
- i0.ɵɵconditional(ctx.TypeName ? 10 : -1);
717
+ i0.ɵɵconditional(ctx.TypeName ? 16 : -1);
483
718
  i0.ɵɵadvance();
484
- i0.ɵɵconditional(ctx.Record.HasModifications ? 11 : -1);
719
+ i0.ɵɵconditional(ctx.Record.HasModifications ? 17 : -1);
485
720
  i0.ɵɵadvance();
486
- i0.ɵɵconditional(ctx.IsAwaitingDocument ? 12 : -1);
487
- i0.ɵɵadvance(3);
488
- i0.ɵɵtextInterpolate(ctx.Record.ContractNumber || "Unnumbered");
489
- i0.ɵɵadvance(4);
490
- i0.ɵɵtextInterpolate(ctx.CustomerName || "\u2014");
491
- i0.ɵɵadvance(4);
492
- i0.ɵɵtextInterpolate(ctx.CompanyName || "\u2014");
721
+ i0.ɵɵconditional(ctx.IsAwaitingDocument ? 18 : -1);
493
722
  i0.ɵɵadvance();
494
- i0.ɵɵconditional(ctx.ContactName ? 24 : -1);
495
- i0.ɵɵadvance(6);
496
- i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(31, 21, ctx.Record.ExecutedDate, "d MMM y") || "\u2014");
497
- i0.ɵɵadvance(2);
498
- i0.ɵɵconditional(!ctx.Record.ExecutedDate ? 32 : -1);
499
- i0.ɵɵadvance(5);
500
- i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(38, 24, ctx.Record.EffectiveDate, "d MMM y") || "\u2014");
501
- i0.ɵɵadvance(6);
502
- i0.ɵɵtextInterpolate(i0.ɵɵpipeBind2(44, 27, ctx.Record.EndDate, "d MMM y") || "\u2014");
503
- i0.ɵɵadvance(2);
504
- i0.ɵɵconditional(ctx.DaysToEnd !== null ? 45 : -1);
505
- i0.ɵɵadvance(5);
506
- i0.ɵɵtextInterpolate(ctx.TemplateName || "\u2014");
507
- i0.ɵɵadvance();
508
- i0.ɵɵconditional(!ctx.TemplateName ? 51 : -1);
509
- i0.ɵɵadvance(5);
510
- i0.ɵɵtextInterpolate(ctx.CreatingEntityName || "\u2014");
723
+ i0.ɵɵproperty("title", ctx.Collapsed ? "Expand header" : "Collapse header");
724
+ i0.ɵɵattribute("aria-label", ctx.Collapsed ? "Expand header" : "Collapse header");
511
725
  i0.ɵɵadvance();
512
- i0.ɵɵconditional(!ctx.CreatingEntityName ? 57 : -1);
726
+ i0.ɵɵclassMap(ctx.Collapsed ? "fa-solid fa-chevron-down" : "fa-solid fa-chevron-up");
513
727
  i0.ɵɵadvance();
514
- i0.ɵɵconditional(!ctx.Record.ContractNumber ? 58 : -1);
515
- } }, dependencies: [CommonModule, BaseFormsModule, i1.DatePipe], encapsulation: 2 });
728
+ i0.ɵɵconditional(!ctx.Collapsed ? 21 : -1);
729
+ } }, dependencies: [CommonModule, BaseFormsModule, i1.MjFormFieldComponent, i2.DatePipe], styles: ["/**\n * contracts-kit.css \u2014 ported from design-docs/ui-design/mockups-v2/tokens.css.\n *\n * \u26A0 SEMANTIC TOKENS ONLY. MJ's design-token rule is explicit: *\"NEVER use primitive tokens\n * (`--mj-color-neutral-*`, `--mj-color-brand-*`) in component CSS. Primitives don't adapt to dark\n * mode.\"* An earlier version of this file used twelve of them, because the mockup did \u2014 and in a\n * standalone mockup page that is harmless, since there is no dark mode to adapt to. Ported into a real\n * component stylesheet it means every tinted surface, chip and clause box keeps its LIGHT colour when\n * the app is themed dark. That is the theming break, and it is invisible until someone switches theme.\n *\n * The mapping applied: neutral-50 \u2192 bg-surface-sunken, neutral-100 \u2192 border-subtle, neutral-300 \u2192\n * border-strong, success/error/warning-50 \u2192 status-*-bg, success/error-700 \u2192 status-*-text, brand-50 \u2192\n * status-info-bg, brand-100 \u2192 status-info-border, brand-700/900 \u2192 status-info-text. `#fff` on a brand\n * fill \u2192 `--mj-brand-on-primary`.\n *\n * THE MOCKUPS ARE THE SPEC, and this file exists to keep them so. An earlier version of this kit was\n * written from MEMORY of them, and it was wrong in a way that is invisible in source and obvious on\n * screen: it used token names that do not exist. `--mj-font-size-sm`, `--mj-text-tertiary`,\n * `--mj-color-warning-text`, `--mj-color-success-border` and four others are not MJ tokens (the real\n * names are `--mj-text-sm`, `--mj-status-warning-text`, `--mj-status-success`, \u2026). Every one of those\n * resolved to NOTHING \u2014 so font sizes fell back to browser defaults and the state chips rendered with\n * no colour at all. That is most of what \"looks bad\" was.\n *\n * So: class names and rules below are the mockup's, verbatim where possible, and **every token used\n * here has been checked to exist in MJ's `_tokens.scss`.** If you add a rule, check the token first \u2014\n * a missing custom property fails silently, which is the worst failure mode CSS has.\n *\n * SHIPPED VIA THE SECTION SHELL (`styleUrls` + `ViewEncapsulation.None`). `ngc` only compiles what a\n * component references, so a stylesheet nothing references is built into dist and never loaded.\n * `None` is required, not lazy: these classes are used by DESCENDANT components.\n *\n * Prefix note: the mockup used bare class names (`.panel`, `.chip`) because it was a standalone page.\n * Here everything is `mjc-` prefixed \u2014 bare `.panel` inside a real Explorer would collide with MJ's own\n * chrome, and `ViewEncapsulation.None` means these rules are global.\n */\n\n/* \u2500\u2500 Page frame \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mjc-page {\n display: flex;\n flex-direction: column;\n gap: var(--mj-space-4);\n padding: var(--mj-space-5);\n}\n\n/* A grid page needs a DEFINITE height: mj-explorer-entity-data-grid is height:100% outside a related\n panel, and 100% of an auto-height parent is zero \u2014 the grid renders no rows while its toolbar still\n reports the count. MJ's own source documents that symptom. `min-height: 0` on the fill slot is\n mandatory: a flex child defaults to min-height:auto and refuses to shrink, so without it the grid\n pushes the page taller instead of scrolling inside itself. */\n.mjc-page--grid { height: 100%; min-height: 0; }\n.mjc-grid-fill { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }\n\n/* Intro prose. Constrained measure \u2014 text stops being readable much past this \u2014 and it is a real\n block with real spacing, not a bare text node floating between cards. */\n.mjc-page__intro {\n color: var(--mj-text-secondary);\n font-size: var(--mj-text-sm);\n line-height: 1.55;\n margin: 0;\n max-width: 78ch;\n}\n\n/* \u2500\u2500 Panel body helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n Our panels are real `<mj-collapsible-panel>`s, so MJ paints the frame, the header and the spacing.\n These classes style only what goes INSIDE one. */\n\n.mjc-body { padding: var(--mj-space-4); }\n.mjc-body--flush { padding: 0; } /* a table fills the panel edge to edge */\n.mjc-body > * + * { margin-top: var(--mj-space-3); } /* nothing ever touches its neighbour */\n\n.mjc-note {\n margin: 0;\n color: var(--mj-text-muted);\n font-size: var(--mj-text-xs);\n line-height: 1.55;\n}\n\n/* A note under a flush table needs its own padding and a rule above it, or it reads as a table row. */\n.mjc-note--footer {\n padding: var(--mj-space-3) var(--mj-space-4);\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n/* \u2500\u2500 Hero (mockup 1) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mjc-hero {\n display: flex;\n flex-direction: column;\n gap: var(--mj-space-4);\n padding: var(--mj-space-4) var(--mj-space-5);\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.mjc-hero__identity { display: flex; align-items: center; gap: var(--mj-space-4); min-width: 0; }\n\n.mjc-hero__avatar {\n flex: none;\n width: 52px; height: 52px;\n border-radius: var(--mj-radius-md);\n display: flex; align-items: center; justify-content: center;\n background: color-mix(in srgb, var(--mj-brand-primary) 18%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: var(--mj-text-xl, 1.375rem);\n}\n\n.mjc-hero__copy { min-width: 0; flex: 1; }\n\n.mjc-hero__title-row {\n display: flex; align-items: center; flex-wrap: wrap;\n gap: var(--mj-space-2) var(--mj-space-3);\n}\n\n.mjc-hero__title {\n margin: 0;\n font-size: var(--mj-text-lg);\n font-weight: 700;\n line-height: 1.25;\n /* Long contract descriptions must not push the chips off the card. */\n overflow-wrap: anywhere;\n}\n\n.mjc-hero__meta {\n display: flex; flex-wrap: wrap;\n gap: var(--mj-space-3);\n color: var(--mj-text-secondary);\n font-size: var(--mj-text-sm);\n margin-top: var(--mj-space-2);\n}\n\n/* auto-fit so five stats sit in a row on a wide screen and reflow on a laptop, rather than\n overflowing the card \u2014 which is what a fixed five-column grid did. */\n.mjc-hero__stats {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));\n gap: var(--mj-space-3);\n}\n\n.mjc-stat {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n padding: var(--mj-space-3);\n min-width: 0;\n}\n.mjc-stat__label {\n display: block; font-size: var(--mj-text-xs);\n text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted);\n}\n.mjc-stat__value {\n display: block; font-size: var(--mj-text-base); font-weight: 700; margin-top: 2px;\n overflow-wrap: anywhere;\n}\n.mjc-stat__sub { display: block; font-size: var(--mj-text-xs); color: var(--mj-text-muted); }\n\n/* \u2500\u2500 Chips \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mjc-chip {\n display: inline-flex; align-items: center; gap: 4px;\n font-size: var(--mj-text-xs); font-weight: 600;\n padding: 2px 9px; border-radius: 999px;\n border: 1px solid transparent; white-space: nowrap;\n}\n.mjc-chip--ok { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); border-color: var(--mj-status-success); }\n.mjc-chip--warn { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); border-color: var(--mj-status-warning); }\n.mjc-chip--error { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); border-color: var(--mj-status-error); }\n.mjc-chip--info { background: var(--mj-status-info-bg); color: var(--mj-status-info-text); border-color: var(--mj-status-info-border); }\n.mjc-chip--muted { background: var(--mj-border-subtle); color: var(--mj-text-secondary); border-color: var(--mj-border-default); }\n\n/* \u2500\u2500 Buttons \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mjc-btn {\n display: inline-flex; align-items: center; gap: 6px;\n font-size: var(--mj-text-sm); font-weight: 600;\n padding: 6px 12px;\n border-radius: var(--mj-radius-sm);\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n cursor: pointer;\n}\n.mjc-btn:disabled { opacity: .5; cursor: default; }\n.mjc-btn--primary { background: var(--mj-brand-primary); border-color: var(--mj-brand-primary); color: var(--mj-brand-on-primary); }\n.mjc-btn--flat { background: transparent; border-color: transparent; color: var(--mj-brand-primary); }\n.mjc-btn--sm { padding: 3px 9px; font-size: var(--mj-text-xs); }\n.mjc-btn--danger { color: var(--mj-status-error-text); border-color: transparent; background: transparent; }\n\n/* \u2500\u2500 Field grid \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mjc-fields {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));\n gap: var(--mj-space-3) var(--mj-space-4);\n}\n.mjc-field > label {\n display: block; font-size: var(--mj-text-xs); color: var(--mj-text-muted);\n text-transform: uppercase; letter-spacing: .04em; margin-bottom: 3px;\n}\n.mjc-field .mjc-val {\n padding: 6px 9px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm);\n background: var(--mj-bg-surface);\n min-height: 31px;\n overflow-wrap: anywhere;\n}\n.mjc-field .mjc-val--ro { background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n\n/* EDIT MODE MATCHES READ MODE. These panels render bare native controls -- `<input type=\"date\">`,\n `<input type=\"number\">`, `<select>` -- because the generated datetime picker shows a time\n component for a `date` column, which is the whole reason the Dates panel exists. A bare control\n inherits none of the app's typography or chrome, so the same field visibly changed font, height\n and border the moment you pressed Edit. This mirrors `.mjc-val` above, field for field, so\n toggling edit does not move anything on screen.\n\n `font: inherit` is doing real work: form controls do NOT inherit font from their ancestor by\n default in any browser -- they fall back to a UA font -- so without it the value box and the\n input disagree even when every other property matches.\n\n THE WIDTH LIVES HERE, not on each control. The panels used to carry seven inline\n `style=\"width:100%\"` attributes; they are gone, and this rule is what replaces them. */\n.mjc-field input,\n.mjc-field select {\n width: 100%;\n box-sizing: border-box;\n font: inherit;\n color: inherit;\n padding: 6px 9px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm);\n background: var(--mj-bg-surface);\n min-height: 31px;\n}\n.mjc-field input:focus-visible,\n.mjc-field select:focus-visible { outline: 2px solid var(--mj-brand-primary); outline-offset: -1px; }\n.mjc-field input:disabled,\n.mjc-field select:disabled { background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n.mjc-hint { color: var(--mj-text-muted); font-size: var(--mj-text-xs); margin-top: 3px; }\n\n/* \u2500\u2500 Tables \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n The mockup renders modifications, documents and lineage as TABLES, not stacked cards. With three\n or more rows a table is scannable and a stack is a wall. */\n\ntable.mjc-grid { width: 100%; border-collapse: collapse; }\ntable.mjc-grid th {\n text-align: left; font-size: var(--mj-text-xs);\n text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700;\n padding: var(--mj-space-2) var(--mj-space-3);\n border-bottom: 1.5px solid var(--mj-border-default);\n}\ntable.mjc-grid td {\n padding: var(--mj-space-3);\n border-bottom: 1px solid var(--mj-border-subtle);\n vertical-align: top;\n}\ntable.mjc-grid tr:last-child td { border-bottom: 0; }\ntable.mjc-grid tbody tr:hover td { background: var(--mj-bg-surface-sunken); }\n.mjc-row--click { cursor: pointer; }\n\n.mjc-muted { color: var(--mj-text-muted); }\n.mjc-mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: var(--mj-text-xs); }\n\n/* \u2500\u2500 The clause pair (mockups 1 and 2) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n The standard clause and what this contract says instead, read side by side. This is the app's\n central idea rendered, so it gets real styling rather than two paragraphs. */\n\n.mjc-pair {\n display: grid; grid-template-columns: 1fr; gap: var(--mj-space-3);\n margin-top: var(--mj-space-2);\n}\n/* Side by side only when there is room \u2014 two 40ch columns on a laptop is worse than one readable one. */\n@media (min-width: 1100px) { .mjc-pair { grid-template-columns: 1fr 1fr; } }\n\n.mjc-pair__tag {\n display: block; font-size: var(--mj-text-xs); font-weight: 700;\n text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); margin-bottom: 3px;\n}\n.mjc-pair__tag--mod { color: var(--mj-status-warning-text); }\n\n.mjc-clause {\n font-size: var(--mj-text-xs);\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface-sunken);\n border-left: 2px solid var(--mj-border-strong);\n padding: var(--mj-space-2) var(--mj-space-3);\n border-radius: 0 var(--mj-radius-sm) var(--mj-radius-sm) 0;\n margin: 0;\n /* Clause text carries its own line breaks, and 9.1 is a single 1,100-character sentence in caps \u2014\n both must wrap inside the box rather than escaping it. */\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n}\n.mjc-clause--mod {\n border-left-color: var(--mj-status-warning);\n background: var(--mj-status-warning-bg);\n color: var(--mj-text-primary);\n}\n\n.mjc-toggle {\n font-size: var(--mj-text-xs); color: var(--mj-brand-primary);\n font-weight: 600; margin-top: 4px; background: none; border: 0; padding: 0; cursor: pointer;\n}\n\n/* \u2500\u2500 Callout \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mjc-flag {\n background: var(--mj-status-warning-bg);\n border-left: 3px solid var(--mj-status-warning);\n padding: var(--mj-space-2) var(--mj-space-3);\n font-size: var(--mj-text-xs);\n color: var(--mj-status-warning-text);\n border-radius: 0 var(--mj-radius-sm) var(--mj-radius-sm) 0;\n line-height: 1.55;\n}\n\n/* \u2500\u2500 Invalid input \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n/* A required field the user has emptied. Border + ring only \u2014 no background change, because these\n are prose textareas and tinting the reading surface makes the text itself harder to read, which\n is the opposite of helpful when the point is \"write something here\". Tokens only, per the\n design-token gate. */\n.mjc-invalid {\n border-color: var(--mj-status-error) !important;\n box-shadow: 0 0 0 2px var(--mj-status-error-bg);\n outline: none;\n}\n\n/* \u2500\u2500 Filter pills (watchlist) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mjc-pills { display: flex; flex-wrap: wrap; gap: var(--mj-space-2); align-items: center; }\n\n.mjc-pill {\n appearance: none; cursor: pointer;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n border-radius: 999px;\n padding: 3px var(--mj-space-3);\n font-size: var(--mj-text-sm);\n line-height: 1.6;\n display: inline-flex; align-items: center; gap: var(--mj-space-2);\n}\n.mjc-pill:hover { border-color: var(--mj-brand-primary); color: var(--mj-text-primary); }\n.mjc-pill:disabled { opacity: .5; cursor: default; }\n.mjc-pill--active {\n background: var(--mj-status-info-bg);\n border-color: var(--mj-brand-primary);\n color: var(--mj-status-info-text);\n font-weight: 700;\n}\n.mjc-pill__count { font-variant-numeric: tabular-nums; opacity: .85; font-size: var(--mj-text-xs); }\n\n/* \u2500\u2500 Dashboard command center \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mjc-kpis {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));\n gap: var(--mj-space-3);\n}\n.mjc-kpi {\n appearance: none;\n font: inherit;\n text-align: left;\n color: inherit;\n cursor: pointer;\n padding: var(--mj-space-3) var(--mj-space-4);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n background: var(--mj-bg-surface);\n}\n.mjc-kpi:hover { border-color: var(--mj-brand-primary); }\n.mjc-kpi:disabled { opacity: .6; cursor: default; }\n.mjc-kpi--alert .mjc-kpi__v { color: var(--mj-status-error-text); }\n.mjc-kpi__l {\n font-size: var(--mj-text-xs);\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: .04em;\n color: var(--mj-text-muted);\n}\n.mjc-kpi__v {\n font-size: var(--mj-text-xl, 1.35rem);\n font-weight: 700;\n margin: 4px 0 2px;\n font-variant-numeric: tabular-nums;\n}\n.mjc-kpi__f {\n font-size: var(--mj-text-xs);\n color: var(--mj-text-secondary);\n line-height: 1.35;\n}\n\n.mjc-dash-split {\n display: grid;\n grid-template-columns: 1.15fr .85fr;\n gap: var(--mj-space-3);\n}\n@media (max-width: 900px) { .mjc-dash-split { grid-template-columns: 1fr; } }\n\n.mjc-dash-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n overflow: hidden;\n}\n.mjc-dash-card__h {\n display: flex;\n justify-content: space-between;\n gap: var(--mj-space-2);\n align-items: baseline;\n padding: var(--mj-space-3) var(--mj-space-4);\n border-bottom: 1px solid var(--mj-border-subtle);\n font-weight: 700;\n}\n.mjc-dash-card__h span {\n font-weight: 500;\n font-size: var(--mj-text-xs);\n color: var(--mj-text-muted);\n}\n\n.mjc-barlist { display: flex; flex-direction: column; gap: var(--mj-space-2); padding: var(--mj-space-3) var(--mj-space-4); }\n.mjc-barrow {\n display: grid;\n grid-template-columns: 7.5rem 1fr auto;\n gap: var(--mj-space-2);\n align-items: center;\n font-size: var(--mj-text-sm);\n}\n.mjc-track {\n height: 8px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 99px;\n overflow: hidden;\n}\n.mjc-fill {\n height: 100%;\n background: var(--mj-brand-primary);\n border-radius: 99px;\n}\n.mjc-num { font-variant-numeric: tabular-nums; font-weight: 700; }\n\n.mjc-funnel {\n display: flex;\n gap: var(--mj-space-2);\n align-items: flex-end;\n height: 10rem;\n padding: var(--mj-space-3) var(--mj-space-4);\n}\n.mjc-funnel__col {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n}\n.mjc-funnel__bar {\n width: 100%;\n min-height: 8px;\n border-radius: 8px 8px 2px 2px;\n background: var(--mj-brand-primary);\n}\n.mjc-funnel__n { font-weight: 700; }\n.mjc-funnel__lab {\n font-size: var(--mj-text-xs);\n color: var(--mj-text-muted);\n text-align: center;\n}\n\n.mjc-buckets {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: var(--mj-space-2);\n padding: var(--mj-space-3) var(--mj-space-4);\n}\n@media (max-width: 700px) { .mjc-buckets { grid-template-columns: 1fr 1fr; } }\n.mjc-bucket {\n background: var(--mj-bg-page);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n padding: var(--mj-space-3);\n}\n\n.mjc-viewer-host {\n min-height: 420px;\n height: 480px;\n display: flex;\n flex-direction: column;\n}\n.mjc-viewer-host mj-entity-viewer {\n display: flex;\n flex-direction: column;\n flex: 1 1 auto;\n min-height: 350px;\n width: 100%;\n}\n\n/* \u2500\u2500 Empty state \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n A sentence in a bordered box, never a bare line of text in whitespace. */\n\n.mjc-empty {\n display: flex; align-items: center; gap: var(--mj-space-2); justify-content: center;\n padding: var(--mj-space-6);\n color: var(--mj-text-muted);\n font-size: var(--mj-text-sm);\n background: var(--mj-bg-surface-sunken);\n border: 1px dashed var(--mj-border-strong);\n border-radius: var(--mj-radius-md);\n text-align: center;\n}\n", "\n .mjc-hero {\n display: flex; flex-direction: column; gap: var(--mj-space-4);\n padding: 20px 24px; margin-bottom: var(--mj-space-4);\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-xl, 16px);\n box-shadow: var(--mj-shadow-md, 0 4px 16px rgba(0, 0, 0, .08));\n position: relative; overflow: hidden;\n }\n .mjc-hero::before {\n content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3.5px;\n background: linear-gradient(90deg, #38bdf8 0%, #6366f1 50%, #10b981 100%);\n }\n .mjc-hero__identity { display: flex; align-items: center; gap: var(--mj-space-4); min-width: 0; }\n .mjc-hero__avatar {\n flex: none; width: 60px; height: 60px; border-radius: var(--mj-radius-lg, 14px);\n position: relative; display: flex; align-items: center; justify-content: center;\n background: linear-gradient(135deg, color-mix(in srgb, var(--mj-brand-primary) 30%, var(--mj-bg-surface)) 0%, color-mix(in srgb, var(--mj-brand-accent, #6366f1) 25%, var(--mj-bg-surface)) 100%);\n color: var(--mj-brand-primary); font-size: 1.35rem;\n box-shadow: 0 4px 14px color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n border: 2px solid color-mix(in srgb, var(--mj-brand-primary) 35%, transparent);\n }\n .mjc-hero__presence {\n position: absolute; bottom: -2px; right: -2px; width: 13px; height: 13px;\n border-radius: 50%; border: 2.5px solid var(--mj-bg-surface-card);\n background: var(--mj-text-muted, #94a3b8);\n }\n .mjc-hero__presence[data-tone='success'] { background: var(--mj-status-success, #10b981); }\n .mjc-hero__presence[data-tone='warning'] { background: var(--mj-status-warning, #f59e0b); }\n .mjc-hero__copy { min-width: 0; flex: 1; }\n .mjc-hero__title {\n margin: 0; font-size: var(--mj-text-lg, 18px); font-weight: 800;\n letter-spacing: -.02em; line-height: 1.25; color: var(--mj-text-primary);\n overflow-wrap: anywhere;\n }\n .mjc-hero__aka { margin-top: 2px; font-size: var(--mj-text-xs); color: var(--mj-text-muted); }\n .mjc-hero__badges {\n display: flex; align-items: center; flex-wrap: wrap;\n gap: var(--mj-space-2); margin-top: var(--mj-space-2);\n }\n .mjc-hero__entity-chip {\n display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px;\n border-radius: var(--mj-radius-sm);\n background: var(--mj-status-info-bg); color: var(--mj-brand-primary);\n font-size: var(--mj-text-xs); font-weight: 650;\n }\n .mjc-hero__toggle {\n display: inline-flex; align-items: center; justify-content: center;\n flex: none; width: 32px; height: 32px; margin-left: auto; padding: 0;\n border: 1px solid var(--mj-border-default); border-radius: var(--mj-radius-md, 8px);\n background: var(--mj-bg-surface-sunken, rgba(255,255,255,.04));\n color: var(--mj-text-secondary); cursor: pointer; font-size: 12px;\n }\n .mjc-hero__toggle:hover {\n background: var(--mj-bg-surface-hover, rgba(255,255,255,.08));\n color: var(--mj-text-primary); border-color: var(--mj-brand-primary);\n }\n .mjc-hero__summary {\n display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));\n gap: var(--mj-space-3); padding-top: var(--mj-space-4);\n border-top: 1px solid var(--mj-border-default);\n }\n .mjc-hero__stat { display: flex; flex-direction: column; gap: 3px; min-width: 0; }\n .mjc-hero__stat-label {\n font-size: var(--mj-text-xs); font-weight: 700; letter-spacing: .04em;\n text-transform: uppercase; color: var(--mj-text-muted);\n }\n .mjc-hero__stat-val {\n font-size: 15px; font-weight: 650; color: var(--mj-text-primary);\n overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n }\n button.mjc-hero__stat-val, .mjc-hero__stat-val.is-link {\n border: 0; padding: 0; background: transparent; color: var(--mj-text-link);\n cursor: pointer; font: inherit; font-weight: 650; text-align: left;\n }\n button.mjc-hero__stat-val:hover { text-decoration: underline; }\n .mjc-hero__next {\n display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px 14px;\n padding: var(--mj-space-3) var(--mj-space-4);\n background: var(--mj-bg-page); border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n }\n .mjc-hero__next-val { font-weight: 700; font-size: 15px; }\n .mjc-hero__edit {\n display: flex; flex-direction: column; gap: var(--mj-space-3);\n padding-top: var(--mj-space-3); border-top: 1px solid var(--mj-border-subtle, var(--mj-border-default));\n }\n .mjc-hero__field { min-width: 0; }\n .mjc-hero__field .mj-forms-field {\n display: flex; flex-direction: column; align-items: stretch; gap: 4px; padding: 0;\n }\n .mjc-hero__field .mj-forms-field-label {\n font-size: var(--mj-text-xs); font-weight: 700; letter-spacing: .06em;\n text-transform: uppercase; color: var(--mj-text-muted);\n }\n .mjc-hero--collapsed { padding: 12px 20px; gap: 0; margin-bottom: var(--mj-space-3); }\n .mjc-hero--collapsed .mjc-hero__avatar { width: 42px; height: 42px; border-radius: var(--mj-radius-md, 10px); font-size: 1.05rem; }\n .mjc-hero--collapsed .mjc-hero__title { font-size: 1.15rem; }\n @media (max-width: 720px) {\n .mjc-hero__identity { align-items: flex-start; }\n .mjc-hero__summary { grid-template-columns: repeat(2, minmax(0, 1fr)); }\n }\n "], encapsulation: 2 });
516
730
  };
517
731
  MJCContractHeroPanel = __decorate([
518
732
  RegisterClassEx(BaseFormPanel, {
@@ -529,84 +743,158 @@ MJCContractHeroPanel = __decorate([
529
743
  export { MJCContractHeroPanel };
530
744
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MJCContractHeroPanel, [{
531
745
  type: Component,
532
- args: [{
533
- selector: 'mjc-contract-hero-panel',
534
- standalone: true,
535
- encapsulation: ViewEncapsulation.None,
536
- imports: [CommonModule, BaseFormsModule],
537
- template: `
538
- <div class="mjc-hero">
746
+ args: [{ selector: 'mjc-contract-hero-panel', standalone: true, encapsulation: ViewEncapsulation.None, imports: [CommonModule, BaseFormsModule], template: `
747
+ <div class="mjc-hero" [class.mjc-hero--collapsed]="Collapsed">
539
748
  <div class="mjc-hero__identity">
540
- <div class="mjc-hero__avatar"><i class="fa-solid fa-file-signature" aria-hidden="true"></i></div>
749
+ <div class="mjc-hero__avatar" aria-hidden="true">
750
+ <i class="fa-solid fa-file-signature"></i>
751
+ @if (!EditMode) {
752
+ <span class="mjc-hero__presence" [attr.data-tone]="StatusTone" [title]="State"></span>
753
+ }
754
+ </div>
541
755
  <div class="mjc-hero__copy">
542
756
  <div class="mjc-hero__title-row">
543
757
  <h1 class="mjc-hero__title">{{ Title }}</h1>
758
+ </div>
759
+ @if (Record.ContractNumber && !Collapsed) {
760
+ <div class="mjc-hero__aka">{{ Record.ContractNumber }}</div>
761
+ }
762
+ <div class="mjc-hero__badges">
763
+ <span class="mjc-hero__entity-chip"><i class="fa-solid fa-file-signature"></i> Contract</span>
544
764
  <span class="mjc-chip" [class]="'mjc-chip ' + StateChipClass">{{ State }}</span>
545
765
  @if (TypeName) { <span class="mjc-chip mjc-chip--info">{{ TypeName }}</span> }
546
766
  @if (Record.HasModifications) {
547
767
  <span class="mjc-chip mjc-chip--warn"
548
- title="This contract deviates from the standard agreement — read the paper">
768
+ title="Standard agreement was modified">
549
769
  <i class="fa-solid fa-triangle-exclamation" aria-hidden="true"></i> Agreement modified
550
770
  </span>
551
771
  }
552
772
  @if (IsAwaitingDocument) {
553
773
  <span class="mjc-chip mjc-chip--warn"
554
- title="This contract type expects an executed document and none is attached">
774
+ title="Executed agreement not attached">
555
775
  <i class="fa-solid fa-file-circle-question" aria-hidden="true"></i> Awaiting document
556
776
  </span>
557
777
  }
558
778
  </div>
559
- <div class="mjc-hero__meta">
560
- <span class="mjc-mono">{{ Record.ContractNumber || 'Unnumbered' }}</span>
561
- <span>Customer: <strong>{{ CustomerName || '—' }}</strong></span>
562
- <span>Selling: <strong>{{ CompanyName || '—' }}</strong></span>
563
- @if (ContactName) { <span>Contact: <strong>{{ ContactName }}</strong></span> }
564
- </div>
565
779
  </div>
780
+ <button type="button" class="mjc-hero__toggle"
781
+ [title]="Collapsed ? 'Expand header' : 'Collapse header'"
782
+ [attr.aria-label]="Collapsed ? 'Expand header' : 'Collapse header'"
783
+ (click)="ToggleCollapsed()">
784
+ <i [class]="Collapsed ? 'fa-solid fa-chevron-down' : 'fa-solid fa-chevron-up'"></i>
785
+ </button>
566
786
  </div>
567
-
568
- <div class="mjc-hero__stats">
569
- <div class="mjc-stat">
570
- <span class="mjc-stat__label">Executed</span>
571
- <span class="mjc-stat__value">{{ (Record.ExecutedDate | date: 'd MMM y') || '—' }}</span>
572
- @if (!Record.ExecutedDate) { <span class="mjc-stat__sub">not signed yet</span> }
573
- </div>
574
- <div class="mjc-stat">
575
- <span class="mjc-stat__label">Effective</span>
576
- <span class="mjc-stat__value">{{ (Record.EffectiveDate | date: 'd MMM y') || '—' }}</span>
577
- </div>
578
- <div class="mjc-stat">
579
- <span class="mjc-stat__label">Term ends</span>
580
- <span class="mjc-stat__value">{{ (Record.EndDate | date: 'd MMM y') || '—' }}</span>
581
- @if (DaysToEnd !== null) { <span class="mjc-stat__sub">{{ EndsInText }}</span> }
582
- </div>
583
- <div class="mjc-stat">
584
- <span class="mjc-stat__label">Agreement</span>
585
- <span class="mjc-stat__value">{{ TemplateName || '—' }}</span>
586
- @if (!TemplateName) { <span class="mjc-stat__sub">no standard terms referenced</span> }
587
- </div>
588
- <div class="mjc-stat">
589
- <span class="mjc-stat__label">Created by</span>
590
- <span class="mjc-stat__value">{{ CreatingEntityName || '—' }}</span>
591
- @if (!CreatingEntityName) { <span class="mjc-stat__sub">entered directly</span> }
592
- </div>
593
- </div>
594
-
595
- @if (!Record.ContractNumber) {
596
- <div class="mjc-flag">
597
- The contract number is minted on first save, from a counter taken under a lock — so it
598
- cannot collide with another contract created at the same moment.
787
+ @if (!Collapsed) {
788
+ <div class="mjc-hero__summary">
789
+ <!--
790
+ OUR SIDE FIRST, AND IT IS NOT CALLED "SELLING" (#28 item 8).
791
+
792
+ CompanyID is which of OUR entities holds the agreement. Someone scanning contracts
793
+ orients by that before they look at who it is with, so it leads the summary rather
794
+ than sitting third. "Selling" was also the wrong word: the company holds the
795
+ contract whether or not anything is being sold.
796
+ -->
797
+ <div class="mjc-hero__stat">
798
+ <span class="mjc-hero__stat-label">Company</span>
799
+ <span class="mjc-hero__stat-val">{{ CompanyName || '—' }}</span>
800
+ </div>
801
+ <div class="mjc-hero__stat">
802
+ <span class="mjc-hero__stat-label">Customer</span>
803
+ @if (Record.CustomerOrganizationID && CustomerName) {
804
+ <button type="button" class="mjc-hero__stat-val is-link" (click)="OpenCustomer($event)">{{ CustomerName }}</button>
805
+ } @else {
806
+ <span class="mjc-hero__stat-val">{{ CustomerName || '—' }}</span>
807
+ }
808
+ </div>
809
+ <div class="mjc-hero__stat">
810
+ <span class="mjc-hero__stat-label">Contact</span>
811
+ @if (Record.PrimaryContactPersonID && ContactName) {
812
+ <button type="button" class="mjc-hero__stat-val is-link" (click)="OpenContact($event)">{{ ContactName }}</button>
813
+ } @else {
814
+ <span class="mjc-hero__stat-val">{{ ContactName || '—' }}</span>
815
+ }
816
+ </div>
817
+ <div class="mjc-hero__stat">
818
+ <span class="mjc-hero__stat-label">Executed</span>
819
+ <span class="mjc-hero__stat-val">{{ (Record.ExecutedDate | date: 'd MMM y') || '—' }}</span>
820
+ </div>
821
+ <div class="mjc-hero__stat">
822
+ <span class="mjc-hero__stat-label">Effective</span>
823
+ <span class="mjc-hero__stat-val">{{ (Record.EffectiveDate | date: 'd MMM y') || '—' }}</span>
824
+ </div>
825
+ <div class="mjc-hero__stat">
826
+ <span class="mjc-hero__stat-label">Term ends</span>
827
+ <span class="mjc-hero__stat-val">{{ (Record.EndDate | date: 'd MMM y') || '—' }}</span>
828
+ </div>
829
+ <div class="mjc-hero__stat">
830
+ <span class="mjc-hero__stat-label">Agreement</span>
831
+ <span class="mjc-hero__stat-val">{{ TemplateName || '—' }}</span>
832
+ </div>
833
+ @if (HasSource) {
834
+ <div class="mjc-hero__stat">
835
+ <span class="mjc-hero__stat-label">{{ SourceLabel }}</span>
836
+ <button type="button" class="mjc-hero__stat-val is-link" (click)="OpenSource($event)"
837
+ [attr.aria-label]="'Open ' + (SourceName || 'the source record')">
838
+ {{ SourceName || 'Open' }}
839
+ </button>
840
+ </div>
841
+ }
599
842
  </div>
843
+ @if (DaysToEnd !== null) {
844
+ <div class="mjc-hero__next">
845
+ <span class="mjc-hero__stat-label">Term</span>
846
+ <span class="mjc-hero__next-val">{{ EndsInText }}</span>
847
+ </div>
848
+ }
849
+ @if (EditMode) {
850
+ <div class="mjc-hero__edit">
851
+ <div class="mjc-hero__field">
852
+ <mj-form-field [Record]="Record" [ShowLabel]="true" FieldName="Description"
853
+ Type="textarea" [EditMode]="EditMode" [FormContext]="FormContext"></mj-form-field>
854
+ </div>
855
+ </div>
856
+ }
857
+ @if (!Record.ContractNumber) {
858
+ <div class="mjc-flag">Contract number is assigned on save.</div>
859
+ }
600
860
  }
601
861
  </div>
602
- `,
603
- }]
862
+ `, styles: ["/**\n * contracts-kit.css \u2014 ported from design-docs/ui-design/mockups-v2/tokens.css.\n *\n * \u26A0 SEMANTIC TOKENS ONLY. MJ's design-token rule is explicit: *\"NEVER use primitive tokens\n * (`--mj-color-neutral-*`, `--mj-color-brand-*`) in component CSS. Primitives don't adapt to dark\n * mode.\"* An earlier version of this file used twelve of them, because the mockup did \u2014 and in a\n * standalone mockup page that is harmless, since there is no dark mode to adapt to. Ported into a real\n * component stylesheet it means every tinted surface, chip and clause box keeps its LIGHT colour when\n * the app is themed dark. That is the theming break, and it is invisible until someone switches theme.\n *\n * The mapping applied: neutral-50 \u2192 bg-surface-sunken, neutral-100 \u2192 border-subtle, neutral-300 \u2192\n * border-strong, success/error/warning-50 \u2192 status-*-bg, success/error-700 \u2192 status-*-text, brand-50 \u2192\n * status-info-bg, brand-100 \u2192 status-info-border, brand-700/900 \u2192 status-info-text. `#fff` on a brand\n * fill \u2192 `--mj-brand-on-primary`.\n *\n * THE MOCKUPS ARE THE SPEC, and this file exists to keep them so. An earlier version of this kit was\n * written from MEMORY of them, and it was wrong in a way that is invisible in source and obvious on\n * screen: it used token names that do not exist. `--mj-font-size-sm`, `--mj-text-tertiary`,\n * `--mj-color-warning-text`, `--mj-color-success-border` and four others are not MJ tokens (the real\n * names are `--mj-text-sm`, `--mj-status-warning-text`, `--mj-status-success`, \u2026). Every one of those\n * resolved to NOTHING \u2014 so font sizes fell back to browser defaults and the state chips rendered with\n * no colour at all. That is most of what \"looks bad\" was.\n *\n * So: class names and rules below are the mockup's, verbatim where possible, and **every token used\n * here has been checked to exist in MJ's `_tokens.scss`.** If you add a rule, check the token first \u2014\n * a missing custom property fails silently, which is the worst failure mode CSS has.\n *\n * SHIPPED VIA THE SECTION SHELL (`styleUrls` + `ViewEncapsulation.None`). `ngc` only compiles what a\n * component references, so a stylesheet nothing references is built into dist and never loaded.\n * `None` is required, not lazy: these classes are used by DESCENDANT components.\n *\n * Prefix note: the mockup used bare class names (`.panel`, `.chip`) because it was a standalone page.\n * Here everything is `mjc-` prefixed \u2014 bare `.panel` inside a real Explorer would collide with MJ's own\n * chrome, and `ViewEncapsulation.None` means these rules are global.\n */\n\n/* \u2500\u2500 Page frame \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mjc-page {\n display: flex;\n flex-direction: column;\n gap: var(--mj-space-4);\n padding: var(--mj-space-5);\n}\n\n/* A grid page needs a DEFINITE height: mj-explorer-entity-data-grid is height:100% outside a related\n panel, and 100% of an auto-height parent is zero \u2014 the grid renders no rows while its toolbar still\n reports the count. MJ's own source documents that symptom. `min-height: 0` on the fill slot is\n mandatory: a flex child defaults to min-height:auto and refuses to shrink, so without it the grid\n pushes the page taller instead of scrolling inside itself. */\n.mjc-page--grid { height: 100%; min-height: 0; }\n.mjc-grid-fill { flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column; }\n\n/* Intro prose. Constrained measure \u2014 text stops being readable much past this \u2014 and it is a real\n block with real spacing, not a bare text node floating between cards. */\n.mjc-page__intro {\n color: var(--mj-text-secondary);\n font-size: var(--mj-text-sm);\n line-height: 1.55;\n margin: 0;\n max-width: 78ch;\n}\n\n/* \u2500\u2500 Panel body helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n Our panels are real `<mj-collapsible-panel>`s, so MJ paints the frame, the header and the spacing.\n These classes style only what goes INSIDE one. */\n\n.mjc-body { padding: var(--mj-space-4); }\n.mjc-body--flush { padding: 0; } /* a table fills the panel edge to edge */\n.mjc-body > * + * { margin-top: var(--mj-space-3); } /* nothing ever touches its neighbour */\n\n.mjc-note {\n margin: 0;\n color: var(--mj-text-muted);\n font-size: var(--mj-text-xs);\n line-height: 1.55;\n}\n\n/* A note under a flush table needs its own padding and a rule above it, or it reads as a table row. */\n.mjc-note--footer {\n padding: var(--mj-space-3) var(--mj-space-4);\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n/* \u2500\u2500 Hero (mockup 1) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mjc-hero {\n display: flex;\n flex-direction: column;\n gap: var(--mj-space-4);\n padding: var(--mj-space-4) var(--mj-space-5);\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg);\n box-shadow: var(--mj-shadow-sm);\n}\n\n.mjc-hero__identity { display: flex; align-items: center; gap: var(--mj-space-4); min-width: 0; }\n\n.mjc-hero__avatar {\n flex: none;\n width: 52px; height: 52px;\n border-radius: var(--mj-radius-md);\n display: flex; align-items: center; justify-content: center;\n background: color-mix(in srgb, var(--mj-brand-primary) 18%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: var(--mj-text-xl, 1.375rem);\n}\n\n.mjc-hero__copy { min-width: 0; flex: 1; }\n\n.mjc-hero__title-row {\n display: flex; align-items: center; flex-wrap: wrap;\n gap: var(--mj-space-2) var(--mj-space-3);\n}\n\n.mjc-hero__title {\n margin: 0;\n font-size: var(--mj-text-lg);\n font-weight: 700;\n line-height: 1.25;\n /* Long contract descriptions must not push the chips off the card. */\n overflow-wrap: anywhere;\n}\n\n.mjc-hero__meta {\n display: flex; flex-wrap: wrap;\n gap: var(--mj-space-3);\n color: var(--mj-text-secondary);\n font-size: var(--mj-text-sm);\n margin-top: var(--mj-space-2);\n}\n\n/* auto-fit so five stats sit in a row on a wide screen and reflow on a laptop, rather than\n overflowing the card \u2014 which is what a fixed five-column grid did. */\n.mjc-hero__stats {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));\n gap: var(--mj-space-3);\n}\n\n.mjc-stat {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n padding: var(--mj-space-3);\n min-width: 0;\n}\n.mjc-stat__label {\n display: block; font-size: var(--mj-text-xs);\n text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted);\n}\n.mjc-stat__value {\n display: block; font-size: var(--mj-text-base); font-weight: 700; margin-top: 2px;\n overflow-wrap: anywhere;\n}\n.mjc-stat__sub { display: block; font-size: var(--mj-text-xs); color: var(--mj-text-muted); }\n\n/* \u2500\u2500 Chips \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mjc-chip {\n display: inline-flex; align-items: center; gap: 4px;\n font-size: var(--mj-text-xs); font-weight: 600;\n padding: 2px 9px; border-radius: 999px;\n border: 1px solid transparent; white-space: nowrap;\n}\n.mjc-chip--ok { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); border-color: var(--mj-status-success); }\n.mjc-chip--warn { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); border-color: var(--mj-status-warning); }\n.mjc-chip--error { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); border-color: var(--mj-status-error); }\n.mjc-chip--info { background: var(--mj-status-info-bg); color: var(--mj-status-info-text); border-color: var(--mj-status-info-border); }\n.mjc-chip--muted { background: var(--mj-border-subtle); color: var(--mj-text-secondary); border-color: var(--mj-border-default); }\n\n/* \u2500\u2500 Buttons \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mjc-btn {\n display: inline-flex; align-items: center; gap: 6px;\n font-size: var(--mj-text-sm); font-weight: 600;\n padding: 6px 12px;\n border-radius: var(--mj-radius-sm);\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n cursor: pointer;\n}\n.mjc-btn:disabled { opacity: .5; cursor: default; }\n.mjc-btn--primary { background: var(--mj-brand-primary); border-color: var(--mj-brand-primary); color: var(--mj-brand-on-primary); }\n.mjc-btn--flat { background: transparent; border-color: transparent; color: var(--mj-brand-primary); }\n.mjc-btn--sm { padding: 3px 9px; font-size: var(--mj-text-xs); }\n.mjc-btn--danger { color: var(--mj-status-error-text); border-color: transparent; background: transparent; }\n\n/* \u2500\u2500 Field grid \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mjc-fields {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));\n gap: var(--mj-space-3) var(--mj-space-4);\n}\n.mjc-field > label {\n display: block; font-size: var(--mj-text-xs); color: var(--mj-text-muted);\n text-transform: uppercase; letter-spacing: .04em; margin-bottom: 3px;\n}\n.mjc-field .mjc-val {\n padding: 6px 9px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm);\n background: var(--mj-bg-surface);\n min-height: 31px;\n overflow-wrap: anywhere;\n}\n.mjc-field .mjc-val--ro { background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n\n/* EDIT MODE MATCHES READ MODE. These panels render bare native controls -- `<input type=\"date\">`,\n `<input type=\"number\">`, `<select>` -- because the generated datetime picker shows a time\n component for a `date` column, which is the whole reason the Dates panel exists. A bare control\n inherits none of the app's typography or chrome, so the same field visibly changed font, height\n and border the moment you pressed Edit. This mirrors `.mjc-val` above, field for field, so\n toggling edit does not move anything on screen.\n\n `font: inherit` is doing real work: form controls do NOT inherit font from their ancestor by\n default in any browser -- they fall back to a UA font -- so without it the value box and the\n input disagree even when every other property matches.\n\n THE WIDTH LIVES HERE, not on each control. The panels used to carry seven inline\n `style=\"width:100%\"` attributes; they are gone, and this rule is what replaces them. */\n.mjc-field input,\n.mjc-field select {\n width: 100%;\n box-sizing: border-box;\n font: inherit;\n color: inherit;\n padding: 6px 9px;\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-sm);\n background: var(--mj-bg-surface);\n min-height: 31px;\n}\n.mjc-field input:focus-visible,\n.mjc-field select:focus-visible { outline: 2px solid var(--mj-brand-primary); outline-offset: -1px; }\n.mjc-field input:disabled,\n.mjc-field select:disabled { background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n.mjc-hint { color: var(--mj-text-muted); font-size: var(--mj-text-xs); margin-top: 3px; }\n\n/* \u2500\u2500 Tables \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n The mockup renders modifications, documents and lineage as TABLES, not stacked cards. With three\n or more rows a table is scannable and a stack is a wall. */\n\ntable.mjc-grid { width: 100%; border-collapse: collapse; }\ntable.mjc-grid th {\n text-align: left; font-size: var(--mj-text-xs);\n text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700;\n padding: var(--mj-space-2) var(--mj-space-3);\n border-bottom: 1.5px solid var(--mj-border-default);\n}\ntable.mjc-grid td {\n padding: var(--mj-space-3);\n border-bottom: 1px solid var(--mj-border-subtle);\n vertical-align: top;\n}\ntable.mjc-grid tr:last-child td { border-bottom: 0; }\ntable.mjc-grid tbody tr:hover td { background: var(--mj-bg-surface-sunken); }\n.mjc-row--click { cursor: pointer; }\n\n.mjc-muted { color: var(--mj-text-muted); }\n.mjc-mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: var(--mj-text-xs); }\n\n/* \u2500\u2500 The clause pair (mockups 1 and 2) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n The standard clause and what this contract says instead, read side by side. This is the app's\n central idea rendered, so it gets real styling rather than two paragraphs. */\n\n.mjc-pair {\n display: grid; grid-template-columns: 1fr; gap: var(--mj-space-3);\n margin-top: var(--mj-space-2);\n}\n/* Side by side only when there is room \u2014 two 40ch columns on a laptop is worse than one readable one. */\n@media (min-width: 1100px) { .mjc-pair { grid-template-columns: 1fr 1fr; } }\n\n.mjc-pair__tag {\n display: block; font-size: var(--mj-text-xs); font-weight: 700;\n text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); margin-bottom: 3px;\n}\n.mjc-pair__tag--mod { color: var(--mj-status-warning-text); }\n\n.mjc-clause {\n font-size: var(--mj-text-xs);\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface-sunken);\n border-left: 2px solid var(--mj-border-strong);\n padding: var(--mj-space-2) var(--mj-space-3);\n border-radius: 0 var(--mj-radius-sm) var(--mj-radius-sm) 0;\n margin: 0;\n /* Clause text carries its own line breaks, and 9.1 is a single 1,100-character sentence in caps \u2014\n both must wrap inside the box rather than escaping it. */\n white-space: pre-wrap;\n overflow-wrap: anywhere;\n}\n.mjc-clause--mod {\n border-left-color: var(--mj-status-warning);\n background: var(--mj-status-warning-bg);\n color: var(--mj-text-primary);\n}\n\n.mjc-toggle {\n font-size: var(--mj-text-xs); color: var(--mj-brand-primary);\n font-weight: 600; margin-top: 4px; background: none; border: 0; padding: 0; cursor: pointer;\n}\n\n/* \u2500\u2500 Callout \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mjc-flag {\n background: var(--mj-status-warning-bg);\n border-left: 3px solid var(--mj-status-warning);\n padding: var(--mj-space-2) var(--mj-space-3);\n font-size: var(--mj-text-xs);\n color: var(--mj-status-warning-text);\n border-radius: 0 var(--mj-radius-sm) var(--mj-radius-sm) 0;\n line-height: 1.55;\n}\n\n/* \u2500\u2500 Invalid input \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n/* A required field the user has emptied. Border + ring only \u2014 no background change, because these\n are prose textareas and tinting the reading surface makes the text itself harder to read, which\n is the opposite of helpful when the point is \"write something here\". Tokens only, per the\n design-token gate. */\n.mjc-invalid {\n border-color: var(--mj-status-error) !important;\n box-shadow: 0 0 0 2px var(--mj-status-error-bg);\n outline: none;\n}\n\n/* \u2500\u2500 Filter pills (watchlist) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mjc-pills { display: flex; flex-wrap: wrap; gap: var(--mj-space-2); align-items: center; }\n\n.mjc-pill {\n appearance: none; cursor: pointer;\n border: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n border-radius: 999px;\n padding: 3px var(--mj-space-3);\n font-size: var(--mj-text-sm);\n line-height: 1.6;\n display: inline-flex; align-items: center; gap: var(--mj-space-2);\n}\n.mjc-pill:hover { border-color: var(--mj-brand-primary); color: var(--mj-text-primary); }\n.mjc-pill:disabled { opacity: .5; cursor: default; }\n.mjc-pill--active {\n background: var(--mj-status-info-bg);\n border-color: var(--mj-brand-primary);\n color: var(--mj-status-info-text);\n font-weight: 700;\n}\n.mjc-pill__count { font-variant-numeric: tabular-nums; opacity: .85; font-size: var(--mj-text-xs); }\n\n/* \u2500\u2500 Dashboard command center \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mjc-kpis {\n display: grid;\n grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));\n gap: var(--mj-space-3);\n}\n.mjc-kpi {\n appearance: none;\n font: inherit;\n text-align: left;\n color: inherit;\n cursor: pointer;\n padding: var(--mj-space-3) var(--mj-space-4);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n background: var(--mj-bg-surface);\n}\n.mjc-kpi:hover { border-color: var(--mj-brand-primary); }\n.mjc-kpi:disabled { opacity: .6; cursor: default; }\n.mjc-kpi--alert .mjc-kpi__v { color: var(--mj-status-error-text); }\n.mjc-kpi__l {\n font-size: var(--mj-text-xs);\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: .04em;\n color: var(--mj-text-muted);\n}\n.mjc-kpi__v {\n font-size: var(--mj-text-xl, 1.35rem);\n font-weight: 700;\n margin: 4px 0 2px;\n font-variant-numeric: tabular-nums;\n}\n.mjc-kpi__f {\n font-size: var(--mj-text-xs);\n color: var(--mj-text-secondary);\n line-height: 1.35;\n}\n\n.mjc-dash-split {\n display: grid;\n grid-template-columns: 1.15fr .85fr;\n gap: var(--mj-space-3);\n}\n@media (max-width: 900px) { .mjc-dash-split { grid-template-columns: 1fr; } }\n\n.mjc-dash-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n overflow: hidden;\n}\n.mjc-dash-card__h {\n display: flex;\n justify-content: space-between;\n gap: var(--mj-space-2);\n align-items: baseline;\n padding: var(--mj-space-3) var(--mj-space-4);\n border-bottom: 1px solid var(--mj-border-subtle);\n font-weight: 700;\n}\n.mjc-dash-card__h span {\n font-weight: 500;\n font-size: var(--mj-text-xs);\n color: var(--mj-text-muted);\n}\n\n.mjc-barlist { display: flex; flex-direction: column; gap: var(--mj-space-2); padding: var(--mj-space-3) var(--mj-space-4); }\n.mjc-barrow {\n display: grid;\n grid-template-columns: 7.5rem 1fr auto;\n gap: var(--mj-space-2);\n align-items: center;\n font-size: var(--mj-text-sm);\n}\n.mjc-track {\n height: 8px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 99px;\n overflow: hidden;\n}\n.mjc-fill {\n height: 100%;\n background: var(--mj-brand-primary);\n border-radius: 99px;\n}\n.mjc-num { font-variant-numeric: tabular-nums; font-weight: 700; }\n\n.mjc-funnel {\n display: flex;\n gap: var(--mj-space-2);\n align-items: flex-end;\n height: 10rem;\n padding: var(--mj-space-3) var(--mj-space-4);\n}\n.mjc-funnel__col {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 6px;\n}\n.mjc-funnel__bar {\n width: 100%;\n min-height: 8px;\n border-radius: 8px 8px 2px 2px;\n background: var(--mj-brand-primary);\n}\n.mjc-funnel__n { font-weight: 700; }\n.mjc-funnel__lab {\n font-size: var(--mj-text-xs);\n color: var(--mj-text-muted);\n text-align: center;\n}\n\n.mjc-buckets {\n display: grid;\n grid-template-columns: repeat(4, 1fr);\n gap: var(--mj-space-2);\n padding: var(--mj-space-3) var(--mj-space-4);\n}\n@media (max-width: 700px) { .mjc-buckets { grid-template-columns: 1fr 1fr; } }\n.mjc-bucket {\n background: var(--mj-bg-page);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n padding: var(--mj-space-3);\n}\n\n.mjc-viewer-host {\n min-height: 420px;\n height: 480px;\n display: flex;\n flex-direction: column;\n}\n.mjc-viewer-host mj-entity-viewer {\n display: flex;\n flex-direction: column;\n flex: 1 1 auto;\n min-height: 350px;\n width: 100%;\n}\n\n/* \u2500\u2500 Empty state \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n A sentence in a bordered box, never a bare line of text in whitespace. */\n\n.mjc-empty {\n display: flex; align-items: center; gap: var(--mj-space-2); justify-content: center;\n padding: var(--mj-space-6);\n color: var(--mj-text-muted);\n font-size: var(--mj-text-sm);\n background: var(--mj-bg-surface-sunken);\n border: 1px dashed var(--mj-border-strong);\n border-radius: var(--mj-radius-md);\n text-align: center;\n}\n", "\n .mjc-hero {\n display: flex; flex-direction: column; gap: var(--mj-space-4);\n padding: 20px 24px; margin-bottom: var(--mj-space-4);\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-xl, 16px);\n box-shadow: var(--mj-shadow-md, 0 4px 16px rgba(0, 0, 0, .08));\n position: relative; overflow: hidden;\n }\n .mjc-hero::before {\n content: ''; position: absolute; top: 0; left: 0; right: 0; height: 3.5px;\n background: linear-gradient(90deg, #38bdf8 0%, #6366f1 50%, #10b981 100%);\n }\n .mjc-hero__identity { display: flex; align-items: center; gap: var(--mj-space-4); min-width: 0; }\n .mjc-hero__avatar {\n flex: none; width: 60px; height: 60px; border-radius: var(--mj-radius-lg, 14px);\n position: relative; display: flex; align-items: center; justify-content: center;\n background: linear-gradient(135deg, color-mix(in srgb, var(--mj-brand-primary) 30%, var(--mj-bg-surface)) 0%, color-mix(in srgb, var(--mj-brand-accent, #6366f1) 25%, var(--mj-bg-surface)) 100%);\n color: var(--mj-brand-primary); font-size: 1.35rem;\n box-shadow: 0 4px 14px color-mix(in srgb, var(--mj-brand-primary) 25%, transparent);\n border: 2px solid color-mix(in srgb, var(--mj-brand-primary) 35%, transparent);\n }\n .mjc-hero__presence {\n position: absolute; bottom: -2px; right: -2px; width: 13px; height: 13px;\n border-radius: 50%; border: 2.5px solid var(--mj-bg-surface-card);\n background: var(--mj-text-muted, #94a3b8);\n }\n .mjc-hero__presence[data-tone='success'] { background: var(--mj-status-success, #10b981); }\n .mjc-hero__presence[data-tone='warning'] { background: var(--mj-status-warning, #f59e0b); }\n .mjc-hero__copy { min-width: 0; flex: 1; }\n .mjc-hero__title {\n margin: 0; font-size: var(--mj-text-lg, 18px); font-weight: 800;\n letter-spacing: -.02em; line-height: 1.25; color: var(--mj-text-primary);\n overflow-wrap: anywhere;\n }\n .mjc-hero__aka { margin-top: 2px; font-size: var(--mj-text-xs); color: var(--mj-text-muted); }\n .mjc-hero__badges {\n display: flex; align-items: center; flex-wrap: wrap;\n gap: var(--mj-space-2); margin-top: var(--mj-space-2);\n }\n .mjc-hero__entity-chip {\n display: inline-flex; align-items: center; gap: 4px; padding: 2px 8px;\n border-radius: var(--mj-radius-sm);\n background: var(--mj-status-info-bg); color: var(--mj-brand-primary);\n font-size: var(--mj-text-xs); font-weight: 650;\n }\n .mjc-hero__toggle {\n display: inline-flex; align-items: center; justify-content: center;\n flex: none; width: 32px; height: 32px; margin-left: auto; padding: 0;\n border: 1px solid var(--mj-border-default); border-radius: var(--mj-radius-md, 8px);\n background: var(--mj-bg-surface-sunken, rgba(255,255,255,.04));\n color: var(--mj-text-secondary); cursor: pointer; font-size: 12px;\n }\n .mjc-hero__toggle:hover {\n background: var(--mj-bg-surface-hover, rgba(255,255,255,.08));\n color: var(--mj-text-primary); border-color: var(--mj-brand-primary);\n }\n .mjc-hero__summary {\n display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));\n gap: var(--mj-space-3); padding-top: var(--mj-space-4);\n border-top: 1px solid var(--mj-border-default);\n }\n .mjc-hero__stat { display: flex; flex-direction: column; gap: 3px; min-width: 0; }\n .mjc-hero__stat-label {\n font-size: var(--mj-text-xs); font-weight: 700; letter-spacing: .04em;\n text-transform: uppercase; color: var(--mj-text-muted);\n }\n .mjc-hero__stat-val {\n font-size: 15px; font-weight: 650; color: var(--mj-text-primary);\n overflow: hidden; text-overflow: ellipsis; white-space: nowrap;\n }\n button.mjc-hero__stat-val, .mjc-hero__stat-val.is-link {\n border: 0; padding: 0; background: transparent; color: var(--mj-text-link);\n cursor: pointer; font: inherit; font-weight: 650; text-align: left;\n }\n button.mjc-hero__stat-val:hover { text-decoration: underline; }\n .mjc-hero__next {\n display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px 14px;\n padding: var(--mj-space-3) var(--mj-space-4);\n background: var(--mj-bg-page); border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-md);\n }\n .mjc-hero__next-val { font-weight: 700; font-size: 15px; }\n .mjc-hero__edit {\n display: flex; flex-direction: column; gap: var(--mj-space-3);\n padding-top: var(--mj-space-3); border-top: 1px solid var(--mj-border-subtle, var(--mj-border-default));\n }\n .mjc-hero__field { min-width: 0; }\n .mjc-hero__field .mj-forms-field {\n display: flex; flex-direction: column; align-items: stretch; gap: 4px; padding: 0;\n }\n .mjc-hero__field .mj-forms-field-label {\n font-size: var(--mj-text-xs); font-weight: 700; letter-spacing: .06em;\n text-transform: uppercase; color: var(--mj-text-muted);\n }\n .mjc-hero--collapsed { padding: 12px 20px; gap: 0; margin-bottom: var(--mj-space-3); }\n .mjc-hero--collapsed .mjc-hero__avatar { width: 42px; height: 42px; border-radius: var(--mj-radius-md, 10px); font-size: 1.05rem; }\n .mjc-hero--collapsed .mjc-hero__title { font-size: 1.15rem; }\n @media (max-width: 720px) {\n .mjc-hero__identity { align-items: flex-start; }\n .mjc-hero__summary { grid-template-columns: repeat(2, minmax(0, 1fr)); }\n }\n "] }]
604
863
  }], null, null); })();
605
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJCContractHeroPanel, { className: "MJCContractHeroPanel", filePath: "lib/form-panels/contract.panels.ts", lineNumber: 144 }); })();
864
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJCContractHeroPanel, { className: "MJCContractHeroPanel", filePath: "lib/form-panels/contract.panels.ts", lineNumber: 303 }); })();
606
865
  /* ────────────────────────────────────────────────────────────────────────────
607
866
  * 2 · Renewal terms — a real rail section
608
867
  * ──────────────────────────────────────────────────────────────────────────── */
609
868
  let MJCContractRenewalPanel = class MJCContractRenewalPanel extends BaseFormPanel {
869
+ /**
870
+ * A day count, where ZERO IS A VALUE (issue #28 item 21).
871
+ *
872
+ * `Record.X ? … : '—'` conflates ZERO with ABSENT: a recorded `0` rendered as the same em dash as
873
+ * a blank, so the panel reported holding no figure about a figure that says "no notice is
874
+ * required" — which somebody negotiated. `== null` is the whole fix, and it is deliberately `==`
875
+ * rather than `===` so `undefined` is caught too.
876
+ */
877
+ Days(v) {
878
+ return v == null ? '—' : `${v} days`;
879
+ }
880
+ /** Same rule for the percentage: `0%` is a negotiated cap, not a missing value. */
881
+ Percent(v) {
882
+ return v == null ? '—' : `${v}%`;
883
+ }
884
+ /**
885
+ * Whether this agreement genuinely states no renewal terms.
886
+ *
887
+ * AUTO-RENEWS IS ONE OF THE TERMS, and leaving it out of the condition is why the empty state
888
+ * used to contradict the screen: `AutoRenew = Yes` with blank day counts showed "No renewal terms
889
+ * recorded" directly beneath a field reading Yes. Auto-renewal is the most consequential renewal
890
+ * term there is, so the panel is only empty when it is No AND all three numbers are absent.
891
+ */
892
+ get NoTermsRecorded() {
893
+ return !this.Record?.AutoRenew &&
894
+ this.Record?.RenewalNoticeDays == null &&
895
+ this.Record?.CancellationWindowDays == null &&
896
+ this.Record?.AnnualIncreasePercent == null;
897
+ }
610
898
  /**
611
899
  * Write a field through the typed entity. Named `Set` rather than bound with two-way `[(ngModel)]`
612
900
  * so the write goes through one place — a two-way binding on `Record.X` works but gives no hook for
@@ -619,7 +907,7 @@ let MJCContractRenewalPanel = class MJCContractRenewalPanel extends BaseFormPane
619
907
  get NoticeDeadline() { return this.Record?.RenewalNoticeDeadline ?? null; }
620
908
  get InCancellationWindow() { return this.Record?.IsInCancellationWindow === true; }
621
909
  static ɵfac = /*@__PURE__*/ (() => { let ɵMJCContractRenewalPanel_BaseFactory; return function MJCContractRenewalPanel_Factory(__ngFactoryType__) { return (ɵMJCContractRenewalPanel_BaseFactory || (ɵMJCContractRenewalPanel_BaseFactory = i0.ɵɵgetInheritedFactory(MJCContractRenewalPanel)))(__ngFactoryType__ || MJCContractRenewalPanel); }; })();
622
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJCContractRenewalPanel, selectors: [["mjc-contract-renewal-panel"]], features: [i0.ɵɵInheritDefinitionFeature], decls: 31, vars: 10, consts: [["SectionKey", "renewal", "SectionName", "Renewal terms", "Icon", "fa-solid fa-calendar-days", 3, "Form", "FormContext"], [1, "mjc-body"], [1, "mjc-chip", "mjc-chip--muted"], [1, "mjc-fields"], [1, "mjc-field"], ["aria-label", "Does this agreement auto-renew?", 3, "ngModel"], [1, "mjc-val"], [1, "mjc-hint"], ["type", "number", "min", "0", "aria-label", "Renewal notice days", 2, "width", "100%", 3, "ngModel"], ["type", "number", "min", "0", "aria-label", "Cancellation window days", 2, "width", "100%", 3, "ngModel"], ["type", "number", "min", "0", "step", "0.01", "aria-label", "Annual increase percent", 2, "width", "100%", 3, "ngModel"], [1, "mjc-note"], [1, "mjc-empty"], ["aria-label", "Does this agreement auto-renew?", 3, "ngModelChange", "ngModel"], [3, "ngValue"], ["type", "number", "min", "0", "aria-label", "Renewal notice days", 2, "width", "100%", 3, "ngModelChange", "ngModel"], ["type", "number", "min", "0", "aria-label", "Cancellation window days", 2, "width", "100%", 3, "ngModelChange", "ngModel"], ["type", "number", "min", "0", "step", "0.01", "aria-label", "Annual increase percent", 2, "width", "100%", 3, "ngModelChange", "ngModel"]], template: function MJCContractRenewalPanel_Template(rf, ctx) { if (rf & 1) {
910
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJCContractRenewalPanel, selectors: [["mjc-contract-renewal-panel"]], features: [i0.ɵɵInheritDefinitionFeature], decls: 32, vars: 9, consts: [["SectionKey", "renewal", "SectionName", "Renewal terms", "Icon", "fa-solid fa-calendar-days", 3, "Form", "FormContext"], [1, "mjc-body"], [1, "mjc-chip", "mjc-chip--muted"], [1, "mjc-fields"], [1, "mjc-field"], ["aria-label", "Does this agreement auto-renew?", 3, "ngModel"], [1, "mjc-val"], ["type", "number", "min", "0", "aria-label", "Renewal notice days", 3, "ngModel"], [1, "mjc-hint"], ["type", "number", "min", "0", "aria-label", "Cancellation window days", 3, "ngModel"], ["type", "number", "min", "0", "step", "0.01", "aria-label", "Annual increase percent", 3, "ngModel"], [1, "mjc-empty"], ["aria-label", "Does this agreement auto-renew?", 3, "ngModelChange", "ngModel"], [3, "ngValue"], ["type", "number", "min", "0", "aria-label", "Renewal notice days", 3, "ngModelChange", "ngModel"], ["type", "number", "min", "0", "aria-label", "Cancellation window days", 3, "ngModelChange", "ngModel"], ["type", "number", "min", "0", "step", "0.01", "aria-label", "Annual increase percent", 3, "ngModelChange", "ngModel"]], template: function MJCContractRenewalPanel_Template(rf, ctx) { if (rf & 1) {
623
911
  i0.ɵɵelementStart(0, "mj-collapsible-panel", 0)(1, "div", 1)(2, "span", 2);
624
912
  i0.ɵɵtext(3, "as stated in the agreement");
625
913
  i0.ɵɵelementEnd();
@@ -627,49 +915,49 @@ let MJCContractRenewalPanel = class MJCContractRenewalPanel extends BaseFormPane
627
915
  i0.ɵɵtext(7, "Auto-renews");
628
916
  i0.ɵɵelementEnd();
629
917
  i0.ɵɵconditionalCreate(8, MJCContractRenewalPanel_Conditional_8_Template, 5, 3, "select", 5)(9, MJCContractRenewalPanel_Conditional_9_Template, 2, 1, "div", 6);
630
- i0.ɵɵconditionalCreate(10, MJCContractRenewalPanel_Conditional_10_Template, 2, 0, "div", 7);
631
918
  i0.ɵɵelementEnd();
632
- i0.ɵɵelementStart(11, "div", 4)(12, "label");
633
- i0.ɵɵtext(13, "Renewal notice we owe (days)");
919
+ i0.ɵɵelementStart(10, "div", 4)(11, "label");
920
+ i0.ɵɵtext(12, "Renewal notice (days)");
634
921
  i0.ɵɵelementEnd();
635
- i0.ɵɵconditionalCreate(14, MJCContractRenewalPanel_Conditional_14_Template, 1, 1, "input", 8)(15, MJCContractRenewalPanel_Conditional_15_Template, 2, 1, "div", 6);
636
- i0.ɵɵconditionalCreate(16, MJCContractRenewalPanel_Conditional_16_Template, 3, 4, "div", 7);
922
+ i0.ɵɵconditionalCreate(13, MJCContractRenewalPanel_Conditional_13_Template, 1, 1, "input", 7)(14, MJCContractRenewalPanel_Conditional_14_Template, 2, 1, "div", 6);
923
+ i0.ɵɵelementStart(15, "div", 8);
924
+ i0.ɵɵtext(16, " Written notice we must give the customer before a renewal price change. ");
637
925
  i0.ɵɵelementEnd();
638
- i0.ɵɵelementStart(17, "div", 4)(18, "label");
639
- i0.ɵɵtext(19, "Notice to cancel (days)");
926
+ i0.ɵɵconditionalCreate(17, MJCContractRenewalPanel_Conditional_17_Template, 3, 4, "div", 8);
640
927
  i0.ɵɵelementEnd();
641
- i0.ɵɵconditionalCreate(20, MJCContractRenewalPanel_Conditional_20_Template, 1, 1, "input", 9)(21, MJCContractRenewalPanel_Conditional_21_Template, 2, 1, "div", 6);
642
- i0.ɵɵconditionalCreate(22, MJCContractRenewalPanel_Conditional_22_Template, 2, 0, "div", 7);
928
+ i0.ɵɵelementStart(18, "div", 4)(19, "label");
929
+ i0.ɵɵtext(20, "Notice to cancel (days)");
643
930
  i0.ɵɵelementEnd();
644
- i0.ɵɵelementStart(23, "div", 4)(24, "label");
645
- i0.ɵɵtext(25, "Annual increase (%)");
931
+ i0.ɵɵconditionalCreate(21, MJCContractRenewalPanel_Conditional_21_Template, 1, 1, "input", 9)(22, MJCContractRenewalPanel_Conditional_22_Template, 2, 1, "div", 6);
932
+ i0.ɵɵelementStart(23, "div", 8);
933
+ i0.ɵɵtext(24, "Notice the customer must give us to cancel.");
646
934
  i0.ɵɵelementEnd();
647
- i0.ɵɵconditionalCreate(26, MJCContractRenewalPanel_Conditional_26_Template, 1, 1, "input", 10)(27, MJCContractRenewalPanel_Conditional_27_Template, 2, 1, "div", 6);
648
- i0.ɵɵelementEnd()();
649
- i0.ɵɵelementStart(28, "p", 11);
650
- i0.ɵɵtext(29, " These record what the signed paper says. The subscription in orders holds the operational setting and may legitimately differ \u2014 a mismatch is a finding, not a bug. ");
935
+ i0.ɵɵconditionalCreate(25, MJCContractRenewalPanel_Conditional_25_Template, 2, 0, "div", 8);
651
936
  i0.ɵɵelementEnd();
652
- i0.ɵɵconditionalCreate(30, MJCContractRenewalPanel_Conditional_30_Template, 2, 0, "div", 12);
937
+ i0.ɵɵelementStart(26, "div", 4)(27, "label");
938
+ i0.ɵɵtext(28, "Annual increase (%)");
939
+ i0.ɵɵelementEnd();
940
+ i0.ɵɵconditionalCreate(29, MJCContractRenewalPanel_Conditional_29_Template, 1, 1, "input", 10)(30, MJCContractRenewalPanel_Conditional_30_Template, 2, 1, "div", 6);
941
+ i0.ɵɵelementEnd()();
942
+ i0.ɵɵconditionalCreate(31, MJCContractRenewalPanel_Conditional_31_Template, 2, 0, "div", 11);
653
943
  i0.ɵɵelementEnd()();
654
944
  } if (rf & 2) {
655
945
  i0.ɵɵproperty("Form", ctx.FormComponent)("FormContext", ctx.FormContext);
656
946
  i0.ɵɵadvance(8);
657
947
  i0.ɵɵconditional(ctx.EditMode ? 8 : 9);
658
- i0.ɵɵadvance(2);
659
- i0.ɵɵconditional(!ctx.Record.AutoRenew ? 10 : -1);
948
+ i0.ɵɵadvance(5);
949
+ i0.ɵɵconditional(ctx.EditMode ? 13 : 14);
660
950
  i0.ɵɵadvance(4);
661
- i0.ɵɵconditional(ctx.EditMode ? 14 : 15);
662
- i0.ɵɵadvance(2);
663
- i0.ɵɵconditional(ctx.NoticeDeadline ? 16 : -1);
951
+ i0.ɵɵconditional(ctx.NoticeDeadline ? 17 : -1);
664
952
  i0.ɵɵadvance(4);
665
- i0.ɵɵconditional(ctx.EditMode ? 20 : 21);
666
- i0.ɵɵadvance(2);
667
- i0.ɵɵconditional(ctx.InCancellationWindow ? 22 : -1);
953
+ i0.ɵɵconditional(ctx.EditMode ? 21 : 22);
668
954
  i0.ɵɵadvance(4);
669
- i0.ɵɵconditional(ctx.EditMode ? 26 : 27);
955
+ i0.ɵɵconditional(ctx.InCancellationWindow ? 25 : -1);
670
956
  i0.ɵɵadvance(4);
671
- i0.ɵɵconditional(!ctx.Record.RenewalNoticeDays && !ctx.Record.CancellationWindowDays && ctx.Record.AnnualIncreasePercent === null ? 30 : -1);
672
- } }, dependencies: [CommonModule, FormsModule, i2.NgSelectOption, i2.ɵNgSelectMultipleOption, i2.DefaultValueAccessor, i2.NumberValueAccessor, i2.SelectControlValueAccessor, i2.NgControlStatus, i2.MinValidator, i2.NgModel, BaseFormsModule, i3.MjCollapsiblePanelComponent, i1.DatePipe], encapsulation: 2 });
957
+ i0.ɵɵconditional(ctx.EditMode ? 29 : 30);
958
+ i0.ɵɵadvance(2);
959
+ i0.ɵɵconditional(ctx.NoTermsRecorded ? 31 : -1);
960
+ } }, dependencies: [CommonModule, FormsModule, i3.NgSelectOption, i3.ɵNgSelectMultipleOption, i3.DefaultValueAccessor, i3.NumberValueAccessor, i3.SelectControlValueAccessor, i3.NgControlStatus, i3.MinValidator, i3.NgModel, BaseFormsModule, i1.MjCollapsiblePanelComponent, i2.DatePipe], encapsulation: 2 });
673
961
  };
674
962
  MJCContractRenewalPanel = __decorate([
675
963
  RegisterClassEx(BaseFormPanel, {
@@ -739,18 +1027,24 @@ export { MJCContractRenewalPanel };
739
1027
  } @else {
740
1028
  <div class="mjc-val">{{ Record.AutoRenew ? 'Yes' : 'No' }}</div>
741
1029
  }
742
- @if (!Record.AutoRenew) {
743
- <div class="mjc-hint">someone must act for this to continue</div>
744
- }
745
1030
  </div>
746
1031
  <div class="mjc-field">
747
- <label>Renewal notice we owe (days)</label>
1032
+ <label>Renewal notice (days)</label>
748
1033
  @if (EditMode) {
749
- <input type="number" min="0" style="width:100%" [ngModel]="Record.RenewalNoticeDays"
1034
+ <input type="number" min="0" [ngModel]="Record.RenewalNoticeDays"
750
1035
  (ngModelChange)="Set('RenewalNoticeDays', $event)" aria-label="Renewal notice days" />
751
1036
  } @else {
752
- <div class="mjc-val">{{ Record.RenewalNoticeDays ? Record.RenewalNoticeDays + ' days' : '—' }}</div>
1037
+ <div class="mjc-val">{{ Days(Record.RenewalNoticeDays) }}</div>
753
1038
  }
1039
+ <!--
1040
+ WHICH DIRECTION THE NOTICE RUNS IS THE WHOLE POINT (#28 item 6). This field
1041
+ and CancellationWindowDays below are both "a number of days of notice",
1042
+ and the old labels left the reader to work out whose notice each one was.
1043
+ One is what we owe them; the other is what they owe us.
1044
+ -->
1045
+ <div class="mjc-hint">
1046
+ Written notice we must give the customer before a renewal price change.
1047
+ </div>
754
1048
  @if (NoticeDeadline) {
755
1049
  <div class="mjc-hint">deadline: {{ NoticeDeadline | date: 'd MMM y' }}</div>
756
1050
  }
@@ -758,41 +1052,35 @@ export { MJCContractRenewalPanel };
758
1052
  <div class="mjc-field">
759
1053
  <label>Notice to cancel (days)</label>
760
1054
  @if (EditMode) {
761
- <input type="number" min="0" style="width:100%" [ngModel]="Record.CancellationWindowDays"
1055
+ <input type="number" min="0" [ngModel]="Record.CancellationWindowDays"
762
1056
  (ngModelChange)="Set('CancellationWindowDays', $event)" aria-label="Cancellation window days" />
763
1057
  } @else {
764
- <div class="mjc-val">{{ Record.CancellationWindowDays ? Record.CancellationWindowDays + ' days' : '—' }}</div>
1058
+ <div class="mjc-val">{{ Days(Record.CancellationWindowDays) }}</div>
765
1059
  }
1060
+ <div class="mjc-hint">Notice the customer must give us to cancel.</div>
766
1061
  @if (InCancellationWindow) { <div class="mjc-hint">the window is open now</div> }
767
1062
  </div>
768
1063
  <div class="mjc-field">
769
1064
  <label>Annual increase (%)</label>
770
1065
  @if (EditMode) {
771
- <input type="number" min="0" step="0.01" style="width:100%" [ngModel]="Record.AnnualIncreasePercent"
1066
+ <input type="number" min="0" step="0.01" [ngModel]="Record.AnnualIncreasePercent"
772
1067
  (ngModelChange)="Set('AnnualIncreasePercent', $event)" aria-label="Annual increase percent" />
773
1068
  } @else {
774
- <div class="mjc-val">{{ Record.AnnualIncreasePercent !== null ? Record.AnnualIncreasePercent + '%' : '—' }}</div>
1069
+ <div class="mjc-val">{{ Percent(Record.AnnualIncreasePercent) }}</div>
775
1070
  }
776
1071
  </div>
777
1072
  </div>
778
1073
 
779
- <p class="mjc-note">
780
- These record what the signed paper says. The subscription in orders holds the operational
781
- setting and may legitimately differ — a mismatch is a finding, not a bug.
782
- </p>
783
1074
 
784
- @if (!Record.RenewalNoticeDays && !Record.CancellationWindowDays && Record.AnnualIncreasePercent === null) {
785
- <div class="mjc-empty">
786
- No renewal terms recorded. If the agreement states any, recording them is what puts this
787
- contract on the renewals watchlist.
788
- </div>
1075
+ @if (NoTermsRecorded) {
1076
+ <div class="mjc-empty">No renewal terms recorded.</div>
789
1077
  }
790
1078
  </div>
791
1079
  </mj-collapsible-panel>
792
1080
  `,
793
1081
  }]
794
1082
  }], null, null); })();
795
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJCContractRenewalPanel, { className: "MJCContractRenewalPanel", filePath: "lib/form-panels/contract.panels.ts", lineNumber: 318 }); })();
1083
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJCContractRenewalPanel, { className: "MJCContractRenewalPanel", filePath: "lib/form-panels/contract.panels.ts", lineNumber: 625 }); })();
796
1084
  /* ────────────────────────────────────────────────────────────────────────────
797
1085
  * 2b · Dates — its own rail section, replacing the generated `datesAndTerms`
798
1086
  * ──────────────────────────────────────────────────────────────────────────── */
@@ -819,63 +1107,41 @@ let MJCContractDatesPanel = class MJCContractDatesPanel extends BaseFormPanel {
819
1107
  SetDate(field, value) {
820
1108
  this.Record[field] = value ? new Date(value + 'T00:00:00Z') : null;
821
1109
  }
822
- get EndsInText() {
823
- const d = this.Record?.DaysToEnd ?? null;
824
- if (d === null)
825
- return '';
826
- if (d < 0)
827
- return `ended ${Math.abs(d)} days ago`;
828
- if (d === 0)
829
- return 'ends today';
830
- return d < 60 ? `in ${d} days` : `in ${Math.round(d / 30)} months`;
831
- }
832
1110
  static ɵfac = /*@__PURE__*/ (() => { let ɵMJCContractDatesPanel_BaseFactory; return function MJCContractDatesPanel_Factory(__ngFactoryType__) { return (ɵMJCContractDatesPanel_BaseFactory || (ɵMJCContractDatesPanel_BaseFactory = i0.ɵɵgetInheritedFactory(MJCContractDatesPanel)))(__ngFactoryType__ || MJCContractDatesPanel); }; })();
833
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJCContractDatesPanel, selectors: [["mjc-contract-dates-panel"]], features: [i0.ɵɵInheritDefinitionFeature], decls: 33, vars: 7, consts: [["SectionKey", "dates", "SectionName", "Dates", "Icon", "fa-solid fa-calendar", 3, "Form", "FormContext"], [1, "mjc-body"], [1, "mjc-fields"], [1, "mjc-field"], ["type", "date", "aria-label", "Executed date", 2, "width", "100%", 3, "ngModel"], [1, "mjc-val"], [1, "mjc-hint"], ["type", "date", "aria-label", "Effective date", 2, "width", "100%", 3, "ngModel"], ["type", "date", "aria-label", "End date", 2, "width", "100%", 3, "ngModel"], ["type", "date", "aria-label", "Terminated date", 2, "width", "100%", 3, "ngModel"], [1, "mjc-val", 3, "mjc-val--ro"], [1, "mjc-note"], ["type", "date", "aria-label", "Executed date", 2, "width", "100%", 3, "ngModelChange", "ngModel"], ["type", "date", "aria-label", "Effective date", 2, "width", "100%", 3, "ngModelChange", "ngModel"], ["type", "date", "aria-label", "End date", 2, "width", "100%", 3, "ngModelChange", "ngModel"], ["type", "date", "aria-label", "Terminated date", 2, "width", "100%", 3, "ngModelChange", "ngModel"]], template: function MJCContractDatesPanel_Template(rf, ctx) { if (rf & 1) {
1111
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJCContractDatesPanel, selectors: [["mjc-contract-dates-panel"]], features: [i0.ɵɵInheritDefinitionFeature], decls: 25, vars: 6, consts: [["SectionKey", "dates", "SectionName", "Dates", "Icon", "fa-solid fa-calendar", 3, "Form", "FormContext"], [1, "mjc-body"], [1, "mjc-fields"], [1, "mjc-field"], ["type", "date", "aria-label", "Executed date", 3, "ngModel"], [1, "mjc-val"], ["type", "date", "aria-label", "Effective date", 3, "ngModel"], ["type", "date", "aria-label", "End date", 3, "ngModel"], ["type", "date", "aria-label", "Terminated date", 3, "ngModel"], [1, "mjc-val", 3, "mjc-val--ro"], [1, "mjc-hint"], ["type", "date", "aria-label", "Executed date", 3, "ngModelChange", "ngModel"], ["type", "date", "aria-label", "Effective date", 3, "ngModelChange", "ngModel"], ["type", "date", "aria-label", "End date", 3, "ngModelChange", "ngModel"], ["type", "date", "aria-label", "Terminated date", 3, "ngModelChange", "ngModel"]], template: function MJCContractDatesPanel_Template(rf, ctx) { if (rf & 1) {
834
1112
  i0.ɵɵelementStart(0, "mj-collapsible-panel", 0)(1, "div", 1)(2, "div", 2)(3, "div", 3)(4, "label");
835
1113
  i0.ɵɵtext(5, "Executed date");
836
1114
  i0.ɵɵelementEnd();
837
1115
  i0.ɵɵconditionalCreate(6, MJCContractDatesPanel_Conditional_6_Template, 1, 1, "input", 4)(7, MJCContractDatesPanel_Conditional_7_Template, 3, 4, "div", 5);
838
- i0.ɵɵelementStart(8, "div", 6);
839
- i0.ɵɵtext(9, "may precede the effective date \u2014 that is normal, not an anomaly");
840
- i0.ɵɵelementEnd()();
841
- i0.ɵɵelementStart(10, "div", 3)(11, "label");
842
- i0.ɵɵtext(12, "Effective date");
843
1116
  i0.ɵɵelementEnd();
844
- i0.ɵɵconditionalCreate(13, MJCContractDatesPanel_Conditional_13_Template, 1, 1, "input", 7)(14, MJCContractDatesPanel_Conditional_14_Template, 3, 4, "div", 5);
1117
+ i0.ɵɵelementStart(8, "div", 3)(9, "label");
1118
+ i0.ɵɵtext(10, "Effective date");
845
1119
  i0.ɵɵelementEnd();
846
- i0.ɵɵelementStart(15, "div", 3)(16, "label");
847
- i0.ɵɵtext(17, "End date");
1120
+ i0.ɵɵconditionalCreate(11, MJCContractDatesPanel_Conditional_11_Template, 1, 1, "input", 6)(12, MJCContractDatesPanel_Conditional_12_Template, 3, 4, "div", 5);
848
1121
  i0.ɵɵelementEnd();
849
- i0.ɵɵconditionalCreate(18, MJCContractDatesPanel_Conditional_18_Template, 1, 1, "input", 8)(19, MJCContractDatesPanel_Conditional_19_Template, 3, 4, "div", 5);
850
- i0.ɵɵconditionalCreate(20, MJCContractDatesPanel_Conditional_20_Template, 2, 1, "div", 6);
1122
+ i0.ɵɵelementStart(13, "div", 3)(14, "label");
1123
+ i0.ɵɵtext(15, "End date");
851
1124
  i0.ɵɵelementEnd();
852
- i0.ɵɵelementStart(21, "div", 3)(22, "label");
853
- i0.ɵɵtext(23, "Terminated date");
1125
+ i0.ɵɵconditionalCreate(16, MJCContractDatesPanel_Conditional_16_Template, 1, 1, "input", 7)(17, MJCContractDatesPanel_Conditional_17_Template, 3, 4, "div", 5);
854
1126
  i0.ɵɵelementEnd();
855
- i0.ɵɵconditionalCreate(24, MJCContractDatesPanel_Conditional_24_Template, 1, 1, "input", 9)(25, MJCContractDatesPanel_Conditional_25_Template, 3, 6, "div", 10);
856
- i0.ɵɵelementStart(26, "div", 6);
857
- i0.ɵɵtext(27, " a fact about what happened \u2014 set it and the contract reads Terminated regardless of its term ");
858
- i0.ɵɵelementEnd()()();
859
- i0.ɵɵelementStart(28, "p", 11);
860
- i0.ɵɵtext(29, " The lifecycle is ");
861
- i0.ɵɵelementStart(30, "strong");
862
- i0.ɵɵtext(31, "derived");
1127
+ i0.ɵɵelementStart(18, "div", 3)(19, "label");
1128
+ i0.ɵɵtext(20, "Terminated date");
863
1129
  i0.ɵɵelementEnd();
864
- i0.ɵɵtext(32, " from these four dates and the two lineage links, not stored \u2014 so a state can never disagree with the dates it came from. ");
865
- i0.ɵɵelementEnd()()();
1130
+ i0.ɵɵconditionalCreate(21, MJCContractDatesPanel_Conditional_21_Template, 1, 1, "input", 8)(22, MJCContractDatesPanel_Conditional_22_Template, 3, 6, "div", 9);
1131
+ i0.ɵɵelementStart(23, "div", 10);
1132
+ i0.ɵɵtext(24, "Setting this marks the contract Terminated from this date.");
1133
+ i0.ɵɵelementEnd()()()()();
866
1134
  } if (rf & 2) {
867
1135
  i0.ɵɵproperty("Form", ctx.FormComponent)("FormContext", ctx.FormContext);
868
1136
  i0.ɵɵadvance(6);
869
1137
  i0.ɵɵconditional(ctx.EditMode ? 6 : 7);
870
- i0.ɵɵadvance(7);
871
- i0.ɵɵconditional(ctx.EditMode ? 13 : 14);
872
1138
  i0.ɵɵadvance(5);
873
- i0.ɵɵconditional(ctx.EditMode ? 18 : 19);
874
- i0.ɵɵadvance(2);
875
- i0.ɵɵconditional(ctx.Record.DaysToEnd !== null ? 20 : -1);
876
- i0.ɵɵadvance(4);
877
- i0.ɵɵconditional(ctx.EditMode ? 24 : 25);
878
- } }, dependencies: [CommonModule, FormsModule, i2.DefaultValueAccessor, i2.NgControlStatus, i2.NgModel, BaseFormsModule, i3.MjCollapsiblePanelComponent, i1.DatePipe], encapsulation: 2 });
1139
+ i0.ɵɵconditional(ctx.EditMode ? 11 : 12);
1140
+ i0.ɵɵadvance(5);
1141
+ i0.ɵɵconditional(ctx.EditMode ? 16 : 17);
1142
+ i0.ɵɵadvance(5);
1143
+ i0.ɵɵconditional(ctx.EditMode ? 21 : 22);
1144
+ } }, dependencies: [CommonModule, FormsModule, i3.DefaultValueAccessor, i3.NgControlStatus, i3.NgModel, BaseFormsModule, i1.MjCollapsiblePanelComponent, i2.DatePipe], encapsulation: 2 });
879
1145
  };
880
1146
  MJCContractDatesPanel = __decorate([
881
1147
  RegisterClassEx(BaseFormPanel, {
@@ -927,17 +1193,16 @@ export { MJCContractDatesPanel };
927
1193
  <div class="mjc-field">
928
1194
  <label>Executed date</label>
929
1195
  @if (EditMode) {
930
- <input type="date" style="width:100%" [ngModel]="AsInput(Record.ExecutedDate)"
1196
+ <input type="date" [ngModel]="AsInput(Record.ExecutedDate)"
931
1197
  (ngModelChange)="SetDate('ExecutedDate', $event)" aria-label="Executed date" />
932
1198
  } @else {
933
1199
  <div class="mjc-val">{{ (Record.ExecutedDate | date: 'd MMM y') || '—' }}</div>
934
1200
  }
935
- <div class="mjc-hint">may precede the effective date — that is normal, not an anomaly</div>
936
1201
  </div>
937
1202
  <div class="mjc-field">
938
1203
  <label>Effective date</label>
939
1204
  @if (EditMode) {
940
- <input type="date" style="width:100%" [ngModel]="AsInput(Record.EffectiveDate)"
1205
+ <input type="date" [ngModel]="AsInput(Record.EffectiveDate)"
941
1206
  (ngModelChange)="SetDate('EffectiveDate', $event)" aria-label="Effective date" />
942
1207
  } @else {
943
1208
  <div class="mjc-val">{{ (Record.EffectiveDate | date: 'd MMM y') || '—' }}</div>
@@ -946,43 +1211,35 @@ export { MJCContractDatesPanel };
946
1211
  <div class="mjc-field">
947
1212
  <label>End date</label>
948
1213
  @if (EditMode) {
949
- <input type="date" style="width:100%" [ngModel]="AsInput(Record.EndDate)"
1214
+ <input type="date" [ngModel]="AsInput(Record.EndDate)"
950
1215
  (ngModelChange)="SetDate('EndDate', $event)" aria-label="End date" />
951
1216
  } @else {
952
1217
  <div class="mjc-val">{{ (Record.EndDate | date: 'd MMM y') || '—' }}</div>
953
1218
  }
954
- @if (Record.DaysToEnd !== null) { <div class="mjc-hint">{{ EndsInText }}</div> }
955
1219
  </div>
956
1220
  <div class="mjc-field">
957
1221
  <label>Terminated date</label>
958
1222
  @if (EditMode) {
959
- <input type="date" style="width:100%" [ngModel]="AsInput(Record.TerminatedDate)"
1223
+ <input type="date" [ngModel]="AsInput(Record.TerminatedDate)"
960
1224
  (ngModelChange)="SetDate('TerminatedDate', $event)" aria-label="Terminated date" />
961
1225
  } @else {
962
1226
  <div class="mjc-val" [class.mjc-val--ro]="!Record.TerminatedDate">{{ (Record.TerminatedDate | date: 'd MMM y') || '—' }}</div>
963
1227
  }
964
- <div class="mjc-hint">
965
- a fact about what happened — set it and the contract reads Terminated regardless of its term
966
- </div>
1228
+ <div class="mjc-hint">Setting this marks the contract Terminated from this date.</div>
967
1229
  </div>
968
1230
  </div>
969
1231
 
970
- <p class="mjc-note">
971
- The lifecycle is <strong>derived</strong> from these four dates and the two lineage links, not
972
- stored — so a state can never disagree with the dates it came from.
973
- </p>
974
1232
  </div>
975
1233
  </mj-collapsible-panel>
976
1234
  `,
977
1235
  }]
978
1236
  }], null, null); })();
979
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJCContractDatesPanel, { className: "MJCContractDatesPanel", filePath: "lib/form-panels/contract.panels.ts", lineNumber: 442 }); })();
1237
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJCContractDatesPanel, { className: "MJCContractDatesPanel", filePath: "lib/form-panels/contract.panels.ts", lineNumber: 773 }); })();
980
1238
  /* ────────────────────────────────────────────────────────────────────────────
981
1239
  * 4 · Lineage — change orders and supersession, read-only
982
1240
  * ──────────────────────────────────────────────────────────────────────────── */
983
1241
  let MJCContractLineagePanel = class MJCContractLineagePanel extends BaseFormPanel {
984
1242
  cdr = inject(ChangeDetectorRef);
985
- navigation = inject(NavigationService);
986
1243
  /**
987
1244
  * The whole tree, declared rather than fetched.
988
1245
  *
@@ -1022,7 +1279,7 @@ let MJCContractLineagePanel = class MJCContractLineagePanel extends BaseFormPane
1022
1279
  const key = e?.Node?.PrimaryKey;
1023
1280
  if (!key)
1024
1281
  return;
1025
- this.navigation.OpenEntityRecord(MJC_ENTITIES.Contract, key);
1282
+ this.FormComponent.OnFormNavigate({ Kind: 'record', EntityName: MJC_ENTITIES.Contract, PrimaryKey: key });
1026
1283
  }
1027
1284
  get ParentName() { return this.Record?.ParentContract ?? ''; }
1028
1285
  get Count() {
@@ -1071,7 +1328,7 @@ let MJCContractLineagePanel = class MJCContractLineagePanel extends BaseFormPane
1071
1328
  return ScopedRunView(this.FormComponent?.ProviderToUse);
1072
1329
  }
1073
1330
  static ɵfac = /*@__PURE__*/ (() => { let ɵMJCContractLineagePanel_BaseFactory; return function MJCContractLineagePanel_Factory(__ngFactoryType__) { return (ɵMJCContractLineagePanel_BaseFactory || (ɵMJCContractLineagePanel_BaseFactory = i0.ɵɵgetInheritedFactory(MJCContractLineagePanel)))(__ngFactoryType__ || MJCContractLineagePanel); }; })();
1074
- static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJCContractLineagePanel, selectors: [["mjc-contract-lineage-panel"]], features: [i0.ɵɵInheritDefinitionFeature], decls: 5, vars: 9, consts: [["SectionKey", "contractLineage", "SectionName", "Lineage", "Icon", "fa-solid fa-sitemap", "Variant", "related-entity", 3, "BadgeCount", "Form", "FormContext"], [1, "mjc-body"], [3, "NodeDoubleClick", "Config", "ActiveRecordID"], [1, "mjc-empty"], [1, "mjc-note"]], template: function MJCContractLineagePanel_Template(rf, ctx) { if (rf & 1) {
1331
+ static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: MJCContractLineagePanel, selectors: [["mjc-contract-lineage-panel"]], features: [i0.ɵɵInheritDefinitionFeature], decls: 5, vars: 9, consts: [["SectionKey", "lineage", "SectionName", "Lineage", "Icon", "fa-solid fa-sitemap", "Variant", "related-entity", 3, "BadgeCount", "Form", "FormContext"], [1, "mjc-body"], [3, "NodeDoubleClick", "Config", "ActiveRecordID"], [1, "mjc-empty"], [1, "mjc-note"]], template: function MJCContractLineagePanel_Template(rf, ctx) { if (rf & 1) {
1075
1332
  i0.ɵɵelementStart(0, "mj-collapsible-panel", 0)(1, "div", 1);
1076
1333
  i0.ɵɵconditionalCreate(2, MJCContractLineagePanel_Conditional_2_Template, 2, 1, "div", 1);
1077
1334
  i0.ɵɵelementStart(3, "mj-hierarchy-tree", 2);
@@ -1089,7 +1346,7 @@ let MJCContractLineagePanel = class MJCContractLineagePanel extends BaseFormPane
1089
1346
  i0.ɵɵproperty("Config", ctx.TreeConfig)("ActiveRecordID", (ctx.Record == null ? null : ctx.Record.ID) ?? undefined);
1090
1347
  i0.ɵɵadvance();
1091
1348
  i0.ɵɵconditional(!ctx.ParentName && !ctx.HasChildren ? 4 : -1);
1092
- } }, dependencies: [CommonModule, FormsModule, BaseFormsModule, i3.MjCollapsiblePanelComponent, HierarchyTreeComponent], styles: ["\n /* A PINNED FLOOR, not a percentage. height:100% cannot work here: it resolves against\n .mj-forms-panel-content, which the variant sets to height:auto, and 100% of auto collapses\n straight back to auto - measured blank. AG Grid escapes this because the grid supplies its\n own intrinsic height; a plain div does not. A px min-height resolves regardless of what the\n parent computes to, and stays under the variant's own 800px cap. */\n /* DIRECT child only. As a descendant selector this also matched the nested .mjc-body wrapper\n inside the panel, so the floor applied twice and left a tall empty block above the tree. */\n mjc-contract-lineage-panel > .mjc-body {\n min-height: 440px;\n overflow-y: auto;\n }\n mjc-contract-lineage-panel mj-hierarchy-tree { display: block; min-height: 380px; }\n "], encapsulation: 2 });
1349
+ } }, dependencies: [CommonModule, FormsModule, BaseFormsModule, i1.MjCollapsiblePanelComponent, HierarchyTreeComponent], styles: ["\n /* A PINNED FLOOR, not a percentage. height:100% cannot work here: it resolves against\n .mj-forms-panel-content, which the variant sets to height:auto, and 100% of auto collapses\n straight back to auto - measured blank. AG Grid escapes this because the grid supplies its\n own intrinsic height; a plain div does not. A px min-height resolves regardless of what the\n parent computes to, and stays under the variant's own 800px cap. */\n /* DIRECT child only. As a descendant selector this also matched the nested .mjc-body wrapper\n inside the panel, so the floor applied twice and left a tall empty block above the tree. */\n mjc-contract-lineage-panel > .mjc-body {\n min-height: 440px;\n overflow-y: auto;\n }\n mjc-contract-lineage-panel mj-hierarchy-tree { display: block; min-height: 380px; }\n "], encapsulation: 2 });
1093
1350
  };
1094
1351
  MJCContractLineagePanel = __decorate([
1095
1352
  RegisterClassEx(BaseFormPanel, {
@@ -1126,7 +1383,7 @@ export { MJCContractLineagePanel };
1126
1383
  type: Component,
1127
1384
  args: [{ selector: 'mjc-contract-lineage-panel', standalone: true, encapsulation: ViewEncapsulation.None, imports: [CommonModule, FormsModule, BaseFormsModule, HierarchyTreeComponent], template: `
1128
1385
  <mj-collapsible-panel
1129
- SectionKey="contractLineage"
1386
+ SectionKey="lineage"
1130
1387
  SectionName="Lineage"
1131
1388
  Icon="fa-solid fa-sitemap"
1132
1389
  Variant="related-entity"
@@ -1156,12 +1413,12 @@ export { MJCContractLineagePanel };
1156
1413
 
1157
1414
  @if (!ParentName && !HasChildren) {
1158
1415
  <div class="mjc-empty">
1159
- A standalone agreement — nothing above it, no change orders, and nothing has replaced it.
1416
+ No parent contract, change orders, or superseding contracts.
1160
1417
  </div>
1161
1418
  }
1162
1419
  </div>
1163
1420
  </mj-collapsible-panel>
1164
1421
  `, styles: ["\n /* A PINNED FLOOR, not a percentage. height:100% cannot work here: it resolves against\n .mj-forms-panel-content, which the variant sets to height:auto, and 100% of auto collapses\n straight back to auto - measured blank. AG Grid escapes this because the grid supplies its\n own intrinsic height; a plain div does not. A px min-height resolves regardless of what the\n parent computes to, and stays under the variant's own 800px cap. */\n /* DIRECT child only. As a descendant selector this also matched the nested .mjc-body wrapper\n inside the panel, so the floor applied twice and left a tall empty block above the tree. */\n mjc-contract-lineage-panel > .mjc-body {\n min-height: 440px;\n overflow-y: auto;\n }\n mjc-contract-lineage-panel mj-hierarchy-tree { display: block; min-height: 380px; }\n "] }]
1165
1422
  }], null, null); })();
1166
- (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJCContractLineagePanel, { className: "MJCContractLineagePanel", filePath: "lib/form-panels/contract.panels.ts", lineNumber: 572 }); })();
1423
+ (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MJCContractLineagePanel, { className: "MJCContractLineagePanel", filePath: "lib/form-panels/contract.panels.ts", lineNumber: 898 }); })();
1167
1424
  //# sourceMappingURL=contract.panels.js.map