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

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:13:12.103Z
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;
@@ -4857,45 +5858,186 @@ dialog#surveyModal button.circle {
4857
5858
  color: var(--kds-color-primary-main);
4858
5859
  }
4859
5860
 
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;
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)));
5988
+ }
5989
+
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)));
5993
+ }
5994
+
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,27 @@ 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;
6383
+ }
6384
+
6385
+ /* En el borde compartido con el card el rhythm lo aporta el padding del propio card:
6386
+ como primer/último hijo directo el padding del bloque no debe sumarse al del padre. */
6387
+ .kds-card-elevated > .kds-status-block:first-child {
6388
+ padding-top: 0;
6389
+ }
6390
+
6391
+ .kds-card-elevated > .kds-status-block:last-child {
6392
+ padding-bottom: 0;
5190
6393
  }
5191
6394
 
5192
6395
  .kds-status-block-icon {
5193
6396
  width: var(--kds-status-icon-size);
5194
6397
  height: var(--kds-status-icon-size);
5195
- margin: 0 auto var(--kds-spacing-3);
6398
+ /* spacing-1 (8px) icono→título: elementos relacionados dentro del bloque. */
6399
+ margin: 0 auto var(--kds-spacing-1);
5196
6400
  border-radius: var(--kds-radius-full);
5197
6401
  display: grid;
5198
6402
  place-items: center;
@@ -5205,15 +6409,22 @@ dialog#surveyModal button.circle {
5205
6409
 
5206
6410
  .kds-status-block-title {
5207
6411
  font-size: var(--kds-font-size-xl);
5208
- font-weight: var(--kds-font-weight-bold);
6412
+ font-weight: var(--kds-font-weight-semibold);
5209
6413
  color: var(--kds-color-text-primary);
5210
6414
  margin: 0 0 var(--kds-spacing-1);
6415
+ /* El reset global :where(h2) usa estas vars con !important; así se overridea. */
6416
+ --kds-typography-h2-margin-block-end: var(--kds-spacing-1);
5211
6417
  }
5212
6418
 
5213
6419
  .kds-status-block-description {
5214
6420
  font-size: var(--kds-font-size-sm);
5215
- color: var(--kds-color-text-secondary);
6421
+ /* Mismo color que el título: título y subtítulo parejos en resultados. */
6422
+ color: var(--kds-color-text-primary);
5216
6423
  margin: 0;
6424
+ /* !important: anula el auto-espaciado de hermanos de BeerCSS
6425
+ (margin-block-start: 1rem, especificidad 0,5,1 imposible de igualar).
6426
+ La separación título→descripción la da el margin-bottom del título. */
6427
+ margin-top: 0 !important;
5217
6428
  }
5218
6429
 
5219
6430
  /* Variant: success */
@@ -5223,11 +6434,11 @@ dialog#surveyModal button.circle {
5223
6434
  color: var(--kds-color-success-main);
5224
6435
  }
5225
6436
 
5226
- /* Variant: pending (animated spinner) */
6437
+ /* Variant: pending (animated blue spinner, sin icono interno) */
5227
6438
  .kds-status-block[data-status="pending"] .kds-status-block-icon {
5228
6439
  background: transparent;
5229
6440
  border: 3px solid var(--kds-color-divider);
5230
- border-top-color: var(--kds-color-primary-main);
6441
+ border-top-color: var(--kds-color-info-main);
5231
6442
  animation: kds-spin 1s linear infinite;
5232
6443
  }
5233
6444
 
@@ -5245,7 +6456,9 @@ dialog#surveyModal button.circle {
5245
6456
  color: var(--kds-color-error-main);
5246
6457
  }
5247
6458
 
5248
- /* Variant: info */
6459
+ /* Variant: info — usar SIEMPRE con icon "info_i" (i minúscula sin glyph circular)
6460
+ para que solo se vea nuestro círculo decorativo. Iconos como "info" tienen
6461
+ su propio círculo built-in → causarían doble círculo. */
5249
6462
  .kds-status-block[data-status="info"] .kds-status-block-icon {
5250
6463
  background: var(--kds-color-info-soft);
5251
6464
  border: var(--kds-border-width-md) solid var(--kds-color-info-main);
@@ -5257,7 +6470,7 @@ dialog#surveyModal button.circle {
5257
6470
  text-align: left;
5258
6471
  display: flex;
5259
6472
  align-items: center;
5260
- gap: var(--kds-spacing-3);
6473
+ gap: var(--kds-spacing-2);
5261
6474
  padding: var(--kds-spacing-1) 0;
5262
6475
  }
5263
6476
 
@@ -5299,6 +6512,9 @@ dialog#surveyModal button.circle {
5299
6512
  .kds-bottom-sheet {
5300
6513
  width: 100%;
5301
6514
  max-width: var(--kds-bottom-sheet-max-width);
6515
+ max-height: 90dvh;
6516
+ display: flex;
6517
+ flex-direction: column;
5302
6518
  background: var(--kds-color-background-paper);
5303
6519
  border-radius: var(--kds-radius-2xl) var(--kds-radius-2xl) 0 0;
5304
6520
  padding: var(--kds-spacing-3-5) var(--kds-spacing-3) var(--kds-spacing-3);
@@ -5319,6 +6535,42 @@ dialog#surveyModal button.circle {
5319
6535
  background: var(--kds-color-divider);
5320
6536
  }
5321
6537
 
6538
+ /* Close button — top-right discreto, NO usa kds-btn (rompía el flex layout) */
6539
+ .kds-bottom-sheet-close {
6540
+ position: absolute;
6541
+ top: var(--kds-spacing-1);
6542
+ right: var(--kds-spacing-1);
6543
+ width: 32px;
6544
+ height: 32px;
6545
+ display: inline-flex;
6546
+ align-items: center;
6547
+ justify-content: center;
6548
+ padding: 0;
6549
+ background: transparent;
6550
+ border: 0;
6551
+ border-radius: 50%;
6552
+ color: var(--kds-color-text-secondary);
6553
+ cursor: pointer;
6554
+ transition: background-color 0.15s ease;
6555
+ }
6556
+
6557
+ .kds-bottom-sheet-close:hover {
6558
+ background: rgba(0, 0, 0, 0.06);
6559
+ }
6560
+
6561
+ .kds-bottom-sheet-close i {
6562
+ font-size: 20px;
6563
+ line-height: 1;
6564
+ }
6565
+
6566
+ /* Body container — separa el content del title/actions */
6567
+ .kds-bottom-sheet-body {
6568
+ text-align: left;
6569
+ margin-top: var(--kds-spacing-1);
6570
+ overflow-y: auto;
6571
+ min-height: 0;
6572
+ }
6573
+
5322
6574
  .kds-bottom-sheet-icon {
5323
6575
  font-size: var(--kds-font-size-4xl);
5324
6576
  }
@@ -5365,6 +6617,19 @@ dialog#surveyModal button.circle {
5365
6617
  to { opacity: 1; }
5366
6618
  }
5367
6619
 
