@memberjunction/ng-dashboards 5.46.0 → 5.48.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 (71) hide show
  1. package/dist/DataExplorer/data-explorer-dashboard.component.d.ts +15 -4
  2. package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
  3. package/dist/DataExplorer/data-explorer-dashboard.component.js +45 -35
  4. package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
  5. package/dist/DataExplorer/models/explorer-state.interface.d.ts +7 -0
  6. package/dist/DataExplorer/models/explorer-state.interface.d.ts.map +1 -1
  7. package/dist/DataExplorer/models/explorer-state.interface.js.map +1 -1
  8. package/dist/Lists/components/lists-browse-resource.component.d.ts +4 -0
  9. package/dist/Lists/components/lists-browse-resource.component.d.ts.map +1 -1
  10. package/dist/Lists/components/lists-browse-resource.component.js +39 -30
  11. package/dist/Lists/components/lists-browse-resource.component.js.map +1 -1
  12. package/dist/Lists/components/lists-my-lists-resource.component.d.ts.map +1 -1
  13. package/dist/Lists/components/lists-my-lists-resource.component.js +14 -12
  14. package/dist/Lists/components/lists-my-lists-resource.component.js.map +1 -1
  15. package/dist/Lists/components/lists-operations-resource.component.d.ts.map +1 -1
  16. package/dist/Lists/components/lists-operations-resource.component.js +6 -1
  17. package/dist/Lists/components/lists-operations-resource.component.js.map +1 -1
  18. package/dist/Lists/services/list-set-operations.service.d.ts +11 -0
  19. package/dist/Lists/services/list-set-operations.service.d.ts.map +1 -1
  20. package/dist/Lists/services/list-set-operations.service.js +27 -2
  21. package/dist/Lists/services/list-set-operations.service.js.map +1 -1
  22. package/dist/PredictiveStudio/at-risk.view-models.d.ts +33 -0
  23. package/dist/PredictiveStudio/at-risk.view-models.d.ts.map +1 -1
  24. package/dist/PredictiveStudio/at-risk.view-models.js +69 -2
  25. package/dist/PredictiveStudio/at-risk.view-models.js.map +1 -1
  26. package/dist/PredictiveStudio/components/ps-catalog.component.js +2 -2
  27. package/dist/PredictiveStudio/components/ps-catalog.component.js.map +1 -1
  28. package/dist/PredictiveStudio/components/ps-compare.component.js +2 -2
  29. package/dist/PredictiveStudio/components/ps-compare.component.js.map +1 -1
  30. package/dist/PredictiveStudio/components/ps-confirm-modal.component.js +2 -2
  31. package/dist/PredictiveStudio/components/ps-confirm-modal.component.js.map +1 -1
  32. package/dist/PredictiveStudio/components/ps-experiments.component.js +2 -2
  33. package/dist/PredictiveStudio/components/ps-experiments.component.js.map +1 -1
  34. package/dist/PredictiveStudio/components/ps-home.component.js +2 -2
  35. package/dist/PredictiveStudio/components/ps-home.component.js.map +1 -1
  36. package/dist/PredictiveStudio/components/ps-operate-dialog.component.js +2 -2
  37. package/dist/PredictiveStudio/components/ps-operate-dialog.component.js.map +1 -1
  38. package/dist/PredictiveStudio/components/ps-pipelines.component.d.ts.map +1 -1
  39. package/dist/PredictiveStudio/components/ps-pipelines.component.js +306 -297
  40. package/dist/PredictiveStudio/components/ps-pipelines.component.js.map +1 -1
  41. package/dist/PredictiveStudio/components/ps-production.component.js +2 -2
  42. package/dist/PredictiveStudio/components/ps-production.component.js.map +1 -1
  43. package/dist/PredictiveStudio/components/ps-registry.component.d.ts +1 -1
  44. package/dist/PredictiveStudio/components/ps-registry.component.d.ts.map +1 -1
  45. package/dist/PredictiveStudio/components/ps-registry.component.js +15 -9
  46. package/dist/PredictiveStudio/components/ps-registry.component.js.map +1 -1
  47. package/dist/PredictiveStudio/predictive-studio-agent-context.d.ts +115 -0
  48. package/dist/PredictiveStudio/predictive-studio-agent-context.d.ts.map +1 -0
  49. package/dist/PredictiveStudio/predictive-studio-agent-context.js +119 -0
  50. package/dist/PredictiveStudio/predictive-studio-agent-context.js.map +1 -0
  51. package/dist/PredictiveStudio/predictive-studio-copilot.view-models.d.ts +36 -0
  52. package/dist/PredictiveStudio/predictive-studio-copilot.view-models.d.ts.map +1 -0
  53. package/dist/PredictiveStudio/predictive-studio-copilot.view-models.js +52 -0
  54. package/dist/PredictiveStudio/predictive-studio-copilot.view-models.js.map +1 -0
  55. package/dist/PredictiveStudio/resources/ps-models-resource.component.d.ts +9 -1
  56. package/dist/PredictiveStudio/resources/ps-models-resource.component.d.ts.map +1 -1
  57. package/dist/PredictiveStudio/resources/ps-models-resource.component.js +133 -56
  58. package/dist/PredictiveStudio/resources/ps-models-resource.component.js.map +1 -1
  59. package/dist/PredictiveStudio/resources/ps-predictions-resource.component.d.ts +50 -1
  60. package/dist/PredictiveStudio/resources/ps-predictions-resource.component.d.ts.map +1 -1
  61. package/dist/PredictiveStudio/resources/ps-predictions-resource.component.js +591 -165
  62. package/dist/PredictiveStudio/resources/ps-predictions-resource.component.js.map +1 -1
  63. package/dist/PredictiveStudio/resources/ps-resource-base.d.ts +17 -0
  64. package/dist/PredictiveStudio/resources/ps-resource-base.d.ts.map +1 -1
  65. package/dist/PredictiveStudio/resources/ps-resource-base.js +27 -1
  66. package/dist/PredictiveStudio/resources/ps-resource-base.js.map +1 -1
  67. package/dist/PredictiveStudio/resources/ps-studio-resource.component.d.ts +11 -1
  68. package/dist/PredictiveStudio/resources/ps-studio-resource.component.d.ts.map +1 -1
  69. package/dist/PredictiveStudio/resources/ps-studio-resource.component.js +199 -99
  70. package/dist/PredictiveStudio/resources/ps-studio-resource.component.js.map +1 -1
  71. package/package.json +59 -59
@@ -117,7 +117,7 @@ export class PSConfirmModalComponent {
117
117
  i0.ɵɵtextInterpolate1(" ", ctx.confirmLabel, " ");
118
118
  i0.ɵɵadvance(2);
119
119
  i0.ɵɵproperty("disabled", ctx.busy);
120
- } }, dependencies: [CommonModule, FormsModule, i1.DefaultValueAccessor, i1.NgControlStatus, i1.NgModel, MJButtonDirective], styles: ["/* ============================================================================\n Predictive Studio \u2014 shared panel styles (design-token compliant)\n Maps the mockup design system onto MemberJunction semantic --mj-* tokens.\n Loaded by every PS panel component (styleUrls) under ViewEncapsulation.None,\n so the class names are global within the PS dashboard subtree.\n No hardcoded colors except the intentional categorical node/algorithm palette\n (declared once as --ps-* custom props below) which must stay distinct across\n light/dark to remain readable as diagram accents.\n ============================================================================ */\n\n/* Categorical accents for DAG node types + algorithm chips. These are\n data-visualization colors that must remain distinct regardless of theme\n (allowed exception #3 in the design-token rules). */\n.ps-panel {\n --ps-node-source: #6b7280;\n --ps-node-feature: #2050c8;\n --ps-node-embedding: #7b3ff0;\n --ps-node-target: #1f9d57;\n --ps-node-algorithm: #d98213;\n /* budget-gauge categorical accents (reuse the node palette so light/dark stay distinct) */\n --ps-gauge-cost: var(--ps-node-algorithm);\n --ps-gauge-iterations: var(--ps-node-feature);\n}\n\n.ps-panel {\n display: block;\n height: 100%;\n}\n\n/* ---- generic layout helpers ---- */\n.ps-row { display: flex; gap: 16px; }\n.ps-col { display: flex; flex-direction: column; gap: 16px; }\n.ps-wrap { flex-wrap: wrap; }\n.ps-spacer { flex: 1; }\n.ps-muted { color: var(--mj-text-muted); }\n.ps-small { font-size: 12.5px; }\n.ps-mono { font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; font-size: 12.5px; }\n.ps-section-title {\n font-size: 12px; text-transform: uppercase; letter-spacing: .06em;\n color: var(--mj-text-muted); margin: 4px 0 2px; font-weight: 700;\n}\n\n/* ---- cards ---- */\n.ps-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n box-shadow: var(--mj-shadow-sm);\n overflow: hidden;\n}\n.ps-card-head {\n display: flex; align-items: center; gap: 10px;\n padding: 14px 16px; border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-card-head h3 { font-size: 14.5px; margin: 0; font-weight: 650; color: var(--mj-text-primary); }\n.ps-card-body { padding: 16px; }\n\n/* ---- KPI tiles ---- */\n.ps-kpi {\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-radius: 10px; padding: 16px 18px; box-shadow: var(--mj-shadow-sm);\n}\n.ps-kpi .label { font-size: 12px; color: var(--mj-text-muted); font-weight: 600; }\n.ps-kpi .val { font-size: 26px; font-weight: 720; letter-spacing: -.02em; margin-top: 2px; color: var(--mj-text-primary); }\n\n/* ---- badges ---- */\n.ps-badge {\n display: inline-flex; align-items: center; gap: 5px;\n padding: 2px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 650; line-height: 1.7;\n}\n.ps-badge.gray { background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n.ps-badge.blue { background: var(--mj-status-info-bg); color: var(--mj-status-info-text); }\n.ps-badge.green { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n.ps-badge.amber { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-badge.red { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }\n\n/* recommendation-level chips (the ranking matrix payoff) */\n.ps-lvl {\n display: inline-flex; align-items: center; justify-content: center;\n min-width: 92px; padding: 4px 10px; border-radius: 7px;\n font-weight: 650; font-size: 11.5px; color: var(--mj-text-inverse);\n}\n.ps-lvl-Primary { background: var(--mj-status-success); }\n.ps-lvl-Strong { background: var(--mj-status-info); }\n.ps-lvl-Viable { background: var(--mj-status-warning); }\n.ps-lvl-Weak { background: var(--mj-text-disabled); }\n.ps-lvl-NotRecommended { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n\n/* ---- tags ---- */\n.ps-tag {\n display: inline-block; padding: 2px 8px; border-radius: 6px;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary);\n font-size: 11.5px; font-weight: 600;\n}\n.ps-tag.muted { opacity: .6; }\n.ps-divider { height: 1px; background: var(--mj-border-subtle); margin: 8px 0; }\n\n/* ---- tables ---- */\n.ps-table { width: 100%; border-collapse: collapse; font-size: 13px; }\n.ps-table th {\n text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; padding: 9px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n.ps-table td { padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); color: var(--mj-text-primary); }\n.ps-table tr:hover td { background: var(--mj-bg-surface-hover); }\n.ps-table .num { text-align: right; font-variant-numeric: tabular-nums; }\n\n.ps-rank {\n width: 22px; height: 22px; border-radius: 6px; background: var(--mj-bg-surface-sunken);\n display: inline-grid; place-items: center; font-weight: 700; font-size: 11.5px; color: var(--mj-text-secondary);\n}\n.ps-rank.gold { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n\n/* ---- progress bars ---- */\n.ps-bar { height: 8px; border-radius: 999px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-bar > span { display: block; height: 100%; border-radius: 999px; background: var(--mj-brand-primary); }\n.ps-bar.warn > span { background: var(--mj-status-warning); }\n.ps-bar.run > span { background: var(--mj-brand-primary); }\n\n.ps-fbar { display: grid; grid-template-columns: 150px 1fr 48px; align-items: center; gap: 10px; padding: 5px 0; }\n.ps-fbar .track { height: 14px; border-radius: 5px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-fbar .track > span { display: block; height: 100%; background: var(--mj-brand-primary); }\n.ps-fbar .track > span.warn { background: var(--mj-status-warning); }\n.ps-fbar .name { color: var(--mj-text-secondary); }\n\n/* gauge */\n.ps-gauge {\n position: relative; width: 64px; height: 64px; border-radius: 50%; flex: 0 0 auto;\n background: conic-gradient(var(--ps-gauge-color, var(--mj-brand-primary)) calc(var(--ps-p, 40) * 1%), var(--mj-bg-surface-sunken) 0);\n display: grid; place-items: center;\n}\n.ps-gauge::after { content: ''; position: absolute; inset: 9px; background: var(--mj-bg-surface); border-radius: 50%; }\n.ps-gauge .g-val { position: relative; font-weight: 720; font-size: 14px; color: var(--mj-text-primary); }\n\n/* warning / info callouts */\n.ps-callout { display: flex; gap: 11px; padding: 12px 14px; border-radius: 10px; border: 1px solid; }\n.ps-callout.warn { background: var(--mj-status-warning-bg); border-color: var(--mj-status-warning-border); color: var(--mj-status-warning-text); }\n.ps-callout.info { background: var(--mj-status-info-bg); border-color: var(--mj-status-info-border); color: var(--mj-status-info-text); }\n.ps-callout.success { background: var(--mj-status-success-bg); border-color: var(--mj-status-success-border); color: var(--mj-status-success-text); }\n.ps-callout i { font-size: 16px; margin-top: 1px; }\n\n/* lifecycle stepper */\n.ps-stepper { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }\n.ps-step { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--mj-text-muted); }\n.ps-step .pip {\n width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--mj-border-strong);\n display: grid; place-items: center; font-size: 11px;\n}\n.ps-step.done { color: var(--mj-status-success-text); }\n.ps-step.done .pip { background: var(--mj-status-success); border-color: var(--mj-status-success); color: var(--mj-text-inverse); }\n.ps-step.curr { color: var(--mj-brand-primary); }\n.ps-step.curr .pip { border-color: var(--mj-brand-primary); color: var(--mj-brand-primary); }\n.ps-step .ln { width: 26px; height: 2px; background: var(--mj-border-strong); }\n\n/* ---- DAG flow canvas ---- */\n.ps-flow {\n position: relative;\n background:\n linear-gradient(var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n linear-gradient(90deg, var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default); border-radius: 10px;\n min-height: 620px; overflow: hidden;\n}\n.ps-node {\n position: absolute; width: 190px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong); border-left-width: 3px; border-radius: 10px;\n box-shadow: var(--mj-shadow-sm); cursor: pointer;\n}\n.ps-node .nh {\n display: flex; align-items: center; gap: 8px; padding: 9px 11px;\n border-bottom: 1px solid var(--mj-border-subtle); font-weight: 650; font-size: 12.5px; color: var(--mj-text-primary);\n}\n.ps-node .nh i.tile {\n width: 24px; height: 24px; border-radius: 6px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 12px; flex: 0 0 auto;\n}\n.ps-node .nh .ps-tag { margin-left: auto; }\n.ps-node .nb { padding: 9px 11px; font-size: 12px; color: var(--mj-text-secondary); }\n.ps-node .nb-row { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; }\n.ps-node .nb-row .k { color: var(--mj-text-muted); }\n.ps-node.src { border-left-color: var(--ps-node-source); }\n.ps-node.src .nh i.tile { background: var(--ps-node-source); }\n.ps-node.feat { border-left-color: var(--ps-node-feature); }\n.ps-node.feat .nh i.tile { background: var(--ps-node-feature); }\n.ps-node.emb { border-left-color: var(--ps-node-embedding); }\n.ps-node.emb .nh i.tile { background: var(--ps-node-embedding); }\n.ps-node.target { border-left-color: var(--ps-node-target); }\n.ps-node.target .nh i.tile { background: var(--ps-node-target); }\n.ps-node.algo { border-left-color: var(--ps-node-algorithm); }\n.ps-node.algo .nh i.tile { background: var(--ps-node-algorithm); }\n.ps-node.output { border-left-color: var(--mj-status-success); }\n.ps-node.output .nh i.tile { background: var(--mj-status-success); }\n.ps-node.selected {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n.ps-node .port {\n position: absolute; width: 11px; height: 11px; border-radius: 50%;\n background: var(--mj-bg-surface); border: 2px solid var(--mj-brand-primary);\n top: 50%; transform: translateY(-50%);\n}\n.ps-node .port.in { left: -6px; }\n.ps-node .port.out { right: -6px; }\nsvg.ps-edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }\nsvg.ps-edges path { stroke: color-mix(in srgb, var(--mj-brand-primary) 45%, transparent); stroke-width: 2; fill: none; }\n\n/* palette chips */\n.ps-pchip {\n display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px; border-radius: 8px;\n border: 1px dashed var(--mj-border-strong); background: var(--mj-bg-surface);\n font-size: 12.5px; font-weight: 600; color: var(--mj-text-secondary); cursor: grab;\n}\n.ps-pchip i { font-size: 12px; color: var(--mj-text-inverse); width: 20px; height: 20px; border-radius: 5px; display: grid; place-items: center; }\n.ps-pchip.s i { background: var(--ps-node-source); }\n.ps-pchip.f i { background: var(--ps-node-feature); }\n.ps-pchip.e i { background: var(--ps-node-embedding); }\n.ps-pchip.t i { background: var(--ps-node-target); }\n.ps-pchip.a i { background: var(--ps-node-algorithm); }\n\n/* segmented control */\n.ps-seg { display: inline-flex; border: 1px solid var(--mj-border-default); border-radius: 8px; overflow: hidden; }\n.ps-seg button {\n border: none; background: var(--mj-bg-surface); color: var(--mj-text-secondary);\n padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer;\n border-right: 1px solid var(--mj-border-default);\n}\n.ps-seg button:last-child { border-right: none; }\n.ps-seg button.on { background: var(--mj-brand-primary); color: var(--mj-text-inverse); }\n\n/* field control */\n.ps-field { margin-bottom: 14px; }\n.ps-field > label {\n display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; margin-bottom: 6px;\n}\n.ps-input {\n width: 100%; padding: 8px 10px; border: 1px solid var(--mj-border-default);\n border-radius: 6px; font-size: 13px; background: var(--mj-bg-surface); color: var(--mj-text-primary);\n box-sizing: border-box;\n}\n.ps-input:focus { outline: none; border-color: var(--mj-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent); }\n\n/* loading container */\n.ps-loading { display: flex; align-items: center; justify-content: center; padding: 60px 0; }\n\n/* ---- empty / prompt states ---- */\n.ps-empty {\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n text-align: center; gap: 10px; padding: 56px 24px; color: var(--mj-text-muted);\n}\n.ps-empty .ps-empty-ico {\n width: 60px; height: 60px; border-radius: 16px; display: grid; place-items: center;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-disabled); font-size: 24px;\n}\n.ps-empty h3 { margin: 4px 0 0; font-size: 16px; font-weight: 650; color: var(--mj-text-secondary); }\n.ps-empty p { margin: 0; max-width: 420px; font-size: 13px; line-height: 1.5; }\n\n/* ---- confirm modal ---- */\n.ps-modal-backdrop {\n position: fixed; inset: 0; z-index: 1000;\n background: var(--mj-bg-overlay);\n display: flex; align-items: center; justify-content: center; padding: 24px;\n}\n.ps-modal {\n width: 100%; max-width: 480px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default); border-radius: 14px;\n box-shadow: var(--mj-shadow-lg, var(--mj-shadow-sm)); overflow: hidden;\n}\n.ps-modal-head {\n display: flex; align-items: center; gap: 12px; padding: 18px 20px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-modal-head .ico {\n width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center;\n font-size: 16px; flex: 0 0 auto;\n background: var(--mj-status-info-bg); color: var(--mj-status-info-text);\n}\n.ps-modal-head.warn .ico { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-modal-head.danger .ico { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-modal-head h3 { margin: 0; font-size: 15.5px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-modal-body { padding: 18px 20px; font-size: 13.5px; line-height: 1.55; color: var(--mj-text-secondary); }\n.ps-modal-body .ps-field { margin-top: 14px; margin-bottom: 0; }\n.ps-modal-foot {\n display: flex; align-items: center; gap: 10px; padding: 14px 20px;\n border-top: 1px solid var(--mj-border-subtle); background: var(--mj-bg-surface-card);\n}\n.ps-modal-foot .ps-spacer { flex: 1; }\n"], encapsulation: 2 });
120
+ } }, dependencies: [CommonModule, FormsModule, i1.DefaultValueAccessor, i1.NgControlStatus, i1.NgModel, MJButtonDirective], styles: ["/* ============================================================================\n Predictive Studio \u2014 shared panel styles (design-token compliant)\n Maps the mockup design system onto MemberJunction semantic --mj-* tokens.\n Loaded by every PS panel component (styleUrls) under ViewEncapsulation.None,\n so the class names are global within the PS dashboard subtree.\n No hardcoded colors except the intentional categorical node/algorithm palette\n (declared once as --ps-* custom props below) which must stay distinct across\n light/dark to remain readable as diagram accents.\n ============================================================================ */\n\n/* Categorical accents for DAG node types + algorithm chips. These are\n data-visualization colors that must remain distinct regardless of theme\n (allowed exception #3 in the design-token rules). */\n.ps-panel {\n --ps-node-source: #6b7280;\n --ps-node-feature: #2050c8;\n --ps-node-embedding: #7b3ff0;\n --ps-node-target: #1f9d57;\n --ps-node-algorithm: #d98213;\n /* budget-gauge categorical accents (reuse the node palette so light/dark stay distinct) */\n --ps-gauge-cost: var(--ps-node-algorithm);\n --ps-gauge-iterations: var(--ps-node-feature);\n}\n\n.ps-panel {\n display: block;\n height: 100%;\n}\n\n/* ---- generic layout helpers ---- */\n.ps-row { display: flex; gap: 16px; }\n.ps-col { display: flex; flex-direction: column; gap: 16px; }\n.ps-wrap { flex-wrap: wrap; }\n.ps-spacer { flex: 1; }\n.ps-muted { color: var(--mj-text-muted); }\n.ps-small { font-size: 12.5px; }\n.ps-mono { font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; font-size: 12.5px; }\n.ps-section-title {\n font-size: 12px; text-transform: uppercase; letter-spacing: .06em;\n color: var(--mj-text-muted); margin: 4px 0 2px; font-weight: 700;\n}\n\n/* ---- cards ---- */\n.ps-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n}\n.ps-card-head {\n display: flex; align-items: center; gap: 10px;\n padding: 14px 16px; border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-card-head h3 { font-size: 14.5px; margin: 0; font-weight: 650; color: var(--mj-text-primary); }\n.ps-card-body { padding: 16px; }\n\n/* ---- KPI tiles ---- */\n.ps-kpi {\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-radius: 8px; padding: 16px 18px;\n}\n.ps-kpi .label { font-size: 12px; color: var(--mj-text-muted); font-weight: 600; }\n.ps-kpi .val { font-size: 26px; font-weight: 720; letter-spacing: -.02em; margin-top: 2px; color: var(--mj-text-primary); }\n\n/* ---- badges ---- */\n.ps-badge {\n display: inline-flex; align-items: center; gap: 5px;\n padding: 2px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 650; line-height: 1.7;\n}\n.ps-badge.gray { background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n.ps-badge.blue { background: var(--mj-status-info-bg); color: var(--mj-status-info-text); }\n.ps-badge.green { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n.ps-badge.amber { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-badge.red { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }\n\n/* recommendation-level chips (the ranking matrix payoff) */\n.ps-lvl {\n display: inline-flex; align-items: center; justify-content: center;\n min-width: 92px; padding: 4px 10px; border-radius: 7px;\n font-weight: 650; font-size: 11.5px; color: var(--mj-text-inverse);\n}\n.ps-lvl-Primary { background: var(--mj-status-success); }\n.ps-lvl-Strong { background: var(--mj-status-info); }\n.ps-lvl-Viable { background: var(--mj-status-warning); }\n.ps-lvl-Weak { background: var(--mj-text-disabled); }\n.ps-lvl-NotRecommended { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n\n/* ---- tags ---- */\n.ps-tag {\n display: inline-block; padding: 2px 8px; border-radius: 6px;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary);\n font-size: 11.5px; font-weight: 600;\n}\n.ps-tag.muted { opacity: .6; }\n.ps-divider { height: 1px; background: var(--mj-border-subtle); margin: 8px 0; }\n\n/* ---- tables ---- */\n.ps-table { width: 100%; border-collapse: collapse; font-size: 13px; }\n.ps-table th {\n text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; padding: 9px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n.ps-table td { padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); color: var(--mj-text-primary); }\n.ps-table tr:hover td { background: var(--mj-bg-surface-hover); }\n.ps-table .num { text-align: right; font-variant-numeric: tabular-nums; }\n\n.ps-rank {\n width: 22px; height: 22px; border-radius: 6px; background: var(--mj-bg-surface-sunken);\n display: inline-grid; place-items: center; font-weight: 700; font-size: 11.5px; color: var(--mj-text-secondary);\n}\n.ps-rank.gold { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n\n/* ---- progress bars ---- */\n.ps-bar { height: 8px; border-radius: 999px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-bar > span { display: block; height: 100%; border-radius: 999px; background: var(--mj-brand-primary); }\n.ps-bar.warn > span { background: var(--mj-status-warning); }\n.ps-bar.run > span { background: var(--mj-brand-primary); }\n\n.ps-fbar { display: grid; grid-template-columns: minmax(150px, 1.3fr) 2fr 56px; align-items: center; gap: 14px; padding: 8px 0; }\n.ps-fbar + .ps-fbar { border-top: 1px solid var(--mj-border-subtle); }\n.ps-fbar .track { height: 10px; border-radius: var(--mj-radius-full); background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-fbar .track > span { display: block; height: 100%; border-radius: var(--mj-radius-full); background: var(--mj-brand-primary); }\n.ps-fbar .track > span.warn { background: var(--mj-status-warning); }\n.ps-fbar .name { color: var(--mj-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ps-fbar > .ps-mono { text-align: right; color: var(--mj-text-secondary); font-variant-numeric: tabular-nums; }\n\n/* gauge */\n.ps-gauge {\n position: relative; width: 64px; height: 64px; border-radius: 50%; flex: 0 0 auto;\n background: conic-gradient(var(--ps-gauge-color, var(--mj-brand-primary)) calc(var(--ps-p, 40) * 1%), var(--mj-bg-surface-sunken) 0);\n display: grid; place-items: center;\n}\n.ps-gauge::after { content: ''; position: absolute; inset: 9px; background: var(--mj-bg-surface); border-radius: 50%; }\n.ps-gauge .g-val { position: relative; font-weight: 720; font-size: 14px; color: var(--mj-text-primary); }\n\n/* warning / info callouts */\n.ps-callout { display: flex; gap: 11px; padding: 12px 14px; border-radius: 10px; border: 1px solid; }\n.ps-callout.warn { background: var(--mj-status-warning-bg); border-color: var(--mj-status-warning-border); color: var(--mj-status-warning-text); }\n.ps-callout.info { background: var(--mj-status-info-bg); border-color: var(--mj-status-info-border); color: var(--mj-status-info-text); }\n.ps-callout.success { background: var(--mj-status-success-bg); border-color: var(--mj-status-success-border); color: var(--mj-status-success-text); }\n.ps-callout i { font-size: 16px; margin-top: 1px; }\n\n/* lifecycle stepper */\n.ps-stepper { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }\n.ps-step { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--mj-text-muted); }\n.ps-step .pip {\n width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--mj-border-strong);\n display: grid; place-items: center; font-size: 11px;\n}\n.ps-step.done { color: var(--mj-status-success-text); }\n.ps-step.done .pip { background: var(--mj-status-success); border-color: var(--mj-status-success); color: var(--mj-text-inverse); }\n.ps-step.curr { color: var(--mj-brand-primary); }\n.ps-step.curr .pip { border-color: var(--mj-brand-primary); color: var(--mj-brand-primary); }\n.ps-step .ln { width: 26px; height: 2px; background: var(--mj-border-strong); }\n\n/* ---- DAG flow canvas ---- */\n.ps-flow {\n position: relative;\n background:\n linear-gradient(var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n linear-gradient(90deg, var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default); border-radius: 10px;\n min-height: 620px; overflow: hidden;\n}\n.ps-node {\n position: absolute; width: 190px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong); border-left-width: 3px; border-radius: 8px;\n cursor: pointer;\n}\n.ps-node .nh {\n display: flex; align-items: center; gap: 8px; padding: 9px 11px;\n border-bottom: 1px solid var(--mj-border-subtle); font-weight: 650; font-size: 12.5px; color: var(--mj-text-primary);\n}\n.ps-node .nh i.tile {\n width: 24px; height: 24px; border-radius: 6px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 12px; flex: 0 0 auto;\n}\n.ps-node .nh .ps-tag { margin-left: auto; }\n.ps-node .nb { padding: 9px 11px; font-size: 12px; color: var(--mj-text-secondary); }\n.ps-node .nb-row { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; }\n.ps-node .nb-row .k { color: var(--mj-text-muted); }\n.ps-node.src { border-left-color: var(--ps-node-source); }\n.ps-node.src .nh i.tile { background: var(--ps-node-source); }\n.ps-node.feat { border-left-color: var(--ps-node-feature); }\n.ps-node.feat .nh i.tile { background: var(--ps-node-feature); }\n.ps-node.emb { border-left-color: var(--ps-node-embedding); }\n.ps-node.emb .nh i.tile { background: var(--ps-node-embedding); }\n.ps-node.target { border-left-color: var(--ps-node-target); }\n.ps-node.target .nh i.tile { background: var(--ps-node-target); }\n.ps-node.algo { border-left-color: var(--ps-node-algorithm); }\n.ps-node.algo .nh i.tile { background: var(--ps-node-algorithm); }\n.ps-node.output { border-left-color: var(--mj-status-success); }\n.ps-node.output .nh i.tile { background: var(--mj-status-success); }\n.ps-node.selected {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n.ps-node .port {\n position: absolute; width: 11px; height: 11px; border-radius: 50%;\n background: var(--mj-bg-surface); border: 2px solid var(--mj-brand-primary);\n top: 50%; transform: translateY(-50%);\n}\n.ps-node .port.in { left: -6px; }\n.ps-node .port.out { right: -6px; }\nsvg.ps-edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }\nsvg.ps-edges path { stroke: color-mix(in srgb, var(--mj-brand-primary) 45%, transparent); stroke-width: 2; fill: none; }\n\n/* palette chips */\n.ps-pchip {\n display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px; border-radius: 8px;\n border: 1px dashed var(--mj-border-strong); background: var(--mj-bg-surface);\n font-size: 12.5px; font-weight: 600; color: var(--mj-text-secondary); cursor: grab;\n}\n.ps-pchip i { font-size: 12px; color: var(--mj-text-inverse); width: 20px; height: 20px; border-radius: 5px; display: grid; place-items: center; }\n.ps-pchip.s i { background: var(--ps-node-source); }\n.ps-pchip.f i { background: var(--ps-node-feature); }\n.ps-pchip.e i { background: var(--ps-node-embedding); }\n.ps-pchip.t i { background: var(--ps-node-target); }\n.ps-pchip.a i { background: var(--ps-node-algorithm); }\n\n/* segmented control */\n.ps-seg { display: inline-flex; border: 1px solid var(--mj-border-default); border-radius: 8px; overflow: hidden; }\n.ps-seg button {\n border: none; background: var(--mj-bg-surface); color: var(--mj-text-secondary);\n padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer;\n border-right: 1px solid var(--mj-border-default);\n}\n.ps-seg button:last-child { border-right: none; }\n.ps-seg button.on { background: var(--mj-brand-primary); color: var(--mj-text-inverse); }\n\n/* field control */\n.ps-field { margin-bottom: 14px; }\n.ps-field > label {\n display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; margin-bottom: 6px;\n}\n.ps-input {\n width: 100%; padding: 8px 10px; border: 1px solid var(--mj-border-default);\n border-radius: 6px; font-size: 13px; background: var(--mj-bg-surface); color: var(--mj-text-primary);\n box-sizing: border-box;\n}\n.ps-input:focus { outline: none; border-color: var(--mj-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent); }\n\n/* loading container */\n.ps-loading { display: flex; align-items: center; justify-content: center; padding: 60px 0; }\n\n/* ---- empty / prompt states ---- */\n.ps-empty {\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n text-align: center; gap: 10px; padding: 56px 24px; color: var(--mj-text-muted);\n}\n.ps-empty .ps-empty-ico {\n width: 60px; height: 60px; border-radius: 16px; display: grid; place-items: center;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-disabled); font-size: 24px;\n}\n.ps-empty h3 { margin: 4px 0 0; font-size: 16px; font-weight: 650; color: var(--mj-text-secondary); }\n.ps-empty p { margin: 0; max-width: 420px; font-size: 13px; line-height: 1.5; }\n\n/* ---- confirm modal ---- */\n.ps-modal-backdrop {\n position: fixed; inset: 0; z-index: 1000;\n background: var(--mj-bg-overlay);\n display: flex; align-items: center; justify-content: center; padding: 24px;\n}\n.ps-modal {\n width: 100%; max-width: 480px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default); border-radius: 14px;\n box-shadow: var(--mj-shadow-lg, var(--mj-shadow-sm)); overflow: hidden;\n}\n.ps-modal-head {\n display: flex; align-items: center; gap: 12px; padding: 18px 20px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-modal-head .ico {\n width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center;\n font-size: 16px; flex: 0 0 auto;\n background: var(--mj-status-info-bg); color: var(--mj-status-info-text);\n}\n.ps-modal-head.warn .ico { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-modal-head.danger .ico { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-modal-head h3 { margin: 0; font-size: 15.5px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-modal-body { padding: 18px 20px; font-size: 13.5px; line-height: 1.55; color: var(--mj-text-secondary); }\n.ps-modal-body .ps-field { margin-top: 14px; margin-bottom: 0; }\n.ps-modal-foot {\n display: flex; align-items: center; gap: 10px; padding: 14px 20px;\n border-top: 1px solid var(--mj-border-subtle); background: var(--mj-bg-surface-card);\n}\n.ps-modal-foot .ps-spacer { flex: 1; }\n"], encapsulation: 2 });
121
121
  }
