import { ChangeDetectorRef, Component, Input, ViewEncapsulation, inject } from '@angular/core';
import { CommonModule } from '@angular/common';
import { MJButtonDirective } from '@memberjunction/ng-ui-components';
import { MJNotificationService } from '@memberjunction/ng-notifications';
import { UUIDsEqual } from '@memberjunction/global';
import { PredictiveStudioPromoteModelOperation, } from '@memberjunction/core-entities';
import { PS_LIFECYCLE_STEPS } from '../predictive-studio.types';
import { PS_FEATURE_DOMINANCE_THRESHOLD, maxFeatureImportance, metricsToDisplay, overfitGap, parseFeatureImportance, parseMetrics, primaryAuc, } from '../predictive-studio.view-models';
import { PSConfirmModalComponent } from './ps-confirm-modal.component';
import * as i0 from "@angular/core";
const _forTrack0 = ($index, $item) => $item.id;
const _forTrack1 = ($index, $item) => $item.key;
const _forTrack2 = ($index, $item) => $item.name;
function PSRegistryComponent_Conditional_1_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 1)(1, "span", 4);
    i0.ɵɵelement(2, "i", 5);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(3, "h3");
    i0.ɵɵtext(4, "No trained models yet");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(5, "p");
    i0.ɵɵtext(6, "Train a pipeline to register your first immutable model. Each successful training run produces a versioned model you can validate, publish, and score with here.");
    i0.ɵɵelementEnd()();
} }
function PSRegistryComponent_Conditional_2_For_9_Template(rf, ctx) { if (rf & 1) {
    const _r1 = i0.ɵɵgetCurrentView();
    i0.ɵɵelementStart(0, "div", 39);
    i0.ɵɵlistener("click", function PSRegistryComponent_Conditional_2_For_9_Template_div_click_0_listener() { const m_r2 = i0.ɵɵrestoreView(_r1).$implicit; const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.select(m_r2.id)); });
    i0.ɵɵelementStart(1, "div", 40);
    i0.ɵɵelement(2, "i", 15);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(3, "div", 41)(4, "div", 42);
    i0.ɵɵtext(5);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(6, "div", 43);
    i0.ɵɵtext(7);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(8, "div", 44)(9, "div", 28);
    i0.ɵɵtext(10);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(11, "div", 45);
    i0.ɵɵtext(12);
    i0.ɵɵelementEnd()()();
} if (rf & 2) {
    const m_r2 = ctx.$implicit;
    const ctx_r2 = i0.ɵɵnextContext(2);
    i0.ɵɵclassProp("sel", m_r2.id === ctx_r2.selectedId)("arc", m_r2.status === "Archived");
    i0.ɵɵadvance();
    i0.ɵɵclassMap(m_r2.iconClass);
    i0.ɵɵadvance(4);
    i0.ɵɵtextInterpolate(m_r2.name);
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate2("v", m_r2.version, " \u00B7 ", m_r2.algorithm);
    i0.ɵɵadvance(3);
    i0.ɵɵtextInterpolate(m_r2.holdoutAuc);
    i0.ɵɵadvance();
    i0.ɵɵclassMap(ctx_r2.statusClass(m_r2.status));
    i0.ɵɵadvance();
    i0.ɵɵtextInterpolate(m_r2.status);
} }
function PSRegistryComponent_Conditional_2_For_30_Conditional_2_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelement(0, "i", 48);
} }
function PSRegistryComponent_Conditional_2_For_30_Conditional_3_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵtext(0);
} if (rf & 2) {
    const $index_r4 = i0.ɵɵnextContext().$index;
    i0.ɵɵtextInterpolate1(" ", $index_r4 + 1, " ");
} }
function PSRegistryComponent_Conditional_2_For_30_Conditional_5_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelement(0, "span", 49);
} }
function PSRegistryComponent_Conditional_2_For_30_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 46)(1, "span", 47);
    i0.ɵɵconditionalCreate(2, PSRegistryComponent_Conditional_2_For_30_Conditional_2_Template, 1, 0, "i", 48)(3, PSRegistryComponent_Conditional_2_For_30_Conditional_3_Template, 1, 1);
    i0.ɵɵelementEnd();
    i0.ɵɵtext(4);
    i0.ɵɵelementEnd();
    i0.ɵɵconditionalCreate(5, PSRegistryComponent_Conditional_2_For_30_Conditional_5_Template, 1, 0, "span", 49);
} if (rf & 2) {
    const step_r5 = ctx.$implicit;
    const ɵ$index_87_r6 = ctx.$index;
    const ɵ$count_87_r7 = ctx.$count;
    const ctx_r2 = i0.ɵɵnextContext(2);
    i0.ɵɵclassProp("done", ctx_r2.stepState(step_r5) === "done")("curr", ctx_r2.stepState(step_r5) === "curr");
    i0.ɵɵadvance(2);
    i0.ɵɵconditional(ctx_r2.stepState(step_r5) === "done" ? 2 : 3);
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate1(" ", step_r5, " ");
    i0.ɵɵadvance();
    i0.ɵɵconditional(!(ɵ$index_87_r6 === ɵ$count_87_r7 - 1) ? 5 : -1);
} }
function PSRegistryComponent_Conditional_2_Conditional_55_For_2_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 50)(1, "span", 25);
    i0.ɵɵtext(2);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(3, "strong");
    i0.ɵɵtext(4);
    i0.ɵɵelementEnd()();
} if (rf & 2) {
    const s_r8 = ctx.$implicit;
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate(s_r8.label);
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate(s_r8.value);
} }
function PSRegistryComponent_Conditional_2_Conditional_55_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 30);
    i0.ɵɵrepeaterCreate(1, PSRegistryComponent_Conditional_2_Conditional_55_For_2_Template, 5, 2, "div", 50, _forTrack1);
    i0.ɵɵelementEnd();
} if (rf & 2) {
    const ctx_r2 = i0.ɵɵnextContext(2);
    i0.ɵɵadvance();
    i0.ɵɵrepeater(ctx_r2.secondaryMetrics);
} }
function PSRegistryComponent_Conditional_2_Conditional_56_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 31);
    i0.ɵɵtext(1, "No secondary metrics recorded for this model.");
    i0.ɵɵelementEnd();
} }
function PSRegistryComponent_Conditional_2_Conditional_57_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 24);
    i0.ɵɵtext(1, " Train\u2013holdout gap of ");
    i0.ɵɵelementStart(2, "strong");
    i0.ɵɵtext(3);
    i0.ɵɵelementEnd();
    i0.ɵɵtext(4);
    i0.ɵɵelementEnd();
} if (rf & 2) {
    const ctx_r2 = i0.ɵɵnextContext(2);
    i0.ɵɵadvance(3);
    i0.ɵɵtextInterpolate(ctx_r2.gapText);
    i0.ɵɵadvance();
    i0.ɵɵtextInterpolate1(" ", ctx_r2.gapVerdict, ". ");
} }
function PSRegistryComponent_Conditional_2_Conditional_65_For_1_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 51)(1, "span", 52);
    i0.ɵɵtext(2);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(3, "div", 53);
    i0.ɵɵelement(4, "span");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(5, "span", 54);
    i0.ɵɵtext(6);
    i0.ɵɵelementEnd()();
} if (rf & 2) {
    const f_r9 = ctx.$implicit;
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate(f_r9.name);
    i0.ɵɵadvance(2);
    i0.ɵɵstyleProp("width", f_r9.pct, "%");
    i0.ɵɵclassProp("warn", f_r9.warning);
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate(f_r9.value);
} }
function PSRegistryComponent_Conditional_2_Conditional_65_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵrepeaterCreate(0, PSRegistryComponent_Conditional_2_Conditional_65_For_1_Template, 7, 6, "div", 51, _forTrack2);
} if (rf & 2) {
    const ctx_r2 = i0.ɵɵnextContext(2);
    i0.ɵɵrepeater(ctx_r2.importance);
} }
function PSRegistryComponent_Conditional_2_Conditional_66_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 32);
    i0.ɵɵtext(1, "No feature-importance data was recorded for this model.");
    i0.ɵɵelementEnd();
} }
function PSRegistryComponent_Conditional_2_Conditional_67_Conditional_3_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "strong");
    i0.ɵɵtext(1, "Leakage flag raised.");
    i0.ɵɵelementEnd();
    i0.ɵɵtext(2, " A single feature dominates \u2014 top importance is ");
    i0.ɵɵelementStart(3, "strong");
    i0.ɵɵtext(4);
    i0.ɵɵelementEnd();
    i0.ɵɵtext(5, ", at/above the ");
    i0.ɵɵelementStart(6, "strong");
    i0.ɵɵtext(7);
    i0.ɵɵelementEnd();
    i0.ɵɵtext(8, " dominance threshold. ");
    i0.ɵɵelementStart(9, "div", 56);
    i0.ɵɵelement(10, "i", 57);
    i0.ɵɵtext(11, " Sign-off with a reason is required to publish this model.");
    i0.ɵɵelementEnd();
} if (rf & 2) {
    const ctx_r2 = i0.ɵɵnextContext(3);
    i0.ɵɵadvance(4);
    i0.ɵɵtextInterpolate2("", ctx_r2.topFeatureName, " at ", ctx_r2.topFeatureValue);
    i0.ɵɵadvance(3);
    i0.ɵɵtextInterpolate(ctx_r2.dominanceThreshold);
} }
function PSRegistryComponent_Conditional_2_Conditional_67_Conditional_4_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "strong");
    i0.ɵɵtext(1, "Leakage gate clear.");
    i0.ɵɵelementEnd();
    i0.ɵɵtext(2, " No single feature dominates \u2014 top importance is ");
    i0.ɵɵelementStart(3, "strong");
    i0.ɵɵtext(4);
    i0.ɵɵelementEnd();
    i0.ɵɵtext(5, ", below the ");
    i0.ɵɵelementStart(6, "strong");
    i0.ɵɵtext(7);
    i0.ɵɵelementEnd();
    i0.ɵɵtext(8, " dominance threshold. ");
    i0.ɵɵelementStart(9, "div", 56);
    i0.ɵɵelement(10, "i", 48);
    i0.ɵɵtext(11, " Sign-off required before any Validated \u2192 Published promotion.");
    i0.ɵɵelementEnd();
} if (rf & 2) {
    const ctx_r2 = i0.ɵɵnextContext(3);
    i0.ɵɵadvance(4);
    i0.ɵɵtextInterpolate2("", ctx_r2.topFeatureName, " at ", ctx_r2.topFeatureValue);
    i0.ɵɵadvance(3);
    i0.ɵɵtextInterpolate(ctx_r2.dominanceThreshold);
} }
function PSRegistryComponent_Conditional_2_Conditional_67_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 55);
    i0.ɵɵelement(1, "i");
    i0.ɵɵelementStart(2, "div");
    i0.ɵɵconditionalCreate(3, PSRegistryComponent_Conditional_2_Conditional_67_Conditional_3_Template, 12, 3)(4, PSRegistryComponent_Conditional_2_Conditional_67_Conditional_4_Template, 12, 3);
    i0.ɵɵelementEnd()();
} if (rf & 2) {
    const ctx_r2 = i0.ɵɵnextContext(2);
    i0.ɵɵclassProp("success", !ctx_r2.leakageFlagged)("warn", ctx_r2.leakageFlagged);
    i0.ɵɵadvance();
    i0.ɵɵclassMap(ctx_r2.leakageFlagged ? "fa-solid fa-triangle-exclamation" : "fa-solid fa-shield-halved");
    i0.ɵɵadvance(2);
    i0.ɵɵconditional(ctx_r2.leakageFlagged ? 3 : 4);
} }
function PSRegistryComponent_Conditional_2_Conditional_72_Template(rf, ctx) { if (rf & 1) {
    const _r10 = i0.ɵɵgetCurrentView();
    i0.ɵɵelementStart(0, "button", 58);
    i0.ɵɵlistener("click", function PSRegistryComponent_Conditional_2_Conditional_72_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r10); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.requestPromote("Validated")); });
    i0.ɵɵelement(1, "i", 59);
    i0.ɵɵtext(2, " Mark Validated");
    i0.ɵɵelementEnd();
} }
function PSRegistryComponent_Conditional_2_Conditional_73_Template(rf, ctx) { if (rf & 1) {
    const _r11 = i0.ɵɵgetCurrentView();
    i0.ɵɵelementStart(0, "button", 60);
    i0.ɵɵlistener("click", function PSRegistryComponent_Conditional_2_Conditional_73_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r11); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.requestPromote("Published")); });
    i0.ɵɵelement(1, "i", 61);
    i0.ɵɵtext(2, " Promote to Published");
    i0.ɵɵelementEnd();
} }
function PSRegistryComponent_Conditional_2_Conditional_74_Template(rf, ctx) { if (rf & 1) {
    const _r12 = i0.ɵɵgetCurrentView();
    i0.ɵɵelementStart(0, "button", 62);
    i0.ɵɵlistener("click", function PSRegistryComponent_Conditional_2_Conditional_74_Template_button_click_0_listener() { i0.ɵɵrestoreView(_r12); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.requestPromote("Archived")); });
    i0.ɵɵelement(1, "i", 63);
    i0.ɵɵtext(2, " Archive");
    i0.ɵɵelementEnd();
} }
function PSRegistryComponent_Conditional_2_Template(rf, ctx) { if (rf & 1) {
    i0.ɵɵelementStart(0, "div", 2)(1, "div", 6)(2, "div", 7)(3, "h3");
    i0.ɵɵtext(4, "ML Models");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(5, "span", 8);
    i0.ɵɵtext(6);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(7, "div", 9);
    i0.ɵɵrepeaterCreate(8, PSRegistryComponent_Conditional_2_For_9_Template, 13, 13, "div", 10, _forTrack0);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(10, "div", 11)(11, "div", 12)(12, "div", 13)(13, "div", 14);
    i0.ɵɵelement(14, "i", 15);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(15, "div", 16)(16, "h2", 17);
    i0.ɵɵtext(17);
    i0.ɵɵelementStart(18, "span", 18);
    i0.ɵɵtext(19);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(20, "div", 19);
    i0.ɵɵtext(21);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(22, "span", 20);
    i0.ɵɵtext(23);
    i0.ɵɵelementEnd()()();
    i0.ɵɵelementStart(24, "div", 12)(25, "div", 21)(26, "div", 22);
    i0.ɵɵtext(27, "Lifecycle");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(28, "div", 23);
    i0.ɵɵrepeaterCreate(29, PSRegistryComponent_Conditional_2_For_30_Template, 6, 7, null, null, i0.ɵɵrepeaterTrackByIdentity);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(31, "div", 24);
    i0.ɵɵtext(32, " Models are immutable once registered; promotion only changes lifecycle state, never weights. ");
    i0.ɵɵelementEnd()()();
    i0.ɵɵelementStart(33, "div", 12)(34, "div", 7)(35, "h3");
    i0.ɵɵtext(36, "Performance");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(37, "span", 25);
    i0.ɵɵtext(38, "Holdout = held-out test fold, never seen in training");
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(39, "div", 21)(40, "div", 26)(41, "div", 27)(42, "div", 22);
    i0.ɵɵtext(43, "Train AUC");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(44, "div", 28);
    i0.ɵɵtext(45);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(46, "div", 25);
    i0.ɵɵtext(47, "in-sample \u00B7 optimistic");
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(48, "div", 29)(49, "div", 22);
    i0.ɵɵtext(50, "Holdout AUC");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(51, "div", 28);
    i0.ɵɵtext(52);
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(53, "div", 25);
    i0.ɵɵtext(54, "out-of-sample \u00B7 the honest number");
    i0.ɵɵelementEnd()()();
    i0.ɵɵconditionalCreate(55, PSRegistryComponent_Conditional_2_Conditional_55_Template, 3, 0, "div", 30)(56, PSRegistryComponent_Conditional_2_Conditional_56_Template, 2, 0, "div", 31);
    i0.ɵɵconditionalCreate(57, PSRegistryComponent_Conditional_2_Conditional_57_Template, 5, 2, "div", 24);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(58, "div", 12)(59, "div", 7)(60, "h3");
    i0.ɵɵtext(61, "Feature Importance");
    i0.ɵɵelementEnd();
    i0.ɵɵelementStart(62, "span", 25);
    i0.ɵɵtext(63);
    i0.ɵɵelementEnd()();
    i0.ɵɵelementStart(64, "div", 21);
    i0.ɵɵconditionalCreate(65, PSRegistryComponent_Conditional_2_Conditional_65_Template, 2, 0)(66, PSRegistryComponent_Conditional_2_Conditional_66_Template, 2, 0, "div", 32);
    i0.ɵɵelementEnd()();
    i0.ɵɵconditionalCreate(67, PSRegistryComponent_Conditional_2_Conditional_67_Template, 5, 7, "div", 33);
    i0.ɵɵelementStart(68, "div", 12)(69, "div", 34)(70, "span", 35);
    i0.ɵɵtext(71);
    i0.ɵɵelementEnd();
    i0.ɵɵconditionalCreate(72, PSRegistryComponent_Conditional_2_Conditional_72_Template, 3, 0, "button", 36);
    i0.ɵɵconditionalCreate(73, PSRegistryComponent_Conditional_2_Conditional_73_Template, 3, 0, "button", 37);
    i0.ɵɵconditionalCreate(74, PSRegistryComponent_Conditional_2_Conditional_74_Template, 3, 0, "button", 38);
    i0.ɵɵelementEnd()()()();
} if (rf & 2) {
    const ctx_r2 = i0.ɵɵnextContext();
    i0.ɵɵadvance(6);
    i0.ɵɵtextInterpolate(ctx_r2.models.length);
    i0.ɵɵadvance(2);
    i0.ɵɵrepeater(ctx_r2.models);
    i0.ɵɵadvance(9);
    i0.ɵɵtextInterpolate1("", ctx_r2.selected.name, " ");
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate1("v", ctx_r2.selected.version);
    i0.ɵɵadvance(2);
    i0.ɵɵtextInterpolate2("", ctx_r2.selected.algorithm, "", ctx_r2.targetSuffix, " \u00B7 immutable snapshot");
    i0.ɵɵadvance();
    i0.ɵɵclassMap(ctx_r2.statusBadgeClass(ctx_r2.selected.status));
    i0.ɵɵadvance();
    i0.ɵɵtextInterpolate(ctx_r2.selected.status);
    i0.ɵɵadvance(6);
    i0.ɵɵrepeater(ctx_r2.lifecycleSteps);
    i0.ɵɵadvance(16);
    i0.ɵɵtextInterpolate(ctx_r2.trainAuc);
    i0.ɵɵadvance(7);
    i0.ɵɵtextInterpolate(ctx_r2.selected.holdoutAuc);
    i0.ɵɵadvance(3);
    i0.ɵɵconditional(ctx_r2.secondaryMetrics.length > 0 ? 55 : 56);
    i0.ɵɵadvance(2);
    i0.ɵɵconditional(ctx_r2.gapText ? 57 : -1);
    i0.ɵɵadvance(6);
    i0.ɵɵtextInterpolate(ctx_r2.importanceCaption);
    i0.ɵɵadvance(2);
    i0.ɵɵconditional(ctx_r2.importance.length > 0 ? 65 : 66);
    i0.ɵɵadvance(2);
    i0.ɵɵconditional(ctx_r2.importance.length > 0 ? 67 : -1);
    i0.ɵɵadvance(4);
    i0.ɵɵtextInterpolate(ctx_r2.actionHint);
    i0.ɵɵadvance();
    i0.ɵɵconditional(ctx_r2.canValidate ? 72 : -1);
    i0.ɵɵadvance();
    i0.ɵɵconditional(ctx_r2.canPublish ? 73 : -1);
    i0.ɵɵadvance();
    i0.ɵɵconditional(ctx_r2.canArchive ? 74 : -1);
} }
function PSRegistryComponent_Conditional_3_Template(rf, ctx) { if (rf & 1) {
    const _r13 = i0.ɵɵgetCurrentView();
    i0.ɵɵelementStart(0, "ps-confirm-modal", 64);
    i0.ɵɵlistener("confirmed", function PSRegistryComponent_Conditional_3_Template_ps_confirm_modal_confirmed_0_listener($event) { i0.ɵɵrestoreView(_r13); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.confirmPromote($event)); })("cancelled", function PSRegistryComponent_Conditional_3_Template_ps_confirm_modal_cancelled_0_listener() { i0.ɵɵrestoreView(_r13); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.cancelPromote()); });
    i0.ɵɵelement(1, "div", 65);
    i0.ɵɵelementEnd();
} if (rf & 2) {
    const ctx_r2 = i0.ɵɵnextContext();
    i0.ɵɵproperty("title", ctx_r2.pendingTitle)("icon", ctx_r2.pendingIcon)("confirmIcon", ctx_r2.pendingIcon)("confirmLabel", ctx_r2.pendingConfirmLabel)("variant", ctx_r2.pendingVariant)("showReason", ctx_r2.pending.leakageFlagged || ctx_r2.pending.targetStatus === "Archived")("reasonRequired", ctx_r2.pending.leakageFlagged)("reasonLabel", ctx_r2.pending.leakageFlagged ? "Leakage sign-off reason" : "Reason (optional)")("reasonPlaceholder", ctx_r2.pending.leakageFlagged ? "Explain why this model is safe to publish despite the leakage flag\u2026" : "Add an optional note for the record\u2026")("busy", ctx_r2.busy);
    i0.ɵɵadvance();
    i0.ɵɵproperty("innerHTML", ctx_r2.pendingMessage, i0.ɵɵsanitizeHtml);
} }
/**
 * Model Registry panel: master list of trained ML Models + a rich detail pane with a lifecycle stepper
 * (Draft → Validated → Published → Archived), train-vs-holdout metric comparison, feature-importance
 * bars, a leakage sign-off gate, and lifecycle actions. Fully live against `MJ: ML Models`:
 *
 * - **Feature importance** is parsed from `model.FeatureImportance` (sorted by |value|, top 6, bars).
 * - **Performance metrics** come from `model.Metrics` (train) + `model.HoldoutMetrics` (holdout) —
 *   only the metrics actually recorded are shown.
 * - **Promote / Archive** call the {@link PredictiveStudioPromoteModelOperation} Remote Op behind a
 *   confirmation modal. On a leakage-flagged model, the modal surfaces the sign-off requirement and
 *   captures a required reason; on success the engine refreshes and the list re-renders reactively.
 *
 * 100% entity-agnostic — model names derive from the producing pipeline, never any business entity.
 */