6620
+ /* Botón con llenado progresivo (feedback visual de auto-cierre).
6621
+ Duración parametrizable con --kds-fill-duration (default 12s). */
6622
+ .kds-btn-fill-progress {
6623
+ background-image: linear-gradient(to right, var(--kds-color-primary-dark), var(--kds-color-primary-dark));
6624
+ background-repeat: no-repeat;
6625
+ background-size: 0% 100%;
6626
+ animation: kds-btn-fill var(--kds-fill-duration, 12s) linear forwards;
6627
+ }
6628
+
6629
+ @keyframes kds-btn-fill {
6630
+ to { background-size: 100% 100%; }
6631
+ }
6632
+
5368
6633
  @keyframes kds-rise {
5369
6634
  from { transform: translateY(var(--kds-spacing-2-5)); opacity: 0.4; }
5370
6635
  to { transform: none; opacity: 1; }
@@ -5552,6 +6817,30 @@ dialog#surveyModal button.circle {
5552
6817
  /* No max-height — clip-path on parent handles visual collapse without layout changes */
5553
6818
  .kds-invoice-sticky .kds-invoice-collapsible {
5554
6819
  opacity: calc(1 - var(--collapse-progress) * 1.5);
6820
+
6821
+ /* Scroll interno discreto (mobile): cuando el detalle abierto excede el viewport, el
6822
+ collapsible scrollea internamente (monto/merchant quedan fijos arriba) para poder leer
6823
+ todo el detalle. Al tocar el fondo, `overscroll-behavior: auto` ENCADENA el gesto al
6824
+ scroll del body → dispara useStickyInvoiceCollapse → colapsa el header y aparece el form
6825
+ (flujo natural). Se auto-desactiva con el detalle cerrado (contenido más corto que el
6826
+ max-height → sin scroll ni efecto visual). */
6827
+ max-height: calc(100vh - var(--kds-spacing-12) - var(--kds-spacing-4));
6828
+ max-height: calc(100dvh - var(--kds-spacing-12) - var(--kds-spacing-4));
6829
+ overflow-y: auto;
6830
+ overflow-x: hidden;
6831
+ overscroll-behavior-y: auto;
6832
+ scrollbar-width: thin;
6833
+ scrollbar-color: var(--kds-color-gray-300) transparent;
6834
+ }
6835
+ .kds-invoice-sticky .kds-invoice-collapsible::-webkit-scrollbar {
6836
+ width: 4px;
6837
+ }
6838
+ .kds-invoice-sticky .kds-invoice-collapsible::-webkit-scrollbar-thumb {
6839
+ background: var(--kds-color-gray-300);
6840
+ border-radius: var(--kds-radius-full);
6841
+ }
6842
+ .kds-invoice-sticky .kds-invoice-collapsible::-webkit-scrollbar-track {
6843
+ background: transparent;
5555
6844
  }
5556
6845
 
5557
6846
  /* Amount: 30px → 24px (progressive) */
@@ -5623,6 +6912,14 @@ dialog#surveyModal button.circle {
5623
6912
  margin-top: var(--kds-spacing-1);
5624
6913
  }
5625
6914
 
6915
+ /* Ritmo uniforme alrededor del toggle: spacing-2 arriba (resumen↔"Ver detalle")
6916
+ iguala el padding-top spacing-2 que la .kds-detail-list da debajo
6917
+ ("Ver detalle"↔primer label del panel). Antes el alto fijo del bare button
6918
+ (40px) hacía que el espacio de arriba y de abajo fueran distintos. */
6919
+ .kds-invoice-collapsible > .kds-expand-toggle {
6920
+ margin-top: var(--kds-spacing-2);
6921
+ }
6922
+
5626
6923
  /* -- Invoice Summary -- */
5627
6924
  .kds-invoice-summary {
5628
6925
  display: flex;
@@ -5646,6 +6943,22 @@ div.kds-invoice-header,
5646
6943
  padding: 0;
5647
6944
  border-radius: 0;
5648
6945
  align-content: initial;
6946
+ /* Override BeerCSS `header { min-height: ... }` — el header debe ajustarse a su
6947
+ contenido (sigue el alto del merchant tile: 64px→50px al colapsar). Sin esto
6948
+ quedaba un min-height fijo de 64px → hueco muerto debajo al colapsar en mobile. */
6949
+ min-height: 0;
6950
+ }
6951
+
6952
+ /* Mobile: center-align el header (merchant tile vs bloque monto+código) tanto
6953
+ expandido como colapsado. Al quitar el logo del header, el bloque de texto quedaba
6954
+ más bajo que el merchant tile y con `flex-start` dejaba hueco abajo; centrado los
6955
+ alinea. CSS-only (no depende de la clase .is-collapsed, que el JS de collapse no setea). */
6956
+ @media (max-width: 767px) {
6957
+ header.kds-invoice-header,
6958
+ div.kds-invoice-header,
6959
+ .kds-invoice-header {
6960
+ align-items: center;
6961
+ }
5649
6962
  }
5650
6963
 
5651
6964
  /* Reset BeerCSS sibling margin on <p> inside invoice header — specificity (0,4,1) ties BeerCSS, wins by source order */
@@ -5655,7 +6968,7 @@ div.kds-invoice-header,
5655
6968
  }
5656
6969
 
5657
6970
  .kds-invoice-amount {
5658
- font-weight: var(--kds-font-weight-bold);
6971
+ font-weight: var(--kds-font-weight-semibold);
5659
6972
  font-size: var(--kds-font-size-3xl);
5660
6973
  line-height: var(--kds-line-height-tight);
5661
6974
  letter-spacing: var(--kds-letter-spacing-wide);
@@ -5677,12 +6990,16 @@ div.kds-invoice-header,
5677
6990
  text-transform: uppercase;
5678
6991
  margin: 0;
5679
6992
  margin-block: unset;
5680
- margin: var(--kds-spacing-1) 0;
6993
+ display: flex;
6994
+ gap: var(--kds-spacing-1-5);
5681
6995
  }
5682
6996
 
5683
6997
  .kds-invoice-code-value {
6998
+ color: var(--kds-color-gray-400);
6999
+ }
7000
+
7001
+ .kds-invoice-code-value--lowercase {
5684
7002
  text-transform: lowercase;
5685
- color: var(--kds-text-primary);
5686
7003
  }
5687
7004
 
5688
7005
  .kds-invoice-merchant {
@@ -5694,6 +7011,7 @@ div.kds-invoice-header,
5694
7011
  place-items: center;
5695
7012
  color: #fff;
5696
7013
  flex-shrink: 0;
7014
+ overflow: hidden;
5697
7015
  }
5698
7016
 
5699
7017
  .kds-invoice-merchant i {
@@ -5706,6 +7024,12 @@ div.kds-invoice-header,
5706
7024
  color: var(--kds-color-text-primary);
5707
7025
  }
5708
7026
 
7027
+ /* Neutro oscuro para logos claros (detección de luminancia — KTUF-204). */
7028
+ .kds-invoice-merchant-neutral.dark {
7029
+ background: var(--kds-color-gray-800);
7030
+ border-color: var(--kds-color-gray-700);
7031
+ }
7032
+
5709
7033
  .kds-invoice-merchant.initials {
5710
7034
  font-weight: 700;
5711
7035
  font-size: 11px;
@@ -5713,27 +7037,42 @@ div.kds-invoice-header,
5713
7037
  }
5714
7038
 
5715
7039
  .kds-invoice-merchant img {
7040
+ /* `contain` sobre el fondo neutro: el logo se muestra completo y las
7041
+ transparencias no quedan sobre el color de marca (KTUF-204). */
5716
7042
  width: 100%;
5717
7043
  height: 100%;
5718
- object-fit: cover;
5719
- border-radius: var(--kds-radius-md);
7044
+ object-fit: contain;
7045
+ padding: var(--kds-spacing-0-5);
5720
7046
  }
5721
7047
 
5722
7048
  /* -- Card Title -- */
5723
7049
  .kds-card-title {
5724
- font-weight: 700;
7050
+ font-weight: var(--kds-font-weight-semibold);
5725
7051
  font-size: var(--kds-font-size-base);
5726
7052
  line-height: 1.5;
5727
7053
  letter-spacing: -0.31px;
5728
7054
  color: var(--kds-color-text-primary);
5729
- margin-bottom: var(--kds-spacing-2) !important;
7055
+ /* sin !important: no hay regla de margin-bottom para h1 en BeerCSS que competir, y así un
7056
+ consumidor puede anularlo (p.ej. FormTitle dentro de un Stack que ya da el espacio). */
7057
+ margin-bottom: var(--kds-spacing-2);
7058
+ }
7059
+
7060
+ /* Opt-in size modifiers (additive — do NOT change default 16/600) */
7061
+ .kds-card-title--lg {
7062
+ font-size: var(--kds-font-size-xl); /* 20px */
7063
+ font-weight: var(--kds-font-weight-semibold);
7064
+ }
7065
+
7066
+ .kds-card-title--xl {
7067
+ font-size: var(--kds-font-size-2xl); /* 24px */
7068
+ font-weight: var(--kds-font-weight-semibold);
5730
7069
  }
5731
7070
 
5732
7071
  /* -- Key-Value Grid -- */
5733
7072
  .kds-kv {
5734
7073
  display: grid;
5735
7074
  grid-template-columns: auto 1fr;
5736
- column-gap: var(--kds-spacing-1-5);
7075
+ column-gap: var(--kds-spacing-1);
5737
7076
  align-items: baseline;
5738
7077
  }
5739
7078
 
@@ -5750,7 +7089,7 @@ div.kds-invoice-header,
5750
7089
  margin: 0;
5751
7090
  font-size: var(--kds-font-size-sm);
5752
7091
  line-height: var(--kds-line-height-relaxed);
5753
- font-weight: var(--kds-font-weight-bold);
7092
+ font-weight: var(--kds-font-weight-semibold);
5754
7093
  letter-spacing: normal;
