@memberjunction/ng-dashboards 5.28.0 → 5.30.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.
- package/dist/AI/components/agents/agent-configuration.component.d.ts.map +1 -1
- package/dist/AI/components/agents/agent-configuration.component.js +11 -9
- package/dist/AI/components/agents/agent-configuration.component.js.map +1 -1
- package/dist/AI/components/agents/agent-editor.component.d.ts.map +1 -1
- package/dist/AI/components/agents/agent-editor.component.js +21 -19
- package/dist/AI/components/agents/agent-editor.component.js.map +1 -1
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js +154 -154
- package/dist/AI/components/autotagging/autotagging-pipeline-resource.component.js.map +1 -1
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.d.ts.map +1 -1
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.js +8 -0
- package/dist/AI/components/duplicates/duplicate-detection-resource.component.js.map +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.d.ts.map +1 -1
- package/dist/AI/components/vectors/vector-management-resource.component.js +2 -2
- package/dist/AI/components/vectors/vector-management-resource.component.js.map +1 -1
- package/dist/APIKeys/api-applications-panel.component.d.ts +1 -1
- package/dist/APIKeys/api-applications-panel.component.d.ts.map +1 -1
- package/dist/APIKeys/api-applications-panel.component.js +15 -2
- package/dist/APIKeys/api-applications-panel.component.js.map +1 -1
- package/dist/APIKeys/api-key-edit-panel.component.d.ts.map +1 -1
- package/dist/APIKeys/api-key-edit-panel.component.js +17 -5
- package/dist/APIKeys/api-key-edit-panel.component.js.map +1 -1
- package/dist/Archiving/components/archive-config-resource.component.d.ts +20 -0
- package/dist/Archiving/components/archive-config-resource.component.d.ts.map +1 -0
- package/dist/Archiving/components/archive-config-resource.component.js +46 -0
- package/dist/Archiving/components/archive-config-resource.component.js.map +1 -0
- package/dist/Archiving/components/archive-runs-resource.component.d.ts +20 -0
- package/dist/Archiving/components/archive-runs-resource.component.d.ts.map +1 -0
- package/dist/Archiving/components/archive-runs-resource.component.js +46 -0
- package/dist/Archiving/components/archive-runs-resource.component.js.map +1 -0
- package/dist/Credentials/components/credentials-list-resource.component.d.ts.map +1 -1
- package/dist/Credentials/components/credentials-list-resource.component.js +56 -71
- package/dist/Credentials/components/credentials-list-resource.component.js.map +1 -1
- package/dist/DashboardBrowser/dashboard-browser-resource.component.d.ts +0 -8
- package/dist/DashboardBrowser/dashboard-browser-resource.component.d.ts.map +1 -1
- package/dist/DashboardBrowser/dashboard-browser-resource.component.js +73 -74
- package/dist/DashboardBrowser/dashboard-browser-resource.component.js.map +1 -1
- package/dist/DashboardBrowser/dashboard-share-adapter.d.ts +25 -0
- package/dist/DashboardBrowser/dashboard-share-adapter.d.ts.map +1 -0
- package/dist/DashboardBrowser/dashboard-share-adapter.js +99 -0
- package/dist/DashboardBrowser/dashboard-share-adapter.js.map +1 -0
- package/dist/DashboardBrowser/dashboard-share-dialog.component.d.ts +21 -104
- package/dist/DashboardBrowser/dashboard-share-dialog.component.d.ts.map +1 -1
- package/dist/DashboardBrowser/dashboard-share-dialog.component.js +48 -530
- package/dist/DashboardBrowser/dashboard-share-dialog.component.js.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts +5 -0
- package/dist/DataExplorer/data-explorer-dashboard.component.d.ts.map +1 -1
- package/dist/DataExplorer/data-explorer-dashboard.component.js +17 -0
- package/dist/DataExplorer/data-explorer-dashboard.component.js.map +1 -1
- package/dist/Integration/components/connections/connections.component.d.ts +18 -1
- package/dist/Integration/components/connections/connections.component.d.ts.map +1 -1
- package/dist/Integration/components/connections/connections.component.js +251 -199
- package/dist/Integration/components/connections/connections.component.js.map +1 -1
- package/dist/Integration/services/integration-data.service.d.ts +7 -2
- package/dist/Integration/services/integration-data.service.d.ts.map +1 -1
- package/dist/Integration/services/integration-data.service.js +10 -2
- package/dist/Integration/services/integration-data.service.js.map +1 -1
- package/dist/KnowledgeHub/components/analytics/analytics-resource.component.js +144 -144
- package/dist/KnowledgeHub/components/analytics/analytics-resource.component.js.map +1 -1
- package/dist/Lists/components/lists-operations-resource.component.d.ts.map +1 -1
- package/dist/Lists/components/lists-operations-resource.component.js +12 -14
- package/dist/Lists/components/lists-operations-resource.component.js.map +1 -1
- package/dist/MCP/components/mcp-connection-dialog.component.js +1 -1
- package/dist/MCP/components/mcp-connection-dialog.component.js.map +1 -1
- package/dist/MCP/mcp-dashboard.component.d.ts +170 -0
- package/dist/MCP/mcp-dashboard.component.d.ts.map +1 -1
- package/dist/MCP/mcp-dashboard.component.js +2521 -758
- package/dist/MCP/mcp-dashboard.component.js.map +1 -1
- package/dist/MCP/mcp-filter-panel.component.d.ts +16 -1
- package/dist/MCP/mcp-filter-panel.component.d.ts.map +1 -1
- package/dist/MCP/mcp-filter-panel.component.js +187 -60
- package/dist/MCP/mcp-filter-panel.component.js.map +1 -1
- package/dist/MCP/mcp-resource.component.d.ts +0 -9
- package/dist/MCP/mcp-resource.component.d.ts.map +1 -1
- package/dist/MCP/mcp-resource.component.js +1 -10
- package/dist/MCP/mcp-resource.component.js.map +1 -1
- package/dist/MCP/mcp.module.d.ts +7 -6
- package/dist/MCP/mcp.module.d.ts.map +1 -1
- package/dist/MCP/mcp.module.js +4 -8
- package/dist/MCP/mcp.module.js.map +1 -1
- package/dist/Permissions/audit-log-resource.component.d.ts +38 -0
- package/dist/Permissions/audit-log-resource.component.d.ts.map +1 -0
- package/dist/Permissions/audit-log-resource.component.js +380 -0
- package/dist/Permissions/audit-log-resource.component.js.map +1 -0
- package/dist/Permissions/permissions-shared.d.ts +51 -0
- package/dist/Permissions/permissions-shared.d.ts.map +1 -0
- package/dist/Permissions/permissions-shared.js +91 -0
- package/dist/Permissions/permissions-shared.js.map +1 -0
- package/dist/Permissions/resource-access-resource.component.d.ts +45 -0
- package/dist/Permissions/resource-access-resource.component.d.ts.map +1 -0
- package/dist/Permissions/resource-access-resource.component.js +342 -0
- package/dist/Permissions/resource-access-resource.component.js.map +1 -0
- package/dist/Permissions/user-access-resource.component.d.ts +39 -0
- package/dist/Permissions/user-access-resource.component.d.ts.map +1 -0
- package/dist/Permissions/user-access-resource.component.js +346 -0
- package/dist/Permissions/user-access-resource.component.js.map +1 -0
- package/dist/QueryBrowser/query-browser-resource.component.d.ts +13 -3
- package/dist/QueryBrowser/query-browser-resource.component.d.ts.map +1 -1
- package/dist/QueryBrowser/query-browser-resource.component.js +186 -139
- package/dist/QueryBrowser/query-browser-resource.component.js.map +1 -1
- package/dist/archiving-dashboards.module.d.ts +19 -0
- package/dist/archiving-dashboards.module.d.ts.map +1 -0
- package/dist/archiving-dashboards.module.js +52 -0
- package/dist/archiving-dashboards.module.js.map +1 -0
- package/dist/component-studio-dashboards.module.d.ts +1 -0
- package/dist/component-studio-dashboards.module.d.ts.map +1 -1
- package/dist/component-studio-dashboards.module.js +7 -0
- package/dist/component-studio-dashboards.module.js.map +1 -1
- package/dist/core-dashboards.module.d.ts +24 -19
- package/dist/core-dashboards.module.d.ts.map +1 -1
- package/dist/core-dashboards.module.js +30 -1
- package/dist/core-dashboards.module.js.map +1 -1
- package/dist/module.d.ts +13 -12
- package/dist/module.d.ts.map +1 -1
- package/dist/module.js +7 -0
- package/dist/module.js.map +1 -1
- package/dist/public-api.d.ts +7 -1
- package/dist/public-api.d.ts.map +1 -1
- package/dist/public-api.js +8 -0
- package/dist/public-api.js.map +1 -1
- package/dist/shared/pipes/highlight-search.pipe.d.ts +1 -1
- package/dist/shared/pipes/highlight-search.pipe.d.ts.map +1 -1
- package/dist/shared/pipes/highlight-search.pipe.js +14 -4
- package/dist/shared/pipes/highlight-search.pipe.js.map +1 -1
- package/package.json +52 -48
|
@@ -2955,8 +2955,8 @@ function AutotaggingPipelineResourceComponent_Conditional_20_Template(rf, ctx) {
|
|
|
2955
2955
|
i0.ɵɵconditional(ctx_r2.FormMode === "add-type" || ctx_r2.FormMode === "edit-type" ? 12 : -1);
|
|
2956
2956
|
} }
|
|
2957
2957
|
function AutotaggingPipelineResourceComponent_Conditional_21_Conditional_15_Conditional_2_Template(rf, ctx) { if (rf & 1) {
|
|
2958
|
-
i0.ɵɵelementStart(0, "span",
|
|
2959
|
-
i0.ɵɵelement(1, "i",
|
|
2958
|
+
i0.ɵɵelementStart(0, "span", 427);
|
|
2959
|
+
i0.ɵɵelement(1, "i", 428);
|
|
2960
2960
|
i0.ɵɵtext(2);
|
|
2961
2961
|
i0.ɵɵelementEnd();
|
|
2962
2962
|
} if (rf & 2) {
|
|
@@ -2965,10 +2965,10 @@ function AutotaggingPipelineResourceComponent_Conditional_21_Conditional_15_Cond
|
|
|
2965
2965
|
i0.ɵɵtextInterpolate1(" ", ctx_r2.SelectedFeedItem.FileTypeName);
|
|
2966
2966
|
} }
|
|
2967
2967
|
function AutotaggingPipelineResourceComponent_Conditional_21_Conditional_15_Template(rf, ctx) { if (rf & 1) {
|
|
2968
|
-
i0.ɵɵelementStart(0, "span",
|
|
2968
|
+
i0.ɵɵelementStart(0, "span", 426);
|
|
2969
2969
|
i0.ɵɵtext(1);
|
|
2970
2970
|
i0.ɵɵelementEnd();
|
|
2971
|
-
i0.ɵɵconditionalCreate(2, AutotaggingPipelineResourceComponent_Conditional_21_Conditional_15_Conditional_2_Template, 3, 1, "span",
|
|
2971
|
+
i0.ɵɵconditionalCreate(2, AutotaggingPipelineResourceComponent_Conditional_21_Conditional_15_Conditional_2_Template, 3, 1, "span", 427);
|
|
2972
2972
|
} if (rf & 2) {
|
|
2973
2973
|
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
2974
2974
|
i0.ɵɵadvance();
|
|
@@ -2977,12 +2977,12 @@ function AutotaggingPipelineResourceComponent_Conditional_21_Conditional_15_Temp
|
|
|
2977
2977
|
i0.ɵɵconditional(ctx_r2.SelectedFeedItem.FileTypeName ? 2 : -1);
|
|
2978
2978
|
} }
|
|
2979
2979
|
function AutotaggingPipelineResourceComponent_Conditional_21_Conditional_16_Template(rf, ctx) { if (rf & 1) {
|
|
2980
|
-
i0.ɵɵelementStart(0, "div",
|
|
2980
|
+
i0.ɵɵelementStart(0, "div", 416)(1, "div", 417);
|
|
2981
2981
|
i0.ɵɵtext(2, "URL");
|
|
2982
2982
|
i0.ɵɵelementEnd();
|
|
2983
|
-
i0.ɵɵelementStart(3, "a",
|
|
2983
|
+
i0.ɵɵelementStart(3, "a", 429);
|
|
2984
2984
|
i0.ɵɵtext(4);
|
|
2985
|
-
i0.ɵɵelement(5, "i",
|
|
2985
|
+
i0.ɵɵelement(5, "i", 430);
|
|
2986
2986
|
i0.ɵɵelementEnd()();
|
|
2987
2987
|
} if (rf & 2) {
|
|
2988
2988
|
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
@@ -2992,10 +2992,10 @@ function AutotaggingPipelineResourceComponent_Conditional_21_Conditional_16_Temp
|
|
|
2992
2992
|
i0.ɵɵtextInterpolate1(" ", ctx_r2.SelectedFeedItem.URL, " ");
|
|
2993
2993
|
} }
|
|
2994
2994
|
function AutotaggingPipelineResourceComponent_Conditional_21_Conditional_17_Template(rf, ctx) { if (rf & 1) {
|
|
2995
|
-
i0.ɵɵelementStart(0, "div",
|
|
2995
|
+
i0.ɵɵelementStart(0, "div", 416)(1, "div", 417);
|
|
2996
2996
|
i0.ɵɵtext(2, "Content Preview");
|
|
2997
2997
|
i0.ɵɵelementEnd();
|
|
2998
|
-
i0.ɵɵelementStart(3, "div",
|
|
2998
|
+
i0.ɵɵelementStart(3, "div", 431);
|
|
2999
2999
|
i0.ɵɵtext(4);
|
|
3000
3000
|
i0.ɵɵelementEnd()();
|
|
3001
3001
|
} if (rf & 2) {
|
|
@@ -3004,9 +3004,9 @@ function AutotaggingPipelineResourceComponent_Conditional_21_Conditional_17_Temp
|
|
|
3004
3004
|
i0.ɵɵtextInterpolate(ctx_r2.SelectedFeedItem.TextContent);
|
|
3005
3005
|
} }
|
|
3006
3006
|
function AutotaggingPipelineResourceComponent_Conditional_21_Conditional_18_For_5_Template(rf, ctx) { if (rf & 1) {
|
|
3007
|
-
i0.ɵɵelementStart(0, "span",
|
|
3007
|
+
i0.ɵɵelementStart(0, "span", 434);
|
|
3008
3008
|
i0.ɵɵtext(1);
|
|
3009
|
-
i0.ɵɵelementStart(2, "span",
|
|
3009
|
+
i0.ɵɵelementStart(2, "span", 435);
|
|
3010
3010
|
i0.ɵɵtext(3);
|
|
3011
3011
|
i0.ɵɵelementEnd()();
|
|
3012
3012
|
} if (rf & 2) {
|
|
@@ -3019,11 +3019,11 @@ function AutotaggingPipelineResourceComponent_Conditional_21_Conditional_18_For_
|
|
|
3019
3019
|
i0.ɵɵtextInterpolate(ctx_r2.FormatWeight(wt_r94.Weight));
|
|
3020
3020
|
} }
|
|
3021
3021
|
function AutotaggingPipelineResourceComponent_Conditional_21_Conditional_18_Template(rf, ctx) { if (rf & 1) {
|
|
3022
|
-
i0.ɵɵelementStart(0, "div",
|
|
3022
|
+
i0.ɵɵelementStart(0, "div", 416)(1, "div", 417);
|
|
3023
3023
|
i0.ɵɵtext(2);
|
|
3024
3024
|
i0.ɵɵelementEnd();
|
|
3025
|
-
i0.ɵɵelementStart(3, "div",
|
|
3026
|
-
i0.ɵɵrepeaterCreate(4, AutotaggingPipelineResourceComponent_Conditional_21_Conditional_18_For_5_Template, 4, 4, "span",
|
|
3025
|
+
i0.ɵɵelementStart(3, "div", 432);
|
|
3026
|
+
i0.ɵɵrepeaterCreate(4, AutotaggingPipelineResourceComponent_Conditional_21_Conditional_18_For_5_Template, 4, 4, "span", 433, _forTrack4);
|
|
3027
3027
|
i0.ɵɵelementEnd()();
|
|
3028
3028
|
} if (rf & 2) {
|
|
3029
3029
|
const ctx_r2 = i0.ɵɵnextContext(2);
|
|
@@ -3033,10 +3033,10 @@ function AutotaggingPipelineResourceComponent_Conditional_21_Conditional_18_Temp
|
|
|
3033
3033
|
i0.ɵɵrepeater(ctx_r2.SelectedFeedItem.Tags);
|
|
3034
3034
|
} }
|
|
3035
3035
|
function AutotaggingPipelineResourceComponent_Conditional_21_Conditional_23_Template(rf, ctx) { if (rf & 1) {
|
|
3036
|
-
i0.ɵɵelementStart(0, "div",
|
|
3036
|
+
i0.ɵɵelementStart(0, "div", 419)(1, "span", 420);
|
|
3037
3037
|
i0.ɵɵtext(2, "Checksum");
|
|
3038
3038
|
i0.ɵɵelementEnd();
|
|
3039
|
-
i0.ɵɵelementStart(3, "span",
|
|
3039
|
+
i0.ɵɵelementStart(3, "span", 436);
|
|
3040
3040
|
i0.ɵɵtext(4);
|
|
3041
3041
|
i0.ɵɵelementEnd()();
|
|
3042
3042
|
} if (rf & 2) {
|
|
@@ -3053,45 +3053,45 @@ function AutotaggingPipelineResourceComponent_Conditional_21_Template(rf, ctx) {
|
|
|
3053
3053
|
i0.ɵɵelement(4, "i", 410);
|
|
3054
3054
|
i0.ɵɵtext(5, " Content Item");
|
|
3055
3055
|
i0.ɵɵelementEnd();
|
|
3056
|
-
i0.ɵɵelementStart(6, "button",
|
|
3056
|
+
i0.ɵɵelementStart(6, "button", 411);
|
|
3057
3057
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_21_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r93); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.CloseItemDetail()); });
|
|
3058
3058
|
i0.ɵɵelement(7, "i", 99);
|
|
3059
3059
|
i0.ɵɵelementEnd()();
|
|
3060
|
-
i0.ɵɵelementStart(8, "div", 388)(9, "div",
|
|
3060
|
+
i0.ɵɵelementStart(8, "div", 388)(9, "div", 412)(10, "h4", 413);
|
|
3061
3061
|
i0.ɵɵtext(11);
|
|
3062
3062
|
i0.ɵɵelementEnd();
|
|
3063
|
-
i0.ɵɵelementStart(12, "div",
|
|
3063
|
+
i0.ɵɵelementStart(12, "div", 414)(13, "span", 415);
|
|
3064
3064
|
i0.ɵɵtext(14);
|
|
3065
3065
|
i0.ɵɵelementEnd();
|
|
3066
3066
|
i0.ɵɵconditionalCreate(15, AutotaggingPipelineResourceComponent_Conditional_21_Conditional_15_Template, 3, 2);
|
|
3067
3067
|
i0.ɵɵelementEnd()();
|
|
3068
|
-
i0.ɵɵconditionalCreate(16, AutotaggingPipelineResourceComponent_Conditional_21_Conditional_16_Template, 6, 2, "div",
|
|
3069
|
-
i0.ɵɵconditionalCreate(17, AutotaggingPipelineResourceComponent_Conditional_21_Conditional_17_Template, 5, 1, "div",
|
|
3070
|
-
i0.ɵɵconditionalCreate(18, AutotaggingPipelineResourceComponent_Conditional_21_Conditional_18_Template, 6, 1, "div",
|
|
3071
|
-
i0.ɵɵelementStart(19, "div",
|
|
3068
|
+
i0.ɵɵconditionalCreate(16, AutotaggingPipelineResourceComponent_Conditional_21_Conditional_16_Template, 6, 2, "div", 416);
|
|
3069
|
+
i0.ɵɵconditionalCreate(17, AutotaggingPipelineResourceComponent_Conditional_21_Conditional_17_Template, 5, 1, "div", 416);
|
|
3070
|
+
i0.ɵɵconditionalCreate(18, AutotaggingPipelineResourceComponent_Conditional_21_Conditional_18_Template, 6, 1, "div", 416);
|
|
3071
|
+
i0.ɵɵelementStart(19, "div", 416)(20, "div", 417);
|
|
3072
3072
|
i0.ɵɵtext(21, "Metadata");
|
|
3073
3073
|
i0.ɵɵelementEnd();
|
|
3074
|
-
i0.ɵɵelementStart(22, "div",
|
|
3075
|
-
i0.ɵɵconditionalCreate(23, AutotaggingPipelineResourceComponent_Conditional_21_Conditional_23_Template, 5, 1, "div",
|
|
3076
|
-
i0.ɵɵelementStart(24, "div",
|
|
3074
|
+
i0.ɵɵelementStart(22, "div", 418);
|
|
3075
|
+
i0.ɵɵconditionalCreate(23, AutotaggingPipelineResourceComponent_Conditional_21_Conditional_23_Template, 5, 1, "div", 419);
|
|
3076
|
+
i0.ɵɵelementStart(24, "div", 419)(25, "span", 420);
|
|
3077
3077
|
i0.ɵɵtext(26, "Created");
|
|
3078
3078
|
i0.ɵɵelementEnd();
|
|
3079
|
-
i0.ɵɵelementStart(27, "span",
|
|
3079
|
+
i0.ɵɵelementStart(27, "span", 421);
|
|
3080
3080
|
i0.ɵɵtext(28);
|
|
3081
3081
|
i0.ɵɵelementEnd()();
|
|
3082
|
-
i0.ɵɵelementStart(29, "div",
|
|
3082
|
+
i0.ɵɵelementStart(29, "div", 419)(30, "span", 420);
|
|
3083
3083
|
i0.ɵɵtext(31, "Updated");
|
|
3084
3084
|
i0.ɵɵelementEnd();
|
|
3085
|
-
i0.ɵɵelementStart(32, "span",
|
|
3085
|
+
i0.ɵɵelementStart(32, "span", 421);
|
|
3086
3086
|
i0.ɵɵtext(33);
|
|
3087
3087
|
i0.ɵɵelementEnd()()()();
|
|
3088
|
-
i0.ɵɵelementStart(34, "div",
|
|
3088
|
+
i0.ɵɵelementStart(34, "div", 422)(35, "button", 90);
|
|
3089
3089
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_21_Template_button_click_35_listener() { i0.ɵɵrestoreView(_r93); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.OpenRecordFromItem(ctx_r2.SelectedFeedItem)); });
|
|
3090
|
-
i0.ɵɵelement(36, "i",
|
|
3090
|
+
i0.ɵɵelement(36, "i", 423);
|
|
3091
3091
|
i0.ɵɵtext(37, " Open Record ");
|
|
3092
3092
|
i0.ɵɵelementEnd();
|
|
3093
|
-
i0.ɵɵelementStart(38, "button",
|
|
3094
|
-
i0.ɵɵelement(39, "i",
|
|
3093
|
+
i0.ɵɵelementStart(38, "button", 424);
|
|
3094
|
+
i0.ɵɵelement(39, "i", 425);
|
|
3095
3095
|
i0.ɵɵtext(40, " See Similar Items ");
|
|
3096
3096
|
i0.ɵɵelementEnd()()()();
|
|
3097
3097
|
} if (rf & 2) {
|
|
@@ -3117,14 +3117,14 @@ function AutotaggingPipelineResourceComponent_Conditional_21_Template(rf, ctx) {
|
|
|
3117
3117
|
} }
|
|
3118
3118
|
function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_9_Template(rf, ctx) { if (rf & 1) {
|
|
3119
3119
|
i0.ɵɵelementStart(0, "div", 12);
|
|
3120
|
-
i0.ɵɵelement(1, "mj-loading",
|
|
3120
|
+
i0.ɵɵelement(1, "mj-loading", 438);
|
|
3121
3121
|
i0.ɵɵelementEnd();
|
|
3122
3122
|
} }
|
|
3123
3123
|
function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Conditional_15_Template(rf, ctx) { if (rf & 1) {
|
|
3124
|
-
i0.ɵɵelementStart(0, "div",
|
|
3124
|
+
i0.ɵɵelementStart(0, "div", 419)(1, "span", 420);
|
|
3125
3125
|
i0.ɵɵtext(2, "URL");
|
|
3126
3126
|
i0.ɵɵelementEnd();
|
|
3127
|
-
i0.ɵɵelementStart(3, "a",
|
|
3127
|
+
i0.ɵɵelementStart(3, "a", 455);
|
|
3128
3128
|
i0.ɵɵtext(4);
|
|
3129
3129
|
i0.ɵɵelementEnd()();
|
|
3130
3130
|
} if (rf & 2) {
|
|
@@ -3135,16 +3135,16 @@ function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Cond
|
|
|
3135
3135
|
i0.ɵɵtextInterpolate(ctx_r2.SelectedSource.URL);
|
|
3136
3136
|
} }
|
|
3137
3137
|
function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Conditional_16_Template(rf, ctx) { if (rf & 1) {
|
|
3138
|
-
i0.ɵɵelementStart(0, "div",
|
|
3138
|
+
i0.ɵɵelementStart(0, "div", 419)(1, "span", 420);
|
|
3139
3139
|
i0.ɵɵtext(2, "Content Type");
|
|
3140
3140
|
i0.ɵɵelementEnd();
|
|
3141
|
-
i0.ɵɵelementStart(3, "span",
|
|
3141
|
+
i0.ɵɵelementStart(3, "span", 421);
|
|
3142
3142
|
i0.ɵɵtext(4);
|
|
3143
3143
|
i0.ɵɵelementEnd()();
|
|
3144
|
-
i0.ɵɵelementStart(5, "div",
|
|
3144
|
+
i0.ɵɵelementStart(5, "div", 419)(6, "span", 420);
|
|
3145
3145
|
i0.ɵɵtext(7, "File Type");
|
|
3146
3146
|
i0.ɵɵelementEnd();
|
|
3147
|
-
i0.ɵɵelementStart(8, "span",
|
|
3147
|
+
i0.ɵɵelementStart(8, "span", 421);
|
|
3148
3148
|
i0.ɵɵtext(9);
|
|
3149
3149
|
i0.ɵɵelementEnd()();
|
|
3150
3150
|
} if (rf & 2) {
|
|
@@ -3165,7 +3165,7 @@ function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_For_
|
|
|
3165
3165
|
i0.ɵɵtextInterpolate(opt_r97);
|
|
3166
3166
|
} }
|
|
3167
3167
|
function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Conditional_69_Template(rf, ctx) { if (rf & 1) {
|
|
3168
|
-
i0.ɵɵelementStart(0, "div",
|
|
3168
|
+
i0.ɵɵelementStart(0, "div", 451)(1, "p");
|
|
3169
3169
|
i0.ɵɵtext(2);
|
|
3170
3170
|
i0.ɵɵelementEnd()();
|
|
3171
3171
|
} if (rf & 2) {
|
|
@@ -3175,22 +3175,22 @@ function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Cond
|
|
|
3175
3175
|
} }
|
|
3176
3176
|
function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_For_71_Template(rf, ctx) { if (rf & 1) {
|
|
3177
3177
|
const _r98 = i0.ɵɵgetCurrentView();
|
|
3178
|
-
i0.ɵɵelementStart(0, "div",
|
|
3178
|
+
i0.ɵɵelementStart(0, "div", 456);
|
|
3179
3179
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_For_71_Template_div_click_0_listener() { const ci_r99 = i0.ɵɵrestoreView(_r98).$implicit; const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.OpenContentItemDetail(ci_r99)); });
|
|
3180
3180
|
i0.ɵɵelement(1, "div", 102);
|
|
3181
|
-
i0.ɵɵelementStart(2, "span",
|
|
3181
|
+
i0.ɵɵelementStart(2, "span", 457);
|
|
3182
3182
|
i0.ɵɵtext(3);
|
|
3183
3183
|
i0.ɵɵelementEnd();
|
|
3184
|
-
i0.ɵɵelementStart(4, "span",
|
|
3184
|
+
i0.ɵɵelementStart(4, "span", 458);
|
|
3185
3185
|
i0.ɵɵtext(5);
|
|
3186
3186
|
i0.ɵɵelementEnd();
|
|
3187
|
-
i0.ɵɵelementStart(6, "span",
|
|
3187
|
+
i0.ɵɵelementStart(6, "span", 458);
|
|
3188
3188
|
i0.ɵɵtext(7);
|
|
3189
3189
|
i0.ɵɵelementEnd();
|
|
3190
|
-
i0.ɵɵelementStart(8, "span",
|
|
3190
|
+
i0.ɵɵelementStart(8, "span", 459);
|
|
3191
3191
|
i0.ɵɵtext(9);
|
|
3192
3192
|
i0.ɵɵelementEnd();
|
|
3193
|
-
i0.ɵɵelementStart(10, "span",
|
|
3193
|
+
i0.ɵɵelementStart(10, "span", 460);
|
|
3194
3194
|
i0.ɵɵtext(11);
|
|
3195
3195
|
i0.ɵɵelementEnd()();
|
|
3196
3196
|
} if (rf & 2) {
|
|
@@ -3215,15 +3215,15 @@ function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_For_
|
|
|
3215
3215
|
} }
|
|
3216
3216
|
function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Conditional_72_Template(rf, ctx) { if (rf & 1) {
|
|
3217
3217
|
const _r100 = i0.ɵɵgetCurrentView();
|
|
3218
|
-
i0.ɵɵelementStart(0, "div",
|
|
3218
|
+
i0.ɵɵelementStart(0, "div", 453)(1, "button", 461);
|
|
3219
3219
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Conditional_72_Template_button_click_1_listener() { i0.ɵɵrestoreView(_r100); const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.SourceDetailPrevPage()); });
|
|
3220
3220
|
i0.ɵɵelement(2, "i", 109);
|
|
3221
3221
|
i0.ɵɵtext(3, " Prev ");
|
|
3222
3222
|
i0.ɵɵelementEnd();
|
|
3223
|
-
i0.ɵɵelementStart(4, "span",
|
|
3223
|
+
i0.ɵɵelementStart(4, "span", 462);
|
|
3224
3224
|
i0.ɵɵtext(5);
|
|
3225
3225
|
i0.ɵɵelementEnd();
|
|
3226
|
-
i0.ɵɵelementStart(6, "button",
|
|
3226
|
+
i0.ɵɵelementStart(6, "button", 461);
|
|
3227
3227
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Conditional_72_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r100); const ctx_r2 = i0.ɵɵnextContext(3); return i0.ɵɵresetView(ctx_r2.SourceDetailNextPage()); });
|
|
3228
3228
|
i0.ɵɵtext(7, " Next ");
|
|
3229
3229
|
i0.ɵɵelement(8, "i", 111);
|
|
@@ -3238,16 +3238,16 @@ function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Cond
|
|
|
3238
3238
|
i0.ɵɵproperty("disabled", ctx_r2.SourceDetailPage >= ctx_r2.SourceDetailTotalPages - 1);
|
|
3239
3239
|
} }
|
|
3240
3240
|
function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Conditional_73_For_5_Template(rf, ctx) { if (rf & 1) {
|
|
3241
|
-
i0.ɵɵelementStart(0, "div",
|
|
3241
|
+
i0.ɵɵelementStart(0, "div", 464)(1, "span", 96);
|
|
3242
3242
|
i0.ɵɵtext(2);
|
|
3243
3243
|
i0.ɵɵelementEnd();
|
|
3244
|
-
i0.ɵɵelementStart(3, "span",
|
|
3244
|
+
i0.ɵɵelementStart(3, "span", 465);
|
|
3245
3245
|
i0.ɵɵtext(4);
|
|
3246
3246
|
i0.ɵɵelementEnd();
|
|
3247
|
-
i0.ɵɵelementStart(5, "span",
|
|
3247
|
+
i0.ɵɵelementStart(5, "span", 466);
|
|
3248
3248
|
i0.ɵɵtext(6);
|
|
3249
3249
|
i0.ɵɵelementEnd();
|
|
3250
|
-
i0.ɵɵelementStart(7, "span",
|
|
3250
|
+
i0.ɵɵelementStart(7, "span", 467);
|
|
3251
3251
|
i0.ɵɵtext(8);
|
|
3252
3252
|
i0.ɵɵelementEnd()();
|
|
3253
3253
|
} if (rf & 2) {
|
|
@@ -3264,11 +3264,11 @@ function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Cond
|
|
|
3264
3264
|
i0.ɵɵtextInterpolate1("", run_r101.Items, " items");
|
|
3265
3265
|
} }
|
|
3266
3266
|
function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Conditional_73_Template(rf, ctx) { if (rf & 1) {
|
|
3267
|
-
i0.ɵɵelementStart(0, "div",
|
|
3267
|
+
i0.ɵɵelementStart(0, "div", 416)(1, "div", 417);
|
|
3268
3268
|
i0.ɵɵtext(2, "Recent Runs");
|
|
3269
3269
|
i0.ɵɵelementEnd();
|
|
3270
|
-
i0.ɵɵelementStart(3, "div",
|
|
3271
|
-
i0.ɵɵrepeaterCreate(4, AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Conditional_73_For_5_Template, 9, 6, "div",
|
|
3270
|
+
i0.ɵɵelementStart(3, "div", 463);
|
|
3271
|
+
i0.ɵɵrepeaterCreate(4, AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Conditional_73_For_5_Template, 9, 6, "div", 464, _forTrack3);
|
|
3272
3272
|
i0.ɵɵelementEnd()();
|
|
3273
3273
|
} if (rf & 2) {
|
|
3274
3274
|
const ctx_r2 = i0.ɵɵnextContext(3);
|
|
@@ -3277,91 +3277,91 @@ function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Cond
|
|
|
3277
3277
|
} }
|
|
3278
3278
|
function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Template(rf, ctx) { if (rf & 1) {
|
|
3279
3279
|
const _r96 = i0.ɵɵgetCurrentView();
|
|
3280
|
-
i0.ɵɵelementStart(0, "div",
|
|
3280
|
+
i0.ɵɵelementStart(0, "div", 439)(1, "div", 150);
|
|
3281
3281
|
i0.ɵɵelement(2, "i");
|
|
3282
3282
|
i0.ɵɵelementEnd();
|
|
3283
|
-
i0.ɵɵelementStart(3, "div")(4, "h4",
|
|
3283
|
+
i0.ɵɵelementStart(3, "div")(4, "h4", 413);
|
|
3284
3284
|
i0.ɵɵtext(5);
|
|
3285
3285
|
i0.ɵɵelementEnd();
|
|
3286
|
-
i0.ɵɵelementStart(6, "div",
|
|
3286
|
+
i0.ɵɵelementStart(6, "div", 414)(7, "span", 426);
|
|
3287
3287
|
i0.ɵɵtext(8);
|
|
3288
3288
|
i0.ɵɵelementEnd();
|
|
3289
|
-
i0.ɵɵelementStart(9, "span",
|
|
3289
|
+
i0.ɵɵelementStart(9, "span", 440);
|
|
3290
3290
|
i0.ɵɵtext(10);
|
|
3291
3291
|
i0.ɵɵelementEnd()()()();
|
|
3292
|
-
i0.ɵɵelementStart(11, "div",
|
|
3292
|
+
i0.ɵɵelementStart(11, "div", 416)(12, "div", 417);
|
|
3293
3293
|
i0.ɵɵtext(13, "Configuration");
|
|
3294
3294
|
i0.ɵɵelementEnd();
|
|
3295
|
-
i0.ɵɵelementStart(14, "div",
|
|
3296
|
-
i0.ɵɵconditionalCreate(15, AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Conditional_15_Template, 5, 2, "div",
|
|
3295
|
+
i0.ɵɵelementStart(14, "div", 418);
|
|
3296
|
+
i0.ɵɵconditionalCreate(15, AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Conditional_15_Template, 5, 2, "div", 419);
|
|
3297
3297
|
i0.ɵɵconditionalCreate(16, AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Conditional_16_Template, 10, 2);
|
|
3298
|
-
i0.ɵɵelementStart(17, "div",
|
|
3298
|
+
i0.ɵɵelementStart(17, "div", 419)(18, "span", 420);
|
|
3299
3299
|
i0.ɵɵtext(19, "Embedding Model");
|
|
3300
3300
|
i0.ɵɵelementEnd();
|
|
3301
|
-
i0.ɵɵelementStart(20, "span",
|
|
3301
|
+
i0.ɵɵelementStart(20, "span", 421);
|
|
3302
3302
|
i0.ɵɵtext(21);
|
|
3303
3303
|
i0.ɵɵelementEnd()();
|
|
3304
|
-
i0.ɵɵelementStart(22, "div",
|
|
3304
|
+
i0.ɵɵelementStart(22, "div", 419)(23, "span", 420);
|
|
3305
3305
|
i0.ɵɵtext(24, "Vector Index");
|
|
3306
3306
|
i0.ɵɵelementEnd();
|
|
3307
|
-
i0.ɵɵelementStart(25, "span",
|
|
3307
|
+
i0.ɵɵelementStart(25, "span", 421);
|
|
3308
3308
|
i0.ɵɵtext(26);
|
|
3309
3309
|
i0.ɵɵelementEnd()()()();
|
|
3310
|
-
i0.ɵɵelementStart(27, "div",
|
|
3310
|
+
i0.ɵɵelementStart(27, "div", 416)(28, "div", 417);
|
|
3311
3311
|
i0.ɵɵtext(29, "Statistics");
|
|
3312
3312
|
i0.ɵɵelementEnd();
|
|
3313
|
-
i0.ɵɵelementStart(30, "div",
|
|
3313
|
+
i0.ɵɵelementStart(30, "div", 441)(31, "div", 442)(32, "div", 443);
|
|
3314
3314
|
i0.ɵɵtext(33);
|
|
3315
3315
|
i0.ɵɵelementEnd();
|
|
3316
|
-
i0.ɵɵelementStart(34, "div",
|
|
3316
|
+
i0.ɵɵelementStart(34, "div", 444);
|
|
3317
3317
|
i0.ɵɵtext(35, "Items");
|
|
3318
3318
|
i0.ɵɵelementEnd()();
|
|
3319
|
-
i0.ɵɵelementStart(36, "div",
|
|
3319
|
+
i0.ɵɵelementStart(36, "div", 442)(37, "div", 443);
|
|
3320
3320
|
i0.ɵɵtext(38);
|
|
3321
3321
|
i0.ɵɵelementEnd();
|
|
3322
|
-
i0.ɵɵelementStart(39, "div",
|
|
3322
|
+
i0.ɵɵelementStart(39, "div", 444);
|
|
3323
3323
|
i0.ɵɵtext(40, "Tags");
|
|
3324
3324
|
i0.ɵɵelementEnd()();
|
|
3325
|
-
i0.ɵɵelementStart(41, "div",
|
|
3325
|
+
i0.ɵɵelementStart(41, "div", 442)(42, "div", 443);
|
|
3326
3326
|
i0.ɵɵtext(43);
|
|
3327
3327
|
i0.ɵɵelementEnd();
|
|
3328
|
-
i0.ɵɵelementStart(44, "div",
|
|
3328
|
+
i0.ɵɵelementStart(44, "div", 444);
|
|
3329
3329
|
i0.ɵɵtext(45, "Avg Tags");
|
|
3330
3330
|
i0.ɵɵelementEnd()();
|
|
3331
|
-
i0.ɵɵelementStart(46, "div",
|
|
3331
|
+
i0.ɵɵelementStart(46, "div", 442)(47, "div", 443);
|
|
3332
3332
|
i0.ɵɵtext(48);
|
|
3333
3333
|
i0.ɵɵelementEnd();
|
|
3334
|
-
i0.ɵɵelementStart(49, "div",
|
|
3334
|
+
i0.ɵɵelementStart(49, "div", 444);
|
|
3335
3335
|
i0.ɵɵtext(50, "Last Run");
|
|
3336
3336
|
i0.ɵɵelementEnd()();
|
|
3337
|
-
i0.ɵɵelementStart(51, "div",
|
|
3337
|
+
i0.ɵɵelementStart(51, "div", 442)(52, "div", 443);
|
|
3338
3338
|
i0.ɵɵtext(53);
|
|
3339
3339
|
i0.ɵɵelementEnd();
|
|
3340
|
-
i0.ɵɵelementStart(54, "div",
|
|
3340
|
+
i0.ɵɵelementStart(54, "div", 444);
|
|
3341
3341
|
i0.ɵɵtext(55, "Errors");
|
|
3342
3342
|
i0.ɵɵelementEnd()()()();
|
|
3343
|
-
i0.ɵɵelementStart(56, "div",
|
|
3343
|
+
i0.ɵɵelementStart(56, "div", 416)(57, "div", 445)(58, "div", 417);
|
|
3344
3344
|
i0.ɵɵtext(59);
|
|
3345
3345
|
i0.ɵɵpipe(60, "number");
|
|
3346
3346
|
i0.ɵɵelementEnd();
|
|
3347
|
-
i0.ɵɵelementStart(61, "div",
|
|
3347
|
+
i0.ɵɵelementStart(61, "div", 446)(62, "select", 447);
|
|
3348
3348
|
i0.ɵɵtwoWayListener("ngModelChange", function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Template_select_ngModelChange_62_listener($event) { i0.ɵɵrestoreView(_r96); const ctx_r2 = i0.ɵɵnextContext(2); i0.ɵɵtwoWayBindingSet(ctx_r2.SourceDetailStatusFilter, $event) || (ctx_r2.SourceDetailStatusFilter = $event); return i0.ɵɵresetView($event); });
|
|
3349
3349
|
i0.ɵɵlistener("ngModelChange", function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Template_select_ngModelChange_62_listener() { i0.ɵɵrestoreView(_r96); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.OnSourceDetailStatusFilterChange()); });
|
|
3350
3350
|
i0.ɵɵrepeaterCreate(63, AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_For_64_Template, 2, 2, "option", 373, i0.ɵɵrepeaterTrackByIdentity);
|
|
3351
3351
|
i0.ɵɵelementEnd();
|
|
3352
|
-
i0.ɵɵelementStart(65, "button",
|
|
3352
|
+
i0.ɵɵelementStart(65, "button", 448);
|
|
3353
3353
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Template_button_click_65_listener() { i0.ɵɵrestoreView(_r96); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.RetryFailedItems()); });
|
|
3354
|
-
i0.ɵɵelement(66, "i",
|
|
3354
|
+
i0.ɵɵelement(66, "i", 449);
|
|
3355
3355
|
i0.ɵɵtext(67, " Retry Failed ");
|
|
3356
3356
|
i0.ɵɵelementEnd()()();
|
|
3357
|
-
i0.ɵɵelementStart(68, "div",
|
|
3358
|
-
i0.ɵɵconditionalCreate(69, AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Conditional_69_Template, 3, 1, "div",
|
|
3359
|
-
i0.ɵɵrepeaterCreate(70, AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_For_71_Template, 12, 11, "div",
|
|
3357
|
+
i0.ɵɵelementStart(68, "div", 450);
|
|
3358
|
+
i0.ɵɵconditionalCreate(69, AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Conditional_69_Template, 3, 1, "div", 451);
|
|
3359
|
+
i0.ɵɵrepeaterCreate(70, AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_For_71_Template, 12, 11, "div", 452, _forTrack3);
|
|
3360
3360
|
i0.ɵɵelementEnd();
|
|
3361
|
-
i0.ɵɵconditionalCreate(72, AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Conditional_72_Template, 9, 4, "div",
|
|
3361
|
+
i0.ɵɵconditionalCreate(72, AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Conditional_72_Template, 9, 4, "div", 453);
|
|
3362
3362
|
i0.ɵɵelementEnd();
|
|
3363
|
-
i0.ɵɵconditionalCreate(73, AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Conditional_73_Template, 6, 0, "div",
|
|
3364
|
-
i0.ɵɵelementStart(74, "div",
|
|
3363
|
+
i0.ɵɵconditionalCreate(73, AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Conditional_73_Template, 6, 0, "div", 416);
|
|
3364
|
+
i0.ɵɵelementStart(74, "div", 422)(75, "button", 90);
|
|
3365
3365
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Template_button_click_75_listener() { i0.ɵɵrestoreView(_r96); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.OpenEditSourceFromDetail()); });
|
|
3366
3366
|
i0.ɵɵelement(76, "i", 162);
|
|
3367
3367
|
i0.ɵɵtext(77, " Edit ");
|
|
@@ -3371,7 +3371,7 @@ function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Temp
|
|
|
3371
3371
|
i0.ɵɵelement(79, "i", 18);
|
|
3372
3372
|
i0.ɵɵtext(80, " Run Now ");
|
|
3373
3373
|
i0.ɵɵelementEnd();
|
|
3374
|
-
i0.ɵɵelementStart(81, "button",
|
|
3374
|
+
i0.ɵɵelementStart(81, "button", 454);
|
|
3375
3375
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_22_Conditional_10_Template_button_click_81_listener() { i0.ɵɵrestoreView(_r96); const ctx_r2 = i0.ɵɵnextContext(2); return i0.ɵɵresetView(ctx_r2.DeleteSourceFromDetail()); });
|
|
3376
3376
|
i0.ɵɵelement(82, "i", 165);
|
|
3377
3377
|
i0.ɵɵtext(83, " Delete ");
|
|
@@ -3432,7 +3432,7 @@ function AutotaggingPipelineResourceComponent_Conditional_22_Template(rf, ctx) {
|
|
|
3432
3432
|
i0.ɵɵelement(4, "i", 52);
|
|
3433
3433
|
i0.ɵɵtext(5, " Source Detail");
|
|
3434
3434
|
i0.ɵɵelementEnd();
|
|
3435
|
-
i0.ɵɵelementStart(6, "button",
|
|
3435
|
+
i0.ɵɵelementStart(6, "button", 437);
|
|
3436
3436
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_22_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r95); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.CloseSourceDetail()); });
|
|
3437
3437
|
i0.ɵɵelement(7, "i", 99);
|
|
3438
3438
|
i0.ɵɵelementEnd()();
|
|
@@ -3457,11 +3457,11 @@ function AutotaggingPipelineResourceComponent_Conditional_23_Conditional_34_Temp
|
|
|
3457
3457
|
} }
|
|
3458
3458
|
function AutotaggingPipelineResourceComponent_Conditional_23_Template(rf, ctx) { if (rf & 1) {
|
|
3459
3459
|
const _r102 = i0.ɵɵgetCurrentView();
|
|
3460
|
-
i0.ɵɵelementStart(0, "div",
|
|
3460
|
+
i0.ɵɵelementStart(0, "div", 468);
|
|
3461
3461
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_23_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r102); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.CloseScheduleDialog()); });
|
|
3462
|
-
i0.ɵɵelementStart(1, "div",
|
|
3462
|
+
i0.ɵɵelementStart(1, "div", 469);
|
|
3463
3463
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_23_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r102); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
3464
|
-
i0.ɵɵelementStart(2, "div",
|
|
3464
|
+
i0.ɵɵelementStart(2, "div", 470)(3, "h3");
|
|
3465
3465
|
i0.ɵɵelement(4, "i", 167);
|
|
3466
3466
|
i0.ɵɵtext(5, " Schedule Pipeline");
|
|
3467
3467
|
i0.ɵɵelementEnd();
|
|
@@ -3469,36 +3469,36 @@ function AutotaggingPipelineResourceComponent_Conditional_23_Template(rf, ctx) {
|
|
|
3469
3469
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_23_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r102); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.CloseScheduleDialog()); });
|
|
3470
3470
|
i0.ɵɵelement(7, "i", 99);
|
|
3471
3471
|
i0.ɵɵelementEnd()();
|
|
3472
|
-
i0.ɵɵelementStart(8, "div",
|
|
3472
|
+
i0.ɵɵelementStart(8, "div", 471)(9, "div", 472)(10, "label");
|
|
3473
3473
|
i0.ɵɵtext(11, "Source");
|
|
3474
3474
|
i0.ɵɵelementEnd();
|
|
3475
|
-
i0.ɵɵelementStart(12, "div",
|
|
3475
|
+
i0.ɵɵelementStart(12, "div", 473);
|
|
3476
3476
|
i0.ɵɵelement(13, "i");
|
|
3477
3477
|
i0.ɵɵtext(14);
|
|
3478
3478
|
i0.ɵɵelementEnd()();
|
|
3479
|
-
i0.ɵɵelementStart(15, "div",
|
|
3479
|
+
i0.ɵɵelementStart(15, "div", 472)(16, "label");
|
|
3480
3480
|
i0.ɵɵtext(17, "Action");
|
|
3481
3481
|
i0.ɵɵelementEnd();
|
|
3482
|
-
i0.ɵɵelementStart(18, "div",
|
|
3482
|
+
i0.ɵɵelementStart(18, "div", 474);
|
|
3483
3483
|
i0.ɵɵtext(19, "Autotag and Vectorize Content");
|
|
3484
3484
|
i0.ɵɵelementEnd()();
|
|
3485
|
-
i0.ɵɵelementStart(20, "div",
|
|
3485
|
+
i0.ɵɵelementStart(20, "div", 472)(21, "label", 475);
|
|
3486
3486
|
i0.ɵɵtext(22, "Cron Expression");
|
|
3487
3487
|
i0.ɵɵelementEnd();
|
|
3488
|
-
i0.ɵɵelementStart(23, "input",
|
|
3488
|
+
i0.ɵɵelementStart(23, "input", 476);
|
|
3489
3489
|
i0.ɵɵtwoWayListener("ngModelChange", function AutotaggingPipelineResourceComponent_Conditional_23_Template_input_ngModelChange_23_listener($event) { i0.ɵɵrestoreView(_r102); const ctx_r2 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r2.ScheduleCron, $event) || (ctx_r2.ScheduleCron = $event); return i0.ɵɵresetView($event); });
|
|
3490
3490
|
i0.ɵɵelementEnd();
|
|
3491
|
-
i0.ɵɵelementStart(24, "div",
|
|
3491
|
+
i0.ɵɵelementStart(24, "div", 477);
|
|
3492
3492
|
i0.ɵɵelement(25, "i", 383);
|
|
3493
3493
|
i0.ɵɵtext(26);
|
|
3494
3494
|
i0.ɵɵelementEnd()();
|
|
3495
|
-
i0.ɵɵelementStart(27, "div",
|
|
3495
|
+
i0.ɵɵelementStart(27, "div", 478)(28, "label", 479);
|
|
3496
3496
|
i0.ɵɵtext(29, "Enabled");
|
|
3497
3497
|
i0.ɵɵelementEnd();
|
|
3498
|
-
i0.ɵɵelementStart(30, "input",
|
|
3498
|
+
i0.ɵɵelementStart(30, "input", 480);
|
|
3499
3499
|
i0.ɵɵtwoWayListener("ngModelChange", function AutotaggingPipelineResourceComponent_Conditional_23_Template_input_ngModelChange_30_listener($event) { i0.ɵɵrestoreView(_r102); const ctx_r2 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r2.ScheduleEnabled, $event) || (ctx_r2.ScheduleEnabled = $event); return i0.ɵɵresetView($event); });
|
|
3500
3500
|
i0.ɵɵelementEnd()()();
|
|
3501
|
-
i0.ɵɵelementStart(31, "div",
|
|
3501
|
+
i0.ɵɵelementStart(31, "div", 481)(32, "button", 393);
|
|
3502
3502
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_23_Template_button_click_32_listener() { i0.ɵɵrestoreView(_r102); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.SaveSchedule()); });
|
|
3503
3503
|
i0.ɵɵconditionalCreate(33, AutotaggingPipelineResourceComponent_Conditional_23_Conditional_33_Template, 2, 0)(34, AutotaggingPipelineResourceComponent_Conditional_23_Conditional_34_Template, 2, 0);
|
|
3504
3504
|
i0.ɵɵelementEnd();
|
|
@@ -3525,22 +3525,22 @@ function AutotaggingPipelineResourceComponent_Conditional_23_Template(rf, ctx) {
|
|
|
3525
3525
|
} }
|
|
3526
3526
|
function AutotaggingPipelineResourceComponent_Conditional_24_Template(rf, ctx) { if (rf & 1) {
|
|
3527
3527
|
const _r103 = i0.ɵɵgetCurrentView();
|
|
3528
|
-
i0.ɵɵelementStart(0, "div",
|
|
3528
|
+
i0.ɵɵelementStart(0, "div", 468);
|
|
3529
3529
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_24_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r103); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.ConfirmDialogCancel()); });
|
|
3530
|
-
i0.ɵɵelementStart(1, "div",
|
|
3530
|
+
i0.ɵɵelementStart(1, "div", 469);
|
|
3531
3531
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_24_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r103); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
3532
|
-
i0.ɵɵelementStart(2, "div",
|
|
3533
|
-
i0.ɵɵelement(4, "i",
|
|
3532
|
+
i0.ɵɵelementStart(2, "div", 470)(3, "h3");
|
|
3533
|
+
i0.ɵɵelement(4, "i", 482);
|
|
3534
3534
|
i0.ɵɵtext(5);
|
|
3535
3535
|
i0.ɵɵelementEnd();
|
|
3536
3536
|
i0.ɵɵelementStart(6, "button", 356);
|
|
3537
3537
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_24_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r103); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.ConfirmDialogCancel()); });
|
|
3538
3538
|
i0.ɵɵelement(7, "i", 99);
|
|
3539
3539
|
i0.ɵɵelementEnd()();
|
|
3540
|
-
i0.ɵɵelementStart(8, "div",
|
|
3540
|
+
i0.ɵɵelementStart(8, "div", 471)(9, "p", 483);
|
|
3541
3541
|
i0.ɵɵtext(10);
|
|
3542
3542
|
i0.ɵɵelementEnd()();
|
|
3543
|
-
i0.ɵɵelementStart(11, "div",
|
|
3543
|
+
i0.ɵɵelementStart(11, "div", 481)(12, "button", 86);
|
|
3544
3544
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_24_Template_button_click_12_listener() { i0.ɵɵrestoreView(_r103); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.ConfirmDialogAccept()); });
|
|
3545
3545
|
i0.ɵɵelement(13, "i", 72);
|
|
3546
3546
|
i0.ɵɵtext(14, " Confirm ");
|
|
@@ -3558,11 +3558,11 @@ function AutotaggingPipelineResourceComponent_Conditional_24_Template(rf, ctx) {
|
|
|
3558
3558
|
} }
|
|
3559
3559
|
function AutotaggingPipelineResourceComponent_Conditional_25_Template(rf, ctx) { if (rf & 1) {
|
|
3560
3560
|
const _r104 = i0.ɵɵgetCurrentView();
|
|
3561
|
-
i0.ɵɵelementStart(0, "div",
|
|
3561
|
+
i0.ɵɵelementStart(0, "div", 468);
|
|
3562
3562
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_25_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r104); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.CloseSplitDialog()); });
|
|
3563
|
-
i0.ɵɵelementStart(1, "div",
|
|
3563
|
+
i0.ɵɵelementStart(1, "div", 469);
|
|
3564
3564
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_25_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r104); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
3565
|
-
i0.ɵɵelementStart(2, "div",
|
|
3565
|
+
i0.ɵɵelementStart(2, "div", 470)(3, "h3");
|
|
3566
3566
|
i0.ɵɵelement(4, "i", 293);
|
|
3567
3567
|
i0.ɵɵtext(5, " Split Tag");
|
|
3568
3568
|
i0.ɵɵelementEnd();
|
|
@@ -3570,16 +3570,16 @@ function AutotaggingPipelineResourceComponent_Conditional_25_Template(rf, ctx) {
|
|
|
3570
3570
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_25_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r104); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.CloseSplitDialog()); });
|
|
3571
3571
|
i0.ɵɵelement(7, "i", 99);
|
|
3572
3572
|
i0.ɵɵelementEnd()();
|
|
3573
|
-
i0.ɵɵelementStart(8, "div",
|
|
3573
|
+
i0.ɵɵelementStart(8, "div", 471)(9, "div", 472)(10, "label");
|
|
3574
3574
|
i0.ɵɵtext(11, "New tag names (comma-separated)");
|
|
3575
3575
|
i0.ɵɵelementEnd();
|
|
3576
|
-
i0.ɵɵelementStart(12, "input",
|
|
3576
|
+
i0.ɵɵelementStart(12, "input", 484);
|
|
3577
3577
|
i0.ɵɵtwoWayListener("ngModelChange", function AutotaggingPipelineResourceComponent_Conditional_25_Template_input_ngModelChange_12_listener($event) { i0.ɵɵrestoreView(_r104); const ctx_r2 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r2.SplitChildNames, $event) || (ctx_r2.SplitChildNames = $event); return i0.ɵɵresetView($event); });
|
|
3578
3578
|
i0.ɵɵelementEnd()();
|
|
3579
|
-
i0.ɵɵelementStart(13, "p",
|
|
3579
|
+
i0.ɵɵelementStart(13, "p", 483);
|
|
3580
3580
|
i0.ɵɵtext(14, "New tags will be created as siblings of the original tag under the same parent.");
|
|
3581
3581
|
i0.ɵɵelementEnd()();
|
|
3582
|
-
i0.ɵɵelementStart(15, "div",
|
|
3582
|
+
i0.ɵɵelementStart(15, "div", 481)(16, "button", 393);
|
|
3583
3583
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_25_Template_button_click_16_listener() { i0.ɵɵrestoreView(_r104); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.ExecuteSplit()); });
|
|
3584
3584
|
i0.ɵɵelement(17, "i", 293);
|
|
3585
3585
|
i0.ɵɵtext(18, " Create Tags ");
|
|
@@ -3597,11 +3597,11 @@ function AutotaggingPipelineResourceComponent_Conditional_25_Template(rf, ctx) {
|
|
|
3597
3597
|
} }
|
|
3598
3598
|
function AutotaggingPipelineResourceComponent_Conditional_26_Template(rf, ctx) { if (rf & 1) {
|
|
3599
3599
|
const _r105 = i0.ɵɵgetCurrentView();
|
|
3600
|
-
i0.ɵɵelementStart(0, "div",
|
|
3600
|
+
i0.ɵɵelementStart(0, "div", 468);
|
|
3601
3601
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_26_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r105); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.CloseCreateTagDialog()); });
|
|
3602
|
-
i0.ɵɵelementStart(1, "div",
|
|
3602
|
+
i0.ɵɵelementStart(1, "div", 469);
|
|
3603
3603
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_26_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r105); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
3604
|
-
i0.ɵɵelementStart(2, "div",
|
|
3604
|
+
i0.ɵɵelementStart(2, "div", 470)(3, "h3");
|
|
3605
3605
|
i0.ɵɵelement(4, "i", 134);
|
|
3606
3606
|
i0.ɵɵtext(5, " Create Tag");
|
|
3607
3607
|
i0.ɵɵelementEnd();
|
|
@@ -3609,23 +3609,23 @@ function AutotaggingPipelineResourceComponent_Conditional_26_Template(rf, ctx) {
|
|
|
3609
3609
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_26_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r105); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.CloseCreateTagDialog()); });
|
|
3610
3610
|
i0.ɵɵelement(7, "i", 99);
|
|
3611
3611
|
i0.ɵɵelementEnd()();
|
|
3612
|
-
i0.ɵɵelementStart(8, "div",
|
|
3612
|
+
i0.ɵɵelementStart(8, "div", 471)(9, "div", 485);
|
|
3613
3613
|
i0.ɵɵtext(10);
|
|
3614
3614
|
i0.ɵɵelementEnd();
|
|
3615
|
-
i0.ɵɵelementStart(11, "div",
|
|
3615
|
+
i0.ɵɵelementStart(11, "div", 472)(12, "label");
|
|
3616
3616
|
i0.ɵɵtext(13, "Name");
|
|
3617
3617
|
i0.ɵɵelementEnd();
|
|
3618
|
-
i0.ɵɵelementStart(14, "input",
|
|
3618
|
+
i0.ɵɵelementStart(14, "input", 486);
|
|
3619
3619
|
i0.ɵɵtwoWayListener("ngModelChange", function AutotaggingPipelineResourceComponent_Conditional_26_Template_input_ngModelChange_14_listener($event) { i0.ɵɵrestoreView(_r105); const ctx_r2 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r2.CreateTagName, $event) || (ctx_r2.CreateTagName = $event); return i0.ɵɵresetView($event); });
|
|
3620
3620
|
i0.ɵɵlistener("keydown.enter", function AutotaggingPipelineResourceComponent_Conditional_26_Template_input_keydown_enter_14_listener() { i0.ɵɵrestoreView(_r105); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.SaveNewTag()); });
|
|
3621
3621
|
i0.ɵɵelementEnd()();
|
|
3622
|
-
i0.ɵɵelementStart(15, "div",
|
|
3622
|
+
i0.ɵɵelementStart(15, "div", 472)(16, "label");
|
|
3623
3623
|
i0.ɵɵtext(17, "Description (optional)");
|
|
3624
3624
|
i0.ɵɵelementEnd();
|
|
3625
|
-
i0.ɵɵelementStart(18, "textarea",
|
|
3625
|
+
i0.ɵɵelementStart(18, "textarea", 487);
|
|
3626
3626
|
i0.ɵɵtwoWayListener("ngModelChange", function AutotaggingPipelineResourceComponent_Conditional_26_Template_textarea_ngModelChange_18_listener($event) { i0.ɵɵrestoreView(_r105); const ctx_r2 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r2.CreateTagDescription, $event) || (ctx_r2.CreateTagDescription = $event); return i0.ɵɵresetView($event); });
|
|
3627
3627
|
i0.ɵɵelementEnd()()();
|
|
3628
|
-
i0.ɵɵelementStart(19, "div",
|
|
3628
|
+
i0.ɵɵelementStart(19, "div", 481)(20, "button", 393);
|
|
3629
3629
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_26_Template_button_click_20_listener() { i0.ɵɵrestoreView(_r105); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.SaveNewTag()); });
|
|
3630
3630
|
i0.ɵɵelement(21, "i", 72);
|
|
3631
3631
|
i0.ɵɵtext(22, " Create ");
|
|
@@ -3646,7 +3646,7 @@ function AutotaggingPipelineResourceComponent_Conditional_26_Template(rf, ctx) {
|
|
|
3646
3646
|
i0.ɵɵproperty("disabled", !ctx_r2.CreateTagName.trim());
|
|
3647
3647
|
} }
|
|
3648
3648
|
function AutotaggingPipelineResourceComponent_Conditional_27_For_16_Template(rf, ctx) { if (rf & 1) {
|
|
3649
|
-
i0.ɵɵelementStart(0, "option",
|
|
3649
|
+
i0.ɵɵelementStart(0, "option", 489);
|
|
3650
3650
|
i0.ɵɵtext(1);
|
|
3651
3651
|
i0.ɵɵelementEnd();
|
|
3652
3652
|
} if (rf & 2) {
|
|
@@ -3657,11 +3657,11 @@ function AutotaggingPipelineResourceComponent_Conditional_27_For_16_Template(rf,
|
|
|
3657
3657
|
} }
|
|
3658
3658
|
function AutotaggingPipelineResourceComponent_Conditional_27_Template(rf, ctx) { if (rf & 1) {
|
|
3659
3659
|
const _r106 = i0.ɵɵgetCurrentView();
|
|
3660
|
-
i0.ɵɵelementStart(0, "div",
|
|
3660
|
+
i0.ɵɵelementStart(0, "div", 468);
|
|
3661
3661
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_27_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r106); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.CloseMoveDialog()); });
|
|
3662
|
-
i0.ɵɵelementStart(1, "div",
|
|
3662
|
+
i0.ɵɵelementStart(1, "div", 469);
|
|
3663
3663
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_27_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r106); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
3664
|
-
i0.ɵɵelementStart(2, "div",
|
|
3664
|
+
i0.ɵɵelementStart(2, "div", 470)(3, "h3");
|
|
3665
3665
|
i0.ɵɵelement(4, "i", 291);
|
|
3666
3666
|
i0.ɵɵtext(5, " Move Tag");
|
|
3667
3667
|
i0.ɵɵelementEnd();
|
|
@@ -3669,17 +3669,17 @@ function AutotaggingPipelineResourceComponent_Conditional_27_Template(rf, ctx) {
|
|
|
3669
3669
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_27_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r106); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.CloseMoveDialog()); });
|
|
3670
3670
|
i0.ɵɵelement(7, "i", 99);
|
|
3671
3671
|
i0.ɵɵelementEnd()();
|
|
3672
|
-
i0.ɵɵelementStart(8, "div",
|
|
3672
|
+
i0.ɵɵelementStart(8, "div", 471)(9, "div", 472)(10, "label");
|
|
3673
3673
|
i0.ɵɵtext(11, "New parent tag");
|
|
3674
3674
|
i0.ɵɵelementEnd();
|
|
3675
|
-
i0.ɵɵelementStart(12, "select",
|
|
3675
|
+
i0.ɵɵelementStart(12, "select", 488);
|
|
3676
3676
|
i0.ɵɵtwoWayListener("ngModelChange", function AutotaggingPipelineResourceComponent_Conditional_27_Template_select_ngModelChange_12_listener($event) { i0.ɵɵrestoreView(_r106); const ctx_r2 = i0.ɵɵnextContext(); i0.ɵɵtwoWayBindingSet(ctx_r2.MoveNewParentID, $event) || (ctx_r2.MoveNewParentID = $event); return i0.ɵɵresetView($event); });
|
|
3677
|
-
i0.ɵɵelementStart(13, "option",
|
|
3677
|
+
i0.ɵɵelementStart(13, "option", 489);
|
|
3678
3678
|
i0.ɵɵtext(14, "(Root level \u2014 no parent)");
|
|
3679
3679
|
i0.ɵɵelementEnd();
|
|
3680
|
-
i0.ɵɵrepeaterCreate(15, AutotaggingPipelineResourceComponent_Conditional_27_For_16_Template, 2, 3, "option",
|
|
3680
|
+
i0.ɵɵrepeaterCreate(15, AutotaggingPipelineResourceComponent_Conditional_27_For_16_Template, 2, 3, "option", 489, _forTrack3);
|
|
3681
3681
|
i0.ɵɵelementEnd()()();
|
|
3682
|
-
i0.ɵɵelementStart(17, "div",
|
|
3682
|
+
i0.ɵɵelementStart(17, "div", 481)(18, "button", 90);
|
|
3683
3683
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_27_Template_button_click_18_listener() { i0.ɵɵrestoreView(_r106); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.ExecuteMove()); });
|
|
3684
3684
|
i0.ɵɵelement(19, "i", 72);
|
|
3685
3685
|
i0.ɵɵtext(20, " Move ");
|
|
@@ -3707,11 +3707,11 @@ function AutotaggingPipelineResourceComponent_Conditional_28_Conditional_18_Temp
|
|
|
3707
3707
|
} }
|
|
3708
3708
|
function AutotaggingPipelineResourceComponent_Conditional_28_Template(rf, ctx) { if (rf & 1) {
|
|
3709
3709
|
const _r108 = i0.ɵɵgetCurrentView();
|
|
3710
|
-
i0.ɵɵelementStart(0, "div",
|
|
3710
|
+
i0.ɵɵelementStart(0, "div", 468);
|
|
3711
3711
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_28_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r108); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.CloseMergeIntoDialog()); });
|
|
3712
|
-
i0.ɵɵelementStart(1, "div",
|
|
3712
|
+
i0.ɵɵelementStart(1, "div", 469);
|
|
3713
3713
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_28_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r108); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
3714
|
-
i0.ɵɵelementStart(2, "div",
|
|
3714
|
+
i0.ɵɵelementStart(2, "div", 470)(3, "h3");
|
|
3715
3715
|
i0.ɵɵelement(4, "i", 292);
|
|
3716
3716
|
i0.ɵɵtext(5);
|
|
3717
3717
|
i0.ɵɵelementEnd();
|
|
@@ -3719,16 +3719,16 @@ function AutotaggingPipelineResourceComponent_Conditional_28_Template(rf, ctx) {
|
|
|
3719
3719
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_28_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r108); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.CloseMergeIntoDialog()); });
|
|
3720
3720
|
i0.ɵɵelement(7, "i", 99);
|
|
3721
3721
|
i0.ɵɵelementEnd()();
|
|
3722
|
-
i0.ɵɵelementStart(8, "div",
|
|
3722
|
+
i0.ɵɵelementStart(8, "div", 471)(9, "p", 490);
|
|
3723
3723
|
i0.ɵɵtext(10);
|
|
3724
3724
|
i0.ɵɵelementEnd();
|
|
3725
|
-
i0.ɵɵelementStart(11, "div",
|
|
3725
|
+
i0.ɵɵelementStart(11, "div", 472)(12, "label");
|
|
3726
3726
|
i0.ɵɵtext(13, "Merge into");
|
|
3727
3727
|
i0.ɵɵelementEnd();
|
|
3728
|
-
i0.ɵɵelementStart(14, "mj-combobox",
|
|
3728
|
+
i0.ɵɵelementStart(14, "mj-combobox", 491);
|
|
3729
3729
|
i0.ɵɵlistener("ValueChange", function AutotaggingPipelineResourceComponent_Conditional_28_Template_mj_combobox_ValueChange_14_listener($event) { i0.ɵɵrestoreView(_r108); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.OnMergeTargetSelected($event)); });
|
|
3730
3730
|
i0.ɵɵelementEnd()()();
|
|
3731
|
-
i0.ɵɵelementStart(15, "div",
|
|
3731
|
+
i0.ɵɵelementStart(15, "div", 481)(16, "button", 393);
|
|
3732
3732
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_28_Template_button_click_16_listener() { i0.ɵɵrestoreView(_r108); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.ExecuteMergeInto()); });
|
|
3733
3733
|
i0.ɵɵconditionalCreate(17, AutotaggingPipelineResourceComponent_Conditional_28_Conditional_17_Template, 2, 0)(18, AutotaggingPipelineResourceComponent_Conditional_28_Conditional_18_Template, 2, 0);
|
|
3734
3734
|
i0.ɵɵelementEnd();
|
|
@@ -3751,31 +3751,31 @@ function AutotaggingPipelineResourceComponent_Conditional_28_Template(rf, ctx) {
|
|
|
3751
3751
|
} }
|
|
3752
3752
|
function AutotaggingPipelineResourceComponent_Conditional_29_Template(rf, ctx) { if (rf & 1) {
|
|
3753
3753
|
const _r109 = i0.ɵɵgetCurrentView();
|
|
3754
|
-
i0.ɵɵelementStart(0, "div",
|
|
3754
|
+
i0.ɵɵelementStart(0, "div", 492);
|
|
3755
3755
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_29_Template_div_click_0_listener() { i0.ɵɵrestoreView(_r109); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.ShowNoContentTypeWarning = false); });
|
|
3756
|
-
i0.ɵɵelementStart(1, "div",
|
|
3756
|
+
i0.ɵɵelementStart(1, "div", 493);
|
|
3757
3757
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_29_Template_div_click_1_listener($event) { i0.ɵɵrestoreView(_r109); return i0.ɵɵresetView($event.stopPropagation()); });
|
|
3758
|
-
i0.ɵɵelementStart(2, "div",
|
|
3759
|
-
i0.ɵɵelement(4, "i",
|
|
3758
|
+
i0.ɵɵelementStart(2, "div", 470)(3, "span");
|
|
3759
|
+
i0.ɵɵelement(4, "i", 494);
|
|
3760
3760
|
i0.ɵɵtext(5, " Content Type Required");
|
|
3761
3761
|
i0.ɵɵelementEnd();
|
|
3762
|
-
i0.ɵɵelementStart(6, "button",
|
|
3762
|
+
i0.ɵɵelementStart(6, "button", 495);
|
|
3763
3763
|
i0.ɵɵlistener("click", function AutotaggingPipelineResourceComponent_Conditional_29_Template_button_click_6_listener() { i0.ɵɵrestoreView(_r109); const ctx_r2 = i0.ɵɵnextContext(); return i0.ɵɵresetView(ctx_r2.ShowNoContentTypeWarning = false); });
|
|
3764
|
-
i0.ɵɵelement(7, "i",
|
|
3764
|
+
i0.ɵɵelement(7, "i", 496);
|
|
3765
3765
|
i0.ɵɵelementEnd()();
|
|
3766
|
-
i0.ɵɵelementStart(8, "div",
|
|
3766
|
+
i0.ɵɵelementStart(8, "div", 497)(9, "p", 498);
|
|
3767
3767
|
i0.ɵɵtext(10, " No content types have been configured yet. At least one content type is required before you can create a content source. ");
|
|
3768
3768
|
i0.ɵɵelementEnd();
|
|
3769
|
-
i0.ɵɵelementStart(11, "p",
|
|
3769
|
+
i0.ɵɵelementStart(11, "p", 499);
|
|
3770
3770
|
i0.ɵɵtext(12, " Go to the ");
|
|
3771
3771
|
i0.ɵɵelementStart(13, "strong");
|
|
3772
3772
|
i0.ɵɵtext(14, "Content Types");
|
|
3773
3773
|
i0.ɵɵelementEnd();
|
|
3774
3774
|
i0.ɵɵtext(15, " tab to create one. ");
|
|
3775
3775
|
i0.ɵɵelementEnd()();
|
|
3776
|
-
i0.ɵɵelementStart(16, "div",
|
|
3776
|
+
i0.ɵɵelementStart(16, "div", 481)(17, "button", 500);
|
|
3777
3777
|
i0.ɵɵlistener("mousedown", function AutotaggingPipelineResourceComponent_Conditional_29_Template_button_mousedown_17_listener() { i0.ɵɵrestoreView(_r109); const ctx_r2 = i0.ɵɵnextContext(); ctx_r2.ShowNoContentTypeWarning = false; ctx_r2.CloseForm(); return i0.ɵɵresetView(ctx_r2.SwitchTab("types")); });
|
|
3778
|
-
i0.ɵɵelement(18, "i",
|
|
3778
|
+
i0.ɵɵelement(18, "i", 501);
|
|
3779
3779
|
i0.ɵɵtext(19, " Go to Content Types ");
|
|
3780
3780
|
i0.ɵɵelementEnd();
|
|
3781
3781
|
i0.ɵɵelementStart(20, "button", 24);
|
|
@@ -7882,7 +7882,7 @@ let AutotaggingPipelineResourceComponent = class AutotaggingPipelineResourceComp
|
|
|
7882
7882
|
this.cdr.detectChanges();
|
|
7883
7883
|
}
|
|
7884
7884
|
static ɵfac = /*@__PURE__*/ (() => { let ɵAutotaggingPipelineResourceComponent_BaseFactory; return function AutotaggingPipelineResourceComponent_Factory(__ngFactoryType__) { return (ɵAutotaggingPipelineResourceComponent_BaseFactory || (ɵAutotaggingPipelineResourceComponent_BaseFactory = i0.ɵɵgetInheritedFactory(AutotaggingPipelineResourceComponent)))(__ngFactoryType__ || AutotaggingPipelineResourceComponent); }; })();
|
|
7885
|
-
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AutotaggingPipelineResourceComponent, selectors: [["app-autotagging-pipeline-resource"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 30, vars: 16, consts: [[1, "at-dashboard"], [1, "at-left-nav"], [1, "at-left-nav-header"], [1, "fa-solid", "fa-tags"], [1, "at-left-nav-items"], [1, "at-nav-item", 3, "active"], [1, "at-nav-divider"], [1, "at-nav-item", 3, "click"], [1, "fa-solid", "fa-clock-rotate-left"], [1, "at-left-nav-footer"], [1, "at-run-pipeline-btn", 3, "click", "disabled"], [1, "at-main-area"], [1, "at-loading-overlay"], [1, "at-schedule-overlay"], [1, "at-schedule-dialog-overlay", 2, "z-index", "10001"], [1, "at-nav-badge", 3, "at-nav-badge-live"], [1, "at-nav-badge"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-play"], ["text", "Loading autotagging data..."], [1, "at-page-header"], [1, "at-page-title"], [1, "at-page-subtitle"], [1, "at-page-actions"], [1, "at-action-btn", "at-secondary-btn", 3, "click"], [1, "fa-solid", "fa-arrows-rotate"], [1, "at-page-body"], [1, "at-kpi-strip"], [1, "at-kpi-card"], [1, "at-pipeline-layout"], [1, "at-pipeline-center"], [1, "at-pipeline-stages"], [1, "at-progress-section"], [1, "at-card", 2, "margin-bottom", "12px"], [1, "at-card", "at-feed-card"], [1, "at-card-header"], [1, "at-card-title"], [1, "fa-solid", "fa-bolt"], [1, "at-feed-header-actions"], [1, "at-feed-sort-btn", 3, "click", "title"], [1, "fa-solid"], [1, "at-feed-count"], [1, "at-feed-search-bar"], [1, "fa-solid", "fa-search", "at-feed-search-icon"], ["type", "text", "placeholder", "Search items, sources, or tags...", 1, "mj-input", "at-feed-search-input", 3, "ngModelChange", "input", "ngModel"], [1, "at-feed-search-clear"], [1, "at-card-body", "at-feed-scroll-body"], [1, "at-empty-state"], [1, "at-feed-item", "at-feed-item-clickable"], [1, "at-feed-pagination"], [1, "at-pipeline-right"], [1, "at-card"], [1, "fa-solid", "fa-database"], [1, "at-card-body"], [1, "at-source-mini"], [1, "at-card", "at-tag-cloud-card"], [1, "at-card-title", 2, "margin-bottom", "10px"], [1, "fa-solid", "fa-chart-bar"], [1, "at-tag-cloud"], [1, "at-tag-pill", 3, "class", "opacity", "title"], [1, "at-card", "at-config-card"], [1, "at-card-header", 2, "cursor", "pointer", 3, "click"], [1, "fa-solid", "fa-sliders"], [1, "fa-solid", 2, "font-size", "0.7rem", "color", "var(--mj-text-muted)"], [1, "at-card-body", "at-config-body"], [1, "at-kpi-value"], [1, "at-kpi-label"], [1, "at-kpi-trend", 3, "up"], [1, "at-kpi-trend"], [1, "fa-solid", "fa-arrow-up"], [1, "at-pipeline-stage"], [1, "at-pipeline-stage-icon"], [1, "fa-solid", "fa-check"], [3, "class"], [1, "at-pipeline-stage-name"], [1, "at-stage-connector", 3, "connector-complete"], [1, "at-stage-connector"], [1, "at-progress-header"], [1, "at-progress-stage-label"], [1, "at-progress-pct"], [1, "at-progress-bar"], [1, "at-progress-fill"], [1, "at-progress-current"], [1, "at-pipeline-controls"], [1, "at-action-btn", "at-secondary-btn", 3, "disabled"], [1, "at-action-btn", "at-primary-btn"], [1, "at-action-btn", "at-danger-btn", 3, "click"], [1, "fa-solid", "fa-stop"], [1, "at-action-btn", "at-secondary-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-pause"], [1, "at-action-btn", "at-primary-btn", 3, "click"], [1, "fa-solid", "fa-list-check"], [1, "at-action-btn", "at-secondary-btn", 2, "font-size", "11px", "padding", "3px 8px", 3, "click"], [1, "at-card-body", 2, "max-height", "200px", "overflow-y", "auto"], [1, "at-run-table"], [1, "at-run-source-name"], [1, "at-run-status-badge"], [1, "fa-solid", "fa-spinner", "fa-spin", 2, "font-size", "0.55rem"], [1, "at-feed-search-clear", 3, "click"], [1, "fa-solid", "fa-times"], [1, "fa-solid", "fa-inbox"], [1, "at-feed-item", "at-feed-item-clickable", 3, "click"], [1, "at-feed-status-dot"], [1, "at-feed-item-content"], [1, "at-feed-item-name"], [1, "at-feed-item-source-label"], [1, "at-feed-item-tags"], [1, "at-feed-tag"], [1, "at-feed-item-time"], [1, "fa-solid", "fa-chevron-left"], [1, "at-feed-pagination-label"], [1, "fa-solid", "fa-chevron-right"], [1, "at-source-mini-icon"], [1, "at-source-mini-info"], [1, "at-source-mini-name"], [1, "at-source-mini-meta"], [1, "at-source-mini-status"], [1, "at-tag-pill", 3, "title"], [1, "at-config-row"], [1, "at-config-label"], [1, "at-config-control"], ["type", "number", "min", "10", "max", "1000", "step", "10", 1, "at-config-input", 3, "ngModelChange", "ngModel"], ["type", "range", "min", "0", "max", "5000", "step", "100", 1, "at-config-slider", 3, "ngModelChange", "ngModel"], [1, "at-config-value"], ["type", "range", "min", "5", "max", "50", "step", "5", 1, "at-config-slider", 3, "ngModelChange", "ngModel"], [1, "at-config-toggle"], ["type", "checkbox", 3, "ngModelChange", "ngModel"], [1, "at-toggle-track"], [1, "at-toggle-thumb"], [1, "at-config-divider"], [1, "at-config-section-label"], ["type", "number", "min", "1", "max", "500", 1, "at-config-input", 3, "ngModelChange", "ngModel"], ["type", "number", "min", "1000", "max", "5000000", "step", "10000", 1, "at-config-input", 3, "ngModelChange", "ngModel"], ["type", "number", "min", "1", "max", "1000", 1, "at-config-input", 3, "ngModelChange", "ngModel"], [1, "fa-solid", "fa-plus"], [1, "at-sources-grid"], [1, "at-source-card-full", "at-source-card-clickable"], [1, "at-add-source-card", 3, "click"], [1, "fa-solid", "fa-plus-circle"], [2, "font-size", "0.85rem", "font-weight", "600"], [2, "font-size", "0.72rem"], [1, "at-dedup-section"], [1, "at-dedup-header"], [1, "at-dedup-title"], [1, "fa-solid", "fa-clone"], [1, "at-dedup-subtitle"], [1, "at-dedup-list"], [1, "at-dedup-empty"], [1, "at-source-card-full", "at-source-card-clickable", 3, "click"], [1, "at-source-card-header"], [1, "at-source-card-icon"], [1, "at-source-card-title"], [1, "at-source-card-type"], [1, "at-source-card-status"], [1, "at-source-card-url"], [1, "at-source-card-stats"], [1, "at-source-stat"], [1, "at-source-stat-value"], [1, "at-source-stat-label"], ["title", "Click to remove schedule", 1, "at-schedule-indicator"], [1, "at-source-card-actions"], [1, "at-source-action-btn", 3, "click"], [1, "fa-solid", "fa-pen"], [1, "at-source-action-btn", "at-source-schedule-btn"], [1, "at-source-action-btn", "at-source-delete-btn", 3, "click"], [1, "fa-solid", "fa-trash"], ["title", "Click to remove schedule", 1, "at-schedule-indicator", 3, "click"], [1, "fa-regular", "fa-clock"], [1, "at-source-action-btn", "at-source-schedule-btn", 3, "click"], [1, "at-dedup-card"], [1, "at-dedup-pair"], [1, "at-dedup-item"], [1, "at-dedup-item-name"], [1, "at-dedup-item-source"], [1, "at-dedup-vs"], [1, "fa-solid", "fa-arrows-left-right"], [1, "at-dedup-meta"], [1, "at-dedup-score"], [1, "at-dedup-method"], [1, "at-dedup-actions"], [1, "at-action-btn", "at-primary-btn", "at-dedup-confirm", 3, "click"], [1, "at-action-btn", "at-secondary-btn", "at-dedup-dismiss", 3, "click"], [1, "fa-solid", "fa-check-circle"], [1, "at-ct-grid"], [1, "at-ct-card"], [1, "at-add-type-card", 3, "click"], [1, "at-ct-card-header"], [1, "at-ct-card-name"], [1, "at-ct-card-model"], [1, "at-ct-field"], [1, "at-ct-field-label"], [1, "at-ct-field-value"], [1, "at-ct-tag-range"], [1, "at-ct-tag-range-bar"], [1, "at-ct-tag-range-fill"], [1, "at-ct-range-suffix"], [1, "at-source-card-actions", 2, "margin-top", "10px"], ["type", "text", "placeholder", "Search tags...", 1, "at-search-input", 3, "ngModelChange", "input", "ngModel"], [1, "at-tag-lib-layout"], [1, "at-tag-lib-main"], [1, "at-card-body", 2, "max-height", "500px", "overflow-y", "auto"], [1, "at-tag-table"], [1, "at-tag-row-clickable", 3, "at-tag-row-selected"], [1, "at-card", 2, "margin-top", "12px"], [1, "at-tag-lib-sidebar"], [1, "at-card-title", 2, "margin-bottom", "12px"], [1, "fa-solid", "fa-cloud"], ["Layout", "spiral", "ColorMode", "weight-gradient", 3, "Items", "MaxFontSize", "MinFontSize", "MaxItems", "Interactive", "Animate"], [1, "at-tag-cloud-empty"], [1, "at-card", 2, "padding", "16px", "margin-top", "12px"], [1, "fa-solid", "fa-chart-pie"], [1, "at-tags-by-source"], [1, "at-tag-source-row"], [1, "at-tag-row-clickable", 3, "click"], [1, "at-tag-name-cell"], [1, "at-weight-indicator"], [1, "at-weight-bar"], [1, "at-weight-bar-fill"], [1, "at-weight-value"], [1, "at-tag-bar"], [1, "at-tag-bar-fill"], [1, "fa-solid", "fa-tag"], [1, "at-slide-close", 2, "background", "none", "border", "none", "cursor", "pointer", "color", "var(--mj-text-muted)", 3, "click"], [1, "at-card-body", 2, "max-height", "300px", "overflow-y", "auto"], [1, "at-tag-row-clickable"], [1, "at-tax-tab-strip"], [1, "at-tax-tab", 3, "click"], [1, "fa-solid", "fa-sitemap"], [1, "fa-solid", "fa-link"], [1, "at-tax-tab-badge", "at-tax-badge-warning"], [1, "fa-solid", "fa-ban"], [1, "at-tax-tab-badge", "at-tax-badge-error"], [1, "fa-solid", "fa-chart-tree-map"], [1, "fa-solid", "fa-scroll"], [1, "at-tax-split-view"], [1, "at-tax-tree-panel", 2, "position", "relative"], [1, "at-tax-tree-toolbar"], ["type", "text", "placeholder", "Search tags...", 1, "at-search-input", 2, "flex", "1", 3, "ngModelChange", "input", "ngModel"], ["title", "Add root tag", 1, "at-tax-toolbar-btn", 3, "click"], ["title", "Toggle multi-select for drag reparenting", 1, "at-tax-toolbar-btn", 3, "click"], [1, "fa-solid", "fa-check-double"], [1, "at-tax-tree-body", 3, "dragover", "drop"], [1, "at-tax-tree-node", 3, "at-tax-node-selected", "at-tax-node-drag-over", "at-tax-node-multi-selected", "padding-left"], [1, "at-tax-tree-saving-overlay"], [1, "at-tax-details-panel"], [1, "at-empty-state", 2, "height", "100%"], [1, "at-tax-health-bar"], [1, "at-tax-health-label"], [1, "at-tax-health-stat"], [1, "at-tax-dot", "at-tax-dot-total"], [1, "at-tax-health-value"], [1, "at-tax-health-text"], [1, "at-tax-dot", "at-tax-dot-healthy"], [1, "at-tax-health-value", "at-tax-val-success"], [1, "at-tax-dot", "at-tax-dot-attention"], [1, "at-tax-health-value", "at-tax-val-warning"], [1, "at-tax-dot", "at-tax-dot-orphaned"], [1, "at-tax-health-value", "at-tax-val-error"], [1, "at-tax-dot", "at-tax-dot-duplicates"], [1, "at-tax-health-value", "at-tax-val-info"], [1, "at-tax-tree-node", 3, "dragstart", "dragover", "dragleave", "drop", "click"], ["type", "checkbox", 1, "at-tax-tree-checkbox", 3, "checked"], [1, "at-tax-tree-arrow", 3, "click"], [1, "at-tax-health-dot"], [1, "at-tax-tree-label"], [1, "at-tax-tree-count"], ["title", "Add child tag", 1, "at-tax-tree-add-child", 3, "click"], ["type", "checkbox", 1, "at-tax-tree-checkbox", 3, "click", "checked"], ["text", "Moving tags...", "size", "small"], [1, "at-tax-details-header"], [1, "at-tax-breadcrumb"], [1, "at-tax-bc-current"], [1, "at-tax-edit-form"], [1, "at-tax-stats-row"], [1, "at-tax-stat-item"], [1, "at-tax-stat-value"], [1, "at-tax-stat-label"], [1, "at-tax-stat-value", "at-tax-stat-date"], [1, "at-tax-action-toolbar"], [1, "at-tax-detail-section"], [1, "at-tax-bc-link", 3, "click"], [1, "at-tax-bc-sep"], [1, "at-tax-details-title"], ["title", "Edit name", 1, "at-tax-edit-icon", 3, "click"], [1, "at-tax-details-desc"], [1, "at-form-group"], [1, "at-form-label"], ["type", "text", 1, "at-form-input", 3, "ngModelChange", "ngModel"], ["rows", "3", 1, "at-form-textarea", 3, "ngModelChange", "ngModel"], [1, "at-form-actions"], [1, "at-tax-action-btn", 3, "click"], [1, "fa-solid", "fa-arrows-up-down"], [1, "fa-solid", "fa-compress"], [1, "fa-solid", "fa-code-branch"], [1, "at-tax-action-btn", "at-tax-action-danger", 3, "click"], [1, "at-tax-section-title"], [1, "at-tax-child-chips"], [1, "at-tax-child-chip"], [1, "at-tax-child-chip", 3, "click"], [1, "at-tax-chip-count"], [1, "at-tax-recent-list"], [1, "at-tax-recent-item"], [1, "at-tax-recent-icon"], [1, "at-tax-recent-name"], [1, "at-tax-recent-weight"], [1, "at-tax-recent-date"], [1, "fa-solid", "fa-hand-pointer"], [1, "at-tax-dup-stats-bar"], [1, "at-tax-dup-stat"], [1, "at-tax-dup-stat", "at-tax-dup-high"], [1, "at-tax-dup-stat", "at-tax-dup-moderate"], [1, "at-tax-dup-list"], [1, "at-tax-dup-card", 3, "at-tax-dup-high", "at-tax-dup-moderate"], [1, "at-tax-dup-card"], [1, "at-tax-dup-tag"], [1, "at-tax-dup-similarity"], [1, "at-tax-sim-percent", "high"], [1, "at-tax-dup-actions"], [1, "at-tax-dup-btn", "at-tax-dup-btn-primary", 3, "click", "disabled"], [1, "at-tax-dup-btn", 3, "click"], [1, "at-tax-dup-arrow"], [1, "at-tax-sim-bar-bg"], [1, "at-tax-sim-bar-fill"], [1, "at-tax-sim-percent"], [1, "at-tax-orphan-toolbar"], [1, "at-tax-orphan-count"], [1, "at-tax-orphan-desc"], [1, "at-tax-orphan-bulk"], [1, "at-tax-bulk-btn", 3, "click"], [1, "fa-solid", "fa-square-check"], [1, "fa-regular", "fa-square"], [1, "at-tax-bulk-btn", "at-tax-bulk-danger", 3, "click"], [1, "fa-solid", "fa-trash-can"], [1, "at-tax-orphan-grid"], [1, "at-tax-orphan-card"], [1, "at-tax-orphan-header"], [1, "at-tax-orphan-name"], ["type", "checkbox", 1, "at-tax-orphan-checkbox", 3, "change", "click", "checked"], [1, "at-tax-orphan-stats"], [1, "at-tax-orphan-dates"], [1, "at-tax-orphan-actions"], [1, "at-tax-orphan-btn", "at-tax-orphan-delete", 3, "click"], [1, "at-tax-orphan-btn"], [1, "at-tax-treemap-kpi-strip"], [1, "at-tax-treemap-kpi"], [1, "at-tax-treemap-container"], [1, "at-tax-drillin-overlay"], [1, "at-tax-treemap-kpi-value"], [1, "at-tax-treemap-kpi-label"], [1, "at-tax-treemap-cell", "at-tax-treemap-cell-clickable", 3, "class", "grid-row"], [1, "at-tax-treemap-cell", "at-tax-treemap-cell-clickable", 3, "click"], [1, "at-tax-cell-name"], [1, "at-tax-cell-count"], [1, "at-tax-drillin-overlay", 3, "click"], [1, "at-tax-drillin-panel", 3, "click"], [1, "at-tax-drillin-header"], [1, "at-schedule-dialog-close", 3, "click"], [1, "at-tax-drillin-body"], [1, "at-tax-drillin-footer"], [1, "at-tax-audit-filters"], [1, "at-tax-audit-filter-label"], [1, "at-tax-audit-checkbox-group"], [1, "at-tax-audit-checkbox"], ["type", "checkbox", 3, "change", "checked"], [1, "at-tax-audit-timeline"], [1, "at-tax-audit-event"], [1, "at-tax-audit-event-icon"], [1, "at-tax-audit-event-body"], [1, "at-tax-audit-event-desc"], [1, "at-tax-tag-ref"], [1, "at-tax-audit-event-meta"], [1, "at-filter-select", 3, "ngModelChange", "change", "ngModel"], ["value", ""], [3, "value"], ["value", "complete"], ["value", "failed"], ["value", "running"], [1, "at-card-body", 2, "max-height", "600px", "overflow-y", "auto"], [1, "at-run-row-clickable", 3, "at-run-row-selected"], [1, "at-run-row-clickable", 3, "click"], [1, "at-run-duration"], [1, "fa-solid", "fa-magnifying-glass-chart"], ["text", "Loading run details...", "size", "small"], [1, "fa-solid", "fa-info-circle"], [1, "at-slide-overlay", 3, "click"], [1, "at-slide-panel"], [1, "at-slide-header"], [1, "at-slide-close", 3, "click"], [1, "at-slide-body"], ["type", "text", "placeholder", "Source name", 1, "at-form-input", 3, "ngModelChange", "ngModel"], [1, "at-form-select", 3, "ngModelChange", "ngModel"], ["SelectionMode", "single", "SelectableTypes", "leaf", "Placeholder", "Use system default", 3, "ValueChange", "BranchConfig", "LeafConfig", "Clearable", "Value"], [1, "at-form-hint"], [1, "at-action-btn", "at-primary-btn", 3, "click", "disabled"], [1, "at-required"], ["type", "url", 1, "at-form-input", 3, "ngModel", "placeholder"], ["type", "text", 1, "at-form-input", 3, "ngModel", "placeholder"], ["type", "text", 1, "at-form-input", 3, "ngModel", "placeholder", "value"], [1, "at-form-select", 3, "ngModel"], ["type", "url", 1, "at-form-input", 3, "ngModelChange", "ngModel", "placeholder"], ["type", "text", 1, "at-form-input", 3, "ngModelChange", "ngModel", "placeholder"], ["type", "text", 1, "at-form-input", 3, "ngModelChange", "ngModel", "placeholder", "value"], ["type", "text", "placeholder", "Content type name", 1, "at-form-input", 3, "ngModelChange", "ngModel"], ["rows", "3", "placeholder", "Description...", 1, "at-form-textarea", 3, "ngModelChange", "ngModel"], ["SelectionMode", "single", "SelectableTypes", "leaf", "Placeholder", "Select AI model...", 3, "ValueChange", "BranchConfig", "LeafConfig", "Clearable", "Value"], [1, "at-form-row"], [1, "at-form-group", 2, "flex", "1"], ["type", "number", "min", "0", 1, "at-form-input", 3, "ngModelChange", "ngModel"], ["type", "number", "min", "1", 1, "at-form-input", 3, "ngModelChange", "ngModel"], [1, "at-slide-panel", "at-detail-panel"], [1, "fa-solid", "fa-file-lines"], [1, "at-detail-item-header"], [1, "at-detail-item-name"], [1, "at-detail-badges"], [1, "at-detail-badge", "at-detail-badge-source"], [1, "at-detail-section"], [1, "at-detail-section-label"], [1, "at-detail-meta-grid"], [1, "at-detail-meta-row"], [1, "at-detail-meta-key"], [1, "at-detail-meta-value"], [1, "at-detail-actions"], [1, "fa-solid", "fa-external-link-alt"], ["disabled", "", 1, "at-action-btn", "at-secondary-btn"], [1, "fa-solid", "fa-magnifying-glass"], [1, "at-detail-badge", "at-detail-badge-type"], [1, "at-detail-badge", "at-detail-badge-file"], [1, "fa-solid", "fa-file"], ["target", "_blank", 1, "at-detail-link", 3, "href"], [1, "fa-solid", "fa-external-link-alt", 2, "font-size", "0.65rem", "margin-left", "4px"], [1, "at-detail-text-preview"], [1, "at-detail-tags"], [1, "at-tag-pill", "at-tag-weighted", 3, "font-size"], [1, "at-tag-pill", "at-tag-weighted"], [1, "at-tag-weight"], [1, "at-detail-meta-value", "at-detail-meta-mono"], ["text", "Loading source details..."], [1, "at-detail-source-header"], [1, "at-detail-badge"], [1, "at-detail-stats-strip"], [1, "at-detail-stat"], [1, "at-detail-stat-value"], [1, "at-detail-stat-label"], [1, "at-detail-section-header-row"], [1, "at-detail-section-controls"], [1, "at-detail-filter-select", 3, "ngModelChange", "ngModel"], ["title", "Re-queue failed items for processing", 1, "at-action-btn", "at-retry-btn", 3, "click"], [1, "fa-solid", "fa-rotate-right"], [1, "at-detail-content-list"], [1, "at-empty-state", 2, "padding", "16px"], [1, "at-detail-content-item"], [1, "at-detail-pagination"], [1, "at-action-btn", "at-secondary-btn", "at-source-delete-btn", 3, "click"], ["target", "_blank", 1, "at-detail-link", "at-detail-meta-value", 3, "href"], [1, "at-detail-content-item", 3, "click"], [1, "at-detail-content-item-name"], [1, "at-status-badge"], [1, "at-detail-content-item-tags"], [1, "at-detail-content-item-time"], [1, "at-page-btn", 3, "click", "disabled"], [1, "at-page-info"], [1, "at-detail-run-history"], [1, "at-detail-run-row"], [1, "at-detail-run-time"], [1, "at-detail-run-duration"], [1, "at-detail-run-items"], [1, "at-schedule-overlay", 3, "click"], [1, "at-schedule-dialog", 3, "click"], [1, "at-schedule-dialog-header"], [1, "at-schedule-dialog-body"], [1, "at-schedule-field"], [1, "at-schedule-source-name"], [1, "at-schedule-action-name"], ["for", "schedule-cron"], ["id", "schedule-cron", "type", "text", "placeholder", "0 2 * * *", 1, "mj-input", "at-schedule-cron-input", 3, "ngModelChange", "ngModel"], [1, "at-schedule-cron-preview"], [1, "at-schedule-field", "at-schedule-toggle-row"], ["for", "schedule-enabled"], ["id", "schedule-enabled", "type", "checkbox", 1, "mj-checkbox", 3, "ngModelChange", "ngModel"], [1, "at-schedule-dialog-footer"], [1, "fa-solid", "fa-triangle-exclamation"], [1, "at-confirm-message"], ["type", "text", "placeholder", "Tag A, Tag B, Tag C", 1, "mj-input", 2, "width", "100%", 3, "ngModelChange", "ngModel"], [1, "at-tax-create-context"], ["type", "text", "placeholder", "Tag name", 1, "mj-input", 2, "width", "100%", 3, "ngModelChange", "keydown.enter", "ngModel"], ["rows", "3", "placeholder", "Brief description of this tag", 1, "mj-textarea", 2, "width", "100%", 3, "ngModelChange", "ngModel"], [1, "mj-input", 2, "width", "100%", 3, "ngModelChange", "ngModel"], [3, "ngValue"], [2, "font-size", "0.85rem", "color", "var(--mj-text-secondary)", "margin", "0 0 12px"], ["TextField", "Label", "ValueField", "ID", "Placeholder", "Search and select a tag...", 3, "ValueChange", "Data", "Filterable", "ValuePrimitive"], [1, "at-schedule-dialog-overlay", 2, "z-index", "10001", 3, "click"], [1, "at-schedule-dialog", 2, "max-width", "440px", "z-index", "10002", 3, "click"], [1, "fa-solid", "fa-triangle-exclamation", 2, "color", "var(--mj-status-warning)", "margin-right", "8px"], [1, "at-close-btn", 3, "click"], [1, "fa-solid", "fa-xmark"], [1, "at-schedule-dialog-body", 2, "text-align", "center", "padding", "24px"], [2, "color", "var(--mj-text-secondary)", "margin-bottom", "16px"], [2, "color", "var(--mj-text-muted)", "font-size", "13px"], [1, "at-action-btn", "at-primary-btn", 3, "mousedown"], [1, "fa-solid", "fa-arrow-right"]], template: function AutotaggingPipelineResourceComponent_Template(rf, ctx) { if (rf & 1) {
|
|
7885
|
+
static ɵcmp = /*@__PURE__*/ i0.ɵɵdefineComponent({ type: AutotaggingPipelineResourceComponent, selectors: [["app-autotagging-pipeline-resource"]], standalone: false, features: [i0.ɵɵInheritDefinitionFeature], decls: 30, vars: 16, consts: [[1, "at-dashboard"], [1, "at-left-nav"], [1, "at-left-nav-header"], [1, "fa-solid", "fa-tags"], [1, "at-left-nav-items"], [1, "at-nav-item", 3, "active"], [1, "at-nav-divider"], [1, "at-nav-item", 3, "click"], [1, "fa-solid", "fa-clock-rotate-left"], [1, "at-left-nav-footer"], [1, "at-run-pipeline-btn", 3, "click", "disabled"], [1, "at-main-area"], [1, "at-loading-overlay"], [1, "at-schedule-overlay"], [1, "at-schedule-dialog-overlay", 2, "z-index", "10001"], [1, "at-nav-badge", 3, "at-nav-badge-live"], [1, "at-nav-badge"], [1, "fa-solid", "fa-spinner", "fa-spin"], [1, "fa-solid", "fa-play"], ["text", "Loading autotagging data..."], [1, "at-page-header"], [1, "at-page-title"], [1, "at-page-subtitle"], [1, "at-page-actions"], [1, "at-action-btn", "at-secondary-btn", 3, "click"], [1, "fa-solid", "fa-arrows-rotate"], [1, "at-page-body"], [1, "at-kpi-strip"], [1, "at-kpi-card"], [1, "at-pipeline-layout"], [1, "at-pipeline-center"], [1, "at-pipeline-stages"], [1, "at-progress-section"], [1, "at-card", 2, "margin-bottom", "12px"], [1, "at-card", "at-feed-card"], [1, "at-card-header"], [1, "at-card-title"], [1, "fa-solid", "fa-bolt"], [1, "at-feed-header-actions"], [1, "at-feed-sort-btn", 3, "click", "title"], [1, "fa-solid"], [1, "at-feed-count"], [1, "at-feed-search-bar"], [1, "fa-solid", "fa-search", "at-feed-search-icon"], ["type", "text", "placeholder", "Search items, sources, or tags...", 1, "mj-input", "at-feed-search-input", 3, "ngModelChange", "input", "ngModel"], [1, "at-feed-search-clear"], [1, "at-card-body", "at-feed-scroll-body"], [1, "at-empty-state"], [1, "at-feed-item", "at-feed-item-clickable"], [1, "at-feed-pagination"], [1, "at-pipeline-right"], [1, "at-card"], [1, "fa-solid", "fa-database"], [1, "at-card-body"], [1, "at-source-mini"], [1, "at-card", "at-tag-cloud-card"], [1, "at-card-title", 2, "margin-bottom", "10px"], [1, "fa-solid", "fa-chart-bar"], [1, "at-tag-cloud"], [1, "at-tag-pill", 3, "class", "opacity", "title"], [1, "at-card", "at-config-card"], [1, "at-card-header", 2, "cursor", "pointer", 3, "click"], [1, "fa-solid", "fa-sliders"], [1, "fa-solid", 2, "font-size", "0.7rem", "color", "var(--mj-text-muted)"], [1, "at-card-body", "at-config-body"], [1, "at-kpi-value"], [1, "at-kpi-label"], [1, "at-kpi-trend", 3, "up"], [1, "at-kpi-trend"], [1, "fa-solid", "fa-arrow-up"], [1, "at-pipeline-stage"], [1, "at-pipeline-stage-icon"], [1, "fa-solid", "fa-check"], [3, "class"], [1, "at-pipeline-stage-name"], [1, "at-stage-connector", 3, "connector-complete"], [1, "at-stage-connector"], [1, "at-progress-header"], [1, "at-progress-stage-label"], [1, "at-progress-pct"], [1, "at-progress-bar"], [1, "at-progress-fill"], [1, "at-progress-current"], [1, "at-pipeline-controls"], [1, "at-action-btn", "at-secondary-btn", 3, "disabled"], [1, "at-action-btn", "at-primary-btn"], [1, "at-action-btn", "at-danger-btn", 3, "click"], [1, "fa-solid", "fa-stop"], [1, "at-action-btn", "at-secondary-btn", 3, "click", "disabled"], [1, "fa-solid", "fa-pause"], [1, "at-action-btn", "at-primary-btn", 3, "click"], [1, "fa-solid", "fa-list-check"], [1, "at-action-btn", "at-secondary-btn", 2, "font-size", "11px", "padding", "3px 8px", 3, "click"], [1, "at-card-body", 2, "max-height", "200px", "overflow-y", "auto"], [1, "at-run-table"], [1, "at-run-source-name"], [1, "at-run-status-badge"], [1, "fa-solid", "fa-spinner", "fa-spin", 2, "font-size", "0.55rem"], [1, "at-feed-search-clear", 3, "click"], [1, "fa-solid", "fa-times"], [1, "fa-solid", "fa-inbox"], [1, "at-feed-item", "at-feed-item-clickable", 3, "click"], [1, "at-feed-status-dot"], [1, "at-feed-item-content"], [1, "at-feed-item-name"], [1, "at-feed-item-source-label"], [1, "at-feed-item-tags"], [1, "at-feed-tag"], [1, "at-feed-item-time"], [1, "fa-solid", "fa-chevron-left"], [1, "at-feed-pagination-label"], [1, "fa-solid", "fa-chevron-right"], [1, "at-source-mini-icon"], [1, "at-source-mini-info"], [1, "at-source-mini-name"], [1, "at-source-mini-meta"], [1, "at-source-mini-status"], [1, "at-tag-pill", 3, "title"], [1, "at-config-row"], [1, "at-config-label"], [1, "at-config-control"], ["type", "number", "min", "10", "max", "1000", "step", "10", 1, "at-config-input", 3, "ngModelChange", "ngModel"], ["type", "range", "min", "0", "max", "5000", "step", "100", 1, "at-config-slider", 3, "ngModelChange", "ngModel"], [1, "at-config-value"], ["type", "range", "min", "5", "max", "50", "step", "5", 1, "at-config-slider", 3, "ngModelChange", "ngModel"], [1, "at-config-toggle"], ["type", "checkbox", 3, "ngModelChange", "ngModel"], [1, "at-toggle-track"], [1, "at-toggle-thumb"], [1, "at-config-divider"], [1, "at-config-section-label"], ["type", "number", "min", "1", "max", "500", 1, "at-config-input", 3, "ngModelChange", "ngModel"], ["type", "number", "min", "1000", "max", "5000000", "step", "10000", 1, "at-config-input", 3, "ngModelChange", "ngModel"], ["type", "number", "min", "1", "max", "1000", 1, "at-config-input", 3, "ngModelChange", "ngModel"], [1, "fa-solid", "fa-plus"], [1, "at-sources-grid"], [1, "at-source-card-full", "at-source-card-clickable"], [1, "at-add-source-card", 3, "click"], [1, "fa-solid", "fa-plus-circle"], [2, "font-size", "0.85rem", "font-weight", "600"], [2, "font-size", "0.72rem"], [1, "at-dedup-section"], [1, "at-dedup-header"], [1, "at-dedup-title"], [1, "fa-solid", "fa-clone"], [1, "at-dedup-subtitle"], [1, "at-dedup-list"], [1, "at-dedup-empty"], [1, "at-source-card-full", "at-source-card-clickable", 3, "click"], [1, "at-source-card-header"], [1, "at-source-card-icon"], [1, "at-source-card-title"], [1, "at-source-card-type"], [1, "at-source-card-status"], [1, "at-source-card-url"], [1, "at-source-card-stats"], [1, "at-source-stat"], [1, "at-source-stat-value"], [1, "at-source-stat-label"], ["title", "Click to remove schedule", 1, "at-schedule-indicator"], [1, "at-source-card-actions"], [1, "at-source-action-btn", 3, "click"], [1, "fa-solid", "fa-pen"], [1, "at-source-action-btn", "at-source-schedule-btn"], [1, "at-source-action-btn", "at-source-delete-btn", 3, "click"], [1, "fa-solid", "fa-trash"], ["title", "Click to remove schedule", 1, "at-schedule-indicator", 3, "click"], [1, "fa-regular", "fa-clock"], [1, "at-source-action-btn", "at-source-schedule-btn", 3, "click"], [1, "at-dedup-card"], [1, "at-dedup-pair"], [1, "at-dedup-item"], [1, "at-dedup-item-name"], [1, "at-dedup-item-source"], [1, "at-dedup-vs"], [1, "fa-solid", "fa-arrows-left-right"], [1, "at-dedup-meta"], [1, "at-dedup-score"], [1, "at-dedup-method"], [1, "at-dedup-actions"], [1, "at-action-btn", "at-primary-btn", "at-dedup-confirm", 3, "click"], [1, "at-action-btn", "at-secondary-btn", "at-dedup-dismiss", 3, "click"], [1, "fa-solid", "fa-check-circle"], [1, "at-ct-grid"], [1, "at-ct-card"], [1, "at-add-type-card", 3, "click"], [1, "at-ct-card-header"], [1, "at-ct-card-name"], [1, "at-ct-card-model"], [1, "at-ct-field"], [1, "at-ct-field-label"], [1, "at-ct-field-value"], [1, "at-ct-tag-range"], [1, "at-ct-tag-range-bar"], [1, "at-ct-tag-range-fill"], [1, "at-ct-range-suffix"], [1, "at-source-card-actions", 2, "margin-top", "10px"], ["type", "text", "placeholder", "Search tags...", 1, "at-search-input", 3, "ngModelChange", "input", "ngModel"], [1, "at-tag-lib-layout"], [1, "at-tag-lib-main"], [1, "at-card-body", 2, "max-height", "500px", "overflow-y", "auto"], [1, "at-tag-table"], [1, "at-tag-row-clickable", 3, "at-tag-row-selected"], [1, "at-card", 2, "margin-top", "12px"], [1, "at-tag-lib-sidebar"], [1, "at-card-title", 2, "margin-bottom", "12px"], [1, "fa-solid", "fa-cloud"], ["Layout", "spiral", "ColorMode", "weight-gradient", 3, "Items", "MaxFontSize", "MinFontSize", "MaxItems", "Interactive", "Animate"], [1, "at-tag-cloud-empty"], [1, "at-card", 2, "padding", "16px", "margin-top", "12px"], [1, "fa-solid", "fa-chart-pie"], [1, "at-tags-by-source"], [1, "at-tag-source-row"], [1, "at-tag-row-clickable", 3, "click"], [1, "at-tag-name-cell"], [1, "at-weight-indicator"], [1, "at-weight-bar"], [1, "at-weight-bar-fill"], [1, "at-weight-value"], [1, "at-tag-bar"], [1, "at-tag-bar-fill"], [1, "fa-solid", "fa-tag"], [1, "at-slide-close", 2, "background", "none", "border", "none", "cursor", "pointer", "color", "var(--mj-text-muted)", 3, "click"], [1, "at-card-body", 2, "max-height", "300px", "overflow-y", "auto"], [1, "at-tag-row-clickable"], [1, "at-tax-tab-strip"], [1, "at-tax-tab", 3, "click"], [1, "fa-solid", "fa-sitemap"], [1, "fa-solid", "fa-link"], [1, "at-tax-tab-badge", "at-tax-badge-warning"], [1, "fa-solid", "fa-ban"], [1, "at-tax-tab-badge", "at-tax-badge-error"], [1, "fa-solid", "fa-chart-tree-map"], [1, "fa-solid", "fa-scroll"], [1, "at-tax-split-view"], [1, "at-tax-tree-panel", 2, "position", "relative"], [1, "at-tax-tree-toolbar"], ["type", "text", "placeholder", "Search tags...", 1, "at-search-input", 2, "flex", "1", 3, "ngModelChange", "input", "ngModel"], ["title", "Add root tag", 1, "at-tax-toolbar-btn", 3, "click"], ["title", "Toggle multi-select for drag reparenting", 1, "at-tax-toolbar-btn", 3, "click"], [1, "fa-solid", "fa-check-double"], [1, "at-tax-tree-body", 3, "dragover", "drop"], [1, "at-tax-tree-node", 3, "at-tax-node-selected", "at-tax-node-drag-over", "at-tax-node-multi-selected", "padding-left"], [1, "at-tax-tree-saving-overlay"], [1, "at-tax-details-panel"], [1, "at-empty-state", 2, "height", "100%"], [1, "at-tax-health-bar"], [1, "at-tax-health-label"], [1, "at-tax-health-stat"], [1, "at-tax-dot", "at-tax-dot-total"], [1, "at-tax-health-value"], [1, "at-tax-health-text"], [1, "at-tax-dot", "at-tax-dot-healthy"], [1, "at-tax-health-value", "at-tax-val-success"], [1, "at-tax-dot", "at-tax-dot-attention"], [1, "at-tax-health-value", "at-tax-val-warning"], [1, "at-tax-dot", "at-tax-dot-orphaned"], [1, "at-tax-health-value", "at-tax-val-error"], [1, "at-tax-dot", "at-tax-dot-duplicates"], [1, "at-tax-health-value", "at-tax-val-info"], [1, "at-tax-tree-node", 3, "dragstart", "dragover", "dragleave", "drop", "click"], ["type", "checkbox", 1, "at-tax-tree-checkbox", 3, "checked"], [1, "at-tax-tree-arrow", 3, "click"], [1, "at-tax-health-dot"], [1, "at-tax-tree-label"], [1, "at-tax-tree-count"], ["title", "Add child tag", 1, "at-tax-tree-add-child", 3, "click"], ["type", "checkbox", 1, "at-tax-tree-checkbox", 3, "click", "checked"], ["text", "Moving tags...", "size", "small"], [1, "at-tax-details-header"], [1, "at-tax-breadcrumb"], [1, "at-tax-bc-current"], [1, "at-tax-edit-form"], [1, "at-tax-stats-row"], [1, "at-tax-stat-item"], [1, "at-tax-stat-value"], [1, "at-tax-stat-label"], [1, "at-tax-stat-value", "at-tax-stat-date"], [1, "at-tax-action-toolbar"], [1, "at-tax-detail-section"], [1, "at-tax-bc-link", 3, "click"], [1, "at-tax-bc-sep"], [1, "at-tax-details-title"], ["title", "Edit name", 1, "at-tax-edit-icon", 3, "click"], [1, "at-tax-details-desc"], [1, "at-form-group"], [1, "at-form-label"], ["type", "text", 1, "at-form-input", 3, "ngModelChange", "ngModel"], ["rows", "3", 1, "at-form-textarea", 3, "ngModelChange", "ngModel"], [1, "at-form-actions"], [1, "at-tax-action-btn", 3, "click"], [1, "fa-solid", "fa-arrows-up-down"], [1, "fa-solid", "fa-compress"], [1, "fa-solid", "fa-code-branch"], [1, "at-tax-action-btn", "at-tax-action-danger", 3, "click"], [1, "at-tax-section-title"], [1, "at-tax-child-chips"], [1, "at-tax-child-chip"], [1, "at-tax-child-chip", 3, "click"], [1, "at-tax-chip-count"], [1, "at-tax-recent-list"], [1, "at-tax-recent-item"], [1, "at-tax-recent-icon"], [1, "at-tax-recent-name"], [1, "at-tax-recent-weight"], [1, "at-tax-recent-date"], [1, "fa-solid", "fa-hand-pointer"], [1, "at-tax-dup-stats-bar"], [1, "at-tax-dup-stat"], [1, "at-tax-dup-stat", "at-tax-dup-high"], [1, "at-tax-dup-stat", "at-tax-dup-moderate"], [1, "at-tax-dup-list"], [1, "at-tax-dup-card", 3, "at-tax-dup-high", "at-tax-dup-moderate"], [1, "at-tax-dup-card"], [1, "at-tax-dup-tag"], [1, "at-tax-dup-similarity"], [1, "at-tax-sim-percent", "high"], [1, "at-tax-dup-actions"], [1, "at-tax-dup-btn", "at-tax-dup-btn-primary", 3, "click", "disabled"], [1, "at-tax-dup-btn", 3, "click"], [1, "at-tax-dup-arrow"], [1, "at-tax-sim-bar-bg"], [1, "at-tax-sim-bar-fill"], [1, "at-tax-sim-percent"], [1, "at-tax-orphan-toolbar"], [1, "at-tax-orphan-count"], [1, "at-tax-orphan-desc"], [1, "at-tax-orphan-bulk"], [1, "at-tax-bulk-btn", 3, "click"], [1, "fa-solid", "fa-square-check"], [1, "fa-regular", "fa-square"], [1, "at-tax-bulk-btn", "at-tax-bulk-danger", 3, "click"], [1, "fa-solid", "fa-trash-can"], [1, "at-tax-orphan-grid"], [1, "at-tax-orphan-card"], [1, "at-tax-orphan-header"], [1, "at-tax-orphan-name"], ["type", "checkbox", 1, "at-tax-orphan-checkbox", 3, "change", "click", "checked"], [1, "at-tax-orphan-stats"], [1, "at-tax-orphan-dates"], [1, "at-tax-orphan-actions"], [1, "at-tax-orphan-btn", "at-tax-orphan-delete", 3, "click"], [1, "at-tax-orphan-btn"], [1, "at-tax-treemap-kpi-strip"], [1, "at-tax-treemap-kpi"], [1, "at-tax-treemap-container"], [1, "at-tax-drillin-overlay"], [1, "at-tax-treemap-kpi-value"], [1, "at-tax-treemap-kpi-label"], [1, "at-tax-treemap-cell", "at-tax-treemap-cell-clickable", 3, "class", "grid-row"], [1, "at-tax-treemap-cell", "at-tax-treemap-cell-clickable", 3, "click"], [1, "at-tax-cell-name"], [1, "at-tax-cell-count"], [1, "at-tax-drillin-overlay", 3, "click"], [1, "at-tax-drillin-panel", 3, "click"], [1, "at-tax-drillin-header"], [1, "at-schedule-dialog-close", 3, "click"], [1, "at-tax-drillin-body"], [1, "at-tax-drillin-footer"], [1, "at-tax-audit-filters"], [1, "at-tax-audit-filter-label"], [1, "at-tax-audit-checkbox-group"], [1, "at-tax-audit-checkbox"], ["type", "checkbox", 3, "change", "checked"], [1, "at-tax-audit-timeline"], [1, "at-tax-audit-event"], [1, "at-tax-audit-event-icon"], [1, "at-tax-audit-event-body"], [1, "at-tax-audit-event-desc"], [1, "at-tax-tag-ref"], [1, "at-tax-audit-event-meta"], [1, "at-filter-select", 3, "ngModelChange", "change", "ngModel"], ["value", ""], [3, "value"], ["value", "complete"], ["value", "failed"], ["value", "running"], [1, "at-card-body", 2, "max-height", "600px", "overflow-y", "auto"], [1, "at-run-row-clickable", 3, "at-run-row-selected"], [1, "at-run-row-clickable", 3, "click"], [1, "at-run-duration"], [1, "fa-solid", "fa-magnifying-glass-chart"], ["text", "Loading run details...", "size", "small"], [1, "fa-solid", "fa-info-circle"], [1, "at-slide-overlay", 3, "click"], [1, "at-slide-panel"], [1, "at-slide-header"], ["aria-label", "Close form", 1, "at-slide-close", 3, "click"], [1, "at-slide-body"], ["type", "text", "placeholder", "Source name", 1, "at-form-input", 3, "ngModelChange", "ngModel"], [1, "at-form-select", 3, "ngModelChange", "ngModel"], ["SelectionMode", "single", "SelectableTypes", "leaf", "Placeholder", "Use system default", 3, "ValueChange", "BranchConfig", "LeafConfig", "Clearable", "Value"], [1, "at-form-hint"], [1, "at-action-btn", "at-primary-btn", 3, "click", "disabled"], [1, "at-required"], ["type", "url", 1, "at-form-input", 3, "ngModel", "placeholder"], ["type", "text", 1, "at-form-input", 3, "ngModel", "placeholder"], ["type", "text", 1, "at-form-input", 3, "ngModel", "placeholder", "value"], [1, "at-form-select", 3, "ngModel"], ["type", "url", 1, "at-form-input", 3, "ngModelChange", "ngModel", "placeholder"], ["type", "text", 1, "at-form-input", 3, "ngModelChange", "ngModel", "placeholder"], ["type", "text", 1, "at-form-input", 3, "ngModelChange", "ngModel", "placeholder", "value"], ["type", "text", "placeholder", "Content type name", 1, "at-form-input", 3, "ngModelChange", "ngModel"], ["rows", "3", "placeholder", "Description...", 1, "at-form-textarea", 3, "ngModelChange", "ngModel"], ["SelectionMode", "single", "SelectableTypes", "leaf", "Placeholder", "Select AI model...", 3, "ValueChange", "BranchConfig", "LeafConfig", "Clearable", "Value"], [1, "at-form-row"], [1, "at-form-group", 2, "flex", "1"], ["type", "number", "min", "0", 1, "at-form-input", 3, "ngModelChange", "ngModel"], ["type", "number", "min", "1", 1, "at-form-input", 3, "ngModelChange", "ngModel"], [1, "at-slide-panel", "at-detail-panel"], [1, "fa-solid", "fa-file-lines"], ["aria-label", "Close item detail", 1, "at-slide-close", 3, "click"], [1, "at-detail-item-header"], [1, "at-detail-item-name"], [1, "at-detail-badges"], [1, "at-detail-badge", "at-detail-badge-source"], [1, "at-detail-section"], [1, "at-detail-section-label"], [1, "at-detail-meta-grid"], [1, "at-detail-meta-row"], [1, "at-detail-meta-key"], [1, "at-detail-meta-value"], [1, "at-detail-actions"], [1, "fa-solid", "fa-external-link-alt"], ["disabled", "", 1, "at-action-btn", "at-secondary-btn"], [1, "fa-solid", "fa-magnifying-glass"], [1, "at-detail-badge", "at-detail-badge-type"], [1, "at-detail-badge", "at-detail-badge-file"], [1, "fa-solid", "fa-file"], ["target", "_blank", 1, "at-detail-link", 3, "href"], [1, "fa-solid", "fa-external-link-alt", 2, "font-size", "0.65rem", "margin-left", "4px"], [1, "at-detail-text-preview"], [1, "at-detail-tags"], [1, "at-tag-pill", "at-tag-weighted", 3, "font-size"], [1, "at-tag-pill", "at-tag-weighted"], [1, "at-tag-weight"], [1, "at-detail-meta-value", "at-detail-meta-mono"], ["aria-label", "Close source detail", 1, "at-slide-close", 3, "click"], ["text", "Loading source details..."], [1, "at-detail-source-header"], [1, "at-detail-badge"], [1, "at-detail-stats-strip"], [1, "at-detail-stat"], [1, "at-detail-stat-value"], [1, "at-detail-stat-label"], [1, "at-detail-section-header-row"], [1, "at-detail-section-controls"], [1, "at-detail-filter-select", 3, "ngModelChange", "ngModel"], ["title", "Re-queue failed items for processing", 1, "at-action-btn", "at-retry-btn", 3, "click"], [1, "fa-solid", "fa-rotate-right"], [1, "at-detail-content-list"], [1, "at-empty-state", 2, "padding", "16px"], [1, "at-detail-content-item"], [1, "at-detail-pagination"], [1, "at-action-btn", "at-secondary-btn", "at-source-delete-btn", 3, "click"], ["target", "_blank", 1, "at-detail-link", "at-detail-meta-value", 3, "href"], [1, "at-detail-content-item", 3, "click"], [1, "at-detail-content-item-name"], [1, "at-status-badge"], [1, "at-detail-content-item-tags"], [1, "at-detail-content-item-time"], [1, "at-page-btn", 3, "click", "disabled"], [1, "at-page-info"], [1, "at-detail-run-history"], [1, "at-detail-run-row"], [1, "at-detail-run-time"], [1, "at-detail-run-duration"], [1, "at-detail-run-items"], [1, "at-schedule-overlay", 3, "click"], [1, "at-schedule-dialog", 3, "click"], [1, "at-schedule-dialog-header"], [1, "at-schedule-dialog-body"], [1, "at-schedule-field"], [1, "at-schedule-source-name"], [1, "at-schedule-action-name"], ["for", "schedule-cron"], ["id", "schedule-cron", "type", "text", "placeholder", "0 2 * * *", 1, "mj-input", "at-schedule-cron-input", 3, "ngModelChange", "ngModel"], [1, "at-schedule-cron-preview"], [1, "at-schedule-field", "at-schedule-toggle-row"], ["for", "schedule-enabled"], ["id", "schedule-enabled", "type", "checkbox", 1, "mj-checkbox", 3, "ngModelChange", "ngModel"], [1, "at-schedule-dialog-footer"], [1, "fa-solid", "fa-triangle-exclamation"], [1, "at-confirm-message"], ["type", "text", "placeholder", "Tag A, Tag B, Tag C", 1, "mj-input", 2, "width", "100%", 3, "ngModelChange", "ngModel"], [1, "at-tax-create-context"], ["type", "text", "placeholder", "Tag name", 1, "mj-input", 2, "width", "100%", 3, "ngModelChange", "keydown.enter", "ngModel"], ["rows", "3", "placeholder", "Brief description of this tag", 1, "mj-textarea", 2, "width", "100%", 3, "ngModelChange", "ngModel"], [1, "mj-input", 2, "width", "100%", 3, "ngModelChange", "ngModel"], [3, "ngValue"], [2, "font-size", "0.85rem", "color", "var(--mj-text-secondary)", "margin", "0 0 12px"], ["TextField", "Label", "ValueField", "ID", "Placeholder", "Search and select a tag...", 3, "ValueChange", "Data", "Filterable", "ValuePrimitive"], [1, "at-schedule-dialog-overlay", 2, "z-index", "10001", 3, "click"], [1, "at-schedule-dialog", 2, "max-width", "440px", "z-index", "10002", 3, "click"], [1, "fa-solid", "fa-triangle-exclamation", 2, "color", "var(--mj-status-warning)", "margin-right", "8px"], ["aria-label", "Close warning", 1, "at-close-btn", 3, "click"], [1, "fa-solid", "fa-xmark"], [1, "at-schedule-dialog-body", 2, "text-align", "center", "padding", "24px"], [2, "color", "var(--mj-text-secondary)", "margin-bottom", "16px"], [2, "color", "var(--mj-text-muted)", "font-size", "13px"], [1, "at-action-btn", "at-primary-btn", 3, "mousedown"], [1, "fa-solid", "fa-arrow-right"]], template: function AutotaggingPipelineResourceComponent_Template(rf, ctx) { if (rf & 1) {
|
|
7886
7886
|
i0.ɵɵelementStart(0, "div", 0)(1, "div", 1)(2, "div", 2)(3, "h2");
|
|
7887
7887
|
i0.ɵɵelement(4, "i", 3);
|
|
7888
7888
|
i0.ɵɵtext(5, " Classify");
|
|
@@ -7955,7 +7955,7 @@ AutotaggingPipelineResourceComponent = AutotaggingPipelineResourceComponent_1 =
|
|
|
7955
7955
|
export { AutotaggingPipelineResourceComponent };
|
|
7956
7956
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(AutotaggingPipelineResourceComponent, [{
|
|
7957
7957
|
type: Component,
|
|
7958
|
-
args: [{ standalone: false, selector: 'app-autotagging-pipeline-resource', encapsulation: ViewEncapsulation.None, template: "<!-- Content Classification Dashboard -->\n<div class=\"at-dashboard\">\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 LEFT NAV \u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div class=\"at-left-nav\">\n <div class=\"at-left-nav-header\">\n <h2><i class=\"fa-solid fa-tags\"></i> Classify</h2>\n </div>\n <div class=\"at-left-nav-items\">\n @for (item of NavItems; track item.Tab) {\n <div class=\"at-nav-item\" [class.active]=\"ActiveTab === item.Tab\" (click)=\"SwitchTab(item.Tab)\">\n <i [class]=\"item.Icon\"></i> {{ item.Label }}\n @if (item.BadgeText) {\n <span class=\"at-nav-badge\" [class.at-nav-badge-live]=\"item.BadgeClass === 'nav-badge-live'\">{{ item.BadgeText }}</span>\n }\n </div>\n }\n <div class=\"at-nav-divider\"></div>\n <div class=\"at-nav-item\" [class.active]=\"ActiveTab === 'history'\" (click)=\"SwitchTab('history')\">\n <i class=\"fa-solid fa-clock-rotate-left\"></i> Run History\n </div>\n </div>\n <div class=\"at-left-nav-footer\">\n <button class=\"at-run-pipeline-btn\" (click)=\"RunPipeline()\" [disabled]=\"IsRunning\">\n @if (IsRunning) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Running...\n } @else {\n <i class=\"fa-solid fa-play\"></i> Run Pipeline\n }\n </button>\n </div>\n </div>\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 MAIN AREA \u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div class=\"at-main-area\">\n\n @if (IsLoading) {\n <div class=\"at-loading-overlay\">\n <mj-loading text=\"Loading autotagging data...\"></mj-loading>\n </div>\n }\n\n @if (!IsLoading) {\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <!-- TAB 1: PIPELINE MONITOR -->\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (ActiveTab === 'pipeline') {\n <div class=\"at-page-header\">\n <div>\n <div class=\"at-page-title\">Pipeline Monitor</div>\n <div class=\"at-page-subtitle\">Real-time processing status and recent activity</div>\n </div>\n <div class=\"at-page-actions\">\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"LoadPipelineData()\">\n <i class=\"fa-solid fa-arrows-rotate\"></i> Refresh\n </button>\n </div>\n </div>\n <div class=\"at-page-body\">\n <!-- KPIs -->\n <div class=\"at-kpi-strip\">\n @for (kpi of KPIMetrics; track kpi.Label) {\n <div class=\"at-kpi-card\">\n <div class=\"at-kpi-value\" [class.at-kpi-error-value]=\"kpi.Label === 'Errors' && kpi.Value > 0\">{{ kpi.Value | number }}</div>\n <div class=\"at-kpi-label\">{{ kpi.Label }}</div>\n @if (kpi.Trend) {\n <div class=\"at-kpi-trend\" [class.up]=\"kpi.TrendUp\">\n @if (kpi.TrendUp) { <i class=\"fa-solid fa-arrow-up\"></i> }\n {{ kpi.Trend }}\n </div>\n }\n </div>\n }\n </div>\n\n <div class=\"at-pipeline-layout\">\n <div class=\"at-pipeline-center\">\n <!-- Pipeline stages visualization (only during active run) -->\n @if (PipelineStages.length > 0 && (IsRunning || IsPaused)) {\n <div class=\"at-pipeline-stages\">\n @for (stage of PipelineStages; track stage.Name; let last = $last) {\n <div class=\"at-pipeline-stage\"\n [class.stage-idle]=\"stage.Status === 'idle'\"\n [class.stage-active]=\"stage.Status === 'active'\"\n [class.stage-complete]=\"stage.Status === 'complete'\">\n <div class=\"at-pipeline-stage-icon\">\n @if (stage.Status === 'active') {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n } @else if (stage.Status === 'complete') {\n <i class=\"fa-solid fa-check\"></i>\n } @else {\n <i [class]=\"stage.Icon\"></i>\n }\n </div>\n <div class=\"at-pipeline-stage-name\">{{ stage.Name }}</div>\n </div>\n @if (!last) {\n <div class=\"at-stage-connector\"\n [class.connector-complete]=\"stage.Status === 'complete'\">\n </div>\n }\n }\n </div>\n }\n\n <!-- Progress bar (visible during run or paused) -->\n @if (IsRunning || IsPaused) {\n <div class=\"at-progress-section\">\n <div class=\"at-progress-header\">\n <span class=\"at-progress-stage-label\">{{ RunStage }}</span>\n <span class=\"at-progress-pct\">{{ RunProgress | number:'1.0-0' }}%</span>\n </div>\n <div class=\"at-progress-bar\"><div class=\"at-progress-fill\" [style.width.%]=\"RunProgress\" [class.at-progress-fill-paused]=\"IsPaused\"></div></div>\n @if (RunCurrentItem) {\n <div class=\"at-progress-current\">{{ RunCurrentItem }}</div>\n }\n <div class=\"at-pipeline-controls\">\n @if (IsRunning && !IsPaused) {\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"PausePipeline()\" [disabled]=\"!CurrentProcessRunID\">\n <i class=\"fa-solid fa-pause\"></i> Pause\n </button>\n }\n @if (IsPaused) {\n <button class=\"at-action-btn at-primary-btn\" (click)=\"ResumePipeline()\">\n <i class=\"fa-solid fa-play\"></i> Resume\n </button>\n }\n <button class=\"at-action-btn at-danger-btn\" (click)=\"CancelPipeline()\">\n <i class=\"fa-solid fa-stop\"></i> Cancel\n </button>\n </div>\n </div>\n }\n\n <!-- D2: Live Per-Source Progress (visible during run) -->\n @if (IsRunning && LiveRunDetailRows.length > 0) {\n <div class=\"at-card\" style=\"margin-bottom: 12px;\">\n <div class=\"at-card-header\">\n <span class=\"at-card-title\"><i class=\"fa-solid fa-list-check\"></i> Per-Source Progress</span>\n <button class=\"at-action-btn at-secondary-btn\" style=\"font-size: 11px; padding: 3px 8px;\" (click)=\"LoadLiveRunDetails()\">\n <i class=\"fa-solid fa-arrows-rotate\"></i>\n </button>\n </div>\n <div class=\"at-card-body\" style=\"max-height: 200px; overflow-y: auto;\">\n <table class=\"at-run-table\">\n <thead>\n <tr>\n <th>Source</th>\n <th>Status</th>\n <th>Items</th>\n <th>Tagged</th>\n <th>Vectorized</th>\n <th>Errors</th>\n </tr>\n </thead>\n <tbody>\n @for (row of LiveRunDetailRows; track row.SourceName) {\n <tr>\n <td class=\"at-run-source-name\">{{ row.SourceName }}</td>\n <td>\n <span class=\"at-run-status-badge\" [class]=\"row.StatusClass\">\n @if (row.StatusClass === 'running') {\n <i class=\"fa-solid fa-spinner fa-spin\" style=\"font-size: 0.55rem\"></i>\n }\n {{ row.Status }}\n </span>\n </td>\n <td>{{ row.ItemsProcessed }}</td>\n <td>{{ row.ItemsTagged }}</td>\n <td>{{ row.ItemsVectorized }}</td>\n <td [class.run-error-text]=\"row.ErrorCount > 0\">{{ row.ErrorCount }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n }\n\n <!-- Recent Processing Feed -->\n <div class=\"at-card at-feed-card\">\n <div class=\"at-card-header\">\n <span class=\"at-card-title\"><i class=\"fa-solid fa-bolt\"></i> Recent Processing</span>\n <div class=\"at-feed-header-actions\">\n <button class=\"at-feed-sort-btn\" (click)=\"ToggleFeedSort()\"\n [title]=\"FeedSortOrder === 'newest' ? 'Showing newest first' : 'Showing oldest first'\">\n <i class=\"fa-solid\" [class.fa-arrow-down-short-wide]=\"FeedSortOrder === 'newest'\"\n [class.fa-arrow-up-short-wide]=\"FeedSortOrder === 'oldest'\"></i>\n {{ FeedSortOrder === 'newest' ? 'Newest' : 'Oldest' }}\n </button>\n <span class=\"at-feed-count\">{{ FilteredFeedItems.length }} items</span>\n </div>\n </div>\n <!-- Feed search bar -->\n <div class=\"at-feed-search-bar\">\n <i class=\"fa-solid fa-search at-feed-search-icon\"></i>\n <input type=\"text\"\n class=\"mj-input at-feed-search-input\"\n placeholder=\"Search items, sources, or tags...\"\n [(ngModel)]=\"FeedSearchQuery\"\n (input)=\"OnFeedSearchChange()\">\n @if (FeedSearchQuery) {\n <button class=\"at-feed-search-clear\" (click)=\"FeedSearchQuery = ''; OnFeedSearchChange()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n }\n </div>\n <div class=\"at-card-body at-feed-scroll-body\">\n @if (FilteredFeedItems.length === 0) {\n <div class=\"at-empty-state\">\n <i class=\"fa-solid fa-inbox\"></i>\n @if (FeedSearchQuery) {\n <p>No items match \"{{ FeedSearchQuery }}\"</p>\n } @else {\n <p>No processed items yet.</p>\n }\n </div>\n }\n @for (item of PaginatedFeedItems; track item.Name + item.SourceName + item.TimeAgo) {\n <div class=\"at-feed-item at-feed-item-clickable\" (click)=\"OpenFeedItemDetail(GetFeedItemOriginalIndex(item))\">\n <div class=\"at-feed-status-dot\" [class]=\"item.Status\"></div>\n <div class=\"at-feed-item-content\">\n <span class=\"at-feed-item-name\">{{ item.Name }}</span>\n <span class=\"at-feed-item-source-label\">{{ item.SourceName }}</span>\n </div>\n <div class=\"at-feed-item-tags\">\n @for (tag of item.Tags; track tag) {\n <span class=\"at-feed-tag\">{{ tag }}</span>\n }\n </div>\n <span class=\"at-feed-item-time\">{{ item.TimeAgo }}</span>\n </div>\n }\n </div>\n <!-- Feed pagination -->\n @if (FeedTotalPages > 1) {\n <div class=\"at-feed-pagination\">\n <button class=\"at-action-btn at-secondary-btn\" [disabled]=\"FeedPage === 0\" (click)=\"FeedPrevPage()\">\n <i class=\"fa-solid fa-chevron-left\"></i>\n </button>\n <span class=\"at-feed-pagination-label\">Page {{ FeedPage + 1 }} of {{ FeedTotalPages }}</span>\n <button class=\"at-action-btn at-secondary-btn\" [disabled]=\"FeedPage >= FeedTotalPages - 1\" (click)=\"FeedNextPage()\">\n <i class=\"fa-solid fa-chevron-right\"></i>\n </button>\n </div>\n }\n </div>\n </div>\n\n <!-- Right sidebar -->\n <div class=\"at-pipeline-right\">\n <!-- Sources Quick List -->\n <div class=\"at-card\">\n <div class=\"at-card-header\">\n <span class=\"at-card-title\"><i class=\"fa-solid fa-database\"></i> Sources</span>\n </div>\n <div class=\"at-card-body\">\n @for (source of SourceMinis; track source.ID) {\n <div class=\"at-source-mini\">\n <div class=\"at-source-mini-icon\"><i [class]=\"source.Icon\"></i></div>\n <div class=\"at-source-mini-info\">\n <div class=\"at-source-mini-name\">{{ source.Name }}</div>\n <div class=\"at-source-mini-meta\">{{ source.Meta }}</div>\n </div>\n <div class=\"at-source-mini-status\" [class]=\"source.StatusClass\"></div>\n </div>\n }\n </div>\n </div>\n <!-- Trending Tags -->\n <div class=\"at-card at-tag-cloud-card\">\n <div class=\"at-card-title\" style=\"margin-bottom: 10px;\"><i class=\"fa-solid fa-chart-bar\"></i> Trending Tags</div>\n <div class=\"at-tag-cloud\">\n @for (tag of TrendingTags; track tag.Tag) {\n <span class=\"at-tag-pill\" [class]=\"tag.SizeClass\"\n [style.opacity]=\"0.4 + tag.AvgWeight * 0.6\"\n [title]=\"'Weight: ' + tag.AvgWeight.toFixed(2)\">{{ tag.Tag }}</span>\n }\n </div>\n </div>\n\n <!-- Pipeline Settings Widget -->\n <div class=\"at-card at-config-card\">\n <div class=\"at-card-header\" (click)=\"TogglePipelineConfig()\" style=\"cursor:pointer\">\n <span class=\"at-card-title\"><i class=\"fa-solid fa-sliders\"></i> Pipeline Settings</span>\n <i class=\"fa-solid\" [class.fa-chevron-down]=\"!ShowPipelineConfig\" [class.fa-chevron-up]=\"ShowPipelineConfig\" style=\"font-size:0.7rem; color:var(--mj-text-muted)\"></i>\n </div>\n @if (ShowPipelineConfig) {\n <div class=\"at-card-body at-config-body\">\n <div class=\"at-config-row\">\n <label class=\"at-config-label\">Batch Size</label>\n <div class=\"at-config-control\">\n <input type=\"number\" class=\"at-config-input\" min=\"10\" max=\"1000\" step=\"10\"\n [(ngModel)]=\"PipelineConfig.Pipeline!.BatchSize\">\n </div>\n </div>\n <div class=\"at-config-row\">\n <label class=\"at-config-label\">Throttle (ms)</label>\n <div class=\"at-config-control\">\n <input type=\"range\" class=\"at-config-slider\" min=\"0\" max=\"5000\" step=\"100\"\n [(ngModel)]=\"PipelineConfig.Pipeline!.DelayBetweenBatchesMs\">\n <span class=\"at-config-value\">{{ PipelineConfig.Pipeline!.DelayBetweenBatchesMs }}ms</span>\n </div>\n </div>\n <div class=\"at-config-row\">\n <label class=\"at-config-label\">Error Tolerance</label>\n <div class=\"at-config-control\">\n <input type=\"range\" class=\"at-config-slider\" min=\"5\" max=\"50\" step=\"5\"\n [(ngModel)]=\"PipelineConfig.Pipeline!.ErrorThresholdPercent\">\n <span class=\"at-config-value\">{{ PipelineConfig.Pipeline!.ErrorThresholdPercent }}%</span>\n </div>\n </div>\n <div class=\"at-config-row\">\n <label class=\"at-config-label\">Auto-resume</label>\n <div class=\"at-config-control\">\n <label class=\"at-config-toggle\">\n <input type=\"checkbox\" [(ngModel)]=\"PipelineConfig.Pipeline!.ResumeFromLastBatch\">\n <span class=\"at-toggle-track\"><span class=\"at-toggle-thumb\"></span></span>\n </label>\n </div>\n </div>\n <div class=\"at-config-divider\"></div>\n <div class=\"at-config-section-label\">LLM Rate Limits</div>\n <div class=\"at-config-row\">\n <label class=\"at-config-label\">Requests/min</label>\n <div class=\"at-config-control\">\n <input type=\"number\" class=\"at-config-input\" min=\"1\" max=\"500\"\n [(ngModel)]=\"PipelineConfig.RateLimits!.LLM!.RequestsPerMinute\">\n </div>\n </div>\n <div class=\"at-config-row\">\n <label class=\"at-config-label\">Tokens/min</label>\n <div class=\"at-config-control\">\n <input type=\"number\" class=\"at-config-input\" min=\"1000\" max=\"5000000\" step=\"10000\"\n [(ngModel)]=\"PipelineConfig.RateLimits!.LLM!.TokensPerMinute\">\n <span class=\"at-config-value\">{{ FormatTokenCount(PipelineConfig.RateLimits!.LLM!.TokensPerMinute ?? 0) }}</span>\n </div>\n </div>\n <div class=\"at-config-section-label\">Embedding Rate Limits</div>\n <div class=\"at-config-row\">\n <label class=\"at-config-label\">Requests/min</label>\n <div class=\"at-config-control\">\n <input type=\"number\" class=\"at-config-input\" min=\"1\" max=\"1000\"\n [(ngModel)]=\"PipelineConfig.RateLimits!.Embedding!.RequestsPerMinute\">\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n }\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <!-- TAB 2: SOURCES -->\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (ActiveTab === 'sources') {\n <div class=\"at-page-header\">\n <div>\n <div class=\"at-page-title\">Content Sources</div>\n <div class=\"at-page-subtitle\">Configure where content is ingested from</div>\n </div>\n <div class=\"at-page-actions\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"OpenAddSourceForm()\">\n <i class=\"fa-solid fa-plus\"></i> Add Source\n </button>\n </div>\n </div>\n <div class=\"at-page-body\">\n <div class=\"at-sources-grid\">\n @for (card of SourceCards; track card.ID) {\n <div class=\"at-source-card-full at-source-card-clickable\" (click)=\"OpenSourceDetail(card)\">\n <div class=\"at-source-card-header\">\n <div class=\"at-source-card-icon\"><i [class]=\"card.Icon\"></i></div>\n <div>\n <div class=\"at-source-card-title\">{{ card.Name }}</div>\n <div class=\"at-source-card-type\">{{ card.SourceTypeName }}@if (card.RequiresFileType) { \u00B7 {{ card.FileTypeName }}}</div>\n </div>\n <div class=\"at-source-card-status\" [class]=\"card.StatusClass\">\n <div class=\"at-source-mini-status\" [class]=\"card.StatusClass\"></div>\n {{ card.StatusLabel }}\n </div>\n </div>\n @if (card.URL) {\n <div class=\"at-source-card-url\">{{ card.URL }}</div>\n }\n <div class=\"at-source-card-stats\">\n <div class=\"at-source-stat\">\n <div class=\"at-source-stat-value\">{{ formatNumber(card.ItemCount) }}</div>\n <div class=\"at-source-stat-label\">Items</div>\n </div>\n <div class=\"at-source-stat\">\n <div class=\"at-source-stat-value\">{{ formatNumber(card.TagCount) }}</div>\n <div class=\"at-source-stat-label\">Tags</div>\n </div>\n <div class=\"at-source-stat\">\n <div class=\"at-source-stat-value\">{{ card.AvgTags }}</div>\n <div class=\"at-source-stat-label\">Avg Tags</div>\n </div>\n <div class=\"at-source-stat\">\n <div class=\"at-source-stat-value\">{{ card.LastRunAgo }}</div>\n <div class=\"at-source-stat-label\">Last Run</div>\n </div>\n </div>\n <!-- Schedule indicator -->\n @if (card.ScheduledActionID) {\n <div class=\"at-schedule-indicator\" (click)=\"RemoveSchedule(card); $event.stopPropagation()\" title=\"Click to remove schedule\">\n <i class=\"fa-regular fa-clock\"></i>\n <span>{{ GetScheduleLabel(card) }}</span>\n </div>\n }\n <div class=\"at-source-card-actions\">\n <button class=\"at-source-action-btn\" (click)=\"OpenEditSourceForm(card); $event.stopPropagation()\"><i class=\"fa-solid fa-pen\"></i> Edit</button>\n <button class=\"at-source-action-btn\" (click)=\"RunPipelineForSource(card.ID); $event.stopPropagation()\"><i class=\"fa-solid fa-play\"></i> Run</button>\n @if (!card.ScheduledActionID) {\n <button class=\"at-source-action-btn at-source-schedule-btn\" (click)=\"OpenScheduleDialog(card); $event.stopPropagation()\"><i class=\"fa-regular fa-clock\"></i> Schedule</button>\n }\n <button class=\"at-source-action-btn at-source-delete-btn\" (click)=\"DeleteSource(card); $event.stopPropagation()\"><i class=\"fa-solid fa-trash\"></i> Delete</button>\n </div>\n </div>\n }\n\n <!-- Add Source Card -->\n <div class=\"at-add-source-card\" (click)=\"OpenAddSourceForm()\">\n <i class=\"fa-solid fa-plus-circle\"></i>\n <span style=\"font-size: 0.85rem; font-weight: 600;\">Add Content Source</span>\n <span style=\"font-size: 0.72rem;\">Web, RSS, Email, Files, API</span>\n </div>\n </div>\n\n <!-- Content Item Duplicates Section -->\n <div class=\"at-dedup-section\">\n <div class=\"at-dedup-header\">\n <div>\n <h3 class=\"at-dedup-title\">\n <i class=\"fa-solid fa-clone\"></i>\n Content Duplicates\n </h3>\n <span class=\"at-dedup-subtitle\">Review detected duplicate content items across sources</span>\n </div>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"LoadContentDuplicates()\" [disabled]=\"IsLoadingDuplicates\">\n @if (IsLoadingDuplicates) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Loading...\n } @else {\n <i class=\"fa-solid fa-arrows-rotate\"></i> Load Duplicates\n }\n </button>\n </div>\n\n @if (ContentDuplicates.length > 0) {\n <div class=\"at-dedup-list\">\n @for (dup of ContentDuplicates; track dup.ID) {\n <div class=\"at-dedup-card\">\n <div class=\"at-dedup-pair\">\n <div class=\"at-dedup-item\">\n <span class=\"at-dedup-item-name\">{{ dup.ItemAName }}</span>\n <span class=\"at-dedup-item-source\">{{ dup.ItemASource }}</span>\n </div>\n <div class=\"at-dedup-vs\">\n <i class=\"fa-solid fa-arrows-left-right\"></i>\n </div>\n <div class=\"at-dedup-item\">\n <span class=\"at-dedup-item-name\">{{ dup.ItemBName }}</span>\n <span class=\"at-dedup-item-source\">{{ dup.ItemBSource }}</span>\n </div>\n </div>\n <div class=\"at-dedup-meta\">\n <span class=\"at-dedup-score\">{{ (dup.SimilarityScore * 100).toFixed(0) }}% match</span>\n <span class=\"at-dedup-method\">{{ dup.DetectionMethod }}</span>\n </div>\n <div class=\"at-dedup-actions\">\n <button class=\"at-action-btn at-primary-btn at-dedup-confirm\" (click)=\"ConfirmContentDuplicate(dup)\">\n <i class=\"fa-solid fa-check\"></i> Confirm\n </button>\n <button class=\"at-action-btn at-secondary-btn at-dedup-dismiss\" (click)=\"DismissContentDuplicate(dup)\">\n <i class=\"fa-solid fa-times\"></i> Dismiss\n </button>\n </div>\n </div>\n }\n </div>\n } @else if (!IsLoadingDuplicates) {\n <div class=\"at-dedup-empty\">\n <i class=\"fa-solid fa-check-circle\"></i>\n <p>No pending duplicates. Click \"Load Duplicates\" to check.</p>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <!-- TAB 3: CONTENT TYPES -->\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (ActiveTab === 'types') {\n <div class=\"at-page-header\">\n <div>\n <div class=\"at-page-title\">Content Types</div>\n <div class=\"at-page-subtitle\">Configure AI tagging rules per content category</div>\n </div>\n <div class=\"at-page-actions\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"OpenAddTypeForm()\">\n <i class=\"fa-solid fa-plus\"></i> Add Type\n </button>\n </div>\n </div>\n <div class=\"at-page-body\">\n <div class=\"at-ct-grid\">\n @for (card of ContentTypeCards; track card.ID) {\n <div class=\"at-ct-card\">\n <div class=\"at-ct-card-header\">\n <span class=\"at-ct-card-name\">{{ card.Name }}</span>\n <span class=\"at-ct-card-model\">{{ card.AIModelName }}</span>\n </div>\n <div class=\"at-ct-field\">\n <span class=\"at-ct-field-label\">Description</span>\n <span class=\"at-ct-field-value\">{{ card.Description || '\\u2014' }}</span>\n </div>\n <div class=\"at-ct-field\">\n <span class=\"at-ct-field-label\">Sources Using</span>\n <span class=\"at-ct-field-value\">{{ card.SourcesUsing }}</span>\n </div>\n <div class=\"at-ct-field\">\n <span class=\"at-ct-field-label\">Items Tagged</span>\n <span class=\"at-ct-field-value\">{{ formatNumber(card.ItemsTagged) }}</span>\n </div>\n <div class=\"at-ct-tag-range\">\n <i class=\"fa-solid fa-tags\"></i>\n <span>{{ card.MinTags }}</span>\n <div class=\"at-ct-tag-range-bar\">\n <div class=\"at-ct-tag-range-fill\" [style.left.%]=\"card.RangeLeftPct\" [style.right.%]=\"card.RangeRightPct\"></div>\n </div>\n <span>{{ card.MaxTags }}</span>\n <span class=\"at-ct-range-suffix\">tags/item</span>\n </div>\n <div class=\"at-source-card-actions\" style=\"margin-top: 10px;\">\n <button class=\"at-source-action-btn\" (click)=\"OpenEditTypeForm(card)\"><i class=\"fa-solid fa-pen\"></i> Edit</button>\n </div>\n </div>\n }\n <!-- Add Content Type Card -->\n <div class=\"at-add-type-card\" (click)=\"OpenAddTypeForm()\">\n <i class=\"fa-solid fa-plus-circle\"></i>\n <span style=\"font-size: 0.85rem; font-weight: 600;\">Add Content Type</span>\n <span style=\"font-size: 0.72rem;\">Configure tagging rules</span>\n </div>\n </div>\n </div>\n }\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <!-- TAB 4: TAG LIBRARY -->\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (ActiveTab === 'tags') {\n <div class=\"at-page-header\">\n <div>\n <div class=\"at-page-title\">Tag Library</div>\n <div class=\"at-page-subtitle\">{{ TagRows.length }} unique tags across all content sources</div>\n </div>\n <div class=\"at-page-actions\">\n <input type=\"text\" class=\"at-search-input\" placeholder=\"Search tags...\"\n [(ngModel)]=\"TagSearchQuery\" (input)=\"FilterTags()\">\n </div>\n </div>\n <div class=\"at-page-body\">\n <div class=\"at-tag-lib-layout\">\n <div class=\"at-tag-lib-main\">\n <div class=\"at-card\">\n <div class=\"at-card-body\" style=\"max-height: 500px; overflow-y: auto;\">\n <table class=\"at-tag-table\">\n <thead>\n <tr>\n <th>Tag</th>\n <th>Count</th>\n <th>Avg Weight</th>\n <th>Distribution</th>\n <th>Top Source</th>\n <th>First Seen</th>\n </tr>\n </thead>\n <tbody>\n @for (row of FilteredTagRows; track row.Tag) {\n <tr class=\"at-tag-row-clickable\" (click)=\"DrillDownTag(row.Tag)\"\n [class.at-tag-row-selected]=\"SelectedDrillDownTag === row.Tag\">\n <td class=\"at-tag-name-cell\">{{ row.Tag }}</td>\n <td>{{ row.UsageCount }}</td>\n <td>\n <div class=\"at-weight-indicator\">\n <div class=\"at-weight-bar\">\n <div class=\"at-weight-bar-fill\" [style.width.%]=\"row.AvgWeight * 100\"\n [class.at-weight-high]=\"row.AvgWeight >= 0.7\"\n [class.at-weight-medium]=\"row.AvgWeight >= 0.4 && row.AvgWeight < 0.7\"\n [class.at-weight-low]=\"row.AvgWeight < 0.4\"></div>\n </div>\n <span class=\"at-weight-value\">{{ row.AvgWeight.toFixed(2) }}</span>\n </div>\n </td>\n <td>\n <div class=\"at-tag-bar\">\n <div class=\"at-tag-bar-fill\" [style.width.%]=\"row.BarWidthPct\"></div>\n </div>\n </td>\n <td>{{ row.TopSource }}</td>\n <td>{{ row.FirstSeen }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n\n <!-- Tag drill-down: content items matching selected tag -->\n @if (SelectedDrillDownTag) {\n <div class=\"at-card\" style=\"margin-top: 12px;\">\n <div class=\"at-card-header\">\n <span class=\"at-card-title\">\n <i class=\"fa-solid fa-tag\"></i>\n Content items tagged \"{{ SelectedDrillDownTag }}\"\n ({{ TagDrillDownItems.length }})\n </span>\n <button class=\"at-slide-close\" (click)=\"CloseDrillDownTag()\" style=\"background:none;border:none;cursor:pointer;color:var(--mj-text-muted)\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div class=\"at-card-body\" style=\"max-height: 300px; overflow-y: auto;\">\n @if (TagDrillDownItems.length === 0) {\n <div class=\"at-empty-state\"><p>No content items found for this tag.</p></div>\n } @else {\n <table class=\"at-tag-table\">\n <thead>\n <tr>\n <th>Name</th>\n <th>Source</th>\n <th>Weight</th>\n <th>Updated</th>\n </tr>\n </thead>\n <tbody>\n @for (di of TagDrillDownItems; track di.ID) {\n <tr class=\"at-tag-row-clickable\" (click)=\"OpenItemDetailByID(di.ID)\">\n <td>{{ di.Name }}</td>\n <td>{{ di.SourceName }}</td>\n <td>{{ FormatWeight(di.Weight) }}</td>\n <td>{{ di.UpdatedAt }}</td>\n </tr>\n }\n </tbody>\n </table>\n }\n </div>\n </div>\n }\n </div>\n <div class=\"at-tag-lib-sidebar\">\n <div class=\"at-card at-tag-cloud-card\">\n <div class=\"at-card-title\" style=\"margin-bottom: 12px;\"><i class=\"fa-solid fa-cloud\"></i> Tag Cloud</div>\n @if (TagCloudWordItems.length > 0) {\n <mj-word-cloud\n [Items]=\"TagCloudWordItems\"\n [MaxFontSize]=\"40\"\n [MinFontSize]=\"12\"\n [MaxItems]=\"20\"\n Layout=\"spiral\"\n ColorMode=\"weight-gradient\"\n [Interactive]=\"true\"\n [Animate]=\"true\">\n </mj-word-cloud>\n } @else {\n <div class=\"at-tag-cloud-empty\">No tags yet</div>\n }\n </div>\n <div class=\"at-card\" style=\"padding: 16px; margin-top: 12px;\">\n <div class=\"at-card-title\" style=\"margin-bottom: 10px;\"><i class=\"fa-solid fa-chart-pie\"></i> By Source</div>\n <div class=\"at-tags-by-source\">\n @for (s of TagsBySource; track s.SourceName) {\n <div class=\"at-tag-source-row\">\n <span>{{ s.SourceName }}</span>\n <strong>{{ s.Count }}</strong>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <!-- TAB 6: TAXONOMY GOVERNANCE -->\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (ActiveTab === 'taxonomy') {\n <div class=\"at-page-header\">\n <div>\n <div class=\"at-page-title\">Taxonomy Governance</div>\n <div class=\"at-page-subtitle\">Manage tag hierarchy, resolve duplicates, and monitor taxonomy health — <strong>{{ TaxHealth.Total }} total tags</strong></div>\n </div>\n <div class=\"at-page-actions\">\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"RefreshTaxonomyData()\">\n <i class=\"fa-solid fa-arrows-rotate\"></i> Refresh\n </button>\n </div>\n </div>\n\n <!-- Sub-tab strip -->\n <div class=\"at-tax-tab-strip\">\n <div class=\"at-tax-tab\" [class.active]=\"TaxSubTab === 'tree'\" (click)=\"SwitchTaxSubTab('tree')\">\n <i class=\"fa-solid fa-sitemap\"></i> Tree View\n </div>\n <div class=\"at-tax-tab\" [class.active]=\"TaxSubTab === 'duplicates'\" (click)=\"SwitchTaxSubTab('duplicates')\">\n <i class=\"fa-solid fa-link\"></i> Duplicates\n @if (TaxDuplicates.length > 0) {\n <span class=\"at-tax-tab-badge at-tax-badge-warning\">{{ TaxDuplicates.length }}</span>\n }\n </div>\n <div class=\"at-tax-tab\" [class.active]=\"TaxSubTab === 'orphans'\" (click)=\"SwitchTaxSubTab('orphans')\">\n <i class=\"fa-solid fa-ban\"></i> Orphans\n @if (TaxOrphans.length > 0) {\n <span class=\"at-tax-tab-badge at-tax-badge-error\">{{ TaxOrphans.length }}</span>\n }\n </div>\n <div class=\"at-tax-tab\" [class.active]=\"TaxSubTab === 'treemap'\" (click)=\"SwitchTaxSubTab('treemap')\">\n <i class=\"fa-solid fa-chart-tree-map\"></i> Treemap\n </div>\n <div class=\"at-tax-tab\" [class.active]=\"TaxSubTab === 'audit'\" (click)=\"SwitchTaxSubTab('audit')\">\n <i class=\"fa-solid fa-scroll\"></i> Audit Log\n </div>\n </div>\n\n <div class=\"at-page-body\">\n\n <!-- \u2550\u2550 SUB-TAB 1: TREE VIEW \u2550\u2550 -->\n @if (TaxSubTab === 'tree') {\n <div class=\"at-tax-split-view\">\n <!-- Tree panel -->\n <div class=\"at-tax-tree-panel\" style=\"position: relative;\">\n <div class=\"at-tax-tree-toolbar\">\n <input type=\"text\" class=\"at-search-input\" style=\"flex: 1;\" placeholder=\"Search tags...\"\n [(ngModel)]=\"TaxTreeSearch\" (input)=\"FilterTaxTree()\">\n <button class=\"at-tax-toolbar-btn\" title=\"Add root tag\" (click)=\"OpenCreateRootTag()\">\n <i class=\"fa-solid fa-plus\"></i>\n </button>\n <button class=\"at-tax-toolbar-btn\" [class.active]=\"TaxMultiSelectMode\"\n title=\"Toggle multi-select for drag reparenting\" (click)=\"ToggleMultiSelectMode()\">\n <i class=\"fa-solid fa-check-double\"></i>\n </button>\n </div>\n <div class=\"at-tax-tree-body\"\n (dragover)=\"$event.preventDefault()\"\n (drop)=\"OnDropToRoot($event)\">\n @if (TaxFilteredNodes.length === 0) {\n <div class=\"at-empty-state\">\n <i class=\"fa-solid fa-sitemap\"></i>\n <p>No tags found</p>\n </div>\n }\n @for (node of TaxFilteredNodes; track node.ID) {\n <div class=\"at-tax-tree-node\"\n [class.at-tax-node-selected]=\"node.IsSelected\"\n [class.at-tax-node-drag-over]=\"TaxDragOverNodeID === node.ID\"\n [class.at-tax-node-multi-selected]=\"IsNodeMultiSelected(node.ID)\"\n [style.padding-left.px]=\"16 + node.Depth * 20\"\n [attr.draggable]=\"true\"\n (dragstart)=\"OnTreeNodeDragStart($event, node)\"\n (dragover)=\"OnTreeNodeDragOver($event, node)\"\n (dragleave)=\"OnTreeNodeDragLeave()\"\n (drop)=\"OnTreeNodeDrop($event, node); $event.stopPropagation()\"\n (click)=\"SelectTaxNode(node)\">\n @if (TaxMultiSelectMode) {\n <input type=\"checkbox\" class=\"at-tax-tree-checkbox\"\n [checked]=\"IsNodeMultiSelected(node.ID)\"\n (click)=\"ToggleNodeSelection(node, $event)\">\n }\n <span class=\"at-tax-tree-arrow\"\n [class.at-tax-arrow-expanded]=\"node.IsExpanded && node.Children.length > 0\"\n [class.at-tax-arrow-collapsed]=\"!node.IsExpanded && node.Children.length > 0\"\n [class.at-tax-arrow-leaf]=\"node.Children.length === 0\"\n (click)=\"ToggleTaxNode(node); $event.stopPropagation()\"></span>\n <span class=\"at-tax-health-dot\" [class]=\"node.HealthColor\"></span>\n <span class=\"at-tax-tree-label\" [class.at-tax-tree-label-selected]=\"node.IsSelected\">{{ node.Name }}</span>\n <span class=\"at-tax-tree-count\">({{ node.ItemCount }})</span>\n <span class=\"at-tax-tree-add-child\" title=\"Add child tag\"\n (click)=\"OpenCreateChildTagFor(node); $event.stopPropagation()\">\n <i class=\"fa-solid fa-plus\"></i>\n </span>\n </div>\n }\n </div>\n @if (TaxTreeSaving) {\n <div class=\"at-tax-tree-saving-overlay\">\n <mj-loading text=\"Moving tags...\" size=\"small\"></mj-loading>\n </div>\n }\n </div>\n\n <!-- Details panel -->\n <div class=\"at-tax-details-panel\">\n @if (TaxSelectedNode) {\n <div class=\"at-tax-details-header\">\n <!-- Breadcrumb -->\n <div class=\"at-tax-breadcrumb\">\n @for (bc of GetTaxBreadcrumb(TaxSelectedNode); track bc.ID) {\n <span class=\"at-tax-bc-link\" (click)=\"NavigateToBreadcrumb(bc.ID)\">{{ bc.Name }}</span>\n <span class=\"at-tax-bc-sep\">›</span>\n }\n <span class=\"at-tax-bc-current\">{{ TaxSelectedNode.Name }}</span>\n </div>\n\n @if (!TaxIsEditing) {\n <div class=\"at-tax-details-title\">\n {{ TaxSelectedNode.DisplayName }}\n <span class=\"at-tax-edit-icon\" (click)=\"StartEditTag()\" title=\"Edit name\">\n <i class=\"fa-solid fa-pen\"></i>\n </span>\n </div>\n @if (TaxSelectedNode.Description) {\n <div class=\"at-tax-details-desc\">{{ TaxSelectedNode.Description }}</div>\n }\n } @else {\n <div class=\"at-tax-edit-form\">\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">Name</label>\n <input type=\"text\" class=\"at-form-input\" [(ngModel)]=\"TaxEditName\">\n </div>\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">Description</label>\n <textarea class=\"at-form-textarea\" rows=\"3\" [(ngModel)]=\"TaxEditDescription\"></textarea>\n </div>\n <div class=\"at-form-actions\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"SaveEditTag()\">Save</button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"CancelEditTag()\">Cancel</button>\n </div>\n </div>\n }\n </div>\n\n <!-- Stats row -->\n <div class=\"at-tax-stats-row\">\n <div class=\"at-tax-stat-item\">\n <div class=\"at-tax-stat-value\">{{ TaxSelectedNode.ItemCount }}</div>\n <div class=\"at-tax-stat-label\">Items Tagged</div>\n </div>\n <div class=\"at-tax-stat-item\">\n <div class=\"at-tax-stat-value\">{{ TaxSelectedNode.AvgWeight.toFixed(2) }}</div>\n <div class=\"at-tax-stat-label\">Avg Weight</div>\n </div>\n <div class=\"at-tax-stat-item\">\n <div class=\"at-tax-stat-value\">{{ TaxSelectedNode.Children.length }}</div>\n <div class=\"at-tax-stat-label\">Children</div>\n </div>\n <div class=\"at-tax-stat-item\">\n <div class=\"at-tax-stat-value\">{{ TaxSelectedNode.Depth }}</div>\n <div class=\"at-tax-stat-label\">Depth</div>\n </div>\n <div class=\"at-tax-stat-item\">\n <div class=\"at-tax-stat-value at-tax-stat-date\">{{ TaxSelectedNode.FirstSeen }}</div>\n <div class=\"at-tax-stat-label\">First Seen</div>\n </div>\n </div>\n\n <!-- Action toolbar -->\n @if (!TaxIsEditing) {\n <div class=\"at-tax-action-toolbar\">\n <button class=\"at-tax-action-btn\" (click)=\"OpenCreateChildTag()\"><i class=\"fa-solid fa-plus\"></i> Add Child</button>\n <button class=\"at-tax-action-btn\" (click)=\"StartEditTag()\"><i class=\"fa-solid fa-pen\"></i> Rename</button>\n <button class=\"at-tax-action-btn\" (click)=\"OpenMoveDialog(TaxSelectedNode)\"><i class=\"fa-solid fa-arrows-up-down\"></i> Move</button>\n <button class=\"at-tax-action-btn\" (click)=\"OpenMergeIntoDialog(TaxSelectedNode)\"><i class=\"fa-solid fa-compress\"></i> Merge Into...</button>\n <button class=\"at-tax-action-btn\" (click)=\"OpenSplitDialog(TaxSelectedNode)\"><i class=\"fa-solid fa-code-branch\"></i> Split</button>\n <button class=\"at-tax-action-btn at-tax-action-danger\" (click)=\"DeleteTag(TaxSelectedNode)\"><i class=\"fa-solid fa-trash\"></i> Delete</button>\n </div>\n }\n\n <!-- Child tags -->\n @if (TaxSelectedNode.Children.length > 0) {\n <div class=\"at-tax-detail-section\">\n <div class=\"at-tax-section-title\">Child Tags</div>\n <div class=\"at-tax-child-chips\">\n @for (child of TaxSelectedNode.Children; track child.ID) {\n <span class=\"at-tax-child-chip\" (click)=\"SelectTaxNode(child)\">\n {{ child.Name }}\n <span class=\"at-tax-chip-count\">{{ child.ItemCount }}</span>\n </span>\n }\n </div>\n </div>\n }\n\n <!-- Recently tagged items -->\n @if (TaxRecentItems.length > 0) {\n <div class=\"at-tax-detail-section\">\n <div class=\"at-tax-section-title\">Recently Tagged Items</div>\n <div class=\"at-tax-recent-list\">\n @for (item of TaxRecentItems; track $index) {\n <div class=\"at-tax-recent-item\">\n <div class=\"at-tax-recent-icon\"><i [class]=\"item.Icon\"></i></div>\n <div class=\"at-tax-recent-name\">{{ item.Name }}</div>\n <div class=\"at-tax-recent-weight\">{{ item.Weight.toFixed(2) }}</div>\n <div class=\"at-tax-recent-date\">{{ item.Date }}</div>\n </div>\n }\n </div>\n </div>\n }\n } @else {\n <div class=\"at-empty-state\" style=\"height: 100%;\">\n <i class=\"fa-solid fa-hand-pointer\"></i>\n <p>Select a tag from the tree to view details</p>\n </div>\n }\n </div>\n </div>\n\n <!-- Health bar -->\n <div class=\"at-tax-health-bar\">\n <span class=\"at-tax-health-label\">Taxonomy Health</span>\n <div class=\"at-tax-health-stat\">\n <span class=\"at-tax-dot at-tax-dot-total\"></span>\n <span class=\"at-tax-health-value\">{{ TaxHealth.Total }}</span>\n <span class=\"at-tax-health-text\">Total</span>\n </div>\n <div class=\"at-tax-health-stat\">\n <span class=\"at-tax-dot at-tax-dot-healthy\"></span>\n <span class=\"at-tax-health-value at-tax-val-success\">{{ TaxHealth.Healthy }}</span>\n <span class=\"at-tax-health-text\">Healthy</span>\n </div>\n <div class=\"at-tax-health-stat\">\n <span class=\"at-tax-dot at-tax-dot-attention\"></span>\n <span class=\"at-tax-health-value at-tax-val-warning\">{{ TaxHealth.NeedAttention }}</span>\n <span class=\"at-tax-health-text\">Need Attention</span>\n </div>\n <div class=\"at-tax-health-stat\">\n <span class=\"at-tax-dot at-tax-dot-orphaned\"></span>\n <span class=\"at-tax-health-value at-tax-val-error\">{{ TaxHealth.Orphaned }}</span>\n <span class=\"at-tax-health-text\">Orphaned</span>\n </div>\n <div class=\"at-tax-health-stat\">\n <span class=\"at-tax-dot at-tax-dot-duplicates\"></span>\n <span class=\"at-tax-health-value at-tax-val-info\">{{ TaxHealth.Duplicates }}</span>\n <span class=\"at-tax-health-text\">Duplicate Candidates</span>\n </div>\n </div>\n }\n\n <!-- \u2550\u2550 SUB-TAB 2: DUPLICATES \u2550\u2550 -->\n @if (TaxSubTab === 'duplicates') {\n <div class=\"at-tax-dup-stats-bar\">\n <div class=\"at-tax-dup-stat\"><strong>{{ TaxDuplicates.length }}</strong> candidates found</div>\n <div class=\"at-tax-dup-stat at-tax-dup-high\"><strong>{{ TaxHighConfidenceDupeCount }}</strong> high confidence (>85%)</div>\n <div class=\"at-tax-dup-stat at-tax-dup-moderate\"><strong>{{ TaxModerateDupeCount }}</strong> moderate (70-85%)</div>\n </div>\n\n @if (TaxDuplicates.length === 0) {\n <div class=\"at-empty-state\">\n <i class=\"fa-solid fa-check-circle\"></i>\n <p>No duplicate tags detected</p>\n </div>\n }\n\n <div class=\"at-tax-dup-list\">\n @for (pair of TaxDuplicates; track $index) {\n <div class=\"at-tax-dup-card\" [class.at-tax-dup-high]=\"pair.SeverityClass === 'high'\" [class.at-tax-dup-moderate]=\"pair.SeverityClass === 'moderate'\">\n @if (pair.IsExactDuplicate) {\n <!-- Exact-name duplicates: show single tag name with count -->\n <div class=\"at-tax-dup-tag\">{{ pair.TagA }}</div>\n <div class=\"at-tax-dup-similarity\">\n <span class=\"at-tax-sim-percent high\">\n <i class=\"fa-solid fa-clone\"></i> {{ pair.ExactDuplicateCount }} identical records\n </span>\n </div>\n <div class=\"at-tax-dup-actions\">\n <button class=\"at-tax-dup-btn at-tax-dup-btn-primary\" (click)=\"MergeTags(pair.TagAID, pair.TagBID, pair.TagA, pair.TagB)\" [disabled]=\"IsMerging\">@if (IsMerging) { <i class=\"fa-solid fa-spinner fa-spin\"></i> Merging... } @else { Merge }</button>\n <button class=\"at-tax-dup-btn\" (click)=\"DismissDuplicate(pair)\">Dismiss</button>\n </div>\n } @else {\n <!-- Similar (non-exact) pairs: show both tags with similarity -->\n <div class=\"at-tax-dup-tag\">{{ pair.TagA }}</div>\n <div class=\"at-tax-dup-arrow\"><i class=\"fa-solid fa-arrows-left-right\"></i></div>\n <div class=\"at-tax-dup-similarity\">\n <div class=\"at-tax-sim-bar-bg\">\n <div class=\"at-tax-sim-bar-fill\" [class]=\"pair.SeverityClass\" [style.width.%]=\"pair.Similarity\"></div>\n </div>\n <span class=\"at-tax-sim-percent\" [class]=\"pair.SeverityClass\">{{ pair.Similarity }}%</span>\n </div>\n <div class=\"at-tax-dup-arrow\"><i class=\"fa-solid fa-arrows-left-right\"></i></div>\n <div class=\"at-tax-dup-tag\">{{ pair.TagB }}</div>\n <div class=\"at-tax-dup-actions\">\n <button class=\"at-tax-dup-btn at-tax-dup-btn-primary\" (click)=\"MergeTags(pair.TagAID, pair.TagBID, pair.TagA, pair.TagB)\" [disabled]=\"IsMerging\">@if (IsMerging) { <i class=\"fa-solid fa-spinner fa-spin\"></i> Merging... } @else { Merge }</button>\n <button class=\"at-tax-dup-btn\" (click)=\"MakeChildTag(pair.TagAID, pair.TagBID)\">Make Child</button>\n <button class=\"at-tax-dup-btn\" (click)=\"DismissDuplicate(pair)\">Dismiss</button>\n </div>\n }\n </div>\n }\n </div>\n }\n\n <!-- \u2550\u2550 SUB-TAB 3: ORPHANS \u2550\u2550 -->\n @if (TaxSubTab === 'orphans') {\n <div class=\"at-tax-orphan-toolbar\">\n <span class=\"at-tax-orphan-count\">{{ TaxOrphans.length }} orphaned tags</span>\n <span class=\"at-tax-orphan-desc\">— no parent, no children, low usage</span>\n <div class=\"at-tax-orphan-bulk\">\n <button class=\"at-tax-bulk-btn\" (click)=\"ToggleAllOrphans()\">\n @if (TaxAllOrphansSelected) { <i class=\"fa-solid fa-square-check\"></i> } @else { <i class=\"fa-regular fa-square\"></i> }\n Select All\n </button>\n <button class=\"at-tax-bulk-btn at-tax-bulk-danger\" (click)=\"BulkDeleteOrphans()\">\n <i class=\"fa-solid fa-trash\"></i> Bulk Delete\n </button>\n <button class=\"at-tax-bulk-btn at-tax-bulk-danger\" (click)=\"DeleteAllOrphans()\">\n <i class=\"fa-solid fa-trash-can\"></i> Delete All ({{ TaxOrphans.length }})\n </button>\n </div>\n </div>\n\n @if (TaxOrphans.length === 0) {\n <div class=\"at-empty-state\">\n <i class=\"fa-solid fa-check-circle\"></i>\n <p>No orphaned tags</p>\n </div>\n }\n\n <div class=\"at-tax-orphan-grid\">\n @for (orphan of TaxOrphans; track orphan.ID) {\n <div class=\"at-tax-orphan-card\">\n <div class=\"at-tax-orphan-header\">\n <span class=\"at-tax-orphan-name\">{{ orphan.Name }}</span>\n <input type=\"checkbox\" class=\"at-tax-orphan-checkbox\" [checked]=\"orphan.IsSelected\"\n (change)=\"ToggleOrphanSelection(orphan)\" (click)=\"$event.stopPropagation()\">\n </div>\n <div class=\"at-tax-orphan-stats\">\n <span>Usage: <strong>{{ orphan.UsageCount }}</strong></span>\n <span>Avg Weight: <strong>{{ orphan.AvgWeight.toFixed(2) }}</strong></span>\n </div>\n <div class=\"at-tax-orphan-dates\">\n <span>First: {{ orphan.FirstSeen }}</span>\n <span>Last: {{ orphan.LastSeen }}</span>\n </div>\n <div class=\"at-tax-orphan-actions\">\n <button class=\"at-tax-orphan-btn at-tax-orphan-delete\" (click)=\"DeleteOrphan(orphan)\">Delete</button>\n <button class=\"at-tax-orphan-btn\">Ignore</button>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- \u2550\u2550 SUB-TAB 4: TREEMAP \u2550\u2550 -->\n @if (TaxSubTab === 'treemap') {\n <div class=\"at-tax-treemap-kpi-strip\">\n @for (kpi of TaxTreemapKPIs; track kpi.Label) {\n <div class=\"at-tax-treemap-kpi\">\n <div class=\"at-tax-treemap-kpi-value\">{{ kpi.Value }}</div>\n <div class=\"at-tax-treemap-kpi-label\">{{ kpi.Label }}</div>\n </div>\n }\n </div>\n\n @if (TaxTreemapCells.length === 0) {\n <div class=\"at-empty-state\">\n <i class=\"fa-solid fa-chart-tree-map\"></i>\n <p>No taxonomy data to visualize</p>\n </div>\n } @else {\n <div class=\"at-tax-treemap-container\">\n @for (cell of TaxTreemapCells; track $index) {\n <div class=\"at-tax-treemap-cell at-tax-treemap-cell-clickable\" [class]=\"cell.ColorClass\"\n [style.grid-row]=\"cell.RowSpan > 1 ? 'span ' + cell.RowSpan : ''\"\n (click)=\"OpenTreemapDrillIn(cell)\">\n <span class=\"at-tax-cell-name\">{{ cell.Name }}</span>\n <span class=\"at-tax-cell-count\">{{ cell.ItemCount }} items</span>\n </div>\n }\n </div>\n }\n\n <!-- Treemap Drill-In Panel -->\n @if (ShowTreemapDrillIn && TreemapDrillInNode) {\n <div class=\"at-tax-drillin-overlay\" (click)=\"CloseTreemapDrillIn()\">\n <div class=\"at-tax-drillin-panel\" (click)=\"$event.stopPropagation()\">\n <div class=\"at-tax-drillin-header\">\n <h3><i class=\"fa-solid fa-tag\"></i> {{ TreemapDrillInNode.Name }}</h3>\n <button class=\"at-schedule-dialog-close\" (click)=\"CloseTreemapDrillIn()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div class=\"at-tax-drillin-body\">\n <div class=\"at-tax-stats-row\">\n <div class=\"at-tax-stat-item\">\n <div class=\"at-tax-stat-value\">{{ TreemapDrillInNode.ItemCount }}</div>\n <div class=\"at-tax-stat-label\">Items Tagged</div>\n </div>\n <div class=\"at-tax-stat-item\">\n <div class=\"at-tax-stat-value\">{{ TreemapDrillInNode.AvgWeight.toFixed(2) }}</div>\n <div class=\"at-tax-stat-label\">Avg Weight</div>\n </div>\n <div class=\"at-tax-stat-item\">\n <div class=\"at-tax-stat-value\">{{ TreemapDrillInNode.Children.length }}</div>\n <div class=\"at-tax-stat-label\">Children</div>\n </div>\n </div>\n\n @if (TreemapDrillInNode.Children.length > 0) {\n <div class=\"at-tax-detail-section\">\n <div class=\"at-tax-section-title\">Child Tags</div>\n <div class=\"at-tax-child-chips\">\n @for (child of TreemapDrillInNode.Children; track child.ID) {\n <span class=\"at-tax-child-chip\">{{ child.Name }} <span class=\"at-tax-chip-count\">{{ child.ItemCount }}</span></span>\n }\n </div>\n </div>\n }\n\n @if (TaxRecentItems.length > 0) {\n <div class=\"at-tax-detail-section\">\n <div class=\"at-tax-section-title\">Recently Tagged Items</div>\n <div class=\"at-tax-recent-list\">\n @for (item of TaxRecentItems; track $index) {\n <div class=\"at-tax-recent-item\">\n <div class=\"at-tax-recent-icon\"><i [class]=\"item.Icon\"></i></div>\n <div class=\"at-tax-recent-name\">{{ item.Name }}</div>\n <div class=\"at-tax-recent-weight\">{{ item.Weight.toFixed(2) }}</div>\n <div class=\"at-tax-recent-date\">{{ item.Date }}</div>\n </div>\n }\n </div>\n </div>\n }\n </div>\n <div class=\"at-tax-drillin-footer\">\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"DrillInToTreeView(TreemapDrillInNode)\">\n <i class=\"fa-solid fa-sitemap\"></i> View in Tree\n </button>\n </div>\n </div>\n </div>\n }\n }\n\n <!-- \u2550\u2550 SUB-TAB 5: AUDIT LOG \u2550\u2550 -->\n @if (TaxSubTab === 'audit') {\n <div class=\"at-tax-audit-filters\">\n <span class=\"at-tax-audit-filter-label\">Filter</span>\n <div class=\"at-tax-audit-checkbox-group\">\n <label class=\"at-tax-audit-checkbox\">\n <input type=\"checkbox\" [checked]=\"TaxAuditFilterTypes.has('created')\" (change)=\"ToggleTaxAuditFilter('created')\"> Created\n </label>\n <label class=\"at-tax-audit-checkbox\">\n <input type=\"checkbox\" [checked]=\"TaxAuditFilterTypes.has('merged')\" (change)=\"ToggleTaxAuditFilter('merged')\"> Merged\n </label>\n <label class=\"at-tax-audit-checkbox\">\n <input type=\"checkbox\" [checked]=\"TaxAuditFilterTypes.has('moved')\" (change)=\"ToggleTaxAuditFilter('moved')\"> Moved\n </label>\n <label class=\"at-tax-audit-checkbox\">\n <input type=\"checkbox\" [checked]=\"TaxAuditFilterTypes.has('deleted')\" (change)=\"ToggleTaxAuditFilter('deleted')\"> Deleted\n </label>\n <label class=\"at-tax-audit-checkbox\">\n <input type=\"checkbox\" [checked]=\"TaxAuditFilterTypes.has('renamed')\" (change)=\"ToggleTaxAuditFilter('renamed')\"> Renamed\n </label>\n <label class=\"at-tax-audit-checkbox\">\n <input type=\"checkbox\" [checked]=\"TaxAuditFilterTypes.has('deprecated')\" (change)=\"ToggleTaxAuditFilter('deprecated')\"> Deprecated\n </label>\n <label class=\"at-tax-audit-checkbox\">\n <input type=\"checkbox\" [checked]=\"TaxAuditFilterTypes.has('split')\" (change)=\"ToggleTaxAuditFilter('split')\"> Split\n </label>\n <label class=\"at-tax-audit-checkbox\">\n <input type=\"checkbox\" [checked]=\"TaxAuditFilterTypes.has('reactivated')\" (change)=\"ToggleTaxAuditFilter('reactivated')\"> Reactivated\n </label>\n </div>\n </div>\n\n @if (GetFilteredAuditEvents().length === 0) {\n <div class=\"at-empty-state\">\n <i class=\"fa-solid fa-scroll\"></i>\n <p>No audit events match the current filters</p>\n </div>\n }\n\n <div class=\"at-tax-audit-timeline\">\n @for (event of GetFilteredAuditEvents(); track $index) {\n <div class=\"at-tax-audit-event\">\n <div class=\"at-tax-audit-event-icon\" [class]=\"event.Type\">\n <i [class]=\"GetTaxAuditIcon(event.Type)\"></i>\n </div>\n <div class=\"at-tax-audit-event-body\">\n <div class=\"at-tax-audit-event-desc\">\n {{ event.Description }} <span class=\"at-tax-tag-ref\">{{ event.TagRef }}</span>\n </div>\n <div class=\"at-tax-audit-event-meta\">\n <span>{{ event.User }}</span>\n <span>{{ event.Timestamp }}</span>\n </div>\n </div>\n </div>\n }\n </div>\n }\n </div>\n }\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <!-- TAB 7: RUN HISTORY -->\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (ActiveTab === 'history') {\n <div class=\"at-page-header\">\n <div>\n <div class=\"at-page-title\">Run History</div>\n <div class=\"at-page-subtitle\">Processing run log across all content sources</div>\n </div>\n <div class=\"at-page-actions\">\n <select class=\"at-filter-select\" [(ngModel)]=\"HistorySourceFilter\" (change)=\"FilterRunHistory()\">\n <option value=\"\">All Sources</option>\n @for (s of HistorySourceOptions; track s) {\n <option [value]=\"s\">{{ s }}</option>\n }\n </select>\n <select class=\"at-filter-select\" [(ngModel)]=\"HistoryStatusFilter\" (change)=\"FilterRunHistory()\">\n <option value=\"\">All Status</option>\n <option value=\"complete\">Complete</option>\n <option value=\"failed\">Failed</option>\n <option value=\"running\">Running</option>\n </select>\n </div>\n </div>\n <div class=\"at-page-body\">\n <div class=\"at-card\">\n <div class=\"at-card-body\" style=\"max-height: 600px; overflow-y: auto;\">\n <table class=\"at-run-table\">\n <thead>\n <tr>\n <th>Status</th>\n <th>Source</th>\n <th>Started</th>\n <th>Duration</th>\n <th>Items</th>\n <th>Tags</th>\n <th>Errors</th>\n </tr>\n </thead>\n <tbody>\n @for (row of FilteredRunRows; track row.ID) {\n <tr class=\"at-run-row-clickable\"\n [class.at-run-row-selected]=\"SelectedRunID === row.ID\"\n (click)=\"OpenRunDetail(row.ID)\">\n <td>\n <span class=\"at-run-status-badge\" [class]=\"row.StatusClass\">\n @if (row.StatusClass === 'running') {\n <i class=\"fa-solid fa-spinner fa-spin\" style=\"font-size: 0.55rem\"></i>\n }\n {{ row.Status }}\n </span>\n </td>\n <td class=\"at-run-source-name\">{{ row.SourceName }}</td>\n <td>{{ row.StartedDisplay }}</td>\n <td class=\"at-run-duration\">{{ row.Duration }}</td>\n <td>{{ row.Items }}</td>\n <td>{{ row.Tags }}</td>\n <td [class]=\"row.ErrorClass\">{{ row.Errors }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n\n <!-- D3/D8: Run Detail Slide-In Panel -->\n @if (SelectedRunID) {\n <div class=\"at-card\" style=\"margin-top: 12px;\">\n <div class=\"at-card-header\">\n <span class=\"at-card-title\">\n <i class=\"fa-solid fa-magnifying-glass-chart\"></i>\n Run Detail: {{ SelectedRunID.slice(0, 14) }}...\n </span>\n <button class=\"at-action-btn at-secondary-btn\" style=\"font-size: 11px; padding: 3px 8px;\" (click)=\"CloseRunDetail()\">\n <i class=\"fa-solid fa-times\"></i> Close\n </button>\n </div>\n <div class=\"at-card-body\">\n @if (IsLoadingRunDetail) {\n <mj-loading text=\"Loading run details...\" size=\"small\"></mj-loading>\n } @else if (RunDetailRows.length > 0) {\n <table class=\"at-run-table\">\n <thead>\n <tr>\n <th>Source</th>\n <th>Source Type</th>\n <th>Status</th>\n <th>Items</th>\n <th>Tagged</th>\n <th>Vectorized</th>\n <th>Errors</th>\n <th>Tokens</th>\n <th>Cost</th>\n <th>Duration</th>\n </tr>\n </thead>\n <tbody>\n @for (detail of RunDetailRows; track detail.SourceName) {\n <tr>\n <td class=\"at-run-source-name\">{{ detail.SourceName }}</td>\n <td>{{ detail.SourceType }}</td>\n <td>\n <span class=\"at-run-status-badge\" [class]=\"detail.StatusClass\">{{ detail.Status }}</span>\n </td>\n <td>{{ detail.ItemsProcessed }}</td>\n <td>{{ detail.ItemsTagged }}</td>\n <td>{{ detail.ItemsVectorized }}</td>\n <td [class.run-error-text]=\"detail.ErrorCount > 0\">{{ detail.ErrorCount }}</td>\n <td>{{ FormatTokenCount(detail.TotalTokens) }}</td>\n <td>{{ detail.TotalCost > 0 ? '$' + detail.TotalCost.toFixed(4) : '$0.00' }}</td>\n <td class=\"at-run-duration\">{{ detail.Duration }}</td>\n </tr>\n }\n </tbody>\n </table>\n } @else {\n <div class=\"at-empty-state\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <p>No per-source detail records found for this run.</p>\n </div>\n }\n </div>\n </div>\n }\n </div>\n }\n }\n </div>\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 SLIDE-IN FORM OVERLAY \u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (FormMode !== 'none') {\n <div class=\"at-slide-overlay\" (click)=\"CloseForm()\"></div>\n <div class=\"at-slide-panel\">\n <div class=\"at-slide-header\">\n <h3>\n @if (FormMode === 'add-source') { Add Content Source }\n @else if (FormMode === 'edit-source') { Edit Content Source }\n @else if (FormMode === 'add-type') { Add Content Type }\n @else if (FormMode === 'edit-type') { Edit Content Type }\n </h3>\n <button class=\"at-slide-close\" (click)=\"CloseForm()\"><i class=\"fa-solid fa-times\"></i></button>\n </div>\n <div class=\"at-slide-body\">\n\n <!-- Source form -->\n @if (FormMode === 'add-source' || FormMode === 'edit-source') {\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">Name</label>\n <input type=\"text\" class=\"at-form-input\" [(ngModel)]=\"FormSourceName\" placeholder=\"Source name\">\n </div>\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">Source Type</label>\n <select class=\"at-form-select\" [(ngModel)]=\"FormSourceTypeID\">\n <option value=\"\">Select source type...</option>\n @for (opt of SourceTypeOptions; track opt.ID) {\n <option [value]=\"opt.ID\">{{ opt.Name }}</option>\n }\n </select>\n </div>\n <!-- Content Type + File Type: hidden for Entity source type -->\n @if (SelectedSourceTypeRequiresContentType) {\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">Content Type</label>\n <select class=\"at-form-select\" [(ngModel)]=\"FormContentTypeID\">\n <option value=\"\">Select content type...</option>\n @for (opt of ContentTypeOptions; track opt.ID) {\n <option [value]=\"opt.ID\">{{ opt.Name }}</option>\n }\n </select>\n </div>\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">File Type</label>\n <select class=\"at-form-select\" [(ngModel)]=\"FormFileTypeID\">\n <option value=\"\">Select file type...</option>\n @for (opt of FileTypeOptions; track opt.ID) {\n <option [value]=\"opt.ID\">{{ opt.Name }}</option>\n }\n </select>\n </div>\n }\n\n <!-- Dynamic source-type-specific fields from ConfigurationObject.RequiredFields -->\n @for (field of SelectedSourceTypeFields; track field.Key) {\n @if (!field.DependsOnField || FormSourceSpecificConfig[field.DependsOnField]) {\n @if (!field.ShowOnlyIfMultiple || GetDependentOptions(field).length > 1) {\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">{{ field.Label }} @if (field.Required) { <span class=\"at-required\">*</span> }</label>\n @switch (field.Type) {\n @case ('url') {\n <input type=\"url\" class=\"at-form-input\" [(ngModel)]=\"FormSourceSpecificConfig[field.Key]\"\n [placeholder]=\"field.Description || 'https://...'\">\n }\n @case ('path') {\n <input type=\"text\" class=\"at-form-input\" [(ngModel)]=\"FormSourceSpecificConfig[field.Key]\"\n [placeholder]=\"field.Description || '/path/to/...'\">\n }\n @case ('text') {\n <input type=\"text\" class=\"at-form-input\" [(ngModel)]=\"FormSourceSpecificConfig[field.Key]\"\n [placeholder]=\"field.Description || ''\" [value]=\"field.DefaultValue || ''\">\n }\n @case ('entity-picker') {\n <select class=\"at-form-select\" [(ngModel)]=\"FormSourceSpecificConfig[field.Key]\"\n (ngModelChange)=\"OnSourceFieldChanged(field.Key)\">\n <option value=\"\">Select entity...</option>\n @for (opt of EntitiesWithDocuments; track opt.ID) {\n <option [value]=\"opt.ID\">{{ opt.Name }}</option>\n }\n </select>\n }\n @case ('entity-doc-picker') {\n <select class=\"at-form-select\" [(ngModel)]=\"FormSourceSpecificConfig[field.Key]\">\n @for (opt of GetDependentOptions(field); track opt.ID) {\n <option [value]=\"opt.ID\">{{ opt.Name }}</option>\n }\n </select>\n }\n @case ('storage-provider-picker') {\n <select class=\"at-form-select\" [(ngModel)]=\"FormSourceSpecificConfig[field.Key]\">\n <option value=\"\">Select provider...</option>\n @for (opt of StorageProviderOptions; track opt) {\n <option [value]=\"opt\">{{ opt }}</option>\n }\n </select>\n }\n @case ('dropdown') {\n <select class=\"at-form-select\" [(ngModel)]=\"FormSourceSpecificConfig[field.Key]\">\n <option value=\"\">Select...</option>\n @for (opt of field.Options || []; track opt.Value) {\n <option [value]=\"opt.Value\">{{ opt.Label }}</option>\n }\n </select>\n }\n }\n @if (field.Description) {\n <span class=\"at-form-hint\">{{ field.Description }}</span>\n }\n </div>\n }\n }\n }\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">Embedding Model Override</label>\n <mj-tree-dropdown\n [BranchConfig]=\"EmbeddingVendorBranch\"\n [LeafConfig]=\"EmbeddingModelsLeaf\"\n SelectionMode=\"single\"\n SelectableTypes=\"leaf\"\n Placeholder=\"Use system default\"\n [Clearable]=\"true\"\n [Value]=\"ToCompositeKey(FormSourceEmbeddingModelID)\"\n (ValueChange)=\"FormSourceEmbeddingModelID = FromCompositeKey($event)\">\n </mj-tree-dropdown>\n <span class=\"at-form-hint\">Overrides Content Type default</span>\n </div>\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">Vector Index Override</label>\n <select class=\"at-form-select\" [(ngModel)]=\"FormSourceVectorIndexID\">\n <option value=\"\">Use system default</option>\n @for (opt of VectorIndexOptions; track opt.ID) {\n <option [value]=\"opt.ID\">{{ opt.Name }}</option>\n }\n </select>\n <span class=\"at-form-hint\">Overrides Content Type default</span>\n </div>\n <div class=\"at-form-actions\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"SaveSource()\" [disabled]=\"FormSaving\">\n @if (FormSaving) { <i class=\"fa-solid fa-spinner fa-spin\"></i> Saving... }\n @else { <i class=\"fa-solid fa-check\"></i> Save }\n </button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"CloseForm()\">Cancel</button>\n </div>\n }\n\n <!-- Content Type form -->\n @if (FormMode === 'add-type' || FormMode === 'edit-type') {\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">Name</label>\n <input type=\"text\" class=\"at-form-input\" [(ngModel)]=\"FormTypeName\" placeholder=\"Content type name\">\n </div>\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">Description</label>\n <textarea class=\"at-form-textarea\" [(ngModel)]=\"FormTypeDescription\" rows=\"3\" placeholder=\"Description...\"></textarea>\n </div>\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">AI Model (for tagging)</label>\n <mj-tree-dropdown\n [BranchConfig]=\"AIModelVendorBranch\"\n [LeafConfig]=\"AllModelsLeaf\"\n SelectionMode=\"single\"\n SelectableTypes=\"leaf\"\n Placeholder=\"Select AI model...\"\n [Clearable]=\"true\"\n [Value]=\"ToCompositeKey(FormTypeAIModelID)\"\n (ValueChange)=\"FormTypeAIModelID = FromCompositeKey($event)\">\n </mj-tree-dropdown>\n </div>\n <div class=\"at-form-row\">\n <div class=\"at-form-group\" style=\"flex: 1;\">\n <label class=\"at-form-label\">Min Tags</label>\n <input type=\"number\" class=\"at-form-input\" [(ngModel)]=\"FormTypeMinTags\" min=\"0\">\n </div>\n <div class=\"at-form-group\" style=\"flex: 1;\">\n <label class=\"at-form-label\">Max Tags</label>\n <input type=\"number\" class=\"at-form-input\" [(ngModel)]=\"FormTypeMaxTags\" min=\"1\">\n </div>\n </div>\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">Embedding Model</label>\n <mj-tree-dropdown\n [BranchConfig]=\"EmbeddingVendorBranch\"\n [LeafConfig]=\"EmbeddingModelsLeaf\"\n SelectionMode=\"single\"\n SelectableTypes=\"leaf\"\n Placeholder=\"Use system default\"\n [Clearable]=\"true\"\n [Value]=\"ToCompositeKey(FormTypeEmbeddingModelID)\"\n (ValueChange)=\"FormTypeEmbeddingModelID = FromCompositeKey($event)\">\n </mj-tree-dropdown>\n </div>\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">Vector Index</label>\n <select class=\"at-form-select\" [(ngModel)]=\"FormTypeVectorIndexID\">\n <option value=\"\">Use system default</option>\n @for (opt of VectorIndexOptions; track opt.ID) {\n <option [value]=\"opt.ID\">{{ opt.Name }}</option>\n }\n </select>\n </div>\n <div class=\"at-form-actions\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"SaveContentType()\" [disabled]=\"FormSaving\">\n @if (FormSaving) { <i class=\"fa-solid fa-spinner fa-spin\"></i> Saving... }\n @else { <i class=\"fa-solid fa-check\"></i> Save }\n </button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"CloseForm()\">Cancel</button>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 ITEM DETAIL SLIDE-IN \u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (ShowItemDetail && SelectedFeedItem) {\n <div class=\"at-slide-overlay\" (click)=\"CloseItemDetail()\"></div>\n <div class=\"at-slide-panel at-detail-panel\">\n <div class=\"at-slide-header\">\n <h3><i class=\"fa-solid fa-file-lines\"></i> Content Item</h3>\n <button class=\"at-slide-close\" (click)=\"CloseItemDetail()\"><i class=\"fa-solid fa-times\"></i></button>\n </div>\n <div class=\"at-slide-body\">\n <!-- Header -->\n <div class=\"at-detail-item-header\">\n <h4 class=\"at-detail-item-name\">{{ SelectedFeedItem.Name }}</h4>\n <div class=\"at-detail-badges\">\n <span class=\"at-detail-badge at-detail-badge-source\">{{ SelectedFeedItem.SourceName }}</span>\n @if (SelectedFeedItem.RequiresContentType) {\n <span class=\"at-detail-badge at-detail-badge-type\">{{ SelectedFeedItem.ContentTypeName }}</span>\n @if (SelectedFeedItem.FileTypeName) {\n <span class=\"at-detail-badge at-detail-badge-file\"><i class=\"fa-solid fa-file\"></i> {{ SelectedFeedItem.FileTypeName }}</span>\n }\n }\n </div>\n </div>\n\n <!-- URL -->\n @if (SelectedFeedItem.URL) {\n <div class=\"at-detail-section\">\n <div class=\"at-detail-section-label\">URL</div>\n <a [href]=\"SelectedFeedItem.URL\" target=\"_blank\" class=\"at-detail-link\">\n {{ SelectedFeedItem.URL }}\n <i class=\"fa-solid fa-external-link-alt\" style=\"font-size: 0.65rem; margin-left: 4px;\"></i>\n </a>\n </div>\n }\n\n <!-- Text Preview -->\n @if (SelectedFeedItem.TextContent) {\n <div class=\"at-detail-section\">\n <div class=\"at-detail-section-label\">Content Preview</div>\n <div class=\"at-detail-text-preview\">{{ SelectedFeedItem.TextContent }}</div>\n </div>\n }\n\n <!-- Tags (weighted) -->\n @if (SelectedFeedItem.Tags.length > 0) {\n <div class=\"at-detail-section\">\n <div class=\"at-detail-section-label\">Tags ({{ SelectedFeedItem.Tags.length }})</div>\n <div class=\"at-detail-tags\">\n @for (wt of SelectedFeedItem.Tags; track wt.Tag) {\n <span class=\"at-tag-pill at-tag-weighted\" [style.font-size]=\"TagFontSize(wt.Weight)\">\n {{ wt.Tag }}\n <span class=\"at-tag-weight\">{{ FormatWeight(wt.Weight) }}</span>\n </span>\n }\n </div>\n </div>\n }\n\n <!-- Metadata -->\n <div class=\"at-detail-section\">\n <div class=\"at-detail-section-label\">Metadata</div>\n <div class=\"at-detail-meta-grid\">\n @if (SelectedFeedItem.Checksum) {\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">Checksum</span>\n <span class=\"at-detail-meta-value at-detail-meta-mono\">{{ SelectedFeedItem.Checksum }}</span>\n </div>\n }\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">Created</span>\n <span class=\"at-detail-meta-value\">{{ SelectedFeedItem.CreatedAt }}</span>\n </div>\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">Updated</span>\n <span class=\"at-detail-meta-value\">{{ SelectedFeedItem.UpdatedAt }}</span>\n </div>\n </div>\n </div>\n\n <!-- Actions -->\n <div class=\"at-detail-actions\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"OpenRecordFromItem(SelectedFeedItem)\">\n <i class=\"fa-solid fa-external-link-alt\"></i> Open Record\n </button>\n <button class=\"at-action-btn at-secondary-btn\" disabled>\n <i class=\"fa-solid fa-magnifying-glass\"></i> See Similar Items\n </button>\n </div>\n </div>\n </div>\n }\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 SOURCE DETAIL SLIDE-IN \u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (ShowSourceDetail) {\n <div class=\"at-slide-overlay\" (click)=\"CloseSourceDetail()\"></div>\n <div class=\"at-slide-panel at-detail-panel\">\n <div class=\"at-slide-header\">\n <h3><i class=\"fa-solid fa-database\"></i> Source Detail</h3>\n <button class=\"at-slide-close\" (click)=\"CloseSourceDetail()\"><i class=\"fa-solid fa-times\"></i></button>\n </div>\n <div class=\"at-slide-body\">\n @if (SourceDetailLoading) {\n <div class=\"at-loading-overlay\">\n <mj-loading text=\"Loading source details...\"></mj-loading>\n </div>\n }\n @if (!SourceDetailLoading && SelectedSource) {\n <!-- Source Header -->\n <div class=\"at-detail-source-header\">\n <div class=\"at-source-card-icon\"><i [class]=\"SelectedSource.Icon\"></i></div>\n <div>\n <h4 class=\"at-detail-item-name\">{{ SelectedSource.Name }}</h4>\n <div class=\"at-detail-badges\">\n <span class=\"at-detail-badge at-detail-badge-type\">{{ SelectedSource.SourceTypeName }}</span>\n <span class=\"at-detail-badge\" [class]=\"'at-detail-badge-status-' + SelectedSource.StatusClass\">\n {{ SelectedSource.StatusLabel }}\n </span>\n </div>\n </div>\n </div>\n\n <!-- Configuration -->\n <div class=\"at-detail-section\">\n <div class=\"at-detail-section-label\">Configuration</div>\n <div class=\"at-detail-meta-grid\">\n @if (SelectedSource.URL) {\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">URL</span>\n <a [href]=\"SelectedSource.URL\" target=\"_blank\" class=\"at-detail-link at-detail-meta-value\">{{ SelectedSource.URL }}</a>\n </div>\n }\n @if (SelectedSource.RequiresFileType) {\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">Content Type</span>\n <span class=\"at-detail-meta-value\">{{ SelectedSource.ContentTypeName }}</span>\n </div>\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">File Type</span>\n <span class=\"at-detail-meta-value\">{{ SelectedSource.FileTypeName }}</span>\n </div>\n }\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">Embedding Model</span>\n <span class=\"at-detail-meta-value\">{{ SelectedSource.EmbeddingModelName }}</span>\n </div>\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">Vector Index</span>\n <span class=\"at-detail-meta-value\">{{ SelectedSource.VectorIndexName }}</span>\n </div>\n </div>\n </div>\n\n <!-- Stats -->\n <div class=\"at-detail-section\">\n <div class=\"at-detail-section-label\">Statistics</div>\n <div class=\"at-detail-stats-strip\">\n <div class=\"at-detail-stat\">\n <div class=\"at-detail-stat-value\">{{ formatNumber(SelectedSource.ItemCount) }}</div>\n <div class=\"at-detail-stat-label\">Items</div>\n </div>\n <div class=\"at-detail-stat\">\n <div class=\"at-detail-stat-value\">{{ formatNumber(SelectedSource.TagCount) }}</div>\n <div class=\"at-detail-stat-label\">Tags</div>\n </div>\n <div class=\"at-detail-stat\">\n <div class=\"at-detail-stat-value\">{{ SelectedSource.AvgTags }}</div>\n <div class=\"at-detail-stat-label\">Avg Tags</div>\n </div>\n <div class=\"at-detail-stat\">\n <div class=\"at-detail-stat-value\">{{ SelectedSource.LastRunAgo }}</div>\n <div class=\"at-detail-stat-label\">Last Run</div>\n </div>\n <div class=\"at-detail-stat\">\n <div class=\"at-detail-stat-value\" [class.at-kpi-error-value]=\"SelectedSource.ErrorCount > 0\">{{ SelectedSource.ErrorCount }}</div>\n <div class=\"at-detail-stat-label\">Errors</div>\n </div>\n </div>\n </div>\n\n <!-- Content Library (D4 status badges, D7 pagination) -->\n <div class=\"at-detail-section\">\n <div class=\"at-detail-section-header-row\">\n <div class=\"at-detail-section-label\">Content Library ({{ FilteredSourceDetailTotal }} of {{ SelectedSource.ItemCount | number }})</div>\n <div class=\"at-detail-section-controls\">\n <select class=\"at-detail-filter-select\" [(ngModel)]=\"SourceDetailStatusFilter\" (ngModelChange)=\"OnSourceDetailStatusFilterChange()\">\n @for (opt of SourceDetailStatusOptions; track opt) {\n <option [value]=\"opt\">{{ opt }}</option>\n }\n </select>\n <button class=\"at-action-btn at-retry-btn\" (click)=\"RetryFailedItems()\" title=\"Re-queue failed items for processing\">\n <i class=\"fa-solid fa-rotate-right\"></i> Retry Failed\n </button>\n </div>\n </div>\n <div class=\"at-detail-content-list\">\n @if (FilteredSourceDetailItems.length === 0) {\n <div class=\"at-empty-state\" style=\"padding: 16px;\">\n <p>{{ SourceDetailStatusFilter === 'All' ? 'No content items yet.' : 'No items match this filter.' }}</p>\n </div>\n }\n @for (ci of FilteredSourceDetailItems; track ci.ID) {\n <div class=\"at-detail-content-item\" (click)=\"OpenContentItemDetail(ci)\">\n <div class=\"at-feed-status-dot\" [class]=\"ci.StatusDot\"></div>\n <span class=\"at-detail-content-item-name\">{{ ci.Name }}</span>\n <span class=\"at-status-badge\" [class]=\"GetStatusBadgeClass(ci.EmbeddingStatus)\">E:{{ ci.EmbeddingStatus }}</span>\n <span class=\"at-status-badge\" [class]=\"GetStatusBadgeClass(ci.TaggingStatus)\">T:{{ ci.TaggingStatus }}</span>\n <span class=\"at-detail-content-item-tags\">{{ ci.TagCount }} tags</span>\n <span class=\"at-detail-content-item-time\">{{ ci.UpdatedAt }}</span>\n </div>\n }\n </div>\n <!-- Pagination -->\n @if (SourceDetailTotalPages > 1) {\n <div class=\"at-detail-pagination\">\n <button class=\"at-page-btn\" [disabled]=\"SourceDetailPage === 0\" (click)=\"SourceDetailPrevPage()\">\n <i class=\"fa-solid fa-chevron-left\"></i> Prev\n </button>\n <span class=\"at-page-info\">Page {{ SourceDetailPage + 1 }} of {{ SourceDetailTotalPages }}</span>\n <button class=\"at-page-btn\" [disabled]=\"SourceDetailPage >= SourceDetailTotalPages - 1\" (click)=\"SourceDetailNextPage()\">\n Next <i class=\"fa-solid fa-chevron-right\"></i>\n </button>\n </div>\n }\n </div>\n\n <!-- Run History -->\n @if (SelectedSource.RunHistory.length > 0) {\n <div class=\"at-detail-section\">\n <div class=\"at-detail-section-label\">Recent Runs</div>\n <div class=\"at-detail-run-history\">\n @for (run of SelectedSource.RunHistory; track run.ID) {\n <div class=\"at-detail-run-row\">\n <span class=\"at-run-status-badge\" [class]=\"run.StatusClass\">{{ run.Status }}</span>\n <span class=\"at-detail-run-time\">{{ run.StartedDisplay }}</span>\n <span class=\"at-detail-run-duration\">{{ run.Duration }}</span>\n <span class=\"at-detail-run-items\">{{ run.Items }} items</span>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Actions -->\n <div class=\"at-detail-actions\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"OpenEditSourceFromDetail()\">\n <i class=\"fa-solid fa-pen\"></i> Edit\n </button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"RunSourceFromDetail()\">\n <i class=\"fa-solid fa-play\"></i> Run Now\n </button>\n <button class=\"at-action-btn at-secondary-btn at-source-delete-btn\" (click)=\"DeleteSourceFromDetail()\">\n <i class=\"fa-solid fa-trash\"></i> Delete\n </button>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 SCHEDULE DIALOG \u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (ShowScheduleDialog && SchedulingSourceCard) {\n <div class=\"at-schedule-overlay\" (click)=\"CloseScheduleDialog()\">\n <div class=\"at-schedule-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"at-schedule-dialog-header\">\n <h3><i class=\"fa-regular fa-clock\"></i> Schedule Pipeline</h3>\n <button class=\"at-schedule-dialog-close\" (click)=\"CloseScheduleDialog()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div class=\"at-schedule-dialog-body\">\n <div class=\"at-schedule-field\">\n <label>Source</label>\n <div class=\"at-schedule-source-name\">\n <i [class]=\"SchedulingSourceCard.Icon\"></i>\n {{ SchedulingSourceCard.Name }}\n </div>\n </div>\n <div class=\"at-schedule-field\">\n <label>Action</label>\n <div class=\"at-schedule-action-name\">Autotag and Vectorize Content</div>\n </div>\n <div class=\"at-schedule-field\">\n <label for=\"schedule-cron\">Cron Expression</label>\n <input id=\"schedule-cron\"\n type=\"text\"\n class=\"mj-input at-schedule-cron-input\"\n [(ngModel)]=\"ScheduleCron\"\n placeholder=\"0 2 * * *\" />\n <div class=\"at-schedule-cron-preview\">\n <i class=\"fa-solid fa-info-circle\"></i>\n {{ GetCronPreview(ScheduleCron) }}\n </div>\n </div>\n <div class=\"at-schedule-field at-schedule-toggle-row\">\n <label for=\"schedule-enabled\">Enabled</label>\n <input id=\"schedule-enabled\"\n type=\"checkbox\"\n class=\"mj-checkbox\"\n [(ngModel)]=\"ScheduleEnabled\" />\n </div>\n </div>\n <div class=\"at-schedule-dialog-footer\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"SaveSchedule()\" [disabled]=\"ScheduleSaving\">\n @if (ScheduleSaving) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Saving...\n } @else {\n <i class=\"fa-solid fa-check\"></i> Create Schedule\n }\n </button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"CloseScheduleDialog()\">Cancel</button>\n </div>\n </div>\n </div>\n }\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 CONFIRMATION DIALOG \u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (ShowConfirmDialog) {\n <div class=\"at-schedule-overlay\" (click)=\"ConfirmDialogCancel()\">\n <div class=\"at-schedule-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"at-schedule-dialog-header\">\n <h3><i class=\"fa-solid fa-triangle-exclamation\"></i> {{ ConfirmDialogTitle }}</h3>\n <button class=\"at-schedule-dialog-close\" (click)=\"ConfirmDialogCancel()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div class=\"at-schedule-dialog-body\">\n <p class=\"at-confirm-message\">{{ ConfirmDialogMessage }}</p>\n </div>\n <div class=\"at-schedule-dialog-footer\">\n <button class=\"at-action-btn at-danger-btn\" (click)=\"ConfirmDialogAccept()\">\n <i class=\"fa-solid fa-check\"></i> Confirm\n </button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"ConfirmDialogCancel()\">Cancel</button>\n </div>\n </div>\n </div>\n }\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 SPLIT DIALOG \u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (ShowSplitDialog) {\n <div class=\"at-schedule-overlay\" (click)=\"CloseSplitDialog()\">\n <div class=\"at-schedule-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"at-schedule-dialog-header\">\n <h3><i class=\"fa-solid fa-code-branch\"></i> Split Tag</h3>\n <button class=\"at-schedule-dialog-close\" (click)=\"CloseSplitDialog()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div class=\"at-schedule-dialog-body\">\n <div class=\"at-schedule-field\">\n <label>New tag names (comma-separated)</label>\n <input type=\"text\" class=\"mj-input\" style=\"width: 100%;\"\n [(ngModel)]=\"SplitChildNames\"\n placeholder=\"Tag A, Tag B, Tag C\" />\n </div>\n <p class=\"at-confirm-message\">New tags will be created as siblings of the original tag under the same parent.</p>\n </div>\n <div class=\"at-schedule-dialog-footer\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"ExecuteSplit()\" [disabled]=\"!SplitChildNames.trim()\">\n <i class=\"fa-solid fa-code-branch\"></i> Create Tags\n </button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"CloseSplitDialog()\">Cancel</button>\n </div>\n </div>\n </div>\n }\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 MOVE DIALOG \u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (ShowCreateTagDialog) {\n <div class=\"at-schedule-overlay\" (click)=\"CloseCreateTagDialog()\">\n <div class=\"at-schedule-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"at-schedule-dialog-header\">\n <h3><i class=\"fa-solid fa-plus\"></i> Create Tag</h3>\n <button class=\"at-schedule-dialog-close\" (click)=\"CloseCreateTagDialog()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div class=\"at-schedule-dialog-body\">\n <div class=\"at-tax-create-context\">{{ CreateTagParentLabel }}</div>\n <div class=\"at-schedule-field\">\n <label>Name</label>\n <input type=\"text\" class=\"mj-input\" style=\"width: 100%;\" [(ngModel)]=\"CreateTagName\"\n placeholder=\"Tag name\" (keydown.enter)=\"SaveNewTag()\">\n </div>\n <div class=\"at-schedule-field\">\n <label>Description (optional)</label>\n <textarea class=\"mj-textarea\" rows=\"3\" style=\"width: 100%;\" [(ngModel)]=\"CreateTagDescription\"\n placeholder=\"Brief description of this tag\"></textarea>\n </div>\n </div>\n <div class=\"at-schedule-dialog-footer\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"SaveNewTag()\" [disabled]=\"!CreateTagName.trim()\">\n <i class=\"fa-solid fa-check\"></i> Create\n </button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"CloseCreateTagDialog()\">Cancel</button>\n </div>\n </div>\n </div>\n }\n\n @if (ShowMoveDialog) {\n <div class=\"at-schedule-overlay\" (click)=\"CloseMoveDialog()\">\n <div class=\"at-schedule-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"at-schedule-dialog-header\">\n <h3><i class=\"fa-solid fa-arrows-up-down\"></i> Move Tag</h3>\n <button class=\"at-schedule-dialog-close\" (click)=\"CloseMoveDialog()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div class=\"at-schedule-dialog-body\">\n <div class=\"at-schedule-field\">\n <label>New parent tag</label>\n <select class=\"mj-input\" style=\"width: 100%;\" [(ngModel)]=\"MoveNewParentID\">\n <option [ngValue]=\"null\">(Root level — no parent)</option>\n @for (opt of GetMoveTargetOptions(); track opt.ID) {\n <option [ngValue]=\"opt.ID\">{{ '\\u00A0\\u00A0'.repeat(opt.Depth) }}{{ opt.Name }}</option>\n }\n </select>\n </div>\n </div>\n <div class=\"at-schedule-dialog-footer\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"ExecuteMove()\">\n <i class=\"fa-solid fa-check\"></i> Move\n </button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"CloseMoveDialog()\">Cancel</button>\n </div>\n </div>\n </div>\n }\n\n @if (ShowMergeIntoDialog) {\n <div class=\"at-schedule-overlay\" (click)=\"CloseMergeIntoDialog()\">\n <div class=\"at-schedule-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"at-schedule-dialog-header\">\n <h3><i class=\"fa-solid fa-compress\"></i> Merge \"{{ MergeSourceTag?.Name }}\" Into...</h3>\n <button class=\"at-schedule-dialog-close\" (click)=\"CloseMergeIntoDialog()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div class=\"at-schedule-dialog-body\">\n <p style=\"font-size: 0.85rem; color: var(--mj-text-secondary); margin: 0 0 12px;\">\n All tagged items will be moved to the target tag, then \"{{ MergeSourceTag?.Name }}\" will be deleted.\n </p>\n <div class=\"at-schedule-field\">\n <label>Merge into</label>\n <mj-combobox\n [Data]=\"MergeTargetData\"\n TextField=\"Label\"\n ValueField=\"ID\"\n [Filterable]=\"true\"\n [ValuePrimitive]=\"true\"\n Placeholder=\"Search and select a tag...\"\n (ValueChange)=\"OnMergeTargetSelected($event)\">\n </mj-combobox>\n </div>\n </div>\n <div class=\"at-schedule-dialog-footer\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"ExecuteMergeInto()\" [disabled]=\"!MergeTargetID || IsMerging\">\n @if (IsMerging) { <i class=\"fa-solid fa-spinner fa-spin\"></i> Merging... } @else { <i class=\"fa-solid fa-compress\"></i> Merge }\n </button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"CloseMergeIntoDialog()\">Cancel</button>\n </div>\n </div>\n </div>\n }\n\n <!-- No Content Types Warning Dialog (z-index above slide-in panel) -->\n @if (ShowNoContentTypeWarning) {\n <div class=\"at-schedule-dialog-overlay\" style=\"z-index: 10001;\" (click)=\"ShowNoContentTypeWarning = false\">\n <div class=\"at-schedule-dialog\" style=\"max-width: 440px; z-index: 10002;\" (click)=\"$event.stopPropagation()\">\n <div class=\"at-schedule-dialog-header\">\n <span><i class=\"fa-solid fa-triangle-exclamation\" style=\"color: var(--mj-status-warning); margin-right: 8px;\"></i> Content Type Required</span>\n <button class=\"at-close-btn\" (click)=\"ShowNoContentTypeWarning = false\"><i class=\"fa-solid fa-xmark\"></i></button>\n </div>\n <div class=\"at-schedule-dialog-body\" style=\"text-align: center; padding: 24px;\">\n <p style=\"color: var(--mj-text-secondary); margin-bottom: 16px;\">\n No content types have been configured yet. At least one content type is required before you can create a content source.\n </p>\n <p style=\"color: var(--mj-text-muted); font-size: 13px;\">\n Go to the <strong>Content Types</strong> tab to create one.\n </p>\n </div>\n <div class=\"at-schedule-dialog-footer\">\n <button class=\"at-action-btn at-primary-btn\" (mousedown)=\"ShowNoContentTypeWarning = false; CloseForm(); SwitchTab('types')\">\n <i class=\"fa-solid fa-arrow-right\"></i> Go to Content Types\n </button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"ShowNoContentTypeWarning = false\">Close</button>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: ["/* ============================================================\n Content Autotagging Dashboard\n All colors use MJ design tokens \u2014 no hardcoded hex values.\n All classes prefixed with at- to prevent leaking (ViewEncapsulation.None).\n ============================================================ */\n\n/* \u2500\u2500 Root layout \u2500\u2500 */\n\n.at-dashboard {\n display: flex;\n height: 100%;\n overflow: hidden;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 LEFT NAV \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-left-nav {\n width: 220px;\n background: var(--mj-bg-surface);\n border-right: 1px solid var(--mj-border-default);\n display: flex;\n flex-direction: column;\n flex-shrink: 0;\n}\n\n.at-left-nav-header {\n padding: 16px 16px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.at-left-nav-header h2 {\n font-size: 0.95rem;\n font-weight: 700;\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0;\n color: var(--mj-text-primary);\n}\n\n.at-left-nav-header h2 i {\n color: var(--mj-brand-primary);\n}\n\n.at-left-nav-items {\n flex: 1;\n padding: 8px;\n overflow-y: auto;\n}\n\n.at-nav-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 12px;\n border-radius: 8px;\n font-size: 0.82rem;\n font-weight: 500;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.12s ease;\n margin-bottom: 2px;\n}\n\n.at-nav-item:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-secondary);\n}\n\n.at-nav-item.active {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.at-nav-item i {\n width: 18px;\n text-align: center;\n font-size: 0.8rem;\n}\n\n.at-nav-badge {\n margin-left: auto;\n background: var(--mj-bg-surface-sunken);\n padding: 1px 7px;\n border-radius: 10px;\n font-size: 0.65rem;\n font-weight: 600;\n color: var(--mj-text-muted);\n}\n\n.at-nav-item.active .at-nav-badge {\n background: color-mix(in srgb, var(--mj-brand-primary) 18%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.at-nav-badge-live {\n background: var(--mj-status-success-bg) !important;\n color: var(--mj-status-success-text) !important;\n}\n\n.at-nav-divider {\n height: 1px;\n background: var(--mj-border-subtle);\n margin: 8px 12px;\n}\n\n.at-left-nav-footer {\n padding: 12px;\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n.at-run-pipeline-btn {\n width: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 10px;\n border: none;\n border-radius: 8px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n font-size: 0.82rem;\n font-weight: 600;\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.at-run-pipeline-btn:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n}\n\n.at-run-pipeline-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 MAIN CONTENT AREA \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-main-area {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n.at-loading-overlay {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n}\n\n.at-page-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.at-page-title {\n font-size: 1rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.at-page-subtitle {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n.at-page-actions {\n display: flex;\n gap: 8px;\n align-items: center;\n}\n\n.at-page-body {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n padding: 16px 20px;\n min-height: 0;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 SHARED COMPONENTS \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-action-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 7px 14px;\n border: 1px solid;\n border-radius: 7px;\n cursor: pointer;\n font-size: 0.78rem;\n font-weight: 500;\n transition: all 0.15s ease;\n}\n\n.at-primary-btn {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-color: var(--mj-brand-primary);\n}\n\n.at-primary-btn:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n}\n\n.at-primary-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.at-secondary-btn {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n border-color: var(--mj-border-default);\n}\n\n.at-secondary-btn:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.at-card {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 10px;\n overflow: hidden;\n}\n\n.at-card-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.at-card-title {\n font-size: 0.82rem;\n font-weight: 600;\n display: flex;\n align-items: center;\n gap: 6px;\n color: var(--mj-text-primary);\n}\n\n.at-card-title i {\n color: var(--mj-brand-primary);\n font-size: 0.75rem;\n}\n\n.at-card-body {\n padding: 0;\n}\n\n.at-empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 32px 16px;\n color: var(--mj-text-disabled);\n}\n\n.at-empty-state i {\n font-size: 28px;\n}\n\n.at-empty-state p {\n margin: 0;\n font-size: 13px;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 KPI STRIP \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-kpi-strip {\n display: flex;\n gap: 12px;\n margin-bottom: 16px;\n}\n\n.at-kpi-card {\n flex: 1;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 10px;\n padding: 14px 16px;\n}\n\n.at-kpi-value {\n font-size: 1.4rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.at-kpi-error-value {\n color: var(--mj-status-error-text);\n}\n\n.at-kpi-label {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n.at-kpi-trend {\n font-size: 0.68rem;\n margin-top: 4px;\n display: flex;\n align-items: center;\n gap: 4px;\n color: var(--mj-text-muted);\n}\n\n.at-kpi-trend.up {\n color: var(--mj-status-success-text);\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 PIPELINE TAB \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-pipeline-layout {\n display: flex;\n gap: 16px;\n flex: 1;\n min-height: 0;\n}\n\n.at-pipeline-center {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 12px;\n min-width: 0;\n min-height: 0;\n}\n\n.at-pipeline-right {\n width: 320px;\n flex-shrink: 0;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n/* Pipeline Stages */\n\n.at-pipeline-stages {\n display: flex;\n gap: 0;\n align-items: center;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 10px;\n padding: 12px 16px;\n}\n\n.at-pipeline-stage {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 4px;\n}\n\n.at-pipeline-stage-icon {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n font-size: 0.85rem;\n}\n\n.stage-idle .at-pipeline-stage-icon {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n.stage-active .at-pipeline-stage-icon {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n animation: at-pulse 1.5s infinite;\n}\n\n.stage-complete .at-pipeline-stage-icon {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n}\n\n@keyframes at-pulse {\n 0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--mj-brand-primary) 30%, transparent); }\n 50% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--mj-brand-primary) 0%, transparent); }\n}\n\n.at-pipeline-stage-name {\n font-size: 0.68rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n}\n\n.at-pipeline-stage-count {\n font-size: 0.6rem;\n color: var(--mj-text-muted);\n}\n\n.at-pipeline-arrow {\n width: 24px;\n text-align: center;\n color: var(--mj-text-disabled);\n font-size: 0.6rem;\n}\n\n.at-stage-connector {\n width: 32px;\n height: 2px;\n background: var(--mj-border-default);\n transition: background 0.4s ease;\n}\n\n.at-stage-connector.connector-complete {\n background: var(--mj-status-success);\n}\n\n/* Progress section */\n\n.at-progress-section {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n padding: 10px 16px;\n}\n\n.at-progress-header {\n display: flex;\n justify-content: space-between;\n font-size: 0.75rem;\n margin-bottom: 4px;\n}\n\n.at-progress-stage-label {\n color: var(--mj-text-secondary);\n font-weight: 500;\n}\n\n.at-progress-pct {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.at-progress-bar {\n height: 4px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 2px;\n overflow: hidden;\n}\n\n.at-progress-fill {\n height: 100%;\n background: var(--mj-brand-primary);\n border-radius: 2px;\n transition: width 0.3s ease;\n}\n\n.at-progress-current {\n font-size: 0.68rem;\n color: var(--mj-text-muted);\n margin-top: 4px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.at-progress-fill-paused {\n background: var(--mj-status-warning);\n animation: pulse-paused 1.5s ease-in-out infinite;\n}\n\n@keyframes pulse-paused {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n}\n\n.at-pipeline-controls {\n display: flex;\n gap: 8px;\n margin-top: 8px;\n justify-content: flex-end;\n}\n\n.at-danger-btn {\n background: var(--mj-status-error);\n color: var(--mj-text-inverse);\n border-color: var(--mj-status-error);\n}\n\n.at-danger-btn:hover:not(:disabled) {\n background: var(--mj-status-error-text);\n border-color: var(--mj-status-error-text);\n}\n\n/* Feed items */\n\n.at-feed-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n border-bottom: 1px solid var(--mj-border-subtle);\n font-size: 0.78rem;\n}\n\n.at-feed-item:last-child {\n border-bottom: none;\n}\n\n.at-feed-status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.at-feed-status-dot.complete {\n background: var(--mj-status-success);\n}\n\n.at-feed-status-dot.processing {\n background: var(--mj-brand-primary);\n animation: at-pulse 1.5s infinite;\n}\n\n.at-feed-status-dot.error {\n background: var(--mj-status-error);\n}\n\n.at-feed-item-name {\n flex: 1;\n font-weight: 500;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--mj-text-primary);\n}\n\n.at-feed-item-source {\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n min-width: 100px;\n}\n\n.at-feed-item-tags {\n display: flex;\n gap: 4px;\n}\n\n.at-feed-tag {\n padding: 1px 6px;\n border-radius: 3px;\n font-size: 0.62rem;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.at-feed-item-time {\n color: var(--mj-text-muted);\n font-size: 0.68rem;\n white-space: nowrap;\n min-width: 60px;\n text-align: right;\n}\n\n/* Source mini cards (right panel) */\n\n.at-source-mini {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n border-bottom: 1px solid var(--mj-border-subtle);\n cursor: pointer;\n transition: background 0.1s ease;\n}\n\n.at-source-mini:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.at-source-mini:last-child {\n border-bottom: none;\n}\n\n.at-source-mini-icon {\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 0.72rem;\n flex-shrink: 0;\n}\n\n.at-source-mini-info {\n flex: 1;\n min-width: 0;\n}\n\n.at-source-mini-name {\n font-size: 0.78rem;\n font-weight: 600;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--mj-text-primary);\n}\n\n.at-source-mini-meta {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n}\n\n.at-source-mini-status {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.at-source-mini-status.active {\n background: var(--mj-status-success);\n}\n\n.at-source-mini-status.error {\n background: var(--mj-status-error);\n}\n\n.at-source-mini-status.inactive {\n background: var(--mj-text-disabled);\n}\n\n/* Tag cloud card */\n\n.at-tag-cloud-card {\n padding: 16px;\n}\n\n.at-tag-cloud {\n display: flex;\n flex-wrap: wrap;\n gap: 5px;\n}\n\n.at-tag-pill {\n padding: 4px 12px;\n border-radius: 14px;\n font-size: 0.72rem;\n font-weight: 500;\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n color: var(--mj-text-secondary);\n border: 1px solid var(--mj-border-subtle);\n cursor: pointer;\n transition: all 0.12s ease;\n}\n\n.at-tag-pill:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.at-tag-weighted {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n.at-tag-weight {\n font-size: 0.6rem;\n opacity: 0.6;\n font-weight: 400;\n}\n\n.at-tag-row-clickable {\n cursor: pointer;\n transition: background 0.1s ease;\n}\n\n.at-tag-row-clickable:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.at-tag-row-selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface)) !important;\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.at-tag-pill.large {\n font-size: 0.85rem;\n padding: 5px 14px;\n}\n\n.at-tag-pill.small {\n font-size: 0.65rem;\n padding: 3px 8px;\n color: var(--mj-text-muted);\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 SOURCES TAB \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-sources-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));\n gap: 12px;\n}\n\n.at-source-card-full {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 10px;\n padding: 16px;\n transition: border-color 0.15s ease;\n}\n\n.at-source-card-full:hover {\n border-color: var(--mj-border-default);\n}\n\n.at-source-card-header {\n display: flex;\n align-items: center;\n gap: 10px;\n margin-bottom: 12px;\n}\n\n.at-source-card-icon {\n width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 10px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 1rem;\n flex-shrink: 0;\n}\n\n.at-source-card-title {\n font-size: 0.9rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.at-source-card-type {\n font-size: 0.68rem;\n color: var(--mj-text-muted);\n}\n\n.at-source-card-status {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 5px;\n font-size: 0.7rem;\n font-weight: 500;\n}\n\n.at-source-card-status.active {\n color: var(--mj-status-success-text);\n}\n\n.at-source-card-status.error {\n color: var(--mj-status-error-text);\n}\n\n.at-source-card-status.inactive {\n color: var(--mj-text-disabled);\n}\n\n.at-source-card-url {\n font-size: 0.7rem;\n color: var(--mj-brand-primary);\n margin-bottom: 10px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.at-source-card-stats {\n display: flex;\n gap: 16px;\n padding-top: 10px;\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n.at-source-stat {\n display: flex;\n flex-direction: column;\n}\n\n.at-source-stat-value {\n font-size: 1rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.at-source-stat-label {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n}\n\n.at-source-card-actions {\n display: flex;\n gap: 6px;\n margin-top: 10px;\n}\n\n.at-source-action-btn {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: 6px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 0.7rem;\n cursor: pointer;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n transition: all 0.12s ease;\n}\n\n.at-source-action-btn:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.at-source-delete-btn:hover {\n border-color: var(--mj-status-error);\n color: var(--mj-status-error-text);\n}\n\n.at-add-source-card {\n background: none;\n border: 2px dashed var(--mj-border-default);\n border-radius: 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 40px;\n cursor: pointer;\n transition: all 0.15s ease;\n color: var(--mj-text-muted);\n min-height: 200px;\n}\n\n.at-add-source-card:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.at-add-source-card i {\n font-size: 1.5rem;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 CONTENT TYPES TAB \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-ct-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));\n gap: 12px;\n}\n\n.at-ct-card {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 10px;\n padding: 16px;\n}\n\n.at-ct-card-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 10px;\n}\n\n.at-ct-card-name {\n font-size: 0.9rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.at-ct-card-model {\n font-size: 0.68rem;\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.at-ct-field {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 0;\n border-bottom: 1px solid var(--mj-border-subtle);\n font-size: 0.78rem;\n}\n\n.at-ct-field:last-child {\n border-bottom: none;\n}\n\n.at-ct-field-label {\n color: var(--mj-text-muted);\n}\n\n.at-ct-field-value {\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.at-ct-tag-range {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: 8px;\n padding: 8px 10px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 6px;\n font-size: 0.75rem;\n color: var(--mj-text-primary);\n}\n\n.at-ct-tag-range i {\n color: var(--mj-brand-primary);\n}\n\n.at-ct-tag-range-bar {\n flex: 1;\n height: 6px;\n background: var(--mj-bg-surface);\n border-radius: 3px;\n position: relative;\n}\n\n.at-ct-tag-range-fill {\n position: absolute;\n height: 100%;\n background: var(--mj-brand-primary);\n border-radius: 3px;\n}\n\n.at-ct-range-suffix {\n color: var(--mj-text-muted);\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 TAG LIBRARY TAB \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-tag-lib-layout {\n display: flex;\n gap: 16px;\n}\n\n.at-tag-lib-main {\n flex: 1;\n}\n\n.at-tag-lib-sidebar {\n width: 280px;\n flex-shrink: 0;\n}\n\n.at-tag-table {\n width: 100%;\n border-collapse: collapse;\n}\n\n.at-tag-table th {\n text-align: left;\n padding: 8px 12px;\n font-size: 0.7rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--mj-text-muted);\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-elevated);\n position: sticky;\n top: 0;\n z-index: 1;\n}\n\n.at-tag-table td {\n padding: 10px 12px;\n font-size: 0.8rem;\n border-bottom: 1px solid var(--mj-border-subtle);\n color: var(--mj-text-primary);\n}\n\n.at-tag-table tr:hover td {\n background: var(--mj-bg-surface-hover);\n}\n\n.at-tag-name-cell {\n font-weight: 600;\n}\n\n.at-tag-bar {\n width: 80px;\n height: 6px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 3px;\n display: inline-block;\n vertical-align: middle;\n}\n\n.at-tag-bar-fill {\n height: 100%;\n background: var(--mj-brand-primary);\n border-radius: 3px;\n}\n\n/* Weight indicator in tag table */\n.at-weight-indicator {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.at-weight-bar {\n width: 50px;\n height: 6px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 3px;\n overflow: hidden;\n}\n\n.at-weight-bar-fill {\n height: 100%;\n border-radius: 3px;\n transition: width 0.2s ease;\n}\n\n.at-weight-bar-fill.at-weight-high {\n background: var(--mj-status-success);\n}\n\n.at-weight-bar-fill.at-weight-medium {\n background: var(--mj-status-warning);\n}\n\n.at-weight-bar-fill.at-weight-low {\n background: var(--mj-status-error);\n}\n\n.at-weight-value {\n font-size: 0.7rem;\n font-weight: 600;\n color: var(--mj-text-muted);\n min-width: 28px;\n}\n\n.at-tags-by-source {\n font-size: 0.78rem;\n}\n\n.at-tag-source-row {\n display: flex;\n justify-content: space-between;\n padding: 5px 0;\n border-bottom: 1px solid var(--mj-border-subtle);\n color: var(--mj-text-primary);\n}\n\n.at-tag-source-row:last-child {\n border-bottom: none;\n}\n\n.at-search-input {\n padding: 7px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 7px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n font-size: 0.8rem;\n width: 200px;\n outline: none;\n}\n\n.at-search-input::placeholder {\n color: var(--mj-text-disabled);\n}\n\n.at-search-input:focus {\n border-color: var(--mj-brand-primary);\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 RUN HISTORY TAB \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-run-table {\n width: 100%;\n border-collapse: collapse;\n}\n\n.at-run-table th {\n text-align: left;\n padding: 10px 14px;\n font-size: 0.7rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--mj-text-muted);\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-elevated);\n position: sticky;\n top: 0;\n z-index: 1;\n}\n\n.at-run-table td {\n padding: 12px 14px;\n font-size: 0.8rem;\n border-bottom: 1px solid var(--mj-border-subtle);\n color: var(--mj-text-primary);\n}\n\n.at-run-table tr:hover td {\n background: var(--mj-bg-surface-hover);\n cursor: pointer;\n}\n\n.at-run-status-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 10px;\n border-radius: 10px;\n font-size: 0.7rem;\n font-weight: 600;\n}\n\n.at-run-status-badge.complete {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n}\n\n.at-run-status-badge.failed {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n}\n\n.at-run-status-badge.running {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.at-run-duration {\n color: var(--mj-text-muted);\n font-size: 0.75rem;\n}\n\n.at-run-source-name {\n font-weight: 500;\n}\n\n.at-run-error-text {\n color: var(--mj-status-error-text);\n}\n\n.at-filter-select {\n padding: 7px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 7px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n font-size: 0.78rem;\n outline: none;\n}\n\n.at-filter-select:focus {\n border-color: var(--mj-brand-primary);\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 SLIDE-IN FORM PANEL \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-slide-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: var(--mj-bg-overlay);\n z-index: 1000;\n}\n\n.at-slide-panel {\n position: fixed;\n right: 0;\n top: 0;\n bottom: 0;\n width: 420px;\n max-width: 100vw;\n background: var(--mj-bg-surface);\n border-left: 1px solid var(--mj-border-default);\n z-index: 1001;\n display: flex;\n flex-direction: column;\n box-shadow: -4px 0 24px color-mix(in srgb, var(--mj-text-primary) 15%, transparent);\n}\n\n.at-slide-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.at-slide-header h3 {\n margin: 0;\n font-size: 1rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.at-slide-close {\n background: none;\n border: none;\n font-size: 1.1rem;\n color: var(--mj-text-muted);\n cursor: pointer;\n padding: 4px;\n}\n\n.at-slide-close:hover {\n color: var(--mj-text-primary);\n}\n\n.at-slide-body {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n}\n\n.at-form-group {\n margin-bottom: 16px;\n}\n\n.at-form-label {\n display: block;\n font-size: 0.78rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n margin-bottom: 6px;\n}\n\n.at-form-input,\n.at-form-select,\n.at-form-textarea {\n width: 100%;\n padding: 9px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 7px;\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-primary);\n font-size: 0.82rem;\n outline: none;\n font-family: inherit;\n}\n\n.at-form-input:focus,\n.at-form-select:focus,\n.at-form-textarea:focus {\n border-color: var(--mj-brand-primary);\n}\n\n.at-form-input::placeholder,\n.at-form-textarea::placeholder {\n color: var(--mj-text-disabled);\n}\n\n.at-form-textarea {\n resize: vertical;\n}\n\n.at-form-row {\n display: flex;\n gap: 12px;\n}\n\n.at-form-actions {\n display: flex;\n gap: 8px;\n margin-top: 24px;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 RESPONSIVE \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n@media (max-width: 1100px) {\n .at-pipeline-layout {\n flex-direction: column;\n }\n\n .at-pipeline-right {\n width: 100%;\n flex-direction: row;\n }\n\n .at-tag-lib-layout {\n flex-direction: column;\n }\n\n .at-tag-lib-sidebar {\n width: 100%;\n }\n}\n\n@media (max-width: 768px) {\n .at-left-nav {\n width: 180px;\n }\n\n .at-kpi-strip {\n flex-wrap: wrap;\n }\n\n .at-kpi-card {\n min-width: 140px;\n }\n\n .at-sources-grid {\n grid-template-columns: 1fr;\n }\n\n .at-ct-grid {\n grid-template-columns: 1fr;\n }\n\n .at-slide-panel {\n width: 100vw;\n }\n}\n\n@media (max-width: 480px) {\n .at-left-nav {\n width: 56px;\n }\n\n .at-left-nav-header h2 {\n font-size: 0;\n }\n\n .at-left-nav-header h2 i {\n font-size: 1rem;\n }\n\n .at-nav-item {\n justify-content: center;\n padding: 10px;\n font-size: 0;\n }\n\n .at-nav-item i {\n font-size: 1rem;\n }\n\n .at-nav-badge {\n display: none;\n }\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n Slide-in Form Panel\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-slide-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.35);\n z-index: 9999;\n animation: at-fade-in 0.2s ease;\n}\n\n@keyframes at-fade-in { from { opacity: 0; } to { opacity: 1; } }\n\n.at-slide-panel {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n width: 420px;\n background: var(--mj-bg-surface);\n border-left: 1px solid var(--mj-border-default);\n box-shadow: -8px 0 40px rgba(0, 0, 0, 0.4);\n z-index: 10000;\n display: flex;\n flex-direction: column;\n animation: at-slide-in 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n@keyframes at-slide-in { from { transform: translateX(100%); } to { transform: translateX(0); } }\n\n.at-slide-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.at-slide-header h3 {\n font-size: 1rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.at-slide-close {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n font-size: 0.85rem;\n transition: all 0.15s ease;\n}\n\n.at-slide-close:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.at-slide-body {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.at-form-group {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.at-form-label {\n font-size: 0.75rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n.at-form-input,\n.at-form-select,\n.at-form-textarea {\n padding: 9px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 7px;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n font-size: 0.85rem;\n outline: none;\n transition: border-color 0.15s ease;\n font-family: inherit;\n}\n\n.at-form-input:focus,\n.at-form-select:focus,\n.at-form-textarea:focus {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.at-form-select {\n appearance: none;\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2364748b' d='M3 5l3 3 3-3'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 10px center;\n padding-right: 30px;\n}\n\n.at-form-textarea {\n resize: vertical;\n min-height: 70px;\n}\n\n.at-form-row {\n display: flex;\n gap: 12px;\n}\n\n.at-form-actions {\n display: flex;\n gap: 8px;\n padding-top: 8px;\n border-top: 1px solid var(--mj-border-subtle);\n margin-top: 8px;\n}\n\n.at-form-actions .at-action-btn {\n flex: 1;\n justify-content: center;\n}\n\n/* Also add empty state for Content Types (matching Sources) */\n.at-add-type-card {\n background: none;\n border: 2px dashed var(--mj-border-default);\n border-radius: 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 40px;\n cursor: pointer;\n transition: all 0.15s ease;\n color: var(--mj-text-muted);\n min-height: 200px;\n}\n\n.at-add-type-card:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.at-add-type-card i {\n font-size: 1.5rem;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 FORKED PIPELINE STAGES \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-pipeline-stages-forked {\n display: flex;\n align-items: center;\n gap: 0;\n}\n\n.at-pipeline-fork {\n display: flex;\n align-items: center;\n gap: 0;\n flex: 2;\n}\n\n.at-pipeline-fork-lines {\n width: 28px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n position: relative;\n flex-shrink: 0;\n}\n\n.at-pipeline-fork-line {\n height: 2px;\n background: var(--mj-border-default);\n width: 100%;\n position: relative;\n}\n\n.at-pipeline-fork-line::before {\n content: '';\n position: absolute;\n width: 2px;\n height: 12px;\n background: var(--mj-border-default);\n left: 0;\n}\n\n.at-fork-top::before {\n bottom: 0;\n left: 0;\n}\n\n.at-fork-bottom::before {\n top: 0;\n left: 0;\n}\n\n.at-pipeline-fork-branches {\n display: flex;\n flex-direction: column;\n gap: 8px;\n flex: 1;\n}\n\n.at-pipeline-fork-branches .at-pipeline-stage {\n flex: none;\n padding: 6px 0;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 CLICKABLE FEED ITEMS \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-feed-item-clickable {\n cursor: pointer;\n}\n\n.at-feed-item-clickable:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 FEED SEARCH & PAGINATION \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-feed-card {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n\n.at-feed-header-actions {\n display: flex;\n align-items: center;\n gap: 10px;\n margin-left: auto;\n}\n\n.at-feed-sort-btn {\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 3px 8px;\n border: 1px solid var(--mj-border-default);\n border-radius: 5px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 0.68rem;\n cursor: pointer;\n transition: background 0.15s, color 0.15s;\n}\n\n.at-feed-sort-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.at-feed-count {\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n}\n\n.at-feed-search-bar {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n border-bottom: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface-card);\n}\n\n.at-feed-search-icon {\n color: var(--mj-text-disabled);\n font-size: 0.72rem;\n flex-shrink: 0;\n}\n\n.at-feed-search-input {\n flex: 1;\n border: none;\n background: transparent;\n font-size: 0.78rem;\n color: var(--mj-text-primary);\n outline: none;\n padding: 4px 0;\n min-width: 0;\n}\n\n.at-feed-search-input::placeholder {\n color: var(--mj-text-disabled);\n}\n\n.at-feed-search-clear {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: var(--mj-border-default);\n color: var(--mj-text-muted);\n cursor: pointer;\n font-size: 9px;\n flex-shrink: 0;\n}\n\n.at-feed-search-clear:hover {\n background: var(--mj-border-strong);\n color: var(--mj-text-primary);\n}\n\n.at-feed-scroll-body {\n overflow-y: auto;\n min-height: 0;\n flex: 1;\n}\n\n.at-feed-item-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n overflow: hidden;\n}\n\n.at-feed-item-content .at-feed-item-name {\n font-size: 0.82rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.at-feed-item-source-label {\n font-size: 0.7rem;\n font-weight: 500;\n color: var(--mj-brand-primary);\n}\n\n.at-feed-pagination {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 8px 14px;\n border-top: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface-card);\n}\n\n.at-feed-pagination-label {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 CLICKABLE SOURCE CARDS \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-source-card-clickable {\n cursor: pointer;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 FORM HINT \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-form-hint {\n font-size: 0.68rem;\n color: var(--mj-text-muted);\n font-style: italic;\n margin-top: 2px;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 DETAIL PANEL (wider) \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-detail-panel {\n width: 500px;\n}\n\n/* \u2500\u2500 Detail: Item header \u2500\u2500 */\n\n.at-detail-item-header {\n margin-bottom: 8px;\n}\n\n.at-detail-item-name {\n font-size: 1.05rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n margin: 0 0 6px 0;\n word-break: break-word;\n}\n\n.at-detail-badges {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.at-detail-badge {\n padding: 2px 10px;\n border-radius: 10px;\n font-size: 0.7rem;\n font-weight: 600;\n}\n\n.at-detail-badge-source {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.at-detail-badge-type {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n}\n\n.at-detail-badge-file {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n.at-detail-badge-status-active {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n}\n\n.at-detail-badge-status-error {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n}\n\n.at-detail-badge-status-inactive {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-disabled);\n}\n\n/* \u2500\u2500 Detail: Sections \u2500\u2500 */\n\n.at-detail-section {\n margin-bottom: 4px;\n}\n\n.at-detail-section-label {\n font-size: 0.72rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n color: var(--mj-text-muted);\n margin-bottom: 6px;\n}\n\n.at-detail-link {\n font-size: 0.8rem;\n color: var(--mj-brand-primary);\n text-decoration: none;\n word-break: break-all;\n}\n\n.at-detail-link:hover {\n text-decoration: underline;\n}\n\n/* \u2500\u2500 Detail: Text preview \u2500\u2500 */\n\n.at-detail-text-preview {\n max-height: 200px;\n overflow-y: auto;\n padding: 10px 12px;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 6px;\n font-size: 0.78rem;\n font-family: 'SF Mono', 'Cascadia Code', 'Menlo', monospace;\n color: var(--mj-text-primary);\n white-space: pre-wrap;\n word-break: break-word;\n line-height: 1.5;\n}\n\n/* \u2500\u2500 Detail: Tags \u2500\u2500 */\n\n.at-detail-tags {\n display: flex;\n flex-wrap: wrap;\n gap: 5px;\n}\n\n/* \u2500\u2500 Detail: Meta grid \u2500\u2500 */\n\n.at-detail-meta-grid {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n overflow: hidden;\n}\n\n.at-detail-meta-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n font-size: 0.78rem;\n}\n\n.at-detail-meta-row:last-child {\n border-bottom: none;\n}\n\n.at-detail-meta-key {\n color: var(--mj-text-muted);\n font-weight: 500;\n flex-shrink: 0;\n margin-right: 12px;\n}\n\n.at-detail-meta-value {\n color: var(--mj-text-primary);\n text-align: right;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.at-detail-meta-mono {\n font-family: 'SF Mono', 'Cascadia Code', 'Menlo', monospace;\n font-size: 0.72rem;\n}\n\n/* \u2500\u2500 Detail: Actions \u2500\u2500 */\n\n.at-detail-actions {\n display: flex;\n gap: 8px;\n padding-top: 8px;\n border-top: 1px solid var(--mj-border-subtle);\n margin-top: 8px;\n}\n\n.at-detail-actions .at-action-btn {\n flex: 1;\n justify-content: center;\n}\n\n/* \u2500\u2500 Detail: Source header \u2500\u2500 */\n\n.at-detail-source-header {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 8px;\n}\n\n/* \u2500\u2500 Detail: Stats strip \u2500\u2500 */\n\n.at-detail-stats-strip {\n display: flex;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.at-detail-stat {\n flex: 1;\n min-width: 60px;\n text-align: center;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n padding: 8px 6px;\n}\n\n.at-detail-stat-value {\n font-size: 1rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.at-detail-stat-label {\n font-size: 0.62rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n/* \u2500\u2500 Detail: Content Library \u2500\u2500 */\n\n.at-detail-content-list {\n max-height: 250px;\n overflow-y: auto;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n}\n\n.at-detail-content-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n font-size: 0.78rem;\n cursor: pointer;\n transition: background 0.1s ease;\n}\n\n.at-detail-content-item:last-child {\n border-bottom: none;\n}\n\n.at-detail-content-item:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.at-detail-content-item-name {\n flex: 1;\n font-weight: 500;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.at-detail-content-item-tags {\n font-size: 0.68rem;\n color: var(--mj-text-muted);\n white-space: nowrap;\n}\n\n.at-detail-content-item-time {\n font-size: 0.68rem;\n color: var(--mj-text-muted);\n white-space: nowrap;\n}\n\n/* \u2500\u2500 Detail: Run history \u2500\u2500 */\n\n.at-detail-run-history {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n max-height: 200px;\n overflow-y: auto;\n}\n\n.at-detail-run-row {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n font-size: 0.75rem;\n}\n\n.at-detail-run-row:last-child {\n border-bottom: none;\n}\n\n.at-detail-run-time {\n color: var(--mj-text-secondary);\n flex: 1;\n}\n\n.at-detail-run-duration {\n color: var(--mj-text-muted);\n}\n\n.at-detail-run-items {\n color: var(--mj-text-muted);\n}\n\n@media (max-width: 600px) {\n .at-slide-panel {\n width: 100%;\n }\n\n .at-detail-panel {\n width: 100%;\n }\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n TAXONOMY GOVERNANCE TAB\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n/* \u2500\u2500 Sub-tab strip \u2500\u2500 */\n\n.at-tax-tab-strip {\n display: flex;\n border-bottom: 2px solid var(--mj-border-default);\n padding: 0 20px;\n gap: 0;\n flex-shrink: 0;\n}\n\n.at-tax-tab {\n padding: 10px 20px;\n font-size: 0.78rem;\n font-weight: 500;\n color: var(--mj-text-muted);\n cursor: pointer;\n border-bottom: 2px solid transparent;\n margin-bottom: -2px;\n transition: all 0.15s;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.at-tax-tab:hover {\n color: var(--mj-text-secondary);\n}\n\n.at-tax-tab.active {\n color: var(--mj-brand-primary);\n border-bottom-color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.at-tax-tab-badge {\n font-size: 0.62rem;\n font-weight: 600;\n padding: 1px 7px;\n border-radius: 10px;\n}\n\n.at-tax-badge-warning {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.at-tax-badge-error {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n/* \u2500\u2500 Tree View: Split layout \u2500\u2500 */\n\n.at-tax-split-view {\n display: flex;\n gap: 0;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 10px;\n height: calc(100vh - 320px);\n min-height: 400px;\n overflow: hidden;\n}\n\n.at-tax-tree-panel {\n width: 40%;\n border-right: 1px solid var(--mj-border-default);\n display: flex;\n flex-direction: column;\n}\n\n.at-tax-tree-toolbar {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.at-tax-toolbar-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 30px;\n height: 30px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n font-size: 0.8rem;\n transition: background 0.15s, color 0.15s;\n}\n\n.at-tax-toolbar-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.at-tax-toolbar-btn.active {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.at-tax-tree-body {\n flex: 1;\n overflow-y: auto;\n padding: 8px 0;\n}\n\n.at-tax-tree-node {\n padding: 6px 16px;\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 0.78rem;\n cursor: pointer;\n transition: background 0.1s;\n line-height: 1.4;\n}\n\n.at-tax-tree-node:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.at-tax-node-selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n}\n\n.at-tax-node-drag-over {\n background: color-mix(in srgb, var(--mj-brand-primary) 20%, var(--mj-bg-surface));\n outline: 2px dashed var(--mj-brand-primary);\n outline-offset: -2px;\n}\n\n.at-tax-node-multi-selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n}\n\n.at-tax-tree-add-child {\n margin-left: auto;\n opacity: 0;\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n cursor: pointer;\n padding: 2px 6px;\n border-radius: 4px;\n transition: opacity 0.15s, color 0.15s, background 0.15s;\n}\n\n.at-tax-tree-node:hover .at-tax-tree-add-child {\n opacity: 1;\n}\n\n.at-tax-tree-add-child:hover {\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n.at-tax-tree-checkbox {\n width: 14px;\n height: 14px;\n cursor: pointer;\n accent-color: var(--mj-brand-primary);\n flex-shrink: 0;\n}\n\n.at-tax-tree-saving-overlay {\n position: absolute;\n inset: 0;\n background: color-mix(in srgb, var(--mj-bg-surface) 75%, transparent);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10;\n backdrop-filter: blur(1px);\n}\n\n.at-tax-create-context {\n font-size: 0.8rem;\n color: var(--mj-text-muted);\n margin-bottom: 12px;\n padding: 6px 10px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 6px;\n}\n\n.at-tax-tree-arrow {\n width: 16px;\n font-size: 0.55rem;\n color: var(--mj-text-muted);\n flex-shrink: 0;\n text-align: center;\n cursor: pointer;\n}\n\n.at-tax-arrow-collapsed::before {\n content: \"\\25B6\";\n}\n\n.at-tax-arrow-expanded::before {\n content: \"\\25BC\";\n}\n\n.at-tax-arrow-leaf {\n visibility: hidden;\n}\n\n.at-tax-health-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.at-tax-health-dot.green {\n background: var(--mj-status-success);\n}\n\n.at-tax-health-dot.yellow {\n background: var(--mj-status-warning);\n}\n\n.at-tax-health-dot.red {\n background: var(--mj-status-error);\n}\n\n.at-tax-tree-label {\n flex: 1;\n color: var(--mj-text-primary);\n}\n\n.at-tax-tree-label-selected {\n font-weight: 700;\n}\n\n.at-tax-tree-count {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n margin-left: 4px;\n}\n\n/* \u2500\u2500 Details panel \u2500\u2500 */\n\n.at-tax-details-panel {\n width: 60%;\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n}\n\n.at-tax-details-header {\n padding: 20px 24px 0;\n}\n\n.at-tax-breadcrumb {\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n margin-bottom: 8px;\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.at-tax-bc-link {\n color: var(--mj-brand-primary);\n cursor: pointer;\n}\n\n.at-tax-bc-link:hover {\n text-decoration: underline;\n}\n\n.at-tax-bc-sep {\n color: var(--mj-border-default);\n}\n\n.at-tax-bc-current {\n color: var(--mj-text-secondary);\n font-weight: 600;\n}\n\n.at-tax-details-title {\n font-size: 1.2rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 4px;\n}\n\n.at-tax-edit-icon {\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n cursor: pointer;\n}\n\n.at-tax-edit-icon:hover {\n color: var(--mj-brand-primary);\n}\n\n.at-tax-details-desc {\n font-size: 0.78rem;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n margin-bottom: 16px;\n padding: 8px 12px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 6px;\n border: 1px solid var(--mj-border-subtle);\n}\n\n.at-tax-edit-form {\n margin-bottom: 16px;\n}\n\n/* \u2500\u2500 Stats row \u2500\u2500 */\n\n.at-tax-stats-row {\n display: flex;\n gap: 12px;\n flex-wrap: wrap;\n margin-bottom: 16px;\n padding: 0 24px;\n}\n\n.at-tax-stat-item {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 10px 14px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 8px;\n min-width: 72px;\n}\n\n.at-tax-stat-value {\n font-size: 1.1rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.at-tax-stat-date {\n font-size: 0.8rem;\n}\n\n.at-tax-stat-label {\n font-size: 0.62rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n/* \u2500\u2500 Action toolbar \u2500\u2500 */\n\n.at-tax-action-toolbar {\n display: flex;\n gap: 8px;\n padding: 0 24px;\n margin-bottom: 20px;\n flex-wrap: wrap;\n}\n\n.at-tax-action-btn {\n padding: 6px 14px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n font-size: 0.72rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 5px;\n transition: all 0.15s;\n}\n\n.at-tax-action-btn:hover {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.at-tax-action-danger:hover {\n border-color: var(--mj-status-error);\n color: var(--mj-status-error);\n background: color-mix(in srgb, var(--mj-status-error) 8%, var(--mj-bg-surface));\n}\n\n/* \u2500\u2500 Detail sections \u2500\u2500 */\n\n.at-tax-detail-section {\n padding: 0 24px 20px;\n}\n\n.at-tax-section-title {\n font-size: 0.7rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mj-text-muted);\n margin-bottom: 10px;\n}\n\n.at-tax-child-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.at-tax-child-chip {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 10px;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-default);\n border-radius: 16px;\n font-size: 0.72rem;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.at-tax-child-chip:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\n.at-tax-chip-count {\n font-size: 0.6rem;\n background: var(--mj-border-default);\n color: var(--mj-text-muted);\n padding: 0 5px;\n border-radius: 8px;\n}\n\n/* \u2500\u2500 Recent items \u2500\u2500 */\n\n.at-tax-recent-list {\n list-style: none;\n}\n\n.at-tax-recent-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 0;\n border-bottom: 1px solid var(--mj-border-subtle);\n font-size: 0.78rem;\n}\n\n.at-tax-recent-item:last-child {\n border-bottom: none;\n}\n\n.at-tax-recent-icon {\n width: 28px;\n height: 28px;\n border-radius: 6px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 0.72rem;\n flex-shrink: 0;\n}\n\n.at-tax-recent-name {\n flex: 1;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.at-tax-recent-weight {\n font-size: 0.68rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface-sunken);\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.at-tax-recent-date {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n}\n\n/* \u2500\u2500 Health bar \u2500\u2500 */\n\n.at-tax-health-bar {\n display: flex;\n align-items: center;\n gap: 20px;\n padding: 12px 20px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n margin-top: 16px;\n flex-wrap: wrap;\n}\n\n.at-tax-health-label {\n font-size: 0.72rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.at-tax-health-stat {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 0.78rem;\n}\n\n.at-tax-dot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n}\n\n.at-tax-dot-total {\n background: var(--mj-text-secondary);\n}\n\n.at-tax-dot-healthy {\n background: var(--mj-status-success);\n}\n\n.at-tax-dot-attention {\n background: var(--mj-status-warning);\n}\n\n.at-tax-dot-orphaned {\n background: var(--mj-status-error);\n}\n\n.at-tax-dot-duplicates {\n background: var(--mj-status-info);\n}\n\n.at-tax-health-value {\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.at-tax-val-success {\n color: var(--mj-status-success);\n}\n\n.at-tax-val-warning {\n color: var(--mj-status-warning);\n}\n\n.at-tax-val-error {\n color: var(--mj-status-error);\n}\n\n.at-tax-val-info {\n color: var(--mj-status-info);\n}\n\n.at-tax-health-text {\n color: var(--mj-text-muted);\n}\n\n/* \u2550\u2550\u2550\u2550 Duplicates sub-tab \u2550\u2550\u2550\u2550 */\n\n.at-tax-dup-stats-bar {\n display: flex;\n gap: 24px;\n margin-bottom: 16px;\n align-items: center;\n}\n\n.at-tax-dup-stat {\n font-size: 0.78rem;\n color: var(--mj-text-secondary);\n}\n\n.at-tax-dup-stat strong {\n font-size: 1.1rem;\n color: var(--mj-text-primary);\n margin-right: 4px;\n}\n\n.at-tax-dup-high strong {\n color: var(--mj-status-error);\n}\n\n.at-tax-dup-moderate strong {\n color: var(--mj-status-warning);\n}\n\n.at-tax-dup-list {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.at-tax-dup-card {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 14px 20px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n transition: border-color 0.15s;\n}\n\n.at-tax-dup-card:hover {\n border-color: var(--mj-brand-primary);\n}\n\n.at-tax-dup-card.at-tax-dup-high {\n border-left: 3px solid var(--mj-status-error);\n}\n\n.at-tax-dup-card.at-tax-dup-moderate {\n border-left: 3px solid var(--mj-status-warning);\n}\n\n.at-tax-dup-tag {\n font-size: 0.82rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n min-width: 120px;\n padding: 6px 12px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 6px;\n text-align: center;\n}\n\n.at-tax-dup-arrow {\n font-size: 0.82rem;\n color: var(--mj-text-muted);\n flex-shrink: 0;\n}\n\n.at-tax-dup-similarity {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 4px;\n min-width: 100px;\n}\n\n.at-tax-sim-bar-bg {\n width: 100%;\n height: 6px;\n background: var(--mj-border-default);\n border-radius: 3px;\n overflow: hidden;\n}\n\n.at-tax-sim-bar-fill {\n height: 100%;\n border-radius: 3px;\n transition: width 0.3s;\n}\n\n.at-tax-sim-bar-fill.high {\n background: var(--mj-status-error);\n}\n\n.at-tax-sim-bar-fill.moderate {\n background: var(--mj-status-warning);\n}\n\n.at-tax-sim-percent {\n font-size: 0.78rem;\n font-weight: 700;\n}\n\n.at-tax-sim-percent.high {\n color: var(--mj-status-error);\n}\n\n.at-tax-sim-percent.moderate {\n color: var(--mj-status-warning);\n}\n\n.at-tax-dup-actions {\n display: flex;\n gap: 6px;\n flex-shrink: 0;\n}\n\n.at-tax-dup-btn {\n padding: 5px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n font-size: 0.68rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.15s;\n white-space: nowrap;\n}\n\n.at-tax-dup-btn:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.at-tax-dup-btn-primary {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-color: var(--mj-brand-primary);\n}\n\n.at-tax-dup-btn-primary:hover {\n background: var(--mj-brand-primary-hover);\n color: var(--mj-text-inverse);\n}\n\n/* \u2550\u2550\u2550\u2550 Orphans sub-tab \u2550\u2550\u2550\u2550 */\n\n.at-tax-orphan-toolbar {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 16px;\n flex-wrap: wrap;\n}\n\n.at-tax-orphan-count {\n font-size: 0.82rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.at-tax-orphan-desc {\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n}\n\n.at-tax-orphan-bulk {\n margin-left: auto;\n display: flex;\n gap: 8px;\n}\n\n.at-tax-bulk-btn {\n padding: 6px 14px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n font-size: 0.72rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 5px;\n transition: all 0.15s;\n}\n\n.at-tax-bulk-btn:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.at-tax-bulk-danger:hover {\n border-color: var(--mj-status-error);\n color: var(--mj-status-error);\n}\n\n.at-tax-orphan-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));\n gap: 12px;\n}\n\n.at-tax-orphan-card {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n padding: 16px;\n transition: all 0.15s;\n display: flex;\n flex-direction: column;\n gap: 10px;\n border-top: 3px solid var(--mj-status-error);\n}\n\n.at-tax-orphan-card:hover {\n border-color: var(--mj-brand-primary);\n}\n\n.at-tax-orphan-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n\n.at-tax-orphan-name {\n font-size: 0.82rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.at-tax-orphan-checkbox {\n width: 16px;\n height: 16px;\n accent-color: var(--mj-brand-primary);\n}\n\n.at-tax-orphan-stats {\n display: flex;\n gap: 12px;\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n}\n\n.at-tax-orphan-stats strong {\n color: var(--mj-text-secondary);\n}\n\n.at-tax-orphan-dates {\n display: flex;\n justify-content: space-between;\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n}\n\n.at-tax-orphan-actions {\n display: flex;\n gap: 6px;\n margin-top: 4px;\n}\n\n.at-tax-orphan-btn {\n flex: 1;\n padding: 5px 8px;\n border: 1px solid var(--mj-border-default);\n border-radius: 5px;\n background: var(--mj-bg-surface);\n font-size: 0.68rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n text-align: center;\n transition: all 0.15s;\n}\n\n.at-tax-orphan-btn:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.at-tax-orphan-delete:hover {\n border-color: var(--mj-status-error);\n color: var(--mj-status-error);\n}\n\n/* \u2550\u2550\u2550\u2550 Treemap sub-tab \u2550\u2550\u2550\u2550 */\n\n.at-tax-treemap-kpi-strip {\n display: flex;\n gap: 20px;\n margin-bottom: 16px;\n flex-wrap: wrap;\n}\n\n.at-tax-treemap-kpi {\n padding: 10px 18px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n display: flex;\n flex-direction: column;\n align-items: center;\n min-width: 120px;\n}\n\n.at-tax-treemap-kpi-value {\n font-size: 1.2rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.at-tax-treemap-kpi-label {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n.at-tax-treemap-container {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 10px;\n padding: 8px;\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));\n grid-auto-rows: 80px;\n gap: 4px;\n min-height: 340px;\n}\n\n.at-tax-treemap-cell {\n border-radius: 6px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-inverse);\n font-weight: 600;\n font-size: 0.78rem;\n cursor: pointer;\n transition: all 0.15s;\n position: relative;\n overflow: hidden;\n}\n\n.at-tax-treemap-cell:hover {\n transform: scale(1.02);\n z-index: 2;\n}\n\n.at-tax-cell-name {\n font-size: 0.78rem;\n}\n\n.at-tax-cell-count {\n font-size: 0.65rem;\n opacity: 0.85;\n font-weight: 400;\n}\n\n/* Treemap color families */\n.at-tm-blue-1 { background: color-mix(in srgb, var(--mj-brand-primary) 90%, black); }\n.at-tm-blue-2 { background: var(--mj-brand-primary); }\n.at-tm-blue-3 { background: color-mix(in srgb, var(--mj-brand-primary) 75%, white); }\n.at-tm-blue-4 { background: color-mix(in srgb, var(--mj-brand-primary) 55%, white); }\n\n.at-tm-green-1 { background: color-mix(in srgb, var(--mj-status-success) 90%, black); }\n.at-tm-green-2 { background: var(--mj-status-success); }\n.at-tm-green-3 { background: color-mix(in srgb, var(--mj-status-success) 60%, white); color: var(--mj-text-primary); }\n\n.at-tm-purple-1 { background: color-mix(in srgb, var(--mj-status-info) 90%, black); }\n.at-tm-purple-2 { background: var(--mj-status-info); }\n.at-tm-purple-3 { background: color-mix(in srgb, var(--mj-status-info) 65%, white); }\n\n.at-tm-orange-1 { background: color-mix(in srgb, var(--mj-status-warning) 90%, black); }\n.at-tm-orange-2 { background: var(--mj-status-warning); }\n.at-tm-orange-3 { background: color-mix(in srgb, var(--mj-status-warning) 60%, white); color: var(--mj-text-primary); }\n\n/* \u2550\u2550\u2550\u2550 Audit Log sub-tab \u2550\u2550\u2550\u2550 */\n\n.at-tax-audit-filters {\n display: flex;\n align-items: center;\n gap: 16px;\n margin-bottom: 16px;\n padding: 12px 16px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n flex-wrap: wrap;\n}\n\n.at-tax-audit-filter-label {\n font-size: 0.72rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.at-tax-audit-checkbox-group {\n display: flex;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.at-tax-audit-checkbox {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 0.72rem;\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n\n.at-tax-audit-checkbox input {\n accent-color: var(--mj-brand-primary);\n}\n\n.at-tax-audit-timeline {\n position: relative;\n padding-left: 32px;\n}\n\n.at-tax-audit-timeline::before {\n content: '';\n position: absolute;\n left: 14px;\n top: 0;\n bottom: 0;\n width: 2px;\n background: var(--mj-border-default);\n}\n\n.at-tax-audit-event {\n position: relative;\n padding: 10px 16px;\n margin-bottom: 4px;\n display: flex;\n align-items: flex-start;\n gap: 12px;\n border-radius: 8px;\n transition: background 0.1s;\n}\n\n.at-tax-audit-event:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.at-tax-audit-event-icon {\n width: 28px;\n height: 28px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 0.68rem;\n flex-shrink: 0;\n position: absolute;\n left: -32px;\n top: 10px;\n z-index: 1;\n}\n\n.at-tax-audit-event-icon.created {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.at-tax-audit-event-icon.merged {\n background: color-mix(in srgb, var(--mj-status-info) 15%, var(--mj-bg-surface));\n color: var(--mj-status-info);\n}\n\n.at-tax-audit-event-icon.moved {\n background: color-mix(in srgb, var(--mj-status-info) 15%, var(--mj-bg-surface));\n color: var(--mj-status-info);\n}\n\n.at-tax-audit-event-icon.deleted {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.at-tax-audit-event-icon.renamed {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.at-tax-audit-event-body {\n flex: 1;\n}\n\n.at-tax-audit-event-desc {\n font-size: 0.78rem;\n color: var(--mj-text-primary);\n line-height: 1.5;\n}\n\n.at-tax-tag-ref {\n background: var(--mj-bg-surface-sunken);\n padding: 1px 6px;\n border-radius: 4px;\n font-weight: 600;\n font-size: 0.72rem;\n border: 1px solid var(--mj-border-default);\n}\n\n.at-tax-audit-event-meta {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n display: flex;\n gap: 12px;\n}\n\n/* \u2500\u2500 Taxonomy responsive \u2500\u2500 */\n\n@media (max-width: 1100px) {\n .at-tax-split-view {\n flex-direction: column;\n }\n\n .at-tax-tree-panel {\n width: 100%;\n max-height: 300px;\n border-right: none;\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n .at-tax-details-panel {\n width: 100%;\n }\n}\n\n@media (max-width: 768px) {\n .at-tax-dup-card {\n flex-wrap: wrap;\n }\n\n .at-tax-orphan-grid {\n grid-template-columns: 1fr;\n }\n}\n\n/* \u2500\u2500 Pipeline Config Widget \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.at-config-card {\n margin-top: 12px;\n}\n\n.at-config-body {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 12px !important;\n}\n\n.at-config-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n\n.at-config-label {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n min-width: 90px;\n flex-shrink: 0;\n}\n\n.at-config-control {\n display: flex;\n align-items: center;\n gap: 6px;\n flex: 1;\n justify-content: flex-end;\n}\n\n.at-config-input {\n width: 70px;\n padding: 3px 6px;\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n font-size: 0.75rem;\n text-align: right;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n}\n\n.at-config-input:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n}\n\n.at-config-slider {\n flex: 1;\n max-width: 100px;\n accent-color: var(--mj-brand-primary);\n height: 4px;\n}\n\n.at-config-value {\n font-size: 0.68rem;\n color: var(--mj-text-muted);\n min-width: 40px;\n text-align: right;\n}\n\n.at-config-divider {\n height: 1px;\n background: var(--mj-border-default);\n margin: 4px 0;\n}\n\n.at-config-section-label {\n font-size: 0.68rem;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n/* Toggle Switch */\n.at-config-toggle {\n position: relative;\n display: inline-block;\n cursor: pointer;\n}\n\n.at-config-toggle input {\n display: none;\n}\n\n.at-toggle-track {\n display: block;\n width: 32px;\n height: 18px;\n background: var(--mj-border-strong);\n border-radius: 9px;\n transition: background 0.2s ease;\n position: relative;\n}\n\n.at-config-toggle input:checked + .at-toggle-track {\n background: var(--mj-brand-primary);\n}\n\n.at-toggle-thumb {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 14px;\n height: 14px;\n background: var(--mj-bg-surface);\n border-radius: 50%;\n transition: transform 0.2s ease;\n box-shadow: 0 1px 3px rgba(0,0,0,0.15);\n}\n\n.at-config-toggle input:checked + .at-toggle-track .at-toggle-thumb {\n transform: translateX(14px);\n}\n\n/* \u2500\u2500 Schedule Indicator on Source Cards \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.at-schedule-indicator {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 3px 10px;\n margin-top: 6px;\n border-radius: 12px;\n font-size: 0.68rem;\n font-weight: 600;\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 20%, var(--mj-border-default));\n cursor: pointer;\n transition: all 0.15s ease;\n width: fit-content;\n}\n\n.at-schedule-indicator i {\n font-size: 0.62rem;\n}\n\n.at-schedule-indicator:hover {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n border-color: color-mix(in srgb, var(--mj-status-error) 30%, var(--mj-border-default));\n}\n\n/* Schedule button in source card actions */\n.at-source-schedule-btn:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n/* \u2500\u2500 Schedule Dialog \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.at-schedule-overlay {\n position: fixed;\n inset: 0;\n z-index: 1000;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-bg-overlay, rgba(0, 0, 0, 0.4));\n}\n\n.at-schedule-dialog {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg, 12px);\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.16);\n width: 420px;\n max-width: 90vw;\n}\n\n.at-schedule-dialog-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.at-schedule-dialog-header h3 {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0;\n font-size: 15px;\n font-weight: 650;\n color: var(--mj-text-primary);\n}\n\n.at-schedule-dialog-header h3 i {\n color: var(--mj-brand-primary);\n}\n\n.at-schedule-dialog-close {\n background: none;\n border: none;\n cursor: pointer;\n color: var(--mj-text-muted);\n font-size: 16px;\n padding: 4px;\n line-height: 1;\n transition: color 0.15s;\n}\n\n.at-schedule-dialog-close:hover {\n color: var(--mj-text-primary);\n}\n\n.at-schedule-dialog-body {\n padding: 20px;\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.at-schedule-field label {\n display: block;\n font-size: 11.5px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n margin-bottom: 6px;\n}\n\n.at-schedule-source-name {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13.5px;\n font-weight: 600;\n color: var(--mj-text-primary);\n padding: 8px 12px;\n background: var(--mj-bg-surface-card);\n border-radius: var(--mj-radius-sm, 6px);\n border: 1px solid var(--mj-border-subtle);\n}\n\n.at-schedule-source-name i {\n color: var(--mj-text-muted);\n}\n\n.at-schedule-action-name {\n font-size: 13px;\n color: var(--mj-text-secondary);\n padding: 8px 12px;\n background: var(--mj-bg-surface-card);\n border-radius: var(--mj-radius-sm, 6px);\n border: 1px solid var(--mj-border-subtle);\n}\n\n.at-schedule-cron-input {\n width: 100%;\n font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;\n font-size: 13px;\n letter-spacing: 0.02em;\n}\n\n.at-schedule-cron-preview {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 6px;\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n\n.at-schedule-cron-preview i {\n font-size: 11px;\n color: var(--mj-brand-primary);\n}\n\n.at-schedule-toggle-row {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 10px;\n}\n\n.at-schedule-toggle-row label {\n margin-bottom: 0;\n}\n\n.at-schedule-dialog-footer {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 14px 20px;\n border-top: 1px solid var(--mj-border-default);\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 D4: Status Badges for Content Items \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-status-badge {\n display: inline-block;\n padding: 1px 6px;\n border-radius: 4px;\n font-size: 0.6rem;\n font-weight: 600;\n white-space: nowrap;\n letter-spacing: 0.02em;\n}\n\n.at-status-badge-complete {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success-text);\n}\n\n.at-status-badge-processing {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning-text);\n}\n\n.at-status-badge-failed {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error-text);\n}\n\n.at-status-badge-pending {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 D4/D7: Source Detail Controls \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-detail-section-header-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 8px;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.at-detail-section-controls {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.at-detail-filter-select {\n padding: 4px 8px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 0.72rem;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n cursor: pointer;\n}\n\n.at-retry-btn {\n font-size: 0.7rem !important;\n padding: 4px 8px !important;\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface)) !important;\n color: var(--mj-status-error-text) !important;\n border: 1px solid color-mix(in srgb, var(--mj-status-error) 20%, var(--mj-border-default)) !important;\n}\n\n.at-retry-btn:hover {\n background: color-mix(in srgb, var(--mj-status-error) 18%, var(--mj-bg-surface)) !important;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 D7: Pagination \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-detail-pagination {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 10px 0 4px;\n border-top: 1px solid var(--mj-border-subtle);\n margin-top: 4px;\n}\n\n.at-page-btn {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 10px;\n font-size: 0.72rem;\n font-weight: 500;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.12s ease;\n}\n\n.at-page-btn:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.at-page-btn:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n.at-page-info {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n font-weight: 500;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 Confirmation Dialog \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-confirm-message {\n font-size: 13.5px;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n margin: 0;\n}\n\n.at-danger-btn {\n background: var(--mj-status-error);\n color: var(--mj-text-inverse);\n border: none;\n border-radius: var(--mj-radius-sm, 6px);\n padding: 7px 14px;\n font-size: 12.5px;\n font-weight: 600;\n cursor: pointer;\n transition: background 0.15s;\n}\n\n.at-danger-btn:hover {\n background: var(--mj-status-error-text);\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 Treemap Drill-In \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-tax-treemap-cell-clickable {\n cursor: pointer;\n transition: transform 0.15s, box-shadow 0.15s;\n}\n\n.at-tax-treemap-cell-clickable:hover {\n transform: scale(1.02);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);\n z-index: 1;\n}\n\n.at-tax-drillin-overlay {\n position: fixed;\n inset: 0;\n z-index: 1000;\n display: flex;\n align-items: flex-start;\n justify-content: flex-end;\n background: var(--mj-bg-overlay, rgba(0, 0, 0, 0.4));\n}\n\n.at-tax-drillin-panel {\n background: var(--mj-bg-surface);\n border-left: 1px solid var(--mj-border-default);\n width: 420px;\n max-width: 90vw;\n height: 100vh;\n display: flex;\n flex-direction: column;\n box-shadow: -4px 0 24px rgba(0, 0, 0, 0.12);\n}\n\n.at-tax-drillin-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.at-tax-drillin-header h3 {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0;\n font-size: 15px;\n font-weight: 650;\n color: var(--mj-text-primary);\n}\n\n.at-tax-drillin-header h3 i {\n color: var(--mj-brand-primary);\n}\n\n.at-tax-drillin-body {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n}\n\n.at-tax-drillin-footer {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 14px 20px;\n border-top: 1px solid var(--mj-border-default);\n}\n\n/* D3/D8: Clickable run history rows */\n.at-run-row-clickable {\n cursor: pointer;\n transition: background 0.12s ease;\n}\n\n.at-run-row-clickable:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.at-run-row-selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface)) !important;\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n/* D2: Error text in detail tables */\n.run-error-text {\n color: var(--mj-status-error);\n font-weight: 600;\n}\n\n/* \u2500\u2500 Content Item Duplicates Section (G3) \u2500\u2500 */\n\n.at-dedup-section {\n margin-top: 24px;\n border-top: 1px solid var(--mj-border-default);\n padding-top: 20px;\n}\n\n.at-dedup-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n margin-bottom: 16px;\n}\n\n.at-dedup-title {\n margin: 0;\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.at-dedup-subtitle {\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n}\n\n.at-dedup-list {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.at-dedup-card {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 14px 16px;\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n background: var(--mj-bg-surface);\n transition: border-color 0.15s;\n}\n\n.at-dedup-card:hover {\n border-color: var(--mj-border-strong);\n}\n\n.at-dedup-pair {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n min-width: 0;\n}\n\n.at-dedup-item {\n display: flex;\n flex-direction: column;\n min-width: 0;\n flex: 1;\n}\n\n.at-dedup-item-name {\n font-size: 0.85rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.at-dedup-item-source {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n}\n\n.at-dedup-vs {\n color: var(--mj-text-muted);\n font-size: 0.75rem;\n flex-shrink: 0;\n}\n\n.at-dedup-meta {\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 3px;\n flex-shrink: 0;\n}\n\n.at-dedup-score {\n font-size: 0.78rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.at-dedup-method {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n.at-dedup-actions {\n display: flex;\n gap: 6px;\n flex-shrink: 0;\n}\n\n.at-dedup-confirm {\n font-size: 0.75rem !important;\n padding: 4px 10px !important;\n}\n\n.at-dedup-dismiss {\n font-size: 0.75rem !important;\n padding: 4px 10px !important;\n}\n\n.at-dedup-empty {\n text-align: center;\n padding: 32px 16px;\n color: var(--mj-text-muted);\n}\n\n.at-dedup-empty i {\n font-size: 1.5rem;\n margin-bottom: 8px;\n color: var(--mj-status-success);\n}\n\n.at-dedup-empty p {\n margin: 8px 0 0;\n font-size: 0.82rem;\n}\n"] }]
|
|
7958
|
+
args: [{ standalone: false, selector: 'app-autotagging-pipeline-resource', encapsulation: ViewEncapsulation.None, template: "<!-- Content Classification Dashboard -->\n<div class=\"at-dashboard\">\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 LEFT NAV \u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div class=\"at-left-nav\">\n <div class=\"at-left-nav-header\">\n <h2><i class=\"fa-solid fa-tags\"></i> Classify</h2>\n </div>\n <div class=\"at-left-nav-items\">\n @for (item of NavItems; track item.Tab) {\n <div class=\"at-nav-item\" [class.active]=\"ActiveTab === item.Tab\" (click)=\"SwitchTab(item.Tab)\">\n <i [class]=\"item.Icon\"></i> {{ item.Label }}\n @if (item.BadgeText) {\n <span class=\"at-nav-badge\" [class.at-nav-badge-live]=\"item.BadgeClass === 'nav-badge-live'\">{{ item.BadgeText }}</span>\n }\n </div>\n }\n <div class=\"at-nav-divider\"></div>\n <div class=\"at-nav-item\" [class.active]=\"ActiveTab === 'history'\" (click)=\"SwitchTab('history')\">\n <i class=\"fa-solid fa-clock-rotate-left\"></i> Run History\n </div>\n </div>\n <div class=\"at-left-nav-footer\">\n <button class=\"at-run-pipeline-btn\" (click)=\"RunPipeline()\" [disabled]=\"IsRunning\">\n @if (IsRunning) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Running...\n } @else {\n <i class=\"fa-solid fa-play\"></i> Run Pipeline\n }\n </button>\n </div>\n </div>\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 MAIN AREA \u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div class=\"at-main-area\">\n\n @if (IsLoading) {\n <div class=\"at-loading-overlay\">\n <mj-loading text=\"Loading autotagging data...\"></mj-loading>\n </div>\n }\n\n @if (!IsLoading) {\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <!-- TAB 1: PIPELINE MONITOR -->\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (ActiveTab === 'pipeline') {\n <div class=\"at-page-header\">\n <div>\n <div class=\"at-page-title\">Pipeline Monitor</div>\n <div class=\"at-page-subtitle\">Real-time processing status and recent activity</div>\n </div>\n <div class=\"at-page-actions\">\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"LoadPipelineData()\">\n <i class=\"fa-solid fa-arrows-rotate\"></i> Refresh\n </button>\n </div>\n </div>\n <div class=\"at-page-body\">\n <!-- KPIs -->\n <div class=\"at-kpi-strip\">\n @for (kpi of KPIMetrics; track kpi.Label) {\n <div class=\"at-kpi-card\">\n <div class=\"at-kpi-value\" [class.at-kpi-error-value]=\"kpi.Label === 'Errors' && kpi.Value > 0\">{{ kpi.Value | number }}</div>\n <div class=\"at-kpi-label\">{{ kpi.Label }}</div>\n @if (kpi.Trend) {\n <div class=\"at-kpi-trend\" [class.up]=\"kpi.TrendUp\">\n @if (kpi.TrendUp) { <i class=\"fa-solid fa-arrow-up\"></i> }\n {{ kpi.Trend }}\n </div>\n }\n </div>\n }\n </div>\n\n <div class=\"at-pipeline-layout\">\n <div class=\"at-pipeline-center\">\n <!-- Pipeline stages visualization (only during active run) -->\n @if (PipelineStages.length > 0 && (IsRunning || IsPaused)) {\n <div class=\"at-pipeline-stages\">\n @for (stage of PipelineStages; track stage.Name; let last = $last) {\n <div class=\"at-pipeline-stage\"\n [class.stage-idle]=\"stage.Status === 'idle'\"\n [class.stage-active]=\"stage.Status === 'active'\"\n [class.stage-complete]=\"stage.Status === 'complete'\">\n <div class=\"at-pipeline-stage-icon\">\n @if (stage.Status === 'active') {\n <i class=\"fa-solid fa-spinner fa-spin\"></i>\n } @else if (stage.Status === 'complete') {\n <i class=\"fa-solid fa-check\"></i>\n } @else {\n <i [class]=\"stage.Icon\"></i>\n }\n </div>\n <div class=\"at-pipeline-stage-name\">{{ stage.Name }}</div>\n </div>\n @if (!last) {\n <div class=\"at-stage-connector\"\n [class.connector-complete]=\"stage.Status === 'complete'\">\n </div>\n }\n }\n </div>\n }\n\n <!-- Progress bar (visible during run or paused) -->\n @if (IsRunning || IsPaused) {\n <div class=\"at-progress-section\">\n <div class=\"at-progress-header\">\n <span class=\"at-progress-stage-label\">{{ RunStage }}</span>\n <span class=\"at-progress-pct\">{{ RunProgress | number:'1.0-0' }}%</span>\n </div>\n <div class=\"at-progress-bar\"><div class=\"at-progress-fill\" [style.width.%]=\"RunProgress\" [class.at-progress-fill-paused]=\"IsPaused\"></div></div>\n @if (RunCurrentItem) {\n <div class=\"at-progress-current\">{{ RunCurrentItem }}</div>\n }\n <div class=\"at-pipeline-controls\">\n @if (IsRunning && !IsPaused) {\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"PausePipeline()\" [disabled]=\"!CurrentProcessRunID\">\n <i class=\"fa-solid fa-pause\"></i> Pause\n </button>\n }\n @if (IsPaused) {\n <button class=\"at-action-btn at-primary-btn\" (click)=\"ResumePipeline()\">\n <i class=\"fa-solid fa-play\"></i> Resume\n </button>\n }\n <button class=\"at-action-btn at-danger-btn\" (click)=\"CancelPipeline()\">\n <i class=\"fa-solid fa-stop\"></i> Cancel\n </button>\n </div>\n </div>\n }\n\n <!-- D2: Live Per-Source Progress (visible during run) -->\n @if (IsRunning && LiveRunDetailRows.length > 0) {\n <div class=\"at-card\" style=\"margin-bottom: 12px;\">\n <div class=\"at-card-header\">\n <span class=\"at-card-title\"><i class=\"fa-solid fa-list-check\"></i> Per-Source Progress</span>\n <button class=\"at-action-btn at-secondary-btn\" style=\"font-size: 11px; padding: 3px 8px;\" (click)=\"LoadLiveRunDetails()\">\n <i class=\"fa-solid fa-arrows-rotate\"></i>\n </button>\n </div>\n <div class=\"at-card-body\" style=\"max-height: 200px; overflow-y: auto;\">\n <table class=\"at-run-table\">\n <thead>\n <tr>\n <th>Source</th>\n <th>Status</th>\n <th>Items</th>\n <th>Tagged</th>\n <th>Vectorized</th>\n <th>Errors</th>\n </tr>\n </thead>\n <tbody>\n @for (row of LiveRunDetailRows; track row.SourceName) {\n <tr>\n <td class=\"at-run-source-name\">{{ row.SourceName }}</td>\n <td>\n <span class=\"at-run-status-badge\" [class]=\"row.StatusClass\">\n @if (row.StatusClass === 'running') {\n <i class=\"fa-solid fa-spinner fa-spin\" style=\"font-size: 0.55rem\"></i>\n }\n {{ row.Status }}\n </span>\n </td>\n <td>{{ row.ItemsProcessed }}</td>\n <td>{{ row.ItemsTagged }}</td>\n <td>{{ row.ItemsVectorized }}</td>\n <td [class.run-error-text]=\"row.ErrorCount > 0\">{{ row.ErrorCount }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n }\n\n <!-- Recent Processing Feed -->\n <div class=\"at-card at-feed-card\">\n <div class=\"at-card-header\">\n <span class=\"at-card-title\"><i class=\"fa-solid fa-bolt\"></i> Recent Processing</span>\n <div class=\"at-feed-header-actions\">\n <button class=\"at-feed-sort-btn\" (click)=\"ToggleFeedSort()\"\n [title]=\"FeedSortOrder === 'newest' ? 'Showing newest first' : 'Showing oldest first'\">\n <i class=\"fa-solid\" [class.fa-arrow-down-short-wide]=\"FeedSortOrder === 'newest'\"\n [class.fa-arrow-up-short-wide]=\"FeedSortOrder === 'oldest'\"></i>\n {{ FeedSortOrder === 'newest' ? 'Newest' : 'Oldest' }}\n </button>\n <span class=\"at-feed-count\">{{ FilteredFeedItems.length }} items</span>\n </div>\n </div>\n <!-- Feed search bar -->\n <div class=\"at-feed-search-bar\">\n <i class=\"fa-solid fa-search at-feed-search-icon\"></i>\n <input type=\"text\"\n class=\"mj-input at-feed-search-input\"\n placeholder=\"Search items, sources, or tags...\"\n [(ngModel)]=\"FeedSearchQuery\"\n (input)=\"OnFeedSearchChange()\">\n @if (FeedSearchQuery) {\n <button class=\"at-feed-search-clear\" (click)=\"FeedSearchQuery = ''; OnFeedSearchChange()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n }\n </div>\n <div class=\"at-card-body at-feed-scroll-body\">\n @if (FilteredFeedItems.length === 0) {\n <div class=\"at-empty-state\">\n <i class=\"fa-solid fa-inbox\"></i>\n @if (FeedSearchQuery) {\n <p>No items match \"{{ FeedSearchQuery }}\"</p>\n } @else {\n <p>No processed items yet.</p>\n }\n </div>\n }\n @for (item of PaginatedFeedItems; track item.Name + item.SourceName + item.TimeAgo) {\n <div class=\"at-feed-item at-feed-item-clickable\" (click)=\"OpenFeedItemDetail(GetFeedItemOriginalIndex(item))\">\n <div class=\"at-feed-status-dot\" [class]=\"item.Status\"></div>\n <div class=\"at-feed-item-content\">\n <span class=\"at-feed-item-name\">{{ item.Name }}</span>\n <span class=\"at-feed-item-source-label\">{{ item.SourceName }}</span>\n </div>\n <div class=\"at-feed-item-tags\">\n @for (tag of item.Tags; track tag) {\n <span class=\"at-feed-tag\">{{ tag }}</span>\n }\n </div>\n <span class=\"at-feed-item-time\">{{ item.TimeAgo }}</span>\n </div>\n }\n </div>\n <!-- Feed pagination -->\n @if (FeedTotalPages > 1) {\n <div class=\"at-feed-pagination\">\n <button class=\"at-action-btn at-secondary-btn\" [disabled]=\"FeedPage === 0\" (click)=\"FeedPrevPage()\">\n <i class=\"fa-solid fa-chevron-left\"></i>\n </button>\n <span class=\"at-feed-pagination-label\">Page {{ FeedPage + 1 }} of {{ FeedTotalPages }}</span>\n <button class=\"at-action-btn at-secondary-btn\" [disabled]=\"FeedPage >= FeedTotalPages - 1\" (click)=\"FeedNextPage()\">\n <i class=\"fa-solid fa-chevron-right\"></i>\n </button>\n </div>\n }\n </div>\n </div>\n\n <!-- Right sidebar -->\n <div class=\"at-pipeline-right\">\n <!-- Sources Quick List -->\n <div class=\"at-card\">\n <div class=\"at-card-header\">\n <span class=\"at-card-title\"><i class=\"fa-solid fa-database\"></i> Sources</span>\n </div>\n <div class=\"at-card-body\">\n @for (source of SourceMinis; track source.ID) {\n <div class=\"at-source-mini\">\n <div class=\"at-source-mini-icon\"><i [class]=\"source.Icon\"></i></div>\n <div class=\"at-source-mini-info\">\n <div class=\"at-source-mini-name\">{{ source.Name }}</div>\n <div class=\"at-source-mini-meta\">{{ source.Meta }}</div>\n </div>\n <div class=\"at-source-mini-status\" [class]=\"source.StatusClass\"></div>\n </div>\n }\n </div>\n </div>\n <!-- Trending Tags -->\n <div class=\"at-card at-tag-cloud-card\">\n <div class=\"at-card-title\" style=\"margin-bottom: 10px;\"><i class=\"fa-solid fa-chart-bar\"></i> Trending Tags</div>\n <div class=\"at-tag-cloud\">\n @for (tag of TrendingTags; track tag.Tag) {\n <span class=\"at-tag-pill\" [class]=\"tag.SizeClass\"\n [style.opacity]=\"0.4 + tag.AvgWeight * 0.6\"\n [title]=\"'Weight: ' + tag.AvgWeight.toFixed(2)\">{{ tag.Tag }}</span>\n }\n </div>\n </div>\n\n <!-- Pipeline Settings Widget -->\n <div class=\"at-card at-config-card\">\n <div class=\"at-card-header\" (click)=\"TogglePipelineConfig()\" style=\"cursor:pointer\">\n <span class=\"at-card-title\"><i class=\"fa-solid fa-sliders\"></i> Pipeline Settings</span>\n <i class=\"fa-solid\" [class.fa-chevron-down]=\"!ShowPipelineConfig\" [class.fa-chevron-up]=\"ShowPipelineConfig\" style=\"font-size:0.7rem; color:var(--mj-text-muted)\"></i>\n </div>\n @if (ShowPipelineConfig) {\n <div class=\"at-card-body at-config-body\">\n <div class=\"at-config-row\">\n <label class=\"at-config-label\">Batch Size</label>\n <div class=\"at-config-control\">\n <input type=\"number\" class=\"at-config-input\" min=\"10\" max=\"1000\" step=\"10\"\n [(ngModel)]=\"PipelineConfig.Pipeline!.BatchSize\">\n </div>\n </div>\n <div class=\"at-config-row\">\n <label class=\"at-config-label\">Throttle (ms)</label>\n <div class=\"at-config-control\">\n <input type=\"range\" class=\"at-config-slider\" min=\"0\" max=\"5000\" step=\"100\"\n [(ngModel)]=\"PipelineConfig.Pipeline!.DelayBetweenBatchesMs\">\n <span class=\"at-config-value\">{{ PipelineConfig.Pipeline!.DelayBetweenBatchesMs }}ms</span>\n </div>\n </div>\n <div class=\"at-config-row\">\n <label class=\"at-config-label\">Error Tolerance</label>\n <div class=\"at-config-control\">\n <input type=\"range\" class=\"at-config-slider\" min=\"5\" max=\"50\" step=\"5\"\n [(ngModel)]=\"PipelineConfig.Pipeline!.ErrorThresholdPercent\">\n <span class=\"at-config-value\">{{ PipelineConfig.Pipeline!.ErrorThresholdPercent }}%</span>\n </div>\n </div>\n <div class=\"at-config-row\">\n <label class=\"at-config-label\">Auto-resume</label>\n <div class=\"at-config-control\">\n <label class=\"at-config-toggle\">\n <input type=\"checkbox\" [(ngModel)]=\"PipelineConfig.Pipeline!.ResumeFromLastBatch\">\n <span class=\"at-toggle-track\"><span class=\"at-toggle-thumb\"></span></span>\n </label>\n </div>\n </div>\n <div class=\"at-config-divider\"></div>\n <div class=\"at-config-section-label\">LLM Rate Limits</div>\n <div class=\"at-config-row\">\n <label class=\"at-config-label\">Requests/min</label>\n <div class=\"at-config-control\">\n <input type=\"number\" class=\"at-config-input\" min=\"1\" max=\"500\"\n [(ngModel)]=\"PipelineConfig.RateLimits!.LLM!.RequestsPerMinute\">\n </div>\n </div>\n <div class=\"at-config-row\">\n <label class=\"at-config-label\">Tokens/min</label>\n <div class=\"at-config-control\">\n <input type=\"number\" class=\"at-config-input\" min=\"1000\" max=\"5000000\" step=\"10000\"\n [(ngModel)]=\"PipelineConfig.RateLimits!.LLM!.TokensPerMinute\">\n <span class=\"at-config-value\">{{ FormatTokenCount(PipelineConfig.RateLimits!.LLM!.TokensPerMinute ?? 0) }}</span>\n </div>\n </div>\n <div class=\"at-config-section-label\">Embedding Rate Limits</div>\n <div class=\"at-config-row\">\n <label class=\"at-config-label\">Requests/min</label>\n <div class=\"at-config-control\">\n <input type=\"number\" class=\"at-config-input\" min=\"1\" max=\"1000\"\n [(ngModel)]=\"PipelineConfig.RateLimits!.Embedding!.RequestsPerMinute\">\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n }\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <!-- TAB 2: SOURCES -->\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (ActiveTab === 'sources') {\n <div class=\"at-page-header\">\n <div>\n <div class=\"at-page-title\">Content Sources</div>\n <div class=\"at-page-subtitle\">Configure where content is ingested from</div>\n </div>\n <div class=\"at-page-actions\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"OpenAddSourceForm()\">\n <i class=\"fa-solid fa-plus\"></i> Add Source\n </button>\n </div>\n </div>\n <div class=\"at-page-body\">\n <div class=\"at-sources-grid\">\n @for (card of SourceCards; track card.ID) {\n <div class=\"at-source-card-full at-source-card-clickable\" (click)=\"OpenSourceDetail(card)\">\n <div class=\"at-source-card-header\">\n <div class=\"at-source-card-icon\"><i [class]=\"card.Icon\"></i></div>\n <div>\n <div class=\"at-source-card-title\">{{ card.Name }}</div>\n <div class=\"at-source-card-type\">{{ card.SourceTypeName }}@if (card.RequiresFileType) { \u00B7 {{ card.FileTypeName }}}</div>\n </div>\n <div class=\"at-source-card-status\" [class]=\"card.StatusClass\">\n <div class=\"at-source-mini-status\" [class]=\"card.StatusClass\"></div>\n {{ card.StatusLabel }}\n </div>\n </div>\n @if (card.URL) {\n <div class=\"at-source-card-url\">{{ card.URL }}</div>\n }\n <div class=\"at-source-card-stats\">\n <div class=\"at-source-stat\">\n <div class=\"at-source-stat-value\">{{ formatNumber(card.ItemCount) }}</div>\n <div class=\"at-source-stat-label\">Items</div>\n </div>\n <div class=\"at-source-stat\">\n <div class=\"at-source-stat-value\">{{ formatNumber(card.TagCount) }}</div>\n <div class=\"at-source-stat-label\">Tags</div>\n </div>\n <div class=\"at-source-stat\">\n <div class=\"at-source-stat-value\">{{ card.AvgTags }}</div>\n <div class=\"at-source-stat-label\">Avg Tags</div>\n </div>\n <div class=\"at-source-stat\">\n <div class=\"at-source-stat-value\">{{ card.LastRunAgo }}</div>\n <div class=\"at-source-stat-label\">Last Run</div>\n </div>\n </div>\n <!-- Schedule indicator -->\n @if (card.ScheduledActionID) {\n <div class=\"at-schedule-indicator\" (click)=\"RemoveSchedule(card); $event.stopPropagation()\" title=\"Click to remove schedule\">\n <i class=\"fa-regular fa-clock\"></i>\n <span>{{ GetScheduleLabel(card) }}</span>\n </div>\n }\n <div class=\"at-source-card-actions\">\n <button class=\"at-source-action-btn\" (click)=\"OpenEditSourceForm(card); $event.stopPropagation()\"><i class=\"fa-solid fa-pen\"></i> Edit</button>\n <button class=\"at-source-action-btn\" (click)=\"RunPipelineForSource(card.ID); $event.stopPropagation()\"><i class=\"fa-solid fa-play\"></i> Run</button>\n @if (!card.ScheduledActionID) {\n <button class=\"at-source-action-btn at-source-schedule-btn\" (click)=\"OpenScheduleDialog(card); $event.stopPropagation()\"><i class=\"fa-regular fa-clock\"></i> Schedule</button>\n }\n <button class=\"at-source-action-btn at-source-delete-btn\" (click)=\"DeleteSource(card); $event.stopPropagation()\"><i class=\"fa-solid fa-trash\"></i> Delete</button>\n </div>\n </div>\n }\n\n <!-- Add Source Card -->\n <div class=\"at-add-source-card\" (click)=\"OpenAddSourceForm()\">\n <i class=\"fa-solid fa-plus-circle\"></i>\n <span style=\"font-size: 0.85rem; font-weight: 600;\">Add Content Source</span>\n <span style=\"font-size: 0.72rem;\">Web, RSS, Email, Files, API</span>\n </div>\n </div>\n\n <!-- Content Item Duplicates Section -->\n <div class=\"at-dedup-section\">\n <div class=\"at-dedup-header\">\n <div>\n <h3 class=\"at-dedup-title\">\n <i class=\"fa-solid fa-clone\"></i>\n Content Duplicates\n </h3>\n <span class=\"at-dedup-subtitle\">Review detected duplicate content items across sources</span>\n </div>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"LoadContentDuplicates()\" [disabled]=\"IsLoadingDuplicates\">\n @if (IsLoadingDuplicates) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Loading...\n } @else {\n <i class=\"fa-solid fa-arrows-rotate\"></i> Load Duplicates\n }\n </button>\n </div>\n\n @if (ContentDuplicates.length > 0) {\n <div class=\"at-dedup-list\">\n @for (dup of ContentDuplicates; track dup.ID) {\n <div class=\"at-dedup-card\">\n <div class=\"at-dedup-pair\">\n <div class=\"at-dedup-item\">\n <span class=\"at-dedup-item-name\">{{ dup.ItemAName }}</span>\n <span class=\"at-dedup-item-source\">{{ dup.ItemASource }}</span>\n </div>\n <div class=\"at-dedup-vs\">\n <i class=\"fa-solid fa-arrows-left-right\"></i>\n </div>\n <div class=\"at-dedup-item\">\n <span class=\"at-dedup-item-name\">{{ dup.ItemBName }}</span>\n <span class=\"at-dedup-item-source\">{{ dup.ItemBSource }}</span>\n </div>\n </div>\n <div class=\"at-dedup-meta\">\n <span class=\"at-dedup-score\">{{ (dup.SimilarityScore * 100).toFixed(0) }}% match</span>\n <span class=\"at-dedup-method\">{{ dup.DetectionMethod }}</span>\n </div>\n <div class=\"at-dedup-actions\">\n <button class=\"at-action-btn at-primary-btn at-dedup-confirm\" (click)=\"ConfirmContentDuplicate(dup)\">\n <i class=\"fa-solid fa-check\"></i> Confirm\n </button>\n <button class=\"at-action-btn at-secondary-btn at-dedup-dismiss\" (click)=\"DismissContentDuplicate(dup)\">\n <i class=\"fa-solid fa-times\"></i> Dismiss\n </button>\n </div>\n </div>\n }\n </div>\n } @else if (!IsLoadingDuplicates) {\n <div class=\"at-dedup-empty\">\n <i class=\"fa-solid fa-check-circle\"></i>\n <p>No pending duplicates. Click \"Load Duplicates\" to check.</p>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <!-- TAB 3: CONTENT TYPES -->\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (ActiveTab === 'types') {\n <div class=\"at-page-header\">\n <div>\n <div class=\"at-page-title\">Content Types</div>\n <div class=\"at-page-subtitle\">Configure AI tagging rules per content category</div>\n </div>\n <div class=\"at-page-actions\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"OpenAddTypeForm()\">\n <i class=\"fa-solid fa-plus\"></i> Add Type\n </button>\n </div>\n </div>\n <div class=\"at-page-body\">\n <div class=\"at-ct-grid\">\n @for (card of ContentTypeCards; track card.ID) {\n <div class=\"at-ct-card\">\n <div class=\"at-ct-card-header\">\n <span class=\"at-ct-card-name\">{{ card.Name }}</span>\n <span class=\"at-ct-card-model\">{{ card.AIModelName }}</span>\n </div>\n <div class=\"at-ct-field\">\n <span class=\"at-ct-field-label\">Description</span>\n <span class=\"at-ct-field-value\">{{ card.Description || '\\u2014' }}</span>\n </div>\n <div class=\"at-ct-field\">\n <span class=\"at-ct-field-label\">Sources Using</span>\n <span class=\"at-ct-field-value\">{{ card.SourcesUsing }}</span>\n </div>\n <div class=\"at-ct-field\">\n <span class=\"at-ct-field-label\">Items Tagged</span>\n <span class=\"at-ct-field-value\">{{ formatNumber(card.ItemsTagged) }}</span>\n </div>\n <div class=\"at-ct-tag-range\">\n <i class=\"fa-solid fa-tags\"></i>\n <span>{{ card.MinTags }}</span>\n <div class=\"at-ct-tag-range-bar\">\n <div class=\"at-ct-tag-range-fill\" [style.left.%]=\"card.RangeLeftPct\" [style.right.%]=\"card.RangeRightPct\"></div>\n </div>\n <span>{{ card.MaxTags }}</span>\n <span class=\"at-ct-range-suffix\">tags/item</span>\n </div>\n <div class=\"at-source-card-actions\" style=\"margin-top: 10px;\">\n <button class=\"at-source-action-btn\" (click)=\"OpenEditTypeForm(card)\"><i class=\"fa-solid fa-pen\"></i> Edit</button>\n </div>\n </div>\n }\n <!-- Add Content Type Card -->\n <div class=\"at-add-type-card\" (click)=\"OpenAddTypeForm()\">\n <i class=\"fa-solid fa-plus-circle\"></i>\n <span style=\"font-size: 0.85rem; font-weight: 600;\">Add Content Type</span>\n <span style=\"font-size: 0.72rem;\">Configure tagging rules</span>\n </div>\n </div>\n </div>\n }\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <!-- TAB 4: TAG LIBRARY -->\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (ActiveTab === 'tags') {\n <div class=\"at-page-header\">\n <div>\n <div class=\"at-page-title\">Tag Library</div>\n <div class=\"at-page-subtitle\">{{ TagRows.length }} unique tags across all content sources</div>\n </div>\n <div class=\"at-page-actions\">\n <input type=\"text\" class=\"at-search-input\" placeholder=\"Search tags...\"\n [(ngModel)]=\"TagSearchQuery\" (input)=\"FilterTags()\">\n </div>\n </div>\n <div class=\"at-page-body\">\n <div class=\"at-tag-lib-layout\">\n <div class=\"at-tag-lib-main\">\n <div class=\"at-card\">\n <div class=\"at-card-body\" style=\"max-height: 500px; overflow-y: auto;\">\n <table class=\"at-tag-table\">\n <thead>\n <tr>\n <th>Tag</th>\n <th>Count</th>\n <th>Avg Weight</th>\n <th>Distribution</th>\n <th>Top Source</th>\n <th>First Seen</th>\n </tr>\n </thead>\n <tbody>\n @for (row of FilteredTagRows; track row.Tag) {\n <tr class=\"at-tag-row-clickable\" (click)=\"DrillDownTag(row.Tag)\"\n [class.at-tag-row-selected]=\"SelectedDrillDownTag === row.Tag\">\n <td class=\"at-tag-name-cell\">{{ row.Tag }}</td>\n <td>{{ row.UsageCount }}</td>\n <td>\n <div class=\"at-weight-indicator\">\n <div class=\"at-weight-bar\">\n <div class=\"at-weight-bar-fill\" [style.width.%]=\"row.AvgWeight * 100\"\n [class.at-weight-high]=\"row.AvgWeight >= 0.7\"\n [class.at-weight-medium]=\"row.AvgWeight >= 0.4 && row.AvgWeight < 0.7\"\n [class.at-weight-low]=\"row.AvgWeight < 0.4\"></div>\n </div>\n <span class=\"at-weight-value\">{{ row.AvgWeight.toFixed(2) }}</span>\n </div>\n </td>\n <td>\n <div class=\"at-tag-bar\">\n <div class=\"at-tag-bar-fill\" [style.width.%]=\"row.BarWidthPct\"></div>\n </div>\n </td>\n <td>{{ row.TopSource }}</td>\n <td>{{ row.FirstSeen }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n\n <!-- Tag drill-down: content items matching selected tag -->\n @if (SelectedDrillDownTag) {\n <div class=\"at-card\" style=\"margin-top: 12px;\">\n <div class=\"at-card-header\">\n <span class=\"at-card-title\">\n <i class=\"fa-solid fa-tag\"></i>\n Content items tagged \"{{ SelectedDrillDownTag }}\"\n ({{ TagDrillDownItems.length }})\n </span>\n <button class=\"at-slide-close\" (click)=\"CloseDrillDownTag()\" style=\"background:none;border:none;cursor:pointer;color:var(--mj-text-muted)\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div class=\"at-card-body\" style=\"max-height: 300px; overflow-y: auto;\">\n @if (TagDrillDownItems.length === 0) {\n <div class=\"at-empty-state\"><p>No content items found for this tag.</p></div>\n } @else {\n <table class=\"at-tag-table\">\n <thead>\n <tr>\n <th>Name</th>\n <th>Source</th>\n <th>Weight</th>\n <th>Updated</th>\n </tr>\n </thead>\n <tbody>\n @for (di of TagDrillDownItems; track di.ID) {\n <tr class=\"at-tag-row-clickable\" (click)=\"OpenItemDetailByID(di.ID)\">\n <td>{{ di.Name }}</td>\n <td>{{ di.SourceName }}</td>\n <td>{{ FormatWeight(di.Weight) }}</td>\n <td>{{ di.UpdatedAt }}</td>\n </tr>\n }\n </tbody>\n </table>\n }\n </div>\n </div>\n }\n </div>\n <div class=\"at-tag-lib-sidebar\">\n <div class=\"at-card at-tag-cloud-card\">\n <div class=\"at-card-title\" style=\"margin-bottom: 12px;\"><i class=\"fa-solid fa-cloud\"></i> Tag Cloud</div>\n @if (TagCloudWordItems.length > 0) {\n <mj-word-cloud\n [Items]=\"TagCloudWordItems\"\n [MaxFontSize]=\"40\"\n [MinFontSize]=\"12\"\n [MaxItems]=\"20\"\n Layout=\"spiral\"\n ColorMode=\"weight-gradient\"\n [Interactive]=\"true\"\n [Animate]=\"true\">\n </mj-word-cloud>\n } @else {\n <div class=\"at-tag-cloud-empty\">No tags yet</div>\n }\n </div>\n <div class=\"at-card\" style=\"padding: 16px; margin-top: 12px;\">\n <div class=\"at-card-title\" style=\"margin-bottom: 10px;\"><i class=\"fa-solid fa-chart-pie\"></i> By Source</div>\n <div class=\"at-tags-by-source\">\n @for (s of TagsBySource; track s.SourceName) {\n <div class=\"at-tag-source-row\">\n <span>{{ s.SourceName }}</span>\n <strong>{{ s.Count }}</strong>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n </div>\n }\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <!-- TAB 6: TAXONOMY GOVERNANCE -->\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (ActiveTab === 'taxonomy') {\n <div class=\"at-page-header\">\n <div>\n <div class=\"at-page-title\">Taxonomy Governance</div>\n <div class=\"at-page-subtitle\">Manage tag hierarchy, resolve duplicates, and monitor taxonomy health — <strong>{{ TaxHealth.Total }} total tags</strong></div>\n </div>\n <div class=\"at-page-actions\">\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"RefreshTaxonomyData()\">\n <i class=\"fa-solid fa-arrows-rotate\"></i> Refresh\n </button>\n </div>\n </div>\n\n <!-- Sub-tab strip -->\n <div class=\"at-tax-tab-strip\">\n <div class=\"at-tax-tab\" [class.active]=\"TaxSubTab === 'tree'\" (click)=\"SwitchTaxSubTab('tree')\">\n <i class=\"fa-solid fa-sitemap\"></i> Tree View\n </div>\n <div class=\"at-tax-tab\" [class.active]=\"TaxSubTab === 'duplicates'\" (click)=\"SwitchTaxSubTab('duplicates')\">\n <i class=\"fa-solid fa-link\"></i> Duplicates\n @if (TaxDuplicates.length > 0) {\n <span class=\"at-tax-tab-badge at-tax-badge-warning\">{{ TaxDuplicates.length }}</span>\n }\n </div>\n <div class=\"at-tax-tab\" [class.active]=\"TaxSubTab === 'orphans'\" (click)=\"SwitchTaxSubTab('orphans')\">\n <i class=\"fa-solid fa-ban\"></i> Orphans\n @if (TaxOrphans.length > 0) {\n <span class=\"at-tax-tab-badge at-tax-badge-error\">{{ TaxOrphans.length }}</span>\n }\n </div>\n <div class=\"at-tax-tab\" [class.active]=\"TaxSubTab === 'treemap'\" (click)=\"SwitchTaxSubTab('treemap')\">\n <i class=\"fa-solid fa-chart-tree-map\"></i> Treemap\n </div>\n <div class=\"at-tax-tab\" [class.active]=\"TaxSubTab === 'audit'\" (click)=\"SwitchTaxSubTab('audit')\">\n <i class=\"fa-solid fa-scroll\"></i> Audit Log\n </div>\n </div>\n\n <div class=\"at-page-body\">\n\n <!-- \u2550\u2550 SUB-TAB 1: TREE VIEW \u2550\u2550 -->\n @if (TaxSubTab === 'tree') {\n <div class=\"at-tax-split-view\">\n <!-- Tree panel -->\n <div class=\"at-tax-tree-panel\" style=\"position: relative;\">\n <div class=\"at-tax-tree-toolbar\">\n <input type=\"text\" class=\"at-search-input\" style=\"flex: 1;\" placeholder=\"Search tags...\"\n [(ngModel)]=\"TaxTreeSearch\" (input)=\"FilterTaxTree()\">\n <button class=\"at-tax-toolbar-btn\" title=\"Add root tag\" (click)=\"OpenCreateRootTag()\">\n <i class=\"fa-solid fa-plus\"></i>\n </button>\n <button class=\"at-tax-toolbar-btn\" [class.active]=\"TaxMultiSelectMode\"\n title=\"Toggle multi-select for drag reparenting\" (click)=\"ToggleMultiSelectMode()\">\n <i class=\"fa-solid fa-check-double\"></i>\n </button>\n </div>\n <div class=\"at-tax-tree-body\"\n (dragover)=\"$event.preventDefault()\"\n (drop)=\"OnDropToRoot($event)\">\n @if (TaxFilteredNodes.length === 0) {\n <div class=\"at-empty-state\">\n <i class=\"fa-solid fa-sitemap\"></i>\n <p>No tags found</p>\n </div>\n }\n @for (node of TaxFilteredNodes; track node.ID) {\n <div class=\"at-tax-tree-node\"\n [class.at-tax-node-selected]=\"node.IsSelected\"\n [class.at-tax-node-drag-over]=\"TaxDragOverNodeID === node.ID\"\n [class.at-tax-node-multi-selected]=\"IsNodeMultiSelected(node.ID)\"\n [style.padding-left.px]=\"16 + node.Depth * 20\"\n [attr.draggable]=\"true\"\n (dragstart)=\"OnTreeNodeDragStart($event, node)\"\n (dragover)=\"OnTreeNodeDragOver($event, node)\"\n (dragleave)=\"OnTreeNodeDragLeave()\"\n (drop)=\"OnTreeNodeDrop($event, node); $event.stopPropagation()\"\n (click)=\"SelectTaxNode(node)\">\n @if (TaxMultiSelectMode) {\n <input type=\"checkbox\" class=\"at-tax-tree-checkbox\"\n [checked]=\"IsNodeMultiSelected(node.ID)\"\n (click)=\"ToggleNodeSelection(node, $event)\">\n }\n <span class=\"at-tax-tree-arrow\"\n [class.at-tax-arrow-expanded]=\"node.IsExpanded && node.Children.length > 0\"\n [class.at-tax-arrow-collapsed]=\"!node.IsExpanded && node.Children.length > 0\"\n [class.at-tax-arrow-leaf]=\"node.Children.length === 0\"\n (click)=\"ToggleTaxNode(node); $event.stopPropagation()\"></span>\n <span class=\"at-tax-health-dot\" [class]=\"node.HealthColor\"></span>\n <span class=\"at-tax-tree-label\" [class.at-tax-tree-label-selected]=\"node.IsSelected\">{{ node.Name }}</span>\n <span class=\"at-tax-tree-count\">({{ node.ItemCount }})</span>\n <span class=\"at-tax-tree-add-child\" title=\"Add child tag\"\n (click)=\"OpenCreateChildTagFor(node); $event.stopPropagation()\">\n <i class=\"fa-solid fa-plus\"></i>\n </span>\n </div>\n }\n </div>\n @if (TaxTreeSaving) {\n <div class=\"at-tax-tree-saving-overlay\">\n <mj-loading text=\"Moving tags...\" size=\"small\"></mj-loading>\n </div>\n }\n </div>\n\n <!-- Details panel -->\n <div class=\"at-tax-details-panel\">\n @if (TaxSelectedNode) {\n <div class=\"at-tax-details-header\">\n <!-- Breadcrumb -->\n <div class=\"at-tax-breadcrumb\">\n @for (bc of GetTaxBreadcrumb(TaxSelectedNode); track bc.ID) {\n <span class=\"at-tax-bc-link\" (click)=\"NavigateToBreadcrumb(bc.ID)\">{{ bc.Name }}</span>\n <span class=\"at-tax-bc-sep\">›</span>\n }\n <span class=\"at-tax-bc-current\">{{ TaxSelectedNode.Name }}</span>\n </div>\n\n @if (!TaxIsEditing) {\n <div class=\"at-tax-details-title\">\n {{ TaxSelectedNode.DisplayName }}\n <span class=\"at-tax-edit-icon\" (click)=\"StartEditTag()\" title=\"Edit name\">\n <i class=\"fa-solid fa-pen\"></i>\n </span>\n </div>\n @if (TaxSelectedNode.Description) {\n <div class=\"at-tax-details-desc\">{{ TaxSelectedNode.Description }}</div>\n }\n } @else {\n <div class=\"at-tax-edit-form\">\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">Name</label>\n <input type=\"text\" class=\"at-form-input\" [(ngModel)]=\"TaxEditName\">\n </div>\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">Description</label>\n <textarea class=\"at-form-textarea\" rows=\"3\" [(ngModel)]=\"TaxEditDescription\"></textarea>\n </div>\n <div class=\"at-form-actions\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"SaveEditTag()\">Save</button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"CancelEditTag()\">Cancel</button>\n </div>\n </div>\n }\n </div>\n\n <!-- Stats row -->\n <div class=\"at-tax-stats-row\">\n <div class=\"at-tax-stat-item\">\n <div class=\"at-tax-stat-value\">{{ TaxSelectedNode.ItemCount }}</div>\n <div class=\"at-tax-stat-label\">Items Tagged</div>\n </div>\n <div class=\"at-tax-stat-item\">\n <div class=\"at-tax-stat-value\">{{ TaxSelectedNode.AvgWeight.toFixed(2) }}</div>\n <div class=\"at-tax-stat-label\">Avg Weight</div>\n </div>\n <div class=\"at-tax-stat-item\">\n <div class=\"at-tax-stat-value\">{{ TaxSelectedNode.Children.length }}</div>\n <div class=\"at-tax-stat-label\">Children</div>\n </div>\n <div class=\"at-tax-stat-item\">\n <div class=\"at-tax-stat-value\">{{ TaxSelectedNode.Depth }}</div>\n <div class=\"at-tax-stat-label\">Depth</div>\n </div>\n <div class=\"at-tax-stat-item\">\n <div class=\"at-tax-stat-value at-tax-stat-date\">{{ TaxSelectedNode.FirstSeen }}</div>\n <div class=\"at-tax-stat-label\">First Seen</div>\n </div>\n </div>\n\n <!-- Action toolbar -->\n @if (!TaxIsEditing) {\n <div class=\"at-tax-action-toolbar\">\n <button class=\"at-tax-action-btn\" (click)=\"OpenCreateChildTag()\"><i class=\"fa-solid fa-plus\"></i> Add Child</button>\n <button class=\"at-tax-action-btn\" (click)=\"StartEditTag()\"><i class=\"fa-solid fa-pen\"></i> Rename</button>\n <button class=\"at-tax-action-btn\" (click)=\"OpenMoveDialog(TaxSelectedNode)\"><i class=\"fa-solid fa-arrows-up-down\"></i> Move</button>\n <button class=\"at-tax-action-btn\" (click)=\"OpenMergeIntoDialog(TaxSelectedNode)\"><i class=\"fa-solid fa-compress\"></i> Merge Into...</button>\n <button class=\"at-tax-action-btn\" (click)=\"OpenSplitDialog(TaxSelectedNode)\"><i class=\"fa-solid fa-code-branch\"></i> Split</button>\n <button class=\"at-tax-action-btn at-tax-action-danger\" (click)=\"DeleteTag(TaxSelectedNode)\"><i class=\"fa-solid fa-trash\"></i> Delete</button>\n </div>\n }\n\n <!-- Child tags -->\n @if (TaxSelectedNode.Children.length > 0) {\n <div class=\"at-tax-detail-section\">\n <div class=\"at-tax-section-title\">Child Tags</div>\n <div class=\"at-tax-child-chips\">\n @for (child of TaxSelectedNode.Children; track child.ID) {\n <span class=\"at-tax-child-chip\" (click)=\"SelectTaxNode(child)\">\n {{ child.Name }}\n <span class=\"at-tax-chip-count\">{{ child.ItemCount }}</span>\n </span>\n }\n </div>\n </div>\n }\n\n <!-- Recently tagged items -->\n @if (TaxRecentItems.length > 0) {\n <div class=\"at-tax-detail-section\">\n <div class=\"at-tax-section-title\">Recently Tagged Items</div>\n <div class=\"at-tax-recent-list\">\n @for (item of TaxRecentItems; track $index) {\n <div class=\"at-tax-recent-item\">\n <div class=\"at-tax-recent-icon\"><i [class]=\"item.Icon\"></i></div>\n <div class=\"at-tax-recent-name\">{{ item.Name }}</div>\n <div class=\"at-tax-recent-weight\">{{ item.Weight.toFixed(2) }}</div>\n <div class=\"at-tax-recent-date\">{{ item.Date }}</div>\n </div>\n }\n </div>\n </div>\n }\n } @else {\n <div class=\"at-empty-state\" style=\"height: 100%;\">\n <i class=\"fa-solid fa-hand-pointer\"></i>\n <p>Select a tag from the tree to view details</p>\n </div>\n }\n </div>\n </div>\n\n <!-- Health bar -->\n <div class=\"at-tax-health-bar\">\n <span class=\"at-tax-health-label\">Taxonomy Health</span>\n <div class=\"at-tax-health-stat\">\n <span class=\"at-tax-dot at-tax-dot-total\"></span>\n <span class=\"at-tax-health-value\">{{ TaxHealth.Total }}</span>\n <span class=\"at-tax-health-text\">Total</span>\n </div>\n <div class=\"at-tax-health-stat\">\n <span class=\"at-tax-dot at-tax-dot-healthy\"></span>\n <span class=\"at-tax-health-value at-tax-val-success\">{{ TaxHealth.Healthy }}</span>\n <span class=\"at-tax-health-text\">Healthy</span>\n </div>\n <div class=\"at-tax-health-stat\">\n <span class=\"at-tax-dot at-tax-dot-attention\"></span>\n <span class=\"at-tax-health-value at-tax-val-warning\">{{ TaxHealth.NeedAttention }}</span>\n <span class=\"at-tax-health-text\">Need Attention</span>\n </div>\n <div class=\"at-tax-health-stat\">\n <span class=\"at-tax-dot at-tax-dot-orphaned\"></span>\n <span class=\"at-tax-health-value at-tax-val-error\">{{ TaxHealth.Orphaned }}</span>\n <span class=\"at-tax-health-text\">Orphaned</span>\n </div>\n <div class=\"at-tax-health-stat\">\n <span class=\"at-tax-dot at-tax-dot-duplicates\"></span>\n <span class=\"at-tax-health-value at-tax-val-info\">{{ TaxHealth.Duplicates }}</span>\n <span class=\"at-tax-health-text\">Duplicate Candidates</span>\n </div>\n </div>\n }\n\n <!-- \u2550\u2550 SUB-TAB 2: DUPLICATES \u2550\u2550 -->\n @if (TaxSubTab === 'duplicates') {\n <div class=\"at-tax-dup-stats-bar\">\n <div class=\"at-tax-dup-stat\"><strong>{{ TaxDuplicates.length }}</strong> candidates found</div>\n <div class=\"at-tax-dup-stat at-tax-dup-high\"><strong>{{ TaxHighConfidenceDupeCount }}</strong> high confidence (>85%)</div>\n <div class=\"at-tax-dup-stat at-tax-dup-moderate\"><strong>{{ TaxModerateDupeCount }}</strong> moderate (70-85%)</div>\n </div>\n\n @if (TaxDuplicates.length === 0) {\n <div class=\"at-empty-state\">\n <i class=\"fa-solid fa-check-circle\"></i>\n <p>No duplicate tags detected</p>\n </div>\n }\n\n <div class=\"at-tax-dup-list\">\n @for (pair of TaxDuplicates; track $index) {\n <div class=\"at-tax-dup-card\" [class.at-tax-dup-high]=\"pair.SeverityClass === 'high'\" [class.at-tax-dup-moderate]=\"pair.SeverityClass === 'moderate'\">\n @if (pair.IsExactDuplicate) {\n <!-- Exact-name duplicates: show single tag name with count -->\n <div class=\"at-tax-dup-tag\">{{ pair.TagA }}</div>\n <div class=\"at-tax-dup-similarity\">\n <span class=\"at-tax-sim-percent high\">\n <i class=\"fa-solid fa-clone\"></i> {{ pair.ExactDuplicateCount }} identical records\n </span>\n </div>\n <div class=\"at-tax-dup-actions\">\n <button class=\"at-tax-dup-btn at-tax-dup-btn-primary\" (click)=\"MergeTags(pair.TagAID, pair.TagBID, pair.TagA, pair.TagB)\" [disabled]=\"IsMerging\">@if (IsMerging) { <i class=\"fa-solid fa-spinner fa-spin\"></i> Merging... } @else { Merge }</button>\n <button class=\"at-tax-dup-btn\" (click)=\"DismissDuplicate(pair)\">Dismiss</button>\n </div>\n } @else {\n <!-- Similar (non-exact) pairs: show both tags with similarity -->\n <div class=\"at-tax-dup-tag\">{{ pair.TagA }}</div>\n <div class=\"at-tax-dup-arrow\"><i class=\"fa-solid fa-arrows-left-right\"></i></div>\n <div class=\"at-tax-dup-similarity\">\n <div class=\"at-tax-sim-bar-bg\">\n <div class=\"at-tax-sim-bar-fill\" [class]=\"pair.SeverityClass\" [style.width.%]=\"pair.Similarity\"></div>\n </div>\n <span class=\"at-tax-sim-percent\" [class]=\"pair.SeverityClass\">{{ pair.Similarity }}%</span>\n </div>\n <div class=\"at-tax-dup-arrow\"><i class=\"fa-solid fa-arrows-left-right\"></i></div>\n <div class=\"at-tax-dup-tag\">{{ pair.TagB }}</div>\n <div class=\"at-tax-dup-actions\">\n <button class=\"at-tax-dup-btn at-tax-dup-btn-primary\" (click)=\"MergeTags(pair.TagAID, pair.TagBID, pair.TagA, pair.TagB)\" [disabled]=\"IsMerging\">@if (IsMerging) { <i class=\"fa-solid fa-spinner fa-spin\"></i> Merging... } @else { Merge }</button>\n <button class=\"at-tax-dup-btn\" (click)=\"MakeChildTag(pair.TagAID, pair.TagBID)\">Make Child</button>\n <button class=\"at-tax-dup-btn\" (click)=\"DismissDuplicate(pair)\">Dismiss</button>\n </div>\n }\n </div>\n }\n </div>\n }\n\n <!-- \u2550\u2550 SUB-TAB 3: ORPHANS \u2550\u2550 -->\n @if (TaxSubTab === 'orphans') {\n <div class=\"at-tax-orphan-toolbar\">\n <span class=\"at-tax-orphan-count\">{{ TaxOrphans.length }} orphaned tags</span>\n <span class=\"at-tax-orphan-desc\">— no parent, no children, low usage</span>\n <div class=\"at-tax-orphan-bulk\">\n <button class=\"at-tax-bulk-btn\" (click)=\"ToggleAllOrphans()\">\n @if (TaxAllOrphansSelected) { <i class=\"fa-solid fa-square-check\"></i> } @else { <i class=\"fa-regular fa-square\"></i> }\n Select All\n </button>\n <button class=\"at-tax-bulk-btn at-tax-bulk-danger\" (click)=\"BulkDeleteOrphans()\">\n <i class=\"fa-solid fa-trash\"></i> Bulk Delete\n </button>\n <button class=\"at-tax-bulk-btn at-tax-bulk-danger\" (click)=\"DeleteAllOrphans()\">\n <i class=\"fa-solid fa-trash-can\"></i> Delete All ({{ TaxOrphans.length }})\n </button>\n </div>\n </div>\n\n @if (TaxOrphans.length === 0) {\n <div class=\"at-empty-state\">\n <i class=\"fa-solid fa-check-circle\"></i>\n <p>No orphaned tags</p>\n </div>\n }\n\n <div class=\"at-tax-orphan-grid\">\n @for (orphan of TaxOrphans; track orphan.ID) {\n <div class=\"at-tax-orphan-card\">\n <div class=\"at-tax-orphan-header\">\n <span class=\"at-tax-orphan-name\">{{ orphan.Name }}</span>\n <input type=\"checkbox\" class=\"at-tax-orphan-checkbox\" [checked]=\"orphan.IsSelected\"\n (change)=\"ToggleOrphanSelection(orphan)\" (click)=\"$event.stopPropagation()\">\n </div>\n <div class=\"at-tax-orphan-stats\">\n <span>Usage: <strong>{{ orphan.UsageCount }}</strong></span>\n <span>Avg Weight: <strong>{{ orphan.AvgWeight.toFixed(2) }}</strong></span>\n </div>\n <div class=\"at-tax-orphan-dates\">\n <span>First: {{ orphan.FirstSeen }}</span>\n <span>Last: {{ orphan.LastSeen }}</span>\n </div>\n <div class=\"at-tax-orphan-actions\">\n <button class=\"at-tax-orphan-btn at-tax-orphan-delete\" (click)=\"DeleteOrphan(orphan)\">Delete</button>\n <button class=\"at-tax-orphan-btn\">Ignore</button>\n </div>\n </div>\n }\n </div>\n }\n\n <!-- \u2550\u2550 SUB-TAB 4: TREEMAP \u2550\u2550 -->\n @if (TaxSubTab === 'treemap') {\n <div class=\"at-tax-treemap-kpi-strip\">\n @for (kpi of TaxTreemapKPIs; track kpi.Label) {\n <div class=\"at-tax-treemap-kpi\">\n <div class=\"at-tax-treemap-kpi-value\">{{ kpi.Value }}</div>\n <div class=\"at-tax-treemap-kpi-label\">{{ kpi.Label }}</div>\n </div>\n }\n </div>\n\n @if (TaxTreemapCells.length === 0) {\n <div class=\"at-empty-state\">\n <i class=\"fa-solid fa-chart-tree-map\"></i>\n <p>No taxonomy data to visualize</p>\n </div>\n } @else {\n <div class=\"at-tax-treemap-container\">\n @for (cell of TaxTreemapCells; track $index) {\n <div class=\"at-tax-treemap-cell at-tax-treemap-cell-clickable\" [class]=\"cell.ColorClass\"\n [style.grid-row]=\"cell.RowSpan > 1 ? 'span ' + cell.RowSpan : ''\"\n (click)=\"OpenTreemapDrillIn(cell)\">\n <span class=\"at-tax-cell-name\">{{ cell.Name }}</span>\n <span class=\"at-tax-cell-count\">{{ cell.ItemCount }} items</span>\n </div>\n }\n </div>\n }\n\n <!-- Treemap Drill-In Panel -->\n @if (ShowTreemapDrillIn && TreemapDrillInNode) {\n <div class=\"at-tax-drillin-overlay\" (click)=\"CloseTreemapDrillIn()\">\n <div class=\"at-tax-drillin-panel\" (click)=\"$event.stopPropagation()\">\n <div class=\"at-tax-drillin-header\">\n <h3><i class=\"fa-solid fa-tag\"></i> {{ TreemapDrillInNode.Name }}</h3>\n <button class=\"at-schedule-dialog-close\" (click)=\"CloseTreemapDrillIn()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div class=\"at-tax-drillin-body\">\n <div class=\"at-tax-stats-row\">\n <div class=\"at-tax-stat-item\">\n <div class=\"at-tax-stat-value\">{{ TreemapDrillInNode.ItemCount }}</div>\n <div class=\"at-tax-stat-label\">Items Tagged</div>\n </div>\n <div class=\"at-tax-stat-item\">\n <div class=\"at-tax-stat-value\">{{ TreemapDrillInNode.AvgWeight.toFixed(2) }}</div>\n <div class=\"at-tax-stat-label\">Avg Weight</div>\n </div>\n <div class=\"at-tax-stat-item\">\n <div class=\"at-tax-stat-value\">{{ TreemapDrillInNode.Children.length }}</div>\n <div class=\"at-tax-stat-label\">Children</div>\n </div>\n </div>\n\n @if (TreemapDrillInNode.Children.length > 0) {\n <div class=\"at-tax-detail-section\">\n <div class=\"at-tax-section-title\">Child Tags</div>\n <div class=\"at-tax-child-chips\">\n @for (child of TreemapDrillInNode.Children; track child.ID) {\n <span class=\"at-tax-child-chip\">{{ child.Name }} <span class=\"at-tax-chip-count\">{{ child.ItemCount }}</span></span>\n }\n </div>\n </div>\n }\n\n @if (TaxRecentItems.length > 0) {\n <div class=\"at-tax-detail-section\">\n <div class=\"at-tax-section-title\">Recently Tagged Items</div>\n <div class=\"at-tax-recent-list\">\n @for (item of TaxRecentItems; track $index) {\n <div class=\"at-tax-recent-item\">\n <div class=\"at-tax-recent-icon\"><i [class]=\"item.Icon\"></i></div>\n <div class=\"at-tax-recent-name\">{{ item.Name }}</div>\n <div class=\"at-tax-recent-weight\">{{ item.Weight.toFixed(2) }}</div>\n <div class=\"at-tax-recent-date\">{{ item.Date }}</div>\n </div>\n }\n </div>\n </div>\n }\n </div>\n <div class=\"at-tax-drillin-footer\">\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"DrillInToTreeView(TreemapDrillInNode)\">\n <i class=\"fa-solid fa-sitemap\"></i> View in Tree\n </button>\n </div>\n </div>\n </div>\n }\n }\n\n <!-- \u2550\u2550 SUB-TAB 5: AUDIT LOG \u2550\u2550 -->\n @if (TaxSubTab === 'audit') {\n <div class=\"at-tax-audit-filters\">\n <span class=\"at-tax-audit-filter-label\">Filter</span>\n <div class=\"at-tax-audit-checkbox-group\">\n <label class=\"at-tax-audit-checkbox\">\n <input type=\"checkbox\" [checked]=\"TaxAuditFilterTypes.has('created')\" (change)=\"ToggleTaxAuditFilter('created')\"> Created\n </label>\n <label class=\"at-tax-audit-checkbox\">\n <input type=\"checkbox\" [checked]=\"TaxAuditFilterTypes.has('merged')\" (change)=\"ToggleTaxAuditFilter('merged')\"> Merged\n </label>\n <label class=\"at-tax-audit-checkbox\">\n <input type=\"checkbox\" [checked]=\"TaxAuditFilterTypes.has('moved')\" (change)=\"ToggleTaxAuditFilter('moved')\"> Moved\n </label>\n <label class=\"at-tax-audit-checkbox\">\n <input type=\"checkbox\" [checked]=\"TaxAuditFilterTypes.has('deleted')\" (change)=\"ToggleTaxAuditFilter('deleted')\"> Deleted\n </label>\n <label class=\"at-tax-audit-checkbox\">\n <input type=\"checkbox\" [checked]=\"TaxAuditFilterTypes.has('renamed')\" (change)=\"ToggleTaxAuditFilter('renamed')\"> Renamed\n </label>\n <label class=\"at-tax-audit-checkbox\">\n <input type=\"checkbox\" [checked]=\"TaxAuditFilterTypes.has('deprecated')\" (change)=\"ToggleTaxAuditFilter('deprecated')\"> Deprecated\n </label>\n <label class=\"at-tax-audit-checkbox\">\n <input type=\"checkbox\" [checked]=\"TaxAuditFilterTypes.has('split')\" (change)=\"ToggleTaxAuditFilter('split')\"> Split\n </label>\n <label class=\"at-tax-audit-checkbox\">\n <input type=\"checkbox\" [checked]=\"TaxAuditFilterTypes.has('reactivated')\" (change)=\"ToggleTaxAuditFilter('reactivated')\"> Reactivated\n </label>\n </div>\n </div>\n\n @if (GetFilteredAuditEvents().length === 0) {\n <div class=\"at-empty-state\">\n <i class=\"fa-solid fa-scroll\"></i>\n <p>No audit events match the current filters</p>\n </div>\n }\n\n <div class=\"at-tax-audit-timeline\">\n @for (event of GetFilteredAuditEvents(); track $index) {\n <div class=\"at-tax-audit-event\">\n <div class=\"at-tax-audit-event-icon\" [class]=\"event.Type\">\n <i [class]=\"GetTaxAuditIcon(event.Type)\"></i>\n </div>\n <div class=\"at-tax-audit-event-body\">\n <div class=\"at-tax-audit-event-desc\">\n {{ event.Description }} <span class=\"at-tax-tag-ref\">{{ event.TagRef }}</span>\n </div>\n <div class=\"at-tax-audit-event-meta\">\n <span>{{ event.User }}</span>\n <span>{{ event.Timestamp }}</span>\n </div>\n </div>\n </div>\n }\n </div>\n }\n </div>\n }\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <!-- TAB 7: RUN HISTORY -->\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (ActiveTab === 'history') {\n <div class=\"at-page-header\">\n <div>\n <div class=\"at-page-title\">Run History</div>\n <div class=\"at-page-subtitle\">Processing run log across all content sources</div>\n </div>\n <div class=\"at-page-actions\">\n <select class=\"at-filter-select\" [(ngModel)]=\"HistorySourceFilter\" (change)=\"FilterRunHistory()\">\n <option value=\"\">All Sources</option>\n @for (s of HistorySourceOptions; track s) {\n <option [value]=\"s\">{{ s }}</option>\n }\n </select>\n <select class=\"at-filter-select\" [(ngModel)]=\"HistoryStatusFilter\" (change)=\"FilterRunHistory()\">\n <option value=\"\">All Status</option>\n <option value=\"complete\">Complete</option>\n <option value=\"failed\">Failed</option>\n <option value=\"running\">Running</option>\n </select>\n </div>\n </div>\n <div class=\"at-page-body\">\n <div class=\"at-card\">\n <div class=\"at-card-body\" style=\"max-height: 600px; overflow-y: auto;\">\n <table class=\"at-run-table\">\n <thead>\n <tr>\n <th>Status</th>\n <th>Source</th>\n <th>Started</th>\n <th>Duration</th>\n <th>Items</th>\n <th>Tags</th>\n <th>Errors</th>\n </tr>\n </thead>\n <tbody>\n @for (row of FilteredRunRows; track row.ID) {\n <tr class=\"at-run-row-clickable\"\n [class.at-run-row-selected]=\"SelectedRunID === row.ID\"\n (click)=\"OpenRunDetail(row.ID)\">\n <td>\n <span class=\"at-run-status-badge\" [class]=\"row.StatusClass\">\n @if (row.StatusClass === 'running') {\n <i class=\"fa-solid fa-spinner fa-spin\" style=\"font-size: 0.55rem\"></i>\n }\n {{ row.Status }}\n </span>\n </td>\n <td class=\"at-run-source-name\">{{ row.SourceName }}</td>\n <td>{{ row.StartedDisplay }}</td>\n <td class=\"at-run-duration\">{{ row.Duration }}</td>\n <td>{{ row.Items }}</td>\n <td>{{ row.Tags }}</td>\n <td [class]=\"row.ErrorClass\">{{ row.Errors }}</td>\n </tr>\n }\n </tbody>\n </table>\n </div>\n </div>\n\n <!-- D3/D8: Run Detail Slide-In Panel -->\n @if (SelectedRunID) {\n <div class=\"at-card\" style=\"margin-top: 12px;\">\n <div class=\"at-card-header\">\n <span class=\"at-card-title\">\n <i class=\"fa-solid fa-magnifying-glass-chart\"></i>\n Run Detail: {{ SelectedRunID.slice(0, 14) }}...\n </span>\n <button class=\"at-action-btn at-secondary-btn\" style=\"font-size: 11px; padding: 3px 8px;\" (click)=\"CloseRunDetail()\">\n <i class=\"fa-solid fa-times\"></i> Close\n </button>\n </div>\n <div class=\"at-card-body\">\n @if (IsLoadingRunDetail) {\n <mj-loading text=\"Loading run details...\" size=\"small\"></mj-loading>\n } @else if (RunDetailRows.length > 0) {\n <table class=\"at-run-table\">\n <thead>\n <tr>\n <th>Source</th>\n <th>Source Type</th>\n <th>Status</th>\n <th>Items</th>\n <th>Tagged</th>\n <th>Vectorized</th>\n <th>Errors</th>\n <th>Tokens</th>\n <th>Cost</th>\n <th>Duration</th>\n </tr>\n </thead>\n <tbody>\n @for (detail of RunDetailRows; track detail.SourceName) {\n <tr>\n <td class=\"at-run-source-name\">{{ detail.SourceName }}</td>\n <td>{{ detail.SourceType }}</td>\n <td>\n <span class=\"at-run-status-badge\" [class]=\"detail.StatusClass\">{{ detail.Status }}</span>\n </td>\n <td>{{ detail.ItemsProcessed }}</td>\n <td>{{ detail.ItemsTagged }}</td>\n <td>{{ detail.ItemsVectorized }}</td>\n <td [class.run-error-text]=\"detail.ErrorCount > 0\">{{ detail.ErrorCount }}</td>\n <td>{{ FormatTokenCount(detail.TotalTokens) }}</td>\n <td>{{ detail.TotalCost > 0 ? '$' + detail.TotalCost.toFixed(4) : '$0.00' }}</td>\n <td class=\"at-run-duration\">{{ detail.Duration }}</td>\n </tr>\n }\n </tbody>\n </table>\n } @else {\n <div class=\"at-empty-state\">\n <i class=\"fa-solid fa-info-circle\"></i>\n <p>No per-source detail records found for this run.</p>\n </div>\n }\n </div>\n </div>\n }\n </div>\n }\n }\n </div>\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 SLIDE-IN FORM OVERLAY \u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (FormMode !== 'none') {\n <div class=\"at-slide-overlay\" (click)=\"CloseForm()\"></div>\n <div class=\"at-slide-panel\">\n <div class=\"at-slide-header\">\n <h3>\n @if (FormMode === 'add-source') { Add Content Source }\n @else if (FormMode === 'edit-source') { Edit Content Source }\n @else if (FormMode === 'add-type') { Add Content Type }\n @else if (FormMode === 'edit-type') { Edit Content Type }\n </h3>\n <button class=\"at-slide-close\" aria-label=\"Close form\" (click)=\"CloseForm()\"><i class=\"fa-solid fa-times\"></i></button>\n </div>\n <div class=\"at-slide-body\">\n\n <!-- Source form -->\n @if (FormMode === 'add-source' || FormMode === 'edit-source') {\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">Name</label>\n <input type=\"text\" class=\"at-form-input\" [(ngModel)]=\"FormSourceName\" placeholder=\"Source name\">\n </div>\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">Source Type</label>\n <select class=\"at-form-select\" [(ngModel)]=\"FormSourceTypeID\">\n <option value=\"\">Select source type...</option>\n @for (opt of SourceTypeOptions; track opt.ID) {\n <option [value]=\"opt.ID\">{{ opt.Name }}</option>\n }\n </select>\n </div>\n <!-- Content Type + File Type: hidden for Entity source type -->\n @if (SelectedSourceTypeRequiresContentType) {\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">Content Type</label>\n <select class=\"at-form-select\" [(ngModel)]=\"FormContentTypeID\">\n <option value=\"\">Select content type...</option>\n @for (opt of ContentTypeOptions; track opt.ID) {\n <option [value]=\"opt.ID\">{{ opt.Name }}</option>\n }\n </select>\n </div>\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">File Type</label>\n <select class=\"at-form-select\" [(ngModel)]=\"FormFileTypeID\">\n <option value=\"\">Select file type...</option>\n @for (opt of FileTypeOptions; track opt.ID) {\n <option [value]=\"opt.ID\">{{ opt.Name }}</option>\n }\n </select>\n </div>\n }\n\n <!-- Dynamic source-type-specific fields from ConfigurationObject.RequiredFields -->\n @for (field of SelectedSourceTypeFields; track field.Key) {\n @if (!field.DependsOnField || FormSourceSpecificConfig[field.DependsOnField]) {\n @if (!field.ShowOnlyIfMultiple || GetDependentOptions(field).length > 1) {\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">{{ field.Label }} @if (field.Required) { <span class=\"at-required\">*</span> }</label>\n @switch (field.Type) {\n @case ('url') {\n <input type=\"url\" class=\"at-form-input\" [(ngModel)]=\"FormSourceSpecificConfig[field.Key]\"\n [placeholder]=\"field.Description || 'https://...'\">\n }\n @case ('path') {\n <input type=\"text\" class=\"at-form-input\" [(ngModel)]=\"FormSourceSpecificConfig[field.Key]\"\n [placeholder]=\"field.Description || '/path/to/...'\">\n }\n @case ('text') {\n <input type=\"text\" class=\"at-form-input\" [(ngModel)]=\"FormSourceSpecificConfig[field.Key]\"\n [placeholder]=\"field.Description || ''\" [value]=\"field.DefaultValue || ''\">\n }\n @case ('entity-picker') {\n <select class=\"at-form-select\" [(ngModel)]=\"FormSourceSpecificConfig[field.Key]\"\n (ngModelChange)=\"OnSourceFieldChanged(field.Key)\">\n <option value=\"\">Select entity...</option>\n @for (opt of EntitiesWithDocuments; track opt.ID) {\n <option [value]=\"opt.ID\">{{ opt.Name }}</option>\n }\n </select>\n }\n @case ('entity-doc-picker') {\n <select class=\"at-form-select\" [(ngModel)]=\"FormSourceSpecificConfig[field.Key]\">\n @for (opt of GetDependentOptions(field); track opt.ID) {\n <option [value]=\"opt.ID\">{{ opt.Name }}</option>\n }\n </select>\n }\n @case ('storage-provider-picker') {\n <select class=\"at-form-select\" [(ngModel)]=\"FormSourceSpecificConfig[field.Key]\">\n <option value=\"\">Select provider...</option>\n @for (opt of StorageProviderOptions; track opt) {\n <option [value]=\"opt\">{{ opt }}</option>\n }\n </select>\n }\n @case ('dropdown') {\n <select class=\"at-form-select\" [(ngModel)]=\"FormSourceSpecificConfig[field.Key]\">\n <option value=\"\">Select...</option>\n @for (opt of field.Options || []; track opt.Value) {\n <option [value]=\"opt.Value\">{{ opt.Label }}</option>\n }\n </select>\n }\n }\n @if (field.Description) {\n <span class=\"at-form-hint\">{{ field.Description }}</span>\n }\n </div>\n }\n }\n }\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">Embedding Model Override</label>\n <mj-tree-dropdown\n [BranchConfig]=\"EmbeddingVendorBranch\"\n [LeafConfig]=\"EmbeddingModelsLeaf\"\n SelectionMode=\"single\"\n SelectableTypes=\"leaf\"\n Placeholder=\"Use system default\"\n [Clearable]=\"true\"\n [Value]=\"ToCompositeKey(FormSourceEmbeddingModelID)\"\n (ValueChange)=\"FormSourceEmbeddingModelID = FromCompositeKey($event)\">\n </mj-tree-dropdown>\n <span class=\"at-form-hint\">Overrides Content Type default</span>\n </div>\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">Vector Index Override</label>\n <select class=\"at-form-select\" [(ngModel)]=\"FormSourceVectorIndexID\">\n <option value=\"\">Use system default</option>\n @for (opt of VectorIndexOptions; track opt.ID) {\n <option [value]=\"opt.ID\">{{ opt.Name }}</option>\n }\n </select>\n <span class=\"at-form-hint\">Overrides Content Type default</span>\n </div>\n <div class=\"at-form-actions\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"SaveSource()\" [disabled]=\"FormSaving\">\n @if (FormSaving) { <i class=\"fa-solid fa-spinner fa-spin\"></i> Saving... }\n @else { <i class=\"fa-solid fa-check\"></i> Save }\n </button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"CloseForm()\">Cancel</button>\n </div>\n }\n\n <!-- Content Type form -->\n @if (FormMode === 'add-type' || FormMode === 'edit-type') {\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">Name</label>\n <input type=\"text\" class=\"at-form-input\" [(ngModel)]=\"FormTypeName\" placeholder=\"Content type name\">\n </div>\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">Description</label>\n <textarea class=\"at-form-textarea\" [(ngModel)]=\"FormTypeDescription\" rows=\"3\" placeholder=\"Description...\"></textarea>\n </div>\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">AI Model (for tagging)</label>\n <mj-tree-dropdown\n [BranchConfig]=\"AIModelVendorBranch\"\n [LeafConfig]=\"AllModelsLeaf\"\n SelectionMode=\"single\"\n SelectableTypes=\"leaf\"\n Placeholder=\"Select AI model...\"\n [Clearable]=\"true\"\n [Value]=\"ToCompositeKey(FormTypeAIModelID)\"\n (ValueChange)=\"FormTypeAIModelID = FromCompositeKey($event)\">\n </mj-tree-dropdown>\n </div>\n <div class=\"at-form-row\">\n <div class=\"at-form-group\" style=\"flex: 1;\">\n <label class=\"at-form-label\">Min Tags</label>\n <input type=\"number\" class=\"at-form-input\" [(ngModel)]=\"FormTypeMinTags\" min=\"0\">\n </div>\n <div class=\"at-form-group\" style=\"flex: 1;\">\n <label class=\"at-form-label\">Max Tags</label>\n <input type=\"number\" class=\"at-form-input\" [(ngModel)]=\"FormTypeMaxTags\" min=\"1\">\n </div>\n </div>\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">Embedding Model</label>\n <mj-tree-dropdown\n [BranchConfig]=\"EmbeddingVendorBranch\"\n [LeafConfig]=\"EmbeddingModelsLeaf\"\n SelectionMode=\"single\"\n SelectableTypes=\"leaf\"\n Placeholder=\"Use system default\"\n [Clearable]=\"true\"\n [Value]=\"ToCompositeKey(FormTypeEmbeddingModelID)\"\n (ValueChange)=\"FormTypeEmbeddingModelID = FromCompositeKey($event)\">\n </mj-tree-dropdown>\n </div>\n <div class=\"at-form-group\">\n <label class=\"at-form-label\">Vector Index</label>\n <select class=\"at-form-select\" [(ngModel)]=\"FormTypeVectorIndexID\">\n <option value=\"\">Use system default</option>\n @for (opt of VectorIndexOptions; track opt.ID) {\n <option [value]=\"opt.ID\">{{ opt.Name }}</option>\n }\n </select>\n </div>\n <div class=\"at-form-actions\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"SaveContentType()\" [disabled]=\"FormSaving\">\n @if (FormSaving) { <i class=\"fa-solid fa-spinner fa-spin\"></i> Saving... }\n @else { <i class=\"fa-solid fa-check\"></i> Save }\n </button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"CloseForm()\">Cancel</button>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 ITEM DETAIL SLIDE-IN \u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (ShowItemDetail && SelectedFeedItem) {\n <div class=\"at-slide-overlay\" (click)=\"CloseItemDetail()\"></div>\n <div class=\"at-slide-panel at-detail-panel\">\n <div class=\"at-slide-header\">\n <h3><i class=\"fa-solid fa-file-lines\"></i> Content Item</h3>\n <button class=\"at-slide-close\" aria-label=\"Close item detail\" (click)=\"CloseItemDetail()\"><i class=\"fa-solid fa-times\"></i></button>\n </div>\n <div class=\"at-slide-body\">\n <!-- Header -->\n <div class=\"at-detail-item-header\">\n <h4 class=\"at-detail-item-name\">{{ SelectedFeedItem.Name }}</h4>\n <div class=\"at-detail-badges\">\n <span class=\"at-detail-badge at-detail-badge-source\">{{ SelectedFeedItem.SourceName }}</span>\n @if (SelectedFeedItem.RequiresContentType) {\n <span class=\"at-detail-badge at-detail-badge-type\">{{ SelectedFeedItem.ContentTypeName }}</span>\n @if (SelectedFeedItem.FileTypeName) {\n <span class=\"at-detail-badge at-detail-badge-file\"><i class=\"fa-solid fa-file\"></i> {{ SelectedFeedItem.FileTypeName }}</span>\n }\n }\n </div>\n </div>\n\n <!-- URL -->\n @if (SelectedFeedItem.URL) {\n <div class=\"at-detail-section\">\n <div class=\"at-detail-section-label\">URL</div>\n <a [href]=\"SelectedFeedItem.URL\" target=\"_blank\" class=\"at-detail-link\">\n {{ SelectedFeedItem.URL }}\n <i class=\"fa-solid fa-external-link-alt\" style=\"font-size: 0.65rem; margin-left: 4px;\"></i>\n </a>\n </div>\n }\n\n <!-- Text Preview -->\n @if (SelectedFeedItem.TextContent) {\n <div class=\"at-detail-section\">\n <div class=\"at-detail-section-label\">Content Preview</div>\n <div class=\"at-detail-text-preview\">{{ SelectedFeedItem.TextContent }}</div>\n </div>\n }\n\n <!-- Tags (weighted) -->\n @if (SelectedFeedItem.Tags.length > 0) {\n <div class=\"at-detail-section\">\n <div class=\"at-detail-section-label\">Tags ({{ SelectedFeedItem.Tags.length }})</div>\n <div class=\"at-detail-tags\">\n @for (wt of SelectedFeedItem.Tags; track wt.Tag) {\n <span class=\"at-tag-pill at-tag-weighted\" [style.font-size]=\"TagFontSize(wt.Weight)\">\n {{ wt.Tag }}\n <span class=\"at-tag-weight\">{{ FormatWeight(wt.Weight) }}</span>\n </span>\n }\n </div>\n </div>\n }\n\n <!-- Metadata -->\n <div class=\"at-detail-section\">\n <div class=\"at-detail-section-label\">Metadata</div>\n <div class=\"at-detail-meta-grid\">\n @if (SelectedFeedItem.Checksum) {\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">Checksum</span>\n <span class=\"at-detail-meta-value at-detail-meta-mono\">{{ SelectedFeedItem.Checksum }}</span>\n </div>\n }\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">Created</span>\n <span class=\"at-detail-meta-value\">{{ SelectedFeedItem.CreatedAt }}</span>\n </div>\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">Updated</span>\n <span class=\"at-detail-meta-value\">{{ SelectedFeedItem.UpdatedAt }}</span>\n </div>\n </div>\n </div>\n\n <!-- Actions -->\n <div class=\"at-detail-actions\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"OpenRecordFromItem(SelectedFeedItem)\">\n <i class=\"fa-solid fa-external-link-alt\"></i> Open Record\n </button>\n <button class=\"at-action-btn at-secondary-btn\" disabled>\n <i class=\"fa-solid fa-magnifying-glass\"></i> See Similar Items\n </button>\n </div>\n </div>\n </div>\n }\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 SOURCE DETAIL SLIDE-IN \u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (ShowSourceDetail) {\n <div class=\"at-slide-overlay\" (click)=\"CloseSourceDetail()\"></div>\n <div class=\"at-slide-panel at-detail-panel\">\n <div class=\"at-slide-header\">\n <h3><i class=\"fa-solid fa-database\"></i> Source Detail</h3>\n <button class=\"at-slide-close\" aria-label=\"Close source detail\" (click)=\"CloseSourceDetail()\"><i class=\"fa-solid fa-times\"></i></button>\n </div>\n <div class=\"at-slide-body\">\n @if (SourceDetailLoading) {\n <div class=\"at-loading-overlay\">\n <mj-loading text=\"Loading source details...\"></mj-loading>\n </div>\n }\n @if (!SourceDetailLoading && SelectedSource) {\n <!-- Source Header -->\n <div class=\"at-detail-source-header\">\n <div class=\"at-source-card-icon\"><i [class]=\"SelectedSource.Icon\"></i></div>\n <div>\n <h4 class=\"at-detail-item-name\">{{ SelectedSource.Name }}</h4>\n <div class=\"at-detail-badges\">\n <span class=\"at-detail-badge at-detail-badge-type\">{{ SelectedSource.SourceTypeName }}</span>\n <span class=\"at-detail-badge\" [class]=\"'at-detail-badge-status-' + SelectedSource.StatusClass\">\n {{ SelectedSource.StatusLabel }}\n </span>\n </div>\n </div>\n </div>\n\n <!-- Configuration -->\n <div class=\"at-detail-section\">\n <div class=\"at-detail-section-label\">Configuration</div>\n <div class=\"at-detail-meta-grid\">\n @if (SelectedSource.URL) {\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">URL</span>\n <a [href]=\"SelectedSource.URL\" target=\"_blank\" class=\"at-detail-link at-detail-meta-value\">{{ SelectedSource.URL }}</a>\n </div>\n }\n @if (SelectedSource.RequiresFileType) {\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">Content Type</span>\n <span class=\"at-detail-meta-value\">{{ SelectedSource.ContentTypeName }}</span>\n </div>\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">File Type</span>\n <span class=\"at-detail-meta-value\">{{ SelectedSource.FileTypeName }}</span>\n </div>\n }\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">Embedding Model</span>\n <span class=\"at-detail-meta-value\">{{ SelectedSource.EmbeddingModelName }}</span>\n </div>\n <div class=\"at-detail-meta-row\">\n <span class=\"at-detail-meta-key\">Vector Index</span>\n <span class=\"at-detail-meta-value\">{{ SelectedSource.VectorIndexName }}</span>\n </div>\n </div>\n </div>\n\n <!-- Stats -->\n <div class=\"at-detail-section\">\n <div class=\"at-detail-section-label\">Statistics</div>\n <div class=\"at-detail-stats-strip\">\n <div class=\"at-detail-stat\">\n <div class=\"at-detail-stat-value\">{{ formatNumber(SelectedSource.ItemCount) }}</div>\n <div class=\"at-detail-stat-label\">Items</div>\n </div>\n <div class=\"at-detail-stat\">\n <div class=\"at-detail-stat-value\">{{ formatNumber(SelectedSource.TagCount) }}</div>\n <div class=\"at-detail-stat-label\">Tags</div>\n </div>\n <div class=\"at-detail-stat\">\n <div class=\"at-detail-stat-value\">{{ SelectedSource.AvgTags }}</div>\n <div class=\"at-detail-stat-label\">Avg Tags</div>\n </div>\n <div class=\"at-detail-stat\">\n <div class=\"at-detail-stat-value\">{{ SelectedSource.LastRunAgo }}</div>\n <div class=\"at-detail-stat-label\">Last Run</div>\n </div>\n <div class=\"at-detail-stat\">\n <div class=\"at-detail-stat-value\" [class.at-kpi-error-value]=\"SelectedSource.ErrorCount > 0\">{{ SelectedSource.ErrorCount }}</div>\n <div class=\"at-detail-stat-label\">Errors</div>\n </div>\n </div>\n </div>\n\n <!-- Content Library (D4 status badges, D7 pagination) -->\n <div class=\"at-detail-section\">\n <div class=\"at-detail-section-header-row\">\n <div class=\"at-detail-section-label\">Content Library ({{ FilteredSourceDetailTotal }} of {{ SelectedSource.ItemCount | number }})</div>\n <div class=\"at-detail-section-controls\">\n <select class=\"at-detail-filter-select\" [(ngModel)]=\"SourceDetailStatusFilter\" (ngModelChange)=\"OnSourceDetailStatusFilterChange()\">\n @for (opt of SourceDetailStatusOptions; track opt) {\n <option [value]=\"opt\">{{ opt }}</option>\n }\n </select>\n <button class=\"at-action-btn at-retry-btn\" (click)=\"RetryFailedItems()\" title=\"Re-queue failed items for processing\">\n <i class=\"fa-solid fa-rotate-right\"></i> Retry Failed\n </button>\n </div>\n </div>\n <div class=\"at-detail-content-list\">\n @if (FilteredSourceDetailItems.length === 0) {\n <div class=\"at-empty-state\" style=\"padding: 16px;\">\n <p>{{ SourceDetailStatusFilter === 'All' ? 'No content items yet.' : 'No items match this filter.' }}</p>\n </div>\n }\n @for (ci of FilteredSourceDetailItems; track ci.ID) {\n <div class=\"at-detail-content-item\" (click)=\"OpenContentItemDetail(ci)\">\n <div class=\"at-feed-status-dot\" [class]=\"ci.StatusDot\"></div>\n <span class=\"at-detail-content-item-name\">{{ ci.Name }}</span>\n <span class=\"at-status-badge\" [class]=\"GetStatusBadgeClass(ci.EmbeddingStatus)\">E:{{ ci.EmbeddingStatus }}</span>\n <span class=\"at-status-badge\" [class]=\"GetStatusBadgeClass(ci.TaggingStatus)\">T:{{ ci.TaggingStatus }}</span>\n <span class=\"at-detail-content-item-tags\">{{ ci.TagCount }} tags</span>\n <span class=\"at-detail-content-item-time\">{{ ci.UpdatedAt }}</span>\n </div>\n }\n </div>\n <!-- Pagination -->\n @if (SourceDetailTotalPages > 1) {\n <div class=\"at-detail-pagination\">\n <button class=\"at-page-btn\" [disabled]=\"SourceDetailPage === 0\" (click)=\"SourceDetailPrevPage()\">\n <i class=\"fa-solid fa-chevron-left\"></i> Prev\n </button>\n <span class=\"at-page-info\">Page {{ SourceDetailPage + 1 }} of {{ SourceDetailTotalPages }}</span>\n <button class=\"at-page-btn\" [disabled]=\"SourceDetailPage >= SourceDetailTotalPages - 1\" (click)=\"SourceDetailNextPage()\">\n Next <i class=\"fa-solid fa-chevron-right\"></i>\n </button>\n </div>\n }\n </div>\n\n <!-- Run History -->\n @if (SelectedSource.RunHistory.length > 0) {\n <div class=\"at-detail-section\">\n <div class=\"at-detail-section-label\">Recent Runs</div>\n <div class=\"at-detail-run-history\">\n @for (run of SelectedSource.RunHistory; track run.ID) {\n <div class=\"at-detail-run-row\">\n <span class=\"at-run-status-badge\" [class]=\"run.StatusClass\">{{ run.Status }}</span>\n <span class=\"at-detail-run-time\">{{ run.StartedDisplay }}</span>\n <span class=\"at-detail-run-duration\">{{ run.Duration }}</span>\n <span class=\"at-detail-run-items\">{{ run.Items }} items</span>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- Actions -->\n <div class=\"at-detail-actions\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"OpenEditSourceFromDetail()\">\n <i class=\"fa-solid fa-pen\"></i> Edit\n </button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"RunSourceFromDetail()\">\n <i class=\"fa-solid fa-play\"></i> Run Now\n </button>\n <button class=\"at-action-btn at-secondary-btn at-source-delete-btn\" (click)=\"DeleteSourceFromDetail()\">\n <i class=\"fa-solid fa-trash\"></i> Delete\n </button>\n </div>\n }\n </div>\n </div>\n }\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 SCHEDULE DIALOG \u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (ShowScheduleDialog && SchedulingSourceCard) {\n <div class=\"at-schedule-overlay\" (click)=\"CloseScheduleDialog()\">\n <div class=\"at-schedule-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"at-schedule-dialog-header\">\n <h3><i class=\"fa-regular fa-clock\"></i> Schedule Pipeline</h3>\n <button class=\"at-schedule-dialog-close\" (click)=\"CloseScheduleDialog()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div class=\"at-schedule-dialog-body\">\n <div class=\"at-schedule-field\">\n <label>Source</label>\n <div class=\"at-schedule-source-name\">\n <i [class]=\"SchedulingSourceCard.Icon\"></i>\n {{ SchedulingSourceCard.Name }}\n </div>\n </div>\n <div class=\"at-schedule-field\">\n <label>Action</label>\n <div class=\"at-schedule-action-name\">Autotag and Vectorize Content</div>\n </div>\n <div class=\"at-schedule-field\">\n <label for=\"schedule-cron\">Cron Expression</label>\n <input id=\"schedule-cron\"\n type=\"text\"\n class=\"mj-input at-schedule-cron-input\"\n [(ngModel)]=\"ScheduleCron\"\n placeholder=\"0 2 * * *\" />\n <div class=\"at-schedule-cron-preview\">\n <i class=\"fa-solid fa-info-circle\"></i>\n {{ GetCronPreview(ScheduleCron) }}\n </div>\n </div>\n <div class=\"at-schedule-field at-schedule-toggle-row\">\n <label for=\"schedule-enabled\">Enabled</label>\n <input id=\"schedule-enabled\"\n type=\"checkbox\"\n class=\"mj-checkbox\"\n [(ngModel)]=\"ScheduleEnabled\" />\n </div>\n </div>\n <div class=\"at-schedule-dialog-footer\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"SaveSchedule()\" [disabled]=\"ScheduleSaving\">\n @if (ScheduleSaving) {\n <i class=\"fa-solid fa-spinner fa-spin\"></i> Saving...\n } @else {\n <i class=\"fa-solid fa-check\"></i> Create Schedule\n }\n </button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"CloseScheduleDialog()\">Cancel</button>\n </div>\n </div>\n </div>\n }\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 CONFIRMATION DIALOG \u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (ShowConfirmDialog) {\n <div class=\"at-schedule-overlay\" (click)=\"ConfirmDialogCancel()\">\n <div class=\"at-schedule-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"at-schedule-dialog-header\">\n <h3><i class=\"fa-solid fa-triangle-exclamation\"></i> {{ ConfirmDialogTitle }}</h3>\n <button class=\"at-schedule-dialog-close\" (click)=\"ConfirmDialogCancel()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div class=\"at-schedule-dialog-body\">\n <p class=\"at-confirm-message\">{{ ConfirmDialogMessage }}</p>\n </div>\n <div class=\"at-schedule-dialog-footer\">\n <button class=\"at-action-btn at-danger-btn\" (click)=\"ConfirmDialogAccept()\">\n <i class=\"fa-solid fa-check\"></i> Confirm\n </button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"ConfirmDialogCancel()\">Cancel</button>\n </div>\n </div>\n </div>\n }\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 SPLIT DIALOG \u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (ShowSplitDialog) {\n <div class=\"at-schedule-overlay\" (click)=\"CloseSplitDialog()\">\n <div class=\"at-schedule-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"at-schedule-dialog-header\">\n <h3><i class=\"fa-solid fa-code-branch\"></i> Split Tag</h3>\n <button class=\"at-schedule-dialog-close\" (click)=\"CloseSplitDialog()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div class=\"at-schedule-dialog-body\">\n <div class=\"at-schedule-field\">\n <label>New tag names (comma-separated)</label>\n <input type=\"text\" class=\"mj-input\" style=\"width: 100%;\"\n [(ngModel)]=\"SplitChildNames\"\n placeholder=\"Tag A, Tag B, Tag C\" />\n </div>\n <p class=\"at-confirm-message\">New tags will be created as siblings of the original tag under the same parent.</p>\n </div>\n <div class=\"at-schedule-dialog-footer\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"ExecuteSplit()\" [disabled]=\"!SplitChildNames.trim()\">\n <i class=\"fa-solid fa-code-branch\"></i> Create Tags\n </button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"CloseSplitDialog()\">Cancel</button>\n </div>\n </div>\n </div>\n }\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550 MOVE DIALOG \u2550\u2550\u2550\u2550\u2550\u2550 -->\n @if (ShowCreateTagDialog) {\n <div class=\"at-schedule-overlay\" (click)=\"CloseCreateTagDialog()\">\n <div class=\"at-schedule-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"at-schedule-dialog-header\">\n <h3><i class=\"fa-solid fa-plus\"></i> Create Tag</h3>\n <button class=\"at-schedule-dialog-close\" (click)=\"CloseCreateTagDialog()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div class=\"at-schedule-dialog-body\">\n <div class=\"at-tax-create-context\">{{ CreateTagParentLabel }}</div>\n <div class=\"at-schedule-field\">\n <label>Name</label>\n <input type=\"text\" class=\"mj-input\" style=\"width: 100%;\" [(ngModel)]=\"CreateTagName\"\n placeholder=\"Tag name\" (keydown.enter)=\"SaveNewTag()\">\n </div>\n <div class=\"at-schedule-field\">\n <label>Description (optional)</label>\n <textarea class=\"mj-textarea\" rows=\"3\" style=\"width: 100%;\" [(ngModel)]=\"CreateTagDescription\"\n placeholder=\"Brief description of this tag\"></textarea>\n </div>\n </div>\n <div class=\"at-schedule-dialog-footer\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"SaveNewTag()\" [disabled]=\"!CreateTagName.trim()\">\n <i class=\"fa-solid fa-check\"></i> Create\n </button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"CloseCreateTagDialog()\">Cancel</button>\n </div>\n </div>\n </div>\n }\n\n @if (ShowMoveDialog) {\n <div class=\"at-schedule-overlay\" (click)=\"CloseMoveDialog()\">\n <div class=\"at-schedule-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"at-schedule-dialog-header\">\n <h3><i class=\"fa-solid fa-arrows-up-down\"></i> Move Tag</h3>\n <button class=\"at-schedule-dialog-close\" (click)=\"CloseMoveDialog()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div class=\"at-schedule-dialog-body\">\n <div class=\"at-schedule-field\">\n <label>New parent tag</label>\n <select class=\"mj-input\" style=\"width: 100%;\" [(ngModel)]=\"MoveNewParentID\">\n <option [ngValue]=\"null\">(Root level — no parent)</option>\n @for (opt of GetMoveTargetOptions(); track opt.ID) {\n <option [ngValue]=\"opt.ID\">{{ '\\u00A0\\u00A0'.repeat(opt.Depth) }}{{ opt.Name }}</option>\n }\n </select>\n </div>\n </div>\n <div class=\"at-schedule-dialog-footer\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"ExecuteMove()\">\n <i class=\"fa-solid fa-check\"></i> Move\n </button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"CloseMoveDialog()\">Cancel</button>\n </div>\n </div>\n </div>\n }\n\n @if (ShowMergeIntoDialog) {\n <div class=\"at-schedule-overlay\" (click)=\"CloseMergeIntoDialog()\">\n <div class=\"at-schedule-dialog\" (click)=\"$event.stopPropagation()\">\n <div class=\"at-schedule-dialog-header\">\n <h3><i class=\"fa-solid fa-compress\"></i> Merge \"{{ MergeSourceTag?.Name }}\" Into...</h3>\n <button class=\"at-schedule-dialog-close\" (click)=\"CloseMergeIntoDialog()\">\n <i class=\"fa-solid fa-times\"></i>\n </button>\n </div>\n <div class=\"at-schedule-dialog-body\">\n <p style=\"font-size: 0.85rem; color: var(--mj-text-secondary); margin: 0 0 12px;\">\n All tagged items will be moved to the target tag, then \"{{ MergeSourceTag?.Name }}\" will be deleted.\n </p>\n <div class=\"at-schedule-field\">\n <label>Merge into</label>\n <mj-combobox\n [Data]=\"MergeTargetData\"\n TextField=\"Label\"\n ValueField=\"ID\"\n [Filterable]=\"true\"\n [ValuePrimitive]=\"true\"\n Placeholder=\"Search and select a tag...\"\n (ValueChange)=\"OnMergeTargetSelected($event)\">\n </mj-combobox>\n </div>\n </div>\n <div class=\"at-schedule-dialog-footer\">\n <button class=\"at-action-btn at-primary-btn\" (click)=\"ExecuteMergeInto()\" [disabled]=\"!MergeTargetID || IsMerging\">\n @if (IsMerging) { <i class=\"fa-solid fa-spinner fa-spin\"></i> Merging... } @else { <i class=\"fa-solid fa-compress\"></i> Merge }\n </button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"CloseMergeIntoDialog()\">Cancel</button>\n </div>\n </div>\n </div>\n }\n\n <!-- No Content Types Warning Dialog (z-index above slide-in panel) -->\n @if (ShowNoContentTypeWarning) {\n <div class=\"at-schedule-dialog-overlay\" style=\"z-index: 10001;\" (click)=\"ShowNoContentTypeWarning = false\">\n <div class=\"at-schedule-dialog\" style=\"max-width: 440px; z-index: 10002;\" (click)=\"$event.stopPropagation()\">\n <div class=\"at-schedule-dialog-header\">\n <span><i class=\"fa-solid fa-triangle-exclamation\" style=\"color: var(--mj-status-warning); margin-right: 8px;\"></i> Content Type Required</span>\n <button class=\"at-close-btn\" aria-label=\"Close warning\" (click)=\"ShowNoContentTypeWarning = false\"><i class=\"fa-solid fa-xmark\"></i></button>\n </div>\n <div class=\"at-schedule-dialog-body\" style=\"text-align: center; padding: 24px;\">\n <p style=\"color: var(--mj-text-secondary); margin-bottom: 16px;\">\n No content types have been configured yet. At least one content type is required before you can create a content source.\n </p>\n <p style=\"color: var(--mj-text-muted); font-size: 13px;\">\n Go to the <strong>Content Types</strong> tab to create one.\n </p>\n </div>\n <div class=\"at-schedule-dialog-footer\">\n <button class=\"at-action-btn at-primary-btn\" (mousedown)=\"ShowNoContentTypeWarning = false; CloseForm(); SwitchTab('types')\">\n <i class=\"fa-solid fa-arrow-right\"></i> Go to Content Types\n </button>\n <button class=\"at-action-btn at-secondary-btn\" (click)=\"ShowNoContentTypeWarning = false\">Close</button>\n </div>\n </div>\n </div>\n }\n</div>\n", styles: ["/* ============================================================\n Content Autotagging Dashboard\n All colors use MJ design tokens \u2014 no hardcoded hex values.\n All classes prefixed with at- to prevent leaking (ViewEncapsulation.None).\n ============================================================ */\n\n/* \u2500\u2500 Root layout \u2500\u2500 */\n\n.at-dashboard {\n display: flex;\n height: 100%;\n overflow: hidden;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 LEFT NAV \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-left-nav {\n width: 220px;\n background: var(--mj-bg-surface);\n border-right: 1px solid var(--mj-border-default);\n display: flex;\n flex-direction: column;\n flex-shrink: 0;\n}\n\n.at-left-nav-header {\n padding: 16px 16px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.at-left-nav-header h2 {\n font-size: 0.95rem;\n font-weight: 700;\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0;\n color: var(--mj-text-primary);\n}\n\n.at-left-nav-header h2 i {\n color: var(--mj-brand-primary);\n}\n\n.at-left-nav-items {\n flex: 1;\n padding: 8px;\n overflow-y: auto;\n}\n\n.at-nav-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 12px;\n border-radius: 8px;\n font-size: 0.82rem;\n font-weight: 500;\n color: var(--mj-text-muted);\n cursor: pointer;\n transition: all 0.12s ease;\n margin-bottom: 2px;\n}\n\n.at-nav-item:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-secondary);\n}\n\n.at-nav-item.active {\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.at-nav-item i {\n width: 18px;\n text-align: center;\n font-size: 0.8rem;\n}\n\n.at-nav-badge {\n margin-left: auto;\n background: var(--mj-bg-surface-sunken);\n padding: 1px 7px;\n border-radius: 10px;\n font-size: 0.65rem;\n font-weight: 600;\n color: var(--mj-text-muted);\n}\n\n.at-nav-item.active .at-nav-badge {\n background: color-mix(in srgb, var(--mj-brand-primary) 18%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.at-nav-badge-live {\n background: var(--mj-status-success-bg) !important;\n color: var(--mj-status-success-text) !important;\n}\n\n.at-nav-divider {\n height: 1px;\n background: var(--mj-border-subtle);\n margin: 8px 12px;\n}\n\n.at-left-nav-footer {\n padding: 12px;\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n.at-run-pipeline-btn {\n width: 100%;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 10px;\n border: none;\n border-radius: 8px;\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n font-size: 0.82rem;\n font-weight: 600;\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.at-run-pipeline-btn:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n}\n\n.at-run-pipeline-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 MAIN CONTENT AREA \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-main-area {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n.at-loading-overlay {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 100%;\n}\n\n.at-page-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 14px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.at-page-title {\n font-size: 1rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.at-page-subtitle {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n.at-page-actions {\n display: flex;\n gap: 8px;\n align-items: center;\n}\n\n.at-page-body {\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n padding: 16px 20px;\n min-height: 0;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 SHARED COMPONENTS \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-action-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 7px 14px;\n border: 1px solid;\n border-radius: 7px;\n cursor: pointer;\n font-size: 0.78rem;\n font-weight: 500;\n transition: all 0.15s ease;\n}\n\n.at-primary-btn {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-color: var(--mj-brand-primary);\n}\n\n.at-primary-btn:hover:not(:disabled) {\n background: var(--mj-brand-primary-hover);\n}\n\n.at-primary-btn:disabled {\n opacity: 0.6;\n cursor: not-allowed;\n}\n\n.at-secondary-btn {\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n border-color: var(--mj-border-default);\n}\n\n.at-secondary-btn:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.at-card {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 10px;\n overflow: hidden;\n}\n\n.at-card-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.at-card-title {\n font-size: 0.82rem;\n font-weight: 600;\n display: flex;\n align-items: center;\n gap: 6px;\n color: var(--mj-text-primary);\n}\n\n.at-card-title i {\n color: var(--mj-brand-primary);\n font-size: 0.75rem;\n}\n\n.at-card-body {\n padding: 0;\n}\n\n.at-empty-state {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 32px 16px;\n color: var(--mj-text-disabled);\n}\n\n.at-empty-state i {\n font-size: 28px;\n}\n\n.at-empty-state p {\n margin: 0;\n font-size: 13px;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 KPI STRIP \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-kpi-strip {\n display: flex;\n gap: 12px;\n margin-bottom: 16px;\n}\n\n.at-kpi-card {\n flex: 1;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 10px;\n padding: 14px 16px;\n}\n\n.at-kpi-value {\n font-size: 1.4rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.at-kpi-error-value {\n color: var(--mj-status-error-text);\n}\n\n.at-kpi-label {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n.at-kpi-trend {\n font-size: 0.68rem;\n margin-top: 4px;\n display: flex;\n align-items: center;\n gap: 4px;\n color: var(--mj-text-muted);\n}\n\n.at-kpi-trend.up {\n color: var(--mj-status-success-text);\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 PIPELINE TAB \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-pipeline-layout {\n display: flex;\n gap: 16px;\n flex: 1;\n min-height: 0;\n}\n\n.at-pipeline-center {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 12px;\n min-width: 0;\n min-height: 0;\n}\n\n.at-pipeline-right {\n width: 320px;\n flex-shrink: 0;\n display: flex;\n flex-direction: column;\n gap: 12px;\n}\n\n/* Pipeline Stages */\n\n.at-pipeline-stages {\n display: flex;\n gap: 0;\n align-items: center;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 10px;\n padding: 12px 16px;\n}\n\n.at-pipeline-stage {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 4px;\n}\n\n.at-pipeline-stage-icon {\n width: 36px;\n height: 36px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 50%;\n font-size: 0.85rem;\n}\n\n.stage-idle .at-pipeline-stage-icon {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n.stage-active .at-pipeline-stage-icon {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n animation: at-pulse 1.5s infinite;\n}\n\n.stage-complete .at-pipeline-stage-icon {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n}\n\n@keyframes at-pulse {\n 0%, 100% { box-shadow: 0 0 0 0 color-mix(in srgb, var(--mj-brand-primary) 30%, transparent); }\n 50% { box-shadow: 0 0 0 6px color-mix(in srgb, var(--mj-brand-primary) 0%, transparent); }\n}\n\n.at-pipeline-stage-name {\n font-size: 0.68rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n}\n\n.at-pipeline-stage-count {\n font-size: 0.6rem;\n color: var(--mj-text-muted);\n}\n\n.at-pipeline-arrow {\n width: 24px;\n text-align: center;\n color: var(--mj-text-disabled);\n font-size: 0.6rem;\n}\n\n.at-stage-connector {\n width: 32px;\n height: 2px;\n background: var(--mj-border-default);\n transition: background 0.4s ease;\n}\n\n.at-stage-connector.connector-complete {\n background: var(--mj-status-success);\n}\n\n/* Progress section */\n\n.at-progress-section {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n padding: 10px 16px;\n}\n\n.at-progress-header {\n display: flex;\n justify-content: space-between;\n font-size: 0.75rem;\n margin-bottom: 4px;\n}\n\n.at-progress-stage-label {\n color: var(--mj-text-secondary);\n font-weight: 500;\n}\n\n.at-progress-pct {\n color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.at-progress-bar {\n height: 4px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 2px;\n overflow: hidden;\n}\n\n.at-progress-fill {\n height: 100%;\n background: var(--mj-brand-primary);\n border-radius: 2px;\n transition: width 0.3s ease;\n}\n\n.at-progress-current {\n font-size: 0.68rem;\n color: var(--mj-text-muted);\n margin-top: 4px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.at-progress-fill-paused {\n background: var(--mj-status-warning);\n animation: pulse-paused 1.5s ease-in-out infinite;\n}\n\n@keyframes pulse-paused {\n 0%, 100% { opacity: 1; }\n 50% { opacity: 0.5; }\n}\n\n.at-pipeline-controls {\n display: flex;\n gap: 8px;\n margin-top: 8px;\n justify-content: flex-end;\n}\n\n.at-danger-btn {\n background: var(--mj-status-error);\n color: var(--mj-text-inverse);\n border-color: var(--mj-status-error);\n}\n\n.at-danger-btn:hover:not(:disabled) {\n background: var(--mj-status-error-text);\n border-color: var(--mj-status-error-text);\n}\n\n/* Feed items */\n\n.at-feed-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n border-bottom: 1px solid var(--mj-border-subtle);\n font-size: 0.78rem;\n}\n\n.at-feed-item:last-child {\n border-bottom: none;\n}\n\n.at-feed-status-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.at-feed-status-dot.complete {\n background: var(--mj-status-success);\n}\n\n.at-feed-status-dot.processing {\n background: var(--mj-brand-primary);\n animation: at-pulse 1.5s infinite;\n}\n\n.at-feed-status-dot.error {\n background: var(--mj-status-error);\n}\n\n.at-feed-item-name {\n flex: 1;\n font-weight: 500;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--mj-text-primary);\n}\n\n.at-feed-item-source {\n color: var(--mj-text-muted);\n font-size: 0.7rem;\n min-width: 100px;\n}\n\n.at-feed-item-tags {\n display: flex;\n gap: 4px;\n}\n\n.at-feed-tag {\n padding: 1px 6px;\n border-radius: 3px;\n font-size: 0.62rem;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.at-feed-item-time {\n color: var(--mj-text-muted);\n font-size: 0.68rem;\n white-space: nowrap;\n min-width: 60px;\n text-align: right;\n}\n\n/* Source mini cards (right panel) */\n\n.at-source-mini {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n border-bottom: 1px solid var(--mj-border-subtle);\n cursor: pointer;\n transition: background 0.1s ease;\n}\n\n.at-source-mini:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.at-source-mini:last-child {\n border-bottom: none;\n}\n\n.at-source-mini-icon {\n width: 28px;\n height: 28px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 6px;\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 0.72rem;\n flex-shrink: 0;\n}\n\n.at-source-mini-info {\n flex: 1;\n min-width: 0;\n}\n\n.at-source-mini-name {\n font-size: 0.78rem;\n font-weight: 600;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n color: var(--mj-text-primary);\n}\n\n.at-source-mini-meta {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n}\n\n.at-source-mini-status {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.at-source-mini-status.active {\n background: var(--mj-status-success);\n}\n\n.at-source-mini-status.error {\n background: var(--mj-status-error);\n}\n\n.at-source-mini-status.inactive {\n background: var(--mj-text-disabled);\n}\n\n/* Tag cloud card */\n\n.at-tag-cloud-card {\n padding: 16px;\n}\n\n.at-tag-cloud {\n display: flex;\n flex-wrap: wrap;\n gap: 5px;\n}\n\n.at-tag-pill {\n padding: 4px 12px;\n border-radius: 14px;\n font-size: 0.72rem;\n font-weight: 500;\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n color: var(--mj-text-secondary);\n border: 1px solid var(--mj-border-subtle);\n cursor: pointer;\n transition: all 0.12s ease;\n}\n\n.at-tag-pill:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.at-tag-weighted {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n}\n\n.at-tag-weight {\n font-size: 0.6rem;\n opacity: 0.6;\n font-weight: 400;\n}\n\n.at-tag-row-clickable {\n cursor: pointer;\n transition: background 0.1s ease;\n}\n\n.at-tag-row-clickable:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.at-tag-row-selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface)) !important;\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n.at-tag-pill.large {\n font-size: 0.85rem;\n padding: 5px 14px;\n}\n\n.at-tag-pill.small {\n font-size: 0.65rem;\n padding: 3px 8px;\n color: var(--mj-text-muted);\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 SOURCES TAB \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-sources-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(320px, 1fr));\n gap: 12px;\n}\n\n.at-source-card-full {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 10px;\n padding: 16px;\n transition: border-color 0.15s ease;\n}\n\n.at-source-card-full:hover {\n border-color: var(--mj-border-default);\n}\n\n.at-source-card-header {\n display: flex;\n align-items: center;\n gap: 10px;\n margin-bottom: 12px;\n}\n\n.at-source-card-icon {\n width: 40px;\n height: 40px;\n display: flex;\n align-items: center;\n justify-content: center;\n border-radius: 10px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n font-size: 1rem;\n flex-shrink: 0;\n}\n\n.at-source-card-title {\n font-size: 0.9rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.at-source-card-type {\n font-size: 0.68rem;\n color: var(--mj-text-muted);\n}\n\n.at-source-card-status {\n margin-left: auto;\n display: flex;\n align-items: center;\n gap: 5px;\n font-size: 0.7rem;\n font-weight: 500;\n}\n\n.at-source-card-status.active {\n color: var(--mj-status-success-text);\n}\n\n.at-source-card-status.error {\n color: var(--mj-status-error-text);\n}\n\n.at-source-card-status.inactive {\n color: var(--mj-text-disabled);\n}\n\n.at-source-card-url {\n font-size: 0.7rem;\n color: var(--mj-brand-primary);\n margin-bottom: 10px;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.at-source-card-stats {\n display: flex;\n gap: 16px;\n padding-top: 10px;\n border-top: 1px solid var(--mj-border-subtle);\n}\n\n.at-source-stat {\n display: flex;\n flex-direction: column;\n}\n\n.at-source-stat-value {\n font-size: 1rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.at-source-stat-label {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n}\n\n.at-source-card-actions {\n display: flex;\n gap: 6px;\n margin-top: 10px;\n}\n\n.at-source-action-btn {\n flex: 1;\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 4px;\n padding: 6px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 0.7rem;\n cursor: pointer;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n transition: all 0.12s ease;\n}\n\n.at-source-action-btn:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.at-source-delete-btn:hover {\n border-color: var(--mj-status-error);\n color: var(--mj-status-error-text);\n}\n\n.at-add-source-card {\n background: none;\n border: 2px dashed var(--mj-border-default);\n border-radius: 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 40px;\n cursor: pointer;\n transition: all 0.15s ease;\n color: var(--mj-text-muted);\n min-height: 200px;\n}\n\n.at-add-source-card:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.at-add-source-card i {\n font-size: 1.5rem;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 CONTENT TYPES TAB \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-ct-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));\n gap: 12px;\n}\n\n.at-ct-card {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 10px;\n padding: 16px;\n}\n\n.at-ct-card-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 10px;\n}\n\n.at-ct-card-name {\n font-size: 0.9rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.at-ct-card-model {\n font-size: 0.68rem;\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.at-ct-field {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 6px 0;\n border-bottom: 1px solid var(--mj-border-subtle);\n font-size: 0.78rem;\n}\n\n.at-ct-field:last-child {\n border-bottom: none;\n}\n\n.at-ct-field-label {\n color: var(--mj-text-muted);\n}\n\n.at-ct-field-value {\n font-weight: 500;\n color: var(--mj-text-primary);\n}\n\n.at-ct-tag-range {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-top: 8px;\n padding: 8px 10px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 6px;\n font-size: 0.75rem;\n color: var(--mj-text-primary);\n}\n\n.at-ct-tag-range i {\n color: var(--mj-brand-primary);\n}\n\n.at-ct-tag-range-bar {\n flex: 1;\n height: 6px;\n background: var(--mj-bg-surface);\n border-radius: 3px;\n position: relative;\n}\n\n.at-ct-tag-range-fill {\n position: absolute;\n height: 100%;\n background: var(--mj-brand-primary);\n border-radius: 3px;\n}\n\n.at-ct-range-suffix {\n color: var(--mj-text-muted);\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 TAG LIBRARY TAB \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-tag-lib-layout {\n display: flex;\n gap: 16px;\n}\n\n.at-tag-lib-main {\n flex: 1;\n}\n\n.at-tag-lib-sidebar {\n width: 280px;\n flex-shrink: 0;\n}\n\n.at-tag-table {\n width: 100%;\n border-collapse: collapse;\n}\n\n.at-tag-table th {\n text-align: left;\n padding: 8px 12px;\n font-size: 0.7rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--mj-text-muted);\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-elevated);\n position: sticky;\n top: 0;\n z-index: 1;\n}\n\n.at-tag-table td {\n padding: 10px 12px;\n font-size: 0.8rem;\n border-bottom: 1px solid var(--mj-border-subtle);\n color: var(--mj-text-primary);\n}\n\n.at-tag-table tr:hover td {\n background: var(--mj-bg-surface-hover);\n}\n\n.at-tag-name-cell {\n font-weight: 600;\n}\n\n.at-tag-bar {\n width: 80px;\n height: 6px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 3px;\n display: inline-block;\n vertical-align: middle;\n}\n\n.at-tag-bar-fill {\n height: 100%;\n background: var(--mj-brand-primary);\n border-radius: 3px;\n}\n\n/* Weight indicator in tag table */\n.at-weight-indicator {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.at-weight-bar {\n width: 50px;\n height: 6px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 3px;\n overflow: hidden;\n}\n\n.at-weight-bar-fill {\n height: 100%;\n border-radius: 3px;\n transition: width 0.2s ease;\n}\n\n.at-weight-bar-fill.at-weight-high {\n background: var(--mj-status-success);\n}\n\n.at-weight-bar-fill.at-weight-medium {\n background: var(--mj-status-warning);\n}\n\n.at-weight-bar-fill.at-weight-low {\n background: var(--mj-status-error);\n}\n\n.at-weight-value {\n font-size: 0.7rem;\n font-weight: 600;\n color: var(--mj-text-muted);\n min-width: 28px;\n}\n\n.at-tags-by-source {\n font-size: 0.78rem;\n}\n\n.at-tag-source-row {\n display: flex;\n justify-content: space-between;\n padding: 5px 0;\n border-bottom: 1px solid var(--mj-border-subtle);\n color: var(--mj-text-primary);\n}\n\n.at-tag-source-row:last-child {\n border-bottom: none;\n}\n\n.at-search-input {\n padding: 7px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 7px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n font-size: 0.8rem;\n width: 200px;\n outline: none;\n}\n\n.at-search-input::placeholder {\n color: var(--mj-text-disabled);\n}\n\n.at-search-input:focus {\n border-color: var(--mj-brand-primary);\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 RUN HISTORY TAB \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-run-table {\n width: 100%;\n border-collapse: collapse;\n}\n\n.at-run-table th {\n text-align: left;\n padding: 10px 14px;\n font-size: 0.7rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n color: var(--mj-text-muted);\n border-bottom: 1px solid var(--mj-border-default);\n background: var(--mj-bg-surface-elevated);\n position: sticky;\n top: 0;\n z-index: 1;\n}\n\n.at-run-table td {\n padding: 12px 14px;\n font-size: 0.8rem;\n border-bottom: 1px solid var(--mj-border-subtle);\n color: var(--mj-text-primary);\n}\n\n.at-run-table tr:hover td {\n background: var(--mj-bg-surface-hover);\n cursor: pointer;\n}\n\n.at-run-status-badge {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 2px 10px;\n border-radius: 10px;\n font-size: 0.7rem;\n font-weight: 600;\n}\n\n.at-run-status-badge.complete {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n}\n\n.at-run-status-badge.failed {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n}\n\n.at-run-status-badge.running {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.at-run-duration {\n color: var(--mj-text-muted);\n font-size: 0.75rem;\n}\n\n.at-run-source-name {\n font-weight: 500;\n}\n\n.at-run-error-text {\n color: var(--mj-status-error-text);\n}\n\n.at-filter-select {\n padding: 7px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 7px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n font-size: 0.78rem;\n outline: none;\n}\n\n.at-filter-select:focus {\n border-color: var(--mj-brand-primary);\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 SLIDE-IN FORM PANEL \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-slide-overlay {\n position: fixed;\n top: 0;\n left: 0;\n right: 0;\n bottom: 0;\n background: var(--mj-bg-overlay);\n z-index: 1000;\n}\n\n.at-slide-panel {\n position: fixed;\n right: 0;\n top: 0;\n bottom: 0;\n width: 420px;\n max-width: 100vw;\n background: var(--mj-bg-surface);\n border-left: 1px solid var(--mj-border-default);\n z-index: 1001;\n display: flex;\n flex-direction: column;\n box-shadow: -4px 0 24px color-mix(in srgb, var(--mj-text-primary) 15%, transparent);\n}\n\n.at-slide-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid var(--mj-border-subtle);\n}\n\n.at-slide-header h3 {\n margin: 0;\n font-size: 1rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.at-slide-close {\n background: none;\n border: none;\n font-size: 1.1rem;\n color: var(--mj-text-muted);\n cursor: pointer;\n padding: 4px;\n}\n\n.at-slide-close:hover {\n color: var(--mj-text-primary);\n}\n\n.at-slide-body {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n}\n\n.at-form-group {\n margin-bottom: 16px;\n}\n\n.at-form-label {\n display: block;\n font-size: 0.78rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n margin-bottom: 6px;\n}\n\n.at-form-input,\n.at-form-select,\n.at-form-textarea {\n width: 100%;\n padding: 9px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 7px;\n background: var(--mj-bg-surface-card);\n color: var(--mj-text-primary);\n font-size: 0.82rem;\n outline: none;\n font-family: inherit;\n}\n\n.at-form-input:focus,\n.at-form-select:focus,\n.at-form-textarea:focus {\n border-color: var(--mj-brand-primary);\n}\n\n.at-form-input::placeholder,\n.at-form-textarea::placeholder {\n color: var(--mj-text-disabled);\n}\n\n.at-form-textarea {\n resize: vertical;\n}\n\n.at-form-row {\n display: flex;\n gap: 12px;\n}\n\n.at-form-actions {\n display: flex;\n gap: 8px;\n margin-top: 24px;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 RESPONSIVE \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n@media (max-width: 1100px) {\n .at-pipeline-layout {\n flex-direction: column;\n }\n\n .at-pipeline-right {\n width: 100%;\n flex-direction: row;\n }\n\n .at-tag-lib-layout {\n flex-direction: column;\n }\n\n .at-tag-lib-sidebar {\n width: 100%;\n }\n}\n\n@media (max-width: 768px) {\n .at-left-nav {\n width: 180px;\n }\n\n .at-kpi-strip {\n flex-wrap: wrap;\n }\n\n .at-kpi-card {\n min-width: 140px;\n }\n\n .at-sources-grid {\n grid-template-columns: 1fr;\n }\n\n .at-ct-grid {\n grid-template-columns: 1fr;\n }\n\n .at-slide-panel {\n width: 100vw;\n }\n}\n\n@media (max-width: 480px) {\n .at-left-nav {\n width: 56px;\n }\n\n .at-left-nav-header h2 {\n font-size: 0;\n }\n\n .at-left-nav-header h2 i {\n font-size: 1rem;\n }\n\n .at-nav-item {\n justify-content: center;\n padding: 10px;\n font-size: 0;\n }\n\n .at-nav-item i {\n font-size: 1rem;\n }\n\n .at-nav-badge {\n display: none;\n }\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n Slide-in Form Panel\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-slide-overlay {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0.35);\n z-index: 9999;\n animation: at-fade-in 0.2s ease;\n}\n\n@keyframes at-fade-in { from { opacity: 0; } to { opacity: 1; } }\n\n.at-slide-panel {\n position: fixed;\n top: 0;\n right: 0;\n bottom: 0;\n width: 420px;\n background: var(--mj-bg-surface);\n border-left: 1px solid var(--mj-border-default);\n box-shadow: -8px 0 40px rgba(0, 0, 0, 0.4);\n z-index: 10000;\n display: flex;\n flex-direction: column;\n animation: at-slide-in 0.25s cubic-bezier(0.4, 0, 0.2, 1);\n}\n\n@keyframes at-slide-in { from { transform: translateX(100%); } to { transform: translateX(0); } }\n\n.at-slide-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n flex-shrink: 0;\n}\n\n.at-slide-header h3 {\n font-size: 1rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.at-slide-close {\n width: 32px;\n height: 32px;\n display: flex;\n align-items: center;\n justify-content: center;\n border: none;\n border-radius: 6px;\n cursor: pointer;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n font-size: 0.85rem;\n transition: all 0.15s ease;\n}\n\n.at-slide-close:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.at-slide-body {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.at-form-group {\n display: flex;\n flex-direction: column;\n gap: 6px;\n}\n\n.at-form-label {\n font-size: 0.75rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n.at-form-input,\n.at-form-select,\n.at-form-textarea {\n padding: 9px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 7px;\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-primary);\n font-size: 0.85rem;\n outline: none;\n transition: border-color 0.15s ease;\n font-family: inherit;\n}\n\n.at-form-input:focus,\n.at-form-select:focus,\n.at-form-textarea:focus {\n border-color: var(--mj-brand-primary);\n box-shadow: 0 0 0 2px color-mix(in srgb, var(--mj-brand-primary) 15%, transparent);\n}\n\n.at-form-select {\n appearance: none;\n background-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%2364748b' d='M3 5l3 3 3-3'/%3E%3C/svg%3E\");\n background-repeat: no-repeat;\n background-position: right 10px center;\n padding-right: 30px;\n}\n\n.at-form-textarea {\n resize: vertical;\n min-height: 70px;\n}\n\n.at-form-row {\n display: flex;\n gap: 12px;\n}\n\n.at-form-actions {\n display: flex;\n gap: 8px;\n padding-top: 8px;\n border-top: 1px solid var(--mj-border-subtle);\n margin-top: 8px;\n}\n\n.at-form-actions .at-action-btn {\n flex: 1;\n justify-content: center;\n}\n\n/* Also add empty state for Content Types (matching Sources) */\n.at-add-type-card {\n background: none;\n border: 2px dashed var(--mj-border-default);\n border-radius: 10px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 8px;\n padding: 40px;\n cursor: pointer;\n transition: all 0.15s ease;\n color: var(--mj-text-muted);\n min-height: 200px;\n}\n\n.at-add-type-card:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.at-add-type-card i {\n font-size: 1.5rem;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 FORKED PIPELINE STAGES \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-pipeline-stages-forked {\n display: flex;\n align-items: center;\n gap: 0;\n}\n\n.at-pipeline-fork {\n display: flex;\n align-items: center;\n gap: 0;\n flex: 2;\n}\n\n.at-pipeline-fork-lines {\n width: 28px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n position: relative;\n flex-shrink: 0;\n}\n\n.at-pipeline-fork-line {\n height: 2px;\n background: var(--mj-border-default);\n width: 100%;\n position: relative;\n}\n\n.at-pipeline-fork-line::before {\n content: '';\n position: absolute;\n width: 2px;\n height: 12px;\n background: var(--mj-border-default);\n left: 0;\n}\n\n.at-fork-top::before {\n bottom: 0;\n left: 0;\n}\n\n.at-fork-bottom::before {\n top: 0;\n left: 0;\n}\n\n.at-pipeline-fork-branches {\n display: flex;\n flex-direction: column;\n gap: 8px;\n flex: 1;\n}\n\n.at-pipeline-fork-branches .at-pipeline-stage {\n flex: none;\n padding: 6px 0;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 CLICKABLE FEED ITEMS \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-feed-item-clickable {\n cursor: pointer;\n}\n\n.at-feed-item-clickable:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 FEED SEARCH & PAGINATION \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-feed-card {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n\n.at-feed-header-actions {\n display: flex;\n align-items: center;\n gap: 10px;\n margin-left: auto;\n}\n\n.at-feed-sort-btn {\n display: flex;\n align-items: center;\n gap: 4px;\n padding: 3px 8px;\n border: 1px solid var(--mj-border-default);\n border-radius: 5px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n font-size: 0.68rem;\n cursor: pointer;\n transition: background 0.15s, color 0.15s;\n}\n\n.at-feed-sort-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.at-feed-count {\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n}\n\n.at-feed-search-bar {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 14px;\n border-bottom: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface-card);\n}\n\n.at-feed-search-icon {\n color: var(--mj-text-disabled);\n font-size: 0.72rem;\n flex-shrink: 0;\n}\n\n.at-feed-search-input {\n flex: 1;\n border: none;\n background: transparent;\n font-size: 0.78rem;\n color: var(--mj-text-primary);\n outline: none;\n padding: 4px 0;\n min-width: 0;\n}\n\n.at-feed-search-input::placeholder {\n color: var(--mj-text-disabled);\n}\n\n.at-feed-search-clear {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 18px;\n height: 18px;\n padding: 0;\n border: none;\n border-radius: 50%;\n background: var(--mj-border-default);\n color: var(--mj-text-muted);\n cursor: pointer;\n font-size: 9px;\n flex-shrink: 0;\n}\n\n.at-feed-search-clear:hover {\n background: var(--mj-border-strong);\n color: var(--mj-text-primary);\n}\n\n.at-feed-scroll-body {\n overflow-y: auto;\n min-height: 0;\n flex: 1;\n}\n\n.at-feed-item-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n overflow: hidden;\n}\n\n.at-feed-item-content .at-feed-item-name {\n font-size: 0.82rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.at-feed-item-source-label {\n font-size: 0.7rem;\n font-weight: 500;\n color: var(--mj-brand-primary);\n}\n\n.at-feed-pagination {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 8px 14px;\n border-top: 1px solid var(--mj-border-subtle);\n background: var(--mj-bg-surface-card);\n}\n\n.at-feed-pagination-label {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 CLICKABLE SOURCE CARDS \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-source-card-clickable {\n cursor: pointer;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 FORM HINT \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-form-hint {\n font-size: 0.68rem;\n color: var(--mj-text-muted);\n font-style: italic;\n margin-top: 2px;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 DETAIL PANEL (wider) \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-detail-panel {\n width: 500px;\n}\n\n/* \u2500\u2500 Detail: Item header \u2500\u2500 */\n\n.at-detail-item-header {\n margin-bottom: 8px;\n}\n\n.at-detail-item-name {\n font-size: 1.05rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n margin: 0 0 6px 0;\n word-break: break-word;\n}\n\n.at-detail-badges {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.at-detail-badge {\n padding: 2px 10px;\n border-radius: 10px;\n font-size: 0.7rem;\n font-weight: 600;\n}\n\n.at-detail-badge-source {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n}\n\n.at-detail-badge-type {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-secondary);\n}\n\n.at-detail-badge-file {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n.at-detail-badge-status-active {\n background: var(--mj-status-success-bg);\n color: var(--mj-status-success-text);\n}\n\n.at-detail-badge-status-error {\n background: var(--mj-status-error-bg);\n color: var(--mj-status-error-text);\n}\n\n.at-detail-badge-status-inactive {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-disabled);\n}\n\n/* \u2500\u2500 Detail: Sections \u2500\u2500 */\n\n.at-detail-section {\n margin-bottom: 4px;\n}\n\n.at-detail-section-label {\n font-size: 0.72rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.3px;\n color: var(--mj-text-muted);\n margin-bottom: 6px;\n}\n\n.at-detail-link {\n font-size: 0.8rem;\n color: var(--mj-brand-primary);\n text-decoration: none;\n word-break: break-all;\n}\n\n.at-detail-link:hover {\n text-decoration: underline;\n}\n\n/* \u2500\u2500 Detail: Text preview \u2500\u2500 */\n\n.at-detail-text-preview {\n max-height: 200px;\n overflow-y: auto;\n padding: 10px 12px;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 6px;\n font-size: 0.78rem;\n font-family: 'SF Mono', 'Cascadia Code', 'Menlo', monospace;\n color: var(--mj-text-primary);\n white-space: pre-wrap;\n word-break: break-word;\n line-height: 1.5;\n}\n\n/* \u2500\u2500 Detail: Tags \u2500\u2500 */\n\n.at-detail-tags {\n display: flex;\n flex-wrap: wrap;\n gap: 5px;\n}\n\n/* \u2500\u2500 Detail: Meta grid \u2500\u2500 */\n\n.at-detail-meta-grid {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n overflow: hidden;\n}\n\n.at-detail-meta-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n font-size: 0.78rem;\n}\n\n.at-detail-meta-row:last-child {\n border-bottom: none;\n}\n\n.at-detail-meta-key {\n color: var(--mj-text-muted);\n font-weight: 500;\n flex-shrink: 0;\n margin-right: 12px;\n}\n\n.at-detail-meta-value {\n color: var(--mj-text-primary);\n text-align: right;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.at-detail-meta-mono {\n font-family: 'SF Mono', 'Cascadia Code', 'Menlo', monospace;\n font-size: 0.72rem;\n}\n\n/* \u2500\u2500 Detail: Actions \u2500\u2500 */\n\n.at-detail-actions {\n display: flex;\n gap: 8px;\n padding-top: 8px;\n border-top: 1px solid var(--mj-border-subtle);\n margin-top: 8px;\n}\n\n.at-detail-actions .at-action-btn {\n flex: 1;\n justify-content: center;\n}\n\n/* \u2500\u2500 Detail: Source header \u2500\u2500 */\n\n.at-detail-source-header {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 8px;\n}\n\n/* \u2500\u2500 Detail: Stats strip \u2500\u2500 */\n\n.at-detail-stats-strip {\n display: flex;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.at-detail-stat {\n flex: 1;\n min-width: 60px;\n text-align: center;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n padding: 8px 6px;\n}\n\n.at-detail-stat-value {\n font-size: 1rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.at-detail-stat-label {\n font-size: 0.62rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n/* \u2500\u2500 Detail: Content Library \u2500\u2500 */\n\n.at-detail-content-list {\n max-height: 250px;\n overflow-y: auto;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n}\n\n.at-detail-content-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n font-size: 0.78rem;\n cursor: pointer;\n transition: background 0.1s ease;\n}\n\n.at-detail-content-item:last-child {\n border-bottom: none;\n}\n\n.at-detail-content-item:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.at-detail-content-item-name {\n flex: 1;\n font-weight: 500;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.at-detail-content-item-tags {\n font-size: 0.68rem;\n color: var(--mj-text-muted);\n white-space: nowrap;\n}\n\n.at-detail-content-item-time {\n font-size: 0.68rem;\n color: var(--mj-text-muted);\n white-space: nowrap;\n}\n\n/* \u2500\u2500 Detail: Run history \u2500\u2500 */\n\n.at-detail-run-history {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n max-height: 200px;\n overflow-y: auto;\n}\n\n.at-detail-run-row {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n border-bottom: 1px solid var(--mj-border-subtle);\n font-size: 0.75rem;\n}\n\n.at-detail-run-row:last-child {\n border-bottom: none;\n}\n\n.at-detail-run-time {\n color: var(--mj-text-secondary);\n flex: 1;\n}\n\n.at-detail-run-duration {\n color: var(--mj-text-muted);\n}\n\n.at-detail-run-items {\n color: var(--mj-text-muted);\n}\n\n@media (max-width: 600px) {\n .at-slide-panel {\n width: 100%;\n }\n\n .at-detail-panel {\n width: 100%;\n }\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n TAXONOMY GOVERNANCE TAB\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 */\n\n/* \u2500\u2500 Sub-tab strip \u2500\u2500 */\n\n.at-tax-tab-strip {\n display: flex;\n border-bottom: 2px solid var(--mj-border-default);\n padding: 0 20px;\n gap: 0;\n flex-shrink: 0;\n}\n\n.at-tax-tab {\n padding: 10px 20px;\n font-size: 0.78rem;\n font-weight: 500;\n color: var(--mj-text-muted);\n cursor: pointer;\n border-bottom: 2px solid transparent;\n margin-bottom: -2px;\n transition: all 0.15s;\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.at-tax-tab:hover {\n color: var(--mj-text-secondary);\n}\n\n.at-tax-tab.active {\n color: var(--mj-brand-primary);\n border-bottom-color: var(--mj-brand-primary);\n font-weight: 600;\n}\n\n.at-tax-tab-badge {\n font-size: 0.62rem;\n font-weight: 600;\n padding: 1px 7px;\n border-radius: 10px;\n}\n\n.at-tax-badge-warning {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.at-tax-badge-error {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n/* \u2500\u2500 Tree View: Split layout \u2500\u2500 */\n\n.at-tax-split-view {\n display: flex;\n gap: 0;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 10px;\n height: calc(100vh - 320px);\n min-height: 400px;\n overflow: hidden;\n}\n\n.at-tax-tree-panel {\n width: 40%;\n border-right: 1px solid var(--mj-border-default);\n display: flex;\n flex-direction: column;\n}\n\n.at-tax-tree-toolbar {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 12px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.at-tax-toolbar-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 30px;\n height: 30px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n font-size: 0.8rem;\n transition: background 0.15s, color 0.15s;\n}\n\n.at-tax-toolbar-btn:hover {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.at-tax-toolbar-btn.active {\n background: color-mix(in srgb, var(--mj-brand-primary) 15%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n border-color: var(--mj-brand-primary);\n}\n\n.at-tax-tree-body {\n flex: 1;\n overflow-y: auto;\n padding: 8px 0;\n}\n\n.at-tax-tree-node {\n padding: 6px 16px;\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 0.78rem;\n cursor: pointer;\n transition: background 0.1s;\n line-height: 1.4;\n}\n\n.at-tax-tree-node:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.at-tax-node-selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n}\n\n.at-tax-node-drag-over {\n background: color-mix(in srgb, var(--mj-brand-primary) 20%, var(--mj-bg-surface));\n outline: 2px dashed var(--mj-brand-primary);\n outline-offset: -2px;\n}\n\n.at-tax-node-multi-selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n}\n\n.at-tax-tree-add-child {\n margin-left: auto;\n opacity: 0;\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n cursor: pointer;\n padding: 2px 6px;\n border-radius: 4px;\n transition: opacity 0.15s, color 0.15s, background 0.15s;\n}\n\n.at-tax-tree-node:hover .at-tax-tree-add-child {\n opacity: 1;\n}\n\n.at-tax-tree-add-child:hover {\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, transparent);\n}\n\n.at-tax-tree-checkbox {\n width: 14px;\n height: 14px;\n cursor: pointer;\n accent-color: var(--mj-brand-primary);\n flex-shrink: 0;\n}\n\n.at-tax-tree-saving-overlay {\n position: absolute;\n inset: 0;\n background: color-mix(in srgb, var(--mj-bg-surface) 75%, transparent);\n display: flex;\n align-items: center;\n justify-content: center;\n z-index: 10;\n backdrop-filter: blur(1px);\n}\n\n.at-tax-create-context {\n font-size: 0.8rem;\n color: var(--mj-text-muted);\n margin-bottom: 12px;\n padding: 6px 10px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 6px;\n}\n\n.at-tax-tree-arrow {\n width: 16px;\n font-size: 0.55rem;\n color: var(--mj-text-muted);\n flex-shrink: 0;\n text-align: center;\n cursor: pointer;\n}\n\n.at-tax-arrow-collapsed::before {\n content: \"\\25B6\";\n}\n\n.at-tax-arrow-expanded::before {\n content: \"\\25BC\";\n}\n\n.at-tax-arrow-leaf {\n visibility: hidden;\n}\n\n.at-tax-health-dot {\n width: 8px;\n height: 8px;\n border-radius: 50%;\n flex-shrink: 0;\n}\n\n.at-tax-health-dot.green {\n background: var(--mj-status-success);\n}\n\n.at-tax-health-dot.yellow {\n background: var(--mj-status-warning);\n}\n\n.at-tax-health-dot.red {\n background: var(--mj-status-error);\n}\n\n.at-tax-tree-label {\n flex: 1;\n color: var(--mj-text-primary);\n}\n\n.at-tax-tree-label-selected {\n font-weight: 700;\n}\n\n.at-tax-tree-count {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n margin-left: 4px;\n}\n\n/* \u2500\u2500 Details panel \u2500\u2500 */\n\n.at-tax-details-panel {\n width: 60%;\n display: flex;\n flex-direction: column;\n overflow-y: auto;\n}\n\n.at-tax-details-header {\n padding: 20px 24px 0;\n}\n\n.at-tax-breadcrumb {\n font-size: 0.7rem;\n color: var(--mj-text-muted);\n margin-bottom: 8px;\n display: flex;\n align-items: center;\n gap: 4px;\n}\n\n.at-tax-bc-link {\n color: var(--mj-brand-primary);\n cursor: pointer;\n}\n\n.at-tax-bc-link:hover {\n text-decoration: underline;\n}\n\n.at-tax-bc-sep {\n color: var(--mj-border-default);\n}\n\n.at-tax-bc-current {\n color: var(--mj-text-secondary);\n font-weight: 600;\n}\n\n.at-tax-details-title {\n font-size: 1.2rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 4px;\n}\n\n.at-tax-edit-icon {\n font-size: 0.75rem;\n color: var(--mj-text-muted);\n cursor: pointer;\n}\n\n.at-tax-edit-icon:hover {\n color: var(--mj-brand-primary);\n}\n\n.at-tax-details-desc {\n font-size: 0.78rem;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n margin-bottom: 16px;\n padding: 8px 12px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 6px;\n border: 1px solid var(--mj-border-subtle);\n}\n\n.at-tax-edit-form {\n margin-bottom: 16px;\n}\n\n/* \u2500\u2500 Stats row \u2500\u2500 */\n\n.at-tax-stats-row {\n display: flex;\n gap: 12px;\n flex-wrap: wrap;\n margin-bottom: 16px;\n padding: 0 24px;\n}\n\n.at-tax-stat-item {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 10px 14px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 8px;\n min-width: 72px;\n}\n\n.at-tax-stat-value {\n font-size: 1.1rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.at-tax-stat-date {\n font-size: 0.8rem;\n}\n\n.at-tax-stat-label {\n font-size: 0.62rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n/* \u2500\u2500 Action toolbar \u2500\u2500 */\n\n.at-tax-action-toolbar {\n display: flex;\n gap: 8px;\n padding: 0 24px;\n margin-bottom: 20px;\n flex-wrap: wrap;\n}\n\n.at-tax-action-btn {\n padding: 6px 14px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n font-size: 0.72rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 5px;\n transition: all 0.15s;\n}\n\n.at-tax-action-btn:hover {\n background: var(--mj-bg-surface-hover);\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.at-tax-action-danger:hover {\n border-color: var(--mj-status-error);\n color: var(--mj-status-error);\n background: color-mix(in srgb, var(--mj-status-error) 8%, var(--mj-bg-surface));\n}\n\n/* \u2500\u2500 Detail sections \u2500\u2500 */\n\n.at-tax-detail-section {\n padding: 0 24px 20px;\n}\n\n.at-tax-section-title {\n font-size: 0.7rem;\n font-weight: 600;\n text-transform: uppercase;\n letter-spacing: 0.04em;\n color: var(--mj-text-muted);\n margin-bottom: 10px;\n}\n\n.at-tax-child-chips {\n display: flex;\n flex-wrap: wrap;\n gap: 6px;\n}\n\n.at-tax-child-chip {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 10px;\n background: var(--mj-bg-surface-sunken);\n border: 1px solid var(--mj-border-default);\n border-radius: 16px;\n font-size: 0.72rem;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.15s;\n}\n\n.at-tax-child-chip:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 10%, var(--mj-bg-surface));\n}\n\n.at-tax-chip-count {\n font-size: 0.6rem;\n background: var(--mj-border-default);\n color: var(--mj-text-muted);\n padding: 0 5px;\n border-radius: 8px;\n}\n\n/* \u2500\u2500 Recent items \u2500\u2500 */\n\n.at-tax-recent-list {\n list-style: none;\n}\n\n.at-tax-recent-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 0;\n border-bottom: 1px solid var(--mj-border-subtle);\n font-size: 0.78rem;\n}\n\n.at-tax-recent-item:last-child {\n border-bottom: none;\n}\n\n.at-tax-recent-icon {\n width: 28px;\n height: 28px;\n border-radius: 6px;\n background: color-mix(in srgb, var(--mj-brand-primary) 12%, var(--mj-bg-surface));\n color: var(--mj-brand-primary);\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 0.72rem;\n flex-shrink: 0;\n}\n\n.at-tax-recent-name {\n flex: 1;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.at-tax-recent-weight {\n font-size: 0.68rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n background: var(--mj-bg-surface-sunken);\n padding: 2px 8px;\n border-radius: 10px;\n}\n\n.at-tax-recent-date {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n}\n\n/* \u2500\u2500 Health bar \u2500\u2500 */\n\n.at-tax-health-bar {\n display: flex;\n align-items: center;\n gap: 20px;\n padding: 12px 20px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n margin-top: 16px;\n flex-wrap: wrap;\n}\n\n.at-tax-health-label {\n font-size: 0.72rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.at-tax-health-stat {\n display: flex;\n align-items: center;\n gap: 6px;\n font-size: 0.78rem;\n}\n\n.at-tax-dot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n}\n\n.at-tax-dot-total {\n background: var(--mj-text-secondary);\n}\n\n.at-tax-dot-healthy {\n background: var(--mj-status-success);\n}\n\n.at-tax-dot-attention {\n background: var(--mj-status-warning);\n}\n\n.at-tax-dot-orphaned {\n background: var(--mj-status-error);\n}\n\n.at-tax-dot-duplicates {\n background: var(--mj-status-info);\n}\n\n.at-tax-health-value {\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.at-tax-val-success {\n color: var(--mj-status-success);\n}\n\n.at-tax-val-warning {\n color: var(--mj-status-warning);\n}\n\n.at-tax-val-error {\n color: var(--mj-status-error);\n}\n\n.at-tax-val-info {\n color: var(--mj-status-info);\n}\n\n.at-tax-health-text {\n color: var(--mj-text-muted);\n}\n\n/* \u2550\u2550\u2550\u2550 Duplicates sub-tab \u2550\u2550\u2550\u2550 */\n\n.at-tax-dup-stats-bar {\n display: flex;\n gap: 24px;\n margin-bottom: 16px;\n align-items: center;\n}\n\n.at-tax-dup-stat {\n font-size: 0.78rem;\n color: var(--mj-text-secondary);\n}\n\n.at-tax-dup-stat strong {\n font-size: 1.1rem;\n color: var(--mj-text-primary);\n margin-right: 4px;\n}\n\n.at-tax-dup-high strong {\n color: var(--mj-status-error);\n}\n\n.at-tax-dup-moderate strong {\n color: var(--mj-status-warning);\n}\n\n.at-tax-dup-list {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.at-tax-dup-card {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 14px 20px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n transition: border-color 0.15s;\n}\n\n.at-tax-dup-card:hover {\n border-color: var(--mj-brand-primary);\n}\n\n.at-tax-dup-card.at-tax-dup-high {\n border-left: 3px solid var(--mj-status-error);\n}\n\n.at-tax-dup-card.at-tax-dup-moderate {\n border-left: 3px solid var(--mj-status-warning);\n}\n\n.at-tax-dup-tag {\n font-size: 0.82rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n min-width: 120px;\n padding: 6px 12px;\n background: var(--mj-bg-surface-sunken);\n border-radius: 6px;\n text-align: center;\n}\n\n.at-tax-dup-arrow {\n font-size: 0.82rem;\n color: var(--mj-text-muted);\n flex-shrink: 0;\n}\n\n.at-tax-dup-similarity {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 4px;\n min-width: 100px;\n}\n\n.at-tax-sim-bar-bg {\n width: 100%;\n height: 6px;\n background: var(--mj-border-default);\n border-radius: 3px;\n overflow: hidden;\n}\n\n.at-tax-sim-bar-fill {\n height: 100%;\n border-radius: 3px;\n transition: width 0.3s;\n}\n\n.at-tax-sim-bar-fill.high {\n background: var(--mj-status-error);\n}\n\n.at-tax-sim-bar-fill.moderate {\n background: var(--mj-status-warning);\n}\n\n.at-tax-sim-percent {\n font-size: 0.78rem;\n font-weight: 700;\n}\n\n.at-tax-sim-percent.high {\n color: var(--mj-status-error);\n}\n\n.at-tax-sim-percent.moderate {\n color: var(--mj-status-warning);\n}\n\n.at-tax-dup-actions {\n display: flex;\n gap: 6px;\n flex-shrink: 0;\n}\n\n.at-tax-dup-btn {\n padding: 5px 12px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n font-size: 0.68rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.15s;\n white-space: nowrap;\n}\n\n.at-tax-dup-btn:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.at-tax-dup-btn-primary {\n background: var(--mj-brand-primary);\n color: var(--mj-text-inverse);\n border-color: var(--mj-brand-primary);\n}\n\n.at-tax-dup-btn-primary:hover {\n background: var(--mj-brand-primary-hover);\n color: var(--mj-text-inverse);\n}\n\n/* \u2550\u2550\u2550\u2550 Orphans sub-tab \u2550\u2550\u2550\u2550 */\n\n.at-tax-orphan-toolbar {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 16px;\n flex-wrap: wrap;\n}\n\n.at-tax-orphan-count {\n font-size: 0.82rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.at-tax-orphan-desc {\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n}\n\n.at-tax-orphan-bulk {\n margin-left: auto;\n display: flex;\n gap: 8px;\n}\n\n.at-tax-bulk-btn {\n padding: 6px 14px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n font-size: 0.72rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n display: flex;\n align-items: center;\n gap: 5px;\n transition: all 0.15s;\n}\n\n.at-tax-bulk-btn:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.at-tax-bulk-danger:hover {\n border-color: var(--mj-status-error);\n color: var(--mj-status-error);\n}\n\n.at-tax-orphan-grid {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));\n gap: 12px;\n}\n\n.at-tax-orphan-card {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n padding: 16px;\n transition: all 0.15s;\n display: flex;\n flex-direction: column;\n gap: 10px;\n border-top: 3px solid var(--mj-status-error);\n}\n\n.at-tax-orphan-card:hover {\n border-color: var(--mj-brand-primary);\n}\n\n.at-tax-orphan-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n}\n\n.at-tax-orphan-name {\n font-size: 0.82rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.at-tax-orphan-checkbox {\n width: 16px;\n height: 16px;\n accent-color: var(--mj-brand-primary);\n}\n\n.at-tax-orphan-stats {\n display: flex;\n gap: 12px;\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n}\n\n.at-tax-orphan-stats strong {\n color: var(--mj-text-secondary);\n}\n\n.at-tax-orphan-dates {\n display: flex;\n justify-content: space-between;\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n}\n\n.at-tax-orphan-actions {\n display: flex;\n gap: 6px;\n margin-top: 4px;\n}\n\n.at-tax-orphan-btn {\n flex: 1;\n padding: 5px 8px;\n border: 1px solid var(--mj-border-default);\n border-radius: 5px;\n background: var(--mj-bg-surface);\n font-size: 0.68rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n cursor: pointer;\n text-align: center;\n transition: all 0.15s;\n}\n\n.at-tax-orphan-btn:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n.at-tax-orphan-delete:hover {\n border-color: var(--mj-status-error);\n color: var(--mj-status-error);\n}\n\n/* \u2550\u2550\u2550\u2550 Treemap sub-tab \u2550\u2550\u2550\u2550 */\n\n.at-tax-treemap-kpi-strip {\n display: flex;\n gap: 20px;\n margin-bottom: 16px;\n flex-wrap: wrap;\n}\n\n.at-tax-treemap-kpi {\n padding: 10px 18px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n display: flex;\n flex-direction: column;\n align-items: center;\n min-width: 120px;\n}\n\n.at-tax-treemap-kpi-value {\n font-size: 1.2rem;\n font-weight: 700;\n color: var(--mj-text-primary);\n}\n\n.at-tax-treemap-kpi-label {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n}\n\n.at-tax-treemap-container {\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 10px;\n padding: 8px;\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));\n grid-auto-rows: 80px;\n gap: 4px;\n min-height: 340px;\n}\n\n.at-tax-treemap-cell {\n border-radius: 6px;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n color: var(--mj-text-inverse);\n font-weight: 600;\n font-size: 0.78rem;\n cursor: pointer;\n transition: all 0.15s;\n position: relative;\n overflow: hidden;\n}\n\n.at-tax-treemap-cell:hover {\n transform: scale(1.02);\n z-index: 2;\n}\n\n.at-tax-cell-name {\n font-size: 0.78rem;\n}\n\n.at-tax-cell-count {\n font-size: 0.65rem;\n opacity: 0.85;\n font-weight: 400;\n}\n\n/* Treemap color families */\n.at-tm-blue-1 { background: color-mix(in srgb, var(--mj-brand-primary) 90%, black); }\n.at-tm-blue-2 { background: var(--mj-brand-primary); }\n.at-tm-blue-3 { background: color-mix(in srgb, var(--mj-brand-primary) 75%, white); }\n.at-tm-blue-4 { background: color-mix(in srgb, var(--mj-brand-primary) 55%, white); }\n\n.at-tm-green-1 { background: color-mix(in srgb, var(--mj-status-success) 90%, black); }\n.at-tm-green-2 { background: var(--mj-status-success); }\n.at-tm-green-3 { background: color-mix(in srgb, var(--mj-status-success) 60%, white); color: var(--mj-text-primary); }\n\n.at-tm-purple-1 { background: color-mix(in srgb, var(--mj-status-info) 90%, black); }\n.at-tm-purple-2 { background: var(--mj-status-info); }\n.at-tm-purple-3 { background: color-mix(in srgb, var(--mj-status-info) 65%, white); }\n\n.at-tm-orange-1 { background: color-mix(in srgb, var(--mj-status-warning) 90%, black); }\n.at-tm-orange-2 { background: var(--mj-status-warning); }\n.at-tm-orange-3 { background: color-mix(in srgb, var(--mj-status-warning) 60%, white); color: var(--mj-text-primary); }\n\n/* \u2550\u2550\u2550\u2550 Audit Log sub-tab \u2550\u2550\u2550\u2550 */\n\n.at-tax-audit-filters {\n display: flex;\n align-items: center;\n gap: 16px;\n margin-bottom: 16px;\n padding: 12px 16px;\n background: var(--mj-bg-surface-card);\n border: 1px solid var(--mj-border-subtle);\n border-radius: 8px;\n flex-wrap: wrap;\n}\n\n.at-tax-audit-filter-label {\n font-size: 0.72rem;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.at-tax-audit-checkbox-group {\n display: flex;\n gap: 12px;\n flex-wrap: wrap;\n}\n\n.at-tax-audit-checkbox {\n display: flex;\n align-items: center;\n gap: 4px;\n font-size: 0.72rem;\n color: var(--mj-text-secondary);\n cursor: pointer;\n}\n\n.at-tax-audit-checkbox input {\n accent-color: var(--mj-brand-primary);\n}\n\n.at-tax-audit-timeline {\n position: relative;\n padding-left: 32px;\n}\n\n.at-tax-audit-timeline::before {\n content: '';\n position: absolute;\n left: 14px;\n top: 0;\n bottom: 0;\n width: 2px;\n background: var(--mj-border-default);\n}\n\n.at-tax-audit-event {\n position: relative;\n padding: 10px 16px;\n margin-bottom: 4px;\n display: flex;\n align-items: flex-start;\n gap: 12px;\n border-radius: 8px;\n transition: background 0.1s;\n}\n\n.at-tax-audit-event:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.at-tax-audit-event-icon {\n width: 28px;\n height: 28px;\n border-radius: 50%;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 0.68rem;\n flex-shrink: 0;\n position: absolute;\n left: -32px;\n top: 10px;\n z-index: 1;\n}\n\n.at-tax-audit-event-icon.created {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success);\n}\n\n.at-tax-audit-event-icon.merged {\n background: color-mix(in srgb, var(--mj-status-info) 15%, var(--mj-bg-surface));\n color: var(--mj-status-info);\n}\n\n.at-tax-audit-event-icon.moved {\n background: color-mix(in srgb, var(--mj-status-info) 15%, var(--mj-bg-surface));\n color: var(--mj-status-info);\n}\n\n.at-tax-audit-event-icon.deleted {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n}\n\n.at-tax-audit-event-icon.renamed {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning);\n}\n\n.at-tax-audit-event-body {\n flex: 1;\n}\n\n.at-tax-audit-event-desc {\n font-size: 0.78rem;\n color: var(--mj-text-primary);\n line-height: 1.5;\n}\n\n.at-tax-tag-ref {\n background: var(--mj-bg-surface-sunken);\n padding: 1px 6px;\n border-radius: 4px;\n font-weight: 600;\n font-size: 0.72rem;\n border: 1px solid var(--mj-border-default);\n}\n\n.at-tax-audit-event-meta {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n margin-top: 2px;\n display: flex;\n gap: 12px;\n}\n\n/* \u2500\u2500 Taxonomy responsive \u2500\u2500 */\n\n@media (max-width: 1100px) {\n .at-tax-split-view {\n flex-direction: column;\n }\n\n .at-tax-tree-panel {\n width: 100%;\n max-height: 300px;\n border-right: none;\n border-bottom: 1px solid var(--mj-border-default);\n }\n\n .at-tax-details-panel {\n width: 100%;\n }\n}\n\n@media (max-width: 768px) {\n .at-tax-dup-card {\n flex-wrap: wrap;\n }\n\n .at-tax-orphan-grid {\n grid-template-columns: 1fr;\n }\n}\n\n/* \u2500\u2500 Pipeline Config Widget \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.at-config-card {\n margin-top: 12px;\n}\n\n.at-config-body {\n display: flex;\n flex-direction: column;\n gap: 10px;\n padding: 12px !important;\n}\n\n.at-config-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 8px;\n}\n\n.at-config-label {\n font-size: 0.75rem;\n font-weight: 500;\n color: var(--mj-text-secondary);\n min-width: 90px;\n flex-shrink: 0;\n}\n\n.at-config-control {\n display: flex;\n align-items: center;\n gap: 6px;\n flex: 1;\n justify-content: flex-end;\n}\n\n.at-config-input {\n width: 70px;\n padding: 3px 6px;\n border: 1px solid var(--mj-border-default);\n border-radius: 4px;\n font-size: 0.75rem;\n text-align: right;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n}\n\n.at-config-input:focus {\n outline: none;\n border-color: var(--mj-brand-primary);\n}\n\n.at-config-slider {\n flex: 1;\n max-width: 100px;\n accent-color: var(--mj-brand-primary);\n height: 4px;\n}\n\n.at-config-value {\n font-size: 0.68rem;\n color: var(--mj-text-muted);\n min-width: 40px;\n text-align: right;\n}\n\n.at-config-divider {\n height: 1px;\n background: var(--mj-border-default);\n margin: 4px 0;\n}\n\n.at-config-section-label {\n font-size: 0.68rem;\n font-weight: 600;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n/* Toggle Switch */\n.at-config-toggle {\n position: relative;\n display: inline-block;\n cursor: pointer;\n}\n\n.at-config-toggle input {\n display: none;\n}\n\n.at-toggle-track {\n display: block;\n width: 32px;\n height: 18px;\n background: var(--mj-border-strong);\n border-radius: 9px;\n transition: background 0.2s ease;\n position: relative;\n}\n\n.at-config-toggle input:checked + .at-toggle-track {\n background: var(--mj-brand-primary);\n}\n\n.at-toggle-thumb {\n position: absolute;\n top: 2px;\n left: 2px;\n width: 14px;\n height: 14px;\n background: var(--mj-bg-surface);\n border-radius: 50%;\n transition: transform 0.2s ease;\n box-shadow: 0 1px 3px rgba(0,0,0,0.15);\n}\n\n.at-config-toggle input:checked + .at-toggle-track .at-toggle-thumb {\n transform: translateX(14px);\n}\n\n/* \u2500\u2500 Schedule Indicator on Source Cards \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.at-schedule-indicator {\n display: inline-flex;\n align-items: center;\n gap: 5px;\n padding: 3px 10px;\n margin-top: 6px;\n border-radius: 12px;\n font-size: 0.68rem;\n font-weight: 600;\n color: var(--mj-brand-primary);\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface));\n border: 1px solid color-mix(in srgb, var(--mj-brand-primary) 20%, var(--mj-border-default));\n cursor: pointer;\n transition: all 0.15s ease;\n width: fit-content;\n}\n\n.at-schedule-indicator i {\n font-size: 0.62rem;\n}\n\n.at-schedule-indicator:hover {\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface));\n color: var(--mj-status-error);\n border-color: color-mix(in srgb, var(--mj-status-error) 30%, var(--mj-border-default));\n}\n\n/* Schedule button in source card actions */\n.at-source-schedule-btn:hover {\n border-color: var(--mj-brand-primary);\n color: var(--mj-brand-primary);\n}\n\n/* \u2500\u2500 Schedule Dialog \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.at-schedule-overlay {\n position: fixed;\n inset: 0;\n z-index: 1000;\n display: flex;\n align-items: center;\n justify-content: center;\n background: var(--mj-bg-overlay, rgba(0, 0, 0, 0.4));\n}\n\n.at-schedule-dialog {\n background: var(--mj-bg-surface);\n border: 1px solid var(--mj-border-default);\n border-radius: var(--mj-radius-lg, 12px);\n box-shadow: 0 8px 32px rgba(0, 0, 0, 0.16);\n width: 420px;\n max-width: 90vw;\n}\n\n.at-schedule-dialog-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.at-schedule-dialog-header h3 {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0;\n font-size: 15px;\n font-weight: 650;\n color: var(--mj-text-primary);\n}\n\n.at-schedule-dialog-header h3 i {\n color: var(--mj-brand-primary);\n}\n\n.at-schedule-dialog-close {\n background: none;\n border: none;\n cursor: pointer;\n color: var(--mj-text-muted);\n font-size: 16px;\n padding: 4px;\n line-height: 1;\n transition: color 0.15s;\n}\n\n.at-schedule-dialog-close:hover {\n color: var(--mj-text-primary);\n}\n\n.at-schedule-dialog-body {\n padding: 20px;\n display: flex;\n flex-direction: column;\n gap: 16px;\n}\n\n.at-schedule-field label {\n display: block;\n font-size: 11.5px;\n font-weight: 600;\n color: var(--mj-text-secondary);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n margin-bottom: 6px;\n}\n\n.at-schedule-source-name {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13.5px;\n font-weight: 600;\n color: var(--mj-text-primary);\n padding: 8px 12px;\n background: var(--mj-bg-surface-card);\n border-radius: var(--mj-radius-sm, 6px);\n border: 1px solid var(--mj-border-subtle);\n}\n\n.at-schedule-source-name i {\n color: var(--mj-text-muted);\n}\n\n.at-schedule-action-name {\n font-size: 13px;\n color: var(--mj-text-secondary);\n padding: 8px 12px;\n background: var(--mj-bg-surface-card);\n border-radius: var(--mj-radius-sm, 6px);\n border: 1px solid var(--mj-border-subtle);\n}\n\n.at-schedule-cron-input {\n width: 100%;\n font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;\n font-size: 13px;\n letter-spacing: 0.02em;\n}\n\n.at-schedule-cron-preview {\n display: flex;\n align-items: center;\n gap: 6px;\n margin-top: 6px;\n font-size: 12px;\n color: var(--mj-text-muted);\n}\n\n.at-schedule-cron-preview i {\n font-size: 11px;\n color: var(--mj-brand-primary);\n}\n\n.at-schedule-toggle-row {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 10px;\n}\n\n.at-schedule-toggle-row label {\n margin-bottom: 0;\n}\n\n.at-schedule-dialog-footer {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 14px 20px;\n border-top: 1px solid var(--mj-border-default);\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 D4: Status Badges for Content Items \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-status-badge {\n display: inline-block;\n padding: 1px 6px;\n border-radius: 4px;\n font-size: 0.6rem;\n font-weight: 600;\n white-space: nowrap;\n letter-spacing: 0.02em;\n}\n\n.at-status-badge-complete {\n background: color-mix(in srgb, var(--mj-status-success) 15%, var(--mj-bg-surface));\n color: var(--mj-status-success-text);\n}\n\n.at-status-badge-processing {\n background: color-mix(in srgb, var(--mj-status-warning) 15%, var(--mj-bg-surface));\n color: var(--mj-status-warning-text);\n}\n\n.at-status-badge-failed {\n background: color-mix(in srgb, var(--mj-status-error) 15%, var(--mj-bg-surface));\n color: var(--mj-status-error-text);\n}\n\n.at-status-badge-pending {\n background: var(--mj-bg-surface-sunken);\n color: var(--mj-text-muted);\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 D4/D7: Source Detail Controls \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-detail-section-header-row {\n display: flex;\n align-items: center;\n justify-content: space-between;\n margin-bottom: 8px;\n gap: 8px;\n flex-wrap: wrap;\n}\n\n.at-detail-section-controls {\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.at-detail-filter-select {\n padding: 4px 8px;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n font-size: 0.72rem;\n background: var(--mj-bg-surface);\n color: var(--mj-text-primary);\n cursor: pointer;\n}\n\n.at-retry-btn {\n font-size: 0.7rem !important;\n padding: 4px 8px !important;\n background: color-mix(in srgb, var(--mj-status-error) 10%, var(--mj-bg-surface)) !important;\n color: var(--mj-status-error-text) !important;\n border: 1px solid color-mix(in srgb, var(--mj-status-error) 20%, var(--mj-border-default)) !important;\n}\n\n.at-retry-btn:hover {\n background: color-mix(in srgb, var(--mj-status-error) 18%, var(--mj-bg-surface)) !important;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 D7: Pagination \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-detail-pagination {\n display: flex;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 10px 0 4px;\n border-top: 1px solid var(--mj-border-subtle);\n margin-top: 4px;\n}\n\n.at-page-btn {\n display: inline-flex;\n align-items: center;\n gap: 4px;\n padding: 4px 10px;\n font-size: 0.72rem;\n font-weight: 500;\n border: 1px solid var(--mj-border-default);\n border-radius: 6px;\n background: var(--mj-bg-surface);\n color: var(--mj-text-secondary);\n cursor: pointer;\n transition: all 0.12s ease;\n}\n\n.at-page-btn:hover:not(:disabled) {\n background: var(--mj-bg-surface-hover);\n color: var(--mj-text-primary);\n}\n\n.at-page-btn:disabled {\n opacity: 0.4;\n cursor: default;\n}\n\n.at-page-info {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n font-weight: 500;\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 Confirmation Dialog \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-confirm-message {\n font-size: 13.5px;\n color: var(--mj-text-secondary);\n line-height: 1.5;\n margin: 0;\n}\n\n.at-danger-btn {\n background: var(--mj-status-error);\n color: var(--mj-text-inverse);\n border: none;\n border-radius: var(--mj-radius-sm, 6px);\n padding: 7px 14px;\n font-size: 12.5px;\n font-weight: 600;\n cursor: pointer;\n transition: background 0.15s;\n}\n\n.at-danger-btn:hover {\n background: var(--mj-status-error-text);\n}\n\n/* \u2550\u2550\u2550\u2550\u2550\u2550 Treemap Drill-In \u2550\u2550\u2550\u2550\u2550\u2550 */\n\n.at-tax-treemap-cell-clickable {\n cursor: pointer;\n transition: transform 0.15s, box-shadow 0.15s;\n}\n\n.at-tax-treemap-cell-clickable:hover {\n transform: scale(1.02);\n box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);\n z-index: 1;\n}\n\n.at-tax-drillin-overlay {\n position: fixed;\n inset: 0;\n z-index: 1000;\n display: flex;\n align-items: flex-start;\n justify-content: flex-end;\n background: var(--mj-bg-overlay, rgba(0, 0, 0, 0.4));\n}\n\n.at-tax-drillin-panel {\n background: var(--mj-bg-surface);\n border-left: 1px solid var(--mj-border-default);\n width: 420px;\n max-width: 90vw;\n height: 100vh;\n display: flex;\n flex-direction: column;\n box-shadow: -4px 0 24px rgba(0, 0, 0, 0.12);\n}\n\n.at-tax-drillin-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 16px 20px;\n border-bottom: 1px solid var(--mj-border-default);\n}\n\n.at-tax-drillin-header h3 {\n display: flex;\n align-items: center;\n gap: 8px;\n margin: 0;\n font-size: 15px;\n font-weight: 650;\n color: var(--mj-text-primary);\n}\n\n.at-tax-drillin-header h3 i {\n color: var(--mj-brand-primary);\n}\n\n.at-tax-drillin-body {\n flex: 1;\n overflow-y: auto;\n padding: 20px;\n}\n\n.at-tax-drillin-footer {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 14px 20px;\n border-top: 1px solid var(--mj-border-default);\n}\n\n/* D3/D8: Clickable run history rows */\n.at-run-row-clickable {\n cursor: pointer;\n transition: background 0.12s ease;\n}\n\n.at-run-row-clickable:hover {\n background: var(--mj-bg-surface-hover);\n}\n\n.at-run-row-selected {\n background: color-mix(in srgb, var(--mj-brand-primary) 8%, var(--mj-bg-surface)) !important;\n border-left: 3px solid var(--mj-brand-primary);\n}\n\n/* D2: Error text in detail tables */\n.run-error-text {\n color: var(--mj-status-error);\n font-weight: 600;\n}\n\n/* \u2500\u2500 Content Item Duplicates Section (G3) \u2500\u2500 */\n\n.at-dedup-section {\n margin-top: 24px;\n border-top: 1px solid var(--mj-border-default);\n padding-top: 20px;\n}\n\n.at-dedup-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n margin-bottom: 16px;\n}\n\n.at-dedup-title {\n margin: 0;\n font-size: 1rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n display: flex;\n align-items: center;\n gap: 8px;\n}\n\n.at-dedup-subtitle {\n font-size: 0.78rem;\n color: var(--mj-text-muted);\n}\n\n.at-dedup-list {\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.at-dedup-card {\n display: flex;\n align-items: center;\n gap: 16px;\n padding: 14px 16px;\n border: 1px solid var(--mj-border-default);\n border-radius: 10px;\n background: var(--mj-bg-surface);\n transition: border-color 0.15s;\n}\n\n.at-dedup-card:hover {\n border-color: var(--mj-border-strong);\n}\n\n.at-dedup-pair {\n display: flex;\n align-items: center;\n gap: 12px;\n flex: 1;\n min-width: 0;\n}\n\n.at-dedup-item {\n display: flex;\n flex-direction: column;\n min-width: 0;\n flex: 1;\n}\n\n.at-dedup-item-name {\n font-size: 0.85rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.at-dedup-item-source {\n font-size: 0.72rem;\n color: var(--mj-text-muted);\n}\n\n.at-dedup-vs {\n color: var(--mj-text-muted);\n font-size: 0.75rem;\n flex-shrink: 0;\n}\n\n.at-dedup-meta {\n display: flex;\n flex-direction: column;\n align-items: flex-end;\n gap: 3px;\n flex-shrink: 0;\n}\n\n.at-dedup-score {\n font-size: 0.78rem;\n font-weight: 600;\n color: var(--mj-text-primary);\n}\n\n.at-dedup-method {\n font-size: 0.65rem;\n color: var(--mj-text-muted);\n text-transform: uppercase;\n letter-spacing: 0.3px;\n}\n\n.at-dedup-actions {\n display: flex;\n gap: 6px;\n flex-shrink: 0;\n}\n\n.at-dedup-confirm {\n font-size: 0.75rem !important;\n padding: 4px 10px !important;\n}\n\n.at-dedup-dismiss {\n font-size: 0.75rem !important;\n padding: 4px 10px !important;\n}\n\n.at-dedup-empty {\n text-align: center;\n padding: 32px 16px;\n color: var(--mj-text-muted);\n}\n\n.at-dedup-empty i {\n font-size: 1.5rem;\n margin-bottom: 8px;\n color: var(--mj-status-success);\n}\n\n.at-dedup-empty p {\n margin: 8px 0 0;\n font-size: 0.82rem;\n}\n"] }]
|
|
7959
7959
|
}], null, null); })();
|
|
7960
7960
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(AutotaggingPipelineResourceComponent, { className: "AutotaggingPipelineResourceComponent", filePath: "src/AI/components/autotagging/autotagging-pipeline-resource.component.ts", lineNumber: 323 }); })();
|
|
7961
7961
|
export function LoadAutotaggingPipelineResource() {
|