122
122
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PSConfirmModalComponent, [{
123
123
  type: Component,
@@ -161,7 +161,7 @@ export class PSConfirmModalComponent {
161
161
  </div>
162
162
  </div>
163
163
  </div>
164
- `, styles: ["/* ============================================================================\n Predictive Studio \u2014 shared panel styles (design-token compliant)\n Maps the mockup design system onto MemberJunction semantic --mj-* tokens.\n Loaded by every PS panel component (styleUrls) under ViewEncapsulation.None,\n so the class names are global within the PS dashboard subtree.\n No hardcoded colors except the intentional categorical node/algorithm palette\n (declared once as --ps-* custom props below) which must stay distinct across\n light/dark to remain readable as diagram accents.\n ============================================================================ */\n\n/* Categorical accents for DAG node types + algorithm chips. These are\n data-visualization colors that must remain distinct regardless of theme\n (allowed exception #3 in the design-token rules). */\n.ps-panel {\n --ps-node-source: #6b7280;\n --ps-node-feature: #2050c8;\n --ps-node-embedding: #7b3ff0;\n --ps-node-target: #1f9d57;\n --ps-node-algorithm: #d98213;\n /* budget-gauge categorical accents (reuse the node palette so light/dark stay distinct) */\n --ps-gauge-cost: var(--ps-node-algorithm);\n --ps-gauge-iterations: var(--ps-node-feature);\n}\n\n.ps-panel {\n display: block;\n height: 100%;\n}\n\n/* ---- generic layout helpers ---- */\n.ps-row { display: flex; gap: 16px; }\n.ps-col { display: flex; flex-direction: column; gap: 16px; }\n.ps-wrap { flex-wrap: wrap; }\n.ps-spacer { flex: 1; }\n.ps-muted { color: var(--mj-text-muted); }\n.ps-small { font-size: 12.5px; }\n.ps-mono { font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; font-size: 12.5px; }\n.ps-section-title {\n font-size: 12px; text-transform: uppercase; letter-spacing: .06em;\n color: var(--mj-text-muted); margin: 4px 0 2px; font-weight: 700;\n}\n\n/* ---- cards ---- */\n.ps-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n box-shadow: var(--mj-shadow-sm);\n overflow: hidden;\n}\n.ps-card-head {\n display: flex; align-items: center; gap: 10px;\n padding: 14px 16px; border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-card-head h3 { font-size: 14.5px; margin: 0; font-weight: 650; color: var(--mj-text-primary); }\n.ps-card-body { padding: 16px; }\n\n/* ---- KPI tiles ---- */\n.ps-kpi {\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-radius: 10px; padding: 16px 18px; box-shadow: var(--mj-shadow-sm);\n}\n.ps-kpi .label { font-size: 12px; color: var(--mj-text-muted); font-weight: 600; }\n.ps-kpi .val { font-size: 26px; font-weight: 720; letter-spacing: -.02em; margin-top: 2px; color: var(--mj-text-primary); }\n\n/* ---- badges ---- */\n.ps-badge {\n display: inline-flex; align-items: center; gap: 5px;\n padding: 2px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 650; line-height: 1.7;\n}\n.ps-badge.gray { background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n.ps-badge.blue { background: var(--mj-status-info-bg); color: var(--mj-status-info-text); }\n.ps-badge.green { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n.ps-badge.amber { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-badge.red { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }\n\n/* recommendation-level chips (the ranking matrix payoff) */\n.ps-lvl {\n display: inline-flex; align-items: center; justify-content: center;\n min-width: 92px; padding: 4px 10px; border-radius: 7px;\n font-weight: 650; font-size: 11.5px; color: var(--mj-text-inverse);\n}\n.ps-lvl-Primary { background: var(--mj-status-success); }\n.ps-lvl-Strong { background: var(--mj-status-info); }\n.ps-lvl-Viable { background: var(--mj-status-warning); }\n.ps-lvl-Weak { background: var(--mj-text-disabled); }\n.ps-lvl-NotRecommended { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n\n/* ---- tags ---- */\n.ps-tag {\n display: inline-block; padding: 2px 8px; border-radius: 6px;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary);\n font-size: 11.5px; font-weight: 600;\n}\n.ps-tag.muted { opacity: .6; }\n.ps-divider { height: 1px; background: var(--mj-border-subtle); margin: 8px 0; }\n\n/* ---- tables ---- */\n.ps-table { width: 100%; border-collapse: collapse; font-size: 13px; }\n.ps-table th {\n text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; padding: 9px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n.ps-table td { padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); color: var(--mj-text-primary); }\n.ps-table tr:hover td { background: var(--mj-bg-surface-hover); }\n.ps-table .num { text-align: right; font-variant-numeric: tabular-nums; }\n\n.ps-rank {\n width: 22px; height: 22px; border-radius: 6px; background: var(--mj-bg-surface-sunken);\n display: inline-grid; place-items: center; font-weight: 700; font-size: 11.5px; color: var(--mj-text-secondary);\n}\n.ps-rank.gold { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n\n/* ---- progress bars ---- */\n.ps-bar { height: 8px; border-radius: 999px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-bar > span { display: block; height: 100%; border-radius: 999px; background: var(--mj-brand-primary); }\n.ps-bar.warn > span { background: var(--mj-status-warning); }\n.ps-bar.run > span { background: var(--mj-brand-primary); }\n\n.ps-fbar { display: grid; grid-template-columns: 150px 1fr 48px; align-items: center; gap: 10px; padding: 5px 0; }\n.ps-fbar .track { height: 14px; border-radius: 5px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-fbar .track > span { display: block; height: 100%; background: var(--mj-brand-primary); }\n.ps-fbar .track > span.warn { background: var(--mj-status-warning); }\n.ps-fbar .name { color: var(--mj-text-secondary); }\n\n/* gauge */\n.ps-gauge {\n position: relative; width: 64px; height: 64px; border-radius: 50%; flex: 0 0 auto;\n background: conic-gradient(var(--ps-gauge-color, var(--mj-brand-primary)) calc(var(--ps-p, 40) * 1%), var(--mj-bg-surface-sunken) 0);\n display: grid; place-items: center;\n}\n.ps-gauge::after { content: ''; position: absolute; inset: 9px; background: var(--mj-bg-surface); border-radius: 50%; }\n.ps-gauge .g-val { position: relative; font-weight: 720; font-size: 14px; color: var(--mj-text-primary); }\n\n/* warning / info callouts */\n.ps-callout { display: flex; gap: 11px; padding: 12px 14px; border-radius: 10px; border: 1px solid; }\n.ps-callout.warn { background: var(--mj-status-warning-bg); border-color: var(--mj-status-warning-border); color: var(--mj-status-warning-text); }\n.ps-callout.info { background: var(--mj-status-info-bg); border-color: var(--mj-status-info-border); color: var(--mj-status-info-text); }\n.ps-callout.success { background: var(--mj-status-success-bg); border-color: var(--mj-status-success-border); color: var(--mj-status-success-text); }\n.ps-callout i { font-size: 16px; margin-top: 1px; }\n\n/* lifecycle stepper */\n.ps-stepper { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }\n.ps-step { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--mj-text-muted); }\n.ps-step .pip {\n width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--mj-border-strong);\n display: grid; place-items: center; font-size: 11px;\n}\n.ps-step.done { color: var(--mj-status-success-text); }\n.ps-step.done .pip { background: var(--mj-status-success); border-color: var(--mj-status-success); color: var(--mj-text-inverse); }\n.ps-step.curr { color: var(--mj-brand-primary); }\n.ps-step.curr .pip { border-color: var(--mj-brand-primary); color: var(--mj-brand-primary); }\n.ps-step .ln { width: 26px; height: 2px; background: var(--mj-border-strong); }\n\n/* ---- DAG flow canvas ---- */\n.ps-flow {\n position: relative;\n background:\n linear-gradient(var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n linear-gradient(90deg, var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default); border-radius: 10px;\n min-height: 620px; overflow: hidden;\n}\n.ps-node {\n position: absolute; width: 190px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong); border-left-width: 3px; border-radius: 10px;\n box-shadow: var(--mj-shadow-sm); cursor: pointer;\n}\n.ps-node .nh {\n display: flex; align-items: center; gap: 8px; padding: 9px 11px;\n border-bottom: 1px solid var(--mj-border-subtle); font-weight: 650; font-size: 12.5px; color: var(--mj-text-primary);\n}\n.ps-node .nh i.tile {\n width: 24px; height: 24px; border-radius: 6px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 12px; flex: 0 0 auto;\n}\n.ps-node .nh .ps-tag { margin-left: auto; }\n.ps-node .nb { padding: 9px 11px; font-size: 12px; color: var(--mj-text-secondary); }\n.ps-node .nb-row { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; }\n.ps-node .nb-row .k { color: var(--mj-text-muted); }\n.ps-node.src { border-left-color: var(--ps-node-source); }\n.ps-node.src .nh i.tile { background: var(--ps-node-source); }\n.ps-node.feat { border-left-color: var(--ps-node-feature); }\n.ps-node.feat .nh i.tile { background: var(--ps-node-feature); }\n.ps-node.emb { border-left-color: var(--ps-node-embedding); }\n.ps-node.emb .nh i.tile { background: var(--ps-node-embedding); }\n.ps-node.target { border-left-color: var(--ps-node-target); }\n.ps-node.target .nh i.tile { background: var(--ps-node-target); }\n.ps-node.algo { border-left-color: var(--ps-node-algorithm); }\n.ps-node.algo .nh i.tile { background: var(--ps-node-algorithm); }\n.ps-node.output { border-left-color: var(--mj-status-success); }\n.ps-node.output .nh i.tile { background: var(--mj-status-success); }\n.ps-node.selected {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n.ps-node .port {\n position: absolute; width: 11px; height: 11px; border-radius: 50%;\n background: var(--mj-bg-surface); border: 2px solid var(--mj-brand-primary);\n top: 50%; transform: translateY(-50%);\n}\n.ps-node .port.in { left: -6px; }\n.ps-node .port.out { right: -6px; }\nsvg.ps-edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }\nsvg.ps-edges path { stroke: color-mix(in srgb, var(--mj-brand-primary) 45%, transparent); stroke-width: 2; fill: none; }\n\n/* palette chips */\n.ps-pchip {\n display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px; border-radius: 8px;\n border: 1px dashed var(--mj-border-strong); background: var(--mj-bg-surface);\n font-size: 12.5px; font-weight: 600; color: var(--mj-text-secondary); cursor: grab;\n}\n.ps-pchip i { font-size: 12px; color: var(--mj-text-inverse); width: 20px; height: 20px; border-radius: 5px; display: grid; place-items: center; }\n.ps-pchip.s i { background: var(--ps-node-source); }\n.ps-pchip.f i { background: var(--ps-node-feature); }\n.ps-pchip.e i { background: var(--ps-node-embedding); }\n.ps-pchip.t i { background: var(--ps-node-target); }\n.ps-pchip.a i { background: var(--ps-node-algorithm); }\n\n/* segmented control */\n.ps-seg { display: inline-flex; border: 1px solid var(--mj-border-default); border-radius: 8px; overflow: hidden; }\n.ps-seg button {\n border: none; background: var(--mj-bg-surface); color: var(--mj-text-secondary);\n padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer;\n border-right: 1px solid var(--mj-border-default);\n}\n.ps-seg button:last-child { border-right: none; }\n.ps-seg button.on { background: var(--mj-brand-primary); color: var(--mj-text-inverse); }\n\n/* field control */\n.ps-field { margin-bottom: 14px; }\n.ps-field > label {\n display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; margin-bottom: 6px;\n}\n.ps-input {\n width: 100%; padding: 8px 10px; border: 1px solid var(--mj-border-default);\n border-radius: 6px; font-size: 13px; background: var(--mj-bg-surface); color: var(--mj-text-primary);\n box-sizing: border-box;\n}\n.ps-input:focus { outline: none; border-color: var(--mj-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent); }\n\n/* loading container */\n.ps-loading { display: flex; align-items: center; justify-content: center; padding: 60px 0; }\n\n/* ---- empty / prompt states ---- */\n.ps-empty {\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n text-align: center; gap: 10px; padding: 56px 24px; color: var(--mj-text-muted);\n}\n.ps-empty .ps-empty-ico {\n width: 60px; height: 60px; border-radius: 16px; display: grid; place-items: center;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-disabled); font-size: 24px;\n}\n.ps-empty h3 { margin: 4px 0 0; font-size: 16px; font-weight: 650; color: var(--mj-text-secondary); }\n.ps-empty p { margin: 0; max-width: 420px; font-size: 13px; line-height: 1.5; }\n\n/* ---- confirm modal ---- */\n.ps-modal-backdrop {\n position: fixed; inset: 0; z-index: 1000;\n background: var(--mj-bg-overlay);\n display: flex; align-items: center; justify-content: center; padding: 24px;\n}\n.ps-modal {\n width: 100%; max-width: 480px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default); border-radius: 14px;\n box-shadow: var(--mj-shadow-lg, var(--mj-shadow-sm)); overflow: hidden;\n}\n.ps-modal-head {\n display: flex; align-items: center; gap: 12px; padding: 18px 20px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-modal-head .ico {\n width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center;\n font-size: 16px; flex: 0 0 auto;\n background: var(--mj-status-info-bg); color: var(--mj-status-info-text);\n}\n.ps-modal-head.warn .ico { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-modal-head.danger .ico { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-modal-head h3 { margin: 0; font-size: 15.5px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-modal-body { padding: 18px 20px; font-size: 13.5px; line-height: 1.55; color: var(--mj-text-secondary); }\n.ps-modal-body .ps-field { margin-top: 14px; margin-bottom: 0; }\n.ps-modal-foot {\n display: flex; align-items: center; gap: 10px; padding: 14px 20px;\n border-top: 1px solid var(--mj-border-subtle); background: var(--mj-bg-surface-card);\n}\n.ps-modal-foot .ps-spacer { flex: 1; }\n"] }]
164
+ `, styles: ["/* ============================================================================\n Predictive Studio \u2014 shared panel styles (design-token compliant)\n Maps the mockup design system onto MemberJunction semantic --mj-* tokens.\n Loaded by every PS panel component (styleUrls) under ViewEncapsulation.None,\n so the class names are global within the PS dashboard subtree.\n No hardcoded colors except the intentional categorical node/algorithm palette\n (declared once as --ps-* custom props below) which must stay distinct across\n light/dark to remain readable as diagram accents.\n ============================================================================ */\n\n/* Categorical accents for DAG node types + algorithm chips. These are\n data-visualization colors that must remain distinct regardless of theme\n (allowed exception #3 in the design-token rules). */\n.ps-panel {\n --ps-node-source: #6b7280;\n --ps-node-feature: #2050c8;\n --ps-node-embedding: #7b3ff0;\n --ps-node-target: #1f9d57;\n --ps-node-algorithm: #d98213;\n /* budget-gauge categorical accents (reuse the node palette so light/dark stay distinct) */\n --ps-gauge-cost: var(--ps-node-algorithm);\n --ps-gauge-iterations: var(--ps-node-feature);\n}\n\n.ps-panel {\n display: block;\n height: 100%;\n}\n\n/* ---- generic layout helpers ---- */\n.ps-row { display: flex; gap: 16px; }\n.ps-col { display: flex; flex-direction: column; gap: 16px; }\n.ps-wrap { flex-wrap: wrap; }\n.ps-spacer { flex: 1; }\n.ps-muted { color: var(--mj-text-muted); }\n.ps-small { font-size: 12.5px; }\n.ps-mono { font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; font-size: 12.5px; }\n.ps-section-title {\n font-size: 12px; text-transform: uppercase; letter-spacing: .06em;\n color: var(--mj-text-muted); margin: 4px 0 2px; font-weight: 700;\n}\n\n/* ---- cards ---- */\n.ps-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n}\n.ps-card-head {\n display: flex; align-items: center; gap: 10px;\n padding: 14px 16px; border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-card-head h3 { font-size: 14.5px; margin: 0; font-weight: 650; color: var(--mj-text-primary); }\n.ps-card-body { padding: 16px; }\n\n/* ---- KPI tiles ---- */\n.ps-kpi {\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-radius: 8px; padding: 16px 18px;\n}\n.ps-kpi .label { font-size: 12px; color: var(--mj-text-muted); font-weight: 600; }\n.ps-kpi .val { font-size: 26px; font-weight: 720; letter-spacing: -.02em; margin-top: 2px; color: var(--mj-text-primary); }\n\n/* ---- badges ---- */\n.ps-badge {\n display: inline-flex; align-items: center; gap: 5px;\n padding: 2px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 650; line-height: 1.7;\n}\n.ps-badge.gray { background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n.ps-badge.blue { background: var(--mj-status-info-bg); color: var(--mj-status-info-text); }\n.ps-badge.green { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n.ps-badge.amber { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-badge.red { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }\n\n/* recommendation-level chips (the ranking matrix payoff) */\n.ps-lvl {\n display: inline-flex; align-items: center; justify-content: center;\n min-width: 92px; padding: 4px 10px; border-radius: 7px;\n font-weight: 650; font-size: 11.5px; color: var(--mj-text-inverse);\n}\n.ps-lvl-Primary { background: var(--mj-status-success); }\n.ps-lvl-Strong { background: var(--mj-status-info); }\n.ps-lvl-Viable { background: var(--mj-status-warning); }\n.ps-lvl-Weak { background: var(--mj-text-disabled); }\n.ps-lvl-NotRecommended { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n\n/* ---- tags ---- */\n.ps-tag {\n display: inline-block; padding: 2px 8px; border-radius: 6px;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary);\n font-size: 11.5px; font-weight: 600;\n}\n.ps-tag.muted { opacity: .6; }\n.ps-divider { height: 1px; background: var(--mj-border-subtle); margin: 8px 0; }\n\n/* ---- tables ---- */\n.ps-table { width: 100%; border-collapse: collapse; font-size: 13px; }\n.ps-table th {\n text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; padding: 9px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n.ps-table td { padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); color: var(--mj-text-primary); }\n.ps-table tr:hover td { background: var(--mj-bg-surface-hover); }\n.ps-table .num { text-align: right; font-variant-numeric: tabular-nums; }\n\n.ps-rank {\n width: 22px; height: 22px; border-radius: 6px; background: var(--mj-bg-surface-sunken);\n display: inline-grid; place-items: center; font-weight: 700; font-size: 11.5px; color: var(--mj-text-secondary);\n}\n.ps-rank.gold { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n\n/* ---- progress bars ---- */\n.ps-bar { height: 8px; border-radius: 999px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-bar > span { display: block; height: 100%; border-radius: 999px; background: var(--mj-brand-primary); }\n.ps-bar.warn > span { background: var(--mj-status-warning); }\n.ps-bar.run > span { background: var(--mj-brand-primary); }\n\n.ps-fbar { display: grid; grid-template-columns: minmax(150px, 1.3fr) 2fr 56px; align-items: center; gap: 14px; padding: 8px 0; }\n.ps-fbar + .ps-fbar { border-top: 1px solid var(--mj-border-subtle); }\n.ps-fbar .track { height: 10px; border-radius: var(--mj-radius-full); background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-fbar .track > span { display: block; height: 100%; border-radius: var(--mj-radius-full); background: var(--mj-brand-primary); }\n.ps-fbar .track > span.warn { background: var(--mj-status-warning); }\n.ps-fbar .name { color: var(--mj-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ps-fbar > .ps-mono { text-align: right; color: var(--mj-text-secondary); font-variant-numeric: tabular-nums; }\n\n/* gauge */\n.ps-gauge {\n position: relative; width: 64px; height: 64px; border-radius: 50%; flex: 0 0 auto;\n background: conic-gradient(var(--ps-gauge-color, var(--mj-brand-primary)) calc(var(--ps-p, 40) * 1%), var(--mj-bg-surface-sunken) 0);\n display: grid; place-items: center;\n}\n.ps-gauge::after { content: ''; position: absolute; inset: 9px; background: var(--mj-bg-surface); border-radius: 50%; }\n.ps-gauge .g-val { position: relative; font-weight: 720; font-size: 14px; color: var(--mj-text-primary); }\n\n/* warning / info callouts */\n.ps-callout { display: flex; gap: 11px; padding: 12px 14px; border-radius: 10px; border: 1px solid; }\n.ps-callout.warn { background: var(--mj-status-warning-bg); border-color: var(--mj-status-warning-border); color: var(--mj-status-warning-text); }\n.ps-callout.info { background: var(--mj-status-info-bg); border-color: var(--mj-status-info-border); color: var(--mj-status-info-text); }\n.ps-callout.success { background: var(--mj-status-success-bg); border-color: var(--mj-status-success-border); color: var(--mj-status-success-text); }\n.ps-callout i { font-size: 16px; margin-top: 1px; }\n\n/* lifecycle stepper */\n.ps-stepper { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }\n.ps-step { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--mj-text-muted); }\n.ps-step .pip {\n width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--mj-border-strong);\n display: grid; place-items: center; font-size: 11px;\n}\n.ps-step.done { color: var(--mj-status-success-text); }\n.ps-step.done .pip { background: var(--mj-status-success); border-color: var(--mj-status-success); color: var(--mj-text-inverse); }\n.ps-step.curr { color: var(--mj-brand-primary); }\n.ps-step.curr .pip { border-color: var(--mj-brand-primary); color: var(--mj-brand-primary); }\n.ps-step .ln { width: 26px; height: 2px; background: var(--mj-border-strong); }\n\n/* ---- DAG flow canvas ---- */\n.ps-flow {\n position: relative;\n background:\n linear-gradient(var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n linear-gradient(90deg, var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default); border-radius: 10px;\n min-height: 620px; overflow: hidden;\n}\n.ps-node {\n position: absolute; width: 190px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong); border-left-width: 3px; border-radius: 8px;\n cursor: pointer;\n}\n.ps-node .nh {\n display: flex; align-items: center; gap: 8px; padding: 9px 11px;\n border-bottom: 1px solid var(--mj-border-subtle); font-weight: 650; font-size: 12.5px; color: var(--mj-text-primary);\n}\n.ps-node .nh i.tile {\n width: 24px; height: 24px; border-radius: 6px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 12px; flex: 0 0 auto;\n}\n.ps-node .nh .ps-tag { margin-left: auto; }\n.ps-node .nb { padding: 9px 11px; font-size: 12px; color: var(--mj-text-secondary); }\n.ps-node .nb-row { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; }\n.ps-node .nb-row .k { color: var(--mj-text-muted); }\n.ps-node.src { border-left-color: var(--ps-node-source); }\n.ps-node.src .nh i.tile { background: var(--ps-node-source); }\n.ps-node.feat { border-left-color: var(--ps-node-feature); }\n.ps-node.feat .nh i.tile { background: var(--ps-node-feature); }\n.ps-node.emb { border-left-color: var(--ps-node-embedding); }\n.ps-node.emb .nh i.tile { background: var(--ps-node-embedding); }\n.ps-node.target { border-left-color: var(--ps-node-target); }\n.ps-node.target .nh i.tile { background: var(--ps-node-target); }\n.ps-node.algo { border-left-color: var(--ps-node-algorithm); }\n.ps-node.algo .nh i.tile { background: var(--ps-node-algorithm); }\n.ps-node.output { border-left-color: var(--mj-status-success); }\n.ps-node.output .nh i.tile { background: var(--mj-status-success); }\n.ps-node.selected {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n.ps-node .port {\n position: absolute; width: 11px; height: 11px; border-radius: 50%;\n background: var(--mj-bg-surface); border: 2px solid var(--mj-brand-primary);\n top: 50%; transform: translateY(-50%);\n}\n.ps-node .port.in { left: -6px; }\n.ps-node .port.out { right: -6px; }\nsvg.ps-edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }\nsvg.ps-edges path { stroke: color-mix(in srgb, var(--mj-brand-primary) 45%, transparent); stroke-width: 2; fill: none; }\n\n/* palette chips */\n.ps-pchip {\n display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px; border-radius: 8px;\n border: 1px dashed var(--mj-border-strong); background: var(--mj-bg-surface);\n font-size: 12.5px; font-weight: 600; color: var(--mj-text-secondary); cursor: grab;\n}\n.ps-pchip i { font-size: 12px; color: var(--mj-text-inverse); width: 20px; height: 20px; border-radius: 5px; display: grid; place-items: center; }\n.ps-pchip.s i { background: var(--ps-node-source); }\n.ps-pchip.f i { background: var(--ps-node-feature); }\n.ps-pchip.e i { background: var(--ps-node-embedding); }\n.ps-pchip.t i { background: var(--ps-node-target); }\n.ps-pchip.a i { background: var(--ps-node-algorithm); }\n\n/* segmented control */\n.ps-seg { display: inline-flex; border: 1px solid var(--mj-border-default); border-radius: 8px; overflow: hidden; }\n.ps-seg button {\n border: none; background: var(--mj-bg-surface); color: var(--mj-text-secondary);\n padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer;\n border-right: 1px solid var(--mj-border-default);\n}\n.ps-seg button:last-child { border-right: none; }\n.ps-seg button.on { background: var(--mj-brand-primary); color: var(--mj-text-inverse); }\n\n/* field control */\n.ps-field { margin-bottom: 14px; }\n.ps-field > label {\n display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; margin-bottom: 6px;\n}\n.ps-input {\n width: 100%; padding: 8px 10px; border: 1px solid var(--mj-border-default);\n border-radius: 6px; font-size: 13px; background: var(--mj-bg-surface); color: var(--mj-text-primary);\n box-sizing: border-box;\n}\n.ps-input:focus { outline: none; border-color: var(--mj-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent); }\n\n/* loading container */\n.ps-loading { display: flex; align-items: center; justify-content: center; padding: 60px 0; }\n\n/* ---- empty / prompt states ---- */\n.ps-empty {\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n text-align: center; gap: 10px; padding: 56px 24px; color: var(--mj-text-muted);\n}\n.ps-empty .ps-empty-ico {\n width: 60px; height: 60px; border-radius: 16px; display: grid; place-items: center;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-disabled); font-size: 24px;\n}\n.ps-empty h3 { margin: 4px 0 0; font-size: 16px; font-weight: 650; color: var(--mj-text-secondary); }\n.ps-empty p { margin: 0; max-width: 420px; font-size: 13px; line-height: 1.5; }\n\n/* ---- confirm modal ---- */\n.ps-modal-backdrop {\n position: fixed; inset: 0; z-index: 1000;\n background: var(--mj-bg-overlay);\n display: flex; align-items: center; justify-content: center; padding: 24px;\n}\n.ps-modal {\n width: 100%; max-width: 480px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default); border-radius: 14px;\n box-shadow: var(--mj-shadow-lg, var(--mj-shadow-sm)); overflow: hidden;\n}\n.ps-modal-head {\n display: flex; align-items: center; gap: 12px; padding: 18px 20px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-modal-head .ico {\n width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center;\n font-size: 16px; flex: 0 0 auto;\n background: var(--mj-status-info-bg); color: var(--mj-status-info-text);\n}\n.ps-modal-head.warn .ico { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-modal-head.danger .ico { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-modal-head h3 { margin: 0; font-size: 15.5px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-modal-body { padding: 18px 20px; font-size: 13.5px; line-height: 1.55; color: var(--mj-text-secondary); }\n.ps-modal-body .ps-field { margin-top: 14px; margin-bottom: 0; }\n.ps-modal-foot {\n display: flex; align-items: center; gap: 10px; padding: 14px 20px;\n border-top: 1px solid var(--mj-border-subtle); background: var(--mj-bg-surface-card);\n}\n.ps-modal-foot .ps-spacer { flex: 1; }\n"] }]
165
165
  }], null, { title: [{
166
166
  type: Input
167
167
  }], icon: [{
@@ -1 +1 @@
1
- {"version":3,"file":"ps-confirm-modal.component.js","sourceRoot":"","sources":["../../../src/PredictiveStudio/components/ps-confirm-modal.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { MJButtonDirective } from '@memberjunction/ng-ui-components';\n\n/** Visual variant of the confirm modal — drives the header icon-tile color. */\nexport type PSConfirmVariant = 'info' | 'warn' | 'danger';\n\n/**\n * Small, reusable confirmation modal for Predictive Studio's real mutations (model promote/archive,\n * experiment pause/cancel). Renders a token-styled backdrop + card with a title, a rich message\n * (projected via `<ng-content>`), an optional reason textarea, and a left-aligned primary confirm\n * button followed by Cancel (MJ button-placement convention). The host owns visibility and the\n * confirm/cancel handlers; this component is purely presentational + emits the captured reason.\n *\n * While `busy` is true the confirm button shows a spinner and both buttons disable, giving the\n * in-flight Remote Op a clear, non-dismissable feel.\n */\n@Component({\n standalone: true,\n selector: 'ps-confirm-modal',\n imports: [CommonModule, FormsModule, MJButtonDirective],\n encapsulation: ViewEncapsulation.None,\n styleUrls: ['../predictive-studio.shared.scss'],\n template: `\n <div class=\"ps-modal-backdrop\" data-testid=\"ps-confirm-modal\" (click)=\"onBackdrop()\">\n <div class=\"ps-modal\" (click)=\"$event.stopPropagation()\">\n <div class=\"ps-modal-head\" [class.warn]=\"variant === 'warn'\" [class.danger]=\"variant === 'danger'\">\n <span class=\"ico\"><i [class]=\"icon\"></i></span>\n <h3>{{ title }}</h3>\n </div>\n <div class=\"ps-modal-body\">\n <ng-content></ng-content>\n @if (showReason) {\n <div class=\"ps-field\">\n <label>{{ reasonLabel }}{{ reasonRequired ? ' *' : '' }}</label>\n <textarea\n class=\"ps-input\"\n rows=\"3\"\n data-testid=\"ps-confirm-reason\"\n [placeholder]=\"reasonPlaceholder\"\n [(ngModel)]=\"reason\"\n [disabled]=\"busy\"></textarea>\n </div>\n }\n </div>\n <div class=\"ps-modal-foot\">\n <button\n mjButton\n [variant]=\"variant === 'danger' ? 'danger' : 'primary'\"\n size=\"sm\"\n data-testid=\"ps-confirm-ok\"\n [disabled]=\"busy || (reasonRequired && !reason.trim())\"\n (click)=\"confirmed.emit(reason.trim())\">\n @if (busy) { <i class=\"fa-solid fa-circle-notch fa-spin\"></i> } @else { <i [class]=\"confirmIcon\"></i> }\n {{ confirmLabel }}\n </button>\n <span class=\"ps-spacer\"></span>\n <button mjButton variant=\"secondary\" size=\"sm\" data-testid=\"ps-confirm-cancel\" [disabled]=\"busy\" (click)=\"cancelled.emit()\">\n Cancel\n </button>\n </div>\n </div>\n </div>\n `,\n})\nexport class PSConfirmModalComponent {\n /** Modal heading. */\n @Input() title = 'Confirm';\n /** Header icon-tile Font Awesome class. */\n @Input() icon = 'fa-solid fa-circle-question';\n /** Confirm-button Font Awesome class (shown when not busy). */\n @Input() confirmIcon = 'fa-solid fa-check';\n /** Confirm-button label. */\n @Input() confirmLabel = 'Confirm';\n /** Visual variant (info / warn / danger). */\n @Input() variant: PSConfirmVariant = 'info';\n /** When true, render the reason textarea. */\n @Input() showReason = false;\n /** When true, the confirm button stays disabled until a non-empty reason is entered. */\n @Input() reasonRequired = false;\n /** Reason field label. */\n @Input() reasonLabel = 'Reason';\n /** Reason field placeholder. */\n @Input() reasonPlaceholder = 'Add an optional note…';\n /** In-flight flag — disables the buttons + shows the confirm spinner. */\n @Input() busy = false;\n\n /** The captured reason (two-way via the textarea). */\n public reason = '';\n\n /** Emitted with the trimmed reason when the user confirms. */\n @Output() confirmed = new EventEmitter<string>();\n /** Emitted when the user cancels (button or backdrop). */\n @Output() cancelled = new EventEmitter<void>();\n\n /** Backdrop click cancels — unless an operation is in flight. */\n public onBackdrop(): void {\n if (!this.busy) this.cancelled.emit();\n }\n}\n"]}
1
+ {"version":3,"file":"ps-confirm-modal.component.js","sourceRoot":"","sources":["../../../src/PredictiveStudio/components/ps-confirm-modal.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { Component, EventEmitter, Input, Output, ViewEncapsulation } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { FormsModule } from '@angular/forms';\nimport { MJButtonDirective } from '@memberjunction/ng-ui-components';\n\n/** Visual variant of the confirm modal — drives the header icon-tile color. */\nexport type PSConfirmVariant = 'info' | 'warn' | 'danger';\n\n/**\n * Small, reusable confirmation modal for Predictive Studio's real mutations (model promote/archive,\n * experiment pause/cancel). Renders a token-styled backdrop + card with a title, a rich message\n * (projected via `<ng-content>`), an optional reason textarea, and a left-aligned primary confirm\n * button followed by Cancel (MJ button-placement convention). The host owns visibility and the\n * confirm/cancel handlers; this component is purely presentational + emits the captured reason.\n *\n * While `busy` is true the confirm button shows a spinner and both buttons disable, giving the\n * in-flight Remote Op a clear, non-dismissable feel.\n */\n@Component({\n standalone: true,\n selector: 'ps-confirm-modal',\n imports: [CommonModule, FormsModule, MJButtonDirective],\n encapsulation: ViewEncapsulation.None,\n styleUrls: ['../predictive-studio.shared.css'],\n template: `\n <div class=\"ps-modal-backdrop\" data-testid=\"ps-confirm-modal\" (click)=\"onBackdrop()\">\n <div class=\"ps-modal\" (click)=\"$event.stopPropagation()\">\n <div class=\"ps-modal-head\" [class.warn]=\"variant === 'warn'\" [class.danger]=\"variant === 'danger'\">\n <span class=\"ico\"><i [class]=\"icon\"></i></span>\n <h3>{{ title }}</h3>\n </div>\n <div class=\"ps-modal-body\">\n <ng-content></ng-content>\n @if (showReason) {\n <div class=\"ps-field\">\n <label>{{ reasonLabel }}{{ reasonRequired ? ' *' : '' }}</label>\n <textarea\n class=\"ps-input\"\n rows=\"3\"\n data-testid=\"ps-confirm-reason\"\n [placeholder]=\"reasonPlaceholder\"\n [(ngModel)]=\"reason\"\n [disabled]=\"busy\"></textarea>\n </div>\n }\n </div>\n <div class=\"ps-modal-foot\">\n <button\n mjButton\n [variant]=\"variant === 'danger' ? 'danger' : 'primary'\"\n size=\"sm\"\n data-testid=\"ps-confirm-ok\"\n [disabled]=\"busy || (reasonRequired && !reason.trim())\"\n (click)=\"confirmed.emit(reason.trim())\">\n @if (busy) { <i class=\"fa-solid fa-circle-notch fa-spin\"></i> } @else { <i [class]=\"confirmIcon\"></i> }\n {{ confirmLabel }}\n </button>\n <span class=\"ps-spacer\"></span>\n <button mjButton variant=\"secondary\" size=\"sm\" data-testid=\"ps-confirm-cancel\" [disabled]=\"busy\" (click)=\"cancelled.emit()\">\n Cancel\n </button>\n </div>\n </div>\n </div>\n `,\n})\nexport class PSConfirmModalComponent {\n /** Modal heading. */\n @Input() title = 'Confirm';\n /** Header icon-tile Font Awesome class. */\n @Input() icon = 'fa-solid fa-circle-question';\n /** Confirm-button Font Awesome class (shown when not busy). */\n @Input() confirmIcon = 'fa-solid fa-check';\n /** Confirm-button label. */\n @Input() confirmLabel = 'Confirm';\n /** Visual variant (info / warn / danger). */\n @Input() variant: PSConfirmVariant = 'info';\n /** When true, render the reason textarea. */\n @Input() showReason = false;\n /** When true, the confirm button stays disabled until a non-empty reason is entered. */\n @Input() reasonRequired = false;\n /** Reason field label. */\n @Input() reasonLabel = 'Reason';\n /** Reason field placeholder. */\n @Input() reasonPlaceholder = 'Add an optional note…';\n /** In-flight flag — disables the buttons + shows the confirm spinner. */\n @Input() busy = false;\n\n /** The captured reason (two-way via the textarea). */\n public reason = '';\n\n /** Emitted with the trimmed reason when the user confirms. */\n @Output() confirmed = new EventEmitter<string>();\n /** Emitted when the user cancels (button or backdrop). */\n @Output() cancelled = new EventEmitter<void>();\n\n /** Backdrop click cancels — unless an operation is in flight. */\n public onBackdrop(): void {\n if (!this.busy) this.cancelled.emit();\n }\n}\n"]}
@@ -627,7 +627,7 @@ export class PSExperimentsComponent {
627
627
  i0.ɵɵconditional(!ctx.session ? 1 : 2);
628
628
  i0.ɵɵadvance(2);
629
629
  i0.ɵɵconditional(ctx.pendingAction ? 3 : -1);
630
- } }, dependencies: [CommonModule, i1.NgTemplateOutlet, MJButtonDirective, PSConfirmModalComponent, i1.DecimalPipe], styles: ["/* ============================================================================\n Predictive Studio \u2014 shared panel styles (design-token compliant)\n Maps the mockup design system onto MemberJunction semantic --mj-* tokens.\n Loaded by every PS panel component (styleUrls) under ViewEncapsulation.None,\n so the class names are global within the PS dashboard subtree.\n No hardcoded colors except the intentional categorical node/algorithm palette\n (declared once as --ps-* custom props below) which must stay distinct across\n light/dark to remain readable as diagram accents.\n ============================================================================ */\n\n/* Categorical accents for DAG node types + algorithm chips. These are\n data-visualization colors that must remain distinct regardless of theme\n (allowed exception #3 in the design-token rules). */\n.ps-panel {\n --ps-node-source: #6b7280;\n --ps-node-feature: #2050c8;\n --ps-node-embedding: #7b3ff0;\n --ps-node-target: #1f9d57;\n --ps-node-algorithm: #d98213;\n /* budget-gauge categorical accents (reuse the node palette so light/dark stay distinct) */\n --ps-gauge-cost: var(--ps-node-algorithm);\n --ps-gauge-iterations: var(--ps-node-feature);\n}\n\n.ps-panel {\n display: block;\n height: 100%;\n}\n\n/* ---- generic layout helpers ---- */\n.ps-row { display: flex; gap: 16px; }\n.ps-col { display: flex; flex-direction: column; gap: 16px; }\n.ps-wrap { flex-wrap: wrap; }\n.ps-spacer { flex: 1; }\n.ps-muted { color: var(--mj-text-muted); }\n.ps-small { font-size: 12.5px; }\n.ps-mono { font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; font-size: 12.5px; }\n.ps-section-title {\n font-size: 12px; text-transform: uppercase; letter-spacing: .06em;\n color: var(--mj-text-muted); margin: 4px 0 2px; font-weight: 700;\n}\n\n/* ---- cards ---- */\n.ps-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n box-shadow: var(--mj-shadow-sm);\n overflow: hidden;\n}\n.ps-card-head {\n display: flex; align-items: center; gap: 10px;\n padding: 14px 16px; border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-card-head h3 { font-size: 14.5px; margin: 0; font-weight: 650; color: var(--mj-text-primary); }\n.ps-card-body { padding: 16px; }\n\n/* ---- KPI tiles ---- */\n.ps-kpi {\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-radius: 10px; padding: 16px 18px; box-shadow: var(--mj-shadow-sm);\n}\n.ps-kpi .label { font-size: 12px; color: var(--mj-text-muted); font-weight: 600; }\n.ps-kpi .val { font-size: 26px; font-weight: 720; letter-spacing: -.02em; margin-top: 2px; color: var(--mj-text-primary); }\n\n/* ---- badges ---- */\n.ps-badge {\n display: inline-flex; align-items: center; gap: 5px;\n padding: 2px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 650; line-height: 1.7;\n}\n.ps-badge.gray { background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n.ps-badge.blue { background: var(--mj-status-info-bg); color: var(--mj-status-info-text); }\n.ps-badge.green { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n.ps-badge.amber { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-badge.red { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }\n\n/* recommendation-level chips (the ranking matrix payoff) */\n.ps-lvl {\n display: inline-flex; align-items: center; justify-content: center;\n min-width: 92px; padding: 4px 10px; border-radius: 7px;\n font-weight: 650; font-size: 11.5px; color: var(--mj-text-inverse);\n}\n.ps-lvl-Primary { background: var(--mj-status-success); }\n.ps-lvl-Strong { background: var(--mj-status-info); }\n.ps-lvl-Viable { background: var(--mj-status-warning); }\n.ps-lvl-Weak { background: var(--mj-text-disabled); }\n.ps-lvl-NotRecommended { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n\n/* ---- tags ---- */\n.ps-tag {\n display: inline-block; padding: 2px 8px; border-radius: 6px;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary);\n font-size: 11.5px; font-weight: 600;\n}\n.ps-tag.muted { opacity: .6; }\n.ps-divider { height: 1px; background: var(--mj-border-subtle); margin: 8px 0; }\n\n/* ---- tables ---- */\n.ps-table { width: 100%; border-collapse: collapse; font-size: 13px; }\n.ps-table th {\n text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; padding: 9px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n.ps-table td { padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); color: var(--mj-text-primary); }\n.ps-table tr:hover td { background: var(--mj-bg-surface-hover); }\n.ps-table .num { text-align: right; font-variant-numeric: tabular-nums; }\n\n.ps-rank {\n width: 22px; height: 22px; border-radius: 6px; background: var(--mj-bg-surface-sunken);\n display: inline-grid; place-items: center; font-weight: 700; font-size: 11.5px; color: var(--mj-text-secondary);\n}\n.ps-rank.gold { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n\n/* ---- progress bars ---- */\n.ps-bar { height: 8px; border-radius: 999px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-bar > span { display: block; height: 100%; border-radius: 999px; background: var(--mj-brand-primary); }\n.ps-bar.warn > span { background: var(--mj-status-warning); }\n.ps-bar.run > span { background: var(--mj-brand-primary); }\n\n.ps-fbar { display: grid; grid-template-columns: 150px 1fr 48px; align-items: center; gap: 10px; padding: 5px 0; }\n.ps-fbar .track { height: 14px; border-radius: 5px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-fbar .track > span { display: block; height: 100%; background: var(--mj-brand-primary); }\n.ps-fbar .track > span.warn { background: var(--mj-status-warning); }\n.ps-fbar .name { color: var(--mj-text-secondary); }\n\n/* gauge */\n.ps-gauge {\n position: relative; width: 64px; height: 64px; border-radius: 50%; flex: 0 0 auto;\n background: conic-gradient(var(--ps-gauge-color, var(--mj-brand-primary)) calc(var(--ps-p, 40) * 1%), var(--mj-bg-surface-sunken) 0);\n display: grid; place-items: center;\n}\n.ps-gauge::after { content: ''; position: absolute; inset: 9px; background: var(--mj-bg-surface); border-radius: 50%; }\n.ps-gauge .g-val { position: relative; font-weight: 720; font-size: 14px; color: var(--mj-text-primary); }\n\n/* warning / info callouts */\n.ps-callout { display: flex; gap: 11px; padding: 12px 14px; border-radius: 10px; border: 1px solid; }\n.ps-callout.warn { background: var(--mj-status-warning-bg); border-color: var(--mj-status-warning-border); color: var(--mj-status-warning-text); }\n.ps-callout.info { background: var(--mj-status-info-bg); border-color: var(--mj-status-info-border); color: var(--mj-status-info-text); }\n.ps-callout.success { background: var(--mj-status-success-bg); border-color: var(--mj-status-success-border); color: var(--mj-status-success-text); }\n.ps-callout i { font-size: 16px; margin-top: 1px; }\n\n/* lifecycle stepper */\n.ps-stepper { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }\n.ps-step { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--mj-text-muted); }\n.ps-step .pip {\n width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--mj-border-strong);\n display: grid; place-items: center; font-size: 11px;\n}\n.ps-step.done { color: var(--mj-status-success-text); }\n.ps-step.done .pip { background: var(--mj-status-success); border-color: var(--mj-status-success); color: var(--mj-text-inverse); }\n.ps-step.curr { color: var(--mj-brand-primary); }\n.ps-step.curr .pip { border-color: var(--mj-brand-primary); color: var(--mj-brand-primary); }\n.ps-step .ln { width: 26px; height: 2px; background: var(--mj-border-strong); }\n\n/* ---- DAG flow canvas ---- */\n.ps-flow {\n position: relative;\n background:\n linear-gradient(var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n linear-gradient(90deg, var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default); border-radius: 10px;\n min-height: 620px; overflow: hidden;\n}\n.ps-node {\n position: absolute; width: 190px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong); border-left-width: 3px; border-radius: 10px;\n box-shadow: var(--mj-shadow-sm); cursor: pointer;\n}\n.ps-node .nh {\n display: flex; align-items: center; gap: 8px; padding: 9px 11px;\n border-bottom: 1px solid var(--mj-border-subtle); font-weight: 650; font-size: 12.5px; color: var(--mj-text-primary);\n}\n.ps-node .nh i.tile {\n width: 24px; height: 24px; border-radius: 6px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 12px; flex: 0 0 auto;\n}\n.ps-node .nh .ps-tag { margin-left: auto; }\n.ps-node .nb { padding: 9px 11px; font-size: 12px; color: var(--mj-text-secondary); }\n.ps-node .nb-row { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; }\n.ps-node .nb-row .k { color: var(--mj-text-muted); }\n.ps-node.src { border-left-color: var(--ps-node-source); }\n.ps-node.src .nh i.tile { background: var(--ps-node-source); }\n.ps-node.feat { border-left-color: var(--ps-node-feature); }\n.ps-node.feat .nh i.tile { background: var(--ps-node-feature); }\n.ps-node.emb { border-left-color: var(--ps-node-embedding); }\n.ps-node.emb .nh i.tile { background: var(--ps-node-embedding); }\n.ps-node.target { border-left-color: var(--ps-node-target); }\n.ps-node.target .nh i.tile { background: var(--ps-node-target); }\n.ps-node.algo { border-left-color: var(--ps-node-algorithm); }\n.ps-node.algo .nh i.tile { background: var(--ps-node-algorithm); }\n.ps-node.output { border-left-color: var(--mj-status-success); }\n.ps-node.output .nh i.tile { background: var(--mj-status-success); }\n.ps-node.selected {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n.ps-node .port {\n position: absolute; width: 11px; height: 11px; border-radius: 50%;\n background: var(--mj-bg-surface); border: 2px solid var(--mj-brand-primary);\n top: 50%; transform: translateY(-50%);\n}\n.ps-node .port.in { left: -6px; }\n.ps-node .port.out { right: -6px; }\nsvg.ps-edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }\nsvg.ps-edges path { stroke: color-mix(in srgb, var(--mj-brand-primary) 45%, transparent); stroke-width: 2; fill: none; }\n\n/* palette chips */\n.ps-pchip {\n display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px; border-radius: 8px;\n border: 1px dashed var(--mj-border-strong); background: var(--mj-bg-surface);\n font-size: 12.5px; font-weight: 600; color: var(--mj-text-secondary); cursor: grab;\n}\n.ps-pchip i { font-size: 12px; color: var(--mj-text-inverse); width: 20px; height: 20px; border-radius: 5px; display: grid; place-items: center; }\n.ps-pchip.s i { background: var(--ps-node-source); }\n.ps-pchip.f i { background: var(--ps-node-feature); }\n.ps-pchip.e i { background: var(--ps-node-embedding); }\n.ps-pchip.t i { background: var(--ps-node-target); }\n.ps-pchip.a i { background: var(--ps-node-algorithm); }\n\n/* segmented control */\n.ps-seg { display: inline-flex; border: 1px solid var(--mj-border-default); border-radius: 8px; overflow: hidden; }\n.ps-seg button {\n border: none; background: var(--mj-bg-surface); color: var(--mj-text-secondary);\n padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer;\n border-right: 1px solid var(--mj-border-default);\n}\n.ps-seg button:last-child { border-right: none; }\n.ps-seg button.on { background: var(--mj-brand-primary); color: var(--mj-text-inverse); }\n\n/* field control */\n.ps-field { margin-bottom: 14px; }\n.ps-field > label {\n display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; margin-bottom: 6px;\n}\n.ps-input {\n width: 100%; padding: 8px 10px; border: 1px solid var(--mj-border-default);\n border-radius: 6px; font-size: 13px; background: var(--mj-bg-surface); color: var(--mj-text-primary);\n box-sizing: border-box;\n}\n.ps-input:focus { outline: none; border-color: var(--mj-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent); }\n\n/* loading container */\n.ps-loading { display: flex; align-items: center; justify-content: center; padding: 60px 0; }\n\n/* ---- empty / prompt states ---- */\n.ps-empty {\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n text-align: center; gap: 10px; padding: 56px 24px; color: var(--mj-text-muted);\n}\n.ps-empty .ps-empty-ico {\n width: 60px; height: 60px; border-radius: 16px; display: grid; place-items: center;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-disabled); font-size: 24px;\n}\n.ps-empty h3 { margin: 4px 0 0; font-size: 16px; font-weight: 650; color: var(--mj-text-secondary); }\n.ps-empty p { margin: 0; max-width: 420px; font-size: 13px; line-height: 1.5; }\n\n/* ---- confirm modal ---- */\n.ps-modal-backdrop {\n position: fixed; inset: 0; z-index: 1000;\n background: var(--mj-bg-overlay);\n display: flex; align-items: center; justify-content: center; padding: 24px;\n}\n.ps-modal {\n width: 100%; max-width: 480px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default); border-radius: 14px;\n box-shadow: var(--mj-shadow-lg, var(--mj-shadow-sm)); overflow: hidden;\n}\n.ps-modal-head {\n display: flex; align-items: center; gap: 12px; padding: 18px 20px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-modal-head .ico {\n width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center;\n font-size: 16px; flex: 0 0 auto;\n background: var(--mj-status-info-bg); color: var(--mj-status-info-text);\n}\n.ps-modal-head.warn .ico { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-modal-head.danger .ico { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-modal-head h3 { margin: 0; font-size: 15.5px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-modal-body { padding: 18px 20px; font-size: 13.5px; line-height: 1.55; color: var(--mj-text-secondary); }\n.ps-modal-body .ps-field { margin-top: 14px; margin-bottom: 0; }\n.ps-modal-foot {\n display: flex; align-items: center; gap: 10px; padding: 14px 20px;\n border-top: 1px solid var(--mj-border-subtle); background: var(--mj-bg-surface-card);\n}\n.ps-modal-foot .ps-spacer { flex: 1; }\n", ".ps-experiments { display: flex; flex-direction: column; gap: 16px; }\n\n.ps-experiments .sess-head { display: flex; align-items: center; gap: 12px; }\n.ps-experiments .sess-head h2 { font-size: 18px; color: var(--mj-text-primary); display: flex; align-items: center; gap: 10px; }\n\n/* leaderboard strip */\n.ps-experiments .lead-strip { display: flex; gap: 10px; align-items: center; overflow-x: auto; }\n.ps-experiments .lead-lbl { padding-right: 14px; border-right: 1px solid var(--mj-border-default); flex: 0 0 auto; }\n.ps-experiments .lead-lbl .t { display: flex; align-items: center; gap: 7px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-experiments .lp {\n display: flex; align-items: center; gap: 9px; flex: 0 0 auto; padding: 8px 12px;\n border: 1px solid var(--mj-border-default); border-radius: 9px; background: var(--mj-bg-surface);\n}\n.ps-experiments .lp.best { background: var(--mj-status-warning-bg); border-color: var(--mj-status-warning-border); }\n.ps-experiments .lp.pruned { opacity: .55; }\n.ps-experiments .algo-ic {\n width: 26px; height: 26px; border-radius: 7px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 12px; flex: 0 0 auto;\n}\n.ps-experiments .lp .nm { font-size: 12.5px; font-weight: 600; color: var(--mj-text-primary); }\n.ps-experiments .lp .auc { font-weight: 740; font-variant-numeric: tabular-nums; color: var(--mj-text-primary); }\n.ps-experiments .lp .auc.win { color: var(--mj-status-success-text); }\n\n/* budget rail */\n.ps-experiments .budget-rail { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }\n@media (max-width: 880px) { .ps-experiments .budget-rail { grid-template-columns: 1fr; } }\n.ps-experiments .bud { display: flex; align-items: center; gap: 13px; }\n.ps-experiments .b-val { font-size: 15px; color: var(--mj-text-primary); margin-top: 2px; }\n\n/* kanban */\n.ps-experiments .kanban { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; align-items: start; }\n@media (max-width: 980px) { .ps-experiments .kanban { grid-template-columns: 1fr; } }\n.ps-experiments .kcol { background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default); border-radius: 10px; overflow: hidden; }\n.ps-experiments .kcol-head {\n display: flex; align-items: center; gap: 9px; padding: 12px 14px; border-bottom: 1px solid var(--mj-border-subtle);\n font-weight: 650; color: var(--mj-text-primary);\n}\n.ps-experiments .kcol-head h3 { font-size: 13.5px; margin: 0; flex: 1; }\n.ps-experiments .kcol.run .kcol-head { border-top: 3px solid var(--mj-brand-primary); }\n.ps-experiments .kcol.done .kcol-head { border-top: 3px solid var(--mj-status-success); }\n.ps-experiments .kcol.prune .kcol-head { border-top: 3px solid var(--mj-text-disabled); }\n.ps-experiments .kcol.run .kcol-head i { color: var(--mj-brand-primary); }\n.ps-experiments .kcol.done .kcol-head i { color: var(--mj-status-success); }\n.ps-experiments .kcol.prune .kcol-head i { color: var(--mj-text-muted); }\n.ps-experiments .cnt {\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary);\n border-radius: 999px; padding: 1px 9px; font-size: 12px; font-weight: 700;\n}\n.ps-experiments .kbody { padding: 11px; display: flex; flex-direction: column; gap: 11px; }\n\n.ps-experiments .icard {\n border: 1px solid var(--mj-border-default); border-radius: 9px; padding: 11px;\n background: var(--mj-bg-surface-card); display: flex; flex-direction: column; gap: 9px;\n}\n.ps-experiments .icard.running { border-color: color-mix(in srgb, var(--mj-brand-primary) 40%, var(--mj-border-default)); }\n.ps-experiments .icard.best { border-left: 3px solid var(--mj-status-warning); }\n.ps-experiments .icard.pruned { opacity: .78; }\n.ps-experiments .ic-top { display: flex; align-items: center; gap: 9px; }\n.ps-experiments .ic-top .algo-ic { width: 30px; height: 30px; }\n.ps-experiments .ic-top .nm { font-weight: 600; color: var(--mj-text-primary); font-size: 13px; }\n.ps-experiments .feat-chips { display: flex; gap: 6px; flex-wrap: wrap; }\n.ps-experiments .ic-prog .meta { display: flex; justify-content: space-between; margin-top: 5px; }\n.ps-experiments .ic-score { display: flex; align-items: baseline; gap: 8px; }\n.ps-experiments .ic-score .v { font-size: 21px; font-weight: 720; color: var(--mj-text-primary); font-variant-numeric: tabular-nums; }\n.ps-experiments .ic-score .v.green { color: var(--mj-status-success-text); }\n.ps-experiments .rationale {\n display: flex; gap: 8px; padding-top: 8px; border-top: 1px solid var(--mj-border-subtle); color: var(--mj-text-secondary);\n}\n.ps-experiments .rationale i { color: var(--mj-brand-primary); margin-top: 2px; }\n"], encapsulation: 2 });
630
+ } }, dependencies: [CommonModule, i1.NgTemplateOutlet, MJButtonDirective, PSConfirmModalComponent, i1.DecimalPipe], styles: ["/* ============================================================================\n Predictive Studio \u2014 shared panel styles (design-token compliant)\n Maps the mockup design system onto MemberJunction semantic --mj-* tokens.\n Loaded by every PS panel component (styleUrls) under ViewEncapsulation.None,\n so the class names are global within the PS dashboard subtree.\n No hardcoded colors except the intentional categorical node/algorithm palette\n (declared once as --ps-* custom props below) which must stay distinct across\n light/dark to remain readable as diagram accents.\n ============================================================================ */\n\n/* Categorical accents for DAG node types + algorithm chips. These are\n data-visualization colors that must remain distinct regardless of theme\n (allowed exception #3 in the design-token rules). */\n.ps-panel {\n --ps-node-source: #6b7280;\n --ps-node-feature: #2050c8;\n --ps-node-embedding: #7b3ff0;\n --ps-node-target: #1f9d57;\n --ps-node-algorithm: #d98213;\n /* budget-gauge categorical accents (reuse the node palette so light/dark stay distinct) */\n --ps-gauge-cost: var(--ps-node-algorithm);\n --ps-gauge-iterations: var(--ps-node-feature);\n}\n\n.ps-panel {\n display: block;\n height: 100%;\n}\n\n/* ---- generic layout helpers ---- */\n.ps-row { display: flex; gap: 16px; }\n.ps-col { display: flex; flex-direction: column; gap: 16px; }\n.ps-wrap { flex-wrap: wrap; }\n.ps-spacer { flex: 1; }\n.ps-muted { color: var(--mj-text-muted); }\n.ps-small { font-size: 12.5px; }\n.ps-mono { font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; font-size: 12.5px; }\n.ps-section-title {\n font-size: 12px; text-transform: uppercase; letter-spacing: .06em;\n color: var(--mj-text-muted); margin: 4px 0 2px; font-weight: 700;\n}\n\n/* ---- cards ---- */\n.ps-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n}\n.ps-card-head {\n display: flex; align-items: center; gap: 10px;\n padding: 14px 16px; border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-card-head h3 { font-size: 14.5px; margin: 0; font-weight: 650; color: var(--mj-text-primary); }\n.ps-card-body { padding: 16px; }\n\n/* ---- KPI tiles ---- */\n.ps-kpi {\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-radius: 8px; padding: 16px 18px;\n}\n.ps-kpi .label { font-size: 12px; color: var(--mj-text-muted); font-weight: 600; }\n.ps-kpi .val { font-size: 26px; font-weight: 720; letter-spacing: -.02em; margin-top: 2px; color: var(--mj-text-primary); }\n\n/* ---- badges ---- */\n.ps-badge {\n display: inline-flex; align-items: center; gap: 5px;\n padding: 2px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 650; line-height: 1.7;\n}\n.ps-badge.gray { background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n.ps-badge.blue { background: var(--mj-status-info-bg); color: var(--mj-status-info-text); }\n.ps-badge.green { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n.ps-badge.amber { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-badge.red { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }\n\n/* recommendation-level chips (the ranking matrix payoff) */\n.ps-lvl {\n display: inline-flex; align-items: center; justify-content: center;\n min-width: 92px; padding: 4px 10px; border-radius: 7px;\n font-weight: 650; font-size: 11.5px; color: var(--mj-text-inverse);\n}\n.ps-lvl-Primary { background: var(--mj-status-success); }\n.ps-lvl-Strong { background: var(--mj-status-info); }\n.ps-lvl-Viable { background: var(--mj-status-warning); }\n.ps-lvl-Weak { background: var(--mj-text-disabled); }\n.ps-lvl-NotRecommended { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n\n/* ---- tags ---- */\n.ps-tag {\n display: inline-block; padding: 2px 8px; border-radius: 6px;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary);\n font-size: 11.5px; font-weight: 600;\n}\n.ps-tag.muted { opacity: .6; }\n.ps-divider { height: 1px; background: var(--mj-border-subtle); margin: 8px 0; }\n\n/* ---- tables ---- */\n.ps-table { width: 100%; border-collapse: collapse; font-size: 13px; }\n.ps-table th {\n text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; padding: 9px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n.ps-table td { padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); color: var(--mj-text-primary); }\n.ps-table tr:hover td { background: var(--mj-bg-surface-hover); }\n.ps-table .num { text-align: right; font-variant-numeric: tabular-nums; }\n\n.ps-rank {\n width: 22px; height: 22px; border-radius: 6px; background: var(--mj-bg-surface-sunken);\n display: inline-grid; place-items: center; font-weight: 700; font-size: 11.5px; color: var(--mj-text-secondary);\n}\n.ps-rank.gold { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n\n/* ---- progress bars ---- */\n.ps-bar { height: 8px; border-radius: 999px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-bar > span { display: block; height: 100%; border-radius: 999px; background: var(--mj-brand-primary); }\n.ps-bar.warn > span { background: var(--mj-status-warning); }\n.ps-bar.run > span { background: var(--mj-brand-primary); }\n\n.ps-fbar { display: grid; grid-template-columns: minmax(150px, 1.3fr) 2fr 56px; align-items: center; gap: 14px; padding: 8px 0; }\n.ps-fbar + .ps-fbar { border-top: 1px solid var(--mj-border-subtle); }\n.ps-fbar .track { height: 10px; border-radius: var(--mj-radius-full); background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-fbar .track > span { display: block; height: 100%; border-radius: var(--mj-radius-full); background: var(--mj-brand-primary); }\n.ps-fbar .track > span.warn { background: var(--mj-status-warning); }\n.ps-fbar .name { color: var(--mj-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ps-fbar > .ps-mono { text-align: right; color: var(--mj-text-secondary); font-variant-numeric: tabular-nums; }\n\n/* gauge */\n.ps-gauge {\n position: relative; width: 64px; height: 64px; border-radius: 50%; flex: 0 0 auto;\n background: conic-gradient(var(--ps-gauge-color, var(--mj-brand-primary)) calc(var(--ps-p, 40) * 1%), var(--mj-bg-surface-sunken) 0);\n display: grid; place-items: center;\n}\n.ps-gauge::after { content: ''; position: absolute; inset: 9px; background: var(--mj-bg-surface); border-radius: 50%; }\n.ps-gauge .g-val { position: relative; font-weight: 720; font-size: 14px; color: var(--mj-text-primary); }\n\n/* warning / info callouts */\n.ps-callout { display: flex; gap: 11px; padding: 12px 14px; border-radius: 10px; border: 1px solid; }\n.ps-callout.warn { background: var(--mj-status-warning-bg); border-color: var(--mj-status-warning-border); color: var(--mj-status-warning-text); }\n.ps-callout.info { background: var(--mj-status-info-bg); border-color: var(--mj-status-info-border); color: var(--mj-status-info-text); }\n.ps-callout.success { background: var(--mj-status-success-bg); border-color: var(--mj-status-success-border); color: var(--mj-status-success-text); }\n.ps-callout i { font-size: 16px; margin-top: 1px; }\n\n/* lifecycle stepper */\n.ps-stepper { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }\n.ps-step { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--mj-text-muted); }\n.ps-step .pip {\n width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--mj-border-strong);\n display: grid; place-items: center; font-size: 11px;\n}\n.ps-step.done { color: var(--mj-status-success-text); }\n.ps-step.done .pip { background: var(--mj-status-success); border-color: var(--mj-status-success); color: var(--mj-text-inverse); }\n.ps-step.curr { color: var(--mj-brand-primary); }\n.ps-step.curr .pip { border-color: var(--mj-brand-primary); color: var(--mj-brand-primary); }\n.ps-step .ln { width: 26px; height: 2px; background: var(--mj-border-strong); }\n\n/* ---- DAG flow canvas ---- */\n.ps-flow {\n position: relative;\n background:\n linear-gradient(var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n linear-gradient(90deg, var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default); border-radius: 10px;\n min-height: 620px; overflow: hidden;\n}\n.ps-node {\n position: absolute; width: 190px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong); border-left-width: 3px; border-radius: 8px;\n cursor: pointer;\n}\n.ps-node .nh {\n display: flex; align-items: center; gap: 8px; padding: 9px 11px;\n border-bottom: 1px solid var(--mj-border-subtle); font-weight: 650; font-size: 12.5px; color: var(--mj-text-primary);\n}\n.ps-node .nh i.tile {\n width: 24px; height: 24px; border-radius: 6px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 12px; flex: 0 0 auto;\n}\n.ps-node .nh .ps-tag { margin-left: auto; }\n.ps-node .nb { padding: 9px 11px; font-size: 12px; color: var(--mj-text-secondary); }\n.ps-node .nb-row { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; }\n.ps-node .nb-row .k { color: var(--mj-text-muted); }\n.ps-node.src { border-left-color: var(--ps-node-source); }\n.ps-node.src .nh i.tile { background: var(--ps-node-source); }\n.ps-node.feat { border-left-color: var(--ps-node-feature); }\n.ps-node.feat .nh i.tile { background: var(--ps-node-feature); }\n.ps-node.emb { border-left-color: var(--ps-node-embedding); }\n.ps-node.emb .nh i.tile { background: var(--ps-node-embedding); }\n.ps-node.target { border-left-color: var(--ps-node-target); }\n.ps-node.target .nh i.tile { background: var(--ps-node-target); }\n.ps-node.algo { border-left-color: var(--ps-node-algorithm); }\n.ps-node.algo .nh i.tile { background: var(--ps-node-algorithm); }\n.ps-node.output { border-left-color: var(--mj-status-success); }\n.ps-node.output .nh i.tile { background: var(--mj-status-success); }\n.ps-node.selected {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n.ps-node .port {\n position: absolute; width: 11px; height: 11px; border-radius: 50%;\n background: var(--mj-bg-surface); border: 2px solid var(--mj-brand-primary);\n top: 50%; transform: translateY(-50%);\n}\n.ps-node .port.in { left: -6px; }\n.ps-node .port.out { right: -6px; }\nsvg.ps-edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }\nsvg.ps-edges path { stroke: color-mix(in srgb, var(--mj-brand-primary) 45%, transparent); stroke-width: 2; fill: none; }\n\n/* palette chips */\n.ps-pchip {\n display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px; border-radius: 8px;\n border: 1px dashed var(--mj-border-strong); background: var(--mj-bg-surface);\n font-size: 12.5px; font-weight: 600; color: var(--mj-text-secondary); cursor: grab;\n}\n.ps-pchip i { font-size: 12px; color: var(--mj-text-inverse); width: 20px; height: 20px; border-radius: 5px; display: grid; place-items: center; }\n.ps-pchip.s i { background: var(--ps-node-source); }\n.ps-pchip.f i { background: var(--ps-node-feature); }\n.ps-pchip.e i { background: var(--ps-node-embedding); }\n.ps-pchip.t i { background: var(--ps-node-target); }\n.ps-pchip.a i { background: var(--ps-node-algorithm); }\n\n/* segmented control */\n.ps-seg { display: inline-flex; border: 1px solid var(--mj-border-default); border-radius: 8px; overflow: hidden; }\n.ps-seg button {\n border: none; background: var(--mj-bg-surface); color: var(--mj-text-secondary);\n padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer;\n border-right: 1px solid var(--mj-border-default);\n}\n.ps-seg button:last-child { border-right: none; }\n.ps-seg button.on { background: var(--mj-brand-primary); color: var(--mj-text-inverse); }\n\n/* field control */\n.ps-field { margin-bottom: 14px; }\n.ps-field > label {\n display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; margin-bottom: 6px;\n}\n.ps-input {\n width: 100%; padding: 8px 10px; border: 1px solid var(--mj-border-default);\n border-radius: 6px; font-size: 13px; background: var(--mj-bg-surface); color: var(--mj-text-primary);\n box-sizing: border-box;\n}\n.ps-input:focus { outline: none; border-color: var(--mj-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent); }\n\n/* loading container */\n.ps-loading { display: flex; align-items: center; justify-content: center; padding: 60px 0; }\n\n/* ---- empty / prompt states ---- */\n.ps-empty {\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n text-align: center; gap: 10px; padding: 56px 24px; color: var(--mj-text-muted);\n}\n.ps-empty .ps-empty-ico {\n width: 60px; height: 60px; border-radius: 16px; display: grid; place-items: center;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-disabled); font-size: 24px;\n}\n.ps-empty h3 { margin: 4px 0 0; font-size: 16px; font-weight: 650; color: var(--mj-text-secondary); }\n.ps-empty p { margin: 0; max-width: 420px; font-size: 13px; line-height: 1.5; }\n\n/* ---- confirm modal ---- */\n.ps-modal-backdrop {\n position: fixed; inset: 0; z-index: 1000;\n background: var(--mj-bg-overlay);\n display: flex; align-items: center; justify-content: center; padding: 24px;\n}\n.ps-modal {\n width: 100%; max-width: 480px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default); border-radius: 14px;\n box-shadow: var(--mj-shadow-lg, var(--mj-shadow-sm)); overflow: hidden;\n}\n.ps-modal-head {\n display: flex; align-items: center; gap: 12px; padding: 18px 20px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-modal-head .ico {\n width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center;\n font-size: 16px; flex: 0 0 auto;\n background: var(--mj-status-info-bg); color: var(--mj-status-info-text);\n}\n.ps-modal-head.warn .ico { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-modal-head.danger .ico { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-modal-head h3 { margin: 0; font-size: 15.5px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-modal-body { padding: 18px 20px; font-size: 13.5px; line-height: 1.55; color: var(--mj-text-secondary); }\n.ps-modal-body .ps-field { margin-top: 14px; margin-bottom: 0; }\n.ps-modal-foot {\n display: flex; align-items: center; gap: 10px; padding: 14px 20px;\n border-top: 1px solid var(--mj-border-subtle); background: var(--mj-bg-surface-card);\n}\n.ps-modal-foot .ps-spacer { flex: 1; }\n", ".ps-experiments { display: flex; flex-direction: column; gap: 16px; }\n\n.ps-experiments .sess-head { display: flex; align-items: center; gap: 12px; }\n.ps-experiments .sess-head h2 { font-size: 18px; color: var(--mj-text-primary); display: flex; align-items: center; gap: 10px; }\n\n/* leaderboard strip */\n.ps-experiments .lead-strip { display: flex; gap: 10px; align-items: center; overflow-x: auto; }\n.ps-experiments .lead-lbl { padding-right: 14px; border-right: 1px solid var(--mj-border-default); flex: 0 0 auto; }\n.ps-experiments .lead-lbl .t { display: flex; align-items: center; gap: 7px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-experiments .lp {\n display: flex; align-items: center; gap: 9px; flex: 0 0 auto; padding: 8px 12px;\n border: 1px solid var(--mj-border-default); border-radius: 9px; background: var(--mj-bg-surface);\n}\n.ps-experiments .lp.best { background: var(--mj-status-warning-bg); border-color: var(--mj-status-warning-border); }\n.ps-experiments .lp.pruned { opacity: .55; }\n.ps-experiments .algo-ic {\n width: 26px; height: 26px; border-radius: 7px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 12px; flex: 0 0 auto;\n}\n.ps-experiments .lp .nm { font-size: 12.5px; font-weight: 600; color: var(--mj-text-primary); }\n.ps-experiments .lp .auc { font-weight: 740; font-variant-numeric: tabular-nums; color: var(--mj-text-primary); }\n.ps-experiments .lp .auc.win { color: var(--mj-status-success-text); }\n\n/* budget rail */\n.ps-experiments .budget-rail { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }\n@media (max-width: 880px) { .ps-experiments .budget-rail { grid-template-columns: 1fr; } }\n.ps-experiments .bud { display: flex; align-items: center; gap: 13px; }\n.ps-experiments .b-val { font-size: 15px; color: var(--mj-text-primary); margin-top: 2px; }\n\n/* kanban */\n.ps-experiments .kanban { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; align-items: start; }\n@media (max-width: 980px) { .ps-experiments .kanban { grid-template-columns: 1fr; } }\n.ps-experiments .kcol { background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default); border-radius: 10px; overflow: hidden; }\n.ps-experiments .kcol-head {\n display: flex; align-items: center; gap: 9px; padding: 12px 14px; border-bottom: 1px solid var(--mj-border-subtle);\n font-weight: 650; color: var(--mj-text-primary);\n}\n.ps-experiments .kcol-head h3 { font-size: 13.5px; margin: 0; flex: 1; }\n.ps-experiments .kcol.run .kcol-head { border-top: 3px solid var(--mj-brand-primary); }\n.ps-experiments .kcol.done .kcol-head { border-top: 3px solid var(--mj-status-success); }\n.ps-experiments .kcol.prune .kcol-head { border-top: 3px solid var(--mj-text-disabled); }\n.ps-experiments .kcol.run .kcol-head i { color: var(--mj-brand-primary); }\n.ps-experiments .kcol.done .kcol-head i { color: var(--mj-status-success); }\n.ps-experiments .kcol.prune .kcol-head i { color: var(--mj-text-muted); }\n.ps-experiments .cnt {\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary);\n border-radius: 999px; padding: 1px 9px; font-size: 12px; font-weight: 700;\n}\n.ps-experiments .kbody { padding: 11px; display: flex; flex-direction: column; gap: 11px; }\n\n.ps-experiments .icard {\n border: 1px solid var(--mj-border-default); border-radius: 9px; padding: 11px;\n background: var(--mj-bg-surface-card); display: flex; flex-direction: column; gap: 9px;\n}\n.ps-experiments .icard.running { border-color: color-mix(in srgb, var(--mj-brand-primary) 40%, var(--mj-border-default)); }\n.ps-experiments .icard.best { border-left: 3px solid var(--mj-status-warning); }\n.ps-experiments .icard.pruned { opacity: .78; }\n.ps-experiments .ic-top { display: flex; align-items: center; gap: 9px; }\n.ps-experiments .ic-top .algo-ic { width: 30px; height: 30px; }\n.ps-experiments .ic-top .nm { font-weight: 600; color: var(--mj-text-primary); font-size: 13px; }\n.ps-experiments .feat-chips { display: flex; gap: 6px; flex-wrap: wrap; }\n.ps-experiments .ic-prog .meta { display: flex; justify-content: space-between; margin-top: 5px; }\n.ps-experiments .ic-score { display: flex; align-items: baseline; gap: 8px; }\n.ps-experiments .ic-score .v { font-size: 21px; font-weight: 720; color: var(--mj-text-primary); font-variant-numeric: tabular-nums; }\n.ps-experiments .ic-score .v.green { color: var(--mj-status-success-text); }\n.ps-experiments .rationale {\n display: flex; gap: 8px; padding-top: 8px; border-top: 1px solid var(--mj-border-subtle); color: var(--mj-text-secondary);\n}\n.ps-experiments .rationale i { color: var(--mj-brand-primary); margin-top: 2px; }\n"], encapsulation: 2 });
631
631
  }