5755
7094
  color: var(--kds-color-gray-800);
5756
7095
  min-width: 0;
@@ -5759,6 +7098,31 @@ div.kds-invoice-header,
5759
7098
  text-overflow: ellipsis;
5760
7099
  }
5761
7100
 
7101
+ /* Dark: these primary-content texts use absolute gray-800/900 (near-black in
7102
+ both modes) — switch to the adaptive text token so they read on dark cards.
7103
+ Covers the invoice amount ($X.XXX), kv values (PAGO A / ASUNTO) and the
7104
+ other gray-800 body texts. */
7105
+ [data-theme="dark"] .kds-invoice-amount,
7106
+ [data-theme="dark"] .kds-kv dd,
7107
+ [data-theme="dark"] .kds-text-value,
7108
+ [data-theme="dark"] .kds-share-copy {
7109
+ color: var(--kds-color-text-primary);
7110
+ }
7111
+
7112
+ /* -- Summary panel --
7113
+ Recuadro de datos del pago en pantallas de resultado: contenido en columna
7114
+ (gap spacing-2, típicamente pares kv) dentro de un borde sutil. */
7115
+ .kds-summary-panel {
7116
+ display: flex;
7117
+ flex-direction: column;
7118
+ gap: var(--kds-spacing-2);
7119
+ width: 100%;
7120
+ padding: var(--kds-spacing-2);
7121
+ border: var(--kds-border-width-sm) solid var(--kds-color-border-default);
7122
+ border-radius: var(--kds-radius-card);
7123
+ text-align: left;
7124
+ }
7125
+
5762
7126
  /* -- Dividers -- */
