@khipu/design-system 0.2.0-alpha.11 → 0.2.0-alpha.110

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-18T16:43:30.229Z
14
+ * Generated: 2026-07-08T20:42:31.506Z
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;
@@ -134,6 +140,8 @@
134
140
  --kds-font-weight-bold: 700;
135
141
 
136
142
  /* Font sizes (base scale xs–4xl: see responsive section below) */
143
+ --kds-font-size-decimal-sup: 0.5em;
144
+
137
145
  /* Line heights */
138
146
  --kds-line-height-tight: 1.2;
139
147
  --kds-line-height-snug: 1.375;
@@ -355,6 +363,7 @@
355
363
  RESPONSIVE TYPOGRAPHY (MOBILE BASE)
356
364
  ========================================================================== */
357
365
 
366
+ --kds-font-size-2xs: 0.5625rem;
358
367
 
359
368
  /* Base font-size scale */
360
369
  --kds-font-size-xs: 0.6875rem;
@@ -402,6 +411,7 @@
402
411
  /* Tablet (600px+) - Typography */
403
412
  @media (min-width: 600px) {
404
413
  :root {
414
+ --kds-font-size-2xs: 0.625rem;
405
415
 
406
416
  /* Base font-size scale */
407
417
  --kds-font-size-xs: 0.75rem;
@@ -439,6 +449,7 @@
439
449
  /* Desktop (840px+) - Typography */
440
450
  @media (min-width: 840px) {
441
451
  :root {
452
+ --kds-font-size-2xs: 0.625rem;
442
453
 
443
454
  /* Base font-size scale */
444
455
  --kds-font-size-xs: 0.75rem;
@@ -688,7 +699,8 @@ body.dark {
688
699
  --kds-color-danger-soft: var(--kds-alert-error-bg); /* #FEF2F2 */
689
700
 
690
701
  /* Bottom Sheet */
691
- --kds-bottom-sheet-max-width: 480px;
702
+ /* Mismo ancho que la card principal del flujo (stage narrow). */
703
+ --kds-bottom-sheet-max-width: var(--kds-stage-narrow-max-width);
692
704
  --kds-bottom-sheet-shadow: 0 -8px var(--kds-spacing-3-5) rgba(0, 0, 0, 0.18);
693
705
 
694
706
  /* Status Block */
@@ -839,6 +851,10 @@ a.kds-btn {
839
851
  /* Spacing - Using design tokens */
840
852
  padding: var(--kds-spacing-button-padding); /* 0 16px */
841
853
  height: var(--kds-spacing-button-min-height); /* 44px */
854
+ /* min-height además de height: en contenedores flex-column (p.ej. pantallas de
855
+ resultado/timeout) el botón es flex-item y, con solo `height`, flex-shrink lo
856
+ colapsaba a la altura del texto (~21px). min-height impide que se encoja. */
857
+ min-height: var(--kds-spacing-button-min-height); /* 44px */
842
858
 
843
859
  /* Border - Using design token */
844
860
  border-radius: var(--kds-radius-button); /* 10px */
@@ -846,7 +862,10 @@ a.kds-btn {
846
862
 
847
863
  /* Interaction */
848
864
  cursor: pointer;
849
- transition: background .15s, box-shadow .15s, transform .05s;
865
+ transition: background-color var(--kds-transition-shorter) var(--kds-easing-standard),
866
+ color var(--kds-transition-shorter) var(--kds-easing-standard),
867
+ box-shadow var(--kds-transition-shorter) var(--kds-easing-standard),
868
+ transform var(--kds-transition-shortest) var(--kds-easing-standard);
850
869
  box-sizing: border-box;
851
870
  vertical-align: middle;
852
871
  }
@@ -978,7 +997,7 @@ a.kds-btn-text {
978
997
 
979
998
  button.kds-btn-text:hover:not(:disabled),
980
999
  a.kds-btn-text:hover:not(:disabled) {
981
- background: var(--kds-color-primary-hover); /* rgba(131, 71, 173, 0.04) */
1000
+ color: var(--kds-color-primary-dark);
982
1001
  }
983
1002
 
984
1003
  button.kds-btn-text:disabled,
@@ -988,6 +1007,30 @@ a.kds-btn-text:disabled {
988
1007
  box-shadow: none;
989
1008
  }
990
1009
 
1010
+ /* Error text (destructive, ghost) — red text, no fill/border. Para acciones
1011
+ destructivas secundarias que no deben competir con el CTA primario. */
1012
+ button.kds-btn-error-text,
1013
+ a.kds-btn-error-text {
1014
+ background: transparent;
1015
+ color: var(--kds-color-error-main);
1016
+ border: none;
1017
+ padding: 6px var(--kds-spacing-2);
1018
+ min-height: 32px;
1019
+ box-shadow: none;
1020
+ }
1021
+
1022
+ button.kds-btn-error-text:hover:not(:disabled),
1023
+ a.kds-btn-error-text:hover:not(:disabled) {
1024
+ color: var(--kds-color-error-dark);
1025
+ }
1026
+
1027
+ button.kds-btn-error-text:disabled,
1028
+ a.kds-btn-error-text:disabled {
1029
+ background: transparent;
1030
+ color: var(--kds-color-action-disabled);
1031
+ box-shadow: none;
1032
+ }
1033
+
991
1034
  /* Icon wrapper - control size via span container */
992
1035
  button.kds-btn > .kds-icon,
993
1036
  a.kds-btn > .kds-icon {
@@ -1073,63 +1116,90 @@ a.kds-btn.kds-btn-md {
1073
1116
  }
1074
1117
  }
1075
1118
 
1076
- /* Selection card */
1119
+ /* Selection card.
1120
+ IMPORTANTE: este componente se renderiza como `<button>`. BeerCSS aplica al
1121
+ `button` base: `display: inline-flex; align-items: center; justify-content: center;
1122
+ block-size: 2.5rem; padding: 0 1rem; background: var(--primary); color: on-primary`.
1123
+ Todas esas reglas hay que sobreescribirlas para que el card-selector funcione como
1124
+ un contenedor flex column de altura variable y alineado a la izquierda.
1125
+
1126
+ Convención del DS: spacing entre hijos via `gap` del flex padre + padding.
1127
+ Los elementos internos (.kds-card-selector-icon/-title/-description) NO usan margin. */
1077
1128
  .kds-card-selector {
1129
+ /* Reset BeerCSS button */
1130
+ display: flex;
1131
+ flex-direction: column;
1132
+ align-items: stretch;
1133
+ justify-content: flex-start;
1134
+ block-size: auto;
1135
+ inline-size: 100%;
1136
+ text-align: left;
1137
+ line-height: 1.5;
1138
+ color: var(--kds-color-text-primary);
1139
+ /* Estilos propios del card-selector */
1078
1140
  padding: var(--kds-spacing-3);
1141
+ gap: var(--kds-spacing-1-5);
1079
1142
  border-radius: var(--kds-radius-md);
1080
- border: 2px solid var(--kds-border-light);
1143
+ border: var(--kds-border-width-md) solid var(--kds-border-light);
1081
1144
  background: var(--kds-color-background-paper);
1082
1145
  transition: all 0.3s ease;
1083
- text-align: left;
1084
1146
  cursor: pointer;
1085
- display: flex;
1086
- flex-direction: column;
1087
1147
  }
1088
1148
 
1089
- .kds-card-selector:hover {
1090
- border-color: #D1D5DB; /* gray-300 */
1149
+ .kds-card-selector:hover:not(.selected) {
1150
+ border-color: var(--kds-color-primary-light);
1151
+ background: var(--kds-color-primary-hover);
1152
+ }
1153
+
1154
+ /* Override del ripple/overlay default de BeerCSS:
1155
+ `:is(.wave,.chip,.button,button,nav.tabbed>a,...):not([class*=ripple])::after`
1156
+ tiene specificity (0,2,4) según DevTools (Chrome cuenta cada selector dentro
1157
+ de :is() individualmente para algunos motores). Usamos !important — consistente
1158
+ con cómo se overridean otros estilos de BeerCSS en este archivo (67 usos previos).
1159
+ Antes pintaba negro porque `currentColor` resolvía a text-primary del card. */
1160
+ .kds-card-selector::after {
1161
+ background-image: radial-gradient(circle, var(--kds-color-primary-main) 1%, transparent 1%) !important;
1091
1162
  }
1092
1163
 
1093
1164
  .kds-card-selector.selected {
1094
- border-color: #3B82F6; /* blue-600 */
1095
- background: #EFF6FF; /* blue-50 */
1165
+ border-color: var(--kds-color-primary-main);
1166
+ background: var(--kds-color-primary-faint);
1096
1167
  box-shadow: var(--kds-shadow-md);
1097
1168
  }
1098
1169
 
1099
- /* Icon container */
1170
+ /* Icon container — spacing manejado por gap del padre, no margin */
1100
1171
  .kds-card-selector-icon {
1101
- width: 48px;
1102
- height: 48px;
1103
- background: #DBEAFE; /* blue-100 */
1104
- border-radius: 8px;
1172
+ width: var(--kds-spacing-6);
1173
+ height: var(--kds-spacing-6);
1174
+ background: var(--kds-color-primary-focus);
1175
+ border-radius: var(--kds-radius-sm);
1105
1176
  display: flex;
1106
1177
  align-items: center;
1107
1178
  justify-content: center;
1108
- margin-bottom: 16px;
1109
1179
  }
1110
1180
 
1111
1181
  .kds-card-selector-icon i,
1112
1182
  .kds-card-selector-icon svg {
1113
- width: 24px;
1114
- height: 24px;
1115
- min-width: 24px;
1116
- min-height: 24px;
1117
- color: #3B82F6; /* blue-600 */
1183
+ width: var(--kds-spacing-3);
1184
+ height: var(--kds-spacing-3);
1185
+ min-width: var(--kds-spacing-3);
1186
+ min-height: var(--kds-spacing-3);
1187
+ font-size: var(--kds-spacing-3);
1188
+ color: var(--kds-color-primary-main);
1118
1189
  }
1119
1190
 
1120
- /* Title */
1191
+ /* Title — spacing manejado por gap del padre, no margin */
1121
1192
  .kds-card-selector-title {
1122
- font-weight: 600;
1193
+ font-weight: var(--kds-font-weight-semibold);
1123
1194
  color: var(--kds-color-text-primary);
1124
- margin-bottom: 8px;
1125
- font-size: 16px;
1195
+ font-size: var(--kds-font-size-lg);
1126
1196
  }
1127
1197
 
1128
1198
  /* Description */
1129
1199
  .kds-card-selector-description {
1130
- font-size: 14px;
1131
- color: #4B5563; /* gray-600 */
1132
- line-height: 1.5;
1200
+ font-size: var(--kds-font-size-sm);
1201
+ color: var(--kds-color-text-secondary);
1202
+ line-height: var(--kds-line-height-normal);
1133
1203
  }
1134
1204
 
1135
1205
  /* Selected state - visual feedback via border only */
@@ -1141,14 +1211,22 @@ a.kds-btn.kds-btn-md {
1141
1211
  display: flex;
1142
1212
  flex-direction: column;
1143
1213
  gap: var(--kds-spacing-2);
1144
- padding: var(--kds-spacing-4);
1214
+ padding: var(--kds-spacing-2);
1145
1215
  border-radius: var(--kds-radius-lg);
1146
1216
  border: 2px solid var(--kds-border-medium);
1147
- max-width: 400px; /* Increased from 365px for better text spacing */
1217
+ /* Ancho mínimo aplicado a TODOS los planes así un plan sin badge en una
1218
+ grilla con un plan recomendado mantiene la misma anchura, y el badge nunca
1219
+ se superpone al título por estrechez del card. */
1220
+ min-width: 260px;
1221
+ max-width: 400px;
1148
1222
  background: var(--kds-surface-base);
1149
1223
  transition: all 0.3s ease;
1150
1224
  cursor: pointer;
1151
- height: 100%; /* Ensure cards fill grid height */
1225
+ /* Alto fijo: el card se estira al alto de su row del grid (align-items stretch
1226
+ es el default) — todas las cards de la misma fila quedan a la misma altura
1227
+ sin importar el contenido. `align-self: stretch` lo hace explícito. */
1228
+ height: 100%;
1229
+ align-self: stretch;
1152
1230
  }
1153
1231
 
1154
1232
  .kds-card-plan:hover {
@@ -1168,18 +1246,19 @@ a.kds-btn.kds-btn-md {
1168
1246
  box-shadow: var(--kds-shadow-elevation-4);
1169
1247
  }
1170
1248
 
1171
- /* Recommended Badge */
1249
+ /* Recommended Badge — inline dentro del card (no flotante).
1250
+ Es el primer hijo del .kds-card-plan, así que aparece arriba de todo
1251
+ el contenido, alineado a la izquierda. */
1172
1252
  .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);
1253
+ align-self: flex-start;
1254
+ padding: var(--kds-spacing-0-5) var(--kds-spacing-1-5);
1177
1255
  background: var(--primary);
1178
1256
  color: white;
1179
- font-size: var(--kds-font-size-sm);
1257
+ font-size: var(--kds-font-size-xs);
1180
1258
  font-weight: var(--kds-font-weight-semibold);
1259
+ line-height: 1;
1181
1260
  border-radius: var(--kds-radius-full);
1182
- box-shadow: var(--kds-shadow-elevation-1);
1261
+ white-space: nowrap;
1183
1262
  }
1184
1263
 
1185
1264
  /* Plan Header */
@@ -1207,13 +1286,11 @@ a.kds-btn.kds-btn-md {
1207
1286
  display: flex;
1208
1287
  align-items: baseline;
1209
1288
  gap: var(--kds-spacing-2);
1210
- padding: var(--kds-spacing-4) 0;
1211
1289
  border-bottom: 1px solid var(--kds-border-light);
1212
- min-height: 88px; /* Standardize price area height */
1213
1290
  }
1214
1291
 
1215
1292
  .kds-price {
1216
- font-size: 42px;
1293
+ font-size: var(--kds-font-size-4xl);
1217
1294
  font-weight: var(--kds-font-weight-bold);
1218
1295
  color: var(--primary);
1219
1296
  line-height: 1.1;
@@ -1468,6 +1545,44 @@ a.kds-btn.kds-btn-md {
1468
1545
  align-items: flex-end;
1469
1546
  }
1470
1547
 
1548
+ .kds-self-center {
1549
+ align-self: center;
1550
+ }
1551
+
1552
+ /* Botón "pelado": neutraliza el chrome de button de BeerCSS (pill 2.5rem,
1553
+ radius, fondo y hover) para botones de terceros con su propio look,
1554
+ p.ej. los share buttons de react-share (ícono circular). */
1555
+ .kds-btn-bare,
1556
+ .kds-btn-bare:hover,
1557
+ .kds-btn-bare:focus,
1558
+ .kds-btn-bare:active {
1559
+ background: none !important;
1560
+ block-size: auto !important;
1561
+ inline-size: auto !important;
1562
+ min-inline-size: 0 !important;
1563
+ padding: 0 !important;
1564
+ margin: 0 !important;
1565
+ border: 0 !important;
1566
+ border-radius: var(--kds-radius-full) !important;
1567
+ box-sizing: border-box !important;
1568
+ box-shadow: none !important;
1569
+ transform: none !important;
1570
+ }
1571
+
1572
+ .kds-btn-bare::before,
1573
+ .kds-btn-bare::after {
1574
+ display: none !important;
1575
+ }
1576
+
1577
+ /* BeerCSS también encoge los íconos hijos de button a 1.5rem (min/max);
1578
+ un botón pelado respeta el tamaño propio del ícono. */
1579
+ .kds-btn-bare > :is(img, svg) {
1580
+ max-block-size: none !important;
1581
+ max-inline-size: none !important;
1582
+ min-block-size: 0 !important;
1583
+ min-inline-size: 0 !important;
1584
+ }
1585
+
1471
1586
  /* Gap utilities */
1472
1587
  .kds-gap-1 { gap: var(--kds-spacing-1); }
1473
1588
  .kds-gap-2 { gap: var(--kds-spacing-2); }
@@ -1504,6 +1619,7 @@ a.kds-btn.kds-btn-md {
1504
1619
  /* Spacing utilities - Margin */
1505
1620
  .kds-m-0 { margin: 0; }
1506
1621
  .kds-m-2 { margin: var(--kds-spacing-2); }
1622
+ .kds-m-2-5 { margin: var(--kds-spacing-2-5); }
1507
1623
  .kds-m-4 { margin: var(--kds-spacing-4); }
1508
1624
  .kds-m-6 { margin: var(--kds-spacing-6); }
1509
1625
  .kds-m-8 { margin: var(--kds-spacing-8); }
@@ -1517,6 +1633,7 @@ a.kds-btn.kds-btn-md {
1517
1633
  .kds-mt-8 { margin-top: var(--kds-spacing-8); }
1518
1634
 
1519
1635
  .kds-mb-0 { margin-bottom: 0; }
1636
+ .kds-mb-0-5 { margin-bottom: var(--kds-spacing-0-5); }
1520
1637
  .kds-mb-1 { margin-bottom: var(--kds-spacing-1); }
1521
1638
  .kds-mb-2 { margin-bottom: var(--kds-spacing-2); }
1522
1639
  .kds-mb-3 { margin-bottom: var(--kds-spacing-3); }
@@ -2029,6 +2146,226 @@ a.kds-btn-google:disabled {
2029
2146
  height: var(--kds-spacing-button-icon-size);
2030
2147
  }
2031
2148
 
2149
+ /* ========================================
2150
+ KDS LINEAR PROGRESS
2151
+ Native <progress> (KdsLinearProgress). Additive over BeerCSS `progress`,
2152
+ scoped to .kds-progress so other consumers are unaffected.
2153
+ ======================================== */
2154
+
2155
+ /* `progress` is inline-block by default → leaves a baseline (descender) gap
2156
+ below the bar. Block removes that gap so it sits flush with siblings. */
2157
+ .kds-progress {
2158
+ display: block;
2159
+ }
2160
+
2161
+ /* Animate the filled value when it changes (e.g. advancing a step). BeerCSS
2162
+ sets the value background but no transition, so the width jumps abruptly. */
2163
+ .kds-progress::-webkit-progress-value {
2164
+ transition: width var(--kds-transition-standard) var(--kds-easing-standard);
2165
+ }
2166
+
2167
+ .kds-progress::-moz-progress-bar {
2168
+ transition: width var(--kds-transition-standard) var(--kds-easing-standard);
2169
+ }
2170
+
2171
+ /* ========================================
2172
+ KDS SECURE LOADER
2173
+ Loader de espera: anillo circular + candado centrado + frases opcionales.
2174
+ El anillo reutiliza el spinner circular de BeerCSS, escalado.
2175
+ ======================================== */
2176
+
2177
+ .kds-secure-loader {
2178
+ display: flex;
2179
+ flex-direction: column;
2180
+ align-items: center;
2181
+ justify-content: center;
2182
+ gap: var(--kds-spacing-4);
2183
+ text-align: center;
2184
+ /* Llena y centra dentro de contenedores flex (pantalla de carga); en flujo
2185
+ normal (contenedor no-flex) se reduce a su contenido. */
2186
+ flex: 1 1 auto;
2187
+ }
2188
+
2189
+ .kds-secure-loader-text {
2190
+ display: flex;
2191
+ flex-direction: column;
2192
+ gap: var(--kds-spacing-0-25);
2193
+ }
2194
+
2195
+ .kds-secure-loader-title {
2196
+ margin: 0;
2197
+ font-family: var(--kds-font-family-primary);
2198
+ font-size: var(--kds-font-size-h6);
2199
+ font-weight: var(--kds-font-weight-semibold);
2200
+ line-height: var(--kds-line-height-snug);
2201
+ color: var(--kds-color-text-primary);
2202
+ }
2203
+
2204
+ .kds-secure-loader-message {
2205
+ margin: 0;
2206
+ /* !important: anula el auto-espaciado de hermanos de BeerCSS (0,5,1);
2207
+ la separación con el título la da el gap del contenedor (spacing-0-25). */
2208
+ margin-top: 0 !important;
2209
+ font-family: var(--kds-font-family-secondary);
2210
+ font-size: var(--kds-font-size-body1);
2211
+ line-height: var(--kds-line-height-normal);
2212
+ color: var(--kds-color-primary-main);
2213
+ }
2214
+
2215
+ /* Caja del spinner con tamaño fijo: la animación no altera el layout.
2216
+ 6.25rem (100px) = tamaño del loader original (CircularProgress size=100). */
2217
+ .kds-secure-loader-spinner {
2218
+ position: relative;
2219
+ display: inline-grid;
2220
+ place-items: center;
2221
+ inline-size: var(--kds-secure-loader-size, 6.25rem);
2222
+ block-size: var(--kds-secure-loader-size, 6.25rem);
2223
+ }
2224
+
2225
+ /* Anillo: SVG nativo con arco vía stroke-dasharray. Replica el CircularProgress
2226
+ original (viewBox 44u, thickness 2, arco ~63%, disableShrink). El giro se anima
2227
+ con stroke-dashoffset (el círculo queda estático y nítido); rotar el SVG con
2228
+ transform rasteriza el anillo y se ve irregular durante el giro. */
2229
+ .kds-secure-loader-ring {
2230
+ inline-size: var(--kds-secure-loader-size, 6.25rem);
2231
+ block-size: var(--kds-secure-loader-size, 6.25rem);
2232
+ }
2233
+
2234
+ .kds-secure-loader-ring circle {
2235
+ fill: none;
2236
+ stroke: var(--kds-color-primary-main);
2237
+ stroke-width: 2;
2238
+ stroke-linecap: round;
2239
+ /* circunferencia = 2π·20.2 ≈ 126.92; arco visible 80 (~63%) + hueco 46.92 */
2240
+ stroke-dasharray: 80 46.92;
2241
+ animation: kds-secure-loader-dash 1.2s linear infinite;
2242
+ }
2243
+
2244
+ @keyframes kds-secure-loader-dash {
2245
+ to {
2246
+ stroke-dashoffset: -126.92;
2247
+ }
2248
+ }
2249
+
2250
+ /* Candado centrado sobre el anillo (~0.3 del diámetro). Centrado explícito
2251
+ (top/left 50% + translate) para que no dependa del grid ni se recorte. */
2252
+ .kds-secure-loader-lock {
2253
+ position: absolute;
2254
+ top: 50%;
2255
+ left: 50%;
2256
+ transform: translate(-50%, -50%);
2257
+ color: var(--kds-color-text-secondary);
2258
+ font-size: calc(var(--kds-secure-loader-size, 6.25rem) * 0.3);
2259
+ line-height: 1;
2260
+ pointer-events: none;
2261
+ }
2262
+
2263
+ /* ========================================
2264
+ KDS COPY BUTTON
2265
+ Chip compacto que muestra un valor + icono copy y lo copia al click.
2266
+ Acción inline (distinto de KdsCopyRow). Look del copy button de khenshin.
2267
+ ======================================== */
2268
+
2269
+ .kds-copy-button {
2270
+ display: inline-flex;
2271
+ align-items: center;
2272
+ gap: var(--kds-spacing-1);
2273
+ padding: var(--kds-spacing-0-25) var(--kds-spacing-1-5);
2274
+ /* No usar el alto de botón táctil de BeerCSS (~44px): es un chip compacto,
2275
+ su alto lo define el contenido + padding. */
2276
+ block-size: auto;
2277
+ min-block-size: 0;
2278
+ border: none;
2279
+ border-radius: var(--kds-radius-button);
2280
+ background: var(--kds-alert-info-bg);
2281
+ color: var(--kds-color-info-dark);
2282
+ font-family: var(--kds-font-family-secondary);
2283
+ font-size: var(--kds-font-size-sm);
2284
+ font-weight: var(--kds-font-weight-medium);
2285
+ line-height: var(--kds-line-height-normal);
2286
+ cursor: pointer;
2287
+ transition: background-color var(--kds-transition-shorter) var(--kds-easing-standard),
2288
+ color var(--kds-transition-shorter) var(--kds-easing-standard);
2289
+ }
2290
+
2291
+ /* active = hover: sin el aro de focus/active (outline morado de BeerCSS). */
2292
+ .kds-copy-button:focus,
2293
+ .kds-copy-button:focus-visible,
2294
+ .kds-copy-button:active {
2295
+ outline: none;
2296
+ }
2297
+
2298
+ /* value y copiedText se apilan en la MISMA celda de grid: la celda toma el
2299
+ ancho del texto más largo, por lo que el botón conserva su ancho al alternar
2300
+ entre "valor" y "copiado" (sin reflow). */
2301
+ .kds-copy-button-label {
2302
+ display: inline-grid;
2303
+ }
2304
+
2305
+ .kds-copy-button-value,
2306
+ .kds-copy-button-copied {
2307
+ grid-area: 1 / 1;
2308
+ white-space: nowrap;
2309
+ }
2310
+
2311
+ /* Estado por defecto: visible el valor, oculto (pero ocupando espacio) el copiado */
2312
+ .kds-copy-button-copied {
2313
+ visibility: hidden;
2314
+ }
2315
+
2316
+ /* Al copiar se invierte la visibilidad; el ancho no cambia */
2317
+ .kds-copy-button.copied .kds-copy-button-value {
2318
+ visibility: hidden;
2319
+ }
2320
+
2321
+ .kds-copy-button.copied .kds-copy-button-copied {
2322
+ visibility: visible;
2323
+ }
2324
+
2325
+ .kds-copy-button .material-symbols-outlined {
2326
+ font-size: 1rem;
2327
+ color: var(--kds-color-info-main);
2328
+ flex-shrink: 0;
2329
+ }
2330
+
2331
+ .kds-copy-button.copied {
2332
+ background: var(--kds-alert-success-bg);
2333
+ color: var(--kds-alert-success-text);
2334
+ animation: kds-copy-button-pulse var(--kds-transition-short) var(--kds-easing-standard);
2335
+ }
2336
+
2337
+ .kds-copy-button.copied .material-symbols-outlined {
2338
+ color: var(--kds-color-success-main);
2339
+ /* el check entra con un pop al copiar */
2340
+ animation: kds-copy-button-pop var(--kds-transition-short) var(--kds-easing-standard);
2341
+ }
2342
+
2343
+ @keyframes kds-copy-button-pulse {
2344
+ 0% {
2345
+ transform: scale(1);
2346
+ }
2347
+ 50% {
2348
+ transform: scale(1.04);
2349
+ }
2350
+ 100% {
2351
+ transform: scale(1);
2352
+ }
2353
+ }
2354
+
2355
+ @keyframes kds-copy-button-pop {
2356
+ 0% {
2357
+ transform: scale(0.3);
2358
+ opacity: 0;
2359
+ }
2360
+ 60% {
2361
+ transform: scale(1.25);
2362
+ opacity: 1;
2363
+ }
2364
+ 100% {
2365
+ transform: scale(1);
2366
+ }
2367
+ }
2368
+
2032
2369
  /* ========================================
2033
2370
  Link Styles
2034
2371
  ======================================== */
@@ -2130,6 +2467,16 @@ footer.primary a:hover {
2130
2467
  margin-right: var(--kds-spacing-2);
2131
2468
  }
2132
2469
 
2470
+ /* Ícono(s) en color primary. Para íconos MUI/SVG dentro del scope que, por el reset
2471
+ `.kds-theme-root svg { fill: currentColor }`, terminan heredando el color de texto
2472
+ (#333) en vez de su color propio — el `color="primary"` de MUI no le gana al contexto.
2473
+ Aplica al contenedor y a sus i/svg hijos. */
2474
+ .kds-icon-primary,
2475
+ .kds-icon-primary i,
2476
+ .kds-icon-primary svg {
2477
+ color: var(--kds-color-primary-main);
2478
+ }
2479
+
2133
2480
  /* Section title icon */
2134
2481
  .kds-stage-section-title i {
2135
2482
  vertical-align: middle;
@@ -2161,6 +2508,25 @@ body.dark {
2161
2508
  transition: border-color 0.2s, box-shadow 0.2s, outline-color 0.2s;
2162
2509
  }
2163
2510
 
2511
+ /* === TEXTAREA FIX === */
2512
+ /* BeerCSS base sets minimal block padding; add breathing room for textarea */
2513
+ .field > textarea {
2514
+ padding-block: 0.75rem;
2515
+ resize: vertical;
2516
+ }
2517
+
2518
+ .field.label > textarea {
2519
+ padding-block-start: 1.5rem;
2520
+ }
2521
+
2522
+ /* Label en reposo (estado "abajo", como placeholder): font-size menor al 1rem de BeerCSS
2523
+ para que un label largo no estorbe con un ícono (prefix/suffix) del input. Token sm
2524
+ (13–14px, siempre < 1rem del input incluso en desktop). Al flotar, BeerCSS lo reduce a
2525
+ .75rem con mayor especificidad, así que esto solo afecta el reposo. */
2526
+ .field.label > label {
2527
+ font-size: var(--kds-font-size-sm);
2528
+ }
2529
+
2164
2530
  /* ==========================================
2165
2531
  NOTCH VISIBILITY (opacity)
2166
2532
  ========================================== */
@@ -2282,6 +2648,23 @@ body.dark {
2282
2648
  color: var(--error); /* Rojo error */
2283
2649
  }
2284
2650
 
2651
+ /* ==========================================
2652
+ PASSWORD REVEAL TOGGLE (KdsTextField `revealable`)
2653
+ El <a role="button"> reusa el posicionamiento de icono de BeerCSS
2654
+ (.field > a, pointer-events: all). Aquí solo: color del icono
2655
+ (on-surface-variant) y un focus ring accesible para el toggle
2656
+ navegable por teclado (BeerCSS hace `outline: inherit` en <a>).
2657
+ ========================================== */
2658
+ .field > a.kds-field-reveal {
2659
+ color: var(--on-surface-variant);
2660
+ }
2661
+
2662
+ .field > a.kds-field-reveal:focus-visible {
2663
+ outline: 2px solid var(--primary);
2664
+ outline-offset: 2px;
2665
+ border-radius: 50%;
2666
+ }
2667
+
2285
2668
  /* ==========================================
2286
2669
  LABEL COLOR
2287
2670
  Mantener sincronizado con border
@@ -2389,6 +2772,71 @@ body.dark {
2389
2772
  display: inline-flex;
2390
2773
  }
2391
2774
 
2775
+ /* ========================================
2776
+ Radix Tooltip (.kds-tooltip)
2777
+ Espejo de `.tooltip` pero para popovers de Radix:
2778
+ bg oscuro, texto blanco, sombra + animación fade/slide direccional.
2779
+ ======================================== */
2780
+
2781
+ .kds-tooltip {
2782
+ background-color: var(--kds-color-text-primary);
2783
+ color: var(--kds-color-background-paper);
2784
+ font-family: var(--kds-font-family-primary);
2785
+ font-size: var(--kds-font-size-xs);
2786
+ font-weight: var(--kds-font-weight-medium);
2787
+ line-height: var(--kds-line-height-normal);
2788
+ border-radius: var(--kds-radius-sm);
2789
+ padding: var(--kds-spacing-0-75) var(--kds-spacing-1-5);
2790
+ box-shadow: var(--kds-shadow-4);
2791
+ max-width: 280px;
2792
+ z-index: var(--kds-z-index-tooltip, 1500);
2793
+ user-select: none;
2794
+ /* Animación direccional según Radix data-side — coordinada con copy-row
2795
+ (cubic-bezier 0.2, 0.8, 0.2, 1) para un feel consistente del DS. */
2796
+ animation-duration: 240ms;
2797
+ animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
2798
+ will-change: transform, opacity;
2799
+ }
2800
+
2801
+ .kds-tooltip[data-state='closed'] {
2802
+ animation-duration: 140ms;
2803
+ animation-timing-function: cubic-bezier(0.4, 0, 0.6, 1);
2804
+ }
2805
+
2806
+ /* Direccional según data-side (Radix añade el attr en runtime).
2807
+ Cada lado entra desde su dirección con ~8px de offset (más visible que 4px). */
2808
+ .kds-tooltip[data-side='top'][data-state='delayed-open'],
2809
+ .kds-tooltip[data-side='top'][data-state='instant-open'] { animation-name: kds-tooltip-in-top; }
2810
+ .kds-tooltip[data-side='bottom'][data-state='delayed-open'],
2811
+ .kds-tooltip[data-side='bottom'][data-state='instant-open'] { animation-name: kds-tooltip-in-bottom; }
2812
+ .kds-tooltip[data-side='left'][data-state='delayed-open'],
2813
+ .kds-tooltip[data-side='left'][data-state='instant-open'] { animation-name: kds-tooltip-in-left; }
2814
+ .kds-tooltip[data-side='right'][data-state='delayed-open'],
2815
+ .kds-tooltip[data-side='right'][data-state='instant-open'] { animation-name: kds-tooltip-in-right; }
2816
+
2817
+ /* Fallback cuando no hay data-side resuelto aún */
2818
+ .kds-tooltip[data-state='delayed-open']:not([data-side]),
2819
+ .kds-tooltip[data-state='instant-open']:not([data-side]) {
2820
+ animation-name: kds-tooltip-in-top;
2821
+ }
2822
+
2823
+ .kds-tooltip[data-state='closed'] { animation-name: kds-tooltip-out; }
2824
+
2825
+ @keyframes kds-tooltip-in-top { from { opacity: 0; transform: translateY(8px) scale(0.96); } to { opacity: 1; transform: none; } }
2826
+ @keyframes kds-tooltip-in-bottom { from { opacity: 0; transform: translateY(-8px) scale(0.96); } to { opacity: 1; transform: none; } }
2827
+ @keyframes kds-tooltip-in-left { from { opacity: 0; transform: translateX(8px) scale(0.96); } to { opacity: 1; transform: none; } }
2828
+ @keyframes kds-tooltip-in-right { from { opacity: 0; transform: translateX(-8px) scale(0.96); } to { opacity: 1; transform: none; } }
2829
+
2830
+ @keyframes kds-tooltip-out {
2831
+ from { opacity: 1; transform: none; }
2832
+ to { opacity: 0; transform: scale(0.96); }
2833
+ }
2834
+
2835
+ /* Arrow (SVG path generado por Radix) */
2836
+ .kds-tooltip-arrow {
2837
+ fill: var(--kds-color-text-primary);
2838
+ }
2839
+
2392
2840
  /* ========================================
2393
2841
  Material Symbols Icon Sizing
2394
2842
  ======================================== */
@@ -2423,6 +2871,16 @@ body.dark {
2423
2871
  width: 100%;
2424
2872
  }
2425
2873
 
2874
+ /* Truncado en una línea con ellipsis. Incluye min-width:0 para que funcione
2875
+ dentro de contenedores flex/grid (el ítem puede encogerse por debajo de su
2876
+ contenido). Útil para nombres/asuntos largos en headers y filas. */
2877
+ .kds-truncate {
2878
+ min-width: 0;
2879
+ overflow: hidden;
2880
+ text-overflow: ellipsis;
2881
+ white-space: nowrap;
2882
+ }
2883
+
2426
2884
  .kds-max-w-sm {
2427
2885
  max-width: 400px;
2428
2886
  }
@@ -2466,6 +2924,11 @@ body.dark {
2466
2924
  display: flex;
2467
2925
  flex-direction: column;
2468
2926
  gap: var(--kds-spacing-3);
2927
+ /* Reset del fieldset nativo: sin borde/padding/margin por defecto del navegador. */
2928
+ margin: 0;
2929
+ padding: 0;
2930
+ border: 0;
2931
+ min-inline-size: 0;
2469
2932
  }
2470
2933
 
2471
2934
  /* Checkbox Label - BeerCSS override */
@@ -2477,7 +2940,7 @@ label.radio {
2477
2940
  border-radius: 8px;
2478
2941
  cursor: pointer;
2479
2942
  transition: background-color 0.2s;
2480
- font-size: 16px;
2943
+ font-size: var(--kds-font-size-sm);
2481
2944
  line-height: 1.5;
2482
2945
  color: var(--kds-color-text-primary);
2483
2946
  }
@@ -2515,6 +2978,42 @@ label.radio:has(input:disabled) {
2515
2978
  opacity: 0.6;
2516
2979
  }
2517
2980
 
2981
+ /* ========================================
2982
+ RADIO GROUP — variante card (.kds-radio-group--card)
2983
+ Cada opción como fila con borde (estilo selección de banco/cuenta),
2984
+ manteniendo el control radio. Hover + opción seleccionada resaltada.
2985
+ ======================================== */
2986
+ .kds-radio-group--card {
2987
+ gap: var(--kds-spacing-2);
2988
+ }
2989
+
2990
+ .kds-radio-group--card label.radio {
2991
+ gap: var(--kds-spacing-1-5);
2992
+ /* Vertical con un poco más de aire; horizontal reducido para pegar el radio a la izquierda. */
2993
+ padding: var(--kds-spacing-2) var(--kds-spacing-1-5);
2994
+ /* Bordes con los valores del bank selector. */
2995
+ border: 1px solid var(--kds-color-border-subtle);
2996
+ border-radius: var(--kds-radius-card);
2997
+ background: var(--kds-color-surface);
2998
+ transition: border-color 0.2s, background 0.2s;
2999
+ }
3000
+
3001
+ /* Leve separación extra entre el radio y el texto de la opción. */
3002
+ .kds-radio-group--card label.radio span {
3003
+ margin-left: var(--kds-spacing-0-5);
3004
+ }
3005
+
3006
+ .kds-radio-group--card label.radio:hover {
3007
+ border-color: var(--kds-color-border-default);
3008
+ }
3009
+
3010
+ /* Seleccionada: poco color primario — solo un fondo faint sutil + borde gris
3011
+ (el punto del radio ya comunica la selección). */
3012
+ .kds-radio-group--card label.radio:has(input:checked) {
3013
+ border-color: var(--kds-color-border-default);
3014
+ background: var(--kds-color-primary-faint);
3015
+ }
3016
+
2518
3017
  /* ========================================
2519
3018
  ONBOARDING COMPONENTS
2520
3019
  Multi-stage onboarding flow with form validation
@@ -3012,19 +3511,17 @@ footer > .kds-container-center {
3012
3511
  display: flex;
3013
3512
  align-items: center;
3014
3513
  gap: var(--kds-spacing-1);
3015
- padding: var(--kds-spacing-2);
3514
+ /* Padding compacto: vertical spacing-1 (8px), horizontal spacing-2 (16px). */
3515
+ padding: var(--kds-spacing-1) var(--kds-spacing-2);
3016
3516
  border-radius: var(--kds-radius-md);
3017
3517
  border: 1px solid;
3018
3518
  font-family: var(--kds-font-family-primary);
3019
3519
  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
- }
3520
+ /* Banner: ocupa siempre el ancho del contenedor. width explícito porque como
3521
+ flex item de una columna con align-items:center el cross-axis no se estira.
3522
+ Sin flex-grow para no estirarse verticalmente en contenedores flex-column. */
3523
+ width: 100%;
3524
+ flex: 0 1 auto;
3028
3525
  }
3029
3526
 
3030
3527
  .kds-alert:last-child {
@@ -3050,7 +3547,9 @@ footer > .kds-container-center {
3050
3547
  font-size: var(--kds-font-size-sm);
3051
3548
  font-weight: var(--kds-font-weight-semibold);
3052
3549
  line-height: var(--kds-line-height-normal);
3053
- margin-bottom: var(--kds-spacing-0-5);
3550
+ /* Solo margin-bottom: el padding del alert da el espaciado. Reset del resto para
3551
+ evitar el margin-block que BeerCSS aplica a <p> dentro del scope. */
3552
+ margin: 0 0 var(--kds-spacing-0-5);
3054
3553
  word-break: normal;
3055
3554
  overflow-wrap: break-word;
3056
3555
  }
@@ -3060,6 +3559,9 @@ footer > .kds-container-center {
3060
3559
  font-size: var(--kds-font-size-sm);
3061
3560
  font-weight: var(--kds-font-weight-regular);
3062
3561
  line-height: var(--kds-line-height-relaxed);
3562
+ /* Sin margen propio: el padding del alert es el único espaciado (evita el
3563
+ margin-block de <p> de BeerCSS dentro del scope). */
3564
+ margin: 0;
3063
3565
  word-break: normal;
3064
3566
  overflow-wrap: break-word;
3065
3567
  }
@@ -3105,6 +3607,32 @@ footer > .kds-container-center {
3105
3607
  color: var(--kds-color-error-dark);
3106
3608
  }
3107
3609
 
3610
+ /* Close button — icon-only, discreto, no usa kds-btn para no romper el flex layout del alert */
3611
+ .kds-alert-close {
3612
+ flex-shrink: 0;
3613
+ display: inline-flex;
3614
+ align-items: center;
3615
+ justify-content: center;
3616
+ width: 28px;
3617
+ height: 28px;
3618
+ padding: 0;
3619
+ background: transparent;
3620
+ border: 0;
3621
+ border-radius: 50%;
3622
+ color: inherit;
3623
+ cursor: pointer;
3624
+ transition: background-color 0.15s ease;
3625
+ }
3626
+
3627
+ .kds-alert-close:hover {
3628
+ background: rgba(0, 0, 0, 0.06);
3629
+ }
3630
+
3631
+ .kds-alert-close i {
3632
+ font-size: 18px;
3633
+ line-height: 1;
3634
+ }
3635
+
3108
3636
  /* Lists inside alerts */
3109
3637
  .kds-alert ul {
3110
3638
  padding-left: var(--kds-spacing-2);
@@ -4101,6 +4629,14 @@ dialog#surveyModal button.circle {
4101
4629
  text-align: center;
4102
4630
  }
4103
4631
 
4632
+ .kds-text-left {
4633
+ text-align: left;
4634
+ }
4635
+
4636
+ .kds-text-right {
4637
+ text-align: right;
4638
+ }
4639
+
4104
4640
  .kds-text-underline {
4105
4641
  text-decoration: underline;
4106
4642
  }
@@ -4113,10 +4649,62 @@ dialog#surveyModal button.circle {
4113
4649
  color: var(--on-surface-variant);
4114
4650
  }
4115
4651
 
4652
+ .kds-text-primary {
4653
+ color: var(--kds-color-primary-main);
4654
+ }
4655
+
4116
4656
  .kds-text-sm {
4117
4657
  font-size: var(--kds-font-size-sm);
4118
4658
  }
4119
4659
 
4660
+ /* ── Font-size scale utilities (additive — opt-in companions to .kds-text-sm) ── */
4661
+ .kds-text-base {
4662
+ font-size: var(--kds-font-size-base);
4663
+ }
4664
+
4665
+ .kds-text-lg {
4666
+ font-size: var(--kds-font-size-lg);
4667
+ }
4668
+
4669
+ .kds-text-xl {
4670
+ font-size: var(--kds-font-size-xl);
4671
+ }
4672
+
4673
+ .kds-text-2xl {
4674
+ font-size: var(--kds-font-size-2xl);
4675
+ }
4676
+
4677
+ /* ── Font-weight utilities ── */
4678
+ .kds-fw-regular {
4679
+ font-weight: var(--kds-font-weight-regular);
4680
+ }
4681
+
4682
+ .kds-fw-medium {
4683
+ font-weight: var(--kds-font-weight-medium);
4684
+ }
4685
+
4686
+ .kds-fw-semibold {
4687
+ font-weight: var(--kds-font-weight-semibold);
4688
+ }
4689
+
4690
+ .kds-fw-bold {
4691
+ font-weight: var(--kds-font-weight-bold);
4692
+ }
4693
+
4694
+ /* ── Text-transform utilities ── */
4695
+ .kds-text-transform-none {
4696
+ text-transform: none;
4697
+ }
4698
+
4699
+ /* ── Soft background utilities (semantic muted backgrounds for callouts) ── */
4700
+ .kds-bg-warning-soft {
4701
+ background-color: var(--kds-color-warning-soft);
4702
+ }
4703
+
4704
+ .kds-bg-info-soft {
4705
+ background-color: var(--kds-color-info-soft);
4706
+ }
4707
+
4120
4708
  /* ── Typography variants ── */
4121
4709
  .kds-text-display1 {
4122
4710
  font-family: var(--kds-font-family-primary, 'Public Sans', sans-serif);
@@ -4194,6 +4782,46 @@ dialog#surveyModal button.circle {
4194
4782
  text-transform: uppercase;
4195
4783
  }
4196
4784
 
4785
+ /* KV label — label del par clave↔valor (letra del dt de .kds-kv: "PAGO A" / "ASUNTO"). */
4786
+ .kds-text-kv-label {
4787
+ font-family: var(--kds-font-family-primary, 'Public Sans', sans-serif);
4788
+ font-size: var(--kds-font-size-xs);
4789
+ font-weight: var(--kds-font-weight-regular);
4790
+ line-height: var(--kds-line-height-relaxed);
4791
+ letter-spacing: normal;
4792
+ text-transform: uppercase;
4793
+ color: var(--kds-color-text-secondary);
4794
+ }
4795
+
4796
+ /* Value — dato/valor destacado (ej. nombre del comercio en el header del cobro). */
4797
+ .kds-text-value {
4798
+ font-family: var(--kds-font-family-primary, 'Public Sans', sans-serif);
4799
+ font-size: var(--kds-font-size-sm);
4800
+ font-weight: var(--kds-font-weight-semibold);
4801
+ line-height: var(--kds-line-height-relaxed);
4802
+ color: var(--kds-color-gray-800);
4803
+ }
4804
+
4805
+ /* Code — texto tipo código/referencia (mono, uppercase, gris), como el "CÓDIGO" del header. */
4806
+ .kds-text-code {
4807
+ font-family: var(--kds-font-family-mono);
4808
+ font-size: var(--kds-font-size-xs);
4809
+ font-weight: var(--kds-font-weight-regular);
4810
+ letter-spacing: var(--kds-letter-spacing-wider);
4811
+ font-variant-numeric: tabular-nums;
4812
+ color: var(--kds-color-gray-400);
4813
+ text-transform: uppercase;
4814
+ }
4815
+
4816
+ /* Caption — texto muy chico y liviano para pie de página (ej. número de versión). */
4817
+ .kds-text-caption {
4818
+ font-family: var(--kds-font-family-primary, 'Public Sans', sans-serif);
4819
+ font-size: var(--kds-font-size-2xs);
4820
+ font-weight: var(--kds-font-weight-regular);
4821
+ line-height: var(--kds-line-height-normal);
4822
+ color: var(--kds-color-gray-400);
4823
+ }
4824
+
4197
4825
  .kds-text-link {
4198
4826
  font-family: var(--kds-font-family-primary, 'Public Sans', sans-serif);
4199
4827
  font-size: inherit;
@@ -4207,6 +4835,12 @@ dialog#surveyModal button.circle {
4207
4835
  color: var(--kds-color-primary-dark, #5B3179);
4208
4836
  }
4209
4837
 
4838
+ /* Inline emphasis: bold weight only, inherits size/color/family from context so it
4839
+ works as a highlighted fragment inside any other variant (body, heading, etc.). */
4840
+ .kds-text-strong {
4841
+ font-weight: var(--kds-font-weight-bold, 700);
4842
+ }
4843
+
4210
4844
  /* Display utilities */
4211
4845
  .kds-hidden {
4212
4846
  display: none;
@@ -4327,7 +4961,9 @@ dialog#surveyModal button.circle {
4327
4961
  ======================================== */
4328
4962
 
4329
4963
  .kds-badge {
4330
- display: inline-block;
4964
+ display: inline-flex;
4965
+ align-items: center;
4966
+ gap: 4px;
4331
4967
  padding: 2px 8px;
4332
4968
  border-radius: var(--kds-radius-sm);
4333
4969
  font-size: var(--kds-font-size-xs);
@@ -4335,6 +4971,40 @@ dialog#surveyModal button.circle {
4335
4971
  line-height: 1.5;
4336
4972
  }
4337
4973
 
4974
+ /* Icon prefix inside chip — small inline icon */
4975
+ .kds-badge > i.material-symbols-outlined {
4976
+ font-size: 14px;
4977
+ line-height: 1;
4978
+ }
4979
+
4980
+ /* Deletable chip close button — discreto, sin kds-btn (eso lo agrandaba ridículo) */
4981
+ .kds-badge-close {
4982
+ display: inline-flex;
4983
+ align-items: center;
4984
+ justify-content: center;
4985
+ width: 16px;
4986
+ height: 16px;
4987
+ margin-right: -2px;
4988
+ padding: 0;
4989
+ background: transparent;
4990
+ border: 0;
4991
+ border-radius: 50%;
4992
+ color: inherit;
4993
+ cursor: pointer;
4994
+ opacity: 0.7;
4995
+ transition: opacity 0.15s, background-color 0.15s;
4996
+ }
4997
+
4998
+ .kds-badge-close:hover {
4999
+ opacity: 1;
5000
+ background: rgba(0, 0, 0, 0.1);
5001
+ }
5002
+
5003
+ .kds-badge-close i {
5004
+ font-size: 14px;
5005
+ line-height: 1;
5006
+ }
5007
+
4338
5008
  .kds-badge.success {
4339
5009
  background-color: var(--kds-color-success-container);
4340
5010
  color: var(--kds-color-success-dark);
@@ -4546,21 +5216,46 @@ dialog#surveyModal button.circle {
4546
5216
  flex-shrink: 0;
4547
5217
  }
4548
5218
 
4549
- /* Merchant tile with logo variant */
5219
+ /* Merchant tile with logo variant — el logo (avatar del comercio) llena TODO el tile.
5220
+ `display: block` evita el desborde del auto-grid; `overflow: hidden` recorta la imagen
5221
+ a las esquinas redondeadas del tile. La imagen va CUADRADA (sin border-radius propio):
5222
+ solo el contenedor recorta, evitando el hairline sub-pixel del doble redondeo. El fondo
5223
+ se deja transparente bajo el logo como respaldo (no debe verse púrpura en los bordes). */
4550
5224
  .kds-merchant-tile.logo {
4551
- padding: var(--kds-spacing-0-5);
5225
+ padding: 0;
5226
+ display: block;
5227
+ overflow: hidden;
5228
+ background: transparent;
4552
5229
  }
4553
5230
 
4554
5231
  .kds-merchant-tile.logo img {
4555
5232
  width: 100%;
4556
5233
  height: 100%;
4557
5234
  object-fit: cover;
4558
- border-radius: var(--kds-radius-md);
5235
+ border-radius: 0;
5236
+ display: block;
5237
+ }
5238
+
5239
+ /* Variante para logos que traen su propio marco/forma (logos de comercio/servicio,
5240
+ no fotos): `contain` + padding para que el logo entre completo sin recortar sus
5241
+ esquinas. Opt-in: pasar className="kds-merchant-tile--contain". */
5242
+ .kds-merchant-tile.logo.kds-merchant-tile--contain {
5243
+ padding: var(--kds-spacing-1);
5244
+ }
5245
+ .kds-merchant-tile.logo.kds-merchant-tile--contain img {
5246
+ object-fit: contain;
4559
5247
  }
4560
5248
 
4561
5249
  .kds-merchant-meta {
4562
5250
  display: flex;
4563
5251
  flex-direction: column;
5252
+ gap: var(--kds-spacing-0-5);
5253
+ }
5254
+
5255
+ /* El meta controla su propio espaciado: anula el margin sibling de BeerCSS
5256
+ (`… + :is(p,h*,…) { margin-block-start: 1rem }`) que separaba de más las líneas. */
5257
+ .kds-merchant-meta > * {
5258
+ margin-block: 0 !important;
4564
5259
  }
4565
5260
 
4566
5261
  .kds-merchant-label {
@@ -4613,6 +5308,11 @@ dialog#surveyModal button.circle {
4613
5308
  color: var(--kds-color-text-primary);
4614
5309
  }
4615
5310
 
5311
+ /* Opt-in color modifier — switches the value to the LigoPay/khipu-blue tone */
5312
+ .kds-amount-value--info {
5313
+ color: var(--kds-color-info-blue); /* #5A5FE0 */
5314
+ }
5315
+
4616
5316
  .kds-amount-value small {
4617
5317
  font-size: 18px;
4618
5318
  font-weight: 500;
@@ -4898,6 +5598,144 @@ dialog#surveyModal button.circle {
4898
5598
  box-shadow: 0 -4px 16px rgba(16,24,40,.08);
4899
5599
  }
4900
5600
 
5601
+ /* ========================================
5602
+ 08b - FAB (.kds-fab)
5603
+ Circular, icon-only floating action button with an animated hide state.
5604
+ Presentational: drive `.kds-fab--hidden` from useHideOnScroll for scroll-away.
5605
+ ======================================== */
5606
+
5607
+ .kds-fab {
5608
+ display: inline-flex;
5609
+ align-items: center;
5610
+ justify-content: center;
5611
+ width: var(--kds-spacing-5);
5612
+ height: var(--kds-spacing-5);
5613
+ padding: 0;
5614
+ border: none;
5615
+ border-radius: var(--kds-radius-full);
5616
+ background: var(--kds-color-background-paper);
5617
+ color: var(--kds-color-text-primary);
5618
+ box-shadow: var(--kds-shadow-elevated);
5619
+ cursor: pointer;
5620
+ z-index: 100;
5621
+ /* Neutralize native button rendering so the pressed state doesn't flash a square. */
5622
+ -webkit-appearance: none;
5623
+ appearance: none;
5624
+ -webkit-tap-highlight-color: transparent;
5625
+ transition: opacity var(--kds-transition-short) ease, transform var(--kds-transition-short) ease,
5626
+ background-color var(--kds-transition-shorter) ease;
5627
+ }
5628
+
5629
+ /* Keep the circle in EVERY interaction state. Uses `button.kds-fab` (element + class) to
5630
+ out-specify BeerCSS's base `button`/`button:active` rules — same trick as `.kds-btn`. */
5631
+ button.kds-fab,
5632
+ button.kds-fab:hover,
5633
+ button.kds-fab:active,
5634
+ button.kds-fab:focus,
5635
+ button.kds-fab:focus-visible {
5636
+ border-radius: var(--kds-radius-full);
5637
+ background: var(--kds-color-background-paper);
5638
+ }
5639
+
5640
+ button.kds-fab:hover:not(:disabled) {
5641
+ background: var(--kds-color-background-muted);
5642
+ }
5643
+
5644
+ /* Subtle press feedback that preserves the round shape. */
5645
+ button.kds-fab:active:not(:disabled) {
5646
+ transform: translateY(var(--kds-spacing-0-25));
5647
+ }
5648
+
5649
+ button.kds-fab:focus-visible {
5650
+ outline: var(--kds-spacing-0-25) solid var(--kds-color-primary-main);
5651
+ outline-offset: var(--kds-spacing-0-25);
5652
+ }
5653
+
5654
+ .kds-fab > i.material-symbols-outlined {
5655
+ font-size: var(--kds-spacing-button-icon-size);
5656
+ line-height: 1;
5657
+ }
5658
+
5659
+ /* Pinned corners — fixed to the viewport (stays visible on scroll-up, iframe-friendly).
5660
+ Horizontally the top-right FAB hugs the right edge of the centered content column set via
5661
+ `--kds-fab-column-width` (default 100% → viewport edge). The consumer sets that var to its
5662
+ widget width so on desktop the FAB overlaps the header's top-right corner instead of drifting
5663
+ to the far viewport edge. `max(...)` keeps at least a token inset on narrow (mobile) viewports. */
5664
+ .kds-fab--top-right,
5665
+ .kds-fab--top-left,
5666
+ .kds-fab--bottom-right,
5667
+ .kds-fab--bottom-left {
5668
+ position: fixed;
5669
+ }
5670
+
5671
+ .kds-fab--top-right {
5672
+ top: var(--kds-spacing-2);
5673
+ right: max(var(--kds-spacing-2), calc((100% - var(--kds-fab-column-width, 100%)) / 2 + var(--kds-spacing-2)));
5674
+ }
5675
+
5676
+ .kds-fab--top-left {
5677
+ top: var(--kds-spacing-2);
5678
+ left: max(var(--kds-spacing-2), calc((100% - var(--kds-fab-column-width, 100%)) / 2 + var(--kds-spacing-2)));
5679
+ }
5680
+
5681
+ .kds-fab--bottom-right {
5682
+ bottom: var(--kds-spacing-2);
5683
+ right: max(var(--kds-spacing-2), calc((100% - var(--kds-fab-column-width, 100%)) / 2 + var(--kds-spacing-2)));
5684
+ }
5685
+
5686
+ .kds-fab--bottom-left {
5687
+ bottom: var(--kds-spacing-2);
5688
+ left: max(var(--kds-spacing-2), calc((100% - var(--kds-fab-column-width, 100%)) / 2 + var(--kds-spacing-2)));
5689
+ }
5690
+
5691
+ /* Desktop: nudge the top-right FAB up so it sits partially over the header's top edge. */
5692
+ @media (min-width: 768px) {
5693
+ .kds-fab--top-right {
5694
+ top: var(--kds-spacing-1);
5695
+ }
5696
+ }
5697
+
5698
+ /* Corner-overlap: mount the FAB centered OVER the top-right corner of the content column
5699
+ (`--kds-fab-column-width`), overlapping outward — vs `--top-right` which insets inside the
5700
+ column. Mobile keeps a small inset from the viewport via the `--kds-fab-inset` token. */
5701
+ .kds-fab--corner {
5702
+ --kds-fab-inset: 5px;
5703
+ position: fixed;
5704
+ top: var(--kds-fab-inset);
5705
+ right: var(--kds-fab-inset);
5706
+ }
5707
+
5708
+ @media (min-width: 768px) {
5709
+ .kds-fab--corner {
5710
+ right: max(
5711
+ var(--kds-fab-inset),
5712
+ calc((100% - var(--kds-fab-column-width, 100%)) / 2 - var(--kds-spacing-5) / 2)
5713
+ );
5714
+ }
5715
+ }
5716
+
5717
+ /* Hidden (scroll-away) state — fade + slide toward the nearest edge */
5718
+ .kds-fab--hidden {
5719
+ opacity: 0;
5720
+ transform: translateY(calc(-1 * var(--kds-spacing-1))) scale(0.9);
5721
+ pointer-events: none;
5722
+ }
5723
+
5724
+ .kds-fab--bottom-right.kds-fab--hidden,
5725
+ .kds-fab--bottom-left.kds-fab--hidden {
5726
+ transform: translateY(var(--kds-spacing-1)) scale(0.9);
5727
+ }
5728
+
5729
+ @media (prefers-reduced-motion: reduce) {
5730
+ .kds-fab {
5731
+ transition: opacity var(--kds-transition-short) ease;
5732
+ }
5733
+
5734
+ .kds-fab--hidden {
5735
+ transform: none;
5736
+ }
5737
+ }
5738
+
4901
5739
  /* ========================================
4902
5740
  09 - SUCCESS MARK (.kds-success-mark)
4903
5741
  Large success icon for completion screens
@@ -4977,22 +5815,22 @@ dialog#surveyModal button.circle {
4977
5815
  padding: var(--kds-spacing-1) 0;
4978
5816
  }
4979
5817
 
4980
- /* Desktop: footer inside card hidden; Mobile: footer outside card hidden */
5818
+ /* Footer SIEMPRE fuera del card body (desktop y mobile): el footer interno
5819
+ (variant="inside") queda oculto en todos los breakpoints; se muestra el
5820
+ externo, hermano directo del card bajo `.kds-screen`. */
4981
5821
  .kds-card-elevated > .kds-secure-footer {
4982
5822
  display: none;
4983
5823
  }
4984
5824
 
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
- }
5825
+ .kds-screen > form {
5826
+ margin-block-start: 0 !important;
5827
+ }
4992
5828
 
4993
- .kds-screen > form {
4994
- margin-block-start: 0 !important;
4995
- }
5829
+ /* Footer externo adyacente a la card: cancela el gap del .kds-screen para que
5830
+ quede pegado debajo de la card y no flotando lejos. Las dos cards
5831
+ (header/body) sí conservan su separación. */
5832
+ .kds-screen > .kds-secure-footer {
5833
+ margin-top: calc(-1 * var(--kds-spacing-3));
4996
5834
  }
4997
5835
 
4998
5836
  .kds-secure-footer-lock {
@@ -5014,6 +5852,32 @@ dialog#surveyModal button.circle {
5014
5852
  display: block;
5015
5853
  object-fit: contain;
5016
5854
  overflow: visible;
5855
+ /* Corrección óptica: la masa visible del wordmark (el cuerpo de "Khipu") queda sobre el
5856
+ centro geométrico de su viewBox (45x15) porque el descendente de la "p" ocupa la parte
5857
+ baja. Al centrar por caja (align-items:center) el logo se ve ~1px alto respecto al texto,
5858
+ así que se baja 0.75px para alinear el centro óptico. Alto fijo (11px) → nudge consistente. */
5859
+ position: relative;
5860
+ top: 0.75px;
5861
+ }
5862
+
5863
+ /* Divisor entre el logo de Khipu y el del PSP ("Khipu | klap") */
5864
+ .kds-secure-footer-sep {
5865
+ width: 1px;
5866
+ height: 0.85em;
5867
+ background: currentColor;
5868
+ opacity: 0.35;
5869
+ flex: 0 0 auto;
5870
+ }
5871
+
5872
+ /* Logo del PSP (proveedor de pagos): lo provee el consumidor, lo alineamos al mismo
5873
+ alto que el mark de Khipu. Sirve para <img> o un elemento con .kds-psp-mark. */
5874
+ .kds-secure-footer img,
5875
+ .kds-secure-footer .kds-psp-mark {
5876
+ height: 11px;
5877
+ width: auto;
5878
+ flex: 0 0 auto;
5879
+ display: block;
5880
+ object-fit: contain;
5017
5881
  }
5018
5882
 
5019
5883
  .kds-secure-footer-code {
@@ -5029,26 +5893,45 @@ dialog#surveyModal button.circle {
5029
5893
  background: none;
5030
5894
  border: 0;
5031
5895
  padding: 0;
5896
+ /* Reset del bare `button { height: 2.5rem }` de BeerCSS (scoped en .kds-theme-root):
5897
+ sin esto el toggle quedaba en una caja de 40px con el texto centrado → espacio
5898
+ irregular arriba (pegado al resumen) y abajo (~26px hasta el panel). Con height
5899
+ auto el botón ajusta a su contenido y el ritmo lo gobiernan los márgenes. */
5900
+ height: auto;
5032
5901
  color: var(--kds-color-primary-main);
5033
5902
  font-weight: var(--kds-font-weight-medium);
5034
5903
  font-size: var(--kds-font-size-sm);
5035
- padding-bottom: var(--kds-spacing-1-25);
5036
5904
  cursor: pointer;
5037
5905
  display: inline-flex;
5038
5906
  align-items: center;
5039
5907
  gap: 0;
5040
5908
  }
5041
5909
 
5042
- .kds-expand-toggle:hover {
5910
+ .kds-expand-toggle:hover,
5911
+ .kds-expand-toggle:focus,
5912
+ .kds-expand-toggle:active {
5043
5913
  background: transparent;
5914
+ outline: none;
5915
+ box-shadow: none;
5044
5916
  }
5045
5917
 
5046
- .kds-expand-toggle:hover span {
5918
+ .kds-expand-toggle:hover span,
5919
+ .kds-expand-toggle:focus span,
5920
+ .kds-expand-toggle:active span {
5047
5921
  text-decoration: underline;
5048
5922
  }
5049
5923
 
5050
5924
  .kds-expand-toggle i {
5051
5925
  font-size: var(--kds-font-size-base);
5926
+ /* BeerCSS estiliza el `i` bare como caja flex de 30px (height/width/min: 30px), que
5927
+ inflaba el toggle y dejaba aire vertical de más alrededor del label. La achicamos a
5928
+ 1.25em MANTENIENDO el centrado flex de BeerCSS (display:flex/align/justify) — así el
5929
+ caret queda compacto Y ópticamente centrado con el texto (no usar height:auto ni
5930
+ line-height:1: descentran el glifo). */
5931
+ width: 1.25em;
5932
+ height: 1.25em;
5933
+ min-width: 0;
5934
+ min-height: 0;
5052
5935
  transition: transform var(--kds-transition-shorter, 0.2s);
5053
5936
  }
5054
5937
 
@@ -5068,7 +5951,10 @@ dialog#surveyModal button.circle {
5068
5951
  }
5069
5952
 
5070
5953
  .kds-expand-panel.open {
5071
- max-height: 800px;
5954
+ /* Fallback cap for no-JS consumers. With JS (React useExpandToggle / vanilla
5955
+ initExpandToggle) the panel's inline max-height is set to its exact scrollHeight,
5956
+ which overrides this and prevents clipping regardless of content length. */
5957
+ max-height: 2000px;
5072
5958
  margin-top: 0;
5073
5959
  }
5074
5960
 
@@ -5186,13 +6072,17 @@ dialog#surveyModal button.circle {
5186
6072
 
5187
6073
  .kds-status-block {
5188
6074
  text-align: center;
5189
- padding: var(--kds-spacing-6) 0;
6075
+ /* Padding vertical estándar: spacing-2 (16px), el mismo rhythm inter-elementos
6076
+ que usan kds-btn-stack y kds-hr-dashed. NO usar spacing-6 (48px): genera
6077
+ demasiado espacio cuando el bloque se compone con header/divider alrededor. */
6078
+ padding: var(--kds-spacing-2) 0;
5190
6079
  }
5191
6080
 
5192
6081
  .kds-status-block-icon {
5193
6082
  width: var(--kds-status-icon-size);
5194
6083
  height: var(--kds-status-icon-size);
5195
- margin: 0 auto var(--kds-spacing-3);
6084
+ /* spacing-1 (8px) icono→título: elementos relacionados dentro del bloque. */
6085
+ margin: 0 auto var(--kds-spacing-1);
5196
6086
  border-radius: var(--kds-radius-full);
5197
6087
  display: grid;
5198
6088
  place-items: center;
@@ -5205,15 +6095,21 @@ dialog#surveyModal button.circle {
5205
6095
 
5206
6096
  .kds-status-block-title {
5207
6097
  font-size: var(--kds-font-size-xl);
5208
- font-weight: var(--kds-font-weight-bold);
6098
+ font-weight: var(--kds-font-weight-semibold);
5209
6099
  color: var(--kds-color-text-primary);
5210
6100
  margin: 0 0 var(--kds-spacing-1);
6101
+ /* El reset global :where(h2) usa estas vars con !important; así se overridea. */
6102
+ --kds-typography-h2-margin-block-end: var(--kds-spacing-1);
5211
6103
  }
5212
6104
 
5213
6105
  .kds-status-block-description {
5214
6106
  font-size: var(--kds-font-size-sm);
5215
6107
  color: var(--kds-color-text-secondary);
5216
6108
  margin: 0;
6109
+ /* !important: anula el auto-espaciado de hermanos de BeerCSS
6110
+ (margin-block-start: 1rem, especificidad 0,5,1 imposible de igualar).
6111
+ La separación título→descripción la da el margin-bottom del título. */
6112
+ margin-top: 0 !important;
5217
6113
  }
5218
6114
 
5219
6115
  /* Variant: success */
@@ -5223,11 +6119,11 @@ dialog#surveyModal button.circle {
5223
6119
  color: var(--kds-color-success-main);
5224
6120
  }
5225
6121
 
5226
- /* Variant: pending (animated spinner) */
6122
+ /* Variant: pending (animated blue spinner, sin icono interno) */
5227
6123
  .kds-status-block[data-status="pending"] .kds-status-block-icon {
5228
6124
  background: transparent;
5229
6125
  border: 3px solid var(--kds-color-divider);
5230
- border-top-color: var(--kds-color-primary-main);
6126
+ border-top-color: var(--kds-color-info-main);
5231
6127
  animation: kds-spin 1s linear infinite;
5232
6128
  }
5233
6129
 
@@ -5245,12 +6141,21 @@ dialog#surveyModal button.circle {
5245
6141
  color: var(--kds-color-error-main);
5246
6142
  }
5247
6143
 
6144
+ /* Variant: info — usar SIEMPRE con icon "info_i" (i minúscula sin glyph circular)
6145
+ para que solo se vea nuestro círculo decorativo. Iconos como "info" tienen
6146
+ su propio círculo built-in → causarían doble círculo. */
6147
+ .kds-status-block[data-status="info"] .kds-status-block-icon {
6148
+ background: var(--kds-color-info-soft);
6149
+ border: var(--kds-border-width-md) solid var(--kds-color-info-main);
6150
+ color: var(--kds-color-info-main);
6151
+ }
6152
+
5248
6153
  /* Inline variant: icon left + text right */
5249
6154
  .kds-status-block.inline {
5250
6155
  text-align: left;
5251
6156
  display: flex;
5252
6157
  align-items: center;
5253
- gap: var(--kds-spacing-3);
6158
+ gap: var(--kds-spacing-2);
5254
6159
  padding: var(--kds-spacing-1) 0;
5255
6160
  }
5256
6161
 
@@ -5292,6 +6197,9 @@ dialog#surveyModal button.circle {
5292
6197
  .kds-bottom-sheet {
5293
6198
  width: 100%;
5294
6199
  max-width: var(--kds-bottom-sheet-max-width);
6200
+ max-height: 90dvh;
6201
+ display: flex;
6202
+ flex-direction: column;
5295
6203
  background: var(--kds-color-background-paper);
5296
6204
  border-radius: var(--kds-radius-2xl) var(--kds-radius-2xl) 0 0;
5297
6205
  padding: var(--kds-spacing-3-5) var(--kds-spacing-3) var(--kds-spacing-3);
@@ -5312,6 +6220,42 @@ dialog#surveyModal button.circle {
5312
6220
  background: var(--kds-color-divider);
5313
6221
  }
5314
6222
 
6223
+ /* Close button — top-right discreto, NO usa kds-btn (rompía el flex layout) */
6224
+ .kds-bottom-sheet-close {
6225
+ position: absolute;
6226
+ top: var(--kds-spacing-1);
6227
+ right: var(--kds-spacing-1);
6228
+ width: 32px;
6229
+ height: 32px;
6230
+ display: inline-flex;
6231
+ align-items: center;
6232
+ justify-content: center;
6233
+ padding: 0;
6234
+ background: transparent;
6235
+ border: 0;
6236
+ border-radius: 50%;
6237
+ color: var(--kds-color-text-secondary);
6238
+ cursor: pointer;
6239
+ transition: background-color 0.15s ease;
6240
+ }
6241
+
6242
+ .kds-bottom-sheet-close:hover {
6243
+ background: rgba(0, 0, 0, 0.06);
6244
+ }
6245
+
6246
+ .kds-bottom-sheet-close i {
6247
+ font-size: 20px;
6248
+ line-height: 1;
6249
+ }
6250
+
6251
+ /* Body container — separa el content del title/actions */
6252
+ .kds-bottom-sheet-body {
6253
+ text-align: left;
6254
+ margin-top: var(--kds-spacing-1);
6255
+ overflow-y: auto;
6256
+ min-height: 0;
6257
+ }
6258
+
5315
6259
  .kds-bottom-sheet-icon {
5316
6260
  font-size: var(--kds-font-size-4xl);
5317
6261
  }
@@ -5358,6 +6302,19 @@ dialog#surveyModal button.circle {
5358
6302
  to { opacity: 1; }
5359
6303
  }
5360
6304
 
6305
+ /* Botón con llenado progresivo (feedback visual de auto-cierre).
6306
+ Duración parametrizable con --kds-fill-duration (default 12s). */
6307
+ .kds-btn-fill-progress {
6308
+ background-image: linear-gradient(to right, var(--kds-color-primary-dark), var(--kds-color-primary-dark));
6309
+ background-repeat: no-repeat;
6310
+ background-size: 0% 100%;
6311
+ animation: kds-btn-fill var(--kds-fill-duration, 12s) linear forwards;
6312
+ }
6313
+
6314
+ @keyframes kds-btn-fill {
6315
+ to { background-size: 100% 100%; }
6316
+ }
6317
+
5361
6318
  @keyframes kds-rise {
5362
6319
  from { transform: translateY(var(--kds-spacing-2-5)); opacity: 0.4; }
5363
6320
  to { transform: none; opacity: 1; }
@@ -5545,6 +6502,30 @@ dialog#surveyModal button.circle {
5545
6502
  /* No max-height — clip-path on parent handles visual collapse without layout changes */
5546
6503
  .kds-invoice-sticky .kds-invoice-collapsible {
5547
6504
  opacity: calc(1 - var(--collapse-progress) * 1.5);
6505
+
6506
+ /* Scroll interno discreto (mobile): cuando el detalle abierto excede el viewport, el
6507
+ collapsible scrollea internamente (monto/merchant quedan fijos arriba) para poder leer
6508
+ todo el detalle. Al tocar el fondo, `overscroll-behavior: auto` ENCADENA el gesto al
6509
+ scroll del body → dispara useStickyInvoiceCollapse → colapsa el header y aparece el form
6510
+ (flujo natural). Se auto-desactiva con el detalle cerrado (contenido más corto que el
6511
+ max-height → sin scroll ni efecto visual). */
6512
+ max-height: calc(100vh - var(--kds-spacing-12) - var(--kds-spacing-4));
6513
+ max-height: calc(100dvh - var(--kds-spacing-12) - var(--kds-spacing-4));
6514
+ overflow-y: auto;
6515
+ overflow-x: hidden;
6516
+ overscroll-behavior-y: auto;
6517
+ scrollbar-width: thin;
6518
+ scrollbar-color: var(--kds-color-gray-300) transparent;
6519
+ }
6520
+ .kds-invoice-sticky .kds-invoice-collapsible::-webkit-scrollbar {
6521
+ width: 4px;
6522
+ }
6523
+ .kds-invoice-sticky .kds-invoice-collapsible::-webkit-scrollbar-thumb {
6524
+ background: var(--kds-color-gray-300);
6525
+ border-radius: var(--kds-radius-full);
6526
+ }
6527
+ .kds-invoice-sticky .kds-invoice-collapsible::-webkit-scrollbar-track {
6528
+ background: transparent;
5548
6529
  }
5549
6530
 
5550
6531
  /* Amount: 30px → 24px (progressive) */
@@ -5616,6 +6597,14 @@ dialog#surveyModal button.circle {
5616
6597
  margin-top: var(--kds-spacing-1);
5617
6598
  }
5618
6599
 
6600
+ /* Ritmo uniforme alrededor del toggle: spacing-2 arriba (resumen↔"Ver detalle")
6601
+ iguala el padding-top spacing-2 que la .kds-detail-list da debajo
6602
+ ("Ver detalle"↔primer label del panel). Antes el alto fijo del bare button
6603
+ (40px) hacía que el espacio de arriba y de abajo fueran distintos. */
6604
+ .kds-invoice-collapsible > .kds-expand-toggle {
6605
+ margin-top: var(--kds-spacing-2);
6606
+ }
6607
+
5619
6608
  /* -- Invoice Summary -- */
5620
6609
  .kds-invoice-summary {
5621
6610
  display: flex;
@@ -5639,6 +6628,22 @@ div.kds-invoice-header,
5639
6628
  padding: 0;
5640
6629
  border-radius: 0;
5641
6630
  align-content: initial;
6631
+ /* Override BeerCSS `header { min-height: ... }` — el header debe ajustarse a su
6632
+ contenido (sigue el alto del merchant tile: 64px→50px al colapsar). Sin esto
6633
+ quedaba un min-height fijo de 64px → hueco muerto debajo al colapsar en mobile. */
6634
+ min-height: 0;
6635
+ }
6636
+
6637
+ /* Mobile: center-align el header (merchant tile vs bloque monto+código) tanto
6638
+ expandido como colapsado. Al quitar el logo del header, el bloque de texto quedaba
6639
+ más bajo que el merchant tile y con `flex-start` dejaba hueco abajo; centrado los
6640
+ alinea. CSS-only (no depende de la clase .is-collapsed, que el JS de collapse no setea). */
6641
+ @media (max-width: 767px) {
6642
+ header.kds-invoice-header,
6643
+ div.kds-invoice-header,
6644
+ .kds-invoice-header {
6645
+ align-items: center;
6646
+ }
5642
6647
  }
5643
6648
 
5644
6649
  /* Reset BeerCSS sibling margin on <p> inside invoice header — specificity (0,4,1) ties BeerCSS, wins by source order */
@@ -5648,7 +6653,7 @@ div.kds-invoice-header,
5648
6653
  }
5649
6654
 
5650
6655
  .kds-invoice-amount {
5651
- font-weight: var(--kds-font-weight-bold);
6656
+ font-weight: var(--kds-font-weight-semibold);
5652
6657
  font-size: var(--kds-font-size-3xl);
5653
6658
  line-height: var(--kds-line-height-tight);
5654
6659
  letter-spacing: var(--kds-letter-spacing-wide);
@@ -5670,12 +6675,16 @@ div.kds-invoice-header,
5670
6675
  text-transform: uppercase;
5671
6676
  margin: 0;
5672
6677
  margin-block: unset;
5673
- margin: var(--kds-spacing-1) 0;
6678
+ display: flex;
6679
+ gap: var(--kds-spacing-1-5);
5674
6680
  }
5675
6681
 
5676
6682
  .kds-invoice-code-value {
6683
+ color: var(--kds-color-gray-400);
6684
+ }
6685
+
6686
+ .kds-invoice-code-value--lowercase {
5677
6687
  text-transform: lowercase;
5678
- color: var(--kds-text-primary);
5679
6688
  }
5680
6689
 
5681
6690
  .kds-invoice-merchant {
@@ -5687,6 +6696,9 @@ div.kds-invoice-header,
5687
6696
  place-items: center;
5688
6697
  color: #fff;
5689
6698
  flex-shrink: 0;
6699
+ /* Clip the slightly-oversized logo (see `.kds-invoice-merchant img`) to the
6700
+ rounded square so it can fully cover the deep-purple background. */
6701
+ overflow: hidden;
5690
6702
  }
5691
6703
 
5692
6704
  .kds-invoice-merchant i {
@@ -5706,27 +6718,43 @@ div.kds-invoice-header,
5706
6718
  }
5707
6719
 
5708
6720
  .kds-invoice-merchant img {
5709
- width: 100%;
5710
- height: 100%;
6721
+ /* Slightly larger than the container (+2px, centered by the grid) so the
6722
+ logo overflows under the parent's rounded clip and the deep-purple
6723
+ background never peeks through at the corners when the logo shrinks. */
6724
+ width: calc(100% + 2px);
6725
+ height: calc(100% + 2px);
5711
6726
  object-fit: cover;
5712
6727
  border-radius: var(--kds-radius-md);
5713
6728
  }
5714
6729
 
5715
6730
  /* -- Card Title -- */
5716
6731
  .kds-card-title {
5717
- font-weight: 700;
6732
+ font-weight: var(--kds-font-weight-semibold);
5718
6733
  font-size: var(--kds-font-size-base);
5719
6734
  line-height: 1.5;
5720
6735
  letter-spacing: -0.31px;
5721
6736
  color: var(--kds-color-text-primary);
5722
- margin-bottom: var(--kds-spacing-2) !important;
6737
+ /* sin !important: no hay regla de margin-bottom para h1 en BeerCSS que competir, y así un
6738
+ consumidor puede anularlo (p.ej. FormTitle dentro de un Stack que ya da el espacio). */
6739
+ margin-bottom: var(--kds-spacing-2);
6740
+ }
6741
+
6742
+ /* Opt-in size modifiers (additive — do NOT change default 16/600) */
6743
+ .kds-card-title--lg {
6744
+ font-size: var(--kds-font-size-xl); /* 20px */
6745
+ font-weight: var(--kds-font-weight-semibold);
6746
+ }
6747
+
6748
+ .kds-card-title--xl {
6749
+ font-size: var(--kds-font-size-2xl); /* 24px */
6750
+ font-weight: var(--kds-font-weight-semibold);
5723
6751
  }
5724
6752
 
5725
6753
  /* -- Key-Value Grid -- */
5726
6754
  .kds-kv {
5727
6755
  display: grid;
5728
6756
  grid-template-columns: auto 1fr;
5729
- column-gap: var(--kds-spacing-1-5);
6757
+ column-gap: var(--kds-spacing-1);
5730
6758
  align-items: baseline;
5731
6759
  }
5732
6760
 
@@ -5743,7 +6771,7 @@ div.kds-invoice-header,
5743
6771
  margin: 0;
5744
6772
  font-size: var(--kds-font-size-sm);
5745
6773
  line-height: var(--kds-line-height-relaxed);
5746
- font-weight: var(--kds-font-weight-bold);
6774
+ font-weight: var(--kds-font-weight-semibold);
5747
6775
  letter-spacing: normal;
5748
6776
  color: var(--kds-color-gray-800);
5749
6777
  min-width: 0;
@@ -5752,6 +6780,20 @@ div.kds-invoice-header,
5752
6780
  text-overflow: ellipsis;
5753
6781
  }
5754
6782
 
6783
+ /* -- Summary panel --
6784
+ Recuadro de datos del pago en pantallas de resultado: contenido en columna
6785
+ (gap spacing-2, típicamente pares kv) dentro de un borde sutil. */
6786
+ .kds-summary-panel {
6787
+ display: flex;
6788
+ flex-direction: column;
6789
+ gap: var(--kds-spacing-2);
6790
+ width: 100%;
6791
+ padding: var(--kds-spacing-2);
6792
+ border: var(--kds-border-width-sm) solid var(--kds-color-border);
6793
+ border-radius: var(--kds-radius-card);
6794
+ text-align: left;
6795
+ }
6796
+
5755
6797
  /* -- Dividers -- */
5756
6798
  .kds-hr {
5757
6799
  height: 1px;
@@ -5771,16 +6813,28 @@ div.kds-invoice-header,
5771
6813
  .kds-detail-list {
5772
6814
  display: flex;
5773
6815
  flex-direction: column;
5774
- gap: var(--kds-spacing-1-5);
6816
+ /* spacing-2 entre grupos del detalle (separación estándar entre piezas de info
6817
+ distintas, ej. "Emisor cobro" vs "Descripción"). El padding-top da la misma
6818
+ separación spacing-2 arriba del primer grupo (Asunto↔Emisor cobro). */
6819
+ gap: var(--kds-spacing-2);
5775
6820
  margin: 0;
5776
- padding: 0 0 var(--kds-spacing-2);
6821
+ padding: var(--kds-spacing-2) 0 0;
5777
6822
  }
5778
6823
 
5779
- .kds-detail-group {
5780
- margin-bottom: var(--kds-spacing-1-5);
6824
+ @media (max-width: 767px) {
6825
+ .kds-detail-list {
6826
+ padding-bottom: var(--kds-spacing-2);
6827
+ }
5781
6828
  }
5782
6829
 
5783
- .kds-detail-group:last-child {
6830
+ .kds-detail-group {
6831
+ /* El espacio ENTRE grupos lo da el `gap` de .kds-detail-list (spacing-2).
6832
+ Dentro del grupo, label↔value llevan un gap chico (spacing-0-5) para un
6833
+ ritmo parejo: antes el dt iba pegado al dd (0px) y se veía desigual frente
6834
+ a los 16px entre los demás elementos del panel. */
6835
+ display: flex;
6836
+ flex-direction: column;
6837
+ gap: var(--kds-spacing-0-5);
5784
6838
  margin-bottom: 0;
5785
6839
  }
5786
6840
 
@@ -5789,6 +6843,7 @@ div.kds-invoice-header,
5789
6843
  letter-spacing: var(--kds-letter-spacing-wide);
5790
6844
  color: var(--kds-color-text-hint);
5791
6845
  text-transform: uppercase;
6846
+ margin: 0;
5792
6847
  }
5793
6848
 
5794
6849
  .kds-detail-group dd {
@@ -5798,6 +6853,19 @@ div.kds-invoice-header,
5798
6853
  color: var(--kds-color-text-primary);
5799
6854
  }
5800
6855
 
6856
+ /* Imagen adjunta del cobro dentro del panel de detalle (ej. comprobante/producto).
6857
+ Reemplaza los inline styles del antiguo modal MerchantInfo: ancho contenido, borde
6858
+ y radio del DS. Va como un grupo más de la .kds-detail-list (hereda su gap). */
6859
+ .kds-detail-image {
6860
+ display: block;
6861
+ max-width: 100%;
6862
+ width: 160px;
6863
+ height: auto;
6864
+ object-fit: contain;
6865
+ border-radius: var(--kds-radius-md);
6866
+ border: 1px solid var(--kds-color-border);
6867
+ }
6868
+
5801
6869
  /* -- Amount Summary Row -- */
5802
6870
  .kds-amount-row {
5803
6871
  display: flex;
@@ -5857,16 +6925,16 @@ div.kds-invoice-header,
5857
6925
  font-size: var(--kds-font-size-sm);
5858
6926
  }
5859
6927
 
5860
- .kds-recap-list .k {
6928
+ .kds-recap-list .kds-key {
5861
6929
  color: var(--kds-color-text-secondary);
5862
6930
  }
5863
6931
 
5864
- .kds-recap-list .v {
6932
+ .kds-recap-list .kds-value {
5865
6933
  color: var(--kds-color-text-primary);
5866
6934
  font-weight: 500;
5867
6935
  }
5868
6936
 
5869
- .kds-recap-list .v.placeholder {
6937
+ .kds-recap-list .kds-value.placeholder {
5870
6938
  color: var(--kds-color-text-hint);
5871
6939
  }
5872
6940
 
@@ -5893,6 +6961,72 @@ a.kds-btn-success:disabled {
5893
6961
  box-shadow: none;
5894
6962
  }
5895
6963
 
6964
+ /* -- Button Warning (amber CTA) -- */
6965
+ button.kds-btn-warning,
6966
+ a.kds-btn-warning {
6967
+ background: var(--kds-color-warning-main);
6968
+ color: var(--kds-color-warning-contrast);
6969
+ border: none;
6970
+ }
6971
+
6972
+ button.kds-btn-warning:hover:not(:disabled),
6973
+ a.kds-btn-warning:hover:not(:disabled) {
6974
+ background: var(--kds-color-warning-dark);
6975
+ }
6976
+
6977
+ button.kds-btn-warning:active:not(:disabled),
6978
+ button.kds-btn-warning:focus-visible:not(:disabled),
6979
+ a.kds-btn-warning:active:not(:disabled),
6980
+ a.kds-btn-warning:focus-visible:not(:disabled) {
6981
+ background: var(--kds-color-warning-dark);
6982
+ outline: 2px solid var(--kds-color-warning-main);
6983
+ outline-offset: 2px;
6984
+ }
6985
+
6986
+ button.kds-btn-warning:disabled,
6987
+ a.kds-btn-warning:disabled {
6988
+ background-color: var(--kds-color-action-disabled-bg);
6989
+ color: var(--kds-color-action-disabled);
6990
+ box-shadow: none;
6991
+ }
6992
+
6993
+ button.kds-btn-warning::after {
6994
+ display: none;
6995
+ }
6996
+
6997
+ /* -- Button Error (red / destructive CTA) -- */
6998
+ button.kds-btn-error,
6999
+ a.kds-btn-error {
7000
+ background: var(--kds-color-error-main);
7001
+ color: var(--kds-color-error-contrast);
7002
+ border: none;
7003
+ }
7004
+
7005
+ button.kds-btn-error:hover:not(:disabled),
7006
+ a.kds-btn-error:hover:not(:disabled) {
7007
+ background: var(--kds-color-error-dark);
7008
+ }
7009
+
7010
+ button.kds-btn-error:active:not(:disabled),
7011
+ button.kds-btn-error:focus-visible:not(:disabled),
7012
+ a.kds-btn-error:active:not(:disabled),
7013
+ a.kds-btn-error:focus-visible:not(:disabled) {
7014
+ background: var(--kds-color-error-dark);
7015
+ outline: 2px solid var(--kds-color-error-main);
7016
+ outline-offset: 2px;
7017
+ }
7018
+
7019
+ button.kds-btn-error:disabled,
7020
+ a.kds-btn-error:disabled {
7021
+ background-color: var(--kds-color-action-disabled-bg);
7022
+ color: var(--kds-color-action-disabled);
7023
+ box-shadow: none;
7024
+ }
7025
+
7026
+ button.kds-btn-error::after {
7027
+ display: none;
7028
+ }
7029
+
5896
7030
  /* -- Field Helper Text -- */
5897
7031
  .kds-field-helper {
5898
7032
  font-size: var(--kds-font-size-xs);
@@ -5935,6 +7069,8 @@ button.kds-btn-success::after {
5935
7069
  /* -- Alert inline (borderless, used inside cards) -- */
5936
7070
  .kds-alert-inline {
5937
7071
  border: none;
7072
+ padding: var(--kds-spacing-1) var(--kds-spacing-1-5);
7073
+ margin-bottom: var(--kds-spacing-1-5);
5938
7074
  }
5939
7075
 
5940
7076
  /* -- Card dimmed (behind overlays) -- */
@@ -6003,6 +7139,25 @@ button.kds-btn-success::after {
6003
7139
  fill: none;
6004
7140
  }
6005
7141
 
7142
+ /* BeerCSS fuerza fill:currentcolor en todo svg, pisando el atributo de
7143
+ presentación; un SVG que declara fill="none" (ilustraciones de trazo)
7144
+ debe conservarlo. */
7145
+ svg[fill='none'] {
7146
+ fill: none;
7147
+ }
7148
+
7149
+ /* -- Bottom sheet centered content --
7150
+ Contenido escueto centrado verticalmente con altura mínima estable, para que
7151
+ el sheet no encoja cuando la vista tiene pocos elementos (p.ej. drawer de
7152
+ autorización: título + ilustración + timer). */
7153
+ .kds-bottom-sheet-centered-content {
7154
+ display: flex;
7155
+ flex-direction: column;
7156
+ align-items: center;
7157
+ justify-content: center;
7158
+ min-height: var(--kds-bottom-sheet-content-min-height, 21rem);
7159
+ }
7160
+
6006
7161
  /* -- Bottom sheet description (subtle / smaller) -- */
6007
7162
  .kds-bottom-sheet-description-subtle {
6008
7163
  color: var(--kds-color-text-secondary);
@@ -6036,17 +7191,42 @@ button.kds-btn-success::after {
6036
7191
  }
6037
7192
 
6038
7193
  .kds-bill-description-picture {
6039
- width: 120px;
6040
- height: 120px;
7194
+ max-width: 100px;
6041
7195
  object-fit: contain;
6042
7196
  background: var(--kds-color-background-paper);
6043
- padding: var(--kds-spacing-1-25);
6044
- border-radius: var(--kds-radius-md);
6045
- margin-top: var(--kds-spacing-1);
7197
+ margin-top: var(--kds-spacing-1-25);
6046
7198
  display: block;
6047
7199
  box-sizing: content-box;
6048
7200
  }
6049
7201
 
7202
+ /* -- Bill Attachments -- */
7203
+ .kds-bill-attachments {
7204
+ display: flex;
7205
+ flex-direction: column;
7206
+ gap: var(--kds-spacing-0-75);
7207
+ }
7208
+
7209
+ .kds-bill-attachment {
7210
+ display: inline-flex;
7211
+ align-items: center;
7212
+ justify-content: flex-start;
7213
+ width: fit-content;
7214
+ color: var(--kds-color-info-main);
7215
+ font-size: var(--kds-font-size-sm);
7216
+ font-weight: var(--kds-font-weight-medium);
7217
+ text-decoration: none;
7218
+ }
7219
+
7220
+ .kds-bill-attachment:hover span {
7221
+ text-decoration: underline;
7222
+ }
7223
+
7224
+ .kds-bill-attachment i {
7225
+ font-size: var(--kds-font-size-lg);
7226
+ min-width: unset;
7227
+ justify-content: flex-start;
7228
+ }
7229
+
6050
7230
  /* -- Share Card (mustContinue) -- */
6051
7231
  .kds-share-card {
6052
7232
  margin-top: var(--kds-spacing-2);
@@ -6156,8 +7336,10 @@ button.kds-btn-success::after {
6156
7336
  Desktop (min-width: 768px) restores padding + full border-radius. */
6157
7337
  .kds-payment-flow .kds-card-elevated {
6158
7338
  margin-top: 0;
6159
- padding-top: 0;
6160
- padding-bottom: 0;
7339
+ /* Vertical > lateral: la card del flujo respira arriba/abajo (spacing-3)
7340
+ manteniendo el padding lateral del base (spacing-2 / spacing-3 mobile). */
7341
+ padding-top: var(--kds-spacing-3);
7342
+ padding-bottom: var(--kds-spacing-3);
6161
7343
  }
6162
7344
 
6163
7345
  /* Reset BeerCSS sibling margin on form/section inside payment cards.
@@ -6172,12 +7354,43 @@ button.kds-btn-success::after {
6172
7354
  padding-bottom: 0;
6173
7355
  }
6174
7356
 
6175
- /* Payment stage full-viewport centered wrapper */
7357
+ /* Body card: alto mínimo estable para que NO salte entre estados de distinta altura
7358
+ (loader/progress ↔ formulario). Solo la body card = hijo directo de `.kds-screen`
7359
+ (los headers van dentro de `.kds-invoice-sticky-wrap`, no la afecta).
7360
+ Flex-column para que el contenido quede arriba y el footer interno (versión / secure
7361
+ footer) pueda pinearse al fondo con `margin-top: auto` aunque el contenido sea corto. */
7362
+ .kds-screen > .kds-card-elevated {
7363
+ min-height: 36rem;
7364
+ display: flex;
7365
+ flex-direction: column;
7366
+ /* mismo radio que el header (invoice-sticky usa radius-card); el base card-elevated
7367
+ es radius-md (8px) y quedaba distinto al header en desktop. */
7368
+ border-radius: var(--kds-radius-card);
7369
+ }
7370
+
7371
+ /* Variante sin radio superior: pantallas de resultado (éxito/fallo/timeout) que van
7372
+ SIN header arriba — la body card va sola y su tope queda plano (flush) en vez de
7373
+ con esquinas redondeadas sueltas. El padding del card sigue manejando el inset. */
7374
+ /* Tope plano SOLO en mobile (< 768px): la body card se pega al borde superior del widget
7375
+ (igual que el header sticky, que también aplana el tope solo en mobile). En desktop la card
7376
+ va centrada y separada, así que conserva el border-radius superior redondeado. */
7377
+ @media (max-width: 767px) {
7378
+ .kds-screen > .kds-card-elevated.kds-card-elevated--flush-top,
7379
+ .kds-card-elevated.kds-card-elevated--flush-top {
7380
+ border-top-left-radius: 0;
7381
+ border-top-right-radius: 0;
7382
+ }
7383
+ }
7384
+
7385
+ /* Payment stage — full-viewport centered wrapper.
7386
+ Fondo muted (gris) para que las cards blancas (header + body) y el gap entre
7387
+ ellas se distingan; sin esto, card blanca sobre fondo blanco se funden. */
6176
7388
  .kds-payment-stage {
6177
7389
  min-height: 100vh;
6178
7390
  display: flex;
6179
7391
  justify-content: center;
6180
7392
  padding: var(--kds-spacing-2-5) var(--kds-spacing-2) var(--kds-spacing-8);
7393
+ background: var(--kds-color-background-muted);
6181
7394
  }
6182
7395
 
6183
7396
  /* Payment flow — narrow column */
@@ -6211,23 +7424,23 @@ button.kds-btn-success::after {
6211
7424
  width: 100%;
6212
7425
  }
6213
7426
 
6214
- .kds-payment-flow .kds-card-elevated {
6215
- padding-top: var(--kds-spacing-2);
6216
- padding-bottom: var(--kds-spacing-2);
6217
- }
6218
-
6219
- /* Invoice sticky card: progressive padding-top (expanded 20px → collapsed 8px).
6220
- Overrides base rule at same specificity; source order wins here. */
7427
+ /* Invoice sticky card padding (mobile).
7428
+ Top: estático 12px. Bottom: progresivo 16px (expandido, = desktop) → 8px (colapsado).
7429
+ El padding-bottom animado se reduce al colapsar — sin depender de que el clip-path
7430
+ corte el padding abruptamente. */
6221
7431
  .kds-payment-flow .kds-card-elevated.kds-invoice-sticky {
6222
7432
  padding-top: var(--kds-spacing-1-5);
7433
+ padding-bottom: calc(
7434
+ var(--kds-spacing-2)
7435
+ - var(--kds-spacing-1) * var(--collapse-progress, 0)
7436
+ );
6223
7437
  }
6224
7438
 
6225
7439
 
6226
7440
  }
6227
7441
 
6228
- /* Desktop: restore card padding and full border-radius on invoice sticky */
7442
+ /* Desktop: restore sticky header padding and full border-radius on invoice sticky */
6229
7443
  @media (min-width: 768px) {
6230
- .kds-payment-flow .kds-card-elevated,
6231
7444
  .kds-payment-flow .kds-card-elevated.kds-invoice-sticky {
6232
7445
  padding-top: var(--kds-spacing-2);
6233
7446
  padding-bottom: var(--kds-spacing-2);
@@ -6531,11 +7744,8 @@ button.kds-btn-success::after {
6531
7744
  display: none;
6532
7745
  }
6533
7746
 
6534
- .kds-bank-modal-search {
6535
- padding: 0 var(--kds-spacing-2) var(--kds-spacing-1-5);
6536
- }
6537
-
6538
- .kds-bank-modal-search input {
7747
+ /* Search field reutilizable (standalone o dentro del KdsBankModal). */
7748
+ .kds-search-field {
6539
7749
  width: 100%;
6540
7750
  padding: var(--kds-spacing-1-25) var(--kds-spacing-1-5);
6541
7751
  border: 1px solid var(--kds-color-divider);
@@ -6547,15 +7757,57 @@ button.kds-btn-success::after {
6547
7757
  background: var(--kds-color-surface);
6548
7758
  }
6549
7759
 
6550
- .kds-bank-modal-search input::placeholder {
7760
+ .kds-search-field::placeholder {
6551
7761
  color: var(--kds-color-text-hint);
6552
7762
  }
6553
7763
 
6554
- .kds-bank-modal-search input:focus {
7764
+ .kds-search-field:focus {
6555
7765
  border-color: var(--kds-color-primary-main);
6556
7766
  box-shadow: 0 0 0 2px var(--kds-color-primary-faint);
6557
7767
  }
6558
7768
 
7769
+ /* Variante con ícono (withIcon): lupa posicionada DENTRO del input, a la derecha. */
7770
+ .kds-search-field-wrapper {
7771
+ position: relative;
7772
+ width: 100%;
7773
+ }
7774
+
7775
+ .kds-search-field-icon {
7776
+ position: absolute;
7777
+ right: var(--kds-spacing-1-5);
7778
+ top: 50%;
7779
+ transform: translateY(-50%);
7780
+ font-size: 20px;
7781
+ color: var(--kds-color-text-hint);
7782
+ pointer-events: none;
7783
+ }
7784
+
7785
+ /* Espacio a la derecha del texto para no solaparse con la lupa. */
7786
+ .kds-search-field-wrapper .kds-search-field {
7787
+ padding-right: calc(var(--kds-spacing-1-5) + var(--kds-spacing-3));
7788
+ }
7789
+
7790
+ /* Variante angosta del field (prop `narrow` de KdsTextField): ancho reducido y texto
7791
+ centrado, para inputs de pocos caracteres dispuestos en fila (ej. segmentos de
7792
+ coordenadas o código). No crece ni se encoge dentro de un flex row. */
7793
+ .field.kds-field--narrow {
7794
+ width: 6rem;
7795
+ flex: 0 0 auto;
7796
+ /* En fila horizontal no aplica el margin vertical que BeerCSS da a fields
7797
+ hermanos (regla de "flow" `... + :is(.field,...)`, specificity ~0,3,1);
7798
+ sin el reset los siguientes quedan desalineados hacia abajo. `!important`
7799
+ para ganarle a esa regla de framework. */
7800
+ margin-block: 0 !important;
7801
+ }
7802
+
7803
+ .field.kds-field--narrow > input {
7804
+ text-align: center;
7805
+ }
7806
+
7807
+ .kds-bank-modal-search {
7808
+ padding: 0 var(--kds-spacing-2) var(--kds-spacing-1-5);
7809
+ }
7810
+
6559
7811
  .kds-bank-modal-body {
6560
7812
  flex: 1;
6561
7813
  overflow-y: auto;
@@ -6653,7 +7905,7 @@ button.kds-btn-success::after {
6653
7905
  min-width: 0;
6654
7906
  }
6655
7907
 
6656
- .kds-qr-text .title {
7908
+ .kds-qr-text .kds-qr-title {
6657
7909
  font-family: var(--kds-font-family-primary);
6658
7910
  font-weight: 600;
6659
7911
  font-size: var(--kds-font-size-sm);
@@ -6661,10 +7913,15 @@ button.kds-btn-success::after {
6661
7913
  color: var(--kds-color-text-primary);
6662
7914
  }
6663
7915
 
6664
- .kds-qr-text .sub {
7916
+ .kds-qr-text .kds-qr-subtitle {
6665
7917
  font-family: var(--kds-font-family-primary);
6666
7918
  font-size: var(--kds-font-size-caption);
6667
7919
  color: var(--kds-color-text-secondary);
7920
+ /* Trunca con ellipsis en lugar de wrap a 2 líneas — evita apretar el row
7921
+ cuando el contenedor es angosto (mobile o decorator constrained). */
7922
+ overflow: hidden;
7923
+ text-overflow: ellipsis;
7924
+ white-space: nowrap;
6668
7925
  }
6669
7926
 
6670
7927
  /* QR Badge - "Rápido" indicator */
@@ -6687,46 +7944,26 @@ button.kds-btn-success::after {
6687
7944
  font-size: 20px;
6688
7945
  }
6689
7946
 
6690
- /* Mobile responsive: Ajustes para pantallas pequeñas */
7947
+ /* Mobile responsive (≤480px): respira más mantenemos padding/gap/avatar igual
7948
+ que desktop y solo agregamos truncation al subtitle y `flex-shrink: 0` en los
7949
+ ítems que deben mantener su tamaño (avatar, badge, chevron). */
6691
7950
  @media (max-width: 480px) {
6692
- .kds-qr-row {
6693
- gap: var(--kds-spacing-1-5);
6694
- padding: var(--kds-spacing-1-5);
6695
- }
6696
-
6697
7951
  .kds-qr-avatar {
6698
- width: var(--kds-spacing-4-5);
6699
- height: var(--kds-spacing-4-5);
6700
7952
  flex-shrink: 0;
6701
7953
  }
6702
7954
 
6703
- .kds-qr-avatar .material-symbols-outlined,
6704
- .kds-qr-avatar .material-icons-round {
6705
- font-size: var(--kds-font-size-lg);
6706
- }
6707
-
6708
- .kds-qr-text .title {
6709
- font-size: var(--kds-font-size-xs);
6710
- line-height: var(--kds-line-height-tight);
6711
- }
6712
-
6713
7955
  .kds-qr-text .sub {
6714
- font-size: var(--kds-font-size-caption);
6715
- line-height: var(--kds-line-height-tight);
6716
7956
  overflow: hidden;
6717
7957
  text-overflow: ellipsis;
6718
7958
  white-space: nowrap;
6719
7959
  }
6720
7960
 
6721
7961
  .kds-qr-badge {
6722
- font-size: var(--kds-font-size-caption);
6723
- padding: var(--kds-spacing-0-25) var(--kds-spacing-0-75);
6724
7962
  flex-shrink: 0;
6725
7963
  }
6726
7964
 
6727
7965
  .kds-qr-row .material-symbols-outlined:last-child,
6728
7966
  .kds-qr-row .material-icons-round:last-child {
6729
- font-size: var(--kds-font-size-base);
6730
7967
  flex-shrink: 0;
6731
7968
  }
6732
7969
  }
@@ -6755,7 +7992,11 @@ button.kds-btn-success::after {
6755
7992
  border-radius: var(--kds-spacing-0-75);
6756
7993
  font-size: var(--kds-font-size-sm);
6757
7994
  cursor: pointer;
6758
- transition: background 0.12s ease;
7995
+ /* Transición más amplia y suave: bg + color + border ease-out */
7996
+ transition:
7997
+ background-color 280ms cubic-bezier(0.2, 0.8, 0.2, 1),
7998
+ color 280ms cubic-bezier(0.2, 0.8, 0.2, 1),
7999
+ border-top-color 280ms ease-out;
6759
8000
  position: relative;
6760
8001
  user-select: none;
6761
8002
  }
@@ -6764,19 +8005,21 @@ button.kds-btn-success::after {
6764
8005
  border-top: 0;
6765
8006
  }
6766
8007
 
6767
- .kds-copyable-table-row:hover {
8008
+ /* Hover NO aplica mientras la row esté en `.copied` o `.settling`
8009
+ (evita el flicker del hover state al desaparecer el verde de "copiado") */
8010
+ .kds-copyable-table-row:not(.copied):not(.settling):hover {
6768
8011
  background: var(--kds-color-primary-faint);
6769
8012
  }
6770
8013
 
6771
- .kds-copyable-table-row:hover + .kds-copyable-table-row {
8014
+ .kds-copyable-table-row:not(.copied):not(.settling):hover + .kds-copyable-table-row {
6772
8015
  border-top-color: transparent;
6773
8016
  }
6774
8017
 
6775
- .kds-copyable-table-row:active {
8018
+ .kds-copyable-table-row:not(.copied):not(.settling):active {
6776
8019
  background: var(--kds-color-primary-selected);
6777
8020
  }
6778
8021
 
6779
- .kds-copyable-table-row .k {
8022
+ .kds-copyable-table-row .kds-key {
6780
8023
  color: var(--kds-color-text-secondary);
6781
8024
  font-size: var(--kds-font-size-caption);
6782
8025
  letter-spacing: 0.3px;
@@ -6785,7 +8028,7 @@ button.kds-btn-success::after {
6785
8028
  flex: 0 0 auto;
6786
8029
  }
6787
8030
 
6788
- .kds-copyable-table-row .v {
8031
+ .kds-copyable-table-row .kds-value {
6789
8032
  color: var(--kds-color-text-primary);
6790
8033
  font-family: 'JetBrains Mono', 'SF Mono', ui-monospace, Menlo, Consolas, monospace;
6791
8034
  font-weight: var(--kds-font-weight-medium);
@@ -6801,17 +8044,18 @@ button.kds-btn-success::after {
6801
8044
  flex: 1 1 auto;
6802
8045
  }
6803
8046
 
6804
- .kds-copyable-table-row .v::after {
8047
+ .kds-copyable-table-row .kds-value::after {
6805
8048
  content: 'content_copy';
6806
8049
  font-family: 'Material Symbols Outlined';
6807
8050
  font-size: var(--kds-font-size-sm);
6808
8051
  color: var(--kds-color-primary-main);
6809
8052
  opacity: 0;
6810
- transition: opacity 0.12s ease;
8053
+ /* Transición coordinada con la row */
8054
+ transition: opacity 280ms cubic-bezier(0.2, 0.8, 0.2, 1);
6811
8055
  flex: 0 0 auto;
6812
8056
  }
6813
8057
 
6814
- .kds-copyable-table-row:hover .v::after {
8058
+ .kds-copyable-table-row:not(.copied):not(.settling):hover .kds-value::after {
6815
8059
  opacity: 0.7;
6816
8060
  }
6817
8061
 
@@ -6819,22 +8063,76 @@ button.kds-btn-success::after {
6819
8063
  background: var(--kds-color-success-soft);
6820
8064
  }
6821
8065
 
6822
- .kds-copyable-table-row.copied .v {
8066
+ .kds-copyable-table-row.copied .kds-value {
6823
8067
  color: var(--kds-color-success-dark);
6824
8068
  }
6825
8069
 
6826
- .kds-copyable-table-row.copied .v::after {
8070
+ .kds-copyable-table-row.copied .kds-value::after {
6827
8071
  content: 'check';
6828
8072
  font-family: 'Material Symbols Outlined';
6829
8073
  color: var(--kds-color-success-dark);
6830
8074
  opacity: 1;
6831
8075
  }
6832
8076
 
8077
+ /* `.settling`: estado de "recién copiado" — la row vuelve al normal sin
8078
+ activar hover. Dura lo mismo que la transición (~300ms) y se aplica
8079
+ desde React tras quitar `.copied`. */
8080
+ .kds-copyable-table-row.settling .kds-value::after {
8081
+ /* Mantén el check con opacity 0 mientras settling, fade out suave */
8082
+ opacity: 0;
8083
+ }
8084
+
6833
8085
  /* Disable BeerCSS ripple on copyable table rows */
6834
8086
  .kds-copyable-table-row::after {
6835
8087
  display: none;
6836
8088
  }
6837
8089
 
8090
+ /* ========================================
8091
+ GRID VARIANT (.kds-copyable-table--grid)
8092
+ Read-only grid of N text cells per row. No copy,
8093
+ no role=button, no "copy all". Cells share the row
8094
+ width equally. Used inside radio options, etc.
8095
+ ======================================== */
8096
+
8097
+ /* La grilla es solo columnas de texto (como el DataTable original): sin caja,
8098
+ borde, fondo ni sombra del contenedor copyable. */
8099
+ .kds-copyable-table--grid {
8100
+ border: 0;
8101
+ border-radius: 0;
8102
+ background: none;
8103
+ padding: 0;
8104
+ box-shadow: none;
8105
+ }
8106
+
8107
+ .kds-copyable-table-row--grid {
8108
+ display: flex;
8109
+ align-items: center;
8110
+ gap: var(--kds-spacing-1-5);
8111
+ padding: 0;
8112
+ /* Sin el divisor border-top que trae la fila base del copyable. */
8113
+ border-top: 0;
8114
+ cursor: default;
8115
+ user-select: text;
8116
+ }
8117
+
8118
+ /* Separación entre filas de la grilla (sin divisores). */
8119
+ .kds-copyable-table-row--grid + .kds-copyable-table-row--grid {
8120
+ margin-top: var(--kds-spacing-0-5);
8121
+ }
8122
+
8123
+ .kds-grid-cell {
8124
+ flex: 1 1 0;
8125
+ min-width: 0;
8126
+ font-size: var(--kds-font-size-sm);
8127
+ font-weight: var(--kds-font-weight-medium);
8128
+ color: var(--kds-color-text-primary);
8129
+ word-break: break-word;
8130
+ }
8131
+
8132
+ .kds-grid-cell--disabled {
8133
+ color: var(--kds-color-text-disabled);
8134
+ }
8135
+
6838
8136
  /* ========================================
6839
8137
  COPY-ALL BUTTON (.kds-copy-all-btn)
6840
8138
  Modifier for kds-btn kds-btn-outlined that adds
@@ -6995,7 +8293,7 @@ a.kds-copy-all-btn.copied:hover {
6995
8293
  .kds-monto-row {
6996
8294
  display: flex;
6997
8295
  justify-content: space-between;
6998
- align-items: flex-end;
8296
+ align-items: flex-start;
6999
8297
  padding: var(--kds-spacing-1-75) 0 var(--kds-spacing-1);
7000
8298
  border-top: 1px dashed var(--kds-color-divider);
7001
8299
  margin-top: var(--kds-spacing-1-75);
@@ -7019,3 +8317,191 @@ a.kds-copy-all-btn.copied:hover {
7019
8317
  color: var(--kds-color-text-primary);
7020
8318
  }
7021
8319
 
8320
+ /* ========================================
8321
+ PAYMENT TOTAL (.kds-payment-total)
8322
+ Bloque "monto a pagar" para QR view y email templates.
8323
+ Portado desde paylink-ligopay → <kh:paymentTotal> + materialize-config.css.
8324
+ Variantes:
8325
+ - default: amount grande (3rem) en color primario Khipu.
8326
+ - email: amount compacto (1.5rem) en color texto primario.
8327
+ Responsive (default): mobile (≤ 1024px) centra y oculta titleMobile.
8328
+ ======================================== */
8329
+
8330
+ .kds-payment-total {
8331
+ /* Component-scoped tokens — overrideables sin tocar el CSS global. */
8332
+ --kds-payment-total-title-size: var(--kds-font-size-2xl); /* 24px */
8333
+ --kds-payment-total-label-size: var(--kds-font-size-xl); /* 20px */
8334
+ --kds-payment-amount-size: 3rem; /* 48px — destacado */
8335
+ --kds-payment-amount-line-height: var(--kds-line-height-snug);
8336
+ --kds-payment-amount-color: var(--kds-color-primary-main);
8337
+ --kds-payment-amount-weight: var(--kds-font-weight-medium);
8338
+
8339
+ text-align: left;
8340
+ }
8341
+
8342
+ .kds-payment-total-title,
8343
+ .kds-payment-total-title-mobile {
8344
+ font-size: var(--kds-payment-total-title-size);
8345
+ font-weight: var(--kds-font-weight-semibold);
8346
+ color: var(--kds-color-text-primary);
8347
+ margin-bottom: var(--kds-spacing-4);
8348
+ }
8349
+
8350
+ .kds-payment-total-title-mobile {
8351
+ display: none;
8352
+ }
8353
+
8354
+ .kds-payment-total .kds-payment-label {
8355
+ font-size: var(--kds-payment-total-label-size);
8356
+ font-weight: var(--kds-font-weight-semibold);
8357
+ color: var(--kds-color-text-primary);
8358
+ margin-bottom: var(--kds-spacing-1);
8359
+ padding-top: 0;
8360
+ }
8361
+
8362
+ .kds-payment-total .kds-payment-amount {
8363
+ font-size: var(--kds-payment-amount-size);
8364
+ font-weight: var(--kds-payment-amount-weight);
8365
+ line-height: var(--kds-payment-amount-line-height);
8366
+ color: var(--kds-payment-amount-color);
8367
+ margin-bottom: var(--kds-spacing-4);
8368
+ }
8369
+
8370
+ .kds-payment-total .kds-payment-amount .kds-payment-total-decimal-sup,
8371
+ .kds-invoice-amount .kds-payment-total-decimal-sup {
8372
+ font-size: var(--kds-font-size-decimal-sup);
8373
+ font-weight: var(--kds-font-weight-semibold);
8374
+ position: relative;
8375
+ top: 0;
8376
+ }
8377
+
8378
+ /* Variante email: amount compacto en color texto primario, alineación izquierda fija */
8379
+ .kds-payment-total--email {
8380
+ --kds-payment-amount-size: var(--kds-font-size-2xl); /* 24px */
8381
+ --kds-payment-amount-line-height: var(--kds-line-height-snug);
8382
+ --kds-payment-amount-color: var(--kds-color-text-primary);
8383
+ --kds-payment-amount-weight: var(--kds-font-weight-semibold);
8384
+ }
8385
+
8386
+ /* Tone modifier: swaps the amount color to the khipu-blue / LigoPay info blue.
8387
+ Combinable with --email or with the default variant. */
8388
+ .kds-payment-total--tone-info {
8389
+ --kds-payment-amount-color: var(--kds-color-info-blue); /* #5A5FE0 */
8390
+ }
8391
+
8392
+ /* Centered modifier: removes the asymmetric desktop padding (which exists for
8393
+ LigoPay QR view) and centers all internal text. Composable with --email and
8394
+ --tone-info. */
8395
+ .kds-payment-total--centered {
8396
+ padding-left: 0;
8397
+ padding-top: 0;
8398
+ text-align: center;
8399
+ }
8400
+ .kds-payment-total--centered .kds-payment-total-title,
8401
+ .kds-payment-total--centered .kds-payment-total-title-mobile,
8402
+ .kds-payment-total--centered .kds-payment-label,
8403
+ .kds-payment-total--centered .kds-payment-amount {
8404
+ text-align: center;
8405
+ }
8406
+
8407
+ /* Responsive: mobile ≤ 1024px — centra y conmuta los títulos (sólo variante default) */
8408
+ @media (max-width: 1024px) {
8409
+ .kds-payment-total {
8410
+ text-align: center;
8411
+ }
8412
+
8413
+ .kds-payment-total .kds-payment-total-title {
8414
+ display: none;
8415
+ }
8416
+
8417
+ .kds-payment-total .kds-payment-total-title-mobile {
8418
+ display: block;
8419
+ text-align: center;
8420
+ }
8421
+
8422
+ .kds-payment-total .kds-payment-label,
8423
+ .kds-payment-total .kds-payment-amount {
8424
+ text-align: center;
8425
+ }
8426
+
8427
+ /* Email mantiene alineación izquierda incluso en mobile (matchea email clients) */
8428
+ .kds-payment-total--email,
8429
+ .kds-payment-total--email .kds-payment-label,
8430
+ .kds-payment-total--email .kds-payment-amount {
8431
+ text-align: left;
8432
+ }
8433
+ }
8434
+
8435
+ /* ==========================================================================
8436
+ GRID UTILITIES — semantic aliases for BeerCSS .grid / .s* / .m* / .l*
8437
+
8438
+ Estas utilidades son ALIAS de las clases cortas de BeerCSS para que
8439
+ consumers (payment, LigoPay, Grails legacy) puedan escribir markup con
8440
+ nombres descriptivos sin renunciar al grid de 12 columnas de BeerCSS.
8441
+
8442
+ Convención (estilo Bootstrap responsive):
8443
+ - kds-grid → .grid (12-col CSS grid + 1rem gap)
8444
+ - kds-col-sm-{1..12} → .s{1..12} (mobile, siempre aplica)
8445
+ - kds-col-md-{1..12} → .m{1..12} (>=601px)
8446
+ - kds-col-lg-{1..12} → .l{1..12} (>=993px)
8447
+
8448
+ Las definiciones originales de BeerCSS NO se tocan; estas son aditivas.
8449
+ ========================================================================== */
8450
+
8451
+ .kds-grid {
8452
+ --_gap: 1rem;
8453
+ display: grid;
8454
+ grid-template-columns: repeat(12, 1fr);
8455
+ gap: var(--_gap);
8456
+ block-size: auto;
8457
+ }
8458
+
8459
+ .kds-grid.kds-grid-no-space { --_gap: 0rem; }
8460
+ .kds-grid.kds-grid-medium-space { --_gap: 1.5rem; }
8461
+ .kds-grid.kds-grid-large-space { --_gap: 2rem; }
8462
+
8463
+ .kds-grid > * { margin: 0; }
8464
+
8465
+ .kds-col-sm-1 { grid-area: auto/span 1; }
8466
+ .kds-col-sm-2 { grid-area: auto/span 2; }
8467
+ .kds-col-sm-3 { grid-area: auto/span 3; }
8468
+ .kds-col-sm-4 { grid-area: auto/span 4; }
8469
+ .kds-col-sm-5 { grid-area: auto/span 5; }
8470
+ .kds-col-sm-6 { grid-area: auto/span 6; }
8471
+ .kds-col-sm-7 { grid-area: auto/span 7; }
8472
+ .kds-col-sm-8 { grid-area: auto/span 8; }
8473
+ .kds-col-sm-9 { grid-area: auto/span 9; }
8474
+ .kds-col-sm-10 { grid-area: auto/span 10; }
8475
+ .kds-col-sm-11 { grid-area: auto/span 11; }
8476
+ .kds-col-sm-12 { grid-area: auto/span 12; }
8477
+
8478
+ @media only screen and (min-width: 601px) {
8479
+ .kds-col-md-1 { grid-area: auto/span 1; }
8480
+ .kds-col-md-2 { grid-area: auto/span 2; }
8481
+ .kds-col-md-3 { grid-area: auto/span 3; }
8482
+ .kds-col-md-4 { grid-area: auto/span 4; }
8483
+ .kds-col-md-5 { grid-area: auto/span 5; }
8484
+ .kds-col-md-6 { grid-area: auto/span 6; }
8485
+ .kds-col-md-7 { grid-area: auto/span 7; }
8486
+ .kds-col-md-8 { grid-area: auto/span 8; }
8487
+ .kds-col-md-9 { grid-area: auto/span 9; }
8488
+ .kds-col-md-10 { grid-area: auto/span 10; }
8489
+ .kds-col-md-11 { grid-area: auto/span 11; }
8490
+ .kds-col-md-12 { grid-area: auto/span 12; }
8491
+ }
8492
+
8493
+ @media only screen and (min-width: 993px) {
8494
+ .kds-col-lg-1 { grid-area: auto/span 1; }
8495
+ .kds-col-lg-2 { grid-area: auto/span 2; }
8496
+ .kds-col-lg-3 { grid-area: auto/span 3; }
8497
+ .kds-col-lg-4 { grid-area: auto/span 4; }
8498
+ .kds-col-lg-5 { grid-area: auto/span 5; }
8499
+ .kds-col-lg-6 { grid-area: auto/span 6; }
8500
+ .kds-col-lg-7 { grid-area: auto/span 7; }
8501
+ .kds-col-lg-8 { grid-area: auto/span 8; }
8502
+ .kds-col-lg-9 { grid-area: auto/span 9; }
8503
+ .kds-col-lg-10 { grid-area: auto/span 10; }
8504
+ .kds-col-lg-11 { grid-area: auto/span 11; }
8505
+ .kds-col-lg-12 { grid-area: auto/span 12; }
8506
+ }
8507
+