632
632
  (() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PSExperimentsComponent, [{
633
633
  type: Component,
@@ -772,7 +772,7 @@ export class PSExperimentsComponent {
772
772
  </ps-confirm-modal>
773
773
  }
774
774
  </div>
775
- `, styles: ["/* ============================================================================\n Predictive Studio \u2014 shared panel styles (design-token compliant)\n Maps the mockup design system onto MemberJunction semantic --mj-* tokens.\n Loaded by every PS panel component (styleUrls) under ViewEncapsulation.None,\n so the class names are global within the PS dashboard subtree.\n No hardcoded colors except the intentional categorical node/algorithm palette\n (declared once as --ps-* custom props below) which must stay distinct across\n light/dark to remain readable as diagram accents.\n ============================================================================ */\n\n/* Categorical accents for DAG node types + algorithm chips. These are\n data-visualization colors that must remain distinct regardless of theme\n (allowed exception #3 in the design-token rules). */\n.ps-panel {\n --ps-node-source: #6b7280;\n --ps-node-feature: #2050c8;\n --ps-node-embedding: #7b3ff0;\n --ps-node-target: #1f9d57;\n --ps-node-algorithm: #d98213;\n /* budget-gauge categorical accents (reuse the node palette so light/dark stay distinct) */\n --ps-gauge-cost: var(--ps-node-algorithm);\n --ps-gauge-iterations: var(--ps-node-feature);\n}\n\n.ps-panel {\n display: block;\n height: 100%;\n}\n\n/* ---- generic layout helpers ---- */\n.ps-row { display: flex; gap: 16px; }\n.ps-col { display: flex; flex-direction: column; gap: 16px; }\n.ps-wrap { flex-wrap: wrap; }\n.ps-spacer { flex: 1; }\n.ps-muted { color: var(--mj-text-muted); }\n.ps-small { font-size: 12.5px; }\n.ps-mono { font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; font-size: 12.5px; }\n.ps-section-title {\n font-size: 12px; text-transform: uppercase; letter-spacing: .06em;\n color: var(--mj-text-muted); margin: 4px 0 2px; font-weight: 700;\n}\n\n/* ---- cards ---- */\n.ps-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n box-shadow: var(--mj-shadow-sm);\n overflow: hidden;\n}\n.ps-card-head {\n display: flex; align-items: center; gap: 10px;\n padding: 14px 16px; border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-card-head h3 { font-size: 14.5px; margin: 0; font-weight: 650; color: var(--mj-text-primary); }\n.ps-card-body { padding: 16px; }\n\n/* ---- KPI tiles ---- */\n.ps-kpi {\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-radius: 10px; padding: 16px 18px; box-shadow: var(--mj-shadow-sm);\n}\n.ps-kpi .label { font-size: 12px; color: var(--mj-text-muted); font-weight: 600; }\n.ps-kpi .val { font-size: 26px; font-weight: 720; letter-spacing: -.02em; margin-top: 2px; color: var(--mj-text-primary); }\n\n/* ---- badges ---- */\n.ps-badge {\n display: inline-flex; align-items: center; gap: 5px;\n padding: 2px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 650; line-height: 1.7;\n}\n.ps-badge.gray { background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n.ps-badge.blue { background: var(--mj-status-info-bg); color: var(--mj-status-info-text); }\n.ps-badge.green { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n.ps-badge.amber { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-badge.red { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }\n\n/* recommendation-level chips (the ranking matrix payoff) */\n.ps-lvl {\n display: inline-flex; align-items: center; justify-content: center;\n min-width: 92px; padding: 4px 10px; border-radius: 7px;\n font-weight: 650; font-size: 11.5px; color: var(--mj-text-inverse);\n}\n.ps-lvl-Primary { background: var(--mj-status-success); }\n.ps-lvl-Strong { background: var(--mj-status-info); }\n.ps-lvl-Viable { background: var(--mj-status-warning); }\n.ps-lvl-Weak { background: var(--mj-text-disabled); }\n.ps-lvl-NotRecommended { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n\n/* ---- tags ---- */\n.ps-tag {\n display: inline-block; padding: 2px 8px; border-radius: 6px;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary);\n font-size: 11.5px; font-weight: 600;\n}\n.ps-tag.muted { opacity: .6; }\n.ps-divider { height: 1px; background: var(--mj-border-subtle); margin: 8px 0; }\n\n/* ---- tables ---- */\n.ps-table { width: 100%; border-collapse: collapse; font-size: 13px; }\n.ps-table th {\n text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; padding: 9px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n.ps-table td { padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); color: var(--mj-text-primary); }\n.ps-table tr:hover td { background: var(--mj-bg-surface-hover); }\n.ps-table .num { text-align: right; font-variant-numeric: tabular-nums; }\n\n.ps-rank {\n width: 22px; height: 22px; border-radius: 6px; background: var(--mj-bg-surface-sunken);\n display: inline-grid; place-items: center; font-weight: 700; font-size: 11.5px; color: var(--mj-text-secondary);\n}\n.ps-rank.gold { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n\n/* ---- progress bars ---- */\n.ps-bar { height: 8px; border-radius: 999px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-bar > span { display: block; height: 100%; border-radius: 999px; background: var(--mj-brand-primary); }\n.ps-bar.warn > span { background: var(--mj-status-warning); }\n.ps-bar.run > span { background: var(--mj-brand-primary); }\n\n.ps-fbar { display: grid; grid-template-columns: 150px 1fr 48px; align-items: center; gap: 10px; padding: 5px 0; }\n.ps-fbar .track { height: 14px; border-radius: 5px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-fbar .track > span { display: block; height: 100%; background: var(--mj-brand-primary); }\n.ps-fbar .track > span.warn { background: var(--mj-status-warning); }\n.ps-fbar .name { color: var(--mj-text-secondary); }\n\n/* gauge */\n.ps-gauge {\n position: relative; width: 64px; height: 64px; border-radius: 50%; flex: 0 0 auto;\n background: conic-gradient(var(--ps-gauge-color, var(--mj-brand-primary)) calc(var(--ps-p, 40) * 1%), var(--mj-bg-surface-sunken) 0);\n display: grid; place-items: center;\n}\n.ps-gauge::after { content: ''; position: absolute; inset: 9px; background: var(--mj-bg-surface); border-radius: 50%; }\n.ps-gauge .g-val { position: relative; font-weight: 720; font-size: 14px; color: var(--mj-text-primary); }\n\n/* warning / info callouts */\n.ps-callout { display: flex; gap: 11px; padding: 12px 14px; border-radius: 10px; border: 1px solid; }\n.ps-callout.warn { background: var(--mj-status-warning-bg); border-color: var(--mj-status-warning-border); color: var(--mj-status-warning-text); }\n.ps-callout.info { background: var(--mj-status-info-bg); border-color: var(--mj-status-info-border); color: var(--mj-status-info-text); }\n.ps-callout.success { background: var(--mj-status-success-bg); border-color: var(--mj-status-success-border); color: var(--mj-status-success-text); }\n.ps-callout i { font-size: 16px; margin-top: 1px; }\n\n/* lifecycle stepper */\n.ps-stepper { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }\n.ps-step { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--mj-text-muted); }\n.ps-step .pip {\n width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--mj-border-strong);\n display: grid; place-items: center; font-size: 11px;\n}\n.ps-step.done { color: var(--mj-status-success-text); }\n.ps-step.done .pip { background: var(--mj-status-success); border-color: var(--mj-status-success); color: var(--mj-text-inverse); }\n.ps-step.curr { color: var(--mj-brand-primary); }\n.ps-step.curr .pip { border-color: var(--mj-brand-primary); color: var(--mj-brand-primary); }\n.ps-step .ln { width: 26px; height: 2px; background: var(--mj-border-strong); }\n\n/* ---- DAG flow canvas ---- */\n.ps-flow {\n position: relative;\n background:\n linear-gradient(var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n linear-gradient(90deg, var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default); border-radius: 10px;\n min-height: 620px; overflow: hidden;\n}\n.ps-node {\n position: absolute; width: 190px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong); border-left-width: 3px; border-radius: 10px;\n box-shadow: var(--mj-shadow-sm); cursor: pointer;\n}\n.ps-node .nh {\n display: flex; align-items: center; gap: 8px; padding: 9px 11px;\n border-bottom: 1px solid var(--mj-border-subtle); font-weight: 650; font-size: 12.5px; color: var(--mj-text-primary);\n}\n.ps-node .nh i.tile {\n width: 24px; height: 24px; border-radius: 6px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 12px; flex: 0 0 auto;\n}\n.ps-node .nh .ps-tag { margin-left: auto; }\n.ps-node .nb { padding: 9px 11px; font-size: 12px; color: var(--mj-text-secondary); }\n.ps-node .nb-row { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; }\n.ps-node .nb-row .k { color: var(--mj-text-muted); }\n.ps-node.src { border-left-color: var(--ps-node-source); }\n.ps-node.src .nh i.tile { background: var(--ps-node-source); }\n.ps-node.feat { border-left-color: var(--ps-node-feature); }\n.ps-node.feat .nh i.tile { background: var(--ps-node-feature); }\n.ps-node.emb { border-left-color: var(--ps-node-embedding); }\n.ps-node.emb .nh i.tile { background: var(--ps-node-embedding); }\n.ps-node.target { border-left-color: var(--ps-node-target); }\n.ps-node.target .nh i.tile { background: var(--ps-node-target); }\n.ps-node.algo { border-left-color: var(--ps-node-algorithm); }\n.ps-node.algo .nh i.tile { background: var(--ps-node-algorithm); }\n.ps-node.output { border-left-color: var(--mj-status-success); }\n.ps-node.output .nh i.tile { background: var(--mj-status-success); }\n.ps-node.selected {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n.ps-node .port {\n position: absolute; width: 11px; height: 11px; border-radius: 50%;\n background: var(--mj-bg-surface); border: 2px solid var(--mj-brand-primary);\n top: 50%; transform: translateY(-50%);\n}\n.ps-node .port.in { left: -6px; }\n.ps-node .port.out { right: -6px; }\nsvg.ps-edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }\nsvg.ps-edges path { stroke: color-mix(in srgb, var(--mj-brand-primary) 45%, transparent); stroke-width: 2; fill: none; }\n\n/* palette chips */\n.ps-pchip {\n display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px; border-radius: 8px;\n border: 1px dashed var(--mj-border-strong); background: var(--mj-bg-surface);\n font-size: 12.5px; font-weight: 600; color: var(--mj-text-secondary); cursor: grab;\n}\n.ps-pchip i { font-size: 12px; color: var(--mj-text-inverse); width: 20px; height: 20px; border-radius: 5px; display: grid; place-items: center; }\n.ps-pchip.s i { background: var(--ps-node-source); }\n.ps-pchip.f i { background: var(--ps-node-feature); }\n.ps-pchip.e i { background: var(--ps-node-embedding); }\n.ps-pchip.t i { background: var(--ps-node-target); }\n.ps-pchip.a i { background: var(--ps-node-algorithm); }\n\n/* segmented control */\n.ps-seg { display: inline-flex; border: 1px solid var(--mj-border-default); border-radius: 8px; overflow: hidden; }\n.ps-seg button {\n border: none; background: var(--mj-bg-surface); color: var(--mj-text-secondary);\n padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer;\n border-right: 1px solid var(--mj-border-default);\n}\n.ps-seg button:last-child { border-right: none; }\n.ps-seg button.on { background: var(--mj-brand-primary); color: var(--mj-text-inverse); }\n\n/* field control */\n.ps-field { margin-bottom: 14px; }\n.ps-field > label {\n display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; margin-bottom: 6px;\n}\n.ps-input {\n width: 100%; padding: 8px 10px; border: 1px solid var(--mj-border-default);\n border-radius: 6px; font-size: 13px; background: var(--mj-bg-surface); color: var(--mj-text-primary);\n box-sizing: border-box;\n}\n.ps-input:focus { outline: none; border-color: var(--mj-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent); }\n\n/* loading container */\n.ps-loading { display: flex; align-items: center; justify-content: center; padding: 60px 0; }\n\n/* ---- empty / prompt states ---- */\n.ps-empty {\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n text-align: center; gap: 10px; padding: 56px 24px; color: var(--mj-text-muted);\n}\n.ps-empty .ps-empty-ico {\n width: 60px; height: 60px; border-radius: 16px; display: grid; place-items: center;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-disabled); font-size: 24px;\n}\n.ps-empty h3 { margin: 4px 0 0; font-size: 16px; font-weight: 650; color: var(--mj-text-secondary); }\n.ps-empty p { margin: 0; max-width: 420px; font-size: 13px; line-height: 1.5; }\n\n/* ---- confirm modal ---- */\n.ps-modal-backdrop {\n position: fixed; inset: 0; z-index: 1000;\n background: var(--mj-bg-overlay);\n display: flex; align-items: center; justify-content: center; padding: 24px;\n}\n.ps-modal {\n width: 100%; max-width: 480px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default); border-radius: 14px;\n box-shadow: var(--mj-shadow-lg, var(--mj-shadow-sm)); overflow: hidden;\n}\n.ps-modal-head {\n display: flex; align-items: center; gap: 12px; padding: 18px 20px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-modal-head .ico {\n width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center;\n font-size: 16px; flex: 0 0 auto;\n background: var(--mj-status-info-bg); color: var(--mj-status-info-text);\n}\n.ps-modal-head.warn .ico { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-modal-head.danger .ico { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-modal-head h3 { margin: 0; font-size: 15.5px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-modal-body { padding: 18px 20px; font-size: 13.5px; line-height: 1.55; color: var(--mj-text-secondary); }\n.ps-modal-body .ps-field { margin-top: 14px; margin-bottom: 0; }\n.ps-modal-foot {\n display: flex; align-items: center; gap: 10px; padding: 14px 20px;\n border-top: 1px solid var(--mj-border-subtle); background: var(--mj-bg-surface-card);\n}\n.ps-modal-foot .ps-spacer { flex: 1; }\n", ".ps-experiments { display: flex; flex-direction: column; gap: 16px; }\n\n.ps-experiments .sess-head { display: flex; align-items: center; gap: 12px; }\n.ps-experiments .sess-head h2 { font-size: 18px; color: var(--mj-text-primary); display: flex; align-items: center; gap: 10px; }\n\n/* leaderboard strip */\n.ps-experiments .lead-strip { display: flex; gap: 10px; align-items: center; overflow-x: auto; }\n.ps-experiments .lead-lbl { padding-right: 14px; border-right: 1px solid var(--mj-border-default); flex: 0 0 auto; }\n.ps-experiments .lead-lbl .t { display: flex; align-items: center; gap: 7px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-experiments .lp {\n display: flex; align-items: center; gap: 9px; flex: 0 0 auto; padding: 8px 12px;\n border: 1px solid var(--mj-border-default); border-radius: 9px; background: var(--mj-bg-surface);\n}\n.ps-experiments .lp.best { background: var(--mj-status-warning-bg); border-color: var(--mj-status-warning-border); }\n.ps-experiments .lp.pruned { opacity: .55; }\n.ps-experiments .algo-ic {\n width: 26px; height: 26px; border-radius: 7px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 12px; flex: 0 0 auto;\n}\n.ps-experiments .lp .nm { font-size: 12.5px; font-weight: 600; color: var(--mj-text-primary); }\n.ps-experiments .lp .auc { font-weight: 740; font-variant-numeric: tabular-nums; color: var(--mj-text-primary); }\n.ps-experiments .lp .auc.win { color: var(--mj-status-success-text); }\n\n/* budget rail */\n.ps-experiments .budget-rail { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }\n@media (max-width: 880px) { .ps-experiments .budget-rail { grid-template-columns: 1fr; } }\n.ps-experiments .bud { display: flex; align-items: center; gap: 13px; }\n.ps-experiments .b-val { font-size: 15px; color: var(--mj-text-primary); margin-top: 2px; }\n\n/* kanban */\n.ps-experiments .kanban { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; align-items: start; }\n@media (max-width: 980px) { .ps-experiments .kanban { grid-template-columns: 1fr; } }\n.ps-experiments .kcol { background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default); border-radius: 10px; overflow: hidden; }\n.ps-experiments .kcol-head {\n display: flex; align-items: center; gap: 9px; padding: 12px 14px; border-bottom: 1px solid var(--mj-border-subtle);\n font-weight: 650; color: var(--mj-text-primary);\n}\n.ps-experiments .kcol-head h3 { font-size: 13.5px; margin: 0; flex: 1; }\n.ps-experiments .kcol.run .kcol-head { border-top: 3px solid var(--mj-brand-primary); }\n.ps-experiments .kcol.done .kcol-head { border-top: 3px solid var(--mj-status-success); }\n.ps-experiments .kcol.prune .kcol-head { border-top: 3px solid var(--mj-text-disabled); }\n.ps-experiments .kcol.run .kcol-head i { color: var(--mj-brand-primary); }\n.ps-experiments .kcol.done .kcol-head i { color: var(--mj-status-success); }\n.ps-experiments .kcol.prune .kcol-head i { color: var(--mj-text-muted); }\n.ps-experiments .cnt {\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary);\n border-radius: 999px; padding: 1px 9px; font-size: 12px; font-weight: 700;\n}\n.ps-experiments .kbody { padding: 11px; display: flex; flex-direction: column; gap: 11px; }\n\n.ps-experiments .icard {\n border: 1px solid var(--mj-border-default); border-radius: 9px; padding: 11px;\n background: var(--mj-bg-surface-card); display: flex; flex-direction: column; gap: 9px;\n}\n.ps-experiments .icard.running { border-color: color-mix(in srgb, var(--mj-brand-primary) 40%, var(--mj-border-default)); }\n.ps-experiments .icard.best { border-left: 3px solid var(--mj-status-warning); }\n.ps-experiments .icard.pruned { opacity: .78; }\n.ps-experiments .ic-top { display: flex; align-items: center; gap: 9px; }\n.ps-experiments .ic-top .algo-ic { width: 30px; height: 30px; }\n.ps-experiments .ic-top .nm { font-weight: 600; color: var(--mj-text-primary); font-size: 13px; }\n.ps-experiments .feat-chips { display: flex; gap: 6px; flex-wrap: wrap; }\n.ps-experiments .ic-prog .meta { display: flex; justify-content: space-between; margin-top: 5px; }\n.ps-experiments .ic-score { display: flex; align-items: baseline; gap: 8px; }\n.ps-experiments .ic-score .v { font-size: 21px; font-weight: 720; color: var(--mj-text-primary); font-variant-numeric: tabular-nums; }\n.ps-experiments .ic-score .v.green { color: var(--mj-status-success-text); }\n.ps-experiments .rationale {\n display: flex; gap: 8px; padding-top: 8px; border-top: 1px solid var(--mj-border-subtle); color: var(--mj-text-secondary);\n}\n.ps-experiments .rationale i { color: var(--mj-brand-primary); margin-top: 2px; }\n"] }]
775
+ `, styles: ["/* ============================================================================\n Predictive Studio \u2014 shared panel styles (design-token compliant)\n Maps the mockup design system onto MemberJunction semantic --mj-* tokens.\n Loaded by every PS panel component (styleUrls) under ViewEncapsulation.None,\n so the class names are global within the PS dashboard subtree.\n No hardcoded colors except the intentional categorical node/algorithm palette\n (declared once as --ps-* custom props below) which must stay distinct across\n light/dark to remain readable as diagram accents.\n ============================================================================ */\n\n/* Categorical accents for DAG node types + algorithm chips. These are\n data-visualization colors that must remain distinct regardless of theme\n (allowed exception #3 in the design-token rules). */\n.ps-panel {\n --ps-node-source: #6b7280;\n --ps-node-feature: #2050c8;\n --ps-node-embedding: #7b3ff0;\n --ps-node-target: #1f9d57;\n --ps-node-algorithm: #d98213;\n /* budget-gauge categorical accents (reuse the node palette so light/dark stay distinct) */\n --ps-gauge-cost: var(--ps-node-algorithm);\n --ps-gauge-iterations: var(--ps-node-feature);\n}\n\n.ps-panel {\n display: block;\n height: 100%;\n}\n\n/* ---- generic layout helpers ---- */\n.ps-row { display: flex; gap: 16px; }\n.ps-col { display: flex; flex-direction: column; gap: 16px; }\n.ps-wrap { flex-wrap: wrap; }\n.ps-spacer { flex: 1; }\n.ps-muted { color: var(--mj-text-muted); }\n.ps-small { font-size: 12.5px; }\n.ps-mono { font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; font-size: 12.5px; }\n.ps-section-title {\n font-size: 12px; text-transform: uppercase; letter-spacing: .06em;\n color: var(--mj-text-muted); margin: 4px 0 2px; font-weight: 700;\n}\n\n/* ---- cards ---- */\n.ps-card {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: 8px;\n overflow: hidden;\n}\n.ps-card-head {\n display: flex; align-items: center; gap: 10px;\n padding: 14px 16px; border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-card-head h3 { font-size: 14.5px; margin: 0; font-weight: 650; color: var(--mj-text-primary); }\n.ps-card-body { padding: 16px; }\n\n/* ---- KPI tiles ---- */\n.ps-kpi {\n background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default);\n border-radius: 8px; padding: 16px 18px;\n}\n.ps-kpi .label { font-size: 12px; color: var(--mj-text-muted); font-weight: 600; }\n.ps-kpi .val { font-size: 26px; font-weight: 720; letter-spacing: -.02em; margin-top: 2px; color: var(--mj-text-primary); }\n\n/* ---- badges ---- */\n.ps-badge {\n display: inline-flex; align-items: center; gap: 5px;\n padding: 2px 9px; border-radius: 999px; font-size: 11.5px; font-weight: 650; line-height: 1.7;\n}\n.ps-badge.gray { background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary); }\n.ps-badge.blue { background: var(--mj-status-info-bg); color: var(--mj-status-info-text); }\n.ps-badge.green { background: var(--mj-status-success-bg); color: var(--mj-status-success-text); }\n.ps-badge.amber { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-badge.red { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-dot { width: 7px; height: 7px; border-radius: 50%; display: inline-block; }\n\n/* recommendation-level chips (the ranking matrix payoff) */\n.ps-lvl {\n display: inline-flex; align-items: center; justify-content: center;\n min-width: 92px; padding: 4px 10px; border-radius: 7px;\n font-weight: 650; font-size: 11.5px; color: var(--mj-text-inverse);\n}\n.ps-lvl-Primary { background: var(--mj-status-success); }\n.ps-lvl-Strong { background: var(--mj-status-info); }\n.ps-lvl-Viable { background: var(--mj-status-warning); }\n.ps-lvl-Weak { background: var(--mj-text-disabled); }\n.ps-lvl-NotRecommended { background: var(--mj-bg-surface-sunken); color: var(--mj-text-muted); }\n\n/* ---- tags ---- */\n.ps-tag {\n display: inline-block; padding: 2px 8px; border-radius: 6px;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary);\n font-size: 11.5px; font-weight: 600;\n}\n.ps-tag.muted { opacity: .6; }\n.ps-divider { height: 1px; background: var(--mj-border-subtle); margin: 8px 0; }\n\n/* ---- tables ---- */\n.ps-table { width: 100%; border-collapse: collapse; font-size: 13px; }\n.ps-table th {\n text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; padding: 9px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n.ps-table td { padding: 11px 12px; border-bottom: 1px solid var(--mj-border-subtle); color: var(--mj-text-primary); }\n.ps-table tr:hover td { background: var(--mj-bg-surface-hover); }\n.ps-table .num { text-align: right; font-variant-numeric: tabular-nums; }\n\n.ps-rank {\n width: 22px; height: 22px; border-radius: 6px; background: var(--mj-bg-surface-sunken);\n display: inline-grid; place-items: center; font-weight: 700; font-size: 11.5px; color: var(--mj-text-secondary);\n}\n.ps-rank.gold { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n\n/* ---- progress bars ---- */\n.ps-bar { height: 8px; border-radius: 999px; background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-bar > span { display: block; height: 100%; border-radius: 999px; background: var(--mj-brand-primary); }\n.ps-bar.warn > span { background: var(--mj-status-warning); }\n.ps-bar.run > span { background: var(--mj-brand-primary); }\n\n.ps-fbar { display: grid; grid-template-columns: minmax(150px, 1.3fr) 2fr 56px; align-items: center; gap: 14px; padding: 8px 0; }\n.ps-fbar + .ps-fbar { border-top: 1px solid var(--mj-border-subtle); }\n.ps-fbar .track { height: 10px; border-radius: var(--mj-radius-full); background: var(--mj-bg-surface-sunken); overflow: hidden; }\n.ps-fbar .track > span { display: block; height: 100%; border-radius: var(--mj-radius-full); background: var(--mj-brand-primary); }\n.ps-fbar .track > span.warn { background: var(--mj-status-warning); }\n.ps-fbar .name { color: var(--mj-text-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }\n.ps-fbar > .ps-mono { text-align: right; color: var(--mj-text-secondary); font-variant-numeric: tabular-nums; }\n\n/* gauge */\n.ps-gauge {\n position: relative; width: 64px; height: 64px; border-radius: 50%; flex: 0 0 auto;\n background: conic-gradient(var(--ps-gauge-color, var(--mj-brand-primary)) calc(var(--ps-p, 40) * 1%), var(--mj-bg-surface-sunken) 0);\n display: grid; place-items: center;\n}\n.ps-gauge::after { content: ''; position: absolute; inset: 9px; background: var(--mj-bg-surface); border-radius: 50%; }\n.ps-gauge .g-val { position: relative; font-weight: 720; font-size: 14px; color: var(--mj-text-primary); }\n\n/* warning / info callouts */\n.ps-callout { display: flex; gap: 11px; padding: 12px 14px; border-radius: 10px; border: 1px solid; }\n.ps-callout.warn { background: var(--mj-status-warning-bg); border-color: var(--mj-status-warning-border); color: var(--mj-status-warning-text); }\n.ps-callout.info { background: var(--mj-status-info-bg); border-color: var(--mj-status-info-border); color: var(--mj-status-info-text); }\n.ps-callout.success { background: var(--mj-status-success-bg); border-color: var(--mj-status-success-border); color: var(--mj-status-success-text); }\n.ps-callout i { font-size: 16px; margin-top: 1px; }\n\n/* lifecycle stepper */\n.ps-stepper { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }\n.ps-step { display: flex; align-items: center; gap: 6px; font-size: 12px; font-weight: 600; color: var(--mj-text-muted); }\n.ps-step .pip {\n width: 22px; height: 22px; border-radius: 50%; border: 2px solid var(--mj-border-strong);\n display: grid; place-items: center; font-size: 11px;\n}\n.ps-step.done { color: var(--mj-status-success-text); }\n.ps-step.done .pip { background: var(--mj-status-success); border-color: var(--mj-status-success); color: var(--mj-text-inverse); }\n.ps-step.curr { color: var(--mj-brand-primary); }\n.ps-step.curr .pip { border-color: var(--mj-brand-primary); color: var(--mj-brand-primary); }\n.ps-step .ln { width: 26px; height: 2px; background: var(--mj-border-strong); }\n\n/* ---- DAG flow canvas ---- */\n.ps-flow {\n position: relative;\n background:\n linear-gradient(var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n linear-gradient(90deg, var(--mj-border-subtle) 1px, transparent 1px) 0 0 / 22px 22px,\n var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-default); border-radius: 10px;\n min-height: 620px; overflow: hidden;\n}\n.ps-node {\n position: absolute; width: 190px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-strong); border-left-width: 3px; border-radius: 8px;\n cursor: pointer;\n}\n.ps-node .nh {\n display: flex; align-items: center; gap: 8px; padding: 9px 11px;\n border-bottom: 1px solid var(--mj-border-subtle); font-weight: 650; font-size: 12.5px; color: var(--mj-text-primary);\n}\n.ps-node .nh i.tile {\n width: 24px; height: 24px; border-radius: 6px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 12px; flex: 0 0 auto;\n}\n.ps-node .nh .ps-tag { margin-left: auto; }\n.ps-node .nb { padding: 9px 11px; font-size: 12px; color: var(--mj-text-secondary); }\n.ps-node .nb-row { display: flex; justify-content: space-between; gap: 8px; padding: 2px 0; }\n.ps-node .nb-row .k { color: var(--mj-text-muted); }\n.ps-node.src { border-left-color: var(--ps-node-source); }\n.ps-node.src .nh i.tile { background: var(--ps-node-source); }\n.ps-node.feat { border-left-color: var(--ps-node-feature); }\n.ps-node.feat .nh i.tile { background: var(--ps-node-feature); }\n.ps-node.emb { border-left-color: var(--ps-node-embedding); }\n.ps-node.emb .nh i.tile { background: var(--ps-node-embedding); }\n.ps-node.target { border-left-color: var(--ps-node-target); }\n.ps-node.target .nh i.tile { background: var(--ps-node-target); }\n.ps-node.algo { border-left-color: var(--ps-node-algorithm); }\n.ps-node.algo .nh i.tile { background: var(--ps-node-algorithm); }\n.ps-node.output { border-left-color: var(--mj-status-success); }\n.ps-node.output .nh i.tile { background: var(--mj-status-success); }\n.ps-node.selected {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 18%, transparent);\n}\n.ps-node .port {\n position: absolute; width: 11px; height: 11px; border-radius: 50%;\n background: var(--mj-bg-surface); border: 2px solid var(--mj-brand-primary);\n top: 50%; transform: translateY(-50%);\n}\n.ps-node .port.in { left: -6px; }\n.ps-node .port.out { right: -6px; }\nsvg.ps-edges { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }\nsvg.ps-edges path { stroke: color-mix(in srgb, var(--mj-brand-primary) 45%, transparent); stroke-width: 2; fill: none; }\n\n/* palette chips */\n.ps-pchip {\n display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px; border-radius: 8px;\n border: 1px dashed var(--mj-border-strong); background: var(--mj-bg-surface);\n font-size: 12.5px; font-weight: 600; color: var(--mj-text-secondary); cursor: grab;\n}\n.ps-pchip i { font-size: 12px; color: var(--mj-text-inverse); width: 20px; height: 20px; border-radius: 5px; display: grid; place-items: center; }\n.ps-pchip.s i { background: var(--ps-node-source); }\n.ps-pchip.f i { background: var(--ps-node-feature); }\n.ps-pchip.e i { background: var(--ps-node-embedding); }\n.ps-pchip.t i { background: var(--ps-node-target); }\n.ps-pchip.a i { background: var(--ps-node-algorithm); }\n\n/* segmented control */\n.ps-seg { display: inline-flex; border: 1px solid var(--mj-border-default); border-radius: 8px; overflow: hidden; }\n.ps-seg button {\n border: none; background: var(--mj-bg-surface); color: var(--mj-text-secondary);\n padding: 6px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer;\n border-right: 1px solid var(--mj-border-default);\n}\n.ps-seg button:last-child { border-right: none; }\n.ps-seg button.on { background: var(--mj-brand-primary); color: var(--mj-text-inverse); }\n\n/* field control */\n.ps-field { margin-bottom: 14px; }\n.ps-field > label {\n display: block; font-size: 11px; text-transform: uppercase; letter-spacing: .04em;\n color: var(--mj-text-muted); font-weight: 700; margin-bottom: 6px;\n}\n.ps-input {\n width: 100%; padding: 8px 10px; border: 1px solid var(--mj-border-default);\n border-radius: 6px; font-size: 13px; background: var(--mj-bg-surface); color: var(--mj-text-primary);\n box-sizing: border-box;\n}\n.ps-input:focus { outline: none; border-color: var(--mj-brand-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--mj-brand-primary) 12%, transparent); }\n\n/* loading container */\n.ps-loading { display: flex; align-items: center; justify-content: center; padding: 60px 0; }\n\n/* ---- empty / prompt states ---- */\n.ps-empty {\n display: flex; flex-direction: column; align-items: center; justify-content: center;\n text-align: center; gap: 10px; padding: 56px 24px; color: var(--mj-text-muted);\n}\n.ps-empty .ps-empty-ico {\n width: 60px; height: 60px; border-radius: 16px; display: grid; place-items: center;\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-disabled); font-size: 24px;\n}\n.ps-empty h3 { margin: 4px 0 0; font-size: 16px; font-weight: 650; color: var(--mj-text-secondary); }\n.ps-empty p { margin: 0; max-width: 420px; font-size: 13px; line-height: 1.5; }\n\n/* ---- confirm modal ---- */\n.ps-modal-backdrop {\n position: fixed; inset: 0; z-index: 1000;\n background: var(--mj-bg-overlay);\n display: flex; align-items: center; justify-content: center; padding: 24px;\n}\n.ps-modal {\n width: 100%; max-width: 480px; background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default); border-radius: 14px;\n box-shadow: var(--mj-shadow-lg, var(--mj-shadow-sm)); overflow: hidden;\n}\n.ps-modal-head {\n display: flex; align-items: center; gap: 12px; padding: 18px 20px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n.ps-modal-head .ico {\n width: 38px; height: 38px; border-radius: 10px; display: grid; place-items: center;\n font-size: 16px; flex: 0 0 auto;\n background: var(--mj-status-info-bg); color: var(--mj-status-info-text);\n}\n.ps-modal-head.warn .ico { background: var(--mj-status-warning-bg); color: var(--mj-status-warning-text); }\n.ps-modal-head.danger .ico { background: var(--mj-status-error-bg); color: var(--mj-status-error-text); }\n.ps-modal-head h3 { margin: 0; font-size: 15.5px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-modal-body { padding: 18px 20px; font-size: 13.5px; line-height: 1.55; color: var(--mj-text-secondary); }\n.ps-modal-body .ps-field { margin-top: 14px; margin-bottom: 0; }\n.ps-modal-foot {\n display: flex; align-items: center; gap: 10px; padding: 14px 20px;\n border-top: 1px solid var(--mj-border-subtle); background: var(--mj-bg-surface-card);\n}\n.ps-modal-foot .ps-spacer { flex: 1; }\n", ".ps-experiments { display: flex; flex-direction: column; gap: 16px; }\n\n.ps-experiments .sess-head { display: flex; align-items: center; gap: 12px; }\n.ps-experiments .sess-head h2 { font-size: 18px; color: var(--mj-text-primary); display: flex; align-items: center; gap: 10px; }\n\n/* leaderboard strip */\n.ps-experiments .lead-strip { display: flex; gap: 10px; align-items: center; overflow-x: auto; }\n.ps-experiments .lead-lbl { padding-right: 14px; border-right: 1px solid var(--mj-border-default); flex: 0 0 auto; }\n.ps-experiments .lead-lbl .t { display: flex; align-items: center; gap: 7px; font-weight: 650; color: var(--mj-text-primary); }\n.ps-experiments .lp {\n display: flex; align-items: center; gap: 9px; flex: 0 0 auto; padding: 8px 12px;\n border: 1px solid var(--mj-border-default); border-radius: 9px; background: var(--mj-bg-surface);\n}\n.ps-experiments .lp.best { background: var(--mj-status-warning-bg); border-color: var(--mj-status-warning-border); }\n.ps-experiments .lp.pruned { opacity: .55; }\n.ps-experiments .algo-ic {\n width: 26px; height: 26px; border-radius: 7px; display: grid; place-items: center;\n color: var(--mj-text-inverse); font-size: 12px; flex: 0 0 auto;\n}\n.ps-experiments .lp .nm { font-size: 12.5px; font-weight: 600; color: var(--mj-text-primary); }\n.ps-experiments .lp .auc { font-weight: 740; font-variant-numeric: tabular-nums; color: var(--mj-text-primary); }\n.ps-experiments .lp .auc.win { color: var(--mj-status-success-text); }\n\n/* budget rail */\n.ps-experiments .budget-rail { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; }\n@media (max-width: 880px) { .ps-experiments .budget-rail { grid-template-columns: 1fr; } }\n.ps-experiments .bud { display: flex; align-items: center; gap: 13px; }\n.ps-experiments .b-val { font-size: 15px; color: var(--mj-text-primary); margin-top: 2px; }\n\n/* kanban */\n.ps-experiments .kanban { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; align-items: start; }\n@media (max-width: 980px) { .ps-experiments .kanban { grid-template-columns: 1fr; } }\n.ps-experiments .kcol { background: var(--mj-bg-surface); border: 1px solid var(--mj-border-default); border-radius: 10px; overflow: hidden; }\n.ps-experiments .kcol-head {\n display: flex; align-items: center; gap: 9px; padding: 12px 14px; border-bottom: 1px solid var(--mj-border-subtle);\n font-weight: 650; color: var(--mj-text-primary);\n}\n.ps-experiments .kcol-head h3 { font-size: 13.5px; margin: 0; flex: 1; }\n.ps-experiments .kcol.run .kcol-head { border-top: 3px solid var(--mj-brand-primary); }\n.ps-experiments .kcol.done .kcol-head { border-top: 3px solid var(--mj-status-success); }\n.ps-experiments .kcol.prune .kcol-head { border-top: 3px solid var(--mj-text-disabled); }\n.ps-experiments .kcol.run .kcol-head i { color: var(--mj-brand-primary); }\n.ps-experiments .kcol.done .kcol-head i { color: var(--mj-status-success); }\n.ps-experiments .kcol.prune .kcol-head i { color: var(--mj-text-muted); }\n.ps-experiments .cnt {\n background: var(--mj-bg-surface-sunken); color: var(--mj-text-secondary);\n border-radius: 999px; padding: 1px 9px; font-size: 12px; font-weight: 700;\n}\n.ps-experiments .kbody { padding: 11px; display: flex; flex-direction: column; gap: 11px; }\n\n.ps-experiments .icard {\n border: 1px solid var(--mj-border-default); border-radius: 9px; padding: 11px;\n background: var(--mj-bg-surface-card); display: flex; flex-direction: column; gap: 9px;\n}\n.ps-experiments .icard.running { border-color: color-mix(in srgb, var(--mj-brand-primary) 40%, var(--mj-border-default)); }\n.ps-experiments .icard.best { border-left: 3px solid var(--mj-status-warning); }\n.ps-experiments .icard.pruned { opacity: .78; }\n.ps-experiments .ic-top { display: flex; align-items: center; gap: 9px; }\n.ps-experiments .ic-top .algo-ic { width: 30px; height: 30px; }\n.ps-experiments .ic-top .nm { font-weight: 600; color: var(--mj-text-primary); font-size: 13px; }\n.ps-experiments .feat-chips { display: flex; gap: 6px; flex-wrap: wrap; }\n.ps-experiments .ic-prog .meta { display: flex; justify-content: space-between; margin-top: 5px; }\n.ps-experiments .ic-score { display: flex; align-items: baseline; gap: 8px; }\n.ps-experiments .ic-score .v { font-size: 21px; font-weight: 720; color: var(--mj-text-primary); font-variant-numeric: tabular-nums; }\n.ps-experiments .ic-score .v.green { color: var(--mj-status-success-text); }\n.ps-experiments .rationale {\n display: flex; gap: 8px; padding-top: 8px; border-top: 1px solid var(--mj-border-subtle); color: var(--mj-text-secondary);\n}\n.ps-experiments .rationale i { color: var(--mj-brand-primary); margin-top: 2px; }\n"] }]
776
776
  }], null, { engine: [{
777
777
  type: Input
778
778
  }], provider: [{
@@ -1 +1 @@
1
- {"version":3,"file":"ps-experiments.component.js","sourceRoot":"","sources":["../../../src/PredictiveStudio/components/ps-experiments.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { ChangeDetectorRef, Component, Input, OnInit, ViewEncapsulation, inject } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { MJButtonDirective } from '@memberjunction/ng-ui-components';\nimport { MJNotificationService } from '@memberjunction/ng-notifications';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { IMetadataProvider, UserInfo } from '@memberjunction/core';\nimport {\n MJExperimentSessionEntity,\n PredictiveStudioControlExperimentSessionOperation,\n PredictiveStudioExperimentSessionAction,\n} from '@memberjunction/core-entities';\nimport { PredictiveStudioEngine } from '../engine/predictive-studio.engine';\nimport { PSIterationCard, PSLeaderboardEntry } from '../predictive-studio.types';\nimport {\n PSKanbanColumns,\n deriveLeaderboard,\n groupIterationsToKanban,\n} from '../predictive-studio.view-models';\nimport { PSConfirmModalComponent } from './ps-confirm-modal.component';\n\n/** A budget gauge derived from the session's budget JSON + actual iteration spend. */\ninterface BudgetGaugeVM {\n label: string;\n fillPct: number;\n centerText: string;\n value: string;\n cap: string;\n color: string;\n}\n\n/**\n * Experiments panel: a kanban of iteration cards in Running / Completed / Pruned columns, a leaderboard\n * strip ranked by score, and budget gauges — all live against `MJ: Experiment Sessions` + their\n * `MJ: Experiment Session Iterations`:\n *\n * - The **active session** is the first Running session, else the most-recently-created one.\n * - The **kanban** + **leaderboard** are derived from that session's iterations (grouped by status,\n * ranked by score) via the pure {@link groupIterationsToKanban} / {@link deriveLeaderboard} helpers.\n * - **Budget gauges** combine the session's budget JSON caps with actual iteration spend (compute\n * cost, iteration count).\n * - **Pause / Cancel** call the {@link PredictiveStudioControlExperimentSessionOperation} Remote Op\n * behind a confirmation; on success the engine refreshes.\n *\n * Empty state when there are no sessions. 100% entity-agnostic.\n */\n@Component({\n standalone: true,\n selector: 'ps-experiments',\n imports: [CommonModule, MJButtonDirective, PSConfirmModalComponent],\n encapsulation: ViewEncapsulation.None,\n styleUrls: ['../predictive-studio.shared.scss', './ps-experiments.component.scss'],\n template: `\n <div class=\"ps-panel ps-experiments\" data-testid=\"ps-experiments-panel\">\n @if (!session) {\n <div class=\"ps-empty\" data-testid=\"ps-experiments-empty\">\n <span class=\"ps-empty-ico\"><i class=\"fa-solid fa-flask\"></i></span>\n <h3>No experiment sessions yet</h3>\n <p>Launch an experiment to watch the Model Dev Agent search algorithms and feature sets in waves. Running and completed iterations will appear here with a live leaderboard.</p>\n </div>\n } @else {\n <!-- session header strip -->\n <div class=\"sess-head\">\n <div>\n <div class=\"ps-small ps-muted\">Experiment session</div>\n <h2>{{ session.Name }} <span class=\"ps-badge\" [class]=\"sessionBadgeClass\"><span class=\"ps-dot\" [style.background]=\"sessionDotColor\"></span> {{ session.Status }}</span></h2>\n </div>\n <span class=\"ps-spacer\"></span>\n <span class=\"ps-small ps-muted\">{{ iterationCountLabel }}</span>\n @if (canPause) {\n <button mjButton variant=\"secondary\" size=\"sm\" data-testid=\"ps-experiments-pause\" (click)=\"requestControl('pause')\"><i class=\"fa-solid fa-pause\"></i> Pause</button>\n }\n @if (canResume) {\n <button mjButton variant=\"secondary\" size=\"sm\" data-testid=\"ps-experiments-resume\" (click)=\"requestControl('resume')\"><i class=\"fa-solid fa-play\"></i> Resume</button>\n }\n @if (canCancel) {\n <button mjButton variant=\"danger\" size=\"sm\" data-testid=\"ps-experiments-cancel\" (click)=\"requestControl('cancel')\"><i class=\"fa-solid fa-stop\"></i> Cancel</button>\n }\n </div>\n\n <!-- Leaderboard strip -->\n <div class=\"ps-card\" data-testid=\"ps-experiments-leaderboard\">\n <div class=\"ps-card-body lead-strip\">\n <div class=\"lead-lbl\">\n <div class=\"t\"><i class=\"fa-solid fa-trophy\" style=\"color:var(--mj-status-warning)\"></i> Leaderboard</div>\n <div class=\"ps-small ps-muted\">{{ leaderboard.length }} scored · by metric</div>\n </div>\n @if (leaderboard.length === 0) {\n <span class=\"ps-small ps-muted\">No scored iterations yet.</span>\n }\n @for (lp of leaderboard; track lp.rank) {\n <div class=\"lp\" [class.best]=\"lp.best\" [class.pruned]=\"lp.pruned\">\n <span class=\"ps-rank\" [class.gold]=\"lp.best\">{{ lp.rank }}</span>\n <span class=\"algo-ic\" [style.background]=\"lp.algorithmColor\"><i [class]=\"lp.algorithmIcon\"></i></span>\n <div><div class=\"nm\">{{ lp.algorithm }}</div><div class=\"fs ps-muted ps-small\">{{ lp.features }}</div></div>\n <span class=\"auc\" [class.win]=\"lp.best\">{{ lp.auc | number: '1.3-3' }}</span>\n </div>\n }\n </div>\n </div>\n\n <!-- Budget rail -->\n @if (budget.length > 0) {\n <div class=\"ps-card\">\n <div class=\"ps-card-body budget-rail\">\n @for (b of budget; track b.label) {\n <div class=\"bud\">\n <div class=\"ps-gauge\" [style.--ps-p]=\"b.fillPct\" [style.--ps-gauge-color]=\"b.color\">\n <span class=\"g-val\">{{ b.centerText }}</span>\n </div>\n <div style=\"flex:1\">\n <div class=\"ps-section-title\">{{ b.label }}</div>\n <div class=\"b-val\"><strong>{{ b.value }}</strong> <span class=\"ps-muted ps-small\">/ {{ b.cap }}</span></div>\n <div class=\"ps-bar\" style=\"margin-top:6px;max-width:150px\"><span [style.width.%]=\"b.fillPct\" [style.background]=\"b.color\"></span></div>\n </div>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Kanban -->\n <div class=\"kanban\" data-testid=\"ps-experiments-kanban\">\n <div class=\"kcol run\" data-testid=\"ps-kanban-col-running\">\n <div class=\"kcol-head\"><i class=\"fa-solid fa-spinner\"></i><h3>Running</h3><span class=\"cnt\">{{ kanban.running.length }}</span></div>\n <div class=\"kbody\">\n @for (c of kanban.running; track c.iteration) {\n <ng-container [ngTemplateOutlet]=\"iterCard\" [ngTemplateOutletContext]=\"{ c: c }\"></ng-container>\n }\n @if (kanban.running.length === 0) { <span class=\"ps-small ps-muted\">No running iterations.</span> }\n </div>\n </div>\n <div class=\"kcol done\" data-testid=\"ps-kanban-col-completed\">\n <div class=\"kcol-head\"><i class=\"fa-solid fa-circle-check\"></i><h3>Completed</h3><span class=\"cnt\">{{ kanban.completed.length }}</span></div>\n <div class=\"kbody\">\n @for (c of kanban.completed; track c.iteration) {\n <ng-container [ngTemplateOutlet]=\"iterCard\" [ngTemplateOutletContext]=\"{ c: c }\"></ng-container>\n }\n @if (kanban.completed.length === 0) { <span class=\"ps-small ps-muted\">No completed iterations.</span> }\n </div>\n </div>\n <div class=\"kcol prune\" data-testid=\"ps-kanban-col-pruned\">\n <div class=\"kcol-head\"><i class=\"fa-solid fa-scissors\"></i><h3>Pruned</h3><span class=\"cnt\">{{ kanban.pruned.length }}</span></div>\n <div class=\"kbody\">\n @for (c of kanban.pruned; track c.iteration) {\n <ng-container [ngTemplateOutlet]=\"iterCard\" [ngTemplateOutletContext]=\"{ c: c }\"></ng-container>\n }\n @if (kanban.pruned.length === 0) { <span class=\"ps-small ps-muted\">Nothing pruned.</span> }\n </div>\n </div>\n </div>\n\n <ng-template #iterCard let-c=\"c\">\n <div class=\"icard\" [class.best]=\"c.status === 'Best'\" [class.running]=\"c.status === 'Running'\" [class.pruned]=\"c.status === 'Pruned'\">\n <div class=\"ic-top\">\n <span class=\"algo-ic\" [style.background]=\"c.algorithmColor\"><i [class]=\"c.algorithmIcon\"></i></span>\n <div><div class=\"nm\">{{ c.algorithm }}</div><div class=\"it ps-muted ps-small\">{{ c.iteration }}</div></div>\n <span class=\"ps-spacer\"></span>\n <span class=\"ps-badge\" [class]=\"statusBadgeClass(c.status)\">{{ statusLabel(c.status) }}</span>\n </div>\n <div class=\"feat-chips\">\n @for (f of c.features; track f) { <span class=\"ps-tag\">{{ f }}</span> }\n </div>\n @if (c.status === 'Running') {\n <div class=\"ic-prog\">\n <div class=\"ps-bar run\"><span [style.width.%]=\"c.progress\"></span></div>\n <div class=\"meta\"><span class=\"ps-muted ps-small\">{{ c.progressDetail }}</span></div>\n </div>\n } @else {\n <div class=\"ic-score\">\n <span class=\"v\" [class.green]=\"c.status === 'Best'\">{{ c.score | number: '1.3-3' }}</span>\n <span class=\"lbl ps-muted ps-small\">score · {{ c.scoreDelta }}</span>\n </div>\n }\n <div class=\"rationale\"><i class=\"fa-solid fa-robot\"></i><span class=\"ps-small\">{{ c.rationale }}</span></div>\n </div>\n </ng-template>\n }\n\n @if (pendingAction) {\n <ps-confirm-modal\n [title]=\"pendingTitle\"\n [icon]=\"pendingIcon\"\n [confirmIcon]=\"pendingIcon\"\n [confirmLabel]=\"pendingConfirmLabel\"\n [variant]=\"pendingAction === 'cancel' ? 'danger' : 'warn'\"\n [busy]=\"busy\"\n (confirmed)=\"confirmControl()\"\n (cancelled)=\"cancelControl()\">\n <div [innerHTML]=\"pendingMessage\"></div>\n </ps-confirm-modal>\n }\n </div>\n `,\n})\nexport class PSExperimentsComponent implements OnInit {\n @Input() engine!: PredictiveStudioEngine;\n /** Provider to route the control Remote Op + engine refresh through (multi-provider correctness). */\n @Input() provider: IMetadataProvider | null = null;\n /** Acting user for the engine refresh after a mutation. */\n @Input() currentUser: UserInfo | null = null;\n\n private cdr = inject(ChangeDetectorRef);\n private notifications = inject(MJNotificationService);\n\n /** The currently-displayed session ID (first Running, else most recent). */\n public sessionId = '';\n public kanban: PSKanbanColumns = { running: [], completed: [], pruned: [] };\n public leaderboard: PSLeaderboardEntry[] = [];\n public budget: BudgetGaugeVM[] = [];\n\n /** Pending control action awaiting confirmation (null when no modal is open). */\n public pendingAction: PredictiveStudioExperimentSessionAction | null = null;\n /** Remote Op in flight. */\n public busy = false;\n\n ngOnInit(): void {\n this.selectActiveSession();\n this.rebuild();\n }\n\n // ---- active session selection ----\n\n /** Pick the most relevant session to display: first Running, else the most-recently-created. */\n private selectActiveSession(): void {\n const sessions = this.engine?.Sessions ?? [];\n const running = sessions.find((s) => s.Status === 'Running');\n // Sessions are loaded OrderBy '__mj_CreatedAt DESC', so index 0 is the most recent.\n this.sessionId = (running ?? sessions[0])?.ID ?? '';\n }\n\n public get session(): MJExperimentSessionEntity | undefined {\n return this.engine?.Sessions.find((s) => UUIDsEqual(s.ID, this.sessionId));\n }\n\n public get iterationCountLabel(): string {\n const total = this.kanban.running.length + this.kanban.completed.length + this.kanban.pruned.length;\n return `${total} iteration${total === 1 ? '' : 's'}`;\n }\n\n // ---- derivations ----\n\n private rebuild(): void {\n if (!this.sessionId) {\n this.kanban = { running: [], completed: [], pruned: [] };\n this.leaderboard = [];\n this.budget = [];\n return;\n }\n const rows = this.engine.IterationRowsForSession(this.sessionId);\n this.kanban = groupIterationsToKanban(rows);\n this.leaderboard = deriveLeaderboard(rows);\n this.budget = this.buildBudget(rows);\n }\n\n /** Build the budget gauges from the session's budget JSON caps + actual iteration spend. */\n private buildBudget(rows: { ComputeCost: number | null }[]): BudgetGaugeVM[] {\n const session = this.session;\n if (!session) return [];\n const budgetJson = this.parseBudget(session.Budget);\n const gauges: BudgetGaugeVM[] = [];\n\n const spentCost = rows.reduce((sum, r) => sum + (r.ComputeCost ?? 0), 0);\n const maxCost = budgetJson.maxComputeCost;\n if (maxCost != null && maxCost > 0) {\n gauges.push(this.gauge('Compute cost', spentCost, maxCost, 'var(--ps-gauge-cost)', (v) => `$${v.toFixed(0)}`, `$${maxCost.toFixed(0)} cap`));\n }\n\n const iterations = rows.length;\n const maxIterations = budgetJson.maxIterations;\n if (maxIterations != null && maxIterations > 0) {\n gauges.push(this.gauge('Iterations', iterations, maxIterations, 'var(--ps-gauge-iterations)', (v) => `${v}`, `${maxIterations} trials`));\n }\n return gauges;\n }\n\n /** Construct a single gauge VM (fill clamped to 0..100). */\n private gauge(label: string, value: number, cap: number, color: string, fmt: (v: number) => string, capLabel: string): BudgetGaugeVM {\n const fillPct = Math.max(0, Math.min(100, Math.round((value / cap) * 100)));\n return { label, fillPct, centerText: `${fillPct}%`, value: fmt(value), cap: capLabel, color };\n }\n\n /** Tolerant parse of the session's budget JSON (accepts a few common key spellings). */\n private parseBudget(json: string | null): { maxComputeCost: number | null; maxIterations: number | null } {\n if (!json) return { maxComputeCost: null, maxIterations: null };\n try {\n const parsed = JSON.parse(json) as Record<string, unknown>;\n const num = (...keys: string[]): number | null => {\n for (const k of keys) {\n const v = parsed[k];\n if (typeof v === 'number' && Number.isFinite(v)) return v;\n }\n return null;\n };\n return {\n maxComputeCost: num('maxComputeCost', 'maxCost', 'computeBudget'),\n maxIterations: num('maxIterations', 'maxTrials', 'iterationBudget'),\n };\n } catch {\n return { maxComputeCost: null, maxIterations: null };\n }\n }\n\n // ---- session status chrome ----\n\n public get sessionBadgeClass(): string {\n switch (this.session?.Status) {\n case 'Running': return 'green';\n case 'Paused': return 'amber';\n case 'Cancelled': return 'red';\n case 'Completed': return 'blue';\n default: return 'gray';\n }\n }\n public get sessionDotColor(): string {\n switch (this.session?.Status) {\n case 'Running': return 'var(--mj-status-success)';\n case 'Paused': return 'var(--mj-status-warning)';\n case 'Cancelled': return 'var(--mj-status-error)';\n default: return 'var(--mj-text-muted)';\n }\n }\n\n public statusBadgeClass(status: PSIterationCard['status']): string {\n switch (status) {\n case 'Best': return 'green';\n case 'Completed': return 'gray';\n case 'AwaitingApproval': return 'amber';\n case 'Pruned': return 'red';\n case 'Running': return 'blue';\n }\n }\n\n public statusLabel(status: PSIterationCard['status']): string {\n switch (status) {\n case 'Best': return 'Best';\n case 'AwaitingApproval': return 'Awaiting approval';\n default: return status;\n }\n }\n\n // ---- control availability ----\n\n public get canPause(): boolean {\n return this.session?.Status === 'Running';\n }\n public get canResume(): boolean {\n return this.session?.Status === 'Paused';\n }\n public get canCancel(): boolean {\n const s = this.session?.Status;\n return s === 'Running' || s === 'Paused' || s === 'AwaitingApproval';\n }\n\n // ---- control flow (Remote Op) ----\n\n public requestControl(action: PredictiveStudioExperimentSessionAction): void {\n if (!this.session) return;\n this.pendingAction = action;\n }\n\n public cancelControl(): void {\n if (this.busy) return;\n this.pendingAction = null;\n }\n\n public get pendingTitle(): string {\n switch (this.pendingAction) {\n case 'pause': return 'Pause experiment';\n case 'resume': return 'Resume experiment';\n case 'cancel': return 'Cancel experiment';\n default: return '';\n }\n }\n public get pendingIcon(): string {\n switch (this.pendingAction) {\n case 'pause': return 'fa-solid fa-pause';\n case 'resume': return 'fa-solid fa-play';\n case 'cancel': return 'fa-solid fa-stop';\n default: return 'fa-solid fa-check';\n }\n }\n public get pendingConfirmLabel(): string {\n switch (this.pendingAction) {\n case 'pause': return 'Pause';\n case 'resume': return 'Resume';\n case 'cancel': return 'Cancel run';\n default: return 'Confirm';\n }\n }\n public get pendingMessage(): string {\n const name = escapeHtml(this.session?.Name ?? 'this session');\n switch (this.pendingAction) {\n case 'pause':\n return `Pause <strong>${name}</strong>? The orchestrator stops at the next wave checkpoint; in-flight iterations finish. You can resume later.`;\n case 'resume':\n return `Resume <strong>${name}</strong>? The orchestrator continues searching from where it paused.`;\n case 'cancel':\n return `Cancel <strong>${name}</strong>? This stops the search at the next checkpoint. Completed iterations and the leaderboard are kept, but the session cannot be resumed.`;\n default:\n return '';\n }\n }\n\n /** Run the control Remote Op, then refresh the engine + close on success. */\n public async confirmControl(): Promise<void> {\n if (!this.pendingAction || !this.session || this.busy) return;\n this.busy = true;\n const action = this.pendingAction;\n const sessionId = this.session.ID;\n const name = this.session.Name;\n try {\n const op = new PredictiveStudioControlExperimentSessionOperation();\n const result = await op.Execute(\n { sessionId, action },\n { provider: this.provider ?? undefined, user: this.currentUser ?? undefined },\n );\n if (result.Success) {\n this.notifications.CreateSimpleNotification(\n `${name} → ${result.Output?.status ?? action}`,\n 'success',\n 3500,\n );\n await this.refreshAfterMutation();\n this.pendingAction = null;\n } else {\n this.notifications.CreateSimpleNotification(\n result.ErrorMessage || `Could not ${action} ${name}.`,\n 'error',\n 5000,\n );\n }\n } catch (e) {\n this.notifications.CreateSimpleNotification(\n `${action} failed: ${e instanceof Error ? e.message : String(e)}`,\n 'error',\n 5000,\n );\n } finally {\n this.busy = false;\n this.cdr.detectChanges();\n }\n }\n\n /** Force-refresh the engine's cached sessions/iterations, then rebuild. */\n private async refreshAfterMutation(): Promise<void> {\n await this.engine.Config(true, this.currentUser ?? undefined, this.provider ?? undefined);\n this.selectActiveSession();\n this.rebuild();\n this.cdr.detectChanges();\n }\n}\n\n/** Minimal HTML-escape for interpolating session names into the modal's innerHTML message. */\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n"]}
1
+ {"version":3,"file":"ps-experiments.component.js","sourceRoot":"","sources":["../../../src/PredictiveStudio/components/ps-experiments.component.ts"],"names":[],"mappings":"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","sourcesContent":["import { ChangeDetectorRef, Component, Input, OnInit, ViewEncapsulation, inject } from '@angular/core';\nimport { CommonModule } from '@angular/common';\nimport { MJButtonDirective } from '@memberjunction/ng-ui-components';\nimport { MJNotificationService } from '@memberjunction/ng-notifications';\nimport { UUIDsEqual } from '@memberjunction/global';\nimport { IMetadataProvider, UserInfo } from '@memberjunction/core';\nimport {\n MJExperimentSessionEntity,\n PredictiveStudioControlExperimentSessionOperation,\n PredictiveStudioExperimentSessionAction,\n} from '@memberjunction/core-entities';\nimport { PredictiveStudioEngine } from '../engine/predictive-studio.engine';\nimport { PSIterationCard, PSLeaderboardEntry } from '../predictive-studio.types';\nimport {\n PSKanbanColumns,\n deriveLeaderboard,\n groupIterationsToKanban,\n} from '../predictive-studio.view-models';\nimport { PSConfirmModalComponent } from './ps-confirm-modal.component';\n\n/** A budget gauge derived from the session's budget JSON + actual iteration spend. */\ninterface BudgetGaugeVM {\n label: string;\n fillPct: number;\n centerText: string;\n value: string;\n cap: string;\n color: string;\n}\n\n/**\n * Experiments panel: a kanban of iteration cards in Running / Completed / Pruned columns, a leaderboard\n * strip ranked by score, and budget gauges — all live against `MJ: Experiment Sessions` + their\n * `MJ: Experiment Session Iterations`:\n *\n * - The **active session** is the first Running session, else the most-recently-created one.\n * - The **kanban** + **leaderboard** are derived from that session's iterations (grouped by status,\n * ranked by score) via the pure {@link groupIterationsToKanban} / {@link deriveLeaderboard} helpers.\n * - **Budget gauges** combine the session's budget JSON caps with actual iteration spend (compute\n * cost, iteration count).\n * - **Pause / Cancel** call the {@link PredictiveStudioControlExperimentSessionOperation} Remote Op\n * behind a confirmation; on success the engine refreshes.\n *\n * Empty state when there are no sessions. 100% entity-agnostic.\n */\n@Component({\n standalone: true,\n selector: 'ps-experiments',\n imports: [CommonModule, MJButtonDirective, PSConfirmModalComponent],\n encapsulation: ViewEncapsulation.None,\n styleUrls: ['../predictive-studio.shared.css', './ps-experiments.component.css'],\n template: `\n <div class=\"ps-panel ps-experiments\" data-testid=\"ps-experiments-panel\">\n @if (!session) {\n <div class=\"ps-empty\" data-testid=\"ps-experiments-empty\">\n <span class=\"ps-empty-ico\"><i class=\"fa-solid fa-flask\"></i></span>\n <h3>No experiment sessions yet</h3>\n <p>Launch an experiment to watch the Model Dev Agent search algorithms and feature sets in waves. Running and completed iterations will appear here with a live leaderboard.</p>\n </div>\n } @else {\n <!-- session header strip -->\n <div class=\"sess-head\">\n <div>\n <div class=\"ps-small ps-muted\">Experiment session</div>\n <h2>{{ session.Name }} <span class=\"ps-badge\" [class]=\"sessionBadgeClass\"><span class=\"ps-dot\" [style.background]=\"sessionDotColor\"></span> {{ session.Status }}</span></h2>\n </div>\n <span class=\"ps-spacer\"></span>\n <span class=\"ps-small ps-muted\">{{ iterationCountLabel }}</span>\n @if (canPause) {\n <button mjButton variant=\"secondary\" size=\"sm\" data-testid=\"ps-experiments-pause\" (click)=\"requestControl('pause')\"><i class=\"fa-solid fa-pause\"></i> Pause</button>\n }\n @if (canResume) {\n <button mjButton variant=\"secondary\" size=\"sm\" data-testid=\"ps-experiments-resume\" (click)=\"requestControl('resume')\"><i class=\"fa-solid fa-play\"></i> Resume</button>\n }\n @if (canCancel) {\n <button mjButton variant=\"danger\" size=\"sm\" data-testid=\"ps-experiments-cancel\" (click)=\"requestControl('cancel')\"><i class=\"fa-solid fa-stop\"></i> Cancel</button>\n }\n </div>\n\n <!-- Leaderboard strip -->\n <div class=\"ps-card\" data-testid=\"ps-experiments-leaderboard\">\n <div class=\"ps-card-body lead-strip\">\n <div class=\"lead-lbl\">\n <div class=\"t\"><i class=\"fa-solid fa-trophy\" style=\"color:var(--mj-status-warning)\"></i> Leaderboard</div>\n <div class=\"ps-small ps-muted\">{{ leaderboard.length }} scored · by metric</div>\n </div>\n @if (leaderboard.length === 0) {\n <span class=\"ps-small ps-muted\">No scored iterations yet.</span>\n }\n @for (lp of leaderboard; track lp.rank) {\n <div class=\"lp\" [class.best]=\"lp.best\" [class.pruned]=\"lp.pruned\">\n <span class=\"ps-rank\" [class.gold]=\"lp.best\">{{ lp.rank }}</span>\n <span class=\"algo-ic\" [style.background]=\"lp.algorithmColor\"><i [class]=\"lp.algorithmIcon\"></i></span>\n <div><div class=\"nm\">{{ lp.algorithm }}</div><div class=\"fs ps-muted ps-small\">{{ lp.features }}</div></div>\n <span class=\"auc\" [class.win]=\"lp.best\">{{ lp.auc | number: '1.3-3' }}</span>\n </div>\n }\n </div>\n </div>\n\n <!-- Budget rail -->\n @if (budget.length > 0) {\n <div class=\"ps-card\">\n <div class=\"ps-card-body budget-rail\">\n @for (b of budget; track b.label) {\n <div class=\"bud\">\n <div class=\"ps-gauge\" [style.--ps-p]=\"b.fillPct\" [style.--ps-gauge-color]=\"b.color\">\n <span class=\"g-val\">{{ b.centerText }}</span>\n </div>\n <div style=\"flex:1\">\n <div class=\"ps-section-title\">{{ b.label }}</div>\n <div class=\"b-val\"><strong>{{ b.value }}</strong> <span class=\"ps-muted ps-small\">/ {{ b.cap }}</span></div>\n <div class=\"ps-bar\" style=\"margin-top:6px;max-width:150px\"><span [style.width.%]=\"b.fillPct\" [style.background]=\"b.color\"></span></div>\n </div>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Kanban -->\n <div class=\"kanban\" data-testid=\"ps-experiments-kanban\">\n <div class=\"kcol run\" data-testid=\"ps-kanban-col-running\">\n <div class=\"kcol-head\"><i class=\"fa-solid fa-spinner\"></i><h3>Running</h3><span class=\"cnt\">{{ kanban.running.length }}</span></div>\n <div class=\"kbody\">\n @for (c of kanban.running; track c.iteration) {\n <ng-container [ngTemplateOutlet]=\"iterCard\" [ngTemplateOutletContext]=\"{ c: c }\"></ng-container>\n }\n @if (kanban.running.length === 0) { <span class=\"ps-small ps-muted\">No running iterations.</span> }\n </div>\n </div>\n <div class=\"kcol done\" data-testid=\"ps-kanban-col-completed\">\n <div class=\"kcol-head\"><i class=\"fa-solid fa-circle-check\"></i><h3>Completed</h3><span class=\"cnt\">{{ kanban.completed.length }}</span></div>\n <div class=\"kbody\">\n @for (c of kanban.completed; track c.iteration) {\n <ng-container [ngTemplateOutlet]=\"iterCard\" [ngTemplateOutletContext]=\"{ c: c }\"></ng-container>\n }\n @if (kanban.completed.length === 0) { <span class=\"ps-small ps-muted\">No completed iterations.</span> }\n </div>\n </div>\n <div class=\"kcol prune\" data-testid=\"ps-kanban-col-pruned\">\n <div class=\"kcol-head\"><i class=\"fa-solid fa-scissors\"></i><h3>Pruned</h3><span class=\"cnt\">{{ kanban.pruned.length }}</span></div>\n <div class=\"kbody\">\n @for (c of kanban.pruned; track c.iteration) {\n <ng-container [ngTemplateOutlet]=\"iterCard\" [ngTemplateOutletContext]=\"{ c: c }\"></ng-container>\n }\n @if (kanban.pruned.length === 0) { <span class=\"ps-small ps-muted\">Nothing pruned.</span> }\n </div>\n </div>\n </div>\n\n <ng-template #iterCard let-c=\"c\">\n <div class=\"icard\" [class.best]=\"c.status === 'Best'\" [class.running]=\"c.status === 'Running'\" [class.pruned]=\"c.status === 'Pruned'\">\n <div class=\"ic-top\">\n <span class=\"algo-ic\" [style.background]=\"c.algorithmColor\"><i [class]=\"c.algorithmIcon\"></i></span>\n <div><div class=\"nm\">{{ c.algorithm }}</div><div class=\"it ps-muted ps-small\">{{ c.iteration }}</div></div>\n <span class=\"ps-spacer\"></span>\n <span class=\"ps-badge\" [class]=\"statusBadgeClass(c.status)\">{{ statusLabel(c.status) }}</span>\n </div>\n <div class=\"feat-chips\">\n @for (f of c.features; track f) { <span class=\"ps-tag\">{{ f }}</span> }\n </div>\n @if (c.status === 'Running') {\n <div class=\"ic-prog\">\n <div class=\"ps-bar run\"><span [style.width.%]=\"c.progress\"></span></div>\n <div class=\"meta\"><span class=\"ps-muted ps-small\">{{ c.progressDetail }}</span></div>\n </div>\n } @else {\n <div class=\"ic-score\">\n <span class=\"v\" [class.green]=\"c.status === 'Best'\">{{ c.score | number: '1.3-3' }}</span>\n <span class=\"lbl ps-muted ps-small\">score · {{ c.scoreDelta }}</span>\n </div>\n }\n <div class=\"rationale\"><i class=\"fa-solid fa-robot\"></i><span class=\"ps-small\">{{ c.rationale }}</span></div>\n </div>\n </ng-template>\n }\n\n @if (pendingAction) {\n <ps-confirm-modal\n [title]=\"pendingTitle\"\n [icon]=\"pendingIcon\"\n [confirmIcon]=\"pendingIcon\"\n [confirmLabel]=\"pendingConfirmLabel\"\n [variant]=\"pendingAction === 'cancel' ? 'danger' : 'warn'\"\n [busy]=\"busy\"\n (confirmed)=\"confirmControl()\"\n (cancelled)=\"cancelControl()\">\n <div [innerHTML]=\"pendingMessage\"></div>\n </ps-confirm-modal>\n }\n </div>\n `,\n})\nexport class PSExperimentsComponent implements OnInit {\n @Input() engine!: PredictiveStudioEngine;\n /** Provider to route the control Remote Op + engine refresh through (multi-provider correctness). */\n @Input() provider: IMetadataProvider | null = null;\n /** Acting user for the engine refresh after a mutation. */\n @Input() currentUser: UserInfo | null = null;\n\n private cdr = inject(ChangeDetectorRef);\n private notifications = inject(MJNotificationService);\n\n /** The currently-displayed session ID (first Running, else most recent). */\n public sessionId = '';\n public kanban: PSKanbanColumns = { running: [], completed: [], pruned: [] };\n public leaderboard: PSLeaderboardEntry[] = [];\n public budget: BudgetGaugeVM[] = [];\n\n /** Pending control action awaiting confirmation (null when no modal is open). */\n public pendingAction: PredictiveStudioExperimentSessionAction | null = null;\n /** Remote Op in flight. */\n public busy = false;\n\n ngOnInit(): void {\n this.selectActiveSession();\n this.rebuild();\n }\n\n // ---- active session selection ----\n\n /** Pick the most relevant session to display: first Running, else the most-recently-created. */\n private selectActiveSession(): void {\n const sessions = this.engine?.Sessions ?? [];\n const running = sessions.find((s) => s.Status === 'Running');\n // Sessions are loaded OrderBy '__mj_CreatedAt DESC', so index 0 is the most recent.\n this.sessionId = (running ?? sessions[0])?.ID ?? '';\n }\n\n public get session(): MJExperimentSessionEntity | undefined {\n return this.engine?.Sessions.find((s) => UUIDsEqual(s.ID, this.sessionId));\n }\n\n public get iterationCountLabel(): string {\n const total = this.kanban.running.length + this.kanban.completed.length + this.kanban.pruned.length;\n return `${total} iteration${total === 1 ? '' : 's'}`;\n }\n\n // ---- derivations ----\n\n private rebuild(): void {\n if (!this.sessionId) {\n this.kanban = { running: [], completed: [], pruned: [] };\n this.leaderboard = [];\n this.budget = [];\n return;\n }\n const rows = this.engine.IterationRowsForSession(this.sessionId);\n this.kanban = groupIterationsToKanban(rows);\n this.leaderboard = deriveLeaderboard(rows);\n this.budget = this.buildBudget(rows);\n }\n\n /** Build the budget gauges from the session's budget JSON caps + actual iteration spend. */\n private buildBudget(rows: { ComputeCost: number | null }[]): BudgetGaugeVM[] {\n const session = this.session;\n if (!session) return [];\n const budgetJson = this.parseBudget(session.Budget);\n const gauges: BudgetGaugeVM[] = [];\n\n const spentCost = rows.reduce((sum, r) => sum + (r.ComputeCost ?? 0), 0);\n const maxCost = budgetJson.maxComputeCost;\n if (maxCost != null && maxCost > 0) {\n gauges.push(this.gauge('Compute cost', spentCost, maxCost, 'var(--ps-gauge-cost)', (v) => `$${v.toFixed(0)}`, `$${maxCost.toFixed(0)} cap`));\n }\n\n const iterations = rows.length;\n const maxIterations = budgetJson.maxIterations;\n if (maxIterations != null && maxIterations > 0) {\n gauges.push(this.gauge('Iterations', iterations, maxIterations, 'var(--ps-gauge-iterations)', (v) => `${v}`, `${maxIterations} trials`));\n }\n return gauges;\n }\n\n /** Construct a single gauge VM (fill clamped to 0..100). */\n private gauge(label: string, value: number, cap: number, color: string, fmt: (v: number) => string, capLabel: string): BudgetGaugeVM {\n const fillPct = Math.max(0, Math.min(100, Math.round((value / cap) * 100)));\n return { label, fillPct, centerText: `${fillPct}%`, value: fmt(value), cap: capLabel, color };\n }\n\n /** Tolerant parse of the session's budget JSON (accepts a few common key spellings). */\n private parseBudget(json: string | null): { maxComputeCost: number | null; maxIterations: number | null } {\n if (!json) return { maxComputeCost: null, maxIterations: null };\n try {\n const parsed = JSON.parse(json) as Record<string, unknown>;\n const num = (...keys: string[]): number | null => {\n for (const k of keys) {\n const v = parsed[k];\n if (typeof v === 'number' && Number.isFinite(v)) return v;\n }\n return null;\n };\n return {\n maxComputeCost: num('maxComputeCost', 'maxCost', 'computeBudget'),\n maxIterations: num('maxIterations', 'maxTrials', 'iterationBudget'),\n };\n } catch {\n return { maxComputeCost: null, maxIterations: null };\n }\n }\n\n // ---- session status chrome ----\n\n public get sessionBadgeClass(): string {\n switch (this.session?.Status) {\n case 'Running': return 'green';\n case 'Paused': return 'amber';\n case 'Cancelled': return 'red';\n case 'Completed': return 'blue';\n default: return 'gray';\n }\n }\n public get sessionDotColor(): string {\n switch (this.session?.Status) {\n case 'Running': return 'var(--mj-status-success)';\n case 'Paused': return 'var(--mj-status-warning)';\n case 'Cancelled': return 'var(--mj-status-error)';\n default: return 'var(--mj-text-muted)';\n }\n }\n\n public statusBadgeClass(status: PSIterationCard['status']): string {\n switch (status) {\n case 'Best': return 'green';\n case 'Completed': return 'gray';\n case 'AwaitingApproval': return 'amber';\n case 'Pruned': return 'red';\n case 'Running': return 'blue';\n }\n }\n\n public statusLabel(status: PSIterationCard['status']): string {\n switch (status) {\n case 'Best': return 'Best';\n case 'AwaitingApproval': return 'Awaiting approval';\n default: return status;\n }\n }\n\n // ---- control availability ----\n\n public get canPause(): boolean {\n return this.session?.Status === 'Running';\n }\n public get canResume(): boolean {\n return this.session?.Status === 'Paused';\n }\n public get canCancel(): boolean {\n const s = this.session?.Status;\n return s === 'Running' || s === 'Paused' || s === 'AwaitingApproval';\n }\n\n // ---- control flow (Remote Op) ----\n\n public requestControl(action: PredictiveStudioExperimentSessionAction): void {\n if (!this.session) return;\n this.pendingAction = action;\n }\n\n public cancelControl(): void {\n if (this.busy) return;\n this.pendingAction = null;\n }\n\n public get pendingTitle(): string {\n switch (this.pendingAction) {\n case 'pause': return 'Pause experiment';\n case 'resume': return 'Resume experiment';\n case 'cancel': return 'Cancel experiment';\n default: return '';\n }\n }\n public get pendingIcon(): string {\n switch (this.pendingAction) {\n case 'pause': return 'fa-solid fa-pause';\n case 'resume': return 'fa-solid fa-play';\n case 'cancel': return 'fa-solid fa-stop';\n default: return 'fa-solid fa-check';\n }\n }\n public get pendingConfirmLabel(): string {\n switch (this.pendingAction) {\n case 'pause': return 'Pause';\n case 'resume': return 'Resume';\n case 'cancel': return 'Cancel run';\n default: return 'Confirm';\n }\n }\n public get pendingMessage(): string {\n const name = escapeHtml(this.session?.Name ?? 'this session');\n switch (this.pendingAction) {\n case 'pause':\n return `Pause <strong>${name}</strong>? The orchestrator stops at the next wave checkpoint; in-flight iterations finish. You can resume later.`;\n case 'resume':\n return `Resume <strong>${name}</strong>? The orchestrator continues searching from where it paused.`;\n case 'cancel':\n return `Cancel <strong>${name}</strong>? This stops the search at the next checkpoint. Completed iterations and the leaderboard are kept, but the session cannot be resumed.`;\n default:\n return '';\n }\n }\n\n /** Run the control Remote Op, then refresh the engine + close on success. */\n public async confirmControl(): Promise<void> {\n if (!this.pendingAction || !this.session || this.busy) return;\n this.busy = true;\n const action = this.pendingAction;\n const sessionId = this.session.ID;\n const name = this.session.Name;\n try {\n const op = new PredictiveStudioControlExperimentSessionOperation();\n const result = await op.Execute(\n { sessionId, action },\n { provider: this.provider ?? undefined, user: this.currentUser ?? undefined },\n );\n if (result.Success) {\n this.notifications.CreateSimpleNotification(\n `${name} → ${result.Output?.status ?? action}`,\n 'success',\n 3500,\n );\n await this.refreshAfterMutation();\n this.pendingAction = null;\n } else {\n this.notifications.CreateSimpleNotification(\n result.ErrorMessage || `Could not ${action} ${name}.`,\n 'error',\n 5000,\n );\n }\n } catch (e) {\n this.notifications.CreateSimpleNotification(\n `${action} failed: ${e instanceof Error ? e.message : String(e)}`,\n 'error',\n 5000,\n );\n } finally {\n this.busy = false;\n this.cdr.detectChanges();\n }\n }\n\n /** Force-refresh the engine's cached sessions/iterations, then rebuild. */\n private async refreshAfterMutation(): Promise<void> {\n await this.engine.Config(true, this.currentUser ?? undefined, this.provider ?? undefined);\n this.selectActiveSession();\n this.rebuild();\n this.cdr.detectChanges();\n }\n}\n\n/** Minimal HTML-escape for interpolating session names into the modal's innerHTML message. */\nfunction escapeHtml(value: string): string {\n return value\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;');\n}\n"]}