@memberjunction/ng-versions 5.4.1 → 5.5.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/lib/label-detail/label-detail.component.d.ts +2 -0
- package/dist/lib/label-detail/label-detail.component.d.ts.map +1 -1
- package/dist/lib/label-detail/label-detail.component.js +15 -10
- package/dist/lib/label-detail/label-detail.component.js.map +1 -1
- package/dist/lib/record-micro-view/record-micro-view.component.d.ts.map +1 -1
- package/dist/lib/record-micro-view/record-micro-view.component.js +4 -3
- package/dist/lib/record-micro-view/record-micro-view.component.js.map +1 -1
- package/package.json +6 -6
|
@@ -163,6 +163,8 @@ export declare class MjLabelDetailComponent implements OnInit, OnDestroy {
|
|
|
163
163
|
private onResizeEnd;
|
|
164
164
|
private loadPanelPreferences;
|
|
165
165
|
private savePanelPreferences;
|
|
166
|
+
/** Check if a label is the parent of the current Label (case-insensitive UUID comparison). */
|
|
167
|
+
IsParentLabel(pl: MJVersionLabelEntityType): boolean;
|
|
166
168
|
resolveEntityName(entityId: string | null | undefined): string;
|
|
167
169
|
/** Resolve icon CSS class for an entity by ID, falling back to generic table icon. */
|
|
168
170
|
resolveEntityIcon(entityId: string): string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"label-detail.component.d.ts","sourceRoot":"","sources":["../../../src/lib/label-detail/label-detail.component.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,YAAY,EAAE,MAAM,EAAE,SAAS,EAAE,iBAAiB,EAAyC,UAAU,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"label-detail.component.d.ts","sourceRoot":"","sources":["../../../src/lib/label-detail/label-detail.component.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@ import { Component, Input, Output, EventEmitter, ChangeDetectionStrategy, HostLi
|
|
|
2
2
|
import { Subject } from 'rxjs';
|
|
3
3
|
import { RunView, Metadata, CompositeKey, EntityRecordNameInput } from '@memberjunction/core';
|
|
4
4
|
import { UserInfoEngine } from '@memberjunction/core-entities';
|
|
5
|
+
import { UUIDsEqual } from '@memberjunction/global';
|
|
5
6
|
import * as i0 from "@angular/core";
|
|
6
7
|
import * as i1 from "@angular/common";
|
|
7
8
|
import * as i2 from "@angular/forms";
|
|
@@ -259,7 +260,7 @@ function MjLabelDetailComponent_Conditional_43_Conditional_2_Conditional_45_Cond
|
|
|
259
260
|
} if (rf & 2) {
|
|
260
261
|
const pl_r5 = ctx.$implicit;
|
|
261
262
|
const ctx_r0 = i0.ɵɵnextContext(5);
|
|
262
|
-
i0.ɵɵconditional(
|
|
263
|
+
i0.ɵɵconditional(ctx_r0.IsParentLabel(pl_r5) ? 0 : -1);
|
|
263
264
|
} }
|
|
264
265
|
function MjLabelDetailComponent_Conditional_43_Conditional_2_Conditional_45_Conditional_4_Template(rf, ctx) { if (rf & 1) {
|
|
265
266
|
i0.ɵɵelementStart(0, "div", 79);
|
|
@@ -1234,7 +1235,7 @@ export class MjLabelDetailComponent {
|
|
|
1234
1235
|
// Data loading
|
|
1235
1236
|
// =========================================================================
|
|
1236
1237
|
computeChildLabels() {
|
|
1237
|
-
this.ChildLabels = this.AllLabels.filter(l => l.ParentID
|
|
1238
|
+
this.ChildLabels = this.AllLabels.filter(l => UUIDsEqual(l.ParentID, this.Label.ID));
|
|
1238
1239
|
}
|
|
1239
1240
|
async loadLabelItems() {
|
|
1240
1241
|
this.IsLoadingItems = true;
|
|
@@ -1557,7 +1558,7 @@ export class MjLabelDetailComponent {
|
|
|
1557
1558
|
this.dependenciesLoaded = true;
|
|
1558
1559
|
return;
|
|
1559
1560
|
}
|
|
1560
|
-
const entityInfo = this.metadata.Entities.find(e => e.ID
|
|
1561
|
+
const entityInfo = this.metadata.Entities.find(e => UUIDsEqual(e.ID, entityId));
|
|
1561
1562
|
if (!entityInfo) {
|
|
1562
1563
|
this.DependencyTree = [];
|
|
1563
1564
|
this.dependenciesLoaded = true;
|
|
@@ -1641,9 +1642,9 @@ export class MjLabelDetailComponent {
|
|
|
1641
1642
|
this.Restores = result.Results;
|
|
1642
1643
|
}
|
|
1643
1644
|
// Find related labels (same entity + record)
|
|
1644
|
-
this.RelatedLabels = this.AllLabels.filter(l => l.ID
|
|
1645
|
-
l.EntityID
|
|
1646
|
-
l.RecordID
|
|
1645
|
+
this.RelatedLabels = this.AllLabels.filter(l => !UUIDsEqual(l.ID, this.Label.ID) &&
|
|
1646
|
+
UUIDsEqual(l.EntityID, this.Label.EntityID) &&
|
|
1647
|
+
UUIDsEqual(l.RecordID, this.Label.RecordID) &&
|
|
1647
1648
|
l.RecordID != null);
|
|
1648
1649
|
this.historyLoaded = true;
|
|
1649
1650
|
}
|
|
@@ -1863,17 +1864,21 @@ export class MjLabelDetailComponent {
|
|
|
1863
1864
|
// =========================================================================
|
|
1864
1865
|
// Display helpers
|
|
1865
1866
|
// =========================================================================
|
|
1867
|
+
/** Check if a label is the parent of the current Label (case-insensitive UUID comparison). */
|
|
1868
|
+
IsParentLabel(pl) {
|
|
1869
|
+
return UUIDsEqual(pl.ID, this.Label.ParentID);
|
|
1870
|
+
}
|
|
1866
1871
|
resolveEntityName(entityId) {
|
|
1867
1872
|
if (!entityId)
|
|
1868
1873
|
return 'Unknown';
|
|
1869
|
-
const entity = this.metadata.Entities.find(e => e.ID
|
|
1874
|
+
const entity = this.metadata.Entities.find(e => UUIDsEqual(e.ID, entityId));
|
|
1870
1875
|
return entity ? entity.Name : 'Unknown';
|
|
1871
1876
|
}
|
|
1872
1877
|
/** Resolve icon CSS class for an entity by ID, falling back to generic table icon. */
|
|
1873
1878
|
resolveEntityIcon(entityId) {
|
|
1874
1879
|
if (!entityId)
|
|
1875
1880
|
return 'fa-solid fa-table';
|
|
1876
|
-
const entity = this.metadata.Entities.find(e => e.ID
|
|
1881
|
+
const entity = this.metadata.Entities.find(e => UUIDsEqual(e.ID, entityId));
|
|
1877
1882
|
return entity?.Icon || 'fa-solid fa-table';
|
|
1878
1883
|
}
|
|
1879
1884
|
/** Resolve icon CSS class for an entity by name, falling back to generic table icon. */
|
|
@@ -2068,7 +2073,7 @@ export class MjLabelDetailComponent {
|
|
|
2068
2073
|
}
|
|
2069
2074
|
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassMetadata(MjLabelDetailComponent, [{
|
|
2070
2075
|
type: Component,
|
|
2071
|
-
args: [{ standalone: false, selector: 'mj-label-detail-panel', changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Backdrop overlay -->\n<div class=\"panel-backdrop\" [class.visible]=\"IsVisible\" (click)=\"OnBackdropClick()\"></div>\n\n<!-- Slide-in panel -->\n<div class=\"detail-panel\" [class.visible]=\"IsVisible\" [style.width.px]=\"PanelWidthPx\">\n <!-- Resize handle -->\n <div class=\"resize-handle\" (mousedown)=\"OnResizeStart($event)\">\n <div class=\"resize-handle-grip\"></div>\n </div>\n\n <!-- ============================================================ -->\n <!-- HEADER -->\n <!-- ============================================================ -->\n <div class=\"panel-header\">\n <div class=\"header-info\">\n <div class=\"header-title-row\">\n <i [class]=\"GetScopeIcon(Label.Scope)\" class=\"header-scope-icon\"></i>\n <h2 class=\"header-title\">{{Label.Name}}</h2>\n <span class=\"header-status\" [ngClass]=\"GetStatusClass(Label.Status)\">\n {{Label.Status}}\n </span>\n <span class=\"header-scope-badge\">{{Label.Scope}}</span>\n </div>\n @if (Label.Description) {\n <p class=\"header-description\">{{Label.Description}}</p>\n }\n </div>\n <div class=\"header-actions\">\n <button class=\"header-action-btn\" (click)=\"OnArchive()\"\n [disabled]=\"IsArchiving || Label.Status === 'Archived'\"\n title=\"Archive this label\">\n <i class=\"fa-solid fa-box-archive\"></i>\n </button>\n <button class=\"header-action-btn\" (click)=\"OnTabChange('changes')\" title=\"View changes\">\n <i class=\"fa-solid fa-code-compare\"></i>\n </button>\n <button class=\"close-btn\" (click)=\"OnClose()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n </div>\n\n <!-- ============================================================ -->\n <!-- SUMMARY KPIs -->\n <!-- ============================================================ -->\n <div class=\"summary-kpis\">\n <div class=\"summary-kpi\">\n <div class=\"kpi-mini-icon\" style=\"background: rgba(99, 102, 241, 0.1); color: #6366f1;\">\n <i class=\"fa-solid fa-camera\"></i>\n </div>\n <div class=\"kpi-mini-content\">\n <span class=\"kpi-mini-value\">{{LabelItems.length || Label.ItemCount || GetItemCount(Label.ID)}}</span>\n <span class=\"kpi-mini-label\">Items</span>\n </div>\n </div>\n <div class=\"summary-kpi\">\n <div class=\"kpi-mini-icon\" style=\"background: rgba(59, 130, 246, 0.1); color: #3b82f6;\">\n <i class=\"fa-solid fa-table\"></i>\n </div>\n <div class=\"kpi-mini-content\">\n <span class=\"kpi-mini-value\">{{UniqueEntityCount}}</span>\n <span class=\"kpi-mini-label\">Entities</span>\n </div>\n </div>\n @if (Label.CreationDurationMS) {\n <div class=\"summary-kpi\">\n <div class=\"kpi-mini-icon\" style=\"background: rgba(16, 185, 129, 0.1); color: #10b981;\">\n <i class=\"fa-solid fa-stopwatch\"></i>\n </div>\n <div class=\"kpi-mini-content\">\n <span class=\"kpi-mini-value\">{{FormatDuration(Label.CreationDurationMS)}}</span>\n <span class=\"kpi-mini-label\">Duration</span>\n </div>\n </div>\n }\n @if (ChildLabels.length > 0) {\n <div class=\"summary-kpi\">\n <div class=\"kpi-mini-icon\" style=\"background: rgba(245, 158, 11, 0.1); color: #f59e0b;\">\n <i class=\"fa-solid fa-layer-group\"></i>\n </div>\n <div class=\"kpi-mini-content\">\n <span class=\"kpi-mini-value\">{{ChildLabels.length}}</span>\n <span class=\"kpi-mini-label\">Children</span>\n </div>\n </div>\n }\n </div>\n\n <!-- ============================================================ -->\n <!-- TAB BAR (hidden when micro-view is active) -->\n <!-- ============================================================ -->\n @if (!ShowMicroView) {\n <div class=\"tab-bar\">\n <button class=\"tab\" [class.active]=\"ActiveTab === 'overview'\" (click)=\"OnTabChange('overview')\">\n <i class=\"fa-solid fa-info-circle tab-icon\"></i> Overview\n </button>\n <button class=\"tab\" [class.active]=\"ActiveTab === 'snapshots'\" (click)=\"OnTabChange('snapshots')\">\n <i class=\"fa-solid fa-camera tab-icon\"></i> Snapshots\n @if (LabelItems.length > 0) {\n <span class=\"tab-count\">{{LabelItems.length}}</span>\n }\n </button>\n <button class=\"tab\" [class.active]=\"ActiveTab === 'dependencies'\" (click)=\"OnTabChange('dependencies')\">\n <i class=\"fa-solid fa-diagram-project tab-icon\"></i> Dependencies\n </button>\n <button class=\"tab\" [class.active]=\"ActiveTab === 'changes'\" (click)=\"OnTabChange('changes')\">\n <i class=\"fa-solid fa-code-compare tab-icon\"></i> Changes\n </button>\n <button class=\"tab\" [class.active]=\"ActiveTab === 'history'\" (click)=\"OnTabChange('history')\">\n <i class=\"fa-solid fa-clock-rotate-left tab-icon\"></i> History\n </button>\n </div>\n }\n\n <!-- ============================================================ -->\n <!-- BREADCRUMB (shown when micro-view is active) -->\n <!-- ============================================================ -->\n @if (ShowMicroView) {\n <div class=\"breadcrumb-bar\">\n <button class=\"breadcrumb-back\" (click)=\"OnBackFromMicroView()\">\n <i class=\"fa-solid fa-arrow-left\"></i>\n </button>\n <div class=\"breadcrumb-trail\">\n <span class=\"breadcrumb-item clickable\" (click)=\"OnBackFromMicroView()\">\n <i class=\"fa-solid fa-camera breadcrumb-icon\"></i> Snapshots\n </span>\n <i class=\"fa-solid fa-chevron-right breadcrumb-separator\"></i>\n <span class=\"breadcrumb-item current\">\n <i class=\"fa-solid fa-file-lines breadcrumb-icon\"></i> {{BreadcrumbLabel}}\n </span>\n </div>\n </div>\n }\n\n <!-- ============================================================ -->\n <!-- TAB CONTENT (scrollable) - or inline micro-view -->\n <!-- ============================================================ -->\n @if (!ShowMicroView) {\n <div class=\"tab-content\">\n <!-- Loading state for items -->\n @if (IsLoadingItems && ActiveTab !== 'dependencies') {\n <mj-loading text=\"Loading label data...\"></mj-loading>\n }\n <!-- ======================================================== -->\n <!-- OVERVIEW TAB -->\n <!-- ======================================================== -->\n @if (ActiveTab === 'overview' && !IsLoadingItems) {\n <div class=\"tab-pane\">\n <!-- Metadata section -->\n <div class=\"detail-section\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-info-circle section-icon\"></i> Label Details\n </h3>\n <div class=\"detail-grid\">\n <div class=\"detail-field\">\n <span class=\"field-label\">Name</span>\n <span class=\"field-value\">{{Label.Name}}</span>\n </div>\n <div class=\"detail-field\">\n <span class=\"field-label\">Status</span>\n <span class=\"field-value\">\n <span class=\"inline-badge\" [ngClass]=\"GetStatusClass(Label.Status)\">{{Label.Status}}</span>\n </span>\n </div>\n <div class=\"detail-field\">\n <span class=\"field-label\">Scope</span>\n <span class=\"field-value\">\n <i [class]=\"GetScopeIcon(Label.Scope)\" style=\"margin-right: 6px;\"></i>{{Label.Scope}}\n </span>\n </div>\n @if (Label.EntityID) {\n <div class=\"detail-field\">\n <span class=\"field-label\">Entity</span>\n <span class=\"field-value\">\n <i [class]=\"OverviewEntityIcon\" style=\"margin-right: 6px; color: #3b82f6;\"></i>{{Label.Entity ?? resolveEntityName(Label.EntityID)}}\n </span>\n </div>\n }\n @if (Label.RecordID && Label.Scope !== 'Record') {\n <div class=\"detail-field\">\n <span class=\"field-label\">Record ID</span>\n <span class=\"field-value field-mono\">{{FormatRecordID(Label.RecordID)}}</span>\n </div>\n }\n @if (Label.ExternalSystemID) {\n <div class=\"detail-field\">\n <span class=\"field-label\">External Ref</span>\n <span class=\"field-value field-mono\">{{Label.ExternalSystemID}}</span>\n </div>\n }\n </div>\n </div>\n <!-- Record card (Record-scoped labels) -->\n @if (Label.Scope === 'Record' && Label.RecordID) {\n <div class=\"record-card\">\n <div class=\"record-card-main\">\n <i [class]=\"OverviewEntityIcon\" class=\"record-card-icon\"></i>\n <div class=\"record-card-info\">\n @if (OverviewRecordName) {\n <span class=\"record-card-name\">{{OverviewRecordName}}</span>\n }\n @if (!OverviewRecordName) {\n <span class=\"record-card-name record-card-name-loading\">Loading...</span>\n }\n <span class=\"record-card-entity\">{{Label.Entity ?? resolveEntityName(Label.EntityID)}}</span>\n <span class=\"record-card-id\">{{FormatRecordID(Label.RecordID)}}</span>\n </div>\n </div>\n <button class=\"record-card-open-btn\" (click)=\"OnOpenOverviewRecord()\" title=\"Open record\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i> Open\n </button>\n </div>\n }\n <!-- Description -->\n @if (Label.Description) {\n <div class=\"detail-section\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-align-left section-icon\"></i> Description\n </h3>\n <p class=\"description-text\">{{Label.Description}}</p>\n </div>\n }\n <!-- Creation info -->\n <div class=\"detail-section\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-clock section-icon\"></i> Creation\n </h3>\n <div class=\"detail-grid\">\n <div class=\"detail-field\">\n <span class=\"field-label\">Created</span>\n <span class=\"field-value\">{{FormatDate(Label.__mj_CreatedAt)}}</span>\n </div>\n @if (Label.CreatedByUser) {\n <div class=\"detail-field\">\n <span class=\"field-label\">Created By</span>\n <span class=\"field-value\">\n <i class=\"fa-solid fa-user\" style=\"margin-right: 6px; opacity: 0.5;\"></i>{{Label.CreatedByUser}}\n </span>\n </div>\n }\n @if (Label.CreationDurationMS) {\n <div class=\"detail-field\">\n <span class=\"field-label\">Capture Duration</span>\n <span class=\"field-value\">{{FormatDuration(Label.CreationDurationMS)}}</span>\n </div>\n }\n <div class=\"detail-field\">\n <span class=\"field-label\">Items Captured</span>\n <span class=\"field-value\">{{LabelItems.length}}</span>\n </div>\n </div>\n </div>\n <!-- Parent / Children -->\n @if (Label.ParentID || ChildLabels.length > 0) {\n <div class=\"detail-section\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-sitemap section-icon\"></i> Hierarchy\n </h3>\n <!-- Parent reference -->\n @if (Label.ParentID) {\n <div class=\"hierarchy-item parent\">\n <i class=\"fa-solid fa-arrow-up hierarchy-icon\"></i>\n <span class=\"hierarchy-label\">Parent:</span>\n <span class=\"hierarchy-name\">\n @for (pl of AllLabels; track pl) {\n @if (pl.ID === Label.ParentID) {\n <span>{{pl.Name}}</span>\n }\n }\n </span>\n </div>\n }\n <!-- Children -->\n @if (ChildLabels.length > 0) {\n <div class=\"hierarchy-children\">\n @for (child of ChildLabels; track child) {\n <div class=\"hierarchy-item child\">\n <i class=\"fa-solid fa-arrow-turn-down-right hierarchy-icon\"></i>\n <span class=\"hierarchy-name\">{{child.Name}}</span>\n @if (child.RecordID) {\n <span class=\"hierarchy-meta\">{{child.RecordID | slice:0:12}}...</span>\n }\n @if (child.ItemCount) {\n <span class=\"hierarchy-meta\">{{child.ItemCount}} items</span>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n <!-- ======================================================== -->\n <!-- SNAPSHOTS TAB -->\n <!-- ======================================================== -->\n @if (ActiveTab === 'snapshots' && !IsLoadingItems) {\n <div class=\"tab-pane\">\n <!-- Snapshot toolbar -->\n <div class=\"snapshot-toolbar\">\n <div class=\"snapshot-search\">\n <i class=\"fa-solid fa-search snapshot-search-icon\"></i>\n <input type=\"text\" class=\"snapshot-search-input\"\n placeholder=\"Search by entity or record...\"\n [ngModel]=\"SnapshotSearch\"\n (ngModelChange)=\"OnSnapshotSearchChange($event)\" />\n </div>\n <div class=\"snapshot-sort-toggle\">\n <button class=\"toggle-btn-sm\" [class.active]=\"SnapshotSortBy === 'name'\"\n (click)=\"OnSnapshotSortChange('name')\" title=\"Sort by name\">\n @if (SnapshotSortBy === 'name' && SnapshotSortDir === 'asc') {\n <i class=\"fa-solid fa-arrow-down-a-z\"></i>\n }\n @if (SnapshotSortBy === 'name' && SnapshotSortDir === 'desc') {\n <i class=\"fa-solid fa-arrow-up-z-a\"></i>\n }\n @if (SnapshotSortBy !== 'name') {\n <i class=\"fa-solid fa-arrow-down-a-z\"></i>\n }\n </button>\n <button class=\"toggle-btn-sm\" [class.active]=\"SnapshotSortBy === 'count'\"\n (click)=\"OnSnapshotSortChange('count')\" title=\"Sort by record count\">\n @if (SnapshotSortBy === 'count' && SnapshotSortDir === 'desc') {\n <i class=\"fa-solid fa-arrow-down-9-1\"></i>\n }\n @if (SnapshotSortBy === 'count' && SnapshotSortDir === 'asc') {\n <i class=\"fa-solid fa-arrow-up-1-9\"></i>\n }\n @if (SnapshotSortBy !== 'count') {\n <i class=\"fa-solid fa-arrow-down-9-1\"></i>\n }\n </button>\n </div>\n <div class=\"snapshot-view-toggle\">\n <button class=\"toggle-btn-sm\" [class.active]=\"SnapshotViewMode === 'list'\" (click)=\"SnapshotViewMode = 'list'\">\n <i class=\"fa-solid fa-list\"></i>\n </button>\n <button class=\"toggle-btn-sm\" [class.active]=\"SnapshotViewMode === 'card'\" (click)=\"SnapshotViewMode = 'card'\">\n <i class=\"fa-solid fa-grip\"></i>\n </button>\n </div>\n </div>\n <!-- Entity groups -->\n <div class=\"snapshot-groups\">\n @for (group of FilteredSnapshotGroups; track group) {\n <div class=\"snapshot-group\">\n <div class=\"group-header\" (click)=\"ToggleSnapshotGroup(group)\">\n <i class=\"fa-solid fa-chevron-right group-chevron\" [class.expanded]=\"group.IsExpanded\"></i>\n <i [class]=\"group.EntityIcon\" class=\"group-entity-icon\"></i>\n <span class=\"group-name\">{{group.EntityName}} <span class=\"group-count-inline\">{{group.Items.length}}</span></span>\n </div>\n @if (group.IsExpanded) {\n <div class=\"group-content\">\n <!-- Loading names indicator -->\n @if (group.IsLoadingNames) {\n <div class=\"group-names-loading\">\n <mj-loading text=\"Loading record names...\" size=\"small\"></mj-loading>\n </div>\n }\n <!-- List mode -->\n @if (SnapshotViewMode === 'list') {\n @for (item of group.Items; track item) {\n <div class=\"snapshot-list-item\"\n (click)=\"OpenMicroView(group.EntityName, item.RecordID, item.RecordChangeID, item.DisplayName)\">\n <i class=\"fa-solid fa-file-lines snapshot-item-icon\"></i>\n <span class=\"snapshot-item-name\">{{item.DisplayName}}</span>\n <i class=\"fa-solid fa-chevron-right snapshot-item-arrow\"></i>\n </div>\n }\n }\n <!-- Card mode -->\n @if (SnapshotViewMode === 'card') {\n <div class=\"snapshot-cards\">\n @for (item of group.Items; track item) {\n <div class=\"snapshot-card\"\n (click)=\"OpenMicroView(group.EntityName, item.RecordID, item.RecordChangeID, item.DisplayName)\">\n <div class=\"snapshot-card-header\">\n <i class=\"fa-solid fa-file-lines\"></i>\n <span class=\"snapshot-card-name\">{{item.DisplayName}}</span>\n </div>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n <!-- Empty state -->\n @if (FilteredSnapshotGroups.length === 0) {\n <div class=\"tab-empty\">\n <i class=\"fa-solid fa-camera tab-empty-icon\"></i>\n <p>No snapshot items found.</p>\n </div>\n }\n </div>\n }\n <!-- ======================================================== -->\n <!-- DEPENDENCIES TAB -->\n <!-- ======================================================== -->\n @if (ActiveTab === 'dependencies') {\n <div class=\"tab-pane\">\n @if (IsLoadingDependencies) {\n <mj-loading text=\"Loading dependencies...\"></mj-loading>\n }\n @if (!IsLoadingDependencies && Label.EntityID) {\n <div class=\"dep-root\">\n <div class=\"dep-root-entity\">\n <i [class]=\"GetScopeIcon(Label.Scope)\" class=\"dep-root-icon\"></i>\n <span class=\"dep-root-name\">{{Label.Entity ?? resolveEntityName(Label.EntityID)}}</span>\n <span class=\"dep-root-badge\">Root</span>\n </div>\n @if (DependencyTree.length > 0) {\n @for (node of DependencyTree; track node) {\n <ng-container *ngTemplateOutlet=\"depNode; context: { $implicit: node }\"></ng-container>\n }\n }\n @if (DependencyTree.length === 0) {\n <div class=\"dep-empty\">\n <p>No dependent entities found.</p>\n </div>\n }\n </div>\n }\n @if (!IsLoadingDependencies && !Label.EntityID) {\n <div class=\"tab-empty\">\n <i class=\"fa-solid fa-diagram-project tab-empty-icon\"></i>\n <p>No entity associated with this label.</p>\n </div>\n }\n </div>\n }\n <!-- Dependency tree node template -->\n <ng-template #depNode let-node>\n <div class=\"dep-node\" [style.padding-left.px]=\"node.Depth * 24\">\n <div class=\"dep-node-header\" (click)=\"ToggleDependencyNode(node)\">\n @if (node.Children.length > 0) {\n <i class=\"fa-solid fa-chevron-right dep-chevron\"\n [class.expanded]=\"node.IsExpanded\"\n ></i>\n }\n @if (node.Children.length === 0) {\n <span class=\"dep-node-spacer\"></span>\n }\n <i [class]=\"resolveEntityIconByName(node.EntityName)\" class=\"dep-node-icon\"></i>\n <span class=\"dep-node-name\">{{node.EntityName}}</span>\n <span class=\"dep-node-field\">via {{node.RelationshipField}}</span>\n @if (node.Children.length > 0) {\n <span class=\"dep-node-count\">\n {{node.Children.length}} dep{{node.Children.length !== 1 ? 's' : ''}}\n </span>\n }\n </div>\n @if (node.IsExpanded && node.Children.length > 0) {\n @for (child of node.Children; track child) {\n <ng-container *ngTemplateOutlet=\"depNode; context: { $implicit: child }\"></ng-container>\n }\n }\n </div>\n </ng-template>\n <!-- ======================================================== -->\n <!-- CHANGES TAB -->\n <!-- ======================================================== -->\n @if (ActiveTab === 'changes') {\n <div class=\"tab-pane\">\n @if (IsLoadingDiff) {\n <mj-loading text=\"Computing differences...\"></mj-loading>\n }\n @if (!IsLoadingDiff && DiffResult) {\n <!-- Diff summary -->\n <div class=\"diff-summary\">\n <div class=\"diff-stat\">\n <span class=\"diff-stat-value change-modified\">{{DiffResult.Summary.Changed}}</span>\n <span class=\"diff-stat-label\">Changed</span>\n </div>\n <div class=\"diff-stat\">\n <span class=\"diff-stat-value\">{{DiffResult.Summary.Unchanged}}</span>\n <span class=\"diff-stat-label\">Unchanged</span>\n </div>\n <div class=\"diff-stat\">\n <span class=\"diff-stat-value\">{{DiffResult.Summary.EntitiesAffected}}</span>\n <span class=\"diff-stat-label\">Entities</span>\n </div>\n </div>\n <!-- Expand/collapse all -->\n @if (DiffResult.EntityGroups.length > 0) {\n <div class=\"diff-toolbar\">\n <button class=\"toolbar-action-btn\" (click)=\"ExpandAllDiffGroups()\" title=\"Expand all\">\n <i class=\"fa-solid fa-angles-down\"></i>\n <span>Expand All</span>\n </button>\n <button class=\"toolbar-action-btn\" (click)=\"CollapseAllDiffGroups()\" title=\"Collapse all\">\n <i class=\"fa-solid fa-angles-up\"></i>\n <span>Collapse All</span>\n </button>\n </div>\n }\n <!-- Entity diff groups -->\n @if (DiffResult.EntityGroups.length > 0) {\n <div class=\"diff-groups\">\n @for (group of DiffResult.EntityGroups; track group) {\n <div class=\"diff-group\">\n <div class=\"diff-group-header\" (click)=\"ToggleDiffGroup(group)\">\n <i class=\"fa-solid fa-chevron-right diff-chevron\" [class.expanded]=\"group.IsExpanded\"></i>\n <i [class]=\"resolveEntityIconByName(group.EntityName)\" class=\"diff-group-icon\"></i>\n <span class=\"diff-group-name\">{{group.EntityName}}</span>\n <span class=\"diff-group-count change-modified\">\n {{group.Records.length}} modified\n </span>\n </div>\n @if (group.IsExpanded) {\n <div class=\"diff-group-content\">\n @for (record of group.Records; track record) {\n <div class=\"diff-record\">\n <div class=\"diff-record-header\">\n <i [class]=\"GetChangeTypeIcon(record.ChangeType)\" [ngClass]=\"GetChangeTypeClass(record.ChangeType)\"></i>\n <span class=\"diff-record-id\">{{record.RecordID | slice:0:20}}...</span>\n </div>\n @if (record.FieldChanges.length > 0) {\n <div class=\"diff-fields\">\n @for (field of record.FieldChanges; track field) {\n <div class=\"diff-field\"\n [ngClass]=\"GetChangeTypeClass(field.ChangeType)\">\n <span class=\"diff-field-name\">{{field.FieldName}}</span>\n @if (field.OldValue) {\n <span class=\"diff-field-old\">{{field.OldValue | slice:0:40}}</span>\n }\n @if (field.OldValue && field.NewValue) {\n <i class=\"fa-solid fa-arrow-right diff-field-arrow\"></i>\n }\n @if (field.NewValue) {\n <span class=\"diff-field-new\">{{field.NewValue | slice:0:40}}</span>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n <!-- No changes -->\n @if (DiffResult.EntityGroups.length === 0 && DiffResult.Summary.Changed === 0) {\n <div class=\"tab-empty\">\n <i class=\"fa-solid fa-circle-check tab-empty-icon\" style=\"color: #10b981;\"></i>\n <p>No changes detected since this label was created.</p>\n </div>\n }\n }\n </div>\n }\n <!-- ======================================================== -->\n <!-- HISTORY TAB -->\n <!-- ======================================================== -->\n @if (ActiveTab === 'history') {\n <div class=\"tab-pane\">\n @if (IsLoadingHistory) {\n <mj-loading text=\"Loading history...\"></mj-loading>\n }\n @if (!IsLoadingHistory) {\n <!-- Restores -->\n @if (Restores.length > 0) {\n <div class=\"detail-section\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-clock-rotate-left section-icon\"></i> Restore Operations\n </h3>\n <div class=\"history-list\">\n @for (restore of Restores; track restore) {\n <div class=\"history-item\">\n <div class=\"history-item-icon\" [ngClass]=\"'status-' + (restore.Status ?? '').toLowerCase().replace(' ', '-')\">\n <i class=\"fa-solid fa-clock-rotate-left\"></i>\n </div>\n <div class=\"history-item-content\">\n <span class=\"history-item-title\">Restore {{restore.Status}}</span>\n <span class=\"history-item-meta\">\n {{restore.CompletedItems ?? 0}}/{{restore.TotalItems ?? 0}} items\n @if (restore.FailedItems) {\n <span> · {{restore.FailedItems}} failed</span>\n }\n </span>\n </div>\n <span class=\"history-item-date\">{{FormatRelativeDate(restore.__mj_CreatedAt)}}</span>\n </div>\n }\n </div>\n </div>\n }\n <!-- Related labels -->\n @if (RelatedLabels.length > 0) {\n <div class=\"detail-section\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-tags section-icon\"></i> Related Labels\n </h3>\n <div class=\"history-list\">\n @for (related of RelatedLabels; track related) {\n <div class=\"history-item\">\n <div class=\"history-item-icon\" style=\"background: rgba(99, 102, 241, 0.1); color: #6366f1;\">\n <i class=\"fa-solid fa-tag\"></i>\n </div>\n <div class=\"history-item-content\">\n <span class=\"history-item-title\">{{related.Name}}</span>\n <span class=\"history-item-meta\">\n <span class=\"inline-badge small\" [ngClass]=\"GetStatusClass(related.Status)\">{{related.Status}}</span>\n </span>\n </div>\n <span class=\"history-item-date\">{{FormatRelativeDate(related.__mj_CreatedAt)}}</span>\n </div>\n }\n </div>\n </div>\n }\n <!-- Empty -->\n @if (Restores.length === 0 && RelatedLabels.length === 0) {\n <div class=\"tab-empty\">\n <i class=\"fa-solid fa-clock-rotate-left tab-empty-icon\"></i>\n <p>No restore history or related labels found.</p>\n </div>\n }\n }\n </div>\n }\n </div>\n }\n\n <!-- ============================================================ -->\n <!-- INLINE MICRO VIEW (replaces tab content when active) -->\n <!-- ============================================================ -->\n @if (ShowMicroView && MicroViewRecord) {\n <div class=\"tab-content\">\n <mj-record-micro-view\n [Data]=\"MicroViewRecord\"\n [Inline]=\"true\"\n (Close)=\"OnBackFromMicroView()\"\n (EntityLinkClick)=\"OnEntityLinkClick($event)\"\n (OpenRecord)=\"OnOpenRecordClick($event)\">\n </mj-record-micro-view>\n </div>\n }\n</div>\n", styles: ["/* ================================================================= */\n/* BACKDROP */\n/* ================================================================= */\n\n.panel-backdrop {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0);\n z-index: 1000;\n transition: background 0.3s ease;\n pointer-events: none;\n}\n\n.panel-backdrop.visible {\n background: rgba(0, 0, 0, 0.3);\n pointer-events: auto;\n}\n\n/* ================================================================= */\n/* PANEL */\n/* ================================================================= */\n\n.detail-panel {\n position: fixed;\n top: 0;\n right: 0;\n height: 100vh;\n background: var(--card-background, #ffffff);\n box-shadow: -8px 0 32px rgba(0, 0, 0, 0.12);\n z-index: 1001;\n display: flex;\n flex-direction: column;\n transform: translateX(100%);\n transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);\n /* Width is set dynamically via [style.width.px] */\n min-width: 400px;\n max-width: 92vw;\n}\n\n.detail-panel.visible {\n transform: translateX(0);\n}\n\n/* ================================================================= */\n/* RESIZE HANDLE */\n/* ================================================================= */\n\n.resize-handle {\n position: absolute;\n left: -4px;\n top: 0;\n width: 8px;\n height: 100%;\n cursor: col-resize;\n z-index: 10;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.resize-handle:hover .resize-handle-grip,\n.resize-handle:active .resize-handle-grip {\n opacity: 1;\n background: #6366f1;\n}\n\n.resize-handle-grip {\n width: 3px;\n height: 40px;\n background: var(--border-color, #d1d5db);\n border-radius: 3px;\n opacity: 0;\n transition: opacity 0.2s ease, background 0.2s ease;\n}\n\n.resize-handle:hover {\n background: rgba(99, 102, 241, 0.04);\n}\n\n/* ================================================================= */\n/* BREADCRUMB BAR */\n/* ================================================================= */\n\n.breadcrumb-bar {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 24px;\n border-bottom: 1px solid var(--border-color, #e5e7eb);\n flex-shrink: 0;\n background: var(--hover-background, #f9fafb);\n}\n\n.breadcrumb-back {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n background: var(--card-background, #ffffff);\n border: 1px solid var(--border-color, #e5e7eb);\n border-radius: 8px;\n color: var(--text-secondary, #6b7280);\n cursor: pointer;\n transition: all 0.15s ease;\n font-size: 13px;\n flex-shrink: 0;\n}\n\n.breadcrumb-back:hover {\n background: var(--card-background, #ffffff);\n color: #6366f1;\n border-color: #6366f1;\n}\n\n.breadcrumb-trail {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n overflow: hidden;\n}\n\n.breadcrumb-item {\n display: flex;\n align-items: center;\n gap: 5px;\n color: var(--text-secondary, #6b7280);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.breadcrumb-item.clickable {\n cursor: pointer;\n transition: color 0.15s ease;\n}\n\n.breadcrumb-item.clickable:hover {\n color: #6366f1;\n}\n\n.breadcrumb-item.current {\n font-weight: 600;\n color: var(--text-primary, #1f2937);\n}\n\n.breadcrumb-separator {\n font-size: 9px;\n color: var(--text-tertiary, #d1d5db);\n flex-shrink: 0;\n}\n\n.breadcrumb-icon {\n font-size: 12px;\n flex-shrink: 0;\n}\n\n/* ================================================================= */\n/* HEADER */\n/* ================================================================= */\n\n.panel-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n padding: 20px 24px 16px;\n border-bottom: 1px solid var(--border-color, #e5e7eb);\n flex-shrink: 0;\n}\n\n.header-info {\n flex: 1;\n min-width: 0;\n}\n\n.header-title-row {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-wrap: wrap;\n}\n\n.header-scope-icon {\n color: #6366f1;\n font-size: 18px;\n flex-shrink: 0;\n}\n\n.header-title {\n font-size: 20px;\n font-weight: 700;\n color: var(--text-primary, #1f2937);\n margin: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.header-status {\n display: inline-flex;\n align-items: center;\n padding: 3px 10px;\n border-radius: 12px;\n font-size: 12px;\n font-weight: 600;\n flex-shrink: 0;\n}\n\n.header-scope-badge {\n display: inline-flex;\n align-items: center;\n padding: 3px 10px;\n background: rgba(99, 102, 241, 0.08);\n color: #6366f1;\n border-radius: 12px;\n font-size: 12px;\n font-weight: 600;\n flex-shrink: 0;\n}\n\n.header-description {\n margin: 8px 0 0 0;\n font-size: 14px;\n color: var(--text-secondary, #6b7280);\n line-height: 1.5;\n}\n\n.header-actions {\n display: flex;\n gap: 8px;\n align-items: center;\n flex-shrink: 0;\n margin-left: 16px;\n}\n\n.header-action-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n background: var(--card-background, #ffffff);\n border: 1px solid var(--border-color, #e5e7eb);\n border-radius: 8px;\n color: var(--text-secondary, #6b7280);\n cursor: pointer;\n transition: all 0.15s ease;\n font-size: 14px;\n}\n\n.header-action-btn:hover {\n background: var(--hover-background, #f3f4f6);\n color: var(--text-primary, #1f2937);\n border-color: var(--border-hover, #d1d5db);\n}\n\n.header-action-btn:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.close-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n background: none;\n border: none;\n border-radius: 8px;\n color: var(--text-secondary, #6b7280);\n cursor: pointer;\n transition: all 0.15s ease;\n font-size: 16px;\n}\n\n.close-btn:hover {\n background: var(--hover-background, #f3f4f6);\n color: var(--text-primary, #1f2937);\n}\n\n/* Status badges (reused) */\n.status-active { background: rgba(16, 185, 129, 0.1); color: #059669; }\n.status-archived { background: rgba(107, 114, 128, 0.1); color: #6b7280; }\n.status-restored { background: rgba(245, 158, 11, 0.1); color: #d97706; }\n\n/* ================================================================= */\n/* SUMMARY KPIs */\n/* ================================================================= */\n\n.summary-kpis {\n display: flex;\n gap: 12px;\n padding: 16px 24px;\n border-bottom: 1px solid var(--border-color, #e5e7eb);\n flex-shrink: 0;\n overflow-x: auto;\n}\n\n.summary-kpi {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n background: var(--hover-background, #f9fafb);\n border-radius: 10px;\n flex-shrink: 0;\n}\n\n.kpi-mini-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n border-radius: 8px;\n font-size: 15px;\n flex-shrink: 0;\n}\n\n.kpi-mini-content {\n display: flex;\n flex-direction: column;\n}\n\n.kpi-mini-value {\n font-size: 18px;\n font-weight: 700;\n color: var(--text-primary, #1f2937);\n line-height: 1;\n}\n\n.kpi-mini-label {\n font-size: 11px;\n color: var(--text-tertiary, #9ca3af);\n margin-top: 2px;\n}\n\n/* ================================================================= */\n/* TAB BAR */\n/* ================================================================= */\n\n.tab-bar {\n display: flex;\n gap: 0;\n padding: 0 24px;\n border-bottom: 1px solid var(--border-color, #e5e7eb);\n flex-shrink: 0;\n overflow-x: auto;\n}\n\n.tab {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 12px 16px;\n background: none;\n border: none;\n border-bottom: 2px solid transparent;\n font-size: 13px;\n font-weight: 500;\n color: var(--text-secondary, #6b7280);\n cursor: pointer;\n transition: all 0.15s ease;\n white-space: nowrap;\n}\n\n.tab:hover {\n color: var(--text-primary, #1f2937);\n background: var(--hover-background, #f9fafb);\n}\n\n.tab.active {\n color: #6366f1;\n border-bottom-color: #6366f1;\n font-weight: 600;\n}\n\n.tab-icon {\n font-size: 13px;\n}\n\n.tab-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 20px;\n height: 20px;\n padding: 0 6px;\n background: rgba(99, 102, 241, 0.1);\n color: #6366f1;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n}\n\n/* ================================================================= */\n/* TAB CONTENT */\n/* ================================================================= */\n\n.tab-content {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n}\n\n.tab-pane {\n padding: 20px 24px;\n animation: tabFadeIn 0.2s ease;\n}\n\n@keyframes tabFadeIn {\n from { opacity: 0; transform: translateY(4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.tab-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 20px;\n text-align: center;\n}\n\n.tab-empty-icon {\n font-size: 40px;\n color: var(--text-tertiary, #d1d5db);\n margin-bottom: 12px;\n}\n\n.tab-empty p {\n font-size: 14px;\n color: var(--text-tertiary, #9ca3af);\n margin: 0;\n}\n\n/* ================================================================= */\n/* OVERVIEW TAB */\n/* ================================================================= */\n\n.detail-section {\n margin-bottom: 24px;\n}\n\n.section-title {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n font-weight: 600;\n color: var(--text-primary, #1f2937);\n margin: 0 0 14px 0;\n padding-bottom: 10px;\n border-bottom: 1px solid var(--border-color, #f3f4f6);\n}\n\n.section-icon {\n color: var(--text-tertiary, #9ca3af);\n font-size: 14px;\n}\n\n.detail-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 14px;\n}\n\n.detail-field {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.field-label {\n font-size: 12px;\n font-weight: 600;\n color: var(--text-tertiary, #9ca3af);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.field-value {\n font-size: 14px;\n color: var(--text-primary, #1f2937);\n display: flex;\n align-items: center;\n}\n\n.field-mono {\n font-family: 'SF Mono', 'Fira Code', monospace;\n font-size: 13px;\n word-break: break-all;\n}\n\n.field-record-name {\n font-weight: 600;\n color: #6366f1;\n}\n\n/* Record card for Record-scoped labels on overview tab */\n.record-card {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n padding: 16px 18px;\n background: rgba(99, 102, 241, 0.04);\n border: 1px solid rgba(99, 102, 241, 0.15);\n border-radius: 12px;\n margin-bottom: 24px;\n}\n\n.record-card-main {\n display: flex;\n align-items: center;\n gap: 14px;\n min-width: 0;\n flex: 1;\n}\n\n.record-card-icon {\n font-size: 22px;\n color: #6366f1;\n flex-shrink: 0;\n}\n\n.record-card-info {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.record-card-name {\n font-size: 16px;\n font-weight: 700;\n color: var(--text-primary, #1f2937);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.record-card-name-loading {\n color: var(--text-tertiary, #9ca3af);\n font-weight: 500;\n font-style: italic;\n}\n\n.record-card-entity {\n font-size: 13px;\n color: var(--text-secondary, #6b7280);\n}\n\n.record-card-id {\n font-size: 12px;\n color: var(--text-tertiary, #9ca3af);\n font-family: 'SF Mono', 'Fira Code', monospace;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.record-card-open-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n background: #6366f1;\n color: #ffffff;\n border: none;\n border-radius: 8px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.15s ease;\n flex-shrink: 0;\n white-space: nowrap;\n}\n\n.record-card-open-btn:hover {\n background: #4f46e5;\n box-shadow: 0 2px 8px rgba(99, 102, 241, 0.3);\n}\n\n.inline-badge {\n display: inline-flex;\n align-items: center;\n padding: 3px 10px;\n border-radius: 12px;\n font-size: 12px;\n font-weight: 600;\n}\n\n.inline-badge.small {\n padding: 2px 8px;\n font-size: 11px;\n}\n\n.description-text {\n font-size: 14px;\n color: var(--text-secondary, #6b7280);\n line-height: 1.6;\n margin: 0;\n}\n\n/* Hierarchy */\n.hierarchy-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 12px;\n border-radius: 8px;\n font-size: 14px;\n transition: background 0.15s ease;\n}\n\n.hierarchy-item:hover {\n background: var(--hover-background, #f3f4f6);\n}\n\n.hierarchy-icon {\n color: var(--text-tertiary, #9ca3af);\n font-size: 13px;\n width: 16px;\n text-align: center;\n}\n\n.hierarchy-label {\n font-size: 12px;\n font-weight: 600;\n color: var(--text-tertiary, #9ca3af);\n text-transform: uppercase;\n}\n\n.hierarchy-name {\n font-weight: 500;\n color: var(--text-primary, #1f2937);\n}\n\n.hierarchy-meta {\n font-size: 12px;\n color: var(--text-tertiary, #9ca3af);\n font-family: monospace;\n}\n\n.hierarchy-children {\n margin-left: 16px;\n border-left: 2px solid var(--border-color, #e5e7eb);\n padding-left: 8px;\n}\n\n/* ================================================================= */\n/* SNAPSHOTS TAB */\n/* ================================================================= */\n\n.snapshot-toolbar {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 16px;\n}\n\n.snapshot-search {\n position: relative;\n flex: 1;\n}\n\n.snapshot-search-icon {\n position: absolute;\n left: 12px;\n top: 50%;\n transform: translateY(-50%);\n color: var(--text-tertiary, #9ca3af);\n font-size: 13px;\n}\n\n.snapshot-search-input {\n width: 100%;\n padding: 8px 12px 8px 36px;\n background: var(--card-background, #ffffff);\n border: 1px solid var(--border-color, #e5e7eb);\n border-radius: 8px;\n font-size: 13px;\n color: var(--text-primary, #1f2937);\n outline: none;\n box-sizing: border-box;\n transition: border-color 0.15s ease;\n}\n\n.snapshot-search-input:focus {\n border-color: #6366f1;\n}\n\n.snapshot-sort-toggle,\n.snapshot-view-toggle {\n display: flex;\n background: var(--card-background, #ffffff);\n border: 1px solid var(--border-color, #e5e7eb);\n border-radius: 6px;\n overflow: hidden;\n}\n\n.toggle-btn-sm {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 30px;\n height: 30px;\n background: transparent;\n border: none;\n cursor: pointer;\n color: var(--text-tertiary, #9ca3af);\n transition: all 0.15s ease;\n font-size: 12px;\n}\n\n.toggle-btn-sm:hover {\n color: var(--text-primary, #1f2937);\n}\n\n.toggle-btn-sm.active {\n color: #6366f1;\n background: rgba(99, 102, 241, 0.08);\n}\n\n.toggle-btn-sm + .toggle-btn-sm {\n border-left: 1px solid var(--border-color, #e5e7eb);\n}\n\n/* Snapshot groups */\n.snapshot-groups {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.snapshot-group {\n background: var(--card-background, #ffffff);\n border: 1px solid var(--border-color, #e5e7eb);\n border-radius: 10px;\n overflow: hidden;\n}\n\n.group-header {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 14px;\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.group-header:hover {\n background: var(--hover-background, #f9fafb);\n}\n\n.group-chevron {\n font-size: 11px;\n color: var(--text-tertiary, #9ca3af);\n transition: transform 0.2s ease;\n width: 14px;\n text-align: center;\n}\n\n.group-chevron.expanded {\n transform: rotate(90deg);\n}\n\n.group-entity-icon {\n color: #3b82f6;\n font-size: 14px;\n}\n\n.group-name {\n font-size: 14px;\n font-weight: 600;\n color: var(--text-primary, #1f2937);\n flex: 1;\n}\n\n.group-count-inline {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 20px;\n height: 20px;\n padding: 0 6px;\n margin-left: 6px;\n background: rgba(59, 130, 246, 0.1);\n color: #3b82f6;\n border-radius: 10px;\n font-size: 12px;\n font-weight: 600;\n vertical-align: middle;\n}\n\n.group-content {\n border-top: 1px solid var(--border-color, #f3f4f6);\n animation: tabFadeIn 0.15s ease;\n}\n\n.group-names-loading {\n display: flex;\n justify-content: center;\n padding: 8px 14px;\n}\n\n/* Snapshot list items */\n.snapshot-list-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px 10px 40px;\n cursor: pointer;\n transition: background 0.15s ease;\n border-bottom: 1px solid var(--border-color, #f3f4f6);\n}\n\n.snapshot-list-item:last-child {\n border-bottom: none;\n}\n\n.snapshot-list-item:hover {\n background: rgba(99, 102, 241, 0.04);\n}\n\n.snapshot-item-icon {\n color: var(--text-tertiary, #9ca3af);\n font-size: 13px;\n}\n\n.snapshot-item-name {\n flex: 1;\n font-size: 13px;\n font-weight: 500;\n color: var(--text-primary, #1f2937);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.snapshot-item-id {\n font-size: 12px;\n color: var(--text-tertiary, #9ca3af);\n font-family: monospace;\n}\n\n.snapshot-item-arrow {\n color: var(--text-tertiary, #d1d5db);\n font-size: 11px;\n transition: transform 0.15s ease, color 0.15s ease;\n}\n\n.snapshot-list-item:hover .snapshot-item-arrow {\n color: #6366f1;\n transform: translateX(2px);\n}\n\n/* Snapshot cards */\n.snapshot-cards {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));\n gap: 10px;\n padding: 12px 14px;\n}\n\n.snapshot-card {\n padding: 12px;\n background: var(--hover-background, #f9fafb);\n border: 1px solid var(--border-color, #e5e7eb);\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.snapshot-card:hover {\n border-color: #6366f1;\n box-shadow: 0 2px 8px rgba(99, 102, 241, 0.1);\n}\n\n.snapshot-card-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 6px;\n font-size: 13px;\n color: var(--text-primary, #1f2937);\n}\n\n.snapshot-card-header i {\n color: var(--text-tertiary, #9ca3af);\n font-size: 12px;\n}\n\n.snapshot-card-name {\n font-weight: 500;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.snapshot-card-meta {\n font-size: 11px;\n color: var(--text-tertiary, #9ca3af);\n font-family: monospace;\n}\n\n/* ================================================================= */\n/* DEPENDENCIES TAB */\n/* ================================================================= */\n\n.dep-root {\n padding: 4px 0;\n}\n\n.dep-root-entity {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 14px;\n background: rgba(99, 102, 241, 0.06);\n border: 1px solid rgba(99, 102, 241, 0.15);\n border-radius: 10px;\n margin-bottom: 12px;\n}\n\n.dep-root-icon {\n color: #6366f1;\n font-size: 16px;\n}\n\n.dep-root-name {\n font-size: 15px;\n font-weight: 600;\n color: var(--text-primary, #1f2937);\n flex: 1;\n}\n\n.dep-root-badge {\n padding: 2px 10px;\n background: #6366f1;\n color: #ffffff;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n}\n\n.dep-node {\n margin-left: 8px;\n border-left: 2px solid var(--border-color, #e5e7eb);\n}\n\n.dep-node-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n cursor: pointer;\n transition: background 0.15s ease;\n border-radius: 6px;\n}\n\n.dep-node-header:hover {\n background: var(--hover-background, #f3f4f6);\n}\n\n.dep-chevron {\n font-size: 10px;\n color: var(--text-tertiary, #9ca3af);\n transition: transform 0.2s ease;\n width: 12px;\n text-align: center;\n}\n\n.dep-chevron.expanded {\n transform: rotate(90deg);\n}\n\n.dep-node-spacer {\n width: 12px;\n}\n\n.dep-node-icon {\n color: #3b82f6;\n font-size: 13px;\n}\n\n.dep-node-name {\n font-size: 13px;\n font-weight: 500;\n color: var(--text-primary, #1f2937);\n}\n\n.dep-node-field {\n font-size: 11px;\n color: var(--text-tertiary, #9ca3af);\n font-style: italic;\n}\n\n.dep-node-count {\n font-size: 11px;\n color: var(--text-tertiary, #9ca3af);\n padding: 1px 8px;\n background: var(--hover-background, #f3f4f6);\n border-radius: 8px;\n}\n\n.dep-empty {\n padding: 20px;\n text-align: center;\n}\n\n.dep-empty p {\n font-size: 14px;\n color: var(--text-tertiary, #9ca3af);\n margin: 0;\n}\n\n/* ================================================================= */\n/* CHANGES TAB */\n/* ================================================================= */\n\n.diff-summary {\n display: flex;\n gap: 16px;\n margin-bottom: 20px;\n}\n\n.diff-stat {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 12px 20px;\n background: var(--hover-background, #f9fafb);\n border-radius: 10px;\n min-width: 80px;\n}\n\n.diff-stat-value {\n font-size: 24px;\n font-weight: 700;\n color: var(--text-primary, #1f2937);\n line-height: 1;\n}\n\n.diff-stat-label {\n font-size: 12px;\n color: var(--text-tertiary, #9ca3af);\n margin-top: 4px;\n}\n\n/* Diff toolbar */\n.diff-toolbar {\n display: flex;\n gap: 8px;\n margin-bottom: 12px;\n}\n\n.toolbar-action-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border: 1px solid var(--border-color, #e5e7eb);\n border-radius: 6px;\n background: var(--card-background, #ffffff);\n color: var(--text-secondary, #6b7280);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.toolbar-action-btn i {\n font-size: 11px;\n}\n\n.toolbar-action-btn:hover {\n border-color: #6366f1;\n color: #6366f1;\n background: rgba(99, 102, 241, 0.06);\n}\n\n.diff-groups {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.diff-group {\n background: var(--card-background, #ffffff);\n border: 1px solid var(--border-color, #e5e7eb);\n border-radius: 10px;\n overflow: hidden;\n}\n\n.diff-group-header {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 14px;\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.diff-group-header:hover {\n background: var(--hover-background, #f9fafb);\n}\n\n.diff-chevron {\n font-size: 11px;\n color: var(--text-tertiary, #9ca3af);\n transition: transform 0.2s ease;\n width: 14px;\n text-align: center;\n}\n\n.diff-chevron.expanded {\n transform: rotate(90deg);\n}\n\n.diff-group-icon {\n color: #3b82f6;\n font-size: 14px;\n}\n\n.diff-group-name {\n font-size: 14px;\n font-weight: 600;\n color: var(--text-primary, #1f2937);\n flex: 1;\n}\n\n.diff-group-count {\n font-size: 12px;\n font-weight: 600;\n padding: 2px 10px;\n border-radius: 10px;\n}\n\n.diff-group-content {\n border-top: 1px solid var(--border-color, #f3f4f6);\n animation: tabFadeIn 0.15s ease;\n}\n\n.diff-record {\n padding: 10px 14px 10px 40px;\n border-bottom: 1px solid var(--border-color, #f3f4f6);\n}\n\n.diff-record:last-child {\n border-bottom: none;\n}\n\n.diff-record-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 8px;\n}\n\n.diff-record-id {\n font-size: 13px;\n font-weight: 500;\n color: var(--text-primary, #1f2937);\n font-family: monospace;\n}\n\n.diff-fields {\n display: flex;\n flex-direction: column;\n gap: 4px;\n margin-left: 22px;\n}\n\n.diff-field {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 12px;\n}\n\n.diff-field-name {\n font-weight: 600;\n color: var(--text-primary, #1f2937);\n min-width: 100px;\n}\n\n.diff-field-old {\n color: #ef4444;\n text-decoration: line-through;\n opacity: 0.7;\n font-family: monospace;\n font-size: 12px;\n}\n\n.diff-field-arrow {\n color: var(--text-tertiary, #9ca3af);\n font-size: 10px;\n}\n\n.diff-field-new {\n color: #10b981;\n font-family: monospace;\n font-size: 12px;\n}\n\n/* Change type colors */\n.change-added { color: #10b981; }\n.change-modified { color: #f59e0b; }\n.change-removed { color: #ef4444; }\n\n.diff-field.change-added { background: rgba(16, 185, 129, 0.06); }\n.diff-field.change-modified { background: rgba(245, 158, 11, 0.06); }\n.diff-field.change-removed { background: rgba(239, 68, 68, 0.06); }\n\n/* ================================================================= */\n/* HISTORY TAB */\n/* ================================================================= */\n\n.history-list {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.history-item {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 12px;\n border-radius: 8px;\n transition: background 0.15s ease;\n}\n\n.history-item:hover {\n background: var(--hover-background, #f3f4f6);\n}\n\n.history-item-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n border-radius: 8px;\n font-size: 14px;\n flex-shrink: 0;\n}\n\n.history-item-icon.status-complete { background: rgba(16, 185, 129, 0.1); color: #10b981; }\n.history-item-icon.status-error { background: rgba(239, 68, 68, 0.1); color: #ef4444; }\n.history-item-icon.status-partial { background: rgba(245, 158, 11, 0.1); color: #f59e0b; }\n.history-item-icon.status-in-progress { background: rgba(59, 130, 246, 0.1); color: #3b82f6; }\n.history-item-icon.status-pending { background: rgba(107, 114, 128, 0.1); color: #6b7280; }\n\n.history-item-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.history-item-title {\n font-size: 14px;\n font-weight: 500;\n color: var(--text-primary, #1f2937);\n}\n\n.history-item-meta {\n font-size: 12px;\n color: var(--text-tertiary, #9ca3af);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.history-item-date {\n font-size: 12px;\n color: var(--text-tertiary, #9ca3af);\n flex-shrink: 0;\n}\n\n/* ================================================================= */\n/* RESPONSIVE */\n/* ================================================================= */\n\n@media (max-width: 1024px) {\n .detail-panel {\n min-width: 360px;\n }\n\n .summary-kpis {\n flex-wrap: wrap;\n }\n}\n\n@media (max-width: 768px) {\n .detail-panel {\n width: 100% !important;\n min-width: unset;\n }\n\n .resize-handle {\n display: none;\n }\n\n .detail-grid {\n grid-template-columns: 1fr;\n }\n\n .summary-kpis {\n flex-wrap: wrap;\n gap: 8px;\n padding: 12px 16px;\n }\n\n .summary-kpi {\n padding: 8px 10px;\n }\n\n .panel-header {\n padding: 16px 16px 12px;\n }\n\n .tab-bar {\n padding: 0 16px;\n }\n\n .tab-pane {\n padding: 16px;\n }\n\n .breadcrumb-bar {\n padding: 8px 16px;\n }\n\n .snapshot-toolbar {\n flex-direction: column;\n align-items: stretch;\n }\n\n .diff-summary {\n flex-wrap: wrap;\n gap: 8px;\n }\n\n .diff-stat {\n padding: 10px 14px;\n min-width: 60px;\n }\n}\n\n@media (max-width: 480px) {\n .header-title {\n font-size: 16px;\n }\n\n .header-title-row {\n gap: 6px;\n }\n\n .header-actions {\n margin-left: 8px;\n }\n\n .tab {\n padding: 10px 10px;\n font-size: 12px;\n }\n\n .snapshot-cards {\n grid-template-columns: 1fr;\n }\n}\n"] }]
|
|
2076
|
+
args: [{ standalone: false, selector: 'mj-label-detail-panel', changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- Backdrop overlay -->\n<div class=\"panel-backdrop\" [class.visible]=\"IsVisible\" (click)=\"OnBackdropClick()\"></div>\n\n<!-- Slide-in panel -->\n<div class=\"detail-panel\" [class.visible]=\"IsVisible\" [style.width.px]=\"PanelWidthPx\">\n <!-- Resize handle -->\n <div class=\"resize-handle\" (mousedown)=\"OnResizeStart($event)\">\n <div class=\"resize-handle-grip\"></div>\n </div>\n\n <!-- ============================================================ -->\n <!-- HEADER -->\n <!-- ============================================================ -->\n <div class=\"panel-header\">\n <div class=\"header-info\">\n <div class=\"header-title-row\">\n <i [class]=\"GetScopeIcon(Label.Scope)\" class=\"header-scope-icon\"></i>\n <h2 class=\"header-title\">{{Label.Name}}</h2>\n <span class=\"header-status\" [ngClass]=\"GetStatusClass(Label.Status)\">\n {{Label.Status}}\n </span>\n <span class=\"header-scope-badge\">{{Label.Scope}}</span>\n </div>\n @if (Label.Description) {\n <p class=\"header-description\">{{Label.Description}}</p>\n }\n </div>\n <div class=\"header-actions\">\n <button class=\"header-action-btn\" (click)=\"OnArchive()\"\n [disabled]=\"IsArchiving || Label.Status === 'Archived'\"\n title=\"Archive this label\">\n <i class=\"fa-solid fa-box-archive\"></i>\n </button>\n <button class=\"header-action-btn\" (click)=\"OnTabChange('changes')\" title=\"View changes\">\n <i class=\"fa-solid fa-code-compare\"></i>\n </button>\n <button class=\"close-btn\" (click)=\"OnClose()\">\n <i class=\"fa-solid fa-xmark\"></i>\n </button>\n </div>\n </div>\n\n <!-- ============================================================ -->\n <!-- SUMMARY KPIs -->\n <!-- ============================================================ -->\n <div class=\"summary-kpis\">\n <div class=\"summary-kpi\">\n <div class=\"kpi-mini-icon\" style=\"background: rgba(99, 102, 241, 0.1); color: #6366f1;\">\n <i class=\"fa-solid fa-camera\"></i>\n </div>\n <div class=\"kpi-mini-content\">\n <span class=\"kpi-mini-value\">{{LabelItems.length || Label.ItemCount || GetItemCount(Label.ID)}}</span>\n <span class=\"kpi-mini-label\">Items</span>\n </div>\n </div>\n <div class=\"summary-kpi\">\n <div class=\"kpi-mini-icon\" style=\"background: rgba(59, 130, 246, 0.1); color: #3b82f6;\">\n <i class=\"fa-solid fa-table\"></i>\n </div>\n <div class=\"kpi-mini-content\">\n <span class=\"kpi-mini-value\">{{UniqueEntityCount}}</span>\n <span class=\"kpi-mini-label\">Entities</span>\n </div>\n </div>\n @if (Label.CreationDurationMS) {\n <div class=\"summary-kpi\">\n <div class=\"kpi-mini-icon\" style=\"background: rgba(16, 185, 129, 0.1); color: #10b981;\">\n <i class=\"fa-solid fa-stopwatch\"></i>\n </div>\n <div class=\"kpi-mini-content\">\n <span class=\"kpi-mini-value\">{{FormatDuration(Label.CreationDurationMS)}}</span>\n <span class=\"kpi-mini-label\">Duration</span>\n </div>\n </div>\n }\n @if (ChildLabels.length > 0) {\n <div class=\"summary-kpi\">\n <div class=\"kpi-mini-icon\" style=\"background: rgba(245, 158, 11, 0.1); color: #f59e0b;\">\n <i class=\"fa-solid fa-layer-group\"></i>\n </div>\n <div class=\"kpi-mini-content\">\n <span class=\"kpi-mini-value\">{{ChildLabels.length}}</span>\n <span class=\"kpi-mini-label\">Children</span>\n </div>\n </div>\n }\n </div>\n\n <!-- ============================================================ -->\n <!-- TAB BAR (hidden when micro-view is active) -->\n <!-- ============================================================ -->\n @if (!ShowMicroView) {\n <div class=\"tab-bar\">\n <button class=\"tab\" [class.active]=\"ActiveTab === 'overview'\" (click)=\"OnTabChange('overview')\">\n <i class=\"fa-solid fa-info-circle tab-icon\"></i> Overview\n </button>\n <button class=\"tab\" [class.active]=\"ActiveTab === 'snapshots'\" (click)=\"OnTabChange('snapshots')\">\n <i class=\"fa-solid fa-camera tab-icon\"></i> Snapshots\n @if (LabelItems.length > 0) {\n <span class=\"tab-count\">{{LabelItems.length}}</span>\n }\n </button>\n <button class=\"tab\" [class.active]=\"ActiveTab === 'dependencies'\" (click)=\"OnTabChange('dependencies')\">\n <i class=\"fa-solid fa-diagram-project tab-icon\"></i> Dependencies\n </button>\n <button class=\"tab\" [class.active]=\"ActiveTab === 'changes'\" (click)=\"OnTabChange('changes')\">\n <i class=\"fa-solid fa-code-compare tab-icon\"></i> Changes\n </button>\n <button class=\"tab\" [class.active]=\"ActiveTab === 'history'\" (click)=\"OnTabChange('history')\">\n <i class=\"fa-solid fa-clock-rotate-left tab-icon\"></i> History\n </button>\n </div>\n }\n\n <!-- ============================================================ -->\n <!-- BREADCRUMB (shown when micro-view is active) -->\n <!-- ============================================================ -->\n @if (ShowMicroView) {\n <div class=\"breadcrumb-bar\">\n <button class=\"breadcrumb-back\" (click)=\"OnBackFromMicroView()\">\n <i class=\"fa-solid fa-arrow-left\"></i>\n </button>\n <div class=\"breadcrumb-trail\">\n <span class=\"breadcrumb-item clickable\" (click)=\"OnBackFromMicroView()\">\n <i class=\"fa-solid fa-camera breadcrumb-icon\"></i> Snapshots\n </span>\n <i class=\"fa-solid fa-chevron-right breadcrumb-separator\"></i>\n <span class=\"breadcrumb-item current\">\n <i class=\"fa-solid fa-file-lines breadcrumb-icon\"></i> {{BreadcrumbLabel}}\n </span>\n </div>\n </div>\n }\n\n <!-- ============================================================ -->\n <!-- TAB CONTENT (scrollable) - or inline micro-view -->\n <!-- ============================================================ -->\n @if (!ShowMicroView) {\n <div class=\"tab-content\">\n <!-- Loading state for items -->\n @if (IsLoadingItems && ActiveTab !== 'dependencies') {\n <mj-loading text=\"Loading label data...\"></mj-loading>\n }\n <!-- ======================================================== -->\n <!-- OVERVIEW TAB -->\n <!-- ======================================================== -->\n @if (ActiveTab === 'overview' && !IsLoadingItems) {\n <div class=\"tab-pane\">\n <!-- Metadata section -->\n <div class=\"detail-section\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-info-circle section-icon\"></i> Label Details\n </h3>\n <div class=\"detail-grid\">\n <div class=\"detail-field\">\n <span class=\"field-label\">Name</span>\n <span class=\"field-value\">{{Label.Name}}</span>\n </div>\n <div class=\"detail-field\">\n <span class=\"field-label\">Status</span>\n <span class=\"field-value\">\n <span class=\"inline-badge\" [ngClass]=\"GetStatusClass(Label.Status)\">{{Label.Status}}</span>\n </span>\n </div>\n <div class=\"detail-field\">\n <span class=\"field-label\">Scope</span>\n <span class=\"field-value\">\n <i [class]=\"GetScopeIcon(Label.Scope)\" style=\"margin-right: 6px;\"></i>{{Label.Scope}}\n </span>\n </div>\n @if (Label.EntityID) {\n <div class=\"detail-field\">\n <span class=\"field-label\">Entity</span>\n <span class=\"field-value\">\n <i [class]=\"OverviewEntityIcon\" style=\"margin-right: 6px; color: #3b82f6;\"></i>{{Label.Entity ?? resolveEntityName(Label.EntityID)}}\n </span>\n </div>\n }\n @if (Label.RecordID && Label.Scope !== 'Record') {\n <div class=\"detail-field\">\n <span class=\"field-label\">Record ID</span>\n <span class=\"field-value field-mono\">{{FormatRecordID(Label.RecordID)}}</span>\n </div>\n }\n @if (Label.ExternalSystemID) {\n <div class=\"detail-field\">\n <span class=\"field-label\">External Ref</span>\n <span class=\"field-value field-mono\">{{Label.ExternalSystemID}}</span>\n </div>\n }\n </div>\n </div>\n <!-- Record card (Record-scoped labels) -->\n @if (Label.Scope === 'Record' && Label.RecordID) {\n <div class=\"record-card\">\n <div class=\"record-card-main\">\n <i [class]=\"OverviewEntityIcon\" class=\"record-card-icon\"></i>\n <div class=\"record-card-info\">\n @if (OverviewRecordName) {\n <span class=\"record-card-name\">{{OverviewRecordName}}</span>\n }\n @if (!OverviewRecordName) {\n <span class=\"record-card-name record-card-name-loading\">Loading...</span>\n }\n <span class=\"record-card-entity\">{{Label.Entity ?? resolveEntityName(Label.EntityID)}}</span>\n <span class=\"record-card-id\">{{FormatRecordID(Label.RecordID)}}</span>\n </div>\n </div>\n <button class=\"record-card-open-btn\" (click)=\"OnOpenOverviewRecord()\" title=\"Open record\">\n <i class=\"fa-solid fa-arrow-up-right-from-square\"></i> Open\n </button>\n </div>\n }\n <!-- Description -->\n @if (Label.Description) {\n <div class=\"detail-section\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-align-left section-icon\"></i> Description\n </h3>\n <p class=\"description-text\">{{Label.Description}}</p>\n </div>\n }\n <!-- Creation info -->\n <div class=\"detail-section\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-clock section-icon\"></i> Creation\n </h3>\n <div class=\"detail-grid\">\n <div class=\"detail-field\">\n <span class=\"field-label\">Created</span>\n <span class=\"field-value\">{{FormatDate(Label.__mj_CreatedAt)}}</span>\n </div>\n @if (Label.CreatedByUser) {\n <div class=\"detail-field\">\n <span class=\"field-label\">Created By</span>\n <span class=\"field-value\">\n <i class=\"fa-solid fa-user\" style=\"margin-right: 6px; opacity: 0.5;\"></i>{{Label.CreatedByUser}}\n </span>\n </div>\n }\n @if (Label.CreationDurationMS) {\n <div class=\"detail-field\">\n <span class=\"field-label\">Capture Duration</span>\n <span class=\"field-value\">{{FormatDuration(Label.CreationDurationMS)}}</span>\n </div>\n }\n <div class=\"detail-field\">\n <span class=\"field-label\">Items Captured</span>\n <span class=\"field-value\">{{LabelItems.length}}</span>\n </div>\n </div>\n </div>\n <!-- Parent / Children -->\n @if (Label.ParentID || ChildLabels.length > 0) {\n <div class=\"detail-section\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-sitemap section-icon\"></i> Hierarchy\n </h3>\n <!-- Parent reference -->\n @if (Label.ParentID) {\n <div class=\"hierarchy-item parent\">\n <i class=\"fa-solid fa-arrow-up hierarchy-icon\"></i>\n <span class=\"hierarchy-label\">Parent:</span>\n <span class=\"hierarchy-name\">\n @for (pl of AllLabels; track pl) {\n @if (IsParentLabel(pl)) {\n <span>{{pl.Name}}</span>\n }\n }\n </span>\n </div>\n }\n <!-- Children -->\n @if (ChildLabels.length > 0) {\n <div class=\"hierarchy-children\">\n @for (child of ChildLabels; track child) {\n <div class=\"hierarchy-item child\">\n <i class=\"fa-solid fa-arrow-turn-down-right hierarchy-icon\"></i>\n <span class=\"hierarchy-name\">{{child.Name}}</span>\n @if (child.RecordID) {\n <span class=\"hierarchy-meta\">{{child.RecordID | slice:0:12}}...</span>\n }\n @if (child.ItemCount) {\n <span class=\"hierarchy-meta\">{{child.ItemCount}} items</span>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n <!-- ======================================================== -->\n <!-- SNAPSHOTS TAB -->\n <!-- ======================================================== -->\n @if (ActiveTab === 'snapshots' && !IsLoadingItems) {\n <div class=\"tab-pane\">\n <!-- Snapshot toolbar -->\n <div class=\"snapshot-toolbar\">\n <div class=\"snapshot-search\">\n <i class=\"fa-solid fa-search snapshot-search-icon\"></i>\n <input type=\"text\" class=\"snapshot-search-input\"\n placeholder=\"Search by entity or record...\"\n [ngModel]=\"SnapshotSearch\"\n (ngModelChange)=\"OnSnapshotSearchChange($event)\" />\n </div>\n <div class=\"snapshot-sort-toggle\">\n <button class=\"toggle-btn-sm\" [class.active]=\"SnapshotSortBy === 'name'\"\n (click)=\"OnSnapshotSortChange('name')\" title=\"Sort by name\">\n @if (SnapshotSortBy === 'name' && SnapshotSortDir === 'asc') {\n <i class=\"fa-solid fa-arrow-down-a-z\"></i>\n }\n @if (SnapshotSortBy === 'name' && SnapshotSortDir === 'desc') {\n <i class=\"fa-solid fa-arrow-up-z-a\"></i>\n }\n @if (SnapshotSortBy !== 'name') {\n <i class=\"fa-solid fa-arrow-down-a-z\"></i>\n }\n </button>\n <button class=\"toggle-btn-sm\" [class.active]=\"SnapshotSortBy === 'count'\"\n (click)=\"OnSnapshotSortChange('count')\" title=\"Sort by record count\">\n @if (SnapshotSortBy === 'count' && SnapshotSortDir === 'desc') {\n <i class=\"fa-solid fa-arrow-down-9-1\"></i>\n }\n @if (SnapshotSortBy === 'count' && SnapshotSortDir === 'asc') {\n <i class=\"fa-solid fa-arrow-up-1-9\"></i>\n }\n @if (SnapshotSortBy !== 'count') {\n <i class=\"fa-solid fa-arrow-down-9-1\"></i>\n }\n </button>\n </div>\n <div class=\"snapshot-view-toggle\">\n <button class=\"toggle-btn-sm\" [class.active]=\"SnapshotViewMode === 'list'\" (click)=\"SnapshotViewMode = 'list'\">\n <i class=\"fa-solid fa-list\"></i>\n </button>\n <button class=\"toggle-btn-sm\" [class.active]=\"SnapshotViewMode === 'card'\" (click)=\"SnapshotViewMode = 'card'\">\n <i class=\"fa-solid fa-grip\"></i>\n </button>\n </div>\n </div>\n <!-- Entity groups -->\n <div class=\"snapshot-groups\">\n @for (group of FilteredSnapshotGroups; track group) {\n <div class=\"snapshot-group\">\n <div class=\"group-header\" (click)=\"ToggleSnapshotGroup(group)\">\n <i class=\"fa-solid fa-chevron-right group-chevron\" [class.expanded]=\"group.IsExpanded\"></i>\n <i [class]=\"group.EntityIcon\" class=\"group-entity-icon\"></i>\n <span class=\"group-name\">{{group.EntityName}} <span class=\"group-count-inline\">{{group.Items.length}}</span></span>\n </div>\n @if (group.IsExpanded) {\n <div class=\"group-content\">\n <!-- Loading names indicator -->\n @if (group.IsLoadingNames) {\n <div class=\"group-names-loading\">\n <mj-loading text=\"Loading record names...\" size=\"small\"></mj-loading>\n </div>\n }\n <!-- List mode -->\n @if (SnapshotViewMode === 'list') {\n @for (item of group.Items; track item) {\n <div class=\"snapshot-list-item\"\n (click)=\"OpenMicroView(group.EntityName, item.RecordID, item.RecordChangeID, item.DisplayName)\">\n <i class=\"fa-solid fa-file-lines snapshot-item-icon\"></i>\n <span class=\"snapshot-item-name\">{{item.DisplayName}}</span>\n <i class=\"fa-solid fa-chevron-right snapshot-item-arrow\"></i>\n </div>\n }\n }\n <!-- Card mode -->\n @if (SnapshotViewMode === 'card') {\n <div class=\"snapshot-cards\">\n @for (item of group.Items; track item) {\n <div class=\"snapshot-card\"\n (click)=\"OpenMicroView(group.EntityName, item.RecordID, item.RecordChangeID, item.DisplayName)\">\n <div class=\"snapshot-card-header\">\n <i class=\"fa-solid fa-file-lines\"></i>\n <span class=\"snapshot-card-name\">{{item.DisplayName}}</span>\n </div>\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n <!-- Empty state -->\n @if (FilteredSnapshotGroups.length === 0) {\n <div class=\"tab-empty\">\n <i class=\"fa-solid fa-camera tab-empty-icon\"></i>\n <p>No snapshot items found.</p>\n </div>\n }\n </div>\n }\n <!-- ======================================================== -->\n <!-- DEPENDENCIES TAB -->\n <!-- ======================================================== -->\n @if (ActiveTab === 'dependencies') {\n <div class=\"tab-pane\">\n @if (IsLoadingDependencies) {\n <mj-loading text=\"Loading dependencies...\"></mj-loading>\n }\n @if (!IsLoadingDependencies && Label.EntityID) {\n <div class=\"dep-root\">\n <div class=\"dep-root-entity\">\n <i [class]=\"GetScopeIcon(Label.Scope)\" class=\"dep-root-icon\"></i>\n <span class=\"dep-root-name\">{{Label.Entity ?? resolveEntityName(Label.EntityID)}}</span>\n <span class=\"dep-root-badge\">Root</span>\n </div>\n @if (DependencyTree.length > 0) {\n @for (node of DependencyTree; track node) {\n <ng-container *ngTemplateOutlet=\"depNode; context: { $implicit: node }\"></ng-container>\n }\n }\n @if (DependencyTree.length === 0) {\n <div class=\"dep-empty\">\n <p>No dependent entities found.</p>\n </div>\n }\n </div>\n }\n @if (!IsLoadingDependencies && !Label.EntityID) {\n <div class=\"tab-empty\">\n <i class=\"fa-solid fa-diagram-project tab-empty-icon\"></i>\n <p>No entity associated with this label.</p>\n </div>\n }\n </div>\n }\n <!-- Dependency tree node template -->\n <ng-template #depNode let-node>\n <div class=\"dep-node\" [style.padding-left.px]=\"node.Depth * 24\">\n <div class=\"dep-node-header\" (click)=\"ToggleDependencyNode(node)\">\n @if (node.Children.length > 0) {\n <i class=\"fa-solid fa-chevron-right dep-chevron\"\n [class.expanded]=\"node.IsExpanded\"\n ></i>\n }\n @if (node.Children.length === 0) {\n <span class=\"dep-node-spacer\"></span>\n }\n <i [class]=\"resolveEntityIconByName(node.EntityName)\" class=\"dep-node-icon\"></i>\n <span class=\"dep-node-name\">{{node.EntityName}}</span>\n <span class=\"dep-node-field\">via {{node.RelationshipField}}</span>\n @if (node.Children.length > 0) {\n <span class=\"dep-node-count\">\n {{node.Children.length}} dep{{node.Children.length !== 1 ? 's' : ''}}\n </span>\n }\n </div>\n @if (node.IsExpanded && node.Children.length > 0) {\n @for (child of node.Children; track child) {\n <ng-container *ngTemplateOutlet=\"depNode; context: { $implicit: child }\"></ng-container>\n }\n }\n </div>\n </ng-template>\n <!-- ======================================================== -->\n <!-- CHANGES TAB -->\n <!-- ======================================================== -->\n @if (ActiveTab === 'changes') {\n <div class=\"tab-pane\">\n @if (IsLoadingDiff) {\n <mj-loading text=\"Computing differences...\"></mj-loading>\n }\n @if (!IsLoadingDiff && DiffResult) {\n <!-- Diff summary -->\n <div class=\"diff-summary\">\n <div class=\"diff-stat\">\n <span class=\"diff-stat-value change-modified\">{{DiffResult.Summary.Changed}}</span>\n <span class=\"diff-stat-label\">Changed</span>\n </div>\n <div class=\"diff-stat\">\n <span class=\"diff-stat-value\">{{DiffResult.Summary.Unchanged}}</span>\n <span class=\"diff-stat-label\">Unchanged</span>\n </div>\n <div class=\"diff-stat\">\n <span class=\"diff-stat-value\">{{DiffResult.Summary.EntitiesAffected}}</span>\n <span class=\"diff-stat-label\">Entities</span>\n </div>\n </div>\n <!-- Expand/collapse all -->\n @if (DiffResult.EntityGroups.length > 0) {\n <div class=\"diff-toolbar\">\n <button class=\"toolbar-action-btn\" (click)=\"ExpandAllDiffGroups()\" title=\"Expand all\">\n <i class=\"fa-solid fa-angles-down\"></i>\n <span>Expand All</span>\n </button>\n <button class=\"toolbar-action-btn\" (click)=\"CollapseAllDiffGroups()\" title=\"Collapse all\">\n <i class=\"fa-solid fa-angles-up\"></i>\n <span>Collapse All</span>\n </button>\n </div>\n }\n <!-- Entity diff groups -->\n @if (DiffResult.EntityGroups.length > 0) {\n <div class=\"diff-groups\">\n @for (group of DiffResult.EntityGroups; track group) {\n <div class=\"diff-group\">\n <div class=\"diff-group-header\" (click)=\"ToggleDiffGroup(group)\">\n <i class=\"fa-solid fa-chevron-right diff-chevron\" [class.expanded]=\"group.IsExpanded\"></i>\n <i [class]=\"resolveEntityIconByName(group.EntityName)\" class=\"diff-group-icon\"></i>\n <span class=\"diff-group-name\">{{group.EntityName}}</span>\n <span class=\"diff-group-count change-modified\">\n {{group.Records.length}} modified\n </span>\n </div>\n @if (group.IsExpanded) {\n <div class=\"diff-group-content\">\n @for (record of group.Records; track record) {\n <div class=\"diff-record\">\n <div class=\"diff-record-header\">\n <i [class]=\"GetChangeTypeIcon(record.ChangeType)\" [ngClass]=\"GetChangeTypeClass(record.ChangeType)\"></i>\n <span class=\"diff-record-id\">{{record.RecordID | slice:0:20}}...</span>\n </div>\n @if (record.FieldChanges.length > 0) {\n <div class=\"diff-fields\">\n @for (field of record.FieldChanges; track field) {\n <div class=\"diff-field\"\n [ngClass]=\"GetChangeTypeClass(field.ChangeType)\">\n <span class=\"diff-field-name\">{{field.FieldName}}</span>\n @if (field.OldValue) {\n <span class=\"diff-field-old\">{{field.OldValue | slice:0:40}}</span>\n }\n @if (field.OldValue && field.NewValue) {\n <i class=\"fa-solid fa-arrow-right diff-field-arrow\"></i>\n }\n @if (field.NewValue) {\n <span class=\"diff-field-new\">{{field.NewValue | slice:0:40}}</span>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n </div>\n }\n <!-- No changes -->\n @if (DiffResult.EntityGroups.length === 0 && DiffResult.Summary.Changed === 0) {\n <div class=\"tab-empty\">\n <i class=\"fa-solid fa-circle-check tab-empty-icon\" style=\"color: #10b981;\"></i>\n <p>No changes detected since this label was created.</p>\n </div>\n }\n }\n </div>\n }\n <!-- ======================================================== -->\n <!-- HISTORY TAB -->\n <!-- ======================================================== -->\n @if (ActiveTab === 'history') {\n <div class=\"tab-pane\">\n @if (IsLoadingHistory) {\n <mj-loading text=\"Loading history...\"></mj-loading>\n }\n @if (!IsLoadingHistory) {\n <!-- Restores -->\n @if (Restores.length > 0) {\n <div class=\"detail-section\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-clock-rotate-left section-icon\"></i> Restore Operations\n </h3>\n <div class=\"history-list\">\n @for (restore of Restores; track restore) {\n <div class=\"history-item\">\n <div class=\"history-item-icon\" [ngClass]=\"'status-' + (restore.Status ?? '').toLowerCase().replace(' ', '-')\">\n <i class=\"fa-solid fa-clock-rotate-left\"></i>\n </div>\n <div class=\"history-item-content\">\n <span class=\"history-item-title\">Restore {{restore.Status}}</span>\n <span class=\"history-item-meta\">\n {{restore.CompletedItems ?? 0}}/{{restore.TotalItems ?? 0}} items\n @if (restore.FailedItems) {\n <span> · {{restore.FailedItems}} failed</span>\n }\n </span>\n </div>\n <span class=\"history-item-date\">{{FormatRelativeDate(restore.__mj_CreatedAt)}}</span>\n </div>\n }\n </div>\n </div>\n }\n <!-- Related labels -->\n @if (RelatedLabels.length > 0) {\n <div class=\"detail-section\">\n <h3 class=\"section-title\">\n <i class=\"fa-solid fa-tags section-icon\"></i> Related Labels\n </h3>\n <div class=\"history-list\">\n @for (related of RelatedLabels; track related) {\n <div class=\"history-item\">\n <div class=\"history-item-icon\" style=\"background: rgba(99, 102, 241, 0.1); color: #6366f1;\">\n <i class=\"fa-solid fa-tag\"></i>\n </div>\n <div class=\"history-item-content\">\n <span class=\"history-item-title\">{{related.Name}}</span>\n <span class=\"history-item-meta\">\n <span class=\"inline-badge small\" [ngClass]=\"GetStatusClass(related.Status)\">{{related.Status}}</span>\n </span>\n </div>\n <span class=\"history-item-date\">{{FormatRelativeDate(related.__mj_CreatedAt)}}</span>\n </div>\n }\n </div>\n </div>\n }\n <!-- Empty -->\n @if (Restores.length === 0 && RelatedLabels.length === 0) {\n <div class=\"tab-empty\">\n <i class=\"fa-solid fa-clock-rotate-left tab-empty-icon\"></i>\n <p>No restore history or related labels found.</p>\n </div>\n }\n }\n </div>\n }\n </div>\n }\n\n <!-- ============================================================ -->\n <!-- INLINE MICRO VIEW (replaces tab content when active) -->\n <!-- ============================================================ -->\n @if (ShowMicroView && MicroViewRecord) {\n <div class=\"tab-content\">\n <mj-record-micro-view\n [Data]=\"MicroViewRecord\"\n [Inline]=\"true\"\n (Close)=\"OnBackFromMicroView()\"\n (EntityLinkClick)=\"OnEntityLinkClick($event)\"\n (OpenRecord)=\"OnOpenRecordClick($event)\">\n </mj-record-micro-view>\n </div>\n }\n</div>\n", styles: ["/* ================================================================= */\n/* BACKDROP */\n/* ================================================================= */\n\n.panel-backdrop {\n position: fixed;\n inset: 0;\n background: rgba(0, 0, 0, 0);\n z-index: 1000;\n transition: background 0.3s ease;\n pointer-events: none;\n}\n\n.panel-backdrop.visible {\n background: rgba(0, 0, 0, 0.3);\n pointer-events: auto;\n}\n\n/* ================================================================= */\n/* PANEL */\n/* ================================================================= */\n\n.detail-panel {\n position: fixed;\n top: 0;\n right: 0;\n height: 100vh;\n background: var(--card-background, #ffffff);\n box-shadow: -8px 0 32px rgba(0, 0, 0, 0.12);\n z-index: 1001;\n display: flex;\n flex-direction: column;\n transform: translateX(100%);\n transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);\n /* Width is set dynamically via [style.width.px] */\n min-width: 400px;\n max-width: 92vw;\n}\n\n.detail-panel.visible {\n transform: translateX(0);\n}\n\n/* ================================================================= */\n/* RESIZE HANDLE */\n/* ================================================================= */\n\n.resize-handle {\n position: absolute;\n left: -4px;\n top: 0;\n width: 8px;\n height: 100%;\n cursor: col-resize;\n z-index: 10;\n display: flex;\n align-items: center;\n justify-content: center;\n}\n\n.resize-handle:hover .resize-handle-grip,\n.resize-handle:active .resize-handle-grip {\n opacity: 1;\n background: #6366f1;\n}\n\n.resize-handle-grip {\n width: 3px;\n height: 40px;\n background: var(--border-color, #d1d5db);\n border-radius: 3px;\n opacity: 0;\n transition: opacity 0.2s ease, background 0.2s ease;\n}\n\n.resize-handle:hover {\n background: rgba(99, 102, 241, 0.04);\n}\n\n/* ================================================================= */\n/* BREADCRUMB BAR */\n/* ================================================================= */\n\n.breadcrumb-bar {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 24px;\n border-bottom: 1px solid var(--border-color, #e5e7eb);\n flex-shrink: 0;\n background: var(--hover-background, #f9fafb);\n}\n\n.breadcrumb-back {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n background: var(--card-background, #ffffff);\n border: 1px solid var(--border-color, #e5e7eb);\n border-radius: 8px;\n color: var(--text-secondary, #6b7280);\n cursor: pointer;\n transition: all 0.15s ease;\n font-size: 13px;\n flex-shrink: 0;\n}\n\n.breadcrumb-back:hover {\n background: var(--card-background, #ffffff);\n color: #6366f1;\n border-color: #6366f1;\n}\n\n.breadcrumb-trail {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 13px;\n overflow: hidden;\n}\n\n.breadcrumb-item {\n display: flex;\n align-items: center;\n gap: 5px;\n color: var(--text-secondary, #6b7280);\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n}\n\n.breadcrumb-item.clickable {\n cursor: pointer;\n transition: color 0.15s ease;\n}\n\n.breadcrumb-item.clickable:hover {\n color: #6366f1;\n}\n\n.breadcrumb-item.current {\n font-weight: 600;\n color: var(--text-primary, #1f2937);\n}\n\n.breadcrumb-separator {\n font-size: 9px;\n color: var(--text-tertiary, #d1d5db);\n flex-shrink: 0;\n}\n\n.breadcrumb-icon {\n font-size: 12px;\n flex-shrink: 0;\n}\n\n/* ================================================================= */\n/* HEADER */\n/* ================================================================= */\n\n.panel-header {\n display: flex;\n align-items: flex-start;\n justify-content: space-between;\n padding: 20px 24px 16px;\n border-bottom: 1px solid var(--border-color, #e5e7eb);\n flex-shrink: 0;\n}\n\n.header-info {\n flex: 1;\n min-width: 0;\n}\n\n.header-title-row {\n display: flex;\n align-items: center;\n gap: 10px;\n flex-wrap: wrap;\n}\n\n.header-scope-icon {\n color: #6366f1;\n font-size: 18px;\n flex-shrink: 0;\n}\n\n.header-title {\n font-size: 20px;\n font-weight: 700;\n color: var(--text-primary, #1f2937);\n margin: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.header-status {\n display: inline-flex;\n align-items: center;\n padding: 3px 10px;\n border-radius: 12px;\n font-size: 12px;\n font-weight: 600;\n flex-shrink: 0;\n}\n\n.header-scope-badge {\n display: inline-flex;\n align-items: center;\n padding: 3px 10px;\n background: rgba(99, 102, 241, 0.08);\n color: #6366f1;\n border-radius: 12px;\n font-size: 12px;\n font-weight: 600;\n flex-shrink: 0;\n}\n\n.header-description {\n margin: 8px 0 0 0;\n font-size: 14px;\n color: var(--text-secondary, #6b7280);\n line-height: 1.5;\n}\n\n.header-actions {\n display: flex;\n gap: 8px;\n align-items: center;\n flex-shrink: 0;\n margin-left: 16px;\n}\n\n.header-action-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n background: var(--card-background, #ffffff);\n border: 1px solid var(--border-color, #e5e7eb);\n border-radius: 8px;\n color: var(--text-secondary, #6b7280);\n cursor: pointer;\n transition: all 0.15s ease;\n font-size: 14px;\n}\n\n.header-action-btn:hover {\n background: var(--hover-background, #f3f4f6);\n color: var(--text-primary, #1f2937);\n border-color: var(--border-hover, #d1d5db);\n}\n\n.header-action-btn:disabled {\n opacity: 0.4;\n cursor: not-allowed;\n}\n\n.close-btn {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n background: none;\n border: none;\n border-radius: 8px;\n color: var(--text-secondary, #6b7280);\n cursor: pointer;\n transition: all 0.15s ease;\n font-size: 16px;\n}\n\n.close-btn:hover {\n background: var(--hover-background, #f3f4f6);\n color: var(--text-primary, #1f2937);\n}\n\n/* Status badges (reused) */\n.status-active { background: rgba(16, 185, 129, 0.1); color: #059669; }\n.status-archived { background: rgba(107, 114, 128, 0.1); color: #6b7280; }\n.status-restored { background: rgba(245, 158, 11, 0.1); color: #d97706; }\n\n/* ================================================================= */\n/* SUMMARY KPIs */\n/* ================================================================= */\n\n.summary-kpis {\n display: flex;\n gap: 12px;\n padding: 16px 24px;\n border-bottom: 1px solid var(--border-color, #e5e7eb);\n flex-shrink: 0;\n overflow-x: auto;\n}\n\n.summary-kpi {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px;\n background: var(--hover-background, #f9fafb);\n border-radius: 10px;\n flex-shrink: 0;\n}\n\n.kpi-mini-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 36px;\n height: 36px;\n border-radius: 8px;\n font-size: 15px;\n flex-shrink: 0;\n}\n\n.kpi-mini-content {\n display: flex;\n flex-direction: column;\n}\n\n.kpi-mini-value {\n font-size: 18px;\n font-weight: 700;\n color: var(--text-primary, #1f2937);\n line-height: 1;\n}\n\n.kpi-mini-label {\n font-size: 11px;\n color: var(--text-tertiary, #9ca3af);\n margin-top: 2px;\n}\n\n/* ================================================================= */\n/* TAB BAR */\n/* ================================================================= */\n\n.tab-bar {\n display: flex;\n gap: 0;\n padding: 0 24px;\n border-bottom: 1px solid var(--border-color, #e5e7eb);\n flex-shrink: 0;\n overflow-x: auto;\n}\n\n.tab {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 12px 16px;\n background: none;\n border: none;\n border-bottom: 2px solid transparent;\n font-size: 13px;\n font-weight: 500;\n color: var(--text-secondary, #6b7280);\n cursor: pointer;\n transition: all 0.15s ease;\n white-space: nowrap;\n}\n\n.tab:hover {\n color: var(--text-primary, #1f2937);\n background: var(--hover-background, #f9fafb);\n}\n\n.tab.active {\n color: #6366f1;\n border-bottom-color: #6366f1;\n font-weight: 600;\n}\n\n.tab-icon {\n font-size: 13px;\n}\n\n.tab-count {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 20px;\n height: 20px;\n padding: 0 6px;\n background: rgba(99, 102, 241, 0.1);\n color: #6366f1;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n}\n\n/* ================================================================= */\n/* TAB CONTENT */\n/* ================================================================= */\n\n.tab-content {\n flex: 1;\n overflow-y: auto;\n overflow-x: hidden;\n}\n\n.tab-pane {\n padding: 20px 24px;\n animation: tabFadeIn 0.2s ease;\n}\n\n@keyframes tabFadeIn {\n from { opacity: 0; transform: translateY(4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n.tab-empty {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n padding: 60px 20px;\n text-align: center;\n}\n\n.tab-empty-icon {\n font-size: 40px;\n color: var(--text-tertiary, #d1d5db);\n margin-bottom: 12px;\n}\n\n.tab-empty p {\n font-size: 14px;\n color: var(--text-tertiary, #9ca3af);\n margin: 0;\n}\n\n/* ================================================================= */\n/* OVERVIEW TAB */\n/* ================================================================= */\n\n.detail-section {\n margin-bottom: 24px;\n}\n\n.section-title {\n display: flex;\n align-items: center;\n gap: 8px;\n font-size: 14px;\n font-weight: 600;\n color: var(--text-primary, #1f2937);\n margin: 0 0 14px 0;\n padding-bottom: 10px;\n border-bottom: 1px solid var(--border-color, #f3f4f6);\n}\n\n.section-icon {\n color: var(--text-tertiary, #9ca3af);\n font-size: 14px;\n}\n\n.detail-grid {\n display: grid;\n grid-template-columns: 1fr 1fr;\n gap: 14px;\n}\n\n.detail-field {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.field-label {\n font-size: 12px;\n font-weight: 600;\n color: var(--text-tertiary, #9ca3af);\n text-transform: uppercase;\n letter-spacing: 0.04em;\n}\n\n.field-value {\n font-size: 14px;\n color: var(--text-primary, #1f2937);\n display: flex;\n align-items: center;\n}\n\n.field-mono {\n font-family: 'SF Mono', 'Fira Code', monospace;\n font-size: 13px;\n word-break: break-all;\n}\n\n.field-record-name {\n font-weight: 600;\n color: #6366f1;\n}\n\n/* Record card for Record-scoped labels on overview tab */\n.record-card {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n padding: 16px 18px;\n background: rgba(99, 102, 241, 0.04);\n border: 1px solid rgba(99, 102, 241, 0.15);\n border-radius: 12px;\n margin-bottom: 24px;\n}\n\n.record-card-main {\n display: flex;\n align-items: center;\n gap: 14px;\n min-width: 0;\n flex: 1;\n}\n\n.record-card-icon {\n font-size: 22px;\n color: #6366f1;\n flex-shrink: 0;\n}\n\n.record-card-info {\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.record-card-name {\n font-size: 16px;\n font-weight: 700;\n color: var(--text-primary, #1f2937);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.record-card-name-loading {\n color: var(--text-tertiary, #9ca3af);\n font-weight: 500;\n font-style: italic;\n}\n\n.record-card-entity {\n font-size: 13px;\n color: var(--text-secondary, #6b7280);\n}\n\n.record-card-id {\n font-size: 12px;\n color: var(--text-tertiary, #9ca3af);\n font-family: 'SF Mono', 'Fira Code', monospace;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.record-card-open-btn {\n display: flex;\n align-items: center;\n gap: 6px;\n padding: 8px 16px;\n background: #6366f1;\n color: #ffffff;\n border: none;\n border-radius: 8px;\n font-size: 13px;\n font-weight: 600;\n cursor: pointer;\n transition: all 0.15s ease;\n flex-shrink: 0;\n white-space: nowrap;\n}\n\n.record-card-open-btn:hover {\n background: #4f46e5;\n box-shadow: 0 2px 8px rgba(99, 102, 241, 0.3);\n}\n\n.inline-badge {\n display: inline-flex;\n align-items: center;\n padding: 3px 10px;\n border-radius: 12px;\n font-size: 12px;\n font-weight: 600;\n}\n\n.inline-badge.small {\n padding: 2px 8px;\n font-size: 11px;\n}\n\n.description-text {\n font-size: 14px;\n color: var(--text-secondary, #6b7280);\n line-height: 1.6;\n margin: 0;\n}\n\n/* Hierarchy */\n.hierarchy-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 8px 12px;\n border-radius: 8px;\n font-size: 14px;\n transition: background 0.15s ease;\n}\n\n.hierarchy-item:hover {\n background: var(--hover-background, #f3f4f6);\n}\n\n.hierarchy-icon {\n color: var(--text-tertiary, #9ca3af);\n font-size: 13px;\n width: 16px;\n text-align: center;\n}\n\n.hierarchy-label {\n font-size: 12px;\n font-weight: 600;\n color: var(--text-tertiary, #9ca3af);\n text-transform: uppercase;\n}\n\n.hierarchy-name {\n font-weight: 500;\n color: var(--text-primary, #1f2937);\n}\n\n.hierarchy-meta {\n font-size: 12px;\n color: var(--text-tertiary, #9ca3af);\n font-family: monospace;\n}\n\n.hierarchy-children {\n margin-left: 16px;\n border-left: 2px solid var(--border-color, #e5e7eb);\n padding-left: 8px;\n}\n\n/* ================================================================= */\n/* SNAPSHOTS TAB */\n/* ================================================================= */\n\n.snapshot-toolbar {\n display: flex;\n align-items: center;\n gap: 12px;\n margin-bottom: 16px;\n}\n\n.snapshot-search {\n position: relative;\n flex: 1;\n}\n\n.snapshot-search-icon {\n position: absolute;\n left: 12px;\n top: 50%;\n transform: translateY(-50%);\n color: var(--text-tertiary, #9ca3af);\n font-size: 13px;\n}\n\n.snapshot-search-input {\n width: 100%;\n padding: 8px 12px 8px 36px;\n background: var(--card-background, #ffffff);\n border: 1px solid var(--border-color, #e5e7eb);\n border-radius: 8px;\n font-size: 13px;\n color: var(--text-primary, #1f2937);\n outline: none;\n box-sizing: border-box;\n transition: border-color 0.15s ease;\n}\n\n.snapshot-search-input:focus {\n border-color: #6366f1;\n}\n\n.snapshot-sort-toggle,\n.snapshot-view-toggle {\n display: flex;\n background: var(--card-background, #ffffff);\n border: 1px solid var(--border-color, #e5e7eb);\n border-radius: 6px;\n overflow: hidden;\n}\n\n.toggle-btn-sm {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 30px;\n height: 30px;\n background: transparent;\n border: none;\n cursor: pointer;\n color: var(--text-tertiary, #9ca3af);\n transition: all 0.15s ease;\n font-size: 12px;\n}\n\n.toggle-btn-sm:hover {\n color: var(--text-primary, #1f2937);\n}\n\n.toggle-btn-sm.active {\n color: #6366f1;\n background: rgba(99, 102, 241, 0.08);\n}\n\n.toggle-btn-sm + .toggle-btn-sm {\n border-left: 1px solid var(--border-color, #e5e7eb);\n}\n\n/* Snapshot groups */\n.snapshot-groups {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.snapshot-group {\n background: var(--card-background, #ffffff);\n border: 1px solid var(--border-color, #e5e7eb);\n border-radius: 10px;\n overflow: hidden;\n}\n\n.group-header {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 14px;\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.group-header:hover {\n background: var(--hover-background, #f9fafb);\n}\n\n.group-chevron {\n font-size: 11px;\n color: var(--text-tertiary, #9ca3af);\n transition: transform 0.2s ease;\n width: 14px;\n text-align: center;\n}\n\n.group-chevron.expanded {\n transform: rotate(90deg);\n}\n\n.group-entity-icon {\n color: #3b82f6;\n font-size: 14px;\n}\n\n.group-name {\n font-size: 14px;\n font-weight: 600;\n color: var(--text-primary, #1f2937);\n flex: 1;\n}\n\n.group-count-inline {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n min-width: 20px;\n height: 20px;\n padding: 0 6px;\n margin-left: 6px;\n background: rgba(59, 130, 246, 0.1);\n color: #3b82f6;\n border-radius: 10px;\n font-size: 12px;\n font-weight: 600;\n vertical-align: middle;\n}\n\n.group-content {\n border-top: 1px solid var(--border-color, #f3f4f6);\n animation: tabFadeIn 0.15s ease;\n}\n\n.group-names-loading {\n display: flex;\n justify-content: center;\n padding: 8px 14px;\n}\n\n/* Snapshot list items */\n.snapshot-list-item {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 10px 14px 10px 40px;\n cursor: pointer;\n transition: background 0.15s ease;\n border-bottom: 1px solid var(--border-color, #f3f4f6);\n}\n\n.snapshot-list-item:last-child {\n border-bottom: none;\n}\n\n.snapshot-list-item:hover {\n background: rgba(99, 102, 241, 0.04);\n}\n\n.snapshot-item-icon {\n color: var(--text-tertiary, #9ca3af);\n font-size: 13px;\n}\n\n.snapshot-item-name {\n flex: 1;\n font-size: 13px;\n font-weight: 500;\n color: var(--text-primary, #1f2937);\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.snapshot-item-id {\n font-size: 12px;\n color: var(--text-tertiary, #9ca3af);\n font-family: monospace;\n}\n\n.snapshot-item-arrow {\n color: var(--text-tertiary, #d1d5db);\n font-size: 11px;\n transition: transform 0.15s ease, color 0.15s ease;\n}\n\n.snapshot-list-item:hover .snapshot-item-arrow {\n color: #6366f1;\n transform: translateX(2px);\n}\n\n/* Snapshot cards */\n.snapshot-cards {\n display: grid;\n grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));\n gap: 10px;\n padding: 12px 14px;\n}\n\n.snapshot-card {\n padding: 12px;\n background: var(--hover-background, #f9fafb);\n border: 1px solid var(--border-color, #e5e7eb);\n border-radius: 8px;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.snapshot-card:hover {\n border-color: #6366f1;\n box-shadow: 0 2px 8px rgba(99, 102, 241, 0.1);\n}\n\n.snapshot-card-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 6px;\n font-size: 13px;\n color: var(--text-primary, #1f2937);\n}\n\n.snapshot-card-header i {\n color: var(--text-tertiary, #9ca3af);\n font-size: 12px;\n}\n\n.snapshot-card-name {\n font-weight: 500;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.snapshot-card-meta {\n font-size: 11px;\n color: var(--text-tertiary, #9ca3af);\n font-family: monospace;\n}\n\n/* ================================================================= */\n/* DEPENDENCIES TAB */\n/* ================================================================= */\n\n.dep-root {\n padding: 4px 0;\n}\n\n.dep-root-entity {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 14px;\n background: rgba(99, 102, 241, 0.06);\n border: 1px solid rgba(99, 102, 241, 0.15);\n border-radius: 10px;\n margin-bottom: 12px;\n}\n\n.dep-root-icon {\n color: #6366f1;\n font-size: 16px;\n}\n\n.dep-root-name {\n font-size: 15px;\n font-weight: 600;\n color: var(--text-primary, #1f2937);\n flex: 1;\n}\n\n.dep-root-badge {\n padding: 2px 10px;\n background: #6366f1;\n color: #ffffff;\n border-radius: 10px;\n font-size: 11px;\n font-weight: 600;\n}\n\n.dep-node {\n margin-left: 8px;\n border-left: 2px solid var(--border-color, #e5e7eb);\n}\n\n.dep-node-header {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 8px 12px;\n cursor: pointer;\n transition: background 0.15s ease;\n border-radius: 6px;\n}\n\n.dep-node-header:hover {\n background: var(--hover-background, #f3f4f6);\n}\n\n.dep-chevron {\n font-size: 10px;\n color: var(--text-tertiary, #9ca3af);\n transition: transform 0.2s ease;\n width: 12px;\n text-align: center;\n}\n\n.dep-chevron.expanded {\n transform: rotate(90deg);\n}\n\n.dep-node-spacer {\n width: 12px;\n}\n\n.dep-node-icon {\n color: #3b82f6;\n font-size: 13px;\n}\n\n.dep-node-name {\n font-size: 13px;\n font-weight: 500;\n color: var(--text-primary, #1f2937);\n}\n\n.dep-node-field {\n font-size: 11px;\n color: var(--text-tertiary, #9ca3af);\n font-style: italic;\n}\n\n.dep-node-count {\n font-size: 11px;\n color: var(--text-tertiary, #9ca3af);\n padding: 1px 8px;\n background: var(--hover-background, #f3f4f6);\n border-radius: 8px;\n}\n\n.dep-empty {\n padding: 20px;\n text-align: center;\n}\n\n.dep-empty p {\n font-size: 14px;\n color: var(--text-tertiary, #9ca3af);\n margin: 0;\n}\n\n/* ================================================================= */\n/* CHANGES TAB */\n/* ================================================================= */\n\n.diff-summary {\n display: flex;\n gap: 16px;\n margin-bottom: 20px;\n}\n\n.diff-stat {\n display: flex;\n flex-direction: column;\n align-items: center;\n padding: 12px 20px;\n background: var(--hover-background, #f9fafb);\n border-radius: 10px;\n min-width: 80px;\n}\n\n.diff-stat-value {\n font-size: 24px;\n font-weight: 700;\n color: var(--text-primary, #1f2937);\n line-height: 1;\n}\n\n.diff-stat-label {\n font-size: 12px;\n color: var(--text-tertiary, #9ca3af);\n margin-top: 4px;\n}\n\n/* Diff toolbar */\n.diff-toolbar {\n display: flex;\n gap: 8px;\n margin-bottom: 12px;\n}\n\n.toolbar-action-btn {\n display: inline-flex;\n align-items: center;\n gap: 6px;\n padding: 6px 12px;\n border: 1px solid var(--border-color, #e5e7eb);\n border-radius: 6px;\n background: var(--card-background, #ffffff);\n color: var(--text-secondary, #6b7280);\n font-size: 12px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.15s ease;\n}\n\n.toolbar-action-btn i {\n font-size: 11px;\n}\n\n.toolbar-action-btn:hover {\n border-color: #6366f1;\n color: #6366f1;\n background: rgba(99, 102, 241, 0.06);\n}\n\n.diff-groups {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.diff-group {\n background: var(--card-background, #ffffff);\n border: 1px solid var(--border-color, #e5e7eb);\n border-radius: 10px;\n overflow: hidden;\n}\n\n.diff-group-header {\n display: flex;\n align-items: center;\n gap: 10px;\n padding: 12px 14px;\n cursor: pointer;\n transition: background 0.15s ease;\n}\n\n.diff-group-header:hover {\n background: var(--hover-background, #f9fafb);\n}\n\n.diff-chevron {\n font-size: 11px;\n color: var(--text-tertiary, #9ca3af);\n transition: transform 0.2s ease;\n width: 14px;\n text-align: center;\n}\n\n.diff-chevron.expanded {\n transform: rotate(90deg);\n}\n\n.diff-group-icon {\n color: #3b82f6;\n font-size: 14px;\n}\n\n.diff-group-name {\n font-size: 14px;\n font-weight: 600;\n color: var(--text-primary, #1f2937);\n flex: 1;\n}\n\n.diff-group-count {\n font-size: 12px;\n font-weight: 600;\n padding: 2px 10px;\n border-radius: 10px;\n}\n\n.diff-group-content {\n border-top: 1px solid var(--border-color, #f3f4f6);\n animation: tabFadeIn 0.15s ease;\n}\n\n.diff-record {\n padding: 10px 14px 10px 40px;\n border-bottom: 1px solid var(--border-color, #f3f4f6);\n}\n\n.diff-record:last-child {\n border-bottom: none;\n}\n\n.diff-record-header {\n display: flex;\n align-items: center;\n gap: 8px;\n margin-bottom: 8px;\n}\n\n.diff-record-id {\n font-size: 13px;\n font-weight: 500;\n color: var(--text-primary, #1f2937);\n font-family: monospace;\n}\n\n.diff-fields {\n display: flex;\n flex-direction: column;\n gap: 4px;\n margin-left: 22px;\n}\n\n.diff-field {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 8px;\n border-radius: 4px;\n font-size: 12px;\n}\n\n.diff-field-name {\n font-weight: 600;\n color: var(--text-primary, #1f2937);\n min-width: 100px;\n}\n\n.diff-field-old {\n color: #ef4444;\n text-decoration: line-through;\n opacity: 0.7;\n font-family: monospace;\n font-size: 12px;\n}\n\n.diff-field-arrow {\n color: var(--text-tertiary, #9ca3af);\n font-size: 10px;\n}\n\n.diff-field-new {\n color: #10b981;\n font-family: monospace;\n font-size: 12px;\n}\n\n/* Change type colors */\n.change-added { color: #10b981; }\n.change-modified { color: #f59e0b; }\n.change-removed { color: #ef4444; }\n\n.diff-field.change-added { background: rgba(16, 185, 129, 0.06); }\n.diff-field.change-modified { background: rgba(245, 158, 11, 0.06); }\n.diff-field.change-removed { background: rgba(239, 68, 68, 0.06); }\n\n/* ================================================================= */\n/* HISTORY TAB */\n/* ================================================================= */\n\n.history-list {\n display: flex;\n flex-direction: column;\n gap: 4px;\n}\n\n.history-item {\n display: flex;\n align-items: center;\n gap: 12px;\n padding: 10px 12px;\n border-radius: 8px;\n transition: background 0.15s ease;\n}\n\n.history-item:hover {\n background: var(--hover-background, #f3f4f6);\n}\n\n.history-item-icon {\n display: flex;\n align-items: center;\n justify-content: center;\n width: 32px;\n height: 32px;\n border-radius: 8px;\n font-size: 14px;\n flex-shrink: 0;\n}\n\n.history-item-icon.status-complete { background: rgba(16, 185, 129, 0.1); color: #10b981; }\n.history-item-icon.status-error { background: rgba(239, 68, 68, 0.1); color: #ef4444; }\n.history-item-icon.status-partial { background: rgba(245, 158, 11, 0.1); color: #f59e0b; }\n.history-item-icon.status-in-progress { background: rgba(59, 130, 246, 0.1); color: #3b82f6; }\n.history-item-icon.status-pending { background: rgba(107, 114, 128, 0.1); color: #6b7280; }\n\n.history-item-content {\n flex: 1;\n display: flex;\n flex-direction: column;\n gap: 2px;\n min-width: 0;\n}\n\n.history-item-title {\n font-size: 14px;\n font-weight: 500;\n color: var(--text-primary, #1f2937);\n}\n\n.history-item-meta {\n font-size: 12px;\n color: var(--text-tertiary, #9ca3af);\n display: flex;\n align-items: center;\n gap: 6px;\n}\n\n.history-item-date {\n font-size: 12px;\n color: var(--text-tertiary, #9ca3af);\n flex-shrink: 0;\n}\n\n/* ================================================================= */\n/* RESPONSIVE */\n/* ================================================================= */\n\n@media (max-width: 1024px) {\n .detail-panel {\n min-width: 360px;\n }\n\n .summary-kpis {\n flex-wrap: wrap;\n }\n}\n\n@media (max-width: 768px) {\n .detail-panel {\n width: 100% !important;\n min-width: unset;\n }\n\n .resize-handle {\n display: none;\n }\n\n .detail-grid {\n grid-template-columns: 1fr;\n }\n\n .summary-kpis {\n flex-wrap: wrap;\n gap: 8px;\n padding: 12px 16px;\n }\n\n .summary-kpi {\n padding: 8px 10px;\n }\n\n .panel-header {\n padding: 16px 16px 12px;\n }\n\n .tab-bar {\n padding: 0 16px;\n }\n\n .tab-pane {\n padding: 16px;\n }\n\n .breadcrumb-bar {\n padding: 8px 16px;\n }\n\n .snapshot-toolbar {\n flex-direction: column;\n align-items: stretch;\n }\n\n .diff-summary {\n flex-wrap: wrap;\n gap: 8px;\n }\n\n .diff-stat {\n padding: 10px 14px;\n min-width: 60px;\n }\n}\n\n@media (max-width: 480px) {\n .header-title {\n font-size: 16px;\n }\n\n .header-title-row {\n gap: 6px;\n }\n\n .header-actions {\n margin-left: 8px;\n }\n\n .tab {\n padding: 10px 10px;\n font-size: 12px;\n }\n\n .snapshot-cards {\n grid-template-columns: 1fr;\n }\n}\n"] }]
|
|
2072
2077
|
}], () => [{ type: i0.ChangeDetectorRef }, { type: i0.NgZone }, { type: i0.ElementRef }], { Label: [{
|
|
2073
2078
|
type: Input
|
|
2074
2079
|
}], AllLabels: [{
|
|
@@ -2085,5 +2090,5 @@ export class MjLabelDetailComponent {
|
|
|
2085
2090
|
type: HostListener,
|
|
2086
2091
|
args: ['document:keydown.escape']
|
|
2087
2092
|
}] }); })();
|
|
2088
|
-
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MjLabelDetailComponent, { className: "MjLabelDetailComponent", filePath: "src/lib/label-detail/label-detail.component.ts", lineNumber:
|
|
2093
|
+
(() => { (typeof ngDevMode === "undefined" || ngDevMode) && i0.ɵsetClassDebugInfo(MjLabelDetailComponent, { className: "MjLabelDetailComponent", filePath: "src/lib/label-detail/label-detail.component.ts", lineNumber: 78 }); })();
|
|
2089
2094
|
//# sourceMappingURL=label-detail.component.js.map
|