@khipu/design-system 0.2.0-alpha.13 → 0.2.0-alpha.130

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.
@@ -11,7 +11,7 @@
11
11
  *
12
12
  * AUTO-GENERATED FILE - DO NOT EDIT MANUALLY
13
13
  * Source: design-system/src/tokens/tokens.json
14
- * Generated: 2026-05-18T17:09:39.680Z
14
+ * Generated: 2026-07-21T15:12:54.686Z
15
15
  *
16
16
  * To regenerate:
17
17
  * cd design-system && npm run tokens:generate
@@ -48,15 +48,21 @@
48
48
  --kds-color-success-main: #2E7D32;
49
49
  --kds-color-success-light: #4CAF50;
50
50
  --kds-color-success-dark: #1B5E20;
51
+ --kds-color-success-contrast: #FFFFFF;
51
52
  --kds-color-warning-main: #EF6C00;
52
53
  --kds-color-warning-light: #FF9800;
53
54
  --kds-color-warning-dark: #E65100;
55
+ --kds-color-warning-contrast: #FFFFFF;
56
+ --kds-color-warning-warm: #8A6D1A;
54
57
  --kds-color-error-main: #D32F2F;
55
58
  --kds-color-error-light: #EF5350;
56
59
  --kds-color-error-dark: #C62828;
60
+ --kds-color-error-contrast: #FFFFFF;
57
61
  --kds-color-info-main: #0288D1;
58
62
  --kds-color-info-light: #03A9F4;
59
63
  --kds-color-info-dark: #01579B;
64
+ --kds-color-info-contrast: #FFFFFF;
65
+ --kds-color-info-blue: #5A5FE0;
60
66
 
61
67
  /* Text colors */
62
68
  --kds-color-text-primary: #333333;
@@ -69,6 +75,7 @@
69
75
  --kds-color-background-paper: #FFFFFF;
70
76
  --kds-color-background-elevated: #FAFAFA;
71
77
  --kds-color-background-muted: #F4F4F7;
78
+ --kds-color-background-brand-subtle: #F0F0FA;
72
79
 
73
80
  /* Action colors */
74
81
  --kds-color-action-active: rgba(0, 0, 0, 0.56);
@@ -134,6 +141,8 @@
134
141
  --kds-font-weight-bold: 700;
135
142
 
136
143
  /* Font sizes (base scale xs–4xl: see responsive section below) */
144
+ --kds-font-size-decimal-sup: 0.5em;
145
+
137
146
  /* Line heights */
138
147
  --kds-line-height-tight: 1.2;
139
148
  --kds-line-height-snug: 1.375;
@@ -355,6 +364,7 @@
355
364
  RESPONSIVE TYPOGRAPHY (MOBILE BASE)
356
365
  ========================================================================== */
357
366
 
367
+ --kds-font-size-2xs: 0.5625rem;
358
368
 
359
369
  /* Base font-size scale */
360
370
  --kds-font-size-xs: 0.6875rem;
@@ -384,6 +394,112 @@
384
394
 
385
395
  }
386
396
 
397
+ /* ============================================================================
398
+ DARK MODE COLOR TOKENS
399
+ Override only color variables; everything else inherits from :root.
400
+ Activated via [data-theme="dark"] on <html>, <body>, or a wrapper.
401
+ ============================================================================ */
402
+
403
+ [data-theme="dark"] {
404
+
405
+ /* Primary palette - Khipu brand */
406
+ --kds-color-primary-main: #E3B5FF;
407
+ --kds-color-primary-light: #F1D6FF;
408
+ --kds-color-primary-dark: #4C0676;
409
+ --kds-color-primary-contrast: rgba(76, 6, 118, 0.87);
410
+ --kds-color-primary-hover: rgba(227, 181, 255, 0.08);
411
+ --kds-color-primary-selected: rgba(227, 181, 255, 0.16);
412
+ --kds-color-primary-focus: rgba(227, 181, 255, 0.24);
413
+ --kds-color-primary-focus-visible: rgba(227, 181, 255, 0.40);
414
+ --kds-color-primary-outlined-border: rgba(227, 181, 255, 0.50);
415
+
416
+ /* Secondary palette */
417
+ --kds-color-secondary-main: #B5EAFF;
418
+ --kds-color-secondary-light: #D6F3FF;
419
+ --kds-color-secondary-dark: #065676;
420
+ --kds-color-secondary-contrast: rgba(6, 86, 118, 0.87);
421
+
422
+ /* Semantic colors */
423
+ --kds-color-success-main: #2E7D32;
424
+ --kds-color-success-light: #4CAF50;
425
+ --kds-color-success-dark: #1B5E20;
426
+ --kds-color-success-contrast: #FFFFFF;
427
+ --kds-color-warning-main: #EF6C00;
428
+ --kds-color-warning-light: #FF9800;
429
+ --kds-color-warning-dark: #E65100;
430
+ --kds-color-warning-contrast: #FFFFFF;
431
+ --kds-color-warning-warm: #8A6D1A;
432
+ --kds-color-error-main: #D32F2F;
433
+ --kds-color-error-light: #EF5350;
434
+ --kds-color-error-dark: #C62828;
435
+ --kds-color-error-contrast: #FFFFFF;
436
+ --kds-color-info-main: #0288D1;
437
+ --kds-color-info-light: #03A9F4;
438
+ --kds-color-info-dark: #01579B;
439
+ --kds-color-info-contrast: #FFFFFF;
440
+ --kds-color-info-blue: #5A5FE0;
441
+
442
+ /* Text colors */
443
+ --kds-color-text-primary: rgba(255, 255, 255, 1);
444
+ --kds-color-text-secondary: rgba(255, 255, 255, 0.70);
445
+ --kds-color-text-disabled: rgba(255, 255, 255, 0.38);
446
+ --kds-color-text-hint: rgba(255, 255, 255, 0.38);
447
+
448
+ /* Background colors */
449
+ --kds-color-background-default: #181818;
450
+ --kds-color-background-paper: #121212;
451
+ --kds-color-background-elevated: #2C2C2C;
452
+ --kds-color-background-muted: #1E1E1E;
453
+ --kds-color-background-brand-subtle: #1E1030;
454
+
455
+ /* Action colors */
456
+ --kds-color-action-active: rgba(255, 255, 255, 0.56);
457
+ --kds-color-action-hover: rgba(255, 255, 255, 0.08);
458
+ --kds-color-action-selected: rgba(255, 255, 255, 0.16);
459
+ --kds-color-action-disabled: rgba(255, 255, 255, 0.38);
460
+ --kds-color-action-disabled-bg: rgba(255, 255, 255, 0.12);
461
+ --kds-color-action-focus: rgba(255, 255, 255, 0.12);
462
+
463
+ /* Divider */
464
+ --kds-color-divider: rgba(255, 255, 255, 0.12);
465
+
466
+ /* Gray palette - Neutral colors */
467
+ --kds-color-gray-50: #F9FAFB;
468
+ --kds-color-gray-100: #F3F4F6;
469
+ --kds-color-gray-200: #E5E7EB;
470
+ --kds-color-gray-300: #D1D5DB;
471
+ --kds-color-gray-400: #9CA3AF;
472
+ --kds-color-gray-500: #6B7280;
473
+ --kds-color-gray-600: #4B5563;
474
+ --kds-color-gray-700: #374151;
475
+ --kds-color-gray-800: #1F2937;
476
+ --kds-color-gray-900: #111827;
477
+
478
+ /* Input borders */
479
+ --kds-color-input-enabled-border: rgba(255, 255, 255, 0.23);
480
+ --kds-color-input-hover-border: rgba(255, 255, 255, 1);
481
+
482
+ /* Snackbar backgrounds */
483
+ --kds-snackbar-success-bg: rgba(76, 175, 80, 0.16);
484
+ --kds-snackbar-warning-bg: rgba(255, 152, 0, 0.16);
485
+ --kds-snackbar-error-bg: rgba(239, 83, 80, 0.16);
486
+ --kds-snackbar-info-bg: rgba(3, 169, 244, 0.16);
487
+
488
+ /* Alert backgrounds */
489
+ --kds-alert-info-bg: rgba(2, 136, 209, 0.12);
490
+ --kds-alert-warning-bg: rgba(239, 108, 0, 0.12);
491
+ --kds-alert-success-bg: rgba(46, 125, 50, 0.12);
492
+ --kds-alert-error-bg: rgba(211, 47, 47, 0.12);
493
+ --kds-alert-success-text: #6AE079;
494
+ --kds-alert-warning-text: #FFB74D;
495
+ --kds-alert-error-text: #EF9A9A;
496
+ --kds-alert-info-text: #4FC3F7;
497
+ --kds-alert-success-border: #4CAF50;
498
+ --kds-alert-warning-border: #FF9800;
499
+ --kds-alert-error-border: #EF5350;
500
+ --kds-alert-info-border: #03A9F4;
501
+ }
502
+
387
503
  /* ============================================================================
388
504
  RESPONSIVE TOKENS (Mobile-First)
389
505
  ============================================================================ */
@@ -402,6 +518,7 @@
402
518
  /* Tablet (600px+) - Typography */
