commons-shared-web-ui 0.0.64 → 0.0.65

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.
@@ -4,20 +4,20 @@ import * as i1 from '@angular/common';
4
4
  import { CommonModule, formatDate } from '@angular/common';
5
5
  import * as i1$2 from '@angular/forms';
6
6
  import { NG_VALUE_ACCESSOR, FormControl, Validators, FormArray, FormGroup, ReactiveFormsModule, FormsModule } from '@angular/forms';
7
- import * as i2$1 from '@angular/cdk/overlay';
7
+ import * as i4 from '@angular/cdk/overlay';
8
8
  import { OverlayModule } from '@angular/cdk/overlay';
9
9
  import { MatCardModule } from '@angular/material/card';
10
10
  import * as i1$3 from '@angular/material/snack-bar';
11
11
  import { MatSnackBarModule, MAT_SNACK_BAR_DATA, MatSnackBarRef } from '@angular/material/snack-bar';
12
12
  import { MatCheckboxModule } from '@angular/material/checkbox';
13
13
  import { MatDividerModule } from '@angular/material/divider';
14
- import * as i6 from '@angular/material/radio';
14
+ import * as i6$1 from '@angular/material/radio';
15
15
  import { MatRadioModule } from '@angular/material/radio';
16
16
  import { MatDialogModule } from '@angular/material/dialog';
17
17
  import { MatFormFieldModule } from '@angular/material/form-field';
18
18
  import * as i2 from '@angular/material/icon';
19
19
  import { MatIconModule } from '@angular/material/icon';
20
- import * as i10 from '@angular/material/button';
20
+ import * as i10$1 from '@angular/material/button';
21
21
  import { MatButtonModule } from '@angular/material/button';
22
22
  import { MatMenuModule } from '@angular/material/menu';
23
23
  import * as i7 from '@angular/material/datepicker';
@@ -28,9 +28,9 @@ import * as i8 from '@angular/material/input';
28
28
  import { MatInputModule } from '@angular/material/input';
29
29
  import { MatProgressBarModule } from '@angular/material/progress-bar';
30
30
  import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
31
- import * as i5 from '@angular/material/select';
31
+ import * as i6 from '@angular/material/select';
32
32
  import { MatSelectModule } from '@angular/material/select';
33
- import * as i2$2 from '@angular/material/tooltip';
33
+ import * as i2$1 from '@angular/material/tooltip';
34
34
  import { MatTooltipModule } from '@angular/material/tooltip';
35
35
  import { MatSliderModule } from '@angular/material/slider';
36
36
  import { MatListModule } from '@angular/material/list';
@@ -40,14 +40,14 @@ import { MatTableModule } from '@angular/material/table';
40
40
  import { MatExpansionModule } from '@angular/material/expansion';
41
41
  import { CdkAccordionModule } from '@angular/cdk/accordion';
42
42
  import { MatSortModule } from '@angular/material/sort';
43
- import * as i9 from '@angular/material/autocomplete';
43
+ import * as i10 from '@angular/material/autocomplete';
44
44
  import { MatAutocompleteModule } from '@angular/material/autocomplete';
45
45
  import { MatSlideToggleModule } from '@angular/material/slide-toggle';
46
46
  import { MatButtonToggleModule } from '@angular/material/button-toggle';
47
47
  import * as i3 from '@angular/common/http';
48
48
  import { HttpParams, HttpHeaders } from '@angular/common/http';
49
49
  import { Subject, BehaviorSubject, combineLatest, forkJoin, takeUntil as takeUntil$1, of } from 'rxjs';
50
- import { takeUntil, finalize, debounceTime, distinctUntilChanged, map, catchError } from 'rxjs/operators';
50
+ import { takeUntil, map, finalize, debounceTime, distinctUntilChanged, catchError } from 'rxjs/operators';
51
51
  import * as i12 from 'ngx-quill';
52
52
  import { QuillModule } from 'ngx-quill';
53
53
  import * as i1$1 from '@angular/platform-browser';
@@ -1392,6 +1392,7 @@ class FormFieldComponent {
1392
1392
  expressionService;
1393
1393
  http;
1394
1394
  hostRef;
1395
+ overlay;
1395
1396
  config;
1396
1397
  controller;
1397
1398
  /**
@@ -1468,6 +1469,11 @@ class FormFieldComponent {
1468
1469
  // ── DROPDOWN state ────────────────────────────────────────────────────────
1469
1470
  isMultiDropdownOpen = false;
1470
1471
  isSingleDropdownOpen = false;
1472
+ // ── Dropdown pagination state ─────────────────────────────────────────────
1473
+ dropdownCurrentPage = 0;
1474
+ dropdownTotalCount = 0;
1475
+ hasMoreDropdownPages = false;
1476
+ isDropdownLoadingMore = false;
1471
1477
  // ── Dropdown search state ─────────────────────────────────────────────────
1472
1478
  searchText = '';
1473
1479
  filteredOptionList = [];
@@ -1560,17 +1566,35 @@ class FormFieldComponent {
1560
1566
  StringUtils.toCamelCase(this.config.sectionConfig?.label) ||
1561
1567
  '__group__');
1562
1568
  }
1563
- constructor(fb, expressionService, http, hostRef) {
1569
+ dropdownScrollStrategy;
1570
+ constructor(fb, expressionService, http, hostRef, overlay) {
1564
1571
  this.fb = fb;
1565
1572
  this.expressionService = expressionService;
1566
1573
  this.http = http;
1567
1574
  this.hostRef = hostRef;
1575
+ this.overlay = overlay;
1576
+ this.dropdownScrollStrategy = this.overlay.scrollStrategies.reposition();
1568
1577
  }
1569
1578
  /** Portal root, resolved from this component's own node rather than the global document. */
1570
1579
  get portalRoot() {
1571
1580
  return this.hostRef.nativeElement.ownerDocument?.body ?? null;
1572
1581
  }
1582
+ _onGlobalScroll = (event) => {
1583
+ if (!this.isSingleDropdownOpen && !this.isMultiDropdownOpen)
1584
+ return;
1585
+ const target = event.target;
1586
+ const isInsideOptionsList = target?.classList?.contains('single-select-options-list') ||
1587
+ target?.classList?.contains('multi-select-options-list');
1588
+ if (!isInsideOptionsList) {
1589
+ this.isSingleDropdownOpen = false;
1590
+ this.isMultiDropdownOpen = false;
1591
+ this.resetDropdownSearch(false);
1592
+ }
1593
+ };
1573
1594
  ngOnInit() {
1595
+ if (typeof document !== 'undefined') {
1596
+ document.addEventListener('scroll', this._onGlobalScroll, true);
1597
+ }
1574
1598
  this.isVisible = this.config.visible !== false && this.config.isEnabled !== false;
1575
1599
  if (this.isGroup) {
1576
1600
  this.initGroupField();
@@ -1659,6 +1683,10 @@ class FormFieldComponent {
1659
1683
  get isSearchableDropdown() {
1660
1684
  return !!this.config.optionConfig?.searchConfig?.enabled;
1661
1685
  }
1686
+ /** Whether the custom single-select dropdown overlay is required (for search OR pagination) */
1687
+ get isCustomSingleDropdown() {
1688
+ return this.isSearchableDropdown || !!this.config.optionConfig?.pagination?.enabled;
1689
+ }
1662
1690
  initGroupField() {
1663
1691
  if (this.config.sectionConfig?.allowMulti) {
1664
1692
  this.groupFormArray = this.fb.array([]);
@@ -1893,6 +1921,9 @@ class FormFieldComponent {
1893
1921
  }
1894
1922
  }
1895
1923
  ngOnDestroy() {
1924
+ if (typeof document !== 'undefined') {
1925
+ document.removeEventListener('scroll', this._onGlobalScroll, true);
1926
+ }
1896
1927
  // Clean up DOM portal — no-op when the node was never appended
1897
1928
  this.portalElement?.remove();
1898
1929
  this.portalElement = null;
@@ -2360,13 +2391,19 @@ class FormFieldComponent {
2360
2391
  control.setValue(null, { emitEvent: true });
2361
2392
  }
2362
2393
  }
2394
+ this.dropdownCurrentPage = 0;
2395
+ this.dropdownTotalCount = 0;
2396
+ this.hasMoreDropdownPages = false;
2363
2397
  this._latestDependencyValues = dependencyValues;
2364
- this.loadDropdownOptions(dependencyValues);
2398
+ this.loadDropdownOptions(dependencyValues, 0, false);
2365
2399
  }
2366
2400
  else if (this.config.optionConfig) {
2367
2401
  this._latestDependencyValues = {};
2368
2402
  this.localOptionList = [];
2369
2403
  this.filteredOptionList = [];
2404
+ this.dropdownCurrentPage = 0;
2405
+ this.dropdownTotalCount = 0;
2406
+ this.hasMoreDropdownPages = false;
2370
2407
  if (parentChanged) {
2371
2408
  const control = this.formGroup?.get(this.config.name);
2372
2409
  if (control && control.value !== null && control.value !== '') {
@@ -2376,45 +2413,133 @@ class FormFieldComponent {
2376
2413
  }
2377
2414
  });
2378
2415
  }
2379
- loadDropdownOptions(dynamicParams = {}) {
2416
+ /** Extracts the total item count from an API response using the configured path or fallbacks. */
2417
+ extractTotalCount(response, configuredPath) {
2418
+ if (!response)
2419
+ return 0;
2420
+ // 1. Try explicit path if configured (e.g. '0.totalCount', 'totalElements', '[0].totalCount')
2421
+ if (configuredPath) {
2422
+ const val = this.getValueByPath(response, configuredPath);
2423
+ if (typeof val === 'number')
2424
+ return val;
2425
+ if (val !== undefined && val !== null && !isNaN(Number(val)))
2426
+ return Number(val);
2427
+ }
2428
+ // 2. Auto-detection fallback chain:
2429
+ // a) Dataset Count Array: [{ totalCount: 39 }] or [{ totalElements: 39 }]
2430
+ const dsCount = this.getValueByPath(response, '0.totalCount');
2431
+ if (dsCount !== undefined && typeof dsCount === 'number')
2432
+ return dsCount;
2433
+ // b) Spring Data REST: { totalElements: 227 }
2434
+ if (typeof response?.totalElements === 'number')
2435
+ return response.totalElements;
2436
+ // c) Standard object: { totalCount: 150 }
2437
+ if (typeof response?.totalCount === 'number')
2438
+ return response.totalCount;
2439
+ // d) Standard object: { total: 85 }
2440
+ if (typeof response?.total === 'number')
2441
+ return response.total;
2442
+ // e) Plain array fallback
2443
+ if (Array.isArray(response))
2444
+ return response.length;
2445
+ return 0;
2446
+ }
2447
+ /** Builds HttpParams for dropdown requests supporting both standard and nested_string (dataset) formats. */
2448
+ buildDropdownQueryParams(url, dynamicParams = {}, pageIndex = 0) {
2380
2449
  const optionConfig = this.config.optionConfig;
2381
2450
  const acConfig = this.config.autocompleteConfig;
2382
- const urls = optionConfig?.apiUrls ||
2383
- (optionConfig?.apiUrl ? [optionConfig.apiUrl] :
2384
- optionConfig?.optionUrl ? [optionConfig.optionUrl] : []);
2385
- if (!urls || urls.length === 0)
2386
- return;
2387
- if (this.optionsSubscription) {
2388
- this.optionsSubscription.unsubscribe();
2389
- }
2390
- this.optionsLoading = !!optionConfig?.showLoader;
2391
- // HTTP method/body/headers: read from autocompleteConfig first, fall back to optionConfig
2392
- const method = acConfig?.method || 'GET';
2393
- const staticBody = acConfig?.body ?? null;
2451
+ const qpConfig = optionConfig?.queryParamsConfig;
2452
+ const pagination = optionConfig?.pagination;
2453
+ const nestedConfig = qpConfig?.nestedStringConfig || optionConfig?.nestedStringConfig;
2394
2454
  const staticQueryParams = acConfig?.queryParams || {};
2395
- const customHeaders = acConfig?.headers;
2396
- const observables = urls.map(url => {
2397
- let fullUrl = url;
2398
- // Merge static queryParams and dynamicParams
2399
- const allQueryParams = { ...staticQueryParams, ...dynamicParams };
2400
- // Interpolate path variables (e.g., {program}) from allQueryParams
2401
- Object.keys(allQueryParams).forEach(key => {
2402
- const placeholder = `{${key}}`;
2403
- if (fullUrl.includes(placeholder)) {
2404
- fullUrl = fullUrl.replace(placeholder, String(allQueryParams[key]));
2405
- // Remove it so it doesn't get appended as a query param
2406
- delete allQueryParams[key];
2455
+ const allQueryParams = { ...staticQueryParams, ...dynamicParams };
2456
+ let fullUrl = url;
2457
+ // Interpolate path variables (e.g., {program}) from allQueryParams
2458
+ Object.keys(allQueryParams).forEach(key => {
2459
+ const placeholder = `{${key}}`;
2460
+ if (fullUrl.includes(placeholder)) {
2461
+ fullUrl = fullUrl.replace(placeholder, String(allQueryParams[key]));
2462
+ delete allQueryParams[key];
2463
+ }
2464
+ });
2465
+ let paramsObj = {};
2466
+ // Pagination query parameters
2467
+ if (pagination?.enabled) {
2468
+ const pageKey = qpConfig?.pageKey || pagination.pageKey || 'page';
2469
+ const sizeKey = qpConfig?.sizeKey || pagination.sizeKey || 'size';
2470
+ const pageOffset = qpConfig?.pageIndexOffset ?? pagination.pageOffset ?? 0;
2471
+ const calculatedPage = pageIndex + pageOffset;
2472
+ const pageSize = pagination.pageSize || 10;
2473
+ paramsObj[pageKey] = calculatedPage.toString();
2474
+ paramsObj[sizeKey] = pageSize.toString();
2475
+ }
2476
+ const filterHandling = qpConfig?.filterHandling || optionConfig?.searchConfig?.handling || (nestedConfig ? 'nested_string' : 'standard');
2477
+ if (filterHandling === 'nested_string' && nestedConfig) {
2478
+ const { paramName, baseValue, separator, assignment } = nestedConfig;
2479
+ let nestedString = baseValue || '';
2480
+ const assign = assignment || '=';
2481
+ const sep = separator || '--';
2482
+ // Merge dynamic params into nested string
2483
+ Object.entries(allQueryParams).forEach(([key, value]) => {
2484
+ if (value !== null && value !== undefined && value !== '') {
2485
+ const prefix = nestedString ? sep : '';
2486
+ nestedString += `${prefix}${key}${assign}${value}`;
2407
2487
  }
2408
2488
  });
2409
- let httpParams = new HttpParams();
2489
+ paramsObj[paramName || 'params'] = nestedString;
2490
+ }
2491
+ else {
2492
+ // Standard handling
2410
2493
  Object.entries(allQueryParams).forEach(([key, value]) => {
2411
2494
  if (value !== null && value !== undefined) {
2412
- httpParams = httpParams.append(key, String(value));
2495
+ paramsObj[key] = String(value);
2413
2496
  }
2414
2497
  });
2498
+ }
2499
+ let httpParams = new HttpParams();
2500
+ Object.entries(paramsObj).forEach(([k, v]) => {
2501
+ httpParams = httpParams.append(k, String(v));
2502
+ });
2503
+ return { url: fullUrl, params: httpParams };
2504
+ }
2505
+ /**
2506
+ * Loads options for DROPDOWN / AUTOCOMPLETE fields.
2507
+ * Supports server-side pagination, separate Dataset count endpoints, and nested_string params.
2508
+ */
2509
+ loadDropdownOptions(dynamicParams = {}, pageIndex = 0, isLoadMore = false) {
2510
+ const optionConfig = this.config.optionConfig;
2511
+ const acConfig = this.config.autocompleteConfig;
2512
+ const pagination = optionConfig?.pagination;
2513
+ const isPaginationEnabled = !!pagination?.enabled;
2514
+ const urls = optionConfig?.apiUrls ||
2515
+ (optionConfig?.apiUrl ? [optionConfig.apiUrl] :
2516
+ optionConfig?.optionUrl ? [optionConfig.optionUrl] : []);
2517
+ if (!urls || urls.length === 0)
2518
+ return;
2519
+ if (!isLoadMore) {
2520
+ if (this.optionsSubscription) {
2521
+ this.optionsSubscription.unsubscribe();
2522
+ }
2523
+ this.optionsLoading = !!optionConfig?.showLoader;
2524
+ this.dropdownCurrentPage = pageIndex;
2525
+ }
2526
+ else {
2527
+ this.isDropdownLoadingMore = true;
2528
+ this.dropdownCurrentPage = pageIndex;
2529
+ }
2530
+ const method = optionConfig?.apiMethod || acConfig?.method || 'GET';
2531
+ const staticBody = optionConfig?.apiPayload ?? acConfig?.body ?? null;
2532
+ const customHeaders = acConfig?.headers;
2533
+ const totalCountCfg = pagination?.totalCountConfig;
2534
+ const isSeparateCount = isPaginationEnabled && totalCountCfg?.source === 'separate' && !!totalCountCfg.apiUrl;
2535
+ const totalPath = totalCountCfg?.responsePath || pagination?.totalCountPath;
2536
+ // Build data API requests
2537
+ const dataObservables = urls.map(url => {
2538
+ const httpParams = this.buildDropdownQueryParams(url, dynamicParams, pageIndex);
2539
+ const fullUrl = httpParams.url;
2415
2540
  const options = {
2416
2541
  headers: this.getHeaders(customHeaders),
2417
- params: httpParams
2542
+ params: httpParams.params
2418
2543
  };
2419
2544
  switch (method) {
2420
2545
  case 'POST': return this.http.post(fullUrl, staticBody, options);
@@ -2423,17 +2548,44 @@ class FormFieldComponent {
2423
2548
  default: return this.http.get(fullUrl, options);
2424
2549
  }
2425
2550
  });
2426
- this.optionsSubscription = forkJoin(observables).pipe(takeUntil(this.destroy$), finalize(() => {
2551
+ const dataRequest$ = forkJoin(dataObservables);
2552
+ let request$;
2553
+ if (isSeparateCount && !isLoadMore) {
2554
+ // Execute both Data API and separate Count API concurrently via forkJoin
2555
+ const countHttpParams = this.buildDropdownQueryParams(totalCountCfg.apiUrl, dynamicParams, 0);
2556
+ const countUrl = countHttpParams.url;
2557
+ const countOptions = {
2558
+ headers: this.getHeaders(customHeaders),
2559
+ params: countHttpParams.params
2560
+ };
2561
+ const countRequest$ = method === 'POST'
2562
+ ? this.http.post(countUrl, staticBody, countOptions)
2563
+ : this.http.get(countUrl, countOptions);
2564
+ request$ = forkJoin({
2565
+ dataRes: dataRequest$,
2566
+ countRes: countRequest$
2567
+ }).pipe(map(({ dataRes, countRes }) => ({
2568
+ responses: dataRes,
2569
+ totalCount: this.extractTotalCount(countRes, totalPath)
2570
+ })));
2571
+ }
2572
+ else {
2573
+ request$ = dataRequest$.pipe(map(responses => ({
2574
+ responses,
2575
+ totalCount: isPaginationEnabled ? this.extractTotalCount(responses[0], totalPath) : undefined
2576
+ })));
2577
+ }
2578
+ this.optionsSubscription = request$.pipe(takeUntil(this.destroy$), finalize(() => {
2427
2579
  this.optionsLoading = false;
2580
+ this.isDropdownLoadingMore = false;
2428
2581
  this.optionsSubscription = null;
2429
2582
  })).subscribe({
2430
- next: (responses) => {
2583
+ next: ({ responses, totalCount }) => {
2431
2584
  let mergedData = [];
2432
2585
  responses.forEach((response) => {
2433
2586
  let data = optionConfig?.dataPath
2434
2587
  ? this.getValueByPath(response, optionConfig.dataPath)
2435
2588
  : (Array.isArray(response) ? response : response.elements || response.data || response.items || response || []);
2436
- // Handle Dictionary Objects by converting them to Arrays
2437
2589
  if (data !== null && typeof data === 'object' && !Array.isArray(data)) {
2438
2590
  data = Object.values(data);
2439
2591
  }
@@ -2453,10 +2605,8 @@ class FormFieldComponent {
2453
2605
  return 0;
2454
2606
  });
2455
2607
  }
2456
- // labelTemplate: read from autocompleteConfig first
2457
2608
  const labelTemplate = acConfig?.labelTemplate;
2458
2609
  const mappedOptions = mergedData.map((item) => {
2459
- // Support labelTemplate: '{firstName} {lastName} ({login})'
2460
2610
  let label;
2461
2611
  if (labelTemplate) {
2462
2612
  label = labelTemplate.replace(/\{(\w+)\}/g, (_, key) => {
@@ -2473,8 +2623,6 @@ class FormFieldComponent {
2473
2623
  const code = optionConfig?.valuePath
2474
2624
  ? this.getValueByPath(item, optionConfig.valuePath)
2475
2625
  : (item.code ?? item.id ?? item.value);
2476
- // Resolve displayFields from autocompleteConfig
2477
- // Supports string (single path, backward-compat) or AutocompleteDisplayField[]
2478
2626
  const displayFields = acConfig?.displayFields;
2479
2627
  let displayMeta;
2480
2628
  if (displayFields) {
@@ -2492,22 +2640,50 @@ class FormFieldComponent {
2492
2640
  }
2493
2641
  return { label, code, value: item, displayMeta };
2494
2642
  });
2495
- // --- PRESERVE SELECTED OPTION ---
2496
- const newCodesSet = new Set(mappedOptions.map(o => o.code));
2497
- const finalOptions = [...mappedOptions];
2498
- this.selectedItems.forEach(opt => {
2499
- if (!newCodesSet.has(opt.code)) {
2500
- finalOptions.push(opt);
2643
+ if (isPaginationEnabled) {
2644
+ if (!isLoadMore && totalCount !== undefined) {
2645
+ this.dropdownTotalCount = totalCount;
2501
2646
  }
2502
- });
2503
- this.localOptionList = finalOptions;
2504
- // ---------------------------------
2505
- // Refresh autocomplete filter list after options arrive from API
2647
+ let finalOptions;
2648
+ if (isLoadMore) {
2649
+ const existingCodes = new Set(this.localOptionList.map(o => o.code));
2650
+ const newUnique = mappedOptions.filter(o => !existingCodes.has(o.code));
2651
+ finalOptions = [...this.localOptionList, ...newUnique];
2652
+ }
2653
+ else {
2654
+ const newCodesSet = new Set(mappedOptions.map(o => o.code));
2655
+ finalOptions = [...mappedOptions];
2656
+ this.selectedItems.forEach(opt => {
2657
+ if (!newCodesSet.has(opt.code)) {
2658
+ finalOptions.push(opt);
2659
+ }
2660
+ });
2661
+ }
2662
+ this.localOptionList = finalOptions;
2663
+ const pageSize = pagination.pageSize || 10;
2664
+ if (this.dropdownTotalCount > 0) {
2665
+ this.hasMoreDropdownPages = (this.localOptionList.length < this.dropdownTotalCount) && (mappedOptions.length > 0);
2666
+ }
2667
+ else {
2668
+ this.hasMoreDropdownPages = mappedOptions.length >= pageSize;
2669
+ }
2670
+ }
2671
+ else {
2672
+ // --- Non-paginated behavior (100% backward compatible) ---
2673
+ const newCodesSet = new Set(mappedOptions.map(o => o.code));
2674
+ const finalOptions = [...mappedOptions];
2675
+ this.selectedItems.forEach(opt => {
2676
+ if (!newCodesSet.has(opt.code)) {
2677
+ finalOptions.push(opt);
2678
+ }
2679
+ });
2680
+ this.localOptionList = finalOptions;
2681
+ this.hasMoreDropdownPages = false;
2682
+ }
2506
2683
  if (this.isAutocomplete) {
2507
2684
  this.filteredOptions = [...this.localOptionList];
2508
2685
  this._syncAutocompleteDisplayValue();
2509
2686
  }
2510
- // Refresh dropdown search filter list after options arrive from API
2511
2687
  if (this.isDropdown) {
2512
2688
  this.filteredOptionList = [...this.localOptionList];
2513
2689
  }
@@ -2515,6 +2691,29 @@ class FormFieldComponent {
2515
2691
  error: err => console.error('Failed to load dropdown options:', err)
2516
2692
  });
2517
2693
  }
2694
+ /** Triggers loading the next page of dropdown options. */
2695
+ loadMoreDropdownOptions() {
2696
+ if (!this.hasMoreDropdownPages || this.isDropdownLoadingMore || this.optionsLoading)
2697
+ return;
2698
+ const nextPage = this.dropdownCurrentPage + 1;
2699
+ this.loadDropdownOptions(this._latestDependencyValues, nextPage, true);
2700
+ }
2701
+ /** Handles scroll event on the dropdown options panel for infiniteScroll mode. */
2702
+ onDropdownScroll(event) {
2703
+ const pagination = this.config.optionConfig?.pagination;
2704
+ if (!pagination?.enabled || pagination.mode !== 'infiniteScroll')
2705
+ return;
2706
+ if (!this.hasMoreDropdownPages || this.isDropdownLoadingMore || this.optionsLoading)
2707
+ return;
2708
+ const element = event.target;
2709
+ if (!element)
2710
+ return;
2711
+ const threshold = 20; // px from bottom
2712
+ const atBottom = element.scrollTop + element.clientHeight >= element.scrollHeight - threshold;
2713
+ if (atBottom) {
2714
+ this.loadMoreDropdownOptions();
2715
+ }
2716
+ }
2518
2717
  getValueByPath(obj, path) {
2519
2718
  if (!path || path === '')
2520
2719
  return obj;
@@ -2756,6 +2955,13 @@ class FormFieldComponent {
2756
2955
  this.resetDropdownSearch(false);
2757
2956
  }
2758
2957
  }
2958
+ onWindowResize() {
2959
+ if (this.isSingleDropdownOpen || this.isMultiDropdownOpen) {
2960
+ this.isSingleDropdownOpen = false;
2961
+ this.isMultiDropdownOpen = false;
2962
+ this.resetDropdownSearch(false);
2963
+ }
2964
+ }
2759
2965
  onEscapeKey() {
2760
2966
  this.isMultiDropdownOpen = false;
2761
2967
  this.isSingleDropdownOpen = false;
@@ -2837,15 +3043,17 @@ class FormFieldComponent {
2837
3043
  const searchConfig = this.config.optionConfig?.searchConfig;
2838
3044
  const mode = searchConfig?.mode || 'local';
2839
3045
  if (mode === 'server') {
2840
- const searchKey = searchConfig?.searchKey || 'search';
3046
+ const searchKey = searchConfig?.searchKey || (searchConfig?.handling === 'nested_string' ? 'SEARCH_TEXT' : 'search');
2841
3047
  const minLen = searchConfig?.minSearchLength ?? 3;
2842
3048
  if (query.length >= minLen) {
2843
3049
  const params = { ...this._latestDependencyValues, [searchKey]: query };
2844
- this.loadDropdownOptions(params);
3050
+ this.dropdownCurrentPage = 0;
3051
+ this.loadDropdownOptions(params, 0, false);
2845
3052
  }
2846
3053
  else if (query.length === 0) {
2847
3054
  // Empty query — restore full list
2848
- this.loadDropdownOptions(this._latestDependencyValues);
3055
+ this.dropdownCurrentPage = 0;
3056
+ this.loadDropdownOptions(this._latestDependencyValues, 0, false);
2849
3057
  }
2850
3058
  }
2851
3059
  else {
@@ -2866,7 +3074,8 @@ class FormFieldComponent {
2866
3074
  const searchConfig = this.config.optionConfig?.searchConfig;
2867
3075
  if (forceLoad && searchConfig?.enabled && searchConfig?.mode === 'server') {
2868
3076
  if (bypassCache || this.localOptionList.length === 0) {
2869
- this.loadDropdownOptions(this._latestDependencyValues);
3077
+ this.dropdownCurrentPage = 0;
3078
+ this.loadDropdownOptions(this._latestDependencyValues, 0, false);
2870
3079
  }
2871
3080
  }
2872
3081
  else {
@@ -4286,13 +4495,13 @@ class FormFieldComponent {
4286
4495
  updateLinkLocal();
4287
4496
  }
4288
4497
  }
4289
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FormFieldComponent, deps: [{ token: i1$2.FormBuilder }, { token: ExpressionService }, { token: i3.HttpClient }, { token: i0.ElementRef }], target: i0.ɵɵFactoryTarget.Component });
4290
- 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", siblingFormArray: "siblingFormArray", siblingIndex: "siblingIndex" }, 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-8px\" *ngIf=\"isVisible\" [class.has-error]=\"errorMessage\">\r\n\r\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 -->\r\n <div *ngIf=\"isRow\" class=\"form-row grid-row\">\r\n <ng-container *ngFor=\"let child of config.children; trackBy: trackByFieldName\">\r\n <div class=\"row-field\" [style.gridColumn]=\"'span ' + getChildColSpan(child)\" *ngIf=\"child.isEnabled !== false\"\r\n [style.display]=\"rowFieldRef.isVisible === false ? 'none' : null\">\r\n <lib-form-field #rowFieldRef [config]=\"child\" [controller]=\"controller\" [formGroup]=\"formGroup\" [allowMulti]=\"allowMulti\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isGroup && config.sectionConfig?.allowMulti\"\r\n class=\"group-section-wrapper mb-20px\"\r\n [class.multi-save-active]=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n\r\n <!-- Multi-Save: header row with label + top-right Add button -->\r\n <div class=\"multi-save-header d-flex justify-content-between align-items-center mb-24\"\r\n *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n <h3 class=\"group-label\" *ngIf=\"config.sectionConfig?.label\">{{ config.sectionConfig!.label }}</h3>\r\n <lib-button [variant]=\"'outline'\" [icon]=\"{type: 'material', value: 'add'}\" (click)=\"addGroupInstance()\"\r\n class=\"btn-add-multi\">\r\n {{ addMultiLabel }}\r\n </lib-button>\r\n </div>\r\n\r\n <!-- Standard heading (non-multiSave) -->\r\n <h3 class=\"group-label\"\r\n *ngIf=\"config.sectionConfig?.label && !config.sectionConfig?.multiSaveConfig?.active\">{{\r\n config.sectionConfig!.label }}</h3>\r\n\r\n <!-- \u2500\u2500 Standard (non-multiSave) repeater: accordion instances \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <ng-container *ngIf=\"!config.sectionConfig?.multiSaveConfig?.active\">\r\n <div *ngFor=\"let instance of instanceList; trackBy: trackByInstanceId; let i = index\"\r\n class=\"group-accordion-instance\"\r\n [class.is-expanded]=\"isGroupExpanded(i)\">\r\n\r\n <!-- Accordion header -->\r\n <div class=\"group-accordion-header\" (click)=\"toggleGroupAccordion(i)\"\r\n role=\"button\" [attr.aria-expanded]=\"isGroupExpanded(i)\">\r\n <div class=\"accordion-header-left d-flex align-items-center gap-10\">\r\n <span class=\"instance-badge\">{{ i + 1 }}</span>\r\n <span class=\"instance-title\">{{ config.sectionConfig!.label }} #{{ i + 1 }}</span>\r\n </div>\r\n <div class=\"accordion-header-right d-flex align-items-center gap-6\">\r\n <button type=\"button\" class=\"accordion-remove-btn\"\r\n *ngIf=\"instanceList.length > 1\"\r\n (click)=\"$event.stopPropagation(); removeGroupInstance(i)\"\r\n aria-label=\"Remove\">\r\n <mat-icon>delete_outline</mat-icon>\r\n </button>\r\n <mat-icon class=\"accordion-chevron\">\r\n {{ isGroupExpanded(i) ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n\r\n <!-- Accordion body (always mounted so form controls survive collapse) -->\r\n <div class=\"group-accordion-body\" [hidden]=\"!isGroupExpanded(i)\">\r\n <div class=\"group-fields sf-grid\">\r\n <ng-container *ngFor=\"let field of config.sectionConfig!.children\">\r\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\"\r\n *ngIf=\"field.isEnabled !== false\"\r\n [style.display]=\"groupFieldRef.isVisible === false ? 'none' : null\">\r\n <lib-form-field #groupFieldRef [config]=\"field\" [controller]=\"instance.rowController\" [formGroup]=\"instance.fg\"\r\n [allowMulti]=\"false\" [siblingFormArray]=\"groupFormArray\" [siblingIndex]=\"i\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Full-width dashed Add button -->\r\n <button type=\"button\" class=\"btn-add-group\" (click)=\"addGroupInstance()\">\r\n <mat-icon>add</mat-icon> {{ addLabel }} {{ config.sectionConfig!.label }}\r\n </button>\r\n </ng-container>\r\n\r\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 -->\r\n <ng-container *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n <div *ngFor=\"let instance of instanceList; trackBy: trackByInstanceId; let i = index\"\r\n class=\"group-instance position-relative mb-16 overflow-hidden\"\r\n [class.is-editing]=\"instance.isEditing\"\r\n [class.is-card]=\"!instance.isEditing\">\r\n\r\n <!-- Edit / new form view -->\r\n <div [hidden]=\"!instance.isEditing\">\r\n <div class=\"group-fields sf-grid\">\r\n <ng-container *ngFor=\"let field of config.sectionConfig!.children\">\r\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\"\r\n *ngIf=\"field.isEnabled !== false\"\r\n [style.display]=\"groupFieldRef.isVisible === false ? 'none' : null\">\r\n <lib-form-field #groupFieldRef [config]=\"field\" [controller]=\"instance.rowController\" [formGroup]=\"instance.fg\"\r\n [allowMulti]=\"false\" [siblingFormArray]=\"groupFormArray\" [siblingIndex]=\"i\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n\r\n <!-- Save / Cancel -->\r\n <div class=\"group-footer d-flex justify-content-end align-items-center gap-16 p-0-24\"\r\n *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"multiSaveError\">{{ multiSaveError }}</span>\r\n <div class=\"footer-actions d-flex gap-12\">\r\n <lib-button [variant]=\"'outline'\" (click)=\"cancelGroupInstance(i)\">Cancel</lib-button>\r\n <lib-button [variant]=\"'primary'\" (click)=\"saveGroupInstance(i)\">Save</lib-button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Card view (saved state) -->\r\n <ng-container *ngIf=\"!instance.isEditing\">\r\n <div class=\"card-view d-flex justify-content-between align-items-center p-18px-24px\"\r\n [class.is-expanded]=\"instance.isExpanded\">\r\n <div class=\"card-content flex-1 d-flex flex-column gap-4 overflow-hidden\">\r\n <span class=\"card-title font-weight-600 overflow-hidden fs-1rem c-111827\">{{\r\n instance.fg.get(config.sectionConfig!.multiSaveConfig!.summaryField || '')?.value\r\n || '\u2014' }}</span>\r\n </div>\r\n <div class=\"card-actions d-flex align-items-center gap-16 ml-20\">\r\n <mat-icon class=\"icon-delete\" (click)=\"removeGroupInstance(i, true)\">delete_outline</mat-icon>\r\n <mat-icon class=\"icon-edit\" (click)=\"editGroupInstance(i)\">edit_outline</mat-icon>\r\n <mat-icon class=\"icon-expand\" (click)=\"toggleExpandGroupInstance(i)\">\r\n {{ instance.isExpanded ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </ng-container>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isGroup && config.sectionConfig && !config.sectionConfig.allowMulti\"\r\n class=\"group-section-wrapper mb-20px\">\r\n <h3 class=\"group-label\" *ngIf=\"config.sectionConfig.label\">{{ config.sectionConfig.label }}</h3>\r\n <div class=\"group-fields sf-grid\">\r\n <ng-container *ngFor=\"let field of config.sectionConfig.children\">\r\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\" *ngIf=\"field.isEnabled !== false\"\r\n [style.display]=\"singleGroupFieldRef.isVisible === false ? 'none' : null\">\r\n <lib-form-field #singleGroupFieldRef [config]=\"field\" [controller]=\"controller\" [formGroup]=\"groupFormGroup\" [allowMulti]=\"false\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n\r\n\r\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 -->\r\n <div *ngIf=\"isTextField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <textarea *ngIf=\"config.subType === 'LONG'\" class=\"field-input textarea\" [placeholder]=\"config.placeholder || ''\"\r\n [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\" rows=\"4\">\r\n </textarea>\r\n\r\n <!-- Password input with show/hide toggle -->\r\n <div *ngIf=\"config.subType === 'PASSWORD'\" class=\"password-wrapper position-relative d-flex align-items-center\">\r\n <input [type]=\"showPassword ? 'text' : 'password'\" class=\"field-input password-input\"\r\n [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\r\n <button type=\"button\"\r\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\"\r\n (click)=\"showPassword = !showPassword\" tabindex=\"-1\"\r\n [attr.aria-label]=\"showPassword ? 'Hide password' : 'Show password'\">\r\n <mat-icon class=\"eye-icon\">{{ showPassword ? 'visibility' : 'visibility_off' }}</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <span class=\"input-prefix br-none\" *ngIf=\"config.prefix\">{{ config.prefix }}</span>\r\n\r\n <input *ngIf=\"config.subType !== 'LONG' && config.subType !== 'PASSWORD'\"\r\n [type]=\"config.subType === 'EMAIL' ? 'email' : config.subType === 'PHONE' ? 'tel' : 'text'\" class=\"field-input\"\r\n [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\"\r\n [readonly]=\"config.readonly\">\r\n\r\n <span class=\"input-suffix d-flex align-items-center font-weight-500\" *ngIf=\"config.suffix\">{{ config.suffix\r\n }}</span>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n\r\n <!-- Suffix Action Icons (clickable) -->\r\n <div class=\"suffix-action-icons position-absolute d-flex gap-8 align-items-center\"\r\n *ngIf=\"config.suffixActionIcons?.length && !config.readonly\">\r\n <mat-icon class=\"suffix-action-icon\"\r\n *ngFor=\"let action of config.suffixActionIcons\"\r\n [style.color]=\"action.color || null\"\r\n [title]=\"action.tooltip || ''\"\r\n (click)=\"onSuffixActionClick(action.actionId)\">\r\n {{ action.icon }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n <div class=\"char-count-hint font-poppins font-weight-400 text-14 text-right c-6B7280\" *ngIf=\"showCharCount\">\r\n {{ remainingCharacters }} characters remaining\r\n </div>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isNumberField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <span class=\"input-prefix br-none\" *ngIf=\"config.prefix\">{{ config.prefix }}</span>\r\n\r\n <input type=\"number\" class=\"field-input\" [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\"\r\n [min]=\"config.numberConfig?.min ?? null\" [max]=\"config.numberConfig?.max ?? null\"\r\n [step]=\"config.numberConfig?.step || 1\" [class.is-invalid]=\"errorMessage\" [readonly]=\"config.readonly\">\r\n\r\n <span class=\"input-suffix d-flex align-items-center font-weight-500\" *ngIf=\"config.suffix\">{{ config.suffix\r\n }}</span>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n\r\n <!-- Suffix Action Icons (clickable) -->\r\n <div class=\"suffix-action-icons position-absolute d-flex gap-8 align-items-center\"\r\n *ngIf=\"config.suffixActionIcons?.length && !config.readonly\">\r\n <mat-icon class=\"suffix-action-icon\"\r\n *ngFor=\"let action of config.suffixActionIcons\"\r\n [style.color]=\"action.color || null\"\r\n [title]=\"action.tooltip || ''\"\r\n (click)=\"onSuffixActionClick(action.actionId)\">\r\n {{ action.icon }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isDateField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <input matInput [matDatepicker]=\"datePicker\" class=\"field-input date-input has-icon-right\"\r\n [formControlName]=\"config.name!\" [min]=\"effectiveMinDate\" [max]=\"config.dateConfig?.maxDate\"\r\n [class.is-invalid]=\"errorMessage\" [placeholder]=\"config.placeholder || ''\"\r\n [readonly]=\"config.readonly || config.dateConfig?.inputReadonly\"\r\n (click)=\"!config.readonly && datePicker.open()\">\r\n <div class=\"date-icon-wrapper position-absolute d-flex align-items-center justify-content-center\"\r\n *ngIf=\"!config.readonly\">\r\n <mat-datepicker-toggle matSuffix [for]=\"datePicker\"></mat-datepicker-toggle>\r\n </div>\r\n <mat-datepicker #datePicker></mat-datepicker>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isTimeField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <!-- DEFAULT variant: existing native input \u2014 unchanged -->\r\n <ng-container *ngIf=\"!isWheelVariant\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <input type=\"time\" class=\"field-input time-input\" [formControlName]=\"config.name!\"\r\n [min]=\"effectiveMinTime\" [max]=\"effectiveMaxTime\"\r\n [class.is-invalid]=\"errorMessage\" [readonly]=\"config.readonly || config.timeConfig?.inputReadonly\">\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- WHEEL variant: drum-roll picker -->\r\n <lib-time-picker\r\n *ngIf=\"isWheelVariant\"\r\n [formControlName]=\"config.name!\"\r\n [label]=\"config.label || ''\"\r\n [placeholder]=\"config.timeConfig?.placeholder || ''\"\r\n [required]=\"config.required || false\"\r\n [disabled]=\"config.readonly || false\"\r\n [mode]=\"config.timeConfig?.mode || '12'\"\r\n [minuteStep]=\"config.timeConfig?.minuteStep || 1\"\r\n [minTime]=\"effectiveMinTime || undefined\"\r\n [maxTime]=\"config.timeConfig?.maxTime\"\r\n [errorMessage]=\"errorMessage\"\r\n [labels]=\"timePickerLabels\">\r\n </lib-time-picker>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isAutocomplete\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <!-- Hidden real control (stores the code value) -->\r\n <input type=\"hidden\" [formControlName]=\"config.name!\">\r\n\r\n <div class=\"autocomplete-wrapper position-relative d-flex align-items-center w-100\"\r\n [class.is-invalid]=\"errorMessage\" [class.readonly]=\"config.readonly\">\r\n <!-- Search icon -->\r\n <mat-icon class=\"ac-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">search</mat-icon>\r\n\r\n <input class=\"field-input ac-input\" [formControl]=\"autocompleteInputCtrl\" [matAutocomplete]=\"auto\"\r\n [placeholder]=\"config.placeholder || 'Search\u2026'\" [readonly]=\"!!config.readonly\" [class.is-invalid]=\"errorMessage\"\r\n (blur)=\"onAutocompleteClear()\" autocomplete=\"off\">\r\n\r\n <!-- Clear button -->\r\n <button type=\"button\"\r\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\"\r\n *ngIf=\"autocompleteInputCtrl.value && !config.readonly\"\r\n (click)=\"autocompleteInputCtrl.setValue(''); updateValue(null)\" tabindex=\"-1\" aria-label=\"Clear\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n\r\n <mat-autocomplete #auto=\"matAutocomplete\" [panelWidth]=\"'auto'\">\r\n <mat-option *ngFor=\"let option of filteredOptions\" [value]=\"option.label\"\r\n (onSelectionChange)=\"onAutocompleteSelected(option)\">\r\n <span class=\"ac-option-label\">{{ option.label }}</span>\r\n\r\n <!-- Dynamic display fields (image / email / phone / text) -->\r\n <div class=\"ac-display-fields d-flex flex-wrap gap-6 mt-2\" *ngIf=\"option['displayMeta']?.length\">\r\n <ng-container *ngFor=\"let field of option['displayMeta']\">\r\n\r\n <!-- Image avatar -->\r\n <span *ngIf=\"field.type === 'image' && field.value\" class=\"ac-df-item ac-df-image\">\r\n <img [src]=\"field.value\" [alt]=\"field.label || 'image'\" class=\"ac-df-avatar\">\r\n </span>\r\n\r\n <!-- Email -->\r\n <span *ngIf=\"field.type === 'email' && field.value\" class=\"ac-df-item ac-df-chip\">\r\n <mat-icon class=\"ac-df-icon\">mail_outline</mat-icon>\r\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\r\n {{ field.value }}\r\n </span>\r\n\r\n <!-- Phone -->\r\n <span *ngIf=\"field.type === 'phone' && field.value\" class=\"ac-df-item ac-df-chip\" [class]=\"field.className\">\r\n <mat-icon class=\"ac-df-icon\">phone</mat-icon>\r\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\r\n {{ field.value }}\r\n </span>\r\n\r\n <!-- Custom / Icon-based / Generic Text -->\r\n <span *ngIf=\"field.type !== 'image' && field.type !== 'email' && field.type !== 'phone' && field.value\" \r\n class=\"ac-df-item\" [class.ac-df-chip]=\"!!field.icon\" [class]=\"field.className\">\r\n <mat-icon class=\"ac-df-icon\" *ngIf=\"field.icon\">{{ field.icon }}</mat-icon>\r\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\r\n {{ field.value }}\r\n </span>\r\n\r\n </ng-container>\r\n </div>\r\n </mat-option>\r\n <mat-option *ngIf=\"filteredOptions.length === 0\" disabled class=\"ac-no-results fs-0-8125rem c-6B7280\">\r\n No results found\r\n </mat-option>\r\n </mat-autocomplete>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isDropdown\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <!-- SINGLE SELECT: native select (default, non-searchable) -->\r\n <div *ngIf=\"config.subType === 'SINGLE' && !isSearchableDropdown\" class=\"native-select-wrapper\">\r\n <select class=\"field-input\" [formControlName]=\"config.name!\"\r\n [class.is-invalid]=\"errorMessage\" [disabled]=\"optionsLoading || config.disabled\">\r\n <option [ngValue]=\"null\" disabled selected>{{ optionsLoading ? (controller.labels['LBL_LOADING'] || 'Loading...') : (config.placeholder || selectPlaceholderLabel) }}</option>\r\n <option *ngFor=\"let option of displayLocalOptionList\" [value]=\"option.code\">\r\n {{ option.label }}\r\n </option>\r\n </select>\r\n <div *ngIf=\"optionsLoading\" class=\"native-select-spinner\" aria-hidden=\"true\"></div>\r\n </div>\r\n\r\n <!-- SINGLE SELECT: custom searchable dropdown -->\r\n <div *ngIf=\"config.subType === 'SINGLE' && isSearchableDropdown\" class=\"single-select-wrapper\"\r\n [class.is-invalid]=\"errorMessage\">\r\n\r\n <div #singleTrigger cdkOverlayOrigin #singleOrigin=\"cdkOverlayOrigin\"\r\n class=\"field-input single-select-trigger d-flex align-items-center justify-content-between cursor-pointer\"\r\n [class.ss-open]=\"isSingleDropdownOpen\"\r\n (click)=\"!optionsLoading && toggleSingleDropdown($event)\">\r\n <span *ngIf=\"singleSelectedLabel && !optionsLoading\" class=\"single-select-value fs-0-9rem\">\r\n {{ singleSelectedLabel }}\r\n </span>\r\n <span *ngIf=\"!singleSelectedLabel || optionsLoading\" class=\"single-select-placeholder\">\r\n {{ optionsLoading ? (controller.labels['LBL_LOADING'] || 'Loading...') : (config.placeholder || selectPlaceholderLabel) }}\r\n </span>\r\n <div class=\"d-flex align-items-center\">\r\n <div *ngIf=\"optionsLoading\" class=\"mini-spinner me-2\" role=\"progressbar\" aria-label=\"Loading options\"></div>\r\n <mat-icon class=\"single-select-arrow\">\r\n {{ isSingleDropdownOpen ? expandLessLabel : expandMoreLabel }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n\r\n <ng-template cdkConnectedOverlay\r\n [cdkConnectedOverlayOrigin]=\"singleOrigin\"\r\n [cdkConnectedOverlayOpen]=\"isSingleDropdownOpen\"\r\n [cdkConnectedOverlayPositions]=\"dropdownOverlayPositions\"\r\n [cdkConnectedOverlayWidth]=\"singleTrigger.offsetWidth\"\r\n [cdkConnectedOverlayViewportMargin]=\"8\"\r\n [cdkConnectedOverlayPush]=\"true\"\r\n (overlayOutsideClick)=\"onDropdownOverlayOutsideClick($event, singleTrigger)\"\r\n (detach)=\"isSingleDropdownOpen = false\">\r\n <div class=\"single-select-panel\"\r\n (click)=\"$event.stopPropagation()\">\r\n\r\n <!-- Search input -->\r\n <div class=\"dropdown-search-wrapper\">\r\n <mat-icon class=\"dropdown-search-icon\">search</mat-icon>\r\n <input type=\"text\" class=\"dropdown-search-input\"\r\n [placeholder]=\"searchPlaceholderLabel\"\r\n [(ngModel)]=\"searchText\" [ngModelOptions]=\"{standalone: true}\"\r\n (input)=\"dropdownSearch$.next(searchText)\"\r\n (click)=\"$event.stopPropagation()\">\r\n <mat-icon *ngIf=\"searchText\" class=\"dropdown-search-clear cursor-pointer\"\r\n (click)=\"searchText = ''; dropdownSearch$.next(''); $event.stopPropagation()\">close</mat-icon>\r\n </div>\r\n\r\n <!-- Options list -->\r\n <div class=\"single-select-options-list\">\r\n <div *ngIf=\"optionsLoading\" class=\"dropdown-loading-state\">\r\n <div class=\"dropdown-spinner\"></div>\r\n <span class=\"fs-0-875rem c-6B7280\">{{ controller.labels['LBL_LOADING'] || 'Loading...' }}</span>\r\n </div>\r\n\r\n <ng-container *ngIf=\"!optionsLoading\">\r\n <div *ngFor=\"let option of displayOptionList\"\r\n class=\"single-select-option d-flex align-items-center gap-8 cursor-pointer\"\r\n [class.selected]=\"formGroup.get(config.name!)?.value === option.code\"\r\n (click)=\"selectSingleOption(option)\">\r\n <span class=\"fs-0-875rem c-111827\">{{ option.label }}</span>\r\n <mat-icon *ngIf=\"formGroup.get(config.name!)?.value === option.code\" class=\"single-select-check\">check</mat-icon>\r\n </div>\r\n <div *ngIf=\"!filteredOptionList?.length && localOptionList?.length\"\r\n class=\"multi-select-empty fs-0-875rem c-6B7280\">\r\n {{ noMatchingOptionsLabel }}\r\n </div>\r\n <div *ngIf=\"!localOptionList?.length\"\r\n class=\"multi-select-empty fs-0-875rem c-6B7280\">\r\n {{ noOptionsAvailableLabel }}\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </ng-template>\r\n </div>\r\n\r\n <!-- MULTIPLE SELECT: custom panel with checkboxes -->\r\n <div *ngIf=\"config.subType === 'MULTIPLE'\" class=\"multi-select-wrapper\"\r\n [class.is-invalid]=\"errorMessage\">\r\n\r\n <div #multiTrigger cdkOverlayOrigin #multiOrigin=\"cdkOverlayOrigin\"\r\n class=\"field-input multi-select-trigger d-flex align-items-center justify-content-between cursor-pointer\"\r\n [class.ms-open]=\"isMultiDropdownOpen\"\r\n (click)=\"!optionsLoading && toggleMultiDropdown($event)\">\r\n <span *ngIf=\"multiSelectedCount > 0 && !optionsLoading\" class=\"multi-select-value fs-0-9rem\">\r\n {{ multiSelectedCount }} selected\r\n </span>\r\n <span *ngIf=\"multiSelectedCount === 0 || optionsLoading\" class=\"multi-select-placeholder\">\r\n {{ optionsLoading ? (controller.labels['LBL_LOADING'] || 'Loading...') : (config.placeholder || selectPlaceholderLabel) }}\r\n </span>\r\n <div class=\"d-flex align-items-center\">\r\n <div *ngIf=\"optionsLoading\" class=\"mini-spinner me-2\" role=\"progressbar\" aria-label=\"Loading options\"></div>\r\n <mat-icon class=\"multi-select-arrow\">\r\n {{ isMultiDropdownOpen ? expandLessLabel : expandMoreLabel }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n\r\n <ng-template cdkConnectedOverlay\r\n [cdkConnectedOverlayOrigin]=\"multiOrigin\"\r\n [cdkConnectedOverlayOpen]=\"isMultiDropdownOpen\"\r\n [cdkConnectedOverlayPositions]=\"dropdownOverlayPositions\"\r\n [cdkConnectedOverlayWidth]=\"multiTrigger.offsetWidth\"\r\n [cdkConnectedOverlayViewportMargin]=\"8\"\r\n [cdkConnectedOverlayPush]=\"true\"\r\n (overlayOutsideClick)=\"onDropdownOverlayOutsideClick($event, multiTrigger)\"\r\n (detach)=\"isMultiDropdownOpen = false\">\r\n <div class=\"multi-select-panel\"\r\n (click)=\"$event.stopPropagation()\">\r\n\r\n <!-- Search input (shown only when searchConfig.enabled) -->\r\n <div class=\"dropdown-search-wrapper\" *ngIf=\"isSearchableDropdown\">\r\n <mat-icon class=\"dropdown-search-icon\">search</mat-icon>\r\n <input type=\"text\" class=\"dropdown-search-input\"\r\n [placeholder]=\"searchPlaceholderLabel\"\r\n [(ngModel)]=\"searchText\" [ngModelOptions]=\"{standalone: true}\"\r\n (input)=\"dropdownSearch$.next(searchText)\"\r\n (click)=\"$event.stopPropagation()\">\r\n <mat-icon *ngIf=\"searchText\" class=\"dropdown-search-clear cursor-pointer\"\r\n (click)=\"searchText = ''; dropdownSearch$.next(''); $event.stopPropagation()\">close</mat-icon>\r\n </div>\r\n\r\n <!-- Select All checkbox (shown only when showSelectAll is true) -->\r\n <label *ngIf=\"config.optionConfig?.showSelectAll && filteredOptionList?.length\"\r\n class=\"multi-select-option multi-select-all d-flex align-items-center gap-8 cursor-pointer\">\r\n <input type=\"checkbox\"\r\n [checked]=\"isAllSelected()\"\r\n [indeterminate]=\"isSomeSelected()\"\r\n [disabled]=\"!!config.disabled\"\r\n (change)=\"toggleSelectAll($any($event.target).checked)\">\r\n <span class=\"fs-0-875rem c-111827 fw-600\">{{ selectAllLabel }}</span>\r\n </label>\r\n\r\n <!-- Options list -->\r\n <div class=\"multi-select-options-list\">\r\n <div *ngIf=\"optionsLoading\" class=\"dropdown-loading-state\">\r\n <div class=\"dropdown-spinner\"></div>\r\n <span class=\"fs-0-875rem c-6B7280\">{{ controller.labels['LBL_LOADING'] || 'Loading...' }}</span>\r\n </div>\r\n\r\n <ng-container *ngIf=\"!optionsLoading\">\r\n <label *ngFor=\"let option of filteredOptionList\"\r\n class=\"multi-select-option d-flex align-items-center gap-8 cursor-pointer\">\r\n <input type=\"checkbox\"\r\n [checked]=\"isChecked(option.code)\"\r\n [disabled]=\"!!config.disabled\"\r\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\">\r\n <span class=\"fs-0-875rem c-111827\">{{ option.label }}</span>\r\n </label>\r\n <div *ngIf=\"!filteredOptionList?.length && localOptionList?.length\"\r\n class=\"multi-select-empty fs-0-875rem c-6B7280\">\r\n {{ noMatchingOptionsLabel }}\r\n </div>\r\n <div *ngIf=\"!localOptionList?.length\"\r\n class=\"multi-select-empty fs-0-875rem c-6B7280\">\r\n {{ noOptionsAvailableLabel }}\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </ng-template>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isRadio\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"radio-group\" [class.is-invalid]=\"errorMessage\"\r\n [class]=\"config.optionConfig?.layout ? 'layout-' + config.optionConfig!.layout.toLowerCase() : ''\">\r\n <label *ngFor=\"let option of localOptionList\" class=\"radio-label\"\r\n [class.card-item]=\"config.subType === 'CARD'\"\r\n [class.selected]=\"formGroup.get(config.name!)?.value === option.code\"\r\n [style.gridColumn]=\"config.optionConfig?.layout?.toUpperCase() === 'COLUMN' ? 'span ' + getOptionColSpan(option) : null\">\r\n <input type=\"radio\" [formControlName]=\"config.name!\" [value]=\"option.code\">\r\n <div class=\"option-content d-flex flex-column gap-4 flex-1 text-left\">\r\n <span class=\"label-text text-16 c-1F2937\">{{ option.label }}</span>\r\n <span class=\"option-hint text-13 color-gray\" *ngIf=\"option.hint\">{{ option.hint }}</span>\r\n </div>\r\n </label>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isCheckbox\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label && config.subType === 'LIST'\"\r\n class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div *ngIf=\"config.subType === 'BOOL'\" class=\"checkbox-single\">\r\n <label class=\"checkbox-label d-flex align-items-center gap-8 cursor-pointer fs-0-875rem c-111827\">\r\n <input type=\"checkbox\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\r\n <span>{{ config.label }}</span>\r\n </label>\r\n </div>\r\n\r\n <div *ngIf=\"config.subType === 'LIST' || config.subType === 'CARD'\" class=\"checkbox-group d-flex flex-column gap-8\"\r\n [class.is-invalid]=\"errorMessage\"\r\n [class]=\"config.optionConfig?.layout ? 'layout-' + config.optionConfig!.layout.toLowerCase() : ''\">\r\n <label *ngFor=\"let option of localOptionList\"\r\n class=\"checkbox-label d-flex align-items-center gap-8 cursor-pointer fs-0-875rem c-111827\"\r\n [class.card-item]=\"config.subType === 'CARD'\" [class.selected]=\"isChecked(option.code)\"\r\n [style.gridColumn]=\"config.optionConfig?.layout?.toUpperCase() === 'COLUMN' ? 'span ' + getOptionColSpan(option) : null\">\r\n <input type=\"checkbox\" [checked]=\"isChecked(option.code)\" [disabled]=\"!!config.disabled\"\r\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\">\r\n <div class=\"option-content d-flex flex-column gap-4 flex-1 text-left\">\r\n <span class=\"label-text text-16 c-1F2937\">{{ option.label }}</span>\r\n <span class=\"option-hint text-13 color-gray\" *ngIf=\"option.hint\">{{ option.hint }}</span>\r\n </div>\r\n </label>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isChip\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"chip-group d-flex flex-wrap gap-8\" [class.is-invalid]=\"errorMessage\">\r\n <label *ngFor=\"let option of localOptionList\"\r\n class=\"chip-label cursor-pointer p-6px-14px b-ffffff c-374151 b-1px-solid-D1D5DB br-20px fs-0-875rem\"\r\n [class.selected]=\"isChecked(option.code)\">\r\n <input type=\"checkbox\" [checked]=\"isChecked(option.code)\" [disabled]=\"!!config.disabled\"\r\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\" hidden>\r\n <span>{{ option.label }}</span>\r\n </label>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isSwitch\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label class=\"switch-container d-flex justify-content-between align-items-center cursor-pointer\">\r\n <span class=\"field-label d-block font-poppins c-202124 fs-18px\">{{ config.label }}</span>\r\n <div class=\"switch position-relative\">\r\n <input type=\"checkbox\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\r\n <span class=\"slider position-absolute cursor-pointer\"></span>\r\n </div>\r\n </label>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isRichText\" class=\"field-wrapper component-rich-text d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"rich-text-container\" [class.is-invalid]=\"errorMessage\">\r\n <quill-editor [formControlName]=\"config.name!\" class=\"rich-text-editor d-block w-100\"\r\n [placeholder]=\"config.richTextConfig?.placeholder || config.placeholder || ''\"\r\n [styles]=\"{height: config.richTextConfig?.height || '200px'}\"\r\n [modules]=\"richTextModules\">\r\n </quill-editor>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n <div class=\"char-count-hint font-poppins font-weight-400 text-14 text-right c-6B7280\" *ngIf=\"showCharCount\">\r\n {{ remainingCharacters }} characters remaining\r\n </div>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isRating\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"rating-group d-flex gap-4\" [class.is-invalid]=\"errorMessage\">\r\n <span *ngFor=\"let star of getStarArray()\" class=\"star d-inline-flex align-items-center cursor-pointer\"\r\n [class.filled]=\"isStarFilled(star)\" [class.half]=\"isStarHalf(star)\" (click)=\"onRatingChange(star, $event)\">\r\n <mat-icon>{{ isStarFilled(star) || isStarHalf(star) ? 'star' : 'star_border' }}</mat-icon>\r\n </span>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isGenerated\" class=\"field-wrapper d-flex flex-column gap-6\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">{{ config.label\r\n }}</label>\r\n <div class=\"generated-value fs-0-875rem p-0-625rem-0-875rem b-F3F4F6 b-1px-solid-E5E7EB br-8px c-6B7280\">{{ value ||\r\n '-' }}</div>\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint\">{{ config.hint }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isFileUpload\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <!-- Drop Zone -->\r\n <div\r\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\"\r\n [class.drag-over]=\"isDragOver\" [class.has-files]=\"value?.length\" [class.is-invalid]=\"errorMessage\"\r\n (dragover)=\"onDragOver($event)\" (dragleave)=\"onDragLeave($event)\" (drop)=\"onFileDrop($event)\"\r\n (click)=\"fileInput.click()\">\r\n\r\n <!-- Icon with accent-colour pill background -->\r\n <div class=\"upload-icon-wrap mb-4\">\r\n <div class=\"dropzone-icon-pill d-flex align-items-center justify-content-center\">\r\n <mat-icon class=\"upload-cloud-icon\">cloud_upload</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <p class=\"upload-main-text font-weight-600 m-0 fs-0-9rem\">Drag &amp; drop files here</p>\r\n <p class=\"upload-sub-text m-0 fs-0-8rem c-64748B\">or <span class=\"upload-link\">Browse files</span></p>\r\n <p class=\"upload-hint-text m-0 fs-0-78rem c-64748B\" *ngIf=\"config.attachmentConfig?.acceptLabel\">\r\n Supported: <span class=\"upload-formats font-weight-500\">{{ config.attachmentConfig!.acceptLabel }}</span>\r\n </p>\r\n <p class=\"upload-hint-text m-0 fs-0-78rem c-64748B\" *ngIf=\"!config.attachmentConfig?.acceptLabel && config.hint\">\r\n {{ config.hint }}\r\n </p>\r\n <span class=\"upload-size-badge fs-0-72rem\" *ngIf=\"config.attachmentConfig?.maxSizeMB\">\r\n Max {{ config.attachmentConfig!.maxSizeMB }}MB\r\n </span>\r\n\r\n <!-- Hidden native file input -->\r\n <input #fileInput type=\"file\" hidden [attr.multiple]=\"config.attachmentConfig?.multiple ? true : null\"\r\n [attr.accept]=\"config.attachmentConfig?.accept || null\" (change)=\"onFileSelected($event)\">\r\n </div>\r\n\r\n <!-- Uploaded file list -->\r\n <div class=\"uploaded-list d-flex flex-column gap-8 mt-10\" *ngIf=\"value?.length\">\r\n <div *ngFor=\"let f of value; let i = index\"\r\n class=\"uploaded-item d-flex align-items-center gap-10 p-10px-14px br-8px\"\r\n [class.uploading]=\"f.isUploading\">\r\n\r\n <!-- Uploading spinner -->\r\n <ng-container *ngIf=\"f.isUploading; else fileReady\">\r\n <div class=\"upload-spinner rounded-50 b-2px-solid-E2E8F0\"></div>\r\n <div class=\"file-info flex-1 d-flex flex-column\">\r\n <span class=\"file-name font-weight-500 overflow-hidden fs-0-85rem\" [title]=\"f.name\">{{ f.name }}</span>\r\n <span class=\"file-size uploading-label fs-0-72rem\">Uploading...</span>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- Normal state once upload is done -->\r\n <ng-template #fileReady>\r\n <mat-icon class=\"file-type-icon\">{{ getFileIcon(f.type) }}</mat-icon>\r\n <img *ngIf=\"f.type?.startsWith('image') && f.dataUrl\" [src]=\"f.dataUrl\" class=\"file-thumb rounded-4\"\r\n alt=\"preview\">\r\n <div class=\"file-info flex-1 d-flex flex-column\">\r\n <span class=\"file-name font-weight-500 overflow-hidden fs-0-85rem\" [title]=\"f.name\">{{ f.name }}</span>\r\n <span class=\"file-size fs-0-72rem\">{{ formatFileSize(f.size) }}</span>\r\n </div>\r\n </ng-template>\r\n\r\n <!-- Compact icon-only remove button -->\r\n <button type=\"button\" class=\"file-remove-btn d-flex align-items-center justify-content-center rounded-50\"\r\n [disabled]=\"f.isUploading\" (click)=\"!f.isUploading && removeUploadedFile(i)\"\r\n aria-label=\"Remove file\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Validation / file errors -->\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"fileUploadError\">{{ fileUploadError }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage && !fileUploadError\">{{ errorMessage }}</span>\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\"\r\n *ngIf=\"config.hint && !errorMessage && !fileUploadError && !config.attachmentConfig?.acceptLabel\">\r\n {{ config.hint }}\r\n </span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isMediaUpload\" class=\"field-wrapper media-upload-wrapper d-flex flex-column gap-6 p-0 border-none b-none\"\r\n [formGroup]=\"formGroup\">\r\n\r\n <!-- Hidden file input lives outside *ngIf \u2014 triggered via ViewChild -->\r\n <input #mediaDeviceInput type=\"file\" hidden multiple accept=\"image/*\" (change)=\"onMediaFileSelected($event)\">\r\n\r\n <!-- Two-column layout -->\r\n <div class=\"mu-layout d-grid align-items-start\">\r\n\r\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 -->\r\n <div class=\"mu-left d-flex flex-column gap-20\">\r\n\r\n <!-- Header: title + max-items badge -->\r\n <div class=\"mu-header d-flex align-items-start flex-wrap gap-10\">\r\n <h3 class=\"mu-title m-0 c-111827 fs-1-35rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </h3>\r\n <span\r\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\"\r\n *ngIf=\"config.attachmentConfig?.maxFiles\">\r\n {{ controller.labels['LBL_MEDIA_MAX_PREFIX'] || 'Maximum' }}\r\n {{ config.attachmentConfig?.maxFiles }}\r\n {{ controller.labels['LBL_MEDIA_MAX_SUFFIX'] || 'Image & Videos' }}\r\n </span>\r\n </div>\r\n\r\n <!-- Description -->\r\n <p class=\"mu-description m-0 fs-0-875rem c-6B7280\" *ngIf=\"config.attachmentConfig?.description\">\r\n {{ config.attachmentConfig?.description }}\r\n </p>\r\n <p class=\"mu-description m-0 fs-0-875rem c-6B7280\"\r\n *ngIf=\"!config.attachmentConfig?.description && controller.labels['LBL_MEDIA_DESC']\">\r\n {{ controller.labels['LBL_MEDIA_DESC'] }}\r\n </p>\r\n\r\n <!-- Feature bullet list -->\r\n <ul class=\"mu-features m-0 p-0 d-flex flex-column gap-8 ls-none\"\r\n *ngIf=\"config.attachmentConfig?.features?.length || controller.labels['LBL_MEDIA_FEATURE_1']\">\r\n <ng-container *ngIf=\"config.attachmentConfig?.features?.length\">\r\n <li *ngFor=\"let f of config.attachmentConfig?.features\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ f }}\r\n </li>\r\n </ng-container>\r\n <ng-container *ngIf=\"!config.attachmentConfig?.features?.length\">\r\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_1']\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_1'] }}\r\n </li>\r\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_2']\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_2'] }}\r\n </li>\r\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_3']\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_3'] }}\r\n </li>\r\n </ng-container>\r\n </ul>\r\n\r\n <!-- Backdrop to close dropdown on outside click -->\r\n <div class=\"media-menu-backdrop\" *ngIf=\"showMediaMenu\"\r\n (click)=\"$event.stopPropagation(); showMediaMenu = false\"></div>\r\n\r\n <!-- Add Media button + dropdown -->\r\n <div class=\"media-add-container position-relative\" (click)=\"showMediaMenu = !showMediaMenu\">\r\n <lib-button id=\"btn-add-media-{{ config.name }}\" [variant]=\"'warning'\"\r\n [icon]=\"{type: 'material', value: 'add_photo_alternate'}\">\r\n {{ controller.labels['LBL_ADD_MEDIA'] || 'Add media' }}\r\n <mat-icon class=\"menu-chevron fs-18px\">add</mat-icon>\r\n </lib-button>\r\n\r\n <div class=\"media-dropdown position-absolute rounded-12 overflow-hidden b-ffffff b-1px-solid-E5E7EB\"\r\n *ngIf=\"showMediaMenu\" role=\"menu\" (click)=\"$event.stopPropagation()\">\r\n <!-- Video -->\r\n <button id=\"btn-media-video-{{ config.name }}\" type=\"button\"\r\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\"\r\n (click)=\"onMediaMenuVideo(); showMediaMenu = false\" role=\"menuitem\">\r\n <span\r\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>\r\n <span class=\"media-drop-text d-flex flex-column flex-1\">\r\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\r\n controller.labels['LBL_MEDIA_VIDEO'] || 'Video' }}</span>\r\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_VIDEO_DESC'] || 'Add\r\n YouTube URL'\r\n }}</span>\r\n </span>\r\n </button>\r\n <!-- Device -->\r\n <button id=\"btn-media-device-{{ config.name }}\" type=\"button\"\r\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\"\r\n (click)=\"onMediaMenuDevice(); showMediaMenu = false\" role=\"menuitem\">\r\n <span\r\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>\r\n <span class=\"media-drop-text d-flex flex-column flex-1\">\r\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\r\n controller.labels['LBL_MEDIA_DEVICE'] || 'Upload from device'\r\n }}</span>\r\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_DEVICE_DESC'] ||\r\n 'Select images from your\r\n computer' }}</span>\r\n </span>\r\n </button>\r\n <!-- Library -->\r\n <button id=\"btn-media-library-{{ config.name }}\" type=\"button\"\r\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\"\r\n (click)=\"onMediaMenuLibrary(); showMediaMenu = false\" role=\"menuitem\">\r\n <span\r\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>\r\n <span class=\"media-drop-text d-flex flex-column flex-1\">\r\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\r\n controller.labels['LBL_MEDIA_LIBRARY'] || 'Upload from library'\r\n }}</span>\r\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_LIBRARY_DESC'] ||\r\n 'Choose from default\r\n images' }}</span>\r\n </span>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- YouTube URL Input (inline below button) -->\r\n <div class=\"youtube-input-panel d-flex flex-column gap-8 p-16 rounded-10 b-FFFAF1 b-1px-solid-E5E7EB\"\r\n *ngIf=\"showYoutubeInput\">\r\n <label class=\"youtube-panel-label d-flex align-items-center gap-6 font-weight-600 fs-0-875rem c-111827\">\r\n {{ controller.labels['LBL_YOUTUBE_URL'] || 'Video URL' }}\r\n </label>\r\n <div class=\"youtube-input-row d-flex gap-8\">\r\n <input id=\"input-youtube-url-{{ config.name }}\" type=\"url\" class=\"field-input youtube-url-input\"\r\n [(ngModel)]=\"youtubeUrlInput\" [ngModelOptions]=\"{standalone: true}\"\r\n [placeholder]=\"controller.labels['PH_YOUTUBE_URL'] || 'Video URL'\" (keyup.enter)=\"addYoutubeMedia()\">\r\n <lib-button id=\"btn-add-youtube-{{ config.name }}\" [variant]=\"'secondary'\" (click)=\"addYoutubeMedia()\">\r\n {{ controller.labels['LBL_ADD'] || 'Add' }}\r\n </lib-button>\r\n </div>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"youtubeUrlError\">{{ youtubeUrlError }}</span>\r\n </div>\r\n\r\n <div\r\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\"\r\n *ngIf=\"mediaUploadError\">\r\n <mat-icon class=\"status-icon fs-18px\">error_outline</mat-icon>\r\n <span>{{ mediaUploadError }}</span>\r\n </div>\r\n\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n </div>\r\n <!-- end left panel -->\r\n\r\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 -->\r\n <div class=\"mu-right d-flex flex-column gap-12\">\r\n\r\n <!-- Carousel (when items exist) -->\r\n <div class=\"media-carousel-section d-flex flex-column gap-12\" *ngIf=\"mediaItems.length\">\r\n <div\r\n class=\"media-carousel-main position-relative w-100 overflow-hidden d-flex align-items-center justify-content-center br-12px b-0F172A\">\r\n <button id=\"btn-carousel-prev-{{ config.name }}\" type=\"button\"\r\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\"\r\n (click)=\"mediaCarouselPrev()\" [disabled]=\"mediaCarouselIndex === 0\" aria-label=\"Previous\">\r\n <mat-icon>chevron_left</mat-icon>\r\n </button>\r\n\r\n <div class=\"carousel-viewer position-absolute d-flex align-items-center justify-content-center\"\r\n *ngFor=\"let item of mediaItems; let i = index\" [hidden]=\"i !== mediaCarouselIndex\">\r\n <div *ngIf=\"item.isUploading\"\r\n class=\"carousel-uploading d-flex flex-column align-items-center gap-12 c-94A3B8 fs-0-85rem\">\r\n <div class=\"carousel-spinner rounded-50 b-3px-solid-rgba-255-255-255-0-15\"></div>\r\n <span>{{ controller.labels['LBL_UPLOADING'] || 'Uploading\u2026' }}</span>\r\n </div>\r\n <ng-container *ngIf=\"!item.isUploading && item.mediaType === 'youtube'\">\r\n <iframe class=\"carousel-iframe w-100 h-100 br-12px\" [src]=\"item.url | trustedUrl\" frameborder=\"0\"\r\n allowfullscreen\r\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\">\r\n </iframe>\r\n </ng-container>\r\n <ng-container *ngIf=\"!item.isUploading && item.mediaType === 'image'\">\r\n <img class=\"carousel-image w-100 h-100 br-12px\" [src]=\"item.url\" alt=\"Media\">\r\n </ng-container>\r\n <button id=\"btn-remove-media-{{ config.name }}-{{ i }}\" type=\"button\"\r\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\"\r\n [disabled]=\"item.isUploading\" (click)=\"removeMediaItem(i)\" aria-label=\"Remove\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <button id=\"btn-carousel-next-{{ config.name }}\" type=\"button\"\r\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\"\r\n (click)=\"mediaCarouselNext()\" [disabled]=\"mediaCarouselIndex === mediaItems.length - 1\" aria-label=\"Next\">\r\n <mat-icon>chevron_right</mat-icon>\r\n </button>\r\n\r\n <div class=\"carousel-dots position-absolute d-flex gap-6\">\r\n <span *ngFor=\"let item of mediaItems; let i = index\"\r\n class=\"carousel-dot rounded-50 cursor-pointer b-rgba-255-255-255-0-45\"\r\n [class.active]=\"i === mediaCarouselIndex\" (click)=\"mediaGoTo(i)\"></span>\r\n </div>\r\n </div>\r\n\r\n <!-- Thumbnail strip -->\r\n <div class=\"media-thumbnail-strip d-flex flex-wrap gap-8 pb-4px\">\r\n <div *ngFor=\"let item of mediaThumbnails; let i = index\"\r\n class=\"media-thumb rounded-8 overflow-hidden cursor-pointer d-flex align-items-center justify-content-center b-2px-solid-transparent b-E2E8F0\"\r\n [class.active]=\"i === mediaCarouselIndex\" (click)=\"mediaGoTo(i)\">\r\n <div *ngIf=\"item.isUploading\"\r\n class=\"thumb-uploading d-flex align-items-center justify-content-center w-100 h-100\">\r\n <div class=\"thumb-spinner rounded-50 b-2px-solid-E2E8F0\"></div>\r\n </div>\r\n <img *ngIf=\"!item.isUploading && item.mediaType === 'youtube' && item.thumbnailUrl\"\r\n [src]=\"item.thumbnailUrl\" class=\"thumb-img w-100 h-100\" alt=\"Video thumbnail\">\r\n <div *ngIf=\"!item.isUploading && item.mediaType === 'youtube' && !item.thumbnailUrl\"\r\n class=\"thumb-yt-placeholder d-flex align-items-center justify-content-center w-100 h-100 b-1E293B c-EF4444\">\r\n <mat-icon>play_circle</mat-icon>\r\n </div>\r\n <img *ngIf=\"!item.isUploading && item.mediaType === 'image' && item.url\" [src]=\"item.url\"\r\n class=\"thumb-img w-100 h-100\" alt=\"Image thumbnail\">\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Empty right-side placeholder -->\r\n <div\r\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\"\r\n *ngIf=\"!mediaItems.length\" (click)=\"onMediaMenuDevice()\">\r\n <mat-icon class=\"mu-right-empty-icon fs-52px\">perm_media</mat-icon>\r\n <p>{{ controller.labels['LBL_ADD_MEDIA'] || 'Add media' }}</p>\r\n </div>\r\n\r\n </div>\r\n <!-- end right panel -->\r\n\r\n </div><!-- end mu-layout -->\r\n </div>\r\n\r\n\r\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 -->\r\n <!-- Wrapper is always in DOM (hidden) so @ViewChild can move it to body -->\r\n <div #libraryModal class=\"media-library-portal-host\" [class.is-open]=\"showLibraryModal\">\r\n\r\n <!-- Backdrop -->\r\n <div class=\"media-library-overlay\" (click)=\"closeLibraryModal()\"></div>\r\n\r\n <!-- Modal card -->\r\n <div class=\"media-library-modal d-flex flex-column overflow-hidden b-ffffff br-16px\"\r\n role=\"dialog\" aria-modal=\"true\">\r\n <div class=\"library-modal-header d-flex align-items-start justify-content-between p-24px-28px bb-1px-solid-E5E7EB\">\r\n <div class=\"header-left d-flex flex-column gap-8\">\r\n <h3 class=\"library-modal-title m-0 color-dark fs-1-25rem\">\r\n {{ controller.labels['LBL_ADD_IMAGES'] || 'Add Images' }}\r\n </h3>\r\n <p class=\"library-modal-subtitle m-0 color-gray fs-0-85rem\">\r\n {{ controller.labels['LBL_LIBRARY_MODAL_DESC'] || 'Select images from your library.' }}\r\n </p>\r\n </div>\r\n <button id=\"btn-close-library-{{ config.name }}\" type=\"button\"\r\n class=\"library-close-btn d-flex align-items-center justify-content-center cursor-pointer rounded-50 border-none b-none c-9CA3AF\"\r\n (click)=\"closeLibraryModal()\" aria-label=\"Close\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <!-- Loading -->\r\n <div class=\"library-loading\" *ngIf=\"libraryLoading\">\r\n <div class=\"lib-spinner rounded-50 b-3px-solid-E2E8F0\"></div>\r\n <span>{{ controller.labels['LBL_LOADING'] || 'Loading\u2026' }}</span>\r\n </div>\r\n\r\n <!-- Error -->\r\n <div class=\"library-error d-flex align-items-center gap-8 color-error b-FEF2F2 fs-0-875rem p-16px-24px\"\r\n *ngIf=\"libraryError && !libraryLoading\">\r\n <mat-icon>error_outline</mat-icon>\r\n {{ libraryError }}\r\n </div>\r\n\r\n <!-- Image grid -->\r\n <div class=\"library-grid d-grid gap-16 flex-1 p-28px b-F9FAFB\" *ngIf=\"!libraryLoading && libraryImages.length\">\r\n <div *ngFor=\"let img of libraryImages; let li = index\" id=\"lib-img-{{ config.name }}-{{ li }}\"\r\n class=\"library-grid-item position-relative rounded-12 overflow-hidden cursor-pointer bg-white b-3px-solid-transparent\"\r\n [class.selected]=\"isLibraryItemSelected(img)\" (click)=\"toggleLibraryItem(img)\">\r\n <img [src]=\"getLibraryItemUrl(img)\" class=\"library-grid-img w-100 h-100 d-block\" alt=\"Library image\">\r\n <div\r\n class=\"library-check position-absolute bg-white rounded-50 d-flex align-items-center justify-content-center c-3B82F6\"\r\n *ngIf=\"isLibraryItemSelected(img)\">\r\n <mat-icon>check_circle</mat-icon>\r\n </div>\r\n <div class=\"library-overlay-hover position-absolute b-rgba-59-130-246-0-12\"></div>\r\n </div>\r\n </div>\r\n\r\n <!-- Empty library -->\r\n <div\r\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\"\r\n *ngIf=\"!libraryLoading && !libraryError && libraryImages.length === 0\">\r\n <mat-icon>image_not_supported</mat-icon>\r\n <span>{{ controller.labels['LBL_LIBRARY_EMPTY'] || 'No images found in library.' }}</span>\r\n </div>\r\n\r\n <!-- Footer -->\r\n <div class=\"library-modal-footer d-flex align-items-center justify-content-end bg-white p-20px-28px bt-1px-solid-E5E7EB\">\r\n <div class=\"library-footer-actions d-flex gap-12\">\r\n <lib-button id=\"btn-library-cancel-{{ config.name }}\" [variant]=\"'outline'\" (click)=\"closeLibraryModal()\">\r\n {{ controller.labels['LBL_CANCEL'] || 'Cancel' }}\r\n </lib-button>\r\n <lib-button id=\"btn-library-confirm-{{ config.name }}\" [variant]=\"'primary'\"\r\n [disabled]=\"librarySelectedIds.size === 0\" (click)=\"confirmLibrarySelection()\">\r\n {{ controller.labels['LBL_CONTINUE'] || 'Continue' }}\r\n </lib-button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n\r\n\r\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 -->\r\n <div *ngIf=\"isLocation\" class=\"field-wrapper location-field-wrapper d-flex flex-column gap-6 gap-12\"\r\n [formGroup]=\"formGroup\">\r\n\r\n <!-- Field label -->\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n <p class=\"location-subtitle m-0 c-6B7280 fs-0-8125rem\" *ngIf=\"config.hint\">{{ config.hint }}</p>\r\n\r\n <!-- Three-tab bar -->\r\n <div class=\"location-tabs d-flex gap-12 mb-24\">\r\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'VENUE' ? 'warning' : 'outline'\"\r\n (click)=\"onLocationTabChange('VENUE')\">\r\n {{ controller.labels['LBL_LOC_VENUE'] || 'Venue' }}\r\n </lib-button>\r\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'ONLINE' ? 'warning' : 'outline'\"\r\n (click)=\"onLocationTabChange('ONLINE')\">\r\n {{ controller.labels['LBL_LOC_ONLINE'] || 'Online Event' }}\r\n </lib-button>\r\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'TBA' ? 'warning' : 'outline'\"\r\n (click)=\"onLocationTabChange('TBA')\">\r\n {{ controller.labels['LBL_LOC_TBA'] || 'To be Announced' }}\r\n </lib-button>\r\n </div>\r\n\r\n <!-- VENUE TAB -->\r\n <div *ngIf=\"locationActiveTab === 'VENUE'\" class=\"loc-panel loc-venue-panel d-flex flex-column gap-12\">\r\n\r\n <p class=\"loc-section-label m-0 font-weight-600 c-111827 fs-0-9rem\">\r\n {{ controller.labels['LBL_LOC_ADDRESS'] || 'Location address' }}\r\n </p>\r\n\r\n <!-- Added venue rows -->\r\n <div class=\"loc-venue-list d-flex flex-column gap-8\" *ngIf=\"locationVenues.length > 0\">\r\n <div *ngFor=\"let venue of locationVenues; let i = index\"\r\n class=\"loc-venue-item d-flex align-items-center gap-10 p-10px-14px br-7px b-ffffff b-1px-solid-D1D5DB\">\r\n <mat-icon class=\"loc-venue-search-icon fs-18px c-9CA3AF\">search</mat-icon>\r\n <span class=\"loc-venue-text flex-1 overflow-hidden fs-0-875rem c-111827\">{{ venue.address || venue.name ||\r\n venue.description }}</span>\r\n <button type=\"button\"\r\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\"\r\n (click)=\"removeLocationVenue(i)\">\r\n <mat-icon>delete_outline</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Location count badge -->\r\n <p class=\"loc-count-text m-0 font-weight-600 fs-0-8125rem c-3B82F6\"\r\n *ngIf=\"locationVenues.length > 0 && config.locationConfig?.allowMulti\">\r\n {{ locationVenues.length }}&nbsp;{{ controller.labels['LBL_LOC_COUNT_SUFFIX'] || 'Locations Added!' }}\r\n </p>\r\n\r\n <!-- Search input (hide when max reached) -->\r\n <div class=\"loc-search-container position-relative\" *ngIf=\"!locationMaxReached\">\r\n <div class=\"loc-search-wrapper position-relative d-flex align-items-center mt-4\">\r\n <mat-icon class=\"loc-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">search</mat-icon>\r\n <input\r\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\"\r\n [placeholder]=\"config.locationConfig?.venuePlaceholder || (controller.labels['PH_LOC_VENUE'] || 'Type to search venue...')\"\r\n [value]=\"locationSearchText\" (input)=\"handleLocationSearchInput($event)\" (blur)=\"hideLocationSuggestions()\"\r\n autocomplete=\"off\" [class.is-invalid]=\"errorMessage\">\r\n </div>\r\n <!-- Suggestions dropdown -->\r\n <div class=\"loc-suggestions-panel position-absolute overflow-hidden br-8px b-ffffff b-1px-solid-D1D5DB\"\r\n *ngIf=\"locationShowSuggestions && locationSuggestions.length\">\r\n <div *ngFor=\"let sug of locationSuggestions\"\r\n class=\"loc-suggestion-item d-flex align-items-center gap-10 cursor-pointer p-10px-14px\"\r\n (mousedown)=\"onLocationSuggestionSelect(sug)\">\r\n <mat-icon class=\"loc-suggestion-icon fs-18px c-E53E3E\">place</mat-icon>\r\n <span class=\"loc-suggestion-text overflow-hidden fs-0-875rem c-374151\">{{ sug.description }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Add another button -->\r\n <button type=\"button\"\r\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\"\r\n *ngIf=\"locationVenues.length > 0 && !locationMaxReached && config.locationConfig?.allowMulti\">\r\n <mat-icon>add_circle_outline</mat-icon>\r\n <span>{{ controller.labels['LBL_LOC_ADD_ANOTHER'] || 'Add another Location' }}</span>\r\n </button>\r\n\r\n <!-- Map -->\r\n <div class=\"loc-map-container overflow-hidden br-10px b-1px-solid-E5E7EB\"\r\n *ngIf=\"config.locationConfig?.showMap !== false\">\r\n <ng-container *ngIf=\"config.locationConfig?.googleMapsApiKey; else simpleEmbed\">\r\n <div [id]=\"'loc-map-' + config.name\" class=\"loc-map-frame w-100 d-block border-none\"\r\n [style.height]=\"config.locationConfig?.mapHeight || '300px'\"></div>\r\n </ng-container>\r\n <ng-template #simpleEmbed>\r\n <iframe class=\"loc-map-frame w-100 d-block border-none\"\r\n [style.height]=\"config.locationConfig?.mapHeight || '300px'\" [src]=\"getLocationMapEmbedUrl() | trustedUrl\"\r\n frameborder=\"0\" allowfullscreen loading=\"lazy\">\r\n </iframe>\r\n </ng-template>\r\n </div>\r\n\r\n <!-- Map hint -->\r\n <p class=\"loc-map-hint m-0 text-center fs-0-78rem c-6B7280\">\r\n {{ controller.labels['LBL_LOC_MAP_HINT'] || 'Type the venue address. A map will appear to assist you.' }}\r\n </p>\r\n </div>\r\n\r\n <!-- ONLINE TAB -->\r\n <div *ngIf=\"locationActiveTab === 'ONLINE'\" class=\"loc-panel loc-online-panel d-flex flex-column gap-12\">\r\n <p class=\"loc-section-label m-0 font-weight-600 c-111827 fs-0-9rem\">\r\n {{ controller.labels['LBL_LOC_ONLINE_URL'] || 'Event URL' }}\r\n </p>\r\n <div class=\"loc-search-wrapper position-relative d-flex align-items-center mt-4\">\r\n <mat-icon class=\"loc-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">link</mat-icon>\r\n <input\r\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\"\r\n type=\"url\"\r\n [placeholder]=\"config.locationConfig?.onlinePlaceholder || (controller.labels['PH_LOC_ONLINE'] || 'https://zoom.us/j/...')\"\r\n [value]=\"locationOnlineUrl\" (input)=\"onLocationUrlChange($any($event.target).value)\"\r\n [class.is-invalid]=\"errorMessage\">\r\n </div>\r\n </div>\r\n\r\n <!-- TBA TAB -->\r\n <div *ngIf=\"locationActiveTab === 'TBA'\"\r\n class=\"loc-panel loc-tba-panel d-flex flex-column gap-12 justify-content-center\">\r\n <div\r\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\">\r\n <mat-icon class=\"loc-tba-icon fs-40px c-9CA3AF\">schedule</mat-icon>\r\n <p class=\"loc-tba-text m-0 c-6B7280 fs-0-9rem\">\r\n {{ controller.labels['LBL_LOC_TBA_DESC'] || \"This event's location is yet to be announced. Check back later\r\n for updates.\" }}\r\n </p>\r\n </div>\r\n </div>\r\n\r\n <!-- Hidden real form control -->\r\n <input type=\"hidden\" [formControlName]=\"config.name!\">\r\n\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Link List 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 -->\r\n <div *ngIf=\"isLinkList\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ controller.labels[config.label] || config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <!-- Top Links list (if listPosition === 'top') -->\r\n <div *ngIf=\"linkListConfig?.listPosition === 'top' && linkListItems.length > 0\" class=\"link-list-container mb-8px\">\r\n <ul class=\"link-items-list p-0 m-0\" \r\n [style.display]=\"'grid'\"\r\n [style.gridTemplateColumns]=\"'repeat(12, 1fr)'\"\r\n [style.gap]=\"'12px 16px'\">\r\n <li *ngFor=\"let item of linkListItems; let idx = index\" \r\n class=\"link-list-item\"\r\n [style.gridColumn]=\"'span ' + (linkListConfig?.colSpan || 12)\">\r\n <!-- Normal display mode -->\r\n <ng-container *ngIf=\"editingLinkIndex !== idx\">\r\n <span class=\"link-url-text text-truncate font-weight-500 color-333\">{{ getLinkDisplayUrl(item) }}</span>\r\n <div class=\"link-item-actions d-flex align-items-center gap-12px\">\r\n <button type=\"button\" class=\"action-btn edit-btn\" \r\n *ngIf=\"linkListConfig?.editable !== false\" (click)=\"startEditLink(idx)\" [title]=\"controller.labels['COMMON.ACTIONS.EDIT'] || 'Edit'\">\r\n <mat-icon>edit</mat-icon>\r\n </button>\r\n <button type=\"button\" class=\"action-btn delete-btn\" \r\n *ngIf=\"linkListConfig?.deleteable !== false\" (click)=\"deleteLinkItem(idx)\" [title]=\"controller.labels['COMMON.ACTIONS.DELETE'] || 'Delete'\">\r\n <mat-icon>delete_outline</mat-icon>\r\n </button>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- Inline Edit mode -->\r\n <ng-container *ngIf=\"editingLinkIndex === idx\">\r\n <div class=\"edit-input-wrapper d-flex align-items-center gap-8px w-100\">\r\n <input type=\"text\" class=\"field-input flex-1 py-4px\" [(ngModel)]=\"editingLinkValue\" \r\n [ngModelOptions]=\"{standalone: true}\" (keydown.enter)=\"saveEditLink(idx)\" (keydown.escape)=\"cancelEditLink()\"\r\n (input)=\"linkValidationError = ''\">\r\n <button type=\"button\" class=\"action-btn save-btn\" (click)=\"saveEditLink(idx)\" [title]=\"controller.labels['COMMON.ACTIONS.SAVE'] || 'Save'\">\r\n <mat-icon>check</mat-icon>\r\n </button>\r\n <button type=\"button\" class=\"action-btn cancel-btn\" (click)=\"cancelEditLink()\" [title]=\"controller.labels['COMMON.ACTIONS.CANCEL'] || 'Cancel'\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n </ng-container>\r\n </li>\r\n </ul>\r\n </div>\r\n\r\n <!-- Input text + plus icon -->\r\n <div class=\"input-group position-relative d-flex w-100\">\r\n <input type=\"text\" class=\"field-input pe-40px\" [placeholder]=\"controller.labels[config.placeholder!] || config.placeholder || controller.labels['COMMON.ENTER_LINK'] || 'Enter link...'\"\r\n [(ngModel)]=\"newLinkInputValue\" [ngModelOptions]=\"{standalone: true}\" (keydown.enter)=\"addLinkItem($event)\"\r\n [disabled]=\"config.readonly || config.disabled\"\r\n (input)=\"linkValidationError = ''\">\r\n \r\n <button type=\"button\" class=\"position-absolute right-8px top-50-percent translate-y-minus-50 d-flex align-items-center justify-content-center b-none bg-none p-0 cursor-pointer color-primary\"\r\n [disabled]=\"config.readonly || config.disabled || !newLinkInputValue?.trim()\"\r\n (click)=\"addLinkItem()\" style=\"right: 8px; top: 50%; transform: translateY(-50%); z-index: 10;\">\r\n <mat-icon>add</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <!-- Validation error message -->\r\n <span class=\"field-error color-error fs-0-75rem mt-4px d-block\" *ngIf=\"linkValidationError\">{{ linkValidationError }}</span>\r\n\r\n <!-- Bottom Links list (if listPosition !== 'top') -->\r\n <div *ngIf=\"linkListConfig?.listPosition !== 'top' && linkListItems.length > 0\" class=\"link-list-container mt-8px\">\r\n <ul class=\"link-items-list p-0 m-0\" \r\n [style.display]=\"'grid'\"\r\n [style.gridTemplateColumns]=\"'repeat(12, 1fr)'\"\r\n [style.gap]=\"'12px 16px'\">\r\n <li *ngFor=\"let item of linkListItems; let idx = index\" \r\n class=\"link-list-item\"\r\n [style.gridColumn]=\"'span ' + (linkListConfig?.colSpan || 12)\">\r\n <!-- Normal display mode -->\r\n <ng-container *ngIf=\"editingLinkIndex !== idx\">\r\n <span class=\"link-url-text text-truncate font-weight-500 color-333\">{{ getLinkDisplayUrl(item) }}</span>\r\n <div class=\"link-item-actions d-flex align-items-center gap-12px\">\r\n <button type=\"button\" class=\"action-btn edit-btn\" \r\n *ngIf=\"linkListConfig?.editable !== false\" (click)=\"startEditLink(idx)\" [title]=\"controller.labels['COMMON.ACTIONS.EDIT'] || 'Edit'\">\r\n <mat-icon>edit</mat-icon>\r\n </button>\r\n <button type=\"button\" class=\"action-btn delete-btn\" \r\n *ngIf=\"linkListConfig?.deleteable !== false\" (click)=\"deleteLinkItem(idx)\" [title]=\"controller.labels['COMMON.ACTIONS.DELETE'] || 'Delete'\">\r\n <mat-icon>delete_outline</mat-icon>\r\n </button>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- Inline Edit mode -->\r\n <ng-container *ngIf=\"editingLinkIndex === idx\">\r\n <div class=\"edit-input-wrapper d-flex align-items-center gap-8px w-100\">\r\n <input type=\"text\" class=\"field-input flex-1 py-4px\" [(ngModel)]=\"editingLinkValue\" \r\n [ngModelOptions]=\"{standalone: true}\" (keydown.enter)=\"saveEditLink(idx)\" (keydown.escape)=\"cancelEditLink()\"\r\n (input)=\"linkValidationError = ''\">\r\n <button type=\"button\" class=\"action-btn save-btn\" (click)=\"saveEditLink(idx)\" [title]=\"controller.labels['COMMON.ACTIONS.SAVE'] || 'Save'\">\r\n <mat-icon>check</mat-icon>\r\n </button>\r\n <button type=\"button\" class=\"action-btn cancel-btn\" (click)=\"cancelEditLink()\" [title]=\"controller.labels['COMMON.ACTIONS.CANCEL'] || 'Cancel'\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n </ng-container>\r\n </li>\r\n </ul>\r\n </div>\r\n\r\n <!-- Hidden real form control -->\r\n <input type=\"hidden\" [formControlName]=\"config.name!\">\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ controller.labels[config.hint] || config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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-8px{margin-bottom:8px!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}.generated-value{word-break:break-all}: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:.8rem!important;font-weight:600!important;line-height:1.2;letter-spacing:.04em;text-transform:uppercase!important;color:#202124!important;margin-bottom:var(--cc-sf-label-mb, 0px)!important}.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}.dropdown-search-wrapper{display:flex;align-items:center;gap:8px;padding:8px 12px;border-bottom:1px solid var(--cc-sf-input-border, #E5E7EB);background:var(--cc-sf-input-bg, #ffffff)}.dropdown-search-wrapper .dropdown-search-icon{font-size:18px;width:18px;height:18px;color:var(--cc-sf-input-placeholder, #9CA3AF);flex-shrink:0}.dropdown-search-wrapper .dropdown-search-input{flex:1;border:none;outline:none;background:transparent;font-size:.875rem;color:var(--cc-sf-label-color, #111827);font-family:inherit}.dropdown-search-wrapper .dropdown-search-input::placeholder{color:var(--cc-sf-input-placeholder, #C4C9D4)}.dropdown-search-wrapper .dropdown-search-clear{font-size:18px;width:18px;height:18px;color:var(--cc-sf-input-placeholder, #9CA3AF);flex-shrink:0;transition:color .15s}.dropdown-search-wrapper .dropdown-search-clear:hover{color:var(--cc-sf-label-color, #111827)}.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:var(--cc-sf-label-color, #6B7280);flex-shrink:0}.multi-select-wrapper .multi-select-panel{position:absolute;top:calc(100% + 4px);left:0;right:0;background:var(--cc-sf-input-bg, #ffffff);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;display:flex;flex-direction:column;overflow:hidden;max-height:320px}.multi-select-wrapper .multi-select-options-list{flex:1;overflow-y:auto;max-height:240px}.multi-select-wrapper .multi-select-options-list::-webkit-scrollbar{width:4px}.multi-select-wrapper .multi-select-options-list::-webkit-scrollbar-thumb{background:var(--cc-sf-input-border, #D1D5DB);border-radius:2px}.multi-select-wrapper .multi-select-option{padding:8px 12px;border-bottom:1px solid var(--cc-sf-input-border, #F3F4F6);transition:background .1s}.multi-select-wrapper .multi-select-option:last-of-type{border-bottom:none}.multi-select-wrapper .multi-select-option:hover{background:var(--cc-sf-input-hover-bg, #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-option.multi-select-all{border-bottom:1px solid var(--cc-sf-input-border, #E5E7EB);background:var(--cc-sf-input-bg, #FAFBFC)}.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}.single-select-wrapper{position:relative}.single-select-wrapper .single-select-trigger{min-height:var(--cc-sf-input-height, 44px);height:auto;-webkit-user-select:none;user-select:none}.single-select-wrapper .single-select-trigger.ss-open{border-color:var(--cc-sf-input-focus-border, #6366F1)!important;box-shadow:0 0 0 3px #6366f124,0 1px 4px #6366f11a!important}.single-select-wrapper .single-select-trigger .single-select-value{flex:1;color:var(--cc-sf-label-color, #111827);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.single-select-wrapper .single-select-trigger .single-select-placeholder{flex:1;font-weight:400;font-size:14px;color:var(--cc-sf-input-placeholder, #C4C9D4)}.single-select-wrapper .single-select-trigger .single-select-arrow{font-size:20px;width:20px;height:20px;line-height:20px;color:var(--cc-sf-label-color, #6B7280);flex-shrink:0}.single-select-wrapper .single-select-panel{position:absolute;top:calc(100% + 4px);left:0;right:0;background:var(--cc-sf-input-bg, #ffffff);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;display:flex;flex-direction:column;overflow:hidden;max-height:320px}.single-select-wrapper .single-select-options-list{flex:1;overflow-y:auto;max-height:240px}.single-select-wrapper .single-select-options-list::-webkit-scrollbar{width:4px}.single-select-wrapper .single-select-options-list::-webkit-scrollbar-thumb{background:var(--cc-sf-input-border, #D1D5DB);border-radius:2px}.single-select-wrapper .single-select-option{padding:8px 12px;border-bottom:1px solid var(--cc-sf-input-border, #F3F4F6);transition:background .1s;justify-content:space-between}.single-select-wrapper .single-select-option:last-of-type{border-bottom:none}.single-select-wrapper .single-select-option:hover{background:var(--cc-sf-input-hover-bg, #F9FAFB)}.single-select-wrapper .single-select-option.selected{background:var(--cc-sf-input-hover-bg, #F0F5FF);color:var(--cc-sf-selected-color, #6366F1)}.single-select-wrapper .single-select-option .single-select-check{font-size:18px;width:18px;height:18px;color:var(--cc-sf-selected-color, #6366F1)}.single-select-wrapper.is-invalid .single-select-trigger{border-color:var(--cc-sf-error-border, #EF4444)!important;background-color:var(--cc-sf-error-bg, #FFF5F5)!important}.multi-select-panel,.single-select-panel{width:100%;background:var(--cc-sf-input-bg, #ffffff);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;display:flex;flex-direction:column;overflow:hidden;max-height:320px}.multi-select-options-list,.single-select-options-list{flex:1;overflow-y:auto;max-height:240px}.multi-select-options-list::-webkit-scrollbar,.single-select-options-list::-webkit-scrollbar{width:4px}.multi-select-options-list::-webkit-scrollbar-thumb,.single-select-options-list::-webkit-scrollbar-thumb{background:var(--cc-sf-input-border, #D1D5DB);border-radius:2px}.multi-select-option,.single-select-option{display:flex;align-items:center;gap:8px;cursor:pointer;padding:8px 12px;border-bottom:1px solid var(--cc-sf-input-border, #F3F4F6);transition:background .1s}.multi-select-option:last-of-type,.single-select-option:last-of-type{border-bottom:none}.multi-select-option:hover,.single-select-option:hover{background:var(--cc-sf-input-hover-bg, #F9FAFB)}.multi-select-option input[type=checkbox],.single-select-option input[type=checkbox]{flex-shrink:0;cursor:pointer;accent-color:var(--cc-sf-selected-color, #6366F1);width:15px;height:15px}.multi-select-option.multi-select-all{border-bottom:1px solid var(--cc-sf-input-border, #E5E7EB);background:var(--cc-sf-input-bg, #FAFBFC)}.single-select-option{justify-content:space-between}.single-select-option.selected{background:var(--cc-sf-input-hover-bg, #F0F5FF);color:var(--cc-sf-selected-color, #6366F1)}.single-select-option .single-select-check{font-size:18px;width:18px;height:18px;color:var(--cc-sf-selected-color, #6366F1)}.multi-select-empty{padding:12px;text-align:center}::ng-deep .cdk-overlay-container{z-index:1100}.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)}.suffix-action-icons{right:.875rem;top:50%;transform:translateY(-50%);z-index:1;pointer-events:auto}.suffix-action-icons mat-icon.suffix-action-icon{font-size:1.125rem;width:1.125rem;height:1.125rem;line-height:1.125rem;color:var(--cc-sf-hint-color, #6B7280);cursor:pointer;transition:color .2s ease,transform .15s ease}.suffix-action-icons mat-icon.suffix-action-icon:hover{color:var(--cc-sf-text-color, #202124);transform:scale(1.15)}.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}.link-list-container{border:none!important;background-color:transparent!important;padding:0!important;box-shadow:none!important;overflow:visible!important}.link-items-list{list-style:none}@media(max-width:640px){.link-items-list{grid-template-columns:1fr!important}.link-items-list .link-list-item{grid-column:span 12!important}}.link-list-item{display:flex;align-items:center;justify-content:space-between;padding:10px 16px;background-color:var(--cc-sf-link-item-bg, #f9fafb)!important;border:1px solid var(--cc-sf-link-item-border, #E5E7EB)!important;border-radius:var(--cc-sf-input-radius, 9px)!important;gap:12px;min-height:48px}.link-list-item .link-url-text{font-size:.875rem;color:var(--cc-sf-link-item-text, #111827);word-break:break-all}.link-list-item .action-btn{border:none;background:transparent;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;padding:0;border-radius:6px;transition:background .15s ease,color .15s ease}.link-list-item .action-btn mat-icon{font-size:22px;width:22px;height:22px;line-height:22px;display:flex;align-items:center;justify-content:center}.link-list-item .action-btn.edit-btn{color:var(--cc-sf-link-edit-color, #4b5563)}.link-list-item .action-btn.edit-btn:hover{color:var(--cc-sf-accent-color, #6366F1);background-color:#f3f4f6}.link-list-item .action-btn.delete-btn{color:var(--cc-sf-link-delete-color, #ef4444)}.link-list-item .action-btn.delete-btn:hover{background-color:var(--cc-sf-error-bg, #FFF5F5)}.link-list-item .action-btn.save-btn{color:var(--cc-sf-link-save-color, #16a34a)!important}.link-list-item .action-btn.save-btn:hover{background-color:#ecfdf5}.link-list-item .action-btn.cancel-btn{color:#ef4444!important}.link-list-item .action-btn.cancel-btn:hover{background-color:var(--cc-sf-error-bg, #FFF5F5)}.pe-40px{padding-right:40px!important}.dropdown-loading-state{display:flex;align-items:center;justify-content:center;padding:16px;gap:8px}.dropdown-spinner{width:20px;height:20px;border:2px solid #E2E8F0;border-top-color:var(--cc-sf-accent-color, #3B82F6);border-radius:50%;animation:cc-spin .7s linear infinite}.mini-spinner{width:16px;height:16px;border:2px solid #E2E8F0;border-top-color:var(--cc-sf-accent-color, #3B82F6);border-radius:50%;animation:cc-spin .7s linear infinite}.native-select-wrapper{display:block;width:100%;position:relative}.native-select-wrapper select{width:100%}.native-select-wrapper .native-select-spinner{position:absolute;right:2.5rem;top:calc(50% - 7px);width:14px;height:14px;border:2px solid #CBD5E1;border-top-color:var(--cc-sf-accent-color, #3B82F6);border-radius:50%;animation:cc-spin .7s linear infinite;pointer-events:none}\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$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$2.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$2.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1$2.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: "directive", type: i2$1.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i2$1.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: ButtonComponent, selector: "lib-button", inputs: ["variant", "type", "disabled", "width", "height", "borderRadius", "fontSize", "fontWeight", "backgroundColor", "color", "border", "icon", "labels"] }, { kind: "component", type: i12.QuillEditorComponent, selector: "quill-editor" }, { kind: "component", type: TimePickerComponent, selector: "lib-time-picker", inputs: ["config", "labels", "label", "placeholder", "variant", "mode", "minuteStep", "disabled", "required", "errorMessage", "minTime", "maxTime", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "padding", "fontWeight", "color", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor"], outputs: ["timeChange"] }, { kind: "component", type: FormFieldComponent, selector: "lib-form-field", inputs: ["config", "controller", "formGroup", "allowMulti", "siblingFormArray", "siblingIndex"] }, { kind: "pipe", type: TrustedUrlPipe, name: "trustedUrl" }] });
4498
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FormFieldComponent, deps: [{ token: i1$2.FormBuilder }, { token: ExpressionService }, { token: i3.HttpClient }, { token: i0.ElementRef }, { token: i4.Overlay }], target: i0.ɵɵFactoryTarget.Component });
4499
+ 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", siblingFormArray: "siblingFormArray", siblingIndex: "siblingIndex" }, host: { listeners: { "document:click": "onDocumentClick()", "window:resize": "onWindowResize()", "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-8px\" *ngIf=\"isVisible\" [class.has-error]=\"errorMessage\">\r\n\r\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 -->\r\n <div *ngIf=\"isRow\" class=\"form-row grid-row\">\r\n <ng-container *ngFor=\"let child of config.children; trackBy: trackByFieldName\">\r\n <div class=\"row-field\" [style.gridColumn]=\"'span ' + getChildColSpan(child)\" *ngIf=\"child.isEnabled !== false\"\r\n [style.display]=\"rowFieldRef.isVisible === false ? 'none' : null\">\r\n <lib-form-field #rowFieldRef [config]=\"child\" [controller]=\"controller\" [formGroup]=\"formGroup\" [allowMulti]=\"allowMulti\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isGroup && config.sectionConfig?.allowMulti\"\r\n class=\"group-section-wrapper mb-20px\"\r\n [class.multi-save-active]=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n\r\n <!-- Multi-Save: header row with label + top-right Add button -->\r\n <div class=\"multi-save-header d-flex justify-content-between align-items-center mb-24\"\r\n *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n <h3 class=\"group-label\" *ngIf=\"config.sectionConfig?.label\">{{ config.sectionConfig!.label }}</h3>\r\n <lib-button [variant]=\"'outline'\" [icon]=\"{type: 'material', value: 'add'}\" (click)=\"addGroupInstance()\"\r\n class=\"btn-add-multi\">\r\n {{ addMultiLabel }}\r\n </lib-button>\r\n </div>\r\n\r\n <!-- Standard heading (non-multiSave) -->\r\n <h3 class=\"group-label\"\r\n *ngIf=\"config.sectionConfig?.label && !config.sectionConfig?.multiSaveConfig?.active\">{{\r\n config.sectionConfig!.label }}</h3>\r\n\r\n <!-- \u2500\u2500 Standard (non-multiSave) repeater: accordion instances \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <ng-container *ngIf=\"!config.sectionConfig?.multiSaveConfig?.active\">\r\n <div *ngFor=\"let instance of instanceList; trackBy: trackByInstanceId; let i = index\"\r\n class=\"group-accordion-instance\"\r\n [class.is-expanded]=\"isGroupExpanded(i)\">\r\n\r\n <!-- Accordion header -->\r\n <div class=\"group-accordion-header\" (click)=\"toggleGroupAccordion(i)\"\r\n role=\"button\" [attr.aria-expanded]=\"isGroupExpanded(i)\">\r\n <div class=\"accordion-header-left d-flex align-items-center gap-10\">\r\n <span class=\"instance-badge\">{{ i + 1 }}</span>\r\n <span class=\"instance-title\">{{ config.sectionConfig!.label }} #{{ i + 1 }}</span>\r\n </div>\r\n <div class=\"accordion-header-right d-flex align-items-center gap-6\">\r\n <button type=\"button\" class=\"accordion-remove-btn\"\r\n *ngIf=\"instanceList.length > 1\"\r\n (click)=\"$event.stopPropagation(); removeGroupInstance(i)\"\r\n aria-label=\"Remove\">\r\n <mat-icon>delete_outline</mat-icon>\r\n </button>\r\n <mat-icon class=\"accordion-chevron\">\r\n {{ isGroupExpanded(i) ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n\r\n <!-- Accordion body (always mounted so form controls survive collapse) -->\r\n <div class=\"group-accordion-body\" [hidden]=\"!isGroupExpanded(i)\">\r\n <div class=\"group-fields sf-grid\">\r\n <ng-container *ngFor=\"let field of config.sectionConfig!.children\">\r\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\"\r\n *ngIf=\"field.isEnabled !== false\"\r\n [style.display]=\"groupFieldRef.isVisible === false ? 'none' : null\">\r\n <lib-form-field #groupFieldRef [config]=\"field\" [controller]=\"instance.rowController\" [formGroup]=\"instance.fg\"\r\n [allowMulti]=\"false\" [siblingFormArray]=\"groupFormArray\" [siblingIndex]=\"i\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Full-width dashed Add button -->\r\n <button type=\"button\" class=\"btn-add-group\" (click)=\"addGroupInstance()\">\r\n <mat-icon>add</mat-icon> {{ addLabel }} {{ config.sectionConfig!.label }}\r\n </button>\r\n </ng-container>\r\n\r\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 -->\r\n <ng-container *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n <div *ngFor=\"let instance of instanceList; trackBy: trackByInstanceId; let i = index\"\r\n class=\"group-instance position-relative mb-16 overflow-hidden\"\r\n [class.is-editing]=\"instance.isEditing\"\r\n [class.is-card]=\"!instance.isEditing\">\r\n\r\n <!-- Edit / new form view -->\r\n <div [hidden]=\"!instance.isEditing\">\r\n <div class=\"group-fields sf-grid\">\r\n <ng-container *ngFor=\"let field of config.sectionConfig!.children\">\r\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\"\r\n *ngIf=\"field.isEnabled !== false\"\r\n [style.display]=\"groupFieldRef.isVisible === false ? 'none' : null\">\r\n <lib-form-field #groupFieldRef [config]=\"field\" [controller]=\"instance.rowController\" [formGroup]=\"instance.fg\"\r\n [allowMulti]=\"false\" [siblingFormArray]=\"groupFormArray\" [siblingIndex]=\"i\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n\r\n <!-- Save / Cancel -->\r\n <div class=\"group-footer d-flex justify-content-end align-items-center gap-16 p-0-24\"\r\n *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"multiSaveError\">{{ multiSaveError }}</span>\r\n <div class=\"footer-actions d-flex gap-12\">\r\n <lib-button [variant]=\"'outline'\" (click)=\"cancelGroupInstance(i)\">Cancel</lib-button>\r\n <lib-button [variant]=\"'primary'\" (click)=\"saveGroupInstance(i)\">Save</lib-button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Card view (saved state) -->\r\n <ng-container *ngIf=\"!instance.isEditing\">\r\n <div class=\"card-view d-flex justify-content-between align-items-center p-18px-24px\"\r\n [class.is-expanded]=\"instance.isExpanded\">\r\n <div class=\"card-content flex-1 d-flex flex-column gap-4 overflow-hidden\">\r\n <span class=\"card-title font-weight-600 overflow-hidden fs-1rem c-111827\">{{\r\n instance.fg.get(config.sectionConfig!.multiSaveConfig!.summaryField || '')?.value\r\n || '\u2014' }}</span>\r\n </div>\r\n <div class=\"card-actions d-flex align-items-center gap-16 ml-20\">\r\n <mat-icon class=\"icon-delete\" (click)=\"removeGroupInstance(i, true)\">delete_outline</mat-icon>\r\n <mat-icon class=\"icon-edit\" (click)=\"editGroupInstance(i)\">edit_outline</mat-icon>\r\n <mat-icon class=\"icon-expand\" (click)=\"toggleExpandGroupInstance(i)\">\r\n {{ instance.isExpanded ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </ng-container>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isGroup && config.sectionConfig && !config.sectionConfig.allowMulti\"\r\n class=\"group-section-wrapper mb-20px\">\r\n <h3 class=\"group-label\" *ngIf=\"config.sectionConfig.label\">{{ config.sectionConfig.label }}</h3>\r\n <div class=\"group-fields sf-grid\">\r\n <ng-container *ngFor=\"let field of config.sectionConfig.children\">\r\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\" *ngIf=\"field.isEnabled !== false\"\r\n [style.display]=\"singleGroupFieldRef.isVisible === false ? 'none' : null\">\r\n <lib-form-field #singleGroupFieldRef [config]=\"field\" [controller]=\"controller\" [formGroup]=\"groupFormGroup\" [allowMulti]=\"false\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n\r\n\r\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 -->\r\n <div *ngIf=\"isTextField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <textarea *ngIf=\"config.subType === 'LONG'\" class=\"field-input textarea\" [placeholder]=\"config.placeholder || ''\"\r\n [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\" rows=\"4\">\r\n </textarea>\r\n\r\n <!-- Password input with show/hide toggle -->\r\n <div *ngIf=\"config.subType === 'PASSWORD'\" class=\"password-wrapper position-relative d-flex align-items-center\">\r\n <input [type]=\"showPassword ? 'text' : 'password'\" class=\"field-input password-input\"\r\n [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\r\n <button type=\"button\"\r\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\"\r\n (click)=\"showPassword = !showPassword\" tabindex=\"-1\"\r\n [attr.aria-label]=\"showPassword ? 'Hide password' : 'Show password'\">\r\n <mat-icon class=\"eye-icon\">{{ showPassword ? 'visibility' : 'visibility_off' }}</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <span class=\"input-prefix br-none\" *ngIf=\"config.prefix\">{{ config.prefix }}</span>\r\n\r\n <input *ngIf=\"config.subType !== 'LONG' && config.subType !== 'PASSWORD'\"\r\n [type]=\"config.subType === 'EMAIL' ? 'email' : config.subType === 'PHONE' ? 'tel' : 'text'\" class=\"field-input\"\r\n [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\"\r\n [readonly]=\"config.readonly\">\r\n\r\n <span class=\"input-suffix d-flex align-items-center font-weight-500\" *ngIf=\"config.suffix\">{{ config.suffix\r\n }}</span>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n\r\n <!-- Suffix Action Icons (clickable) -->\r\n <div class=\"suffix-action-icons position-absolute d-flex gap-8 align-items-center\"\r\n *ngIf=\"config.suffixActionIcons?.length && !config.readonly\">\r\n <mat-icon class=\"suffix-action-icon\"\r\n *ngFor=\"let action of config.suffixActionIcons\"\r\n [style.color]=\"action.color || null\"\r\n [title]=\"action.tooltip || ''\"\r\n (click)=\"onSuffixActionClick(action.actionId)\">\r\n {{ action.icon }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n <div class=\"char-count-hint font-poppins font-weight-400 text-14 text-right c-6B7280\" *ngIf=\"showCharCount\">\r\n {{ remainingCharacters }} characters remaining\r\n </div>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isNumberField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <span class=\"input-prefix br-none\" *ngIf=\"config.prefix\">{{ config.prefix }}</span>\r\n\r\n <input type=\"number\" class=\"field-input\" [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\"\r\n [min]=\"config.numberConfig?.min ?? null\" [max]=\"config.numberConfig?.max ?? null\"\r\n [step]=\"config.numberConfig?.step || 1\" [class.is-invalid]=\"errorMessage\" [readonly]=\"config.readonly\">\r\n\r\n <span class=\"input-suffix d-flex align-items-center font-weight-500\" *ngIf=\"config.suffix\">{{ config.suffix\r\n }}</span>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n\r\n <!-- Suffix Action Icons (clickable) -->\r\n <div class=\"suffix-action-icons position-absolute d-flex gap-8 align-items-center\"\r\n *ngIf=\"config.suffixActionIcons?.length && !config.readonly\">\r\n <mat-icon class=\"suffix-action-icon\"\r\n *ngFor=\"let action of config.suffixActionIcons\"\r\n [style.color]=\"action.color || null\"\r\n [title]=\"action.tooltip || ''\"\r\n (click)=\"onSuffixActionClick(action.actionId)\">\r\n {{ action.icon }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isDateField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <input matInput [matDatepicker]=\"datePicker\" class=\"field-input date-input has-icon-right\"\r\n [formControlName]=\"config.name!\" [min]=\"effectiveMinDate\" [max]=\"config.dateConfig?.maxDate\"\r\n [class.is-invalid]=\"errorMessage\" [placeholder]=\"config.placeholder || ''\"\r\n [readonly]=\"config.readonly || config.dateConfig?.inputReadonly\"\r\n (click)=\"!config.readonly && datePicker.open()\">\r\n <div class=\"date-icon-wrapper position-absolute d-flex align-items-center justify-content-center\"\r\n *ngIf=\"!config.readonly\">\r\n <mat-datepicker-toggle matSuffix [for]=\"datePicker\"></mat-datepicker-toggle>\r\n </div>\r\n <mat-datepicker #datePicker></mat-datepicker>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isTimeField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <!-- DEFAULT variant: existing native input \u2014 unchanged -->\r\n <ng-container *ngIf=\"!isWheelVariant\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <input type=\"time\" class=\"field-input time-input\" [formControlName]=\"config.name!\"\r\n [min]=\"effectiveMinTime\" [max]=\"effectiveMaxTime\"\r\n [class.is-invalid]=\"errorMessage\" [readonly]=\"config.readonly || config.timeConfig?.inputReadonly\">\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- WHEEL variant: drum-roll picker -->\r\n <lib-time-picker\r\n *ngIf=\"isWheelVariant\"\r\n [formControlName]=\"config.name!\"\r\n [label]=\"config.label || ''\"\r\n [placeholder]=\"config.timeConfig?.placeholder || ''\"\r\n [required]=\"config.required || false\"\r\n [disabled]=\"config.readonly || false\"\r\n [mode]=\"config.timeConfig?.mode || '12'\"\r\n [minuteStep]=\"config.timeConfig?.minuteStep || 1\"\r\n [minTime]=\"effectiveMinTime || undefined\"\r\n [maxTime]=\"config.timeConfig?.maxTime\"\r\n [errorMessage]=\"errorMessage\"\r\n [labels]=\"timePickerLabels\">\r\n </lib-time-picker>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isAutocomplete\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <!-- Hidden real control (stores the code value) -->\r\n <input type=\"hidden\" [formControlName]=\"config.name!\">\r\n\r\n <div class=\"autocomplete-wrapper position-relative d-flex align-items-center w-100\"\r\n [class.is-invalid]=\"errorMessage\" [class.readonly]=\"config.readonly\">\r\n <!-- Search icon -->\r\n <mat-icon class=\"ac-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">search</mat-icon>\r\n\r\n <input class=\"field-input ac-input\" [formControl]=\"autocompleteInputCtrl\" [matAutocomplete]=\"auto\"\r\n [placeholder]=\"config.placeholder || 'Search\u2026'\" [readonly]=\"!!config.readonly\" [class.is-invalid]=\"errorMessage\"\r\n (blur)=\"onAutocompleteClear()\" autocomplete=\"off\">\r\n\r\n <!-- Clear button -->\r\n <button type=\"button\"\r\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\"\r\n *ngIf=\"autocompleteInputCtrl.value && !config.readonly\"\r\n (click)=\"autocompleteInputCtrl.setValue(''); updateValue(null)\" tabindex=\"-1\" aria-label=\"Clear\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n\r\n <mat-autocomplete #auto=\"matAutocomplete\" [panelWidth]=\"'auto'\">\r\n <mat-option *ngFor=\"let option of filteredOptions\" [value]=\"option.label\"\r\n (onSelectionChange)=\"onAutocompleteSelected(option)\">\r\n <span class=\"ac-option-label\">{{ option.label }}</span>\r\n\r\n <!-- Dynamic display fields (image / email / phone / text) -->\r\n <div class=\"ac-display-fields d-flex flex-wrap gap-6 mt-2\" *ngIf=\"option['displayMeta']?.length\">\r\n <ng-container *ngFor=\"let field of option['displayMeta']\">\r\n\r\n <!-- Image avatar -->\r\n <span *ngIf=\"field.type === 'image' && field.value\" class=\"ac-df-item ac-df-image\">\r\n <img [src]=\"field.value\" [alt]=\"field.label || 'image'\" class=\"ac-df-avatar\">\r\n </span>\r\n\r\n <!-- Email -->\r\n <span *ngIf=\"field.type === 'email' && field.value\" class=\"ac-df-item ac-df-chip\">\r\n <mat-icon class=\"ac-df-icon\">mail_outline</mat-icon>\r\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\r\n {{ field.value }}\r\n </span>\r\n\r\n <!-- Phone -->\r\n <span *ngIf=\"field.type === 'phone' && field.value\" class=\"ac-df-item ac-df-chip\" [class]=\"field.className\">\r\n <mat-icon class=\"ac-df-icon\">phone</mat-icon>\r\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\r\n {{ field.value }}\r\n </span>\r\n\r\n <!-- Custom / Icon-based / Generic Text -->\r\n <span *ngIf=\"field.type !== 'image' && field.type !== 'email' && field.type !== 'phone' && field.value\" \r\n class=\"ac-df-item\" [class.ac-df-chip]=\"!!field.icon\" [class]=\"field.className\">\r\n <mat-icon class=\"ac-df-icon\" *ngIf=\"field.icon\">{{ field.icon }}</mat-icon>\r\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\r\n {{ field.value }}\r\n </span>\r\n\r\n </ng-container>\r\n </div>\r\n </mat-option>\r\n <mat-option *ngIf=\"filteredOptions.length === 0\" disabled class=\"ac-no-results fs-0-8125rem c-6B7280\">\r\n No results found\r\n </mat-option>\r\n </mat-autocomplete>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isDropdown\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <!-- SINGLE SELECT: native select (default, non-searchable, non-paginated) -->\r\n <div *ngIf=\"config.subType === 'SINGLE' && !isCustomSingleDropdown\" class=\"native-select-wrapper\">\r\n <select class=\"field-input\" [formControlName]=\"config.name!\"\r\n [class.is-invalid]=\"errorMessage\" [disabled]=\"optionsLoading || config.disabled\">\r\n <option [ngValue]=\"null\" disabled selected>{{ optionsLoading ? (controller.labels['LBL_LOADING'] || 'Loading...') : (config.placeholder || selectPlaceholderLabel) }}</option>\r\n <option *ngFor=\"let option of displayLocalOptionList\" [value]=\"option.code\">\r\n {{ option.label }}\r\n </option>\r\n </select>\r\n <div *ngIf=\"optionsLoading\" class=\"native-select-spinner\" aria-hidden=\"true\"></div>\r\n </div>\r\n\r\n <!-- SINGLE SELECT: custom dropdown panel (searchable or paginated) -->\r\n <div *ngIf=\"config.subType === 'SINGLE' && isCustomSingleDropdown\" class=\"single-select-wrapper\"\r\n [class.is-invalid]=\"errorMessage\">\r\n\r\n <div #singleTrigger cdkOverlayOrigin #singleOrigin=\"cdkOverlayOrigin\"\r\n class=\"field-input single-select-trigger d-flex align-items-center justify-content-between cursor-pointer\"\r\n [class.ss-open]=\"isSingleDropdownOpen\"\r\n (click)=\"!optionsLoading && toggleSingleDropdown($event)\">\r\n <span *ngIf=\"singleSelectedLabel && !optionsLoading\" class=\"single-select-value fs-0-9rem\">\r\n {{ singleSelectedLabel }}\r\n </span>\r\n <span *ngIf=\"!singleSelectedLabel || optionsLoading\" class=\"single-select-placeholder\">\r\n {{ optionsLoading ? (controller.labels['LBL_LOADING'] || 'Loading...') : (config.placeholder || selectPlaceholderLabel) }}\r\n </span>\r\n <div class=\"d-flex align-items-center\">\r\n <div *ngIf=\"optionsLoading\" class=\"mini-spinner me-2\" role=\"progressbar\" aria-label=\"Loading options\"></div>\r\n <mat-icon class=\"single-select-arrow\">\r\n {{ isSingleDropdownOpen ? expandLessLabel : expandMoreLabel }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n\r\n <ng-template cdkConnectedOverlay\r\n [cdkConnectedOverlayOrigin]=\"singleOrigin\"\r\n [cdkConnectedOverlayOpen]=\"isSingleDropdownOpen\"\r\n [cdkConnectedOverlayPositions]=\"dropdownOverlayPositions\"\r\n [cdkConnectedOverlayScrollStrategy]=\"dropdownScrollStrategy\"\r\n [cdkConnectedOverlayWidth]=\"singleTrigger.offsetWidth\"\r\n [cdkConnectedOverlayViewportMargin]=\"8\"\r\n [cdkConnectedOverlayPush]=\"true\"\r\n (overlayOutsideClick)=\"onDropdownOverlayOutsideClick($event, singleTrigger)\"\r\n (detach)=\"isSingleDropdownOpen = false\">\r\n <div class=\"single-select-panel\"\r\n (click)=\"$event.stopPropagation()\">\r\n\r\n <!-- Search input (shown only when searchConfig.enabled) -->\r\n <div class=\"dropdown-search-wrapper\" *ngIf=\"isSearchableDropdown\">\r\n <mat-icon class=\"dropdown-search-icon\">search</mat-icon>\r\n <input type=\"text\" class=\"dropdown-search-input\"\r\n [placeholder]=\"searchPlaceholderLabel\"\r\n [(ngModel)]=\"searchText\" [ngModelOptions]=\"{standalone: true}\"\r\n (input)=\"dropdownSearch$.next(searchText)\"\r\n (click)=\"$event.stopPropagation()\">\r\n <mat-icon *ngIf=\"searchText\" class=\"dropdown-search-clear cursor-pointer\"\r\n (click)=\"searchText = ''; dropdownSearch$.next(''); $event.stopPropagation()\">close</mat-icon>\r\n </div>\r\n\r\n <!-- Options list -->\r\n <div class=\"single-select-options-list\" (scroll)=\"onDropdownScroll($event)\">\r\n <div *ngIf=\"optionsLoading\" class=\"dropdown-loading-state\">\r\n <div class=\"dropdown-spinner\"></div>\r\n <span class=\"fs-0-875rem c-6B7280\">{{ controller.labels['LBL_LOADING'] || 'Loading...' }}</span>\r\n </div>\r\n\r\n <ng-container *ngIf=\"!optionsLoading\">\r\n <div *ngFor=\"let option of displayOptionList\"\r\n class=\"single-select-option d-flex align-items-center gap-8 cursor-pointer\"\r\n [class.selected]=\"formGroup.get(config.name!)?.value === option.code\"\r\n (click)=\"selectSingleOption(option)\">\r\n <span class=\"fs-0-875rem c-111827\">{{ option.label }}</span>\r\n <mat-icon *ngIf=\"formGroup.get(config.name!)?.value === option.code\" class=\"single-select-check\">check</mat-icon>\r\n </div>\r\n <div *ngIf=\"!filteredOptionList?.length && localOptionList?.length\"\r\n class=\"multi-select-empty fs-0-875rem c-6B7280\">\r\n {{ noMatchingOptionsLabel }}\r\n </div>\r\n <div *ngIf=\"!localOptionList?.length\"\r\n class=\"multi-select-empty fs-0-875rem c-6B7280\">\r\n {{ noOptionsAvailableLabel }}\r\n </div>\r\n\r\n <!-- Load More button / Pagination Spinner -->\r\n <div *ngIf=\"config.optionConfig?.pagination?.enabled && ((hasMoreDropdownPages && config.optionConfig?.pagination?.mode !== 'infiniteScroll') || isDropdownLoadingMore)\" class=\"dropdown-pagination-footer\">\r\n <button type=\"button\" class=\"btn-dropdown-load-more\"\r\n *ngIf=\"hasMoreDropdownPages && config.optionConfig?.pagination?.mode !== 'infiniteScroll' && !isDropdownLoadingMore\"\r\n (click)=\"loadMoreDropdownOptions(); $event.stopPropagation()\">\r\n {{ controller.labels['LBL_LOAD_MORE'] || 'Load More' }}\r\n </button>\r\n <div *ngIf=\"isDropdownLoadingMore\" class=\"dropdown-loading-more-state\">\r\n <div class=\"dropdown-spinner-small\"></div>\r\n <span class=\"fs-0-8125rem c-6B7280\">{{ controller.labels['LBL_LOADING_MORE'] || 'Loading more...' }}</span>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </ng-template>\r\n </div>\r\n\r\n <!-- MULTIPLE SELECT: custom panel with checkboxes -->\r\n <div *ngIf=\"config.subType === 'MULTIPLE'\" class=\"multi-select-wrapper\"\r\n [class.is-invalid]=\"errorMessage\">\r\n\r\n <div #multiTrigger cdkOverlayOrigin #multiOrigin=\"cdkOverlayOrigin\"\r\n class=\"field-input multi-select-trigger d-flex align-items-center justify-content-between cursor-pointer\"\r\n [class.ms-open]=\"isMultiDropdownOpen\"\r\n (click)=\"!optionsLoading && toggleMultiDropdown($event)\">\r\n <span *ngIf=\"multiSelectedCount > 0 && !optionsLoading\" class=\"multi-select-value fs-0-9rem\">\r\n {{ multiSelectedCount }} selected\r\n </span>\r\n <span *ngIf=\"multiSelectedCount === 0 || optionsLoading\" class=\"multi-select-placeholder\">\r\n {{ optionsLoading ? (controller.labels['LBL_LOADING'] || 'Loading...') : (config.placeholder || selectPlaceholderLabel) }}\r\n </span>\r\n <div class=\"d-flex align-items-center\">\r\n <div *ngIf=\"optionsLoading\" class=\"mini-spinner me-2\" role=\"progressbar\" aria-label=\"Loading options\"></div>\r\n <mat-icon class=\"multi-select-arrow\">\r\n {{ isMultiDropdownOpen ? expandLessLabel : expandMoreLabel }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n\r\n <ng-template cdkConnectedOverlay\r\n [cdkConnectedOverlayOrigin]=\"multiOrigin\"\r\n [cdkConnectedOverlayOpen]=\"isMultiDropdownOpen\"\r\n [cdkConnectedOverlayPositions]=\"dropdownOverlayPositions\"\r\n [cdkConnectedOverlayScrollStrategy]=\"dropdownScrollStrategy\"\r\n [cdkConnectedOverlayWidth]=\"multiTrigger.offsetWidth\"\r\n [cdkConnectedOverlayViewportMargin]=\"8\"\r\n [cdkConnectedOverlayPush]=\"true\"\r\n (overlayOutsideClick)=\"onDropdownOverlayOutsideClick($event, multiTrigger)\"\r\n (detach)=\"isMultiDropdownOpen = false\">\r\n <div class=\"multi-select-panel\"\r\n (click)=\"$event.stopPropagation()\">\r\n\r\n <!-- Search input (shown only when searchConfig.enabled) -->\r\n <div class=\"dropdown-search-wrapper\" *ngIf=\"isSearchableDropdown\">\r\n <mat-icon class=\"dropdown-search-icon\">search</mat-icon>\r\n <input type=\"text\" class=\"dropdown-search-input\"\r\n [placeholder]=\"searchPlaceholderLabel\"\r\n [(ngModel)]=\"searchText\" [ngModelOptions]=\"{standalone: true}\"\r\n (input)=\"dropdownSearch$.next(searchText)\"\r\n (click)=\"$event.stopPropagation()\">\r\n <mat-icon *ngIf=\"searchText\" class=\"dropdown-search-clear cursor-pointer\"\r\n (click)=\"searchText = ''; dropdownSearch$.next(''); $event.stopPropagation()\">close</mat-icon>\r\n </div>\r\n\r\n <!-- Select All checkbox (shown only when showSelectAll is true) -->\r\n <label *ngIf=\"config.optionConfig?.showSelectAll && filteredOptionList?.length\"\r\n class=\"multi-select-option multi-select-all d-flex align-items-center gap-8 cursor-pointer\">\r\n <input type=\"checkbox\"\r\n [checked]=\"isAllSelected()\"\r\n [indeterminate]=\"isSomeSelected()\"\r\n [disabled]=\"!!config.disabled\"\r\n (change)=\"toggleSelectAll($any($event.target).checked)\">\r\n <span class=\"fs-0-875rem c-111827 fw-600\">{{ selectAllLabel }}</span>\r\n </label>\r\n\r\n <!-- Options list -->\r\n <div class=\"multi-select-options-list\" (scroll)=\"onDropdownScroll($event)\">\r\n <div *ngIf=\"optionsLoading\" class=\"dropdown-loading-state\">\r\n <div class=\"dropdown-spinner\"></div>\r\n <span class=\"fs-0-875rem c-6B7280\">{{ controller.labels['LBL_LOADING'] || 'Loading...' }}</span>\r\n </div>\r\n\r\n <ng-container *ngIf=\"!optionsLoading\">\r\n <label *ngFor=\"let option of filteredOptionList\"\r\n class=\"multi-select-option d-flex align-items-center gap-8 cursor-pointer\">\r\n <input type=\"checkbox\"\r\n [checked]=\"isChecked(option.code)\"\r\n [disabled]=\"!!config.disabled\"\r\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\">\r\n <span class=\"fs-0-875rem c-111827\">{{ option.label }}</span>\r\n </label>\r\n <div *ngIf=\"!filteredOptionList?.length && localOptionList?.length\"\r\n class=\"multi-select-empty fs-0-875rem c-6B7280\">\r\n {{ noMatchingOptionsLabel }}\r\n </div>\r\n <div *ngIf=\"!localOptionList?.length\"\r\n class=\"multi-select-empty fs-0-875rem c-6B7280\">\r\n {{ noOptionsAvailableLabel }}\r\n </div>\r\n\r\n <!-- Load More button / Pagination Spinner -->\r\n <div *ngIf=\"config.optionConfig?.pagination?.enabled && ((hasMoreDropdownPages && config.optionConfig?.pagination?.mode !== 'infiniteScroll') || isDropdownLoadingMore)\" class=\"dropdown-pagination-footer\">\r\n <button type=\"button\" class=\"btn-dropdown-load-more\"\r\n *ngIf=\"hasMoreDropdownPages && config.optionConfig?.pagination?.mode !== 'infiniteScroll' && !isDropdownLoadingMore\"\r\n (click)=\"loadMoreDropdownOptions(); $event.stopPropagation()\">\r\n {{ controller.labels['LBL_LOAD_MORE'] || 'Load More' }}\r\n </button>\r\n <div *ngIf=\"isDropdownLoadingMore\" class=\"dropdown-loading-more-state\">\r\n <div class=\"dropdown-spinner-small\"></div>\r\n <span class=\"fs-0-8125rem c-6B7280\">{{ controller.labels['LBL_LOADING_MORE'] || 'Loading more...' }}</span>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </ng-template>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isRadio\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"radio-group\" [class.is-invalid]=\"errorMessage\"\r\n [class]=\"config.optionConfig?.layout ? 'layout-' + config.optionConfig!.layout.toLowerCase() : ''\">\r\n <label *ngFor=\"let option of localOptionList\" class=\"radio-label\"\r\n [class.card-item]=\"config.subType === 'CARD'\"\r\n [class.selected]=\"formGroup.get(config.name!)?.value === option.code\"\r\n [style.gridColumn]=\"config.optionConfig?.layout?.toUpperCase() === 'COLUMN' ? 'span ' + getOptionColSpan(option) : null\">\r\n <input type=\"radio\" [formControlName]=\"config.name!\" [value]=\"option.code\">\r\n <div class=\"option-content d-flex flex-column gap-4 flex-1 text-left\">\r\n <span class=\"label-text text-16 c-1F2937\">{{ option.label }}</span>\r\n <span class=\"option-hint text-13 color-gray\" *ngIf=\"option.hint\">{{ option.hint }}</span>\r\n </div>\r\n </label>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isCheckbox\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label && config.subType === 'LIST'\"\r\n class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div *ngIf=\"config.subType === 'BOOL'\" class=\"checkbox-single\">\r\n <label class=\"checkbox-label d-flex align-items-center gap-8 cursor-pointer fs-0-875rem c-111827\">\r\n <input type=\"checkbox\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\r\n <span>{{ config.label }}</span>\r\n </label>\r\n </div>\r\n\r\n <div *ngIf=\"config.subType === 'LIST' || config.subType === 'CARD'\" class=\"checkbox-group d-flex flex-column gap-8\"\r\n [class.is-invalid]=\"errorMessage\"\r\n [class]=\"config.optionConfig?.layout ? 'layout-' + config.optionConfig!.layout.toLowerCase() : ''\">\r\n <label *ngFor=\"let option of localOptionList\"\r\n class=\"checkbox-label d-flex align-items-center gap-8 cursor-pointer fs-0-875rem c-111827\"\r\n [class.card-item]=\"config.subType === 'CARD'\" [class.selected]=\"isChecked(option.code)\"\r\n [style.gridColumn]=\"config.optionConfig?.layout?.toUpperCase() === 'COLUMN' ? 'span ' + getOptionColSpan(option) : null\">\r\n <input type=\"checkbox\" [checked]=\"isChecked(option.code)\" [disabled]=\"!!config.disabled\"\r\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\">\r\n <div class=\"option-content d-flex flex-column gap-4 flex-1 text-left\">\r\n <span class=\"label-text text-16 c-1F2937\">{{ option.label }}</span>\r\n <span class=\"option-hint text-13 color-gray\" *ngIf=\"option.hint\">{{ option.hint }}</span>\r\n </div>\r\n </label>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isChip\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"chip-group d-flex flex-wrap gap-8\" [class.is-invalid]=\"errorMessage\">\r\n <label *ngFor=\"let option of localOptionList\"\r\n class=\"chip-label cursor-pointer p-6px-14px b-ffffff c-374151 b-1px-solid-D1D5DB br-20px fs-0-875rem\"\r\n [class.selected]=\"isChecked(option.code)\">\r\n <input type=\"checkbox\" [checked]=\"isChecked(option.code)\" [disabled]=\"!!config.disabled\"\r\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\" hidden>\r\n <span>{{ option.label }}</span>\r\n </label>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isSwitch\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label class=\"switch-container d-flex justify-content-between align-items-center cursor-pointer\">\r\n <span class=\"field-label d-block font-poppins c-202124 fs-18px\">{{ config.label }}</span>\r\n <div class=\"switch position-relative\">\r\n <input type=\"checkbox\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\r\n <span class=\"slider position-absolute cursor-pointer\"></span>\r\n </div>\r\n </label>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isRichText\" class=\"field-wrapper component-rich-text d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"rich-text-container\" [class.is-invalid]=\"errorMessage\">\r\n <quill-editor [formControlName]=\"config.name!\" class=\"rich-text-editor d-block w-100\"\r\n [placeholder]=\"config.richTextConfig?.placeholder || config.placeholder || ''\"\r\n [styles]=\"{height: config.richTextConfig?.height || '200px'}\"\r\n [modules]=\"richTextModules\">\r\n </quill-editor>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n <div class=\"char-count-hint font-poppins font-weight-400 text-14 text-right c-6B7280\" *ngIf=\"showCharCount\">\r\n {{ remainingCharacters }} characters remaining\r\n </div>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isRating\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"rating-group d-flex gap-4\" [class.is-invalid]=\"errorMessage\">\r\n <span *ngFor=\"let star of getStarArray()\" class=\"star d-inline-flex align-items-center cursor-pointer\"\r\n [class.filled]=\"isStarFilled(star)\" [class.half]=\"isStarHalf(star)\" (click)=\"onRatingChange(star, $event)\">\r\n <mat-icon>{{ isStarFilled(star) || isStarHalf(star) ? 'star' : 'star_border' }}</mat-icon>\r\n </span>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isGenerated\" class=\"field-wrapper d-flex flex-column gap-6\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">{{ config.label\r\n }}</label>\r\n <div class=\"generated-value fs-0-875rem p-0-625rem-0-875rem b-F3F4F6 b-1px-solid-E5E7EB br-8px c-6B7280\">{{ value ||\r\n '-' }}</div>\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint\">{{ config.hint }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isFileUpload\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <!-- Drop Zone -->\r\n <div\r\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\"\r\n [class.drag-over]=\"isDragOver\" [class.has-files]=\"value?.length\" [class.is-invalid]=\"errorMessage\"\r\n (dragover)=\"onDragOver($event)\" (dragleave)=\"onDragLeave($event)\" (drop)=\"onFileDrop($event)\"\r\n (click)=\"fileInput.click()\">\r\n\r\n <!-- Icon with accent-colour pill background -->\r\n <div class=\"upload-icon-wrap mb-4\">\r\n <div class=\"dropzone-icon-pill d-flex align-items-center justify-content-center\">\r\n <mat-icon class=\"upload-cloud-icon\">cloud_upload</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <p class=\"upload-main-text font-weight-600 m-0 fs-0-9rem\">Drag &amp; drop files here</p>\r\n <p class=\"upload-sub-text m-0 fs-0-8rem c-64748B\">or <span class=\"upload-link\">Browse files</span></p>\r\n <p class=\"upload-hint-text m-0 fs-0-78rem c-64748B\" *ngIf=\"config.attachmentConfig?.acceptLabel\">\r\n Supported: <span class=\"upload-formats font-weight-500\">{{ config.attachmentConfig!.acceptLabel }}</span>\r\n </p>\r\n <p class=\"upload-hint-text m-0 fs-0-78rem c-64748B\" *ngIf=\"!config.attachmentConfig?.acceptLabel && config.hint\">\r\n {{ config.hint }}\r\n </p>\r\n <span class=\"upload-size-badge fs-0-72rem\" *ngIf=\"config.attachmentConfig?.maxSizeMB\">\r\n Max {{ config.attachmentConfig!.maxSizeMB }}MB\r\n </span>\r\n\r\n <!-- Hidden native file input -->\r\n <input #fileInput type=\"file\" hidden [attr.multiple]=\"config.attachmentConfig?.multiple ? true : null\"\r\n [attr.accept]=\"config.attachmentConfig?.accept || null\" (change)=\"onFileSelected($event)\">\r\n </div>\r\n\r\n <!-- Uploaded file list -->\r\n <div class=\"uploaded-list d-flex flex-column gap-8 mt-10\" *ngIf=\"value?.length\">\r\n <div *ngFor=\"let f of value; let i = index\"\r\n class=\"uploaded-item d-flex align-items-center gap-10 p-10px-14px br-8px\"\r\n [class.uploading]=\"f.isUploading\">\r\n\r\n <!-- Uploading spinner -->\r\n <ng-container *ngIf=\"f.isUploading; else fileReady\">\r\n <div class=\"upload-spinner rounded-50 b-2px-solid-E2E8F0\"></div>\r\n <div class=\"file-info flex-1 d-flex flex-column\">\r\n <span class=\"file-name font-weight-500 overflow-hidden fs-0-85rem\" [title]=\"f.name\">{{ f.name }}</span>\r\n <span class=\"file-size uploading-label fs-0-72rem\">Uploading...</span>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- Normal state once upload is done -->\r\n <ng-template #fileReady>\r\n <mat-icon class=\"file-type-icon\">{{ getFileIcon(f.type) }}</mat-icon>\r\n <img *ngIf=\"f.type?.startsWith('image') && f.dataUrl\" [src]=\"f.dataUrl\" class=\"file-thumb rounded-4\"\r\n alt=\"preview\">\r\n <div class=\"file-info flex-1 d-flex flex-column\">\r\n <span class=\"file-name font-weight-500 overflow-hidden fs-0-85rem\" [title]=\"f.name\">{{ f.name }}</span>\r\n <span class=\"file-size fs-0-72rem\">{{ formatFileSize(f.size) }}</span>\r\n </div>\r\n </ng-template>\r\n\r\n <!-- Compact icon-only remove button -->\r\n <button type=\"button\" class=\"file-remove-btn d-flex align-items-center justify-content-center rounded-50\"\r\n [disabled]=\"f.isUploading\" (click)=\"!f.isUploading && removeUploadedFile(i)\"\r\n aria-label=\"Remove file\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Validation / file errors -->\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"fileUploadError\">{{ fileUploadError }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage && !fileUploadError\">{{ errorMessage }}</span>\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\"\r\n *ngIf=\"config.hint && !errorMessage && !fileUploadError && !config.attachmentConfig?.acceptLabel\">\r\n {{ config.hint }}\r\n </span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isMediaUpload\" class=\"field-wrapper media-upload-wrapper d-flex flex-column gap-6 p-0 border-none b-none\"\r\n [formGroup]=\"formGroup\">\r\n\r\n <!-- Hidden file input lives outside *ngIf \u2014 triggered via ViewChild -->\r\n <input #mediaDeviceInput type=\"file\" hidden multiple accept=\"image/*\" (change)=\"onMediaFileSelected($event)\">\r\n\r\n <!-- Two-column layout -->\r\n <div class=\"mu-layout d-grid align-items-start\">\r\n\r\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 -->\r\n <div class=\"mu-left d-flex flex-column gap-20\">\r\n\r\n <!-- Header: title + max-items badge -->\r\n <div class=\"mu-header d-flex align-items-start flex-wrap gap-10\">\r\n <h3 class=\"mu-title m-0 c-111827 fs-1-35rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </h3>\r\n <span\r\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\"\r\n *ngIf=\"config.attachmentConfig?.maxFiles\">\r\n {{ controller.labels['LBL_MEDIA_MAX_PREFIX'] || 'Maximum' }}\r\n {{ config.attachmentConfig?.maxFiles }}\r\n {{ controller.labels['LBL_MEDIA_MAX_SUFFIX'] || 'Image & Videos' }}\r\n </span>\r\n </div>\r\n\r\n <!-- Description -->\r\n <p class=\"mu-description m-0 fs-0-875rem c-6B7280\" *ngIf=\"config.attachmentConfig?.description\">\r\n {{ config.attachmentConfig?.description }}\r\n </p>\r\n <p class=\"mu-description m-0 fs-0-875rem c-6B7280\"\r\n *ngIf=\"!config.attachmentConfig?.description && controller.labels['LBL_MEDIA_DESC']\">\r\n {{ controller.labels['LBL_MEDIA_DESC'] }}\r\n </p>\r\n\r\n <!-- Feature bullet list -->\r\n <ul class=\"mu-features m-0 p-0 d-flex flex-column gap-8 ls-none\"\r\n *ngIf=\"config.attachmentConfig?.features?.length || controller.labels['LBL_MEDIA_FEATURE_1']\">\r\n <ng-container *ngIf=\"config.attachmentConfig?.features?.length\">\r\n <li *ngFor=\"let f of config.attachmentConfig?.features\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ f }}\r\n </li>\r\n </ng-container>\r\n <ng-container *ngIf=\"!config.attachmentConfig?.features?.length\">\r\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_1']\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_1'] }}\r\n </li>\r\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_2']\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_2'] }}\r\n </li>\r\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_3']\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_3'] }}\r\n </li>\r\n </ng-container>\r\n </ul>\r\n\r\n <!-- Backdrop to close dropdown on outside click -->\r\n <div class=\"media-menu-backdrop\" *ngIf=\"showMediaMenu\"\r\n (click)=\"$event.stopPropagation(); showMediaMenu = false\"></div>\r\n\r\n <!-- Add Media button + dropdown -->\r\n <div class=\"media-add-container position-relative\" (click)=\"showMediaMenu = !showMediaMenu\">\r\n <lib-button id=\"btn-add-media-{{ config.name }}\" [variant]=\"'warning'\"\r\n [icon]=\"{type: 'material', value: 'add_photo_alternate'}\">\r\n {{ controller.labels['LBL_ADD_MEDIA'] || 'Add media' }}\r\n <mat-icon class=\"menu-chevron fs-18px\">add</mat-icon>\r\n </lib-button>\r\n\r\n <div class=\"media-dropdown position-absolute rounded-12 overflow-hidden b-ffffff b-1px-solid-E5E7EB\"\r\n *ngIf=\"showMediaMenu\" role=\"menu\" (click)=\"$event.stopPropagation()\">\r\n <!-- Video -->\r\n <button id=\"btn-media-video-{{ config.name }}\" type=\"button\"\r\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\"\r\n (click)=\"onMediaMenuVideo(); showMediaMenu = false\" role=\"menuitem\">\r\n <span\r\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>\r\n <span class=\"media-drop-text d-flex flex-column flex-1\">\r\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\r\n controller.labels['LBL_MEDIA_VIDEO'] || 'Video' }}</span>\r\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_VIDEO_DESC'] || 'Add\r\n YouTube URL'\r\n }}</span>\r\n </span>\r\n </button>\r\n <!-- Device -->\r\n <button id=\"btn-media-device-{{ config.name }}\" type=\"button\"\r\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\"\r\n (click)=\"onMediaMenuDevice(); showMediaMenu = false\" role=\"menuitem\">\r\n <span\r\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>\r\n <span class=\"media-drop-text d-flex flex-column flex-1\">\r\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\r\n controller.labels['LBL_MEDIA_DEVICE'] || 'Upload from device'\r\n }}</span>\r\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_DEVICE_DESC'] ||\r\n 'Select images from your\r\n computer' }}</span>\r\n </span>\r\n </button>\r\n <!-- Library -->\r\n <button id=\"btn-media-library-{{ config.name }}\" type=\"button\"\r\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\"\r\n (click)=\"onMediaMenuLibrary(); showMediaMenu = false\" role=\"menuitem\">\r\n <span\r\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>\r\n <span class=\"media-drop-text d-flex flex-column flex-1\">\r\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\r\n controller.labels['LBL_MEDIA_LIBRARY'] || 'Upload from library'\r\n }}</span>\r\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_LIBRARY_DESC'] ||\r\n 'Choose from default\r\n images' }}</span>\r\n </span>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- YouTube URL Input (inline below button) -->\r\n <div class=\"youtube-input-panel d-flex flex-column gap-8 p-16 rounded-10 b-FFFAF1 b-1px-solid-E5E7EB\"\r\n *ngIf=\"showYoutubeInput\">\r\n <label class=\"youtube-panel-label d-flex align-items-center gap-6 font-weight-600 fs-0-875rem c-111827\">\r\n {{ controller.labels['LBL_YOUTUBE_URL'] || 'Video URL' }}\r\n </label>\r\n <div class=\"youtube-input-row d-flex gap-8\">\r\n <input id=\"input-youtube-url-{{ config.name }}\" type=\"url\" class=\"field-input youtube-url-input\"\r\n [(ngModel)]=\"youtubeUrlInput\" [ngModelOptions]=\"{standalone: true}\"\r\n [placeholder]=\"controller.labels['PH_YOUTUBE_URL'] || 'Video URL'\" (keyup.enter)=\"addYoutubeMedia()\">\r\n <lib-button id=\"btn-add-youtube-{{ config.name }}\" [variant]=\"'secondary'\" (click)=\"addYoutubeMedia()\">\r\n {{ controller.labels['LBL_ADD'] || 'Add' }}\r\n </lib-button>\r\n </div>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"youtubeUrlError\">{{ youtubeUrlError }}</span>\r\n </div>\r\n\r\n <div\r\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\"\r\n *ngIf=\"mediaUploadError\">\r\n <mat-icon class=\"status-icon fs-18px\">error_outline</mat-icon>\r\n <span>{{ mediaUploadError }}</span>\r\n </div>\r\n\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n </div>\r\n <!-- end left panel -->\r\n\r\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 -->\r\n <div class=\"mu-right d-flex flex-column gap-12\">\r\n\r\n <!-- Carousel (when items exist) -->\r\n <div class=\"media-carousel-section d-flex flex-column gap-12\" *ngIf=\"mediaItems.length\">\r\n <div\r\n class=\"media-carousel-main position-relative w-100 overflow-hidden d-flex align-items-center justify-content-center br-12px b-0F172A\">\r\n <button id=\"btn-carousel-prev-{{ config.name }}\" type=\"button\"\r\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\"\r\n (click)=\"mediaCarouselPrev()\" [disabled]=\"mediaCarouselIndex === 0\" aria-label=\"Previous\">\r\n <mat-icon>chevron_left</mat-icon>\r\n </button>\r\n\r\n <div class=\"carousel-viewer position-absolute d-flex align-items-center justify-content-center\"\r\n *ngFor=\"let item of mediaItems; let i = index\" [hidden]=\"i !== mediaCarouselIndex\">\r\n <div *ngIf=\"item.isUploading\"\r\n class=\"carousel-uploading d-flex flex-column align-items-center gap-12 c-94A3B8 fs-0-85rem\">\r\n <div class=\"carousel-spinner rounded-50 b-3px-solid-rgba-255-255-255-0-15\"></div>\r\n <span>{{ controller.labels['LBL_UPLOADING'] || 'Uploading\u2026' }}</span>\r\n </div>\r\n <ng-container *ngIf=\"!item.isUploading && item.mediaType === 'youtube'\">\r\n <iframe class=\"carousel-iframe w-100 h-100 br-12px\" [src]=\"item.url | trustedUrl\" frameborder=\"0\"\r\n allowfullscreen\r\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\">\r\n </iframe>\r\n </ng-container>\r\n <ng-container *ngIf=\"!item.isUploading && item.mediaType === 'image'\">\r\n <img class=\"carousel-image w-100 h-100 br-12px\" [src]=\"item.url\" alt=\"Media\">\r\n </ng-container>\r\n <button id=\"btn-remove-media-{{ config.name }}-{{ i }}\" type=\"button\"\r\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\"\r\n [disabled]=\"item.isUploading\" (click)=\"removeMediaItem(i)\" aria-label=\"Remove\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <button id=\"btn-carousel-next-{{ config.name }}\" type=\"button\"\r\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\"\r\n (click)=\"mediaCarouselNext()\" [disabled]=\"mediaCarouselIndex === mediaItems.length - 1\" aria-label=\"Next\">\r\n <mat-icon>chevron_right</mat-icon>\r\n </button>\r\n\r\n <div class=\"carousel-dots position-absolute d-flex gap-6\">\r\n <span *ngFor=\"let item of mediaItems; let i = index\"\r\n class=\"carousel-dot rounded-50 cursor-pointer b-rgba-255-255-255-0-45\"\r\n [class.active]=\"i === mediaCarouselIndex\" (click)=\"mediaGoTo(i)\"></span>\r\n </div>\r\n </div>\r\n\r\n <!-- Thumbnail strip -->\r\n <div class=\"media-thumbnail-strip d-flex flex-wrap gap-8 pb-4px\">\r\n <div *ngFor=\"let item of mediaThumbnails; let i = index\"\r\n class=\"media-thumb rounded-8 overflow-hidden cursor-pointer d-flex align-items-center justify-content-center b-2px-solid-transparent b-E2E8F0\"\r\n [class.active]=\"i === mediaCarouselIndex\" (click)=\"mediaGoTo(i)\">\r\n <div *ngIf=\"item.isUploading\"\r\n class=\"thumb-uploading d-flex align-items-center justify-content-center w-100 h-100\">\r\n <div class=\"thumb-spinner rounded-50 b-2px-solid-E2E8F0\"></div>\r\n </div>\r\n <img *ngIf=\"!item.isUploading && item.mediaType === 'youtube' && item.thumbnailUrl\"\r\n [src]=\"item.thumbnailUrl\" class=\"thumb-img w-100 h-100\" alt=\"Video thumbnail\">\r\n <div *ngIf=\"!item.isUploading && item.mediaType === 'youtube' && !item.thumbnailUrl\"\r\n class=\"thumb-yt-placeholder d-flex align-items-center justify-content-center w-100 h-100 b-1E293B c-EF4444\">\r\n <mat-icon>play_circle</mat-icon>\r\n </div>\r\n <img *ngIf=\"!item.isUploading && item.mediaType === 'image' && item.url\" [src]=\"item.url\"\r\n class=\"thumb-img w-100 h-100\" alt=\"Image thumbnail\">\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Empty right-side placeholder -->\r\n <div\r\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\"\r\n *ngIf=\"!mediaItems.length\" (click)=\"onMediaMenuDevice()\">\r\n <mat-icon class=\"mu-right-empty-icon fs-52px\">perm_media</mat-icon>\r\n <p>{{ controller.labels['LBL_ADD_MEDIA'] || 'Add media' }}</p>\r\n </div>\r\n\r\n </div>\r\n <!-- end right panel -->\r\n\r\n </div><!-- end mu-layout -->\r\n </div>\r\n\r\n\r\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 -->\r\n <!-- Wrapper is always in DOM (hidden) so @ViewChild can move it to body -->\r\n <div #libraryModal class=\"media-library-portal-host\" [class.is-open]=\"showLibraryModal\">\r\n\r\n <!-- Backdrop -->\r\n <div class=\"media-library-overlay\" (click)=\"closeLibraryModal()\"></div>\r\n\r\n <!-- Modal card -->\r\n <div class=\"media-library-modal d-flex flex-column overflow-hidden b-ffffff br-16px\"\r\n role=\"dialog\" aria-modal=\"true\">\r\n <div class=\"library-modal-header d-flex align-items-start justify-content-between p-24px-28px bb-1px-solid-E5E7EB\">\r\n <div class=\"header-left d-flex flex-column gap-8\">\r\n <h3 class=\"library-modal-title m-0 color-dark fs-1-25rem\">\r\n {{ controller.labels['LBL_ADD_IMAGES'] || 'Add Images' }}\r\n </h3>\r\n <p class=\"library-modal-subtitle m-0 color-gray fs-0-85rem\">\r\n {{ controller.labels['LBL_LIBRARY_MODAL_DESC'] || 'Select images from your library.' }}\r\n </p>\r\n </div>\r\n <button id=\"btn-close-library-{{ config.name }}\" type=\"button\"\r\n class=\"library-close-btn d-flex align-items-center justify-content-center cursor-pointer rounded-50 border-none b-none c-9CA3AF\"\r\n (click)=\"closeLibraryModal()\" aria-label=\"Close\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <!-- Loading -->\r\n <div class=\"library-loading\" *ngIf=\"libraryLoading\">\r\n <div class=\"lib-spinner rounded-50 b-3px-solid-E2E8F0\"></div>\r\n <span>{{ controller.labels['LBL_LOADING'] || 'Loading\u2026' }}</span>\r\n </div>\r\n\r\n <!-- Error -->\r\n <div class=\"library-error d-flex align-items-center gap-8 color-error b-FEF2F2 fs-0-875rem p-16px-24px\"\r\n *ngIf=\"libraryError && !libraryLoading\">\r\n <mat-icon>error_outline</mat-icon>\r\n {{ libraryError }}\r\n </div>\r\n\r\n <!-- Image grid -->\r\n <div class=\"library-grid d-grid gap-16 flex-1 p-28px b-F9FAFB\" *ngIf=\"!libraryLoading && libraryImages.length\">\r\n <div *ngFor=\"let img of libraryImages; let li = index\" id=\"lib-img-{{ config.name }}-{{ li }}\"\r\n class=\"library-grid-item position-relative rounded-12 overflow-hidden cursor-pointer bg-white b-3px-solid-transparent\"\r\n [class.selected]=\"isLibraryItemSelected(img)\" (click)=\"toggleLibraryItem(img)\">\r\n <img [src]=\"getLibraryItemUrl(img)\" class=\"library-grid-img w-100 h-100 d-block\" alt=\"Library image\">\r\n <div\r\n class=\"library-check position-absolute bg-white rounded-50 d-flex align-items-center justify-content-center c-3B82F6\"\r\n *ngIf=\"isLibraryItemSelected(img)\">\r\n <mat-icon>check_circle</mat-icon>\r\n </div>\r\n <div class=\"library-overlay-hover position-absolute b-rgba-59-130-246-0-12\"></div>\r\n </div>\r\n </div>\r\n\r\n <!-- Empty library -->\r\n <div\r\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\"\r\n *ngIf=\"!libraryLoading && !libraryError && libraryImages.length === 0\">\r\n <mat-icon>image_not_supported</mat-icon>\r\n <span>{{ controller.labels['LBL_LIBRARY_EMPTY'] || 'No images found in library.' }}</span>\r\n </div>\r\n\r\n <!-- Footer -->\r\n <div class=\"library-modal-footer d-flex align-items-center justify-content-end bg-white p-20px-28px bt-1px-solid-E5E7EB\">\r\n <div class=\"library-footer-actions d-flex gap-12\">\r\n <lib-button id=\"btn-library-cancel-{{ config.name }}\" [variant]=\"'outline'\" (click)=\"closeLibraryModal()\">\r\n {{ controller.labels['LBL_CANCEL'] || 'Cancel' }}\r\n </lib-button>\r\n <lib-button id=\"btn-library-confirm-{{ config.name }}\" [variant]=\"'primary'\"\r\n [disabled]=\"librarySelectedIds.size === 0\" (click)=\"confirmLibrarySelection()\">\r\n {{ controller.labels['LBL_CONTINUE'] || 'Continue' }}\r\n </lib-button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n\r\n\r\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 -->\r\n <div *ngIf=\"isLocation\" class=\"field-wrapper location-field-wrapper d-flex flex-column gap-6 gap-12\"\r\n [formGroup]=\"formGroup\">\r\n\r\n <!-- Field label -->\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n <p class=\"location-subtitle m-0 c-6B7280 fs-0-8125rem\" *ngIf=\"config.hint\">{{ config.hint }}</p>\r\n\r\n <!-- Three-tab bar -->\r\n <div class=\"location-tabs d-flex gap-12 mb-24\">\r\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'VENUE' ? 'warning' : 'outline'\"\r\n (click)=\"onLocationTabChange('VENUE')\">\r\n {{ controller.labels['LBL_LOC_VENUE'] || 'Venue' }}\r\n </lib-button>\r\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'ONLINE' ? 'warning' : 'outline'\"\r\n (click)=\"onLocationTabChange('ONLINE')\">\r\n {{ controller.labels['LBL_LOC_ONLINE'] || 'Online Event' }}\r\n </lib-button>\r\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'TBA' ? 'warning' : 'outline'\"\r\n (click)=\"onLocationTabChange('TBA')\">\r\n {{ controller.labels['LBL_LOC_TBA'] || 'To be Announced' }}\r\n </lib-button>\r\n </div>\r\n\r\n <!-- VENUE TAB -->\r\n <div *ngIf=\"locationActiveTab === 'VENUE'\" class=\"loc-panel loc-venue-panel d-flex flex-column gap-12\">\r\n\r\n <p class=\"loc-section-label m-0 font-weight-600 c-111827 fs-0-9rem\">\r\n {{ controller.labels['LBL_LOC_ADDRESS'] || 'Location address' }}\r\n </p>\r\n\r\n <!-- Added venue rows -->\r\n <div class=\"loc-venue-list d-flex flex-column gap-8\" *ngIf=\"locationVenues.length > 0\">\r\n <div *ngFor=\"let venue of locationVenues; let i = index\"\r\n class=\"loc-venue-item d-flex align-items-center gap-10 p-10px-14px br-7px b-ffffff b-1px-solid-D1D5DB\">\r\n <mat-icon class=\"loc-venue-search-icon fs-18px c-9CA3AF\">search</mat-icon>\r\n <span class=\"loc-venue-text flex-1 overflow-hidden fs-0-875rem c-111827\">{{ venue.address || venue.name ||\r\n venue.description }}</span>\r\n <button type=\"button\"\r\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\"\r\n (click)=\"removeLocationVenue(i)\">\r\n <mat-icon>delete_outline</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Location count badge -->\r\n <p class=\"loc-count-text m-0 font-weight-600 fs-0-8125rem c-3B82F6\"\r\n *ngIf=\"locationVenues.length > 0 && config.locationConfig?.allowMulti\">\r\n {{ locationVenues.length }}&nbsp;{{ controller.labels['LBL_LOC_COUNT_SUFFIX'] || 'Locations Added!' }}\r\n </p>\r\n\r\n <!-- Search input (hide when max reached) -->\r\n <div class=\"loc-search-container position-relative\" *ngIf=\"!locationMaxReached\">\r\n <div class=\"loc-search-wrapper position-relative d-flex align-items-center mt-4\">\r\n <mat-icon class=\"loc-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">search</mat-icon>\r\n <input\r\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\"\r\n [placeholder]=\"config.locationConfig?.venuePlaceholder || (controller.labels['PH_LOC_VENUE'] || 'Type to search venue...')\"\r\n [value]=\"locationSearchText\" (input)=\"handleLocationSearchInput($event)\" (blur)=\"hideLocationSuggestions()\"\r\n autocomplete=\"off\" [class.is-invalid]=\"errorMessage\">\r\n </div>\r\n <!-- Suggestions dropdown -->\r\n <div class=\"loc-suggestions-panel position-absolute overflow-hidden br-8px b-ffffff b-1px-solid-D1D5DB\"\r\n *ngIf=\"locationShowSuggestions && locationSuggestions.length\">\r\n <div *ngFor=\"let sug of locationSuggestions\"\r\n class=\"loc-suggestion-item d-flex align-items-center gap-10 cursor-pointer p-10px-14px\"\r\n (mousedown)=\"onLocationSuggestionSelect(sug)\">\r\n <mat-icon class=\"loc-suggestion-icon fs-18px c-E53E3E\">place</mat-icon>\r\n <span class=\"loc-suggestion-text overflow-hidden fs-0-875rem c-374151\">{{ sug.description }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Add another button -->\r\n <button type=\"button\"\r\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\"\r\n *ngIf=\"locationVenues.length > 0 && !locationMaxReached && config.locationConfig?.allowMulti\">\r\n <mat-icon>add_circle_outline</mat-icon>\r\n <span>{{ controller.labels['LBL_LOC_ADD_ANOTHER'] || 'Add another Location' }}</span>\r\n </button>\r\n\r\n <!-- Map -->\r\n <div class=\"loc-map-container overflow-hidden br-10px b-1px-solid-E5E7EB\"\r\n *ngIf=\"config.locationConfig?.showMap !== false\">\r\n <ng-container *ngIf=\"config.locationConfig?.googleMapsApiKey; else simpleEmbed\">\r\n <div [id]=\"'loc-map-' + config.name\" class=\"loc-map-frame w-100 d-block border-none\"\r\n [style.height]=\"config.locationConfig?.mapHeight || '300px'\"></div>\r\n </ng-container>\r\n <ng-template #simpleEmbed>\r\n <iframe class=\"loc-map-frame w-100 d-block border-none\"\r\n [style.height]=\"config.locationConfig?.mapHeight || '300px'\" [src]=\"getLocationMapEmbedUrl() | trustedUrl\"\r\n frameborder=\"0\" allowfullscreen loading=\"lazy\">\r\n </iframe>\r\n </ng-template>\r\n </div>\r\n\r\n <!-- Map hint -->\r\n <p class=\"loc-map-hint m-0 text-center fs-0-78rem c-6B7280\">\r\n {{ controller.labels['LBL_LOC_MAP_HINT'] || 'Type the venue address. A map will appear to assist you.' }}\r\n </p>\r\n </div>\r\n\r\n <!-- ONLINE TAB -->\r\n <div *ngIf=\"locationActiveTab === 'ONLINE'\" class=\"loc-panel loc-online-panel d-flex flex-column gap-12\">\r\n <p class=\"loc-section-label m-0 font-weight-600 c-111827 fs-0-9rem\">\r\n {{ controller.labels['LBL_LOC_ONLINE_URL'] || 'Event URL' }}\r\n </p>\r\n <div class=\"loc-search-wrapper position-relative d-flex align-items-center mt-4\">\r\n <mat-icon class=\"loc-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">link</mat-icon>\r\n <input\r\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\"\r\n type=\"url\"\r\n [placeholder]=\"config.locationConfig?.onlinePlaceholder || (controller.labels['PH_LOC_ONLINE'] || 'https://zoom.us/j/...')\"\r\n [value]=\"locationOnlineUrl\" (input)=\"onLocationUrlChange($any($event.target).value)\"\r\n [class.is-invalid]=\"errorMessage\">\r\n </div>\r\n </div>\r\n\r\n <!-- TBA TAB -->\r\n <div *ngIf=\"locationActiveTab === 'TBA'\"\r\n class=\"loc-panel loc-tba-panel d-flex flex-column gap-12 justify-content-center\">\r\n <div\r\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\">\r\n <mat-icon class=\"loc-tba-icon fs-40px c-9CA3AF\">schedule</mat-icon>\r\n <p class=\"loc-tba-text m-0 c-6B7280 fs-0-9rem\">\r\n {{ controller.labels['LBL_LOC_TBA_DESC'] || \"This event's location is yet to be announced. Check back later\r\n for updates.\" }}\r\n </p>\r\n </div>\r\n </div>\r\n\r\n <!-- Hidden real form control -->\r\n <input type=\"hidden\" [formControlName]=\"config.name!\">\r\n\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Link List 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 -->\r\n <div *ngIf=\"isLinkList\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ controller.labels[config.label] || config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <!-- Top Links list (if listPosition === 'top') -->\r\n <div *ngIf=\"linkListConfig?.listPosition === 'top' && linkListItems.length > 0\" class=\"link-list-container mb-8px\">\r\n <ul class=\"link-items-list p-0 m-0\" \r\n [style.display]=\"'grid'\"\r\n [style.gridTemplateColumns]=\"'repeat(12, 1fr)'\"\r\n [style.gap]=\"'12px 16px'\">\r\n <li *ngFor=\"let item of linkListItems; let idx = index\" \r\n class=\"link-list-item\"\r\n [style.gridColumn]=\"'span ' + (linkListConfig?.colSpan || 12)\">\r\n <!-- Normal display mode -->\r\n <ng-container *ngIf=\"editingLinkIndex !== idx\">\r\n <span class=\"link-url-text text-truncate font-weight-500 color-333\">{{ getLinkDisplayUrl(item) }}</span>\r\n <div class=\"link-item-actions d-flex align-items-center gap-12px\">\r\n <button type=\"button\" class=\"action-btn edit-btn\" \r\n *ngIf=\"linkListConfig?.editable !== false\" (click)=\"startEditLink(idx)\" [title]=\"controller.labels['COMMON.ACTIONS.EDIT'] || 'Edit'\">\r\n <mat-icon>edit</mat-icon>\r\n </button>\r\n <button type=\"button\" class=\"action-btn delete-btn\" \r\n *ngIf=\"linkListConfig?.deleteable !== false\" (click)=\"deleteLinkItem(idx)\" [title]=\"controller.labels['COMMON.ACTIONS.DELETE'] || 'Delete'\">\r\n <mat-icon>delete_outline</mat-icon>\r\n </button>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- Inline Edit mode -->\r\n <ng-container *ngIf=\"editingLinkIndex === idx\">\r\n <div class=\"edit-input-wrapper d-flex align-items-center gap-8px w-100\">\r\n <input type=\"text\" class=\"field-input flex-1 py-4px\" [(ngModel)]=\"editingLinkValue\" \r\n [ngModelOptions]=\"{standalone: true}\" (keydown.enter)=\"saveEditLink(idx)\" (keydown.escape)=\"cancelEditLink()\"\r\n (input)=\"linkValidationError = ''\">\r\n <button type=\"button\" class=\"action-btn save-btn\" (click)=\"saveEditLink(idx)\" [title]=\"controller.labels['COMMON.ACTIONS.SAVE'] || 'Save'\">\r\n <mat-icon>check</mat-icon>\r\n </button>\r\n <button type=\"button\" class=\"action-btn cancel-btn\" (click)=\"cancelEditLink()\" [title]=\"controller.labels['COMMON.ACTIONS.CANCEL'] || 'Cancel'\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n </ng-container>\r\n </li>\r\n </ul>\r\n </div>\r\n\r\n <!-- Input text + plus icon -->\r\n <div class=\"input-group position-relative d-flex w-100\">\r\n <input type=\"text\" class=\"field-input pe-40px\" [placeholder]=\"controller.labels[config.placeholder!] || config.placeholder || controller.labels['COMMON.ENTER_LINK'] || 'Enter link...'\"\r\n [(ngModel)]=\"newLinkInputValue\" [ngModelOptions]=\"{standalone: true}\" (keydown.enter)=\"addLinkItem($event)\"\r\n [disabled]=\"config.readonly || config.disabled\"\r\n (input)=\"linkValidationError = ''\">\r\n \r\n <button type=\"button\" class=\"position-absolute right-8px top-50-percent translate-y-minus-50 d-flex align-items-center justify-content-center b-none bg-none p-0 cursor-pointer color-primary\"\r\n [disabled]=\"config.readonly || config.disabled || !newLinkInputValue?.trim()\"\r\n (click)=\"addLinkItem()\" style=\"right: 8px; top: 50%; transform: translateY(-50%); z-index: 10;\">\r\n <mat-icon>add</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <!-- Validation error message -->\r\n <span class=\"field-error color-error fs-0-75rem mt-4px d-block\" *ngIf=\"linkValidationError\">{{ linkValidationError }}</span>\r\n\r\n <!-- Bottom Links list (if listPosition !== 'top') -->\r\n <div *ngIf=\"linkListConfig?.listPosition !== 'top' && linkListItems.length > 0\" class=\"link-list-container mt-8px\">\r\n <ul class=\"link-items-list p-0 m-0\" \r\n [style.display]=\"'grid'\"\r\n [style.gridTemplateColumns]=\"'repeat(12, 1fr)'\"\r\n [style.gap]=\"'12px 16px'\">\r\n <li *ngFor=\"let item of linkListItems; let idx = index\" \r\n class=\"link-list-item\"\r\n [style.gridColumn]=\"'span ' + (linkListConfig?.colSpan || 12)\">\r\n <!-- Normal display mode -->\r\n <ng-container *ngIf=\"editingLinkIndex !== idx\">\r\n <span class=\"link-url-text text-truncate font-weight-500 color-333\">{{ getLinkDisplayUrl(item) }}</span>\r\n <div class=\"link-item-actions d-flex align-items-center gap-12px\">\r\n <button type=\"button\" class=\"action-btn edit-btn\" \r\n *ngIf=\"linkListConfig?.editable !== false\" (click)=\"startEditLink(idx)\" [title]=\"controller.labels['COMMON.ACTIONS.EDIT'] || 'Edit'\">\r\n <mat-icon>edit</mat-icon>\r\n </button>\r\n <button type=\"button\" class=\"action-btn delete-btn\" \r\n *ngIf=\"linkListConfig?.deleteable !== false\" (click)=\"deleteLinkItem(idx)\" [title]=\"controller.labels['COMMON.ACTIONS.DELETE'] || 'Delete'\">\r\n <mat-icon>delete_outline</mat-icon>\r\n </button>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- Inline Edit mode -->\r\n <ng-container *ngIf=\"editingLinkIndex === idx\">\r\n <div class=\"edit-input-wrapper d-flex align-items-center gap-8px w-100\">\r\n <input type=\"text\" class=\"field-input flex-1 py-4px\" [(ngModel)]=\"editingLinkValue\" \r\n [ngModelOptions]=\"{standalone: true}\" (keydown.enter)=\"saveEditLink(idx)\" (keydown.escape)=\"cancelEditLink()\"\r\n (input)=\"linkValidationError = ''\">\r\n <button type=\"button\" class=\"action-btn save-btn\" (click)=\"saveEditLink(idx)\" [title]=\"controller.labels['COMMON.ACTIONS.SAVE'] || 'Save'\">\r\n <mat-icon>check</mat-icon>\r\n </button>\r\n <button type=\"button\" class=\"action-btn cancel-btn\" (click)=\"cancelEditLink()\" [title]=\"controller.labels['COMMON.ACTIONS.CANCEL'] || 'Cancel'\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n </ng-container>\r\n </li>\r\n </ul>\r\n </div>\r\n\r\n <!-- Hidden real form control -->\r\n <input type=\"hidden\" [formControlName]=\"config.name!\">\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ controller.labels[config.hint] || config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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-8px{margin-bottom:8px!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}.generated-value{word-break:break-all}: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:.8rem!important;font-weight:600!important;line-height:1.2;letter-spacing:.04em;text-transform:uppercase!important;color:#202124!important;margin-bottom:var(--cc-sf-label-mb, 0px)!important}.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}.dropdown-search-wrapper{display:flex;align-items:center;gap:8px;padding:8px 12px;border-bottom:1px solid var(--cc-sf-input-border, #E5E7EB);background:var(--cc-sf-input-bg, #ffffff)}.dropdown-search-wrapper .dropdown-search-icon{font-size:18px;width:18px;height:18px;color:var(--cc-sf-input-placeholder, #9CA3AF);flex-shrink:0}.dropdown-search-wrapper .dropdown-search-input{flex:1;border:none;outline:none;background:transparent;font-size:.875rem;color:var(--cc-sf-label-color, #111827);font-family:inherit}.dropdown-search-wrapper .dropdown-search-input::placeholder{color:var(--cc-sf-input-placeholder, #C4C9D4)}.dropdown-search-wrapper .dropdown-search-clear{font-size:18px;width:18px;height:18px;color:var(--cc-sf-input-placeholder, #9CA3AF);flex-shrink:0;transition:color .15s}.dropdown-search-wrapper .dropdown-search-clear:hover{color:var(--cc-sf-label-color, #111827)}.dropdown-pagination-footer{padding:8px 12px;border-top:1px solid var(--cc-sf-input-border, #E5E7EB);background:var(--cc-sf-input-bg, #ffffff);display:flex;justify-content:center;align-items:center}.dropdown-pagination-footer .btn-dropdown-load-more{width:100%;padding:6px 12px;font-size:.8125rem;font-weight:500;color:var(--cc-sf-accent-color, #6366F1);background:transparent;border:1px dashed var(--cc-sf-accent-color, #6366F1);border-radius:var(--cc-sf-input-radius, 6px);cursor:pointer;transition:all .2s ease}.dropdown-pagination-footer .btn-dropdown-load-more:hover{background:#6366f114}.dropdown-pagination-footer .dropdown-loading-more-state{display:flex;align-items:center;justify-content:center;gap:8px;padding:4px 0}.dropdown-pagination-footer .dropdown-loading-more-state .dropdown-spinner-small{width:14px;height:14px;border:2px solid var(--cc-sf-input-border, #E5E7EB);border-top-color:var(--cc-sf-accent-color, #6366F1);border-radius:50%;animation:spin .8s linear infinite}.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:var(--cc-sf-label-color, #6B7280);flex-shrink:0}.multi-select-wrapper .multi-select-panel{position:relative;width:100%;max-width:100vw;box-sizing:border-box;background:var(--cc-sf-input-bg, #ffffff);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;display:flex;flex-direction:column;overflow:hidden;max-height:320px}.multi-select-wrapper .multi-select-options-list{flex:1;overflow-y:auto;max-height:240px}.multi-select-wrapper .multi-select-options-list::-webkit-scrollbar{width:4px}.multi-select-wrapper .multi-select-options-list::-webkit-scrollbar-thumb{background:var(--cc-sf-input-border, #D1D5DB);border-radius:2px}.multi-select-wrapper .multi-select-option{padding:8px 12px;border-bottom:1px solid var(--cc-sf-input-border, #F3F4F6);transition:background .1s}.multi-select-wrapper .multi-select-option:last-of-type{border-bottom:none}.multi-select-wrapper .multi-select-option:hover{background:var(--cc-sf-input-hover-bg, #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-option.multi-select-all{border-bottom:1px solid var(--cc-sf-input-border, #E5E7EB);background:var(--cc-sf-input-bg, #FAFBFC)}.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}.single-select-wrapper{position:relative}.single-select-wrapper .single-select-trigger{min-height:var(--cc-sf-input-height, 44px);height:auto;-webkit-user-select:none;user-select:none}.single-select-wrapper .single-select-trigger.ss-open{border-color:var(--cc-sf-input-focus-border, #6366F1)!important;box-shadow:0 0 0 3px #6366f124,0 1px 4px #6366f11a!important}.single-select-wrapper .single-select-trigger .single-select-value{flex:1;color:var(--cc-sf-label-color, #111827);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.single-select-wrapper .single-select-trigger .single-select-placeholder{flex:1;font-weight:400;font-size:14px;color:var(--cc-sf-input-placeholder, #C4C9D4)}.single-select-wrapper .single-select-trigger .single-select-arrow{font-size:20px;width:20px;height:20px;line-height:20px;color:var(--cc-sf-label-color, #6B7280);flex-shrink:0}.single-select-wrapper .single-select-panel{position:relative;width:100%;max-width:100vw;box-sizing:border-box;background:var(--cc-sf-input-bg, #ffffff);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;display:flex;flex-direction:column;overflow:hidden;max-height:320px}.single-select-wrapper .single-select-options-list{flex:1;overflow-y:auto;max-height:240px}.single-select-wrapper .single-select-options-list::-webkit-scrollbar{width:4px}.single-select-wrapper .single-select-options-list::-webkit-scrollbar-thumb{background:var(--cc-sf-input-border, #D1D5DB);border-radius:2px}.single-select-wrapper .single-select-option{padding:8px 12px;border-bottom:1px solid var(--cc-sf-input-border, #F3F4F6);transition:background .1s;justify-content:space-between}.single-select-wrapper .single-select-option:last-of-type{border-bottom:none}.single-select-wrapper .single-select-option:hover{background:var(--cc-sf-input-hover-bg, #F9FAFB)}.single-select-wrapper .single-select-option.selected{background:var(--cc-sf-input-hover-bg, #F0F5FF);color:var(--cc-sf-selected-color, #6366F1)}.single-select-wrapper .single-select-option .single-select-check{font-size:18px;width:18px;height:18px;color:var(--cc-sf-selected-color, #6366F1)}.single-select-wrapper.is-invalid .single-select-trigger{border-color:var(--cc-sf-error-border, #EF4444)!important;background-color:var(--cc-sf-error-bg, #FFF5F5)!important}.multi-select-panel,.single-select-panel{width:100%;background:var(--cc-sf-input-bg, #ffffff);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;display:flex;flex-direction:column;overflow:hidden;max-height:320px}.multi-select-options-list,.single-select-options-list{flex:1;overflow-y:auto;max-height:240px}.multi-select-options-list::-webkit-scrollbar,.single-select-options-list::-webkit-scrollbar{width:4px}.multi-select-options-list::-webkit-scrollbar-thumb,.single-select-options-list::-webkit-scrollbar-thumb{background:var(--cc-sf-input-border, #D1D5DB);border-radius:2px}.multi-select-option,.single-select-option{display:flex;align-items:center;gap:8px;cursor:pointer;padding:8px 12px;border-bottom:1px solid var(--cc-sf-input-border, #F3F4F6);transition:background .1s}.multi-select-option:last-of-type,.single-select-option:last-of-type{border-bottom:none}.multi-select-option:hover,.single-select-option:hover{background:var(--cc-sf-input-hover-bg, #F9FAFB)}.multi-select-option input[type=checkbox],.single-select-option input[type=checkbox]{flex-shrink:0;cursor:pointer;accent-color:var(--cc-sf-selected-color, #6366F1);width:15px;height:15px}.multi-select-option.multi-select-all{border-bottom:1px solid var(--cc-sf-input-border, #E5E7EB);background:var(--cc-sf-input-bg, #FAFBFC)}.single-select-option{justify-content:space-between}.single-select-option.selected{background:var(--cc-sf-input-hover-bg, #F0F5FF);color:var(--cc-sf-selected-color, #6366F1)}.single-select-option .single-select-check{font-size:18px;width:18px;height:18px;color:var(--cc-sf-selected-color, #6366F1)}.multi-select-empty{padding:12px;text-align:center}::ng-deep .cdk-overlay-container{z-index:1100}.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)}.suffix-action-icons{right:.875rem;top:50%;transform:translateY(-50%);z-index:1;pointer-events:auto}.suffix-action-icons mat-icon.suffix-action-icon{font-size:1.125rem;width:1.125rem;height:1.125rem;line-height:1.125rem;color:var(--cc-sf-hint-color, #6B7280);cursor:pointer;transition:color .2s ease,transform .15s ease}.suffix-action-icons mat-icon.suffix-action-icon:hover{color:var(--cc-sf-text-color, #202124);transform:scale(1.15)}.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}.link-list-container{border:none!important;background-color:transparent!important;padding:0!important;box-shadow:none!important;overflow:visible!important}.link-items-list{list-style:none}@media(max-width:640px){.link-items-list{grid-template-columns:1fr!important}.link-items-list .link-list-item{grid-column:span 12!important}}.link-list-item{display:flex;align-items:center;justify-content:space-between;padding:10px 16px;background-color:var(--cc-sf-link-item-bg, #f9fafb)!important;border:1px solid var(--cc-sf-link-item-border, #E5E7EB)!important;border-radius:var(--cc-sf-input-radius, 9px)!important;gap:12px;min-height:48px}.link-list-item .link-url-text{font-size:.875rem;color:var(--cc-sf-link-item-text, #111827);word-break:break-all}.link-list-item .action-btn{border:none;background:transparent;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;padding:0;border-radius:6px;transition:background .15s ease,color .15s ease}.link-list-item .action-btn mat-icon{font-size:22px;width:22px;height:22px;line-height:22px;display:flex;align-items:center;justify-content:center}.link-list-item .action-btn.edit-btn{color:var(--cc-sf-link-edit-color, #4b5563)}.link-list-item .action-btn.edit-btn:hover{color:var(--cc-sf-accent-color, #6366F1);background-color:#f3f4f6}.link-list-item .action-btn.delete-btn{color:var(--cc-sf-link-delete-color, #ef4444)}.link-list-item .action-btn.delete-btn:hover{background-color:var(--cc-sf-error-bg, #FFF5F5)}.link-list-item .action-btn.save-btn{color:var(--cc-sf-link-save-color, #16a34a)!important}.link-list-item .action-btn.save-btn:hover{background-color:#ecfdf5}.link-list-item .action-btn.cancel-btn{color:#ef4444!important}.link-list-item .action-btn.cancel-btn:hover{background-color:var(--cc-sf-error-bg, #FFF5F5)}.pe-40px{padding-right:40px!important}.dropdown-loading-state{display:flex;align-items:center;justify-content:center;padding:16px;gap:8px}.dropdown-spinner{width:20px;height:20px;border:2px solid #E2E8F0;border-top-color:var(--cc-sf-accent-color, #3B82F6);border-radius:50%;animation:cc-spin .7s linear infinite}.mini-spinner{width:16px;height:16px;border:2px solid #E2E8F0;border-top-color:var(--cc-sf-accent-color, #3B82F6);border-radius:50%;animation:cc-spin .7s linear infinite}.native-select-wrapper{display:block;width:100%;position:relative}.native-select-wrapper select{width:100%}.native-select-wrapper .native-select-spinner{position:absolute;right:2.5rem;top:calc(50% - 7px);width:14px;height:14px;border:2px solid #CBD5E1;border-top-color:var(--cc-sf-accent-color, #3B82F6);border-radius:50%;animation:cc-spin .7s linear infinite;pointer-events:none}\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$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.NumberValueAccessor, selector: "input[type=number][formControlName],input[type=number][formControl],input[type=number][ngModel]" }, { kind: "directive", type: i1$2.CheckboxControlValueAccessor, selector: "input[type=checkbox][formControlName],input[type=checkbox][formControl],input[type=checkbox][ngModel]" }, { kind: "directive", type: i1$2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$2.RadioControlValueAccessor, selector: "input[type=radio][formControlName],input[type=radio][formControl],input[type=radio][ngModel]", inputs: ["name", "formControlName", "value"] }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1$2.MinValidator, selector: "input[type=number][min][formControlName],input[type=number][min][formControl],input[type=number][min][ngModel]", inputs: ["min"] }, { kind: "directive", type: i1$2.MaxValidator, selector: "input[type=number][max][formControlName],input[type=number][max][formControl],input[type=number][max][ngModel]", inputs: ["max"] }, { kind: "directive", type: i1$2.FormControlDirective, selector: "[formControl]", inputs: ["formControl", "disabled", "ngModel"], outputs: ["ngModelChange"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1$2.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i6.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "component", type: i6.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: i10.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: i10.MatAutocompleteTrigger, selector: "input[matAutocomplete], textarea[matAutocomplete]", inputs: ["matAutocomplete", "matAutocompletePosition", "matAutocompleteConnectedTo", "autocomplete", "matAutocompleteDisabled"], exportAs: ["matAutocompleteTrigger"] }, { kind: "directive", type: i4.CdkConnectedOverlay, selector: "[cdk-connected-overlay], [connected-overlay], [cdkConnectedOverlay]", inputs: ["cdkConnectedOverlayOrigin", "cdkConnectedOverlayPositions", "cdkConnectedOverlayPositionStrategy", "cdkConnectedOverlayOffsetX", "cdkConnectedOverlayOffsetY", "cdkConnectedOverlayWidth", "cdkConnectedOverlayHeight", "cdkConnectedOverlayMinWidth", "cdkConnectedOverlayMinHeight", "cdkConnectedOverlayBackdropClass", "cdkConnectedOverlayPanelClass", "cdkConnectedOverlayViewportMargin", "cdkConnectedOverlayScrollStrategy", "cdkConnectedOverlayOpen", "cdkConnectedOverlayDisableClose", "cdkConnectedOverlayTransformOriginOn", "cdkConnectedOverlayHasBackdrop", "cdkConnectedOverlayLockPosition", "cdkConnectedOverlayFlexibleDimensions", "cdkConnectedOverlayGrowAfterOpen", "cdkConnectedOverlayPush", "cdkConnectedOverlayDisposeOnNavigation"], outputs: ["backdropClick", "positionChange", "attach", "detach", "overlayKeydown", "overlayOutsideClick"], exportAs: ["cdkConnectedOverlay"] }, { kind: "directive", type: i4.CdkOverlayOrigin, selector: "[cdk-overlay-origin], [overlay-origin], [cdkOverlayOrigin]", exportAs: ["cdkOverlayOrigin"] }, { kind: "component", type: ButtonComponent, selector: "lib-button", inputs: ["variant", "type", "disabled", "width", "height", "borderRadius", "fontSize", "fontWeight", "backgroundColor", "color", "border", "icon", "labels"] }, { kind: "component", type: i12.QuillEditorComponent, selector: "quill-editor" }, { kind: "component", type: TimePickerComponent, selector: "lib-time-picker", inputs: ["config", "labels", "label", "placeholder", "variant", "mode", "minuteStep", "disabled", "required", "errorMessage", "minTime", "maxTime", "width", "height", "borderRadius", "fontSize", "gap", "fontFamily", "labelColor", "labelFontSize", "labelFontWeight", "backgroundColor", "borderColor", "borderWidth", "padding", "fontWeight", "color", "placeholderColor", "focusBorderColor", "errorColor", "disabledBackgroundColor", "disabledColor"], outputs: ["timeChange"] }, { kind: "component", type: FormFieldComponent, selector: "lib-form-field", inputs: ["config", "controller", "formGroup", "allowMulti", "siblingFormArray", "siblingIndex"] }, { kind: "pipe", type: TrustedUrlPipe, name: "trustedUrl" }] });
4291
4500
  }
4292
4501
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: FormFieldComponent, decorators: [{
4293
4502
  type: Component,
4294
- args: [{ selector: 'lib-form-field', standalone: false, template: "<div class=\"form-field mb-8px\" *ngIf=\"isVisible\" [class.has-error]=\"errorMessage\">\r\n\r\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 -->\r\n <div *ngIf=\"isRow\" class=\"form-row grid-row\">\r\n <ng-container *ngFor=\"let child of config.children; trackBy: trackByFieldName\">\r\n <div class=\"row-field\" [style.gridColumn]=\"'span ' + getChildColSpan(child)\" *ngIf=\"child.isEnabled !== false\"\r\n [style.display]=\"rowFieldRef.isVisible === false ? 'none' : null\">\r\n <lib-form-field #rowFieldRef [config]=\"child\" [controller]=\"controller\" [formGroup]=\"formGroup\" [allowMulti]=\"allowMulti\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isGroup && config.sectionConfig?.allowMulti\"\r\n class=\"group-section-wrapper mb-20px\"\r\n [class.multi-save-active]=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n\r\n <!-- Multi-Save: header row with label + top-right Add button -->\r\n <div class=\"multi-save-header d-flex justify-content-between align-items-center mb-24\"\r\n *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n <h3 class=\"group-label\" *ngIf=\"config.sectionConfig?.label\">{{ config.sectionConfig!.label }}</h3>\r\n <lib-button [variant]=\"'outline'\" [icon]=\"{type: 'material', value: 'add'}\" (click)=\"addGroupInstance()\"\r\n class=\"btn-add-multi\">\r\n {{ addMultiLabel }}\r\n </lib-button>\r\n </div>\r\n\r\n <!-- Standard heading (non-multiSave) -->\r\n <h3 class=\"group-label\"\r\n *ngIf=\"config.sectionConfig?.label && !config.sectionConfig?.multiSaveConfig?.active\">{{\r\n config.sectionConfig!.label }}</h3>\r\n\r\n <!-- \u2500\u2500 Standard (non-multiSave) repeater: accordion instances \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <ng-container *ngIf=\"!config.sectionConfig?.multiSaveConfig?.active\">\r\n <div *ngFor=\"let instance of instanceList; trackBy: trackByInstanceId; let i = index\"\r\n class=\"group-accordion-instance\"\r\n [class.is-expanded]=\"isGroupExpanded(i)\">\r\n\r\n <!-- Accordion header -->\r\n <div class=\"group-accordion-header\" (click)=\"toggleGroupAccordion(i)\"\r\n role=\"button\" [attr.aria-expanded]=\"isGroupExpanded(i)\">\r\n <div class=\"accordion-header-left d-flex align-items-center gap-10\">\r\n <span class=\"instance-badge\">{{ i + 1 }}</span>\r\n <span class=\"instance-title\">{{ config.sectionConfig!.label }} #{{ i + 1 }}</span>\r\n </div>\r\n <div class=\"accordion-header-right d-flex align-items-center gap-6\">\r\n <button type=\"button\" class=\"accordion-remove-btn\"\r\n *ngIf=\"instanceList.length > 1\"\r\n (click)=\"$event.stopPropagation(); removeGroupInstance(i)\"\r\n aria-label=\"Remove\">\r\n <mat-icon>delete_outline</mat-icon>\r\n </button>\r\n <mat-icon class=\"accordion-chevron\">\r\n {{ isGroupExpanded(i) ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n\r\n <!-- Accordion body (always mounted so form controls survive collapse) -->\r\n <div class=\"group-accordion-body\" [hidden]=\"!isGroupExpanded(i)\">\r\n <div class=\"group-fields sf-grid\">\r\n <ng-container *ngFor=\"let field of config.sectionConfig!.children\">\r\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\"\r\n *ngIf=\"field.isEnabled !== false\"\r\n [style.display]=\"groupFieldRef.isVisible === false ? 'none' : null\">\r\n <lib-form-field #groupFieldRef [config]=\"field\" [controller]=\"instance.rowController\" [formGroup]=\"instance.fg\"\r\n [allowMulti]=\"false\" [siblingFormArray]=\"groupFormArray\" [siblingIndex]=\"i\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Full-width dashed Add button -->\r\n <button type=\"button\" class=\"btn-add-group\" (click)=\"addGroupInstance()\">\r\n <mat-icon>add</mat-icon> {{ addLabel }} {{ config.sectionConfig!.label }}\r\n </button>\r\n </ng-container>\r\n\r\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 -->\r\n <ng-container *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n <div *ngFor=\"let instance of instanceList; trackBy: trackByInstanceId; let i = index\"\r\n class=\"group-instance position-relative mb-16 overflow-hidden\"\r\n [class.is-editing]=\"instance.isEditing\"\r\n [class.is-card]=\"!instance.isEditing\">\r\n\r\n <!-- Edit / new form view -->\r\n <div [hidden]=\"!instance.isEditing\">\r\n <div class=\"group-fields sf-grid\">\r\n <ng-container *ngFor=\"let field of config.sectionConfig!.children\">\r\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\"\r\n *ngIf=\"field.isEnabled !== false\"\r\n [style.display]=\"groupFieldRef.isVisible === false ? 'none' : null\">\r\n <lib-form-field #groupFieldRef [config]=\"field\" [controller]=\"instance.rowController\" [formGroup]=\"instance.fg\"\r\n [allowMulti]=\"false\" [siblingFormArray]=\"groupFormArray\" [siblingIndex]=\"i\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n\r\n <!-- Save / Cancel -->\r\n <div class=\"group-footer d-flex justify-content-end align-items-center gap-16 p-0-24\"\r\n *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"multiSaveError\">{{ multiSaveError }}</span>\r\n <div class=\"footer-actions d-flex gap-12\">\r\n <lib-button [variant]=\"'outline'\" (click)=\"cancelGroupInstance(i)\">Cancel</lib-button>\r\n <lib-button [variant]=\"'primary'\" (click)=\"saveGroupInstance(i)\">Save</lib-button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Card view (saved state) -->\r\n <ng-container *ngIf=\"!instance.isEditing\">\r\n <div class=\"card-view d-flex justify-content-between align-items-center p-18px-24px\"\r\n [class.is-expanded]=\"instance.isExpanded\">\r\n <div class=\"card-content flex-1 d-flex flex-column gap-4 overflow-hidden\">\r\n <span class=\"card-title font-weight-600 overflow-hidden fs-1rem c-111827\">{{\r\n instance.fg.get(config.sectionConfig!.multiSaveConfig!.summaryField || '')?.value\r\n || '\u2014' }}</span>\r\n </div>\r\n <div class=\"card-actions d-flex align-items-center gap-16 ml-20\">\r\n <mat-icon class=\"icon-delete\" (click)=\"removeGroupInstance(i, true)\">delete_outline</mat-icon>\r\n <mat-icon class=\"icon-edit\" (click)=\"editGroupInstance(i)\">edit_outline</mat-icon>\r\n <mat-icon class=\"icon-expand\" (click)=\"toggleExpandGroupInstance(i)\">\r\n {{ instance.isExpanded ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </ng-container>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isGroup && config.sectionConfig && !config.sectionConfig.allowMulti\"\r\n class=\"group-section-wrapper mb-20px\">\r\n <h3 class=\"group-label\" *ngIf=\"config.sectionConfig.label\">{{ config.sectionConfig.label }}</h3>\r\n <div class=\"group-fields sf-grid\">\r\n <ng-container *ngFor=\"let field of config.sectionConfig.children\">\r\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\" *ngIf=\"field.isEnabled !== false\"\r\n [style.display]=\"singleGroupFieldRef.isVisible === false ? 'none' : null\">\r\n <lib-form-field #singleGroupFieldRef [config]=\"field\" [controller]=\"controller\" [formGroup]=\"groupFormGroup\" [allowMulti]=\"false\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n\r\n\r\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 -->\r\n <div *ngIf=\"isTextField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <textarea *ngIf=\"config.subType === 'LONG'\" class=\"field-input textarea\" [placeholder]=\"config.placeholder || ''\"\r\n [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\" rows=\"4\">\r\n </textarea>\r\n\r\n <!-- Password input with show/hide toggle -->\r\n <div *ngIf=\"config.subType === 'PASSWORD'\" class=\"password-wrapper position-relative d-flex align-items-center\">\r\n <input [type]=\"showPassword ? 'text' : 'password'\" class=\"field-input password-input\"\r\n [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\r\n <button type=\"button\"\r\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\"\r\n (click)=\"showPassword = !showPassword\" tabindex=\"-1\"\r\n [attr.aria-label]=\"showPassword ? 'Hide password' : 'Show password'\">\r\n <mat-icon class=\"eye-icon\">{{ showPassword ? 'visibility' : 'visibility_off' }}</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <span class=\"input-prefix br-none\" *ngIf=\"config.prefix\">{{ config.prefix }}</span>\r\n\r\n <input *ngIf=\"config.subType !== 'LONG' && config.subType !== 'PASSWORD'\"\r\n [type]=\"config.subType === 'EMAIL' ? 'email' : config.subType === 'PHONE' ? 'tel' : 'text'\" class=\"field-input\"\r\n [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\"\r\n [readonly]=\"config.readonly\">\r\n\r\n <span class=\"input-suffix d-flex align-items-center font-weight-500\" *ngIf=\"config.suffix\">{{ config.suffix\r\n }}</span>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n\r\n <!-- Suffix Action Icons (clickable) -->\r\n <div class=\"suffix-action-icons position-absolute d-flex gap-8 align-items-center\"\r\n *ngIf=\"config.suffixActionIcons?.length && !config.readonly\">\r\n <mat-icon class=\"suffix-action-icon\"\r\n *ngFor=\"let action of config.suffixActionIcons\"\r\n [style.color]=\"action.color || null\"\r\n [title]=\"action.tooltip || ''\"\r\n (click)=\"onSuffixActionClick(action.actionId)\">\r\n {{ action.icon }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n <div class=\"char-count-hint font-poppins font-weight-400 text-14 text-right c-6B7280\" *ngIf=\"showCharCount\">\r\n {{ remainingCharacters }} characters remaining\r\n </div>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isNumberField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <span class=\"input-prefix br-none\" *ngIf=\"config.prefix\">{{ config.prefix }}</span>\r\n\r\n <input type=\"number\" class=\"field-input\" [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\"\r\n [min]=\"config.numberConfig?.min ?? null\" [max]=\"config.numberConfig?.max ?? null\"\r\n [step]=\"config.numberConfig?.step || 1\" [class.is-invalid]=\"errorMessage\" [readonly]=\"config.readonly\">\r\n\r\n <span class=\"input-suffix d-flex align-items-center font-weight-500\" *ngIf=\"config.suffix\">{{ config.suffix\r\n }}</span>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n\r\n <!-- Suffix Action Icons (clickable) -->\r\n <div class=\"suffix-action-icons position-absolute d-flex gap-8 align-items-center\"\r\n *ngIf=\"config.suffixActionIcons?.length && !config.readonly\">\r\n <mat-icon class=\"suffix-action-icon\"\r\n *ngFor=\"let action of config.suffixActionIcons\"\r\n [style.color]=\"action.color || null\"\r\n [title]=\"action.tooltip || ''\"\r\n (click)=\"onSuffixActionClick(action.actionId)\">\r\n {{ action.icon }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isDateField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <input matInput [matDatepicker]=\"datePicker\" class=\"field-input date-input has-icon-right\"\r\n [formControlName]=\"config.name!\" [min]=\"effectiveMinDate\" [max]=\"config.dateConfig?.maxDate\"\r\n [class.is-invalid]=\"errorMessage\" [placeholder]=\"config.placeholder || ''\"\r\n [readonly]=\"config.readonly || config.dateConfig?.inputReadonly\"\r\n (click)=\"!config.readonly && datePicker.open()\">\r\n <div class=\"date-icon-wrapper position-absolute d-flex align-items-center justify-content-center\"\r\n *ngIf=\"!config.readonly\">\r\n <mat-datepicker-toggle matSuffix [for]=\"datePicker\"></mat-datepicker-toggle>\r\n </div>\r\n <mat-datepicker #datePicker></mat-datepicker>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isTimeField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <!-- DEFAULT variant: existing native input \u2014 unchanged -->\r\n <ng-container *ngIf=\"!isWheelVariant\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <input type=\"time\" class=\"field-input time-input\" [formControlName]=\"config.name!\"\r\n [min]=\"effectiveMinTime\" [max]=\"effectiveMaxTime\"\r\n [class.is-invalid]=\"errorMessage\" [readonly]=\"config.readonly || config.timeConfig?.inputReadonly\">\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- WHEEL variant: drum-roll picker -->\r\n <lib-time-picker\r\n *ngIf=\"isWheelVariant\"\r\n [formControlName]=\"config.name!\"\r\n [label]=\"config.label || ''\"\r\n [placeholder]=\"config.timeConfig?.placeholder || ''\"\r\n [required]=\"config.required || false\"\r\n [disabled]=\"config.readonly || false\"\r\n [mode]=\"config.timeConfig?.mode || '12'\"\r\n [minuteStep]=\"config.timeConfig?.minuteStep || 1\"\r\n [minTime]=\"effectiveMinTime || undefined\"\r\n [maxTime]=\"config.timeConfig?.maxTime\"\r\n [errorMessage]=\"errorMessage\"\r\n [labels]=\"timePickerLabels\">\r\n </lib-time-picker>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isAutocomplete\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <!-- Hidden real control (stores the code value) -->\r\n <input type=\"hidden\" [formControlName]=\"config.name!\">\r\n\r\n <div class=\"autocomplete-wrapper position-relative d-flex align-items-center w-100\"\r\n [class.is-invalid]=\"errorMessage\" [class.readonly]=\"config.readonly\">\r\n <!-- Search icon -->\r\n <mat-icon class=\"ac-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">search</mat-icon>\r\n\r\n <input class=\"field-input ac-input\" [formControl]=\"autocompleteInputCtrl\" [matAutocomplete]=\"auto\"\r\n [placeholder]=\"config.placeholder || 'Search\u2026'\" [readonly]=\"!!config.readonly\" [class.is-invalid]=\"errorMessage\"\r\n (blur)=\"onAutocompleteClear()\" autocomplete=\"off\">\r\n\r\n <!-- Clear button -->\r\n <button type=\"button\"\r\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\"\r\n *ngIf=\"autocompleteInputCtrl.value && !config.readonly\"\r\n (click)=\"autocompleteInputCtrl.setValue(''); updateValue(null)\" tabindex=\"-1\" aria-label=\"Clear\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n\r\n <mat-autocomplete #auto=\"matAutocomplete\" [panelWidth]=\"'auto'\">\r\n <mat-option *ngFor=\"let option of filteredOptions\" [value]=\"option.label\"\r\n (onSelectionChange)=\"onAutocompleteSelected(option)\">\r\n <span class=\"ac-option-label\">{{ option.label }}</span>\r\n\r\n <!-- Dynamic display fields (image / email / phone / text) -->\r\n <div class=\"ac-display-fields d-flex flex-wrap gap-6 mt-2\" *ngIf=\"option['displayMeta']?.length\">\r\n <ng-container *ngFor=\"let field of option['displayMeta']\">\r\n\r\n <!-- Image avatar -->\r\n <span *ngIf=\"field.type === 'image' && field.value\" class=\"ac-df-item ac-df-image\">\r\n <img [src]=\"field.value\" [alt]=\"field.label || 'image'\" class=\"ac-df-avatar\">\r\n </span>\r\n\r\n <!-- Email -->\r\n <span *ngIf=\"field.type === 'email' && field.value\" class=\"ac-df-item ac-df-chip\">\r\n <mat-icon class=\"ac-df-icon\">mail_outline</mat-icon>\r\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\r\n {{ field.value }}\r\n </span>\r\n\r\n <!-- Phone -->\r\n <span *ngIf=\"field.type === 'phone' && field.value\" class=\"ac-df-item ac-df-chip\" [class]=\"field.className\">\r\n <mat-icon class=\"ac-df-icon\">phone</mat-icon>\r\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\r\n {{ field.value }}\r\n </span>\r\n\r\n <!-- Custom / Icon-based / Generic Text -->\r\n <span *ngIf=\"field.type !== 'image' && field.type !== 'email' && field.type !== 'phone' && field.value\" \r\n class=\"ac-df-item\" [class.ac-df-chip]=\"!!field.icon\" [class]=\"field.className\">\r\n <mat-icon class=\"ac-df-icon\" *ngIf=\"field.icon\">{{ field.icon }}</mat-icon>\r\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\r\n {{ field.value }}\r\n </span>\r\n\r\n </ng-container>\r\n </div>\r\n </mat-option>\r\n <mat-option *ngIf=\"filteredOptions.length === 0\" disabled class=\"ac-no-results fs-0-8125rem c-6B7280\">\r\n No results found\r\n </mat-option>\r\n </mat-autocomplete>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isDropdown\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <!-- SINGLE SELECT: native select (default, non-searchable) -->\r\n <div *ngIf=\"config.subType === 'SINGLE' && !isSearchableDropdown\" class=\"native-select-wrapper\">\r\n <select class=\"field-input\" [formControlName]=\"config.name!\"\r\n [class.is-invalid]=\"errorMessage\" [disabled]=\"optionsLoading || config.disabled\">\r\n <option [ngValue]=\"null\" disabled selected>{{ optionsLoading ? (controller.labels['LBL_LOADING'] || 'Loading...') : (config.placeholder || selectPlaceholderLabel) }}</option>\r\n <option *ngFor=\"let option of displayLocalOptionList\" [value]=\"option.code\">\r\n {{ option.label }}\r\n </option>\r\n </select>\r\n <div *ngIf=\"optionsLoading\" class=\"native-select-spinner\" aria-hidden=\"true\"></div>\r\n </div>\r\n\r\n <!-- SINGLE SELECT: custom searchable dropdown -->\r\n <div *ngIf=\"config.subType === 'SINGLE' && isSearchableDropdown\" class=\"single-select-wrapper\"\r\n [class.is-invalid]=\"errorMessage\">\r\n\r\n <div #singleTrigger cdkOverlayOrigin #singleOrigin=\"cdkOverlayOrigin\"\r\n class=\"field-input single-select-trigger d-flex align-items-center justify-content-between cursor-pointer\"\r\n [class.ss-open]=\"isSingleDropdownOpen\"\r\n (click)=\"!optionsLoading && toggleSingleDropdown($event)\">\r\n <span *ngIf=\"singleSelectedLabel && !optionsLoading\" class=\"single-select-value fs-0-9rem\">\r\n {{ singleSelectedLabel }}\r\n </span>\r\n <span *ngIf=\"!singleSelectedLabel || optionsLoading\" class=\"single-select-placeholder\">\r\n {{ optionsLoading ? (controller.labels['LBL_LOADING'] || 'Loading...') : (config.placeholder || selectPlaceholderLabel) }}\r\n </span>\r\n <div class=\"d-flex align-items-center\">\r\n <div *ngIf=\"optionsLoading\" class=\"mini-spinner me-2\" role=\"progressbar\" aria-label=\"Loading options\"></div>\r\n <mat-icon class=\"single-select-arrow\">\r\n {{ isSingleDropdownOpen ? expandLessLabel : expandMoreLabel }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n\r\n <ng-template cdkConnectedOverlay\r\n [cdkConnectedOverlayOrigin]=\"singleOrigin\"\r\n [cdkConnectedOverlayOpen]=\"isSingleDropdownOpen\"\r\n [cdkConnectedOverlayPositions]=\"dropdownOverlayPositions\"\r\n [cdkConnectedOverlayWidth]=\"singleTrigger.offsetWidth\"\r\n [cdkConnectedOverlayViewportMargin]=\"8\"\r\n [cdkConnectedOverlayPush]=\"true\"\r\n (overlayOutsideClick)=\"onDropdownOverlayOutsideClick($event, singleTrigger)\"\r\n (detach)=\"isSingleDropdownOpen = false\">\r\n <div class=\"single-select-panel\"\r\n (click)=\"$event.stopPropagation()\">\r\n\r\n <!-- Search input -->\r\n <div class=\"dropdown-search-wrapper\">\r\n <mat-icon class=\"dropdown-search-icon\">search</mat-icon>\r\n <input type=\"text\" class=\"dropdown-search-input\"\r\n [placeholder]=\"searchPlaceholderLabel\"\r\n [(ngModel)]=\"searchText\" [ngModelOptions]=\"{standalone: true}\"\r\n (input)=\"dropdownSearch$.next(searchText)\"\r\n (click)=\"$event.stopPropagation()\">\r\n <mat-icon *ngIf=\"searchText\" class=\"dropdown-search-clear cursor-pointer\"\r\n (click)=\"searchText = ''; dropdownSearch$.next(''); $event.stopPropagation()\">close</mat-icon>\r\n </div>\r\n\r\n <!-- Options list -->\r\n <div class=\"single-select-options-list\">\r\n <div *ngIf=\"optionsLoading\" class=\"dropdown-loading-state\">\r\n <div class=\"dropdown-spinner\"></div>\r\n <span class=\"fs-0-875rem c-6B7280\">{{ controller.labels['LBL_LOADING'] || 'Loading...' }}</span>\r\n </div>\r\n\r\n <ng-container *ngIf=\"!optionsLoading\">\r\n <div *ngFor=\"let option of displayOptionList\"\r\n class=\"single-select-option d-flex align-items-center gap-8 cursor-pointer\"\r\n [class.selected]=\"formGroup.get(config.name!)?.value === option.code\"\r\n (click)=\"selectSingleOption(option)\">\r\n <span class=\"fs-0-875rem c-111827\">{{ option.label }}</span>\r\n <mat-icon *ngIf=\"formGroup.get(config.name!)?.value === option.code\" class=\"single-select-check\">check</mat-icon>\r\n </div>\r\n <div *ngIf=\"!filteredOptionList?.length && localOptionList?.length\"\r\n class=\"multi-select-empty fs-0-875rem c-6B7280\">\r\n {{ noMatchingOptionsLabel }}\r\n </div>\r\n <div *ngIf=\"!localOptionList?.length\"\r\n class=\"multi-select-empty fs-0-875rem c-6B7280\">\r\n {{ noOptionsAvailableLabel }}\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </ng-template>\r\n </div>\r\n\r\n <!-- MULTIPLE SELECT: custom panel with checkboxes -->\r\n <div *ngIf=\"config.subType === 'MULTIPLE'\" class=\"multi-select-wrapper\"\r\n [class.is-invalid]=\"errorMessage\">\r\n\r\n <div #multiTrigger cdkOverlayOrigin #multiOrigin=\"cdkOverlayOrigin\"\r\n class=\"field-input multi-select-trigger d-flex align-items-center justify-content-between cursor-pointer\"\r\n [class.ms-open]=\"isMultiDropdownOpen\"\r\n (click)=\"!optionsLoading && toggleMultiDropdown($event)\">\r\n <span *ngIf=\"multiSelectedCount > 0 && !optionsLoading\" class=\"multi-select-value fs-0-9rem\">\r\n {{ multiSelectedCount }} selected\r\n </span>\r\n <span *ngIf=\"multiSelectedCount === 0 || optionsLoading\" class=\"multi-select-placeholder\">\r\n {{ optionsLoading ? (controller.labels['LBL_LOADING'] || 'Loading...') : (config.placeholder || selectPlaceholderLabel) }}\r\n </span>\r\n <div class=\"d-flex align-items-center\">\r\n <div *ngIf=\"optionsLoading\" class=\"mini-spinner me-2\" role=\"progressbar\" aria-label=\"Loading options\"></div>\r\n <mat-icon class=\"multi-select-arrow\">\r\n {{ isMultiDropdownOpen ? expandLessLabel : expandMoreLabel }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n\r\n <ng-template cdkConnectedOverlay\r\n [cdkConnectedOverlayOrigin]=\"multiOrigin\"\r\n [cdkConnectedOverlayOpen]=\"isMultiDropdownOpen\"\r\n [cdkConnectedOverlayPositions]=\"dropdownOverlayPositions\"\r\n [cdkConnectedOverlayWidth]=\"multiTrigger.offsetWidth\"\r\n [cdkConnectedOverlayViewportMargin]=\"8\"\r\n [cdkConnectedOverlayPush]=\"true\"\r\n (overlayOutsideClick)=\"onDropdownOverlayOutsideClick($event, multiTrigger)\"\r\n (detach)=\"isMultiDropdownOpen = false\">\r\n <div class=\"multi-select-panel\"\r\n (click)=\"$event.stopPropagation()\">\r\n\r\n <!-- Search input (shown only when searchConfig.enabled) -->\r\n <div class=\"dropdown-search-wrapper\" *ngIf=\"isSearchableDropdown\">\r\n <mat-icon class=\"dropdown-search-icon\">search</mat-icon>\r\n <input type=\"text\" class=\"dropdown-search-input\"\r\n [placeholder]=\"searchPlaceholderLabel\"\r\n [(ngModel)]=\"searchText\" [ngModelOptions]=\"{standalone: true}\"\r\n (input)=\"dropdownSearch$.next(searchText)\"\r\n (click)=\"$event.stopPropagation()\">\r\n <mat-icon *ngIf=\"searchText\" class=\"dropdown-search-clear cursor-pointer\"\r\n (click)=\"searchText = ''; dropdownSearch$.next(''); $event.stopPropagation()\">close</mat-icon>\r\n </div>\r\n\r\n <!-- Select All checkbox (shown only when showSelectAll is true) -->\r\n <label *ngIf=\"config.optionConfig?.showSelectAll && filteredOptionList?.length\"\r\n class=\"multi-select-option multi-select-all d-flex align-items-center gap-8 cursor-pointer\">\r\n <input type=\"checkbox\"\r\n [checked]=\"isAllSelected()\"\r\n [indeterminate]=\"isSomeSelected()\"\r\n [disabled]=\"!!config.disabled\"\r\n (change)=\"toggleSelectAll($any($event.target).checked)\">\r\n <span class=\"fs-0-875rem c-111827 fw-600\">{{ selectAllLabel }}</span>\r\n </label>\r\n\r\n <!-- Options list -->\r\n <div class=\"multi-select-options-list\">\r\n <div *ngIf=\"optionsLoading\" class=\"dropdown-loading-state\">\r\n <div class=\"dropdown-spinner\"></div>\r\n <span class=\"fs-0-875rem c-6B7280\">{{ controller.labels['LBL_LOADING'] || 'Loading...' }}</span>\r\n </div>\r\n\r\n <ng-container *ngIf=\"!optionsLoading\">\r\n <label *ngFor=\"let option of filteredOptionList\"\r\n class=\"multi-select-option d-flex align-items-center gap-8 cursor-pointer\">\r\n <input type=\"checkbox\"\r\n [checked]=\"isChecked(option.code)\"\r\n [disabled]=\"!!config.disabled\"\r\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\">\r\n <span class=\"fs-0-875rem c-111827\">{{ option.label }}</span>\r\n </label>\r\n <div *ngIf=\"!filteredOptionList?.length && localOptionList?.length\"\r\n class=\"multi-select-empty fs-0-875rem c-6B7280\">\r\n {{ noMatchingOptionsLabel }}\r\n </div>\r\n <div *ngIf=\"!localOptionList?.length\"\r\n class=\"multi-select-empty fs-0-875rem c-6B7280\">\r\n {{ noOptionsAvailableLabel }}\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </ng-template>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isRadio\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"radio-group\" [class.is-invalid]=\"errorMessage\"\r\n [class]=\"config.optionConfig?.layout ? 'layout-' + config.optionConfig!.layout.toLowerCase() : ''\">\r\n <label *ngFor=\"let option of localOptionList\" class=\"radio-label\"\r\n [class.card-item]=\"config.subType === 'CARD'\"\r\n [class.selected]=\"formGroup.get(config.name!)?.value === option.code\"\r\n [style.gridColumn]=\"config.optionConfig?.layout?.toUpperCase() === 'COLUMN' ? 'span ' + getOptionColSpan(option) : null\">\r\n <input type=\"radio\" [formControlName]=\"config.name!\" [value]=\"option.code\">\r\n <div class=\"option-content d-flex flex-column gap-4 flex-1 text-left\">\r\n <span class=\"label-text text-16 c-1F2937\">{{ option.label }}</span>\r\n <span class=\"option-hint text-13 color-gray\" *ngIf=\"option.hint\">{{ option.hint }}</span>\r\n </div>\r\n </label>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isCheckbox\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label && config.subType === 'LIST'\"\r\n class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div *ngIf=\"config.subType === 'BOOL'\" class=\"checkbox-single\">\r\n <label class=\"checkbox-label d-flex align-items-center gap-8 cursor-pointer fs-0-875rem c-111827\">\r\n <input type=\"checkbox\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\r\n <span>{{ config.label }}</span>\r\n </label>\r\n </div>\r\n\r\n <div *ngIf=\"config.subType === 'LIST' || config.subType === 'CARD'\" class=\"checkbox-group d-flex flex-column gap-8\"\r\n [class.is-invalid]=\"errorMessage\"\r\n [class]=\"config.optionConfig?.layout ? 'layout-' + config.optionConfig!.layout.toLowerCase() : ''\">\r\n <label *ngFor=\"let option of localOptionList\"\r\n class=\"checkbox-label d-flex align-items-center gap-8 cursor-pointer fs-0-875rem c-111827\"\r\n [class.card-item]=\"config.subType === 'CARD'\" [class.selected]=\"isChecked(option.code)\"\r\n [style.gridColumn]=\"config.optionConfig?.layout?.toUpperCase() === 'COLUMN' ? 'span ' + getOptionColSpan(option) : null\">\r\n <input type=\"checkbox\" [checked]=\"isChecked(option.code)\" [disabled]=\"!!config.disabled\"\r\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\">\r\n <div class=\"option-content d-flex flex-column gap-4 flex-1 text-left\">\r\n <span class=\"label-text text-16 c-1F2937\">{{ option.label }}</span>\r\n <span class=\"option-hint text-13 color-gray\" *ngIf=\"option.hint\">{{ option.hint }}</span>\r\n </div>\r\n </label>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isChip\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"chip-group d-flex flex-wrap gap-8\" [class.is-invalid]=\"errorMessage\">\r\n <label *ngFor=\"let option of localOptionList\"\r\n class=\"chip-label cursor-pointer p-6px-14px b-ffffff c-374151 b-1px-solid-D1D5DB br-20px fs-0-875rem\"\r\n [class.selected]=\"isChecked(option.code)\">\r\n <input type=\"checkbox\" [checked]=\"isChecked(option.code)\" [disabled]=\"!!config.disabled\"\r\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\" hidden>\r\n <span>{{ option.label }}</span>\r\n </label>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isSwitch\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label class=\"switch-container d-flex justify-content-between align-items-center cursor-pointer\">\r\n <span class=\"field-label d-block font-poppins c-202124 fs-18px\">{{ config.label }}</span>\r\n <div class=\"switch position-relative\">\r\n <input type=\"checkbox\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\r\n <span class=\"slider position-absolute cursor-pointer\"></span>\r\n </div>\r\n </label>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isRichText\" class=\"field-wrapper component-rich-text d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"rich-text-container\" [class.is-invalid]=\"errorMessage\">\r\n <quill-editor [formControlName]=\"config.name!\" class=\"rich-text-editor d-block w-100\"\r\n [placeholder]=\"config.richTextConfig?.placeholder || config.placeholder || ''\"\r\n [styles]=\"{height: config.richTextConfig?.height || '200px'}\"\r\n [modules]=\"richTextModules\">\r\n </quill-editor>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n <div class=\"char-count-hint font-poppins font-weight-400 text-14 text-right c-6B7280\" *ngIf=\"showCharCount\">\r\n {{ remainingCharacters }} characters remaining\r\n </div>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isRating\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"rating-group d-flex gap-4\" [class.is-invalid]=\"errorMessage\">\r\n <span *ngFor=\"let star of getStarArray()\" class=\"star d-inline-flex align-items-center cursor-pointer\"\r\n [class.filled]=\"isStarFilled(star)\" [class.half]=\"isStarHalf(star)\" (click)=\"onRatingChange(star, $event)\">\r\n <mat-icon>{{ isStarFilled(star) || isStarHalf(star) ? 'star' : 'star_border' }}</mat-icon>\r\n </span>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isGenerated\" class=\"field-wrapper d-flex flex-column gap-6\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">{{ config.label\r\n }}</label>\r\n <div class=\"generated-value fs-0-875rem p-0-625rem-0-875rem b-F3F4F6 b-1px-solid-E5E7EB br-8px c-6B7280\">{{ value ||\r\n '-' }}</div>\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint\">{{ config.hint }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isFileUpload\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <!-- Drop Zone -->\r\n <div\r\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\"\r\n [class.drag-over]=\"isDragOver\" [class.has-files]=\"value?.length\" [class.is-invalid]=\"errorMessage\"\r\n (dragover)=\"onDragOver($event)\" (dragleave)=\"onDragLeave($event)\" (drop)=\"onFileDrop($event)\"\r\n (click)=\"fileInput.click()\">\r\n\r\n <!-- Icon with accent-colour pill background -->\r\n <div class=\"upload-icon-wrap mb-4\">\r\n <div class=\"dropzone-icon-pill d-flex align-items-center justify-content-center\">\r\n <mat-icon class=\"upload-cloud-icon\">cloud_upload</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <p class=\"upload-main-text font-weight-600 m-0 fs-0-9rem\">Drag &amp; drop files here</p>\r\n <p class=\"upload-sub-text m-0 fs-0-8rem c-64748B\">or <span class=\"upload-link\">Browse files</span></p>\r\n <p class=\"upload-hint-text m-0 fs-0-78rem c-64748B\" *ngIf=\"config.attachmentConfig?.acceptLabel\">\r\n Supported: <span class=\"upload-formats font-weight-500\">{{ config.attachmentConfig!.acceptLabel }}</span>\r\n </p>\r\n <p class=\"upload-hint-text m-0 fs-0-78rem c-64748B\" *ngIf=\"!config.attachmentConfig?.acceptLabel && config.hint\">\r\n {{ config.hint }}\r\n </p>\r\n <span class=\"upload-size-badge fs-0-72rem\" *ngIf=\"config.attachmentConfig?.maxSizeMB\">\r\n Max {{ config.attachmentConfig!.maxSizeMB }}MB\r\n </span>\r\n\r\n <!-- Hidden native file input -->\r\n <input #fileInput type=\"file\" hidden [attr.multiple]=\"config.attachmentConfig?.multiple ? true : null\"\r\n [attr.accept]=\"config.attachmentConfig?.accept || null\" (change)=\"onFileSelected($event)\">\r\n </div>\r\n\r\n <!-- Uploaded file list -->\r\n <div class=\"uploaded-list d-flex flex-column gap-8 mt-10\" *ngIf=\"value?.length\">\r\n <div *ngFor=\"let f of value; let i = index\"\r\n class=\"uploaded-item d-flex align-items-center gap-10 p-10px-14px br-8px\"\r\n [class.uploading]=\"f.isUploading\">\r\n\r\n <!-- Uploading spinner -->\r\n <ng-container *ngIf=\"f.isUploading; else fileReady\">\r\n <div class=\"upload-spinner rounded-50 b-2px-solid-E2E8F0\"></div>\r\n <div class=\"file-info flex-1 d-flex flex-column\">\r\n <span class=\"file-name font-weight-500 overflow-hidden fs-0-85rem\" [title]=\"f.name\">{{ f.name }}</span>\r\n <span class=\"file-size uploading-label fs-0-72rem\">Uploading...</span>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- Normal state once upload is done -->\r\n <ng-template #fileReady>\r\n <mat-icon class=\"file-type-icon\">{{ getFileIcon(f.type) }}</mat-icon>\r\n <img *ngIf=\"f.type?.startsWith('image') && f.dataUrl\" [src]=\"f.dataUrl\" class=\"file-thumb rounded-4\"\r\n alt=\"preview\">\r\n <div class=\"file-info flex-1 d-flex flex-column\">\r\n <span class=\"file-name font-weight-500 overflow-hidden fs-0-85rem\" [title]=\"f.name\">{{ f.name }}</span>\r\n <span class=\"file-size fs-0-72rem\">{{ formatFileSize(f.size) }}</span>\r\n </div>\r\n </ng-template>\r\n\r\n <!-- Compact icon-only remove button -->\r\n <button type=\"button\" class=\"file-remove-btn d-flex align-items-center justify-content-center rounded-50\"\r\n [disabled]=\"f.isUploading\" (click)=\"!f.isUploading && removeUploadedFile(i)\"\r\n aria-label=\"Remove file\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Validation / file errors -->\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"fileUploadError\">{{ fileUploadError }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage && !fileUploadError\">{{ errorMessage }}</span>\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\"\r\n *ngIf=\"config.hint && !errorMessage && !fileUploadError && !config.attachmentConfig?.acceptLabel\">\r\n {{ config.hint }}\r\n </span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isMediaUpload\" class=\"field-wrapper media-upload-wrapper d-flex flex-column gap-6 p-0 border-none b-none\"\r\n [formGroup]=\"formGroup\">\r\n\r\n <!-- Hidden file input lives outside *ngIf \u2014 triggered via ViewChild -->\r\n <input #mediaDeviceInput type=\"file\" hidden multiple accept=\"image/*\" (change)=\"onMediaFileSelected($event)\">\r\n\r\n <!-- Two-column layout -->\r\n <div class=\"mu-layout d-grid align-items-start\">\r\n\r\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 -->\r\n <div class=\"mu-left d-flex flex-column gap-20\">\r\n\r\n <!-- Header: title + max-items badge -->\r\n <div class=\"mu-header d-flex align-items-start flex-wrap gap-10\">\r\n <h3 class=\"mu-title m-0 c-111827 fs-1-35rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </h3>\r\n <span\r\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\"\r\n *ngIf=\"config.attachmentConfig?.maxFiles\">\r\n {{ controller.labels['LBL_MEDIA_MAX_PREFIX'] || 'Maximum' }}\r\n {{ config.attachmentConfig?.maxFiles }}\r\n {{ controller.labels['LBL_MEDIA_MAX_SUFFIX'] || 'Image & Videos' }}\r\n </span>\r\n </div>\r\n\r\n <!-- Description -->\r\n <p class=\"mu-description m-0 fs-0-875rem c-6B7280\" *ngIf=\"config.attachmentConfig?.description\">\r\n {{ config.attachmentConfig?.description }}\r\n </p>\r\n <p class=\"mu-description m-0 fs-0-875rem c-6B7280\"\r\n *ngIf=\"!config.attachmentConfig?.description && controller.labels['LBL_MEDIA_DESC']\">\r\n {{ controller.labels['LBL_MEDIA_DESC'] }}\r\n </p>\r\n\r\n <!-- Feature bullet list -->\r\n <ul class=\"mu-features m-0 p-0 d-flex flex-column gap-8 ls-none\"\r\n *ngIf=\"config.attachmentConfig?.features?.length || controller.labels['LBL_MEDIA_FEATURE_1']\">\r\n <ng-container *ngIf=\"config.attachmentConfig?.features?.length\">\r\n <li *ngFor=\"let f of config.attachmentConfig?.features\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ f }}\r\n </li>\r\n </ng-container>\r\n <ng-container *ngIf=\"!config.attachmentConfig?.features?.length\">\r\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_1']\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_1'] }}\r\n </li>\r\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_2']\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_2'] }}\r\n </li>\r\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_3']\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_3'] }}\r\n </li>\r\n </ng-container>\r\n </ul>\r\n\r\n <!-- Backdrop to close dropdown on outside click -->\r\n <div class=\"media-menu-backdrop\" *ngIf=\"showMediaMenu\"\r\n (click)=\"$event.stopPropagation(); showMediaMenu = false\"></div>\r\n\r\n <!-- Add Media button + dropdown -->\r\n <div class=\"media-add-container position-relative\" (click)=\"showMediaMenu = !showMediaMenu\">\r\n <lib-button id=\"btn-add-media-{{ config.name }}\" [variant]=\"'warning'\"\r\n [icon]=\"{type: 'material', value: 'add_photo_alternate'}\">\r\n {{ controller.labels['LBL_ADD_MEDIA'] || 'Add media' }}\r\n <mat-icon class=\"menu-chevron fs-18px\">add</mat-icon>\r\n </lib-button>\r\n\r\n <div class=\"media-dropdown position-absolute rounded-12 overflow-hidden b-ffffff b-1px-solid-E5E7EB\"\r\n *ngIf=\"showMediaMenu\" role=\"menu\" (click)=\"$event.stopPropagation()\">\r\n <!-- Video -->\r\n <button id=\"btn-media-video-{{ config.name }}\" type=\"button\"\r\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\"\r\n (click)=\"onMediaMenuVideo(); showMediaMenu = false\" role=\"menuitem\">\r\n <span\r\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>\r\n <span class=\"media-drop-text d-flex flex-column flex-1\">\r\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\r\n controller.labels['LBL_MEDIA_VIDEO'] || 'Video' }}</span>\r\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_VIDEO_DESC'] || 'Add\r\n YouTube URL'\r\n }}</span>\r\n </span>\r\n </button>\r\n <!-- Device -->\r\n <button id=\"btn-media-device-{{ config.name }}\" type=\"button\"\r\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\"\r\n (click)=\"onMediaMenuDevice(); showMediaMenu = false\" role=\"menuitem\">\r\n <span\r\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>\r\n <span class=\"media-drop-text d-flex flex-column flex-1\">\r\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\r\n controller.labels['LBL_MEDIA_DEVICE'] || 'Upload from device'\r\n }}</span>\r\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_DEVICE_DESC'] ||\r\n 'Select images from your\r\n computer' }}</span>\r\n </span>\r\n </button>\r\n <!-- Library -->\r\n <button id=\"btn-media-library-{{ config.name }}\" type=\"button\"\r\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\"\r\n (click)=\"onMediaMenuLibrary(); showMediaMenu = false\" role=\"menuitem\">\r\n <span\r\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>\r\n <span class=\"media-drop-text d-flex flex-column flex-1\">\r\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\r\n controller.labels['LBL_MEDIA_LIBRARY'] || 'Upload from library'\r\n }}</span>\r\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_LIBRARY_DESC'] ||\r\n 'Choose from default\r\n images' }}</span>\r\n </span>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- YouTube URL Input (inline below button) -->\r\n <div class=\"youtube-input-panel d-flex flex-column gap-8 p-16 rounded-10 b-FFFAF1 b-1px-solid-E5E7EB\"\r\n *ngIf=\"showYoutubeInput\">\r\n <label class=\"youtube-panel-label d-flex align-items-center gap-6 font-weight-600 fs-0-875rem c-111827\">\r\n {{ controller.labels['LBL_YOUTUBE_URL'] || 'Video URL' }}\r\n </label>\r\n <div class=\"youtube-input-row d-flex gap-8\">\r\n <input id=\"input-youtube-url-{{ config.name }}\" type=\"url\" class=\"field-input youtube-url-input\"\r\n [(ngModel)]=\"youtubeUrlInput\" [ngModelOptions]=\"{standalone: true}\"\r\n [placeholder]=\"controller.labels['PH_YOUTUBE_URL'] || 'Video URL'\" (keyup.enter)=\"addYoutubeMedia()\">\r\n <lib-button id=\"btn-add-youtube-{{ config.name }}\" [variant]=\"'secondary'\" (click)=\"addYoutubeMedia()\">\r\n {{ controller.labels['LBL_ADD'] || 'Add' }}\r\n </lib-button>\r\n </div>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"youtubeUrlError\">{{ youtubeUrlError }}</span>\r\n </div>\r\n\r\n <div\r\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\"\r\n *ngIf=\"mediaUploadError\">\r\n <mat-icon class=\"status-icon fs-18px\">error_outline</mat-icon>\r\n <span>{{ mediaUploadError }}</span>\r\n </div>\r\n\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n </div>\r\n <!-- end left panel -->\r\n\r\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 -->\r\n <div class=\"mu-right d-flex flex-column gap-12\">\r\n\r\n <!-- Carousel (when items exist) -->\r\n <div class=\"media-carousel-section d-flex flex-column gap-12\" *ngIf=\"mediaItems.length\">\r\n <div\r\n class=\"media-carousel-main position-relative w-100 overflow-hidden d-flex align-items-center justify-content-center br-12px b-0F172A\">\r\n <button id=\"btn-carousel-prev-{{ config.name }}\" type=\"button\"\r\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\"\r\n (click)=\"mediaCarouselPrev()\" [disabled]=\"mediaCarouselIndex === 0\" aria-label=\"Previous\">\r\n <mat-icon>chevron_left</mat-icon>\r\n </button>\r\n\r\n <div class=\"carousel-viewer position-absolute d-flex align-items-center justify-content-center\"\r\n *ngFor=\"let item of mediaItems; let i = index\" [hidden]=\"i !== mediaCarouselIndex\">\r\n <div *ngIf=\"item.isUploading\"\r\n class=\"carousel-uploading d-flex flex-column align-items-center gap-12 c-94A3B8 fs-0-85rem\">\r\n <div class=\"carousel-spinner rounded-50 b-3px-solid-rgba-255-255-255-0-15\"></div>\r\n <span>{{ controller.labels['LBL_UPLOADING'] || 'Uploading\u2026' }}</span>\r\n </div>\r\n <ng-container *ngIf=\"!item.isUploading && item.mediaType === 'youtube'\">\r\n <iframe class=\"carousel-iframe w-100 h-100 br-12px\" [src]=\"item.url | trustedUrl\" frameborder=\"0\"\r\n allowfullscreen\r\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\">\r\n </iframe>\r\n </ng-container>\r\n <ng-container *ngIf=\"!item.isUploading && item.mediaType === 'image'\">\r\n <img class=\"carousel-image w-100 h-100 br-12px\" [src]=\"item.url\" alt=\"Media\">\r\n </ng-container>\r\n <button id=\"btn-remove-media-{{ config.name }}-{{ i }}\" type=\"button\"\r\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\"\r\n [disabled]=\"item.isUploading\" (click)=\"removeMediaItem(i)\" aria-label=\"Remove\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <button id=\"btn-carousel-next-{{ config.name }}\" type=\"button\"\r\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\"\r\n (click)=\"mediaCarouselNext()\" [disabled]=\"mediaCarouselIndex === mediaItems.length - 1\" aria-label=\"Next\">\r\n <mat-icon>chevron_right</mat-icon>\r\n </button>\r\n\r\n <div class=\"carousel-dots position-absolute d-flex gap-6\">\r\n <span *ngFor=\"let item of mediaItems; let i = index\"\r\n class=\"carousel-dot rounded-50 cursor-pointer b-rgba-255-255-255-0-45\"\r\n [class.active]=\"i === mediaCarouselIndex\" (click)=\"mediaGoTo(i)\"></span>\r\n </div>\r\n </div>\r\n\r\n <!-- Thumbnail strip -->\r\n <div class=\"media-thumbnail-strip d-flex flex-wrap gap-8 pb-4px\">\r\n <div *ngFor=\"let item of mediaThumbnails; let i = index\"\r\n class=\"media-thumb rounded-8 overflow-hidden cursor-pointer d-flex align-items-center justify-content-center b-2px-solid-transparent b-E2E8F0\"\r\n [class.active]=\"i === mediaCarouselIndex\" (click)=\"mediaGoTo(i)\">\r\n <div *ngIf=\"item.isUploading\"\r\n class=\"thumb-uploading d-flex align-items-center justify-content-center w-100 h-100\">\r\n <div class=\"thumb-spinner rounded-50 b-2px-solid-E2E8F0\"></div>\r\n </div>\r\n <img *ngIf=\"!item.isUploading && item.mediaType === 'youtube' && item.thumbnailUrl\"\r\n [src]=\"item.thumbnailUrl\" class=\"thumb-img w-100 h-100\" alt=\"Video thumbnail\">\r\n <div *ngIf=\"!item.isUploading && item.mediaType === 'youtube' && !item.thumbnailUrl\"\r\n class=\"thumb-yt-placeholder d-flex align-items-center justify-content-center w-100 h-100 b-1E293B c-EF4444\">\r\n <mat-icon>play_circle</mat-icon>\r\n </div>\r\n <img *ngIf=\"!item.isUploading && item.mediaType === 'image' && item.url\" [src]=\"item.url\"\r\n class=\"thumb-img w-100 h-100\" alt=\"Image thumbnail\">\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Empty right-side placeholder -->\r\n <div\r\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\"\r\n *ngIf=\"!mediaItems.length\" (click)=\"onMediaMenuDevice()\">\r\n <mat-icon class=\"mu-right-empty-icon fs-52px\">perm_media</mat-icon>\r\n <p>{{ controller.labels['LBL_ADD_MEDIA'] || 'Add media' }}</p>\r\n </div>\r\n\r\n </div>\r\n <!-- end right panel -->\r\n\r\n </div><!-- end mu-layout -->\r\n </div>\r\n\r\n\r\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 -->\r\n <!-- Wrapper is always in DOM (hidden) so @ViewChild can move it to body -->\r\n <div #libraryModal class=\"media-library-portal-host\" [class.is-open]=\"showLibraryModal\">\r\n\r\n <!-- Backdrop -->\r\n <div class=\"media-library-overlay\" (click)=\"closeLibraryModal()\"></div>\r\n\r\n <!-- Modal card -->\r\n <div class=\"media-library-modal d-flex flex-column overflow-hidden b-ffffff br-16px\"\r\n role=\"dialog\" aria-modal=\"true\">\r\n <div class=\"library-modal-header d-flex align-items-start justify-content-between p-24px-28px bb-1px-solid-E5E7EB\">\r\n <div class=\"header-left d-flex flex-column gap-8\">\r\n <h3 class=\"library-modal-title m-0 color-dark fs-1-25rem\">\r\n {{ controller.labels['LBL_ADD_IMAGES'] || 'Add Images' }}\r\n </h3>\r\n <p class=\"library-modal-subtitle m-0 color-gray fs-0-85rem\">\r\n {{ controller.labels['LBL_LIBRARY_MODAL_DESC'] || 'Select images from your library.' }}\r\n </p>\r\n </div>\r\n <button id=\"btn-close-library-{{ config.name }}\" type=\"button\"\r\n class=\"library-close-btn d-flex align-items-center justify-content-center cursor-pointer rounded-50 border-none b-none c-9CA3AF\"\r\n (click)=\"closeLibraryModal()\" aria-label=\"Close\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <!-- Loading -->\r\n <div class=\"library-loading\" *ngIf=\"libraryLoading\">\r\n <div class=\"lib-spinner rounded-50 b-3px-solid-E2E8F0\"></div>\r\n <span>{{ controller.labels['LBL_LOADING'] || 'Loading\u2026' }}</span>\r\n </div>\r\n\r\n <!-- Error -->\r\n <div class=\"library-error d-flex align-items-center gap-8 color-error b-FEF2F2 fs-0-875rem p-16px-24px\"\r\n *ngIf=\"libraryError && !libraryLoading\">\r\n <mat-icon>error_outline</mat-icon>\r\n {{ libraryError }}\r\n </div>\r\n\r\n <!-- Image grid -->\r\n <div class=\"library-grid d-grid gap-16 flex-1 p-28px b-F9FAFB\" *ngIf=\"!libraryLoading && libraryImages.length\">\r\n <div *ngFor=\"let img of libraryImages; let li = index\" id=\"lib-img-{{ config.name }}-{{ li }}\"\r\n class=\"library-grid-item position-relative rounded-12 overflow-hidden cursor-pointer bg-white b-3px-solid-transparent\"\r\n [class.selected]=\"isLibraryItemSelected(img)\" (click)=\"toggleLibraryItem(img)\">\r\n <img [src]=\"getLibraryItemUrl(img)\" class=\"library-grid-img w-100 h-100 d-block\" alt=\"Library image\">\r\n <div\r\n class=\"library-check position-absolute bg-white rounded-50 d-flex align-items-center justify-content-center c-3B82F6\"\r\n *ngIf=\"isLibraryItemSelected(img)\">\r\n <mat-icon>check_circle</mat-icon>\r\n </div>\r\n <div class=\"library-overlay-hover position-absolute b-rgba-59-130-246-0-12\"></div>\r\n </div>\r\n </div>\r\n\r\n <!-- Empty library -->\r\n <div\r\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\"\r\n *ngIf=\"!libraryLoading && !libraryError && libraryImages.length === 0\">\r\n <mat-icon>image_not_supported</mat-icon>\r\n <span>{{ controller.labels['LBL_LIBRARY_EMPTY'] || 'No images found in library.' }}</span>\r\n </div>\r\n\r\n <!-- Footer -->\r\n <div class=\"library-modal-footer d-flex align-items-center justify-content-end bg-white p-20px-28px bt-1px-solid-E5E7EB\">\r\n <div class=\"library-footer-actions d-flex gap-12\">\r\n <lib-button id=\"btn-library-cancel-{{ config.name }}\" [variant]=\"'outline'\" (click)=\"closeLibraryModal()\">\r\n {{ controller.labels['LBL_CANCEL'] || 'Cancel' }}\r\n </lib-button>\r\n <lib-button id=\"btn-library-confirm-{{ config.name }}\" [variant]=\"'primary'\"\r\n [disabled]=\"librarySelectedIds.size === 0\" (click)=\"confirmLibrarySelection()\">\r\n {{ controller.labels['LBL_CONTINUE'] || 'Continue' }}\r\n </lib-button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n\r\n\r\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 -->\r\n <div *ngIf=\"isLocation\" class=\"field-wrapper location-field-wrapper d-flex flex-column gap-6 gap-12\"\r\n [formGroup]=\"formGroup\">\r\n\r\n <!-- Field label -->\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n <p class=\"location-subtitle m-0 c-6B7280 fs-0-8125rem\" *ngIf=\"config.hint\">{{ config.hint }}</p>\r\n\r\n <!-- Three-tab bar -->\r\n <div class=\"location-tabs d-flex gap-12 mb-24\">\r\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'VENUE' ? 'warning' : 'outline'\"\r\n (click)=\"onLocationTabChange('VENUE')\">\r\n {{ controller.labels['LBL_LOC_VENUE'] || 'Venue' }}\r\n </lib-button>\r\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'ONLINE' ? 'warning' : 'outline'\"\r\n (click)=\"onLocationTabChange('ONLINE')\">\r\n {{ controller.labels['LBL_LOC_ONLINE'] || 'Online Event' }}\r\n </lib-button>\r\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'TBA' ? 'warning' : 'outline'\"\r\n (click)=\"onLocationTabChange('TBA')\">\r\n {{ controller.labels['LBL_LOC_TBA'] || 'To be Announced' }}\r\n </lib-button>\r\n </div>\r\n\r\n <!-- VENUE TAB -->\r\n <div *ngIf=\"locationActiveTab === 'VENUE'\" class=\"loc-panel loc-venue-panel d-flex flex-column gap-12\">\r\n\r\n <p class=\"loc-section-label m-0 font-weight-600 c-111827 fs-0-9rem\">\r\n {{ controller.labels['LBL_LOC_ADDRESS'] || 'Location address' }}\r\n </p>\r\n\r\n <!-- Added venue rows -->\r\n <div class=\"loc-venue-list d-flex flex-column gap-8\" *ngIf=\"locationVenues.length > 0\">\r\n <div *ngFor=\"let venue of locationVenues; let i = index\"\r\n class=\"loc-venue-item d-flex align-items-center gap-10 p-10px-14px br-7px b-ffffff b-1px-solid-D1D5DB\">\r\n <mat-icon class=\"loc-venue-search-icon fs-18px c-9CA3AF\">search</mat-icon>\r\n <span class=\"loc-venue-text flex-1 overflow-hidden fs-0-875rem c-111827\">{{ venue.address || venue.name ||\r\n venue.description }}</span>\r\n <button type=\"button\"\r\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\"\r\n (click)=\"removeLocationVenue(i)\">\r\n <mat-icon>delete_outline</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Location count badge -->\r\n <p class=\"loc-count-text m-0 font-weight-600 fs-0-8125rem c-3B82F6\"\r\n *ngIf=\"locationVenues.length > 0 && config.locationConfig?.allowMulti\">\r\n {{ locationVenues.length }}&nbsp;{{ controller.labels['LBL_LOC_COUNT_SUFFIX'] || 'Locations Added!' }}\r\n </p>\r\n\r\n <!-- Search input (hide when max reached) -->\r\n <div class=\"loc-search-container position-relative\" *ngIf=\"!locationMaxReached\">\r\n <div class=\"loc-search-wrapper position-relative d-flex align-items-center mt-4\">\r\n <mat-icon class=\"loc-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">search</mat-icon>\r\n <input\r\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\"\r\n [placeholder]=\"config.locationConfig?.venuePlaceholder || (controller.labels['PH_LOC_VENUE'] || 'Type to search venue...')\"\r\n [value]=\"locationSearchText\" (input)=\"handleLocationSearchInput($event)\" (blur)=\"hideLocationSuggestions()\"\r\n autocomplete=\"off\" [class.is-invalid]=\"errorMessage\">\r\n </div>\r\n <!-- Suggestions dropdown -->\r\n <div class=\"loc-suggestions-panel position-absolute overflow-hidden br-8px b-ffffff b-1px-solid-D1D5DB\"\r\n *ngIf=\"locationShowSuggestions && locationSuggestions.length\">\r\n <div *ngFor=\"let sug of locationSuggestions\"\r\n class=\"loc-suggestion-item d-flex align-items-center gap-10 cursor-pointer p-10px-14px\"\r\n (mousedown)=\"onLocationSuggestionSelect(sug)\">\r\n <mat-icon class=\"loc-suggestion-icon fs-18px c-E53E3E\">place</mat-icon>\r\n <span class=\"loc-suggestion-text overflow-hidden fs-0-875rem c-374151\">{{ sug.description }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Add another button -->\r\n <button type=\"button\"\r\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\"\r\n *ngIf=\"locationVenues.length > 0 && !locationMaxReached && config.locationConfig?.allowMulti\">\r\n <mat-icon>add_circle_outline</mat-icon>\r\n <span>{{ controller.labels['LBL_LOC_ADD_ANOTHER'] || 'Add another Location' }}</span>\r\n </button>\r\n\r\n <!-- Map -->\r\n <div class=\"loc-map-container overflow-hidden br-10px b-1px-solid-E5E7EB\"\r\n *ngIf=\"config.locationConfig?.showMap !== false\">\r\n <ng-container *ngIf=\"config.locationConfig?.googleMapsApiKey; else simpleEmbed\">\r\n <div [id]=\"'loc-map-' + config.name\" class=\"loc-map-frame w-100 d-block border-none\"\r\n [style.height]=\"config.locationConfig?.mapHeight || '300px'\"></div>\r\n </ng-container>\r\n <ng-template #simpleEmbed>\r\n <iframe class=\"loc-map-frame w-100 d-block border-none\"\r\n [style.height]=\"config.locationConfig?.mapHeight || '300px'\" [src]=\"getLocationMapEmbedUrl() | trustedUrl\"\r\n frameborder=\"0\" allowfullscreen loading=\"lazy\">\r\n </iframe>\r\n </ng-template>\r\n </div>\r\n\r\n <!-- Map hint -->\r\n <p class=\"loc-map-hint m-0 text-center fs-0-78rem c-6B7280\">\r\n {{ controller.labels['LBL_LOC_MAP_HINT'] || 'Type the venue address. A map will appear to assist you.' }}\r\n </p>\r\n </div>\r\n\r\n <!-- ONLINE TAB -->\r\n <div *ngIf=\"locationActiveTab === 'ONLINE'\" class=\"loc-panel loc-online-panel d-flex flex-column gap-12\">\r\n <p class=\"loc-section-label m-0 font-weight-600 c-111827 fs-0-9rem\">\r\n {{ controller.labels['LBL_LOC_ONLINE_URL'] || 'Event URL' }}\r\n </p>\r\n <div class=\"loc-search-wrapper position-relative d-flex align-items-center mt-4\">\r\n <mat-icon class=\"loc-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">link</mat-icon>\r\n <input\r\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\"\r\n type=\"url\"\r\n [placeholder]=\"config.locationConfig?.onlinePlaceholder || (controller.labels['PH_LOC_ONLINE'] || 'https://zoom.us/j/...')\"\r\n [value]=\"locationOnlineUrl\" (input)=\"onLocationUrlChange($any($event.target).value)\"\r\n [class.is-invalid]=\"errorMessage\">\r\n </div>\r\n </div>\r\n\r\n <!-- TBA TAB -->\r\n <div *ngIf=\"locationActiveTab === 'TBA'\"\r\n class=\"loc-panel loc-tba-panel d-flex flex-column gap-12 justify-content-center\">\r\n <div\r\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\">\r\n <mat-icon class=\"loc-tba-icon fs-40px c-9CA3AF\">schedule</mat-icon>\r\n <p class=\"loc-tba-text m-0 c-6B7280 fs-0-9rem\">\r\n {{ controller.labels['LBL_LOC_TBA_DESC'] || \"This event's location is yet to be announced. Check back later\r\n for updates.\" }}\r\n </p>\r\n </div>\r\n </div>\r\n\r\n <!-- Hidden real form control -->\r\n <input type=\"hidden\" [formControlName]=\"config.name!\">\r\n\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Link List 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 -->\r\n <div *ngIf=\"isLinkList\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ controller.labels[config.label] || config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <!-- Top Links list (if listPosition === 'top') -->\r\n <div *ngIf=\"linkListConfig?.listPosition === 'top' && linkListItems.length > 0\" class=\"link-list-container mb-8px\">\r\n <ul class=\"link-items-list p-0 m-0\" \r\n [style.display]=\"'grid'\"\r\n [style.gridTemplateColumns]=\"'repeat(12, 1fr)'\"\r\n [style.gap]=\"'12px 16px'\">\r\n <li *ngFor=\"let item of linkListItems; let idx = index\" \r\n class=\"link-list-item\"\r\n [style.gridColumn]=\"'span ' + (linkListConfig?.colSpan || 12)\">\r\n <!-- Normal display mode -->\r\n <ng-container *ngIf=\"editingLinkIndex !== idx\">\r\n <span class=\"link-url-text text-truncate font-weight-500 color-333\">{{ getLinkDisplayUrl(item) }}</span>\r\n <div class=\"link-item-actions d-flex align-items-center gap-12px\">\r\n <button type=\"button\" class=\"action-btn edit-btn\" \r\n *ngIf=\"linkListConfig?.editable !== false\" (click)=\"startEditLink(idx)\" [title]=\"controller.labels['COMMON.ACTIONS.EDIT'] || 'Edit'\">\r\n <mat-icon>edit</mat-icon>\r\n </button>\r\n <button type=\"button\" class=\"action-btn delete-btn\" \r\n *ngIf=\"linkListConfig?.deleteable !== false\" (click)=\"deleteLinkItem(idx)\" [title]=\"controller.labels['COMMON.ACTIONS.DELETE'] || 'Delete'\">\r\n <mat-icon>delete_outline</mat-icon>\r\n </button>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- Inline Edit mode -->\r\n <ng-container *ngIf=\"editingLinkIndex === idx\">\r\n <div class=\"edit-input-wrapper d-flex align-items-center gap-8px w-100\">\r\n <input type=\"text\" class=\"field-input flex-1 py-4px\" [(ngModel)]=\"editingLinkValue\" \r\n [ngModelOptions]=\"{standalone: true}\" (keydown.enter)=\"saveEditLink(idx)\" (keydown.escape)=\"cancelEditLink()\"\r\n (input)=\"linkValidationError = ''\">\r\n <button type=\"button\" class=\"action-btn save-btn\" (click)=\"saveEditLink(idx)\" [title]=\"controller.labels['COMMON.ACTIONS.SAVE'] || 'Save'\">\r\n <mat-icon>check</mat-icon>\r\n </button>\r\n <button type=\"button\" class=\"action-btn cancel-btn\" (click)=\"cancelEditLink()\" [title]=\"controller.labels['COMMON.ACTIONS.CANCEL'] || 'Cancel'\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n </ng-container>\r\n </li>\r\n </ul>\r\n </div>\r\n\r\n <!-- Input text + plus icon -->\r\n <div class=\"input-group position-relative d-flex w-100\">\r\n <input type=\"text\" class=\"field-input pe-40px\" [placeholder]=\"controller.labels[config.placeholder!] || config.placeholder || controller.labels['COMMON.ENTER_LINK'] || 'Enter link...'\"\r\n [(ngModel)]=\"newLinkInputValue\" [ngModelOptions]=\"{standalone: true}\" (keydown.enter)=\"addLinkItem($event)\"\r\n [disabled]=\"config.readonly || config.disabled\"\r\n (input)=\"linkValidationError = ''\">\r\n \r\n <button type=\"button\" class=\"position-absolute right-8px top-50-percent translate-y-minus-50 d-flex align-items-center justify-content-center b-none bg-none p-0 cursor-pointer color-primary\"\r\n [disabled]=\"config.readonly || config.disabled || !newLinkInputValue?.trim()\"\r\n (click)=\"addLinkItem()\" style=\"right: 8px; top: 50%; transform: translateY(-50%); z-index: 10;\">\r\n <mat-icon>add</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <!-- Validation error message -->\r\n <span class=\"field-error color-error fs-0-75rem mt-4px d-block\" *ngIf=\"linkValidationError\">{{ linkValidationError }}</span>\r\n\r\n <!-- Bottom Links list (if listPosition !== 'top') -->\r\n <div *ngIf=\"linkListConfig?.listPosition !== 'top' && linkListItems.length > 0\" class=\"link-list-container mt-8px\">\r\n <ul class=\"link-items-list p-0 m-0\" \r\n [style.display]=\"'grid'\"\r\n [style.gridTemplateColumns]=\"'repeat(12, 1fr)'\"\r\n [style.gap]=\"'12px 16px'\">\r\n <li *ngFor=\"let item of linkListItems; let idx = index\" \r\n class=\"link-list-item\"\r\n [style.gridColumn]=\"'span ' + (linkListConfig?.colSpan || 12)\">\r\n <!-- Normal display mode -->\r\n <ng-container *ngIf=\"editingLinkIndex !== idx\">\r\n <span class=\"link-url-text text-truncate font-weight-500 color-333\">{{ getLinkDisplayUrl(item) }}</span>\r\n <div class=\"link-item-actions d-flex align-items-center gap-12px\">\r\n <button type=\"button\" class=\"action-btn edit-btn\" \r\n *ngIf=\"linkListConfig?.editable !== false\" (click)=\"startEditLink(idx)\" [title]=\"controller.labels['COMMON.ACTIONS.EDIT'] || 'Edit'\">\r\n <mat-icon>edit</mat-icon>\r\n </button>\r\n <button type=\"button\" class=\"action-btn delete-btn\" \r\n *ngIf=\"linkListConfig?.deleteable !== false\" (click)=\"deleteLinkItem(idx)\" [title]=\"controller.labels['COMMON.ACTIONS.DELETE'] || 'Delete'\">\r\n <mat-icon>delete_outline</mat-icon>\r\n </button>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- Inline Edit mode -->\r\n <ng-container *ngIf=\"editingLinkIndex === idx\">\r\n <div class=\"edit-input-wrapper d-flex align-items-center gap-8px w-100\">\r\n <input type=\"text\" class=\"field-input flex-1 py-4px\" [(ngModel)]=\"editingLinkValue\" \r\n [ngModelOptions]=\"{standalone: true}\" (keydown.enter)=\"saveEditLink(idx)\" (keydown.escape)=\"cancelEditLink()\"\r\n (input)=\"linkValidationError = ''\">\r\n <button type=\"button\" class=\"action-btn save-btn\" (click)=\"saveEditLink(idx)\" [title]=\"controller.labels['COMMON.ACTIONS.SAVE'] || 'Save'\">\r\n <mat-icon>check</mat-icon>\r\n </button>\r\n <button type=\"button\" class=\"action-btn cancel-btn\" (click)=\"cancelEditLink()\" [title]=\"controller.labels['COMMON.ACTIONS.CANCEL'] || 'Cancel'\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n </ng-container>\r\n </li>\r\n </ul>\r\n </div>\r\n\r\n <!-- Hidden real form control -->\r\n <input type=\"hidden\" [formControlName]=\"config.name!\">\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ controller.labels[config.hint] || config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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-8px{margin-bottom:8px!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}.generated-value{word-break:break-all}: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:.8rem!important;font-weight:600!important;line-height:1.2;letter-spacing:.04em;text-transform:uppercase!important;color:#202124!important;margin-bottom:var(--cc-sf-label-mb, 0px)!important}.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}.dropdown-search-wrapper{display:flex;align-items:center;gap:8px;padding:8px 12px;border-bottom:1px solid var(--cc-sf-input-border, #E5E7EB);background:var(--cc-sf-input-bg, #ffffff)}.dropdown-search-wrapper .dropdown-search-icon{font-size:18px;width:18px;height:18px;color:var(--cc-sf-input-placeholder, #9CA3AF);flex-shrink:0}.dropdown-search-wrapper .dropdown-search-input{flex:1;border:none;outline:none;background:transparent;font-size:.875rem;color:var(--cc-sf-label-color, #111827);font-family:inherit}.dropdown-search-wrapper .dropdown-search-input::placeholder{color:var(--cc-sf-input-placeholder, #C4C9D4)}.dropdown-search-wrapper .dropdown-search-clear{font-size:18px;width:18px;height:18px;color:var(--cc-sf-input-placeholder, #9CA3AF);flex-shrink:0;transition:color .15s}.dropdown-search-wrapper .dropdown-search-clear:hover{color:var(--cc-sf-label-color, #111827)}.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:var(--cc-sf-label-color, #6B7280);flex-shrink:0}.multi-select-wrapper .multi-select-panel{position:absolute;top:calc(100% + 4px);left:0;right:0;background:var(--cc-sf-input-bg, #ffffff);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;display:flex;flex-direction:column;overflow:hidden;max-height:320px}.multi-select-wrapper .multi-select-options-list{flex:1;overflow-y:auto;max-height:240px}.multi-select-wrapper .multi-select-options-list::-webkit-scrollbar{width:4px}.multi-select-wrapper .multi-select-options-list::-webkit-scrollbar-thumb{background:var(--cc-sf-input-border, #D1D5DB);border-radius:2px}.multi-select-wrapper .multi-select-option{padding:8px 12px;border-bottom:1px solid var(--cc-sf-input-border, #F3F4F6);transition:background .1s}.multi-select-wrapper .multi-select-option:last-of-type{border-bottom:none}.multi-select-wrapper .multi-select-option:hover{background:var(--cc-sf-input-hover-bg, #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-option.multi-select-all{border-bottom:1px solid var(--cc-sf-input-border, #E5E7EB);background:var(--cc-sf-input-bg, #FAFBFC)}.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}.single-select-wrapper{position:relative}.single-select-wrapper .single-select-trigger{min-height:var(--cc-sf-input-height, 44px);height:auto;-webkit-user-select:none;user-select:none}.single-select-wrapper .single-select-trigger.ss-open{border-color:var(--cc-sf-input-focus-border, #6366F1)!important;box-shadow:0 0 0 3px #6366f124,0 1px 4px #6366f11a!important}.single-select-wrapper .single-select-trigger .single-select-value{flex:1;color:var(--cc-sf-label-color, #111827);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.single-select-wrapper .single-select-trigger .single-select-placeholder{flex:1;font-weight:400;font-size:14px;color:var(--cc-sf-input-placeholder, #C4C9D4)}.single-select-wrapper .single-select-trigger .single-select-arrow{font-size:20px;width:20px;height:20px;line-height:20px;color:var(--cc-sf-label-color, #6B7280);flex-shrink:0}.single-select-wrapper .single-select-panel{position:absolute;top:calc(100% + 4px);left:0;right:0;background:var(--cc-sf-input-bg, #ffffff);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;display:flex;flex-direction:column;overflow:hidden;max-height:320px}.single-select-wrapper .single-select-options-list{flex:1;overflow-y:auto;max-height:240px}.single-select-wrapper .single-select-options-list::-webkit-scrollbar{width:4px}.single-select-wrapper .single-select-options-list::-webkit-scrollbar-thumb{background:var(--cc-sf-input-border, #D1D5DB);border-radius:2px}.single-select-wrapper .single-select-option{padding:8px 12px;border-bottom:1px solid var(--cc-sf-input-border, #F3F4F6);transition:background .1s;justify-content:space-between}.single-select-wrapper .single-select-option:last-of-type{border-bottom:none}.single-select-wrapper .single-select-option:hover{background:var(--cc-sf-input-hover-bg, #F9FAFB)}.single-select-wrapper .single-select-option.selected{background:var(--cc-sf-input-hover-bg, #F0F5FF);color:var(--cc-sf-selected-color, #6366F1)}.single-select-wrapper .single-select-option .single-select-check{font-size:18px;width:18px;height:18px;color:var(--cc-sf-selected-color, #6366F1)}.single-select-wrapper.is-invalid .single-select-trigger{border-color:var(--cc-sf-error-border, #EF4444)!important;background-color:var(--cc-sf-error-bg, #FFF5F5)!important}.multi-select-panel,.single-select-panel{width:100%;background:var(--cc-sf-input-bg, #ffffff);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;display:flex;flex-direction:column;overflow:hidden;max-height:320px}.multi-select-options-list,.single-select-options-list{flex:1;overflow-y:auto;max-height:240px}.multi-select-options-list::-webkit-scrollbar,.single-select-options-list::-webkit-scrollbar{width:4px}.multi-select-options-list::-webkit-scrollbar-thumb,.single-select-options-list::-webkit-scrollbar-thumb{background:var(--cc-sf-input-border, #D1D5DB);border-radius:2px}.multi-select-option,.single-select-option{display:flex;align-items:center;gap:8px;cursor:pointer;padding:8px 12px;border-bottom:1px solid var(--cc-sf-input-border, #F3F4F6);transition:background .1s}.multi-select-option:last-of-type,.single-select-option:last-of-type{border-bottom:none}.multi-select-option:hover,.single-select-option:hover{background:var(--cc-sf-input-hover-bg, #F9FAFB)}.multi-select-option input[type=checkbox],.single-select-option input[type=checkbox]{flex-shrink:0;cursor:pointer;accent-color:var(--cc-sf-selected-color, #6366F1);width:15px;height:15px}.multi-select-option.multi-select-all{border-bottom:1px solid var(--cc-sf-input-border, #E5E7EB);background:var(--cc-sf-input-bg, #FAFBFC)}.single-select-option{justify-content:space-between}.single-select-option.selected{background:var(--cc-sf-input-hover-bg, #F0F5FF);color:var(--cc-sf-selected-color, #6366F1)}.single-select-option .single-select-check{font-size:18px;width:18px;height:18px;color:var(--cc-sf-selected-color, #6366F1)}.multi-select-empty{padding:12px;text-align:center}::ng-deep .cdk-overlay-container{z-index:1100}.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)}.suffix-action-icons{right:.875rem;top:50%;transform:translateY(-50%);z-index:1;pointer-events:auto}.suffix-action-icons mat-icon.suffix-action-icon{font-size:1.125rem;width:1.125rem;height:1.125rem;line-height:1.125rem;color:var(--cc-sf-hint-color, #6B7280);cursor:pointer;transition:color .2s ease,transform .15s ease}.suffix-action-icons mat-icon.suffix-action-icon:hover{color:var(--cc-sf-text-color, #202124);transform:scale(1.15)}.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}.link-list-container{border:none!important;background-color:transparent!important;padding:0!important;box-shadow:none!important;overflow:visible!important}.link-items-list{list-style:none}@media(max-width:640px){.link-items-list{grid-template-columns:1fr!important}.link-items-list .link-list-item{grid-column:span 12!important}}.link-list-item{display:flex;align-items:center;justify-content:space-between;padding:10px 16px;background-color:var(--cc-sf-link-item-bg, #f9fafb)!important;border:1px solid var(--cc-sf-link-item-border, #E5E7EB)!important;border-radius:var(--cc-sf-input-radius, 9px)!important;gap:12px;min-height:48px}.link-list-item .link-url-text{font-size:.875rem;color:var(--cc-sf-link-item-text, #111827);word-break:break-all}.link-list-item .action-btn{border:none;background:transparent;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;padding:0;border-radius:6px;transition:background .15s ease,color .15s ease}.link-list-item .action-btn mat-icon{font-size:22px;width:22px;height:22px;line-height:22px;display:flex;align-items:center;justify-content:center}.link-list-item .action-btn.edit-btn{color:var(--cc-sf-link-edit-color, #4b5563)}.link-list-item .action-btn.edit-btn:hover{color:var(--cc-sf-accent-color, #6366F1);background-color:#f3f4f6}.link-list-item .action-btn.delete-btn{color:var(--cc-sf-link-delete-color, #ef4444)}.link-list-item .action-btn.delete-btn:hover{background-color:var(--cc-sf-error-bg, #FFF5F5)}.link-list-item .action-btn.save-btn{color:var(--cc-sf-link-save-color, #16a34a)!important}.link-list-item .action-btn.save-btn:hover{background-color:#ecfdf5}.link-list-item .action-btn.cancel-btn{color:#ef4444!important}.link-list-item .action-btn.cancel-btn:hover{background-color:var(--cc-sf-error-bg, #FFF5F5)}.pe-40px{padding-right:40px!important}.dropdown-loading-state{display:flex;align-items:center;justify-content:center;padding:16px;gap:8px}.dropdown-spinner{width:20px;height:20px;border:2px solid #E2E8F0;border-top-color:var(--cc-sf-accent-color, #3B82F6);border-radius:50%;animation:cc-spin .7s linear infinite}.mini-spinner{width:16px;height:16px;border:2px solid #E2E8F0;border-top-color:var(--cc-sf-accent-color, #3B82F6);border-radius:50%;animation:cc-spin .7s linear infinite}.native-select-wrapper{display:block;width:100%;position:relative}.native-select-wrapper select{width:100%}.native-select-wrapper .native-select-spinner{position:absolute;right:2.5rem;top:calc(50% - 7px);width:14px;height:14px;border:2px solid #CBD5E1;border-top-color:var(--cc-sf-accent-color, #3B82F6);border-radius:50%;animation:cc-spin .7s linear infinite;pointer-events:none}\n"] }]
4295
- }], ctorParameters: () => [{ type: i1$2.FormBuilder }, { type: ExpressionService }, { type: i3.HttpClient }, { type: i0.ElementRef }], propDecorators: { config: [{
4503
+ args: [{ selector: 'lib-form-field', standalone: false, template: "<div class=\"form-field mb-8px\" *ngIf=\"isVisible\" [class.has-error]=\"errorMessage\">\r\n\r\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 -->\r\n <div *ngIf=\"isRow\" class=\"form-row grid-row\">\r\n <ng-container *ngFor=\"let child of config.children; trackBy: trackByFieldName\">\r\n <div class=\"row-field\" [style.gridColumn]=\"'span ' + getChildColSpan(child)\" *ngIf=\"child.isEnabled !== false\"\r\n [style.display]=\"rowFieldRef.isVisible === false ? 'none' : null\">\r\n <lib-form-field #rowFieldRef [config]=\"child\" [controller]=\"controller\" [formGroup]=\"formGroup\" [allowMulti]=\"allowMulti\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isGroup && config.sectionConfig?.allowMulti\"\r\n class=\"group-section-wrapper mb-20px\"\r\n [class.multi-save-active]=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n\r\n <!-- Multi-Save: header row with label + top-right Add button -->\r\n <div class=\"multi-save-header d-flex justify-content-between align-items-center mb-24\"\r\n *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n <h3 class=\"group-label\" *ngIf=\"config.sectionConfig?.label\">{{ config.sectionConfig!.label }}</h3>\r\n <lib-button [variant]=\"'outline'\" [icon]=\"{type: 'material', value: 'add'}\" (click)=\"addGroupInstance()\"\r\n class=\"btn-add-multi\">\r\n {{ addMultiLabel }}\r\n </lib-button>\r\n </div>\r\n\r\n <!-- Standard heading (non-multiSave) -->\r\n <h3 class=\"group-label\"\r\n *ngIf=\"config.sectionConfig?.label && !config.sectionConfig?.multiSaveConfig?.active\">{{\r\n config.sectionConfig!.label }}</h3>\r\n\r\n <!-- \u2500\u2500 Standard (non-multiSave) repeater: accordion instances \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\r\n <ng-container *ngIf=\"!config.sectionConfig?.multiSaveConfig?.active\">\r\n <div *ngFor=\"let instance of instanceList; trackBy: trackByInstanceId; let i = index\"\r\n class=\"group-accordion-instance\"\r\n [class.is-expanded]=\"isGroupExpanded(i)\">\r\n\r\n <!-- Accordion header -->\r\n <div class=\"group-accordion-header\" (click)=\"toggleGroupAccordion(i)\"\r\n role=\"button\" [attr.aria-expanded]=\"isGroupExpanded(i)\">\r\n <div class=\"accordion-header-left d-flex align-items-center gap-10\">\r\n <span class=\"instance-badge\">{{ i + 1 }}</span>\r\n <span class=\"instance-title\">{{ config.sectionConfig!.label }} #{{ i + 1 }}</span>\r\n </div>\r\n <div class=\"accordion-header-right d-flex align-items-center gap-6\">\r\n <button type=\"button\" class=\"accordion-remove-btn\"\r\n *ngIf=\"instanceList.length > 1\"\r\n (click)=\"$event.stopPropagation(); removeGroupInstance(i)\"\r\n aria-label=\"Remove\">\r\n <mat-icon>delete_outline</mat-icon>\r\n </button>\r\n <mat-icon class=\"accordion-chevron\">\r\n {{ isGroupExpanded(i) ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n\r\n <!-- Accordion body (always mounted so form controls survive collapse) -->\r\n <div class=\"group-accordion-body\" [hidden]=\"!isGroupExpanded(i)\">\r\n <div class=\"group-fields sf-grid\">\r\n <ng-container *ngFor=\"let field of config.sectionConfig!.children\">\r\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\"\r\n *ngIf=\"field.isEnabled !== false\"\r\n [style.display]=\"groupFieldRef.isVisible === false ? 'none' : null\">\r\n <lib-form-field #groupFieldRef [config]=\"field\" [controller]=\"instance.rowController\" [formGroup]=\"instance.fg\"\r\n [allowMulti]=\"false\" [siblingFormArray]=\"groupFormArray\" [siblingIndex]=\"i\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Full-width dashed Add button -->\r\n <button type=\"button\" class=\"btn-add-group\" (click)=\"addGroupInstance()\">\r\n <mat-icon>add</mat-icon> {{ addLabel }} {{ config.sectionConfig!.label }}\r\n </button>\r\n </ng-container>\r\n\r\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 -->\r\n <ng-container *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n <div *ngFor=\"let instance of instanceList; trackBy: trackByInstanceId; let i = index\"\r\n class=\"group-instance position-relative mb-16 overflow-hidden\"\r\n [class.is-editing]=\"instance.isEditing\"\r\n [class.is-card]=\"!instance.isEditing\">\r\n\r\n <!-- Edit / new form view -->\r\n <div [hidden]=\"!instance.isEditing\">\r\n <div class=\"group-fields sf-grid\">\r\n <ng-container *ngFor=\"let field of config.sectionConfig!.children\">\r\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\"\r\n *ngIf=\"field.isEnabled !== false\"\r\n [style.display]=\"groupFieldRef.isVisible === false ? 'none' : null\">\r\n <lib-form-field #groupFieldRef [config]=\"field\" [controller]=\"instance.rowController\" [formGroup]=\"instance.fg\"\r\n [allowMulti]=\"false\" [siblingFormArray]=\"groupFormArray\" [siblingIndex]=\"i\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n\r\n <!-- Save / Cancel -->\r\n <div class=\"group-footer d-flex justify-content-end align-items-center gap-16 p-0-24\"\r\n *ngIf=\"config.sectionConfig?.multiSaveConfig?.active\">\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"multiSaveError\">{{ multiSaveError }}</span>\r\n <div class=\"footer-actions d-flex gap-12\">\r\n <lib-button [variant]=\"'outline'\" (click)=\"cancelGroupInstance(i)\">Cancel</lib-button>\r\n <lib-button [variant]=\"'primary'\" (click)=\"saveGroupInstance(i)\">Save</lib-button>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Card view (saved state) -->\r\n <ng-container *ngIf=\"!instance.isEditing\">\r\n <div class=\"card-view d-flex justify-content-between align-items-center p-18px-24px\"\r\n [class.is-expanded]=\"instance.isExpanded\">\r\n <div class=\"card-content flex-1 d-flex flex-column gap-4 overflow-hidden\">\r\n <span class=\"card-title font-weight-600 overflow-hidden fs-1rem c-111827\">{{\r\n instance.fg.get(config.sectionConfig!.multiSaveConfig!.summaryField || '')?.value\r\n || '\u2014' }}</span>\r\n </div>\r\n <div class=\"card-actions d-flex align-items-center gap-16 ml-20\">\r\n <mat-icon class=\"icon-delete\" (click)=\"removeGroupInstance(i, true)\">delete_outline</mat-icon>\r\n <mat-icon class=\"icon-edit\" (click)=\"editGroupInstance(i)\">edit_outline</mat-icon>\r\n <mat-icon class=\"icon-expand\" (click)=\"toggleExpandGroupInstance(i)\">\r\n {{ instance.isExpanded ? 'keyboard_arrow_up' : 'keyboard_arrow_down' }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </ng-container>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isGroup && config.sectionConfig && !config.sectionConfig.allowMulti\"\r\n class=\"group-section-wrapper mb-20px\">\r\n <h3 class=\"group-label\" *ngIf=\"config.sectionConfig.label\">{{ config.sectionConfig.label }}</h3>\r\n <div class=\"group-fields sf-grid\">\r\n <ng-container *ngFor=\"let field of config.sectionConfig.children\">\r\n <div class=\"sf-col\" [style.gridColumn]=\"'span ' + (field.colSpan || 12)\" *ngIf=\"field.isEnabled !== false\"\r\n [style.display]=\"singleGroupFieldRef.isVisible === false ? 'none' : null\">\r\n <lib-form-field #singleGroupFieldRef [config]=\"field\" [controller]=\"controller\" [formGroup]=\"groupFormGroup\" [allowMulti]=\"false\">\r\n </lib-form-field>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n\r\n\r\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 -->\r\n <div *ngIf=\"isTextField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <textarea *ngIf=\"config.subType === 'LONG'\" class=\"field-input textarea\" [placeholder]=\"config.placeholder || ''\"\r\n [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\" rows=\"4\">\r\n </textarea>\r\n\r\n <!-- Password input with show/hide toggle -->\r\n <div *ngIf=\"config.subType === 'PASSWORD'\" class=\"password-wrapper position-relative d-flex align-items-center\">\r\n <input [type]=\"showPassword ? 'text' : 'password'\" class=\"field-input password-input\"\r\n [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\r\n <button type=\"button\"\r\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\"\r\n (click)=\"showPassword = !showPassword\" tabindex=\"-1\"\r\n [attr.aria-label]=\"showPassword ? 'Hide password' : 'Show password'\">\r\n <mat-icon class=\"eye-icon\">{{ showPassword ? 'visibility' : 'visibility_off' }}</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <span class=\"input-prefix br-none\" *ngIf=\"config.prefix\">{{ config.prefix }}</span>\r\n\r\n <input *ngIf=\"config.subType !== 'LONG' && config.subType !== 'PASSWORD'\"\r\n [type]=\"config.subType === 'EMAIL' ? 'email' : config.subType === 'PHONE' ? 'tel' : 'text'\" class=\"field-input\"\r\n [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\"\r\n [readonly]=\"config.readonly\">\r\n\r\n <span class=\"input-suffix d-flex align-items-center font-weight-500\" *ngIf=\"config.suffix\">{{ config.suffix\r\n }}</span>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n\r\n <!-- Suffix Action Icons (clickable) -->\r\n <div class=\"suffix-action-icons position-absolute d-flex gap-8 align-items-center\"\r\n *ngIf=\"config.suffixActionIcons?.length && !config.readonly\">\r\n <mat-icon class=\"suffix-action-icon\"\r\n *ngFor=\"let action of config.suffixActionIcons\"\r\n [style.color]=\"action.color || null\"\r\n [title]=\"action.tooltip || ''\"\r\n (click)=\"onSuffixActionClick(action.actionId)\">\r\n {{ action.icon }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n <div class=\"char-count-hint font-poppins font-weight-400 text-14 text-right c-6B7280\" *ngIf=\"showCharCount\">\r\n {{ remainingCharacters }} characters remaining\r\n </div>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isNumberField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <span class=\"input-prefix br-none\" *ngIf=\"config.prefix\">{{ config.prefix }}</span>\r\n\r\n <input type=\"number\" class=\"field-input\" [placeholder]=\"config.placeholder || ''\" [formControlName]=\"config.name!\"\r\n [min]=\"config.numberConfig?.min ?? null\" [max]=\"config.numberConfig?.max ?? null\"\r\n [step]=\"config.numberConfig?.step || 1\" [class.is-invalid]=\"errorMessage\" [readonly]=\"config.readonly\">\r\n\r\n <span class=\"input-suffix d-flex align-items-center font-weight-500\" *ngIf=\"config.suffix\">{{ config.suffix\r\n }}</span>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n\r\n <!-- Suffix Action Icons (clickable) -->\r\n <div class=\"suffix-action-icons position-absolute d-flex gap-8 align-items-center\"\r\n *ngIf=\"config.suffixActionIcons?.length && !config.readonly\">\r\n <mat-icon class=\"suffix-action-icon\"\r\n *ngFor=\"let action of config.suffixActionIcons\"\r\n [style.color]=\"action.color || null\"\r\n [title]=\"action.tooltip || ''\"\r\n (click)=\"onSuffixActionClick(action.actionId)\">\r\n {{ action.icon }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isDateField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <input matInput [matDatepicker]=\"datePicker\" class=\"field-input date-input has-icon-right\"\r\n [formControlName]=\"config.name!\" [min]=\"effectiveMinDate\" [max]=\"config.dateConfig?.maxDate\"\r\n [class.is-invalid]=\"errorMessage\" [placeholder]=\"config.placeholder || ''\"\r\n [readonly]=\"config.readonly || config.dateConfig?.inputReadonly\"\r\n (click)=\"!config.readonly && datePicker.open()\">\r\n <div class=\"date-icon-wrapper position-absolute d-flex align-items-center justify-content-center\"\r\n *ngIf=\"!config.readonly\">\r\n <mat-datepicker-toggle matSuffix [for]=\"datePicker\"></mat-datepicker-toggle>\r\n </div>\r\n <mat-datepicker #datePicker></mat-datepicker>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isTimeField\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <!-- DEFAULT variant: existing native input \u2014 unchanged -->\r\n <ng-container *ngIf=\"!isWheelVariant\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"input-group position-relative d-flex w-100\" [class.readonly]=\"config.readonly\">\r\n <input type=\"time\" class=\"field-input time-input\" [formControlName]=\"config.name!\"\r\n [min]=\"effectiveMinTime\" [max]=\"effectiveMaxTime\"\r\n [class.is-invalid]=\"errorMessage\" [readonly]=\"config.readonly || config.timeConfig?.inputReadonly\">\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- WHEEL variant: drum-roll picker -->\r\n <lib-time-picker\r\n *ngIf=\"isWheelVariant\"\r\n [formControlName]=\"config.name!\"\r\n [label]=\"config.label || ''\"\r\n [placeholder]=\"config.timeConfig?.placeholder || ''\"\r\n [required]=\"config.required || false\"\r\n [disabled]=\"config.readonly || false\"\r\n [mode]=\"config.timeConfig?.mode || '12'\"\r\n [minuteStep]=\"config.timeConfig?.minuteStep || 1\"\r\n [minTime]=\"effectiveMinTime || undefined\"\r\n [maxTime]=\"config.timeConfig?.maxTime\"\r\n [errorMessage]=\"errorMessage\"\r\n [labels]=\"timePickerLabels\">\r\n </lib-time-picker>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isAutocomplete\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <!-- Hidden real control (stores the code value) -->\r\n <input type=\"hidden\" [formControlName]=\"config.name!\">\r\n\r\n <div class=\"autocomplete-wrapper position-relative d-flex align-items-center w-100\"\r\n [class.is-invalid]=\"errorMessage\" [class.readonly]=\"config.readonly\">\r\n <!-- Search icon -->\r\n <mat-icon class=\"ac-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">search</mat-icon>\r\n\r\n <input class=\"field-input ac-input\" [formControl]=\"autocompleteInputCtrl\" [matAutocomplete]=\"auto\"\r\n [placeholder]=\"config.placeholder || 'Search\u2026'\" [readonly]=\"!!config.readonly\" [class.is-invalid]=\"errorMessage\"\r\n (blur)=\"onAutocompleteClear()\" autocomplete=\"off\">\r\n\r\n <!-- Clear button -->\r\n <button type=\"button\"\r\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\"\r\n *ngIf=\"autocompleteInputCtrl.value && !config.readonly\"\r\n (click)=\"autocompleteInputCtrl.setValue(''); updateValue(null)\" tabindex=\"-1\" aria-label=\"Clear\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n\r\n <mat-autocomplete #auto=\"matAutocomplete\" [panelWidth]=\"'auto'\">\r\n <mat-option *ngFor=\"let option of filteredOptions\" [value]=\"option.label\"\r\n (onSelectionChange)=\"onAutocompleteSelected(option)\">\r\n <span class=\"ac-option-label\">{{ option.label }}</span>\r\n\r\n <!-- Dynamic display fields (image / email / phone / text) -->\r\n <div class=\"ac-display-fields d-flex flex-wrap gap-6 mt-2\" *ngIf=\"option['displayMeta']?.length\">\r\n <ng-container *ngFor=\"let field of option['displayMeta']\">\r\n\r\n <!-- Image avatar -->\r\n <span *ngIf=\"field.type === 'image' && field.value\" class=\"ac-df-item ac-df-image\">\r\n <img [src]=\"field.value\" [alt]=\"field.label || 'image'\" class=\"ac-df-avatar\">\r\n </span>\r\n\r\n <!-- Email -->\r\n <span *ngIf=\"field.type === 'email' && field.value\" class=\"ac-df-item ac-df-chip\">\r\n <mat-icon class=\"ac-df-icon\">mail_outline</mat-icon>\r\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\r\n {{ field.value }}\r\n </span>\r\n\r\n <!-- Phone -->\r\n <span *ngIf=\"field.type === 'phone' && field.value\" class=\"ac-df-item ac-df-chip\" [class]=\"field.className\">\r\n <mat-icon class=\"ac-df-icon\">phone</mat-icon>\r\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\r\n {{ field.value }}\r\n </span>\r\n\r\n <!-- Custom / Icon-based / Generic Text -->\r\n <span *ngIf=\"field.type !== 'image' && field.type !== 'email' && field.type !== 'phone' && field.value\" \r\n class=\"ac-df-item\" [class.ac-df-chip]=\"!!field.icon\" [class]=\"field.className\">\r\n <mat-icon class=\"ac-df-icon\" *ngIf=\"field.icon\">{{ field.icon }}</mat-icon>\r\n <span *ngIf=\"field.label\" class=\"ac-df-label\">{{ field.label }}</span>\r\n {{ field.value }}\r\n </span>\r\n\r\n </ng-container>\r\n </div>\r\n </mat-option>\r\n <mat-option *ngIf=\"filteredOptions.length === 0\" disabled class=\"ac-no-results fs-0-8125rem c-6B7280\">\r\n No results found\r\n </mat-option>\r\n </mat-autocomplete>\r\n\r\n <div class=\"readonly-icons position-absolute d-flex gap-8 pe-none\" *ngIf=\"config.readonly\">\r\n <mat-icon class=\"lock-icon\">lock</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isDropdown\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <!-- SINGLE SELECT: native select (default, non-searchable, non-paginated) -->\r\n <div *ngIf=\"config.subType === 'SINGLE' && !isCustomSingleDropdown\" class=\"native-select-wrapper\">\r\n <select class=\"field-input\" [formControlName]=\"config.name!\"\r\n [class.is-invalid]=\"errorMessage\" [disabled]=\"optionsLoading || config.disabled\">\r\n <option [ngValue]=\"null\" disabled selected>{{ optionsLoading ? (controller.labels['LBL_LOADING'] || 'Loading...') : (config.placeholder || selectPlaceholderLabel) }}</option>\r\n <option *ngFor=\"let option of displayLocalOptionList\" [value]=\"option.code\">\r\n {{ option.label }}\r\n </option>\r\n </select>\r\n <div *ngIf=\"optionsLoading\" class=\"native-select-spinner\" aria-hidden=\"true\"></div>\r\n </div>\r\n\r\n <!-- SINGLE SELECT: custom dropdown panel (searchable or paginated) -->\r\n <div *ngIf=\"config.subType === 'SINGLE' && isCustomSingleDropdown\" class=\"single-select-wrapper\"\r\n [class.is-invalid]=\"errorMessage\">\r\n\r\n <div #singleTrigger cdkOverlayOrigin #singleOrigin=\"cdkOverlayOrigin\"\r\n class=\"field-input single-select-trigger d-flex align-items-center justify-content-between cursor-pointer\"\r\n [class.ss-open]=\"isSingleDropdownOpen\"\r\n (click)=\"!optionsLoading && toggleSingleDropdown($event)\">\r\n <span *ngIf=\"singleSelectedLabel && !optionsLoading\" class=\"single-select-value fs-0-9rem\">\r\n {{ singleSelectedLabel }}\r\n </span>\r\n <span *ngIf=\"!singleSelectedLabel || optionsLoading\" class=\"single-select-placeholder\">\r\n {{ optionsLoading ? (controller.labels['LBL_LOADING'] || 'Loading...') : (config.placeholder || selectPlaceholderLabel) }}\r\n </span>\r\n <div class=\"d-flex align-items-center\">\r\n <div *ngIf=\"optionsLoading\" class=\"mini-spinner me-2\" role=\"progressbar\" aria-label=\"Loading options\"></div>\r\n <mat-icon class=\"single-select-arrow\">\r\n {{ isSingleDropdownOpen ? expandLessLabel : expandMoreLabel }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n\r\n <ng-template cdkConnectedOverlay\r\n [cdkConnectedOverlayOrigin]=\"singleOrigin\"\r\n [cdkConnectedOverlayOpen]=\"isSingleDropdownOpen\"\r\n [cdkConnectedOverlayPositions]=\"dropdownOverlayPositions\"\r\n [cdkConnectedOverlayScrollStrategy]=\"dropdownScrollStrategy\"\r\n [cdkConnectedOverlayWidth]=\"singleTrigger.offsetWidth\"\r\n [cdkConnectedOverlayViewportMargin]=\"8\"\r\n [cdkConnectedOverlayPush]=\"true\"\r\n (overlayOutsideClick)=\"onDropdownOverlayOutsideClick($event, singleTrigger)\"\r\n (detach)=\"isSingleDropdownOpen = false\">\r\n <div class=\"single-select-panel\"\r\n (click)=\"$event.stopPropagation()\">\r\n\r\n <!-- Search input (shown only when searchConfig.enabled) -->\r\n <div class=\"dropdown-search-wrapper\" *ngIf=\"isSearchableDropdown\">\r\n <mat-icon class=\"dropdown-search-icon\">search</mat-icon>\r\n <input type=\"text\" class=\"dropdown-search-input\"\r\n [placeholder]=\"searchPlaceholderLabel\"\r\n [(ngModel)]=\"searchText\" [ngModelOptions]=\"{standalone: true}\"\r\n (input)=\"dropdownSearch$.next(searchText)\"\r\n (click)=\"$event.stopPropagation()\">\r\n <mat-icon *ngIf=\"searchText\" class=\"dropdown-search-clear cursor-pointer\"\r\n (click)=\"searchText = ''; dropdownSearch$.next(''); $event.stopPropagation()\">close</mat-icon>\r\n </div>\r\n\r\n <!-- Options list -->\r\n <div class=\"single-select-options-list\" (scroll)=\"onDropdownScroll($event)\">\r\n <div *ngIf=\"optionsLoading\" class=\"dropdown-loading-state\">\r\n <div class=\"dropdown-spinner\"></div>\r\n <span class=\"fs-0-875rem c-6B7280\">{{ controller.labels['LBL_LOADING'] || 'Loading...' }}</span>\r\n </div>\r\n\r\n <ng-container *ngIf=\"!optionsLoading\">\r\n <div *ngFor=\"let option of displayOptionList\"\r\n class=\"single-select-option d-flex align-items-center gap-8 cursor-pointer\"\r\n [class.selected]=\"formGroup.get(config.name!)?.value === option.code\"\r\n (click)=\"selectSingleOption(option)\">\r\n <span class=\"fs-0-875rem c-111827\">{{ option.label }}</span>\r\n <mat-icon *ngIf=\"formGroup.get(config.name!)?.value === option.code\" class=\"single-select-check\">check</mat-icon>\r\n </div>\r\n <div *ngIf=\"!filteredOptionList?.length && localOptionList?.length\"\r\n class=\"multi-select-empty fs-0-875rem c-6B7280\">\r\n {{ noMatchingOptionsLabel }}\r\n </div>\r\n <div *ngIf=\"!localOptionList?.length\"\r\n class=\"multi-select-empty fs-0-875rem c-6B7280\">\r\n {{ noOptionsAvailableLabel }}\r\n </div>\r\n\r\n <!-- Load More button / Pagination Spinner -->\r\n <div *ngIf=\"config.optionConfig?.pagination?.enabled && ((hasMoreDropdownPages && config.optionConfig?.pagination?.mode !== 'infiniteScroll') || isDropdownLoadingMore)\" class=\"dropdown-pagination-footer\">\r\n <button type=\"button\" class=\"btn-dropdown-load-more\"\r\n *ngIf=\"hasMoreDropdownPages && config.optionConfig?.pagination?.mode !== 'infiniteScroll' && !isDropdownLoadingMore\"\r\n (click)=\"loadMoreDropdownOptions(); $event.stopPropagation()\">\r\n {{ controller.labels['LBL_LOAD_MORE'] || 'Load More' }}\r\n </button>\r\n <div *ngIf=\"isDropdownLoadingMore\" class=\"dropdown-loading-more-state\">\r\n <div class=\"dropdown-spinner-small\"></div>\r\n <span class=\"fs-0-8125rem c-6B7280\">{{ controller.labels['LBL_LOADING_MORE'] || 'Loading more...' }}</span>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </ng-template>\r\n </div>\r\n\r\n <!-- MULTIPLE SELECT: custom panel with checkboxes -->\r\n <div *ngIf=\"config.subType === 'MULTIPLE'\" class=\"multi-select-wrapper\"\r\n [class.is-invalid]=\"errorMessage\">\r\n\r\n <div #multiTrigger cdkOverlayOrigin #multiOrigin=\"cdkOverlayOrigin\"\r\n class=\"field-input multi-select-trigger d-flex align-items-center justify-content-between cursor-pointer\"\r\n [class.ms-open]=\"isMultiDropdownOpen\"\r\n (click)=\"!optionsLoading && toggleMultiDropdown($event)\">\r\n <span *ngIf=\"multiSelectedCount > 0 && !optionsLoading\" class=\"multi-select-value fs-0-9rem\">\r\n {{ multiSelectedCount }} selected\r\n </span>\r\n <span *ngIf=\"multiSelectedCount === 0 || optionsLoading\" class=\"multi-select-placeholder\">\r\n {{ optionsLoading ? (controller.labels['LBL_LOADING'] || 'Loading...') : (config.placeholder || selectPlaceholderLabel) }}\r\n </span>\r\n <div class=\"d-flex align-items-center\">\r\n <div *ngIf=\"optionsLoading\" class=\"mini-spinner me-2\" role=\"progressbar\" aria-label=\"Loading options\"></div>\r\n <mat-icon class=\"multi-select-arrow\">\r\n {{ isMultiDropdownOpen ? expandLessLabel : expandMoreLabel }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n\r\n <ng-template cdkConnectedOverlay\r\n [cdkConnectedOverlayOrigin]=\"multiOrigin\"\r\n [cdkConnectedOverlayOpen]=\"isMultiDropdownOpen\"\r\n [cdkConnectedOverlayPositions]=\"dropdownOverlayPositions\"\r\n [cdkConnectedOverlayScrollStrategy]=\"dropdownScrollStrategy\"\r\n [cdkConnectedOverlayWidth]=\"multiTrigger.offsetWidth\"\r\n [cdkConnectedOverlayViewportMargin]=\"8\"\r\n [cdkConnectedOverlayPush]=\"true\"\r\n (overlayOutsideClick)=\"onDropdownOverlayOutsideClick($event, multiTrigger)\"\r\n (detach)=\"isMultiDropdownOpen = false\">\r\n <div class=\"multi-select-panel\"\r\n (click)=\"$event.stopPropagation()\">\r\n\r\n <!-- Search input (shown only when searchConfig.enabled) -->\r\n <div class=\"dropdown-search-wrapper\" *ngIf=\"isSearchableDropdown\">\r\n <mat-icon class=\"dropdown-search-icon\">search</mat-icon>\r\n <input type=\"text\" class=\"dropdown-search-input\"\r\n [placeholder]=\"searchPlaceholderLabel\"\r\n [(ngModel)]=\"searchText\" [ngModelOptions]=\"{standalone: true}\"\r\n (input)=\"dropdownSearch$.next(searchText)\"\r\n (click)=\"$event.stopPropagation()\">\r\n <mat-icon *ngIf=\"searchText\" class=\"dropdown-search-clear cursor-pointer\"\r\n (click)=\"searchText = ''; dropdownSearch$.next(''); $event.stopPropagation()\">close</mat-icon>\r\n </div>\r\n\r\n <!-- Select All checkbox (shown only when showSelectAll is true) -->\r\n <label *ngIf=\"config.optionConfig?.showSelectAll && filteredOptionList?.length\"\r\n class=\"multi-select-option multi-select-all d-flex align-items-center gap-8 cursor-pointer\">\r\n <input type=\"checkbox\"\r\n [checked]=\"isAllSelected()\"\r\n [indeterminate]=\"isSomeSelected()\"\r\n [disabled]=\"!!config.disabled\"\r\n (change)=\"toggleSelectAll($any($event.target).checked)\">\r\n <span class=\"fs-0-875rem c-111827 fw-600\">{{ selectAllLabel }}</span>\r\n </label>\r\n\r\n <!-- Options list -->\r\n <div class=\"multi-select-options-list\" (scroll)=\"onDropdownScroll($event)\">\r\n <div *ngIf=\"optionsLoading\" class=\"dropdown-loading-state\">\r\n <div class=\"dropdown-spinner\"></div>\r\n <span class=\"fs-0-875rem c-6B7280\">{{ controller.labels['LBL_LOADING'] || 'Loading...' }}</span>\r\n </div>\r\n\r\n <ng-container *ngIf=\"!optionsLoading\">\r\n <label *ngFor=\"let option of filteredOptionList\"\r\n class=\"multi-select-option d-flex align-items-center gap-8 cursor-pointer\">\r\n <input type=\"checkbox\"\r\n [checked]=\"isChecked(option.code)\"\r\n [disabled]=\"!!config.disabled\"\r\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\">\r\n <span class=\"fs-0-875rem c-111827\">{{ option.label }}</span>\r\n </label>\r\n <div *ngIf=\"!filteredOptionList?.length && localOptionList?.length\"\r\n class=\"multi-select-empty fs-0-875rem c-6B7280\">\r\n {{ noMatchingOptionsLabel }}\r\n </div>\r\n <div *ngIf=\"!localOptionList?.length\"\r\n class=\"multi-select-empty fs-0-875rem c-6B7280\">\r\n {{ noOptionsAvailableLabel }}\r\n </div>\r\n\r\n <!-- Load More button / Pagination Spinner -->\r\n <div *ngIf=\"config.optionConfig?.pagination?.enabled && ((hasMoreDropdownPages && config.optionConfig?.pagination?.mode !== 'infiniteScroll') || isDropdownLoadingMore)\" class=\"dropdown-pagination-footer\">\r\n <button type=\"button\" class=\"btn-dropdown-load-more\"\r\n *ngIf=\"hasMoreDropdownPages && config.optionConfig?.pagination?.mode !== 'infiniteScroll' && !isDropdownLoadingMore\"\r\n (click)=\"loadMoreDropdownOptions(); $event.stopPropagation()\">\r\n {{ controller.labels['LBL_LOAD_MORE'] || 'Load More' }}\r\n </button>\r\n <div *ngIf=\"isDropdownLoadingMore\" class=\"dropdown-loading-more-state\">\r\n <div class=\"dropdown-spinner-small\"></div>\r\n <span class=\"fs-0-8125rem c-6B7280\">{{ controller.labels['LBL_LOADING_MORE'] || 'Loading more...' }}</span>\r\n </div>\r\n </div>\r\n </ng-container>\r\n </div>\r\n </div>\r\n </ng-template>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isRadio\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"radio-group\" [class.is-invalid]=\"errorMessage\"\r\n [class]=\"config.optionConfig?.layout ? 'layout-' + config.optionConfig!.layout.toLowerCase() : ''\">\r\n <label *ngFor=\"let option of localOptionList\" class=\"radio-label\"\r\n [class.card-item]=\"config.subType === 'CARD'\"\r\n [class.selected]=\"formGroup.get(config.name!)?.value === option.code\"\r\n [style.gridColumn]=\"config.optionConfig?.layout?.toUpperCase() === 'COLUMN' ? 'span ' + getOptionColSpan(option) : null\">\r\n <input type=\"radio\" [formControlName]=\"config.name!\" [value]=\"option.code\">\r\n <div class=\"option-content d-flex flex-column gap-4 flex-1 text-left\">\r\n <span class=\"label-text text-16 c-1F2937\">{{ option.label }}</span>\r\n <span class=\"option-hint text-13 color-gray\" *ngIf=\"option.hint\">{{ option.hint }}</span>\r\n </div>\r\n </label>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isCheckbox\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label && config.subType === 'LIST'\"\r\n class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div *ngIf=\"config.subType === 'BOOL'\" class=\"checkbox-single\">\r\n <label class=\"checkbox-label d-flex align-items-center gap-8 cursor-pointer fs-0-875rem c-111827\">\r\n <input type=\"checkbox\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\r\n <span>{{ config.label }}</span>\r\n </label>\r\n </div>\r\n\r\n <div *ngIf=\"config.subType === 'LIST' || config.subType === 'CARD'\" class=\"checkbox-group d-flex flex-column gap-8\"\r\n [class.is-invalid]=\"errorMessage\"\r\n [class]=\"config.optionConfig?.layout ? 'layout-' + config.optionConfig!.layout.toLowerCase() : ''\">\r\n <label *ngFor=\"let option of localOptionList\"\r\n class=\"checkbox-label d-flex align-items-center gap-8 cursor-pointer fs-0-875rem c-111827\"\r\n [class.card-item]=\"config.subType === 'CARD'\" [class.selected]=\"isChecked(option.code)\"\r\n [style.gridColumn]=\"config.optionConfig?.layout?.toUpperCase() === 'COLUMN' ? 'span ' + getOptionColSpan(option) : null\">\r\n <input type=\"checkbox\" [checked]=\"isChecked(option.code)\" [disabled]=\"!!config.disabled\"\r\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\">\r\n <div class=\"option-content d-flex flex-column gap-4 flex-1 text-left\">\r\n <span class=\"label-text text-16 c-1F2937\">{{ option.label }}</span>\r\n <span class=\"option-hint text-13 color-gray\" *ngIf=\"option.hint\">{{ option.hint }}</span>\r\n </div>\r\n </label>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isChip\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"chip-group d-flex flex-wrap gap-8\" [class.is-invalid]=\"errorMessage\">\r\n <label *ngFor=\"let option of localOptionList\"\r\n class=\"chip-label cursor-pointer p-6px-14px b-ffffff c-374151 b-1px-solid-D1D5DB br-20px fs-0-875rem\"\r\n [class.selected]=\"isChecked(option.code)\">\r\n <input type=\"checkbox\" [checked]=\"isChecked(option.code)\" [disabled]=\"!!config.disabled\"\r\n (change)=\"onCheckboxListChange(option.code, $any($event.target).checked)\" hidden>\r\n <span>{{ option.label }}</span>\r\n </label>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isSwitch\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label class=\"switch-container d-flex justify-content-between align-items-center cursor-pointer\">\r\n <span class=\"field-label d-block font-poppins c-202124 fs-18px\">{{ config.label }}</span>\r\n <div class=\"switch position-relative\">\r\n <input type=\"checkbox\" [formControlName]=\"config.name!\" [class.is-invalid]=\"errorMessage\">\r\n <span class=\"slider position-absolute cursor-pointer\"></span>\r\n </div>\r\n </label>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isRichText\" class=\"field-wrapper component-rich-text d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"rich-text-container\" [class.is-invalid]=\"errorMessage\">\r\n <quill-editor [formControlName]=\"config.name!\" class=\"rich-text-editor d-block w-100\"\r\n [placeholder]=\"config.richTextConfig?.placeholder || config.placeholder || ''\"\r\n [styles]=\"{height: config.richTextConfig?.height || '200px'}\"\r\n [modules]=\"richTextModules\">\r\n </quill-editor>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n <div class=\"char-count-hint font-poppins font-weight-400 text-14 text-right c-6B7280\" *ngIf=\"showCharCount\">\r\n {{ remainingCharacters }} characters remaining\r\n </div>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isRating\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <div class=\"rating-group d-flex gap-4\" [class.is-invalid]=\"errorMessage\">\r\n <span *ngFor=\"let star of getStarArray()\" class=\"star d-inline-flex align-items-center cursor-pointer\"\r\n [class.filled]=\"isStarFilled(star)\" [class.half]=\"isStarHalf(star)\" (click)=\"onRatingChange(star, $event)\">\r\n <mat-icon>{{ isStarFilled(star) || isStarHalf(star) ? 'star' : 'star_border' }}</mat-icon>\r\n </span>\r\n </div>\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isGenerated\" class=\"field-wrapper d-flex flex-column gap-6\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">{{ config.label\r\n }}</label>\r\n <div class=\"generated-value fs-0-875rem p-0-625rem-0-875rem b-F3F4F6 b-1px-solid-E5E7EB br-8px c-6B7280\">{{ value ||\r\n '-' }}</div>\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint\">{{ config.hint }}</span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isFileUpload\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <!-- Drop Zone -->\r\n <div\r\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\"\r\n [class.drag-over]=\"isDragOver\" [class.has-files]=\"value?.length\" [class.is-invalid]=\"errorMessage\"\r\n (dragover)=\"onDragOver($event)\" (dragleave)=\"onDragLeave($event)\" (drop)=\"onFileDrop($event)\"\r\n (click)=\"fileInput.click()\">\r\n\r\n <!-- Icon with accent-colour pill background -->\r\n <div class=\"upload-icon-wrap mb-4\">\r\n <div class=\"dropzone-icon-pill d-flex align-items-center justify-content-center\">\r\n <mat-icon class=\"upload-cloud-icon\">cloud_upload</mat-icon>\r\n </div>\r\n </div>\r\n\r\n <p class=\"upload-main-text font-weight-600 m-0 fs-0-9rem\">Drag &amp; drop files here</p>\r\n <p class=\"upload-sub-text m-0 fs-0-8rem c-64748B\">or <span class=\"upload-link\">Browse files</span></p>\r\n <p class=\"upload-hint-text m-0 fs-0-78rem c-64748B\" *ngIf=\"config.attachmentConfig?.acceptLabel\">\r\n Supported: <span class=\"upload-formats font-weight-500\">{{ config.attachmentConfig!.acceptLabel }}</span>\r\n </p>\r\n <p class=\"upload-hint-text m-0 fs-0-78rem c-64748B\" *ngIf=\"!config.attachmentConfig?.acceptLabel && config.hint\">\r\n {{ config.hint }}\r\n </p>\r\n <span class=\"upload-size-badge fs-0-72rem\" *ngIf=\"config.attachmentConfig?.maxSizeMB\">\r\n Max {{ config.attachmentConfig!.maxSizeMB }}MB\r\n </span>\r\n\r\n <!-- Hidden native file input -->\r\n <input #fileInput type=\"file\" hidden [attr.multiple]=\"config.attachmentConfig?.multiple ? true : null\"\r\n [attr.accept]=\"config.attachmentConfig?.accept || null\" (change)=\"onFileSelected($event)\">\r\n </div>\r\n\r\n <!-- Uploaded file list -->\r\n <div class=\"uploaded-list d-flex flex-column gap-8 mt-10\" *ngIf=\"value?.length\">\r\n <div *ngFor=\"let f of value; let i = index\"\r\n class=\"uploaded-item d-flex align-items-center gap-10 p-10px-14px br-8px\"\r\n [class.uploading]=\"f.isUploading\">\r\n\r\n <!-- Uploading spinner -->\r\n <ng-container *ngIf=\"f.isUploading; else fileReady\">\r\n <div class=\"upload-spinner rounded-50 b-2px-solid-E2E8F0\"></div>\r\n <div class=\"file-info flex-1 d-flex flex-column\">\r\n <span class=\"file-name font-weight-500 overflow-hidden fs-0-85rem\" [title]=\"f.name\">{{ f.name }}</span>\r\n <span class=\"file-size uploading-label fs-0-72rem\">Uploading...</span>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- Normal state once upload is done -->\r\n <ng-template #fileReady>\r\n <mat-icon class=\"file-type-icon\">{{ getFileIcon(f.type) }}</mat-icon>\r\n <img *ngIf=\"f.type?.startsWith('image') && f.dataUrl\" [src]=\"f.dataUrl\" class=\"file-thumb rounded-4\"\r\n alt=\"preview\">\r\n <div class=\"file-info flex-1 d-flex flex-column\">\r\n <span class=\"file-name font-weight-500 overflow-hidden fs-0-85rem\" [title]=\"f.name\">{{ f.name }}</span>\r\n <span class=\"file-size fs-0-72rem\">{{ formatFileSize(f.size) }}</span>\r\n </div>\r\n </ng-template>\r\n\r\n <!-- Compact icon-only remove button -->\r\n <button type=\"button\" class=\"file-remove-btn d-flex align-items-center justify-content-center rounded-50\"\r\n [disabled]=\"f.isUploading\" (click)=\"!f.isUploading && removeUploadedFile(i)\"\r\n aria-label=\"Remove file\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Validation / file errors -->\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"fileUploadError\">{{ fileUploadError }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage && !fileUploadError\">{{ errorMessage }}</span>\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\"\r\n *ngIf=\"config.hint && !errorMessage && !fileUploadError && !config.attachmentConfig?.acceptLabel\">\r\n {{ config.hint }}\r\n </span>\r\n </div>\r\n\r\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 -->\r\n <div *ngIf=\"isMediaUpload\" class=\"field-wrapper media-upload-wrapper d-flex flex-column gap-6 p-0 border-none b-none\"\r\n [formGroup]=\"formGroup\">\r\n\r\n <!-- Hidden file input lives outside *ngIf \u2014 triggered via ViewChild -->\r\n <input #mediaDeviceInput type=\"file\" hidden multiple accept=\"image/*\" (change)=\"onMediaFileSelected($event)\">\r\n\r\n <!-- Two-column layout -->\r\n <div class=\"mu-layout d-grid align-items-start\">\r\n\r\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 -->\r\n <div class=\"mu-left d-flex flex-column gap-20\">\r\n\r\n <!-- Header: title + max-items badge -->\r\n <div class=\"mu-header d-flex align-items-start flex-wrap gap-10\">\r\n <h3 class=\"mu-title m-0 c-111827 fs-1-35rem\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </h3>\r\n <span\r\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\"\r\n *ngIf=\"config.attachmentConfig?.maxFiles\">\r\n {{ controller.labels['LBL_MEDIA_MAX_PREFIX'] || 'Maximum' }}\r\n {{ config.attachmentConfig?.maxFiles }}\r\n {{ controller.labels['LBL_MEDIA_MAX_SUFFIX'] || 'Image & Videos' }}\r\n </span>\r\n </div>\r\n\r\n <!-- Description -->\r\n <p class=\"mu-description m-0 fs-0-875rem c-6B7280\" *ngIf=\"config.attachmentConfig?.description\">\r\n {{ config.attachmentConfig?.description }}\r\n </p>\r\n <p class=\"mu-description m-0 fs-0-875rem c-6B7280\"\r\n *ngIf=\"!config.attachmentConfig?.description && controller.labels['LBL_MEDIA_DESC']\">\r\n {{ controller.labels['LBL_MEDIA_DESC'] }}\r\n </p>\r\n\r\n <!-- Feature bullet list -->\r\n <ul class=\"mu-features m-0 p-0 d-flex flex-column gap-8 ls-none\"\r\n *ngIf=\"config.attachmentConfig?.features?.length || controller.labels['LBL_MEDIA_FEATURE_1']\">\r\n <ng-container *ngIf=\"config.attachmentConfig?.features?.length\">\r\n <li *ngFor=\"let f of config.attachmentConfig?.features\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ f }}\r\n </li>\r\n </ng-container>\r\n <ng-container *ngIf=\"!config.attachmentConfig?.features?.length\">\r\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_1']\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_1'] }}\r\n </li>\r\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_2']\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_2'] }}\r\n </li>\r\n <li *ngIf=\"controller.labels['LBL_MEDIA_FEATURE_3']\"\r\n class=\"mu-feature-item d-flex align-items-center gap-8 fs-0-875rem c-374151\">\r\n <mat-icon class=\"mu-check text-16 c-3B82F6\">check</mat-icon>{{ controller.labels['LBL_MEDIA_FEATURE_3'] }}\r\n </li>\r\n </ng-container>\r\n </ul>\r\n\r\n <!-- Backdrop to close dropdown on outside click -->\r\n <div class=\"media-menu-backdrop\" *ngIf=\"showMediaMenu\"\r\n (click)=\"$event.stopPropagation(); showMediaMenu = false\"></div>\r\n\r\n <!-- Add Media button + dropdown -->\r\n <div class=\"media-add-container position-relative\" (click)=\"showMediaMenu = !showMediaMenu\">\r\n <lib-button id=\"btn-add-media-{{ config.name }}\" [variant]=\"'warning'\"\r\n [icon]=\"{type: 'material', value: 'add_photo_alternate'}\">\r\n {{ controller.labels['LBL_ADD_MEDIA'] || 'Add media' }}\r\n <mat-icon class=\"menu-chevron fs-18px\">add</mat-icon>\r\n </lib-button>\r\n\r\n <div class=\"media-dropdown position-absolute rounded-12 overflow-hidden b-ffffff b-1px-solid-E5E7EB\"\r\n *ngIf=\"showMediaMenu\" role=\"menu\" (click)=\"$event.stopPropagation()\">\r\n <!-- Video -->\r\n <button id=\"btn-media-video-{{ config.name }}\" type=\"button\"\r\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\"\r\n (click)=\"onMediaMenuVideo(); showMediaMenu = false\" role=\"menuitem\">\r\n <span\r\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>\r\n <span class=\"media-drop-text d-flex flex-column flex-1\">\r\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\r\n controller.labels['LBL_MEDIA_VIDEO'] || 'Video' }}</span>\r\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_VIDEO_DESC'] || 'Add\r\n YouTube URL'\r\n }}</span>\r\n </span>\r\n </button>\r\n <!-- Device -->\r\n <button id=\"btn-media-device-{{ config.name }}\" type=\"button\"\r\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\"\r\n (click)=\"onMediaMenuDevice(); showMediaMenu = false\" role=\"menuitem\">\r\n <span\r\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>\r\n <span class=\"media-drop-text d-flex flex-column flex-1\">\r\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\r\n controller.labels['LBL_MEDIA_DEVICE'] || 'Upload from device'\r\n }}</span>\r\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_DEVICE_DESC'] ||\r\n 'Select images from your\r\n computer' }}</span>\r\n </span>\r\n </button>\r\n <!-- Library -->\r\n <button id=\"btn-media-library-{{ config.name }}\" type=\"button\"\r\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\"\r\n (click)=\"onMediaMenuLibrary(); showMediaMenu = false\" role=\"menuitem\">\r\n <span\r\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>\r\n <span class=\"media-drop-text d-flex flex-column flex-1\">\r\n <span class=\"media-drop-label font-weight-600 fs-0-875rem c-111827\">{{\r\n controller.labels['LBL_MEDIA_LIBRARY'] || 'Upload from library'\r\n }}</span>\r\n <span class=\"media-drop-desc c-6B7280 fs-0-75rem\">{{ controller.labels['LBL_MEDIA_LIBRARY_DESC'] ||\r\n 'Choose from default\r\n images' }}</span>\r\n </span>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- YouTube URL Input (inline below button) -->\r\n <div class=\"youtube-input-panel d-flex flex-column gap-8 p-16 rounded-10 b-FFFAF1 b-1px-solid-E5E7EB\"\r\n *ngIf=\"showYoutubeInput\">\r\n <label class=\"youtube-panel-label d-flex align-items-center gap-6 font-weight-600 fs-0-875rem c-111827\">\r\n {{ controller.labels['LBL_YOUTUBE_URL'] || 'Video URL' }}\r\n </label>\r\n <div class=\"youtube-input-row d-flex gap-8\">\r\n <input id=\"input-youtube-url-{{ config.name }}\" type=\"url\" class=\"field-input youtube-url-input\"\r\n [(ngModel)]=\"youtubeUrlInput\" [ngModelOptions]=\"{standalone: true}\"\r\n [placeholder]=\"controller.labels['PH_YOUTUBE_URL'] || 'Video URL'\" (keyup.enter)=\"addYoutubeMedia()\">\r\n <lib-button id=\"btn-add-youtube-{{ config.name }}\" [variant]=\"'secondary'\" (click)=\"addYoutubeMedia()\">\r\n {{ controller.labels['LBL_ADD'] || 'Add' }}\r\n </lib-button>\r\n </div>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"youtubeUrlError\">{{ youtubeUrlError }}</span>\r\n </div>\r\n\r\n <div\r\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\"\r\n *ngIf=\"mediaUploadError\">\r\n <mat-icon class=\"status-icon fs-18px\">error_outline</mat-icon>\r\n <span>{{ mediaUploadError }}</span>\r\n </div>\r\n\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ config.hint }}</span>\r\n </div>\r\n <!-- end left panel -->\r\n\r\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 -->\r\n <div class=\"mu-right d-flex flex-column gap-12\">\r\n\r\n <!-- Carousel (when items exist) -->\r\n <div class=\"media-carousel-section d-flex flex-column gap-12\" *ngIf=\"mediaItems.length\">\r\n <div\r\n class=\"media-carousel-main position-relative w-100 overflow-hidden d-flex align-items-center justify-content-center br-12px b-0F172A\">\r\n <button id=\"btn-carousel-prev-{{ config.name }}\" type=\"button\"\r\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\"\r\n (click)=\"mediaCarouselPrev()\" [disabled]=\"mediaCarouselIndex === 0\" aria-label=\"Previous\">\r\n <mat-icon>chevron_left</mat-icon>\r\n </button>\r\n\r\n <div class=\"carousel-viewer position-absolute d-flex align-items-center justify-content-center\"\r\n *ngFor=\"let item of mediaItems; let i = index\" [hidden]=\"i !== mediaCarouselIndex\">\r\n <div *ngIf=\"item.isUploading\"\r\n class=\"carousel-uploading d-flex flex-column align-items-center gap-12 c-94A3B8 fs-0-85rem\">\r\n <div class=\"carousel-spinner rounded-50 b-3px-solid-rgba-255-255-255-0-15\"></div>\r\n <span>{{ controller.labels['LBL_UPLOADING'] || 'Uploading\u2026' }}</span>\r\n </div>\r\n <ng-container *ngIf=\"!item.isUploading && item.mediaType === 'youtube'\">\r\n <iframe class=\"carousel-iframe w-100 h-100 br-12px\" [src]=\"item.url | trustedUrl\" frameborder=\"0\"\r\n allowfullscreen\r\n allow=\"accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture\">\r\n </iframe>\r\n </ng-container>\r\n <ng-container *ngIf=\"!item.isUploading && item.mediaType === 'image'\">\r\n <img class=\"carousel-image w-100 h-100 br-12px\" [src]=\"item.url\" alt=\"Media\">\r\n </ng-container>\r\n <button id=\"btn-remove-media-{{ config.name }}-{{ i }}\" type=\"button\"\r\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\"\r\n [disabled]=\"item.isUploading\" (click)=\"removeMediaItem(i)\" aria-label=\"Remove\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <button id=\"btn-carousel-next-{{ config.name }}\" type=\"button\"\r\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\"\r\n (click)=\"mediaCarouselNext()\" [disabled]=\"mediaCarouselIndex === mediaItems.length - 1\" aria-label=\"Next\">\r\n <mat-icon>chevron_right</mat-icon>\r\n </button>\r\n\r\n <div class=\"carousel-dots position-absolute d-flex gap-6\">\r\n <span *ngFor=\"let item of mediaItems; let i = index\"\r\n class=\"carousel-dot rounded-50 cursor-pointer b-rgba-255-255-255-0-45\"\r\n [class.active]=\"i === mediaCarouselIndex\" (click)=\"mediaGoTo(i)\"></span>\r\n </div>\r\n </div>\r\n\r\n <!-- Thumbnail strip -->\r\n <div class=\"media-thumbnail-strip d-flex flex-wrap gap-8 pb-4px\">\r\n <div *ngFor=\"let item of mediaThumbnails; let i = index\"\r\n class=\"media-thumb rounded-8 overflow-hidden cursor-pointer d-flex align-items-center justify-content-center b-2px-solid-transparent b-E2E8F0\"\r\n [class.active]=\"i === mediaCarouselIndex\" (click)=\"mediaGoTo(i)\">\r\n <div *ngIf=\"item.isUploading\"\r\n class=\"thumb-uploading d-flex align-items-center justify-content-center w-100 h-100\">\r\n <div class=\"thumb-spinner rounded-50 b-2px-solid-E2E8F0\"></div>\r\n </div>\r\n <img *ngIf=\"!item.isUploading && item.mediaType === 'youtube' && item.thumbnailUrl\"\r\n [src]=\"item.thumbnailUrl\" class=\"thumb-img w-100 h-100\" alt=\"Video thumbnail\">\r\n <div *ngIf=\"!item.isUploading && item.mediaType === 'youtube' && !item.thumbnailUrl\"\r\n class=\"thumb-yt-placeholder d-flex align-items-center justify-content-center w-100 h-100 b-1E293B c-EF4444\">\r\n <mat-icon>play_circle</mat-icon>\r\n </div>\r\n <img *ngIf=\"!item.isUploading && item.mediaType === 'image' && item.url\" [src]=\"item.url\"\r\n class=\"thumb-img w-100 h-100\" alt=\"Image thumbnail\">\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Empty right-side placeholder -->\r\n <div\r\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\"\r\n *ngIf=\"!mediaItems.length\" (click)=\"onMediaMenuDevice()\">\r\n <mat-icon class=\"mu-right-empty-icon fs-52px\">perm_media</mat-icon>\r\n <p>{{ controller.labels['LBL_ADD_MEDIA'] || 'Add media' }}</p>\r\n </div>\r\n\r\n </div>\r\n <!-- end right panel -->\r\n\r\n </div><!-- end mu-layout -->\r\n </div>\r\n\r\n\r\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 -->\r\n <!-- Wrapper is always in DOM (hidden) so @ViewChild can move it to body -->\r\n <div #libraryModal class=\"media-library-portal-host\" [class.is-open]=\"showLibraryModal\">\r\n\r\n <!-- Backdrop -->\r\n <div class=\"media-library-overlay\" (click)=\"closeLibraryModal()\"></div>\r\n\r\n <!-- Modal card -->\r\n <div class=\"media-library-modal d-flex flex-column overflow-hidden b-ffffff br-16px\"\r\n role=\"dialog\" aria-modal=\"true\">\r\n <div class=\"library-modal-header d-flex align-items-start justify-content-between p-24px-28px bb-1px-solid-E5E7EB\">\r\n <div class=\"header-left d-flex flex-column gap-8\">\r\n <h3 class=\"library-modal-title m-0 color-dark fs-1-25rem\">\r\n {{ controller.labels['LBL_ADD_IMAGES'] || 'Add Images' }}\r\n </h3>\r\n <p class=\"library-modal-subtitle m-0 color-gray fs-0-85rem\">\r\n {{ controller.labels['LBL_LIBRARY_MODAL_DESC'] || 'Select images from your library.' }}\r\n </p>\r\n </div>\r\n <button id=\"btn-close-library-{{ config.name }}\" type=\"button\"\r\n class=\"library-close-btn d-flex align-items-center justify-content-center cursor-pointer rounded-50 border-none b-none c-9CA3AF\"\r\n (click)=\"closeLibraryModal()\" aria-label=\"Close\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <!-- Loading -->\r\n <div class=\"library-loading\" *ngIf=\"libraryLoading\">\r\n <div class=\"lib-spinner rounded-50 b-3px-solid-E2E8F0\"></div>\r\n <span>{{ controller.labels['LBL_LOADING'] || 'Loading\u2026' }}</span>\r\n </div>\r\n\r\n <!-- Error -->\r\n <div class=\"library-error d-flex align-items-center gap-8 color-error b-FEF2F2 fs-0-875rem p-16px-24px\"\r\n *ngIf=\"libraryError && !libraryLoading\">\r\n <mat-icon>error_outline</mat-icon>\r\n {{ libraryError }}\r\n </div>\r\n\r\n <!-- Image grid -->\r\n <div class=\"library-grid d-grid gap-16 flex-1 p-28px b-F9FAFB\" *ngIf=\"!libraryLoading && libraryImages.length\">\r\n <div *ngFor=\"let img of libraryImages; let li = index\" id=\"lib-img-{{ config.name }}-{{ li }}\"\r\n class=\"library-grid-item position-relative rounded-12 overflow-hidden cursor-pointer bg-white b-3px-solid-transparent\"\r\n [class.selected]=\"isLibraryItemSelected(img)\" (click)=\"toggleLibraryItem(img)\">\r\n <img [src]=\"getLibraryItemUrl(img)\" class=\"library-grid-img w-100 h-100 d-block\" alt=\"Library image\">\r\n <div\r\n class=\"library-check position-absolute bg-white rounded-50 d-flex align-items-center justify-content-center c-3B82F6\"\r\n *ngIf=\"isLibraryItemSelected(img)\">\r\n <mat-icon>check_circle</mat-icon>\r\n </div>\r\n <div class=\"library-overlay-hover position-absolute b-rgba-59-130-246-0-12\"></div>\r\n </div>\r\n </div>\r\n\r\n <!-- Empty library -->\r\n <div\r\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\"\r\n *ngIf=\"!libraryLoading && !libraryError && libraryImages.length === 0\">\r\n <mat-icon>image_not_supported</mat-icon>\r\n <span>{{ controller.labels['LBL_LIBRARY_EMPTY'] || 'No images found in library.' }}</span>\r\n </div>\r\n\r\n <!-- Footer -->\r\n <div class=\"library-modal-footer d-flex align-items-center justify-content-end bg-white p-20px-28px bt-1px-solid-E5E7EB\">\r\n <div class=\"library-footer-actions d-flex gap-12\">\r\n <lib-button id=\"btn-library-cancel-{{ config.name }}\" [variant]=\"'outline'\" (click)=\"closeLibraryModal()\">\r\n {{ controller.labels['LBL_CANCEL'] || 'Cancel' }}\r\n </lib-button>\r\n <lib-button id=\"btn-library-confirm-{{ config.name }}\" [variant]=\"'primary'\"\r\n [disabled]=\"librarySelectedIds.size === 0\" (click)=\"confirmLibrarySelection()\">\r\n {{ controller.labels['LBL_CONTINUE'] || 'Continue' }}\r\n </lib-button>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n\r\n\r\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 -->\r\n <div *ngIf=\"isLocation\" class=\"field-wrapper location-field-wrapper d-flex flex-column gap-6 gap-12\"\r\n [formGroup]=\"formGroup\">\r\n\r\n <!-- Field label -->\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n <p class=\"location-subtitle m-0 c-6B7280 fs-0-8125rem\" *ngIf=\"config.hint\">{{ config.hint }}</p>\r\n\r\n <!-- Three-tab bar -->\r\n <div class=\"location-tabs d-flex gap-12 mb-24\">\r\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'VENUE' ? 'warning' : 'outline'\"\r\n (click)=\"onLocationTabChange('VENUE')\">\r\n {{ controller.labels['LBL_LOC_VENUE'] || 'Venue' }}\r\n </lib-button>\r\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'ONLINE' ? 'warning' : 'outline'\"\r\n (click)=\"onLocationTabChange('ONLINE')\">\r\n {{ controller.labels['LBL_LOC_ONLINE'] || 'Online Event' }}\r\n </lib-button>\r\n <lib-button class=\"loc-tab-btn flex-1\" [variant]=\"locationActiveTab === 'TBA' ? 'warning' : 'outline'\"\r\n (click)=\"onLocationTabChange('TBA')\">\r\n {{ controller.labels['LBL_LOC_TBA'] || 'To be Announced' }}\r\n </lib-button>\r\n </div>\r\n\r\n <!-- VENUE TAB -->\r\n <div *ngIf=\"locationActiveTab === 'VENUE'\" class=\"loc-panel loc-venue-panel d-flex flex-column gap-12\">\r\n\r\n <p class=\"loc-section-label m-0 font-weight-600 c-111827 fs-0-9rem\">\r\n {{ controller.labels['LBL_LOC_ADDRESS'] || 'Location address' }}\r\n </p>\r\n\r\n <!-- Added venue rows -->\r\n <div class=\"loc-venue-list d-flex flex-column gap-8\" *ngIf=\"locationVenues.length > 0\">\r\n <div *ngFor=\"let venue of locationVenues; let i = index\"\r\n class=\"loc-venue-item d-flex align-items-center gap-10 p-10px-14px br-7px b-ffffff b-1px-solid-D1D5DB\">\r\n <mat-icon class=\"loc-venue-search-icon fs-18px c-9CA3AF\">search</mat-icon>\r\n <span class=\"loc-venue-text flex-1 overflow-hidden fs-0-875rem c-111827\">{{ venue.address || venue.name ||\r\n venue.description }}</span>\r\n <button type=\"button\"\r\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\"\r\n (click)=\"removeLocationVenue(i)\">\r\n <mat-icon>delete_outline</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n <!-- Location count badge -->\r\n <p class=\"loc-count-text m-0 font-weight-600 fs-0-8125rem c-3B82F6\"\r\n *ngIf=\"locationVenues.length > 0 && config.locationConfig?.allowMulti\">\r\n {{ locationVenues.length }}&nbsp;{{ controller.labels['LBL_LOC_COUNT_SUFFIX'] || 'Locations Added!' }}\r\n </p>\r\n\r\n <!-- Search input (hide when max reached) -->\r\n <div class=\"loc-search-container position-relative\" *ngIf=\"!locationMaxReached\">\r\n <div class=\"loc-search-wrapper position-relative d-flex align-items-center mt-4\">\r\n <mat-icon class=\"loc-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">search</mat-icon>\r\n <input\r\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\"\r\n [placeholder]=\"config.locationConfig?.venuePlaceholder || (controller.labels['PH_LOC_VENUE'] || 'Type to search venue...')\"\r\n [value]=\"locationSearchText\" (input)=\"handleLocationSearchInput($event)\" (blur)=\"hideLocationSuggestions()\"\r\n autocomplete=\"off\" [class.is-invalid]=\"errorMessage\">\r\n </div>\r\n <!-- Suggestions dropdown -->\r\n <div class=\"loc-suggestions-panel position-absolute overflow-hidden br-8px b-ffffff b-1px-solid-D1D5DB\"\r\n *ngIf=\"locationShowSuggestions && locationSuggestions.length\">\r\n <div *ngFor=\"let sug of locationSuggestions\"\r\n class=\"loc-suggestion-item d-flex align-items-center gap-10 cursor-pointer p-10px-14px\"\r\n (mousedown)=\"onLocationSuggestionSelect(sug)\">\r\n <mat-icon class=\"loc-suggestion-icon fs-18px c-E53E3E\">place</mat-icon>\r\n <span class=\"loc-suggestion-text overflow-hidden fs-0-875rem c-374151\">{{ sug.description }}</span>\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- Add another button -->\r\n <button type=\"button\"\r\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\"\r\n *ngIf=\"locationVenues.length > 0 && !locationMaxReached && config.locationConfig?.allowMulti\">\r\n <mat-icon>add_circle_outline</mat-icon>\r\n <span>{{ controller.labels['LBL_LOC_ADD_ANOTHER'] || 'Add another Location' }}</span>\r\n </button>\r\n\r\n <!-- Map -->\r\n <div class=\"loc-map-container overflow-hidden br-10px b-1px-solid-E5E7EB\"\r\n *ngIf=\"config.locationConfig?.showMap !== false\">\r\n <ng-container *ngIf=\"config.locationConfig?.googleMapsApiKey; else simpleEmbed\">\r\n <div [id]=\"'loc-map-' + config.name\" class=\"loc-map-frame w-100 d-block border-none\"\r\n [style.height]=\"config.locationConfig?.mapHeight || '300px'\"></div>\r\n </ng-container>\r\n <ng-template #simpleEmbed>\r\n <iframe class=\"loc-map-frame w-100 d-block border-none\"\r\n [style.height]=\"config.locationConfig?.mapHeight || '300px'\" [src]=\"getLocationMapEmbedUrl() | trustedUrl\"\r\n frameborder=\"0\" allowfullscreen loading=\"lazy\">\r\n </iframe>\r\n </ng-template>\r\n </div>\r\n\r\n <!-- Map hint -->\r\n <p class=\"loc-map-hint m-0 text-center fs-0-78rem c-6B7280\">\r\n {{ controller.labels['LBL_LOC_MAP_HINT'] || 'Type the venue address. A map will appear to assist you.' }}\r\n </p>\r\n </div>\r\n\r\n <!-- ONLINE TAB -->\r\n <div *ngIf=\"locationActiveTab === 'ONLINE'\" class=\"loc-panel loc-online-panel d-flex flex-column gap-12\">\r\n <p class=\"loc-section-label m-0 font-weight-600 c-111827 fs-0-9rem\">\r\n {{ controller.labels['LBL_LOC_ONLINE_URL'] || 'Event URL' }}\r\n </p>\r\n <div class=\"loc-search-wrapper position-relative d-flex align-items-center mt-4\">\r\n <mat-icon class=\"loc-search-icon position-absolute fs-1-1rem c-9CA3AF pe-none\">link</mat-icon>\r\n <input\r\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\"\r\n type=\"url\"\r\n [placeholder]=\"config.locationConfig?.onlinePlaceholder || (controller.labels['PH_LOC_ONLINE'] || 'https://zoom.us/j/...')\"\r\n [value]=\"locationOnlineUrl\" (input)=\"onLocationUrlChange($any($event.target).value)\"\r\n [class.is-invalid]=\"errorMessage\">\r\n </div>\r\n </div>\r\n\r\n <!-- TBA TAB -->\r\n <div *ngIf=\"locationActiveTab === 'TBA'\"\r\n class=\"loc-panel loc-tba-panel d-flex flex-column gap-12 justify-content-center\">\r\n <div\r\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\">\r\n <mat-icon class=\"loc-tba-icon fs-40px c-9CA3AF\">schedule</mat-icon>\r\n <p class=\"loc-tba-text m-0 c-6B7280 fs-0-9rem\">\r\n {{ controller.labels['LBL_LOC_TBA_DESC'] || \"This event's location is yet to be announced. Check back later\r\n for updates.\" }}\r\n </p>\r\n </div>\r\n </div>\r\n\r\n <!-- Hidden real form control -->\r\n <input type=\"hidden\" [formControlName]=\"config.name!\">\r\n\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\n <!-- \u2550\u2550 Link List 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 -->\r\n <div *ngIf=\"isLinkList\" class=\"field-wrapper d-flex flex-column gap-6\" [formGroup]=\"formGroup\">\r\n <label *ngIf=\"config.label\" class=\"field-label d-block font-poppins c-202124 fs-18px\">\r\n {{ controller.labels[config.label] || config.label }}\r\n <span class=\"required c-DC2626 ml-0-125rem\" *ngIf=\"config.required\">*</span>\r\n </label>\r\n\r\n <!-- Top Links list (if listPosition === 'top') -->\r\n <div *ngIf=\"linkListConfig?.listPosition === 'top' && linkListItems.length > 0\" class=\"link-list-container mb-8px\">\r\n <ul class=\"link-items-list p-0 m-0\" \r\n [style.display]=\"'grid'\"\r\n [style.gridTemplateColumns]=\"'repeat(12, 1fr)'\"\r\n [style.gap]=\"'12px 16px'\">\r\n <li *ngFor=\"let item of linkListItems; let idx = index\" \r\n class=\"link-list-item\"\r\n [style.gridColumn]=\"'span ' + (linkListConfig?.colSpan || 12)\">\r\n <!-- Normal display mode -->\r\n <ng-container *ngIf=\"editingLinkIndex !== idx\">\r\n <span class=\"link-url-text text-truncate font-weight-500 color-333\">{{ getLinkDisplayUrl(item) }}</span>\r\n <div class=\"link-item-actions d-flex align-items-center gap-12px\">\r\n <button type=\"button\" class=\"action-btn edit-btn\" \r\n *ngIf=\"linkListConfig?.editable !== false\" (click)=\"startEditLink(idx)\" [title]=\"controller.labels['COMMON.ACTIONS.EDIT'] || 'Edit'\">\r\n <mat-icon>edit</mat-icon>\r\n </button>\r\n <button type=\"button\" class=\"action-btn delete-btn\" \r\n *ngIf=\"linkListConfig?.deleteable !== false\" (click)=\"deleteLinkItem(idx)\" [title]=\"controller.labels['COMMON.ACTIONS.DELETE'] || 'Delete'\">\r\n <mat-icon>delete_outline</mat-icon>\r\n </button>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- Inline Edit mode -->\r\n <ng-container *ngIf=\"editingLinkIndex === idx\">\r\n <div class=\"edit-input-wrapper d-flex align-items-center gap-8px w-100\">\r\n <input type=\"text\" class=\"field-input flex-1 py-4px\" [(ngModel)]=\"editingLinkValue\" \r\n [ngModelOptions]=\"{standalone: true}\" (keydown.enter)=\"saveEditLink(idx)\" (keydown.escape)=\"cancelEditLink()\"\r\n (input)=\"linkValidationError = ''\">\r\n <button type=\"button\" class=\"action-btn save-btn\" (click)=\"saveEditLink(idx)\" [title]=\"controller.labels['COMMON.ACTIONS.SAVE'] || 'Save'\">\r\n <mat-icon>check</mat-icon>\r\n </button>\r\n <button type=\"button\" class=\"action-btn cancel-btn\" (click)=\"cancelEditLink()\" [title]=\"controller.labels['COMMON.ACTIONS.CANCEL'] || 'Cancel'\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n </ng-container>\r\n </li>\r\n </ul>\r\n </div>\r\n\r\n <!-- Input text + plus icon -->\r\n <div class=\"input-group position-relative d-flex w-100\">\r\n <input type=\"text\" class=\"field-input pe-40px\" [placeholder]=\"controller.labels[config.placeholder!] || config.placeholder || controller.labels['COMMON.ENTER_LINK'] || 'Enter link...'\"\r\n [(ngModel)]=\"newLinkInputValue\" [ngModelOptions]=\"{standalone: true}\" (keydown.enter)=\"addLinkItem($event)\"\r\n [disabled]=\"config.readonly || config.disabled\"\r\n (input)=\"linkValidationError = ''\">\r\n \r\n <button type=\"button\" class=\"position-absolute right-8px top-50-percent translate-y-minus-50 d-flex align-items-center justify-content-center b-none bg-none p-0 cursor-pointer color-primary\"\r\n [disabled]=\"config.readonly || config.disabled || !newLinkInputValue?.trim()\"\r\n (click)=\"addLinkItem()\" style=\"right: 8px; top: 50%; transform: translateY(-50%); z-index: 10;\">\r\n <mat-icon>add</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <!-- Validation error message -->\r\n <span class=\"field-error color-error fs-0-75rem mt-4px d-block\" *ngIf=\"linkValidationError\">{{ linkValidationError }}</span>\r\n\r\n <!-- Bottom Links list (if listPosition !== 'top') -->\r\n <div *ngIf=\"linkListConfig?.listPosition !== 'top' && linkListItems.length > 0\" class=\"link-list-container mt-8px\">\r\n <ul class=\"link-items-list p-0 m-0\" \r\n [style.display]=\"'grid'\"\r\n [style.gridTemplateColumns]=\"'repeat(12, 1fr)'\"\r\n [style.gap]=\"'12px 16px'\">\r\n <li *ngFor=\"let item of linkListItems; let idx = index\" \r\n class=\"link-list-item\"\r\n [style.gridColumn]=\"'span ' + (linkListConfig?.colSpan || 12)\">\r\n <!-- Normal display mode -->\r\n <ng-container *ngIf=\"editingLinkIndex !== idx\">\r\n <span class=\"link-url-text text-truncate font-weight-500 color-333\">{{ getLinkDisplayUrl(item) }}</span>\r\n <div class=\"link-item-actions d-flex align-items-center gap-12px\">\r\n <button type=\"button\" class=\"action-btn edit-btn\" \r\n *ngIf=\"linkListConfig?.editable !== false\" (click)=\"startEditLink(idx)\" [title]=\"controller.labels['COMMON.ACTIONS.EDIT'] || 'Edit'\">\r\n <mat-icon>edit</mat-icon>\r\n </button>\r\n <button type=\"button\" class=\"action-btn delete-btn\" \r\n *ngIf=\"linkListConfig?.deleteable !== false\" (click)=\"deleteLinkItem(idx)\" [title]=\"controller.labels['COMMON.ACTIONS.DELETE'] || 'Delete'\">\r\n <mat-icon>delete_outline</mat-icon>\r\n </button>\r\n </div>\r\n </ng-container>\r\n\r\n <!-- Inline Edit mode -->\r\n <ng-container *ngIf=\"editingLinkIndex === idx\">\r\n <div class=\"edit-input-wrapper d-flex align-items-center gap-8px w-100\">\r\n <input type=\"text\" class=\"field-input flex-1 py-4px\" [(ngModel)]=\"editingLinkValue\" \r\n [ngModelOptions]=\"{standalone: true}\" (keydown.enter)=\"saveEditLink(idx)\" (keydown.escape)=\"cancelEditLink()\"\r\n (input)=\"linkValidationError = ''\">\r\n <button type=\"button\" class=\"action-btn save-btn\" (click)=\"saveEditLink(idx)\" [title]=\"controller.labels['COMMON.ACTIONS.SAVE'] || 'Save'\">\r\n <mat-icon>check</mat-icon>\r\n </button>\r\n <button type=\"button\" class=\"action-btn cancel-btn\" (click)=\"cancelEditLink()\" [title]=\"controller.labels['COMMON.ACTIONS.CANCEL'] || 'Cancel'\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n </ng-container>\r\n </li>\r\n </ul>\r\n </div>\r\n\r\n <!-- Hidden real form control -->\r\n <input type=\"hidden\" [formControlName]=\"config.name!\">\r\n\r\n <span class=\"field-hint c-6B7280 fs-0-75rem\" *ngIf=\"config.hint && !errorMessage\">{{ controller.labels[config.hint] || config.hint }}</span>\r\n <span class=\"field-error color-error fs-0-75rem\" *ngIf=\"errorMessage\">{{ errorMessage }}</span>\r\n </div>\r\n\r\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-8px{margin-bottom:8px!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}.generated-value{word-break:break-all}: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:.8rem!important;font-weight:600!important;line-height:1.2;letter-spacing:.04em;text-transform:uppercase!important;color:#202124!important;margin-bottom:var(--cc-sf-label-mb, 0px)!important}.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}.dropdown-search-wrapper{display:flex;align-items:center;gap:8px;padding:8px 12px;border-bottom:1px solid var(--cc-sf-input-border, #E5E7EB);background:var(--cc-sf-input-bg, #ffffff)}.dropdown-search-wrapper .dropdown-search-icon{font-size:18px;width:18px;height:18px;color:var(--cc-sf-input-placeholder, #9CA3AF);flex-shrink:0}.dropdown-search-wrapper .dropdown-search-input{flex:1;border:none;outline:none;background:transparent;font-size:.875rem;color:var(--cc-sf-label-color, #111827);font-family:inherit}.dropdown-search-wrapper .dropdown-search-input::placeholder{color:var(--cc-sf-input-placeholder, #C4C9D4)}.dropdown-search-wrapper .dropdown-search-clear{font-size:18px;width:18px;height:18px;color:var(--cc-sf-input-placeholder, #9CA3AF);flex-shrink:0;transition:color .15s}.dropdown-search-wrapper .dropdown-search-clear:hover{color:var(--cc-sf-label-color, #111827)}.dropdown-pagination-footer{padding:8px 12px;border-top:1px solid var(--cc-sf-input-border, #E5E7EB);background:var(--cc-sf-input-bg, #ffffff);display:flex;justify-content:center;align-items:center}.dropdown-pagination-footer .btn-dropdown-load-more{width:100%;padding:6px 12px;font-size:.8125rem;font-weight:500;color:var(--cc-sf-accent-color, #6366F1);background:transparent;border:1px dashed var(--cc-sf-accent-color, #6366F1);border-radius:var(--cc-sf-input-radius, 6px);cursor:pointer;transition:all .2s ease}.dropdown-pagination-footer .btn-dropdown-load-more:hover{background:#6366f114}.dropdown-pagination-footer .dropdown-loading-more-state{display:flex;align-items:center;justify-content:center;gap:8px;padding:4px 0}.dropdown-pagination-footer .dropdown-loading-more-state .dropdown-spinner-small{width:14px;height:14px;border:2px solid var(--cc-sf-input-border, #E5E7EB);border-top-color:var(--cc-sf-accent-color, #6366F1);border-radius:50%;animation:spin .8s linear infinite}.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:var(--cc-sf-label-color, #6B7280);flex-shrink:0}.multi-select-wrapper .multi-select-panel{position:relative;width:100%;max-width:100vw;box-sizing:border-box;background:var(--cc-sf-input-bg, #ffffff);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;display:flex;flex-direction:column;overflow:hidden;max-height:320px}.multi-select-wrapper .multi-select-options-list{flex:1;overflow-y:auto;max-height:240px}.multi-select-wrapper .multi-select-options-list::-webkit-scrollbar{width:4px}.multi-select-wrapper .multi-select-options-list::-webkit-scrollbar-thumb{background:var(--cc-sf-input-border, #D1D5DB);border-radius:2px}.multi-select-wrapper .multi-select-option{padding:8px 12px;border-bottom:1px solid var(--cc-sf-input-border, #F3F4F6);transition:background .1s}.multi-select-wrapper .multi-select-option:last-of-type{border-bottom:none}.multi-select-wrapper .multi-select-option:hover{background:var(--cc-sf-input-hover-bg, #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-option.multi-select-all{border-bottom:1px solid var(--cc-sf-input-border, #E5E7EB);background:var(--cc-sf-input-bg, #FAFBFC)}.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}.single-select-wrapper{position:relative}.single-select-wrapper .single-select-trigger{min-height:var(--cc-sf-input-height, 44px);height:auto;-webkit-user-select:none;user-select:none}.single-select-wrapper .single-select-trigger.ss-open{border-color:var(--cc-sf-input-focus-border, #6366F1)!important;box-shadow:0 0 0 3px #6366f124,0 1px 4px #6366f11a!important}.single-select-wrapper .single-select-trigger .single-select-value{flex:1;color:var(--cc-sf-label-color, #111827);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.single-select-wrapper .single-select-trigger .single-select-placeholder{flex:1;font-weight:400;font-size:14px;color:var(--cc-sf-input-placeholder, #C4C9D4)}.single-select-wrapper .single-select-trigger .single-select-arrow{font-size:20px;width:20px;height:20px;line-height:20px;color:var(--cc-sf-label-color, #6B7280);flex-shrink:0}.single-select-wrapper .single-select-panel{position:relative;width:100%;max-width:100vw;box-sizing:border-box;background:var(--cc-sf-input-bg, #ffffff);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;display:flex;flex-direction:column;overflow:hidden;max-height:320px}.single-select-wrapper .single-select-options-list{flex:1;overflow-y:auto;max-height:240px}.single-select-wrapper .single-select-options-list::-webkit-scrollbar{width:4px}.single-select-wrapper .single-select-options-list::-webkit-scrollbar-thumb{background:var(--cc-sf-input-border, #D1D5DB);border-radius:2px}.single-select-wrapper .single-select-option{padding:8px 12px;border-bottom:1px solid var(--cc-sf-input-border, #F3F4F6);transition:background .1s;justify-content:space-between}.single-select-wrapper .single-select-option:last-of-type{border-bottom:none}.single-select-wrapper .single-select-option:hover{background:var(--cc-sf-input-hover-bg, #F9FAFB)}.single-select-wrapper .single-select-option.selected{background:var(--cc-sf-input-hover-bg, #F0F5FF);color:var(--cc-sf-selected-color, #6366F1)}.single-select-wrapper .single-select-option .single-select-check{font-size:18px;width:18px;height:18px;color:var(--cc-sf-selected-color, #6366F1)}.single-select-wrapper.is-invalid .single-select-trigger{border-color:var(--cc-sf-error-border, #EF4444)!important;background-color:var(--cc-sf-error-bg, #FFF5F5)!important}.multi-select-panel,.single-select-panel{width:100%;background:var(--cc-sf-input-bg, #ffffff);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;display:flex;flex-direction:column;overflow:hidden;max-height:320px}.multi-select-options-list,.single-select-options-list{flex:1;overflow-y:auto;max-height:240px}.multi-select-options-list::-webkit-scrollbar,.single-select-options-list::-webkit-scrollbar{width:4px}.multi-select-options-list::-webkit-scrollbar-thumb,.single-select-options-list::-webkit-scrollbar-thumb{background:var(--cc-sf-input-border, #D1D5DB);border-radius:2px}.multi-select-option,.single-select-option{display:flex;align-items:center;gap:8px;cursor:pointer;padding:8px 12px;border-bottom:1px solid var(--cc-sf-input-border, #F3F4F6);transition:background .1s}.multi-select-option:last-of-type,.single-select-option:last-of-type{border-bottom:none}.multi-select-option:hover,.single-select-option:hover{background:var(--cc-sf-input-hover-bg, #F9FAFB)}.multi-select-option input[type=checkbox],.single-select-option input[type=checkbox]{flex-shrink:0;cursor:pointer;accent-color:var(--cc-sf-selected-color, #6366F1);width:15px;height:15px}.multi-select-option.multi-select-all{border-bottom:1px solid var(--cc-sf-input-border, #E5E7EB);background:var(--cc-sf-input-bg, #FAFBFC)}.single-select-option{justify-content:space-between}.single-select-option.selected{background:var(--cc-sf-input-hover-bg, #F0F5FF);color:var(--cc-sf-selected-color, #6366F1)}.single-select-option .single-select-check{font-size:18px;width:18px;height:18px;color:var(--cc-sf-selected-color, #6366F1)}.multi-select-empty{padding:12px;text-align:center}::ng-deep .cdk-overlay-container{z-index:1100}.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)}.suffix-action-icons{right:.875rem;top:50%;transform:translateY(-50%);z-index:1;pointer-events:auto}.suffix-action-icons mat-icon.suffix-action-icon{font-size:1.125rem;width:1.125rem;height:1.125rem;line-height:1.125rem;color:var(--cc-sf-hint-color, #6B7280);cursor:pointer;transition:color .2s ease,transform .15s ease}.suffix-action-icons mat-icon.suffix-action-icon:hover{color:var(--cc-sf-text-color, #202124);transform:scale(1.15)}.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}.link-list-container{border:none!important;background-color:transparent!important;padding:0!important;box-shadow:none!important;overflow:visible!important}.link-items-list{list-style:none}@media(max-width:640px){.link-items-list{grid-template-columns:1fr!important}.link-items-list .link-list-item{grid-column:span 12!important}}.link-list-item{display:flex;align-items:center;justify-content:space-between;padding:10px 16px;background-color:var(--cc-sf-link-item-bg, #f9fafb)!important;border:1px solid var(--cc-sf-link-item-border, #E5E7EB)!important;border-radius:var(--cc-sf-input-radius, 9px)!important;gap:12px;min-height:48px}.link-list-item .link-url-text{font-size:.875rem;color:var(--cc-sf-link-item-text, #111827);word-break:break-all}.link-list-item .action-btn{border:none;background:transparent;cursor:pointer;display:inline-flex;align-items:center;justify-content:center;width:34px;height:34px;padding:0;border-radius:6px;transition:background .15s ease,color .15s ease}.link-list-item .action-btn mat-icon{font-size:22px;width:22px;height:22px;line-height:22px;display:flex;align-items:center;justify-content:center}.link-list-item .action-btn.edit-btn{color:var(--cc-sf-link-edit-color, #4b5563)}.link-list-item .action-btn.edit-btn:hover{color:var(--cc-sf-accent-color, #6366F1);background-color:#f3f4f6}.link-list-item .action-btn.delete-btn{color:var(--cc-sf-link-delete-color, #ef4444)}.link-list-item .action-btn.delete-btn:hover{background-color:var(--cc-sf-error-bg, #FFF5F5)}.link-list-item .action-btn.save-btn{color:var(--cc-sf-link-save-color, #16a34a)!important}.link-list-item .action-btn.save-btn:hover{background-color:#ecfdf5}.link-list-item .action-btn.cancel-btn{color:#ef4444!important}.link-list-item .action-btn.cancel-btn:hover{background-color:var(--cc-sf-error-bg, #FFF5F5)}.pe-40px{padding-right:40px!important}.dropdown-loading-state{display:flex;align-items:center;justify-content:center;padding:16px;gap:8px}.dropdown-spinner{width:20px;height:20px;border:2px solid #E2E8F0;border-top-color:var(--cc-sf-accent-color, #3B82F6);border-radius:50%;animation:cc-spin .7s linear infinite}.mini-spinner{width:16px;height:16px;border:2px solid #E2E8F0;border-top-color:var(--cc-sf-accent-color, #3B82F6);border-radius:50%;animation:cc-spin .7s linear infinite}.native-select-wrapper{display:block;width:100%;position:relative}.native-select-wrapper select{width:100%}.native-select-wrapper .native-select-spinner{position:absolute;right:2.5rem;top:calc(50% - 7px);width:14px;height:14px;border:2px solid #CBD5E1;border-top-color:var(--cc-sf-accent-color, #3B82F6);border-radius:50%;animation:cc-spin .7s linear infinite;pointer-events:none}\n"] }]
4504
+ }], ctorParameters: () => [{ type: i1$2.FormBuilder }, { type: ExpressionService }, { type: i3.HttpClient }, { type: i0.ElementRef }, { type: i4.Overlay }], propDecorators: { config: [{
4296
4505
  type: Input
4297
4506
  }], controller: [{
4298
4507
  type: Input
@@ -4313,6 +4522,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImpo
4313
4522
  }], onDocumentClick: [{
4314
4523
  type: HostListener,
4315
4524
  args: ['document:click']
4525
+ }], onWindowResize: [{
4526
+ type: HostListener,
4527
+ args: ['window:resize']
4316
4528
  }], onEscapeKey: [{
4317
4529
  type: HostListener,
4318
4530
  args: ['document:keydown.escape']
@@ -6437,7 +6649,7 @@ class DropdownComponent {
6437
6649
  useExisting: forwardRef(() => DropdownComponent),
6438
6650
  multi: true
6439
6651
  }
6440
- ], 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()\">\r\n <!-- Label -->\r\n <label *ngIf=\"label\" class=\"cc-dropdown-label\" [ngStyle]=\"labelStyles\">\r\n {{ label }}\r\n <span *ngIf=\"required\" class=\"cc-required\">{{ resolvedLabels.requiredMarker }}</span>\r\n </label>\r\n\r\n <!-- Trigger -->\r\n <div #triggerEl class=\"cc-dropdown-trigger\" [class.disabled]=\"disabled\" [class.open]=\"isOpen\" [class.error]=\"errorMessage\"\r\n (click)=\"toggle()\" tabindex=\"0\" (keydown)=\"handleKeyboardEvent($event)\" [ngStyle]=\"fieldStyles\">\r\n\r\n <div class=\"cc-selected-value\" *ngIf=\"hasValue()\">\r\n {{ multiple ? value.length + ' ' + resolvedLabels.selectedSuffix : getSelectedLabel() }}\r\n </div>\r\n <div class=\"cc-placeholder\" *ngIf=\"!hasValue()\">{{ placeholder }}</div>\r\n\r\n <div class=\"cc-actions\">\r\n <span *ngIf=\"clearable && hasValue() && !disabled\" class=\"cc-clear-btn\" (click)=\"clearSelection($event)\"\r\n [attr.aria-label]=\"resolvedLabels.clearAriaLabel\">\r\n <span class=\"material-icons\">close</span>\r\n </span>\r\n <span class=\"cc-arrow material-icons\">expand_more</span>\r\n </div>\r\n </div>\r\n\r\n <!-- Dropdown Menu -->\r\n <div class=\"cc-dropdown-menu\" *ngIf=\"isOpen\"\r\n [ngStyle]=\"{ position: 'fixed', top: menuPosition.top + 'px', left: menuPosition.left + 'px', width: menuPosition.width + 'px', 'z-index': '9999' }\"\r\n (click)=\"$event.stopPropagation()\" (mousedown)=\"$event.stopPropagation()\">\r\n <!-- Search -->\r\n <div class=\"cc-search-container\" *ngIf=\"searchable\">\r\n <input type=\"text\" #searchInput [placeholder]=\"resolvedLabels.searchPlaceholder\"\r\n (input)=\"onSearch($any($event.target).value)\" (click)=\"$event.stopPropagation()\"\r\n class=\"cc-dropdown-search-input\" />\r\n </div>\r\n\r\n <!-- Options Virtual Scroll -->\r\n <cdk-virtual-scroll-viewport itemSize=\"40\" class=\"cc-virtual-scroll\"\r\n [style.height.px]=\"filteredOptions.length * 40 > 240 ? 240 : (filteredOptions.length * 40 || 40)\">\r\n\r\n <div *cdkVirtualFor=\"let option of filteredOptions; let i = index\" class=\"cc-option\"\r\n [class.selected]=\"isSelected(option)\" [class.focused]=\"i === focusedIndex\"\r\n [class.disabled]=\"option.disabled\" (click)=\"selectOption(option)\">\r\n\r\n <!-- Multi-select checkbox visual -->\r\n <span class=\"cc-multi-check\" *ngIf=\"multiple\">\r\n <span class=\"material-icons\" *ngIf=\"isSelected(option)\">check_box</span>\r\n <span class=\"material-icons\" *ngIf=\"!isSelected(option)\">check_box_outline_blank</span>\r\n </span>\r\n\r\n <!-- Icon -->\r\n <span class=\"cc-option-icon-wrapper\" *ngIf=\"option.icon\">\r\n <span class=\"material-icons\" *ngIf=\"getIconType(option.icon) === 'material'\">{{\r\n getIconValue(option.icon) }}</span>\r\n <i *ngIf=\"getIconType(option.icon) === 'fontawesome'\" [class]=\"getIconValue(option.icon)\"></i>\r\n <img *ngIf=\"getIconType(option.icon) === 'img'\" [src]=\"getIconValue(option.icon)\"\r\n class=\"cc-option-img\" alt=\"icon\" />\r\n </span>\r\n\r\n <span class=\"cc-option-label\">{{ option.label }}</span>\r\n\r\n <!-- Single-select check visual -->\r\n <span class=\"cc-single-check\" *ngIf=\"!multiple && isSelected(option)\">\r\n <span class=\"material-icons\">check</span>\r\n </span>\r\n </div>\r\n\r\n <div *ngIf=\"filteredOptions.length === 0\" class=\"cc-no-results\">{{ resolvedLabels.noResultsFound }}</div>\r\n </cdk-virtual-scroll-viewport>\r\n </div>\r\n\r\n <!-- Error Message -->\r\n <div class=\"cc-error-message\" *ngIf=\"errorMessage\">{{ errorMessage }}</div>\r\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"] }] });
6652
+ ], 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()\">\r\n <!-- Label -->\r\n <label *ngIf=\"label\" class=\"cc-dropdown-label\" [ngStyle]=\"labelStyles\">\r\n {{ label }}\r\n <span *ngIf=\"required\" class=\"cc-required\">{{ resolvedLabels.requiredMarker }}</span>\r\n </label>\r\n\r\n <!-- Trigger -->\r\n <div #triggerEl class=\"cc-dropdown-trigger\" [class.disabled]=\"disabled\" [class.open]=\"isOpen\" [class.error]=\"errorMessage\"\r\n (click)=\"toggle()\" tabindex=\"0\" (keydown)=\"handleKeyboardEvent($event)\" [ngStyle]=\"fieldStyles\">\r\n\r\n <div class=\"cc-selected-value\" *ngIf=\"hasValue()\">\r\n {{ multiple ? value.length + ' ' + resolvedLabels.selectedSuffix : getSelectedLabel() }}\r\n </div>\r\n <div class=\"cc-placeholder\" *ngIf=\"!hasValue()\">{{ placeholder }}</div>\r\n\r\n <div class=\"cc-actions\">\r\n <span *ngIf=\"clearable && hasValue() && !disabled\" class=\"cc-clear-btn\" (click)=\"clearSelection($event)\"\r\n [attr.aria-label]=\"resolvedLabels.clearAriaLabel\">\r\n <span class=\"material-icons\">close</span>\r\n </span>\r\n <span class=\"cc-arrow material-icons\">expand_more</span>\r\n </div>\r\n </div>\r\n\r\n <!-- Dropdown Menu -->\r\n <div class=\"cc-dropdown-menu\" *ngIf=\"isOpen\"\r\n [ngStyle]=\"{ position: 'fixed', top: menuPosition.top + 'px', left: menuPosition.left + 'px', width: menuPosition.width + 'px', 'z-index': '9999' }\"\r\n (click)=\"$event.stopPropagation()\" (mousedown)=\"$event.stopPropagation()\">\r\n <!-- Search -->\r\n <div class=\"cc-search-container\" *ngIf=\"searchable\">\r\n <input type=\"text\" #searchInput [placeholder]=\"resolvedLabels.searchPlaceholder\"\r\n (input)=\"onSearch($any($event.target).value)\" (click)=\"$event.stopPropagation()\"\r\n class=\"cc-dropdown-search-input\" />\r\n </div>\r\n\r\n <!-- Options Virtual Scroll -->\r\n <cdk-virtual-scroll-viewport itemSize=\"40\" class=\"cc-virtual-scroll\"\r\n [style.height.px]=\"filteredOptions.length * 40 > 240 ? 240 : (filteredOptions.length * 40 || 40)\">\r\n\r\n <div *cdkVirtualFor=\"let option of filteredOptions; let i = index\" class=\"cc-option\"\r\n [class.selected]=\"isSelected(option)\" [class.focused]=\"i === focusedIndex\"\r\n [class.disabled]=\"option.disabled\" (click)=\"selectOption(option)\">\r\n\r\n <!-- Multi-select checkbox visual -->\r\n <span class=\"cc-multi-check\" *ngIf=\"multiple\">\r\n <span class=\"material-icons\" *ngIf=\"isSelected(option)\">check_box</span>\r\n <span class=\"material-icons\" *ngIf=\"!isSelected(option)\">check_box_outline_blank</span>\r\n </span>\r\n\r\n <!-- Icon -->\r\n <span class=\"cc-option-icon-wrapper\" *ngIf=\"option.icon\">\r\n <span class=\"material-icons\" *ngIf=\"getIconType(option.icon) === 'material'\">{{\r\n getIconValue(option.icon) }}</span>\r\n <i *ngIf=\"getIconType(option.icon) === 'fontawesome'\" [class]=\"getIconValue(option.icon)\"></i>\r\n <img *ngIf=\"getIconType(option.icon) === 'img'\" [src]=\"getIconValue(option.icon)\"\r\n class=\"cc-option-img\" alt=\"icon\" />\r\n </span>\r\n\r\n <span class=\"cc-option-label\">{{ option.label }}</span>\r\n\r\n <!-- Single-select check visual -->\r\n <span class=\"cc-single-check\" *ngIf=\"!multiple && isSelected(option)\">\r\n <span class=\"material-icons\">check</span>\r\n </span>\r\n </div>\r\n\r\n <div *ngIf=\"filteredOptions.length === 0\" class=\"cc-no-results\">{{ resolvedLabels.noResultsFound }}</div>\r\n </cdk-virtual-scroll-viewport>\r\n </div>\r\n\r\n <!-- Error Message -->\r\n <div class=\"cc-error-message\" *ngIf=\"errorMessage\">{{ errorMessage }}</div>\r\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: i4.ɵɵCdkFixedSizeVirtualScroll, selector: "cdk-virtual-scroll-viewport[itemSize]", inputs: ["itemSize", "minBufferPx", "maxBufferPx"] }, { kind: "directive", type: i4.ɵɵCdkVirtualForOf, selector: "[cdkVirtualFor][cdkVirtualForOf]", inputs: ["cdkVirtualForOf", "cdkVirtualForTrackBy", "cdkVirtualForTemplate", "cdkVirtualForTemplateCacheSize"] }, { kind: "component", type: i4.ɵɵCdkVirtualScrollViewport, selector: "cdk-virtual-scroll-viewport", inputs: ["orientation", "appendOnly"], outputs: ["scrolledIndexChange"] }, { kind: "directive", type: ClickOutsideDirective, selector: "[libClickOutside]", outputs: ["libClickOutside"] }] });
6441
6653
  }
6442
6654
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: DropdownComponent, decorators: [{
6443
6655
  type: Component,
@@ -12407,7 +12619,7 @@ class ConfigurableFormComponent {
12407
12619
  return this.passwordFieldState.get(fieldName) || false;
12408
12620
  }
12409
12621
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ConfigurableFormComponent, deps: [{ token: i1$2.FormBuilder }, { token: i1$3.MatSnackBar }, { token: i3.HttpClient }], target: i0.ɵɵFactoryTarget.Component });
12410
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: ConfigurableFormComponent, isStandalone: true, selector: "lib-configurable-form", inputs: { config: "config", jsonConfig: "jsonConfig", data: "data", baseApiUrl: "baseApiUrl", labels: "labels" }, outputs: { optionsLoad: "optionsLoad" }, usesOnChanges: true, ngImport: i0, template: "<form [formGroup]=\"form\" class=\"configurable-form-container\" *ngIf=\"form\">\r\n\r\n <ng-container *ngFor=\"let section of sections\">\r\n\r\n <!-- Repeater Section -->\r\n <div *ngIf=\"section.isRepeater; else normalSection\"\r\n [ngClass]=\"section.noCardLayout ? 'section-no-card' : 'section-card'\">\r\n <div class=\"section-header\" *ngIf=\"section.sectionTitle\">\r\n <h3 class=\"section-title\">{{ section.sectionTitle }}</h3>\r\n <button *ngIf=\"section.collapsible\" mat-icon-button type=\"button\" (click)=\"toggleSection(section)\">\r\n <mat-icon>{{ section.collapsed ? 'expand_more' : 'expand_less' }}</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <div [formArrayName]=\"section.formArrayName || 'items'\" *ngIf=\"!section.collapsed\">\r\n <div *ngFor=\"let item of getFormArray(section.formArrayName!).controls; let i = index\"\r\n [formGroupName]=\"i\" class=\"repeater-item\">\r\n <div class=\"repeater-item-header\" *ngIf=\"getFormArray(section.formArrayName!).length > 1\">\r\n <span class=\"text-small\">{{ section.repeaterItemLabel || labels.repeaterItemDefaultLabel }} {{i\r\n + 1}}</span>\r\n <button mat-icon-button color=\"warn\" type=\"button\"\r\n (click)=\"removeRepeaterItem(section.formArrayName!, i)\"\r\n [disabled]=\"section.minItems && getFormArray(section.formArrayName!).length <= section.minItems\">\r\n <mat-icon>delete</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <!-- Fields Grid -->\r\n <div class=\"form-grid\">\r\n <div *ngFor=\"let field of section.fields\" [ngClass]=\"['form-col', field.class || 'col-12']\"\r\n [hidden]=\"!isFieldVisible(field)\">\r\n <ng-container\r\n *ngTemplateOutlet=\"fieldTemplate; context: {field: field, group: item}\"></ng-container>\r\n </div>\r\n </div>\r\n\r\n <div class=\"repeater-separator\" *ngIf=\"i < getFormArray(section.formArrayName!).length - 1\"></div>\r\n </div>\r\n </div>\r\n\r\n <button mat-button color=\"primary\" type=\"button\" (click)=\"addRepeaterItem(section)\" class=\"add-btn\"\r\n *ngIf=\"!section.collapsed\"\r\n [disabled]=\"section.maxItems && getFormArray(section.formArrayName!).length >= section.maxItems\">\r\n <mat-icon>add_circle_outline</mat-icon> {{ section.addLabel || labels.addMoreDefaultLabel }}\r\n </button>\r\n </div>\r\n\r\n <!-- Normal Section -->\r\n <ng-template #normalSection>\r\n <div [ngClass]=\"section.noCardLayout ? 'section-no-card' : 'section-card'\">\r\n <div class=\"section-header\" *ngIf=\"section.sectionTitle\">\r\n <h3 class=\"section-title\">{{ section.sectionTitle }}</h3>\r\n <button *ngIf=\"section.collapsible\" mat-icon-button type=\"button\" (click)=\"toggleSection(section)\">\r\n <mat-icon>{{ section.collapsed ? 'expand_more' : 'expand_less' }}</mat-icon>\r\n </button>\r\n </div>\r\n <div class=\"form-grid\" *ngIf=\"!section.collapsed\">\r\n <div *ngFor=\"let field of section.fields\" [ngClass]=\"['form-col', field.class || 'col-12']\"\r\n [hidden]=\"!isFieldVisible(field)\">\r\n <!-- Pass the root form group to the template -->\r\n <ng-container\r\n *ngTemplateOutlet=\"fieldTemplate; context: {field: field, group: form}\"></ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n </ng-template>\r\n\r\n </ng-container>\r\n\r\n</form>\r\n\r\n<!-- Reusable Field Template -->\r\n<ng-template #fieldTemplate let-field=\"field\" let-group=\"group\">\r\n <div [formGroup]=\"group\">\r\n\r\n <!-- Text / Email / Number / Tel / URL (Password removed) -->\r\n <div *ngIf=\"['text', 'email', 'number', 'tel', 'url'].includes(field.type || '')\" class=\"field-container\">\r\n <div class=\"field-label\" *ngIf=\"field.label\">\r\n {{ field.label }}\r\n <span class=\"text-danger\" *ngIf=\"field.required || field.mandatory\">*</span>\r\n </div>\r\n <div class=\"input-wrapper\">\r\n <input [type]=\"field.type\" [formControlName]=\"field.name\" [placeholder]=\"field.placeholder || ''\"\r\n [readonly]=\"field.readonly\" class=\"form-input\" [name]=\"field.name\"\r\n [class.is-invalid]=\"group.get(field.name)?.touched && group.get(field.name)?.invalid\"\r\n [maxlength]=\"field.validationRules?.maxLength || field.uiConfig?.maxCharacters\">\r\n <div class=\"input-suffix\">\r\n <span class=\"suffix-text\" *ngIf=\"field.suffixText\">{{ field.suffixText }}</span>\r\n <mat-icon *ngIf=\"field.suffixIcon\">{{ field.suffixIcon }}</mat-icon>\r\n <mat-icon *ngIf=\"field.icon\">{{ field.icon }}</mat-icon>\r\n <mat-icon *ngIf=\"field.readonly\" class=\"lock-icon\">lock_outline</mat-icon>\r\n </div>\r\n </div>\r\n <!-- Character count -->\r\n <div class=\"char-count-row\" *ngIf=\"field.uiConfig?.maxCharacters || field.validationRules?.maxLength\">\r\n <div class=\"hint-text\" *ngIf=\"field.hint || field.helpText\">{{ field.hint || field.helpText }}</div>\r\n <div class=\"char-count\">\r\n {{ getCharacterCount(field.name) }} / {{ field.uiConfig?.maxCharacters ||\r\n field.validationRules?.maxLength }} {{ labels.charactersLabel }}\r\n </div>\r\n </div>\r\n <div class=\"hint-text mt-2\"\r\n *ngIf=\"(field.hint || field.helpText) && !field.uiConfig?.maxCharacters && !field.validationRules?.maxLength\">\r\n {{ field.hint || field.helpText }}\r\n </div>\r\n </div>\r\n\r\n <!-- Password Field (Separated) -->\r\n <div *ngIf=\"field.type === 'password'\" class=\"field-container\">\r\n <div class=\"field-label\" *ngIf=\"field.label\">\r\n {{ field.label }}\r\n <span class=\"text-danger\" *ngIf=\"field.required || field.mandatory\">*</span>\r\n </div>\r\n <div class=\"input-wrapper\">\r\n <input [type]=\"isPasswordVisible(field.name) ? 'text' : 'password'\" [formControlName]=\"field.name\"\r\n [placeholder]=\"field.placeholder || ''\" [readonly]=\"field.readonly\" class=\"form-input\"\r\n [class.is-invalid]=\"group.get(field.name)?.touched && group.get(field.name)?.invalid\"\r\n [maxlength]=\"field.validationRules?.maxLength || field.uiConfig?.maxCharacters\">\r\n <div class=\"input-suffix\">\r\n <span class=\"suffix-text\" *ngIf=\"field.suffixText\">{{ field.suffixText }}</span>\r\n <mat-icon *ngIf=\"field.suffixIcon\">{{ field.suffixIcon }}</mat-icon>\r\n <mat-icon *ngIf=\"field.icon\">{{ field.icon }}</mat-icon>\r\n <mat-icon *ngIf=\"field.readonly\" class=\"lock-icon\">lock_outline</mat-icon>\r\n <mat-icon class=\"password-toggle-icon\" (click)=\"togglePassword(field.name)\">\r\n {{ isPasswordVisible(field.name) ? 'visibility' : 'visibility_off' }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n <!-- Character count -->\r\n <div class=\"char-count-row\" *ngIf=\"field.uiConfig?.maxCharacters || field.validationRules?.maxLength\">\r\n <div class=\"hint-text\" *ngIf=\"field.hint || field.helpText\">{{ field.hint || field.helpText }}</div>\r\n <div class=\"char-count\">\r\n {{ getCharacterCount(field.name) }} / {{ field.uiConfig?.maxCharacters ||\r\n field.validationRules?.maxLength }} {{ labels.charactersLabel }}\r\n </div>\r\n </div>\r\n <div class=\"hint-text mt-2\"\r\n *ngIf=\"(field.hint || field.helpText) && !field.uiConfig?.maxCharacters && !field.validationRules?.maxLength\">\r\n {{ field.hint || field.helpText }}\r\n </div>\r\n </div>\r\n\r\n <!-- Textarea -->\r\n <div *ngIf=\"field.type === 'textarea'\" class=\"field-container\">\r\n <div class=\"field-label\" *ngIf=\"field.label\">\r\n {{ field.label }}\r\n <span class=\"text-danger\" *ngIf=\"field.required || field.mandatory\">*</span>\r\n </div>\r\n <textarea [formControlName]=\"field.name\" [placeholder]=\"field.placeholder || ''\" [readonly]=\"field.readonly\"\r\n class=\"form-input form-textarea\" rows=\"4\"\r\n [class.is-invalid]=\"group.get(field.name)?.touched && group.get(field.name)?.invalid\"\r\n [maxlength]=\"field.validationRules?.maxLength || field.uiConfig?.maxCharacters\"></textarea>\r\n <!-- Character count -->\r\n <div class=\"char-count-row\" *ngIf=\"field.uiConfig?.maxCharacters || field.validationRules?.maxLength\">\r\n <div class=\"hint-text\" *ngIf=\"field.hint || field.helpText\">{{ field.hint || field.helpText }}</div>\r\n <div class=\"char-count\">\r\n {{ getCharacterCount(field.name) }} / {{ field.uiConfig?.maxCharacters ||\r\n field.validationRules?.maxLength }} {{ labels.charactersLabel }}\r\n </div>\r\n </div>\r\n <div class=\"hint-text mt-2\"\r\n *ngIf=\"(field.hint || field.helpText) && !field.uiConfig?.maxCharacters && !field.validationRules?.maxLength\">\r\n {{ field.hint || field.helpText }}\r\n </div>\r\n </div>\r\n\r\n <!-- Select / Dropdown -->\r\n <div *ngIf=\"field.type === 'select' || field.type === 'dropdown'\" class=\"field-container\">\r\n <div class=\"field-label\" *ngIf=\"field.label\">\r\n {{ field.label }}\r\n <span class=\"text-danger\" *ngIf=\"field.required || field.mandatory\">*</span>\r\n </div>\r\n <select [formControlName]=\"field.name\" class=\"form-input\"\r\n [class.is-invalid]=\"group.get(field.name)?.touched && group.get(field.name)?.invalid\">\r\n <option value=\"\" disabled selected *ngIf=\"field.placeholder\">{{ field.placeholder }}</option>\r\n <option value=\"\" disabled selected *ngIf=\"!field.placeholder\">{{ labels.selectDefaultPlaceholder }}\r\n </option>\r\n <option *ngFor=\"let opt of getFieldOptions(field)\" [value]=\"opt.value\">{{ opt.label }}</option>\r\n </select>\r\n <div class=\"hint-text mt-2\" *ngIf=\"field.hint || field.helpText\">{{ field.hint || field.helpText }}</div>\r\n </div>\r\n\r\n <!-- Date -->\r\n <div *ngIf=\"field.type === 'date'\" class=\"field-container\">\r\n <div class=\"field-label\" *ngIf=\"field.label\">\r\n {{ field.label }}\r\n <span class=\"text-danger\" *ngIf=\"field.required || field.mandatory\">*</span>\r\n </div>\r\n <div class=\"relative\">\r\n <input matInput [matDatepicker]=\"picker\" [formControlName]=\"field.name\"\r\n [placeholder]=\"field.placeholder || ''\" class=\"form-input\"\r\n [class.is-invalid]=\"group.get(field.name)?.touched && group.get(field.name)?.invalid\">\r\n <mat-datepicker-toggle matSuffix [for]=\"picker\" class=\"date-toggle\"></mat-datepicker-toggle>\r\n <mat-datepicker #picker></mat-datepicker>\r\n </div>\r\n <div class=\"hint-text mt-2\" *ngIf=\"field.hint || field.helpText\">{{ field.hint || field.helpText }}</div>\r\n </div>\r\n\r\n <!-- Radio -->\r\n <div *ngIf=\"field.type === 'radio'\" class=\"radio-group-container\"\r\n [class.is-invalid]=\"group.get(field.name)?.touched && group.get(field.name)?.invalid\">\r\n <label class=\"field-label\">\r\n {{ field.label }}\r\n <span class=\"text-danger\" *ngIf=\"field.required || field.mandatory\">*</span>\r\n </label>\r\n <mat-radio-group [formControlName]=\"field.name\" class=\"radio-group\">\r\n <mat-radio-button *ngFor=\"let opt of getFieldOptions(field)\" [value]=\"opt.value\" class=\"radio-button\">\r\n {{ opt.label }}\r\n </mat-radio-button>\r\n </mat-radio-group>\r\n <div class=\"hint-text mt-2\" *ngIf=\"field.hint || field.helpText\">{{ field.hint || field.helpText }}</div>\r\n </div>\r\n\r\n <!-- Composite Field (Sub-groups) -->\r\n <div *ngIf=\"field.type === 'composite'\" class=\"field-container\">\r\n <div class=\"field-label\" *ngIf=\"field.label\">\r\n {{ field.label }}\r\n <span class=\"text-danger\" *ngIf=\"field.required || field.mandatory\">*</span>\r\n </div>\r\n <div class=\"composite-container\">\r\n <ng-container *ngFor=\"let sub of field.subFields; let last = last\">\r\n <div class=\"composite-sub-field\" style=\"flex: 1;\">\r\n <div class=\"field-label\" *ngIf=\"sub.label\"\r\n style=\"font-size: 0.75rem; font-weight: normal; margin-bottom: 0.25rem;\">\r\n {{ sub.label }}\r\n </div>\r\n <div class=\"input-wrapper\">\r\n <input [type]=\"sub.type\" [formControlName]=\"sub.name\" [placeholder]=\"sub.placeholder || ''\"\r\n [readonly]=\"sub.readonly\" class=\"form-input\"\r\n [class.is-invalid]=\"group.get(sub.name)?.touched && group.get(sub.name)?.invalid\">\r\n <div class=\"input-suffix\" [class.color-suffix]=\"sub.suffixText === '%'\">\r\n <span class=\"suffix-text\" *ngIf=\"sub.suffixText\">{{ sub.suffixText }}</span>\r\n <mat-icon *ngIf=\"sub.readonly\" class=\"lock-icon\">lock_outline</mat-icon>\r\n </div>\r\n </div>\r\n </div>\r\n <!-- Adjust separator alignment if labels are present -->\r\n <span class=\"separator\" *ngIf=\"!last && field.separator\"\r\n [style.padding-top]=\"sub.label ? '1.25rem' : '0'\">{{ field.separator }}</span>\r\n </ng-container>\r\n </div>\r\n <div class=\"hint-text mt-2\" *ngIf=\"field.hint || field.helpText\">{{ field.hint || field.helpText }}</div>\r\n <div class=\"error-text text-danger text-small mt-1\" *ngIf=\"group.errors?.['invalid_minMax_' + field.name]\">\r\n {{ labels.errorMinValue }}\r\n </div>\r\n <div class=\"error-text text-danger text-small mt-1\"\r\n *ngIf=\"group.errors?.['invalid_percentageTotal_' + field.name]\">\r\n {{ labels.errorPercentageTotal }}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- File Upload -->\r\n <div *ngIf=\"field.type === 'file'\" class=\"field-container\">\r\n <div class=\"field-label\" *ngIf=\"field.label\">\r\n {{ field.label }}\r\n <span class=\"text-danger\" *ngIf=\"field.required || field.mandatory\">*</span>\r\n </div>\r\n\r\n <!-- Upload Box -->\r\n <div class=\"upload-box\" (click)=\"fileInput.click()\"\r\n [class.is-invalid]=\"group.get(field.name)?.touched && group.get(field.name)?.invalid\"\r\n *ngIf=\"!field.uploadedFiles || field.uploadedFiles.length === 0 || field.multiple\">\r\n <mat-icon class=\"upload-icon\">cloud_upload</mat-icon>\r\n <div class=\"upload-text\">{{ labels.uploadDragDropText }}</div>\r\n <div class=\"upload-hint\">{{ labels.uploadHint }}</div>\r\n <input #fileInput type=\"file\" [attr.multiple]=\"field.multiple ? true : null\" [attr.accept]=\"field.accept\"\r\n (change)=\"onFileChange($event, field)\" style=\"display: none;\">\r\n </div>\r\n\r\n <!-- Uploaded Files List -->\r\n <div class=\"uploaded-files-list\" *ngIf=\"field.uploadedFiles && field.uploadedFiles.length > 0\">\r\n <div class=\"uploaded-file-item\" *ngFor=\"let file of field.uploadedFiles; let i = index\">\r\n <div class=\"file-icon\">\r\n <mat-icon class=\"pdf-icon\" *ngIf=\"file.type === 'application/pdf'\">picture_as_pdf</mat-icon>\r\n <mat-icon class=\"doc-icon\"\r\n *ngIf=\"file.type.includes('word') || file.type.includes('document')\">description</mat-icon>\r\n <mat-icon class=\"img-icon\" *ngIf=\"file.type.includes('image')\">image</mat-icon>\r\n <mat-icon class=\"file-icon-default\"\r\n *ngIf=\"!file.type.includes('pdf') && !file.type.includes('word') && !file.type.includes('document') && !file.type.includes('image')\">insert_drive_file</mat-icon>\r\n </div>\r\n <div class=\"file-info\">\r\n <div class=\"file-name\">{{ file.name }}</div>\r\n <div class=\"file-size\">{{ (file.size / 1024).toFixed(2) }} KB</div>\r\n </div>\r\n <button mat-icon-button color=\"warn\" type=\"button\" (click)=\"removeFile(field, i)\"\r\n class=\"remove-file-btn\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n </div>\r\n</ng-template>", styles: [":host{display:block;width:100%}.configurable-form-container{padding:0;font-family:var(--cf-font-family, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif)}.configurable-form-container .section-card{background:var(--cf-surface-background, #ffffff);border:1px solid var(--cf-border-color, #D1D5DB);border-radius:var(--cf-section-radius, 12px);padding:var(--cf-section-padding, 2rem);margin-bottom:var(--cf-section-spacing, 1.5rem);box-shadow:var(--cf-section-shadow, 0 1px 3px 0 rgba(0, 0, 0, .1), 0 1px 2px 0 rgba(0, 0, 0, .06))}.configurable-form-container .section-card:last-of-type{margin-bottom:0}.configurable-form-container .section-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--cf-section-spacing, 1.5rem);padding-bottom:1rem;border-bottom:2px solid var(--cf-border-color, #D1D5DB)}.configurable-form-container .section-title{font-size:var(--cf-section-title-size, 1.25rem);font-weight:var(--cf-section-title-weight, 600);color:var(--cf-text-primary, #111827);margin:0;letter-spacing:-.025em}.configurable-form-container .form-grid{display:flex;flex-wrap:wrap;margin-left:-.625rem;margin-right:-.625rem}.configurable-form-container .form-col{position:relative;width:100%;padding-left:.625rem;padding-right:.625rem;margin-bottom:1.25rem}.configurable-form-container .col-1{flex:0 0 8.3333333333%;max-width:8.3333333333%}.configurable-form-container .col-2{flex:0 0 16.6666666667%;max-width:16.6666666667%}.configurable-form-container .col-3{flex:0 0 25%;max-width:25%}.configurable-form-container .col-4{flex:0 0 33.3333333333%;max-width:33.3333333333%}.configurable-form-container .col-5{flex:0 0 41.6666666667%;max-width:41.6666666667%}.configurable-form-container .col-6{flex:0 0 50%;max-width:50%}.configurable-form-container .col-7{flex:0 0 58.3333333333%;max-width:58.3333333333%}.configurable-form-container .col-8{flex:0 0 66.6666666667%;max-width:66.6666666667%}.configurable-form-container .col-9{flex:0 0 75%;max-width:75%}.configurable-form-container .col-10{flex:0 0 83.3333333333%;max-width:83.3333333333%}.configurable-form-container .col-11{flex:0 0 91.6666666667%;max-width:91.6666666667%}.configurable-form-container .col-12{flex:0 0 100%;max-width:100%}@media(min-width:769px){.configurable-form-container .col-md-1{flex:0 0 8.3333333333%;max-width:8.3333333333%}.configurable-form-container .col-md-2{flex:0 0 16.6666666667%;max-width:16.6666666667%}.configurable-form-container .col-md-3{flex:0 0 25%;max-width:25%}.configurable-form-container .col-md-4{flex:0 0 33.3333333333%;max-width:33.3333333333%}.configurable-form-container .col-md-5{flex:0 0 41.6666666667%;max-width:41.6666666667%}.configurable-form-container .col-md-6{flex:0 0 50%;max-width:50%}.configurable-form-container .col-md-7{flex:0 0 58.3333333333%;max-width:58.3333333333%}.configurable-form-container .col-md-8{flex:0 0 66.6666666667%;max-width:66.6666666667%}.configurable-form-container .col-md-9{flex:0 0 75%;max-width:75%}.configurable-form-container .col-md-10{flex:0 0 83.3333333333%;max-width:83.3333333333%}.configurable-form-container .col-md-11{flex:0 0 91.6666666667%;max-width:91.6666666667%}.configurable-form-container .col-md-12{flex:0 0 100%;max-width:100%}}.configurable-form-container .field-container{margin-bottom:0}.configurable-form-container .field-label{display:block;font-size:var(--cf-label-size, .875rem);font-weight:var(--cf-label-weight, 600);color:var(--cf-text-primary, #111827);margin-bottom:.5rem;line-height:1.25rem}.configurable-form-container .field-label .text-danger{color:var(--cf-error-color, #DC2626);margin-left:.125rem}.configurable-form-container .input-wrapper{position:relative;display:flex;align-items:center}.configurable-form-container .form-input{width:100%;padding:var(--cf-input-padding-y, .625rem) var(--cf-input-padding-x, .875rem);font-size:var(--cf-input-font-size, .875rem);line-height:1.5;color:var(--cf-text-primary, #111827);background-color:var(--cf-input-bg, #ffffff);border:1.5px solid var(--cf-border-color, #D1D5DB);border-radius:var(--cf-input-radius, 8px);transition:all .2s ease;font-family:inherit}.configurable-form-container .form-input:hover:not(:disabled):not([readonly]){border-color:var(--cf-input-hover-border-color, #9CA3AF)}.configurable-form-container .form-input:focus{outline:none;border-color:var(--cf-primary-color, #3B82F6);box-shadow:0 0 0 3px #3b82f61a}.configurable-form-container .form-input::placeholder{color:#9ca3af}.configurable-form-container .form-input:disabled,.configurable-form-container .form-input[readonly]{background-color:var(--cf-disabled-background, #F3F4F6);color:var(--cf-text-secondary, #6B7280);cursor:not-allowed;border-color:#e5e7eb}.configurable-form-container .form-input.is-invalid{border-color:var(--cf-error-color, #DC2626);background-color:#fef2f2}.configurable-form-container .form-input.is-invalid:focus{box-shadow:0 0 0 3px #dc26261a}.configurable-form-container select.form-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}.configurable-form-container select.form-input:disabled{cursor:not-allowed}.configurable-form-container .form-textarea{resize:vertical;min-height:100px;font-family:inherit}.configurable-form-container .input-suffix{position:absolute;right:.75rem;display:flex;align-items:center;gap:.5rem;pointer-events:none}.configurable-form-container .input-suffix .password-toggle-icon{pointer-events:auto;cursor:pointer}.configurable-form-container .input-suffix .password-toggle-icon:hover{color:var(--cf-text-primary, #111827)}.configurable-form-container .input-suffix .suffix-text{font-size:.875rem;color:var(--cf-text-secondary, #6B7280);font-weight:500}.configurable-form-container .input-suffix mat-icon{font-size:1.25rem;width:1.25rem;height:1.25rem;color:var(--cf-text-secondary, #6B7280)}.configurable-form-container .input-suffix .lock-icon{color:#9ca3af}.configurable-form-container .input-suffix.color-suffix .suffix-text{color:var(--cf-primary-color, #3B82F6);font-weight:600}.configurable-form-container .char-count-row{display:flex;justify-content:space-between;align-items:center;margin-top:.5rem}.configurable-form-container .char-count{font-size:.75rem;color:var(--cf-text-secondary, #6B7280);font-weight:500}.configurable-form-container .hint-text{font-size:var(--cf-hint-size, .75rem);color:var(--cf-text-secondary, #6B7280);margin-top:.375rem;line-height:1.25rem}.configurable-form-container .mt-2{margin-top:.5rem}.configurable-form-container .radio-group-container.is-invalid .field-label{color:var(--cf-error-color, #DC2626)}.configurable-form-container .radio-group{display:flex;gap:2rem;flex-wrap:wrap;margin-top:.5rem}.configurable-form-container .radio-button{margin:0}.configurable-form-container .composite-container{display:flex;align-items:center;gap:.75rem}.configurable-form-container .composite-container .separator{font-size:.875rem;color:var(--cf-text-secondary, #6B7280);font-weight:500}.configurable-form-container .composite-container .input-wrapper{flex:1}.configurable-form-container .upload-box{border:2px dashed var(--cf-border-color, #D1D5DB);border-radius:var(--cf-section-radius, 12px);padding:2.5rem;text-align:center;cursor:pointer;transition:all .2s ease;background-color:var(--cf-hover-background, #F9FAFB)}.configurable-form-container .upload-box:hover{border-color:var(--cf-primary-color, #3B82F6);background-color:#3b82f608}.configurable-form-container .upload-box.is-invalid{border-color:var(--cf-error-color, #DC2626);background-color:#fef2f2}.configurable-form-container .upload-box .upload-icon{font-size:3rem;width:3rem;height:3rem;color:var(--cf-primary-color, #3B82F6);margin-bottom:.75rem}.configurable-form-container .upload-box .upload-text{font-size:.875rem;color:var(--cf-text-primary, #111827);font-weight:500;margin-bottom:.375rem}.configurable-form-container .upload-box .upload-hint{font-size:.75rem;color:var(--cf-text-secondary, #6B7280)}.configurable-form-container .uploaded-files-list{margin-top:1rem}.configurable-form-container .uploaded-file-item{display:flex;align-items:center;gap:.75rem;padding:.875rem;border:1.5px solid var(--cf-border-color, #D1D5DB);border-radius:8px;margin-bottom:.5rem;background-color:var(--cf-hover-background, #F9FAFB);transition:all .2s ease}.configurable-form-container .uploaded-file-item:hover{border-color:#9ca3af;box-shadow:0 1px 3px #0000001a}.configurable-form-container .uploaded-file-item .file-icon mat-icon{font-size:2rem;width:2rem;height:2rem}.configurable-form-container .uploaded-file-item .file-icon mat-icon.pdf-icon{color:#dc2626}.configurable-form-container .uploaded-file-item .file-icon mat-icon.doc-icon{color:#2563eb}.configurable-form-container .uploaded-file-item .file-icon mat-icon.img-icon{color:#059669}.configurable-form-container .uploaded-file-item .file-icon mat-icon.file-icon-default{color:var(--cf-text-secondary, #6B7280)}.configurable-form-container .uploaded-file-item .file-info{flex:1}.configurable-form-container .uploaded-file-item .file-info .file-name{font-size:.875rem;color:var(--cf-text-primary, #111827);font-weight:500;margin-bottom:.125rem}.configurable-form-container .uploaded-file-item .file-info .file-size{font-size:.75rem;color:var(--cf-text-secondary, #6B7280)}.configurable-form-container .uploaded-file-item .remove-file-btn mat-icon{font-size:1.25rem;width:1.25rem;height:1.25rem}.configurable-form-container .relative{position:relative}.configurable-form-container .date-toggle{position:absolute;right:0;top:50%;transform:translateY(-50%)}.configurable-form-container .repeater-item{padding:1.5rem;border:1.5px solid var(--cf-border-color, #D1D5DB);border-radius:10px;margin-bottom:1rem;background-color:var(--cf-hover-background, #F9FAFB);transition:all .2s ease}.configurable-form-container .repeater-item:hover{border-color:#9ca3af;box-shadow:0 2px 4px #0000000d}.configurable-form-container .repeater-item-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:1.25rem;padding-bottom:.75rem;border-bottom:1.5px solid var(--cf-border-color, #D1D5DB)}.configurable-form-container .repeater-item-header .text-small{font-size:.875rem;font-weight:600;color:var(--cf-text-primary, #111827);text-transform:uppercase;letter-spacing:.025em}.configurable-form-container .repeater-separator{height:1px;background-color:var(--cf-border-color, #D1D5DB);margin:1.5rem 0}.configurable-form-container .add-btn{margin-top:1rem;display:inline-flex;align-items:center;gap:.5rem;padding:.625rem 1.25rem;background-color:transparent;color:var(--cf-primary-color, #3B82F6);border:1.5px solid var(--cf-primary-color, #3B82F6);border-radius:8px;font-size:.875rem;font-weight:600;cursor:pointer;transition:all .2s ease}.configurable-form-container .add-btn:hover:not(:disabled){background-color:var(--cf-primary-color, #3B82F6);color:#fff}.configurable-form-container .add-btn:disabled{opacity:.5;cursor:not-allowed;border-color:var(--cf-disabled-background, #E5E7EB)}.configurable-form-container .add-btn mat-icon{font-size:1.25rem;width:1.25rem;height:1.25rem}@media(max-width:768px){.configurable-form-container .section-card{padding:1.25rem;border-radius:8px}.configurable-form-container .form-grid{margin-left:-.5rem;margin-right:-.5rem}.configurable-form-container .form-col{padding-left:.5rem;padding-right:.5rem;margin-bottom:1rem}.configurable-form-container .radio-group{flex-direction:column;gap:.75rem}.configurable-form-container .composite-container{flex-direction:column;align-items:stretch}.configurable-form-container .composite-container .separator{text-align:center}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1$2.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "directive", type: i1$2.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "ngmodule", type: MaterialModule }, { kind: "directive", type: i5.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: i6.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i6.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { 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: i10.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i10.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }] });
12622
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: ConfigurableFormComponent, isStandalone: true, selector: "lib-configurable-form", inputs: { config: "config", jsonConfig: "jsonConfig", data: "data", baseApiUrl: "baseApiUrl", labels: "labels" }, outputs: { optionsLoad: "optionsLoad" }, usesOnChanges: true, ngImport: i0, template: "<form [formGroup]=\"form\" class=\"configurable-form-container\" *ngIf=\"form\">\r\n\r\n <ng-container *ngFor=\"let section of sections\">\r\n\r\n <!-- Repeater Section -->\r\n <div *ngIf=\"section.isRepeater; else normalSection\"\r\n [ngClass]=\"section.noCardLayout ? 'section-no-card' : 'section-card'\">\r\n <div class=\"section-header\" *ngIf=\"section.sectionTitle\">\r\n <h3 class=\"section-title\">{{ section.sectionTitle }}</h3>\r\n <button *ngIf=\"section.collapsible\" mat-icon-button type=\"button\" (click)=\"toggleSection(section)\">\r\n <mat-icon>{{ section.collapsed ? 'expand_more' : 'expand_less' }}</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <div [formArrayName]=\"section.formArrayName || 'items'\" *ngIf=\"!section.collapsed\">\r\n <div *ngFor=\"let item of getFormArray(section.formArrayName!).controls; let i = index\"\r\n [formGroupName]=\"i\" class=\"repeater-item\">\r\n <div class=\"repeater-item-header\" *ngIf=\"getFormArray(section.formArrayName!).length > 1\">\r\n <span class=\"text-small\">{{ section.repeaterItemLabel || labels.repeaterItemDefaultLabel }} {{i\r\n + 1}}</span>\r\n <button mat-icon-button color=\"warn\" type=\"button\"\r\n (click)=\"removeRepeaterItem(section.formArrayName!, i)\"\r\n [disabled]=\"section.minItems && getFormArray(section.formArrayName!).length <= section.minItems\">\r\n <mat-icon>delete</mat-icon>\r\n </button>\r\n </div>\r\n\r\n <!-- Fields Grid -->\r\n <div class=\"form-grid\">\r\n <div *ngFor=\"let field of section.fields\" [ngClass]=\"['form-col', field.class || 'col-12']\"\r\n [hidden]=\"!isFieldVisible(field)\">\r\n <ng-container\r\n *ngTemplateOutlet=\"fieldTemplate; context: {field: field, group: item}\"></ng-container>\r\n </div>\r\n </div>\r\n\r\n <div class=\"repeater-separator\" *ngIf=\"i < getFormArray(section.formArrayName!).length - 1\"></div>\r\n </div>\r\n </div>\r\n\r\n <button mat-button color=\"primary\" type=\"button\" (click)=\"addRepeaterItem(section)\" class=\"add-btn\"\r\n *ngIf=\"!section.collapsed\"\r\n [disabled]=\"section.maxItems && getFormArray(section.formArrayName!).length >= section.maxItems\">\r\n <mat-icon>add_circle_outline</mat-icon> {{ section.addLabel || labels.addMoreDefaultLabel }}\r\n </button>\r\n </div>\r\n\r\n <!-- Normal Section -->\r\n <ng-template #normalSection>\r\n <div [ngClass]=\"section.noCardLayout ? 'section-no-card' : 'section-card'\">\r\n <div class=\"section-header\" *ngIf=\"section.sectionTitle\">\r\n <h3 class=\"section-title\">{{ section.sectionTitle }}</h3>\r\n <button *ngIf=\"section.collapsible\" mat-icon-button type=\"button\" (click)=\"toggleSection(section)\">\r\n <mat-icon>{{ section.collapsed ? 'expand_more' : 'expand_less' }}</mat-icon>\r\n </button>\r\n </div>\r\n <div class=\"form-grid\" *ngIf=\"!section.collapsed\">\r\n <div *ngFor=\"let field of section.fields\" [ngClass]=\"['form-col', field.class || 'col-12']\"\r\n [hidden]=\"!isFieldVisible(field)\">\r\n <!-- Pass the root form group to the template -->\r\n <ng-container\r\n *ngTemplateOutlet=\"fieldTemplate; context: {field: field, group: form}\"></ng-container>\r\n </div>\r\n </div>\r\n </div>\r\n </ng-template>\r\n\r\n </ng-container>\r\n\r\n</form>\r\n\r\n<!-- Reusable Field Template -->\r\n<ng-template #fieldTemplate let-field=\"field\" let-group=\"group\">\r\n <div [formGroup]=\"group\">\r\n\r\n <!-- Text / Email / Number / Tel / URL (Password removed) -->\r\n <div *ngIf=\"['text', 'email', 'number', 'tel', 'url'].includes(field.type || '')\" class=\"field-container\">\r\n <div class=\"field-label\" *ngIf=\"field.label\">\r\n {{ field.label }}\r\n <span class=\"text-danger\" *ngIf=\"field.required || field.mandatory\">*</span>\r\n </div>\r\n <div class=\"input-wrapper\">\r\n <input [type]=\"field.type\" [formControlName]=\"field.name\" [placeholder]=\"field.placeholder || ''\"\r\n [readonly]=\"field.readonly\" class=\"form-input\" [name]=\"field.name\"\r\n [class.is-invalid]=\"group.get(field.name)?.touched && group.get(field.name)?.invalid\"\r\n [maxlength]=\"field.validationRules?.maxLength || field.uiConfig?.maxCharacters\">\r\n <div class=\"input-suffix\">\r\n <span class=\"suffix-text\" *ngIf=\"field.suffixText\">{{ field.suffixText }}</span>\r\n <mat-icon *ngIf=\"field.suffixIcon\">{{ field.suffixIcon }}</mat-icon>\r\n <mat-icon *ngIf=\"field.icon\">{{ field.icon }}</mat-icon>\r\n <mat-icon *ngIf=\"field.readonly\" class=\"lock-icon\">lock_outline</mat-icon>\r\n </div>\r\n </div>\r\n <!-- Character count -->\r\n <div class=\"char-count-row\" *ngIf=\"field.uiConfig?.maxCharacters || field.validationRules?.maxLength\">\r\n <div class=\"hint-text\" *ngIf=\"field.hint || field.helpText\">{{ field.hint || field.helpText }}</div>\r\n <div class=\"char-count\">\r\n {{ getCharacterCount(field.name) }} / {{ field.uiConfig?.maxCharacters ||\r\n field.validationRules?.maxLength }} {{ labels.charactersLabel }}\r\n </div>\r\n </div>\r\n <div class=\"hint-text mt-2\"\r\n *ngIf=\"(field.hint || field.helpText) && !field.uiConfig?.maxCharacters && !field.validationRules?.maxLength\">\r\n {{ field.hint || field.helpText }}\r\n </div>\r\n </div>\r\n\r\n <!-- Password Field (Separated) -->\r\n <div *ngIf=\"field.type === 'password'\" class=\"field-container\">\r\n <div class=\"field-label\" *ngIf=\"field.label\">\r\n {{ field.label }}\r\n <span class=\"text-danger\" *ngIf=\"field.required || field.mandatory\">*</span>\r\n </div>\r\n <div class=\"input-wrapper\">\r\n <input [type]=\"isPasswordVisible(field.name) ? 'text' : 'password'\" [formControlName]=\"field.name\"\r\n [placeholder]=\"field.placeholder || ''\" [readonly]=\"field.readonly\" class=\"form-input\"\r\n [class.is-invalid]=\"group.get(field.name)?.touched && group.get(field.name)?.invalid\"\r\n [maxlength]=\"field.validationRules?.maxLength || field.uiConfig?.maxCharacters\">\r\n <div class=\"input-suffix\">\r\n <span class=\"suffix-text\" *ngIf=\"field.suffixText\">{{ field.suffixText }}</span>\r\n <mat-icon *ngIf=\"field.suffixIcon\">{{ field.suffixIcon }}</mat-icon>\r\n <mat-icon *ngIf=\"field.icon\">{{ field.icon }}</mat-icon>\r\n <mat-icon *ngIf=\"field.readonly\" class=\"lock-icon\">lock_outline</mat-icon>\r\n <mat-icon class=\"password-toggle-icon\" (click)=\"togglePassword(field.name)\">\r\n {{ isPasswordVisible(field.name) ? 'visibility' : 'visibility_off' }}\r\n </mat-icon>\r\n </div>\r\n </div>\r\n <!-- Character count -->\r\n <div class=\"char-count-row\" *ngIf=\"field.uiConfig?.maxCharacters || field.validationRules?.maxLength\">\r\n <div class=\"hint-text\" *ngIf=\"field.hint || field.helpText\">{{ field.hint || field.helpText }}</div>\r\n <div class=\"char-count\">\r\n {{ getCharacterCount(field.name) }} / {{ field.uiConfig?.maxCharacters ||\r\n field.validationRules?.maxLength }} {{ labels.charactersLabel }}\r\n </div>\r\n </div>\r\n <div class=\"hint-text mt-2\"\r\n *ngIf=\"(field.hint || field.helpText) && !field.uiConfig?.maxCharacters && !field.validationRules?.maxLength\">\r\n {{ field.hint || field.helpText }}\r\n </div>\r\n </div>\r\n\r\n <!-- Textarea -->\r\n <div *ngIf=\"field.type === 'textarea'\" class=\"field-container\">\r\n <div class=\"field-label\" *ngIf=\"field.label\">\r\n {{ field.label }}\r\n <span class=\"text-danger\" *ngIf=\"field.required || field.mandatory\">*</span>\r\n </div>\r\n <textarea [formControlName]=\"field.name\" [placeholder]=\"field.placeholder || ''\" [readonly]=\"field.readonly\"\r\n class=\"form-input form-textarea\" rows=\"4\"\r\n [class.is-invalid]=\"group.get(field.name)?.touched && group.get(field.name)?.invalid\"\r\n [maxlength]=\"field.validationRules?.maxLength || field.uiConfig?.maxCharacters\"></textarea>\r\n <!-- Character count -->\r\n <div class=\"char-count-row\" *ngIf=\"field.uiConfig?.maxCharacters || field.validationRules?.maxLength\">\r\n <div class=\"hint-text\" *ngIf=\"field.hint || field.helpText\">{{ field.hint || field.helpText }}</div>\r\n <div class=\"char-count\">\r\n {{ getCharacterCount(field.name) }} / {{ field.uiConfig?.maxCharacters ||\r\n field.validationRules?.maxLength }} {{ labels.charactersLabel }}\r\n </div>\r\n </div>\r\n <div class=\"hint-text mt-2\"\r\n *ngIf=\"(field.hint || field.helpText) && !field.uiConfig?.maxCharacters && !field.validationRules?.maxLength\">\r\n {{ field.hint || field.helpText }}\r\n </div>\r\n </div>\r\n\r\n <!-- Select / Dropdown -->\r\n <div *ngIf=\"field.type === 'select' || field.type === 'dropdown'\" class=\"field-container\">\r\n <div class=\"field-label\" *ngIf=\"field.label\">\r\n {{ field.label }}\r\n <span class=\"text-danger\" *ngIf=\"field.required || field.mandatory\">*</span>\r\n </div>\r\n <select [formControlName]=\"field.name\" class=\"form-input\"\r\n [class.is-invalid]=\"group.get(field.name)?.touched && group.get(field.name)?.invalid\">\r\n <option value=\"\" disabled selected *ngIf=\"field.placeholder\">{{ field.placeholder }}</option>\r\n <option value=\"\" disabled selected *ngIf=\"!field.placeholder\">{{ labels.selectDefaultPlaceholder }}\r\n </option>\r\n <option *ngFor=\"let opt of getFieldOptions(field)\" [value]=\"opt.value\">{{ opt.label }}</option>\r\n </select>\r\n <div class=\"hint-text mt-2\" *ngIf=\"field.hint || field.helpText\">{{ field.hint || field.helpText }}</div>\r\n </div>\r\n\r\n <!-- Date -->\r\n <div *ngIf=\"field.type === 'date'\" class=\"field-container\">\r\n <div class=\"field-label\" *ngIf=\"field.label\">\r\n {{ field.label }}\r\n <span class=\"text-danger\" *ngIf=\"field.required || field.mandatory\">*</span>\r\n </div>\r\n <div class=\"relative\">\r\n <input matInput [matDatepicker]=\"picker\" [formControlName]=\"field.name\"\r\n [placeholder]=\"field.placeholder || ''\" class=\"form-input\"\r\n [class.is-invalid]=\"group.get(field.name)?.touched && group.get(field.name)?.invalid\">\r\n <mat-datepicker-toggle matSuffix [for]=\"picker\" class=\"date-toggle\"></mat-datepicker-toggle>\r\n <mat-datepicker #picker></mat-datepicker>\r\n </div>\r\n <div class=\"hint-text mt-2\" *ngIf=\"field.hint || field.helpText\">{{ field.hint || field.helpText }}</div>\r\n </div>\r\n\r\n <!-- Radio -->\r\n <div *ngIf=\"field.type === 'radio'\" class=\"radio-group-container\"\r\n [class.is-invalid]=\"group.get(field.name)?.touched && group.get(field.name)?.invalid\">\r\n <label class=\"field-label\">\r\n {{ field.label }}\r\n <span class=\"text-danger\" *ngIf=\"field.required || field.mandatory\">*</span>\r\n </label>\r\n <mat-radio-group [formControlName]=\"field.name\" class=\"radio-group\">\r\n <mat-radio-button *ngFor=\"let opt of getFieldOptions(field)\" [value]=\"opt.value\" class=\"radio-button\">\r\n {{ opt.label }}\r\n </mat-radio-button>\r\n </mat-radio-group>\r\n <div class=\"hint-text mt-2\" *ngIf=\"field.hint || field.helpText\">{{ field.hint || field.helpText }}</div>\r\n </div>\r\n\r\n <!-- Composite Field (Sub-groups) -->\r\n <div *ngIf=\"field.type === 'composite'\" class=\"field-container\">\r\n <div class=\"field-label\" *ngIf=\"field.label\">\r\n {{ field.label }}\r\n <span class=\"text-danger\" *ngIf=\"field.required || field.mandatory\">*</span>\r\n </div>\r\n <div class=\"composite-container\">\r\n <ng-container *ngFor=\"let sub of field.subFields; let last = last\">\r\n <div class=\"composite-sub-field\" style=\"flex: 1;\">\r\n <div class=\"field-label\" *ngIf=\"sub.label\"\r\n style=\"font-size: 0.75rem; font-weight: normal; margin-bottom: 0.25rem;\">\r\n {{ sub.label }}\r\n </div>\r\n <div class=\"input-wrapper\">\r\n <input [type]=\"sub.type\" [formControlName]=\"sub.name\" [placeholder]=\"sub.placeholder || ''\"\r\n [readonly]=\"sub.readonly\" class=\"form-input\"\r\n [class.is-invalid]=\"group.get(sub.name)?.touched && group.get(sub.name)?.invalid\">\r\n <div class=\"input-suffix\" [class.color-suffix]=\"sub.suffixText === '%'\">\r\n <span class=\"suffix-text\" *ngIf=\"sub.suffixText\">{{ sub.suffixText }}</span>\r\n <mat-icon *ngIf=\"sub.readonly\" class=\"lock-icon\">lock_outline</mat-icon>\r\n </div>\r\n </div>\r\n </div>\r\n <!-- Adjust separator alignment if labels are present -->\r\n <span class=\"separator\" *ngIf=\"!last && field.separator\"\r\n [style.padding-top]=\"sub.label ? '1.25rem' : '0'\">{{ field.separator }}</span>\r\n </ng-container>\r\n </div>\r\n <div class=\"hint-text mt-2\" *ngIf=\"field.hint || field.helpText\">{{ field.hint || field.helpText }}</div>\r\n <div class=\"error-text text-danger text-small mt-1\" *ngIf=\"group.errors?.['invalid_minMax_' + field.name]\">\r\n {{ labels.errorMinValue }}\r\n </div>\r\n <div class=\"error-text text-danger text-small mt-1\"\r\n *ngIf=\"group.errors?.['invalid_percentageTotal_' + field.name]\">\r\n {{ labels.errorPercentageTotal }}\r\n </div>\r\n </div>\r\n </div>\r\n\r\n <!-- File Upload -->\r\n <div *ngIf=\"field.type === 'file'\" class=\"field-container\">\r\n <div class=\"field-label\" *ngIf=\"field.label\">\r\n {{ field.label }}\r\n <span class=\"text-danger\" *ngIf=\"field.required || field.mandatory\">*</span>\r\n </div>\r\n\r\n <!-- Upload Box -->\r\n <div class=\"upload-box\" (click)=\"fileInput.click()\"\r\n [class.is-invalid]=\"group.get(field.name)?.touched && group.get(field.name)?.invalid\"\r\n *ngIf=\"!field.uploadedFiles || field.uploadedFiles.length === 0 || field.multiple\">\r\n <mat-icon class=\"upload-icon\">cloud_upload</mat-icon>\r\n <div class=\"upload-text\">{{ labels.uploadDragDropText }}</div>\r\n <div class=\"upload-hint\">{{ labels.uploadHint }}</div>\r\n <input #fileInput type=\"file\" [attr.multiple]=\"field.multiple ? true : null\" [attr.accept]=\"field.accept\"\r\n (change)=\"onFileChange($event, field)\" style=\"display: none;\">\r\n </div>\r\n\r\n <!-- Uploaded Files List -->\r\n <div class=\"uploaded-files-list\" *ngIf=\"field.uploadedFiles && field.uploadedFiles.length > 0\">\r\n <div class=\"uploaded-file-item\" *ngFor=\"let file of field.uploadedFiles; let i = index\">\r\n <div class=\"file-icon\">\r\n <mat-icon class=\"pdf-icon\" *ngIf=\"file.type === 'application/pdf'\">picture_as_pdf</mat-icon>\r\n <mat-icon class=\"doc-icon\"\r\n *ngIf=\"file.type.includes('word') || file.type.includes('document')\">description</mat-icon>\r\n <mat-icon class=\"img-icon\" *ngIf=\"file.type.includes('image')\">image</mat-icon>\r\n <mat-icon class=\"file-icon-default\"\r\n *ngIf=\"!file.type.includes('pdf') && !file.type.includes('word') && !file.type.includes('document') && !file.type.includes('image')\">insert_drive_file</mat-icon>\r\n </div>\r\n <div class=\"file-info\">\r\n <div class=\"file-name\">{{ file.name }}</div>\r\n <div class=\"file-size\">{{ (file.size / 1024).toFixed(2) }} KB</div>\r\n </div>\r\n <button mat-icon-button color=\"warn\" type=\"button\" (click)=\"removeFile(field, i)\"\r\n class=\"remove-file-btn\">\r\n <mat-icon>close</mat-icon>\r\n </button>\r\n </div>\r\n </div>\r\n\r\n </div>\r\n</ng-template>", styles: [":host{display:block;width:100%}.configurable-form-container{padding:0;font-family:var(--cf-font-family, -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Helvetica Neue\", Arial, sans-serif)}.configurable-form-container .section-card{background:var(--cf-surface-background, #ffffff);border:1px solid var(--cf-border-color, #D1D5DB);border-radius:var(--cf-section-radius, 12px);padding:var(--cf-section-padding, 2rem);margin-bottom:var(--cf-section-spacing, 1.5rem);box-shadow:var(--cf-section-shadow, 0 1px 3px 0 rgba(0, 0, 0, .1), 0 1px 2px 0 rgba(0, 0, 0, .06))}.configurable-form-container .section-card:last-of-type{margin-bottom:0}.configurable-form-container .section-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:var(--cf-section-spacing, 1.5rem);padding-bottom:1rem;border-bottom:2px solid var(--cf-border-color, #D1D5DB)}.configurable-form-container .section-title{font-size:var(--cf-section-title-size, 1.25rem);font-weight:var(--cf-section-title-weight, 600);color:var(--cf-text-primary, #111827);margin:0;letter-spacing:-.025em}.configurable-form-container .form-grid{display:flex;flex-wrap:wrap;margin-left:-.625rem;margin-right:-.625rem}.configurable-form-container .form-col{position:relative;width:100%;padding-left:.625rem;padding-right:.625rem;margin-bottom:1.25rem}.configurable-form-container .col-1{flex:0 0 8.3333333333%;max-width:8.3333333333%}.configurable-form-container .col-2{flex:0 0 16.6666666667%;max-width:16.6666666667%}.configurable-form-container .col-3{flex:0 0 25%;max-width:25%}.configurable-form-container .col-4{flex:0 0 33.3333333333%;max-width:33.3333333333%}.configurable-form-container .col-5{flex:0 0 41.6666666667%;max-width:41.6666666667%}.configurable-form-container .col-6{flex:0 0 50%;max-width:50%}.configurable-form-container .col-7{flex:0 0 58.3333333333%;max-width:58.3333333333%}.configurable-form-container .col-8{flex:0 0 66.6666666667%;max-width:66.6666666667%}.configurable-form-container .col-9{flex:0 0 75%;max-width:75%}.configurable-form-container .col-10{flex:0 0 83.3333333333%;max-width:83.3333333333%}.configurable-form-container .col-11{flex:0 0 91.6666666667%;max-width:91.6666666667%}.configurable-form-container .col-12{flex:0 0 100%;max-width:100%}@media(min-width:769px){.configurable-form-container .col-md-1{flex:0 0 8.3333333333%;max-width:8.3333333333%}.configurable-form-container .col-md-2{flex:0 0 16.6666666667%;max-width:16.6666666667%}.configurable-form-container .col-md-3{flex:0 0 25%;max-width:25%}.configurable-form-container .col-md-4{flex:0 0 33.3333333333%;max-width:33.3333333333%}.configurable-form-container .col-md-5{flex:0 0 41.6666666667%;max-width:41.6666666667%}.configurable-form-container .col-md-6{flex:0 0 50%;max-width:50%}.configurable-form-container .col-md-7{flex:0 0 58.3333333333%;max-width:58.3333333333%}.configurable-form-container .col-md-8{flex:0 0 66.6666666667%;max-width:66.6666666667%}.configurable-form-container .col-md-9{flex:0 0 75%;max-width:75%}.configurable-form-container .col-md-10{flex:0 0 83.3333333333%;max-width:83.3333333333%}.configurable-form-container .col-md-11{flex:0 0 91.6666666667%;max-width:91.6666666667%}.configurable-form-container .col-md-12{flex:0 0 100%;max-width:100%}}.configurable-form-container .field-container{margin-bottom:0}.configurable-form-container .field-label{display:block;font-size:var(--cf-label-size, .875rem);font-weight:var(--cf-label-weight, 600);color:var(--cf-text-primary, #111827);margin-bottom:.5rem;line-height:1.25rem}.configurable-form-container .field-label .text-danger{color:var(--cf-error-color, #DC2626);margin-left:.125rem}.configurable-form-container .input-wrapper{position:relative;display:flex;align-items:center}.configurable-form-container .form-input{width:100%;padding:var(--cf-input-padding-y, .625rem) var(--cf-input-padding-x, .875rem);font-size:var(--cf-input-font-size, .875rem);line-height:1.5;color:var(--cf-text-primary, #111827);background-color:var(--cf-input-bg, #ffffff);border:1.5px solid var(--cf-border-color, #D1D5DB);border-radius:var(--cf-input-radius, 8px);transition:all .2s ease;font-family:inherit}.configurable-form-container .form-input:hover:not(:disabled):not([readonly]){border-color:var(--cf-input-hover-border-color, #9CA3AF)}.configurable-form-container .form-input:focus{outline:none;border-color:var(--cf-primary-color, #3B82F6);box-shadow:0 0 0 3px #3b82f61a}.configurable-form-container .form-input::placeholder{color:#9ca3af}.configurable-form-container .form-input:disabled,.configurable-form-container .form-input[readonly]{background-color:var(--cf-disabled-background, #F3F4F6);color:var(--cf-text-secondary, #6B7280);cursor:not-allowed;border-color:#e5e7eb}.configurable-form-container .form-input.is-invalid{border-color:var(--cf-error-color, #DC2626);background-color:#fef2f2}.configurable-form-container .form-input.is-invalid:focus{box-shadow:0 0 0 3px #dc26261a}.configurable-form-container select.form-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}.configurable-form-container select.form-input:disabled{cursor:not-allowed}.configurable-form-container .form-textarea{resize:vertical;min-height:100px;font-family:inherit}.configurable-form-container .input-suffix{position:absolute;right:.75rem;display:flex;align-items:center;gap:.5rem;pointer-events:none}.configurable-form-container .input-suffix .password-toggle-icon{pointer-events:auto;cursor:pointer}.configurable-form-container .input-suffix .password-toggle-icon:hover{color:var(--cf-text-primary, #111827)}.configurable-form-container .input-suffix .suffix-text{font-size:.875rem;color:var(--cf-text-secondary, #6B7280);font-weight:500}.configurable-form-container .input-suffix mat-icon{font-size:1.25rem;width:1.25rem;height:1.25rem;color:var(--cf-text-secondary, #6B7280)}.configurable-form-container .input-suffix .lock-icon{color:#9ca3af}.configurable-form-container .input-suffix.color-suffix .suffix-text{color:var(--cf-primary-color, #3B82F6);font-weight:600}.configurable-form-container .char-count-row{display:flex;justify-content:space-between;align-items:center;margin-top:.5rem}.configurable-form-container .char-count{font-size:.75rem;color:var(--cf-text-secondary, #6B7280);font-weight:500}.configurable-form-container .hint-text{font-size:var(--cf-hint-size, .75rem);color:var(--cf-text-secondary, #6B7280);margin-top:.375rem;line-height:1.25rem}.configurable-form-container .mt-2{margin-top:.5rem}.configurable-form-container .radio-group-container.is-invalid .field-label{color:var(--cf-error-color, #DC2626)}.configurable-form-container .radio-group{display:flex;gap:2rem;flex-wrap:wrap;margin-top:.5rem}.configurable-form-container .radio-button{margin:0}.configurable-form-container .composite-container{display:flex;align-items:center;gap:.75rem}.configurable-form-container .composite-container .separator{font-size:.875rem;color:var(--cf-text-secondary, #6B7280);font-weight:500}.configurable-form-container .composite-container .input-wrapper{flex:1}.configurable-form-container .upload-box{border:2px dashed var(--cf-border-color, #D1D5DB);border-radius:var(--cf-section-radius, 12px);padding:2.5rem;text-align:center;cursor:pointer;transition:all .2s ease;background-color:var(--cf-hover-background, #F9FAFB)}.configurable-form-container .upload-box:hover{border-color:var(--cf-primary-color, #3B82F6);background-color:#3b82f608}.configurable-form-container .upload-box.is-invalid{border-color:var(--cf-error-color, #DC2626);background-color:#fef2f2}.configurable-form-container .upload-box .upload-icon{font-size:3rem;width:3rem;height:3rem;color:var(--cf-primary-color, #3B82F6);margin-bottom:.75rem}.configurable-form-container .upload-box .upload-text{font-size:.875rem;color:var(--cf-text-primary, #111827);font-weight:500;margin-bottom:.375rem}.configurable-form-container .upload-box .upload-hint{font-size:.75rem;color:var(--cf-text-secondary, #6B7280)}.configurable-form-container .uploaded-files-list{margin-top:1rem}.configurable-form-container .uploaded-file-item{display:flex;align-items:center;gap:.75rem;padding:.875rem;border:1.5px solid var(--cf-border-color, #D1D5DB);border-radius:8px;margin-bottom:.5rem;background-color:var(--cf-hover-background, #F9FAFB);transition:all .2s ease}.configurable-form-container .uploaded-file-item:hover{border-color:#9ca3af;box-shadow:0 1px 3px #0000001a}.configurable-form-container .uploaded-file-item .file-icon mat-icon{font-size:2rem;width:2rem;height:2rem}.configurable-form-container .uploaded-file-item .file-icon mat-icon.pdf-icon{color:#dc2626}.configurable-form-container .uploaded-file-item .file-icon mat-icon.doc-icon{color:#2563eb}.configurable-form-container .uploaded-file-item .file-icon mat-icon.img-icon{color:#059669}.configurable-form-container .uploaded-file-item .file-icon mat-icon.file-icon-default{color:var(--cf-text-secondary, #6B7280)}.configurable-form-container .uploaded-file-item .file-info{flex:1}.configurable-form-container .uploaded-file-item .file-info .file-name{font-size:.875rem;color:var(--cf-text-primary, #111827);font-weight:500;margin-bottom:.125rem}.configurable-form-container .uploaded-file-item .file-info .file-size{font-size:.75rem;color:var(--cf-text-secondary, #6B7280)}.configurable-form-container .uploaded-file-item .remove-file-btn mat-icon{font-size:1.25rem;width:1.25rem;height:1.25rem}.configurable-form-container .relative{position:relative}.configurable-form-container .date-toggle{position:absolute;right:0;top:50%;transform:translateY(-50%)}.configurable-form-container .repeater-item{padding:1.5rem;border:1.5px solid var(--cf-border-color, #D1D5DB);border-radius:10px;margin-bottom:1rem;background-color:var(--cf-hover-background, #F9FAFB);transition:all .2s ease}.configurable-form-container .repeater-item:hover{border-color:#9ca3af;box-shadow:0 2px 4px #0000000d}.configurable-form-container .repeater-item-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:1.25rem;padding-bottom:.75rem;border-bottom:1.5px solid var(--cf-border-color, #D1D5DB)}.configurable-form-container .repeater-item-header .text-small{font-size:.875rem;font-weight:600;color:var(--cf-text-primary, #111827);text-transform:uppercase;letter-spacing:.025em}.configurable-form-container .repeater-separator{height:1px;background-color:var(--cf-border-color, #D1D5DB);margin:1.5rem 0}.configurable-form-container .add-btn{margin-top:1rem;display:inline-flex;align-items:center;gap:.5rem;padding:.625rem 1.25rem;background-color:transparent;color:var(--cf-primary-color, #3B82F6);border:1.5px solid var(--cf-primary-color, #3B82F6);border-radius:8px;font-size:.875rem;font-weight:600;cursor:pointer;transition:all .2s ease}.configurable-form-container .add-btn:hover:not(:disabled){background-color:var(--cf-primary-color, #3B82F6);color:#fff}.configurable-form-container .add-btn:disabled{opacity:.5;cursor:not-allowed;border-color:var(--cf-disabled-background, #E5E7EB)}.configurable-form-container .add-btn mat-icon{font-size:1.25rem;width:1.25rem;height:1.25rem}@media(max-width:768px){.configurable-form-container .section-card{padding:1.25rem;border-radius:8px}.configurable-form-container .form-grid{margin-left:-.5rem;margin-right:-.5rem}.configurable-form-container .form-col{padding-left:.5rem;padding-right:.5rem;margin-bottom:1rem}.configurable-form-container .radio-group{flex-direction:column;gap:.75rem}.configurable-form-container .composite-container{flex-direction:column;align-items:stretch}.configurable-form-container .composite-container .separator{text-align:center}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { 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: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1$2.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1$2.NgSelectOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.ɵNgSelectMultipleOption, selector: "option", inputs: ["ngValue", "value"] }, { kind: "directive", type: i1$2.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1$2.SelectControlValueAccessor, selector: "select:not([multiple])[formControlName],select:not([multiple])[formControl],select:not([multiple])[ngModel]", inputs: ["compareWith"] }, { kind: "directive", type: i1$2.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1$2.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1$2.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1$2.FormGroupDirective, selector: "[formGroup]", inputs: ["formGroup"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }, { kind: "directive", type: i1$2.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "directive", type: i1$2.FormGroupName, selector: "[formGroupName]", inputs: ["formGroupName"] }, { kind: "directive", type: i1$2.FormArrayName, selector: "[formArrayName]", inputs: ["formArrayName"] }, { kind: "ngmodule", type: MaterialModule }, { kind: "directive", type: i6.MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: i6$1.MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: i6$1.MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { 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: i10$1.MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: i10$1.MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }] });
12411
12623
  }
12412
12624
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: ConfigurableFormComponent, decorators: [{
12413
12625
  type: Component,
@@ -12838,7 +13050,7 @@ class SideNavComponent {
12838
13050
  return styles;
12839
13051
  }
12840
13052
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SideNavComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
12841
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: SideNavComponent, isStandalone: false, selector: "lib-side-nav", inputs: { sections: "sections", userRoles: "userRoles", activeId: "activeId", styleConfig: "styleConfig", collapsed: "collapsed", width: "width", collapsedWidth: "collapsedWidth", showCollapseToggle: "showCollapseToggle", hideIconsWhenExpanded: "hideIconsWhenExpanded", showTooltips: "showTooltips", tooltipPosition: "tooltipPosition", labels: "labels" }, outputs: { itemClicked: "itemClicked", collapsedChange: "collapsedChange" }, host: { properties: { "class.cc-side-nav-host-collapsed": "this.isHostCollapsed" } }, usesOnChanges: true, ngImport: i0, template: "<nav class=\"cc-side-nav\" [class.collapsed]=\"collapsed\" role=\"navigation\" [ngStyle]=\"customStyles\">\r\n\r\n <div class=\"cc-side-nav-section\" *ngFor=\"let section of filteredSections; let i = index\">\r\n\r\n <div class=\"cc-side-nav-heading-row\" *ngIf=\"section.heading && !collapsed\">\r\n <h3 class=\"cc-side-nav-heading\">{{ section.heading }}</h3>\r\n </div>\r\n\r\n <ul class=\"cc-side-nav-list\">\r\n <li *ngFor=\"let item of section.items\" class=\"cc-side-nav-list-item\">\r\n <a (click)=\"onItemClick(item, $event)\" class=\"cc-side-nav-link\" [class.active]=\"item.id === activeId\"\r\n [class.disabled]=\"item.disabled\" [matTooltip]=\"item.tooltip || item.label\"\r\n [matTooltipPosition]=\"tooltipPosition\"\r\n [matTooltipClass]=\"'cc-side-nav-tooltip'\" [attr.aria-current]=\"(item.id === activeId) ? 'page' : null\"\r\n [attr.aria-disabled]=\"item.disabled ? true : null\">\r\n <span *ngIf=\"item.icon && (collapsed || !hideIconsWhenExpanded)\" class=\"cc-side-nav-icon\">\r\n <span *ngIf=\"!item.icon.includes('fa') && !item.icon.includes('bi')\" class=\"material-icons\">\r\n {{ item.icon }}\r\n </span>\r\n <i *ngIf=\"item.icon.includes('fa') || item.icon.includes('bi')\" [class]=\"item.icon\"></i>\r\n </span>\r\n <!-- Label: always in DOM, CSS transitions handle fade on collapse -->\r\n <span class=\"cc-side-nav-label\">{{ item.label }}</span>\r\n <!-- Arrow: rendered for active items regardless of collapse state; CSS hides it -->\r\n <span class=\"cc-side-nav-arrow material-icons\"\r\n *ngIf=\"(item.id === activeId) && item.showArrow !== false\">chevron_right</span>\r\n </a>\r\n </li>\r\n </ul>\r\n\r\n </div>\r\n\r\n</nav>\r\n\r\n<!--\r\n Toggle pill \u2014 always straddles the right edge of :host (right: -12px).\r\n Hidden via opacity/scale until :host is hovered; always visible when collapsed.\r\n Single chevron SVG rotates 180\u00B0 via CSS \u2014 no icon swap needed.\r\n-->\r\n<button *ngIf=\"showCollapseToggle\"\r\n class=\"cc-sn-toggle\"\r\n (click)=\"toggleCollapse()\"\r\n type=\"button\"\r\n [attr.aria-label]=\"collapsed ? 'Expand navigation' : 'Collapse navigation'\">\r\n <svg class=\"cc-sn-chevron\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 16 16\"\r\n fill=\"none\"\r\n aria-hidden=\"true\">\r\n <path d=\"M10 3L5 8L10 13\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"1.75\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"/>\r\n </svg>\r\n</button>\r\n", styles: [":host{display:block;height:100%;overflow:visible;position:relative;width:var(--cc-side-nav-width, 220px);transition:width .3s cubic-bezier(.4,0,.2,1);--cc-side-nav-bg: #ffffff;--cc-side-nav-border-color: rgba(0, 0, 0, .08);--cc-side-nav-width: 220px;--cc-side-nav-collapsed-width: 56px;--cc-side-nav-gap-sections: 24px;--cc-side-nav-padding: 16px;--cc-side-nav-font-family: Poppins, sans-serif;--cc-side-nav-heading-font-weight: 500;--cc-side-nav-heading-font-size: 16px;--cc-side-nav-heading-color: #3C4043;--cc-side-nav-item-gap: 4px;--cc-side-nav-item-padding: 12px 16px;--cc-side-nav-item-border-radius: 8px;--cc-side-nav-item-font-weight: 400;--cc-side-nav-item-font-size: 14px;--cc-side-nav-item-color: #5F6368;--cc-side-nav-item-hover-bg: rgba(30, 30, 45, .08);--cc-side-nav-item-hover-color: #1e1e2d;--cc-side-nav-active-bg: #1e1e2d;--cc-side-nav-active-color: #ffffff;--cc-side-nav-active-font-weight: 500;--cc-side-nav-active-hover-bg: #2a2a3d;--cc-side-nav-disabled-opacity: .5;--cc-side-nav-toggle-bg: #ffffff;--cc-side-nav-toggle-border-color: rgba(0, 0, 0, .1);--cc-side-nav-tooltip-bg: #1e1e2d;--cc-side-nav-tooltip-color: #FFFFFF;--cc-side-nav-tooltip-padding: 8px 14px;--cc-side-nav-tooltip-border-radius: 6px;--cc-side-nav-tooltip-font-size: 12px;--cc-side-nav-tooltip-font-weight: 500;--cc-side-nav-tooltip-letter-spacing: .2px;--cc-side-nav-tooltip-offset: 20px;--cc-side-nav-tooltip-shadow: 0 4px 12px rgba(0, 0, 0, .35)}:host.cc-side-nav-host-collapsed{width:var(--cc-side-nav-collapsed-width, 56px)}:host:after{content:\"\";position:absolute;top:10%;right:0;width:2px;height:80%;background:var(--cc-side-nav-active-bg, #1e1e2d);border-radius:2px;opacity:0;transition:opacity .25s ease;pointer-events:none;z-index:4}:host:hover:after{opacity:.18}:host.cc-side-nav-host-collapsed:after{display:none}.cc-sn-toggle{position:absolute;bottom:20px;right:-12px;width:30px;height:30px;border-radius:50%;border:1.5px solid var(--cc-side-nav-toggle-border-color, rgba(0, 0, 0, .1));background:var(--cc-side-nav-toggle-bg, #ffffff);cursor:pointer;display:flex;align-items:center;justify-content:center;z-index:20;padding:0;color:var(--cc-side-nav-item-color, #5F6368);box-shadow:0 1px 4px #0000001a,0 0 0 1px #0000000a;opacity:1;transform:scale(.65);transition:opacity .2s ease,transform .25s cubic-bezier(.34,1.56,.64,1),background .15s ease,color .15s ease,border-color .15s ease,box-shadow .2s ease}.cc-sn-toggle:focus-visible{outline:2px solid var(--cc-side-nav-active-bg, #E63E30);outline-offset:2px}.cc-sn-toggle .cc-sn-chevron{width:12px;height:12px;flex-shrink:0;display:block;transition:transform .3s cubic-bezier(.4,0,.2,1)}:host:hover .cc-sn-toggle{opacity:1;transform:scale(1)}:host:hover .cc-sn-toggle:hover,:host.cc-side-nav-host-collapsed .cc-sn-toggle:hover{background:var(--cc-side-nav-active-bg, #E63E30);color:var(--cc-side-nav-active-color, #ffffff);border-color:transparent;box-shadow:0 4px 14px #0000001a,0 1px 4px #0000001a;transform:scale(1.05)}:host.cc-side-nav-host-collapsed .cc-sn-toggle{opacity:1;transform:scale(1);box-shadow:0 2px 8px #00000021,0 0 0 1.5px #00000014}:host.cc-side-nav-host-collapsed .cc-sn-chevron{transform:rotate(180deg)}.cc-side-nav{display:flex;flex-direction:column;gap:var(--cc-side-nav-gap-sections, 24px);padding:var(--cc-side-nav-padding, 16px);width:var(--cc-side-nav-width, 220px);min-width:var(--cc-side-nav-width, 220px);box-sizing:border-box;background-color:var(--cc-side-nav-bg, #ffffff);border-right:1px solid var(--cc-side-nav-border-color, rgba(0, 0, 0, .08));height:100%;overflow-y:auto;overflow-x:hidden;transition:width .3s cubic-bezier(.4,0,.2,1),min-width .3s cubic-bezier(.4,0,.2,1),padding .3s cubic-bezier(.4,0,.2,1)}.cc-side-nav::-webkit-scrollbar{width:3px}.cc-side-nav::-webkit-scrollbar-track{background:transparent}.cc-side-nav::-webkit-scrollbar-thumb{background:#00000026;border-radius:2px}.cc-side-nav::-webkit-scrollbar-thumb:hover{background:#0000004d}.cc-side-nav.collapsed{width:var(--cc-side-nav-collapsed-width, 56px);min-width:var(--cc-side-nav-collapsed-width, 56px);padding:var(--cc-side-nav-padding, 16px) 8px}.cc-side-nav.collapsed .cc-side-nav-link{justify-content:center;gap:0;padding:10px}.cc-side-nav.collapsed .cc-side-nav-icon{font-size:20px}.cc-side-nav-section{display:flex;flex-direction:column;gap:12px}.cc-side-nav-heading-row{display:flex;align-items:center;justify-content:space-between;min-height:28px}.cc-side-nav-heading{margin:0;padding:0 16px;font-family:var(--cc-side-nav-font-family, \"Poppins\", sans-serif);font-weight:var(--cc-side-nav-heading-font-weight, 500);font-style:normal;font-size:var(--cc-side-nav-heading-font-size, 16px);text-transform:uppercase;color:var(--cc-side-nav-heading-color, #3C4043);flex:1}.cc-side-nav-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--cc-side-nav-item-gap, 4px)}.cc-side-nav-list-item{margin:0;padding:0}.cc-side-nav-link{display:flex;align-items:center;gap:12px;padding:var(--cc-side-nav-item-padding, 12px 16px);text-decoration:none;cursor:pointer;border-radius:var(--cc-side-nav-item-border-radius, 8px);transition:background-color .2s ease,opacity .2s ease;font-family:var(--cc-side-nav-font-family, \"Poppins\", sans-serif);font-weight:var(--cc-side-nav-item-font-weight, 400);font-style:normal;font-size:var(--cc-side-nav-item-font-size, 14px);color:var(--cc-side-nav-item-color, #5F6368)}.cc-side-nav-link:hover:not(.disabled){background-color:var(--cc-side-nav-item-hover-bg, rgba(30, 30, 45, .08));color:var(--cc-side-nav-item-hover-color, #1e1e2d)}.cc-side-nav-link.active{background:var(--cc-side-nav-active-bg, #1e1e2d);opacity:1;border-radius:var(--cc-side-nav-item-border-radius, 8px);font-weight:var(--cc-side-nav-active-font-weight, 500);color:var(--cc-side-nav-active-color, #ffffff)}.cc-side-nav-link.active:hover{background-color:var(--cc-side-nav-active-hover-bg, #2a2a3d);color:var(--cc-side-nav-active-color, #ffffff)}.cc-side-nav-link.disabled{cursor:not-allowed;opacity:var(--cc-side-nav-disabled-opacity, .5)}.cc-side-nav-icon{display:flex;align-items:center;justify-content:center;font-size:16px;flex-shrink:0}.cc-side-nav-label{flex:1;white-space:wrap;overflow:hidden;max-width:200px;opacity:1;transition:opacity .15s ease,max-width .3s cubic-bezier(.4,0,.2,1)}.collapsed .cc-side-nav-label{opacity:0;max-width:0}.cc-side-nav-arrow{margin-left:auto;font-size:18px;opacity:.7;overflow:hidden;max-width:30px;flex-shrink:0;white-space:nowrap;transition:opacity .15s ease,max-width .3s cubic-bezier(.4,0,.2,1)}.collapsed .cc-side-nav-arrow{opacity:0;max-width:0}.cc-side-nav-link.active .cc-side-nav-arrow{color:var(--cc-side-nav-active-color, #FFFFFF);opacity:1}::ng-deep .mat-mdc-tooltip.cc-side-nav-tooltip{margin-left:var(--cc-side-nav-tooltip-offset, 20px)!important;overflow:visible!important}::ng-deep .mat-mdc-tooltip.cc-side-nav-tooltip .mdc-tooltip__surface{background-color:var(--cc-side-nav-tooltip-bg, #1e1e2d)!important;color:var(--cc-side-nav-tooltip-color, #FFFFFF)!important;font-family:var(--cc-side-nav-font-family, \"Poppins\", sans-serif)!important;font-size:var(--cc-side-nav-tooltip-font-size, 12px)!important;font-weight:var(--cc-side-nav-tooltip-font-weight, 500)!important;padding:var(--cc-side-nav-tooltip-padding, 8px 14px)!important;border-radius:var(--cc-side-nav-tooltip-border-radius, 6px)!important;box-shadow:var(--cc-side-nav-tooltip-shadow, 0 4px 12px rgba(0, 0, 0, .35))!important;letter-spacing:var(--cc-side-nav-tooltip-letter-spacing, .2px)!important;position:relative!important;overflow:visible!important}::ng-deep .mat-mdc-tooltip.cc-side-nav-tooltip .mdc-tooltip__surface:before{content:\"\";position:absolute;left:-6px;top:50%;transform:translateY(-50%);width:0;height:0;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:6px solid var(--cc-side-nav-tooltip-bg, #1e1e2d)}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2$2.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
13053
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.15", type: SideNavComponent, isStandalone: false, selector: "lib-side-nav", inputs: { sections: "sections", userRoles: "userRoles", activeId: "activeId", styleConfig: "styleConfig", collapsed: "collapsed", width: "width", collapsedWidth: "collapsedWidth", showCollapseToggle: "showCollapseToggle", hideIconsWhenExpanded: "hideIconsWhenExpanded", showTooltips: "showTooltips", tooltipPosition: "tooltipPosition", labels: "labels" }, outputs: { itemClicked: "itemClicked", collapsedChange: "collapsedChange" }, host: { properties: { "class.cc-side-nav-host-collapsed": "this.isHostCollapsed" } }, usesOnChanges: true, ngImport: i0, template: "<nav class=\"cc-side-nav\" [class.collapsed]=\"collapsed\" role=\"navigation\" [ngStyle]=\"customStyles\">\r\n\r\n <div class=\"cc-side-nav-section\" *ngFor=\"let section of filteredSections; let i = index\">\r\n\r\n <div class=\"cc-side-nav-heading-row\" *ngIf=\"section.heading && !collapsed\">\r\n <h3 class=\"cc-side-nav-heading\">{{ section.heading }}</h3>\r\n </div>\r\n\r\n <ul class=\"cc-side-nav-list\">\r\n <li *ngFor=\"let item of section.items\" class=\"cc-side-nav-list-item\">\r\n <a (click)=\"onItemClick(item, $event)\" class=\"cc-side-nav-link\" [class.active]=\"item.id === activeId\"\r\n [class.disabled]=\"item.disabled\" [matTooltip]=\"item.tooltip || item.label\"\r\n [matTooltipPosition]=\"tooltipPosition\"\r\n [matTooltipClass]=\"'cc-side-nav-tooltip'\" [attr.aria-current]=\"(item.id === activeId) ? 'page' : null\"\r\n [attr.aria-disabled]=\"item.disabled ? true : null\">\r\n <span *ngIf=\"item.icon && (collapsed || !hideIconsWhenExpanded)\" class=\"cc-side-nav-icon\">\r\n <span *ngIf=\"!item.icon.includes('fa') && !item.icon.includes('bi')\" class=\"material-icons\">\r\n {{ item.icon }}\r\n </span>\r\n <i *ngIf=\"item.icon.includes('fa') || item.icon.includes('bi')\" [class]=\"item.icon\"></i>\r\n </span>\r\n <!-- Label: always in DOM, CSS transitions handle fade on collapse -->\r\n <span class=\"cc-side-nav-label\">{{ item.label }}</span>\r\n <!-- Arrow: rendered for active items regardless of collapse state; CSS hides it -->\r\n <span class=\"cc-side-nav-arrow material-icons\"\r\n *ngIf=\"(item.id === activeId) && item.showArrow !== false\">chevron_right</span>\r\n </a>\r\n </li>\r\n </ul>\r\n\r\n </div>\r\n\r\n</nav>\r\n\r\n<!--\r\n Toggle pill \u2014 always straddles the right edge of :host (right: -12px).\r\n Hidden via opacity/scale until :host is hovered; always visible when collapsed.\r\n Single chevron SVG rotates 180\u00B0 via CSS \u2014 no icon swap needed.\r\n-->\r\n<button *ngIf=\"showCollapseToggle\"\r\n class=\"cc-sn-toggle\"\r\n (click)=\"toggleCollapse()\"\r\n type=\"button\"\r\n [attr.aria-label]=\"collapsed ? 'Expand navigation' : 'Collapse navigation'\">\r\n <svg class=\"cc-sn-chevron\"\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n viewBox=\"0 0 16 16\"\r\n fill=\"none\"\r\n aria-hidden=\"true\">\r\n <path d=\"M10 3L5 8L10 13\"\r\n stroke=\"currentColor\"\r\n stroke-width=\"1.75\"\r\n stroke-linecap=\"round\"\r\n stroke-linejoin=\"round\"/>\r\n </svg>\r\n</button>\r\n", styles: [":host{display:block;height:100%;overflow:visible;position:relative;width:var(--cc-side-nav-width, 220px);transition:width .3s cubic-bezier(.4,0,.2,1);--cc-side-nav-bg: #ffffff;--cc-side-nav-border-color: rgba(0, 0, 0, .08);--cc-side-nav-width: 220px;--cc-side-nav-collapsed-width: 56px;--cc-side-nav-gap-sections: 24px;--cc-side-nav-padding: 16px;--cc-side-nav-font-family: Poppins, sans-serif;--cc-side-nav-heading-font-weight: 500;--cc-side-nav-heading-font-size: 16px;--cc-side-nav-heading-color: #3C4043;--cc-side-nav-item-gap: 4px;--cc-side-nav-item-padding: 12px 16px;--cc-side-nav-item-border-radius: 8px;--cc-side-nav-item-font-weight: 400;--cc-side-nav-item-font-size: 14px;--cc-side-nav-item-color: #5F6368;--cc-side-nav-item-hover-bg: rgba(30, 30, 45, .08);--cc-side-nav-item-hover-color: #1e1e2d;--cc-side-nav-active-bg: #1e1e2d;--cc-side-nav-active-color: #ffffff;--cc-side-nav-active-font-weight: 500;--cc-side-nav-active-hover-bg: #2a2a3d;--cc-side-nav-disabled-opacity: .5;--cc-side-nav-toggle-bg: #ffffff;--cc-side-nav-toggle-border-color: rgba(0, 0, 0, .1);--cc-side-nav-tooltip-bg: #1e1e2d;--cc-side-nav-tooltip-color: #FFFFFF;--cc-side-nav-tooltip-padding: 8px 14px;--cc-side-nav-tooltip-border-radius: 6px;--cc-side-nav-tooltip-font-size: 12px;--cc-side-nav-tooltip-font-weight: 500;--cc-side-nav-tooltip-letter-spacing: .2px;--cc-side-nav-tooltip-offset: 20px;--cc-side-nav-tooltip-shadow: 0 4px 12px rgba(0, 0, 0, .35)}:host.cc-side-nav-host-collapsed{width:var(--cc-side-nav-collapsed-width, 56px)}:host:after{content:\"\";position:absolute;top:10%;right:0;width:2px;height:80%;background:var(--cc-side-nav-active-bg, #1e1e2d);border-radius:2px;opacity:0;transition:opacity .25s ease;pointer-events:none;z-index:4}:host:hover:after{opacity:.18}:host.cc-side-nav-host-collapsed:after{display:none}.cc-sn-toggle{position:absolute;bottom:20px;right:-12px;width:30px;height:30px;border-radius:50%;border:1.5px solid var(--cc-side-nav-toggle-border-color, rgba(0, 0, 0, .1));background:var(--cc-side-nav-toggle-bg, #ffffff);cursor:pointer;display:flex;align-items:center;justify-content:center;z-index:20;padding:0;color:var(--cc-side-nav-item-color, #5F6368);box-shadow:0 1px 4px #0000001a,0 0 0 1px #0000000a;opacity:1;transform:scale(.65);transition:opacity .2s ease,transform .25s cubic-bezier(.34,1.56,.64,1),background .15s ease,color .15s ease,border-color .15s ease,box-shadow .2s ease}.cc-sn-toggle:focus-visible{outline:2px solid var(--cc-side-nav-active-bg, #E63E30);outline-offset:2px}.cc-sn-toggle .cc-sn-chevron{width:12px;height:12px;flex-shrink:0;display:block;transition:transform .3s cubic-bezier(.4,0,.2,1)}:host:hover .cc-sn-toggle{opacity:1;transform:scale(1)}:host:hover .cc-sn-toggle:hover,:host.cc-side-nav-host-collapsed .cc-sn-toggle:hover{background:var(--cc-side-nav-active-bg, #E63E30);color:var(--cc-side-nav-active-color, #ffffff);border-color:transparent;box-shadow:0 4px 14px #0000001a,0 1px 4px #0000001a;transform:scale(1.05)}:host.cc-side-nav-host-collapsed .cc-sn-toggle{opacity:1;transform:scale(1);box-shadow:0 2px 8px #00000021,0 0 0 1.5px #00000014}:host.cc-side-nav-host-collapsed .cc-sn-chevron{transform:rotate(180deg)}.cc-side-nav{display:flex;flex-direction:column;gap:var(--cc-side-nav-gap-sections, 24px);padding:var(--cc-side-nav-padding, 16px);width:var(--cc-side-nav-width, 220px);min-width:var(--cc-side-nav-width, 220px);box-sizing:border-box;background-color:var(--cc-side-nav-bg, #ffffff);border-right:1px solid var(--cc-side-nav-border-color, rgba(0, 0, 0, .08));height:100%;overflow-y:auto;overflow-x:hidden;transition:width .3s cubic-bezier(.4,0,.2,1),min-width .3s cubic-bezier(.4,0,.2,1),padding .3s cubic-bezier(.4,0,.2,1)}.cc-side-nav::-webkit-scrollbar{width:3px}.cc-side-nav::-webkit-scrollbar-track{background:transparent}.cc-side-nav::-webkit-scrollbar-thumb{background:#00000026;border-radius:2px}.cc-side-nav::-webkit-scrollbar-thumb:hover{background:#0000004d}.cc-side-nav.collapsed{width:var(--cc-side-nav-collapsed-width, 56px);min-width:var(--cc-side-nav-collapsed-width, 56px);padding:var(--cc-side-nav-padding, 16px) 8px}.cc-side-nav.collapsed .cc-side-nav-link{justify-content:center;gap:0;padding:10px}.cc-side-nav.collapsed .cc-side-nav-icon{font-size:20px}.cc-side-nav-section{display:flex;flex-direction:column;gap:12px}.cc-side-nav-heading-row{display:flex;align-items:center;justify-content:space-between;min-height:28px}.cc-side-nav-heading{margin:0;padding:0 16px;font-family:var(--cc-side-nav-font-family, \"Poppins\", sans-serif);font-weight:var(--cc-side-nav-heading-font-weight, 500);font-style:normal;font-size:var(--cc-side-nav-heading-font-size, 16px);text-transform:uppercase;color:var(--cc-side-nav-heading-color, #3C4043);flex:1}.cc-side-nav-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--cc-side-nav-item-gap, 4px)}.cc-side-nav-list-item{margin:0;padding:0}.cc-side-nav-link{display:flex;align-items:center;gap:12px;padding:var(--cc-side-nav-item-padding, 12px 16px);text-decoration:none;cursor:pointer;border-radius:var(--cc-side-nav-item-border-radius, 8px);transition:background-color .2s ease,opacity .2s ease;font-family:var(--cc-side-nav-font-family, \"Poppins\", sans-serif);font-weight:var(--cc-side-nav-item-font-weight, 400);font-style:normal;font-size:var(--cc-side-nav-item-font-size, 14px);color:var(--cc-side-nav-item-color, #5F6368)}.cc-side-nav-link:hover:not(.disabled){background-color:var(--cc-side-nav-item-hover-bg, rgba(30, 30, 45, .08));color:var(--cc-side-nav-item-hover-color, #1e1e2d)}.cc-side-nav-link.active{background:var(--cc-side-nav-active-bg, #1e1e2d);opacity:1;border-radius:var(--cc-side-nav-item-border-radius, 8px);font-weight:var(--cc-side-nav-active-font-weight, 500);color:var(--cc-side-nav-active-color, #ffffff)}.cc-side-nav-link.active:hover{background-color:var(--cc-side-nav-active-hover-bg, #2a2a3d);color:var(--cc-side-nav-active-color, #ffffff)}.cc-side-nav-link.disabled{cursor:not-allowed;opacity:var(--cc-side-nav-disabled-opacity, .5)}.cc-side-nav-icon{display:flex;align-items:center;justify-content:center;font-size:16px;flex-shrink:0}.cc-side-nav-label{flex:1;white-space:wrap;overflow:hidden;max-width:200px;opacity:1;transition:opacity .15s ease,max-width .3s cubic-bezier(.4,0,.2,1)}.collapsed .cc-side-nav-label{opacity:0;max-width:0}.cc-side-nav-arrow{margin-left:auto;font-size:18px;opacity:.7;overflow:hidden;max-width:30px;flex-shrink:0;white-space:nowrap;transition:opacity .15s ease,max-width .3s cubic-bezier(.4,0,.2,1)}.collapsed .cc-side-nav-arrow{opacity:0;max-width:0}.cc-side-nav-link.active .cc-side-nav-arrow{color:var(--cc-side-nav-active-color, #FFFFFF);opacity:1}::ng-deep .mat-mdc-tooltip.cc-side-nav-tooltip{margin-left:var(--cc-side-nav-tooltip-offset, 20px)!important;overflow:visible!important}::ng-deep .mat-mdc-tooltip.cc-side-nav-tooltip .mdc-tooltip__surface{background-color:var(--cc-side-nav-tooltip-bg, #1e1e2d)!important;color:var(--cc-side-nav-tooltip-color, #FFFFFF)!important;font-family:var(--cc-side-nav-font-family, \"Poppins\", sans-serif)!important;font-size:var(--cc-side-nav-tooltip-font-size, 12px)!important;font-weight:var(--cc-side-nav-tooltip-font-weight, 500)!important;padding:var(--cc-side-nav-tooltip-padding, 8px 14px)!important;border-radius:var(--cc-side-nav-tooltip-border-radius, 6px)!important;box-shadow:var(--cc-side-nav-tooltip-shadow, 0 4px 12px rgba(0, 0, 0, .35))!important;letter-spacing:var(--cc-side-nav-tooltip-letter-spacing, .2px)!important;position:relative!important;overflow:visible!important}::ng-deep .mat-mdc-tooltip.cc-side-nav-tooltip .mdc-tooltip__surface:before{content:\"\";position:absolute;left:-6px;top:50%;transform:translateY(-50%);width:0;height:0;border-top:5px solid transparent;border-bottom:5px solid transparent;border-right:6px solid var(--cc-side-nav-tooltip-bg, #1e1e2d)}\n"], dependencies: [{ kind: "directive", type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2$1.MatTooltip, selector: "[matTooltip]", inputs: ["matTooltipPosition", "matTooltipPositionAtOrigin", "matTooltipDisabled", "matTooltipShowDelay", "matTooltipHideDelay", "matTooltipTouchGestures", "matTooltip", "matTooltipClass"], exportAs: ["matTooltip"] }] });
12842
13054
  }
12843
13055
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.15", ngImport: i0, type: SideNavComponent, decorators: [{
12844
13056
  type: Component,