@naniteninja/profile-comparison-lib 1.0.18 → 1.0.20

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.
@@ -1,13 +1,11 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, Injectable, Optional, Inject, inject, EventEmitter, ViewChild, HostListener, Output, Input, Component, NgModule } from '@angular/core';
2
+ import { InjectionToken, Injectable, Optional, Inject, inject, EventEmitter, ViewChild, Output, Input, Component, NgModule } from '@angular/core';
3
3
  import { switchMap as switchMap$1, map as map$1, catchError, tap as tap$1 } from 'rxjs/operators';
4
4
  import { from, switchMap, map, of, Observable, BehaviorSubject, throwError, tap, retryWhen, concatMap, timer, forkJoin, catchError as catchError$1 } from 'rxjs';
5
5
  import * as i1 from '@angular/common/http';
6
6
  import { HttpHeaders, HttpClient, HttpClientModule } from '@angular/common/http';
7
- import * as i2 from '@angular/common';
7
+ import * as i2_1 from '@angular/common';
8
8
  import { CommonModule } from '@angular/common';
9
- import * as i3 from '@naniteninja/ionic-lib';
10
- import { LibMarqueeModule } from '@naniteninja/ionic-lib';
11
9
  import { FormsModule, ReactiveFormsModule } from '@angular/forms';
12
10
 
13
11
  /** Backend mode for the profile comparison component. */
@@ -204,10 +202,10 @@ class CachePersistenceService {
204
202
  static STORE_OPENAI_EMBEDDINGS = STORE_OPENAI_EMBEDDINGS;
205
203
  static STORE_PROFILE_FACE = STORE_PROFILE_FACE;
206
204
  static STORE_PROFILE_COMPARE = STORE_PROFILE_COMPARE;
207
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CachePersistenceService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
208
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CachePersistenceService, providedIn: 'root' });
205
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: CachePersistenceService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
206
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: CachePersistenceService, providedIn: 'root' });
209
207
  }
210
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: CachePersistenceService, decorators: [{
208
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: CachePersistenceService, decorators: [{
211
209
  type: Injectable,
212
210
  args: [{ providedIn: 'root' }]
213
211
  }] });
@@ -253,10 +251,10 @@ class FileConversionService {
253
251
  const mime = this.getMimeFromFilename(imageStr);
254
252
  return this.urlToFile(imageStr, fileBaseName, mime);
255
253
  }
256
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FileConversionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
257
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FileConversionService, providedIn: 'root' });
254
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: FileConversionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
255
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: FileConversionService, providedIn: 'root' });
258
256
  }
259
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: FileConversionService, decorators: [{
257
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: FileConversionService, decorators: [{
260
258
  type: Injectable,
261
259
  args: [{
262
260
  providedIn: 'root'
@@ -372,10 +370,10 @@ class ImageCompressionService {
372
370
  return `${base}.webp`;
373
371
  return `${base}.jpg`;
374
372
  }
375
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ImageCompressionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
376
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ImageCompressionService, providedIn: 'root' });
373
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ImageCompressionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
374
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ImageCompressionService, providedIn: 'root' });
377
375
  }
378
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ImageCompressionService, decorators: [{
376
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ImageCompressionService, decorators: [{
379
377
  type: Injectable,
380
378
  args: [{
381
379
  providedIn: 'root'
@@ -1154,10 +1152,10 @@ class OpenAIEmbeddingService {
1154
1152
  this.persistence.clear(CachePersistenceService.STORE_OPENAI_SIMILARITY);
1155
1153
  this.persistence.clear(CachePersistenceService.STORE_OPENAI_EMBEDDINGS);
1156
1154
  }
1157
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: OpenAIEmbeddingService, deps: [{ token: i1.HttpClient }, { token: PROFILE_COMPARISON_API_BASE_URL, optional: true }, { token: CachePersistenceService }], target: i0.ɵɵFactoryTarget.Injectable });
1158
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: OpenAIEmbeddingService, providedIn: 'root' });
1155
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: OpenAIEmbeddingService, deps: [{ token: i1.HttpClient }, { token: PROFILE_COMPARISON_API_BASE_URL, optional: true }, { token: CachePersistenceService }], target: i0.ɵɵFactoryTarget.Injectable });
1156
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: OpenAIEmbeddingService, providedIn: 'root' });
1159
1157
  }
1160
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: OpenAIEmbeddingService, decorators: [{
1158
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: OpenAIEmbeddingService, decorators: [{
1161
1159
  type: Injectable,
1162
1160
  args: [{
1163
1161
  providedIn: 'root'
@@ -1189,10 +1187,10 @@ class ProfileComparisonLibService {
1189
1187
  getVersion() {
1190
1188
  return '0.0.0';
1191
1189
  }
1192
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProfileComparisonLibService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1193
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProfileComparisonLibService, providedIn: 'root' });
1190
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileComparisonLibService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1191
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileComparisonLibService, providedIn: 'root' });
1194
1192
  }
1195
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProfileComparisonLibService, decorators: [{
1193
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileComparisonLibService, decorators: [{
1196
1194
  type: Injectable,
1197
1195
  args: [{
1198
1196
  providedIn: 'root'
@@ -1446,10 +1444,10 @@ class ProfileService {
1446
1444
  this.compareInterestsCache.clear();
1447
1445
  this.persistence.clear(CachePersistenceService.STORE_PROFILE_COMPARE);
1448
1446
  }
1449
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProfileService, deps: [{ token: i1.HttpClient }, { token: PROFILE_COMPARISON_API_BASE_URL, optional: true }, { token: CachePersistenceService }], target: i0.ɵɵFactoryTarget.Injectable });
1450
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProfileService, providedIn: 'root' });
1447
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileService, deps: [{ token: i1.HttpClient }, { token: PROFILE_COMPARISON_API_BASE_URL, optional: true }, { token: CachePersistenceService }], target: i0.ɵɵFactoryTarget.Injectable });
1448
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileService, providedIn: 'root' });
1451
1449
  }
1452
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProfileService, decorators: [{
1450
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileService, decorators: [{
1453
1451
  type: Injectable,
1454
1452
  args: [{ providedIn: 'root' }]
1455
1453
  }], ctorParameters: () => [{ type: i1.HttpClient }, { type: undefined, decorators: [{
@@ -1505,10 +1503,10 @@ class ProfileComparisonBackendService {
1505
1503
  return throwError(() => err);
1506
1504
  }));
1507
1505
  }
1508
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProfileComparisonBackendService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1509
- static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProfileComparisonBackendService, providedIn: 'root' });
1506
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileComparisonBackendService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
1507
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileComparisonBackendService, providedIn: 'root' });
1510
1508
  }
1511
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProfileComparisonBackendService, decorators: [{
1509
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileComparisonBackendService, decorators: [{
1512
1510
  type: Injectable,
1513
1511
  args: [{ providedIn: 'root' }]
1514
1512
  }], ctorParameters: () => [] });
