codexly-ui 0.1.22 → 0.1.24

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.
@@ -165,6 +165,163 @@ const BADGE_SHAPE_FIXED = {
165
165
  circle: 'rounded-full',
166
166
  };
167
167
 
168
+ const CLX_FONT_CATALOG = [
169
+ {
170
+ label: 'Roboto',
171
+ value: 'Roboto',
172
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap',
173
+ },
174
+ {
175
+ label: 'Inter',
176
+ value: 'Inter',
177
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Inter:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap',
178
+ },
179
+ {
180
+ label: 'Poppins',
181
+ value: 'Poppins',
182
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap',
183
+ },
184
+ {
185
+ label: 'Plus Jakarta Sans',
186
+ value: 'Plus Jakarta Sans',
187
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap',
188
+ },
189
+ {
190
+ label: 'Nunito',
191
+ value: 'Nunito',
192
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Nunito:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap',
193
+ },
194
+ {
195
+ label: 'DM Sans',
196
+ value: 'DM Sans',
197
+ googleUrl: 'https://fonts.googleapis.com/css2?family=DM+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap',
198
+ },
199
+ {
200
+ label: 'Geist',
201
+ value: 'Geist',
202
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Geist:wght@300;400;500;600;700&display=swap',
203
+ },
204
+ {
205
+ label: 'Lato',
206
+ value: 'Lato',
207
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Lato:ital,wght@0,300;0,400;0,700;1,300;1,400;1,700&display=swap',
208
+ },
209
+ {
210
+ label: 'Montserrat',
211
+ value: 'Montserrat',
212
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Montserrat:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap',
213
+ },
214
+ {
215
+ label: 'Open Sans',
216
+ value: 'Open Sans',
217
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Open+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap',
218
+ },
219
+ {
220
+ label: 'Work Sans',
221
+ value: 'Work Sans',
222
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Work+Sans:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap',
223
+ },
224
+ {
225
+ label: 'Manrope',
226
+ value: 'Manrope',
227
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Manrope:wght@300;400;500;600;700&display=swap',
228
+ },
229
+ {
230
+ label: 'Sora',
231
+ value: 'Sora',
232
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Sora:wght@300;400;500;600;700&display=swap',
233
+ },
234
+ {
235
+ label: 'Outfit',
236
+ value: 'Outfit',
237
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap',
238
+ },
239
+ {
240
+ label: 'Rubik',
241
+ value: 'Rubik',
242
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Rubik:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap',
243
+ },
244
+ {
245
+ label: 'Source Sans 3',
246
+ value: 'Source Sans 3',
247
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Source+Sans+3:ital,wght@0,300;0,400;0,500;0,600;0,700;1,300;1,400;1,500;1,600;1,700&display=swap',
248
+ },
249
+ {
250
+ label: 'Space Grotesk',
251
+ value: 'Space Grotesk',
252
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&display=swap',
253
+ },
254
+ {
255
+ label: 'Merriweather',
256
+ value: 'Merriweather',
257
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Merriweather:ital,wght@0,300;0,400;0,700;0,900;1,300;1,400;1,700;1,900&display=swap',
258
+ },
259
+ {
260
+ label: 'Playfair Display',
261
+ value: 'Playfair Display',
262
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,500;0,600;0,700;0,800;0,900;1,400;1,500;1,600;1,700;1,800;1,900&display=swap',
263
+ },
264
+ {
265
+ label: 'Lora',
266
+ value: 'Lora',
267
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Lora:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600;1,700&display=swap',
268
+ },
269
+ {
270
+ label: 'PT Serif',
271
+ value: 'PT Serif',
272
+ googleUrl: 'https://fonts.googleapis.com/css2?family=PT+Serif:ital,wght@0,400;0,700;1,400;1,700&display=swap',
273
+ },
274
+ {
275
+ label: 'Caveat',
276
+ value: 'Caveat',
277
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Caveat:wght@400;500;600;700&display=swap',
278
+ },
279
+ {
280
+ label: 'Dancing Script',
281
+ value: 'Dancing Script',
282
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Dancing+Script:wght@400;500;600;700&display=swap',
283
+ },
284
+ {
285
+ label: 'Cinzel',
286
+ value: 'Cinzel',
287
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Cinzel:wght@400;500;600;700;800;900&display=swap',
288
+ },
289
+ {
290
+ label: 'Berkshire Swash',
291
+ value: 'Berkshire Swash',
292
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Berkshire+Swash&display=swap',
293
+ },
294
+ {
295
+ label: 'JetBrains Mono',
296
+ value: 'JetBrains Mono',
297
+ googleUrl: 'https://fonts.googleapis.com/css2?family=JetBrains+Mono:ital,wght@0,400;0,500;0,600;0,700;1,400;1,500;1,600;1,700&display=swap',
298
+ },
299
+ {
300
+ label: 'Orbitron',
301
+ value: 'Orbitron',
302
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Orbitron:wght@400;500;600;700;800;900&display=swap',
303
+ },
304
+ {
305
+ label: 'UnifrakturMaguntia',
306
+ value: 'UnifrakturMaguntia',
307
+ googleUrl: 'https://fonts.googleapis.com/css2?family=UnifrakturMaguntia&display=swap',
308
+ },
309
+ {
310
+ label: 'Pirata One',
311
+ value: 'Pirata One',
312
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Pirata+One&display=swap',
313
+ },
314
+ {
315
+ label: 'Press Start 2P',
316
+ value: 'Press Start 2P',
317
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap',
318
+ },
319
+ {
320
+ label: 'Bungee',
321
+ value: 'Bungee',
322
+ googleUrl: 'https://fonts.googleapis.com/css2?family=Bungee&display=swap',
323
+ },
324
+ ];
168
325
  // ── Tailwind rounded classes per borderRadius token ───────────────────────────