403
519
  @media (min-width: 600px) {
404
520
  :root {
521
+ --kds-font-size-2xs: 0.625rem;
405
522
 
406
523
  /* Base font-size scale */
407
524
  --kds-font-size-xs: 0.75rem;
@@ -439,6 +556,7 @@
439
556
  /* Desktop (840px+) - Typography */
440
557
  @media (min-width: 840px) {
441
558
  :root {
559
+ --kds-font-size-2xs: 0.625rem;
442
560
 
443
561
  /* Base font-size scale */
444
562
  --kds-font-size-xs: 0.75rem;
@@ -510,7 +628,8 @@
510
628
  /* Import core design tokens (auto-generated) */
511
629
  :root,
512
630
  body.light,
513
- body.dark {
631
+ body.dark,
632
+ [data-theme="dark"] {
514
633
  /* ========================================
515
634
  Map core tokens to BeerCSS-specific names
516
635
  (BeerCSS may use different naming conventions)
@@ -688,7 +807,8 @@ body.dark {
688
807
  --kds-color-danger-soft: var(--kds-alert-error-bg); /* #FEF2F2 */
689
808
 
690
809
  /* Bottom Sheet */
691
- --kds-bottom-sheet-max-width: 480px;
810
+ /* Mismo ancho que la card principal del flujo (stage narrow). */
811
+ --kds-bottom-sheet-max-width: var(--kds-stage-narrow-max-width);
692
812
  --kds-bottom-sheet-shadow: 0 -8px var(--kds-spacing-3-5) rgba(0, 0, 0, 0.18);
693
813
 
694
814
  /* Status Block */
@@ -710,23 +830,53 @@ body.dark {
710
830
  }
711
831
 
712
832
  /* ========================================
713
- Force Light Mode Only
833
+ Dark mode overrides — extended payment-flow tokens
834
+ These were authored as light-only literals in the group above (subtle
835
+ surfaces, soft backgrounds, borders, hovers, shell). Give them dark-aware
836
+ values so selected states / rows / hovers adapt. Additive: the light
837
+ :root / body.light values above are unchanged. Tokens already defined as
838
+ var() refs (warning-soft, danger-soft) adapt on their own and are omitted.
839
+ body.dark is included on purpose: the light group above also matches
840
+ body.dark (specificity 0,1,1), so a bare [data-theme] override (0,1,0)
841
+ would lose on a body that carries the transitional dark class.
842
+ ======================================== */
843
+ body.dark,
844
+ [data-theme="dark"] {
845
+ --kds-color-primary-faint: #1E1030; /* dark brand-tinted subtle surface (selected rows/cards) */
846
+ --kds-color-info-soft: var(--kds-alert-info-bg); /* dark translucent blue */
847
+ --kds-color-success-soft: var(--kds-alert-success-bg); /* dark translucent green */
848
+ --kds-color-border-subtle: rgba(255, 255, 255, 0.08); /* subtle divider on dark */
849
+ --kds-color-border-default: rgba(255, 255, 255, 0.16); /* standard border on dark */
850
+ --kds-color-hover-bg: rgba(255, 255, 255, 0.04); /* subtle row hover on dark */
851
+ --kds-color-shell: var(--kds-color-background-default); /* neutral page background */
852
+
853
+ /* QR featured row — dark mirror of the light purple→blue gradient (the
854
+ light literals painted a light surface under adaptive white text). */
855
+ --kds-qr-bg-gradient: linear-gradient(135deg, #1E1030 0%, #101C2C 100%);
856
+ --kds-qr-border: rgba(227, 181, 255, 0.28); /* dark primary @ same alpha as light */
857
+ --kds-qr-focus-ring: rgba(227, 181, 255, 0.35);
858
+ --kds-qr-avatar-bg: var(--kds-color-background-elevated);
859
+ --kds-qr-avatar-border: rgba(227, 181, 255, 0.3);
860
+ --kds-qr-badge-text: var(--kds-color-primary-contrast); /* dark text on the lavender badge */
861
+ }
862
+
863
+ /* ========================================
864
+ Color Scheme (explicit opt-in to dark)
714
865
  ======================================== */
715
866
 
716
- /* Force light mode - disable dark mode */
867
+ /* Default to light regardless of OS preference. Because we use `light` (not
868
+ `light dark`), the system dark-mode preference never auto-applies — dark is
869
+ reached only by an explicit [data-theme="dark"] / body.dark opt-in. */
717
870
  :root,
718
871
  html,
719
872
  body {
720
- color-scheme: light only;
873
+ color-scheme: light;
721
874
  }
722
875
 
723
- /* Override system dark mode preference */
724
- @media (prefers-color-scheme: dark) {
725
- :root,
726
- html,
727
- body {
728
- color-scheme: light only;
729
- }
876
+ /* Explicit dark theme: let native UI (form controls, scrollbars) render dark. */
877
+ [data-theme="dark"],
878
+ body.dark {
879
+ color-scheme: dark;
730
880
  }
731
881
 
732
882
  /* ========================================
@@ -839,6 +989,10 @@ a.kds-btn {
839
989
  /* Spacing - Using design tokens */
840
990
  padding: var(--kds-spacing-button-padding); /* 0 16px */
841
991
  height: var(--kds-spacing-button-min-height); /* 44px */
992
+ /* min-height además de height: en contenedores flex-column (p.ej. pantallas de
993
+ resultado/timeout) el botón es flex-item y, con solo `height`, flex-shrink lo
994
+ colapsaba a la altura del texto (~21px). min-height impide que se encoja. */
995
+ min-height: var(--kds-spacing-button-min-height); /* 44px */
842
996
 
843
997
  /* Border - Using design token */
844
998
  border-radius: var(--kds-radius-button); /* 10px */
@@ -846,7 +1000,10 @@ a.kds-btn {
846
1000
 
847
1001
  /* Interaction */
848
1002
  cursor: pointer;
849
- transition: background .15s, box-shadow .15s, transform .05s;
1003
+ transition: background-color var(--kds-transition-shorter) var(--kds-easing-standard),
1004
+ color var(--kds-transition-shorter) var(--kds-easing-standard),
1005
+ box-shadow var(--kds-transition-shorter) var(--kds-easing-standard),
1006
+ transform var(--kds-transition-shortest) var(--kds-easing-standard);
850
1007
  box-sizing: border-box;
851
1008
  vertical-align: middle;
852
1009
  }
@@ -872,7 +1029,7 @@ a.kds-btn:disabled {
872
1029
  button.kds-btn-primary,
873
1030
  a.kds-btn-primary {
874
1031
  background: var(--kds-color-primary-main); /* #8347AD */
875
- color: white;
1032
+ color: var(--kds-color-primary-contrast); /* #FFF light / dark-purple on dark (lavender bg) */
876
1033
  border: none;
877
1034
  box-shadow: 0 1px 2px rgba(131, 71, 173, .25);
878
1035
  }
@@ -893,6 +1050,18 @@ a.kds-btn-primary:focus-visible:not(:disabled) {
893
1050
  outline-offset: 2px;
894
1051
  }
895
1052
 
1053
+ /* Dark mode: the button is a light lavender with dark text, so hover/active must
1054
+ LIGHTEN (not darken) — else primary-dark bg + dark contrast text = invisible.
1055
+ Additive override; the light-mode rules above stay unchanged. */
1056
+ [data-theme="dark"] button.kds-btn-primary:hover:not(:disabled),
1057
+ [data-theme="dark"] a.kds-btn-primary:hover:not(:disabled),
1058
+ [data-theme="dark"] button.kds-btn-primary:active:not(:disabled),
1059
+ [data-theme="dark"] button.kds-btn-primary:focus-visible:not(:disabled),
1060
+ [data-theme="dark"] a.kds-btn-primary:active:not(:disabled),
1061
+ [data-theme="dark"] a.kds-btn-primary:focus-visible:not(:disabled) {
1062
+ background: var(--kds-color-primary-light);
1063
+ }
1064
+
896
1065
  /* Override disabled para primary */
897
1066
  button.kds-btn-primary:disabled,
898
1067
  a.kds-btn-primary:disabled {
@@ -978,7 +1147,13 @@ a.kds-btn-text {
978
1147
 
979
1148
  button.kds-btn-text:hover:not(:disabled),
980
1149
  a.kds-btn-text:hover:not(:disabled) {
981
- background: var(--kds-color-primary-hover); /* rgba(131, 71, 173, 0.04) */
1150
+ color: var(--kds-color-primary-dark);
1151
+ }
1152
+
1153
+ /* Dark: hover must lighten — primary-dark text is near-invisible on dark bg. */
1154
+ [data-theme="dark"] button.kds-btn-text:hover:not(:disabled),
1155
+ [data-theme="dark"] a.kds-btn-text:hover:not(:disabled) {
1156
+ color: var(--kds-color-primary-light);
982
1157
  }
983
1158
 
984
1159
  button.kds-btn-text:disabled,
@@ -988,6 +1163,30 @@ a.kds-btn-text:disabled {
988
1163
  box-shadow: none;
989
1164
  }
990
1165
 
1166
+ /* Error text (destructive, ghost) — red text, no fill/border. Para acciones
1167
+ destructivas secundarias que no deben competir con el CTA primario. */
1168
+ button.kds-btn-error-text,
1169
+ a.kds-btn-error-text {
1170
+ background: transparent;
1171
+ color: var(--kds-color-error-main);
1172
+ border: none;
1173
+ padding: 6px var(--kds-spacing-2);
1174
+ min-height: 32px;
1175
+ box-shadow: none;
1176
+ }
1177
+
1178
+ button.kds-btn-error-text:hover:not(:disabled),
1179
+ a.kds-btn-error-text:hover:not(:disabled) {
1180
+ color: var(--kds-color-error-dark);
1181
+ }
1182
+
1183
+ button.kds-btn-error-text:disabled,
1184
+ a.kds-btn-error-text:disabled {
1185
+ background: transparent;
1186
+ color: var(--kds-color-action-disabled);
1187
+ box-shadow: none;
1188
+ }
1189
+
991
1190
  /* Icon wrapper - control size via span container */
992
1191
  button.kds-btn > .kds-icon,
993
1192
  a.kds-btn > .kds-icon {
@@ -1073,63 +1272,90 @@ a.kds-btn.kds-btn-md {
1073
1272
  }
1074
1273
  }
1075
1274
 
1076
- /* Selection card */
1275
+ /* Selection card.
1276
+ IMPORTANTE: este componente se renderiza como `<button>`. BeerCSS aplica al
1277
+ `button` base: `display: inline-flex; align-items: center; justify-content: center;
1278
+ block-size: 2.5rem; padding: 0 1rem; background: var(--primary); color: on-primary`.
1279
+ Todas esas reglas hay que sobreescribirlas para que el card-selector funcione como
1280
+ un contenedor flex column de altura variable y alineado a la izquierda.
1281
+
1282
+ Convención del DS: spacing entre hijos via `gap` del flex padre + padding.
1283
+ Los elementos internos (.kds-card-selector-icon/-title/-description) NO usan margin. */
1077
1284
  .kds-card-selector {
1285
+ /* Reset BeerCSS button */
1286
+ display: flex;
1287
+ flex-direction: column;
1288
+ align-items: stretch;
1289
+ justify-content: flex-start;
1290
+ block-size: auto;
1291
+ inline-size: 100%;
1292
+ text-align: left;
1293
+ line-height: 1.5;
1294
+ color: var(--kds-color-text-primary);
1295
+ /* Estilos propios del card-selector */
1078
1296
  padding: var(--kds-spacing-3);
1297
+ gap: var(--kds-spacing-1-5);
1079
1298
  border-radius: var(--kds-radius-md);
1080
- border: 2px solid var(--kds-border-light);
1299
+ border: var(--kds-border-width-md) solid var(--kds-border-light);
1081
1300
  background: var(--kds-color-background-paper);
1082
1301
  transition: all 0.3s ease;
1083
- text-align: left;
1084
1302
  cursor: pointer;
1085
- display: flex;
1086
- flex-direction: column;
1087
1303
  }
1088
1304
 
1089
- .kds-card-selector:hover {
1090
- border-color: #D1D5DB; /* gray-300 */
1305
+ .kds-card-selector:hover:not(.selected) {
1306
+ border-color: var(--kds-color-primary-light);
1307
+ background: var(--kds-color-primary-hover);
1308
+ }
1309
+
1310
+ /* Override del ripple/overlay default de BeerCSS:
1311
+ `:is(.wave,.chip,.button,button,nav.tabbed>a,...):not([class*=ripple])::after`
1312
+ tiene specificity (0,2,4) según DevTools (Chrome cuenta cada selector dentro
1313
+ de :is() individualmente para algunos motores). Usamos !important — consistente
1314
+ con cómo se overridean otros estilos de BeerCSS en este archivo (67 usos previos).
1315
+ Antes pintaba negro porque `currentColor` resolvía a text-primary del card. */
1316
+ .kds-card-selector::after {
1317
+ background-image: radial-gradient(circle, var(--kds-color-primary-main) 1%, transparent 1%) !important;
1091
1318
  }
1092
1319
 
1093
1320
  .kds-card-selector.selected {
1094
- border-color: #3B82F6; /* blue-600 */
1095
- background: #EFF6FF; /* blue-50 */
1321
+ border-color: var(--kds-color-primary-main);
1322
+ background: var(--kds-color-primary-faint);
1096
1323
  box-shadow: var(--kds-shadow-md);
1097
1324
  }
1098
1325
 
1099
- /* Icon container */
1326
+ /* Icon container — spacing manejado por gap del padre, no margin */
1100
1327
  .kds-card-selector-icon {
1101
- width: 48px;
1102
- height: 48px;
1103
- background: #DBEAFE; /* blue-100 */
1104
- border-radius: 8px;
1328
+ width: var(--kds-spacing-6);
1329
+ height: var(--kds-spacing-6);
1330
+ background: var(--kds-color-primary-focus);
1331
+ border-radius: var(--kds-radius-sm);
1105
1332
  display: flex;
1106
1333
  align-items: center;
1107
1334
  justify-content: center;
1108
- margin-bottom: 16px;
1109
1335
  }
1110
1336
 
1111
1337
  .kds-card-selector-icon i,
1112
1338
  .kds-card-selector-icon svg {
1113
- width: 24px;
1114
- height: 24px;
1115
- min-width: 24px;
1116
- min-height: 24px;
1117
- color: #3B82F6; /* blue-600 */
1339
+ width: var(--kds-spacing-3);
1340
+ height: var(--kds-spacing-3);
1341
+ min-width: var(--kds-spacing-3);
1342
+ min-height: var(--kds-spacing-3);
1343
+ font-size: var(--kds-spacing-3);
1344
+ color: var(--kds-color-primary-main);
1118
1345
  }
1119
1346
 
1120
- /* Title */
1347
+ /* Title — spacing manejado por gap del padre, no margin */
1121
1348
  .kds-card-selector-title {
1122
- font-weight: 600;
1349
+ font-weight: var(--kds-font-weight-semibold);
1123
1350
  color: var(--kds-color-text-primary);
1124
- margin-bottom: 8px;
1125
- font-size: 16px;
1351
+ font-size: var(--kds-font-size-lg);
1126
1352
  }
1127
1353
 
1128
1354
  /* Description */
1129
1355
  .kds-card-selector-description {
1130
- font-size: 14px;
1131
- color: #4B5563; /* gray-600 */
1132
- line-height: 1.5;
1356
+ font-size: var(--kds-font-size-sm);
1357
+ color: var(--kds-color-text-secondary);
1358
+ line-height: var(--kds-line-height-normal);
1133
1359
  }
1134
1360
 
1135
1361
  /* Selected state - visual feedback via border only */
@@ -1141,14 +1367,22 @@ a.kds-btn.kds-btn-md {
1141
1367
  display: flex;
1142
1368
  flex-direction: column;
1143
1369
  gap: var(--kds-spacing-2);
1144
- padding: var(--kds-spacing-4);
1370
+ padding: var(--kds-spacing-2);
1145
1371
  border-radius: var(--kds-radius-lg);
1146
1372
  border: 2px solid var(--kds-border-medium);
1147
- max-width: 400px; /* Increased from 365px for better text spacing */
1373
+ /* Ancho mínimo aplicado a TODOS los planes así un plan sin badge en una
1374
+ grilla con un plan recomendado mantiene la misma anchura, y el badge nunca
1375
+ se superpone al título por estrechez del card. */
1376
+ min-width: 260px;
1377
+ max-width: 400px;
1148
1378
  background: var(--kds-surface-base);
1149
1379
  transition: all 0.3s ease;
1150
1380
  cursor: pointer;
1151
- height: 100%; /* Ensure cards fill grid height */
1381
+ /* Alto fijo: el card se estira al alto de su row del grid (align-items stretch
1382
+ es el default) — todas las cards de la misma fila quedan a la misma altura
1383
+ sin importar el contenido. `align-self: stretch` lo hace explícito. */
1384
+ height: 100%;
1385
+ align-self: stretch;
1152
1386
  }
1153
1387
 
1154
1388
  .kds-card-plan:hover {
@@ -1168,18 +1402,19 @@ a.kds-btn.kds-btn-md {
1168
1402
  box-shadow: var(--kds-shadow-elevation-4);
1169
1403
  }
1170
1404
 
1171
- /* Recommended Badge */
1405
+ /* Recommended Badge — inline dentro del card (no flotante).
1406
+ Es el primer hijo del .kds-card-plan, así que aparece arriba de todo
1407
+ el contenido, alineado a la izquierda. */
1172
1408
  .kds-card-plan-badge {
1173
- position: absolute;
1174
- top: -12px;
1175
- right: var(--kds-spacing-6);
1176
- padding: var(--kds-spacing-1) var(--kds-spacing-4);
1409
+ align-self: flex-start;
1410
+ padding: var(--kds-spacing-0-5) var(--kds-spacing-1-5);
1177
1411
  background: var(--primary);
1178
- color: white;
1179
- font-size: var(--kds-font-size-sm);
1412
+ color: var(--kds-color-primary-contrast); /* contrast adapts: #FFF light / dark on dark */
1413
+ font-size: var(--kds-font-size-xs);
1180
1414
  font-weight: var(--kds-font-weight-semibold);
1415
+ line-height: 1;
1181
1416
  border-radius: var(--kds-radius-full);
1182
- box-shadow: var(--kds-shadow-elevation-1);
1417
+ white-space: nowrap;
1183
1418
  }
1184
1419
 
1185
1420
  /* Plan Header */
@@ -1207,13 +1442,11 @@ a.kds-btn.kds-btn-md {
1207
1442
  display: flex;
1208
1443
  align-items: baseline;
1209
1444
  gap: var(--kds-spacing-2);
1210
- padding: var(--kds-spacing-4) 0;
1211
1445
  border-bottom: 1px solid var(--kds-border-light);
1212
- min-height: 88px; /* Standardize price area height */
1213
1446
  }
1214
1447
 
1215
1448
  .kds-price {
1216
- font-size: 42px;
1449
+ font-size: var(--kds-font-size-4xl);
1217
1450
  font-weight: var(--kds-font-weight-bold);
1218
1451
  color: var(--primary);
1219
1452
  line-height: 1.1;
@@ -1468,7 +1701,63 @@ a.kds-btn.kds-btn-md {
1468
1701
  align-items: flex-end;
1469
1702
  }
1470
1703
 
1704
+ .kds-self-center {
1705
+ align-self: center;
1706
+ }
1707
+
1708
+ /* Botón "pelado": neutraliza el chrome de button de BeerCSS (pill 2.5rem,
1709
+ radius, fondo y hover) para botones de terceros con su propio look,
1710
+ p.ej. los share buttons de react-share (ícono circular). */
1711
+ .kds-btn-bare,
1712
+ .kds-btn-bare:hover,
1713
+ .kds-btn-bare:focus,
1714
+ .kds-btn-bare:active {
1715
+ background: none !important;
1716
+ block-size: auto !important;
1717
+ inline-size: auto !important;
1718
+ min-inline-size: 0 !important;
1719
+ padding: 0 !important;
1720
+ margin: 0 !important;
1721
+ border: 0 !important;
1722
+ border-radius: var(--kds-radius-full) !important;
1723
+ box-sizing: border-box !important;
1724
+ box-shadow: none !important;
1725
+ transform: none !important;
1726
+ }
1727
+
1728
+ .kds-btn-bare::before,
1729
+ .kds-btn-bare::after {
1730
+ display: none !important;
1731
+ }
1732
+
1733
+ /* BeerCSS también encoge los íconos hijos de button a 1.5rem (min/max);
1734
+ un botón pelado respeta el tamaño propio del ícono. */
1735
+ .kds-btn-bare > :is(img, svg) {
1736
+ max-block-size: none !important;
1737
+ max-inline-size: none !important;
1738
+ min-block-size: 0 !important;
1739
+ min-inline-size: 0 !important;
1740
+ }
1741
+
1742
+ /* Versión de la app anclada al fondo del card body (flex column): margin-top
1743
+ auto absorbe el espacio libre; el padding garantiza respiro mínimo cuando el
1744
+ contenido llena la card. */
1745
+ .kds-card-version {
1746
+ margin-top: auto;
1747
+ padding-top: var(--kds-spacing-2);
1748
+ text-align: center;
1749
+ }
1750
+
1751
+ /* Separador vertical (entre chips/logos hermanos en una fila). */
1752
+ .kds-vr {
1753
+ width: 1px;
1754
+ height: var(--kds-spacing-3);
1755
+ background: var(--kds-color-divider);
1756
+ flex: 0 0 auto;
1757
+ }
1758
+
1471
1759
  /* Gap utilities */
1760
+ .kds-gap-0-25 { gap: var(--kds-spacing-0-25); }
1472
1761
  .kds-gap-1 { gap: var(--kds-spacing-1); }
1473
1762
  .kds-gap-2 { gap: var(--kds-spacing-2); }
1474
1763
  .kds-gap-3 { gap: var(--kds-spacing-3); }
@@ -1504,6 +1793,7 @@ a.kds-btn.kds-btn-md {
1504
1793
  /* Spacing utilities - Margin */
1505
1794
  .kds-m-0 { margin: 0; }
1506
1795
  .kds-m-2 { margin: var(--kds-spacing-2); }
1796
+ .kds-m-2-5 { margin: var(--kds-spacing-2-5); }
1507
1797
  .kds-m-4 { margin: var(--kds-spacing-4); }
1508
1798
  .kds-m-6 { margin: var(--kds-spacing-6); }
1509
1799
  .kds-m-8 { margin: var(--kds-spacing-8); }
@@ -1517,6 +1807,7 @@ a.kds-btn.kds-btn-md {
1517
1807
  .kds-mt-8 { margin-top: var(--kds-spacing-8); }
1518
1808
 
1519
1809
  .kds-mb-0 { margin-bottom: 0; }
1810
+ .kds-mb-0-5 { margin-bottom: var(--kds-spacing-0-5); }
1520
1811
  .kds-mb-1 { margin-bottom: var(--kds-spacing-1); }
1521
1812
  .kds-mb-2 { margin-bottom: var(--kds-spacing-2); }
1522
1813
  .kds-mb-3 { margin-bottom: var(--kds-spacing-3); }
@@ -1753,33 +2044,35 @@ a.kds-btn.kds-btn-md {
1753
2044
  opacity: 1 !important;
1754
2045
  }
1755
2046
 
1756
- /* Snackbar semantic variants
1757
- All use white text (--kds-color-primary-contrast) on solid semantic backgrounds */
2047
+ /* Snackbar semantic variants — white text on solid semantic backgrounds.
2048
+ Use each semantic's own *-contrast token (always #FFFFFF, both modes) rather
2049
+ than --kds-color-primary-contrast, which flips to dark purple in dark mode
2050
+ and made the text illegible on the colored surface. */
1758
2051
  .snackbar.error {
1759
2052
  background-color: var(--kds-color-error-main) !important;
1760
- color: var(--kds-color-primary-contrast) !important;
2053
+ color: var(--kds-color-error-contrast) !important;
1761
2054
  }
1762
2055
 
1763
2056
  .snackbar.error i {
1764
- color: var(--kds-color-primary-contrast) !important;
2057
+ color: var(--kds-color-error-contrast) !important;
1765
2058
  }
1766
2059
 
1767
2060
  .snackbar.info {
1768
2061
  background-color: var(--kds-color-info-main) !important;
1769
- color: var(--kds-color-primary-contrast) !important;
2062
+ color: var(--kds-color-info-contrast) !important;
1770
2063
  }
1771
2064
 
1772
2065
  .snackbar.info i {
1773
- color: var(--kds-color-primary-contrast) !important;
2066
+ color: var(--kds-color-info-contrast) !important;
1774
2067
  }
1775
2068
 
1776
2069
  .snackbar.success {
1777
2070
  background-color: var(--kds-color-success-main) !important;
1778
- color: var(--kds-color-primary-contrast) !important;
2071
+ color: var(--kds-color-success-contrast) !important;
1779
2072
  }
1780
2073
 
1781
2074
  .snackbar.success i {
1782
- color: var(--kds-color-primary-contrast) !important;
2075
+ color: var(--kds-color-success-contrast) !important;
1783
2076
  }
1784
2077
 
1785
2078
  /* Snackbar progress bar — animated timer for auto-dismiss */
@@ -2030,121 +2323,380 @@ a.kds-btn-google:disabled {
2030
2323
  }
2031
2324
 
2032
2325
  /* ========================================
2033
- Link Styles
2326
+ KDS LINEAR PROGRESS
2327
+ Native <progress> (KdsLinearProgress). Additive over BeerCSS `progress`,
2328
+ scoped to .kds-progress so other consumers are unaffected.
2034
2329
  ======================================== */
2035
2330
 
2036
- /* Explicit link class */
2037
- .kds-link {
2038
- color: var(--kds-color-primary-main);
2039
- text-decoration: none;
2040
- cursor: pointer;
2041
- transition: color var(--kds-transition-shorter) var(--kds-easing-standard);
2042
- }
2043
-
2044
- .kds-link:hover {
2045
- color: var(--kds-color-primary-dark);
2046
- text-decoration: underline;
2331
+ /* `progress` is inline-block by default → leaves a baseline (descender) gap
2332
+ below the bar. Block removes that gap so it sits flush with siblings. */
2333
+ .kds-progress {
2334
+ display: block;
2047
2335
  }
2048
2336
 
2049
- /* Contextual links inside cards and stage forms */
2050
- .kds-card-body a:not(.kds-btn):not([class*="kds-btn"]),
2051
- .kds-stage-content a:not(.kds-btn):not([class*="kds-btn"]) {
2052
- color: var(--kds-color-primary-main);
2053
- text-decoration: none;
2054
- transition: color var(--kds-transition-shorter) var(--kds-easing-standard);
2337
+ /* Animate the filled value when it changes (e.g. advancing a step). BeerCSS
2338
+ sets the value background but no transition, so the width jumps abruptly. */
2339
+ .kds-progress::-webkit-progress-value {
2340
+ transition: width var(--kds-transition-standard) var(--kds-easing-standard);
2055
2341
  }
2056
2342
 
2057
- .kds-card-body a:not(.kds-btn):not([class*="kds-btn"]):hover,
2058
- .kds-stage-content a:not(.kds-btn):not([class*="kds-btn"]):hover {
2059
- color: var(--kds-color-primary-dark);
2060
- text-decoration: underline;
2343
+ .kds-progress::-moz-progress-bar {
2344
+ transition: width var(--kds-transition-standard) var(--kds-easing-standard);
2061
2345
  }
2062
2346
 
2063
2347
  /* ========================================
2064
- Flex Layout System (Body + Container + Sidebar)
2348
+ KDS SECURE LOADER
2349
+ Loader de espera: anillo circular + candado centrado + frases opcionales.
2350
+ El anillo reutiliza el spinner circular de BeerCSS, escalado.
2065
2351
  ======================================== */
2066
2352
 
2067
- /* Body flex-column layout */
2068
- body.kds-layout {
2353
+ .kds-secure-loader {
2069
2354
  display: flex;
2070
2355
  flex-direction: column;
2071
- min-height: 100vh;
2072
- margin: 0;
2356
+ align-items: center;
2357
+ justify-content: center;
2358
+ gap: var(--kds-spacing-4);
2359
+ text-align: center;
2360
+ /* Llena y centra dentro de contenedores flex (pantalla de carga); en flujo
2361
+ normal (contenedor no-flex) se reduce a su contenido. */
2362
+ flex: 1 1 auto;
2073
2363
  }
2074
2364
 
2075
- /* Layout container (flex-row) - below header */
2076
- .kds-layout-container {
2365
+ .kds-secure-loader-text {
2077
2366
  display: flex;
2078
- flex-direction: row;
2079
- flex: 1;
2080
- overflow: hidden;
2081
- }
2082
- /* ========================================
2083
- Footer Customization
2084
- ======================================== */
2085
-
2086
- footer.primary {
2087
- background: var(--primary);
2088
- padding: var(--kds-spacing-4) var(--kds-spacing-3);
2089
- color: var(--on-primary);
2367
+ flex-direction: column;
2368
+ gap: var(--kds-spacing-0-25);
2090
2369
  }
2091
2370
 
2092
- footer.primary h6 {
2093
- color: var(--on-primary);
2371
+ .kds-secure-loader-title {
2372
+ margin: 0;
2373
+ font-family: var(--kds-font-family-primary);
2374
+ font-size: var(--kds-font-size-h6);
2094
2375
  font-weight: var(--kds-font-weight-semibold);
2095
- margin-bottom: var(--kds-spacing-2);
2376
+ line-height: var(--kds-line-height-snug);
2377
+ color: var(--kds-color-text-primary);
2096
2378
  }
2097
2379
 
2098
- footer.primary a {
2099
- color: var(--on-primary);
2100
- text-decoration: none;
2101
- display: block;
2102
- padding: var(--kds-spacing-0-5) 0;
2103
- transition: opacity var(--kds-transition-shorter) var(--kds-easing-standard);
2104
- text-align: center;
2380
+ .kds-secure-loader-message {
2381
+ margin: 0;
2382
+ /* !important: anula el auto-espaciado de hermanos de BeerCSS (0,5,1);
2383
+ la separación con el título la da el gap del contenedor (spacing-0-25). */
2384
+ margin-top: 0 !important;
2385
+ font-family: var(--kds-font-family-secondary);
2386
+ font-size: var(--kds-font-size-body1);
2387
+ line-height: var(--kds-line-height-normal);
2388
+ color: var(--kds-color-primary-main);
2105
2389
  }
2106
2390
 
2107
- footer.primary a:hover {
2108
- opacity: 0.8;
2391
+ /* Caja del spinner con tamaño fijo: la animación no altera el layout.
2392
+ 6.25rem (100px) = tamaño del loader original (CircularProgress size=100). */
2393
+ .kds-secure-loader-spinner {
2394
+ position: relative;
2395
+ display: inline-grid;
2396
+ place-items: center;
2397
+ inline-size: var(--kds-secure-loader-size, 6.25rem);
2398
+ block-size: var(--kds-secure-loader-size, 6.25rem);
2109
2399
  }
2110
2400
 
2111
- /* ========================================
2112
- Form Inputs (Material Design 3)
2113
- ======================================== */
2114
-
2115
- /* Form labels */
2116
- .field label,
2117
- .kds-form-label {
2118
- font-size: 14px;
2119
- font-weight: 500;
2120
- color: var(--kds-color-text-primary);
2121
- margin-bottom: 8px;
2122
- display: block;
2123
- display: flex;
2124
- gap: 5px;
2401
+ /* Anillo: SVG nativo con arco vía stroke-dasharray. Replica el CircularProgress
2402
+ original (viewBox 44u, thickness 2, arco ~63%, disableShrink). El giro se anima
2403
+ con stroke-dashoffset (el círculo queda estático y nítido); rotar el SVG con
2404
+ transform rasteriza el anillo y se ve irregular durante el giro. */
2405
+ .kds-secure-loader-ring {
2406
+ inline-size: var(--kds-secure-loader-size, 6.25rem);
2407
+ block-size: var(--kds-secure-loader-size, 6.25rem);
2125
2408
  }
2126
2409
 
2127
- /* Icon utilities for inline icons */
2128
- .kds-icon-inline {
2129
- vertical-align: middle;
2130
- margin-right: var(--kds-spacing-2);
2410
+ .kds-secure-loader-ring circle {
2411
+ fill: none;
2412
+ stroke: var(--kds-color-primary-main);
2413
+ stroke-width: 2;
2414
+ stroke-linecap: round;
2415
+ /* circunferencia = 2π·20.2 ≈ 126.92; arco visible 80 (~63%) + hueco 46.92 */
2416
+ stroke-dasharray: 80 46.92;
2417
+ animation: kds-secure-loader-dash 1.2s linear infinite;
2131
2418
  }
2132
2419
 
2133
- /* Section title icon */
2134
- .kds-stage-section-title i {
2135
- vertical-align: middle;
2136
- margin-right: var(--kds-spacing-2);
2420
+ @keyframes kds-secure-loader-dash {
2421
+ to {
2422
+ stroke-dashoffset: -126.92;
2423
+ }
2137
2424
  }
2138
2425
 
2139
- /* ==============================================
2140
- MATERIAL DESIGN 3 FLOATING LABELS (BeerCSS)
2141
- Solución limpia con principios claros
2142
- ============================================== */
2143
-
2426
+ /* Candado centrado sobre el anillo (~0.3 del diámetro). Centrado explícito
2427
+ (top/left 50% + translate) para que no dependa del grid ni se recorte. */
2428
+ .kds-secure-loader-lock {
2429
+ position: absolute;
2430
+ top: 50%;
2431
+ left: 50%;
2432
+ transform: translate(-50%, -50%);
2433
+ color: var(--kds-color-text-secondary);
2434
+ font-size: calc(var(--kds-secure-loader-size, 6.25rem) * 0.3);
2435
+ line-height: 1;
2436
+ pointer-events: none;
2437
+ }
2438
+
2439
+ /* ========================================
2440
+ KDS COPY BUTTON
2441
+ Chip compacto que muestra un valor + icono copy y lo copia al click.
2442
+ Acción inline (distinto de KdsCopyRow). Look del copy button de khenshin.
2443
+ ======================================== */
2444
+
2445
+ .kds-copy-button {
2446
+ display: inline-flex;
2447
+ align-items: center;
2448
+ gap: var(--kds-spacing-1);
2449
+ padding: var(--kds-spacing-0-25) var(--kds-spacing-1-5);
2450
+ /* No usar el alto de botón táctil de BeerCSS (~44px): es un chip compacto,
2451
+ su alto lo define el contenido + padding. */
2452
+ block-size: auto;
2453
+ min-block-size: 0;
2454
+ border: none;
2455
+ border-radius: var(--kds-radius-button);
2456
+ background: var(--kds-alert-info-bg);
2457
+ color: var(--kds-color-info-dark);
2458
+ font-family: var(--kds-font-family-secondary);
2459
+ font-size: var(--kds-font-size-sm);
2460
+ font-weight: var(--kds-font-weight-medium);
2461
+ line-height: var(--kds-line-height-normal);
2462
+ cursor: pointer;
2463
+ transition: background-color var(--kds-transition-shorter) var(--kds-easing-standard),
2464
+ color var(--kds-transition-shorter) var(--kds-easing-standard);
2465
+ }
2466
+
2467
+ /* active = hover: sin el aro de focus/active (outline morado de BeerCSS). */
2468
+ .kds-copy-button:focus,
2469
+ .kds-copy-button:focus-visible,
2470
+ .kds-copy-button:active {
2471
+ outline: none;
2472
+ }
2473
+
2474
+ /* value y copiedText se apilan en la MISMA celda de grid: la celda toma el
2475
+ ancho del texto más largo, por lo que el botón conserva su ancho al alternar
2476
+ entre "valor" y "copiado" (sin reflow). */
2477
+ .kds-copy-button-label {
2478
+ display: inline-grid;
2479
+ }
2480
+
2481
+ .kds-copy-button-value,
2482
+ .kds-copy-button-copied {
2483
+ grid-area: 1 / 1;
2484
+ white-space: nowrap;
2485
+ }
2486
+
2487
+ /* Estado por defecto: visible el valor, oculto (pero ocupando espacio) el copiado */
2488
+ .kds-copy-button-copied {
2489
+ visibility: hidden;
2490
+ }
2491
+
2492
+ /* Al copiar se invierte la visibilidad; el ancho no cambia */
2493
+ .kds-copy-button.copied .kds-copy-button-value {
2494
+ visibility: hidden;
2495
+ }
2496
+
2497
+ .kds-copy-button.copied .kds-copy-button-copied {
2498
+ visibility: visible;
2499
+ }
2500
+
2501
+ .kds-copy-button .material-symbols-outlined {
2502
+ font-size: 1rem;
2503
+ color: var(--kds-color-info-main);
2504
+ flex-shrink: 0;
2505
+ }
2506
+
2507
+ /* Dark mode: info-dark text is near-invisible on the dark translucent chip.
2508
+ Use the alert info text token (light blue in dark), same pattern as KdsAlert;
2509
+ the copied state already uses alert-success-text and adapts on its own. */
2510
+ [data-theme="dark"] .kds-copy-button {
2511
+ color: var(--kds-alert-info-text);
2512
+ }
2513
+
2514
+ [data-theme="dark"] .kds-copy-button .material-symbols-outlined {
2515
+ color: var(--kds-alert-info-text);
2516
+ }
2517
+
2518
+ .kds-copy-button.copied {
2519
+ background: var(--kds-alert-success-bg);
2520
+ color: var(--kds-alert-success-text);
2521
+ animation: kds-copy-button-pulse var(--kds-transition-short) var(--kds-easing-standard);
2522
+ }
2523
+
2524
+ .kds-copy-button.copied .material-symbols-outlined {
2525
+ color: var(--kds-color-success-main);
2526
+ /* el check entra con un pop al copiar */
2527
+ animation: kds-copy-button-pop var(--kds-transition-short) var(--kds-easing-standard);
2528
+ }
2529
+
2530
+ @keyframes kds-copy-button-pulse {
2531
+ 0% {
2532
+ transform: scale(1);
2533
+ }
2534
+ 50% {
2535
+ transform: scale(1.04);
2536
+ }
2537
+ 100% {
2538
+ transform: scale(1);
2539
+ }
2540
+ }
2541
+
2542
+ @keyframes kds-copy-button-pop {
2543
+ 0% {
2544
+ transform: scale(0.3);
2545
+ opacity: 0;
2546
+ }
2547
+ 60% {
2548
+ transform: scale(1.25);
2549
+ opacity: 1;
2550
+ }
2551
+ 100% {
2552
+ transform: scale(1);
2553
+ }
2554
+ }
2555
+
2556
+ /* ========================================
2557
+ Link Styles
2558
+ ======================================== */
2559
+
2560
+ /* Explicit link class */
2561
+ .kds-link {
2562
+ color: var(--kds-color-primary-main);
2563
+ text-decoration: none;
2564
+ cursor: pointer;
2565
+ transition: color var(--kds-transition-shorter) var(--kds-easing-standard);
2566
+ }
2567
+
2568
+ .kds-link:hover {
2569
+ color: var(--kds-color-primary-dark);
2570
+ text-decoration: underline;
2571
+ }
2572
+
2573
+ /* Dark: hover lightens (primary-dark is near-invisible on dark surfaces). */
2574
+ [data-theme="dark"] .kds-link:hover {
2575
+ color: var(--kds-color-primary-light);
2576
+ }
2577
+
2578
+ /* Contextual links inside cards and stage forms */
2579
+ .kds-card-body a:not(.kds-btn):not([class*="kds-btn"]),
2580
+ .kds-stage-content a:not(.kds-btn):not([class*="kds-btn"]),
2581
+ label.checkbox a:not(.kds-btn):not([class*="kds-btn"]),
2582
+ label.radio a:not(.kds-btn):not([class*="kds-btn"]) {
2583
+ color: var(--kds-color-primary-main);
2584
+ text-decoration: none;
2585
+ transition: color var(--kds-transition-shorter) var(--kds-easing-standard);
2586
+ }
2587
+
2588
+ .kds-card-body a:not(.kds-btn):not([class*="kds-btn"]):hover,
2589
+ .kds-stage-content a:not(.kds-btn):not([class*="kds-btn"]):hover,
2590
+ label.checkbox a:not(.kds-btn):not([class*="kds-btn"]):hover,
2591
+ label.radio a:not(.kds-btn):not([class*="kds-btn"]):hover {
2592
+ color: var(--kds-color-primary-dark);
2593
+ text-decoration: underline;
2594
+ }
2595
+
2596
+ /* Dark: hover must lighten (primary-dark is near-invisible on a dark surface). */
2597
+ [data-theme="dark"] .kds-card-body a:not(.kds-btn):not([class*="kds-btn"]):hover,
2598
+ [data-theme="dark"] .kds-stage-content a:not(.kds-btn):not([class*="kds-btn"]):hover,
2599
+ [data-theme="dark"] label.checkbox a:not(.kds-btn):not([class*="kds-btn"]):hover,
2600
+ [data-theme="dark"] label.radio a:not(.kds-btn):not([class*="kds-btn"]):hover {
2601
+ color: var(--kds-color-primary-light);
2602
+ }
2603
+
2604
+ /* ========================================
2605
+ Flex Layout System (Body + Container + Sidebar)
2606
+ ======================================== */
2607
+
2608
+ /* Body flex-column layout */
2609
+ body.kds-layout {
2610
+ display: flex;
2611
+ flex-direction: column;
2612
+ min-height: 100vh;
2613
+ margin: 0;
2614
+ }
2615
+
2616
+ /* Layout container (flex-row) - below header */
2617
+ .kds-layout-container {
2618
+ display: flex;
2619
+ flex-direction: row;
2620
+ flex: 1;
2621
+ overflow: hidden;
2622
+ }
2623
+ /* ========================================
2624
+ Footer Customization
2625
+ ======================================== */
2626
+
2627
+ footer.primary {
2628
+ background: var(--primary);
2629
+ padding: var(--kds-spacing-4) var(--kds-spacing-3);
2630
+ color: var(--on-primary);
2631
+ }
2632
+
2633
+ footer.primary h6 {
2634
+ color: var(--on-primary);
2635
+ font-weight: var(--kds-font-weight-semibold);
2636
+ margin-bottom: var(--kds-spacing-2);
2637
+ }
2638
+
2639
+ footer.primary a {
2640
+ color: var(--on-primary);
2641
+ text-decoration: none;
2642
+ display: block;
2643
+ padding: var(--kds-spacing-0-5) 0;
2644
+ transition: opacity var(--kds-transition-shorter) var(--kds-easing-standard);
2645
+ text-align: center;
2646
+ }
2647
+
2648
+ footer.primary a:hover {
2649
+ opacity: 0.8;
2650
+ }
2651
+
2652
+ /* ========================================
2653
+ Form Inputs (Material Design 3)
2654
+ ======================================== */
2655
+
2656
+ /* Form labels */
2657
+ .field label,
2658
+ .kds-form-label {
2659
+ font-size: 14px;
2660
+ font-weight: 500;
2661
+ color: var(--kds-color-text-primary);
2662
+ margin-bottom: 8px;
2663
+ display: block;
2664
+ display: flex;
2665
+ gap: 5px;
2666
+ }
2667
+
2668
+ /* Icon utilities for inline icons */
2669
+ .kds-icon-inline {
2670
+ vertical-align: middle;
2671
+ margin-right: var(--kds-spacing-2);
2672
+ }
2673
+
2674
+ /* Ícono(s) en color primary. Para íconos MUI/SVG dentro del scope que, por el reset
2675
+ `.kds-theme-root svg { fill: currentColor }`, terminan heredando el color de texto
2676
+ (#333) en vez de su color propio — el `color="primary"` de MUI no le gana al contexto.
2677
+ Aplica al contenedor y a sus i/svg hijos. */
2678
+ .kds-icon-primary,
2679
+ .kds-icon-primary i,
2680
+ .kds-icon-primary svg {
2681
+ color: var(--kds-color-primary-main);
2682
+ }
2683
+
2684
+ /* Section title icon */
2685
+ .kds-stage-section-title i {
2686
+ vertical-align: middle;
2687
+ margin-right: var(--kds-spacing-2);
2688
+ }
2689
+
2690
+ /* ==============================================
2691
+ MATERIAL DESIGN 3 FLOATING LABELS (BeerCSS)
2692
+ Solución limpia con principios claros
2693
+ ============================================== */
2694
+
2144
2695
  /* === VARIABLES GLOBALES === */
2145
2696
  :root,
2146
2697
  body.light,
2147
- body.dark {
2698
+ body.dark,
2699
+ [data-theme="dark"] {
2148
2700
  --primary: var(--kds-color-primary-main); /* Purple Khipu - Focus */
2149
2701
  --outline: var(--kds-color-input-enabled-border); /* Gris normal - Unfocused */
2150
2702
  --error: var(--kds-color-error-main); /* Rojo - Invalid */
@@ -2161,6 +2713,25 @@ body.dark {
2161
2713
  transition: border-color 0.2s, box-shadow 0.2s, outline-color 0.2s;
2162
2714
  }
2163
2715
 
2716
+ /* === TEXTAREA FIX === */
2717
+ /* BeerCSS base sets minimal block padding; add breathing room for textarea */
2718
+ .field > textarea {
2719
+ padding-block: 0.75rem;
2720
+ resize: vertical;
2721
+ }
2722
+
2723
+ .field.label > textarea {
2724
+ padding-block-start: 1.5rem;
2725
+ }
2726
+
2727
+ /* Label en reposo (estado "abajo", como placeholder): font-size menor al 1rem de BeerCSS
2728
+ para que un label largo no estorbe con un ícono (prefix/suffix) del input. Token sm
2729
+ (13–14px, siempre < 1rem del input incluso en desktop). Al flotar, BeerCSS lo reduce a
2730
+ .75rem con mayor especificidad, así que esto solo afecta el reposo. */
2731
+ .field.label > label {
2732
+ font-size: var(--kds-font-size-sm);
2733
+ }
2734
+
2164
2735
  /* ==========================================
2165
2736
  NOTCH VISIBILITY (opacity)
2166
2737
  ========================================== */
@@ -2282,6 +2853,23 @@ body.dark {
2282
2853
  color: var(--error); /* Rojo error */
2283
2854
  }
2284
2855
 
2856
+ /* ==========================================
2857
+ PASSWORD REVEAL TOGGLE (KdsTextField `revealable`)
2858
+ El <a role="button"> reusa el posicionamiento de icono de BeerCSS
2859
+ (.field > a, pointer-events: all). Aquí solo: color del icono
2860
+ (on-surface-variant) y un focus ring accesible para el toggle
2861
+ navegable por teclado (BeerCSS hace `outline: inherit` en <a>).
2862
+ ========================================== */
2863
+ .field > a.kds-field-reveal {
2864
+ color: var(--on-surface-variant);
2865
+ }
2866
+
2867
+ .field > a.kds-field-reveal:focus-visible {
2868
+ outline: 2px solid var(--primary);
2869
+ outline-offset: 2px;
2870
+ border-radius: 50%;
2871
+ }
2872
+
2285
2873
  /* ==========================================
2286
2874
  LABEL COLOR
2287
2875
  Mantener sincronizado con border
@@ -2292,9 +2880,9 @@ body.dark {
2292
2880
  color: var(--kds-color-text-secondary);
2293
2881
  }
2294
2882
 
2295
- /* Hover SIN focus: más oscuro (70% opacity) */
2883
+ /* Hover SIN focus: máxima emphasis (adapta a blanco en dark) */
2296
2884
  .field.label:hover:not(:focus-within) > label {
2297
- color: rgba(0, 0, 0, 0.7);
2885
+ color: var(--kds-color-text-primary);
2298
2886
  }
2299
2887
 
2300
2888
  /* Focus: primary purple (BeerCSS ya lo hace, pero aseguramos) */
@@ -2389,6 +2977,71 @@ body.dark {
2389
2977
  display: inline-flex;
2390
2978
  }
2391
2979
 
2980
+ /* ========================================
2981
+ Radix Tooltip (.kds-tooltip)
2982
+ Espejo de `.tooltip` pero para popovers de Radix:
2983
+ bg oscuro, texto blanco, sombra + animación fade/slide direccional.
2984
+ ======================================== */
2985
+
2986
+ .kds-tooltip {
2987
+ background-color: var(--kds-color-text-primary);
2988
+ color: var(--kds-color-background-paper);
2989
+ font-family: var(--kds-font-family-primary);
2990
+ font-size: var(--kds-font-size-xs);
2991
+ font-weight: var(--kds-font-weight-medium);
2992
+ line-height: var(--kds-line-height-normal);
2993
+ border-radius: var(--kds-radius-sm);
2994
+ padding: var(--kds-spacing-0-75) var(--kds-spacing-1-5);
2995
+ box-shadow: var(--kds-shadow-4);
2996
+ max-width: 280px;
2997
+ z-index: var(--kds-z-index-tooltip, 1500);
2998
+ user-select: none;
2999
+ /* Animación direccional según Radix data-side — coordinada con copy-row
3000
+ (cubic-bezier 0.2, 0.8, 0.2, 1) para un feel consistente del DS. */
3001
+ animation-duration: 240ms;
3002
+ animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
3003
+ will-change: transform, opacity;
3004
+ }
3005
+
3006
+ .kds-tooltip[data-state='closed'] {
3007
+ animation-duration: 140ms;
3008
+ animation-timing-function: cubic-bezier(0.4, 0, 0.6, 1);
3009
+ }
3010
+
3011
+ /* Direccional según data-side (Radix añade el attr en runtime).
3012
+ Cada lado entra desde su dirección con ~8px de offset (más visible que 4px). */
3013
+ .kds-tooltip[data-side='top'][data-state='delayed-open'],
3014
+ .kds-tooltip[data-side='top'][data-state='instant-open'] { animation-name: kds-tooltip-in-top; }
3015
+ .kds-tooltip[data-side='bottom'][data-state='delayed-open'],
3016
+ .kds-tooltip[data-side='bottom'][data-state='instant-open'] { animation-name: kds-tooltip-in-bottom; }
3017
+ .kds-tooltip[data-side='left'][data-state='delayed-open'],
3018
+ .kds-tooltip[data-side='left'][data-state='instant-open'] { animation-name: kds-tooltip-in-left; }
3019
+ .kds-tooltip[data-side='right'][data-state='delayed-open'],
3020
+ .kds-tooltip[data-side='right'][data-state='instant-open'] { animation-name: kds-tooltip-in-right; }
3021
+
3022
+ /* Fallback cuando no hay data-side resuelto aún */
3023
+ .kds-tooltip[data-state='delayed-open']:not([data-side]),
3024
+ .kds-tooltip[data-state='instant-open']:not([data-side]) {
3025
+ animation-name: kds-tooltip-in-top;
3026
+ }
3027
+
3028
+ .kds-tooltip[data-state='closed'] { animation-name: kds-tooltip-out; }
3029
+
3030
+ @keyframes kds-tooltip-in-top { from { opacity: 0; transform: translateY(8px) scale(0.96); } to { opacity: 1; transform: none; } }
3031
+ @keyframes kds-tooltip-in-bottom { from { opacity: 0; transform: translateY(-8px) scale(0.96); } to { opacity: 1; transform: none; } }
3032
+ @keyframes kds-tooltip-in-left { from { opacity: 0; transform: translateX(8px) scale(0.96); } to { opacity: 1; transform: none; } }
3033
+ @keyframes kds-tooltip-in-right { from { opacity: 0; transform: translateX(-8px) scale(0.96); } to { opacity: 1; transform: none; } }
3034
+
3035
+ @keyframes kds-tooltip-out {
3036
+ from { opacity: 1; transform: none; }
3037
+ to { opacity: 0; transform: scale(0.96); }
3038
+ }
3039
+
3040
+ /* Arrow (SVG path generado por Radix) */
3041
+ .kds-tooltip-arrow {
3042
+ fill: var(--kds-color-text-primary);
3043
+ }
3044
+
2392
3045
  /* ========================================
2393
3046
  Material Symbols Icon Sizing
2394
3047
  ======================================== */
@@ -2399,8 +3052,13 @@ body.dark {
2399
3052
  'wght' 400,
2400
3053
  'GRAD' 0,
2401
3054
  'opsz' 24;
2402
- min-width: 30px;
2403
- min-height: 30px;
3055
+ /* La caja debe seguir al font-size (1em): una caja fija (30px, o el --_size de Beer via
3056
+ overflow:hidden en `i`) recorta los glifos mayores y descentra los menores junto a texto. */
3057
+ width: 1em;
3058
+ height: 1em;
3059
+ min-width: 1em;
3060
+ min-height: 1em;
3061
+ line-height: 1;
2404
3062
  display: inline-flex;
2405
3063
  align-items: center;
2406
3064
  justify-content: center;
@@ -2423,6 +3081,16 @@ body.dark {
2423
3081
  width: 100%;
2424
3082
  }
2425
3083
 
3084
+ /* Truncado en una línea con ellipsis. Incluye min-width:0 para que funcione
3085
+ dentro de contenedores flex/grid (el ítem puede encogerse por debajo de su
3086
+ contenido). Útil para nombres/asuntos largos en headers y filas. */
3087
+ .kds-truncate {
3088
+ min-width: 0;
3089
+ overflow: hidden;
3090
+ text-overflow: ellipsis;
3091
+ white-space: nowrap;
3092
+ }
3093
+
2426
3094
  .kds-max-w-sm {
2427
3095
  max-width: 400px;
2428
3096
  }
@@ -2466,6 +3134,11 @@ body.dark {
2466
3134
  display: flex;
2467
3135
  flex-direction: column;
2468
3136
  gap: var(--kds-spacing-3);
3137
+ /* Reset del fieldset nativo: sin borde/padding/margin por defecto del navegador. */
3138
+ margin: 0;
3139
+ padding: 0;
3140
+ border: 0;
3141
+ min-inline-size: 0;
2469
3142
  }
2470
3143
 
2471
3144
  /* Checkbox Label - BeerCSS override */
@@ -2477,7 +3150,7 @@ label.radio {
2477
3150
  border-radius: 8px;
2478
3151
  cursor: pointer;
2479
3152
  transition: background-color 0.2s;
2480
- font-size: 16px;
3153
+ font-size: var(--kds-font-size-sm);
2481
3154
  line-height: 1.5;
2482
3155
  color: var(--kds-color-text-primary);
2483
3156
  }
@@ -2515,6 +3188,42 @@ label.radio:has(input:disabled) {
2515
3188
  opacity: 0.6;
2516
3189
  }
2517
3190
 
3191
+ /* ========================================
3192
+ RADIO GROUP — variante card (.kds-radio-group--card)
3193
+ Cada opción como fila con borde (estilo selección de banco/cuenta),
3194
+ manteniendo el control radio. Hover + opción seleccionada resaltada.
3195
+ ======================================== */
3196
+ .kds-radio-group--card {
3197
+ gap: var(--kds-spacing-2);
3198
+ }
3199
+
3200
+ .kds-radio-group--card label.radio {
3201
+ gap: var(--kds-spacing-1-5);
3202
+ /* Vertical con un poco más de aire; horizontal reducido para pegar el radio a la izquierda. */
3203
+ padding: var(--kds-spacing-2) var(--kds-spacing-1-5);
3204
+ /* Bordes con los valores del bank selector. */
3205
+ border: 1px solid var(--kds-color-border-subtle);
3206
+ border-radius: var(--kds-radius-card);
3207
+ background: var(--kds-color-surface);
3208
+ transition: border-color 0.2s, background 0.2s;
3209
+ }
3210
+
3211
+ /* Leve separación extra entre el radio y el texto de la opción. */
3212
+ .kds-radio-group--card label.radio span {
3213
+ margin-left: var(--kds-spacing-0-5);
3214
+ }
3215
+
3216
+ .kds-radio-group--card label.radio:hover {
3217
+ border-color: var(--kds-color-border-default);
3218
+ }
3219
+
3220
+ /* Seleccionada: poco color primario — solo un fondo faint sutil + borde gris
3221
+ (el punto del radio ya comunica la selección). */
3222
+ .kds-radio-group--card label.radio:has(input:checked) {
3223
+ border-color: var(--kds-color-border-default);
3224
+ background: var(--kds-color-primary-faint);
3225
+ }
3226
+
2518
3227
  /* ========================================
2519
3228
  ONBOARDING COMPONENTS
2520
3229
  Multi-stage onboarding flow with form validation
@@ -2711,7 +3420,7 @@ dialog.modal .field.border:focus-within {
2711
3420
  background: var(--kds-accordion-bg-open);
2712
3421
  font-size: var(--kds-font-size-sm);
2713
3422
  line-height: 1.6;
2714
- color: #374151;
3423
+ color: var(--kds-color-text-primary);
2715
3424
  }
2716
3425
 
2717
3426
  .kds-contract-accordion-content h4 {
@@ -3012,19 +3721,17 @@ footer > .kds-container-center {
3012
3721
  display: flex;
3013
3722
  align-items: center;
3014
3723
  gap: var(--kds-spacing-1);
3015
- padding: var(--kds-spacing-2);
3724
+ /* Padding compacto: vertical spacing-1 (8px), horizontal spacing-2 (16px). */
3725
+ padding: var(--kds-spacing-1) var(--kds-spacing-2);
3016
3726
  border-radius: var(--kds-radius-md);
3017
3727
  border: 1px solid;
3018
3728
  font-family: var(--kds-font-family-primary);
3019
3729
  min-width: 0;
3020
- flex: 1 1 100%;
3021
- }
3022
-
3023
- @media (min-width: 768px) {
3024
- .kds-alert {
3025
- padding: var(--kds-spacing-2);
3026
- flex: 1 1 auto;
3027
- }
3730
+ /* Banner: ocupa siempre el ancho del contenedor. width explícito porque como
3731
+ flex item de una columna con align-items:center el cross-axis no se estira.
3732
+ Sin flex-grow para no estirarse verticalmente en contenedores flex-column. */
3733
+ width: 100%;
3734
+ flex: 0 1 auto;
3028
3735
  }
3029
3736
 
3030
3737
  .kds-alert:last-child {
@@ -3050,7 +3757,9 @@ footer > .kds-container-center {
3050
3757
  font-size: var(--kds-font-size-sm);
3051
3758
  font-weight: var(--kds-font-weight-semibold);
3052
3759
  line-height: var(--kds-line-height-normal);
3053
- margin-bottom: var(--kds-spacing-0-5);
3760
+ /* Solo margin-bottom: el padding del alert da el espaciado. Reset del resto para
3761
+ evitar el margin-block que BeerCSS aplica a <p> dentro del scope. */
3762
+ margin: 0 0 var(--kds-spacing-0-5);
3054
3763
  word-break: normal;
3055
3764
  overflow-wrap: break-word;
3056
3765
  }
@@ -3060,6 +3769,9 @@ footer > .kds-container-center {
3060
3769
  font-size: var(--kds-font-size-sm);
3061
3770
  font-weight: var(--kds-font-weight-regular);
3062
3771
  line-height: var(--kds-line-height-relaxed);
3772
+ /* Sin margen propio: el padding del alert es el único espaciado (evita el
3773
+ margin-block de <p> de BeerCSS dentro del scope). */
3774
+ margin: 0;
3063
3775
  word-break: normal;
3064
3776
  overflow-wrap: break-word;
3065
3777
  }
@@ -3105,6 +3817,53 @@ footer > .kds-container-center {
3105
3817
  color: var(--kds-color-error-dark);
3106
3818
  }
3107
3819
 
3820
+ /* Dark mode: the semantic *-dark shades used for text/icons above are too dark
3821
+ on the dark translucent alert surfaces (poor visibility). Use the alert
3822
+ *-text tokens (light shades in dark) so title, description and icon stay
3823
+ legible. Title/description inherit this color. Additive — light unchanged. */
3824
+ [data-theme="dark"] .kds-alert.kds-info,
3825
+ [data-theme="dark"] .kds-alert.kds-info .kds-alert-icon {
3826
+ color: var(--kds-alert-info-text);
3827
+ }
3828
+ [data-theme="dark"] .kds-alert.kds-success,
3829
+ [data-theme="dark"] .kds-alert.kds-success .kds-alert-icon {
3830
+ color: var(--kds-alert-success-text);
3831
+ }
3832
+ [data-theme="dark"] .kds-alert.kds-warning,
3833
+ [data-theme="dark"] .kds-alert.kds-warning .kds-alert-icon {
3834
+ color: var(--kds-alert-warning-text);
3835
+ }
3836
+ [data-theme="dark"] .kds-alert.kds-error,
3837
+ [data-theme="dark"] .kds-alert.kds-error .kds-alert-icon {
3838
+ color: var(--kds-alert-error-text);
3839
+ }
3840
+
3841
+ /* Close button — icon-only, discreto, no usa kds-btn para no romper el flex layout del alert */
3842
+ .kds-alert-close {
3843
+ flex-shrink: 0;
3844
+ display: inline-flex;
3845
+ align-items: center;
3846
+ justify-content: center;
3847
+ width: 28px;
3848
+ height: 28px;
3849
+ padding: 0;
3850
+ background: transparent;
3851
+ border: 0;
3852
+ border-radius: 50%;
3853
+ color: inherit;
3854
+ cursor: pointer;
3855
+ transition: background-color 0.15s ease;
3856
+ }
3857
+
3858
+ .kds-alert-close:hover {
3859
+ background: rgba(0, 0, 0, 0.06);
3860
+ }
3861
+
3862
+ .kds-alert-close i {
3863
+ font-size: 18px;
3864
+ line-height: 1;
3865
+ }
3866
+
3108
3867
  /* Lists inside alerts */
3109
3868
  .kds-alert ul {
3110
3869
  padding-left: var(--kds-spacing-2);
@@ -3707,6 +4466,11 @@ footer > .kds-container-center {
3707
4466
  color: var(--kds-color-primary-dark);
3708
4467
  }
3709
4468
 
4469
+ /* Dark: hover lightens (primary-dark is near-invisible on dark surfaces). */
4470
+ [data-theme="dark"] .page-card-link:hover {
4471
+ color: var(--kds-color-primary-light);
4472
+ }
4473
+
3710
4474
  .page-card-link i {
3711
4475
  font-size: var(--kds-font-size-xl);
3712
4476
  transition: transform var(--kds-transition-shorter) var(--kds-easing-standard);
@@ -4101,6 +4865,14 @@ dialog#surveyModal button.circle {
4101
4865
  text-align: center;
4102
4866
  }
4103
4867
 
4868
+ .kds-text-left {
4869
+ text-align: left;
4870
+ }
4871
+
4872
+ .kds-text-right {
4873
+ text-align: right;
4874
+ }
4875
+
4104
4876
  .kds-text-underline {
4105
4877
  text-decoration: underline;
4106
4878
  }
@@ -4113,10 +4885,62 @@ dialog#surveyModal button.circle {
4113
4885
  color: var(--on-surface-variant);
4114
4886
  }
4115
4887
 
4888
+ .kds-text-primary {
4889
+ color: var(--kds-color-primary-main);
4890
+ }
4891
+
4116
4892
  .kds-text-sm {
4117
4893
  font-size: var(--kds-font-size-sm);
4118
4894
  }
4119
4895
 
4896
+ /* ── Font-size scale utilities (additive — opt-in companions to .kds-text-sm) ── */
4897
+ .kds-text-base {
4898
+ font-size: var(--kds-font-size-base);
4899
+ }
4900
+
4901
+ .kds-text-lg {
4902
+ font-size: var(--kds-font-size-lg);
4903
+ }
4904
+
4905
+ .kds-text-xl {
4906
+ font-size: var(--kds-font-size-xl);
4907
+ }
4908
+
4909
+ .kds-text-2xl {
4910
+ font-size: var(--kds-font-size-2xl);
4911
+ }
4912
+
4913
+ /* ── Font-weight utilities ── */
4914
+ .kds-fw-regular {
4915
+ font-weight: var(--kds-font-weight-regular);
4916
+ }
4917
+
4918
+ .kds-fw-medium {
4919
+ font-weight: var(--kds-font-weight-medium);
4920
+ }
4921
+
4922
+ .kds-fw-semibold {
4923
+ font-weight: var(--kds-font-weight-semibold);
4924
+ }
4925
+
4926
+ .kds-fw-bold {
4927
+ font-weight: var(--kds-font-weight-bold);
4928
+ }
4929
+
4930
+ /* ── Text-transform utilities ── */
4931
+ .kds-text-transform-none {
4932
+ text-transform: none;
4933
+ }
4934
+
4935
+ /* ── Soft background utilities (semantic muted backgrounds for callouts) ── */
4936
+ .kds-bg-warning-soft {
4937
+ background-color: var(--kds-color-warning-soft);
4938
+ }
4939
+
4940
+ .kds-bg-info-soft {
4941
+ background-color: var(--kds-color-info-soft);
4942
+ }
4943
+
4120
4944
  /* ── Typography variants ── */
4121
4945
  .kds-text-display1 {
4122
4946
  font-family: var(--kds-font-family-primary, 'Public Sans', sans-serif);
@@ -4194,7 +5018,50 @@ dialog#surveyModal button.circle {
4194
5018
  text-transform: uppercase;
4195
5019
  }
4196
5020
 
5021
+ /* KV label — label del par clave↔valor (letra del dt de .kds-kv: "PAGO A" / "ASUNTO").
5022
+ line-height tight: en pares apilados el relaxed infla la separación de tinta. */
5023
+ .kds-text-kv-label {
5024
+ font-family: var(--kds-font-family-primary, 'Public Sans', sans-serif);
5025
+ font-size: var(--kds-font-size-xs);
5026
+ font-weight: var(--kds-font-weight-regular);
5027
+ line-height: var(--kds-line-height-tight);
5028
+ letter-spacing: normal;
5029
+ text-transform: uppercase;
5030
+ color: var(--kds-color-text-secondary);
5031
+ }
5032
+
5033
+ /* Value — dato/valor destacado (ej. nombre del comercio en el header del cobro).
5034
+ line-height tight: se usa en pares apilados y el relaxed infla la separación. */
5035
+ .kds-text-value {
5036
+ font-family: var(--kds-font-family-primary, 'Public Sans', sans-serif);
5037
+ font-size: var(--kds-font-size-sm);
5038
+ font-weight: var(--kds-font-weight-semibold);
5039
+ line-height: var(--kds-line-height-tight);
5040
+ color: var(--kds-color-gray-800);
5041
+ }
5042
+
5043
+ /* Code — texto tipo código/referencia (mono, uppercase, gris), como el "CÓDIGO" del header. */
5044
+ .kds-text-code {
5045
+ font-family: var(--kds-font-family-mono);
5046
+ font-size: var(--kds-font-size-xs);
5047
+ font-weight: var(--kds-font-weight-regular);
5048
+ letter-spacing: var(--kds-letter-spacing-wider);
5049
+ font-variant-numeric: tabular-nums;
5050
+ color: var(--kds-color-gray-400);
5051
+ text-transform: uppercase;
5052
+ }
5053
+
5054
+ /* Caption — texto muy chico y liviano para pie de página (ej. número de versión). */
5055
+ .kds-text-caption {
5056
+ font-family: var(--kds-font-family-primary, 'Public Sans', sans-serif);
5057
+ font-size: var(--kds-font-size-2xs);
5058
+ font-weight: var(--kds-font-weight-regular);
5059
+ line-height: var(--kds-line-height-normal);
5060
+ color: var(--kds-color-gray-400);
5061
+ }
5062
+
4197
5063
  .kds-text-link {
5064
+ cursor: pointer;
4198
5065
  font-family: var(--kds-font-family-primary, 'Public Sans', sans-serif);
4199
5066
  font-size: inherit;
4200
5067
  font-weight: var(--kds-font-weight-medium, 500);
@@ -4207,6 +5074,12 @@ dialog#surveyModal button.circle {
4207
5074
  color: var(--kds-color-primary-dark, #5B3179);
4208
5075
  }
4209
5076
 
5077
+ /* Inline emphasis: bold weight only, inherits size/color/family from context so it
5078
+ works as a highlighted fragment inside any other variant (body, heading, etc.). */
5079
+ .kds-text-strong {
5080
+ font-weight: var(--kds-font-weight-bold, 700);
5081
+ }
5082
+
4210
5083
  /* Display utilities */
4211
5084
  .kds-hidden {
4212
5085
  display: none;
@@ -4232,13 +5105,13 @@ dialog#surveyModal button.circle {
4232
5105
 
4233
5106
  /* Color utilities (solo para casos específicos de demo) */
4234
5107
  .kds-text-warning {
4235
- color: #92400E;
5108
+ color: var(--kds-alert-warning-text); /* #663C00 light / #FFB74D dark */
4236
5109
  }
4237
5110
 
4238
5111
  .kds-list-warning {
4239
5112
  margin: 0;
4240
5113
  padding-left: var(--kds-spacing-5);
4241
- color: #78350F;
5114
+ color: var(--kds-alert-warning-text); /* #663C00 light / #FFB74D dark */
4242
5115
  }
4243
5116
 
4244
5117
  /* ========================================
@@ -4296,7 +5169,25 @@ dialog#surveyModal button.circle {
4296
5169
  flex-shrink: 0;
4297
5170
  }
4298
5171
 
5172
+ /* Versión de la app anclada al fondo del card body (flex column): margin-top
5173
+ auto absorbe el espacio libre; el padding garantiza respiro mínimo cuando el
5174
+ contenido llena la card. */
5175
+ .kds-card-version {
5176
+ margin-top: auto;
5177
+ padding-top: var(--kds-spacing-2);
5178
+ text-align: center;
5179
+ }
5180
+
5181
+ /* Separador vertical (entre chips/logos hermanos en una fila). */
5182
+ .kds-vr {
5183
+ width: 1px;
5184
+ height: var(--kds-spacing-3);
5185
+ background: var(--kds-color-divider);
5186
+ flex: 0 0 auto;
5187
+ }
5188
+
4299
5189
  /* Gap utilities */
5190
+ .kds-gap-0-25 { gap: var(--kds-spacing-0-25); }
4300
5191
  .kds-gap-1 { gap: var(--kds-spacing-1); }
4301
5192
  .kds-gap-2 { gap: var(--kds-spacing-2); }
4302
5193
  .kds-gap-3 { gap: var(--kds-spacing-3); }
@@ -4327,7 +5218,9 @@ dialog#surveyModal button.circle {
4327
5218
  ======================================== */
4328
5219
 
4329
5220
  .kds-badge {
4330
- display: inline-block;
5221
+ display: inline-flex;
5222
+ align-items: center;
5223
+ gap: 4px;
4331
5224
  padding: 2px 8px;
4332
5225
  border-radius: var(--kds-radius-sm);
4333
5226
  font-size: var(--kds-font-size-xs);
@@ -4335,6 +5228,40 @@ dialog#surveyModal button.circle {
4335
5228
  line-height: 1.5;
4336
5229
  }
4337
5230
 
5231
+ /* Icon prefix inside chip — small inline icon */
5232
+ .kds-badge > i.material-symbols-outlined {
5233
+ font-size: 14px;
5234
+ line-height: 1;
5235
+ }
5236
+
5237
+ /* Deletable chip close button — discreto, sin kds-btn (eso lo agrandaba ridículo) */
5238
+ .kds-badge-close {
5239
+ display: inline-flex;
5240
+ align-items: center;
5241
+ justify-content: center;
5242
+ width: 16px;
5243
+ height: 16px;
5244
+ margin-right: -2px;
5245
+ padding: 0;
5246
+ background: transparent;
5247
+ border: 0;
5248
+ border-radius: 50%;
5249
+ color: inherit;
5250
+ cursor: pointer;
5251
+ opacity: 0.7;
5252
+ transition: opacity 0.15s, background-color 0.15s;
5253
+ }
5254
+
5255
+ .kds-badge-close:hover {
5256
+ opacity: 1;
5257
+ background: rgba(0, 0, 0, 0.1);
5258
+ }
5259
+
5260
+ .kds-badge-close i {
5261
+ font-size: 14px;
5262
+ line-height: 1;
5263
+ }
5264
+
4338
5265
  .kds-badge.success {
4339
5266
  background-color: var(--kds-color-success-container);
4340
5267
  color: var(--kds-color-success-dark);
@@ -4360,6 +5287,30 @@ dialog#surveyModal button.circle {
4360
5287
  color: var(--kds-color-primary-dark);
4361
5288
  }
4362
5289
 
5290
+ /* Dark: semantic *-dark text is near-invisible on dark surfaces (and the
5291
+ *-container background tokens are not generated). Use the alert bg/text
5292
+ pairs, which are dark-aware; primary uses selected/light. */
5293
+ [data-theme="dark"] .kds-badge.success {
5294
+ background-color: var(--kds-alert-success-bg);
5295
+ color: var(--kds-alert-success-text);
5296
+ }
5297
+ [data-theme="dark"] .kds-badge.error {
5298
+ background-color: var(--kds-alert-error-bg);
5299
+ color: var(--kds-alert-error-text);
5300
+ }
5301
+ [data-theme="dark"] .kds-badge.warning {
5302
+ background-color: var(--kds-alert-warning-bg);
5303
+ color: var(--kds-alert-warning-text);
5304
+ }
5305
+ [data-theme="dark"] .kds-badge.info {
5306
+ background-color: var(--kds-alert-info-bg);
5307
+ color: var(--kds-alert-info-text);
5308
+ }
5309
+ [data-theme="dark"] .kds-badge.primary {
5310
+ background-color: var(--kds-color-primary-selected);
5311
+ color: var(--kds-color-primary-light);
5312
+ }
5313
+
4363
5314
  /* ========================================
4364
5315
  DIVIDER WITH TEXT
4365
5316
  Separator with optional text
@@ -4546,21 +5497,46 @@ dialog#surveyModal button.circle {
4546
5497
  flex-shrink: 0;
4547
5498
  }
4548
5499
 
4549
- /* Merchant tile with logo variant */
5500
+ /* Merchant tile with logo variant — el logo (avatar del comercio) llena TODO el tile.
5501
+ `display: block` evita el desborde del auto-grid; `overflow: hidden` recorta la imagen
5502
+ a las esquinas redondeadas del tile. La imagen va CUADRADA (sin border-radius propio):
5503
+ solo el contenedor recorta, evitando el hairline sub-pixel del doble redondeo. El fondo
5504
+ se deja transparente bajo el logo como respaldo (no debe verse púrpura en los bordes). */
4550
5505
  .kds-merchant-tile.logo {
4551
- padding: var(--kds-spacing-0-5);
5506
+ padding: 0;
5507
+ display: block;
5508
+ overflow: hidden;
5509
+ background: transparent;
4552
5510
  }
4553
5511
 
4554
5512
  .kds-merchant-tile.logo img {
4555
5513
  width: 100%;
4556
5514
  height: 100%;
4557
5515
  object-fit: cover;
4558
- border-radius: var(--kds-radius-md);
5516
+ border-radius: 0;
5517
+ display: block;
5518
+ }
5519
+
5520
+ /* Variante para logos que traen su propio marco/forma (logos de comercio/servicio,
5521
+ no fotos): `contain` + padding para que el logo entre completo sin recortar sus
5522
+ esquinas. Opt-in: pasar className="kds-merchant-tile--contain". */
5523
+ .kds-merchant-tile.logo.kds-merchant-tile--contain {
5524
+ padding: var(--kds-spacing-1);
5525
+ }
5526
+ .kds-merchant-tile.logo.kds-merchant-tile--contain img {
5527
+ object-fit: contain;
4559
5528
  }
4560
5529
 
4561
5530
  .kds-merchant-meta {
4562
5531
  display: flex;
4563
5532
  flex-direction: column;
5533
+ gap: var(--kds-spacing-0-5);
5534
+ }
5535
+
5536
+ /* El meta controla su propio espaciado: anula el margin sibling de BeerCSS
5537
+ (`… + :is(p,h*,…) { margin-block-start: 1rem }`) que separaba de más las líneas. */
5538
+ .kds-merchant-meta > * {
5539
+ margin-block: 0 !important;
4564
5540
  }
4565
5541
 
4566
5542
  .kds-merchant-label {
@@ -4613,6 +5589,11 @@ dialog#surveyModal button.circle {
4613
5589
  color: var(--kds-color-text-primary);
4614
5590
  }
4615
5591
 
5592
+ /* Opt-in color modifier — switches the value to the LigoPay/khipu-blue tone */
5593
+ .kds-amount-value--info {
5594
+ color: var(--kds-color-info-blue); /* #5A5FE0 */
5595
+ }
5596
+
4616
5597
  .kds-amount-value small {
4617
5598
  font-size: 18px;
4618
5599
  font-weight: 500;
@@ -4819,6 +5800,26 @@ dialog#surveyModal button.circle {
4819
5800
  background: var(--kds-color-hover-bg);
4820
5801
  }
4821
5802
 
5803
+ /* BeerCSS forces `border-radius: .5rem !important` on button:active
5804
+ (:is(.button,button):not(.chip):active — specificity 0,3,0). Keep each
5805
+ row-button's own radius in the active state: doubled class matches that
5806
+ specificity and this file loads after beer.css, so these win. */
5807
+ .kds-bank-row.kds-bank-row:active {
5808
+ border-radius: var(--kds-radius-card) !important;
5809
+ }
5810
+ .kds-qr-row.kds-qr-row:active {
5811
+ border-radius: var(--kds-radius-card) !important;
5812
+ }
5813
+ .kds-copy-row.kds-copy-row:active {
5814
+ border-radius: var(--kds-radius-row) !important;
5815
+ }
5816
+ .kds-copyable-table-row.kds-copyable-table-row:active {
5817
+ border-radius: var(--kds-spacing-0-75) !important;
5818
+ }
5819
+ .kds-bank-item.kds-bank-item:active {
5820
+ border-radius: var(--kds-radius-lg) !important;
5821
+ }
5822
+
4822
5823
  .kds-bank-row-logo {
4823
5824
  width: 40px;
4824
5825
  height: 40px;
@@ -4847,55 +5848,196 @@ dialog#surveyModal button.circle {
4847
5848
  flex-shrink: 0;
4848
5849
  }
4849
5850
 
4850
- /* Selected state */
4851
- .kds-bank-row.selected {
4852
- border-color: var(--kds-color-primary-main);
4853
- background: var(--kds-color-primary-faint);
5851
+ /* Selected state */
5852
+ .kds-bank-row.selected {
5853
+ border-color: var(--kds-color-primary-main);
5854
+ background: var(--kds-color-primary-faint);
5855
+ }
5856
+
5857
+ .kds-bank-row.selected > i {
5858
+ color: var(--kds-color-primary-main);
5859
+ }
5860
+
5861
+ /* Disabled state */
5862
+ .kds-bank-row:disabled,
5863
+ .kds-bank-row.disabled {
5864
+ opacity: 0.5;
5865
+ cursor: not-allowed;
5866
+ pointer-events: none;
5867
+ }
5868
+
5869
+ /* ========================================
5870
+ 08 - STICKY CTA BAR (.kds-cta-bar)
5871
+ Fixed bottom action bar with primary button
5872
+ ======================================== */
5873
+
5874
+ .kds-cta-bar {
5875
+ position: sticky;
5876
+ bottom: 0;
5877
+ display: flex;
5878
+ gap: var(--kds-spacing-1-25);
5879
+ padding: var(--kds-spacing-2);
5880
+ background: var(--kds-color-background-default); /* #FFF light / #181818 dark */
5881
+ border-top: 1px solid var(--kds-color-divider);
5882
+ padding-bottom: calc(var(--kds-spacing-2) + env(safe-area-inset-bottom));
5883
+ z-index: 100;
5884
+ }
5885
+
5886
+ .kds-cta-bar > button {
5887
+ flex: 1;
5888
+ min-height: 48px;
5889
+ }
5890
+
5891
+ .kds-cta-bar > button.primary,
5892
+ .kds-cta-bar > button.kds-btn-primary {
5893
+ flex: 1;
5894
+ }
5895
+
5896
+ /* Floating variant (no border, with shadow) */
5897
+ .kds-cta-bar.floating {
5898
+ border-top: none;
5899
+ box-shadow: 0 -4px 16px rgba(16,24,40,.08);
5900
+ }
5901
+
5902
+ /* ========================================
5903
+ 08b - FAB (.kds-fab)
5904
+ Circular, icon-only floating action button with an animated hide state.
5905
+ Presentational: drive `.kds-fab--hidden` from useHideOnScroll for scroll-away.
5906
+ ======================================== */
5907
+
5908
+ .kds-fab {
5909
+ display: inline-flex;
5910
+ align-items: center;
5911
+ justify-content: center;
5912
+ width: var(--kds-spacing-5);
5913
+ height: var(--kds-spacing-5);
5914
+ padding: 0;
5915
+ border: none;
5916
+ border-radius: var(--kds-radius-full);
5917
+ background: var(--kds-color-background-paper);
5918
+ color: var(--kds-color-text-primary);
5919
+ box-shadow: var(--kds-shadow-elevated);
5920
+ cursor: pointer;
5921
+ z-index: 100;
5922
+ /* Neutralize native button rendering so the pressed state doesn't flash a square. */
5923
+ -webkit-appearance: none;
5924
+ appearance: none;
5925
+ -webkit-tap-highlight-color: transparent;
5926
+ transition: opacity var(--kds-transition-short) ease, transform var(--kds-transition-short) ease,
5927
+ background-color var(--kds-transition-shorter) ease;
5928
+ }
5929
+
5930
+ /* Keep the circle in EVERY interaction state. BeerCSS forces
5931
+ `border-radius: .5rem !important` on button:active (specificity 0,3,0), so
5932
+ plain specificity is not enough — match it with a doubled class AND
5933
+ !important; this file loads after beer.css, so these win. */
5934
+ button.kds-fab.kds-fab,
5935
+ button.kds-fab.kds-fab:hover,
5936
+ button.kds-fab.kds-fab:active,
5937
+ button.kds-fab.kds-fab.active,
5938
+ button.kds-fab.kds-fab:focus,
5939
+ button.kds-fab.kds-fab:focus-visible {
5940
+ border-radius: var(--kds-radius-full) !important;
5941
+ background: var(--kds-color-background-paper);
5942
+ }
5943
+
5944
+ button.kds-fab:hover:not(:disabled) {
5945
+ background: var(--kds-color-background-muted);
5946
+ }
5947
+
5948
+ /* Subtle press feedback that preserves the round shape. */
5949
+ button.kds-fab:active:not(:disabled) {
5950
+ transform: translateY(var(--kds-spacing-0-25));
5951
+ }
5952
+
5953
+ button.kds-fab:focus-visible {
5954
+ outline: var(--kds-spacing-0-25) solid var(--kds-color-primary-main);
5955
+ outline-offset: var(--kds-spacing-0-25);
5956
+ }
5957
+
5958
+ .kds-fab > i.material-symbols-outlined {
5959
+ font-size: var(--kds-spacing-button-icon-size);
5960
+ line-height: 1;
5961
+ }
5962
+
5963
+ /* Pinned corners — fixed to the viewport (stays visible on scroll-up, iframe-friendly).
5964
+ Horizontally the top-right FAB hugs the right edge of the centered content column set via
5965
+ `--kds-fab-column-width` (default 100% → viewport edge). The consumer sets that var to its
5966
+ widget width so on desktop the FAB overlaps the header's top-right corner instead of drifting
5967
+ to the far viewport edge. `max(...)` keeps at least a token inset on narrow (mobile) viewports. */
5968
+ .kds-fab--top-right,
5969
+ .kds-fab--top-left,
5970
+ .kds-fab--bottom-right,
5971
+ .kds-fab--bottom-left {
5972
+ position: fixed;
5973
+ }
5974
+
5975
+ .kds-fab--top-right {
5976
+ top: var(--kds-spacing-2);
5977
+ right: max(var(--kds-spacing-2), calc((100% - var(--kds-fab-column-width, 100%)) / 2 + var(--kds-spacing-2)));
5978
+ }
5979
+
5980
+ .kds-fab--top-left {
5981
+ top: var(--kds-spacing-2);
5982
+ left: max(var(--kds-spacing-2), calc((100% - var(--kds-fab-column-width, 100%)) / 2 + var(--kds-spacing-2)));
5983
+ }
5984
+
5985
+ .kds-fab--bottom-right {
5986
+ bottom: var(--kds-spacing-2);
5987
+ right: max(var(--kds-spacing-2), calc((100% - var(--kds-fab-column-width, 100%)) / 2 + var(--kds-spacing-2)));
4854
5988
  }
4855
5989
 
4856
- .kds-bank-row.selected > i {
4857
- color: var(--kds-color-primary-main);
5990
+ .kds-fab--bottom-left {
5991
+ bottom: var(--kds-spacing-2);
5992
+ left: max(var(--kds-spacing-2), calc((100% - var(--kds-fab-column-width, 100%)) / 2 + var(--kds-spacing-2)));
4858
5993
  }
4859
5994
 
4860
- /* Disabled state */
4861
- .kds-bank-row:disabled,
4862
- .kds-bank-row.disabled {
4863
- opacity: 0.5;
4864
- cursor: not-allowed;
4865
- pointer-events: none;
5995
+ /* Desktop: nudge the top-right FAB up so it sits partially over the header's top edge. */
5996
+ @media (min-width: 768px) {
5997
+ .kds-fab--top-right {
5998
+ top: var(--kds-spacing-1);
5999
+ }
4866
6000
  }
4867
6001
 
4868
- /* ========================================
4869
- 08 - STICKY CTA BAR (.kds-cta-bar)
4870
- Fixed bottom action bar with primary button
4871
- ======================================== */
6002
+ /* Corner-overlap: mount the FAB centered OVER the top-right corner of the content column
6003
+ (`--kds-fab-column-width`), overlapping outward — vs `--top-right` which insets inside the
6004
+ column. Mobile keeps a small inset from the viewport via the `--kds-fab-inset` token. */
6005
+ .kds-fab--corner {
6006
+ --kds-fab-inset: 5px;
6007
+ position: fixed;
6008
+ top: var(--kds-fab-inset);
6009
+ right: var(--kds-fab-inset);
6010
+ }
4872
6011
 
4873
- .kds-cta-bar {
4874
- position: sticky;
4875
- bottom: 0;
4876
- display: flex;
4877
- gap: var(--kds-spacing-1-25);
4878
- padding: var(--kds-spacing-2);
4879
- background: #FFFFFF;
4880
- border-top: 1px solid var(--kds-color-divider);
4881
- padding-bottom: calc(var(--kds-spacing-2) + env(safe-area-inset-bottom));
4882
- z-index: 100;
6012
+ @media (min-width: 768px) {
6013
+ .kds-fab--corner {
6014
+ right: max(
6015
+ var(--kds-fab-inset),
6016
+ calc((100% - var(--kds-fab-column-width, 100%)) / 2 - var(--kds-spacing-5) / 2)
6017
+ );
6018
+ }
4883
6019
  }
4884
6020
 
4885
- .kds-cta-bar > button {
4886
- flex: 1;
4887
- min-height: 48px;
6021
+ /* Hidden (scroll-away) state fade + slide toward the nearest edge */
6022
+ .kds-fab--hidden {
6023
+ opacity: 0;
6024
+ transform: translateY(calc(-1 * var(--kds-spacing-1))) scale(0.9);
6025
+ pointer-events: none;
4888
6026
  }
4889
6027
 
4890
- .kds-cta-bar > button.primary,
4891
- .kds-cta-bar > button.kds-btn-primary {
4892
- flex: 1;
6028
+ .kds-fab--bottom-right.kds-fab--hidden,
6029
+ .kds-fab--bottom-left.kds-fab--hidden {
6030
+ transform: translateY(var(--kds-spacing-1)) scale(0.9);
4893
6031
  }
4894
6032
 
4895
- /* Floating variant (no border, with shadow) */
4896
- .kds-cta-bar.floating {
4897
- border-top: none;
4898
- box-shadow: 0 -4px 16px rgba(16,24,40,.08);
6033
+ @media (prefers-reduced-motion: reduce) {
6034
+ .kds-fab {
6035
+ transition: opacity var(--kds-transition-short) ease;
6036
+ }
6037
+
6038
+ .kds-fab--hidden {
6039
+ transform: none;
6040
+ }
4899
6041
  }
4900
6042
 
4901
6043
  /* ========================================
@@ -4977,22 +6119,22 @@ dialog#surveyModal button.circle {
4977
6119
  padding: var(--kds-spacing-1) 0;
4978
6120
  }
4979
6121
 
4980
- /* Desktop: footer inside card hidden; Mobile: footer outside card hidden */
6122
+ /* Footer SIEMPRE fuera del card body (desktop y mobile): el footer interno
6123
+ (variant="inside") queda oculto en todos los breakpoints; se muestra el
6124
+ externo, hermano directo del card bajo `.kds-screen`. */
4981
6125
  .kds-card-elevated > .kds-secure-footer {
4982
6126
  display: none;
4983
6127
  }
4984
6128
 
4985
- @media (max-width: 767px) {
4986
- .kds-card-elevated > .kds-secure-footer {
4987
- display: flex;
4988
- }
4989
- .kds-screen > .kds-secure-footer {
4990
- display: none;
4991
- }
6129
+ .kds-screen > form {
6130
+ margin-block-start: 0 !important;
6131
+ }
4992
6132
 
4993
- .kds-screen > form {
4994
- margin-block-start: 0 !important;
4995
- }
6133
+ /* Footer externo adyacente a la card: cancela el gap del .kds-screen para que
6134
+ quede pegado debajo de la card y no flotando lejos. Las dos cards
6135
+ (header/body) sí conservan su separación. */
6136
+ .kds-screen > .kds-secure-footer {
6137
+ margin-top: calc(-1 * var(--kds-spacing-3));
4996
6138
  }
4997
6139
 
4998
6140
  .kds-secure-footer-lock {
@@ -5014,6 +6156,32 @@ dialog#surveyModal button.circle {
5014
6156
  display: block;
5015
6157
  object-fit: contain;
5016
6158
  overflow: visible;
6159
+ /* Corrección óptica: la masa visible del wordmark (el cuerpo de "Khipu") queda sobre el
6160
+ centro geométrico de su viewBox (45x15) porque el descendente de la "p" ocupa la parte
6161
+ baja. Al centrar por caja (align-items:center) el logo se ve ~1px alto respecto al texto,
6162
+ así que se baja 0.75px para alinear el centro óptico. Alto fijo (11px) → nudge consistente. */
6163
+ position: relative;
6164
+ top: 0.75px;
6165
+ }
6166
+
6167
+ /* Divisor entre el logo de Khipu y el del PSP ("Khipu | klap") */
6168
+ .kds-secure-footer-sep {
6169
+ width: 1px;
6170
+ height: 0.85em;
6171
+ background: currentColor;
6172
+ opacity: 0.35;
6173
+ flex: 0 0 auto;
6174
+ }
6175
+
6176
+ /* Logo del PSP (proveedor de pagos): lo provee el consumidor, lo alineamos al mismo
6177
+ alto que el mark de Khipu. Sirve para <img> o un elemento con .kds-psp-mark. */
6178
+ .kds-secure-footer img,
6179
+ .kds-secure-footer .kds-psp-mark {
6180
+ height: 11px;
6181
+ width: auto;
6182
+ flex: 0 0 auto;
6183
+ display: block;
6184
+ object-fit: contain;
5017
6185
  }
5018
6186
 
5019
6187
  .kds-secure-footer-code {
@@ -5029,26 +6197,45 @@ dialog#surveyModal button.circle {
5029
6197
  background: none;
5030
6198
  border: 0;
5031
6199
  padding: 0;
6200
+ /* Reset del bare `button { height: 2.5rem }` de BeerCSS (scoped en .kds-theme-root):
6201
+ sin esto el toggle quedaba en una caja de 40px con el texto centrado → espacio
6202
+ irregular arriba (pegado al resumen) y abajo (~26px hasta el panel). Con height
6203
+ auto el botón ajusta a su contenido y el ritmo lo gobiernan los márgenes. */
6204
+ height: auto;
5032
6205
  color: var(--kds-color-primary-main);
5033
6206
  font-weight: var(--kds-font-weight-medium);
5034
6207
  font-size: var(--kds-font-size-sm);
5035
- padding-bottom: var(--kds-spacing-1-25);
5036
6208
  cursor: pointer;
5037
6209
  display: inline-flex;
5038
6210
  align-items: center;
5039
6211
  gap: 0;
5040
6212
  }
5041
6213
 
5042
- .kds-expand-toggle:hover {
6214
+ .kds-expand-toggle:hover,
6215
+ .kds-expand-toggle:focus,
6216
+ .kds-expand-toggle:active {
5043
6217
  background: transparent;
6218
+ outline: none;
6219
+ box-shadow: none;
5044
6220
  }
5045
6221
 
5046
- .kds-expand-toggle:hover span {
6222
+ .kds-expand-toggle:hover span,
6223
+ .kds-expand-toggle:focus span,
6224
+ .kds-expand-toggle:active span {
5047
6225
  text-decoration: underline;
5048
6226
  }
5049
6227
 
5050
6228
  .kds-expand-toggle i {
5051
6229
  font-size: var(--kds-font-size-base);
6230
+ /* BeerCSS estiliza el `i` bare como caja flex de 30px (height/width/min: 30px), que
6231
+ inflaba el toggle y dejaba aire vertical de más alrededor del label. La achicamos a
6232
+ 1.25em MANTENIENDO el centrado flex de BeerCSS (display:flex/align/justify) — así el
6233
+ caret queda compacto Y ópticamente centrado con el texto (no usar height:auto ni
6234
+ line-height:1: descentran el glifo). */
6235
+ width: 1.25em;
6236
+ height: 1.25em;
6237
+ min-width: 0;
6238
+ min-height: 0;
5052
6239
  transition: transform var(--kds-transition-shorter, 0.2s);
5053
6240
  }
5054
6241
 
@@ -5068,7 +6255,10 @@ dialog#surveyModal button.circle {
5068
6255
  }
5069
6256
 
5070
6257
  .kds-expand-panel.open {
5071
- max-height: 800px;
6258
+ /* Fallback cap for no-JS consumers. With JS (React useExpandToggle / vanilla
6259
+ initExpandToggle) the panel's inline max-height is set to its exact scrollHeight,
6260
+ which overrides this and prevents clipping regardless of content length. */
6261
+ max-height: 2000px;
5072
6262
  margin-top: 0;
5073
6263
  }
5074
6264
 
@@ -5186,13 +6376,17 @@ dialog#surveyModal button.circle {
5186
6376
 
5187
6377
  .kds-status-block {
5188
6378
  text-align: center;
5189
- padding: var(--kds-spacing-6) 0;
6379
+ /* Padding vertical estándar: spacing-2 (16px), el mismo rhythm inter-elementos
6380
+ que usan kds-btn-stack y kds-hr-dashed. NO usar spacing-6 (48px): genera
6381
+ demasiado espacio cuando el bloque se compone con header/divider alrededor. */
6382
+ padding: var(--kds-spacing-2) 0;
5190
6383
  }
5191
6384
 
5192
6385
  .kds-status-block-icon {
5193
6386
  width: var(--kds-status-icon-size);
5194
6387
  height: var(--kds-status-icon-size);
5195
- margin: 0 auto var(--kds-spacing-3);
6388
+ /* spacing-1 (8px) icono→título: elementos relacionados dentro del bloque. */
6389
+ margin: 0 auto var(--kds-spacing-1);
5196
6390
  border-radius: var(--kds-radius-full);
5197
6391
  display: grid;
5198
6392
  place-items: center;
@@ -5205,15 +6399,22 @@ dialog#surveyModal button.circle {
5205
6399
 
5206
6400
  .kds-status-block-title {
5207
6401
  font-size: var(--kds-font-size-xl);
5208
- font-weight: var(--kds-font-weight-bold);
6402
+ font-weight: var(--kds-font-weight-semibold);
5209
6403
  color: var(--kds-color-text-primary);
5210
6404
  margin: 0 0 var(--kds-spacing-1);
6405
+ /* El reset global :where(h2) usa estas vars con !important; así se overridea. */
6406
+ --kds-typography-h2-margin-block-end: var(--kds-spacing-1);
5211
6407
  }
5212
6408
 
5213
6409
  .kds-status-block-description {
5214
6410
  font-size: var(--kds-font-size-sm);
5215
- color: var(--kds-color-text-secondary);
6411
+ /* Mismo color que el título: título y subtítulo parejos en resultados. */
6412
+ color: var(--kds-color-text-primary);
5216
6413
  margin: 0;
6414
+ /* !important: anula el auto-espaciado de hermanos de BeerCSS
6415
+ (margin-block-start: 1rem, especificidad 0,5,1 imposible de igualar).
6416
+ La separación título→descripción la da el margin-bottom del título. */
6417
+ margin-top: 0 !important;
5217
6418
  }
5218
6419
 
5219
6420
  /* Variant: success */
@@ -5223,11 +6424,11 @@ dialog#surveyModal button.circle {
5223
6424
  color: var(--kds-color-success-main);
5224
6425
  }
5225
6426
 
5226
- /* Variant: pending (animated spinner) */
6427
+ /* Variant: pending (animated blue spinner, sin icono interno) */
5227
6428
  .kds-status-block[data-status="pending"] .kds-status-block-icon {
5228
6429
  background: transparent;
5229
6430
  border: 3px solid var(--kds-color-divider);
5230
- border-top-color: var(--kds-color-primary-main);
6431
+ border-top-color: var(--kds-color-info-main);
5231
6432
  animation: kds-spin 1s linear infinite;
5232
6433
  }
5233
6434
 
@@ -5245,7 +6446,9 @@ dialog#surveyModal button.circle {
5245
6446
  color: var(--kds-color-error-main);
5246
6447
  }
5247
6448
 
5248
- /* Variant: info */
6449
+ /* Variant: info — usar SIEMPRE con icon "info_i" (i minúscula sin glyph circular)
6450
+ para que solo se vea nuestro círculo decorativo. Iconos como "info" tienen
6451
+ su propio círculo built-in → causarían doble círculo. */
5249
6452
  .kds-status-block[data-status="info"] .kds-status-block-icon {
5250
6453
  background: var(--kds-color-info-soft);
5251
6454
  border: var(--kds-border-width-md) solid var(--kds-color-info-main);
@@ -5257,7 +6460,7 @@ dialog#surveyModal button.circle {
5257
6460
  text-align: left;
5258
6461
  display: flex;
5259
6462
  align-items: center;
5260
- gap: var(--kds-spacing-3);
6463
+ gap: var(--kds-spacing-2);
5261
6464
  padding: var(--kds-spacing-1) 0;
5262
6465
  }
5263
6466
 
@@ -5299,6 +6502,9 @@ dialog#surveyModal button.circle {
5299
6502
  .kds-bottom-sheet {
5300
6503
  width: 100%;
5301
6504
  max-width: var(--kds-bottom-sheet-max-width);
6505
+ max-height: 90dvh;
6506
+ display: flex;
6507
+ flex-direction: column;
5302
6508
  background: var(--kds-color-background-paper);
5303
6509
  border-radius: var(--kds-radius-2xl) var(--kds-radius-2xl) 0 0;
5304
6510
  padding: var(--kds-spacing-3-5) var(--kds-spacing-3) var(--kds-spacing-3);
@@ -5319,6 +6525,42 @@ dialog#surveyModal button.circle {
5319
6525
  background: var(--kds-color-divider);
5320
6526
  }
5321
6527
 
6528
+ /* Close button — top-right discreto, NO usa kds-btn (rompía el flex layout) */
6529
+ .kds-bottom-sheet-close {
6530
+ position: absolute;
6531
+ top: var(--kds-spacing-1);
6532
+ right: var(--kds-spacing-1);
6533
+ width: 32px;
6534
+ height: 32px;
6535
+ display: inline-flex;
6536
+ align-items: center;
6537
+ justify-content: center;
6538
+ padding: 0;
6539
+ background: transparent;
6540
+ border: 0;
6541
+ border-radius: 50%;
6542
+ color: var(--kds-color-text-secondary);
6543
+ cursor: pointer;
6544
+ transition: background-color 0.15s ease;
6545
+ }
6546
+
6547
+ .kds-bottom-sheet-close:hover {
6548
+ background: rgba(0, 0, 0, 0.06);
6549
+ }
6550
+
6551
+ .kds-bottom-sheet-close i {
6552
+ font-size: 20px;
6553
+ line-height: 1;
6554
+ }
6555
+
6556
+ /* Body container — separa el content del title/actions */
6557
+ .kds-bottom-sheet-body {
6558
+ text-align: left;
6559
+ margin-top: var(--kds-spacing-1);
6560
+ overflow-y: auto;
6561
+ min-height: 0;
6562
+ }
6563
+
5322
6564
  .kds-bottom-sheet-icon {
5323
6565
  font-size: var(--kds-font-size-4xl);
5324
6566
  }
@@ -5365,6 +6607,19 @@ dialog#surveyModal button.circle {
5365
6607
  to { opacity: 1; }
5366
6608
  }
5367
6609
 
6610
+ /* Botón con llenado progresivo (feedback visual de auto-cierre).
6611
+ Duración parametrizable con --kds-fill-duration (default 12s). */
6612
+ .kds-btn-fill-progress {
6613
+ background-image: linear-gradient(to right, var(--kds-color-primary-dark), var(--kds-color-primary-dark));
6614
+ background-repeat: no-repeat;
6615
+ background-size: 0% 100%;
6616
+ animation: kds-btn-fill var(--kds-fill-duration, 12s) linear forwards;
6617
+ }
6618
+
6619
+ @keyframes kds-btn-fill {
6620
+ to { background-size: 100% 100%; }
6621
+ }
6622
+
5368
6623
  @keyframes kds-rise {
5369
6624
  from { transform: translateY(var(--kds-spacing-2-5)); opacity: 0.4; }
5370
6625
  to { transform: none; opacity: 1; }
@@ -5552,6 +6807,30 @@ dialog#surveyModal button.circle {
5552
6807
  /* No max-height — clip-path on parent handles visual collapse without layout changes */
5553
6808
  .kds-invoice-sticky .kds-invoice-collapsible {
5554
6809
  opacity: calc(1 - var(--collapse-progress) * 1.5);
6810
+
6811
+ /* Scroll interno discreto (mobile): cuando el detalle abierto excede el viewport, el
6812
+ collapsible scrollea internamente (monto/merchant quedan fijos arriba) para poder leer
6813
+ todo el detalle. Al tocar el fondo, `overscroll-behavior: auto` ENCADENA el gesto al
6814
+ scroll del body → dispara useStickyInvoiceCollapse → colapsa el header y aparece el form
6815
+ (flujo natural). Se auto-desactiva con el detalle cerrado (contenido más corto que el
6816
+ max-height → sin scroll ni efecto visual). */
6817
+ max-height: calc(100vh - var(--kds-spacing-12) - var(--kds-spacing-4));
6818
+ max-height: calc(100dvh - var(--kds-spacing-12) - var(--kds-spacing-4));
6819
+ overflow-y: auto;
6820
+ overflow-x: hidden;
6821
+ overscroll-behavior-y: auto;
6822
+ scrollbar-width: thin;
6823
+ scrollbar-color: var(--kds-color-gray-300) transparent;
6824
+ }
6825
+ .kds-invoice-sticky .kds-invoice-collapsible::-webkit-scrollbar {
6826
+ width: 4px;
6827
+ }
6828
+ .kds-invoice-sticky .kds-invoice-collapsible::-webkit-scrollbar-thumb {
6829
+ background: var(--kds-color-gray-300);
6830
+ border-radius: var(--kds-radius-full);
6831
+ }
6832
+ .kds-invoice-sticky .kds-invoice-collapsible::-webkit-scrollbar-track {
6833
+ background: transparent;
5555
6834
  }
5556
6835
 
5557
6836
  /* Amount: 30px → 24px (progressive) */
@@ -5623,6 +6902,14 @@ dialog#surveyModal button.circle {
5623
6902
  margin-top: var(--kds-spacing-1);
5624
6903
  }
5625
6904
 
6905
+ /* Ritmo uniforme alrededor del toggle: spacing-2 arriba (resumen↔"Ver detalle")
6906
+ iguala el padding-top spacing-2 que la .kds-detail-list da debajo
6907
+ ("Ver detalle"↔primer label del panel). Antes el alto fijo del bare button
6908
+ (40px) hacía que el espacio de arriba y de abajo fueran distintos. */
6909
+ .kds-invoice-collapsible > .kds-expand-toggle {
6910
+ margin-top: var(--kds-spacing-2);
6911
+ }
6912
+
5626
6913
  /* -- Invoice Summary -- */
5627
6914
  .kds-invoice-summary {
5628
6915
  display: flex;
@@ -5646,6 +6933,22 @@ div.kds-invoice-header,
5646
6933
  padding: 0;
5647
6934
  border-radius: 0;
5648
6935
  align-content: initial;
6936
+ /* Override BeerCSS `header { min-height: ... }` — el header debe ajustarse a su
6937
+ contenido (sigue el alto del merchant tile: 64px→50px al colapsar). Sin esto
6938
+ quedaba un min-height fijo de 64px → hueco muerto debajo al colapsar en mobile. */
6939
+ min-height: 0;
6940
+ }
6941
+
6942
+ /* Mobile: center-align el header (merchant tile vs bloque monto+código) tanto
6943
+ expandido como colapsado. Al quitar el logo del header, el bloque de texto quedaba
6944
+ más bajo que el merchant tile y con `flex-start` dejaba hueco abajo; centrado los
6945
+ alinea. CSS-only (no depende de la clase .is-collapsed, que el JS de collapse no setea). */
6946
+ @media (max-width: 767px) {
6947
+ header.kds-invoice-header,
6948
+ div.kds-invoice-header,
6949
+ .kds-invoice-header {
6950
+ align-items: center;
6951
+ }
5649
6952
  }
5650
6953
 
5651
6954
  /* Reset BeerCSS sibling margin on <p> inside invoice header — specificity (0,4,1) ties BeerCSS, wins by source order */
@@ -5655,7 +6958,7 @@ div.kds-invoice-header,
5655
6958
  }
5656
6959
 
5657
6960
  .kds-invoice-amount {
5658
- font-weight: var(--kds-font-weight-bold);
6961
+ font-weight: var(--kds-font-weight-semibold);
5659
6962
  font-size: var(--kds-font-size-3xl);
5660
6963
  line-height: var(--kds-line-height-tight);
5661
6964
  letter-spacing: var(--kds-letter-spacing-wide);
@@ -5677,12 +6980,16 @@ div.kds-invoice-header,
5677
6980
  text-transform: uppercase;
5678
6981
  margin: 0;
5679
6982
  margin-block: unset;
5680
- margin: var(--kds-spacing-1) 0;
6983
+ display: flex;
6984
+ gap: var(--kds-spacing-1-5);
5681
6985
  }
5682
6986
 
5683
6987
  .kds-invoice-code-value {
6988
+ color: var(--kds-color-gray-400);
6989
+ }
6990
+
6991
+ .kds-invoice-code-value--lowercase {
5684
6992
  text-transform: lowercase;
5685
- color: var(--kds-text-primary);
5686
6993
  }
5687
6994
 
5688
6995
  .kds-invoice-merchant {
@@ -5694,6 +7001,7 @@ div.kds-invoice-header,
5694
7001
  place-items: center;
5695
7002
  color: #fff;
5696
7003
  flex-shrink: 0;
7004
+ overflow: hidden;
5697
7005
  }
5698
7006
 
5699
7007
  .kds-invoice-merchant i {
@@ -5706,6 +7014,12 @@ div.kds-invoice-header,
5706
7014
  color: var(--kds-color-text-primary);
5707
7015
  }
5708
7016
 
7017
+ /* Neutro oscuro para logos claros (detección de luminancia — KTUF-204). */
7018
+ .kds-invoice-merchant-neutral.dark {
7019
+ background: var(--kds-color-gray-800);
7020
+ border-color: var(--kds-color-gray-700);
7021
+ }
7022
+
5709
7023
  .kds-invoice-merchant.initials {
5710
7024
  font-weight: 700;
5711
7025
  font-size: 11px;
@@ -5713,27 +7027,42 @@ div.kds-invoice-header,
5713
7027
  }
5714
7028
 
5715
7029
  .kds-invoice-merchant img {
7030
+ /* `contain` sobre el fondo neutro: el logo se muestra completo y las
7031
+ transparencias no quedan sobre el color de marca (KTUF-204). */
5716
7032
  width: 100%;
5717
7033
  height: 100%;
5718
- object-fit: cover;
5719
- border-radius: var(--kds-radius-md);
7034
+ object-fit: contain;
7035
+ padding: var(--kds-spacing-0-5);
5720
7036
  }
5721
7037
 
5722
7038
  /* -- Card Title -- */
5723
7039
  .kds-card-title {
5724
- font-weight: 700;
7040
+ font-weight: var(--kds-font-weight-semibold);
5725
7041
  font-size: var(--kds-font-size-base);
5726
7042
  line-height: 1.5;
5727
7043
  letter-spacing: -0.31px;
5728
7044
  color: var(--kds-color-text-primary);
5729
- margin-bottom: var(--kds-spacing-2) !important;
7045
+ /* sin !important: no hay regla de margin-bottom para h1 en BeerCSS que competir, y así un
7046
+ consumidor puede anularlo (p.ej. FormTitle dentro de un Stack que ya da el espacio). */
7047
+ margin-bottom: var(--kds-spacing-2);
7048
+ }
7049
+
7050
+ /* Opt-in size modifiers (additive — do NOT change default 16/600) */
7051
+ .kds-card-title--lg {
7052
+ font-size: var(--kds-font-size-xl); /* 20px */
7053
+ font-weight: var(--kds-font-weight-semibold);
7054
+ }
7055
+
7056
+ .kds-card-title--xl {
7057
+ font-size: var(--kds-font-size-2xl); /* 24px */
7058
+ font-weight: var(--kds-font-weight-semibold);
5730
7059
  }
5731
7060
 
5732
7061
  /* -- Key-Value Grid -- */
5733
7062
  .kds-kv {
5734
7063
  display: grid;
5735
7064
  grid-template-columns: auto 1fr;
5736
- column-gap: var(--kds-spacing-1-5);
7065
+ column-gap: var(--kds-spacing-1);
5737
7066
  align-items: baseline;
5738
7067
  }
5739
7068
 
@@ -5750,7 +7079,7 @@ div.kds-invoice-header,
5750
7079
  margin: 0;
5751
7080
  font-size: var(--kds-font-size-sm);
5752
7081
  line-height: var(--kds-line-height-relaxed);
5753
- font-weight: var(--kds-font-weight-bold);
7082
+ font-weight: var(--kds-font-weight-semibold);
5754
7083
  letter-spacing: normal;
5755
7084
  color: var(--kds-color-gray-800);
5756
7085
  min-width: 0;
@@ -5759,6 +7088,31 @@ div.kds-invoice-header,
5759
7088
  text-overflow: ellipsis;
5760
7089
  }
5761
7090
 
7091
+ /* Dark: these primary-content texts use absolute gray-800/900 (near-black in
7092
+ both modes) — switch to the adaptive text token so they read on dark cards.
7093
+ Covers the invoice amount ($X.XXX), kv values (PAGO A / ASUNTO) and the
7094
+ other gray-800 body texts. */
7095
+ [data-theme="dark"] .kds-invoice-amount,
7096
+ [data-theme="dark"] .kds-kv dd,
7097
+ [data-theme="dark"] .kds-text-value,
7098
+ [data-theme="dark"] .kds-share-copy {
7099
+ color: var(--kds-color-text-primary);
7100
+ }
7101
+
7102
+ /* -- Summary panel --
7103
+ Recuadro de datos del pago en pantallas de resultado: contenido en columna
7104
+ (gap spacing-2, típicamente pares kv) dentro de un borde sutil. */
7105
+ .kds-summary-panel {
7106
+ display: flex;
7107
+ flex-direction: column;
7108
+ gap: var(--kds-spacing-2);
7109
+ width: 100%;
7110
+ padding: var(--kds-spacing-2);
7111
+ border: var(--kds-border-width-sm) solid var(--kds-color-border-default);
7112
+ border-radius: var(--kds-radius-card);
7113
+ text-align: left;
7114
+ }
7115
+
5762
7116
  /* -- Dividers -- */
5763
7117
  .kds-hr {
5764
7118
  height: 1px;
@@ -5778,16 +7132,28 @@ div.kds-invoice-header,
5778
7132
  .kds-detail-list {
5779
7133
  display: flex;
5780
7134
  flex-direction: column;
5781
- gap: var(--kds-spacing-1-5);
7135
+ /* spacing-2 entre grupos del detalle (separación estándar entre piezas de info
7136
+ distintas, ej. "Emisor cobro" vs "Descripción"). El padding-top da la misma
7137
+ separación spacing-2 arriba del primer grupo (Asunto↔Emisor cobro). */
7138
+ gap: var(--kds-spacing-2);
5782
7139
  margin: 0;
5783
- padding: 0 0 var(--kds-spacing-2);
7140
+ padding: var(--kds-spacing-2) 0 0;
5784
7141
  }
5785
7142
 
5786
- .kds-detail-group {
5787
- margin-bottom: var(--kds-spacing-1-5);
7143
+ @media (max-width: 767px) {
7144
+ .kds-detail-list {
7145
+ padding-bottom: var(--kds-spacing-2);
7146
+ }
5788
7147
  }
5789
7148
 
5790
- .kds-detail-group:last-child {
7149
+ .kds-detail-group {
7150
+ /* El espacio ENTRE grupos lo da el `gap` de .kds-detail-list (spacing-2).
7151
+ Dentro del grupo, label↔value llevan un gap chico (spacing-0-5) para un
7152
+ ritmo parejo: antes el dt iba pegado al dd (0px) y se veía desigual frente
7153
+ a los 16px entre los demás elementos del panel. */
7154
+ display: flex;
7155
+ flex-direction: column;
7156
+ gap: var(--kds-spacing-0-5);
5791
7157
  margin-bottom: 0;
5792
7158
  }
5793
7159
 
@@ -5796,6 +7162,7 @@ div.kds-invoice-header,
5796
7162
  letter-spacing: var(--kds-letter-spacing-wide);
5797
7163
  color: var(--kds-color-text-hint);
5798
7164
  text-transform: uppercase;
7165
+ margin: 0;
5799
7166
  }
5800
7167
 
5801
7168
  .kds-detail-group dd {
@@ -5805,6 +7172,19 @@ div.kds-invoice-header,
5805
7172
  color: var(--kds-color-text-primary);
5806
7173
  }
5807
7174
 
7175
+ /* Imagen adjunta del cobro dentro del panel de detalle (ej. comprobante/producto).
7176
+ Reemplaza los inline styles del antiguo modal MerchantInfo: ancho contenido, borde
7177
+ y radio del DS. Va como un grupo más de la .kds-detail-list (hereda su gap). */
7178
+ .kds-detail-image {
7179
+ display: block;
7180
+ max-width: 100%;
7181
+ width: 160px;
7182
+ height: auto;
7183
+ object-fit: contain;
7184
+ border-radius: var(--kds-radius-md);
7185
+ border: 1px solid var(--kds-color-border);
7186
+ }
7187
+
5808
7188
  /* -- Amount Summary Row -- */
5809
7189
  .kds-amount-row {
5810
7190
  display: flex;
@@ -5864,16 +7244,16 @@ div.kds-invoice-header,
5864
7244
  font-size: var(--kds-font-size-sm);
5865
7245
  }
5866
7246
 
5867
- .kds-recap-list .k {
7247
+ .kds-recap-list .kds-key {
5868
7248
  color: var(--kds-color-text-secondary);
5869
7249
  }
5870
7250
 
5871
- .kds-recap-list .v {
7251
+ .kds-recap-list .kds-value {
5872
7252
  color: var(--kds-color-text-primary);
5873
7253
  font-weight: 500;
5874
7254
  }
5875
7255
 
5876
- .kds-recap-list .v.placeholder {
7256
+ .kds-recap-list .kds-value.placeholder {
5877
7257
  color: var(--kds-color-text-hint);
5878
7258
  }
5879
7259
 
@@ -5900,6 +7280,72 @@ a.kds-btn-success:disabled {
5900
7280
  box-shadow: none;
5901
7281
  }
5902
7282
 
7283
+ /* -- Button Warning (amber CTA) -- */
7284
+ button.kds-btn-warning,
7285
+ a.kds-btn-warning {
7286
+ background: var(--kds-color-warning-main);
7287
+ color: var(--kds-color-warning-contrast);
7288
+ border: none;
7289
+ }
7290
+
7291
+ button.kds-btn-warning:hover:not(:disabled),
7292
+ a.kds-btn-warning:hover:not(:disabled) {
7293
+ background: var(--kds-color-warning-dark);
7294
+ }
7295
+
7296
+ button.kds-btn-warning:active:not(:disabled),
7297
+ button.kds-btn-warning:focus-visible:not(:disabled),
7298
+ a.kds-btn-warning:active:not(:disabled),
7299
+ a.kds-btn-warning:focus-visible:not(:disabled) {
7300
+ background: var(--kds-color-warning-dark);
7301
+ outline: 2px solid var(--kds-color-warning-main);
7302
+ outline-offset: 2px;
7303
+ }
7304
+
7305
+ button.kds-btn-warning:disabled,
7306
+ a.kds-btn-warning:disabled {
7307
+ background-color: var(--kds-color-action-disabled-bg);
7308
+ color: var(--kds-color-action-disabled);
7309
+ box-shadow: none;
7310
+ }
7311
+
7312
+ button.kds-btn-warning::after {
7313
+ display: none;
7314
+ }
7315
+
7316
+ /* -- Button Error (red / destructive CTA) -- */
7317
+ button.kds-btn-error,
7318
+ a.kds-btn-error {
7319
+ background: var(--kds-color-error-main);
7320
+ color: var(--kds-color-error-contrast);
7321
+ border: none;
7322
+ }
7323
+
7324
+ button.kds-btn-error:hover:not(:disabled),
7325
+ a.kds-btn-error:hover:not(:disabled) {
7326
+ background: var(--kds-color-error-dark);
7327
+ }
7328
+
7329
+ button.kds-btn-error:active:not(:disabled),
7330
+ button.kds-btn-error:focus-visible:not(:disabled),
7331
+ a.kds-btn-error:active:not(:disabled),
7332
+ a.kds-btn-error:focus-visible:not(:disabled) {
7333
+ background: var(--kds-color-error-dark);
7334
+ outline: 2px solid var(--kds-color-error-main);
7335
+ outline-offset: 2px;
7336
+ }
7337
+
7338
+ button.kds-btn-error:disabled,
7339
+ a.kds-btn-error:disabled {
7340
+ background-color: var(--kds-color-action-disabled-bg);
7341
+ color: var(--kds-color-action-disabled);
7342
+ box-shadow: none;
7343
+ }
7344
+
7345
+ button.kds-btn-error::after {
7346
+ display: none;
7347
+ }
7348
+
5903
7349
  /* -- Field Helper Text -- */
5904
7350
  .kds-field-helper {
5905
7351
  font-size: var(--kds-font-size-xs);
@@ -5942,6 +7388,8 @@ button.kds-btn-success::after {
5942
7388
  /* -- Alert inline (borderless, used inside cards) -- */
5943
7389
  .kds-alert-inline {
5944
7390
  border: none;
7391
+ padding: var(--kds-spacing-1) var(--kds-spacing-1-5);
7392
+ margin-bottom: var(--kds-spacing-1-5);
5945
7393
  }
5946
7394
 
5947
7395
  /* -- Card dimmed (behind overlays) -- */
@@ -6010,6 +7458,25 @@ button.kds-btn-success::after {
6010
7458
  fill: none;
6011
7459
  }
6012
7460
 
7461
+ /* BeerCSS fuerza fill:currentcolor en todo svg, pisando el atributo de
7462
+ presentación; un SVG que declara fill="none" (ilustraciones de trazo)
7463
+ debe conservarlo. */
7464
+ svg[fill='none'] {
7465
+ fill: none;
7466
+ }
7467
+
7468
+ /* -- Bottom sheet centered content --
7469
+ Contenido escueto centrado verticalmente con altura mínima estable, para que
7470
+ el sheet no encoja cuando la vista tiene pocos elementos (p.ej. drawer de
7471
+ autorización: título + ilustración + timer). */
7472
+ .kds-bottom-sheet-centered-content {
7473
+ display: flex;
7474
+ flex-direction: column;
7475
+ align-items: center;
7476
+ justify-content: center;
7477
+ min-height: var(--kds-bottom-sheet-content-min-height, 21rem);
7478
+ }
7479
+
6013
7480
  /* -- Bottom sheet description (subtle / smaller) -- */
6014
7481
  .kds-bottom-sheet-description-subtle {
6015
7482
  color: var(--kds-color-text-secondary);
@@ -6043,17 +7510,42 @@ button.kds-btn-success::after {
6043
7510
  }
6044
7511
 
6045
7512
  .kds-bill-description-picture {
6046
- width: 120px;
6047
- height: 120px;
7513
+ max-width: 100px;
6048
7514
  object-fit: contain;
6049
7515
  background: var(--kds-color-background-paper);
6050
- padding: var(--kds-spacing-1-25);
6051
- border-radius: var(--kds-radius-md);
6052
- margin-top: var(--kds-spacing-1);
7516
+ margin-top: var(--kds-spacing-1-25);
6053
7517
  display: block;
6054
7518
  box-sizing: content-box;
6055
7519
  }
6056
7520
 
7521
+ /* -- Bill Attachments -- */
7522
+ .kds-bill-attachments {
7523
+ display: flex;
7524
+ flex-direction: column;
7525
+ gap: var(--kds-spacing-0-75);
7526
+ }
7527
+
7528
+ .kds-bill-attachment {
7529
+ display: inline-flex;
7530
+ align-items: center;
7531
+ justify-content: flex-start;
7532
+ width: fit-content;
7533
+ color: var(--kds-color-info-main);
7534
+ font-size: var(--kds-font-size-sm);
7535
+ font-weight: var(--kds-font-weight-medium);
7536
+ text-decoration: none;
7537
+ }
7538
+
7539
+ .kds-bill-attachment:hover span {
7540
+ text-decoration: underline;
7541
+ }
7542
+
7543
+ .kds-bill-attachment i {
7544
+ font-size: var(--kds-font-size-lg);
7545
+ min-width: unset;
7546
+ justify-content: flex-start;
7547
+ }
7548
+
6057
7549
  /* -- Share Card (mustContinue) -- */
6058
7550
  .kds-share-card {
6059
7551
  margin-top: var(--kds-spacing-2);
@@ -6163,8 +7655,10 @@ button.kds-btn-success::after {
6163
7655
  Desktop (min-width: 768px) restores padding + full border-radius. */
6164
7656
  .kds-payment-flow .kds-card-elevated {
6165
7657
  margin-top: 0;
6166
- padding-top: 0;
6167
- padding-bottom: 0;
7658
+ /* Vertical > lateral: la card del flujo respira arriba/abajo (spacing-3)
7659
+ manteniendo el padding lateral del base (spacing-2 / spacing-3 mobile). */
7660
+ padding-top: var(--kds-spacing-3);
7661
+ padding-bottom: var(--kds-spacing-3);
6168
7662
  }
6169
7663
 
6170
7664
  /* Reset BeerCSS sibling margin on form/section inside payment cards.
@@ -6179,12 +7673,43 @@ button.kds-btn-success::after {
6179
7673
  padding-bottom: 0;
6180
7674
  }
6181
7675
 
6182
- /* Payment stage full-viewport centered wrapper */
7676
+ /* Body card: alto mínimo estable para que NO salte entre estados de distinta altura
7677
+ (loader/progress ↔ formulario). Solo la body card = hijo directo de `.kds-screen`
7678
+ (los headers van dentro de `.kds-invoice-sticky-wrap`, no la afecta).
7679
+ Flex-column para que el contenido quede arriba y el footer interno (versión / secure
7680
+ footer) pueda pinearse al fondo con `margin-top: auto` aunque el contenido sea corto. */
7681
+ .kds-screen > .kds-card-elevated {
7682
+ min-height: 36rem;
7683
+ display: flex;
7684
+ flex-direction: column;
7685
+ /* mismo radio que el header (invoice-sticky usa radius-card); el base card-elevated
7686
+ es radius-md (8px) y quedaba distinto al header en desktop. */
7687
+ border-radius: var(--kds-radius-card);
7688
+ }
7689
+
7690
+ /* Variante sin radio superior: pantallas de resultado (éxito/fallo/timeout) que van
7691
+ SIN header arriba — la body card va sola y su tope queda plano (flush) en vez de
7692
+ con esquinas redondeadas sueltas. El padding del card sigue manejando el inset. */
7693
+ /* Tope plano SOLO en mobile (< 768px): la body card se pega al borde superior del widget
7694
+ (igual que el header sticky, que también aplana el tope solo en mobile). En desktop la card
7695
+ va centrada y separada, así que conserva el border-radius superior redondeado. */
7696
+ @media (max-width: 767px) {
7697
+ .kds-screen > .kds-card-elevated.kds-card-elevated--flush-top,
7698
+ .kds-card-elevated.kds-card-elevated--flush-top {
7699
+ border-top-left-radius: 0;
7700
+ border-top-right-radius: 0;
7701
+ }
7702
+ }
7703
+
7704
+ /* Payment stage — full-viewport centered wrapper.
7705
+ Fondo muted (gris) para que las cards blancas (header + body) y el gap entre
7706
+ ellas se distingan; sin esto, card blanca sobre fondo blanco se funden. */
6183
7707
  .kds-payment-stage {
6184
7708
  min-height: 100vh;
6185
7709
  display: flex;
6186
7710
  justify-content: center;
6187
7711
  padding: var(--kds-spacing-2-5) var(--kds-spacing-2) var(--kds-spacing-8);
7712
+ background: var(--kds-color-background-muted);
6188
7713
  }
6189
7714
 
6190
7715
  /* Payment flow — narrow column */
@@ -6218,23 +7743,23 @@ button.kds-btn-success::after {
6218
7743
  width: 100%;
6219
7744
  }
6220
7745
 
6221
- .kds-payment-flow .kds-card-elevated {
6222
- padding-top: var(--kds-spacing-2);
6223
- padding-bottom: var(--kds-spacing-2);
6224
- }
6225
-
6226
- /* Invoice sticky card: progressive padding-top (expanded 20px → collapsed 8px).
6227
- Overrides base rule at same specificity; source order wins here. */
7746
+ /* Invoice sticky card padding (mobile).
7747
+ Top: estático 12px. Bottom: progresivo 16px (expandido, = desktop) → 8px (colapsado).
7748
+ El padding-bottom animado se reduce al colapsar — sin depender de que el clip-path
7749
+ corte el padding abruptamente. */
6228
7750
  .kds-payment-flow .kds-card-elevated.kds-invoice-sticky {
6229
7751
  padding-top: var(--kds-spacing-1-5);
7752
+ padding-bottom: calc(
7753
+ var(--kds-spacing-2)
7754
+ - var(--kds-spacing-1) * var(--collapse-progress, 0)
7755
+ );
6230
7756
  }
6231
7757
 
6232
7758
 
6233
7759
  }
6234
7760
 
6235
- /* Desktop: restore card padding and full border-radius on invoice sticky */
7761
+ /* Desktop: restore sticky header padding and full border-radius on invoice sticky */
6236
7762
  @media (min-width: 768px) {
6237
- .kds-payment-flow .kds-card-elevated,
6238
7763
  .kds-payment-flow .kds-card-elevated.kds-invoice-sticky {
6239
7764
  padding-top: var(--kds-spacing-2);
6240
7765
  padding-bottom: var(--kds-spacing-2);
@@ -6338,6 +7863,28 @@ button.kds-btn-success::after {
6338
7863
  background: rgba(255, 255, 255, .08);
6339
7864
  }
6340
7865
 
7866
+ /* Icon-only arrows: kill the BeerCSS button active/focus ring (stray halo). */
7867
+ .kds-proto-nav-arrow:active,
7868
+ .kds-proto-nav-arrow:focus,
7869
+ .kds-proto-nav-arrow:focus-visible {
7870
+ outline: none;
7871
+ border: 0;
7872
+ box-shadow: none;
7873
+ }
7874
+
7875
+ /* Dark: the pill inverts to a LIGHT surface (bg = text-primary = white), so
7876
+ its contents need dark ink — the white-alpha tokens above become invisible. */
7877
+ [data-theme="dark"] .kds-proto-nav-label {
7878
+ color: var(--kds-color-gray-500);
7879
+ }
7880
+ [data-theme="dark"] .kds-proto-nav-arrow {
7881
+ color: var(--kds-color-gray-600);
7882
+ }
7883
+ [data-theme="dark"] .kds-proto-nav-arrow:hover {
7884
+ color: var(--kds-color-gray-900);
7885
+ background: rgba(0, 0, 0, .08);
7886
+ }
7887
+
6341
7888
  /* Tweaks panel — floating config panel */
6342
7889
  .kds-proto-tweaks {
6343
7890
  position: fixed;
@@ -6538,11 +8085,21 @@ button.kds-btn-success::after {
6538
8085
  display: none;
6539
8086
  }
6540
8087
 
6541
- .kds-bank-modal-search {
6542
- padding: 0 var(--kds-spacing-2) var(--kds-spacing-1-5);
8088
+ /* Icon-only close: kill the BeerCSS button active/focus ring (stray border). */
8089
+ .kds-bank-modal-close:active,
8090
+ .kds-bank-modal-close:focus,
8091
+ .kds-bank-modal-close:focus-visible {
8092
+ outline: none;
8093
+ border: 0;
8094
+ box-shadow: none;
8095
+ }
8096
+
8097
+ .kds-bank-modal-close:hover {
8098
+ background: var(--kds-color-action-hover);
6543
8099
  }
6544
8100
 
6545
- .kds-bank-modal-search input {
8101
+ /* Search field reutilizable (standalone o dentro del KdsBankModal). */
8102
+ .kds-search-field {
6546
8103
  width: 100%;
6547
8104
  padding: var(--kds-spacing-1-25) var(--kds-spacing-1-5);
6548
8105
  border: 1px solid var(--kds-color-divider);
@@ -6554,15 +8111,57 @@ button.kds-btn-success::after {
6554
8111
  background: var(--kds-color-surface);
6555
8112
  }
6556
8113
 
6557
- .kds-bank-modal-search input::placeholder {
8114
+ .kds-search-field::placeholder {
6558
8115
  color: var(--kds-color-text-hint);
6559
8116
  }
6560
8117
 
6561
- .kds-bank-modal-search input:focus {
8118
+ .kds-search-field:focus {
6562
8119
  border-color: var(--kds-color-primary-main);
6563
8120
  box-shadow: 0 0 0 2px var(--kds-color-primary-faint);
6564
8121
  }
6565
8122
 
8123
+ /* Variante con ícono (withIcon): lupa posicionada DENTRO del input, a la derecha. */
8124
+ .kds-search-field-wrapper {
8125
+ position: relative;
8126
+ width: 100%;
8127
+ }
8128
+
8129
+ .kds-search-field-icon {
8130
+ position: absolute;
8131
+ right: var(--kds-spacing-1-5);
8132
+ top: 50%;
8133
+ transform: translateY(-50%);
8134
+ font-size: 20px;
8135
+ color: var(--kds-color-text-hint);
8136
+ pointer-events: none;
8137
+ }
8138
+
8139
+ /* Espacio a la derecha del texto para no solaparse con la lupa. */
8140
+ .kds-search-field-wrapper .kds-search-field {
8141
+ padding-right: calc(var(--kds-spacing-1-5) + var(--kds-spacing-3));
8142
+ }
8143
+
8144
+ /* Variante angosta del field (prop `narrow` de KdsTextField): ancho reducido y texto
8145
+ centrado, para inputs de pocos caracteres dispuestos en fila (ej. segmentos de
8146
+ coordenadas o código). No crece ni se encoge dentro de un flex row. */
8147
+ .field.kds-field--narrow {
8148
+ width: 6rem;
8149
+ flex: 0 0 auto;
8150
+ /* En fila horizontal no aplica el margin vertical que BeerCSS da a fields
8151
+ hermanos (regla de "flow" `... + :is(.field,...)`, specificity ~0,3,1);
8152
+ sin el reset los siguientes quedan desalineados hacia abajo. `!important`
8153
+ para ganarle a esa regla de framework. */
8154
+ margin-block: 0 !important;
8155
+ }
8156
+
8157
+ .field.kds-field--narrow > input {
8158
+ text-align: center;
8159
+ }
8160
+
8161
+ .kds-bank-modal-search {
8162
+ padding: 0 var(--kds-spacing-2) var(--kds-spacing-1-5);
8163
+ }
8164
+
6566
8165
  .kds-bank-modal-body {
6567
8166
  flex: 1;
6568
8167
  overflow-y: auto;
@@ -6660,7 +8259,7 @@ button.kds-btn-success::after {
6660
8259
  min-width: 0;
6661
8260
  }
6662
8261
 
6663
- .kds-qr-text .title {
8262
+ .kds-qr-text .kds-qr-title {
6664
8263
  font-family: var(--kds-font-family-primary);
6665
8264
  font-weight: 600;
6666
8265
  font-size: var(--kds-font-size-sm);
@@ -6668,10 +8267,15 @@ button.kds-btn-success::after {
6668
8267
  color: var(--kds-color-text-primary);
6669
8268
  }
6670
8269
 
6671
- .kds-qr-text .sub {
8270
+ .kds-qr-text .kds-qr-subtitle {
6672
8271
  font-family: var(--kds-font-family-primary);
6673
8272
  font-size: var(--kds-font-size-caption);
6674
8273
  color: var(--kds-color-text-secondary);
8274
+ /* Trunca con ellipsis en lugar de wrap a 2 líneas — evita apretar el row
8275
+ cuando el contenedor es angosto (mobile o decorator constrained). */
8276
+ overflow: hidden;
8277
+ text-overflow: ellipsis;
8278
+ white-space: nowrap;
6675
8279
  }
6676
8280
 
6677
8281
  /* QR Badge - "Rápido" indicator */
@@ -6694,46 +8298,26 @@ button.kds-btn-success::after {
6694
8298
  font-size: 20px;
6695
8299
  }
6696
8300
 
6697
- /* Mobile responsive: Ajustes para pantallas pequeñas */
8301
+ /* Mobile responsive (≤480px): respira más mantenemos padding/gap/avatar igual
8302
+ que desktop y solo agregamos truncation al subtitle y `flex-shrink: 0` en los
8303
+ ítems que deben mantener su tamaño (avatar, badge, chevron). */
6698
8304
  @media (max-width: 480px) {
6699
- .kds-qr-row {
6700
- gap: var(--kds-spacing-1-5);
6701
- padding: var(--kds-spacing-1-5);
6702
- }
6703
-
6704
8305
  .kds-qr-avatar {
6705
- width: var(--kds-spacing-4-5);
6706
- height: var(--kds-spacing-4-5);
6707
8306
  flex-shrink: 0;
6708
8307
  }
6709
8308
 
6710
- .kds-qr-avatar .material-symbols-outlined,
6711
- .kds-qr-avatar .material-icons-round {
6712
- font-size: var(--kds-font-size-lg);
6713
- }
6714
-
6715
- .kds-qr-text .title {
6716
- font-size: var(--kds-font-size-xs);
6717
- line-height: var(--kds-line-height-tight);
6718
- }
6719
-
6720
8309
  .kds-qr-text .sub {
6721
- font-size: var(--kds-font-size-caption);
6722
- line-height: var(--kds-line-height-tight);
6723
8310
  overflow: hidden;
6724
8311
  text-overflow: ellipsis;
6725
8312
  white-space: nowrap;
6726
8313
  }
6727
8314
 
6728
8315
  .kds-qr-badge {
6729
- font-size: var(--kds-font-size-caption);
6730
- padding: var(--kds-spacing-0-25) var(--kds-spacing-0-75);
6731
8316
  flex-shrink: 0;
6732
8317
  }
6733
8318
 
6734
8319
  .kds-qr-row .material-symbols-outlined:last-child,
6735
8320
  .kds-qr-row .material-icons-round:last-child {
6736
- font-size: var(--kds-font-size-base);
6737
8321
  flex-shrink: 0;
6738
8322
  }
6739
8323
  }
@@ -6762,7 +8346,11 @@ button.kds-btn-success::after {
6762
8346
  border-radius: var(--kds-spacing-0-75);
6763
8347
  font-size: var(--kds-font-size-sm);
6764
8348
  cursor: pointer;
6765
- transition: background 0.12s ease;
8349
+ /* Transición más amplia y suave: bg + color + border ease-out */
8350
+ transition:
8351
+ background-color 280ms cubic-bezier(0.2, 0.8, 0.2, 1),
8352
+ color 280ms cubic-bezier(0.2, 0.8, 0.2, 1),
8353
+ border-top-color 280ms ease-out;
6766
8354
  position: relative;
6767
8355
  user-select: none;
6768
8356
  }
@@ -6771,19 +8359,21 @@ button.kds-btn-success::after {
6771
8359
  border-top: 0;
6772
8360
  }
6773
8361
 
6774
- .kds-copyable-table-row:hover {
8362
+ /* Hover NO aplica mientras la row esté en `.copied` o `.settling`
8363
+ (evita el flicker del hover state al desaparecer el verde de "copiado") */
8364
+ .kds-copyable-table-row:not(.copied):not(.settling):hover {
6775
8365
  background: var(--kds-color-primary-faint);
6776
8366
  }
6777
8367
 
6778
- .kds-copyable-table-row:hover + .kds-copyable-table-row {
8368
+ .kds-copyable-table-row:not(.copied):not(.settling):hover + .kds-copyable-table-row {
6779
8369
  border-top-color: transparent;
6780
8370
  }
6781
8371
 
6782
- .kds-copyable-table-row:active {
8372
+ .kds-copyable-table-row:not(.copied):not(.settling):active {
6783
8373
  background: var(--kds-color-primary-selected);
6784
8374
  }
6785
8375
 
6786
- .kds-copyable-table-row .k {
8376
+ .kds-copyable-table-row .kds-key {
6787
8377
  color: var(--kds-color-text-secondary);
6788
8378
  font-size: var(--kds-font-size-caption);
6789
8379
  letter-spacing: 0.3px;
@@ -6792,7 +8382,7 @@ button.kds-btn-success::after {
6792
8382
  flex: 0 0 auto;
6793
8383
  }
6794
8384
 
6795
- .kds-copyable-table-row .v {
8385
+ .kds-copyable-table-row .kds-value {
6796
8386
  color: var(--kds-color-text-primary);
6797
8387
  font-family: 'JetBrains Mono', 'SF Mono', ui-monospace, Menlo, Consolas, monospace;
6798
8388
  font-weight: var(--kds-font-weight-medium);
@@ -6808,17 +8398,18 @@ button.kds-btn-success::after {
6808
8398
  flex: 1 1 auto;
6809
8399
  }
6810
8400
 
6811
- .kds-copyable-table-row .v::after {
8401
+ .kds-copyable-table-row .kds-value::after {
6812
8402
  content: 'content_copy';
6813
8403
  font-family: 'Material Symbols Outlined';
6814
8404
  font-size: var(--kds-font-size-sm);
6815
8405
  color: var(--kds-color-primary-main);
6816
8406
  opacity: 0;
6817
- transition: opacity 0.12s ease;
8407
+ /* Transición coordinada con la row */
8408
+ transition: opacity 280ms cubic-bezier(0.2, 0.8, 0.2, 1);
6818
8409
  flex: 0 0 auto;
6819
8410
  }
6820
8411
 
6821
- .kds-copyable-table-row:hover .v::after {
8412
+ .kds-copyable-table-row:not(.copied):not(.settling):hover .kds-value::after {
6822
8413
  opacity: 0.7;
6823
8414
  }
6824
8415
 
@@ -6826,22 +8417,83 @@ button.kds-btn-success::after {
6826
8417
  background: var(--kds-color-success-soft);
6827
8418
  }
6828
8419
 
6829
- .kds-copyable-table-row.copied .v {
8420
+ .kds-copyable-table-row.copied .kds-value {
6830
8421
  color: var(--kds-color-success-dark);
6831
8422
  }
6832
8423
 
6833
- .kds-copyable-table-row.copied .v::after {
8424
+ .kds-copyable-table-row.copied .kds-value::after {
6834
8425
  content: 'check';
6835
8426
  font-family: 'Material Symbols Outlined';
6836
8427
  color: var(--kds-color-success-dark);
6837
8428
  opacity: 1;
6838
8429
  }
6839
8430
 
8431
+ /* Dark: success-dark text is near-invisible on the dark translucent success
8432
+ surface — use the alert success text token (light green in dark). */
8433
+ [data-theme="dark"] .kds-copyable-table-row.copied .kds-value,
8434
+ [data-theme="dark"] .kds-copyable-table-row.copied .kds-value::after {
8435
+ color: var(--kds-alert-success-text);
8436
+ }
8437
+
8438
+ /* `.settling`: estado de "recién copiado" — la row vuelve al normal sin
8439
+ activar hover. Dura lo mismo que la transición (~300ms) y se aplica
8440
+ desde React tras quitar `.copied`. */
8441
+ .kds-copyable-table-row.settling .kds-value::after {
8442
+ /* Mantén el check con opacity 0 mientras settling, fade out suave */
8443
+ opacity: 0;
8444
+ }
8445
+
6840
8446
  /* Disable BeerCSS ripple on copyable table rows */
6841
8447
  .kds-copyable-table-row::after {
6842
8448
  display: none;
6843
8449
  }
6844
8450
 
8451
+ /* ========================================
8452
+ GRID VARIANT (.kds-copyable-table--grid)
8453
+ Read-only grid of N text cells per row. No copy,
8454
+ no role=button, no "copy all". Cells share the row
8455
+ width equally. Used inside radio options, etc.
8456
+ ======================================== */
8457
+
8458
+ /* La grilla es solo columnas de texto (como el DataTable original): sin caja,
8459
+ borde, fondo ni sombra del contenedor copyable. */
8460
+ .kds-copyable-table--grid {
8461
+ border: 0;
8462
+ border-radius: 0;
8463
+ background: none;
8464
+ padding: 0;
8465
+ box-shadow: none;
8466
+ }
8467
+
8468
+ .kds-copyable-table-row--grid {
8469
+ display: flex;
8470
+ align-items: center;
8471
+ gap: var(--kds-spacing-1-5);
8472
+ padding: 0;
8473
+ /* Sin el divisor border-top que trae la fila base del copyable. */
8474
+ border-top: 0;
8475
+ cursor: default;
8476
+ user-select: text;
8477
+ }
8478
+
8479
+ /* Separación entre filas de la grilla (sin divisores). */
8480
+ .kds-copyable-table-row--grid + .kds-copyable-table-row--grid {
8481
+ margin-top: var(--kds-spacing-0-5);
8482
+ }
8483
+
8484
+ .kds-grid-cell {
8485
+ flex: 1 1 0;
8486
+ min-width: 0;
8487
+ font-size: var(--kds-font-size-sm);
8488
+ font-weight: var(--kds-font-weight-medium);
8489
+ color: var(--kds-color-text-primary);
8490
+ word-break: break-word;
8491
+ }
8492
+
8493
+ .kds-grid-cell--disabled {
8494
+ color: var(--kds-color-text-disabled);
8495
+ }
8496
+
6845
8497
  /* ========================================
6846
8498
  COPY-ALL BUTTON (.kds-copy-all-btn)
6847
8499
  Modifier for kds-btn kds-btn-outlined that adds
@@ -6873,6 +8525,15 @@ a.kds-copy-all-btn.copied:hover {
6873
8525
  border-color: var(--kds-color-success-border-light);
6874
8526
  }
6875
8527
 
8528
+ /* Dark: lighten the "Datos copiados" text/border on the dark success surface. */
8529
+ [data-theme="dark"] button.kds-copy-all-btn.copied,
8530
+ [data-theme="dark"] button.kds-copy-all-btn.copied:hover,
8531
+ [data-theme="dark"] a.kds-copy-all-btn.copied,
8532
+ [data-theme="dark"] a.kds-copy-all-btn.copied:hover {
8533
+ color: var(--kds-alert-success-text);
8534
+ border-color: var(--kds-alert-success-border);
8535
+ }
8536
+
6876
8537
  /* ========================================
6877
8538
  SECTION NOTE (.kds-section-note)
6878
8539
  Lightweight inline note for contextual caveats.
@@ -6907,6 +8568,17 @@ a.kds-copy-all-btn.copied:hover {
6907
8568
  .kds-section-note.kds-error { color: var(--kds-color-error-dark); }
6908
8569
  .kds-section-note.kds-error > i { color: var(--kds-color-error-dark); }
6909
8570
 
8571
+ /* Dark: the semantic *-dark shades are near-invisible on the dark page bg —
8572
+ use the alert *-text tokens (light shades in dark). Base note is warning. */
8573
+ [data-theme="dark"] .kds-section-note,
8574
+ [data-theme="dark"] .kds-section-note > i { color: var(--kds-alert-warning-text); }
8575
+ [data-theme="dark"] .kds-section-note.kds-info,
8576
+ [data-theme="dark"] .kds-section-note.kds-info > i { color: var(--kds-alert-info-text); }
8577
+ [data-theme="dark"] .kds-section-note.kds-success,
8578
+ [data-theme="dark"] .kds-section-note.kds-success > i { color: var(--kds-alert-success-text); }
8579
+ [data-theme="dark"] .kds-section-note.kds-error,
8580
+ [data-theme="dark"] .kds-section-note.kds-error > i { color: var(--kds-alert-error-text); }
8581
+
6910
8582
  /* ========================================
6911
8583
  INFO TOOLTIP (.kds-info-tip)
6912
8584
  Inline info button with hover/click tooltip bubble.
@@ -7002,7 +8674,7 @@ a.kds-copy-all-btn.copied:hover {
7002
8674
  .kds-monto-row {
7003
8675
  display: flex;
7004
8676
  justify-content: space-between;
7005
- align-items: flex-end;
8677
+ align-items: flex-start;
7006
8678
  padding: var(--kds-spacing-1-75) 0 var(--kds-spacing-1);
7007
8679
  border-top: 1px dashed var(--kds-color-divider);
7008
8680
  margin-top: var(--kds-spacing-1-75);
@@ -7026,3 +8698,332 @@ a.kds-copy-all-btn.copied:hover {
7026
8698
  color: var(--kds-color-text-primary);
7027
8699
  }
7028
8700
 
8701
+ /* ========================================
8702
+ PAYMENT TOTAL (.kds-payment-total)
8703
+ Bloque "monto a pagar" para QR view y email templates.
8704
+ Portado desde paylink-ligopay → <kh:paymentTotal> + materialize-config.css.
8705
+ Variantes:
8706
+ - default: amount grande (3rem) en color primario Khipu.
8707
+ - email: amount compacto (1.5rem) en color texto primario.
8708
+ Responsive (default): mobile (≤ 1024px) centra y oculta titleMobile.
8709
+ ======================================== */
8710
+
8711
+ .kds-payment-total {
8712
+ /* Component-scoped tokens — overrideables sin tocar el CSS global. */
8713
+ --kds-payment-total-title-size: var(--kds-font-size-2xl); /* 24px */
8714
+ --kds-payment-total-label-size: var(--kds-font-size-xl); /* 20px */
8715
+ --kds-payment-amount-size: 3rem; /* 48px — destacado */
8716
+ --kds-payment-amount-line-height: var(--kds-line-height-snug);
8717
+ --kds-payment-amount-color: var(--kds-color-primary-main);
8718
+ --kds-payment-amount-weight: var(--kds-font-weight-medium);
8719
+
8720
+ text-align: left;
8721
+ }
8722
+
8723
+ .kds-payment-total-title,
8724
+ .kds-payment-total-title-mobile {
8725
+ font-size: var(--kds-payment-total-title-size);
8726
+ font-weight: var(--kds-font-weight-semibold);
8727
+ color: var(--kds-color-text-primary);
8728
+ margin-bottom: var(--kds-spacing-4);
8729
+ }
8730
+
8731
+ .kds-payment-total-title-mobile {
8732
+ display: none;
8733
+ }
8734
+
8735
+ .kds-payment-total .kds-payment-label {
8736
+ font-size: var(--kds-payment-total-label-size);
8737
+ font-weight: var(--kds-font-weight-semibold);
8738
+ color: var(--kds-color-text-primary);
8739
+ margin-bottom: var(--kds-spacing-1);
8740
+ padding-top: 0;
8741
+ }
8742
+
8743
+ .kds-payment-total .kds-payment-amount {
8744
+ font-size: var(--kds-payment-amount-size);
8745
+ font-weight: var(--kds-payment-amount-weight);
8746
+ line-height: var(--kds-payment-amount-line-height);
8747
+ color: var(--kds-payment-amount-color);
8748
+ margin-bottom: var(--kds-spacing-4);
8749
+ }
8750
+
8751
+ .kds-payment-total .kds-payment-amount .kds-payment-total-decimal-sup,
8752
+ .kds-invoice-amount .kds-payment-total-decimal-sup {
8753
+ font-size: var(--kds-font-size-decimal-sup);
8754
+ font-weight: var(--kds-font-weight-semibold);
8755
+ position: relative;
8756
+ top: 0;
8757
+ }
8758
+
8759
+ /* Variante email: amount compacto en color texto primario, alineación izquierda fija */
8760
+ .kds-payment-total--email {
8761
+ --kds-payment-amount-size: var(--kds-font-size-2xl); /* 24px */
8762
+ --kds-payment-amount-line-height: var(--kds-line-height-snug);
8763
+ --kds-payment-amount-color: var(--kds-color-text-primary);
8764
+ --kds-payment-amount-weight: var(--kds-font-weight-semibold);
8765
+ }
8766
+
8767
+ /* Tone modifier: swaps the amount color to the khipu-blue / LigoPay info blue.
8768
+ Combinable with --email or with the default variant. */
8769
+ .kds-payment-total--tone-info {
8770
+ --kds-payment-amount-color: var(--kds-color-info-blue); /* #5A5FE0 */
8771
+ }
8772
+
8773
+ /* Centered modifier: removes the asymmetric desktop padding (which exists for
8774
+ LigoPay QR view) and centers all internal text. Composable with --email and
8775
+ --tone-info. */
8776
+ .kds-payment-total--centered {
8777
+ padding-left: 0;
8778
+ padding-top: 0;
8779
+ text-align: center;
8780
+ }
8781
+ .kds-payment-total--centered .kds-payment-total-title,
8782
+ .kds-payment-total--centered .kds-payment-total-title-mobile,
8783
+ .kds-payment-total--centered .kds-payment-label,
8784
+ .kds-payment-total--centered .kds-payment-amount {
8785
+ text-align: center;
8786
+ }
8787
+
8788
+ /* Responsive: mobile ≤ 1024px — centra y conmuta los títulos (sólo variante default) */
8789
+ @media (max-width: 1024px) {
8790
+ .kds-payment-total {
8791
+ text-align: center;
8792
+ }
8793
+
8794
+ .kds-payment-total .kds-payment-total-title {
8795
+ display: none;
8796
+ }
8797
+
8798
+ .kds-payment-total .kds-payment-total-title-mobile {
8799
+ display: block;
8800
+ text-align: center;
8801
+ }
8802
+
8803
+ .kds-payment-total .kds-payment-label,
8804
+ .kds-payment-total .kds-payment-amount {
8805
+ text-align: center;
8806
+ }
8807
+
8808
+ /* Email mantiene alineación izquierda incluso en mobile (matchea email clients) */
8809
+ .kds-payment-total--email,
8810
+ .kds-payment-total--email .kds-payment-label,
8811
+ .kds-payment-total--email .kds-payment-amount {
8812
+ text-align: left;
8813
+ }
8814
+ }
8815
+
8816
+ /* ==========================================================================
8817
+ GRID UTILITIES — semantic aliases for BeerCSS .grid / .s* / .m* / .l*
8818
+
8819
+ Estas utilidades son ALIAS de las clases cortas de BeerCSS para que
8820
+ consumers (payment, LigoPay, Grails legacy) puedan escribir markup con
8821
+ nombres descriptivos sin renunciar al grid de 12 columnas de BeerCSS.
8822
+
8823
+ Convención (estilo Bootstrap responsive):
8824
+ - kds-grid → .grid (12-col CSS grid + 1rem gap)
8825
+ - kds-col-sm-{1..12} → .s{1..12} (mobile, siempre aplica)
8826
+ - kds-col-md-{1..12} → .m{1..12} (>=601px)
8827
+ - kds-col-lg-{1..12} → .l{1..12} (>=993px)
8828
+
8829
+ Las definiciones originales de BeerCSS NO se tocan; estas son aditivas.
8830
+ ========================================================================== */
8831
+
8832
+ .kds-grid {
8833
+ --_gap: 1rem;
8834
+ display: grid;
8835
+ grid-template-columns: repeat(12, 1fr);
8836
+ gap: var(--_gap);
8837
+ block-size: auto;
8838
+ }
8839
+
8840
+ .kds-grid.kds-grid-no-space { --_gap: 0rem; }
8841
+ .kds-grid.kds-grid-medium-space { --_gap: 1.5rem; }
8842
+ .kds-grid.kds-grid-large-space { --_gap: 2rem; }
8843
+
8844
+ .kds-grid > * { margin: 0; }
8845
+
8846
+ .kds-col-sm-1 { grid-area: auto/span 1; }
8847
+ .kds-col-sm-2 { grid-area: auto/span 2; }
8848
+ .kds-col-sm-3 { grid-area: auto/span 3; }
8849
+ .kds-col-sm-4 { grid-area: auto/span 4; }
8850
+ .kds-col-sm-5 { grid-area: auto/span 5; }
8851
+ .kds-col-sm-6 { grid-area: auto/span 6; }
8852
+ .kds-col-sm-7 { grid-area: auto/span 7; }
8853
+ .kds-col-sm-8 { grid-area: auto/span 8; }
8854
+ .kds-col-sm-9 { grid-area: auto/span 9; }
8855
+ .kds-col-sm-10 { grid-area: auto/span 10; }
8856
+ .kds-col-sm-11 { grid-area: auto/span 11; }
8857
+ .kds-col-sm-12 { grid-area: auto/span 12; }
8858
+
8859
+ @media only screen and (min-width: 601px) {
8860
+ .kds-col-md-1 { grid-area: auto/span 1; }
8861
+ .kds-col-md-2 { grid-area: auto/span 2; }
8862
+ .kds-col-md-3 { grid-area: auto/span 3; }
8863
+ .kds-col-md-4 { grid-area: auto/span 4; }
8864
+ .kds-col-md-5 { grid-area: auto/span 5; }
8865
+ .kds-col-md-6 { grid-area: auto/span 6; }
8866
+ .kds-col-md-7 { grid-area: auto/span 7; }
8867
+ .kds-col-md-8 { grid-area: auto/span 8; }
8868
+ .kds-col-md-9 { grid-area: auto/span 9; }
8869
+ .kds-col-md-10 { grid-area: auto/span 10; }
8870
+ .kds-col-md-11 { grid-area: auto/span 11; }
8871
+ .kds-col-md-12 { grid-area: auto/span 12; }
8872
+ }
8873
+
8874
+ @media only screen and (min-width: 993px) {
8875
+ .kds-col-lg-1 { grid-area: auto/span 1; }
8876
+ .kds-col-lg-2 { grid-area: auto/span 2; }
8877
+ .kds-col-lg-3 { grid-area: auto/span 3; }
8878
+ .kds-col-lg-4 { grid-area: auto/span 4; }
8879
+ .kds-col-lg-5 { grid-area: auto/span 5; }
8880
+ .kds-col-lg-6 { grid-area: auto/span 6; }
8881
+ .kds-col-lg-7 { grid-area: auto/span 7; }
8882
+ .kds-col-lg-8 { grid-area: auto/span 8; }
8883
+ .kds-col-lg-9 { grid-area: auto/span 9; }
8884
+ .kds-col-lg-10 { grid-area: auto/span 10; }
8885
+ .kds-col-lg-11 { grid-area: auto/span 11; }
8886
+ .kds-col-lg-12 { grid-area: auto/span 12; }
8887
+ }
8888
+
8889
+ /* === Form icon chip ================================================== */
8890
+ /* Chip cuadrado con el icono del formulario (SecureForm, headers OIDC). */
8891
+
8892
+ .kds-form-icon-container {
8893
+ align-items: center;
8894
+ align-self: stretch;
8895
+ display: flex;
8896
+ flex-direction: column;
8897
+ gap: var(--kds-spacing-1);
8898
+ justify-content: center;
8899
+ padding: 0 var(--kds-spacing-2);
8900
+ }
8901
+
8902
+ .kds-form-icon-box {
8903
+ align-items: center;
8904
+ background: var(--kds-color-background-brand-subtle, #f0f0fa);
8905
+ border-radius: var(--kds-radius-md);
8906
+ display: flex;
8907
+ height: 52px;
8908
+ justify-content: center;
8909
+ padding: var(--kds-spacing-1);
8910
+ width: 52px;
8911
+ }
8912
+
8913
+ .kds-form-icon-box .kds-form-icon,
8914
+ .kds-form-icon-box > img,
8915
+ .kds-form-icon-box > svg {
8916
+ aspect-ratio: 1 / 1;
8917
+ flex-shrink: 0;
8918
+ height: 32px;
8919
+ width: 32px;
8920
+ }
8921
+
8922
+ @media (min-width: 600px) {
8923
+ .kds-form-icon-box {
8924
+ height: 56px;
8925
+ width: 56px;
8926
+ }
8927
+
8928
+ .kds-form-icon-box .kds-form-icon,
8929
+ .kds-form-icon-box > img,
8930
+ .kds-form-icon-box > svg {
8931
+ height: 30px;
8932
+ width: 30px;
8933
+ }
8934
+ }
8935
+
8936
+ @media (min-width: 900px) {
8937
+ .kds-form-icon-box {
8938
+ height: 60px;
8939
+ width: 60px;
8940
+ }
8941
+
8942
+ .kds-form-icon-box .kds-form-icon,
8943
+ .kds-form-icon-box > img,
8944
+ .kds-form-icon-box > svg {
8945
+ height: 32px;
8946
+ width: 32px;
8947
+ }
8948
+ }
8949
+
8950
+ /* === Accordion (componente KdsAccordion) ============================= */
8951
+ /* El bundle solo estilaba .kds-contract-accordion; estas son las clases
8952
+ que emite el componente React (details/summary con chevron propio). */
8953
+
8954
+ .kds-accordion {
8955
+ border: 1px solid var(--kds-accordion-border);
8956
+ border-radius: var(--kds-radius-md);
8957
+ overflow: hidden;
8958
+ }
8959
+
8960
+ .kds-accordion + .kds-accordion {
8961
+ margin-top: var(--kds-spacing-1);
8962
+ }
8963
+
8964
+ .kds-accordion-summary {
8965
+ align-items: center;
8966
+ background: var(--kds-accordion-header-bg);
8967
+ color: var(--kds-color-text-primary);
8968
+ cursor: pointer;
8969
+ display: flex;
8970
+ font-weight: var(--kds-font-weight-semibold);
8971
+ justify-content: space-between;
8972
+ list-style: none;
8973
+ padding: var(--kds-spacing-1) var(--kds-spacing-2);
8974
+ transition: background 0.2s ease;
8975
+ }
8976
+
8977
+ .kds-accordion-summary::-webkit-details-marker {
8978
+ display: none;
8979
+ }
8980
+
8981
+ .kds-accordion-summary:hover {
8982
+ background: var(--kds-color-background-elevated);
8983
+ }
8984
+
8985
+ .kds-accordion-summary > .material-symbols-outlined {
8986
+ transition: transform 0.3s ease;
8987
+ }
8988
+
8989
+ .kds-accordion[open] > .kds-accordion-summary > .material-symbols-outlined {
8990
+ transform: rotate(180deg);
8991
+ }
8992
+
8993
+ .kds-accordion[open] > .kds-accordion-summary {
8994
+ background: var(--kds-accordion-bg-open);
8995
+ border-bottom: 1px solid var(--kds-accordion-border);
8996
+ }
8997
+
8998
+ .kds-accordion-details {
8999
+ background: var(--kds-accordion-bg-open);
9000
+ color: var(--kds-color-text-secondary);
9001
+ font-size: var(--kds-font-size-sm);
9002
+ line-height: var(--kds-line-height-relaxed);
9003
+ padding: var(--kds-spacing-1) var(--kds-spacing-2);
9004
+ }
9005
+
9006
+ .kds-accordion-details ul,
9007
+ .kds-accordion-details ol {
9008
+ margin: 0;
9009
+ padding-inline-start: var(--kds-spacing-2);
9010
+ }
9011
+
9012
+ /* === Display utilities ================================================ */
9013
+
9014
+ .kds-block {
9015
+ display: block;
9016
+ }
9017
+
9018
+ .kds-inline {
9019
+ display: inline;
9020
+ }
9021
+
9022
+ .kds-inline-block {
9023
+ display: inline-block;
9024
+ }
9025
+
9026
+ .kds-pre-wrap {
9027
+ white-space: pre-wrap;
9028
+ }
9029
+