tin-spa 20.11.3 → 20.11.6

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.
@@ -4809,11 +4809,11 @@ class ImportDialogComponent {
4809
4809
  this.stepper.selectedIndex = index; }, 0);
4810
4810
  }
4811
4811
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ImportDialogComponent, deps: [{ token: HttpService }, { token: i1$1.HttpClient }, { token: MessageService }, { token: i4.MatDialogRef }, { token: MAT_DIALOG_DATA }], target: i0.ɵɵFactoryTarget.Component }); }
4812
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ImportDialogComponent, isStandalone: false, selector: "spa-import-dialog", viewQueries: [{ propertyName: "stepper", first: true, predicate: ["stepper"], descendants: true }], ngImport: i0, template: "<div class=\"import-dialog\">\n\n <div class=\"import-header\">\n <h2>{{ title }}</h2>\n <button mat-icon-button (click)=\"close()\" aria-label=\"Close\"><mat-icon>close</mat-icon></button>\n </div>\n\n <mat-progress-bar *ngIf=\"loading\" mode=\"indeterminate\"></mat-progress-bar>\n\n <mat-horizontal-stepper #stepper [linear]=\"false\" class=\"import-stepper\">\n\n <!-- Step 1: Upload -->\n <mat-step label=\"Upload\">\n <div class=\"step-body\">\n <p class=\"hint\">Upload an Excel (.xlsx) file. Not sure of the format? Download a template with the expected columns.</p>\n\n <div class=\"upload-row\">\n <input type=\"file\" accept=\".xlsx\" (change)=\"onFileSelected($event)\" />\n <button mat-stroked-button color=\"primary\" (click)=\"downloadTemplate()\">\n <mat-icon>download</mat-icon> Download template\n </button>\n </div>\n\n <div class=\"step-actions\">\n <button mat-flat-button color=\"primary\" [disabled]=\"!selectedFile || loading\" (click)=\"doUpload()\">Upload</button>\n </div>\n </div>\n </mat-step>\n\n <!-- Step 2: Map columns -->\n <mat-step label=\"Map columns\">\n <div class=\"step-body\">\n <div *ngIf=\"unmappedRequired.length\" class=\"alert alert-warn\">\n Required fields not yet mapped: <strong>{{ unmappedRequired.join(', ') }}</strong>\n </div>\n\n <table class=\"map-table\">\n <thead>\n <tr><th>Spreadsheet column</th><th>Sample values</th><th>Maps to field</th><th>Match</th></tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let m of mappings\">\n <td class=\"col-header\">{{ m.header }}</td>\n <td class=\"col-samples\">\n <span *ngFor=\"let s of samplesFor(m.header)\" class=\"sample\">{{ s }}</span>\n </td>\n <td>\n <mat-select [(ngModel)]=\"m.property\" (selectionChange)=\"onMappingChange(m)\" placeholder=\"\u2014 Ignore \u2014\">\n <mat-option [value]=\"null\">\u2014 Ignore \u2014</mat-option>\n <mat-option *ngFor=\"let f of availableFields(m.header)\" [value]=\"f.property\">\n {{ f.display }}<span *ngIf=\"f.required\"> *</span>\n </mat-option>\n </mat-select>\n </td>\n <td>\n <span class=\"chip\" [ngClass]=\"confidenceClass(m)\" [matTooltip]=\"m.reasoning || ''\">\n {{ m.property ? (m.method === 'AI' ? ((m.confidence * 100) | number:'1.0-0') + '%' : m.method) : 'Unmapped' }}\n </span>\n </td>\n </tr>\n </tbody>\n </table>\n\n <div class=\"step-actions\">\n <button mat-button (click)=\"startOver()\">Start over</button>\n <button mat-flat-button color=\"primary\" [disabled]=\"unmappedRequired.length > 0 || loading\" (click)=\"confirmMapping()\">Next</button>\n </div>\n </div>\n </mat-step>\n\n <!-- Step 3: Review -->\n <mat-step label=\"Review\">\n <div class=\"step-body\">\n <div class=\"tiles\">\n <div class=\"tile\"><span class=\"tile-num\">{{ counts.total }}</span><span class=\"tile-label\">Total</span></div>\n <div class=\"tile tile-ready\"><span class=\"tile-num\">{{ counts.ready }}</span><span class=\"tile-label\">Ready</span></div>\n <div class=\"tile tile-warn\"><span class=\"tile-num\">{{ counts.warning }}</span><span class=\"tile-label\">Warnings</span></div>\n <div class=\"tile tile-error\"><span class=\"tile-num\">{{ counts.error }}</span><span class=\"tile-label\">Errors</span></div>\n </div>\n\n <mat-checkbox [(ngModel)]=\"onlyProblems\">Only show rows with problems</mat-checkbox>\n\n <div class=\"grid-scroll\">\n <table mat-table [dataSource]=\"visibleRows\" class=\"review-table\">\n\n <ng-container matColumnDef=\"rowNumber\">\n <th mat-header-cell *matHeaderCellDef>#</th>\n <td mat-cell *matCellDef=\"let row\">{{ row.rowNumber }}</td>\n </ng-container>\n\n <ng-container *ngFor=\"let col of reviewColumns\" [matColumnDef]=\"col.property\">\n <th mat-header-cell *matHeaderCellDef>{{ col.display }}</th>\n <td mat-cell *matCellDef=\"let row\"\n [class.cell-error]=\"cellSeverity(row, col.property) === 'error'\"\n [class.cell-warn]=\"cellSeverity(row, col.property) === 'warning'\">\n <ng-container *ngIf=\"editingRowId === row.importRowID; else showVal\">\n <input class=\"cell-input\" [(ngModel)]=\"editModel[col.property]\" />\n </ng-container>\n <ng-template #showVal>{{ cellValue(row, col.property) }}</ng-template>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"status\">\n <th mat-header-cell *matHeaderCellDef>Status</th>\n <td mat-cell *matCellDef=\"let row\">\n <span class=\"status-chip\" [ngClass]=\"{\n 'chip-green': row.status === 1,\n 'chip-orange': row.status === 2,\n 'chip-red': row.status === 3\n }\" [matTooltip]=\"rowIssueText(row)\">{{ row.statusName }}</span>\n <button *ngIf=\"editingRowId !== row.importRowID\" mat-button color=\"primary\" (click)=\"startEdit(row)\">Edit</button>\n <ng-container *ngIf=\"editingRowId === row.importRowID\">\n <button mat-button color=\"primary\" (click)=\"saveEdit(row)\">Save</button>\n <button mat-button (click)=\"cancelEdit()\">Cancel</button>\n </ng-container>\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n </div>\n\n <div class=\"step-actions\">\n <button mat-button (click)=\"startOver()\">Start over</button>\n <button mat-flat-button color=\"primary\" [disabled]=\"counts.error > 0 || loading\" (click)=\"doCommit()\">\n Import {{ counts.total - counts.error }} record(s)\n </button>\n </div>\n </div>\n </mat-step>\n\n <!-- Step 4: Done -->\n <mat-step label=\"Done\">\n <div class=\"step-body done-body\">\n <mat-icon class=\"done-icon\">check_circle</mat-icon>\n <h3>Imported {{ committedCount }} record(s)</h3>\n <p *ngIf=\"counts.warning > 0\" class=\"hint\">{{ counts.warning }} row(s) imported with warnings.</p>\n <div class=\"step-actions\">\n <button mat-flat-button color=\"primary\" (click)=\"close()\">Close</button>\n </div>\n </div>\n </mat-step>\n\n </mat-horizontal-stepper>\n</div>\n", styles: [".import-dialog{display:flex;flex-direction:column;min-width:60vw;max-width:90vw}.import-header{display:flex;align-items:center;justify-content:space-between;padding:4px 8px}.import-header h2{margin:0;font-size:18px}.import-stepper{background:transparent}.step-body{padding:12px 8px;display:flex;flex-direction:column;gap:14px}.hint{color:#0009;font-size:13px;margin:0}.upload-row{display:flex;align-items:center;gap:16px;flex-wrap:wrap}.step-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:8px}.alert{padding:8px 12px;border-radius:4px;font-size:13px}.alert-warn{background:#fff3e0;color:#8a5300;border:1px solid #ffcc80}.map-table{width:100%;border-collapse:collapse}.map-table th,.map-table td{text-align:left;padding:6px 8px;border-bottom:1px solid #eee;vertical-align:middle}.map-table th{font-size:12px;color:#0009;font-weight:600}.col-header{font-weight:600}.col-samples .sample{display:inline-block;background:#f2f2f2;border-radius:3px;padding:1px 6px;margin:1px 3px 1px 0;font-size:12px;color:#555}.chip{display:inline-block;padding:2px 8px;border-radius:10px;font-size:12px;color:#fff}.chip-green{background:#2e7d32}.chip-blue{background:#1565c0}.chip-orange{background:#ef6c00}.chip-grey{background:#9e9e9e}.chip-red{background:#c62828}.tiles{display:flex;gap:12px;flex-wrap:wrap}.tile{display:flex;flex-direction:column;align-items:center;min-width:72px;padding:8px 14px;border-radius:6px;background:#f5f5f5}.tile-num{font-size:20px;font-weight:700}.tile-label{font-size:12px;color:#0009}.tile-ready{background:#e8f5e9}.tile-warn{background:#fff3e0}.tile-error{background:#ffebee}.grid-scroll{overflow-x:auto;max-height:46vh;overflow-y:auto;border:1px solid #eee;border-radius:4px}.review-table{width:100%}.cell-error{background:#ffebee;color:#b71c1c}.cell-warn{background:#fff3e0;color:#8a5300}.cell-input{width:100%;box-sizing:border-box;padding:2px 4px}.status-chip{display:inline-block;padding:2px 8px;border-radius:10px;font-size:11px;color:#fff;margin-right:6px}.done-body{align-items:center;text-align:center;padding:32px 8px}.done-icon{color:#2e7d32;font-size:56px;height:56px;width:56px}@media (prefers-color-scheme: dark){.hint,.map-table th,.tile-label{color:#ffffffb3}.col-samples .sample{background:#3a3a3a;color:#ccc}.tile{background:#333}.grid-scroll,.map-table th,.map-table td{border-color:#444}}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i7.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i7.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i7.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i7.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i7.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i7.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i7.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i7.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i7.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i7.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "component", type: i8.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i7$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i7$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i7$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: i13.MatStep, selector: "mat-step", inputs: ["color"], exportAs: ["matStep"] }, { kind: "component", type: i13.MatStepper, selector: "mat-stepper, mat-vertical-stepper, mat-horizontal-stepper, [matStepper]", inputs: ["disableRipple", "color", "labelPosition", "headerPosition", "animationDuration"], outputs: ["animationDone"], exportAs: ["matStepper", "matVerticalStepper", "matHorizontalStepper"] }, { kind: "component", type: i14.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }] }); }
4812
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ImportDialogComponent, isStandalone: false, selector: "spa-import-dialog", viewQueries: [{ propertyName: "stepper", first: true, predicate: ["stepper"], descendants: true }], ngImport: i0, template: "<div class=\"import-dialog\">\r\n\r\n <div class=\"import-header\">\r\n <h2>{{ title }}</h2>\r\n <button mat-icon-button (click)=\"close()\" aria-label=\"Close\"><mat-icon>close</mat-icon></button>\r\n </div>\r\n\r\n <mat-progress-bar *ngIf=\"loading\" mode=\"indeterminate\"></mat-progress-bar>\r\n\r\n <mat-horizontal-stepper #stepper [linear]=\"false\" class=\"import-stepper\">\r\n\r\n <!-- Step 1: Upload -->\r\n <mat-step label=\"Upload\">\r\n <div class=\"step-body\">\r\n <p class=\"hint\">Upload an Excel (.xlsx) file. Not sure of the format? Download a template with the expected columns.</p>\r\n\r\n <div class=\"upload-row\">\r\n <input type=\"file\" accept=\".xlsx\" (change)=\"onFileSelected($event)\" />\r\n <button mat-stroked-button color=\"primary\" (click)=\"downloadTemplate()\">\r\n <mat-icon>download</mat-icon> Download template\r\n </button>\r\n </div>\r\n\r\n <div class=\"step-actions\">\r\n <button mat-flat-button color=\"primary\" [disabled]=\"!selectedFile || loading\" (click)=\"doUpload()\">Upload</button>\r\n </div>\r\n </div>\r\n </mat-step>\r\n\r\n <!-- Step 2: Map columns -->\r\n <mat-step label=\"Map columns\">\r\n <div class=\"step-body\">\r\n <div *ngIf=\"unmappedRequired.length\" class=\"alert alert-warn\">\r\n Required fields not yet mapped: <strong>{{ unmappedRequired.join(', ') }}</strong>\r\n </div>\r\n\r\n <table class=\"map-table\">\r\n <thead>\r\n <tr><th>Spreadsheet column</th><th>Sample values</th><th>Maps to field</th><th>Match</th></tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let m of mappings\">\r\n <td class=\"col-header\">{{ m.header }}</td>\r\n <td class=\"col-samples\">\r\n <span *ngFor=\"let s of samplesFor(m.header)\" class=\"sample\">{{ s }}</span>\r\n </td>\r\n <td>\r\n <mat-select [(ngModel)]=\"m.property\" (selectionChange)=\"onMappingChange(m)\" placeholder=\"\u2014 Ignore \u2014\">\r\n <mat-option [value]=\"null\">\u2014 Ignore \u2014</mat-option>\r\n <mat-option *ngFor=\"let f of availableFields(m.header)\" [value]=\"f.property\">\r\n {{ f.display }}<span *ngIf=\"f.required\"> *</span>\r\n </mat-option>\r\n </mat-select>\r\n </td>\r\n <td>\r\n <span class=\"chip\" [ngClass]=\"confidenceClass(m)\" [matTooltip]=\"m.reasoning || ''\">\r\n {{ m.property ? (m.method === 'AI' ? ((m.confidence * 100) | number:'1.0-0') + '%' : m.method) : 'Unmapped' }}\r\n </span>\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <div class=\"step-actions\">\r\n <button mat-button (click)=\"startOver()\">Start over</button>\r\n <button mat-flat-button color=\"primary\" [disabled]=\"unmappedRequired.length > 0 || loading\" (click)=\"confirmMapping()\">Next</button>\r\n </div>\r\n </div>\r\n </mat-step>\r\n\r\n <!-- Step 3: Review -->\r\n <mat-step label=\"Review\">\r\n <div class=\"step-body\">\r\n <div class=\"tiles\">\r\n <div class=\"tile\"><span class=\"tile-num\">{{ counts.total }}</span><span class=\"tile-label\">Total</span></div>\r\n <div class=\"tile tile-ready\"><span class=\"tile-num\">{{ counts.ready }}</span><span class=\"tile-label\">Ready</span></div>\r\n <div class=\"tile tile-warn\"><span class=\"tile-num\">{{ counts.warning }}</span><span class=\"tile-label\">Warnings</span></div>\r\n <div class=\"tile tile-error\"><span class=\"tile-num\">{{ counts.error }}</span><span class=\"tile-label\">Errors</span></div>\r\n </div>\r\n\r\n <mat-checkbox [(ngModel)]=\"onlyProblems\">Only show rows with problems</mat-checkbox>\r\n\r\n <div class=\"grid-scroll\">\r\n <table mat-table [dataSource]=\"visibleRows\" class=\"review-table\">\r\n\r\n <ng-container matColumnDef=\"rowNumber\">\r\n <th mat-header-cell *matHeaderCellDef>#</th>\r\n <td mat-cell *matCellDef=\"let row\">{{ row.rowNumber }}</td>\r\n </ng-container>\r\n\r\n <ng-container *ngFor=\"let col of reviewColumns\" [matColumnDef]=\"col.property\">\r\n <th mat-header-cell *matHeaderCellDef>{{ col.display }}</th>\r\n <td mat-cell *matCellDef=\"let row\"\r\n [class.cell-error]=\"cellSeverity(row, col.property) === 'error'\"\r\n [class.cell-warn]=\"cellSeverity(row, col.property) === 'warning'\">\r\n <ng-container *ngIf=\"editingRowId === row.importRowID; else showVal\">\r\n <input class=\"cell-input\" [(ngModel)]=\"editModel[col.property]\" />\r\n </ng-container>\r\n <ng-template #showVal>{{ cellValue(row, col.property) }}</ng-template>\r\n </td>\r\n </ng-container>\r\n\r\n <ng-container matColumnDef=\"status\">\r\n <th mat-header-cell *matHeaderCellDef>Status</th>\r\n <td mat-cell *matCellDef=\"let row\">\r\n <span class=\"status-chip\" [ngClass]=\"{\r\n 'chip-green': row.status === 1,\r\n 'chip-orange': row.status === 2,\r\n 'chip-red': row.status === 3\r\n }\" [matTooltip]=\"rowIssueText(row)\">{{ row.statusName }}</span>\r\n <button *ngIf=\"editingRowId !== row.importRowID\" mat-button color=\"primary\" (click)=\"startEdit(row)\">Edit</button>\r\n <ng-container *ngIf=\"editingRowId === row.importRowID\">\r\n <button mat-button color=\"primary\" (click)=\"saveEdit(row)\">Save</button>\r\n <button mat-button (click)=\"cancelEdit()\">Cancel</button>\r\n </ng-container>\r\n </td>\r\n </ng-container>\r\n\r\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\r\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\r\n </table>\r\n </div>\r\n\r\n <div class=\"step-actions\">\r\n <button mat-button (click)=\"startOver()\">Start over</button>\r\n <button mat-flat-button color=\"primary\" [disabled]=\"counts.error > 0 || loading\" (click)=\"doCommit()\">\r\n Import {{ counts.total - counts.error }} record(s)\r\n </button>\r\n </div>\r\n </div>\r\n </mat-step>\r\n\r\n <!-- Step 4: Done -->\r\n <mat-step label=\"Done\">\r\n <div class=\"step-body done-body\">\r\n <mat-icon class=\"done-icon\">check_circle</mat-icon>\r\n <h3>Imported {{ committedCount }} record(s)</h3>\r\n <p *ngIf=\"counts.warning > 0\" class=\"hint\">{{ counts.warning }} row(s) imported with warnings.</p>\r\n <div class=\"step-actions\">\r\n <button mat-flat-button color=\"primary\" (click)=\"close()\">Close</button>\r\n </div>\r\n </div>\r\n </mat-step>\r\n\r\n </mat-horizontal-stepper>\r\n</div>\r\n", styles: [".import-dialog{display:flex;flex-direction:column;min-width:60vw;max-width:90vw}.import-header{display:flex;align-items:center;justify-content:space-between;padding:4px 8px}.import-header h2{margin:0;font-size:18px}.import-stepper{background:transparent}.step-body{padding:12px 8px;display:flex;flex-direction:column;gap:14px}.hint{color:#0009;font-size:13px;margin:0}.upload-row{display:flex;align-items:center;gap:16px;flex-wrap:wrap}.step-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:8px}.alert{padding:8px 12px;border-radius:4px;font-size:13px}.alert-warn{background:#fff3e0;color:#8a5300;border:1px solid #ffcc80}.map-table{width:100%;border-collapse:collapse}.map-table th,.map-table td{text-align:left;padding:6px 8px;border-bottom:1px solid #eee;vertical-align:middle}.map-table th{font-size:12px;color:#0009;font-weight:600}.col-header{font-weight:600}.col-samples .sample{display:inline-block;background:#f2f2f2;border-radius:3px;padding:1px 6px;margin:1px 3px 1px 0;font-size:12px;color:#555}.chip{display:inline-block;padding:2px 8px;border-radius:10px;font-size:12px;color:#fff}.chip-green{background:#2e7d32}.chip-blue{background:#1565c0}.chip-orange{background:#ef6c00}.chip-grey{background:#9e9e9e}.chip-red{background:#c62828}.tiles{display:flex;gap:12px;flex-wrap:wrap}.tile{display:flex;flex-direction:column;align-items:center;min-width:72px;padding:8px 14px;border-radius:6px;background:#f5f5f5}.tile-num{font-size:20px;font-weight:700}.tile-label{font-size:12px;color:#0009}.tile-ready{background:#e8f5e9}.tile-warn{background:#fff3e0}.tile-error{background:#ffebee}.grid-scroll{overflow-x:auto;max-height:46vh;overflow-y:auto;border:1px solid #eee;border-radius:4px}.review-table{width:100%}.cell-error{background:#ffebee;color:#b71c1c}.cell-warn{background:#fff3e0;color:#8a5300}.cell-input{width:100%;box-sizing:border-box;padding:2px 4px}.status-chip{display:inline-block;padding:2px 8px;border-radius:10px;font-size:11px;color:#fff;margin-right:6px}.done-body{align-items:center;text-align:center;padding:32px 8px}.done-icon{color:#2e7d32;font-size:56px;height:56px;width:56px}@media (prefers-color-scheme: dark){.hint,.map-table th,.tile-label{color:#ffffffb3}.col-samples .sample{background:#3a3a3a;color:#ccc}.tile{background:#333}.grid-scroll,.map-table th,.map-table td{border-color:#444}}\n"], dependencies: [{ kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i7.MatTable, selector: "mat-table, table[mat-table]", exportAs: ["matTable"] }, { kind: "directive", type: i7.MatHeaderCellDef, selector: "[matHeaderCellDef]" }, { kind: "directive", type: i7.MatHeaderRowDef, selector: "[matHeaderRowDef]", inputs: ["matHeaderRowDef", "matHeaderRowDefSticky"] }, { kind: "directive", type: i7.MatColumnDef, selector: "[matColumnDef]", inputs: ["matColumnDef"] }, { kind: "directive", type: i7.MatCellDef, selector: "[matCellDef]" }, { kind: "directive", type: i7.MatRowDef, selector: "[matRowDef]", inputs: ["matRowDefColumns", "matRowDefWhen"] }, { kind: "directive", type: i7.MatHeaderCell, selector: "mat-header-cell, th[mat-header-cell]" }, { kind: "directive", type: i7.MatCell, selector: "mat-cell, td[mat-cell]" }, { kind: "component", type: i7.MatHeaderRow, selector: "mat-header-row, tr[mat-header-row]", exportAs: ["matHeaderRow"] }, { kind: "component", type: i7.MatRow, selector: "mat-row, tr[mat-row]", exportAs: ["matRow"] }, { kind: "component", type: i8.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i7$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i7$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: i7$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: i13.MatStep, selector: "mat-step", inputs: ["color"], exportAs: ["matStep"] }, { kind: "component", type: i13.MatStepper, selector: "mat-stepper, mat-vertical-stepper, mat-horizontal-stepper, [matStepper]", inputs: ["disableRipple", "color", "labelPosition", "headerPosition", "animationDuration"], outputs: ["animationDone"], exportAs: ["matStepper", "matVerticalStepper", "matHorizontalStepper"] }, { kind: "component", type: i14.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }] }); }
4813
4813
  }
4814
4814
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ImportDialogComponent, decorators: [{
4815
4815
  type: Component,
4816
- args: [{ selector: 'spa-import-dialog', standalone: false, template: "<div class=\"import-dialog\">\n\n <div class=\"import-header\">\n <h2>{{ title }}</h2>\n <button mat-icon-button (click)=\"close()\" aria-label=\"Close\"><mat-icon>close</mat-icon></button>\n </div>\n\n <mat-progress-bar *ngIf=\"loading\" mode=\"indeterminate\"></mat-progress-bar>\n\n <mat-horizontal-stepper #stepper [linear]=\"false\" class=\"import-stepper\">\n\n <!-- Step 1: Upload -->\n <mat-step label=\"Upload\">\n <div class=\"step-body\">\n <p class=\"hint\">Upload an Excel (.xlsx) file. Not sure of the format? Download a template with the expected columns.</p>\n\n <div class=\"upload-row\">\n <input type=\"file\" accept=\".xlsx\" (change)=\"onFileSelected($event)\" />\n <button mat-stroked-button color=\"primary\" (click)=\"downloadTemplate()\">\n <mat-icon>download</mat-icon> Download template\n </button>\n </div>\n\n <div class=\"step-actions\">\n <button mat-flat-button color=\"primary\" [disabled]=\"!selectedFile || loading\" (click)=\"doUpload()\">Upload</button>\n </div>\n </div>\n </mat-step>\n\n <!-- Step 2: Map columns -->\n <mat-step label=\"Map columns\">\n <div class=\"step-body\">\n <div *ngIf=\"unmappedRequired.length\" class=\"alert alert-warn\">\n Required fields not yet mapped: <strong>{{ unmappedRequired.join(', ') }}</strong>\n </div>\n\n <table class=\"map-table\">\n <thead>\n <tr><th>Spreadsheet column</th><th>Sample values</th><th>Maps to field</th><th>Match</th></tr>\n </thead>\n <tbody>\n <tr *ngFor=\"let m of mappings\">\n <td class=\"col-header\">{{ m.header }}</td>\n <td class=\"col-samples\">\n <span *ngFor=\"let s of samplesFor(m.header)\" class=\"sample\">{{ s }}</span>\n </td>\n <td>\n <mat-select [(ngModel)]=\"m.property\" (selectionChange)=\"onMappingChange(m)\" placeholder=\"\u2014 Ignore \u2014\">\n <mat-option [value]=\"null\">\u2014 Ignore \u2014</mat-option>\n <mat-option *ngFor=\"let f of availableFields(m.header)\" [value]=\"f.property\">\n {{ f.display }}<span *ngIf=\"f.required\"> *</span>\n </mat-option>\n </mat-select>\n </td>\n <td>\n <span class=\"chip\" [ngClass]=\"confidenceClass(m)\" [matTooltip]=\"m.reasoning || ''\">\n {{ m.property ? (m.method === 'AI' ? ((m.confidence * 100) | number:'1.0-0') + '%' : m.method) : 'Unmapped' }}\n </span>\n </td>\n </tr>\n </tbody>\n </table>\n\n <div class=\"step-actions\">\n <button mat-button (click)=\"startOver()\">Start over</button>\n <button mat-flat-button color=\"primary\" [disabled]=\"unmappedRequired.length > 0 || loading\" (click)=\"confirmMapping()\">Next</button>\n </div>\n </div>\n </mat-step>\n\n <!-- Step 3: Review -->\n <mat-step label=\"Review\">\n <div class=\"step-body\">\n <div class=\"tiles\">\n <div class=\"tile\"><span class=\"tile-num\">{{ counts.total }}</span><span class=\"tile-label\">Total</span></div>\n <div class=\"tile tile-ready\"><span class=\"tile-num\">{{ counts.ready }}</span><span class=\"tile-label\">Ready</span></div>\n <div class=\"tile tile-warn\"><span class=\"tile-num\">{{ counts.warning }}</span><span class=\"tile-label\">Warnings</span></div>\n <div class=\"tile tile-error\"><span class=\"tile-num\">{{ counts.error }}</span><span class=\"tile-label\">Errors</span></div>\n </div>\n\n <mat-checkbox [(ngModel)]=\"onlyProblems\">Only show rows with problems</mat-checkbox>\n\n <div class=\"grid-scroll\">\n <table mat-table [dataSource]=\"visibleRows\" class=\"review-table\">\n\n <ng-container matColumnDef=\"rowNumber\">\n <th mat-header-cell *matHeaderCellDef>#</th>\n <td mat-cell *matCellDef=\"let row\">{{ row.rowNumber }}</td>\n </ng-container>\n\n <ng-container *ngFor=\"let col of reviewColumns\" [matColumnDef]=\"col.property\">\n <th mat-header-cell *matHeaderCellDef>{{ col.display }}</th>\n <td mat-cell *matCellDef=\"let row\"\n [class.cell-error]=\"cellSeverity(row, col.property) === 'error'\"\n [class.cell-warn]=\"cellSeverity(row, col.property) === 'warning'\">\n <ng-container *ngIf=\"editingRowId === row.importRowID; else showVal\">\n <input class=\"cell-input\" [(ngModel)]=\"editModel[col.property]\" />\n </ng-container>\n <ng-template #showVal>{{ cellValue(row, col.property) }}</ng-template>\n </td>\n </ng-container>\n\n <ng-container matColumnDef=\"status\">\n <th mat-header-cell *matHeaderCellDef>Status</th>\n <td mat-cell *matCellDef=\"let row\">\n <span class=\"status-chip\" [ngClass]=\"{\n 'chip-green': row.status === 1,\n 'chip-orange': row.status === 2,\n 'chip-red': row.status === 3\n }\" [matTooltip]=\"rowIssueText(row)\">{{ row.statusName }}</span>\n <button *ngIf=\"editingRowId !== row.importRowID\" mat-button color=\"primary\" (click)=\"startEdit(row)\">Edit</button>\n <ng-container *ngIf=\"editingRowId === row.importRowID\">\n <button mat-button color=\"primary\" (click)=\"saveEdit(row)\">Save</button>\n <button mat-button (click)=\"cancelEdit()\">Cancel</button>\n </ng-container>\n </td>\n </ng-container>\n\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\n </table>\n </div>\n\n <div class=\"step-actions\">\n <button mat-button (click)=\"startOver()\">Start over</button>\n <button mat-flat-button color=\"primary\" [disabled]=\"counts.error > 0 || loading\" (click)=\"doCommit()\">\n Import {{ counts.total - counts.error }} record(s)\n </button>\n </div>\n </div>\n </mat-step>\n\n <!-- Step 4: Done -->\n <mat-step label=\"Done\">\n <div class=\"step-body done-body\">\n <mat-icon class=\"done-icon\">check_circle</mat-icon>\n <h3>Imported {{ committedCount }} record(s)</h3>\n <p *ngIf=\"counts.warning > 0\" class=\"hint\">{{ counts.warning }} row(s) imported with warnings.</p>\n <div class=\"step-actions\">\n <button mat-flat-button color=\"primary\" (click)=\"close()\">Close</button>\n </div>\n </div>\n </mat-step>\n\n </mat-horizontal-stepper>\n</div>\n", styles: [".import-dialog{display:flex;flex-direction:column;min-width:60vw;max-width:90vw}.import-header{display:flex;align-items:center;justify-content:space-between;padding:4px 8px}.import-header h2{margin:0;font-size:18px}.import-stepper{background:transparent}.step-body{padding:12px 8px;display:flex;flex-direction:column;gap:14px}.hint{color:#0009;font-size:13px;margin:0}.upload-row{display:flex;align-items:center;gap:16px;flex-wrap:wrap}.step-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:8px}.alert{padding:8px 12px;border-radius:4px;font-size:13px}.alert-warn{background:#fff3e0;color:#8a5300;border:1px solid #ffcc80}.map-table{width:100%;border-collapse:collapse}.map-table th,.map-table td{text-align:left;padding:6px 8px;border-bottom:1px solid #eee;vertical-align:middle}.map-table th{font-size:12px;color:#0009;font-weight:600}.col-header{font-weight:600}.col-samples .sample{display:inline-block;background:#f2f2f2;border-radius:3px;padding:1px 6px;margin:1px 3px 1px 0;font-size:12px;color:#555}.chip{display:inline-block;padding:2px 8px;border-radius:10px;font-size:12px;color:#fff}.chip-green{background:#2e7d32}.chip-blue{background:#1565c0}.chip-orange{background:#ef6c00}.chip-grey{background:#9e9e9e}.chip-red{background:#c62828}.tiles{display:flex;gap:12px;flex-wrap:wrap}.tile{display:flex;flex-direction:column;align-items:center;min-width:72px;padding:8px 14px;border-radius:6px;background:#f5f5f5}.tile-num{font-size:20px;font-weight:700}.tile-label{font-size:12px;color:#0009}.tile-ready{background:#e8f5e9}.tile-warn{background:#fff3e0}.tile-error{background:#ffebee}.grid-scroll{overflow-x:auto;max-height:46vh;overflow-y:auto;border:1px solid #eee;border-radius:4px}.review-table{width:100%}.cell-error{background:#ffebee;color:#b71c1c}.cell-warn{background:#fff3e0;color:#8a5300}.cell-input{width:100%;box-sizing:border-box;padding:2px 4px}.status-chip{display:inline-block;padding:2px 8px;border-radius:10px;font-size:11px;color:#fff;margin-right:6px}.done-body{align-items:center;text-align:center;padding:32px 8px}.done-icon{color:#2e7d32;font-size:56px;height:56px;width:56px}@media (prefers-color-scheme: dark){.hint,.map-table th,.tile-label{color:#ffffffb3}.col-samples .sample{background:#3a3a3a;color:#ccc}.tile{background:#333}.grid-scroll,.map-table th,.map-table td{border-color:#444}}\n"] }]
4816
+ args: [{ selector: 'spa-import-dialog', standalone: false, template: "<div class=\"import-dialog\">\r\n\r\n <div class=\"import-header\">\r\n <h2>{{ title }}</h2>\r\n <button mat-icon-button (click)=\"close()\" aria-label=\"Close\"><mat-icon>close</mat-icon></button>\r\n </div>\r\n\r\n <mat-progress-bar *ngIf=\"loading\" mode=\"indeterminate\"></mat-progress-bar>\r\n\r\n <mat-horizontal-stepper #stepper [linear]=\"false\" class=\"import-stepper\">\r\n\r\n <!-- Step 1: Upload -->\r\n <mat-step label=\"Upload\">\r\n <div class=\"step-body\">\r\n <p class=\"hint\">Upload an Excel (.xlsx) file. Not sure of the format? Download a template with the expected columns.</p>\r\n\r\n <div class=\"upload-row\">\r\n <input type=\"file\" accept=\".xlsx\" (change)=\"onFileSelected($event)\" />\r\n <button mat-stroked-button color=\"primary\" (click)=\"downloadTemplate()\">\r\n <mat-icon>download</mat-icon> Download template\r\n </button>\r\n </div>\r\n\r\n <div class=\"step-actions\">\r\n <button mat-flat-button color=\"primary\" [disabled]=\"!selectedFile || loading\" (click)=\"doUpload()\">Upload</button>\r\n </div>\r\n </div>\r\n </mat-step>\r\n\r\n <!-- Step 2: Map columns -->\r\n <mat-step label=\"Map columns\">\r\n <div class=\"step-body\">\r\n <div *ngIf=\"unmappedRequired.length\" class=\"alert alert-warn\">\r\n Required fields not yet mapped: <strong>{{ unmappedRequired.join(', ') }}</strong>\r\n </div>\r\n\r\n <table class=\"map-table\">\r\n <thead>\r\n <tr><th>Spreadsheet column</th><th>Sample values</th><th>Maps to field</th><th>Match</th></tr>\r\n </thead>\r\n <tbody>\r\n <tr *ngFor=\"let m of mappings\">\r\n <td class=\"col-header\">{{ m.header }}</td>\r\n <td class=\"col-samples\">\r\n <span *ngFor=\"let s of samplesFor(m.header)\" class=\"sample\">{{ s }}</span>\r\n </td>\r\n <td>\r\n <mat-select [(ngModel)]=\"m.property\" (selectionChange)=\"onMappingChange(m)\" placeholder=\"\u2014 Ignore \u2014\">\r\n <mat-option [value]=\"null\">\u2014 Ignore \u2014</mat-option>\r\n <mat-option *ngFor=\"let f of availableFields(m.header)\" [value]=\"f.property\">\r\n {{ f.display }}<span *ngIf=\"f.required\"> *</span>\r\n </mat-option>\r\n </mat-select>\r\n </td>\r\n <td>\r\n <span class=\"chip\" [ngClass]=\"confidenceClass(m)\" [matTooltip]=\"m.reasoning || ''\">\r\n {{ m.property ? (m.method === 'AI' ? ((m.confidence * 100) | number:'1.0-0') + '%' : m.method) : 'Unmapped' }}\r\n </span>\r\n </td>\r\n </tr>\r\n </tbody>\r\n </table>\r\n\r\n <div class=\"step-actions\">\r\n <button mat-button (click)=\"startOver()\">Start over</button>\r\n <button mat-flat-button color=\"primary\" [disabled]=\"unmappedRequired.length > 0 || loading\" (click)=\"confirmMapping()\">Next</button>\r\n </div>\r\n </div>\r\n </mat-step>\r\n\r\n <!-- Step 3: Review -->\r\n <mat-step label=\"Review\">\r\n <div class=\"step-body\">\r\n <div class=\"tiles\">\r\n <div class=\"tile\"><span class=\"tile-num\">{{ counts.total }}</span><span class=\"tile-label\">Total</span></div>\r\n <div class=\"tile tile-ready\"><span class=\"tile-num\">{{ counts.ready }}</span><span class=\"tile-label\">Ready</span></div>\r\n <div class=\"tile tile-warn\"><span class=\"tile-num\">{{ counts.warning }}</span><span class=\"tile-label\">Warnings</span></div>\r\n <div class=\"tile tile-error\"><span class=\"tile-num\">{{ counts.error }}</span><span class=\"tile-label\">Errors</span></div>\r\n </div>\r\n\r\n <mat-checkbox [(ngModel)]=\"onlyProblems\">Only show rows with problems</mat-checkbox>\r\n\r\n <div class=\"grid-scroll\">\r\n <table mat-table [dataSource]=\"visibleRows\" class=\"review-table\">\r\n\r\n <ng-container matColumnDef=\"rowNumber\">\r\n <th mat-header-cell *matHeaderCellDef>#</th>\r\n <td mat-cell *matCellDef=\"let row\">{{ row.rowNumber }}</td>\r\n </ng-container>\r\n\r\n <ng-container *ngFor=\"let col of reviewColumns\" [matColumnDef]=\"col.property\">\r\n <th mat-header-cell *matHeaderCellDef>{{ col.display }}</th>\r\n <td mat-cell *matCellDef=\"let row\"\r\n [class.cell-error]=\"cellSeverity(row, col.property) === 'error'\"\r\n [class.cell-warn]=\"cellSeverity(row, col.property) === 'warning'\">\r\n <ng-container *ngIf=\"editingRowId === row.importRowID; else showVal\">\r\n <input class=\"cell-input\" [(ngModel)]=\"editModel[col.property]\" />\r\n </ng-container>\r\n <ng-template #showVal>{{ cellValue(row, col.property) }}</ng-template>\r\n </td>\r\n </ng-container>\r\n\r\n <ng-container matColumnDef=\"status\">\r\n <th mat-header-cell *matHeaderCellDef>Status</th>\r\n <td mat-cell *matCellDef=\"let row\">\r\n <span class=\"status-chip\" [ngClass]=\"{\r\n 'chip-green': row.status === 1,\r\n 'chip-orange': row.status === 2,\r\n 'chip-red': row.status === 3\r\n }\" [matTooltip]=\"rowIssueText(row)\">{{ row.statusName }}</span>\r\n <button *ngIf=\"editingRowId !== row.importRowID\" mat-button color=\"primary\" (click)=\"startEdit(row)\">Edit</button>\r\n <ng-container *ngIf=\"editingRowId === row.importRowID\">\r\n <button mat-button color=\"primary\" (click)=\"saveEdit(row)\">Save</button>\r\n <button mat-button (click)=\"cancelEdit()\">Cancel</button>\r\n </ng-container>\r\n </td>\r\n </ng-container>\r\n\r\n <tr mat-header-row *matHeaderRowDef=\"displayedColumns\"></tr>\r\n <tr mat-row *matRowDef=\"let row; columns: displayedColumns;\"></tr>\r\n </table>\r\n </div>\r\n\r\n <div class=\"step-actions\">\r\n <button mat-button (click)=\"startOver()\">Start over</button>\r\n <button mat-flat-button color=\"primary\" [disabled]=\"counts.error > 0 || loading\" (click)=\"doCommit()\">\r\n Import {{ counts.total - counts.error }} record(s)\r\n </button>\r\n </div>\r\n </div>\r\n </mat-step>\r\n\r\n <!-- Step 4: Done -->\r\n <mat-step label=\"Done\">\r\n <div class=\"step-body done-body\">\r\n <mat-icon class=\"done-icon\">check_circle</mat-icon>\r\n <h3>Imported {{ committedCount }} record(s)</h3>\r\n <p *ngIf=\"counts.warning > 0\" class=\"hint\">{{ counts.warning }} row(s) imported with warnings.</p>\r\n <div class=\"step-actions\">\r\n <button mat-flat-button color=\"primary\" (click)=\"close()\">Close</button>\r\n </div>\r\n </div>\r\n </mat-step>\r\n\r\n </mat-horizontal-stepper>\r\n</div>\r\n", styles: [".import-dialog{display:flex;flex-direction:column;min-width:60vw;max-width:90vw}.import-header{display:flex;align-items:center;justify-content:space-between;padding:4px 8px}.import-header h2{margin:0;font-size:18px}.import-stepper{background:transparent}.step-body{padding:12px 8px;display:flex;flex-direction:column;gap:14px}.hint{color:#0009;font-size:13px;margin:0}.upload-row{display:flex;align-items:center;gap:16px;flex-wrap:wrap}.step-actions{display:flex;justify-content:flex-end;gap:8px;margin-top:8px}.alert{padding:8px 12px;border-radius:4px;font-size:13px}.alert-warn{background:#fff3e0;color:#8a5300;border:1px solid #ffcc80}.map-table{width:100%;border-collapse:collapse}.map-table th,.map-table td{text-align:left;padding:6px 8px;border-bottom:1px solid #eee;vertical-align:middle}.map-table th{font-size:12px;color:#0009;font-weight:600}.col-header{font-weight:600}.col-samples .sample{display:inline-block;background:#f2f2f2;border-radius:3px;padding:1px 6px;margin:1px 3px 1px 0;font-size:12px;color:#555}.chip{display:inline-block;padding:2px 8px;border-radius:10px;font-size:12px;color:#fff}.chip-green{background:#2e7d32}.chip-blue{background:#1565c0}.chip-orange{background:#ef6c00}.chip-grey{background:#9e9e9e}.chip-red{background:#c62828}.tiles{display:flex;gap:12px;flex-wrap:wrap}.tile{display:flex;flex-direction:column;align-items:center;min-width:72px;padding:8px 14px;border-radius:6px;background:#f5f5f5}.tile-num{font-size:20px;font-weight:700}.tile-label{font-size:12px;color:#0009}.tile-ready{background:#e8f5e9}.tile-warn{background:#fff3e0}.tile-error{background:#ffebee}.grid-scroll{overflow-x:auto;max-height:46vh;overflow-y:auto;border:1px solid #eee;border-radius:4px}.review-table{width:100%}.cell-error{background:#ffebee;color:#b71c1c}.cell-warn{background:#fff3e0;color:#8a5300}.cell-input{width:100%;box-sizing:border-box;padding:2px 4px}.status-chip{display:inline-block;padding:2px 8px;border-radius:10px;font-size:11px;color:#fff;margin-right:6px}.done-body{align-items:center;text-align:center;padding:32px 8px}.done-icon{color:#2e7d32;font-size:56px;height:56px;width:56px}@media (prefers-color-scheme: dark){.hint,.map-table th,.tile-label{color:#ffffffb3}.col-samples .sample{background:#3a3a3a;color:#ccc}.tile{background:#333}.grid-scroll,.map-table th,.map-table td{border-color:#444}}\n"] }]
4817
4817
  }], ctorParameters: () => [{ type: HttpService }, { type: i1$1.HttpClient }, { type: MessageService }, { type: i4.MatDialogRef }, { type: undefined, decorators: [{
4818
4818
  type: Inject,
4819
4819
  args: [MAT_DIALOG_DATA]
@@ -8861,11 +8861,11 @@ class SetupGuideComponent {
8861
8861
  this.dataService.Navigate(act.link || step.link);
8862
8862
  }
8863
8863
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: SetupGuideComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
8864
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: SetupGuideComponent, isStandalone: false, selector: "spa-setup-guide", ngImport: i0, template: "<div class=\"setup-page\" *ngIf=\"status\">\n\n <!-- Hero: overall readiness + celebration state at 100% -->\n <mat-card class=\"setup-hero\" [class.celebrate]=\"status.percent === 100\">\n <div class=\"hero-content\" *ngIf=\"status.percent < 100\">\n <div class=\"hero-text\">\n <h1>{{ title }}</h1>\n <p>Complete these steps to get your system ready for day-to-day operation.</p>\n <span class=\"hero-counter\">{{ status.completed }} of {{ status.total }} steps completed</span>\n </div>\n <div class=\"hero-progress\">\n <span class=\"hero-percent\">{{ status.percent }}%</span>\n <mat-progress-bar mode=\"determinate\" [value]=\"status.percent\"></mat-progress-bar>\n </div>\n </div>\n <div class=\"hero-content celebration\" *ngIf=\"status.percent === 100\">\n <mat-icon class=\"celebrate-icon\">celebration</mat-icon>\n <div class=\"hero-text\">\n <h1>You're all set!</h1>\n <p>All setup steps are complete \u2014 your system is ready to operate.</p>\n </div>\n </div>\n </mat-card>\n\n <!-- Category groups, top-down -->\n <mat-card class=\"setup-group\" *ngFor=\"let group of groups\">\n <div class=\"group-header\">\n <h2>{{ group.name }}</h2>\n <span class=\"group-counter\">{{ groupCompleted(group) }} of {{ group.steps.length }}</span>\n </div>\n <mat-accordion displayMode=\"flat\">\n <mat-expansion-panel *ngFor=\"let step of group.steps\" [expanded]=\"step.key === expandedKey\" [class.step-complete]=\"step.complete\">\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon class=\"step-status-icon\" [class.done]=\"step.complete\" [class.required]=\"!step.complete && step.required\">{{ step.complete ? 'check_circle' : 'radio_button_unchecked' }}</mat-icon>\n <span class=\"step-title\" [class.done]=\"step.complete\">{{ step.title }}</span>\n </mat-panel-title>\n <mat-panel-description>\n <span class=\"step-chip\" *ngIf=\"step.target > 1\">{{ progressLabel(step) }}</span>\n <span class=\"step-optional\" *ngIf=\"!step.required && !step.complete\">Optional</span>\n </mat-panel-description>\n </mat-expansion-panel-header>\n <p class=\"step-description\">{{ step.description }}</p>\n <div class=\"step-actions\" *ngIf=\"hasAction(step)\">\n <button mat-flat-button color=\"primary\" (click)=\"doPrimary(step)\">{{ primaryLabel(step) }}</button>\n <button mat-button *ngIf=\"hasDialog(step)\" (click)=\"navigate(step)\">View page</button>\n </div>\n </mat-expansion-panel>\n </mat-accordion>\n </mat-card>\n\n</div>\n\n<!-- Graceful empty state (feature disabled or status unavailable) -->\n<div class=\"setup-empty\" *ngIf=\"!status\">\n <mat-icon>rocket_launch</mat-icon>\n <p>Setup status is not available yet.</p>\n</div>\n", styles: [".setup-page{max-width:860px;margin:0 auto;padding:16px;display:flex;flex-direction:column;gap:16px}.setup-hero{padding:24px}.hero-content{display:flex;align-items:center;justify-content:space-between;gap:24px;flex-wrap:wrap}.hero-text h1{margin:0 0 4px;font-size:24px}.hero-text p{margin:0 0 8px;color:#0009}.hero-counter{font-size:13px;color:#0009}.hero-progress{flex:1;min-width:220px;max-width:340px}.hero-percent{display:block;font-size:28px;font-weight:600;color:#2e7d32;margin-bottom:6px;text-align:right}.hero-progress mat-progress-bar{height:10px;border-radius:5px}.setup-hero.celebrate{background:linear-gradient(135deg,#e8f5e9,#f1f8e9)}.celebration{justify-content:flex-start}.celebrate-icon{font-size:48px;width:48px;height:48px;color:#2e7d32;animation:celebrate-pop .6s ease-out}@keyframes celebrate-pop{0%{transform:scale(.3);opacity:0}70%{transform:scale(1.15)}to{transform:scale(1);opacity:1}}.setup-group{padding:16px}.group-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.group-header h2{margin:0;font-size:17px}.group-counter{font-size:12px;color:#0000008c}.step-status-icon{margin-right:10px;color:#9e9e9e}.step-status-icon.done{color:#4caf50}.step-status-icon.required{color:#ff9800}.step-title.done{color:#00000073;text-decoration:line-through}.step-chip{background:#e3f2fd;color:#1565c0;border-radius:12px;padding:2px 10px;font-size:12px;margin-right:8px}.step-optional{font-size:12px;color:#00000073;font-style:italic}.step-description{margin:4px 0 12px;color:#000000a6}.step-actions{display:flex;gap:8px;margin-bottom:4px}mat-panel-title{align-items:center}mat-panel-description{justify-content:flex-end;align-items:center}.setup-empty{text-align:center;padding:48px 16px;color:#00000080}.setup-empty mat-icon{font-size:40px;width:40px;height:40px}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i18.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "component", type: i14.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "directive", type: i6$1.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i6$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i6$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i6$1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "directive", type: i6$1.MatExpansionPanelDescription, selector: "mat-panel-description" }] }); }
8864
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: SetupGuideComponent, isStandalone: false, selector: "spa-setup-guide", ngImport: i0, template: "<div class=\"setup-page\" *ngIf=\"status\">\r\n\r\n <!-- Hero: overall readiness + celebration state at 100% -->\r\n <mat-card class=\"setup-hero\" [class.celebrate]=\"status.percent === 100\">\r\n <div class=\"hero-content\" *ngIf=\"status.percent < 100\">\r\n <div class=\"hero-text\">\r\n <h1>{{ title }}</h1>\r\n <p>Complete these steps to get your system ready for day-to-day operation.</p>\r\n <span class=\"hero-counter\">{{ status.completed }} of {{ status.total }} steps completed</span>\r\n </div>\r\n <div class=\"hero-progress\">\r\n <span class=\"hero-percent\">{{ status.percent }}%</span>\r\n <mat-progress-bar mode=\"determinate\" [value]=\"status.percent\"></mat-progress-bar>\r\n </div>\r\n </div>\r\n <div class=\"hero-content celebration\" *ngIf=\"status.percent === 100\">\r\n <mat-icon class=\"celebrate-icon\">celebration</mat-icon>\r\n <div class=\"hero-text\">\r\n <h1>You're all set!</h1>\r\n <p>All setup steps are complete \u2014 your system is ready to operate.</p>\r\n </div>\r\n </div>\r\n </mat-card>\r\n\r\n <!-- Category groups, top-down -->\r\n <mat-card class=\"setup-group\" *ngFor=\"let group of groups\">\r\n <div class=\"group-header\">\r\n <h2>{{ group.name }}</h2>\r\n <span class=\"group-counter\">{{ groupCompleted(group) }} of {{ group.steps.length }}</span>\r\n </div>\r\n <mat-accordion displayMode=\"flat\">\r\n <mat-expansion-panel *ngFor=\"let step of group.steps\" [expanded]=\"step.key === expandedKey\" [class.step-complete]=\"step.complete\">\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>\r\n <mat-icon class=\"step-status-icon\" [class.done]=\"step.complete\" [class.required]=\"!step.complete && step.required\">{{ step.complete ? 'check_circle' : 'radio_button_unchecked' }}</mat-icon>\r\n <span class=\"step-title\" [class.done]=\"step.complete\">{{ step.title }}</span>\r\n </mat-panel-title>\r\n <mat-panel-description>\r\n <span class=\"step-chip\" *ngIf=\"step.target > 1\">{{ progressLabel(step) }}</span>\r\n <span class=\"step-optional\" *ngIf=\"!step.required && !step.complete\">Optional</span>\r\n </mat-panel-description>\r\n </mat-expansion-panel-header>\r\n <p class=\"step-description\">{{ step.description }}</p>\r\n <div class=\"step-actions\" *ngIf=\"hasAction(step)\">\r\n <button mat-flat-button color=\"primary\" (click)=\"doPrimary(step)\">{{ primaryLabel(step) }}</button>\r\n <button mat-button *ngIf=\"hasDialog(step)\" (click)=\"navigate(step)\">View page</button>\r\n </div>\r\n </mat-expansion-panel>\r\n </mat-accordion>\r\n </mat-card>\r\n\r\n</div>\r\n\r\n<!-- Graceful empty state (feature disabled or status unavailable) -->\r\n<div class=\"setup-empty\" *ngIf=\"!status\">\r\n <mat-icon>rocket_launch</mat-icon>\r\n <p>Setup status is not available yet.</p>\r\n</div>\r\n", styles: [".setup-page{max-width:860px;margin:0 auto;padding:16px;display:flex;flex-direction:column;gap:16px}.setup-hero{padding:24px}.hero-content{display:flex;align-items:center;justify-content:space-between;gap:24px;flex-wrap:wrap}.hero-text h1{margin:0 0 4px;font-size:24px}.hero-text p{margin:0 0 8px;color:#0009}.hero-counter{font-size:13px;color:#0009}.hero-progress{flex:1;min-width:220px;max-width:340px}.hero-percent{display:block;font-size:28px;font-weight:600;color:#2e7d32;margin-bottom:6px;text-align:right}.hero-progress mat-progress-bar{height:10px;border-radius:5px}.setup-hero.celebrate{background:linear-gradient(135deg,#e8f5e9,#f1f8e9)}.celebration{justify-content:flex-start}.celebrate-icon{font-size:48px;width:48px;height:48px;color:#2e7d32;animation:celebrate-pop .6s ease-out}@keyframes celebrate-pop{0%{transform:scale(.3);opacity:0}70%{transform:scale(1.15)}to{transform:scale(1);opacity:1}}.setup-group{padding:16px}.group-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.group-header h2{margin:0;font-size:17px}.group-counter{font-size:12px;color:#0000008c}.step-status-icon{margin-right:10px;color:#9e9e9e}.step-status-icon.done{color:#4caf50}.step-status-icon.required{color:#ff9800}.step-title.done{color:#00000073;text-decoration:line-through}.step-chip{background:#e3f2fd;color:#1565c0;border-radius:12px;padding:2px 10px;font-size:12px;margin-right:8px}.step-optional{font-size:12px;color:#00000073;font-style:italic}.step-description{margin:4px 0 12px;color:#000000a6}.step-actions{display:flex;gap:8px;margin-bottom:4px}mat-panel-title{align-items:center}mat-panel-description{justify-content:flex-end;align-items:center}.setup-empty{text-align:center;padding:48px 16px;color:#00000080}.setup-empty mat-icon{font-size:40px;width:40px;height:40px}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i18.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "component", type: i14.MatProgressBar, selector: "mat-progress-bar", inputs: ["color", "value", "bufferValue", "mode"], outputs: ["animationEnd"], exportAs: ["matProgressBar"] }, { kind: "directive", type: i6$1.MatAccordion, selector: "mat-accordion", inputs: ["hideToggle", "displayMode", "togglePosition"], exportAs: ["matAccordion"] }, { kind: "component", type: i6$1.MatExpansionPanel, selector: "mat-expansion-panel", inputs: ["hideToggle", "togglePosition"], outputs: ["afterExpand", "afterCollapse"], exportAs: ["matExpansionPanel"] }, { kind: "component", type: i6$1.MatExpansionPanelHeader, selector: "mat-expansion-panel-header", inputs: ["expandedHeight", "collapsedHeight", "tabIndex"] }, { kind: "directive", type: i6$1.MatExpansionPanelTitle, selector: "mat-panel-title" }, { kind: "directive", type: i6$1.MatExpansionPanelDescription, selector: "mat-panel-description" }] }); }
8865
8865
  }
8866
8866
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: SetupGuideComponent, decorators: [{
8867
8867
  type: Component,
8868
- args: [{ selector: 'spa-setup-guide', standalone: false, template: "<div class=\"setup-page\" *ngIf=\"status\">\n\n <!-- Hero: overall readiness + celebration state at 100% -->\n <mat-card class=\"setup-hero\" [class.celebrate]=\"status.percent === 100\">\n <div class=\"hero-content\" *ngIf=\"status.percent < 100\">\n <div class=\"hero-text\">\n <h1>{{ title }}</h1>\n <p>Complete these steps to get your system ready for day-to-day operation.</p>\n <span class=\"hero-counter\">{{ status.completed }} of {{ status.total }} steps completed</span>\n </div>\n <div class=\"hero-progress\">\n <span class=\"hero-percent\">{{ status.percent }}%</span>\n <mat-progress-bar mode=\"determinate\" [value]=\"status.percent\"></mat-progress-bar>\n </div>\n </div>\n <div class=\"hero-content celebration\" *ngIf=\"status.percent === 100\">\n <mat-icon class=\"celebrate-icon\">celebration</mat-icon>\n <div class=\"hero-text\">\n <h1>You're all set!</h1>\n <p>All setup steps are complete \u2014 your system is ready to operate.</p>\n </div>\n </div>\n </mat-card>\n\n <!-- Category groups, top-down -->\n <mat-card class=\"setup-group\" *ngFor=\"let group of groups\">\n <div class=\"group-header\">\n <h2>{{ group.name }}</h2>\n <span class=\"group-counter\">{{ groupCompleted(group) }} of {{ group.steps.length }}</span>\n </div>\n <mat-accordion displayMode=\"flat\">\n <mat-expansion-panel *ngFor=\"let step of group.steps\" [expanded]=\"step.key === expandedKey\" [class.step-complete]=\"step.complete\">\n <mat-expansion-panel-header>\n <mat-panel-title>\n <mat-icon class=\"step-status-icon\" [class.done]=\"step.complete\" [class.required]=\"!step.complete && step.required\">{{ step.complete ? 'check_circle' : 'radio_button_unchecked' }}</mat-icon>\n <span class=\"step-title\" [class.done]=\"step.complete\">{{ step.title }}</span>\n </mat-panel-title>\n <mat-panel-description>\n <span class=\"step-chip\" *ngIf=\"step.target > 1\">{{ progressLabel(step) }}</span>\n <span class=\"step-optional\" *ngIf=\"!step.required && !step.complete\">Optional</span>\n </mat-panel-description>\n </mat-expansion-panel-header>\n <p class=\"step-description\">{{ step.description }}</p>\n <div class=\"step-actions\" *ngIf=\"hasAction(step)\">\n <button mat-flat-button color=\"primary\" (click)=\"doPrimary(step)\">{{ primaryLabel(step) }}</button>\n <button mat-button *ngIf=\"hasDialog(step)\" (click)=\"navigate(step)\">View page</button>\n </div>\n </mat-expansion-panel>\n </mat-accordion>\n </mat-card>\n\n</div>\n\n<!-- Graceful empty state (feature disabled or status unavailable) -->\n<div class=\"setup-empty\" *ngIf=\"!status\">\n <mat-icon>rocket_launch</mat-icon>\n <p>Setup status is not available yet.</p>\n</div>\n", styles: [".setup-page{max-width:860px;margin:0 auto;padding:16px;display:flex;flex-direction:column;gap:16px}.setup-hero{padding:24px}.hero-content{display:flex;align-items:center;justify-content:space-between;gap:24px;flex-wrap:wrap}.hero-text h1{margin:0 0 4px;font-size:24px}.hero-text p{margin:0 0 8px;color:#0009}.hero-counter{font-size:13px;color:#0009}.hero-progress{flex:1;min-width:220px;max-width:340px}.hero-percent{display:block;font-size:28px;font-weight:600;color:#2e7d32;margin-bottom:6px;text-align:right}.hero-progress mat-progress-bar{height:10px;border-radius:5px}.setup-hero.celebrate{background:linear-gradient(135deg,#e8f5e9,#f1f8e9)}.celebration{justify-content:flex-start}.celebrate-icon{font-size:48px;width:48px;height:48px;color:#2e7d32;animation:celebrate-pop .6s ease-out}@keyframes celebrate-pop{0%{transform:scale(.3);opacity:0}70%{transform:scale(1.15)}to{transform:scale(1);opacity:1}}.setup-group{padding:16px}.group-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.group-header h2{margin:0;font-size:17px}.group-counter{font-size:12px;color:#0000008c}.step-status-icon{margin-right:10px;color:#9e9e9e}.step-status-icon.done{color:#4caf50}.step-status-icon.required{color:#ff9800}.step-title.done{color:#00000073;text-decoration:line-through}.step-chip{background:#e3f2fd;color:#1565c0;border-radius:12px;padding:2px 10px;font-size:12px;margin-right:8px}.step-optional{font-size:12px;color:#00000073;font-style:italic}.step-description{margin:4px 0 12px;color:#000000a6}.step-actions{display:flex;gap:8px;margin-bottom:4px}mat-panel-title{align-items:center}mat-panel-description{justify-content:flex-end;align-items:center}.setup-empty{text-align:center;padding:48px 16px;color:#00000080}.setup-empty mat-icon{font-size:40px;width:40px;height:40px}\n"] }]
8868
+ args: [{ selector: 'spa-setup-guide', standalone: false, template: "<div class=\"setup-page\" *ngIf=\"status\">\r\n\r\n <!-- Hero: overall readiness + celebration state at 100% -->\r\n <mat-card class=\"setup-hero\" [class.celebrate]=\"status.percent === 100\">\r\n <div class=\"hero-content\" *ngIf=\"status.percent < 100\">\r\n <div class=\"hero-text\">\r\n <h1>{{ title }}</h1>\r\n <p>Complete these steps to get your system ready for day-to-day operation.</p>\r\n <span class=\"hero-counter\">{{ status.completed }} of {{ status.total }} steps completed</span>\r\n </div>\r\n <div class=\"hero-progress\">\r\n <span class=\"hero-percent\">{{ status.percent }}%</span>\r\n <mat-progress-bar mode=\"determinate\" [value]=\"status.percent\"></mat-progress-bar>\r\n </div>\r\n </div>\r\n <div class=\"hero-content celebration\" *ngIf=\"status.percent === 100\">\r\n <mat-icon class=\"celebrate-icon\">celebration</mat-icon>\r\n <div class=\"hero-text\">\r\n <h1>You're all set!</h1>\r\n <p>All setup steps are complete \u2014 your system is ready to operate.</p>\r\n </div>\r\n </div>\r\n </mat-card>\r\n\r\n <!-- Category groups, top-down -->\r\n <mat-card class=\"setup-group\" *ngFor=\"let group of groups\">\r\n <div class=\"group-header\">\r\n <h2>{{ group.name }}</h2>\r\n <span class=\"group-counter\">{{ groupCompleted(group) }} of {{ group.steps.length }}</span>\r\n </div>\r\n <mat-accordion displayMode=\"flat\">\r\n <mat-expansion-panel *ngFor=\"let step of group.steps\" [expanded]=\"step.key === expandedKey\" [class.step-complete]=\"step.complete\">\r\n <mat-expansion-panel-header>\r\n <mat-panel-title>\r\n <mat-icon class=\"step-status-icon\" [class.done]=\"step.complete\" [class.required]=\"!step.complete && step.required\">{{ step.complete ? 'check_circle' : 'radio_button_unchecked' }}</mat-icon>\r\n <span class=\"step-title\" [class.done]=\"step.complete\">{{ step.title }}</span>\r\n </mat-panel-title>\r\n <mat-panel-description>\r\n <span class=\"step-chip\" *ngIf=\"step.target > 1\">{{ progressLabel(step) }}</span>\r\n <span class=\"step-optional\" *ngIf=\"!step.required && !step.complete\">Optional</span>\r\n </mat-panel-description>\r\n </mat-expansion-panel-header>\r\n <p class=\"step-description\">{{ step.description }}</p>\r\n <div class=\"step-actions\" *ngIf=\"hasAction(step)\">\r\n <button mat-flat-button color=\"primary\" (click)=\"doPrimary(step)\">{{ primaryLabel(step) }}</button>\r\n <button mat-button *ngIf=\"hasDialog(step)\" (click)=\"navigate(step)\">View page</button>\r\n </div>\r\n </mat-expansion-panel>\r\n </mat-accordion>\r\n </mat-card>\r\n\r\n</div>\r\n\r\n<!-- Graceful empty state (feature disabled or status unavailable) -->\r\n<div class=\"setup-empty\" *ngIf=\"!status\">\r\n <mat-icon>rocket_launch</mat-icon>\r\n <p>Setup status is not available yet.</p>\r\n</div>\r\n", styles: [".setup-page{max-width:860px;margin:0 auto;padding:16px;display:flex;flex-direction:column;gap:16px}.setup-hero{padding:24px}.hero-content{display:flex;align-items:center;justify-content:space-between;gap:24px;flex-wrap:wrap}.hero-text h1{margin:0 0 4px;font-size:24px}.hero-text p{margin:0 0 8px;color:#0009}.hero-counter{font-size:13px;color:#0009}.hero-progress{flex:1;min-width:220px;max-width:340px}.hero-percent{display:block;font-size:28px;font-weight:600;color:#2e7d32;margin-bottom:6px;text-align:right}.hero-progress mat-progress-bar{height:10px;border-radius:5px}.setup-hero.celebrate{background:linear-gradient(135deg,#e8f5e9,#f1f8e9)}.celebration{justify-content:flex-start}.celebrate-icon{font-size:48px;width:48px;height:48px;color:#2e7d32;animation:celebrate-pop .6s ease-out}@keyframes celebrate-pop{0%{transform:scale(.3);opacity:0}70%{transform:scale(1.15)}to{transform:scale(1);opacity:1}}.setup-group{padding:16px}.group-header{display:flex;align-items:center;justify-content:space-between;margin-bottom:8px}.group-header h2{margin:0;font-size:17px}.group-counter{font-size:12px;color:#0000008c}.step-status-icon{margin-right:10px;color:#9e9e9e}.step-status-icon.done{color:#4caf50}.step-status-icon.required{color:#ff9800}.step-title.done{color:#00000073;text-decoration:line-through}.step-chip{background:#e3f2fd;color:#1565c0;border-radius:12px;padding:2px 10px;font-size:12px;margin-right:8px}.step-optional{font-size:12px;color:#00000073;font-style:italic}.step-description{margin:4px 0 12px;color:#000000a6}.step-actions{display:flex;gap:8px;margin-bottom:4px}mat-panel-title{align-items:center}mat-panel-description{justify-content:flex-end;align-items:center}.setup-empty{text-align:center;padding:48px 16px;color:#00000080}.setup-empty mat-icon{font-size:40px;width:40px;height:40px}\n"] }]
8869
8869
  }] });
8870
8870
 
8871
8871
  // TinSync toolbar indicator — a persistent connection light that lives in the nav bar next to Notifications.
@@ -11194,11 +11194,11 @@ class TableRowComponent {
11194
11194
  return false;
11195
11195
  }
11196
11196
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: TableRowComponent, deps: [{ token: ButtonService }], target: i0.ɵɵFactoryTarget.Component }); }
11197
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: TableRowComponent, isStandalone: false, selector: "app-table-row", inputs: { column: "column", row: "row", config: "config", smallScreen: "smallScreen" }, outputs: { actionClick: "actionClick", columnClick: "columnClick", showBannerEvent: "showBannerEvent" }, usesOnChanges: true, ngImport: i0, template: "<ng-container [ngSwitch]=\"column.type\">\r\n <ng-container *ngSwitchCase=\"'checkbox'\">\r\n <spa-check [value]=\"row[column.name]\" [readonly]=\"true\"></spa-check>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'select'\">\r\n <spa-select-lite [options]=\"column.options\" [optionDisplay]=\"column.optionDisplay\" [optionValue]=\"column.optionValue\" [(value)]=\"row[column.name]\" width=\"90%\"></spa-select-lite>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'chip'\">\r\n <button mat-stroked-button (click)=\"onColumnClick(column, row)\" [ngStyle]=\"{'background-color': vm.color, 'color': 'rgba(0, 0, 0, 0.87)', 'border': 'none'}\">{{row[column.name]}}</button>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'icon'\">\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'date'\">\r\n {{row[column.name] | date : 'dd/MM/yyyy'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'date-short'\">\r\n {{row[column.name] | date : 'd MMM'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'datetime'\">\r\n {{row[column.name] | date : 'dd/MM/yyyy HH:mm'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'datetimesec'\">\r\n {{row[column.name] | date : 'dd/MM/yyyy HH:mm:ss'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'money'\">\r\n <label [ngStyle]=\"{'color': vm.color }\">{{row[column.name] | currency:'':''}}</label>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'button'\">\r\n <button mat-stroked-button (click)=\"onColumnClick(column, row)\" [ngStyle]=\"{'color': vm.color}\" >{{row[column.name]}}</button>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchDefault>\r\n <label [ngStyle]=\"{'color': vm.color }\">\r\n <ng-container *ngIf=\"column.type === 'number'\">\r\n {{row[column.name] | number:'1.0-2'}}\r\n </ng-container>\r\n <ng-container *ngIf=\"column.type !== 'number'\">\r\n {{vm.text}}\r\n </ng-container>\r\n </label>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.truncated\" matTooltip='Show more' matTooltipPosition=\"above\" (click)=\"showBanner(row[column.name])\">more_horiz</mat-icon>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n</ng-container>\r\n", styles: [".top{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between;align-items:center;margin-bottom:10px;margin-top:10px}.mat-mini-fab{width:32px;height:32px}.mat-mini-fab mat-icon{font-size:16px;margin-top:-3px}.mat-icon-button{width:32px;height:32px}.mat-icon-button mat-icon{font-size:20px;margin-top:-7px}.col-icon{margin-left:10px;vertical-align:middle}.title{margin-top:10px;font-size:larger;font-weight:300}.make-gray{background-color:#e5e5e5}.right-padding{padding-right:10px}.action-buttons-container{display:flex;justify-content:flex-end;align-items:center}.refreshIcon{font-size:22px!important;margin-top:-7px!important}.mat-mdc-cell .mat-mdc-outlined-button,.mat-mdc-cell .mat-mdc-button-base:not(.mat-mdc-icon-button){height:auto!important;min-height:36px!important;white-space:normal!important;word-break:break-word!important;overflow-wrap:break-word!important;line-height:1.4!important;text-align:left!important;padding:6px 16px!important}.mat-mdc-cell .mat-mdc-outlined-button .mdc-button__label,.mat-mdc-cell .mat-mdc-button-base:not(.mat-mdc-icon-button) .mdc-button__label{white-space:normal!important;word-break:break-word!important;overflow-wrap:break-word!important}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i7$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: CheckComponent, selector: "spa-check", inputs: ["readonly", "display", "value", "infoMessage"], outputs: ["valueChange", "click", "check", "uncheck", "infoClick"] }, { kind: "component", type: SelectLiteComponent, selector: "spa-select-lite" }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }, { kind: "pipe", type: i1.CurrencyPipe, name: "currency" }, { kind: "pipe", type: i1.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
11197
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: TableRowComponent, isStandalone: false, selector: "app-table-row", inputs: { column: "column", row: "row", config: "config", smallScreen: "smallScreen" }, outputs: { actionClick: "actionClick", columnClick: "columnClick", showBannerEvent: "showBannerEvent" }, usesOnChanges: true, ngImport: i0, template: "<ng-container [ngSwitch]=\"column.type\">\r\n <ng-container *ngSwitchCase=\"'checkbox'\">\r\n <spa-check [value]=\"row[column.name]\" [readonly]=\"true\"></spa-check>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'select'\">\r\n <spa-select-lite [options]=\"column.options\" [optionDisplay]=\"column.optionDisplay\" [optionValue]=\"column.optionValue\" [(value)]=\"row[column.name]\" width=\"90%\"></spa-select-lite>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'chip'\">\r\n <button mat-stroked-button (click)=\"onColumnClick(column, row)\" [ngStyle]=\"{'background-color': vm.color || '#eceff1', 'color': 'rgba(0, 0, 0, 0.87)', 'border': 'none'}\">{{row[column.name]}}</button>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'icon'\">\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'date'\">\r\n {{row[column.name] | date : 'dd/MM/yyyy'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'date-short'\">\r\n {{row[column.name] | date : 'd MMM'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'datetime'\">\r\n {{row[column.name] | date : 'dd/MM/yyyy HH:mm'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'datetimesec'\">\r\n {{row[column.name] | date : 'dd/MM/yyyy HH:mm:ss'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'money'\">\r\n <label [ngStyle]=\"{'color': vm.color }\">{{row[column.name] | currency:'':''}}</label>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'button'\">\r\n <button mat-stroked-button (click)=\"onColumnClick(column, row)\" [ngStyle]=\"{'color': vm.color}\" >{{row[column.name]}}</button>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchDefault>\r\n <label [ngStyle]=\"{'color': vm.color }\">\r\n <ng-container *ngIf=\"column.type === 'number'\">\r\n {{row[column.name] | number:'1.0-2'}}\r\n </ng-container>\r\n <ng-container *ngIf=\"column.type !== 'number'\">\r\n {{vm.text}}\r\n </ng-container>\r\n </label>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.truncated\" matTooltip='Show more' matTooltipPosition=\"above\" (click)=\"showBanner(row[column.name])\">more_horiz</mat-icon>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n</ng-container>\r\n", styles: [".top{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between;align-items:center;margin-bottom:10px;margin-top:10px}.mat-mini-fab{width:32px;height:32px}.mat-mini-fab mat-icon{font-size:16px;margin-top:-3px}.mat-icon-button{width:32px;height:32px}.mat-icon-button mat-icon{font-size:20px;margin-top:-7px}.col-icon{margin-left:10px;vertical-align:middle}.title{margin-top:10px;font-size:larger;font-weight:300}.make-gray{background-color:#e5e5e5}.right-padding{padding-right:10px}.action-buttons-container{display:flex;justify-content:flex-end;align-items:center}.refreshIcon{font-size:22px!important;margin-top:-7px!important}.mat-mdc-cell .mat-mdc-outlined-button,.mat-mdc-cell .mat-mdc-button-base:not(.mat-mdc-icon-button){height:auto!important;min-height:36px!important;white-space:normal!important;word-break:break-word!important;overflow-wrap:break-word!important;line-height:1.4!important;text-align:left!important;padding:6px 16px!important}.mat-mdc-cell .mat-mdc-outlined-button .mdc-button__label,.mat-mdc-cell .mat-mdc-button-base:not(.mat-mdc-icon-button) .mdc-button__label{white-space:normal!important;word-break:break-word!important;overflow-wrap:break-word!important}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i7$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "component", type: CheckComponent, selector: "spa-check", inputs: ["readonly", "display", "value", "infoMessage"], outputs: ["valueChange", "click", "check", "uncheck", "infoClick"] }, { kind: "component", type: SelectLiteComponent, selector: "spa-select-lite" }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }, { kind: "pipe", type: i1.CurrencyPipe, name: "currency" }, { kind: "pipe", type: i1.DatePipe, name: "date" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None }); }
11198
11198
  }
11199
11199
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: TableRowComponent, decorators: [{
11200
11200
  type: Component,
11201
- args: [{ selector: 'app-table-row', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<ng-container [ngSwitch]=\"column.type\">\r\n <ng-container *ngSwitchCase=\"'checkbox'\">\r\n <spa-check [value]=\"row[column.name]\" [readonly]=\"true\"></spa-check>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'select'\">\r\n <spa-select-lite [options]=\"column.options\" [optionDisplay]=\"column.optionDisplay\" [optionValue]=\"column.optionValue\" [(value)]=\"row[column.name]\" width=\"90%\"></spa-select-lite>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'chip'\">\r\n <button mat-stroked-button (click)=\"onColumnClick(column, row)\" [ngStyle]=\"{'background-color': vm.color, 'color': 'rgba(0, 0, 0, 0.87)', 'border': 'none'}\">{{row[column.name]}}</button>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'icon'\">\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'date'\">\r\n {{row[column.name] | date : 'dd/MM/yyyy'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'date-short'\">\r\n {{row[column.name] | date : 'd MMM'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'datetime'\">\r\n {{row[column.name] | date : 'dd/MM/yyyy HH:mm'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'datetimesec'\">\r\n {{row[column.name] | date : 'dd/MM/yyyy HH:mm:ss'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'money'\">\r\n <label [ngStyle]=\"{'color': vm.color }\">{{row[column.name] | currency:'':''}}</label>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'button'\">\r\n <button mat-stroked-button (click)=\"onColumnClick(column, row)\" [ngStyle]=\"{'color': vm.color}\" >{{row[column.name]}}</button>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchDefault>\r\n <label [ngStyle]=\"{'color': vm.color }\">\r\n <ng-container *ngIf=\"column.type === 'number'\">\r\n {{row[column.name] | number:'1.0-2'}}\r\n </ng-container>\r\n <ng-container *ngIf=\"column.type !== 'number'\">\r\n {{vm.text}}\r\n </ng-container>\r\n </label>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.truncated\" matTooltip='Show more' matTooltipPosition=\"above\" (click)=\"showBanner(row[column.name])\">more_horiz</mat-icon>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n</ng-container>\r\n", styles: [".top{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between;align-items:center;margin-bottom:10px;margin-top:10px}.mat-mini-fab{width:32px;height:32px}.mat-mini-fab mat-icon{font-size:16px;margin-top:-3px}.mat-icon-button{width:32px;height:32px}.mat-icon-button mat-icon{font-size:20px;margin-top:-7px}.col-icon{margin-left:10px;vertical-align:middle}.title{margin-top:10px;font-size:larger;font-weight:300}.make-gray{background-color:#e5e5e5}.right-padding{padding-right:10px}.action-buttons-container{display:flex;justify-content:flex-end;align-items:center}.refreshIcon{font-size:22px!important;margin-top:-7px!important}.mat-mdc-cell .mat-mdc-outlined-button,.mat-mdc-cell .mat-mdc-button-base:not(.mat-mdc-icon-button){height:auto!important;min-height:36px!important;white-space:normal!important;word-break:break-word!important;overflow-wrap:break-word!important;line-height:1.4!important;text-align:left!important;padding:6px 16px!important}.mat-mdc-cell .mat-mdc-outlined-button .mdc-button__label,.mat-mdc-cell .mat-mdc-button-base:not(.mat-mdc-icon-button) .mdc-button__label{white-space:normal!important;word-break:break-word!important;overflow-wrap:break-word!important}\n"] }]
11201
+ args: [{ selector: 'app-table-row', encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<ng-container [ngSwitch]=\"column.type\">\r\n <ng-container *ngSwitchCase=\"'checkbox'\">\r\n <spa-check [value]=\"row[column.name]\" [readonly]=\"true\"></spa-check>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'select'\">\r\n <spa-select-lite [options]=\"column.options\" [optionDisplay]=\"column.optionDisplay\" [optionValue]=\"column.optionValue\" [(value)]=\"row[column.name]\" width=\"90%\"></spa-select-lite>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'chip'\">\r\n <button mat-stroked-button (click)=\"onColumnClick(column, row)\" [ngStyle]=\"{'background-color': vm.color || '#eceff1', 'color': 'rgba(0, 0, 0, 0.87)', 'border': 'none'}\">{{row[column.name]}}</button>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'icon'\">\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'date'\">\r\n {{row[column.name] | date : 'dd/MM/yyyy'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'date-short'\">\r\n {{row[column.name] | date : 'd MMM'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'datetime'\">\r\n {{row[column.name] | date : 'dd/MM/yyyy HH:mm'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'datetimesec'\">\r\n {{row[column.name] | date : 'dd/MM/yyyy HH:mm:ss'}}\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'money'\">\r\n <label [ngStyle]=\"{'color': vm.color }\">{{row[column.name] | currency:'':''}}</label>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchCase=\"'button'\">\r\n <button mat-stroked-button (click)=\"onColumnClick(column, row)\" [ngStyle]=\"{'color': vm.color}\" >{{row[column.name]}}</button>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n\r\n <ng-container *ngSwitchDefault>\r\n <label [ngStyle]=\"{'color': vm.color }\">\r\n <ng-container *ngIf=\"column.type === 'number'\">\r\n {{row[column.name] | number:'1.0-2'}}\r\n </ng-container>\r\n <ng-container *ngIf=\"column.type !== 'number'\">\r\n {{vm.text}}\r\n </ng-container>\r\n </label>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.truncated\" matTooltip='Show more' matTooltipPosition=\"above\" (click)=\"showBanner(row[column.name])\">more_horiz</mat-icon>\r\n <mat-icon class=\"col-icon\" *ngIf=\"vm.showColumnIcon\" [matTooltip]=\"row[column.icon.tipField] ?? column.icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[column.icon.tipField])\" [style.color]=\"column.icon?.color\">{{column.icon.name }}</mat-icon>\r\n <ng-container *ngFor=\"let icon of vm.visibleIcons\">\r\n <mat-icon class=\"col-icon\" [matTooltip]=\"row[icon.tipField] ?? icon?.tip\" matTooltipPosition=\"above\" (click)=\"showBanner(row[icon.tipField])\" [style.color]=\"icon.color\">{{icon.name }}</mat-icon>\r\n </ng-container>\r\n </ng-container>\r\n</ng-container>\r\n", styles: [".top{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between;align-items:center;margin-bottom:10px;margin-top:10px}.mat-mini-fab{width:32px;height:32px}.mat-mini-fab mat-icon{font-size:16px;margin-top:-3px}.mat-icon-button{width:32px;height:32px}.mat-icon-button mat-icon{font-size:20px;margin-top:-7px}.col-icon{margin-left:10px;vertical-align:middle}.title{margin-top:10px;font-size:larger;font-weight:300}.make-gray{background-color:#e5e5e5}.right-padding{padding-right:10px}.action-buttons-container{display:flex;justify-content:flex-end;align-items:center}.refreshIcon{font-size:22px!important;margin-top:-7px!important}.mat-mdc-cell .mat-mdc-outlined-button,.mat-mdc-cell .mat-mdc-button-base:not(.mat-mdc-icon-button){height:auto!important;min-height:36px!important;white-space:normal!important;word-break:break-word!important;overflow-wrap:break-word!important;line-height:1.4!important;text-align:left!important;padding:6px 16px!important}.mat-mdc-cell .mat-mdc-outlined-button .mdc-button__label,.mat-mdc-cell .mat-mdc-button-base:not(.mat-mdc-icon-button) .mdc-button__label{white-space:normal!important;word-break:break-word!important;overflow-wrap:break-word!important}\n"] }]
11202
11202
  }], ctorParameters: () => [{ type: ButtonService }], propDecorators: { column: [{
11203
11203
  type: Input
11204
11204
  }], row: [{
@@ -11314,11 +11314,11 @@ class TableActionComponent {
11314
11314
  : [];
11315
11315
  }
11316
11316
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: TableActionComponent, deps: [{ token: ButtonService }], target: i0.ɵɵFactoryTarget.Component }); }
11317
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: TableActionComponent, isStandalone: false, selector: "app-table-action", inputs: { displayedButtons: "displayedButtons", config: "config", row: "row", smallScreen: "smallScreen" }, outputs: { actionClick: "actionClick" }, usesOnChanges: true, ngImport: i0, template: "<!-- TS-2: iterate precomputed view-models; no per-cell method calls or getters during change detection -->\n<ng-container *ngFor=\"let vm of visibleVMs\">\n\n <ng-container *ngIf=\"vm.name != 'create' && vm.visible\">\n <button mat-icon-button [matTooltip]=\"vm.button.tip ?? vm.name | titlecase\" matTooltipPosition=\"above\" style=\"margin-right:5px\" [disabled]=\"vm.disabled\" (click)=\"onActionClick(vm.name, row)\">\n <mat-icon [style.color]=\"vm.color\">{{vm.icon}}</mat-icon>\n </button>\n </ng-container>\n\n <ng-container *ngIf=\"vm.name != 'create' && !vm.visible && config?.collapseButtons === false\">\n <label style=\"margin-right: 35px;\"></label>\n </ng-container>\n\n</ng-container>\n\n<button *ngIf=\"overflowVMs.length > 0\" mat-icon-button [matMenuTriggerFor]=\"menu\" [matTooltip]=\"'More actions'\" matTooltipPosition=\"above\">\n <mat-icon>more_vert</mat-icon>\n</button>\n\n<mat-menu #menu=\"matMenu\">\n <ng-container *ngFor=\"let vm of overflowVMs\">\n <button *ngIf=\"vm.visible\" mat-menu-item [disabled]=\"vm.disabled\" (click)=\"onActionClick(vm.name, row)\">\n <mat-icon [style.color]=\"vm.color\">\n {{vm.icon}}\n </mat-icon>\n <span>{{vm.button.tip ?? vm.name | titlecase}}</span>\n </button>\n </ng-container>\n</mat-menu>\n", styles: [".top{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between;align-items:center;margin-bottom:10px;margin-top:10px}.mat-mini-fab{width:32px;height:32px}.mat-mini-fab mat-icon{font-size:16px;margin-top:-3px}.mat-icon-button{width:32px;height:32px}.mat-icon-button mat-icon{font-size:20px;margin-top:-7px}.col-icon{margin-left:10px}.title{margin-top:10px;font-size:larger;font-weight:300}.make-gray{background-color:#e5e5e5}.right-padding{padding-right:10px}.action-buttons-container{display:flex;justify-content:flex-end;align-items:center}.refreshIcon{font-size:22px!important;margin-top:-7px!important}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i4$4.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i4$4.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i4$4.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i7$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "pipe", type: i1.TitleCasePipe, name: "titlecase" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11317
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: TableActionComponent, isStandalone: false, selector: "app-table-action", inputs: { displayedButtons: "displayedButtons", config: "config", row: "row", smallScreen: "smallScreen" }, outputs: { actionClick: "actionClick" }, usesOnChanges: true, ngImport: i0, template: "<!-- TS-2: iterate precomputed view-models; no per-cell method calls or getters during change detection -->\r\n<ng-container *ngFor=\"let vm of visibleVMs\">\r\n\r\n <ng-container *ngIf=\"vm.name != 'create' && vm.visible\">\r\n <button mat-icon-button [matTooltip]=\"vm.button.tip ?? vm.name | titlecase\" matTooltipPosition=\"above\" style=\"margin-right:5px\" [disabled]=\"vm.disabled\" (click)=\"onActionClick(vm.name, row)\">\r\n <mat-icon [style.color]=\"vm.color\">{{vm.icon}}</mat-icon>\r\n </button>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"vm.name != 'create' && !vm.visible && config?.collapseButtons === false\">\r\n <label style=\"margin-right: 35px;\"></label>\r\n </ng-container>\r\n\r\n</ng-container>\r\n\r\n<button *ngIf=\"overflowVMs.length > 0\" mat-icon-button [matMenuTriggerFor]=\"menu\" [matTooltip]=\"'More actions'\" matTooltipPosition=\"above\">\r\n <mat-icon>more_vert</mat-icon>\r\n</button>\r\n\r\n<mat-menu #menu=\"matMenu\">\r\n <ng-container *ngFor=\"let vm of overflowVMs\">\r\n <button *ngIf=\"vm.visible\" mat-menu-item [disabled]=\"vm.disabled\" (click)=\"onActionClick(vm.name, row)\">\r\n <mat-icon [style.color]=\"vm.color\">\r\n {{vm.icon}}\r\n </mat-icon>\r\n <span>{{vm.button.tip ?? vm.name | titlecase}}</span>\r\n </button>\r\n </ng-container>\r\n</mat-menu>\r\n", styles: [".top{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between;align-items:center;margin-bottom:10px;margin-top:10px}.mat-mini-fab{width:32px;height:32px}.mat-mini-fab mat-icon{font-size:16px;margin-top:-3px}.mat-icon-button{width:32px;height:32px}.mat-icon-button mat-icon{font-size:20px;margin-top:-7px}.col-icon{margin-left:10px}.title{margin-top:10px;font-size:larger;font-weight:300}.make-gray{background-color:#e5e5e5}.right-padding{padding-right:10px}.action-buttons-container{display:flex;justify-content:flex-end;align-items:center}.refreshIcon{font-size:22px!important;margin-top:-7px!important}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i4$4.MatMenu, selector: "mat-menu", inputs: ["backdropClass", "aria-label", "aria-labelledby", "aria-describedby", "xPosition", "yPosition", "overlapTrigger", "hasBackdrop", "class", "classList"], outputs: ["closed", "close"], exportAs: ["matMenu"] }, { kind: "component", type: i4$4.MatMenuItem, selector: "[mat-menu-item]", inputs: ["role", "disabled", "disableRipple"], exportAs: ["matMenuItem"] }, { kind: "directive", type: i4$4.MatMenuTrigger, selector: "[mat-menu-trigger-for], [matMenuTriggerFor]", inputs: ["mat-menu-trigger-for", "matMenuTriggerFor", "matMenuTriggerData", "matMenuTriggerRestoreFocus"], outputs: ["menuOpened", "onMenuOpen", "menuClosed", "onMenuClose"], exportAs: ["matMenuTrigger"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i7$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }, { kind: "pipe", type: i1.TitleCasePipe, name: "titlecase" }], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
11318
11318
  }
11319
11319
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: TableActionComponent, decorators: [{
11320
11320
  type: Component,
11321
- args: [{ selector: 'app-table-action', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<!-- TS-2: iterate precomputed view-models; no per-cell method calls or getters during change detection -->\n<ng-container *ngFor=\"let vm of visibleVMs\">\n\n <ng-container *ngIf=\"vm.name != 'create' && vm.visible\">\n <button mat-icon-button [matTooltip]=\"vm.button.tip ?? vm.name | titlecase\" matTooltipPosition=\"above\" style=\"margin-right:5px\" [disabled]=\"vm.disabled\" (click)=\"onActionClick(vm.name, row)\">\n <mat-icon [style.color]=\"vm.color\">{{vm.icon}}</mat-icon>\n </button>\n </ng-container>\n\n <ng-container *ngIf=\"vm.name != 'create' && !vm.visible && config?.collapseButtons === false\">\n <label style=\"margin-right: 35px;\"></label>\n </ng-container>\n\n</ng-container>\n\n<button *ngIf=\"overflowVMs.length > 0\" mat-icon-button [matMenuTriggerFor]=\"menu\" [matTooltip]=\"'More actions'\" matTooltipPosition=\"above\">\n <mat-icon>more_vert</mat-icon>\n</button>\n\n<mat-menu #menu=\"matMenu\">\n <ng-container *ngFor=\"let vm of overflowVMs\">\n <button *ngIf=\"vm.visible\" mat-menu-item [disabled]=\"vm.disabled\" (click)=\"onActionClick(vm.name, row)\">\n <mat-icon [style.color]=\"vm.color\">\n {{vm.icon}}\n </mat-icon>\n <span>{{vm.button.tip ?? vm.name | titlecase}}</span>\n </button>\n </ng-container>\n</mat-menu>\n", styles: [".top{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between;align-items:center;margin-bottom:10px;margin-top:10px}.mat-mini-fab{width:32px;height:32px}.mat-mini-fab mat-icon{font-size:16px;margin-top:-3px}.mat-icon-button{width:32px;height:32px}.mat-icon-button mat-icon{font-size:20px;margin-top:-7px}.col-icon{margin-left:10px}.title{margin-top:10px;font-size:larger;font-weight:300}.make-gray{background-color:#e5e5e5}.right-padding{padding-right:10px}.action-buttons-container{display:flex;justify-content:flex-end;align-items:center}.refreshIcon{font-size:22px!important;margin-top:-7px!important}\n"] }]
11321
+ args: [{ selector: 'app-table-action', changeDetection: ChangeDetectionStrategy.OnPush, standalone: false, template: "<!-- TS-2: iterate precomputed view-models; no per-cell method calls or getters during change detection -->\r\n<ng-container *ngFor=\"let vm of visibleVMs\">\r\n\r\n <ng-container *ngIf=\"vm.name != 'create' && vm.visible\">\r\n <button mat-icon-button [matTooltip]=\"vm.button.tip ?? vm.name | titlecase\" matTooltipPosition=\"above\" style=\"margin-right:5px\" [disabled]=\"vm.disabled\" (click)=\"onActionClick(vm.name, row)\">\r\n <mat-icon [style.color]=\"vm.color\">{{vm.icon}}</mat-icon>\r\n </button>\r\n </ng-container>\r\n\r\n <ng-container *ngIf=\"vm.name != 'create' && !vm.visible && config?.collapseButtons === false\">\r\n <label style=\"margin-right: 35px;\"></label>\r\n </ng-container>\r\n\r\n</ng-container>\r\n\r\n<button *ngIf=\"overflowVMs.length > 0\" mat-icon-button [matMenuTriggerFor]=\"menu\" [matTooltip]=\"'More actions'\" matTooltipPosition=\"above\">\r\n <mat-icon>more_vert</mat-icon>\r\n</button>\r\n\r\n<mat-menu #menu=\"matMenu\">\r\n <ng-container *ngFor=\"let vm of overflowVMs\">\r\n <button *ngIf=\"vm.visible\" mat-menu-item [disabled]=\"vm.disabled\" (click)=\"onActionClick(vm.name, row)\">\r\n <mat-icon [style.color]=\"vm.color\">\r\n {{vm.icon}}\r\n </mat-icon>\r\n <span>{{vm.button.tip ?? vm.name | titlecase}}</span>\r\n </button>\r\n </ng-container>\r\n</mat-menu>\r\n", styles: [".top{display:flex;flex-direction:row;flex-wrap:wrap;justify-content:space-between;align-items:center;margin-bottom:10px;margin-top:10px}.mat-mini-fab{width:32px;height:32px}.mat-mini-fab mat-icon{font-size:16px;margin-top:-3px}.mat-icon-button{width:32px;height:32px}.mat-icon-button mat-icon{font-size:20px;margin-top:-7px}.col-icon{margin-left:10px}.title{margin-top:10px;font-size:larger;font-weight:300}.make-gray{background-color:#e5e5e5}.right-padding{padding-right:10px}.action-buttons-container{display:flex;justify-content:flex-end;align-items:center}.refreshIcon{font-size:22px!important;margin-top:-7px!important}\n"] }]
11322
11322
  }], ctorParameters: () => [{ type: ButtonService }], propDecorators: { displayedButtons: [{
11323
11323
  type: Input
11324
11324
  }], config: [{
@@ -12396,58 +12396,58 @@ class InlineCellComponent {
12396
12396
  this.valueChange.emit({ field: this.field, value: this.data[this.field.name] });
12397
12397
  }
12398
12398
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: InlineCellComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
12399
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: InlineCellComponent, isStandalone: false, selector: "app-inline-cell", inputs: { field: "field", data: "data" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: `
12400
- <ng-container [ngSwitch]="field.type">
12401
-
12402
- <spa-number *ngSwitchCase="'number'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [suffix]="field.suffix"></spa-number>
12403
-
12404
- <spa-money *ngSwitchCase="'money'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [suffix]="field.suffix"></spa-money>
12405
-
12406
- <spa-check *ngSwitchCase="'checkbox'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()"></spa-check>
12407
-
12408
- <spa-date *ngSwitchCase="'date'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [min]="field.min" [max]="field.max"></spa-date>
12409
-
12410
- <spa-datetime *ngSwitchCase="'datetime'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [min]="field.min" [max]="field.max"></spa-datetime>
12411
-
12412
- <spa-select *ngSwitchCase="'select'" [display]="display" [nullable]="field.nullable" [options]="field.options" [masterOptions]="field.masterOptions" [masterField]="field.masterField"
12413
- [optionDisplay]="field.optionDisplay ?? 'name'" [optionValue]="field.optionValue ?? 'value'" [(value)]="data[field.name]" (valueChange)="cellChanged()"
12414
- [required]="field.required" [loadAction]="field.loadAction" [loadIDField]="field.loadIDField" [field]="field" [data]="data"></spa-select>
12415
-
12416
- <spa-email *ngSwitchCase="'email'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required"></spa-email>
12417
-
12418
- <spa-text-area *ngSwitchCase="'text-area'" [display]="display" [rows]="1" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [regex]="field.regex"></spa-text-area>
12419
-
12420
- <spa-text *ngSwitchDefault [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [suffix]="field.suffix" [regex]="field.regex"></spa-text>
12421
-
12422
- </ng-container>
12399
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: InlineCellComponent, isStandalone: false, selector: "app-inline-cell", inputs: { field: "field", data: "data" }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: `
12400
+ <ng-container [ngSwitch]="field.type">
12401
+
12402
+ <spa-number *ngSwitchCase="'number'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [suffix]="field.suffix"></spa-number>
12403
+
12404
+ <spa-money *ngSwitchCase="'money'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [suffix]="field.suffix"></spa-money>
12405
+
12406
+ <spa-check *ngSwitchCase="'checkbox'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()"></spa-check>
12407
+
12408
+ <spa-date *ngSwitchCase="'date'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [min]="field.min" [max]="field.max"></spa-date>
12409
+
12410
+ <spa-datetime *ngSwitchCase="'datetime'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [min]="field.min" [max]="field.max"></spa-datetime>
12411
+
12412
+ <spa-select *ngSwitchCase="'select'" [display]="display" [nullable]="field.nullable" [options]="field.options" [masterOptions]="field.masterOptions" [masterField]="field.masterField"
12413
+ [optionDisplay]="field.optionDisplay ?? 'name'" [optionValue]="field.optionValue ?? 'value'" [(value)]="data[field.name]" (valueChange)="cellChanged()"
12414
+ [required]="field.required" [loadAction]="field.loadAction" [loadIDField]="field.loadIDField" [field]="field" [data]="data"></spa-select>
12415
+
12416
+ <spa-email *ngSwitchCase="'email'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required"></spa-email>
12417
+
12418
+ <spa-text-area *ngSwitchCase="'text-area'" [display]="display" [rows]="1" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [regex]="field.regex"></spa-text-area>
12419
+
12420
+ <spa-text *ngSwitchDefault [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [suffix]="field.suffix" [regex]="field.regex"></spa-text>
12421
+
12422
+ </ng-container>
12423
12423
  `, isInline: true, styles: [":host{display:block;min-width:90px;padding-top:6px}\n"], dependencies: [{ kind: "directive", type: i1.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i1.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "directive", type: i1.NgSwitchDefault, selector: "[ngSwitchDefault]" }, { kind: "component", type: TextComponent, selector: "spa-text", inputs: ["appearance", "readonly", "hint", "display", "placeholder", "value", "format", "type", "width", "copyContent", "clearContent", "required", "min", "max", "regex", "suffix", "infoMessage"], outputs: ["valueChange", "leave", "enterPress"] }, { kind: "component", type: TextAreaComponent, selector: "spa-text-area", inputs: ["appearance", "readonly", "hint", "display", "placeholder", "value", "rows", "width", "copyContent", "clearContent", "required", "min", "max", "regex", "suffix", "infoMessage"], outputs: ["valueChange", "leave", "enterPress"] }, { kind: "component", type: CheckComponent, selector: "spa-check", inputs: ["readonly", "display", "value", "infoMessage"], outputs: ["valueChange", "click", "check", "uncheck", "infoClick"] }, { kind: "component", type: DateComponent, selector: "spa-date", inputs: ["required", "min", "max", "readonly", "hint", "value", "display", "placeholder", "width", "suffix", "infoMessage", "copyContent", "clearContent"], outputs: ["valueChange"] }, { kind: "component", type: DatetimeComponent, selector: "spa-datetime", inputs: ["display", "value", "readonly", "width", "min", "max", "suffix", "infoMessage", "copyContent", "clearContent"], outputs: ["valueChange"] }, { kind: "component", type: SelectComponent, selector: "spa-select", inputs: ["detailsConfig"] }, { kind: "component", type: MoneyComponent, selector: "spa-money", inputs: ["readonly", "hint", "display", "placeholder", "value", "width", "currency", "required", "min", "max", "infoMessage", "copyContent", "clearContent", "suffix"], outputs: ["valueChange", "leave", "enterPress", "infoClick"] }, { kind: "component", type: NumberComponent, selector: "spa-number", inputs: ["readonly", "hint", "display", "placeholder", "value", "width", "required", "min", "max", "step", "suffix", "infoMessage", "copyContent", "clearContent"], outputs: ["valueChange", "leave", "enterPress", "infoClick"] }, { kind: "component", type: EmailComponent, selector: "spa-email", inputs: ["display", "value", "readonly", "required", "hint", "suffix", "infoMessage", "copyContent", "clearContent", "options", "optionValue"], outputs: ["valueChange"] }] }); }
12424
12424
  }
12425
12425
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: InlineCellComponent, decorators: [{
12426
12426
  type: Component,
12427
- args: [{ selector: 'app-inline-cell', template: `
12428
- <ng-container [ngSwitch]="field.type">
12429
-
12430
- <spa-number *ngSwitchCase="'number'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [suffix]="field.suffix"></spa-number>
12431
-
12432
- <spa-money *ngSwitchCase="'money'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [suffix]="field.suffix"></spa-money>
12433
-
12434
- <spa-check *ngSwitchCase="'checkbox'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()"></spa-check>
12435
-
12436
- <spa-date *ngSwitchCase="'date'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [min]="field.min" [max]="field.max"></spa-date>
12437
-
12438
- <spa-datetime *ngSwitchCase="'datetime'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [min]="field.min" [max]="field.max"></spa-datetime>
12439
-
12440
- <spa-select *ngSwitchCase="'select'" [display]="display" [nullable]="field.nullable" [options]="field.options" [masterOptions]="field.masterOptions" [masterField]="field.masterField"
12441
- [optionDisplay]="field.optionDisplay ?? 'name'" [optionValue]="field.optionValue ?? 'value'" [(value)]="data[field.name]" (valueChange)="cellChanged()"
12442
- [required]="field.required" [loadAction]="field.loadAction" [loadIDField]="field.loadIDField" [field]="field" [data]="data"></spa-select>
12443
-
12444
- <spa-email *ngSwitchCase="'email'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required"></spa-email>
12445
-
12446
- <spa-text-area *ngSwitchCase="'text-area'" [display]="display" [rows]="1" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [regex]="field.regex"></spa-text-area>
12447
-
12448
- <spa-text *ngSwitchDefault [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [suffix]="field.suffix" [regex]="field.regex"></spa-text>
12449
-
12450
- </ng-container>
12427
+ args: [{ selector: 'app-inline-cell', template: `
12428
+ <ng-container [ngSwitch]="field.type">
12429
+
12430
+ <spa-number *ngSwitchCase="'number'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [suffix]="field.suffix"></spa-number>
12431
+
12432
+ <spa-money *ngSwitchCase="'money'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [suffix]="field.suffix"></spa-money>
12433
+
12434
+ <spa-check *ngSwitchCase="'checkbox'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()"></spa-check>
12435
+
12436
+ <spa-date *ngSwitchCase="'date'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [min]="field.min" [max]="field.max"></spa-date>
12437
+
12438
+ <spa-datetime *ngSwitchCase="'datetime'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [min]="field.min" [max]="field.max"></spa-datetime>
12439
+
12440
+ <spa-select *ngSwitchCase="'select'" [display]="display" [nullable]="field.nullable" [options]="field.options" [masterOptions]="field.masterOptions" [masterField]="field.masterField"
12441
+ [optionDisplay]="field.optionDisplay ?? 'name'" [optionValue]="field.optionValue ?? 'value'" [(value)]="data[field.name]" (valueChange)="cellChanged()"
12442
+ [required]="field.required" [loadAction]="field.loadAction" [loadIDField]="field.loadIDField" [field]="field" [data]="data"></spa-select>
12443
+
12444
+ <spa-email *ngSwitchCase="'email'" [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required"></spa-email>
12445
+
12446
+ <spa-text-area *ngSwitchCase="'text-area'" [display]="display" [rows]="1" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [regex]="field.regex"></spa-text-area>
12447
+
12448
+ <spa-text *ngSwitchDefault [display]="display" [(value)]="data[field.name]" (valueChange)="cellChanged()" [required]="field.required" [min]="field.min" [max]="field.max" [suffix]="field.suffix" [regex]="field.regex"></spa-text>
12449
+
12450
+ </ng-container>
12451
12451
  `, standalone: false, styles: [":host{display:block;min-width:90px;padding-top:6px}\n"] }]
12452
12452
  }], propDecorators: { field: [{
12453
12453
  type: Input
@@ -14584,7 +14584,7 @@ class ToastComponent {
14584
14584
  this.subs.forEach(s => s.unsubscribe());
14585
14585
  }
14586
14586
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ToastComponent, deps: [{ token: SignalRService }], target: i0.ɵɵFactoryTarget.Component }); }
14587
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ToastComponent, isStandalone: false, selector: "spa-toast", ngImport: i0, template: "<!-- Changed: Fixed-position toast container, top-right corner with cascading layout -->\n<div class=\"toast-container\">\n <div *ngFor=\"let toast of toasts; trackBy: trackToast\"\n class=\"toast-item\"\n [@toastAnim]\n (click)=\"dismiss(toast.id)\">\n <div class=\"toast-accent\" [style.background-color]=\"toast.color\"></div>\n <mat-icon class=\"toast-icon\" [style.color]=\"toast.color\">{{toast.icon}}</mat-icon>\n <div class=\"toast-content\">\n <span class=\"toast-category\">{{toast.category}}</span>\n <span class=\"toast-message\">{{toast.message}}</span>\n </div>\n <mat-icon class=\"toast-close\">close</mat-icon>\n </div>\n</div>\n", styles: [".toast-container{position:fixed;top:72px;right:16px;z-index:9999;display:flex;flex-direction:column;gap:8px;pointer-events:none}.toast-item{display:flex;align-items:center;min-width:280px;max-width:360px;padding:10px 14px;border-radius:8px;background:#fffffff2;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);box-shadow:0 4px 20px #0000001f,0 1px 4px #00000014;cursor:pointer;pointer-events:auto;overflow:hidden;position:relative}.toast-item:hover{box-shadow:0 6px 24px #00000029}.toast-accent{position:absolute;left:0;top:0;bottom:0;width:4px;border-radius:8px 0 0 8px}.toast-icon{font-size:20px;width:20px;height:20px;margin-left:8px;margin-right:10px;flex-shrink:0}.toast-content{flex:1;display:flex;flex-direction:column;min-width:0}.toast-category{font-size:10px;font-weight:600;text-transform:uppercase;color:#999;letter-spacing:.5px;line-height:1.2}.toast-message{font-size:13px;font-weight:500;color:#333;line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.toast-close{font-size:16px;width:16px;height:16px;color:#bbb;margin-left:8px;flex-shrink:0}.toast-item:hover .toast-close{color:#666}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], animations: [
14587
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ToastComponent, isStandalone: false, selector: "spa-toast", ngImport: i0, template: "<!-- Changed: Fixed-position toast container, top-right corner with cascading layout -->\r\n<div class=\"toast-container\">\r\n <div *ngFor=\"let toast of toasts; trackBy: trackToast\"\r\n class=\"toast-item\"\r\n [@toastAnim]\r\n (click)=\"dismiss(toast.id)\">\r\n <div class=\"toast-accent\" [style.background-color]=\"toast.color\"></div>\r\n <mat-icon class=\"toast-icon\" [style.color]=\"toast.color\">{{toast.icon}}</mat-icon>\r\n <div class=\"toast-content\">\r\n <span class=\"toast-category\">{{toast.category}}</span>\r\n <span class=\"toast-message\">{{toast.message}}</span>\r\n </div>\r\n <mat-icon class=\"toast-close\">close</mat-icon>\r\n </div>\r\n</div>\r\n", styles: [".toast-container{position:fixed;top:72px;right:16px;z-index:9999;display:flex;flex-direction:column;gap:8px;pointer-events:none}.toast-item{display:flex;align-items:center;min-width:280px;max-width:360px;padding:10px 14px;border-radius:8px;background:#fffffff2;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);box-shadow:0 4px 20px #0000001f,0 1px 4px #00000014;cursor:pointer;pointer-events:auto;overflow:hidden;position:relative}.toast-item:hover{box-shadow:0 6px 24px #00000029}.toast-accent{position:absolute;left:0;top:0;bottom:0;width:4px;border-radius:8px 0 0 8px}.toast-icon{font-size:20px;width:20px;height:20px;margin-left:8px;margin-right:10px;flex-shrink:0}.toast-content{flex:1;display:flex;flex-direction:column;min-width:0}.toast-category{font-size:10px;font-weight:600;text-transform:uppercase;color:#999;letter-spacing:.5px;line-height:1.2}.toast-message{font-size:13px;font-weight:500;color:#333;line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.toast-close{font-size:16px;width:16px;height:16px;color:#bbb;margin-left:8px;flex-shrink:0}.toast-item:hover .toast-close{color:#666}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }], animations: [
14588
14588
  trigger('toastAnim', [
14589
14589
  transition(':enter', [
14590
14590
  style({ opacity: 0, transform: 'translateX(100%)' }),
@@ -14608,7 +14608,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
14608
14608
  animate('250ms cubic-bezier(0.4, 0, 0.2, 1)', style({ opacity: 0, transform: 'translateX(100%)' }))
14609
14609
  ])
14610
14610
  ])
14611
- ], template: "<!-- Changed: Fixed-position toast container, top-right corner with cascading layout -->\n<div class=\"toast-container\">\n <div *ngFor=\"let toast of toasts; trackBy: trackToast\"\n class=\"toast-item\"\n [@toastAnim]\n (click)=\"dismiss(toast.id)\">\n <div class=\"toast-accent\" [style.background-color]=\"toast.color\"></div>\n <mat-icon class=\"toast-icon\" [style.color]=\"toast.color\">{{toast.icon}}</mat-icon>\n <div class=\"toast-content\">\n <span class=\"toast-category\">{{toast.category}}</span>\n <span class=\"toast-message\">{{toast.message}}</span>\n </div>\n <mat-icon class=\"toast-close\">close</mat-icon>\n </div>\n</div>\n", styles: [".toast-container{position:fixed;top:72px;right:16px;z-index:9999;display:flex;flex-direction:column;gap:8px;pointer-events:none}.toast-item{display:flex;align-items:center;min-width:280px;max-width:360px;padding:10px 14px;border-radius:8px;background:#fffffff2;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);box-shadow:0 4px 20px #0000001f,0 1px 4px #00000014;cursor:pointer;pointer-events:auto;overflow:hidden;position:relative}.toast-item:hover{box-shadow:0 6px 24px #00000029}.toast-accent{position:absolute;left:0;top:0;bottom:0;width:4px;border-radius:8px 0 0 8px}.toast-icon{font-size:20px;width:20px;height:20px;margin-left:8px;margin-right:10px;flex-shrink:0}.toast-content{flex:1;display:flex;flex-direction:column;min-width:0}.toast-category{font-size:10px;font-weight:600;text-transform:uppercase;color:#999;letter-spacing:.5px;line-height:1.2}.toast-message{font-size:13px;font-weight:500;color:#333;line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.toast-close{font-size:16px;width:16px;height:16px;color:#bbb;margin-left:8px;flex-shrink:0}.toast-item:hover .toast-close{color:#666}\n"] }]
14611
+ ], template: "<!-- Changed: Fixed-position toast container, top-right corner with cascading layout -->\r\n<div class=\"toast-container\">\r\n <div *ngFor=\"let toast of toasts; trackBy: trackToast\"\r\n class=\"toast-item\"\r\n [@toastAnim]\r\n (click)=\"dismiss(toast.id)\">\r\n <div class=\"toast-accent\" [style.background-color]=\"toast.color\"></div>\r\n <mat-icon class=\"toast-icon\" [style.color]=\"toast.color\">{{toast.icon}}</mat-icon>\r\n <div class=\"toast-content\">\r\n <span class=\"toast-category\">{{toast.category}}</span>\r\n <span class=\"toast-message\">{{toast.message}}</span>\r\n </div>\r\n <mat-icon class=\"toast-close\">close</mat-icon>\r\n </div>\r\n</div>\r\n", styles: [".toast-container{position:fixed;top:72px;right:16px;z-index:9999;display:flex;flex-direction:column;gap:8px;pointer-events:none}.toast-item{display:flex;align-items:center;min-width:280px;max-width:360px;padding:10px 14px;border-radius:8px;background:#fffffff2;-webkit-backdrop-filter:blur(12px);backdrop-filter:blur(12px);box-shadow:0 4px 20px #0000001f,0 1px 4px #00000014;cursor:pointer;pointer-events:auto;overflow:hidden;position:relative}.toast-item:hover{box-shadow:0 6px 24px #00000029}.toast-accent{position:absolute;left:0;top:0;bottom:0;width:4px;border-radius:8px 0 0 8px}.toast-icon{font-size:20px;width:20px;height:20px;margin-left:8px;margin-right:10px;flex-shrink:0}.toast-content{flex:1;display:flex;flex-direction:column;min-width:0}.toast-category{font-size:10px;font-weight:600;text-transform:uppercase;color:#999;letter-spacing:.5px;line-height:1.2}.toast-message{font-size:13px;font-weight:500;color:#333;line-height:1.3;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.toast-close{font-size:16px;width:16px;height:16px;color:#bbb;margin-left:8px;flex-shrink:0}.toast-item:hover .toast-close{color:#666}\n"] }]
14612
14612
  }], ctorParameters: () => [{ type: SignalRService }] });
14613
14613
 
14614
14614
  // Floating chat widget component for in-app Agent (renamed from AssistantComponent)
@@ -14717,7 +14717,7 @@ class AgentComponent {
14717
14717
  }
14718
14718
  }
14719
14719
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: AgentComponent, deps: [{ token: AgentService }, { token: SignalRService }], target: i0.ɵɵFactoryTarget.Component }); }
14720
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: AgentComponent, isStandalone: false, selector: "spa-agent", host: { listeners: { "document:click": "onDocumentClick($event)" } }, viewQueries: [{ propertyName: "messageContainer", first: true, predicate: ["messageContainer"], descendants: true }, { propertyName: "messageInput", first: true, predicate: ["messageInput"], descendants: true }], ngImport: i0, template: "<!-- Floating chat widget for in-app Agent (renamed from Assistant) -->\n\n<!-- FAB toggle button -->\n<button mat-fab class=\"agent-fab\" (click)=\"toggleChat()\" [matTooltip]=\"agentName\">\n <mat-icon>{{ isOpen ? 'close' : 'chat_bubble' }}</mat-icon>\n</button>\n\n<!-- Chat window -->\n<div class=\"agent-window\" *ngIf=\"isOpen\" @slideUp>\n\n <!-- Header with online status -->\n <div class=\"agent-header\">\n <div class=\"header-info\">\n <mat-icon class=\"header-icon\">smart_toy</mat-icon>\n <div class=\"header-text\">\n <span class=\"header-title\">{{ agentName }}</span>\n <span class=\"status-badge\" [class.online]=\"isOnline\" [class.offline]=\"!isOnline\">\n <span class=\"status-dot\"></span>\n {{ isOnline ? 'Online' : 'Offline' }}\n </span>\n </div>\n </div>\n <div class=\"header-actions\">\n <button mat-icon-button matTooltip=\"New conversation\" (click)=\"newConversation()\">\n <mat-icon>add_comment</mat-icon>\n </button>\n <button mat-icon-button matTooltip=\"Close\" (click)=\"toggleChat()\">\n <mat-icon>remove</mat-icon>\n </button>\n </div>\n </div>\n\n <!-- Messages area -->\n <div class=\"agent-messages\" #messageContainer>\n\n <!-- Welcome experience (show when no messages) -->\n <div class=\"agent-greeting\" *ngIf=\"messages.length === 0\">\n <mat-icon class=\"greeting-icon\">smart_toy</mat-icon>\n <p class=\"greeting-text\">{{ greeting }}</p>\n\n <!-- Three capability pillars -->\n <div class=\"capability-pillars\">\n\n <!-- Pillar 1: Navigate & Inquire -->\n <div class=\"pillar\" (click)=\"sendSuggested('How do I post a transaction?')\">\n <div class=\"pillar-header\">\n <mat-icon class=\"pillar-icon\">explore</mat-icon>\n <span class=\"pillar-title\">Ask & Navigate</span>\n </div>\n <p class=\"pillar-desc\">Ask how to do things or find features in the app</p>\n <span class=\"pillar-example\">\"How do I post a transaction?\"</span>\n </div>\n\n <!-- Pillar 2: Create & Modify -->\n <div class=\"pillar\" (click)=\"sendSuggested('Create a new customer')\">\n <div class=\"pillar-header\">\n <mat-icon class=\"pillar-icon\">edit_note</mat-icon>\n <span class=\"pillar-title\">Create & Modify</span>\n </div>\n <p class=\"pillar-desc\">Add, edit, or delete records using natural language</p>\n <span class=\"pillar-example\">\"Create a new customer called Acme Ltd\"</span>\n </div>\n\n <!-- Pillar 3: Get Information -->\n <div class=\"pillar\" (click)=\"sendSuggested('List all accounts')\">\n <div class=\"pillar-header\">\n <mat-icon class=\"pillar-icon\">search</mat-icon>\n <span class=\"pillar-title\">Get Information</span>\n </div>\n <p class=\"pillar-desc\">Retrieve details, list records, or get summaries</p>\n <span class=\"pillar-example\">\"Show me the details of Customer A\"</span>\n </div>\n\n </div>\n </div>\n\n <!-- Message bubbles -->\n <div *ngFor=\"let msg of messages; trackBy: trackMessage\" class=\"message-row\" [class.user-row]=\"msg.role === 'user'\" [class.agent-row]=\"msg.role === 'assistant'\">\n <div class=\"message-bubble\" [class.user-bubble]=\"msg.role === 'user'\" [class.agent-bubble]=\"msg.role === 'assistant'\">\n {{ msg.content }}\n </div>\n </div>\n\n <!-- Typing indicator -->\n <div class=\"message-row agent-row\" *ngIf=\"isTyping\">\n <div class=\"message-bubble agent-bubble typing-bubble\">\n <span class=\"typing-dot\"></span>\n <span class=\"typing-dot\"></span>\n <span class=\"typing-dot\"></span>\n </div>\n </div>\n\n </div>\n\n <!-- Input area -->\n <div class=\"agent-input\">\n <mat-form-field appearance=\"outline\" class=\"input-field\">\n <input matInput #messageInput placeholder=\"Type a message...\" [(ngModel)]=\"inputText\" (keydown)=\"onKeydown($event)\" autocomplete=\"off\" />\n </mat-form-field>\n <button mat-icon-button color=\"primary\" class=\"send-btn\" (click)=\"sendMessage()\" [disabled]=\"!inputText.trim()\">\n <mat-icon>send</mat-icon>\n </button>\n </div>\n\n</div>\n", styles: [".agent-fab{position:fixed;bottom:24px;right:24px;z-index:1001;background:#1976d2;color:#fff}.agent-window{position:fixed;bottom:96px;right:24px;width:400px;height:580px;background:#fff;border-radius:16px;box-shadow:0 8px 32px #00000026;display:flex;flex-direction:column;z-index:1000;overflow:hidden}.agent-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;background:#1976d2;color:#fff;min-height:56px}.header-info{display:flex;align-items:center;gap:10px}.header-icon{font-size:24px;width:24px;height:24px}.header-text{display:flex;flex-direction:column;gap:1px}.header-title{font-size:16px;font-weight:500;line-height:1.2}.status-badge{display:flex;align-items:center;gap:4px;font-size:11px;font-weight:400;opacity:.9;line-height:1}.status-dot{width:7px;height:7px;border-radius:50%;display:inline-block}.status-badge.online .status-dot{background:#4caf50;box-shadow:0 0 4px #4caf5099}.status-badge.offline .status-dot{background:#9e9e9e}.header-actions{display:flex;gap:0}.header-actions button{color:#fff}.agent-messages{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:8px}.agent-greeting{display:flex;flex-direction:column;align-items:center;text-align:center;padding:20px 8px 8px;gap:10px}.greeting-icon{font-size:48px;width:48px;height:48px;color:#1976d2;opacity:.8}.greeting-text{font-size:15px;color:#424242;line-height:1.5;margin:0}.capability-pillars{display:flex;flex-direction:column;gap:10px;margin-top:8px;width:100%}.pillar{border:1px solid #e0e0e0;border-radius:12px;padding:12px 14px;text-align:left;cursor:pointer;transition:all .2s ease;background:#fafafa}.pillar:hover{border-color:#1976d2;background:#1976d20a;box-shadow:0 2px 8px #1976d21a}.pillar-header{display:flex;align-items:center;gap:8px;margin-bottom:4px}.pillar-icon{font-size:20px;width:20px;height:20px;color:#1976d2}.pillar-title{font-size:13px;font-weight:600;color:#212121}.pillar-desc{font-size:12px;color:#616161;margin:0 0 6px;line-height:1.4}.pillar-example{font-size:12px;color:#1976d2;font-style:italic;opacity:.85}.message-row{display:flex;max-width:85%}.user-row{align-self:flex-end}.agent-row{align-self:flex-start}.message-bubble{padding:10px 14px;border-radius:16px;font-size:14px;line-height:1.5;word-break:break-word;white-space:pre-wrap}.user-bubble{background:#1976d2;color:#fff;border-bottom-right-radius:4px}.agent-bubble{background:#f0f0f0;color:#212121;border-bottom-left-radius:4px}.typing-bubble{display:flex;align-items:center;gap:4px;padding:12px 18px}.typing-dot{width:8px;height:8px;border-radius:50%;background:#9e9e9e;animation:typingBounce 1.4s infinite ease-in-out}.typing-dot:nth-child(2){animation-delay:.2s}.typing-dot:nth-child(3){animation-delay:.4s}@keyframes typingBounce{0%,60%,to{transform:translateY(0);opacity:.4}30%{transform:translateY(-6px);opacity:1}}.agent-input{display:flex;align-items:center;padding:8px 12px;border-top:1px solid #e0e0e0;gap:4px}.input-field{flex:1}.input-field ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}.input-field ::ng-deep .mat-mdc-text-field-wrapper{padding:0 12px}.send-btn{margin-bottom:4px}@media (max-width: 600px){.agent-window{inset:0 0 auto;width:100%;height:100%;height:100dvh;height:var(--agent-vh, 100dvh);border-radius:0}.agent-fab{bottom:72px;right:16px}}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4$3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "directive", type: i7$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], animations: [
14720
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: AgentComponent, isStandalone: false, selector: "spa-agent", host: { listeners: { "document:click": "onDocumentClick($event)" } }, viewQueries: [{ propertyName: "messageContainer", first: true, predicate: ["messageContainer"], descendants: true }, { propertyName: "messageInput", first: true, predicate: ["messageInput"], descendants: true }], ngImport: i0, template: "<!-- Floating chat widget for in-app Agent (renamed from Assistant) -->\r\n\r\n<!-- FAB toggle button -->\r\n<button mat-fab class=\"agent-fab\" (click)=\"toggleChat()\" [matTooltip]=\"agentName\">\r\n <mat-icon>{{ isOpen ? 'close' : 'chat_bubble' }}</mat-icon>\r\n</button>\r\n\r\n<!-- Chat window -->\r\n<div class=\"agent-window\" *ngIf=\"isOpen\" @slideUp>\r\n\r\n <!-- Header with online status -->\r\n <div class=\"agent-header\">\r\n <div class=\"header-info\">\r\n <mat-icon class=\"header-icon\">smart_toy</mat-icon>\r\n <div class=\"header-text\">\r\n <span class=\"header-title\">{{ agentName }}</span>\r\n <span class=\"status-badge\" [class.online]=\"isOnline\" [class.offline]=\"!isOnline\">\r\n <span class=\"status-dot\"></span>\r\n {{ isOnline ? 'Online' : 'Offline' }}\r\n </span>\r\n </div>\r\n </div>\r\n <div class=\"header-actions\">\r\n <button mat-icon-button matTooltip=\"New conversation\" (click)=\"newConversation()\">\r\n <mat-icon>add_comment</mat-icon>\r\n </button>\r\n <button mat-icon-button matTooltip=\"Close\" (click)=\"toggleChat()\">\r\n <mat-icon>remove</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Messages area -->\r\n <div class=\"agent-messages\" #messageContainer>\r\n\r\n <!-- Welcome experience (show when no messages) -->\r\n <div class=\"agent-greeting\" *ngIf=\"messages.length === 0\">\r\n <mat-icon class=\"greeting-icon\">smart_toy</mat-icon>\r\n <p class=\"greeting-text\">{{ greeting }}</p>\r\n\r\n <!-- Three capability pillars -->\r\n <div class=\"capability-pillars\">\r\n\r\n <!-- Pillar 1: Navigate & Inquire -->\r\n <div class=\"pillar\" (click)=\"sendSuggested('How do I post a transaction?')\">\r\n <div class=\"pillar-header\">\r\n <mat-icon class=\"pillar-icon\">explore</mat-icon>\r\n <span class=\"pillar-title\">Ask & Navigate</span>\r\n </div>\r\n <p class=\"pillar-desc\">Ask how to do things or find features in the app</p>\r\n <span class=\"pillar-example\">\"How do I post a transaction?\"</span>\r\n </div>\r\n\r\n <!-- Pillar 2: Create & Modify -->\r\n <div class=\"pillar\" (click)=\"sendSuggested('Create a new customer')\">\r\n <div class=\"pillar-header\">\r\n <mat-icon class=\"pillar-icon\">edit_note</mat-icon>\r\n <span class=\"pillar-title\">Create & Modify</span>\r\n </div>\r\n <p class=\"pillar-desc\">Add, edit, or delete records using natural language</p>\r\n <span class=\"pillar-example\">\"Create a new customer called Acme Ltd\"</span>\r\n </div>\r\n\r\n <!-- Pillar 3: Get Information -->\r\n <div class=\"pillar\" (click)=\"sendSuggested('List all accounts')\">\r\n <div class=\"pillar-header\">\r\n <mat-icon class=\"pillar-icon\">search</mat-icon>\r\n <span class=\"pillar-title\">Get Information</span>\r\n </div>\r\n <p class=\"pillar-desc\">Retrieve details, list records, or get summaries</p>\r\n <span class=\"pillar-example\">\"Show me the details of Customer A\"</span>\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Message bubbles -->\r\n <div *ngFor=\"let msg of messages; trackBy: trackMessage\" class=\"message-row\" [class.user-row]=\"msg.role === 'user'\" [class.agent-row]=\"msg.role === 'assistant'\">\r\n <div class=\"message-bubble\" [class.user-bubble]=\"msg.role === 'user'\" [class.agent-bubble]=\"msg.role === 'assistant'\">\r\n {{ msg.content }}\r\n </div>\r\n </div>\r\n\r\n <!-- Typing indicator -->\r\n <div class=\"message-row agent-row\" *ngIf=\"isTyping\">\r\n <div class=\"message-bubble agent-bubble typing-bubble\">\r\n <span class=\"typing-dot\"></span>\r\n <span class=\"typing-dot\"></span>\r\n <span class=\"typing-dot\"></span>\r\n </div>\r\n </div>\r\n\r\n </div>\r\n\r\n <!-- Input area -->\r\n <div class=\"agent-input\">\r\n <mat-form-field appearance=\"outline\" class=\"input-field\">\r\n <input matInput #messageInput placeholder=\"Type a message...\" [(ngModel)]=\"inputText\" (keydown)=\"onKeydown($event)\" autocomplete=\"off\" />\r\n </mat-form-field>\r\n <button mat-icon-button color=\"primary\" class=\"send-btn\" (click)=\"sendMessage()\" [disabled]=\"!inputText.trim()\">\r\n <mat-icon>send</mat-icon>\r\n </button>\r\n </div>\r\n\r\n</div>\r\n", styles: [".agent-fab{position:fixed;bottom:24px;right:24px;z-index:1001;background:#1976d2;color:#fff}.agent-window{position:fixed;bottom:96px;right:24px;width:400px;height:580px;background:#fff;border-radius:16px;box-shadow:0 8px 32px #00000026;display:flex;flex-direction:column;z-index:1000;overflow:hidden}.agent-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;background:#1976d2;color:#fff;min-height:56px}.header-info{display:flex;align-items:center;gap:10px}.header-icon{font-size:24px;width:24px;height:24px}.header-text{display:flex;flex-direction:column;gap:1px}.header-title{font-size:16px;font-weight:500;line-height:1.2}.status-badge{display:flex;align-items:center;gap:4px;font-size:11px;font-weight:400;opacity:.9;line-height:1}.status-dot{width:7px;height:7px;border-radius:50%;display:inline-block}.status-badge.online .status-dot{background:#4caf50;box-shadow:0 0 4px #4caf5099}.status-badge.offline .status-dot{background:#9e9e9e}.header-actions{display:flex;gap:0}.header-actions button{color:#fff}.agent-messages{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:8px}.agent-greeting{display:flex;flex-direction:column;align-items:center;text-align:center;padding:20px 8px 8px;gap:10px}.greeting-icon{font-size:48px;width:48px;height:48px;color:#1976d2;opacity:.8}.greeting-text{font-size:15px;color:#424242;line-height:1.5;margin:0}.capability-pillars{display:flex;flex-direction:column;gap:10px;margin-top:8px;width:100%}.pillar{border:1px solid #e0e0e0;border-radius:12px;padding:12px 14px;text-align:left;cursor:pointer;transition:all .2s ease;background:#fafafa}.pillar:hover{border-color:#1976d2;background:#1976d20a;box-shadow:0 2px 8px #1976d21a}.pillar-header{display:flex;align-items:center;gap:8px;margin-bottom:4px}.pillar-icon{font-size:20px;width:20px;height:20px;color:#1976d2}.pillar-title{font-size:13px;font-weight:600;color:#212121}.pillar-desc{font-size:12px;color:#616161;margin:0 0 6px;line-height:1.4}.pillar-example{font-size:12px;color:#1976d2;font-style:italic;opacity:.85}.message-row{display:flex;max-width:85%}.user-row{align-self:flex-end}.agent-row{align-self:flex-start}.message-bubble{padding:10px 14px;border-radius:16px;font-size:14px;line-height:1.5;word-break:break-word;white-space:pre-wrap}.user-bubble{background:#1976d2;color:#fff;border-bottom-right-radius:4px}.agent-bubble{background:#f0f0f0;color:#212121;border-bottom-left-radius:4px}.typing-bubble{display:flex;align-items:center;gap:4px;padding:12px 18px}.typing-dot{width:8px;height:8px;border-radius:50%;background:#9e9e9e;animation:typingBounce 1.4s infinite ease-in-out}.typing-dot:nth-child(2){animation-delay:.2s}.typing-dot:nth-child(3){animation-delay:.4s}@keyframes typingBounce{0%,60%,to{transform:translateY(0);opacity:.4}30%{transform:translateY(-6px);opacity:1}}.agent-input{display:flex;align-items:center;padding:8px 12px;border-top:1px solid #e0e0e0;gap:4px}.input-field{flex:1}.input-field ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}.input-field ::ng-deep .mat-mdc-text-field-wrapper{padding:0 12px}.send-btn{margin-bottom:4px}@media (max-width: 600px){.agent-window{inset:0 0 auto;width:100%;height:100%;height:100dvh;height:var(--agent-vh, 100dvh);border-radius:0}.agent-fab{bottom:72px;right:16px}}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3.MatFabButton, selector: "button[mat-fab], a[mat-fab], button[matFab], a[matFab]", inputs: ["extended"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i4$3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "directive", type: i7$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }], animations: [
14721
14721
  trigger('slideUp', [
14722
14722
  transition(':enter', [
14723
14723
  style({ opacity: 0, transform: 'translateY(20px) scale(0.95)' }),
@@ -14741,7 +14741,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
14741
14741
  animate('200ms cubic-bezier(0.4, 0, 0.2, 1)', style({ opacity: 0, transform: 'translateY(20px) scale(0.95)' }))
14742
14742
  ])
14743
14743
  ])
14744
- ], template: "<!-- Floating chat widget for in-app Agent (renamed from Assistant) -->\n\n<!-- FAB toggle button -->\n<button mat-fab class=\"agent-fab\" (click)=\"toggleChat()\" [matTooltip]=\"agentName\">\n <mat-icon>{{ isOpen ? 'close' : 'chat_bubble' }}</mat-icon>\n</button>\n\n<!-- Chat window -->\n<div class=\"agent-window\" *ngIf=\"isOpen\" @slideUp>\n\n <!-- Header with online status -->\n <div class=\"agent-header\">\n <div class=\"header-info\">\n <mat-icon class=\"header-icon\">smart_toy</mat-icon>\n <div class=\"header-text\">\n <span class=\"header-title\">{{ agentName }}</span>\n <span class=\"status-badge\" [class.online]=\"isOnline\" [class.offline]=\"!isOnline\">\n <span class=\"status-dot\"></span>\n {{ isOnline ? 'Online' : 'Offline' }}\n </span>\n </div>\n </div>\n <div class=\"header-actions\">\n <button mat-icon-button matTooltip=\"New conversation\" (click)=\"newConversation()\">\n <mat-icon>add_comment</mat-icon>\n </button>\n <button mat-icon-button matTooltip=\"Close\" (click)=\"toggleChat()\">\n <mat-icon>remove</mat-icon>\n </button>\n </div>\n </div>\n\n <!-- Messages area -->\n <div class=\"agent-messages\" #messageContainer>\n\n <!-- Welcome experience (show when no messages) -->\n <div class=\"agent-greeting\" *ngIf=\"messages.length === 0\">\n <mat-icon class=\"greeting-icon\">smart_toy</mat-icon>\n <p class=\"greeting-text\">{{ greeting }}</p>\n\n <!-- Three capability pillars -->\n <div class=\"capability-pillars\">\n\n <!-- Pillar 1: Navigate & Inquire -->\n <div class=\"pillar\" (click)=\"sendSuggested('How do I post a transaction?')\">\n <div class=\"pillar-header\">\n <mat-icon class=\"pillar-icon\">explore</mat-icon>\n <span class=\"pillar-title\">Ask & Navigate</span>\n </div>\n <p class=\"pillar-desc\">Ask how to do things or find features in the app</p>\n <span class=\"pillar-example\">\"How do I post a transaction?\"</span>\n </div>\n\n <!-- Pillar 2: Create & Modify -->\n <div class=\"pillar\" (click)=\"sendSuggested('Create a new customer')\">\n <div class=\"pillar-header\">\n <mat-icon class=\"pillar-icon\">edit_note</mat-icon>\n <span class=\"pillar-title\">Create & Modify</span>\n </div>\n <p class=\"pillar-desc\">Add, edit, or delete records using natural language</p>\n <span class=\"pillar-example\">\"Create a new customer called Acme Ltd\"</span>\n </div>\n\n <!-- Pillar 3: Get Information -->\n <div class=\"pillar\" (click)=\"sendSuggested('List all accounts')\">\n <div class=\"pillar-header\">\n <mat-icon class=\"pillar-icon\">search</mat-icon>\n <span class=\"pillar-title\">Get Information</span>\n </div>\n <p class=\"pillar-desc\">Retrieve details, list records, or get summaries</p>\n <span class=\"pillar-example\">\"Show me the details of Customer A\"</span>\n </div>\n\n </div>\n </div>\n\n <!-- Message bubbles -->\n <div *ngFor=\"let msg of messages; trackBy: trackMessage\" class=\"message-row\" [class.user-row]=\"msg.role === 'user'\" [class.agent-row]=\"msg.role === 'assistant'\">\n <div class=\"message-bubble\" [class.user-bubble]=\"msg.role === 'user'\" [class.agent-bubble]=\"msg.role === 'assistant'\">\n {{ msg.content }}\n </div>\n </div>\n\n <!-- Typing indicator -->\n <div class=\"message-row agent-row\" *ngIf=\"isTyping\">\n <div class=\"message-bubble agent-bubble typing-bubble\">\n <span class=\"typing-dot\"></span>\n <span class=\"typing-dot\"></span>\n <span class=\"typing-dot\"></span>\n </div>\n </div>\n\n </div>\n\n <!-- Input area -->\n <div class=\"agent-input\">\n <mat-form-field appearance=\"outline\" class=\"input-field\">\n <input matInput #messageInput placeholder=\"Type a message...\" [(ngModel)]=\"inputText\" (keydown)=\"onKeydown($event)\" autocomplete=\"off\" />\n </mat-form-field>\n <button mat-icon-button color=\"primary\" class=\"send-btn\" (click)=\"sendMessage()\" [disabled]=\"!inputText.trim()\">\n <mat-icon>send</mat-icon>\n </button>\n </div>\n\n</div>\n", styles: [".agent-fab{position:fixed;bottom:24px;right:24px;z-index:1001;background:#1976d2;color:#fff}.agent-window{position:fixed;bottom:96px;right:24px;width:400px;height:580px;background:#fff;border-radius:16px;box-shadow:0 8px 32px #00000026;display:flex;flex-direction:column;z-index:1000;overflow:hidden}.agent-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;background:#1976d2;color:#fff;min-height:56px}.header-info{display:flex;align-items:center;gap:10px}.header-icon{font-size:24px;width:24px;height:24px}.header-text{display:flex;flex-direction:column;gap:1px}.header-title{font-size:16px;font-weight:500;line-height:1.2}.status-badge{display:flex;align-items:center;gap:4px;font-size:11px;font-weight:400;opacity:.9;line-height:1}.status-dot{width:7px;height:7px;border-radius:50%;display:inline-block}.status-badge.online .status-dot{background:#4caf50;box-shadow:0 0 4px #4caf5099}.status-badge.offline .status-dot{background:#9e9e9e}.header-actions{display:flex;gap:0}.header-actions button{color:#fff}.agent-messages{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:8px}.agent-greeting{display:flex;flex-direction:column;align-items:center;text-align:center;padding:20px 8px 8px;gap:10px}.greeting-icon{font-size:48px;width:48px;height:48px;color:#1976d2;opacity:.8}.greeting-text{font-size:15px;color:#424242;line-height:1.5;margin:0}.capability-pillars{display:flex;flex-direction:column;gap:10px;margin-top:8px;width:100%}.pillar{border:1px solid #e0e0e0;border-radius:12px;padding:12px 14px;text-align:left;cursor:pointer;transition:all .2s ease;background:#fafafa}.pillar:hover{border-color:#1976d2;background:#1976d20a;box-shadow:0 2px 8px #1976d21a}.pillar-header{display:flex;align-items:center;gap:8px;margin-bottom:4px}.pillar-icon{font-size:20px;width:20px;height:20px;color:#1976d2}.pillar-title{font-size:13px;font-weight:600;color:#212121}.pillar-desc{font-size:12px;color:#616161;margin:0 0 6px;line-height:1.4}.pillar-example{font-size:12px;color:#1976d2;font-style:italic;opacity:.85}.message-row{display:flex;max-width:85%}.user-row{align-self:flex-end}.agent-row{align-self:flex-start}.message-bubble{padding:10px 14px;border-radius:16px;font-size:14px;line-height:1.5;word-break:break-word;white-space:pre-wrap}.user-bubble{background:#1976d2;color:#fff;border-bottom-right-radius:4px}.agent-bubble{background:#f0f0f0;color:#212121;border-bottom-left-radius:4px}.typing-bubble{display:flex;align-items:center;gap:4px;padding:12px 18px}.typing-dot{width:8px;height:8px;border-radius:50%;background:#9e9e9e;animation:typingBounce 1.4s infinite ease-in-out}.typing-dot:nth-child(2){animation-delay:.2s}.typing-dot:nth-child(3){animation-delay:.4s}@keyframes typingBounce{0%,60%,to{transform:translateY(0);opacity:.4}30%{transform:translateY(-6px);opacity:1}}.agent-input{display:flex;align-items:center;padding:8px 12px;border-top:1px solid #e0e0e0;gap:4px}.input-field{flex:1}.input-field ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}.input-field ::ng-deep .mat-mdc-text-field-wrapper{padding:0 12px}.send-btn{margin-bottom:4px}@media (max-width: 600px){.agent-window{inset:0 0 auto;width:100%;height:100%;height:100dvh;height:var(--agent-vh, 100dvh);border-radius:0}.agent-fab{bottom:72px;right:16px}}\n"] }]
14744
+ ], template: "<!-- Floating chat widget for in-app Agent (renamed from Assistant) -->\r\n\r\n<!-- FAB toggle button -->\r\n<button mat-fab class=\"agent-fab\" (click)=\"toggleChat()\" [matTooltip]=\"agentName\">\r\n <mat-icon>{{ isOpen ? 'close' : 'chat_bubble' }}</mat-icon>\r\n</button>\r\n\r\n<!-- Chat window -->\r\n<div class=\"agent-window\" *ngIf=\"isOpen\" @slideUp>\r\n\r\n <!-- Header with online status -->\r\n <div class=\"agent-header\">\r\n <div class=\"header-info\">\r\n <mat-icon class=\"header-icon\">smart_toy</mat-icon>\r\n <div class=\"header-text\">\r\n <span class=\"header-title\">{{ agentName }}</span>\r\n <span class=\"status-badge\" [class.online]=\"isOnline\" [class.offline]=\"!isOnline\">\r\n <span class=\"status-dot\"></span>\r\n {{ isOnline ? 'Online' : 'Offline' }}\r\n </span>\r\n </div>\r\n </div>\r\n <div class=\"header-actions\">\r\n <button mat-icon-button matTooltip=\"New conversation\" (click)=\"newConversation()\">\r\n <mat-icon>add_comment</mat-icon>\r\n </button>\r\n <button mat-icon-button matTooltip=\"Close\" (click)=\"toggleChat()\">\r\n <mat-icon>remove</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Messages area -->\r\n <div class=\"agent-messages\" #messageContainer>\r\n\r\n <!-- Welcome experience (show when no messages) -->\r\n <div class=\"agent-greeting\" *ngIf=\"messages.length === 0\">\r\n <mat-icon class=\"greeting-icon\">smart_toy</mat-icon>\r\n <p class=\"greeting-text\">{{ greeting }}</p>\r\n\r\n <!-- Three capability pillars -->\r\n <div class=\"capability-pillars\">\r\n\r\n <!-- Pillar 1: Navigate & Inquire -->\r\n <div class=\"pillar\" (click)=\"sendSuggested('How do I post a transaction?')\">\r\n <div class=\"pillar-header\">\r\n <mat-icon class=\"pillar-icon\">explore</mat-icon>\r\n <span class=\"pillar-title\">Ask & Navigate</span>\r\n </div>\r\n <p class=\"pillar-desc\">Ask how to do things or find features in the app</p>\r\n <span class=\"pillar-example\">\"How do I post a transaction?\"</span>\r\n </div>\r\n\r\n <!-- Pillar 2: Create & Modify -->\r\n <div class=\"pillar\" (click)=\"sendSuggested('Create a new customer')\">\r\n <div class=\"pillar-header\">\r\n <mat-icon class=\"pillar-icon\">edit_note</mat-icon>\r\n <span class=\"pillar-title\">Create & Modify</span>\r\n </div>\r\n <p class=\"pillar-desc\">Add, edit, or delete records using natural language</p>\r\n <span class=\"pillar-example\">\"Create a new customer called Acme Ltd\"</span>\r\n </div>\r\n\r\n <!-- Pillar 3: Get Information -->\r\n <div class=\"pillar\" (click)=\"sendSuggested('List all accounts')\">\r\n <div class=\"pillar-header\">\r\n <mat-icon class=\"pillar-icon\">search</mat-icon>\r\n <span class=\"pillar-title\">Get Information</span>\r\n </div>\r\n <p class=\"pillar-desc\">Retrieve details, list records, or get summaries</p>\r\n <span class=\"pillar-example\">\"Show me the details of Customer A\"</span>\r\n </div>\r\n\r\n </div>\r\n </div>\r\n\r\n <!-- Message bubbles -->\r\n <div *ngFor=\"let msg of messages; trackBy: trackMessage\" class=\"message-row\" [class.user-row]=\"msg.role === 'user'\" [class.agent-row]=\"msg.role === 'assistant'\">\r\n <div class=\"message-bubble\" [class.user-bubble]=\"msg.role === 'user'\" [class.agent-bubble]=\"msg.role === 'assistant'\">\r\n {{ msg.content }}\r\n </div>\r\n </div>\r\n\r\n <!-- Typing indicator -->\r\n <div class=\"message-row agent-row\" *ngIf=\"isTyping\">\r\n <div class=\"message-bubble agent-bubble typing-bubble\">\r\n <span class=\"typing-dot\"></span>\r\n <span class=\"typing-dot\"></span>\r\n <span class=\"typing-dot\"></span>\r\n </div>\r\n </div>\r\n\r\n </div>\r\n\r\n <!-- Input area -->\r\n <div class=\"agent-input\">\r\n <mat-form-field appearance=\"outline\" class=\"input-field\">\r\n <input matInput #messageInput placeholder=\"Type a message...\" [(ngModel)]=\"inputText\" (keydown)=\"onKeydown($event)\" autocomplete=\"off\" />\r\n </mat-form-field>\r\n <button mat-icon-button color=\"primary\" class=\"send-btn\" (click)=\"sendMessage()\" [disabled]=\"!inputText.trim()\">\r\n <mat-icon>send</mat-icon>\r\n </button>\r\n </div>\r\n\r\n</div>\r\n", styles: [".agent-fab{position:fixed;bottom:24px;right:24px;z-index:1001;background:#1976d2;color:#fff}.agent-window{position:fixed;bottom:96px;right:24px;width:400px;height:580px;background:#fff;border-radius:16px;box-shadow:0 8px 32px #00000026;display:flex;flex-direction:column;z-index:1000;overflow:hidden}.agent-header{display:flex;align-items:center;justify-content:space-between;padding:12px 16px;background:#1976d2;color:#fff;min-height:56px}.header-info{display:flex;align-items:center;gap:10px}.header-icon{font-size:24px;width:24px;height:24px}.header-text{display:flex;flex-direction:column;gap:1px}.header-title{font-size:16px;font-weight:500;line-height:1.2}.status-badge{display:flex;align-items:center;gap:4px;font-size:11px;font-weight:400;opacity:.9;line-height:1}.status-dot{width:7px;height:7px;border-radius:50%;display:inline-block}.status-badge.online .status-dot{background:#4caf50;box-shadow:0 0 4px #4caf5099}.status-badge.offline .status-dot{background:#9e9e9e}.header-actions{display:flex;gap:0}.header-actions button{color:#fff}.agent-messages{flex:1;overflow-y:auto;padding:16px;display:flex;flex-direction:column;gap:8px}.agent-greeting{display:flex;flex-direction:column;align-items:center;text-align:center;padding:20px 8px 8px;gap:10px}.greeting-icon{font-size:48px;width:48px;height:48px;color:#1976d2;opacity:.8}.greeting-text{font-size:15px;color:#424242;line-height:1.5;margin:0}.capability-pillars{display:flex;flex-direction:column;gap:10px;margin-top:8px;width:100%}.pillar{border:1px solid #e0e0e0;border-radius:12px;padding:12px 14px;text-align:left;cursor:pointer;transition:all .2s ease;background:#fafafa}.pillar:hover{border-color:#1976d2;background:#1976d20a;box-shadow:0 2px 8px #1976d21a}.pillar-header{display:flex;align-items:center;gap:8px;margin-bottom:4px}.pillar-icon{font-size:20px;width:20px;height:20px;color:#1976d2}.pillar-title{font-size:13px;font-weight:600;color:#212121}.pillar-desc{font-size:12px;color:#616161;margin:0 0 6px;line-height:1.4}.pillar-example{font-size:12px;color:#1976d2;font-style:italic;opacity:.85}.message-row{display:flex;max-width:85%}.user-row{align-self:flex-end}.agent-row{align-self:flex-start}.message-bubble{padding:10px 14px;border-radius:16px;font-size:14px;line-height:1.5;word-break:break-word;white-space:pre-wrap}.user-bubble{background:#1976d2;color:#fff;border-bottom-right-radius:4px}.agent-bubble{background:#f0f0f0;color:#212121;border-bottom-left-radius:4px}.typing-bubble{display:flex;align-items:center;gap:4px;padding:12px 18px}.typing-dot{width:8px;height:8px;border-radius:50%;background:#9e9e9e;animation:typingBounce 1.4s infinite ease-in-out}.typing-dot:nth-child(2){animation-delay:.2s}.typing-dot:nth-child(3){animation-delay:.4s}@keyframes typingBounce{0%,60%,to{transform:translateY(0);opacity:.4}30%{transform:translateY(-6px);opacity:1}}.agent-input{display:flex;align-items:center;padding:8px 12px;border-top:1px solid #e0e0e0;gap:4px}.input-field{flex:1}.input-field ::ng-deep .mat-mdc-form-field-subscript-wrapper{display:none}.input-field ::ng-deep .mat-mdc-text-field-wrapper{padding:0 12px}.send-btn{margin-bottom:4px}@media (max-width: 600px){.agent-window{inset:0 0 auto;width:100%;height:100%;height:100dvh;height:var(--agent-vh, 100dvh);border-radius:0}.agent-fab{bottom:72px;right:16px}}\n"] }]
14745
14745
  }], ctorParameters: () => [{ type: AgentService }, { type: SignalRService }], propDecorators: { messageContainer: [{
14746
14746
  type: ViewChild,
14747
14747
  args: ['messageContainer']
@@ -17241,11 +17241,11 @@ class SpaLandingComponent {
17241
17241
  s.setProperty('--lp-secondary-light', c.secondaryLight);
17242
17242
  }
17243
17243
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: SpaLandingComponent, deps: [{ token: i1$2.Router }, { token: AuthService }, { token: LastRouteService }, { token: i0.ElementRef }, { token: i3$2.DomSanitizer }], target: i0.ɵɵFactoryTarget.Component }); }
17244
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: SpaLandingComponent, isStandalone: false, selector: "spa-landing", inputs: { config: "config" }, host: { listeners: { "window:scroll": "onWindowScroll()" } }, ngImport: i0, template: "<!-- ===== NAVBAR ===== -->\n<nav class=\"lp-navbar\" [class.lp-navbar-scrolled]=\"navScrolled\">\n <div class=\"lp-container lp-nav-inner\">\n <!-- Brand: custom SVG override OR default img+name -->\n <div class=\"lp-nav-brand\" (click)=\"scrollToSection('lp-hero')\">\n <ng-container *ngIf=\"config.navBrand; else defaultNavBrand\">\n <span class=\"lp-nav-brand-svg\" [innerHTML]=\"safeNavBrandSvg\"></span>\n <div *ngIf=\"config.navBrand.wordmarkLine1\" class=\"lp-nav-brand-wordmark\">\n <span class=\"lp-nav-wordmark-line1\">{{ config.navBrand.wordmarkLine1 }}</span>\n <span *ngIf=\"config.navBrand.wordmarkLine2\" class=\"lp-nav-wordmark-line2\">{{ config.navBrand.wordmarkLine2 }}</span>\n </div>\n </ng-container>\n <ng-template #defaultNavBrand>\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-nav-logo\">\n <span class=\"lp-nav-brand-text\">{{ config.appName }}</span>\n </ng-template>\n </div>\n <!-- Desktop nav links -->\n <div class=\"lp-nav-links\">\n <a *ngFor=\"let link of config.navLinks\" (click)=\"scrollToSection(link.target)\">{{ link.label }}</a>\n </div>\n <!-- Mobile menu overlay -->\n <div class=\"lp-mobile-menu\" [class.lp-mobile-menu-open]=\"mobileMenuOpen\">\n <a *ngFor=\"let link of config.navLinks\" (click)=\"scrollToSection(link.target)\">{{ link.label }}</a>\n <div class=\"lp-mobile-auth\">\n <a class=\"lp-nav-login\" (click)=\"navigateTo('login')\">Log in</a>\n <button class=\"lp-btn lp-btn-primary\" (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">Get Started</button>\n </div>\n </div>\n <!-- Desktop auth -->\n <div class=\"lp-nav-auth\">\n <a class=\"lp-nav-login\" (click)=\"navigateTo('login')\">Log in</a>\n <button class=\"lp-btn lp-btn-primary lp-btn-sm\" (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">Get Started</button>\n </div>\n <!-- Hamburger for mobile -->\n <button class=\"lp-hamburger\" (click)=\"toggleMobileMenu()\" [class.lp-hamburger-open]=\"mobileMenuOpen\">\n <span></span><span></span><span></span>\n </button>\n </div>\n</nav>\n\n<!-- ===== HERO ===== -->\n<section id=\"lp-hero\" class=\"lp-hero-bg\" [class.lp-hero-split]=\"config.hero.layout === 'split'\">\n <!-- Optional faded watermark SVG (e.g. company logo) -->\n <!-- Changed: wrapper div gets Angular scoping attribute; inner div holds innerHTML so svg size CSS applies to scoped .lp-hero-watermark-inner -->\n <div *ngIf=\"config.hero.watermarkSvg\" class=\"lp-hero-watermark\" aria-hidden=\"true\">\n <div class=\"lp-hero-watermark-inner\" [innerHTML]=\"safeHeroWatermarkSvg\"></div>\n </div>\n\n <div class=\"lp-container lp-hero-content\">\n <!-- Left / centered column: brand, headline, CTAs -->\n <div class=\"lp-hero-main\">\n <!-- App brand: logo + name + decorative divider -->\n <div class=\"lp-hero-brand\">\n <div class=\"lp-hero-brand-logo\">\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-hero-brand-icon\">\n <span class=\"lp-hero-brand-name\">{{ config.appName }}</span>\n </div>\n <div class=\"lp-hero-divider\" aria-hidden=\"true\">\n <span class=\"lp-hero-divider-line\"></span>\n <span class=\"lp-hero-divider-dot\"></span>\n <span class=\"lp-hero-divider-line\"></span>\n </div>\n </div>\n <!-- Hero text: headline, subtitle, CTAs, trust signals -->\n <div class=\"lp-hero-text\">\n <div *ngIf=\"config.hero.badge\" class=\"lp-hero-badge\">{{ config.hero.badge }}</div>\n <h1>{{ config.hero.headline }} <span class=\"lp-gradient-text\">{{ config.hero.gradientText }}</span></h1>\n <p class=\"lp-hero-subtitle\">{{ config.hero.subtitle }}</p>\n <div class=\"lp-hero-ctas\">\n <button class=\"lp-btn lp-btn-primary lp-btn-lg\" (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">\n {{ config.hero.primaryCTA }}\n </button>\n <button class=\"lp-btn lp-btn-outline lp-btn-lg\" (click)=\"scrollToSection(config.hero.secondaryCTASection)\">\n {{ config.hero.secondaryCTA }}\n </button>\n </div>\n <!-- Trust signals with checkmark SVG icons -->\n <div class=\"lp-trust-signals\">\n <div *ngFor=\"let signal of config.hero.trustSignals\" class=\"lp-trust-item\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path d=\"M20 6L9 17l-5-5\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n <span>{{ signal }}</span>\n </div>\n </div>\n </div>\n </div>\n <!-- App visual: always rendered when projected content exists (stacked below hero text) -->\n <!-- Changed: removed *ngIf so visual always renders below text; layout is always stacked/column -->\n <div class=\"lp-hero-visual\">\n <ng-content select=\"[lpHeroVisual]\"></ng-content>\n </div>\n </div>\n</section>\n\n<!-- ===== METRICS (optional) ===== -->\n<section *ngIf=\"config.metrics\" class=\"lp-section lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-metrics-grid\">\n <div *ngFor=\"let metric of config.metrics\" class=\"lp-metric-card\">\n <div class=\"lp-metric-value\">{{ metric.value }}</div>\n <div class=\"lp-metric-label\">{{ metric.label }}</div>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== FEATURES ===== -->\n<section id=\"lp-features\" class=\"lp-section lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-section-header\">\n <h2>{{ config.features.title }}</h2>\n <p>{{ config.features.subtitle }}</p>\n </div>\n <div class=\"lp-features-grid\">\n <div *ngFor=\"let feature of config.features.items\" class=\"lp-feature-card\">\n <!-- Icon tint: hex + '20' = ~12% opacity background -->\n <div class=\"lp-feature-icon\"\n [style.background]=\"iconBg(feature.color)\"\n [style.color]=\"feature.color\">\n <span class=\"material-icons\">{{ feature.icon }}</span>\n </div>\n <h3 class=\"lp-feature-title\">{{ feature.title }}</h3>\n <p class=\"lp-feature-desc\">{{ feature.description }}</p>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== WORKFLOW (optional) ===== -->\n<section *ngIf=\"config.workflow\" id=\"lp-workflow\" class=\"lp-section lp-section-alt lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-section-header\">\n <h2>{{ config.workflow!.title }}</h2>\n <p *ngIf=\"config.workflow!.subtitle\">{{ config.workflow!.subtitle }}</p>\n </div>\n\n <!-- Road metaphor layout: horizontal road line with waypoints -->\n <div *ngIf=\"config.workflow!.style === 'road'\" class=\"lp-workflow-road\">\n <div class=\"lp-road-line\"></div>\n <div *ngFor=\"let step of config.workflow!.steps\" class=\"lp-waypoint\">\n <div class=\"lp-waypoint-marker\">\n <span class=\"material-icons\">{{ step.icon }}</span>\n </div>\n <div class=\"lp-waypoint-number\">Step {{ step.number }}</div>\n <div class=\"lp-waypoint-title\">{{ step.title }}</div>\n <div class=\"lp-waypoint-desc\">{{ step.description }}</div>\n </div>\n </div>\n\n <!-- Default steps grid layout -->\n <div *ngIf=\"!config.workflow!.style || config.workflow!.style === 'steps'\" class=\"lp-steps-grid\">\n <div *ngFor=\"let step of config.workflow!.steps; let i = index\" class=\"lp-step-card\">\n <div class=\"lp-step-number\">{{ step.number }}</div>\n <div class=\"lp-step-icon\">\n <span class=\"material-icons\">{{ step.icon }}</span>\n </div>\n <h3 class=\"lp-step-title\">{{ step.title }}</h3>\n <p class=\"lp-step-desc\">{{ step.description }}</p>\n <!-- Connector line between steps, not after last -->\n <div *ngIf=\"i < config.workflow!.steps.length - 1\" class=\"lp-step-connector\"></div>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== CUSTOM SECTION SLOT ===== -->\n<!-- Consumer projects app-specific sections here (fleet board, benefits rows, etc.)\n using [lpCustomSection] attribute on a wrapper element -->\n<ng-content select=\"[lpCustomSection]\"></ng-content>\n\n<!-- ===== MODULES (optional) ===== -->\n<section *ngIf=\"config.modules\" id=\"lp-modules\" class=\"lp-section lp-section-alt lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-section-header\">\n <h2>{{ config.modules!.title }}</h2>\n <p>{{ config.modules!.subtitle }}</p>\n </div>\n <div class=\"lp-modules-grid\">\n <div *ngFor=\"let group of config.modules!.groups\" class=\"lp-module-group\">\n <div class=\"lp-module-group-label\">{{ group.category }}</div>\n <div class=\"lp-module-tiles\">\n <div *ngFor=\"let tile of group.tiles\" class=\"lp-module-tile\">\n <span class=\"material-icons\">{{ tile.icon }}</span>\n <span>{{ tile.name }}</span>\n </div>\n </div>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== SECURITY (optional \u2014 dark background) ===== -->\n<section *ngIf=\"config.security\" class=\"lp-section lp-section-dark lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-section-header lp-header-light\">\n <h2>{{ config.security!.title }}</h2>\n <p>{{ config.security!.subtitle }}</p>\n </div>\n <div class=\"lp-security-grid\">\n <div *ngFor=\"let item of config.security!.items\" class=\"lp-security-card\">\n <div class=\"lp-security-icon\">\n <span class=\"material-icons\">{{ item.icon }}</span>\n </div>\n <h3>{{ item.title }}</h3>\n <p>{{ item.description }}</p>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== TESTIMONIALS (optional) ===== -->\n<section *ngIf=\"config.testimonials\" class=\"lp-section lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-section-header\">\n <h2>{{ config.testimonials!.title }}</h2>\n <p *ngIf=\"config.testimonials!.subtitle\">{{ config.testimonials!.subtitle }}</p>\n </div>\n <div class=\"lp-testimonials-grid\">\n <div *ngFor=\"let item of config.testimonials!.items\" class=\"lp-testimonial-card\">\n <div class=\"lp-stars\">\n <span class=\"material-icons\">star</span>\n <span class=\"material-icons\">star</span>\n <span class=\"material-icons\">star</span>\n <span class=\"material-icons\">star</span>\n <span class=\"material-icons\">star</span>\n </div>\n <p class=\"lp-testimonial-quote\">\"{{ item.quote }}\"</p>\n <div class=\"lp-testimonial-author\">\n <div class=\"lp-author-avatar\">{{ item.authorInitials }}</div>\n <div>\n <div class=\"lp-author-name\">{{ item.authorName }}</div>\n <div class=\"lp-author-role\">{{ item.authorRole }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== PRICING (optional) ===== -->\n<section *ngIf=\"config.pricing\" id=\"lp-pricing\" class=\"lp-section lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-section-header\">\n <h2>{{ config.pricing!.title }}</h2>\n <p *ngIf=\"config.pricing!.subtitle\">{{ config.pricing!.subtitle }}</p>\n </div>\n <div class=\"lp-pricing-grid\">\n <div *ngFor=\"let plan of config.pricing!.plans\"\n class=\"lp-pricing-card\"\n [class.lp-pricing-popular]=\"plan.popular\">\n <div *ngIf=\"plan.popular\" class=\"lp-popular-badge\">Most Popular</div>\n <div class=\"lp-pricing-header\">\n <h3>{{ plan.name }}</h3>\n <div class=\"lp-pricing-price\">\n <span class=\"lp-price-amount\">{{ plan.price }}</span>\n <span *ngIf=\"plan.period\" class=\"lp-price-period\">{{ plan.period }}</span>\n </div>\n <p class=\"lp-pricing-desc\">{{ plan.description }}</p>\n </div>\n <ul class=\"lp-pricing-features\">\n <li *ngFor=\"let feature of plan.features\">{{ feature }}</li>\n </ul>\n <!-- Popular plans get primary button, others get outline -->\n <button *ngIf=\"plan.popular\"\n class=\"lp-btn lp-btn-primary lp-btn-block\"\n (click)=\"plan.ctaRoute ? navigateTo(plan.ctaRoute) : scrollToSection(plan.ctaSection || '')\">\n {{ plan.ctaLabel }}\n </button>\n <button *ngIf=\"!plan.popular\"\n class=\"lp-btn lp-btn-outline lp-btn-block\"\n (click)=\"plan.ctaRoute ? navigateTo(plan.ctaRoute) : scrollToSection(plan.ctaSection || '')\">\n {{ plan.ctaLabel }}\n </button>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== FINAL CTA BANNER (optional) ===== -->\n<section *ngIf=\"config.finalCta\" class=\"lp-final-cta lp-animate\">\n <div class=\"lp-container lp-cta-content\">\n <h2>{{ config.finalCta!.headline }}</h2>\n <p>{{ config.finalCta!.subtitle }}</p>\n <div class=\"lp-cta-buttons\">\n <button class=\"lp-btn lp-btn-white lp-btn-lg\"\n (click)=\"navigateTo(config.finalCta!.primaryCTARoute || 'signup')\">\n {{ config.finalCta!.primaryCTA }}\n </button>\n <button *ngIf=\"config.finalCta!.secondaryCTA\"\n class=\"lp-btn lp-btn-outline-white lp-btn-lg\"\n (click)=\"scrollToSection(config.finalCta!.secondaryCTASection || '')\">\n {{ config.finalCta!.secondaryCTA }}\n </button>\n </div>\n </div>\n</section>\n\n<!-- ===== FOOTER ===== -->\n<footer class=\"lp-footer\" id=\"lp-footer\">\n <div class=\"lp-container\">\n\n <!-- Rich multi-column footer when footerColumns provided -->\n <div *ngIf=\"config.footerColumns && config.footerColumns.length; else simpleFooter\" class=\"lp-footer-grid\">\n <!-- Brand column -->\n <div class=\"lp-footer-brand-col\">\n <!-- Custom SVG footer brand OR default img+name -->\n <ng-container *ngIf=\"config.footerBrand; else defaultFooterBrand\">\n <div class=\"lp-footer-brand-custom\">\n <span class=\"lp-footer-brand-svg\" [innerHTML]=\"safeFooterBrandSvg\"></span>\n <div *ngIf=\"config.footerBrand.wordmarkLine1\" class=\"lp-footer-brand-wordmark\">\n <span class=\"lp-footer-wordmark-line1\">{{ config.footerBrand.wordmarkLine1 }}</span>\n <span *ngIf=\"config.footerBrand.wordmarkLine2\" class=\"lp-footer-wordmark-line2\">{{ config.footerBrand.wordmarkLine2 }}</span>\n </div>\n </div>\n </ng-container>\n <ng-template #defaultFooterBrand>\n <div class=\"lp-footer-brand-default\">\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-footer-logo\">\n <span class=\"lp-footer-name\">{{ config.appName }}</span>\n </div>\n </ng-template>\n <p *ngIf=\"config.footerTagline\" class=\"lp-footer-tagline\">{{ config.footerTagline }}</p>\n </div>\n <!-- Link columns -->\n <div *ngFor=\"let col of config.footerColumns\" class=\"lp-footer-col\">\n <h4>{{ col.title }}</h4>\n <a *ngFor=\"let link of col.links\"\n (click)=\"link.section ? scrollToSection(link.section) : (link.route ? navigateTo(link.route) : null)\">\n {{ link.label }}\n </a>\n </div>\n </div>\n\n <!-- Simple single-row footer (default) -->\n <ng-template #simpleFooter>\n <div class=\"lp-footer-inner\">\n <div class=\"lp-footer-brand\">\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-footer-logo\">\n <span class=\"lp-footer-name\">{{ config.appName }}</span>\n </div>\n <div class=\"lp-footer-links\">\n <a (click)=\"navigateTo('login')\">Log in</a>\n <a (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">Sign up</a>\n </div>\n <div class=\"lp-footer-copy\">\n &copy; {{ currentYear }} {{ config.footerCompany || 'alsquare technologies' }}. All rights reserved.\n </div>\n </div>\n </ng-template>\n\n <!-- Footer bottom bar (shown for both layouts) -->\n <div *ngIf=\"config.footerColumns && config.footerColumns.length\" class=\"lp-footer-bottom\">\n <span>&copy; {{ currentYear }} {{ config.footerCompany || 'alsquare technologies' }}. All rights reserved.</span>\n </div>\n </div>\n</footer>\n", styles: ["@import\"https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700;800;900&display=swap\";:host{--lp-primary: #1E3A5F;--lp-primary-dark: #152C4A;--lp-primary-light: #EBF0F7;--lp-secondary: #F59E0B;--lp-secondary-light:#FEF3C7;--lp-dark: #0D1117;--lp-gray-800: #1F2937;--lp-gray-700: #374151;--lp-gray-600: #4B5563;--lp-gray-400: #9CA3AF;--lp-gray-200: #E5E7EB;--lp-gray-100: #F3F4F6;--lp-gray-50: #F9FAFB;--lp-white: #FFFFFF;--lp-radius: 12px;--lp-radius-sm: 8px;--lp-radius-lg: 20px;--lp-radius-xl: 28px;--lp-shadow-xs: 0 1px 2px rgba(0,0,0,.05);--lp-shadow: 0 1px 3px rgba(0,0,0,.07), 0 1px 2px rgba(0,0,0,.05);--lp-shadow-md: 0 4px 8px rgba(0,0,0,.06), 0 2px 4px rgba(0,0,0,.04);--lp-shadow-lg: 0 12px 28px rgba(0,0,0,.1), 0 4px 8px rgba(0,0,0,.06);--lp-shadow-xl: 0 20px 40px rgba(0,0,0,.12), 0 8px 16px rgba(0,0,0,.07);--lp-ease: cubic-bezier(.4, 0, .2, 1);--lp-spring: cubic-bezier(.34, 1.56, .64, 1);display:block;font-family:Outfit,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;color:var(--lp-gray-700);line-height:1.6;overflow-x:hidden;-webkit-font-smoothing:antialiased}.lp-container{max-width:1200px;margin:0 auto;padding:0 32px}.lp-navbar{position:fixed;top:0;left:0;right:0;z-index:1000;padding:18px 0;transition:background .3s var(--lp-ease),padding .3s var(--lp-ease),box-shadow .3s var(--lp-ease)}.lp-navbar-scrolled{background:#ffffffeb;backdrop-filter:blur(16px) saturate(180%);-webkit-backdrop-filter:blur(16px) saturate(180%);box-shadow:0 1px #0000000f,0 2px 8px #0000000a;padding:12px 0}.lp-nav-inner{display:flex;align-items:center;justify-content:space-between}.lp-nav-brand{display:flex;align-items:center;gap:10px;cursor:pointer;text-decoration:none}.lp-nav-logo{width:30px;height:30px;border-radius:7px;object-fit:contain}.lp-nav-brand-text{font-size:18px;font-weight:700;color:var(--lp-primary);letter-spacing:-.01em}.lp-nav-links{display:flex;align-items:center;gap:36px}.lp-nav-links a{font-size:14px;font-weight:500;color:var(--lp-gray-600);cursor:pointer;transition:color .2s;text-decoration:none;letter-spacing:.01em}.lp-nav-links a:hover{color:var(--lp-primary)}.lp-nav-auth{display:flex;align-items:center;gap:12px}.lp-nav-login{font-size:14px;font-weight:500;color:var(--lp-gray-600);cursor:pointer;transition:color .2s;text-decoration:none}.lp-nav-login:hover{color:var(--lp-primary)}.lp-hamburger{display:none;flex-direction:column;gap:5px;background:none;border:none;cursor:pointer;padding:4px;z-index:1001}.lp-hamburger span{display:block;width:22px;height:2px;background:var(--lp-gray-700);border-radius:2px;transition:transform .3s var(--lp-ease),opacity .3s}.lp-hamburger-open span:nth-child(1){transform:translateY(7px) rotate(45deg)}.lp-hamburger-open span:nth-child(2){opacity:0}.lp-hamburger-open span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}.lp-mobile-menu{display:none}.lp-mobile-auth{display:flex;flex-direction:column;gap:12px;margin-top:24px;padding-top:24px;border-top:1px solid var(--lp-gray-200)}.lp-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;font-family:Outfit,sans-serif;font-size:14px;font-weight:600;padding:10px 22px;border-radius:var(--lp-radius-sm);border:none;cursor:pointer;transition:all .25s var(--lp-ease);text-decoration:none;white-space:nowrap;letter-spacing:.01em}.lp-btn-primary{background:var(--lp-primary);color:var(--lp-white);box-shadow:0 1px 3px #00000026,inset 0 1px #ffffff1f}.lp-btn-primary:hover{background:var(--lp-primary-dark);transform:translateY(-1px);box-shadow:0 6px 16px #0003,inset 0 1px #ffffff1f}.lp-btn-primary:active{transform:translateY(0)}.lp-btn-outline{background:transparent;color:var(--lp-primary);border:1.5px solid var(--lp-primary)}.lp-btn-outline:hover{background:var(--lp-primary);color:var(--lp-white);transform:translateY(-1px);box-shadow:0 4px 12px #00000026}.lp-btn-sm{padding:7px 16px;font-size:13px}.lp-btn-lg{padding:14px 32px;font-size:15px;border-radius:var(--lp-radius)}.lp-btn-block{width:100%}.lp-hero-bg{position:relative;overflow:hidden;padding:140px 0 100px;background:radial-gradient(ellipse 80% 60% at 60% -10%,color-mix(in srgb,var(--lp-primary) 8%,transparent) 0%,transparent 70%),radial-gradient(ellipse 60% 50% at -10% 80%,color-mix(in srgb,var(--lp-secondary) 6%,transparent) 0%,transparent 70%),linear-gradient(160deg,var(--lp-primary-light) 0%,#FAFBFF 45%,var(--lp-secondary-light) 100%);min-height:100vh;display:flex;align-items:center}.lp-hero-bg:before{content:\"\";position:absolute;inset:0;background-image:radial-gradient(circle,rgba(0,0,0,.04) 1px,transparent 1px);background-size:32px 32px;pointer-events:none;-webkit-mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black 40%,transparent 100%);mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black 40%,transparent 100%)}.lp-hero-bg:after{content:\"\";position:absolute;width:700px;height:700px;border-radius:50%;background:color-mix(in srgb,var(--lp-primary) 5%,transparent);top:-200px;right:-150px;filter:blur(100px);pointer-events:none}.lp-hero-content{position:relative;z-index:1;text-align:center;width:100%}.lp-hero-badge{display:inline-flex;align-items:center;gap:6px;background:color-mix(in srgb,var(--lp-primary) 10%,transparent);color:var(--lp-primary);border:1px solid color-mix(in srgb,var(--lp-primary) 20%,transparent);border-radius:100px;padding:6px 16px;font-size:13px;font-weight:600;margin-bottom:20px;letter-spacing:.02em}.lp-hero-brand{margin-bottom:20px;text-align:center}.lp-hero-brand-logo{display:inline-flex;align-items:center;gap:14px;margin-bottom:12px}.lp-hero-brand-icon{height:52px;width:52px;border-radius:14px;object-fit:contain;box-shadow:0 4px 12px #0000001f}.lp-hero-brand-name{font-size:38px;font-weight:800;color:var(--lp-primary);letter-spacing:-.03em;font-family:Outfit,sans-serif}.lp-hero-divider{display:flex;align-items:center;justify-content:center;gap:10px}.lp-hero-divider-line{display:block;height:1px;width:40px;background:linear-gradient(90deg,transparent,var(--lp-primary),transparent);opacity:.3}.lp-hero-divider-dot{display:block;height:5px;width:5px;border-radius:50%;background:var(--lp-secondary);opacity:.8}.lp-hero-text{max-width:760px;margin:0 auto 64px}.lp-hero-text h1{font-family:Outfit,sans-serif;font-size:68px;font-weight:900;line-height:1.05;color:var(--lp-dark);margin:0 0 24px;letter-spacing:-.03em}.lp-gradient-text{background:linear-gradient(135deg,var(--lp-primary) 0%,var(--lp-secondary) 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}.lp-hero-subtitle{font-size:19px;color:var(--lp-gray-600);line-height:1.65;font-weight:400;max-width:560px;margin:0 auto 36px}.lp-hero-ctas{display:flex;align-items:center;justify-content:center;gap:14px;margin-bottom:32px;flex-wrap:wrap}.lp-trust-signals{display:flex;align-items:center;justify-content:center;gap:28px;flex-wrap:wrap}.lp-trust-item{display:flex;align-items:center;gap:7px;font-size:13px;font-weight:500;color:var(--lp-gray-500, #6B7280)}.lp-trust-item svg{color:var(--lp-primary);flex-shrink:0}.lp-section{padding:100px 0;background:var(--lp-white)}.lp-section-alt{background:var(--lp-gray-50)}.lp-section-header{text-align:center;margin-bottom:64px}.lp-section-header h2{font-family:Outfit,sans-serif;font-size:42px;font-weight:800;color:var(--lp-dark);margin:0 0 14px;letter-spacing:-.025em;line-height:1.15}.lp-section-header p{font-size:17px;color:var(--lp-gray-600);max-width:540px;margin:0 auto;line-height:1.65}.lp-features-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}.lp-feature-card{background:var(--lp-white);border-radius:var(--lp-radius-lg);padding:36px 28px;transition:transform .25s var(--lp-ease),box-shadow .25s var(--lp-ease);border:1px solid var(--lp-gray-200);box-shadow:var(--lp-shadow-xs);position:relative;overflow:hidden}.lp-feature-card:before{content:\"\";position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--lp-primary),var(--lp-secondary));transform:scaleX(0);transform-origin:left;transition:transform .3s var(--lp-ease);border-radius:3px 3px 0 0}.lp-feature-card:hover{transform:translateY(-5px);box-shadow:var(--lp-shadow-xl);border-color:transparent}.lp-feature-card:hover:before{transform:scaleX(1)}.lp-feature-icon{width:52px;height:52px;border-radius:var(--lp-radius-sm);display:flex;align-items:center;justify-content:center;margin-bottom:20px}.lp-feature-icon .material-icons{font-size:26px}.lp-feature-title{font-family:Outfit,sans-serif;font-size:17px;font-weight:700;color:var(--lp-dark);margin:0 0 10px;letter-spacing:-.01em}.lp-feature-desc{font-size:14px;color:var(--lp-gray-600);line-height:1.65;margin:0}.lp-steps-grid{display:flex;align-items:flex-start;justify-content:space-between;position:relative;gap:16px}.lp-step-card{flex:1;display:flex;flex-direction:column;align-items:center;text-align:center;position:relative;padding:0 8px}.lp-step-number{font-size:11px;font-weight:700;color:var(--lp-secondary);text-transform:uppercase;letter-spacing:2px;margin-bottom:10px}.lp-step-icon{width:56px;height:56px;border-radius:50%;background:var(--lp-primary);display:flex;align-items:center;justify-content:center;margin-bottom:18px;box-shadow:0 4px 14px color-mix(in srgb,var(--lp-primary) 30%,transparent);border:3px solid var(--lp-white);position:relative;z-index:1}.lp-step-icon .material-icons{font-size:24px;color:var(--lp-white)}.lp-step-title{font-family:Outfit,sans-serif;font-size:15px;font-weight:700;color:var(--lp-dark);margin:0 0 8px;letter-spacing:-.01em}.lp-step-desc{font-size:13px;color:var(--lp-gray-600);line-height:1.55;margin:0}.lp-step-connector{position:absolute;top:40px;left:60%;width:80%;height:2px;background:repeating-linear-gradient(90deg,color-mix(in srgb,var(--lp-secondary) 50%,transparent) 0px,color-mix(in srgb,var(--lp-secondary) 50%,transparent) 10px,transparent 10px,transparent 18px);pointer-events:none;z-index:0}.lp-metrics-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:0;border:1px solid var(--lp-gray-200);border-radius:var(--lp-radius-lg);overflow:hidden;background:var(--lp-white);box-shadow:var(--lp-shadow)}.lp-metric-card{text-align:center;padding:40px 24px;border-right:1px solid var(--lp-gray-200);transition:background .2s}.lp-metric-card:last-child{border-right:none}.lp-metric-card:hover{background:var(--lp-gray-50)}.lp-metric-value{font-family:Outfit,sans-serif;font-size:48px;font-weight:900;color:var(--lp-primary);line-height:1;letter-spacing:-.03em;margin-bottom:8px}.lp-metric-label{font-size:14px;font-weight:500;color:var(--lp-gray-600)}.lp-modules-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px}.lp-module-group{background:var(--lp-white);border-radius:var(--lp-radius-lg);padding:28px 28px 24px;border:1px solid var(--lp-gray-200);box-shadow:var(--lp-shadow-xs);transition:box-shadow .2s}.lp-module-group:hover{box-shadow:var(--lp-shadow-md)}.lp-module-group-label{font-family:Outfit,sans-serif;font-size:11px;font-weight:700;color:var(--lp-primary);margin-bottom:16px;text-transform:uppercase;letter-spacing:1px}.lp-module-tiles{display:flex;flex-wrap:wrap;gap:8px}.lp-module-tile{display:inline-flex;align-items:center;gap:6px;background:var(--lp-gray-50);border:1px solid var(--lp-gray-200);padding:7px 13px;border-radius:100px;font-size:12px;font-weight:600;color:var(--lp-gray-700);transition:all .2s var(--lp-ease);cursor:default}.lp-module-tile:hover{background:color-mix(in srgb,var(--lp-primary) 6%,transparent);border-color:color-mix(in srgb,var(--lp-primary) 25%,transparent);color:var(--lp-primary);transform:translateY(-1px)}.lp-module-tile .material-icons{font-size:15px;color:var(--lp-primary)}.lp-footer{background:var(--lp-dark);padding:44px 0 36px;border-top:1px solid rgba(255,255,255,.04)}.lp-footer-inner{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:20px}.lp-footer-brand{display:flex;align-items:center;gap:10px}.lp-footer-logo{width:30px;height:30px;border-radius:7px;object-fit:contain;opacity:.9}.lp-footer-name{font-family:Outfit,sans-serif;font-size:17px;font-weight:700;color:var(--lp-white);letter-spacing:-.01em}.lp-footer-links{display:flex;gap:28px}.lp-footer-links a{font-size:14px;font-weight:500;color:#ffffff80;cursor:pointer;text-decoration:none;transition:color .2s}.lp-footer-links a:hover{color:var(--lp-white)}.lp-footer-copy{font-size:13px;color:#ffffff59;width:100%;text-align:center;padding-top:20px;margin-top:4px;border-top:1px solid rgba(255,255,255,.06)}.lp-animate{opacity:0;transform:translateY(28px);transition:opacity .65s var(--lp-ease),transform .65s var(--lp-ease)}.lp-visible{opacity:1;transform:translateY(0)}.lp-visible .lp-feature-card:nth-child(1){transition-delay:0ms}.lp-visible .lp-feature-card:nth-child(2){transition-delay:60ms}.lp-visible .lp-feature-card:nth-child(3){transition-delay:.12s}.lp-visible .lp-feature-card:nth-child(4){transition-delay:.18s}.lp-visible .lp-feature-card:nth-child(5){transition-delay:.24s}.lp-visible .lp-feature-card:nth-child(6){transition-delay:.3s}.lp-visible .lp-step-card:nth-child(1){transition-delay:0ms}.lp-visible .lp-step-card:nth-child(2){transition-delay:80ms}.lp-visible .lp-step-card:nth-child(3){transition-delay:.16s}.lp-visible .lp-step-card:nth-child(4){transition-delay:.24s}.lp-visible .lp-metric-card:nth-child(1){transition-delay:0ms}.lp-visible .lp-metric-card:nth-child(2){transition-delay:60ms}.lp-visible .lp-metric-card:nth-child(3){transition-delay:.12s}.lp-visible .lp-metric-card:nth-child(4){transition-delay:.18s}@media (max-width: 1024px){.lp-hero-text h1{font-size:52px}.lp-section-header h2{font-size:36px}.lp-features-grid,.lp-metrics-grid{grid-template-columns:repeat(2,1fr)}.lp-metrics-grid .lp-metric-card:nth-child(2){border-right:none}.lp-metrics-grid .lp-metric-card:nth-child(3){border-top:1px solid var(--lp-gray-200)}.lp-metrics-grid .lp-metric-card:nth-child(4){border-top:1px solid var(--lp-gray-200)}.lp-steps-grid{flex-wrap:wrap;justify-content:center;gap:40px}.lp-step-connector{display:none}}@media (max-width: 767px){.lp-container{padding:0 20px}.lp-nav-links{display:none}.lp-hamburger{display:flex}.lp-mobile-auth{display:none}.lp-mobile-menu{display:none;position:fixed;inset:0;background:#fffffff7;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);flex-direction:column;align-items:center;justify-content:center;gap:28px;z-index:999}.lp-mobile-menu-open{display:flex}.lp-mobile-menu a{font-size:22px;font-weight:700;color:var(--lp-dark);cursor:pointer;text-decoration:none;letter-spacing:-.01em}.lp-hero-bg{padding:100px 0 60px;min-height:auto}.lp-hero-text h1{font-size:38px}.lp-hero-brand-name{font-size:28px}.lp-hero-subtitle{font-size:16px}.lp-hero-ctas{flex-direction:column}.lp-hero-ctas .lp-btn{width:100%;justify-content:center}.lp-trust-signals{gap:16px}.lp-section{padding:72px 0}.lp-section-header{margin-bottom:40px}.lp-section-header h2{font-size:28px}.lp-features-grid{grid-template-columns:1fr}.lp-metrics-grid{grid-template-columns:repeat(2,1fr)}.lp-metrics-grid .lp-metric-card{border-right:none;border-bottom:1px solid var(--lp-gray-200)}.lp-metrics-grid .lp-metric-card:last-child{border-bottom:none}.lp-metric-value{font-size:36px}.lp-modules-grid{grid-template-columns:1fr}.lp-steps-grid{flex-direction:column;align-items:center;gap:32px}.lp-step-connector{display:none}.lp-footer-inner{flex-direction:column;align-items:center;text-align:center}}.lp-nav-brand-svg{display:flex;align-items:center;width:36px;height:36px;flex-shrink:0}.lp-nav-brand-svg ::ng-deep svg{width:36px;height:36px;display:block}.lp-nav-brand-wordmark{display:flex;flex-direction:column;line-height:1.1;margin-left:8px}.lp-nav-wordmark-line1{font-size:15px;font-weight:800;color:#1a3a7a;letter-spacing:-.01em;font-family:Outfit,sans-serif}.lp-nav-wordmark-line2{font-size:9px;font-weight:600;letter-spacing:.12em;color:#e8a020;text-transform:uppercase}.lp-hero-bg{position:relative}.lp-hero-watermark{position:absolute;inset:0;display:flex;align-items:flex-start;justify-content:center;padding-top:40px;pointer-events:none;z-index:1}.lp-hero-watermark-inner{width:700px;height:700px;opacity:.04;overflow:hidden;flex-shrink:0}@media (min-width: 768px){.lp-hero-watermark-inner{width:800px;height:800px}}.lp-hero-content{display:flex;flex-direction:column;align-items:center}.lp-hero-main{z-index:1;width:100%}.lp-hero-visual{position:relative;z-index:1;width:100%;max-width:900px;margin:48px auto 0}.lp-workflow-road{position:relative;display:flex;justify-content:space-between;align-items:flex-start;padding:48px 0 24px;gap:16px}.lp-road-line{position:absolute;top:72px;left:6%;right:6%;height:3px;background:linear-gradient(90deg,var(--lp-primary-light),var(--lp-primary),var(--lp-primary-light));border-radius:2px;opacity:.4}.lp-waypoint{flex:1;display:flex;flex-direction:column;align-items:center;text-align:center;position:relative;z-index:1}.lp-waypoint-marker{width:52px;height:52px;border-radius:50%;background:var(--lp-primary);color:#fff;display:flex;align-items:center;justify-content:center;margin-bottom:12px;box-shadow:0 4px 16px color-mix(in srgb,var(--lp-primary) 35%,transparent);transition:transform .25s cubic-bezier(.34,1.56,.64,1)}.lp-waypoint:hover .lp-waypoint-marker{transform:translateY(-4px) scale(1.08)}.lp-waypoint-marker .material-icons{font-size:22px}.lp-waypoint-number{font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--lp-primary);margin-bottom:6px;opacity:.7}.lp-waypoint-title{font-size:14px;font-weight:700;color:var(--lp-dark);margin-bottom:6px;font-family:Outfit,sans-serif}.lp-waypoint-desc{font-size:12px;color:var(--lp-gray-600);line-height:1.5;max-width:140px}.lp-section-dark{background:var(--lp-dark);color:#fff}.lp-section-dark.lp-section{padding:96px 0}.lp-header-light h2{color:#fff}.lp-header-light p{color:#ffffffa6}.lp-security-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:32px}.lp-security-card{background:#ffffff0d;border:1px solid rgba(255,255,255,.08);border-radius:20px;padding:32px 28px;transition:background .25s ease}.lp-security-card:hover{background:#ffffff14}.lp-security-icon{width:52px;height:52px;border-radius:14px;background:#ffffff1a;display:flex;align-items:center;justify-content:center;margin-bottom:20px;color:#fff}.lp-security-icon .material-icons{font-size:24px}.lp-security-card h3{font-size:18px;font-weight:700;color:#fff;margin:0 0 12px;font-family:Outfit,sans-serif}.lp-security-card p{font-size:14px;color:#fff9;line-height:1.65;margin:0}.lp-testimonials-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:28px}.lp-testimonial-card{background:#fff;border:1px solid var(--lp-gray-200);border-radius:20px;padding:32px 28px;display:flex;flex-direction:column;gap:16px;box-shadow:0 1px 3px #00000012,0 1px 2px #0000000d;transition:box-shadow .25s ease,transform .25s ease}.lp-testimonial-card:hover{box-shadow:0 4px 8px #0000000f;transform:translateY(-2px)}.lp-stars{display:flex;gap:2px;color:var(--lp-secondary)}.lp-stars .material-icons{font-size:18px}.lp-testimonial-quote{font-size:15px;color:var(--lp-gray-700);line-height:1.65;font-style:italic;margin:0;flex:1}.lp-testimonial-author{display:flex;align-items:center;gap:12px}.lp-author-avatar{width:44px;height:44px;border-radius:50%;background:var(--lp-primary);color:#fff;font-size:14px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-family:Outfit,sans-serif}.lp-author-name{font-size:14px;font-weight:700;color:var(--lp-dark)}.lp-author-role{font-size:12px;color:var(--lp-gray-400)}.lp-pricing-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:28px;align-items:start}.lp-pricing-card{background:#fff;border:1px solid var(--lp-gray-200);border-radius:20px;padding:36px 28px 28px;position:relative;display:flex;flex-direction:column;box-shadow:0 1px 3px #00000012,0 1px 2px #0000000d;transition:box-shadow .25s ease,transform .25s ease}.lp-pricing-card:hover{box-shadow:0 4px 8px #0000000f;transform:translateY(-3px)}.lp-pricing-popular{border-color:var(--lp-primary);box-shadow:0 0 0 2px var(--lp-primary);transform:scale(1.03)}.lp-pricing-popular:hover{transform:scale(1.03) translateY(-3px)}.lp-popular-badge{position:absolute;top:-12px;left:50%;transform:translate(-50%);background:var(--lp-primary);color:#fff;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;padding:4px 14px;border-radius:100px;white-space:nowrap;font-family:Outfit,sans-serif}.lp-pricing-header{margin-bottom:24px}.lp-pricing-header h3{font-size:20px;font-weight:800;color:var(--lp-dark);margin:0 0 12px;font-family:Outfit,sans-serif}.lp-pricing-price{display:flex;align-items:baseline;gap:4px;margin-bottom:8px}.lp-price-amount{font-size:40px;font-weight:900;color:var(--lp-primary);font-family:Outfit,sans-serif;line-height:1}.lp-price-period{font-size:16px;color:var(--lp-gray-600)}.lp-pricing-desc{font-size:13px;color:var(--lp-gray-600);margin:0}.lp-pricing-features{list-style:none;padding:0;margin:0 0 28px;display:flex;flex-direction:column;gap:10px;flex:1}.lp-pricing-features li{font-size:14px;color:var(--lp-gray-700);padding-left:20px;position:relative}.lp-pricing-features li:before{content:\"\\2713\";position:absolute;left:0;color:var(--lp-primary);font-weight:700}.lp-btn-block{width:100%;justify-content:center;text-align:center}.lp-final-cta{background:linear-gradient(135deg,var(--lp-primary-dark, #152C4A) 0%,var(--lp-primary) 100%);padding:96px 0}.lp-cta-content{text-align:center}.lp-cta-content h2{font-size:40px;font-weight:900;color:#fff;margin:0 0 16px;font-family:Outfit,sans-serif;letter-spacing:-.02em}.lp-cta-content p{font-size:18px;color:#ffffffbf;margin:0 auto 36px;max-width:560px}.lp-cta-buttons{display:flex;gap:16px;justify-content:center;flex-wrap:wrap}.lp-btn-white{background:#fff;color:var(--lp-primary);border:2px solid white;font-weight:700}.lp-btn-white:hover{background:#ffffffe6;transform:translateY(-1px)}.lp-btn-outline-white{background:transparent;color:#fff;border:2px solid rgba(255,255,255,.5);font-weight:700}.lp-btn-outline-white:hover{background:#ffffff1a;border-color:#fff;transform:translateY(-1px)}.lp-footer-grid{display:grid;grid-template-columns:2fr 1fr 1fr 1fr;gap:48px;padding:64px 0 48px}.lp-footer-brand-col{display:flex;flex-direction:column;gap:16px}.lp-footer-brand-custom{display:flex;align-items:center;gap:10px}.lp-footer-brand-svg{display:flex;align-items:center;width:36px;height:36px;flex-shrink:0}.lp-footer-brand-svg ::ng-deep svg{width:36px;height:36px;display:block}.lp-footer-brand-wordmark{display:flex;flex-direction:column;line-height:1.1}.lp-footer-wordmark-line1{font-size:15px;font-weight:800;color:#fff;font-family:Outfit,sans-serif}.lp-footer-wordmark-line2{font-size:9px;font-weight:600;letter-spacing:.12em;color:#e8a020;text-transform:uppercase}.lp-footer-brand-default{display:flex;align-items:center;gap:10px}.lp-footer-tagline{font-size:14px;color:#ffffff80;line-height:1.6;margin:0;max-width:280px}.lp-footer-col{display:flex;flex-direction:column;gap:12px}.lp-footer-col h4{font-size:13px;font-weight:700;color:#fff;margin:0 0 4px;letter-spacing:.04em;text-transform:uppercase;font-family:Outfit,sans-serif}.lp-footer-col a{font-size:14px;color:#ffffff80;cursor:pointer;text-decoration:none;transition:color .2s}.lp-footer-col a:hover{color:#fff}.lp-footer-bottom{border-top:1px solid rgba(255,255,255,.08);padding:24px 0;font-size:13px;color:#ffffff59}@media (max-width: 768px){.lp-hero-visual{max-width:100%;margin-top:32px}.lp-workflow-road{flex-direction:column;align-items:center;gap:32px}.lp-road-line{display:none}.lp-waypoint-desc{max-width:260px}.lp-security-grid,.lp-testimonials-grid,.lp-pricing-grid{grid-template-columns:1fr}.lp-pricing-popular{transform:none}.lp-pricing-popular:hover{transform:translateY(-3px)}.lp-cta-content h2{font-size:28px}.lp-footer-grid{grid-template-columns:1fr 1fr;gap:32px;padding:48px 0 32px}}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
17244
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: SpaLandingComponent, isStandalone: false, selector: "spa-landing", inputs: { config: "config" }, host: { listeners: { "window:scroll": "onWindowScroll()" } }, ngImport: i0, template: "<!-- ===== NAVBAR ===== -->\r\n<nav class=\"lp-navbar\" [class.lp-navbar-scrolled]=\"navScrolled\">\r\n <div class=\"lp-container lp-nav-inner\">\r\n <!-- Brand: custom SVG override OR default img+name -->\r\n <div class=\"lp-nav-brand\" (click)=\"scrollToSection('lp-hero')\">\r\n <ng-container *ngIf=\"config.navBrand; else defaultNavBrand\">\r\n <span class=\"lp-nav-brand-svg\" [innerHTML]=\"safeNavBrandSvg\"></span>\r\n <div *ngIf=\"config.navBrand.wordmarkLine1\" class=\"lp-nav-brand-wordmark\">\r\n <span class=\"lp-nav-wordmark-line1\">{{ config.navBrand.wordmarkLine1 }}</span>\r\n <span *ngIf=\"config.navBrand.wordmarkLine2\" class=\"lp-nav-wordmark-line2\">{{ config.navBrand.wordmarkLine2 }}</span>\r\n </div>\r\n </ng-container>\r\n <ng-template #defaultNavBrand>\r\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-nav-logo\">\r\n <span class=\"lp-nav-brand-text\">{{ config.appName }}</span>\r\n </ng-template>\r\n </div>\r\n <!-- Desktop nav links -->\r\n <div class=\"lp-nav-links\">\r\n <a *ngFor=\"let link of config.navLinks\" (click)=\"scrollToSection(link.target)\">{{ link.label }}</a>\r\n </div>\r\n <!-- Mobile menu overlay -->\r\n <div class=\"lp-mobile-menu\" [class.lp-mobile-menu-open]=\"mobileMenuOpen\">\r\n <a *ngFor=\"let link of config.navLinks\" (click)=\"scrollToSection(link.target)\">{{ link.label }}</a>\r\n <div class=\"lp-mobile-auth\">\r\n <a class=\"lp-nav-login\" (click)=\"navigateTo('login')\">Log in</a>\r\n <button class=\"lp-btn lp-btn-primary\" (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">Get Started</button>\r\n </div>\r\n </div>\r\n <!-- Desktop auth -->\r\n <div class=\"lp-nav-auth\">\r\n <a class=\"lp-nav-login\" (click)=\"navigateTo('login')\">Log in</a>\r\n <button class=\"lp-btn lp-btn-primary lp-btn-sm\" (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">Get Started</button>\r\n </div>\r\n <!-- Hamburger for mobile -->\r\n <button class=\"lp-hamburger\" (click)=\"toggleMobileMenu()\" [class.lp-hamburger-open]=\"mobileMenuOpen\">\r\n <span></span><span></span><span></span>\r\n </button>\r\n </div>\r\n</nav>\r\n\r\n<!-- ===== HERO ===== -->\r\n<section id=\"lp-hero\" class=\"lp-hero-bg\" [class.lp-hero-split]=\"config.hero.layout === 'split'\">\r\n <!-- Optional faded watermark SVG (e.g. company logo) -->\r\n <!-- Changed: wrapper div gets Angular scoping attribute; inner div holds innerHTML so svg size CSS applies to scoped .lp-hero-watermark-inner -->\r\n <div *ngIf=\"config.hero.watermarkSvg\" class=\"lp-hero-watermark\" aria-hidden=\"true\">\r\n <div class=\"lp-hero-watermark-inner\" [innerHTML]=\"safeHeroWatermarkSvg\"></div>\r\n </div>\r\n\r\n <div class=\"lp-container lp-hero-content\">\r\n <!-- Left / centered column: brand, headline, CTAs -->\r\n <div class=\"lp-hero-main\">\r\n <!-- App brand: logo + name + decorative divider -->\r\n <div class=\"lp-hero-brand\">\r\n <div class=\"lp-hero-brand-logo\">\r\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-hero-brand-icon\">\r\n <span class=\"lp-hero-brand-name\">{{ config.appName }}</span>\r\n </div>\r\n <div class=\"lp-hero-divider\" aria-hidden=\"true\">\r\n <span class=\"lp-hero-divider-line\"></span>\r\n <span class=\"lp-hero-divider-dot\"></span>\r\n <span class=\"lp-hero-divider-line\"></span>\r\n </div>\r\n </div>\r\n <!-- Hero text: headline, subtitle, CTAs, trust signals -->\r\n <div class=\"lp-hero-text\">\r\n <div *ngIf=\"config.hero.badge\" class=\"lp-hero-badge\">{{ config.hero.badge }}</div>\r\n <h1>{{ config.hero.headline }} <span class=\"lp-gradient-text\">{{ config.hero.gradientText }}</span></h1>\r\n <p class=\"lp-hero-subtitle\">{{ config.hero.subtitle }}</p>\r\n <div class=\"lp-hero-ctas\">\r\n <button class=\"lp-btn lp-btn-primary lp-btn-lg\" (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">\r\n {{ config.hero.primaryCTA }}\r\n </button>\r\n <button class=\"lp-btn lp-btn-outline lp-btn-lg\" (click)=\"scrollToSection(config.hero.secondaryCTASection)\">\r\n {{ config.hero.secondaryCTA }}\r\n </button>\r\n </div>\r\n <!-- Trust signals with checkmark SVG icons -->\r\n <div class=\"lp-trust-signals\">\r\n <div *ngFor=\"let signal of config.hero.trustSignals\" class=\"lp-trust-item\">\r\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\">\r\n <path d=\"M20 6L9 17l-5-5\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\r\n </svg>\r\n <span>{{ signal }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <!-- App visual: always rendered when projected content exists (stacked below hero text) -->\r\n <!-- Changed: removed *ngIf so visual always renders below text; layout is always stacked/column -->\r\n <div class=\"lp-hero-visual\">\r\n <ng-content select=\"[lpHeroVisual]\"></ng-content>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== METRICS (optional) ===== -->\r\n<section *ngIf=\"config.metrics\" class=\"lp-section lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-metrics-grid\">\r\n <div *ngFor=\"let metric of config.metrics\" class=\"lp-metric-card\">\r\n <div class=\"lp-metric-value\">{{ metric.value }}</div>\r\n <div class=\"lp-metric-label\">{{ metric.label }}</div>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== FEATURES ===== -->\r\n<section id=\"lp-features\" class=\"lp-section lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-section-header\">\r\n <h2>{{ config.features.title }}</h2>\r\n <p>{{ config.features.subtitle }}</p>\r\n </div>\r\n <div class=\"lp-features-grid\">\r\n <div *ngFor=\"let feature of config.features.items\" class=\"lp-feature-card\">\r\n <!-- Icon tint: hex + '20' = ~12% opacity background -->\r\n <div class=\"lp-feature-icon\"\r\n [style.background]=\"iconBg(feature.color)\"\r\n [style.color]=\"feature.color\">\r\n <span class=\"material-icons\">{{ feature.icon }}</span>\r\n </div>\r\n <h3 class=\"lp-feature-title\">{{ feature.title }}</h3>\r\n <p class=\"lp-feature-desc\">{{ feature.description }}</p>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== WORKFLOW (optional) ===== -->\r\n<section *ngIf=\"config.workflow\" id=\"lp-workflow\" class=\"lp-section lp-section-alt lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-section-header\">\r\n <h2>{{ config.workflow!.title }}</h2>\r\n <p *ngIf=\"config.workflow!.subtitle\">{{ config.workflow!.subtitle }}</p>\r\n </div>\r\n\r\n <!-- Road metaphor layout: horizontal road line with waypoints -->\r\n <div *ngIf=\"config.workflow!.style === 'road'\" class=\"lp-workflow-road\">\r\n <div class=\"lp-road-line\"></div>\r\n <div *ngFor=\"let step of config.workflow!.steps\" class=\"lp-waypoint\">\r\n <div class=\"lp-waypoint-marker\">\r\n <span class=\"material-icons\">{{ step.icon }}</span>\r\n </div>\r\n <div class=\"lp-waypoint-number\">Step {{ step.number }}</div>\r\n <div class=\"lp-waypoint-title\">{{ step.title }}</div>\r\n <div class=\"lp-waypoint-desc\">{{ step.description }}</div>\r\n </div>\r\n </div>\r\n\r\n <!-- Default steps grid layout -->\r\n <div *ngIf=\"!config.workflow!.style || config.workflow!.style === 'steps'\" class=\"lp-steps-grid\">\r\n <div *ngFor=\"let step of config.workflow!.steps; let i = index\" class=\"lp-step-card\">\r\n <div class=\"lp-step-number\">{{ step.number }}</div>\r\n <div class=\"lp-step-icon\">\r\n <span class=\"material-icons\">{{ step.icon }}</span>\r\n </div>\r\n <h3 class=\"lp-step-title\">{{ step.title }}</h3>\r\n <p class=\"lp-step-desc\">{{ step.description }}</p>\r\n <!-- Connector line between steps, not after last -->\r\n <div *ngIf=\"i < config.workflow!.steps.length - 1\" class=\"lp-step-connector\"></div>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== CUSTOM SECTION SLOT ===== -->\r\n<!-- Consumer projects app-specific sections here (fleet board, benefits rows, etc.)\r\n using [lpCustomSection] attribute on a wrapper element -->\r\n<ng-content select=\"[lpCustomSection]\"></ng-content>\r\n\r\n<!-- ===== MODULES (optional) ===== -->\r\n<section *ngIf=\"config.modules\" id=\"lp-modules\" class=\"lp-section lp-section-alt lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-section-header\">\r\n <h2>{{ config.modules!.title }}</h2>\r\n <p>{{ config.modules!.subtitle }}</p>\r\n </div>\r\n <div class=\"lp-modules-grid\">\r\n <div *ngFor=\"let group of config.modules!.groups\" class=\"lp-module-group\">\r\n <div class=\"lp-module-group-label\">{{ group.category }}</div>\r\n <div class=\"lp-module-tiles\">\r\n <div *ngFor=\"let tile of group.tiles\" class=\"lp-module-tile\">\r\n <span class=\"material-icons\">{{ tile.icon }}</span>\r\n <span>{{ tile.name }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== SECURITY (optional \u2014 dark background) ===== -->\r\n<section *ngIf=\"config.security\" class=\"lp-section lp-section-dark lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-section-header lp-header-light\">\r\n <h2>{{ config.security!.title }}</h2>\r\n <p>{{ config.security!.subtitle }}</p>\r\n </div>\r\n <div class=\"lp-security-grid\">\r\n <div *ngFor=\"let item of config.security!.items\" class=\"lp-security-card\">\r\n <div class=\"lp-security-icon\">\r\n <span class=\"material-icons\">{{ item.icon }}</span>\r\n </div>\r\n <h3>{{ item.title }}</h3>\r\n <p>{{ item.description }}</p>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== TESTIMONIALS (optional) ===== -->\r\n<section *ngIf=\"config.testimonials\" class=\"lp-section lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-section-header\">\r\n <h2>{{ config.testimonials!.title }}</h2>\r\n <p *ngIf=\"config.testimonials!.subtitle\">{{ config.testimonials!.subtitle }}</p>\r\n </div>\r\n <div class=\"lp-testimonials-grid\">\r\n <div *ngFor=\"let item of config.testimonials!.items\" class=\"lp-testimonial-card\">\r\n <div class=\"lp-stars\">\r\n <span class=\"material-icons\">star</span>\r\n <span class=\"material-icons\">star</span>\r\n <span class=\"material-icons\">star</span>\r\n <span class=\"material-icons\">star</span>\r\n <span class=\"material-icons\">star</span>\r\n </div>\r\n <p class=\"lp-testimonial-quote\">\"{{ item.quote }}\"</p>\r\n <div class=\"lp-testimonial-author\">\r\n <div class=\"lp-author-avatar\">{{ item.authorInitials }}</div>\r\n <div>\r\n <div class=\"lp-author-name\">{{ item.authorName }}</div>\r\n <div class=\"lp-author-role\">{{ item.authorRole }}</div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== PRICING (optional) ===== -->\r\n<section *ngIf=\"config.pricing\" id=\"lp-pricing\" class=\"lp-section lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-section-header\">\r\n <h2>{{ config.pricing!.title }}</h2>\r\n <p *ngIf=\"config.pricing!.subtitle\">{{ config.pricing!.subtitle }}</p>\r\n </div>\r\n <div class=\"lp-pricing-grid\">\r\n <div *ngFor=\"let plan of config.pricing!.plans\"\r\n class=\"lp-pricing-card\"\r\n [class.lp-pricing-popular]=\"plan.popular\">\r\n <div *ngIf=\"plan.popular\" class=\"lp-popular-badge\">Most Popular</div>\r\n <div class=\"lp-pricing-header\">\r\n <h3>{{ plan.name }}</h3>\r\n <div class=\"lp-pricing-price\">\r\n <span class=\"lp-price-amount\">{{ plan.price }}</span>\r\n <span *ngIf=\"plan.period\" class=\"lp-price-period\">{{ plan.period }}</span>\r\n </div>\r\n <p class=\"lp-pricing-desc\">{{ plan.description }}</p>\r\n </div>\r\n <ul class=\"lp-pricing-features\">\r\n <li *ngFor=\"let feature of plan.features\">{{ feature }}</li>\r\n </ul>\r\n <!-- Popular plans get primary button, others get outline -->\r\n <button *ngIf=\"plan.popular\"\r\n class=\"lp-btn lp-btn-primary lp-btn-block\"\r\n (click)=\"plan.ctaRoute ? navigateTo(plan.ctaRoute) : scrollToSection(plan.ctaSection || '')\">\r\n {{ plan.ctaLabel }}\r\n </button>\r\n <button *ngIf=\"!plan.popular\"\r\n class=\"lp-btn lp-btn-outline lp-btn-block\"\r\n (click)=\"plan.ctaRoute ? navigateTo(plan.ctaRoute) : scrollToSection(plan.ctaSection || '')\">\r\n {{ plan.ctaLabel }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== FINAL CTA BANNER (optional) ===== -->\r\n<section *ngIf=\"config.finalCta\" class=\"lp-final-cta lp-animate\">\r\n <div class=\"lp-container lp-cta-content\">\r\n <h2>{{ config.finalCta!.headline }}</h2>\r\n <p>{{ config.finalCta!.subtitle }}</p>\r\n <div class=\"lp-cta-buttons\">\r\n <button class=\"lp-btn lp-btn-white lp-btn-lg\"\r\n (click)=\"navigateTo(config.finalCta!.primaryCTARoute || 'signup')\">\r\n {{ config.finalCta!.primaryCTA }}\r\n </button>\r\n <button *ngIf=\"config.finalCta!.secondaryCTA\"\r\n class=\"lp-btn lp-btn-outline-white lp-btn-lg\"\r\n (click)=\"scrollToSection(config.finalCta!.secondaryCTASection || '')\">\r\n {{ config.finalCta!.secondaryCTA }}\r\n </button>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== FOOTER ===== -->\r\n<footer class=\"lp-footer\" id=\"lp-footer\">\r\n <div class=\"lp-container\">\r\n\r\n <!-- Rich multi-column footer when footerColumns provided -->\r\n <div *ngIf=\"config.footerColumns && config.footerColumns.length; else simpleFooter\" class=\"lp-footer-grid\">\r\n <!-- Brand column -->\r\n <div class=\"lp-footer-brand-col\">\r\n <!-- Custom SVG footer brand OR default img+name -->\r\n <ng-container *ngIf=\"config.footerBrand; else defaultFooterBrand\">\r\n <div class=\"lp-footer-brand-custom\">\r\n <span class=\"lp-footer-brand-svg\" [innerHTML]=\"safeFooterBrandSvg\"></span>\r\n <div *ngIf=\"config.footerBrand.wordmarkLine1\" class=\"lp-footer-brand-wordmark\">\r\n <span class=\"lp-footer-wordmark-line1\">{{ config.footerBrand.wordmarkLine1 }}</span>\r\n <span *ngIf=\"config.footerBrand.wordmarkLine2\" class=\"lp-footer-wordmark-line2\">{{ config.footerBrand.wordmarkLine2 }}</span>\r\n </div>\r\n </div>\r\n </ng-container>\r\n <ng-template #defaultFooterBrand>\r\n <div class=\"lp-footer-brand-default\">\r\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-footer-logo\">\r\n <span class=\"lp-footer-name\">{{ config.appName }}</span>\r\n </div>\r\n </ng-template>\r\n <p *ngIf=\"config.footerTagline\" class=\"lp-footer-tagline\">{{ config.footerTagline }}</p>\r\n </div>\r\n <!-- Link columns -->\r\n <div *ngFor=\"let col of config.footerColumns\" class=\"lp-footer-col\">\r\n <h4>{{ col.title }}</h4>\r\n <a *ngFor=\"let link of col.links\"\r\n (click)=\"link.section ? scrollToSection(link.section) : (link.route ? navigateTo(link.route) : null)\">\r\n {{ link.label }}\r\n </a>\r\n </div>\r\n </div>\r\n\r\n <!-- Simple single-row footer (default) -->\r\n <ng-template #simpleFooter>\r\n <div class=\"lp-footer-inner\">\r\n <div class=\"lp-footer-brand\">\r\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-footer-logo\">\r\n <span class=\"lp-footer-name\">{{ config.appName }}</span>\r\n </div>\r\n <div class=\"lp-footer-links\">\r\n <a (click)=\"navigateTo('login')\">Log in</a>\r\n <a (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">Sign up</a>\r\n </div>\r\n <div class=\"lp-footer-copy\">\r\n &copy; {{ currentYear }} {{ config.footerCompany || 'alsquare technologies' }}. All rights reserved.\r\n </div>\r\n </div>\r\n </ng-template>\r\n\r\n <!-- Footer bottom bar (shown for both layouts) -->\r\n <div *ngIf=\"config.footerColumns && config.footerColumns.length\" class=\"lp-footer-bottom\">\r\n <span>&copy; {{ currentYear }} {{ config.footerCompany || 'alsquare technologies' }}. All rights reserved.</span>\r\n </div>\r\n </div>\r\n</footer>\r\n", styles: ["@import\"https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700;800;900&display=swap\";:host{--lp-primary: #1E3A5F;--lp-primary-dark: #152C4A;--lp-primary-light: #EBF0F7;--lp-secondary: #F59E0B;--lp-secondary-light:#FEF3C7;--lp-dark: #0D1117;--lp-gray-800: #1F2937;--lp-gray-700: #374151;--lp-gray-600: #4B5563;--lp-gray-400: #9CA3AF;--lp-gray-200: #E5E7EB;--lp-gray-100: #F3F4F6;--lp-gray-50: #F9FAFB;--lp-white: #FFFFFF;--lp-radius: 12px;--lp-radius-sm: 8px;--lp-radius-lg: 20px;--lp-radius-xl: 28px;--lp-shadow-xs: 0 1px 2px rgba(0,0,0,.05);--lp-shadow: 0 1px 3px rgba(0,0,0,.07), 0 1px 2px rgba(0,0,0,.05);--lp-shadow-md: 0 4px 8px rgba(0,0,0,.06), 0 2px 4px rgba(0,0,0,.04);--lp-shadow-lg: 0 12px 28px rgba(0,0,0,.1), 0 4px 8px rgba(0,0,0,.06);--lp-shadow-xl: 0 20px 40px rgba(0,0,0,.12), 0 8px 16px rgba(0,0,0,.07);--lp-ease: cubic-bezier(.4, 0, .2, 1);--lp-spring: cubic-bezier(.34, 1.56, .64, 1);display:block;font-family:Outfit,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;color:var(--lp-gray-700);line-height:1.6;overflow-x:hidden;-webkit-font-smoothing:antialiased}.lp-container{max-width:1200px;margin:0 auto;padding:0 32px}.lp-navbar{position:fixed;top:0;left:0;right:0;z-index:1000;padding:18px 0;transition:background .3s var(--lp-ease),padding .3s var(--lp-ease),box-shadow .3s var(--lp-ease)}.lp-navbar-scrolled{background:#ffffffeb;backdrop-filter:blur(16px) saturate(180%);-webkit-backdrop-filter:blur(16px) saturate(180%);box-shadow:0 1px #0000000f,0 2px 8px #0000000a;padding:12px 0}.lp-nav-inner{display:flex;align-items:center;justify-content:space-between}.lp-nav-brand{display:flex;align-items:center;gap:10px;cursor:pointer;text-decoration:none}.lp-nav-logo{width:30px;height:30px;border-radius:7px;object-fit:contain}.lp-nav-brand-text{font-size:18px;font-weight:700;color:var(--lp-primary);letter-spacing:-.01em}.lp-nav-links{display:flex;align-items:center;gap:36px}.lp-nav-links a{font-size:14px;font-weight:500;color:var(--lp-gray-600);cursor:pointer;transition:color .2s;text-decoration:none;letter-spacing:.01em}.lp-nav-links a:hover{color:var(--lp-primary)}.lp-nav-auth{display:flex;align-items:center;gap:12px}.lp-nav-login{font-size:14px;font-weight:500;color:var(--lp-gray-600);cursor:pointer;transition:color .2s;text-decoration:none}.lp-nav-login:hover{color:var(--lp-primary)}.lp-hamburger{display:none;flex-direction:column;gap:5px;background:none;border:none;cursor:pointer;padding:4px;z-index:1001}.lp-hamburger span{display:block;width:22px;height:2px;background:var(--lp-gray-700);border-radius:2px;transition:transform .3s var(--lp-ease),opacity .3s}.lp-hamburger-open span:nth-child(1){transform:translateY(7px) rotate(45deg)}.lp-hamburger-open span:nth-child(2){opacity:0}.lp-hamburger-open span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}.lp-mobile-menu{display:none}.lp-mobile-auth{display:flex;flex-direction:column;gap:12px;margin-top:24px;padding-top:24px;border-top:1px solid var(--lp-gray-200)}.lp-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;font-family:Outfit,sans-serif;font-size:14px;font-weight:600;padding:10px 22px;border-radius:var(--lp-radius-sm);border:none;cursor:pointer;transition:all .25s var(--lp-ease);text-decoration:none;white-space:nowrap;letter-spacing:.01em}.lp-btn-primary{background:var(--lp-primary);color:var(--lp-white);box-shadow:0 1px 3px #00000026,inset 0 1px #ffffff1f}.lp-btn-primary:hover{background:var(--lp-primary-dark);transform:translateY(-1px);box-shadow:0 6px 16px #0003,inset 0 1px #ffffff1f}.lp-btn-primary:active{transform:translateY(0)}.lp-btn-outline{background:transparent;color:var(--lp-primary);border:1.5px solid var(--lp-primary)}.lp-btn-outline:hover{background:var(--lp-primary);color:var(--lp-white);transform:translateY(-1px);box-shadow:0 4px 12px #00000026}.lp-btn-sm{padding:7px 16px;font-size:13px}.lp-btn-lg{padding:14px 32px;font-size:15px;border-radius:var(--lp-radius)}.lp-btn-block{width:100%}.lp-hero-bg{position:relative;overflow:hidden;padding:140px 0 100px;background:radial-gradient(ellipse 80% 60% at 60% -10%,color-mix(in srgb,var(--lp-primary) 8%,transparent) 0%,transparent 70%),radial-gradient(ellipse 60% 50% at -10% 80%,color-mix(in srgb,var(--lp-secondary) 6%,transparent) 0%,transparent 70%),linear-gradient(160deg,var(--lp-primary-light) 0%,#FAFBFF 45%,var(--lp-secondary-light) 100%);min-height:100vh;display:flex;align-items:center}.lp-hero-bg:before{content:\"\";position:absolute;inset:0;background-image:radial-gradient(circle,rgba(0,0,0,.04) 1px,transparent 1px);background-size:32px 32px;pointer-events:none;-webkit-mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black 40%,transparent 100%);mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black 40%,transparent 100%)}.lp-hero-bg:after{content:\"\";position:absolute;width:700px;height:700px;border-radius:50%;background:color-mix(in srgb,var(--lp-primary) 5%,transparent);top:-200px;right:-150px;filter:blur(100px);pointer-events:none}.lp-hero-content{position:relative;z-index:1;text-align:center;width:100%}.lp-hero-badge{display:inline-flex;align-items:center;gap:6px;background:color-mix(in srgb,var(--lp-primary) 10%,transparent);color:var(--lp-primary);border:1px solid color-mix(in srgb,var(--lp-primary) 20%,transparent);border-radius:100px;padding:6px 16px;font-size:13px;font-weight:600;margin-bottom:20px;letter-spacing:.02em}.lp-hero-brand{margin-bottom:20px;text-align:center}.lp-hero-brand-logo{display:inline-flex;align-items:center;gap:14px;margin-bottom:12px}.lp-hero-brand-icon{height:52px;width:52px;border-radius:14px;object-fit:contain;box-shadow:0 4px 12px #0000001f}.lp-hero-brand-name{font-size:38px;font-weight:800;color:var(--lp-primary);letter-spacing:-.03em;font-family:Outfit,sans-serif}.lp-hero-divider{display:flex;align-items:center;justify-content:center;gap:10px}.lp-hero-divider-line{display:block;height:1px;width:40px;background:linear-gradient(90deg,transparent,var(--lp-primary),transparent);opacity:.3}.lp-hero-divider-dot{display:block;height:5px;width:5px;border-radius:50%;background:var(--lp-secondary);opacity:.8}.lp-hero-text{max-width:760px;margin:0 auto 64px}.lp-hero-text h1{font-family:Outfit,sans-serif;font-size:68px;font-weight:900;line-height:1.05;color:var(--lp-dark);margin:0 0 24px;letter-spacing:-.03em}.lp-gradient-text{background:linear-gradient(135deg,var(--lp-primary) 0%,var(--lp-secondary) 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}.lp-hero-subtitle{font-size:19px;color:var(--lp-gray-600);line-height:1.65;font-weight:400;max-width:560px;margin:0 auto 36px}.lp-hero-ctas{display:flex;align-items:center;justify-content:center;gap:14px;margin-bottom:32px;flex-wrap:wrap}.lp-trust-signals{display:flex;align-items:center;justify-content:center;gap:28px;flex-wrap:wrap}.lp-trust-item{display:flex;align-items:center;gap:7px;font-size:13px;font-weight:500;color:var(--lp-gray-500, #6B7280)}.lp-trust-item svg{color:var(--lp-primary);flex-shrink:0}.lp-section{padding:100px 0;background:var(--lp-white)}.lp-section-alt{background:var(--lp-gray-50)}.lp-section-header{text-align:center;margin-bottom:64px}.lp-section-header h2{font-family:Outfit,sans-serif;font-size:42px;font-weight:800;color:var(--lp-dark);margin:0 0 14px;letter-spacing:-.025em;line-height:1.15}.lp-section-header p{font-size:17px;color:var(--lp-gray-600);max-width:540px;margin:0 auto;line-height:1.65}.lp-features-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}.lp-feature-card{background:var(--lp-white);border-radius:var(--lp-radius-lg);padding:36px 28px;transition:transform .25s var(--lp-ease),box-shadow .25s var(--lp-ease);border:1px solid var(--lp-gray-200);box-shadow:var(--lp-shadow-xs);position:relative;overflow:hidden}.lp-feature-card:before{content:\"\";position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--lp-primary),var(--lp-secondary));transform:scaleX(0);transform-origin:left;transition:transform .3s var(--lp-ease);border-radius:3px 3px 0 0}.lp-feature-card:hover{transform:translateY(-5px);box-shadow:var(--lp-shadow-xl);border-color:transparent}.lp-feature-card:hover:before{transform:scaleX(1)}.lp-feature-icon{width:52px;height:52px;border-radius:var(--lp-radius-sm);display:flex;align-items:center;justify-content:center;margin-bottom:20px}.lp-feature-icon .material-icons{font-size:26px}.lp-feature-title{font-family:Outfit,sans-serif;font-size:17px;font-weight:700;color:var(--lp-dark);margin:0 0 10px;letter-spacing:-.01em}.lp-feature-desc{font-size:14px;color:var(--lp-gray-600);line-height:1.65;margin:0}.lp-steps-grid{display:flex;align-items:flex-start;justify-content:space-between;position:relative;gap:16px}.lp-step-card{flex:1;display:flex;flex-direction:column;align-items:center;text-align:center;position:relative;padding:0 8px}.lp-step-number{font-size:11px;font-weight:700;color:var(--lp-secondary);text-transform:uppercase;letter-spacing:2px;margin-bottom:10px}.lp-step-icon{width:56px;height:56px;border-radius:50%;background:var(--lp-primary);display:flex;align-items:center;justify-content:center;margin-bottom:18px;box-shadow:0 4px 14px color-mix(in srgb,var(--lp-primary) 30%,transparent);border:3px solid var(--lp-white);position:relative;z-index:1}.lp-step-icon .material-icons{font-size:24px;color:var(--lp-white)}.lp-step-title{font-family:Outfit,sans-serif;font-size:15px;font-weight:700;color:var(--lp-dark);margin:0 0 8px;letter-spacing:-.01em}.lp-step-desc{font-size:13px;color:var(--lp-gray-600);line-height:1.55;margin:0}.lp-step-connector{position:absolute;top:40px;left:60%;width:80%;height:2px;background:repeating-linear-gradient(90deg,color-mix(in srgb,var(--lp-secondary) 50%,transparent) 0px,color-mix(in srgb,var(--lp-secondary) 50%,transparent) 10px,transparent 10px,transparent 18px);pointer-events:none;z-index:0}.lp-metrics-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:0;border:1px solid var(--lp-gray-200);border-radius:var(--lp-radius-lg);overflow:hidden;background:var(--lp-white);box-shadow:var(--lp-shadow)}.lp-metric-card{text-align:center;padding:40px 24px;border-right:1px solid var(--lp-gray-200);transition:background .2s}.lp-metric-card:last-child{border-right:none}.lp-metric-card:hover{background:var(--lp-gray-50)}.lp-metric-value{font-family:Outfit,sans-serif;font-size:48px;font-weight:900;color:var(--lp-primary);line-height:1;letter-spacing:-.03em;margin-bottom:8px}.lp-metric-label{font-size:14px;font-weight:500;color:var(--lp-gray-600)}.lp-modules-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px}.lp-module-group{background:var(--lp-white);border-radius:var(--lp-radius-lg);padding:28px 28px 24px;border:1px solid var(--lp-gray-200);box-shadow:var(--lp-shadow-xs);transition:box-shadow .2s}.lp-module-group:hover{box-shadow:var(--lp-shadow-md)}.lp-module-group-label{font-family:Outfit,sans-serif;font-size:11px;font-weight:700;color:var(--lp-primary);margin-bottom:16px;text-transform:uppercase;letter-spacing:1px}.lp-module-tiles{display:flex;flex-wrap:wrap;gap:8px}.lp-module-tile{display:inline-flex;align-items:center;gap:6px;background:var(--lp-gray-50);border:1px solid var(--lp-gray-200);padding:7px 13px;border-radius:100px;font-size:12px;font-weight:600;color:var(--lp-gray-700);transition:all .2s var(--lp-ease);cursor:default}.lp-module-tile:hover{background:color-mix(in srgb,var(--lp-primary) 6%,transparent);border-color:color-mix(in srgb,var(--lp-primary) 25%,transparent);color:var(--lp-primary);transform:translateY(-1px)}.lp-module-tile .material-icons{font-size:15px;color:var(--lp-primary)}.lp-footer{background:var(--lp-dark);padding:44px 0 36px;border-top:1px solid rgba(255,255,255,.04)}.lp-footer-inner{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:20px}.lp-footer-brand{display:flex;align-items:center;gap:10px}.lp-footer-logo{width:30px;height:30px;border-radius:7px;object-fit:contain;opacity:.9}.lp-footer-name{font-family:Outfit,sans-serif;font-size:17px;font-weight:700;color:var(--lp-white);letter-spacing:-.01em}.lp-footer-links{display:flex;gap:28px}.lp-footer-links a{font-size:14px;font-weight:500;color:#ffffff80;cursor:pointer;text-decoration:none;transition:color .2s}.lp-footer-links a:hover{color:var(--lp-white)}.lp-footer-copy{font-size:13px;color:#ffffff59;width:100%;text-align:center;padding-top:20px;margin-top:4px;border-top:1px solid rgba(255,255,255,.06)}.lp-animate{opacity:0;transform:translateY(28px);transition:opacity .65s var(--lp-ease),transform .65s var(--lp-ease)}.lp-visible{opacity:1;transform:translateY(0)}.lp-visible .lp-feature-card:nth-child(1){transition-delay:0ms}.lp-visible .lp-feature-card:nth-child(2){transition-delay:60ms}.lp-visible .lp-feature-card:nth-child(3){transition-delay:.12s}.lp-visible .lp-feature-card:nth-child(4){transition-delay:.18s}.lp-visible .lp-feature-card:nth-child(5){transition-delay:.24s}.lp-visible .lp-feature-card:nth-child(6){transition-delay:.3s}.lp-visible .lp-step-card:nth-child(1){transition-delay:0ms}.lp-visible .lp-step-card:nth-child(2){transition-delay:80ms}.lp-visible .lp-step-card:nth-child(3){transition-delay:.16s}.lp-visible .lp-step-card:nth-child(4){transition-delay:.24s}.lp-visible .lp-metric-card:nth-child(1){transition-delay:0ms}.lp-visible .lp-metric-card:nth-child(2){transition-delay:60ms}.lp-visible .lp-metric-card:nth-child(3){transition-delay:.12s}.lp-visible .lp-metric-card:nth-child(4){transition-delay:.18s}@media (max-width: 1024px){.lp-hero-text h1{font-size:52px}.lp-section-header h2{font-size:36px}.lp-features-grid,.lp-metrics-grid{grid-template-columns:repeat(2,1fr)}.lp-metrics-grid .lp-metric-card:nth-child(2){border-right:none}.lp-metrics-grid .lp-metric-card:nth-child(3){border-top:1px solid var(--lp-gray-200)}.lp-metrics-grid .lp-metric-card:nth-child(4){border-top:1px solid var(--lp-gray-200)}.lp-steps-grid{flex-wrap:wrap;justify-content:center;gap:40px}.lp-step-connector{display:none}}@media (max-width: 767px){.lp-container{padding:0 20px}.lp-nav-links{display:none}.lp-hamburger{display:flex}.lp-mobile-auth{display:none}.lp-mobile-menu{display:none;position:fixed;inset:0;background:#fffffff7;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);flex-direction:column;align-items:center;justify-content:center;gap:28px;z-index:999}.lp-mobile-menu-open{display:flex}.lp-mobile-menu a{font-size:22px;font-weight:700;color:var(--lp-dark);cursor:pointer;text-decoration:none;letter-spacing:-.01em}.lp-hero-bg{padding:100px 0 60px;min-height:auto}.lp-hero-text h1{font-size:38px}.lp-hero-brand-name{font-size:28px}.lp-hero-subtitle{font-size:16px}.lp-hero-ctas{flex-direction:column}.lp-hero-ctas .lp-btn{width:100%;justify-content:center}.lp-trust-signals{gap:16px}.lp-section{padding:72px 0}.lp-section-header{margin-bottom:40px}.lp-section-header h2{font-size:28px}.lp-features-grid{grid-template-columns:1fr}.lp-metrics-grid{grid-template-columns:repeat(2,1fr)}.lp-metrics-grid .lp-metric-card{border-right:none;border-bottom:1px solid var(--lp-gray-200)}.lp-metrics-grid .lp-metric-card:last-child{border-bottom:none}.lp-metric-value{font-size:36px}.lp-modules-grid{grid-template-columns:1fr}.lp-steps-grid{flex-direction:column;align-items:center;gap:32px}.lp-step-connector{display:none}.lp-footer-inner{flex-direction:column;align-items:center;text-align:center}}.lp-nav-brand-svg{display:flex;align-items:center;width:36px;height:36px;flex-shrink:0}.lp-nav-brand-svg ::ng-deep svg{width:36px;height:36px;display:block}.lp-nav-brand-wordmark{display:flex;flex-direction:column;line-height:1.1;margin-left:8px}.lp-nav-wordmark-line1{font-size:15px;font-weight:800;color:#1a3a7a;letter-spacing:-.01em;font-family:Outfit,sans-serif}.lp-nav-wordmark-line2{font-size:9px;font-weight:600;letter-spacing:.12em;color:#e8a020;text-transform:uppercase}.lp-hero-bg{position:relative}.lp-hero-watermark{position:absolute;inset:0;display:flex;align-items:flex-start;justify-content:center;padding-top:40px;pointer-events:none;z-index:1}.lp-hero-watermark-inner{width:700px;height:700px;opacity:.04;overflow:hidden;flex-shrink:0}@media (min-width: 768px){.lp-hero-watermark-inner{width:800px;height:800px}}.lp-hero-content{display:flex;flex-direction:column;align-items:center}.lp-hero-main{z-index:1;width:100%}.lp-hero-visual{position:relative;z-index:1;width:100%;max-width:900px;margin:48px auto 0}.lp-workflow-road{position:relative;display:flex;justify-content:space-between;align-items:flex-start;padding:48px 0 24px;gap:16px}.lp-road-line{position:absolute;top:72px;left:6%;right:6%;height:3px;background:linear-gradient(90deg,var(--lp-primary-light),var(--lp-primary),var(--lp-primary-light));border-radius:2px;opacity:.4}.lp-waypoint{flex:1;display:flex;flex-direction:column;align-items:center;text-align:center;position:relative;z-index:1}.lp-waypoint-marker{width:52px;height:52px;border-radius:50%;background:var(--lp-primary);color:#fff;display:flex;align-items:center;justify-content:center;margin-bottom:12px;box-shadow:0 4px 16px color-mix(in srgb,var(--lp-primary) 35%,transparent);transition:transform .25s cubic-bezier(.34,1.56,.64,1)}.lp-waypoint:hover .lp-waypoint-marker{transform:translateY(-4px) scale(1.08)}.lp-waypoint-marker .material-icons{font-size:22px}.lp-waypoint-number{font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--lp-primary);margin-bottom:6px;opacity:.7}.lp-waypoint-title{font-size:14px;font-weight:700;color:var(--lp-dark);margin-bottom:6px;font-family:Outfit,sans-serif}.lp-waypoint-desc{font-size:12px;color:var(--lp-gray-600);line-height:1.5;max-width:140px}.lp-section-dark{background:var(--lp-dark);color:#fff}.lp-section-dark.lp-section{padding:96px 0}.lp-header-light h2{color:#fff}.lp-header-light p{color:#ffffffa6}.lp-security-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:32px}.lp-security-card{background:#ffffff0d;border:1px solid rgba(255,255,255,.08);border-radius:20px;padding:32px 28px;transition:background .25s ease}.lp-security-card:hover{background:#ffffff14}.lp-security-icon{width:52px;height:52px;border-radius:14px;background:#ffffff1a;display:flex;align-items:center;justify-content:center;margin-bottom:20px;color:#fff}.lp-security-icon .material-icons{font-size:24px}.lp-security-card h3{font-size:18px;font-weight:700;color:#fff;margin:0 0 12px;font-family:Outfit,sans-serif}.lp-security-card p{font-size:14px;color:#fff9;line-height:1.65;margin:0}.lp-testimonials-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:28px}.lp-testimonial-card{background:#fff;border:1px solid var(--lp-gray-200);border-radius:20px;padding:32px 28px;display:flex;flex-direction:column;gap:16px;box-shadow:0 1px 3px #00000012,0 1px 2px #0000000d;transition:box-shadow .25s ease,transform .25s ease}.lp-testimonial-card:hover{box-shadow:0 4px 8px #0000000f;transform:translateY(-2px)}.lp-stars{display:flex;gap:2px;color:var(--lp-secondary)}.lp-stars .material-icons{font-size:18px}.lp-testimonial-quote{font-size:15px;color:var(--lp-gray-700);line-height:1.65;font-style:italic;margin:0;flex:1}.lp-testimonial-author{display:flex;align-items:center;gap:12px}.lp-author-avatar{width:44px;height:44px;border-radius:50%;background:var(--lp-primary);color:#fff;font-size:14px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-family:Outfit,sans-serif}.lp-author-name{font-size:14px;font-weight:700;color:var(--lp-dark)}.lp-author-role{font-size:12px;color:var(--lp-gray-400)}.lp-pricing-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:28px;align-items:start}.lp-pricing-card{background:#fff;border:1px solid var(--lp-gray-200);border-radius:20px;padding:36px 28px 28px;position:relative;display:flex;flex-direction:column;box-shadow:0 1px 3px #00000012,0 1px 2px #0000000d;transition:box-shadow .25s ease,transform .25s ease}.lp-pricing-card:hover{box-shadow:0 4px 8px #0000000f;transform:translateY(-3px)}.lp-pricing-popular{border-color:var(--lp-primary);box-shadow:0 0 0 2px var(--lp-primary);transform:scale(1.03)}.lp-pricing-popular:hover{transform:scale(1.03) translateY(-3px)}.lp-popular-badge{position:absolute;top:-12px;left:50%;transform:translate(-50%);background:var(--lp-primary);color:#fff;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;padding:4px 14px;border-radius:100px;white-space:nowrap;font-family:Outfit,sans-serif}.lp-pricing-header{margin-bottom:24px}.lp-pricing-header h3{font-size:20px;font-weight:800;color:var(--lp-dark);margin:0 0 12px;font-family:Outfit,sans-serif}.lp-pricing-price{display:flex;align-items:baseline;gap:4px;margin-bottom:8px}.lp-price-amount{font-size:40px;font-weight:900;color:var(--lp-primary);font-family:Outfit,sans-serif;line-height:1}.lp-price-period{font-size:16px;color:var(--lp-gray-600)}.lp-pricing-desc{font-size:13px;color:var(--lp-gray-600);margin:0}.lp-pricing-features{list-style:none;padding:0;margin:0 0 28px;display:flex;flex-direction:column;gap:10px;flex:1}.lp-pricing-features li{font-size:14px;color:var(--lp-gray-700);padding-left:20px;position:relative}.lp-pricing-features li:before{content:\"\\2713\";position:absolute;left:0;color:var(--lp-primary);font-weight:700}.lp-btn-block{width:100%;justify-content:center;text-align:center}.lp-final-cta{background:linear-gradient(135deg,var(--lp-primary-dark, #152C4A) 0%,var(--lp-primary) 100%);padding:96px 0}.lp-cta-content{text-align:center}.lp-cta-content h2{font-size:40px;font-weight:900;color:#fff;margin:0 0 16px;font-family:Outfit,sans-serif;letter-spacing:-.02em}.lp-cta-content p{font-size:18px;color:#ffffffbf;margin:0 auto 36px;max-width:560px}.lp-cta-buttons{display:flex;gap:16px;justify-content:center;flex-wrap:wrap}.lp-btn-white{background:#fff;color:var(--lp-primary);border:2px solid white;font-weight:700}.lp-btn-white:hover{background:#ffffffe6;transform:translateY(-1px)}.lp-btn-outline-white{background:transparent;color:#fff;border:2px solid rgba(255,255,255,.5);font-weight:700}.lp-btn-outline-white:hover{background:#ffffff1a;border-color:#fff;transform:translateY(-1px)}.lp-footer-grid{display:grid;grid-template-columns:2fr 1fr 1fr 1fr;gap:48px;padding:64px 0 48px}.lp-footer-brand-col{display:flex;flex-direction:column;gap:16px}.lp-footer-brand-custom{display:flex;align-items:center;gap:10px}.lp-footer-brand-svg{display:flex;align-items:center;width:36px;height:36px;flex-shrink:0}.lp-footer-brand-svg ::ng-deep svg{width:36px;height:36px;display:block}.lp-footer-brand-wordmark{display:flex;flex-direction:column;line-height:1.1}.lp-footer-wordmark-line1{font-size:15px;font-weight:800;color:#fff;font-family:Outfit,sans-serif}.lp-footer-wordmark-line2{font-size:9px;font-weight:600;letter-spacing:.12em;color:#e8a020;text-transform:uppercase}.lp-footer-brand-default{display:flex;align-items:center;gap:10px}.lp-footer-tagline{font-size:14px;color:#ffffff80;line-height:1.6;margin:0;max-width:280px}.lp-footer-col{display:flex;flex-direction:column;gap:12px}.lp-footer-col h4{font-size:13px;font-weight:700;color:#fff;margin:0 0 4px;letter-spacing:.04em;text-transform:uppercase;font-family:Outfit,sans-serif}.lp-footer-col a{font-size:14px;color:#ffffff80;cursor:pointer;text-decoration:none;transition:color .2s}.lp-footer-col a:hover{color:#fff}.lp-footer-bottom{border-top:1px solid rgba(255,255,255,.08);padding:24px 0;font-size:13px;color:#ffffff59}@media (max-width: 768px){.lp-hero-visual{max-width:100%;margin-top:32px}.lp-workflow-road{flex-direction:column;align-items:center;gap:32px}.lp-road-line{display:none}.lp-waypoint-desc{max-width:260px}.lp-security-grid,.lp-testimonials-grid,.lp-pricing-grid{grid-template-columns:1fr}.lp-pricing-popular{transform:none}.lp-pricing-popular:hover{transform:translateY(-3px)}.lp-cta-content h2{font-size:28px}.lp-footer-grid{grid-template-columns:1fr 1fr;gap:32px;padding:48px 0 32px}}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] }); }
17245
17245
  }
17246
17246
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: SpaLandingComponent, decorators: [{
17247
17247
  type: Component,
17248
- args: [{ selector: 'spa-landing', standalone: false, template: "<!-- ===== NAVBAR ===== -->\n<nav class=\"lp-navbar\" [class.lp-navbar-scrolled]=\"navScrolled\">\n <div class=\"lp-container lp-nav-inner\">\n <!-- Brand: custom SVG override OR default img+name -->\n <div class=\"lp-nav-brand\" (click)=\"scrollToSection('lp-hero')\">\n <ng-container *ngIf=\"config.navBrand; else defaultNavBrand\">\n <span class=\"lp-nav-brand-svg\" [innerHTML]=\"safeNavBrandSvg\"></span>\n <div *ngIf=\"config.navBrand.wordmarkLine1\" class=\"lp-nav-brand-wordmark\">\n <span class=\"lp-nav-wordmark-line1\">{{ config.navBrand.wordmarkLine1 }}</span>\n <span *ngIf=\"config.navBrand.wordmarkLine2\" class=\"lp-nav-wordmark-line2\">{{ config.navBrand.wordmarkLine2 }}</span>\n </div>\n </ng-container>\n <ng-template #defaultNavBrand>\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-nav-logo\">\n <span class=\"lp-nav-brand-text\">{{ config.appName }}</span>\n </ng-template>\n </div>\n <!-- Desktop nav links -->\n <div class=\"lp-nav-links\">\n <a *ngFor=\"let link of config.navLinks\" (click)=\"scrollToSection(link.target)\">{{ link.label }}</a>\n </div>\n <!-- Mobile menu overlay -->\n <div class=\"lp-mobile-menu\" [class.lp-mobile-menu-open]=\"mobileMenuOpen\">\n <a *ngFor=\"let link of config.navLinks\" (click)=\"scrollToSection(link.target)\">{{ link.label }}</a>\n <div class=\"lp-mobile-auth\">\n <a class=\"lp-nav-login\" (click)=\"navigateTo('login')\">Log in</a>\n <button class=\"lp-btn lp-btn-primary\" (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">Get Started</button>\n </div>\n </div>\n <!-- Desktop auth -->\n <div class=\"lp-nav-auth\">\n <a class=\"lp-nav-login\" (click)=\"navigateTo('login')\">Log in</a>\n <button class=\"lp-btn lp-btn-primary lp-btn-sm\" (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">Get Started</button>\n </div>\n <!-- Hamburger for mobile -->\n <button class=\"lp-hamburger\" (click)=\"toggleMobileMenu()\" [class.lp-hamburger-open]=\"mobileMenuOpen\">\n <span></span><span></span><span></span>\n </button>\n </div>\n</nav>\n\n<!-- ===== HERO ===== -->\n<section id=\"lp-hero\" class=\"lp-hero-bg\" [class.lp-hero-split]=\"config.hero.layout === 'split'\">\n <!-- Optional faded watermark SVG (e.g. company logo) -->\n <!-- Changed: wrapper div gets Angular scoping attribute; inner div holds innerHTML so svg size CSS applies to scoped .lp-hero-watermark-inner -->\n <div *ngIf=\"config.hero.watermarkSvg\" class=\"lp-hero-watermark\" aria-hidden=\"true\">\n <div class=\"lp-hero-watermark-inner\" [innerHTML]=\"safeHeroWatermarkSvg\"></div>\n </div>\n\n <div class=\"lp-container lp-hero-content\">\n <!-- Left / centered column: brand, headline, CTAs -->\n <div class=\"lp-hero-main\">\n <!-- App brand: logo + name + decorative divider -->\n <div class=\"lp-hero-brand\">\n <div class=\"lp-hero-brand-logo\">\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-hero-brand-icon\">\n <span class=\"lp-hero-brand-name\">{{ config.appName }}</span>\n </div>\n <div class=\"lp-hero-divider\" aria-hidden=\"true\">\n <span class=\"lp-hero-divider-line\"></span>\n <span class=\"lp-hero-divider-dot\"></span>\n <span class=\"lp-hero-divider-line\"></span>\n </div>\n </div>\n <!-- Hero text: headline, subtitle, CTAs, trust signals -->\n <div class=\"lp-hero-text\">\n <div *ngIf=\"config.hero.badge\" class=\"lp-hero-badge\">{{ config.hero.badge }}</div>\n <h1>{{ config.hero.headline }} <span class=\"lp-gradient-text\">{{ config.hero.gradientText }}</span></h1>\n <p class=\"lp-hero-subtitle\">{{ config.hero.subtitle }}</p>\n <div class=\"lp-hero-ctas\">\n <button class=\"lp-btn lp-btn-primary lp-btn-lg\" (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">\n {{ config.hero.primaryCTA }}\n </button>\n <button class=\"lp-btn lp-btn-outline lp-btn-lg\" (click)=\"scrollToSection(config.hero.secondaryCTASection)\">\n {{ config.hero.secondaryCTA }}\n </button>\n </div>\n <!-- Trust signals with checkmark SVG icons -->\n <div class=\"lp-trust-signals\">\n <div *ngFor=\"let signal of config.hero.trustSignals\" class=\"lp-trust-item\">\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\">\n <path d=\"M20 6L9 17l-5-5\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\n </svg>\n <span>{{ signal }}</span>\n </div>\n </div>\n </div>\n </div>\n <!-- App visual: always rendered when projected content exists (stacked below hero text) -->\n <!-- Changed: removed *ngIf so visual always renders below text; layout is always stacked/column -->\n <div class=\"lp-hero-visual\">\n <ng-content select=\"[lpHeroVisual]\"></ng-content>\n </div>\n </div>\n</section>\n\n<!-- ===== METRICS (optional) ===== -->\n<section *ngIf=\"config.metrics\" class=\"lp-section lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-metrics-grid\">\n <div *ngFor=\"let metric of config.metrics\" class=\"lp-metric-card\">\n <div class=\"lp-metric-value\">{{ metric.value }}</div>\n <div class=\"lp-metric-label\">{{ metric.label }}</div>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== FEATURES ===== -->\n<section id=\"lp-features\" class=\"lp-section lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-section-header\">\n <h2>{{ config.features.title }}</h2>\n <p>{{ config.features.subtitle }}</p>\n </div>\n <div class=\"lp-features-grid\">\n <div *ngFor=\"let feature of config.features.items\" class=\"lp-feature-card\">\n <!-- Icon tint: hex + '20' = ~12% opacity background -->\n <div class=\"lp-feature-icon\"\n [style.background]=\"iconBg(feature.color)\"\n [style.color]=\"feature.color\">\n <span class=\"material-icons\">{{ feature.icon }}</span>\n </div>\n <h3 class=\"lp-feature-title\">{{ feature.title }}</h3>\n <p class=\"lp-feature-desc\">{{ feature.description }}</p>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== WORKFLOW (optional) ===== -->\n<section *ngIf=\"config.workflow\" id=\"lp-workflow\" class=\"lp-section lp-section-alt lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-section-header\">\n <h2>{{ config.workflow!.title }}</h2>\n <p *ngIf=\"config.workflow!.subtitle\">{{ config.workflow!.subtitle }}</p>\n </div>\n\n <!-- Road metaphor layout: horizontal road line with waypoints -->\n <div *ngIf=\"config.workflow!.style === 'road'\" class=\"lp-workflow-road\">\n <div class=\"lp-road-line\"></div>\n <div *ngFor=\"let step of config.workflow!.steps\" class=\"lp-waypoint\">\n <div class=\"lp-waypoint-marker\">\n <span class=\"material-icons\">{{ step.icon }}</span>\n </div>\n <div class=\"lp-waypoint-number\">Step {{ step.number }}</div>\n <div class=\"lp-waypoint-title\">{{ step.title }}</div>\n <div class=\"lp-waypoint-desc\">{{ step.description }}</div>\n </div>\n </div>\n\n <!-- Default steps grid layout -->\n <div *ngIf=\"!config.workflow!.style || config.workflow!.style === 'steps'\" class=\"lp-steps-grid\">\n <div *ngFor=\"let step of config.workflow!.steps; let i = index\" class=\"lp-step-card\">\n <div class=\"lp-step-number\">{{ step.number }}</div>\n <div class=\"lp-step-icon\">\n <span class=\"material-icons\">{{ step.icon }}</span>\n </div>\n <h3 class=\"lp-step-title\">{{ step.title }}</h3>\n <p class=\"lp-step-desc\">{{ step.description }}</p>\n <!-- Connector line between steps, not after last -->\n <div *ngIf=\"i < config.workflow!.steps.length - 1\" class=\"lp-step-connector\"></div>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== CUSTOM SECTION SLOT ===== -->\n<!-- Consumer projects app-specific sections here (fleet board, benefits rows, etc.)\n using [lpCustomSection] attribute on a wrapper element -->\n<ng-content select=\"[lpCustomSection]\"></ng-content>\n\n<!-- ===== MODULES (optional) ===== -->\n<section *ngIf=\"config.modules\" id=\"lp-modules\" class=\"lp-section lp-section-alt lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-section-header\">\n <h2>{{ config.modules!.title }}</h2>\n <p>{{ config.modules!.subtitle }}</p>\n </div>\n <div class=\"lp-modules-grid\">\n <div *ngFor=\"let group of config.modules!.groups\" class=\"lp-module-group\">\n <div class=\"lp-module-group-label\">{{ group.category }}</div>\n <div class=\"lp-module-tiles\">\n <div *ngFor=\"let tile of group.tiles\" class=\"lp-module-tile\">\n <span class=\"material-icons\">{{ tile.icon }}</span>\n <span>{{ tile.name }}</span>\n </div>\n </div>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== SECURITY (optional \u2014 dark background) ===== -->\n<section *ngIf=\"config.security\" class=\"lp-section lp-section-dark lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-section-header lp-header-light\">\n <h2>{{ config.security!.title }}</h2>\n <p>{{ config.security!.subtitle }}</p>\n </div>\n <div class=\"lp-security-grid\">\n <div *ngFor=\"let item of config.security!.items\" class=\"lp-security-card\">\n <div class=\"lp-security-icon\">\n <span class=\"material-icons\">{{ item.icon }}</span>\n </div>\n <h3>{{ item.title }}</h3>\n <p>{{ item.description }}</p>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== TESTIMONIALS (optional) ===== -->\n<section *ngIf=\"config.testimonials\" class=\"lp-section lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-section-header\">\n <h2>{{ config.testimonials!.title }}</h2>\n <p *ngIf=\"config.testimonials!.subtitle\">{{ config.testimonials!.subtitle }}</p>\n </div>\n <div class=\"lp-testimonials-grid\">\n <div *ngFor=\"let item of config.testimonials!.items\" class=\"lp-testimonial-card\">\n <div class=\"lp-stars\">\n <span class=\"material-icons\">star</span>\n <span class=\"material-icons\">star</span>\n <span class=\"material-icons\">star</span>\n <span class=\"material-icons\">star</span>\n <span class=\"material-icons\">star</span>\n </div>\n <p class=\"lp-testimonial-quote\">\"{{ item.quote }}\"</p>\n <div class=\"lp-testimonial-author\">\n <div class=\"lp-author-avatar\">{{ item.authorInitials }}</div>\n <div>\n <div class=\"lp-author-name\">{{ item.authorName }}</div>\n <div class=\"lp-author-role\">{{ item.authorRole }}</div>\n </div>\n </div>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== PRICING (optional) ===== -->\n<section *ngIf=\"config.pricing\" id=\"lp-pricing\" class=\"lp-section lp-animate\">\n <div class=\"lp-container\">\n <div class=\"lp-section-header\">\n <h2>{{ config.pricing!.title }}</h2>\n <p *ngIf=\"config.pricing!.subtitle\">{{ config.pricing!.subtitle }}</p>\n </div>\n <div class=\"lp-pricing-grid\">\n <div *ngFor=\"let plan of config.pricing!.plans\"\n class=\"lp-pricing-card\"\n [class.lp-pricing-popular]=\"plan.popular\">\n <div *ngIf=\"plan.popular\" class=\"lp-popular-badge\">Most Popular</div>\n <div class=\"lp-pricing-header\">\n <h3>{{ plan.name }}</h3>\n <div class=\"lp-pricing-price\">\n <span class=\"lp-price-amount\">{{ plan.price }}</span>\n <span *ngIf=\"plan.period\" class=\"lp-price-period\">{{ plan.period }}</span>\n </div>\n <p class=\"lp-pricing-desc\">{{ plan.description }}</p>\n </div>\n <ul class=\"lp-pricing-features\">\n <li *ngFor=\"let feature of plan.features\">{{ feature }}</li>\n </ul>\n <!-- Popular plans get primary button, others get outline -->\n <button *ngIf=\"plan.popular\"\n class=\"lp-btn lp-btn-primary lp-btn-block\"\n (click)=\"plan.ctaRoute ? navigateTo(plan.ctaRoute) : scrollToSection(plan.ctaSection || '')\">\n {{ plan.ctaLabel }}\n </button>\n <button *ngIf=\"!plan.popular\"\n class=\"lp-btn lp-btn-outline lp-btn-block\"\n (click)=\"plan.ctaRoute ? navigateTo(plan.ctaRoute) : scrollToSection(plan.ctaSection || '')\">\n {{ plan.ctaLabel }}\n </button>\n </div>\n </div>\n </div>\n</section>\n\n<!-- ===== FINAL CTA BANNER (optional) ===== -->\n<section *ngIf=\"config.finalCta\" class=\"lp-final-cta lp-animate\">\n <div class=\"lp-container lp-cta-content\">\n <h2>{{ config.finalCta!.headline }}</h2>\n <p>{{ config.finalCta!.subtitle }}</p>\n <div class=\"lp-cta-buttons\">\n <button class=\"lp-btn lp-btn-white lp-btn-lg\"\n (click)=\"navigateTo(config.finalCta!.primaryCTARoute || 'signup')\">\n {{ config.finalCta!.primaryCTA }}\n </button>\n <button *ngIf=\"config.finalCta!.secondaryCTA\"\n class=\"lp-btn lp-btn-outline-white lp-btn-lg\"\n (click)=\"scrollToSection(config.finalCta!.secondaryCTASection || '')\">\n {{ config.finalCta!.secondaryCTA }}\n </button>\n </div>\n </div>\n</section>\n\n<!-- ===== FOOTER ===== -->\n<footer class=\"lp-footer\" id=\"lp-footer\">\n <div class=\"lp-container\">\n\n <!-- Rich multi-column footer when footerColumns provided -->\n <div *ngIf=\"config.footerColumns && config.footerColumns.length; else simpleFooter\" class=\"lp-footer-grid\">\n <!-- Brand column -->\n <div class=\"lp-footer-brand-col\">\n <!-- Custom SVG footer brand OR default img+name -->\n <ng-container *ngIf=\"config.footerBrand; else defaultFooterBrand\">\n <div class=\"lp-footer-brand-custom\">\n <span class=\"lp-footer-brand-svg\" [innerHTML]=\"safeFooterBrandSvg\"></span>\n <div *ngIf=\"config.footerBrand.wordmarkLine1\" class=\"lp-footer-brand-wordmark\">\n <span class=\"lp-footer-wordmark-line1\">{{ config.footerBrand.wordmarkLine1 }}</span>\n <span *ngIf=\"config.footerBrand.wordmarkLine2\" class=\"lp-footer-wordmark-line2\">{{ config.footerBrand.wordmarkLine2 }}</span>\n </div>\n </div>\n </ng-container>\n <ng-template #defaultFooterBrand>\n <div class=\"lp-footer-brand-default\">\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-footer-logo\">\n <span class=\"lp-footer-name\">{{ config.appName }}</span>\n </div>\n </ng-template>\n <p *ngIf=\"config.footerTagline\" class=\"lp-footer-tagline\">{{ config.footerTagline }}</p>\n </div>\n <!-- Link columns -->\n <div *ngFor=\"let col of config.footerColumns\" class=\"lp-footer-col\">\n <h4>{{ col.title }}</h4>\n <a *ngFor=\"let link of col.links\"\n (click)=\"link.section ? scrollToSection(link.section) : (link.route ? navigateTo(link.route) : null)\">\n {{ link.label }}\n </a>\n </div>\n </div>\n\n <!-- Simple single-row footer (default) -->\n <ng-template #simpleFooter>\n <div class=\"lp-footer-inner\">\n <div class=\"lp-footer-brand\">\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-footer-logo\">\n <span class=\"lp-footer-name\">{{ config.appName }}</span>\n </div>\n <div class=\"lp-footer-links\">\n <a (click)=\"navigateTo('login')\">Log in</a>\n <a (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">Sign up</a>\n </div>\n <div class=\"lp-footer-copy\">\n &copy; {{ currentYear }} {{ config.footerCompany || 'alsquare technologies' }}. All rights reserved.\n </div>\n </div>\n </ng-template>\n\n <!-- Footer bottom bar (shown for both layouts) -->\n <div *ngIf=\"config.footerColumns && config.footerColumns.length\" class=\"lp-footer-bottom\">\n <span>&copy; {{ currentYear }} {{ config.footerCompany || 'alsquare technologies' }}. All rights reserved.</span>\n </div>\n </div>\n</footer>\n", styles: ["@import\"https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700;800;900&display=swap\";:host{--lp-primary: #1E3A5F;--lp-primary-dark: #152C4A;--lp-primary-light: #EBF0F7;--lp-secondary: #F59E0B;--lp-secondary-light:#FEF3C7;--lp-dark: #0D1117;--lp-gray-800: #1F2937;--lp-gray-700: #374151;--lp-gray-600: #4B5563;--lp-gray-400: #9CA3AF;--lp-gray-200: #E5E7EB;--lp-gray-100: #F3F4F6;--lp-gray-50: #F9FAFB;--lp-white: #FFFFFF;--lp-radius: 12px;--lp-radius-sm: 8px;--lp-radius-lg: 20px;--lp-radius-xl: 28px;--lp-shadow-xs: 0 1px 2px rgba(0,0,0,.05);--lp-shadow: 0 1px 3px rgba(0,0,0,.07), 0 1px 2px rgba(0,0,0,.05);--lp-shadow-md: 0 4px 8px rgba(0,0,0,.06), 0 2px 4px rgba(0,0,0,.04);--lp-shadow-lg: 0 12px 28px rgba(0,0,0,.1), 0 4px 8px rgba(0,0,0,.06);--lp-shadow-xl: 0 20px 40px rgba(0,0,0,.12), 0 8px 16px rgba(0,0,0,.07);--lp-ease: cubic-bezier(.4, 0, .2, 1);--lp-spring: cubic-bezier(.34, 1.56, .64, 1);display:block;font-family:Outfit,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;color:var(--lp-gray-700);line-height:1.6;overflow-x:hidden;-webkit-font-smoothing:antialiased}.lp-container{max-width:1200px;margin:0 auto;padding:0 32px}.lp-navbar{position:fixed;top:0;left:0;right:0;z-index:1000;padding:18px 0;transition:background .3s var(--lp-ease),padding .3s var(--lp-ease),box-shadow .3s var(--lp-ease)}.lp-navbar-scrolled{background:#ffffffeb;backdrop-filter:blur(16px) saturate(180%);-webkit-backdrop-filter:blur(16px) saturate(180%);box-shadow:0 1px #0000000f,0 2px 8px #0000000a;padding:12px 0}.lp-nav-inner{display:flex;align-items:center;justify-content:space-between}.lp-nav-brand{display:flex;align-items:center;gap:10px;cursor:pointer;text-decoration:none}.lp-nav-logo{width:30px;height:30px;border-radius:7px;object-fit:contain}.lp-nav-brand-text{font-size:18px;font-weight:700;color:var(--lp-primary);letter-spacing:-.01em}.lp-nav-links{display:flex;align-items:center;gap:36px}.lp-nav-links a{font-size:14px;font-weight:500;color:var(--lp-gray-600);cursor:pointer;transition:color .2s;text-decoration:none;letter-spacing:.01em}.lp-nav-links a:hover{color:var(--lp-primary)}.lp-nav-auth{display:flex;align-items:center;gap:12px}.lp-nav-login{font-size:14px;font-weight:500;color:var(--lp-gray-600);cursor:pointer;transition:color .2s;text-decoration:none}.lp-nav-login:hover{color:var(--lp-primary)}.lp-hamburger{display:none;flex-direction:column;gap:5px;background:none;border:none;cursor:pointer;padding:4px;z-index:1001}.lp-hamburger span{display:block;width:22px;height:2px;background:var(--lp-gray-700);border-radius:2px;transition:transform .3s var(--lp-ease),opacity .3s}.lp-hamburger-open span:nth-child(1){transform:translateY(7px) rotate(45deg)}.lp-hamburger-open span:nth-child(2){opacity:0}.lp-hamburger-open span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}.lp-mobile-menu{display:none}.lp-mobile-auth{display:flex;flex-direction:column;gap:12px;margin-top:24px;padding-top:24px;border-top:1px solid var(--lp-gray-200)}.lp-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;font-family:Outfit,sans-serif;font-size:14px;font-weight:600;padding:10px 22px;border-radius:var(--lp-radius-sm);border:none;cursor:pointer;transition:all .25s var(--lp-ease);text-decoration:none;white-space:nowrap;letter-spacing:.01em}.lp-btn-primary{background:var(--lp-primary);color:var(--lp-white);box-shadow:0 1px 3px #00000026,inset 0 1px #ffffff1f}.lp-btn-primary:hover{background:var(--lp-primary-dark);transform:translateY(-1px);box-shadow:0 6px 16px #0003,inset 0 1px #ffffff1f}.lp-btn-primary:active{transform:translateY(0)}.lp-btn-outline{background:transparent;color:var(--lp-primary);border:1.5px solid var(--lp-primary)}.lp-btn-outline:hover{background:var(--lp-primary);color:var(--lp-white);transform:translateY(-1px);box-shadow:0 4px 12px #00000026}.lp-btn-sm{padding:7px 16px;font-size:13px}.lp-btn-lg{padding:14px 32px;font-size:15px;border-radius:var(--lp-radius)}.lp-btn-block{width:100%}.lp-hero-bg{position:relative;overflow:hidden;padding:140px 0 100px;background:radial-gradient(ellipse 80% 60% at 60% -10%,color-mix(in srgb,var(--lp-primary) 8%,transparent) 0%,transparent 70%),radial-gradient(ellipse 60% 50% at -10% 80%,color-mix(in srgb,var(--lp-secondary) 6%,transparent) 0%,transparent 70%),linear-gradient(160deg,var(--lp-primary-light) 0%,#FAFBFF 45%,var(--lp-secondary-light) 100%);min-height:100vh;display:flex;align-items:center}.lp-hero-bg:before{content:\"\";position:absolute;inset:0;background-image:radial-gradient(circle,rgba(0,0,0,.04) 1px,transparent 1px);background-size:32px 32px;pointer-events:none;-webkit-mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black 40%,transparent 100%);mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black 40%,transparent 100%)}.lp-hero-bg:after{content:\"\";position:absolute;width:700px;height:700px;border-radius:50%;background:color-mix(in srgb,var(--lp-primary) 5%,transparent);top:-200px;right:-150px;filter:blur(100px);pointer-events:none}.lp-hero-content{position:relative;z-index:1;text-align:center;width:100%}.lp-hero-badge{display:inline-flex;align-items:center;gap:6px;background:color-mix(in srgb,var(--lp-primary) 10%,transparent);color:var(--lp-primary);border:1px solid color-mix(in srgb,var(--lp-primary) 20%,transparent);border-radius:100px;padding:6px 16px;font-size:13px;font-weight:600;margin-bottom:20px;letter-spacing:.02em}.lp-hero-brand{margin-bottom:20px;text-align:center}.lp-hero-brand-logo{display:inline-flex;align-items:center;gap:14px;margin-bottom:12px}.lp-hero-brand-icon{height:52px;width:52px;border-radius:14px;object-fit:contain;box-shadow:0 4px 12px #0000001f}.lp-hero-brand-name{font-size:38px;font-weight:800;color:var(--lp-primary);letter-spacing:-.03em;font-family:Outfit,sans-serif}.lp-hero-divider{display:flex;align-items:center;justify-content:center;gap:10px}.lp-hero-divider-line{display:block;height:1px;width:40px;background:linear-gradient(90deg,transparent,var(--lp-primary),transparent);opacity:.3}.lp-hero-divider-dot{display:block;height:5px;width:5px;border-radius:50%;background:var(--lp-secondary);opacity:.8}.lp-hero-text{max-width:760px;margin:0 auto 64px}.lp-hero-text h1{font-family:Outfit,sans-serif;font-size:68px;font-weight:900;line-height:1.05;color:var(--lp-dark);margin:0 0 24px;letter-spacing:-.03em}.lp-gradient-text{background:linear-gradient(135deg,var(--lp-primary) 0%,var(--lp-secondary) 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}.lp-hero-subtitle{font-size:19px;color:var(--lp-gray-600);line-height:1.65;font-weight:400;max-width:560px;margin:0 auto 36px}.lp-hero-ctas{display:flex;align-items:center;justify-content:center;gap:14px;margin-bottom:32px;flex-wrap:wrap}.lp-trust-signals{display:flex;align-items:center;justify-content:center;gap:28px;flex-wrap:wrap}.lp-trust-item{display:flex;align-items:center;gap:7px;font-size:13px;font-weight:500;color:var(--lp-gray-500, #6B7280)}.lp-trust-item svg{color:var(--lp-primary);flex-shrink:0}.lp-section{padding:100px 0;background:var(--lp-white)}.lp-section-alt{background:var(--lp-gray-50)}.lp-section-header{text-align:center;margin-bottom:64px}.lp-section-header h2{font-family:Outfit,sans-serif;font-size:42px;font-weight:800;color:var(--lp-dark);margin:0 0 14px;letter-spacing:-.025em;line-height:1.15}.lp-section-header p{font-size:17px;color:var(--lp-gray-600);max-width:540px;margin:0 auto;line-height:1.65}.lp-features-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}.lp-feature-card{background:var(--lp-white);border-radius:var(--lp-radius-lg);padding:36px 28px;transition:transform .25s var(--lp-ease),box-shadow .25s var(--lp-ease);border:1px solid var(--lp-gray-200);box-shadow:var(--lp-shadow-xs);position:relative;overflow:hidden}.lp-feature-card:before{content:\"\";position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--lp-primary),var(--lp-secondary));transform:scaleX(0);transform-origin:left;transition:transform .3s var(--lp-ease);border-radius:3px 3px 0 0}.lp-feature-card:hover{transform:translateY(-5px);box-shadow:var(--lp-shadow-xl);border-color:transparent}.lp-feature-card:hover:before{transform:scaleX(1)}.lp-feature-icon{width:52px;height:52px;border-radius:var(--lp-radius-sm);display:flex;align-items:center;justify-content:center;margin-bottom:20px}.lp-feature-icon .material-icons{font-size:26px}.lp-feature-title{font-family:Outfit,sans-serif;font-size:17px;font-weight:700;color:var(--lp-dark);margin:0 0 10px;letter-spacing:-.01em}.lp-feature-desc{font-size:14px;color:var(--lp-gray-600);line-height:1.65;margin:0}.lp-steps-grid{display:flex;align-items:flex-start;justify-content:space-between;position:relative;gap:16px}.lp-step-card{flex:1;display:flex;flex-direction:column;align-items:center;text-align:center;position:relative;padding:0 8px}.lp-step-number{font-size:11px;font-weight:700;color:var(--lp-secondary);text-transform:uppercase;letter-spacing:2px;margin-bottom:10px}.lp-step-icon{width:56px;height:56px;border-radius:50%;background:var(--lp-primary);display:flex;align-items:center;justify-content:center;margin-bottom:18px;box-shadow:0 4px 14px color-mix(in srgb,var(--lp-primary) 30%,transparent);border:3px solid var(--lp-white);position:relative;z-index:1}.lp-step-icon .material-icons{font-size:24px;color:var(--lp-white)}.lp-step-title{font-family:Outfit,sans-serif;font-size:15px;font-weight:700;color:var(--lp-dark);margin:0 0 8px;letter-spacing:-.01em}.lp-step-desc{font-size:13px;color:var(--lp-gray-600);line-height:1.55;margin:0}.lp-step-connector{position:absolute;top:40px;left:60%;width:80%;height:2px;background:repeating-linear-gradient(90deg,color-mix(in srgb,var(--lp-secondary) 50%,transparent) 0px,color-mix(in srgb,var(--lp-secondary) 50%,transparent) 10px,transparent 10px,transparent 18px);pointer-events:none;z-index:0}.lp-metrics-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:0;border:1px solid var(--lp-gray-200);border-radius:var(--lp-radius-lg);overflow:hidden;background:var(--lp-white);box-shadow:var(--lp-shadow)}.lp-metric-card{text-align:center;padding:40px 24px;border-right:1px solid var(--lp-gray-200);transition:background .2s}.lp-metric-card:last-child{border-right:none}.lp-metric-card:hover{background:var(--lp-gray-50)}.lp-metric-value{font-family:Outfit,sans-serif;font-size:48px;font-weight:900;color:var(--lp-primary);line-height:1;letter-spacing:-.03em;margin-bottom:8px}.lp-metric-label{font-size:14px;font-weight:500;color:var(--lp-gray-600)}.lp-modules-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px}.lp-module-group{background:var(--lp-white);border-radius:var(--lp-radius-lg);padding:28px 28px 24px;border:1px solid var(--lp-gray-200);box-shadow:var(--lp-shadow-xs);transition:box-shadow .2s}.lp-module-group:hover{box-shadow:var(--lp-shadow-md)}.lp-module-group-label{font-family:Outfit,sans-serif;font-size:11px;font-weight:700;color:var(--lp-primary);margin-bottom:16px;text-transform:uppercase;letter-spacing:1px}.lp-module-tiles{display:flex;flex-wrap:wrap;gap:8px}.lp-module-tile{display:inline-flex;align-items:center;gap:6px;background:var(--lp-gray-50);border:1px solid var(--lp-gray-200);padding:7px 13px;border-radius:100px;font-size:12px;font-weight:600;color:var(--lp-gray-700);transition:all .2s var(--lp-ease);cursor:default}.lp-module-tile:hover{background:color-mix(in srgb,var(--lp-primary) 6%,transparent);border-color:color-mix(in srgb,var(--lp-primary) 25%,transparent);color:var(--lp-primary);transform:translateY(-1px)}.lp-module-tile .material-icons{font-size:15px;color:var(--lp-primary)}.lp-footer{background:var(--lp-dark);padding:44px 0 36px;border-top:1px solid rgba(255,255,255,.04)}.lp-footer-inner{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:20px}.lp-footer-brand{display:flex;align-items:center;gap:10px}.lp-footer-logo{width:30px;height:30px;border-radius:7px;object-fit:contain;opacity:.9}.lp-footer-name{font-family:Outfit,sans-serif;font-size:17px;font-weight:700;color:var(--lp-white);letter-spacing:-.01em}.lp-footer-links{display:flex;gap:28px}.lp-footer-links a{font-size:14px;font-weight:500;color:#ffffff80;cursor:pointer;text-decoration:none;transition:color .2s}.lp-footer-links a:hover{color:var(--lp-white)}.lp-footer-copy{font-size:13px;color:#ffffff59;width:100%;text-align:center;padding-top:20px;margin-top:4px;border-top:1px solid rgba(255,255,255,.06)}.lp-animate{opacity:0;transform:translateY(28px);transition:opacity .65s var(--lp-ease),transform .65s var(--lp-ease)}.lp-visible{opacity:1;transform:translateY(0)}.lp-visible .lp-feature-card:nth-child(1){transition-delay:0ms}.lp-visible .lp-feature-card:nth-child(2){transition-delay:60ms}.lp-visible .lp-feature-card:nth-child(3){transition-delay:.12s}.lp-visible .lp-feature-card:nth-child(4){transition-delay:.18s}.lp-visible .lp-feature-card:nth-child(5){transition-delay:.24s}.lp-visible .lp-feature-card:nth-child(6){transition-delay:.3s}.lp-visible .lp-step-card:nth-child(1){transition-delay:0ms}.lp-visible .lp-step-card:nth-child(2){transition-delay:80ms}.lp-visible .lp-step-card:nth-child(3){transition-delay:.16s}.lp-visible .lp-step-card:nth-child(4){transition-delay:.24s}.lp-visible .lp-metric-card:nth-child(1){transition-delay:0ms}.lp-visible .lp-metric-card:nth-child(2){transition-delay:60ms}.lp-visible .lp-metric-card:nth-child(3){transition-delay:.12s}.lp-visible .lp-metric-card:nth-child(4){transition-delay:.18s}@media (max-width: 1024px){.lp-hero-text h1{font-size:52px}.lp-section-header h2{font-size:36px}.lp-features-grid,.lp-metrics-grid{grid-template-columns:repeat(2,1fr)}.lp-metrics-grid .lp-metric-card:nth-child(2){border-right:none}.lp-metrics-grid .lp-metric-card:nth-child(3){border-top:1px solid var(--lp-gray-200)}.lp-metrics-grid .lp-metric-card:nth-child(4){border-top:1px solid var(--lp-gray-200)}.lp-steps-grid{flex-wrap:wrap;justify-content:center;gap:40px}.lp-step-connector{display:none}}@media (max-width: 767px){.lp-container{padding:0 20px}.lp-nav-links{display:none}.lp-hamburger{display:flex}.lp-mobile-auth{display:none}.lp-mobile-menu{display:none;position:fixed;inset:0;background:#fffffff7;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);flex-direction:column;align-items:center;justify-content:center;gap:28px;z-index:999}.lp-mobile-menu-open{display:flex}.lp-mobile-menu a{font-size:22px;font-weight:700;color:var(--lp-dark);cursor:pointer;text-decoration:none;letter-spacing:-.01em}.lp-hero-bg{padding:100px 0 60px;min-height:auto}.lp-hero-text h1{font-size:38px}.lp-hero-brand-name{font-size:28px}.lp-hero-subtitle{font-size:16px}.lp-hero-ctas{flex-direction:column}.lp-hero-ctas .lp-btn{width:100%;justify-content:center}.lp-trust-signals{gap:16px}.lp-section{padding:72px 0}.lp-section-header{margin-bottom:40px}.lp-section-header h2{font-size:28px}.lp-features-grid{grid-template-columns:1fr}.lp-metrics-grid{grid-template-columns:repeat(2,1fr)}.lp-metrics-grid .lp-metric-card{border-right:none;border-bottom:1px solid var(--lp-gray-200)}.lp-metrics-grid .lp-metric-card:last-child{border-bottom:none}.lp-metric-value{font-size:36px}.lp-modules-grid{grid-template-columns:1fr}.lp-steps-grid{flex-direction:column;align-items:center;gap:32px}.lp-step-connector{display:none}.lp-footer-inner{flex-direction:column;align-items:center;text-align:center}}.lp-nav-brand-svg{display:flex;align-items:center;width:36px;height:36px;flex-shrink:0}.lp-nav-brand-svg ::ng-deep svg{width:36px;height:36px;display:block}.lp-nav-brand-wordmark{display:flex;flex-direction:column;line-height:1.1;margin-left:8px}.lp-nav-wordmark-line1{font-size:15px;font-weight:800;color:#1a3a7a;letter-spacing:-.01em;font-family:Outfit,sans-serif}.lp-nav-wordmark-line2{font-size:9px;font-weight:600;letter-spacing:.12em;color:#e8a020;text-transform:uppercase}.lp-hero-bg{position:relative}.lp-hero-watermark{position:absolute;inset:0;display:flex;align-items:flex-start;justify-content:center;padding-top:40px;pointer-events:none;z-index:1}.lp-hero-watermark-inner{width:700px;height:700px;opacity:.04;overflow:hidden;flex-shrink:0}@media (min-width: 768px){.lp-hero-watermark-inner{width:800px;height:800px}}.lp-hero-content{display:flex;flex-direction:column;align-items:center}.lp-hero-main{z-index:1;width:100%}.lp-hero-visual{position:relative;z-index:1;width:100%;max-width:900px;margin:48px auto 0}.lp-workflow-road{position:relative;display:flex;justify-content:space-between;align-items:flex-start;padding:48px 0 24px;gap:16px}.lp-road-line{position:absolute;top:72px;left:6%;right:6%;height:3px;background:linear-gradient(90deg,var(--lp-primary-light),var(--lp-primary),var(--lp-primary-light));border-radius:2px;opacity:.4}.lp-waypoint{flex:1;display:flex;flex-direction:column;align-items:center;text-align:center;position:relative;z-index:1}.lp-waypoint-marker{width:52px;height:52px;border-radius:50%;background:var(--lp-primary);color:#fff;display:flex;align-items:center;justify-content:center;margin-bottom:12px;box-shadow:0 4px 16px color-mix(in srgb,var(--lp-primary) 35%,transparent);transition:transform .25s cubic-bezier(.34,1.56,.64,1)}.lp-waypoint:hover .lp-waypoint-marker{transform:translateY(-4px) scale(1.08)}.lp-waypoint-marker .material-icons{font-size:22px}.lp-waypoint-number{font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--lp-primary);margin-bottom:6px;opacity:.7}.lp-waypoint-title{font-size:14px;font-weight:700;color:var(--lp-dark);margin-bottom:6px;font-family:Outfit,sans-serif}.lp-waypoint-desc{font-size:12px;color:var(--lp-gray-600);line-height:1.5;max-width:140px}.lp-section-dark{background:var(--lp-dark);color:#fff}.lp-section-dark.lp-section{padding:96px 0}.lp-header-light h2{color:#fff}.lp-header-light p{color:#ffffffa6}.lp-security-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:32px}.lp-security-card{background:#ffffff0d;border:1px solid rgba(255,255,255,.08);border-radius:20px;padding:32px 28px;transition:background .25s ease}.lp-security-card:hover{background:#ffffff14}.lp-security-icon{width:52px;height:52px;border-radius:14px;background:#ffffff1a;display:flex;align-items:center;justify-content:center;margin-bottom:20px;color:#fff}.lp-security-icon .material-icons{font-size:24px}.lp-security-card h3{font-size:18px;font-weight:700;color:#fff;margin:0 0 12px;font-family:Outfit,sans-serif}.lp-security-card p{font-size:14px;color:#fff9;line-height:1.65;margin:0}.lp-testimonials-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:28px}.lp-testimonial-card{background:#fff;border:1px solid var(--lp-gray-200);border-radius:20px;padding:32px 28px;display:flex;flex-direction:column;gap:16px;box-shadow:0 1px 3px #00000012,0 1px 2px #0000000d;transition:box-shadow .25s ease,transform .25s ease}.lp-testimonial-card:hover{box-shadow:0 4px 8px #0000000f;transform:translateY(-2px)}.lp-stars{display:flex;gap:2px;color:var(--lp-secondary)}.lp-stars .material-icons{font-size:18px}.lp-testimonial-quote{font-size:15px;color:var(--lp-gray-700);line-height:1.65;font-style:italic;margin:0;flex:1}.lp-testimonial-author{display:flex;align-items:center;gap:12px}.lp-author-avatar{width:44px;height:44px;border-radius:50%;background:var(--lp-primary);color:#fff;font-size:14px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-family:Outfit,sans-serif}.lp-author-name{font-size:14px;font-weight:700;color:var(--lp-dark)}.lp-author-role{font-size:12px;color:var(--lp-gray-400)}.lp-pricing-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:28px;align-items:start}.lp-pricing-card{background:#fff;border:1px solid var(--lp-gray-200);border-radius:20px;padding:36px 28px 28px;position:relative;display:flex;flex-direction:column;box-shadow:0 1px 3px #00000012,0 1px 2px #0000000d;transition:box-shadow .25s ease,transform .25s ease}.lp-pricing-card:hover{box-shadow:0 4px 8px #0000000f;transform:translateY(-3px)}.lp-pricing-popular{border-color:var(--lp-primary);box-shadow:0 0 0 2px var(--lp-primary);transform:scale(1.03)}.lp-pricing-popular:hover{transform:scale(1.03) translateY(-3px)}.lp-popular-badge{position:absolute;top:-12px;left:50%;transform:translate(-50%);background:var(--lp-primary);color:#fff;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;padding:4px 14px;border-radius:100px;white-space:nowrap;font-family:Outfit,sans-serif}.lp-pricing-header{margin-bottom:24px}.lp-pricing-header h3{font-size:20px;font-weight:800;color:var(--lp-dark);margin:0 0 12px;font-family:Outfit,sans-serif}.lp-pricing-price{display:flex;align-items:baseline;gap:4px;margin-bottom:8px}.lp-price-amount{font-size:40px;font-weight:900;color:var(--lp-primary);font-family:Outfit,sans-serif;line-height:1}.lp-price-period{font-size:16px;color:var(--lp-gray-600)}.lp-pricing-desc{font-size:13px;color:var(--lp-gray-600);margin:0}.lp-pricing-features{list-style:none;padding:0;margin:0 0 28px;display:flex;flex-direction:column;gap:10px;flex:1}.lp-pricing-features li{font-size:14px;color:var(--lp-gray-700);padding-left:20px;position:relative}.lp-pricing-features li:before{content:\"\\2713\";position:absolute;left:0;color:var(--lp-primary);font-weight:700}.lp-btn-block{width:100%;justify-content:center;text-align:center}.lp-final-cta{background:linear-gradient(135deg,var(--lp-primary-dark, #152C4A) 0%,var(--lp-primary) 100%);padding:96px 0}.lp-cta-content{text-align:center}.lp-cta-content h2{font-size:40px;font-weight:900;color:#fff;margin:0 0 16px;font-family:Outfit,sans-serif;letter-spacing:-.02em}.lp-cta-content p{font-size:18px;color:#ffffffbf;margin:0 auto 36px;max-width:560px}.lp-cta-buttons{display:flex;gap:16px;justify-content:center;flex-wrap:wrap}.lp-btn-white{background:#fff;color:var(--lp-primary);border:2px solid white;font-weight:700}.lp-btn-white:hover{background:#ffffffe6;transform:translateY(-1px)}.lp-btn-outline-white{background:transparent;color:#fff;border:2px solid rgba(255,255,255,.5);font-weight:700}.lp-btn-outline-white:hover{background:#ffffff1a;border-color:#fff;transform:translateY(-1px)}.lp-footer-grid{display:grid;grid-template-columns:2fr 1fr 1fr 1fr;gap:48px;padding:64px 0 48px}.lp-footer-brand-col{display:flex;flex-direction:column;gap:16px}.lp-footer-brand-custom{display:flex;align-items:center;gap:10px}.lp-footer-brand-svg{display:flex;align-items:center;width:36px;height:36px;flex-shrink:0}.lp-footer-brand-svg ::ng-deep svg{width:36px;height:36px;display:block}.lp-footer-brand-wordmark{display:flex;flex-direction:column;line-height:1.1}.lp-footer-wordmark-line1{font-size:15px;font-weight:800;color:#fff;font-family:Outfit,sans-serif}.lp-footer-wordmark-line2{font-size:9px;font-weight:600;letter-spacing:.12em;color:#e8a020;text-transform:uppercase}.lp-footer-brand-default{display:flex;align-items:center;gap:10px}.lp-footer-tagline{font-size:14px;color:#ffffff80;line-height:1.6;margin:0;max-width:280px}.lp-footer-col{display:flex;flex-direction:column;gap:12px}.lp-footer-col h4{font-size:13px;font-weight:700;color:#fff;margin:0 0 4px;letter-spacing:.04em;text-transform:uppercase;font-family:Outfit,sans-serif}.lp-footer-col a{font-size:14px;color:#ffffff80;cursor:pointer;text-decoration:none;transition:color .2s}.lp-footer-col a:hover{color:#fff}.lp-footer-bottom{border-top:1px solid rgba(255,255,255,.08);padding:24px 0;font-size:13px;color:#ffffff59}@media (max-width: 768px){.lp-hero-visual{max-width:100%;margin-top:32px}.lp-workflow-road{flex-direction:column;align-items:center;gap:32px}.lp-road-line{display:none}.lp-waypoint-desc{max-width:260px}.lp-security-grid,.lp-testimonials-grid,.lp-pricing-grid{grid-template-columns:1fr}.lp-pricing-popular{transform:none}.lp-pricing-popular:hover{transform:translateY(-3px)}.lp-cta-content h2{font-size:28px}.lp-footer-grid{grid-template-columns:1fr 1fr;gap:32px;padding:48px 0 32px}}\n"] }]
17248
+ args: [{ selector: 'spa-landing', standalone: false, template: "<!-- ===== NAVBAR ===== -->\r\n<nav class=\"lp-navbar\" [class.lp-navbar-scrolled]=\"navScrolled\">\r\n <div class=\"lp-container lp-nav-inner\">\r\n <!-- Brand: custom SVG override OR default img+name -->\r\n <div class=\"lp-nav-brand\" (click)=\"scrollToSection('lp-hero')\">\r\n <ng-container *ngIf=\"config.navBrand; else defaultNavBrand\">\r\n <span class=\"lp-nav-brand-svg\" [innerHTML]=\"safeNavBrandSvg\"></span>\r\n <div *ngIf=\"config.navBrand.wordmarkLine1\" class=\"lp-nav-brand-wordmark\">\r\n <span class=\"lp-nav-wordmark-line1\">{{ config.navBrand.wordmarkLine1 }}</span>\r\n <span *ngIf=\"config.navBrand.wordmarkLine2\" class=\"lp-nav-wordmark-line2\">{{ config.navBrand.wordmarkLine2 }}</span>\r\n </div>\r\n </ng-container>\r\n <ng-template #defaultNavBrand>\r\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-nav-logo\">\r\n <span class=\"lp-nav-brand-text\">{{ config.appName }}</span>\r\n </ng-template>\r\n </div>\r\n <!-- Desktop nav links -->\r\n <div class=\"lp-nav-links\">\r\n <a *ngFor=\"let link of config.navLinks\" (click)=\"scrollToSection(link.target)\">{{ link.label }}</a>\r\n </div>\r\n <!-- Mobile menu overlay -->\r\n <div class=\"lp-mobile-menu\" [class.lp-mobile-menu-open]=\"mobileMenuOpen\">\r\n <a *ngFor=\"let link of config.navLinks\" (click)=\"scrollToSection(link.target)\">{{ link.label }}</a>\r\n <div class=\"lp-mobile-auth\">\r\n <a class=\"lp-nav-login\" (click)=\"navigateTo('login')\">Log in</a>\r\n <button class=\"lp-btn lp-btn-primary\" (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">Get Started</button>\r\n </div>\r\n </div>\r\n <!-- Desktop auth -->\r\n <div class=\"lp-nav-auth\">\r\n <a class=\"lp-nav-login\" (click)=\"navigateTo('login')\">Log in</a>\r\n <button class=\"lp-btn lp-btn-primary lp-btn-sm\" (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">Get Started</button>\r\n </div>\r\n <!-- Hamburger for mobile -->\r\n <button class=\"lp-hamburger\" (click)=\"toggleMobileMenu()\" [class.lp-hamburger-open]=\"mobileMenuOpen\">\r\n <span></span><span></span><span></span>\r\n </button>\r\n </div>\r\n</nav>\r\n\r\n<!-- ===== HERO ===== -->\r\n<section id=\"lp-hero\" class=\"lp-hero-bg\" [class.lp-hero-split]=\"config.hero.layout === 'split'\">\r\n <!-- Optional faded watermark SVG (e.g. company logo) -->\r\n <!-- Changed: wrapper div gets Angular scoping attribute; inner div holds innerHTML so svg size CSS applies to scoped .lp-hero-watermark-inner -->\r\n <div *ngIf=\"config.hero.watermarkSvg\" class=\"lp-hero-watermark\" aria-hidden=\"true\">\r\n <div class=\"lp-hero-watermark-inner\" [innerHTML]=\"safeHeroWatermarkSvg\"></div>\r\n </div>\r\n\r\n <div class=\"lp-container lp-hero-content\">\r\n <!-- Left / centered column: brand, headline, CTAs -->\r\n <div class=\"lp-hero-main\">\r\n <!-- App brand: logo + name + decorative divider -->\r\n <div class=\"lp-hero-brand\">\r\n <div class=\"lp-hero-brand-logo\">\r\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-hero-brand-icon\">\r\n <span class=\"lp-hero-brand-name\">{{ config.appName }}</span>\r\n </div>\r\n <div class=\"lp-hero-divider\" aria-hidden=\"true\">\r\n <span class=\"lp-hero-divider-line\"></span>\r\n <span class=\"lp-hero-divider-dot\"></span>\r\n <span class=\"lp-hero-divider-line\"></span>\r\n </div>\r\n </div>\r\n <!-- Hero text: headline, subtitle, CTAs, trust signals -->\r\n <div class=\"lp-hero-text\">\r\n <div *ngIf=\"config.hero.badge\" class=\"lp-hero-badge\">{{ config.hero.badge }}</div>\r\n <h1>{{ config.hero.headline }} <span class=\"lp-gradient-text\">{{ config.hero.gradientText }}</span></h1>\r\n <p class=\"lp-hero-subtitle\">{{ config.hero.subtitle }}</p>\r\n <div class=\"lp-hero-ctas\">\r\n <button class=\"lp-btn lp-btn-primary lp-btn-lg\" (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">\r\n {{ config.hero.primaryCTA }}\r\n </button>\r\n <button class=\"lp-btn lp-btn-outline lp-btn-lg\" (click)=\"scrollToSection(config.hero.secondaryCTASection)\">\r\n {{ config.hero.secondaryCTA }}\r\n </button>\r\n </div>\r\n <!-- Trust signals with checkmark SVG icons -->\r\n <div class=\"lp-trust-signals\">\r\n <div *ngFor=\"let signal of config.hero.trustSignals\" class=\"lp-trust-item\">\r\n <svg width=\"18\" height=\"18\" viewBox=\"0 0 24 24\" fill=\"none\">\r\n <path d=\"M20 6L9 17l-5-5\" stroke=\"currentColor\" stroke-width=\"2.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\"/>\r\n </svg>\r\n <span>{{ signal }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <!-- App visual: always rendered when projected content exists (stacked below hero text) -->\r\n <!-- Changed: removed *ngIf so visual always renders below text; layout is always stacked/column -->\r\n <div class=\"lp-hero-visual\">\r\n <ng-content select=\"[lpHeroVisual]\"></ng-content>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== METRICS (optional) ===== -->\r\n<section *ngIf=\"config.metrics\" class=\"lp-section lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-metrics-grid\">\r\n <div *ngFor=\"let metric of config.metrics\" class=\"lp-metric-card\">\r\n <div class=\"lp-metric-value\">{{ metric.value }}</div>\r\n <div class=\"lp-metric-label\">{{ metric.label }}</div>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== FEATURES ===== -->\r\n<section id=\"lp-features\" class=\"lp-section lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-section-header\">\r\n <h2>{{ config.features.title }}</h2>\r\n <p>{{ config.features.subtitle }}</p>\r\n </div>\r\n <div class=\"lp-features-grid\">\r\n <div *ngFor=\"let feature of config.features.items\" class=\"lp-feature-card\">\r\n <!-- Icon tint: hex + '20' = ~12% opacity background -->\r\n <div class=\"lp-feature-icon\"\r\n [style.background]=\"iconBg(feature.color)\"\r\n [style.color]=\"feature.color\">\r\n <span class=\"material-icons\">{{ feature.icon }}</span>\r\n </div>\r\n <h3 class=\"lp-feature-title\">{{ feature.title }}</h3>\r\n <p class=\"lp-feature-desc\">{{ feature.description }}</p>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== WORKFLOW (optional) ===== -->\r\n<section *ngIf=\"config.workflow\" id=\"lp-workflow\" class=\"lp-section lp-section-alt lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-section-header\">\r\n <h2>{{ config.workflow!.title }}</h2>\r\n <p *ngIf=\"config.workflow!.subtitle\">{{ config.workflow!.subtitle }}</p>\r\n </div>\r\n\r\n <!-- Road metaphor layout: horizontal road line with waypoints -->\r\n <div *ngIf=\"config.workflow!.style === 'road'\" class=\"lp-workflow-road\">\r\n <div class=\"lp-road-line\"></div>\r\n <div *ngFor=\"let step of config.workflow!.steps\" class=\"lp-waypoint\">\r\n <div class=\"lp-waypoint-marker\">\r\n <span class=\"material-icons\">{{ step.icon }}</span>\r\n </div>\r\n <div class=\"lp-waypoint-number\">Step {{ step.number }}</div>\r\n <div class=\"lp-waypoint-title\">{{ step.title }}</div>\r\n <div class=\"lp-waypoint-desc\">{{ step.description }}</div>\r\n </div>\r\n </div>\r\n\r\n <!-- Default steps grid layout -->\r\n <div *ngIf=\"!config.workflow!.style || config.workflow!.style === 'steps'\" class=\"lp-steps-grid\">\r\n <div *ngFor=\"let step of config.workflow!.steps; let i = index\" class=\"lp-step-card\">\r\n <div class=\"lp-step-number\">{{ step.number }}</div>\r\n <div class=\"lp-step-icon\">\r\n <span class=\"material-icons\">{{ step.icon }}</span>\r\n </div>\r\n <h3 class=\"lp-step-title\">{{ step.title }}</h3>\r\n <p class=\"lp-step-desc\">{{ step.description }}</p>\r\n <!-- Connector line between steps, not after last -->\r\n <div *ngIf=\"i < config.workflow!.steps.length - 1\" class=\"lp-step-connector\"></div>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== CUSTOM SECTION SLOT ===== -->\r\n<!-- Consumer projects app-specific sections here (fleet board, benefits rows, etc.)\r\n using [lpCustomSection] attribute on a wrapper element -->\r\n<ng-content select=\"[lpCustomSection]\"></ng-content>\r\n\r\n<!-- ===== MODULES (optional) ===== -->\r\n<section *ngIf=\"config.modules\" id=\"lp-modules\" class=\"lp-section lp-section-alt lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-section-header\">\r\n <h2>{{ config.modules!.title }}</h2>\r\n <p>{{ config.modules!.subtitle }}</p>\r\n </div>\r\n <div class=\"lp-modules-grid\">\r\n <div *ngFor=\"let group of config.modules!.groups\" class=\"lp-module-group\">\r\n <div class=\"lp-module-group-label\">{{ group.category }}</div>\r\n <div class=\"lp-module-tiles\">\r\n <div *ngFor=\"let tile of group.tiles\" class=\"lp-module-tile\">\r\n <span class=\"material-icons\">{{ tile.icon }}</span>\r\n <span>{{ tile.name }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== SECURITY (optional \u2014 dark background) ===== -->\r\n<section *ngIf=\"config.security\" class=\"lp-section lp-section-dark lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-section-header lp-header-light\">\r\n <h2>{{ config.security!.title }}</h2>\r\n <p>{{ config.security!.subtitle }}</p>\r\n </div>\r\n <div class=\"lp-security-grid\">\r\n <div *ngFor=\"let item of config.security!.items\" class=\"lp-security-card\">\r\n <div class=\"lp-security-icon\">\r\n <span class=\"material-icons\">{{ item.icon }}</span>\r\n </div>\r\n <h3>{{ item.title }}</h3>\r\n <p>{{ item.description }}</p>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== TESTIMONIALS (optional) ===== -->\r\n<section *ngIf=\"config.testimonials\" class=\"lp-section lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-section-header\">\r\n <h2>{{ config.testimonials!.title }}</h2>\r\n <p *ngIf=\"config.testimonials!.subtitle\">{{ config.testimonials!.subtitle }}</p>\r\n </div>\r\n <div class=\"lp-testimonials-grid\">\r\n <div *ngFor=\"let item of config.testimonials!.items\" class=\"lp-testimonial-card\">\r\n <div class=\"lp-stars\">\r\n <span class=\"material-icons\">star</span>\r\n <span class=\"material-icons\">star</span>\r\n <span class=\"material-icons\">star</span>\r\n <span class=\"material-icons\">star</span>\r\n <span class=\"material-icons\">star</span>\r\n </div>\r\n <p class=\"lp-testimonial-quote\">\"{{ item.quote }}\"</p>\r\n <div class=\"lp-testimonial-author\">\r\n <div class=\"lp-author-avatar\">{{ item.authorInitials }}</div>\r\n <div>\r\n <div class=\"lp-author-name\">{{ item.authorName }}</div>\r\n <div class=\"lp-author-role\">{{ item.authorRole }}</div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== PRICING (optional) ===== -->\r\n<section *ngIf=\"config.pricing\" id=\"lp-pricing\" class=\"lp-section lp-animate\">\r\n <div class=\"lp-container\">\r\n <div class=\"lp-section-header\">\r\n <h2>{{ config.pricing!.title }}</h2>\r\n <p *ngIf=\"config.pricing!.subtitle\">{{ config.pricing!.subtitle }}</p>\r\n </div>\r\n <div class=\"lp-pricing-grid\">\r\n <div *ngFor=\"let plan of config.pricing!.plans\"\r\n class=\"lp-pricing-card\"\r\n [class.lp-pricing-popular]=\"plan.popular\">\r\n <div *ngIf=\"plan.popular\" class=\"lp-popular-badge\">Most Popular</div>\r\n <div class=\"lp-pricing-header\">\r\n <h3>{{ plan.name }}</h3>\r\n <div class=\"lp-pricing-price\">\r\n <span class=\"lp-price-amount\">{{ plan.price }}</span>\r\n <span *ngIf=\"plan.period\" class=\"lp-price-period\">{{ plan.period }}</span>\r\n </div>\r\n <p class=\"lp-pricing-desc\">{{ plan.description }}</p>\r\n </div>\r\n <ul class=\"lp-pricing-features\">\r\n <li *ngFor=\"let feature of plan.features\">{{ feature }}</li>\r\n </ul>\r\n <!-- Popular plans get primary button, others get outline -->\r\n <button *ngIf=\"plan.popular\"\r\n class=\"lp-btn lp-btn-primary lp-btn-block\"\r\n (click)=\"plan.ctaRoute ? navigateTo(plan.ctaRoute) : scrollToSection(plan.ctaSection || '')\">\r\n {{ plan.ctaLabel }}\r\n </button>\r\n <button *ngIf=\"!plan.popular\"\r\n class=\"lp-btn lp-btn-outline lp-btn-block\"\r\n (click)=\"plan.ctaRoute ? navigateTo(plan.ctaRoute) : scrollToSection(plan.ctaSection || '')\">\r\n {{ plan.ctaLabel }}\r\n </button>\r\n </div>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== FINAL CTA BANNER (optional) ===== -->\r\n<section *ngIf=\"config.finalCta\" class=\"lp-final-cta lp-animate\">\r\n <div class=\"lp-container lp-cta-content\">\r\n <h2>{{ config.finalCta!.headline }}</h2>\r\n <p>{{ config.finalCta!.subtitle }}</p>\r\n <div class=\"lp-cta-buttons\">\r\n <button class=\"lp-btn lp-btn-white lp-btn-lg\"\r\n (click)=\"navigateTo(config.finalCta!.primaryCTARoute || 'signup')\">\r\n {{ config.finalCta!.primaryCTA }}\r\n </button>\r\n <button *ngIf=\"config.finalCta!.secondaryCTA\"\r\n class=\"lp-btn lp-btn-outline-white lp-btn-lg\"\r\n (click)=\"scrollToSection(config.finalCta!.secondaryCTASection || '')\">\r\n {{ config.finalCta!.secondaryCTA }}\r\n </button>\r\n </div>\r\n </div>\r\n</section>\r\n\r\n<!-- ===== FOOTER ===== -->\r\n<footer class=\"lp-footer\" id=\"lp-footer\">\r\n <div class=\"lp-container\">\r\n\r\n <!-- Rich multi-column footer when footerColumns provided -->\r\n <div *ngIf=\"config.footerColumns && config.footerColumns.length; else simpleFooter\" class=\"lp-footer-grid\">\r\n <!-- Brand column -->\r\n <div class=\"lp-footer-brand-col\">\r\n <!-- Custom SVG footer brand OR default img+name -->\r\n <ng-container *ngIf=\"config.footerBrand; else defaultFooterBrand\">\r\n <div class=\"lp-footer-brand-custom\">\r\n <span class=\"lp-footer-brand-svg\" [innerHTML]=\"safeFooterBrandSvg\"></span>\r\n <div *ngIf=\"config.footerBrand.wordmarkLine1\" class=\"lp-footer-brand-wordmark\">\r\n <span class=\"lp-footer-wordmark-line1\">{{ config.footerBrand.wordmarkLine1 }}</span>\r\n <span *ngIf=\"config.footerBrand.wordmarkLine2\" class=\"lp-footer-wordmark-line2\">{{ config.footerBrand.wordmarkLine2 }}</span>\r\n </div>\r\n </div>\r\n </ng-container>\r\n <ng-template #defaultFooterBrand>\r\n <div class=\"lp-footer-brand-default\">\r\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-footer-logo\">\r\n <span class=\"lp-footer-name\">{{ config.appName }}</span>\r\n </div>\r\n </ng-template>\r\n <p *ngIf=\"config.footerTagline\" class=\"lp-footer-tagline\">{{ config.footerTagline }}</p>\r\n </div>\r\n <!-- Link columns -->\r\n <div *ngFor=\"let col of config.footerColumns\" class=\"lp-footer-col\">\r\n <h4>{{ col.title }}</h4>\r\n <a *ngFor=\"let link of col.links\"\r\n (click)=\"link.section ? scrollToSection(link.section) : (link.route ? navigateTo(link.route) : null)\">\r\n {{ link.label }}\r\n </a>\r\n </div>\r\n </div>\r\n\r\n <!-- Simple single-row footer (default) -->\r\n <ng-template #simpleFooter>\r\n <div class=\"lp-footer-inner\">\r\n <div class=\"lp-footer-brand\">\r\n <img [src]=\"config.logoSrc\" [alt]=\"config.appName\" class=\"lp-footer-logo\">\r\n <span class=\"lp-footer-name\">{{ config.appName }}</span>\r\n </div>\r\n <div class=\"lp-footer-links\">\r\n <a (click)=\"navigateTo('login')\">Log in</a>\r\n <a (click)=\"navigateTo(config.hero.primaryCTARoute || 'signup')\">Sign up</a>\r\n </div>\r\n <div class=\"lp-footer-copy\">\r\n &copy; {{ currentYear }} {{ config.footerCompany || 'alsquare technologies' }}. All rights reserved.\r\n </div>\r\n </div>\r\n </ng-template>\r\n\r\n <!-- Footer bottom bar (shown for both layouts) -->\r\n <div *ngIf=\"config.footerColumns && config.footerColumns.length\" class=\"lp-footer-bottom\">\r\n <span>&copy; {{ currentYear }} {{ config.footerCompany || 'alsquare technologies' }}. All rights reserved.</span>\r\n </div>\r\n </div>\r\n</footer>\r\n", styles: ["@import\"https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700;800;900&display=swap\";:host{--lp-primary: #1E3A5F;--lp-primary-dark: #152C4A;--lp-primary-light: #EBF0F7;--lp-secondary: #F59E0B;--lp-secondary-light:#FEF3C7;--lp-dark: #0D1117;--lp-gray-800: #1F2937;--lp-gray-700: #374151;--lp-gray-600: #4B5563;--lp-gray-400: #9CA3AF;--lp-gray-200: #E5E7EB;--lp-gray-100: #F3F4F6;--lp-gray-50: #F9FAFB;--lp-white: #FFFFFF;--lp-radius: 12px;--lp-radius-sm: 8px;--lp-radius-lg: 20px;--lp-radius-xl: 28px;--lp-shadow-xs: 0 1px 2px rgba(0,0,0,.05);--lp-shadow: 0 1px 3px rgba(0,0,0,.07), 0 1px 2px rgba(0,0,0,.05);--lp-shadow-md: 0 4px 8px rgba(0,0,0,.06), 0 2px 4px rgba(0,0,0,.04);--lp-shadow-lg: 0 12px 28px rgba(0,0,0,.1), 0 4px 8px rgba(0,0,0,.06);--lp-shadow-xl: 0 20px 40px rgba(0,0,0,.12), 0 8px 16px rgba(0,0,0,.07);--lp-ease: cubic-bezier(.4, 0, .2, 1);--lp-spring: cubic-bezier(.34, 1.56, .64, 1);display:block;font-family:Outfit,-apple-system,BlinkMacSystemFont,Segoe UI,sans-serif;color:var(--lp-gray-700);line-height:1.6;overflow-x:hidden;-webkit-font-smoothing:antialiased}.lp-container{max-width:1200px;margin:0 auto;padding:0 32px}.lp-navbar{position:fixed;top:0;left:0;right:0;z-index:1000;padding:18px 0;transition:background .3s var(--lp-ease),padding .3s var(--lp-ease),box-shadow .3s var(--lp-ease)}.lp-navbar-scrolled{background:#ffffffeb;backdrop-filter:blur(16px) saturate(180%);-webkit-backdrop-filter:blur(16px) saturate(180%);box-shadow:0 1px #0000000f,0 2px 8px #0000000a;padding:12px 0}.lp-nav-inner{display:flex;align-items:center;justify-content:space-between}.lp-nav-brand{display:flex;align-items:center;gap:10px;cursor:pointer;text-decoration:none}.lp-nav-logo{width:30px;height:30px;border-radius:7px;object-fit:contain}.lp-nav-brand-text{font-size:18px;font-weight:700;color:var(--lp-primary);letter-spacing:-.01em}.lp-nav-links{display:flex;align-items:center;gap:36px}.lp-nav-links a{font-size:14px;font-weight:500;color:var(--lp-gray-600);cursor:pointer;transition:color .2s;text-decoration:none;letter-spacing:.01em}.lp-nav-links a:hover{color:var(--lp-primary)}.lp-nav-auth{display:flex;align-items:center;gap:12px}.lp-nav-login{font-size:14px;font-weight:500;color:var(--lp-gray-600);cursor:pointer;transition:color .2s;text-decoration:none}.lp-nav-login:hover{color:var(--lp-primary)}.lp-hamburger{display:none;flex-direction:column;gap:5px;background:none;border:none;cursor:pointer;padding:4px;z-index:1001}.lp-hamburger span{display:block;width:22px;height:2px;background:var(--lp-gray-700);border-radius:2px;transition:transform .3s var(--lp-ease),opacity .3s}.lp-hamburger-open span:nth-child(1){transform:translateY(7px) rotate(45deg)}.lp-hamburger-open span:nth-child(2){opacity:0}.lp-hamburger-open span:nth-child(3){transform:translateY(-7px) rotate(-45deg)}.lp-mobile-menu{display:none}.lp-mobile-auth{display:flex;flex-direction:column;gap:12px;margin-top:24px;padding-top:24px;border-top:1px solid var(--lp-gray-200)}.lp-btn{display:inline-flex;align-items:center;justify-content:center;gap:8px;font-family:Outfit,sans-serif;font-size:14px;font-weight:600;padding:10px 22px;border-radius:var(--lp-radius-sm);border:none;cursor:pointer;transition:all .25s var(--lp-ease);text-decoration:none;white-space:nowrap;letter-spacing:.01em}.lp-btn-primary{background:var(--lp-primary);color:var(--lp-white);box-shadow:0 1px 3px #00000026,inset 0 1px #ffffff1f}.lp-btn-primary:hover{background:var(--lp-primary-dark);transform:translateY(-1px);box-shadow:0 6px 16px #0003,inset 0 1px #ffffff1f}.lp-btn-primary:active{transform:translateY(0)}.lp-btn-outline{background:transparent;color:var(--lp-primary);border:1.5px solid var(--lp-primary)}.lp-btn-outline:hover{background:var(--lp-primary);color:var(--lp-white);transform:translateY(-1px);box-shadow:0 4px 12px #00000026}.lp-btn-sm{padding:7px 16px;font-size:13px}.lp-btn-lg{padding:14px 32px;font-size:15px;border-radius:var(--lp-radius)}.lp-btn-block{width:100%}.lp-hero-bg{position:relative;overflow:hidden;padding:140px 0 100px;background:radial-gradient(ellipse 80% 60% at 60% -10%,color-mix(in srgb,var(--lp-primary) 8%,transparent) 0%,transparent 70%),radial-gradient(ellipse 60% 50% at -10% 80%,color-mix(in srgb,var(--lp-secondary) 6%,transparent) 0%,transparent 70%),linear-gradient(160deg,var(--lp-primary-light) 0%,#FAFBFF 45%,var(--lp-secondary-light) 100%);min-height:100vh;display:flex;align-items:center}.lp-hero-bg:before{content:\"\";position:absolute;inset:0;background-image:radial-gradient(circle,rgba(0,0,0,.04) 1px,transparent 1px);background-size:32px 32px;pointer-events:none;-webkit-mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black 40%,transparent 100%);mask-image:radial-gradient(ellipse 80% 80% at 50% 50%,black 40%,transparent 100%)}.lp-hero-bg:after{content:\"\";position:absolute;width:700px;height:700px;border-radius:50%;background:color-mix(in srgb,var(--lp-primary) 5%,transparent);top:-200px;right:-150px;filter:blur(100px);pointer-events:none}.lp-hero-content{position:relative;z-index:1;text-align:center;width:100%}.lp-hero-badge{display:inline-flex;align-items:center;gap:6px;background:color-mix(in srgb,var(--lp-primary) 10%,transparent);color:var(--lp-primary);border:1px solid color-mix(in srgb,var(--lp-primary) 20%,transparent);border-radius:100px;padding:6px 16px;font-size:13px;font-weight:600;margin-bottom:20px;letter-spacing:.02em}.lp-hero-brand{margin-bottom:20px;text-align:center}.lp-hero-brand-logo{display:inline-flex;align-items:center;gap:14px;margin-bottom:12px}.lp-hero-brand-icon{height:52px;width:52px;border-radius:14px;object-fit:contain;box-shadow:0 4px 12px #0000001f}.lp-hero-brand-name{font-size:38px;font-weight:800;color:var(--lp-primary);letter-spacing:-.03em;font-family:Outfit,sans-serif}.lp-hero-divider{display:flex;align-items:center;justify-content:center;gap:10px}.lp-hero-divider-line{display:block;height:1px;width:40px;background:linear-gradient(90deg,transparent,var(--lp-primary),transparent);opacity:.3}.lp-hero-divider-dot{display:block;height:5px;width:5px;border-radius:50%;background:var(--lp-secondary);opacity:.8}.lp-hero-text{max-width:760px;margin:0 auto 64px}.lp-hero-text h1{font-family:Outfit,sans-serif;font-size:68px;font-weight:900;line-height:1.05;color:var(--lp-dark);margin:0 0 24px;letter-spacing:-.03em}.lp-gradient-text{background:linear-gradient(135deg,var(--lp-primary) 0%,var(--lp-secondary) 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}.lp-hero-subtitle{font-size:19px;color:var(--lp-gray-600);line-height:1.65;font-weight:400;max-width:560px;margin:0 auto 36px}.lp-hero-ctas{display:flex;align-items:center;justify-content:center;gap:14px;margin-bottom:32px;flex-wrap:wrap}.lp-trust-signals{display:flex;align-items:center;justify-content:center;gap:28px;flex-wrap:wrap}.lp-trust-item{display:flex;align-items:center;gap:7px;font-size:13px;font-weight:500;color:var(--lp-gray-500, #6B7280)}.lp-trust-item svg{color:var(--lp-primary);flex-shrink:0}.lp-section{padding:100px 0;background:var(--lp-white)}.lp-section-alt{background:var(--lp-gray-50)}.lp-section-header{text-align:center;margin-bottom:64px}.lp-section-header h2{font-family:Outfit,sans-serif;font-size:42px;font-weight:800;color:var(--lp-dark);margin:0 0 14px;letter-spacing:-.025em;line-height:1.15}.lp-section-header p{font-size:17px;color:var(--lp-gray-600);max-width:540px;margin:0 auto;line-height:1.65}.lp-features-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:20px}.lp-feature-card{background:var(--lp-white);border-radius:var(--lp-radius-lg);padding:36px 28px;transition:transform .25s var(--lp-ease),box-shadow .25s var(--lp-ease);border:1px solid var(--lp-gray-200);box-shadow:var(--lp-shadow-xs);position:relative;overflow:hidden}.lp-feature-card:before{content:\"\";position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--lp-primary),var(--lp-secondary));transform:scaleX(0);transform-origin:left;transition:transform .3s var(--lp-ease);border-radius:3px 3px 0 0}.lp-feature-card:hover{transform:translateY(-5px);box-shadow:var(--lp-shadow-xl);border-color:transparent}.lp-feature-card:hover:before{transform:scaleX(1)}.lp-feature-icon{width:52px;height:52px;border-radius:var(--lp-radius-sm);display:flex;align-items:center;justify-content:center;margin-bottom:20px}.lp-feature-icon .material-icons{font-size:26px}.lp-feature-title{font-family:Outfit,sans-serif;font-size:17px;font-weight:700;color:var(--lp-dark);margin:0 0 10px;letter-spacing:-.01em}.lp-feature-desc{font-size:14px;color:var(--lp-gray-600);line-height:1.65;margin:0}.lp-steps-grid{display:flex;align-items:flex-start;justify-content:space-between;position:relative;gap:16px}.lp-step-card{flex:1;display:flex;flex-direction:column;align-items:center;text-align:center;position:relative;padding:0 8px}.lp-step-number{font-size:11px;font-weight:700;color:var(--lp-secondary);text-transform:uppercase;letter-spacing:2px;margin-bottom:10px}.lp-step-icon{width:56px;height:56px;border-radius:50%;background:var(--lp-primary);display:flex;align-items:center;justify-content:center;margin-bottom:18px;box-shadow:0 4px 14px color-mix(in srgb,var(--lp-primary) 30%,transparent);border:3px solid var(--lp-white);position:relative;z-index:1}.lp-step-icon .material-icons{font-size:24px;color:var(--lp-white)}.lp-step-title{font-family:Outfit,sans-serif;font-size:15px;font-weight:700;color:var(--lp-dark);margin:0 0 8px;letter-spacing:-.01em}.lp-step-desc{font-size:13px;color:var(--lp-gray-600);line-height:1.55;margin:0}.lp-step-connector{position:absolute;top:40px;left:60%;width:80%;height:2px;background:repeating-linear-gradient(90deg,color-mix(in srgb,var(--lp-secondary) 50%,transparent) 0px,color-mix(in srgb,var(--lp-secondary) 50%,transparent) 10px,transparent 10px,transparent 18px);pointer-events:none;z-index:0}.lp-metrics-grid{display:grid;grid-template-columns:repeat(4,1fr);gap:0;border:1px solid var(--lp-gray-200);border-radius:var(--lp-radius-lg);overflow:hidden;background:var(--lp-white);box-shadow:var(--lp-shadow)}.lp-metric-card{text-align:center;padding:40px 24px;border-right:1px solid var(--lp-gray-200);transition:background .2s}.lp-metric-card:last-child{border-right:none}.lp-metric-card:hover{background:var(--lp-gray-50)}.lp-metric-value{font-family:Outfit,sans-serif;font-size:48px;font-weight:900;color:var(--lp-primary);line-height:1;letter-spacing:-.03em;margin-bottom:8px}.lp-metric-label{font-size:14px;font-weight:500;color:var(--lp-gray-600)}.lp-modules-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:20px}.lp-module-group{background:var(--lp-white);border-radius:var(--lp-radius-lg);padding:28px 28px 24px;border:1px solid var(--lp-gray-200);box-shadow:var(--lp-shadow-xs);transition:box-shadow .2s}.lp-module-group:hover{box-shadow:var(--lp-shadow-md)}.lp-module-group-label{font-family:Outfit,sans-serif;font-size:11px;font-weight:700;color:var(--lp-primary);margin-bottom:16px;text-transform:uppercase;letter-spacing:1px}.lp-module-tiles{display:flex;flex-wrap:wrap;gap:8px}.lp-module-tile{display:inline-flex;align-items:center;gap:6px;background:var(--lp-gray-50);border:1px solid var(--lp-gray-200);padding:7px 13px;border-radius:100px;font-size:12px;font-weight:600;color:var(--lp-gray-700);transition:all .2s var(--lp-ease);cursor:default}.lp-module-tile:hover{background:color-mix(in srgb,var(--lp-primary) 6%,transparent);border-color:color-mix(in srgb,var(--lp-primary) 25%,transparent);color:var(--lp-primary);transform:translateY(-1px)}.lp-module-tile .material-icons{font-size:15px;color:var(--lp-primary)}.lp-footer{background:var(--lp-dark);padding:44px 0 36px;border-top:1px solid rgba(255,255,255,.04)}.lp-footer-inner{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;gap:20px}.lp-footer-brand{display:flex;align-items:center;gap:10px}.lp-footer-logo{width:30px;height:30px;border-radius:7px;object-fit:contain;opacity:.9}.lp-footer-name{font-family:Outfit,sans-serif;font-size:17px;font-weight:700;color:var(--lp-white);letter-spacing:-.01em}.lp-footer-links{display:flex;gap:28px}.lp-footer-links a{font-size:14px;font-weight:500;color:#ffffff80;cursor:pointer;text-decoration:none;transition:color .2s}.lp-footer-links a:hover{color:var(--lp-white)}.lp-footer-copy{font-size:13px;color:#ffffff59;width:100%;text-align:center;padding-top:20px;margin-top:4px;border-top:1px solid rgba(255,255,255,.06)}.lp-animate{opacity:0;transform:translateY(28px);transition:opacity .65s var(--lp-ease),transform .65s var(--lp-ease)}.lp-visible{opacity:1;transform:translateY(0)}.lp-visible .lp-feature-card:nth-child(1){transition-delay:0ms}.lp-visible .lp-feature-card:nth-child(2){transition-delay:60ms}.lp-visible .lp-feature-card:nth-child(3){transition-delay:.12s}.lp-visible .lp-feature-card:nth-child(4){transition-delay:.18s}.lp-visible .lp-feature-card:nth-child(5){transition-delay:.24s}.lp-visible .lp-feature-card:nth-child(6){transition-delay:.3s}.lp-visible .lp-step-card:nth-child(1){transition-delay:0ms}.lp-visible .lp-step-card:nth-child(2){transition-delay:80ms}.lp-visible .lp-step-card:nth-child(3){transition-delay:.16s}.lp-visible .lp-step-card:nth-child(4){transition-delay:.24s}.lp-visible .lp-metric-card:nth-child(1){transition-delay:0ms}.lp-visible .lp-metric-card:nth-child(2){transition-delay:60ms}.lp-visible .lp-metric-card:nth-child(3){transition-delay:.12s}.lp-visible .lp-metric-card:nth-child(4){transition-delay:.18s}@media (max-width: 1024px){.lp-hero-text h1{font-size:52px}.lp-section-header h2{font-size:36px}.lp-features-grid,.lp-metrics-grid{grid-template-columns:repeat(2,1fr)}.lp-metrics-grid .lp-metric-card:nth-child(2){border-right:none}.lp-metrics-grid .lp-metric-card:nth-child(3){border-top:1px solid var(--lp-gray-200)}.lp-metrics-grid .lp-metric-card:nth-child(4){border-top:1px solid var(--lp-gray-200)}.lp-steps-grid{flex-wrap:wrap;justify-content:center;gap:40px}.lp-step-connector{display:none}}@media (max-width: 767px){.lp-container{padding:0 20px}.lp-nav-links{display:none}.lp-hamburger{display:flex}.lp-mobile-auth{display:none}.lp-mobile-menu{display:none;position:fixed;inset:0;background:#fffffff7;-webkit-backdrop-filter:blur(16px);backdrop-filter:blur(16px);flex-direction:column;align-items:center;justify-content:center;gap:28px;z-index:999}.lp-mobile-menu-open{display:flex}.lp-mobile-menu a{font-size:22px;font-weight:700;color:var(--lp-dark);cursor:pointer;text-decoration:none;letter-spacing:-.01em}.lp-hero-bg{padding:100px 0 60px;min-height:auto}.lp-hero-text h1{font-size:38px}.lp-hero-brand-name{font-size:28px}.lp-hero-subtitle{font-size:16px}.lp-hero-ctas{flex-direction:column}.lp-hero-ctas .lp-btn{width:100%;justify-content:center}.lp-trust-signals{gap:16px}.lp-section{padding:72px 0}.lp-section-header{margin-bottom:40px}.lp-section-header h2{font-size:28px}.lp-features-grid{grid-template-columns:1fr}.lp-metrics-grid{grid-template-columns:repeat(2,1fr)}.lp-metrics-grid .lp-metric-card{border-right:none;border-bottom:1px solid var(--lp-gray-200)}.lp-metrics-grid .lp-metric-card:last-child{border-bottom:none}.lp-metric-value{font-size:36px}.lp-modules-grid{grid-template-columns:1fr}.lp-steps-grid{flex-direction:column;align-items:center;gap:32px}.lp-step-connector{display:none}.lp-footer-inner{flex-direction:column;align-items:center;text-align:center}}.lp-nav-brand-svg{display:flex;align-items:center;width:36px;height:36px;flex-shrink:0}.lp-nav-brand-svg ::ng-deep svg{width:36px;height:36px;display:block}.lp-nav-brand-wordmark{display:flex;flex-direction:column;line-height:1.1;margin-left:8px}.lp-nav-wordmark-line1{font-size:15px;font-weight:800;color:#1a3a7a;letter-spacing:-.01em;font-family:Outfit,sans-serif}.lp-nav-wordmark-line2{font-size:9px;font-weight:600;letter-spacing:.12em;color:#e8a020;text-transform:uppercase}.lp-hero-bg{position:relative}.lp-hero-watermark{position:absolute;inset:0;display:flex;align-items:flex-start;justify-content:center;padding-top:40px;pointer-events:none;z-index:1}.lp-hero-watermark-inner{width:700px;height:700px;opacity:.04;overflow:hidden;flex-shrink:0}@media (min-width: 768px){.lp-hero-watermark-inner{width:800px;height:800px}}.lp-hero-content{display:flex;flex-direction:column;align-items:center}.lp-hero-main{z-index:1;width:100%}.lp-hero-visual{position:relative;z-index:1;width:100%;max-width:900px;margin:48px auto 0}.lp-workflow-road{position:relative;display:flex;justify-content:space-between;align-items:flex-start;padding:48px 0 24px;gap:16px}.lp-road-line{position:absolute;top:72px;left:6%;right:6%;height:3px;background:linear-gradient(90deg,var(--lp-primary-light),var(--lp-primary),var(--lp-primary-light));border-radius:2px;opacity:.4}.lp-waypoint{flex:1;display:flex;flex-direction:column;align-items:center;text-align:center;position:relative;z-index:1}.lp-waypoint-marker{width:52px;height:52px;border-radius:50%;background:var(--lp-primary);color:#fff;display:flex;align-items:center;justify-content:center;margin-bottom:12px;box-shadow:0 4px 16px color-mix(in srgb,var(--lp-primary) 35%,transparent);transition:transform .25s cubic-bezier(.34,1.56,.64,1)}.lp-waypoint:hover .lp-waypoint-marker{transform:translateY(-4px) scale(1.08)}.lp-waypoint-marker .material-icons{font-size:22px}.lp-waypoint-number{font-size:11px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--lp-primary);margin-bottom:6px;opacity:.7}.lp-waypoint-title{font-size:14px;font-weight:700;color:var(--lp-dark);margin-bottom:6px;font-family:Outfit,sans-serif}.lp-waypoint-desc{font-size:12px;color:var(--lp-gray-600);line-height:1.5;max-width:140px}.lp-section-dark{background:var(--lp-dark);color:#fff}.lp-section-dark.lp-section{padding:96px 0}.lp-header-light h2{color:#fff}.lp-header-light p{color:#ffffffa6}.lp-security-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:32px}.lp-security-card{background:#ffffff0d;border:1px solid rgba(255,255,255,.08);border-radius:20px;padding:32px 28px;transition:background .25s ease}.lp-security-card:hover{background:#ffffff14}.lp-security-icon{width:52px;height:52px;border-radius:14px;background:#ffffff1a;display:flex;align-items:center;justify-content:center;margin-bottom:20px;color:#fff}.lp-security-icon .material-icons{font-size:24px}.lp-security-card h3{font-size:18px;font-weight:700;color:#fff;margin:0 0 12px;font-family:Outfit,sans-serif}.lp-security-card p{font-size:14px;color:#fff9;line-height:1.65;margin:0}.lp-testimonials-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:28px}.lp-testimonial-card{background:#fff;border:1px solid var(--lp-gray-200);border-radius:20px;padding:32px 28px;display:flex;flex-direction:column;gap:16px;box-shadow:0 1px 3px #00000012,0 1px 2px #0000000d;transition:box-shadow .25s ease,transform .25s ease}.lp-testimonial-card:hover{box-shadow:0 4px 8px #0000000f;transform:translateY(-2px)}.lp-stars{display:flex;gap:2px;color:var(--lp-secondary)}.lp-stars .material-icons{font-size:18px}.lp-testimonial-quote{font-size:15px;color:var(--lp-gray-700);line-height:1.65;font-style:italic;margin:0;flex:1}.lp-testimonial-author{display:flex;align-items:center;gap:12px}.lp-author-avatar{width:44px;height:44px;border-radius:50%;background:var(--lp-primary);color:#fff;font-size:14px;font-weight:700;display:flex;align-items:center;justify-content:center;flex-shrink:0;font-family:Outfit,sans-serif}.lp-author-name{font-size:14px;font-weight:700;color:var(--lp-dark)}.lp-author-role{font-size:12px;color:var(--lp-gray-400)}.lp-pricing-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:28px;align-items:start}.lp-pricing-card{background:#fff;border:1px solid var(--lp-gray-200);border-radius:20px;padding:36px 28px 28px;position:relative;display:flex;flex-direction:column;box-shadow:0 1px 3px #00000012,0 1px 2px #0000000d;transition:box-shadow .25s ease,transform .25s ease}.lp-pricing-card:hover{box-shadow:0 4px 8px #0000000f;transform:translateY(-3px)}.lp-pricing-popular{border-color:var(--lp-primary);box-shadow:0 0 0 2px var(--lp-primary);transform:scale(1.03)}.lp-pricing-popular:hover{transform:scale(1.03) translateY(-3px)}.lp-popular-badge{position:absolute;top:-12px;left:50%;transform:translate(-50%);background:var(--lp-primary);color:#fff;font-size:11px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;padding:4px 14px;border-radius:100px;white-space:nowrap;font-family:Outfit,sans-serif}.lp-pricing-header{margin-bottom:24px}.lp-pricing-header h3{font-size:20px;font-weight:800;color:var(--lp-dark);margin:0 0 12px;font-family:Outfit,sans-serif}.lp-pricing-price{display:flex;align-items:baseline;gap:4px;margin-bottom:8px}.lp-price-amount{font-size:40px;font-weight:900;color:var(--lp-primary);font-family:Outfit,sans-serif;line-height:1}.lp-price-period{font-size:16px;color:var(--lp-gray-600)}.lp-pricing-desc{font-size:13px;color:var(--lp-gray-600);margin:0}.lp-pricing-features{list-style:none;padding:0;margin:0 0 28px;display:flex;flex-direction:column;gap:10px;flex:1}.lp-pricing-features li{font-size:14px;color:var(--lp-gray-700);padding-left:20px;position:relative}.lp-pricing-features li:before{content:\"\\2713\";position:absolute;left:0;color:var(--lp-primary);font-weight:700}.lp-btn-block{width:100%;justify-content:center;text-align:center}.lp-final-cta{background:linear-gradient(135deg,var(--lp-primary-dark, #152C4A) 0%,var(--lp-primary) 100%);padding:96px 0}.lp-cta-content{text-align:center}.lp-cta-content h2{font-size:40px;font-weight:900;color:#fff;margin:0 0 16px;font-family:Outfit,sans-serif;letter-spacing:-.02em}.lp-cta-content p{font-size:18px;color:#ffffffbf;margin:0 auto 36px;max-width:560px}.lp-cta-buttons{display:flex;gap:16px;justify-content:center;flex-wrap:wrap}.lp-btn-white{background:#fff;color:var(--lp-primary);border:2px solid white;font-weight:700}.lp-btn-white:hover{background:#ffffffe6;transform:translateY(-1px)}.lp-btn-outline-white{background:transparent;color:#fff;border:2px solid rgba(255,255,255,.5);font-weight:700}.lp-btn-outline-white:hover{background:#ffffff1a;border-color:#fff;transform:translateY(-1px)}.lp-footer-grid{display:grid;grid-template-columns:2fr 1fr 1fr 1fr;gap:48px;padding:64px 0 48px}.lp-footer-brand-col{display:flex;flex-direction:column;gap:16px}.lp-footer-brand-custom{display:flex;align-items:center;gap:10px}.lp-footer-brand-svg{display:flex;align-items:center;width:36px;height:36px;flex-shrink:0}.lp-footer-brand-svg ::ng-deep svg{width:36px;height:36px;display:block}.lp-footer-brand-wordmark{display:flex;flex-direction:column;line-height:1.1}.lp-footer-wordmark-line1{font-size:15px;font-weight:800;color:#fff;font-family:Outfit,sans-serif}.lp-footer-wordmark-line2{font-size:9px;font-weight:600;letter-spacing:.12em;color:#e8a020;text-transform:uppercase}.lp-footer-brand-default{display:flex;align-items:center;gap:10px}.lp-footer-tagline{font-size:14px;color:#ffffff80;line-height:1.6;margin:0;max-width:280px}.lp-footer-col{display:flex;flex-direction:column;gap:12px}.lp-footer-col h4{font-size:13px;font-weight:700;color:#fff;margin:0 0 4px;letter-spacing:.04em;text-transform:uppercase;font-family:Outfit,sans-serif}.lp-footer-col a{font-size:14px;color:#ffffff80;cursor:pointer;text-decoration:none;transition:color .2s}.lp-footer-col a:hover{color:#fff}.lp-footer-bottom{border-top:1px solid rgba(255,255,255,.08);padding:24px 0;font-size:13px;color:#ffffff59}@media (max-width: 768px){.lp-hero-visual{max-width:100%;margin-top:32px}.lp-workflow-road{flex-direction:column;align-items:center;gap:32px}.lp-road-line{display:none}.lp-waypoint-desc{max-width:260px}.lp-security-grid,.lp-testimonials-grid,.lp-pricing-grid{grid-template-columns:1fr}.lp-pricing-popular{transform:none}.lp-pricing-popular:hover{transform:translateY(-3px)}.lp-cta-content h2{font-size:28px}.lp-footer-grid{grid-template-columns:1fr 1fr;gap:32px;padding:48px 0 32px}}\n"] }]
17249
17249
  }], ctorParameters: () => [{ type: i1$2.Router }, { type: AuthService }, { type: LastRouteService }, { type: i0.ElementRef }, { type: i3$2.DomSanitizer }], propDecorators: { config: [{
17250
17250
  type: Input
17251
17251
  }], onWindowScroll: [{
@@ -19788,150 +19788,150 @@ class JournalEntryDialogComponent {
19788
19788
  });
19789
19789
  }
19790
19790
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: JournalEntryDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
19791
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: JournalEntryDialogComponent, isStandalone: false, selector: "spa-journal-lines", ngImport: i0, template: `
19792
- <h2 mat-dialog-title>New Journal Entry</h2>
19793
- <mat-dialog-content>
19794
- <div style="display: flex; gap: 16px; flex-wrap: wrap;">
19795
- <mat-form-field>
19796
- <mat-label>Date</mat-label>
19797
- <input matInput type="date" [(ngModel)]="date">
19798
- </mat-form-field>
19799
- <mat-form-field style="flex: 1; min-width: 260px;">
19800
- <mat-label>Description</mat-label>
19801
- <input matInput [(ngModel)]="description" placeholder="Enter journal description">
19802
- </mat-form-field>
19803
- <mat-form-field>
19804
- <mat-label>Journal Type</mat-label>
19805
- <mat-select [(ngModel)]="journalType">
19806
- <mat-option value="Journal Entry">Journal Entry</mat-option>
19807
- <mat-option value="Opening Balance">Opening Balance</mat-option>
19808
- </mat-select>
19809
- </mat-form-field>
19810
- </div>
19811
-
19812
- <table style="width: 100%; border-collapse: collapse;">
19813
- <tr style="text-align: left; border-bottom: 1px solid #ccc;">
19814
- <th style="padding: 4px;">Account</th>
19815
- <th style="padding: 4px; width: 140px;">Debit</th>
19816
- <th style="padding: 4px; width: 140px;">Credit</th>
19817
- <th style="width: 48px;"></th>
19818
- </tr>
19819
- <tr *ngFor="let line of lines; let i = index">
19820
- <td style="padding: 4px;">
19821
- <mat-form-field style="width: 100%;" subscriptSizing="dynamic">
19822
- <mat-select [(ngModel)]="line.accountID" placeholder="Select account">
19823
- <mat-option *ngFor="let option of accounts" [value]="option.value">{{ option.name }}</mat-option>
19824
- </mat-select>
19825
- </mat-form-field>
19826
- </td>
19827
- <td style="padding: 4px;">
19828
- <mat-form-field style="width: 100%;" subscriptSizing="dynamic">
19829
- <input matInput type="number" min="0" [(ngModel)]="line.debit" (ngModelChange)="line.debit && (line.credit = 0)">
19830
- </mat-form-field>
19831
- </td>
19832
- <td style="padding: 4px;">
19833
- <mat-form-field style="width: 100%;" subscriptSizing="dynamic">
19834
- <input matInput type="number" min="0" [(ngModel)]="line.credit" (ngModelChange)="line.credit && (line.debit = 0)">
19835
- </mat-form-field>
19836
- </td>
19837
- <td>
19838
- <button mat-icon-button (click)="removeLine(i)" [disabled]="lines.length <= 2">
19839
- <mat-icon>delete</mat-icon>
19840
- </button>
19841
- </td>
19842
- </tr>
19843
- <tr style="border-top: 2px solid #333;">
19844
- <td style="padding: 8px 4px;">
19845
- <button mat-stroked-button (click)="addLine()"><mat-icon>add</mat-icon> Add Line</button>
19846
- </td>
19847
- <td style="padding: 8px 4px;"><b>{{ totalDebits | number:'1.2-2' }}</b></td>
19848
- <td style="padding: 8px 4px;"><b>{{ totalCredits | number:'1.2-2' }}</b></td>
19849
- <td></td>
19850
- </tr>
19851
- </table>
19852
- <p *ngIf="!isBalanced && (totalDebits || totalCredits)" style="color: red;">
19853
- <em>Journal is out of balance by {{ (totalDebits - totalCredits) | number:'1.2-2' }}</em>
19854
- </p>
19855
- </mat-dialog-content>
19856
- <mat-dialog-actions align="end">
19857
- <button mat-button (click)="dialogRef.close(false)">Cancel</button>
19858
- <button mat-flat-button color="primary" (click)="post()" [disabled]="!canPost">Post Journal</button>
19859
- </mat-dialog-actions>
19791
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: JournalEntryDialogComponent, isStandalone: false, selector: "spa-journal-lines", ngImport: i0, template: `
19792
+ <h2 mat-dialog-title>New Journal Entry</h2>
19793
+ <mat-dialog-content>
19794
+ <div style="display: flex; gap: 16px; flex-wrap: wrap;">
19795
+ <mat-form-field>
19796
+ <mat-label>Date</mat-label>
19797
+ <input matInput type="date" [(ngModel)]="date">
19798
+ </mat-form-field>
19799
+ <mat-form-field style="flex: 1; min-width: 260px;">
19800
+ <mat-label>Description</mat-label>
19801
+ <input matInput [(ngModel)]="description" placeholder="Enter journal description">
19802
+ </mat-form-field>
19803
+ <mat-form-field>
19804
+ <mat-label>Journal Type</mat-label>
19805
+ <mat-select [(ngModel)]="journalType">
19806
+ <mat-option value="Journal Entry">Journal Entry</mat-option>
19807
+ <mat-option value="Opening Balance">Opening Balance</mat-option>
19808
+ </mat-select>
19809
+ </mat-form-field>
19810
+ </div>
19811
+
19812
+ <table style="width: 100%; border-collapse: collapse;">
19813
+ <tr style="text-align: left; border-bottom: 1px solid #ccc;">
19814
+ <th style="padding: 4px;">Account</th>
19815
+ <th style="padding: 4px; width: 140px;">Debit</th>
19816
+ <th style="padding: 4px; width: 140px;">Credit</th>
19817
+ <th style="width: 48px;"></th>
19818
+ </tr>
19819
+ <tr *ngFor="let line of lines; let i = index">
19820
+ <td style="padding: 4px;">
19821
+ <mat-form-field style="width: 100%;" subscriptSizing="dynamic">
19822
+ <mat-select [(ngModel)]="line.accountID" placeholder="Select account">
19823
+ <mat-option *ngFor="let option of accounts" [value]="option.value">{{ option.name }}</mat-option>
19824
+ </mat-select>
19825
+ </mat-form-field>
19826
+ </td>
19827
+ <td style="padding: 4px;">
19828
+ <mat-form-field style="width: 100%;" subscriptSizing="dynamic">
19829
+ <input matInput type="number" min="0" [(ngModel)]="line.debit" (ngModelChange)="line.debit && (line.credit = 0)">
19830
+ </mat-form-field>
19831
+ </td>
19832
+ <td style="padding: 4px;">
19833
+ <mat-form-field style="width: 100%;" subscriptSizing="dynamic">
19834
+ <input matInput type="number" min="0" [(ngModel)]="line.credit" (ngModelChange)="line.credit && (line.debit = 0)">
19835
+ </mat-form-field>
19836
+ </td>
19837
+ <td>
19838
+ <button mat-icon-button (click)="removeLine(i)" [disabled]="lines.length <= 2">
19839
+ <mat-icon>delete</mat-icon>
19840
+ </button>
19841
+ </td>
19842
+ </tr>
19843
+ <tr style="border-top: 2px solid #333;">
19844
+ <td style="padding: 8px 4px;">
19845
+ <button mat-stroked-button (click)="addLine()"><mat-icon>add</mat-icon> Add Line</button>
19846
+ </td>
19847
+ <td style="padding: 8px 4px;"><b>{{ totalDebits | number:'1.2-2' }}</b></td>
19848
+ <td style="padding: 8px 4px;"><b>{{ totalCredits | number:'1.2-2' }}</b></td>
19849
+ <td></td>
19850
+ </tr>
19851
+ </table>
19852
+ <p *ngIf="!isBalanced && (totalDebits || totalCredits)" style="color: red;">
19853
+ <em>Journal is out of balance by {{ (totalDebits - totalCredits) | number:'1.2-2' }}</em>
19854
+ </p>
19855
+ </mat-dialog-content>
19856
+ <mat-dialog-actions align="end">
19857
+ <button mat-button (click)="dialogRef.close(false)">Cancel</button>
19858
+ <button mat-flat-button color="primary" (click)="post()" [disabled]="!canPost">Post Journal</button>
19859
+ </mat-dialog-actions>
19860
19860
  `, isInline: true, dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i3.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "directive", type: i4.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i4.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i4.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4$3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i7$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i7$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }] }); }
19861
19861
  }
19862
19862
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: JournalEntryDialogComponent, decorators: [{
19863
19863
  type: Component,
19864
19864
  args: [{
19865
19865
  selector: 'spa-journal-lines',
19866
- template: `
19867
- <h2 mat-dialog-title>New Journal Entry</h2>
19868
- <mat-dialog-content>
19869
- <div style="display: flex; gap: 16px; flex-wrap: wrap;">
19870
- <mat-form-field>
19871
- <mat-label>Date</mat-label>
19872
- <input matInput type="date" [(ngModel)]="date">
19873
- </mat-form-field>
19874
- <mat-form-field style="flex: 1; min-width: 260px;">
19875
- <mat-label>Description</mat-label>
19876
- <input matInput [(ngModel)]="description" placeholder="Enter journal description">
19877
- </mat-form-field>
19878
- <mat-form-field>
19879
- <mat-label>Journal Type</mat-label>
19880
- <mat-select [(ngModel)]="journalType">
19881
- <mat-option value="Journal Entry">Journal Entry</mat-option>
19882
- <mat-option value="Opening Balance">Opening Balance</mat-option>
19883
- </mat-select>
19884
- </mat-form-field>
19885
- </div>
19886
-
19887
- <table style="width: 100%; border-collapse: collapse;">
19888
- <tr style="text-align: left; border-bottom: 1px solid #ccc;">
19889
- <th style="padding: 4px;">Account</th>
19890
- <th style="padding: 4px; width: 140px;">Debit</th>
19891
- <th style="padding: 4px; width: 140px;">Credit</th>
19892
- <th style="width: 48px;"></th>
19893
- </tr>
19894
- <tr *ngFor="let line of lines; let i = index">
19895
- <td style="padding: 4px;">
19896
- <mat-form-field style="width: 100%;" subscriptSizing="dynamic">
19897
- <mat-select [(ngModel)]="line.accountID" placeholder="Select account">
19898
- <mat-option *ngFor="let option of accounts" [value]="option.value">{{ option.name }}</mat-option>
19899
- </mat-select>
19900
- </mat-form-field>
19901
- </td>
19902
- <td style="padding: 4px;">
19903
- <mat-form-field style="width: 100%;" subscriptSizing="dynamic">
19904
- <input matInput type="number" min="0" [(ngModel)]="line.debit" (ngModelChange)="line.debit && (line.credit = 0)">
19905
- </mat-form-field>
19906
- </td>
19907
- <td style="padding: 4px;">
19908
- <mat-form-field style="width: 100%;" subscriptSizing="dynamic">
19909
- <input matInput type="number" min="0" [(ngModel)]="line.credit" (ngModelChange)="line.credit && (line.debit = 0)">
19910
- </mat-form-field>
19911
- </td>
19912
- <td>
19913
- <button mat-icon-button (click)="removeLine(i)" [disabled]="lines.length <= 2">
19914
- <mat-icon>delete</mat-icon>
19915
- </button>
19916
- </td>
19917
- </tr>
19918
- <tr style="border-top: 2px solid #333;">
19919
- <td style="padding: 8px 4px;">
19920
- <button mat-stroked-button (click)="addLine()"><mat-icon>add</mat-icon> Add Line</button>
19921
- </td>
19922
- <td style="padding: 8px 4px;"><b>{{ totalDebits | number:'1.2-2' }}</b></td>
19923
- <td style="padding: 8px 4px;"><b>{{ totalCredits | number:'1.2-2' }}</b></td>
19924
- <td></td>
19925
- </tr>
19926
- </table>
19927
- <p *ngIf="!isBalanced && (totalDebits || totalCredits)" style="color: red;">
19928
- <em>Journal is out of balance by {{ (totalDebits - totalCredits) | number:'1.2-2' }}</em>
19929
- </p>
19930
- </mat-dialog-content>
19931
- <mat-dialog-actions align="end">
19932
- <button mat-button (click)="dialogRef.close(false)">Cancel</button>
19933
- <button mat-flat-button color="primary" (click)="post()" [disabled]="!canPost">Post Journal</button>
19934
- </mat-dialog-actions>
19866
+ template: `
19867
+ <h2 mat-dialog-title>New Journal Entry</h2>
19868
+ <mat-dialog-content>
19869
+ <div style="display: flex; gap: 16px; flex-wrap: wrap;">
19870
+ <mat-form-field>
19871
+ <mat-label>Date</mat-label>
19872
+ <input matInput type="date" [(ngModel)]="date">
19873
+ </mat-form-field>
19874
+ <mat-form-field style="flex: 1; min-width: 260px;">
19875
+ <mat-label>Description</mat-label>
19876
+ <input matInput [(ngModel)]="description" placeholder="Enter journal description">
19877
+ </mat-form-field>
19878
+ <mat-form-field>
19879
+ <mat-label>Journal Type</mat-label>
19880
+ <mat-select [(ngModel)]="journalType">
19881
+ <mat-option value="Journal Entry">Journal Entry</mat-option>
19882
+ <mat-option value="Opening Balance">Opening Balance</mat-option>
19883
+ </mat-select>
19884
+ </mat-form-field>
19885
+ </div>
19886
+
19887
+ <table style="width: 100%; border-collapse: collapse;">
19888
+ <tr style="text-align: left; border-bottom: 1px solid #ccc;">
19889
+ <th style="padding: 4px;">Account</th>
19890
+ <th style="padding: 4px; width: 140px;">Debit</th>
19891
+ <th style="padding: 4px; width: 140px;">Credit</th>
19892
+ <th style="width: 48px;"></th>
19893
+ </tr>
19894
+ <tr *ngFor="let line of lines; let i = index">
19895
+ <td style="padding: 4px;">
19896
+ <mat-form-field style="width: 100%;" subscriptSizing="dynamic">
19897
+ <mat-select [(ngModel)]="line.accountID" placeholder="Select account">
19898
+ <mat-option *ngFor="let option of accounts" [value]="option.value">{{ option.name }}</mat-option>
19899
+ </mat-select>
19900
+ </mat-form-field>
19901
+ </td>
19902
+ <td style="padding: 4px;">
19903
+ <mat-form-field style="width: 100%;" subscriptSizing="dynamic">
19904
+ <input matInput type="number" min="0" [(ngModel)]="line.debit" (ngModelChange)="line.debit && (line.credit = 0)">
19905
+ </mat-form-field>
19906
+ </td>
19907
+ <td style="padding: 4px;">
19908
+ <mat-form-field style="width: 100%;" subscriptSizing="dynamic">
19909
+ <input matInput type="number" min="0" [(ngModel)]="line.credit" (ngModelChange)="line.credit && (line.debit = 0)">
19910
+ </mat-form-field>
19911
+ </td>
19912
+ <td>
19913
+ <button mat-icon-button (click)="removeLine(i)" [disabled]="lines.length <= 2">
19914
+ <mat-icon>delete</mat-icon>
19915
+ </button>
19916
+ </td>
19917
+ </tr>
19918
+ <tr style="border-top: 2px solid #333;">
19919
+ <td style="padding: 8px 4px;">
19920
+ <button mat-stroked-button (click)="addLine()"><mat-icon>add</mat-icon> Add Line</button>
19921
+ </td>
19922
+ <td style="padding: 8px 4px;"><b>{{ totalDebits | number:'1.2-2' }}</b></td>
19923
+ <td style="padding: 8px 4px;"><b>{{ totalCredits | number:'1.2-2' }}</b></td>
19924
+ <td></td>
19925
+ </tr>
19926
+ </table>
19927
+ <p *ngIf="!isBalanced && (totalDebits || totalCredits)" style="color: red;">
19928
+ <em>Journal is out of balance by {{ (totalDebits - totalCredits) | number:'1.2-2' }}</em>
19929
+ </p>
19930
+ </mat-dialog-content>
19931
+ <mat-dialog-actions align="end">
19932
+ <button mat-button (click)="dialogRef.close(false)">Cancel</button>
19933
+ <button mat-flat-button color="primary" (click)="post()" [disabled]="!canPost">Post Journal</button>
19934
+ </mat-dialog-actions>
19935
19935
  `,
19936
19936
  standalone: false
19937
19937
  }]
@@ -19957,26 +19957,26 @@ class TransactionsComponent {
19957
19957
  this.page?.tableReload.next(true); });
19958
19958
  }
19959
19959
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: TransactionsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
19960
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: TransactionsComponent, isStandalone: false, selector: "spa-transactions", viewQueries: [{ propertyName: "page", first: true, predicate: PageComponent, descendants: true }], ngImport: i0, template: `
19961
- <div style="display: flex; justify-content: flex-end;">
19962
- <button mat-stroked-button (click)="newJournal()">
19963
- <mat-icon>library_books</mat-icon> New Journal
19964
- </button>
19965
- </div>
19966
- <spa-page [config]="pageConfig"></spa-page>
19960
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: TransactionsComponent, isStandalone: false, selector: "spa-transactions", viewQueries: [{ propertyName: "page", first: true, predicate: PageComponent, descendants: true }], ngImport: i0, template: `
19961
+ <div style="display: flex; justify-content: flex-end;">
19962
+ <button mat-stroked-button (click)="newJournal()">
19963
+ <mat-icon>library_books</mat-icon> New Journal
19964
+ </button>
19965
+ </div>
19966
+ <spa-page [config]="pageConfig"></spa-page>
19967
19967
  `, isInline: true, dependencies: [{ kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: PageComponent, selector: "spa-page", inputs: ["config"], outputs: ["searchModeActivated", "searchModeDeactivated", "refreshClick", "actionClick", "actionResponse", "inputChange", "createClick", "searchClick", "dataLoad", "titleActionChange"] }] }); }
19968
19968
  }
19969
19969
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: TransactionsComponent, decorators: [{
19970
19970
  type: Component,
19971
19971
  args: [{
19972
19972
  selector: 'spa-transactions',
19973
- template: `
19974
- <div style="display: flex; justify-content: flex-end;">
19975
- <button mat-stroked-button (click)="newJournal()">
19976
- <mat-icon>library_books</mat-icon> New Journal
19977
- </button>
19978
- </div>
19979
- <spa-page [config]="pageConfig"></spa-page>
19973
+ template: `
19974
+ <div style="display: flex; justify-content: flex-end;">
19975
+ <button mat-stroked-button (click)="newJournal()">
19976
+ <mat-icon>library_books</mat-icon> New Journal
19977
+ </button>
19978
+ </div>
19979
+ <spa-page [config]="pageConfig"></spa-page>
19980
19980
  `,
19981
19981
  standalone: false
19982
19982
  }]
@@ -20099,166 +20099,166 @@ class StatementComponent {
20099
20099
  });
20100
20100
  }
20101
20101
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: StatementComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
20102
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: StatementComponent, isStandalone: false, selector: "spa-statement", ngImport: i0, template: `
20103
- <mat-tab-group>
20104
- <mat-tab label="Invoice History">
20105
- <div style="padding-top: 8px;">
20106
- <spa-page [config]="pageConfig"></spa-page>
20107
- </div>
20108
- </mat-tab>
20109
-
20110
- <mat-tab label="Customer Statement">
20111
- <ng-container *ngTemplateOutlet="statementTab; context: { kind: 'customer' }"></ng-container>
20112
- </mat-tab>
20113
-
20114
- <mat-tab label="Supplier Statement">
20115
- <ng-container *ngTemplateOutlet="statementTab; context: { kind: 'supplier' }"></ng-container>
20116
- </mat-tab>
20117
- </mat-tab-group>
20118
-
20119
- <ng-template #statementTab let-kind="kind">
20120
- <div style="padding: 16px 0;">
20121
- <div style="display: flex; gap: 16px; align-items: center; flex-wrap: wrap;">
20122
- <mat-form-field style="min-width: 240px;">
20123
- <mat-label>{{ kind === 'customer' ? 'Customer' : 'Supplier' }}</mat-label>
20124
- <mat-select [(ngModel)]="state[kind].entityId">
20125
- <mat-option *ngFor="let option of state[kind].options" [value]="option.value">{{ option.name }}</mat-option>
20126
- </mat-select>
20127
- </mat-form-field>
20128
- <mat-form-field>
20129
- <mat-label>From</mat-label>
20130
- <input matInput type="date" [(ngModel)]="state[kind].from">
20131
- </mat-form-field>
20132
- <mat-form-field>
20133
- <mat-label>To</mat-label>
20134
- <input matInput type="date" [(ngModel)]="state[kind].to">
20135
- </mat-form-field>
20136
- <button mat-flat-button color="primary" (click)="load(kind)" [disabled]="!state[kind].entityId">Run</button>
20137
- <button mat-stroked-button (click)="export(kind)" [disabled]="!state[kind].entityId">
20138
- <mat-icon>download</mat-icon> Export to Excel
20139
- </button>
20140
- <button mat-stroked-button *ngIf="kind === 'customer'" (click)="exportPdf(kind)" [disabled]="!state[kind].entityId">
20141
- <mat-icon>picture_as_pdf</mat-icon> Download PDF
20142
- </button>
20143
- </div>
20144
-
20145
- <mat-card *ngIf="state[kind].data" style="padding: 16px;">
20146
- <div style="display: flex; gap: 32px; flex-wrap: wrap; margin-bottom: 16px;">
20147
- <div><small>{{ kind === 'customer' ? 'Total Invoiced' : 'Total Purchased' }}</small><br><b>{{ (state[kind].data.totalInvoiced ?? state[kind].data.totalPurchased) | number:'1.2-2' }}</b></div>
20148
- <div><small>Total Paid</small><br><b>{{ state[kind].data.totalPaid | number:'1.2-2' }}</b></div>
20149
- <div *ngIf="kind === 'customer'"><small>Total Credited</small><br><b>{{ state[kind].data.totalCredited | number:'1.2-2' }}</b></div>
20150
- <div><small>Total Outstanding</small><br><b [style.color]="state[kind].data.totalOutstanding > 0 ? 'red' : 'green'">{{ state[kind].data.totalOutstanding | number:'1.2-2' }}</b></div>
20151
- </div>
20152
-
20153
- <table style="width: 100%; border-collapse: collapse;" *ngIf="state[kind].data.lines?.length; else noLines">
20154
- <tr style="text-align: left; border-bottom: 2px solid #333;">
20155
- <th style="padding: 6px;">Date</th>
20156
- <th style="padding: 6px;">Reference</th>
20157
- <th style="padding: 6px;">Type</th>
20158
- <th style="padding: 6px; text-align: right;">Total</th>
20159
- <th style="padding: 6px; text-align: right;">Paid</th>
20160
- <th style="padding: 6px; text-align: right;">Outstanding</th>
20161
- <th style="padding: 6px;">Status</th>
20162
- <th style="padding: 6px;">Aging</th>
20163
- </tr>
20164
- <tr *ngFor="let line of state[kind].data.lines" style="border-bottom: 1px solid #eee;">
20165
- <td style="padding: 6px;">{{ line.date | date:'mediumDate' }}</td>
20166
- <td style="padding: 6px;">{{ line.reference }}</td>
20167
- <td style="padding: 6px;">{{ line.type }}</td>
20168
- <td style="padding: 6px; text-align: right;">{{ line.total | number:'1.2-2' }}</td>
20169
- <td style="padding: 6px; text-align: right;">{{ line.paid | number:'1.2-2' }}</td>
20170
- <td style="padding: 6px; text-align: right;">{{ line.outstanding | number:'1.2-2' }}</td>
20171
- <td style="padding: 6px;">{{ line.status }}</td>
20172
- <td style="padding: 6px;">{{ line.agingBucket }}</td>
20173
- </tr>
20174
- </table>
20175
- <ng-template #noLines><p><em>No open items for the selected period</em></p></ng-template>
20176
- </mat-card>
20177
- </div>
20178
- </ng-template>
20102
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: StatementComponent, isStandalone: false, selector: "spa-statement", ngImport: i0, template: `
20103
+ <mat-tab-group>
20104
+ <mat-tab label="Invoice History">
20105
+ <div style="padding-top: 8px;">
20106
+ <spa-page [config]="pageConfig"></spa-page>
20107
+ </div>
20108
+ </mat-tab>
20109
+
20110
+ <mat-tab label="Customer Statement">
20111
+ <ng-container *ngTemplateOutlet="statementTab; context: { kind: 'customer' }"></ng-container>
20112
+ </mat-tab>
20113
+
20114
+ <mat-tab label="Supplier Statement">
20115
+ <ng-container *ngTemplateOutlet="statementTab; context: { kind: 'supplier' }"></ng-container>
20116
+ </mat-tab>
20117
+ </mat-tab-group>
20118
+
20119
+ <ng-template #statementTab let-kind="kind">
20120
+ <div style="padding: 16px 0;">
20121
+ <div style="display: flex; gap: 16px; align-items: center; flex-wrap: wrap;">
20122
+ <mat-form-field style="min-width: 240px;">
20123
+ <mat-label>{{ kind === 'customer' ? 'Customer' : 'Supplier' }}</mat-label>
20124
+ <mat-select [(ngModel)]="state[kind].entityId">
20125
+ <mat-option *ngFor="let option of state[kind].options" [value]="option.value">{{ option.name }}</mat-option>
20126
+ </mat-select>
20127
+ </mat-form-field>
20128
+ <mat-form-field>
20129
+ <mat-label>From</mat-label>
20130
+ <input matInput type="date" [(ngModel)]="state[kind].from">
20131
+ </mat-form-field>
20132
+ <mat-form-field>
20133
+ <mat-label>To</mat-label>
20134
+ <input matInput type="date" [(ngModel)]="state[kind].to">
20135
+ </mat-form-field>
20136
+ <button mat-flat-button color="primary" (click)="load(kind)" [disabled]="!state[kind].entityId">Run</button>
20137
+ <button mat-stroked-button (click)="export(kind)" [disabled]="!state[kind].entityId">
20138
+ <mat-icon>download</mat-icon> Export to Excel
20139
+ </button>
20140
+ <button mat-stroked-button *ngIf="kind === 'customer'" (click)="exportPdf(kind)" [disabled]="!state[kind].entityId">
20141
+ <mat-icon>picture_as_pdf</mat-icon> Download PDF
20142
+ </button>
20143
+ </div>
20144
+
20145
+ <mat-card *ngIf="state[kind].data" style="padding: 16px;">
20146
+ <div style="display: flex; gap: 32px; flex-wrap: wrap; margin-bottom: 16px;">
20147
+ <div><small>{{ kind === 'customer' ? 'Total Invoiced' : 'Total Purchased' }}</small><br><b>{{ (state[kind].data.totalInvoiced ?? state[kind].data.totalPurchased) | number:'1.2-2' }}</b></div>
20148
+ <div><small>Total Paid</small><br><b>{{ state[kind].data.totalPaid | number:'1.2-2' }}</b></div>
20149
+ <div *ngIf="kind === 'customer'"><small>Total Credited</small><br><b>{{ state[kind].data.totalCredited | number:'1.2-2' }}</b></div>
20150
+ <div><small>Total Outstanding</small><br><b [style.color]="state[kind].data.totalOutstanding > 0 ? 'red' : 'green'">{{ state[kind].data.totalOutstanding | number:'1.2-2' }}</b></div>
20151
+ </div>
20152
+
20153
+ <table style="width: 100%; border-collapse: collapse;" *ngIf="state[kind].data.lines?.length; else noLines">
20154
+ <tr style="text-align: left; border-bottom: 2px solid #333;">
20155
+ <th style="padding: 6px;">Date</th>
20156
+ <th style="padding: 6px;">Reference</th>
20157
+ <th style="padding: 6px;">Type</th>
20158
+ <th style="padding: 6px; text-align: right;">Total</th>
20159
+ <th style="padding: 6px; text-align: right;">Paid</th>
20160
+ <th style="padding: 6px; text-align: right;">Outstanding</th>
20161
+ <th style="padding: 6px;">Status</th>
20162
+ <th style="padding: 6px;">Aging</th>
20163
+ </tr>
20164
+ <tr *ngFor="let line of state[kind].data.lines" style="border-bottom: 1px solid #eee;">
20165
+ <td style="padding: 6px;">{{ line.date | date:'mediumDate' }}</td>
20166
+ <td style="padding: 6px;">{{ line.reference }}</td>
20167
+ <td style="padding: 6px;">{{ line.type }}</td>
20168
+ <td style="padding: 6px; text-align: right;">{{ line.total | number:'1.2-2' }}</td>
20169
+ <td style="padding: 6px; text-align: right;">{{ line.paid | number:'1.2-2' }}</td>
20170
+ <td style="padding: 6px; text-align: right;">{{ line.outstanding | number:'1.2-2' }}</td>
20171
+ <td style="padding: 6px;">{{ line.status }}</td>
20172
+ <td style="padding: 6px;">{{ line.agingBucket }}</td>
20173
+ </tr>
20174
+ </table>
20175
+ <ng-template #noLines><p><em>No open items for the selected period</em></p></ng-template>
20176
+ </mat-card>
20177
+ </div>
20178
+ </ng-template>
20179
20179
  `, isInline: true, dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i5$2.MatTab, selector: "mat-tab", inputs: ["disabled", "label", "aria-label", "aria-labelledby", "labelClass", "bodyClass", "id"], exportAs: ["matTab"] }, { kind: "component", type: i5$2.MatTabGroup, selector: "mat-tab-group", inputs: ["color", "fitInkBarToContent", "mat-stretch-tabs", "mat-align-tabs", "dynamicHeight", "selectedIndex", "headerPosition", "animationDuration", "contentTabIndex", "disablePagination", "disableRipple", "preserveContent", "backgroundColor", "aria-label", "aria-labelledby"], outputs: ["selectedIndexChange", "focusChange", "animationDone", "selectedTabChange"], exportAs: ["matTabGroup"] }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4$3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i18.MatCard, selector: "mat-card", inputs: ["appearance"], exportAs: ["matCard"] }, { kind: "component", type: i7$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i7$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: PageComponent, selector: "spa-page", inputs: ["config"], outputs: ["searchModeActivated", "searchModeDeactivated", "refreshClick", "actionClick", "actionResponse", "inputChange", "createClick", "searchClick", "dataLoad", "titleActionChange"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }, { kind: "pipe", type: i1.DatePipe, name: "date" }] }); }
20180
20180
  }
20181
20181
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: StatementComponent, decorators: [{
20182
20182
  type: Component,
20183
20183
  args: [{
20184
20184
  selector: 'spa-statement',
20185
- template: `
20186
- <mat-tab-group>
20187
- <mat-tab label="Invoice History">
20188
- <div style="padding-top: 8px;">
20189
- <spa-page [config]="pageConfig"></spa-page>
20190
- </div>
20191
- </mat-tab>
20192
-
20193
- <mat-tab label="Customer Statement">
20194
- <ng-container *ngTemplateOutlet="statementTab; context: { kind: 'customer' }"></ng-container>
20195
- </mat-tab>
20196
-
20197
- <mat-tab label="Supplier Statement">
20198
- <ng-container *ngTemplateOutlet="statementTab; context: { kind: 'supplier' }"></ng-container>
20199
- </mat-tab>
20200
- </mat-tab-group>
20201
-
20202
- <ng-template #statementTab let-kind="kind">
20203
- <div style="padding: 16px 0;">
20204
- <div style="display: flex; gap: 16px; align-items: center; flex-wrap: wrap;">
20205
- <mat-form-field style="min-width: 240px;">
20206
- <mat-label>{{ kind === 'customer' ? 'Customer' : 'Supplier' }}</mat-label>
20207
- <mat-select [(ngModel)]="state[kind].entityId">
20208
- <mat-option *ngFor="let option of state[kind].options" [value]="option.value">{{ option.name }}</mat-option>
20209
- </mat-select>
20210
- </mat-form-field>
20211
- <mat-form-field>
20212
- <mat-label>From</mat-label>
20213
- <input matInput type="date" [(ngModel)]="state[kind].from">
20214
- </mat-form-field>
20215
- <mat-form-field>
20216
- <mat-label>To</mat-label>
20217
- <input matInput type="date" [(ngModel)]="state[kind].to">
20218
- </mat-form-field>
20219
- <button mat-flat-button color="primary" (click)="load(kind)" [disabled]="!state[kind].entityId">Run</button>
20220
- <button mat-stroked-button (click)="export(kind)" [disabled]="!state[kind].entityId">
20221
- <mat-icon>download</mat-icon> Export to Excel
20222
- </button>
20223
- <button mat-stroked-button *ngIf="kind === 'customer'" (click)="exportPdf(kind)" [disabled]="!state[kind].entityId">
20224
- <mat-icon>picture_as_pdf</mat-icon> Download PDF
20225
- </button>
20226
- </div>
20227
-
20228
- <mat-card *ngIf="state[kind].data" style="padding: 16px;">
20229
- <div style="display: flex; gap: 32px; flex-wrap: wrap; margin-bottom: 16px;">
20230
- <div><small>{{ kind === 'customer' ? 'Total Invoiced' : 'Total Purchased' }}</small><br><b>{{ (state[kind].data.totalInvoiced ?? state[kind].data.totalPurchased) | number:'1.2-2' }}</b></div>
20231
- <div><small>Total Paid</small><br><b>{{ state[kind].data.totalPaid | number:'1.2-2' }}</b></div>
20232
- <div *ngIf="kind === 'customer'"><small>Total Credited</small><br><b>{{ state[kind].data.totalCredited | number:'1.2-2' }}</b></div>
20233
- <div><small>Total Outstanding</small><br><b [style.color]="state[kind].data.totalOutstanding > 0 ? 'red' : 'green'">{{ state[kind].data.totalOutstanding | number:'1.2-2' }}</b></div>
20234
- </div>
20235
-
20236
- <table style="width: 100%; border-collapse: collapse;" *ngIf="state[kind].data.lines?.length; else noLines">
20237
- <tr style="text-align: left; border-bottom: 2px solid #333;">
20238
- <th style="padding: 6px;">Date</th>
20239
- <th style="padding: 6px;">Reference</th>
20240
- <th style="padding: 6px;">Type</th>
20241
- <th style="padding: 6px; text-align: right;">Total</th>
20242
- <th style="padding: 6px; text-align: right;">Paid</th>
20243
- <th style="padding: 6px; text-align: right;">Outstanding</th>
20244
- <th style="padding: 6px;">Status</th>
20245
- <th style="padding: 6px;">Aging</th>
20246
- </tr>
20247
- <tr *ngFor="let line of state[kind].data.lines" style="border-bottom: 1px solid #eee;">
20248
- <td style="padding: 6px;">{{ line.date | date:'mediumDate' }}</td>
20249
- <td style="padding: 6px;">{{ line.reference }}</td>
20250
- <td style="padding: 6px;">{{ line.type }}</td>
20251
- <td style="padding: 6px; text-align: right;">{{ line.total | number:'1.2-2' }}</td>
20252
- <td style="padding: 6px; text-align: right;">{{ line.paid | number:'1.2-2' }}</td>
20253
- <td style="padding: 6px; text-align: right;">{{ line.outstanding | number:'1.2-2' }}</td>
20254
- <td style="padding: 6px;">{{ line.status }}</td>
20255
- <td style="padding: 6px;">{{ line.agingBucket }}</td>
20256
- </tr>
20257
- </table>
20258
- <ng-template #noLines><p><em>No open items for the selected period</em></p></ng-template>
20259
- </mat-card>
20260
- </div>
20261
- </ng-template>
20185
+ template: `
20186
+ <mat-tab-group>
20187
+ <mat-tab label="Invoice History">
20188
+ <div style="padding-top: 8px;">
20189
+ <spa-page [config]="pageConfig"></spa-page>
20190
+ </div>
20191
+ </mat-tab>
20192
+
20193
+ <mat-tab label="Customer Statement">
20194
+ <ng-container *ngTemplateOutlet="statementTab; context: { kind: 'customer' }"></ng-container>
20195
+ </mat-tab>
20196
+
20197
+ <mat-tab label="Supplier Statement">
20198
+ <ng-container *ngTemplateOutlet="statementTab; context: { kind: 'supplier' }"></ng-container>
20199
+ </mat-tab>
20200
+ </mat-tab-group>
20201
+
20202
+ <ng-template #statementTab let-kind="kind">
20203
+ <div style="padding: 16px 0;">
20204
+ <div style="display: flex; gap: 16px; align-items: center; flex-wrap: wrap;">
20205
+ <mat-form-field style="min-width: 240px;">
20206
+ <mat-label>{{ kind === 'customer' ? 'Customer' : 'Supplier' }}</mat-label>
20207
+ <mat-select [(ngModel)]="state[kind].entityId">
20208
+ <mat-option *ngFor="let option of state[kind].options" [value]="option.value">{{ option.name }}</mat-option>
20209
+ </mat-select>
20210
+ </mat-form-field>
20211
+ <mat-form-field>
20212
+ <mat-label>From</mat-label>
20213
+ <input matInput type="date" [(ngModel)]="state[kind].from">
20214
+ </mat-form-field>
20215
+ <mat-form-field>
20216
+ <mat-label>To</mat-label>
20217
+ <input matInput type="date" [(ngModel)]="state[kind].to">
20218
+ </mat-form-field>
20219
+ <button mat-flat-button color="primary" (click)="load(kind)" [disabled]="!state[kind].entityId">Run</button>
20220
+ <button mat-stroked-button (click)="export(kind)" [disabled]="!state[kind].entityId">
20221
+ <mat-icon>download</mat-icon> Export to Excel
20222
+ </button>
20223
+ <button mat-stroked-button *ngIf="kind === 'customer'" (click)="exportPdf(kind)" [disabled]="!state[kind].entityId">
20224
+ <mat-icon>picture_as_pdf</mat-icon> Download PDF
20225
+ </button>
20226
+ </div>
20227
+
20228
+ <mat-card *ngIf="state[kind].data" style="padding: 16px;">
20229
+ <div style="display: flex; gap: 32px; flex-wrap: wrap; margin-bottom: 16px;">
20230
+ <div><small>{{ kind === 'customer' ? 'Total Invoiced' : 'Total Purchased' }}</small><br><b>{{ (state[kind].data.totalInvoiced ?? state[kind].data.totalPurchased) | number:'1.2-2' }}</b></div>
20231
+ <div><small>Total Paid</small><br><b>{{ state[kind].data.totalPaid | number:'1.2-2' }}</b></div>
20232
+ <div *ngIf="kind === 'customer'"><small>Total Credited</small><br><b>{{ state[kind].data.totalCredited | number:'1.2-2' }}</b></div>
20233
+ <div><small>Total Outstanding</small><br><b [style.color]="state[kind].data.totalOutstanding > 0 ? 'red' : 'green'">{{ state[kind].data.totalOutstanding | number:'1.2-2' }}</b></div>
20234
+ </div>
20235
+
20236
+ <table style="width: 100%; border-collapse: collapse;" *ngIf="state[kind].data.lines?.length; else noLines">
20237
+ <tr style="text-align: left; border-bottom: 2px solid #333;">
20238
+ <th style="padding: 6px;">Date</th>
20239
+ <th style="padding: 6px;">Reference</th>
20240
+ <th style="padding: 6px;">Type</th>
20241
+ <th style="padding: 6px; text-align: right;">Total</th>
20242
+ <th style="padding: 6px; text-align: right;">Paid</th>
20243
+ <th style="padding: 6px; text-align: right;">Outstanding</th>
20244
+ <th style="padding: 6px;">Status</th>
20245
+ <th style="padding: 6px;">Aging</th>
20246
+ </tr>
20247
+ <tr *ngFor="let line of state[kind].data.lines" style="border-bottom: 1px solid #eee;">
20248
+ <td style="padding: 6px;">{{ line.date | date:'mediumDate' }}</td>
20249
+ <td style="padding: 6px;">{{ line.reference }}</td>
20250
+ <td style="padding: 6px;">{{ line.type }}</td>
20251
+ <td style="padding: 6px; text-align: right;">{{ line.total | number:'1.2-2' }}</td>
20252
+ <td style="padding: 6px; text-align: right;">{{ line.paid | number:'1.2-2' }}</td>
20253
+ <td style="padding: 6px; text-align: right;">{{ line.outstanding | number:'1.2-2' }}</td>
20254
+ <td style="padding: 6px;">{{ line.status }}</td>
20255
+ <td style="padding: 6px;">{{ line.agingBucket }}</td>
20256
+ </tr>
20257
+ </table>
20258
+ <ng-template #noLines><p><em>No open items for the selected period</em></p></ng-template>
20259
+ </mat-card>
20260
+ </div>
20261
+ </ng-template>
20262
20262
  `,
20263
20263
  standalone: false
20264
20264
  }]
@@ -21144,140 +21144,140 @@ class ReceiptDialogComponent {
21144
21144
  });
21145
21145
  }
21146
21146
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ReceiptDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
21147
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ReceiptDialogComponent, isStandalone: false, selector: "spa-receipt-dialog", ngImport: i0, template: `
21148
- <h2 mat-dialog-title>{{ allocateMode ? 'Allocate Receipt ' + data.receiptNumber : 'New Receipt' }}</h2>
21149
- <mat-dialog-content>
21150
- <div style="display: flex; gap: 16px; flex-wrap: wrap;" *ngIf="!allocateMode">
21151
- <mat-form-field style="min-width: 220px;">
21152
- <mat-label>Customer</mat-label>
21153
- <mat-select [(ngModel)]="customerID" (ngModelChange)="loadOpenInvoices()">
21154
- <mat-option *ngFor="let option of customers" [value]="option.value">{{ option.name }}</mat-option>
21155
- </mat-select>
21156
- </mat-form-field>
21157
- <mat-form-field>
21158
- <mat-label>Date</mat-label>
21159
- <input matInput type="date" [(ngModel)]="receiptDate">
21160
- </mat-form-field>
21161
- <mat-form-field>
21162
- <mat-label>Amount</mat-label>
21163
- <input matInput type="number" min="0" [(ngModel)]="amount">
21164
- </mat-form-field>
21165
- <mat-form-field>
21166
- <mat-label>Method</mat-label>
21167
- <mat-select [(ngModel)]="method">
21168
- <mat-option [value]="0">Cash</mat-option>
21169
- <mat-option [value]="1">Bank</mat-option>
21170
- </mat-select>
21171
- </mat-form-field>
21172
- <mat-form-field style="min-width: 200px;">
21173
- <mat-label>Reference</mat-label>
21174
- <input matInput [(ngModel)]="reference" placeholder="e.g. bank ref / receipt book #">
21175
- </mat-form-field>
21176
- </div>
21177
- <p *ngIf="allocateMode"><b>Unallocated balance: {{ available | number:'1.2-2' }}</b></p>
21178
-
21179
- <table style="width: 100%; border-collapse: collapse;" *ngIf="openInvoices.length; else noInvoices">
21180
- <tr style="text-align: left; border-bottom: 1px solid #ccc;">
21181
- <th style="padding: 4px;">Invoice #</th>
21182
- <th style="padding: 4px;">Date</th>
21183
- <th style="padding: 4px; text-align: right;">Outstanding</th>
21184
- <th style="padding: 4px; width: 160px;">Allocate</th>
21185
- </tr>
21186
- <tr *ngFor="let invoice of openInvoices" style="border-bottom: 1px solid #eee;">
21187
- <td style="padding: 4px;">{{ invoice.invoiceNumber }}</td>
21188
- <td style="padding: 4px;">{{ invoice.invoiceDate | date:'mediumDate' }}</td>
21189
- <td style="padding: 4px; text-align: right;">{{ invoice.outstandingAmount | number:'1.2-2' }}</td>
21190
- <td style="padding: 4px;">
21191
- <mat-form-field style="width: 100%;" subscriptSizing="dynamic">
21192
- <input matInput type="number" min="0" [max]="invoice.outstandingAmount" [(ngModel)]="invoice.allocation">
21193
- </mat-form-field>
21194
- </td>
21195
- </tr>
21196
- <tr style="border-top: 2px solid #333;">
21197
- <td style="padding: 8px 4px;" colspan="2">
21198
- <button mat-stroked-button (click)="autoAllocate()">Auto Allocate (oldest first)</button>
21199
- </td>
21200
- <td style="padding: 8px 4px; text-align: right;"><b>Allocated:</b></td>
21201
- <td style="padding: 8px 4px;"><b>{{ totalAllocated | number:'1.2-2' }}</b></td>
21202
- </tr>
21203
- </table>
21204
- <ng-template #noInvoices><p><em>{{ customerID ? 'No open invoices — the full amount will be recorded on-account' : 'Select a customer to load open invoices' }}</em></p></ng-template>
21205
- <p *ngIf="overAllocated" style="color: red;"><em>Allocations exceed the {{ allocateMode ? 'unallocated balance' : 'receipt amount' }}</em></p>
21206
- </mat-dialog-content>
21207
- <mat-dialog-actions align="end">
21208
- <button mat-button (click)="dialogRef.close(false)">Cancel</button>
21209
- <button mat-flat-button color="primary" (click)="save()" [disabled]="!canSave">{{ allocateMode ? 'Allocate' : 'Record Receipt' }}</button>
21210
- </mat-dialog-actions>
21147
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ReceiptDialogComponent, isStandalone: false, selector: "spa-receipt-dialog", ngImport: i0, template: `
21148
+ <h2 mat-dialog-title>{{ allocateMode ? 'Allocate Receipt ' + data.receiptNumber : 'New Receipt' }}</h2>
21149
+ <mat-dialog-content>
21150
+ <div style="display: flex; gap: 16px; flex-wrap: wrap;" *ngIf="!allocateMode">
21151
+ <mat-form-field style="min-width: 220px;">
21152
+ <mat-label>Customer</mat-label>
21153
+ <mat-select [(ngModel)]="customerID" (ngModelChange)="loadOpenInvoices()">
21154
+ <mat-option *ngFor="let option of customers" [value]="option.value">{{ option.name }}</mat-option>
21155
+ </mat-select>
21156
+ </mat-form-field>
21157
+ <mat-form-field>
21158
+ <mat-label>Date</mat-label>
21159
+ <input matInput type="date" [(ngModel)]="receiptDate">
21160
+ </mat-form-field>
21161
+ <mat-form-field>
21162
+ <mat-label>Amount</mat-label>
21163
+ <input matInput type="number" min="0" [(ngModel)]="amount">
21164
+ </mat-form-field>
21165
+ <mat-form-field>
21166
+ <mat-label>Method</mat-label>
21167
+ <mat-select [(ngModel)]="method">
21168
+ <mat-option [value]="0">Cash</mat-option>
21169
+ <mat-option [value]="1">Bank</mat-option>
21170
+ </mat-select>
21171
+ </mat-form-field>
21172
+ <mat-form-field style="min-width: 200px;">
21173
+ <mat-label>Reference</mat-label>
21174
+ <input matInput [(ngModel)]="reference" placeholder="e.g. bank ref / receipt book #">
21175
+ </mat-form-field>
21176
+ </div>
21177
+ <p *ngIf="allocateMode"><b>Unallocated balance: {{ available | number:'1.2-2' }}</b></p>
21178
+
21179
+ <table style="width: 100%; border-collapse: collapse;" *ngIf="openInvoices.length; else noInvoices">
21180
+ <tr style="text-align: left; border-bottom: 1px solid #ccc;">
21181
+ <th style="padding: 4px;">Invoice #</th>
21182
+ <th style="padding: 4px;">Date</th>
21183
+ <th style="padding: 4px; text-align: right;">Outstanding</th>
21184
+ <th style="padding: 4px; width: 160px;">Allocate</th>
21185
+ </tr>
21186
+ <tr *ngFor="let invoice of openInvoices" style="border-bottom: 1px solid #eee;">
21187
+ <td style="padding: 4px;">{{ invoice.invoiceNumber }}</td>
21188
+ <td style="padding: 4px;">{{ invoice.invoiceDate | date:'mediumDate' }}</td>
21189
+ <td style="padding: 4px; text-align: right;">{{ invoice.outstandingAmount | number:'1.2-2' }}</td>
21190
+ <td style="padding: 4px;">
21191
+ <mat-form-field style="width: 100%;" subscriptSizing="dynamic">
21192
+ <input matInput type="number" min="0" [max]="invoice.outstandingAmount" [(ngModel)]="invoice.allocation">
21193
+ </mat-form-field>
21194
+ </td>
21195
+ </tr>
21196
+ <tr style="border-top: 2px solid #333;">
21197
+ <td style="padding: 8px 4px;" colspan="2">
21198
+ <button mat-stroked-button (click)="autoAllocate()">Auto Allocate (oldest first)</button>
21199
+ </td>
21200
+ <td style="padding: 8px 4px; text-align: right;"><b>Allocated:</b></td>
21201
+ <td style="padding: 8px 4px;"><b>{{ totalAllocated | number:'1.2-2' }}</b></td>
21202
+ </tr>
21203
+ </table>
21204
+ <ng-template #noInvoices><p><em>{{ customerID ? 'No open invoices — the full amount will be recorded on-account' : 'Select a customer to load open invoices' }}</em></p></ng-template>
21205
+ <p *ngIf="overAllocated" style="color: red;"><em>Allocations exceed the {{ allocateMode ? 'unallocated balance' : 'receipt amount' }}</em></p>
21206
+ </mat-dialog-content>
21207
+ <mat-dialog-actions align="end">
21208
+ <button mat-button (click)="dialogRef.close(false)">Cancel</button>
21209
+ <button mat-flat-button color="primary" (click)="save()" [disabled]="!canSave">{{ allocateMode ? 'Allocate' : 'Record Receipt' }}</button>
21210
+ </mat-dialog-actions>
21211
21211
  `, isInline: true, dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i2$2.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: i4.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i4.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i4.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4$3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i7$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i7$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }, { kind: "pipe", type: i1.DatePipe, name: "date" }] }); }
21212
21212
  }
21213
21213
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ReceiptDialogComponent, decorators: [{
21214
21214
  type: Component,
21215
21215
  args: [{
21216
21216
  selector: 'spa-receipt-dialog',
21217
- template: `
21218
- <h2 mat-dialog-title>{{ allocateMode ? 'Allocate Receipt ' + data.receiptNumber : 'New Receipt' }}</h2>
21219
- <mat-dialog-content>
21220
- <div style="display: flex; gap: 16px; flex-wrap: wrap;" *ngIf="!allocateMode">
21221
- <mat-form-field style="min-width: 220px;">
21222
- <mat-label>Customer</mat-label>
21223
- <mat-select [(ngModel)]="customerID" (ngModelChange)="loadOpenInvoices()">
21224
- <mat-option *ngFor="let option of customers" [value]="option.value">{{ option.name }}</mat-option>
21225
- </mat-select>
21226
- </mat-form-field>
21227
- <mat-form-field>
21228
- <mat-label>Date</mat-label>
21229
- <input matInput type="date" [(ngModel)]="receiptDate">
21230
- </mat-form-field>
21231
- <mat-form-field>
21232
- <mat-label>Amount</mat-label>
21233
- <input matInput type="number" min="0" [(ngModel)]="amount">
21234
- </mat-form-field>
21235
- <mat-form-field>
21236
- <mat-label>Method</mat-label>
21237
- <mat-select [(ngModel)]="method">
21238
- <mat-option [value]="0">Cash</mat-option>
21239
- <mat-option [value]="1">Bank</mat-option>
21240
- </mat-select>
21241
- </mat-form-field>
21242
- <mat-form-field style="min-width: 200px;">
21243
- <mat-label>Reference</mat-label>
21244
- <input matInput [(ngModel)]="reference" placeholder="e.g. bank ref / receipt book #">
21245
- </mat-form-field>
21246
- </div>
21247
- <p *ngIf="allocateMode"><b>Unallocated balance: {{ available | number:'1.2-2' }}</b></p>
21248
-
21249
- <table style="width: 100%; border-collapse: collapse;" *ngIf="openInvoices.length; else noInvoices">
21250
- <tr style="text-align: left; border-bottom: 1px solid #ccc;">
21251
- <th style="padding: 4px;">Invoice #</th>
21252
- <th style="padding: 4px;">Date</th>
21253
- <th style="padding: 4px; text-align: right;">Outstanding</th>
21254
- <th style="padding: 4px; width: 160px;">Allocate</th>
21255
- </tr>
21256
- <tr *ngFor="let invoice of openInvoices" style="border-bottom: 1px solid #eee;">
21257
- <td style="padding: 4px;">{{ invoice.invoiceNumber }}</td>
21258
- <td style="padding: 4px;">{{ invoice.invoiceDate | date:'mediumDate' }}</td>
21259
- <td style="padding: 4px; text-align: right;">{{ invoice.outstandingAmount | number:'1.2-2' }}</td>
21260
- <td style="padding: 4px;">
21261
- <mat-form-field style="width: 100%;" subscriptSizing="dynamic">
21262
- <input matInput type="number" min="0" [max]="invoice.outstandingAmount" [(ngModel)]="invoice.allocation">
21263
- </mat-form-field>
21264
- </td>
21265
- </tr>
21266
- <tr style="border-top: 2px solid #333;">
21267
- <td style="padding: 8px 4px;" colspan="2">
21268
- <button mat-stroked-button (click)="autoAllocate()">Auto Allocate (oldest first)</button>
21269
- </td>
21270
- <td style="padding: 8px 4px; text-align: right;"><b>Allocated:</b></td>
21271
- <td style="padding: 8px 4px;"><b>{{ totalAllocated | number:'1.2-2' }}</b></td>
21272
- </tr>
21273
- </table>
21274
- <ng-template #noInvoices><p><em>{{ customerID ? 'No open invoices — the full amount will be recorded on-account' : 'Select a customer to load open invoices' }}</em></p></ng-template>
21275
- <p *ngIf="overAllocated" style="color: red;"><em>Allocations exceed the {{ allocateMode ? 'unallocated balance' : 'receipt amount' }}</em></p>
21276
- </mat-dialog-content>
21277
- <mat-dialog-actions align="end">
21278
- <button mat-button (click)="dialogRef.close(false)">Cancel</button>
21279
- <button mat-flat-button color="primary" (click)="save()" [disabled]="!canSave">{{ allocateMode ? 'Allocate' : 'Record Receipt' }}</button>
21280
- </mat-dialog-actions>
21217
+ template: `
21218
+ <h2 mat-dialog-title>{{ allocateMode ? 'Allocate Receipt ' + data.receiptNumber : 'New Receipt' }}</h2>
21219
+ <mat-dialog-content>
21220
+ <div style="display: flex; gap: 16px; flex-wrap: wrap;" *ngIf="!allocateMode">
21221
+ <mat-form-field style="min-width: 220px;">
21222
+ <mat-label>Customer</mat-label>
21223
+ <mat-select [(ngModel)]="customerID" (ngModelChange)="loadOpenInvoices()">
21224
+ <mat-option *ngFor="let option of customers" [value]="option.value">{{ option.name }}</mat-option>
21225
+ </mat-select>
21226
+ </mat-form-field>
21227
+ <mat-form-field>
21228
+ <mat-label>Date</mat-label>
21229
+ <input matInput type="date" [(ngModel)]="receiptDate">
21230
+ </mat-form-field>
21231
+ <mat-form-field>
21232
+ <mat-label>Amount</mat-label>
21233
+ <input matInput type="number" min="0" [(ngModel)]="amount">
21234
+ </mat-form-field>
21235
+ <mat-form-field>
21236
+ <mat-label>Method</mat-label>
21237
+ <mat-select [(ngModel)]="method">
21238
+ <mat-option [value]="0">Cash</mat-option>
21239
+ <mat-option [value]="1">Bank</mat-option>
21240
+ </mat-select>
21241
+ </mat-form-field>
21242
+ <mat-form-field style="min-width: 200px;">
21243
+ <mat-label>Reference</mat-label>
21244
+ <input matInput [(ngModel)]="reference" placeholder="e.g. bank ref / receipt book #">
21245
+ </mat-form-field>
21246
+ </div>
21247
+ <p *ngIf="allocateMode"><b>Unallocated balance: {{ available | number:'1.2-2' }}</b></p>
21248
+
21249
+ <table style="width: 100%; border-collapse: collapse;" *ngIf="openInvoices.length; else noInvoices">
21250
+ <tr style="text-align: left; border-bottom: 1px solid #ccc;">
21251
+ <th style="padding: 4px;">Invoice #</th>
21252
+ <th style="padding: 4px;">Date</th>
21253
+ <th style="padding: 4px; text-align: right;">Outstanding</th>
21254
+ <th style="padding: 4px; width: 160px;">Allocate</th>
21255
+ </tr>
21256
+ <tr *ngFor="let invoice of openInvoices" style="border-bottom: 1px solid #eee;">
21257
+ <td style="padding: 4px;">{{ invoice.invoiceNumber }}</td>
21258
+ <td style="padding: 4px;">{{ invoice.invoiceDate | date:'mediumDate' }}</td>
21259
+ <td style="padding: 4px; text-align: right;">{{ invoice.outstandingAmount | number:'1.2-2' }}</td>
21260
+ <td style="padding: 4px;">
21261
+ <mat-form-field style="width: 100%;" subscriptSizing="dynamic">
21262
+ <input matInput type="number" min="0" [max]="invoice.outstandingAmount" [(ngModel)]="invoice.allocation">
21263
+ </mat-form-field>
21264
+ </td>
21265
+ </tr>
21266
+ <tr style="border-top: 2px solid #333;">
21267
+ <td style="padding: 8px 4px;" colspan="2">
21268
+ <button mat-stroked-button (click)="autoAllocate()">Auto Allocate (oldest first)</button>
21269
+ </td>
21270
+ <td style="padding: 8px 4px; text-align: right;"><b>Allocated:</b></td>
21271
+ <td style="padding: 8px 4px;"><b>{{ totalAllocated | number:'1.2-2' }}</b></td>
21272
+ </tr>
21273
+ </table>
21274
+ <ng-template #noInvoices><p><em>{{ customerID ? 'No open invoices — the full amount will be recorded on-account' : 'Select a customer to load open invoices' }}</em></p></ng-template>
21275
+ <p *ngIf="overAllocated" style="color: red;"><em>Allocations exceed the {{ allocateMode ? 'unallocated balance' : 'receipt amount' }}</em></p>
21276
+ </mat-dialog-content>
21277
+ <mat-dialog-actions align="end">
21278
+ <button mat-button (click)="dialogRef.close(false)">Cancel</button>
21279
+ <button mat-flat-button color="primary" (click)="save()" [disabled]="!canSave">{{ allocateMode ? 'Allocate' : 'Record Receipt' }}</button>
21280
+ </mat-dialog-actions>
21281
21281
  `,
21282
21282
  standalone: false
21283
21283
  }]
@@ -21321,26 +21321,26 @@ class ReceiptsComponent {
21321
21321
  });
21322
21322
  }
21323
21323
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ReceiptsComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
21324
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ReceiptsComponent, isStandalone: false, selector: "spa-receipts", viewQueries: [{ propertyName: "page", first: true, predicate: PageComponent, descendants: true }], ngImport: i0, template: `
21325
- <div style="display: flex; justify-content: flex-end;">
21326
- <button mat-stroked-button (click)="openDialog()">
21327
- <mat-icon>receipt</mat-icon> New Receipt
21328
- </button>
21329
- </div>
21330
- <spa-page [config]="pageConfig"></spa-page>
21324
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ReceiptsComponent, isStandalone: false, selector: "spa-receipts", viewQueries: [{ propertyName: "page", first: true, predicate: PageComponent, descendants: true }], ngImport: i0, template: `
21325
+ <div style="display: flex; justify-content: flex-end;">
21326
+ <button mat-stroked-button (click)="openDialog()">
21327
+ <mat-icon>receipt</mat-icon> New Receipt
21328
+ </button>
21329
+ </div>
21330
+ <spa-page [config]="pageConfig"></spa-page>
21331
21331
  `, isInline: true, dependencies: [{ kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: PageComponent, selector: "spa-page", inputs: ["config"], outputs: ["searchModeActivated", "searchModeDeactivated", "refreshClick", "actionClick", "actionResponse", "inputChange", "createClick", "searchClick", "dataLoad", "titleActionChange"] }] }); }
21332
21332
  }
21333
21333
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ReceiptsComponent, decorators: [{
21334
21334
  type: Component,
21335
21335
  args: [{
21336
21336
  selector: 'spa-receipts',
21337
- template: `
21338
- <div style="display: flex; justify-content: flex-end;">
21339
- <button mat-stroked-button (click)="openDialog()">
21340
- <mat-icon>receipt</mat-icon> New Receipt
21341
- </button>
21342
- </div>
21343
- <spa-page [config]="pageConfig"></spa-page>
21337
+ template: `
21338
+ <div style="display: flex; justify-content: flex-end;">
21339
+ <button mat-stroked-button (click)="openDialog()">
21340
+ <mat-icon>receipt</mat-icon> New Receipt
21341
+ </button>
21342
+ </div>
21343
+ <spa-page [config]="pageConfig"></spa-page>
21344
21344
  `,
21345
21345
  standalone: false
21346
21346
  }]
@@ -21407,80 +21407,80 @@ class StatementImportDialogComponent {
21407
21407
  });
21408
21408
  }
21409
21409
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: StatementImportDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
21410
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: StatementImportDialogComponent, isStandalone: false, selector: "spa-statement-import-dialog", ngImport: i0, template: `
21411
- <h2 mat-dialog-title>Import Bank Statement</h2>
21412
- <mat-dialog-content>
21413
- <div style="display: flex; gap: 16px; flex-wrap: wrap;">
21414
- <mat-form-field style="min-width: 220px;">
21415
- <mat-label>Account</mat-label>
21416
- <mat-select [(ngModel)]="accountID">
21417
- <mat-option *ngFor="let option of accounts" [value]="option.value">{{ option.name }}</mat-option>
21418
- </mat-select>
21419
- </mat-form-field>
21420
- <mat-form-field>
21421
- <mat-label>From</mat-label>
21422
- <input matInput type="date" [(ngModel)]="statementFrom">
21423
- </mat-form-field>
21424
- <mat-form-field>
21425
- <mat-label>To</mat-label>
21426
- <input matInput type="date" [(ngModel)]="statementTo">
21427
- </mat-form-field>
21428
- <mat-form-field>
21429
- <mat-label>Closing Balance</mat-label>
21430
- <input matInput type="number" [(ngModel)]="closingBalance">
21431
- </mat-form-field>
21432
- </div>
21433
- <mat-form-field style="width: 100%;">
21434
- <mat-label>Statement lines (CSV: date, description, reference, amount — money out negative)</mat-label>
21435
- <textarea matInput rows="10" [(ngModel)]="csv" placeholder="2026-07-01, Deposit ABC, REF001, 1500.00&#10;2026-07-03, Bank charges, , -25.50"></textarea>
21436
- </mat-form-field>
21437
- <p *ngIf="parseError" style="color: red;"><em>{{ parseError }}</em></p>
21438
- <p *ngIf="parsedLines.length"><em>{{ parsedLines.length }} line(s) parsed, net movement {{ netMovement | number:'1.2-2' }}</em></p>
21439
- </mat-dialog-content>
21440
- <mat-dialog-actions align="end">
21441
- <button mat-button (click)="dialogRef.close(false)">Cancel</button>
21442
- <button mat-flat-button color="primary" (click)="save()" [disabled]="!canSave">Import</button>
21443
- </mat-dialog-actions>
21410
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: StatementImportDialogComponent, isStandalone: false, selector: "spa-statement-import-dialog", ngImport: i0, template: `
21411
+ <h2 mat-dialog-title>Import Bank Statement</h2>
21412
+ <mat-dialog-content>
21413
+ <div style="display: flex; gap: 16px; flex-wrap: wrap;">
21414
+ <mat-form-field style="min-width: 220px;">
21415
+ <mat-label>Account</mat-label>
21416
+ <mat-select [(ngModel)]="accountID">
21417
+ <mat-option *ngFor="let option of accounts" [value]="option.value">{{ option.name }}</mat-option>
21418
+ </mat-select>
21419
+ </mat-form-field>
21420
+ <mat-form-field>
21421
+ <mat-label>From</mat-label>
21422
+ <input matInput type="date" [(ngModel)]="statementFrom">
21423
+ </mat-form-field>
21424
+ <mat-form-field>
21425
+ <mat-label>To</mat-label>
21426
+ <input matInput type="date" [(ngModel)]="statementTo">
21427
+ </mat-form-field>
21428
+ <mat-form-field>
21429
+ <mat-label>Closing Balance</mat-label>
21430
+ <input matInput type="number" [(ngModel)]="closingBalance">
21431
+ </mat-form-field>
21432
+ </div>
21433
+ <mat-form-field style="width: 100%;">
21434
+ <mat-label>Statement lines (CSV: date, description, reference, amount — money out negative)</mat-label>
21435
+ <textarea matInput rows="10" [(ngModel)]="csv" placeholder="2026-07-01, Deposit ABC, REF001, 1500.00&#10;2026-07-03, Bank charges, , -25.50"></textarea>
21436
+ </mat-form-field>
21437
+ <p *ngIf="parseError" style="color: red;"><em>{{ parseError }}</em></p>
21438
+ <p *ngIf="parsedLines.length"><em>{{ parsedLines.length }} line(s) parsed, net movement {{ netMovement | number:'1.2-2' }}</em></p>
21439
+ </mat-dialog-content>
21440
+ <mat-dialog-actions align="end">
21441
+ <button mat-button (click)="dialogRef.close(false)">Cancel</button>
21442
+ <button mat-flat-button color="primary" (click)="save()" [disabled]="!canSave">Import</button>
21443
+ </mat-dialog-actions>
21444
21444
  `, isInline: true, dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: i4.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i4.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i4.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4$3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i7$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i7$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }] }); }
21445
21445
  }
21446
21446
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: StatementImportDialogComponent, decorators: [{
21447
21447
  type: Component,
21448
21448
  args: [{
21449
21449
  selector: 'spa-statement-import-dialog',
21450
- template: `
21451
- <h2 mat-dialog-title>Import Bank Statement</h2>
21452
- <mat-dialog-content>
21453
- <div style="display: flex; gap: 16px; flex-wrap: wrap;">
21454
- <mat-form-field style="min-width: 220px;">
21455
- <mat-label>Account</mat-label>
21456
- <mat-select [(ngModel)]="accountID">
21457
- <mat-option *ngFor="let option of accounts" [value]="option.value">{{ option.name }}</mat-option>
21458
- </mat-select>
21459
- </mat-form-field>
21460
- <mat-form-field>
21461
- <mat-label>From</mat-label>
21462
- <input matInput type="date" [(ngModel)]="statementFrom">
21463
- </mat-form-field>
21464
- <mat-form-field>
21465
- <mat-label>To</mat-label>
21466
- <input matInput type="date" [(ngModel)]="statementTo">
21467
- </mat-form-field>
21468
- <mat-form-field>
21469
- <mat-label>Closing Balance</mat-label>
21470
- <input matInput type="number" [(ngModel)]="closingBalance">
21471
- </mat-form-field>
21472
- </div>
21473
- <mat-form-field style="width: 100%;">
21474
- <mat-label>Statement lines (CSV: date, description, reference, amount — money out negative)</mat-label>
21475
- <textarea matInput rows="10" [(ngModel)]="csv" placeholder="2026-07-01, Deposit ABC, REF001, 1500.00&#10;2026-07-03, Bank charges, , -25.50"></textarea>
21476
- </mat-form-field>
21477
- <p *ngIf="parseError" style="color: red;"><em>{{ parseError }}</em></p>
21478
- <p *ngIf="parsedLines.length"><em>{{ parsedLines.length }} line(s) parsed, net movement {{ netMovement | number:'1.2-2' }}</em></p>
21479
- </mat-dialog-content>
21480
- <mat-dialog-actions align="end">
21481
- <button mat-button (click)="dialogRef.close(false)">Cancel</button>
21482
- <button mat-flat-button color="primary" (click)="save()" [disabled]="!canSave">Import</button>
21483
- </mat-dialog-actions>
21450
+ template: `
21451
+ <h2 mat-dialog-title>Import Bank Statement</h2>
21452
+ <mat-dialog-content>
21453
+ <div style="display: flex; gap: 16px; flex-wrap: wrap;">
21454
+ <mat-form-field style="min-width: 220px;">
21455
+ <mat-label>Account</mat-label>
21456
+ <mat-select [(ngModel)]="accountID">
21457
+ <mat-option *ngFor="let option of accounts" [value]="option.value">{{ option.name }}</mat-option>
21458
+ </mat-select>
21459
+ </mat-form-field>
21460
+ <mat-form-field>
21461
+ <mat-label>From</mat-label>
21462
+ <input matInput type="date" [(ngModel)]="statementFrom">
21463
+ </mat-form-field>
21464
+ <mat-form-field>
21465
+ <mat-label>To</mat-label>
21466
+ <input matInput type="date" [(ngModel)]="statementTo">
21467
+ </mat-form-field>
21468
+ <mat-form-field>
21469
+ <mat-label>Closing Balance</mat-label>
21470
+ <input matInput type="number" [(ngModel)]="closingBalance">
21471
+ </mat-form-field>
21472
+ </div>
21473
+ <mat-form-field style="width: 100%;">
21474
+ <mat-label>Statement lines (CSV: date, description, reference, amount — money out negative)</mat-label>
21475
+ <textarea matInput rows="10" [(ngModel)]="csv" placeholder="2026-07-01, Deposit ABC, REF001, 1500.00&#10;2026-07-03, Bank charges, , -25.50"></textarea>
21476
+ </mat-form-field>
21477
+ <p *ngIf="parseError" style="color: red;"><em>{{ parseError }}</em></p>
21478
+ <p *ngIf="parsedLines.length"><em>{{ parsedLines.length }} line(s) parsed, net movement {{ netMovement | number:'1.2-2' }}</em></p>
21479
+ </mat-dialog-content>
21480
+ <mat-dialog-actions align="end">
21481
+ <button mat-button (click)="dialogRef.close(false)">Cancel</button>
21482
+ <button mat-flat-button color="primary" (click)="save()" [disabled]="!canSave">Import</button>
21483
+ </mat-dialog-actions>
21484
21484
  `,
21485
21485
  standalone: false
21486
21486
  }]
@@ -21528,132 +21528,132 @@ class ReconcileDialogComponent {
21528
21528
  });
21529
21529
  }
21530
21530
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ReconcileDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
21531
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ReconcileDialogComponent, isStandalone: false, selector: "spa-reconcile-dialog", ngImport: i0, template: `
21532
- <h2 mat-dialog-title>Reconcile: {{ rec?.name }}</h2>
21533
- <mat-dialog-content>
21534
- <div *ngIf="rec" style="display: flex; gap: 32px; flex-wrap: wrap; margin-bottom: 12px;">
21535
- <div><small>Book Balance</small><br><b>{{ rec.bookBalance | number:'1.2-2' }}</b></div>
21536
- <div><small>Statement Balance</small><br><b>{{ rec.statementBalance | number:'1.2-2' }}</b></div>
21537
- <div><small>Difference</small><br><b [style.color]="difference ? 'red' : 'green'">{{ difference | number:'1.2-2' }}</b></div>
21538
- <div><small>Matched</small><br><b>{{ rec.matchedCount }} / {{ rec.lineCount }}</b></div>
21539
- </div>
21540
-
21541
- <div style="display: flex; gap: 24px; align-items: flex-start; flex-wrap: wrap;" *ngIf="rec">
21542
- <div style="flex: 1; min-width: 380px;">
21543
- <h4>Statement Lines</h4>
21544
- <table style="width: 100%; border-collapse: collapse;">
21545
- <tr style="text-align: left; border-bottom: 1px solid #ccc;">
21546
- <th style="padding: 4px;"></th><th style="padding: 4px;">Date</th><th style="padding: 4px;">Description</th>
21547
- <th style="padding: 4px; text-align: right;">Amount</th><th style="padding: 4px;">Status</th>
21548
- </tr>
21549
- <tr *ngFor="let line of rec.lines" style="border-bottom: 1px solid #eee;"
21550
- [style.background]="selectedLine === line ? '#E3F2FD' : line.isMatched ? '#F1F8E9' : ''">
21551
- <td style="padding: 4px;">
21552
- <mat-checkbox *ngIf="!line.isMatched" [checked]="selectedLine === line" (change)="selectedLine = $event.checked ? line : null"></mat-checkbox>
21553
- </td>
21554
- <td style="padding: 4px;">{{ line.date }}</td>
21555
- <td style="padding: 4px;">{{ line.description }}</td>
21556
- <td style="padding: 4px; text-align: right;">{{ line.amount | number:'1.2-2' }}</td>
21557
- <td style="padding: 4px;">
21558
- <span *ngIf="line.isMatched" title="{{ line.matchedDescription }}">✔</span>
21559
- <button mat-button *ngIf="line.isMatched" (click)="unmatch(line)">Unmatch</button>
21560
- </td>
21561
- </tr>
21562
- </table>
21563
- </div>
21564
-
21565
- <div style="flex: 1; min-width: 380px;">
21566
- <h4>Unreconciled Book Entries</h4>
21567
- <table style="width: 100%; border-collapse: collapse;">
21568
- <tr style="text-align: left; border-bottom: 1px solid #ccc;">
21569
- <th style="padding: 4px;"></th><th style="padding: 4px;">Date</th><th style="padding: 4px;">Description</th>
21570
- <th style="padding: 4px; text-align: right;">Amount</th>
21571
- </tr>
21572
- <tr *ngFor="let entry of rec.unreconciledEntries" style="border-bottom: 1px solid #eee;"
21573
- [style.background]="selectedEntry === entry ? '#E3F2FD' : ''">
21574
- <td style="padding: 4px;">
21575
- <mat-checkbox [checked]="selectedEntry === entry" (change)="selectedEntry = $event.checked ? entry : null"></mat-checkbox>
21576
- </td>
21577
- <td style="padding: 4px;">{{ entry.date }}</td>
21578
- <td style="padding: 4px;">{{ entry.description }}</td>
21579
- <td style="padding: 4px; text-align: right;">{{ entry.amount | number:'1.2-2' }}</td>
21580
- </tr>
21581
- </table>
21582
- </div>
21583
- </div>
21584
- </mat-dialog-content>
21585
- <mat-dialog-actions align="end">
21586
- <button mat-button (click)="dialogRef.close(changed)">Close</button>
21587
- <button mat-stroked-button (click)="autoMatch()">Auto Match</button>
21588
- <button mat-stroked-button (click)="match()" [disabled]="!selectedLine || !selectedEntry">Match Selected</button>
21589
- <button mat-flat-button color="primary" (click)="complete()" [disabled]="!rec || rec.matchedCount != rec.lineCount">Complete Reconciliation</button>
21590
- </mat-dialog-actions>
21531
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: ReconcileDialogComponent, isStandalone: false, selector: "spa-reconcile-dialog", ngImport: i0, template: `
21532
+ <h2 mat-dialog-title>Reconcile: {{ rec?.name }}</h2>
21533
+ <mat-dialog-content>
21534
+ <div *ngIf="rec" style="display: flex; gap: 32px; flex-wrap: wrap; margin-bottom: 12px;">
21535
+ <div><small>Book Balance</small><br><b>{{ rec.bookBalance | number:'1.2-2' }}</b></div>
21536
+ <div><small>Statement Balance</small><br><b>{{ rec.statementBalance | number:'1.2-2' }}</b></div>
21537
+ <div><small>Difference</small><br><b [style.color]="difference ? 'red' : 'green'">{{ difference | number:'1.2-2' }}</b></div>
21538
+ <div><small>Matched</small><br><b>{{ rec.matchedCount }} / {{ rec.lineCount }}</b></div>
21539
+ </div>
21540
+
21541
+ <div style="display: flex; gap: 24px; align-items: flex-start; flex-wrap: wrap;" *ngIf="rec">
21542
+ <div style="flex: 1; min-width: 380px;">
21543
+ <h4>Statement Lines</h4>
21544
+ <table style="width: 100%; border-collapse: collapse;">
21545
+ <tr style="text-align: left; border-bottom: 1px solid #ccc;">
21546
+ <th style="padding: 4px;"></th><th style="padding: 4px;">Date</th><th style="padding: 4px;">Description</th>
21547
+ <th style="padding: 4px; text-align: right;">Amount</th><th style="padding: 4px;">Status</th>
21548
+ </tr>
21549
+ <tr *ngFor="let line of rec.lines" style="border-bottom: 1px solid #eee;"
21550
+ [style.background]="selectedLine === line ? '#E3F2FD' : line.isMatched ? '#F1F8E9' : ''">
21551
+ <td style="padding: 4px;">
21552
+ <mat-checkbox *ngIf="!line.isMatched" [checked]="selectedLine === line" (change)="selectedLine = $event.checked ? line : null"></mat-checkbox>
21553
+ </td>
21554
+ <td style="padding: 4px;">{{ line.date }}</td>
21555
+ <td style="padding: 4px;">{{ line.description }}</td>
21556
+ <td style="padding: 4px; text-align: right;">{{ line.amount | number:'1.2-2' }}</td>
21557
+ <td style="padding: 4px;">
21558
+ <span *ngIf="line.isMatched" title="{{ line.matchedDescription }}">✔</span>
21559
+ <button mat-button *ngIf="line.isMatched" (click)="unmatch(line)">Unmatch</button>
21560
+ </td>
21561
+ </tr>
21562
+ </table>
21563
+ </div>
21564
+
21565
+ <div style="flex: 1; min-width: 380px;">
21566
+ <h4>Unreconciled Book Entries</h4>
21567
+ <table style="width: 100%; border-collapse: collapse;">
21568
+ <tr style="text-align: left; border-bottom: 1px solid #ccc;">
21569
+ <th style="padding: 4px;"></th><th style="padding: 4px;">Date</th><th style="padding: 4px;">Description</th>
21570
+ <th style="padding: 4px; text-align: right;">Amount</th>
21571
+ </tr>
21572
+ <tr *ngFor="let entry of rec.unreconciledEntries" style="border-bottom: 1px solid #eee;"
21573
+ [style.background]="selectedEntry === entry ? '#E3F2FD' : ''">
21574
+ <td style="padding: 4px;">
21575
+ <mat-checkbox [checked]="selectedEntry === entry" (change)="selectedEntry = $event.checked ? entry : null"></mat-checkbox>
21576
+ </td>
21577
+ <td style="padding: 4px;">{{ entry.date }}</td>
21578
+ <td style="padding: 4px;">{{ entry.description }}</td>
21579
+ <td style="padding: 4px; text-align: right;">{{ entry.amount | number:'1.2-2' }}</td>
21580
+ </tr>
21581
+ </table>
21582
+ </div>
21583
+ </div>
21584
+ </mat-dialog-content>
21585
+ <mat-dialog-actions align="end">
21586
+ <button mat-button (click)="dialogRef.close(changed)">Close</button>
21587
+ <button mat-stroked-button (click)="autoMatch()">Auto Match</button>
21588
+ <button mat-stroked-button (click)="match()" [disabled]="!selectedLine || !selectedEntry">Match Selected</button>
21589
+ <button mat-flat-button color="primary" (click)="complete()" [disabled]="!rec || rec.matchedCount != rec.lineCount">Complete Reconciliation</button>
21590
+ </mat-dialog-actions>
21591
21591
  `, isInline: true, dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "component", type: i8.MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: i4.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i4.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i4.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "pipe", type: i1.DecimalPipe, name: "number" }] }); }
21592
21592
  }
21593
21593
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: ReconcileDialogComponent, decorators: [{
21594
21594
  type: Component,
21595
21595
  args: [{
21596
21596
  selector: 'spa-reconcile-dialog',
21597
- template: `
21598
- <h2 mat-dialog-title>Reconcile: {{ rec?.name }}</h2>
21599
- <mat-dialog-content>
21600
- <div *ngIf="rec" style="display: flex; gap: 32px; flex-wrap: wrap; margin-bottom: 12px;">
21601
- <div><small>Book Balance</small><br><b>{{ rec.bookBalance | number:'1.2-2' }}</b></div>
21602
- <div><small>Statement Balance</small><br><b>{{ rec.statementBalance | number:'1.2-2' }}</b></div>
21603
- <div><small>Difference</small><br><b [style.color]="difference ? 'red' : 'green'">{{ difference | number:'1.2-2' }}</b></div>
21604
- <div><small>Matched</small><br><b>{{ rec.matchedCount }} / {{ rec.lineCount }}</b></div>
21605
- </div>
21606
-
21607
- <div style="display: flex; gap: 24px; align-items: flex-start; flex-wrap: wrap;" *ngIf="rec">
21608
- <div style="flex: 1; min-width: 380px;">
21609
- <h4>Statement Lines</h4>
21610
- <table style="width: 100%; border-collapse: collapse;">
21611
- <tr style="text-align: left; border-bottom: 1px solid #ccc;">
21612
- <th style="padding: 4px;"></th><th style="padding: 4px;">Date</th><th style="padding: 4px;">Description</th>
21613
- <th style="padding: 4px; text-align: right;">Amount</th><th style="padding: 4px;">Status</th>
21614
- </tr>
21615
- <tr *ngFor="let line of rec.lines" style="border-bottom: 1px solid #eee;"
21616
- [style.background]="selectedLine === line ? '#E3F2FD' : line.isMatched ? '#F1F8E9' : ''">
21617
- <td style="padding: 4px;">
21618
- <mat-checkbox *ngIf="!line.isMatched" [checked]="selectedLine === line" (change)="selectedLine = $event.checked ? line : null"></mat-checkbox>
21619
- </td>
21620
- <td style="padding: 4px;">{{ line.date }}</td>
21621
- <td style="padding: 4px;">{{ line.description }}</td>
21622
- <td style="padding: 4px; text-align: right;">{{ line.amount | number:'1.2-2' }}</td>
21623
- <td style="padding: 4px;">
21624
- <span *ngIf="line.isMatched" title="{{ line.matchedDescription }}">✔</span>
21625
- <button mat-button *ngIf="line.isMatched" (click)="unmatch(line)">Unmatch</button>
21626
- </td>
21627
- </tr>
21628
- </table>
21629
- </div>
21630
-
21631
- <div style="flex: 1; min-width: 380px;">
21632
- <h4>Unreconciled Book Entries</h4>
21633
- <table style="width: 100%; border-collapse: collapse;">
21634
- <tr style="text-align: left; border-bottom: 1px solid #ccc;">
21635
- <th style="padding: 4px;"></th><th style="padding: 4px;">Date</th><th style="padding: 4px;">Description</th>
21636
- <th style="padding: 4px; text-align: right;">Amount</th>
21637
- </tr>
21638
- <tr *ngFor="let entry of rec.unreconciledEntries" style="border-bottom: 1px solid #eee;"
21639
- [style.background]="selectedEntry === entry ? '#E3F2FD' : ''">
21640
- <td style="padding: 4px;">
21641
- <mat-checkbox [checked]="selectedEntry === entry" (change)="selectedEntry = $event.checked ? entry : null"></mat-checkbox>
21642
- </td>
21643
- <td style="padding: 4px;">{{ entry.date }}</td>
21644
- <td style="padding: 4px;">{{ entry.description }}</td>
21645
- <td style="padding: 4px; text-align: right;">{{ entry.amount | number:'1.2-2' }}</td>
21646
- </tr>
21647
- </table>
21648
- </div>
21649
- </div>
21650
- </mat-dialog-content>
21651
- <mat-dialog-actions align="end">
21652
- <button mat-button (click)="dialogRef.close(changed)">Close</button>
21653
- <button mat-stroked-button (click)="autoMatch()">Auto Match</button>
21654
- <button mat-stroked-button (click)="match()" [disabled]="!selectedLine || !selectedEntry">Match Selected</button>
21655
- <button mat-flat-button color="primary" (click)="complete()" [disabled]="!rec || rec.matchedCount != rec.lineCount">Complete Reconciliation</button>
21656
- </mat-dialog-actions>
21597
+ template: `
21598
+ <h2 mat-dialog-title>Reconcile: {{ rec?.name }}</h2>
21599
+ <mat-dialog-content>
21600
+ <div *ngIf="rec" style="display: flex; gap: 32px; flex-wrap: wrap; margin-bottom: 12px;">
21601
+ <div><small>Book Balance</small><br><b>{{ rec.bookBalance | number:'1.2-2' }}</b></div>
21602
+ <div><small>Statement Balance</small><br><b>{{ rec.statementBalance | number:'1.2-2' }}</b></div>
21603
+ <div><small>Difference</small><br><b [style.color]="difference ? 'red' : 'green'">{{ difference | number:'1.2-2' }}</b></div>
21604
+ <div><small>Matched</small><br><b>{{ rec.matchedCount }} / {{ rec.lineCount }}</b></div>
21605
+ </div>
21606
+
21607
+ <div style="display: flex; gap: 24px; align-items: flex-start; flex-wrap: wrap;" *ngIf="rec">
21608
+ <div style="flex: 1; min-width: 380px;">
21609
+ <h4>Statement Lines</h4>
21610
+ <table style="width: 100%; border-collapse: collapse;">
21611
+ <tr style="text-align: left; border-bottom: 1px solid #ccc;">
21612
+ <th style="padding: 4px;"></th><th style="padding: 4px;">Date</th><th style="padding: 4px;">Description</th>
21613
+ <th style="padding: 4px; text-align: right;">Amount</th><th style="padding: 4px;">Status</th>
21614
+ </tr>
21615
+ <tr *ngFor="let line of rec.lines" style="border-bottom: 1px solid #eee;"
21616
+ [style.background]="selectedLine === line ? '#E3F2FD' : line.isMatched ? '#F1F8E9' : ''">
21617
+ <td style="padding: 4px;">
21618
+ <mat-checkbox *ngIf="!line.isMatched" [checked]="selectedLine === line" (change)="selectedLine = $event.checked ? line : null"></mat-checkbox>
21619
+ </td>
21620
+ <td style="padding: 4px;">{{ line.date }}</td>
21621
+ <td style="padding: 4px;">{{ line.description }}</td>
21622
+ <td style="padding: 4px; text-align: right;">{{ line.amount | number:'1.2-2' }}</td>
21623
+ <td style="padding: 4px;">
21624
+ <span *ngIf="line.isMatched" title="{{ line.matchedDescription }}">✔</span>
21625
+ <button mat-button *ngIf="line.isMatched" (click)="unmatch(line)">Unmatch</button>
21626
+ </td>
21627
+ </tr>
21628
+ </table>
21629
+ </div>
21630
+
21631
+ <div style="flex: 1; min-width: 380px;">
21632
+ <h4>Unreconciled Book Entries</h4>
21633
+ <table style="width: 100%; border-collapse: collapse;">
21634
+ <tr style="text-align: left; border-bottom: 1px solid #ccc;">
21635
+ <th style="padding: 4px;"></th><th style="padding: 4px;">Date</th><th style="padding: 4px;">Description</th>
21636
+ <th style="padding: 4px; text-align: right;">Amount</th>
21637
+ </tr>
21638
+ <tr *ngFor="let entry of rec.unreconciledEntries" style="border-bottom: 1px solid #eee;"
21639
+ [style.background]="selectedEntry === entry ? '#E3F2FD' : ''">
21640
+ <td style="padding: 4px;">
21641
+ <mat-checkbox [checked]="selectedEntry === entry" (change)="selectedEntry = $event.checked ? entry : null"></mat-checkbox>
21642
+ </td>
21643
+ <td style="padding: 4px;">{{ entry.date }}</td>
21644
+ <td style="padding: 4px;">{{ entry.description }}</td>
21645
+ <td style="padding: 4px; text-align: right;">{{ entry.amount | number:'1.2-2' }}</td>
21646
+ </tr>
21647
+ </table>
21648
+ </div>
21649
+ </div>
21650
+ </mat-dialog-content>
21651
+ <mat-dialog-actions align="end">
21652
+ <button mat-button (click)="dialogRef.close(changed)">Close</button>
21653
+ <button mat-stroked-button (click)="autoMatch()">Auto Match</button>
21654
+ <button mat-stroked-button (click)="match()" [disabled]="!selectedLine || !selectedEntry">Match Selected</button>
21655
+ <button mat-flat-button color="primary" (click)="complete()" [disabled]="!rec || rec.matchedCount != rec.lineCount">Complete Reconciliation</button>
21656
+ </mat-dialog-actions>
21657
21657
  `,
21658
21658
  standalone: false
21659
21659
  }]
@@ -21681,26 +21681,26 @@ class BankReconciliationComponent {
21681
21681
  this.page?.tableReload.next(true); });
21682
21682
  }
21683
21683
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: BankReconciliationComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
21684
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: BankReconciliationComponent, isStandalone: false, selector: "spa-bank-reconciliation", viewQueries: [{ propertyName: "page", first: true, predicate: PageComponent, descendants: true }], ngImport: i0, template: `
21685
- <div style="display: flex; justify-content: flex-end;">
21686
- <button mat-stroked-button (click)="newImport()">
21687
- <mat-icon>upload_file</mat-icon> Import Statement
21688
- </button>
21689
- </div>
21690
- <spa-page [config]="pageConfig"></spa-page>
21684
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: BankReconciliationComponent, isStandalone: false, selector: "spa-bank-reconciliation", viewQueries: [{ propertyName: "page", first: true, predicate: PageComponent, descendants: true }], ngImport: i0, template: `
21685
+ <div style="display: flex; justify-content: flex-end;">
21686
+ <button mat-stroked-button (click)="newImport()">
21687
+ <mat-icon>upload_file</mat-icon> Import Statement
21688
+ </button>
21689
+ </div>
21690
+ <spa-page [config]="pageConfig"></spa-page>
21691
21691
  `, isInline: true, dependencies: [{ kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: PageComponent, selector: "spa-page", inputs: ["config"], outputs: ["searchModeActivated", "searchModeDeactivated", "refreshClick", "actionClick", "actionResponse", "inputChange", "createClick", "searchClick", "dataLoad", "titleActionChange"] }] }); }
21692
21692
  }
21693
21693
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: BankReconciliationComponent, decorators: [{
21694
21694
  type: Component,
21695
21695
  args: [{
21696
21696
  selector: 'spa-bank-reconciliation',
21697
- template: `
21698
- <div style="display: flex; justify-content: flex-end;">
21699
- <button mat-stroked-button (click)="newImport()">
21700
- <mat-icon>upload_file</mat-icon> Import Statement
21701
- </button>
21702
- </div>
21703
- <spa-page [config]="pageConfig"></spa-page>
21697
+ template: `
21698
+ <div style="display: flex; justify-content: flex-end;">
21699
+ <button mat-stroked-button (click)="newImport()">
21700
+ <mat-icon>upload_file</mat-icon> Import Statement
21701
+ </button>
21702
+ </div>
21703
+ <spa-page [config]="pageConfig"></spa-page>
21704
21704
  `,
21705
21705
  standalone: false
21706
21706
  }]
@@ -21735,62 +21735,62 @@ class RevenueScheduleDialogComponent {
21735
21735
  });
21736
21736
  }
21737
21737
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: RevenueScheduleDialogComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
21738
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: RevenueScheduleDialogComponent, isStandalone: false, selector: "spa-revenue-schedule-dialog", ngImport: i0, template: `
21739
- <h2 mat-dialog-title>Defer Invoice Revenue</h2>
21740
- <mat-dialog-content>
21741
- <div style="display: flex; gap: 16px; flex-wrap: wrap;">
21742
- <mat-form-field style="min-width: 280px;">
21743
- <mat-label>Invoice</mat-label>
21744
- <mat-select [(ngModel)]="invoiceID">
21745
- <mat-option *ngFor="let option of invoices" [value]="option.value">{{ option.name }}</mat-option>
21746
- </mat-select>
21747
- </mat-form-field>
21748
- <mat-form-field>
21749
- <mat-label>Service Period Start</mat-label>
21750
- <input matInput type="date" [(ngModel)]="startDate">
21751
- </mat-form-field>
21752
- <mat-form-field>
21753
- <mat-label>Service Period End</mat-label>
21754
- <input matInput type="date" [(ngModel)]="endDate">
21755
- </mat-form-field>
21756
- </div>
21757
- <p><em>The invoice's net revenue (excl. VAT) moves to Deferred Revenue and is recognized monthly, pro-rata by days, as each period ends.</em></p>
21758
- </mat-dialog-content>
21759
- <mat-dialog-actions align="end">
21760
- <button mat-button (click)="dialogRef.close(false)">Cancel</button>
21761
- <button mat-flat-button color="primary" (click)="save()" [disabled]="!invoiceID || !startDate || !endDate || endDate <= startDate">Defer Revenue</button>
21762
- </mat-dialog-actions>
21738
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: RevenueScheduleDialogComponent, isStandalone: false, selector: "spa-revenue-schedule-dialog", ngImport: i0, template: `
21739
+ <h2 mat-dialog-title>Defer Invoice Revenue</h2>
21740
+ <mat-dialog-content>
21741
+ <div style="display: flex; gap: 16px; flex-wrap: wrap;">
21742
+ <mat-form-field style="min-width: 280px;">
21743
+ <mat-label>Invoice</mat-label>
21744
+ <mat-select [(ngModel)]="invoiceID">
21745
+ <mat-option *ngFor="let option of invoices" [value]="option.value">{{ option.name }}</mat-option>
21746
+ </mat-select>
21747
+ </mat-form-field>
21748
+ <mat-form-field>
21749
+ <mat-label>Service Period Start</mat-label>
21750
+ <input matInput type="date" [(ngModel)]="startDate">
21751
+ </mat-form-field>
21752
+ <mat-form-field>
21753
+ <mat-label>Service Period End</mat-label>
21754
+ <input matInput type="date" [(ngModel)]="endDate">
21755
+ </mat-form-field>
21756
+ </div>
21757
+ <p><em>The invoice's net revenue (excl. VAT) moves to Deferred Revenue and is recognized monthly, pro-rata by days, as each period ends.</em></p>
21758
+ </mat-dialog-content>
21759
+ <mat-dialog-actions align="end">
21760
+ <button mat-button (click)="dialogRef.close(false)">Cancel</button>
21761
+ <button mat-flat-button color="primary" (click)="save()" [disabled]="!invoiceID || !startDate || !endDate || endDate <= startDate">Defer Revenue</button>
21762
+ </mat-dialog-actions>
21763
21763
  `, isInline: true, dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i2$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i2$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "directive", type: i4.MatDialogTitle, selector: "[mat-dialog-title], [matDialogTitle]", inputs: ["id"], exportAs: ["matDialogTitle"] }, { kind: "directive", type: i4.MatDialogActions, selector: "[mat-dialog-actions], mat-dialog-actions, [matDialogActions]", inputs: ["align"] }, { kind: "directive", type: i4.MatDialogContent, selector: "[mat-dialog-content], mat-dialog-content, [matDialogContent]" }, { kind: "component", type: i3$1.MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: i3$1.MatLabel, selector: "mat-label" }, { kind: "directive", type: i4$3.MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "component", type: i7$1.MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: i7$1.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }] }); }
21764
21764
  }
21765
21765
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: RevenueScheduleDialogComponent, decorators: [{
21766
21766
  type: Component,
21767
21767
  args: [{
21768
21768
  selector: 'spa-revenue-schedule-dialog',
21769
- template: `
21770
- <h2 mat-dialog-title>Defer Invoice Revenue</h2>
21771
- <mat-dialog-content>
21772
- <div style="display: flex; gap: 16px; flex-wrap: wrap;">
21773
- <mat-form-field style="min-width: 280px;">
21774
- <mat-label>Invoice</mat-label>
21775
- <mat-select [(ngModel)]="invoiceID">
21776
- <mat-option *ngFor="let option of invoices" [value]="option.value">{{ option.name }}</mat-option>
21777
- </mat-select>
21778
- </mat-form-field>
21779
- <mat-form-field>
21780
- <mat-label>Service Period Start</mat-label>
21781
- <input matInput type="date" [(ngModel)]="startDate">
21782
- </mat-form-field>
21783
- <mat-form-field>
21784
- <mat-label>Service Period End</mat-label>
21785
- <input matInput type="date" [(ngModel)]="endDate">
21786
- </mat-form-field>
21787
- </div>
21788
- <p><em>The invoice's net revenue (excl. VAT) moves to Deferred Revenue and is recognized monthly, pro-rata by days, as each period ends.</em></p>
21789
- </mat-dialog-content>
21790
- <mat-dialog-actions align="end">
21791
- <button mat-button (click)="dialogRef.close(false)">Cancel</button>
21792
- <button mat-flat-button color="primary" (click)="save()" [disabled]="!invoiceID || !startDate || !endDate || endDate <= startDate">Defer Revenue</button>
21793
- </mat-dialog-actions>
21769
+ template: `
21770
+ <h2 mat-dialog-title>Defer Invoice Revenue</h2>
21771
+ <mat-dialog-content>
21772
+ <div style="display: flex; gap: 16px; flex-wrap: wrap;">
21773
+ <mat-form-field style="min-width: 280px;">
21774
+ <mat-label>Invoice</mat-label>
21775
+ <mat-select [(ngModel)]="invoiceID">
21776
+ <mat-option *ngFor="let option of invoices" [value]="option.value">{{ option.name }}</mat-option>
21777
+ </mat-select>
21778
+ </mat-form-field>
21779
+ <mat-form-field>
21780
+ <mat-label>Service Period Start</mat-label>
21781
+ <input matInput type="date" [(ngModel)]="startDate">
21782
+ </mat-form-field>
21783
+ <mat-form-field>
21784
+ <mat-label>Service Period End</mat-label>
21785
+ <input matInput type="date" [(ngModel)]="endDate">
21786
+ </mat-form-field>
21787
+ </div>
21788
+ <p><em>The invoice's net revenue (excl. VAT) moves to Deferred Revenue and is recognized monthly, pro-rata by days, as each period ends.</em></p>
21789
+ </mat-dialog-content>
21790
+ <mat-dialog-actions align="end">
21791
+ <button mat-button (click)="dialogRef.close(false)">Cancel</button>
21792
+ <button mat-flat-button color="primary" (click)="save()" [disabled]="!invoiceID || !startDate || !endDate || endDate <= startDate">Defer Revenue</button>
21793
+ </mat-dialog-actions>
21794
21794
  `,
21795
21795
  standalone: false
21796
21796
  }]
@@ -21814,26 +21814,26 @@ class RevenueSchedulesComponent {
21814
21814
  this.page?.tableReload.next(true); });
21815
21815
  }
21816
21816
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: RevenueSchedulesComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
21817
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: RevenueSchedulesComponent, isStandalone: false, selector: "spa-revenue-schedules", viewQueries: [{ propertyName: "page", first: true, predicate: PageComponent, descendants: true }], ngImport: i0, template: `
21818
- <div style="display: flex; justify-content: flex-end;">
21819
- <button mat-stroked-button (click)="newSchedule()">
21820
- <mat-icon>schedule_send</mat-icon> Defer Invoice Revenue
21821
- </button>
21822
- </div>
21823
- <spa-page [config]="pageConfig"></spa-page>
21817
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: RevenueSchedulesComponent, isStandalone: false, selector: "spa-revenue-schedules", viewQueries: [{ propertyName: "page", first: true, predicate: PageComponent, descendants: true }], ngImport: i0, template: `
21818
+ <div style="display: flex; justify-content: flex-end;">
21819
+ <button mat-stroked-button (click)="newSchedule()">
21820
+ <mat-icon>schedule_send</mat-icon> Defer Invoice Revenue
21821
+ </button>
21822
+ </div>
21823
+ <spa-page [config]="pageConfig"></spa-page>
21824
21824
  `, isInline: true, dependencies: [{ kind: "component", type: i3.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: PageComponent, selector: "spa-page", inputs: ["config"], outputs: ["searchModeActivated", "searchModeDeactivated", "refreshClick", "actionClick", "actionResponse", "inputChange", "createClick", "searchClick", "dataLoad", "titleActionChange"] }] }); }
21825
21825
  }
21826
21826
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: RevenueSchedulesComponent, decorators: [{
21827
21827
  type: Component,
21828
21828
  args: [{
21829
21829
  selector: 'spa-revenue-schedules',
21830
- template: `
21831
- <div style="display: flex; justify-content: flex-end;">
21832
- <button mat-stroked-button (click)="newSchedule()">
21833
- <mat-icon>schedule_send</mat-icon> Defer Invoice Revenue
21834
- </button>
21835
- </div>
21836
- <spa-page [config]="pageConfig"></spa-page>
21830
+ template: `
21831
+ <div style="display: flex; justify-content: flex-end;">
21832
+ <button mat-stroked-button (click)="newSchedule()">
21833
+ <mat-icon>schedule_send</mat-icon> Defer Invoice Revenue
21834
+ </button>
21835
+ </div>
21836
+ <spa-page [config]="pageConfig"></spa-page>
21837
21837
  `,
21838
21838
  standalone: false
21839
21839
  }]
@@ -26295,26 +26295,26 @@ class FixedAssetsDashboardComponent {
26295
26295
  };
26296
26296
  }
26297
26297
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: FixedAssetsDashboardComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
26298
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: FixedAssetsDashboardComponent, isStandalone: false, selector: "spa-fixed-assets-dashboard", ngImport: i0, template: `
26299
- <div class="dashboard-container">
26300
- <h4 class="dashboard-title"><mat-icon>precision_manufacturing</mat-icon> Fixed Assets Dashboard</h4>
26301
- <spa-tiles [config]="summaryTiles"></spa-tiles>
26302
- <div style="margin-top: 16px;"></div>
26303
- <spa-tiles [config]="chartTiles"></spa-tiles>
26304
- <spa-charts [config]="chartConfig"></spa-charts>
26305
- </div>
26298
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.14", type: FixedAssetsDashboardComponent, isStandalone: false, selector: "spa-fixed-assets-dashboard", ngImport: i0, template: `
26299
+ <div class="dashboard-container">
26300
+ <h4 class="dashboard-title"><mat-icon>precision_manufacturing</mat-icon> Fixed Assets Dashboard</h4>
26301
+ <spa-tiles [config]="summaryTiles"></spa-tiles>
26302
+ <div style="margin-top: 16px;"></div>
26303
+ <spa-tiles [config]="chartTiles"></spa-tiles>
26304
+ <spa-charts [config]="chartConfig"></spa-charts>
26305
+ </div>
26306
26306
  `, isInline: true, styles: [".dashboard-container{padding:16px}.dashboard-title{display:flex;align-items:center;gap:8px;margin-bottom:16px;color:#333;font-weight:500}\n"], dependencies: [{ kind: "component", type: i4$1.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: TilesComponent, selector: "spa-tiles", inputs: ["config", "lastSearch", "data", "reload"], outputs: ["tileActionSelected", "tileClick", "tileUnClick"] }, { kind: "component", type: ChartsComponent, selector: "spa-charts", inputs: ["config", "data", "reload"] }] }); }
26307
26307
  }
26308
26308
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImport: i0, type: FixedAssetsDashboardComponent, decorators: [{
26309
26309
  type: Component,
26310
- args: [{ selector: 'spa-fixed-assets-dashboard', template: `
26311
- <div class="dashboard-container">
26312
- <h4 class="dashboard-title"><mat-icon>precision_manufacturing</mat-icon> Fixed Assets Dashboard</h4>
26313
- <spa-tiles [config]="summaryTiles"></spa-tiles>
26314
- <div style="margin-top: 16px;"></div>
26315
- <spa-tiles [config]="chartTiles"></spa-tiles>
26316
- <spa-charts [config]="chartConfig"></spa-charts>
26317
- </div>
26310
+ args: [{ selector: 'spa-fixed-assets-dashboard', template: `
26311
+ <div class="dashboard-container">
26312
+ <h4 class="dashboard-title"><mat-icon>precision_manufacturing</mat-icon> Fixed Assets Dashboard</h4>
26313
+ <spa-tiles [config]="summaryTiles"></spa-tiles>
26314
+ <div style="margin-top: 16px;"></div>
26315
+ <spa-tiles [config]="chartTiles"></spa-tiles>
26316
+ <spa-charts [config]="chartConfig"></spa-charts>
26317
+ </div>
26318
26318
  `, standalone: false, styles: [".dashboard-container{padding:16px}.dashboard-title{display:flex;align-items:center;gap:8px;margin-bottom:16px;color:#333;font-weight:500}\n"] }]
26319
26319
  }] });
26320
26320
 
@@ -26919,28 +26919,28 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.14", ngImpo
26919
26919
  // Config interfaces for the spa-landing component
26920
26920
  // Consumer apps provide this config to drive the entire landing page
26921
26921
  // Alsquare brand SVG — dark variant (for navbar on light backgrounds)
26922
- const ALSQUARE_SVG_DARK = `<svg viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
26923
- <path d="M32 62C17.6 62 6 50.4 6 36S17.6 10 32 10c9.2 0 17.3 4.9 21.8 12.2" stroke="#1a3a7a" stroke-width="9" stroke-linecap="round" fill="none"/>
26924
- <circle cx="32" cy="36" r="10" fill="white" stroke="#1a3a7a" stroke-width="7"/>
26925
- <circle cx="32" cy="36" r="5.5" fill="#e8a020"/>
26926
- <line x1="48" y1="22" x2="48" y2="62" stroke="#1a3a7a" stroke-width="9" stroke-linecap="round"/>
26927
- <rect x="56" y="6" width="7" height="56" rx="3.5" fill="#e8a020"/>
26928
- <rect x="67" y="0" width="6" height="6" rx="1" fill="#1a3a7a"/>
26929
- <rect x="67" y="9" width="6" height="6" rx="1" fill="#1a3a7a"/>
26930
- <rect x="75" y="0" width="5" height="5" rx="1" fill="#1a3a7a" opacity="0.7"/>
26931
- <rect x="75" y="8" width="5" height="5" rx="1" fill="#1a3a7a" opacity="0.5"/>
26922
+ const ALSQUARE_SVG_DARK = `<svg viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
26923
+ <path d="M32 62C17.6 62 6 50.4 6 36S17.6 10 32 10c9.2 0 17.3 4.9 21.8 12.2" stroke="#1a3a7a" stroke-width="9" stroke-linecap="round" fill="none"/>
26924
+ <circle cx="32" cy="36" r="10" fill="white" stroke="#1a3a7a" stroke-width="7"/>
26925
+ <circle cx="32" cy="36" r="5.5" fill="#e8a020"/>
26926
+ <line x1="48" y1="22" x2="48" y2="62" stroke="#1a3a7a" stroke-width="9" stroke-linecap="round"/>
26927
+ <rect x="56" y="6" width="7" height="56" rx="3.5" fill="#e8a020"/>
26928
+ <rect x="67" y="0" width="6" height="6" rx="1" fill="#1a3a7a"/>
26929
+ <rect x="67" y="9" width="6" height="6" rx="1" fill="#1a3a7a"/>
26930
+ <rect x="75" y="0" width="5" height="5" rx="1" fill="#1a3a7a" opacity="0.7"/>
26931
+ <rect x="75" y="8" width="5" height="5" rx="1" fill="#1a3a7a" opacity="0.5"/>
26932
26932
  </svg>`;
26933
26933
  // Alsquare brand SVG — white variant (for footer on dark backgrounds)
26934
- const ALSQUARE_SVG_WHITE = `<svg viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
26935
- <path d="M32 62C17.6 62 6 50.4 6 36S17.6 10 32 10c9.2 0 17.3 4.9 21.8 12.2" stroke="#ffffff" stroke-width="9" stroke-linecap="round" fill="none"/>
26936
- <circle cx="32" cy="36" r="10" fill="#0f172a" stroke="#ffffff" stroke-width="7"/>
26937
- <circle cx="32" cy="36" r="5.5" fill="#e8a020"/>
26938
- <line x1="48" y1="22" x2="48" y2="62" stroke="#ffffff" stroke-width="9" stroke-linecap="round"/>
26939
- <rect x="56" y="6" width="7" height="56" rx="3.5" fill="#e8a020"/>
26940
- <rect x="67" y="0" width="6" height="6" rx="1" fill="#ffffff"/>
26941
- <rect x="67" y="9" width="6" height="6" rx="1" fill="#ffffff"/>
26942
- <rect x="75" y="0" width="5" height="5" rx="1" fill="#ffffff" opacity="0.7"/>
26943
- <rect x="75" y="8" width="5" height="5" rx="1" fill="#ffffff" opacity="0.5"/>
26934
+ const ALSQUARE_SVG_WHITE = `<svg viewBox="0 0 80 80" fill="none" xmlns="http://www.w3.org/2000/svg">
26935
+ <path d="M32 62C17.6 62 6 50.4 6 36S17.6 10 32 10c9.2 0 17.3 4.9 21.8 12.2" stroke="#ffffff" stroke-width="9" stroke-linecap="round" fill="none"/>
26936
+ <circle cx="32" cy="36" r="10" fill="#0f172a" stroke="#ffffff" stroke-width="7"/>
26937
+ <circle cx="32" cy="36" r="5.5" fill="#e8a020"/>
26938
+ <line x1="48" y1="22" x2="48" y2="62" stroke="#ffffff" stroke-width="9" stroke-linecap="round"/>
26939
+ <rect x="56" y="6" width="7" height="56" rx="3.5" fill="#e8a020"/>
26940
+ <rect x="67" y="0" width="6" height="6" rx="1" fill="#ffffff"/>
26941
+ <rect x="67" y="9" width="6" height="6" rx="1" fill="#ffffff"/>
26942
+ <rect x="75" y="0" width="5" height="5" rx="1" fill="#ffffff" opacity="0.7"/>
26943
+ <rect x="75" y="8" width="5" height="5" rx="1" fill="#ffffff" opacity="0.5"/>
26944
26944
  </svg>`;
26945
26945
 
26946
26946
  /*