commons-shared-web-ui 0.0.40 → 0.0.42
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.
|
@@ -44,10 +44,10 @@ import { MatSlideToggleModule } from '@angular/material/slide-toggle';
|
|
|
44
44
|
import { MatButtonToggleModule } from '@angular/material/button-toggle';
|
|
45
45
|
import * as i3 from '@angular/common/http';
|
|
46
46
|
import { HttpParams, HttpHeaders } from '@angular/common/http';
|
|
47
|
-
import { Subject, BehaviorSubject, combineLatest, forkJoin, takeUntil as takeUntil$1, of } from 'rxjs';
|
|
47
|
+
import { Subject, BehaviorSubject, combineLatest, merge, forkJoin, takeUntil as takeUntil$1, of } from 'rxjs';
|
|
48
|
+
import { takeUntil, debounceTime, distinctUntilChanged, map, catchError, finalize } from 'rxjs/operators';
|
|
48
49
|
import * as i1$4 from '@angular/router';
|
|
49
50
|
import { RouterModule } from '@angular/router';
|
|
50
|
-
import { takeUntil, debounceTime, distinctUntilChanged, map, finalize, catchError } from 'rxjs/operators';
|
|
51
51
|
import * as i11 from 'ngx-quill';
|
|
52
52
|
import { QuillModule } from 'ngx-quill';
|
|
53
53
|
import * as i1$2 from '@angular/platform-browser';
|
|
@@ -646,6 +646,7 @@ class FormFieldComponent {
|
|
|
646
646
|
value;
|
|
647
647
|
isVisible = true;
|
|
648
648
|
showPassword = false; // password show/hide toggle
|
|
649
|
+
dynamicMinDate = null;
|
|
649
650
|
// ── MULTIPLE dropdown state ───────────────────────────────────────────────
|
|
650
651
|
isMultiDropdownOpen = false;
|
|
651
652
|
isDragOver = false; // file upload drag-over state
|
|
@@ -689,6 +690,12 @@ class FormFieldComponent {
|
|
|
689
690
|
autocompleteInputCtrl = new FormControl('');
|
|
690
691
|
/** Filtered option list shown in the mat-autocomplete panel */
|
|
691
692
|
filteredOptions = [];
|
|
693
|
+
/**
|
|
694
|
+
* Component-local option list for DROPDOWN/RADIO/CHECKBOX/CHIP fields.
|
|
695
|
+
* Using a local copy prevents shared-config mutation when the same field config
|
|
696
|
+
* object is reused across multiple allowMulti repeater instances.
|
|
697
|
+
*/
|
|
698
|
+
localOptionList = [];
|
|
692
699
|
/** Cache of the latest dependency parameters for server-side autocomplete filtering */
|
|
693
700
|
_latestDependencyValues = {};
|
|
694
701
|
// ── GROUP / allowMulti support ──────────────────────────────────────────
|
|
@@ -703,6 +710,8 @@ class FormFieldComponent {
|
|
|
703
710
|
* cached values from bleeding into new instances.
|
|
704
711
|
*
|
|
705
712
|
* Enhanced with isEditing and isSaved flags for the 'multiSave' card UI.
|
|
713
|
+
* Each instance gets its own rowController so cascading dropdowns work
|
|
714
|
+
* independently within each repeater row.
|
|
706
715
|
*/
|
|
707
716
|
instanceList = [];
|
|
708
717
|
_nextInstanceId = 0;
|
|
@@ -728,8 +737,13 @@ class FormFieldComponent {
|
|
|
728
737
|
this.initGroupField();
|
|
729
738
|
return;
|
|
730
739
|
}
|
|
740
|
+
// Seed localOptionList from any static options already in the config
|
|
741
|
+
// (e.g. optionList defined directly in the JSON like Yes/No dropdowns).
|
|
742
|
+
// Dynamic options loaded via API will replace this array via loadDropdownOptions.
|
|
743
|
+
this.localOptionList = [...(this.config.optionConfig?.optionList || [])];
|
|
731
744
|
this.registerControl();
|
|
732
745
|
this.setupVisibility();
|
|
746
|
+
this.setupMinDateField();
|
|
733
747
|
this.setupGeneratedField();
|
|
734
748
|
this.setupFormulaValidation(); // Generic formula-based validation
|
|
735
749
|
this.setupDependencies();
|
|
@@ -782,7 +796,7 @@ class FormFieldComponent {
|
|
|
782
796
|
}
|
|
783
797
|
else {
|
|
784
798
|
// We always start with at least one instance.
|
|
785
|
-
// If multi-save is active, it starts in editing mode and is NOT
|
|
799
|
+
// If multi-save is active, it starts in editing mode and is NOT
|
|
786
800
|
// added to the main form value until the user actually saves it.
|
|
787
801
|
this.addGroupInstance();
|
|
788
802
|
}
|
|
@@ -829,13 +843,28 @@ class FormFieldComponent {
|
|
|
829
843
|
// If initialData exists, treat the instance as already submitted/saved to form
|
|
830
844
|
const isEditing = initialData ? false : isMultiSave;
|
|
831
845
|
const isSaved = initialData ? true : !isMultiSave;
|
|
846
|
+
// Per-row controller: inherits token/labels from global but has isolated field state.
|
|
847
|
+
// This allows cascading dropdowns (dependencies) to work within each repeater row
|
|
848
|
+
// without cross-row data collisions.
|
|
849
|
+
const rowController = new SmartFormController();
|
|
850
|
+
rowController.token = this.controller.token;
|
|
851
|
+
rowController.tokenHeader = this.controller.tokenHeader;
|
|
852
|
+
rowController.labels = this.controller.labels;
|
|
853
|
+
rowController.actionLabels = this.controller.actionLabels;
|
|
854
|
+
// Pre-populate row controller so child fields initialize with the correct values
|
|
855
|
+
if (initialData) {
|
|
856
|
+
Object.entries(initialData).forEach(([key, value]) => {
|
|
857
|
+
rowController.updateField(key, value);
|
|
858
|
+
});
|
|
859
|
+
}
|
|
832
860
|
const instance = {
|
|
833
861
|
id: this._nextInstanceId++,
|
|
834
862
|
fg,
|
|
835
863
|
isEditing,
|
|
836
864
|
isSaved,
|
|
837
865
|
isExpanded: false,
|
|
838
|
-
initialValue: initialData ? { ...initialData } : undefined
|
|
866
|
+
initialValue: initialData ? { ...initialData } : undefined,
|
|
867
|
+
rowController
|
|
839
868
|
};
|
|
840
869
|
if (isSaved) {
|
|
841
870
|
this.groupFormArray.push(fg);
|
|
@@ -918,6 +947,7 @@ class FormFieldComponent {
|
|
|
918
947
|
if (arrayIndex >= 0) {
|
|
919
948
|
this.groupFormArray.removeAt(arrayIndex);
|
|
920
949
|
}
|
|
950
|
+
instance.rowController.destroy();
|
|
921
951
|
this.instanceList = this.instanceList.filter((_, i) => i !== index);
|
|
922
952
|
// Rebuild accordion expanded set after index shift
|
|
923
953
|
if (!isMultiSave) {
|
|
@@ -963,6 +993,9 @@ class FormFieldComponent {
|
|
|
963
993
|
this.destroy$.next();
|
|
964
994
|
this.destroy$.complete();
|
|
965
995
|
if (this.isGroup) {
|
|
996
|
+
if (this.config.sectionConfig?.allowMulti) {
|
|
997
|
+
this.instanceList.forEach(inst => inst.rowController?.destroy());
|
|
998
|
+
}
|
|
966
999
|
if (this.formGroup?.contains(this.groupKey)) {
|
|
967
1000
|
this.formGroup.removeControl(this.groupKey);
|
|
968
1001
|
}
|
|
@@ -1095,16 +1128,85 @@ class FormFieldComponent {
|
|
|
1095
1128
|
setupVisibility() {
|
|
1096
1129
|
if (!this.config.visibilityExpression)
|
|
1097
1130
|
return;
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
const
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1131
|
+
// Prefer formGroup-level lookup: works for both allowMulti rows and non-multi rows
|
|
1132
|
+
// that still have a row-scoped formGroup (e.g. accordion repeater with allowMulti=false).
|
|
1133
|
+
if (this.formGroup) {
|
|
1134
|
+
const variables = this.expressionService.extractVariables(this.config.visibilityExpression);
|
|
1135
|
+
const evaluate = () => {
|
|
1136
|
+
const context = this.formGroup.value;
|
|
1137
|
+
this.isVisible = this.expressionService.evaluateCondition(this.config.visibilityExpression, context);
|
|
1138
|
+
const control = this.formGroup.get(this.config.name);
|
|
1139
|
+
if (control) {
|
|
1140
|
+
this.isVisible ? control.enable({ emitEvent: false }) : control.disable({ emitEvent: false });
|
|
1141
|
+
}
|
|
1142
|
+
};
|
|
1143
|
+
evaluate();
|
|
1144
|
+
const varControls = variables
|
|
1145
|
+
.map(v => this.formGroup.get(v))
|
|
1146
|
+
.filter((c) => !!c);
|
|
1147
|
+
const stream$ = varControls.length > 0
|
|
1148
|
+
? merge(...varControls.map(c => c.valueChanges))
|
|
1149
|
+
: this.formGroup.valueChanges;
|
|
1150
|
+
stream$.pipe(takeUntil(this.destroy$)).subscribe(() => evaluate());
|
|
1151
|
+
}
|
|
1152
|
+
else {
|
|
1153
|
+
const variables = this.expressionService.extractVariables(this.config.visibilityExpression);
|
|
1154
|
+
const observables = variables.map(v => this.controller.getFieldObservable(v));
|
|
1155
|
+
combineLatest(observables).pipe(takeUntil(this.destroy$)).subscribe(() => {
|
|
1156
|
+
const context = this.controller.getAllData();
|
|
1157
|
+
this.isVisible = this.expressionService.evaluateCondition(this.config.visibilityExpression, context);
|
|
1158
|
+
const control = this.formGroup?.get(this.config.name);
|
|
1159
|
+
if (control) {
|
|
1160
|
+
this.isVisible ? control.enable({ emitEvent: false }) : control.disable({ emitEvent: false });
|
|
1161
|
+
}
|
|
1162
|
+
});
|
|
1163
|
+
}
|
|
1164
|
+
}
|
|
1165
|
+
setupMinDateField() {
|
|
1166
|
+
const minDateField = this.config.dateConfig?.minDateField;
|
|
1167
|
+
if (!minDateField || this.config.type !== 'DATE')
|
|
1168
|
+
return;
|
|
1169
|
+
// Use local date to avoid UTC-offset day shift (e.g. IST midnight = prev-day UTC)
|
|
1170
|
+
const toIso = (val) => {
|
|
1171
|
+
if (!val)
|
|
1172
|
+
return null;
|
|
1173
|
+
const d = val instanceof Date ? val : new Date(val);
|
|
1174
|
+
if (isNaN(d.getTime()))
|
|
1175
|
+
return String(val);
|
|
1176
|
+
const y = d.getFullYear();
|
|
1177
|
+
const mo = String(d.getMonth() + 1).padStart(2, '0');
|
|
1178
|
+
const dy = String(d.getDate()).padStart(2, '0');
|
|
1179
|
+
return `${y}-${mo}-${dy}`;
|
|
1180
|
+
};
|
|
1181
|
+
// Prefer formGroup-level lookup: works for both allowMulti rows and non-multi rows
|
|
1182
|
+
// that still have a row-scoped formGroup (e.g. accordion repeater with allowMulti=false).
|
|
1183
|
+
if (this.formGroup) {
|
|
1184
|
+
const sourceCtrl = this.formGroup.get(minDateField);
|
|
1185
|
+
if (sourceCtrl) {
|
|
1186
|
+
this.dynamicMinDate = toIso(sourceCtrl.value);
|
|
1187
|
+
sourceCtrl.valueChanges.pipe(takeUntil(this.destroy$)).subscribe(val => {
|
|
1188
|
+
this.dynamicMinDate = toIso(val);
|
|
1189
|
+
// If end date is now before the new start date, clear it immediately
|
|
1190
|
+
const thisCtrl = this.formGroup?.get(this.config.name);
|
|
1191
|
+
if (thisCtrl?.value && val) {
|
|
1192
|
+
const endD = thisCtrl.value instanceof Date ? thisCtrl.value : new Date(thisCtrl.value);
|
|
1193
|
+
const minD = val instanceof Date ? val : new Date(val);
|
|
1194
|
+
if (!isNaN(endD.getTime()) && !isNaN(minD.getTime()) && endD < minD) {
|
|
1195
|
+
thisCtrl.setValue(null, { emitEvent: true });
|
|
1196
|
+
}
|
|
1197
|
+
}
|
|
1198
|
+
});
|
|
1199
|
+
return;
|
|
1106
1200
|
}
|
|
1107
|
-
}
|
|
1201
|
+
}
|
|
1202
|
+
// Fall back to controller-based lookup (non-repeater flat fields).
|
|
1203
|
+
const updateMin = (context) => {
|
|
1204
|
+
this.dynamicMinDate = toIso(context?.[minDateField]);
|
|
1205
|
+
};
|
|
1206
|
+
updateMin(this.controller.getAllData());
|
|
1207
|
+
this.controller.getFieldObservable(minDateField)
|
|
1208
|
+
.pipe(takeUntil(this.destroy$))
|
|
1209
|
+
.subscribe(() => updateMin(this.controller.getAllData()));
|
|
1108
1210
|
}
|
|
1109
1211
|
setupGeneratedField() {
|
|
1110
1212
|
if (this.config.type !== 'GENERATED' || !this.config.generatedConfig)
|
|
@@ -1138,19 +1240,41 @@ class FormFieldComponent {
|
|
|
1138
1240
|
return;
|
|
1139
1241
|
const dependencies = this.config.optionConfig.dependencies;
|
|
1140
1242
|
const observables = Object.values(dependencies).map(fieldName => this.controller.getFieldObservable(fieldName));
|
|
1243
|
+
// Track previous values so we can detect user-driven parent changes vs. initial load.
|
|
1244
|
+
// BehaviorSubjects always emit the current value on subscribe — we must NOT clear the
|
|
1245
|
+
// child's value on that first emission (it would wipe pre-populated edit-mode data).
|
|
1246
|
+
let prevValues = null;
|
|
1141
1247
|
combineLatest(observables).pipe(takeUntil(this.destroy$)).subscribe(values => {
|
|
1142
1248
|
const dependencyValues = {};
|
|
1143
1249
|
Object.keys(dependencies).forEach((paramKey, index) => {
|
|
1144
1250
|
dependencyValues[paramKey] = values[index];
|
|
1145
1251
|
});
|
|
1146
1252
|
const allPresent = Object.values(dependencyValues).every(v => v !== null && v !== undefined && v !== '');
|
|
1253
|
+
const isFirstEmit = prevValues === null;
|
|
1254
|
+
const parentChanged = !isFirstEmit && values.some((v, i) => v !== prevValues[i]);
|
|
1255
|
+
prevValues = [...values];
|
|
1147
1256
|
if (allPresent) {
|
|
1257
|
+
if (parentChanged) {
|
|
1258
|
+
// Parent value changed by user interaction — reset child selection so the
|
|
1259
|
+
// previously chosen value (from a different parent) isn't shown as selected
|
|
1260
|
+
// against the newly loaded options.
|
|
1261
|
+
const control = this.formGroup?.get(this.config.name);
|
|
1262
|
+
if (control && control.value !== null && control.value !== '') {
|
|
1263
|
+
control.setValue(null, { emitEvent: true });
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1148
1266
|
this._latestDependencyValues = dependencyValues;
|
|
1149
1267
|
this.loadDropdownOptions(dependencyValues);
|
|
1150
1268
|
}
|
|
1151
1269
|
else if (this.config.optionConfig) {
|
|
1152
1270
|
this._latestDependencyValues = {};
|
|
1153
|
-
this.
|
|
1271
|
+
this.localOptionList = [];
|
|
1272
|
+
if (parentChanged) {
|
|
1273
|
+
const control = this.formGroup?.get(this.config.name);
|
|
1274
|
+
if (control && control.value !== null && control.value !== '') {
|
|
1275
|
+
control.setValue(null, { emitEvent: true });
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1154
1278
|
}
|
|
1155
1279
|
});
|
|
1156
1280
|
}
|
|
@@ -1226,7 +1350,7 @@ class FormFieldComponent {
|
|
|
1226
1350
|
}
|
|
1227
1351
|
// labelTemplate: read from autocompleteConfig first
|
|
1228
1352
|
const labelTemplate = acConfig?.labelTemplate;
|
|
1229
|
-
this.
|
|
1353
|
+
this.localOptionList = mergedData.map((item) => {
|
|
1230
1354
|
// Support labelTemplate: '{firstName} {lastName} ({login})'
|
|
1231
1355
|
let label;
|
|
1232
1356
|
if (labelTemplate) {
|
|
@@ -1265,7 +1389,7 @@ class FormFieldComponent {
|
|
|
1265
1389
|
});
|
|
1266
1390
|
// Refresh autocomplete filter list after options arrive from API
|
|
1267
1391
|
if (this.isAutocomplete) {
|
|
1268
|
-
this.filteredOptions = [...
|
|
1392
|
+
this.filteredOptions = [...this.localOptionList];
|
|
1269
1393
|
this._syncAutocompleteDisplayValue();
|
|
1270
1394
|
}
|
|
1271
1395
|
},
|
|
@@ -1285,6 +1409,11 @@ class FormFieldComponent {
|
|
|
1285
1409
|
/** Builds HttpHeaders using the token stored in the SmartFormController (sourced from configJSON)
|
|
1286
1410
|
* merged with any custom headers declared in optionConfig.headers.
|
|
1287
1411
|
*/
|
|
1412
|
+
_fileLabel(key, fallback, vars = {}) {
|
|
1413
|
+
let tpl = this.controller.actionLabels?.[key] ?? fallback;
|
|
1414
|
+
Object.entries(vars).forEach(([k, v]) => { tpl = tpl.replace(`{${k}}`, v); });
|
|
1415
|
+
return tpl;
|
|
1416
|
+
}
|
|
1288
1417
|
getHeaders(customHeaders) {
|
|
1289
1418
|
let headers = new HttpHeaders();
|
|
1290
1419
|
if (this.controller.token) {
|
|
@@ -1433,8 +1562,7 @@ class FormFieldComponent {
|
|
|
1433
1562
|
}
|
|
1434
1563
|
else {
|
|
1435
1564
|
// If query is below min length, revert to existing full list if empty query
|
|
1436
|
-
this.filteredOptions = query.length === 0
|
|
1437
|
-
? [...(this.config.optionConfig?.optionList || [])] : [];
|
|
1565
|
+
this.filteredOptions = query.length === 0 ? [...this.localOptionList] : [];
|
|
1438
1566
|
}
|
|
1439
1567
|
}
|
|
1440
1568
|
else {
|
|
@@ -1443,14 +1571,14 @@ class FormFieldComponent {
|
|
|
1443
1571
|
}
|
|
1444
1572
|
});
|
|
1445
1573
|
// Initialise filtered list with all available options
|
|
1446
|
-
this.filteredOptions = [...
|
|
1574
|
+
this.filteredOptions = [...this.localOptionList];
|
|
1447
1575
|
}
|
|
1448
1576
|
/** Filter options by the user's search text (matches label or code). */
|
|
1449
1577
|
_filterOptions(search) {
|
|
1450
1578
|
const q = search.toLowerCase().trim();
|
|
1451
1579
|
if (!q)
|
|
1452
|
-
return [...
|
|
1453
|
-
return
|
|
1580
|
+
return [...this.localOptionList];
|
|
1581
|
+
return this.localOptionList.filter(opt => opt.label?.toLowerCase().includes(q) ||
|
|
1454
1582
|
String(opt.code).toLowerCase().includes(q));
|
|
1455
1583
|
}
|
|
1456
1584
|
/** Put the human-readable label into the display control based on the stored code. */
|
|
@@ -1460,7 +1588,7 @@ class FormFieldComponent {
|
|
|
1460
1588
|
const code = this.formGroup?.get(this.config.name)?.value;
|
|
1461
1589
|
if (code === null || code === undefined || code === '')
|
|
1462
1590
|
return;
|
|
1463
|
-
const matched =
|
|
1591
|
+
const matched = this.localOptionList.find(o => o.code === code);
|
|
1464
1592
|
if (matched) {
|
|
1465
1593
|
this.autocompleteInputCtrl.setValue(matched.label, { emitEvent: false });
|
|
1466
1594
|
}
|
|
@@ -1496,7 +1624,7 @@ class FormFieldComponent {
|
|
|
1496
1624
|
const span = option.colSpan || option.colspan;
|
|
1497
1625
|
if (span)
|
|
1498
1626
|
return span;
|
|
1499
|
-
const count = this.
|
|
1627
|
+
const count = this.localOptionList.length || 1;
|
|
1500
1628
|
return Math.max(1, Math.floor(12 / count));
|
|
1501
1629
|
}
|
|
1502
1630
|
// ── Rating helpers ───────────────────────────────────────────────────────
|
|
@@ -1615,14 +1743,31 @@ class FormFieldComponent {
|
|
|
1615
1743
|
const isMultiple = cfg?.multiple ?? false;
|
|
1616
1744
|
const incoming = Array.from(files);
|
|
1617
1745
|
for (const file of incoming) {
|
|
1746
|
+
// File type validation
|
|
1747
|
+
if (cfg?.accept) {
|
|
1748
|
+
const accepted = cfg.accept.split(',').map((s) => s.trim().toLowerCase());
|
|
1749
|
+
const fileExt = '.' + file.name.toLowerCase().split('.').pop();
|
|
1750
|
+
const fileMime = (file.type || '').toLowerCase();
|
|
1751
|
+
const isAllowed = accepted.some((pattern) => {
|
|
1752
|
+
if (pattern.startsWith('.'))
|
|
1753
|
+
return fileExt === pattern;
|
|
1754
|
+
if (pattern.endsWith('/*'))
|
|
1755
|
+
return fileMime.startsWith(pattern.slice(0, -1));
|
|
1756
|
+
return fileMime === pattern;
|
|
1757
|
+
});
|
|
1758
|
+
if (!isAllowed) {
|
|
1759
|
+
this.fileUploadError = this._fileLabel('fileTypeError', '"{fileName}" is not a supported file type.', { fileName: file.name });
|
|
1760
|
+
continue;
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1618
1763
|
// Size validation
|
|
1619
1764
|
if (file.size > maxSizeBytes) {
|
|
1620
|
-
this.fileUploadError =
|
|
1765
|
+
this.fileUploadError = this._fileLabel('fileSizeError', '"{fileName}" exceeds the maximum allowed size of {maxSizeMB} MB.', { fileName: file.name, maxSizeMB: String(cfg?.maxSizeMB ?? 10) });
|
|
1621
1766
|
continue;
|
|
1622
1767
|
}
|
|
1623
1768
|
// Max-files validation
|
|
1624
1769
|
if (isMultiple && currentFiles.length >= maxFiles) {
|
|
1625
|
-
this.fileUploadError =
|
|
1770
|
+
this.fileUploadError = this._fileLabel('maxFilesError', 'Maximum {maxFiles} files allowed.', { maxFiles: String(maxFiles) });
|
|
1626
1771
|
break;
|
|
1627
1772
|
}
|
|
1628
1773
|
if (cfg?.uploadUrl) {
|
|
@@ -1668,7 +1813,7 @@ class FormFieldComponent {
|
|
|
1668
1813
|
const current = this.value || [];
|
|
1669
1814
|
const cleaned = current.filter(f => f !== entry);
|
|
1670
1815
|
this.updateValue(cleaned.length ? cleaned : null);
|
|
1671
|
-
this.fileUploadError =
|
|
1816
|
+
this.fileUploadError = this._fileLabel('fileUploadFailed', 'Failed to upload "{fileName}". Please try again.', { fileName: file.name });
|
|
1672
1817
|
this.controller.fileUploadFinished$.next(file);
|
|
1673
1818
|
}
|
|
1674
1819
|
});
|
|
@@ -1724,7 +1869,7 @@ class FormFieldComponent {
|
|
|
1724
1869
|
error: (err) => {
|
|
1725
1870
|
console.error('Failed to delete attachment', err);
|
|
1726
1871
|
fileToRemove.isUploading = false;
|
|
1727
|
-
this.fileUploadError =
|
|
1872
|
+
this.fileUploadError = this._fileLabel('fileDeleteFailed', 'Failed to delete "{fileName}". Please try again.', { fileName: fileToRemove.name });
|
|
1728
1873
|
this.updateValue([...current]);
|
|
1729
1874
|
}
|
|
1730
1875
|
});
|
|
@@ -2384,11 +2529,11 @@ class FormFieldComponent {
|
|
|
2384
2529
|
}
|
|
2385
2530
|
}
|
|
2386
2531
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FormFieldComponent, deps: [{ token: i1$3.FormBuilder }, { token: ExpressionService }, { token: i3.HttpClient }], target: i0.ɵɵFactoryTarget.Component });
|
|
2387
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: FormFieldComponent, isStandalone: false, selector: "lib-form-field", inputs: { config: "config", controller: "controller", formGroup: "formGroup", allowMulti: "allowMulti" }, host: { listeners: { "document:click": "onDocumentClick()", "document:keydown.escape": "onEscapeKey()" } }, viewQueries: [{ propertyName: "mediaDeviceInput", first: true, predicate: ["mediaDeviceInput"], descendants: true }, { propertyName: "libraryModalRef", first: true, predicate: ["libraryModal"], descendants: true }], ngImport: i0, template: "<div class=\"form-field mb-16px\" *ngIf=\"isVisible\" [class.has-error]=\"errorMessage\">\n\n <!-- \u2550\u2550 ROW Layout \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isRow\" class=\"form-row grid-row\">\n <ng-container *ngFor=\"let child of config.children\">\n <div class=\"row-field\" [style.gridColumn]=\"'span ' + getChildColSpan(child)\" *ngIf=\"child.isEnabled !== false\">\n <lib-form-field [config]=\"child\" [controller]=\"controller\" [formGroup]=\"formGroup\" [allowMulti]=\"allowMulti\">\n </lib-form-field>\n </div>\n </ng-container>\n </div>\n\n <!-- \u2550\u2550 GROUP \u2014 allowMulti (repeater) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isGroup && config.sectionConfig?.allowMulti\"\n class=\"group-section-wrapper mb-20px\"\n [class.multi-save-active]=\"config.sectionConfig?.multiSaveConfig?.active\">\n\n <!-- Multi-Save: header row with label + top-right Add button -->\n <div class=\"multi-save-header d-flex justify-content-between align-items-center mb-24\"\n *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\n <h3 class=\"group-label\" *ngIf=\"config.sectionConfig?.label\">{{ config.sectionConfig!.label }}</h3>\n <lib-button [variant]=\"'outline'\" [icon]=\"{type: 'material', value: 'add'}\" (click)=\"addGroupInstance()\"\n class=\"btn-add-multi\">\n {{ addMultiLabel }}\n </lib-button>\n </div>\n\n <!-- Standard heading (non-multiSave) -->\n <h3 class=\"group-label\"\n *ngIf=\"config.sectionConfig?.label && !config.sectionConfig?.multiSaveConfig?.active\">{{\n config.sectionConfig!.label }}</h3>\n\n <!-- \u2500\u2500 Standard (non-multiSave) repeater: accordion instances \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <ng-container *ngIf=\"!config.sectionConfig?.multiSaveConfig?.active\">\n <div *ngFor=\"let instance of instanceList; trackBy: trackByInstanceId; let i = index\"\n class=\"group-accordion-instance\"\n [class.is-expanded]=\"isGroupExpanded(i)\">\n\n <!-- Accordion header -->\n <div class=\"group-accordion-header\" (click)=\"toggleGroupAccordion(i)\"\n role=\"button\" [attr.aria-expanded]=\"isGroupExpanded(i)\">\n <div class=\"accordion-header-left d-flex align-items-center gap-10\">\n <span class=\"instance-badge\">{{ i + 1 }}</span>\n <span class=\"instance-title\">{{ config.sectionConfig!.label }} #{{ i + 1 }}</span>\n </div>\n <div class=\"accordion-header-right d-flex align-items-center gap-6\">\n <button type=\"button\" class=\"accordion-remove-btn\"\n *ngIf=\"instanceList.length > 1\"\n (click)=\"$event.stopPropagation(); removeGroupInstance(i)\"\n aria-label=\"Remove\">\n <mat-icon>delete_outline</mat-icon>\n </button>\n <mat-icon class=\"accordion-chevron\">\n {{ isGroupExpanded(i) ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}\n </mat-icon>\n </div>\n </div>\n\n <!-- Accordion body (always mounted so form controls survive collapse) -->\n <div class=\"group-accordion-body\" [hidden]=\"!isGroupExpanded(i)\">\n <div class=\"group-fields sf-grid\">\n <ng-container *ngFor=\"let field of config.sectionConfig!.children\">\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\"\n *ngIf=\"field.isEnabled !== false\">\n <lib-form-field [config]=\"field\" [controller]=\"controller\" [formGroup]=\"instance.fg\"\n [allowMulti]=\"true\">\n </lib-form-field>\n </div>\n </ng-container>\n </div>\n </div>\n </div>\n\n <!-- Full-width dashed Add button -->\n <button type=\"button\" class=\"btn-add-group\" (click)=\"addGroupInstance()\">\n <mat-icon>add</mat-icon> {{ addLabel }} {{ config.sectionConfig!.label }}\n </button>\n </ng-container>\n\n <!-- \u2500\u2500 MultiSave: card instances \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <ng-container *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\n <div *ngFor=\"let instance of instanceList; trackBy: trackByInstanceId; let i = index\"\n class=\"group-instance position-relative mb-16 overflow-hidden\"\n [class.is-editing]=\"instance.isEditing\"\n [class.is-card]=\"!instance.isEditing\">\n\n <!-- Edit / new form view -->\n <div [hidden]=\"!instance.isEditing\">\n <div class=\"group-fields sf-grid\">\n <ng-container *ngFor=\"let field of config.sectionConfig!.children\">\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\"\n *ngIf=\"field.isEnabled !== false\">\n <lib-form-field [config]=\"field\" [controller]=\"controller\" [formGroup]=\"instance.fg\"\n [allowMulti]=\"true\">\n </lib-form-field>\n </div>\n </ng-container>\n </div>\n\n <!-- Save / Cancel -->\n <div class=\"group-footer d-flex justify-content-end align-items-center gap-16 p-0-24\"\n *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"multiSaveError\">{{ multiSaveError }}</span>\n <div class=\"footer-actions d-flex gap-12\">\n <lib-button [variant]=\"'outline'\" (click)=\"cancelGroupInstance(i)\">Cancel</lib-button>\n <lib-button [variant]=\"'primary'\" (click)=\"saveGroupInstance(i)\">Save</lib-button>\n </div>\n </div>\n </div>\n\n <!-- Card view (saved state) -->\n <ng-container *ngIf=\"!instance.isEditing\">\n <div class=\"card-view d-flex justify-content-between align-items-center p-18px-24px\"\n [class.is-expanded]=\"instance.isExpanded\">\n <div class=\"card-content flex-1 d-flex flex-column gap-4 overflow-hidden\">\n <span class=\"card-title font-weight-600 overflow-hidden fs-1rem c-111827\">{{\n instance.fg.get(config.sectionConfig!.multiSaveConfig!.summaryField || '')?.value\n || '\u2014' }}</span>\n </div>\n <div class=\"card-actions d-flex align-items-center gap-16 ml-20\">\n <mat-icon class=\"icon-delete\" (click)=\"removeGroupInstance(i, true)\">delete_outline</mat-icon>\n <mat-icon class=\"icon-edit\" (click)=\"editGroupInstance(i)\">edit_outline</mat-icon>\n <mat-icon class=\"icon-expand\" (click)=\"toggleExpandGroupInstance(i)\">\n {{ instance.isExpanded ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}\n </mat-icon>\n </div>\n </div>\n </ng-container>\n </div>\n </ng-container>\n </div>\n\n <!-- \u2550\u2550 GROUP \u2014 single (non-repeater) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isGroup && config.sectionConfig && !config.sectionConfig.allowMulti\"\n class=\"group-section-wrapper mb-20px\">\n <h3 class=\"group-label\" *ngIf=\"config.sectionConfig.label\">{{ config.sectionConfig.label }}</h3>\n <div class=\"group-fields sf-grid\">\n <ng-container *ngFor=\"let field of config.sectionConfig.children\">\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\" *ngIf=\"field.isEnabled !== false\">\n <lib-form-field [config]=\"field\" [controller]=\"controller\" [formGroup]=\"groupFormGroup\" [allowMulti]=\"false\">\n </lib-form-field>\n </div>\n </ng-container>\n </div>\n </div>\n\n\n <!-- \u2550\u2550 Text Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isTextField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <textarea *ngIf=\"config.subType === 'LONG'\" class=\"field-input textarea\" [placeholder]=\"config.placeholder || ''\"\n [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\" rows=\"4\">\n </textarea>\n\n <!-- Password input with show/hide toggle -->\n <div *ngIf=\"config.subType === 'PASSWORD'\" class=\"password-wrapper position-relative d-flex align-items-center\">\n <input [type]=\"showPassword ? 'text' : 'password'\" class=\"field-input password-input\"\n [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\n <button type=\"button\"\n class=\"password-toggle position-absolute cursor-pointer d-flex align-items-center justify-content-center b-none border-none c-6B7280 p-0-25rem\"\n (click)=\"showPassword = !showPassword\" tabindex=\"-1\"\n [attr.aria-label]=\"showPassword ? 'Hide password' : 'Show password'\">\n <mat-icon class=\"eye-icon\">{{ showPassword ? 'visibility' : 'visibility_off' }}</mat-icon>\n </button>\n </div>\n\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\n <span class=\"input-prefix br-none\" *ngIf=\"config.prefix\">{{ config.prefix }}</span>\n\n <input *ngIf=\"config.subType !== 'LONG' && config.subType !== 'PASSWORD'\"\n [type]=\"config.subType === 'EMAIL' ? 'email' : config.subType === 'PHONE' ? 'tel' : 'text'\" class=\"field-input\"\n [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\"\n [readonly]=\"config.readonly\">\n\n <span class=\"input-suffix d-flex align-items-center font-weight-500\" *ngIf=\"config.suffix\">{{ config.suffix\n }}</span>\n\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\n <mat-icon class=\"lock-icon\">lock</mat-icon>\n </div>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n <div class=\"char-count-hint font-poppins font-weight-400 text-14 text-right c-6B7280\" *ngIf=\"showCharCount\">\n {{ remainingCharacters }} characters remaining\n </div>\n </div>\n\n <!-- \u2550\u2550 Number Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isNumberField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\n <span class=\"input-prefix br-none\" *ngIf=\"config.prefix\">{{ config.prefix }}</span>\n\n <input type=\"number\" class=\"field-input\" [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\"\n [min]=\"config.numberConfig?.min ?? null\" [max]=\"config.numberConfig?.max ?? null\"\n [step]=\"config.numberConfig?.step || 1\" [class.is-invalid]=\"errorMessage\" [readonly]=\"config.readonly\">\n\n <span class=\"input-suffix d-flex align-items-center font-weight-500\" *ngIf=\"config.suffix\">{{ config.suffix\n }}</span>\n\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\n <mat-icon class=\"lock-icon\">lock</mat-icon>\n </div>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Date Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isDateField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\n <input matInput [matDatepicker]=\"datePicker\" class=\"field-input date-input has-icon-right\"\n [formControlName]=\"config.name!\" [min]=\"config.dateConfig?.minDate\" [max]=\"config.dateConfig?.maxDate\"\n [class.is-invalid]=\"errorMessage\" [placeholder]=\"config.placeholder || ''\" [readonly]=\"config.readonly\"\n (click)=\"!config.readonly && datePicker.open()\">\n <div class=\"date-icon-wrapper position-absolute d-flex align-items-center justify-content-center\"\n *ngIf=\"!config.readonly\">\n <mat-datepicker-toggle matSuffix [for]=\"datePicker\"></mat-datepicker-toggle>\n </div>\n <mat-datepicker #datePicker></mat-datepicker>\n\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\n <mat-icon class=\"lock-icon\">lock</mat-icon>\n </div>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Time Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isTimeField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\n <input type=\"time\" class=\"field-input time-input\" [formControlName]=\"config.name!\"\n [class.is-invalid]=\"errorMessage\" [readonly]=\"!!config.readonly\">\n\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\n <mat-icon class=\"lock-icon\">lock</mat-icon>\n </div>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Autocomplete \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isAutocomplete\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <!-- Hidden real control (stores the code value) -->\n <input type=\"hidden\" [formControlName]=\"config.name!\">\n\n <div class=\"autocomplete-wrapper position-relative d-flex align-items-center w-100\"\n [class.is-invalid]=\"errorMessage\" [class.readonly]=\"config.readonly\">\n <!-- Search icon -->\n <mat-icon class=\"ac-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">search</mat-icon>\n\n <input class=\"field-input ac-input\" [formControl]=\"autocompleteInputCtrl\" [matAutocomplete]=\"auto\"\n [placeholder]=\"config.placeholder || 'Search\u2026'\" [readonly]=\"!!config.readonly\" [class.is-invalid]=\"errorMessage\"\n (blur)=\"onAutocompleteClear()\" autocomplete=\"off\">\n\n <!-- Clear button -->\n <button type=\"button\"\n class=\"ac-clear-btn position-absolute d-flex align-items-center justify-content-center cursor-pointer rounded-50 b-none border-none c-9CA3AF p-0-2rem\"\n *ngIf=\"autocompleteInputCtrl.value && !config.readonly\"\n (click)=\"autocompleteInputCtrl.setValue(''); updateValue(null)\" tabindex=\"-1\" aria-label=\"Clear\">\n <mat-icon>close</mat-icon>\n </button>\n\n <mat-autocomplete #auto=\"matAutocomplete\" [panelWidth]=\"'auto'\">\n <mat-option *ngFor=\"let option of filteredOptions\" [value]=\"option.label\"\n (onSelectionChange)=\"onAutocompleteSelected(option)\">\n <span class=\"ac-option-label\">{{ option.label }}</span>\n\n <!-- Dynamic display fields (image / email / phone / text) -->\n <div class=\"ac-display-fields d-flex flex-wrap gap-6 mt-2\" *ngIf=\"option['displayMeta']?.length\">\n <ng-container *ngFor=\"let field of option['displayMeta']\">\n\n <!-- Image avatar -->\n <span *ngIf=\"field.type === 'image' && field.value\" class=\"ac-df-item ac-df-image\">\n <img [src]=\"field.value\" [alt]=\"field.label || 'image'\" class=\"ac-df-avatar\">\n </span>\n\n <!-- Email -->\n <span *ngIf=\"field.type === 'email' && field.value\" class=\"ac-df-item ac-df-chip\">\n <mat-icon class=\"ac-df-icon\">mail_outline</mat-icon>\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\n {{ field.value }}\n </span>\n\n <!-- Phone -->\n <span *ngIf=\"field.type === 'phone' && field.value\" class=\"ac-df-item ac-df-chip\" [class]=\"field.className\">\n <mat-icon class=\"ac-df-icon\">phone</mat-icon>\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\n {{ field.value }}\n </span>\n\n <!-- Custom / Icon-based / Generic Text -->\n <span *ngIf=\"field.type !== 'image' && field.type !== 'email' && field.type !== 'phone' && field.value\" \n class=\"ac-df-item\" [class.ac-df-chip]=\"!!field.icon\" [class]=\"field.className\">\n <mat-icon class=\"ac-df-icon\" *ngIf=\"field.icon\">{{ field.icon }}</mat-icon>\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\n {{ field.value }}\n </span>\n\n </ng-container>\n </div>\n </mat-option>\n <mat-option *ngIf=\"filteredOptions.length === 0\" disabled class=\"ac-no-results fs-0-8125rem c-6B7280\">\n No results found\n </mat-option>\n </mat-autocomplete>\n\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\n <mat-icon class=\"lock-icon\">lock</mat-icon>\n </div>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Dropdown \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isDropdown\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <select *ngIf=\"config.subType === 'SINGLE'\" class=\"field-input\" [formControlName]=\"config.name!\"\n [class.is-invalid]=\"errorMessage\">\n <option [ngValue]=\"null\" disabled selected>{{ config.placeholder || 'Select' }}</option>\n <option *ngFor=\"let option of config.optionConfig?.optionList\" [value]=\"option.code\">\n {{ option.label }}\n </option>\n </select>\n\n <!-- MULTIPLE SELECT: custom panel with checkboxes -->\n <div *ngIf=\"config.subType === 'MULTIPLE'\" class=\"multi-select-wrapper\"\n [class.is-invalid]=\"errorMessage\">\n\n <div class=\"field-input multi-select-trigger d-flex align-items-center justify-content-between cursor-pointer\"\n [class.ms-open]=\"isMultiDropdownOpen\"\n (click)=\"toggleMultiDropdown($event)\">\n <span *ngIf=\"multiSelectedCount > 0\" class=\"multi-select-value fs-0-9rem\">\n {{ multiSelectedCount }} selected\n </span>\n <span *ngIf=\"multiSelectedCount === 0\" class=\"multi-select-placeholder\">\n {{ config.placeholder || selectPlaceholderLabel }}\n </span>\n <mat-icon class=\"multi-select-arrow\">\n {{ isMultiDropdownOpen ? expandLessLabel : expandMoreLabel }}\n </mat-icon>\n </div>\n\n <div class=\"multi-select-panel\" *ngIf=\"isMultiDropdownOpen\"\n (click)=\"$event.stopPropagation()\">\n <label *ngFor=\"let option of config.optionConfig?.optionList\"\n class=\"multi-select-option d-flex align-items-center gap-8 cursor-pointer\">\n <input type=\"checkbox\"\n [checked]=\"isChecked(option.code)\"\n [disabled]=\"!!config.disabled\"\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\">\n <span class=\"fs-0-875rem c-111827\">{{ option.label }}</span>\n </label>\n <div *ngIf=\"!config.optionConfig?.optionList?.length\"\n class=\"multi-select-empty fs-0-875rem c-6B7280\">\n {{ noOptionsAvailableLabel }}\n </div>\n </div>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Radio \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isRadio\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"radio-group\" [class.is-invalid]=\"errorMessage\"\n [class]=\"config.optionConfig?.layout ? 'layout-' + config.optionConfig!.layout.toLowerCase() : ''\">\n <label *ngFor=\"let option of config.optionConfig?.optionList\" class=\"radio-label\"\n [class.card-item]=\"config.subType === 'CARD'\"\n [class.selected]=\"formGroup.get(config.name!)?.value === option.code\"\n [style.gridColumn]=\"config.optionConfig?.layout?.toUpperCase() === 'COLUMN' ? 'span ' + getOptionColSpan(option) : null\">\n <input type=\"radio\" [formControlName]=\"config.name!\" [value]=\"option.code\">\n <div class=\"option-content d-flex flex-column gap-4 flex-1 text-left\">\n <span class=\"label-text text-16 c-1F2937\">{{ option.label }}</span>\n <span class=\"option-hint text-13 color-gray\" *ngIf=\"option.hint\">{{ option.hint }}</span>\n </div>\n </label>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Checkbox \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isCheckbox\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label && config.subType === 'LIST'\"\n class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div *ngIf=\"config.subType === 'BOOL'\" class=\"checkbox-single\">\n <label class=\"checkbox-label d-flex align-items-center gap-8 cursor-pointer fs-0-875rem c-111827\">\n <input type=\"checkbox\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\n <span>{{ config.label }}</span>\n </label>\n </div>\n\n <div *ngIf=\"config.subType === 'LIST' || config.subType === 'CARD'\" class=\"checkbox-group d-flex flex-column gap-8\"\n [class.is-invalid]=\"errorMessage\"\n [class]=\"config.optionConfig?.layout ? 'layout-' + config.optionConfig!.layout.toLowerCase() : ''\">\n <label *ngFor=\"let option of config.optionConfig?.optionList\"\n class=\"checkbox-label d-flex align-items-center gap-8 cursor-pointer fs-0-875rem c-111827\"\n [class.card-item]=\"config.subType === 'CARD'\" [class.selected]=\"isChecked(option.code)\"\n [style.gridColumn]=\"config.optionConfig?.layout?.toUpperCase() === 'COLUMN' ? 'span ' + getOptionColSpan(option) : null\">\n <input type=\"checkbox\" [checked]=\"isChecked(option.code)\" [disabled]=\"!!config.disabled\"\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\">\n <div class=\"option-content d-flex flex-column gap-4 flex-1 text-left\">\n <span class=\"label-text text-16 c-1F2937\">{{ option.label }}</span>\n <span class=\"option-hint text-13 color-gray\" *ngIf=\"option.hint\">{{ option.hint }}</span>\n </div>\n </label>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Chip \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isChip\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"chip-group d-flex flex-wrap gap-8\" [class.is-invalid]=\"errorMessage\">\n <label *ngFor=\"let option of config.optionConfig?.optionList\"\n class=\"chip-label cursor-pointer p-6px-14px b-ffffff c-374151 b-1px-solid-D1D5DB br-20px fs-0-875rem\"\n [class.selected]=\"isChecked(option.code)\">\n <input type=\"checkbox\" [checked]=\"isChecked(option.code)\" [disabled]=\"!!config.disabled\"\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\" hidden>\n <span>{{ option.label }}</span>\n </label>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Switch \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isSwitch\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label class=\"switch-container d-flex justify-content-between align-items-center cursor-pointer\">\n <span class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">{{ config.label }}</span>\n <div class=\"switch position-relative\">\n <input type=\"checkbox\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\n <span class=\"slider position-absolute cursor-pointer\"></span>\n </div>\n </label>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Rich Text \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isRichText\" class=\"field-wrapper component-rich-text d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"rich-text-container\" [class.is-invalid]=\"errorMessage\">\n <quill-editor [formControlName]=\"config.name!\" class=\"rich-text-editor d-block w-100\"\n [placeholder]=\"config.richTextConfig?.placeholder || config.placeholder || ''\"\n [styles]=\"{height: config.richTextConfig?.height || '200px'}\">\n </quill-editor>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n <div class=\"char-count-hint font-poppins font-weight-400 text-14 text-right c-6B7280\" *ngIf=\"showCharCount\">\n {{ remainingCharacters }} characters remaining\n </div>\n </div>\n\n <!-- \u2550\u2550 Rating \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isRating\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"rating-group d-flex gap-4\" [class.is-invalid]=\"errorMessage\">\n <span *ngFor=\"let star of getStarArray()\" class=\"star d-inline-flex align-items-center cursor-pointer\"\n [class.filled]=\"isStarFilled(star)\" [class.half]=\"isStarHalf(star)\" (click)=\"onRatingChange(star, $event)\">\n <mat-icon>{{ isStarFilled(star) || isStarHalf(star) ? 'star' : 'star_border' }}</mat-icon>\n </span>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Generated Field (read-only) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isGenerated\" class=\"field-wrapper d-flex flex-column gap-6\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">{{ config.label\n }}</label>\n <div class=\"generated-value fs-0-875rem p-0-625rem-0-875rem b-F3F4F6 b-1px-solid-E5E7EB br-8px c-6B7280\">{{ value ||\n '-' }}</div>\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint\">{{ config.hint }}</span>\n </div>\n\n <!-- \u2550\u2550 File Upload \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isFileUpload\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <!-- Drop Zone -->\n <div\n class=\"upload-drop-zone d-flex flex-column align-items-center justify-content-center gap-8 cursor-pointer text-center p-32px-24px us-none\"\n [class.drag-over]=\"isDragOver\" [class.has-files]=\"value?.length\" [class.is-invalid]=\"errorMessage\"\n (dragover)=\"onDragOver($event)\" (dragleave)=\"onDragLeave($event)\" (drop)=\"onFileDrop($event)\"\n (click)=\"fileInput.click()\">\n\n <!-- Icon with accent-colour pill background -->\n <div class=\"upload-icon-wrap mb-4\">\n <div class=\"dropzone-icon-pill d-flex align-items-center justify-content-center\">\n <mat-icon class=\"upload-cloud-icon\">cloud_upload</mat-icon>\n </div>\n </div>\n\n <p class=\"upload-main-text font-weight-600 m-0 fs-0-9rem\">Drag & drop files here</p>\n <p class=\"upload-sub-text m-0 fs-0-8rem c-64748B\">or <span class=\"upload-link\">Browse files</span></p>\n <p class=\"upload-hint-text m-0 fs-0-78rem c-64748B\" *ngIf=\"config.attachmentConfig?.acceptLabel\">\n Supported: <span class=\"upload-formats font-weight-500\">{{ config.attachmentConfig!.acceptLabel }}</span>\n </p>\n <p class=\"upload-hint-text m-0 fs-0-78rem c-64748B\" *ngIf=\"!config.attachmentConfig?.acceptLabel && config.hint\">\n {{ config.hint }}\n </p>\n <span class=\"upload-size-badge fs-0-72rem\" *ngIf=\"config.attachmentConfig?.maxSizeMB\">\n Max {{ config.attachmentConfig!.maxSizeMB }}MB\n </span>\n\n <!-- Hidden native file input -->\n <input #fileInput type=\"file\" hidden [attr.multiple]=\"config.attachmentConfig?.multiple ? true : null\"\n [attr.accept]=\"config.attachmentConfig?.accept || null\" (change)=\"onFileSelected($event)\">\n </div>\n\n <!-- Uploaded file list -->\n <div class=\"uploaded-list d-flex flex-column gap-8 mt-10\" *ngIf=\"value?.length\">\n <div *ngFor=\"let f of value; let i = index\"\n class=\"uploaded-item d-flex align-items-center gap-10 p-10px-14px br-8px\"\n [class.uploading]=\"f.isUploading\">\n\n <!-- Uploading spinner -->\n <ng-container *ngIf=\"f.isUploading; else fileReady\">\n <div class=\"upload-spinner rounded-50 b-2px-solid-E2E8F0\"></div>\n <div class=\"file-info flex-1 d-flex flex-column\">\n <span class=\"file-name font-weight-500 overflow-hidden fs-0-85rem\" [title]=\"f.name\">{{ f.name }}</span>\n <span class=\"file-size uploading-label fs-0-72rem\">Uploading...</span>\n </div>\n </ng-container>\n\n <!-- Normal state once upload is done -->\n <ng-template #fileReady>\n <mat-icon class=\"file-type-icon\">{{ getFileIcon(f.type) }}</mat-icon>\n <img *ngIf=\"f.type?.startsWith('image') && f.dataUrl\" [src]=\"f.dataUrl\" class=\"file-thumb rounded-4\"\n alt=\"preview\">\n <div class=\"file-info flex-1 d-flex flex-column\">\n <span class=\"file-name font-weight-500 overflow-hidden fs-0-85rem\" [title]=\"f.name\">{{ f.name }}</span>\n <span class=\"file-size fs-0-72rem\">{{ formatFileSize(f.size) }}</span>\n </div>\n </ng-template>\n\n <!-- Compact icon-only remove button -->\n <button type=\"button\" class=\"file-remove-btn d-flex align-items-center justify-content-center rounded-50\"\n [disabled]=\"f.isUploading\" (click)=\"!f.isUploading && removeUploadedFile(i)\"\n aria-label=\"Remove file\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n\n <!-- Validation / file errors -->\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"fileUploadError\">{{ fileUploadError }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage && !fileUploadError\">{{ errorMessage }}</span>\n <span class=\"field-hint c-6B7280 fs-0-75rem\"\n *ngIf=\"config.hint && !errorMessage && !fileUploadError && !config.attachmentConfig?.acceptLabel\">\n {{ config.hint }}\n </span>\n </div>\n\n <!-- \u2550\u2550 Media Upload (Type 2) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isMediaUpload\" class=\"field-wrapper media-upload-wrapper d-flex flex-column gap-6 p-0 border-none b-none\"\n [formGroup]=\"formGroup\">\n\n <!-- Hidden file input lives outside *ngIf \u2014 triggered via ViewChild -->\n <input #mediaDeviceInput type=\"file\" hidden multiple accept=\"image/*\" (change)=\"onMediaFileSelected($event)\">\n\n <!-- Two-column layout -->\n <div class=\"mu-layout d-grid align-items-start\">\n\n <!-- \u2500\u2500 LEFT PANEL \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"mu-left d-flex flex-column gap-20\">\n\n <!-- Header: title + max-items badge -->\n <div class=\"mu-header d-flex align-items-start flex-wrap gap-10\">\n <h3 class=\"mu-title m-0 c-111827 fs-1-35rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </h3>\n <span\n class=\"mu-badge d-inline-flex align-items-center rounded-20 color-white font-weight-600 fs-0-72rem p-4px-12px b-111827\"\n *ngIf=\"config.attachmentConfig?.maxFiles\">\n {{ controller.labels['LBL_MEDIA_MAX_PREFIX'] || 'Maximum' }}\n {{ config.attachmentConfig?.maxFiles }}\n {{ controller.labels['LBL_MEDIA_MAX_SUFFIX'] || 'Image & Videos' }}\n </span>\n </div>\n\n <!-- Description -->\n <p class=\"mu-description m-0 fs-0-875rem c-6B7280\" *ngIf=\"config.attachmentConfig?.description\">\n {{ config.attachmentConfig?.description }}\n </p>\n <p class=\"mu-description m-0 fs-0-875rem c-6B7280\"\n *ngIf=\"!config.attachmentConfig?.description && controller.labels['LBL_MEDIA_DESC']\">\n {{ controller.labels['LBL_MEDIA_DESC'] }}\n </p>\n\n <!-- Feature bullet list -->\n <ul class=\"mu-features m-0 p-0 d-flex flex-column gap-8 ls-none\"\n *ngIf=\"config.attachmentConfig?.features?.length || controller.labels['LBL_MEDIA_FEATURE_1']\">\n <ng-container *ngIf=\"config.attachmentConfig?.features?.length\">\n <li *ngFor=\"let f of config.attachmentConfig?.features\"\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ f }}\n </li>\n </ng-container>\n <ng-container *ngIf=\"!config.attachmentConfig?.features?.length\">\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_1']\"\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_1'] }}\n </li>\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_2']\"\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_2'] }}\n </li>\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_3']\"\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_3'] }}\n </li>\n </ng-container>\n </ul>\n\n <!-- Backdrop to close dropdown on outside click -->\n <div class=\"media-menu-backdrop\" *ngIf=\"showMediaMenu\"\n (click)=\"$event.stopPropagation(); showMediaMenu = false\"></div>\n\n <!-- Add Media button + dropdown -->\n <div class=\"media-add-container position-relative\" (click)=\"showMediaMenu = !showMediaMenu\">\n <lib-button id=\"btn-add-media-{{ config.name }}\" [variant]=\"'warning'\"\n [icon]=\"{type: 'material', value: 'add_photo_alternate'}\">\n {{ controller.labels['LBL_ADD_MEDIA'] || 'Add media' }}\n <mat-icon class=\"menu-chevron fs-18px\">add</mat-icon>\n </lib-button>\n\n <div class=\"media-dropdown position-absolute rounded-12 overflow-hidden b-ffffff b-1px-solid-E5E7EB\"\n *ngIf=\"showMediaMenu\" role=\"menu\" (click)=\"$event.stopPropagation()\">\n <!-- Video -->\n <button id=\"btn-media-video-{{ config.name }}\" type=\"button\"\n class=\"media-dropdown-item d-flex align-items-center gap-12 w-100 cursor-pointer text-left b-none border-none p-12px-16px bb-1px-solid-F3F4F6\"\n (click)=\"onMediaMenuVideo(); showMediaMenu = false\" role=\"menuitem\">\n <span\n class=\"media-drop-icon media-drop-icon--video d-flex align-items-center justify-content-center rounded-8\"><mat-icon>videocam</mat-icon></span>\n <span class=\"media-drop-text d-flex flex-column flex-1\">\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\n controller.labels['LBL_MEDIA_VIDEO'] || 'Video' }}</span>\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_VIDEO_DESC'] || 'Add\n YouTube URL'\n }}</span>\n </span>\n </button>\n <!-- Device -->\n <button id=\"btn-media-device-{{ config.name }}\" type=\"button\"\n class=\"media-dropdown-item d-flex align-items-center gap-12 w-100 cursor-pointer text-left b-none border-none p-12px-16px bb-1px-solid-F3F4F6\"\n (click)=\"onMediaMenuDevice(); showMediaMenu = false\" role=\"menuitem\">\n <span\n class=\"media-drop-icon media-drop-icon--device d-flex align-items-center justify-content-center rounded-8\"><mat-icon>upload</mat-icon></span>\n <span class=\"media-drop-text d-flex flex-column flex-1\">\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\n controller.labels['LBL_MEDIA_DEVICE'] || 'Upload from device'\n }}</span>\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_DEVICE_DESC'] ||\n 'Select images from your\n computer' }}</span>\n </span>\n </button>\n <!-- Library -->\n <button id=\"btn-media-library-{{ config.name }}\" type=\"button\"\n class=\"media-dropdown-item d-flex align-items-center gap-12 w-100 cursor-pointer text-left b-none border-none p-12px-16px bb-1px-solid-F3F4F6\"\n (click)=\"onMediaMenuLibrary(); showMediaMenu = false\" role=\"menuitem\">\n <span\n class=\"media-drop-icon media-drop-icon--library d-flex align-items-center justify-content-center rounded-8\"><mat-icon>photo_library</mat-icon></span>\n <span class=\"media-drop-text d-flex flex-column flex-1\">\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\n controller.labels['LBL_MEDIA_LIBRARY'] || 'Upload from library'\n }}</span>\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_LIBRARY_DESC'] ||\n 'Choose from default\n images' }}</span>\n </span>\n </button>\n </div>\n </div>\n\n <!-- YouTube URL Input (inline below button) -->\n <div class=\"youtube-input-panel d-flex flex-column gap-8 p-16 rounded-10 b-FFFAF1 b-1px-solid-E5E7EB\"\n *ngIf=\"showYoutubeInput\">\n <label class=\"youtube-panel-label d-flex align-items-center gap-6 font-weight-600 fs-0-875rem c-111827\">\n {{ controller.labels['LBL_YOUTUBE_URL'] || 'Video URL' }}\n </label>\n <div class=\"youtube-input-row d-flex gap-8\">\n <input id=\"input-youtube-url-{{ config.name }}\" type=\"url\" class=\"field-input youtube-url-input\"\n [(ngModel)]=\"youtubeUrlInput\" [ngModelOptions]=\"{standalone: true}\"\n [placeholder]=\"controller.labels['PH_YOUTUBE_URL'] || 'Video URL'\" (keyup.enter)=\"addYoutubeMedia()\">\n <lib-button id=\"btn-add-youtube-{{ config.name }}\" [variant]=\"'secondary'\" (click)=\"addYoutubeMedia()\">\n {{ controller.labels['LBL_ADD'] || 'Add' }}\n </lib-button>\n </div>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"youtubeUrlError\">{{ youtubeUrlError }}</span>\n </div>\n\n <div\n class=\"media-upload-status d-flex align-items-center gap-8 mt-4 color-error rounded-8 font-weight-500 p-10px-14px b-FEF2F2 fs-0-85rem\"\n *ngIf=\"mediaUploadError\">\n <mat-icon class=\"status-icon fs-18px\">error_outline</mat-icon>\n <span>{{ mediaUploadError }}</span>\n </div>\n\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n </div>\n <!-- end left panel -->\n\n <!-- \u2500\u2500 RIGHT PANEL (carousel) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"mu-right d-flex flex-column gap-12\">\n\n <!-- Carousel (when items exist) -->\n <div class=\"media-carousel-section d-flex flex-column gap-12\" *ngIf=\"mediaItems.length\">\n <div\n class=\"media-carousel-main position-relative w-100 overflow-hidden d-flex align-items-center justify-content-center br-12px b-0F172A\">\n <button id=\"btn-carousel-prev-{{ config.name }}\" type=\"button\"\n class=\"carousel-nav carousel-nav--prev position-absolute rounded-50 cursor-pointer d-flex align-items-center justify-content-center border-none b-rgba-255-255-255-0-85\"\n (click)=\"mediaCarouselPrev()\" [disabled]=\"mediaCarouselIndex === 0\" aria-label=\"Previous\">\n <mat-icon>chevron_left</mat-icon>\n </button>\n\n <div class=\"carousel-viewer position-absolute d-flex align-items-center justify-content-center\"\n *ngFor=\"let item of mediaItems; let i = index\" [hidden]=\"i !== mediaCarouselIndex\">\n <div *ngIf=\"item.isUploading\"\n class=\"carousel-uploading d-flex flex-column align-items-center gap-12 c-94A3B8 fs-0-85rem\">\n <div class=\"carousel-spinner rounded-50 b-3px-solid-rgba-255-255-255-0-15\"></div>\n <span>{{ controller.labels['LBL_UPLOADING'] || 'Uploading\u2026' }}</span>\n </div>\n <ng-container *ngIf=\"!item.isUploading && item.mediaType === 'youtube'\">\n <iframe class=\"carousel-iframe w-100 h-100 br-12px\" [src]=\"item.url | trustedUrl\" frameborder=\"0\"\n allowfullscreen\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\">\n </iframe>\n </ng-container>\n <ng-container *ngIf=\"!item.isUploading && item.mediaType === 'image'\">\n <img class=\"carousel-image w-100 h-100 br-12px\" [src]=\"item.url\" alt=\"Media\">\n </ng-container>\n <button id=\"btn-remove-media-{{ config.name }}-{{ i }}\" type=\"button\"\n class=\"carousel-remove-btn position-absolute rounded-50 cursor-pointer d-flex align-items-center justify-content-center border-none b-rgba-0-0-0-0-55\"\n [disabled]=\"item.isUploading\" (click)=\"removeMediaItem(i)\" aria-label=\"Remove\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n\n <button id=\"btn-carousel-next-{{ config.name }}\" type=\"button\"\n class=\"carousel-nav carousel-nav--next position-absolute rounded-50 cursor-pointer d-flex align-items-center justify-content-center border-none b-rgba-255-255-255-0-85\"\n (click)=\"mediaCarouselNext()\" [disabled]=\"mediaCarouselIndex === mediaItems.length - 1\" aria-label=\"Next\">\n <mat-icon>chevron_right</mat-icon>\n </button>\n\n <div class=\"carousel-dots position-absolute d-flex gap-6\">\n <span *ngFor=\"let item of mediaItems; let i = index\"\n class=\"carousel-dot rounded-50 cursor-pointer b-rgba-255-255-255-0-45\"\n [class.active]=\"i === mediaCarouselIndex\" (click)=\"mediaGoTo(i)\"></span>\n </div>\n </div>\n\n <!-- Thumbnail strip -->\n <div class=\"media-thumbnail-strip d-flex flex-wrap gap-8 pb-4px\">\n <div *ngFor=\"let item of mediaThumbnails; let i = index\"\n class=\"media-thumb rounded-8 overflow-hidden cursor-pointer d-flex align-items-center justify-content-center b-2px-solid-transparent b-E2E8F0\"\n [class.active]=\"i === mediaCarouselIndex\" (click)=\"mediaGoTo(i)\">\n <div *ngIf=\"item.isUploading\"\n class=\"thumb-uploading d-flex align-items-center justify-content-center w-100 h-100\">\n <div class=\"thumb-spinner rounded-50 b-2px-solid-E2E8F0\"></div>\n </div>\n <img *ngIf=\"!item.isUploading && item.mediaType === 'youtube' && item.thumbnailUrl\"\n [src]=\"item.thumbnailUrl\" class=\"thumb-img w-100 h-100\" alt=\"Video thumbnail\">\n <div *ngIf=\"!item.isUploading && item.mediaType === 'youtube' && !item.thumbnailUrl\"\n class=\"thumb-yt-placeholder d-flex align-items-center justify-content-center w-100 h-100 b-1E293B c-EF4444\">\n <mat-icon>play_circle</mat-icon>\n </div>\n <img *ngIf=\"!item.isUploading && item.mediaType === 'image' && item.url\" [src]=\"item.url\"\n class=\"thumb-img w-100 h-100\" alt=\"Image thumbnail\">\n </div>\n </div>\n </div>\n\n <!-- Empty right-side placeholder -->\n <div\n class=\"mu-right-empty d-flex flex-column align-items-center justify-content-center gap-10 h-100 text-center p-24 br-12px b-FFFAF1 c-94A3B8 b-2px-dashed-CBD5E1\"\n *ngIf=\"!mediaItems.length\" (click)=\"onMediaMenuDevice()\">\n <mat-icon class=\"mu-right-empty-icon fs-52px\">perm_media</mat-icon>\n <p>{{ controller.labels['LBL_ADD_MEDIA'] || 'Add media' }}</p>\n </div>\n\n </div>\n <!-- end right panel -->\n\n </div><!-- end mu-layout -->\n </div>\n\n\n <!-- \u2550\u2550 Library Image Picker Modal \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <!-- Wrapper is always in DOM (hidden) so @ViewChild can move it to body -->\n <div #libraryModal class=\"media-library-portal-host\" [class.is-open]=\"showLibraryModal\">\n\n <!-- Backdrop -->\n <div class=\"media-library-overlay\" (click)=\"closeLibraryModal()\"></div>\n\n <!-- Modal card -->\n <div class=\"media-library-modal d-flex flex-column overflow-hidden b-ffffff br-16px\"\n role=\"dialog\" aria-modal=\"true\">\n <div class=\"library-modal-header d-flex align-items-start justify-content-between p-24px-28px bb-1px-solid-E5E7EB\">\n <div class=\"header-left d-flex flex-column gap-8\">\n <h3 class=\"library-modal-title m-0 color-dark fs-1-25rem\">\n {{ controller.labels['LBL_ADD_IMAGES'] || 'Add Images' }}\n </h3>\n <p class=\"library-modal-subtitle m-0 color-gray fs-0-85rem\">\n {{ controller.labels['LBL_LIBRARY_MODAL_DESC'] || 'Select images from your library.' }}\n </p>\n </div>\n <button id=\"btn-close-library-{{ config.name }}\" type=\"button\"\n class=\"library-close-btn d-flex align-items-center justify-content-center cursor-pointer rounded-50 border-none b-none c-9CA3AF\"\n (click)=\"closeLibraryModal()\" aria-label=\"Close\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n\n <!-- Loading -->\n <div class=\"library-loading\" *ngIf=\"libraryLoading\">\n <div class=\"lib-spinner rounded-50 b-3px-solid-E2E8F0\"></div>\n <span>{{ controller.labels['LBL_LOADING'] || 'Loading\u2026' }}</span>\n </div>\n\n <!-- Error -->\n <div class=\"library-error d-flex align-items-center gap-8 color-error b-FEF2F2 fs-0-875rem p-16px-24px\"\n *ngIf=\"libraryError && !libraryLoading\">\n <mat-icon>error_outline</mat-icon>\n {{ libraryError }}\n </div>\n\n <!-- Image grid -->\n <div class=\"library-grid d-grid gap-16 flex-1 p-28px b-F9FAFB\" *ngIf=\"!libraryLoading && libraryImages.length\">\n <div *ngFor=\"let img of libraryImages; let li = index\" id=\"lib-img-{{ config.name }}-{{ li }}\"\n class=\"library-grid-item position-relative rounded-12 overflow-hidden cursor-pointer bg-white b-3px-solid-transparent\"\n [class.selected]=\"isLibraryItemSelected(img)\" (click)=\"toggleLibraryItem(img)\">\n <img [src]=\"getLibraryItemUrl(img)\" class=\"library-grid-img w-100 h-100 d-block\" alt=\"Library image\">\n <div\n class=\"library-check position-absolute bg-white rounded-50 d-flex align-items-center justify-content-center c-3B82F6\"\n *ngIf=\"isLibraryItemSelected(img)\">\n <mat-icon>check_circle</mat-icon>\n </div>\n <div class=\"library-overlay-hover position-absolute b-rgba-59-130-246-0-12\"></div>\n </div>\n </div>\n\n <!-- Empty library -->\n <div\n class=\"library-empty d-flex flex-column align-items-center justify-content-center gap-12 flex-1 c-9CA3AF fs-0-875rem p-48px-24px\"\n *ngIf=\"!libraryLoading && !libraryError && libraryImages.length === 0\">\n <mat-icon>image_not_supported</mat-icon>\n <span>{{ controller.labels['LBL_LIBRARY_EMPTY'] || 'No images found in library.' }}</span>\n </div>\n\n <!-- Footer -->\n <div class=\"library-modal-footer d-flex align-items-center justify-content-end bg-white p-20px-28px bt-1px-solid-E5E7EB\">\n <div class=\"library-footer-actions d-flex gap-12\">\n <lib-button id=\"btn-library-cancel-{{ config.name }}\" [variant]=\"'outline'\" (click)=\"closeLibraryModal()\">\n {{ controller.labels['LBL_CANCEL'] || 'Cancel' }}\n </lib-button>\n <lib-button id=\"btn-library-confirm-{{ config.name }}\" [variant]=\"'primary'\"\n [disabled]=\"librarySelectedIds.size === 0\" (click)=\"confirmLibrarySelection()\">\n {{ controller.labels['LBL_CONTINUE'] || 'Continue' }}\n </lib-button>\n </div>\n </div>\n </div>\n </div>\n\n\n\n <!-- \u2550\u2550 Location Field \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isLocation\" class=\"field-wrapper location-field-wrapper d-flex flex-column gap-6 gap-12\"\n [formGroup]=\"formGroup\">\n\n <!-- Field label -->\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n <p class=\"location-subtitle m-0 c-6B7280 fs-0-8125rem\" *ngIf=\"config.hint\">{{ config.hint }}</p>\n\n <!-- Three-tab bar -->\n <div class=\"location-tabs d-flex gap-12 mb-24\">\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'VENUE' ? 'warning' : 'outline'\"\n (click)=\"onLocationTabChange('VENUE')\">\n {{ controller.labels['LBL_LOC_VENUE'] || 'Venue' }}\n </lib-button>\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'ONLINE' ? 'warning' : 'outline'\"\n (click)=\"onLocationTabChange('ONLINE')\">\n {{ controller.labels['LBL_LOC_ONLINE'] || 'Online Event' }}\n </lib-button>\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'TBA' ? 'warning' : 'outline'\"\n (click)=\"onLocationTabChange('TBA')\">\n {{ controller.labels['LBL_LOC_TBA'] || 'To be Announced' }}\n </lib-button>\n </div>\n\n <!-- VENUE TAB -->\n <div *ngIf=\"locationActiveTab === 'VENUE'\" class=\"loc-panel loc-venue-panel d-flex flex-column gap-12\">\n\n <p class=\"loc-section-label m-0 font-weight-600 c-111827 fs-0-9rem\">\n {{ controller.labels['LBL_LOC_ADDRESS'] || 'Location address' }}\n </p>\n\n <!-- Added venue rows -->\n <div class=\"loc-venue-list d-flex flex-column gap-8\" *ngIf=\"locationVenues.length > 0\">\n <div *ngFor=\"let venue of locationVenues; let i = index\"\n class=\"loc-venue-item d-flex align-items-center gap-10 p-10px-14px br-7px b-ffffff b-1px-solid-D1D5DB\">\n <mat-icon class=\"loc-venue-search-icon fs-18px c-9CA3AF\">search</mat-icon>\n <span class=\"loc-venue-text flex-1 overflow-hidden fs-0-875rem c-111827\">{{ venue.address || venue.name ||\n venue.description }}</span>\n <button type=\"button\"\n class=\"loc-action-btn loc-delete-btn d-flex align-items-center justify-content-center cursor-pointer rounded-50 b-none border-none p-4px\"\n (click)=\"removeLocationVenue(i)\">\n <mat-icon>delete_outline</mat-icon>\n </button>\n </div>\n </div>\n\n <!-- Location count badge -->\n <p class=\"loc-count-text m-0 font-weight-600 fs-0-8125rem c-3B82F6\"\n *ngIf=\"locationVenues.length > 0 && config.locationConfig?.allowMulti\">\n {{ locationVenues.length }} {{ controller.labels['LBL_LOC_COUNT_SUFFIX'] || 'Locations Added!' }}\n </p>\n\n <!-- Search input (hide when max reached) -->\n <div class=\"loc-search-container position-relative\" *ngIf=\"!locationMaxReached\">\n <div class=\"loc-search-wrapper position-relative d-flex align-items-center mt-4\">\n <mat-icon class=\"loc-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">search</mat-icon>\n <input\n class=\"field-input loc-search-input w-100 font-poppins flex-1 fs-0-875rem c-111827 br-7px br-8px bc-F3F4F6 pl-2-4rem bc-DC2626 pt-0-625rem pb-0-625rem pl-16px pr-16px bc-ffffff b-1px-solid-D1D5DB pr-3-5rem\"\n [placeholder]=\"config.locationConfig?.venuePlaceholder || (controller.labels['PH_LOC_VENUE'] || 'Type to search venue...')\"\n [value]=\"locationSearchText\" (input)=\"handleLocationSearchInput($event)\" (blur)=\"hideLocationSuggestions()\"\n autocomplete=\"off\" [class.is-invalid]=\"errorMessage\">\n </div>\n <!-- Suggestions dropdown -->\n <div class=\"loc-suggestions-panel position-absolute overflow-hidden br-8px b-ffffff b-1px-solid-D1D5DB\"\n *ngIf=\"locationShowSuggestions && locationSuggestions.length\">\n <div *ngFor=\"let sug of locationSuggestions\"\n class=\"loc-suggestion-item d-flex align-items-center gap-10 cursor-pointer p-10px-14px\"\n (mousedown)=\"onLocationSuggestionSelect(sug)\">\n <mat-icon class=\"loc-suggestion-icon fs-18px c-E53E3E\">place</mat-icon>\n <span class=\"loc-suggestion-text overflow-hidden fs-0-875rem c-374151\">{{ sug.description }}</span>\n </div>\n </div>\n </div>\n\n <!-- Add another button -->\n <button type=\"button\"\n class=\"loc-add-btn d-inline-flex align-items-center gap-6 cursor-pointer font-weight-600 p-0 b-none border-none fs-0-875rem c-1A56DB\"\n *ngIf=\"locationVenues.length > 0 && !locationMaxReached && config.locationConfig?.allowMulti\">\n <mat-icon>add_circle_outline</mat-icon>\n <span>{{ controller.labels['LBL_LOC_ADD_ANOTHER'] || 'Add another Location' }}</span>\n </button>\n\n <!-- Map -->\n <div class=\"loc-map-container overflow-hidden br-10px b-1px-solid-E5E7EB\"\n *ngIf=\"config.locationConfig?.showMap !== false\">\n <ng-container *ngIf=\"config.locationConfig?.googleMapsApiKey; else simpleEmbed\">\n <div [id]=\"'loc-map-' + config.name\" class=\"loc-map-frame w-100 d-block border-none\"\n [style.height]=\"config.locationConfig?.mapHeight || '300px'\"></div>\n </ng-container>\n <ng-template #simpleEmbed>\n <iframe class=\"loc-map-frame w-100 d-block border-none\"\n [style.height]=\"config.locationConfig?.mapHeight || '300px'\" [src]=\"getLocationMapEmbedUrl() | trustedUrl\"\n frameborder=\"0\" allowfullscreen loading=\"lazy\">\n </iframe>\n </ng-template>\n </div>\n\n <!-- Map hint -->\n <p class=\"loc-map-hint m-0 text-center fs-0-78rem c-6B7280\">\n {{ controller.labels['LBL_LOC_MAP_HINT'] || 'Type the venue address. A map will appear to assist you.' }}\n </p>\n </div>\n\n <!-- ONLINE TAB -->\n <div *ngIf=\"locationActiveTab === 'ONLINE'\" class=\"loc-panel loc-online-panel d-flex flex-column gap-12\">\n <p class=\"loc-section-label m-0 font-weight-600 c-111827 fs-0-9rem\">\n {{ controller.labels['LBL_LOC_ONLINE_URL'] || 'Event URL' }}\n </p>\n <div class=\"loc-search-wrapper position-relative d-flex align-items-center mt-4\">\n <mat-icon class=\"loc-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">link</mat-icon>\n <input\n class=\"field-input loc-search-input w-100 font-poppins flex-1 fs-0-875rem c-111827 br-7px br-8px bc-F3F4F6 pl-2-4rem bc-DC2626 pt-0-625rem pb-0-625rem pl-16px pr-16px bc-ffffff b-1px-solid-D1D5DB pr-3-5rem\"\n type=\"url\"\n [placeholder]=\"config.locationConfig?.onlinePlaceholder || (controller.labels['PH_LOC_ONLINE'] || 'https://zoom.us/j/...')\"\n [value]=\"locationOnlineUrl\" (input)=\"onLocationUrlChange($any($event.target).value)\"\n [class.is-invalid]=\"errorMessage\">\n </div>\n </div>\n\n <!-- TBA TAB -->\n <div *ngIf=\"locationActiveTab === 'TBA'\"\n class=\"loc-panel loc-tba-panel d-flex flex-column gap-12 justify-content-center\">\n <div\n class=\"loc-tba-content d-flex flex-column align-items-center justify-content-center text-center gap-12 p-32px-24px b-F9FAFB b-1px-dashed-D1D5DB br-10px\">\n <mat-icon class=\"loc-tba-icon fs-40px c-9CA3AF\">schedule</mat-icon>\n <p class=\"loc-tba-text m-0 c-6B7280 fs-0-9rem\">\n {{ controller.labels['LBL_LOC_TBA_DESC'] || \"This event's location is yet to be announced. Check back later\n for updates.\" }}\n </p>\n </div>\n </div>\n\n <!-- Hidden real form control -->\n <input type=\"hidden\" [formControlName]=\"config.name!\">\n\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n</div>", styles: [".d-flex{display:flex}.d-inline-flex{display:inline-flex}.d-grid{display:grid}.d-block{display:block}.d-none{display:none}.flex-column{flex-direction:column}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.flex-wrap{flex-wrap:wrap}.flex-1{flex:1}.align-items-center{align-items:center}.align-items-start{align-items:flex-start}.align-items-end{align-items:flex-end}.justify-content-center{justify-content:center}.justify-content-between{justify-content:space-between}.justify-content-start{justify-content:flex-start}.justify-content-end{justify-content:flex-end}.grid-cols-12{grid-template-columns:repeat(12,1fr)}.w-100{width:100%}.h-100{height:100%}.position-relative{position:relative}.position-absolute{position:absolute}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-poppins{font-family:var(--cc-sf-font-family, \"Poppins\", sans-serif)}.font-weight-400{font-weight:400}.font-weight-500{font-weight:500}.font-weight-600{font-weight:600}.text-13{font-size:13px}.text-14{font-size:14px}.text-16{font-size:16px}.color-white{color:#fff}.color-dark{color:#111827}.color-gray{color:#6b7280}.color-error{color:var(--cc-sf-error-text-color, #DC2626)}.bg-white{background-color:#fff}.bg-transparent{background-color:transparent}.m-0{margin:0}.mt-4{margin-top:4px}.mt-8{margin-top:8px}.mt-10{margin-top:10px}.mt-12{margin-top:12px}.mt-16{margin-top:16px}.mt-20{margin-top:20px}.mt-24{margin-top:24px}.mb-0{margin-bottom:0}.mb-4{margin-bottom:4px}.mb-8{margin-bottom:8px}.mb-10{margin-bottom:10px}.mb-12{margin-bottom:12px}.mb-16{margin-bottom:16px}.mb-20{margin-bottom:20px}.mb-24{margin-bottom:24px}.ml-16{margin-left:16px}.ml-20{margin-left:20px}.p-0{padding:0}.p-16{padding:16px}.p-20{padding:20px}.p-24{padding:24px}.pt-20{padding-top:20px}.pb-10{padding-bottom:10px}.gap-4{gap:4px}.gap-6{gap:6px}.gap-8{gap:8px}.gap-10{gap:10px}.gap-12{gap:12px}.gap-16{gap:16px}.gap-20{gap:20px}.rounded-4{border-radius:4px}.rounded-6{border-radius:6px}.rounded-8{border-radius:8px}.rounded-10{border-radius:10px}.rounded-12{border-radius:12px}.rounded-20{border-radius:20px}.rounded-24{border-radius:24px}.rounded-50{border-radius:50%}.cursor-pointer{cursor:pointer}.overflow-hidden{overflow:hidden}.resize-vertical{resize:vertical}.box-sizing-border{box-sizing:border-box}.border-none{border:none!important}.mb-16px{margin-bottom:var(--cc-sf-grid-gap, 16px)!important}.c-DC2626{color:var(--cc-sf-label-required-color, #DC2626)!important}.ml-0-125rem{margin-left:.125rem!important}.fs-0-875rem{font-size:.875rem!important}.c-111827{color:var(--cc-sf-label-color, #111827)!important}.br-7px{border-radius:var(--cc-sf-input-radius, 7px)!important}.c-6B7280{color:var(--cc-sf-hint-color, #6B7280)!important}.fs-0-75rem{font-size:var(--cc-sf-error-text-size, .75rem)!important}.b-none{background:none!important}.p-32px-24px{padding:32px 24px!important}.us-none{-webkit-user-select:none!important;user-select:none!important}.c-1E293B{color:var(--cc-sf-label-color, #1E293B)!important}.c-3B82F6{color:var(--cc-sf-chip-selected-bg, #3B82F6)!important}.fs-0-78rem{font-size:.78rem!important}.p-10px-14px{padding:10px 14px!important}.fs-0-85rem{font-size:.85rem!important}.fs-0-72rem{font-size:.72rem!important}.c-94A3B8{color:#94a3b8!important}.p-4px{padding:4px!important}.br-8px{border-radius:var(--cc-sf-input-radius, 8px)!important}.bc-F3F4F6{background-color:var(--cc-sf-input-disabled-bg, #F3F4F6)!important}.br-none{border-right:none!important}.bl-none{border-left:none!important}.pe-none{pointer-events:none!important}.fs-1-1rem{font-size:1.1rem!important}.c-9CA3AF{color:var(--cc-sf-hint-color, #9CA3AF)!important}.pl-2-4rem{padding-left:2.4rem!important}.fs-0-8125rem{font-size:.8125rem!important}.ls-none{list-style:none!important}.br-12px{border-radius:var(--mu-carousel-radius, 12px)!important}.b-FFFAF1{background:var(--cc-sf-dropzone-bg, #FFFAF1)!important}.fs-18px{font-size:18px!important}.b-FEF2F2{background:var(--cc-sf-error-bg, #FEF2F2)!important}.bc-DC2626{border-color:var(--cc-sf-error-border, #DC2626)!important}.c-202124{color:var(--cc-sf-label-color, #202124)!important}.fs-18px{font-size:var(--cc-sf-label-size, 18px)!important}.mb-0-5rem{margin-bottom:.5rem!important}.pt-0-625rem{padding-top:var(--cc-sf-input-padding-y, .625rem)!important}.pb-0-625rem{padding-bottom:var(--cc-sf-input-padding-y, .625rem)!important}.pl-16px{padding-left:var(--cc-sf-input-padding-x, 16px)!important}.pr-16px{padding-right:var(--cc-sf-input-padding-x, 16px)!important}.bc-ffffff{background-color:var(--cc-sf-section-bg, #ffffff)!important}.b-1px-solid-D1D5DB{border:1px solid var(--cc-sf-input-border, #D1D5DB)!important}.fs-0-75rem{font-size:.75rem!important}.c-1F2937{color:var(--cc-sf-section-label-color, #1F2937)!important}.p-6px-14px{padding:var(--cc-sf-chip-padding, 6px 14px)!important}.b-ffffff{background:var(--loc-suggestion-bg, #ffffff)!important}.c-374151{color:var(--cc-sf-label-color, #374151)!important}.br-20px{border-radius:var(--cc-sf-chip-radius, 20px)!important}.fs-0-875rem{font-size:var(--cc-sf-btn-font-size, .875rem)!important}.bc-D1D5DB{background-color:var(--cc-sf-switch-track-off, #D1D5DB)!important}.pr-2-75rem{padding-right:2.75rem!important}.p-0-25rem{padding:.25rem!important}.p-0-625rem-0-875rem{padding:var(--cc-sf-generated-padding, .625rem .875rem)!important}.b-F3F4F6{background:var(--cc-sf-generated-bg, #F3F4F6)!important}.b-1px-solid-E5E7EB{border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)!important}.br-8px{border-radius:var(--cc-sf-uploaded-item-radius, 8px)!important}.c-6B7280{color:var(--ms-desc-color, #6B7280)!important}.mb-20px{margin-bottom:var(--cc-sf-section-gap, 20px)!important}.br-10px{border-radius:var(--cc-sf-input-radius, 10px)!important}.p-20px{padding:var(--cc-sf-section-padding, 20px)!important}.fs-1rem{font-size:1rem!important}.m-0-0-16px-0{margin:0 0 16px!important}.bb-2px-solid-E5E7EB{border-bottom:var(--cc-sf-section-label-border, 2px solid #E5E7EB)!important}.p-16px{padding:var(--cc-sf-instance-padding, 16px)!important}.b-F9FAFB{background:var(--loc-tba-bg, #F9FAFB)!important}.bb-1px-dashed-D1D5DB{border-bottom:var(--cc-sf-instance-divider, 1px dashed #D1D5DB)!important}.c-4B5563{color:var(--cc-sf-instance-num-color, #4B5563)!important}.fs-0-8125rem{font-size:var(--cc-sf-hint-size, .8125rem)!important}.pb-0{padding-bottom:0!important}.p-18px-24px{padding:18px 24px!important}.c-111827{color:var(--ms-title-color, #111827)!important}.bt-1px-solid-E5E7EB{border-top:1px solid #E5E7EB!important}.p-4px-10px{padding:4px 10px!important}.b-FFF5F5{background:var(--cc-sf-btn-remove-bg, #FFF5F5)!important}.c-E53E3E{color:var(--loc-delete-color, #E53E3E)!important}.b-1px-solid-FED7D7{border:var(--cc-sf-btn-remove-border, 1px solid #FED7D7)!important}.br-4px{border-radius:var(--cc-sf-btn-remove-radius, 4px)!important}.p-8px-16px{padding:8px 16px!important}.b-transparent{background:var(--cc-sf-btn-add-bg, transparent)!important}.c-3B82F6{color:var(--cc-sf-input-focus-border, #3B82F6)!important}.b-1px-dashed-CBD5E1{border:var(--cc-sf-btn-add-border, 1px dashed #CBD5E1)!important}.br-6px{border-radius:var(--cc-sf-btn-add-radius, 6px)!important}.b-1-5px-dashed-CBD5E1{border:var(--cc-sf-dropzone-border, 1.5px dashed #CBD5E1)!important}.br-12px{border-radius:var(--cc-sf-dropzone-radius, 12px)!important}.bc-FFFAF1{background-color:var(--cc-sf-dropzone-bg, #FFFAF1)!important}.c-94A3B8{color:var(--cc-sf-uploaded-remove-color, #94A3B8)!important}.fs-0-9rem{font-size:var(--cc-sf-input-font-size, .9rem)!important}.c-64748B{color:var(--cc-sf-dropzone-hint-color, #64748B)!important}.b-1px-solid-E2E8F0{border:var(--cc-sf-uploaded-item-border, 1px solid #E2E8F0)!important}.b-2px-solid-E2E8F0{border:2px solid #E2E8F0!important}.pr-3-5rem{padding-right:3.5rem!important}.p-0-0-875rem{padding:0 .875rem!important}.bc-FFFFFF{background-color:var(--cc-sf-input-bg, #FFFFFF)!important}.b-1-5px-solid-D1D5DB{border:var(--cc-sf-input-border, 1.5px solid #D1D5DB)!important}.mb-0-75rem{margin-bottom:.75rem!important}.mt-6px{margin-top:6px!important}.pr-2-4rem{padding-right:2.4rem!important}.p-0-2rem{padding:.2rem!important}.fs-1-35rem{font-size:1.35rem!important}.p-4px-12px{padding:4px 12px!important}.b-111827{background:var(--cc-sf-label-color, #111827)!important}.b-2px-dashed-CBD5E1{border:2px dashed var(--cc-sf-dropzone-border, #CBD5E1)!important}.fs-52px{font-size:52px!important}.p-12px-16px{padding:12px 16px!important}.bb-1px-solid-F3F4F6{border-bottom:1px solid var(--cc-sf-input-disabled-border, #F3F4F6)!important}.b-0F172A{background:var(--mu-carousel-bg, #0F172A)!important}.b-3px-solid-rgba-255-255-255-0-15{border:3px solid rgba(255,255,255,.15)!important}.b-rgba-255-255-255-0-85{background:#ffffffd9!important}.b-rgba-0-0-0-0-55{background:#0000008c!important}.b-rgba-255-255-255-0-45{background:#ffffff73!important}.pb-4px{padding-bottom:4px!important}.b-2px-solid-transparent{border:2px solid transparent!important}.b-E2E8F0{background:var(--mu-thumb-bg, #E2E8F0)!important}.b-1E293B{background:#1e293b!important}.c-EF4444{color:#ef4444!important}.b-rgba-0-0-0-0-5{background:#00000080!important}.br-16px{border-radius:var(--mu-modal-radius, 16px)!important}.p-24px-28px{padding:24px 28px!important}.bb-1px-solid-E5E7EB{border-bottom:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)!important}.fs-1-25rem{font-size:1.25rem!important}.p-48px-24px{padding:48px 24px!important}.b-3px-solid-E2E8F0{border:3px solid #E2E8F0!important}.p-16px-24px{padding:16px 24px!important}.p-28px{padding:28px!important}.b-3px-solid-transparent{border:3px solid transparent!important}.b-rgba-59-130-246-0-12{background:#3b82f61f!important}.p-20px-28px{padding:20px 28px!important}.c-1A56DB{color:var(--loc-add-color, #1A56DB)!important}.b-1px-dashed-D1D5DB{border:1px dashed var(--cc-sf-input-disabled-border, #D1D5DB)!important}.fs-40px{font-size:40px!important}.c-9CA3AF{color:var(--loc-tba-icon-color, #9CA3AF)!important}.form-field{font-family:var(--cc-sf-font-family, \"Poppins\", sans-serif)!important}:host{--cc-sf-input-border: #D1D5DB;--cc-sf-input-bg: #ffffff;--cc-sf-input-radius: 9px;--cc-sf-input-height: 44px;--cc-sf-label-color: #111827;--cc-sf-hint-color: #9CA3AF;--cc-sf-error-border: #EF4444;--cc-sf-error-bg: #FFF5F5;--cc-sf-accent-color: #6366F1;--cc-sf-input-focus-border: #6366F1;--cc-sf-input-hover-border: #A5B4FC;--cc-sf-input-placeholder: #C4C9D4;--cc-sf-input-disabled-bg: #F8F9FB;--cc-sf-input-disabled-border: #E5E7EB;--cc-sf-switch-track-on: #6366F1;--cc-sf-switch-track-off: #D1D5DB;--cc-sf-switch-thumb: #ffffff;--cc-sf-selected-color: #6366F1}.form-row{gap:var(--cc-sf-grid-gap, 16px)}.form-row.horizontal{display:flex;flex-direction:row}.form-row.horizontal>*{flex:1}.form-row:not(.horizontal){flex-direction:column}.form-row.grid-row{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--cc-sf-grid-gap, 16px);align-items:start}@media(max-width:640px){.form-row.grid-row{grid-template-columns:1fr}.form-row.grid-row .row-field{grid-column:span 12!important}}.field-label{font-size:.75rem;font-weight:600;line-height:1;letter-spacing:.04em;text-transform:uppercase;color:#6b7280}.field-input,input.matInput,.mat-mdc-input-element{display:block;width:100%;height:var(--cc-sf-input-height)!important;padding:0 14px!important;font-family:inherit;font-size:.9rem;color:var(--cc-sf-label-color);background-color:var(--cc-sf-input-bg)!important;border:1.5px solid var(--cc-sf-input-border)!important;border-radius:var(--cc-sf-input-radius)!important;box-sizing:border-box;box-shadow:0 1px 2px #0000000a!important;transition:all .2s cubic-bezier(.4,0,.2,1)}.field-input::placeholder,input.matInput::placeholder,.mat-mdc-input-element::placeholder{font-weight:400;font-size:14px;color:var(--cc-sf-input-placeholder)}.field-input{opacity:var(--cc-sf-input-opacity, 1);line-height:var(--cc-sf-input-line-height, 1.5);transition:var(--cc-sf-input-transition, all .2s ease)}.field-input::placeholder{font-weight:var(--cc-sf-placeholder-weight, 400);font-size:var(--cc-sf-placeholder-size, 14px);line-height:var(--cc-sf-placeholder-line-height, 100%);color:var(--cc-sf-input-placeholder)}.field-input:hover:not(:disabled):not([readonly]){border-color:var(--cc-sf-input-hover-border)!important;box-shadow:0 1px 4px #6366f114!important}.field-input:focus{outline:none;border-color:var(--cc-sf-input-focus-border)!important;box-shadow:0 0 0 3px #6366f124,0 1px 4px #6366f11a!important;background-color:#fefeff!important}.field-input:disabled,.field-input[readonly]{background-color:var(--cc-sf-input-disabled-bg)!important;color:#9ca3af!important;cursor:not-allowed;border-color:var(--cc-sf-input-disabled-border)!important;box-shadow:none!important}.field-input.is-invalid{border-color:var(--cc-sf-error-border)!important;background-color:var(--cc-sf-error-bg)!important}.field-input.is-invalid:focus{box-shadow:0 0 0 3px #ef44441f,0 1px 4px #ef44441a!important}.field-input.textarea{resize:vertical;min-height:100px;height:auto;padding:12px 16px!important}input[type=time].time-input{cursor:pointer}input[type=time].time-input::-webkit-calendar-picker-indicator{cursor:pointer;opacity:.7;filter:invert(30%)}input[type=time].time-input::-webkit-calendar-picker-indicator:hover{opacity:1}select.field-input{appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236B7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3E%3C/svg%3E\");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;cursor:pointer}select.field-input:disabled{cursor:not-allowed}.multi-select-wrapper{position:relative}.multi-select-wrapper .multi-select-trigger{min-height:var(--cc-sf-input-height, 44px);height:auto;-webkit-user-select:none;user-select:none}.multi-select-wrapper .multi-select-trigger.ms-open{border-color:var(--cc-sf-input-focus-border, #6366F1)!important;box-shadow:0 0 0 3px #6366f124,0 1px 4px #6366f11a!important}.multi-select-wrapper .multi-select-trigger .multi-select-value{flex:1;color:var(--cc-sf-label-color, #111827);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.multi-select-wrapper .multi-select-trigger .multi-select-placeholder{flex:1;font-weight:400;font-size:14px;color:var(--cc-sf-input-placeholder, #C4C9D4)}.multi-select-wrapper .multi-select-trigger .multi-select-arrow{font-size:20px;width:20px;height:20px;line-height:20px;color:#6b7280;flex-shrink:0}.multi-select-wrapper .multi-select-panel{position:absolute;top:calc(100% + 4px);left:0;right:0;background:#fff;border:1.5px solid var(--cc-sf-input-border, #D1D5DB);border-radius:var(--cc-sf-input-radius, 9px);box-shadow:0 8px 24px #0000001a,0 2px 6px #0000000f;z-index:1050;max-height:240px;overflow-y:auto}.multi-select-wrapper .multi-select-panel::-webkit-scrollbar{width:4px}.multi-select-wrapper .multi-select-panel::-webkit-scrollbar-thumb{background:#d1d5db;border-radius:2px}.multi-select-wrapper .multi-select-option{padding:8px 12px;border-bottom:1px solid #F3F4F6;transition:background .1s}.multi-select-wrapper .multi-select-option:last-of-type{border-bottom:none}.multi-select-wrapper .multi-select-option:hover{background:#f9fafb}.multi-select-wrapper .multi-select-option input[type=checkbox]{flex-shrink:0;cursor:pointer;accent-color:var(--cc-sf-selected-color, #6366F1);width:15px;height:15px}.multi-select-wrapper .multi-select-empty{padding:12px;text-align:center}.multi-select-wrapper.is-invalid .multi-select-trigger{border-color:var(--cc-sf-error-border, #EF4444)!important;background-color:var(--cc-sf-error-bg, #FFF5F5)!important}.char-count-hint{font-style:normal;line-height:100%;letter-spacing:.02em;margin-top:.4rem}.radio-group.layout-column,.checkbox-group.layout-column{display:grid!important;grid-template-columns:repeat(12,1fr);gap:16px;width:100%}.radio-group.layout-row,.checkbox-group.layout-row{flex-direction:column!important;gap:12px;width:100%}.radio-label input,.checkbox-label input{cursor:pointer;accent-color:var(--cc-sf-chip-selected-bg, #F05A28)}.radio-label.card-item,.checkbox-label.card-item{display:flex!important;flex-direction:row-reverse!important;justify-content:space-between!important;align-items:center!important;border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB);border-radius:12px;padding:16px 20px;box-sizing:border-box;transition:all .2s ease;background:var(--cc-sf-input-bg, #ffffff);margin-bottom:0}.radio-label.card-item input,.checkbox-label.card-item input{margin-left:16px}.radio-label.card-item.selected,.checkbox-label.card-item.selected{border-color:var(--cc-sf-selected-color);background-color:#f05a280d}.radio-label.card-item .option-content .label-text,.checkbox-label.card-item .option-content .label-text,.checkbox-single .checkbox-label{font-weight:var(--cc-sf-label-weight, 500)}.chip-label{transition:var(--cc-sf-input-transition, all .2s ease)}.chip-label:hover{background:var(--cc-sf-chip-hover-bg, #F3F4F6)}.chip-label.selected{background:var(--cc-sf-selected-color);color:var(--cc-sf-chip-selected-color, #ffffff);border-color:var(--cc-sf-selected-color)}.switch{width:50px;height:24px;display:inline-block}.switch input{opacity:0;width:0;height:0;position:absolute}.switch input:checked+.slider{background-color:var(--cc-sf-switch-track-on)!important}.switch input:checked+.slider:before{transform:translate(26px)}.switch .slider{inset:0;transition:.4s;background-color:var(--cc-sf-switch-track-off);border-radius:24px}.switch .slider:before{position:absolute;content:\"\";height:18px;width:18px;left:3px;bottom:3px;background-color:var(--cc-sf-switch-thumb);transition:.4s;border-radius:50%}.rating-group .star{transition:var(--cc-sf-input-transition, all .2s ease)}.rating-group .star mat-icon{font-size:var(--cc-sf-star-size, 28px);width:var(--cc-sf-star-size, 28px);height:var(--cc-sf-star-size, 28px);line-height:var(--cc-sf-star-size, 28px);color:var(--cc-sf-star-empty, #D1D5DB);transition:var(--cc-sf-input-transition, all .2s ease)}.rating-group .star.filled mat-icon,.rating-group .star.half mat-icon{color:var(--cc-sf-star-filled, #F59E0B)}.rating-group .star:hover mat-icon{color:var(--cc-sf-star-filled, #F59E0B)}.password-wrapper .password-toggle{right:.625rem;top:50%;transform:translateY(-50%);line-height:1;transition:color var(--cc-sf-input-transition, .2s ease)}.password-wrapper .password-toggle mat-icon.eye-icon{font-size:1.125rem;width:1.125rem;height:1.125rem;line-height:1.125rem}.password-wrapper .password-toggle:hover{color:var(--cc-sf-label-color, #374151)}.password-wrapper .password-toggle:focus{outline:none}.group-section-wrapper .group-label{font-size:var(--cc-sf-section-label-size, 1rem);font-weight:var(--cc-sf-section-label-weight, 600);color:var(--cc-sf-section-label-color, #1F2937);margin:0 0 16px;padding-left:12px;padding-top:2px;padding-bottom:2px;border-left:var(--cc-sf-section-header-accent-width, 4px) solid var(--cc-sf-section-header-accent-color, #3B82F6);line-height:1.4}.group-section-wrapper .group-fields.sf-grid{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--cc-sf-grid-gap, 16px);align-items:start}@media(max-width:640px){.group-section-wrapper .group-fields.sf-grid{grid-template-columns:1fr}.group-section-wrapper .group-fields.sf-grid .sf-col{grid-column:span 12!important}}.group-section-wrapper .group-accordion-instance{border:var(--cc-sf-instance-border, 1px solid #E5E7EB);border-radius:var(--cc-sf-section-border-radius-inner, 8px);margin-bottom:8px;overflow:hidden;transition:border-color .2s ease}.group-section-wrapper .group-accordion-instance:last-of-type{margin-bottom:0}.group-section-wrapper .group-accordion-instance .group-accordion-header{display:flex;justify-content:space-between;align-items:center;padding:10px 14px;background:var(--cc-sf-repeater-accordion-header-bg, #F9FAFB);cursor:pointer;-webkit-user-select:none;user-select:none;transition:background .15s ease}.group-section-wrapper .group-accordion-instance .group-accordion-header:hover{background:var(--cc-sf-repeater-accordion-active-bg, #EFF6FF)}.group-section-wrapper .group-accordion-instance .group-accordion-header .instance-badge{width:22px;height:22px;border-radius:50%;background:var(--cc-sf-repeater-badge-bg, #E5E7EB);color:var(--cc-sf-repeater-badge-color, #374151);font-size:.75rem;font-weight:600;display:flex;align-items:center;justify-content:center;flex-shrink:0}.group-section-wrapper .group-accordion-instance .group-accordion-header .instance-title{font-size:var(--cc-sf-instance-num-size, .8125rem);font-weight:600;color:var(--cc-sf-repeater-accordion-header-color, #1F2937)}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-remove-btn{background:none;border:none;cursor:pointer;color:var(--cc-sf-btn-remove-color, #E53E3E);padding:4px;border-radius:4px;line-height:1;display:flex;align-items:center;transition:background .15s ease}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-remove-btn mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-remove-btn:hover{background:var(--cc-sf-btn-remove-hover-bg, #FED7D7)}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-chevron{font-size:1.25rem;width:1.25rem;height:1.25rem;line-height:1.25rem;color:var(--cc-sf-instance-num-color, #4B5563)}.group-section-wrapper .group-accordion-instance .group-accordion-body{padding:var(--cc-sf-instance-padding, 16px);background:var(--cc-sf-instance-bg, #F9FAFB);border-top:var(--cc-sf-instance-divider, 1px dashed #D1D5DB)}.group-section-wrapper .btn-add-group{display:flex;align-items:center;justify-content:center;gap:6px;width:100%;padding:10px 20px;margin-top:12px;background:var(--cc-sf-btn-add-bg, transparent);color:var(--cc-sf-btn-add-color, #3B82F6);border:var(--cc-sf-btn-add-border, 1px dashed #CBD5E1);border-radius:var(--cc-sf-btn-add-radius, 6px);cursor:pointer;font-family:inherit;font-size:var(--cc-sf-btn-font-size, .875rem);font-weight:var(--cc-sf-btn-font-weight, 600);transition:var(--cc-sf-btn-transition, all .2s ease)}.group-section-wrapper .btn-add-group mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.group-section-wrapper .btn-add-group:hover{background:var(--cc-sf-btn-add-hover-bg, #EFF6FF);border-color:var(--cc-sf-btn-add-hover-border, #BFDBFE)}.group-section-wrapper .group-instance:last-child{margin-bottom:0}.group-section-wrapper.multi-save-active{border:none;box-shadow:none;padding:0;background:transparent}.group-section-wrapper.multi-save-active .multi-save-header .btn-add-multi ::ng-deep button{color:var(--ms-btn-add-color, #3B82F6);font-weight:600;font-size:.875rem;padding:8px 12px}.group-section-wrapper.multi-save-active .multi-save-header .btn-add-multi ::ng-deep button:hover{color:var(--ms-btn-add-hover, #2563EB);background-color:var(--cc-sf-btn-add-hover-bg, #EFF6FF)}.group-section-wrapper.multi-save-active .group-instance.is-card{cursor:pointer;transition:all .2s ease-in-out}.group-section-wrapper.multi-save-active .group-instance.is-card:hover{box-shadow:var(--ms-card-shadow-hover, 0 8px 24px rgba(0, 0, 0, .08));border-color:var(--cc-sf-input-focus-border, #3B82F6)}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-content .card-title{white-space:nowrap;text-overflow:ellipsis}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-content .card-desc{line-height:1.4;display:-webkit-box;-webkit-line-clamp:1;line-clamp:1;-webkit-box-orient:vertical}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view.is-expanded .card-content .card-desc{-webkit-line-clamp:unset;line-clamp:unset}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon{font-size:22px;width:22px;height:22px;color:var(--cc-sf-hint-color, #9CA3AF);transition:color .2s}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon.icon-delete:hover{color:var(--cc-sf-error-border, #DC2626)}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon.icon-edit:hover{color:var(--cc-sf-input-focus-border, #3B82F6)}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon.icon-expand{color:var(--cc-sf-input-disabled-border, #E5E7EB)}.btn-remove{transition:var(--cc-sf-btn-transition, all .2s ease)}.btn-remove mat-icon{font-size:1rem;width:1rem;height:1rem;line-height:1rem}.btn-remove:hover{background:var(--cc-sf-btn-remove-hover-bg, #FED7D7)}.btn-add-group{font-weight:var(--cc-sf-btn-font-weight, 600);transition:var(--cc-sf-btn-transition, all .2s ease)}.btn-add-group mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.btn-add-group:hover{background:var(--cc-sf-btn-add-hover-bg, #EFF6FF);border-color:var(--cc-sf-btn-add-hover-border, #BFDBFE)}.upload-drop-zone{background-color:var(--cc-sf-dropzone-bg, #F8FAFC);border:var(--cc-sf-dropzone-border, 1.5px dashed #CBD5E1);border-radius:var(--cc-sf-dropzone-radius, 12px);transition:background-color .2s ease,border-color .2s ease}.upload-drop-zone:hover{background-color:var(--cc-sf-dropzone-hover-bg, #EFF6FF);border-color:var(--cc-sf-dropzone-hover-border, #93C5FD)}.upload-drop-zone.drag-over{background-color:var(--cc-sf-dropzone-hover-bg, #EFF6FF);border-color:var(--cc-sf-dropzone-over-border, #3B82F6);box-shadow:var(--cc-sf-dropzone-over-shadow, 0 0 0 4px rgba(59, 130, 246, .12))}.upload-drop-zone.is-invalid{border-color:var(--cc-sf-error-border, #DC2626);background-color:var(--cc-sf-error-bg, #FEF2F2)}.upload-icon-wrap .dropzone-icon-pill{width:52px;height:52px;border-radius:50%;background:var(--cc-sf-dropzone-icon-bg, rgba(59, 130, 246, .1))}.upload-icon-wrap mat-icon.upload-cloud-icon{font-size:28px;width:28px;height:28px;line-height:28px;color:var(--cc-sf-accent-color, #3B82F6)}.upload-main-text{color:var(--cc-sf-label-color, #1E293B)}.upload-sub-text{color:var(--cc-sf-hint-color, #64748B)}.upload-link{color:var(--cc-sf-dropzone-link-color, #3B82F6);font-weight:500}.upload-formats{color:var(--cc-sf-dropzone-link-color, #3B82F6)}.upload-size-badge{display:inline-block;padding:2px 8px;border-radius:20px;background:var(--cc-sf-input-disabled-bg, #F3F4F6);color:var(--cc-sf-hint-color, #6B7280);font-weight:500}.uploaded-item{background:var(--cc-sf-uploaded-item-bg, #ffffff);border:var(--cc-sf-uploaded-item-border, 1px solid #E2E8F0);border-radius:var(--cc-sf-uploaded-item-radius, 8px);transition:box-shadow .15s ease}.uploaded-item:hover{box-shadow:0 2px 6px #0000000f}.uploaded-item mat-icon.file-type-icon{font-size:20px;width:20px;height:20px;line-height:20px;flex-shrink:0;color:var(--cc-sf-hint-color, #64748B)}.uploaded-item .file-thumb{width:36px;height:36px;object-fit:cover;flex-shrink:0}.uploaded-item .file-info{min-width:0;gap:2px}.uploaded-item .file-info .file-name{white-space:nowrap;text-overflow:ellipsis}.uploaded-item .file-remove-btn{flex-shrink:0;width:32px;height:32px;background:none;border:none;cursor:pointer;color:var(--cc-sf-uploaded-remove-color, #94A3B8);padding:0;display:flex;align-items:center;justify-content:center;transition:color .15s ease,background .15s ease}.uploaded-item .file-remove-btn mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.uploaded-item .file-remove-btn:hover:not(:disabled){color:var(--cc-sf-uploaded-remove-hover-color, #DC2626);background:var(--cc-sf-uploaded-remove-hover-bg, #FEF2F2)}.uploaded-item .file-remove-btn:disabled{opacity:.4;cursor:not-allowed}.uploaded-item.uploading{background:var(--cc-sf-uploaded-uploading-bg, #F8FAFC);border-color:var(--cc-sf-uploaded-uploading-border, #CBD5E1);opacity:.85}.upload-spinner{width:20px;height:20px;flex-shrink:0;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}@keyframes cc-spin{to{transform:rotate(360deg)}}.uploading-label{font-style:italic}.input-group{align-items:stretch}.input-group .field-input{flex:1;width:auto}.input-prefix+.input-group .field-input{border-top-left-radius:0;border-bottom-left-radius:0}.input-group .field-input:has(+.input-suffix){border-top-right-radius:0;border-bottom-right-radius:0}.input-group .field-input.has-icon-right{padding-right:3rem}.input-group.readonly .field-input{cursor:default}.input-prefix,.input-suffix{display:flex!important;align-items:center;white-space:nowrap;padding:0 14px;background-color:var(--cc-sf-input-disabled-bg);border:1px solid var(--cc-sf-input-border);font-size:.875rem;color:var(--cc-sf-hint-color);-webkit-user-select:none;user-select:none}.input-prefix{border-right:none;border-top-left-radius:var(--cc-sf-input-radius, 8px);border-bottom-left-radius:var(--cc-sf-input-radius, 8px)}.input-suffix{border-left:none;border-top-right-radius:var(--cc-sf-input-radius, 8px);border-bottom-right-radius:var(--cc-sf-input-radius, 8px)}.readonly-icons{right:.875rem;top:50%;transform:translateY(-50%)}.readonly-icons mat-icon.lock-icon{font-size:1rem;width:1rem;height:1rem;line-height:1rem;opacity:.5;color:var(--cc-sf-hint-color, #6B7280)}.date-icon-wrapper{right:.5rem;top:50%;transform:translateY(-50%);pointer-events:auto}.date-icon-wrapper .mat-icon-button{width:32px;height:32px;line-height:32px}.subfields-group-wrapper .subfields-row{transition:all .2s ease}.subfields-group-wrapper .subfields-row.is-invalid .subfield-item ::ng-deep .field-input{border-color:var(--cc-sf-error-border, #DC2626);background-color:var(--cc-sf-error-bg, #FEF2F2)}.subfields-group-wrapper .subfields-row .subfield-item{min-width:0}.subfields-group-wrapper .subfields-row .subfield-item ::ng-deep .field-label{font-size:.75rem!important;margin-bottom:4px!important;font-weight:500!important;color:var(--cc-sf-hint-color, #6B7280)!important}.subfields-group-wrapper .subfields-row .subfield-separator{font-weight:700}.autocomplete-wrapper .ac-input{padding-left:40px!important}.autocomplete-wrapper .ac-search-icon{left:.75rem;width:1.1rem;height:1.1rem;line-height:1.1rem;z-index:1;transition:color var(--cc-sf-input-transition, .2s ease)}.autocomplete-wrapper .ac-clear-btn{right:.6rem;transition:color .15s ease,background .15s ease}.autocomplete-wrapper .ac-clear-btn mat-icon{font-size:1rem;width:1rem;height:1rem;line-height:1rem}.autocomplete-wrapper .ac-clear-btn:hover{color:var(--cc-sf-label-color, #374151);background:var(--cc-sf-input-disabled-bg, #F3F4F6)}.autocomplete-wrapper .ac-clear-btn:focus{outline:none}.autocomplete-wrapper:focus-within .ac-search-icon{color:var(--cc-sf-accent-color, #3B82F6)}.autocomplete-wrapper.is-invalid .ac-input{border-color:var(--cc-sf-error-border)!important;background-color:var(--cc-sf-error-bg)}.autocomplete-wrapper.readonly .ac-input{background-color:var(--cc-sf-input-disabled-bg);color:var(--cc-sf-input-disabled-color, #6B7280);cursor:not-allowed;border-color:var(--cc-sf-input-disabled-border)!important}.ac-no-results{font-style:italic}::ng-deep .mat-mdc-autocomplete-panel,::ng-deep .mat-autocomplete-panel{background:var(--cc-sf-input-bg, #ffffff)!important;border-radius:var(--cc-sf-input-radius, 9px)!important;border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)!important;box-shadow:0 8px 24px #0000001a,0 2px 6px #0000000f!important;padding:4px 0!important;min-width:200px}::ng-deep .mat-mdc-autocomplete-panel mat-option,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option,::ng-deep .mat-mdc-autocomplete-panel .mat-option,::ng-deep .mat-autocomplete-panel mat-option,::ng-deep .mat-autocomplete-panel .mat-mdc-option,::ng-deep .mat-autocomplete-panel .mat-option{background:var(--cc-sf-input-bg, #ffffff)!important;color:var(--cc-sf-label-color, #111827)!important;font-size:.875rem!important;padding:10px 16px!important;min-height:40px!important;line-height:1.4!important;display:flex!important;flex-direction:column!important;align-items:flex-start!important;transition:background var(--cc-sf-input-transition, .2s ease)!important}::ng-deep .mat-mdc-autocomplete-panel mat-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-mdc-autocomplete-panel .mat-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-autocomplete-panel mat-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-autocomplete-panel .mat-mdc-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-autocomplete-panel .mat-option:hover:not(.mat-option-disabled):not([disabled]){background:var(--cc-sf-input-disabled-bg, #F3F4F6)!important}::ng-deep .mat-mdc-autocomplete-panel mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-mdc-autocomplete-panel mat-option.mdc-list-item--selected,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mdc-list-item--selected,::ng-deep .mat-mdc-autocomplete-panel .mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-mdc-autocomplete-panel .mat-option.mdc-list-item--selected,::ng-deep .mat-autocomplete-panel mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-autocomplete-panel mat-option.mdc-list-item--selected,::ng-deep .mat-autocomplete-panel .mat-mdc-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-autocomplete-panel .mat-mdc-option.mdc-list-item--selected,::ng-deep .mat-autocomplete-panel .mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-autocomplete-panel .mat-option.mdc-list-item--selected{background:var(--cc-sf-dropzone-hover-bg, #EFF6FF)!important;color:var(--cc-sf-selected-color, #6366F1)!important;font-weight:600!important}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-option-label,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-option-label,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-option-label,::ng-deep .mat-autocomplete-panel mat-option .ac-option-label,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-option-label,::ng-deep .mat-autocomplete-panel .mat-option .ac-option-label{font-weight:500;color:var(--cc-sf-label-color, #111827);font-size:.875rem;display:block}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-display-fields,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-display-fields,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-display-fields,::ng-deep .mat-autocomplete-panel mat-option .ac-display-fields,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-display-fields,::ng-deep .mat-autocomplete-panel .mat-option .ac-display-fields{align-items:center;line-height:1}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-item,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-item,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-item,::ng-deep .mat-autocomplete-panel mat-option .ac-df-item,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-item,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-item{display:inline-flex;align-items:center;font-size:.72rem;color:var(--cc-sf-hint-color, #6B7280);gap:3px}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-chip,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-chip,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-chip,::ng-deep .mat-autocomplete-panel mat-option .ac-df-chip,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-chip,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-chip{background:var(--cc-sf-input-disabled-bg, #F3F4F6);border-radius:4px;padding:2px 6px}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-text,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-text,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-text,::ng-deep .mat-autocomplete-panel mat-option .ac-df-text,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-text,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-text{color:var(--cc-sf-hint-color, #6B7280)}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-avatar,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-avatar,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-avatar,::ng-deep .mat-autocomplete-panel mat-option .ac-df-avatar,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-avatar,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-avatar{width:24px;height:24px;border-radius:50%;object-fit:cover;border:1px solid var(--cc-sf-input-border, #D1D5DB);vertical-align:middle}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-label,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-label,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-label,::ng-deep .mat-autocomplete-panel mat-option .ac-df-label,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-label,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-label{font-weight:600;color:var(--cc-sf-hint-color, #9CA3AF);margin-right:2px}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-icon,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-icon,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-icon,::ng-deep .mat-autocomplete-panel mat-option .ac-df-icon,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-icon,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-icon{font-size:11px;width:11px;height:11px;line-height:11px;color:var(--cc-sf-hint-color, #9CA3AF);flex-shrink:0}.mu-layout{grid-template-columns:1fr 1fr;gap:32px}@media(max-width:768px){.mu-layout{grid-template-columns:1fr}}.mu-title{font-weight:700;line-height:1.3}.mu-badge{white-space:nowrap;flex-shrink:0}.mu-description{line-height:1.6}.mu-feature-item .mu-check{width:16px;height:16px;line-height:16px;flex-shrink:0}.mu-right{min-height:260px}.mu-right-empty{min-height:250px;max-width:400px;box-shadow:0 2px 10px #0000000d;transition:box-shadow .2s ease}.mu-right-empty:hover{cursor:pointer;box-shadow:0 4px 16px #0000001a}.mu-right-empty .mu-right-empty-icon{width:52px;height:52px;line-height:52px;opacity:.3}.mu-right-empty p{margin:0;font-size:.85rem}.media-add-container{display:inline-block}.media-add-container ::ng-deep button{display:flex;align-items:center;gap:6px}.media-add-container ::ng-deep button .menu-chevron{width:18px;height:18px;line-height:18px;transition:transform .2s ease}.media-dropdown{top:calc(100% + 6px);left:0;z-index:200;min-width:240px;box-shadow:var(--mu-dropdown-shadow, 0 8px 32px rgba(0, 0, 0, .12));animation:mu-fade-in .15s ease}@keyframes mu-fade-in{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}.media-dropdown-item{transition:background .15s ease}.media-dropdown-item:last-child{border-bottom:none}.media-dropdown-item:hover{background:var(--cc-sf-dropzone-hover-bg, #F0F9FF)}.media-drop-icon{width:36px;height:36px;flex-shrink:0}.media-drop-icon mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.media-drop-icon--video{background:var(--mu-icon-video-bg, #FFF0F0);color:var(--mu-icon-video-color, #EF4444)}.media-drop-icon--device{background:var(--mu-icon-device-bg, #EFF6FF);color:var(--mu-icon-device-color, #3B82F6)}.media-drop-icon--library{background:var(--mu-icon-library-bg, #F0FDF4);color:var(--mu-icon-library-color, #22C55E)}.media-drop-text{gap:2px}.youtube-input-panel{animation:mu-fade-in .18s ease}.youtube-panel-label mat-icon{font-size:18px;width:18px;height:18px;line-height:18px;color:var(--mu-icon-video-color, #EF4444)}.youtube-input-row{align-items:stretch}.media-menu-backdrop{position:fixed;inset:0;z-index:100}.media-upload-status{animation:mu-fade-in .2s ease}.media-upload-status .status-icon{width:18px;height:18px;line-height:18px}.media-carousel-main{max-width:400px;height:var(--mu-carousel-height, 250px)}.carousel-viewer{inset:0}.carousel-viewer .carousel-image{object-fit:cover}.carousel-viewer .carousel-spinner{width:36px;height:36px;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}.carousel-nav{top:50%;transform:translateY(-50%);z-index:10;width:40px;height:40px;box-shadow:0 2px 8px #0003;transition:background .2s ease,opacity .2s ease;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.carousel-nav mat-icon{font-size:22px;width:22px;height:22px;line-height:22px;color:#1e293b}.carousel-nav:hover:not(:disabled){background:#fff}.carousel-nav:disabled{opacity:.3;cursor:not-allowed}.carousel-nav--prev{left:12px}.carousel-nav--next{right:12px}.carousel-remove-btn{top:10px;right:10px;z-index:10;width:32px;height:32px;transition:background .2s ease}.carousel-remove-btn mat-icon{font-size:18px;width:18px;height:18px;line-height:18px;color:#fff}.carousel-remove-btn:hover:not(:disabled){background:#dc2626d9}.carousel-remove-btn:disabled{opacity:.4;cursor:not-allowed}.carousel-dots{bottom:10px;left:50%;transform:translate(-50%);z-index:10}.carousel-dot{width:8px;height:8px;transition:background .2s ease,transform .2s ease}.carousel-dot.active{background:#fff;transform:scale(1.3)}.media-thumbnail-strip{max-width:400px;overflow-x:auto}.media-thumbnail-strip::-webkit-scrollbar{height:4px}.media-thumbnail-strip::-webkit-scrollbar-thumb{background:var(--cc-sf-input-disabled-border, #D1D5DB);border-radius:2px}.media-thumb{flex-shrink:0;width:72px;height:52px;transition:border-color .2s ease,transform .15s ease}.media-thumb.active{border-color:var(--mu-thumb-active-border, var(--cc-sf-accent-color, #3B82F6));transform:scale(1.04)}.media-thumb:hover:not(.active){border-color:var(--cc-sf-input-hover-border, #9CA3AF)}.media-thumb .thumb-img{object-fit:cover}.media-thumb .thumb-yt-placeholder mat-icon{font-size:28px;width:28px;height:28px;line-height:28px}.media-thumb .thumb-uploading .thumb-spinner{width:20px;height:20px;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}.media-library-portal-host{position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center;visibility:hidden;opacity:0;pointer-events:none;transition:opacity .2s ease,visibility .2s ease}.media-library-portal-host.is-open{visibility:visible;opacity:1;pointer-events:auto}.media-library-portal-host.is-open .media-library-modal{transform:scale(1) translateY(0)}.media-library-overlay{position:absolute;inset:0;background:#00000080;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.media-library-modal{position:relative;z-index:10000;width:90vw;max-width:900px;max-height:90vh;background:#fff;border-radius:16px;box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a;transform:scale(.95) translateY(10px);transition:transform .3s cubic-bezier(.175,.885,.32,1.275)}.library-modal-header{flex-shrink:0}.library-modal-title{font-weight:700}.library-modal-subtitle{line-height:1.5;max-width:600px}.library-close-btn{width:32px;height:32px;transition:background .15s ease,color .15s ease}.library-close-btn mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.library-close-btn:hover{background:var(--cc-sf-input-disabled-bg, #F3F4F6);color:var(--cc-sf-label-color, #374151)}.library-loading mat-icon,.library-empty mat-icon{font-size:40px;width:40px;height:40px;line-height:40px;opacity:.5}.lib-spinner{width:36px;height:36px;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}.library-error{flex-shrink:0}.library-error mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.library-grid{grid-template-columns:repeat(5,1fr);max-height:50vh;overflow-y:auto}.library-grid::-webkit-scrollbar{width:8px}.library-grid::-webkit-scrollbar-track{background:#f1f1f1}.library-grid::-webkit-scrollbar-thumb{background:#c1c1c1;border-radius:10px;border:2px solid #F1F1F1}.library-grid-item{aspect-ratio:1/1;transition:all .3s cubic-bezier(.4,0,.2,1);box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.library-grid-item:hover{transform:translateY(-4px) scale(1.02);box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a}.library-grid-item.selected{border-color:var(--cc-sf-accent-color, #3B82F6)}.library-grid-item.selected .library-grid-img{opacity:.7}.library-grid-item:hover .library-overlay-hover{opacity:1}.library-grid-img{object-fit:cover}.library-overlay-hover{inset:0;opacity:0;transition:opacity .15s ease}.library-check{top:6px;right:6px;width:22px;height:22px;box-shadow:0 1px 4px #00000026}.library-check mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}.library-modal-footer{flex-shrink:0}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-primary{background-color:var(--cc-sf-accent-color, #3B82F6)!important;border-color:var(--cc-sf-accent-color, #3B82F6)!important;color:#fff!important;font-weight:600;padding-left:32px;padding-right:32px}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-primary:hover{background-color:var(--cc-sf-btn-primary-hover-bg, #2563EB)!important}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-primary:disabled{background-color:#93c5fd!important;cursor:not-allowed}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-outline{font-weight:600;padding-left:24px;padding-right:24px;border-color:#d1d5db;color:#374151}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-outline:hover{background-color:#f9fafb}.location-subtitle{line-height:1.5}.loc-tab-btn ::ng-deep button{width:100%}.loc-tab-btn ::ng-deep button:not(.cc-btn-warning){background-color:var(--cc-sf-input-bg, #ffffff)!important;color:var(--cc-sf-label-color, #000000)!important;border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)}.loc-tab-btn ::ng-deep button:not(.cc-btn-warning):hover{background-color:var(--cc-sf-input-disabled-bg, #F3F4F6)!important}.loc-venue-item{transition:box-shadow .15s ease,border-color .15s ease}.loc-venue-item:hover{box-shadow:0 2px 8px #0000000f;border-color:var(--cc-sf-input-hover-border, #9CA3AF)}.loc-venue-search-icon{width:18px;height:18px;line-height:18px;flex-shrink:0}.loc-venue-text{white-space:nowrap;text-overflow:ellipsis}.loc-action-btn{transition:background .15s ease,color .15s ease;flex-shrink:0}.loc-action-btn mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}.loc-action-btn.loc-delete-btn{color:var(--loc-delete-color, #E53E3E)}.loc-action-btn.loc-delete-btn:hover{background:var(--cc-sf-error-bg, #FEF2F2)}.loc-action-btn.loc-edit-btn{color:var(--cc-sf-hint-color, #9CA3AF)}.loc-action-btn.loc-edit-btn:hover{color:var(--cc-sf-input-focus-border, #3B82F6);background:var(--cc-sf-dropzone-hover-bg, #EFF6FF)}.loc-search-icon{left:.75rem;width:1.1rem;height:1.1rem;line-height:1.1rem;z-index:1}.loc-suggestions-panel{top:calc(100% + 4px);left:0;right:0;z-index:300;box-shadow:0 8px 24px #0000001a;animation:mu-fade-in .15s ease;max-height:260px;overflow-y:auto}.loc-suggestion-item{transition:background .12s ease}.loc-suggestion-item:hover,.loc-suggestion-item:focus{background:var(--loc-suggestion-hover-bg, #F0F9FF)}.loc-suggestion-item:not(:last-child){border-bottom:1px solid var(--cc-sf-input-disabled-border, #F3F4F6)}.loc-suggestion-icon{width:18px;height:18px;line-height:18px;flex-shrink:0}.loc-suggestion-text{white-space:nowrap;text-overflow:ellipsis}.loc-add-btn{transition:opacity .15s ease}.loc-add-btn mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.loc-add-btn:hover{opacity:.8}.loc-map-container{box-shadow:0 2px 10px #0000000f}.loc-tba-panel{min-height:120px}.loc-tba-icon{width:40px;height:40px;line-height:40px;opacity:.6}.loc-tba-text{line-height:1.6;max-width:360px}.radio-label{display:flex!important}.radio-label .option-content{padding-left:10px}\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$3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.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: i1$3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$3.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$3.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$3.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$3.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i5.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "component", type: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i7.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i7.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i7.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "directive", type: i8.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: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "directive", type: i9.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "component", type: ButtonComponent, selector: "lib-button", inputs: ["variant", "type", "disabled", "width", "height", "borderRadius", "fontSize", "fontWeight", "backgroundColor", "color", "border", "icon", "labels"] }, { kind: "component", type: i11.QuillEditorComponent, selector: "quill-editor" }, { kind: "component", type: FormFieldComponent, selector: "lib-form-field", inputs: ["config", "controller", "formGroup", "allowMulti"] }, { kind: "pipe", type: TrustedUrlPipe, name: "trustedUrl" }] });
|
|
2532
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: FormFieldComponent, isStandalone: false, selector: "lib-form-field", inputs: { config: "config", controller: "controller", formGroup: "formGroup", allowMulti: "allowMulti" }, host: { listeners: { "document:click": "onDocumentClick()", "document:keydown.escape": "onEscapeKey()" } }, viewQueries: [{ propertyName: "mediaDeviceInput", first: true, predicate: ["mediaDeviceInput"], descendants: true }, { propertyName: "libraryModalRef", first: true, predicate: ["libraryModal"], descendants: true }], ngImport: i0, template: "<div class=\"form-field mb-16px\" *ngIf=\"isVisible\" [class.has-error]=\"errorMessage\">\n\n <!-- \u2550\u2550 ROW Layout \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isRow\" class=\"form-row grid-row\">\n <ng-container *ngFor=\"let child of config.children\">\n <div class=\"row-field\" [style.gridColumn]=\"'span ' + getChildColSpan(child)\" *ngIf=\"child.isEnabled !== false\">\n <lib-form-field [config]=\"child\" [controller]=\"controller\" [formGroup]=\"formGroup\" [allowMulti]=\"allowMulti\">\n </lib-form-field>\n </div>\n </ng-container>\n </div>\n\n <!-- \u2550\u2550 GROUP \u2014 allowMulti (repeater) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isGroup && config.sectionConfig?.allowMulti\"\n class=\"group-section-wrapper mb-20px\"\n [class.multi-save-active]=\"config.sectionConfig?.multiSaveConfig?.active\">\n\n <!-- Multi-Save: header row with label + top-right Add button -->\n <div class=\"multi-save-header d-flex justify-content-between align-items-center mb-24\"\n *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\n <h3 class=\"group-label\" *ngIf=\"config.sectionConfig?.label\">{{ config.sectionConfig!.label }}</h3>\n <lib-button [variant]=\"'outline'\" [icon]=\"{type: 'material', value: 'add'}\" (click)=\"addGroupInstance()\"\n class=\"btn-add-multi\">\n {{ addMultiLabel }}\n </lib-button>\n </div>\n\n <!-- Standard heading (non-multiSave) -->\n <h3 class=\"group-label\"\n *ngIf=\"config.sectionConfig?.label && !config.sectionConfig?.multiSaveConfig?.active\">{{\n config.sectionConfig!.label }}</h3>\n\n <!-- \u2500\u2500 Standard (non-multiSave) repeater: accordion instances \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <ng-container *ngIf=\"!config.sectionConfig?.multiSaveConfig?.active\">\n <div *ngFor=\"let instance of instanceList; trackBy: trackByInstanceId; let i = index\"\n class=\"group-accordion-instance\"\n [class.is-expanded]=\"isGroupExpanded(i)\">\n\n <!-- Accordion header -->\n <div class=\"group-accordion-header\" (click)=\"toggleGroupAccordion(i)\"\n role=\"button\" [attr.aria-expanded]=\"isGroupExpanded(i)\">\n <div class=\"accordion-header-left d-flex align-items-center gap-10\">\n <span class=\"instance-badge\">{{ i + 1 }}</span>\n <span class=\"instance-title\">{{ config.sectionConfig!.label }} #{{ i + 1 }}</span>\n </div>\n <div class=\"accordion-header-right d-flex align-items-center gap-6\">\n <button type=\"button\" class=\"accordion-remove-btn\"\n *ngIf=\"instanceList.length > 1\"\n (click)=\"$event.stopPropagation(); removeGroupInstance(i)\"\n aria-label=\"Remove\">\n <mat-icon>delete_outline</mat-icon>\n </button>\n <mat-icon class=\"accordion-chevron\">\n {{ isGroupExpanded(i) ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}\n </mat-icon>\n </div>\n </div>\n\n <!-- Accordion body (always mounted so form controls survive collapse) -->\n <div class=\"group-accordion-body\" [hidden]=\"!isGroupExpanded(i)\">\n <div class=\"group-fields sf-grid\">\n <ng-container *ngFor=\"let field of config.sectionConfig!.children\">\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\"\n *ngIf=\"field.isEnabled !== false\">\n <lib-form-field [config]=\"field\" [controller]=\"instance.rowController\" [formGroup]=\"instance.fg\"\n [allowMulti]=\"false\">\n </lib-form-field>\n </div>\n </ng-container>\n </div>\n </div>\n </div>\n\n <!-- Full-width dashed Add button -->\n <button type=\"button\" class=\"btn-add-group\" (click)=\"addGroupInstance()\">\n <mat-icon>add</mat-icon> {{ addLabel }} {{ config.sectionConfig!.label }}\n </button>\n </ng-container>\n\n <!-- \u2500\u2500 MultiSave: card instances \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <ng-container *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\n <div *ngFor=\"let instance of instanceList; trackBy: trackByInstanceId; let i = index\"\n class=\"group-instance position-relative mb-16 overflow-hidden\"\n [class.is-editing]=\"instance.isEditing\"\n [class.is-card]=\"!instance.isEditing\">\n\n <!-- Edit / new form view -->\n <div [hidden]=\"!instance.isEditing\">\n <div class=\"group-fields sf-grid\">\n <ng-container *ngFor=\"let field of config.sectionConfig!.children\">\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\"\n *ngIf=\"field.isEnabled !== false\">\n <lib-form-field [config]=\"field\" [controller]=\"instance.rowController\" [formGroup]=\"instance.fg\"\n [allowMulti]=\"false\">\n </lib-form-field>\n </div>\n </ng-container>\n </div>\n\n <!-- Save / Cancel -->\n <div class=\"group-footer d-flex justify-content-end align-items-center gap-16 p-0-24\"\n *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"multiSaveError\">{{ multiSaveError }}</span>\n <div class=\"footer-actions d-flex gap-12\">\n <lib-button [variant]=\"'outline'\" (click)=\"cancelGroupInstance(i)\">Cancel</lib-button>\n <lib-button [variant]=\"'primary'\" (click)=\"saveGroupInstance(i)\">Save</lib-button>\n </div>\n </div>\n </div>\n\n <!-- Card view (saved state) -->\n <ng-container *ngIf=\"!instance.isEditing\">\n <div class=\"card-view d-flex justify-content-between align-items-center p-18px-24px\"\n [class.is-expanded]=\"instance.isExpanded\">\n <div class=\"card-content flex-1 d-flex flex-column gap-4 overflow-hidden\">\n <span class=\"card-title font-weight-600 overflow-hidden fs-1rem c-111827\">{{\n instance.fg.get(config.sectionConfig!.multiSaveConfig!.summaryField || '')?.value\n || '\u2014' }}</span>\n </div>\n <div class=\"card-actions d-flex align-items-center gap-16 ml-20\">\n <mat-icon class=\"icon-delete\" (click)=\"removeGroupInstance(i, true)\">delete_outline</mat-icon>\n <mat-icon class=\"icon-edit\" (click)=\"editGroupInstance(i)\">edit_outline</mat-icon>\n <mat-icon class=\"icon-expand\" (click)=\"toggleExpandGroupInstance(i)\">\n {{ instance.isExpanded ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}\n </mat-icon>\n </div>\n </div>\n </ng-container>\n </div>\n </ng-container>\n </div>\n\n <!-- \u2550\u2550 GROUP \u2014 single (non-repeater) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isGroup && config.sectionConfig && !config.sectionConfig.allowMulti\"\n class=\"group-section-wrapper mb-20px\">\n <h3 class=\"group-label\" *ngIf=\"config.sectionConfig.label\">{{ config.sectionConfig.label }}</h3>\n <div class=\"group-fields sf-grid\">\n <ng-container *ngFor=\"let field of config.sectionConfig.children\">\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\" *ngIf=\"field.isEnabled !== false\">\n <lib-form-field [config]=\"field\" [controller]=\"controller\" [formGroup]=\"groupFormGroup\" [allowMulti]=\"false\">\n </lib-form-field>\n </div>\n </ng-container>\n </div>\n </div>\n\n\n <!-- \u2550\u2550 Text Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isTextField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <textarea *ngIf=\"config.subType === 'LONG'\" class=\"field-input textarea\" [placeholder]=\"config.placeholder || ''\"\n [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\" rows=\"4\">\n </textarea>\n\n <!-- Password input with show/hide toggle -->\n <div *ngIf=\"config.subType === 'PASSWORD'\" class=\"password-wrapper position-relative d-flex align-items-center\">\n <input [type]=\"showPassword ? 'text' : 'password'\" class=\"field-input password-input\"\n [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\n <button type=\"button\"\n class=\"password-toggle position-absolute cursor-pointer d-flex align-items-center justify-content-center b-none border-none c-6B7280 p-0-25rem\"\n (click)=\"showPassword = !showPassword\" tabindex=\"-1\"\n [attr.aria-label]=\"showPassword ? 'Hide password' : 'Show password'\">\n <mat-icon class=\"eye-icon\">{{ showPassword ? 'visibility' : 'visibility_off' }}</mat-icon>\n </button>\n </div>\n\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\n <span class=\"input-prefix br-none\" *ngIf=\"config.prefix\">{{ config.prefix }}</span>\n\n <input *ngIf=\"config.subType !== 'LONG' && config.subType !== 'PASSWORD'\"\n [type]=\"config.subType === 'EMAIL' ? 'email' : config.subType === 'PHONE' ? 'tel' : 'text'\" class=\"field-input\"\n [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\"\n [readonly]=\"config.readonly\">\n\n <span class=\"input-suffix d-flex align-items-center font-weight-500\" *ngIf=\"config.suffix\">{{ config.suffix\n }}</span>\n\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\n <mat-icon class=\"lock-icon\">lock</mat-icon>\n </div>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n <div class=\"char-count-hint font-poppins font-weight-400 text-14 text-right c-6B7280\" *ngIf=\"showCharCount\">\n {{ remainingCharacters }} characters remaining\n </div>\n </div>\n\n <!-- \u2550\u2550 Number Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isNumberField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\n <span class=\"input-prefix br-none\" *ngIf=\"config.prefix\">{{ config.prefix }}</span>\n\n <input type=\"number\" class=\"field-input\" [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\"\n [min]=\"config.numberConfig?.min ?? null\" [max]=\"config.numberConfig?.max ?? null\"\n [step]=\"config.numberConfig?.step || 1\" [class.is-invalid]=\"errorMessage\" [readonly]=\"config.readonly\">\n\n <span class=\"input-suffix d-flex align-items-center font-weight-500\" *ngIf=\"config.suffix\">{{ config.suffix\n }}</span>\n\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\n <mat-icon class=\"lock-icon\">lock</mat-icon>\n </div>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Date Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isDateField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\n <input matInput [matDatepicker]=\"datePicker\" class=\"field-input date-input has-icon-right\"\n [formControlName]=\"config.name!\" [min]=\"dynamicMinDate || config.dateConfig?.minDate\" [max]=\"config.dateConfig?.maxDate\"\n [class.is-invalid]=\"errorMessage\" [placeholder]=\"config.placeholder || ''\"\n [readonly]=\"config.readonly || config.dateConfig?.inputReadonly\"\n (click)=\"!config.readonly && datePicker.open()\">\n <div class=\"date-icon-wrapper position-absolute d-flex align-items-center justify-content-center\"\n *ngIf=\"!config.readonly\">\n <mat-datepicker-toggle matSuffix [for]=\"datePicker\"></mat-datepicker-toggle>\n </div>\n <mat-datepicker #datePicker></mat-datepicker>\n\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\n <mat-icon class=\"lock-icon\">lock</mat-icon>\n </div>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Time Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isTimeField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\n <input type=\"time\" class=\"field-input time-input\" [formControlName]=\"config.name!\"\n [class.is-invalid]=\"errorMessage\" [readonly]=\"!!config.readonly\">\n\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\n <mat-icon class=\"lock-icon\">lock</mat-icon>\n </div>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Autocomplete \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isAutocomplete\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <!-- Hidden real control (stores the code value) -->\n <input type=\"hidden\" [formControlName]=\"config.name!\">\n\n <div class=\"autocomplete-wrapper position-relative d-flex align-items-center w-100\"\n [class.is-invalid]=\"errorMessage\" [class.readonly]=\"config.readonly\">\n <!-- Search icon -->\n <mat-icon class=\"ac-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">search</mat-icon>\n\n <input class=\"field-input ac-input\" [formControl]=\"autocompleteInputCtrl\" [matAutocomplete]=\"auto\"\n [placeholder]=\"config.placeholder || 'Search\u2026'\" [readonly]=\"!!config.readonly\" [class.is-invalid]=\"errorMessage\"\n (blur)=\"onAutocompleteClear()\" autocomplete=\"off\">\n\n <!-- Clear button -->\n <button type=\"button\"\n class=\"ac-clear-btn position-absolute d-flex align-items-center justify-content-center cursor-pointer rounded-50 b-none border-none c-9CA3AF p-0-2rem\"\n *ngIf=\"autocompleteInputCtrl.value && !config.readonly\"\n (click)=\"autocompleteInputCtrl.setValue(''); updateValue(null)\" tabindex=\"-1\" aria-label=\"Clear\">\n <mat-icon>close</mat-icon>\n </button>\n\n <mat-autocomplete #auto=\"matAutocomplete\" [panelWidth]=\"'auto'\">\n <mat-option *ngFor=\"let option of filteredOptions\" [value]=\"option.label\"\n (onSelectionChange)=\"onAutocompleteSelected(option)\">\n <span class=\"ac-option-label\">{{ option.label }}</span>\n\n <!-- Dynamic display fields (image / email / phone / text) -->\n <div class=\"ac-display-fields d-flex flex-wrap gap-6 mt-2\" *ngIf=\"option['displayMeta']?.length\">\n <ng-container *ngFor=\"let field of option['displayMeta']\">\n\n <!-- Image avatar -->\n <span *ngIf=\"field.type === 'image' && field.value\" class=\"ac-df-item ac-df-image\">\n <img [src]=\"field.value\" [alt]=\"field.label || 'image'\" class=\"ac-df-avatar\">\n </span>\n\n <!-- Email -->\n <span *ngIf=\"field.type === 'email' && field.value\" class=\"ac-df-item ac-df-chip\">\n <mat-icon class=\"ac-df-icon\">mail_outline</mat-icon>\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\n {{ field.value }}\n </span>\n\n <!-- Phone -->\n <span *ngIf=\"field.type === 'phone' && field.value\" class=\"ac-df-item ac-df-chip\" [class]=\"field.className\">\n <mat-icon class=\"ac-df-icon\">phone</mat-icon>\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\n {{ field.value }}\n </span>\n\n <!-- Custom / Icon-based / Generic Text -->\n <span *ngIf=\"field.type !== 'image' && field.type !== 'email' && field.type !== 'phone' && field.value\" \n class=\"ac-df-item\" [class.ac-df-chip]=\"!!field.icon\" [class]=\"field.className\">\n <mat-icon class=\"ac-df-icon\" *ngIf=\"field.icon\">{{ field.icon }}</mat-icon>\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\n {{ field.value }}\n </span>\n\n </ng-container>\n </div>\n </mat-option>\n <mat-option *ngIf=\"filteredOptions.length === 0\" disabled class=\"ac-no-results fs-0-8125rem c-6B7280\">\n No results found\n </mat-option>\n </mat-autocomplete>\n\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\n <mat-icon class=\"lock-icon\">lock</mat-icon>\n </div>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Dropdown \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isDropdown\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <select *ngIf=\"config.subType === 'SINGLE'\" class=\"field-input\" [formControlName]=\"config.name!\"\n [class.is-invalid]=\"errorMessage\">\n <option [ngValue]=\"null\" disabled selected>{{ config.placeholder || 'Select' }}</option>\n <option *ngFor=\"let option of localOptionList\" [value]=\"option.code\">\n {{ option.label }}\n </option>\n </select>\n\n <!-- MULTIPLE SELECT: custom panel with checkboxes -->\n <div *ngIf=\"config.subType === 'MULTIPLE'\" class=\"multi-select-wrapper\"\n [class.is-invalid]=\"errorMessage\">\n\n <div class=\"field-input multi-select-trigger d-flex align-items-center justify-content-between cursor-pointer\"\n [class.ms-open]=\"isMultiDropdownOpen\"\n (click)=\"toggleMultiDropdown($event)\">\n <span *ngIf=\"multiSelectedCount > 0\" class=\"multi-select-value fs-0-9rem\">\n {{ multiSelectedCount }} selected\n </span>\n <span *ngIf=\"multiSelectedCount === 0\" class=\"multi-select-placeholder\">\n {{ config.placeholder || selectPlaceholderLabel }}\n </span>\n <mat-icon class=\"multi-select-arrow\">\n {{ isMultiDropdownOpen ? expandLessLabel : expandMoreLabel }}\n </mat-icon>\n </div>\n\n <div class=\"multi-select-panel\" *ngIf=\"isMultiDropdownOpen\"\n (click)=\"$event.stopPropagation()\">\n <label *ngFor=\"let option of localOptionList\"\n class=\"multi-select-option d-flex align-items-center gap-8 cursor-pointer\">\n <input type=\"checkbox\"\n [checked]=\"isChecked(option.code)\"\n [disabled]=\"!!config.disabled\"\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\">\n <span class=\"fs-0-875rem c-111827\">{{ option.label }}</span>\n </label>\n <div *ngIf=\"!localOptionList?.length\"\n class=\"multi-select-empty fs-0-875rem c-6B7280\">\n {{ noOptionsAvailableLabel }}\n </div>\n </div>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Radio \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isRadio\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"radio-group\" [class.is-invalid]=\"errorMessage\"\n [class]=\"config.optionConfig?.layout ? 'layout-' + config.optionConfig!.layout.toLowerCase() : ''\">\n <label *ngFor=\"let option of localOptionList\" class=\"radio-label\"\n [class.card-item]=\"config.subType === 'CARD'\"\n [class.selected]=\"formGroup.get(config.name!)?.value === option.code\"\n [style.gridColumn]=\"config.optionConfig?.layout?.toUpperCase() === 'COLUMN' ? 'span ' + getOptionColSpan(option) : null\">\n <input type=\"radio\" [formControlName]=\"config.name!\" [value]=\"option.code\">\n <div class=\"option-content d-flex flex-column gap-4 flex-1 text-left\">\n <span class=\"label-text text-16 c-1F2937\">{{ option.label }}</span>\n <span class=\"option-hint text-13 color-gray\" *ngIf=\"option.hint\">{{ option.hint }}</span>\n </div>\n </label>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Checkbox \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isCheckbox\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label && config.subType === 'LIST'\"\n class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div *ngIf=\"config.subType === 'BOOL'\" class=\"checkbox-single\">\n <label class=\"checkbox-label d-flex align-items-center gap-8 cursor-pointer fs-0-875rem c-111827\">\n <input type=\"checkbox\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\n <span>{{ config.label }}</span>\n </label>\n </div>\n\n <div *ngIf=\"config.subType === 'LIST' || config.subType === 'CARD'\" class=\"checkbox-group d-flex flex-column gap-8\"\n [class.is-invalid]=\"errorMessage\"\n [class]=\"config.optionConfig?.layout ? 'layout-' + config.optionConfig!.layout.toLowerCase() : ''\">\n <label *ngFor=\"let option of localOptionList\"\n class=\"checkbox-label d-flex align-items-center gap-8 cursor-pointer fs-0-875rem c-111827\"\n [class.card-item]=\"config.subType === 'CARD'\" [class.selected]=\"isChecked(option.code)\"\n [style.gridColumn]=\"config.optionConfig?.layout?.toUpperCase() === 'COLUMN' ? 'span ' + getOptionColSpan(option) : null\">\n <input type=\"checkbox\" [checked]=\"isChecked(option.code)\" [disabled]=\"!!config.disabled\"\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\">\n <div class=\"option-content d-flex flex-column gap-4 flex-1 text-left\">\n <span class=\"label-text text-16 c-1F2937\">{{ option.label }}</span>\n <span class=\"option-hint text-13 color-gray\" *ngIf=\"option.hint\">{{ option.hint }}</span>\n </div>\n </label>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Chip \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isChip\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"chip-group d-flex flex-wrap gap-8\" [class.is-invalid]=\"errorMessage\">\n <label *ngFor=\"let option of localOptionList\"\n class=\"chip-label cursor-pointer p-6px-14px b-ffffff c-374151 b-1px-solid-D1D5DB br-20px fs-0-875rem\"\n [class.selected]=\"isChecked(option.code)\">\n <input type=\"checkbox\" [checked]=\"isChecked(option.code)\" [disabled]=\"!!config.disabled\"\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\" hidden>\n <span>{{ option.label }}</span>\n </label>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Switch \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isSwitch\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label class=\"switch-container d-flex justify-content-between align-items-center cursor-pointer\">\n <span class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">{{ config.label }}</span>\n <div class=\"switch position-relative\">\n <input type=\"checkbox\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\n <span class=\"slider position-absolute cursor-pointer\"></span>\n </div>\n </label>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Rich Text \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isRichText\" class=\"field-wrapper component-rich-text d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"rich-text-container\" [class.is-invalid]=\"errorMessage\">\n <quill-editor [formControlName]=\"config.name!\" class=\"rich-text-editor d-block w-100\"\n [placeholder]=\"config.richTextConfig?.placeholder || config.placeholder || ''\"\n [styles]=\"{height: config.richTextConfig?.height || '200px'}\">\n </quill-editor>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n <div class=\"char-count-hint font-poppins font-weight-400 text-14 text-right c-6B7280\" *ngIf=\"showCharCount\">\n {{ remainingCharacters }} characters remaining\n </div>\n </div>\n\n <!-- \u2550\u2550 Rating \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isRating\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"rating-group d-flex gap-4\" [class.is-invalid]=\"errorMessage\">\n <span *ngFor=\"let star of getStarArray()\" class=\"star d-inline-flex align-items-center cursor-pointer\"\n [class.filled]=\"isStarFilled(star)\" [class.half]=\"isStarHalf(star)\" (click)=\"onRatingChange(star, $event)\">\n <mat-icon>{{ isStarFilled(star) || isStarHalf(star) ? 'star' : 'star_border' }}</mat-icon>\n </span>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Generated Field (read-only) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isGenerated\" class=\"field-wrapper d-flex flex-column gap-6\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">{{ config.label\n }}</label>\n <div class=\"generated-value fs-0-875rem p-0-625rem-0-875rem b-F3F4F6 b-1px-solid-E5E7EB br-8px c-6B7280\">{{ value ||\n '-' }}</div>\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint\">{{ config.hint }}</span>\n </div>\n\n <!-- \u2550\u2550 File Upload \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isFileUpload\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <!-- Drop Zone -->\n <div\n class=\"upload-drop-zone d-flex flex-column align-items-center justify-content-center gap-8 cursor-pointer text-center p-32px-24px us-none\"\n [class.drag-over]=\"isDragOver\" [class.has-files]=\"value?.length\" [class.is-invalid]=\"errorMessage\"\n (dragover)=\"onDragOver($event)\" (dragleave)=\"onDragLeave($event)\" (drop)=\"onFileDrop($event)\"\n (click)=\"fileInput.click()\">\n\n <!-- Icon with accent-colour pill background -->\n <div class=\"upload-icon-wrap mb-4\">\n <div class=\"dropzone-icon-pill d-flex align-items-center justify-content-center\">\n <mat-icon class=\"upload-cloud-icon\">cloud_upload</mat-icon>\n </div>\n </div>\n\n <p class=\"upload-main-text font-weight-600 m-0 fs-0-9rem\">Drag & drop files here</p>\n <p class=\"upload-sub-text m-0 fs-0-8rem c-64748B\">or <span class=\"upload-link\">Browse files</span></p>\n <p class=\"upload-hint-text m-0 fs-0-78rem c-64748B\" *ngIf=\"config.attachmentConfig?.acceptLabel\">\n Supported: <span class=\"upload-formats font-weight-500\">{{ config.attachmentConfig!.acceptLabel }}</span>\n </p>\n <p class=\"upload-hint-text m-0 fs-0-78rem c-64748B\" *ngIf=\"!config.attachmentConfig?.acceptLabel && config.hint\">\n {{ config.hint }}\n </p>\n <span class=\"upload-size-badge fs-0-72rem\" *ngIf=\"config.attachmentConfig?.maxSizeMB\">\n Max {{ config.attachmentConfig!.maxSizeMB }}MB\n </span>\n\n <!-- Hidden native file input -->\n <input #fileInput type=\"file\" hidden [attr.multiple]=\"config.attachmentConfig?.multiple ? true : null\"\n [attr.accept]=\"config.attachmentConfig?.accept || null\" (change)=\"onFileSelected($event)\">\n </div>\n\n <!-- Uploaded file list -->\n <div class=\"uploaded-list d-flex flex-column gap-8 mt-10\" *ngIf=\"value?.length\">\n <div *ngFor=\"let f of value; let i = index\"\n class=\"uploaded-item d-flex align-items-center gap-10 p-10px-14px br-8px\"\n [class.uploading]=\"f.isUploading\">\n\n <!-- Uploading spinner -->\n <ng-container *ngIf=\"f.isUploading; else fileReady\">\n <div class=\"upload-spinner rounded-50 b-2px-solid-E2E8F0\"></div>\n <div class=\"file-info flex-1 d-flex flex-column\">\n <span class=\"file-name font-weight-500 overflow-hidden fs-0-85rem\" [title]=\"f.name\">{{ f.name }}</span>\n <span class=\"file-size uploading-label fs-0-72rem\">Uploading...</span>\n </div>\n </ng-container>\n\n <!-- Normal state once upload is done -->\n <ng-template #fileReady>\n <mat-icon class=\"file-type-icon\">{{ getFileIcon(f.type) }}</mat-icon>\n <img *ngIf=\"f.type?.startsWith('image') && f.dataUrl\" [src]=\"f.dataUrl\" class=\"file-thumb rounded-4\"\n alt=\"preview\">\n <div class=\"file-info flex-1 d-flex flex-column\">\n <span class=\"file-name font-weight-500 overflow-hidden fs-0-85rem\" [title]=\"f.name\">{{ f.name }}</span>\n <span class=\"file-size fs-0-72rem\">{{ formatFileSize(f.size) }}</span>\n </div>\n </ng-template>\n\n <!-- Compact icon-only remove button -->\n <button type=\"button\" class=\"file-remove-btn d-flex align-items-center justify-content-center rounded-50\"\n [disabled]=\"f.isUploading\" (click)=\"!f.isUploading && removeUploadedFile(i)\"\n aria-label=\"Remove file\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n\n <!-- Validation / file errors -->\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"fileUploadError\">{{ fileUploadError }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage && !fileUploadError\">{{ errorMessage }}</span>\n <span class=\"field-hint c-6B7280 fs-0-75rem\"\n *ngIf=\"config.hint && !errorMessage && !fileUploadError && !config.attachmentConfig?.acceptLabel\">\n {{ config.hint }}\n </span>\n </div>\n\n <!-- \u2550\u2550 Media Upload (Type 2) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isMediaUpload\" class=\"field-wrapper media-upload-wrapper d-flex flex-column gap-6 p-0 border-none b-none\"\n [formGroup]=\"formGroup\">\n\n <!-- Hidden file input lives outside *ngIf \u2014 triggered via ViewChild -->\n <input #mediaDeviceInput type=\"file\" hidden multiple accept=\"image/*\" (change)=\"onMediaFileSelected($event)\">\n\n <!-- Two-column layout -->\n <div class=\"mu-layout d-grid align-items-start\">\n\n <!-- \u2500\u2500 LEFT PANEL \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"mu-left d-flex flex-column gap-20\">\n\n <!-- Header: title + max-items badge -->\n <div class=\"mu-header d-flex align-items-start flex-wrap gap-10\">\n <h3 class=\"mu-title m-0 c-111827 fs-1-35rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </h3>\n <span\n class=\"mu-badge d-inline-flex align-items-center rounded-20 color-white font-weight-600 fs-0-72rem p-4px-12px b-111827\"\n *ngIf=\"config.attachmentConfig?.maxFiles\">\n {{ controller.labels['LBL_MEDIA_MAX_PREFIX'] || 'Maximum' }}\n {{ config.attachmentConfig?.maxFiles }}\n {{ controller.labels['LBL_MEDIA_MAX_SUFFIX'] || 'Image & Videos' }}\n </span>\n </div>\n\n <!-- Description -->\n <p class=\"mu-description m-0 fs-0-875rem c-6B7280\" *ngIf=\"config.attachmentConfig?.description\">\n {{ config.attachmentConfig?.description }}\n </p>\n <p class=\"mu-description m-0 fs-0-875rem c-6B7280\"\n *ngIf=\"!config.attachmentConfig?.description && controller.labels['LBL_MEDIA_DESC']\">\n {{ controller.labels['LBL_MEDIA_DESC'] }}\n </p>\n\n <!-- Feature bullet list -->\n <ul class=\"mu-features m-0 p-0 d-flex flex-column gap-8 ls-none\"\n *ngIf=\"config.attachmentConfig?.features?.length || controller.labels['LBL_MEDIA_FEATURE_1']\">\n <ng-container *ngIf=\"config.attachmentConfig?.features?.length\">\n <li *ngFor=\"let f of config.attachmentConfig?.features\"\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ f }}\n </li>\n </ng-container>\n <ng-container *ngIf=\"!config.attachmentConfig?.features?.length\">\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_1']\"\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_1'] }}\n </li>\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_2']\"\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_2'] }}\n </li>\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_3']\"\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_3'] }}\n </li>\n </ng-container>\n </ul>\n\n <!-- Backdrop to close dropdown on outside click -->\n <div class=\"media-menu-backdrop\" *ngIf=\"showMediaMenu\"\n (click)=\"$event.stopPropagation(); showMediaMenu = false\"></div>\n\n <!-- Add Media button + dropdown -->\n <div class=\"media-add-container position-relative\" (click)=\"showMediaMenu = !showMediaMenu\">\n <lib-button id=\"btn-add-media-{{ config.name }}\" [variant]=\"'warning'\"\n [icon]=\"{type: 'material', value: 'add_photo_alternate'}\">\n {{ controller.labels['LBL_ADD_MEDIA'] || 'Add media' }}\n <mat-icon class=\"menu-chevron fs-18px\">add</mat-icon>\n </lib-button>\n\n <div class=\"media-dropdown position-absolute rounded-12 overflow-hidden b-ffffff b-1px-solid-E5E7EB\"\n *ngIf=\"showMediaMenu\" role=\"menu\" (click)=\"$event.stopPropagation()\">\n <!-- Video -->\n <button id=\"btn-media-video-{{ config.name }}\" type=\"button\"\n class=\"media-dropdown-item d-flex align-items-center gap-12 w-100 cursor-pointer text-left b-none border-none p-12px-16px bb-1px-solid-F3F4F6\"\n (click)=\"onMediaMenuVideo(); showMediaMenu = false\" role=\"menuitem\">\n <span\n class=\"media-drop-icon media-drop-icon--video d-flex align-items-center justify-content-center rounded-8\"><mat-icon>videocam</mat-icon></span>\n <span class=\"media-drop-text d-flex flex-column flex-1\">\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\n controller.labels['LBL_MEDIA_VIDEO'] || 'Video' }}</span>\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_VIDEO_DESC'] || 'Add\n YouTube URL'\n }}</span>\n </span>\n </button>\n <!-- Device -->\n <button id=\"btn-media-device-{{ config.name }}\" type=\"button\"\n class=\"media-dropdown-item d-flex align-items-center gap-12 w-100 cursor-pointer text-left b-none border-none p-12px-16px bb-1px-solid-F3F4F6\"\n (click)=\"onMediaMenuDevice(); showMediaMenu = false\" role=\"menuitem\">\n <span\n class=\"media-drop-icon media-drop-icon--device d-flex align-items-center justify-content-center rounded-8\"><mat-icon>upload</mat-icon></span>\n <span class=\"media-drop-text d-flex flex-column flex-1\">\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\n controller.labels['LBL_MEDIA_DEVICE'] || 'Upload from device'\n }}</span>\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_DEVICE_DESC'] ||\n 'Select images from your\n computer' }}</span>\n </span>\n </button>\n <!-- Library -->\n <button id=\"btn-media-library-{{ config.name }}\" type=\"button\"\n class=\"media-dropdown-item d-flex align-items-center gap-12 w-100 cursor-pointer text-left b-none border-none p-12px-16px bb-1px-solid-F3F4F6\"\n (click)=\"onMediaMenuLibrary(); showMediaMenu = false\" role=\"menuitem\">\n <span\n class=\"media-drop-icon media-drop-icon--library d-flex align-items-center justify-content-center rounded-8\"><mat-icon>photo_library</mat-icon></span>\n <span class=\"media-drop-text d-flex flex-column flex-1\">\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\n controller.labels['LBL_MEDIA_LIBRARY'] || 'Upload from library'\n }}</span>\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_LIBRARY_DESC'] ||\n 'Choose from default\n images' }}</span>\n </span>\n </button>\n </div>\n </div>\n\n <!-- YouTube URL Input (inline below button) -->\n <div class=\"youtube-input-panel d-flex flex-column gap-8 p-16 rounded-10 b-FFFAF1 b-1px-solid-E5E7EB\"\n *ngIf=\"showYoutubeInput\">\n <label class=\"youtube-panel-label d-flex align-items-center gap-6 font-weight-600 fs-0-875rem c-111827\">\n {{ controller.labels['LBL_YOUTUBE_URL'] || 'Video URL' }}\n </label>\n <div class=\"youtube-input-row d-flex gap-8\">\n <input id=\"input-youtube-url-{{ config.name }}\" type=\"url\" class=\"field-input youtube-url-input\"\n [(ngModel)]=\"youtubeUrlInput\" [ngModelOptions]=\"{standalone: true}\"\n [placeholder]=\"controller.labels['PH_YOUTUBE_URL'] || 'Video URL'\" (keyup.enter)=\"addYoutubeMedia()\">\n <lib-button id=\"btn-add-youtube-{{ config.name }}\" [variant]=\"'secondary'\" (click)=\"addYoutubeMedia()\">\n {{ controller.labels['LBL_ADD'] || 'Add' }}\n </lib-button>\n </div>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"youtubeUrlError\">{{ youtubeUrlError }}</span>\n </div>\n\n <div\n class=\"media-upload-status d-flex align-items-center gap-8 mt-4 color-error rounded-8 font-weight-500 p-10px-14px b-FEF2F2 fs-0-85rem\"\n *ngIf=\"mediaUploadError\">\n <mat-icon class=\"status-icon fs-18px\">error_outline</mat-icon>\n <span>{{ mediaUploadError }}</span>\n </div>\n\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n </div>\n <!-- end left panel -->\n\n <!-- \u2500\u2500 RIGHT PANEL (carousel) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"mu-right d-flex flex-column gap-12\">\n\n <!-- Carousel (when items exist) -->\n <div class=\"media-carousel-section d-flex flex-column gap-12\" *ngIf=\"mediaItems.length\">\n <div\n class=\"media-carousel-main position-relative w-100 overflow-hidden d-flex align-items-center justify-content-center br-12px b-0F172A\">\n <button id=\"btn-carousel-prev-{{ config.name }}\" type=\"button\"\n class=\"carousel-nav carousel-nav--prev position-absolute rounded-50 cursor-pointer d-flex align-items-center justify-content-center border-none b-rgba-255-255-255-0-85\"\n (click)=\"mediaCarouselPrev()\" [disabled]=\"mediaCarouselIndex === 0\" aria-label=\"Previous\">\n <mat-icon>chevron_left</mat-icon>\n </button>\n\n <div class=\"carousel-viewer position-absolute d-flex align-items-center justify-content-center\"\n *ngFor=\"let item of mediaItems; let i = index\" [hidden]=\"i !== mediaCarouselIndex\">\n <div *ngIf=\"item.isUploading\"\n class=\"carousel-uploading d-flex flex-column align-items-center gap-12 c-94A3B8 fs-0-85rem\">\n <div class=\"carousel-spinner rounded-50 b-3px-solid-rgba-255-255-255-0-15\"></div>\n <span>{{ controller.labels['LBL_UPLOADING'] || 'Uploading\u2026' }}</span>\n </div>\n <ng-container *ngIf=\"!item.isUploading && item.mediaType === 'youtube'\">\n <iframe class=\"carousel-iframe w-100 h-100 br-12px\" [src]=\"item.url | trustedUrl\" frameborder=\"0\"\n allowfullscreen\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\">\n </iframe>\n </ng-container>\n <ng-container *ngIf=\"!item.isUploading && item.mediaType === 'image'\">\n <img class=\"carousel-image w-100 h-100 br-12px\" [src]=\"item.url\" alt=\"Media\">\n </ng-container>\n <button id=\"btn-remove-media-{{ config.name }}-{{ i }}\" type=\"button\"\n class=\"carousel-remove-btn position-absolute rounded-50 cursor-pointer d-flex align-items-center justify-content-center border-none b-rgba-0-0-0-0-55\"\n [disabled]=\"item.isUploading\" (click)=\"removeMediaItem(i)\" aria-label=\"Remove\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n\n <button id=\"btn-carousel-next-{{ config.name }}\" type=\"button\"\n class=\"carousel-nav carousel-nav--next position-absolute rounded-50 cursor-pointer d-flex align-items-center justify-content-center border-none b-rgba-255-255-255-0-85\"\n (click)=\"mediaCarouselNext()\" [disabled]=\"mediaCarouselIndex === mediaItems.length - 1\" aria-label=\"Next\">\n <mat-icon>chevron_right</mat-icon>\n </button>\n\n <div class=\"carousel-dots position-absolute d-flex gap-6\">\n <span *ngFor=\"let item of mediaItems; let i = index\"\n class=\"carousel-dot rounded-50 cursor-pointer b-rgba-255-255-255-0-45\"\n [class.active]=\"i === mediaCarouselIndex\" (click)=\"mediaGoTo(i)\"></span>\n </div>\n </div>\n\n <!-- Thumbnail strip -->\n <div class=\"media-thumbnail-strip d-flex flex-wrap gap-8 pb-4px\">\n <div *ngFor=\"let item of mediaThumbnails; let i = index\"\n class=\"media-thumb rounded-8 overflow-hidden cursor-pointer d-flex align-items-center justify-content-center b-2px-solid-transparent b-E2E8F0\"\n [class.active]=\"i === mediaCarouselIndex\" (click)=\"mediaGoTo(i)\">\n <div *ngIf=\"item.isUploading\"\n class=\"thumb-uploading d-flex align-items-center justify-content-center w-100 h-100\">\n <div class=\"thumb-spinner rounded-50 b-2px-solid-E2E8F0\"></div>\n </div>\n <img *ngIf=\"!item.isUploading && item.mediaType === 'youtube' && item.thumbnailUrl\"\n [src]=\"item.thumbnailUrl\" class=\"thumb-img w-100 h-100\" alt=\"Video thumbnail\">\n <div *ngIf=\"!item.isUploading && item.mediaType === 'youtube' && !item.thumbnailUrl\"\n class=\"thumb-yt-placeholder d-flex align-items-center justify-content-center w-100 h-100 b-1E293B c-EF4444\">\n <mat-icon>play_circle</mat-icon>\n </div>\n <img *ngIf=\"!item.isUploading && item.mediaType === 'image' && item.url\" [src]=\"item.url\"\n class=\"thumb-img w-100 h-100\" alt=\"Image thumbnail\">\n </div>\n </div>\n </div>\n\n <!-- Empty right-side placeholder -->\n <div\n class=\"mu-right-empty d-flex flex-column align-items-center justify-content-center gap-10 h-100 text-center p-24 br-12px b-FFFAF1 c-94A3B8 b-2px-dashed-CBD5E1\"\n *ngIf=\"!mediaItems.length\" (click)=\"onMediaMenuDevice()\">\n <mat-icon class=\"mu-right-empty-icon fs-52px\">perm_media</mat-icon>\n <p>{{ controller.labels['LBL_ADD_MEDIA'] || 'Add media' }}</p>\n </div>\n\n </div>\n <!-- end right panel -->\n\n </div><!-- end mu-layout -->\n </div>\n\n\n <!-- \u2550\u2550 Library Image Picker Modal \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <!-- Wrapper is always in DOM (hidden) so @ViewChild can move it to body -->\n <div #libraryModal class=\"media-library-portal-host\" [class.is-open]=\"showLibraryModal\">\n\n <!-- Backdrop -->\n <div class=\"media-library-overlay\" (click)=\"closeLibraryModal()\"></div>\n\n <!-- Modal card -->\n <div class=\"media-library-modal d-flex flex-column overflow-hidden b-ffffff br-16px\"\n role=\"dialog\" aria-modal=\"true\">\n <div class=\"library-modal-header d-flex align-items-start justify-content-between p-24px-28px bb-1px-solid-E5E7EB\">\n <div class=\"header-left d-flex flex-column gap-8\">\n <h3 class=\"library-modal-title m-0 color-dark fs-1-25rem\">\n {{ controller.labels['LBL_ADD_IMAGES'] || 'Add Images' }}\n </h3>\n <p class=\"library-modal-subtitle m-0 color-gray fs-0-85rem\">\n {{ controller.labels['LBL_LIBRARY_MODAL_DESC'] || 'Select images from your library.' }}\n </p>\n </div>\n <button id=\"btn-close-library-{{ config.name }}\" type=\"button\"\n class=\"library-close-btn d-flex align-items-center justify-content-center cursor-pointer rounded-50 border-none b-none c-9CA3AF\"\n (click)=\"closeLibraryModal()\" aria-label=\"Close\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n\n <!-- Loading -->\n <div class=\"library-loading\" *ngIf=\"libraryLoading\">\n <div class=\"lib-spinner rounded-50 b-3px-solid-E2E8F0\"></div>\n <span>{{ controller.labels['LBL_LOADING'] || 'Loading\u2026' }}</span>\n </div>\n\n <!-- Error -->\n <div class=\"library-error d-flex align-items-center gap-8 color-error b-FEF2F2 fs-0-875rem p-16px-24px\"\n *ngIf=\"libraryError && !libraryLoading\">\n <mat-icon>error_outline</mat-icon>\n {{ libraryError }}\n </div>\n\n <!-- Image grid -->\n <div class=\"library-grid d-grid gap-16 flex-1 p-28px b-F9FAFB\" *ngIf=\"!libraryLoading && libraryImages.length\">\n <div *ngFor=\"let img of libraryImages; let li = index\" id=\"lib-img-{{ config.name }}-{{ li }}\"\n class=\"library-grid-item position-relative rounded-12 overflow-hidden cursor-pointer bg-white b-3px-solid-transparent\"\n [class.selected]=\"isLibraryItemSelected(img)\" (click)=\"toggleLibraryItem(img)\">\n <img [src]=\"getLibraryItemUrl(img)\" class=\"library-grid-img w-100 h-100 d-block\" alt=\"Library image\">\n <div\n class=\"library-check position-absolute bg-white rounded-50 d-flex align-items-center justify-content-center c-3B82F6\"\n *ngIf=\"isLibraryItemSelected(img)\">\n <mat-icon>check_circle</mat-icon>\n </div>\n <div class=\"library-overlay-hover position-absolute b-rgba-59-130-246-0-12\"></div>\n </div>\n </div>\n\n <!-- Empty library -->\n <div\n class=\"library-empty d-flex flex-column align-items-center justify-content-center gap-12 flex-1 c-9CA3AF fs-0-875rem p-48px-24px\"\n *ngIf=\"!libraryLoading && !libraryError && libraryImages.length === 0\">\n <mat-icon>image_not_supported</mat-icon>\n <span>{{ controller.labels['LBL_LIBRARY_EMPTY'] || 'No images found in library.' }}</span>\n </div>\n\n <!-- Footer -->\n <div class=\"library-modal-footer d-flex align-items-center justify-content-end bg-white p-20px-28px bt-1px-solid-E5E7EB\">\n <div class=\"library-footer-actions d-flex gap-12\">\n <lib-button id=\"btn-library-cancel-{{ config.name }}\" [variant]=\"'outline'\" (click)=\"closeLibraryModal()\">\n {{ controller.labels['LBL_CANCEL'] || 'Cancel' }}\n </lib-button>\n <lib-button id=\"btn-library-confirm-{{ config.name }}\" [variant]=\"'primary'\"\n [disabled]=\"librarySelectedIds.size === 0\" (click)=\"confirmLibrarySelection()\">\n {{ controller.labels['LBL_CONTINUE'] || 'Continue' }}\n </lib-button>\n </div>\n </div>\n </div>\n </div>\n\n\n\n <!-- \u2550\u2550 Location Field \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isLocation\" class=\"field-wrapper location-field-wrapper d-flex flex-column gap-6 gap-12\"\n [formGroup]=\"formGroup\">\n\n <!-- Field label -->\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n <p class=\"location-subtitle m-0 c-6B7280 fs-0-8125rem\" *ngIf=\"config.hint\">{{ config.hint }}</p>\n\n <!-- Three-tab bar -->\n <div class=\"location-tabs d-flex gap-12 mb-24\">\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'VENUE' ? 'warning' : 'outline'\"\n (click)=\"onLocationTabChange('VENUE')\">\n {{ controller.labels['LBL_LOC_VENUE'] || 'Venue' }}\n </lib-button>\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'ONLINE' ? 'warning' : 'outline'\"\n (click)=\"onLocationTabChange('ONLINE')\">\n {{ controller.labels['LBL_LOC_ONLINE'] || 'Online Event' }}\n </lib-button>\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'TBA' ? 'warning' : 'outline'\"\n (click)=\"onLocationTabChange('TBA')\">\n {{ controller.labels['LBL_LOC_TBA'] || 'To be Announced' }}\n </lib-button>\n </div>\n\n <!-- VENUE TAB -->\n <div *ngIf=\"locationActiveTab === 'VENUE'\" class=\"loc-panel loc-venue-panel d-flex flex-column gap-12\">\n\n <p class=\"loc-section-label m-0 font-weight-600 c-111827 fs-0-9rem\">\n {{ controller.labels['LBL_LOC_ADDRESS'] || 'Location address' }}\n </p>\n\n <!-- Added venue rows -->\n <div class=\"loc-venue-list d-flex flex-column gap-8\" *ngIf=\"locationVenues.length > 0\">\n <div *ngFor=\"let venue of locationVenues; let i = index\"\n class=\"loc-venue-item d-flex align-items-center gap-10 p-10px-14px br-7px b-ffffff b-1px-solid-D1D5DB\">\n <mat-icon class=\"loc-venue-search-icon fs-18px c-9CA3AF\">search</mat-icon>\n <span class=\"loc-venue-text flex-1 overflow-hidden fs-0-875rem c-111827\">{{ venue.address || venue.name ||\n venue.description }}</span>\n <button type=\"button\"\n class=\"loc-action-btn loc-delete-btn d-flex align-items-center justify-content-center cursor-pointer rounded-50 b-none border-none p-4px\"\n (click)=\"removeLocationVenue(i)\">\n <mat-icon>delete_outline</mat-icon>\n </button>\n </div>\n </div>\n\n <!-- Location count badge -->\n <p class=\"loc-count-text m-0 font-weight-600 fs-0-8125rem c-3B82F6\"\n *ngIf=\"locationVenues.length > 0 && config.locationConfig?.allowMulti\">\n {{ locationVenues.length }} {{ controller.labels['LBL_LOC_COUNT_SUFFIX'] || 'Locations Added!' }}\n </p>\n\n <!-- Search input (hide when max reached) -->\n <div class=\"loc-search-container position-relative\" *ngIf=\"!locationMaxReached\">\n <div class=\"loc-search-wrapper position-relative d-flex align-items-center mt-4\">\n <mat-icon class=\"loc-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">search</mat-icon>\n <input\n class=\"field-input loc-search-input w-100 font-poppins flex-1 fs-0-875rem c-111827 br-7px br-8px bc-F3F4F6 pl-2-4rem bc-DC2626 pt-0-625rem pb-0-625rem pl-16px pr-16px bc-ffffff b-1px-solid-D1D5DB pr-3-5rem\"\n [placeholder]=\"config.locationConfig?.venuePlaceholder || (controller.labels['PH_LOC_VENUE'] || 'Type to search venue...')\"\n [value]=\"locationSearchText\" (input)=\"handleLocationSearchInput($event)\" (blur)=\"hideLocationSuggestions()\"\n autocomplete=\"off\" [class.is-invalid]=\"errorMessage\">\n </div>\n <!-- Suggestions dropdown -->\n <div class=\"loc-suggestions-panel position-absolute overflow-hidden br-8px b-ffffff b-1px-solid-D1D5DB\"\n *ngIf=\"locationShowSuggestions && locationSuggestions.length\">\n <div *ngFor=\"let sug of locationSuggestions\"\n class=\"loc-suggestion-item d-flex align-items-center gap-10 cursor-pointer p-10px-14px\"\n (mousedown)=\"onLocationSuggestionSelect(sug)\">\n <mat-icon class=\"loc-suggestion-icon fs-18px c-E53E3E\">place</mat-icon>\n <span class=\"loc-suggestion-text overflow-hidden fs-0-875rem c-374151\">{{ sug.description }}</span>\n </div>\n </div>\n </div>\n\n <!-- Add another button -->\n <button type=\"button\"\n class=\"loc-add-btn d-inline-flex align-items-center gap-6 cursor-pointer font-weight-600 p-0 b-none border-none fs-0-875rem c-1A56DB\"\n *ngIf=\"locationVenues.length > 0 && !locationMaxReached && config.locationConfig?.allowMulti\">\n <mat-icon>add_circle_outline</mat-icon>\n <span>{{ controller.labels['LBL_LOC_ADD_ANOTHER'] || 'Add another Location' }}</span>\n </button>\n\n <!-- Map -->\n <div class=\"loc-map-container overflow-hidden br-10px b-1px-solid-E5E7EB\"\n *ngIf=\"config.locationConfig?.showMap !== false\">\n <ng-container *ngIf=\"config.locationConfig?.googleMapsApiKey; else simpleEmbed\">\n <div [id]=\"'loc-map-' + config.name\" class=\"loc-map-frame w-100 d-block border-none\"\n [style.height]=\"config.locationConfig?.mapHeight || '300px'\"></div>\n </ng-container>\n <ng-template #simpleEmbed>\n <iframe class=\"loc-map-frame w-100 d-block border-none\"\n [style.height]=\"config.locationConfig?.mapHeight || '300px'\" [src]=\"getLocationMapEmbedUrl() | trustedUrl\"\n frameborder=\"0\" allowfullscreen loading=\"lazy\">\n </iframe>\n </ng-template>\n </div>\n\n <!-- Map hint -->\n <p class=\"loc-map-hint m-0 text-center fs-0-78rem c-6B7280\">\n {{ controller.labels['LBL_LOC_MAP_HINT'] || 'Type the venue address. A map will appear to assist you.' }}\n </p>\n </div>\n\n <!-- ONLINE TAB -->\n <div *ngIf=\"locationActiveTab === 'ONLINE'\" class=\"loc-panel loc-online-panel d-flex flex-column gap-12\">\n <p class=\"loc-section-label m-0 font-weight-600 c-111827 fs-0-9rem\">\n {{ controller.labels['LBL_LOC_ONLINE_URL'] || 'Event URL' }}\n </p>\n <div class=\"loc-search-wrapper position-relative d-flex align-items-center mt-4\">\n <mat-icon class=\"loc-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">link</mat-icon>\n <input\n class=\"field-input loc-search-input w-100 font-poppins flex-1 fs-0-875rem c-111827 br-7px br-8px bc-F3F4F6 pl-2-4rem bc-DC2626 pt-0-625rem pb-0-625rem pl-16px pr-16px bc-ffffff b-1px-solid-D1D5DB pr-3-5rem\"\n type=\"url\"\n [placeholder]=\"config.locationConfig?.onlinePlaceholder || (controller.labels['PH_LOC_ONLINE'] || 'https://zoom.us/j/...')\"\n [value]=\"locationOnlineUrl\" (input)=\"onLocationUrlChange($any($event.target).value)\"\n [class.is-invalid]=\"errorMessage\">\n </div>\n </div>\n\n <!-- TBA TAB -->\n <div *ngIf=\"locationActiveTab === 'TBA'\"\n class=\"loc-panel loc-tba-panel d-flex flex-column gap-12 justify-content-center\">\n <div\n class=\"loc-tba-content d-flex flex-column align-items-center justify-content-center text-center gap-12 p-32px-24px b-F9FAFB b-1px-dashed-D1D5DB br-10px\">\n <mat-icon class=\"loc-tba-icon fs-40px c-9CA3AF\">schedule</mat-icon>\n <p class=\"loc-tba-text m-0 c-6B7280 fs-0-9rem\">\n {{ controller.labels['LBL_LOC_TBA_DESC'] || \"This event's location is yet to be announced. Check back later\n for updates.\" }}\n </p>\n </div>\n </div>\n\n <!-- Hidden real form control -->\n <input type=\"hidden\" [formControlName]=\"config.name!\">\n\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n</div>", styles: [".d-flex{display:flex}.d-inline-flex{display:inline-flex}.d-grid{display:grid}.d-block{display:block}.d-none{display:none}.flex-column{flex-direction:column}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.flex-wrap{flex-wrap:wrap}.flex-1{flex:1}.align-items-center{align-items:center}.align-items-start{align-items:flex-start}.align-items-end{align-items:flex-end}.justify-content-center{justify-content:center}.justify-content-between{justify-content:space-between}.justify-content-start{justify-content:flex-start}.justify-content-end{justify-content:flex-end}.grid-cols-12{grid-template-columns:repeat(12,1fr)}.w-100{width:100%}.h-100{height:100%}.position-relative{position:relative}.position-absolute{position:absolute}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-poppins{font-family:var(--cc-sf-font-family, \"Poppins\", sans-serif)}.font-weight-400{font-weight:400}.font-weight-500{font-weight:500}.font-weight-600{font-weight:600}.text-13{font-size:13px}.text-14{font-size:14px}.text-16{font-size:16px}.color-white{color:#fff}.color-dark{color:#111827}.color-gray{color:#6b7280}.color-error{color:var(--cc-sf-error-text-color, #DC2626)}.bg-white{background-color:#fff}.bg-transparent{background-color:transparent}.m-0{margin:0}.mt-4{margin-top:4px}.mt-8{margin-top:8px}.mt-10{margin-top:10px}.mt-12{margin-top:12px}.mt-16{margin-top:16px}.mt-20{margin-top:20px}.mt-24{margin-top:24px}.mb-0{margin-bottom:0}.mb-4{margin-bottom:4px}.mb-8{margin-bottom:8px}.mb-10{margin-bottom:10px}.mb-12{margin-bottom:12px}.mb-16{margin-bottom:16px}.mb-20{margin-bottom:20px}.mb-24{margin-bottom:24px}.ml-16{margin-left:16px}.ml-20{margin-left:20px}.p-0{padding:0}.p-16{padding:16px}.p-20{padding:20px}.p-24{padding:24px}.pt-20{padding-top:20px}.pb-10{padding-bottom:10px}.gap-4{gap:4px}.gap-6{gap:6px}.gap-8{gap:8px}.gap-10{gap:10px}.gap-12{gap:12px}.gap-16{gap:16px}.gap-20{gap:20px}.rounded-4{border-radius:4px}.rounded-6{border-radius:6px}.rounded-8{border-radius:8px}.rounded-10{border-radius:10px}.rounded-12{border-radius:12px}.rounded-20{border-radius:20px}.rounded-24{border-radius:24px}.rounded-50{border-radius:50%}.cursor-pointer{cursor:pointer}.overflow-hidden{overflow:hidden}.resize-vertical{resize:vertical}.box-sizing-border{box-sizing:border-box}.border-none{border:none!important}.mb-16px{margin-bottom:var(--cc-sf-grid-gap, 16px)!important}.c-DC2626{color:var(--cc-sf-label-required-color, #DC2626)!important}.ml-0-125rem{margin-left:.125rem!important}.fs-0-875rem{font-size:.875rem!important}.c-111827{color:var(--cc-sf-label-color, #111827)!important}.br-7px{border-radius:var(--cc-sf-input-radius, 7px)!important}.c-6B7280{color:var(--cc-sf-hint-color, #6B7280)!important}.fs-0-75rem{font-size:var(--cc-sf-error-text-size, .75rem)!important}.b-none{background:none!important}.p-32px-24px{padding:32px 24px!important}.us-none{-webkit-user-select:none!important;user-select:none!important}.c-1E293B{color:var(--cc-sf-label-color, #1E293B)!important}.c-3B82F6{color:var(--cc-sf-chip-selected-bg, #3B82F6)!important}.fs-0-78rem{font-size:.78rem!important}.p-10px-14px{padding:10px 14px!important}.fs-0-85rem{font-size:.85rem!important}.fs-0-72rem{font-size:.72rem!important}.c-94A3B8{color:#94a3b8!important}.p-4px{padding:4px!important}.br-8px{border-radius:var(--cc-sf-input-radius, 8px)!important}.bc-F3F4F6{background-color:var(--cc-sf-input-disabled-bg, #F3F4F6)!important}.br-none{border-right:none!important}.bl-none{border-left:none!important}.pe-none{pointer-events:none!important}.fs-1-1rem{font-size:1.1rem!important}.c-9CA3AF{color:var(--cc-sf-hint-color, #9CA3AF)!important}.pl-2-4rem{padding-left:2.4rem!important}.fs-0-8125rem{font-size:.8125rem!important}.ls-none{list-style:none!important}.br-12px{border-radius:var(--mu-carousel-radius, 12px)!important}.b-FFFAF1{background:var(--cc-sf-dropzone-bg, #FFFAF1)!important}.fs-18px{font-size:18px!important}.b-FEF2F2{background:var(--cc-sf-error-bg, #FEF2F2)!important}.bc-DC2626{border-color:var(--cc-sf-error-border, #DC2626)!important}.c-202124{color:var(--cc-sf-label-color, #202124)!important}.fs-18px{font-size:var(--cc-sf-label-size, 18px)!important}.mb-0-5rem{margin-bottom:.5rem!important}.pt-0-625rem{padding-top:var(--cc-sf-input-padding-y, .625rem)!important}.pb-0-625rem{padding-bottom:var(--cc-sf-input-padding-y, .625rem)!important}.pl-16px{padding-left:var(--cc-sf-input-padding-x, 16px)!important}.pr-16px{padding-right:var(--cc-sf-input-padding-x, 16px)!important}.bc-ffffff{background-color:var(--cc-sf-section-bg, #ffffff)!important}.b-1px-solid-D1D5DB{border:1px solid var(--cc-sf-input-border, #D1D5DB)!important}.fs-0-75rem{font-size:.75rem!important}.c-1F2937{color:var(--cc-sf-section-label-color, #1F2937)!important}.p-6px-14px{padding:var(--cc-sf-chip-padding, 6px 14px)!important}.b-ffffff{background:var(--loc-suggestion-bg, #ffffff)!important}.c-374151{color:var(--cc-sf-label-color, #374151)!important}.br-20px{border-radius:var(--cc-sf-chip-radius, 20px)!important}.fs-0-875rem{font-size:var(--cc-sf-btn-font-size, .875rem)!important}.bc-D1D5DB{background-color:var(--cc-sf-switch-track-off, #D1D5DB)!important}.pr-2-75rem{padding-right:2.75rem!important}.p-0-25rem{padding:.25rem!important}.p-0-625rem-0-875rem{padding:var(--cc-sf-generated-padding, .625rem .875rem)!important}.b-F3F4F6{background:var(--cc-sf-generated-bg, #F3F4F6)!important}.b-1px-solid-E5E7EB{border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)!important}.br-8px{border-radius:var(--cc-sf-uploaded-item-radius, 8px)!important}.c-6B7280{color:var(--ms-desc-color, #6B7280)!important}.mb-20px{margin-bottom:var(--cc-sf-section-gap, 20px)!important}.br-10px{border-radius:var(--cc-sf-input-radius, 10px)!important}.p-20px{padding:var(--cc-sf-section-padding, 20px)!important}.fs-1rem{font-size:1rem!important}.m-0-0-16px-0{margin:0 0 16px!important}.bb-2px-solid-E5E7EB{border-bottom:var(--cc-sf-section-label-border, 2px solid #E5E7EB)!important}.p-16px{padding:var(--cc-sf-instance-padding, 16px)!important}.b-F9FAFB{background:var(--loc-tba-bg, #F9FAFB)!important}.bb-1px-dashed-D1D5DB{border-bottom:var(--cc-sf-instance-divider, 1px dashed #D1D5DB)!important}.c-4B5563{color:var(--cc-sf-instance-num-color, #4B5563)!important}.fs-0-8125rem{font-size:var(--cc-sf-hint-size, .8125rem)!important}.pb-0{padding-bottom:0!important}.p-18px-24px{padding:18px 24px!important}.c-111827{color:var(--ms-title-color, #111827)!important}.bt-1px-solid-E5E7EB{border-top:1px solid #E5E7EB!important}.p-4px-10px{padding:4px 10px!important}.b-FFF5F5{background:var(--cc-sf-btn-remove-bg, #FFF5F5)!important}.c-E53E3E{color:var(--loc-delete-color, #E53E3E)!important}.b-1px-solid-FED7D7{border:var(--cc-sf-btn-remove-border, 1px solid #FED7D7)!important}.br-4px{border-radius:var(--cc-sf-btn-remove-radius, 4px)!important}.p-8px-16px{padding:8px 16px!important}.b-transparent{background:var(--cc-sf-btn-add-bg, transparent)!important}.c-3B82F6{color:var(--cc-sf-input-focus-border, #3B82F6)!important}.b-1px-dashed-CBD5E1{border:var(--cc-sf-btn-add-border, 1px dashed #CBD5E1)!important}.br-6px{border-radius:var(--cc-sf-btn-add-radius, 6px)!important}.b-1-5px-dashed-CBD5E1{border:var(--cc-sf-dropzone-border, 1.5px dashed #CBD5E1)!important}.br-12px{border-radius:var(--cc-sf-dropzone-radius, 12px)!important}.bc-FFFAF1{background-color:var(--cc-sf-dropzone-bg, #FFFAF1)!important}.c-94A3B8{color:var(--cc-sf-uploaded-remove-color, #94A3B8)!important}.fs-0-9rem{font-size:var(--cc-sf-input-font-size, .9rem)!important}.c-64748B{color:var(--cc-sf-dropzone-hint-color, #64748B)!important}.b-1px-solid-E2E8F0{border:var(--cc-sf-uploaded-item-border, 1px solid #E2E8F0)!important}.b-2px-solid-E2E8F0{border:2px solid #E2E8F0!important}.pr-3-5rem{padding-right:3.5rem!important}.p-0-0-875rem{padding:0 .875rem!important}.bc-FFFFFF{background-color:var(--cc-sf-input-bg, #FFFFFF)!important}.b-1-5px-solid-D1D5DB{border:var(--cc-sf-input-border, 1.5px solid #D1D5DB)!important}.mb-0-75rem{margin-bottom:.75rem!important}.mt-6px{margin-top:6px!important}.pr-2-4rem{padding-right:2.4rem!important}.p-0-2rem{padding:.2rem!important}.fs-1-35rem{font-size:1.35rem!important}.p-4px-12px{padding:4px 12px!important}.b-111827{background:var(--cc-sf-label-color, #111827)!important}.b-2px-dashed-CBD5E1{border:2px dashed var(--cc-sf-dropzone-border, #CBD5E1)!important}.fs-52px{font-size:52px!important}.p-12px-16px{padding:12px 16px!important}.bb-1px-solid-F3F4F6{border-bottom:1px solid var(--cc-sf-input-disabled-border, #F3F4F6)!important}.b-0F172A{background:var(--mu-carousel-bg, #0F172A)!important}.b-3px-solid-rgba-255-255-255-0-15{border:3px solid rgba(255,255,255,.15)!important}.b-rgba-255-255-255-0-85{background:#ffffffd9!important}.b-rgba-0-0-0-0-55{background:#0000008c!important}.b-rgba-255-255-255-0-45{background:#ffffff73!important}.pb-4px{padding-bottom:4px!important}.b-2px-solid-transparent{border:2px solid transparent!important}.b-E2E8F0{background:var(--mu-thumb-bg, #E2E8F0)!important}.b-1E293B{background:#1e293b!important}.c-EF4444{color:#ef4444!important}.b-rgba-0-0-0-0-5{background:#00000080!important}.br-16px{border-radius:var(--mu-modal-radius, 16px)!important}.p-24px-28px{padding:24px 28px!important}.bb-1px-solid-E5E7EB{border-bottom:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)!important}.fs-1-25rem{font-size:1.25rem!important}.p-48px-24px{padding:48px 24px!important}.b-3px-solid-E2E8F0{border:3px solid #E2E8F0!important}.p-16px-24px{padding:16px 24px!important}.p-28px{padding:28px!important}.b-3px-solid-transparent{border:3px solid transparent!important}.b-rgba-59-130-246-0-12{background:#3b82f61f!important}.p-20px-28px{padding:20px 28px!important}.c-1A56DB{color:var(--loc-add-color, #1A56DB)!important}.b-1px-dashed-D1D5DB{border:1px dashed var(--cc-sf-input-disabled-border, #D1D5DB)!important}.fs-40px{font-size:40px!important}.c-9CA3AF{color:var(--loc-tba-icon-color, #9CA3AF)!important}.form-field{font-family:var(--cc-sf-font-family, \"Poppins\", sans-serif)!important}:host{--cc-sf-input-border: #D1D5DB;--cc-sf-input-bg: #ffffff;--cc-sf-input-radius: 9px;--cc-sf-input-height: 44px;--cc-sf-label-color: #111827;--cc-sf-hint-color: #9CA3AF;--cc-sf-error-border: #EF4444;--cc-sf-error-bg: #FFF5F5;--cc-sf-accent-color: #6366F1;--cc-sf-input-focus-border: #6366F1;--cc-sf-input-hover-border: #A5B4FC;--cc-sf-input-placeholder: #C4C9D4;--cc-sf-input-disabled-bg: #F8F9FB;--cc-sf-input-disabled-border: #E5E7EB;--cc-sf-switch-track-on: #6366F1;--cc-sf-switch-track-off: #D1D5DB;--cc-sf-switch-thumb: #ffffff;--cc-sf-selected-color: #6366F1}.form-row{gap:var(--cc-sf-grid-gap, 16px)}.form-row.horizontal{display:flex;flex-direction:row}.form-row.horizontal>*{flex:1}.form-row:not(.horizontal){flex-direction:column}.form-row.grid-row{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--cc-sf-grid-gap, 16px);align-items:start}@media(max-width:640px){.form-row.grid-row{grid-template-columns:1fr}.form-row.grid-row .row-field{grid-column:span 12!important}}.field-label{font-size:.75rem;font-weight:600;line-height:1;letter-spacing:.04em;text-transform:uppercase;color:#6b7280}.field-input,input.matInput,.mat-mdc-input-element{display:block;width:100%;height:var(--cc-sf-input-height)!important;padding:0 14px!important;font-family:inherit;font-size:.9rem;color:var(--cc-sf-label-color);background-color:var(--cc-sf-input-bg)!important;border:1.5px solid var(--cc-sf-input-border)!important;border-radius:var(--cc-sf-input-radius)!important;box-sizing:border-box;box-shadow:0 1px 2px #0000000a!important;transition:all .2s cubic-bezier(.4,0,.2,1)}.field-input::placeholder,input.matInput::placeholder,.mat-mdc-input-element::placeholder{font-weight:400;font-size:14px;color:var(--cc-sf-input-placeholder)}.field-input{opacity:var(--cc-sf-input-opacity, 1);line-height:var(--cc-sf-input-line-height, 1.5);transition:var(--cc-sf-input-transition, all .2s ease)}.field-input::placeholder{font-weight:var(--cc-sf-placeholder-weight, 400);font-size:var(--cc-sf-placeholder-size, 14px);line-height:var(--cc-sf-placeholder-line-height, 100%);color:var(--cc-sf-input-placeholder)}.field-input:hover:not(:disabled):not([readonly]){border-color:var(--cc-sf-input-hover-border)!important;box-shadow:0 1px 4px #6366f114!important}.field-input:focus{outline:none;border-color:var(--cc-sf-input-focus-border)!important;box-shadow:0 0 0 3px #6366f124,0 1px 4px #6366f11a!important;background-color:#fefeff!important}.field-input:disabled,.field-input[readonly]{background-color:var(--cc-sf-input-disabled-bg)!important;color:#9ca3af!important;cursor:not-allowed;border-color:var(--cc-sf-input-disabled-border)!important;box-shadow:none!important}.field-input.is-invalid{border-color:var(--cc-sf-error-border)!important;background-color:var(--cc-sf-error-bg)!important}.field-input.is-invalid:focus{box-shadow:0 0 0 3px #ef44441f,0 1px 4px #ef44441a!important}.field-input.textarea{resize:vertical;min-height:100px;height:auto;padding:12px 16px!important}input[type=time].time-input{cursor:pointer}input[type=time].time-input::-webkit-calendar-picker-indicator{cursor:pointer;opacity:.7;filter:invert(30%)}input[type=time].time-input::-webkit-calendar-picker-indicator:hover{opacity:1}select.field-input{appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236B7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3E%3C/svg%3E\");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;cursor:pointer}select.field-input:disabled{cursor:not-allowed}.multi-select-wrapper{position:relative}.multi-select-wrapper .multi-select-trigger{min-height:var(--cc-sf-input-height, 44px);height:auto;-webkit-user-select:none;user-select:none}.multi-select-wrapper .multi-select-trigger.ms-open{border-color:var(--cc-sf-input-focus-border, #6366F1)!important;box-shadow:0 0 0 3px #6366f124,0 1px 4px #6366f11a!important}.multi-select-wrapper .multi-select-trigger .multi-select-value{flex:1;color:var(--cc-sf-label-color, #111827);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.multi-select-wrapper .multi-select-trigger .multi-select-placeholder{flex:1;font-weight:400;font-size:14px;color:var(--cc-sf-input-placeholder, #C4C9D4)}.multi-select-wrapper .multi-select-trigger .multi-select-arrow{font-size:20px;width:20px;height:20px;line-height:20px;color:#6b7280;flex-shrink:0}.multi-select-wrapper .multi-select-panel{position:absolute;top:calc(100% + 4px);left:0;right:0;background:#fff;border:1.5px solid var(--cc-sf-input-border, #D1D5DB);border-radius:var(--cc-sf-input-radius, 9px);box-shadow:0 8px 24px #0000001a,0 2px 6px #0000000f;z-index:1050;max-height:240px;overflow-y:auto}.multi-select-wrapper .multi-select-panel::-webkit-scrollbar{width:4px}.multi-select-wrapper .multi-select-panel::-webkit-scrollbar-thumb{background:#d1d5db;border-radius:2px}.multi-select-wrapper .multi-select-option{padding:8px 12px;border-bottom:1px solid #F3F4F6;transition:background .1s}.multi-select-wrapper .multi-select-option:last-of-type{border-bottom:none}.multi-select-wrapper .multi-select-option:hover{background:#f9fafb}.multi-select-wrapper .multi-select-option input[type=checkbox]{flex-shrink:0;cursor:pointer;accent-color:var(--cc-sf-selected-color, #6366F1);width:15px;height:15px}.multi-select-wrapper .multi-select-empty{padding:12px;text-align:center}.multi-select-wrapper.is-invalid .multi-select-trigger{border-color:var(--cc-sf-error-border, #EF4444)!important;background-color:var(--cc-sf-error-bg, #FFF5F5)!important}.char-count-hint{font-style:normal;line-height:100%;letter-spacing:.02em;margin-top:.4rem}.radio-group.layout-column,.checkbox-group.layout-column{display:grid!important;grid-template-columns:repeat(12,1fr);gap:16px;width:100%}.radio-group.layout-row,.checkbox-group.layout-row{flex-direction:column!important;gap:12px;width:100%}.radio-label input,.checkbox-label input{cursor:pointer;accent-color:var(--cc-sf-chip-selected-bg, #F05A28)}.radio-label.card-item,.checkbox-label.card-item{display:flex!important;flex-direction:row-reverse!important;justify-content:space-between!important;align-items:center!important;border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB);border-radius:12px;padding:16px 20px;box-sizing:border-box;transition:all .2s ease;background:var(--cc-sf-input-bg, #ffffff);margin-bottom:0}.radio-label.card-item input,.checkbox-label.card-item input{margin-left:16px}.radio-label.card-item.selected,.checkbox-label.card-item.selected{border-color:var(--cc-sf-selected-color);background-color:#f05a280d}.radio-label.card-item .option-content .label-text,.checkbox-label.card-item .option-content .label-text,.checkbox-single .checkbox-label{font-weight:var(--cc-sf-label-weight, 500)}.chip-label{transition:var(--cc-sf-input-transition, all .2s ease)}.chip-label:hover{background:var(--cc-sf-chip-hover-bg, #F3F4F6)}.chip-label.selected{background:var(--cc-sf-selected-color);color:var(--cc-sf-chip-selected-color, #ffffff);border-color:var(--cc-sf-selected-color)}.switch{width:50px;height:24px;display:inline-block}.switch input{opacity:0;width:0;height:0;position:absolute}.switch input:checked+.slider{background-color:var(--cc-sf-switch-track-on)!important}.switch input:checked+.slider:before{transform:translate(26px)}.switch .slider{inset:0;transition:.4s;background-color:var(--cc-sf-switch-track-off);border-radius:24px}.switch .slider:before{position:absolute;content:\"\";height:18px;width:18px;left:3px;bottom:3px;background-color:var(--cc-sf-switch-thumb);transition:.4s;border-radius:50%}.rating-group .star{transition:var(--cc-sf-input-transition, all .2s ease)}.rating-group .star mat-icon{font-size:var(--cc-sf-star-size, 28px);width:var(--cc-sf-star-size, 28px);height:var(--cc-sf-star-size, 28px);line-height:var(--cc-sf-star-size, 28px);color:var(--cc-sf-star-empty, #D1D5DB);transition:var(--cc-sf-input-transition, all .2s ease)}.rating-group .star.filled mat-icon,.rating-group .star.half mat-icon{color:var(--cc-sf-star-filled, #F59E0B)}.rating-group .star:hover mat-icon{color:var(--cc-sf-star-filled, #F59E0B)}.password-wrapper .password-toggle{right:.625rem;top:50%;transform:translateY(-50%);line-height:1;transition:color var(--cc-sf-input-transition, .2s ease)}.password-wrapper .password-toggle mat-icon.eye-icon{font-size:1.125rem;width:1.125rem;height:1.125rem;line-height:1.125rem}.password-wrapper .password-toggle:hover{color:var(--cc-sf-label-color, #374151)}.password-wrapper .password-toggle:focus{outline:none}.group-section-wrapper .group-label{font-size:var(--cc-sf-section-label-size, 1rem);font-weight:var(--cc-sf-section-label-weight, 600);color:var(--cc-sf-section-label-color, #1F2937);margin:0 0 16px;padding-left:12px;padding-top:2px;padding-bottom:2px;border-left:var(--cc-sf-section-header-accent-width, 4px) solid var(--cc-sf-section-header-accent-color, #3B82F6);line-height:1.4}.group-section-wrapper .group-fields.sf-grid{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--cc-sf-grid-gap, 16px);align-items:start}@media(max-width:640px){.group-section-wrapper .group-fields.sf-grid{grid-template-columns:1fr}.group-section-wrapper .group-fields.sf-grid .sf-col{grid-column:span 12!important}}.group-section-wrapper .group-accordion-instance{border:var(--cc-sf-instance-border, 1px solid #E5E7EB);border-radius:var(--cc-sf-section-border-radius-inner, 8px);margin-bottom:8px;overflow:hidden;transition:border-color .2s ease}.group-section-wrapper .group-accordion-instance:last-of-type{margin-bottom:0}.group-section-wrapper .group-accordion-instance .group-accordion-header{display:flex;justify-content:space-between;align-items:center;padding:10px 14px;background:var(--cc-sf-repeater-accordion-header-bg, #F9FAFB);cursor:pointer;-webkit-user-select:none;user-select:none;transition:background .15s ease}.group-section-wrapper .group-accordion-instance .group-accordion-header:hover{background:var(--cc-sf-repeater-accordion-active-bg, #EFF6FF)}.group-section-wrapper .group-accordion-instance .group-accordion-header .instance-badge{width:22px;height:22px;border-radius:50%;background:var(--cc-sf-repeater-badge-bg, #E5E7EB);color:var(--cc-sf-repeater-badge-color, #374151);font-size:.75rem;font-weight:600;display:flex;align-items:center;justify-content:center;flex-shrink:0}.group-section-wrapper .group-accordion-instance .group-accordion-header .instance-title{font-size:var(--cc-sf-instance-num-size, .8125rem);font-weight:600;color:var(--cc-sf-repeater-accordion-header-color, #1F2937)}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-remove-btn{background:none;border:none;cursor:pointer;color:var(--cc-sf-btn-remove-color, #E53E3E);padding:4px;border-radius:4px;line-height:1;display:flex;align-items:center;transition:background .15s ease}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-remove-btn mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-remove-btn:hover{background:var(--cc-sf-btn-remove-hover-bg, #FED7D7)}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-chevron{font-size:1.25rem;width:1.25rem;height:1.25rem;line-height:1.25rem;color:var(--cc-sf-instance-num-color, #4B5563)}.group-section-wrapper .group-accordion-instance .group-accordion-body{padding:var(--cc-sf-instance-padding, 16px);background:var(--cc-sf-instance-bg, #F9FAFB);border-top:var(--cc-sf-instance-divider, 1px dashed #D1D5DB)}.group-section-wrapper .btn-add-group{display:flex;align-items:center;justify-content:center;gap:6px;width:100%;padding:10px 20px;margin-top:12px;background:var(--cc-sf-btn-add-bg, transparent);color:var(--cc-sf-btn-add-color, #3B82F6);border:var(--cc-sf-btn-add-border, 1px dashed #CBD5E1);border-radius:var(--cc-sf-btn-add-radius, 6px);cursor:pointer;font-family:inherit;font-size:var(--cc-sf-btn-font-size, .875rem);font-weight:var(--cc-sf-btn-font-weight, 600);transition:var(--cc-sf-btn-transition, all .2s ease)}.group-section-wrapper .btn-add-group mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.group-section-wrapper .btn-add-group:hover{background:var(--cc-sf-btn-add-hover-bg, #EFF6FF);border-color:var(--cc-sf-btn-add-hover-border, #BFDBFE)}.group-section-wrapper .group-instance:last-child{margin-bottom:0}.group-section-wrapper.multi-save-active{border:none;box-shadow:none;padding:0;background:transparent}.group-section-wrapper.multi-save-active .multi-save-header .btn-add-multi ::ng-deep button{color:var(--ms-btn-add-color, #3B82F6);font-weight:600;font-size:.875rem;padding:8px 12px}.group-section-wrapper.multi-save-active .multi-save-header .btn-add-multi ::ng-deep button:hover{color:var(--ms-btn-add-hover, #2563EB);background-color:var(--cc-sf-btn-add-hover-bg, #EFF6FF)}.group-section-wrapper.multi-save-active .group-instance.is-card{cursor:pointer;transition:all .2s ease-in-out}.group-section-wrapper.multi-save-active .group-instance.is-card:hover{box-shadow:var(--ms-card-shadow-hover, 0 8px 24px rgba(0, 0, 0, .08));border-color:var(--cc-sf-input-focus-border, #3B82F6)}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-content .card-title{white-space:nowrap;text-overflow:ellipsis}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-content .card-desc{line-height:1.4;display:-webkit-box;-webkit-line-clamp:1;line-clamp:1;-webkit-box-orient:vertical}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view.is-expanded .card-content .card-desc{-webkit-line-clamp:unset;line-clamp:unset}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon{font-size:22px;width:22px;height:22px;color:var(--cc-sf-hint-color, #9CA3AF);transition:color .2s}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon.icon-delete:hover{color:var(--cc-sf-error-border, #DC2626)}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon.icon-edit:hover{color:var(--cc-sf-input-focus-border, #3B82F6)}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon.icon-expand{color:var(--cc-sf-input-disabled-border, #E5E7EB)}.btn-remove{transition:var(--cc-sf-btn-transition, all .2s ease)}.btn-remove mat-icon{font-size:1rem;width:1rem;height:1rem;line-height:1rem}.btn-remove:hover{background:var(--cc-sf-btn-remove-hover-bg, #FED7D7)}.btn-add-group{font-weight:var(--cc-sf-btn-font-weight, 600);transition:var(--cc-sf-btn-transition, all .2s ease)}.btn-add-group mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.btn-add-group:hover{background:var(--cc-sf-btn-add-hover-bg, #EFF6FF);border-color:var(--cc-sf-btn-add-hover-border, #BFDBFE)}.upload-drop-zone{background-color:var(--cc-sf-dropzone-bg, #F8FAFC);border:var(--cc-sf-dropzone-border, 1.5px dashed #CBD5E1);border-radius:var(--cc-sf-dropzone-radius, 12px);transition:background-color .2s ease,border-color .2s ease}.upload-drop-zone:hover{background-color:var(--cc-sf-dropzone-hover-bg, #EFF6FF);border-color:var(--cc-sf-dropzone-hover-border, #93C5FD)}.upload-drop-zone.drag-over{background-color:var(--cc-sf-dropzone-hover-bg, #EFF6FF);border-color:var(--cc-sf-dropzone-over-border, #3B82F6);box-shadow:var(--cc-sf-dropzone-over-shadow, 0 0 0 4px rgba(59, 130, 246, .12))}.upload-drop-zone.is-invalid{border-color:var(--cc-sf-error-border, #DC2626);background-color:var(--cc-sf-error-bg, #FEF2F2)}.upload-icon-wrap .dropzone-icon-pill{width:52px;height:52px;border-radius:50%;background:var(--cc-sf-dropzone-icon-bg, rgba(59, 130, 246, .1))}.upload-icon-wrap mat-icon.upload-cloud-icon{font-size:28px;width:28px;height:28px;line-height:28px;color:var(--cc-sf-accent-color, #3B82F6)}.upload-main-text{color:var(--cc-sf-label-color, #1E293B)}.upload-sub-text{color:var(--cc-sf-hint-color, #64748B)}.upload-link{color:var(--cc-sf-dropzone-link-color, #3B82F6);font-weight:500}.upload-formats{color:var(--cc-sf-dropzone-link-color, #3B82F6)}.upload-size-badge{display:inline-block;padding:2px 8px;border-radius:20px;background:var(--cc-sf-input-disabled-bg, #F3F4F6);color:var(--cc-sf-hint-color, #6B7280);font-weight:500}.uploaded-item{background:var(--cc-sf-uploaded-item-bg, #ffffff);border:var(--cc-sf-uploaded-item-border, 1px solid #E2E8F0);border-radius:var(--cc-sf-uploaded-item-radius, 8px);transition:box-shadow .15s ease}.uploaded-item:hover{box-shadow:0 2px 6px #0000000f}.uploaded-item mat-icon.file-type-icon{font-size:20px;width:20px;height:20px;line-height:20px;flex-shrink:0;color:var(--cc-sf-hint-color, #64748B)}.uploaded-item .file-thumb{width:36px;height:36px;object-fit:cover;flex-shrink:0}.uploaded-item .file-info{min-width:0;gap:2px}.uploaded-item .file-info .file-name{white-space:nowrap;text-overflow:ellipsis}.uploaded-item .file-remove-btn{flex-shrink:0;width:32px;height:32px;background:none;border:none;cursor:pointer;color:var(--cc-sf-uploaded-remove-color, #94A3B8);padding:0;display:flex;align-items:center;justify-content:center;transition:color .15s ease,background .15s ease}.uploaded-item .file-remove-btn mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.uploaded-item .file-remove-btn:hover:not(:disabled){color:var(--cc-sf-uploaded-remove-hover-color, #DC2626);background:var(--cc-sf-uploaded-remove-hover-bg, #FEF2F2)}.uploaded-item .file-remove-btn:disabled{opacity:.4;cursor:not-allowed}.uploaded-item.uploading{background:var(--cc-sf-uploaded-uploading-bg, #F8FAFC);border-color:var(--cc-sf-uploaded-uploading-border, #CBD5E1);opacity:.85}.upload-spinner{width:20px;height:20px;flex-shrink:0;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}@keyframes cc-spin{to{transform:rotate(360deg)}}.uploading-label{font-style:italic}.input-group{align-items:stretch}.input-group .field-input{flex:1;width:auto}.input-prefix+.input-group .field-input{border-top-left-radius:0;border-bottom-left-radius:0}.input-group .field-input:has(+.input-suffix){border-top-right-radius:0;border-bottom-right-radius:0}.input-group .field-input.has-icon-right{padding-right:3rem}.input-group.readonly .field-input{cursor:default}.input-prefix,.input-suffix{display:flex!important;align-items:center;white-space:nowrap;padding:0 14px;background-color:var(--cc-sf-input-disabled-bg);border:1px solid var(--cc-sf-input-border);font-size:.875rem;color:var(--cc-sf-hint-color);-webkit-user-select:none;user-select:none}.input-prefix{border-right:none;border-top-left-radius:var(--cc-sf-input-radius, 8px);border-bottom-left-radius:var(--cc-sf-input-radius, 8px)}.input-suffix{border-left:none;border-top-right-radius:var(--cc-sf-input-radius, 8px);border-bottom-right-radius:var(--cc-sf-input-radius, 8px)}.readonly-icons{right:.875rem;top:50%;transform:translateY(-50%)}.readonly-icons mat-icon.lock-icon{font-size:1rem;width:1rem;height:1rem;line-height:1rem;opacity:.5;color:var(--cc-sf-hint-color, #6B7280)}.date-icon-wrapper{right:.5rem;top:50%;transform:translateY(-50%);pointer-events:auto}.date-icon-wrapper .mat-icon-button{width:32px;height:32px;line-height:32px}.subfields-group-wrapper .subfields-row{transition:all .2s ease}.subfields-group-wrapper .subfields-row.is-invalid .subfield-item ::ng-deep .field-input{border-color:var(--cc-sf-error-border, #DC2626);background-color:var(--cc-sf-error-bg, #FEF2F2)}.subfields-group-wrapper .subfields-row .subfield-item{min-width:0}.subfields-group-wrapper .subfields-row .subfield-item ::ng-deep .field-label{font-size:.75rem!important;margin-bottom:4px!important;font-weight:500!important;color:var(--cc-sf-hint-color, #6B7280)!important}.subfields-group-wrapper .subfields-row .subfield-separator{font-weight:700}.autocomplete-wrapper .ac-input{padding-left:40px!important}.autocomplete-wrapper .ac-search-icon{left:.75rem;width:1.1rem;height:1.1rem;line-height:1.1rem;z-index:1;transition:color var(--cc-sf-input-transition, .2s ease)}.autocomplete-wrapper .ac-clear-btn{right:.6rem;transition:color .15s ease,background .15s ease}.autocomplete-wrapper .ac-clear-btn mat-icon{font-size:1rem;width:1rem;height:1rem;line-height:1rem}.autocomplete-wrapper .ac-clear-btn:hover{color:var(--cc-sf-label-color, #374151);background:var(--cc-sf-input-disabled-bg, #F3F4F6)}.autocomplete-wrapper .ac-clear-btn:focus{outline:none}.autocomplete-wrapper:focus-within .ac-search-icon{color:var(--cc-sf-accent-color, #3B82F6)}.autocomplete-wrapper.is-invalid .ac-input{border-color:var(--cc-sf-error-border)!important;background-color:var(--cc-sf-error-bg)}.autocomplete-wrapper.readonly .ac-input{background-color:var(--cc-sf-input-disabled-bg);color:var(--cc-sf-input-disabled-color, #6B7280);cursor:not-allowed;border-color:var(--cc-sf-input-disabled-border)!important}.ac-no-results{font-style:italic}::ng-deep .mat-mdc-autocomplete-panel,::ng-deep .mat-autocomplete-panel{background:var(--cc-sf-input-bg, #ffffff)!important;border-radius:var(--cc-sf-input-radius, 9px)!important;border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)!important;box-shadow:0 8px 24px #0000001a,0 2px 6px #0000000f!important;padding:4px 0!important;min-width:200px}::ng-deep .mat-mdc-autocomplete-panel mat-option,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option,::ng-deep .mat-mdc-autocomplete-panel .mat-option,::ng-deep .mat-autocomplete-panel mat-option,::ng-deep .mat-autocomplete-panel .mat-mdc-option,::ng-deep .mat-autocomplete-panel .mat-option{background:var(--cc-sf-input-bg, #ffffff)!important;color:var(--cc-sf-label-color, #111827)!important;font-size:.875rem!important;padding:10px 16px!important;min-height:40px!important;line-height:1.4!important;display:flex!important;flex-direction:column!important;align-items:flex-start!important;transition:background var(--cc-sf-input-transition, .2s ease)!important}::ng-deep .mat-mdc-autocomplete-panel mat-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-mdc-autocomplete-panel .mat-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-autocomplete-panel mat-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-autocomplete-panel .mat-mdc-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-autocomplete-panel .mat-option:hover:not(.mat-option-disabled):not([disabled]){background:var(--cc-sf-input-disabled-bg, #F3F4F6)!important}::ng-deep .mat-mdc-autocomplete-panel mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-mdc-autocomplete-panel mat-option.mdc-list-item--selected,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mdc-list-item--selected,::ng-deep .mat-mdc-autocomplete-panel .mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-mdc-autocomplete-panel .mat-option.mdc-list-item--selected,::ng-deep .mat-autocomplete-panel mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-autocomplete-panel mat-option.mdc-list-item--selected,::ng-deep .mat-autocomplete-panel .mat-mdc-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-autocomplete-panel .mat-mdc-option.mdc-list-item--selected,::ng-deep .mat-autocomplete-panel .mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-autocomplete-panel .mat-option.mdc-list-item--selected{background:var(--cc-sf-dropzone-hover-bg, #EFF6FF)!important;color:var(--cc-sf-selected-color, #6366F1)!important;font-weight:600!important}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-option-label,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-option-label,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-option-label,::ng-deep .mat-autocomplete-panel mat-option .ac-option-label,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-option-label,::ng-deep .mat-autocomplete-panel .mat-option .ac-option-label{font-weight:500;color:var(--cc-sf-label-color, #111827);font-size:.875rem;display:block}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-display-fields,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-display-fields,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-display-fields,::ng-deep .mat-autocomplete-panel mat-option .ac-display-fields,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-display-fields,::ng-deep .mat-autocomplete-panel .mat-option .ac-display-fields{align-items:center;line-height:1}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-item,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-item,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-item,::ng-deep .mat-autocomplete-panel mat-option .ac-df-item,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-item,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-item{display:inline-flex;align-items:center;font-size:.72rem;color:var(--cc-sf-hint-color, #6B7280);gap:3px}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-chip,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-chip,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-chip,::ng-deep .mat-autocomplete-panel mat-option .ac-df-chip,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-chip,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-chip{background:var(--cc-sf-input-disabled-bg, #F3F4F6);border-radius:4px;padding:2px 6px}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-text,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-text,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-text,::ng-deep .mat-autocomplete-panel mat-option .ac-df-text,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-text,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-text{color:var(--cc-sf-hint-color, #6B7280)}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-avatar,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-avatar,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-avatar,::ng-deep .mat-autocomplete-panel mat-option .ac-df-avatar,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-avatar,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-avatar{width:24px;height:24px;border-radius:50%;object-fit:cover;border:1px solid var(--cc-sf-input-border, #D1D5DB);vertical-align:middle}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-label,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-label,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-label,::ng-deep .mat-autocomplete-panel mat-option .ac-df-label,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-label,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-label{font-weight:600;color:var(--cc-sf-hint-color, #9CA3AF);margin-right:2px}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-icon,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-icon,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-icon,::ng-deep .mat-autocomplete-panel mat-option .ac-df-icon,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-icon,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-icon{font-size:11px;width:11px;height:11px;line-height:11px;color:var(--cc-sf-hint-color, #9CA3AF);flex-shrink:0}.mu-layout{grid-template-columns:1fr 1fr;gap:32px}@media(max-width:768px){.mu-layout{grid-template-columns:1fr}}.mu-title{font-weight:700;line-height:1.3}.mu-badge{white-space:nowrap;flex-shrink:0}.mu-description{line-height:1.6}.mu-feature-item .mu-check{width:16px;height:16px;line-height:16px;flex-shrink:0}.mu-right{min-height:260px}.mu-right-empty{min-height:250px;max-width:400px;box-shadow:0 2px 10px #0000000d;transition:box-shadow .2s ease}.mu-right-empty:hover{cursor:pointer;box-shadow:0 4px 16px #0000001a}.mu-right-empty .mu-right-empty-icon{width:52px;height:52px;line-height:52px;opacity:.3}.mu-right-empty p{margin:0;font-size:.85rem}.media-add-container{display:inline-block}.media-add-container ::ng-deep button{display:flex;align-items:center;gap:6px}.media-add-container ::ng-deep button .menu-chevron{width:18px;height:18px;line-height:18px;transition:transform .2s ease}.media-dropdown{top:calc(100% + 6px);left:0;z-index:200;min-width:240px;box-shadow:var(--mu-dropdown-shadow, 0 8px 32px rgba(0, 0, 0, .12));animation:mu-fade-in .15s ease}@keyframes mu-fade-in{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}.media-dropdown-item{transition:background .15s ease}.media-dropdown-item:last-child{border-bottom:none}.media-dropdown-item:hover{background:var(--cc-sf-dropzone-hover-bg, #F0F9FF)}.media-drop-icon{width:36px;height:36px;flex-shrink:0}.media-drop-icon mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.media-drop-icon--video{background:var(--mu-icon-video-bg, #FFF0F0);color:var(--mu-icon-video-color, #EF4444)}.media-drop-icon--device{background:var(--mu-icon-device-bg, #EFF6FF);color:var(--mu-icon-device-color, #3B82F6)}.media-drop-icon--library{background:var(--mu-icon-library-bg, #F0FDF4);color:var(--mu-icon-library-color, #22C55E)}.media-drop-text{gap:2px}.youtube-input-panel{animation:mu-fade-in .18s ease}.youtube-panel-label mat-icon{font-size:18px;width:18px;height:18px;line-height:18px;color:var(--mu-icon-video-color, #EF4444)}.youtube-input-row{align-items:stretch}.media-menu-backdrop{position:fixed;inset:0;z-index:100}.media-upload-status{animation:mu-fade-in .2s ease}.media-upload-status .status-icon{width:18px;height:18px;line-height:18px}.media-carousel-main{max-width:400px;height:var(--mu-carousel-height, 250px)}.carousel-viewer{inset:0}.carousel-viewer .carousel-image{object-fit:cover}.carousel-viewer .carousel-spinner{width:36px;height:36px;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}.carousel-nav{top:50%;transform:translateY(-50%);z-index:10;width:40px;height:40px;box-shadow:0 2px 8px #0003;transition:background .2s ease,opacity .2s ease;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.carousel-nav mat-icon{font-size:22px;width:22px;height:22px;line-height:22px;color:#1e293b}.carousel-nav:hover:not(:disabled){background:#fff}.carousel-nav:disabled{opacity:.3;cursor:not-allowed}.carousel-nav--prev{left:12px}.carousel-nav--next{right:12px}.carousel-remove-btn{top:10px;right:10px;z-index:10;width:32px;height:32px;transition:background .2s ease}.carousel-remove-btn mat-icon{font-size:18px;width:18px;height:18px;line-height:18px;color:#fff}.carousel-remove-btn:hover:not(:disabled){background:#dc2626d9}.carousel-remove-btn:disabled{opacity:.4;cursor:not-allowed}.carousel-dots{bottom:10px;left:50%;transform:translate(-50%);z-index:10}.carousel-dot{width:8px;height:8px;transition:background .2s ease,transform .2s ease}.carousel-dot.active{background:#fff;transform:scale(1.3)}.media-thumbnail-strip{max-width:400px;overflow-x:auto}.media-thumbnail-strip::-webkit-scrollbar{height:4px}.media-thumbnail-strip::-webkit-scrollbar-thumb{background:var(--cc-sf-input-disabled-border, #D1D5DB);border-radius:2px}.media-thumb{flex-shrink:0;width:72px;height:52px;transition:border-color .2s ease,transform .15s ease}.media-thumb.active{border-color:var(--mu-thumb-active-border, var(--cc-sf-accent-color, #3B82F6));transform:scale(1.04)}.media-thumb:hover:not(.active){border-color:var(--cc-sf-input-hover-border, #9CA3AF)}.media-thumb .thumb-img{object-fit:cover}.media-thumb .thumb-yt-placeholder mat-icon{font-size:28px;width:28px;height:28px;line-height:28px}.media-thumb .thumb-uploading .thumb-spinner{width:20px;height:20px;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}.media-library-portal-host{position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center;visibility:hidden;opacity:0;pointer-events:none;transition:opacity .2s ease,visibility .2s ease}.media-library-portal-host.is-open{visibility:visible;opacity:1;pointer-events:auto}.media-library-portal-host.is-open .media-library-modal{transform:scale(1) translateY(0)}.media-library-overlay{position:absolute;inset:0;background:#00000080;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.media-library-modal{position:relative;z-index:10000;width:90vw;max-width:900px;max-height:90vh;background:#fff;border-radius:16px;box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a;transform:scale(.95) translateY(10px);transition:transform .3s cubic-bezier(.175,.885,.32,1.275)}.library-modal-header{flex-shrink:0}.library-modal-title{font-weight:700}.library-modal-subtitle{line-height:1.5;max-width:600px}.library-close-btn{width:32px;height:32px;transition:background .15s ease,color .15s ease}.library-close-btn mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.library-close-btn:hover{background:var(--cc-sf-input-disabled-bg, #F3F4F6);color:var(--cc-sf-label-color, #374151)}.library-loading mat-icon,.library-empty mat-icon{font-size:40px;width:40px;height:40px;line-height:40px;opacity:.5}.lib-spinner{width:36px;height:36px;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}.library-error{flex-shrink:0}.library-error mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.library-grid{grid-template-columns:repeat(5,1fr);max-height:50vh;overflow-y:auto}.library-grid::-webkit-scrollbar{width:8px}.library-grid::-webkit-scrollbar-track{background:#f1f1f1}.library-grid::-webkit-scrollbar-thumb{background:#c1c1c1;border-radius:10px;border:2px solid #F1F1F1}.library-grid-item{aspect-ratio:1/1;transition:all .3s cubic-bezier(.4,0,.2,1);box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.library-grid-item:hover{transform:translateY(-4px) scale(1.02);box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a}.library-grid-item.selected{border-color:var(--cc-sf-accent-color, #3B82F6)}.library-grid-item.selected .library-grid-img{opacity:.7}.library-grid-item:hover .library-overlay-hover{opacity:1}.library-grid-img{object-fit:cover}.library-overlay-hover{inset:0;opacity:0;transition:opacity .15s ease}.library-check{top:6px;right:6px;width:22px;height:22px;box-shadow:0 1px 4px #00000026}.library-check mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}.library-modal-footer{flex-shrink:0}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-primary{background-color:var(--cc-sf-accent-color, #3B82F6)!important;border-color:var(--cc-sf-accent-color, #3B82F6)!important;color:#fff!important;font-weight:600;padding-left:32px;padding-right:32px}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-primary:hover{background-color:var(--cc-sf-btn-primary-hover-bg, #2563EB)!important}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-primary:disabled{background-color:#93c5fd!important;cursor:not-allowed}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-outline{font-weight:600;padding-left:24px;padding-right:24px;border-color:#d1d5db;color:#374151}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-outline:hover{background-color:#f9fafb}.location-subtitle{line-height:1.5}.loc-tab-btn ::ng-deep button{width:100%}.loc-tab-btn ::ng-deep button:not(.cc-btn-warning){background-color:var(--cc-sf-input-bg, #ffffff)!important;color:var(--cc-sf-label-color, #000000)!important;border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)}.loc-tab-btn ::ng-deep button:not(.cc-btn-warning):hover{background-color:var(--cc-sf-input-disabled-bg, #F3F4F6)!important}.loc-venue-item{transition:box-shadow .15s ease,border-color .15s ease}.loc-venue-item:hover{box-shadow:0 2px 8px #0000000f;border-color:var(--cc-sf-input-hover-border, #9CA3AF)}.loc-venue-search-icon{width:18px;height:18px;line-height:18px;flex-shrink:0}.loc-venue-text{white-space:nowrap;text-overflow:ellipsis}.loc-action-btn{transition:background .15s ease,color .15s ease;flex-shrink:0}.loc-action-btn mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}.loc-action-btn.loc-delete-btn{color:var(--loc-delete-color, #E53E3E)}.loc-action-btn.loc-delete-btn:hover{background:var(--cc-sf-error-bg, #FEF2F2)}.loc-action-btn.loc-edit-btn{color:var(--cc-sf-hint-color, #9CA3AF)}.loc-action-btn.loc-edit-btn:hover{color:var(--cc-sf-input-focus-border, #3B82F6);background:var(--cc-sf-dropzone-hover-bg, #EFF6FF)}.loc-search-icon{left:.75rem;width:1.1rem;height:1.1rem;line-height:1.1rem;z-index:1}.loc-suggestions-panel{top:calc(100% + 4px);left:0;right:0;z-index:300;box-shadow:0 8px 24px #0000001a;animation:mu-fade-in .15s ease;max-height:260px;overflow-y:auto}.loc-suggestion-item{transition:background .12s ease}.loc-suggestion-item:hover,.loc-suggestion-item:focus{background:var(--loc-suggestion-hover-bg, #F0F9FF)}.loc-suggestion-item:not(:last-child){border-bottom:1px solid var(--cc-sf-input-disabled-border, #F3F4F6)}.loc-suggestion-icon{width:18px;height:18px;line-height:18px;flex-shrink:0}.loc-suggestion-text{white-space:nowrap;text-overflow:ellipsis}.loc-add-btn{transition:opacity .15s ease}.loc-add-btn mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.loc-add-btn:hover{opacity:.8}.loc-map-container{box-shadow:0 2px 10px #0000000f}.loc-tba-panel{min-height:120px}.loc-tba-icon{width:40px;height:40px;line-height:40px;opacity:.6}.loc-tba-text{line-height:1.6;max-width:360px}.radio-label{display:flex!important}.radio-label .option-content{padding-left:10px}\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$3.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$3.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: i1$3.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$3.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$3.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$3.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$3.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1$3.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$3.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i5.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "component", type: i5.MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "component", type: i7.MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: i7.MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: i7.MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "directive", type: i8.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: i2.MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: i9.MatAutocomplete, selector: "mat-autocomplete", inputs: ["aria-label", "aria-labelledby", "displayWith", "autoActiveFirstOption", "autoSelectActiveOption", "requireSelection", "panelWidth", "disableRipple", "class", "hideSingleSelectionIndicator"], outputs: ["optionSelected", "opened", "closed", "optionActivated"], exportAs: ["matAutocomplete"] }, { kind: "directive", type: i9.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "component", type: ButtonComponent, selector: "lib-button", inputs: ["variant", "type", "disabled", "width", "height", "borderRadius", "fontSize", "fontWeight", "backgroundColor", "color", "border", "icon", "labels"] }, { kind: "component", type: i11.QuillEditorComponent, selector: "quill-editor" }, { kind: "component", type: FormFieldComponent, selector: "lib-form-field", inputs: ["config", "controller", "formGroup", "allowMulti"] }, { kind: "pipe", type: TrustedUrlPipe, name: "trustedUrl" }] });
|
|
2388
2533
|
}
|
|
2389
2534
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FormFieldComponent, decorators: [{
|
|
2390
2535
|
type: Component,
|
|
2391
|
-
args: [{ selector: 'lib-form-field', standalone: false, template: "<div class=\"form-field mb-16px\" *ngIf=\"isVisible\" [class.has-error]=\"errorMessage\">\n\n <!-- \u2550\u2550 ROW Layout \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isRow\" class=\"form-row grid-row\">\n <ng-container *ngFor=\"let child of config.children\">\n <div class=\"row-field\" [style.gridColumn]=\"'span ' + getChildColSpan(child)\" *ngIf=\"child.isEnabled !== false\">\n <lib-form-field [config]=\"child\" [controller]=\"controller\" [formGroup]=\"formGroup\" [allowMulti]=\"allowMulti\">\n </lib-form-field>\n </div>\n </ng-container>\n </div>\n\n <!-- \u2550\u2550 GROUP \u2014 allowMulti (repeater) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isGroup && config.sectionConfig?.allowMulti\"\n class=\"group-section-wrapper mb-20px\"\n [class.multi-save-active]=\"config.sectionConfig?.multiSaveConfig?.active\">\n\n <!-- Multi-Save: header row with label + top-right Add button -->\n <div class=\"multi-save-header d-flex justify-content-between align-items-center mb-24\"\n *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\n <h3 class=\"group-label\" *ngIf=\"config.sectionConfig?.label\">{{ config.sectionConfig!.label }}</h3>\n <lib-button [variant]=\"'outline'\" [icon]=\"{type: 'material', value: 'add'}\" (click)=\"addGroupInstance()\"\n class=\"btn-add-multi\">\n {{ addMultiLabel }}\n </lib-button>\n </div>\n\n <!-- Standard heading (non-multiSave) -->\n <h3 class=\"group-label\"\n *ngIf=\"config.sectionConfig?.label && !config.sectionConfig?.multiSaveConfig?.active\">{{\n config.sectionConfig!.label }}</h3>\n\n <!-- \u2500\u2500 Standard (non-multiSave) repeater: accordion instances \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <ng-container *ngIf=\"!config.sectionConfig?.multiSaveConfig?.active\">\n <div *ngFor=\"let instance of instanceList; trackBy: trackByInstanceId; let i = index\"\n class=\"group-accordion-instance\"\n [class.is-expanded]=\"isGroupExpanded(i)\">\n\n <!-- Accordion header -->\n <div class=\"group-accordion-header\" (click)=\"toggleGroupAccordion(i)\"\n role=\"button\" [attr.aria-expanded]=\"isGroupExpanded(i)\">\n <div class=\"accordion-header-left d-flex align-items-center gap-10\">\n <span class=\"instance-badge\">{{ i + 1 }}</span>\n <span class=\"instance-title\">{{ config.sectionConfig!.label }} #{{ i + 1 }}</span>\n </div>\n <div class=\"accordion-header-right d-flex align-items-center gap-6\">\n <button type=\"button\" class=\"accordion-remove-btn\"\n *ngIf=\"instanceList.length > 1\"\n (click)=\"$event.stopPropagation(); removeGroupInstance(i)\"\n aria-label=\"Remove\">\n <mat-icon>delete_outline</mat-icon>\n </button>\n <mat-icon class=\"accordion-chevron\">\n {{ isGroupExpanded(i) ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}\n </mat-icon>\n </div>\n </div>\n\n <!-- Accordion body (always mounted so form controls survive collapse) -->\n <div class=\"group-accordion-body\" [hidden]=\"!isGroupExpanded(i)\">\n <div class=\"group-fields sf-grid\">\n <ng-container *ngFor=\"let field of config.sectionConfig!.children\">\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\"\n *ngIf=\"field.isEnabled !== false\">\n <lib-form-field [config]=\"field\" [controller]=\"controller\" [formGroup]=\"instance.fg\"\n [allowMulti]=\"true\">\n </lib-form-field>\n </div>\n </ng-container>\n </div>\n </div>\n </div>\n\n <!-- Full-width dashed Add button -->\n <button type=\"button\" class=\"btn-add-group\" (click)=\"addGroupInstance()\">\n <mat-icon>add</mat-icon> {{ addLabel }} {{ config.sectionConfig!.label }}\n </button>\n </ng-container>\n\n <!-- \u2500\u2500 MultiSave: card instances \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <ng-container *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\n <div *ngFor=\"let instance of instanceList; trackBy: trackByInstanceId; let i = index\"\n class=\"group-instance position-relative mb-16 overflow-hidden\"\n [class.is-editing]=\"instance.isEditing\"\n [class.is-card]=\"!instance.isEditing\">\n\n <!-- Edit / new form view -->\n <div [hidden]=\"!instance.isEditing\">\n <div class=\"group-fields sf-grid\">\n <ng-container *ngFor=\"let field of config.sectionConfig!.children\">\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\"\n *ngIf=\"field.isEnabled !== false\">\n <lib-form-field [config]=\"field\" [controller]=\"controller\" [formGroup]=\"instance.fg\"\n [allowMulti]=\"true\">\n </lib-form-field>\n </div>\n </ng-container>\n </div>\n\n <!-- Save / Cancel -->\n <div class=\"group-footer d-flex justify-content-end align-items-center gap-16 p-0-24\"\n *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"multiSaveError\">{{ multiSaveError }}</span>\n <div class=\"footer-actions d-flex gap-12\">\n <lib-button [variant]=\"'outline'\" (click)=\"cancelGroupInstance(i)\">Cancel</lib-button>\n <lib-button [variant]=\"'primary'\" (click)=\"saveGroupInstance(i)\">Save</lib-button>\n </div>\n </div>\n </div>\n\n <!-- Card view (saved state) -->\n <ng-container *ngIf=\"!instance.isEditing\">\n <div class=\"card-view d-flex justify-content-between align-items-center p-18px-24px\"\n [class.is-expanded]=\"instance.isExpanded\">\n <div class=\"card-content flex-1 d-flex flex-column gap-4 overflow-hidden\">\n <span class=\"card-title font-weight-600 overflow-hidden fs-1rem c-111827\">{{\n instance.fg.get(config.sectionConfig!.multiSaveConfig!.summaryField || '')?.value\n || '\u2014' }}</span>\n </div>\n <div class=\"card-actions d-flex align-items-center gap-16 ml-20\">\n <mat-icon class=\"icon-delete\" (click)=\"removeGroupInstance(i, true)\">delete_outline</mat-icon>\n <mat-icon class=\"icon-edit\" (click)=\"editGroupInstance(i)\">edit_outline</mat-icon>\n <mat-icon class=\"icon-expand\" (click)=\"toggleExpandGroupInstance(i)\">\n {{ instance.isExpanded ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}\n </mat-icon>\n </div>\n </div>\n </ng-container>\n </div>\n </ng-container>\n </div>\n\n <!-- \u2550\u2550 GROUP \u2014 single (non-repeater) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isGroup && config.sectionConfig && !config.sectionConfig.allowMulti\"\n class=\"group-section-wrapper mb-20px\">\n <h3 class=\"group-label\" *ngIf=\"config.sectionConfig.label\">{{ config.sectionConfig.label }}</h3>\n <div class=\"group-fields sf-grid\">\n <ng-container *ngFor=\"let field of config.sectionConfig.children\">\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\" *ngIf=\"field.isEnabled !== false\">\n <lib-form-field [config]=\"field\" [controller]=\"controller\" [formGroup]=\"groupFormGroup\" [allowMulti]=\"false\">\n </lib-form-field>\n </div>\n </ng-container>\n </div>\n </div>\n\n\n <!-- \u2550\u2550 Text Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isTextField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <textarea *ngIf=\"config.subType === 'LONG'\" class=\"field-input textarea\" [placeholder]=\"config.placeholder || ''\"\n [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\" rows=\"4\">\n </textarea>\n\n <!-- Password input with show/hide toggle -->\n <div *ngIf=\"config.subType === 'PASSWORD'\" class=\"password-wrapper position-relative d-flex align-items-center\">\n <input [type]=\"showPassword ? 'text' : 'password'\" class=\"field-input password-input\"\n [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\n <button type=\"button\"\n class=\"password-toggle position-absolute cursor-pointer d-flex align-items-center justify-content-center b-none border-none c-6B7280 p-0-25rem\"\n (click)=\"showPassword = !showPassword\" tabindex=\"-1\"\n [attr.aria-label]=\"showPassword ? 'Hide password' : 'Show password'\">\n <mat-icon class=\"eye-icon\">{{ showPassword ? 'visibility' : 'visibility_off' }}</mat-icon>\n </button>\n </div>\n\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\n <span class=\"input-prefix br-none\" *ngIf=\"config.prefix\">{{ config.prefix }}</span>\n\n <input *ngIf=\"config.subType !== 'LONG' && config.subType !== 'PASSWORD'\"\n [type]=\"config.subType === 'EMAIL' ? 'email' : config.subType === 'PHONE' ? 'tel' : 'text'\" class=\"field-input\"\n [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\"\n [readonly]=\"config.readonly\">\n\n <span class=\"input-suffix d-flex align-items-center font-weight-500\" *ngIf=\"config.suffix\">{{ config.suffix\n }}</span>\n\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\n <mat-icon class=\"lock-icon\">lock</mat-icon>\n </div>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n <div class=\"char-count-hint font-poppins font-weight-400 text-14 text-right c-6B7280\" *ngIf=\"showCharCount\">\n {{ remainingCharacters }} characters remaining\n </div>\n </div>\n\n <!-- \u2550\u2550 Number Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isNumberField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\n <span class=\"input-prefix br-none\" *ngIf=\"config.prefix\">{{ config.prefix }}</span>\n\n <input type=\"number\" class=\"field-input\" [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\"\n [min]=\"config.numberConfig?.min ?? null\" [max]=\"config.numberConfig?.max ?? null\"\n [step]=\"config.numberConfig?.step || 1\" [class.is-invalid]=\"errorMessage\" [readonly]=\"config.readonly\">\n\n <span class=\"input-suffix d-flex align-items-center font-weight-500\" *ngIf=\"config.suffix\">{{ config.suffix\n }}</span>\n\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\n <mat-icon class=\"lock-icon\">lock</mat-icon>\n </div>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Date Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isDateField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\n <input matInput [matDatepicker]=\"datePicker\" class=\"field-input date-input has-icon-right\"\n [formControlName]=\"config.name!\" [min]=\"config.dateConfig?.minDate\" [max]=\"config.dateConfig?.maxDate\"\n [class.is-invalid]=\"errorMessage\" [placeholder]=\"config.placeholder || ''\" [readonly]=\"config.readonly\"\n (click)=\"!config.readonly && datePicker.open()\">\n <div class=\"date-icon-wrapper position-absolute d-flex align-items-center justify-content-center\"\n *ngIf=\"!config.readonly\">\n <mat-datepicker-toggle matSuffix [for]=\"datePicker\"></mat-datepicker-toggle>\n </div>\n <mat-datepicker #datePicker></mat-datepicker>\n\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\n <mat-icon class=\"lock-icon\">lock</mat-icon>\n </div>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Time Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isTimeField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\n <input type=\"time\" class=\"field-input time-input\" [formControlName]=\"config.name!\"\n [class.is-invalid]=\"errorMessage\" [readonly]=\"!!config.readonly\">\n\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\n <mat-icon class=\"lock-icon\">lock</mat-icon>\n </div>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Autocomplete \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isAutocomplete\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <!-- Hidden real control (stores the code value) -->\n <input type=\"hidden\" [formControlName]=\"config.name!\">\n\n <div class=\"autocomplete-wrapper position-relative d-flex align-items-center w-100\"\n [class.is-invalid]=\"errorMessage\" [class.readonly]=\"config.readonly\">\n <!-- Search icon -->\n <mat-icon class=\"ac-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">search</mat-icon>\n\n <input class=\"field-input ac-input\" [formControl]=\"autocompleteInputCtrl\" [matAutocomplete]=\"auto\"\n [placeholder]=\"config.placeholder || 'Search\u2026'\" [readonly]=\"!!config.readonly\" [class.is-invalid]=\"errorMessage\"\n (blur)=\"onAutocompleteClear()\" autocomplete=\"off\">\n\n <!-- Clear button -->\n <button type=\"button\"\n class=\"ac-clear-btn position-absolute d-flex align-items-center justify-content-center cursor-pointer rounded-50 b-none border-none c-9CA3AF p-0-2rem\"\n *ngIf=\"autocompleteInputCtrl.value && !config.readonly\"\n (click)=\"autocompleteInputCtrl.setValue(''); updateValue(null)\" tabindex=\"-1\" aria-label=\"Clear\">\n <mat-icon>close</mat-icon>\n </button>\n\n <mat-autocomplete #auto=\"matAutocomplete\" [panelWidth]=\"'auto'\">\n <mat-option *ngFor=\"let option of filteredOptions\" [value]=\"option.label\"\n (onSelectionChange)=\"onAutocompleteSelected(option)\">\n <span class=\"ac-option-label\">{{ option.label }}</span>\n\n <!-- Dynamic display fields (image / email / phone / text) -->\n <div class=\"ac-display-fields d-flex flex-wrap gap-6 mt-2\" *ngIf=\"option['displayMeta']?.length\">\n <ng-container *ngFor=\"let field of option['displayMeta']\">\n\n <!-- Image avatar -->\n <span *ngIf=\"field.type === 'image' && field.value\" class=\"ac-df-item ac-df-image\">\n <img [src]=\"field.value\" [alt]=\"field.label || 'image'\" class=\"ac-df-avatar\">\n </span>\n\n <!-- Email -->\n <span *ngIf=\"field.type === 'email' && field.value\" class=\"ac-df-item ac-df-chip\">\n <mat-icon class=\"ac-df-icon\">mail_outline</mat-icon>\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\n {{ field.value }}\n </span>\n\n <!-- Phone -->\n <span *ngIf=\"field.type === 'phone' && field.value\" class=\"ac-df-item ac-df-chip\" [class]=\"field.className\">\n <mat-icon class=\"ac-df-icon\">phone</mat-icon>\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\n {{ field.value }}\n </span>\n\n <!-- Custom / Icon-based / Generic Text -->\n <span *ngIf=\"field.type !== 'image' && field.type !== 'email' && field.type !== 'phone' && field.value\" \n class=\"ac-df-item\" [class.ac-df-chip]=\"!!field.icon\" [class]=\"field.className\">\n <mat-icon class=\"ac-df-icon\" *ngIf=\"field.icon\">{{ field.icon }}</mat-icon>\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\n {{ field.value }}\n </span>\n\n </ng-container>\n </div>\n </mat-option>\n <mat-option *ngIf=\"filteredOptions.length === 0\" disabled class=\"ac-no-results fs-0-8125rem c-6B7280\">\n No results found\n </mat-option>\n </mat-autocomplete>\n\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\n <mat-icon class=\"lock-icon\">lock</mat-icon>\n </div>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Dropdown \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isDropdown\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <select *ngIf=\"config.subType === 'SINGLE'\" class=\"field-input\" [formControlName]=\"config.name!\"\n [class.is-invalid]=\"errorMessage\">\n <option [ngValue]=\"null\" disabled selected>{{ config.placeholder || 'Select' }}</option>\n <option *ngFor=\"let option of config.optionConfig?.optionList\" [value]=\"option.code\">\n {{ option.label }}\n </option>\n </select>\n\n <!-- MULTIPLE SELECT: custom panel with checkboxes -->\n <div *ngIf=\"config.subType === 'MULTIPLE'\" class=\"multi-select-wrapper\"\n [class.is-invalid]=\"errorMessage\">\n\n <div class=\"field-input multi-select-trigger d-flex align-items-center justify-content-between cursor-pointer\"\n [class.ms-open]=\"isMultiDropdownOpen\"\n (click)=\"toggleMultiDropdown($event)\">\n <span *ngIf=\"multiSelectedCount > 0\" class=\"multi-select-value fs-0-9rem\">\n {{ multiSelectedCount }} selected\n </span>\n <span *ngIf=\"multiSelectedCount === 0\" class=\"multi-select-placeholder\">\n {{ config.placeholder || selectPlaceholderLabel }}\n </span>\n <mat-icon class=\"multi-select-arrow\">\n {{ isMultiDropdownOpen ? expandLessLabel : expandMoreLabel }}\n </mat-icon>\n </div>\n\n <div class=\"multi-select-panel\" *ngIf=\"isMultiDropdownOpen\"\n (click)=\"$event.stopPropagation()\">\n <label *ngFor=\"let option of config.optionConfig?.optionList\"\n class=\"multi-select-option d-flex align-items-center gap-8 cursor-pointer\">\n <input type=\"checkbox\"\n [checked]=\"isChecked(option.code)\"\n [disabled]=\"!!config.disabled\"\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\">\n <span class=\"fs-0-875rem c-111827\">{{ option.label }}</span>\n </label>\n <div *ngIf=\"!config.optionConfig?.optionList?.length\"\n class=\"multi-select-empty fs-0-875rem c-6B7280\">\n {{ noOptionsAvailableLabel }}\n </div>\n </div>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Radio \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isRadio\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"radio-group\" [class.is-invalid]=\"errorMessage\"\n [class]=\"config.optionConfig?.layout ? 'layout-' + config.optionConfig!.layout.toLowerCase() : ''\">\n <label *ngFor=\"let option of config.optionConfig?.optionList\" class=\"radio-label\"\n [class.card-item]=\"config.subType === 'CARD'\"\n [class.selected]=\"formGroup.get(config.name!)?.value === option.code\"\n [style.gridColumn]=\"config.optionConfig?.layout?.toUpperCase() === 'COLUMN' ? 'span ' + getOptionColSpan(option) : null\">\n <input type=\"radio\" [formControlName]=\"config.name!\" [value]=\"option.code\">\n <div class=\"option-content d-flex flex-column gap-4 flex-1 text-left\">\n <span class=\"label-text text-16 c-1F2937\">{{ option.label }}</span>\n <span class=\"option-hint text-13 color-gray\" *ngIf=\"option.hint\">{{ option.hint }}</span>\n </div>\n </label>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Checkbox \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isCheckbox\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label && config.subType === 'LIST'\"\n class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div *ngIf=\"config.subType === 'BOOL'\" class=\"checkbox-single\">\n <label class=\"checkbox-label d-flex align-items-center gap-8 cursor-pointer fs-0-875rem c-111827\">\n <input type=\"checkbox\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\n <span>{{ config.label }}</span>\n </label>\n </div>\n\n <div *ngIf=\"config.subType === 'LIST' || config.subType === 'CARD'\" class=\"checkbox-group d-flex flex-column gap-8\"\n [class.is-invalid]=\"errorMessage\"\n [class]=\"config.optionConfig?.layout ? 'layout-' + config.optionConfig!.layout.toLowerCase() : ''\">\n <label *ngFor=\"let option of config.optionConfig?.optionList\"\n class=\"checkbox-label d-flex align-items-center gap-8 cursor-pointer fs-0-875rem c-111827\"\n [class.card-item]=\"config.subType === 'CARD'\" [class.selected]=\"isChecked(option.code)\"\n [style.gridColumn]=\"config.optionConfig?.layout?.toUpperCase() === 'COLUMN' ? 'span ' + getOptionColSpan(option) : null\">\n <input type=\"checkbox\" [checked]=\"isChecked(option.code)\" [disabled]=\"!!config.disabled\"\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\">\n <div class=\"option-content d-flex flex-column gap-4 flex-1 text-left\">\n <span class=\"label-text text-16 c-1F2937\">{{ option.label }}</span>\n <span class=\"option-hint text-13 color-gray\" *ngIf=\"option.hint\">{{ option.hint }}</span>\n </div>\n </label>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Chip \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isChip\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"chip-group d-flex flex-wrap gap-8\" [class.is-invalid]=\"errorMessage\">\n <label *ngFor=\"let option of config.optionConfig?.optionList\"\n class=\"chip-label cursor-pointer p-6px-14px b-ffffff c-374151 b-1px-solid-D1D5DB br-20px fs-0-875rem\"\n [class.selected]=\"isChecked(option.code)\">\n <input type=\"checkbox\" [checked]=\"isChecked(option.code)\" [disabled]=\"!!config.disabled\"\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\" hidden>\n <span>{{ option.label }}</span>\n </label>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Switch \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isSwitch\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label class=\"switch-container d-flex justify-content-between align-items-center cursor-pointer\">\n <span class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">{{ config.label }}</span>\n <div class=\"switch position-relative\">\n <input type=\"checkbox\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\n <span class=\"slider position-absolute cursor-pointer\"></span>\n </div>\n </label>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Rich Text \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isRichText\" class=\"field-wrapper component-rich-text d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"rich-text-container\" [class.is-invalid]=\"errorMessage\">\n <quill-editor [formControlName]=\"config.name!\" class=\"rich-text-editor d-block w-100\"\n [placeholder]=\"config.richTextConfig?.placeholder || config.placeholder || ''\"\n [styles]=\"{height: config.richTextConfig?.height || '200px'}\">\n </quill-editor>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n <div class=\"char-count-hint font-poppins font-weight-400 text-14 text-right c-6B7280\" *ngIf=\"showCharCount\">\n {{ remainingCharacters }} characters remaining\n </div>\n </div>\n\n <!-- \u2550\u2550 Rating \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isRating\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"rating-group d-flex gap-4\" [class.is-invalid]=\"errorMessage\">\n <span *ngFor=\"let star of getStarArray()\" class=\"star d-inline-flex align-items-center cursor-pointer\"\n [class.filled]=\"isStarFilled(star)\" [class.half]=\"isStarHalf(star)\" (click)=\"onRatingChange(star, $event)\">\n <mat-icon>{{ isStarFilled(star) || isStarHalf(star) ? 'star' : 'star_border' }}</mat-icon>\n </span>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Generated Field (read-only) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isGenerated\" class=\"field-wrapper d-flex flex-column gap-6\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">{{ config.label\n }}</label>\n <div class=\"generated-value fs-0-875rem p-0-625rem-0-875rem b-F3F4F6 b-1px-solid-E5E7EB br-8px c-6B7280\">{{ value ||\n '-' }}</div>\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint\">{{ config.hint }}</span>\n </div>\n\n <!-- \u2550\u2550 File Upload \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isFileUpload\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <!-- Drop Zone -->\n <div\n class=\"upload-drop-zone d-flex flex-column align-items-center justify-content-center gap-8 cursor-pointer text-center p-32px-24px us-none\"\n [class.drag-over]=\"isDragOver\" [class.has-files]=\"value?.length\" [class.is-invalid]=\"errorMessage\"\n (dragover)=\"onDragOver($event)\" (dragleave)=\"onDragLeave($event)\" (drop)=\"onFileDrop($event)\"\n (click)=\"fileInput.click()\">\n\n <!-- Icon with accent-colour pill background -->\n <div class=\"upload-icon-wrap mb-4\">\n <div class=\"dropzone-icon-pill d-flex align-items-center justify-content-center\">\n <mat-icon class=\"upload-cloud-icon\">cloud_upload</mat-icon>\n </div>\n </div>\n\n <p class=\"upload-main-text font-weight-600 m-0 fs-0-9rem\">Drag & drop files here</p>\n <p class=\"upload-sub-text m-0 fs-0-8rem c-64748B\">or <span class=\"upload-link\">Browse files</span></p>\n <p class=\"upload-hint-text m-0 fs-0-78rem c-64748B\" *ngIf=\"config.attachmentConfig?.acceptLabel\">\n Supported: <span class=\"upload-formats font-weight-500\">{{ config.attachmentConfig!.acceptLabel }}</span>\n </p>\n <p class=\"upload-hint-text m-0 fs-0-78rem c-64748B\" *ngIf=\"!config.attachmentConfig?.acceptLabel && config.hint\">\n {{ config.hint }}\n </p>\n <span class=\"upload-size-badge fs-0-72rem\" *ngIf=\"config.attachmentConfig?.maxSizeMB\">\n Max {{ config.attachmentConfig!.maxSizeMB }}MB\n </span>\n\n <!-- Hidden native file input -->\n <input #fileInput type=\"file\" hidden [attr.multiple]=\"config.attachmentConfig?.multiple ? true : null\"\n [attr.accept]=\"config.attachmentConfig?.accept || null\" (change)=\"onFileSelected($event)\">\n </div>\n\n <!-- Uploaded file list -->\n <div class=\"uploaded-list d-flex flex-column gap-8 mt-10\" *ngIf=\"value?.length\">\n <div *ngFor=\"let f of value; let i = index\"\n class=\"uploaded-item d-flex align-items-center gap-10 p-10px-14px br-8px\"\n [class.uploading]=\"f.isUploading\">\n\n <!-- Uploading spinner -->\n <ng-container *ngIf=\"f.isUploading; else fileReady\">\n <div class=\"upload-spinner rounded-50 b-2px-solid-E2E8F0\"></div>\n <div class=\"file-info flex-1 d-flex flex-column\">\n <span class=\"file-name font-weight-500 overflow-hidden fs-0-85rem\" [title]=\"f.name\">{{ f.name }}</span>\n <span class=\"file-size uploading-label fs-0-72rem\">Uploading...</span>\n </div>\n </ng-container>\n\n <!-- Normal state once upload is done -->\n <ng-template #fileReady>\n <mat-icon class=\"file-type-icon\">{{ getFileIcon(f.type) }}</mat-icon>\n <img *ngIf=\"f.type?.startsWith('image') && f.dataUrl\" [src]=\"f.dataUrl\" class=\"file-thumb rounded-4\"\n alt=\"preview\">\n <div class=\"file-info flex-1 d-flex flex-column\">\n <span class=\"file-name font-weight-500 overflow-hidden fs-0-85rem\" [title]=\"f.name\">{{ f.name }}</span>\n <span class=\"file-size fs-0-72rem\">{{ formatFileSize(f.size) }}</span>\n </div>\n </ng-template>\n\n <!-- Compact icon-only remove button -->\n <button type=\"button\" class=\"file-remove-btn d-flex align-items-center justify-content-center rounded-50\"\n [disabled]=\"f.isUploading\" (click)=\"!f.isUploading && removeUploadedFile(i)\"\n aria-label=\"Remove file\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n\n <!-- Validation / file errors -->\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"fileUploadError\">{{ fileUploadError }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage && !fileUploadError\">{{ errorMessage }}</span>\n <span class=\"field-hint c-6B7280 fs-0-75rem\"\n *ngIf=\"config.hint && !errorMessage && !fileUploadError && !config.attachmentConfig?.acceptLabel\">\n {{ config.hint }}\n </span>\n </div>\n\n <!-- \u2550\u2550 Media Upload (Type 2) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isMediaUpload\" class=\"field-wrapper media-upload-wrapper d-flex flex-column gap-6 p-0 border-none b-none\"\n [formGroup]=\"formGroup\">\n\n <!-- Hidden file input lives outside *ngIf \u2014 triggered via ViewChild -->\n <input #mediaDeviceInput type=\"file\" hidden multiple accept=\"image/*\" (change)=\"onMediaFileSelected($event)\">\n\n <!-- Two-column layout -->\n <div class=\"mu-layout d-grid align-items-start\">\n\n <!-- \u2500\u2500 LEFT PANEL \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"mu-left d-flex flex-column gap-20\">\n\n <!-- Header: title + max-items badge -->\n <div class=\"mu-header d-flex align-items-start flex-wrap gap-10\">\n <h3 class=\"mu-title m-0 c-111827 fs-1-35rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </h3>\n <span\n class=\"mu-badge d-inline-flex align-items-center rounded-20 color-white font-weight-600 fs-0-72rem p-4px-12px b-111827\"\n *ngIf=\"config.attachmentConfig?.maxFiles\">\n {{ controller.labels['LBL_MEDIA_MAX_PREFIX'] || 'Maximum' }}\n {{ config.attachmentConfig?.maxFiles }}\n {{ controller.labels['LBL_MEDIA_MAX_SUFFIX'] || 'Image & Videos' }}\n </span>\n </div>\n\n <!-- Description -->\n <p class=\"mu-description m-0 fs-0-875rem c-6B7280\" *ngIf=\"config.attachmentConfig?.description\">\n {{ config.attachmentConfig?.description }}\n </p>\n <p class=\"mu-description m-0 fs-0-875rem c-6B7280\"\n *ngIf=\"!config.attachmentConfig?.description && controller.labels['LBL_MEDIA_DESC']\">\n {{ controller.labels['LBL_MEDIA_DESC'] }}\n </p>\n\n <!-- Feature bullet list -->\n <ul class=\"mu-features m-0 p-0 d-flex flex-column gap-8 ls-none\"\n *ngIf=\"config.attachmentConfig?.features?.length || controller.labels['LBL_MEDIA_FEATURE_1']\">\n <ng-container *ngIf=\"config.attachmentConfig?.features?.length\">\n <li *ngFor=\"let f of config.attachmentConfig?.features\"\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ f }}\n </li>\n </ng-container>\n <ng-container *ngIf=\"!config.attachmentConfig?.features?.length\">\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_1']\"\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_1'] }}\n </li>\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_2']\"\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_2'] }}\n </li>\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_3']\"\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_3'] }}\n </li>\n </ng-container>\n </ul>\n\n <!-- Backdrop to close dropdown on outside click -->\n <div class=\"media-menu-backdrop\" *ngIf=\"showMediaMenu\"\n (click)=\"$event.stopPropagation(); showMediaMenu = false\"></div>\n\n <!-- Add Media button + dropdown -->\n <div class=\"media-add-container position-relative\" (click)=\"showMediaMenu = !showMediaMenu\">\n <lib-button id=\"btn-add-media-{{ config.name }}\" [variant]=\"'warning'\"\n [icon]=\"{type: 'material', value: 'add_photo_alternate'}\">\n {{ controller.labels['LBL_ADD_MEDIA'] || 'Add media' }}\n <mat-icon class=\"menu-chevron fs-18px\">add</mat-icon>\n </lib-button>\n\n <div class=\"media-dropdown position-absolute rounded-12 overflow-hidden b-ffffff b-1px-solid-E5E7EB\"\n *ngIf=\"showMediaMenu\" role=\"menu\" (click)=\"$event.stopPropagation()\">\n <!-- Video -->\n <button id=\"btn-media-video-{{ config.name }}\" type=\"button\"\n class=\"media-dropdown-item d-flex align-items-center gap-12 w-100 cursor-pointer text-left b-none border-none p-12px-16px bb-1px-solid-F3F4F6\"\n (click)=\"onMediaMenuVideo(); showMediaMenu = false\" role=\"menuitem\">\n <span\n class=\"media-drop-icon media-drop-icon--video d-flex align-items-center justify-content-center rounded-8\"><mat-icon>videocam</mat-icon></span>\n <span class=\"media-drop-text d-flex flex-column flex-1\">\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\n controller.labels['LBL_MEDIA_VIDEO'] || 'Video' }}</span>\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_VIDEO_DESC'] || 'Add\n YouTube URL'\n }}</span>\n </span>\n </button>\n <!-- Device -->\n <button id=\"btn-media-device-{{ config.name }}\" type=\"button\"\n class=\"media-dropdown-item d-flex align-items-center gap-12 w-100 cursor-pointer text-left b-none border-none p-12px-16px bb-1px-solid-F3F4F6\"\n (click)=\"onMediaMenuDevice(); showMediaMenu = false\" role=\"menuitem\">\n <span\n class=\"media-drop-icon media-drop-icon--device d-flex align-items-center justify-content-center rounded-8\"><mat-icon>upload</mat-icon></span>\n <span class=\"media-drop-text d-flex flex-column flex-1\">\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\n controller.labels['LBL_MEDIA_DEVICE'] || 'Upload from device'\n }}</span>\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_DEVICE_DESC'] ||\n 'Select images from your\n computer' }}</span>\n </span>\n </button>\n <!-- Library -->\n <button id=\"btn-media-library-{{ config.name }}\" type=\"button\"\n class=\"media-dropdown-item d-flex align-items-center gap-12 w-100 cursor-pointer text-left b-none border-none p-12px-16px bb-1px-solid-F3F4F6\"\n (click)=\"onMediaMenuLibrary(); showMediaMenu = false\" role=\"menuitem\">\n <span\n class=\"media-drop-icon media-drop-icon--library d-flex align-items-center justify-content-center rounded-8\"><mat-icon>photo_library</mat-icon></span>\n <span class=\"media-drop-text d-flex flex-column flex-1\">\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\n controller.labels['LBL_MEDIA_LIBRARY'] || 'Upload from library'\n }}</span>\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_LIBRARY_DESC'] ||\n 'Choose from default\n images' }}</span>\n </span>\n </button>\n </div>\n </div>\n\n <!-- YouTube URL Input (inline below button) -->\n <div class=\"youtube-input-panel d-flex flex-column gap-8 p-16 rounded-10 b-FFFAF1 b-1px-solid-E5E7EB\"\n *ngIf=\"showYoutubeInput\">\n <label class=\"youtube-panel-label d-flex align-items-center gap-6 font-weight-600 fs-0-875rem c-111827\">\n {{ controller.labels['LBL_YOUTUBE_URL'] || 'Video URL' }}\n </label>\n <div class=\"youtube-input-row d-flex gap-8\">\n <input id=\"input-youtube-url-{{ config.name }}\" type=\"url\" class=\"field-input youtube-url-input\"\n [(ngModel)]=\"youtubeUrlInput\" [ngModelOptions]=\"{standalone: true}\"\n [placeholder]=\"controller.labels['PH_YOUTUBE_URL'] || 'Video URL'\" (keyup.enter)=\"addYoutubeMedia()\">\n <lib-button id=\"btn-add-youtube-{{ config.name }}\" [variant]=\"'secondary'\" (click)=\"addYoutubeMedia()\">\n {{ controller.labels['LBL_ADD'] || 'Add' }}\n </lib-button>\n </div>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"youtubeUrlError\">{{ youtubeUrlError }}</span>\n </div>\n\n <div\n class=\"media-upload-status d-flex align-items-center gap-8 mt-4 color-error rounded-8 font-weight-500 p-10px-14px b-FEF2F2 fs-0-85rem\"\n *ngIf=\"mediaUploadError\">\n <mat-icon class=\"status-icon fs-18px\">error_outline</mat-icon>\n <span>{{ mediaUploadError }}</span>\n </div>\n\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n </div>\n <!-- end left panel -->\n\n <!-- \u2500\u2500 RIGHT PANEL (carousel) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"mu-right d-flex flex-column gap-12\">\n\n <!-- Carousel (when items exist) -->\n <div class=\"media-carousel-section d-flex flex-column gap-12\" *ngIf=\"mediaItems.length\">\n <div\n class=\"media-carousel-main position-relative w-100 overflow-hidden d-flex align-items-center justify-content-center br-12px b-0F172A\">\n <button id=\"btn-carousel-prev-{{ config.name }}\" type=\"button\"\n class=\"carousel-nav carousel-nav--prev position-absolute rounded-50 cursor-pointer d-flex align-items-center justify-content-center border-none b-rgba-255-255-255-0-85\"\n (click)=\"mediaCarouselPrev()\" [disabled]=\"mediaCarouselIndex === 0\" aria-label=\"Previous\">\n <mat-icon>chevron_left</mat-icon>\n </button>\n\n <div class=\"carousel-viewer position-absolute d-flex align-items-center justify-content-center\"\n *ngFor=\"let item of mediaItems; let i = index\" [hidden]=\"i !== mediaCarouselIndex\">\n <div *ngIf=\"item.isUploading\"\n class=\"carousel-uploading d-flex flex-column align-items-center gap-12 c-94A3B8 fs-0-85rem\">\n <div class=\"carousel-spinner rounded-50 b-3px-solid-rgba-255-255-255-0-15\"></div>\n <span>{{ controller.labels['LBL_UPLOADING'] || 'Uploading\u2026' }}</span>\n </div>\n <ng-container *ngIf=\"!item.isUploading && item.mediaType === 'youtube'\">\n <iframe class=\"carousel-iframe w-100 h-100 br-12px\" [src]=\"item.url | trustedUrl\" frameborder=\"0\"\n allowfullscreen\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\">\n </iframe>\n </ng-container>\n <ng-container *ngIf=\"!item.isUploading && item.mediaType === 'image'\">\n <img class=\"carousel-image w-100 h-100 br-12px\" [src]=\"item.url\" alt=\"Media\">\n </ng-container>\n <button id=\"btn-remove-media-{{ config.name }}-{{ i }}\" type=\"button\"\n class=\"carousel-remove-btn position-absolute rounded-50 cursor-pointer d-flex align-items-center justify-content-center border-none b-rgba-0-0-0-0-55\"\n [disabled]=\"item.isUploading\" (click)=\"removeMediaItem(i)\" aria-label=\"Remove\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n\n <button id=\"btn-carousel-next-{{ config.name }}\" type=\"button\"\n class=\"carousel-nav carousel-nav--next position-absolute rounded-50 cursor-pointer d-flex align-items-center justify-content-center border-none b-rgba-255-255-255-0-85\"\n (click)=\"mediaCarouselNext()\" [disabled]=\"mediaCarouselIndex === mediaItems.length - 1\" aria-label=\"Next\">\n <mat-icon>chevron_right</mat-icon>\n </button>\n\n <div class=\"carousel-dots position-absolute d-flex gap-6\">\n <span *ngFor=\"let item of mediaItems; let i = index\"\n class=\"carousel-dot rounded-50 cursor-pointer b-rgba-255-255-255-0-45\"\n [class.active]=\"i === mediaCarouselIndex\" (click)=\"mediaGoTo(i)\"></span>\n </div>\n </div>\n\n <!-- Thumbnail strip -->\n <div class=\"media-thumbnail-strip d-flex flex-wrap gap-8 pb-4px\">\n <div *ngFor=\"let item of mediaThumbnails; let i = index\"\n class=\"media-thumb rounded-8 overflow-hidden cursor-pointer d-flex align-items-center justify-content-center b-2px-solid-transparent b-E2E8F0\"\n [class.active]=\"i === mediaCarouselIndex\" (click)=\"mediaGoTo(i)\">\n <div *ngIf=\"item.isUploading\"\n class=\"thumb-uploading d-flex align-items-center justify-content-center w-100 h-100\">\n <div class=\"thumb-spinner rounded-50 b-2px-solid-E2E8F0\"></div>\n </div>\n <img *ngIf=\"!item.isUploading && item.mediaType === 'youtube' && item.thumbnailUrl\"\n [src]=\"item.thumbnailUrl\" class=\"thumb-img w-100 h-100\" alt=\"Video thumbnail\">\n <div *ngIf=\"!item.isUploading && item.mediaType === 'youtube' && !item.thumbnailUrl\"\n class=\"thumb-yt-placeholder d-flex align-items-center justify-content-center w-100 h-100 b-1E293B c-EF4444\">\n <mat-icon>play_circle</mat-icon>\n </div>\n <img *ngIf=\"!item.isUploading && item.mediaType === 'image' && item.url\" [src]=\"item.url\"\n class=\"thumb-img w-100 h-100\" alt=\"Image thumbnail\">\n </div>\n </div>\n </div>\n\n <!-- Empty right-side placeholder -->\n <div\n class=\"mu-right-empty d-flex flex-column align-items-center justify-content-center gap-10 h-100 text-center p-24 br-12px b-FFFAF1 c-94A3B8 b-2px-dashed-CBD5E1\"\n *ngIf=\"!mediaItems.length\" (click)=\"onMediaMenuDevice()\">\n <mat-icon class=\"mu-right-empty-icon fs-52px\">perm_media</mat-icon>\n <p>{{ controller.labels['LBL_ADD_MEDIA'] || 'Add media' }}</p>\n </div>\n\n </div>\n <!-- end right panel -->\n\n </div><!-- end mu-layout -->\n </div>\n\n\n <!-- \u2550\u2550 Library Image Picker Modal \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <!-- Wrapper is always in DOM (hidden) so @ViewChild can move it to body -->\n <div #libraryModal class=\"media-library-portal-host\" [class.is-open]=\"showLibraryModal\">\n\n <!-- Backdrop -->\n <div class=\"media-library-overlay\" (click)=\"closeLibraryModal()\"></div>\n\n <!-- Modal card -->\n <div class=\"media-library-modal d-flex flex-column overflow-hidden b-ffffff br-16px\"\n role=\"dialog\" aria-modal=\"true\">\n <div class=\"library-modal-header d-flex align-items-start justify-content-between p-24px-28px bb-1px-solid-E5E7EB\">\n <div class=\"header-left d-flex flex-column gap-8\">\n <h3 class=\"library-modal-title m-0 color-dark fs-1-25rem\">\n {{ controller.labels['LBL_ADD_IMAGES'] || 'Add Images' }}\n </h3>\n <p class=\"library-modal-subtitle m-0 color-gray fs-0-85rem\">\n {{ controller.labels['LBL_LIBRARY_MODAL_DESC'] || 'Select images from your library.' }}\n </p>\n </div>\n <button id=\"btn-close-library-{{ config.name }}\" type=\"button\"\n class=\"library-close-btn d-flex align-items-center justify-content-center cursor-pointer rounded-50 border-none b-none c-9CA3AF\"\n (click)=\"closeLibraryModal()\" aria-label=\"Close\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n\n <!-- Loading -->\n <div class=\"library-loading\" *ngIf=\"libraryLoading\">\n <div class=\"lib-spinner rounded-50 b-3px-solid-E2E8F0\"></div>\n <span>{{ controller.labels['LBL_LOADING'] || 'Loading\u2026' }}</span>\n </div>\n\n <!-- Error -->\n <div class=\"library-error d-flex align-items-center gap-8 color-error b-FEF2F2 fs-0-875rem p-16px-24px\"\n *ngIf=\"libraryError && !libraryLoading\">\n <mat-icon>error_outline</mat-icon>\n {{ libraryError }}\n </div>\n\n <!-- Image grid -->\n <div class=\"library-grid d-grid gap-16 flex-1 p-28px b-F9FAFB\" *ngIf=\"!libraryLoading && libraryImages.length\">\n <div *ngFor=\"let img of libraryImages; let li = index\" id=\"lib-img-{{ config.name }}-{{ li }}\"\n class=\"library-grid-item position-relative rounded-12 overflow-hidden cursor-pointer bg-white b-3px-solid-transparent\"\n [class.selected]=\"isLibraryItemSelected(img)\" (click)=\"toggleLibraryItem(img)\">\n <img [src]=\"getLibraryItemUrl(img)\" class=\"library-grid-img w-100 h-100 d-block\" alt=\"Library image\">\n <div\n class=\"library-check position-absolute bg-white rounded-50 d-flex align-items-center justify-content-center c-3B82F6\"\n *ngIf=\"isLibraryItemSelected(img)\">\n <mat-icon>check_circle</mat-icon>\n </div>\n <div class=\"library-overlay-hover position-absolute b-rgba-59-130-246-0-12\"></div>\n </div>\n </div>\n\n <!-- Empty library -->\n <div\n class=\"library-empty d-flex flex-column align-items-center justify-content-center gap-12 flex-1 c-9CA3AF fs-0-875rem p-48px-24px\"\n *ngIf=\"!libraryLoading && !libraryError && libraryImages.length === 0\">\n <mat-icon>image_not_supported</mat-icon>\n <span>{{ controller.labels['LBL_LIBRARY_EMPTY'] || 'No images found in library.' }}</span>\n </div>\n\n <!-- Footer -->\n <div class=\"library-modal-footer d-flex align-items-center justify-content-end bg-white p-20px-28px bt-1px-solid-E5E7EB\">\n <div class=\"library-footer-actions d-flex gap-12\">\n <lib-button id=\"btn-library-cancel-{{ config.name }}\" [variant]=\"'outline'\" (click)=\"closeLibraryModal()\">\n {{ controller.labels['LBL_CANCEL'] || 'Cancel' }}\n </lib-button>\n <lib-button id=\"btn-library-confirm-{{ config.name }}\" [variant]=\"'primary'\"\n [disabled]=\"librarySelectedIds.size === 0\" (click)=\"confirmLibrarySelection()\">\n {{ controller.labels['LBL_CONTINUE'] || 'Continue' }}\n </lib-button>\n </div>\n </div>\n </div>\n </div>\n\n\n\n <!-- \u2550\u2550 Location Field \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isLocation\" class=\"field-wrapper location-field-wrapper d-flex flex-column gap-6 gap-12\"\n [formGroup]=\"formGroup\">\n\n <!-- Field label -->\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n <p class=\"location-subtitle m-0 c-6B7280 fs-0-8125rem\" *ngIf=\"config.hint\">{{ config.hint }}</p>\n\n <!-- Three-tab bar -->\n <div class=\"location-tabs d-flex gap-12 mb-24\">\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'VENUE' ? 'warning' : 'outline'\"\n (click)=\"onLocationTabChange('VENUE')\">\n {{ controller.labels['LBL_LOC_VENUE'] || 'Venue' }}\n </lib-button>\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'ONLINE' ? 'warning' : 'outline'\"\n (click)=\"onLocationTabChange('ONLINE')\">\n {{ controller.labels['LBL_LOC_ONLINE'] || 'Online Event' }}\n </lib-button>\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'TBA' ? 'warning' : 'outline'\"\n (click)=\"onLocationTabChange('TBA')\">\n {{ controller.labels['LBL_LOC_TBA'] || 'To be Announced' }}\n </lib-button>\n </div>\n\n <!-- VENUE TAB -->\n <div *ngIf=\"locationActiveTab === 'VENUE'\" class=\"loc-panel loc-venue-panel d-flex flex-column gap-12\">\n\n <p class=\"loc-section-label m-0 font-weight-600 c-111827 fs-0-9rem\">\n {{ controller.labels['LBL_LOC_ADDRESS'] || 'Location address' }}\n </p>\n\n <!-- Added venue rows -->\n <div class=\"loc-venue-list d-flex flex-column gap-8\" *ngIf=\"locationVenues.length > 0\">\n <div *ngFor=\"let venue of locationVenues; let i = index\"\n class=\"loc-venue-item d-flex align-items-center gap-10 p-10px-14px br-7px b-ffffff b-1px-solid-D1D5DB\">\n <mat-icon class=\"loc-venue-search-icon fs-18px c-9CA3AF\">search</mat-icon>\n <span class=\"loc-venue-text flex-1 overflow-hidden fs-0-875rem c-111827\">{{ venue.address || venue.name ||\n venue.description }}</span>\n <button type=\"button\"\n class=\"loc-action-btn loc-delete-btn d-flex align-items-center justify-content-center cursor-pointer rounded-50 b-none border-none p-4px\"\n (click)=\"removeLocationVenue(i)\">\n <mat-icon>delete_outline</mat-icon>\n </button>\n </div>\n </div>\n\n <!-- Location count badge -->\n <p class=\"loc-count-text m-0 font-weight-600 fs-0-8125rem c-3B82F6\"\n *ngIf=\"locationVenues.length > 0 && config.locationConfig?.allowMulti\">\n {{ locationVenues.length }} {{ controller.labels['LBL_LOC_COUNT_SUFFIX'] || 'Locations Added!' }}\n </p>\n\n <!-- Search input (hide when max reached) -->\n <div class=\"loc-search-container position-relative\" *ngIf=\"!locationMaxReached\">\n <div class=\"loc-search-wrapper position-relative d-flex align-items-center mt-4\">\n <mat-icon class=\"loc-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">search</mat-icon>\n <input\n class=\"field-input loc-search-input w-100 font-poppins flex-1 fs-0-875rem c-111827 br-7px br-8px bc-F3F4F6 pl-2-4rem bc-DC2626 pt-0-625rem pb-0-625rem pl-16px pr-16px bc-ffffff b-1px-solid-D1D5DB pr-3-5rem\"\n [placeholder]=\"config.locationConfig?.venuePlaceholder || (controller.labels['PH_LOC_VENUE'] || 'Type to search venue...')\"\n [value]=\"locationSearchText\" (input)=\"handleLocationSearchInput($event)\" (blur)=\"hideLocationSuggestions()\"\n autocomplete=\"off\" [class.is-invalid]=\"errorMessage\">\n </div>\n <!-- Suggestions dropdown -->\n <div class=\"loc-suggestions-panel position-absolute overflow-hidden br-8px b-ffffff b-1px-solid-D1D5DB\"\n *ngIf=\"locationShowSuggestions && locationSuggestions.length\">\n <div *ngFor=\"let sug of locationSuggestions\"\n class=\"loc-suggestion-item d-flex align-items-center gap-10 cursor-pointer p-10px-14px\"\n (mousedown)=\"onLocationSuggestionSelect(sug)\">\n <mat-icon class=\"loc-suggestion-icon fs-18px c-E53E3E\">place</mat-icon>\n <span class=\"loc-suggestion-text overflow-hidden fs-0-875rem c-374151\">{{ sug.description }}</span>\n </div>\n </div>\n </div>\n\n <!-- Add another button -->\n <button type=\"button\"\n class=\"loc-add-btn d-inline-flex align-items-center gap-6 cursor-pointer font-weight-600 p-0 b-none border-none fs-0-875rem c-1A56DB\"\n *ngIf=\"locationVenues.length > 0 && !locationMaxReached && config.locationConfig?.allowMulti\">\n <mat-icon>add_circle_outline</mat-icon>\n <span>{{ controller.labels['LBL_LOC_ADD_ANOTHER'] || 'Add another Location' }}</span>\n </button>\n\n <!-- Map -->\n <div class=\"loc-map-container overflow-hidden br-10px b-1px-solid-E5E7EB\"\n *ngIf=\"config.locationConfig?.showMap !== false\">\n <ng-container *ngIf=\"config.locationConfig?.googleMapsApiKey; else simpleEmbed\">\n <div [id]=\"'loc-map-' + config.name\" class=\"loc-map-frame w-100 d-block border-none\"\n [style.height]=\"config.locationConfig?.mapHeight || '300px'\"></div>\n </ng-container>\n <ng-template #simpleEmbed>\n <iframe class=\"loc-map-frame w-100 d-block border-none\"\n [style.height]=\"config.locationConfig?.mapHeight || '300px'\" [src]=\"getLocationMapEmbedUrl() | trustedUrl\"\n frameborder=\"0\" allowfullscreen loading=\"lazy\">\n </iframe>\n </ng-template>\n </div>\n\n <!-- Map hint -->\n <p class=\"loc-map-hint m-0 text-center fs-0-78rem c-6B7280\">\n {{ controller.labels['LBL_LOC_MAP_HINT'] || 'Type the venue address. A map will appear to assist you.' }}\n </p>\n </div>\n\n <!-- ONLINE TAB -->\n <div *ngIf=\"locationActiveTab === 'ONLINE'\" class=\"loc-panel loc-online-panel d-flex flex-column gap-12\">\n <p class=\"loc-section-label m-0 font-weight-600 c-111827 fs-0-9rem\">\n {{ controller.labels['LBL_LOC_ONLINE_URL'] || 'Event URL' }}\n </p>\n <div class=\"loc-search-wrapper position-relative d-flex align-items-center mt-4\">\n <mat-icon class=\"loc-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">link</mat-icon>\n <input\n class=\"field-input loc-search-input w-100 font-poppins flex-1 fs-0-875rem c-111827 br-7px br-8px bc-F3F4F6 pl-2-4rem bc-DC2626 pt-0-625rem pb-0-625rem pl-16px pr-16px bc-ffffff b-1px-solid-D1D5DB pr-3-5rem\"\n type=\"url\"\n [placeholder]=\"config.locationConfig?.onlinePlaceholder || (controller.labels['PH_LOC_ONLINE'] || 'https://zoom.us/j/...')\"\n [value]=\"locationOnlineUrl\" (input)=\"onLocationUrlChange($any($event.target).value)\"\n [class.is-invalid]=\"errorMessage\">\n </div>\n </div>\n\n <!-- TBA TAB -->\n <div *ngIf=\"locationActiveTab === 'TBA'\"\n class=\"loc-panel loc-tba-panel d-flex flex-column gap-12 justify-content-center\">\n <div\n class=\"loc-tba-content d-flex flex-column align-items-center justify-content-center text-center gap-12 p-32px-24px b-F9FAFB b-1px-dashed-D1D5DB br-10px\">\n <mat-icon class=\"loc-tba-icon fs-40px c-9CA3AF\">schedule</mat-icon>\n <p class=\"loc-tba-text m-0 c-6B7280 fs-0-9rem\">\n {{ controller.labels['LBL_LOC_TBA_DESC'] || \"This event's location is yet to be announced. Check back later\n for updates.\" }}\n </p>\n </div>\n </div>\n\n <!-- Hidden real form control -->\n <input type=\"hidden\" [formControlName]=\"config.name!\">\n\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n</div>", styles: [".d-flex{display:flex}.d-inline-flex{display:inline-flex}.d-grid{display:grid}.d-block{display:block}.d-none{display:none}.flex-column{flex-direction:column}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.flex-wrap{flex-wrap:wrap}.flex-1{flex:1}.align-items-center{align-items:center}.align-items-start{align-items:flex-start}.align-items-end{align-items:flex-end}.justify-content-center{justify-content:center}.justify-content-between{justify-content:space-between}.justify-content-start{justify-content:flex-start}.justify-content-end{justify-content:flex-end}.grid-cols-12{grid-template-columns:repeat(12,1fr)}.w-100{width:100%}.h-100{height:100%}.position-relative{position:relative}.position-absolute{position:absolute}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-poppins{font-family:var(--cc-sf-font-family, \"Poppins\", sans-serif)}.font-weight-400{font-weight:400}.font-weight-500{font-weight:500}.font-weight-600{font-weight:600}.text-13{font-size:13px}.text-14{font-size:14px}.text-16{font-size:16px}.color-white{color:#fff}.color-dark{color:#111827}.color-gray{color:#6b7280}.color-error{color:var(--cc-sf-error-text-color, #DC2626)}.bg-white{background-color:#fff}.bg-transparent{background-color:transparent}.m-0{margin:0}.mt-4{margin-top:4px}.mt-8{margin-top:8px}.mt-10{margin-top:10px}.mt-12{margin-top:12px}.mt-16{margin-top:16px}.mt-20{margin-top:20px}.mt-24{margin-top:24px}.mb-0{margin-bottom:0}.mb-4{margin-bottom:4px}.mb-8{margin-bottom:8px}.mb-10{margin-bottom:10px}.mb-12{margin-bottom:12px}.mb-16{margin-bottom:16px}.mb-20{margin-bottom:20px}.mb-24{margin-bottom:24px}.ml-16{margin-left:16px}.ml-20{margin-left:20px}.p-0{padding:0}.p-16{padding:16px}.p-20{padding:20px}.p-24{padding:24px}.pt-20{padding-top:20px}.pb-10{padding-bottom:10px}.gap-4{gap:4px}.gap-6{gap:6px}.gap-8{gap:8px}.gap-10{gap:10px}.gap-12{gap:12px}.gap-16{gap:16px}.gap-20{gap:20px}.rounded-4{border-radius:4px}.rounded-6{border-radius:6px}.rounded-8{border-radius:8px}.rounded-10{border-radius:10px}.rounded-12{border-radius:12px}.rounded-20{border-radius:20px}.rounded-24{border-radius:24px}.rounded-50{border-radius:50%}.cursor-pointer{cursor:pointer}.overflow-hidden{overflow:hidden}.resize-vertical{resize:vertical}.box-sizing-border{box-sizing:border-box}.border-none{border:none!important}.mb-16px{margin-bottom:var(--cc-sf-grid-gap, 16px)!important}.c-DC2626{color:var(--cc-sf-label-required-color, #DC2626)!important}.ml-0-125rem{margin-left:.125rem!important}.fs-0-875rem{font-size:.875rem!important}.c-111827{color:var(--cc-sf-label-color, #111827)!important}.br-7px{border-radius:var(--cc-sf-input-radius, 7px)!important}.c-6B7280{color:var(--cc-sf-hint-color, #6B7280)!important}.fs-0-75rem{font-size:var(--cc-sf-error-text-size, .75rem)!important}.b-none{background:none!important}.p-32px-24px{padding:32px 24px!important}.us-none{-webkit-user-select:none!important;user-select:none!important}.c-1E293B{color:var(--cc-sf-label-color, #1E293B)!important}.c-3B82F6{color:var(--cc-sf-chip-selected-bg, #3B82F6)!important}.fs-0-78rem{font-size:.78rem!important}.p-10px-14px{padding:10px 14px!important}.fs-0-85rem{font-size:.85rem!important}.fs-0-72rem{font-size:.72rem!important}.c-94A3B8{color:#94a3b8!important}.p-4px{padding:4px!important}.br-8px{border-radius:var(--cc-sf-input-radius, 8px)!important}.bc-F3F4F6{background-color:var(--cc-sf-input-disabled-bg, #F3F4F6)!important}.br-none{border-right:none!important}.bl-none{border-left:none!important}.pe-none{pointer-events:none!important}.fs-1-1rem{font-size:1.1rem!important}.c-9CA3AF{color:var(--cc-sf-hint-color, #9CA3AF)!important}.pl-2-4rem{padding-left:2.4rem!important}.fs-0-8125rem{font-size:.8125rem!important}.ls-none{list-style:none!important}.br-12px{border-radius:var(--mu-carousel-radius, 12px)!important}.b-FFFAF1{background:var(--cc-sf-dropzone-bg, #FFFAF1)!important}.fs-18px{font-size:18px!important}.b-FEF2F2{background:var(--cc-sf-error-bg, #FEF2F2)!important}.bc-DC2626{border-color:var(--cc-sf-error-border, #DC2626)!important}.c-202124{color:var(--cc-sf-label-color, #202124)!important}.fs-18px{font-size:var(--cc-sf-label-size, 18px)!important}.mb-0-5rem{margin-bottom:.5rem!important}.pt-0-625rem{padding-top:var(--cc-sf-input-padding-y, .625rem)!important}.pb-0-625rem{padding-bottom:var(--cc-sf-input-padding-y, .625rem)!important}.pl-16px{padding-left:var(--cc-sf-input-padding-x, 16px)!important}.pr-16px{padding-right:var(--cc-sf-input-padding-x, 16px)!important}.bc-ffffff{background-color:var(--cc-sf-section-bg, #ffffff)!important}.b-1px-solid-D1D5DB{border:1px solid var(--cc-sf-input-border, #D1D5DB)!important}.fs-0-75rem{font-size:.75rem!important}.c-1F2937{color:var(--cc-sf-section-label-color, #1F2937)!important}.p-6px-14px{padding:var(--cc-sf-chip-padding, 6px 14px)!important}.b-ffffff{background:var(--loc-suggestion-bg, #ffffff)!important}.c-374151{color:var(--cc-sf-label-color, #374151)!important}.br-20px{border-radius:var(--cc-sf-chip-radius, 20px)!important}.fs-0-875rem{font-size:var(--cc-sf-btn-font-size, .875rem)!important}.bc-D1D5DB{background-color:var(--cc-sf-switch-track-off, #D1D5DB)!important}.pr-2-75rem{padding-right:2.75rem!important}.p-0-25rem{padding:.25rem!important}.p-0-625rem-0-875rem{padding:var(--cc-sf-generated-padding, .625rem .875rem)!important}.b-F3F4F6{background:var(--cc-sf-generated-bg, #F3F4F6)!important}.b-1px-solid-E5E7EB{border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)!important}.br-8px{border-radius:var(--cc-sf-uploaded-item-radius, 8px)!important}.c-6B7280{color:var(--ms-desc-color, #6B7280)!important}.mb-20px{margin-bottom:var(--cc-sf-section-gap, 20px)!important}.br-10px{border-radius:var(--cc-sf-input-radius, 10px)!important}.p-20px{padding:var(--cc-sf-section-padding, 20px)!important}.fs-1rem{font-size:1rem!important}.m-0-0-16px-0{margin:0 0 16px!important}.bb-2px-solid-E5E7EB{border-bottom:var(--cc-sf-section-label-border, 2px solid #E5E7EB)!important}.p-16px{padding:var(--cc-sf-instance-padding, 16px)!important}.b-F9FAFB{background:var(--loc-tba-bg, #F9FAFB)!important}.bb-1px-dashed-D1D5DB{border-bottom:var(--cc-sf-instance-divider, 1px dashed #D1D5DB)!important}.c-4B5563{color:var(--cc-sf-instance-num-color, #4B5563)!important}.fs-0-8125rem{font-size:var(--cc-sf-hint-size, .8125rem)!important}.pb-0{padding-bottom:0!important}.p-18px-24px{padding:18px 24px!important}.c-111827{color:var(--ms-title-color, #111827)!important}.bt-1px-solid-E5E7EB{border-top:1px solid #E5E7EB!important}.p-4px-10px{padding:4px 10px!important}.b-FFF5F5{background:var(--cc-sf-btn-remove-bg, #FFF5F5)!important}.c-E53E3E{color:var(--loc-delete-color, #E53E3E)!important}.b-1px-solid-FED7D7{border:var(--cc-sf-btn-remove-border, 1px solid #FED7D7)!important}.br-4px{border-radius:var(--cc-sf-btn-remove-radius, 4px)!important}.p-8px-16px{padding:8px 16px!important}.b-transparent{background:var(--cc-sf-btn-add-bg, transparent)!important}.c-3B82F6{color:var(--cc-sf-input-focus-border, #3B82F6)!important}.b-1px-dashed-CBD5E1{border:var(--cc-sf-btn-add-border, 1px dashed #CBD5E1)!important}.br-6px{border-radius:var(--cc-sf-btn-add-radius, 6px)!important}.b-1-5px-dashed-CBD5E1{border:var(--cc-sf-dropzone-border, 1.5px dashed #CBD5E1)!important}.br-12px{border-radius:var(--cc-sf-dropzone-radius, 12px)!important}.bc-FFFAF1{background-color:var(--cc-sf-dropzone-bg, #FFFAF1)!important}.c-94A3B8{color:var(--cc-sf-uploaded-remove-color, #94A3B8)!important}.fs-0-9rem{font-size:var(--cc-sf-input-font-size, .9rem)!important}.c-64748B{color:var(--cc-sf-dropzone-hint-color, #64748B)!important}.b-1px-solid-E2E8F0{border:var(--cc-sf-uploaded-item-border, 1px solid #E2E8F0)!important}.b-2px-solid-E2E8F0{border:2px solid #E2E8F0!important}.pr-3-5rem{padding-right:3.5rem!important}.p-0-0-875rem{padding:0 .875rem!important}.bc-FFFFFF{background-color:var(--cc-sf-input-bg, #FFFFFF)!important}.b-1-5px-solid-D1D5DB{border:var(--cc-sf-input-border, 1.5px solid #D1D5DB)!important}.mb-0-75rem{margin-bottom:.75rem!important}.mt-6px{margin-top:6px!important}.pr-2-4rem{padding-right:2.4rem!important}.p-0-2rem{padding:.2rem!important}.fs-1-35rem{font-size:1.35rem!important}.p-4px-12px{padding:4px 12px!important}.b-111827{background:var(--cc-sf-label-color, #111827)!important}.b-2px-dashed-CBD5E1{border:2px dashed var(--cc-sf-dropzone-border, #CBD5E1)!important}.fs-52px{font-size:52px!important}.p-12px-16px{padding:12px 16px!important}.bb-1px-solid-F3F4F6{border-bottom:1px solid var(--cc-sf-input-disabled-border, #F3F4F6)!important}.b-0F172A{background:var(--mu-carousel-bg, #0F172A)!important}.b-3px-solid-rgba-255-255-255-0-15{border:3px solid rgba(255,255,255,.15)!important}.b-rgba-255-255-255-0-85{background:#ffffffd9!important}.b-rgba-0-0-0-0-55{background:#0000008c!important}.b-rgba-255-255-255-0-45{background:#ffffff73!important}.pb-4px{padding-bottom:4px!important}.b-2px-solid-transparent{border:2px solid transparent!important}.b-E2E8F0{background:var(--mu-thumb-bg, #E2E8F0)!important}.b-1E293B{background:#1e293b!important}.c-EF4444{color:#ef4444!important}.b-rgba-0-0-0-0-5{background:#00000080!important}.br-16px{border-radius:var(--mu-modal-radius, 16px)!important}.p-24px-28px{padding:24px 28px!important}.bb-1px-solid-E5E7EB{border-bottom:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)!important}.fs-1-25rem{font-size:1.25rem!important}.p-48px-24px{padding:48px 24px!important}.b-3px-solid-E2E8F0{border:3px solid #E2E8F0!important}.p-16px-24px{padding:16px 24px!important}.p-28px{padding:28px!important}.b-3px-solid-transparent{border:3px solid transparent!important}.b-rgba-59-130-246-0-12{background:#3b82f61f!important}.p-20px-28px{padding:20px 28px!important}.c-1A56DB{color:var(--loc-add-color, #1A56DB)!important}.b-1px-dashed-D1D5DB{border:1px dashed var(--cc-sf-input-disabled-border, #D1D5DB)!important}.fs-40px{font-size:40px!important}.c-9CA3AF{color:var(--loc-tba-icon-color, #9CA3AF)!important}.form-field{font-family:var(--cc-sf-font-family, \"Poppins\", sans-serif)!important}:host{--cc-sf-input-border: #D1D5DB;--cc-sf-input-bg: #ffffff;--cc-sf-input-radius: 9px;--cc-sf-input-height: 44px;--cc-sf-label-color: #111827;--cc-sf-hint-color: #9CA3AF;--cc-sf-error-border: #EF4444;--cc-sf-error-bg: #FFF5F5;--cc-sf-accent-color: #6366F1;--cc-sf-input-focus-border: #6366F1;--cc-sf-input-hover-border: #A5B4FC;--cc-sf-input-placeholder: #C4C9D4;--cc-sf-input-disabled-bg: #F8F9FB;--cc-sf-input-disabled-border: #E5E7EB;--cc-sf-switch-track-on: #6366F1;--cc-sf-switch-track-off: #D1D5DB;--cc-sf-switch-thumb: #ffffff;--cc-sf-selected-color: #6366F1}.form-row{gap:var(--cc-sf-grid-gap, 16px)}.form-row.horizontal{display:flex;flex-direction:row}.form-row.horizontal>*{flex:1}.form-row:not(.horizontal){flex-direction:column}.form-row.grid-row{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--cc-sf-grid-gap, 16px);align-items:start}@media(max-width:640px){.form-row.grid-row{grid-template-columns:1fr}.form-row.grid-row .row-field{grid-column:span 12!important}}.field-label{font-size:.75rem;font-weight:600;line-height:1;letter-spacing:.04em;text-transform:uppercase;color:#6b7280}.field-input,input.matInput,.mat-mdc-input-element{display:block;width:100%;height:var(--cc-sf-input-height)!important;padding:0 14px!important;font-family:inherit;font-size:.9rem;color:var(--cc-sf-label-color);background-color:var(--cc-sf-input-bg)!important;border:1.5px solid var(--cc-sf-input-border)!important;border-radius:var(--cc-sf-input-radius)!important;box-sizing:border-box;box-shadow:0 1px 2px #0000000a!important;transition:all .2s cubic-bezier(.4,0,.2,1)}.field-input::placeholder,input.matInput::placeholder,.mat-mdc-input-element::placeholder{font-weight:400;font-size:14px;color:var(--cc-sf-input-placeholder)}.field-input{opacity:var(--cc-sf-input-opacity, 1);line-height:var(--cc-sf-input-line-height, 1.5);transition:var(--cc-sf-input-transition, all .2s ease)}.field-input::placeholder{font-weight:var(--cc-sf-placeholder-weight, 400);font-size:var(--cc-sf-placeholder-size, 14px);line-height:var(--cc-sf-placeholder-line-height, 100%);color:var(--cc-sf-input-placeholder)}.field-input:hover:not(:disabled):not([readonly]){border-color:var(--cc-sf-input-hover-border)!important;box-shadow:0 1px 4px #6366f114!important}.field-input:focus{outline:none;border-color:var(--cc-sf-input-focus-border)!important;box-shadow:0 0 0 3px #6366f124,0 1px 4px #6366f11a!important;background-color:#fefeff!important}.field-input:disabled,.field-input[readonly]{background-color:var(--cc-sf-input-disabled-bg)!important;color:#9ca3af!important;cursor:not-allowed;border-color:var(--cc-sf-input-disabled-border)!important;box-shadow:none!important}.field-input.is-invalid{border-color:var(--cc-sf-error-border)!important;background-color:var(--cc-sf-error-bg)!important}.field-input.is-invalid:focus{box-shadow:0 0 0 3px #ef44441f,0 1px 4px #ef44441a!important}.field-input.textarea{resize:vertical;min-height:100px;height:auto;padding:12px 16px!important}input[type=time].time-input{cursor:pointer}input[type=time].time-input::-webkit-calendar-picker-indicator{cursor:pointer;opacity:.7;filter:invert(30%)}input[type=time].time-input::-webkit-calendar-picker-indicator:hover{opacity:1}select.field-input{appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236B7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3E%3C/svg%3E\");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;cursor:pointer}select.field-input:disabled{cursor:not-allowed}.multi-select-wrapper{position:relative}.multi-select-wrapper .multi-select-trigger{min-height:var(--cc-sf-input-height, 44px);height:auto;-webkit-user-select:none;user-select:none}.multi-select-wrapper .multi-select-trigger.ms-open{border-color:var(--cc-sf-input-focus-border, #6366F1)!important;box-shadow:0 0 0 3px #6366f124,0 1px 4px #6366f11a!important}.multi-select-wrapper .multi-select-trigger .multi-select-value{flex:1;color:var(--cc-sf-label-color, #111827);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.multi-select-wrapper .multi-select-trigger .multi-select-placeholder{flex:1;font-weight:400;font-size:14px;color:var(--cc-sf-input-placeholder, #C4C9D4)}.multi-select-wrapper .multi-select-trigger .multi-select-arrow{font-size:20px;width:20px;height:20px;line-height:20px;color:#6b7280;flex-shrink:0}.multi-select-wrapper .multi-select-panel{position:absolute;top:calc(100% + 4px);left:0;right:0;background:#fff;border:1.5px solid var(--cc-sf-input-border, #D1D5DB);border-radius:var(--cc-sf-input-radius, 9px);box-shadow:0 8px 24px #0000001a,0 2px 6px #0000000f;z-index:1050;max-height:240px;overflow-y:auto}.multi-select-wrapper .multi-select-panel::-webkit-scrollbar{width:4px}.multi-select-wrapper .multi-select-panel::-webkit-scrollbar-thumb{background:#d1d5db;border-radius:2px}.multi-select-wrapper .multi-select-option{padding:8px 12px;border-bottom:1px solid #F3F4F6;transition:background .1s}.multi-select-wrapper .multi-select-option:last-of-type{border-bottom:none}.multi-select-wrapper .multi-select-option:hover{background:#f9fafb}.multi-select-wrapper .multi-select-option input[type=checkbox]{flex-shrink:0;cursor:pointer;accent-color:var(--cc-sf-selected-color, #6366F1);width:15px;height:15px}.multi-select-wrapper .multi-select-empty{padding:12px;text-align:center}.multi-select-wrapper.is-invalid .multi-select-trigger{border-color:var(--cc-sf-error-border, #EF4444)!important;background-color:var(--cc-sf-error-bg, #FFF5F5)!important}.char-count-hint{font-style:normal;line-height:100%;letter-spacing:.02em;margin-top:.4rem}.radio-group.layout-column,.checkbox-group.layout-column{display:grid!important;grid-template-columns:repeat(12,1fr);gap:16px;width:100%}.radio-group.layout-row,.checkbox-group.layout-row{flex-direction:column!important;gap:12px;width:100%}.radio-label input,.checkbox-label input{cursor:pointer;accent-color:var(--cc-sf-chip-selected-bg, #F05A28)}.radio-label.card-item,.checkbox-label.card-item{display:flex!important;flex-direction:row-reverse!important;justify-content:space-between!important;align-items:center!important;border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB);border-radius:12px;padding:16px 20px;box-sizing:border-box;transition:all .2s ease;background:var(--cc-sf-input-bg, #ffffff);margin-bottom:0}.radio-label.card-item input,.checkbox-label.card-item input{margin-left:16px}.radio-label.card-item.selected,.checkbox-label.card-item.selected{border-color:var(--cc-sf-selected-color);background-color:#f05a280d}.radio-label.card-item .option-content .label-text,.checkbox-label.card-item .option-content .label-text,.checkbox-single .checkbox-label{font-weight:var(--cc-sf-label-weight, 500)}.chip-label{transition:var(--cc-sf-input-transition, all .2s ease)}.chip-label:hover{background:var(--cc-sf-chip-hover-bg, #F3F4F6)}.chip-label.selected{background:var(--cc-sf-selected-color);color:var(--cc-sf-chip-selected-color, #ffffff);border-color:var(--cc-sf-selected-color)}.switch{width:50px;height:24px;display:inline-block}.switch input{opacity:0;width:0;height:0;position:absolute}.switch input:checked+.slider{background-color:var(--cc-sf-switch-track-on)!important}.switch input:checked+.slider:before{transform:translate(26px)}.switch .slider{inset:0;transition:.4s;background-color:var(--cc-sf-switch-track-off);border-radius:24px}.switch .slider:before{position:absolute;content:\"\";height:18px;width:18px;left:3px;bottom:3px;background-color:var(--cc-sf-switch-thumb);transition:.4s;border-radius:50%}.rating-group .star{transition:var(--cc-sf-input-transition, all .2s ease)}.rating-group .star mat-icon{font-size:var(--cc-sf-star-size, 28px);width:var(--cc-sf-star-size, 28px);height:var(--cc-sf-star-size, 28px);line-height:var(--cc-sf-star-size, 28px);color:var(--cc-sf-star-empty, #D1D5DB);transition:var(--cc-sf-input-transition, all .2s ease)}.rating-group .star.filled mat-icon,.rating-group .star.half mat-icon{color:var(--cc-sf-star-filled, #F59E0B)}.rating-group .star:hover mat-icon{color:var(--cc-sf-star-filled, #F59E0B)}.password-wrapper .password-toggle{right:.625rem;top:50%;transform:translateY(-50%);line-height:1;transition:color var(--cc-sf-input-transition, .2s ease)}.password-wrapper .password-toggle mat-icon.eye-icon{font-size:1.125rem;width:1.125rem;height:1.125rem;line-height:1.125rem}.password-wrapper .password-toggle:hover{color:var(--cc-sf-label-color, #374151)}.password-wrapper .password-toggle:focus{outline:none}.group-section-wrapper .group-label{font-size:var(--cc-sf-section-label-size, 1rem);font-weight:var(--cc-sf-section-label-weight, 600);color:var(--cc-sf-section-label-color, #1F2937);margin:0 0 16px;padding-left:12px;padding-top:2px;padding-bottom:2px;border-left:var(--cc-sf-section-header-accent-width, 4px) solid var(--cc-sf-section-header-accent-color, #3B82F6);line-height:1.4}.group-section-wrapper .group-fields.sf-grid{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--cc-sf-grid-gap, 16px);align-items:start}@media(max-width:640px){.group-section-wrapper .group-fields.sf-grid{grid-template-columns:1fr}.group-section-wrapper .group-fields.sf-grid .sf-col{grid-column:span 12!important}}.group-section-wrapper .group-accordion-instance{border:var(--cc-sf-instance-border, 1px solid #E5E7EB);border-radius:var(--cc-sf-section-border-radius-inner, 8px);margin-bottom:8px;overflow:hidden;transition:border-color .2s ease}.group-section-wrapper .group-accordion-instance:last-of-type{margin-bottom:0}.group-section-wrapper .group-accordion-instance .group-accordion-header{display:flex;justify-content:space-between;align-items:center;padding:10px 14px;background:var(--cc-sf-repeater-accordion-header-bg, #F9FAFB);cursor:pointer;-webkit-user-select:none;user-select:none;transition:background .15s ease}.group-section-wrapper .group-accordion-instance .group-accordion-header:hover{background:var(--cc-sf-repeater-accordion-active-bg, #EFF6FF)}.group-section-wrapper .group-accordion-instance .group-accordion-header .instance-badge{width:22px;height:22px;border-radius:50%;background:var(--cc-sf-repeater-badge-bg, #E5E7EB);color:var(--cc-sf-repeater-badge-color, #374151);font-size:.75rem;font-weight:600;display:flex;align-items:center;justify-content:center;flex-shrink:0}.group-section-wrapper .group-accordion-instance .group-accordion-header .instance-title{font-size:var(--cc-sf-instance-num-size, .8125rem);font-weight:600;color:var(--cc-sf-repeater-accordion-header-color, #1F2937)}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-remove-btn{background:none;border:none;cursor:pointer;color:var(--cc-sf-btn-remove-color, #E53E3E);padding:4px;border-radius:4px;line-height:1;display:flex;align-items:center;transition:background .15s ease}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-remove-btn mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-remove-btn:hover{background:var(--cc-sf-btn-remove-hover-bg, #FED7D7)}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-chevron{font-size:1.25rem;width:1.25rem;height:1.25rem;line-height:1.25rem;color:var(--cc-sf-instance-num-color, #4B5563)}.group-section-wrapper .group-accordion-instance .group-accordion-body{padding:var(--cc-sf-instance-padding, 16px);background:var(--cc-sf-instance-bg, #F9FAFB);border-top:var(--cc-sf-instance-divider, 1px dashed #D1D5DB)}.group-section-wrapper .btn-add-group{display:flex;align-items:center;justify-content:center;gap:6px;width:100%;padding:10px 20px;margin-top:12px;background:var(--cc-sf-btn-add-bg, transparent);color:var(--cc-sf-btn-add-color, #3B82F6);border:var(--cc-sf-btn-add-border, 1px dashed #CBD5E1);border-radius:var(--cc-sf-btn-add-radius, 6px);cursor:pointer;font-family:inherit;font-size:var(--cc-sf-btn-font-size, .875rem);font-weight:var(--cc-sf-btn-font-weight, 600);transition:var(--cc-sf-btn-transition, all .2s ease)}.group-section-wrapper .btn-add-group mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.group-section-wrapper .btn-add-group:hover{background:var(--cc-sf-btn-add-hover-bg, #EFF6FF);border-color:var(--cc-sf-btn-add-hover-border, #BFDBFE)}.group-section-wrapper .group-instance:last-child{margin-bottom:0}.group-section-wrapper.multi-save-active{border:none;box-shadow:none;padding:0;background:transparent}.group-section-wrapper.multi-save-active .multi-save-header .btn-add-multi ::ng-deep button{color:var(--ms-btn-add-color, #3B82F6);font-weight:600;font-size:.875rem;padding:8px 12px}.group-section-wrapper.multi-save-active .multi-save-header .btn-add-multi ::ng-deep button:hover{color:var(--ms-btn-add-hover, #2563EB);background-color:var(--cc-sf-btn-add-hover-bg, #EFF6FF)}.group-section-wrapper.multi-save-active .group-instance.is-card{cursor:pointer;transition:all .2s ease-in-out}.group-section-wrapper.multi-save-active .group-instance.is-card:hover{box-shadow:var(--ms-card-shadow-hover, 0 8px 24px rgba(0, 0, 0, .08));border-color:var(--cc-sf-input-focus-border, #3B82F6)}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-content .card-title{white-space:nowrap;text-overflow:ellipsis}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-content .card-desc{line-height:1.4;display:-webkit-box;-webkit-line-clamp:1;line-clamp:1;-webkit-box-orient:vertical}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view.is-expanded .card-content .card-desc{-webkit-line-clamp:unset;line-clamp:unset}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon{font-size:22px;width:22px;height:22px;color:var(--cc-sf-hint-color, #9CA3AF);transition:color .2s}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon.icon-delete:hover{color:var(--cc-sf-error-border, #DC2626)}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon.icon-edit:hover{color:var(--cc-sf-input-focus-border, #3B82F6)}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon.icon-expand{color:var(--cc-sf-input-disabled-border, #E5E7EB)}.btn-remove{transition:var(--cc-sf-btn-transition, all .2s ease)}.btn-remove mat-icon{font-size:1rem;width:1rem;height:1rem;line-height:1rem}.btn-remove:hover{background:var(--cc-sf-btn-remove-hover-bg, #FED7D7)}.btn-add-group{font-weight:var(--cc-sf-btn-font-weight, 600);transition:var(--cc-sf-btn-transition, all .2s ease)}.btn-add-group mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.btn-add-group:hover{background:var(--cc-sf-btn-add-hover-bg, #EFF6FF);border-color:var(--cc-sf-btn-add-hover-border, #BFDBFE)}.upload-drop-zone{background-color:var(--cc-sf-dropzone-bg, #F8FAFC);border:var(--cc-sf-dropzone-border, 1.5px dashed #CBD5E1);border-radius:var(--cc-sf-dropzone-radius, 12px);transition:background-color .2s ease,border-color .2s ease}.upload-drop-zone:hover{background-color:var(--cc-sf-dropzone-hover-bg, #EFF6FF);border-color:var(--cc-sf-dropzone-hover-border, #93C5FD)}.upload-drop-zone.drag-over{background-color:var(--cc-sf-dropzone-hover-bg, #EFF6FF);border-color:var(--cc-sf-dropzone-over-border, #3B82F6);box-shadow:var(--cc-sf-dropzone-over-shadow, 0 0 0 4px rgba(59, 130, 246, .12))}.upload-drop-zone.is-invalid{border-color:var(--cc-sf-error-border, #DC2626);background-color:var(--cc-sf-error-bg, #FEF2F2)}.upload-icon-wrap .dropzone-icon-pill{width:52px;height:52px;border-radius:50%;background:var(--cc-sf-dropzone-icon-bg, rgba(59, 130, 246, .1))}.upload-icon-wrap mat-icon.upload-cloud-icon{font-size:28px;width:28px;height:28px;line-height:28px;color:var(--cc-sf-accent-color, #3B82F6)}.upload-main-text{color:var(--cc-sf-label-color, #1E293B)}.upload-sub-text{color:var(--cc-sf-hint-color, #64748B)}.upload-link{color:var(--cc-sf-dropzone-link-color, #3B82F6);font-weight:500}.upload-formats{color:var(--cc-sf-dropzone-link-color, #3B82F6)}.upload-size-badge{display:inline-block;padding:2px 8px;border-radius:20px;background:var(--cc-sf-input-disabled-bg, #F3F4F6);color:var(--cc-sf-hint-color, #6B7280);font-weight:500}.uploaded-item{background:var(--cc-sf-uploaded-item-bg, #ffffff);border:var(--cc-sf-uploaded-item-border, 1px solid #E2E8F0);border-radius:var(--cc-sf-uploaded-item-radius, 8px);transition:box-shadow .15s ease}.uploaded-item:hover{box-shadow:0 2px 6px #0000000f}.uploaded-item mat-icon.file-type-icon{font-size:20px;width:20px;height:20px;line-height:20px;flex-shrink:0;color:var(--cc-sf-hint-color, #64748B)}.uploaded-item .file-thumb{width:36px;height:36px;object-fit:cover;flex-shrink:0}.uploaded-item .file-info{min-width:0;gap:2px}.uploaded-item .file-info .file-name{white-space:nowrap;text-overflow:ellipsis}.uploaded-item .file-remove-btn{flex-shrink:0;width:32px;height:32px;background:none;border:none;cursor:pointer;color:var(--cc-sf-uploaded-remove-color, #94A3B8);padding:0;display:flex;align-items:center;justify-content:center;transition:color .15s ease,background .15s ease}.uploaded-item .file-remove-btn mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.uploaded-item .file-remove-btn:hover:not(:disabled){color:var(--cc-sf-uploaded-remove-hover-color, #DC2626);background:var(--cc-sf-uploaded-remove-hover-bg, #FEF2F2)}.uploaded-item .file-remove-btn:disabled{opacity:.4;cursor:not-allowed}.uploaded-item.uploading{background:var(--cc-sf-uploaded-uploading-bg, #F8FAFC);border-color:var(--cc-sf-uploaded-uploading-border, #CBD5E1);opacity:.85}.upload-spinner{width:20px;height:20px;flex-shrink:0;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}@keyframes cc-spin{to{transform:rotate(360deg)}}.uploading-label{font-style:italic}.input-group{align-items:stretch}.input-group .field-input{flex:1;width:auto}.input-prefix+.input-group .field-input{border-top-left-radius:0;border-bottom-left-radius:0}.input-group .field-input:has(+.input-suffix){border-top-right-radius:0;border-bottom-right-radius:0}.input-group .field-input.has-icon-right{padding-right:3rem}.input-group.readonly .field-input{cursor:default}.input-prefix,.input-suffix{display:flex!important;align-items:center;white-space:nowrap;padding:0 14px;background-color:var(--cc-sf-input-disabled-bg);border:1px solid var(--cc-sf-input-border);font-size:.875rem;color:var(--cc-sf-hint-color);-webkit-user-select:none;user-select:none}.input-prefix{border-right:none;border-top-left-radius:var(--cc-sf-input-radius, 8px);border-bottom-left-radius:var(--cc-sf-input-radius, 8px)}.input-suffix{border-left:none;border-top-right-radius:var(--cc-sf-input-radius, 8px);border-bottom-right-radius:var(--cc-sf-input-radius, 8px)}.readonly-icons{right:.875rem;top:50%;transform:translateY(-50%)}.readonly-icons mat-icon.lock-icon{font-size:1rem;width:1rem;height:1rem;line-height:1rem;opacity:.5;color:var(--cc-sf-hint-color, #6B7280)}.date-icon-wrapper{right:.5rem;top:50%;transform:translateY(-50%);pointer-events:auto}.date-icon-wrapper .mat-icon-button{width:32px;height:32px;line-height:32px}.subfields-group-wrapper .subfields-row{transition:all .2s ease}.subfields-group-wrapper .subfields-row.is-invalid .subfield-item ::ng-deep .field-input{border-color:var(--cc-sf-error-border, #DC2626);background-color:var(--cc-sf-error-bg, #FEF2F2)}.subfields-group-wrapper .subfields-row .subfield-item{min-width:0}.subfields-group-wrapper .subfields-row .subfield-item ::ng-deep .field-label{font-size:.75rem!important;margin-bottom:4px!important;font-weight:500!important;color:var(--cc-sf-hint-color, #6B7280)!important}.subfields-group-wrapper .subfields-row .subfield-separator{font-weight:700}.autocomplete-wrapper .ac-input{padding-left:40px!important}.autocomplete-wrapper .ac-search-icon{left:.75rem;width:1.1rem;height:1.1rem;line-height:1.1rem;z-index:1;transition:color var(--cc-sf-input-transition, .2s ease)}.autocomplete-wrapper .ac-clear-btn{right:.6rem;transition:color .15s ease,background .15s ease}.autocomplete-wrapper .ac-clear-btn mat-icon{font-size:1rem;width:1rem;height:1rem;line-height:1rem}.autocomplete-wrapper .ac-clear-btn:hover{color:var(--cc-sf-label-color, #374151);background:var(--cc-sf-input-disabled-bg, #F3F4F6)}.autocomplete-wrapper .ac-clear-btn:focus{outline:none}.autocomplete-wrapper:focus-within .ac-search-icon{color:var(--cc-sf-accent-color, #3B82F6)}.autocomplete-wrapper.is-invalid .ac-input{border-color:var(--cc-sf-error-border)!important;background-color:var(--cc-sf-error-bg)}.autocomplete-wrapper.readonly .ac-input{background-color:var(--cc-sf-input-disabled-bg);color:var(--cc-sf-input-disabled-color, #6B7280);cursor:not-allowed;border-color:var(--cc-sf-input-disabled-border)!important}.ac-no-results{font-style:italic}::ng-deep .mat-mdc-autocomplete-panel,::ng-deep .mat-autocomplete-panel{background:var(--cc-sf-input-bg, #ffffff)!important;border-radius:var(--cc-sf-input-radius, 9px)!important;border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)!important;box-shadow:0 8px 24px #0000001a,0 2px 6px #0000000f!important;padding:4px 0!important;min-width:200px}::ng-deep .mat-mdc-autocomplete-panel mat-option,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option,::ng-deep .mat-mdc-autocomplete-panel .mat-option,::ng-deep .mat-autocomplete-panel mat-option,::ng-deep .mat-autocomplete-panel .mat-mdc-option,::ng-deep .mat-autocomplete-panel .mat-option{background:var(--cc-sf-input-bg, #ffffff)!important;color:var(--cc-sf-label-color, #111827)!important;font-size:.875rem!important;padding:10px 16px!important;min-height:40px!important;line-height:1.4!important;display:flex!important;flex-direction:column!important;align-items:flex-start!important;transition:background var(--cc-sf-input-transition, .2s ease)!important}::ng-deep .mat-mdc-autocomplete-panel mat-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-mdc-autocomplete-panel .mat-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-autocomplete-panel mat-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-autocomplete-panel .mat-mdc-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-autocomplete-panel .mat-option:hover:not(.mat-option-disabled):not([disabled]){background:var(--cc-sf-input-disabled-bg, #F3F4F6)!important}::ng-deep .mat-mdc-autocomplete-panel mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-mdc-autocomplete-panel mat-option.mdc-list-item--selected,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mdc-list-item--selected,::ng-deep .mat-mdc-autocomplete-panel .mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-mdc-autocomplete-panel .mat-option.mdc-list-item--selected,::ng-deep .mat-autocomplete-panel mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-autocomplete-panel mat-option.mdc-list-item--selected,::ng-deep .mat-autocomplete-panel .mat-mdc-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-autocomplete-panel .mat-mdc-option.mdc-list-item--selected,::ng-deep .mat-autocomplete-panel .mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-autocomplete-panel .mat-option.mdc-list-item--selected{background:var(--cc-sf-dropzone-hover-bg, #EFF6FF)!important;color:var(--cc-sf-selected-color, #6366F1)!important;font-weight:600!important}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-option-label,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-option-label,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-option-label,::ng-deep .mat-autocomplete-panel mat-option .ac-option-label,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-option-label,::ng-deep .mat-autocomplete-panel .mat-option .ac-option-label{font-weight:500;color:var(--cc-sf-label-color, #111827);font-size:.875rem;display:block}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-display-fields,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-display-fields,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-display-fields,::ng-deep .mat-autocomplete-panel mat-option .ac-display-fields,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-display-fields,::ng-deep .mat-autocomplete-panel .mat-option .ac-display-fields{align-items:center;line-height:1}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-item,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-item,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-item,::ng-deep .mat-autocomplete-panel mat-option .ac-df-item,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-item,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-item{display:inline-flex;align-items:center;font-size:.72rem;color:var(--cc-sf-hint-color, #6B7280);gap:3px}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-chip,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-chip,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-chip,::ng-deep .mat-autocomplete-panel mat-option .ac-df-chip,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-chip,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-chip{background:var(--cc-sf-input-disabled-bg, #F3F4F6);border-radius:4px;padding:2px 6px}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-text,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-text,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-text,::ng-deep .mat-autocomplete-panel mat-option .ac-df-text,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-text,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-text{color:var(--cc-sf-hint-color, #6B7280)}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-avatar,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-avatar,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-avatar,::ng-deep .mat-autocomplete-panel mat-option .ac-df-avatar,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-avatar,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-avatar{width:24px;height:24px;border-radius:50%;object-fit:cover;border:1px solid var(--cc-sf-input-border, #D1D5DB);vertical-align:middle}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-label,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-label,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-label,::ng-deep .mat-autocomplete-panel mat-option .ac-df-label,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-label,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-label{font-weight:600;color:var(--cc-sf-hint-color, #9CA3AF);margin-right:2px}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-icon,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-icon,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-icon,::ng-deep .mat-autocomplete-panel mat-option .ac-df-icon,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-icon,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-icon{font-size:11px;width:11px;height:11px;line-height:11px;color:var(--cc-sf-hint-color, #9CA3AF);flex-shrink:0}.mu-layout{grid-template-columns:1fr 1fr;gap:32px}@media(max-width:768px){.mu-layout{grid-template-columns:1fr}}.mu-title{font-weight:700;line-height:1.3}.mu-badge{white-space:nowrap;flex-shrink:0}.mu-description{line-height:1.6}.mu-feature-item .mu-check{width:16px;height:16px;line-height:16px;flex-shrink:0}.mu-right{min-height:260px}.mu-right-empty{min-height:250px;max-width:400px;box-shadow:0 2px 10px #0000000d;transition:box-shadow .2s ease}.mu-right-empty:hover{cursor:pointer;box-shadow:0 4px 16px #0000001a}.mu-right-empty .mu-right-empty-icon{width:52px;height:52px;line-height:52px;opacity:.3}.mu-right-empty p{margin:0;font-size:.85rem}.media-add-container{display:inline-block}.media-add-container ::ng-deep button{display:flex;align-items:center;gap:6px}.media-add-container ::ng-deep button .menu-chevron{width:18px;height:18px;line-height:18px;transition:transform .2s ease}.media-dropdown{top:calc(100% + 6px);left:0;z-index:200;min-width:240px;box-shadow:var(--mu-dropdown-shadow, 0 8px 32px rgba(0, 0, 0, .12));animation:mu-fade-in .15s ease}@keyframes mu-fade-in{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}.media-dropdown-item{transition:background .15s ease}.media-dropdown-item:last-child{border-bottom:none}.media-dropdown-item:hover{background:var(--cc-sf-dropzone-hover-bg, #F0F9FF)}.media-drop-icon{width:36px;height:36px;flex-shrink:0}.media-drop-icon mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.media-drop-icon--video{background:var(--mu-icon-video-bg, #FFF0F0);color:var(--mu-icon-video-color, #EF4444)}.media-drop-icon--device{background:var(--mu-icon-device-bg, #EFF6FF);color:var(--mu-icon-device-color, #3B82F6)}.media-drop-icon--library{background:var(--mu-icon-library-bg, #F0FDF4);color:var(--mu-icon-library-color, #22C55E)}.media-drop-text{gap:2px}.youtube-input-panel{animation:mu-fade-in .18s ease}.youtube-panel-label mat-icon{font-size:18px;width:18px;height:18px;line-height:18px;color:var(--mu-icon-video-color, #EF4444)}.youtube-input-row{align-items:stretch}.media-menu-backdrop{position:fixed;inset:0;z-index:100}.media-upload-status{animation:mu-fade-in .2s ease}.media-upload-status .status-icon{width:18px;height:18px;line-height:18px}.media-carousel-main{max-width:400px;height:var(--mu-carousel-height, 250px)}.carousel-viewer{inset:0}.carousel-viewer .carousel-image{object-fit:cover}.carousel-viewer .carousel-spinner{width:36px;height:36px;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}.carousel-nav{top:50%;transform:translateY(-50%);z-index:10;width:40px;height:40px;box-shadow:0 2px 8px #0003;transition:background .2s ease,opacity .2s ease;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.carousel-nav mat-icon{font-size:22px;width:22px;height:22px;line-height:22px;color:#1e293b}.carousel-nav:hover:not(:disabled){background:#fff}.carousel-nav:disabled{opacity:.3;cursor:not-allowed}.carousel-nav--prev{left:12px}.carousel-nav--next{right:12px}.carousel-remove-btn{top:10px;right:10px;z-index:10;width:32px;height:32px;transition:background .2s ease}.carousel-remove-btn mat-icon{font-size:18px;width:18px;height:18px;line-height:18px;color:#fff}.carousel-remove-btn:hover:not(:disabled){background:#dc2626d9}.carousel-remove-btn:disabled{opacity:.4;cursor:not-allowed}.carousel-dots{bottom:10px;left:50%;transform:translate(-50%);z-index:10}.carousel-dot{width:8px;height:8px;transition:background .2s ease,transform .2s ease}.carousel-dot.active{background:#fff;transform:scale(1.3)}.media-thumbnail-strip{max-width:400px;overflow-x:auto}.media-thumbnail-strip::-webkit-scrollbar{height:4px}.media-thumbnail-strip::-webkit-scrollbar-thumb{background:var(--cc-sf-input-disabled-border, #D1D5DB);border-radius:2px}.media-thumb{flex-shrink:0;width:72px;height:52px;transition:border-color .2s ease,transform .15s ease}.media-thumb.active{border-color:var(--mu-thumb-active-border, var(--cc-sf-accent-color, #3B82F6));transform:scale(1.04)}.media-thumb:hover:not(.active){border-color:var(--cc-sf-input-hover-border, #9CA3AF)}.media-thumb .thumb-img{object-fit:cover}.media-thumb .thumb-yt-placeholder mat-icon{font-size:28px;width:28px;height:28px;line-height:28px}.media-thumb .thumb-uploading .thumb-spinner{width:20px;height:20px;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}.media-library-portal-host{position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center;visibility:hidden;opacity:0;pointer-events:none;transition:opacity .2s ease,visibility .2s ease}.media-library-portal-host.is-open{visibility:visible;opacity:1;pointer-events:auto}.media-library-portal-host.is-open .media-library-modal{transform:scale(1) translateY(0)}.media-library-overlay{position:absolute;inset:0;background:#00000080;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.media-library-modal{position:relative;z-index:10000;width:90vw;max-width:900px;max-height:90vh;background:#fff;border-radius:16px;box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a;transform:scale(.95) translateY(10px);transition:transform .3s cubic-bezier(.175,.885,.32,1.275)}.library-modal-header{flex-shrink:0}.library-modal-title{font-weight:700}.library-modal-subtitle{line-height:1.5;max-width:600px}.library-close-btn{width:32px;height:32px;transition:background .15s ease,color .15s ease}.library-close-btn mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.library-close-btn:hover{background:var(--cc-sf-input-disabled-bg, #F3F4F6);color:var(--cc-sf-label-color, #374151)}.library-loading mat-icon,.library-empty mat-icon{font-size:40px;width:40px;height:40px;line-height:40px;opacity:.5}.lib-spinner{width:36px;height:36px;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}.library-error{flex-shrink:0}.library-error mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.library-grid{grid-template-columns:repeat(5,1fr);max-height:50vh;overflow-y:auto}.library-grid::-webkit-scrollbar{width:8px}.library-grid::-webkit-scrollbar-track{background:#f1f1f1}.library-grid::-webkit-scrollbar-thumb{background:#c1c1c1;border-radius:10px;border:2px solid #F1F1F1}.library-grid-item{aspect-ratio:1/1;transition:all .3s cubic-bezier(.4,0,.2,1);box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.library-grid-item:hover{transform:translateY(-4px) scale(1.02);box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a}.library-grid-item.selected{border-color:var(--cc-sf-accent-color, #3B82F6)}.library-grid-item.selected .library-grid-img{opacity:.7}.library-grid-item:hover .library-overlay-hover{opacity:1}.library-grid-img{object-fit:cover}.library-overlay-hover{inset:0;opacity:0;transition:opacity .15s ease}.library-check{top:6px;right:6px;width:22px;height:22px;box-shadow:0 1px 4px #00000026}.library-check mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}.library-modal-footer{flex-shrink:0}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-primary{background-color:var(--cc-sf-accent-color, #3B82F6)!important;border-color:var(--cc-sf-accent-color, #3B82F6)!important;color:#fff!important;font-weight:600;padding-left:32px;padding-right:32px}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-primary:hover{background-color:var(--cc-sf-btn-primary-hover-bg, #2563EB)!important}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-primary:disabled{background-color:#93c5fd!important;cursor:not-allowed}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-outline{font-weight:600;padding-left:24px;padding-right:24px;border-color:#d1d5db;color:#374151}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-outline:hover{background-color:#f9fafb}.location-subtitle{line-height:1.5}.loc-tab-btn ::ng-deep button{width:100%}.loc-tab-btn ::ng-deep button:not(.cc-btn-warning){background-color:var(--cc-sf-input-bg, #ffffff)!important;color:var(--cc-sf-label-color, #000000)!important;border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)}.loc-tab-btn ::ng-deep button:not(.cc-btn-warning):hover{background-color:var(--cc-sf-input-disabled-bg, #F3F4F6)!important}.loc-venue-item{transition:box-shadow .15s ease,border-color .15s ease}.loc-venue-item:hover{box-shadow:0 2px 8px #0000000f;border-color:var(--cc-sf-input-hover-border, #9CA3AF)}.loc-venue-search-icon{width:18px;height:18px;line-height:18px;flex-shrink:0}.loc-venue-text{white-space:nowrap;text-overflow:ellipsis}.loc-action-btn{transition:background .15s ease,color .15s ease;flex-shrink:0}.loc-action-btn mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}.loc-action-btn.loc-delete-btn{color:var(--loc-delete-color, #E53E3E)}.loc-action-btn.loc-delete-btn:hover{background:var(--cc-sf-error-bg, #FEF2F2)}.loc-action-btn.loc-edit-btn{color:var(--cc-sf-hint-color, #9CA3AF)}.loc-action-btn.loc-edit-btn:hover{color:var(--cc-sf-input-focus-border, #3B82F6);background:var(--cc-sf-dropzone-hover-bg, #EFF6FF)}.loc-search-icon{left:.75rem;width:1.1rem;height:1.1rem;line-height:1.1rem;z-index:1}.loc-suggestions-panel{top:calc(100% + 4px);left:0;right:0;z-index:300;box-shadow:0 8px 24px #0000001a;animation:mu-fade-in .15s ease;max-height:260px;overflow-y:auto}.loc-suggestion-item{transition:background .12s ease}.loc-suggestion-item:hover,.loc-suggestion-item:focus{background:var(--loc-suggestion-hover-bg, #F0F9FF)}.loc-suggestion-item:not(:last-child){border-bottom:1px solid var(--cc-sf-input-disabled-border, #F3F4F6)}.loc-suggestion-icon{width:18px;height:18px;line-height:18px;flex-shrink:0}.loc-suggestion-text{white-space:nowrap;text-overflow:ellipsis}.loc-add-btn{transition:opacity .15s ease}.loc-add-btn mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.loc-add-btn:hover{opacity:.8}.loc-map-container{box-shadow:0 2px 10px #0000000f}.loc-tba-panel{min-height:120px}.loc-tba-icon{width:40px;height:40px;line-height:40px;opacity:.6}.loc-tba-text{line-height:1.6;max-width:360px}.radio-label{display:flex!important}.radio-label .option-content{padding-left:10px}\n"] }]
|
|
2536
|
+
args: [{ selector: 'lib-form-field', standalone: false, template: "<div class=\"form-field mb-16px\" *ngIf=\"isVisible\" [class.has-error]=\"errorMessage\">\n\n <!-- \u2550\u2550 ROW Layout \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isRow\" class=\"form-row grid-row\">\n <ng-container *ngFor=\"let child of config.children\">\n <div class=\"row-field\" [style.gridColumn]=\"'span ' + getChildColSpan(child)\" *ngIf=\"child.isEnabled !== false\">\n <lib-form-field [config]=\"child\" [controller]=\"controller\" [formGroup]=\"formGroup\" [allowMulti]=\"allowMulti\">\n </lib-form-field>\n </div>\n </ng-container>\n </div>\n\n <!-- \u2550\u2550 GROUP \u2014 allowMulti (repeater) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isGroup && config.sectionConfig?.allowMulti\"\n class=\"group-section-wrapper mb-20px\"\n [class.multi-save-active]=\"config.sectionConfig?.multiSaveConfig?.active\">\n\n <!-- Multi-Save: header row with label + top-right Add button -->\n <div class=\"multi-save-header d-flex justify-content-between align-items-center mb-24\"\n *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\n <h3 class=\"group-label\" *ngIf=\"config.sectionConfig?.label\">{{ config.sectionConfig!.label }}</h3>\n <lib-button [variant]=\"'outline'\" [icon]=\"{type: 'material', value: 'add'}\" (click)=\"addGroupInstance()\"\n class=\"btn-add-multi\">\n {{ addMultiLabel }}\n </lib-button>\n </div>\n\n <!-- Standard heading (non-multiSave) -->\n <h3 class=\"group-label\"\n *ngIf=\"config.sectionConfig?.label && !config.sectionConfig?.multiSaveConfig?.active\">{{\n config.sectionConfig!.label }}</h3>\n\n <!-- \u2500\u2500 Standard (non-multiSave) repeater: accordion instances \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <ng-container *ngIf=\"!config.sectionConfig?.multiSaveConfig?.active\">\n <div *ngFor=\"let instance of instanceList; trackBy: trackByInstanceId; let i = index\"\n class=\"group-accordion-instance\"\n [class.is-expanded]=\"isGroupExpanded(i)\">\n\n <!-- Accordion header -->\n <div class=\"group-accordion-header\" (click)=\"toggleGroupAccordion(i)\"\n role=\"button\" [attr.aria-expanded]=\"isGroupExpanded(i)\">\n <div class=\"accordion-header-left d-flex align-items-center gap-10\">\n <span class=\"instance-badge\">{{ i + 1 }}</span>\n <span class=\"instance-title\">{{ config.sectionConfig!.label }} #{{ i + 1 }}</span>\n </div>\n <div class=\"accordion-header-right d-flex align-items-center gap-6\">\n <button type=\"button\" class=\"accordion-remove-btn\"\n *ngIf=\"instanceList.length > 1\"\n (click)=\"$event.stopPropagation(); removeGroupInstance(i)\"\n aria-label=\"Remove\">\n <mat-icon>delete_outline</mat-icon>\n </button>\n <mat-icon class=\"accordion-chevron\">\n {{ isGroupExpanded(i) ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}\n </mat-icon>\n </div>\n </div>\n\n <!-- Accordion body (always mounted so form controls survive collapse) -->\n <div class=\"group-accordion-body\" [hidden]=\"!isGroupExpanded(i)\">\n <div class=\"group-fields sf-grid\">\n <ng-container *ngFor=\"let field of config.sectionConfig!.children\">\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\"\n *ngIf=\"field.isEnabled !== false\">\n <lib-form-field [config]=\"field\" [controller]=\"instance.rowController\" [formGroup]=\"instance.fg\"\n [allowMulti]=\"false\">\n </lib-form-field>\n </div>\n </ng-container>\n </div>\n </div>\n </div>\n\n <!-- Full-width dashed Add button -->\n <button type=\"button\" class=\"btn-add-group\" (click)=\"addGroupInstance()\">\n <mat-icon>add</mat-icon> {{ addLabel }} {{ config.sectionConfig!.label }}\n </button>\n </ng-container>\n\n <!-- \u2500\u2500 MultiSave: card instances \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <ng-container *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\n <div *ngFor=\"let instance of instanceList; trackBy: trackByInstanceId; let i = index\"\n class=\"group-instance position-relative mb-16 overflow-hidden\"\n [class.is-editing]=\"instance.isEditing\"\n [class.is-card]=\"!instance.isEditing\">\n\n <!-- Edit / new form view -->\n <div [hidden]=\"!instance.isEditing\">\n <div class=\"group-fields sf-grid\">\n <ng-container *ngFor=\"let field of config.sectionConfig!.children\">\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\"\n *ngIf=\"field.isEnabled !== false\">\n <lib-form-field [config]=\"field\" [controller]=\"instance.rowController\" [formGroup]=\"instance.fg\"\n [allowMulti]=\"false\">\n </lib-form-field>\n </div>\n </ng-container>\n </div>\n\n <!-- Save / Cancel -->\n <div class=\"group-footer d-flex justify-content-end align-items-center gap-16 p-0-24\"\n *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"multiSaveError\">{{ multiSaveError }}</span>\n <div class=\"footer-actions d-flex gap-12\">\n <lib-button [variant]=\"'outline'\" (click)=\"cancelGroupInstance(i)\">Cancel</lib-button>\n <lib-button [variant]=\"'primary'\" (click)=\"saveGroupInstance(i)\">Save</lib-button>\n </div>\n </div>\n </div>\n\n <!-- Card view (saved state) -->\n <ng-container *ngIf=\"!instance.isEditing\">\n <div class=\"card-view d-flex justify-content-between align-items-center p-18px-24px\"\n [class.is-expanded]=\"instance.isExpanded\">\n <div class=\"card-content flex-1 d-flex flex-column gap-4 overflow-hidden\">\n <span class=\"card-title font-weight-600 overflow-hidden fs-1rem c-111827\">{{\n instance.fg.get(config.sectionConfig!.multiSaveConfig!.summaryField || '')?.value\n || '\u2014' }}</span>\n </div>\n <div class=\"card-actions d-flex align-items-center gap-16 ml-20\">\n <mat-icon class=\"icon-delete\" (click)=\"removeGroupInstance(i, true)\">delete_outline</mat-icon>\n <mat-icon class=\"icon-edit\" (click)=\"editGroupInstance(i)\">edit_outline</mat-icon>\n <mat-icon class=\"icon-expand\" (click)=\"toggleExpandGroupInstance(i)\">\n {{ instance.isExpanded ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}\n </mat-icon>\n </div>\n </div>\n </ng-container>\n </div>\n </ng-container>\n </div>\n\n <!-- \u2550\u2550 GROUP \u2014 single (non-repeater) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isGroup && config.sectionConfig && !config.sectionConfig.allowMulti\"\n class=\"group-section-wrapper mb-20px\">\n <h3 class=\"group-label\" *ngIf=\"config.sectionConfig.label\">{{ config.sectionConfig.label }}</h3>\n <div class=\"group-fields sf-grid\">\n <ng-container *ngFor=\"let field of config.sectionConfig.children\">\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\" *ngIf=\"field.isEnabled !== false\">\n <lib-form-field [config]=\"field\" [controller]=\"controller\" [formGroup]=\"groupFormGroup\" [allowMulti]=\"false\">\n </lib-form-field>\n </div>\n </ng-container>\n </div>\n </div>\n\n\n <!-- \u2550\u2550 Text Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isTextField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <textarea *ngIf=\"config.subType === 'LONG'\" class=\"field-input textarea\" [placeholder]=\"config.placeholder || ''\"\n [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\" rows=\"4\">\n </textarea>\n\n <!-- Password input with show/hide toggle -->\n <div *ngIf=\"config.subType === 'PASSWORD'\" class=\"password-wrapper position-relative d-flex align-items-center\">\n <input [type]=\"showPassword ? 'text' : 'password'\" class=\"field-input password-input\"\n [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\n <button type=\"button\"\n class=\"password-toggle position-absolute cursor-pointer d-flex align-items-center justify-content-center b-none border-none c-6B7280 p-0-25rem\"\n (click)=\"showPassword = !showPassword\" tabindex=\"-1\"\n [attr.aria-label]=\"showPassword ? 'Hide password' : 'Show password'\">\n <mat-icon class=\"eye-icon\">{{ showPassword ? 'visibility' : 'visibility_off' }}</mat-icon>\n </button>\n </div>\n\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\n <span class=\"input-prefix br-none\" *ngIf=\"config.prefix\">{{ config.prefix }}</span>\n\n <input *ngIf=\"config.subType !== 'LONG' && config.subType !== 'PASSWORD'\"\n [type]=\"config.subType === 'EMAIL' ? 'email' : config.subType === 'PHONE' ? 'tel' : 'text'\" class=\"field-input\"\n [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\"\n [readonly]=\"config.readonly\">\n\n <span class=\"input-suffix d-flex align-items-center font-weight-500\" *ngIf=\"config.suffix\">{{ config.suffix\n }}</span>\n\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\n <mat-icon class=\"lock-icon\">lock</mat-icon>\n </div>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n <div class=\"char-count-hint font-poppins font-weight-400 text-14 text-right c-6B7280\" *ngIf=\"showCharCount\">\n {{ remainingCharacters }} characters remaining\n </div>\n </div>\n\n <!-- \u2550\u2550 Number Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isNumberField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\n <span class=\"input-prefix br-none\" *ngIf=\"config.prefix\">{{ config.prefix }}</span>\n\n <input type=\"number\" class=\"field-input\" [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\"\n [min]=\"config.numberConfig?.min ?? null\" [max]=\"config.numberConfig?.max ?? null\"\n [step]=\"config.numberConfig?.step || 1\" [class.is-invalid]=\"errorMessage\" [readonly]=\"config.readonly\">\n\n <span class=\"input-suffix d-flex align-items-center font-weight-500\" *ngIf=\"config.suffix\">{{ config.suffix\n }}</span>\n\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\n <mat-icon class=\"lock-icon\">lock</mat-icon>\n </div>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Date Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isDateField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\n <input matInput [matDatepicker]=\"datePicker\" class=\"field-input date-input has-icon-right\"\n [formControlName]=\"config.name!\" [min]=\"dynamicMinDate || config.dateConfig?.minDate\" [max]=\"config.dateConfig?.maxDate\"\n [class.is-invalid]=\"errorMessage\" [placeholder]=\"config.placeholder || ''\"\n [readonly]=\"config.readonly || config.dateConfig?.inputReadonly\"\n (click)=\"!config.readonly && datePicker.open()\">\n <div class=\"date-icon-wrapper position-absolute d-flex align-items-center justify-content-center\"\n *ngIf=\"!config.readonly\">\n <mat-datepicker-toggle matSuffix [for]=\"datePicker\"></mat-datepicker-toggle>\n </div>\n <mat-datepicker #datePicker></mat-datepicker>\n\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\n <mat-icon class=\"lock-icon\">lock</mat-icon>\n </div>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Time Input \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isTimeField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\n <input type=\"time\" class=\"field-input time-input\" [formControlName]=\"config.name!\"\n [class.is-invalid]=\"errorMessage\" [readonly]=\"!!config.readonly\">\n\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\n <mat-icon class=\"lock-icon\">lock</mat-icon>\n </div>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Autocomplete \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isAutocomplete\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <!-- Hidden real control (stores the code value) -->\n <input type=\"hidden\" [formControlName]=\"config.name!\">\n\n <div class=\"autocomplete-wrapper position-relative d-flex align-items-center w-100\"\n [class.is-invalid]=\"errorMessage\" [class.readonly]=\"config.readonly\">\n <!-- Search icon -->\n <mat-icon class=\"ac-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">search</mat-icon>\n\n <input class=\"field-input ac-input\" [formControl]=\"autocompleteInputCtrl\" [matAutocomplete]=\"auto\"\n [placeholder]=\"config.placeholder || 'Search\u2026'\" [readonly]=\"!!config.readonly\" [class.is-invalid]=\"errorMessage\"\n (blur)=\"onAutocompleteClear()\" autocomplete=\"off\">\n\n <!-- Clear button -->\n <button type=\"button\"\n class=\"ac-clear-btn position-absolute d-flex align-items-center justify-content-center cursor-pointer rounded-50 b-none border-none c-9CA3AF p-0-2rem\"\n *ngIf=\"autocompleteInputCtrl.value && !config.readonly\"\n (click)=\"autocompleteInputCtrl.setValue(''); updateValue(null)\" tabindex=\"-1\" aria-label=\"Clear\">\n <mat-icon>close</mat-icon>\n </button>\n\n <mat-autocomplete #auto=\"matAutocomplete\" [panelWidth]=\"'auto'\">\n <mat-option *ngFor=\"let option of filteredOptions\" [value]=\"option.label\"\n (onSelectionChange)=\"onAutocompleteSelected(option)\">\n <span class=\"ac-option-label\">{{ option.label }}</span>\n\n <!-- Dynamic display fields (image / email / phone / text) -->\n <div class=\"ac-display-fields d-flex flex-wrap gap-6 mt-2\" *ngIf=\"option['displayMeta']?.length\">\n <ng-container *ngFor=\"let field of option['displayMeta']\">\n\n <!-- Image avatar -->\n <span *ngIf=\"field.type === 'image' && field.value\" class=\"ac-df-item ac-df-image\">\n <img [src]=\"field.value\" [alt]=\"field.label || 'image'\" class=\"ac-df-avatar\">\n </span>\n\n <!-- Email -->\n <span *ngIf=\"field.type === 'email' && field.value\" class=\"ac-df-item ac-df-chip\">\n <mat-icon class=\"ac-df-icon\">mail_outline</mat-icon>\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\n {{ field.value }}\n </span>\n\n <!-- Phone -->\n <span *ngIf=\"field.type === 'phone' && field.value\" class=\"ac-df-item ac-df-chip\" [class]=\"field.className\">\n <mat-icon class=\"ac-df-icon\">phone</mat-icon>\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\n {{ field.value }}\n </span>\n\n <!-- Custom / Icon-based / Generic Text -->\n <span *ngIf=\"field.type !== 'image' && field.type !== 'email' && field.type !== 'phone' && field.value\" \n class=\"ac-df-item\" [class.ac-df-chip]=\"!!field.icon\" [class]=\"field.className\">\n <mat-icon class=\"ac-df-icon\" *ngIf=\"field.icon\">{{ field.icon }}</mat-icon>\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\n {{ field.value }}\n </span>\n\n </ng-container>\n </div>\n </mat-option>\n <mat-option *ngIf=\"filteredOptions.length === 0\" disabled class=\"ac-no-results fs-0-8125rem c-6B7280\">\n No results found\n </mat-option>\n </mat-autocomplete>\n\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\n <mat-icon class=\"lock-icon\">lock</mat-icon>\n </div>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Dropdown \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isDropdown\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <select *ngIf=\"config.subType === 'SINGLE'\" class=\"field-input\" [formControlName]=\"config.name!\"\n [class.is-invalid]=\"errorMessage\">\n <option [ngValue]=\"null\" disabled selected>{{ config.placeholder || 'Select' }}</option>\n <option *ngFor=\"let option of localOptionList\" [value]=\"option.code\">\n {{ option.label }}\n </option>\n </select>\n\n <!-- MULTIPLE SELECT: custom panel with checkboxes -->\n <div *ngIf=\"config.subType === 'MULTIPLE'\" class=\"multi-select-wrapper\"\n [class.is-invalid]=\"errorMessage\">\n\n <div class=\"field-input multi-select-trigger d-flex align-items-center justify-content-between cursor-pointer\"\n [class.ms-open]=\"isMultiDropdownOpen\"\n (click)=\"toggleMultiDropdown($event)\">\n <span *ngIf=\"multiSelectedCount > 0\" class=\"multi-select-value fs-0-9rem\">\n {{ multiSelectedCount }} selected\n </span>\n <span *ngIf=\"multiSelectedCount === 0\" class=\"multi-select-placeholder\">\n {{ config.placeholder || selectPlaceholderLabel }}\n </span>\n <mat-icon class=\"multi-select-arrow\">\n {{ isMultiDropdownOpen ? expandLessLabel : expandMoreLabel }}\n </mat-icon>\n </div>\n\n <div class=\"multi-select-panel\" *ngIf=\"isMultiDropdownOpen\"\n (click)=\"$event.stopPropagation()\">\n <label *ngFor=\"let option of localOptionList\"\n class=\"multi-select-option d-flex align-items-center gap-8 cursor-pointer\">\n <input type=\"checkbox\"\n [checked]=\"isChecked(option.code)\"\n [disabled]=\"!!config.disabled\"\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\">\n <span class=\"fs-0-875rem c-111827\">{{ option.label }}</span>\n </label>\n <div *ngIf=\"!localOptionList?.length\"\n class=\"multi-select-empty fs-0-875rem c-6B7280\">\n {{ noOptionsAvailableLabel }}\n </div>\n </div>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Radio \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isRadio\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"radio-group\" [class.is-invalid]=\"errorMessage\"\n [class]=\"config.optionConfig?.layout ? 'layout-' + config.optionConfig!.layout.toLowerCase() : ''\">\n <label *ngFor=\"let option of localOptionList\" class=\"radio-label\"\n [class.card-item]=\"config.subType === 'CARD'\"\n [class.selected]=\"formGroup.get(config.name!)?.value === option.code\"\n [style.gridColumn]=\"config.optionConfig?.layout?.toUpperCase() === 'COLUMN' ? 'span ' + getOptionColSpan(option) : null\">\n <input type=\"radio\" [formControlName]=\"config.name!\" [value]=\"option.code\">\n <div class=\"option-content d-flex flex-column gap-4 flex-1 text-left\">\n <span class=\"label-text text-16 c-1F2937\">{{ option.label }}</span>\n <span class=\"option-hint text-13 color-gray\" *ngIf=\"option.hint\">{{ option.hint }}</span>\n </div>\n </label>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Checkbox \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isCheckbox\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label && config.subType === 'LIST'\"\n class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div *ngIf=\"config.subType === 'BOOL'\" class=\"checkbox-single\">\n <label class=\"checkbox-label d-flex align-items-center gap-8 cursor-pointer fs-0-875rem c-111827\">\n <input type=\"checkbox\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\n <span>{{ config.label }}</span>\n </label>\n </div>\n\n <div *ngIf=\"config.subType === 'LIST' || config.subType === 'CARD'\" class=\"checkbox-group d-flex flex-column gap-8\"\n [class.is-invalid]=\"errorMessage\"\n [class]=\"config.optionConfig?.layout ? 'layout-' + config.optionConfig!.layout.toLowerCase() : ''\">\n <label *ngFor=\"let option of localOptionList\"\n class=\"checkbox-label d-flex align-items-center gap-8 cursor-pointer fs-0-875rem c-111827\"\n [class.card-item]=\"config.subType === 'CARD'\" [class.selected]=\"isChecked(option.code)\"\n [style.gridColumn]=\"config.optionConfig?.layout?.toUpperCase() === 'COLUMN' ? 'span ' + getOptionColSpan(option) : null\">\n <input type=\"checkbox\" [checked]=\"isChecked(option.code)\" [disabled]=\"!!config.disabled\"\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\">\n <div class=\"option-content d-flex flex-column gap-4 flex-1 text-left\">\n <span class=\"label-text text-16 c-1F2937\">{{ option.label }}</span>\n <span class=\"option-hint text-13 color-gray\" *ngIf=\"option.hint\">{{ option.hint }}</span>\n </div>\n </label>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Chip \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isChip\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"chip-group d-flex flex-wrap gap-8\" [class.is-invalid]=\"errorMessage\">\n <label *ngFor=\"let option of localOptionList\"\n class=\"chip-label cursor-pointer p-6px-14px b-ffffff c-374151 b-1px-solid-D1D5DB br-20px fs-0-875rem\"\n [class.selected]=\"isChecked(option.code)\">\n <input type=\"checkbox\" [checked]=\"isChecked(option.code)\" [disabled]=\"!!config.disabled\"\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\" hidden>\n <span>{{ option.label }}</span>\n </label>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Switch \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isSwitch\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label class=\"switch-container d-flex justify-content-between align-items-center cursor-pointer\">\n <span class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">{{ config.label }}</span>\n <div class=\"switch position-relative\">\n <input type=\"checkbox\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\n <span class=\"slider position-absolute cursor-pointer\"></span>\n </div>\n </label>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Rich Text \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isRichText\" class=\"field-wrapper component-rich-text d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"rich-text-container\" [class.is-invalid]=\"errorMessage\">\n <quill-editor [formControlName]=\"config.name!\" class=\"rich-text-editor d-block w-100\"\n [placeholder]=\"config.richTextConfig?.placeholder || config.placeholder || ''\"\n [styles]=\"{height: config.richTextConfig?.height || '200px'}\">\n </quill-editor>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n <div class=\"char-count-hint font-poppins font-weight-400 text-14 text-right c-6B7280\" *ngIf=\"showCharCount\">\n {{ remainingCharacters }} characters remaining\n </div>\n </div>\n\n <!-- \u2550\u2550 Rating \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isRating\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <div class=\"rating-group d-flex gap-4\" [class.is-invalid]=\"errorMessage\">\n <span *ngFor=\"let star of getStarArray()\" class=\"star d-inline-flex align-items-center cursor-pointer\"\n [class.filled]=\"isStarFilled(star)\" [class.half]=\"isStarHalf(star)\" (click)=\"onRatingChange(star, $event)\">\n <mat-icon>{{ isStarFilled(star) || isStarHalf(star) ? 'star' : 'star_border' }}</mat-icon>\n </span>\n </div>\n\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n <!-- \u2550\u2550 Generated Field (read-only) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isGenerated\" class=\"field-wrapper d-flex flex-column gap-6\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">{{ config.label\n }}</label>\n <div class=\"generated-value fs-0-875rem p-0-625rem-0-875rem b-F3F4F6 b-1px-solid-E5E7EB br-8px c-6B7280\">{{ value ||\n '-' }}</div>\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint\">{{ config.hint }}</span>\n </div>\n\n <!-- \u2550\u2550 File Upload \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isFileUpload\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n\n <!-- Drop Zone -->\n <div\n class=\"upload-drop-zone d-flex flex-column align-items-center justify-content-center gap-8 cursor-pointer text-center p-32px-24px us-none\"\n [class.drag-over]=\"isDragOver\" [class.has-files]=\"value?.length\" [class.is-invalid]=\"errorMessage\"\n (dragover)=\"onDragOver($event)\" (dragleave)=\"onDragLeave($event)\" (drop)=\"onFileDrop($event)\"\n (click)=\"fileInput.click()\">\n\n <!-- Icon with accent-colour pill background -->\n <div class=\"upload-icon-wrap mb-4\">\n <div class=\"dropzone-icon-pill d-flex align-items-center justify-content-center\">\n <mat-icon class=\"upload-cloud-icon\">cloud_upload</mat-icon>\n </div>\n </div>\n\n <p class=\"upload-main-text font-weight-600 m-0 fs-0-9rem\">Drag & drop files here</p>\n <p class=\"upload-sub-text m-0 fs-0-8rem c-64748B\">or <span class=\"upload-link\">Browse files</span></p>\n <p class=\"upload-hint-text m-0 fs-0-78rem c-64748B\" *ngIf=\"config.attachmentConfig?.acceptLabel\">\n Supported: <span class=\"upload-formats font-weight-500\">{{ config.attachmentConfig!.acceptLabel }}</span>\n </p>\n <p class=\"upload-hint-text m-0 fs-0-78rem c-64748B\" *ngIf=\"!config.attachmentConfig?.acceptLabel && config.hint\">\n {{ config.hint }}\n </p>\n <span class=\"upload-size-badge fs-0-72rem\" *ngIf=\"config.attachmentConfig?.maxSizeMB\">\n Max {{ config.attachmentConfig!.maxSizeMB }}MB\n </span>\n\n <!-- Hidden native file input -->\n <input #fileInput type=\"file\" hidden [attr.multiple]=\"config.attachmentConfig?.multiple ? true : null\"\n [attr.accept]=\"config.attachmentConfig?.accept || null\" (change)=\"onFileSelected($event)\">\n </div>\n\n <!-- Uploaded file list -->\n <div class=\"uploaded-list d-flex flex-column gap-8 mt-10\" *ngIf=\"value?.length\">\n <div *ngFor=\"let f of value; let i = index\"\n class=\"uploaded-item d-flex align-items-center gap-10 p-10px-14px br-8px\"\n [class.uploading]=\"f.isUploading\">\n\n <!-- Uploading spinner -->\n <ng-container *ngIf=\"f.isUploading; else fileReady\">\n <div class=\"upload-spinner rounded-50 b-2px-solid-E2E8F0\"></div>\n <div class=\"file-info flex-1 d-flex flex-column\">\n <span class=\"file-name font-weight-500 overflow-hidden fs-0-85rem\" [title]=\"f.name\">{{ f.name }}</span>\n <span class=\"file-size uploading-label fs-0-72rem\">Uploading...</span>\n </div>\n </ng-container>\n\n <!-- Normal state once upload is done -->\n <ng-template #fileReady>\n <mat-icon class=\"file-type-icon\">{{ getFileIcon(f.type) }}</mat-icon>\n <img *ngIf=\"f.type?.startsWith('image') && f.dataUrl\" [src]=\"f.dataUrl\" class=\"file-thumb rounded-4\"\n alt=\"preview\">\n <div class=\"file-info flex-1 d-flex flex-column\">\n <span class=\"file-name font-weight-500 overflow-hidden fs-0-85rem\" [title]=\"f.name\">{{ f.name }}</span>\n <span class=\"file-size fs-0-72rem\">{{ formatFileSize(f.size) }}</span>\n </div>\n </ng-template>\n\n <!-- Compact icon-only remove button -->\n <button type=\"button\" class=\"file-remove-btn d-flex align-items-center justify-content-center rounded-50\"\n [disabled]=\"f.isUploading\" (click)=\"!f.isUploading && removeUploadedFile(i)\"\n aria-label=\"Remove file\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n </div>\n\n <!-- Validation / file errors -->\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"fileUploadError\">{{ fileUploadError }}</span>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage && !fileUploadError\">{{ errorMessage }}</span>\n <span class=\"field-hint c-6B7280 fs-0-75rem\"\n *ngIf=\"config.hint && !errorMessage && !fileUploadError && !config.attachmentConfig?.acceptLabel\">\n {{ config.hint }}\n </span>\n </div>\n\n <!-- \u2550\u2550 Media Upload (Type 2) \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isMediaUpload\" class=\"field-wrapper media-upload-wrapper d-flex flex-column gap-6 p-0 border-none b-none\"\n [formGroup]=\"formGroup\">\n\n <!-- Hidden file input lives outside *ngIf \u2014 triggered via ViewChild -->\n <input #mediaDeviceInput type=\"file\" hidden multiple accept=\"image/*\" (change)=\"onMediaFileSelected($event)\">\n\n <!-- Two-column layout -->\n <div class=\"mu-layout d-grid align-items-start\">\n\n <!-- \u2500\u2500 LEFT PANEL \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"mu-left d-flex flex-column gap-20\">\n\n <!-- Header: title + max-items badge -->\n <div class=\"mu-header d-flex align-items-start flex-wrap gap-10\">\n <h3 class=\"mu-title m-0 c-111827 fs-1-35rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </h3>\n <span\n class=\"mu-badge d-inline-flex align-items-center rounded-20 color-white font-weight-600 fs-0-72rem p-4px-12px b-111827\"\n *ngIf=\"config.attachmentConfig?.maxFiles\">\n {{ controller.labels['LBL_MEDIA_MAX_PREFIX'] || 'Maximum' }}\n {{ config.attachmentConfig?.maxFiles }}\n {{ controller.labels['LBL_MEDIA_MAX_SUFFIX'] || 'Image & Videos' }}\n </span>\n </div>\n\n <!-- Description -->\n <p class=\"mu-description m-0 fs-0-875rem c-6B7280\" *ngIf=\"config.attachmentConfig?.description\">\n {{ config.attachmentConfig?.description }}\n </p>\n <p class=\"mu-description m-0 fs-0-875rem c-6B7280\"\n *ngIf=\"!config.attachmentConfig?.description && controller.labels['LBL_MEDIA_DESC']\">\n {{ controller.labels['LBL_MEDIA_DESC'] }}\n </p>\n\n <!-- Feature bullet list -->\n <ul class=\"mu-features m-0 p-0 d-flex flex-column gap-8 ls-none\"\n *ngIf=\"config.attachmentConfig?.features?.length || controller.labels['LBL_MEDIA_FEATURE_1']\">\n <ng-container *ngIf=\"config.attachmentConfig?.features?.length\">\n <li *ngFor=\"let f of config.attachmentConfig?.features\"\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ f }}\n </li>\n </ng-container>\n <ng-container *ngIf=\"!config.attachmentConfig?.features?.length\">\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_1']\"\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_1'] }}\n </li>\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_2']\"\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_2'] }}\n </li>\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_3']\"\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_3'] }}\n </li>\n </ng-container>\n </ul>\n\n <!-- Backdrop to close dropdown on outside click -->\n <div class=\"media-menu-backdrop\" *ngIf=\"showMediaMenu\"\n (click)=\"$event.stopPropagation(); showMediaMenu = false\"></div>\n\n <!-- Add Media button + dropdown -->\n <div class=\"media-add-container position-relative\" (click)=\"showMediaMenu = !showMediaMenu\">\n <lib-button id=\"btn-add-media-{{ config.name }}\" [variant]=\"'warning'\"\n [icon]=\"{type: 'material', value: 'add_photo_alternate'}\">\n {{ controller.labels['LBL_ADD_MEDIA'] || 'Add media' }}\n <mat-icon class=\"menu-chevron fs-18px\">add</mat-icon>\n </lib-button>\n\n <div class=\"media-dropdown position-absolute rounded-12 overflow-hidden b-ffffff b-1px-solid-E5E7EB\"\n *ngIf=\"showMediaMenu\" role=\"menu\" (click)=\"$event.stopPropagation()\">\n <!-- Video -->\n <button id=\"btn-media-video-{{ config.name }}\" type=\"button\"\n class=\"media-dropdown-item d-flex align-items-center gap-12 w-100 cursor-pointer text-left b-none border-none p-12px-16px bb-1px-solid-F3F4F6\"\n (click)=\"onMediaMenuVideo(); showMediaMenu = false\" role=\"menuitem\">\n <span\n class=\"media-drop-icon media-drop-icon--video d-flex align-items-center justify-content-center rounded-8\"><mat-icon>videocam</mat-icon></span>\n <span class=\"media-drop-text d-flex flex-column flex-1\">\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\n controller.labels['LBL_MEDIA_VIDEO'] || 'Video' }}</span>\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_VIDEO_DESC'] || 'Add\n YouTube URL'\n }}</span>\n </span>\n </button>\n <!-- Device -->\n <button id=\"btn-media-device-{{ config.name }}\" type=\"button\"\n class=\"media-dropdown-item d-flex align-items-center gap-12 w-100 cursor-pointer text-left b-none border-none p-12px-16px bb-1px-solid-F3F4F6\"\n (click)=\"onMediaMenuDevice(); showMediaMenu = false\" role=\"menuitem\">\n <span\n class=\"media-drop-icon media-drop-icon--device d-flex align-items-center justify-content-center rounded-8\"><mat-icon>upload</mat-icon></span>\n <span class=\"media-drop-text d-flex flex-column flex-1\">\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\n controller.labels['LBL_MEDIA_DEVICE'] || 'Upload from device'\n }}</span>\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_DEVICE_DESC'] ||\n 'Select images from your\n computer' }}</span>\n </span>\n </button>\n <!-- Library -->\n <button id=\"btn-media-library-{{ config.name }}\" type=\"button\"\n class=\"media-dropdown-item d-flex align-items-center gap-12 w-100 cursor-pointer text-left b-none border-none p-12px-16px bb-1px-solid-F3F4F6\"\n (click)=\"onMediaMenuLibrary(); showMediaMenu = false\" role=\"menuitem\">\n <span\n class=\"media-drop-icon media-drop-icon--library d-flex align-items-center justify-content-center rounded-8\"><mat-icon>photo_library</mat-icon></span>\n <span class=\"media-drop-text d-flex flex-column flex-1\">\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\n controller.labels['LBL_MEDIA_LIBRARY'] || 'Upload from library'\n }}</span>\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_LIBRARY_DESC'] ||\n 'Choose from default\n images' }}</span>\n </span>\n </button>\n </div>\n </div>\n\n <!-- YouTube URL Input (inline below button) -->\n <div class=\"youtube-input-panel d-flex flex-column gap-8 p-16 rounded-10 b-FFFAF1 b-1px-solid-E5E7EB\"\n *ngIf=\"showYoutubeInput\">\n <label class=\"youtube-panel-label d-flex align-items-center gap-6 font-weight-600 fs-0-875rem c-111827\">\n {{ controller.labels['LBL_YOUTUBE_URL'] || 'Video URL' }}\n </label>\n <div class=\"youtube-input-row d-flex gap-8\">\n <input id=\"input-youtube-url-{{ config.name }}\" type=\"url\" class=\"field-input youtube-url-input\"\n [(ngModel)]=\"youtubeUrlInput\" [ngModelOptions]=\"{standalone: true}\"\n [placeholder]=\"controller.labels['PH_YOUTUBE_URL'] || 'Video URL'\" (keyup.enter)=\"addYoutubeMedia()\">\n <lib-button id=\"btn-add-youtube-{{ config.name }}\" [variant]=\"'secondary'\" (click)=\"addYoutubeMedia()\">\n {{ controller.labels['LBL_ADD'] || 'Add' }}\n </lib-button>\n </div>\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"youtubeUrlError\">{{ youtubeUrlError }}</span>\n </div>\n\n <div\n class=\"media-upload-status d-flex align-items-center gap-8 mt-4 color-error rounded-8 font-weight-500 p-10px-14px b-FEF2F2 fs-0-85rem\"\n *ngIf=\"mediaUploadError\">\n <mat-icon class=\"status-icon fs-18px\">error_outline</mat-icon>\n <span>{{ mediaUploadError }}</span>\n </div>\n\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\n </div>\n <!-- end left panel -->\n\n <!-- \u2500\u2500 RIGHT PANEL (carousel) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"mu-right d-flex flex-column gap-12\">\n\n <!-- Carousel (when items exist) -->\n <div class=\"media-carousel-section d-flex flex-column gap-12\" *ngIf=\"mediaItems.length\">\n <div\n class=\"media-carousel-main position-relative w-100 overflow-hidden d-flex align-items-center justify-content-center br-12px b-0F172A\">\n <button id=\"btn-carousel-prev-{{ config.name }}\" type=\"button\"\n class=\"carousel-nav carousel-nav--prev position-absolute rounded-50 cursor-pointer d-flex align-items-center justify-content-center border-none b-rgba-255-255-255-0-85\"\n (click)=\"mediaCarouselPrev()\" [disabled]=\"mediaCarouselIndex === 0\" aria-label=\"Previous\">\n <mat-icon>chevron_left</mat-icon>\n </button>\n\n <div class=\"carousel-viewer position-absolute d-flex align-items-center justify-content-center\"\n *ngFor=\"let item of mediaItems; let i = index\" [hidden]=\"i !== mediaCarouselIndex\">\n <div *ngIf=\"item.isUploading\"\n class=\"carousel-uploading d-flex flex-column align-items-center gap-12 c-94A3B8 fs-0-85rem\">\n <div class=\"carousel-spinner rounded-50 b-3px-solid-rgba-255-255-255-0-15\"></div>\n <span>{{ controller.labels['LBL_UPLOADING'] || 'Uploading\u2026' }}</span>\n </div>\n <ng-container *ngIf=\"!item.isUploading && item.mediaType === 'youtube'\">\n <iframe class=\"carousel-iframe w-100 h-100 br-12px\" [src]=\"item.url | trustedUrl\" frameborder=\"0\"\n allowfullscreen\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\">\n </iframe>\n </ng-container>\n <ng-container *ngIf=\"!item.isUploading && item.mediaType === 'image'\">\n <img class=\"carousel-image w-100 h-100 br-12px\" [src]=\"item.url\" alt=\"Media\">\n </ng-container>\n <button id=\"btn-remove-media-{{ config.name }}-{{ i }}\" type=\"button\"\n class=\"carousel-remove-btn position-absolute rounded-50 cursor-pointer d-flex align-items-center justify-content-center border-none b-rgba-0-0-0-0-55\"\n [disabled]=\"item.isUploading\" (click)=\"removeMediaItem(i)\" aria-label=\"Remove\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n\n <button id=\"btn-carousel-next-{{ config.name }}\" type=\"button\"\n class=\"carousel-nav carousel-nav--next position-absolute rounded-50 cursor-pointer d-flex align-items-center justify-content-center border-none b-rgba-255-255-255-0-85\"\n (click)=\"mediaCarouselNext()\" [disabled]=\"mediaCarouselIndex === mediaItems.length - 1\" aria-label=\"Next\">\n <mat-icon>chevron_right</mat-icon>\n </button>\n\n <div class=\"carousel-dots position-absolute d-flex gap-6\">\n <span *ngFor=\"let item of mediaItems; let i = index\"\n class=\"carousel-dot rounded-50 cursor-pointer b-rgba-255-255-255-0-45\"\n [class.active]=\"i === mediaCarouselIndex\" (click)=\"mediaGoTo(i)\"></span>\n </div>\n </div>\n\n <!-- Thumbnail strip -->\n <div class=\"media-thumbnail-strip d-flex flex-wrap gap-8 pb-4px\">\n <div *ngFor=\"let item of mediaThumbnails; let i = index\"\n class=\"media-thumb rounded-8 overflow-hidden cursor-pointer d-flex align-items-center justify-content-center b-2px-solid-transparent b-E2E8F0\"\n [class.active]=\"i === mediaCarouselIndex\" (click)=\"mediaGoTo(i)\">\n <div *ngIf=\"item.isUploading\"\n class=\"thumb-uploading d-flex align-items-center justify-content-center w-100 h-100\">\n <div class=\"thumb-spinner rounded-50 b-2px-solid-E2E8F0\"></div>\n </div>\n <img *ngIf=\"!item.isUploading && item.mediaType === 'youtube' && item.thumbnailUrl\"\n [src]=\"item.thumbnailUrl\" class=\"thumb-img w-100 h-100\" alt=\"Video thumbnail\">\n <div *ngIf=\"!item.isUploading && item.mediaType === 'youtube' && !item.thumbnailUrl\"\n class=\"thumb-yt-placeholder d-flex align-items-center justify-content-center w-100 h-100 b-1E293B c-EF4444\">\n <mat-icon>play_circle</mat-icon>\n </div>\n <img *ngIf=\"!item.isUploading && item.mediaType === 'image' && item.url\" [src]=\"item.url\"\n class=\"thumb-img w-100 h-100\" alt=\"Image thumbnail\">\n </div>\n </div>\n </div>\n\n <!-- Empty right-side placeholder -->\n <div\n class=\"mu-right-empty d-flex flex-column align-items-center justify-content-center gap-10 h-100 text-center p-24 br-12px b-FFFAF1 c-94A3B8 b-2px-dashed-CBD5E1\"\n *ngIf=\"!mediaItems.length\" (click)=\"onMediaMenuDevice()\">\n <mat-icon class=\"mu-right-empty-icon fs-52px\">perm_media</mat-icon>\n <p>{{ controller.labels['LBL_ADD_MEDIA'] || 'Add media' }}</p>\n </div>\n\n </div>\n <!-- end right panel -->\n\n </div><!-- end mu-layout -->\n </div>\n\n\n <!-- \u2550\u2550 Library Image Picker Modal \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <!-- Wrapper is always in DOM (hidden) so @ViewChild can move it to body -->\n <div #libraryModal class=\"media-library-portal-host\" [class.is-open]=\"showLibraryModal\">\n\n <!-- Backdrop -->\n <div class=\"media-library-overlay\" (click)=\"closeLibraryModal()\"></div>\n\n <!-- Modal card -->\n <div class=\"media-library-modal d-flex flex-column overflow-hidden b-ffffff br-16px\"\n role=\"dialog\" aria-modal=\"true\">\n <div class=\"library-modal-header d-flex align-items-start justify-content-between p-24px-28px bb-1px-solid-E5E7EB\">\n <div class=\"header-left d-flex flex-column gap-8\">\n <h3 class=\"library-modal-title m-0 color-dark fs-1-25rem\">\n {{ controller.labels['LBL_ADD_IMAGES'] || 'Add Images' }}\n </h3>\n <p class=\"library-modal-subtitle m-0 color-gray fs-0-85rem\">\n {{ controller.labels['LBL_LIBRARY_MODAL_DESC'] || 'Select images from your library.' }}\n </p>\n </div>\n <button id=\"btn-close-library-{{ config.name }}\" type=\"button\"\n class=\"library-close-btn d-flex align-items-center justify-content-center cursor-pointer rounded-50 border-none b-none c-9CA3AF\"\n (click)=\"closeLibraryModal()\" aria-label=\"Close\">\n <mat-icon>close</mat-icon>\n </button>\n </div>\n\n <!-- Loading -->\n <div class=\"library-loading\" *ngIf=\"libraryLoading\">\n <div class=\"lib-spinner rounded-50 b-3px-solid-E2E8F0\"></div>\n <span>{{ controller.labels['LBL_LOADING'] || 'Loading\u2026' }}</span>\n </div>\n\n <!-- Error -->\n <div class=\"library-error d-flex align-items-center gap-8 color-error b-FEF2F2 fs-0-875rem p-16px-24px\"\n *ngIf=\"libraryError && !libraryLoading\">\n <mat-icon>error_outline</mat-icon>\n {{ libraryError }}\n </div>\n\n <!-- Image grid -->\n <div class=\"library-grid d-grid gap-16 flex-1 p-28px b-F9FAFB\" *ngIf=\"!libraryLoading && libraryImages.length\">\n <div *ngFor=\"let img of libraryImages; let li = index\" id=\"lib-img-{{ config.name }}-{{ li }}\"\n class=\"library-grid-item position-relative rounded-12 overflow-hidden cursor-pointer bg-white b-3px-solid-transparent\"\n [class.selected]=\"isLibraryItemSelected(img)\" (click)=\"toggleLibraryItem(img)\">\n <img [src]=\"getLibraryItemUrl(img)\" class=\"library-grid-img w-100 h-100 d-block\" alt=\"Library image\">\n <div\n class=\"library-check position-absolute bg-white rounded-50 d-flex align-items-center justify-content-center c-3B82F6\"\n *ngIf=\"isLibraryItemSelected(img)\">\n <mat-icon>check_circle</mat-icon>\n </div>\n <div class=\"library-overlay-hover position-absolute b-rgba-59-130-246-0-12\"></div>\n </div>\n </div>\n\n <!-- Empty library -->\n <div\n class=\"library-empty d-flex flex-column align-items-center justify-content-center gap-12 flex-1 c-9CA3AF fs-0-875rem p-48px-24px\"\n *ngIf=\"!libraryLoading && !libraryError && libraryImages.length === 0\">\n <mat-icon>image_not_supported</mat-icon>\n <span>{{ controller.labels['LBL_LIBRARY_EMPTY'] || 'No images found in library.' }}</span>\n </div>\n\n <!-- Footer -->\n <div class=\"library-modal-footer d-flex align-items-center justify-content-end bg-white p-20px-28px bt-1px-solid-E5E7EB\">\n <div class=\"library-footer-actions d-flex gap-12\">\n <lib-button id=\"btn-library-cancel-{{ config.name }}\" [variant]=\"'outline'\" (click)=\"closeLibraryModal()\">\n {{ controller.labels['LBL_CANCEL'] || 'Cancel' }}\n </lib-button>\n <lib-button id=\"btn-library-confirm-{{ config.name }}\" [variant]=\"'primary'\"\n [disabled]=\"librarySelectedIds.size === 0\" (click)=\"confirmLibrarySelection()\">\n {{ controller.labels['LBL_CONTINUE'] || 'Continue' }}\n </lib-button>\n </div>\n </div>\n </div>\n </div>\n\n\n\n <!-- \u2550\u2550 Location Field \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div *ngIf=\"isLocation\" class=\"field-wrapper location-field-wrapper d-flex flex-column gap-6 gap-12\"\n [formGroup]=\"formGroup\">\n\n <!-- Field label -->\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px mb-0-5rem\">\n {{ config.label }}\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\n </label>\n <p class=\"location-subtitle m-0 c-6B7280 fs-0-8125rem\" *ngIf=\"config.hint\">{{ config.hint }}</p>\n\n <!-- Three-tab bar -->\n <div class=\"location-tabs d-flex gap-12 mb-24\">\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'VENUE' ? 'warning' : 'outline'\"\n (click)=\"onLocationTabChange('VENUE')\">\n {{ controller.labels['LBL_LOC_VENUE'] || 'Venue' }}\n </lib-button>\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'ONLINE' ? 'warning' : 'outline'\"\n (click)=\"onLocationTabChange('ONLINE')\">\n {{ controller.labels['LBL_LOC_ONLINE'] || 'Online Event' }}\n </lib-button>\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'TBA' ? 'warning' : 'outline'\"\n (click)=\"onLocationTabChange('TBA')\">\n {{ controller.labels['LBL_LOC_TBA'] || 'To be Announced' }}\n </lib-button>\n </div>\n\n <!-- VENUE TAB -->\n <div *ngIf=\"locationActiveTab === 'VENUE'\" class=\"loc-panel loc-venue-panel d-flex flex-column gap-12\">\n\n <p class=\"loc-section-label m-0 font-weight-600 c-111827 fs-0-9rem\">\n {{ controller.labels['LBL_LOC_ADDRESS'] || 'Location address' }}\n </p>\n\n <!-- Added venue rows -->\n <div class=\"loc-venue-list d-flex flex-column gap-8\" *ngIf=\"locationVenues.length > 0\">\n <div *ngFor=\"let venue of locationVenues; let i = index\"\n class=\"loc-venue-item d-flex align-items-center gap-10 p-10px-14px br-7px b-ffffff b-1px-solid-D1D5DB\">\n <mat-icon class=\"loc-venue-search-icon fs-18px c-9CA3AF\">search</mat-icon>\n <span class=\"loc-venue-text flex-1 overflow-hidden fs-0-875rem c-111827\">{{ venue.address || venue.name ||\n venue.description }}</span>\n <button type=\"button\"\n class=\"loc-action-btn loc-delete-btn d-flex align-items-center justify-content-center cursor-pointer rounded-50 b-none border-none p-4px\"\n (click)=\"removeLocationVenue(i)\">\n <mat-icon>delete_outline</mat-icon>\n </button>\n </div>\n </div>\n\n <!-- Location count badge -->\n <p class=\"loc-count-text m-0 font-weight-600 fs-0-8125rem c-3B82F6\"\n *ngIf=\"locationVenues.length > 0 && config.locationConfig?.allowMulti\">\n {{ locationVenues.length }} {{ controller.labels['LBL_LOC_COUNT_SUFFIX'] || 'Locations Added!' }}\n </p>\n\n <!-- Search input (hide when max reached) -->\n <div class=\"loc-search-container position-relative\" *ngIf=\"!locationMaxReached\">\n <div class=\"loc-search-wrapper position-relative d-flex align-items-center mt-4\">\n <mat-icon class=\"loc-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">search</mat-icon>\n <input\n class=\"field-input loc-search-input w-100 font-poppins flex-1 fs-0-875rem c-111827 br-7px br-8px bc-F3F4F6 pl-2-4rem bc-DC2626 pt-0-625rem pb-0-625rem pl-16px pr-16px bc-ffffff b-1px-solid-D1D5DB pr-3-5rem\"\n [placeholder]=\"config.locationConfig?.venuePlaceholder || (controller.labels['PH_LOC_VENUE'] || 'Type to search venue...')\"\n [value]=\"locationSearchText\" (input)=\"handleLocationSearchInput($event)\" (blur)=\"hideLocationSuggestions()\"\n autocomplete=\"off\" [class.is-invalid]=\"errorMessage\">\n </div>\n <!-- Suggestions dropdown -->\n <div class=\"loc-suggestions-panel position-absolute overflow-hidden br-8px b-ffffff b-1px-solid-D1D5DB\"\n *ngIf=\"locationShowSuggestions && locationSuggestions.length\">\n <div *ngFor=\"let sug of locationSuggestions\"\n class=\"loc-suggestion-item d-flex align-items-center gap-10 cursor-pointer p-10px-14px\"\n (mousedown)=\"onLocationSuggestionSelect(sug)\">\n <mat-icon class=\"loc-suggestion-icon fs-18px c-E53E3E\">place</mat-icon>\n <span class=\"loc-suggestion-text overflow-hidden fs-0-875rem c-374151\">{{ sug.description }}</span>\n </div>\n </div>\n </div>\n\n <!-- Add another button -->\n <button type=\"button\"\n class=\"loc-add-btn d-inline-flex align-items-center gap-6 cursor-pointer font-weight-600 p-0 b-none border-none fs-0-875rem c-1A56DB\"\n *ngIf=\"locationVenues.length > 0 && !locationMaxReached && config.locationConfig?.allowMulti\">\n <mat-icon>add_circle_outline</mat-icon>\n <span>{{ controller.labels['LBL_LOC_ADD_ANOTHER'] || 'Add another Location' }}</span>\n </button>\n\n <!-- Map -->\n <div class=\"loc-map-container overflow-hidden br-10px b-1px-solid-E5E7EB\"\n *ngIf=\"config.locationConfig?.showMap !== false\">\n <ng-container *ngIf=\"config.locationConfig?.googleMapsApiKey; else simpleEmbed\">\n <div [id]=\"'loc-map-' + config.name\" class=\"loc-map-frame w-100 d-block border-none\"\n [style.height]=\"config.locationConfig?.mapHeight || '300px'\"></div>\n </ng-container>\n <ng-template #simpleEmbed>\n <iframe class=\"loc-map-frame w-100 d-block border-none\"\n [style.height]=\"config.locationConfig?.mapHeight || '300px'\" [src]=\"getLocationMapEmbedUrl() | trustedUrl\"\n frameborder=\"0\" allowfullscreen loading=\"lazy\">\n </iframe>\n </ng-template>\n </div>\n\n <!-- Map hint -->\n <p class=\"loc-map-hint m-0 text-center fs-0-78rem c-6B7280\">\n {{ controller.labels['LBL_LOC_MAP_HINT'] || 'Type the venue address. A map will appear to assist you.' }}\n </p>\n </div>\n\n <!-- ONLINE TAB -->\n <div *ngIf=\"locationActiveTab === 'ONLINE'\" class=\"loc-panel loc-online-panel d-flex flex-column gap-12\">\n <p class=\"loc-section-label m-0 font-weight-600 c-111827 fs-0-9rem\">\n {{ controller.labels['LBL_LOC_ONLINE_URL'] || 'Event URL' }}\n </p>\n <div class=\"loc-search-wrapper position-relative d-flex align-items-center mt-4\">\n <mat-icon class=\"loc-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">link</mat-icon>\n <input\n class=\"field-input loc-search-input w-100 font-poppins flex-1 fs-0-875rem c-111827 br-7px br-8px bc-F3F4F6 pl-2-4rem bc-DC2626 pt-0-625rem pb-0-625rem pl-16px pr-16px bc-ffffff b-1px-solid-D1D5DB pr-3-5rem\"\n type=\"url\"\n [placeholder]=\"config.locationConfig?.onlinePlaceholder || (controller.labels['PH_LOC_ONLINE'] || 'https://zoom.us/j/...')\"\n [value]=\"locationOnlineUrl\" (input)=\"onLocationUrlChange($any($event.target).value)\"\n [class.is-invalid]=\"errorMessage\">\n </div>\n </div>\n\n <!-- TBA TAB -->\n <div *ngIf=\"locationActiveTab === 'TBA'\"\n class=\"loc-panel loc-tba-panel d-flex flex-column gap-12 justify-content-center\">\n <div\n class=\"loc-tba-content d-flex flex-column align-items-center justify-content-center text-center gap-12 p-32px-24px b-F9FAFB b-1px-dashed-D1D5DB br-10px\">\n <mat-icon class=\"loc-tba-icon fs-40px c-9CA3AF\">schedule</mat-icon>\n <p class=\"loc-tba-text m-0 c-6B7280 fs-0-9rem\">\n {{ controller.labels['LBL_LOC_TBA_DESC'] || \"This event's location is yet to be announced. Check back later\n for updates.\" }}\n </p>\n </div>\n </div>\n\n <!-- Hidden real form control -->\n <input type=\"hidden\" [formControlName]=\"config.name!\">\n\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\n </div>\n\n</div>", styles: [".d-flex{display:flex}.d-inline-flex{display:inline-flex}.d-grid{display:grid}.d-block{display:block}.d-none{display:none}.flex-column{flex-direction:column}.flex-row{flex-direction:row}.flex-row-reverse{flex-direction:row-reverse}.flex-wrap{flex-wrap:wrap}.flex-1{flex:1}.align-items-center{align-items:center}.align-items-start{align-items:flex-start}.align-items-end{align-items:flex-end}.justify-content-center{justify-content:center}.justify-content-between{justify-content:space-between}.justify-content-start{justify-content:flex-start}.justify-content-end{justify-content:flex-end}.grid-cols-12{grid-template-columns:repeat(12,1fr)}.w-100{width:100%}.h-100{height:100%}.position-relative{position:relative}.position-absolute{position:absolute}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.font-poppins{font-family:var(--cc-sf-font-family, \"Poppins\", sans-serif)}.font-weight-400{font-weight:400}.font-weight-500{font-weight:500}.font-weight-600{font-weight:600}.text-13{font-size:13px}.text-14{font-size:14px}.text-16{font-size:16px}.color-white{color:#fff}.color-dark{color:#111827}.color-gray{color:#6b7280}.color-error{color:var(--cc-sf-error-text-color, #DC2626)}.bg-white{background-color:#fff}.bg-transparent{background-color:transparent}.m-0{margin:0}.mt-4{margin-top:4px}.mt-8{margin-top:8px}.mt-10{margin-top:10px}.mt-12{margin-top:12px}.mt-16{margin-top:16px}.mt-20{margin-top:20px}.mt-24{margin-top:24px}.mb-0{margin-bottom:0}.mb-4{margin-bottom:4px}.mb-8{margin-bottom:8px}.mb-10{margin-bottom:10px}.mb-12{margin-bottom:12px}.mb-16{margin-bottom:16px}.mb-20{margin-bottom:20px}.mb-24{margin-bottom:24px}.ml-16{margin-left:16px}.ml-20{margin-left:20px}.p-0{padding:0}.p-16{padding:16px}.p-20{padding:20px}.p-24{padding:24px}.pt-20{padding-top:20px}.pb-10{padding-bottom:10px}.gap-4{gap:4px}.gap-6{gap:6px}.gap-8{gap:8px}.gap-10{gap:10px}.gap-12{gap:12px}.gap-16{gap:16px}.gap-20{gap:20px}.rounded-4{border-radius:4px}.rounded-6{border-radius:6px}.rounded-8{border-radius:8px}.rounded-10{border-radius:10px}.rounded-12{border-radius:12px}.rounded-20{border-radius:20px}.rounded-24{border-radius:24px}.rounded-50{border-radius:50%}.cursor-pointer{cursor:pointer}.overflow-hidden{overflow:hidden}.resize-vertical{resize:vertical}.box-sizing-border{box-sizing:border-box}.border-none{border:none!important}.mb-16px{margin-bottom:var(--cc-sf-grid-gap, 16px)!important}.c-DC2626{color:var(--cc-sf-label-required-color, #DC2626)!important}.ml-0-125rem{margin-left:.125rem!important}.fs-0-875rem{font-size:.875rem!important}.c-111827{color:var(--cc-sf-label-color, #111827)!important}.br-7px{border-radius:var(--cc-sf-input-radius, 7px)!important}.c-6B7280{color:var(--cc-sf-hint-color, #6B7280)!important}.fs-0-75rem{font-size:var(--cc-sf-error-text-size, .75rem)!important}.b-none{background:none!important}.p-32px-24px{padding:32px 24px!important}.us-none{-webkit-user-select:none!important;user-select:none!important}.c-1E293B{color:var(--cc-sf-label-color, #1E293B)!important}.c-3B82F6{color:var(--cc-sf-chip-selected-bg, #3B82F6)!important}.fs-0-78rem{font-size:.78rem!important}.p-10px-14px{padding:10px 14px!important}.fs-0-85rem{font-size:.85rem!important}.fs-0-72rem{font-size:.72rem!important}.c-94A3B8{color:#94a3b8!important}.p-4px{padding:4px!important}.br-8px{border-radius:var(--cc-sf-input-radius, 8px)!important}.bc-F3F4F6{background-color:var(--cc-sf-input-disabled-bg, #F3F4F6)!important}.br-none{border-right:none!important}.bl-none{border-left:none!important}.pe-none{pointer-events:none!important}.fs-1-1rem{font-size:1.1rem!important}.c-9CA3AF{color:var(--cc-sf-hint-color, #9CA3AF)!important}.pl-2-4rem{padding-left:2.4rem!important}.fs-0-8125rem{font-size:.8125rem!important}.ls-none{list-style:none!important}.br-12px{border-radius:var(--mu-carousel-radius, 12px)!important}.b-FFFAF1{background:var(--cc-sf-dropzone-bg, #FFFAF1)!important}.fs-18px{font-size:18px!important}.b-FEF2F2{background:var(--cc-sf-error-bg, #FEF2F2)!important}.bc-DC2626{border-color:var(--cc-sf-error-border, #DC2626)!important}.c-202124{color:var(--cc-sf-label-color, #202124)!important}.fs-18px{font-size:var(--cc-sf-label-size, 18px)!important}.mb-0-5rem{margin-bottom:.5rem!important}.pt-0-625rem{padding-top:var(--cc-sf-input-padding-y, .625rem)!important}.pb-0-625rem{padding-bottom:var(--cc-sf-input-padding-y, .625rem)!important}.pl-16px{padding-left:var(--cc-sf-input-padding-x, 16px)!important}.pr-16px{padding-right:var(--cc-sf-input-padding-x, 16px)!important}.bc-ffffff{background-color:var(--cc-sf-section-bg, #ffffff)!important}.b-1px-solid-D1D5DB{border:1px solid var(--cc-sf-input-border, #D1D5DB)!important}.fs-0-75rem{font-size:.75rem!important}.c-1F2937{color:var(--cc-sf-section-label-color, #1F2937)!important}.p-6px-14px{padding:var(--cc-sf-chip-padding, 6px 14px)!important}.b-ffffff{background:var(--loc-suggestion-bg, #ffffff)!important}.c-374151{color:var(--cc-sf-label-color, #374151)!important}.br-20px{border-radius:var(--cc-sf-chip-radius, 20px)!important}.fs-0-875rem{font-size:var(--cc-sf-btn-font-size, .875rem)!important}.bc-D1D5DB{background-color:var(--cc-sf-switch-track-off, #D1D5DB)!important}.pr-2-75rem{padding-right:2.75rem!important}.p-0-25rem{padding:.25rem!important}.p-0-625rem-0-875rem{padding:var(--cc-sf-generated-padding, .625rem .875rem)!important}.b-F3F4F6{background:var(--cc-sf-generated-bg, #F3F4F6)!important}.b-1px-solid-E5E7EB{border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)!important}.br-8px{border-radius:var(--cc-sf-uploaded-item-radius, 8px)!important}.c-6B7280{color:var(--ms-desc-color, #6B7280)!important}.mb-20px{margin-bottom:var(--cc-sf-section-gap, 20px)!important}.br-10px{border-radius:var(--cc-sf-input-radius, 10px)!important}.p-20px{padding:var(--cc-sf-section-padding, 20px)!important}.fs-1rem{font-size:1rem!important}.m-0-0-16px-0{margin:0 0 16px!important}.bb-2px-solid-E5E7EB{border-bottom:var(--cc-sf-section-label-border, 2px solid #E5E7EB)!important}.p-16px{padding:var(--cc-sf-instance-padding, 16px)!important}.b-F9FAFB{background:var(--loc-tba-bg, #F9FAFB)!important}.bb-1px-dashed-D1D5DB{border-bottom:var(--cc-sf-instance-divider, 1px dashed #D1D5DB)!important}.c-4B5563{color:var(--cc-sf-instance-num-color, #4B5563)!important}.fs-0-8125rem{font-size:var(--cc-sf-hint-size, .8125rem)!important}.pb-0{padding-bottom:0!important}.p-18px-24px{padding:18px 24px!important}.c-111827{color:var(--ms-title-color, #111827)!important}.bt-1px-solid-E5E7EB{border-top:1px solid #E5E7EB!important}.p-4px-10px{padding:4px 10px!important}.b-FFF5F5{background:var(--cc-sf-btn-remove-bg, #FFF5F5)!important}.c-E53E3E{color:var(--loc-delete-color, #E53E3E)!important}.b-1px-solid-FED7D7{border:var(--cc-sf-btn-remove-border, 1px solid #FED7D7)!important}.br-4px{border-radius:var(--cc-sf-btn-remove-radius, 4px)!important}.p-8px-16px{padding:8px 16px!important}.b-transparent{background:var(--cc-sf-btn-add-bg, transparent)!important}.c-3B82F6{color:var(--cc-sf-input-focus-border, #3B82F6)!important}.b-1px-dashed-CBD5E1{border:var(--cc-sf-btn-add-border, 1px dashed #CBD5E1)!important}.br-6px{border-radius:var(--cc-sf-btn-add-radius, 6px)!important}.b-1-5px-dashed-CBD5E1{border:var(--cc-sf-dropzone-border, 1.5px dashed #CBD5E1)!important}.br-12px{border-radius:var(--cc-sf-dropzone-radius, 12px)!important}.bc-FFFAF1{background-color:var(--cc-sf-dropzone-bg, #FFFAF1)!important}.c-94A3B8{color:var(--cc-sf-uploaded-remove-color, #94A3B8)!important}.fs-0-9rem{font-size:var(--cc-sf-input-font-size, .9rem)!important}.c-64748B{color:var(--cc-sf-dropzone-hint-color, #64748B)!important}.b-1px-solid-E2E8F0{border:var(--cc-sf-uploaded-item-border, 1px solid #E2E8F0)!important}.b-2px-solid-E2E8F0{border:2px solid #E2E8F0!important}.pr-3-5rem{padding-right:3.5rem!important}.p-0-0-875rem{padding:0 .875rem!important}.bc-FFFFFF{background-color:var(--cc-sf-input-bg, #FFFFFF)!important}.b-1-5px-solid-D1D5DB{border:var(--cc-sf-input-border, 1.5px solid #D1D5DB)!important}.mb-0-75rem{margin-bottom:.75rem!important}.mt-6px{margin-top:6px!important}.pr-2-4rem{padding-right:2.4rem!important}.p-0-2rem{padding:.2rem!important}.fs-1-35rem{font-size:1.35rem!important}.p-4px-12px{padding:4px 12px!important}.b-111827{background:var(--cc-sf-label-color, #111827)!important}.b-2px-dashed-CBD5E1{border:2px dashed var(--cc-sf-dropzone-border, #CBD5E1)!important}.fs-52px{font-size:52px!important}.p-12px-16px{padding:12px 16px!important}.bb-1px-solid-F3F4F6{border-bottom:1px solid var(--cc-sf-input-disabled-border, #F3F4F6)!important}.b-0F172A{background:var(--mu-carousel-bg, #0F172A)!important}.b-3px-solid-rgba-255-255-255-0-15{border:3px solid rgba(255,255,255,.15)!important}.b-rgba-255-255-255-0-85{background:#ffffffd9!important}.b-rgba-0-0-0-0-55{background:#0000008c!important}.b-rgba-255-255-255-0-45{background:#ffffff73!important}.pb-4px{padding-bottom:4px!important}.b-2px-solid-transparent{border:2px solid transparent!important}.b-E2E8F0{background:var(--mu-thumb-bg, #E2E8F0)!important}.b-1E293B{background:#1e293b!important}.c-EF4444{color:#ef4444!important}.b-rgba-0-0-0-0-5{background:#00000080!important}.br-16px{border-radius:var(--mu-modal-radius, 16px)!important}.p-24px-28px{padding:24px 28px!important}.bb-1px-solid-E5E7EB{border-bottom:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)!important}.fs-1-25rem{font-size:1.25rem!important}.p-48px-24px{padding:48px 24px!important}.b-3px-solid-E2E8F0{border:3px solid #E2E8F0!important}.p-16px-24px{padding:16px 24px!important}.p-28px{padding:28px!important}.b-3px-solid-transparent{border:3px solid transparent!important}.b-rgba-59-130-246-0-12{background:#3b82f61f!important}.p-20px-28px{padding:20px 28px!important}.c-1A56DB{color:var(--loc-add-color, #1A56DB)!important}.b-1px-dashed-D1D5DB{border:1px dashed var(--cc-sf-input-disabled-border, #D1D5DB)!important}.fs-40px{font-size:40px!important}.c-9CA3AF{color:var(--loc-tba-icon-color, #9CA3AF)!important}.form-field{font-family:var(--cc-sf-font-family, \"Poppins\", sans-serif)!important}:host{--cc-sf-input-border: #D1D5DB;--cc-sf-input-bg: #ffffff;--cc-sf-input-radius: 9px;--cc-sf-input-height: 44px;--cc-sf-label-color: #111827;--cc-sf-hint-color: #9CA3AF;--cc-sf-error-border: #EF4444;--cc-sf-error-bg: #FFF5F5;--cc-sf-accent-color: #6366F1;--cc-sf-input-focus-border: #6366F1;--cc-sf-input-hover-border: #A5B4FC;--cc-sf-input-placeholder: #C4C9D4;--cc-sf-input-disabled-bg: #F8F9FB;--cc-sf-input-disabled-border: #E5E7EB;--cc-sf-switch-track-on: #6366F1;--cc-sf-switch-track-off: #D1D5DB;--cc-sf-switch-thumb: #ffffff;--cc-sf-selected-color: #6366F1}.form-row{gap:var(--cc-sf-grid-gap, 16px)}.form-row.horizontal{display:flex;flex-direction:row}.form-row.horizontal>*{flex:1}.form-row:not(.horizontal){flex-direction:column}.form-row.grid-row{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--cc-sf-grid-gap, 16px);align-items:start}@media(max-width:640px){.form-row.grid-row{grid-template-columns:1fr}.form-row.grid-row .row-field{grid-column:span 12!important}}.field-label{font-size:.75rem;font-weight:600;line-height:1;letter-spacing:.04em;text-transform:uppercase;color:#6b7280}.field-input,input.matInput,.mat-mdc-input-element{display:block;width:100%;height:var(--cc-sf-input-height)!important;padding:0 14px!important;font-family:inherit;font-size:.9rem;color:var(--cc-sf-label-color);background-color:var(--cc-sf-input-bg)!important;border:1.5px solid var(--cc-sf-input-border)!important;border-radius:var(--cc-sf-input-radius)!important;box-sizing:border-box;box-shadow:0 1px 2px #0000000a!important;transition:all .2s cubic-bezier(.4,0,.2,1)}.field-input::placeholder,input.matInput::placeholder,.mat-mdc-input-element::placeholder{font-weight:400;font-size:14px;color:var(--cc-sf-input-placeholder)}.field-input{opacity:var(--cc-sf-input-opacity, 1);line-height:var(--cc-sf-input-line-height, 1.5);transition:var(--cc-sf-input-transition, all .2s ease)}.field-input::placeholder{font-weight:var(--cc-sf-placeholder-weight, 400);font-size:var(--cc-sf-placeholder-size, 14px);line-height:var(--cc-sf-placeholder-line-height, 100%);color:var(--cc-sf-input-placeholder)}.field-input:hover:not(:disabled):not([readonly]){border-color:var(--cc-sf-input-hover-border)!important;box-shadow:0 1px 4px #6366f114!important}.field-input:focus{outline:none;border-color:var(--cc-sf-input-focus-border)!important;box-shadow:0 0 0 3px #6366f124,0 1px 4px #6366f11a!important;background-color:#fefeff!important}.field-input:disabled,.field-input[readonly]{background-color:var(--cc-sf-input-disabled-bg)!important;color:#9ca3af!important;cursor:not-allowed;border-color:var(--cc-sf-input-disabled-border)!important;box-shadow:none!important}.field-input.is-invalid{border-color:var(--cc-sf-error-border)!important;background-color:var(--cc-sf-error-bg)!important}.field-input.is-invalid:focus{box-shadow:0 0 0 3px #ef44441f,0 1px 4px #ef44441a!important}.field-input.textarea{resize:vertical;min-height:100px;height:auto;padding:12px 16px!important}input[type=time].time-input{cursor:pointer}input[type=time].time-input::-webkit-calendar-picker-indicator{cursor:pointer;opacity:.7;filter:invert(30%)}input[type=time].time-input::-webkit-calendar-picker-indicator:hover{opacity:1}select.field-input{appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236B7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='M6 8l4 4 4-4'/%3E%3C/svg%3E\");background-position:right .5rem center;background-repeat:no-repeat;background-size:1.5em 1.5em;padding-right:2.5rem;cursor:pointer}select.field-input:disabled{cursor:not-allowed}.multi-select-wrapper{position:relative}.multi-select-wrapper .multi-select-trigger{min-height:var(--cc-sf-input-height, 44px);height:auto;-webkit-user-select:none;user-select:none}.multi-select-wrapper .multi-select-trigger.ms-open{border-color:var(--cc-sf-input-focus-border, #6366F1)!important;box-shadow:0 0 0 3px #6366f124,0 1px 4px #6366f11a!important}.multi-select-wrapper .multi-select-trigger .multi-select-value{flex:1;color:var(--cc-sf-label-color, #111827);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.multi-select-wrapper .multi-select-trigger .multi-select-placeholder{flex:1;font-weight:400;font-size:14px;color:var(--cc-sf-input-placeholder, #C4C9D4)}.multi-select-wrapper .multi-select-trigger .multi-select-arrow{font-size:20px;width:20px;height:20px;line-height:20px;color:#6b7280;flex-shrink:0}.multi-select-wrapper .multi-select-panel{position:absolute;top:calc(100% + 4px);left:0;right:0;background:#fff;border:1.5px solid var(--cc-sf-input-border, #D1D5DB);border-radius:var(--cc-sf-input-radius, 9px);box-shadow:0 8px 24px #0000001a,0 2px 6px #0000000f;z-index:1050;max-height:240px;overflow-y:auto}.multi-select-wrapper .multi-select-panel::-webkit-scrollbar{width:4px}.multi-select-wrapper .multi-select-panel::-webkit-scrollbar-thumb{background:#d1d5db;border-radius:2px}.multi-select-wrapper .multi-select-option{padding:8px 12px;border-bottom:1px solid #F3F4F6;transition:background .1s}.multi-select-wrapper .multi-select-option:last-of-type{border-bottom:none}.multi-select-wrapper .multi-select-option:hover{background:#f9fafb}.multi-select-wrapper .multi-select-option input[type=checkbox]{flex-shrink:0;cursor:pointer;accent-color:var(--cc-sf-selected-color, #6366F1);width:15px;height:15px}.multi-select-wrapper .multi-select-empty{padding:12px;text-align:center}.multi-select-wrapper.is-invalid .multi-select-trigger{border-color:var(--cc-sf-error-border, #EF4444)!important;background-color:var(--cc-sf-error-bg, #FFF5F5)!important}.char-count-hint{font-style:normal;line-height:100%;letter-spacing:.02em;margin-top:.4rem}.radio-group.layout-column,.checkbox-group.layout-column{display:grid!important;grid-template-columns:repeat(12,1fr);gap:16px;width:100%}.radio-group.layout-row,.checkbox-group.layout-row{flex-direction:column!important;gap:12px;width:100%}.radio-label input,.checkbox-label input{cursor:pointer;accent-color:var(--cc-sf-chip-selected-bg, #F05A28)}.radio-label.card-item,.checkbox-label.card-item{display:flex!important;flex-direction:row-reverse!important;justify-content:space-between!important;align-items:center!important;border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB);border-radius:12px;padding:16px 20px;box-sizing:border-box;transition:all .2s ease;background:var(--cc-sf-input-bg, #ffffff);margin-bottom:0}.radio-label.card-item input,.checkbox-label.card-item input{margin-left:16px}.radio-label.card-item.selected,.checkbox-label.card-item.selected{border-color:var(--cc-sf-selected-color);background-color:#f05a280d}.radio-label.card-item .option-content .label-text,.checkbox-label.card-item .option-content .label-text,.checkbox-single .checkbox-label{font-weight:var(--cc-sf-label-weight, 500)}.chip-label{transition:var(--cc-sf-input-transition, all .2s ease)}.chip-label:hover{background:var(--cc-sf-chip-hover-bg, #F3F4F6)}.chip-label.selected{background:var(--cc-sf-selected-color);color:var(--cc-sf-chip-selected-color, #ffffff);border-color:var(--cc-sf-selected-color)}.switch{width:50px;height:24px;display:inline-block}.switch input{opacity:0;width:0;height:0;position:absolute}.switch input:checked+.slider{background-color:var(--cc-sf-switch-track-on)!important}.switch input:checked+.slider:before{transform:translate(26px)}.switch .slider{inset:0;transition:.4s;background-color:var(--cc-sf-switch-track-off);border-radius:24px}.switch .slider:before{position:absolute;content:\"\";height:18px;width:18px;left:3px;bottom:3px;background-color:var(--cc-sf-switch-thumb);transition:.4s;border-radius:50%}.rating-group .star{transition:var(--cc-sf-input-transition, all .2s ease)}.rating-group .star mat-icon{font-size:var(--cc-sf-star-size, 28px);width:var(--cc-sf-star-size, 28px);height:var(--cc-sf-star-size, 28px);line-height:var(--cc-sf-star-size, 28px);color:var(--cc-sf-star-empty, #D1D5DB);transition:var(--cc-sf-input-transition, all .2s ease)}.rating-group .star.filled mat-icon,.rating-group .star.half mat-icon{color:var(--cc-sf-star-filled, #F59E0B)}.rating-group .star:hover mat-icon{color:var(--cc-sf-star-filled, #F59E0B)}.password-wrapper .password-toggle{right:.625rem;top:50%;transform:translateY(-50%);line-height:1;transition:color var(--cc-sf-input-transition, .2s ease)}.password-wrapper .password-toggle mat-icon.eye-icon{font-size:1.125rem;width:1.125rem;height:1.125rem;line-height:1.125rem}.password-wrapper .password-toggle:hover{color:var(--cc-sf-label-color, #374151)}.password-wrapper .password-toggle:focus{outline:none}.group-section-wrapper .group-label{font-size:var(--cc-sf-section-label-size, 1rem);font-weight:var(--cc-sf-section-label-weight, 600);color:var(--cc-sf-section-label-color, #1F2937);margin:0 0 16px;padding-left:12px;padding-top:2px;padding-bottom:2px;border-left:var(--cc-sf-section-header-accent-width, 4px) solid var(--cc-sf-section-header-accent-color, #3B82F6);line-height:1.4}.group-section-wrapper .group-fields.sf-grid{display:grid;grid-template-columns:repeat(12,1fr);gap:var(--cc-sf-grid-gap, 16px);align-items:start}@media(max-width:640px){.group-section-wrapper .group-fields.sf-grid{grid-template-columns:1fr}.group-section-wrapper .group-fields.sf-grid .sf-col{grid-column:span 12!important}}.group-section-wrapper .group-accordion-instance{border:var(--cc-sf-instance-border, 1px solid #E5E7EB);border-radius:var(--cc-sf-section-border-radius-inner, 8px);margin-bottom:8px;overflow:hidden;transition:border-color .2s ease}.group-section-wrapper .group-accordion-instance:last-of-type{margin-bottom:0}.group-section-wrapper .group-accordion-instance .group-accordion-header{display:flex;justify-content:space-between;align-items:center;padding:10px 14px;background:var(--cc-sf-repeater-accordion-header-bg, #F9FAFB);cursor:pointer;-webkit-user-select:none;user-select:none;transition:background .15s ease}.group-section-wrapper .group-accordion-instance .group-accordion-header:hover{background:var(--cc-sf-repeater-accordion-active-bg, #EFF6FF)}.group-section-wrapper .group-accordion-instance .group-accordion-header .instance-badge{width:22px;height:22px;border-radius:50%;background:var(--cc-sf-repeater-badge-bg, #E5E7EB);color:var(--cc-sf-repeater-badge-color, #374151);font-size:.75rem;font-weight:600;display:flex;align-items:center;justify-content:center;flex-shrink:0}.group-section-wrapper .group-accordion-instance .group-accordion-header .instance-title{font-size:var(--cc-sf-instance-num-size, .8125rem);font-weight:600;color:var(--cc-sf-repeater-accordion-header-color, #1F2937)}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-remove-btn{background:none;border:none;cursor:pointer;color:var(--cc-sf-btn-remove-color, #E53E3E);padding:4px;border-radius:4px;line-height:1;display:flex;align-items:center;transition:background .15s ease}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-remove-btn mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-remove-btn:hover{background:var(--cc-sf-btn-remove-hover-bg, #FED7D7)}.group-section-wrapper .group-accordion-instance .group-accordion-header .accordion-chevron{font-size:1.25rem;width:1.25rem;height:1.25rem;line-height:1.25rem;color:var(--cc-sf-instance-num-color, #4B5563)}.group-section-wrapper .group-accordion-instance .group-accordion-body{padding:var(--cc-sf-instance-padding, 16px);background:var(--cc-sf-instance-bg, #F9FAFB);border-top:var(--cc-sf-instance-divider, 1px dashed #D1D5DB)}.group-section-wrapper .btn-add-group{display:flex;align-items:center;justify-content:center;gap:6px;width:100%;padding:10px 20px;margin-top:12px;background:var(--cc-sf-btn-add-bg, transparent);color:var(--cc-sf-btn-add-color, #3B82F6);border:var(--cc-sf-btn-add-border, 1px dashed #CBD5E1);border-radius:var(--cc-sf-btn-add-radius, 6px);cursor:pointer;font-family:inherit;font-size:var(--cc-sf-btn-font-size, .875rem);font-weight:var(--cc-sf-btn-font-weight, 600);transition:var(--cc-sf-btn-transition, all .2s ease)}.group-section-wrapper .btn-add-group mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.group-section-wrapper .btn-add-group:hover{background:var(--cc-sf-btn-add-hover-bg, #EFF6FF);border-color:var(--cc-sf-btn-add-hover-border, #BFDBFE)}.group-section-wrapper .group-instance:last-child{margin-bottom:0}.group-section-wrapper.multi-save-active{border:none;box-shadow:none;padding:0;background:transparent}.group-section-wrapper.multi-save-active .multi-save-header .btn-add-multi ::ng-deep button{color:var(--ms-btn-add-color, #3B82F6);font-weight:600;font-size:.875rem;padding:8px 12px}.group-section-wrapper.multi-save-active .multi-save-header .btn-add-multi ::ng-deep button:hover{color:var(--ms-btn-add-hover, #2563EB);background-color:var(--cc-sf-btn-add-hover-bg, #EFF6FF)}.group-section-wrapper.multi-save-active .group-instance.is-card{cursor:pointer;transition:all .2s ease-in-out}.group-section-wrapper.multi-save-active .group-instance.is-card:hover{box-shadow:var(--ms-card-shadow-hover, 0 8px 24px rgba(0, 0, 0, .08));border-color:var(--cc-sf-input-focus-border, #3B82F6)}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-content .card-title{white-space:nowrap;text-overflow:ellipsis}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-content .card-desc{line-height:1.4;display:-webkit-box;-webkit-line-clamp:1;line-clamp:1;-webkit-box-orient:vertical}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view.is-expanded .card-content .card-desc{-webkit-line-clamp:unset;line-clamp:unset}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon{font-size:22px;width:22px;height:22px;color:var(--cc-sf-hint-color, #9CA3AF);transition:color .2s}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon.icon-delete:hover{color:var(--cc-sf-error-border, #DC2626)}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon.icon-edit:hover{color:var(--cc-sf-input-focus-border, #3B82F6)}.group-section-wrapper.multi-save-active .group-instance.is-card .card-view .card-actions mat-icon.icon-expand{color:var(--cc-sf-input-disabled-border, #E5E7EB)}.btn-remove{transition:var(--cc-sf-btn-transition, all .2s ease)}.btn-remove mat-icon{font-size:1rem;width:1rem;height:1rem;line-height:1rem}.btn-remove:hover{background:var(--cc-sf-btn-remove-hover-bg, #FED7D7)}.btn-add-group{font-weight:var(--cc-sf-btn-font-weight, 600);transition:var(--cc-sf-btn-transition, all .2s ease)}.btn-add-group mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.btn-add-group:hover{background:var(--cc-sf-btn-add-hover-bg, #EFF6FF);border-color:var(--cc-sf-btn-add-hover-border, #BFDBFE)}.upload-drop-zone{background-color:var(--cc-sf-dropzone-bg, #F8FAFC);border:var(--cc-sf-dropzone-border, 1.5px dashed #CBD5E1);border-radius:var(--cc-sf-dropzone-radius, 12px);transition:background-color .2s ease,border-color .2s ease}.upload-drop-zone:hover{background-color:var(--cc-sf-dropzone-hover-bg, #EFF6FF);border-color:var(--cc-sf-dropzone-hover-border, #93C5FD)}.upload-drop-zone.drag-over{background-color:var(--cc-sf-dropzone-hover-bg, #EFF6FF);border-color:var(--cc-sf-dropzone-over-border, #3B82F6);box-shadow:var(--cc-sf-dropzone-over-shadow, 0 0 0 4px rgba(59, 130, 246, .12))}.upload-drop-zone.is-invalid{border-color:var(--cc-sf-error-border, #DC2626);background-color:var(--cc-sf-error-bg, #FEF2F2)}.upload-icon-wrap .dropzone-icon-pill{width:52px;height:52px;border-radius:50%;background:var(--cc-sf-dropzone-icon-bg, rgba(59, 130, 246, .1))}.upload-icon-wrap mat-icon.upload-cloud-icon{font-size:28px;width:28px;height:28px;line-height:28px;color:var(--cc-sf-accent-color, #3B82F6)}.upload-main-text{color:var(--cc-sf-label-color, #1E293B)}.upload-sub-text{color:var(--cc-sf-hint-color, #64748B)}.upload-link{color:var(--cc-sf-dropzone-link-color, #3B82F6);font-weight:500}.upload-formats{color:var(--cc-sf-dropzone-link-color, #3B82F6)}.upload-size-badge{display:inline-block;padding:2px 8px;border-radius:20px;background:var(--cc-sf-input-disabled-bg, #F3F4F6);color:var(--cc-sf-hint-color, #6B7280);font-weight:500}.uploaded-item{background:var(--cc-sf-uploaded-item-bg, #ffffff);border:var(--cc-sf-uploaded-item-border, 1px solid #E2E8F0);border-radius:var(--cc-sf-uploaded-item-radius, 8px);transition:box-shadow .15s ease}.uploaded-item:hover{box-shadow:0 2px 6px #0000000f}.uploaded-item mat-icon.file-type-icon{font-size:20px;width:20px;height:20px;line-height:20px;flex-shrink:0;color:var(--cc-sf-hint-color, #64748B)}.uploaded-item .file-thumb{width:36px;height:36px;object-fit:cover;flex-shrink:0}.uploaded-item .file-info{min-width:0;gap:2px}.uploaded-item .file-info .file-name{white-space:nowrap;text-overflow:ellipsis}.uploaded-item .file-remove-btn{flex-shrink:0;width:32px;height:32px;background:none;border:none;cursor:pointer;color:var(--cc-sf-uploaded-remove-color, #94A3B8);padding:0;display:flex;align-items:center;justify-content:center;transition:color .15s ease,background .15s ease}.uploaded-item .file-remove-btn mat-icon{font-size:1.1rem;width:1.1rem;height:1.1rem;line-height:1.1rem}.uploaded-item .file-remove-btn:hover:not(:disabled){color:var(--cc-sf-uploaded-remove-hover-color, #DC2626);background:var(--cc-sf-uploaded-remove-hover-bg, #FEF2F2)}.uploaded-item .file-remove-btn:disabled{opacity:.4;cursor:not-allowed}.uploaded-item.uploading{background:var(--cc-sf-uploaded-uploading-bg, #F8FAFC);border-color:var(--cc-sf-uploaded-uploading-border, #CBD5E1);opacity:.85}.upload-spinner{width:20px;height:20px;flex-shrink:0;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}@keyframes cc-spin{to{transform:rotate(360deg)}}.uploading-label{font-style:italic}.input-group{align-items:stretch}.input-group .field-input{flex:1;width:auto}.input-prefix+.input-group .field-input{border-top-left-radius:0;border-bottom-left-radius:0}.input-group .field-input:has(+.input-suffix){border-top-right-radius:0;border-bottom-right-radius:0}.input-group .field-input.has-icon-right{padding-right:3rem}.input-group.readonly .field-input{cursor:default}.input-prefix,.input-suffix{display:flex!important;align-items:center;white-space:nowrap;padding:0 14px;background-color:var(--cc-sf-input-disabled-bg);border:1px solid var(--cc-sf-input-border);font-size:.875rem;color:var(--cc-sf-hint-color);-webkit-user-select:none;user-select:none}.input-prefix{border-right:none;border-top-left-radius:var(--cc-sf-input-radius, 8px);border-bottom-left-radius:var(--cc-sf-input-radius, 8px)}.input-suffix{border-left:none;border-top-right-radius:var(--cc-sf-input-radius, 8px);border-bottom-right-radius:var(--cc-sf-input-radius, 8px)}.readonly-icons{right:.875rem;top:50%;transform:translateY(-50%)}.readonly-icons mat-icon.lock-icon{font-size:1rem;width:1rem;height:1rem;line-height:1rem;opacity:.5;color:var(--cc-sf-hint-color, #6B7280)}.date-icon-wrapper{right:.5rem;top:50%;transform:translateY(-50%);pointer-events:auto}.date-icon-wrapper .mat-icon-button{width:32px;height:32px;line-height:32px}.subfields-group-wrapper .subfields-row{transition:all .2s ease}.subfields-group-wrapper .subfields-row.is-invalid .subfield-item ::ng-deep .field-input{border-color:var(--cc-sf-error-border, #DC2626);background-color:var(--cc-sf-error-bg, #FEF2F2)}.subfields-group-wrapper .subfields-row .subfield-item{min-width:0}.subfields-group-wrapper .subfields-row .subfield-item ::ng-deep .field-label{font-size:.75rem!important;margin-bottom:4px!important;font-weight:500!important;color:var(--cc-sf-hint-color, #6B7280)!important}.subfields-group-wrapper .subfields-row .subfield-separator{font-weight:700}.autocomplete-wrapper .ac-input{padding-left:40px!important}.autocomplete-wrapper .ac-search-icon{left:.75rem;width:1.1rem;height:1.1rem;line-height:1.1rem;z-index:1;transition:color var(--cc-sf-input-transition, .2s ease)}.autocomplete-wrapper .ac-clear-btn{right:.6rem;transition:color .15s ease,background .15s ease}.autocomplete-wrapper .ac-clear-btn mat-icon{font-size:1rem;width:1rem;height:1rem;line-height:1rem}.autocomplete-wrapper .ac-clear-btn:hover{color:var(--cc-sf-label-color, #374151);background:var(--cc-sf-input-disabled-bg, #F3F4F6)}.autocomplete-wrapper .ac-clear-btn:focus{outline:none}.autocomplete-wrapper:focus-within .ac-search-icon{color:var(--cc-sf-accent-color, #3B82F6)}.autocomplete-wrapper.is-invalid .ac-input{border-color:var(--cc-sf-error-border)!important;background-color:var(--cc-sf-error-bg)}.autocomplete-wrapper.readonly .ac-input{background-color:var(--cc-sf-input-disabled-bg);color:var(--cc-sf-input-disabled-color, #6B7280);cursor:not-allowed;border-color:var(--cc-sf-input-disabled-border)!important}.ac-no-results{font-style:italic}::ng-deep .mat-mdc-autocomplete-panel,::ng-deep .mat-autocomplete-panel{background:var(--cc-sf-input-bg, #ffffff)!important;border-radius:var(--cc-sf-input-radius, 9px)!important;border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)!important;box-shadow:0 8px 24px #0000001a,0 2px 6px #0000000f!important;padding:4px 0!important;min-width:200px}::ng-deep .mat-mdc-autocomplete-panel mat-option,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option,::ng-deep .mat-mdc-autocomplete-panel .mat-option,::ng-deep .mat-autocomplete-panel mat-option,::ng-deep .mat-autocomplete-panel .mat-mdc-option,::ng-deep .mat-autocomplete-panel .mat-option{background:var(--cc-sf-input-bg, #ffffff)!important;color:var(--cc-sf-label-color, #111827)!important;font-size:.875rem!important;padding:10px 16px!important;min-height:40px!important;line-height:1.4!important;display:flex!important;flex-direction:column!important;align-items:flex-start!important;transition:background var(--cc-sf-input-transition, .2s ease)!important}::ng-deep .mat-mdc-autocomplete-panel mat-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-mdc-autocomplete-panel .mat-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-autocomplete-panel mat-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-autocomplete-panel .mat-mdc-option:hover:not(.mat-option-disabled):not([disabled]),::ng-deep .mat-autocomplete-panel .mat-option:hover:not(.mat-option-disabled):not([disabled]){background:var(--cc-sf-input-disabled-bg, #F3F4F6)!important}::ng-deep .mat-mdc-autocomplete-panel mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-mdc-autocomplete-panel mat-option.mdc-list-item--selected,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option.mdc-list-item--selected,::ng-deep .mat-mdc-autocomplete-panel .mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-mdc-autocomplete-panel .mat-option.mdc-list-item--selected,::ng-deep .mat-autocomplete-panel mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-autocomplete-panel mat-option.mdc-list-item--selected,::ng-deep .mat-autocomplete-panel .mat-mdc-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-autocomplete-panel .mat-mdc-option.mdc-list-item--selected,::ng-deep .mat-autocomplete-panel .mat-option.mat-selected:not(.mat-option-multiple),::ng-deep .mat-autocomplete-panel .mat-option.mdc-list-item--selected{background:var(--cc-sf-dropzone-hover-bg, #EFF6FF)!important;color:var(--cc-sf-selected-color, #6366F1)!important;font-weight:600!important}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-option-label,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-option-label,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-option-label,::ng-deep .mat-autocomplete-panel mat-option .ac-option-label,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-option-label,::ng-deep .mat-autocomplete-panel .mat-option .ac-option-label{font-weight:500;color:var(--cc-sf-label-color, #111827);font-size:.875rem;display:block}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-display-fields,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-display-fields,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-display-fields,::ng-deep .mat-autocomplete-panel mat-option .ac-display-fields,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-display-fields,::ng-deep .mat-autocomplete-panel .mat-option .ac-display-fields{align-items:center;line-height:1}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-item,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-item,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-item,::ng-deep .mat-autocomplete-panel mat-option .ac-df-item,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-item,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-item{display:inline-flex;align-items:center;font-size:.72rem;color:var(--cc-sf-hint-color, #6B7280);gap:3px}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-chip,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-chip,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-chip,::ng-deep .mat-autocomplete-panel mat-option .ac-df-chip,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-chip,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-chip{background:var(--cc-sf-input-disabled-bg, #F3F4F6);border-radius:4px;padding:2px 6px}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-text,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-text,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-text,::ng-deep .mat-autocomplete-panel mat-option .ac-df-text,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-text,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-text{color:var(--cc-sf-hint-color, #6B7280)}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-avatar,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-avatar,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-avatar,::ng-deep .mat-autocomplete-panel mat-option .ac-df-avatar,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-avatar,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-avatar{width:24px;height:24px;border-radius:50%;object-fit:cover;border:1px solid var(--cc-sf-input-border, #D1D5DB);vertical-align:middle}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-label,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-label,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-label,::ng-deep .mat-autocomplete-panel mat-option .ac-df-label,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-label,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-label{font-weight:600;color:var(--cc-sf-hint-color, #9CA3AF);margin-right:2px}::ng-deep .mat-mdc-autocomplete-panel mat-option .ac-df-icon,::ng-deep .mat-mdc-autocomplete-panel .mat-mdc-option .ac-df-icon,::ng-deep .mat-mdc-autocomplete-panel .mat-option .ac-df-icon,::ng-deep .mat-autocomplete-panel mat-option .ac-df-icon,::ng-deep .mat-autocomplete-panel .mat-mdc-option .ac-df-icon,::ng-deep .mat-autocomplete-panel .mat-option .ac-df-icon{font-size:11px;width:11px;height:11px;line-height:11px;color:var(--cc-sf-hint-color, #9CA3AF);flex-shrink:0}.mu-layout{grid-template-columns:1fr 1fr;gap:32px}@media(max-width:768px){.mu-layout{grid-template-columns:1fr}}.mu-title{font-weight:700;line-height:1.3}.mu-badge{white-space:nowrap;flex-shrink:0}.mu-description{line-height:1.6}.mu-feature-item .mu-check{width:16px;height:16px;line-height:16px;flex-shrink:0}.mu-right{min-height:260px}.mu-right-empty{min-height:250px;max-width:400px;box-shadow:0 2px 10px #0000000d;transition:box-shadow .2s ease}.mu-right-empty:hover{cursor:pointer;box-shadow:0 4px 16px #0000001a}.mu-right-empty .mu-right-empty-icon{width:52px;height:52px;line-height:52px;opacity:.3}.mu-right-empty p{margin:0;font-size:.85rem}.media-add-container{display:inline-block}.media-add-container ::ng-deep button{display:flex;align-items:center;gap:6px}.media-add-container ::ng-deep button .menu-chevron{width:18px;height:18px;line-height:18px;transition:transform .2s ease}.media-dropdown{top:calc(100% + 6px);left:0;z-index:200;min-width:240px;box-shadow:var(--mu-dropdown-shadow, 0 8px 32px rgba(0, 0, 0, .12));animation:mu-fade-in .15s ease}@keyframes mu-fade-in{0%{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:translateY(0)}}.media-dropdown-item{transition:background .15s ease}.media-dropdown-item:last-child{border-bottom:none}.media-dropdown-item:hover{background:var(--cc-sf-dropzone-hover-bg, #F0F9FF)}.media-drop-icon{width:36px;height:36px;flex-shrink:0}.media-drop-icon mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.media-drop-icon--video{background:var(--mu-icon-video-bg, #FFF0F0);color:var(--mu-icon-video-color, #EF4444)}.media-drop-icon--device{background:var(--mu-icon-device-bg, #EFF6FF);color:var(--mu-icon-device-color, #3B82F6)}.media-drop-icon--library{background:var(--mu-icon-library-bg, #F0FDF4);color:var(--mu-icon-library-color, #22C55E)}.media-drop-text{gap:2px}.youtube-input-panel{animation:mu-fade-in .18s ease}.youtube-panel-label mat-icon{font-size:18px;width:18px;height:18px;line-height:18px;color:var(--mu-icon-video-color, #EF4444)}.youtube-input-row{align-items:stretch}.media-menu-backdrop{position:fixed;inset:0;z-index:100}.media-upload-status{animation:mu-fade-in .2s ease}.media-upload-status .status-icon{width:18px;height:18px;line-height:18px}.media-carousel-main{max-width:400px;height:var(--mu-carousel-height, 250px)}.carousel-viewer{inset:0}.carousel-viewer .carousel-image{object-fit:cover}.carousel-viewer .carousel-spinner{width:36px;height:36px;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}.carousel-nav{top:50%;transform:translateY(-50%);z-index:10;width:40px;height:40px;box-shadow:0 2px 8px #0003;transition:background .2s ease,opacity .2s ease;-webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}.carousel-nav mat-icon{font-size:22px;width:22px;height:22px;line-height:22px;color:#1e293b}.carousel-nav:hover:not(:disabled){background:#fff}.carousel-nav:disabled{opacity:.3;cursor:not-allowed}.carousel-nav--prev{left:12px}.carousel-nav--next{right:12px}.carousel-remove-btn{top:10px;right:10px;z-index:10;width:32px;height:32px;transition:background .2s ease}.carousel-remove-btn mat-icon{font-size:18px;width:18px;height:18px;line-height:18px;color:#fff}.carousel-remove-btn:hover:not(:disabled){background:#dc2626d9}.carousel-remove-btn:disabled{opacity:.4;cursor:not-allowed}.carousel-dots{bottom:10px;left:50%;transform:translate(-50%);z-index:10}.carousel-dot{width:8px;height:8px;transition:background .2s ease,transform .2s ease}.carousel-dot.active{background:#fff;transform:scale(1.3)}.media-thumbnail-strip{max-width:400px;overflow-x:auto}.media-thumbnail-strip::-webkit-scrollbar{height:4px}.media-thumbnail-strip::-webkit-scrollbar-thumb{background:var(--cc-sf-input-disabled-border, #D1D5DB);border-radius:2px}.media-thumb{flex-shrink:0;width:72px;height:52px;transition:border-color .2s ease,transform .15s ease}.media-thumb.active{border-color:var(--mu-thumb-active-border, var(--cc-sf-accent-color, #3B82F6));transform:scale(1.04)}.media-thumb:hover:not(.active){border-color:var(--cc-sf-input-hover-border, #9CA3AF)}.media-thumb .thumb-img{object-fit:cover}.media-thumb .thumb-yt-placeholder mat-icon{font-size:28px;width:28px;height:28px;line-height:28px}.media-thumb .thumb-uploading .thumb-spinner{width:20px;height:20px;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}.media-library-portal-host{position:fixed;inset:0;z-index:9999;display:flex;align-items:center;justify-content:center;visibility:hidden;opacity:0;pointer-events:none;transition:opacity .2s ease,visibility .2s ease}.media-library-portal-host.is-open{visibility:visible;opacity:1;pointer-events:auto}.media-library-portal-host.is-open .media-library-modal{transform:scale(1) translateY(0)}.media-library-overlay{position:absolute;inset:0;background:#00000080;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}.media-library-modal{position:relative;z-index:10000;width:90vw;max-width:900px;max-height:90vh;background:#fff;border-radius:16px;box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a;transform:scale(.95) translateY(10px);transition:transform .3s cubic-bezier(.175,.885,.32,1.275)}.library-modal-header{flex-shrink:0}.library-modal-title{font-weight:700}.library-modal-subtitle{line-height:1.5;max-width:600px}.library-close-btn{width:32px;height:32px;transition:background .15s ease,color .15s ease}.library-close-btn mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.library-close-btn:hover{background:var(--cc-sf-input-disabled-bg, #F3F4F6);color:var(--cc-sf-label-color, #374151)}.library-loading mat-icon,.library-empty mat-icon{font-size:40px;width:40px;height:40px;line-height:40px;opacity:.5}.lib-spinner{width:36px;height:36px;border-top-color:var(--cc-sf-accent-color, #3B82F6);animation:cc-spin .7s linear infinite}.library-error{flex-shrink:0}.library-error mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.library-grid{grid-template-columns:repeat(5,1fr);max-height:50vh;overflow-y:auto}.library-grid::-webkit-scrollbar{width:8px}.library-grid::-webkit-scrollbar-track{background:#f1f1f1}.library-grid::-webkit-scrollbar-thumb{background:#c1c1c1;border-radius:10px;border:2px solid #F1F1F1}.library-grid-item{aspect-ratio:1/1;transition:all .3s cubic-bezier(.4,0,.2,1);box-shadow:0 4px 6px -1px #0000001a,0 2px 4px -1px #0000000f}.library-grid-item:hover{transform:translateY(-4px) scale(1.02);box-shadow:0 20px 25px -5px #0000001a,0 10px 10px -5px #0000000a}.library-grid-item.selected{border-color:var(--cc-sf-accent-color, #3B82F6)}.library-grid-item.selected .library-grid-img{opacity:.7}.library-grid-item:hover .library-overlay-hover{opacity:1}.library-grid-img{object-fit:cover}.library-overlay-hover{inset:0;opacity:0;transition:opacity .15s ease}.library-check{top:6px;right:6px;width:22px;height:22px;box-shadow:0 1px 4px #00000026}.library-check mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}.library-modal-footer{flex-shrink:0}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-primary{background-color:var(--cc-sf-accent-color, #3B82F6)!important;border-color:var(--cc-sf-accent-color, #3B82F6)!important;color:#fff!important;font-weight:600;padding-left:32px;padding-right:32px}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-primary:hover{background-color:var(--cc-sf-btn-primary-hover-bg, #2563EB)!important}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-primary:disabled{background-color:#93c5fd!important;cursor:not-allowed}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-outline{font-weight:600;padding-left:24px;padding-right:24px;border-color:#d1d5db;color:#374151}.library-modal-footer .library-footer-actions ::ng-deep .cc-btn-outline:hover{background-color:#f9fafb}.location-subtitle{line-height:1.5}.loc-tab-btn ::ng-deep button{width:100%}.loc-tab-btn ::ng-deep button:not(.cc-btn-warning){background-color:var(--cc-sf-input-bg, #ffffff)!important;color:var(--cc-sf-label-color, #000000)!important;border:1px solid var(--cc-sf-input-disabled-border, #E5E7EB)}.loc-tab-btn ::ng-deep button:not(.cc-btn-warning):hover{background-color:var(--cc-sf-input-disabled-bg, #F3F4F6)!important}.loc-venue-item{transition:box-shadow .15s ease,border-color .15s ease}.loc-venue-item:hover{box-shadow:0 2px 8px #0000000f;border-color:var(--cc-sf-input-hover-border, #9CA3AF)}.loc-venue-search-icon{width:18px;height:18px;line-height:18px;flex-shrink:0}.loc-venue-text{white-space:nowrap;text-overflow:ellipsis}.loc-action-btn{transition:background .15s ease,color .15s ease;flex-shrink:0}.loc-action-btn mat-icon{font-size:18px;width:18px;height:18px;line-height:18px}.loc-action-btn.loc-delete-btn{color:var(--loc-delete-color, #E53E3E)}.loc-action-btn.loc-delete-btn:hover{background:var(--cc-sf-error-bg, #FEF2F2)}.loc-action-btn.loc-edit-btn{color:var(--cc-sf-hint-color, #9CA3AF)}.loc-action-btn.loc-edit-btn:hover{color:var(--cc-sf-input-focus-border, #3B82F6);background:var(--cc-sf-dropzone-hover-bg, #EFF6FF)}.loc-search-icon{left:.75rem;width:1.1rem;height:1.1rem;line-height:1.1rem;z-index:1}.loc-suggestions-panel{top:calc(100% + 4px);left:0;right:0;z-index:300;box-shadow:0 8px 24px #0000001a;animation:mu-fade-in .15s ease;max-height:260px;overflow-y:auto}.loc-suggestion-item{transition:background .12s ease}.loc-suggestion-item:hover,.loc-suggestion-item:focus{background:var(--loc-suggestion-hover-bg, #F0F9FF)}.loc-suggestion-item:not(:last-child){border-bottom:1px solid var(--cc-sf-input-disabled-border, #F3F4F6)}.loc-suggestion-icon{width:18px;height:18px;line-height:18px;flex-shrink:0}.loc-suggestion-text{white-space:nowrap;text-overflow:ellipsis}.loc-add-btn{transition:opacity .15s ease}.loc-add-btn mat-icon{font-size:20px;width:20px;height:20px;line-height:20px}.loc-add-btn:hover{opacity:.8}.loc-map-container{box-shadow:0 2px 10px #0000000f}.loc-tba-panel{min-height:120px}.loc-tba-icon{width:40px;height:40px;line-height:40px;opacity:.6}.loc-tba-text{line-height:1.6;max-width:360px}.radio-label{display:flex!important}.radio-label .option-content{padding-left:10px}\n"] }]
|
|
2392
2537
|
}], ctorParameters: () => [{ type: i1$3.FormBuilder }, { type: ExpressionService }, { type: i3.HttpClient }], propDecorators: { config: [{
|
|
2393
2538
|
type: Input
|
|
2394
2539
|
}], controller: [{
|
|
@@ -2592,18 +2737,7 @@ class SmartFormComponent {
|
|
|
2592
2737
|
}
|
|
2593
2738
|
ngOnInit() {
|
|
2594
2739
|
this._skeletonStart = Date.now();
|
|
2595
|
-
this.
|
|
2596
|
-
// Load data in EDIT mode, or use fallback initialValues
|
|
2597
|
-
if (this.mode === 'EDIT' && this.formSchema?.editConfig?.loadApiUrl) {
|
|
2598
|
-
this.loadEditData();
|
|
2599
|
-
}
|
|
2600
|
-
else if (this.initialValues) {
|
|
2601
|
-
this.controller.initialize(this.initialValues);
|
|
2602
|
-
this._markReady();
|
|
2603
|
-
}
|
|
2604
|
-
else {
|
|
2605
|
-
this._markReady();
|
|
2606
|
-
}
|
|
2740
|
+
this._startForm();
|
|
2607
2741
|
this.controller.fileAdded$.pipe(takeUntil$1(this.destroy$)).subscribe(file => this.fileAdded.emit(file));
|
|
2608
2742
|
this.controller.fileUploadFinished$.pipe(takeUntil$1(this.destroy$)).subscribe(file => this.fileUploadFinished.emit(file));
|
|
2609
2743
|
this.controller.fileRemoved$.pipe(takeUntil$1(this.destroy$)).subscribe(file => this.fileRemoved.emit(file));
|
|
@@ -2641,11 +2775,10 @@ class SmartFormComponent {
|
|
|
2641
2775
|
}
|
|
2642
2776
|
ngOnChanges(changes) {
|
|
2643
2777
|
if (changes['formJson'] && !changes['formJson'].isFirstChange()) {
|
|
2644
|
-
this.
|
|
2778
|
+
this._startForm();
|
|
2645
2779
|
}
|
|
2646
|
-
// Re-translate if labels arrive after the JSON was already parsed
|
|
2647
2780
|
if (changes['labels'] && !changes['labels'].isFirstChange() && this.formSchema) {
|
|
2648
|
-
this.
|
|
2781
|
+
this._startForm();
|
|
2649
2782
|
}
|
|
2650
2783
|
}
|
|
2651
2784
|
ngOnDestroy() {
|
|
@@ -2653,46 +2786,79 @@ class SmartFormComponent {
|
|
|
2653
2786
|
this.destroy$.next();
|
|
2654
2787
|
this.destroy$.complete();
|
|
2655
2788
|
}
|
|
2789
|
+
/** Public backward-compatible entry point — delegates to _startForm. */
|
|
2656
2790
|
parseFormJson() {
|
|
2657
|
-
|
|
2791
|
+
this._startForm();
|
|
2792
|
+
}
|
|
2793
|
+
_startForm() {
|
|
2794
|
+
if (!this.formJson)
|
|
2658
2795
|
return;
|
|
2659
|
-
|
|
2796
|
+
let schema;
|
|
2660
2797
|
try {
|
|
2661
|
-
|
|
2662
|
-
this.formSchema = jsonData;
|
|
2663
|
-
// Push token from configJSON into the shared controller so all child
|
|
2664
|
-
// components (form-field, etc.) can read it without @Input prop-drilling.
|
|
2665
|
-
this.controller.token = this.formSchema.token;
|
|
2666
|
-
this.controller.tokenHeader = this.formSchema.tokenHeader;
|
|
2667
|
-
// Translate i18n keys before rendering
|
|
2668
|
-
if (this.labels && Object.keys(this.labels).length) {
|
|
2669
|
-
SmartFormTranslationUtils.translateSchema(this.formSchema, this.labels);
|
|
2670
|
-
}
|
|
2671
|
-
// Share translated labels with child components via controller
|
|
2672
|
-
this.controller.labels = this.labels;
|
|
2673
|
-
this.controller.actionLabels = this.formSchema.labels;
|
|
2674
|
-
this.isStepper = this.formSchema.formType === 'STEPPER';
|
|
2675
|
-
this.fieldList = this.isStepper
|
|
2676
|
-
? (this.formSchema.stepperConfig?.children || []).filter(step => step.isEnabled !== false && step.sectionConfig?.isEnabled !== false)
|
|
2677
|
-
: (this.formSchema.sectionConfig?.children || []).filter(field => field.isEnabled !== false);
|
|
2678
|
-
// Section stepper: active when explicitly set OR when all enabled top-level
|
|
2679
|
-
// children are GROUP type (auto-detection for structured section forms).
|
|
2680
|
-
const topLevelGroups = this.fieldList.filter(f => f.type === 'GROUP' && f.sectionConfig);
|
|
2681
|
-
this.isSectionStepper = !this.isStepper &&
|
|
2682
|
-
(this.formSchema.sectionStepper === true ||
|
|
2683
|
-
(this.formSchema.sectionStepper !== false && topLevelGroups.length === this.fieldList.length && topLevelGroups.length > 1));
|
|
2684
|
-
if (this.isSectionStepper) {
|
|
2685
|
-
this.sectionSteps = topLevelGroups;
|
|
2686
|
-
this.currentSectionStep = 0;
|
|
2687
|
-
this.stepValidationStates = this.sectionSteps.map(() => 'untouched');
|
|
2688
|
-
this.stepFieldNames = this.sectionSteps.map(s => this._collectFieldNames(s.sectionConfig?.children || []));
|
|
2689
|
-
// Emit initial state after a tick so host bindings are ready
|
|
2690
|
-
setTimeout(() => this._emitStepChange());
|
|
2691
|
-
}
|
|
2692
|
-
this.initializeForm();
|
|
2798
|
+
schema = JSON.parse(this.formJson);
|
|
2693
2799
|
}
|
|
2694
2800
|
catch (e) {
|
|
2695
|
-
console.error('
|
|
2801
|
+
console.error('SmartForm: invalid formJson', e);
|
|
2802
|
+
return;
|
|
2803
|
+
}
|
|
2804
|
+
if (this._hasHierarchyDynamic(schema)) {
|
|
2805
|
+
this._expandHierarchyNodes(schema)
|
|
2806
|
+
.pipe(takeUntil$1(this.destroy$))
|
|
2807
|
+
.subscribe({
|
|
2808
|
+
next: expanded => this._applySchema(expanded),
|
|
2809
|
+
error: () => this._applySchema(schema),
|
|
2810
|
+
});
|
|
2811
|
+
}
|
|
2812
|
+
else {
|
|
2813
|
+
this._applySchema(schema);
|
|
2814
|
+
}
|
|
2815
|
+
}
|
|
2816
|
+
_applySchema(schema) {
|
|
2817
|
+
this.formSchema = schema;
|
|
2818
|
+
this.controller.token = this.formSchema.token;
|
|
2819
|
+
this.controller.tokenHeader = this.formSchema.tokenHeader;
|
|
2820
|
+
if (this.labels && Object.keys(this.labels).length) {
|
|
2821
|
+
SmartFormTranslationUtils.translateSchema(this.formSchema, this.labels);
|
|
2822
|
+
}
|
|
2823
|
+
this.controller.labels = this.labels;
|
|
2824
|
+
this.controller.actionLabels = this.formSchema.labels;
|
|
2825
|
+
this.isStepper = this.formSchema.formType === 'STEPPER';
|
|
2826
|
+
this.fieldList = this.isStepper
|
|
2827
|
+
? (this.formSchema.stepperConfig?.children || []).filter(step => step.isEnabled !== false && step.sectionConfig?.isEnabled !== false)
|
|
2828
|
+
: (this.formSchema.sectionConfig?.children || []).filter(field => field.isEnabled !== false);
|
|
2829
|
+
const topLevelGroups = this.fieldList.filter(f => f.type === 'GROUP' && f.sectionConfig);
|
|
2830
|
+
this.isSectionStepper = !this.isStepper &&
|
|
2831
|
+
(this.formSchema.sectionStepper === true ||
|
|
2832
|
+
(this.formSchema.sectionStepper !== false && topLevelGroups.length === this.fieldList.length && topLevelGroups.length > 1));
|
|
2833
|
+
if (this.isSectionStepper) {
|
|
2834
|
+
this.sectionSteps = topLevelGroups;
|
|
2835
|
+
this.currentSectionStep = 0;
|
|
2836
|
+
this.stepValidationStates = this.sectionSteps.map(() => 'untouched');
|
|
2837
|
+
this.stepFieldNames = this.sectionSteps.map(s => this._collectFieldNames(s.sectionConfig?.children || []));
|
|
2838
|
+
setTimeout(() => this._emitStepChange());
|
|
2839
|
+
}
|
|
2840
|
+
// Pre-seed array values (allowMulti GROUP initial data) into the controller
|
|
2841
|
+
// BEFORE initializeForm() runs. form-field.initGroupField() calls
|
|
2842
|
+
// controller.getFieldValue(groupKey) during ngOnInit to determine how many
|
|
2843
|
+
// rows to create — it must find the array already there or it falls back to
|
|
2844
|
+
// a single empty row and loses all initial values.
|
|
2845
|
+
if (this.initialValues) {
|
|
2846
|
+
Object.entries(this.initialValues).forEach(([key, value]) => {
|
|
2847
|
+
if (Array.isArray(value)) {
|
|
2848
|
+
this.controller.updateField(key, value);
|
|
2849
|
+
}
|
|
2850
|
+
});
|
|
2851
|
+
}
|
|
2852
|
+
this.initializeForm();
|
|
2853
|
+
if (this.mode === 'EDIT' && this.formSchema?.editConfig?.loadApiUrl) {
|
|
2854
|
+
this.loadEditData();
|
|
2855
|
+
}
|
|
2856
|
+
else if (this.initialValues) {
|
|
2857
|
+
this.controller.initialize(this.initialValues);
|
|
2858
|
+
this._markReady();
|
|
2859
|
+
}
|
|
2860
|
+
else {
|
|
2861
|
+
this._markReady();
|
|
2696
2862
|
}
|
|
2697
2863
|
}
|
|
2698
2864
|
initializeForm() {
|
|
@@ -3246,6 +3412,259 @@ class SmartFormComponent {
|
|
|
3246
3412
|
this.router.navigateByUrl(url);
|
|
3247
3413
|
}
|
|
3248
3414
|
}
|
|
3415
|
+
// ── GEOGRAPHY_DYNAMIC / Hierarchy expansion ───────────────────────────────
|
|
3416
|
+
// Supports any API-driven hierarchy template. All paths, URLs, field names
|
|
3417
|
+
// and dependency params are driven entirely by the geographyConfig JSON node —
|
|
3418
|
+
// nothing is hardcoded here. New deployments only need to update the JSON.
|
|
3419
|
+
_hasHierarchyDynamic(obj) {
|
|
3420
|
+
if (!obj || typeof obj !== 'object')
|
|
3421
|
+
return false;
|
|
3422
|
+
if (obj['type'] === 'GEOGRAPHY_DYNAMIC')
|
|
3423
|
+
return true;
|
|
3424
|
+
return Object.values(obj).some((v) => this._hasHierarchyDynamic(v));
|
|
3425
|
+
}
|
|
3426
|
+
_expandHierarchyNodes(schema) {
|
|
3427
|
+
return this._expandNodeRecursive(schema);
|
|
3428
|
+
}
|
|
3429
|
+
_expandNodeRecursive(obj) {
|
|
3430
|
+
if (!this._hasHierarchyDynamic(obj))
|
|
3431
|
+
return of(obj);
|
|
3432
|
+
if (Array.isArray(obj)) {
|
|
3433
|
+
const tasks$ = obj.map((item) => {
|
|
3434
|
+
if (item && item['type'] === 'GEOGRAPHY_DYNAMIC') {
|
|
3435
|
+
return this._expandSingleNode(item);
|
|
3436
|
+
}
|
|
3437
|
+
return this._expandNodeRecursive(item).pipe(map((v) => [v]));
|
|
3438
|
+
});
|
|
3439
|
+
return (tasks$.length > 0 ? forkJoin(tasks$) : of([])).pipe(map((arrays) => [].concat(...arrays)));
|
|
3440
|
+
}
|
|
3441
|
+
if (typeof obj === 'object' && obj !== null) {
|
|
3442
|
+
const keysWithDynamic = Object.keys(obj).filter(k => this._hasHierarchyDynamic(obj[k]));
|
|
3443
|
+
if (keysWithDynamic.length === 0)
|
|
3444
|
+
return of(obj);
|
|
3445
|
+
const updates$ = keysWithDynamic.map(key => this._expandNodeRecursive(obj[key]).pipe(map((v) => ({ key, value: v }))));
|
|
3446
|
+
return forkJoin(updates$).pipe(map((updates) => {
|
|
3447
|
+
const result = { ...obj };
|
|
3448
|
+
updates.forEach(u => { result[u.key] = u.value; });
|
|
3449
|
+
return result;
|
|
3450
|
+
}));
|
|
3451
|
+
}
|
|
3452
|
+
return of(obj);
|
|
3453
|
+
}
|
|
3454
|
+
_expandSingleNode(node) {
|
|
3455
|
+
const rawCfg = node['geographyConfig'] || node['hierarchyConfig'];
|
|
3456
|
+
if (!rawCfg)
|
|
3457
|
+
return of([]);
|
|
3458
|
+
const cfg = this._normalizeHierarchyCfg(rawCfg);
|
|
3459
|
+
if (!cfg.templateApiUrl)
|
|
3460
|
+
return of([]);
|
|
3461
|
+
return this._fetchHierarchyTemplate(cfg).pipe(map((template) => template ? this._buildHierarchyFields(cfg, template) : []), catchError(() => of([])));
|
|
3462
|
+
}
|
|
3463
|
+
_normalizeHierarchyCfg(raw) {
|
|
3464
|
+
// New nested format: templateApi / dataApi / structure / field sub-objects
|
|
3465
|
+
if (raw['templateApi'] && typeof raw['templateApi'] === 'object') {
|
|
3466
|
+
const ta = raw['templateApi'];
|
|
3467
|
+
const da = raw['dataApi'] || {};
|
|
3468
|
+
const st = raw['structure'] || {};
|
|
3469
|
+
const fi = raw['field'] || {};
|
|
3470
|
+
return {
|
|
3471
|
+
templateApiUrl: ta['apiUrl'] || ta['url'] || '',
|
|
3472
|
+
templateListPath: ta['listPath'] ?? null,
|
|
3473
|
+
templateSelect: ta['select'] || 'default',
|
|
3474
|
+
templateSelectField: ta['selectField'] || 'isDefault',
|
|
3475
|
+
templateSelectCode: ta['selectCode'],
|
|
3476
|
+
rootCodePath: st['rootCodePath'] || 'rootClassCode',
|
|
3477
|
+
templateCodePath: st['templateCodePath'] || 'templateCode',
|
|
3478
|
+
hierarchyListPath: st['hierarchyListPath'] || 'mdmclasshierarchytemplatestructList',
|
|
3479
|
+
parentField: st['parentField'] || 'parentCode',
|
|
3480
|
+
childField: st['childField'] || 'childCode',
|
|
3481
|
+
dataApiUrl: da['apiUrl'] || da['url'] || '',
|
|
3482
|
+
dataPath: da['dataPath'] || 'elements',
|
|
3483
|
+
labelPath: da['labelPath'] || 'name[0].text',
|
|
3484
|
+
valuePath: da['valuePath'] || 'code',
|
|
3485
|
+
colSpan: fi['colSpan'] ?? 12,
|
|
3486
|
+
payloadPrefix: fi['payloadPathPrefix'] || '',
|
|
3487
|
+
dependencyParam: fi['dependency']?.['paramName'] || 'parentDataCode',
|
|
3488
|
+
nodes: fi['nodes'] || {},
|
|
3489
|
+
branchSubType: fi['branchSubType'] || 'SINGLE',
|
|
3490
|
+
};
|
|
3491
|
+
}
|
|
3492
|
+
// Legacy flat format (backward compatibility with existing geographyConfig JSON)
|
|
3493
|
+
const reqCodes = raw['requiredClassCodes'] || [];
|
|
3494
|
+
const payloadMap = raw['classCodePayloadFieldMap'] || {};
|
|
3495
|
+
const nodes = {};
|
|
3496
|
+
[...new Set([...reqCodes, ...Object.keys(payloadMap)])].forEach(code => {
|
|
3497
|
+
nodes[code] = { payloadField: payloadMap[code], required: reqCodes.includes(code) };
|
|
3498
|
+
});
|
|
3499
|
+
return {
|
|
3500
|
+
templateApiUrl: raw['templateApiUrl'] || '',
|
|
3501
|
+
templateListPath: null,
|
|
3502
|
+
templateSelect: raw['useDefault'] !== false ? 'default' : 'first',
|
|
3503
|
+
templateSelectField: 'isDefault',
|
|
3504
|
+
rootCodePath: 'rootClassCode',
|
|
3505
|
+
templateCodePath: 'templateCode',
|
|
3506
|
+
hierarchyListPath: 'mdmclasshierarchytemplatestructList',
|
|
3507
|
+
parentField: 'parentCode',
|
|
3508
|
+
childField: 'childCode',
|
|
3509
|
+
dataApiUrl: raw['dataApiUrl'] || '',
|
|
3510
|
+
dataPath: raw['dataPath'] || 'elements',
|
|
3511
|
+
labelPath: raw['labelPath'] || 'name[0].text',
|
|
3512
|
+
valuePath: raw['valuePath'] || 'code',
|
|
3513
|
+
colSpan: raw['colSpan'] ?? 12,
|
|
3514
|
+
payloadPrefix: raw['payloadPathPrefix'] || '',
|
|
3515
|
+
dependencyParam: 'parentDataCode',
|
|
3516
|
+
nodes,
|
|
3517
|
+
branchSubType: 'SINGLE',
|
|
3518
|
+
};
|
|
3519
|
+
}
|
|
3520
|
+
_fetchHierarchyTemplate(cfg) {
|
|
3521
|
+
const headers = this.getHeaders();
|
|
3522
|
+
return this.http.get(cfg.templateApiUrl, { headers }).pipe(map((response) => {
|
|
3523
|
+
let list;
|
|
3524
|
+
if (cfg.templateListPath) {
|
|
3525
|
+
list = this._getPathValue(response, cfg.templateListPath) || [];
|
|
3526
|
+
}
|
|
3527
|
+
else if (Array.isArray(response)) {
|
|
3528
|
+
list = response;
|
|
3529
|
+
}
|
|
3530
|
+
else {
|
|
3531
|
+
list = [response];
|
|
3532
|
+
}
|
|
3533
|
+
if (!Array.isArray(list) || list.length === 0)
|
|
3534
|
+
return null;
|
|
3535
|
+
if (cfg.templateSelect === 'first')
|
|
3536
|
+
return list[0];
|
|
3537
|
+
if (cfg.templateSelect === 'byCode') {
|
|
3538
|
+
return list.find((t) => this._getPathValue(t, cfg.templateCodePath) === cfg.templateSelectCode) || list[0];
|
|
3539
|
+
}
|
|
3540
|
+
return list.find((t) => !!t[cfg.templateSelectField]) || list[0];
|
|
3541
|
+
}), catchError(() => of(null)));
|
|
3542
|
+
}
|
|
3543
|
+
_buildHierarchyFields(cfg, template) {
|
|
3544
|
+
const rootCode = this._getPathValue(template, cfg.rootCodePath) || '';
|
|
3545
|
+
const templateCode = this._getPathValue(template, cfg.templateCodePath) || '';
|
|
3546
|
+
const hierList = this._getPathValue(template, cfg.hierarchyListPath) || [];
|
|
3547
|
+
const childrenMap = new Map();
|
|
3548
|
+
hierList.forEach((s) => {
|
|
3549
|
+
const parent = s[cfg.parentField];
|
|
3550
|
+
const child = s[cfg.childField];
|
|
3551
|
+
if (parent != null && child != null) {
|
|
3552
|
+
if (!childrenMap.has(parent))
|
|
3553
|
+
childrenMap.set(parent, []);
|
|
3554
|
+
childrenMap.get(parent).push(child);
|
|
3555
|
+
}
|
|
3556
|
+
});
|
|
3557
|
+
const fields = [];
|
|
3558
|
+
const buildField = (code, parentFieldName, visibilityExpression) => {
|
|
3559
|
+
const nodeOvr = cfg.nodes[code] || {};
|
|
3560
|
+
if (nodeOvr['hidden']) {
|
|
3561
|
+
(childrenMap.get(code) || []).forEach(c => buildField(c, parentFieldName, visibilityExpression));
|
|
3562
|
+
return;
|
|
3563
|
+
}
|
|
3564
|
+
const fieldName = nodeOvr['name'] || this._codeToFieldName(code);
|
|
3565
|
+
const label = nodeOvr['label'] || this._codeToLabel(code);
|
|
3566
|
+
const payloadField = nodeOvr['payloadField'] || fieldName;
|
|
3567
|
+
const colSpan = nodeOvr['colSpan'] ?? cfg.colSpan;
|
|
3568
|
+
const payloadPath = cfg.payloadPrefix ? `${cfg.payloadPrefix}.${payloadField}` : payloadField;
|
|
3569
|
+
const apiUrl = (cfg.dataApiUrl || '')
|
|
3570
|
+
.replace(/\{classCode\}/g, encodeURIComponent(code))
|
|
3571
|
+
.replace(/\{templateCode\}/g, encodeURIComponent(templateCode));
|
|
3572
|
+
const optionConfig = {
|
|
3573
|
+
apiUrl,
|
|
3574
|
+
dataPath: cfg.dataPath,
|
|
3575
|
+
labelPath: cfg.labelPath,
|
|
3576
|
+
valuePath: cfg.valuePath,
|
|
3577
|
+
};
|
|
3578
|
+
if (parentFieldName && !nodeOvr['noDependency']) {
|
|
3579
|
+
optionConfig['dependencies'] = { [cfg.dependencyParam]: parentFieldName };
|
|
3580
|
+
}
|
|
3581
|
+
const fieldDef = {
|
|
3582
|
+
name: fieldName,
|
|
3583
|
+
label,
|
|
3584
|
+
payloadPath,
|
|
3585
|
+
type: 'DROPDOWN',
|
|
3586
|
+
subType: 'SINGLE',
|
|
3587
|
+
colSpan,
|
|
3588
|
+
isEnabled: true,
|
|
3589
|
+
optionConfig,
|
|
3590
|
+
};
|
|
3591
|
+
if (visibilityExpression)
|
|
3592
|
+
fieldDef['visibilityExpression'] = visibilityExpression;
|
|
3593
|
+
if (nodeOvr['required']) {
|
|
3594
|
+
fieldDef['required'] = true;
|
|
3595
|
+
fieldDef['errorMessage'] = nodeOvr['errorMessage'] || `Please select ${label.toLowerCase()}`;
|
|
3596
|
+
}
|
|
3597
|
+
if (nodeOvr['placeholder'])
|
|
3598
|
+
fieldDef['placeholder'] = nodeOvr['placeholder'];
|
|
3599
|
+
fields.push(fieldDef);
|
|
3600
|
+
const children = childrenMap.get(code) || [];
|
|
3601
|
+
if (children.length > 1) {
|
|
3602
|
+
// Branch point: dynamically build Area Type selector from children
|
|
3603
|
+
const branchOptions = children.map(c => {
|
|
3604
|
+
const childOvr = cfg.nodes[c] || {};
|
|
3605
|
+
const childFieldName = childOvr['name'] || this._codeToFieldName(c);
|
|
3606
|
+
const childLabel = childOvr['label'] || this._codeToLabel(c);
|
|
3607
|
+
return { label: childLabel, code: childFieldName };
|
|
3608
|
+
});
|
|
3609
|
+
const areaTypeField = {
|
|
3610
|
+
name: 'areaType',
|
|
3611
|
+
label: 'Area Type',
|
|
3612
|
+
type: 'DROPDOWN',
|
|
3613
|
+
subType: cfg.branchSubType,
|
|
3614
|
+
colSpan,
|
|
3615
|
+
isEnabled: true,
|
|
3616
|
+
optionConfig: { optionList: branchOptions },
|
|
3617
|
+
};
|
|
3618
|
+
if (visibilityExpression)
|
|
3619
|
+
areaTypeField['visibilityExpression'] = visibilityExpression;
|
|
3620
|
+
fields.push(areaTypeField);
|
|
3621
|
+
// Skip structural branch-type nodes (e.g. RURAL, URABN) — they carry no MDM data.
|
|
3622
|
+
// Walk their descendants with `fieldName` (the linear parent before the fork, e.g. State)
|
|
3623
|
+
// as the dependency, so the first real data level loads correctly.
|
|
3624
|
+
children.forEach(c => {
|
|
3625
|
+
const childOvr = cfg.nodes[c] || {};
|
|
3626
|
+
const childFieldName = childOvr['name'] || this._codeToFieldName(c);
|
|
3627
|
+
const branchVisExpr = `areaType === '${childFieldName}'`;
|
|
3628
|
+
const branchDescendants = childrenMap.get(c) || [];
|
|
3629
|
+
if (branchDescendants.length === 0) {
|
|
3630
|
+
// Unusual: branch-type node has no descendants — fall back to treating it as a data field
|
|
3631
|
+
buildField(c, fieldName, branchVisExpr);
|
|
3632
|
+
}
|
|
3633
|
+
else {
|
|
3634
|
+
// Skip the branch-type node; walk its children with the pre-fork parent as dependency
|
|
3635
|
+
branchDescendants.forEach(desc => buildField(desc, fieldName, branchVisExpr));
|
|
3636
|
+
}
|
|
3637
|
+
});
|
|
3638
|
+
}
|
|
3639
|
+
else {
|
|
3640
|
+
children.forEach(c => buildField(c, fieldName, visibilityExpression));
|
|
3641
|
+
}
|
|
3642
|
+
};
|
|
3643
|
+
buildField(rootCode, null);
|
|
3644
|
+
return fields;
|
|
3645
|
+
}
|
|
3646
|
+
_getPathValue(obj, path) {
|
|
3647
|
+
if (!path || obj == null)
|
|
3648
|
+
return obj;
|
|
3649
|
+
return path.split('.').reduce((curr, key) => {
|
|
3650
|
+
if (curr == null)
|
|
3651
|
+
return undefined;
|
|
3652
|
+
const m = key.match(/^(\w+)\[(\d+)\]$/);
|
|
3653
|
+
return m ? curr[m[1]]?.[Number(m[2])] : curr[key];
|
|
3654
|
+
}, obj);
|
|
3655
|
+
}
|
|
3656
|
+
_codeToFieldName(classCode) {
|
|
3657
|
+
const suffix = (classCode.split('.').pop() || classCode).replace(/^REGION_/, '');
|
|
3658
|
+
const words = suffix.split('_');
|
|
3659
|
+
if (words.length === 0)
|
|
3660
|
+
return classCode.toLowerCase();
|
|
3661
|
+
return words[0].toLowerCase() +
|
|
3662
|
+
words.slice(1).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join('');
|
|
3663
|
+
}
|
|
3664
|
+
_codeToLabel(classCode) {
|
|
3665
|
+
const suffix = (classCode.split('.').pop() || classCode).replace(/^REGION_/, '');
|
|
3666
|
+
return suffix.split('_').map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join(' ');
|
|
3667
|
+
}
|
|
3249
3668
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SmartFormComponent, deps: [{ token: i1$3.FormBuilder }, { token: SmartFormController }, { token: ExpressionService }, { token: i3.HttpClient }, { token: SnackbarService }, { token: i1$4.Router }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
3250
3669
|
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: SmartFormComponent, isStandalone: false, selector: "lib-smart-form", inputs: { formJson: "formJson", initialValues: "initialValues", enableDraftAutoSave: "enableDraftAutoSave", labels: "labels", mode: "mode", readOnly: "readOnly" }, outputs: { submit: "submit", draftSave: "draftSave", actionClick: "actionClick", valueChange: "valueChange", fileAdded: "fileAdded", fileUploadFinished: "fileUploadFinished", fileRemoved: "fileRemoved", stepChange: "stepChange" }, providers: [SmartFormController], usesOnChanges: true, ngImport: i0, template: "<div class=\"smart-form-container\">\n\n <!-- \u2550\u2550 Skeleton loader \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div class=\"smart-form-skeleton\" *ngIf=\"!isFormReady\" aria-hidden=\"true\">\n\n <!-- Title + description -->\n <div class=\"skel-header\">\n <div class=\"skel-block skel-title\" style=\"--sd: 0s\"></div>\n <div class=\"skel-block skel-desc\" style=\"--sd: 0.07s\"></div>\n </div>\n\n <!-- Section 1 \u2014 two full-width fields -->\n <div class=\"skel-section\">\n <div class=\"skel-block skel-section-label\" style=\"--sd: 0.1s; width: 32%\"></div>\n <div class=\"skel-fields skel-fields--two\">\n <div class=\"skel-field\">\n <div class=\"skel-block skel-label\" style=\"--sd: 0.13s; width: 55%\"></div>\n <div class=\"skel-block skel-input\" style=\"--sd: 0.15s\"></div>\n </div>\n <div class=\"skel-field\">\n <div class=\"skel-block skel-label\" style=\"--sd: 0.16s; width: 45%\"></div>\n <div class=\"skel-block skel-input\" style=\"--sd: 0.18s\"></div>\n </div>\n <div class=\"skel-field skel-field--full\">\n <div class=\"skel-block skel-label\" style=\"--sd: 0.2s; width: 38%\"></div>\n <div class=\"skel-block skel-input skel-input--textarea\" style=\"--sd: 0.22s\"></div>\n </div>\n </div>\n </div>\n\n <!-- Section 2 \u2014 three columns -->\n <div class=\"skel-section\">\n <div class=\"skel-block skel-section-label\" style=\"--sd: 0.25s; width: 24%\"></div>\n <div class=\"skel-fields skel-fields--three\">\n <div class=\"skel-field\">\n <div class=\"skel-block skel-label\" style=\"--sd: 0.28s; width: 60%\"></div>\n <div class=\"skel-block skel-input\" style=\"--sd: 0.30s\"></div>\n </div>\n <div class=\"skel-field\">\n <div class=\"skel-block skel-label\" style=\"--sd: 0.31s; width: 50%\"></div>\n <div class=\"skel-block skel-input\" style=\"--sd: 0.33s\"></div>\n </div>\n <div class=\"skel-field\">\n <div class=\"skel-block skel-label\" style=\"--sd: 0.34s; width: 65%\"></div>\n <div class=\"skel-block skel-input\" style=\"--sd: 0.36s\"></div>\n </div>\n </div>\n </div>\n\n <!-- Section 3 \u2014 two columns + chip row -->\n <div class=\"skel-section\">\n <div class=\"skel-block skel-section-label\" style=\"--sd: 0.38s; width: 28%\"></div>\n <div class=\"skel-fields skel-fields--two\">\n <div class=\"skel-field\">\n <div class=\"skel-block skel-label\" style=\"--sd: 0.40s; width: 48%\"></div>\n <div class=\"skel-block skel-input\" style=\"--sd: 0.42s\"></div>\n </div>\n <div class=\"skel-field\">\n <div class=\"skel-block skel-label\" style=\"--sd: 0.43s; width: 55%\"></div>\n <div class=\"skel-block skel-input\" style=\"--sd: 0.45s\"></div>\n </div>\n </div>\n <div class=\"skel-chips\">\n <div class=\"skel-block skel-chip\" style=\"--sd: 0.47s; width: 72px\"></div>\n <div class=\"skel-block skel-chip\" style=\"--sd: 0.49s; width: 96px\"></div>\n <div class=\"skel-block skel-chip\" style=\"--sd: 0.51s; width: 80px\"></div>\n <div class=\"skel-block skel-chip\" style=\"--sd: 0.53s; width: 64px\"></div>\n </div>\n </div>\n\n <!-- Action bar -->\n <div class=\"skel-actions\">\n <div class=\"skel-block skel-btn\" style=\"--sd: 0.55s; width: 88px\"></div>\n <div class=\"skel-block skel-btn skel-btn--primary\" style=\"--sd: 0.58s; width: 120px\"></div>\n </div>\n </div>\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n SECTION STEPPER layout \u2014 stepper nav card + per-step form panels.\n Rendered when formSchema.sectionStepper === true.\n The nav and each step section are separate cards (no outer wrapper card).\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <ng-container *ngIf=\"formSchema && isFormReady && isSectionStepper\">\n\n <!-- Horizontal stepper nav card -->\n <div class=\"section-stepper-nav\" *ngIf=\"sectionSteps.length > 0\">\n <div class=\"section-stepper-track\">\n <div\n *ngFor=\"let step of sectionSteps; let i = index\"\n class=\"section-stepper-step\"\n [class.ss-active]=\"i === currentSectionStep\"\n [class.ss-completed]=\"i < currentSectionStep && stepValidationStates[i] !== 'warning'\"\n [class.ss-warning]=\"i < currentSectionStep && stepValidationStates[i] === 'warning'\"\n [attr.data-tooltip]=\"step.sectionConfig?.label || 'Step ' + (i + 1)\"\n (click)=\"goToSectionStep(i)\">\n\n <!-- Connector line \u2014 left side (hidden for first step) -->\n <div class=\"ss-connector ss-connector--left\" *ngIf=\"i > 0\"></div>\n\n <!-- Step badge -->\n <div class=\"ss-badge\">\n <!-- Green checkmark: visited and valid -->\n <svg *ngIf=\"i < currentSectionStep && stepValidationStates[i] !== 'warning'\"\n width=\"13\" height=\"13\" viewBox=\"0 0 24 24\"\n fill=\"none\" stroke=\"currentColor\" stroke-width=\"3\"\n stroke-linecap=\"round\" stroke-linejoin=\"round\">\n <polyline points=\"20 6 9 17 4 12\"></polyline>\n </svg>\n <!-- Warning: bold ! is the most visible option inside a 38px circle badge -->\n <span *ngIf=\"i < currentSectionStep && stepValidationStates[i] === 'warning'\"\n class=\"ss-warning-mark\">!</span>\n <!-- Number: current or future step -->\n <span *ngIf=\"i >= currentSectionStep\">{{ i + 1 }}</span>\n </div>\n\n <!-- Step label (truncated; full text revealed by CSS tooltip) -->\n <div class=\"ss-label\" [title]=\"step.sectionConfig?.label || 'Step ' + (i + 1)\">\n {{ step.sectionConfig?.label || 'Step ' + (i + 1) }}\n </div>\n\n <!-- Connector line \u2014 right side (hidden for last step) -->\n <div class=\"ss-connector ss-connector--right\" *ngIf=\"i < sectionSteps.length - 1\"></div>\n </div>\n </div>\n </div>\n\n <!-- Step panels \u2014 all mounted, only active one is visible (preserves form data) -->\n <form [formGroup]=\"formGroup\" class=\"smart-form ss-form\">\n <div\n *ngFor=\"let step of sectionSteps; let i = index\"\n class=\"ss-step-panel\"\n [style.display]=\"i === currentSectionStep ? 'block' : 'none'\">\n <lib-form-section\n [config]=\"getSectionStepConfig(step)\"\n [controller]=\"controller\"\n [formGroup]=\"formGroup\">\n </lib-form-section>\n </div>\n </form>\n\n <!-- \u2500\u2500 Action bar: Prev / custom action buttons / Next \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"form-actions\"\n *ngIf=\"!readOnly && formSchema.showActions !== false && actionBarConfig?.buttons?.length\">\n\n <!-- LEFT GROUP: Previous button + left-aligned custom buttons -->\n <div class=\"action-group action-group--left\">\n <lib-button\n *ngIf=\"!isSectionStepFirst\"\n variant=\"outline\"\n (click)=\"navigateToPrevious()\">\n {{ previousLabel }}\n </lib-button>\n <ng-container *ngFor=\"let btn of getButtonsForAlignment('left')\">\n <lib-button\n *ngIf=\"!btn.showOnLastStepOnly || isSectionStepLast\"\n [variant]=\"$any(btn.variant) || 'outline'\"\n [disabled]=\"isButtonDisabled(btn)\"\n (click)=\"handleButtonClick(btn)\">\n {{ getButtonLabel(btn) }}\n </lib-button>\n </ng-container>\n </div>\n\n <!-- RIGHT GROUP: right-aligned custom buttons + Next button -->\n <div class=\"action-group action-group--right\">\n <ng-container *ngFor=\"let btn of getButtonsForAlignment('right')\">\n <lib-button\n *ngIf=\"!btn.showOnLastStepOnly || isSectionStepLast\"\n [variant]=\"$any(btn.variant) || 'primary'\"\n [disabled]=\"isButtonDisabled(btn)\"\n (click)=\"handleButtonClick(btn)\">\n {{ getButtonLabel(btn) }}\n </lib-button>\n </ng-container>\n <lib-button\n *ngIf=\"!isSectionStepLast\"\n variant=\"primary\"\n (click)=\"navigateToNext()\">\n {{ nextLabel }}\n </lib-button>\n </div>\n\n </div>\n\n </ng-container>\n\n <!-- \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n Regular card wrapper \u2014 used for SECTION and STEPPER form types.\n \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n <div class=\"smart-form-wrapper\" *ngIf=\"formSchema && isFormReady && !isSectionStepper\"\n [class.smart-form-wrapper--readonly]=\"readOnly\">\n\n <!-- Form Header -->\n <div class=\"form-header\" *ngIf=\"formSchema.showTitle !== false\">\n <h2 class=\"form-title\">{{ formSchema.label }}</h2>\n <p class=\"form-description\" *ngIf=\"formSchema.description\">{{ formSchema.description }}</p>\n </div>\n\n <!-- STEPPER type nav -->\n <div class=\"stepper-nav\" *ngIf=\"isStepper && formSchema.stepperConfig?.showStep !== false\">\n <div class=\"stepper-steps\" [class.horizontal]=\"formSchema.stepperConfig?.isHorizontal !== false\">\n <div *ngFor=\"let step of fieldList; let i = index\" class=\"stepper-step\" [class.active]=\"i === currentStep\"\n [class.completed]=\"i < currentStep\">\n <div class=\"step-number\">{{ i + 1 }}</div>\n <div class=\"step-label\">{{ step.sectionConfig?.label || 'Step ' + (i + 1) }}</div>\n </div>\n </div>\n </div>\n\n <!-- Form Content -->\n <form [formGroup]=\"formGroup\" class=\"smart-form\">\n <!-- Regular SECTION form -->\n <div *ngIf=\"!isStepper && formSchema.sectionConfig && formSchema.sectionConfig.isEnabled !== false\" class=\"form-section\">\n <lib-form-section [config]=\"formSchema.sectionConfig\" [controller]=\"controller\" [formGroup]=\"formGroup\">\n </lib-form-section>\n </div>\n\n <!-- STEPPER type form -->\n <div *ngIf=\"isStepper && currentStepConfig && currentStepConfig.sectionConfig?.isEnabled !== false\" class=\"form-stepper\">\n <lib-form-section [config]=\"currentStepConfig.sectionConfig!\" [controller]=\"controller\" [formGroup]=\"formGroup\">\n </lib-form-section>\n </div>\n </form>\n\n <!-- \u2500\u2500 Form Actions \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"form-actions\" *ngIf=\"formSchema.showActions !== false && !readOnly && actionBarConfig?.buttons?.length\">\n\n <!-- LEFT GROUP -->\n <div class=\"action-group action-group--left\">\n <lib-button\n *ngFor=\"let btn of getButtonsForAlignment('left')\"\n [variant]=\"$any(btn.variant) || 'outline'\"\n [disabled]=\"isButtonDisabled(btn)\"\n (click)=\"handleButtonClick(btn)\">\n {{ getButtonLabel(btn) }}\n </lib-button>\n </div>\n\n <!-- RIGHT GROUP -->\n <div class=\"action-group action-group--right\">\n <lib-button\n *ngFor=\"let btn of getButtonsForAlignment('right')\"\n [variant]=\"$any(btn.variant) || 'primary'\"\n [disabled]=\"isButtonDisabled(btn)\"\n (click)=\"handleButtonClick(btn)\">\n {{ getButtonLabel(btn) }}\n </lib-button>\n </div>\n\n </div>\n </div>\n\n</div>\n", styles: ["@keyframes skel-wave{0%{transform:translate(-100%)}to{transform:translate(200%)}}@keyframes skel-fade-in{0%{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}@keyframes form-reveal{0%{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}.smart-form-skeleton{background:var(--cc-sf-form-bg, #ffffff);border-radius:var(--cc-sf-form-border-radius, 12px);border:var(--cc-sf-form-border, none);box-shadow:var(--cc-sf-form-shadow, 0 1px 3px rgba(0, 0, 0, .06));padding:1.5rem;display:flex;flex-direction:column;gap:var(--cc-sf-form-section-gap, 24px);animation:skel-fade-in .3s ease both}.skel-block{position:relative;overflow:hidden;border-radius:6px;background:var(--cc-sf-input-disabled-bg, #F0F2F5)}.skel-block:after{content:\"\";position:absolute;inset:0;transform:translate(-100%);background:linear-gradient(90deg,transparent 0%,rgba(255,255,255,.55) 45%,rgba(255,255,255,.75) 50%,rgba(255,255,255,.55) 55%,transparent 100%);animation:skel-wave 1.8s ease-in-out infinite;animation-delay:var(--sd, 0s)}.skel-header{display:flex;flex-direction:column;gap:10px}.skel-header .skel-title{height:26px;border-radius:8px}.skel-header .skel-desc{height:14px;border-radius:5px;opacity:.7}.skel-section{display:flex;flex-direction:column;gap:14px}.skel-section-label{height:16px;border-radius:0 5px 5px 0;border-left:var(--cc-sf-section-header-accent-width, 4px) solid var(--cc-sf-section-header-accent-color, #3B82F6);background:var(--cc-sf-input-disabled-bg, #F0F2F5);opacity:.85}.skel-fields{display:grid;gap:var(--cc-sf-grid-gap, 16px);align-items:start}.skel-fields--two{grid-template-columns:1fr 1fr}.skel-fields--three{grid-template-columns:1fr 1fr 1fr}@media(max-width:640px){.skel-fields--two,.skel-fields--three{grid-template-columns:1fr}}.skel-field{display:flex;flex-direction:column;gap:7px}.skel-field--full{grid-column:1/-1}.skel-label{height:12px;border-radius:4px;opacity:.75}.skel-input{height:42px;border-radius:8px}.skel-input--textarea{height:80px}.skel-chips{display:flex;flex-wrap:wrap;gap:8px}.skel-chip{height:32px;border-radius:20px;opacity:.8}.skel-actions{display:flex;justify-content:flex-end;align-items:center;gap:12px;padding-top:20px;border-top:var(--cc-sf-actions-border, 1px solid #E5E7EB)}.skel-btn{height:40px;border-radius:8px}.skel-btn--primary{background:color-mix(in srgb,var(--cc-sf-accent-color, #3B82F6) 18%,var(--cc-sf-input-disabled-bg, #F0F2F5))}.smart-form-container{width:100%;font-family:var(--cc-sf-font-family, \"Inter\", sans-serif)}.smart-form-wrapper{background:var(--cc-sf-form-bg, #ffffff);border-radius:var(--cc-sf-form-border-radius, 12px);border:var(--cc-sf-form-border, none);animation:form-reveal .3s ease both}.smart-form-wrapper .form-alert-feedback,.form-header{margin-bottom:1rem}.form-header .form-title{font-size:var(--cc-sf-form-title-size, 1.5rem);font-weight:var(--cc-sf-form-title-weight, 700);color:var(--cc-sf-form-title-color, #111827);margin:0 0 8px;line-height:1.25}.form-header .form-description{font-size:var(--cc-sf-form-desc-size, .875rem);color:var(--cc-sf-form-desc-color, #6B7280);margin:0}@keyframes ss-badge-pop{0%{transform:scale(.6);opacity:0}70%{transform:scale(1.12)}to{transform:scale(1);opacity:1}}@keyframes ss-pulse-ring{0%{box-shadow:0 0 #6366f173}70%{box-shadow:0 0 0 10px #6366f100}to{box-shadow:0 0 #6366f100}}@keyframes ss-panel-in{0%{opacity:0;transform:translateY(14px)}to{opacity:1;transform:translateY(0)}}@keyframes ss-connector-fill{0%{transform:scaleX(0)}to{transform:scaleX(1)}}.section-stepper-nav{position:relative;background:#fff;border-radius:16px;border:1px solid #E8EAF0;box-shadow:0 1px 3px #0000000d,0 4px 16px #6366f10f;padding:24px 28px 48px;margin-bottom:14px;overflow-x:clip;overflow-y:hidden}.section-stepper-nav:before{content:\"\";position:absolute;inset:0 0 auto;height:3px;border-radius:16px 16px 0 0;background:linear-gradient(90deg,#6366f1,#8b5cf6,#ec4899)}.section-stepper-track{display:flex;align-items:flex-start;justify-content:space-between;min-width:max-content;width:100%}.section-stepper-step{display:flex;flex-direction:column;align-items:center;position:relative;flex:1;cursor:pointer;padding:0 4px}.section-stepper-step:after{content:attr(data-tooltip);position:absolute;top:calc(100% + 8px);left:50%;transform:translate(-50%) translateY(4px);white-space:nowrap;background:#1e1b4b;color:#fff;font-size:.6875rem;font-weight:500;letter-spacing:.02em;padding:5px 11px;border-radius:6px;pointer-events:none;opacity:0;transition:opacity .2s ease,transform .2s ease;z-index:200;box-shadow:0 4px 14px #0003}.section-stepper-step:hover:after{opacity:1;transform:translate(-50%) translateY(0)}.section-stepper-step .ss-badge{width:38px;height:38px;min-width:38px;border-radius:50%;background:#f1f2f6;color:#9ca3af;border:2px solid #E5E7EB;display:flex;align-items:center;justify-content:center;font-size:.8125rem;font-weight:700;position:relative;z-index:1;transition:all .3s cubic-bezier(.34,1.56,.64,1)}.section-stepper-step .ss-badge span{line-height:1;letter-spacing:-.01em}.section-stepper-step .ss-badge svg{flex-shrink:0}.section-stepper-step .ss-badge .ss-warning-mark{font-size:1.25rem;font-weight:900;line-height:1;letter-spacing:0;color:#fff}.section-stepper-step .ss-label{margin-top:9px;font-size:.6875rem;font-weight:500;color:#b0b7c3;white-space:nowrap;text-align:center;transition:color .25s ease,font-weight .25s ease;max-width:88px;overflow:hidden;text-overflow:ellipsis;letter-spacing:.01em}.section-stepper-step .ss-connector{height:2px;background:#e8eaf0;position:absolute;top:19px;z-index:0;overflow:hidden;border-radius:2px}.section-stepper-step .ss-connector--left{left:0;right:calc(50% + 19px)}.section-stepper-step .ss-connector--right{left:calc(50% + 19px);right:0}.section-stepper-step .ss-connector:after{content:\"\";position:absolute;inset:0;background:linear-gradient(90deg,#6366f1,#8b5cf6);transform:scaleX(0);transform-origin:left;transition:transform .4s cubic-bezier(.4,0,.2,1)}.section-stepper-step.ss-active{cursor:default}.section-stepper-step.ss-active .ss-badge{background:linear-gradient(135deg,#6366f1,#8b5cf6);color:#fff;border-color:transparent;box-shadow:0 0 0 4px #6366f12e,0 4px 12px #6366f159;animation:ss-badge-pop .4s cubic-bezier(.34,1.56,.64,1) both,ss-pulse-ring 2s .4s ease-out infinite}.section-stepper-step.ss-active .ss-label{color:#4f46e5;font-weight:700}.section-stepper-step.ss-completed .ss-badge{background:linear-gradient(135deg,#10b981,#059669);color:#fff;border-color:transparent;box-shadow:0 2px 8px #10b9814d}.section-stepper-step.ss-completed .ss-label{color:#6b7280;font-weight:500}.section-stepper-step.ss-completed .ss-connector--left:after,.section-stepper-step.ss-completed .ss-connector--right:after{transform:scaleX(1)}.section-stepper-step.ss-warning .ss-badge{background:linear-gradient(135deg,#f59e0b,#d97706);color:#fff;border-color:transparent;box-shadow:0 2px 8px #f59e0b59;animation:ss-badge-pop .35s cubic-bezier(.34,1.56,.64,1) both}.section-stepper-step.ss-warning .ss-label{color:#b45309;font-weight:600}.section-stepper-step.ss-warning .ss-connector--left:after{transform:scaleX(0)}.section-stepper-step.ss-warning .ss-connector--right:after{transform:scaleX(0)}.section-stepper-step:not(.ss-active):not(.ss-completed):not(.ss-warning):hover .ss-badge{background:#e8eaf0;color:#6366f1;border-color:#c7d2fe;transform:translateY(-2px)}.section-stepper-step:not(.ss-active):not(.ss-completed):not(.ss-warning):hover .ss-label{color:#6b7280}.ss-step-panel{animation:ss-panel-in .35s cubic-bezier(.4,0,.2,1) both}.ss-form{padding-top:0}@media(max-width:768px){.section-stepper-nav{padding:20px 16px 16px}.section-stepper-step .ss-label{font-size:.625rem;max-width:64px}.section-stepper-step .ss-badge{width:32px;height:32px;min-width:32px;font-size:.75rem}.section-stepper-step .ss-connector{top:16px}.section-stepper-step .ss-connector--left{right:calc(50% + 16px)}.section-stepper-step .ss-connector--right{left:calc(50% + 16px)}}.stepper-nav{margin-bottom:32px}.stepper-nav .stepper-steps{display:flex;gap:16px}.stepper-nav .stepper-steps.horizontal{flex-direction:row;justify-content:space-between}.stepper-nav .stepper-steps:not(.horizontal){flex-direction:column}.stepper-nav .stepper-step{display:flex;align-items:center;gap:12px;flex:1;position:relative}.stepper-nav .stepper-step:not(:last-child):after{content:\"\";position:absolute;top:calc(var(--cc-sf-step-number-size, 40px) / 2);left:calc(100% + 8px);width:calc(100% - 40px);height:2px;background:var(--cc-sf-step-connector-color, #E5E7EB);transition:background var(--cc-sf-btn-transition, .2s ease)}.stepper-nav .stepper-step.completed:after{background:var(--cc-sf-step-connector-done, #22C55E)}.stepper-nav .stepper-step .step-number{width:var(--cc-sf-step-number-size, 40px);height:var(--cc-sf-step-number-size, 40px);min-width:var(--cc-sf-step-number-size, 40px);border-radius:50%;background:var(--cc-sf-step-number-bg, #E5E7EB);color:var(--cc-sf-step-number-color, #6B7280);display:flex;align-items:center;justify-content:center;font-size:var(--cc-sf-step-number-font-size, .875rem);font-weight:var(--cc-sf-step-number-weight, 600);transition:var(--cc-sf-btn-transition, all .2s ease)}.stepper-nav .stepper-step .step-label{font-size:var(--cc-sf-step-label-size, .875rem);color:var(--cc-sf-step-label-color, #6B7280);font-weight:var(--cc-sf-step-label-weight, 500);transition:var(--cc-sf-btn-transition, all .2s ease)}.stepper-nav .stepper-step.active .step-number{background:var(--cc-sf-step-active-bg, #3B82F6);color:var(--cc-sf-step-active-color, #ffffff)}.stepper-nav .stepper-step.active .step-label{color:var(--cc-sf-step-active-label, #1D4ED8);font-weight:var(--cc-sf-step-active-label-weight, 700)}.stepper-nav .stepper-step.completed .step-number{background:var(--cc-sf-step-done-bg, #22C55E);color:var(--cc-sf-step-done-color, #ffffff)}.smart-form{display:flex;flex-direction:column;gap:var(--cc-sf-form-section-gap, 24px)}.smart-form>*{margin-bottom:0!important}.form-actions{display:flex;justify-content:space-between;align-items:center;gap:var(--cc-sf-actions-gap, 12px);padding:var(--cc-sf-actions-padding, 20px 10px 0);margin-top:8px}.form-actions .action-group{display:flex;align-items:center;gap:var(--cc-sf-actions-gap, 12px)}.form-actions .action-group--left{justify-content:flex-start}.form-actions .action-group--right{justify-content:flex-end;margin-left:auto}\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$3.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$3.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$3.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "component", type: ButtonComponent, selector: "lib-button", inputs: ["variant", "type", "disabled", "width", "height", "borderRadius", "fontSize", "fontWeight", "backgroundColor", "color", "border", "icon", "labels"] }, { kind: "component", type: FormSectionComponent, selector: "lib-form-section", inputs: ["config", "controller", "formGroup"] }] });
|
|
3251
3670
|
}
|
|
@@ -3800,6 +4219,10 @@ class ClickOutsideDirective {
|
|
|
3800
4219
|
this.elementRef = elementRef;
|
|
3801
4220
|
}
|
|
3802
4221
|
onClick(target) {
|
|
4222
|
+
// Skip if the target was removed from the DOM before the click event fired
|
|
4223
|
+
// (happens with CDK virtual scroll which detaches nodes during rendering)
|
|
4224
|
+
if (!document.documentElement.contains(target))
|
|
4225
|
+
return;
|
|
3803
4226
|
const clickedInside = this.elementRef.nativeElement.contains(target);
|
|
3804
4227
|
if (!clickedInside) {
|
|
3805
4228
|
this.libClickOutside.emit();
|
|
@@ -3822,9 +4245,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
3822
4245
|
}] } });
|
|
3823
4246
|
|
|
3824
4247
|
class DropdownComponent {
|
|
4248
|
+
el;
|
|
3825
4249
|
config;
|
|
3826
4250
|
labels;
|
|
3827
4251
|
options = [];
|
|
4252
|
+
selectedValue;
|
|
3828
4253
|
placeholder = '';
|
|
3829
4254
|
label = '';
|
|
3830
4255
|
multiple = false;
|
|
@@ -3859,13 +4284,18 @@ class DropdownComponent {
|
|
|
3859
4284
|
selectionChange = new EventEmitter();
|
|
3860
4285
|
viewport;
|
|
3861
4286
|
searchInput;
|
|
4287
|
+
triggerEl;
|
|
3862
4288
|
filteredOptions = [];
|
|
3863
4289
|
searchTerm = '';
|
|
3864
4290
|
value = null;
|
|
3865
4291
|
isOpen = false;
|
|
3866
4292
|
focusedIndex = -1;
|
|
4293
|
+
menuPosition = { top: 0, left: 0, width: 0 };
|
|
3867
4294
|
onChange = () => { };
|
|
3868
4295
|
onTouched = () => { };
|
|
4296
|
+
constructor(el) {
|
|
4297
|
+
this.el = el;
|
|
4298
|
+
}
|
|
3869
4299
|
ngOnInit() {
|
|
3870
4300
|
this.updateFromConfig();
|
|
3871
4301
|
this.updateFromLabels();
|
|
@@ -3881,6 +4311,12 @@ class DropdownComponent {
|
|
|
3881
4311
|
if (changes['config'] || changes['options']) {
|
|
3882
4312
|
this.filteredOptions = this.validOptions;
|
|
3883
4313
|
}
|
|
4314
|
+
if (changes['selectedValue']) {
|
|
4315
|
+
const incoming = changes['selectedValue'].currentValue;
|
|
4316
|
+
if (incoming !== undefined && incoming !== this.value) {
|
|
4317
|
+
this.value = incoming ?? null;
|
|
4318
|
+
}
|
|
4319
|
+
}
|
|
3884
4320
|
}
|
|
3885
4321
|
get validOptions() {
|
|
3886
4322
|
return this.options.filter(o => o.label != null && o.label !== '');
|
|
@@ -3950,6 +4386,21 @@ class DropdownComponent {
|
|
|
3950
4386
|
toggle() {
|
|
3951
4387
|
if (this.disabled)
|
|
3952
4388
|
return;
|
|
4389
|
+
if (!this.isOpen && this.triggerEl) {
|
|
4390
|
+
const rect = this.triggerEl.nativeElement.getBoundingClientRect();
|
|
4391
|
+
const optCount = this.filteredOptions.length || 1;
|
|
4392
|
+
const menuH = Math.min(optCount * 40, 240) + (this.searchable ? 50 : 0) + 8;
|
|
4393
|
+
const spaceBelow = window.innerHeight - rect.bottom;
|
|
4394
|
+
const top = spaceBelow < menuH + 8 ? rect.top - menuH - 4 : rect.bottom + 2;
|
|
4395
|
+
this.menuPosition = { top, left: rect.left, width: rect.width };
|
|
4396
|
+
const closeOnScroll = (e) => {
|
|
4397
|
+
if (this.el.nativeElement.contains(e.target))
|
|
4398
|
+
return;
|
|
4399
|
+
this.close();
|
|
4400
|
+
document.removeEventListener('scroll', closeOnScroll, true);
|
|
4401
|
+
};
|
|
4402
|
+
document.addEventListener('scroll', closeOnScroll, true);
|
|
4403
|
+
}
|
|
3953
4404
|
this.isOpen = !this.isOpen;
|
|
3954
4405
|
if (this.isOpen) {
|
|
3955
4406
|
this.focusedIndex = -1;
|
|
@@ -4123,14 +4574,14 @@ class DropdownComponent {
|
|
|
4123
4574
|
'box-shadow': this.getStyleValue(this.boxShadow)
|
|
4124
4575
|
};
|
|
4125
4576
|
}
|
|
4126
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DropdownComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4127
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: DropdownComponent, isStandalone: false, selector: "lib-dropdown", inputs: { config: "config", labels: "labels", options: "options", placeholder: "placeholder", label: "label", multiple: "multiple", searchable: "searchable", clearable: "clearable", disabled: "disabled", required: "required", errorMessage: "errorMessage", width: "width", height: "height", borderRadius: "borderRadius", fontSize: "fontSize", gap: "gap", fontFamily: "fontFamily", labelColor: "labelColor", labelFontSize: "labelFontSize", labelFontWeight: "labelFontWeight", backgroundColor: "backgroundColor", borderColor: "borderColor", borderWidth: "borderWidth", padding: "padding", fontWeight: "fontWeight", color: "color", placeholderColor: "placeholderColor", focusBorderColor: "focusBorderColor", errorColor: "errorColor", disabledBackgroundColor: "disabledBackgroundColor", disabledColor: "disabledColor", boxShadow: "boxShadow" }, outputs: { selectionChange: "selectionChange" }, host: { listeners: { "keydown": "handleKeyboardEvent($event)" } }, providers: [
|
|
4577
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DropdownComponent, deps: [{ token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
|
|
4578
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: DropdownComponent, isStandalone: false, selector: "lib-dropdown", inputs: { config: "config", labels: "labels", options: "options", selectedValue: "selectedValue", placeholder: "placeholder", label: "label", multiple: "multiple", searchable: "searchable", clearable: "clearable", disabled: "disabled", required: "required", errorMessage: "errorMessage", width: "width", height: "height", borderRadius: "borderRadius", fontSize: "fontSize", gap: "gap", fontFamily: "fontFamily", labelColor: "labelColor", labelFontSize: "labelFontSize", labelFontWeight: "labelFontWeight", backgroundColor: "backgroundColor", borderColor: "borderColor", borderWidth: "borderWidth", padding: "padding", fontWeight: "fontWeight", color: "color", placeholderColor: "placeholderColor", focusBorderColor: "focusBorderColor", errorColor: "errorColor", disabledBackgroundColor: "disabledBackgroundColor", disabledColor: "disabledColor", boxShadow: "boxShadow" }, outputs: { selectionChange: "selectionChange" }, host: { listeners: { "keydown": "handleKeyboardEvent($event)" } }, providers: [
|
|
4128
4579
|
{
|
|
4129
4580
|
provide: NG_VALUE_ACCESSOR,
|
|
4130
4581
|
useExisting: forwardRef(() => DropdownComponent),
|
|
4131
4582
|
multi: true
|
|
4132
4583
|
}
|
|
4133
|
-
], viewQueries: [{ propertyName: "viewport", first: true, predicate: CdkVirtualScrollViewport, descendants: true }, { propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"cc-dropdown-wrapper\" [ngStyle]=\"wrapperStyles\" libClickOutside (libClickOutside)=\"close()\">\n <!-- Label -->\n <label *ngIf=\"label\" class=\"cc-dropdown-label\" [ngStyle]=\"labelStyles\">\n {{ label }}\n <span *ngIf=\"required\" class=\"cc-required\">{{ resolvedLabels.requiredMarker }}</span>\n </label>\n\n <!-- Trigger -->\n <div class=\"cc-dropdown-trigger\" [class.disabled]=\"disabled\" [class.open]=\"isOpen\" [class.error]=\"errorMessage\"\n (click)=\"toggle()\" tabindex=\"0\" (keydown)=\"handleKeyboardEvent($event)\" [ngStyle]=\"fieldStyles\">\n\n <div class=\"cc-selected-value\" *ngIf=\"hasValue()\">\n {{ multiple ? value.length + ' ' + resolvedLabels.selectedSuffix : getSelectedLabel() }}\n </div>\n <div class=\"cc-placeholder\" *ngIf=\"!hasValue()\">{{ placeholder }}</div>\n\n <div class=\"cc-actions\">\n <span *ngIf=\"clearable && hasValue() && !disabled\" class=\"cc-clear-btn\" (click)=\"clearSelection($event)\"\n [attr.aria-label]=\"resolvedLabels.clearAriaLabel\">\n <span class=\"material-icons\">close</span>\n </span>\n <span class=\"cc-arrow material-icons\">expand_more</span>\n </div>\n </div>\n\n <!-- Dropdown Menu -->\n <div class=\"cc-dropdown-menu\" *ngIf=\"isOpen\">\n <!-- Search -->\n <div class=\"cc-search-container\" *ngIf=\"searchable\">\n <input type=\"text\" #searchInput [placeholder]=\"resolvedLabels.searchPlaceholder\"\n (input)=\"onSearch($any($event.target).value)\" (click)=\"$event.stopPropagation()\"\n class=\"cc-dropdown-search-input\" />\n </div>\n\n <!-- Options Virtual Scroll -->\n <cdk-virtual-scroll-viewport itemSize=\"40\" class=\"cc-virtual-scroll\"\n [style.height.px]=\"filteredOptions.length * 40 > 240 ? 240 : (filteredOptions.length * 40 || 40)\">\n\n <div *cdkVirtualFor=\"let option of filteredOptions; let i = index\" class=\"cc-option\"\n [class.selected]=\"isSelected(option)\" [class.focused]=\"i === focusedIndex\"\n [class.disabled]=\"option.disabled\" (click)=\"selectOption(option)\">\n\n <!-- Multi-select checkbox visual -->\n <span class=\"cc-multi-check\" *ngIf=\"multiple\">\n <span class=\"material-icons\" *ngIf=\"isSelected(option)\">check_box</span>\n <span class=\"material-icons\" *ngIf=\"!isSelected(option)\">check_box_outline_blank</span>\n </span>\n\n <!-- Icon -->\n <span class=\"cc-option-icon-wrapper\" *ngIf=\"option.icon\">\n <span class=\"material-icons\" *ngIf=\"getIconType(option.icon) === 'material'\">{{\n getIconValue(option.icon) }}</span>\n <i *ngIf=\"getIconType(option.icon) === 'fontawesome'\" [class]=\"getIconValue(option.icon)\"></i>\n <img *ngIf=\"getIconType(option.icon) === 'img'\" [src]=\"getIconValue(option.icon)\"\n class=\"cc-option-img\" alt=\"icon\" />\n </span>\n\n <span class=\"cc-option-label\">{{ option.label }}</span>\n\n <!-- Single-select check visual -->\n <span class=\"cc-single-check\" *ngIf=\"!multiple && isSelected(option)\">\n <span class=\"material-icons\">check</span>\n </span>\n </div>\n\n <div *ngIf=\"filteredOptions.length === 0\" class=\"cc-no-results\">{{ resolvedLabels.noResultsFound }}</div>\n </cdk-virtual-scroll-viewport>\n </div>\n\n <!-- Error Message -->\n <div class=\"cc-error-message\" *ngIf=\"errorMessage\">{{ errorMessage }}</div>\n</div>", styles: [".cc-dropdown-wrapper{display:flex;flex-direction:column;width:100%;position:relative;font-family:var(--cc-dropdown-font-family, inherit);gap:var(--cc-dropdown-label-gap, .5rem)}.cc-dropdown-label{font-size:var(--cc-dropdown-font-size, .875rem);font-weight:var(--cc-dropdown-font-weight, 500);color:var(--cc-dropdown-label-color, #202124);margin:0;line-height:1.4}.cc-required{color:var(--cc-dropdown-required-color, #D93025);margin-left:.25rem}.cc-dropdown-trigger{display:flex;align-items:center;justify-content:space-between;width:100%;min-height:var(--cc-dropdown-height, 2.5rem);padding:var(--cc-dropdown-padding, .5rem .75rem);background-color:var(--cc-dropdown-bg, #FEFEFE);border:var(--cc-dropdown-border-width, 1px) solid var(--cc-dropdown-border-color, #BDC1C6);border-radius:var(--cc-dropdown-border-radius, .4375rem);cursor:pointer;-webkit-user-select:none;user-select:none;box-sizing:border-box;transition:all .2s}.cc-dropdown-trigger:focus{outline:none;border-color:var(--cc-dropdown-focus-border-color, #1A73E8)}.cc-dropdown-trigger.open{border-color:var(--cc-dropdown-focus-border-color, #1A73E8)}.cc-dropdown-trigger.error{border-color:var(--cc-dropdown-error-color, #D93025)}.cc-dropdown-trigger.disabled{background-color:var(--cc-dropdown-disabled-bg, #F1F3F4);cursor:not-allowed;color:var(--cc-dropdown-disabled-color, #80868B)}.cc-dropdown-trigger.disabled .cc-arrow,.cc-dropdown-trigger.disabled .cc-clear-btn{color:var(--cc-dropdown-disabled-color, #80868B)}.cc-selected-value{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--cc-dropdown-color, #202124);font-size:var(--cc-dropdown-font-size, .875rem);font-weight:var(--cc-dropdown-font-weight, 400)}.cc-placeholder{flex:1;color:var(--cc-dropdown-placeholder-color, #80868B);font-size:var(--cc-dropdown-font-size, .875rem)}.cc-actions{display:flex;align-items:center;gap:.5rem}.cc-arrow{color:var(--cc-dropdown-arrow-color, #5F6368);transition:transform .2s;font-size:1.25rem}.cc-dropdown-trigger.open .cc-arrow{transform:rotate(180deg)}.cc-clear-btn{display:flex;align-items:center;justify-content:center;color:var(--cc-dropdown-arrow-color, #5F6368);cursor:pointer}.cc-clear-btn:hover{color:#202124}.cc-clear-btn .material-icons{font-size:1.125rem}.cc-dropdown-menu{position:absolute;top:100%;left:0;width:100%;background-color:#fff;border:1px solid #BDC1C6;border-radius:0 0 4px 4px;box-shadow:0 2px 4px #0000001a;z-index:1000;margin-top:2px;overflow:hidden;animation:fadeIn .1s ease-out}@keyframes fadeIn{0%{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:translateY(0)}}.cc-search-container{padding:.5rem;border-bottom:1px solid var(--cc-dropdown-divider-color, #E0E0E0)}.cc-dropdown-search-input{width:100%;padding:.5rem;border:1px solid #BDC1C6;border-radius:4px;outline:none;box-sizing:border-box;font-size:.875rem}.cc-dropdown-search-input:focus{border-color:var(--cc-dropdown-focus-border-color, #1A73E8)}.cc-virtual-scroll{width:100%;max-height:240px}.cc-option{display:flex;align-items:center;padding:0 .75rem;height:40px;cursor:pointer;color:var(--cc-dropdown-color, #202124);font-size:var(--cc-dropdown-font-size, .875rem);transition:background-color .1s;box-sizing:border-box}.cc-option:hover,.cc-option.focused{background-color:#f1f3f4}.cc-option.selected{background-color:#e8f0fe;color:#1a73e8}.cc-option.disabled{opacity:.5;cursor:not-allowed;background-color:transparent}.cc-multi-check{margin-right:.5rem;color:var(--cc-dropdown-arrow-color, #5F6368);display:flex;align-items:center}.cc-multi-check .material-icons{font-size:1.25rem}.cc-option.selected .cc-multi-check{color:#1a73e8}.cc-option-icon-wrapper{margin-right:.5rem;display:flex;align-items:center}.cc-option-icon-wrapper .material-icons{font-size:1.25rem}.cc-option-img{width:1.25rem;height:1.25rem;object-fit:contain}.cc-option-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.cc-single-check{margin-left:.5rem;color:#1a73e8;display:flex;align-items:center}.cc-single-check .material-icons{font-size:1.125rem}.cc-no-results{padding:.75rem;color:#80868b;text-align:center;font-size:.875rem}.cc-error-message{font-size:.75rem;color:var(--cc-dropdown-error-color, #D93025);margin-top:.25rem}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2$1.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i2$1.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i2$1.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "directive", type: ClickOutsideDirective, selector: "[libClickOutside]", outputs: ["libClickOutside"] }] });
|
|
4584
|
+
], viewQueries: [{ propertyName: "viewport", first: true, predicate: CdkVirtualScrollViewport, descendants: true }, { propertyName: "searchInput", first: true, predicate: ["searchInput"], descendants: true }, { propertyName: "triggerEl", first: true, predicate: ["triggerEl"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"cc-dropdown-wrapper\" [ngStyle]=\"wrapperStyles\" libClickOutside (libClickOutside)=\"close()\">\n <!-- Label -->\n <label *ngIf=\"label\" class=\"cc-dropdown-label\" [ngStyle]=\"labelStyles\">\n {{ label }}\n <span *ngIf=\"required\" class=\"cc-required\">{{ resolvedLabels.requiredMarker }}</span>\n </label>\n\n <!-- Trigger -->\n <div #triggerEl class=\"cc-dropdown-trigger\" [class.disabled]=\"disabled\" [class.open]=\"isOpen\" [class.error]=\"errorMessage\"\n (click)=\"toggle()\" tabindex=\"0\" (keydown)=\"handleKeyboardEvent($event)\" [ngStyle]=\"fieldStyles\">\n\n <div class=\"cc-selected-value\" *ngIf=\"hasValue()\">\n {{ multiple ? value.length + ' ' + resolvedLabels.selectedSuffix : getSelectedLabel() }}\n </div>\n <div class=\"cc-placeholder\" *ngIf=\"!hasValue()\">{{ placeholder }}</div>\n\n <div class=\"cc-actions\">\n <span *ngIf=\"clearable && hasValue() && !disabled\" class=\"cc-clear-btn\" (click)=\"clearSelection($event)\"\n [attr.aria-label]=\"resolvedLabels.clearAriaLabel\">\n <span class=\"material-icons\">close</span>\n </span>\n <span class=\"cc-arrow material-icons\">expand_more</span>\n </div>\n </div>\n\n <!-- Dropdown Menu -->\n <div class=\"cc-dropdown-menu\" *ngIf=\"isOpen\"\n [ngStyle]=\"{ position: 'fixed', top: menuPosition.top + 'px', left: menuPosition.left + 'px', width: menuPosition.width + 'px', 'z-index': '9999' }\"\n (click)=\"$event.stopPropagation()\" (mousedown)=\"$event.stopPropagation()\">\n <!-- Search -->\n <div class=\"cc-search-container\" *ngIf=\"searchable\">\n <input type=\"text\" #searchInput [placeholder]=\"resolvedLabels.searchPlaceholder\"\n (input)=\"onSearch($any($event.target).value)\" (click)=\"$event.stopPropagation()\"\n class=\"cc-dropdown-search-input\" />\n </div>\n\n <!-- Options Virtual Scroll -->\n <cdk-virtual-scroll-viewport itemSize=\"40\" class=\"cc-virtual-scroll\"\n [style.height.px]=\"filteredOptions.length * 40 > 240 ? 240 : (filteredOptions.length * 40 || 40)\">\n\n <div *cdkVirtualFor=\"let option of filteredOptions; let i = index\" class=\"cc-option\"\n [class.selected]=\"isSelected(option)\" [class.focused]=\"i === focusedIndex\"\n [class.disabled]=\"option.disabled\" (click)=\"selectOption(option)\">\n\n <!-- Multi-select checkbox visual -->\n <span class=\"cc-multi-check\" *ngIf=\"multiple\">\n <span class=\"material-icons\" *ngIf=\"isSelected(option)\">check_box</span>\n <span class=\"material-icons\" *ngIf=\"!isSelected(option)\">check_box_outline_blank</span>\n </span>\n\n <!-- Icon -->\n <span class=\"cc-option-icon-wrapper\" *ngIf=\"option.icon\">\n <span class=\"material-icons\" *ngIf=\"getIconType(option.icon) === 'material'\">{{\n getIconValue(option.icon) }}</span>\n <i *ngIf=\"getIconType(option.icon) === 'fontawesome'\" [class]=\"getIconValue(option.icon)\"></i>\n <img *ngIf=\"getIconType(option.icon) === 'img'\" [src]=\"getIconValue(option.icon)\"\n class=\"cc-option-img\" alt=\"icon\" />\n </span>\n\n <span class=\"cc-option-label\">{{ option.label }}</span>\n\n <!-- Single-select check visual -->\n <span class=\"cc-single-check\" *ngIf=\"!multiple && isSelected(option)\">\n <span class=\"material-icons\">check</span>\n </span>\n </div>\n\n <div *ngIf=\"filteredOptions.length === 0\" class=\"cc-no-results\">{{ resolvedLabels.noResultsFound }}</div>\n </cdk-virtual-scroll-viewport>\n </div>\n\n <!-- Error Message -->\n <div class=\"cc-error-message\" *ngIf=\"errorMessage\">{{ errorMessage }}</div>\n</div>", styles: [".cc-dropdown-wrapper{display:flex;flex-direction:column;width:100%;position:relative;font-family:var(--cc-dropdown-font-family, inherit);gap:var(--cc-dropdown-label-gap, .5rem)}.cc-dropdown-label{font-size:var(--cc-dropdown-font-size, .875rem);font-weight:var(--cc-dropdown-font-weight, 500);color:var(--cc-dropdown-label-color, #202124);margin:0;line-height:1.4}.cc-required{color:var(--cc-dropdown-required-color, #D93025);margin-left:.25rem}.cc-dropdown-trigger{display:flex;align-items:center;justify-content:space-between;width:100%;min-height:var(--cc-dropdown-height, 2.5rem);padding:var(--cc-dropdown-padding, .5rem .75rem);background-color:var(--cc-dropdown-bg, #FEFEFE);border:var(--cc-dropdown-border-width, 1px) solid var(--cc-dropdown-border-color, #BDC1C6);border-radius:var(--cc-dropdown-border-radius, .4375rem);cursor:pointer;-webkit-user-select:none;user-select:none;box-sizing:border-box;transition:all .2s}.cc-dropdown-trigger:focus{outline:none;border-color:var(--cc-dropdown-focus-border-color, #1A73E8)}.cc-dropdown-trigger.open{border-color:var(--cc-dropdown-focus-border-color, #1A73E8)}.cc-dropdown-trigger.error{border-color:var(--cc-dropdown-error-color, #D93025)}.cc-dropdown-trigger.disabled{background-color:var(--cc-dropdown-disabled-bg, #F1F3F4);cursor:not-allowed;color:var(--cc-dropdown-disabled-color, #80868B)}.cc-dropdown-trigger.disabled .cc-arrow,.cc-dropdown-trigger.disabled .cc-clear-btn{color:var(--cc-dropdown-disabled-color, #80868B)}.cc-selected-value{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--cc-dropdown-color, #202124);font-size:var(--cc-dropdown-font-size, .875rem);font-weight:var(--cc-dropdown-font-weight, 400)}.cc-placeholder{flex:1;color:var(--cc-dropdown-placeholder-color, #80868B);font-size:var(--cc-dropdown-font-size, .875rem)}.cc-actions{display:flex;align-items:center;gap:.5rem}.cc-arrow{color:var(--cc-dropdown-arrow-color, #5F6368);transition:transform .2s;font-size:1.25rem}.cc-dropdown-trigger.open .cc-arrow{transform:rotate(180deg)}.cc-clear-btn{display:flex;align-items:center;justify-content:center;color:var(--cc-dropdown-arrow-color, #5F6368);cursor:pointer}.cc-clear-btn:hover{color:#202124}.cc-clear-btn .material-icons{font-size:1.125rem}.cc-dropdown-menu{position:absolute;top:100%;left:0;width:100%;background-color:#fff;border:1px solid #BDC1C6;border-radius:0 0 4px 4px;box-shadow:0 2px 4px #0000001a;z-index:1000;margin-top:2px;overflow:hidden;animation:fadeIn .1s ease-out}@keyframes fadeIn{0%{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:translateY(0)}}.cc-search-container{padding:.5rem;border-bottom:1px solid var(--cc-dropdown-divider-color, #E0E0E0)}.cc-dropdown-search-input{width:100%;padding:.5rem;border:1px solid #BDC1C6;border-radius:4px;outline:none;box-sizing:border-box;font-size:.875rem}.cc-dropdown-search-input:focus{border-color:var(--cc-dropdown-focus-border-color, #1A73E8)}.cc-virtual-scroll{width:100%;max-height:240px}.cc-option{display:flex;align-items:center;padding:0 .75rem;height:40px;cursor:pointer;color:var(--cc-dropdown-color, #202124);font-size:var(--cc-dropdown-font-size, .875rem);transition:background-color .1s;box-sizing:border-box}.cc-option:hover,.cc-option.focused{background-color:#f1f3f4}.cc-option.selected{background-color:#e8f0fe;color:#1a73e8}.cc-option.disabled{opacity:.5;cursor:not-allowed;background-color:transparent}.cc-multi-check{margin-right:.5rem;color:var(--cc-dropdown-arrow-color, #5F6368);display:flex;align-items:center}.cc-multi-check .material-icons{font-size:1.25rem}.cc-option.selected .cc-multi-check{color:#1a73e8}.cc-option-icon-wrapper{margin-right:.5rem;display:flex;align-items:center}.cc-option-icon-wrapper .material-icons{font-size:1.25rem}.cc-option-img{width:1.25rem;height:1.25rem;object-fit:contain}.cc-option-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.cc-single-check{margin-left:.5rem;color:#1a73e8;display:flex;align-items:center}.cc-single-check .material-icons{font-size:1.125rem}.cc-no-results{padding:.75rem;color:#80868b;text-align:center;font-size:.875rem}.cc-error-message{font-size:.75rem;color:var(--cc-dropdown-error-color, #D93025);margin-top:.25rem}\n"], dependencies: [{ kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2$1.CdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i2$1.CdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i2$1.CdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "directive", type: ClickOutsideDirective, selector: "[libClickOutside]", outputs: ["libClickOutside"] }] });
|
|
4134
4585
|
}
|
|
4135
4586
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DropdownComponent, decorators: [{
|
|
4136
4587
|
type: Component,
|
|
@@ -4140,13 +4591,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
4140
4591
|
useExisting: forwardRef(() => DropdownComponent),
|
|
4141
4592
|
multi: true
|
|
4142
4593
|
}
|
|
4143
|
-
], template: "<div class=\"cc-dropdown-wrapper\" [ngStyle]=\"wrapperStyles\" libClickOutside (libClickOutside)=\"close()\">\n <!-- Label -->\n <label *ngIf=\"label\" class=\"cc-dropdown-label\" [ngStyle]=\"labelStyles\">\n {{ label }}\n <span *ngIf=\"required\" class=\"cc-required\">{{ resolvedLabels.requiredMarker }}</span>\n </label>\n\n <!-- Trigger -->\n <div class=\"cc-dropdown-trigger\" [class.disabled]=\"disabled\" [class.open]=\"isOpen\" [class.error]=\"errorMessage\"\n (click)=\"toggle()\" tabindex=\"0\" (keydown)=\"handleKeyboardEvent($event)\" [ngStyle]=\"fieldStyles\">\n\n <div class=\"cc-selected-value\" *ngIf=\"hasValue()\">\n {{ multiple ? value.length + ' ' + resolvedLabels.selectedSuffix : getSelectedLabel() }}\n </div>\n <div class=\"cc-placeholder\" *ngIf=\"!hasValue()\">{{ placeholder }}</div>\n\n <div class=\"cc-actions\">\n <span *ngIf=\"clearable && hasValue() && !disabled\" class=\"cc-clear-btn\" (click)=\"clearSelection($event)\"\n [attr.aria-label]=\"resolvedLabels.clearAriaLabel\">\n <span class=\"material-icons\">close</span>\n </span>\n <span class=\"cc-arrow material-icons\">expand_more</span>\n </div>\n </div>\n\n <!-- Dropdown Menu -->\n <div class=\"cc-dropdown-menu\" *ngIf=\"isOpen\">\n <!-- Search -->\n <div class=\"cc-search-container\" *ngIf=\"searchable\">\n <input type=\"text\" #searchInput [placeholder]=\"resolvedLabels.searchPlaceholder\"\n (input)=\"onSearch($any($event.target).value)\" (click)=\"$event.stopPropagation()\"\n class=\"cc-dropdown-search-input\" />\n </div>\n\n <!-- Options Virtual Scroll -->\n <cdk-virtual-scroll-viewport itemSize=\"40\" class=\"cc-virtual-scroll\"\n [style.height.px]=\"filteredOptions.length * 40 > 240 ? 240 : (filteredOptions.length * 40 || 40)\">\n\n <div *cdkVirtualFor=\"let option of filteredOptions; let i = index\" class=\"cc-option\"\n [class.selected]=\"isSelected(option)\" [class.focused]=\"i === focusedIndex\"\n [class.disabled]=\"option.disabled\" (click)=\"selectOption(option)\">\n\n <!-- Multi-select checkbox visual -->\n <span class=\"cc-multi-check\" *ngIf=\"multiple\">\n <span class=\"material-icons\" *ngIf=\"isSelected(option)\">check_box</span>\n <span class=\"material-icons\" *ngIf=\"!isSelected(option)\">check_box_outline_blank</span>\n </span>\n\n <!-- Icon -->\n <span class=\"cc-option-icon-wrapper\" *ngIf=\"option.icon\">\n <span class=\"material-icons\" *ngIf=\"getIconType(option.icon) === 'material'\">{{\n getIconValue(option.icon) }}</span>\n <i *ngIf=\"getIconType(option.icon) === 'fontawesome'\" [class]=\"getIconValue(option.icon)\"></i>\n <img *ngIf=\"getIconType(option.icon) === 'img'\" [src]=\"getIconValue(option.icon)\"\n class=\"cc-option-img\" alt=\"icon\" />\n </span>\n\n <span class=\"cc-option-label\">{{ option.label }}</span>\n\n <!-- Single-select check visual -->\n <span class=\"cc-single-check\" *ngIf=\"!multiple && isSelected(option)\">\n <span class=\"material-icons\">check</span>\n </span>\n </div>\n\n <div *ngIf=\"filteredOptions.length === 0\" class=\"cc-no-results\">{{ resolvedLabels.noResultsFound }}</div>\n </cdk-virtual-scroll-viewport>\n </div>\n\n <!-- Error Message -->\n <div class=\"cc-error-message\" *ngIf=\"errorMessage\">{{ errorMessage }}</div>\n</div>", styles: [".cc-dropdown-wrapper{display:flex;flex-direction:column;width:100%;position:relative;font-family:var(--cc-dropdown-font-family, inherit);gap:var(--cc-dropdown-label-gap, .5rem)}.cc-dropdown-label{font-size:var(--cc-dropdown-font-size, .875rem);font-weight:var(--cc-dropdown-font-weight, 500);color:var(--cc-dropdown-label-color, #202124);margin:0;line-height:1.4}.cc-required{color:var(--cc-dropdown-required-color, #D93025);margin-left:.25rem}.cc-dropdown-trigger{display:flex;align-items:center;justify-content:space-between;width:100%;min-height:var(--cc-dropdown-height, 2.5rem);padding:var(--cc-dropdown-padding, .5rem .75rem);background-color:var(--cc-dropdown-bg, #FEFEFE);border:var(--cc-dropdown-border-width, 1px) solid var(--cc-dropdown-border-color, #BDC1C6);border-radius:var(--cc-dropdown-border-radius, .4375rem);cursor:pointer;-webkit-user-select:none;user-select:none;box-sizing:border-box;transition:all .2s}.cc-dropdown-trigger:focus{outline:none;border-color:var(--cc-dropdown-focus-border-color, #1A73E8)}.cc-dropdown-trigger.open{border-color:var(--cc-dropdown-focus-border-color, #1A73E8)}.cc-dropdown-trigger.error{border-color:var(--cc-dropdown-error-color, #D93025)}.cc-dropdown-trigger.disabled{background-color:var(--cc-dropdown-disabled-bg, #F1F3F4);cursor:not-allowed;color:var(--cc-dropdown-disabled-color, #80868B)}.cc-dropdown-trigger.disabled .cc-arrow,.cc-dropdown-trigger.disabled .cc-clear-btn{color:var(--cc-dropdown-disabled-color, #80868B)}.cc-selected-value{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--cc-dropdown-color, #202124);font-size:var(--cc-dropdown-font-size, .875rem);font-weight:var(--cc-dropdown-font-weight, 400)}.cc-placeholder{flex:1;color:var(--cc-dropdown-placeholder-color, #80868B);font-size:var(--cc-dropdown-font-size, .875rem)}.cc-actions{display:flex;align-items:center;gap:.5rem}.cc-arrow{color:var(--cc-dropdown-arrow-color, #5F6368);transition:transform .2s;font-size:1.25rem}.cc-dropdown-trigger.open .cc-arrow{transform:rotate(180deg)}.cc-clear-btn{display:flex;align-items:center;justify-content:center;color:var(--cc-dropdown-arrow-color, #5F6368);cursor:pointer}.cc-clear-btn:hover{color:#202124}.cc-clear-btn .material-icons{font-size:1.125rem}.cc-dropdown-menu{position:absolute;top:100%;left:0;width:100%;background-color:#fff;border:1px solid #BDC1C6;border-radius:0 0 4px 4px;box-shadow:0 2px 4px #0000001a;z-index:1000;margin-top:2px;overflow:hidden;animation:fadeIn .1s ease-out}@keyframes fadeIn{0%{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:translateY(0)}}.cc-search-container{padding:.5rem;border-bottom:1px solid var(--cc-dropdown-divider-color, #E0E0E0)}.cc-dropdown-search-input{width:100%;padding:.5rem;border:1px solid #BDC1C6;border-radius:4px;outline:none;box-sizing:border-box;font-size:.875rem}.cc-dropdown-search-input:focus{border-color:var(--cc-dropdown-focus-border-color, #1A73E8)}.cc-virtual-scroll{width:100%;max-height:240px}.cc-option{display:flex;align-items:center;padding:0 .75rem;height:40px;cursor:pointer;color:var(--cc-dropdown-color, #202124);font-size:var(--cc-dropdown-font-size, .875rem);transition:background-color .1s;box-sizing:border-box}.cc-option:hover,.cc-option.focused{background-color:#f1f3f4}.cc-option.selected{background-color:#e8f0fe;color:#1a73e8}.cc-option.disabled{opacity:.5;cursor:not-allowed;background-color:transparent}.cc-multi-check{margin-right:.5rem;color:var(--cc-dropdown-arrow-color, #5F6368);display:flex;align-items:center}.cc-multi-check .material-icons{font-size:1.25rem}.cc-option.selected .cc-multi-check{color:#1a73e8}.cc-option-icon-wrapper{margin-right:.5rem;display:flex;align-items:center}.cc-option-icon-wrapper .material-icons{font-size:1.25rem}.cc-option-img{width:1.25rem;height:1.25rem;object-fit:contain}.cc-option-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.cc-single-check{margin-left:.5rem;color:#1a73e8;display:flex;align-items:center}.cc-single-check .material-icons{font-size:1.125rem}.cc-no-results{padding:.75rem;color:#80868b;text-align:center;font-size:.875rem}.cc-error-message{font-size:.75rem;color:var(--cc-dropdown-error-color, #D93025);margin-top:.25rem}\n"] }]
|
|
4144
|
-
}], propDecorators: { config: [{
|
|
4594
|
+
], template: "<div class=\"cc-dropdown-wrapper\" [ngStyle]=\"wrapperStyles\" libClickOutside (libClickOutside)=\"close()\">\n <!-- Label -->\n <label *ngIf=\"label\" class=\"cc-dropdown-label\" [ngStyle]=\"labelStyles\">\n {{ label }}\n <span *ngIf=\"required\" class=\"cc-required\">{{ resolvedLabels.requiredMarker }}</span>\n </label>\n\n <!-- Trigger -->\n <div #triggerEl class=\"cc-dropdown-trigger\" [class.disabled]=\"disabled\" [class.open]=\"isOpen\" [class.error]=\"errorMessage\"\n (click)=\"toggle()\" tabindex=\"0\" (keydown)=\"handleKeyboardEvent($event)\" [ngStyle]=\"fieldStyles\">\n\n <div class=\"cc-selected-value\" *ngIf=\"hasValue()\">\n {{ multiple ? value.length + ' ' + resolvedLabels.selectedSuffix : getSelectedLabel() }}\n </div>\n <div class=\"cc-placeholder\" *ngIf=\"!hasValue()\">{{ placeholder }}</div>\n\n <div class=\"cc-actions\">\n <span *ngIf=\"clearable && hasValue() && !disabled\" class=\"cc-clear-btn\" (click)=\"clearSelection($event)\"\n [attr.aria-label]=\"resolvedLabels.clearAriaLabel\">\n <span class=\"material-icons\">close</span>\n </span>\n <span class=\"cc-arrow material-icons\">expand_more</span>\n </div>\n </div>\n\n <!-- Dropdown Menu -->\n <div class=\"cc-dropdown-menu\" *ngIf=\"isOpen\"\n [ngStyle]=\"{ position: 'fixed', top: menuPosition.top + 'px', left: menuPosition.left + 'px', width: menuPosition.width + 'px', 'z-index': '9999' }\"\n (click)=\"$event.stopPropagation()\" (mousedown)=\"$event.stopPropagation()\">\n <!-- Search -->\n <div class=\"cc-search-container\" *ngIf=\"searchable\">\n <input type=\"text\" #searchInput [placeholder]=\"resolvedLabels.searchPlaceholder\"\n (input)=\"onSearch($any($event.target).value)\" (click)=\"$event.stopPropagation()\"\n class=\"cc-dropdown-search-input\" />\n </div>\n\n <!-- Options Virtual Scroll -->\n <cdk-virtual-scroll-viewport itemSize=\"40\" class=\"cc-virtual-scroll\"\n [style.height.px]=\"filteredOptions.length * 40 > 240 ? 240 : (filteredOptions.length * 40 || 40)\">\n\n <div *cdkVirtualFor=\"let option of filteredOptions; let i = index\" class=\"cc-option\"\n [class.selected]=\"isSelected(option)\" [class.focused]=\"i === focusedIndex\"\n [class.disabled]=\"option.disabled\" (click)=\"selectOption(option)\">\n\n <!-- Multi-select checkbox visual -->\n <span class=\"cc-multi-check\" *ngIf=\"multiple\">\n <span class=\"material-icons\" *ngIf=\"isSelected(option)\">check_box</span>\n <span class=\"material-icons\" *ngIf=\"!isSelected(option)\">check_box_outline_blank</span>\n </span>\n\n <!-- Icon -->\n <span class=\"cc-option-icon-wrapper\" *ngIf=\"option.icon\">\n <span class=\"material-icons\" *ngIf=\"getIconType(option.icon) === 'material'\">{{\n getIconValue(option.icon) }}</span>\n <i *ngIf=\"getIconType(option.icon) === 'fontawesome'\" [class]=\"getIconValue(option.icon)\"></i>\n <img *ngIf=\"getIconType(option.icon) === 'img'\" [src]=\"getIconValue(option.icon)\"\n class=\"cc-option-img\" alt=\"icon\" />\n </span>\n\n <span class=\"cc-option-label\">{{ option.label }}</span>\n\n <!-- Single-select check visual -->\n <span class=\"cc-single-check\" *ngIf=\"!multiple && isSelected(option)\">\n <span class=\"material-icons\">check</span>\n </span>\n </div>\n\n <div *ngIf=\"filteredOptions.length === 0\" class=\"cc-no-results\">{{ resolvedLabels.noResultsFound }}</div>\n </cdk-virtual-scroll-viewport>\n </div>\n\n <!-- Error Message -->\n <div class=\"cc-error-message\" *ngIf=\"errorMessage\">{{ errorMessage }}</div>\n</div>", styles: [".cc-dropdown-wrapper{display:flex;flex-direction:column;width:100%;position:relative;font-family:var(--cc-dropdown-font-family, inherit);gap:var(--cc-dropdown-label-gap, .5rem)}.cc-dropdown-label{font-size:var(--cc-dropdown-font-size, .875rem);font-weight:var(--cc-dropdown-font-weight, 500);color:var(--cc-dropdown-label-color, #202124);margin:0;line-height:1.4}.cc-required{color:var(--cc-dropdown-required-color, #D93025);margin-left:.25rem}.cc-dropdown-trigger{display:flex;align-items:center;justify-content:space-between;width:100%;min-height:var(--cc-dropdown-height, 2.5rem);padding:var(--cc-dropdown-padding, .5rem .75rem);background-color:var(--cc-dropdown-bg, #FEFEFE);border:var(--cc-dropdown-border-width, 1px) solid var(--cc-dropdown-border-color, #BDC1C6);border-radius:var(--cc-dropdown-border-radius, .4375rem);cursor:pointer;-webkit-user-select:none;user-select:none;box-sizing:border-box;transition:all .2s}.cc-dropdown-trigger:focus{outline:none;border-color:var(--cc-dropdown-focus-border-color, #1A73E8)}.cc-dropdown-trigger.open{border-color:var(--cc-dropdown-focus-border-color, #1A73E8)}.cc-dropdown-trigger.error{border-color:var(--cc-dropdown-error-color, #D93025)}.cc-dropdown-trigger.disabled{background-color:var(--cc-dropdown-disabled-bg, #F1F3F4);cursor:not-allowed;color:var(--cc-dropdown-disabled-color, #80868B)}.cc-dropdown-trigger.disabled .cc-arrow,.cc-dropdown-trigger.disabled .cc-clear-btn{color:var(--cc-dropdown-disabled-color, #80868B)}.cc-selected-value{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;color:var(--cc-dropdown-color, #202124);font-size:var(--cc-dropdown-font-size, .875rem);font-weight:var(--cc-dropdown-font-weight, 400)}.cc-placeholder{flex:1;color:var(--cc-dropdown-placeholder-color, #80868B);font-size:var(--cc-dropdown-font-size, .875rem)}.cc-actions{display:flex;align-items:center;gap:.5rem}.cc-arrow{color:var(--cc-dropdown-arrow-color, #5F6368);transition:transform .2s;font-size:1.25rem}.cc-dropdown-trigger.open .cc-arrow{transform:rotate(180deg)}.cc-clear-btn{display:flex;align-items:center;justify-content:center;color:var(--cc-dropdown-arrow-color, #5F6368);cursor:pointer}.cc-clear-btn:hover{color:#202124}.cc-clear-btn .material-icons{font-size:1.125rem}.cc-dropdown-menu{position:absolute;top:100%;left:0;width:100%;background-color:#fff;border:1px solid #BDC1C6;border-radius:0 0 4px 4px;box-shadow:0 2px 4px #0000001a;z-index:1000;margin-top:2px;overflow:hidden;animation:fadeIn .1s ease-out}@keyframes fadeIn{0%{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:translateY(0)}}.cc-search-container{padding:.5rem;border-bottom:1px solid var(--cc-dropdown-divider-color, #E0E0E0)}.cc-dropdown-search-input{width:100%;padding:.5rem;border:1px solid #BDC1C6;border-radius:4px;outline:none;box-sizing:border-box;font-size:.875rem}.cc-dropdown-search-input:focus{border-color:var(--cc-dropdown-focus-border-color, #1A73E8)}.cc-virtual-scroll{width:100%;max-height:240px}.cc-option{display:flex;align-items:center;padding:0 .75rem;height:40px;cursor:pointer;color:var(--cc-dropdown-color, #202124);font-size:var(--cc-dropdown-font-size, .875rem);transition:background-color .1s;box-sizing:border-box}.cc-option:hover,.cc-option.focused{background-color:#f1f3f4}.cc-option.selected{background-color:#e8f0fe;color:#1a73e8}.cc-option.disabled{opacity:.5;cursor:not-allowed;background-color:transparent}.cc-multi-check{margin-right:.5rem;color:var(--cc-dropdown-arrow-color, #5F6368);display:flex;align-items:center}.cc-multi-check .material-icons{font-size:1.25rem}.cc-option.selected .cc-multi-check{color:#1a73e8}.cc-option-icon-wrapper{margin-right:.5rem;display:flex;align-items:center}.cc-option-icon-wrapper .material-icons{font-size:1.25rem}.cc-option-img{width:1.25rem;height:1.25rem;object-fit:contain}.cc-option-label{flex:1;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.cc-single-check{margin-left:.5rem;color:#1a73e8;display:flex;align-items:center}.cc-single-check .material-icons{font-size:1.125rem}.cc-no-results{padding:.75rem;color:#80868b;text-align:center;font-size:.875rem}.cc-error-message{font-size:.75rem;color:var(--cc-dropdown-error-color, #D93025);margin-top:.25rem}\n"] }]
|
|
4595
|
+
}], ctorParameters: () => [{ type: i0.ElementRef }], propDecorators: { config: [{
|
|
4145
4596
|
type: Input
|
|
4146
4597
|
}], labels: [{
|
|
4147
4598
|
type: Input
|
|
4148
4599
|
}], options: [{
|
|
4149
4600
|
type: Input
|
|
4601
|
+
}], selectedValue: [{
|
|
4602
|
+
type: Input
|
|
4150
4603
|
}], placeholder: [{
|
|
4151
4604
|
type: Input
|
|
4152
4605
|
}], label: [{
|
|
@@ -4213,6 +4666,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
4213
4666
|
}], searchInput: [{
|
|
4214
4667
|
type: ViewChild,
|
|
4215
4668
|
args: ['searchInput']
|
|
4669
|
+
}], triggerEl: [{
|
|
4670
|
+
type: ViewChild,
|
|
4671
|
+
args: ['triggerEl']
|
|
4216
4672
|
}], handleKeyboardEvent: [{
|
|
4217
4673
|
type: HostListener,
|
|
4218
4674
|
args: ['keydown', ['$event']]
|
|
@@ -7198,6 +7654,12 @@ class ConfiguratorConfigPanelComponent {
|
|
|
7198
7654
|
* option URLs — these are pre-filled by the developer in the master JSON.
|
|
7199
7655
|
*/
|
|
7200
7656
|
showOptionConfig = true;
|
|
7657
|
+
/**
|
|
7658
|
+
* Field names that are mandatory and cannot be disabled.
|
|
7659
|
+
* When the currently selected field is in this list, IS ENABLED / READ ONLY /
|
|
7660
|
+
* DISABLED controls are hidden from the Field State step.
|
|
7661
|
+
*/
|
|
7662
|
+
lockedFieldNames = [];
|
|
7201
7663
|
/** Exposed to the template for the native type-switcher select */
|
|
7202
7664
|
switchableFieldTypes = SWITCHABLE_FIELD_TYPES;
|
|
7203
7665
|
configFormJson = '';
|
|
@@ -7280,6 +7742,13 @@ class ConfiguratorConfigPanelComponent {
|
|
|
7280
7742
|
if (!this.showOptionConfig) {
|
|
7281
7743
|
schema = this._filterSchemaForOptionConfig(schema);
|
|
7282
7744
|
}
|
|
7745
|
+
// When a mandatory/locked field is selected, hide IS ENABLED / READ ONLY / DISABLED
|
|
7746
|
+
const isLockedField = this.selectedField?.name
|
|
7747
|
+
? this.lockedFieldNames.includes(this.selectedField.name)
|
|
7748
|
+
: false;
|
|
7749
|
+
if (isLockedField) {
|
|
7750
|
+
schema = this._filterSchemaForLockedField(schema);
|
|
7751
|
+
}
|
|
7283
7752
|
// 1. Hide current SmartForm instance (renders false in the current CD cycle
|
|
7284
7753
|
// since ngOnChanges fires before the view is rendered)
|
|
7285
7754
|
this.showConfigForm = false;
|
|
@@ -7355,6 +7824,39 @@ class ConfiguratorConfigPanelComponent {
|
|
|
7355
7824
|
: schema.sectionConfig,
|
|
7356
7825
|
};
|
|
7357
7826
|
}
|
|
7827
|
+
/**
|
|
7828
|
+
* Remove IS ENABLED, READ ONLY, and DISABLED controls from the config schema
|
|
7829
|
+
* when the selected field is a mandatory/locked field.
|
|
7830
|
+
*/
|
|
7831
|
+
_filterSchemaForLockedField(schema) {
|
|
7832
|
+
const LOCKED_HIDDEN_FIELDS = new Set(['isEnabled', 'readonly', 'disabled']);
|
|
7833
|
+
const filterChildren = (children) => children
|
|
7834
|
+
.map((child) => {
|
|
7835
|
+
if (child.type === 'ROW' && Array.isArray(child.children)) {
|
|
7836
|
+
const filtered = child.children.filter((c) => !LOCKED_HIDDEN_FIELDS.has(c.name));
|
|
7837
|
+
return filtered.length ? { ...child, children: filtered } : null;
|
|
7838
|
+
}
|
|
7839
|
+
if (child.sectionConfig?.children) {
|
|
7840
|
+
const filteredChildren = filterChildren(child.sectionConfig.children);
|
|
7841
|
+
if (!filteredChildren.length)
|
|
7842
|
+
return null;
|
|
7843
|
+
return { ...child, sectionConfig: { ...child.sectionConfig, children: filteredChildren } };
|
|
7844
|
+
}
|
|
7845
|
+
if (LOCKED_HIDDEN_FIELDS.has(child.name))
|
|
7846
|
+
return null;
|
|
7847
|
+
return child;
|
|
7848
|
+
})
|
|
7849
|
+
.filter(Boolean);
|
|
7850
|
+
const topChildren = schema.sectionConfig?.children
|
|
7851
|
+
? filterChildren(schema.sectionConfig.children)
|
|
7852
|
+
: [];
|
|
7853
|
+
return {
|
|
7854
|
+
...schema,
|
|
7855
|
+
sectionConfig: schema.sectionConfig
|
|
7856
|
+
? { ...schema.sectionConfig, children: topChildren }
|
|
7857
|
+
: schema.sectionConfig,
|
|
7858
|
+
};
|
|
7859
|
+
}
|
|
7358
7860
|
// ─── Private: Extract initial values from FieldConfig ─────────────────────
|
|
7359
7861
|
_extractInitialValuesFromField(field) {
|
|
7360
7862
|
const v = {};
|
|
@@ -7520,7 +8022,7 @@ class ConfiguratorConfigPanelComponent {
|
|
|
7520
8022
|
return patch;
|
|
7521
8023
|
}
|
|
7522
8024
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ConfiguratorConfigPanelComponent, deps: [{ token: i0.ChangeDetectorRef }, { token: SnackbarService }], target: i0.ɵɵFactoryTarget.Component });
|
|
7523
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: ConfiguratorConfigPanelComponent, isStandalone: false, selector: "lib-configurator-config-panel", inputs: { selectedField: "selectedField", selectedFieldInfo: "selectedFieldInfo", builderFieldType: "builderFieldType", fieldTypeSchemaMap: "fieldTypeSchemaMap", showOptionConfig: "showOptionConfig" }, outputs: { configChange: "configChange", typeChange: "typeChange" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"fb-config-panel\">\n @if (!selectedField) {\n <div class=\"fb-config-panel__empty\">\n <div class=\"fb-config-panel__empty-icon\">\n <svg width=\"64\" height=\"64\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"/>\n <line x1=\"3\" y1=\"9\" x2=\"21\" y2=\"9\"/>\n <line x1=\"9\" y1=\"21\" x2=\"9\" y2=\"9\"/>\n </svg>\n </div>\n <p class=\"fb-config-panel__empty-text\">\n Select a field from the tree panel to edit its configuration.\n </p>\n </div>\n } @else {\n <div class=\"fb-config-panel__header\">\n <div class=\"fb-config-panel__header-left\">\n <h3 class=\"fb-config-panel__title\">\n {{ selectedField.label || selectedField.name || 'Field Settings' }}\n </h3>\n @if (selectedField.type) {\n <span class=\"fb-config-panel__badge\">{{ selectedField.type }}</span>\n }\n </div>\n\n <!-- Field Type Switcher: hidden for DROPDOWN / MULTI_SELECT fields -->\n @if (!isDropdownType) {\n <div class=\"fb-config-panel__type-switcher\">\n <label class=\"fb-config-panel__type-label\" for=\"fb-field-type-select\">Field Type</label>\n <select\n id=\"fb-field-type-select\"\n class=\"fb-config-panel__type-select\"\n [value]=\"currentBuilderType\"\n (change)=\"onTypeSelectChange($event)\"\n >\n @for (opt of switchableFieldTypes; track opt.value) {\n <option [value]=\"opt.value\">{{ opt.label }}</option>\n }\n </select>\n </div>\n }\n </div>\n\n <div class=\"fb-config-panel__form-wrapper\">\n @if (showConfigForm && configFormJson) {\n <div class=\"fb-config-card\">\n <lib-smart-form\n [formJson]=\"configFormJson\"\n [initialValues]=\"configInitialValues\"\n (actionClick)=\"onActionClick($event)\"\n />\n </div>\n } @else if (!showConfigForm) {\n <!-- intentionally empty \u2014 SmartForm is being recycled -->\n } @else {\n <div class=\"fb-config-panel__error\">\n No configuration schema available for this field type.\n </div>\n }\n </div>\n }\n</div>\n", styles: [".fb-config-panel{display:flex;flex-direction:column;height:100%;background:var(--fb-fc-panel-bg, #f9fafb);overflow:hidden;--cc-sf-font-family: Inter, Segoe UI, sans-serif;--cc-sf-font-size-base: .875rem;--cc-sf-accent-color: #3B82F6;--cc-sf-selected-color: #3B82F6;--cc-sf-form-bg: transparent;--cc-sf-form-padding: 0;--cc-sf-form-border-radius: 12px;--cc-sf-form-border: none;--cc-sf-form-shadow: none;--cc-sf-form-max-width: 1200px;--cc-sf-form-section-gap: 24px;--cc-sf-form-title-size: 1.5rem;--cc-sf-form-title-weight: 700;--cc-sf-form-title-color: #111827;--cc-sf-form-desc-size: .875rem;--cc-sf-form-desc-color: #6B7280;--cc-sf-section-bg: transparent;--cc-sf-section-border: none;--cc-sf-section-radius: 10px;--cc-sf-section-padding: 12px;--cc-sf-section-shadow: none;--cc-sf-section-gap: 20px;--cc-sf-section-label-size: 1rem;--cc-sf-section-label-weight: 600;--cc-sf-section-label-color: #1F2937;--cc-sf-section-label-border: 2px solid #E5E7EB;--cc-sf-section-header-accent-width: 4px;--cc-sf-section-header-accent-color: #3B82F6;--cc-sf-section-header-bg: #F9FAFB;--cc-sf-repeater-accordion-header-bg: #F9FAFB;--cc-sf-repeater-accordion-header-color: #1F2937;--cc-sf-repeater-accordion-active-bg: #EFF6FF;--cc-sf-repeater-badge-bg: #E5E7EB;--cc-sf-repeater-badge-color: #374151;--cc-sf-section-border-radius-inner: 8px;--cc-sf-instance-bg: #F9FAFB;--cc-sf-instance-border: 1px solid #E5E7EB;--cc-sf-instance-radius: 8px;--cc-sf-instance-padding: 16px;--cc-sf-instance-divider: 1px dashed #D1D5DB;--cc-sf-instance-num-color:#4B5563;--cc-sf-instance-num-size: .8125rem;--cc-sf-grid-gap: 12px;--cc-sf-field-gap: 6px;--cc-sf-focus-ring-width: 3px;--cc-sf-focus-ring-offset: 0;--cc-sf-label-size: .75rem;--cc-sf-label-weight: 600;--cc-sf-label-color: #111827;--cc-sf-label-required-color: #DC2626;--cc-sf-input-bg: #ffffff;--cc-sf-input-color: #111827;--cc-sf-input-placeholder: #9CA3AF;--cc-sf-input-border: 1.5px solid #D1D5DB;--cc-sf-input-radius: 8px;--cc-sf-input-padding: .5rem .75rem;--cc-sf-input-font-size: .875rem;--cc-sf-input-shadow: none;--cc-sf-input-hover-border:#9CA3AF;--cc-sf-input-focus-border:#3B82F6;--cc-sf-input-focus-shadow:0 0 0 3px rgba(59, 130, 246, .12);--cc-sf-input-transition: all .2s ease;--cc-sf-input-disabled-bg: #F3F4F6;--cc-sf-input-disabled-color: #6B7280;--cc-sf-input-disabled-border:#E5E7EB;--cc-sf-error-border: #DC2626;--cc-sf-error-bg: #FEF2F2;--cc-sf-error-focus-shadow: 0 0 0 3px rgba(220, 38, 38, .1);--cc-sf-error-text-color: #DC2626;--cc-sf-error-text-size: .75rem;--cc-sf-hint-color: #6B7280;--cc-sf-hint-size: .75rem;--cc-sf-chip-border: 1px solid #D1D5DB;--cc-sf-chip-radius: 20px;--cc-sf-chip-padding: 6px 14px;--cc-sf-chip-bg: #ffffff;--cc-sf-chip-color: #374151;--cc-sf-chip-hover-bg: #F3F4F6;--cc-sf-chip-selected-bg: #3B82F6;--cc-sf-chip-selected-color: #ffffff;--cc-sf-chip-selected-border:#3B82F6;--cc-sf-switch-track-off: #D1D5DB;--cc-sf-switch-track-on: #3B82F6;--cc-sf-switch-thumb: #ffffff;--cc-sf-star-empty: #D1D5DB;--cc-sf-star-filled: #F59E0B;--cc-sf-star-size: 28px;--cc-sf-generated-bg: #F3F4F6;--cc-sf-generated-border: 1px solid #E5E7EB;--cc-sf-generated-color: #6B7280;--cc-sf-generated-radius: 8px;--cc-sf-generated-padding: .625rem .875rem;--cc-sf-dropzone-bg: #F8FAFC;--cc-sf-dropzone-border: 1.5px dashed #CBD5E1;--cc-sf-dropzone-radius: 12px;--cc-sf-dropzone-hover-bg: #EFF6FF;--cc-sf-dropzone-hover-border: #93C5FD;--cc-sf-dropzone-over-border: #3B82F6;--cc-sf-dropzone-over-shadow: 0 0 0 4px rgba(59, 130, 246, .12);--cc-sf-dropzone-icon-color: #94A3B8;--cc-sf-dropzone-icon-bg: rgba(59, 130, 246, .1);--cc-sf-dropzone-link-color: #3B82F6;--cc-sf-dropzone-hint-color: #64748B;--cc-sf-uploaded-item-bg: #ffffff;--cc-sf-uploaded-item-border: 1px solid #E2E8F0;--cc-sf-uploaded-item-radius: 8px;--cc-sf-uploaded-remove-color: #94A3B8;--cc-sf-uploaded-remove-hover-bg: #FEF2F2;--cc-sf-uploaded-remove-hover-color: #DC2626;--cc-sf-btn-add-bg: transparent;--cc-sf-btn-add-color: #3B82F6;--cc-sf-btn-add-border: 1px dashed #CBD5E1;--cc-sf-btn-add-radius: 6px;--cc-sf-btn-add-hover-bg: #EFF6FF;--cc-sf-btn-add-hover-border: #BFDBFE;--cc-sf-btn-remove-bg: #FFF5F5;--cc-sf-btn-remove-color: #E53E3E;--cc-sf-btn-remove-border: 1px solid #FED7D7;--cc-sf-btn-remove-radius: 4px;--cc-sf-btn-remove-hover-bg: #FED7D7;--cc-sf-btn-remove-padding: .375rem .75rem;--cc-sf-btn-remove-font-size: .875rem;--cc-sf-btn-remove-font-weight: 500;--cc-sf-btn-add-padding: .625rem 1.5rem;--cc-sf-btn-add-font-size: .875rem;--cc-sf-btn-add-font-weight: 600;--cc-sf-stepper-connector-display: block;--cc-sf-instance-shadow: none;--cc-sf-instance-hover-shadow: none;--cc-sf-instance-transition: none;--cc-sf-instance-header-divider: none;--cc-sf-instance-header-pb: 0px;--cc-sf-instance-header-mb: 0px;--cc-sf-instance-num-weight: 600;--cc-sf-section-stepper-accent: #6366F1;--cc-sf-section-stepper-accent-end: #8B5CF6;--cc-sf-section-stepper-active-label: #4F46E5;--cc-sf-section-stepper-active-glow: rgba(99, 102, 241, .25);--cc-sf-step-connector-color: #E5E7EB;--cc-sf-step-connector-done: #22C55E;--cc-sf-step-number-bg: #E5E7EB;--cc-sf-step-number-color: #6B7280;--cc-sf-step-number-size: 40px;--cc-sf-step-number-font-size: .875rem;--cc-sf-step-number-weight: 600;--cc-sf-step-label-size: .875rem;--cc-sf-step-label-color: #6B7280;--cc-sf-step-label-weight: 500;--cc-sf-step-active-bg: #3B82F6;--cc-sf-step-active-color: #ffffff;--cc-sf-step-active-label: #1D4ED8;--cc-sf-step-active-label-weight: 700;--cc-sf-step-done-bg: #22C55E;--cc-sf-step-done-color: #ffffff;--cc-sf-actions-border: 1px solid #E5E7EB;--cc-sf-actions-padding: 20px 10px 0;--cc-sf-actions-gap: 12px;--cc-sf-btn-primary-bg: #3B82F6;--cc-sf-btn-primary-color: #ffffff;--cc-sf-btn-primary-radius: 8px;--cc-sf-btn-primary-padding: .625rem 1.5rem;--cc-sf-btn-primary-hover-bg: #2563EB;--cc-sf-btn-secondary-bg: #F3F4F6;--cc-sf-btn-secondary-color: #374151;--cc-sf-btn-secondary-radius: 8px;--cc-sf-btn-secondary-padding: .625rem 1.5rem;--cc-sf-btn-secondary-hover-bg: #E5E7EB;--cc-sf-btn-font-size: .875rem;--cc-sf-btn-font-weight: 600;--cc-sf-btn-transition: all .2s ease;--cc-sf-btn-disabled-opacity: .55}.fb-config-panel ::ng-deep .stepper-nav .stepper-step:not(:last-child):after{display:var(--cc-sf-stepper-connector-display)!important}.fb-config-panel ::ng-deep .section-instance{background:var(--cc-sf-instance-bg)!important;border:var(--cc-sf-instance-border)!important;border-radius:var(--cc-sf-instance-radius)!important;box-shadow:var(--cc-sf-instance-shadow);transition:var(--cc-sf-instance-transition)}.fb-config-panel ::ng-deep .section-instance:hover{box-shadow:var(--cc-sf-instance-hover-shadow)}.fb-config-panel ::ng-deep .section-instance-header{display:flex;align-items:center;justify-content:space-between;padding-bottom:var(--cc-sf-instance-header-pb);margin-bottom:var(--cc-sf-instance-header-mb);border-bottom:var(--cc-sf-instance-header-divider)}.fb-config-panel ::ng-deep .section-instance-num{font-size:var(--cc-sf-instance-num-size);font-weight:var(--cc-sf-instance-num-weight);color:var(--cc-sf-instance-num-color)}.fb-config-panel ::ng-deep .btn-instance-remove{display:inline-flex;align-items:center;gap:6px;padding:var(--cc-sf-btn-remove-padding);border-radius:var(--cc-sf-btn-remove-radius);border:var(--cc-sf-btn-remove-border)!important;background:var(--cc-sf-btn-remove-bg);color:var(--cc-sf-btn-remove-color)!important;font-size:var(--cc-sf-btn-remove-font-size);font-weight:var(--cc-sf-btn-remove-font-weight);cursor:pointer;transition:all .15s ease}.fb-config-panel ::ng-deep .btn-instance-remove:hover{background:var(--cc-sf-btn-remove-hover-bg)}.fb-config-panel ::ng-deep .btn-add-section{display:inline-flex;align-items:center;gap:8px;padding:var(--cc-sf-btn-add-padding);border-radius:var(--cc-sf-btn-add-radius);border:var(--cc-sf-btn-add-border)!important;background:var(--cc-sf-btn-add-bg);color:var(--cc-sf-btn-add-color);font-size:var(--cc-sf-btn-add-font-size);font-weight:var(--cc-sf-btn-add-font-weight);cursor:pointer;transition:all .18s ease}.fb-config-panel ::ng-deep .btn-add-section:hover{background:var(--cc-sf-btn-add-hover-bg);border-color:var(--cc-sf-btn-add-hover-border)!important}.fb-config-panel ::ng-deep .section-stepper-nav:before{background:linear-gradient(90deg,var(--cc-sf-section-stepper-accent) 0%,var(--cc-sf-section-stepper-accent-end) 100%)!important}.fb-config-panel ::ng-deep .section-stepper-step.ss-active .ss-badge{background:linear-gradient(135deg,var(--cc-sf-section-stepper-accent) 0%,var(--cc-sf-section-stepper-accent-end) 100%)!important;box-shadow:0 0 0 4px var(--cc-sf-section-stepper-active-glow),0 4px 12px var(--cc-sf-section-stepper-active-glow)!important}.fb-config-panel ::ng-deep .section-stepper-step.ss-active .ss-label{color:var(--cc-sf-section-stepper-active-label)!important}.fb-config-panel ::ng-deep .section-stepper-step:not(.ss-active):not(.ss-completed):not(.ss-warning):hover .ss-badge{color:var(--cc-sf-section-stepper-accent)!important;border-color:var(--cc-sf-section-stepper-accent-end)!important}.fb-config-panel ::ng-deep .section-stepper-step .ss-connector:after{background:linear-gradient(90deg,var(--cc-sf-section-stepper-accent),var(--cc-sf-section-stepper-accent-end))!important}@media(min-width:600px){.fb-config-panel{--cc-sf-font-family: Inter, Segoe UI, sans-serif;--cc-sf-font-size-base: .875rem;--cc-sf-accent-color: #3B82F6;--cc-sf-selected-color: #3B82F6;--cc-sf-form-bg: transparent;--cc-sf-form-padding: 0;--cc-sf-form-border-radius: 12px;--cc-sf-form-border: none;--cc-sf-form-shadow: none;--cc-sf-form-max-width: 1200px;--cc-sf-form-section-gap: 24px;--cc-sf-form-title-size: 1.5rem;--cc-sf-form-title-weight: 700;--cc-sf-form-title-color: #111827;--cc-sf-form-desc-size: .875rem;--cc-sf-form-desc-color: #6B7280;--cc-sf-section-bg: transparent;--cc-sf-section-border: none;--cc-sf-section-radius: 10px;--cc-sf-section-padding: 14px;--cc-sf-section-shadow: none;--cc-sf-section-gap: 20px;--cc-sf-section-label-size: 1rem;--cc-sf-section-label-weight: 600;--cc-sf-section-label-color: #1F2937;--cc-sf-section-label-border: 2px solid #E5E7EB;--cc-sf-section-header-accent-width: 4px;--cc-sf-section-header-accent-color: #3B82F6;--cc-sf-section-header-bg: #F9FAFB;--cc-sf-repeater-accordion-header-bg: #F9FAFB;--cc-sf-repeater-accordion-header-color: #1F2937;--cc-sf-repeater-accordion-active-bg: #EFF6FF;--cc-sf-repeater-badge-bg: #E5E7EB;--cc-sf-repeater-badge-color: #374151;--cc-sf-section-border-radius-inner: 8px;--cc-sf-instance-bg: #F9FAFB;--cc-sf-instance-border: 1px solid #E5E7EB;--cc-sf-instance-radius: 8px;--cc-sf-instance-padding: 16px;--cc-sf-instance-divider: 1px dashed #D1D5DB;--cc-sf-instance-num-color:#4B5563;--cc-sf-instance-num-size: .8125rem;--cc-sf-grid-gap: 14px;--cc-sf-field-gap: 6px;--cc-sf-focus-ring-width: 3px;--cc-sf-focus-ring-offset: 0;--cc-sf-label-size: .8rem;--cc-sf-label-weight: 600;--cc-sf-label-color: #111827;--cc-sf-label-required-color: #DC2626;--cc-sf-input-bg: #ffffff;--cc-sf-input-color: #111827;--cc-sf-input-placeholder: #9CA3AF;--cc-sf-input-border: 1.5px solid #D1D5DB;--cc-sf-input-radius: 8px;--cc-sf-input-padding: .5rem .75rem;--cc-sf-input-font-size: .875rem;--cc-sf-input-shadow: none;--cc-sf-input-hover-border:#9CA3AF;--cc-sf-input-focus-border:#3B82F6;--cc-sf-input-focus-shadow:0 0 0 3px rgba(59, 130, 246, .12);--cc-sf-input-transition: all .2s ease;--cc-sf-input-disabled-bg: #F3F4F6;--cc-sf-input-disabled-color: #6B7280;--cc-sf-input-disabled-border:#E5E7EB;--cc-sf-error-border: #DC2626;--cc-sf-error-bg: #FEF2F2;--cc-sf-error-focus-shadow: 0 0 0 3px rgba(220, 38, 38, .1);--cc-sf-error-text-color: #DC2626;--cc-sf-error-text-size: .75rem;--cc-sf-hint-color: #6B7280;--cc-sf-hint-size: .75rem;--cc-sf-chip-border: 1px solid #D1D5DB;--cc-sf-chip-radius: 20px;--cc-sf-chip-padding: 6px 14px;--cc-sf-chip-bg: #ffffff;--cc-sf-chip-color: #374151;--cc-sf-chip-hover-bg: #F3F4F6;--cc-sf-chip-selected-bg: #3B82F6;--cc-sf-chip-selected-color: #ffffff;--cc-sf-chip-selected-border:#3B82F6;--cc-sf-switch-track-off: #D1D5DB;--cc-sf-switch-track-on: #3B82F6;--cc-sf-switch-thumb: #ffffff;--cc-sf-star-empty: #D1D5DB;--cc-sf-star-filled: #F59E0B;--cc-sf-star-size: 28px;--cc-sf-generated-bg: #F3F4F6;--cc-sf-generated-border: 1px solid #E5E7EB;--cc-sf-generated-color: #6B7280;--cc-sf-generated-radius: 8px;--cc-sf-generated-padding: .625rem .875rem;--cc-sf-dropzone-bg: #F8FAFC;--cc-sf-dropzone-border: 1.5px dashed #CBD5E1;--cc-sf-dropzone-radius: 12px;--cc-sf-dropzone-hover-bg: #EFF6FF;--cc-sf-dropzone-hover-border: #93C5FD;--cc-sf-dropzone-over-border: #3B82F6;--cc-sf-dropzone-over-shadow: 0 0 0 4px rgba(59, 130, 246, .12);--cc-sf-dropzone-icon-color: #94A3B8;--cc-sf-dropzone-icon-bg: rgba(59, 130, 246, .1);--cc-sf-dropzone-link-color: #3B82F6;--cc-sf-dropzone-hint-color: #64748B;--cc-sf-uploaded-item-bg: #ffffff;--cc-sf-uploaded-item-border: 1px solid #E2E8F0;--cc-sf-uploaded-item-radius: 8px;--cc-sf-uploaded-remove-color: #94A3B8;--cc-sf-uploaded-remove-hover-bg: #FEF2F2;--cc-sf-uploaded-remove-hover-color: #DC2626;--cc-sf-btn-add-bg: transparent;--cc-sf-btn-add-color: #3B82F6;--cc-sf-btn-add-border: 1px dashed #CBD5E1;--cc-sf-btn-add-radius: 6px;--cc-sf-btn-add-hover-bg: #EFF6FF;--cc-sf-btn-add-hover-border: #BFDBFE;--cc-sf-btn-remove-bg: #FFF5F5;--cc-sf-btn-remove-color: #E53E3E;--cc-sf-btn-remove-border: 1px solid #FED7D7;--cc-sf-btn-remove-radius: 4px;--cc-sf-btn-remove-hover-bg: #FED7D7;--cc-sf-btn-remove-padding: .375rem .75rem;--cc-sf-btn-remove-font-size: .875rem;--cc-sf-btn-remove-font-weight: 500;--cc-sf-btn-add-padding: .625rem 1.5rem;--cc-sf-btn-add-font-size: .875rem;--cc-sf-btn-add-font-weight: 600;--cc-sf-stepper-connector-display: block;--cc-sf-instance-shadow: none;--cc-sf-instance-hover-shadow: none;--cc-sf-instance-transition: none;--cc-sf-instance-header-divider: none;--cc-sf-instance-header-pb: 0px;--cc-sf-instance-header-mb: 0px;--cc-sf-instance-num-weight: 600;--cc-sf-section-stepper-accent: #6366F1;--cc-sf-section-stepper-accent-end: #8B5CF6;--cc-sf-section-stepper-active-label: #4F46E5;--cc-sf-section-stepper-active-glow: rgba(99, 102, 241, .25);--cc-sf-step-connector-color: #E5E7EB;--cc-sf-step-connector-done: #22C55E;--cc-sf-step-number-bg: #E5E7EB;--cc-sf-step-number-color: #6B7280;--cc-sf-step-number-size: 40px;--cc-sf-step-number-font-size: .875rem;--cc-sf-step-number-weight: 600;--cc-sf-step-label-size: .875rem;--cc-sf-step-label-color: #6B7280;--cc-sf-step-label-weight: 500;--cc-sf-step-active-bg: #3B82F6;--cc-sf-step-active-color: #ffffff;--cc-sf-step-active-label: #1D4ED8;--cc-sf-step-active-label-weight: 700;--cc-sf-step-done-bg: #22C55E;--cc-sf-step-done-color: #ffffff;--cc-sf-actions-border: 1px solid #E5E7EB;--cc-sf-actions-padding: 20px 10px 0;--cc-sf-actions-gap: 12px;--cc-sf-btn-primary-bg: #3B82F6;--cc-sf-btn-primary-color: #ffffff;--cc-sf-btn-primary-radius: 8px;--cc-sf-btn-primary-padding: .625rem 1.5rem;--cc-sf-btn-primary-hover-bg: #2563EB;--cc-sf-btn-secondary-bg: #F3F4F6;--cc-sf-btn-secondary-color: #374151;--cc-sf-btn-secondary-radius: 8px;--cc-sf-btn-secondary-padding: .625rem 1.5rem;--cc-sf-btn-secondary-hover-bg: #E5E7EB;--cc-sf-btn-font-size: .875rem;--cc-sf-btn-font-weight: 600;--cc-sf-btn-transition: all .2s ease;--cc-sf-btn-disabled-opacity: .55}.fb-config-panel ::ng-deep .stepper-nav .stepper-step:not(:last-child):after{display:var(--cc-sf-stepper-connector-display)!important}.fb-config-panel ::ng-deep .section-instance{background:var(--cc-sf-instance-bg)!important;border:var(--cc-sf-instance-border)!important;border-radius:var(--cc-sf-instance-radius)!important;box-shadow:var(--cc-sf-instance-shadow);transition:var(--cc-sf-instance-transition)}.fb-config-panel ::ng-deep .section-instance:hover{box-shadow:var(--cc-sf-instance-hover-shadow)}.fb-config-panel ::ng-deep .section-instance-header{display:flex;align-items:center;justify-content:space-between;padding-bottom:var(--cc-sf-instance-header-pb);margin-bottom:var(--cc-sf-instance-header-mb);border-bottom:var(--cc-sf-instance-header-divider)}.fb-config-panel ::ng-deep .section-instance-num{font-size:var(--cc-sf-instance-num-size);font-weight:var(--cc-sf-instance-num-weight);color:var(--cc-sf-instance-num-color)}.fb-config-panel ::ng-deep .btn-instance-remove{display:inline-flex;align-items:center;gap:6px;padding:var(--cc-sf-btn-remove-padding);border-radius:var(--cc-sf-btn-remove-radius);border:var(--cc-sf-btn-remove-border)!important;background:var(--cc-sf-btn-remove-bg);color:var(--cc-sf-btn-remove-color)!important;font-size:var(--cc-sf-btn-remove-font-size);font-weight:var(--cc-sf-btn-remove-font-weight);cursor:pointer;transition:all .15s ease}.fb-config-panel ::ng-deep .btn-instance-remove:hover{background:var(--cc-sf-btn-remove-hover-bg)}.fb-config-panel ::ng-deep .btn-add-section{display:inline-flex;align-items:center;gap:8px;padding:var(--cc-sf-btn-add-padding);border-radius:var(--cc-sf-btn-add-radius);border:var(--cc-sf-btn-add-border)!important;background:var(--cc-sf-btn-add-bg);color:var(--cc-sf-btn-add-color);font-size:var(--cc-sf-btn-add-font-size);font-weight:var(--cc-sf-btn-add-font-weight);cursor:pointer;transition:all .18s ease}.fb-config-panel ::ng-deep .btn-add-section:hover{background:var(--cc-sf-btn-add-hover-bg);border-color:var(--cc-sf-btn-add-hover-border)!important}.fb-config-panel ::ng-deep .section-stepper-nav:before{background:linear-gradient(90deg,var(--cc-sf-section-stepper-accent) 0%,var(--cc-sf-section-stepper-accent-end) 100%)!important}.fb-config-panel ::ng-deep .section-stepper-step.ss-active .ss-badge{background:linear-gradient(135deg,var(--cc-sf-section-stepper-accent) 0%,var(--cc-sf-section-stepper-accent-end) 100%)!important;box-shadow:0 0 0 4px var(--cc-sf-section-stepper-active-glow),0 4px 12px var(--cc-sf-section-stepper-active-glow)!important}.fb-config-panel ::ng-deep .section-stepper-step.ss-active .ss-label{color:var(--cc-sf-section-stepper-active-label)!important}.fb-config-panel ::ng-deep .section-stepper-step:not(.ss-active):not(.ss-completed):not(.ss-warning):hover .ss-badge{color:var(--cc-sf-section-stepper-accent)!important;border-color:var(--cc-sf-section-stepper-accent-end)!important}.fb-config-panel ::ng-deep .section-stepper-step .ss-connector:after{background:linear-gradient(90deg,var(--cc-sf-section-stepper-accent),var(--cc-sf-section-stepper-accent-end))!important}}@media(min-width:768px){.fb-config-panel{--cc-sf-font-family: Inter, Segoe UI, sans-serif;--cc-sf-font-size-base: .875rem;--cc-sf-accent-color: #3B82F6;--cc-sf-selected-color: #3B82F6;--cc-sf-form-bg: transparent;--cc-sf-form-padding: 0;--cc-sf-form-border-radius: 12px;--cc-sf-form-border: none;--cc-sf-form-shadow: none;--cc-sf-form-max-width: 1200px;--cc-sf-form-section-gap: 24px;--cc-sf-form-title-size: 1.5rem;--cc-sf-form-title-weight: 700;--cc-sf-form-title-color: #111827;--cc-sf-form-desc-size: .875rem;--cc-sf-form-desc-color: #6B7280;--cc-sf-section-bg: transparent;--cc-sf-section-border: none;--cc-sf-section-radius: 10px;--cc-sf-section-padding: 16px;--cc-sf-section-shadow: none;--cc-sf-section-gap: 20px;--cc-sf-section-label-size: 1rem;--cc-sf-section-label-weight: 600;--cc-sf-section-label-color: #1F2937;--cc-sf-section-label-border: 2px solid #E5E7EB;--cc-sf-section-header-accent-width: 4px;--cc-sf-section-header-accent-color: #3B82F6;--cc-sf-section-header-bg: #F9FAFB;--cc-sf-repeater-accordion-header-bg: #F9FAFB;--cc-sf-repeater-accordion-header-color: #1F2937;--cc-sf-repeater-accordion-active-bg: #EFF6FF;--cc-sf-repeater-badge-bg: #E5E7EB;--cc-sf-repeater-badge-color: #374151;--cc-sf-section-border-radius-inner: 8px;--cc-sf-instance-bg: #F9FAFB;--cc-sf-instance-border: 1px solid #E5E7EB;--cc-sf-instance-radius: 8px;--cc-sf-instance-padding: 16px;--cc-sf-instance-divider: 1px dashed #D1D5DB;--cc-sf-instance-num-color:#4B5563;--cc-sf-instance-num-size: .8125rem;--cc-sf-grid-gap: 16px;--cc-sf-field-gap: 6px;--cc-sf-focus-ring-width: 3px;--cc-sf-focus-ring-offset: 0;--cc-sf-label-size: .8125rem;--cc-sf-label-weight: 600;--cc-sf-label-color: #111827;--cc-sf-label-required-color: #DC2626;--cc-sf-input-bg: #ffffff;--cc-sf-input-color: #111827;--cc-sf-input-placeholder: #9CA3AF;--cc-sf-input-border: 1.5px solid #D1D5DB;--cc-sf-input-radius: 8px;--cc-sf-input-padding: .625rem .875rem;--cc-sf-input-font-size: .875rem;--cc-sf-input-shadow: none;--cc-sf-input-hover-border:#9CA3AF;--cc-sf-input-focus-border:#3B82F6;--cc-sf-input-focus-shadow:0 0 0 3px rgba(59, 130, 246, .12);--cc-sf-input-transition: all .2s ease;--cc-sf-input-disabled-bg: #F3F4F6;--cc-sf-input-disabled-color: #6B7280;--cc-sf-input-disabled-border:#E5E7EB;--cc-sf-error-border: #DC2626;--cc-sf-error-bg: #FEF2F2;--cc-sf-error-focus-shadow: 0 0 0 3px rgba(220, 38, 38, .1);--cc-sf-error-text-color: #DC2626;--cc-sf-error-text-size: .75rem;--cc-sf-hint-color: #6B7280;--cc-sf-hint-size: .75rem;--cc-sf-chip-border: 1px solid #D1D5DB;--cc-sf-chip-radius: 20px;--cc-sf-chip-padding: 6px 14px;--cc-sf-chip-bg: #ffffff;--cc-sf-chip-color: #374151;--cc-sf-chip-hover-bg: #F3F4F6;--cc-sf-chip-selected-bg: #3B82F6;--cc-sf-chip-selected-color: #ffffff;--cc-sf-chip-selected-border:#3B82F6;--cc-sf-switch-track-off: #D1D5DB;--cc-sf-switch-track-on: #3B82F6;--cc-sf-switch-thumb: #ffffff;--cc-sf-star-empty: #D1D5DB;--cc-sf-star-filled: #F59E0B;--cc-sf-star-size: 28px;--cc-sf-generated-bg: #F3F4F6;--cc-sf-generated-border: 1px solid #E5E7EB;--cc-sf-generated-color: #6B7280;--cc-sf-generated-radius: 8px;--cc-sf-generated-padding: .625rem .875rem;--cc-sf-dropzone-bg: #F8FAFC;--cc-sf-dropzone-border: 1.5px dashed #CBD5E1;--cc-sf-dropzone-radius: 12px;--cc-sf-dropzone-hover-bg: #EFF6FF;--cc-sf-dropzone-hover-border: #93C5FD;--cc-sf-dropzone-over-border: #3B82F6;--cc-sf-dropzone-over-shadow: 0 0 0 4px rgba(59, 130, 246, .12);--cc-sf-dropzone-icon-color: #94A3B8;--cc-sf-dropzone-icon-bg: rgba(59, 130, 246, .1);--cc-sf-dropzone-link-color: #3B82F6;--cc-sf-dropzone-hint-color: #64748B;--cc-sf-uploaded-item-bg: #ffffff;--cc-sf-uploaded-item-border: 1px solid #E2E8F0;--cc-sf-uploaded-item-radius: 8px;--cc-sf-uploaded-remove-color: #94A3B8;--cc-sf-uploaded-remove-hover-bg: #FEF2F2;--cc-sf-uploaded-remove-hover-color: #DC2626;--cc-sf-btn-add-bg: transparent;--cc-sf-btn-add-color: #3B82F6;--cc-sf-btn-add-border: 1px dashed #CBD5E1;--cc-sf-btn-add-radius: 6px;--cc-sf-btn-add-hover-bg: #EFF6FF;--cc-sf-btn-add-hover-border: #BFDBFE;--cc-sf-btn-remove-bg: #FFF5F5;--cc-sf-btn-remove-color: #E53E3E;--cc-sf-btn-remove-border: 1px solid #FED7D7;--cc-sf-btn-remove-radius: 4px;--cc-sf-btn-remove-hover-bg: #FED7D7;--cc-sf-btn-remove-padding: .375rem .75rem;--cc-sf-btn-remove-font-size: .875rem;--cc-sf-btn-remove-font-weight: 500;--cc-sf-btn-add-padding: .625rem 1.5rem;--cc-sf-btn-add-font-size: .875rem;--cc-sf-btn-add-font-weight: 600;--cc-sf-stepper-connector-display: block;--cc-sf-instance-shadow: none;--cc-sf-instance-hover-shadow: none;--cc-sf-instance-transition: none;--cc-sf-instance-header-divider: none;--cc-sf-instance-header-pb: 0px;--cc-sf-instance-header-mb: 0px;--cc-sf-instance-num-weight: 600;--cc-sf-section-stepper-accent: #6366F1;--cc-sf-section-stepper-accent-end: #8B5CF6;--cc-sf-section-stepper-active-label: #4F46E5;--cc-sf-section-stepper-active-glow: rgba(99, 102, 241, .25);--cc-sf-step-connector-color: #E5E7EB;--cc-sf-step-connector-done: #22C55E;--cc-sf-step-number-bg: #E5E7EB;--cc-sf-step-number-color: #6B7280;--cc-sf-step-number-size: 40px;--cc-sf-step-number-font-size: .875rem;--cc-sf-step-number-weight: 600;--cc-sf-step-label-size: .875rem;--cc-sf-step-label-color: #6B7280;--cc-sf-step-label-weight: 500;--cc-sf-step-active-bg: #3B82F6;--cc-sf-step-active-color: #ffffff;--cc-sf-step-active-label: #1D4ED8;--cc-sf-step-active-label-weight: 700;--cc-sf-step-done-bg: #22C55E;--cc-sf-step-done-color: #ffffff;--cc-sf-actions-border: 1px solid #E5E7EB;--cc-sf-actions-padding: 20px 10px 0;--cc-sf-actions-gap: 12px;--cc-sf-btn-primary-bg: #3B82F6;--cc-sf-btn-primary-color: #ffffff;--cc-sf-btn-primary-radius: 8px;--cc-sf-btn-primary-padding: .625rem 1.5rem;--cc-sf-btn-primary-hover-bg: #2563EB;--cc-sf-btn-secondary-bg: #F3F4F6;--cc-sf-btn-secondary-color: #374151;--cc-sf-btn-secondary-radius: 8px;--cc-sf-btn-secondary-padding: .625rem 1.5rem;--cc-sf-btn-secondary-hover-bg: #E5E7EB;--cc-sf-btn-font-size: .875rem;--cc-sf-btn-font-weight: 600;--cc-sf-btn-transition: all .2s ease;--cc-sf-btn-disabled-opacity: .55}.fb-config-panel ::ng-deep .stepper-nav .stepper-step:not(:last-child):after{display:var(--cc-sf-stepper-connector-display)!important}.fb-config-panel ::ng-deep .section-instance{background:var(--cc-sf-instance-bg)!important;border:var(--cc-sf-instance-border)!important;border-radius:var(--cc-sf-instance-radius)!important;box-shadow:var(--cc-sf-instance-shadow);transition:var(--cc-sf-instance-transition)}.fb-config-panel ::ng-deep .section-instance:hover{box-shadow:var(--cc-sf-instance-hover-shadow)}.fb-config-panel ::ng-deep .section-instance-header{display:flex;align-items:center;justify-content:space-between;padding-bottom:var(--cc-sf-instance-header-pb);margin-bottom:var(--cc-sf-instance-header-mb);border-bottom:var(--cc-sf-instance-header-divider)}.fb-config-panel ::ng-deep .section-instance-num{font-size:var(--cc-sf-instance-num-size);font-weight:var(--cc-sf-instance-num-weight);color:var(--cc-sf-instance-num-color)}.fb-config-panel ::ng-deep .btn-instance-remove{display:inline-flex;align-items:center;gap:6px;padding:var(--cc-sf-btn-remove-padding);border-radius:var(--cc-sf-btn-remove-radius);border:var(--cc-sf-btn-remove-border)!important;background:var(--cc-sf-btn-remove-bg);color:var(--cc-sf-btn-remove-color)!important;font-size:var(--cc-sf-btn-remove-font-size);font-weight:var(--cc-sf-btn-remove-font-weight);cursor:pointer;transition:all .15s ease}.fb-config-panel ::ng-deep .btn-instance-remove:hover{background:var(--cc-sf-btn-remove-hover-bg)}.fb-config-panel ::ng-deep .btn-add-section{display:inline-flex;align-items:center;gap:8px;padding:var(--cc-sf-btn-add-padding);border-radius:var(--cc-sf-btn-add-radius);border:var(--cc-sf-btn-add-border)!important;background:var(--cc-sf-btn-add-bg);color:var(--cc-sf-btn-add-color);font-size:var(--cc-sf-btn-add-font-size);font-weight:var(--cc-sf-btn-add-font-weight);cursor:pointer;transition:all .18s ease}.fb-config-panel ::ng-deep .btn-add-section:hover{background:var(--cc-sf-btn-add-hover-bg);border-color:var(--cc-sf-btn-add-hover-border)!important}.fb-config-panel ::ng-deep .section-stepper-nav:before{background:linear-gradient(90deg,var(--cc-sf-section-stepper-accent) 0%,var(--cc-sf-section-stepper-accent-end) 100%)!important}.fb-config-panel ::ng-deep .section-stepper-step.ss-active .ss-badge{background:linear-gradient(135deg,var(--cc-sf-section-stepper-accent) 0%,var(--cc-sf-section-stepper-accent-end) 100%)!important;box-shadow:0 0 0 4px var(--cc-sf-section-stepper-active-glow),0 4px 12px var(--cc-sf-section-stepper-active-glow)!important}.fb-config-panel ::ng-deep .section-stepper-step.ss-active .ss-label{color:var(--cc-sf-section-stepper-active-label)!important}.fb-config-panel ::ng-deep .section-stepper-step:not(.ss-active):not(.ss-completed):not(.ss-warning):hover .ss-badge{color:var(--cc-sf-section-stepper-accent)!important;border-color:var(--cc-sf-section-stepper-accent-end)!important}.fb-config-panel ::ng-deep .section-stepper-step .ss-connector:after{background:linear-gradient(90deg,var(--cc-sf-section-stepper-accent),var(--cc-sf-section-stepper-accent-end))!important}}@media(min-width:1024px){.fb-config-panel{--cc-sf-font-family: Inter, Segoe UI, sans-serif;--cc-sf-font-size-base: .875rem;--cc-sf-accent-color: #3B82F6;--cc-sf-selected-color: #3B82F6;--cc-sf-form-bg: transparent;--cc-sf-form-padding: 0;--cc-sf-form-border-radius: 12px;--cc-sf-form-border: none;--cc-sf-form-shadow: none;--cc-sf-form-max-width: 1200px;--cc-sf-form-section-gap: 24px;--cc-sf-form-title-size: 1.5rem;--cc-sf-form-title-weight: 700;--cc-sf-form-title-color: #111827;--cc-sf-form-desc-size: .875rem;--cc-sf-form-desc-color: #6B7280;--cc-sf-section-bg: transparent;--cc-sf-section-border: none;--cc-sf-section-radius: 10px;--cc-sf-section-padding: 16px;--cc-sf-section-shadow: none;--cc-sf-section-gap: 20px;--cc-sf-section-label-size: 1rem;--cc-sf-section-label-weight: 600;--cc-sf-section-label-color: #1F2937;--cc-sf-section-label-border: 2px solid #E5E7EB;--cc-sf-section-header-accent-width: 4px;--cc-sf-section-header-accent-color: #3B82F6;--cc-sf-section-header-bg: #F9FAFB;--cc-sf-repeater-accordion-header-bg: #F9FAFB;--cc-sf-repeater-accordion-header-color: #1F2937;--cc-sf-repeater-accordion-active-bg: #EFF6FF;--cc-sf-repeater-badge-bg: #E5E7EB;--cc-sf-repeater-badge-color: #374151;--cc-sf-section-border-radius-inner: 8px;--cc-sf-instance-bg: #F9FAFB;--cc-sf-instance-border: 1px solid #E5E7EB;--cc-sf-instance-radius: 8px;--cc-sf-instance-padding: 16px;--cc-sf-instance-divider: 1px dashed #D1D5DB;--cc-sf-instance-num-color:#4B5563;--cc-sf-instance-num-size: .8125rem;--cc-sf-grid-gap: 16px;--cc-sf-field-gap: 6px;--cc-sf-focus-ring-width: 3px;--cc-sf-focus-ring-offset: 0;--cc-sf-label-size: .8125rem;--cc-sf-label-weight: 600;--cc-sf-label-color: #111827;--cc-sf-label-required-color: #DC2626;--cc-sf-input-bg: #ffffff;--cc-sf-input-color: #111827;--cc-sf-input-placeholder: #9CA3AF;--cc-sf-input-border: 1.5px solid #D1D5DB;--cc-sf-input-radius: 8px;--cc-sf-input-padding: .625rem .875rem;--cc-sf-input-font-size: .875rem;--cc-sf-input-shadow: none;--cc-sf-input-hover-border:#9CA3AF;--cc-sf-input-focus-border:#3B82F6;--cc-sf-input-focus-shadow:0 0 0 3px rgba(59, 130, 246, .12);--cc-sf-input-transition: all .2s ease;--cc-sf-input-disabled-bg: #F3F4F6;--cc-sf-input-disabled-color: #6B7280;--cc-sf-input-disabled-border:#E5E7EB;--cc-sf-error-border: #DC2626;--cc-sf-error-bg: #FEF2F2;--cc-sf-error-focus-shadow: 0 0 0 3px rgba(220, 38, 38, .1);--cc-sf-error-text-color: #DC2626;--cc-sf-error-text-size: .75rem;--cc-sf-hint-color: #6B7280;--cc-sf-hint-size: .75rem;--cc-sf-chip-border: 1px solid #D1D5DB;--cc-sf-chip-radius: 20px;--cc-sf-chip-padding: 6px 14px;--cc-sf-chip-bg: #ffffff;--cc-sf-chip-color: #374151;--cc-sf-chip-hover-bg: #F3F4F6;--cc-sf-chip-selected-bg: #3B82F6;--cc-sf-chip-selected-color: #ffffff;--cc-sf-chip-selected-border:#3B82F6;--cc-sf-switch-track-off: #D1D5DB;--cc-sf-switch-track-on: #3B82F6;--cc-sf-switch-thumb: #ffffff;--cc-sf-star-empty: #D1D5DB;--cc-sf-star-filled: #F59E0B;--cc-sf-star-size: 28px;--cc-sf-generated-bg: #F3F4F6;--cc-sf-generated-border: 1px solid #E5E7EB;--cc-sf-generated-color: #6B7280;--cc-sf-generated-radius: 8px;--cc-sf-generated-padding: .625rem .875rem;--cc-sf-dropzone-bg: #F8FAFC;--cc-sf-dropzone-border: 1.5px dashed #CBD5E1;--cc-sf-dropzone-radius: 12px;--cc-sf-dropzone-hover-bg: #EFF6FF;--cc-sf-dropzone-hover-border: #93C5FD;--cc-sf-dropzone-over-border: #3B82F6;--cc-sf-dropzone-over-shadow: 0 0 0 4px rgba(59, 130, 246, .12);--cc-sf-dropzone-icon-color: #94A3B8;--cc-sf-dropzone-icon-bg: rgba(59, 130, 246, .1);--cc-sf-dropzone-link-color: #3B82F6;--cc-sf-dropzone-hint-color: #64748B;--cc-sf-uploaded-item-bg: #ffffff;--cc-sf-uploaded-item-border: 1px solid #E2E8F0;--cc-sf-uploaded-item-radius: 8px;--cc-sf-uploaded-remove-color: #94A3B8;--cc-sf-uploaded-remove-hover-bg: #FEF2F2;--cc-sf-uploaded-remove-hover-color: #DC2626;--cc-sf-btn-add-bg: transparent;--cc-sf-btn-add-color: #3B82F6;--cc-sf-btn-add-border: 1px dashed #CBD5E1;--cc-sf-btn-add-radius: 6px;--cc-sf-btn-add-hover-bg: #EFF6FF;--cc-sf-btn-add-hover-border: #BFDBFE;--cc-sf-btn-remove-bg: #FFF5F5;--cc-sf-btn-remove-color: #E53E3E;--cc-sf-btn-remove-border: 1px solid #FED7D7;--cc-sf-btn-remove-radius: 4px;--cc-sf-btn-remove-hover-bg: #FED7D7;--cc-sf-btn-remove-padding: .375rem .75rem;--cc-sf-btn-remove-font-size: .875rem;--cc-sf-btn-remove-font-weight: 500;--cc-sf-btn-add-padding: .625rem 1.5rem;--cc-sf-btn-add-font-size: .875rem;--cc-sf-btn-add-font-weight: 600;--cc-sf-stepper-connector-display: block;--cc-sf-instance-shadow: none;--cc-sf-instance-hover-shadow: none;--cc-sf-instance-transition: none;--cc-sf-instance-header-divider: none;--cc-sf-instance-header-pb: 0px;--cc-sf-instance-header-mb: 0px;--cc-sf-instance-num-weight: 600;--cc-sf-section-stepper-accent: #6366F1;--cc-sf-section-stepper-accent-end: #8B5CF6;--cc-sf-section-stepper-active-label: #4F46E5;--cc-sf-section-stepper-active-glow: rgba(99, 102, 241, .25);--cc-sf-step-connector-color: #E5E7EB;--cc-sf-step-connector-done: #22C55E;--cc-sf-step-number-bg: #E5E7EB;--cc-sf-step-number-color: #6B7280;--cc-sf-step-number-size: 40px;--cc-sf-step-number-font-size: .875rem;--cc-sf-step-number-weight: 600;--cc-sf-step-label-size: .875rem;--cc-sf-step-label-color: #6B7280;--cc-sf-step-label-weight: 500;--cc-sf-step-active-bg: #3B82F6;--cc-sf-step-active-color: #ffffff;--cc-sf-step-active-label: #1D4ED8;--cc-sf-step-active-label-weight: 700;--cc-sf-step-done-bg: #22C55E;--cc-sf-step-done-color: #ffffff;--cc-sf-actions-border: 1px solid #E5E7EB;--cc-sf-actions-padding: 20px 10px 0;--cc-sf-actions-gap: 12px;--cc-sf-btn-primary-bg: #3B82F6;--cc-sf-btn-primary-color: #ffffff;--cc-sf-btn-primary-radius: 8px;--cc-sf-btn-primary-padding: .625rem 1.5rem;--cc-sf-btn-primary-hover-bg: #2563EB;--cc-sf-btn-secondary-bg: #F3F4F6;--cc-sf-btn-secondary-color: #374151;--cc-sf-btn-secondary-radius: 8px;--cc-sf-btn-secondary-padding: .625rem 1.5rem;--cc-sf-btn-secondary-hover-bg: #E5E7EB;--cc-sf-btn-font-size: .875rem;--cc-sf-btn-font-weight: 600;--cc-sf-btn-transition: all .2s ease;--cc-sf-btn-disabled-opacity: .55}.fb-config-panel ::ng-deep .stepper-nav .stepper-step:not(:last-child):after{display:var(--cc-sf-stepper-connector-display)!important}.fb-config-panel ::ng-deep .section-instance{background:var(--cc-sf-instance-bg)!important;border:var(--cc-sf-instance-border)!important;border-radius:var(--cc-sf-instance-radius)!important;box-shadow:var(--cc-sf-instance-shadow);transition:var(--cc-sf-instance-transition)}.fb-config-panel ::ng-deep .section-instance:hover{box-shadow:var(--cc-sf-instance-hover-shadow)}.fb-config-panel ::ng-deep .section-instance-header{display:flex;align-items:center;justify-content:space-between;padding-bottom:var(--cc-sf-instance-header-pb);margin-bottom:var(--cc-sf-instance-header-mb);border-bottom:var(--cc-sf-instance-header-divider)}.fb-config-panel ::ng-deep .section-instance-num{font-size:var(--cc-sf-instance-num-size);font-weight:var(--cc-sf-instance-num-weight);color:var(--cc-sf-instance-num-color)}.fb-config-panel ::ng-deep .btn-instance-remove{display:inline-flex;align-items:center;gap:6px;padding:var(--cc-sf-btn-remove-padding);border-radius:var(--cc-sf-btn-remove-radius);border:var(--cc-sf-btn-remove-border)!important;background:var(--cc-sf-btn-remove-bg);color:var(--cc-sf-btn-remove-color)!important;font-size:var(--cc-sf-btn-remove-font-size);font-weight:var(--cc-sf-btn-remove-font-weight);cursor:pointer;transition:all .15s ease}.fb-config-panel ::ng-deep .btn-instance-remove:hover{background:var(--cc-sf-btn-remove-hover-bg)}.fb-config-panel ::ng-deep .btn-add-section{display:inline-flex;align-items:center;gap:8px;padding:var(--cc-sf-btn-add-padding);border-radius:var(--cc-sf-btn-add-radius);border:var(--cc-sf-btn-add-border)!important;background:var(--cc-sf-btn-add-bg);color:var(--cc-sf-btn-add-color);font-size:var(--cc-sf-btn-add-font-size);font-weight:var(--cc-sf-btn-add-font-weight);cursor:pointer;transition:all .18s ease}.fb-config-panel ::ng-deep .btn-add-section:hover{background:var(--cc-sf-btn-add-hover-bg);border-color:var(--cc-sf-btn-add-hover-border)!important}.fb-config-panel ::ng-deep .section-stepper-nav:before{background:linear-gradient(90deg,var(--cc-sf-section-stepper-accent) 0%,var(--cc-sf-section-stepper-accent-end) 100%)!important}.fb-config-panel ::ng-deep .section-stepper-step.ss-active .ss-badge{background:linear-gradient(135deg,var(--cc-sf-section-stepper-accent) 0%,var(--cc-sf-section-stepper-accent-end) 100%)!important;box-shadow:0 0 0 4px var(--cc-sf-section-stepper-active-glow),0 4px 12px var(--cc-sf-section-stepper-active-glow)!important}.fb-config-panel ::ng-deep .section-stepper-step.ss-active .ss-label{color:var(--cc-sf-section-stepper-active-label)!important}.fb-config-panel ::ng-deep .section-stepper-step:not(.ss-active):not(.ss-completed):not(.ss-warning):hover .ss-badge{color:var(--cc-sf-section-stepper-accent)!important;border-color:var(--cc-sf-section-stepper-accent-end)!important}.fb-config-panel ::ng-deep .section-stepper-step .ss-connector:after{background:linear-gradient(90deg,var(--cc-sf-section-stepper-accent),var(--cc-sf-section-stepper-accent-end))!important}}@media(min-width:1440px){.fb-config-panel{--cc-sf-font-family: Inter, Segoe UI, sans-serif;--cc-sf-font-size-base: .875rem;--cc-sf-accent-color: #3B82F6;--cc-sf-selected-color: #3B82F6;--cc-sf-form-bg: transparent;--cc-sf-form-padding: 0;--cc-sf-form-border-radius: 12px;--cc-sf-form-border: none;--cc-sf-form-shadow: none;--cc-sf-form-max-width: 1200px;--cc-sf-form-section-gap: 24px;--cc-sf-form-title-size: 1.5rem;--cc-sf-form-title-weight: 700;--cc-sf-form-title-color: #111827;--cc-sf-form-desc-size: .875rem;--cc-sf-form-desc-color: #6B7280;--cc-sf-section-bg: transparent;--cc-sf-section-border: none;--cc-sf-section-radius: 10px;--cc-sf-section-padding: 18px;--cc-sf-section-shadow: none;--cc-sf-section-gap: 20px;--cc-sf-section-label-size: 1rem;--cc-sf-section-label-weight: 600;--cc-sf-section-label-color: #1F2937;--cc-sf-section-label-border: 2px solid #E5E7EB;--cc-sf-section-header-accent-width: 4px;--cc-sf-section-header-accent-color: #3B82F6;--cc-sf-section-header-bg: #F9FAFB;--cc-sf-repeater-accordion-header-bg: #F9FAFB;--cc-sf-repeater-accordion-header-color: #1F2937;--cc-sf-repeater-accordion-active-bg: #EFF6FF;--cc-sf-repeater-badge-bg: #E5E7EB;--cc-sf-repeater-badge-color: #374151;--cc-sf-section-border-radius-inner: 8px;--cc-sf-instance-bg: #F9FAFB;--cc-sf-instance-border: 1px solid #E5E7EB;--cc-sf-instance-radius: 8px;--cc-sf-instance-padding: 16px;--cc-sf-instance-divider: 1px dashed #D1D5DB;--cc-sf-instance-num-color:#4B5563;--cc-sf-instance-num-size: .8125rem;--cc-sf-grid-gap: 18px;--cc-sf-field-gap: 6px;--cc-sf-focus-ring-width: 3px;--cc-sf-focus-ring-offset: 0;--cc-sf-label-size: .8125rem;--cc-sf-label-weight: 600;--cc-sf-label-color: #111827;--cc-sf-label-required-color: #DC2626;--cc-sf-input-bg: #ffffff;--cc-sf-input-color: #111827;--cc-sf-input-placeholder: #9CA3AF;--cc-sf-input-border: 1.5px solid #D1D5DB;--cc-sf-input-radius: 8px;--cc-sf-input-padding: .625rem .875rem;--cc-sf-input-font-size: .875rem;--cc-sf-input-shadow: none;--cc-sf-input-hover-border:#9CA3AF;--cc-sf-input-focus-border:#3B82F6;--cc-sf-input-focus-shadow:0 0 0 3px rgba(59, 130, 246, .12);--cc-sf-input-transition: all .2s ease;--cc-sf-input-disabled-bg: #F3F4F6;--cc-sf-input-disabled-color: #6B7280;--cc-sf-input-disabled-border:#E5E7EB;--cc-sf-error-border: #DC2626;--cc-sf-error-bg: #FEF2F2;--cc-sf-error-focus-shadow: 0 0 0 3px rgba(220, 38, 38, .1);--cc-sf-error-text-color: #DC2626;--cc-sf-error-text-size: .75rem;--cc-sf-hint-color: #6B7280;--cc-sf-hint-size: .75rem;--cc-sf-chip-border: 1px solid #D1D5DB;--cc-sf-chip-radius: 20px;--cc-sf-chip-padding: 6px 14px;--cc-sf-chip-bg: #ffffff;--cc-sf-chip-color: #374151;--cc-sf-chip-hover-bg: #F3F4F6;--cc-sf-chip-selected-bg: #3B82F6;--cc-sf-chip-selected-color: #ffffff;--cc-sf-chip-selected-border:#3B82F6;--cc-sf-switch-track-off: #D1D5DB;--cc-sf-switch-track-on: #3B82F6;--cc-sf-switch-thumb: #ffffff;--cc-sf-star-empty: #D1D5DB;--cc-sf-star-filled: #F59E0B;--cc-sf-star-size: 28px;--cc-sf-generated-bg: #F3F4F6;--cc-sf-generated-border: 1px solid #E5E7EB;--cc-sf-generated-color: #6B7280;--cc-sf-generated-radius: 8px;--cc-sf-generated-padding: .625rem .875rem;--cc-sf-dropzone-bg: #F8FAFC;--cc-sf-dropzone-border: 1.5px dashed #CBD5E1;--cc-sf-dropzone-radius: 12px;--cc-sf-dropzone-hover-bg: #EFF6FF;--cc-sf-dropzone-hover-border: #93C5FD;--cc-sf-dropzone-over-border: #3B82F6;--cc-sf-dropzone-over-shadow: 0 0 0 4px rgba(59, 130, 246, .12);--cc-sf-dropzone-icon-color: #94A3B8;--cc-sf-dropzone-icon-bg: rgba(59, 130, 246, .1);--cc-sf-dropzone-link-color: #3B82F6;--cc-sf-dropzone-hint-color: #64748B;--cc-sf-uploaded-item-bg: #ffffff;--cc-sf-uploaded-item-border: 1px solid #E2E8F0;--cc-sf-uploaded-item-radius: 8px;--cc-sf-uploaded-remove-color: #94A3B8;--cc-sf-uploaded-remove-hover-bg: #FEF2F2;--cc-sf-uploaded-remove-hover-color: #DC2626;--cc-sf-btn-add-bg: transparent;--cc-sf-btn-add-color: #3B82F6;--cc-sf-btn-add-border: 1px dashed #CBD5E1;--cc-sf-btn-add-radius: 6px;--cc-sf-btn-add-hover-bg: #EFF6FF;--cc-sf-btn-add-hover-border: #BFDBFE;--cc-sf-btn-remove-bg: #FFF5F5;--cc-sf-btn-remove-color: #E53E3E;--cc-sf-btn-remove-border: 1px solid #FED7D7;--cc-sf-btn-remove-radius: 4px;--cc-sf-btn-remove-hover-bg: #FED7D7;--cc-sf-btn-remove-padding: .375rem .75rem;--cc-sf-btn-remove-font-size: .875rem;--cc-sf-btn-remove-font-weight: 500;--cc-sf-btn-add-padding: .625rem 1.5rem;--cc-sf-btn-add-font-size: .875rem;--cc-sf-btn-add-font-weight: 600;--cc-sf-stepper-connector-display: block;--cc-sf-instance-shadow: none;--cc-sf-instance-hover-shadow: none;--cc-sf-instance-transition: none;--cc-sf-instance-header-divider: none;--cc-sf-instance-header-pb: 0px;--cc-sf-instance-header-mb: 0px;--cc-sf-instance-num-weight: 600;--cc-sf-section-stepper-accent: #6366F1;--cc-sf-section-stepper-accent-end: #8B5CF6;--cc-sf-section-stepper-active-label: #4F46E5;--cc-sf-section-stepper-active-glow: rgba(99, 102, 241, .25);--cc-sf-step-connector-color: #E5E7EB;--cc-sf-step-connector-done: #22C55E;--cc-sf-step-number-bg: #E5E7EB;--cc-sf-step-number-color: #6B7280;--cc-sf-step-number-size: 40px;--cc-sf-step-number-font-size: .875rem;--cc-sf-step-number-weight: 600;--cc-sf-step-label-size: .875rem;--cc-sf-step-label-color: #6B7280;--cc-sf-step-label-weight: 500;--cc-sf-step-active-bg: #3B82F6;--cc-sf-step-active-color: #ffffff;--cc-sf-step-active-label: #1D4ED8;--cc-sf-step-active-label-weight: 700;--cc-sf-step-done-bg: #22C55E;--cc-sf-step-done-color: #ffffff;--cc-sf-actions-border: 1px solid #E5E7EB;--cc-sf-actions-padding: 20px 10px 0;--cc-sf-actions-gap: 12px;--cc-sf-btn-primary-bg: #3B82F6;--cc-sf-btn-primary-color: #ffffff;--cc-sf-btn-primary-radius: 8px;--cc-sf-btn-primary-padding: .625rem 1.5rem;--cc-sf-btn-primary-hover-bg: #2563EB;--cc-sf-btn-secondary-bg: #F3F4F6;--cc-sf-btn-secondary-color: #374151;--cc-sf-btn-secondary-radius: 8px;--cc-sf-btn-secondary-padding: .625rem 1.5rem;--cc-sf-btn-secondary-hover-bg: #E5E7EB;--cc-sf-btn-font-size: .875rem;--cc-sf-btn-font-weight: 600;--cc-sf-btn-transition: all .2s ease;--cc-sf-btn-disabled-opacity: .55}.fb-config-panel ::ng-deep .stepper-nav .stepper-step:not(:last-child):after{display:var(--cc-sf-stepper-connector-display)!important}.fb-config-panel ::ng-deep .section-instance{background:var(--cc-sf-instance-bg)!important;border:var(--cc-sf-instance-border)!important;border-radius:var(--cc-sf-instance-radius)!important;box-shadow:var(--cc-sf-instance-shadow);transition:var(--cc-sf-instance-transition)}.fb-config-panel ::ng-deep .section-instance:hover{box-shadow:var(--cc-sf-instance-hover-shadow)}.fb-config-panel ::ng-deep .section-instance-header{display:flex;align-items:center;justify-content:space-between;padding-bottom:var(--cc-sf-instance-header-pb);margin-bottom:var(--cc-sf-instance-header-mb);border-bottom:var(--cc-sf-instance-header-divider)}.fb-config-panel ::ng-deep .section-instance-num{font-size:var(--cc-sf-instance-num-size);font-weight:var(--cc-sf-instance-num-weight);color:var(--cc-sf-instance-num-color)}.fb-config-panel ::ng-deep .btn-instance-remove{display:inline-flex;align-items:center;gap:6px;padding:var(--cc-sf-btn-remove-padding);border-radius:var(--cc-sf-btn-remove-radius);border:var(--cc-sf-btn-remove-border)!important;background:var(--cc-sf-btn-remove-bg);color:var(--cc-sf-btn-remove-color)!important;font-size:var(--cc-sf-btn-remove-font-size);font-weight:var(--cc-sf-btn-remove-font-weight);cursor:pointer;transition:all .15s ease}.fb-config-panel ::ng-deep .btn-instance-remove:hover{background:var(--cc-sf-btn-remove-hover-bg)}.fb-config-panel ::ng-deep .btn-add-section{display:inline-flex;align-items:center;gap:8px;padding:var(--cc-sf-btn-add-padding);border-radius:var(--cc-sf-btn-add-radius);border:var(--cc-sf-btn-add-border)!important;background:var(--cc-sf-btn-add-bg);color:var(--cc-sf-btn-add-color);font-size:var(--cc-sf-btn-add-font-size);font-weight:var(--cc-sf-btn-add-font-weight);cursor:pointer;transition:all .18s ease}.fb-config-panel ::ng-deep .btn-add-section:hover{background:var(--cc-sf-btn-add-hover-bg);border-color:var(--cc-sf-btn-add-hover-border)!important}.fb-config-panel ::ng-deep .section-stepper-nav:before{background:linear-gradient(90deg,var(--cc-sf-section-stepper-accent) 0%,var(--cc-sf-section-stepper-accent-end) 100%)!important}.fb-config-panel ::ng-deep .section-stepper-step.ss-active .ss-badge{background:linear-gradient(135deg,var(--cc-sf-section-stepper-accent) 0%,var(--cc-sf-section-stepper-accent-end) 100%)!important;box-shadow:0 0 0 4px var(--cc-sf-section-stepper-active-glow),0 4px 12px var(--cc-sf-section-stepper-active-glow)!important}.fb-config-panel ::ng-deep .section-stepper-step.ss-active .ss-label{color:var(--cc-sf-section-stepper-active-label)!important}.fb-config-panel ::ng-deep .section-stepper-step:not(.ss-active):not(.ss-completed):not(.ss-warning):hover .ss-badge{color:var(--cc-sf-section-stepper-accent)!important;border-color:var(--cc-sf-section-stepper-accent-end)!important}.fb-config-panel ::ng-deep .section-stepper-step .ss-connector:after{background:linear-gradient(90deg,var(--cc-sf-section-stepper-accent),var(--cc-sf-section-stepper-accent-end))!important}}@media(min-width:1920px){.fb-config-panel{--cc-sf-font-family: Inter, Segoe UI, sans-serif;--cc-sf-font-size-base: .875rem;--cc-sf-accent-color: #3B82F6;--cc-sf-selected-color: #3B82F6;--cc-sf-form-bg: transparent;--cc-sf-form-padding: 0;--cc-sf-form-border-radius: 12px;--cc-sf-form-border: none;--cc-sf-form-shadow: none;--cc-sf-form-max-width: 1200px;--cc-sf-form-section-gap: 24px;--cc-sf-form-title-size: 1.5rem;--cc-sf-form-title-weight: 700;--cc-sf-form-title-color: #111827;--cc-sf-form-desc-size: .875rem;--cc-sf-form-desc-color: #6B7280;--cc-sf-section-bg: transparent;--cc-sf-section-border: none;--cc-sf-section-radius: 10px;--cc-sf-section-padding: 18px;--cc-sf-section-shadow: none;--cc-sf-section-gap: 20px;--cc-sf-section-label-size: 1rem;--cc-sf-section-label-weight: 600;--cc-sf-section-label-color: #1F2937;--cc-sf-section-label-border: 2px solid #E5E7EB;--cc-sf-section-header-accent-width: 4px;--cc-sf-section-header-accent-color: #3B82F6;--cc-sf-section-header-bg: #F9FAFB;--cc-sf-repeater-accordion-header-bg: #F9FAFB;--cc-sf-repeater-accordion-header-color: #1F2937;--cc-sf-repeater-accordion-active-bg: #EFF6FF;--cc-sf-repeater-badge-bg: #E5E7EB;--cc-sf-repeater-badge-color: #374151;--cc-sf-section-border-radius-inner: 8px;--cc-sf-instance-bg: #F9FAFB;--cc-sf-instance-border: 1px solid #E5E7EB;--cc-sf-instance-radius: 8px;--cc-sf-instance-padding: 16px;--cc-sf-instance-divider: 1px dashed #D1D5DB;--cc-sf-instance-num-color:#4B5563;--cc-sf-instance-num-size: .8125rem;--cc-sf-grid-gap: 18px;--cc-sf-field-gap: 6px;--cc-sf-focus-ring-width: 3px;--cc-sf-focus-ring-offset: 0;--cc-sf-label-size: .8125rem;--cc-sf-label-weight: 600;--cc-sf-label-color: #111827;--cc-sf-label-required-color: #DC2626;--cc-sf-input-bg: #ffffff;--cc-sf-input-color: #111827;--cc-sf-input-placeholder: #9CA3AF;--cc-sf-input-border: 1.5px solid #D1D5DB;--cc-sf-input-radius: 8px;--cc-sf-input-padding: .625rem .875rem;--cc-sf-input-font-size: .875rem;--cc-sf-input-shadow: none;--cc-sf-input-hover-border:#9CA3AF;--cc-sf-input-focus-border:#3B82F6;--cc-sf-input-focus-shadow:0 0 0 3px rgba(59, 130, 246, .12);--cc-sf-input-transition: all .2s ease;--cc-sf-input-disabled-bg: #F3F4F6;--cc-sf-input-disabled-color: #6B7280;--cc-sf-input-disabled-border:#E5E7EB;--cc-sf-error-border: #DC2626;--cc-sf-error-bg: #FEF2F2;--cc-sf-error-focus-shadow: 0 0 0 3px rgba(220, 38, 38, .1);--cc-sf-error-text-color: #DC2626;--cc-sf-error-text-size: .75rem;--cc-sf-hint-color: #6B7280;--cc-sf-hint-size: .75rem;--cc-sf-chip-border: 1px solid #D1D5DB;--cc-sf-chip-radius: 20px;--cc-sf-chip-padding: 6px 14px;--cc-sf-chip-bg: #ffffff;--cc-sf-chip-color: #374151;--cc-sf-chip-hover-bg: #F3F4F6;--cc-sf-chip-selected-bg: #3B82F6;--cc-sf-chip-selected-color: #ffffff;--cc-sf-chip-selected-border:#3B82F6;--cc-sf-switch-track-off: #D1D5DB;--cc-sf-switch-track-on: #3B82F6;--cc-sf-switch-thumb: #ffffff;--cc-sf-star-empty: #D1D5DB;--cc-sf-star-filled: #F59E0B;--cc-sf-star-size: 28px;--cc-sf-generated-bg: #F3F4F6;--cc-sf-generated-border: 1px solid #E5E7EB;--cc-sf-generated-color: #6B7280;--cc-sf-generated-radius: 8px;--cc-sf-generated-padding: .625rem .875rem;--cc-sf-dropzone-bg: #F8FAFC;--cc-sf-dropzone-border: 1.5px dashed #CBD5E1;--cc-sf-dropzone-radius: 12px;--cc-sf-dropzone-hover-bg: #EFF6FF;--cc-sf-dropzone-hover-border: #93C5FD;--cc-sf-dropzone-over-border: #3B82F6;--cc-sf-dropzone-over-shadow: 0 0 0 4px rgba(59, 130, 246, .12);--cc-sf-dropzone-icon-color: #94A3B8;--cc-sf-dropzone-icon-bg: rgba(59, 130, 246, .1);--cc-sf-dropzone-link-color: #3B82F6;--cc-sf-dropzone-hint-color: #64748B;--cc-sf-uploaded-item-bg: #ffffff;--cc-sf-uploaded-item-border: 1px solid #E2E8F0;--cc-sf-uploaded-item-radius: 8px;--cc-sf-uploaded-remove-color: #94A3B8;--cc-sf-uploaded-remove-hover-bg: #FEF2F2;--cc-sf-uploaded-remove-hover-color: #DC2626;--cc-sf-btn-add-bg: transparent;--cc-sf-btn-add-color: #3B82F6;--cc-sf-btn-add-border: 1px dashed #CBD5E1;--cc-sf-btn-add-radius: 6px;--cc-sf-btn-add-hover-bg: #EFF6FF;--cc-sf-btn-add-hover-border: #BFDBFE;--cc-sf-btn-remove-bg: #FFF5F5;--cc-sf-btn-remove-color: #E53E3E;--cc-sf-btn-remove-border: 1px solid #FED7D7;--cc-sf-btn-remove-radius: 4px;--cc-sf-btn-remove-hover-bg: #FED7D7;--cc-sf-btn-remove-padding: .375rem .75rem;--cc-sf-btn-remove-font-size: .875rem;--cc-sf-btn-remove-font-weight: 500;--cc-sf-btn-add-padding: .625rem 1.5rem;--cc-sf-btn-add-font-size: .875rem;--cc-sf-btn-add-font-weight: 600;--cc-sf-stepper-connector-display: block;--cc-sf-instance-shadow: none;--cc-sf-instance-hover-shadow: none;--cc-sf-instance-transition: none;--cc-sf-instance-header-divider: none;--cc-sf-instance-header-pb: 0px;--cc-sf-instance-header-mb: 0px;--cc-sf-instance-num-weight: 600;--cc-sf-section-stepper-accent: #6366F1;--cc-sf-section-stepper-accent-end: #8B5CF6;--cc-sf-section-stepper-active-label: #4F46E5;--cc-sf-section-stepper-active-glow: rgba(99, 102, 241, .25);--cc-sf-step-connector-color: #E5E7EB;--cc-sf-step-connector-done: #22C55E;--cc-sf-step-number-bg: #E5E7EB;--cc-sf-step-number-color: #6B7280;--cc-sf-step-number-size: 40px;--cc-sf-step-number-font-size: .875rem;--cc-sf-step-number-weight: 600;--cc-sf-step-label-size: .875rem;--cc-sf-step-label-color: #6B7280;--cc-sf-step-label-weight: 500;--cc-sf-step-active-bg: #3B82F6;--cc-sf-step-active-color: #ffffff;--cc-sf-step-active-label: #1D4ED8;--cc-sf-step-active-label-weight: 700;--cc-sf-step-done-bg: #22C55E;--cc-sf-step-done-color: #ffffff;--cc-sf-actions-border: 1px solid #E5E7EB;--cc-sf-actions-padding: 20px 10px 0;--cc-sf-actions-gap: 12px;--cc-sf-btn-primary-bg: #3B82F6;--cc-sf-btn-primary-color: #ffffff;--cc-sf-btn-primary-radius: 8px;--cc-sf-btn-primary-padding: .625rem 1.5rem;--cc-sf-btn-primary-hover-bg: #2563EB;--cc-sf-btn-secondary-bg: #F3F4F6;--cc-sf-btn-secondary-color: #374151;--cc-sf-btn-secondary-radius: 8px;--cc-sf-btn-secondary-padding: .625rem 1.5rem;--cc-sf-btn-secondary-hover-bg: #E5E7EB;--cc-sf-btn-font-size: .875rem;--cc-sf-btn-font-weight: 600;--cc-sf-btn-transition: all .2s ease;--cc-sf-btn-disabled-opacity: .55}.fb-config-panel ::ng-deep .stepper-nav .stepper-step:not(:last-child):after{display:var(--cc-sf-stepper-connector-display)!important}.fb-config-panel ::ng-deep .section-instance{background:var(--cc-sf-instance-bg)!important;border:var(--cc-sf-instance-border)!important;border-radius:var(--cc-sf-instance-radius)!important;box-shadow:var(--cc-sf-instance-shadow);transition:var(--cc-sf-instance-transition)}.fb-config-panel ::ng-deep .section-instance:hover{box-shadow:var(--cc-sf-instance-hover-shadow)}.fb-config-panel ::ng-deep .section-instance-header{display:flex;align-items:center;justify-content:space-between;padding-bottom:var(--cc-sf-instance-header-pb);margin-bottom:var(--cc-sf-instance-header-mb);border-bottom:var(--cc-sf-instance-header-divider)}.fb-config-panel ::ng-deep .section-instance-num{font-size:var(--cc-sf-instance-num-size);font-weight:var(--cc-sf-instance-num-weight);color:var(--cc-sf-instance-num-color)}.fb-config-panel ::ng-deep .btn-instance-remove{display:inline-flex;align-items:center;gap:6px;padding:var(--cc-sf-btn-remove-padding);border-radius:var(--cc-sf-btn-remove-radius);border:var(--cc-sf-btn-remove-border)!important;background:var(--cc-sf-btn-remove-bg);color:var(--cc-sf-btn-remove-color)!important;font-size:var(--cc-sf-btn-remove-font-size);font-weight:var(--cc-sf-btn-remove-font-weight);cursor:pointer;transition:all .15s ease}.fb-config-panel ::ng-deep .btn-instance-remove:hover{background:var(--cc-sf-btn-remove-hover-bg)}.fb-config-panel ::ng-deep .btn-add-section{display:inline-flex;align-items:center;gap:8px;padding:var(--cc-sf-btn-add-padding);border-radius:var(--cc-sf-btn-add-radius);border:var(--cc-sf-btn-add-border)!important;background:var(--cc-sf-btn-add-bg);color:var(--cc-sf-btn-add-color);font-size:var(--cc-sf-btn-add-font-size);font-weight:var(--cc-sf-btn-add-font-weight);cursor:pointer;transition:all .18s ease}.fb-config-panel ::ng-deep .btn-add-section:hover{background:var(--cc-sf-btn-add-hover-bg);border-color:var(--cc-sf-btn-add-hover-border)!important}.fb-config-panel ::ng-deep .section-stepper-nav:before{background:linear-gradient(90deg,var(--cc-sf-section-stepper-accent) 0%,var(--cc-sf-section-stepper-accent-end) 100%)!important}.fb-config-panel ::ng-deep .section-stepper-step.ss-active .ss-badge{background:linear-gradient(135deg,var(--cc-sf-section-stepper-accent) 0%,var(--cc-sf-section-stepper-accent-end) 100%)!important;box-shadow:0 0 0 4px var(--cc-sf-section-stepper-active-glow),0 4px 12px var(--cc-sf-section-stepper-active-glow)!important}.fb-config-panel ::ng-deep .section-stepper-step.ss-active .ss-label{color:var(--cc-sf-section-stepper-active-label)!important}.fb-config-panel ::ng-deep .section-stepper-step:not(.ss-active):not(.ss-completed):not(.ss-warning):hover .ss-badge{color:var(--cc-sf-section-stepper-accent)!important;border-color:var(--cc-sf-section-stepper-accent-end)!important}.fb-config-panel ::ng-deep .section-stepper-step .ss-connector:after{background:linear-gradient(90deg,var(--cc-sf-section-stepper-accent),var(--cc-sf-section-stepper-accent-end))!important}}.fb-config-panel__empty{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--fb-fc-empty-color, #9ca3af);text-align:center;padding:48px 24px}@media(max-width:640px){.fb-config-panel__empty{padding:24px 16px}}.fb-config-panel__empty-icon{margin-bottom:24px;opacity:.4;color:var(--fb-fc-accent, #3b82f6)}@media(max-width:640px){.fb-config-panel__empty-icon{margin-bottom:16px}}.fb-config-panel__empty-text{font-size:15px;max-width:320px;line-height:1.6;font-weight:500}@media(max-width:640px){.fb-config-panel__empty-text{font-size:14px;max-width:100%}}.fb-config-panel__header{background:var(--fb-fc-header-bg, #ffffff);padding:1.4rem;border-bottom:1px solid var(--fb-fc-border, #e5e7eb);display:flex;align-items:center;justify-content:space-between;flex-shrink:0;gap:16px;flex-wrap:wrap}@media(max-width:768px){.fb-config-panel__header{padding:16px 24px;gap:12px}}@media(max-width:640px){.fb-config-panel__header{padding:12px 16px;gap:8px}}.fb-config-panel__header-left{display:flex;align-items:center;gap:12px;min-width:0;flex-wrap:wrap}@media(max-width:640px){.fb-config-panel__header-left{gap:8px}}.fb-config-panel__type-switcher{display:flex;align-items:center;gap:8px;flex-shrink:0}@media(max-width:640px){.fb-config-panel__type-switcher{gap:4px}}.fb-config-panel__type-label{font-size:var(--fb-fc-type-label-size, .75rem);font-weight:600;color:var(--fb-fc-type-label-color, #6b7280);white-space:nowrap}@media(max-width:640px){.fb-config-panel__type-label{font-size:.7rem}}.fb-config-panel__type-select{height:var(--fb-fc-type-select-height, 36px);padding:0 32px 0 10px;border:1px solid var(--fb-fc-type-select-border, #d1d5db);border-radius:var(--fb-fc-type-select-radius, 8px);background-color:var(--fb-fc-type-select-bg, #ffffff);color:var(--fb-fc-type-select-color, #111827);font-size:.8125rem;font-weight:500;cursor:pointer;appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E\");background-repeat:no-repeat;background-position:right 8px center;min-width:160px;transition:border-color .15s ease,box-shadow .15s ease}@media(max-width:640px){.fb-config-panel__type-select{height:32px;font-size:.75rem;min-width:140px;padding:0 28px 0 8px}}.fb-config-panel__type-select:hover{border-color:var(--fb-fc-type-select-hover-border, #9ca3af)}.fb-config-panel__type-select:focus{outline:none;border-color:var(--fb-fc-accent, #3b82f6);box-shadow:0 0 0 3px #3b82f61f}.fb-config-panel__title{margin:0;font-size:1.25rem;font-weight:700;color:var(--fb-fc-title-color, #111827)}@media(max-width:1024px){.fb-config-panel__title{font-size:1.125rem}}@media(max-width:640px){.fb-config-panel__title{font-size:1rem}}.fb-config-panel__badge{padding:4px 12px;background:var(--fb-fc-badge-bg, #eff6ff);color:var(--fb-fc-badge-color, #3b82f6);border-radius:20px;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;border:1px solid var(--fb-fc-badge-border, #dbeafe)}@media(max-width:640px){.fb-config-panel__badge{padding:3px 8px;font-size:9px}}.fb-config-panel__form-wrapper{flex:1;overflow-y:auto;overflow-x:hidden;width:100%;min-width:0;display:flex;flex-direction:column;align-items:stretch;box-sizing:border-box;padding:12px 16px}@media(min-width:600px){.fb-config-panel__form-wrapper{padding:16px 20px}}@media(min-width:768px){.fb-config-panel__form-wrapper{padding:20px 24px}}@media(min-width:1024px){.fb-config-panel__form-wrapper{padding:1rem}}@media(min-width:1440px){.fb-config-panel__form-wrapper{padding:28px 40px}}@media(min-width:1920px){.fb-config-panel__form-wrapper{padding:32px 48px}}.fb-config-panel__form-wrapper .fb-config-card{width:100%;max-width:none;margin:0;background:transparent;border:none;box-shadow:none;display:flex;flex-direction:column;min-width:0;flex:1;box-sizing:border-box}.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .sf-col{min-width:0}.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal{display:grid;grid-auto-flow:dense;gap:12px;width:100%;align-items:start}@media(max-width:599px){.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal{grid-template-columns:1fr;gap:12px}}@media(min-width:600px)and (max-width:767px){.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal{grid-template-columns:1fr;gap:14px}}@media(min-width:768px)and (max-width:1023px){.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal{grid-template-columns:repeat(2,1fr);gap:14px}}@media(min-width:1024px)and (max-width:1439px){.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal{grid-template-columns:repeat(2,1fr);gap:16px}}@media(min-width:1440px){.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal{grid-template-columns:repeat(3,1fr);gap:18px}}@media(min-width:1920px){.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal{grid-template-columns:repeat(3,1fr);gap:20px}}.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal>*{display:flex;flex-direction:column;min-width:0;overflow:hidden}.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal>* ::ng-deep .form-field-wrapper,.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal>* ::ng-deep .field-wrapper,.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal>* ::ng-deep input,.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal>* ::ng-deep select,.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal>* ::ng-deep textarea{width:100%;min-width:0}.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal .sf-switch-wrapper{display:flex;flex-direction:row;align-items:center;gap:8px;white-space:nowrap;min-width:0}@media(max-width:768px){.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal .sf-switch-wrapper{gap:6px}}.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal .sf-switch-wrapper .field-label{font-size:.75rem;font-weight:600;margin:0;flex-shrink:1;min-width:0;word-break:keep-all}@media(max-width:768px){.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal .sf-switch-wrapper .field-label{font-size:.7rem}}@media(max-width:640px){.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal .sf-switch-wrapper .field-label{font-size:.65rem}}.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal .sf-switch-wrapper .switch{flex-shrink:0}.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .grid-row .sf-col .sf-switch-wrapper{display:flex;flex-direction:row;align-items:center;gap:8px;white-space:nowrap;min-width:0}@media(max-width:768px){.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .grid-row .sf-col .sf-switch-wrapper{gap:6px}}.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .grid-row .sf-col .sf-switch-wrapper .field-label{font-size:.75rem;font-weight:600;margin:0;flex-shrink:1;min-width:0;word-break:keep-all}@media(max-width:768px){.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .grid-row .sf-col .sf-switch-wrapper .field-label{font-size:.7rem}}@media(max-width:640px){.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .grid-row .sf-col .sf-switch-wrapper .field-label{font-size:.65rem}}.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .grid-row .sf-col .sf-switch-wrapper .switch{flex-shrink:0}.fb-config-panel__error{padding:24px;background:var(--fb-fc-error-bg, #fef2f2);color:var(--fb-fc-error-color, #dc2626);border:1px solid var(--fb-fc-error-border, #fee2e2);border-radius:8px;font-size:14px;font-weight:500}@media(max-width:768px){.fb-config-panel__error{padding:16px;font-size:13px}}@media(max-width:640px){.fb-config-panel__error{padding:12px;font-size:12px}}\n"], dependencies: [{ kind: "component", type: SmartFormComponent, selector: "lib-smart-form", inputs: ["formJson", "initialValues", "enableDraftAutoSave", "labels", "mode", "readOnly"], outputs: ["submit", "draftSave", "actionClick", "valueChange", "fileAdded", "fileUploadFinished", "fileRemoved", "stepChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
8025
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.3.15", type: ConfiguratorConfigPanelComponent, isStandalone: false, selector: "lib-configurator-config-panel", inputs: { selectedField: "selectedField", selectedFieldInfo: "selectedFieldInfo", builderFieldType: "builderFieldType", fieldTypeSchemaMap: "fieldTypeSchemaMap", showOptionConfig: "showOptionConfig", lockedFieldNames: "lockedFieldNames" }, outputs: { configChange: "configChange", typeChange: "typeChange" }, usesOnChanges: true, ngImport: i0, template: "<div class=\"fb-config-panel\">\n @if (!selectedField) {\n <div class=\"fb-config-panel__empty\">\n <div class=\"fb-config-panel__empty-icon\">\n <svg width=\"64\" height=\"64\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\"\n stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\">\n <rect x=\"3\" y=\"3\" width=\"18\" height=\"18\" rx=\"2\" ry=\"2\"/>\n <line x1=\"3\" y1=\"9\" x2=\"21\" y2=\"9\"/>\n <line x1=\"9\" y1=\"21\" x2=\"9\" y2=\"9\"/>\n </svg>\n </div>\n <p class=\"fb-config-panel__empty-text\">\n Select a field from the tree panel to edit its configuration.\n </p>\n </div>\n } @else {\n <div class=\"fb-config-panel__header\">\n <div class=\"fb-config-panel__header-left\">\n <h3 class=\"fb-config-panel__title\">\n {{ selectedField.label || selectedField.name || 'Field Settings' }}\n </h3>\n @if (selectedField.type) {\n <span class=\"fb-config-panel__badge\">{{ selectedField.type }}</span>\n }\n </div>\n\n <!-- Field Type Switcher: hidden for DROPDOWN / MULTI_SELECT fields -->\n @if (!isDropdownType) {\n <div class=\"fb-config-panel__type-switcher\">\n <label class=\"fb-config-panel__type-label\" for=\"fb-field-type-select\">Field Type</label>\n <select\n id=\"fb-field-type-select\"\n class=\"fb-config-panel__type-select\"\n [value]=\"currentBuilderType\"\n (change)=\"onTypeSelectChange($event)\"\n >\n @for (opt of switchableFieldTypes; track opt.value) {\n <option [value]=\"opt.value\">{{ opt.label }}</option>\n }\n </select>\n </div>\n }\n </div>\n\n <div class=\"fb-config-panel__form-wrapper\">\n @if (showConfigForm && configFormJson) {\n <div class=\"fb-config-card\">\n <lib-smart-form\n [formJson]=\"configFormJson\"\n [initialValues]=\"configInitialValues\"\n (actionClick)=\"onActionClick($event)\"\n />\n </div>\n } @else if (!showConfigForm) {\n <!-- intentionally empty \u2014 SmartForm is being recycled -->\n } @else {\n <div class=\"fb-config-panel__error\">\n No configuration schema available for this field type.\n </div>\n }\n </div>\n }\n</div>\n", styles: [".fb-config-panel{display:flex;flex-direction:column;height:100%;background:var(--fb-fc-panel-bg, #f9fafb);overflow:hidden;--cc-sf-font-family: Inter, Segoe UI, sans-serif;--cc-sf-font-size-base: .875rem;--cc-sf-accent-color: #3B82F6;--cc-sf-selected-color: #3B82F6;--cc-sf-form-bg: transparent;--cc-sf-form-padding: 0;--cc-sf-form-border-radius: 12px;--cc-sf-form-border: none;--cc-sf-form-shadow: none;--cc-sf-form-max-width: 1200px;--cc-sf-form-section-gap: 24px;--cc-sf-form-title-size: 1.5rem;--cc-sf-form-title-weight: 700;--cc-sf-form-title-color: #111827;--cc-sf-form-desc-size: .875rem;--cc-sf-form-desc-color: #6B7280;--cc-sf-section-bg: transparent;--cc-sf-section-border: none;--cc-sf-section-radius: 10px;--cc-sf-section-padding: 12px;--cc-sf-section-shadow: none;--cc-sf-section-gap: 20px;--cc-sf-section-label-size: 1rem;--cc-sf-section-label-weight: 600;--cc-sf-section-label-color: #1F2937;--cc-sf-section-label-border: 2px solid #E5E7EB;--cc-sf-section-header-accent-width: 4px;--cc-sf-section-header-accent-color: #3B82F6;--cc-sf-section-header-bg: #F9FAFB;--cc-sf-repeater-accordion-header-bg: #F9FAFB;--cc-sf-repeater-accordion-header-color: #1F2937;--cc-sf-repeater-accordion-active-bg: #EFF6FF;--cc-sf-repeater-badge-bg: #E5E7EB;--cc-sf-repeater-badge-color: #374151;--cc-sf-section-border-radius-inner: 8px;--cc-sf-instance-bg: #F9FAFB;--cc-sf-instance-border: 1px solid #E5E7EB;--cc-sf-instance-radius: 8px;--cc-sf-instance-padding: 16px;--cc-sf-instance-divider: 1px dashed #D1D5DB;--cc-sf-instance-num-color:#4B5563;--cc-sf-instance-num-size: .8125rem;--cc-sf-grid-gap: 12px;--cc-sf-field-gap: 6px;--cc-sf-focus-ring-width: 3px;--cc-sf-focus-ring-offset: 0;--cc-sf-label-size: .75rem;--cc-sf-label-weight: 600;--cc-sf-label-color: #111827;--cc-sf-label-required-color: #DC2626;--cc-sf-input-bg: #ffffff;--cc-sf-input-color: #111827;--cc-sf-input-placeholder: #9CA3AF;--cc-sf-input-border: 1.5px solid #D1D5DB;--cc-sf-input-radius: 8px;--cc-sf-input-padding: .5rem .75rem;--cc-sf-input-font-size: .875rem;--cc-sf-input-shadow: none;--cc-sf-input-hover-border:#9CA3AF;--cc-sf-input-focus-border:#3B82F6;--cc-sf-input-focus-shadow:0 0 0 3px rgba(59, 130, 246, .12);--cc-sf-input-transition: all .2s ease;--cc-sf-input-disabled-bg: #F3F4F6;--cc-sf-input-disabled-color: #6B7280;--cc-sf-input-disabled-border:#E5E7EB;--cc-sf-error-border: #DC2626;--cc-sf-error-bg: #FEF2F2;--cc-sf-error-focus-shadow: 0 0 0 3px rgba(220, 38, 38, .1);--cc-sf-error-text-color: #DC2626;--cc-sf-error-text-size: .75rem;--cc-sf-hint-color: #6B7280;--cc-sf-hint-size: .75rem;--cc-sf-chip-border: 1px solid #D1D5DB;--cc-sf-chip-radius: 20px;--cc-sf-chip-padding: 6px 14px;--cc-sf-chip-bg: #ffffff;--cc-sf-chip-color: #374151;--cc-sf-chip-hover-bg: #F3F4F6;--cc-sf-chip-selected-bg: #3B82F6;--cc-sf-chip-selected-color: #ffffff;--cc-sf-chip-selected-border:#3B82F6;--cc-sf-switch-track-off: #D1D5DB;--cc-sf-switch-track-on: #3B82F6;--cc-sf-switch-thumb: #ffffff;--cc-sf-star-empty: #D1D5DB;--cc-sf-star-filled: #F59E0B;--cc-sf-star-size: 28px;--cc-sf-generated-bg: #F3F4F6;--cc-sf-generated-border: 1px solid #E5E7EB;--cc-sf-generated-color: #6B7280;--cc-sf-generated-radius: 8px;--cc-sf-generated-padding: .625rem .875rem;--cc-sf-dropzone-bg: #F8FAFC;--cc-sf-dropzone-border: 1.5px dashed #CBD5E1;--cc-sf-dropzone-radius: 12px;--cc-sf-dropzone-hover-bg: #EFF6FF;--cc-sf-dropzone-hover-border: #93C5FD;--cc-sf-dropzone-over-border: #3B82F6;--cc-sf-dropzone-over-shadow: 0 0 0 4px rgba(59, 130, 246, .12);--cc-sf-dropzone-icon-color: #94A3B8;--cc-sf-dropzone-icon-bg: rgba(59, 130, 246, .1);--cc-sf-dropzone-link-color: #3B82F6;--cc-sf-dropzone-hint-color: #64748B;--cc-sf-uploaded-item-bg: #ffffff;--cc-sf-uploaded-item-border: 1px solid #E2E8F0;--cc-sf-uploaded-item-radius: 8px;--cc-sf-uploaded-remove-color: #94A3B8;--cc-sf-uploaded-remove-hover-bg: #FEF2F2;--cc-sf-uploaded-remove-hover-color: #DC2626;--cc-sf-btn-add-bg: transparent;--cc-sf-btn-add-color: #3B82F6;--cc-sf-btn-add-border: 1px dashed #CBD5E1;--cc-sf-btn-add-radius: 6px;--cc-sf-btn-add-hover-bg: #EFF6FF;--cc-sf-btn-add-hover-border: #BFDBFE;--cc-sf-btn-remove-bg: #FFF5F5;--cc-sf-btn-remove-color: #E53E3E;--cc-sf-btn-remove-border: 1px solid #FED7D7;--cc-sf-btn-remove-radius: 4px;--cc-sf-btn-remove-hover-bg: #FED7D7;--cc-sf-btn-remove-padding: .375rem .75rem;--cc-sf-btn-remove-font-size: .875rem;--cc-sf-btn-remove-font-weight: 500;--cc-sf-btn-add-padding: .625rem 1.5rem;--cc-sf-btn-add-font-size: .875rem;--cc-sf-btn-add-font-weight: 600;--cc-sf-stepper-connector-display: block;--cc-sf-instance-shadow: none;--cc-sf-instance-hover-shadow: none;--cc-sf-instance-transition: none;--cc-sf-instance-header-divider: none;--cc-sf-instance-header-pb: 0px;--cc-sf-instance-header-mb: 0px;--cc-sf-instance-num-weight: 600;--cc-sf-section-stepper-accent: #6366F1;--cc-sf-section-stepper-accent-end: #8B5CF6;--cc-sf-section-stepper-active-label: #4F46E5;--cc-sf-section-stepper-active-glow: rgba(99, 102, 241, .25);--cc-sf-step-connector-color: #E5E7EB;--cc-sf-step-connector-done: #22C55E;--cc-sf-step-number-bg: #E5E7EB;--cc-sf-step-number-color: #6B7280;--cc-sf-step-number-size: 40px;--cc-sf-step-number-font-size: .875rem;--cc-sf-step-number-weight: 600;--cc-sf-step-label-size: .875rem;--cc-sf-step-label-color: #6B7280;--cc-sf-step-label-weight: 500;--cc-sf-step-active-bg: #3B82F6;--cc-sf-step-active-color: #ffffff;--cc-sf-step-active-label: #1D4ED8;--cc-sf-step-active-label-weight: 700;--cc-sf-step-done-bg: #22C55E;--cc-sf-step-done-color: #ffffff;--cc-sf-actions-border: 1px solid #E5E7EB;--cc-sf-actions-padding: 20px 10px 0;--cc-sf-actions-gap: 12px;--cc-sf-btn-primary-bg: #3B82F6;--cc-sf-btn-primary-color: #ffffff;--cc-sf-btn-primary-radius: 8px;--cc-sf-btn-primary-padding: .625rem 1.5rem;--cc-sf-btn-primary-hover-bg: #2563EB;--cc-sf-btn-secondary-bg: #F3F4F6;--cc-sf-btn-secondary-color: #374151;--cc-sf-btn-secondary-radius: 8px;--cc-sf-btn-secondary-padding: .625rem 1.5rem;--cc-sf-btn-secondary-hover-bg: #E5E7EB;--cc-sf-btn-font-size: .875rem;--cc-sf-btn-font-weight: 600;--cc-sf-btn-transition: all .2s ease;--cc-sf-btn-disabled-opacity: .55}.fb-config-panel ::ng-deep .stepper-nav .stepper-step:not(:last-child):after{display:var(--cc-sf-stepper-connector-display)!important}.fb-config-panel ::ng-deep .section-instance{background:var(--cc-sf-instance-bg)!important;border:var(--cc-sf-instance-border)!important;border-radius:var(--cc-sf-instance-radius)!important;box-shadow:var(--cc-sf-instance-shadow);transition:var(--cc-sf-instance-transition)}.fb-config-panel ::ng-deep .section-instance:hover{box-shadow:var(--cc-sf-instance-hover-shadow)}.fb-config-panel ::ng-deep .section-instance-header{display:flex;align-items:center;justify-content:space-between;padding-bottom:var(--cc-sf-instance-header-pb);margin-bottom:var(--cc-sf-instance-header-mb);border-bottom:var(--cc-sf-instance-header-divider)}.fb-config-panel ::ng-deep .section-instance-num{font-size:var(--cc-sf-instance-num-size);font-weight:var(--cc-sf-instance-num-weight);color:var(--cc-sf-instance-num-color)}.fb-config-panel ::ng-deep .btn-instance-remove{display:inline-flex;align-items:center;gap:6px;padding:var(--cc-sf-btn-remove-padding);border-radius:var(--cc-sf-btn-remove-radius);border:var(--cc-sf-btn-remove-border)!important;background:var(--cc-sf-btn-remove-bg);color:var(--cc-sf-btn-remove-color)!important;font-size:var(--cc-sf-btn-remove-font-size);font-weight:var(--cc-sf-btn-remove-font-weight);cursor:pointer;transition:all .15s ease}.fb-config-panel ::ng-deep .btn-instance-remove:hover{background:var(--cc-sf-btn-remove-hover-bg)}.fb-config-panel ::ng-deep .btn-add-section{display:inline-flex;align-items:center;gap:8px;padding:var(--cc-sf-btn-add-padding);border-radius:var(--cc-sf-btn-add-radius);border:var(--cc-sf-btn-add-border)!important;background:var(--cc-sf-btn-add-bg);color:var(--cc-sf-btn-add-color);font-size:var(--cc-sf-btn-add-font-size);font-weight:var(--cc-sf-btn-add-font-weight);cursor:pointer;transition:all .18s ease}.fb-config-panel ::ng-deep .btn-add-section:hover{background:var(--cc-sf-btn-add-hover-bg);border-color:var(--cc-sf-btn-add-hover-border)!important}.fb-config-panel ::ng-deep .section-stepper-nav:before{background:linear-gradient(90deg,var(--cc-sf-section-stepper-accent) 0%,var(--cc-sf-section-stepper-accent-end) 100%)!important}.fb-config-panel ::ng-deep .section-stepper-step.ss-active .ss-badge{background:linear-gradient(135deg,var(--cc-sf-section-stepper-accent) 0%,var(--cc-sf-section-stepper-accent-end) 100%)!important;box-shadow:0 0 0 4px var(--cc-sf-section-stepper-active-glow),0 4px 12px var(--cc-sf-section-stepper-active-glow)!important}.fb-config-panel ::ng-deep .section-stepper-step.ss-active .ss-label{color:var(--cc-sf-section-stepper-active-label)!important}.fb-config-panel ::ng-deep .section-stepper-step:not(.ss-active):not(.ss-completed):not(.ss-warning):hover .ss-badge{color:var(--cc-sf-section-stepper-accent)!important;border-color:var(--cc-sf-section-stepper-accent-end)!important}.fb-config-panel ::ng-deep .section-stepper-step .ss-connector:after{background:linear-gradient(90deg,var(--cc-sf-section-stepper-accent),var(--cc-sf-section-stepper-accent-end))!important}@media(min-width:600px){.fb-config-panel{--cc-sf-font-family: Inter, Segoe UI, sans-serif;--cc-sf-font-size-base: .875rem;--cc-sf-accent-color: #3B82F6;--cc-sf-selected-color: #3B82F6;--cc-sf-form-bg: transparent;--cc-sf-form-padding: 0;--cc-sf-form-border-radius: 12px;--cc-sf-form-border: none;--cc-sf-form-shadow: none;--cc-sf-form-max-width: 1200px;--cc-sf-form-section-gap: 24px;--cc-sf-form-title-size: 1.5rem;--cc-sf-form-title-weight: 700;--cc-sf-form-title-color: #111827;--cc-sf-form-desc-size: .875rem;--cc-sf-form-desc-color: #6B7280;--cc-sf-section-bg: transparent;--cc-sf-section-border: none;--cc-sf-section-radius: 10px;--cc-sf-section-padding: 14px;--cc-sf-section-shadow: none;--cc-sf-section-gap: 20px;--cc-sf-section-label-size: 1rem;--cc-sf-section-label-weight: 600;--cc-sf-section-label-color: #1F2937;--cc-sf-section-label-border: 2px solid #E5E7EB;--cc-sf-section-header-accent-width: 4px;--cc-sf-section-header-accent-color: #3B82F6;--cc-sf-section-header-bg: #F9FAFB;--cc-sf-repeater-accordion-header-bg: #F9FAFB;--cc-sf-repeater-accordion-header-color: #1F2937;--cc-sf-repeater-accordion-active-bg: #EFF6FF;--cc-sf-repeater-badge-bg: #E5E7EB;--cc-sf-repeater-badge-color: #374151;--cc-sf-section-border-radius-inner: 8px;--cc-sf-instance-bg: #F9FAFB;--cc-sf-instance-border: 1px solid #E5E7EB;--cc-sf-instance-radius: 8px;--cc-sf-instance-padding: 16px;--cc-sf-instance-divider: 1px dashed #D1D5DB;--cc-sf-instance-num-color:#4B5563;--cc-sf-instance-num-size: .8125rem;--cc-sf-grid-gap: 14px;--cc-sf-field-gap: 6px;--cc-sf-focus-ring-width: 3px;--cc-sf-focus-ring-offset: 0;--cc-sf-label-size: .8rem;--cc-sf-label-weight: 600;--cc-sf-label-color: #111827;--cc-sf-label-required-color: #DC2626;--cc-sf-input-bg: #ffffff;--cc-sf-input-color: #111827;--cc-sf-input-placeholder: #9CA3AF;--cc-sf-input-border: 1.5px solid #D1D5DB;--cc-sf-input-radius: 8px;--cc-sf-input-padding: .5rem .75rem;--cc-sf-input-font-size: .875rem;--cc-sf-input-shadow: none;--cc-sf-input-hover-border:#9CA3AF;--cc-sf-input-focus-border:#3B82F6;--cc-sf-input-focus-shadow:0 0 0 3px rgba(59, 130, 246, .12);--cc-sf-input-transition: all .2s ease;--cc-sf-input-disabled-bg: #F3F4F6;--cc-sf-input-disabled-color: #6B7280;--cc-sf-input-disabled-border:#E5E7EB;--cc-sf-error-border: #DC2626;--cc-sf-error-bg: #FEF2F2;--cc-sf-error-focus-shadow: 0 0 0 3px rgba(220, 38, 38, .1);--cc-sf-error-text-color: #DC2626;--cc-sf-error-text-size: .75rem;--cc-sf-hint-color: #6B7280;--cc-sf-hint-size: .75rem;--cc-sf-chip-border: 1px solid #D1D5DB;--cc-sf-chip-radius: 20px;--cc-sf-chip-padding: 6px 14px;--cc-sf-chip-bg: #ffffff;--cc-sf-chip-color: #374151;--cc-sf-chip-hover-bg: #F3F4F6;--cc-sf-chip-selected-bg: #3B82F6;--cc-sf-chip-selected-color: #ffffff;--cc-sf-chip-selected-border:#3B82F6;--cc-sf-switch-track-off: #D1D5DB;--cc-sf-switch-track-on: #3B82F6;--cc-sf-switch-thumb: #ffffff;--cc-sf-star-empty: #D1D5DB;--cc-sf-star-filled: #F59E0B;--cc-sf-star-size: 28px;--cc-sf-generated-bg: #F3F4F6;--cc-sf-generated-border: 1px solid #E5E7EB;--cc-sf-generated-color: #6B7280;--cc-sf-generated-radius: 8px;--cc-sf-generated-padding: .625rem .875rem;--cc-sf-dropzone-bg: #F8FAFC;--cc-sf-dropzone-border: 1.5px dashed #CBD5E1;--cc-sf-dropzone-radius: 12px;--cc-sf-dropzone-hover-bg: #EFF6FF;--cc-sf-dropzone-hover-border: #93C5FD;--cc-sf-dropzone-over-border: #3B82F6;--cc-sf-dropzone-over-shadow: 0 0 0 4px rgba(59, 130, 246, .12);--cc-sf-dropzone-icon-color: #94A3B8;--cc-sf-dropzone-icon-bg: rgba(59, 130, 246, .1);--cc-sf-dropzone-link-color: #3B82F6;--cc-sf-dropzone-hint-color: #64748B;--cc-sf-uploaded-item-bg: #ffffff;--cc-sf-uploaded-item-border: 1px solid #E2E8F0;--cc-sf-uploaded-item-radius: 8px;--cc-sf-uploaded-remove-color: #94A3B8;--cc-sf-uploaded-remove-hover-bg: #FEF2F2;--cc-sf-uploaded-remove-hover-color: #DC2626;--cc-sf-btn-add-bg: transparent;--cc-sf-btn-add-color: #3B82F6;--cc-sf-btn-add-border: 1px dashed #CBD5E1;--cc-sf-btn-add-radius: 6px;--cc-sf-btn-add-hover-bg: #EFF6FF;--cc-sf-btn-add-hover-border: #BFDBFE;--cc-sf-btn-remove-bg: #FFF5F5;--cc-sf-btn-remove-color: #E53E3E;--cc-sf-btn-remove-border: 1px solid #FED7D7;--cc-sf-btn-remove-radius: 4px;--cc-sf-btn-remove-hover-bg: #FED7D7;--cc-sf-btn-remove-padding: .375rem .75rem;--cc-sf-btn-remove-font-size: .875rem;--cc-sf-btn-remove-font-weight: 500;--cc-sf-btn-add-padding: .625rem 1.5rem;--cc-sf-btn-add-font-size: .875rem;--cc-sf-btn-add-font-weight: 600;--cc-sf-stepper-connector-display: block;--cc-sf-instance-shadow: none;--cc-sf-instance-hover-shadow: none;--cc-sf-instance-transition: none;--cc-sf-instance-header-divider: none;--cc-sf-instance-header-pb: 0px;--cc-sf-instance-header-mb: 0px;--cc-sf-instance-num-weight: 600;--cc-sf-section-stepper-accent: #6366F1;--cc-sf-section-stepper-accent-end: #8B5CF6;--cc-sf-section-stepper-active-label: #4F46E5;--cc-sf-section-stepper-active-glow: rgba(99, 102, 241, .25);--cc-sf-step-connector-color: #E5E7EB;--cc-sf-step-connector-done: #22C55E;--cc-sf-step-number-bg: #E5E7EB;--cc-sf-step-number-color: #6B7280;--cc-sf-step-number-size: 40px;--cc-sf-step-number-font-size: .875rem;--cc-sf-step-number-weight: 600;--cc-sf-step-label-size: .875rem;--cc-sf-step-label-color: #6B7280;--cc-sf-step-label-weight: 500;--cc-sf-step-active-bg: #3B82F6;--cc-sf-step-active-color: #ffffff;--cc-sf-step-active-label: #1D4ED8;--cc-sf-step-active-label-weight: 700;--cc-sf-step-done-bg: #22C55E;--cc-sf-step-done-color: #ffffff;--cc-sf-actions-border: 1px solid #E5E7EB;--cc-sf-actions-padding: 20px 10px 0;--cc-sf-actions-gap: 12px;--cc-sf-btn-primary-bg: #3B82F6;--cc-sf-btn-primary-color: #ffffff;--cc-sf-btn-primary-radius: 8px;--cc-sf-btn-primary-padding: .625rem 1.5rem;--cc-sf-btn-primary-hover-bg: #2563EB;--cc-sf-btn-secondary-bg: #F3F4F6;--cc-sf-btn-secondary-color: #374151;--cc-sf-btn-secondary-radius: 8px;--cc-sf-btn-secondary-padding: .625rem 1.5rem;--cc-sf-btn-secondary-hover-bg: #E5E7EB;--cc-sf-btn-font-size: .875rem;--cc-sf-btn-font-weight: 600;--cc-sf-btn-transition: all .2s ease;--cc-sf-btn-disabled-opacity: .55}.fb-config-panel ::ng-deep .stepper-nav .stepper-step:not(:last-child):after{display:var(--cc-sf-stepper-connector-display)!important}.fb-config-panel ::ng-deep .section-instance{background:var(--cc-sf-instance-bg)!important;border:var(--cc-sf-instance-border)!important;border-radius:var(--cc-sf-instance-radius)!important;box-shadow:var(--cc-sf-instance-shadow);transition:var(--cc-sf-instance-transition)}.fb-config-panel ::ng-deep .section-instance:hover{box-shadow:var(--cc-sf-instance-hover-shadow)}.fb-config-panel ::ng-deep .section-instance-header{display:flex;align-items:center;justify-content:space-between;padding-bottom:var(--cc-sf-instance-header-pb);margin-bottom:var(--cc-sf-instance-header-mb);border-bottom:var(--cc-sf-instance-header-divider)}.fb-config-panel ::ng-deep .section-instance-num{font-size:var(--cc-sf-instance-num-size);font-weight:var(--cc-sf-instance-num-weight);color:var(--cc-sf-instance-num-color)}.fb-config-panel ::ng-deep .btn-instance-remove{display:inline-flex;align-items:center;gap:6px;padding:var(--cc-sf-btn-remove-padding);border-radius:var(--cc-sf-btn-remove-radius);border:var(--cc-sf-btn-remove-border)!important;background:var(--cc-sf-btn-remove-bg);color:var(--cc-sf-btn-remove-color)!important;font-size:var(--cc-sf-btn-remove-font-size);font-weight:var(--cc-sf-btn-remove-font-weight);cursor:pointer;transition:all .15s ease}.fb-config-panel ::ng-deep .btn-instance-remove:hover{background:var(--cc-sf-btn-remove-hover-bg)}.fb-config-panel ::ng-deep .btn-add-section{display:inline-flex;align-items:center;gap:8px;padding:var(--cc-sf-btn-add-padding);border-radius:var(--cc-sf-btn-add-radius);border:var(--cc-sf-btn-add-border)!important;background:var(--cc-sf-btn-add-bg);color:var(--cc-sf-btn-add-color);font-size:var(--cc-sf-btn-add-font-size);font-weight:var(--cc-sf-btn-add-font-weight);cursor:pointer;transition:all .18s ease}.fb-config-panel ::ng-deep .btn-add-section:hover{background:var(--cc-sf-btn-add-hover-bg);border-color:var(--cc-sf-btn-add-hover-border)!important}.fb-config-panel ::ng-deep .section-stepper-nav:before{background:linear-gradient(90deg,var(--cc-sf-section-stepper-accent) 0%,var(--cc-sf-section-stepper-accent-end) 100%)!important}.fb-config-panel ::ng-deep .section-stepper-step.ss-active .ss-badge{background:linear-gradient(135deg,var(--cc-sf-section-stepper-accent) 0%,var(--cc-sf-section-stepper-accent-end) 100%)!important;box-shadow:0 0 0 4px var(--cc-sf-section-stepper-active-glow),0 4px 12px var(--cc-sf-section-stepper-active-glow)!important}.fb-config-panel ::ng-deep .section-stepper-step.ss-active .ss-label{color:var(--cc-sf-section-stepper-active-label)!important}.fb-config-panel ::ng-deep .section-stepper-step:not(.ss-active):not(.ss-completed):not(.ss-warning):hover .ss-badge{color:var(--cc-sf-section-stepper-accent)!important;border-color:var(--cc-sf-section-stepper-accent-end)!important}.fb-config-panel ::ng-deep .section-stepper-step .ss-connector:after{background:linear-gradient(90deg,var(--cc-sf-section-stepper-accent),var(--cc-sf-section-stepper-accent-end))!important}}@media(min-width:768px){.fb-config-panel{--cc-sf-font-family: Inter, Segoe UI, sans-serif;--cc-sf-font-size-base: .875rem;--cc-sf-accent-color: #3B82F6;--cc-sf-selected-color: #3B82F6;--cc-sf-form-bg: transparent;--cc-sf-form-padding: 0;--cc-sf-form-border-radius: 12px;--cc-sf-form-border: none;--cc-sf-form-shadow: none;--cc-sf-form-max-width: 1200px;--cc-sf-form-section-gap: 24px;--cc-sf-form-title-size: 1.5rem;--cc-sf-form-title-weight: 700;--cc-sf-form-title-color: #111827;--cc-sf-form-desc-size: .875rem;--cc-sf-form-desc-color: #6B7280;--cc-sf-section-bg: transparent;--cc-sf-section-border: none;--cc-sf-section-radius: 10px;--cc-sf-section-padding: 16px;--cc-sf-section-shadow: none;--cc-sf-section-gap: 20px;--cc-sf-section-label-size: 1rem;--cc-sf-section-label-weight: 600;--cc-sf-section-label-color: #1F2937;--cc-sf-section-label-border: 2px solid #E5E7EB;--cc-sf-section-header-accent-width: 4px;--cc-sf-section-header-accent-color: #3B82F6;--cc-sf-section-header-bg: #F9FAFB;--cc-sf-repeater-accordion-header-bg: #F9FAFB;--cc-sf-repeater-accordion-header-color: #1F2937;--cc-sf-repeater-accordion-active-bg: #EFF6FF;--cc-sf-repeater-badge-bg: #E5E7EB;--cc-sf-repeater-badge-color: #374151;--cc-sf-section-border-radius-inner: 8px;--cc-sf-instance-bg: #F9FAFB;--cc-sf-instance-border: 1px solid #E5E7EB;--cc-sf-instance-radius: 8px;--cc-sf-instance-padding: 16px;--cc-sf-instance-divider: 1px dashed #D1D5DB;--cc-sf-instance-num-color:#4B5563;--cc-sf-instance-num-size: .8125rem;--cc-sf-grid-gap: 16px;--cc-sf-field-gap: 6px;--cc-sf-focus-ring-width: 3px;--cc-sf-focus-ring-offset: 0;--cc-sf-label-size: .8125rem;--cc-sf-label-weight: 600;--cc-sf-label-color: #111827;--cc-sf-label-required-color: #DC2626;--cc-sf-input-bg: #ffffff;--cc-sf-input-color: #111827;--cc-sf-input-placeholder: #9CA3AF;--cc-sf-input-border: 1.5px solid #D1D5DB;--cc-sf-input-radius: 8px;--cc-sf-input-padding: .625rem .875rem;--cc-sf-input-font-size: .875rem;--cc-sf-input-shadow: none;--cc-sf-input-hover-border:#9CA3AF;--cc-sf-input-focus-border:#3B82F6;--cc-sf-input-focus-shadow:0 0 0 3px rgba(59, 130, 246, .12);--cc-sf-input-transition: all .2s ease;--cc-sf-input-disabled-bg: #F3F4F6;--cc-sf-input-disabled-color: #6B7280;--cc-sf-input-disabled-border:#E5E7EB;--cc-sf-error-border: #DC2626;--cc-sf-error-bg: #FEF2F2;--cc-sf-error-focus-shadow: 0 0 0 3px rgba(220, 38, 38, .1);--cc-sf-error-text-color: #DC2626;--cc-sf-error-text-size: .75rem;--cc-sf-hint-color: #6B7280;--cc-sf-hint-size: .75rem;--cc-sf-chip-border: 1px solid #D1D5DB;--cc-sf-chip-radius: 20px;--cc-sf-chip-padding: 6px 14px;--cc-sf-chip-bg: #ffffff;--cc-sf-chip-color: #374151;--cc-sf-chip-hover-bg: #F3F4F6;--cc-sf-chip-selected-bg: #3B82F6;--cc-sf-chip-selected-color: #ffffff;--cc-sf-chip-selected-border:#3B82F6;--cc-sf-switch-track-off: #D1D5DB;--cc-sf-switch-track-on: #3B82F6;--cc-sf-switch-thumb: #ffffff;--cc-sf-star-empty: #D1D5DB;--cc-sf-star-filled: #F59E0B;--cc-sf-star-size: 28px;--cc-sf-generated-bg: #F3F4F6;--cc-sf-generated-border: 1px solid #E5E7EB;--cc-sf-generated-color: #6B7280;--cc-sf-generated-radius: 8px;--cc-sf-generated-padding: .625rem .875rem;--cc-sf-dropzone-bg: #F8FAFC;--cc-sf-dropzone-border: 1.5px dashed #CBD5E1;--cc-sf-dropzone-radius: 12px;--cc-sf-dropzone-hover-bg: #EFF6FF;--cc-sf-dropzone-hover-border: #93C5FD;--cc-sf-dropzone-over-border: #3B82F6;--cc-sf-dropzone-over-shadow: 0 0 0 4px rgba(59, 130, 246, .12);--cc-sf-dropzone-icon-color: #94A3B8;--cc-sf-dropzone-icon-bg: rgba(59, 130, 246, .1);--cc-sf-dropzone-link-color: #3B82F6;--cc-sf-dropzone-hint-color: #64748B;--cc-sf-uploaded-item-bg: #ffffff;--cc-sf-uploaded-item-border: 1px solid #E2E8F0;--cc-sf-uploaded-item-radius: 8px;--cc-sf-uploaded-remove-color: #94A3B8;--cc-sf-uploaded-remove-hover-bg: #FEF2F2;--cc-sf-uploaded-remove-hover-color: #DC2626;--cc-sf-btn-add-bg: transparent;--cc-sf-btn-add-color: #3B82F6;--cc-sf-btn-add-border: 1px dashed #CBD5E1;--cc-sf-btn-add-radius: 6px;--cc-sf-btn-add-hover-bg: #EFF6FF;--cc-sf-btn-add-hover-border: #BFDBFE;--cc-sf-btn-remove-bg: #FFF5F5;--cc-sf-btn-remove-color: #E53E3E;--cc-sf-btn-remove-border: 1px solid #FED7D7;--cc-sf-btn-remove-radius: 4px;--cc-sf-btn-remove-hover-bg: #FED7D7;--cc-sf-btn-remove-padding: .375rem .75rem;--cc-sf-btn-remove-font-size: .875rem;--cc-sf-btn-remove-font-weight: 500;--cc-sf-btn-add-padding: .625rem 1.5rem;--cc-sf-btn-add-font-size: .875rem;--cc-sf-btn-add-font-weight: 600;--cc-sf-stepper-connector-display: block;--cc-sf-instance-shadow: none;--cc-sf-instance-hover-shadow: none;--cc-sf-instance-transition: none;--cc-sf-instance-header-divider: none;--cc-sf-instance-header-pb: 0px;--cc-sf-instance-header-mb: 0px;--cc-sf-instance-num-weight: 600;--cc-sf-section-stepper-accent: #6366F1;--cc-sf-section-stepper-accent-end: #8B5CF6;--cc-sf-section-stepper-active-label: #4F46E5;--cc-sf-section-stepper-active-glow: rgba(99, 102, 241, .25);--cc-sf-step-connector-color: #E5E7EB;--cc-sf-step-connector-done: #22C55E;--cc-sf-step-number-bg: #E5E7EB;--cc-sf-step-number-color: #6B7280;--cc-sf-step-number-size: 40px;--cc-sf-step-number-font-size: .875rem;--cc-sf-step-number-weight: 600;--cc-sf-step-label-size: .875rem;--cc-sf-step-label-color: #6B7280;--cc-sf-step-label-weight: 500;--cc-sf-step-active-bg: #3B82F6;--cc-sf-step-active-color: #ffffff;--cc-sf-step-active-label: #1D4ED8;--cc-sf-step-active-label-weight: 700;--cc-sf-step-done-bg: #22C55E;--cc-sf-step-done-color: #ffffff;--cc-sf-actions-border: 1px solid #E5E7EB;--cc-sf-actions-padding: 20px 10px 0;--cc-sf-actions-gap: 12px;--cc-sf-btn-primary-bg: #3B82F6;--cc-sf-btn-primary-color: #ffffff;--cc-sf-btn-primary-radius: 8px;--cc-sf-btn-primary-padding: .625rem 1.5rem;--cc-sf-btn-primary-hover-bg: #2563EB;--cc-sf-btn-secondary-bg: #F3F4F6;--cc-sf-btn-secondary-color: #374151;--cc-sf-btn-secondary-radius: 8px;--cc-sf-btn-secondary-padding: .625rem 1.5rem;--cc-sf-btn-secondary-hover-bg: #E5E7EB;--cc-sf-btn-font-size: .875rem;--cc-sf-btn-font-weight: 600;--cc-sf-btn-transition: all .2s ease;--cc-sf-btn-disabled-opacity: .55}.fb-config-panel ::ng-deep .stepper-nav .stepper-step:not(:last-child):after{display:var(--cc-sf-stepper-connector-display)!important}.fb-config-panel ::ng-deep .section-instance{background:var(--cc-sf-instance-bg)!important;border:var(--cc-sf-instance-border)!important;border-radius:var(--cc-sf-instance-radius)!important;box-shadow:var(--cc-sf-instance-shadow);transition:var(--cc-sf-instance-transition)}.fb-config-panel ::ng-deep .section-instance:hover{box-shadow:var(--cc-sf-instance-hover-shadow)}.fb-config-panel ::ng-deep .section-instance-header{display:flex;align-items:center;justify-content:space-between;padding-bottom:var(--cc-sf-instance-header-pb);margin-bottom:var(--cc-sf-instance-header-mb);border-bottom:var(--cc-sf-instance-header-divider)}.fb-config-panel ::ng-deep .section-instance-num{font-size:var(--cc-sf-instance-num-size);font-weight:var(--cc-sf-instance-num-weight);color:var(--cc-sf-instance-num-color)}.fb-config-panel ::ng-deep .btn-instance-remove{display:inline-flex;align-items:center;gap:6px;padding:var(--cc-sf-btn-remove-padding);border-radius:var(--cc-sf-btn-remove-radius);border:var(--cc-sf-btn-remove-border)!important;background:var(--cc-sf-btn-remove-bg);color:var(--cc-sf-btn-remove-color)!important;font-size:var(--cc-sf-btn-remove-font-size);font-weight:var(--cc-sf-btn-remove-font-weight);cursor:pointer;transition:all .15s ease}.fb-config-panel ::ng-deep .btn-instance-remove:hover{background:var(--cc-sf-btn-remove-hover-bg)}.fb-config-panel ::ng-deep .btn-add-section{display:inline-flex;align-items:center;gap:8px;padding:var(--cc-sf-btn-add-padding);border-radius:var(--cc-sf-btn-add-radius);border:var(--cc-sf-btn-add-border)!important;background:var(--cc-sf-btn-add-bg);color:var(--cc-sf-btn-add-color);font-size:var(--cc-sf-btn-add-font-size);font-weight:var(--cc-sf-btn-add-font-weight);cursor:pointer;transition:all .18s ease}.fb-config-panel ::ng-deep .btn-add-section:hover{background:var(--cc-sf-btn-add-hover-bg);border-color:var(--cc-sf-btn-add-hover-border)!important}.fb-config-panel ::ng-deep .section-stepper-nav:before{background:linear-gradient(90deg,var(--cc-sf-section-stepper-accent) 0%,var(--cc-sf-section-stepper-accent-end) 100%)!important}.fb-config-panel ::ng-deep .section-stepper-step.ss-active .ss-badge{background:linear-gradient(135deg,var(--cc-sf-section-stepper-accent) 0%,var(--cc-sf-section-stepper-accent-end) 100%)!important;box-shadow:0 0 0 4px var(--cc-sf-section-stepper-active-glow),0 4px 12px var(--cc-sf-section-stepper-active-glow)!important}.fb-config-panel ::ng-deep .section-stepper-step.ss-active .ss-label{color:var(--cc-sf-section-stepper-active-label)!important}.fb-config-panel ::ng-deep .section-stepper-step:not(.ss-active):not(.ss-completed):not(.ss-warning):hover .ss-badge{color:var(--cc-sf-section-stepper-accent)!important;border-color:var(--cc-sf-section-stepper-accent-end)!important}.fb-config-panel ::ng-deep .section-stepper-step .ss-connector:after{background:linear-gradient(90deg,var(--cc-sf-section-stepper-accent),var(--cc-sf-section-stepper-accent-end))!important}}@media(min-width:1024px){.fb-config-panel{--cc-sf-font-family: Inter, Segoe UI, sans-serif;--cc-sf-font-size-base: .875rem;--cc-sf-accent-color: #3B82F6;--cc-sf-selected-color: #3B82F6;--cc-sf-form-bg: transparent;--cc-sf-form-padding: 0;--cc-sf-form-border-radius: 12px;--cc-sf-form-border: none;--cc-sf-form-shadow: none;--cc-sf-form-max-width: 1200px;--cc-sf-form-section-gap: 24px;--cc-sf-form-title-size: 1.5rem;--cc-sf-form-title-weight: 700;--cc-sf-form-title-color: #111827;--cc-sf-form-desc-size: .875rem;--cc-sf-form-desc-color: #6B7280;--cc-sf-section-bg: transparent;--cc-sf-section-border: none;--cc-sf-section-radius: 10px;--cc-sf-section-padding: 16px;--cc-sf-section-shadow: none;--cc-sf-section-gap: 20px;--cc-sf-section-label-size: 1rem;--cc-sf-section-label-weight: 600;--cc-sf-section-label-color: #1F2937;--cc-sf-section-label-border: 2px solid #E5E7EB;--cc-sf-section-header-accent-width: 4px;--cc-sf-section-header-accent-color: #3B82F6;--cc-sf-section-header-bg: #F9FAFB;--cc-sf-repeater-accordion-header-bg: #F9FAFB;--cc-sf-repeater-accordion-header-color: #1F2937;--cc-sf-repeater-accordion-active-bg: #EFF6FF;--cc-sf-repeater-badge-bg: #E5E7EB;--cc-sf-repeater-badge-color: #374151;--cc-sf-section-border-radius-inner: 8px;--cc-sf-instance-bg: #F9FAFB;--cc-sf-instance-border: 1px solid #E5E7EB;--cc-sf-instance-radius: 8px;--cc-sf-instance-padding: 16px;--cc-sf-instance-divider: 1px dashed #D1D5DB;--cc-sf-instance-num-color:#4B5563;--cc-sf-instance-num-size: .8125rem;--cc-sf-grid-gap: 16px;--cc-sf-field-gap: 6px;--cc-sf-focus-ring-width: 3px;--cc-sf-focus-ring-offset: 0;--cc-sf-label-size: .8125rem;--cc-sf-label-weight: 600;--cc-sf-label-color: #111827;--cc-sf-label-required-color: #DC2626;--cc-sf-input-bg: #ffffff;--cc-sf-input-color: #111827;--cc-sf-input-placeholder: #9CA3AF;--cc-sf-input-border: 1.5px solid #D1D5DB;--cc-sf-input-radius: 8px;--cc-sf-input-padding: .625rem .875rem;--cc-sf-input-font-size: .875rem;--cc-sf-input-shadow: none;--cc-sf-input-hover-border:#9CA3AF;--cc-sf-input-focus-border:#3B82F6;--cc-sf-input-focus-shadow:0 0 0 3px rgba(59, 130, 246, .12);--cc-sf-input-transition: all .2s ease;--cc-sf-input-disabled-bg: #F3F4F6;--cc-sf-input-disabled-color: #6B7280;--cc-sf-input-disabled-border:#E5E7EB;--cc-sf-error-border: #DC2626;--cc-sf-error-bg: #FEF2F2;--cc-sf-error-focus-shadow: 0 0 0 3px rgba(220, 38, 38, .1);--cc-sf-error-text-color: #DC2626;--cc-sf-error-text-size: .75rem;--cc-sf-hint-color: #6B7280;--cc-sf-hint-size: .75rem;--cc-sf-chip-border: 1px solid #D1D5DB;--cc-sf-chip-radius: 20px;--cc-sf-chip-padding: 6px 14px;--cc-sf-chip-bg: #ffffff;--cc-sf-chip-color: #374151;--cc-sf-chip-hover-bg: #F3F4F6;--cc-sf-chip-selected-bg: #3B82F6;--cc-sf-chip-selected-color: #ffffff;--cc-sf-chip-selected-border:#3B82F6;--cc-sf-switch-track-off: #D1D5DB;--cc-sf-switch-track-on: #3B82F6;--cc-sf-switch-thumb: #ffffff;--cc-sf-star-empty: #D1D5DB;--cc-sf-star-filled: #F59E0B;--cc-sf-star-size: 28px;--cc-sf-generated-bg: #F3F4F6;--cc-sf-generated-border: 1px solid #E5E7EB;--cc-sf-generated-color: #6B7280;--cc-sf-generated-radius: 8px;--cc-sf-generated-padding: .625rem .875rem;--cc-sf-dropzone-bg: #F8FAFC;--cc-sf-dropzone-border: 1.5px dashed #CBD5E1;--cc-sf-dropzone-radius: 12px;--cc-sf-dropzone-hover-bg: #EFF6FF;--cc-sf-dropzone-hover-border: #93C5FD;--cc-sf-dropzone-over-border: #3B82F6;--cc-sf-dropzone-over-shadow: 0 0 0 4px rgba(59, 130, 246, .12);--cc-sf-dropzone-icon-color: #94A3B8;--cc-sf-dropzone-icon-bg: rgba(59, 130, 246, .1);--cc-sf-dropzone-link-color: #3B82F6;--cc-sf-dropzone-hint-color: #64748B;--cc-sf-uploaded-item-bg: #ffffff;--cc-sf-uploaded-item-border: 1px solid #E2E8F0;--cc-sf-uploaded-item-radius: 8px;--cc-sf-uploaded-remove-color: #94A3B8;--cc-sf-uploaded-remove-hover-bg: #FEF2F2;--cc-sf-uploaded-remove-hover-color: #DC2626;--cc-sf-btn-add-bg: transparent;--cc-sf-btn-add-color: #3B82F6;--cc-sf-btn-add-border: 1px dashed #CBD5E1;--cc-sf-btn-add-radius: 6px;--cc-sf-btn-add-hover-bg: #EFF6FF;--cc-sf-btn-add-hover-border: #BFDBFE;--cc-sf-btn-remove-bg: #FFF5F5;--cc-sf-btn-remove-color: #E53E3E;--cc-sf-btn-remove-border: 1px solid #FED7D7;--cc-sf-btn-remove-radius: 4px;--cc-sf-btn-remove-hover-bg: #FED7D7;--cc-sf-btn-remove-padding: .375rem .75rem;--cc-sf-btn-remove-font-size: .875rem;--cc-sf-btn-remove-font-weight: 500;--cc-sf-btn-add-padding: .625rem 1.5rem;--cc-sf-btn-add-font-size: .875rem;--cc-sf-btn-add-font-weight: 600;--cc-sf-stepper-connector-display: block;--cc-sf-instance-shadow: none;--cc-sf-instance-hover-shadow: none;--cc-sf-instance-transition: none;--cc-sf-instance-header-divider: none;--cc-sf-instance-header-pb: 0px;--cc-sf-instance-header-mb: 0px;--cc-sf-instance-num-weight: 600;--cc-sf-section-stepper-accent: #6366F1;--cc-sf-section-stepper-accent-end: #8B5CF6;--cc-sf-section-stepper-active-label: #4F46E5;--cc-sf-section-stepper-active-glow: rgba(99, 102, 241, .25);--cc-sf-step-connector-color: #E5E7EB;--cc-sf-step-connector-done: #22C55E;--cc-sf-step-number-bg: #E5E7EB;--cc-sf-step-number-color: #6B7280;--cc-sf-step-number-size: 40px;--cc-sf-step-number-font-size: .875rem;--cc-sf-step-number-weight: 600;--cc-sf-step-label-size: .875rem;--cc-sf-step-label-color: #6B7280;--cc-sf-step-label-weight: 500;--cc-sf-step-active-bg: #3B82F6;--cc-sf-step-active-color: #ffffff;--cc-sf-step-active-label: #1D4ED8;--cc-sf-step-active-label-weight: 700;--cc-sf-step-done-bg: #22C55E;--cc-sf-step-done-color: #ffffff;--cc-sf-actions-border: 1px solid #E5E7EB;--cc-sf-actions-padding: 20px 10px 0;--cc-sf-actions-gap: 12px;--cc-sf-btn-primary-bg: #3B82F6;--cc-sf-btn-primary-color: #ffffff;--cc-sf-btn-primary-radius: 8px;--cc-sf-btn-primary-padding: .625rem 1.5rem;--cc-sf-btn-primary-hover-bg: #2563EB;--cc-sf-btn-secondary-bg: #F3F4F6;--cc-sf-btn-secondary-color: #374151;--cc-sf-btn-secondary-radius: 8px;--cc-sf-btn-secondary-padding: .625rem 1.5rem;--cc-sf-btn-secondary-hover-bg: #E5E7EB;--cc-sf-btn-font-size: .875rem;--cc-sf-btn-font-weight: 600;--cc-sf-btn-transition: all .2s ease;--cc-sf-btn-disabled-opacity: .55}.fb-config-panel ::ng-deep .stepper-nav .stepper-step:not(:last-child):after{display:var(--cc-sf-stepper-connector-display)!important}.fb-config-panel ::ng-deep .section-instance{background:var(--cc-sf-instance-bg)!important;border:var(--cc-sf-instance-border)!important;border-radius:var(--cc-sf-instance-radius)!important;box-shadow:var(--cc-sf-instance-shadow);transition:var(--cc-sf-instance-transition)}.fb-config-panel ::ng-deep .section-instance:hover{box-shadow:var(--cc-sf-instance-hover-shadow)}.fb-config-panel ::ng-deep .section-instance-header{display:flex;align-items:center;justify-content:space-between;padding-bottom:var(--cc-sf-instance-header-pb);margin-bottom:var(--cc-sf-instance-header-mb);border-bottom:var(--cc-sf-instance-header-divider)}.fb-config-panel ::ng-deep .section-instance-num{font-size:var(--cc-sf-instance-num-size);font-weight:var(--cc-sf-instance-num-weight);color:var(--cc-sf-instance-num-color)}.fb-config-panel ::ng-deep .btn-instance-remove{display:inline-flex;align-items:center;gap:6px;padding:var(--cc-sf-btn-remove-padding);border-radius:var(--cc-sf-btn-remove-radius);border:var(--cc-sf-btn-remove-border)!important;background:var(--cc-sf-btn-remove-bg);color:var(--cc-sf-btn-remove-color)!important;font-size:var(--cc-sf-btn-remove-font-size);font-weight:var(--cc-sf-btn-remove-font-weight);cursor:pointer;transition:all .15s ease}.fb-config-panel ::ng-deep .btn-instance-remove:hover{background:var(--cc-sf-btn-remove-hover-bg)}.fb-config-panel ::ng-deep .btn-add-section{display:inline-flex;align-items:center;gap:8px;padding:var(--cc-sf-btn-add-padding);border-radius:var(--cc-sf-btn-add-radius);border:var(--cc-sf-btn-add-border)!important;background:var(--cc-sf-btn-add-bg);color:var(--cc-sf-btn-add-color);font-size:var(--cc-sf-btn-add-font-size);font-weight:var(--cc-sf-btn-add-font-weight);cursor:pointer;transition:all .18s ease}.fb-config-panel ::ng-deep .btn-add-section:hover{background:var(--cc-sf-btn-add-hover-bg);border-color:var(--cc-sf-btn-add-hover-border)!important}.fb-config-panel ::ng-deep .section-stepper-nav:before{background:linear-gradient(90deg,var(--cc-sf-section-stepper-accent) 0%,var(--cc-sf-section-stepper-accent-end) 100%)!important}.fb-config-panel ::ng-deep .section-stepper-step.ss-active .ss-badge{background:linear-gradient(135deg,var(--cc-sf-section-stepper-accent) 0%,var(--cc-sf-section-stepper-accent-end) 100%)!important;box-shadow:0 0 0 4px var(--cc-sf-section-stepper-active-glow),0 4px 12px var(--cc-sf-section-stepper-active-glow)!important}.fb-config-panel ::ng-deep .section-stepper-step.ss-active .ss-label{color:var(--cc-sf-section-stepper-active-label)!important}.fb-config-panel ::ng-deep .section-stepper-step:not(.ss-active):not(.ss-completed):not(.ss-warning):hover .ss-badge{color:var(--cc-sf-section-stepper-accent)!important;border-color:var(--cc-sf-section-stepper-accent-end)!important}.fb-config-panel ::ng-deep .section-stepper-step .ss-connector:after{background:linear-gradient(90deg,var(--cc-sf-section-stepper-accent),var(--cc-sf-section-stepper-accent-end))!important}}@media(min-width:1440px){.fb-config-panel{--cc-sf-font-family: Inter, Segoe UI, sans-serif;--cc-sf-font-size-base: .875rem;--cc-sf-accent-color: #3B82F6;--cc-sf-selected-color: #3B82F6;--cc-sf-form-bg: transparent;--cc-sf-form-padding: 0;--cc-sf-form-border-radius: 12px;--cc-sf-form-border: none;--cc-sf-form-shadow: none;--cc-sf-form-max-width: 1200px;--cc-sf-form-section-gap: 24px;--cc-sf-form-title-size: 1.5rem;--cc-sf-form-title-weight: 700;--cc-sf-form-title-color: #111827;--cc-sf-form-desc-size: .875rem;--cc-sf-form-desc-color: #6B7280;--cc-sf-section-bg: transparent;--cc-sf-section-border: none;--cc-sf-section-radius: 10px;--cc-sf-section-padding: 18px;--cc-sf-section-shadow: none;--cc-sf-section-gap: 20px;--cc-sf-section-label-size: 1rem;--cc-sf-section-label-weight: 600;--cc-sf-section-label-color: #1F2937;--cc-sf-section-label-border: 2px solid #E5E7EB;--cc-sf-section-header-accent-width: 4px;--cc-sf-section-header-accent-color: #3B82F6;--cc-sf-section-header-bg: #F9FAFB;--cc-sf-repeater-accordion-header-bg: #F9FAFB;--cc-sf-repeater-accordion-header-color: #1F2937;--cc-sf-repeater-accordion-active-bg: #EFF6FF;--cc-sf-repeater-badge-bg: #E5E7EB;--cc-sf-repeater-badge-color: #374151;--cc-sf-section-border-radius-inner: 8px;--cc-sf-instance-bg: #F9FAFB;--cc-sf-instance-border: 1px solid #E5E7EB;--cc-sf-instance-radius: 8px;--cc-sf-instance-padding: 16px;--cc-sf-instance-divider: 1px dashed #D1D5DB;--cc-sf-instance-num-color:#4B5563;--cc-sf-instance-num-size: .8125rem;--cc-sf-grid-gap: 18px;--cc-sf-field-gap: 6px;--cc-sf-focus-ring-width: 3px;--cc-sf-focus-ring-offset: 0;--cc-sf-label-size: .8125rem;--cc-sf-label-weight: 600;--cc-sf-label-color: #111827;--cc-sf-label-required-color: #DC2626;--cc-sf-input-bg: #ffffff;--cc-sf-input-color: #111827;--cc-sf-input-placeholder: #9CA3AF;--cc-sf-input-border: 1.5px solid #D1D5DB;--cc-sf-input-radius: 8px;--cc-sf-input-padding: .625rem .875rem;--cc-sf-input-font-size: .875rem;--cc-sf-input-shadow: none;--cc-sf-input-hover-border:#9CA3AF;--cc-sf-input-focus-border:#3B82F6;--cc-sf-input-focus-shadow:0 0 0 3px rgba(59, 130, 246, .12);--cc-sf-input-transition: all .2s ease;--cc-sf-input-disabled-bg: #F3F4F6;--cc-sf-input-disabled-color: #6B7280;--cc-sf-input-disabled-border:#E5E7EB;--cc-sf-error-border: #DC2626;--cc-sf-error-bg: #FEF2F2;--cc-sf-error-focus-shadow: 0 0 0 3px rgba(220, 38, 38, .1);--cc-sf-error-text-color: #DC2626;--cc-sf-error-text-size: .75rem;--cc-sf-hint-color: #6B7280;--cc-sf-hint-size: .75rem;--cc-sf-chip-border: 1px solid #D1D5DB;--cc-sf-chip-radius: 20px;--cc-sf-chip-padding: 6px 14px;--cc-sf-chip-bg: #ffffff;--cc-sf-chip-color: #374151;--cc-sf-chip-hover-bg: #F3F4F6;--cc-sf-chip-selected-bg: #3B82F6;--cc-sf-chip-selected-color: #ffffff;--cc-sf-chip-selected-border:#3B82F6;--cc-sf-switch-track-off: #D1D5DB;--cc-sf-switch-track-on: #3B82F6;--cc-sf-switch-thumb: #ffffff;--cc-sf-star-empty: #D1D5DB;--cc-sf-star-filled: #F59E0B;--cc-sf-star-size: 28px;--cc-sf-generated-bg: #F3F4F6;--cc-sf-generated-border: 1px solid #E5E7EB;--cc-sf-generated-color: #6B7280;--cc-sf-generated-radius: 8px;--cc-sf-generated-padding: .625rem .875rem;--cc-sf-dropzone-bg: #F8FAFC;--cc-sf-dropzone-border: 1.5px dashed #CBD5E1;--cc-sf-dropzone-radius: 12px;--cc-sf-dropzone-hover-bg: #EFF6FF;--cc-sf-dropzone-hover-border: #93C5FD;--cc-sf-dropzone-over-border: #3B82F6;--cc-sf-dropzone-over-shadow: 0 0 0 4px rgba(59, 130, 246, .12);--cc-sf-dropzone-icon-color: #94A3B8;--cc-sf-dropzone-icon-bg: rgba(59, 130, 246, .1);--cc-sf-dropzone-link-color: #3B82F6;--cc-sf-dropzone-hint-color: #64748B;--cc-sf-uploaded-item-bg: #ffffff;--cc-sf-uploaded-item-border: 1px solid #E2E8F0;--cc-sf-uploaded-item-radius: 8px;--cc-sf-uploaded-remove-color: #94A3B8;--cc-sf-uploaded-remove-hover-bg: #FEF2F2;--cc-sf-uploaded-remove-hover-color: #DC2626;--cc-sf-btn-add-bg: transparent;--cc-sf-btn-add-color: #3B82F6;--cc-sf-btn-add-border: 1px dashed #CBD5E1;--cc-sf-btn-add-radius: 6px;--cc-sf-btn-add-hover-bg: #EFF6FF;--cc-sf-btn-add-hover-border: #BFDBFE;--cc-sf-btn-remove-bg: #FFF5F5;--cc-sf-btn-remove-color: #E53E3E;--cc-sf-btn-remove-border: 1px solid #FED7D7;--cc-sf-btn-remove-radius: 4px;--cc-sf-btn-remove-hover-bg: #FED7D7;--cc-sf-btn-remove-padding: .375rem .75rem;--cc-sf-btn-remove-font-size: .875rem;--cc-sf-btn-remove-font-weight: 500;--cc-sf-btn-add-padding: .625rem 1.5rem;--cc-sf-btn-add-font-size: .875rem;--cc-sf-btn-add-font-weight: 600;--cc-sf-stepper-connector-display: block;--cc-sf-instance-shadow: none;--cc-sf-instance-hover-shadow: none;--cc-sf-instance-transition: none;--cc-sf-instance-header-divider: none;--cc-sf-instance-header-pb: 0px;--cc-sf-instance-header-mb: 0px;--cc-sf-instance-num-weight: 600;--cc-sf-section-stepper-accent: #6366F1;--cc-sf-section-stepper-accent-end: #8B5CF6;--cc-sf-section-stepper-active-label: #4F46E5;--cc-sf-section-stepper-active-glow: rgba(99, 102, 241, .25);--cc-sf-step-connector-color: #E5E7EB;--cc-sf-step-connector-done: #22C55E;--cc-sf-step-number-bg: #E5E7EB;--cc-sf-step-number-color: #6B7280;--cc-sf-step-number-size: 40px;--cc-sf-step-number-font-size: .875rem;--cc-sf-step-number-weight: 600;--cc-sf-step-label-size: .875rem;--cc-sf-step-label-color: #6B7280;--cc-sf-step-label-weight: 500;--cc-sf-step-active-bg: #3B82F6;--cc-sf-step-active-color: #ffffff;--cc-sf-step-active-label: #1D4ED8;--cc-sf-step-active-label-weight: 700;--cc-sf-step-done-bg: #22C55E;--cc-sf-step-done-color: #ffffff;--cc-sf-actions-border: 1px solid #E5E7EB;--cc-sf-actions-padding: 20px 10px 0;--cc-sf-actions-gap: 12px;--cc-sf-btn-primary-bg: #3B82F6;--cc-sf-btn-primary-color: #ffffff;--cc-sf-btn-primary-radius: 8px;--cc-sf-btn-primary-padding: .625rem 1.5rem;--cc-sf-btn-primary-hover-bg: #2563EB;--cc-sf-btn-secondary-bg: #F3F4F6;--cc-sf-btn-secondary-color: #374151;--cc-sf-btn-secondary-radius: 8px;--cc-sf-btn-secondary-padding: .625rem 1.5rem;--cc-sf-btn-secondary-hover-bg: #E5E7EB;--cc-sf-btn-font-size: .875rem;--cc-sf-btn-font-weight: 600;--cc-sf-btn-transition: all .2s ease;--cc-sf-btn-disabled-opacity: .55}.fb-config-panel ::ng-deep .stepper-nav .stepper-step:not(:last-child):after{display:var(--cc-sf-stepper-connector-display)!important}.fb-config-panel ::ng-deep .section-instance{background:var(--cc-sf-instance-bg)!important;border:var(--cc-sf-instance-border)!important;border-radius:var(--cc-sf-instance-radius)!important;box-shadow:var(--cc-sf-instance-shadow);transition:var(--cc-sf-instance-transition)}.fb-config-panel ::ng-deep .section-instance:hover{box-shadow:var(--cc-sf-instance-hover-shadow)}.fb-config-panel ::ng-deep .section-instance-header{display:flex;align-items:center;justify-content:space-between;padding-bottom:var(--cc-sf-instance-header-pb);margin-bottom:var(--cc-sf-instance-header-mb);border-bottom:var(--cc-sf-instance-header-divider)}.fb-config-panel ::ng-deep .section-instance-num{font-size:var(--cc-sf-instance-num-size);font-weight:var(--cc-sf-instance-num-weight);color:var(--cc-sf-instance-num-color)}.fb-config-panel ::ng-deep .btn-instance-remove{display:inline-flex;align-items:center;gap:6px;padding:var(--cc-sf-btn-remove-padding);border-radius:var(--cc-sf-btn-remove-radius);border:var(--cc-sf-btn-remove-border)!important;background:var(--cc-sf-btn-remove-bg);color:var(--cc-sf-btn-remove-color)!important;font-size:var(--cc-sf-btn-remove-font-size);font-weight:var(--cc-sf-btn-remove-font-weight);cursor:pointer;transition:all .15s ease}.fb-config-panel ::ng-deep .btn-instance-remove:hover{background:var(--cc-sf-btn-remove-hover-bg)}.fb-config-panel ::ng-deep .btn-add-section{display:inline-flex;align-items:center;gap:8px;padding:var(--cc-sf-btn-add-padding);border-radius:var(--cc-sf-btn-add-radius);border:var(--cc-sf-btn-add-border)!important;background:var(--cc-sf-btn-add-bg);color:var(--cc-sf-btn-add-color);font-size:var(--cc-sf-btn-add-font-size);font-weight:var(--cc-sf-btn-add-font-weight);cursor:pointer;transition:all .18s ease}.fb-config-panel ::ng-deep .btn-add-section:hover{background:var(--cc-sf-btn-add-hover-bg);border-color:var(--cc-sf-btn-add-hover-border)!important}.fb-config-panel ::ng-deep .section-stepper-nav:before{background:linear-gradient(90deg,var(--cc-sf-section-stepper-accent) 0%,var(--cc-sf-section-stepper-accent-end) 100%)!important}.fb-config-panel ::ng-deep .section-stepper-step.ss-active .ss-badge{background:linear-gradient(135deg,var(--cc-sf-section-stepper-accent) 0%,var(--cc-sf-section-stepper-accent-end) 100%)!important;box-shadow:0 0 0 4px var(--cc-sf-section-stepper-active-glow),0 4px 12px var(--cc-sf-section-stepper-active-glow)!important}.fb-config-panel ::ng-deep .section-stepper-step.ss-active .ss-label{color:var(--cc-sf-section-stepper-active-label)!important}.fb-config-panel ::ng-deep .section-stepper-step:not(.ss-active):not(.ss-completed):not(.ss-warning):hover .ss-badge{color:var(--cc-sf-section-stepper-accent)!important;border-color:var(--cc-sf-section-stepper-accent-end)!important}.fb-config-panel ::ng-deep .section-stepper-step .ss-connector:after{background:linear-gradient(90deg,var(--cc-sf-section-stepper-accent),var(--cc-sf-section-stepper-accent-end))!important}}@media(min-width:1920px){.fb-config-panel{--cc-sf-font-family: Inter, Segoe UI, sans-serif;--cc-sf-font-size-base: .875rem;--cc-sf-accent-color: #3B82F6;--cc-sf-selected-color: #3B82F6;--cc-sf-form-bg: transparent;--cc-sf-form-padding: 0;--cc-sf-form-border-radius: 12px;--cc-sf-form-border: none;--cc-sf-form-shadow: none;--cc-sf-form-max-width: 1200px;--cc-sf-form-section-gap: 24px;--cc-sf-form-title-size: 1.5rem;--cc-sf-form-title-weight: 700;--cc-sf-form-title-color: #111827;--cc-sf-form-desc-size: .875rem;--cc-sf-form-desc-color: #6B7280;--cc-sf-section-bg: transparent;--cc-sf-section-border: none;--cc-sf-section-radius: 10px;--cc-sf-section-padding: 18px;--cc-sf-section-shadow: none;--cc-sf-section-gap: 20px;--cc-sf-section-label-size: 1rem;--cc-sf-section-label-weight: 600;--cc-sf-section-label-color: #1F2937;--cc-sf-section-label-border: 2px solid #E5E7EB;--cc-sf-section-header-accent-width: 4px;--cc-sf-section-header-accent-color: #3B82F6;--cc-sf-section-header-bg: #F9FAFB;--cc-sf-repeater-accordion-header-bg: #F9FAFB;--cc-sf-repeater-accordion-header-color: #1F2937;--cc-sf-repeater-accordion-active-bg: #EFF6FF;--cc-sf-repeater-badge-bg: #E5E7EB;--cc-sf-repeater-badge-color: #374151;--cc-sf-section-border-radius-inner: 8px;--cc-sf-instance-bg: #F9FAFB;--cc-sf-instance-border: 1px solid #E5E7EB;--cc-sf-instance-radius: 8px;--cc-sf-instance-padding: 16px;--cc-sf-instance-divider: 1px dashed #D1D5DB;--cc-sf-instance-num-color:#4B5563;--cc-sf-instance-num-size: .8125rem;--cc-sf-grid-gap: 18px;--cc-sf-field-gap: 6px;--cc-sf-focus-ring-width: 3px;--cc-sf-focus-ring-offset: 0;--cc-sf-label-size: .8125rem;--cc-sf-label-weight: 600;--cc-sf-label-color: #111827;--cc-sf-label-required-color: #DC2626;--cc-sf-input-bg: #ffffff;--cc-sf-input-color: #111827;--cc-sf-input-placeholder: #9CA3AF;--cc-sf-input-border: 1.5px solid #D1D5DB;--cc-sf-input-radius: 8px;--cc-sf-input-padding: .625rem .875rem;--cc-sf-input-font-size: .875rem;--cc-sf-input-shadow: none;--cc-sf-input-hover-border:#9CA3AF;--cc-sf-input-focus-border:#3B82F6;--cc-sf-input-focus-shadow:0 0 0 3px rgba(59, 130, 246, .12);--cc-sf-input-transition: all .2s ease;--cc-sf-input-disabled-bg: #F3F4F6;--cc-sf-input-disabled-color: #6B7280;--cc-sf-input-disabled-border:#E5E7EB;--cc-sf-error-border: #DC2626;--cc-sf-error-bg: #FEF2F2;--cc-sf-error-focus-shadow: 0 0 0 3px rgba(220, 38, 38, .1);--cc-sf-error-text-color: #DC2626;--cc-sf-error-text-size: .75rem;--cc-sf-hint-color: #6B7280;--cc-sf-hint-size: .75rem;--cc-sf-chip-border: 1px solid #D1D5DB;--cc-sf-chip-radius: 20px;--cc-sf-chip-padding: 6px 14px;--cc-sf-chip-bg: #ffffff;--cc-sf-chip-color: #374151;--cc-sf-chip-hover-bg: #F3F4F6;--cc-sf-chip-selected-bg: #3B82F6;--cc-sf-chip-selected-color: #ffffff;--cc-sf-chip-selected-border:#3B82F6;--cc-sf-switch-track-off: #D1D5DB;--cc-sf-switch-track-on: #3B82F6;--cc-sf-switch-thumb: #ffffff;--cc-sf-star-empty: #D1D5DB;--cc-sf-star-filled: #F59E0B;--cc-sf-star-size: 28px;--cc-sf-generated-bg: #F3F4F6;--cc-sf-generated-border: 1px solid #E5E7EB;--cc-sf-generated-color: #6B7280;--cc-sf-generated-radius: 8px;--cc-sf-generated-padding: .625rem .875rem;--cc-sf-dropzone-bg: #F8FAFC;--cc-sf-dropzone-border: 1.5px dashed #CBD5E1;--cc-sf-dropzone-radius: 12px;--cc-sf-dropzone-hover-bg: #EFF6FF;--cc-sf-dropzone-hover-border: #93C5FD;--cc-sf-dropzone-over-border: #3B82F6;--cc-sf-dropzone-over-shadow: 0 0 0 4px rgba(59, 130, 246, .12);--cc-sf-dropzone-icon-color: #94A3B8;--cc-sf-dropzone-icon-bg: rgba(59, 130, 246, .1);--cc-sf-dropzone-link-color: #3B82F6;--cc-sf-dropzone-hint-color: #64748B;--cc-sf-uploaded-item-bg: #ffffff;--cc-sf-uploaded-item-border: 1px solid #E2E8F0;--cc-sf-uploaded-item-radius: 8px;--cc-sf-uploaded-remove-color: #94A3B8;--cc-sf-uploaded-remove-hover-bg: #FEF2F2;--cc-sf-uploaded-remove-hover-color: #DC2626;--cc-sf-btn-add-bg: transparent;--cc-sf-btn-add-color: #3B82F6;--cc-sf-btn-add-border: 1px dashed #CBD5E1;--cc-sf-btn-add-radius: 6px;--cc-sf-btn-add-hover-bg: #EFF6FF;--cc-sf-btn-add-hover-border: #BFDBFE;--cc-sf-btn-remove-bg: #FFF5F5;--cc-sf-btn-remove-color: #E53E3E;--cc-sf-btn-remove-border: 1px solid #FED7D7;--cc-sf-btn-remove-radius: 4px;--cc-sf-btn-remove-hover-bg: #FED7D7;--cc-sf-btn-remove-padding: .375rem .75rem;--cc-sf-btn-remove-font-size: .875rem;--cc-sf-btn-remove-font-weight: 500;--cc-sf-btn-add-padding: .625rem 1.5rem;--cc-sf-btn-add-font-size: .875rem;--cc-sf-btn-add-font-weight: 600;--cc-sf-stepper-connector-display: block;--cc-sf-instance-shadow: none;--cc-sf-instance-hover-shadow: none;--cc-sf-instance-transition: none;--cc-sf-instance-header-divider: none;--cc-sf-instance-header-pb: 0px;--cc-sf-instance-header-mb: 0px;--cc-sf-instance-num-weight: 600;--cc-sf-section-stepper-accent: #6366F1;--cc-sf-section-stepper-accent-end: #8B5CF6;--cc-sf-section-stepper-active-label: #4F46E5;--cc-sf-section-stepper-active-glow: rgba(99, 102, 241, .25);--cc-sf-step-connector-color: #E5E7EB;--cc-sf-step-connector-done: #22C55E;--cc-sf-step-number-bg: #E5E7EB;--cc-sf-step-number-color: #6B7280;--cc-sf-step-number-size: 40px;--cc-sf-step-number-font-size: .875rem;--cc-sf-step-number-weight: 600;--cc-sf-step-label-size: .875rem;--cc-sf-step-label-color: #6B7280;--cc-sf-step-label-weight: 500;--cc-sf-step-active-bg: #3B82F6;--cc-sf-step-active-color: #ffffff;--cc-sf-step-active-label: #1D4ED8;--cc-sf-step-active-label-weight: 700;--cc-sf-step-done-bg: #22C55E;--cc-sf-step-done-color: #ffffff;--cc-sf-actions-border: 1px solid #E5E7EB;--cc-sf-actions-padding: 20px 10px 0;--cc-sf-actions-gap: 12px;--cc-sf-btn-primary-bg: #3B82F6;--cc-sf-btn-primary-color: #ffffff;--cc-sf-btn-primary-radius: 8px;--cc-sf-btn-primary-padding: .625rem 1.5rem;--cc-sf-btn-primary-hover-bg: #2563EB;--cc-sf-btn-secondary-bg: #F3F4F6;--cc-sf-btn-secondary-color: #374151;--cc-sf-btn-secondary-radius: 8px;--cc-sf-btn-secondary-padding: .625rem 1.5rem;--cc-sf-btn-secondary-hover-bg: #E5E7EB;--cc-sf-btn-font-size: .875rem;--cc-sf-btn-font-weight: 600;--cc-sf-btn-transition: all .2s ease;--cc-sf-btn-disabled-opacity: .55}.fb-config-panel ::ng-deep .stepper-nav .stepper-step:not(:last-child):after{display:var(--cc-sf-stepper-connector-display)!important}.fb-config-panel ::ng-deep .section-instance{background:var(--cc-sf-instance-bg)!important;border:var(--cc-sf-instance-border)!important;border-radius:var(--cc-sf-instance-radius)!important;box-shadow:var(--cc-sf-instance-shadow);transition:var(--cc-sf-instance-transition)}.fb-config-panel ::ng-deep .section-instance:hover{box-shadow:var(--cc-sf-instance-hover-shadow)}.fb-config-panel ::ng-deep .section-instance-header{display:flex;align-items:center;justify-content:space-between;padding-bottom:var(--cc-sf-instance-header-pb);margin-bottom:var(--cc-sf-instance-header-mb);border-bottom:var(--cc-sf-instance-header-divider)}.fb-config-panel ::ng-deep .section-instance-num{font-size:var(--cc-sf-instance-num-size);font-weight:var(--cc-sf-instance-num-weight);color:var(--cc-sf-instance-num-color)}.fb-config-panel ::ng-deep .btn-instance-remove{display:inline-flex;align-items:center;gap:6px;padding:var(--cc-sf-btn-remove-padding);border-radius:var(--cc-sf-btn-remove-radius);border:var(--cc-sf-btn-remove-border)!important;background:var(--cc-sf-btn-remove-bg);color:var(--cc-sf-btn-remove-color)!important;font-size:var(--cc-sf-btn-remove-font-size);font-weight:var(--cc-sf-btn-remove-font-weight);cursor:pointer;transition:all .15s ease}.fb-config-panel ::ng-deep .btn-instance-remove:hover{background:var(--cc-sf-btn-remove-hover-bg)}.fb-config-panel ::ng-deep .btn-add-section{display:inline-flex;align-items:center;gap:8px;padding:var(--cc-sf-btn-add-padding);border-radius:var(--cc-sf-btn-add-radius);border:var(--cc-sf-btn-add-border)!important;background:var(--cc-sf-btn-add-bg);color:var(--cc-sf-btn-add-color);font-size:var(--cc-sf-btn-add-font-size);font-weight:var(--cc-sf-btn-add-font-weight);cursor:pointer;transition:all .18s ease}.fb-config-panel ::ng-deep .btn-add-section:hover{background:var(--cc-sf-btn-add-hover-bg);border-color:var(--cc-sf-btn-add-hover-border)!important}.fb-config-panel ::ng-deep .section-stepper-nav:before{background:linear-gradient(90deg,var(--cc-sf-section-stepper-accent) 0%,var(--cc-sf-section-stepper-accent-end) 100%)!important}.fb-config-panel ::ng-deep .section-stepper-step.ss-active .ss-badge{background:linear-gradient(135deg,var(--cc-sf-section-stepper-accent) 0%,var(--cc-sf-section-stepper-accent-end) 100%)!important;box-shadow:0 0 0 4px var(--cc-sf-section-stepper-active-glow),0 4px 12px var(--cc-sf-section-stepper-active-glow)!important}.fb-config-panel ::ng-deep .section-stepper-step.ss-active .ss-label{color:var(--cc-sf-section-stepper-active-label)!important}.fb-config-panel ::ng-deep .section-stepper-step:not(.ss-active):not(.ss-completed):not(.ss-warning):hover .ss-badge{color:var(--cc-sf-section-stepper-accent)!important;border-color:var(--cc-sf-section-stepper-accent-end)!important}.fb-config-panel ::ng-deep .section-stepper-step .ss-connector:after{background:linear-gradient(90deg,var(--cc-sf-section-stepper-accent),var(--cc-sf-section-stepper-accent-end))!important}}.fb-config-panel__empty{display:flex;flex-direction:column;align-items:center;justify-content:center;height:100%;color:var(--fb-fc-empty-color, #9ca3af);text-align:center;padding:48px 24px}@media(max-width:640px){.fb-config-panel__empty{padding:24px 16px}}.fb-config-panel__empty-icon{margin-bottom:24px;opacity:.4;color:var(--fb-fc-accent, #3b82f6)}@media(max-width:640px){.fb-config-panel__empty-icon{margin-bottom:16px}}.fb-config-panel__empty-text{font-size:15px;max-width:320px;line-height:1.6;font-weight:500}@media(max-width:640px){.fb-config-panel__empty-text{font-size:14px;max-width:100%}}.fb-config-panel__header{background:var(--fb-fc-header-bg, #ffffff);padding:1.4rem;border-bottom:1px solid var(--fb-fc-border, #e5e7eb);display:flex;align-items:center;justify-content:space-between;flex-shrink:0;gap:16px;flex-wrap:wrap}@media(max-width:768px){.fb-config-panel__header{padding:16px 24px;gap:12px}}@media(max-width:640px){.fb-config-panel__header{padding:12px 16px;gap:8px}}.fb-config-panel__header-left{display:flex;align-items:center;gap:12px;min-width:0;flex-wrap:wrap}@media(max-width:640px){.fb-config-panel__header-left{gap:8px}}.fb-config-panel__type-switcher{display:flex;align-items:center;gap:8px;flex-shrink:0}@media(max-width:640px){.fb-config-panel__type-switcher{gap:4px}}.fb-config-panel__type-label{font-size:var(--fb-fc-type-label-size, .75rem);font-weight:600;color:var(--fb-fc-type-label-color, #6b7280);white-space:nowrap}@media(max-width:640px){.fb-config-panel__type-label{font-size:.7rem}}.fb-config-panel__type-select{height:var(--fb-fc-type-select-height, 36px);padding:0 32px 0 10px;border:1px solid var(--fb-fc-type-select-border, #d1d5db);border-radius:var(--fb-fc-type-select-radius, 8px);background-color:var(--fb-fc-type-select-bg, #ffffff);color:var(--fb-fc-type-select-color, #111827);font-size:.8125rem;font-weight:500;cursor:pointer;appearance:none;background-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='16' height='16' viewBox='0 0 24 24' fill='none' stroke='%236b7280' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E\");background-repeat:no-repeat;background-position:right 8px center;min-width:160px;transition:border-color .15s ease,box-shadow .15s ease}@media(max-width:640px){.fb-config-panel__type-select{height:32px;font-size:.75rem;min-width:140px;padding:0 28px 0 8px}}.fb-config-panel__type-select:hover{border-color:var(--fb-fc-type-select-hover-border, #9ca3af)}.fb-config-panel__type-select:focus{outline:none;border-color:var(--fb-fc-accent, #3b82f6);box-shadow:0 0 0 3px #3b82f61f}.fb-config-panel__title{margin:0;font-size:1.25rem;font-weight:700;color:var(--fb-fc-title-color, #111827)}@media(max-width:1024px){.fb-config-panel__title{font-size:1.125rem}}@media(max-width:640px){.fb-config-panel__title{font-size:1rem}}.fb-config-panel__badge{padding:4px 12px;background:var(--fb-fc-badge-bg, #eff6ff);color:var(--fb-fc-badge-color, #3b82f6);border-radius:20px;font-size:11px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;border:1px solid var(--fb-fc-badge-border, #dbeafe)}@media(max-width:640px){.fb-config-panel__badge{padding:3px 8px;font-size:9px}}.fb-config-panel__form-wrapper{flex:1;overflow-y:auto;overflow-x:hidden;width:100%;min-width:0;display:flex;flex-direction:column;align-items:stretch;box-sizing:border-box;padding:12px 16px}@media(min-width:600px){.fb-config-panel__form-wrapper{padding:16px 20px}}@media(min-width:768px){.fb-config-panel__form-wrapper{padding:20px 24px}}@media(min-width:1024px){.fb-config-panel__form-wrapper{padding:1rem}}@media(min-width:1440px){.fb-config-panel__form-wrapper{padding:28px 40px}}@media(min-width:1920px){.fb-config-panel__form-wrapper{padding:32px 48px}}.fb-config-panel__form-wrapper .fb-config-card{width:100%;max-width:none;margin:0;background:transparent;border:none;box-shadow:none;display:flex;flex-direction:column;min-width:0;flex:1;box-sizing:border-box}.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .sf-col{min-width:0}.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal{display:grid;grid-auto-flow:dense;gap:12px;width:100%;align-items:start}@media(max-width:599px){.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal{grid-template-columns:1fr;gap:12px}}@media(min-width:600px)and (max-width:767px){.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal{grid-template-columns:1fr;gap:14px}}@media(min-width:768px)and (max-width:1023px){.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal{grid-template-columns:repeat(2,1fr);gap:14px}}@media(min-width:1024px)and (max-width:1439px){.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal{grid-template-columns:repeat(2,1fr);gap:16px}}@media(min-width:1440px){.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal{grid-template-columns:repeat(3,1fr);gap:18px}}@media(min-width:1920px){.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal{grid-template-columns:repeat(3,1fr);gap:20px}}.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal>*{display:flex;flex-direction:column;min-width:0;overflow:hidden}.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal>* ::ng-deep .form-field-wrapper,.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal>* ::ng-deep .field-wrapper,.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal>* ::ng-deep input,.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal>* ::ng-deep select,.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal>* ::ng-deep textarea{width:100%;min-width:0}.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal .sf-switch-wrapper{display:flex;flex-direction:row;align-items:center;gap:8px;white-space:nowrap;min-width:0}@media(max-width:768px){.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal .sf-switch-wrapper{gap:6px}}.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal .sf-switch-wrapper .field-label{font-size:.75rem;font-weight:600;margin:0;flex-shrink:1;min-width:0;word-break:keep-all}@media(max-width:768px){.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal .sf-switch-wrapper .field-label{font-size:.7rem}}@media(max-width:640px){.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal .sf-switch-wrapper .field-label{font-size:.65rem}}.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .form-row.horizontal .sf-switch-wrapper .switch{flex-shrink:0}.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .grid-row .sf-col .sf-switch-wrapper{display:flex;flex-direction:row;align-items:center;gap:8px;white-space:nowrap;min-width:0}@media(max-width:768px){.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .grid-row .sf-col .sf-switch-wrapper{gap:6px}}.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .grid-row .sf-col .sf-switch-wrapper .field-label{font-size:.75rem;font-weight:600;margin:0;flex-shrink:1;min-width:0;word-break:keep-all}@media(max-width:768px){.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .grid-row .sf-col .sf-switch-wrapper .field-label{font-size:.7rem}}@media(max-width:640px){.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .grid-row .sf-col .sf-switch-wrapper .field-label{font-size:.65rem}}.fb-config-panel__form-wrapper .fb-config-card ::ng-deep .grid-row .sf-col .sf-switch-wrapper .switch{flex-shrink:0}.fb-config-panel__error{padding:24px;background:var(--fb-fc-error-bg, #fef2f2);color:var(--fb-fc-error-color, #dc2626);border:1px solid var(--fb-fc-error-border, #fee2e2);border-radius:8px;font-size:14px;font-weight:500}@media(max-width:768px){.fb-config-panel__error{padding:16px;font-size:13px}}@media(max-width:640px){.fb-config-panel__error{padding:12px;font-size:12px}}\n"], dependencies: [{ kind: "component", type: SmartFormComponent, selector: "lib-smart-form", inputs: ["formJson", "initialValues", "enableDraftAutoSave", "labels", "mode", "readOnly"], outputs: ["submit", "draftSave", "actionClick", "valueChange", "fileAdded", "fileUploadFinished", "fileRemoved", "stepChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7524
8026
|
}
|
|
7525
8027
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ConfiguratorConfigPanelComponent, decorators: [{
|
|
7526
8028
|
type: Component,
|
|
@@ -7539,6 +8041,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
7539
8041
|
type: Output
|
|
7540
8042
|
}], showOptionConfig: [{
|
|
7541
8043
|
type: Input
|
|
8044
|
+
}], lockedFieldNames: [{
|
|
8045
|
+
type: Input
|
|
7542
8046
|
}] } });
|
|
7543
8047
|
|
|
7544
8048
|
class FieldConfiguratorComponent {
|
|
@@ -7551,6 +8055,12 @@ class FieldConfiguratorComponent {
|
|
|
7551
8055
|
* Defaults to true (show everything).
|
|
7552
8056
|
*/
|
|
7553
8057
|
showOptionConfig = true;
|
|
8058
|
+
/**
|
|
8059
|
+
* Field names that must always remain enabled and cannot be toggled off.
|
|
8060
|
+
* When a locked field is selected, the IS ENABLED / READ ONLY / DISABLED
|
|
8061
|
+
* controls are hidden so the user cannot accidentally disable the field.
|
|
8062
|
+
*/
|
|
8063
|
+
lockedFieldNames = [];
|
|
7554
8064
|
store = inject(FieldConfiguratorService);
|
|
7555
8065
|
cdr = inject(ChangeDetectorRef);
|
|
7556
8066
|
schemaMapOverride = inject(BUILDER_FIELD_TYPE_SCHEMA_MAP, { optional: true });
|
|
@@ -7591,11 +8101,11 @@ class FieldConfiguratorComponent {
|
|
|
7591
8101
|
}
|
|
7592
8102
|
}
|
|
7593
8103
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FieldConfiguratorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
7594
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: FieldConfiguratorComponent, isStandalone: false, selector: "lib-field-configurator", inputs: { schema: "schema", showOptionConfig: "showOptionConfig" }, outputs: { schemaChange: "schemaChange" }, providers: [FieldConfiguratorService], usesOnChanges: true, ngImport: i0, template: "<div class=\"fb-configurator\">\n <div class=\"fb-configurator__layout\">\n <div class=\"fb-configurator__sidebar\">\n <lib-configurator-tree\n [tree]=\"store.tree()\"\n [selectedFieldPath]=\"store.selectedFieldPath()\"\n (selectField)=\"onFieldSelected($event)\"\n (toggleExpanded)=\"onNodeToggleExpanded($event)\"\n />\n </div>\n\n <div class=\"fb-configurator__main\">\n <lib-configurator-config-panel\n [selectedField]=\"store.selectedField()\"\n [selectedFieldInfo]=\"store.selectedFieldInfo()\"\n [builderFieldType]=\"store.selectedFieldBuilderType()\"\n [fieldTypeSchemaMap]=\"finalFieldTypeSchemaMap\"\n [showOptionConfig]=\"showOptionConfig\"\n (configChange)=\"onConfigChange($event)\"\n (typeChange)=\"onTypeChange($event)\"\n />\n </div>\n </div>\n</div>\n", styles: [".fb-configurator{display:flex;flex-direction:column;height:100%;width:100%;background:var(--fb-fc-bg, #ffffff);font-family:var(--fb-font-family, \"Inter\", sans-serif)}.fb-configurator__layout{flex:1;display:flex;overflow:hidden;width:100%}@media(max-width:768px){.fb-configurator__layout{flex-direction:column}}.fb-configurator__sidebar{flex:0 0 280px;border-right:1px solid var(--fb-fc-border, #e5e7eb);background:var(--fb-fc-sidebar-bg, #f9fafb);display:flex;flex-direction:column;overflow:hidden;min-width:0}@media(max-width:1024px){.fb-configurator__sidebar{flex:0 0 250px}}@media(min-width:1440px){.fb-configurator__sidebar{flex:0 0 300px}}@media(max-width:768px){.fb-configurator__sidebar{flex:0 0 auto;width:100%;border-right:none;border-bottom:1px solid var(--fb-fc-border, #e5e7eb);max-height:40vh}}@media(max-width:640px){.fb-configurator__sidebar{max-height:35vh}}.fb-configurator__main{flex:1;min-width:0;overflow-y:auto;overflow-x:hidden;background:var(--fb-fc-main-bg, #ffffff);display:flex;flex-direction:column}@media(max-width:768px){.fb-configurator__main{width:100%;flex:1;min-height:0}}\n"], dependencies: [{ kind: "component", type: ConfiguratorTreeComponent, selector: "lib-configurator-tree", inputs: ["tree", "selectedFieldPath"], outputs: ["selectField", "toggleExpanded"] }, { kind: "component", type: ConfiguratorConfigPanelComponent, selector: "lib-configurator-config-panel", inputs: ["selectedField", "selectedFieldInfo", "builderFieldType", "fieldTypeSchemaMap", "showOptionConfig"], outputs: ["configChange", "typeChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
8104
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: FieldConfiguratorComponent, isStandalone: false, selector: "lib-field-configurator", inputs: { schema: "schema", showOptionConfig: "showOptionConfig", lockedFieldNames: "lockedFieldNames" }, outputs: { schemaChange: "schemaChange" }, providers: [FieldConfiguratorService], usesOnChanges: true, ngImport: i0, template: "<div class=\"fb-configurator\">\n <div class=\"fb-configurator__layout\">\n <div class=\"fb-configurator__sidebar\">\n <lib-configurator-tree\n [tree]=\"store.tree()\"\n [selectedFieldPath]=\"store.selectedFieldPath()\"\n (selectField)=\"onFieldSelected($event)\"\n (toggleExpanded)=\"onNodeToggleExpanded($event)\"\n />\n </div>\n\n <div class=\"fb-configurator__main\">\n <lib-configurator-config-panel\n [selectedField]=\"store.selectedField()\"\n [selectedFieldInfo]=\"store.selectedFieldInfo()\"\n [builderFieldType]=\"store.selectedFieldBuilderType()\"\n [fieldTypeSchemaMap]=\"finalFieldTypeSchemaMap\"\n [showOptionConfig]=\"showOptionConfig\"\n [lockedFieldNames]=\"lockedFieldNames\"\n (configChange)=\"onConfigChange($event)\"\n (typeChange)=\"onTypeChange($event)\"\n />\n </div>\n </div>\n</div>\n", styles: [".fb-configurator{display:flex;flex-direction:column;height:100%;width:100%;background:var(--fb-fc-bg, #ffffff);font-family:var(--fb-font-family, \"Inter\", sans-serif)}.fb-configurator__layout{flex:1;display:flex;overflow:hidden;width:100%}@media(max-width:768px){.fb-configurator__layout{flex-direction:column}}.fb-configurator__sidebar{flex:0 0 280px;border-right:1px solid var(--fb-fc-border, #e5e7eb);background:var(--fb-fc-sidebar-bg, #f9fafb);display:flex;flex-direction:column;overflow:hidden;min-width:0}@media(max-width:1024px){.fb-configurator__sidebar{flex:0 0 250px}}@media(min-width:1440px){.fb-configurator__sidebar{flex:0 0 300px}}@media(max-width:768px){.fb-configurator__sidebar{flex:0 0 auto;width:100%;border-right:none;border-bottom:1px solid var(--fb-fc-border, #e5e7eb);max-height:40vh}}@media(max-width:640px){.fb-configurator__sidebar{max-height:35vh}}.fb-configurator__main{flex:1;min-width:0;overflow-y:auto;overflow-x:hidden;background:var(--fb-fc-main-bg, #ffffff);display:flex;flex-direction:column}@media(max-width:768px){.fb-configurator__main{width:100%;flex:1;min-height:0}}\n"], dependencies: [{ kind: "component", type: ConfiguratorTreeComponent, selector: "lib-configurator-tree", inputs: ["tree", "selectedFieldPath"], outputs: ["selectField", "toggleExpanded"] }, { kind: "component", type: ConfiguratorConfigPanelComponent, selector: "lib-configurator-config-panel", inputs: ["selectedField", "selectedFieldInfo", "builderFieldType", "fieldTypeSchemaMap", "showOptionConfig", "lockedFieldNames"], outputs: ["configChange", "typeChange"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
7595
8105
|
}
|
|
7596
8106
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FieldConfiguratorComponent, decorators: [{
|
|
7597
8107
|
type: Component,
|
|
7598
|
-
args: [{ selector: 'lib-field-configurator', standalone: false, providers: [FieldConfiguratorService], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-configurator\">\n <div class=\"fb-configurator__layout\">\n <div class=\"fb-configurator__sidebar\">\n <lib-configurator-tree\n [tree]=\"store.tree()\"\n [selectedFieldPath]=\"store.selectedFieldPath()\"\n (selectField)=\"onFieldSelected($event)\"\n (toggleExpanded)=\"onNodeToggleExpanded($event)\"\n />\n </div>\n\n <div class=\"fb-configurator__main\">\n <lib-configurator-config-panel\n [selectedField]=\"store.selectedField()\"\n [selectedFieldInfo]=\"store.selectedFieldInfo()\"\n [builderFieldType]=\"store.selectedFieldBuilderType()\"\n [fieldTypeSchemaMap]=\"finalFieldTypeSchemaMap\"\n [showOptionConfig]=\"showOptionConfig\"\n (configChange)=\"onConfigChange($event)\"\n (typeChange)=\"onTypeChange($event)\"\n />\n </div>\n </div>\n</div>\n", styles: [".fb-configurator{display:flex;flex-direction:column;height:100%;width:100%;background:var(--fb-fc-bg, #ffffff);font-family:var(--fb-font-family, \"Inter\", sans-serif)}.fb-configurator__layout{flex:1;display:flex;overflow:hidden;width:100%}@media(max-width:768px){.fb-configurator__layout{flex-direction:column}}.fb-configurator__sidebar{flex:0 0 280px;border-right:1px solid var(--fb-fc-border, #e5e7eb);background:var(--fb-fc-sidebar-bg, #f9fafb);display:flex;flex-direction:column;overflow:hidden;min-width:0}@media(max-width:1024px){.fb-configurator__sidebar{flex:0 0 250px}}@media(min-width:1440px){.fb-configurator__sidebar{flex:0 0 300px}}@media(max-width:768px){.fb-configurator__sidebar{flex:0 0 auto;width:100%;border-right:none;border-bottom:1px solid var(--fb-fc-border, #e5e7eb);max-height:40vh}}@media(max-width:640px){.fb-configurator__sidebar{max-height:35vh}}.fb-configurator__main{flex:1;min-width:0;overflow-y:auto;overflow-x:hidden;background:var(--fb-fc-main-bg, #ffffff);display:flex;flex-direction:column}@media(max-width:768px){.fb-configurator__main{width:100%;flex:1;min-height:0}}\n"] }]
|
|
8108
|
+
args: [{ selector: 'lib-field-configurator', standalone: false, providers: [FieldConfiguratorService], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"fb-configurator\">\n <div class=\"fb-configurator__layout\">\n <div class=\"fb-configurator__sidebar\">\n <lib-configurator-tree\n [tree]=\"store.tree()\"\n [selectedFieldPath]=\"store.selectedFieldPath()\"\n (selectField)=\"onFieldSelected($event)\"\n (toggleExpanded)=\"onNodeToggleExpanded($event)\"\n />\n </div>\n\n <div class=\"fb-configurator__main\">\n <lib-configurator-config-panel\n [selectedField]=\"store.selectedField()\"\n [selectedFieldInfo]=\"store.selectedFieldInfo()\"\n [builderFieldType]=\"store.selectedFieldBuilderType()\"\n [fieldTypeSchemaMap]=\"finalFieldTypeSchemaMap\"\n [showOptionConfig]=\"showOptionConfig\"\n [lockedFieldNames]=\"lockedFieldNames\"\n (configChange)=\"onConfigChange($event)\"\n (typeChange)=\"onTypeChange($event)\"\n />\n </div>\n </div>\n</div>\n", styles: [".fb-configurator{display:flex;flex-direction:column;height:100%;width:100%;background:var(--fb-fc-bg, #ffffff);font-family:var(--fb-font-family, \"Inter\", sans-serif)}.fb-configurator__layout{flex:1;display:flex;overflow:hidden;width:100%}@media(max-width:768px){.fb-configurator__layout{flex-direction:column}}.fb-configurator__sidebar{flex:0 0 280px;border-right:1px solid var(--fb-fc-border, #e5e7eb);background:var(--fb-fc-sidebar-bg, #f9fafb);display:flex;flex-direction:column;overflow:hidden;min-width:0}@media(max-width:1024px){.fb-configurator__sidebar{flex:0 0 250px}}@media(min-width:1440px){.fb-configurator__sidebar{flex:0 0 300px}}@media(max-width:768px){.fb-configurator__sidebar{flex:0 0 auto;width:100%;border-right:none;border-bottom:1px solid var(--fb-fc-border, #e5e7eb);max-height:40vh}}@media(max-width:640px){.fb-configurator__sidebar{max-height:35vh}}.fb-configurator__main{flex:1;min-width:0;overflow-y:auto;overflow-x:hidden;background:var(--fb-fc-main-bg, #ffffff);display:flex;flex-direction:column}@media(max-width:768px){.fb-configurator__main{width:100%;flex:1;min-height:0}}\n"] }]
|
|
7599
8109
|
}], propDecorators: { schema: [{
|
|
7600
8110
|
type: Input,
|
|
7601
8111
|
args: [{ required: true }]
|
|
@@ -7603,6 +8113,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
|
|
|
7603
8113
|
type: Output
|
|
7604
8114
|
}], showOptionConfig: [{
|
|
7605
8115
|
type: Input
|
|
8116
|
+
}], lockedFieldNames: [{
|
|
8117
|
+
type: Input
|
|
7606
8118
|
}] } });
|
|
7607
8119
|
|
|
7608
8120
|
const COMPONENTS = [
|
|
@@ -8248,7 +8760,7 @@ class FilterSidebarComponent {
|
|
|
8248
8760
|
useExisting: forwardRef(() => FilterSidebarComponent),
|
|
8249
8761
|
multi: true
|
|
8250
8762
|
}
|
|
8251
|
-
], ngImport: i0, template: "<div class=\"cc-filter-sidebar\" [ngStyle]=\"sidebarStyles\" [class.collapsed]=\"isCollapsed\">\n\n <!-- Header -->\n <div class=\"cc-filter-header\" *ngIf=\"config.header?.visible !== false\">\n <div class=\"cc-header-content\">\n <span class=\"cc-header-title\" *ngIf=\"config.header?.title\">\n <span *ngIf=\"config.header?.icon\" [class]=\"config.header?.icon\" class=\"cc-header-icon material-icons\">\n <!-- {{config.header?.icon }} -->\n </span>\n {{ config.header?.title }}\n </span>\n <ng-content select=\"[header-actions]\"></ng-content>\n </div>\n <button type=\"button\" *ngIf=\"config.settings?.collapsible\" (click)=\"toggleCollapse()\" class=\"cc-icon-btn\"\n [attr.aria-label]=\"isCollapsed ? config.labels?.expandAriaLabel : config.labels?.collapseAriaLabel\">\n <span class=\"material-icons\">{{ isCollapsed ? 'menu_open' : 'menu' }}</span>\n </button>\n <button type=\"button\" *ngIf=\"config.header?.showClose && !isCollapsed\" (click)=\"onClose()\"\n class=\"cc-icon-btn cc-close-btn\" [attr.aria-label]=\"config.labels?.closeAriaLabel\">\n <span class=\"material-icons\">close</span>\n </button>\n <!-- Code snippet debug (optional) -->\n <button type=\"button\" *ngIf=\"config.settings?.showCodeSnippet\" (click)=\"onShowCodeSnippet()\" class=\"cc-icon-btn\"\n [attr.aria-label]=\"config.labels?.codeSnippetAriaLabel\">\n <span class=\"material-icons\">code</span>\n </button>\n </div>\n\n <!-- Content (Scrollable) -->\n <div class=\"cc-filter-content\" [class.hidden]=\"isCollapsed\">\n <ng-container *ngFor=\"let item of config.items; trackBy: trackByFn\">\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { item: item }\"></ng-container>\n </ng-container>\n </div>\n\n <!-- Footer -->\n <div class=\"cc-filter-footer\" *ngIf=\"config.footer?.visible !== false && !isCollapsed\">\n <lib-button variant=\"outline\" color=\"warn\" class=\"cc-btn-clear\"\n *ngIf=\"config.footer?.clearButton?.visible !== false\" (click)=\"clearFilters()\">\n {{ config.footer?.clearButton?.label }}\n </lib-button>\n <lib-button variant=\"primary\" color=\"warn\" class=\"cc-btn-apply\"\n *ngIf=\"config.footer?.applyButton?.visible !== false\" [disabled]=\"config.footer?.applyButton?.disabled ?? false\"\n (click)=\"applyFilters()\">\n {{ config.footer?.applyButton?.label }}\n </lib-button>\n </div>\n</div>\n\n<!-- Recursive Template for Items -->\n<ng-template #itemTemplate let-item=\"item\">\n <div class=\"cc-filter-item\" [ngClass]=\"['type-' + item.type]\"\n [style.display]=\"item.visible === false ? 'none' : 'block'\" [ngStyle]=\"item.styles\">\n\n <ng-container [ngSwitch]=\"item.type\">\n\n <!-- Input -->\n <lib-input *ngSwitchCase=\"'input'\" [config]=\"item.inputConfig\" [label]=\"item.label\"\n [ngModel]=\"filters[item.key]\" (valueChange)=\"onValueChange(item.key, $event)\">\n </lib-input>\n\n <!-- Dropdown -->\n <lib-dropdown *ngSwitchCase=\"'dropdown'\" [config]=\"item.dropdownConfig\" [label]=\"item.label\"\n [ngModel]=\"filters[item.key]\" (selectionChange)=\"onValueChange(item.key, $event)\">\n </lib-dropdown>\n\n <!-- Checkbox -->\n <lib-checkbox *ngSwitchCase=\"'checkbox'\" [config]=\"item.checkboxConfig\" [label]=\"item.label\"\n [checked]=\"!!filters[item.key]\" (checkedChange)=\"onValueChange(item.key, $event)\">\n </lib-checkbox>\n\n <!-- Radio -->\n <lib-radio *ngSwitchCase=\"'radio'\" [config]=\"item.radioConfig\" [label]=\"item.label\"\n [ngModel]=\"filters[item.key]\" (selectionChange)=\"onValueChange(item.key, $event)\">\n </lib-radio>\n\n <!-- Toggle -->\n <lib-toggle *ngSwitchCase=\"'toggle'\" [config]=\"item.toggleConfig\" [label]=\"item.label\"\n [checked]=\"filters[item.key]\" (toggleChange)=\"onValueChange(item.key, $event)\">\n </lib-toggle>\n\n <!-- Datepicker -->\n <lib-datepicker *ngSwitchCase=\"'datepicker'\" [config]=\"item.datepickerConfig\" [label]=\"item.label\"\n [ngModel]=\"filters[item.key]\" (dateChange)=\"onValueChange(item.key, $event)\">\n </lib-datepicker>\n\n <!-- Search -->\n <lib-search *ngSwitchCase=\"'active-search'\" [config]=\"item.searchConfig\" [label]=\"item.label\"\n [ngModel]=\"filters[item.key]\" (search)=\"onValueChange(item.key, $event)\">\n </lib-search>\n\n <!-- Group / Accordion -->\n <div *ngSwitchCase=\"'group'\" class=\"cc-accordion-item\" [class.expanded]=\"item.expanded\">\n <button type=\"button\" class=\"cc-accordion-header\" (click)=\"item.expanded = !item.expanded\">\n <span class=\"cc-accordion-title\">{{ item.label }}</span>\n <span class=\"material-icons cc-accordion-icon\">{{ item.expanded ? 'expand_less' : 'expand_more'\n }}</span>\n </button>\n\n <div class=\"cc-accordion-body\" *ngIf=\"item.expanded\">\n <ng-container *ngFor=\"let child of item.children; trackBy: trackByFn\">\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { item: child }\"></ng-container>\n </ng-container>\n </div>\n </div>\n\n <!-- Custom Divider -->\n <div *ngSwitchCase=\"'divider'\" class=\"cc-filter-divider\"></div>\n\n </ng-container>\n </div>\n</ng-template>", styles: [":host{display:block;height:100%;--cc-filter-sidebar-width: 100%;--cc-filter-sidebar-padding: 1.5rem;--cc-filter-sidebar-gap: 1.5rem;--cc-filter-sidebar-bg: #FEFEFE;--cc-filter-sidebar-border-radius: 0;--cc-filter-header-height: 3rem;--cc-filter-header-title-size: 1.125rem;--cc-filter-header-title-weight: 500;--cc-filter-header-title-color: #202124;--cc-filter-footer-padding: 1rem 0 0 0;--cc-filter-footer-gap: 1rem}.cc-filter-sidebar{display:flex;flex-direction:column;height:100%;width:var(--cc-filter-sidebar-width, 280px);background-color:var(--cc-filter-sidebar-bg, #FEFEFE);padding:var(--cc-filter-sidebar-padding, 1.5rem);box-sizing:border-box;overflow:hidden;border-radius:var(--cc-filter-sidebar-border-radius, 0);transition:width .3s ease,padding .3s ease}.cc-filter-sidebar.collapsed{width:60px!important;padding:1.5rem .5rem}.cc-filter-sidebar.collapsed .cc-filter-header{justify-content:center;margin-bottom:0}.cc-filter-sidebar.collapsed .cc-filter-header .cc-header-content,.cc-filter-sidebar.collapsed .cc-filter-header .cc-close-btn,.cc-filter-sidebar.collapsed .cc-filter-content{display:none}.cc-filter-header{display:flex;align-items:center;justify-content:space-between;min-height:var(--cc-filter-header-height, 3rem);margin-bottom:1rem;flex-shrink:0}.cc-filter-header .cc-header-content{display:flex;align-items:center;gap:.5rem;flex:1}.cc-filter-header .cc-header-title{font-size:var(--cc-filter-header-title-size, 1.125rem);font-weight:var(--cc-filter-header-title-weight, 500);color:var(--cc-filter-header-title-color, #202124);display:flex;align-items:center;gap:.5rem}.cc-filter-content{flex:1;overflow-y:auto;display:flex;flex-direction:column;gap:var(--cc-filter-sidebar-gap, 1.5rem);padding-right:.25rem}.cc-filter-content::-webkit-scrollbar{width:6px}.cc-filter-content::-webkit-scrollbar-thumb{background-color:#e0e0e0;border-radius:3px}.cc-accordion-item{border-bottom:1px solid #E0E0E0}.cc-accordion-item:last-child{border-bottom:none}.cc-accordion-item .cc-accordion-header{display:flex;align-items:center;justify-content:space-between;width:100%;padding:.75rem 0;background:none;border:none;cursor:pointer;text-align:left;outline:none}.cc-accordion-item .cc-accordion-header .cc-accordion-title{font-size:.875rem;font-weight:500;color:#202124}.cc-accordion-item .cc-accordion-header .cc-accordion-icon{color:#5f6368;transition:transform .2s}.cc-accordion-item .cc-accordion-body{padding:0 0 1rem;display:flex;flex-direction:column;gap:1rem;animation:slideDown .2s ease-out}@keyframes slideDown{0%{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:translateY(0)}}.cc-group-content{display:flex;flex-direction:column;gap:1rem}.cc-filter-divider{height:1px;background-color:#e0e0e0;margin:.5rem 0}.cc-filter-footer{display:flex;gap:var(--cc-filter-footer-gap, 1rem);padding:var(--cc-filter-footer-padding, 1rem 0 0 0);margin-top:auto;flex-shrink:0}.cc-filter-footer button{flex:1}.cc-icon-btn{background:none;border:none;cursor:pointer;color:#5f6368;padding:.5rem;border-radius:50%;display:flex;align-items:center;justify-content:center}.cc-icon-btn:hover{background-color:#0000000a}.cc-close-btn{margin-right:-.5rem}\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: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { 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$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: InputComponent, selector: "lib-input", inputs: ["config", "labels", "type", "label", "placeholder", "disabled", "required", "readonly", "clearable", "maxLength", "minLength", "min", "max", "pattern", "errorMessage", "helperText", "rows", "prefixIcon", "suffixIcon", "value", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "padding", "fontWeight", "color", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor", "boxShadow"], outputs: ["valueChange", "inputBlur", "inputFocus"] }, { kind: "component", type: DropdownComponent, selector: "lib-dropdown", inputs: ["config", "labels", "options", "placeholder", "label", "multiple", "searchable", "clearable", "disabled", "required", "errorMessage", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "padding", "fontWeight", "color", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor", "boxShadow"], outputs: ["selectionChange"] }, { kind: "component", type: CheckboxComponent, selector: "lib-checkbox", inputs: ["config", "labels", "label", "checked", "disabled", "required", "indeterminate", "options", "labelPosition", "color", "borderRadius", "value", "errorMessage", "width", "height", "fontSize", "fontWeight", "labelColor", "labelFontSize", "labelFontWeight", "gap", "fontFamily", "backgroundColor", "borderColor", "borderWidth", "padding", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor", "boxShadow", "size", "checkedColor", "uncheckedColor", "groupLabelColor", "groupLabelFontSize", "groupLabelFontWeight"], outputs: ["checkedChange"] }, { kind: "component", type: RadioComponent, selector: "lib-radio", inputs: ["config", "label", "options", "disabled", "required", "labelPosition", "color", "layout", "labels", "gap", "labelColor", "checkedColor", "uncheckedColor", "fontSize", "fontWeight", "fontFamily", "groupLabelColor", "groupLabelFontSize", "groupLabelFontWeight", "disabledColor", "errorColor", "size", "borderRadius", "labelFontSize", "labelFontWeight"], outputs: ["selectionChange"] }, { kind: "component", type: ToggleComponent, selector: "lib-toggle", inputs: ["config", "labels", "label", "checked", "disabled", "required", "labelPosition", "color", "labelColor", "uncheckedColor", "checkedColor", "thumbColor", "checkedThumbColor", "fontSize", "fontWeight", "fontFamily", "toggleWidth", "toggleHeight", "gap", "sliderColor", "labelFontSize", "labelFontWeight", "disabledColor"], outputs: ["toggleChange"] }, { kind: "component", type: DatepickerComponent, selector: "lib-datepicker", inputs: ["config", "labels", "label", "placeholder", "disabled", "required", "minDate", "maxDate", "isRange", "startView", "value", "startDate", "endDate", "errorMessage", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "padding", "fontWeight", "color", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor", "boxShadow"], outputs: ["dateChange"] }, { kind: "component", type: SearchComponent, selector: "lib-search", inputs: ["config", "labels", "placeholder", "label", "disabled", "debounceMs", "clearable", "value", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "border", "padding", "fontWeight", "color", "textColor", "iconColor", "placeholderColor", "focusBorderColor", "disabledBackgroundColor", "disabledColor", "boxShadow"], outputs: ["search", "clear"] }, { kind: "component", type: ButtonComponent, selector: "lib-button", inputs: ["variant", "type", "disabled", "width", "height", "borderRadius", "fontSize", "fontWeight", "backgroundColor", "color", "border", "icon", "labels"] }] });
|
|
8763
|
+
], ngImport: i0, template: "<div class=\"cc-filter-sidebar\" [ngStyle]=\"sidebarStyles\" [class.collapsed]=\"isCollapsed\">\n\n <!-- Header -->\n <div class=\"cc-filter-header\" *ngIf=\"config.header?.visible !== false\">\n <div class=\"cc-header-content\">\n <span class=\"cc-header-title\" *ngIf=\"config.header?.title\">\n <span *ngIf=\"config.header?.icon\" [class]=\"config.header?.icon\" class=\"cc-header-icon material-icons\">\n <!-- {{config.header?.icon }} -->\n </span>\n {{ config.header?.title }}\n </span>\n <ng-content select=\"[header-actions]\"></ng-content>\n </div>\n <button type=\"button\" *ngIf=\"config.settings?.collapsible\" (click)=\"toggleCollapse()\" class=\"cc-icon-btn\"\n [attr.aria-label]=\"isCollapsed ? config.labels?.expandAriaLabel : config.labels?.collapseAriaLabel\">\n <span class=\"material-icons\">{{ isCollapsed ? 'menu_open' : 'menu' }}</span>\n </button>\n <button type=\"button\" *ngIf=\"config.header?.showClose && !isCollapsed\" (click)=\"onClose()\"\n class=\"cc-icon-btn cc-close-btn\" [attr.aria-label]=\"config.labels?.closeAriaLabel\">\n <span class=\"material-icons\">close</span>\n </button>\n <!-- Code snippet debug (optional) -->\n <button type=\"button\" *ngIf=\"config.settings?.showCodeSnippet\" (click)=\"onShowCodeSnippet()\" class=\"cc-icon-btn\"\n [attr.aria-label]=\"config.labels?.codeSnippetAriaLabel\">\n <span class=\"material-icons\">code</span>\n </button>\n </div>\n\n <!-- Content (Scrollable) -->\n <div class=\"cc-filter-content\" [class.hidden]=\"isCollapsed\">\n <ng-container *ngFor=\"let item of config.items; trackBy: trackByFn\">\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { item: item }\"></ng-container>\n </ng-container>\n </div>\n\n <!-- Footer -->\n <div class=\"cc-filter-footer\" *ngIf=\"config.footer?.visible !== false && !isCollapsed\">\n <lib-button variant=\"outline\" color=\"warn\" class=\"cc-btn-clear\"\n *ngIf=\"config.footer?.clearButton?.visible !== false\" (click)=\"clearFilters()\">\n {{ config.footer?.clearButton?.label }}\n </lib-button>\n <lib-button variant=\"primary\" color=\"warn\" class=\"cc-btn-apply\"\n *ngIf=\"config.footer?.applyButton?.visible !== false\" [disabled]=\"config.footer?.applyButton?.disabled ?? false\"\n (click)=\"applyFilters()\">\n {{ config.footer?.applyButton?.label }}\n </lib-button>\n </div>\n</div>\n\n<!-- Recursive Template for Items -->\n<ng-template #itemTemplate let-item=\"item\">\n <div class=\"cc-filter-item\" [ngClass]=\"['type-' + item.type]\"\n [style.display]=\"item.visible === false ? 'none' : 'block'\" [ngStyle]=\"item.styles\">\n\n <ng-container [ngSwitch]=\"item.type\">\n\n <!-- Input -->\n <lib-input *ngSwitchCase=\"'input'\" [config]=\"item.inputConfig\" [label]=\"item.label\"\n [ngModel]=\"filters[item.key]\" (valueChange)=\"onValueChange(item.key, $event)\">\n </lib-input>\n\n <!-- Dropdown -->\n <lib-dropdown *ngSwitchCase=\"'dropdown'\" [config]=\"item.dropdownConfig\" [label]=\"item.label\"\n [ngModel]=\"filters[item.key]\" (selectionChange)=\"onValueChange(item.key, $event)\">\n </lib-dropdown>\n\n <!-- Checkbox -->\n <lib-checkbox *ngSwitchCase=\"'checkbox'\" [config]=\"item.checkboxConfig\" [label]=\"item.label\"\n [checked]=\"!!filters[item.key]\" (checkedChange)=\"onValueChange(item.key, $event)\">\n </lib-checkbox>\n\n <!-- Radio -->\n <lib-radio *ngSwitchCase=\"'radio'\" [config]=\"item.radioConfig\" [label]=\"item.label\"\n [ngModel]=\"filters[item.key]\" (selectionChange)=\"onValueChange(item.key, $event)\">\n </lib-radio>\n\n <!-- Toggle -->\n <lib-toggle *ngSwitchCase=\"'toggle'\" [config]=\"item.toggleConfig\" [label]=\"item.label\"\n [checked]=\"filters[item.key]\" (toggleChange)=\"onValueChange(item.key, $event)\">\n </lib-toggle>\n\n <!-- Datepicker -->\n <lib-datepicker *ngSwitchCase=\"'datepicker'\" [config]=\"item.datepickerConfig\" [label]=\"item.label\"\n [ngModel]=\"filters[item.key]\" (dateChange)=\"onValueChange(item.key, $event)\">\n </lib-datepicker>\n\n <!-- Search -->\n <lib-search *ngSwitchCase=\"'active-search'\" [config]=\"item.searchConfig\" [label]=\"item.label\"\n [ngModel]=\"filters[item.key]\" (search)=\"onValueChange(item.key, $event)\">\n </lib-search>\n\n <!-- Group / Accordion -->\n <div *ngSwitchCase=\"'group'\" class=\"cc-accordion-item\" [class.expanded]=\"item.expanded\">\n <button type=\"button\" class=\"cc-accordion-header\" (click)=\"item.expanded = !item.expanded\">\n <span class=\"cc-accordion-title\">{{ item.label }}</span>\n <span class=\"material-icons cc-accordion-icon\">{{ item.expanded ? 'expand_less' : 'expand_more'\n }}</span>\n </button>\n\n <div class=\"cc-accordion-body\" *ngIf=\"item.expanded\">\n <ng-container *ngFor=\"let child of item.children; trackBy: trackByFn\">\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { item: child }\"></ng-container>\n </ng-container>\n </div>\n </div>\n\n <!-- Custom Divider -->\n <div *ngSwitchCase=\"'divider'\" class=\"cc-filter-divider\"></div>\n\n </ng-container>\n </div>\n</ng-template>", styles: [":host{display:block;height:100%;--cc-filter-sidebar-width: 100%;--cc-filter-sidebar-padding: 1.5rem;--cc-filter-sidebar-gap: 1.5rem;--cc-filter-sidebar-bg: #FEFEFE;--cc-filter-sidebar-border-radius: 0;--cc-filter-header-height: 3rem;--cc-filter-header-title-size: 1.125rem;--cc-filter-header-title-weight: 500;--cc-filter-header-title-color: #202124;--cc-filter-footer-padding: 1rem 0 0 0;--cc-filter-footer-gap: 1rem}.cc-filter-sidebar{display:flex;flex-direction:column;height:100%;width:var(--cc-filter-sidebar-width, 280px);background-color:var(--cc-filter-sidebar-bg, #FEFEFE);padding:var(--cc-filter-sidebar-padding, 1.5rem);box-sizing:border-box;overflow:hidden;border-radius:var(--cc-filter-sidebar-border-radius, 0);transition:width .3s ease,padding .3s ease}.cc-filter-sidebar.collapsed{width:60px!important;padding:1.5rem .5rem}.cc-filter-sidebar.collapsed .cc-filter-header{justify-content:center;margin-bottom:0}.cc-filter-sidebar.collapsed .cc-filter-header .cc-header-content,.cc-filter-sidebar.collapsed .cc-filter-header .cc-close-btn,.cc-filter-sidebar.collapsed .cc-filter-content{display:none}.cc-filter-header{display:flex;align-items:center;justify-content:space-between;min-height:var(--cc-filter-header-height, 3rem);margin-bottom:1rem;flex-shrink:0}.cc-filter-header .cc-header-content{display:flex;align-items:center;gap:.5rem;flex:1}.cc-filter-header .cc-header-title{font-size:var(--cc-filter-header-title-size, 1.125rem);font-weight:var(--cc-filter-header-title-weight, 500);color:var(--cc-filter-header-title-color, #202124);display:flex;align-items:center;gap:.5rem}.cc-filter-content{flex:1;overflow-y:auto;display:flex;flex-direction:column;gap:var(--cc-filter-sidebar-gap, 1.5rem);padding-right:.25rem}.cc-filter-content::-webkit-scrollbar{width:6px}.cc-filter-content::-webkit-scrollbar-thumb{background-color:#e0e0e0;border-radius:3px}.cc-accordion-item{border-bottom:1px solid #E0E0E0}.cc-accordion-item:last-child{border-bottom:none}.cc-accordion-item .cc-accordion-header{display:flex;align-items:center;justify-content:space-between;width:100%;padding:.75rem 0;background:none;border:none;cursor:pointer;text-align:left;outline:none}.cc-accordion-item .cc-accordion-header .cc-accordion-title{font-size:.875rem;font-weight:500;color:#202124}.cc-accordion-item .cc-accordion-header .cc-accordion-icon{color:#5f6368;transition:transform .2s}.cc-accordion-item .cc-accordion-body{padding:0 0 1rem;display:flex;flex-direction:column;gap:1rem;animation:slideDown .2s ease-out}@keyframes slideDown{0%{opacity:0;transform:translateY(-5px)}to{opacity:1;transform:translateY(0)}}.cc-group-content{display:flex;flex-direction:column;gap:1rem}.cc-filter-divider{height:1px;background-color:#e0e0e0;margin:.5rem 0}.cc-filter-footer{display:flex;gap:var(--cc-filter-footer-gap, 1rem);padding:var(--cc-filter-footer-padding, 1rem 0 0 0);margin-top:auto;flex-shrink:0}.cc-filter-footer button{flex:1}.cc-icon-btn{background:none;border:none;cursor:pointer;color:#5f6368;padding:.5rem;border-radius:50%;display:flex;align-items:center;justify-content:center}.cc-icon-btn:hover{background-color:#0000000a}.cc-close-btn{margin-right:-.5rem}\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: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { 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$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: InputComponent, selector: "lib-input", inputs: ["config", "labels", "type", "label", "placeholder", "disabled", "required", "readonly", "clearable", "maxLength", "minLength", "min", "max", "pattern", "errorMessage", "helperText", "rows", "prefixIcon", "suffixIcon", "value", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "padding", "fontWeight", "color", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor", "boxShadow"], outputs: ["valueChange", "inputBlur", "inputFocus"] }, { kind: "component", type: DropdownComponent, selector: "lib-dropdown", inputs: ["config", "labels", "options", "selectedValue", "placeholder", "label", "multiple", "searchable", "clearable", "disabled", "required", "errorMessage", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "padding", "fontWeight", "color", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor", "boxShadow"], outputs: ["selectionChange"] }, { kind: "component", type: CheckboxComponent, selector: "lib-checkbox", inputs: ["config", "labels", "label", "checked", "disabled", "required", "indeterminate", "options", "labelPosition", "color", "borderRadius", "value", "errorMessage", "width", "height", "fontSize", "fontWeight", "labelColor", "labelFontSize", "labelFontWeight", "gap", "fontFamily", "backgroundColor", "borderColor", "borderWidth", "padding", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor", "boxShadow", "size", "checkedColor", "uncheckedColor", "groupLabelColor", "groupLabelFontSize", "groupLabelFontWeight"], outputs: ["checkedChange"] }, { kind: "component", type: RadioComponent, selector: "lib-radio", inputs: ["config", "label", "options", "disabled", "required", "labelPosition", "color", "layout", "labels", "gap", "labelColor", "checkedColor", "uncheckedColor", "fontSize", "fontWeight", "fontFamily", "groupLabelColor", "groupLabelFontSize", "groupLabelFontWeight", "disabledColor", "errorColor", "size", "borderRadius", "labelFontSize", "labelFontWeight"], outputs: ["selectionChange"] }, { kind: "component", type: ToggleComponent, selector: "lib-toggle", inputs: ["config", "labels", "label", "checked", "disabled", "required", "labelPosition", "color", "labelColor", "uncheckedColor", "checkedColor", "thumbColor", "checkedThumbColor", "fontSize", "fontWeight", "fontFamily", "toggleWidth", "toggleHeight", "gap", "sliderColor", "labelFontSize", "labelFontWeight", "disabledColor"], outputs: ["toggleChange"] }, { kind: "component", type: DatepickerComponent, selector: "lib-datepicker", inputs: ["config", "labels", "label", "placeholder", "disabled", "required", "minDate", "maxDate", "isRange", "startView", "value", "startDate", "endDate", "errorMessage", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "padding", "fontWeight", "color", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor", "boxShadow"], outputs: ["dateChange"] }, { kind: "component", type: SearchComponent, selector: "lib-search", inputs: ["config", "labels", "placeholder", "label", "disabled", "debounceMs", "clearable", "value", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "border", "padding", "fontWeight", "color", "textColor", "iconColor", "placeholderColor", "focusBorderColor", "disabledBackgroundColor", "disabledColor", "boxShadow"], outputs: ["search", "clear"] }, { kind: "component", type: ButtonComponent, selector: "lib-button", inputs: ["variant", "type", "disabled", "width", "height", "borderRadius", "fontSize", "fontWeight", "backgroundColor", "color", "border", "icon", "labels"] }] });
|
|
8252
8764
|
}
|
|
8253
8765
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FilterSidebarComponent, decorators: [{
|
|
8254
8766
|
type: Component,
|
|
@@ -9241,7 +9753,7 @@ class FilterComponent {
|
|
|
9241
9753
|
return item.key || index;
|
|
9242
9754
|
}
|
|
9243
9755
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FilterComponent, deps: [{ token: i0.ElementRef }, { token: i1$4.Router }, { token: i1$4.ActivatedRoute }], target: i0.ɵɵFactoryTarget.Component });
|
|
9244
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: FilterComponent, isStandalone: false, selector: "lib-filter", inputs: { config: "config", activeFilters: "activeFilters", columns: "columns", labels: "labels", theme: "theme" }, outputs: { filterChange: "filterChange", columnChange: "columnChange", toggle: "toggle" }, host: { listeners: { "document:click": "onClickOutside($event)" } }, ngImport: i0, template: "<div class=\"cc-filter-container\" [class.is-open]=\"isOpen\" [ngStyle]=\"containerStyles\">\n\n <!-- Trigger Button -->\n <lib-button variant=\"outline\" (click)=\"togglePanel()\" class=\"cc-filter-trigger\"\n [class.active]=\"activeFilterCount > 0\">\n <span class=\"fas fa-filter\"></span> {{ labels?.filterBtn || 'Filter' }}\n <span *ngIf=\"activeFilterCount > 0\" class=\"cc-badge\">{{ activeFilterCount }}</span>\n </lib-button>\n\n <!-- Dropdown Panel -->\n <div class=\"cc-filter-panel mat-elevation-z4\" *ngIf=\"isOpen\" (click)=\"$event.stopPropagation()\">\n\n <!-- Header / Tabs -->\n <div class=\"cc-panel-header\">\n <div class=\"cc-tabs\">\n <div class=\"cc-tab\" [class.active]=\"activeTab === 'filters'\" (click)=\"setActiveTab('filters')\">\n {{ labels.filterBtn }}\n </div>\n <!-- Hide columns tab if no columns config provided -->\n <div class=\"cc-tab\" [class.active]=\"activeTab === 'columns'\" (click)=\"setActiveTab('columns')\"\n *ngIf=\"columns?.length\">\n {{ labels.columns }}\n </div>\n </div>\n <button type=\"button\" class=\"cc-icon-btn\" (click)=\"togglePanel()\">\n <span class=\"material-icons\">close</span>\n </button>\n </div>\n\n <!-- Filters Tab Content -->\n <div class=\"cc-panel-content\" *ngIf=\"activeTab === 'filters'\">\n\n <!-- Dynamic Items -->\n <ng-container *ngFor=\"let item of config.items; trackBy: trackByFn\">\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { item: item }\"></ng-container>\n </ng-container>\n\n </div>\n\n <!-- Columns Tab Content -->\n <div class=\"cc-panel-content\" *ngIf=\"activeTab === 'columns'\">\n <div class=\"cc-columns-actions\">\n <lib-button variant=\"outline\" class=\"cc-btn-text\" (click)=\"toggleAllColumns(true)\">{{ labels.showAll\n }}</lib-button>\n <lib-button variant=\"outline\" class=\"cc-btn-text\" (click)=\"toggleAllColumns(false)\">{{ labels.hideAll\n }}</lib-button>\n </div>\n <div class=\"cc-columns-list\">\n <div *ngFor=\"let col of tempColumns\" class=\"cc-column-item\">\n <lib-checkbox [label]=\"col.label\" [checked]=\"col.visible\" (checkedChange)=\"toggleColumn(col.id)\">\n </lib-checkbox>\n </div>\n </div>\n </div>\n\n <!-- Footer Actions -->\n <div class=\"cc-panel-footer\">\n <lib-button variant=\"outline\" color=\"warn\" class=\"cc-btn-text text-danger\" (click)=\"clearAll()\">\n {{ labels.clear }}\n </lib-button>\n <lib-button class=\"ms-2\" variant=\"primary\" (click)=\"apply()\">\n {{ labels.apply }}\n </lib-button>\n </div>\n\n </div>\n</div>\n\n<!-- Recursive Template for Items -->\n<ng-template #itemTemplate let-item=\"item\">\n <div class=\"cc-filter-item\" [ngClass]=\"['type-' + item.type]\"\n [style.display]=\"item.visible === false ? 'none' : 'block'\" [ngStyle]=\"item.styles\">\n\n <ng-container [ngSwitch]=\"item.type\">\n\n <!-- Input -->\n <lib-input *ngSwitchCase=\"'input'\" [config]=\"item.inputConfig\" [label]=\"item.label\"\n [ngModel]=\"tempFilters[item.key]\" (valueChange)=\"onFilterChange(item.key, $event)\">\n </lib-input>\n\n <!-- Dropdown -->\n <lib-dropdown *ngSwitchCase=\"'dropdown'\" [config]=\"item.dropdownConfig\" [label]=\"item.label\"\n [ngModel]=\"tempFilters[item.key]\" (selectionChange)=\"onFilterChange(item.key, $event)\">\n </lib-dropdown>\n\n <!-- Checkbox -->\n <lib-checkbox *ngSwitchCase=\"'checkbox'\" [config]=\"item.checkboxConfig\" [label]=\"item.label\"\n [ngModel]=\"tempFilters[item.key]\" (checkedChange)=\"onFilterChange(item.key, $event)\">\n </lib-checkbox>\n\n <!-- Radio -->\n <lib-radio *ngSwitchCase=\"'radio'\" [config]=\"item.radioConfig\" [label]=\"item.label\"\n [ngModel]=\"tempFilters[item.key]\" (selectionChange)=\"onFilterChange(item.key, $event)\">\n </lib-radio>\n\n <!-- Toggle -->\n <lib-toggle *ngSwitchCase=\"'toggle'\" [config]=\"item.toggleConfig\" [label]=\"item.label\"\n [checked]=\"tempFilters[item.key]\" (toggleChange)=\"onFilterChange(item.key, $event)\">\n </lib-toggle>\n\n <!-- Datepicker -->\n <lib-datepicker *ngSwitchCase=\"'datepicker'\" [config]=\"item.datepickerConfig\" [label]=\"item.label\"\n [ngModel]=\"tempFilters[item.key]\" (dateChange)=\"onFilterChange(item.key, $event)\">\n </lib-datepicker>\n\n <!-- Search -->\n <lib-search *ngSwitchCase=\"'active-search'\" [config]=\"item.searchConfig\" [label]=\"item.label\"\n [ngModel]=\"tempFilters[item.key]\" (search)=\"onFilterChange(item.key, $event)\">\n </lib-search>\n\n <!-- Group / Accordion -->\n <div *ngSwitchCase=\"'group'\" class=\"cc-accordion-item\" [class.expanded]=\"item.expanded\">\n <button type=\"button\" class=\"cc-accordion-header\" (click)=\"item.expanded = !item.expanded\">\n <span class=\"cc-accordion-title\">{{ item.label }}</span>\n <span class=\"material-icons cc-accordion-icon\">{{ item.expanded ? 'expand_less' : 'expand_more'\n }}</span>\n </button>\n\n <div class=\"cc-accordion-body\" *ngIf=\"item.expanded\">\n <ng-container *ngFor=\"let child of item.children; trackBy: trackByFn\">\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { item: child }\"></ng-container>\n </ng-container>\n </div>\n </div>\n\n <!-- Custom Divider -->\n <div *ngSwitchCase=\"'divider'\" class=\"cc-filter-divider\"></div>\n\n </ng-container>\n </div>\n</ng-template>", styles: [".cc-filter-container{position:relative;display:inline-block;font-family:var(--cc-filter-font-family, \"Roboto\", sans-serif)}.cc-filter-container .cc-filter-trigger ::ng-deep button{display:inline-flex;align-items:center;justify-content:center;gap:8px;width:var(--cc-filter-btn-width, 103px);height:var(--cc-filter-btn-height, 44px);padding:0 16px;background-color:var(--cc-filter-btn-bg, #FCFCFB);border:var(--cc-filter-btn-border, 1px solid #E8EAED);border-radius:var(--cc-filter-btn-radius, 8px);color:var(--cc-filter-btn-text-color, #5F6368);font-size:14px;font-weight:500;transition:all .2s ease}.cc-filter-container .cc-filter-trigger ::ng-deep button mat-icon,.cc-filter-container .cc-filter-trigger ::ng-deep button .material-icons{color:#5f6368}.cc-filter-container .cc-filter-trigger ::ng-deep button:hover{background-color:var(--cc-filter-btn-hover-bg, #f1f3f4)}.cc-filter-container .cc-filter-trigger.active ::ng-deep button{background-color:var(--cc-filter-btn-active-bg, #e3f2fd);color:var(--cc-filter-btn-active-text, #1976d2);border-color:var(--cc-filter-btn-active-text, #1976d2)}.cc-filter-container .cc-filter-trigger.active ::ng-deep button mat-icon,.cc-filter-container .cc-filter-trigger.active ::ng-deep button .material-icons{color:var(--cc-filter-btn-active-text, #1976d2)}.cc-filter-container .cc-filter-trigger .cc-badge{background-color:var(--cc-filter-btn-badge-bg, #f44336);color:var(--cc-filter-btn-badge-text, #fff);font-size:10px;font-weight:700;padding:2px 6px;border-radius:10px;min-width:16px;text-align:center;margin-left:4px;line-height:1}.cc-filter-container .cc-filter-panel{position:absolute;top:calc(100% + 8px);right:0;z-index:1000;width:var(--cc-filter-panel-width, 320px);min-width:var(--cc-filter-panel-min-width, 480px)!important;padding:var(--cc-filter-panel-padding, 0);background-color:var(--cc-filter-panel-bg, #fff);border:var(--cc-filter-panel-border, 1px solid #e0e0e0);box-shadow:var(--cc-filter-panel-shadow, 0 4px 12px rgba(0, 0, 0, .15));border-radius:var(--cc-filter-panel-radius, 8px);display:flex;flex-direction:column;overflow:hidden}.cc-filter-container .cc-filter-panel.hidden{display:none}.cc-filter-container .cc-filter-panel .cc-panel-header{display:flex;align-items:center;justify-content:space-between;padding:0 1rem;height:var(--cc-filter-panel-header-height, 48px);border-bottom:1px solid #E0E0E0;background-color:var(--cc-filter-panel-header-bg, #fafafa)}.cc-filter-container .cc-tabs{display:flex;gap:1rem}.cc-filter-container .cc-tabs .cc-tab{cursor:pointer;padding:.5rem 0;font-weight:500;color:#5f6368;border-bottom:2px solid transparent;font-size:14px}.cc-filter-container .cc-tabs .cc-tab.active{color:var(--cc-filter-tab-active-text, #1976d2);border-bottom:var(--cc-filter-tab-border-bottom, 2px solid #1976d2)}.cc-filter-container .cc-icon-btn{background:none;border:none;cursor:pointer;color:#5f6368;padding:.25rem;border-radius:50%;display:flex;align-items:center;justify-content:center}.cc-filter-container .cc-icon-btn:hover{background-color:#0000000a}.cc-filter-container .cc-panel-content{padding:16px;max-height:400px;overflow-y:auto;display:flex;flex-direction:column;gap:var(--cc-filter-panel-gap, 1rem)}.cc-filter-container .cc-filter-section{margin-bottom:16px}.cc-filter-container .cc-filter-section .cc-section-title{font-size:13px;font-weight:600;margin-bottom:8px;color:var(--cc-filter-section-title, #333);text-transform:uppercase;letter-spacing:.5px}.cc-filter-container .cc-filter-section .cc-control-group{display:flex;flex-direction:column;gap:8px}.cc-filter-container .cc-filter-section .cc-control-group label{display:flex;align-items:center;gap:8px;font-size:14px;color:var(--cc-filter-option-text, #555);cursor:pointer}.cc-filter-container .cc-filter-section .cc-control-group label:hover{color:#000}.cc-filter-container .cc-accordion-item{border-bottom:1px solid #E0E0E0}.cc-filter-container .cc-accordion-item:last-child{border-bottom:none}.cc-filter-container .cc-accordion-item .cc-accordion-header{display:flex;align-items:center;justify-content:space-between;width:100%;padding:.75rem 0;background:none;border:none;cursor:pointer;text-align:left;outline:none}.cc-filter-container .cc-accordion-item .cc-accordion-header .cc-accordion-title{font-size:.875rem;font-weight:500;color:#202124}.cc-filter-container .cc-accordion-item .cc-accordion-header .cc-accordion-icon{color:#5f6368;transition:transform .2s}.cc-filter-container .cc-accordion-item .cc-accordion-body{padding:0 0 1rem;display:flex;flex-direction:column;gap:1rem;animation:slideDown .2s ease-out}.cc-filter-container .cc-filter-divider{height:1px;background-color:#e0e0e0;margin:.5rem 0}.cc-filter-container .cc-panel-footer{padding:12px 16px;background-color:var(--cc-filter-footer-bg, #fff);border-top:var(--cc-filter-footer-border-top, 1px solid #e0e0e0);display:flex;justify-content:flex-end;align-items:center;gap:10px}.cc-filter-container .cc-columns-actions{display:flex;gap:.5rem;margin-bottom:.5rem}.cc-filter-container .cc-columns-list{display:flex;flex-direction:column;gap:.5rem}\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: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { 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$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ButtonComponent, selector: "lib-button", inputs: ["variant", "type", "disabled", "width", "height", "borderRadius", "fontSize", "fontWeight", "backgroundColor", "color", "border", "icon", "labels"] }, { kind: "component", type: InputComponent, selector: "lib-input", inputs: ["config", "labels", "type", "label", "placeholder", "disabled", "required", "readonly", "clearable", "maxLength", "minLength", "min", "max", "pattern", "errorMessage", "helperText", "rows", "prefixIcon", "suffixIcon", "value", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "padding", "fontWeight", "color", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor", "boxShadow"], outputs: ["valueChange", "inputBlur", "inputFocus"] }, { kind: "component", type: DropdownComponent, selector: "lib-dropdown", inputs: ["config", "labels", "options", "placeholder", "label", "multiple", "searchable", "clearable", "disabled", "required", "errorMessage", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "padding", "fontWeight", "color", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor", "boxShadow"], outputs: ["selectionChange"] }, { kind: "component", type: CheckboxComponent, selector: "lib-checkbox", inputs: ["config", "labels", "label", "checked", "disabled", "required", "indeterminate", "options", "labelPosition", "color", "borderRadius", "value", "errorMessage", "width", "height", "fontSize", "fontWeight", "labelColor", "labelFontSize", "labelFontWeight", "gap", "fontFamily", "backgroundColor", "borderColor", "borderWidth", "padding", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor", "boxShadow", "size", "checkedColor", "uncheckedColor", "groupLabelColor", "groupLabelFontSize", "groupLabelFontWeight"], outputs: ["checkedChange"] }, { kind: "component", type: RadioComponent, selector: "lib-radio", inputs: ["config", "label", "options", "disabled", "required", "labelPosition", "color", "layout", "labels", "gap", "labelColor", "checkedColor", "uncheckedColor", "fontSize", "fontWeight", "fontFamily", "groupLabelColor", "groupLabelFontSize", "groupLabelFontWeight", "disabledColor", "errorColor", "size", "borderRadius", "labelFontSize", "labelFontWeight"], outputs: ["selectionChange"] }, { kind: "component", type: ToggleComponent, selector: "lib-toggle", inputs: ["config", "labels", "label", "checked", "disabled", "required", "labelPosition", "color", "labelColor", "uncheckedColor", "checkedColor", "thumbColor", "checkedThumbColor", "fontSize", "fontWeight", "fontFamily", "toggleWidth", "toggleHeight", "gap", "sliderColor", "labelFontSize", "labelFontWeight", "disabledColor"], outputs: ["toggleChange"] }, { kind: "component", type: DatepickerComponent, selector: "lib-datepicker", inputs: ["config", "labels", "label", "placeholder", "disabled", "required", "minDate", "maxDate", "isRange", "startView", "value", "startDate", "endDate", "errorMessage", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "padding", "fontWeight", "color", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor", "boxShadow"], outputs: ["dateChange"] }, { kind: "component", type: SearchComponent, selector: "lib-search", inputs: ["config", "labels", "placeholder", "label", "disabled", "debounceMs", "clearable", "value", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "border", "padding", "fontWeight", "color", "textColor", "iconColor", "placeholderColor", "focusBorderColor", "disabledBackgroundColor", "disabledColor", "boxShadow"], outputs: ["search", "clear"] }] });
|
|
9756
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: FilterComponent, isStandalone: false, selector: "lib-filter", inputs: { config: "config", activeFilters: "activeFilters", columns: "columns", labels: "labels", theme: "theme" }, outputs: { filterChange: "filterChange", columnChange: "columnChange", toggle: "toggle" }, host: { listeners: { "document:click": "onClickOutside($event)" } }, ngImport: i0, template: "<div class=\"cc-filter-container\" [class.is-open]=\"isOpen\" [ngStyle]=\"containerStyles\">\n\n <!-- Trigger Button -->\n <lib-button variant=\"outline\" (click)=\"togglePanel()\" class=\"cc-filter-trigger\"\n [class.active]=\"activeFilterCount > 0\">\n <span class=\"fas fa-filter\"></span> {{ labels?.filterBtn || 'Filter' }}\n <span *ngIf=\"activeFilterCount > 0\" class=\"cc-badge\">{{ activeFilterCount }}</span>\n </lib-button>\n\n <!-- Dropdown Panel -->\n <div class=\"cc-filter-panel mat-elevation-z4\" *ngIf=\"isOpen\" (click)=\"$event.stopPropagation()\">\n\n <!-- Header / Tabs -->\n <div class=\"cc-panel-header\">\n <div class=\"cc-tabs\">\n <div class=\"cc-tab\" [class.active]=\"activeTab === 'filters'\" (click)=\"setActiveTab('filters')\">\n {{ labels.filterBtn }}\n </div>\n <!-- Hide columns tab if no columns config provided -->\n <div class=\"cc-tab\" [class.active]=\"activeTab === 'columns'\" (click)=\"setActiveTab('columns')\"\n *ngIf=\"columns?.length\">\n {{ labels.columns }}\n </div>\n </div>\n <button type=\"button\" class=\"cc-icon-btn\" (click)=\"togglePanel()\">\n <span class=\"material-icons\">close</span>\n </button>\n </div>\n\n <!-- Filters Tab Content -->\n <div class=\"cc-panel-content\" *ngIf=\"activeTab === 'filters'\">\n\n <!-- Dynamic Items -->\n <ng-container *ngFor=\"let item of config.items; trackBy: trackByFn\">\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { item: item }\"></ng-container>\n </ng-container>\n\n </div>\n\n <!-- Columns Tab Content -->\n <div class=\"cc-panel-content\" *ngIf=\"activeTab === 'columns'\">\n <div class=\"cc-columns-actions\">\n <lib-button variant=\"outline\" class=\"cc-btn-text\" (click)=\"toggleAllColumns(true)\">{{ labels.showAll\n }}</lib-button>\n <lib-button variant=\"outline\" class=\"cc-btn-text\" (click)=\"toggleAllColumns(false)\">{{ labels.hideAll\n }}</lib-button>\n </div>\n <div class=\"cc-columns-list\">\n <div *ngFor=\"let col of tempColumns\" class=\"cc-column-item\">\n <lib-checkbox [label]=\"col.label\" [checked]=\"col.visible\" (checkedChange)=\"toggleColumn(col.id)\">\n </lib-checkbox>\n </div>\n </div>\n </div>\n\n <!-- Footer Actions -->\n <div class=\"cc-panel-footer\">\n <lib-button variant=\"outline\" color=\"warn\" class=\"cc-btn-text text-danger\" (click)=\"clearAll()\">\n {{ labels.clear }}\n </lib-button>\n <lib-button class=\"ms-2\" variant=\"primary\" (click)=\"apply()\">\n {{ labels.apply }}\n </lib-button>\n </div>\n\n </div>\n</div>\n\n<!-- Recursive Template for Items -->\n<ng-template #itemTemplate let-item=\"item\">\n <div class=\"cc-filter-item\" [ngClass]=\"['type-' + item.type]\"\n [style.display]=\"item.visible === false ? 'none' : 'block'\" [ngStyle]=\"item.styles\">\n\n <ng-container [ngSwitch]=\"item.type\">\n\n <!-- Input -->\n <lib-input *ngSwitchCase=\"'input'\" [config]=\"item.inputConfig\" [label]=\"item.label\"\n [ngModel]=\"tempFilters[item.key]\" (valueChange)=\"onFilterChange(item.key, $event)\">\n </lib-input>\n\n <!-- Dropdown -->\n <lib-dropdown *ngSwitchCase=\"'dropdown'\" [config]=\"item.dropdownConfig\" [label]=\"item.label\"\n [ngModel]=\"tempFilters[item.key]\" (selectionChange)=\"onFilterChange(item.key, $event)\">\n </lib-dropdown>\n\n <!-- Checkbox -->\n <lib-checkbox *ngSwitchCase=\"'checkbox'\" [config]=\"item.checkboxConfig\" [label]=\"item.label\"\n [ngModel]=\"tempFilters[item.key]\" (checkedChange)=\"onFilterChange(item.key, $event)\">\n </lib-checkbox>\n\n <!-- Radio -->\n <lib-radio *ngSwitchCase=\"'radio'\" [config]=\"item.radioConfig\" [label]=\"item.label\"\n [ngModel]=\"tempFilters[item.key]\" (selectionChange)=\"onFilterChange(item.key, $event)\">\n </lib-radio>\n\n <!-- Toggle -->\n <lib-toggle *ngSwitchCase=\"'toggle'\" [config]=\"item.toggleConfig\" [label]=\"item.label\"\n [checked]=\"tempFilters[item.key]\" (toggleChange)=\"onFilterChange(item.key, $event)\">\n </lib-toggle>\n\n <!-- Datepicker -->\n <lib-datepicker *ngSwitchCase=\"'datepicker'\" [config]=\"item.datepickerConfig\" [label]=\"item.label\"\n [ngModel]=\"tempFilters[item.key]\" (dateChange)=\"onFilterChange(item.key, $event)\">\n </lib-datepicker>\n\n <!-- Search -->\n <lib-search *ngSwitchCase=\"'active-search'\" [config]=\"item.searchConfig\" [label]=\"item.label\"\n [ngModel]=\"tempFilters[item.key]\" (search)=\"onFilterChange(item.key, $event)\">\n </lib-search>\n\n <!-- Group / Accordion -->\n <div *ngSwitchCase=\"'group'\" class=\"cc-accordion-item\" [class.expanded]=\"item.expanded\">\n <button type=\"button\" class=\"cc-accordion-header\" (click)=\"item.expanded = !item.expanded\">\n <span class=\"cc-accordion-title\">{{ item.label }}</span>\n <span class=\"material-icons cc-accordion-icon\">{{ item.expanded ? 'expand_less' : 'expand_more'\n }}</span>\n </button>\n\n <div class=\"cc-accordion-body\" *ngIf=\"item.expanded\">\n <ng-container *ngFor=\"let child of item.children; trackBy: trackByFn\">\n <ng-container *ngTemplateOutlet=\"itemTemplate; context: { item: child }\"></ng-container>\n </ng-container>\n </div>\n </div>\n\n <!-- Custom Divider -->\n <div *ngSwitchCase=\"'divider'\" class=\"cc-filter-divider\"></div>\n\n </ng-container>\n </div>\n</ng-template>", styles: [".cc-filter-container{position:relative;display:inline-block;font-family:var(--cc-filter-font-family, \"Roboto\", sans-serif)}.cc-filter-container .cc-filter-trigger ::ng-deep button{display:inline-flex;align-items:center;justify-content:center;gap:8px;width:var(--cc-filter-btn-width, 103px);height:var(--cc-filter-btn-height, 44px);padding:0 16px;background-color:var(--cc-filter-btn-bg, #FCFCFB);border:var(--cc-filter-btn-border, 1px solid #E8EAED);border-radius:var(--cc-filter-btn-radius, 8px);color:var(--cc-filter-btn-text-color, #5F6368);font-size:14px;font-weight:500;transition:all .2s ease}.cc-filter-container .cc-filter-trigger ::ng-deep button mat-icon,.cc-filter-container .cc-filter-trigger ::ng-deep button .material-icons{color:#5f6368}.cc-filter-container .cc-filter-trigger ::ng-deep button:hover{background-color:var(--cc-filter-btn-hover-bg, #f1f3f4)}.cc-filter-container .cc-filter-trigger.active ::ng-deep button{background-color:var(--cc-filter-btn-active-bg, #e3f2fd);color:var(--cc-filter-btn-active-text, #1976d2);border-color:var(--cc-filter-btn-active-text, #1976d2)}.cc-filter-container .cc-filter-trigger.active ::ng-deep button mat-icon,.cc-filter-container .cc-filter-trigger.active ::ng-deep button .material-icons{color:var(--cc-filter-btn-active-text, #1976d2)}.cc-filter-container .cc-filter-trigger .cc-badge{background-color:var(--cc-filter-btn-badge-bg, #f44336);color:var(--cc-filter-btn-badge-text, #fff);font-size:10px;font-weight:700;padding:2px 6px;border-radius:10px;min-width:16px;text-align:center;margin-left:4px;line-height:1}.cc-filter-container .cc-filter-panel{position:absolute;top:calc(100% + 8px);right:0;z-index:1000;width:var(--cc-filter-panel-width, 320px);min-width:var(--cc-filter-panel-min-width, 480px)!important;padding:var(--cc-filter-panel-padding, 0);background-color:var(--cc-filter-panel-bg, #fff);border:var(--cc-filter-panel-border, 1px solid #e0e0e0);box-shadow:var(--cc-filter-panel-shadow, 0 4px 12px rgba(0, 0, 0, .15));border-radius:var(--cc-filter-panel-radius, 8px);display:flex;flex-direction:column;overflow:hidden}.cc-filter-container .cc-filter-panel.hidden{display:none}.cc-filter-container .cc-filter-panel .cc-panel-header{display:flex;align-items:center;justify-content:space-between;padding:0 1rem;height:var(--cc-filter-panel-header-height, 48px);border-bottom:1px solid #E0E0E0;background-color:var(--cc-filter-panel-header-bg, #fafafa)}.cc-filter-container .cc-tabs{display:flex;gap:1rem}.cc-filter-container .cc-tabs .cc-tab{cursor:pointer;padding:.5rem 0;font-weight:500;color:#5f6368;border-bottom:2px solid transparent;font-size:14px}.cc-filter-container .cc-tabs .cc-tab.active{color:var(--cc-filter-tab-active-text, #1976d2);border-bottom:var(--cc-filter-tab-border-bottom, 2px solid #1976d2)}.cc-filter-container .cc-icon-btn{background:none;border:none;cursor:pointer;color:#5f6368;padding:.25rem;border-radius:50%;display:flex;align-items:center;justify-content:center}.cc-filter-container .cc-icon-btn:hover{background-color:#0000000a}.cc-filter-container .cc-panel-content{padding:16px;max-height:400px;overflow-y:auto;display:flex;flex-direction:column;gap:var(--cc-filter-panel-gap, 1rem)}.cc-filter-container .cc-filter-section{margin-bottom:16px}.cc-filter-container .cc-filter-section .cc-section-title{font-size:13px;font-weight:600;margin-bottom:8px;color:var(--cc-filter-section-title, #333);text-transform:uppercase;letter-spacing:.5px}.cc-filter-container .cc-filter-section .cc-control-group{display:flex;flex-direction:column;gap:8px}.cc-filter-container .cc-filter-section .cc-control-group label{display:flex;align-items:center;gap:8px;font-size:14px;color:var(--cc-filter-option-text, #555);cursor:pointer}.cc-filter-container .cc-filter-section .cc-control-group label:hover{color:#000}.cc-filter-container .cc-accordion-item{border-bottom:1px solid #E0E0E0}.cc-filter-container .cc-accordion-item:last-child{border-bottom:none}.cc-filter-container .cc-accordion-item .cc-accordion-header{display:flex;align-items:center;justify-content:space-between;width:100%;padding:.75rem 0;background:none;border:none;cursor:pointer;text-align:left;outline:none}.cc-filter-container .cc-accordion-item .cc-accordion-header .cc-accordion-title{font-size:.875rem;font-weight:500;color:#202124}.cc-filter-container .cc-accordion-item .cc-accordion-header .cc-accordion-icon{color:#5f6368;transition:transform .2s}.cc-filter-container .cc-accordion-item .cc-accordion-body{padding:0 0 1rem;display:flex;flex-direction:column;gap:1rem;animation:slideDown .2s ease-out}.cc-filter-container .cc-filter-divider{height:1px;background-color:#e0e0e0;margin:.5rem 0}.cc-filter-container .cc-panel-footer{padding:12px 16px;background-color:var(--cc-filter-footer-bg, #fff);border-top:var(--cc-filter-footer-border-top, 1px solid #e0e0e0);display:flex;justify-content:flex-end;align-items:center;gap:10px}.cc-filter-container .cc-columns-actions{display:flex;gap:.5rem;margin-bottom:.5rem}.cc-filter-container .cc-columns-list{display:flex;flex-direction:column;gap:.5rem}\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: i1.NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { 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$3.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$3.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ButtonComponent, selector: "lib-button", inputs: ["variant", "type", "disabled", "width", "height", "borderRadius", "fontSize", "fontWeight", "backgroundColor", "color", "border", "icon", "labels"] }, { kind: "component", type: InputComponent, selector: "lib-input", inputs: ["config", "labels", "type", "label", "placeholder", "disabled", "required", "readonly", "clearable", "maxLength", "minLength", "min", "max", "pattern", "errorMessage", "helperText", "rows", "prefixIcon", "suffixIcon", "value", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "padding", "fontWeight", "color", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor", "boxShadow"], outputs: ["valueChange", "inputBlur", "inputFocus"] }, { kind: "component", type: DropdownComponent, selector: "lib-dropdown", inputs: ["config", "labels", "options", "selectedValue", "placeholder", "label", "multiple", "searchable", "clearable", "disabled", "required", "errorMessage", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "padding", "fontWeight", "color", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor", "boxShadow"], outputs: ["selectionChange"] }, { kind: "component", type: CheckboxComponent, selector: "lib-checkbox", inputs: ["config", "labels", "label", "checked", "disabled", "required", "indeterminate", "options", "labelPosition", "color", "borderRadius", "value", "errorMessage", "width", "height", "fontSize", "fontWeight", "labelColor", "labelFontSize", "labelFontWeight", "gap", "fontFamily", "backgroundColor", "borderColor", "borderWidth", "padding", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor", "boxShadow", "size", "checkedColor", "uncheckedColor", "groupLabelColor", "groupLabelFontSize", "groupLabelFontWeight"], outputs: ["checkedChange"] }, { kind: "component", type: RadioComponent, selector: "lib-radio", inputs: ["config", "label", "options", "disabled", "required", "labelPosition", "color", "layout", "labels", "gap", "labelColor", "checkedColor", "uncheckedColor", "fontSize", "fontWeight", "fontFamily", "groupLabelColor", "groupLabelFontSize", "groupLabelFontWeight", "disabledColor", "errorColor", "size", "borderRadius", "labelFontSize", "labelFontWeight"], outputs: ["selectionChange"] }, { kind: "component", type: ToggleComponent, selector: "lib-toggle", inputs: ["config", "labels", "label", "checked", "disabled", "required", "labelPosition", "color", "labelColor", "uncheckedColor", "checkedColor", "thumbColor", "checkedThumbColor", "fontSize", "fontWeight", "fontFamily", "toggleWidth", "toggleHeight", "gap", "sliderColor", "labelFontSize", "labelFontWeight", "disabledColor"], outputs: ["toggleChange"] }, { kind: "component", type: DatepickerComponent, selector: "lib-datepicker", inputs: ["config", "labels", "label", "placeholder", "disabled", "required", "minDate", "maxDate", "isRange", "startView", "value", "startDate", "endDate", "errorMessage", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "padding", "fontWeight", "color", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor", "boxShadow"], outputs: ["dateChange"] }, { kind: "component", type: SearchComponent, selector: "lib-search", inputs: ["config", "labels", "placeholder", "label", "disabled", "debounceMs", "clearable", "value", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "border", "padding", "fontWeight", "color", "textColor", "iconColor", "placeholderColor", "focusBorderColor", "disabledBackgroundColor", "disabledColor", "boxShadow"], outputs: ["search", "clear"] }] });
|
|
9245
9757
|
}
|
|
9246
9758
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FilterComponent, decorators: [{
|
|
9247
9759
|
type: Component,
|
|
@@ -10303,6 +10815,9 @@ class SmartTableComponent {
|
|
|
10303
10815
|
}
|
|
10304
10816
|
return new Date(val).toLocaleDateString();
|
|
10305
10817
|
}
|
|
10818
|
+
if (val === null || val === undefined || val === '') {
|
|
10819
|
+
return col.emptyValue ?? this.config.emptyValue ?? '-';
|
|
10820
|
+
}
|
|
10306
10821
|
return val;
|
|
10307
10822
|
}
|
|
10308
10823
|
getBadgeClass(row, col) {
|
|
@@ -10489,8 +11004,13 @@ class SmartTableComponent {
|
|
|
10489
11004
|
}
|
|
10490
11005
|
const btn = event.currentTarget;
|
|
10491
11006
|
const rect = btn.getBoundingClientRect();
|
|
11007
|
+
const estimatedHeight = (items?.length || 1) * 44 + 8;
|
|
11008
|
+
const spaceBelow = window.innerHeight - rect.bottom;
|
|
11009
|
+
const top = spaceBelow < estimatedHeight + 4
|
|
11010
|
+
? Math.max(4, rect.top - estimatedHeight - 4)
|
|
11011
|
+
: rect.bottom + 4;
|
|
10492
11012
|
this.dropdownPosition = {
|
|
10493
|
-
top
|
|
11013
|
+
top,
|
|
10494
11014
|
right: window.innerWidth - rect.right
|
|
10495
11015
|
};
|
|
10496
11016
|
this.openDropdownId = id;
|