169
326
  const CLX_RADIUS_MAP = {
170
327
  none: 'rounded-none',
@@ -194,13 +351,16 @@ function resolveContainerRadius(r) {
194
351
  // ── Default theme ─────────────────────────────────────────────────────────────
195
352
  const CLX_THEME_DEFAULTS = {
196
353
  primaryColor: 'indigo',
354
+ secondaryColor: 'slate',
197
355
  dangerColor: 'red',
198
356
  successColor: 'green',
199
357
  warningColor: 'amber',
200
358
  neutralColor: 'slate',
359
+ textColor: 'slate',
201
360
  borderRadius: 'md',
202
361
  defaultSize: 'md',
203
362
  defaultShape: 'rounded',
363
+ fontFamily: 'Roboto',
204
364
  };
205
365
  // ── Injection token ───────────────────────────────────────────────────────────
206
366
  const CLX_THEME_CONFIG = new InjectionToken('CLX_THEME_CONFIG', { factory: () => CLX_THEME_DEFAULTS });
@@ -212,7 +372,107 @@ function provideCodexlyTheme(config) {
212
372
  };
213
373
  }
214
374
 
375
+ // ── Full Tailwind v4 palette (hex), all colors × all shades ──────────────────
376
+ // Source of truth: official Tailwind CSS v4 color values.
377
+ const CLX_COLOR_PALETTE = {
378
+ red: {
379
+ 50: '#fef2f2', 100: '#fee2e2', 200: '#fecaca', 300: '#fca5a5', 400: '#f87171',
380
+ 500: '#ef4444', 600: '#dc2626', 700: '#b91c1c', 800: '#991b1b', 900: '#7f1d1d', 950: '#450a0a',
381
+ },
382
+ orange: {
383
+ 50: '#fff7ed', 100: '#ffedd5', 200: '#fed7aa', 300: '#fdba74', 400: '#fb923c',
384
+ 500: '#f97316', 600: '#ea580c', 700: '#c2410c', 800: '#9a3412', 900: '#7c2d12', 950: '#431407',
385
+ },
386
+ amber: {
387
+ 50: '#fffbeb', 100: '#fef3c7', 200: '#fde68a', 300: '#fcd34d', 400: '#fbbf24',
388
+ 500: '#f59e0b', 600: '#d97706', 700: '#b45309', 800: '#92400e', 900: '#78350f', 950: '#451a03',
389
+ },
390
+ yellow: {
391
+ 50: '#fefce8', 100: '#fef9c3', 200: '#fef08a', 300: '#fde047', 400: '#facc15',
392
+ 500: '#eab308', 600: '#ca8a04', 700: '#a16207', 800: '#854d0e', 900: '#713f12', 950: '#422006',
393
+ },
394
+ lime: {
395
+ 50: '#f7fee7', 100: '#ecfccb', 200: '#d9f99d', 300: '#bef264', 400: '#a3e635',
396
+ 500: '#84cc16', 600: '#65a30d', 700: '#4d7c0f', 800: '#3f6212', 900: '#365314', 950: '#1a2e05',
397
+ },
398
+ green: {
399
+ 50: '#f0fdf4', 100: '#dcfce7', 200: '#bbf7d0', 300: '#86efac', 400: '#4ade80',
400
+ 500: '#22c55e', 600: '#16a34a', 700: '#15803d', 800: '#166534', 900: '#14532d', 950: '#052e16',
401
+ },
402
+ emerald: {
403
+ 50: '#ecfdf5', 100: '#d1fae5', 200: '#a7f3d0', 300: '#6ee7b7', 400: '#34d399',
404
+ 500: '#10b981', 600: '#059669', 700: '#047857', 800: '#065f46', 900: '#064e3b', 950: '#022c22',
405
+ },
406
+ teal: {
407
+ 50: '#f0fdfa', 100: '#ccfbf1', 200: '#99f6e4', 300: '#5eead4', 400: '#2dd4bf',
408
+ 500: '#14b8a6', 600: '#0d9488', 700: '#0f766e', 800: '#115e59', 900: '#134e4a', 950: '#042f2e',
409
+ },
410
+ cyan: {
411
+ 50: '#ecfeff', 100: '#cffafe', 200: '#a5f3fc', 300: '#67e8f9', 400: '#22d3ee',
412
+ 500: '#06b6d4', 600: '#0891b2', 700: '#0e7490', 800: '#155e75', 900: '#164e63', 950: '#083344',
413
+ },
414
+ sky: {
415
+ 50: '#f0f9ff', 100: '#e0f2fe', 200: '#bae6fd', 300: '#7dd3fc', 400: '#38bdf8',
416
+ 500: '#0ea5e9', 600: '#0284c7', 700: '#0369a1', 800: '#075985', 900: '#0c4a6e', 950: '#082f49',
417
+ },
418
+ blue: {
419
+ 50: '#eff6ff', 100: '#dbeafe', 200: '#bfdbfe', 300: '#93c5fd', 400: '#60a5fa',
420
+ 500: '#3b82f6', 600: '#2563eb', 700: '#1d4ed8', 800: '#1e40af', 900: '#1e3a8a', 950: '#172554',
421
+ },
422
+ indigo: {
423
+ 50: '#eef2ff', 100: '#e0e7ff', 200: '#c7d2fe', 300: '#a5b4fc', 400: '#818cf8',
424
+ 500: '#6366f1', 600: '#4f46e5', 700: '#4338ca', 800: '#3730a3', 900: '#312e81', 950: '#1e1b4b',
425
+ },
426
+ violet: {
427
+ 50: '#f5f3ff', 100: '#ede9fe', 200: '#ddd6fe', 300: '#c4b5fd', 400: '#a78bfa',
428
+ 500: '#8b5cf6', 600: '#7c3aed', 700: '#6d28d9', 800: '#5b21b6', 900: '#4c1d95', 950: '#2e1065',
429
+ },
430
+ purple: {
431
+ 50: '#faf5ff', 100: '#f3e8ff', 200: '#e9d5ff', 300: '#d8b4fe', 400: '#c084fc',
432
+ 500: '#a855f7', 600: '#9333ea', 700: '#7e22ce', 800: '#6b21a8', 900: '#581c87', 950: '#3b0764',
433
+ },
434
+ fuchsia: {
435
+ 50: '#fdf4ff', 100: '#fae8ff', 200: '#f5d0fe', 300: '#f0abfc', 400: '#e879f9',
436
+ 500: '#d946ef', 600: '#c026d3', 700: '#a21caf', 800: '#86198f', 900: '#701a75', 950: '#4a044e',
437
+ },
438
+ pink: {
439
+ 50: '#fdf2f8', 100: '#fce7f3', 200: '#fbcfe8', 300: '#f9a8d4', 400: '#f472b6',
440
+ 500: '#ec4899', 600: '#db2777', 700: '#be185d', 800: '#9d174d', 900: '#831843', 950: '#500724',
441
+ },
442
+ rose: {
443
+ 50: '#fff1f2', 100: '#ffe4e6', 200: '#fecdd3', 300: '#fda4af', 400: '#fb7185',
444
+ 500: '#f43f5e', 600: '#e11d48', 700: '#be123c', 800: '#9f1239', 900: '#881337', 950: '#4c0519',
445
+ },
446
+ slate: {
447
+ 50: '#f8fafc', 100: '#f1f5f9', 200: '#e2e8f0', 300: '#cbd5e1', 400: '#94a3b8',
448
+ 500: '#64748b', 600: '#475569', 700: '#334155', 800: '#1e293b', 900: '#0f172a', 950: '#020617',
449
+ },
450
+ gray: {
451
+ 50: '#f9fafb', 100: '#f3f4f6', 200: '#e5e7eb', 300: '#d1d5db', 400: '#9ca3af',
452
+ 500: '#6b7280', 600: '#4b5563', 700: '#374151', 800: '#1f2937', 900: '#111827', 950: '#030712',
453
+ },
454
+ zinc: {
455
+ 50: '#fafafa', 100: '#f4f4f5', 200: '#e4e4e7', 300: '#d4d4d8', 400: '#a1a1aa',
456
+ 500: '#71717a', 600: '#52525b', 700: '#3f3f46', 800: '#27272a', 900: '#18181b', 950: '#09090b',
457
+ },
458
+ neutral: {
459
+ 50: '#fafafa', 100: '#f5f5f5', 200: '#e5e5e5', 300: '#d4d4d4', 400: '#a3a3a3',
460
+ 500: '#737373', 600: '#525252', 700: '#404040', 800: '#262626', 900: '#171717', 950: '#0a0a0a',
461
+ },
462
+ stone: {
463
+ 50: '#fafaf9', 100: '#f5f5f4', 200: '#e7e5e4', 300: '#d6d3d1', 400: '#a8a29e',
464
+ 500: '#78716c', 600: '#57534e', 700: '#44403c', 800: '#292524', 900: '#1c1917', 950: '#0c0a09',
465
+ },
466
+ white: { 500: '#ffffff' },
467
+ };
468
+ /** Resolve a hex value for any Tailwind color + shade, with a safe fallback. */
469
+ function resolvePaletteHex(color, shade) {
470
+ const entry = CLX_COLOR_PALETTE[color] ?? CLX_COLOR_PALETTE.indigo;
471
+ return entry[shade] ?? entry[500] ?? CLX_COLOR_PALETTE.indigo[500];
472
+ }
473
+
215
474
  const STORAGE_KEY = 'clx-theme-mode';
475
+ const FONT_LINK_ID = 'clx-google-font';
216
476
  class ClxThemeService {
217
477
  _isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
218
478
  _initial = inject(CLX_THEME_CONFIG);
@@ -229,6 +489,12 @@ class ClxThemeService {
229
489
  return false;
230
490
  return this._sysDark();
231
491
  }, ...(ngDevMode ? [{ debugName: "isDark" }] : /* istanbul ignore next */ []));
492
+ /** Color to apply to clx-nav-group labels — falls back to neutralColor */
493
+ sidebarGroupColor = computed(() => this._config().sidebarGroupColor ?? this._config().neutralColor, ...(ngDevMode ? [{ debugName: "sidebarGroupColor" }] : /* istanbul ignore next */ []));
494
+ /** Color to apply to top-level clx-menu — falls back to primaryColor */
495
+ sidebarMenuColor = computed(() => this._config().sidebarMenuColor ?? this._config().primaryColor, ...(ngDevMode ? [{ debugName: "sidebarMenuColor" }] : /* istanbul ignore next */ []));
496
+ /** Color to apply to clx-menu-item — falls back to primaryColor */
497
+ sidebarItemColor = computed(() => this._config().sidebarItemColor ?? this._config().primaryColor, ...(ngDevMode ? [{ debugName: "sidebarItemColor" }] : /* istanbul ignore next */ []));
232
498
  constructor() {
233
499
  effect(() => {
234
500
  if (!this._isBrowser)
@@ -238,14 +504,15 @@ class ClxThemeService {
238
504
  effect(() => {
239
505
  if (!this._isBrowser)
240
506
  return;
241
- const primaryColor = this._config().primaryColor;
242
- if (!primaryColor)
507
+ const cfg = this._config();
508
+ this._applyColorVar('--clx-primary', '--clx-primary-light', cfg.primaryColor);
509
+ this._applyColorVar('--clx-secondary', '--clx-secondary-light', cfg.secondaryColor);
510
+ this._applyTextColor(cfg.textColor, this.isDark());
511
+ });
512
+ effect(() => {
513
+ if (!this._isBrowser)
243
514
  return;
244
- const { color } = parseColorInput(primaryColor);
245
- const hex500 = CLX_COLOR_HEX[color] ?? CLX_COLOR_HEX.indigo;
246
- const hex100 = CLX_COLOR_HEX_100[color] ?? CLX_COLOR_HEX_100.indigo;
247
- document.documentElement.style.setProperty('--clx-primary', hex500);
248
- document.documentElement.style.setProperty('--clx-primary-light', hex100);
515
+ this._applyFont(this._config().fontFamily ?? 'Roboto');
249
516
  });
250
517
  if (this._isBrowser) {
251
518
  const mq = window.matchMedia('(prefers-color-scheme: dark)');
@@ -263,6 +530,42 @@ class ClxThemeService {
263
530
  toggle() {
264
531
  this.setMode(this.isDark() ? 'light' : 'dark');
265
532
  }
533
+ _applyColorVar(varName, lightVarName, input) {
534
+ if (!input)
535
+ return;
536
+ const { color, shade } = parseColorInput(input);
537
+ document.documentElement.style.setProperty(varName, resolvePaletteHex(color, shade));
538
+ if (lightVarName) {
539
+ document.documentElement.style.setProperty(lightVarName, resolvePaletteHex(color, 100));
540
+ }
541
+ }
542
+ _applyTextColor(input, dark) {
543
+ if (!input)
544
+ return;
545
+ const { color } = parseColorInput(input);
546
+ const shades = dark
547
+ ? { text: 100, label: 300, muted: 400, subtle: 500, faint: 600 }
548
+ : { text: 900, label: 600, muted: 500, subtle: 400, faint: 300 };
549
+ document.documentElement.style.setProperty('--clx-text', resolvePaletteHex(color, shades.text));
550
+ document.documentElement.style.setProperty('--clx-text-label', resolvePaletteHex(color, shades.label));
551
+ document.documentElement.style.setProperty('--clx-text-muted', resolvePaletteHex(color, shades.muted));
552
+ document.documentElement.style.setProperty('--clx-text-subtle', resolvePaletteHex(color, shades.subtle));
553
+ document.documentElement.style.setProperty('--clx-text-faint', resolvePaletteHex(color, shades.faint));
554
+ }
555
+ _applyFont(family) {
556
+ const option = CLX_FONT_CATALOG.find(f => f.value === family);
557
+ if (!option)
558
+ return;
559
+ let link = document.getElementById(FONT_LINK_ID);
560
+ if (!link) {
561
+ link = document.createElement('link');
562
+ link.id = FONT_LINK_ID;
563
+ link.rel = 'stylesheet';
564
+ document.head.appendChild(link);
565
+ }
566
+ link.href = option.googleUrl;
567
+ document.documentElement.style.setProperty('--clx-font-body', `'${family}', ui-sans-serif, system-ui, sans-serif`);
568
+ }
266
569
  _loadMode() {
267
570
  if (!this._isBrowser)
268
571
  return 'system';
@@ -690,7 +993,7 @@ const PRODUCT_TAG_VARIANT = {
690
993
  'hot': 'solid',
691
994
  };
692
995
 
693
- const BUTTON_BASE_CLASS = 'relative overflow-hidden inline-flex items-center justify-center font-medium select-none transition-all duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2';
996
+ const BUTTON_BASE_CLASS = 'relative overflow-hidden inline-flex items-center justify-center font-medium select-none transition-[color,background-color,border-color,box-shadow] duration-200 focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2';
694
997
  const BUTTON_SIZE_MAP = {
695
998
  xxs: 'text-xs px-1.5 py-0.5 gap-0.5',
696
999
  xs: 'text-xs px-2 py-1 gap-1',
@@ -1483,7 +1786,7 @@ class ClxProductComponent {
1483
1786
  _buttonSize = computed(() => this._sm() ? 'sm' : 'md', ...(ngDevMode ? [{ debugName: "_buttonSize" }] : /* istanbul ignore next */ []));
1484
1787
  _skeletonW = computed(() => this._sm() ? 'sm:w-32' : 'sm:w-44', ...(ngDevMode ? [{ debugName: "_skeletonW" }] : /* istanbul ignore next */ []));
1485
1788
  _skeletonH = computed(() => this._sm() ? '160px' : '200px', ...(ngDevMode ? [{ debugName: "_skeletonH" }] : /* istanbul ignore next */ []));
1486
- _cardClass = computed(() => `bg-clx-surface rounded-2xl shadow-sm transition-all duration-300 overflow-hidden group hover:shadow-xl hover:-translate-y-0.5 ${this._h() ? 'flex flex-col sm:flex-row' : 'flex flex-col'}`, ...(ngDevMode ? [{ debugName: "_cardClass" }] : /* istanbul ignore next */ []));
1789
+ _cardClass = computed(() => `bg-clx-surface rounded-2xl shadow-sm transition-[box-shadow,transform] duration-300 overflow-hidden group hover:shadow-xl hover:-translate-y-0.5 ${this._h() ? 'flex flex-col sm:flex-row' : 'flex flex-col'}`, ...(ngDevMode ? [{ debugName: "_cardClass" }] : /* istanbul ignore next */ []));
1487
1790
  _imageContainerClass = computed(() => this._h()
1488
1791
  ? `relative shrink-0 w-full aspect-square sm:aspect-auto ${this._sm() ? 'sm:w-32' : 'sm:w-44'} overflow-hidden rounded-t-2xl sm:rounded-t-none sm:rounded-l-2xl`
1489
1792
  : 'relative w-full aspect-square overflow-hidden rounded-t-2xl', ...(ngDevMode ? [{ debugName: "_imageContainerClass" }] : /* istanbul ignore next */ []));
@@ -2044,7 +2347,7 @@ class ClxCarouselComponent {
2044
2347
  @for (dot of _dotItems(); track dot.idx) {
2045
2348
  <button
2046
2349
  type="button"
2047
- class="rounded-full transition-all duration-300 focus:outline-none cursor-pointer"
2350
+ class="rounded-full transition-[width,background-color] duration-300 focus:outline-none cursor-pointer"
2048
2351
  [class]="dot.cls"
2049
2352
  (click)="goTo(dot.idx)"
2050
2353
  [attr.aria-label]="'Slide ' + (dot.idx + 1)">
@@ -2110,7 +2413,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
2110
2413
  @for (dot of _dotItems(); track dot.idx) {
2111
2414
  <button
2112
2415
  type="button"
2113
- class="rounded-full transition-all duration-300 focus:outline-none cursor-pointer"
2416
+ class="rounded-full transition-[width,background-color] duration-300 focus:outline-none cursor-pointer"
2114
2417
  [class]="dot.cls"
2115
2418
  (click)="goTo(dot.idx)"
2116
2419
  [attr.aria-label]="'Slide ' + (dot.idx + 1)">
@@ -2332,7 +2635,7 @@ class ClxNumberComponent {
2332
2635
  const focus = this._disabled() ? '' : resolveColor(this._color()).focusWithin;
2333
2636
  const border = this._disabled() ? `${CLX_BORDER_DISABLED} ${CLX_BG_DISABLED}` : CLX_BORDER_DEFAULT;
2334
2637
  const cursor = this._disabled() ? 'cursor-not-allowed' : '';
2335
- return `flex items-stretch ${size.container} bg-clx-surface ${resolveRadius(this._themeSvc.config().borderRadius)} border ${border} ${focus} ${cursor} overflow-hidden transition-all duration-200`;
2638
+ return `flex items-stretch ${size.container} bg-clx-surface ${resolveRadius(this._themeSvc.config().borderRadius)} border ${border} ${focus} ${cursor} overflow-hidden transition-[border-color,box-shadow] duration-200`;
2336
2639
  }, ...(ngDevMode ? [{ debugName: "_containerClass" }] : /* istanbul ignore next */ []));
2337
2640
  /** Colored prefix icon cell — matches the input accent color */
2338
2641
  _prefixCellCls = computed(() => {
@@ -2635,7 +2938,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
2635
2938
  const CLX_LIST_CONTEXT = new InjectionToken('CLX_LIST_CONTEXT');
2636
2939
  // ── Variant maps ──────────────────────────────────────────────────────────────
2637
2940
  const LIST_VARIANT_SHAPE = {
2638
- bordered: 'bg-white rounded-2xl overflow-hidden',
2941
+ bordered: 'bg-clx-surface rounded-2xl overflow-hidden border border-clx-border',
2639
2942
  flat: 'bg-transparent',
2640
2943
  flush: 'bg-transparent',
2641
2944
  };
@@ -2872,7 +3175,7 @@ class ClxProductDetailComponent {
2872
3175
  specsLeft(specs) { return specs.slice(0, Math.ceil(specs.length / 2)); }
2873
3176
  specsRight(specs) { return specs.slice(Math.ceil(specs.length / 2)); }
2874
3177
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxProductDetailComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
2875
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxProductDetailComponent, isStandalone: true, selector: "clx-product-detail", inputs: { product: { classPropertyName: "product", publicName: "product", isSignal: true, isRequired: false, transformFunction: null }, clxColor: { classPropertyName: "clxColor", publicName: "clxColor", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, currency: { classPropertyName: "currency", publicName: "currency", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "_carousel", first: true, predicate: ClxCarouselComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<!-- \u2550\u2550 SKELETON \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (loading()) {\n <div class=\"grid grid-cols-1 xl:grid-cols-2 gap-8 p-6\">\n <div class=\"space-y-3\">\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"420px\" class=\"rounded-2xl\"></clx-skeleton>\n <div class=\"flex gap-2\">\n @for (t of [1,2,3,4]; track t) {\n <clx-skeleton variant=\"rectangular\" width=\"80px\" height=\"80px\" class=\"rounded-xl\"></clx-skeleton>\n }\n </div>\n </div>\n <div class=\"space-y-4 pt-2\">\n <clx-skeleton variant=\"text\" size=\"lg\" width=\"70%\"></clx-skeleton>\n <clx-skeleton variant=\"text\" size=\"sm\" width=\"40%\"></clx-skeleton>\n <clx-skeleton variant=\"text\" size=\"xl\" width=\"50%\"></clx-skeleton>\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"100px\" class=\"rounded-xl\"></clx-skeleton>\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"52px\" class=\"rounded-xl\"></clx-skeleton>\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"52px\" class=\"rounded-xl\"></clx-skeleton>\n </div>\n </div>\n}\n\n<!-- \u2550\u2550 CONTENT \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (!loading() && product(); as p) {\n @let v = activeVariation();\n <div class=\"space-y-8\">\n\n <div class=\"grid grid-cols-1 xl:grid-cols-2 gap-8 xl:items-start\">\n\n <!-- \u2500\u2500 Column 1: Image Gallery \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"space-y-3\">\n <div class=\"relative\">\n <clx-carousel [clxColor]=\"clxColor()\" aspectRatio=\"4/3\" [loop]=\"true\">\n @for (img of v?.images ?? []; track img) {\n <ng-template clxSlide>\n <img [src]=\"img\" [alt]=\"p.product_name\" class=\"w-full h-full object-cover\" loading=\"lazy\" />\n </ng-template>\n }\n </clx-carousel>\n <div class=\"absolute top-3 right-3 z-10\">\n <button clx-button variant=\"solid\" [color]=\"clxColor()\" shape=\"circle\" size=\"md\"\n [iconOnly]=\"true\" clxTooltip=\"Add to Wishlist\" [clxTooltipColor]=\"clxColor()\" clxTooltipPosition=\"left\">\n <span clx-icon name=\"favorite_border\" size=\"sm\"></span>\n </button>\n </div>\n </div>\n <div class=\"flex flex-wrap gap-2\">\n @for (img of v?.images ?? []; track img; let i = $index) {\n <img [src]=\"img\" [alt]=\"p.product_name\"\n class=\"w-20 h-20 rounded-xl object-cover border-[3px] cursor-pointer shrink-0 transition-all\"\n [class]=\"_carousel()?.currentPageIndex() === i\n ? 'border-' + clxColor() + '-500 opacity-100'\n : 'border-transparent opacity-60 hover:opacity-90'\"\n loading=\"lazy\"\n (click)=\"_carousel()?.goTo(i)\" />\n }\n </div>\n </div>\n\n <!-- \u2500\u2500 Column 2: Purchase Info \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"space-y-5\">\n\n <!-- Tags (st_products.tags) -->\n <div class=\"flex flex-wrap gap-2\">\n @for (tag of p.tags; track tag) {\n <span clx-badge variant=\"light\" [color]=\"clxColor()\" size=\"sm\" shape=\"pill\">{{ tag }}</span>\n }\n </div>\n\n <!-- Title -->\n <div>\n <p class=\"text-xs text-clx-text-subtle uppercase tracking-wide font-medium\">{{ p.trademark }}</p>\n <h1 class=\"text-2xl font-bold text-clx-text-label leading-tight mt-0.5\">{{ p.product_name }}</h1>\n @if (v?.description) {\n <p class=\"text-clx-text-muted text-sm mt-1\">{{ v!.description }}</p>\n }\n </div>\n\n <!-- Rating -->\n <div class=\"flex items-center gap-2\">\n <clx-rating [value]=\"p.rating\" [color]=\"clxColor()\"></clx-rating>\n <span class=\"text-sm font-semibold text-clx-text-label\">{{ p.rating | number:'1.1-1' }}</span>\n <span class=\"text-sm text-clx-text-subtle\">({{ p.review_count }} reviews)</span>\n </div>\n\n <!-- Price block -->\n <div class=\"py-4\">\n <div class=\"flex items-baseline gap-3\">\n <span class=\"text-4xl font-black text-clx-text-label\">\n {{ finalPrice() | currency:currency():'symbol':'1.0-0' }}\n </span>\n @if (p.has_discount && p.discount_price) {\n <span class=\"text-lg text-clx-text-subtle line-through\">\n {{ p.discount_price | currency:currency():'symbol':'1.0-0' }}\n </span>\n <span clx-badge variant=\"solid\" [color]=\"clxColor()\" size=\"sm\" shape=\"pill\">\n -{{ discount() }}%\n </span>\n }\n </div>\n @if (v && !v.in_stock) {\n <p class=\"text-sm text-rose-500 font-medium mt-1\">Out of stock</p>\n }\n </div>\n\n <!-- Selectores de variaci\u00F3n (st_spec_types + st_spec_options) -->\n @for (specType of p.spec_types; track specType.id) {\n @if (specType.input_type === 'color') {\n <clx-button-group [label]=\"specType.name\" shape=\"circular\" [attached]=\"false\">\n @for (opt of specType.options; track opt.id) {\n <button clx-button\n [variant]=\"isSpecSelected(specType.id, opt.id) ? 'solid' : 'outline'\"\n [color]=\"asColor(opt.value)\"\n shape=\"circle\" size=\"xs\" [iconOnly]=\"true\"\n [disabled]=\"!isOptionAvailable(specType.id, opt.id)\"\n [clxTooltip]=\"opt.value\"\n [clxTooltipColor]=\"asColor(opt.value)\"\n clxTooltipPosition=\"top\"\n (click)=\"selectSpec(specType.id, opt.id)\">\n </button>\n }\n </clx-button-group>\n } @else {\n <clx-button-group [label]=\"specType.name\" shape=\"flat\" [attached]=\"false\">\n @for (opt of specType.options; track opt.id) {\n <button clx-button\n [variant]=\"isSpecSelected(specType.id, opt.id) ? 'solid' : 'outline'\"\n [color]=\"clxColor()\" size=\"sm\"\n [disabled]=\"!isOptionAvailable(specType.id, opt.id)\"\n (click)=\"selectSpec(specType.id, opt.id)\">\n {{ opt.value }}\n </button>\n }\n </clx-button-group>\n }\n }\n\n <!-- Quantity -->\n <clx-number label=\"Quantity:\" variant=\"stepper\" [color]=\"clxColor()\"\n [min]=\"1\" [max]=\"99\" [ngModel]=\"quantity()\" (ngModelChange)=\"quantity.set($event)\">\n </clx-number>\n\n <!-- CTA -->\n <div class=\"flex gap-3\">\n <button clx-button variant=\"solid\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"bolt\"\n [disabled]=\"!v?.in_stock\">Buy Now</button>\n <button clx-button variant=\"light\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"shopping_cart\"\n [disabled]=\"!v?.in_stock\">Add to Cart</button>\n </div>\n\n </div>\n </div>\n\n <!-- \u2500\u2500 Tabs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <clx-tabs [tabs]=\"tabItems\" [clxColor]=\"clxColor()\" [(activeTab)]=\"activeTab\">\n\n <!-- Tab: Caracter\u00EDsticas generales (st_product_specs \u2014 ficha t\u00E9cnica global) -->\n <ng-template [clxTabPanel]=\"'general'\">\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-x-8\">\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (spec of specsLeft(p.product_specs); track spec.id) {\n <clx-list-item [label]=\"spec.name + ':'\" [sublabel]=\"spec.value\" [icon]=\"spec.icon ?? ''\" />\n }\n </clx-list>\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (spec of specsRight(p.product_specs); track spec.id) {\n <clx-list-item [label]=\"spec.name + ':'\" [sublabel]=\"spec.value\" [icon]=\"spec.icon ?? ''\" />\n }\n </clx-list>\n </div>\n </ng-template>\n\n <!-- Tab: Especificaciones (st_spec_types \u2014 specs de la variaci\u00F3n activa) -->\n <ng-template [clxTabPanel]=\"'specs'\">\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-x-8\">\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (st of specsLeft(p.spec_types); track st.id) {\n <clx-list-item [label]=\"st.name + ':'\" [sublabel]=\"selectedOptionLabel(st)\" [icon]=\"st.icon ?? ''\" />\n }\n </clx-list>\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (st of specsRight(p.spec_types); track st.id) {\n <clx-list-item [label]=\"st.name + ':'\" [sublabel]=\"selectedOptionLabel(st)\" [icon]=\"st.icon ?? ''\" />\n }\n </clx-list>\n </div>\n </ng-template>\n\n <!-- Tab: Descripci\u00F3n (st_product_descriptions) -->\n <ng-template [clxTabPanel]=\"'description'\">\n <div class=\"space-y-5 max-w-3xl\">\n @for (block of p.descriptions; track block.id) {\n <div>\n @if (block.title) {\n <h3 class=\"text-base font-semibold text-clx-text-label mb-1\">{{ block.title }}</h3>\n }\n <p class=\"text-sm text-clx-text-muted leading-relaxed\">{{ block.body }}</p>\n </div>\n }\n </div>\n </ng-template>\n\n </clx-tabs>\n\n <!-- \u2500\u2500 Reviews \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"grid grid-cols-1 xl:grid-cols-3 gap-8 border-t border-clx-border pt-8\">\n\n <div class=\"space-y-3\">\n <h2 class=\"text-base font-bold text-clx-text-label\">Customer Reviews</h2>\n <div class=\"flex items-end gap-3\">\n <span class=\"text-5xl font-black text-clx-text-label\">{{ p.rating | number:'1.1-1' }}</span>\n <div class=\"pb-1 space-y-1\">\n <clx-rating [value]=\"p.rating\" [color]=\"clxColor()\"></clx-rating>\n <p class=\"text-xs text-clx-text-muted\">{{ p.review_count }} verified ratings</p>\n </div>\n </div>\n <p class=\"text-sm font-semibold\" [class]=\"'text-' + clxColor() + '-600'\">Very Good</p>\n <div class=\"pt-2 space-y-2\">\n <button clx-button variant=\"solid\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"rate_review\">\n Leave Us a Review\n </button>\n <button clx-button variant=\"light\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"flag\">\n Report abuse\n </button>\n </div>\n </div>\n\n <div class=\"lg:col-span-2 space-y-4\">\n @for (review of p.reviews ?? []; track review.id) {\n <div class=\"rounded-2xl overflow-hidden shadow-sm\">\n <div class=\"flex items-center gap-3 px-4 py-3\">\n <div class=\"w-10 h-10 rounded-full bg-clx-surface-2 flex items-center justify-center shrink-0\">\n <span class=\"text-sm font-bold text-clx-text-muted\">\n {{ review.author_name.substring(0,1) }}{{ review.author_name.split(' ')[1]?.substring(0,1) ?? '' }}\n </span>\n </div>\n <div class=\"flex-1 min-w-0\">\n <p class=\"font-semibold text-sm text-clx-text-label truncate\">{{ review.author_name }}</p>\n <p class=\"text-xs text-clx-text-subtle\">{{ review.created_at }}</p>\n </div>\n @if (review.is_verified) {\n <span clx-badge variant=\"light\" color=\"emerald\" size=\"sm\" shape=\"pill\">Verified</span>\n }\n </div>\n <div class=\"px-4 pb-4 space-y-2\">\n <div class=\"flex items-center gap-2\">\n <clx-rating [value]=\"review.rating\" [color]=\"clxColor()\"></clx-rating>\n @if (review.title) {\n <span class=\"text-sm font-semibold text-clx-text-label\">{{ review.title }}</span>\n }\n </div>\n @if (review.body) {\n <p class=\"text-sm text-clx-text-muted\">{{ review.body }}</p>\n }\n <div class=\"flex items-center gap-3 pt-1\">\n <span class=\"text-xs text-clx-text-subtle\">Helpful?</span>\n <button clx-button variant=\"ghost\" color=\"slate\" size=\"xs\" icon=\"thumb_up\">{{ review.helpful }}</button>\n <button clx-button variant=\"ghost\" color=\"slate\" size=\"xs\" icon=\"thumb_down\">{{ review.not_helpful }}</button>\n </div>\n </div>\n </div>\n }\n </div>\n\n </div>\n\n </div>\n}\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ClxButtonComponent, selector: "button[clx-button], a[clx-button]", inputs: ["variant", "color", "size", "shape", "loading", "disabled", "block", "icon", "iconPosition", "iconOnly", "badge", "badgeColor"] }, { kind: "component", type: ClxButtonGroupComponent, selector: "clx-button-group", inputs: ["label", "shape", "attached", "orientation"] }, { kind: "component", type: ClxBadgeComponent, selector: "span[clx-badge]", inputs: ["variant", "color", "size", "shape", "positioned"] }, { kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }, { kind: "component", type: ClxSkeletonComponent, selector: "clx-skeleton", inputs: ["variant", "size", "width", "height"] }, { kind: "component", type: ClxRatingComponent, selector: "clx-rating", inputs: ["value", "max", "color"] }, { kind: "component", type: ClxCarouselComponent, selector: "clx-carousel", inputs: ["clxColor", "autoPlay", "interval", "loop", "aspectRatio", "transparent"] }, { kind: "directive", type: ClxCarouselDirective, selector: "[clxSlide]" }, { kind: "component", type: ClxTabsComponent, selector: "clx-tabs", inputs: ["tabs", "clxColor", "activeTab"], outputs: ["activeTabChange"] }, { kind: "directive", type: ClxTabDirective, selector: "[clxTabPanel]", inputs: ["clxTabPanel"] }, { kind: "component", type: ClxNumberComponent, selector: "clx-number", inputs: ["variant", "size", "color", "label", "placeholder", "hint", "prefixIcon", "min", "max", "step", "value", "disabled"] }, { kind: "directive", type: ClxTooltipDirective, selector: "[clxTooltip]", inputs: ["clxTooltip", "clxTooltipPosition", "clxTooltipColor", "clxTooltipSize", "clxTooltipDelay"] }, { kind: "component", type: ClxListComponent, selector: "clx-list", inputs: ["color", "variant", "size", "numbered"] }, { kind: "component", type: ClxListItemComponent, selector: "clx-list-item", inputs: ["label", "sublabel", "icon", "meta", "metaColor", "disabled"] }, { kind: "pipe", type: CurrencyPipe, name: "currency" }, { kind: "pipe", type: DecimalPipe, name: "number" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
3178
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxProductDetailComponent, isStandalone: true, selector: "clx-product-detail", inputs: { product: { classPropertyName: "product", publicName: "product", isSignal: true, isRequired: false, transformFunction: null }, clxColor: { classPropertyName: "clxColor", publicName: "clxColor", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, currency: { classPropertyName: "currency", publicName: "currency", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "_carousel", first: true, predicate: ClxCarouselComponent, descendants: true, isSignal: true }], ngImport: i0, template: "<!-- \u2550\u2550 SKELETON \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (loading()) {\n <div class=\"grid grid-cols-1 xl:grid-cols-2 gap-8 p-6\">\n <div class=\"space-y-3\">\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"420px\" class=\"rounded-2xl\"></clx-skeleton>\n <div class=\"flex gap-2\">\n @for (t of [1,2,3,4]; track t) {\n <clx-skeleton variant=\"rectangular\" width=\"80px\" height=\"80px\" class=\"rounded-xl\"></clx-skeleton>\n }\n </div>\n </div>\n <div class=\"space-y-4 pt-2\">\n <clx-skeleton variant=\"text\" size=\"lg\" width=\"70%\"></clx-skeleton>\n <clx-skeleton variant=\"text\" size=\"sm\" width=\"40%\"></clx-skeleton>\n <clx-skeleton variant=\"text\" size=\"xl\" width=\"50%\"></clx-skeleton>\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"100px\" class=\"rounded-xl\"></clx-skeleton>\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"52px\" class=\"rounded-xl\"></clx-skeleton>\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"52px\" class=\"rounded-xl\"></clx-skeleton>\n </div>\n </div>\n}\n\n<!-- \u2550\u2550 CONTENT \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (!loading() && product(); as p) {\n @let v = activeVariation();\n <div class=\"space-y-8\">\n\n <div class=\"grid grid-cols-1 xl:grid-cols-2 gap-8 xl:items-start\">\n\n <!-- \u2500\u2500 Column 1: Image Gallery \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"space-y-3\">\n <div class=\"relative\">\n <clx-carousel [clxColor]=\"clxColor()\" aspectRatio=\"4/3\" [loop]=\"true\">\n @for (img of v?.images ?? []; track img) {\n <ng-template clxSlide>\n <img [src]=\"img\" [alt]=\"p.product_name\" class=\"w-full h-full object-cover\" loading=\"lazy\" />\n </ng-template>\n }\n </clx-carousel>\n <div class=\"absolute top-3 right-3 z-10\">\n <button clx-button variant=\"solid\" [color]=\"clxColor()\" shape=\"circle\" size=\"md\"\n [iconOnly]=\"true\" clxTooltip=\"Add to Wishlist\" [clxTooltipColor]=\"clxColor()\" clxTooltipPosition=\"left\">\n <span clx-icon name=\"favorite_border\" size=\"sm\"></span>\n </button>\n </div>\n </div>\n <div class=\"flex flex-wrap gap-2\">\n @for (img of v?.images ?? []; track img; let i = $index) {\n <img [src]=\"img\" [alt]=\"p.product_name\"\n class=\"w-20 h-20 rounded-xl object-cover border-[3px] cursor-pointer shrink-0 transition-[border-color,opacity]\"\n [class]=\"_carousel()?.currentPageIndex() === i\n ? 'border-' + clxColor() + '-500 opacity-100'\n : 'border-transparent opacity-60 hover:opacity-90'\"\n loading=\"lazy\"\n (click)=\"_carousel()?.goTo(i)\" />\n }\n </div>\n </div>\n\n <!-- \u2500\u2500 Column 2: Purchase Info \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"space-y-5\">\n\n <!-- Tags (st_products.tags) -->\n <div class=\"flex flex-wrap gap-2\">\n @for (tag of p.tags; track tag) {\n <span clx-badge variant=\"light\" [color]=\"clxColor()\" size=\"sm\" shape=\"pill\">{{ tag }}</span>\n }\n </div>\n\n <!-- Title -->\n <div>\n <p class=\"text-xs text-clx-text-subtle uppercase tracking-wide font-medium\">{{ p.trademark }}</p>\n <h1 class=\"text-2xl font-bold text-clx-text-label leading-tight mt-0.5\">{{ p.product_name }}</h1>\n @if (v?.description) {\n <p class=\"text-clx-text-muted text-sm mt-1\">{{ v!.description }}</p>\n }\n </div>\n\n <!-- Rating -->\n <div class=\"flex items-center gap-2\">\n <clx-rating [value]=\"p.rating\" [color]=\"clxColor()\"></clx-rating>\n <span class=\"text-sm font-semibold text-clx-text-label\">{{ p.rating | number:'1.1-1' }}</span>\n <span class=\"text-sm text-clx-text-subtle\">({{ p.review_count }} reviews)</span>\n </div>\n\n <!-- Price block -->\n <div class=\"py-4\">\n <div class=\"flex items-baseline gap-3\">\n <span class=\"text-4xl font-black text-clx-text-label\">\n {{ finalPrice() | currency:currency():'symbol':'1.0-0' }}\n </span>\n @if (p.has_discount && p.discount_price) {\n <span class=\"text-lg text-clx-text-subtle line-through\">\n {{ p.discount_price | currency:currency():'symbol':'1.0-0' }}\n </span>\n <span clx-badge variant=\"solid\" [color]=\"clxColor()\" size=\"sm\" shape=\"pill\">\n -{{ discount() }}%\n </span>\n }\n </div>\n @if (v && !v.in_stock) {\n <p class=\"text-sm text-rose-500 font-medium mt-1\">Out of stock</p>\n }\n </div>\n\n <!-- Selectores de variaci\u00F3n (st_spec_types + st_spec_options) -->\n @for (specType of p.spec_types; track specType.id) {\n @if (specType.input_type === 'color') {\n <clx-button-group [label]=\"specType.name\" shape=\"circular\" [attached]=\"false\">\n @for (opt of specType.options; track opt.id) {\n <button clx-button\n [variant]=\"isSpecSelected(specType.id, opt.id) ? 'solid' : 'outline'\"\n [color]=\"asColor(opt.value)\"\n shape=\"circle\" size=\"xs\" [iconOnly]=\"true\"\n [disabled]=\"!isOptionAvailable(specType.id, opt.id)\"\n [clxTooltip]=\"opt.value\"\n [clxTooltipColor]=\"asColor(opt.value)\"\n clxTooltipPosition=\"top\"\n (click)=\"selectSpec(specType.id, opt.id)\">\n </button>\n }\n </clx-button-group>\n } @else {\n <clx-button-group [label]=\"specType.name\" shape=\"flat\" [attached]=\"false\">\n @for (opt of specType.options; track opt.id) {\n <button clx-button\n [variant]=\"isSpecSelected(specType.id, opt.id) ? 'solid' : 'outline'\"\n [color]=\"clxColor()\" size=\"sm\"\n [disabled]=\"!isOptionAvailable(specType.id, opt.id)\"\n (click)=\"selectSpec(specType.id, opt.id)\">\n {{ opt.value }}\n </button>\n }\n </clx-button-group>\n }\n }\n\n <!-- Quantity -->\n <clx-number label=\"Quantity:\" variant=\"stepper\" [color]=\"clxColor()\"\n [min]=\"1\" [max]=\"99\" [ngModel]=\"quantity()\" (ngModelChange)=\"quantity.set($event)\">\n </clx-number>\n\n <!-- CTA -->\n <div class=\"flex gap-3\">\n <button clx-button variant=\"solid\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"bolt\"\n [disabled]=\"!v?.in_stock\">Buy Now</button>\n <button clx-button variant=\"light\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"shopping_cart\"\n [disabled]=\"!v?.in_stock\">Add to Cart</button>\n </div>\n\n </div>\n </div>\n\n <!-- \u2500\u2500 Tabs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <clx-tabs [tabs]=\"tabItems\" [clxColor]=\"clxColor()\" [(activeTab)]=\"activeTab\">\n\n <!-- Tab: Caracter\u00EDsticas generales (st_product_specs \u2014 ficha t\u00E9cnica global) -->\n <ng-template [clxTabPanel]=\"'general'\">\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-x-8\">\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (spec of specsLeft(p.product_specs); track spec.id) {\n <clx-list-item [label]=\"spec.name + ':'\" [sublabel]=\"spec.value\" [icon]=\"spec.icon ?? ''\" />\n }\n </clx-list>\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (spec of specsRight(p.product_specs); track spec.id) {\n <clx-list-item [label]=\"spec.name + ':'\" [sublabel]=\"spec.value\" [icon]=\"spec.icon ?? ''\" />\n }\n </clx-list>\n </div>\n </ng-template>\n\n <!-- Tab: Especificaciones (st_spec_types \u2014 specs de la variaci\u00F3n activa) -->\n <ng-template [clxTabPanel]=\"'specs'\">\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-x-8\">\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (st of specsLeft(p.spec_types); track st.id) {\n <clx-list-item [label]=\"st.name + ':'\" [sublabel]=\"selectedOptionLabel(st)\" [icon]=\"st.icon ?? ''\" />\n }\n </clx-list>\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (st of specsRight(p.spec_types); track st.id) {\n <clx-list-item [label]=\"st.name + ':'\" [sublabel]=\"selectedOptionLabel(st)\" [icon]=\"st.icon ?? ''\" />\n }\n </clx-list>\n </div>\n </ng-template>\n\n <!-- Tab: Descripci\u00F3n (st_product_descriptions) -->\n <ng-template [clxTabPanel]=\"'description'\">\n <div class=\"space-y-5 max-w-3xl\">\n @for (block of p.descriptions; track block.id) {\n <div>\n @if (block.title) {\n <h3 class=\"text-base font-semibold text-clx-text-label mb-1\">{{ block.title }}</h3>\n }\n <p class=\"text-sm text-clx-text-muted leading-relaxed\">{{ block.body }}</p>\n </div>\n }\n </div>\n </ng-template>\n\n </clx-tabs>\n\n <!-- \u2500\u2500 Reviews \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"grid grid-cols-1 xl:grid-cols-3 gap-8 border-t border-clx-border pt-8\">\n\n <div class=\"space-y-3\">\n <h2 class=\"text-base font-bold text-clx-text-label\">Customer Reviews</h2>\n <div class=\"flex items-end gap-3\">\n <span class=\"text-5xl font-black text-clx-text-label\">{{ p.rating | number:'1.1-1' }}</span>\n <div class=\"pb-1 space-y-1\">\n <clx-rating [value]=\"p.rating\" [color]=\"clxColor()\"></clx-rating>\n <p class=\"text-xs text-clx-text-muted\">{{ p.review_count }} verified ratings</p>\n </div>\n </div>\n <p class=\"text-sm font-semibold\" [class]=\"'text-' + clxColor() + '-600'\">Very Good</p>\n <div class=\"pt-2 space-y-2\">\n <button clx-button variant=\"solid\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"rate_review\">\n Leave Us a Review\n </button>\n <button clx-button variant=\"light\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"flag\">\n Report abuse\n </button>\n </div>\n </div>\n\n <div class=\"lg:col-span-2 space-y-4\">\n @for (review of p.reviews ?? []; track review.id) {\n <div class=\"rounded-2xl overflow-hidden shadow-sm\">\n <div class=\"flex items-center gap-3 px-4 py-3\">\n <div class=\"w-10 h-10 rounded-full bg-clx-surface-2 flex items-center justify-center shrink-0\">\n <span class=\"text-sm font-bold text-clx-text-muted\">\n {{ review.author_name.substring(0,1) }}{{ review.author_name.split(' ')[1]?.substring(0,1) ?? '' }}\n </span>\n </div>\n <div class=\"flex-1 min-w-0\">\n <p class=\"font-semibold text-sm text-clx-text-label truncate\">{{ review.author_name }}</p>\n <p class=\"text-xs text-clx-text-subtle\">{{ review.created_at }}</p>\n </div>\n @if (review.is_verified) {\n <span clx-badge variant=\"light\" color=\"emerald\" size=\"sm\" shape=\"pill\">Verified</span>\n }\n </div>\n <div class=\"px-4 pb-4 space-y-2\">\n <div class=\"flex items-center gap-2\">\n <clx-rating [value]=\"review.rating\" [color]=\"clxColor()\"></clx-rating>\n @if (review.title) {\n <span class=\"text-sm font-semibold text-clx-text-label\">{{ review.title }}</span>\n }\n </div>\n @if (review.body) {\n <p class=\"text-sm text-clx-text-muted\">{{ review.body }}</p>\n }\n <div class=\"flex items-center gap-3 pt-1\">\n <span class=\"text-xs text-clx-text-subtle\">Helpful?</span>\n <button clx-button variant=\"ghost\" color=\"slate\" size=\"xs\" icon=\"thumb_up\">{{ review.helpful }}</button>\n <button clx-button variant=\"ghost\" color=\"slate\" size=\"xs\" icon=\"thumb_down\">{{ review.not_helpful }}</button>\n </div>\n </div>\n </div>\n }\n </div>\n\n </div>\n\n </div>\n}\n", dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: ClxButtonComponent, selector: "button[clx-button], a[clx-button]", inputs: ["variant", "color", "size", "shape", "loading", "disabled", "block", "icon", "iconPosition", "iconOnly", "badge", "badgeColor"] }, { kind: "component", type: ClxButtonGroupComponent, selector: "clx-button-group", inputs: ["label", "shape", "attached", "orientation"] }, { kind: "component", type: ClxBadgeComponent, selector: "span[clx-badge]", inputs: ["variant", "color", "size", "shape", "positioned"] }, { kind: "component", type: ClxIconComponent, selector: "span[clx-icon]", inputs: ["name", "size", "color", "fill"] }, { kind: "component", type: ClxSkeletonComponent, selector: "clx-skeleton", inputs: ["variant", "size", "width", "height"] }, { kind: "component", type: ClxRatingComponent, selector: "clx-rating", inputs: ["value", "max", "color"] }, { kind: "component", type: ClxCarouselComponent, selector: "clx-carousel", inputs: ["clxColor", "autoPlay", "interval", "loop", "aspectRatio", "transparent"] }, { kind: "directive", type: ClxCarouselDirective, selector: "[clxSlide]" }, { kind: "component", type: ClxTabsComponent, selector: "clx-tabs", inputs: ["tabs", "clxColor", "activeTab"], outputs: ["activeTabChange"] }, { kind: "directive", type: ClxTabDirective, selector: "[clxTabPanel]", inputs: ["clxTabPanel"] }, { kind: "component", type: ClxNumberComponent, selector: "clx-number", inputs: ["variant", "size", "color", "label", "placeholder", "hint", "prefixIcon", "min", "max", "step", "value", "disabled"] }, { kind: "directive", type: ClxTooltipDirective, selector: "[clxTooltip]", inputs: ["clxTooltip", "clxTooltipPosition", "clxTooltipColor", "clxTooltipSize", "clxTooltipDelay"] }, { kind: "component", type: ClxListComponent, selector: "clx-list", inputs: ["color", "variant", "size", "numbered"] }, { kind: "component", type: ClxListItemComponent, selector: "clx-list-item", inputs: ["label", "sublabel", "icon", "meta", "metaColor", "disabled"] }, { kind: "pipe", type: CurrencyPipe, name: "currency" }, { kind: "pipe", type: DecimalPipe, name: "number" }], changeDetection: i0.ChangeDetectionStrategy.OnPush, encapsulation: i0.ViewEncapsulation.None });
2876
3179
  }
2877
3180
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxProductDetailComponent, decorators: [{
2878
3181
  type: Component,
@@ -2894,7 +3197,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
2894
3197
  ClxTooltipDirective,
2895
3198
  ClxListComponent,
2896
3199
  ClxListItemComponent,
2897
- ], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- \u2550\u2550 SKELETON \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (loading()) {\n <div class=\"grid grid-cols-1 xl:grid-cols-2 gap-8 p-6\">\n <div class=\"space-y-3\">\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"420px\" class=\"rounded-2xl\"></clx-skeleton>\n <div class=\"flex gap-2\">\n @for (t of [1,2,3,4]; track t) {\n <clx-skeleton variant=\"rectangular\" width=\"80px\" height=\"80px\" class=\"rounded-xl\"></clx-skeleton>\n }\n </div>\n </div>\n <div class=\"space-y-4 pt-2\">\n <clx-skeleton variant=\"text\" size=\"lg\" width=\"70%\"></clx-skeleton>\n <clx-skeleton variant=\"text\" size=\"sm\" width=\"40%\"></clx-skeleton>\n <clx-skeleton variant=\"text\" size=\"xl\" width=\"50%\"></clx-skeleton>\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"100px\" class=\"rounded-xl\"></clx-skeleton>\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"52px\" class=\"rounded-xl\"></clx-skeleton>\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"52px\" class=\"rounded-xl\"></clx-skeleton>\n </div>\n </div>\n}\n\n<!-- \u2550\u2550 CONTENT \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (!loading() && product(); as p) {\n @let v = activeVariation();\n <div class=\"space-y-8\">\n\n <div class=\"grid grid-cols-1 xl:grid-cols-2 gap-8 xl:items-start\">\n\n <!-- \u2500\u2500 Column 1: Image Gallery \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"space-y-3\">\n <div class=\"relative\">\n <clx-carousel [clxColor]=\"clxColor()\" aspectRatio=\"4/3\" [loop]=\"true\">\n @for (img of v?.images ?? []; track img) {\n <ng-template clxSlide>\n <img [src]=\"img\" [alt]=\"p.product_name\" class=\"w-full h-full object-cover\" loading=\"lazy\" />\n </ng-template>\n }\n </clx-carousel>\n <div class=\"absolute top-3 right-3 z-10\">\n <button clx-button variant=\"solid\" [color]=\"clxColor()\" shape=\"circle\" size=\"md\"\n [iconOnly]=\"true\" clxTooltip=\"Add to Wishlist\" [clxTooltipColor]=\"clxColor()\" clxTooltipPosition=\"left\">\n <span clx-icon name=\"favorite_border\" size=\"sm\"></span>\n </button>\n </div>\n </div>\n <div class=\"flex flex-wrap gap-2\">\n @for (img of v?.images ?? []; track img; let i = $index) {\n <img [src]=\"img\" [alt]=\"p.product_name\"\n class=\"w-20 h-20 rounded-xl object-cover border-[3px] cursor-pointer shrink-0 transition-all\"\n [class]=\"_carousel()?.currentPageIndex() === i\n ? 'border-' + clxColor() + '-500 opacity-100'\n : 'border-transparent opacity-60 hover:opacity-90'\"\n loading=\"lazy\"\n (click)=\"_carousel()?.goTo(i)\" />\n }\n </div>\n </div>\n\n <!-- \u2500\u2500 Column 2: Purchase Info \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"space-y-5\">\n\n <!-- Tags (st_products.tags) -->\n <div class=\"flex flex-wrap gap-2\">\n @for (tag of p.tags; track tag) {\n <span clx-badge variant=\"light\" [color]=\"clxColor()\" size=\"sm\" shape=\"pill\">{{ tag }}</span>\n }\n </div>\n\n <!-- Title -->\n <div>\n <p class=\"text-xs text-clx-text-subtle uppercase tracking-wide font-medium\">{{ p.trademark }}</p>\n <h1 class=\"text-2xl font-bold text-clx-text-label leading-tight mt-0.5\">{{ p.product_name }}</h1>\n @if (v?.description) {\n <p class=\"text-clx-text-muted text-sm mt-1\">{{ v!.description }}</p>\n }\n </div>\n\n <!-- Rating -->\n <div class=\"flex items-center gap-2\">\n <clx-rating [value]=\"p.rating\" [color]=\"clxColor()\"></clx-rating>\n <span class=\"text-sm font-semibold text-clx-text-label\">{{ p.rating | number:'1.1-1' }}</span>\n <span class=\"text-sm text-clx-text-subtle\">({{ p.review_count }} reviews)</span>\n </div>\n\n <!-- Price block -->\n <div class=\"py-4\">\n <div class=\"flex items-baseline gap-3\">\n <span class=\"text-4xl font-black text-clx-text-label\">\n {{ finalPrice() | currency:currency():'symbol':'1.0-0' }}\n </span>\n @if (p.has_discount && p.discount_price) {\n <span class=\"text-lg text-clx-text-subtle line-through\">\n {{ p.discount_price | currency:currency():'symbol':'1.0-0' }}\n </span>\n <span clx-badge variant=\"solid\" [color]=\"clxColor()\" size=\"sm\" shape=\"pill\">\n -{{ discount() }}%\n </span>\n }\n </div>\n @if (v && !v.in_stock) {\n <p class=\"text-sm text-rose-500 font-medium mt-1\">Out of stock</p>\n }\n </div>\n\n <!-- Selectores de variaci\u00F3n (st_spec_types + st_spec_options) -->\n @for (specType of p.spec_types; track specType.id) {\n @if (specType.input_type === 'color') {\n <clx-button-group [label]=\"specType.name\" shape=\"circular\" [attached]=\"false\">\n @for (opt of specType.options; track opt.id) {\n <button clx-button\n [variant]=\"isSpecSelected(specType.id, opt.id) ? 'solid' : 'outline'\"\n [color]=\"asColor(opt.value)\"\n shape=\"circle\" size=\"xs\" [iconOnly]=\"true\"\n [disabled]=\"!isOptionAvailable(specType.id, opt.id)\"\n [clxTooltip]=\"opt.value\"\n [clxTooltipColor]=\"asColor(opt.value)\"\n clxTooltipPosition=\"top\"\n (click)=\"selectSpec(specType.id, opt.id)\">\n </button>\n }\n </clx-button-group>\n } @else {\n <clx-button-group [label]=\"specType.name\" shape=\"flat\" [attached]=\"false\">\n @for (opt of specType.options; track opt.id) {\n <button clx-button\n [variant]=\"isSpecSelected(specType.id, opt.id) ? 'solid' : 'outline'\"\n [color]=\"clxColor()\" size=\"sm\"\n [disabled]=\"!isOptionAvailable(specType.id, opt.id)\"\n (click)=\"selectSpec(specType.id, opt.id)\">\n {{ opt.value }}\n </button>\n }\n </clx-button-group>\n }\n }\n\n <!-- Quantity -->\n <clx-number label=\"Quantity:\" variant=\"stepper\" [color]=\"clxColor()\"\n [min]=\"1\" [max]=\"99\" [ngModel]=\"quantity()\" (ngModelChange)=\"quantity.set($event)\">\n </clx-number>\n\n <!-- CTA -->\n <div class=\"flex gap-3\">\n <button clx-button variant=\"solid\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"bolt\"\n [disabled]=\"!v?.in_stock\">Buy Now</button>\n <button clx-button variant=\"light\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"shopping_cart\"\n [disabled]=\"!v?.in_stock\">Add to Cart</button>\n </div>\n\n </div>\n </div>\n\n <!-- \u2500\u2500 Tabs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <clx-tabs [tabs]=\"tabItems\" [clxColor]=\"clxColor()\" [(activeTab)]=\"activeTab\">\n\n <!-- Tab: Caracter\u00EDsticas generales (st_product_specs \u2014 ficha t\u00E9cnica global) -->\n <ng-template [clxTabPanel]=\"'general'\">\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-x-8\">\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (spec of specsLeft(p.product_specs); track spec.id) {\n <clx-list-item [label]=\"spec.name + ':'\" [sublabel]=\"spec.value\" [icon]=\"spec.icon ?? ''\" />\n }\n </clx-list>\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (spec of specsRight(p.product_specs); track spec.id) {\n <clx-list-item [label]=\"spec.name + ':'\" [sublabel]=\"spec.value\" [icon]=\"spec.icon ?? ''\" />\n }\n </clx-list>\n </div>\n </ng-template>\n\n <!-- Tab: Especificaciones (st_spec_types \u2014 specs de la variaci\u00F3n activa) -->\n <ng-template [clxTabPanel]=\"'specs'\">\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-x-8\">\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (st of specsLeft(p.spec_types); track st.id) {\n <clx-list-item [label]=\"st.name + ':'\" [sublabel]=\"selectedOptionLabel(st)\" [icon]=\"st.icon ?? ''\" />\n }\n </clx-list>\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (st of specsRight(p.spec_types); track st.id) {\n <clx-list-item [label]=\"st.name + ':'\" [sublabel]=\"selectedOptionLabel(st)\" [icon]=\"st.icon ?? ''\" />\n }\n </clx-list>\n </div>\n </ng-template>\n\n <!-- Tab: Descripci\u00F3n (st_product_descriptions) -->\n <ng-template [clxTabPanel]=\"'description'\">\n <div class=\"space-y-5 max-w-3xl\">\n @for (block of p.descriptions; track block.id) {\n <div>\n @if (block.title) {\n <h3 class=\"text-base font-semibold text-clx-text-label mb-1\">{{ block.title }}</h3>\n }\n <p class=\"text-sm text-clx-text-muted leading-relaxed\">{{ block.body }}</p>\n </div>\n }\n </div>\n </ng-template>\n\n </clx-tabs>\n\n <!-- \u2500\u2500 Reviews \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"grid grid-cols-1 xl:grid-cols-3 gap-8 border-t border-clx-border pt-8\">\n\n <div class=\"space-y-3\">\n <h2 class=\"text-base font-bold text-clx-text-label\">Customer Reviews</h2>\n <div class=\"flex items-end gap-3\">\n <span class=\"text-5xl font-black text-clx-text-label\">{{ p.rating | number:'1.1-1' }}</span>\n <div class=\"pb-1 space-y-1\">\n <clx-rating [value]=\"p.rating\" [color]=\"clxColor()\"></clx-rating>\n <p class=\"text-xs text-clx-text-muted\">{{ p.review_count }} verified ratings</p>\n </div>\n </div>\n <p class=\"text-sm font-semibold\" [class]=\"'text-' + clxColor() + '-600'\">Very Good</p>\n <div class=\"pt-2 space-y-2\">\n <button clx-button variant=\"solid\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"rate_review\">\n Leave Us a Review\n </button>\n <button clx-button variant=\"light\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"flag\">\n Report abuse\n </button>\n </div>\n </div>\n\n <div class=\"lg:col-span-2 space-y-4\">\n @for (review of p.reviews ?? []; track review.id) {\n <div class=\"rounded-2xl overflow-hidden shadow-sm\">\n <div class=\"flex items-center gap-3 px-4 py-3\">\n <div class=\"w-10 h-10 rounded-full bg-clx-surface-2 flex items-center justify-center shrink-0\">\n <span class=\"text-sm font-bold text-clx-text-muted\">\n {{ review.author_name.substring(0,1) }}{{ review.author_name.split(' ')[1]?.substring(0,1) ?? '' }}\n </span>\n </div>\n <div class=\"flex-1 min-w-0\">\n <p class=\"font-semibold text-sm text-clx-text-label truncate\">{{ review.author_name }}</p>\n <p class=\"text-xs text-clx-text-subtle\">{{ review.created_at }}</p>\n </div>\n @if (review.is_verified) {\n <span clx-badge variant=\"light\" color=\"emerald\" size=\"sm\" shape=\"pill\">Verified</span>\n }\n </div>\n <div class=\"px-4 pb-4 space-y-2\">\n <div class=\"flex items-center gap-2\">\n <clx-rating [value]=\"review.rating\" [color]=\"clxColor()\"></clx-rating>\n @if (review.title) {\n <span class=\"text-sm font-semibold text-clx-text-label\">{{ review.title }}</span>\n }\n </div>\n @if (review.body) {\n <p class=\"text-sm text-clx-text-muted\">{{ review.body }}</p>\n }\n <div class=\"flex items-center gap-3 pt-1\">\n <span class=\"text-xs text-clx-text-subtle\">Helpful?</span>\n <button clx-button variant=\"ghost\" color=\"slate\" size=\"xs\" icon=\"thumb_up\">{{ review.helpful }}</button>\n <button clx-button variant=\"ghost\" color=\"slate\" size=\"xs\" icon=\"thumb_down\">{{ review.not_helpful }}</button>\n </div>\n </div>\n </div>\n }\n </div>\n\n </div>\n\n </div>\n}\n" }]
3200
+ ], encapsulation: ViewEncapsulation.None, changeDetection: ChangeDetectionStrategy.OnPush, template: "<!-- \u2550\u2550 SKELETON \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (loading()) {\n <div class=\"grid grid-cols-1 xl:grid-cols-2 gap-8 p-6\">\n <div class=\"space-y-3\">\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"420px\" class=\"rounded-2xl\"></clx-skeleton>\n <div class=\"flex gap-2\">\n @for (t of [1,2,3,4]; track t) {\n <clx-skeleton variant=\"rectangular\" width=\"80px\" height=\"80px\" class=\"rounded-xl\"></clx-skeleton>\n }\n </div>\n </div>\n <div class=\"space-y-4 pt-2\">\n <clx-skeleton variant=\"text\" size=\"lg\" width=\"70%\"></clx-skeleton>\n <clx-skeleton variant=\"text\" size=\"sm\" width=\"40%\"></clx-skeleton>\n <clx-skeleton variant=\"text\" size=\"xl\" width=\"50%\"></clx-skeleton>\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"100px\" class=\"rounded-xl\"></clx-skeleton>\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"52px\" class=\"rounded-xl\"></clx-skeleton>\n <clx-skeleton variant=\"rectangular\" width=\"100%\" height=\"52px\" class=\"rounded-xl\"></clx-skeleton>\n </div>\n </div>\n}\n\n<!-- \u2550\u2550 CONTENT \u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550 -->\n@if (!loading() && product(); as p) {\n @let v = activeVariation();\n <div class=\"space-y-8\">\n\n <div class=\"grid grid-cols-1 xl:grid-cols-2 gap-8 xl:items-start\">\n\n <!-- \u2500\u2500 Column 1: Image Gallery \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"space-y-3\">\n <div class=\"relative\">\n <clx-carousel [clxColor]=\"clxColor()\" aspectRatio=\"4/3\" [loop]=\"true\">\n @for (img of v?.images ?? []; track img) {\n <ng-template clxSlide>\n <img [src]=\"img\" [alt]=\"p.product_name\" class=\"w-full h-full object-cover\" loading=\"lazy\" />\n </ng-template>\n }\n </clx-carousel>\n <div class=\"absolute top-3 right-3 z-10\">\n <button clx-button variant=\"solid\" [color]=\"clxColor()\" shape=\"circle\" size=\"md\"\n [iconOnly]=\"true\" clxTooltip=\"Add to Wishlist\" [clxTooltipColor]=\"clxColor()\" clxTooltipPosition=\"left\">\n <span clx-icon name=\"favorite_border\" size=\"sm\"></span>\n </button>\n </div>\n </div>\n <div class=\"flex flex-wrap gap-2\">\n @for (img of v?.images ?? []; track img; let i = $index) {\n <img [src]=\"img\" [alt]=\"p.product_name\"\n class=\"w-20 h-20 rounded-xl object-cover border-[3px] cursor-pointer shrink-0 transition-[border-color,opacity]\"\n [class]=\"_carousel()?.currentPageIndex() === i\n ? 'border-' + clxColor() + '-500 opacity-100'\n : 'border-transparent opacity-60 hover:opacity-90'\"\n loading=\"lazy\"\n (click)=\"_carousel()?.goTo(i)\" />\n }\n </div>\n </div>\n\n <!-- \u2500\u2500 Column 2: Purchase Info \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"space-y-5\">\n\n <!-- Tags (st_products.tags) -->\n <div class=\"flex flex-wrap gap-2\">\n @for (tag of p.tags; track tag) {\n <span clx-badge variant=\"light\" [color]=\"clxColor()\" size=\"sm\" shape=\"pill\">{{ tag }}</span>\n }\n </div>\n\n <!-- Title -->\n <div>\n <p class=\"text-xs text-clx-text-subtle uppercase tracking-wide font-medium\">{{ p.trademark }}</p>\n <h1 class=\"text-2xl font-bold text-clx-text-label leading-tight mt-0.5\">{{ p.product_name }}</h1>\n @if (v?.description) {\n <p class=\"text-clx-text-muted text-sm mt-1\">{{ v!.description }}</p>\n }\n </div>\n\n <!-- Rating -->\n <div class=\"flex items-center gap-2\">\n <clx-rating [value]=\"p.rating\" [color]=\"clxColor()\"></clx-rating>\n <span class=\"text-sm font-semibold text-clx-text-label\">{{ p.rating | number:'1.1-1' }}</span>\n <span class=\"text-sm text-clx-text-subtle\">({{ p.review_count }} reviews)</span>\n </div>\n\n <!-- Price block -->\n <div class=\"py-4\">\n <div class=\"flex items-baseline gap-3\">\n <span class=\"text-4xl font-black text-clx-text-label\">\n {{ finalPrice() | currency:currency():'symbol':'1.0-0' }}\n </span>\n @if (p.has_discount && p.discount_price) {\n <span class=\"text-lg text-clx-text-subtle line-through\">\n {{ p.discount_price | currency:currency():'symbol':'1.0-0' }}\n </span>\n <span clx-badge variant=\"solid\" [color]=\"clxColor()\" size=\"sm\" shape=\"pill\">\n -{{ discount() }}%\n </span>\n }\n </div>\n @if (v && !v.in_stock) {\n <p class=\"text-sm text-rose-500 font-medium mt-1\">Out of stock</p>\n }\n </div>\n\n <!-- Selectores de variaci\u00F3n (st_spec_types + st_spec_options) -->\n @for (specType of p.spec_types; track specType.id) {\n @if (specType.input_type === 'color') {\n <clx-button-group [label]=\"specType.name\" shape=\"circular\" [attached]=\"false\">\n @for (opt of specType.options; track opt.id) {\n <button clx-button\n [variant]=\"isSpecSelected(specType.id, opt.id) ? 'solid' : 'outline'\"\n [color]=\"asColor(opt.value)\"\n shape=\"circle\" size=\"xs\" [iconOnly]=\"true\"\n [disabled]=\"!isOptionAvailable(specType.id, opt.id)\"\n [clxTooltip]=\"opt.value\"\n [clxTooltipColor]=\"asColor(opt.value)\"\n clxTooltipPosition=\"top\"\n (click)=\"selectSpec(specType.id, opt.id)\">\n </button>\n }\n </clx-button-group>\n } @else {\n <clx-button-group [label]=\"specType.name\" shape=\"flat\" [attached]=\"false\">\n @for (opt of specType.options; track opt.id) {\n <button clx-button\n [variant]=\"isSpecSelected(specType.id, opt.id) ? 'solid' : 'outline'\"\n [color]=\"clxColor()\" size=\"sm\"\n [disabled]=\"!isOptionAvailable(specType.id, opt.id)\"\n (click)=\"selectSpec(specType.id, opt.id)\">\n {{ opt.value }}\n </button>\n }\n </clx-button-group>\n }\n }\n\n <!-- Quantity -->\n <clx-number label=\"Quantity:\" variant=\"stepper\" [color]=\"clxColor()\"\n [min]=\"1\" [max]=\"99\" [ngModel]=\"quantity()\" (ngModelChange)=\"quantity.set($event)\">\n </clx-number>\n\n <!-- CTA -->\n <div class=\"flex gap-3\">\n <button clx-button variant=\"solid\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"bolt\"\n [disabled]=\"!v?.in_stock\">Buy Now</button>\n <button clx-button variant=\"light\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"shopping_cart\"\n [disabled]=\"!v?.in_stock\">Add to Cart</button>\n </div>\n\n </div>\n </div>\n\n <!-- \u2500\u2500 Tabs \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <clx-tabs [tabs]=\"tabItems\" [clxColor]=\"clxColor()\" [(activeTab)]=\"activeTab\">\n\n <!-- Tab: Caracter\u00EDsticas generales (st_product_specs \u2014 ficha t\u00E9cnica global) -->\n <ng-template [clxTabPanel]=\"'general'\">\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-x-8\">\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (spec of specsLeft(p.product_specs); track spec.id) {\n <clx-list-item [label]=\"spec.name + ':'\" [sublabel]=\"spec.value\" [icon]=\"spec.icon ?? ''\" />\n }\n </clx-list>\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (spec of specsRight(p.product_specs); track spec.id) {\n <clx-list-item [label]=\"spec.name + ':'\" [sublabel]=\"spec.value\" [icon]=\"spec.icon ?? ''\" />\n }\n </clx-list>\n </div>\n </ng-template>\n\n <!-- Tab: Especificaciones (st_spec_types \u2014 specs de la variaci\u00F3n activa) -->\n <ng-template [clxTabPanel]=\"'specs'\">\n <div class=\"grid grid-cols-1 sm:grid-cols-2 gap-x-8\">\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (st of specsLeft(p.spec_types); track st.id) {\n <clx-list-item [label]=\"st.name + ':'\" [sublabel]=\"selectedOptionLabel(st)\" [icon]=\"st.icon ?? ''\" />\n }\n </clx-list>\n <clx-list [color]=\"clxColor()\" variant=\"flush\" size=\"sm\">\n @for (st of specsRight(p.spec_types); track st.id) {\n <clx-list-item [label]=\"st.name + ':'\" [sublabel]=\"selectedOptionLabel(st)\" [icon]=\"st.icon ?? ''\" />\n }\n </clx-list>\n </div>\n </ng-template>\n\n <!-- Tab: Descripci\u00F3n (st_product_descriptions) -->\n <ng-template [clxTabPanel]=\"'description'\">\n <div class=\"space-y-5 max-w-3xl\">\n @for (block of p.descriptions; track block.id) {\n <div>\n @if (block.title) {\n <h3 class=\"text-base font-semibold text-clx-text-label mb-1\">{{ block.title }}</h3>\n }\n <p class=\"text-sm text-clx-text-muted leading-relaxed\">{{ block.body }}</p>\n </div>\n }\n </div>\n </ng-template>\n\n </clx-tabs>\n\n <!-- \u2500\u2500 Reviews \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 -->\n <div class=\"grid grid-cols-1 xl:grid-cols-3 gap-8 border-t border-clx-border pt-8\">\n\n <div class=\"space-y-3\">\n <h2 class=\"text-base font-bold text-clx-text-label\">Customer Reviews</h2>\n <div class=\"flex items-end gap-3\">\n <span class=\"text-5xl font-black text-clx-text-label\">{{ p.rating | number:'1.1-1' }}</span>\n <div class=\"pb-1 space-y-1\">\n <clx-rating [value]=\"p.rating\" [color]=\"clxColor()\"></clx-rating>\n <p class=\"text-xs text-clx-text-muted\">{{ p.review_count }} verified ratings</p>\n </div>\n </div>\n <p class=\"text-sm font-semibold\" [class]=\"'text-' + clxColor() + '-600'\">Very Good</p>\n <div class=\"pt-2 space-y-2\">\n <button clx-button variant=\"solid\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"rate_review\">\n Leave Us a Review\n </button>\n <button clx-button variant=\"light\" [color]=\"clxColor()\" size=\"md\" [block]=\"true\" icon=\"flag\">\n Report abuse\n </button>\n </div>\n </div>\n\n <div class=\"lg:col-span-2 space-y-4\">\n @for (review of p.reviews ?? []; track review.id) {\n <div class=\"rounded-2xl overflow-hidden shadow-sm\">\n <div class=\"flex items-center gap-3 px-4 py-3\">\n <div class=\"w-10 h-10 rounded-full bg-clx-surface-2 flex items-center justify-center shrink-0\">\n <span class=\"text-sm font-bold text-clx-text-muted\">\n {{ review.author_name.substring(0,1) }}{{ review.author_name.split(' ')[1]?.substring(0,1) ?? '' }}\n </span>\n </div>\n <div class=\"flex-1 min-w-0\">\n <p class=\"font-semibold text-sm text-clx-text-label truncate\">{{ review.author_name }}</p>\n <p class=\"text-xs text-clx-text-subtle\">{{ review.created_at }}</p>\n </div>\n @if (review.is_verified) {\n <span clx-badge variant=\"light\" color=\"emerald\" size=\"sm\" shape=\"pill\">Verified</span>\n }\n </div>\n <div class=\"px-4 pb-4 space-y-2\">\n <div class=\"flex items-center gap-2\">\n <clx-rating [value]=\"review.rating\" [color]=\"clxColor()\"></clx-rating>\n @if (review.title) {\n <span class=\"text-sm font-semibold text-clx-text-label\">{{ review.title }}</span>\n }\n </div>\n @if (review.body) {\n <p class=\"text-sm text-clx-text-muted\">{{ review.body }}</p>\n }\n <div class=\"flex items-center gap-3 pt-1\">\n <span class=\"text-xs text-clx-text-subtle\">Helpful?</span>\n <button clx-button variant=\"ghost\" color=\"slate\" size=\"xs\" icon=\"thumb_up\">{{ review.helpful }}</button>\n <button clx-button variant=\"ghost\" color=\"slate\" size=\"xs\" icon=\"thumb_down\">{{ review.not_helpful }}</button>\n </div>\n </div>\n </div>\n }\n </div>\n\n </div>\n\n </div>\n}\n" }]
2898
3201
  }], ctorParameters: () => [], propDecorators: { product: [{ type: i0.Input, args: [{ isSignal: true, alias: "product", required: false }] }], clxColor: [{ type: i0.Input, args: [{ isSignal: true, alias: "clxColor", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], currency: [{ type: i0.Input, args: [{ isSignal: true, alias: "currency", required: false }] }], _carousel: [{ type: i0.ViewChild, args: [i0.forwardRef(() => ClxCarouselComponent), { isSignal: true }] }] } });
2899
3202
 
2900
3203
  const SIZE_MAP = {
@@ -3145,7 +3448,7 @@ class ClxCheckboxComponent {
3145
3448
  _onTouched = () => { };
3146
3449
  _sizeConfig = computed(() => CHECKBOX_SIZE_MAP[this._size()], ...(ngDevMode ? [{ debugName: "_sizeConfig" }] : /* istanbul ignore next */ []));
3147
3450
  _boxClass = computed(() => {
3148
- const base = `${this._sizeConfig().box} flex items-center justify-center rounded border-2 transition-all duration-200`;
3451
+ const base = `${this._sizeConfig().box} flex items-center justify-center rounded border-2 transition-colors duration-200`;
3149
3452
  if (this._disabled()) {
3150
3453
  return this._checked()
3151
3454
  ? `${base} border-clx-border bg-clx-border text-white`
@@ -3318,7 +3621,7 @@ class ClxSliderComponent {
3318
3621
  _isInvalid = computed(() => this._wasTouched() && (this._ngControl?.control?.invalid ?? false), ...(ngDevMode ? [{ debugName: "_isInvalid" }] : /* istanbul ignore next */ []));
3319
3622
  _trackClass = computed(() => 'absolute inset-x-0 top-1/2 -translate-y-1/2 rounded-full bg-clx-border overflow-visible', ...(ngDevMode ? [{ debugName: "_trackClass" }] : /* istanbul ignore next */ []));
3320
3623
  _thumbClass = computed(() => {
3321
- const base = 'absolute top-1/2 -translate-y-1/2 -translate-x-1/2 rounded-full bg-clx-surface border-[3px] shadow-md transition-all duration-75 pointer-events-none z-10';
3624
+ const base = 'absolute top-1/2 -translate-y-1/2 -translate-x-1/2 rounded-full bg-clx-surface border-[3px] shadow-md transition-[border-color,box-shadow,transform] duration-75 pointer-events-none z-10';
3322
3625
  const t = this._colorTokens();
3323
3626
  const border = this._disabled() ? 'border-clx-border' : t.border;
3324
3627
  const ring = this._isFocused() && !this._disabled()
@@ -3386,7 +3689,7 @@ class ClxSliderComponent {
3386
3689
 
3387
3690
  <!-- Filled track -->
3388
3691
  <div
3389
- class="absolute left-0 top-0 h-full rounded-full transition-all duration-75"
3692
+ class="absolute left-0 top-0 h-full rounded-full transition-[width,background-color] duration-75"
3390
3693
  [class]="_disabled() ? 'bg-clx-border' : _colorTokens().bg"
3391
3694
  [style.width]="_pct() + '%'">
3392
3695
  </div>
@@ -3485,7 +3788,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
3485
3788
 
3486
3789
  <!-- Filled track -->
3487
3790
  <div
3488
- class="absolute left-0 top-0 h-full rounded-full transition-all duration-75"
3791
+ class="absolute left-0 top-0 h-full rounded-full transition-[width,background-color] duration-75"
3489
3792
  [class]="_disabled() ? 'bg-clx-border' : _colorTokens().bg"
3490
3793
  [style.width]="_pct() + '%'">
3491
3794
  </div>
@@ -3671,7 +3974,7 @@ class ClxRadioComponent {
3671
3974
  _size = computed(() => this.size() ?? this._group?.size() ?? this._themeSvc.config().defaultSize, ...(ngDevMode ? [{ debugName: "_size" }] : /* istanbul ignore next */ []));
3672
3975
  _sizeConfig = computed(() => RADIO_SIZE_MAP[this._size()], ...(ngDevMode ? [{ debugName: "_sizeConfig" }] : /* istanbul ignore next */ []));
3673
3976
  _ringClass = computed(() => {
3674
- const base = `${this._sizeConfig().ring} rounded-full border-2 flex items-center justify-center transition-all duration-200`;
3977
+ const base = `${this._sizeConfig().ring} rounded-full border-2 flex items-center justify-center transition-colors duration-200`;
3675
3978
  if (this._isDisabled()) {
3676
3979
  return this._checked()
3677
3980
  ? `${base} border-clx-border bg-clx-border`
@@ -3685,7 +3988,7 @@ class ClxRadioComponent {
3685
3988
  return `${base} ${t.bgSubtle} ${t.borderLight}`;
3686
3989
  }, ...(ngDevMode ? [{ debugName: "_ringClass" }] : /* istanbul ignore next */ []));
3687
3990
  _dotClass = computed(() => {
3688
- const base = `${this._sizeConfig().dot} rounded-full transition-all duration-200`;
3991
+ const base = `${this._sizeConfig().dot} rounded-full transition-[transform,color,background-color] duration-200`;
3689
3992
  if (!this._checked())
3690
3993
  return `${base} scale-0`;
3691
3994
  if (this._isDisabled())
@@ -3841,7 +4144,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
3841
4144
 
3842
4145
  const INPUT_STATUS_MAP = {
3843
4146
  default: {
3844
- border: 'border-slate-200',
4147
+ border: 'border-clx-border',
3845
4148
  ring: '',
3846
4149
  msgCls: '',
3847
4150
  iconName: '',
@@ -3960,7 +4263,7 @@ class ClxInputComponent {
3960
4263
  : this.status() !== 'default'
3961
4264
  ? this._statusCfg().ring.replace(/focus:/g, 'focus-within:')
3962
4265
  : resolveColor(this._color()).focusWithin;
3963
- return `relative flex items-stretch ${resolveRadius(this._themeSvc.config().borderRadius)} border ${border} ${focusCls} overflow-hidden transition-all duration-200`;
4266
+ return `relative flex items-stretch ${resolveRadius(this._themeSvc.config().borderRadius)} border ${border} ${focusCls} overflow-hidden transition-[border-color,box-shadow] duration-200`;
3964
4267
  }, ...(ngDevMode ? [{ debugName: "_wrapperClass" }] : /* istanbul ignore next */ []));
3965
4268
  _inputClass = computed(() => {
3966
4269
  const size = this._sizeConfig();
@@ -3978,7 +4281,7 @@ class ClxInputComponent {
3978
4281
  const hasSuffix = this.type() === 'password' || this.status() !== 'default' || !!this.suffixIcon();
3979
4282
  const pl = size.padDefaultL;
3980
4283
  const pr = hasSuffix ? size.padRight : size.padDefaultR;
3981
- const base = `w-full ${resolveRadius(this._themeSvc.config().borderRadius)} border ${size.input} ${pl} ${pr} outline-none transition-all duration-200`;
4284
+ const base = `w-full ${resolveRadius(this._themeSvc.config().borderRadius)} border ${size.input} ${pl} ${pr} outline-none transition-[border-color,box-shadow] duration-200`;
3982
4285
  if (this._disabled()) {
3983
4286
  return `${base} ${CLX_BORDER_DISABLED} ${CLX_BG_DISABLED} cursor-not-allowed ${CLX_TEXT_DISABLED} ${CLX_PLACEHOLDER}`;
3984
4287
  }
@@ -4254,11 +4557,18 @@ class ClxSelectComponent {
4254
4557
  _activeColor = computed(() => this.activeColor() ?? this._themeSvc.config().primaryColor, ...(ngDevMode ? [{ debugName: "_activeColor" }] : /* istanbul ignore next */ []));
4255
4558
  _el = inject((ElementRef));
4256
4559
  _sso = inject(ScrollStrategyOptions);
4560
+ _isBrowser = isPlatformBrowser(inject(PLATFORM_ID));
4561
+ _loadedFonts = new Set();
4257
4562
  _scrollStrategy = this._sso.reposition();
4258
4563
  constructor() {
4259
4564
  this._searchQuery = toSignal(this._searchControl.valueChanges.pipe(startWith(''), map(v => v ?? '')), { initialValue: '' });
4260
4565
  if (this._ngControl)
4261
4566
  this._ngControl.valueAccessor = this;
4567
+ effect(() => {
4568
+ const family = this._displayFontFamily();
4569
+ if (family)
4570
+ this._loadPreviewFont(family);
4571
+ });
4262
4572
  // Async autocomplete: debounce + switchMap sobre el search control
4263
4573
  this._searchControl.valueChanges.pipe(debounceTime(this.debounceMs()), distinctUntilChanged(), switchMap(query => {
4264
4574
  const q = query ?? '';
@@ -4306,12 +4616,16 @@ class ClxSelectComponent {
4306
4616
  const sel = this._selectedOptions();
4307
4617
  return sel.length ? sel[0].label : this.placeholder();
4308
4618
  }, ...(ngDevMode ? [{ debugName: "_displayValue" }] : /* istanbul ignore next */ []));
4619
+ _displayFontFamily = computed(() => {
4620
+ const sel = this._selectedOptions();
4621
+ return sel.length ? sel[0].previewFontFamily ?? null : null;
4622
+ }, ...(ngDevMode ? [{ debugName: "_displayFontFamily" }] : /* istanbul ignore next */ []));
4309
4623
  _overlayWidth = computed(() => this._overlayWidthPx(), ...(ngDevMode ? [{ debugName: "_overlayWidth" }] : /* istanbul ignore next */ []));
4310
4624
  _labelClass = computed(() => `${this._sizeConfig().label} ${CLX_TEXT_LABEL} leading-none`, ...(ngDevMode ? [{ debugName: "_labelClass" }] : /* istanbul ignore next */ []));
4311
4625
  _hintClass = computed(() => `${this._sizeConfig().hint} ${CLX_TEXT_HINT}`, ...(ngDevMode ? [{ debugName: "_hintClass" }] : /* istanbul ignore next */ []));
4312
4626
  _triggerClass = computed(() => {
4313
4627
  const s = this._sizeConfig();
4314
- const base = `relative flex items-center ${resolveRadius(this._themeSvc.config().borderRadius)} border transition-all duration-200 outline-none w-full overflow-hidden`;
4628
+ const base = `relative flex items-center ${resolveRadius(this._themeSvc.config().borderRadius)} border transition-[border-color,box-shadow] duration-200 outline-none w-full overflow-hidden`;
4315
4629
  if (this._disabled()) {
4316
4630
  return `${base} ${s.minH} ${CLX_BORDER_DISABLED} ${CLX_BG_DISABLED} cursor-not-allowed opacity-60`;
4317
4631
  }
@@ -4356,6 +4670,26 @@ class ClxSelectComponent {
4356
4670
  if (!this.async())
4357
4671
  this._searchControl.setValue('', { emitEvent: false });
4358
4672
  this._isOpen.set(true);
4673
+ for (const opt of this.options()) {
4674
+ if (opt.previewFontFamily)
4675
+ this._loadPreviewFont(opt.previewFontFamily);
4676
+ }
4677
+ }
4678
+ _loadPreviewFont(family) {
4679
+ if (!this._isBrowser || this._loadedFonts.has(family))
4680
+ return;
4681
+ const option = CLX_FONT_CATALOG.find(f => f.value === family);
4682
+ if (!option)
4683
+ return;
4684
+ this._loadedFonts.add(family);
4685
+ const id = `clx-preview-font-${family.replace(/\s+/g, '-').toLowerCase()}`;
4686
+ if (document.getElementById(id))
4687
+ return;
4688
+ const link = document.createElement('link');
4689
+ link.id = id;
4690
+ link.rel = 'stylesheet';
4691
+ link.href = option.googleUrl;
4692
+ document.head.appendChild(link);
4359
4693
  }
4360
4694
  _closePanel() {
4361
4695
  this._isOpen.set(false);
@@ -4465,7 +4799,7 @@ class ClxSelectComponent {
4465
4799
 
4466
4800
  <!-- Single mode: display value -->
4467
4801
  } @else {
4468
- <span [class]="_singleDisplayClass()">{{ _displayValue() }}</span>
4802
+ <span [class]="_singleDisplayClass()" [style.font-family]="_displayFontFamily()">{{ _displayValue() }}</span>
4469
4803
  }
4470
4804
 
4471
4805
  <!-- Right controls: clear + chevron -->
@@ -4545,7 +4879,7 @@ class ClxSelectComponent {
4545
4879
  [disabled]="opt.disabled ?? false"
4546
4880
  (mousedown)="$event.preventDefault()"
4547
4881
  (click)="_selectOption(opt)">
4548
- <span class="flex-1 text-left truncate">{{ opt.label }}</span>
4882
+ <span class="flex-1 text-left truncate" [style.font-family]="opt.previewFontFamily ?? null">{{ opt.label }}</span>
4549
4883
  @if (_isSelected(opt.value)) {
4550
4884
  <span clx-icon name="check" size="xs" class="shrink-0"></span>
4551
4885
  }
@@ -4612,7 +4946,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
4612
4946
 
4613
4947
  <!-- Single mode: display value -->
4614
4948
  } @else {
4615
- <span [class]="_singleDisplayClass()">{{ _displayValue() }}</span>
4949
+ <span [class]="_singleDisplayClass()" [style.font-family]="_displayFontFamily()">{{ _displayValue() }}</span>
4616
4950
  }
4617
4951
 
4618
4952
  <!-- Right controls: clear + chevron -->
@@ -4692,7 +5026,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
4692
5026
  [disabled]="opt.disabled ?? false"
4693
5027
  (mousedown)="$event.preventDefault()"
4694
5028
  (click)="_selectOption(opt)">
4695
- <span class="flex-1 text-left truncate">{{ opt.label }}</span>
5029
+ <span class="flex-1 text-left truncate" [style.font-family]="opt.previewFontFamily ?? null">{{ opt.label }}</span>
4696
5030
  @if (_isSelected(opt.value)) {
4697
5031
  <span clx-icon name="check" size="xs" class="shrink-0"></span>
4698
5032
  }
@@ -5137,6 +5471,7 @@ class ClxAlertComponent {
5137
5471
  _resolve = inject(CLX_ALERT_RESOLVE);
5138
5472
  _animate = inject(ClxAnimateService);
5139
5473
  _el = inject((ElementRef));
5474
+ _themeSvc = inject(ClxThemeService);
5140
5475
  _zone;
5141
5476
  constructor() {
5142
5477
  const zone = inject(NgZone, { optional: true });
@@ -5144,17 +5479,36 @@ class ClxAlertComponent {
5144
5479
  }
5145
5480
  // ─── Resolved options ─────────────────────────────────────────────────────
5146
5481
  get _type() { return this._opts.type ?? 'info'; }
5482
+ get _isConfirm() { return this._type === 'confirm'; }
5147
5483
  _icon = signal(ALERT_ICON_MAP[this._type], ...(ngDevMode ? [{ debugName: "_icon" }] : /* istanbul ignore next */ []));
5148
- _iconBg = signal(ALERT_ICON_BG_MAP[this._type], ...(ngDevMode ? [{ debugName: "_iconBg" }] : /* istanbul ignore next */ []));
5149
- _iconColor = signal(ALERT_ICON_COLOR_MAP[this._type], ...(ngDevMode ? [{ debugName: "_iconColor" }] : /* istanbul ignore next */ []));
5150
- _progressColor = signal(ALERT_PROGRESS_COLOR_MAP[this._type], ...(ngDevMode ? [{ debugName: "_progressColor" }] : /* istanbul ignore next */ []));
5151
5484
  _title = signal(this._opts.title ?? '', ...(ngDevMode ? [{ debugName: "_title" }] : /* istanbul ignore next */ []));
5152
5485
  _message = signal(this._opts.message ?? '', ...(ngDevMode ? [{ debugName: "_message" }] : /* istanbul ignore next */ []));
5153
5486
  _showCancel = signal(this._opts.showCancelButton ?? this._opts.type === 'confirm', ...(ngDevMode ? [{ debugName: "_showCancel" }] : /* istanbul ignore next */ []));
5487
+ _iconBg = computed(() => {
5488
+ if (!this._isConfirm)
5489
+ return ALERT_ICON_BG_MAP[this._type];
5490
+ return resolveColor(this._themeSvc.config().primaryColor).bgSubtle;
5491
+ }, ...(ngDevMode ? [{ debugName: "_iconBg" }] : /* istanbul ignore next */ []));
5492
+ _iconColor = computed(() => {
5493
+ if (!this._isConfirm)
5494
+ return ALERT_ICON_COLOR_MAP[this._type];
5495
+ return resolveColor(this._themeSvc.config().primaryColor).textSubtle;
5496
+ }, ...(ngDevMode ? [{ debugName: "_iconColor" }] : /* istanbul ignore next */ []));
5497
+ _progressColor = computed(() => {
5498
+ if (!this._isConfirm)
5499
+ return ALERT_PROGRESS_COLOR_MAP[this._type];
5500
+ return resolveColor(this._themeSvc.config().primaryColor).bg;
5501
+ }, ...(ngDevMode ? [{ debugName: "_progressColor" }] : /* istanbul ignore next */ []));
5154
5502
  _confirmText = signal(this._opts.confirmButton?.text ?? 'Aceptar', ...(ngDevMode ? [{ debugName: "_confirmText" }] : /* istanbul ignore next */ []));
5155
- _confirmColor = signal(this._opts.confirmButton?.color ?? ALERT_CONFIRM_COLOR_MAP[this._type], ...(ngDevMode ? [{ debugName: "_confirmColor" }] : /* istanbul ignore next */ []));
5503
+ _confirmColor = computed(() => {
5504
+ if (this._opts.confirmButton?.color)
5505
+ return this._opts.confirmButton.color;
5506
+ if (this._isConfirm)
5507
+ return this._themeSvc.config().primaryColor;
5508
+ return ALERT_CONFIRM_COLOR_MAP[this._type];
5509
+ }, ...(ngDevMode ? [{ debugName: "_confirmColor" }] : /* istanbul ignore next */ []));
5156
5510
  _cancelText = signal(this._opts.cancelButton?.text ?? 'Cancelar', ...(ngDevMode ? [{ debugName: "_cancelText" }] : /* istanbul ignore next */ []));
5157
- _cancelColor = signal(this._opts.cancelButton?.color ?? 'slate', ...(ngDevMode ? [{ debugName: "_cancelColor" }] : /* istanbul ignore next */ []));
5511
+ _cancelColor = computed(() => this._opts.cancelButton?.color ?? this._themeSvc.config().secondaryColor, ...(ngDevMode ? [{ debugName: "_cancelColor" }] : /* istanbul ignore next */ []));
5158
5512
  _cancelVariant = signal(this._opts.cancelButton?.variant ?? 'ghost', ...(ngDevMode ? [{ debugName: "_cancelVariant" }] : /* istanbul ignore next */ []));
5159
5513
  // ─── Timer state ─────────────────────────────────────────────────────────
5160
5514
  _timerTotal = signal(this._opts.timer ?? 0, ...(ngDevMode ? [{ debugName: "_timerTotal" }] : /* istanbul ignore next */ []));
@@ -5258,7 +5612,6 @@ class ClxAlertComponent {
5258
5612
  [color]="_cancelColor()"
5259
5613
  [variant]="_cancelVariant()"
5260
5614
  size="md"
5261
- shape="pill"
5262
5615
  (click)="dismiss()"
5263
5616
  >
5264
5617
  {{ _cancelText() }}
@@ -5270,7 +5623,6 @@ class ClxAlertComponent {
5270
5623
  [color]="_confirmColor()"
5271
5624
  variant="solid"
5272
5625
  size="md"
5273
- shape="pill"
5274
5626
  (click)="confirm()"
5275
5627
  >
5276
5628
  {{ _confirmText() }}
@@ -5330,7 +5682,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
5330
5682
  [color]="_cancelColor()"
5331
5683
  [variant]="_cancelVariant()"
5332
5684
  size="md"
5333
- shape="pill"
5334
5685
  (click)="dismiss()"
5335
5686
  >
5336
5687
  {{ _cancelText() }}
@@ -5342,7 +5693,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
5342
5693
  [color]="_confirmColor()"
5343
5694
  variant="solid"
5344
5695
  size="md"
5345
- shape="pill"
5346
5696
  (click)="confirm()"
5347
5697
  >
5348
5698
  {{ _confirmText() }}
@@ -5371,7 +5721,7 @@ const MODAL_SIZE_MAP = {
5371
5721
  xl: 'w-full',
5372
5722
  full: 'w-full h-screen rounded-none',
5373
5723
  };
5374
- const MODAL_CONTAINER_CLASS = 'flex flex-col bg-white rounded-2xl shadow-2xl overflow-hidden';
5724
+ const MODAL_CONTAINER_CLASS = 'flex flex-col bg-clx-surface rounded-2xl shadow-2xl overflow-hidden';
5375
5725
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
5376
5726
  const CLX_MODAL_DATA = new InjectionToken('CLX_MODAL_DATA');
5377
5727
  const CLX_MODAL_SIZE = new InjectionToken('CLX_MODAL_SIZE');
@@ -5394,7 +5744,7 @@ const DRAWER_WIDTH_MAP = {
5394
5744
  md: '420px',
5395
5745
  lg: '560px',
5396
5746
  };
5397
- const DRAWER_CONTAINER_CLASS = 'flex flex-col bg-white shadow-2xl overflow-hidden h-screen';
5747
+ const DRAWER_CONTAINER_CLASS = 'flex flex-col bg-clx-surface shadow-2xl overflow-hidden h-screen';
5398
5748
  const CLX_DRAWER_SIZE = new InjectionToken('CLX_DRAWER_SIZE');
5399
5749
 
5400
5750
  class ClxDrawerComponent {
@@ -5448,7 +5798,7 @@ class ClxDrawerComponent {
5448
5798
  </div>
5449
5799
 
5450
5800
  <!-- Footer -->
5451
- <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-clx-border-soft shrink-0 bg-clx-surface-2">
5801
+ <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-clx-border-soft shrink-0">
5452
5802
  @if (_hasBuiltInButtons()) {
5453
5803
  @if (showCancelButton()) {
5454
5804
  <button
@@ -5456,7 +5806,6 @@ class ClxDrawerComponent {
5456
5806
  [color]="_cancelColor()"
5457
5807
  [variant]="_cancelVariant()"
5458
5808
  size="md"
5459
- shape="pill"
5460
5809
  (click)="cancelClick.emit()">
5461
5810
  {{ _cancelText() }}
5462
5811
  </button>
@@ -5466,7 +5815,6 @@ class ClxDrawerComponent {
5466
5815
  [color]="_confirmColor()"
5467
5816
  [variant]="_confirmVariant()"
5468
5817
  size="md"
5469
- shape="pill"
5470
5818
  (click)="confirmClick.emit()">
5471
5819
  {{ _confirmText() }}
5472
5820
  </button>
@@ -5509,7 +5857,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
5509
5857
  </div>
5510
5858
 
5511
5859
  <!-- Footer -->
5512
- <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-clx-border-soft shrink-0 bg-clx-surface-2">
5860
+ <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-clx-border-soft shrink-0">
5513
5861
  @if (_hasBuiltInButtons()) {
5514
5862
  @if (showCancelButton()) {
5515
5863
  <button
@@ -5517,7 +5865,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
5517
5865
  [color]="_cancelColor()"
5518
5866
  [variant]="_cancelVariant()"
5519
5867
  size="md"
5520
- shape="pill"
5521
5868
  (click)="cancelClick.emit()">
5522
5869
  {{ _cancelText() }}
5523
5870
  </button>
@@ -5527,7 +5874,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
5527
5874
  [color]="_confirmColor()"
5528
5875
  [variant]="_confirmVariant()"
5529
5876
  size="md"
5530
- shape="pill"
5531
5877
  (click)="confirmClick.emit()">
5532
5878
  {{ _confirmText() }}
5533
5879
  </button>
@@ -5569,7 +5915,7 @@ class ClxModalComponent {
5569
5915
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxModalComponent, isStandalone: true, selector: "clx-modal", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, showClose: { classPropertyName: "showClose", publicName: "showClose", isSignal: true, isRequired: false, transformFunction: null }, confirmButton: { classPropertyName: "confirmButton", publicName: "confirmButton", isSignal: true, isRequired: false, transformFunction: null }, cancelButton: { classPropertyName: "cancelButton", publicName: "cancelButton", isSignal: true, isRequired: false, transformFunction: null }, showCancelButton: { classPropertyName: "showCancelButton", publicName: "showCancelButton", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { confirmClick: "confirmClick", cancelClick: "cancelClick" }, host: { properties: { "class": "_hostClass()" } }, ngImport: i0, template: `
5570
5916
  <!-- Header -->
5571
5917
  <div class="flex items-center gap-3 px-6 py-4 border-b border-clx-border-soft shrink-0">
5572
- <div class="flex-1 min-w-0">
5918
+ <div class="flex-1 min-w-0 text-clx-text-label">
5573
5919
  <ng-content select="[clx-modal-title]" />
5574
5920
  </div>
5575
5921
  @if (showClose()) {
@@ -5593,7 +5939,7 @@ class ClxModalComponent {
5593
5939
  </div>
5594
5940
 
5595
5941
  <!-- Footer -->
5596
- <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-clx-border-soft shrink-0 bg-clx-surface-2">
5942
+ <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-clx-border-soft shrink-0">
5597
5943
  @if (_hasBuiltInButtons()) {
5598
5944
  @if (showCancelButton()) {
5599
5945
  <button
@@ -5601,7 +5947,6 @@ class ClxModalComponent {
5601
5947
  [color]="_cancelColor()"
5602
5948
  [variant]="_cancelVariant()"
5603
5949
  size="md"
5604
- shape="pill"
5605
5950
  (click)="cancelClick.emit()">
5606
5951
  {{ _cancelText() }}
5607
5952
  </button>
@@ -5611,7 +5956,6 @@ class ClxModalComponent {
5611
5956
  [color]="_confirmColor()"
5612
5957
  [variant]="_confirmVariant()"
5613
5958
  size="md"
5614
- shape="pill"
5615
5959
  (click)="confirmClick.emit()">
5616
5960
  {{ _confirmText() }}
5617
5961
  </button>
@@ -5630,7 +5974,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
5630
5974
  template: `
5631
5975
  <!-- Header -->
5632
5976
  <div class="flex items-center gap-3 px-6 py-4 border-b border-clx-border-soft shrink-0">
5633
- <div class="flex-1 min-w-0">
5977
+ <div class="flex-1 min-w-0 text-clx-text-label">
5634
5978
  <ng-content select="[clx-modal-title]" />
5635
5979
  </div>
5636
5980
  @if (showClose()) {
@@ -5654,7 +5998,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
5654
5998
  </div>
5655
5999
 
5656
6000
  <!-- Footer -->
5657
- <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-clx-border-soft shrink-0 bg-clx-surface-2">
6001
+ <div class="flex items-center justify-end gap-3 px-6 py-4 border-t border-clx-border-soft shrink-0">
5658
6002
  @if (_hasBuiltInButtons()) {
5659
6003
  @if (showCancelButton()) {
5660
6004
  <button
@@ -5662,7 +6006,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
5662
6006
  [color]="_cancelColor()"
5663
6007
  [variant]="_cancelVariant()"
5664
6008
  size="md"
5665
- shape="pill"
5666
6009
  (click)="cancelClick.emit()">
5667
6010
  {{ _cancelText() }}
5668
6011
  </button>
@@ -5672,7 +6015,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
5672
6015
  [color]="_confirmColor()"
5673
6016
  [variant]="_confirmVariant()"
5674
6017
  size="md"
5675
- shape="pill"
5676
6018
  (click)="confirmClick.emit()">
5677
6019
  {{ _confirmText() }}
5678
6020
  </button>
@@ -7401,7 +7743,7 @@ class ClxColorPickerComponent {
7401
7743
  }, ...(ngDevMode ? [{ debugName: "_displayClass" }] : /* istanbul ignore next */ []));
7402
7744
  _triggerClass = computed(() => {
7403
7745
  const s = this._sizeConfig();
7404
- const base = `relative flex items-center ${resolveRadius(this._themeSvc.config().borderRadius)} border transition-all duration-200 outline-none w-full`;
7746
+ const base = `relative flex items-center ${resolveRadius(this._themeSvc.config().borderRadius)} border transition-[border-color,box-shadow] duration-200 outline-none w-full`;
7405
7747
  if (this._disabled()) {
7406
7748
  return `${base} ${s.minH} ${CLX_BORDER_DISABLED} ${CLX_BG_DISABLED} cursor-not-allowed opacity-60`;
7407
7749
  }
@@ -7714,7 +8056,7 @@ class ClxColorPickerComponent {
7714
8056
  @for (swatch of presets(); track swatch) {
7715
8057
  <button
7716
8058
  type="button"
7717
- class="w-6 h-6 rounded border border-black/10 transition-all duration-150 hover:scale-110 hover:shadow-md focus:outline-none focus:ring-2 focus:ring-offset-1 focus:ring-slate-400"
8059
+ class="w-6 h-6 rounded border border-black/10 transition-[transform,box-shadow] duration-150 hover:scale-110 hover:shadow-md focus:outline-none focus:ring-2 focus:ring-offset-1 focus:ring-slate-400"
7718
8060
  [style.background-color]="swatch"
7719
8061
  [title]="swatch"
7720
8062
  (click)="_selectPreset(swatch)">
@@ -7881,7 +8223,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
7881
8223
  @for (swatch of presets(); track swatch) {
7882
8224
  <button
7883
8225
  type="button"
7884
- class="w-6 h-6 rounded border border-black/10 transition-all duration-150 hover:scale-110 hover:shadow-md focus:outline-none focus:ring-2 focus:ring-offset-1 focus:ring-slate-400"
8226
+ class="w-6 h-6 rounded border border-black/10 transition-[transform,box-shadow] duration-150 hover:scale-110 hover:shadow-md focus:outline-none focus:ring-2 focus:ring-offset-1 focus:ring-slate-400"
7885
8227
  [style.background-color]="swatch"
7886
8228
  [title]="swatch"
7887
8229
  (click)="_selectPreset(swatch)">
@@ -8003,7 +8345,7 @@ class ClxDatepickerComponent {
8003
8345
  const focus = resolveColor(this._color()).focus;
8004
8346
  const border = this._disabled() ? `${CLX_BORDER_DISABLED} ${CLX_BG_DISABLED}` : `${status.border} focus:outline-none ${focus}`;
8005
8347
  const cursor = this._disabled() ? 'cursor-not-allowed opacity-60' : 'cursor-pointer';
8006
- return `relative flex items-stretch ${size.trigger} ${resolveRadius(this._themeSvc.config().borderRadius)} border ${border} ${cursor} overflow-hidden transition-all duration-200 select-none`;
8348
+ return `relative flex items-stretch ${size.trigger} ${resolveRadius(this._themeSvc.config().borderRadius)} border ${border} ${cursor} overflow-hidden transition-[border-color,box-shadow] duration-200 select-none`;
8007
8349
  }, ...(ngDevMode ? [{ debugName: "_triggerClass" }] : /* istanbul ignore next */ []));
8008
8350
  _displayClass = computed(() => {
8009
8351
  const hasValue = !!this._value();
@@ -8421,7 +8763,7 @@ class ClxTimepickerComponent {
8421
8763
  const focus = resolveColor(this._color()).focus;
8422
8764
  const border = this._disabled() ? `${CLX_BORDER_DISABLED} ${CLX_BG_DISABLED}` : `${status.border} focus:outline-none ${focus}`;
8423
8765
  const cursor = this._disabled() ? 'cursor-not-allowed opacity-60' : 'cursor-pointer';
8424
- return `relative flex items-stretch ${size.trigger} ${resolveRadius(this._themeSvc.config().borderRadius)} border ${border} ${cursor} overflow-hidden transition-all duration-200 select-none`;
8766
+ return `relative flex items-stretch ${size.trigger} ${resolveRadius(this._themeSvc.config().borderRadius)} border ${border} ${cursor} overflow-hidden transition-[border-color,box-shadow] duration-200 select-none`;
8425
8767
  }, ...(ngDevMode ? [{ debugName: "_triggerClass" }] : /* istanbul ignore next */ []));
8426
8768
  _displayClass = computed(() => {
8427
8769
  const hasValue = !!this._value();
@@ -8802,7 +9144,7 @@ class ClxDateRangePickerComponent {
8802
9144
  const focus = resolveColor(this._color()).focus;
8803
9145
  const border = this._disabled() ? 'border-gray-200 bg-gray-50' : `${status.border} focus:outline-none ${focus}`;
8804
9146
  const cursor = this._disabled() ? 'cursor-not-allowed opacity-60' : 'cursor-pointer';
8805
- return `relative flex items-stretch ${size.trigger} ${resolveRadius(this._themeSvc.config().borderRadius)} border ${border} ${cursor} overflow-hidden transition-all duration-200 select-none`;
9147
+ return `relative flex items-stretch ${size.trigger} ${resolveRadius(this._themeSvc.config().borderRadius)} border ${border} ${cursor} overflow-hidden transition-[border-color,box-shadow] duration-200 select-none`;
8806
9148
  }, ...(ngDevMode ? [{ debugName: "_triggerClass" }] : /* istanbul ignore next */ []));
8807
9149
  _displayClass = computed(() => {
8808
9150
  const color = this._disabled() ? 'text-clx-text-faint' : this._hasValue() ? 'text-clx-text-label' : 'text-clx-text-faint';
@@ -9662,7 +10004,7 @@ class ClxEditorComponent {
9662
10004
  const focus = this.status() !== 'default'
9663
10005
  ? statusCfg.ring.replace(/focus:/g, 'focus-within:')
9664
10006
  : resolveColor(this._color()).focusWithin;
9665
- return `${r} border ${statusCfg.border} ${focus} overflow-hidden bg-clx-surface transition-all duration-200`;
10007
+ return `${r} border ${statusCfg.border} ${focus} overflow-hidden bg-clx-surface transition-[border-color,box-shadow] duration-200`;
9666
10008
  }, ...(ngDevMode ? [{ debugName: "_shellClass" }] : /* istanbul ignore next */ []));
9667
10009
  _toolbarClass = computed(() => `flex flex-wrap items-center gap-1 ${this._sizeCfg().toolbar} border-b border-clx-border-soft bg-clx-surface`, ...(ngDevMode ? [{ debugName: "_toolbarClass" }] : /* istanbul ignore next */ []));
9668
10010
  _bodyClass = computed(() => {
@@ -10302,11 +10644,11 @@ class ClxSwitchComponent {
10302
10644
  _onTouched = () => { };
10303
10645
  _sizeConfig = computed(() => SWITCH_SIZE_MAP[this._size()], ...(ngDevMode ? [{ debugName: "_sizeConfig" }] : /* istanbul ignore next */ []));
10304
10646
  _trackClass = computed(() => {
10305
- const base = `${this._sizeConfig().track} relative rounded-full transition-all duration-200 flex-shrink-0`;
10647
+ const base = `${this._sizeConfig().track} relative rounded-full transition-colors duration-200 flex-shrink-0`;
10306
10648
  return `${base} ${this._checked() ? resolveColor(this._color()).bg : 'bg-gray-300'}`;
10307
10649
  }, ...(ngDevMode ? [{ debugName: "_trackClass" }] : /* istanbul ignore next */ []));
10308
10650
  _thumbClass = computed(() => {
10309
- const base = `${this._sizeConfig().thumb} absolute top-[2px] left-[2px] bg-clx-surface rounded-full shadow-sm transition-all duration-200`;
10651
+ const base = `${this._sizeConfig().thumb} absolute top-[2px] left-[2px] bg-clx-surface rounded-full shadow-sm transition-transform duration-200`;
10310
10652
  return this._checked() ? `${base} ${this._sizeConfig().translate}` : base;
10311
10653
  }, ...(ngDevMode ? [{ debugName: "_thumbClass" }] : /* istanbul ignore next */ []));
10312
10654
  _labelClass = computed(() => `${this._sizeConfig().label} text-clx-text-label leading-none`, ...(ngDevMode ? [{ debugName: "_labelClass" }] : /* istanbul ignore next */ []));
@@ -10488,13 +10830,13 @@ class ClxTextareaComponent {
10488
10830
  _wrapperClass = computed(() => {
10489
10831
  const statusCfg = this._statusCfg();
10490
10832
  if (this._disabled()) {
10491
- return `flex items-stretch ${resolveRadius(this._themeSvc.config().borderRadius)} border ${CLX_BORDER_DISABLED} overflow-hidden transition-all duration-200`;
10833
+ return `flex items-stretch ${resolveRadius(this._themeSvc.config().borderRadius)} border ${CLX_BORDER_DISABLED} overflow-hidden transition-[border-color,box-shadow] duration-200`;
10492
10834
  }
10493
10835
  const focusCls = this.status() !== 'default'
10494
10836
  ? statusCfg.ring.replace(/focus:/g, 'focus-within:')
10495
10837
  : resolveColor(this._color()).focusWithin;
10496
10838
  const border = statusCfg.border;
10497
- return `flex items-stretch ${resolveRadius(this._themeSvc.config().borderRadius)} border ${border} ${focusCls} overflow-hidden transition-all duration-200`;
10839
+ return `flex items-stretch ${resolveRadius(this._themeSvc.config().borderRadius)} border ${border} ${focusCls} overflow-hidden transition-[border-color,box-shadow] duration-200`;
10498
10840
  }, ...(ngDevMode ? [{ debugName: "_wrapperClass" }] : /* istanbul ignore next */ []));
10499
10841
  /** Prefix icon cell — flex column, full height of the wrapper */
10500
10842
  _prefixCellCls = computed(() => {
@@ -10728,7 +11070,7 @@ class ClxUploadComponent {
10728
11070
  _zoneClass = computed(() => {
10729
11071
  const base = [
10730
11072
  'flex flex-col items-center justify-center text-center',
10731
- `w-full ${resolveContainerRadius(this._themeSvc.config().borderRadius)} border-2 border-dashed transition-all duration-200`,
11073
+ `w-full ${resolveContainerRadius(this._themeSvc.config().borderRadius)} border-2 border-dashed transition-colors duration-200`,
10732
11074
  this._sizeConfig().zone,
10733
11075
  ].join(' ');
10734
11076
  const t = resolveColor(this._color());
@@ -11209,7 +11551,7 @@ class ClxWizardComponent {
11209
11551
  }, ...(ngDevMode ? [{ debugName: "_rootClass" }] : /* istanbul ignore next */ []));
11210
11552
  // ── Circle (vertical) ─────────────────────────────────────────────────────
11211
11553
  _circleClass(i) {
11212
- const base = 'relative z-10 flex items-center justify-center w-9 h-9 rounded-full transition-all duration-200 shrink-0 focus:outline-none';
11554
+ const base = 'relative z-10 flex items-center justify-center w-9 h-9 rounded-full transition-colors duration-200 shrink-0 focus:outline-none';
11213
11555
  const status = this._stepStatus(i);
11214
11556
  const color = this._color();
11215
11557
  const ptr = !this._isTabDisabled(i) ? 'cursor-pointer' : 'cursor-default';
@@ -11693,12 +12035,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
11693
12035
  }], propDecorators: { logo: [{ type: i0.Input, args: [{ isSignal: true, alias: "logo", required: true }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }] } });
11694
12036
 
11695
12037
  // ── Item geometry ──────────────────────────────────────────────────────────────
11696
- const NAV_ITEM_BASE = 'flex items-center gap-3 w-full text-sm font-medium transition-colors duration-150 cursor-pointer select-none';
12038
+ const NAV_ITEM_BASE = 'flex items-center gap-3 w-full text-sm font-medium cursor-pointer select-none';
11697
12039
  const NAV_ITEM_L1 = 'px-6 py-3';
11698
12040
  const NAV_ITEM_L2 = 'pl-4 pr-3 py-2';
11699
12041
  const NAV_ITEM_IDLE = 'text-clx-text-subtle';
11700
12042
  // ── Children container ─────────────────────────────────────────────────────────
11701
- const NAV_CHILDREN_WRAPPER = 'grid transition-all duration-300 ease-in-out';
12043
+ const NAV_CHILDREN_WRAPPER = 'grid transition-[grid-template-rows] duration-300 ease-in-out';
11702
12044
  const NAV_CHILDREN_INNER = 'overflow-hidden';
11703
12045
  const NAV_CHILDREN_LIST = 'ml-6 py-1 space-y-0.5';
11704
12046
 
@@ -11713,9 +12055,7 @@ class ClxMenuItemComponent {
11713
12055
  routerLink = input('', ...(ngDevMode ? [{ debugName: "routerLink" }] : /* istanbul ignore next */ []));
11714
12056
  routerLinkActiveOptions = input({ exact: false }, ...(ngDevMode ? [{ debugName: "routerLinkActiveOptions" }] : /* istanbul ignore next */ []));
11715
12057
  itemClick = output();
11716
- _inheritedColor = signal(null, ...(ngDevMode ? [{ debugName: "_inheritedColor" }] : /* istanbul ignore next */ []));
11717
- _resolvedColor = computed(() => this._inheritedColor() ?? this.color() ?? this._themeSvc.config().primaryColor, ...(ngDevMode ? [{ debugName: "_resolvedColor" }] : /* istanbul ignore next */ []));
11718
- inheritColor(c) { this._inheritedColor.set(c); }
12058
+ _resolvedColor = computed(() => this.color() ?? this._themeSvc.sidebarItemColor(), ...(ngDevMode ? [{ debugName: "_resolvedColor" }] : /* istanbul ignore next */ []));
11719
12059
  _geom = computed(() => {
11720
12060
  if (this.collapsed())
11721
12061
  return 'w-full py-3 px-0 justify-center';
@@ -11723,8 +12063,8 @@ class ClxMenuItemComponent {
11723
12063
  }, ...(ngDevMode ? [{ debugName: "_geom" }] : /* istanbul ignore next */ []));
11724
12064
  _iconCls = computed(() => resolveColor(this._resolvedColor()).textSubtle, ...(ngDevMode ? [{ debugName: "_iconCls" }] : /* istanbul ignore next */ []));
11725
12065
  _idleCls = computed(() => {
11726
- const t = resolveColor(this._resolvedColor());
11727
- return `${NAV_ITEM_BASE} ${this._geom()} ${NAV_ITEM_IDLE} ${t.hoverBgMuted}`;
12066
+ const hover = resolveColor(this._resolvedColor()).hoverBgMuted;
12067
+ return `${NAV_ITEM_BASE} ${this._geom()} ${hover}`;
11728
12068
  }, ...(ngDevMode ? [{ debugName: "_idleCls" }] : /* istanbul ignore next */ []));
11729
12069
  _activeCls = computed(() => {
11730
12070
  const t = resolveColor(this._resolvedColor());
@@ -11744,10 +12084,10 @@ class ClxMenuItemComponent {
11744
12084
  [class]="rla.isActive ? _activeCls() : _idleCls()"
11745
12085
  [title]="collapsed() ? label() : ''">
11746
12086
  @if (icon()) {
11747
- <span clx-icon [name]="icon()" size="sm" [class]="rla.isActive ? _iconCls() : 'text-clx-text-subtle'"></span>
12087
+ <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
11748
12088
  }
11749
12089
  @if (!collapsed()) {
11750
- <span class="flex-1 text-left truncate">{{ label() }}</span>
12090
+ <span class="flex-1 text-left truncate" [class]="rla.isActive ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
11751
12091
  }
11752
12092
  </a>
11753
12093
  } @else {
@@ -11757,10 +12097,10 @@ class ClxMenuItemComponent {
11757
12097
  [title]="collapsed() ? label() : ''"
11758
12098
  (click)="itemClick.emit()">
11759
12099
  @if (icon()) {
11760
- <span clx-icon [name]="icon()" size="sm" [class]="active() ? _iconCls() : 'text-clx-text-subtle'"></span>
12100
+ <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
11761
12101
  }
11762
12102
  @if (!collapsed()) {
11763
- <span class="flex-1 text-left truncate">{{ label() }}</span>
12103
+ <span class="flex-1 text-left truncate" [class]="active() ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
11764
12104
  }
11765
12105
  </button>
11766
12106
  }
@@ -11782,10 +12122,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
11782
12122
  [class]="rla.isActive ? _activeCls() : _idleCls()"
11783
12123
  [title]="collapsed() ? label() : ''">
11784
12124
  @if (icon()) {
11785
- <span clx-icon [name]="icon()" size="sm" [class]="rla.isActive ? _iconCls() : 'text-clx-text-subtle'"></span>
12125
+ <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
11786
12126
  }
11787
12127
  @if (!collapsed()) {
11788
- <span class="flex-1 text-left truncate">{{ label() }}</span>
12128
+ <span class="flex-1 text-left truncate" [class]="rla.isActive ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
11789
12129
  }
11790
12130
  </a>
11791
12131
  } @else {
@@ -11795,10 +12135,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
11795
12135
  [title]="collapsed() ? label() : ''"
11796
12136
  (click)="itemClick.emit()">
11797
12137
  @if (icon()) {
11798
- <span clx-icon [name]="icon()" size="sm" [class]="active() ? _iconCls() : 'text-clx-text-subtle'"></span>
12138
+ <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
11799
12139
  }
11800
12140
  @if (!collapsed()) {
11801
- <span class="flex-1 text-left truncate">{{ label() }}</span>
12141
+ <span class="flex-1 text-left truncate" [class]="active() ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
11802
12142
  }
11803
12143
  </button>
11804
12144
  }
@@ -11814,14 +12154,13 @@ class ClxMenuComponent {
11814
12154
  label = input.required(...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
11815
12155
  icon = input('', ...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
11816
12156
  color = input(undefined, ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
11817
- _color = computed(() => this.color() ?? this._themeSvc.config().primaryColor, ...(ngDevMode ? [{ debugName: "_color" }] : /* istanbul ignore next */ []));
12157
+ _color = computed(() => this.color() ?? this._themeSvc.sidebarMenuColor(), ...(ngDevMode ? [{ debugName: "_color" }] : /* istanbul ignore next */ []));
11818
12158
  collapsed = input(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
11819
12159
  active = input(false, ...(ngDevMode ? [{ debugName: "active" }] : /* istanbul ignore next */ []));
11820
12160
  routerLink = input('', ...(ngDevMode ? [{ debugName: "routerLink" }] : /* istanbul ignore next */ []));
11821
12161
  routerLinkActiveOptions = input({ exact: false }, ...(ngDevMode ? [{ debugName: "routerLinkActiveOptions" }] : /* istanbul ignore next */ []));
11822
12162
  _isExpanded = signal(false, ...(ngDevMode ? [{ debugName: "_isExpanded" }] : /* istanbul ignore next */ []));
11823
12163
  _rla;
11824
- _childItems;
11825
12164
  _destroyRef = inject(DestroyRef);
11826
12165
  constructor() {
11827
12166
  effect(() => { if (this.active())
@@ -11849,26 +12188,16 @@ class ClxMenuComponent {
11849
12188
  return `${base} ${t.textSubtle} font-semibold`;
11850
12189
  return `${base} ${t.textSubtle} font-semibold border-l-2 ${t.borderLight} !pl-[22px]`;
11851
12190
  }
11852
- return `${base} ${NAV_ITEM_IDLE} ${t.hoverBgMuted}`;
12191
+ return `${base} ${t.hoverBgMuted}`;
11853
12192
  }
11854
12193
  NAV_CHILDREN_WRAPPER = NAV_CHILDREN_WRAPPER;
11855
12194
  NAV_CHILDREN_INNER = NAV_CHILDREN_INNER;
11856
12195
  NAV_CHILDREN_LIST = NAV_CHILDREN_LIST;
11857
- ngAfterContentInit() {
11858
- this._syncChildren();
11859
- this._childItems.changes
11860
- .pipe(takeUntilDestroyed(this._destroyRef))
11861
- .subscribe(() => this._syncChildren());
11862
- }
11863
- _syncChildren() {
11864
- const color = this._color();
11865
- this._childItems.forEach(c => c.inheritColor(color));
11866
- }
11867
12196
  _toggle() {
11868
12197
  this._isExpanded.update(v => !v);
11869
12198
  }
11870
12199
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxMenuComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
11871
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxMenuComponent, isStandalone: true, selector: "clx-menu", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, routerLink: { classPropertyName: "routerLink", publicName: "routerLink", isSignal: true, isRequired: false, transformFunction: null }, routerLinkActiveOptions: { classPropertyName: "routerLinkActiveOptions", publicName: "routerLinkActiveOptions", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block" }, queries: [{ propertyName: "_childItems", predicate: ClxMenuItemComponent, descendants: true }], viewQueries: [{ propertyName: "_rla", first: true, predicate: RouterLinkActive, descendants: true }], ngImport: i0, template: `
12200
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxMenuComponent, isStandalone: true, selector: "clx-menu", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, routerLink: { classPropertyName: "routerLink", publicName: "routerLink", isSignal: true, isRequired: false, transformFunction: null }, routerLinkActiveOptions: { classPropertyName: "routerLinkActiveOptions", publicName: "routerLinkActiveOptions", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block" }, viewQueries: [{ propertyName: "_rla", first: true, predicate: RouterLinkActive, descendants: true }], ngImport: i0, template: `
11872
12201
  @if (routerLink()) {
11873
12202
  <a
11874
12203
  [routerLink]="routerLink()"
@@ -11882,7 +12211,7 @@ class ClxMenuComponent {
11882
12211
  <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
11883
12212
  }
11884
12213
  @if (!collapsed()) {
11885
- <span class="flex-1 text-left truncate">{{ label() }}</span>
12214
+ <span class="flex-1 text-left truncate" [class]="rla.isActive ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
11886
12215
  <span
11887
12216
  clx-icon
11888
12217
  name="expand_more"
@@ -11902,7 +12231,7 @@ class ClxMenuComponent {
11902
12231
  <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
11903
12232
  }
11904
12233
  @if (!collapsed()) {
11905
- <span class="flex-1 text-left truncate">{{ label() }}</span>
12234
+ <span class="flex-1 text-left truncate" [class]="active() ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
11906
12235
  <span
11907
12236
  clx-icon
11908
12237
  name="expand_more"
@@ -11945,7 +12274,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
11945
12274
  <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
11946
12275
  }
11947
12276
  @if (!collapsed()) {
11948
- <span class="flex-1 text-left truncate">{{ label() }}</span>
12277
+ <span class="flex-1 text-left truncate" [class]="rla.isActive ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
11949
12278
  <span
11950
12279
  clx-icon
11951
12280
  name="expand_more"
@@ -11965,7 +12294,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
11965
12294
  <span clx-icon [name]="icon()" size="sm" [class]="_iconCls()"></span>
11966
12295
  }
11967
12296
  @if (!collapsed()) {
11968
- <span class="flex-1 text-left truncate">{{ label() }}</span>
12297
+ <span class="flex-1 text-left truncate" [class]="active() ? '' : 'text-clx-text-subtle'">{{ label() }}</span>
11969
12298
  <span
11970
12299
  clx-icon
11971
12300
  name="expand_more"
@@ -11994,18 +12323,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
11994
12323
  }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], active: [{ type: i0.Input, args: [{ isSignal: true, alias: "active", required: false }] }], routerLink: [{ type: i0.Input, args: [{ isSignal: true, alias: "routerLink", required: false }] }], routerLinkActiveOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "routerLinkActiveOptions", required: false }] }], _rla: [{
11995
12324
  type: ViewChild,
11996
12325
  args: [RouterLinkActive]
11997
- }], _childItems: [{
11998
- type: ContentChildren,
11999
- args: [ClxMenuItemComponent, { descendants: true }]
12000
12326
  }] } });
12001
12327
 
12002
12328
  class ClxNavGroupComponent {
12003
12329
  label = input.required(...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
12004
12330
  collapsed = input(false, ...(ngDevMode ? [{ debugName: "collapsed" }] : /* istanbul ignore next */ []));
12331
+ color = input(undefined, ...(ngDevMode ? [{ debugName: "color" }] : /* istanbul ignore next */ []));
12332
+ labelClass = computed(() => {
12333
+ const c = this.color();
12334
+ return c ? resolveColor(c).textSubtle : 'text-clx-text-subtle';
12335
+ }, ...(ngDevMode ? [{ debugName: "labelClass" }] : /* istanbul ignore next */ []));
12005
12336
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: ClxNavGroupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
12006
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxNavGroupComponent, isStandalone: true, selector: "clx-nav-group", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: `
12337
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: ClxNavGroupComponent, isStandalone: true, selector: "clx-nav-group", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null }, color: { classPropertyName: "color", publicName: "color", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "block" }, ngImport: i0, template: `
12007
12338
  @if (!collapsed()) {
12008
- <span class="block px-6 pt-5 pb-1 text-[10px] font-bold uppercase tracking-widest text-clx-text-subtle select-none">{{ label() }}</span>
12339
+ <span class="block px-6 pt-5 pb-1 text-[10px] font-bold uppercase tracking-widest select-none" [class]="labelClass()">{{ label() }}</span>
12009
12340
  } @else {
12010
12341
  <span class="flex items-center justify-center w-full py-3">
12011
12342
  <span class="w-1.5 h-1.5 rounded-full bg-clx-border"></span>
@@ -12020,7 +12351,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
12020
12351
  standalone: true,
12021
12352
  template: `
12022
12353
  @if (!collapsed()) {
12023
- <span class="block px-6 pt-5 pb-1 text-[10px] font-bold uppercase tracking-widest text-clx-text-subtle select-none">{{ label() }}</span>
12354
+ <span class="block px-6 pt-5 pb-1 text-[10px] font-bold uppercase tracking-widest select-none" [class]="labelClass()">{{ label() }}</span>
12024
12355
  } @else {
12025
12356
  <span class="flex items-center justify-center w-full py-3">
12026
12357
  <span class="w-1.5 h-1.5 rounded-full bg-clx-border"></span>
@@ -12031,7 +12362,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
12031
12362
  changeDetection: ChangeDetectionStrategy.OnPush,
12032
12363
  host: { class: 'block' },
12033
12364
  }]
12034
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }] } });
12365
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], color: [{ type: i0.Input, args: [{ isSignal: true, alias: "color", required: false }] }] } });
12035
12366
 
12036
12367
  class ClxProfileComponent {
12037
12368
  // ── Inputs ──────────────────────────────────────────────────────────────────
@@ -12252,7 +12583,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
12252
12583
  // ── Base classes ─────────────────────────────────────────────────────────────
12253
12584
  const TABLE_BASE_CLASS = 'w-full text-left border-collapse text-sm';
12254
12585
  const TABLE_HEADER_CELL_CLASS = 'px-4 py-3 font-semibold whitespace-nowrap';
12255
- const TABLE_CELL_CLASS = 'px-4 py-3 border-b border-slate-100 text-slate-700';
12586
+ const TABLE_CELL_CLASS = 'px-4 py-3 border-b border-clx-border-soft text-clx-text-label';
12256
12587
  const TABLE_ROW_CLASS = '';
12257
12588
  // ── Alignment ────────────────────────────────────────────────────────────────
12258
12589
  const TABLE_ALIGN_MAP = {
@@ -14585,7 +14916,7 @@ class ClxProgressBarComponent {
14585
14916
 
14586
14917
  <!-- Fill -->
14587
14918
  <div
14588
- class="h-full rounded-full transition-all duration-500 ease-out"
14919
+ class="h-full rounded-full transition-[width,background-color] duration-500 ease-out"
14589
14920
  [class]="_fillCls()"
14590
14921
  [style.width]="variant() === 'indeterminate' ? '40%' : _pct() + '%'">
14591
14922
  </div>
@@ -14620,7 +14951,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
14620
14951
 
14621
14952
  <!-- Fill -->
14622
14953
  <div
14623
- class="h-full rounded-full transition-all duration-500 ease-out"
14954
+ class="h-full rounded-full transition-[width,background-color] duration-500 ease-out"
14624
14955
  [class]="_fillCls()"
14625
14956
  [style.width]="variant() === 'indeterminate' ? '40%' : _pct() + '%'">
14626
14957
  </div>
@@ -14873,5 +15204,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImpo
14873
15204
  * Generated bundle index. Do not edit.
14874
15205
  */
14875
15206
 
14876
- export { CLX_ADDON_BORDER, CLX_ADDON_TEXT, CLX_ALERT_OPTIONS, CLX_ALERT_RESOLVE, CLX_BG_ADDON, CLX_BG_DISABLED, CLX_BG_ICON_WRAP, CLX_BG_SECTION, CLX_BG_SURFACE, CLX_BORDER_DEFAULT, CLX_BORDER_DISABLED, CLX_BORDER_MEDIUM, CLX_COLOR_HEX, CLX_COLOR_HEX_100, CLX_COLOR_MAP, CLX_MODAL_ANIM_CONFIG, CLX_MODAL_DATA, CLX_MODAL_REF, CLX_OPTION_DISABLED, CLX_PLACEHOLDER, CLX_RADIO_GROUP, CLX_RADIUS_MAP, CLX_TEXT_BODY, CLX_TEXT_DISABLED, CLX_TEXT_HEADING, CLX_TEXT_HINT, CLX_TEXT_IDLE, CLX_TEXT_INPUT, CLX_TEXT_LABEL, CLX_TEXT_OPTION, CLX_TEXT_SUBTITLE, CLX_TEXT_TITLE, CLX_THEME_CONFIG, CLX_THEME_DEFAULTS, CLX_TOAST_DEFAULTS, ClxAlertComponent, ClxAlertService, ClxAnimateDirective, ClxAnimateGroupDirective, ClxAnimateService, ClxAppLayoutComponent, ClxAvatarComponent, ClxBadgeComponent, ClxBrandComponent, ClxButtonComponent, ClxButtonGroupComponent, ClxCardBodyDirective, ClxCardComponent, ClxCardFooterDirective, ClxCardHeaderDirective, ClxCarouselComponent, ClxCarouselDirective, ClxCartComponent, ClxCartSummaryDrawer, ClxCellDirective, ClxChartComponent, ClxCheckboxComponent, ClxColorPickerComponent, ClxColumnDefDirective, ClxDateRangePickerComponent, ClxDatepickerComponent, ClxDrawerComponent, ClxDrawerService, ClxEditorComponent, ClxEditorLinkModalComponent, ClxFilterPanelComponent, ClxHeaderCellDirective, ClxIconComponent, ClxInputComponent, ClxListComponent, ClxListItemComponent, ClxMenuComponent, ClxMenuItemComponent, ClxModalComponent, ClxModalService, ClxNavGroupComponent, ClxNumberComponent, ClxPageEmptyComponent, ClxPageNotFoundComponent, ClxPageServerErrorComponent, ClxPageUnauthorizedComponent, ClxPaginationComponent, ClxProductComponent, ClxProductDetailComponent, ClxProfileComponent, ClxProgressBarComponent, ClxRadioComponent, ClxRadioGroupComponent, ClxRatingComponent, ClxSelectComponent, ClxSkeletonComponent, ClxSliderComponent, ClxSpinnerComponent, ClxStatCardComponent, ClxStepComponent, ClxStepperComponent, ClxSwitchComponent, ClxTabDirective, ClxTableComponent, ClxTabsComponent, ClxTagComponent, ClxTextareaComponent, ClxThemeService, ClxTimelineComponent, ClxTimelineItemComponent, ClxTimepickerComponent, ClxToastComponent, ClxToastContainerComponent, ClxToastService, ClxTooltipComponent, ClxTooltipDirective, ClxTreeComponent, ClxUploadComponent, ClxWishlistComponent, ClxWizardComponent, TIMEPICKER_SIZE_MAP, parseColorInput, provideCodexlyTheme, resolveColor, resolveContainerRadius, resolveRadius };
15207
+ export { CLX_ADDON_BORDER, CLX_ADDON_TEXT, CLX_ALERT_OPTIONS, CLX_ALERT_RESOLVE, CLX_BG_ADDON, CLX_BG_DISABLED, CLX_BG_ICON_WRAP, CLX_BG_SECTION, CLX_BG_SURFACE, CLX_BORDER_DEFAULT, CLX_BORDER_DISABLED, CLX_BORDER_MEDIUM, CLX_COLOR_HEX, CLX_COLOR_HEX_100, CLX_COLOR_MAP, CLX_FONT_CATALOG, CLX_MODAL_ANIM_CONFIG, CLX_MODAL_DATA, CLX_MODAL_REF, CLX_OPTION_DISABLED, CLX_PLACEHOLDER, CLX_RADIO_GROUP, CLX_RADIUS_MAP, CLX_TEXT_BODY, CLX_TEXT_DISABLED, CLX_TEXT_HEADING, CLX_TEXT_HINT, CLX_TEXT_IDLE, CLX_TEXT_INPUT, CLX_TEXT_LABEL, CLX_TEXT_OPTION, CLX_TEXT_SUBTITLE, CLX_TEXT_TITLE, CLX_THEME_CONFIG, CLX_THEME_DEFAULTS, CLX_TOAST_DEFAULTS, ClxAlertComponent, ClxAlertService, ClxAnimateDirective, ClxAnimateGroupDirective, ClxAnimateService, ClxAppLayoutComponent, ClxAvatarComponent, ClxBadgeComponent, ClxBrandComponent, ClxButtonComponent, ClxButtonGroupComponent, ClxCardBodyDirective, ClxCardComponent, ClxCardFooterDirective, ClxCardHeaderDirective, ClxCarouselComponent, ClxCarouselDirective, ClxCartComponent, ClxCartSummaryDrawer, ClxCellDirective, ClxChartComponent, ClxCheckboxComponent, ClxColorPickerComponent, ClxColumnDefDirective, ClxDateRangePickerComponent, ClxDatepickerComponent, ClxDrawerComponent, ClxDrawerService, ClxEditorComponent, ClxEditorLinkModalComponent, ClxFilterPanelComponent, ClxHeaderCellDirective, ClxIconComponent, ClxInputComponent, ClxListComponent, ClxListItemComponent, ClxMenuComponent, ClxMenuItemComponent, ClxModalComponent, ClxModalService, ClxNavGroupComponent, ClxNumberComponent, ClxPageEmptyComponent, ClxPageNotFoundComponent, ClxPageServerErrorComponent, ClxPageUnauthorizedComponent, ClxPaginationComponent, ClxProductComponent, ClxProductDetailComponent, ClxProfileComponent, ClxProgressBarComponent, ClxRadioComponent, ClxRadioGroupComponent, ClxRatingComponent, ClxSelectComponent, ClxSkeletonComponent, ClxSliderComponent, ClxSpinnerComponent, ClxStatCardComponent, ClxStepComponent, ClxStepperComponent, ClxSwitchComponent, ClxTabDirective, ClxTableComponent, ClxTabsComponent, ClxTagComponent, ClxTextareaComponent, ClxThemeService, ClxTimelineComponent, ClxTimelineItemComponent, ClxTimepickerComponent, ClxToastComponent, ClxToastContainerComponent, ClxToastService, ClxTooltipComponent, ClxTooltipDirective, ClxTreeComponent, ClxUploadComponent, ClxWishlistComponent, ClxWizardComponent, TIMEPICKER_SIZE_MAP, parseColorInput, provideCodexlyTheme, resolveColor, resolveContainerRadius, resolveRadius };
14877
15208
  //# sourceMappingURL=codexly-ui.mjs.map