5763
7127
  .kds-hr {
5764
7128
  height: 1px;
@@ -5778,16 +7142,28 @@ div.kds-invoice-header,
5778
7142
  .kds-detail-list {
5779
7143
  display: flex;
5780
7144
  flex-direction: column;
5781
- gap: var(--kds-spacing-1-5);
7145
+ /* spacing-2 entre grupos del detalle (separación estándar entre piezas de info
7146
+ distintas, ej. "Emisor cobro" vs "Descripción"). El padding-top da la misma
7147
+ separación spacing-2 arriba del primer grupo (Asunto↔Emisor cobro). */
7148
+ gap: var(--kds-spacing-2);
5782
7149
  margin: 0;
5783
- padding: 0 0 var(--kds-spacing-2);
7150
+ padding: var(--kds-spacing-2) 0 0;
5784
7151
  }
5785
7152
 
5786
- .kds-detail-group {
5787
- margin-bottom: var(--kds-spacing-1-5);
7153
+ @media (max-width: 767px) {
7154
+ .kds-detail-list {
7155
+ padding-bottom: var(--kds-spacing-2);
7156
+ }
5788
7157
  }
5789
7158
 
5790
- .kds-detail-group:last-child {
7159
+ .kds-detail-group {
7160
+ /* El espacio ENTRE grupos lo da el `gap` de .kds-detail-list (spacing-2).
7161
+ Dentro del grupo, label↔value llevan un gap chico (spacing-0-5) para un
7162
+ ritmo parejo: antes el dt iba pegado al dd (0px) y se veía desigual frente
7163
+ a los 16px entre los demás elementos del panel. */
7164
+ display: flex;
7165
+ flex-direction: column;
7166
+ gap: var(--kds-spacing-0-5);
5791
7167
  margin-bottom: 0;
5792
7168
  }
5793
7169
 
@@ -5796,6 +7172,7 @@ div.kds-invoice-header,
5796
7172
  letter-spacing: var(--kds-letter-spacing-wide);
5797
7173
  color: var(--kds-color-text-hint);
5798
7174
  text-transform: uppercase;
7175
+ margin: 0;
5799
7176
  }
5800
7177
 
5801
7178
  .kds-detail-group dd {
@@ -5805,6 +7182,19 @@ div.kds-invoice-header,
5805
7182
  color: var(--kds-color-text-primary);
5806
7183
  }
5807
7184
 
7185
+ /* Imagen adjunta del cobro dentro del panel de detalle (ej. comprobante/producto).
7186
+ Reemplaza los inline styles del antiguo modal MerchantInfo: ancho contenido, borde
7187
+ y radio del DS. Va como un grupo más de la .kds-detail-list (hereda su gap). */
7188
+ .kds-detail-image {
7189
+ display: block;
7190
+ max-width: 100%;
7191
+ width: 160px;
7192
+ height: auto;
7193
+ object-fit: contain;
7194
+ border-radius: var(--kds-radius-md);
7195
+ border: 1px solid var(--kds-color-border);
7196
+ }
7197
+
5808
7198
  /* -- Amount Summary Row -- */
5809
7199
  .kds-amount-row {
5810
7200
  display: flex;
@@ -5864,16 +7254,16 @@ div.kds-invoice-header,
5864
7254
  font-size: var(--kds-font-size-sm);
5865
7255
  }
5866
7256
 
5867
- .kds-recap-list .k {
7257
+ .kds-recap-list .kds-key {
5868
7258
  color: var(--kds-color-text-secondary);
5869
7259
  }
5870
7260
 
5871
- .kds-recap-list .v {
7261
+ .kds-recap-list .kds-value {
5872
7262
  color: var(--kds-color-text-primary);
5873
7263
  font-weight: 500;
5874
7264
  }
5875
7265
 
5876
- .kds-recap-list .v.placeholder {
7266
+ .kds-recap-list .kds-value.placeholder {
5877
7267
  color: var(--kds-color-text-hint);
5878
7268
  }
5879
7269
 
@@ -5900,6 +7290,72 @@ a.kds-btn-success:disabled {
5900
7290
  box-shadow: none;
5901
7291
  }
5902
7292
 
7293
+ /* -- Button Warning (amber CTA) -- */
7294
+ button.kds-btn-warning,
7295
+ a.kds-btn-warning {
7296
+ background: var(--kds-color-warning-main);
7297
+ color: var(--kds-color-warning-contrast);
7298
+ border: none;
7299
+ }
7300
+
7301
+ button.kds-btn-warning:hover:not(:disabled),
7302
+ a.kds-btn-warning:hover:not(:disabled) {
7303
+ background: var(--kds-color-warning-dark);
7304
+ }
7305
+
7306
+ button.kds-btn-warning:active:not(:disabled),
7307
+ button.kds-btn-warning:focus-visible:not(:disabled),
7308
+ a.kds-btn-warning:active:not(:disabled),
7309
+ a.kds-btn-warning:focus-visible:not(:disabled) {
7310
+ background: var(--kds-color-warning-dark);
7311
+ outline: 2px solid var(--kds-color-warning-main);
7312
+ outline-offset: 2px;
7313
+ }
7314
+
7315
+ button.kds-btn-warning:disabled,
7316
+ a.kds-btn-warning:disabled {
7317
+ background-color: var(--kds-color-action-disabled-bg);
7318
+ color: var(--kds-color-action-disabled);
7319
+ box-shadow: none;
7320
+ }
7321
+
7322
+ button.kds-btn-warning::after {
7323
+ display: none;
7324
+ }
7325
+
7326
+ /* -- Button Error (red / destructive CTA) -- */
7327
+ button.kds-btn-error,
7328
+ a.kds-btn-error {
7329
+ background: var(--kds-color-error-main);
7330
+ color: var(--kds-color-error-contrast);
7331
+ border: none;
7332
+ }
7333
+
7334
+ button.kds-btn-error:hover:not(:disabled),
7335
+ a.kds-btn-error:hover:not(:disabled) {
7336
+ background: var(--kds-color-error-dark);
7337
+ }
7338
+
7339
+ button.kds-btn-error:active:not(:disabled),
7340
+ button.kds-btn-error:focus-visible:not(:disabled),
7341
+ a.kds-btn-error:active:not(:disabled),
7342
+ a.kds-btn-error:focus-visible:not(:disabled) {
7343
+ background: var(--kds-color-error-dark);
7344
+ outline: 2px solid var(--kds-color-error-main);
7345
+ outline-offset: 2px;
7346
+ }
7347
+
7348
+ button.kds-btn-error:disabled,
7349
+ a.kds-btn-error:disabled {
7350
+ background-color: var(--kds-color-action-disabled-bg);
7351
+ color: var(--kds-color-action-disabled);
7352
+ box-shadow: none;
7353
+ }
7354
+
7355
+ button.kds-btn-error::after {
7356
+ display: none;
7357
+ }
7358
+
5903
7359
  /* -- Field Helper Text -- */
5904
7360
  .kds-field-helper {
5905
7361
  font-size: var(--kds-font-size-xs);
@@ -5942,6 +7398,8 @@ button.kds-btn-success::after {
5942
7398
  /* -- Alert inline (borderless, used inside cards) -- */
5943
7399
  .kds-alert-inline {
5944
7400
  border: none;
7401
+ padding: var(--kds-spacing-1) var(--kds-spacing-1-5);
7402
+ margin-bottom: var(--kds-spacing-1-5);
5945
7403
  }
5946
7404
 
5947
7405
  /* -- Card dimmed (behind overlays) -- */
@@ -6010,6 +7468,25 @@ button.kds-btn-success::after {
6010
7468
  fill: none;
6011
7469
  }
6012
7470
 
7471
+ /* BeerCSS fuerza fill:currentcolor en todo svg, pisando el atributo de
7472
+ presentación; un SVG que declara fill="none" (ilustraciones de trazo)
7473
+ debe conservarlo. */
7474
+ svg[fill='none'] {
7475
+ fill: none;
7476
+ }
7477
+
7478
+ /* -- Bottom sheet centered content --
7479
+ Contenido escueto centrado verticalmente con altura mínima estable, para que
7480
+ el sheet no encoja cuando la vista tiene pocos elementos (p.ej. drawer de
7481
+ autorización: título + ilustración + timer). */
7482
+ .kds-bottom-sheet-centered-content {
7483
+ display: flex;
7484
+ flex-direction: column;
7485
+ align-items: center;
7486
+ justify-content: center;
7487
+ min-height: var(--kds-bottom-sheet-content-min-height, 21rem);
7488
+ }
7489
+
6013
7490
  /* -- Bottom sheet description (subtle / smaller) -- */
6014
7491
  .kds-bottom-sheet-description-subtle {
6015
7492
  color: var(--kds-color-text-secondary);
@@ -6043,17 +7520,42 @@ button.kds-btn-success::after {
6043
7520
  }
6044
7521
 
6045
7522
  .kds-bill-description-picture {
6046
- width: 120px;
6047
- height: 120px;
7523
+ max-width: 100px;
6048
7524
  object-fit: contain;
6049
7525
  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);
7526
+ margin-top: var(--kds-spacing-1-25);
6053
7527
  display: block;
6054
7528
  box-sizing: content-box;
6055
7529
  }
6056
7530
 
7531
+ /* -- Bill Attachments -- */
7532
+ .kds-bill-attachments {
7533
+ display: flex;
7534
+ flex-direction: column;
7535
+ gap: var(--kds-spacing-0-75);
7536
+ }
7537
+
7538
+ .kds-bill-attachment {
7539
+ display: inline-flex;
7540
+ align-items: center;
7541
+ justify-content: flex-start;
7542
+ width: fit-content;
7543
+ color: var(--kds-color-info-main);
7544
+ font-size: var(--kds-font-size-sm);
7545
+ font-weight: var(--kds-font-weight-medium);
7546
+ text-decoration: none;
7547
+ }
7548
+
7549
+ .kds-bill-attachment:hover span {
7550
+ text-decoration: underline;
7551
+ }
7552
+
7553
+ .kds-bill-attachment i {
7554
+ font-size: var(--kds-font-size-lg);
7555
+ min-width: unset;
7556
+ justify-content: flex-start;
7557
+ }
7558
+
6057
7559
  /* -- Share Card (mustContinue) -- */
6058
7560
  .kds-share-card {
6059
7561
  margin-top: var(--kds-spacing-2);
@@ -6163,8 +7665,10 @@ button.kds-btn-success::after {
6163
7665
  Desktop (min-width: 768px) restores padding + full border-radius. */
6164
7666
  .kds-payment-flow .kds-card-elevated {
6165
7667
  margin-top: 0;
6166
- padding-top: 0;
6167
- padding-bottom: 0;
7668
+ /* Vertical > lateral: la card del flujo respira arriba/abajo (spacing-3)
7669
+ manteniendo el padding lateral del base (spacing-2 / spacing-3 mobile). */
7670
+ padding-top: var(--kds-spacing-3);
7671
+ padding-bottom: var(--kds-spacing-3);
6168
7672
  }
6169
7673
 
6170
7674
  /* Reset BeerCSS sibling margin on form/section inside payment cards.
@@ -6179,12 +7683,43 @@ button.kds-btn-success::after {
6179
7683
  padding-bottom: 0;
6180
7684
  }
6181
7685
 
6182
- /* Payment stage full-viewport centered wrapper */
7686
+ /* Body card: alto mínimo estable para que NO salte entre estados de distinta altura
7687
+ (loader/progress ↔ formulario). Solo la body card = hijo directo de `.kds-screen`
7688
+ (los headers van dentro de `.kds-invoice-sticky-wrap`, no la afecta).
7689
+ Flex-column para que el contenido quede arriba y el footer interno (versión / secure
7690
+ footer) pueda pinearse al fondo con `margin-top: auto` aunque el contenido sea corto. */
7691
+ .kds-screen > .kds-card-elevated {
7692
+ min-height: 36rem;
7693
+ display: flex;
7694
+ flex-direction: column;
7695
+ /* mismo radio que el header (invoice-sticky usa radius-card); el base card-elevated
7696
+ es radius-md (8px) y quedaba distinto al header en desktop. */
7697
+ border-radius: var(--kds-radius-card);
7698
+ }
7699
+
7700
+ /* Variante sin radio superior: pantallas de resultado (éxito/fallo/timeout) que van
7701
+ SIN header arriba — la body card va sola y su tope queda plano (flush) en vez de
7702
+ con esquinas redondeadas sueltas. El padding del card sigue manejando el inset. */
7703
+ /* Tope plano SOLO en mobile (< 768px): la body card se pega al borde superior del widget
7704
+ (igual que el header sticky, que también aplana el tope solo en mobile). En desktop la card
7705
+ va centrada y separada, así que conserva el border-radius superior redondeado. */
7706
+ @media (max-width: 767px) {
7707
+ .kds-screen > .kds-card-elevated.kds-card-elevated--flush-top,
7708
+ .kds-card-elevated.kds-card-elevated--flush-top {
7709
+ border-top-left-radius: 0;
7710
+ border-top-right-radius: 0;
7711
+ }
7712
+ }
7713
+
7714
+ /* Payment stage — full-viewport centered wrapper.
7715
+ Fondo muted (gris) para que las cards blancas (header + body) y el gap entre
7716
+ ellas se distingan; sin esto, card blanca sobre fondo blanco se funden. */
6183
7717
  .kds-payment-stage {
6184
7718
  min-height: 100vh;
6185
7719
  display: flex;
6186
7720
  justify-content: center;
6187
7721
  padding: var(--kds-spacing-2-5) var(--kds-spacing-2) var(--kds-spacing-8);
7722
+ background: var(--kds-color-background-muted);
6188
7723
  }
6189
7724
 
6190
7725
  /* Payment flow — narrow column */
@@ -6218,23 +7753,23 @@ button.kds-btn-success::after {
6218
7753
  width: 100%;
6219
7754
  }
6220
7755
 
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. */
7756
+ /* Invoice sticky card padding (mobile).
7757
+ Top: estático 12px. Bottom: progresivo 16px (expandido, = desktop) → 8px (colapsado).
7758
+ El padding-bottom animado se reduce al colapsar — sin depender de que el clip-path
7759
+ corte el padding abruptamente. */
6228
7760
  .kds-payment-flow .kds-card-elevated.kds-invoice-sticky {
6229
7761
  padding-top: var(--kds-spacing-1-5);
7762
+ padding-bottom: calc(
7763
+ var(--kds-spacing-2)
7764
+ - var(--kds-spacing-1) * var(--collapse-progress, 0)
7765
+ );
6230
7766
  }
6231
7767
 
6232
7768
 
6233
7769
  }
6234
7770
 
6235
- /* Desktop: restore card padding and full border-radius on invoice sticky */
7771
+ /* Desktop: restore sticky header padding and full border-radius on invoice sticky */
6236
7772
  @media (min-width: 768px) {
6237
- .kds-payment-flow .kds-card-elevated,
6238
7773
  .kds-payment-flow .kds-card-elevated.kds-invoice-sticky {
6239
7774
  padding-top: var(--kds-spacing-2);
6240
7775
  padding-bottom: var(--kds-spacing-2);
@@ -6338,6 +7873,28 @@ button.kds-btn-success::after {
6338
7873
  background: rgba(255, 255, 255, .08);
6339
7874
  }
6340
7875
 
7876
+ /* Icon-only arrows: kill the BeerCSS button active/focus ring (stray halo). */
7877
+ .kds-proto-nav-arrow:active,
7878
+ .kds-proto-nav-arrow:focus,
7879
+ .kds-proto-nav-arrow:focus-visible {
7880
+ outline: none;
7881
+ border: 0;
7882
+ box-shadow: none;
7883
+ }
7884
+
7885
+ /* Dark: the pill inverts to a LIGHT surface (bg = text-primary = white), so
7886
+ its contents need dark ink — the white-alpha tokens above become invisible. */
7887
+ [data-theme="dark"] .kds-proto-nav-label {
7888
+ color: var(--kds-color-gray-500);
7889
+ }
7890
+ [data-theme="dark"] .kds-proto-nav-arrow {
7891
+ color: var(--kds-color-gray-600);
7892
+ }
7893
+ [data-theme="dark"] .kds-proto-nav-arrow:hover {
7894
+ color: var(--kds-color-gray-900);
7895
+ background: rgba(0, 0, 0, .08);
7896
+ }
7897
+
6341
7898
  /* Tweaks panel — floating config panel */
6342
7899
  .kds-proto-tweaks {
6343
7900
  position: fixed;
@@ -6538,11 +8095,21 @@ button.kds-btn-success::after {
6538
8095
  display: none;
6539
8096
  }
6540
8097
 
6541
- .kds-bank-modal-search {
6542
- padding: 0 var(--kds-spacing-2) var(--kds-spacing-1-5);
8098
+ /* Icon-only close: kill the BeerCSS button active/focus ring (stray border). */
8099
+ .kds-bank-modal-close:active,
8100
+ .kds-bank-modal-close:focus,
8101
+ .kds-bank-modal-close:focus-visible {
8102
+ outline: none;
8103
+ border: 0;
8104
+ box-shadow: none;
8105
+ }
8106
+
8107
+ .kds-bank-modal-close:hover {
8108
+ background: var(--kds-color-action-hover);
6543
8109
  }
6544
8110
 
6545
- .kds-bank-modal-search input {
8111
+ /* Search field reutilizable (standalone o dentro del KdsBankModal). */
8112
+ .kds-search-field {
6546
8113
  width: 100%;
6547
8114
  padding: var(--kds-spacing-1-25) var(--kds-spacing-1-5);
6548
8115
  border: 1px solid var(--kds-color-divider);
@@ -6554,15 +8121,57 @@ button.kds-btn-success::after {
6554
8121
  background: var(--kds-color-surface);
6555
8122
  }
6556
8123
 
6557
- .kds-bank-modal-search input::placeholder {
8124
+ .kds-search-field::placeholder {
6558
8125
  color: var(--kds-color-text-hint);
6559
8126
  }
6560
8127
 
6561
- .kds-bank-modal-search input:focus {
8128
+ .kds-search-field:focus {
6562
8129
  border-color: var(--kds-color-primary-main);
6563
8130
  box-shadow: 0 0 0 2px var(--kds-color-primary-faint);
6564
8131
  }
6565
8132
 
8133
+ /* Variante con ícono (withIcon): lupa posicionada DENTRO del input, a la derecha. */
8134
+ .kds-search-field-wrapper {
8135
+ position: relative;
8136
+ width: 100%;
8137
+ }
8138
+
8139
+ .kds-search-field-icon {
8140
+ position: absolute;
8141
+ right: var(--kds-spacing-1-5);
8142
+ top: 50%;
8143
+ transform: translateY(-50%);
8144
+ font-size: 20px;
8145
+ color: var(--kds-color-text-hint);
8146
+ pointer-events: none;
8147
+ }
8148
+
8149
+ /* Espacio a la derecha del texto para no solaparse con la lupa. */
8150
+ .kds-search-field-wrapper .kds-search-field {
8151
+ padding-right: calc(var(--kds-spacing-1-5) + var(--kds-spacing-3));
8152
+ }
8153
+
8154
+ /* Variante angosta del field (prop `narrow` de KdsTextField): ancho reducido y texto
8155
+ centrado, para inputs de pocos caracteres dispuestos en fila (ej. segmentos de
8156
+ coordenadas o código). No crece ni se encoge dentro de un flex row. */
8157
+ .field.kds-field--narrow {
8158
+ width: 6rem;
8159
+ flex: 0 0 auto;
8160
+ /* En fila horizontal no aplica el margin vertical que BeerCSS da a fields
8161
+ hermanos (regla de "flow" `... + :is(.field,...)`, specificity ~0,3,1);
8162
+ sin el reset los siguientes quedan desalineados hacia abajo. `!important`
8163
+ para ganarle a esa regla de framework. */
8164
+ margin-block: 0 !important;
8165
+ }
8166
+
8167
+ .field.kds-field--narrow > input {
8168
+ text-align: center;
8169
+ }
8170
+
8171
+ .kds-bank-modal-search {
8172
+ padding: 0 var(--kds-spacing-2) var(--kds-spacing-1-5);
8173
+ }
8174
+
6566
8175
  .kds-bank-modal-body {
6567
8176
  flex: 1;
6568
8177
  overflow-y: auto;
@@ -6660,7 +8269,7 @@ button.kds-btn-success::after {
6660
8269
  min-width: 0;
6661
8270
  }
6662
8271
 
6663
- .kds-qr-text .title {
8272
+ .kds-qr-text .kds-qr-title {
6664
8273
  font-family: var(--kds-font-family-primary);
6665
8274
  font-weight: 600;
6666
8275
  font-size: var(--kds-font-size-sm);
@@ -6668,10 +8277,15 @@ button.kds-btn-success::after {
6668
8277
  color: var(--kds-color-text-primary);
6669
8278
  }
6670
8279
 
6671
- .kds-qr-text .sub {
8280
+ .kds-qr-text .kds-qr-subtitle {
6672
8281
  font-family: var(--kds-font-family-primary);
6673
8282
  font-size: var(--kds-font-size-caption);
6674
8283
  color: var(--kds-color-text-secondary);
8284
+ /* Trunca con ellipsis en lugar de wrap a 2 líneas — evita apretar el row
8285
+ cuando el contenedor es angosto (mobile o decorator constrained). */
8286
+ overflow: hidden;
8287
+ text-overflow: ellipsis;
8288
+ white-space: nowrap;
6675
8289
  }
6676
8290
 
6677
8291
  /* QR Badge - "Rápido" indicator */
@@ -6694,46 +8308,26 @@ button.kds-btn-success::after {
6694
8308
  font-size: 20px;
6695
8309
  }
6696
8310
 
6697
- /* Mobile responsive: Ajustes para pantallas pequeñas */
8311
+ /* Mobile responsive (≤480px): respira más mantenemos padding/gap/avatar igual
8312
+ que desktop y solo agregamos truncation al subtitle y `flex-shrink: 0` en los
8313
+ ítems que deben mantener su tamaño (avatar, badge, chevron). */
6698
8314
  @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
8315
  .kds-qr-avatar {
6705
- width: var(--kds-spacing-4-5);
6706
- height: var(--kds-spacing-4-5);
6707
8316
  flex-shrink: 0;
6708
8317
  }
6709
8318
 
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
8319
  .kds-qr-text .sub {
6721
- font-size: var(--kds-font-size-caption);
6722
- line-height: var(--kds-line-height-tight);
6723
8320
  overflow: hidden;
6724
8321
  text-overflow: ellipsis;
6725
8322
  white-space: nowrap;
6726
8323
  }
6727
8324
 
6728
8325
  .kds-qr-badge {
6729
- font-size: var(--kds-font-size-caption);
6730
- padding: var(--kds-spacing-0-25) var(--kds-spacing-0-75);
6731
8326
  flex-shrink: 0;
6732
8327
  }
6733
8328
 
6734
8329
  .kds-qr-row .material-symbols-outlined:last-child,
6735
8330
  .kds-qr-row .material-icons-round:last-child {
6736
- font-size: var(--kds-font-size-base);
6737
8331
  flex-shrink: 0;
6738
8332
  }
6739
8333
  }
@@ -6762,7 +8356,11 @@ button.kds-btn-success::after {
6762
8356
  border-radius: var(--kds-spacing-0-75);
6763
8357
  font-size: var(--kds-font-size-sm);
6764
8358
  cursor: pointer;
6765
- transition: background 0.12s ease;
8359
+ /* Transición más amplia y suave: bg + color + border ease-out */
8360
+ transition:
8361
+ background-color 280ms cubic-bezier(0.2, 0.8, 0.2, 1),
8362
+ color 280ms cubic-bezier(0.2, 0.8, 0.2, 1),
8363
+ border-top-color 280ms ease-out;
6766
8364
  position: relative;
6767
8365
  user-select: none;
6768
8366
  }
@@ -6771,19 +8369,21 @@ button.kds-btn-success::after {
6771
8369
  border-top: 0;
6772
8370
  }
6773
8371
 
6774
- .kds-copyable-table-row:hover {
8372
+ /* Hover NO aplica mientras la row esté en `.copied` o `.settling`
8373
+ (evita el flicker del hover state al desaparecer el verde de "copiado") */
8374
+ .kds-copyable-table-row:not(.copied):not(.settling):hover {
6775
8375
  background: var(--kds-color-primary-faint);
6776
8376
  }
6777
8377
 
6778
- .kds-copyable-table-row:hover + .kds-copyable-table-row {
8378
+ .kds-copyable-table-row:not(.copied):not(.settling):hover + .kds-copyable-table-row {
6779
8379
  border-top-color: transparent;
6780
8380
  }
6781
8381
 
6782
- .kds-copyable-table-row:active {
8382
+ .kds-copyable-table-row:not(.copied):not(.settling):active {
6783
8383
  background: var(--kds-color-primary-selected);
6784
8384
  }
6785
8385
 
6786
- .kds-copyable-table-row .k {
8386
+ .kds-copyable-table-row .kds-key {
6787
8387
  color: var(--kds-color-text-secondary);
6788
8388
  font-size: var(--kds-font-size-caption);
6789
8389
  letter-spacing: 0.3px;
@@ -6792,7 +8392,7 @@ button.kds-btn-success::after {
6792
8392
  flex: 0 0 auto;
6793
8393
  }
6794
8394
 
6795
- .kds-copyable-table-row .v {
8395
+ .kds-copyable-table-row .kds-value {
6796
8396
  color: var(--kds-color-text-primary);
6797
8397
  font-family: 'JetBrains Mono', 'SF Mono', ui-monospace, Menlo, Consolas, monospace;
6798
8398
  font-weight: var(--kds-font-weight-medium);
@@ -6808,17 +8408,18 @@ button.kds-btn-success::after {
6808
8408
  flex: 1 1 auto;
6809
8409
  }
6810
8410
 
6811
- .kds-copyable-table-row .v::after {
8411
+ .kds-copyable-table-row .kds-value::after {
6812
8412
  content: 'content_copy';
6813
8413
  font-family: 'Material Symbols Outlined';
6814
8414
  font-size: var(--kds-font-size-sm);
6815
8415
  color: var(--kds-color-primary-main);
6816
8416
  opacity: 0;
6817
- transition: opacity 0.12s ease;
8417
+ /* Transición coordinada con la row */
8418
+ transition: opacity 280ms cubic-bezier(0.2, 0.8, 0.2, 1);
6818
8419
  flex: 0 0 auto;
6819
8420
  }
6820
8421
 
6821
- .kds-copyable-table-row:hover .v::after {
8422
+ .kds-copyable-table-row:not(.copied):not(.settling):hover .kds-value::after {
6822
8423
  opacity: 0.7;
6823
8424
  }
6824
8425
 
@@ -6826,22 +8427,83 @@ button.kds-btn-success::after {
6826
8427
  background: var(--kds-color-success-soft);
6827
8428
  }
6828
8429
 
6829
- .kds-copyable-table-row.copied .v {
8430
+ .kds-copyable-table-row.copied .kds-value {
6830
8431
  color: var(--kds-color-success-dark);
6831
8432
  }
6832
8433
 
6833
- .kds-copyable-table-row.copied .v::after {
8434
+ .kds-copyable-table-row.copied .kds-value::after {
6834
8435
  content: 'check';
6835
8436
  font-family: 'Material Symbols Outlined';
6836
8437
  color: var(--kds-color-success-dark);
6837
8438
  opacity: 1;
6838
8439
  }
6839
8440
 
8441
+ /* Dark: success-dark text is near-invisible on the dark translucent success
8442
+ surface — use the alert success text token (light green in dark). */
8443
+ [data-theme="dark"] .kds-copyable-table-row.copied .kds-value,
8444
+ [data-theme="dark"] .kds-copyable-table-row.copied .kds-value::after {
8445
+ color: var(--kds-alert-success-text);
8446
+ }
8447
+
8448
+ /* `.settling`: estado de "recién copiado" — la row vuelve al normal sin
8449
+ activar hover. Dura lo mismo que la transición (~300ms) y se aplica
8450
+ desde React tras quitar `.copied`. */
8451
+ .kds-copyable-table-row.settling .kds-value::after {
8452
+ /* Mantén el check con opacity 0 mientras settling, fade out suave */
8453
+ opacity: 0;
8454
+ }
8455
+
6840
8456
  /* Disable BeerCSS ripple on copyable table rows */
6841
8457
  .kds-copyable-table-row::after {
6842
8458
  display: none;
6843
8459
  }
6844
8460
 
8461
+ /* ========================================
8462
+ GRID VARIANT (.kds-copyable-table--grid)
8463
+ Read-only grid of N text cells per row. No copy,
8464
+ no role=button, no "copy all". Cells share the row
8465
+ width equally. Used inside radio options, etc.
8466
+ ======================================== */
8467
+
8468
+ /* La grilla es solo columnas de texto (como el DataTable original): sin caja,
8469
+ borde, fondo ni sombra del contenedor copyable. */
8470
+ .kds-copyable-table--grid {
8471
+ border: 0;
8472
+ border-radius: 0;
8473
+ background: none;
8474
+ padding: 0;
8475
+ box-shadow: none;
8476
+ }
8477
+
8478
+ .kds-copyable-table-row--grid {
8479
+ display: flex;
8480
+ align-items: center;
8481
+ gap: var(--kds-spacing-1-5);
8482
+ padding: 0;
8483
+ /* Sin el divisor border-top que trae la fila base del copyable. */
8484
+ border-top: 0;
8485
+ cursor: default;
8486
+ user-select: text;
8487
+ }
8488
+
8489
+ /* Separación entre filas de la grilla (sin divisores). */
8490
+ .kds-copyable-table-row--grid + .kds-copyable-table-row--grid {
8491
+ margin-top: var(--kds-spacing-0-5);
8492
+ }
8493
+
8494
+ .kds-grid-cell {
8495
+ flex: 1 1 0;
8496
+ min-width: 0;
8497
+ font-size: var(--kds-font-size-sm);
8498
+ font-weight: var(--kds-font-weight-medium);
8499
+ color: var(--kds-color-text-primary);
8500
+ word-break: break-word;
8501
+ }
8502
+
8503
+ .kds-grid-cell--disabled {
8504
+ color: var(--kds-color-text-disabled);
8505
+ }
8506
+
6845
8507
  /* ========================================
6846
8508
  COPY-ALL BUTTON (.kds-copy-all-btn)
6847
8509
  Modifier for kds-btn kds-btn-outlined that adds
@@ -6873,6 +8535,15 @@ a.kds-copy-all-btn.copied:hover {
6873
8535
  border-color: var(--kds-color-success-border-light);
6874
8536
  }
6875
8537
 
8538
+ /* Dark: lighten the "Datos copiados" text/border on the dark success surface. */
8539
+ [data-theme="dark"] button.kds-copy-all-btn.copied,
8540
+ [data-theme="dark"] button.kds-copy-all-btn.copied:hover,
8541
+ [data-theme="dark"] a.kds-copy-all-btn.copied,
8542
+ [data-theme="dark"] a.kds-copy-all-btn.copied:hover {
8543
+ color: var(--kds-alert-success-text);
8544
+ border-color: var(--kds-alert-success-border);
8545
+ }
8546
+
6876
8547
  /* ========================================
6877
8548
  SECTION NOTE (.kds-section-note)
6878
8549
  Lightweight inline note for contextual caveats.
@@ -6907,6 +8578,17 @@ a.kds-copy-all-btn.copied:hover {
6907
8578
  .kds-section-note.kds-error { color: var(--kds-color-error-dark); }
6908
8579
  .kds-section-note.kds-error > i { color: var(--kds-color-error-dark); }
6909
8580
 
8581
+ /* Dark: the semantic *-dark shades are near-invisible on the dark page bg —
8582
+ use the alert *-text tokens (light shades in dark). Base note is warning. */
8583
+ [data-theme="dark"] .kds-section-note,
8584
+ [data-theme="dark"] .kds-section-note > i { color: var(--kds-alert-warning-text); }
8585
+ [data-theme="dark"] .kds-section-note.kds-info,
8586
+ [data-theme="dark"] .kds-section-note.kds-info > i { color: var(--kds-alert-info-text); }
8587
+ [data-theme="dark"] .kds-section-note.kds-success,
8588
+ [data-theme="dark"] .kds-section-note.kds-success > i { color: var(--kds-alert-success-text); }
8589
+ [data-theme="dark"] .kds-section-note.kds-error,
8590
+ [data-theme="dark"] .kds-section-note.kds-error > i { color: var(--kds-alert-error-text); }
8591
+
6910
8592
  /* ========================================
6911
8593
  INFO TOOLTIP (.kds-info-tip)
6912
8594
  Inline info button with hover/click tooltip bubble.
@@ -7002,7 +8684,7 @@ a.kds-copy-all-btn.copied:hover {
7002
8684
  .kds-monto-row {
7003
8685
  display: flex;
7004
8686
  justify-content: space-between;
7005
- align-items: flex-end;
8687
+ align-items: flex-start;
7006
8688
  padding: var(--kds-spacing-1-75) 0 var(--kds-spacing-1);
7007
8689
  border-top: 1px dashed var(--kds-color-divider);
7008
8690
  margin-top: var(--kds-spacing-1-75);
@@ -7026,3 +8708,332 @@ a.kds-copy-all-btn.copied:hover {
7026
8708
  color: var(--kds-color-text-primary);
7027
8709
  }
7028
8710
 
8711
+ /* ========================================
8712
+ PAYMENT TOTAL (.kds-payment-total)
8713
+ Bloque "monto a pagar" para QR view y email templates.
8714
+ Portado desde paylink-ligopay → <kh:paymentTotal> + materialize-config.css.
8715
+ Variantes:
8716
+ - default: amount grande (3rem) en color primario Khipu.
8717
+ - email: amount compacto (1.5rem) en color texto primario.
8718
+ Responsive (default): mobile (≤ 1024px) centra y oculta titleMobile.
8719
+ ======================================== */
8720
+
8721
+ .kds-payment-total {
8722
+ /* Component-scoped tokens — overrideables sin tocar el CSS global. */
8723
+ --kds-payment-total-title-size: var(--kds-font-size-2xl); /* 24px */
8724
+ --kds-payment-total-label-size: var(--kds-font-size-xl); /* 20px */
8725
+ --kds-payment-amount-size: 3rem; /* 48px — destacado */
8726
+ --kds-payment-amount-line-height: var(--kds-line-height-snug);
8727
+ --kds-payment-amount-color: var(--kds-color-primary-main);
8728
+ --kds-payment-amount-weight: var(--kds-font-weight-medium);
8729
+
8730
+ text-align: left;
8731
+ }
8732
+
8733
+ .kds-payment-total-title,
8734
+ .kds-payment-total-title-mobile {
8735
+ font-size: var(--kds-payment-total-title-size);
8736
+ font-weight: var(--kds-font-weight-semibold);
8737
+ color: var(--kds-color-text-primary);
8738
+ margin-bottom: var(--kds-spacing-4);
8739
+ }
8740
+
8741
+ .kds-payment-total-title-mobile {
8742
+ display: none;
8743
+ }
8744
+
8745
+ .kds-payment-total .kds-payment-label {
8746
+ font-size: var(--kds-payment-total-label-size);
8747
+ font-weight: var(--kds-font-weight-semibold);
8748
+ color: var(--kds-color-text-primary);
8749
+ margin-bottom: var(--kds-spacing-1);
8750
+ padding-top: 0;
8751
+ }
8752
+
8753
+ .kds-payment-total .kds-payment-amount {
8754
+ font-size: var(--kds-payment-amount-size);
8755
+ font-weight: var(--kds-payment-amount-weight);
8756
+ line-height: var(--kds-payment-amount-line-height);
8757
+ color: var(--kds-payment-amount-color);
8758
+ margin-bottom: var(--kds-spacing-4);
8759
+ }
8760
+
8761
+ .kds-payment-total .kds-payment-amount .kds-payment-total-decimal-sup,
8762
+ .kds-invoice-amount .kds-payment-total-decimal-sup {
8763
+ font-size: var(--kds-font-size-decimal-sup);
8764
+ font-weight: var(--kds-font-weight-semibold);
8765
+ position: relative;
8766
+ top: 0;
8767
+ }
8768
+
8769
+ /* Variante email: amount compacto en color texto primario, alineación izquierda fija */
8770
+ .kds-payment-total--email {
8771
+ --kds-payment-amount-size: var(--kds-font-size-2xl); /* 24px */
8772
+ --kds-payment-amount-line-height: var(--kds-line-height-snug);
8773
+ --kds-payment-amount-color: var(--kds-color-text-primary);
8774
+ --kds-payment-amount-weight: var(--kds-font-weight-semibold);
8775
+ }
8776
+
8777
+ /* Tone modifier: swaps the amount color to the khipu-blue / LigoPay info blue.
8778
+ Combinable with --email or with the default variant. */
8779
+ .kds-payment-total--tone-info {
8780
+ --kds-payment-amount-color: var(--kds-color-info-blue); /* #5A5FE0 */
8781
+ }
8782
+
8783
+ /* Centered modifier: removes the asymmetric desktop padding (which exists for
8784
+ LigoPay QR view) and centers all internal text. Composable with --email and
8785
+ --tone-info. */
8786
+ .kds-payment-total--centered {
8787
+ padding-left: 0;
8788
+ padding-top: 0;
8789
+ text-align: center;
8790
+ }
8791
+ .kds-payment-total--centered .kds-payment-total-title,
8792
+ .kds-payment-total--centered .kds-payment-total-title-mobile,
8793
+ .kds-payment-total--centered .kds-payment-label,
8794
+ .kds-payment-total--centered .kds-payment-amount {
8795
+ text-align: center;
8796
+ }
8797
+
8798
+ /* Responsive: mobile ≤ 1024px — centra y conmuta los títulos (sólo variante default) */
8799
+ @media (max-width: 1024px) {
8800
+ .kds-payment-total {
8801
+ text-align: center;
8802
+ }
8803
+
8804
+ .kds-payment-total .kds-payment-total-title {
8805
+ display: none;
8806
+ }
8807
+
8808
+ .kds-payment-total .kds-payment-total-title-mobile {
8809
+ display: block;
8810
+ text-align: center;
8811
+ }
8812
+
8813
+ .kds-payment-total .kds-payment-label,
8814
+ .kds-payment-total .kds-payment-amount {
8815
+ text-align: center;
8816
+ }
8817
+
8818
+ /* Email mantiene alineación izquierda incluso en mobile (matchea email clients) */
8819
+ .kds-payment-total--email,
8820
+ .kds-payment-total--email .kds-payment-label,
8821
+ .kds-payment-total--email .kds-payment-amount {
8822
+ text-align: left;
8823
+ }
8824
+ }
8825
+
8826
+ /* ==========================================================================
8827
+ GRID UTILITIES — semantic aliases for BeerCSS .grid / .s* / .m* / .l*
8828
+
8829
+ Estas utilidades son ALIAS de las clases cortas de BeerCSS para que
8830
+ consumers (payment, LigoPay, Grails legacy) puedan escribir markup con
8831
+ nombres descriptivos sin renunciar al grid de 12 columnas de BeerCSS.
8832
+
8833
+ Convención (estilo Bootstrap responsive):
8834
+ - kds-grid → .grid (12-col CSS grid + 1rem gap)
8835
+ - kds-col-sm-{1..12} → .s{1..12} (mobile, siempre aplica)
8836
+ - kds-col-md-{1..12} → .m{1..12} (>=601px)
8837
+ - kds-col-lg-{1..12} → .l{1..12} (>=993px)
8838
+
8839
+ Las definiciones originales de BeerCSS NO se tocan; estas son aditivas.
8840
+ ========================================================================== */
8841
+
8842
+ .kds-grid {
8843
+ --_gap: 1rem;
8844
+ display: grid;
8845
+ grid-template-columns: repeat(12, 1fr);
8846
+ gap: var(--_gap);
8847
+ block-size: auto;
8848
+ }
8849
+
8850
+ .kds-grid.kds-grid-no-space { --_gap: 0rem; }
8851
+ .kds-grid.kds-grid-medium-space { --_gap: 1.5rem; }
8852
+ .kds-grid.kds-grid-large-space { --_gap: 2rem; }
8853
+
8854
+ .kds-grid > * { margin: 0; }
8855
+
8856
+ .kds-col-sm-1 { grid-area: auto/span 1; }
8857
+ .kds-col-sm-2 { grid-area: auto/span 2; }
8858
+ .kds-col-sm-3 { grid-area: auto/span 3; }
8859
+ .kds-col-sm-4 { grid-area: auto/span 4; }
8860
+ .kds-col-sm-5 { grid-area: auto/span 5; }
8861
+ .kds-col-sm-6 { grid-area: auto/span 6; }
8862
+ .kds-col-sm-7 { grid-area: auto/span 7; }
8863
+ .kds-col-sm-8 { grid-area: auto/span 8; }
8864
+ .kds-col-sm-9 { grid-area: auto/span 9; }
8865
+ .kds-col-sm-10 { grid-area: auto/span 10; }
8866
+ .kds-col-sm-11 { grid-area: auto/span 11; }
8867
+ .kds-col-sm-12 { grid-area: auto/span 12; }
8868
+
8869
+ @media only screen and (min-width: 601px) {
8870
+ .kds-col-md-1 { grid-area: auto/span 1; }
8871
+ .kds-col-md-2 { grid-area: auto/span 2; }
8872
+ .kds-col-md-3 { grid-area: auto/span 3; }
8873
+ .kds-col-md-4 { grid-area: auto/span 4; }
8874
+ .kds-col-md-5 { grid-area: auto/span 5; }
8875
+ .kds-col-md-6 { grid-area: auto/span 6; }
8876
+ .kds-col-md-7 { grid-area: auto/span 7; }
8877
+ .kds-col-md-8 { grid-area: auto/span 8; }
8878
+ .kds-col-md-9 { grid-area: auto/span 9; }
8879
+ .kds-col-md-10 { grid-area: auto/span 10; }
8880
+ .kds-col-md-11 { grid-area: auto/span 11; }
8881
+ .kds-col-md-12 { grid-area: auto/span 12; }
8882
+ }
8883
+
8884
+ @media only screen and (min-width: 993px) {
8885
+ .kds-col-lg-1 { grid-area: auto/span 1; }
8886
+ .kds-col-lg-2 { grid-area: auto/span 2; }
8887
+ .kds-col-lg-3 { grid-area: auto/span 3; }
8888
+ .kds-col-lg-4 { grid-area: auto/span 4; }
8889
+ .kds-col-lg-5 { grid-area: auto/span 5; }
8890
+ .kds-col-lg-6 { grid-area: auto/span 6; }
8891
+ .kds-col-lg-7 { grid-area: auto/span 7; }
8892
+ .kds-col-lg-8 { grid-area: auto/span 8; }
8893
+ .kds-col-lg-9 { grid-area: auto/span 9; }
8894
+ .kds-col-lg-10 { grid-area: auto/span 10; }
8895
+ .kds-col-lg-11 { grid-area: auto/span 11; }
8896
+ .kds-col-lg-12 { grid-area: auto/span 12; }
8897
+ }
8898
+
8899
+ /* === Form icon chip ================================================== */
8900
+ /* Chip cuadrado con el icono del formulario (SecureForm, headers OIDC). */
8901
+
8902
+ .kds-form-icon-container {
8903
+ align-items: center;
8904
+ align-self: stretch;
8905
+ display: flex;
8906
+ flex-direction: column;
8907
+ gap: var(--kds-spacing-1);
8908
+ justify-content: center;
8909
+ padding: 0 var(--kds-spacing-2);
8910
+ }
8911
+
8912
+ .kds-form-icon-box {
8913
+ align-items: center;
8914
+ background: var(--kds-color-background-brand-subtle, #f0f0fa);
8915
+ border-radius: var(--kds-radius-md);
8916
+ display: flex;
8917
+ height: 52px;
8918
+ justify-content: center;
8919
+ padding: var(--kds-spacing-1);
8920
+ width: 52px;
8921
+ }
8922
+
8923
+ .kds-form-icon-box .kds-form-icon,
8924
+ .kds-form-icon-box > img,
8925
+ .kds-form-icon-box > svg {
8926
+ aspect-ratio: 1 / 1;
8927
+ flex-shrink: 0;
8928
+ height: 32px;
8929
+ width: 32px;
8930
+ }
8931
+
8932
+ @media (min-width: 600px) {
8933
+ .kds-form-icon-box {
8934
+ height: 56px;
8935
+ width: 56px;
8936
+ }
8937
+
8938
+ .kds-form-icon-box .kds-form-icon,
8939
+ .kds-form-icon-box > img,
8940
+ .kds-form-icon-box > svg {
8941
+ height: 30px;
8942
+ width: 30px;
8943
+ }
8944
+ }
8945
+
8946
+ @media (min-width: 900px) {
8947
+ .kds-form-icon-box {
8948
+ height: 60px;
8949
+ width: 60px;
8950
+ }
8951
+
8952
+ .kds-form-icon-box .kds-form-icon,
8953
+ .kds-form-icon-box > img,
8954
+ .kds-form-icon-box > svg {
8955
+ height: 32px;
8956
+ width: 32px;
8957
+ }
8958
+ }
8959
+
8960
+ /* === Accordion (componente KdsAccordion) ============================= */
8961
+ /* El bundle solo estilaba .kds-contract-accordion; estas son las clases
8962
+ que emite el componente React (details/summary con chevron propio). */
8963
+
8964
+ .kds-accordion {
8965
+ border: 1px solid var(--kds-accordion-border);
8966
+ border-radius: var(--kds-radius-md);
8967
+ overflow: hidden;
8968
+ }
8969
+
8970
+ .kds-accordion + .kds-accordion {
8971
+ margin-top: var(--kds-spacing-1);
8972
+ }
8973
+
8974
+ .kds-accordion-summary {
8975
+ align-items: center;
8976
+ background: var(--kds-accordion-header-bg);
8977
+ color: var(--kds-color-text-primary);
8978
+ cursor: pointer;
8979
+ display: flex;
8980
+ font-weight: var(--kds-font-weight-semibold);
8981
+ justify-content: space-between;
8982
+ list-style: none;
8983
+ padding: var(--kds-spacing-1) var(--kds-spacing-2);
8984
+ transition: background 0.2s ease;
8985
+ }
8986
+
8987
+ .kds-accordion-summary::-webkit-details-marker {
8988
+ display: none;
8989
+ }
8990
+
8991
+ .kds-accordion-summary:hover {
8992
+ background: var(--kds-color-background-elevated);
8993
+ }
8994
+
8995
+ .kds-accordion-summary > .material-symbols-outlined {
8996
+ transition: transform 0.3s ease;
8997
+ }
8998
+
8999
+ .kds-accordion[open] > .kds-accordion-summary > .material-symbols-outlined {
9000
+ transform: rotate(180deg);
9001
+ }
9002
+
9003
+ .kds-accordion[open] > .kds-accordion-summary {
9004
+ background: var(--kds-accordion-bg-open);
9005
+ border-bottom: 1px solid var(--kds-accordion-border);
9006
+ }
9007
+
9008
+ .kds-accordion-details {
9009
+ background: var(--kds-accordion-bg-open);
9010
+ color: var(--kds-color-text-secondary);
9011
+ font-size: var(--kds-font-size-sm);
9012
+ line-height: var(--kds-line-height-relaxed);
9013
+ padding: var(--kds-spacing-1) var(--kds-spacing-2);
9014
+ }
9015
+
9016
+ .kds-accordion-details ul,
9017
+ .kds-accordion-details ol {
9018
+ margin: 0;
9019
+ padding-inline-start: var(--kds-spacing-2);
9020
+ }
9021
+
9022
+ /* === Display utilities ================================================ */
9023
+
9024
+ .kds-block {
9025
+ display: block;
9026
+ }
9027
+
9028
+ .kds-inline {
9029
+ display: inline;
9030
+ }
9031
+
9032
+ .kds-inline-block {
9033
+ display: inline-block;
9034
+ }
9035
+
9036
+ .kds-pre-wrap {
9037
+ white-space: pre-wrap;
9038
+ }
9039
+