@@ -1520,6 +1518,7 @@ class ProfileComparisonLibComponent {
1520
1518
  imageCompressionService;
1521
1519
  cdr;
1522
1520
  getVerboseLogging;
1521
+ static DEFAULT_OBJECT_POSITION = '50% 50%';
1523
1522
  static DEFAULT_EYE_ALIGNMENT_BIAS_PX = 50;
1524
1523
  static DEFAULT_IMAGE_FORMAT = 'image/jpeg';
1525
1524
  static DEFAULT_IMAGE_QUALITY_START = 0.85;
@@ -1533,7 +1532,6 @@ class ProfileComparisonLibComponent {
1533
1532
  static DEFAULT_MAX_HEIGHT = 1600;
1534
1533
  static SPACER_SMALL = '-';
1535
1534
  static SPACER_LARGE = '----';
1536
- static PLACEHOLDER = '--';
1537
1535
  static CONSOLIDATION_SEPARATOR_X = '×';
1538
1536
  static CONSOLIDATION_SEPARATOR_SLASH = '/';
1539
1537
  static FACEPP_RATE_LIMIT_DELAY_MS = 5000;
@@ -1541,8 +1539,8 @@ class ProfileComparisonLibComponent {
1541
1539
  // Drag and shape constants
1542
1540
  static SHAPE_BG_HEIGHT = '350px';
1543
1541
  static SHAPE_BG_OBJECT_FIT = 'cover';
1544
- static SHAPE_BG1_BACK_POSITION = 'right center';
1545
- static SHAPE_BG2_BACK_POSITION = 'left center';
1542
+ static SHAPE_BG1_OBJECT_POSITION = 'right';
1543
+ static SHAPE_BG2_OBJECT_POSITION = 'left';
1546
1544
  static DRAG_MIN_WIDTH = 200;
1547
1545
  static DRAG_MAX_WIDTH = 400;
1548
1546
  static MAX_DRAG_DISTANCE = 100;
@@ -1558,28 +1556,30 @@ class ProfileComparisonLibComponent {
1558
1556
  person3Interests: [],
1559
1557
  user1Image: '',
1560
1558
  user2Image: '',
1561
- edgeShading: true,
1562
- shadingColor: 'rgba(44, 40, 47, 0.7)'
1563
1559
  };
1564
1560
  backendMode = BackendMode.Real;
1565
1561
  backendUrl = null;
1566
1562
  fadeAllEdges = true;
1563
+ person1Name = 'Josh';
1564
+ person2Name = 'Amy';
1567
1565
  matrixDataChange = new EventEmitter();
1568
1566
  rawLLMOutputChange = new EventEmitter();
1569
1567
  viewProfileClick = new EventEmitter();
1570
- /** Fires when the user drags a frame to its maximum displacement and
1571
- * continues pulling past the edge. The parent can use this to swipe to
1572
- * the next/previous prospect in its own swiper. */
1573
- edgeDrag = new EventEmitter();
1574
1568
  selectedFile = null;
1575
1569
  firstImageData = null;
1576
1570
  secondImageData = null;
1577
1571
  user1Transform = '';
1578
1572
  user2Transform = '';
1573
+ user1Top = '0px';
1574
+ user1Height = '100%';
1575
+ user2Top = '0px';
1576
+ user2Height = '100%';
1579
1577
  alignmentCalculated = false;
1580
1578
  isAligning = false;
1581
- user1ObjectPosition = ProfileComparisonLibComponent.SHAPE_BG1_BACK_POSITION;
1582
- user2ObjectPosition = ProfileComparisonLibComponent.SHAPE_BG2_BACK_POSITION;
1579
+ /** Set when backend returns an error (e.g. 503 API keys not configured). */
1580
+ backendError = null;
1581
+ user1ObjectPosition = ProfileComparisonLibComponent.DEFAULT_OBJECT_POSITION;
1582
+ user2ObjectPosition = ProfileComparisonLibComponent.DEFAULT_OBJECT_POSITION;
1583
1583
  user1NaturalWidth = 0;
1584
1584
  user1NaturalHeight = 0;
1585
1585
  user2NaturalWidth = 0;
@@ -1609,34 +1609,6 @@ class ProfileComparisonLibComponent {
1609
1609
  alignedPerson2Interests = [];
1610
1610
  leftProfileClicked = false;
1611
1611
  rightProfileClicked = false;
1612
- leftShapeAnimating = false;
1613
- rightShapeAnimating = false;
1614
- similarities = [];
1615
- // ── Small-screen responsive helpers ──────────────────────────────────────
1616
- get viewProfileLeftX() {
1617
- const w = window.innerWidth;
1618
- if (w <= 360)
1619
- return 65;
1620
- if (w <= 400)
1621
- return 52;
1622
- if (w <= 600)
1623
- return 48;
1624
- return 15;
1625
- }
1626
- get viewProfileRightX() {
1627
- const w = window.innerWidth;
1628
- if (w <= 360)
1629
- return 191;
1630
- if (w <= 400)
1631
- return 204;
1632
- if (w <= 600)
1633
- return 208;
1634
- return 241;
1635
- }
1636
- onResize() {
1637
- this.cdr.detectChanges();
1638
- }
1639
- // ─────────────────────────────────────────────────────────────────────────
1640
1612
  sortedA = [];
1641
1613
  sortedB = [];
1642
1614
  sortedC = [];
@@ -1647,8 +1619,6 @@ class ProfileComparisonLibComponent {
1647
1619
  backendConfigured = false;
1648
1620
  /** The resolved backend URL computed from inputs and legacy token. */
1649
1621
  resolvedBackendUrl = null;
1650
- /** Flag to prevent click animations after a drag. */
1651
- wasDragging = false;
1652
1622
  compressionConfig = {
1653
1623
  maxWidth: ProfileComparisonLibComponent.DEFAULT_MAX_WIDTH,
1654
1624
  maxHeight: ProfileComparisonLibComponent.DEFAULT_MAX_HEIGHT,
@@ -1666,11 +1636,15 @@ class ProfileComparisonLibComponent {
1666
1636
  fetchRequestId = 0;
1667
1637
  leftContainer;
1668
1638
  rightContainer;
1669
- leftFrame;
1670
- rightFrame;
1671
- leftBgFrame;
1672
- rightBgFrame;
1639
+ profileFlex;
1640
+ profileImgLeft;
1641
+ profileImgRight;
1673
1642
  shapeContainer;
1643
+ shapeBg;
1644
+ shapeBg1;
1645
+ shapeBg2;
1646
+ shapeTextLeft;
1647
+ shapeTextRight;
1674
1648
  shapeTextCenter;
1675
1649
  constructor(backendService, renderer, fileConversionService, imageCompressionService, cdr, getVerboseLogging) {
1676
1650
  this.backendService = backendService;
@@ -1711,15 +1685,19 @@ class ProfileComparisonLibComponent {
1711
1685
  this.user1Transform = 'translateY(0px)';
1712
1686
  this.user2Transform = 'translateY(0px)';
1713
1687
  this.alignmentCalculated = false;
1688
+ this.isAligning = true;
1689
+ this.cdr.detectChanges();
1714
1690
  this.log('ngOnInit', 'starting compressConfigImagesIfNeeded then fetchComparison');
1715
1691
  this.compressConfigImagesIfNeeded().subscribe({
1716
1692
  next: () => {
1717
1693
  this.log('ngOnInit', 'compressConfigImagesIfNeeded done, calling fetchComparison');
1694
+ this.isAligning = false;
1718
1695
  this.fetchComparison();
1719
1696
  },
1720
1697
  error: (e) => {
1721
1698
  console.warn('Config image compression failed, continuing:', e);
1722
1699
  this.log('ngOnInit', 'compressConfigImagesIfNeeded error, calling fetchComparison anyway');
1700
+ this.isAligning = false;
1723
1701
  this.fetchComparison();
1724
1702
  }
1725
1703
  });
@@ -1733,16 +1711,9 @@ class ProfileComparisonLibComponent {
1733
1711
  this.backendConfigured = !!this.resolvedBackendUrl;
1734
1712
  this.log('ngOnChanges', 'backend inputs changed', 'resolvedUrl', this.resolvedBackendUrl ?? '(null)');
1735
1713
  }
1736
- if (changes['fadeAllEdges']) {
1737
- this.config.edgeShading = this.fadeAllEdges;
1738
- }
1739
1714
  if (changes['config']) {
1740
1715
  const first = changes['config'].firstChange;
1741
1716
  this.log('ngOnChanges', 'config changed', 'firstChange', first, 'backendConfigured', this.backendConfigured);
1742
- // Sync fadeAllEdges if it was explicitly changed via Input but not in config
1743
- if (this.config.edgeShading === undefined) {
1744
- this.config.edgeShading = this.fadeAllEdges;
1745
- }
1746
1717
  this.updateConfigProperties();
1747
1718
  if (!this.backendConfigured) {
1748
1719
  this.log('ngOnChanges', 'skipping fetch: backend not configured');
@@ -1767,19 +1738,12 @@ class ProfileComparisonLibComponent {
1767
1738
  }
1768
1739
  }
1769
1740
  }
1770
- /**
1771
- * Returns the type of dash based on similarity score:
1772
- * - 'high': Very similar (Short single dash, 0.5 opacity)
1773
- * - 'medium': Almost similar (Standard 1 dash)
1774
- * - 'low': Very dissimilar (4 small dashes)
1775
- */
1776
- getDashType(index) {
1777
- const score = this.similarities[index] ?? 0;
1778
- if (score > 0.8)
1779
- return 'high';
1780
- if (score > 0.4)
1781
- return 'medium';
1782
- return 'low';
1741
+ /** Use for template: hide left/right item when it's a shared (center) item. Compares case-insensitively. */
1742
+ isInCenter(interest) {
1743
+ if (!interest || !this.centerItem?.length)
1744
+ return false;
1745
+ const n = String(interest).trim().toLowerCase();
1746
+ return this.centerItem.some((c) => String(c).trim().toLowerCase() === n);
1783
1747
  }
1784
1748
  fetchComparison() {
1785
1749
  this.log('fetchComparison', 'start');
@@ -1793,6 +1757,8 @@ class ProfileComparisonLibComponent {
1793
1757
  }
1794
1758
  const requestId = ++this.fetchRequestId;
1795
1759
  this.isAligning = true;
1760
+ this.backendError = null;
1761
+ this.cdr.detectChanges();
1796
1762
  const normalized = this.buildConfigWithDefaults(this.config, this.baseConfig);
1797
1763
  const configToSend = {
1798
1764
  person1Interests: normalized.person1Interests,
@@ -1815,17 +1781,32 @@ class ProfileComparisonLibComponent {
1815
1781
  }
1816
1782
  this.log('fetchComparison', 'backend response received', { aligned1: payload?.alignedPerson1Interests?.length, aligned2: payload?.alignedPerson2Interests?.length });
1817
1783
  this.isAligning = false;
1818
- const a1 = payload.alignedPerson1Interests ?? [];
1819
- const a2 = payload.alignedPerson2Interests ?? [];
1820
- this.alignedPerson1Interests = a1;
1821
- this.alignedPerson2Interests = a2;
1822
- this.centerItem = payload.centerItem ?? [];
1823
- this.similarities = payload.similarities ?? [];
1824
- this.displayPerson1Interests = this.alignedPerson1Interests;
1825
- this.displayPerson2Interests = this.alignedPerson2Interests;
1784
+ this.backendError = null;
1826
1785
  this.matrixData = payload.matrixData ?? null;
1827
1786
  if (this.matrixData)
1828
1787
  this.matrixDataChange.emit(this.matrixData);
1788
+ const a1 = payload.alignedPerson1Interests ?? [];
1789
+ const a2 = payload.alignedPerson2Interests ?? [];
1790
+ const sameLists = a1.length === a2.length && a1.every((v, i) => String(v).trim().toLowerCase() === String(a2[i]).trim().toLowerCase());
1791
+ if (sameLists && a1.length > 0) {
1792
+ this.log('fetchComparison', 'backend returned identical aligned lists; using raw lists to keep sides distinct', { len: a1.length });
1793
+ this.alignedPerson1Interests = [];
1794
+ this.alignedPerson2Interests = [];
1795
+ this.displayPerson1Interests = this.person1Interests;
1796
+ this.displayPerson2Interests = this.person2Interests;
1797
+ this.centerItem = payload.centerItem ?? [];
1798
+ }
1799
+ else {
1800
+ const rawCenter = payload.centerItem ?? [];
1801
+ const list1ToUse = this.person1Interests?.length ? this.person1Interests : a1;
1802
+ const list2ToUse = this.person2Interests?.length ? this.person2Interests : a2;
1803
+ const reordered = this.reorderInterestsByMatrix(list1ToUse, list2ToUse, rawCenter);
1804
+ this.alignedPerson1Interests = reordered.p1;
1805
+ this.alignedPerson2Interests = reordered.p2;
1806
+ this.displayPerson1Interests = reordered.p1;
1807
+ this.displayPerson2Interests = reordered.p2;
1808
+ this.centerItem = reordered.center;
1809
+ }
1829
1810
  if (payload.rawLLMOutput != null) {
1830
1811
  this.rawLLMOutput = payload.rawLLMOutput;
1831
1812
  this.rawLLMOutputChange.emit(payload.rawLLMOutput);
@@ -1851,6 +1832,7 @@ class ProfileComparisonLibComponent {
1851
1832
  }
1852
1833
  this.log('fetchComparison', 'backend error', err);
1853
1834
  const msg = err?.error?.message ?? err?.message ?? err?.statusText ?? (typeof err?.error === 'string' ? err.error : null) ?? 'Request failed';
1835
+ this.backendError = msg;
1854
1836
  console.error('[ProfileComparisonLib] fetchComparison error', err?.status ?? '', msg);
1855
1837
  this.isAligning = false;
1856
1838
  this.cdr.detectChanges();
@@ -1864,6 +1846,10 @@ class ProfileComparisonLibComponent {
1864
1846
  this.person2Interests = [...normalized.person2Interests];
1865
1847
  this.user1Image = normalized.user1Image;
1866
1848
  this.user2Image = normalized.user2Image;
1849
+ if (normalized.person1Name)
1850
+ this.person1Name = normalized.person1Name;
1851
+ if (normalized.person2Name)
1852
+ this.person2Name = normalized.person2Name;
1867
1853
  }
1868
1854
  buildConfigWithDefaults(cfg, base) {
1869
1855
  const list = (val, fb) => {
@@ -1882,13 +1868,13 @@ class ProfileComparisonLibComponent {
1882
1868
  };
1883
1869
  const safe = cfg || {};
1884
1870
  return {
1871
+ person1Name: safe.person1Name ? str(safe.person1Name, '') : undefined,
1872
+ person2Name: safe.person2Name ? str(safe.person2Name, '') : undefined,
1885
1873
  person1Interests: list(safe.person1Interests, base.person1Interests),
1886
1874
  person2Interests: list(safe.person2Interests, base.person2Interests),
1887
1875
  person3Interests: list(safe.person3Interests, base.person3Interests),
1888
1876
  user1Image: str(safe.user1Image, base.user1Image),
1889
1877
  user2Image: str(safe.user2Image, base.user2Image),
1890
- edgeShading: safe.edgeShading ?? base.edgeShading ?? true,
1891
- shadingColor: safe.shadingColor ?? base.shadingColor ?? 'rgba(44, 40, 47, 0.7)',
1892
1878
  };
1893
1879
  }
1894
1880
  onUserImageLoad(which, event) {
@@ -1918,9 +1904,9 @@ class ProfileComparisonLibComponent {
1918
1904
  const natH = which === 1 ? this.user1NaturalHeight : this.user2NaturalHeight;
1919
1905
  if (!face || !natW || !natH) {
1920
1906
  if (which === 1)
1921
- this.user1ObjectPosition = ProfileComparisonLibComponent.SHAPE_BG1_BACK_POSITION;
1907
+ this.user1ObjectPosition = ProfileComparisonLibComponent.DEFAULT_OBJECT_POSITION;
1922
1908
  else
1923
- this.user2ObjectPosition = ProfileComparisonLibComponent.SHAPE_BG2_BACK_POSITION;
1909
+ this.user2ObjectPosition = ProfileComparisonLibComponent.DEFAULT_OBJECT_POSITION;
1924
1910
  return;
1925
1911
  }
1926
1912
  const eyes = this.getEyeCoordinatesFromFace(face);
@@ -2016,7 +2002,7 @@ class ProfileComparisonLibComponent {
2016
2002
  }
2017
2003
  getProfileContainerSize(which) {
2018
2004
  try {
2019
- const el = (which === 1 ? this.leftFrame : this.rightFrame)?.nativeElement || null;
2005
+ const el = (which === 1 ? this.profileImgLeft : this.profileImgRight)?.nativeElement || null;
2020
2006
  if (!el)
2021
2007
  return { width: 0, height: 0 };
2022
2008
  const rect = el.getBoundingClientRect();
@@ -2027,384 +2013,297 @@ class ProfileComparisonLibComponent {
2027
2013
  }
2028
2014
  }
2029
2015
  getOverlapSizeCssPx() {
2030
- return 40;
2016
+ try {
2017
+ const root = this.profileFlex?.nativeElement || null;
2018
+ const fallback = 40;
2019
+ if (!root)
2020
+ return fallback;
2021
+ const val = getComputedStyle(root).getPropertyValue('--overlap-size').trim();
2022
+ const parsed = parseFloat(val);
2023
+ return isNaN(parsed) ? fallback : parsed;
2024
+ }
2025
+ catch {
2026
+ return 40;
2027
+ }
2031
2028
  }
2032
2029
  setOverlapSizeCssPx(px) {
2033
- // deprecated
2030
+ try {
2031
+ const root = this.profileFlex?.nativeElement || null;
2032
+ if (!root)
2033
+ return;
2034
+ const clamped = Math.max(8, Math.min(80, Math.round(px)));
2035
+ root.style.setProperty('--overlap-size', `${clamped}px`);
2036
+ }
2037
+ catch { }
2034
2038
  }
2035
2039
  waitForImagesAndInitDrag() {
2036
- const leftFrame = this.leftFrame?.nativeElement;
2037
- const rightFrame = this.rightFrame?.nativeElement;
2038
- if (!leftFrame || !rightFrame) {
2039
- setTimeout(() => this.waitForImagesAndInitDrag(), 100);
2040
+ const shapeBg1 = this.shapeBg1?.nativeElement;
2041
+ const shapeBg2 = this.shapeBg2?.nativeElement;
2042
+ if (!shapeBg1 || !shapeBg2) {
2040
2043
  return;
2041
2044
  }
2042
- setTimeout(() => {
2043
- this.initDrag();
2044
- // Apply initial edge mask state
2045
- setTimeout(() => this.updateEdgeMasks(), 50);
2046
- }, 100);
2047
- }
2048
- /**
2049
- * Toggles .edge-at-left-border / .edge-at-right-border classes on both
2050
- * interactive and background frames based on their outer edge position
2051
- * relative to the container boundary.
2052
- */
2053
- updateEdgeMasks() {
2054
- const container = this.shapeContainer?.nativeElement;
2055
- if (!container)
2056
- return;
2057
- const containerRect = container.getBoundingClientRect();
2058
- const frames = [
2059
- { side: 'left', el: this.leftFrame?.nativeElement, bg: this.leftBgFrame?.nativeElement },
2060
- { side: 'right', el: this.rightFrame?.nativeElement, bg: this.rightBgFrame?.nativeElement }
2061
- ];
2062
- frames.forEach(f => {
2063
- if (!f.el)
2064
- return;
2065
- const rect = f.el.getBoundingClientRect();
2066
- const className = `edge-at-${f.side}-border`;
2067
- const touches = f.side === 'left'
2068
- ? rect.left <= containerRect.left + 1
2069
- : rect.right >= containerRect.right - 1;
2070
- f.el.classList.toggle(className, touches);
2071
- if (f.bg)
2072
- f.bg.classList.toggle(className, touches);
2073
- });
2045
+ const checkImagesLoaded = () => {
2046
+ // For SVG elements, check if they're rendered instead of naturalWidth
2047
+ const img1Loaded = shapeBg1 && shapeBg1.getBoundingClientRect().width > 0;
2048
+ const img2Loaded = shapeBg2 && shapeBg2.getBoundingClientRect().width > 0;
2049
+ if (img1Loaded && img2Loaded) {
2050
+ setTimeout(() => this.initDrag(), 50);
2051
+ }
2052
+ else {
2053
+ setTimeout(checkImagesLoaded, 100);
2054
+ }
2055
+ };
2056
+ checkImagesLoaded();
2074
2057
  }
2075
2058
  initDrag() {
2076
- const leftFrame = this.leftFrame?.nativeElement;
2077
- const rightFrame = this.rightFrame?.nativeElement;
2078
- const leftBgFrame = this.leftBgFrame?.nativeElement;
2079
- const rightBgFrame = this.rightBgFrame?.nativeElement;
2080
- const shapeTextCenter = this.shapeTextCenter?.nativeElement;
2081
- if (!leftFrame || !rightFrame)
2059
+ const shapeBg1 = this.shapeBg1?.nativeElement;
2060
+ const shapeBg2 = this.shapeBg2?.nativeElement;
2061
+ const shapeTextLeft = this.shapeTextLeft?.nativeElement || null;
2062
+ const shapeTextRight = this.shapeTextRight?.nativeElement || null;
2063
+ if (!shapeBg1 || !shapeBg2 || !shapeTextLeft || !shapeTextRight)
2082
2064
  return;
2083
- // Glass SVG references for shadow fade-on-drag behaviour
2084
- const leftGlass = leftFrame.querySelector('.profile-comparison__frame-svg--glass');
2085
- const rightGlass = rightFrame.querySelector('.profile-comparison__frame-svg--glass');
2065
+ // Cast to HTMLElement to access style properties conveniently, as SVGElement style handling can be similar in this context
2066
+ const shapeBg1El = shapeBg1;
2067
+ const shapeBg2El = shapeBg2;
2068
+ shapeBg1El.style.height = ProfileComparisonLibComponent.SHAPE_BG_HEIGHT;
2069
+ // shapeBg1El.style.objectFit = ProfileComparisonLibComponent.SHAPE_BG_OBJECT_FIT;
2070
+ // shapeBg1El.style.objectPosition = ProfileComparisonLibComponent.SHAPE_BG1_OBJECT_POSITION;
2071
+ shapeBg2El.style.height = ProfileComparisonLibComponent.SHAPE_BG_HEIGHT;
2072
+ // shapeBg2El.style.objectFit = ProfileComparisonLibComponent.SHAPE_BG_OBJECT_FIT;
2073
+ // shapeBg2El.style.objectPosition = ProfileComparisonLibComponent.SHAPE_BG2_OBJECT_POSITION;
2074
+ const originalWidth1 = shapeBg1.getBoundingClientRect().width;
2075
+ const originalWidth2 = shapeBg2.getBoundingClientRect().width;
2076
+ const minWidth = ProfileComparisonLibComponent.DRAG_MIN_WIDTH;
2077
+ const maxWidth = ProfileComparisonLibComponent.DRAG_MAX_WIDTH;
2086
2078
  let isDragging = false;
2087
2079
  let dragStartX = 0;
2088
- let activeFrame = null;
2089
- let edgeDragEmitted = false; // Track whether we have emitted edgeDrag for the current gesture
2090
- const initialTranslateXLeft = -24; // starting CSS value percent
2091
- const initialTranslateXRight = 24;
2080
+ let initialWidth1 = 0;
2081
+ let initialWidth2 = 0;
2082
+ let activeImg = null;
2083
+ // Use ViewChild for center text
2084
+ const shapeTextCenter = this.shapeTextCenter?.nativeElement || null;
2092
2085
  const onMouseMove = (e) => {
2093
- if (!isDragging)
2086
+ if (!isDragging || !activeImg)
2094
2087
  return;
2095
2088
  const deltaX = e.clientX - dragStartX;
2096
- const containerWidth = leftFrame.parentElement?.clientWidth || 360;
2097
- // Current drag delta in container-relative percentage
2098
- let deltaPercent = (deltaX / containerWidth) * 100;
2099
- if (Math.abs(deltaX) > 5) {
2100
- this.wasDragging = true;
2101
- }
2102
- // Check for edge drag swiping (both mouse and touch support!)
2103
- const leftRect = leftFrame.getBoundingClientRect();
2104
- const rightRect = rightFrame.getBoundingClientRect();
2105
- const overlapPx = Math.max(0, leftRect.right - leftRect.left); // Frame width
2106
- const maxDisplacementPx = overlapPx / 3;
2107
- if (!edgeDragEmitted && Math.abs(deltaX) > maxDisplacementPx * 1.05) {
2108
- // Only trigger swipe outward:
2109
- // Left frame dragged further left (deltaX < 0) or Right frame dragged further right (deltaX > 0)
2110
- if ((activeFrame === leftFrame && deltaX < 0) || (activeFrame === rightFrame && deltaX > 0)) {
2111
- edgeDragEmitted = true;
2112
- this.edgeDrag.emit(deltaX < 0 ? 'left' : 'right');
2113
- }
2089
+ const containerWidth = this.shapeContainer?.nativeElement?.clientWidth || 0;
2090
+ let newWidth1 = initialWidth1;
2091
+ let newWidth2 = initialWidth2;
2092
+ if (activeImg === shapeBg1) {
2093
+ newWidth1 = initialWidth1 + deltaX;
2094
+ newWidth2 = initialWidth2 - deltaX;
2114
2095
  }
2115
- // Clamp delta to the pre-calculated displacement limit
2116
- deltaPercent = Math.max(-initialMaxDisplacementPercent, Math.min(initialMaxDisplacementPercent, deltaPercent));
2117
- // Calculate actual frame displacement in pixels for text coordination
2118
- // Frame width is 75% of container width
2119
- const frameMovePx = (deltaPercent / 100) * (containerWidth * 0.75);
2120
- let newLeftTranslate = initialTranslateXLeft;
2121
- let newRightTranslate = initialTranslateXRight;
2122
- // Staggered text movement references
2123
- const leftContent = leftFrame.querySelector('.profile-comparison__content--left');
2124
- const rightContent = rightFrame.querySelector('.profile-comparison__content--right');
2125
- // Mouse-controlled sequential staged text movement (no auto-animations)
2126
- const maxStageMove = Math.abs(initialMaxDisplacementPercent * 0.75 * containerWidth / 100);
2127
- const getStagedX = (current, startPct, endPct, totalDisplacementPx) => {
2128
- const absD = Math.abs(current);
2129
- const startPx = maxStageMove * startPct;
2130
- const endPx = maxStageMove * endPct;
2131
- if (absD <= startPx)
2132
- return 0;
2133
- // Linear progress [0, 1]
2134
- let progress = Math.min(1, (absD - startPx) / (endPx - startPx));
2135
- // Apply smoothstep easing (3x^2 - 2x^3) for soft start/end
2136
- progress = progress * progress * (3 - 2 * progress);
2137
- return progress * totalDisplacementPx * Math.sign(current);
2138
- };
2139
- const leadTarget = 35;
2140
- const followTarget = 40;
2141
- const centerTarget = 15;
2142
- // Use a moderate transition to smooth out mouse jitter while maintaining control
2143
- const textSmoothing = 'transform 0.4s cubic-bezier(0.2, 0, 0.2, 1)';
2144
- if (leftContent)
2145
- leftContent.style.transition = textSmoothing;
2146
- if (rightContent)
2147
- rightContent.style.transition = textSmoothing;
2148
- if (shapeTextCenter)
2149
- shapeTextCenter.style.transition = textSmoothing;
2150
- if (deltaPercent < 0) {
2151
- // Dragging left: only left shape moves further left
2152
- newLeftTranslate = initialTranslateXLeft + deltaPercent;
2153
- leftFrame.style.transform = `translateX(${newLeftTranslate}%) scale(0.86)`;
2154
- if (leftBgFrame) {
2155
- leftBgFrame.style.transform = `translateX(${newLeftTranslate}%) scale(0.86)`;
2156
- const bg = leftBgFrame.querySelector('.profile-comparison__bg');
2157
- if (bg) {
2158
- bg.style.position = 'absolute';
2159
- bg.style.left = '0%';
2160
- bg.style.right = `${deltaPercent}%`; // Expand right when dragging left
2161
- }
2162
- }
2163
- // Ensure right frame is at rest
2164
- rightFrame.style.transform = `translateX(${initialTranslateXRight}%) scale(0.86)`;
2165
- if (rightBgFrame) {
2166
- rightBgFrame.style.transform = `translateX(${initialTranslateXRight}%) scale(0.86)`;
2167
- const bg = rightBgFrame.querySelector('.profile-comparison__bg');
2168
- if (bg) {
2169
- bg.style.position = 'absolute';
2170
- bg.style.left = '0%';
2171
- bg.style.right = '0%';
2172
- bg.style.backgroundPositionX = ''; // restore CSS default (right)
2173
- }
2174
- }
2175
- // Lead = Left, Follow = Right
2176
- if (leftContent)
2177
- leftContent.style.transform = `translateX(${getStagedX(frameMovePx, 0, 0.4, -leadTarget)}px)`;
2178
- if (rightContent)
2179
- rightContent.style.transform = `translateX(${getStagedX(frameMovePx, 0.4, 0.8, followTarget)}px)`;
2180
- if (shapeTextCenter)
2181
- shapeTextCenter.style.transform = `translateX(${getStagedX(frameMovePx, 0.8, 1.0, centerTarget)}px) scale(0.86)`;
2096
+ else if (activeImg === shapeBg2) {
2097
+ newWidth1 = initialWidth1 - deltaX;
2098
+ newWidth2 = initialWidth2 + deltaX;
2182
2099
  }
2183
- else if (deltaPercent > 0) {
2184
- // Dragging right: only right shape moves further right
2185
- newRightTranslate = initialTranslateXRight + deltaPercent;
2186
- rightFrame.style.transform = `translateX(${newRightTranslate}%) scale(0.86)`;
2187
- if (rightBgFrame) {
2188
- rightBgFrame.style.transform = `translateX(${newRightTranslate}%) scale(0.86)`;
2189
- // Same formula as left bg: left:-deltaPercent% keeps image stationary.
2190
- // For right drag deltaPercent>0, so left is negative — div extends
2191
- // left of frame (clipped by overflow:hidden) while image anchor stays put.
2192
- const bg = rightBgFrame.querySelector('.profile-comparison__bg');
2193
- if (bg) {
2194
- bg.style.position = 'absolute';
2195
- bg.style.left = `${-deltaPercent}%`;
2196
- bg.style.right = '0%';
2197
- }
2198
- }
2199
- // Ensure left frame is at rest
2200
- leftFrame.style.transform = `translateX(${initialTranslateXLeft}%) scale(0.86)`;
2201
- if (leftBgFrame) {
2202
- leftBgFrame.style.transform = `translateX(${initialTranslateXLeft}%) scale(0.86)`;
2203
- const bg = leftBgFrame.querySelector('.profile-comparison__bg');
2204
- if (bg) {
2205
- bg.style.position = 'absolute';
2206
- bg.style.left = '0%';
2207
- bg.style.right = '0%';
2208
- }
2209
- }
2210
- // Lead = Right, Follow = Left
2211
- if (rightContent)
2212
- rightContent.style.transform = `translateX(${getStagedX(frameMovePx, 0, 0.4, -leadTarget)}px)`;
2213
- if (leftContent)
2214
- leftContent.style.transform = `translateX(${getStagedX(frameMovePx, 0.4, 0.8, followTarget)}px)`;
2215
- if (shapeTextCenter)
2216
- shapeTextCenter.style.transform = `translateX(${getStagedX(frameMovePx, 0.8, 1.0, centerTarget)}px) scale(0.86)`;
2100
+ newWidth1 = Math.max(minWidth, Math.min(maxWidth, newWidth1));
2101
+ newWidth2 = Math.max(minWidth, Math.min(maxWidth, newWidth2));
2102
+ shapeBg1El.style.width = newWidth1 + 'px';
2103
+ shapeBg1El.style.height = ProfileComparisonLibComponent.SHAPE_BG_HEIGHT;
2104
+ shapeBg2El.style.width = newWidth2 + 'px';
2105
+ shapeBg2El.style.height = ProfileComparisonLibComponent.SHAPE_BG_HEIGHT;
2106
+ const widthChange1 = newWidth1 - originalWidth1;
2107
+ const widthChange2 = newWidth2 - originalWidth2;
2108
+ const dragDirection = deltaX > 0 ? 1 : -1;
2109
+ const dragDistance = Math.abs(deltaX);
2110
+ const maxDragDistance = ProfileComparisonLibComponent.MAX_DRAG_DISTANCE;
2111
+ const centerMoveDistance = Math.min(dragDistance, maxDragDistance) * dragDirection * ProfileComparisonLibComponent.CENTER_MOVE_MULTIPLIER;
2112
+ shapeTextLeft.style.transform = `translateX(${widthChange1 * ProfileComparisonLibComponent.TEXT_MOVE_MULTIPLIER}px)`;
2113
+ shapeTextRight.style.transform = `translateX(${-widthChange2 * ProfileComparisonLibComponent.TEXT_MOVE_MULTIPLIER}px)`;
2114
+ if (shapeTextCenter) {
2115
+ shapeTextCenter.style.transform = `translateX(${centerMoveDistance * ProfileComparisonLibComponent.TEXT_MOVE_MULTIPLIER}px)`;
2217
2116
  }
2218
- // Update edge masks during drag
2219
- this.updateEdgeMasks();
2220
2117
  };
2221
- const resetDrag = () => {
2118
+ const resetWidths = () => {
2222
2119
  isDragging = false;
2223
- activeFrame = null;
2224
- edgeDragEmitted = false; // Reset edge drag flag on release
2225
- const leftContent = leftFrame.querySelector('.profile-comparison__content--left');
2226
- const rightContent = rightFrame.querySelector('.profile-comparison__content--right');
2227
- // Smoother rebound transition
2228
- const reboundTransition = 'transform 0.6s cubic-bezier(0.16, 1, 0.3, 1)';
2229
- [leftFrame, rightFrame, leftBgFrame, rightBgFrame, leftContent, rightContent, shapeTextCenter].forEach(el => {
2230
- if (el) {
2231
- el.style.transition = reboundTransition;
2232
- el.style.transitionDelay = '0s'; // Reset delay for simultaneous rebound
2233
- }
2234
- });
2235
- // On release: instantly flash glass to near-zero, then spring back inside the rAF
2236
- [leftGlass, rightGlass].forEach(gl => {
2237
- if (gl) {
2238
- gl.style.transition = 'none';
2239
- gl.style.opacity = '0.03';
2240
- }
2241
- });
2242
- // Use requestAnimationFrame to ensure transition is applied before position reset
2243
- requestAnimationFrame(() => {
2244
- leftFrame.style.transform = `translateX(-24%) scale(0.86)`;
2245
- rightFrame.style.transform = `translateX(24%) scale(0.86)`;
2246
- if (leftBgFrame) {
2247
- leftBgFrame.style.transform = `translateX(-24%) scale(0.86)`;
2248
- const bg = leftBgFrame.querySelector('.profile-comparison__bg');
2249
- if (bg) {
2250
- bg.style.transition = `left 0.6s cubic-bezier(0.16, 1, 0.3, 1), right 0.6s cubic-bezier(0.16, 1, 0.3, 1), ${reboundTransition}`;
2251
- bg.style.left = '0%';
2252
- bg.style.right = '0%';
2253
- }
2254
- }
2255
- if (rightBgFrame) {
2256
- rightBgFrame.style.transform = `translateX(24%) scale(0.86)`;
2257
- const bg = rightBgFrame.querySelector('.profile-comparison__bg');
2258
- if (bg) {
2259
- bg.style.transition = `left 0.6s cubic-bezier(0.16, 1, 0.3, 1), right 0.6s cubic-bezier(0.16, 1, 0.3, 1), ${reboundTransition}`;
2260
- bg.style.left = '0%';
2261
- bg.style.right = '0%';
2262
- }
2263
- }
2264
- // Force reset all transforms and clear inline styles
2265
- [leftContent, rightContent].forEach(el => {
2266
- if (el) {
2267
- el.style.transform = 'translateX(0px)';
2268
- el.style.transition = reboundTransition;
2269
- }
2270
- });
2271
- if (shapeTextCenter) {
2272
- shapeTextCenter.style.transform = 'translateX(0px) scale(0.86)';
2273
- shapeTextCenter.style.transition = reboundTransition;
2274
- }
2275
- // Restore glass shadow opacity with spring rebound
2276
- [leftGlass, rightGlass].forEach(gl => {
2277
- if (gl) {
2278
- gl.style.transition = 'opacity 0.6s cubic-bezier(0.16, 1, 0.3, 1)';
2279
- gl.style.opacity = '1';
2280
- }
2281
- });
2282
- });
2120
+ activeImg = null;
2121
+ shapeBg1El.style.transition = ProfileComparisonLibComponent.TRANSITION_WIDTH;
2122
+ shapeBg2El.style.transition = ProfileComparisonLibComponent.TRANSITION_WIDTH;
2123
+ shapeTextLeft.style.transition = ProfileComparisonLibComponent.TRANSITION_TRANSFORM;
2124
+ shapeTextRight.style.transition = ProfileComparisonLibComponent.TRANSITION_TRANSFORM;
2125
+ if (shapeTextCenter) {
2126
+ shapeTextCenter.style.transition = ProfileComparisonLibComponent.TRANSITION_TRANSFORM;
2127
+ }
2128
+ shapeBg1El.style.width = originalWidth1 + 'px';
2129
+ shapeBg2El.style.width = originalWidth2 + 'px';
2130
+ shapeTextLeft.style.transform = ProfileComparisonLibComponent.TRANSFORM_RESET;
2131
+ shapeTextRight.style.transform = ProfileComparisonLibComponent.TRANSFORM_RESET;
2132
+ if (shapeTextCenter) {
2133
+ shapeTextCenter.style.transform = ProfileComparisonLibComponent.TRANSFORM_RESET;
2134
+ }
2283
2135
  document.removeEventListener('mousemove', onMouseMove);
2284
- document.removeEventListener('mouseup', resetDrag);
2285
- document.removeEventListener('mouseleave', resetDrag);
2286
- // Re-evaluate edge masks after rebound animation completes
2287
- setTimeout(() => this.updateEdgeMasks(), 650);
2136
+ document.removeEventListener('mouseup', resetWidths);
2137
+ document.removeEventListener('mouseleave', resetWidths);
2288
2138
  setTimeout(() => {
2289
- [leftFrame, rightFrame, leftBgFrame, rightBgFrame, leftContent, rightContent, shapeTextCenter].forEach(el => {
2290
- if (el)
2291
- el.style.transition = '';
2292
- // Also clear image transitions and positions
2293
- if (el === leftBgFrame || el === rightBgFrame) {
2294
- const bg = el.querySelector('.profile-comparison__bg');
2295
- if (bg) {
2296
- bg.style.transition = '';
2297
- bg.style.left = '';
2298
- bg.style.right = '';
2299
- bg.style.backgroundPositionX = '';
2300
- }
2301
- }
2302
- });
2303
- // Clear glass opacity inline style after transition completes
2304
- [leftGlass, rightGlass].forEach(gl => {
2305
- if (gl) {
2306
- gl.style.transition = '';
2307
- gl.style.opacity = '';
2308
- }
2309
- });
2310
- }, 600);
2139
+ shapeBg1El.style.transition = '';
2140
+ shapeBg2El.style.transition = '';
2141
+ shapeTextLeft.style.transition = '';
2142
+ shapeTextRight.style.transition = '';
2143
+ if (shapeTextCenter) {
2144
+ shapeTextCenter.style.transition = '';
2145
+ }
2146
+ }, ProfileComparisonLibComponent.TRANSITION_RESET_DELAY_MS);
2311
2147
  };
2312
- let initialMaxDisplacementPercent = 0;
2313
- [leftFrame, rightFrame].forEach((frame) => {
2314
- // Ensure cursor visually indicates grab on the svg images inside the frame
2315
- const svgs = frame.querySelectorAll('svg');
2316
- svgs.forEach(s => s.style.cursor = 'grab');
2317
- frame.addEventListener('mousedown', (e) => {
2318
- // Prevent default text selection during drag
2148
+ [shapeBg1, shapeBg2].forEach((img) => {
2149
+ img.addEventListener('mousedown', (e) => {
2319
2150
  e.preventDefault();
2320
2151
  isDragging = true;
2321
- this.wasDragging = false;
2322
- activeFrame = frame; // Track which frame is active
2152
+ activeImg = img;
2323
2153
  dragStartX = e.clientX;
2324
- // Lighten shadow on grab, then slowly return to normal while holding
2325
- const activeGlass = frame === leftFrame ? leftGlass : rightGlass;
2326
- if (activeGlass) {
2327
- // Instant drop on grab
2328
- activeGlass.style.transition = 'none';
2329
- activeGlass.style.opacity = '0.15';
2330
- // Next frame: begin slow return to normal while user holds
2331
- requestAnimationFrame(() => {
2332
- if (activeGlass) {
2333
- activeGlass.style.transition = 'opacity 1.5s ease-in';
2334
- activeGlass.style.opacity = '1';
2335
- }
2336
- });
2337
- }
2338
- // Calculate initial max displacement once at the start of drag
2339
- const containerWidth = leftFrame.parentElement?.clientWidth || 360;
2340
- const leftRect = leftFrame.getBoundingClientRect();
2341
- const rightRect = rightFrame.getBoundingClientRect();
2342
- const overlapPx = Math.max(0, leftRect.right - rightRect.left);
2343
- const maxDisplacementPx = overlapPx / 3;
2344
- initialMaxDisplacementPercent = (maxDisplacementPx / containerWidth) * 100;
2154
+ initialWidth1 = shapeBg1.getBoundingClientRect().width;
2155
+ initialWidth2 = shapeBg2.getBoundingClientRect().width;
2345
2156
  document.addEventListener('mousemove', onMouseMove);
2346
- document.addEventListener('mouseup', resetDrag);
2347
- document.addEventListener('mouseleave', resetDrag);
2157
+ document.addEventListener('mouseup', resetWidths);
2158
+ document.addEventListener('mouseleave', resetWidths);
2348
2159
  });
2349
- frame.addEventListener('touchstart', (e) => {
2350
- // Prevent default to avoid scrolling/swiping
2351
- if (e.cancelable)
2352
- e.preventDefault();
2353
- // Stop propagation to prevent swiper from seeing this touch
2354
- e.stopPropagation();
2355
- isDragging = true;
2356
- this.wasDragging = false;
2357
- activeFrame = frame; // Track which frame is active
2358
- dragStartX = e.touches[0].clientX;
2359
- const activeGlass = frame === leftFrame ? leftGlass : rightGlass;
2360
- if (activeGlass) {
2361
- activeGlass.style.transition = 'none';
2362
- activeGlass.style.opacity = '0.15';
2363
- requestAnimationFrame(() => {
2364
- if (activeGlass) {
2365
- activeGlass.style.transition = 'opacity 1.5s ease-in';
2366
- activeGlass.style.opacity = '1';
2367
- }
2368
- });
2369
- }
2370
- const onTouchMove = (ev) => {
2371
- onMouseMove({ clientX: ev.touches[0].clientX });
2372
- };
2373
- const onTouchEnd = () => {
2374
- resetDrag();
2375
- document.removeEventListener('touchmove', onTouchMove);
2376
- document.removeEventListener('touchend', onTouchEnd);
2377
- };
2378
- document.addEventListener('touchmove', onTouchMove, { passive: false });
2379
- document.addEventListener('touchend', onTouchEnd);
2380
- document.addEventListener('touchcancel', onTouchEnd);
2381
- }, { passive: false });
2382
2160
  });
2383
2161
  }
2384
- onViewProfile(side) {
2385
- if (side === 'left') {
2386
- this.leftProfileClicked = true;
2387
- setTimeout(() => this.leftProfileClicked = false, 650);
2162
+ getSimilarity(item1, item2) {
2163
+ if (!this.matrixData || !item1 || !item2)
2164
+ return 0;
2165
+ const n1 = String(item1).trim().toLowerCase();
2166
+ const n2 = String(item2).trim().toLowerCase();
2167
+ if (n1 === n2)
2168
+ return 1.0;
2169
+ const leg1 = this.matrixData.legend?.find(l => l.full.trim().toLowerCase() === n1);
2170
+ const leg2 = this.matrixData.legend?.find(l => l.full.trim().toLowerCase() === n2);
2171
+ if (!leg1 || !leg2)
2172
+ return 0;
2173
+ const colIdx = this.matrixData.headers?.indexOf(leg2.abbr);
2174
+ if (colIdx === -1 || colIdx === undefined)
2175
+ return 0;
2176
+ const row = this.matrixData.rows?.find(r => r.label === leg1.abbr);
2177
+ if (!row || !row.values || colIdx >= row.values.length)
2178
+ return 0;
2179
+ const val = parseFloat(row.values[colIdx]);
2180
+ return isNaN(val) ? 0 : val;
2181
+ }
2182
+ reorderInterestsByMatrix(p1List, p2List, centerList) {
2183
+ if (!this.matrixData)
2184
+ return { p1: p1List, p2: p2List, center: centerList };
2185
+ const cleanCenter = (centerList || []).filter(c => c && c !== '-' && c !== '----');
2186
+ const list1 = (p1List || []).filter(item => item && item !== '-' && item !== '----');
2187
+ const list2 = (p2List || []).filter(item => item && item !== '-' && item !== '----');
2188
+ const ordered1 = [];
2189
+ const ordered2 = [];
2190
+ const used1 = new Set();
2191
+ const used2 = new Set();
2192
+ // 1. PRIORITIZE LEFT AND RIGHT SIMILARITY (Pair ordering from top to bottom)
2193
+ const remaining1 = [...list1];
2194
+ const remaining2 = [...list2];
2195
+ while (remaining1.length > 0 && remaining2.length > 0) {
2196
+ let bestPair = { i1: -1, i2: -1, score: -1 };
2197
+ for (let i = 0; i < remaining1.length; i++) {
2198
+ for (let j = 0; j < remaining2.length; j++) {
2199
+ const score = this.getSimilarity(remaining1[i], remaining2[j]);
2200
+ if (score > bestPair.score) {
2201
+ bestPair = { i1: i, i2: j, score };
2202
+ }
2203
+ }
2204
+ }
2205
+ if (bestPair.score >= 0.15 && bestPair.i1 !== -1 && bestPair.i2 !== -1) {
2206
+ const item1 = remaining1[bestPair.i1];
2207
+ const item2 = remaining2[bestPair.i2];
2208
+ ordered1.push(item1);
2209
+ ordered2.push(item2);
2210
+ used1.add(item1);
2211
+ used2.add(item2);
2212
+ remaining1.splice(bestPair.i1, 1);
2213
+ remaining2.splice(bestPair.i2, 1);
2214
+ }
2215
+ else {
2216
+ break;
2217
+ }
2388
2218
  }
2389
- else {
2390
- this.rightProfileClicked = true;
2391
- setTimeout(() => this.rightProfileClicked = false, 650);
2219
+ // 2. FOR REMAINING UNMATCHED LEFT & RIGHT, ORDER BY RELEVANCE TO ALREADY PLACED ITEMS
2220
+ const unplaced1 = list1.filter(item => !used1.has(item));
2221
+ const unplaced2 = list2.filter(item => !used2.has(item));
2222
+ const rankUnplaced = (items, placedContext) => {
2223
+ return items.map(item => {
2224
+ let maxSim = 0;
2225
+ placedContext.forEach(placed => {
2226
+ const s = this.getSimilarity(item, placed);
2227
+ if (s > maxSim)
2228
+ maxSim = s;
2229
+ });
2230
+ return { item, maxSim };
2231
+ }).sort((a, b) => b.maxSim - a.maxSim).map(x => x.item);
2232
+ };
2233
+ const allPlaced = [...ordered1, ...ordered2];
2234
+ const sortedUnplaced1 = rankUnplaced(unplaced1, allPlaced);
2235
+ const sortedUnplaced2 = rankUnplaced(unplaced2, allPlaced);
2236
+ sortedUnplaced1.forEach(item => {
2237
+ ordered1.push(item);
2238
+ used1.add(item);
2239
+ });
2240
+ sortedUnplaced2.forEach(item => {
2241
+ ordered2.push(item);
2242
+ used2.add(item);
2243
+ });
2244
+ // 3. REARRANGE MIDDLE (CENTER) HIERARCHY TO MATCH EXACT ROW POSITIONS OF LEFT AND RIGHT
2245
+ const orderedCenter = [];
2246
+ const usedCenter = new Set();
2247
+ const maxRows = Math.max(ordered1.length, ordered2.length);
2248
+ for (let k = 0; k < maxRows; k++) {
2249
+ const item1 = ordered1[k];
2250
+ const item2 = ordered2[k];
2251
+ let bestCenterMatch = { item: '', score: -1 };
2252
+ cleanCenter.forEach(cItem => {
2253
+ if (usedCenter.has(cItem))
2254
+ return;
2255
+ const score1 = item1 ? this.getSimilarity(cItem, item1) : 0;
2256
+ const score2 = item2 ? this.getSimilarity(cItem, item2) : 0;
2257
+ const maxScore = Math.max(score1, score2);
2258
+ if (maxScore > bestCenterMatch.score) {
2259
+ bestCenterMatch = { item: cItem, score: maxScore };
2260
+ }
2261
+ });
2262
+ if (bestCenterMatch.score >= 0.45 && bestCenterMatch.item) {
2263
+ orderedCenter.push(bestCenterMatch.item);
2264
+ usedCenter.add(bestCenterMatch.item);
2265
+ }
2266
+ else {
2267
+ orderedCenter.push('-'); // Insert slot spacer so center item pairs directly with its row
2268
+ }
2392
2269
  }
2270
+ // Append any remaining center items not yet matched to a row
2271
+ cleanCenter.forEach(cItem => {
2272
+ if (!usedCenter.has(cItem)) {
2273
+ orderedCenter.push(cItem);
2274
+ usedCenter.add(cItem);
2275
+ }
2276
+ });
2277
+ return { p1: ordered1, p2: ordered2, center: orderedCenter };
2278
+ }
2279
+ onViewProfile(side) {
2393
2280
  this.viewProfileClick.emit({ side });
2394
2281
  }
2395
- onShapeClick(side) {
2396
- if (this.wasDragging) {
2397
- this.wasDragging = false;
2398
- return;
2399
- }
2400
- if (side === 'left') {
2401
- this.leftShapeAnimating = true;
2402
- setTimeout(() => this.leftShapeAnimating = false, 550);
2403
- }
2404
- else {
2405
- this.rightShapeAnimating = true;
2406
- setTimeout(() => this.rightShapeAnimating = false, 550);
2407
- }
2282
+ getPerson1UniqueInterests() {
2283
+ const raw = this.alignedPerson1Interests.length > 0
2284
+ ? this.alignedPerson1Interests
2285
+ : this.displayPerson1Interests.length > 0
2286
+ ? this.displayPerson1Interests
2287
+ : this.person1Interests;
2288
+ return raw.filter(item => item && item !== '-' && item !== '----' && !this.isInCenter(item));
2289
+ }
2290
+ getPerson2UniqueInterests() {
2291
+ const raw = this.alignedPerson2Interests.length > 0
2292
+ ? this.alignedPerson2Interests
2293
+ : this.displayPerson2Interests.length > 0
2294
+ ? this.displayPerson2Interests
2295
+ : this.person2Interests;
2296
+ return raw.filter(item => item && item !== '-' && item !== '----' && !this.isInCenter(item));
2297
+ }
2298
+ getChipOffset(index, column) {
2299
+ const leftOffsets = [0, 20, 6, 26, 12, 30, 4, 18];
2300
+ const rightOffsets = [0, 20, 6, 26, 12, 30, 4, 18];
2301
+ const centerOffsets = [0, -14, 12, -6, 16, -10, 8, -4];
2302
+ if (column === 'left')
2303
+ return leftOffsets[index % leftOffsets.length];
2304
+ if (column === 'right')
2305
+ return rightOffsets[index % rightOffsets.length];
2306
+ return centerOffsets[index % centerOffsets.length];
2408
2307
  }
2409
2308
  compressConfigImagesIfNeeded() {
2410
2309
  const tasks = [];
@@ -2503,50 +2402,62 @@ class ProfileComparisonLibComponent {
2503
2402
  const user2FaceCoords = this.getFaceCoordinatesFromBBox(face2);
2504
2403
  const user1Eyes = this.getEyeCoordinatesFromFace(face1);
2505
2404
  const user2Eyes = this.getEyeCoordinatesFromFace(face2);
2506
- const leftContainerEl = this.leftFrame?.nativeElement || null;
2507
- const rightContainerEl = this.rightFrame?.nativeElement || null;
2405
+ const leftContainerEl = this.profileImgLeft?.nativeElement || null;
2406
+ const rightContainerEl = this.profileImgRight?.nativeElement || null;
2508
2407
  const containerWidth1 = leftContainerEl?.clientWidth || 160;
2509
2408
  const containerHeight1 = leftContainerEl?.clientHeight || 550;
2510
2409
  const containerWidth2 = rightContainerEl?.clientWidth || 160;
2511
2410
  const containerHeight2 = rightContainerEl?.clientHeight || 550;
2512
2411
  const scale1 = this.user1NaturalWidth ? Math.max(containerWidth1 / this.user1NaturalWidth, containerHeight1 / this.user1NaturalHeight) : 1;
2513
2412
  const scale2 = this.user2NaturalWidth ? Math.max(containerWidth2 / this.user2NaturalWidth, containerHeight2 / this.user2NaturalHeight) : 1;
2514
- const displayedFaceHeight1 = face1.height * scale1;
2515
- const displayedFaceHeight2 = face2.height * scale2;
2516
- const ratio1 = containerHeight1 ? displayedFaceHeight1 / containerHeight1 : 0;
2517
- const ratio2 = containerHeight2 ? displayedFaceHeight2 / containerHeight2 : 0;
2518
- let finalScale1 = ratio1 > 0 && ratio1 < 0.22 ? Math.min(3, 0.32 / ratio1) : 1;
2519
- let finalScale2 = ratio2 > 0 && ratio2 < 0.22 ? Math.min(3, 0.32 / ratio2) : 1;
2520
- const effRatio1 = ratio1 * finalScale1;
2521
- const effRatio2 = ratio2 * finalScale2;
2522
- if (effRatio1 > effRatio2 * 1.35)
2523
- finalScale1 = Math.max(0.95, finalScale1 * (effRatio2 / effRatio1));
2524
- else if (effRatio2 > effRatio1 * 1.35)
2525
- finalScale2 = Math.max(0.95, finalScale2 * (effRatio1 / effRatio2));
2526
2413
  const nose1 = this.getNoseCoordinateFromFace(face1);
2527
2414
  const nose2 = this.getNoseCoordinateFromFace(face2);
2528
2415
  const anchor1Y = (Math.min(user1Eyes.leftEye.y, user1Eyes.rightEye.y) + nose1.y) / 2;
2529
2416
  const anchor2Y = (Math.min(user2Eyes.leftEye.y, user2Eyes.rightEye.y) + nose2.y) / 2;
2530
- const tCenter1 = this.user1NaturalHeight ? -((anchor1Y - this.user1NaturalHeight / 2) * scale1 * finalScale1) : 0;
2531
- const tCenter2 = this.user2NaturalHeight ? -((anchor2Y - this.user2NaturalHeight / 2) * scale2 * finalScale2) : 0;
2532
- this.user1Transform = `translateY(${tCenter1 - this.eyeAlignmentBiasPx}px) scale(${finalScale1})`;
2533
- this.user2Transform = `translateY(${tCenter2 - this.eyeAlignmentBiasPx}px) scale(${finalScale2})`;
2417
+ const tCenter1 = this.user1NaturalHeight ? -((anchor1Y - this.user1NaturalHeight / 2) * scale1) : 0;
2418
+ const tCenter2 = this.user2NaturalHeight ? -((anchor2Y - this.user2NaturalHeight / 2) * scale2) : 0;
2419
+ const shiftY1 = tCenter1 - this.eyeAlignmentBiasPx;
2420
+ const shiftY2 = tCenter2 - this.eyeAlignmentBiasPx;
2421
+ // Apply eye-to-eye vertical repositioning transforms without any scaling
2422
+ this.user1Transform = `translateY(${shiftY1}px)`;
2423
+ this.user2Transform = `translateY(${shiftY2}px)`;
2424
+ // Extend image dimensions into the adjusted area without applying any uniform zoom/scaling
2425
+ if (shiftY1 > 0) {
2426
+ this.user1Top = `-${shiftY1}px`;
2427
+ this.user1Height = `calc(100% + ${shiftY1}px)`;
2428
+ }
2429
+ else {
2430
+ this.user1Top = '0px';
2431
+ this.user1Height = `calc(100% + ${Math.abs(shiftY1)}px)`;
2432
+ }
2433
+ if (shiftY2 > 0) {
2434
+ this.user2Top = `-${shiftY2}px`;
2435
+ this.user2Height = `calc(100% + ${shiftY2}px)`;
2436
+ }
2437
+ else {
2438
+ this.user2Top = '0px';
2439
+ this.user2Height = `calc(100% + ${Math.abs(shiftY2)}px)`;
2440
+ }
2534
2441
  this.alignmentCalculated = true;
2535
2442
  }
2536
2443
  applyDefaultAlignment() {
2537
2444
  this.user1Transform = 'translateY(0px)';
2538
2445
  this.user2Transform = 'translateY(0px)';
2446
+ this.user1Top = '0px';
2447
+ this.user1Height = '100%';
2448
+ this.user2Top = '0px';
2449
+ this.user2Height = '100%';
2539
2450
  this.alignmentCalculated = true;
2540
2451
  }
2541
2452
  isValidFaceData(face) {
2542
2453
  return !!face && typeof face.x === 'number' && face.width > 0 && face.height > 0;
2543
2454
  }
2544
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProfileComparisonLibComponent, deps: [{ token: ProfileComparisonBackendService }, { token: i0.Renderer2 }, { token: FileConversionService }, { token: ImageCompressionService }, { token: i0.ChangeDetectorRef }, { token: PROFILE_COMPARISON_VERBOSE_LOGGING, optional: true }], target: i0.ɵɵFactoryTarget.Component });
2545
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.19", type: ProfileComparisonLibComponent, isStandalone: false, selector: "lib-profile-comparison", inputs: { config: "config", backendMode: "backendMode", backendUrl: "backendUrl", fadeAllEdges: "fadeAllEdges" }, outputs: { matrixDataChange: "matrixDataChange", rawLLMOutputChange: "rawLLMOutputChange", viewProfileClick: "viewProfileClick", edgeDrag: "edgeDrag" }, host: { listeners: { "window:resize": "onResize()" } }, viewQueries: [{ propertyName: "leftContainer", first: true, predicate: ["leftContainer"], descendants: true }, { propertyName: "rightContainer", first: true, predicate: ["rightContainer"], descendants: true }, { propertyName: "leftFrame", first: true, predicate: ["leftFrame"], descendants: true }, { propertyName: "rightFrame", first: true, predicate: ["rightFrame"], descendants: true }, { propertyName: "leftBgFrame", first: true, predicate: ["leftBgFrame"], descendants: true }, { propertyName: "rightBgFrame", first: true, predicate: ["rightBgFrame"], descendants: true }, { propertyName: "shapeContainer", first: true, predicate: ["shapeContainer"], descendants: true }, { propertyName: "shapeTextCenter", first: true, predicate: ["shapeTextCenter"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"configure-backend-message\" *ngIf=\"!backendConfigured\">\n Configure backend\n</div>\n\n<div class=\"profile-screen profile-comparison\" *ngIf=\"backendConfigured\"\n [class.fade-all-edges]=\"config.edgeShading !== false\"\n [style.--edge-shading-color]=\"config.shadingColor || 'rgba(44, 40, 47, 0.0)'\"\n [style.--edge-shading-display]=\"config.edgeShading !== false ? 'block' : 'none'\">\n\n <div class=\"profile-comparison__inner\" #shapeContainer>\n <!-- Backgrounds layer: structurally below everything else to fix stacking -->\n <div class=\"profile-comparison__bg-layer\">\n <div class=\"profile-comparison__frame profile-comparison__frame--left bg-frame\"\n [class.is-animating-nudge]=\"leftShapeAnimating\"\n #leftBgFrame>\n <div\n class=\"profile-comparison__bg profile-comparison__bg--left\"\n [ngStyle]=\"{ 'background-image': 'url(' + user1Image + ')', 'transform': user1Transform, 'background-position': user1ObjectPosition }\"\n ></div>\n </div>\n <div class=\"profile-comparison__frame profile-comparison__frame--right bg-frame\"\n [class.is-animating-nudge]=\"rightShapeAnimating\"\n #rightBgFrame>\n <div\n class=\"profile-comparison__bg profile-comparison__bg--right\"\n [ngStyle]=\"{ 'background-image': 'url(' + user2Image + ')', 'transform': user2Transform, 'background-position': user2ObjectPosition }\"\n ></div>\n </div>\n </div>\n\n <!-- Active interactive shapes and content layer -->\n <div class=\"profile-comparison__frame profile-comparison__frame--left swiper-no-swiping\"\n [class.is-animating-nudge]=\"leftShapeAnimating\"\n (click)=\"onShapeClick('left')\"\n #leftFrame>\n <svg\n class=\"profile-comparison__frame-svg profile-comparison__frame-svg--glass\"\n viewBox=\"0 0 256 275\"\n preserveAspectRatio=\"none\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <g filter=\"url(#filter-glass-left)\">\n <path\n d=\"M246.177 26.4924 L10 1.07 Q0 0 0 10 L0 268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924\"\n stroke=\"#61C2AB\"\n stroke-opacity=\"0.3\"\n stroke-width=\"0.809707\"\n stroke-linejoin=\"round\"\n shape-rendering=\"geometricPrecision\"\n />\n </g>\n <defs>\n <filter id=\"filter-glass-left\" x=\"-40\" y=\"-40\" width=\"336\" height=\"355\" filterUnits=\"userSpaceOnUse\" color-interpolation-filters=\"sRGB\">\n <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"BackgroundImageFix\" result=\"effect1_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect1_dropShadow\" result=\"effect2_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect2_dropShadow\" result=\"effect3_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect3_dropShadow\" result=\"effect4_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect4_dropShadow\" result=\"effect5_dropShadow\"/>\n <feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"effect5_dropShadow\" result=\"shape\"/>\n </filter>\n </defs>\n </svg>\n <svg\n class=\"profile-comparison__frame-svg profile-comparison__frame-svg--middle\"\n viewBox=\"0 0 256 276\"\n preserveAspectRatio=\"none\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <defs>\n <clipPath id=\"clip-middle-left\">\n <path d=\"M246.177 26.4924 L10 1.07 Q0 0 0 10 V268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924 Z\"/>\n </clipPath>\n <filter id=\"filter0_i_2126_3746\" x=\"-0.638672\" y=\"-2\" width=\"256\" height=\"279\" filterUnits=\"userSpaceOnUse\" color-interpolation-filters=\"sRGB\">\n <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\" />\n <feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14.2857 0\" result=\"hardAlpha\" />\n <feOffset />\n <feGaussianBlur stdDeviation=\"7.95\" />\n <feComposite in2=\"hardAlpha\" operator=\"arithmetic\" k2=\"-1\" k3=\"1\" />\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0.516667 0 0 0 0 1 0 0 0 1 0\" />\n <feBlend mode=\"normal\" in2=\"shape\" result=\"effect1_innerShadow_2126_3746\" />\n </filter>\n </defs>\n <g clip-path=\"url(#clip-middle-left)\" filter=\"url(#filter0_i_2126_3746)\">\n <path\n d=\"M245.538 25.2463 L9.36 -1.2459 Q-0.638672 -2.5 -0.638672 7.5 V266.75 Q-0.638672 276.75 9.16 275.25 L246.042 238.623 C251.404 237.794 255.361 233.161 255.361 227.735 V36.2037 C255.361 30.5841 251.126 25.8476 245.538 25.2463 Z\"\n fill=\"#00CFE6\"\n fill-opacity=\"0.07\"\n shape-rendering=\"geometricPrecision\"\n />\n </g>\n </svg>\n <svg\n class=\"profile-comparison__frame-svg profile-comparison__frame-svg--left\"\n viewBox=\"0 0 256 278\"\n preserveAspectRatio=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <defs>\n <radialGradient id=\"profile-left-fill\" cx=\"0\" cy=\"0\" r=\"1\" gradientTransform=\"matrix(65.1034 -81.3964 74.955 49.415 128 139)\" gradientUnits=\"userSpaceOnUse\">\n <stop stop-color=\"#0051E6\" stop-opacity=\"0\" />\n <stop offset=\"0.5\" stop-color=\"#0051E6\" stop-opacity=\"0.035\" />\n <stop offset=\"1\" stop-color=\"white\" />\n </radialGradient>\n <linearGradient id=\"profile-left-stroke\" x1=\"128\" y1=\"0\" x2=\"128\" y2=\"278\" gradientUnits=\"userSpaceOnUse\">\n <stop offset=\"0\" stop-color=\"#52FFEB\" />\n <stop offset=\"0.5\" stop-color=\"#41CFA1\" />\n <stop offset=\"1\" stop-color=\"#319990\" />\n </linearGradient>\n </defs>\n <!-- Fill Layer -->\n <path\n d=\"M246.177 26.4924 L10 1.07 Q0 0 0 10 V268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924 Z\"\n fill=\"url(#profile-left-fill)\"\n fill-opacity=\"0.07\"\n shape-rendering=\"geometricPrecision\"\n />\n <!-- Stroke Layer (Left outer border skipped) -->\n <path\n d=\"M246.177 26.4924 L10 1.07 Q0 0 0 10 L0 268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924\"\n fill=\"none\"\n stroke=\"url(#profile-left-stroke)\"\n stroke-width=\"2\"\n stroke-miterlimit=\"3.99393\"\n stroke-linejoin=\"round\"\n shape-rendering=\"geometricPrecision\"\n />\n <!-- Added explicit embedded SVG text anchoring tightly inside the graphical shape bounds instead of pure flexbox logic padding. -->\n <g\n style=\"cursor: pointer !important;\"\n (click)=\"onViewProfile('left'); $event.stopPropagation()\"\n (mousedown)=\"$event.stopPropagation()\"\n >\n <rect [attr.x]=\"viewProfileLeftX - 15\" y=\"240\" width=\"115\" height=\"40\" fill=\"transparent\" pointer-events=\"auto\" />\n <text\n [attr.x]=\"viewProfileLeftX\" y=\"256\"\n fill=\"rgba(255, 255, 255, 0.60)\"\n font-family=\"'Calistoga', serif\"\n font-size=\"9\"\n text-anchor=\"start\"\n [class.is-animating-bob]=\"leftProfileClicked\"\n style=\"text-shadow: 0 2px 4.2px #000;\"\n pointer-events=\"none\"\n >View Profile</text>\n </g>\n </svg>\n <div class=\"profile-comparison__content profile-comparison__content--left\">\n <div class=\"profile-comparison__list profile-comparison__list--left\">\n <!-- Semantic Items only -->\n <ng-container *ngFor=\"let interest of alignedPerson1Interests; let i = index\">\n <div class=\"profile-comparison__item profile-comparison__item--left\">\n <lib-marquee\n class=\"profile-comparison__text\"\n [title]=\"interest\"\n [onlyMarqueeOnHover]=\"false\"\n style=\"--marquee-font-size: 15px; --marquee-font-family: 'Gilroy', sans-serif; --marquee-font-color: white; --marquee-font-weight: 100; --marquee-animation-duration: 8s;\"\n ></lib-marquee>\n\n <!-- Dash beneath each interest -->\n <div class=\"profile-comparison__dash-container\" [ngSwitch]=\"getDashType(i)\">\n <!-- High Similarity: Short single dash, 0.3 opacity -->\n <svg *ngSwitchCase=\"'high'\" class=\"profile-comparison__dash profile-comparison__dash--high\" width=\"15\" height=\"4\" viewBox=\"0 0 15 4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 2H13\" stroke=\"white\" stroke-width=\"4\" stroke-opacity=\"0.3\" stroke-linecap=\"round\"/>\n </svg>\n <!-- Medium Similarity: Standard 1 dash -->\n <svg *ngSwitchCase=\"'medium'\" class=\"profile-comparison__dash profile-comparison__dash--medium\" width=\"30\" height=\"4\" viewBox=\"0 0 30 4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 2H28\" stroke=\"white\" stroke-width=\"4\" stroke-linecap=\"round\"/>\n </svg>\n <!-- Low Similarity: 4 small dashes -->\n <svg *ngSwitchCase=\"'low'\" class=\"profile-comparison__dash profile-comparison__dash--low\" width=\"30\" height=\"4\" viewBox=\"0 0 30 4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 2H28\" stroke=\"white\" stroke-width=\"4\" stroke-dasharray=\"2,5\" stroke-linecap=\"round\"/>\n </svg>\n </div>\n </div>\n </ng-container>\n </div>\n </div>\n </div>\n\n <div class=\"profile-comparison__frame profile-comparison__frame--right swiper-no-swiping\"\n [class.is-animating-nudge]=\"rightShapeAnimating\"\n (click)=\"onShapeClick('right')\"\n #rightFrame>\n <svg\n class=\"profile-comparison__frame-svg profile-comparison__frame-svg--glass\"\n viewBox=\"0 0 257 275\"\n preserveAspectRatio=\"none\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <g filter=\"url(#filter-glass-right)\">\n <path\n d=\"M256.24 267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 L256.24 267.5\"\n stroke=\"#AB4AFF\"\n stroke-opacity=\"0.3\"\n stroke-width=\"0.809707\"\n stroke-linejoin=\"round\"\n shape-rendering=\"geometricPrecision\"\n />\n </g>\n <defs>\n <filter id=\"filter-glass-right\" x=\"-40\" y=\"-40\" width=\"337\" height=\"355\" filterUnits=\"userSpaceOnUse\" color-interpolation-filters=\"sRGB\">\n <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"BackgroundImageFix\" result=\"effect1_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect1_dropShadow\" result=\"effect2_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect2_dropShadow\" result=\"effect3_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect3_dropShadow\" result=\"effect4_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect4_dropShadow\" result=\"effect5_dropShadow\"/>\n <feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"effect5_dropShadow\" result=\"shape\"/>\n </filter>\n </defs>\n </svg>\n <svg\n class=\"profile-comparison__frame-svg profile-comparison__frame-svg--middle\"\n viewBox=\"0 0 257 276\"\n preserveAspectRatio=\"none\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <defs>\n <clipPath id=\"clip-middle-right\">\n <path d=\"M9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 V267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95935 237.368 -9.15527e-05 232.733 -9.15527e-05 227.305 V36.1607 C-9.15527e-05 30.5399 4.23741 25.8028 9.8262 26.4454 Z\"/>\n </clipPath>\n <filter id=\"filter0_i_2126_3750\" x=\"0\" y=\"-2\" width=\"256.24\" height=\"279\" filterUnits=\"userSpaceOnUse\" color-interpolation-filters=\"sRGB\">\n <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\" />\n <feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14.2857 0\" result=\"hardAlpha\" />\n <feOffset />\n <feGaussianBlur stdDeviation=\"7.95\" />\n <feComposite in2=\"hardAlpha\" operator=\"arithmetic\" k2=\"-1\" k3=\"1\" />\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0.482353 0 0 0 0 0 0 0 0 0 0.901961 0 0 0 1 0\" />\n <feBlend mode=\"normal\" in2=\"shape\" result=\"effect1_innerShadow_2126_3750\" />\n </filter>\n </defs>\n <g clip-path=\"url(#clip-middle-right)\" filter=\"url(#filter0_i_2126_3750)\">\n <path\n d=\"M9.82611 25.203 L244.066 0.0640762 L246.24 -0.05 Q256.24 -1.2424 256.24 8.75 V266.25 Q256.24 276.258 246.24 274.75 L243.564 274.304 L9.32402 238.195 C3.95935 237.368 -9.15527e-05 232.733 -9.15527e-05 227.305 V36.1607 C-9.15527e-05 30.5399 4.23741 25.8028 9.82611 25.203 Z\"\n fill=\"#7B00E6\"\n fill-opacity=\"0.07\"\n shape-rendering=\"geometricPrecision\"\n />\n </g>\n </svg>\n <svg\n class=\"profile-comparison__frame-svg profile-comparison__frame-svg--right\"\n viewBox=\"0 0 257 278\"\n preserveAspectRatio=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <defs>\n <radialGradient id=\"profile-right-fill\" cx=\"0\" cy=\"0\" r=\"1\" gradientTransform=\"matrix(-65.1644 -81.25 -75.0252 49.3261 128.12 138.75)\" gradientUnits=\"userSpaceOnUse\">\n <stop stop-color=\"#0051E6\" stop-opacity=\"0\" />\n <stop offset=\"0.5\" stop-color=\"#0051E6\" stop-opacity=\"0.035\" />\n <stop offset=\"1\" stop-color=\"white\" />\n </radialGradient>\n <linearGradient id=\"profile-right-stroke\" x1=\"128.12\" y1=\"277.5\" x2=\"128.12\" y2=\"8.50002\" gradientUnits=\"userSpaceOnUse\">\n <stop offset=\"0\" stop-color=\"#662C99\" />\n <stop offset=\"0.5\" stop-color=\"#893BD3\" />\n <stop offset=\"1\" stop-color=\"#AB4AFF\" />\n </linearGradient>\n </defs>\n <!-- Fill Layer -->\n <path\n d=\"M9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 V267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 Z\"\n fill=\"url(#profile-right-fill)\"\n fill-opacity=\"0.07\"\n shape-rendering=\"geometricPrecision\"\n />\n <!-- Stroke Layer (Right outer border skipped) -->\n <path\n d=\"M256.24 267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 L256.24 267.5\"\n fill=\"none\"\n stroke=\"url(#profile-right-stroke)\"\n stroke-width=\"2\"\n stroke-miterlimit=\"3.99393\"\n stroke-linejoin=\"round\"\n shape-rendering=\"geometricPrecision\"\n />\n <g\n style=\"cursor: pointer !important;\"\n (click)=\"onViewProfile('right'); $event.stopPropagation()\"\n (mousedown)=\"$event.stopPropagation()\"\n >\n <rect [attr.x]=\"viewProfileRightX - 100\" y=\"240\" width=\"115\" height=\"40\" fill=\"transparent\" pointer-events=\"auto\" />\n <text\n [attr.x]=\"viewProfileRightX\" y=\"256\"\n fill=\"rgba(255, 255, 255, 0.60)\"\n font-family=\"'Calistoga', serif\"\n font-size=\"9\"\n text-anchor=\"end\"\n [class.is-animating-bob]=\"rightProfileClicked\"\n style=\"text-shadow: 0 2px 4.2px #000;\"\n pointer-events=\"auto\"\n >View Profile</text>\n </g>\n </svg>\n <div class=\"profile-comparison__content profile-comparison__content--right\">\n <div class=\"profile-comparison__list profile-comparison__list--right\">\n <!-- Semantic Items only -->\n <ng-container *ngFor=\"let interest of alignedPerson2Interests; let i = index\">\n <div class=\"profile-comparison__item profile-comparison__item--right\">\n <lib-marquee\n class=\"profile-comparison__text\"\n [title]=\"interest\"\n [onlyMarqueeOnHover]=\"false\"\n style=\"--marquee-font-size: 15px; --marquee-font-family: 'Gilroy', sans-serif; --marquee-font-color: white; --marquee-font-weight: 100; --marquee-animation-duration: 8s;\"\n ></lib-marquee>\n\n <!-- Dash beneath each interest -->\n <div class=\"profile-comparison__dash-container\" [ngSwitch]=\"getDashType(i)\">\n <!-- High Similarity: Short single dash, 0.5 opacity -->\n <svg *ngSwitchCase=\"'high'\" class=\"profile-comparison__dash profile-comparison__dash--high\" width=\"15\" height=\"4\" viewBox=\"0 0 15 4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 2H13\" stroke=\"white\" stroke-width=\"4\" stroke-opacity=\"0.5\" stroke-linecap=\"round\"/>\n </svg>\n <!-- Medium Similarity: Standard 1 dash -->\n <svg *ngSwitchCase=\"'medium'\" class=\"profile-comparison__dash profile-comparison__dash--medium\" width=\"30\" height=\"4\" viewBox=\"0 0 30 4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 2H28\" stroke=\"white\" stroke-width=\"4\" stroke-linecap=\"round\"/>\n </svg>\n <!-- Low Similarity: 4 small dashes -->\n <svg *ngSwitchCase=\"'low'\" class=\"profile-comparison__dash profile-comparison__dash--low\" width=\"30\" height=\"4\" viewBox=\"0 0 30 4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 2H28\" stroke=\"white\" stroke-width=\"4\" stroke-dasharray=\"2,5\" stroke-linecap=\"round\"/>\n </svg>\n </div>\n </div>\n </ng-container>\n </div>\n </div>\n </div>\n\n <!-- Center text section -->\n <div class=\"profile-comparison__center\" #shapeTextCenter>\n <div class=\"profile-comparison__center-inner\">\n <!-- Exact Items only -->\n <div *ngFor=\"let item of centerItem\" class=\"profile-comparison__center-item\">\n <lib-marquee\n class=\"profile-comparison__center-text\"\n [title]=\"item\"\n [onlyMarqueeOnHover]=\"false\"\n style=\"--marquee-font-size: 15px; --marquee-font-family: 'Gilroy', sans-serif; --marquee-font-color: white; --marquee-font-weight: 700; --marquee-animation-duration: 8s;\"\n ></lib-marquee>\n <!-- Invisible dash as requested -->\n <div class=\"profile-comparison__center-dash-placeholder\"></div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Loading indicator for alignment process -->\n <div *ngIf=\"isAligning\" class=\"loading-indicator\">\n <div class=\"loading-spinner\"></div>\n </div>\n</div>\n\n\n", styles: ["@keyframes textDropIn{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}@keyframes nudgeLeft{0%,to{transform:translate(-24%) scale(.86)}50%{transform:translate(-26%) scale(.86)}}@keyframes nudgeRight{0%,to{transform:translate(24%) scale(.86)}50%{transform:translate(26%) scale(.86)}}.profile-img.left{-webkit-mask-image:linear-gradient(to right,rgb(0,0,0) 0%,rgb(0,0,0) calc(100% - var(--overlap-size, 40px)),rgba(0,0,0,0) 100%);-webkit-mask-size:100% 100%;-webkit-mask-repeat:no-repeat;mask-image:linear-gradient(to right,#fff 0% calc(100% - var(--overlap-size, 40px)),#fff0);mask-size:100% 100%;mask-repeat:no-repeat;margin-right:calc(-.5 * var(--overlap-size, 40px))}.profile-img.right{-webkit-mask-image:linear-gradient(to left,rgb(0,0,0) 0%,rgb(0,0,0) calc(100% - var(--overlap-size, 40px)),rgba(0,0,0,0) 100%);-webkit-mask-size:100% 100%;-webkit-mask-repeat:no-repeat;mask-image:linear-gradient(to left,#fff 0% calc(100% - var(--overlap-size, 40px)),#fff0);mask-size:100% 100%;mask-repeat:no-repeat;margin-left:calc(-.5 * var(--overlap-size, 40px))}.profile-img.fade-all{-webkit-mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%),linear-gradient(to right,transparent,black 15%,black 85%,transparent);-webkit-mask-composite:source-in;mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%),linear-gradient(to right,transparent,black 15%,black 85%,transparent);mask-composite:intersect}.profile-img.fade-all.left{-webkit-mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%),linear-gradient(to right,transparent 0%,black 15%,black calc(100% - var(--overlap-size, 40px)),transparent 100%);mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%),linear-gradient(to right,transparent 0%,black 15%,black calc(100% - var(--overlap-size, 40px)),transparent 100%)}.profile-img.fade-all.right{-webkit-mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%),linear-gradient(to left,transparent 0%,black 15%,black calc(100% - var(--overlap-size, 40px)),transparent 100%);mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%),linear-gradient(to left,transparent 0%,black 15%,black calc(100% - var(--overlap-size, 40px)),transparent 100%)}.configure-backend-message{padding:1rem;text-align:center;color:#bdc3c7;background:#34495e;border-radius:8px}.profile-screen{width:100%;max-width:460px;margin:0 auto;overflow:visible;position:relative}.profile-flex{display:flex;width:100%;height:auto;align-items:center;overflow:hidden;background:linear-gradient(135deg,#1a1a1a,#0a0a0a);gap:0;margin:0;padding:0;position:relative;box-shadow:none;border:none;--overlap-size: 40px}.profile-flex.fade-all{-webkit-mask-image:linear-gradient(to right,transparent 0%,black 15%,black 85%,transparent 100%),linear-gradient(to bottom,transparent 0%,black 15%,black 85%,transparent 100%);-webkit-mask-composite:intersect;mask-image:linear-gradient(to right,transparent 0%,black 15%,black 85%,transparent 100%),linear-gradient(to bottom,transparent 0%,black 15%,black 85%,transparent 100%);mask-composite:intersect}.shape-bg{position:relative;pointer-events:auto;width:100%;height:100%}.shape-bg1,.shape-bg2{position:absolute;width:75%;max-width:none;height:350px;opacity:1;transition:transform .2s ease-out}.shape-bg1{z-index:1;left:0}.shape-bg2{right:0}.shape-bg.fade-all .shape-bg1{-webkit-mask-image:linear-gradient(to right,transparent 0%,black 20%,black 100%);mask-image:linear-gradient(to right,transparent 0%,black 20%,black 100%)}.shape-bg.fade-all .shape-bg2{-webkit-mask-image:linear-gradient(to left,transparent 0%,black 20%,black 100%);mask-image:linear-gradient(to left,transparent 0%,black 20%,black 100%)}.shape-bg svg{cursor:grab;overflow:visible!important}.shape-bg svg:active{cursor:grabbing}.profile-img{width:50%;height:350px;position:relative;flex-shrink:0;background:linear-gradient(135deg,#1a1a1a,#0a0a0a);margin:0;padding:0;left:0;top:0;border:none;box-sizing:border-box;overflow:hidden;box-shadow:none;outline:none}.profile-img img{transition:transform .3s ease;width:100%;height:100%;object-fit:cover;display:block;object-position:center 30%;opacity:.75;transform-origin:center center;border:none;outline:none;box-shadow:none;position:relative;top:0;left:0;filter:contrast(1.2) brightness(1.1) saturate(1.1);image-rendering:auto}.shape{position:absolute;top:32px;width:100%;left:0;z-index:2;pointer-events:none}.shape-bg{position:relative;pointer-events:auto}.shape-text{position:absolute;top:0;left:0;right:0;width:100%;height:100%;z-index:1;padding:0 1.25rem;box-sizing:border-box;pointer-events:none}.shape-text-left{margin-top:40px;text-align:left;position:absolute;top:0;bottom:0;right:calc(50% + 75px);z-index:2;pointer-events:auto;display:flex;flex-direction:column;align-items:flex-end}.shape-text-left .scroll-container{width:100%}.shape-text-center{position:absolute;left:0;right:0;margin:0 auto;width:fit-content;font-family:Gilroy,sans-serif;font-weight:700;font-size:.75rem;line-height:100%;letter-spacing:0%;color:#fff;text-align:center;display:flex;justify-content:center;align-items:center;flex-direction:column;text-shadow:0 2px 4.2px #000;z-index:10;transition:transform .3s ease;pointer-events:none;top:50%;transform:translateY(-50%);padding:0 .625rem;margin-top:3.8125rem}.shape-text-right{margin-top:40px;text-align:right;position:absolute;top:0;bottom:0;left:calc(50% + 70px);z-index:2;pointer-events:auto;display:flex;flex-direction:column;align-items:flex-start}.shape-text-right .scroll-container{width:100%}.shape-p{font-family:Gilroy,sans-serif;font-weight:400;font-size:.75rem;line-height:100%;letter-spacing:0%;color:#fff;padding-bottom:.125rem;text-align:right;text-shadow:0 2px 4.2px #000;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;direction:rtl;-webkit-user-select:none;user-select:none;margin:0}.shape-p-center{font-family:Gilroy,sans-serif;font-weight:700;font-size:.75rem;line-height:100%;letter-spacing:0%;color:#fff;margin:0;text-align:center;display:flex;justify-content:center;align-items:center;text-shadow:0 2px 4.2px #000}.shape-p-right{font-family:Gilroy,sans-serif;font-weight:400;font-size:.75rem;line-height:100%;letter-spacing:0%;color:#fff;padding-bottom:.125rem;text-align:left;text-shadow:0 2px 4.2px #000;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin:0;-webkit-user-select:none;user-select:none}.dash-item{color:#ffffff4d!important;font-size:.625rem!important;height:.625rem}.spacer-item{height:auto!important;color:#fffc!important;font-weight:700;font-size:.75rem!important;letter-spacing:2px}.shape-h2{font-family:Calistoga,serif;font-weight:400;font-size:.625rem;letter-spacing:0%;color:#fff;text-shadow:0 2px 4.2px #000;cursor:pointer;transition:all .3s ease;border:none;outline:none;white-space:nowrap;pointer-events:auto;z-index:100;position:relative;margin-left:.5rem}.shape-h2:hover{opacity:.8;transform:translateY(-2px)}.shape-h2-right{font-family:Calistoga,serif;font-weight:400;font-size:.625rem;letter-spacing:0%;color:#fff;text-shadow:0 2px 4.2px #000;cursor:pointer;transition:all .3s ease;border:none;outline:none;white-space:nowrap;pointer-events:auto;z-index:100;position:relative;margin-right:3.75rem}.shape-h2-right:hover{opacity:.8;transform:translateY(-2px)}.scroll-when-long{display:inline-block;max-width:4.5625rem;white-space:nowrap;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch}.scroll-when-long::-webkit-scrollbar{height:2px}.scroll-when-long::-webkit-scrollbar-track{background:#eee;border-radius:.625rem}.scroll-when-long::-webkit-scrollbar-thumb{background:#888;border-radius:.625rem}.scroll-when-long::-webkit-scrollbar-thumb:hover{background:#555}.shape-text p{cursor:default;-webkit-user-select:none;user-select:none}.shape-text p:active{cursor:default}:host{display:block}.profile-comparison{width:100%;padding:0;box-sizing:border-box;display:flex;justify-content:center;align-items:center;overflow:visible}.profile-comparison__inner{position:relative;width:100%;min-width:25.75rem;max-width:100%;min-height:500px;display:grid;grid-template-columns:1fr;grid-template-rows:auto;background:transparent;overflow:visible}.profile-comparison__bg-layer{grid-area:1/1;position:relative;width:100%;height:100%;min-height:inherit;pointer-events:none;display:grid;grid-template-columns:1fr;grid-template-rows:1fr}.profile-comparison__frame{grid-area:1/1;position:relative;pointer-events:none}.profile-comparison__frame.bg-frame{width:100%;height:100%;min-height:inherit;overflow:hidden;border-radius:10px;-webkit-mask-composite:source-in;mask-composite:intersect;will-change:auto}.profile-comparison__frame.bg-frame.profile-comparison__frame--left{-webkit-mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 278'%3E%3Cpath d='M246.177 26.4924 L10 1.07 Q0 0 0 10 V268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%);mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 278'%3E%3Cpath d='M246.177 26.4924 L10 1.07 Q0 0 0 10 V268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%);-webkit-mask-size:100% 100%,100% 100%;mask-size:100% 100%,100% 100%;-webkit-mask-composite:source-in;mask-composite:intersect}.fade-all-edges .profile-comparison__frame.bg-frame.profile-comparison__frame--left.edge-at-left-border{-webkit-mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 278'%3E%3Cpath d='M246.177 26.4924 L10 1.07 Q0 0 0 10 V268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%),linear-gradient(to right,transparent 0%,black 20px,black 100%);mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 278'%3E%3Cpath d='M246.177 26.4924 L10 1.07 Q0 0 0 10 V268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%),linear-gradient(to right,transparent 0%,black 20px,black 100%);-webkit-mask-size:100% 100%,100% 100%,100% 100%;mask-size:100% 100%,100% 100%,100% 100%;-webkit-mask-composite:source-in;mask-composite:intersect}.profile-comparison__frame.bg-frame.profile-comparison__frame--right{-webkit-mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 257 278'%3E%3Cpath d='M9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 V267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%);mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 257 278'%3E%3Cpath d='M9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 V267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%);-webkit-mask-size:100% 100%,100% 100%;mask-size:100% 100%,100% 100%;-webkit-mask-composite:source-in;mask-composite:intersect}.fade-all-edges .profile-comparison__frame.bg-frame.profile-comparison__frame--right.edge-at-right-border{-webkit-mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 257 278'%3E%3Cpath d='M9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 V267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%),linear-gradient(to left,transparent 0%,black 20px,black 100%);mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 257 278'%3E%3Cpath d='M9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 V267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%),linear-gradient(to left,transparent 0%,black 20px,black 100%);-webkit-mask-size:100% 100%,100% 100%,100% 100%;mask-size:100% 100%,100% 100%,100% 100%;-webkit-mask-composite:source-in;mask-composite:intersect}.profile-comparison__frame--left{transform:translate(-24%) scale(.86)}.profile-comparison__frame--left.is-animating-nudge{animation:nudgeLeft .5s ease-out}.profile-comparison__frame--left .profile-comparison__frame-svg{-webkit-mask-image:var(--edge-mask-left, linear-gradient(to right, transparent 0%, rgba(0, 0, 0, .4) 14px, black var(--edge-shading-width, 28px)));mask-image:var(--edge-mask-left, linear-gradient(to right, transparent 0%, rgba(0, 0, 0, .4) 14px, black var(--edge-shading-width, 28px)))}.profile-comparison__frame--left .profile-comparison__frame-svg--glass{transform:scale(1.01) translateY(-2px);transform-origin:center;-webkit-mask-image:none;mask-image:none}.profile-comparison__frame--right{transform:translate(24%) scale(.86)}.profile-comparison__frame--right.is-animating-nudge{animation:nudgeRight .5s ease-out}.profile-comparison__frame--right .profile-comparison__frame-svg{-webkit-mask-image:var(--edge-mask-right, linear-gradient(to left, transparent 0%, rgba(0, 0, 0, .4) 14px, black var(--edge-shading-width, 28px)));mask-image:var(--edge-mask-right, linear-gradient(to left, transparent, black var(--edge-shading-width, 28px)))}.profile-comparison__frame--right .profile-comparison__frame-svg--glass{transform:scale(1.01) translate(-1px,-2px);transform-origin:center;-webkit-mask-image:none;mask-image:none}.profile-comparison__frame--right .profile-comparison__frame-svg--bottom{width:113.2%}.profile-comparison__frame-svg{position:absolute;inset:0;width:100%;height:100%;pointer-events:auto;overflow:visible}.profile-comparison__frame-svg--glass{z-index:1}.profile-comparison__frame-svg--middle{z-index:2;mix-blend-mode:screen}.profile-comparison__frame-svg--left,.profile-comparison__frame-svg--right{z-index:3}.profile-comparison__frame-svg--bottom{width:113.7%;height:112.6%;inset:51% auto auto 50%;transform:translate(-50%,-51%)}.profile-comparison__bg{position:absolute;top:0;left:0;right:0;height:130%;background-size:cover;background-repeat:no-repeat;background-position:center 30%;pointer-events:auto;opacity:.8;transition:filter .6s ease,opacity .6s ease}.profile-comparison__bg--left{background-position:right center;margin-right:0%;-webkit-mask-image:linear-gradient(to right,black 50%,transparent 100%);mask-image:linear-gradient(to right,black 50%,transparent 100%);-webkit-mask-composite:source-in;mask-composite:intersect}.profile-comparison__bg--right{background-position:left center;margin-left:0%;-webkit-mask-image:linear-gradient(to left,black 50%,transparent 100%);mask-image:linear-gradient(to left,black 50%,transparent 100%);-webkit-mask-composite:source-in;mask-composite:intersect}@keyframes viewProfileBob{0%{transform:translateY(0)}50%{transform:translateY(-3px)}to{transform:translateY(0)}}.profile-comparison svg text{transition:opacity .3s ease}.profile-comparison svg text.is-animating-bob{animation:viewProfileBob .6s ease-in-out;fill:#fff!important}.profile-comparison__content{position:relative;z-index:20;padding:50px 12% 75px;min-height:100%;display:flex;flex-direction:column;justify-content:flex-start;color:#ecf0f1;font-family:Gilroy,serif;pointer-events:none}.profile-comparison__content--left{align-items:flex-end;text-align:right;padding-right:29%}.profile-comparison__content--right{align-items:flex-start;text-align:left;padding-left:29%}.profile-comparison__list{display:flex;flex-direction:column;gap:2px;width:100%;pointer-events:auto}.profile-comparison__list--left{align-items:flex-end;text-align:right;padding-right:29%}.profile-comparison__list--right{align-items:flex-start;text-align:left;padding-left:29%}.profile-comparison__item{display:flex;flex-direction:column;height:auto;width:11rem;flex-shrink:0;gap:6px;margin-bottom:6px;animation:textDropIn .4s ease-out backwards}.profile-comparison__item--left{align-items:flex-end;text-align:right}.profile-comparison__item--right{align-items:flex-start;text-align:left}.profile-comparison__text{font-family:Gilroy,sans-serif;font-size:15px;font-weight:100!important;color:#fff;width:100%;display:block;text-shadow:0 2px 4.2px #000}.profile-comparison__text ::ng-deep *{font-weight:400!important;text-shadow:0 2px 4.2px #000}.profile-comparison__dash-container{display:flex;width:100%;height:8px;align-items:center}.profile-comparison__item--left .profile-comparison__dash-container{justify-content:flex-end}.profile-comparison__item--right .profile-comparison__dash-container{justify-content:flex-start}.profile-comparison__dash{display:block}.profile-comparison__center-empty,.profile-comparison__center-dash-placeholder{height:8px;width:100%}.profile-comparison__center{grid-area:1/1;display:flex;align-items:flex-start;justify-content:center;z-index:15;pointer-events:none;transform:scale(.86)}.profile-comparison__center-inner{display:flex;flex-direction:column;align-items:center;text-align:center;justify-content:flex-start;padding:50px 0 75px;gap:6px;width:100%}.profile-comparison__center-item{height:auto;min-height:22px;display:flex;flex-direction:column;align-items:center;justify-content:flex-start;width:9rem;flex-shrink:0;gap:6px;margin-bottom:6px;animation:textDropIn .4s ease-out backwards}.profile-comparison__center-text{font-family:Gilroy,sans-serif;font-size:15px;font-weight:700;color:#fff;width:100%;display:block;text-shadow:0 2px 4.2px #000}.profile-comparison__center-text--empty{color:#ecf0f180}.loading-indicator{position:fixed;top:25%;left:50%;transform:translate(-50%,-50%);background:transparent;color:#fff;padding:20px 30px;border-radius:10px;text-align:center;z-index:1000;display:flex;flex-direction:column;align-items:center;gap:8px}.loading-spinner{width:40px;height:40px;border:4px solid rgba(255,255,255,.3);border-top:4px solid #667eea;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.loading-indicator p{margin:0;font-size:16px;font-weight:500}.api-key-modal-overlay{position:fixed;inset:0;background:#0f172a99;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;z-index:2000}.api-key-modal{width:min(640px,92vw);background:#0f172a;color:#e2e8f0;border:1px solid #334155;border-radius:12px;box-shadow:0 20px 50px #00000073;padding:16px 18px}.api-key-modal-header{display:flex;align-items:center;justify-content:space-between;gap:12px}.api-key-modal-header h2{margin:0;font-size:20px;font-weight:700;color:#e2e8f0}.warning-icon{color:#fbbf24}.api-key-modal .close-btn{background:#0b1220;border:1px solid #334155;color:#94a3b8;border-radius:8px;padding:6px;cursor:pointer;line-height:0}.api-key-modal-body{margin-top:12px;display:flex;flex-direction:column;gap:10px}.modal-message,.modal-instruction{margin:0;color:#cbd5e1}.modal-message strong{color:#e5e7eb}.api-key-input-group{display:flex;align-items:center;gap:10px}.api-key-input{flex:1 1 auto;background:#0a1020;color:#e2e8f0;border:1px solid #334155;border-radius:8px;padding:10px 12px}.api-key-input:focus{outline:none;border-color:#2563eb;box-shadow:0 0 0 3px #2563eb33}.api-key-load-btn{background:#2563eb;color:#fff;border:none;border-radius:8px;padding:10px 12px;cursor:pointer;font-weight:600}.api-key-load-btn:hover{background:#1d4ed8}.modal-hint{display:flex;align-items:center;gap:8px;color:#94a3b8}.modal-hint a{color:#93c5fd}\n"], dependencies: [{ kind: "directive", type: i2.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { kind: "directive", type: i2.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }, { kind: "directive", type: i2.NgSwitch, selector: "[ngSwitch]", inputs: ["ngSwitch"] }, { kind: "directive", type: i2.NgSwitchCase, selector: "[ngSwitchCase]", inputs: ["ngSwitchCase"] }, { kind: "component", type: i3.MarqueeComponent, selector: "lib-marquee", inputs: ["title", "searchText", "padding", "onlyMarqueeOnHover"], outputs: ["marquee"] }] });
2455
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileComparisonLibComponent, deps: [{ token: ProfileComparisonBackendService }, { token: i0.Renderer2 }, { token: FileConversionService }, { token: ImageCompressionService }, { token: i0.ChangeDetectorRef }, { token: PROFILE_COMPARISON_VERBOSE_LOGGING, optional: true }], target: i0.ɵɵFactoryTarget.Component });
2456
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.3.25", type: ProfileComparisonLibComponent, isStandalone: false, selector: "lib-profile-comparison", inputs: { config: "config", backendMode: "backendMode", backendUrl: "backendUrl", fadeAllEdges: "fadeAllEdges", person1Name: "person1Name", person2Name: "person2Name" }, outputs: { matrixDataChange: "matrixDataChange", rawLLMOutputChange: "rawLLMOutputChange", viewProfileClick: "viewProfileClick" }, viewQueries: [{ propertyName: "leftContainer", first: true, predicate: ["leftContainer"], descendants: true }, { propertyName: "rightContainer", first: true, predicate: ["rightContainer"], descendants: true }, { propertyName: "profileFlex", first: true, predicate: ["profileFlex"], descendants: true }, { propertyName: "profileImgLeft", first: true, predicate: ["profileImgLeft"], descendants: true }, { propertyName: "profileImgRight", first: true, predicate: ["profileImgRight"], descendants: true }, { propertyName: "shapeContainer", first: true, predicate: ["shapeContainer"], descendants: true }, { propertyName: "shapeBg", first: true, predicate: ["shapeBg"], descendants: true }, { propertyName: "shapeBg1", first: true, predicate: ["shapeBg1"], descendants: true }, { propertyName: "shapeBg2", first: true, predicate: ["shapeBg2"], descendants: true }, { propertyName: "shapeTextLeft", first: true, predicate: ["shapeTextLeft"], descendants: true }, { propertyName: "shapeTextRight", first: true, predicate: ["shapeTextRight"], descendants: true }, { propertyName: "shapeTextCenter", first: true, predicate: ["shapeTextCenter"], descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"configure-backend-message\" *ngIf=\"!backendConfigured\">\n Configure backend\n</div>\n\n<div class=\"profile-screen\" *ngIf=\"backendConfigured\">\n <div #profileFlex class=\"profile-flex\" [class.fade-all]=\"fadeAllEdges\">\n <svg class=\"image-noise-overlay\" style=\"position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; overflow: hidden; z-index: 0;\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 320\" fill=\"none\" preserveAspectRatio=\"none\">\n <g filter=\"url(#filter0_n_1_1753)\">\n <rect width=\"100%\" height=\"100%\" fill=\"url(#paint0_radial_1_1753)\" fill-opacity=\"0.33\"/>\n </g>\n <defs>\n <filter id=\"filter0_n_1_1753\" x=\"0%\" y=\"0%\" width=\"100%\" height=\"100%\" filterUnits=\"objectBoundingBox\" color-interpolation-filters=\"sRGB\">\n <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\"/>\n <feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\"/>\n <feTurbulence type=\"fractalNoise\" baseFrequency=\"0.10869565606117249 0.10869565606117249\" stitchTiles=\"stitch\" numOctaves=\"3\" result=\"noise\" seed=\"7339\"/>\n <feColorMatrix in=\"noise\" type=\"luminanceToAlpha\" result=\"alphaNoise\"/>\n <feComponentTransfer in=\"alphaNoise\" result=\"coloredNoise1\">\n <feFuncA type=\"discrete\" tableValues=\"0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 \"/>\n </feComponentTransfer>\n <feComposite operator=\"in\" in2=\"shape\" in=\"coloredNoise1\" result=\"noise1Clipped\"/>\n <feFlood flood-color=\"rgba(0, 0, 0, 0.25)\" result=\"color1Flood\"/>\n <feComposite operator=\"in\" in2=\"noise1Clipped\" in=\"color1Flood\" result=\"color1\"/>\n <feMerge result=\"effect1_noise_1_1753\">\n <feMergeNode in=\"shape\"/>\n <feMergeNode in=\"color1\"/>\n </feMerge>\n </filter>\n <radialGradient id=\"paint0_radial_1_1753\" cx=\"50%\" cy=\"50%\" r=\"50%\" fx=\"50%\" fy=\"50%\">\n <stop offset=\"0%\" stop-opacity=\"0\"/>\n <stop offset=\"100%\" stop-opacity=\"0.25\"/>\n </radialGradient>\n </defs>\n </svg>\n <div #profileImgLeft class=\"profile-img left\" [class.fade-all]=\"fadeAllEdges\">\n <img\n [src]=\"user1Image\"\n alt=\"User 1\"\n [style.transform]=\"user1Transform\"\n [style.top]=\"user1Top\"\n [style.height]=\"user1Height\"\n [style.object-position]=\"user1ObjectPosition\"\n (load)=\"onUserImageLoad(1, $event)\"\n />\n </div>\n <div #profileImgRight class=\"profile-img right\" [class.fade-all]=\"fadeAllEdges\">\n <img\n [src]=\"user2Image\"\n alt=\"User 2\"\n [style.transform]=\"user2Transform\"\n [style.top]=\"user2Top\"\n [style.height]=\"user2Height\"\n [style.object-position]=\"user2ObjectPosition\"\n (load)=\"onUserImageLoad(2, $event)\"\n />\n </div>\n <div class=\"image-shade-overlay\"></div>\n\n <!-- Frosted Glass Overlay Layer -->\n <div class=\"overlay-container\">\n <!-- Top Headers -->\n <div class=\"header-label left\">{{ person1Name }}</div>\n <div class=\"header-label center\">Shared</div>\n <div class=\"header-label right\">{{ person2Name }}</div>\n\n <!-- Chips Columns Grid -->\n <div class=\"chips-grid\">\n <!-- Person 1 Unique Interests -->\n <div class=\"chips-column left\">\n <ng-container *ngFor=\"let interest of getPerson1UniqueInterests(); let i = index\">\n <div class=\"frosted-chip person-a\" [style.margin-right.px]=\"getChipOffset(i, 'left')\" [style.margin-top.px]=\"(i > 0 ? 4 : 0)\">\n <span class=\"chip-label\">{{ interest }}</span>\n </div>\n </ng-container>\n </div>\n\n <!-- Shared Interests (Center Column) -->\n <div class=\"chips-column center\">\n <ng-container *ngFor=\"let interest of centerItem; let i = index\">\n <div class=\"frosted-chip shared\" *ngIf=\"interest && interest !== '-' && interest !== '----'\" [style.margin-left.px]=\"getChipOffset(i, 'center')\" [style.margin-top.px]=\"(i > 0 ? 4 : 0)\">\n <span class=\"chip-label\">{{ interest }}</span>\n </div>\n </ng-container>\n </div>\n\n <!-- Person 2 Unique Interests -->\n <div class=\"chips-column right\">\n <ng-container *ngFor=\"let interest of getPerson2UniqueInterests(); let i = index\">\n <div class=\"frosted-chip person-b\" [style.margin-left.px]=\"getChipOffset(i, 'right')\" [style.margin-top.px]=\"(i > 0 ? 4 : 0)\">\n <span class=\"chip-label\">{{ interest }}</span>\n </div>\n </ng-container>\n </div>\n </div>\n\n <!-- Bottom Actions -->\n <div class=\"view-profile left\" (click)=\"onViewProfile('left')\">View Profile</div>\n <div class=\"view-profile right\" (click)=\"onViewProfile('right')\">View Profile</div>\n </div>\n\n <!-- Loading indicator for alignment process -->\n <div *ngIf=\"isAligning\" class=\"loading-indicator\">\n <div class=\"loading-spinner\"></div>\n </div>\n</div>\n\n\n", styles: [":host{display:block;width:100%;max-width:100%;overflow-x:hidden!important;box-sizing:border-box!important}.profile-img.left{position:absolute;top:0;bottom:0;left:0;width:58%;height:100%;margin:0;z-index:1;-webkit-mask-image:linear-gradient(to right,black 0%,black 45%,transparent 92%);mask-image:linear-gradient(to right,black 0%,black 45%,transparent 92%)}.profile-img.right{position:absolute;inset:0 0 0 auto;width:58%;height:100%;margin:0;z-index:2;-webkit-mask-image:linear-gradient(to right,transparent 8%,black 55%,black 100%);mask-image:linear-gradient(to right,transparent 8%,black 55%,black 100%)}@media(min-width:768px){.profile-img.left{width:65%;-webkit-mask-image:linear-gradient(to right,black 0%,black 40%,transparent 90%);mask-image:linear-gradient(to right,black 0%,black 40%,transparent 90%)}.profile-img.right{width:65%;-webkit-mask-image:linear-gradient(to right,transparent 10%,black 60%,black 100%);mask-image:linear-gradient(to right,transparent 10%,black 60%,black 100%)}}.configure-backend-message{padding:1rem;text-align:center;color:#bdc3c7;background:#34495e;border-radius:8px}.backend-error-message{padding:.75rem 1rem;text-align:center;color:#fef3c7;background:#b4530940;border:1px solid rgba(245,158,11,.5);border-radius:8px;margin-bottom:.5rem}.profile-screen{width:100%;max-width:768px;margin:0 auto;overflow:hidden;position:relative;border-radius:12px}.profile-flex{display:flex;width:100%;height:440px;overflow:hidden;background:transparent;gap:0;margin:0;padding:0;position:relative;box-shadow:none;border:none;--overlap-size: 40px}.profile-flex.fade-all{-webkit-mask-image:linear-gradient(to right,transparent 0%,black 18%,black 82%,transparent 100%),linear-gradient(to bottom,transparent 0%,black 15%,black 85%,transparent 100%);-webkit-mask-composite:intersect;mask-image:linear-gradient(to right,transparent 0%,black 18%,black 82%,transparent 100%),linear-gradient(to bottom,transparent 0%,black 15%,black 85%,transparent 100%);mask-composite:intersect}.profile-flex .image-shade-overlay{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:3;background:radial-gradient(ellipse at center,transparent 40%,rgba(41,37,45,.25) 100%)}.profile-flex .image-noise-overlay{position:absolute;inset:0;width:100%;height:100%;max-width:100%;max-height:100%;pointer-events:none;overflow:hidden;z-index:0}.shape-bg1,.shape-bg2{position:absolute;max-width:250px;opacity:1;transition:transform .2s ease-out}.shape-bg1{z-index:1;left:0}.shape-bg2{right:0}.shape-bg.fade-all .shape-bg1{-webkit-mask-image:linear-gradient(to right,transparent 0%,black 20%,black 100%);mask-image:linear-gradient(to right,transparent 0%,black 20%,black 100%)}.shape-bg.fade-all .shape-bg2{-webkit-mask-image:linear-gradient(to left,transparent 0%,black 20%,black 100%);mask-image:linear-gradient(to left,transparent 0%,black 20%,black 100%)}.shape-bg svg{cursor:grab}.shape-bg svg:active{cursor:grabbing}.profile-img{height:100%;position:relative;flex-shrink:0;background:transparent;margin:0;padding:0;left:0;top:0;border:none;box-sizing:border-box;overflow:hidden;box-shadow:none;outline:none}.profile-img img{transition:transform .3s ease;width:100%;height:100%;object-fit:cover;display:block;object-position:center center;opacity:1;transform-origin:center center;border:none;outline:none;box-shadow:none;position:relative;top:0;left:0;filter:contrast(1.2) brightness(1.1) saturate(1.1);image-rendering:auto}.overlay-container{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:10;-webkit-user-select:none;user-select:none;overflow:hidden;background:#29252da6;backdrop-filter:blur(.5px);-webkit-backdrop-filter:blur(.5px)}.header-label{position:absolute;top:20px;font-family:Gilroy,sans-serif;text-shadow:0 1px 8px rgba(0,0,0,.8);pointer-events:none}.header-label.left{left:5%;transform:none;font-size:14px;font-weight:400;color:#fffffff2}.header-label.center{left:50%;transform:translate(-50%);font-size:12px;font-weight:400;color:#fffc;letter-spacing:0}.header-label.right{right:5%;left:auto;transform:none;font-size:14px;font-weight:400;color:#fffffff2}.chips-grid{position:absolute;inset:45px 0;display:grid;grid-template-columns:repeat(3,1fr);padding:0;pointer-events:none;box-sizing:border-box}.chips-column{display:flex;flex-direction:column;gap:8px;max-height:100%;overflow-y:auto;padding:4px 10px;box-sizing:border-box;width:100%;scrollbar-width:none}.chips-column::-webkit-scrollbar{display:none}.chips-column.left{align-items:flex-end}.chips-column.center{align-items:center}.chips-column.right{align-items:flex-start}.frosted-chip{position:relative;cursor:pointer;pointer-events:auto;transition:transform .2s ease,box-shadow .2s ease,background .2s ease;flex-shrink:0;padding:6px 18px;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;border:none;background:#ffffff30;box-shadow:inset 0 1.5px 2px #ffffff59;backdrop-filter:blur(.5px);-webkit-backdrop-filter:blur(.5px)}.frosted-chip:hover{transform:scale(1.04);background:#ffffff29}.frosted-chip .chip-label{position:relative;color:#a8a8a8;text-align:center;font-family:Gilroy,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:140%;letter-spacing:-.01em;text-shadow:none;white-space:nowrap;pointer-events:none;z-index:1}.view-profile{position:absolute;bottom:40px!important;color:#ffffff80;text-shadow:0 2px 4.2px #000;font-family:Calistoga,serif;font-size:18px;font-style:normal;font-weight:400;line-height:normal;cursor:pointer;pointer-events:auto;transition:opacity .2s ease,transform .2s ease}.view-profile:hover{opacity:.85}.view-profile.left{left:5%;transform:none}.view-profile.left:hover{transform:translateY(-2px)}.view-profile.right{right:5%;left:auto;transform:none}.view-profile.right:hover{transform:translateY(-2px)}#drag-preview{position:absolute;pointer-events:none;display:none;background:#000000b3;color:#fff;padding:5px 10px;border-radius:5px;z-index:9999;max-width:200px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.shape-text-container{width:70px;position:relative;overflow:hidden}.draggable{width:70px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:grab;-webkit-user-select:none;user-select:none;position:relative;display:inline-block}.dragging{width:auto;text-overflow:clip;cursor:grabbing;position:absolute;z-index:1000}.p-wrapper{width:70px;overflow-x:hidden}.shape-text-center{display:flex;justify-content:space-between;flex-direction:column;gap:10px;height:100%;margin-top:55px}.loading-indicator{position:absolute;inset:0;width:100%;height:100%;background:#00000040;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);color:#fff;z-index:100;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;pointer-events:auto}.loading-spinner{width:44px;height:44px;border:3.5px solid rgba(255,255,255,.25);border-top:3.5px solid #ffffff;border-radius:50%;animation:spin .8s linear infinite;box-shadow:0 0 15px #fff3}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.loading-indicator p{margin:0;font-size:14px;font-weight:500;color:#ffffffd9;letter-spacing:.02em}.api-key-modal-overlay{position:fixed;inset:0;background:#0f172a99;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;z-index:2000}.api-key-modal{width:min(640px,92vw);background:#0f172a;color:#e2e8f0;border:1px solid #334155;border-radius:12px;box-shadow:0 20px 50px #00000073;padding:16px 18px}.api-key-modal-header{display:flex;align-items:center;justify-content:space-between;gap:12px}.api-key-modal-header h2{margin:0;font-size:20px;font-weight:700;color:#e2e8f0}.warning-icon{color:#fbbf24}.api-key-modal .close-btn{background:#0b1220;border:1px solid #334155;color:#94a3b8;border-radius:8px;padding:6px;cursor:pointer;line-height:0}.api-key-modal-body{margin-top:12px;display:flex;flex-direction:column;gap:10px}.modal-message,.modal-instruction{margin:0;color:#cbd5e1}.modal-message strong{color:#e5e7eb}.api-key-input-group{display:flex;align-items:center;gap:10px}.api-key-input{flex:1 1 auto;background:#0a1020;color:#e2e8f0;border:1px solid #334155;border-radius:8px;padding:10px 12px}.api-key-input:focus{outline:none;border-color:#2563eb;box-shadow:0 0 0 3px #2563eb33}.api-key-load-btn{background:#2563eb;color:#fff;border:none;border-radius:8px;padding:10px 12px;cursor:pointer;font-weight:600}.api-key-load-btn:hover{background:#1d4ed8}.modal-hint{display:flex;align-items:center;gap:8px;color:#94a3b8}.modal-hint a{color:#93c5fd}@media(max-width:1023px){.profile-screen{max-width:640px}.profile-flex{height:400px}.profile-img{height:550px}}@media(max-width:767px){.profile-screen,.profile-flex,.overlay-container{width:100%;max-width:100%;overflow-x:hidden;box-sizing:border-box}.profile-screen{border-radius:0}.profile-flex{height:360px}.profile-img{height:510px}.chips-grid{width:100%;max-width:100%;padding:0 4px;top:50px;bottom:50px;box-sizing:border-box}.chips-column{padding:2px}.chips-column.left{align-items:flex-end}.chips-column.center{align-items:center}.chips-column.right{align-items:flex-start}.frosted-chip{padding:4px 10px;max-width:100%;box-sizing:border-box}.frosted-chip .chip-label{font-size:11px;max-width:100%;overflow:hidden;text-overflow:ellipsis}.view-profile{bottom:14px}}@media(max-width:480px){.profile-flex{height:340px}.profile-img{height:480px}.frosted-chip{padding:3px 8px}.frosted-chip .chip-label{font-size:10px}.header-label{top:14px}.header-label.left{font-size:12px}.header-label.center{font-size:10px}.header-label.right{font-size:12px}}\n"], dependencies: [{ kind: "directive", type: i2_1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { kind: "directive", type: i2_1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
2546
2457
  }
2547
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProfileComparisonLibComponent, decorators: [{
2458
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileComparisonLibComponent, decorators: [{
2548
2459
  type: Component,
2549
- args: [{ selector: 'lib-profile-comparison', standalone: false, template: "<div class=\"configure-backend-message\" *ngIf=\"!backendConfigured\">\n Configure backend\n</div>\n\n<div class=\"profile-screen profile-comparison\" *ngIf=\"backendConfigured\"\n [class.fade-all-edges]=\"config.edgeShading !== false\"\n [style.--edge-shading-color]=\"config.shadingColor || 'rgba(44, 40, 47, 0.0)'\"\n [style.--edge-shading-display]=\"config.edgeShading !== false ? 'block' : 'none'\">\n\n <div class=\"profile-comparison__inner\" #shapeContainer>\n <!-- Backgrounds layer: structurally below everything else to fix stacking -->\n <div class=\"profile-comparison__bg-layer\">\n <div class=\"profile-comparison__frame profile-comparison__frame--left bg-frame\"\n [class.is-animating-nudge]=\"leftShapeAnimating\"\n #leftBgFrame>\n <div\n class=\"profile-comparison__bg profile-comparison__bg--left\"\n [ngStyle]=\"{ 'background-image': 'url(' + user1Image + ')', 'transform': user1Transform, 'background-position': user1ObjectPosition }\"\n ></div>\n </div>\n <div class=\"profile-comparison__frame profile-comparison__frame--right bg-frame\"\n [class.is-animating-nudge]=\"rightShapeAnimating\"\n #rightBgFrame>\n <div\n class=\"profile-comparison__bg profile-comparison__bg--right\"\n [ngStyle]=\"{ 'background-image': 'url(' + user2Image + ')', 'transform': user2Transform, 'background-position': user2ObjectPosition }\"\n ></div>\n </div>\n </div>\n\n <!-- Active interactive shapes and content layer -->\n <div class=\"profile-comparison__frame profile-comparison__frame--left swiper-no-swiping\"\n [class.is-animating-nudge]=\"leftShapeAnimating\"\n (click)=\"onShapeClick('left')\"\n #leftFrame>\n <svg\n class=\"profile-comparison__frame-svg profile-comparison__frame-svg--glass\"\n viewBox=\"0 0 256 275\"\n preserveAspectRatio=\"none\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <g filter=\"url(#filter-glass-left)\">\n <path\n d=\"M246.177 26.4924 L10 1.07 Q0 0 0 10 L0 268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924\"\n stroke=\"#61C2AB\"\n stroke-opacity=\"0.3\"\n stroke-width=\"0.809707\"\n stroke-linejoin=\"round\"\n shape-rendering=\"geometricPrecision\"\n />\n </g>\n <defs>\n <filter id=\"filter-glass-left\" x=\"-40\" y=\"-40\" width=\"336\" height=\"355\" filterUnits=\"userSpaceOnUse\" color-interpolation-filters=\"sRGB\">\n <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"BackgroundImageFix\" result=\"effect1_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect1_dropShadow\" result=\"effect2_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect2_dropShadow\" result=\"effect3_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect3_dropShadow\" result=\"effect4_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect4_dropShadow\" result=\"effect5_dropShadow\"/>\n <feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"effect5_dropShadow\" result=\"shape\"/>\n </filter>\n </defs>\n </svg>\n <svg\n class=\"profile-comparison__frame-svg profile-comparison__frame-svg--middle\"\n viewBox=\"0 0 256 276\"\n preserveAspectRatio=\"none\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <defs>\n <clipPath id=\"clip-middle-left\">\n <path d=\"M246.177 26.4924 L10 1.07 Q0 0 0 10 V268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924 Z\"/>\n </clipPath>\n <filter id=\"filter0_i_2126_3746\" x=\"-0.638672\" y=\"-2\" width=\"256\" height=\"279\" filterUnits=\"userSpaceOnUse\" color-interpolation-filters=\"sRGB\">\n <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\" />\n <feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14.2857 0\" result=\"hardAlpha\" />\n <feOffset />\n <feGaussianBlur stdDeviation=\"7.95\" />\n <feComposite in2=\"hardAlpha\" operator=\"arithmetic\" k2=\"-1\" k3=\"1\" />\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0.516667 0 0 0 0 1 0 0 0 1 0\" />\n <feBlend mode=\"normal\" in2=\"shape\" result=\"effect1_innerShadow_2126_3746\" />\n </filter>\n </defs>\n <g clip-path=\"url(#clip-middle-left)\" filter=\"url(#filter0_i_2126_3746)\">\n <path\n d=\"M245.538 25.2463 L9.36 -1.2459 Q-0.638672 -2.5 -0.638672 7.5 V266.75 Q-0.638672 276.75 9.16 275.25 L246.042 238.623 C251.404 237.794 255.361 233.161 255.361 227.735 V36.2037 C255.361 30.5841 251.126 25.8476 245.538 25.2463 Z\"\n fill=\"#00CFE6\"\n fill-opacity=\"0.07\"\n shape-rendering=\"geometricPrecision\"\n />\n </g>\n </svg>\n <svg\n class=\"profile-comparison__frame-svg profile-comparison__frame-svg--left\"\n viewBox=\"0 0 256 278\"\n preserveAspectRatio=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <defs>\n <radialGradient id=\"profile-left-fill\" cx=\"0\" cy=\"0\" r=\"1\" gradientTransform=\"matrix(65.1034 -81.3964 74.955 49.415 128 139)\" gradientUnits=\"userSpaceOnUse\">\n <stop stop-color=\"#0051E6\" stop-opacity=\"0\" />\n <stop offset=\"0.5\" stop-color=\"#0051E6\" stop-opacity=\"0.035\" />\n <stop offset=\"1\" stop-color=\"white\" />\n </radialGradient>\n <linearGradient id=\"profile-left-stroke\" x1=\"128\" y1=\"0\" x2=\"128\" y2=\"278\" gradientUnits=\"userSpaceOnUse\">\n <stop offset=\"0\" stop-color=\"#52FFEB\" />\n <stop offset=\"0.5\" stop-color=\"#41CFA1\" />\n <stop offset=\"1\" stop-color=\"#319990\" />\n </linearGradient>\n </defs>\n <!-- Fill Layer -->\n <path\n d=\"M246.177 26.4924 L10 1.07 Q0 0 0 10 V268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924 Z\"\n fill=\"url(#profile-left-fill)\"\n fill-opacity=\"0.07\"\n shape-rendering=\"geometricPrecision\"\n />\n <!-- Stroke Layer (Left outer border skipped) -->\n <path\n d=\"M246.177 26.4924 L10 1.07 Q0 0 0 10 L0 268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924\"\n fill=\"none\"\n stroke=\"url(#profile-left-stroke)\"\n stroke-width=\"2\"\n stroke-miterlimit=\"3.99393\"\n stroke-linejoin=\"round\"\n shape-rendering=\"geometricPrecision\"\n />\n <!-- Added explicit embedded SVG text anchoring tightly inside the graphical shape bounds instead of pure flexbox logic padding. -->\n <g\n style=\"cursor: pointer !important;\"\n (click)=\"onViewProfile('left'); $event.stopPropagation()\"\n (mousedown)=\"$event.stopPropagation()\"\n >\n <rect [attr.x]=\"viewProfileLeftX - 15\" y=\"240\" width=\"115\" height=\"40\" fill=\"transparent\" pointer-events=\"auto\" />\n <text\n [attr.x]=\"viewProfileLeftX\" y=\"256\"\n fill=\"rgba(255, 255, 255, 0.60)\"\n font-family=\"'Calistoga', serif\"\n font-size=\"9\"\n text-anchor=\"start\"\n [class.is-animating-bob]=\"leftProfileClicked\"\n style=\"text-shadow: 0 2px 4.2px #000;\"\n pointer-events=\"none\"\n >View Profile</text>\n </g>\n </svg>\n <div class=\"profile-comparison__content profile-comparison__content--left\">\n <div class=\"profile-comparison__list profile-comparison__list--left\">\n <!-- Semantic Items only -->\n <ng-container *ngFor=\"let interest of alignedPerson1Interests; let i = index\">\n <div class=\"profile-comparison__item profile-comparison__item--left\">\n <lib-marquee\n class=\"profile-comparison__text\"\n [title]=\"interest\"\n [onlyMarqueeOnHover]=\"false\"\n style=\"--marquee-font-size: 15px; --marquee-font-family: 'Gilroy', sans-serif; --marquee-font-color: white; --marquee-font-weight: 100; --marquee-animation-duration: 8s;\"\n ></lib-marquee>\n\n <!-- Dash beneath each interest -->\n <div class=\"profile-comparison__dash-container\" [ngSwitch]=\"getDashType(i)\">\n <!-- High Similarity: Short single dash, 0.3 opacity -->\n <svg *ngSwitchCase=\"'high'\" class=\"profile-comparison__dash profile-comparison__dash--high\" width=\"15\" height=\"4\" viewBox=\"0 0 15 4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 2H13\" stroke=\"white\" stroke-width=\"4\" stroke-opacity=\"0.3\" stroke-linecap=\"round\"/>\n </svg>\n <!-- Medium Similarity: Standard 1 dash -->\n <svg *ngSwitchCase=\"'medium'\" class=\"profile-comparison__dash profile-comparison__dash--medium\" width=\"30\" height=\"4\" viewBox=\"0 0 30 4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 2H28\" stroke=\"white\" stroke-width=\"4\" stroke-linecap=\"round\"/>\n </svg>\n <!-- Low Similarity: 4 small dashes -->\n <svg *ngSwitchCase=\"'low'\" class=\"profile-comparison__dash profile-comparison__dash--low\" width=\"30\" height=\"4\" viewBox=\"0 0 30 4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 2H28\" stroke=\"white\" stroke-width=\"4\" stroke-dasharray=\"2,5\" stroke-linecap=\"round\"/>\n </svg>\n </div>\n </div>\n </ng-container>\n </div>\n </div>\n </div>\n\n <div class=\"profile-comparison__frame profile-comparison__frame--right swiper-no-swiping\"\n [class.is-animating-nudge]=\"rightShapeAnimating\"\n (click)=\"onShapeClick('right')\"\n #rightFrame>\n <svg\n class=\"profile-comparison__frame-svg profile-comparison__frame-svg--glass\"\n viewBox=\"0 0 257 275\"\n preserveAspectRatio=\"none\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <g filter=\"url(#filter-glass-right)\">\n <path\n d=\"M256.24 267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 L256.24 267.5\"\n stroke=\"#AB4AFF\"\n stroke-opacity=\"0.3\"\n stroke-width=\"0.809707\"\n stroke-linejoin=\"round\"\n shape-rendering=\"geometricPrecision\"\n />\n </g>\n <defs>\n <filter id=\"filter-glass-right\" x=\"-40\" y=\"-40\" width=\"337\" height=\"355\" filterUnits=\"userSpaceOnUse\" color-interpolation-filters=\"sRGB\">\n <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"BackgroundImageFix\" result=\"effect1_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect1_dropShadow\" result=\"effect2_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect2_dropShadow\" result=\"effect3_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect3_dropShadow\" result=\"effect4_dropShadow\"/>\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3.3333 0\" result=\"hardAlpha\"/>\n <feOffset/>\n <feGaussianBlur stdDeviation=\"6.3562\"/>\n <feComposite in2=\"hardAlpha\" operator=\"out\"/>\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0\"/>\n <feBlend mode=\"normal\" in2=\"effect4_dropShadow\" result=\"effect5_dropShadow\"/>\n <feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"effect5_dropShadow\" result=\"shape\"/>\n </filter>\n </defs>\n </svg>\n <svg\n class=\"profile-comparison__frame-svg profile-comparison__frame-svg--middle\"\n viewBox=\"0 0 257 276\"\n preserveAspectRatio=\"none\"\n fill=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <defs>\n <clipPath id=\"clip-middle-right\">\n <path d=\"M9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 V267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95935 237.368 -9.15527e-05 232.733 -9.15527e-05 227.305 V36.1607 C-9.15527e-05 30.5399 4.23741 25.8028 9.8262 26.4454 Z\"/>\n </clipPath>\n <filter id=\"filter0_i_2126_3750\" x=\"0\" y=\"-2\" width=\"256.24\" height=\"279\" filterUnits=\"userSpaceOnUse\" color-interpolation-filters=\"sRGB\">\n <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\" />\n <feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\" />\n <feColorMatrix in=\"SourceAlpha\" type=\"matrix\" values=\"0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14.2857 0\" result=\"hardAlpha\" />\n <feOffset />\n <feGaussianBlur stdDeviation=\"7.95\" />\n <feComposite in2=\"hardAlpha\" operator=\"arithmetic\" k2=\"-1\" k3=\"1\" />\n <feColorMatrix type=\"matrix\" values=\"0 0 0 0 0.482353 0 0 0 0 0 0 0 0 0 0.901961 0 0 0 1 0\" />\n <feBlend mode=\"normal\" in2=\"shape\" result=\"effect1_innerShadow_2126_3750\" />\n </filter>\n </defs>\n <g clip-path=\"url(#clip-middle-right)\" filter=\"url(#filter0_i_2126_3750)\">\n <path\n d=\"M9.82611 25.203 L244.066 0.0640762 L246.24 -0.05 Q256.24 -1.2424 256.24 8.75 V266.25 Q256.24 276.258 246.24 274.75 L243.564 274.304 L9.32402 238.195 C3.95935 237.368 -9.15527e-05 232.733 -9.15527e-05 227.305 V36.1607 C-9.15527e-05 30.5399 4.23741 25.8028 9.82611 25.203 Z\"\n fill=\"#7B00E6\"\n fill-opacity=\"0.07\"\n shape-rendering=\"geometricPrecision\"\n />\n </g>\n </svg>\n <svg\n class=\"profile-comparison__frame-svg profile-comparison__frame-svg--right\"\n viewBox=\"0 0 257 278\"\n preserveAspectRatio=\"none\"\n xmlns=\"http://www.w3.org/2000/svg\"\n aria-hidden=\"true\"\n >\n <defs>\n <radialGradient id=\"profile-right-fill\" cx=\"0\" cy=\"0\" r=\"1\" gradientTransform=\"matrix(-65.1644 -81.25 -75.0252 49.3261 128.12 138.75)\" gradientUnits=\"userSpaceOnUse\">\n <stop stop-color=\"#0051E6\" stop-opacity=\"0\" />\n <stop offset=\"0.5\" stop-color=\"#0051E6\" stop-opacity=\"0.035\" />\n <stop offset=\"1\" stop-color=\"white\" />\n </radialGradient>\n <linearGradient id=\"profile-right-stroke\" x1=\"128.12\" y1=\"277.5\" x2=\"128.12\" y2=\"8.50002\" gradientUnits=\"userSpaceOnUse\">\n <stop offset=\"0\" stop-color=\"#662C99\" />\n <stop offset=\"0.5\" stop-color=\"#893BD3\" />\n <stop offset=\"1\" stop-color=\"#AB4AFF\" />\n </linearGradient>\n </defs>\n <!-- Fill Layer -->\n <path\n d=\"M9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 V267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 Z\"\n fill=\"url(#profile-right-fill)\"\n fill-opacity=\"0.07\"\n shape-rendering=\"geometricPrecision\"\n />\n <!-- Stroke Layer (Right outer border skipped) -->\n <path\n d=\"M256.24 267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 L256.24 267.5\"\n fill=\"none\"\n stroke=\"url(#profile-right-stroke)\"\n stroke-width=\"2\"\n stroke-miterlimit=\"3.99393\"\n stroke-linejoin=\"round\"\n shape-rendering=\"geometricPrecision\"\n />\n <g\n style=\"cursor: pointer !important;\"\n (click)=\"onViewProfile('right'); $event.stopPropagation()\"\n (mousedown)=\"$event.stopPropagation()\"\n >\n <rect [attr.x]=\"viewProfileRightX - 100\" y=\"240\" width=\"115\" height=\"40\" fill=\"transparent\" pointer-events=\"auto\" />\n <text\n [attr.x]=\"viewProfileRightX\" y=\"256\"\n fill=\"rgba(255, 255, 255, 0.60)\"\n font-family=\"'Calistoga', serif\"\n font-size=\"9\"\n text-anchor=\"end\"\n [class.is-animating-bob]=\"rightProfileClicked\"\n style=\"text-shadow: 0 2px 4.2px #000;\"\n pointer-events=\"auto\"\n >View Profile</text>\n </g>\n </svg>\n <div class=\"profile-comparison__content profile-comparison__content--right\">\n <div class=\"profile-comparison__list profile-comparison__list--right\">\n <!-- Semantic Items only -->\n <ng-container *ngFor=\"let interest of alignedPerson2Interests; let i = index\">\n <div class=\"profile-comparison__item profile-comparison__item--right\">\n <lib-marquee\n class=\"profile-comparison__text\"\n [title]=\"interest\"\n [onlyMarqueeOnHover]=\"false\"\n style=\"--marquee-font-size: 15px; --marquee-font-family: 'Gilroy', sans-serif; --marquee-font-color: white; --marquee-font-weight: 100; --marquee-animation-duration: 8s;\"\n ></lib-marquee>\n\n <!-- Dash beneath each interest -->\n <div class=\"profile-comparison__dash-container\" [ngSwitch]=\"getDashType(i)\">\n <!-- High Similarity: Short single dash, 0.5 opacity -->\n <svg *ngSwitchCase=\"'high'\" class=\"profile-comparison__dash profile-comparison__dash--high\" width=\"15\" height=\"4\" viewBox=\"0 0 15 4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 2H13\" stroke=\"white\" stroke-width=\"4\" stroke-opacity=\"0.5\" stroke-linecap=\"round\"/>\n </svg>\n <!-- Medium Similarity: Standard 1 dash -->\n <svg *ngSwitchCase=\"'medium'\" class=\"profile-comparison__dash profile-comparison__dash--medium\" width=\"30\" height=\"4\" viewBox=\"0 0 30 4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 2H28\" stroke=\"white\" stroke-width=\"4\" stroke-linecap=\"round\"/>\n </svg>\n <!-- Low Similarity: 4 small dashes -->\n <svg *ngSwitchCase=\"'low'\" class=\"profile-comparison__dash profile-comparison__dash--low\" width=\"30\" height=\"4\" viewBox=\"0 0 30 4\" fill=\"none\" xmlns=\"http://www.w3.org/2000/svg\">\n <path d=\"M2 2H28\" stroke=\"white\" stroke-width=\"4\" stroke-dasharray=\"2,5\" stroke-linecap=\"round\"/>\n </svg>\n </div>\n </div>\n </ng-container>\n </div>\n </div>\n </div>\n\n <!-- Center text section -->\n <div class=\"profile-comparison__center\" #shapeTextCenter>\n <div class=\"profile-comparison__center-inner\">\n <!-- Exact Items only -->\n <div *ngFor=\"let item of centerItem\" class=\"profile-comparison__center-item\">\n <lib-marquee\n class=\"profile-comparison__center-text\"\n [title]=\"item\"\n [onlyMarqueeOnHover]=\"false\"\n style=\"--marquee-font-size: 15px; --marquee-font-family: 'Gilroy', sans-serif; --marquee-font-color: white; --marquee-font-weight: 700; --marquee-animation-duration: 8s;\"\n ></lib-marquee>\n <!-- Invisible dash as requested -->\n <div class=\"profile-comparison__center-dash-placeholder\"></div>\n </div>\n </div>\n </div>\n </div>\n\n <!-- Loading indicator for alignment process -->\n <div *ngIf=\"isAligning\" class=\"loading-indicator\">\n <div class=\"loading-spinner\"></div>\n </div>\n</div>\n\n\n", styles: ["@keyframes textDropIn{0%{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:translateY(0)}}@keyframes nudgeLeft{0%,to{transform:translate(-24%) scale(.86)}50%{transform:translate(-26%) scale(.86)}}@keyframes nudgeRight{0%,to{transform:translate(24%) scale(.86)}50%{transform:translate(26%) scale(.86)}}.profile-img.left{-webkit-mask-image:linear-gradient(to right,rgb(0,0,0) 0%,rgb(0,0,0) calc(100% - var(--overlap-size, 40px)),rgba(0,0,0,0) 100%);-webkit-mask-size:100% 100%;-webkit-mask-repeat:no-repeat;mask-image:linear-gradient(to right,#fff 0% calc(100% - var(--overlap-size, 40px)),#fff0);mask-size:100% 100%;mask-repeat:no-repeat;margin-right:calc(-.5 * var(--overlap-size, 40px))}.profile-img.right{-webkit-mask-image:linear-gradient(to left,rgb(0,0,0) 0%,rgb(0,0,0) calc(100% - var(--overlap-size, 40px)),rgba(0,0,0,0) 100%);-webkit-mask-size:100% 100%;-webkit-mask-repeat:no-repeat;mask-image:linear-gradient(to left,#fff 0% calc(100% - var(--overlap-size, 40px)),#fff0);mask-size:100% 100%;mask-repeat:no-repeat;margin-left:calc(-.5 * var(--overlap-size, 40px))}.profile-img.fade-all{-webkit-mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%),linear-gradient(to right,transparent,black 15%,black 85%,transparent);-webkit-mask-composite:source-in;mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%),linear-gradient(to right,transparent,black 15%,black 85%,transparent);mask-composite:intersect}.profile-img.fade-all.left{-webkit-mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%),linear-gradient(to right,transparent 0%,black 15%,black calc(100% - var(--overlap-size, 40px)),transparent 100%);mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%),linear-gradient(to right,transparent 0%,black 15%,black calc(100% - var(--overlap-size, 40px)),transparent 100%)}.profile-img.fade-all.right{-webkit-mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%),linear-gradient(to left,transparent 0%,black 15%,black calc(100% - var(--overlap-size, 40px)),transparent 100%);mask-image:linear-gradient(to bottom,transparent 0%,black 10%,black 90%,transparent 100%),linear-gradient(to left,transparent 0%,black 15%,black calc(100% - var(--overlap-size, 40px)),transparent 100%)}.configure-backend-message{padding:1rem;text-align:center;color:#bdc3c7;background:#34495e;border-radius:8px}.profile-screen{width:100%;max-width:460px;margin:0 auto;overflow:visible;position:relative}.profile-flex{display:flex;width:100%;height:auto;align-items:center;overflow:hidden;background:linear-gradient(135deg,#1a1a1a,#0a0a0a);gap:0;margin:0;padding:0;position:relative;box-shadow:none;border:none;--overlap-size: 40px}.profile-flex.fade-all{-webkit-mask-image:linear-gradient(to right,transparent 0%,black 15%,black 85%,transparent 100%),linear-gradient(to bottom,transparent 0%,black 15%,black 85%,transparent 100%);-webkit-mask-composite:intersect;mask-image:linear-gradient(to right,transparent 0%,black 15%,black 85%,transparent 100%),linear-gradient(to bottom,transparent 0%,black 15%,black 85%,transparent 100%);mask-composite:intersect}.shape-bg{position:relative;pointer-events:auto;width:100%;height:100%}.shape-bg1,.shape-bg2{position:absolute;width:75%;max-width:none;height:350px;opacity:1;transition:transform .2s ease-out}.shape-bg1{z-index:1;left:0}.shape-bg2{right:0}.shape-bg.fade-all .shape-bg1{-webkit-mask-image:linear-gradient(to right,transparent 0%,black 20%,black 100%);mask-image:linear-gradient(to right,transparent 0%,black 20%,black 100%)}.shape-bg.fade-all .shape-bg2{-webkit-mask-image:linear-gradient(to left,transparent 0%,black 20%,black 100%);mask-image:linear-gradient(to left,transparent 0%,black 20%,black 100%)}.shape-bg svg{cursor:grab;overflow:visible!important}.shape-bg svg:active{cursor:grabbing}.profile-img{width:50%;height:350px;position:relative;flex-shrink:0;background:linear-gradient(135deg,#1a1a1a,#0a0a0a);margin:0;padding:0;left:0;top:0;border:none;box-sizing:border-box;overflow:hidden;box-shadow:none;outline:none}.profile-img img{transition:transform .3s ease;width:100%;height:100%;object-fit:cover;display:block;object-position:center 30%;opacity:.75;transform-origin:center center;border:none;outline:none;box-shadow:none;position:relative;top:0;left:0;filter:contrast(1.2) brightness(1.1) saturate(1.1);image-rendering:auto}.shape{position:absolute;top:32px;width:100%;left:0;z-index:2;pointer-events:none}.shape-bg{position:relative;pointer-events:auto}.shape-text{position:absolute;top:0;left:0;right:0;width:100%;height:100%;z-index:1;padding:0 1.25rem;box-sizing:border-box;pointer-events:none}.shape-text-left{margin-top:40px;text-align:left;position:absolute;top:0;bottom:0;right:calc(50% + 75px);z-index:2;pointer-events:auto;display:flex;flex-direction:column;align-items:flex-end}.shape-text-left .scroll-container{width:100%}.shape-text-center{position:absolute;left:0;right:0;margin:0 auto;width:fit-content;font-family:Gilroy,sans-serif;font-weight:700;font-size:.75rem;line-height:100%;letter-spacing:0%;color:#fff;text-align:center;display:flex;justify-content:center;align-items:center;flex-direction:column;text-shadow:0 2px 4.2px #000;z-index:10;transition:transform .3s ease;pointer-events:none;top:50%;transform:translateY(-50%);padding:0 .625rem;margin-top:3.8125rem}.shape-text-right{margin-top:40px;text-align:right;position:absolute;top:0;bottom:0;left:calc(50% + 70px);z-index:2;pointer-events:auto;display:flex;flex-direction:column;align-items:flex-start}.shape-text-right .scroll-container{width:100%}.shape-p{font-family:Gilroy,sans-serif;font-weight:400;font-size:.75rem;line-height:100%;letter-spacing:0%;color:#fff;padding-bottom:.125rem;text-align:right;text-shadow:0 2px 4.2px #000;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;direction:rtl;-webkit-user-select:none;user-select:none;margin:0}.shape-p-center{font-family:Gilroy,sans-serif;font-weight:700;font-size:.75rem;line-height:100%;letter-spacing:0%;color:#fff;margin:0;text-align:center;display:flex;justify-content:center;align-items:center;text-shadow:0 2px 4.2px #000}.shape-p-right{font-family:Gilroy,sans-serif;font-weight:400;font-size:.75rem;line-height:100%;letter-spacing:0%;color:#fff;padding-bottom:.125rem;text-align:left;text-shadow:0 2px 4.2px #000;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;margin:0;-webkit-user-select:none;user-select:none}.dash-item{color:#ffffff4d!important;font-size:.625rem!important;height:.625rem}.spacer-item{height:auto!important;color:#fffc!important;font-weight:700;font-size:.75rem!important;letter-spacing:2px}.shape-h2{font-family:Calistoga,serif;font-weight:400;font-size:.625rem;letter-spacing:0%;color:#fff;text-shadow:0 2px 4.2px #000;cursor:pointer;transition:all .3s ease;border:none;outline:none;white-space:nowrap;pointer-events:auto;z-index:100;position:relative;margin-left:.5rem}.shape-h2:hover{opacity:.8;transform:translateY(-2px)}.shape-h2-right{font-family:Calistoga,serif;font-weight:400;font-size:.625rem;letter-spacing:0%;color:#fff;text-shadow:0 2px 4.2px #000;cursor:pointer;transition:all .3s ease;border:none;outline:none;white-space:nowrap;pointer-events:auto;z-index:100;position:relative;margin-right:3.75rem}.shape-h2-right:hover{opacity:.8;transform:translateY(-2px)}.scroll-when-long{display:inline-block;max-width:4.5625rem;white-space:nowrap;overflow-x:auto;overflow-y:hidden;-webkit-overflow-scrolling:touch}.scroll-when-long::-webkit-scrollbar{height:2px}.scroll-when-long::-webkit-scrollbar-track{background:#eee;border-radius:.625rem}.scroll-when-long::-webkit-scrollbar-thumb{background:#888;border-radius:.625rem}.scroll-when-long::-webkit-scrollbar-thumb:hover{background:#555}.shape-text p{cursor:default;-webkit-user-select:none;user-select:none}.shape-text p:active{cursor:default}:host{display:block}.profile-comparison{width:100%;padding:0;box-sizing:border-box;display:flex;justify-content:center;align-items:center;overflow:visible}.profile-comparison__inner{position:relative;width:100%;min-width:25.75rem;max-width:100%;min-height:500px;display:grid;grid-template-columns:1fr;grid-template-rows:auto;background:transparent;overflow:visible}.profile-comparison__bg-layer{grid-area:1/1;position:relative;width:100%;height:100%;min-height:inherit;pointer-events:none;display:grid;grid-template-columns:1fr;grid-template-rows:1fr}.profile-comparison__frame{grid-area:1/1;position:relative;pointer-events:none}.profile-comparison__frame.bg-frame{width:100%;height:100%;min-height:inherit;overflow:hidden;border-radius:10px;-webkit-mask-composite:source-in;mask-composite:intersect;will-change:auto}.profile-comparison__frame.bg-frame.profile-comparison__frame--left{-webkit-mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 278'%3E%3Cpath d='M246.177 26.4924 L10 1.07 Q0 0 0 10 V268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%);mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 278'%3E%3Cpath d='M246.177 26.4924 L10 1.07 Q0 0 0 10 V268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%);-webkit-mask-size:100% 100%,100% 100%;mask-size:100% 100%,100% 100%;-webkit-mask-composite:source-in;mask-composite:intersect}.fade-all-edges .profile-comparison__frame.bg-frame.profile-comparison__frame--left.edge-at-left-border{-webkit-mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 278'%3E%3Cpath d='M246.177 26.4924 L10 1.07 Q0 0 0 10 V268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%),linear-gradient(to right,transparent 0%,black 20px,black 100%);mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 256 278'%3E%3Cpath d='M246.177 26.4924 L10 1.07 Q0 0 0 10 V268 Q0 278 9.8 276.5 L246.68 239.869 C252.043 239.04 256 234.407 256 228.981 V37.4497 C256 31.8302 251.764 27.0937 246.177 26.4924 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%),linear-gradient(to right,transparent 0%,black 20px,black 100%);-webkit-mask-size:100% 100%,100% 100%,100% 100%;mask-size:100% 100%,100% 100%,100% 100%;-webkit-mask-composite:source-in;mask-composite:intersect}.profile-comparison__frame.bg-frame.profile-comparison__frame--right{-webkit-mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 257 278'%3E%3Cpath d='M9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 V267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%);mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 257 278'%3E%3Cpath d='M9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 V267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%);-webkit-mask-size:100% 100%,100% 100%;mask-size:100% 100%,100% 100%;-webkit-mask-composite:source-in;mask-composite:intersect}.fade-all-edges .profile-comparison__frame.bg-frame.profile-comparison__frame--right.edge-at-right-border{-webkit-mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 257 278'%3E%3Cpath d='M9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 V267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%),linear-gradient(to left,transparent 0%,black 20px,black 100%);mask-image:url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 257 278'%3E%3Cpath d='M9.8262 26.4454 L244.066 1.30651 L246.24 1.1 Q256.24 0 256.24 10 V267.5 Q256.24 277.5 246.24 275.9 L243.564 275.546 L9.32411 239.437 C3.95944 238.61 0 233.976 0 228.548 V37.4032 C0 31.7824 4.2375 27.0452 9.8262 26.4454 Z' fill='white'/%3E%3C/svg%3E\"),linear-gradient(to top,transparent 0%,black 15px,black 100%),linear-gradient(to left,transparent 0%,black 20px,black 100%);-webkit-mask-size:100% 100%,100% 100%,100% 100%;mask-size:100% 100%,100% 100%,100% 100%;-webkit-mask-composite:source-in;mask-composite:intersect}.profile-comparison__frame--left{transform:translate(-24%) scale(.86)}.profile-comparison__frame--left.is-animating-nudge{animation:nudgeLeft .5s ease-out}.profile-comparison__frame--left .profile-comparison__frame-svg{-webkit-mask-image:var(--edge-mask-left, linear-gradient(to right, transparent 0%, rgba(0, 0, 0, .4) 14px, black var(--edge-shading-width, 28px)));mask-image:var(--edge-mask-left, linear-gradient(to right, transparent 0%, rgba(0, 0, 0, .4) 14px, black var(--edge-shading-width, 28px)))}.profile-comparison__frame--left .profile-comparison__frame-svg--glass{transform:scale(1.01) translateY(-2px);transform-origin:center;-webkit-mask-image:none;mask-image:none}.profile-comparison__frame--right{transform:translate(24%) scale(.86)}.profile-comparison__frame--right.is-animating-nudge{animation:nudgeRight .5s ease-out}.profile-comparison__frame--right .profile-comparison__frame-svg{-webkit-mask-image:var(--edge-mask-right, linear-gradient(to left, transparent 0%, rgba(0, 0, 0, .4) 14px, black var(--edge-shading-width, 28px)));mask-image:var(--edge-mask-right, linear-gradient(to left, transparent, black var(--edge-shading-width, 28px)))}.profile-comparison__frame--right .profile-comparison__frame-svg--glass{transform:scale(1.01) translate(-1px,-2px);transform-origin:center;-webkit-mask-image:none;mask-image:none}.profile-comparison__frame--right .profile-comparison__frame-svg--bottom{width:113.2%}.profile-comparison__frame-svg{position:absolute;inset:0;width:100%;height:100%;pointer-events:auto;overflow:visible}.profile-comparison__frame-svg--glass{z-index:1}.profile-comparison__frame-svg--middle{z-index:2;mix-blend-mode:screen}.profile-comparison__frame-svg--left,.profile-comparison__frame-svg--right{z-index:3}.profile-comparison__frame-svg--bottom{width:113.7%;height:112.6%;inset:51% auto auto 50%;transform:translate(-50%,-51%)}.profile-comparison__bg{position:absolute;top:0;left:0;right:0;height:130%;background-size:cover;background-repeat:no-repeat;background-position:center 30%;pointer-events:auto;opacity:.8;transition:filter .6s ease,opacity .6s ease}.profile-comparison__bg--left{background-position:right center;margin-right:0%;-webkit-mask-image:linear-gradient(to right,black 50%,transparent 100%);mask-image:linear-gradient(to right,black 50%,transparent 100%);-webkit-mask-composite:source-in;mask-composite:intersect}.profile-comparison__bg--right{background-position:left center;margin-left:0%;-webkit-mask-image:linear-gradient(to left,black 50%,transparent 100%);mask-image:linear-gradient(to left,black 50%,transparent 100%);-webkit-mask-composite:source-in;mask-composite:intersect}@keyframes viewProfileBob{0%{transform:translateY(0)}50%{transform:translateY(-3px)}to{transform:translateY(0)}}.profile-comparison svg text{transition:opacity .3s ease}.profile-comparison svg text.is-animating-bob{animation:viewProfileBob .6s ease-in-out;fill:#fff!important}.profile-comparison__content{position:relative;z-index:20;padding:50px 12% 75px;min-height:100%;display:flex;flex-direction:column;justify-content:flex-start;color:#ecf0f1;font-family:Gilroy,serif;pointer-events:none}.profile-comparison__content--left{align-items:flex-end;text-align:right;padding-right:29%}.profile-comparison__content--right{align-items:flex-start;text-align:left;padding-left:29%}.profile-comparison__list{display:flex;flex-direction:column;gap:2px;width:100%;pointer-events:auto}.profile-comparison__list--left{align-items:flex-end;text-align:right;padding-right:29%}.profile-comparison__list--right{align-items:flex-start;text-align:left;padding-left:29%}.profile-comparison__item{display:flex;flex-direction:column;height:auto;width:11rem;flex-shrink:0;gap:6px;margin-bottom:6px;animation:textDropIn .4s ease-out backwards}.profile-comparison__item--left{align-items:flex-end;text-align:right}.profile-comparison__item--right{align-items:flex-start;text-align:left}.profile-comparison__text{font-family:Gilroy,sans-serif;font-size:15px;font-weight:100!important;color:#fff;width:100%;display:block;text-shadow:0 2px 4.2px #000}.profile-comparison__text ::ng-deep *{font-weight:400!important;text-shadow:0 2px 4.2px #000}.profile-comparison__dash-container{display:flex;width:100%;height:8px;align-items:center}.profile-comparison__item--left .profile-comparison__dash-container{justify-content:flex-end}.profile-comparison__item--right .profile-comparison__dash-container{justify-content:flex-start}.profile-comparison__dash{display:block}.profile-comparison__center-empty,.profile-comparison__center-dash-placeholder{height:8px;width:100%}.profile-comparison__center{grid-area:1/1;display:flex;align-items:flex-start;justify-content:center;z-index:15;pointer-events:none;transform:scale(.86)}.profile-comparison__center-inner{display:flex;flex-direction:column;align-items:center;text-align:center;justify-content:flex-start;padding:50px 0 75px;gap:6px;width:100%}.profile-comparison__center-item{height:auto;min-height:22px;display:flex;flex-direction:column;align-items:center;justify-content:flex-start;width:9rem;flex-shrink:0;gap:6px;margin-bottom:6px;animation:textDropIn .4s ease-out backwards}.profile-comparison__center-text{font-family:Gilroy,sans-serif;font-size:15px;font-weight:700;color:#fff;width:100%;display:block;text-shadow:0 2px 4.2px #000}.profile-comparison__center-text--empty{color:#ecf0f180}.loading-indicator{position:fixed;top:25%;left:50%;transform:translate(-50%,-50%);background:transparent;color:#fff;padding:20px 30px;border-radius:10px;text-align:center;z-index:1000;display:flex;flex-direction:column;align-items:center;gap:8px}.loading-spinner{width:40px;height:40px;border:4px solid rgba(255,255,255,.3);border-top:4px solid #667eea;border-radius:50%;animation:spin 1s linear infinite}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.loading-indicator p{margin:0;font-size:16px;font-weight:500}.api-key-modal-overlay{position:fixed;inset:0;background:#0f172a99;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;z-index:2000}.api-key-modal{width:min(640px,92vw);background:#0f172a;color:#e2e8f0;border:1px solid #334155;border-radius:12px;box-shadow:0 20px 50px #00000073;padding:16px 18px}.api-key-modal-header{display:flex;align-items:center;justify-content:space-between;gap:12px}.api-key-modal-header h2{margin:0;font-size:20px;font-weight:700;color:#e2e8f0}.warning-icon{color:#fbbf24}.api-key-modal .close-btn{background:#0b1220;border:1px solid #334155;color:#94a3b8;border-radius:8px;padding:6px;cursor:pointer;line-height:0}.api-key-modal-body{margin-top:12px;display:flex;flex-direction:column;gap:10px}.modal-message,.modal-instruction{margin:0;color:#cbd5e1}.modal-message strong{color:#e5e7eb}.api-key-input-group{display:flex;align-items:center;gap:10px}.api-key-input{flex:1 1 auto;background:#0a1020;color:#e2e8f0;border:1px solid #334155;border-radius:8px;padding:10px 12px}.api-key-input:focus{outline:none;border-color:#2563eb;box-shadow:0 0 0 3px #2563eb33}.api-key-load-btn{background:#2563eb;color:#fff;border:none;border-radius:8px;padding:10px 12px;cursor:pointer;font-weight:600}.api-key-load-btn:hover{background:#1d4ed8}.modal-hint{display:flex;align-items:center;gap:8px;color:#94a3b8}.modal-hint a{color:#93c5fd}\n"] }]
2460
+ args: [{ selector: 'lib-profile-comparison', standalone: false, template: "<div class=\"configure-backend-message\" *ngIf=\"!backendConfigured\">\n Configure backend\n</div>\n\n<div class=\"profile-screen\" *ngIf=\"backendConfigured\">\n <div #profileFlex class=\"profile-flex\" [class.fade-all]=\"fadeAllEdges\">\n <svg class=\"image-noise-overlay\" style=\"position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; overflow: hidden; z-index: 0;\" xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 320 320\" fill=\"none\" preserveAspectRatio=\"none\">\n <g filter=\"url(#filter0_n_1_1753)\">\n <rect width=\"100%\" height=\"100%\" fill=\"url(#paint0_radial_1_1753)\" fill-opacity=\"0.33\"/>\n </g>\n <defs>\n <filter id=\"filter0_n_1_1753\" x=\"0%\" y=\"0%\" width=\"100%\" height=\"100%\" filterUnits=\"objectBoundingBox\" color-interpolation-filters=\"sRGB\">\n <feFlood flood-opacity=\"0\" result=\"BackgroundImageFix\"/>\n <feBlend mode=\"normal\" in=\"SourceGraphic\" in2=\"BackgroundImageFix\" result=\"shape\"/>\n <feTurbulence type=\"fractalNoise\" baseFrequency=\"0.10869565606117249 0.10869565606117249\" stitchTiles=\"stitch\" numOctaves=\"3\" result=\"noise\" seed=\"7339\"/>\n <feColorMatrix in=\"noise\" type=\"luminanceToAlpha\" result=\"alphaNoise\"/>\n <feComponentTransfer in=\"alphaNoise\" result=\"coloredNoise1\">\n <feFuncA type=\"discrete\" tableValues=\"0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 \"/>\n </feComponentTransfer>\n <feComposite operator=\"in\" in2=\"shape\" in=\"coloredNoise1\" result=\"noise1Clipped\"/>\n <feFlood flood-color=\"rgba(0, 0, 0, 0.25)\" result=\"color1Flood\"/>\n <feComposite operator=\"in\" in2=\"noise1Clipped\" in=\"color1Flood\" result=\"color1\"/>\n <feMerge result=\"effect1_noise_1_1753\">\n <feMergeNode in=\"shape\"/>\n <feMergeNode in=\"color1\"/>\n </feMerge>\n </filter>\n <radialGradient id=\"paint0_radial_1_1753\" cx=\"50%\" cy=\"50%\" r=\"50%\" fx=\"50%\" fy=\"50%\">\n <stop offset=\"0%\" stop-opacity=\"0\"/>\n <stop offset=\"100%\" stop-opacity=\"0.25\"/>\n </radialGradient>\n </defs>\n </svg>\n <div #profileImgLeft class=\"profile-img left\" [class.fade-all]=\"fadeAllEdges\">\n <img\n [src]=\"user1Image\"\n alt=\"User 1\"\n [style.transform]=\"user1Transform\"\n [style.top]=\"user1Top\"\n [style.height]=\"user1Height\"\n [style.object-position]=\"user1ObjectPosition\"\n (load)=\"onUserImageLoad(1, $event)\"\n />\n </div>\n <div #profileImgRight class=\"profile-img right\" [class.fade-all]=\"fadeAllEdges\">\n <img\n [src]=\"user2Image\"\n alt=\"User 2\"\n [style.transform]=\"user2Transform\"\n [style.top]=\"user2Top\"\n [style.height]=\"user2Height\"\n [style.object-position]=\"user2ObjectPosition\"\n (load)=\"onUserImageLoad(2, $event)\"\n />\n </div>\n <div class=\"image-shade-overlay\"></div>\n\n <!-- Frosted Glass Overlay Layer -->\n <div class=\"overlay-container\">\n <!-- Top Headers -->\n <div class=\"header-label left\">{{ person1Name }}</div>\n <div class=\"header-label center\">Shared</div>\n <div class=\"header-label right\">{{ person2Name }}</div>\n\n <!-- Chips Columns Grid -->\n <div class=\"chips-grid\">\n <!-- Person 1 Unique Interests -->\n <div class=\"chips-column left\">\n <ng-container *ngFor=\"let interest of getPerson1UniqueInterests(); let i = index\">\n <div class=\"frosted-chip person-a\" [style.margin-right.px]=\"getChipOffset(i, 'left')\" [style.margin-top.px]=\"(i > 0 ? 4 : 0)\">\n <span class=\"chip-label\">{{ interest }}</span>\n </div>\n </ng-container>\n </div>\n\n <!-- Shared Interests (Center Column) -->\n <div class=\"chips-column center\">\n <ng-container *ngFor=\"let interest of centerItem; let i = index\">\n <div class=\"frosted-chip shared\" *ngIf=\"interest && interest !== '-' && interest !== '----'\" [style.margin-left.px]=\"getChipOffset(i, 'center')\" [style.margin-top.px]=\"(i > 0 ? 4 : 0)\">\n <span class=\"chip-label\">{{ interest }}</span>\n </div>\n </ng-container>\n </div>\n\n <!-- Person 2 Unique Interests -->\n <div class=\"chips-column right\">\n <ng-container *ngFor=\"let interest of getPerson2UniqueInterests(); let i = index\">\n <div class=\"frosted-chip person-b\" [style.margin-left.px]=\"getChipOffset(i, 'right')\" [style.margin-top.px]=\"(i > 0 ? 4 : 0)\">\n <span class=\"chip-label\">{{ interest }}</span>\n </div>\n </ng-container>\n </div>\n </div>\n\n <!-- Bottom Actions -->\n <div class=\"view-profile left\" (click)=\"onViewProfile('left')\">View Profile</div>\n <div class=\"view-profile right\" (click)=\"onViewProfile('right')\">View Profile</div>\n </div>\n\n <!-- Loading indicator for alignment process -->\n <div *ngIf=\"isAligning\" class=\"loading-indicator\">\n <div class=\"loading-spinner\"></div>\n </div>\n</div>\n\n\n", styles: [":host{display:block;width:100%;max-width:100%;overflow-x:hidden!important;box-sizing:border-box!important}.profile-img.left{position:absolute;top:0;bottom:0;left:0;width:58%;height:100%;margin:0;z-index:1;-webkit-mask-image:linear-gradient(to right,black 0%,black 45%,transparent 92%);mask-image:linear-gradient(to right,black 0%,black 45%,transparent 92%)}.profile-img.right{position:absolute;inset:0 0 0 auto;width:58%;height:100%;margin:0;z-index:2;-webkit-mask-image:linear-gradient(to right,transparent 8%,black 55%,black 100%);mask-image:linear-gradient(to right,transparent 8%,black 55%,black 100%)}@media(min-width:768px){.profile-img.left{width:65%;-webkit-mask-image:linear-gradient(to right,black 0%,black 40%,transparent 90%);mask-image:linear-gradient(to right,black 0%,black 40%,transparent 90%)}.profile-img.right{width:65%;-webkit-mask-image:linear-gradient(to right,transparent 10%,black 60%,black 100%);mask-image:linear-gradient(to right,transparent 10%,black 60%,black 100%)}}.configure-backend-message{padding:1rem;text-align:center;color:#bdc3c7;background:#34495e;border-radius:8px}.backend-error-message{padding:.75rem 1rem;text-align:center;color:#fef3c7;background:#b4530940;border:1px solid rgba(245,158,11,.5);border-radius:8px;margin-bottom:.5rem}.profile-screen{width:100%;max-width:768px;margin:0 auto;overflow:hidden;position:relative;border-radius:12px}.profile-flex{display:flex;width:100%;height:440px;overflow:hidden;background:transparent;gap:0;margin:0;padding:0;position:relative;box-shadow:none;border:none;--overlap-size: 40px}.profile-flex.fade-all{-webkit-mask-image:linear-gradient(to right,transparent 0%,black 18%,black 82%,transparent 100%),linear-gradient(to bottom,transparent 0%,black 15%,black 85%,transparent 100%);-webkit-mask-composite:intersect;mask-image:linear-gradient(to right,transparent 0%,black 18%,black 82%,transparent 100%),linear-gradient(to bottom,transparent 0%,black 15%,black 85%,transparent 100%);mask-composite:intersect}.profile-flex .image-shade-overlay{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:3;background:radial-gradient(ellipse at center,transparent 40%,rgba(41,37,45,.25) 100%)}.profile-flex .image-noise-overlay{position:absolute;inset:0;width:100%;height:100%;max-width:100%;max-height:100%;pointer-events:none;overflow:hidden;z-index:0}.shape-bg1,.shape-bg2{position:absolute;max-width:250px;opacity:1;transition:transform .2s ease-out}.shape-bg1{z-index:1;left:0}.shape-bg2{right:0}.shape-bg.fade-all .shape-bg1{-webkit-mask-image:linear-gradient(to right,transparent 0%,black 20%,black 100%);mask-image:linear-gradient(to right,transparent 0%,black 20%,black 100%)}.shape-bg.fade-all .shape-bg2{-webkit-mask-image:linear-gradient(to left,transparent 0%,black 20%,black 100%);mask-image:linear-gradient(to left,transparent 0%,black 20%,black 100%)}.shape-bg svg{cursor:grab}.shape-bg svg:active{cursor:grabbing}.profile-img{height:100%;position:relative;flex-shrink:0;background:transparent;margin:0;padding:0;left:0;top:0;border:none;box-sizing:border-box;overflow:hidden;box-shadow:none;outline:none}.profile-img img{transition:transform .3s ease;width:100%;height:100%;object-fit:cover;display:block;object-position:center center;opacity:1;transform-origin:center center;border:none;outline:none;box-shadow:none;position:relative;top:0;left:0;filter:contrast(1.2) brightness(1.1) saturate(1.1);image-rendering:auto}.overlay-container{position:absolute;inset:0;width:100%;height:100%;pointer-events:none;z-index:10;-webkit-user-select:none;user-select:none;overflow:hidden;background:#29252da6;backdrop-filter:blur(.5px);-webkit-backdrop-filter:blur(.5px)}.header-label{position:absolute;top:20px;font-family:Gilroy,sans-serif;text-shadow:0 1px 8px rgba(0,0,0,.8);pointer-events:none}.header-label.left{left:5%;transform:none;font-size:14px;font-weight:400;color:#fffffff2}.header-label.center{left:50%;transform:translate(-50%);font-size:12px;font-weight:400;color:#fffc;letter-spacing:0}.header-label.right{right:5%;left:auto;transform:none;font-size:14px;font-weight:400;color:#fffffff2}.chips-grid{position:absolute;inset:45px 0;display:grid;grid-template-columns:repeat(3,1fr);padding:0;pointer-events:none;box-sizing:border-box}.chips-column{display:flex;flex-direction:column;gap:8px;max-height:100%;overflow-y:auto;padding:4px 10px;box-sizing:border-box;width:100%;scrollbar-width:none}.chips-column::-webkit-scrollbar{display:none}.chips-column.left{align-items:flex-end}.chips-column.center{align-items:center}.chips-column.right{align-items:flex-start}.frosted-chip{position:relative;cursor:pointer;pointer-events:auto;transition:transform .2s ease,box-shadow .2s ease,background .2s ease;flex-shrink:0;padding:6px 18px;border-radius:999px;display:inline-flex;align-items:center;justify-content:center;border:none;background:#ffffff30;box-shadow:inset 0 1.5px 2px #ffffff59;backdrop-filter:blur(.5px);-webkit-backdrop-filter:blur(.5px)}.frosted-chip:hover{transform:scale(1.04);background:#ffffff29}.frosted-chip .chip-label{position:relative;color:#a8a8a8;text-align:center;font-family:Gilroy,sans-serif;font-size:14px;font-style:normal;font-weight:400;line-height:140%;letter-spacing:-.01em;text-shadow:none;white-space:nowrap;pointer-events:none;z-index:1}.view-profile{position:absolute;bottom:40px!important;color:#ffffff80;text-shadow:0 2px 4.2px #000;font-family:Calistoga,serif;font-size:18px;font-style:normal;font-weight:400;line-height:normal;cursor:pointer;pointer-events:auto;transition:opacity .2s ease,transform .2s ease}.view-profile:hover{opacity:.85}.view-profile.left{left:5%;transform:none}.view-profile.left:hover{transform:translateY(-2px)}.view-profile.right{right:5%;left:auto;transform:none}.view-profile.right:hover{transform:translateY(-2px)}#drag-preview{position:absolute;pointer-events:none;display:none;background:#000000b3;color:#fff;padding:5px 10px;border-radius:5px;z-index:9999;max-width:200px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.shape-text-container{width:70px;position:relative;overflow:hidden}.draggable{width:70px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;cursor:grab;-webkit-user-select:none;user-select:none;position:relative;display:inline-block}.dragging{width:auto;text-overflow:clip;cursor:grabbing;position:absolute;z-index:1000}.p-wrapper{width:70px;overflow-x:hidden}.shape-text-center{display:flex;justify-content:space-between;flex-direction:column;gap:10px;height:100%;margin-top:55px}.loading-indicator{position:absolute;inset:0;width:100%;height:100%;background:#00000040;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);color:#fff;z-index:100;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;pointer-events:auto}.loading-spinner{width:44px;height:44px;border:3.5px solid rgba(255,255,255,.25);border-top:3.5px solid #ffffff;border-radius:50%;animation:spin .8s linear infinite;box-shadow:0 0 15px #fff3}@keyframes spin{0%{transform:rotate(0)}to{transform:rotate(360deg)}}.loading-indicator p{margin:0;font-size:14px;font-weight:500;color:#ffffffd9;letter-spacing:.02em}.api-key-modal-overlay{position:fixed;inset:0;background:#0f172a99;-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);display:flex;align-items:center;justify-content:center;z-index:2000}.api-key-modal{width:min(640px,92vw);background:#0f172a;color:#e2e8f0;border:1px solid #334155;border-radius:12px;box-shadow:0 20px 50px #00000073;padding:16px 18px}.api-key-modal-header{display:flex;align-items:center;justify-content:space-between;gap:12px}.api-key-modal-header h2{margin:0;font-size:20px;font-weight:700;color:#e2e8f0}.warning-icon{color:#fbbf24}.api-key-modal .close-btn{background:#0b1220;border:1px solid #334155;color:#94a3b8;border-radius:8px;padding:6px;cursor:pointer;line-height:0}.api-key-modal-body{margin-top:12px;display:flex;flex-direction:column;gap:10px}.modal-message,.modal-instruction{margin:0;color:#cbd5e1}.modal-message strong{color:#e5e7eb}.api-key-input-group{display:flex;align-items:center;gap:10px}.api-key-input{flex:1 1 auto;background:#0a1020;color:#e2e8f0;border:1px solid #334155;border-radius:8px;padding:10px 12px}.api-key-input:focus{outline:none;border-color:#2563eb;box-shadow:0 0 0 3px #2563eb33}.api-key-load-btn{background:#2563eb;color:#fff;border:none;border-radius:8px;padding:10px 12px;cursor:pointer;font-weight:600}.api-key-load-btn:hover{background:#1d4ed8}.modal-hint{display:flex;align-items:center;gap:8px;color:#94a3b8}.modal-hint a{color:#93c5fd}@media(max-width:1023px){.profile-screen{max-width:640px}.profile-flex{height:400px}.profile-img{height:550px}}@media(max-width:767px){.profile-screen,.profile-flex,.overlay-container{width:100%;max-width:100%;overflow-x:hidden;box-sizing:border-box}.profile-screen{border-radius:0}.profile-flex{height:360px}.profile-img{height:510px}.chips-grid{width:100%;max-width:100%;padding:0 4px;top:50px;bottom:50px;box-sizing:border-box}.chips-column{padding:2px}.chips-column.left{align-items:flex-end}.chips-column.center{align-items:center}.chips-column.right{align-items:flex-start}.frosted-chip{padding:4px 10px;max-width:100%;box-sizing:border-box}.frosted-chip .chip-label{font-size:11px;max-width:100%;overflow:hidden;text-overflow:ellipsis}.view-profile{bottom:14px}}@media(max-width:480px){.profile-flex{height:340px}.profile-img{height:480px}.frosted-chip{padding:3px 8px}.frosted-chip .chip-label{font-size:10px}.header-label{top:14px}.header-label.left{font-size:12px}.header-label.center{font-size:10px}.header-label.right{font-size:12px}}\n"] }]
2550
2461
  }], ctorParameters: () => [{ type: ProfileComparisonBackendService }, { type: i0.Renderer2 }, { type: FileConversionService }, { type: ImageCompressionService }, { type: i0.ChangeDetectorRef }, { type: undefined, decorators: [{
2551
2462
  type: Optional
2552
2463
  }, {
@@ -2560,59 +2471,68 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
2560
2471
  type: Input
2561
2472
  }], fadeAllEdges: [{
2562
2473
  type: Input
2474
+ }], person1Name: [{
2475
+ type: Input
2476
+ }], person2Name: [{
2477
+ type: Input
2563
2478
  }], matrixDataChange: [{
2564
2479
  type: Output
2565
2480
  }], rawLLMOutputChange: [{
2566
2481
  type: Output
2567
2482
  }], viewProfileClick: [{
2568
2483
  type: Output
2569
- }], edgeDrag: [{
2570
- type: Output
2571
- }], onResize: [{
2572
- type: HostListener,
2573
- args: ['window:resize']
2574
2484
  }], leftContainer: [{
2575
2485
  type: ViewChild,
2576
2486
  args: ['leftContainer']
2577
2487
  }], rightContainer: [{
2578
2488
  type: ViewChild,
2579
2489
  args: ['rightContainer']
2580
- }], leftFrame: [{
2581
- type: ViewChild,
2582
- args: ['leftFrame']
2583
- }], rightFrame: [{
2490
+ }], profileFlex: [{
2584
2491
  type: ViewChild,
2585
- args: ['rightFrame']
2586
- }], leftBgFrame: [{
2492
+ args: ['profileFlex']
2493
+ }], profileImgLeft: [{
2587
2494
  type: ViewChild,
2588
- args: ['leftBgFrame']
2589
- }], rightBgFrame: [{
2495
+ args: ['profileImgLeft']
2496
+ }], profileImgRight: [{
2590
2497
  type: ViewChild,
2591
- args: ['rightBgFrame']
2498
+ args: ['profileImgRight']
2592
2499
  }], shapeContainer: [{
2593
2500
  type: ViewChild,
2594
2501
  args: ['shapeContainer']
2502
+ }], shapeBg: [{
2503
+ type: ViewChild,
2504
+ args: ['shapeBg']
2505
+ }], shapeBg1: [{
2506
+ type: ViewChild,
2507
+ args: ['shapeBg1']
2508
+ }], shapeBg2: [{
2509
+ type: ViewChild,
2510
+ args: ['shapeBg2']
2511
+ }], shapeTextLeft: [{
2512
+ type: ViewChild,
2513
+ args: ['shapeTextLeft']
2514
+ }], shapeTextRight: [{
2515
+ type: ViewChild,
2516
+ args: ['shapeTextRight']
2595
2517
  }], shapeTextCenter: [{
2596
2518
  type: ViewChild,
2597
2519
  args: ['shapeTextCenter']
2598
2520
  }] } });
2599
2521
 
2600
2522
  class ProfileComparisonLibModule {
2601
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProfileComparisonLibModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
2602
- static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.19", ngImport: i0, type: ProfileComparisonLibModule, declarations: [ProfileComparisonLibComponent], imports: [CommonModule,
2523
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileComparisonLibModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
2524
+ static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.25", ngImport: i0, type: ProfileComparisonLibModule, declarations: [ProfileComparisonLibComponent], imports: [CommonModule,
2603
2525
  HttpClientModule,
2604
2526
  FormsModule,
2605
- ReactiveFormsModule,
2606
- LibMarqueeModule], exports: [ProfileComparisonLibComponent] });
2607
- static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProfileComparisonLibModule, providers: [
2527
+ ReactiveFormsModule], exports: [ProfileComparisonLibComponent] });
2528
+ static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileComparisonLibModule, providers: [
2608
2529
  ProfileComparisonBackendService
2609
2530
  ], imports: [CommonModule,
2610
2531
  HttpClientModule,
2611
2532
  FormsModule,
2612
- ReactiveFormsModule,
2613
- LibMarqueeModule] });
2533
+ ReactiveFormsModule] });
2614
2534
  }
2615
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImport: i0, type: ProfileComparisonLibModule, decorators: [{
2535
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.25", ngImport: i0, type: ProfileComparisonLibModule, decorators: [{
2616
2536
  type: NgModule,
2617
2537
  args: [{
2618
2538
  declarations: [
@@ -2622,8 +2542,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.19", ngImpo
2622
2542
  CommonModule,
2623
2543
  HttpClientModule,
2624
2544
  FormsModule,
2625
- ReactiveFormsModule,
2626
- LibMarqueeModule
2545
+ ReactiveFormsModule
2627
2546
  ],
2628
2547
  exports: [
2629
2548
  ProfileComparisonLibComponent