export class PSRegistryComponent {
    engine;
    /** Provider to route the promote Remote Op + engine refresh through (multi-provider correctness). */
    provider = null;
    /** Acting user for the engine refresh after a mutation. */
    currentUser = null;
    cdr = inject(ChangeDetectorRef);
    notifications = inject(MJNotificationService);
    models = [];
    selectedId = '';
    lifecycleSteps = PS_LIFECYCLE_STEPS;
    dominanceThreshold = PS_FEATURE_DOMINANCE_THRESHOLD.toFixed(2);
    /** Pending confirmation (null when no modal is open). */
    pending = null;
    /** Remote Op in flight — drives the modal spinner + disables the buttons. */
    busy = false;
    ngOnInit() {
        this.buildModels();
        this.selectedId = this.models[0]?.id ?? '';
    }
    // ---- selection + master list ----
    select(id) {
        this.selectedId = id;
    }
    get selected() {
        return this.models.find((m) => m.id === this.selectedId) ?? this.models[0] ?? this.placeholder();
    }
    get selectedEntity() {
        return this.engine?.Models.find((m) => UUIDsEqual(m.ID, this.selectedId));
    }
    // ---- live detail: feature importance + metrics ----
    /** Top-6 feature importance bars parsed live from the selected model's `FeatureImportance` JSON. */
    get importance() {
        return parseFeatureImportance(this.selectedEntity?.FeatureImportance, 6);
    }
    get importanceCaption() {
        const n = this.importance.length;
        return n === 0 ? 'no data' : `normalized · top ${n}`;
    }
    get topFeatureName() {
        return this.importance[0]?.name ?? '—';
    }
    get topFeatureValue() {
        return this.importance[0]?.value ?? '—';
    }
    /** True when a single feature's |importance| meets/exceeds the dominance threshold. */
    get leakageFlagged() {
        const max = maxFeatureImportance(this.selectedEntity?.FeatureImportance);
        return max != null && max >= PS_FEATURE_DOMINANCE_THRESHOLD;
    }
    /** Holdout AUC hero number, formatted, or '—'. */
    get trainAuc() {
        const train = parseMetrics(this.selectedEntity?.Metrics).AUC;
        return train != null ? train.toFixed(3) : '—';
    }
    /** Secondary metric tiles (precision/recall/F1/log-loss/…) — only those actually recorded. */
    get secondaryMetrics() {
        const e = this.selectedEntity;
        if (!e)
            return [];
        // Prefer holdout metrics for the honest secondary numbers; fall back to training metrics.
        const holdout = metricsToDisplay(parseMetrics(e.HoldoutMetrics));
        return holdout.length > 0 ? holdout : metricsToDisplay(parseMetrics(e.Metrics));
    }
    /** The formatted train–holdout overfit gap, or '' when not computable. */
    get gapText() {
        const gap = overfitGap(this.selectedEntity ?? { Metrics: null, HoldoutMetrics: null });
        return gap == null ? '' : Math.abs(gap).toFixed(3);
    }
    get gapVerdict() {
        const gap = overfitGap(this.selectedEntity ?? { Metrics: null, HoldoutMetrics: null });
        if (gap == null)
            return '';
        return gap > 0.1 ? 'is wide — watch for overfitting' : 'is within tolerance — no overfitting flag';
    }
    get targetSuffix() {
        const target = this.selectedEntity?.TargetVariable;
        return target ? ` · predicts ${target}` : '';
    }
    // ---- lifecycle stepper ----
    statusClass(status) {
        switch (status) {
            case 'Published': return 'pub';
            case 'Validated': return 'val';
            case 'Draft': return 'dr';
            case 'Archived': return 'arc';
            default: return 'dr';
        }
    }
    statusBadgeClass(status) {
        switch (status) {
            case 'Published': return 'green';
            case 'Validated': return 'blue';
            case 'Archived': return 'gray';
            default: return 'amber';
        }
    }
    /** Stepper state for a lifecycle step relative to the selected model's status. */
    stepState(step) {
        const order = PS_LIFECYCLE_STEPS.indexOf(this.selected.status);
        const idx = PS_LIFECYCLE_STEPS.indexOf(step);
        if (order < 0)
            return 'todo';
        if (idx < order)
            return 'done';
        if (idx === order)
            return 'curr';
        return 'todo';
    }
    // ---- promote/archive availability ----
    get canValidate() {
        return this.selected.status === 'Draft';
    }
    get canPublish() {
        return this.selected.status === 'Validated' || this.selected.status === 'Draft';
    }
    get canArchive() {
        return this.selected.status === 'Published' || this.selected.status === 'Validated';
    }
    get actionHint() {
        switch (this.selected.status) {
            case 'Published': return 'This model is live. Archiving detaches its scoring bindings.';
            case 'Validated': return 'Validated and ready to publish — publishing makes it available for scoring.';
            case 'Draft': return 'Newly registered. Validate it before publishing to production.';
            case 'Archived': return 'Archived — superseded by a newer model.';
            default: return '';
        }
    }
    // ---- promote/archive flow (Remote Op) ----
    /** Open the confirmation modal for a lifecycle transition. */
    requestPromote(targetStatus) {
        const entity = this.selectedEntity;
        if (!entity)
            return;
        this.pending = {
            modelId: entity.ID,
            modelName: this.selected.name,
            targetStatus,
            leakageFlagged: targetStatus === 'Published' && this.leakageFlagged,
        };
    }
    cancelPromote() {
        if (this.busy)
            return;
        this.pending = null;
    }
    get pendingTitle() {
        if (!this.pending)
            return '';
        return this.pending.targetStatus === 'Archived' ? 'Archive model' : `Promote to ${this.pending.targetStatus}`;
    }
    get pendingIcon() {
        if (!this.pending)
            return 'fa-solid fa-check';
        return this.pending.targetStatus === 'Archived' ? 'fa-solid fa-box-archive' : 'fa-solid fa-arrow-up';
    }
    get pendingConfirmLabel() {
        if (!this.pending)
            return 'Confirm';
        return this.pending.targetStatus === 'Archived' ? 'Archive' : `Promote to ${this.pending.targetStatus}`;
    }
    get pendingVariant() {
        if (!this.pending)
            return 'info';
        if (this.pending.targetStatus === 'Archived')
            return 'warn';
        return this.pending.leakageFlagged ? 'warn' : 'info';
    }
    get pendingMessage() {
        if (!this.pending)
            return '';
        const name = escapeHtml(this.pending.modelName);
        if (this.pending.targetStatus === 'Archived') {
            return `Archive <strong>${name}</strong>? This detaches any active scoring bindings and removes it from production scoring. The model artifact remains immutable and recoverable.`;
        }
        if (this.pending.leakageFlagged) {
            return `<strong>${name}</strong> is flagged for possible target leakage. Publishing requires an explicit sign-off — confirm you understand the risk and provide a reason below.`;
        }
        return `Promote <strong>${name}</strong> to <strong>${this.pending.targetStatus}</strong>? This changes only the lifecycle state — the trained weights never change.`;
    }
    /** Run the promote Remote Op, then refresh the engine + close on success. */
    async confirmPromote(reason) {
        if (!this.pending || this.busy)
            return;
        this.busy = true;
        const { modelId, modelName, targetStatus, leakageFlagged } = this.pending;
        try {
            const op = new PredictiveStudioPromoteModelOperation();
            const result = await op.Execute({ modelId, targetStatus, signOff: leakageFlagged ? true : undefined, reason: reason || undefined }, { provider: this.provider ?? undefined, user: this.currentUser ?? undefined });
            if (result.Success && result.Output?.promoted) {
                this.notifications.CreateSimpleNotification(`${modelName} → ${result.Output.status}`, 'success', 3500);
                await this.refreshAfterMutation();
                this.pending = null;
            }
            else {
                this.notifications.CreateSimpleNotification(result.ErrorMessage || `Could not promote ${modelName} (status: ${result.Output?.status ?? 'unchanged'}).`, 'error', 5000);
            }
        }
        catch (e) {
            this.notifications.CreateSimpleNotification(`Promotion failed: ${e instanceof Error ? e.message : String(e)}`, 'error', 5000);
        }
        finally {
            this.busy = false;
            this.cdr.detectChanges();
        }
    }
    /** Force-refresh the engine's cached models, then rebuild the master list. */
    async refreshAfterMutation() {
        const provider = this.provider ?? undefined;
        await this.engine.Config(true, this.currentUser ?? undefined, provider);
        this.buildModels();
        if (!this.models.some((m) => m.id === this.selectedId)) {
            this.selectedId = this.models[0]?.id ?? '';
        }
        this.cdr.detectChanges();
    }
    // ---- master-list view-models ----
    buildModels() {
        this.models = (this.engine?.Models ?? []).map((m) => this.toVM(m));
    }
    toVM(m) {
        const holdout = primaryAuc(m);
        return {
            id: m.ID,
            name: this.engine.ModelDisplayName(m),
            version: m.Version,
            algorithm: this.engine.AlgorithmName(m.AlgorithmID),
            holdoutAuc: holdout != null ? holdout.toFixed(3) : '—',
            status: m.Status,
            iconClass: 'xgb',
        };
    }
    placeholder() {
        return { id: '', name: 'No model', version: 0, algorithm: '—', holdoutAuc: '—', status: 'Draft', iconClass: 'xgb' };
    }
    static ɵfac = function PSRegistryComponent_Factory(__ngFactoryType__) { return new (__ngFactoryType__ || PSRegistryComponent)(); };
    static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: PSRegistryComponent, selectors: [["ps-registry"]], inputs: { engine: "engine", provider: "provider", currentUser: "currentUser" }, decls: 4, vars: 2, consts: [["data-testid", "ps-registry-panel", 1, "ps-panel", "ps-registry"], ["data-testid", "ps-registry-empty", 1, "ps-empty"], [1, "md-layout"], [3, "title", "icon", "confirmIcon", "confirmLabel", "variant", "showReason", "reasonRequired", "reasonLabel", "reasonPlaceholder", "busy"], [1, "ps-empty-ico"], [1, "fa-solid", "fa-cubes"], ["data-testid", "ps-registry-list", 1, "ps-card", "mlist"], [1, "ps-card-head"], [1, "ps-badge", "gray"], [1, "ps-card-body", 2, "padding", "8px"], ["data-testid", "ps-registry-row", 1, "mrow", 3, "sel", "arc"], ["data-testid", "ps-registry-detail", 1, "ps-col", "detail"], [1, "ps-card"], [1, "ps-card-body", "dh"], [1, "big-ico"], [1, "fa-solid", "fa-cube"], [2, "flex", "1"], ["data-testid", "ps-registry-detail-name"], [1, "ps-tag", "ps-mono"], [1, "ps-muted", "ps-small", "sub"], [1, "ps-badge"], [1, "ps-card-body"], [1, "ps-section-title"], [1, "ps-stepper"], [1, "ps-small", "ps-muted", 2, "margin-top", "10px"], [1, "ps-muted", "ps-small"], [1, "metric-pair"], [1, "mtile"], [1, "v"], [1, "mtile", "honest"], [1, "stat-bar"], [1, "ps-small", "ps-muted", 2, "margin-top", "6px"], [1, "ps-small", "ps-muted"], [1, "ps-callout", "gate", 3, "success", "warn"], [1, "ps-card-body", "ps-row", 2, "align-items", "center"], [1, "ps-muted", "ps-small", 2, "flex", "1"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-registry-validate"], ["mjButton", "", "variant", "primary", "size", "sm", "data-testid", "ps-registry-promote"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-registry-archive"], ["data-testid", "ps-registry-row", 1, "mrow", 3, "click"], [1, "ico"], [2, "flex", "1", "min-width", "0"], [1, "nm"], [1, "ln2", "ps-muted", "ps-small"], [1, "auc"], [1, "st"], [1, "ps-step"], [1, "pip"], [1, "fa-solid", "fa-check"], [1, "ln"], [1, "b"], [1, "ps-fbar"], [1, "name", "ps-small"], [1, "track"], [1, "ps-mono"], [1, "ps-callout", "gate"], [1, "ps-small", 2, "margin-top", "6px"], [1, "fa-solid", "fa-pen"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-registry-validate", 3, "click"], [1, "fa-solid", "fa-circle-check"], ["mjButton", "", "variant", "primary", "size", "sm", "data-testid", "ps-registry-promote", 3, "click"], [1, "fa-solid", "fa-arrow-up"], ["mjButton", "", "variant", "secondary", "size", "sm", "data-testid", "ps-registry-archive", 3, "click"], [1, "fa-solid", "fa-box-archive"], [3, "confirmed", "cancelled", "title", "icon", "confirmIcon", "confirmLabel", "variant", "showReason", "reasonRequired", "reasonLabel", "reasonPlaceholder", "busy"], [3, "innerHTML"]], template: function PSRegistryComponent_Template(rf, ctx) { if (rf & 1) {
            i0.ɵɵelementStart(0, "div", 0);
            i0.ɵɵconditionalCreate(1, PSRegistryComponent_Conditional_1_Template, 7, 0, "div", 1)(2, PSRegistryComponent_Conditional_2_Template, 75, 19, "div", 2);
            i0.ɵɵconditionalCreate(3, PSRegistryComponent_Conditional_3_Template, 2, 11, "ps-confirm-modal", 3);
            i0.ɵɵelementEnd();
        } if (rf & 2) {
            i0.ɵɵadvance();
            i0.ɵɵconditional(ctx.models.length === 0 ? 1 : 2);
            i0.ɵɵadvance(2);
            i0.ɵɵconditional(ctx.pending ? 3 : -1);
        } }, dependencies: [CommonModule, MJButtonDirective, PSConfirmModalComponent], 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-registry .md-layout { display: grid; grid-template-columns: 300px 1fr; gap: 18px; align-items: start; }\n@media (max-width: 1080px) { .ps-registry .md-layout { grid-template-columns: 1fr; } }\n\n.ps-registry .mlist { position: sticky; top: 0; }\n.ps-registry .mrow {\n  display: flex; align-items: center; gap: 10px; padding: 10px; border-radius: 8px; cursor: pointer; transition: background .12s;\n}\n.ps-registry .mrow:hover { background: var(--mj-bg-surface-hover); }\n.ps-registry .mrow.sel { background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface)); box-shadow: inset 3px 0 0 var(--mj-brand-primary); }\n.ps-registry .mrow.arc { opacity: .72; }\n.ps-registry .mrow .ico {\n  width: 30px; height: 30px; border-radius: 7px; display: grid; place-items: center; color: var(--mj-text-inverse); font-size: 13px; flex: 0 0 auto;\n}\n.ps-registry .mrow .ico.xgb { background: var(--ps-node-feature); }\n.ps-registry .mrow .ico.lgbm { background: var(--ps-node-algorithm); }\n.ps-registry .mrow .ico.rf { background: var(--ps-node-source); }\n.ps-registry .mrow .nm { font-weight: 600; color: var(--mj-text-primary); font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.ps-registry .mrow .auc { text-align: right; flex: 0 0 auto; }\n.ps-registry .mrow .auc .v { font-weight: 700; color: var(--mj-text-primary); font-variant-numeric: tabular-nums; }\n.ps-registry .mrow .auc .st { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; }\n.ps-registry .mrow .auc .st.pub { color: var(--mj-status-success-text); }\n.ps-registry .mrow .auc .st.val { color: var(--mj-status-info-text); }\n.ps-registry .mrow .auc .st.dr { color: var(--mj-text-muted); }\n.ps-registry .mrow .auc .st.arc { color: var(--mj-text-disabled); }\n\n/* detail */\n.ps-registry .dh { display: flex; align-items: center; gap: 14px; }\n.ps-registry .big-ico {\n  width: 48px; height: 48px; border-radius: 12px; display: grid; place-items: center;\n  background: var(--ps-node-feature); color: var(--mj-text-inverse); font-size: 20px; flex: 0 0 auto;\n}\n.ps-registry .dh h2 { font-size: 18px; color: var(--mj-text-primary); display: flex; align-items: center; gap: 8px; }\n.ps-registry .dh .sub { margin-top: 2px; }\n\n.ps-registry .metric-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }\n.ps-registry .mtile { border: 1px solid var(--mj-border-default); border-radius: 9px; padding: 14px; }\n.ps-registry .mtile .v { font-size: 28px; font-weight: 720; color: var(--mj-text-primary); letter-spacing: -.02em; }\n.ps-registry .mtile.honest { border-color: var(--mj-brand-primary); background: color-mix(in srgb, var(--mj-brand-primary) 6%, var(--mj-bg-surface)); }\n.ps-registry .mtile.honest .v { color: var(--mj-brand-primary); }\n.ps-registry .stat-bar { display: flex; gap: 10px; margin-top: 14px; flex-wrap: wrap; }\n.ps-registry .stat-bar .b { display: flex; flex-direction: column; gap: 2px; padding: 8px 12px; border: 1px solid var(--mj-border-subtle); border-radius: 8px; flex: 1; min-width: 90px; }\n.ps-registry .stat-bar .b strong { color: var(--mj-text-primary); font-size: 15px; }\n\n.ps-registry .gate { align-items: flex-start; }\n.ps-registry .gate i { font-size: 20px; }\n\n.ps-registry .lineage { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }\n.ps-registry .ln-card { border: 1px solid var(--mj-border-default); border-radius: 9px; padding: 10px 12px; min-width: 120px; }\n.ps-registry .ln-card.cur { border-color: var(--mj-brand-primary); background: color-mix(in srgb, var(--mj-brand-primary) 6%, var(--mj-bg-surface)); }\n.ps-registry .ln-card .t { font-size: 10.5px; text-transform: uppercase; letter-spacing: .04em; color: var(--mj-text-muted); font-weight: 700; }\n.ps-registry .ln-card .n { font-weight: 650; color: var(--mj-text-primary); font-size: 13px; margin: 2px 0; }\n.ps-registry .ln-arrow { color: var(--mj-text-disabled); }\n"], encapsulation: 2 });
}
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(PSRegistryComponent, [{
        type: Component,
        args: [{ standalone: true, selector: 'ps-registry', imports: [CommonModule, MJButtonDirective, PSConfirmModalComponent], encapsulation: ViewEncapsulation.None, template: `
    <div class="ps-panel ps-registry" data-testid="ps-registry-panel">
      @if (models.length === 0) {
        <div class="ps-empty" data-testid="ps-registry-empty">
          <span class="ps-empty-ico"><i class="fa-solid fa-cubes"></i></span>
          <h3>No trained models yet</h3>
          <p>Train a pipeline to register your first immutable model. Each successful training run produces a versioned model you can validate, publish, and score with here.</p>
        </div>
      } @else {
        <div class="md-layout">
          <!-- master list -->
          <div class="ps-card mlist" data-testid="ps-registry-list">
            <div class="ps-card-head"><h3>ML Models</h3><span class="ps-badge gray">{{ models.length }}</span></div>
            <div class="ps-card-body" style="padding:8px">
              @for (m of models; track m.id) {
                <div class="mrow" data-testid="ps-registry-row" [class.sel]="m.id === selectedId" [class.arc]="m.status === 'Archived'" (click)="select(m.id)">
                  <div class="ico" [class]="m.iconClass"><i class="fa-solid fa-cube"></i></div>
                  <div style="flex:1;min-width:0">
                    <div class="nm">{{ m.name }}</div>
                    <div class="ln2 ps-muted ps-small">v{{ m.version }} · {{ m.algorithm }}</div>
                  </div>
                  <div class="auc">
                    <div class="v">{{ m.holdoutAuc }}</div>
                    <div class="st" [class]="statusClass(m.status)">{{ m.status }}</div>
                  </div>
                </div>
              }
            </div>
          </div>

          <!-- detail -->
          <div class="ps-col detail" data-testid="ps-registry-detail">
            <div class="ps-card">
              <div class="ps-card-body dh">
                <div class="big-ico"><i class="fa-solid fa-cube"></i></div>
                <div style="flex:1">
                  <h2 data-testid="ps-registry-detail-name">{{ selected.name }} <span class="ps-tag ps-mono">v{{ selected.version }}</span></h2>
                  <div class="ps-muted ps-small sub">{{ selected.algorithm }}{{ targetSuffix }} · immutable snapshot</div>
                </div>
                <span class="ps-badge" [class]="statusBadgeClass(selected.status)">{{ selected.status }}</span>
              </div>
            </div>

            <!-- lifecycle -->
            <div class="ps-card">
              <div class="ps-card-body">
                <div class="ps-section-title">Lifecycle</div>
                <div class="ps-stepper">
                  @for (step of lifecycleSteps; track step; let last = $last) {
                    <div class="ps-step" [class.done]="stepState(step) === 'done'" [class.curr]="stepState(step) === 'curr'">
                      <span class="pip">@if (stepState(step) === 'done') { <i class="fa-solid fa-check"></i> } @else { {{ $index + 1 }} }</span>
                      {{ step }}
                    </div>
                    @if (!last) { <span class="ln"></span> }
                  }
                </div>
                <div class="ps-small ps-muted" style="margin-top:10px">
                  Models are immutable once registered; promotion only changes lifecycle state, never weights.
                </div>
              </div>
            </div>

            <!-- performance -->
            <div class="ps-card">
              <div class="ps-card-head"><h3>Performance</h3><span class="ps-muted ps-small">Holdout = held-out test fold, never seen in training</span></div>
              <div class="ps-card-body">
                <div class="metric-pair">
                  <div class="mtile"><div class="ps-section-title">Train AUC</div><div class="v">{{ trainAuc }}</div><div class="ps-muted ps-small">in-sample · optimistic</div></div>
                  <div class="mtile honest"><div class="ps-section-title">Holdout AUC</div><div class="v">{{ selected.holdoutAuc }}</div><div class="ps-muted ps-small">out-of-sample · the honest number</div></div>
                </div>
                @if (secondaryMetrics.length > 0) {
                  <div class="stat-bar">
                    @for (s of secondaryMetrics; track s.key) {
                      <div class="b"><span class="ps-muted ps-small">{{ s.label }}</span><strong>{{ s.value }}</strong></div>
                    }
                  </div>
                } @else {
                  <div class="ps-small ps-muted" style="margin-top:6px">No secondary metrics recorded for this model.</div>
                }
                @if (gapText) {
                  <div class="ps-small ps-muted" style="margin-top:10px">
                    Train–holdout gap of <strong>{{ gapText }}</strong> {{ gapVerdict }}.
                  </div>
                }
              </div>
            </div>

            <!-- feature importance -->
            <div class="ps-card">
              <div class="ps-card-head"><h3>Feature Importance</h3><span class="ps-muted ps-small">{{ importanceCaption }}</span></div>
              <div class="ps-card-body">
                @if (importance.length > 0) {
                  @for (f of importance; track f.name) {
                    <div class="ps-fbar">
                      <span class="name ps-small">{{ f.name }}</span>
                      <div class="track"><span [class.warn]="f.warning" [style.width.%]="f.pct"></span></div>
                      <span class="ps-mono">{{ f.value }}</span>
                    </div>
                  }
                } @else {
                  <div class="ps-small ps-muted">No feature-importance data was recorded for this model.</div>
                }
              </div>
            </div>

            <!-- leakage sign-off gate -->
            @if (importance.length > 0) {
              <div class="ps-callout gate" [class.success]="!leakageFlagged" [class.warn]="leakageFlagged">
                <i [class]="leakageFlagged ? 'fa-solid fa-triangle-exclamation' : 'fa-solid fa-shield-halved'"></i>
                <div>
                  @if (leakageFlagged) {
                    <strong>Leakage flag raised.</strong> A single feature dominates — top importance is
                    <strong>{{ topFeatureName }} at {{ topFeatureValue }}</strong>, at/above the
                    <strong>{{ dominanceThreshold }}</strong> dominance threshold.
                    <div class="ps-small" style="margin-top:6px"><i class="fa-solid fa-pen"></i> Sign-off with a reason is required to publish this model.</div>
                  } @else {
                    <strong>Leakage gate clear.</strong> No single feature dominates — top importance is
                    <strong>{{ topFeatureName }} at {{ topFeatureValue }}</strong>, below the
                    <strong>{{ dominanceThreshold }}</strong> dominance threshold.
                    <div class="ps-small" style="margin-top:6px"><i class="fa-solid fa-check"></i> Sign-off required before any Validated → Published promotion.</div>
                  }
                </div>
              </div>
            }

            <!-- actions -->
            <div class="ps-card">
              <div class="ps-card-body ps-row" style="align-items:center">
                <span class="ps-muted ps-small" style="flex:1">{{ actionHint }}</span>
                @if (canValidate) {
                  <button mjButton variant="secondary" size="sm" data-testid="ps-registry-validate" (click)="requestPromote('Validated')"><i class="fa-solid fa-circle-check"></i> Mark Validated</button>
                }
                @if (canPublish) {
                  <button mjButton variant="primary" size="sm" data-testid="ps-registry-promote" (click)="requestPromote('Published')"><i class="fa-solid fa-arrow-up"></i> Promote to Published</button>
                }
                @if (canArchive) {
                  <button mjButton variant="secondary" size="sm" data-testid="ps-registry-archive" (click)="requestPromote('Archived')"><i class="fa-solid fa-box-archive"></i> Archive</button>
                }
              </div>
            </div>
          </div>
        </div>
      }

      @if (pending) {
        <ps-confirm-modal
          [title]="pendingTitle"
          [icon]="pendingIcon"
          [confirmIcon]="pendingIcon"
          [confirmLabel]="pendingConfirmLabel"
          [variant]="pendingVariant"
          [showReason]="pending.leakageFlagged || pending.targetStatus === 'Archived'"
          [reasonRequired]="pending.leakageFlagged"
          [reasonLabel]="pending.leakageFlagged ? 'Leakage sign-off reason' : 'Reason (optional)'"
          [reasonPlaceholder]="pending.leakageFlagged ? 'Explain why this model is safe to publish despite the leakage flag…' : 'Add an optional note for the record…'"
          [busy]="busy"
          (confirmed)="confirmPromote($event)"
          (cancelled)="cancelPromote()">
          <div [innerHTML]="pendingMessage"></div>
        </ps-confirm-modal>
      }
    </div>
  `, 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-registry .md-layout { display: grid; grid-template-columns: 300px 1fr; gap: 18px; align-items: start; }\n@media (max-width: 1080px) { .ps-registry .md-layout { grid-template-columns: 1fr; } }\n\n.ps-registry .mlist { position: sticky; top: 0; }\n.ps-registry .mrow {\n  display: flex; align-items: center; gap: 10px; padding: 10px; border-radius: 8px; cursor: pointer; transition: background .12s;\n}\n.ps-registry .mrow:hover { background: var(--mj-bg-surface-hover); }\n.ps-registry .mrow.sel { background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface)); box-shadow: inset 3px 0 0 var(--mj-brand-primary); }\n.ps-registry .mrow.arc { opacity: .72; }\n.ps-registry .mrow .ico {\n  width: 30px; height: 30px; border-radius: 7px; display: grid; place-items: center; color: var(--mj-text-inverse); font-size: 13px; flex: 0 0 auto;\n}\n.ps-registry .mrow .ico.xgb { background: var(--ps-node-feature); }\n.ps-registry .mrow .ico.lgbm { background: var(--ps-node-algorithm); }\n.ps-registry .mrow .ico.rf { background: var(--ps-node-source); }\n.ps-registry .mrow .nm { font-weight: 600; color: var(--mj-text-primary); font-size: 13px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }\n.ps-registry .mrow .auc { text-align: right; flex: 0 0 auto; }\n.ps-registry .mrow .auc .v { font-weight: 700; color: var(--mj-text-primary); font-variant-numeric: tabular-nums; }\n.ps-registry .mrow .auc .st { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; }\n.ps-registry .mrow .auc .st.pub { color: var(--mj-status-success-text); }\n.ps-registry .mrow .auc .st.val { color: var(--mj-status-info-text); }\n.ps-registry .mrow .auc .st.dr { color: var(--mj-text-muted); }\n.ps-registry .mrow .auc .st.arc { color: var(--mj-text-disabled); }\n\n/* detail */\n.ps-registry .dh { display: flex; align-items: center; gap: 14px; }\n.ps-registry .big-ico {\n  width: 48px; height: 48px; border-radius: 12px; display: grid; place-items: center;\n  background: var(--ps-node-feature); color: var(--mj-text-inverse); font-size: 20px; flex: 0 0 auto;\n}\n.ps-registry .dh h2 { font-size: 18px; color: var(--mj-text-primary); display: flex; align-items: center; gap: 8px; }\n.ps-registry .dh .sub { margin-top: 2px; }\n\n.ps-registry .metric-pair { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }\n.ps-registry .mtile { border: 1px solid var(--mj-border-default); border-radius: 9px; padding: 14px; }\n.ps-registry .mtile .v { font-size: 28px; font-weight: 720; color: var(--mj-text-primary); letter-spacing: -.02em; }\n.ps-registry .mtile.honest { border-color: var(--mj-brand-primary); background: color-mix(in srgb, var(--mj-brand-primary) 6%, var(--mj-bg-surface)); }\n.ps-registry .mtile.honest .v { color: var(--mj-brand-primary); }\n.ps-registry .stat-bar { display: flex; gap: 10px; margin-top: 14px; flex-wrap: wrap; }\n.ps-registry .stat-bar .b { display: flex; flex-direction: column; gap: 2px; padding: 8px 12px; border: 1px solid var(--mj-border-subtle); border-radius: 8px; flex: 1; min-width: 90px; }\n.ps-registry .stat-bar .b strong { color: var(--mj-text-primary); font-size: 15px; }\n\n.ps-registry .gate { align-items: flex-start; }\n.ps-registry .gate i { font-size: 20px; }\n\n.ps-registry .lineage { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }\n.ps-registry .ln-card { border: 1px solid var(--mj-border-default); border-radius: 9px; padding: 10px 12px; min-width: 120px; }\n.ps-registry .ln-card.cur { border-color: var(--mj-brand-primary); background: color-mix(in srgb, var(--mj-brand-primary) 6%, var(--mj-bg-surface)); }\n.ps-registry .ln-card .t { font-size: 10.5px; text-transform: uppercase; letter-spacing: .04em; color: var(--mj-text-muted); font-weight: 700; }\n.ps-registry .ln-card .n { font-weight: 650; color: var(--mj-text-primary); font-size: 13px; margin: 2px 0; }\n.ps-registry .ln-arrow { color: var(--mj-text-disabled); }\n"] }]
    }], null, { engine: [{
            type: Input
        }], provider: [{
            type: Input
        }], currentUser: [{
            type: Input
        }] }); })();
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(PSRegistryComponent, { className: "PSRegistryComponent", filePath: "src/PredictiveStudio/components/ps-registry.component.ts", lineNumber: 229 }); })();
/** Minimal HTML-escape for interpolating model names into the modal's innerHTML message. */
function escapeHtml(value) {
    return value
        .replace(/&/g, '&amp;')
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        .replace(/"/g, '&quot;');
}
//# sourceMappingURL=ps-registry.component.js.map