@khipu/design-system 0.2.0-alpha.10 → 0.2.0-alpha.100

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-18T14:15:54.397Z
14
+ * Generated: 2026-07-07T13:01:50.848Z
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;
@@ -839,6 +850,10 @@ a.kds-btn {
839
850
  /* Spacing - Using design tokens */
840
851
  padding: var(--kds-spacing-button-padding); /* 0 16px */
841
852
  height: var(--kds-spacing-button-min-height); /* 44px */
853
+ /* min-height además de height: en contenedores flex-column (p.ej. pantallas de
854
+ resultado/timeout) el botón es flex-item y, con solo `height`, flex-shrink lo
855
+ colapsaba a la altura del texto (~21px). min-height impide que se encoja. */
856
+ min-height: var(--kds-spacing-button-min-height); /* 44px */
842
857
 
843
858
  /* Border - Using design token */
844
859
  border-radius: var(--kds-radius-button); /* 10px */
@@ -846,7 +861,10 @@ a.kds-btn {
846
861
 
847
862
  /* Interaction */
848
863
  cursor: pointer;
849
- transition: background .15s, box-shadow .15s, transform .05s;
864
+ transition: background-color var(--kds-transition-shorter) var(--kds-easing-standard),
865
+ color var(--kds-transition-shorter) var(--kds-easing-standard),
866
+ box-shadow var(--kds-transition-shorter) var(--kds-easing-standard),
867
+ transform var(--kds-transition-shortest) var(--kds-easing-standard);
850
868
  box-sizing: border-box;
851
869
  vertical-align: middle;
852
870
  }
@@ -978,7 +996,7 @@ a.kds-btn-text {
978
996
 
979
997
  button.kds-btn-text:hover:not(:disabled),
980
998
  a.kds-btn-text:hover:not(:disabled) {
981
- background: var(--kds-color-primary-hover); /* rgba(131, 71, 173, 0.04) */
999
+ color: var(--kds-color-primary-dark);
982
1000
  }
983
1001
 
984
1002
  button.kds-btn-text:disabled,
@@ -988,6 +1006,30 @@ a.kds-btn-text:disabled {
988
1006
  box-shadow: none;
989
1007
  }
990
1008
 
1009
+ /* Error text (destructive, ghost) — red text, no fill/border. Para acciones
1010
+ destructivas secundarias que no deben competir con el CTA primario. */
1011
+ button.kds-btn-error-text,
1012
+ a.kds-btn-error-text {
1013
+ background: transparent;
1014
+ color: var(--kds-color-error-main);
1015
+ border: none;
1016
+ padding: 6px var(--kds-spacing-2);
1017
+ min-height: 32px;
1018
+ box-shadow: none;
1019
+ }
1020
+
1021
+ button.kds-btn-error-text:hover:not(:disabled),
1022
+ a.kds-btn-error-text:hover:not(:disabled) {
1023
+ color: var(--kds-color-error-dark);
1024
+ }
1025
+
1026
+ button.kds-btn-error-text:disabled,
1027
+ a.kds-btn-error-text:disabled {
1028
+ background: transparent;
1029
+ color: var(--kds-color-action-disabled);
1030
+ box-shadow: none;
1031
+ }
1032
+
991
1033
  /* Icon wrapper - control size via span container */
992
1034
  button.kds-btn > .kds-icon,
993
1035
  a.kds-btn > .kds-icon {
@@ -1073,63 +1115,90 @@ a.kds-btn.kds-btn-md {
1073
1115
  }
1074
1116
  }
1075
1117
 
1076
- /* Selection card */
1118
+ /* Selection card.
1119
+ IMPORTANTE: este componente se renderiza como `<button>`. BeerCSS aplica al
1120
+ `button` base: `display: inline-flex; align-items: center; justify-content: center;
1121
+ block-size: 2.5rem; padding: 0 1rem; background: var(--primary); color: on-primary`.
1122
+ Todas esas reglas hay que sobreescribirlas para que el card-selector funcione como
1123
+ un contenedor flex column de altura variable y alineado a la izquierda.
1124
+
1125
+ Convención del DS: spacing entre hijos via `gap` del flex padre + padding.
1126
+ Los elementos internos (.kds-card-selector-icon/-title/-description) NO usan margin. */
1077
1127
  .kds-card-selector {
1128
+ /* Reset BeerCSS button */
1129
+ display: flex;
1130
+ flex-direction: column;
1131
+ align-items: stretch;
1132
+ justify-content: flex-start;
1133
+ block-size: auto;
1134
+ inline-size: 100%;
1135
+ text-align: left;
1136
+ line-height: 1.5;
1137
+ color: var(--kds-color-text-primary);
1138
+ /* Estilos propios del card-selector */
1078
1139
  padding: var(--kds-spacing-3);
1140
+ gap: var(--kds-spacing-1-5);
1079
1141
  border-radius: var(--kds-radius-md);
1080
- border: 2px solid var(--kds-border-light);
1142
+ border: var(--kds-border-width-md) solid var(--kds-border-light);
1081
1143
  background: var(--kds-color-background-paper);
1082
1144
  transition: all 0.3s ease;
1083
- text-align: left;
1084
1145
  cursor: pointer;
1085
- display: flex;
1086
- flex-direction: column;
1087
1146
  }
1088
1147
 
1089
- .kds-card-selector:hover {
1090
- border-color: #D1D5DB; /* gray-300 */
1148
+ .kds-card-selector:hover:not(.selected) {
1149
+ border-color: var(--kds-color-primary-light);
1150
+ background: var(--kds-color-primary-hover);
1151
+ }
1152
+
1153
+ /* Override del ripple/overlay default de BeerCSS:
1154
+ `:is(.wave,.chip,.button,button,nav.tabbed>a,...):not([class*=ripple])::after`
1155
+ tiene specificity (0,2,4) según DevTools (Chrome cuenta cada selector dentro
1156
+ de :is() individualmente para algunos motores). Usamos !important — consistente
1157
+ con cómo se overridean otros estilos de BeerCSS en este archivo (67 usos previos).
1158
+ Antes pintaba negro porque `currentColor` resolvía a text-primary del card. */
1159
+ .kds-card-selector::after {
1160
+ background-image: radial-gradient(circle, var(--kds-color-primary-main) 1%, transparent 1%) !important;
1091
1161
  }
1092
1162
 
1093
1163
  .kds-card-selector.selected {
1094
- border-color: #3B82F6; /* blue-600 */
1095
- background: #EFF6FF; /* blue-50 */
1164
+ border-color: var(--kds-color-primary-main);
1165
+ background: var(--kds-color-primary-faint);
1096
1166
  box-shadow: var(--kds-shadow-md);
1097
1167
  }
1098
1168
 
1099
- /* Icon container */
1169
+ /* Icon container — spacing manejado por gap del padre, no margin */
1100
1170
  .kds-card-selector-icon {
1101
- width: 48px;
1102
- height: 48px;
1103
- background: #DBEAFE; /* blue-100 */
1104
- border-radius: 8px;
1171
+ width: var(--kds-spacing-6);
1172
+ height: var(--kds-spacing-6);
1173
+ background: var(--kds-color-primary-focus);
1174
+ border-radius: var(--kds-radius-sm);
1105
1175
  display: flex;
1106
1176
  align-items: center;
1107
1177
  justify-content: center;
1108
- margin-bottom: 16px;
1109
1178
  }
1110
1179
 
1111
1180
  .kds-card-selector-icon i,
1112
1181
  .kds-card-selector-icon svg {
1113
- width: 24px;
1114
- height: 24px;
1115
- min-width: 24px;
1116
- min-height: 24px;
1117
- color: #3B82F6; /* blue-600 */
1182
+ width: var(--kds-spacing-3);
1183
+ height: var(--kds-spacing-3);
1184
+ min-width: var(--kds-spacing-3);
1185
+ min-height: var(--kds-spacing-3);
1186
+ font-size: var(--kds-spacing-3);
1187
+ color: var(--kds-color-primary-main);
1118
1188
  }
1119
1189
 
1120
- /* Title */
1190
+ /* Title — spacing manejado por gap del padre, no margin */
1121
1191
  .kds-card-selector-title {
1122
- font-weight: 600;
1192
+ font-weight: var(--kds-font-weight-semibold);
1123
1193
  color: var(--kds-color-text-primary);
1124
- margin-bottom: 8px;
1125
- font-size: 16px;
1194
+ font-size: var(--kds-font-size-lg);
1126
1195
  }
1127
1196
 
1128
1197
  /* Description */
1129
1198
  .kds-card-selector-description {
1130
- font-size: 14px;
1131
- color: #4B5563; /* gray-600 */
1132
- line-height: 1.5;
1199
+ font-size: var(--kds-font-size-sm);
1200
+ color: var(--kds-color-text-secondary);
1201
+ line-height: var(--kds-line-height-normal);
1133
1202
  }
1134
1203
 
1135
1204
  /* Selected state - visual feedback via border only */
@@ -1141,14 +1210,22 @@ a.kds-btn.kds-btn-md {
1141
1210
  display: flex;
1142
1211
  flex-direction: column;
1143
1212
  gap: var(--kds-spacing-2);
1144
- padding: var(--kds-spacing-4);
1213
+ padding: var(--kds-spacing-2);
1145
1214
  border-radius: var(--kds-radius-lg);
1146
1215
  border: 2px solid var(--kds-border-medium);
1147
- max-width: 400px; /* Increased from 365px for better text spacing */
1216
+ /* Ancho mínimo aplicado a TODOS los planes así un plan sin badge en una
1217
+ grilla con un plan recomendado mantiene la misma anchura, y el badge nunca
1218
+ se superpone al título por estrechez del card. */
1219
+ min-width: 260px;
1220
+ max-width: 400px;
1148
1221
  background: var(--kds-surface-base);
1149
1222
  transition: all 0.3s ease;
1150
1223
  cursor: pointer;
1151
- height: 100%; /* Ensure cards fill grid height */
1224
+ /* Alto fijo: el card se estira al alto de su row del grid (align-items stretch
1225
+ es el default) — todas las cards de la misma fila quedan a la misma altura
1226
+ sin importar el contenido. `align-self: stretch` lo hace explícito. */
1227
+ height: 100%;
1228
+ align-self: stretch;
1152
1229
  }
1153
1230
 
1154
1231
  .kds-card-plan:hover {
@@ -1168,18 +1245,19 @@ a.kds-btn.kds-btn-md {
1168
1245
  box-shadow: var(--kds-shadow-elevation-4);
1169
1246
  }
1170
1247
 
1171
- /* Recommended Badge */
1248
+ /* Recommended Badge — inline dentro del card (no flotante).
1249
+ Es el primer hijo del .kds-card-plan, así que aparece arriba de todo
1250
+ el contenido, alineado a la izquierda. */
1172
1251
  .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);
1252
+ align-self: flex-start;
1253
+ padding: var(--kds-spacing-0-5) var(--kds-spacing-1-5);
1177
1254
  background: var(--primary);
1178
1255
  color: white;
1179
- font-size: var(--kds-font-size-sm);
1256
+ font-size: var(--kds-font-size-xs);
1180
1257
  font-weight: var(--kds-font-weight-semibold);
1258
+ line-height: 1;
1181
1259
  border-radius: var(--kds-radius-full);
1182
- box-shadow: var(--kds-shadow-elevation-1);
1260
+ white-space: nowrap;
1183
1261
  }
1184
1262
 
1185
1263
  /* Plan Header */
@@ -1207,13 +1285,11 @@ a.kds-btn.kds-btn-md {
1207
1285
  display: flex;
1208
1286
  align-items: baseline;
1209
1287
  gap: var(--kds-spacing-2);
1210
- padding: var(--kds-spacing-4) 0;
1211
1288
  border-bottom: 1px solid var(--kds-border-light);
1212
- min-height: 88px; /* Standardize price area height */
1213
1289
  }
1214
1290
 
1215
1291
  .kds-price {
1216
- font-size: 42px;
1292
+ font-size: var(--kds-font-size-4xl);
1217
1293
  font-weight: var(--kds-font-weight-bold);
1218
1294
  color: var(--primary);
1219
1295
  line-height: 1.1;
@@ -1468,6 +1544,10 @@ a.kds-btn.kds-btn-md {
1468
1544
  align-items: flex-end;
1469
1545
  }
1470
1546
 
1547
+ .kds-self-center {
1548
+ align-self: center;
1549
+ }
1550
+
1471
1551
  /* Gap utilities */
1472
1552
  .kds-gap-1 { gap: var(--kds-spacing-1); }
1473
1553
  .kds-gap-2 { gap: var(--kds-spacing-2); }
@@ -1504,6 +1584,7 @@ a.kds-btn.kds-btn-md {
1504
1584
  /* Spacing utilities - Margin */
1505
1585
  .kds-m-0 { margin: 0; }
1506
1586
  .kds-m-2 { margin: var(--kds-spacing-2); }
1587
+ .kds-m-2-5 { margin: var(--kds-spacing-2-5); }
1507
1588
  .kds-m-4 { margin: var(--kds-spacing-4); }
1508
1589
  .kds-m-6 { margin: var(--kds-spacing-6); }
1509
1590
  .kds-m-8 { margin: var(--kds-spacing-8); }
@@ -1517,6 +1598,7 @@ a.kds-btn.kds-btn-md {
1517
1598
  .kds-mt-8 { margin-top: var(--kds-spacing-8); }
1518
1599
 
1519
1600
  .kds-mb-0 { margin-bottom: 0; }
1601
+ .kds-mb-0-5 { margin-bottom: var(--kds-spacing-0-5); }
1520
1602
  .kds-mb-1 { margin-bottom: var(--kds-spacing-1); }
1521
1603
  .kds-mb-2 { margin-bottom: var(--kds-spacing-2); }
1522
1604
  .kds-mb-3 { margin-bottom: var(--kds-spacing-3); }
@@ -2029,6 +2111,221 @@ a.kds-btn-google:disabled {
2029
2111
  height: var(--kds-spacing-button-icon-size);
2030
2112
  }
2031
2113
 
2114
+ /* ========================================
2115
+ KDS LINEAR PROGRESS
2116
+ Native <progress> (KdsLinearProgress). Additive over BeerCSS `progress`,
2117
+ scoped to .kds-progress so other consumers are unaffected.
2118
+ ======================================== */
2119
+
2120
+ /* `progress` is inline-block by default → leaves a baseline (descender) gap
2121
+ below the bar. Block removes that gap so it sits flush with siblings. */
2122
+ .kds-progress {
2123
+ display: block;
2124
+ }
2125
+
2126
+ /* Animate the filled value when it changes (e.g. advancing a step). BeerCSS
2127
+ sets the value background but no transition, so the width jumps abruptly. */
2128
+ .kds-progress::-webkit-progress-value {
2129
+ transition: width var(--kds-transition-standard) var(--kds-easing-standard);
2130
+ }
2131
+
2132
+ .kds-progress::-moz-progress-bar {
2133
+ transition: width var(--kds-transition-standard) var(--kds-easing-standard);
2134
+ }
2135
+
2136
+ /* ========================================
2137
+ KDS SECURE LOADER
2138
+ Loader de espera: anillo circular + candado centrado + frases opcionales.
2139
+ El anillo reutiliza el spinner circular de BeerCSS, escalado.
2140
+ ======================================== */
2141
+
2142
+ .kds-secure-loader {
2143
+ display: flex;
2144
+ flex-direction: column;
2145
+ align-items: center;
2146
+ justify-content: center;
2147
+ gap: var(--kds-spacing-4);
2148
+ text-align: center;
2149
+ /* Llena y centra dentro de contenedores flex (pantalla de carga); en flujo
2150
+ normal (contenedor no-flex) se reduce a su contenido. */
2151
+ flex: 1 1 auto;
2152
+ }
2153
+
2154
+ .kds-secure-loader-text {
2155
+ display: flex;
2156
+ flex-direction: column;
2157
+ gap: var(--kds-spacing-0-25);
2158
+ }
2159
+
2160
+ .kds-secure-loader-title {
2161
+ margin: 0;
2162
+ font-family: var(--kds-font-family-primary);
2163
+ font-size: var(--kds-font-size-h6);
2164
+ font-weight: var(--kds-font-weight-semibold);
2165
+ line-height: var(--kds-line-height-snug);
2166
+ color: var(--kds-color-text-primary);
2167
+ }
2168
+
2169
+ .kds-secure-loader-message {
2170
+ margin: 0;
2171
+ font-family: var(--kds-font-family-secondary);
2172
+ font-size: var(--kds-font-size-body1);
2173
+ line-height: var(--kds-line-height-normal);
2174
+ color: var(--kds-color-primary-main);
2175
+ }
2176
+
2177
+ /* Caja del spinner con tamaño fijo: la animación no altera el layout.
2178
+ 6.25rem (100px) = tamaño del loader original (CircularProgress size=100). */
2179
+ .kds-secure-loader-spinner {
2180
+ position: relative;
2181
+ display: inline-grid;
2182
+ place-items: center;
2183
+ inline-size: var(--kds-secure-loader-size, 6.25rem);
2184
+ block-size: var(--kds-secure-loader-size, 6.25rem);
2185
+ }
2186
+
2187
+ /* Anillo: SVG nativo con arco vía stroke-dasharray, rotando. Replica el
2188
+ CircularProgress original (viewBox 44u, thickness 2, arco ~63%, disableShrink). */
2189
+ .kds-secure-loader-ring {
2190
+ inline-size: var(--kds-secure-loader-size, 6.25rem);
2191
+ block-size: var(--kds-secure-loader-size, 6.25rem);
2192
+ animation: kds-secure-loader-spin 1.2s linear infinite;
2193
+ }
2194
+
2195
+ .kds-secure-loader-ring circle {
2196
+ fill: none;
2197
+ stroke: var(--kds-color-primary-main);
2198
+ stroke-width: 2;
2199
+ stroke-linecap: round;
2200
+ /* circunferencia ≈ 2π·20.2 ≈ 127; arco visible ≈ 80 (~63%) */
2201
+ stroke-dasharray: 80 127;
2202
+ }
2203
+
2204
+ @keyframes kds-secure-loader-spin {
2205
+ to {
2206
+ transform: rotate(360deg);
2207
+ }
2208
+ }
2209
+
2210
+ /* Candado centrado sobre el anillo (~0.3 del diámetro). Centrado explícito
2211
+ (top/left 50% + translate) para que no dependa del grid ni se recorte. */
2212
+ .kds-secure-loader-lock {
2213
+ position: absolute;
2214
+ top: 50%;
2215
+ left: 50%;
2216
+ transform: translate(-50%, -50%);
2217
+ color: var(--kds-color-text-secondary);
2218
+ font-size: calc(var(--kds-secure-loader-size, 6.25rem) * 0.3);
2219
+ line-height: 1;
2220
+ pointer-events: none;
2221
+ }
2222
+
2223
+ /* ========================================
2224
+ KDS COPY BUTTON
2225
+ Chip compacto que muestra un valor + icono copy y lo copia al click.
2226
+ Acción inline (distinto de KdsCopyRow). Look del copy button de khenshin.
2227
+ ======================================== */
2228
+
2229
+ .kds-copy-button {
2230
+ display: inline-flex;
2231
+ align-items: center;
2232
+ gap: var(--kds-spacing-1);
2233
+ padding: var(--kds-spacing-0-25) var(--kds-spacing-1-5);
2234
+ /* No usar el alto de botón táctil de BeerCSS (~44px): es un chip compacto,
2235
+ su alto lo define el contenido + padding. */
2236
+ block-size: auto;
2237
+ min-block-size: 0;
2238
+ border: none;
2239
+ border-radius: var(--kds-radius-button);
2240
+ background: var(--kds-alert-info-bg);
2241
+ color: var(--kds-color-info-dark);
2242
+ font-family: var(--kds-font-family-secondary);
2243
+ font-size: var(--kds-font-size-sm);
2244
+ font-weight: var(--kds-font-weight-medium);
2245
+ line-height: var(--kds-line-height-normal);
2246
+ cursor: pointer;
2247
+ transition: background-color var(--kds-transition-shorter) var(--kds-easing-standard),
2248
+ color var(--kds-transition-shorter) var(--kds-easing-standard);
2249
+ }
2250
+
2251
+ /* active = hover: sin el aro de focus/active (outline morado de BeerCSS). */
2252
+ .kds-copy-button:focus,
2253
+ .kds-copy-button:focus-visible,
2254
+ .kds-copy-button:active {
2255
+ outline: none;
2256
+ }
2257
+
2258
+ /* value y copiedText se apilan en la MISMA celda de grid: la celda toma el
2259
+ ancho del texto más largo, por lo que el botón conserva su ancho al alternar
2260
+ entre "valor" y "copiado" (sin reflow). */
2261
+ .kds-copy-button-label {
2262
+ display: inline-grid;
2263
+ }
2264
+
2265
+ .kds-copy-button-value,
2266
+ .kds-copy-button-copied {
2267
+ grid-area: 1 / 1;
2268
+ white-space: nowrap;
2269
+ }
2270
+
2271
+ /* Estado por defecto: visible el valor, oculto (pero ocupando espacio) el copiado */
2272
+ .kds-copy-button-copied {
2273
+ visibility: hidden;
2274
+ }
2275
+
2276
+ /* Al copiar se invierte la visibilidad; el ancho no cambia */
2277
+ .kds-copy-button.copied .kds-copy-button-value {
2278
+ visibility: hidden;
2279
+ }
2280
+
2281
+ .kds-copy-button.copied .kds-copy-button-copied {
2282
+ visibility: visible;
2283
+ }
2284
+
2285
+ .kds-copy-button .material-symbols-outlined {
2286
+ font-size: 1rem;
2287
+ color: var(--kds-color-info-main);
2288
+ flex-shrink: 0;
2289
+ }
2290
+
2291
+ .kds-copy-button.copied {
2292
+ background: var(--kds-alert-success-bg);
2293
+ color: var(--kds-alert-success-text);
2294
+ animation: kds-copy-button-pulse var(--kds-transition-short) var(--kds-easing-standard);
2295
+ }
2296
+
2297
+ .kds-copy-button.copied .material-symbols-outlined {
2298
+ color: var(--kds-color-success-main);
2299
+ /* el check entra con un pop al copiar */
2300
+ animation: kds-copy-button-pop var(--kds-transition-short) var(--kds-easing-standard);
2301
+ }
2302
+
2303
+ @keyframes kds-copy-button-pulse {
2304
+ 0% {
2305
+ transform: scale(1);
2306
+ }
2307
+ 50% {
2308
+ transform: scale(1.04);
2309
+ }
2310
+ 100% {
2311
+ transform: scale(1);
2312
+ }
2313
+ }
2314
+
2315
+ @keyframes kds-copy-button-pop {
2316
+ 0% {
2317
+ transform: scale(0.3);
2318
+ opacity: 0;
2319
+ }
2320
+ 60% {
2321
+ transform: scale(1.25);
2322
+ opacity: 1;
2323
+ }
2324
+ 100% {
2325
+ transform: scale(1);
2326
+ }
2327
+ }
2328
+
2032
2329
  /* ========================================
2033
2330
  Link Styles
2034
2331
  ======================================== */
@@ -2130,6 +2427,16 @@ footer.primary a:hover {
2130
2427
  margin-right: var(--kds-spacing-2);
2131
2428
  }
2132
2429
 
2430
+ /* Ícono(s) en color primary. Para íconos MUI/SVG dentro del scope que, por el reset
2431
+ `.kds-theme-root svg { fill: currentColor }`, terminan heredando el color de texto
2432
+ (#333) en vez de su color propio — el `color="primary"` de MUI no le gana al contexto.
2433
+ Aplica al contenedor y a sus i/svg hijos. */
2434
+ .kds-icon-primary,
2435
+ .kds-icon-primary i,
2436
+ .kds-icon-primary svg {
2437
+ color: var(--kds-color-primary-main);
2438
+ }
2439
+
2133
2440
  /* Section title icon */
2134
2441
  .kds-stage-section-title i {
2135
2442
  vertical-align: middle;
@@ -2161,6 +2468,25 @@ body.dark {
2161
2468
  transition: border-color 0.2s, box-shadow 0.2s, outline-color 0.2s;
2162
2469
  }
2163
2470
 
2471
+ /* === TEXTAREA FIX === */
2472
+ /* BeerCSS base sets minimal block padding; add breathing room for textarea */
2473
+ .field > textarea {
2474
+ padding-block: 0.75rem;
2475
+ resize: vertical;
2476
+ }
2477
+
2478
+ .field.label > textarea {
2479
+ padding-block-start: 1.5rem;
2480
+ }
2481
+
2482
+ /* Label en reposo (estado "abajo", como placeholder): font-size menor al 1rem de BeerCSS
2483
+ para que un label largo no estorbe con un ícono (prefix/suffix) del input. Token sm
2484
+ (13–14px, siempre < 1rem del input incluso en desktop). Al flotar, BeerCSS lo reduce a
2485
+ .75rem con mayor especificidad, así que esto solo afecta el reposo. */
2486
+ .field.label > label {
2487
+ font-size: var(--kds-font-size-sm);
2488
+ }
2489
+
2164
2490
  /* ==========================================
2165
2491
  NOTCH VISIBILITY (opacity)
2166
2492
  ========================================== */
@@ -2282,6 +2608,23 @@ body.dark {
2282
2608
  color: var(--error); /* Rojo error */
2283
2609
  }
2284
2610
 
2611
+ /* ==========================================
2612
+ PASSWORD REVEAL TOGGLE (KdsTextField `revealable`)
2613
+ El <a role="button"> reusa el posicionamiento de icono de BeerCSS
2614
+ (.field > a, pointer-events: all). Aquí solo: color del icono
2615
+ (on-surface-variant) y un focus ring accesible para el toggle
2616
+ navegable por teclado (BeerCSS hace `outline: inherit` en <a>).
2617
+ ========================================== */
2618
+ .field > a.kds-field-reveal {
2619
+ color: var(--on-surface-variant);
2620
+ }
2621
+
2622
+ .field > a.kds-field-reveal:focus-visible {
2623
+ outline: 2px solid var(--primary);
2624
+ outline-offset: 2px;
2625
+ border-radius: 50%;
2626
+ }
2627
+
2285
2628
  /* ==========================================
2286
2629
  LABEL COLOR
2287
2630
  Mantener sincronizado con border
@@ -2389,6 +2732,71 @@ body.dark {
2389
2732
  display: inline-flex;
2390
2733
  }
2391
2734
 
2735
+ /* ========================================
2736
+ Radix Tooltip (.kds-tooltip)
2737
+ Espejo de `.tooltip` pero para popovers de Radix:
2738
+ bg oscuro, texto blanco, sombra + animación fade/slide direccional.
2739
+ ======================================== */
2740
+
2741
+ .kds-tooltip {
2742
+ background-color: var(--kds-color-text-primary);
2743
+ color: var(--kds-color-background-paper);
2744
+ font-family: var(--kds-font-family-primary);
2745
+ font-size: var(--kds-font-size-xs);
2746
+ font-weight: var(--kds-font-weight-medium);
2747
+ line-height: var(--kds-line-height-normal);
2748
+ border-radius: var(--kds-radius-sm);
2749
+ padding: var(--kds-spacing-0-75) var(--kds-spacing-1-5);
2750
+ box-shadow: var(--kds-shadow-4);
2751
+ max-width: 280px;
2752
+ z-index: var(--kds-z-index-tooltip, 1500);
2753
+ user-select: none;
2754
+ /* Animación direccional según Radix data-side — coordinada con copy-row
2755
+ (cubic-bezier 0.2, 0.8, 0.2, 1) para un feel consistente del DS. */
2756
+ animation-duration: 240ms;
2757
+ animation-timing-function: cubic-bezier(0.2, 0.8, 0.2, 1);
2758
+ will-change: transform, opacity;
2759
+ }
2760
+
2761
+ .kds-tooltip[data-state='closed'] {
2762
+ animation-duration: 140ms;
2763
+ animation-timing-function: cubic-bezier(0.4, 0, 0.6, 1);
2764
+ }
2765
+
2766
+ /* Direccional según data-side (Radix añade el attr en runtime).
2767
+ Cada lado entra desde su dirección con ~8px de offset (más visible que 4px). */
2768
+ .kds-tooltip[data-side='top'][data-state='delayed-open'],
2769
+ .kds-tooltip[data-side='top'][data-state='instant-open'] { animation-name: kds-tooltip-in-top; }
2770
+ .kds-tooltip[data-side='bottom'][data-state='delayed-open'],
2771
+ .kds-tooltip[data-side='bottom'][data-state='instant-open'] { animation-name: kds-tooltip-in-bottom; }
2772
+ .kds-tooltip[data-side='left'][data-state='delayed-open'],
2773
+ .kds-tooltip[data-side='left'][data-state='instant-open'] { animation-name: kds-tooltip-in-left; }
2774
+ .kds-tooltip[data-side='right'][data-state='delayed-open'],
2775
+ .kds-tooltip[data-side='right'][data-state='instant-open'] { animation-name: kds-tooltip-in-right; }
2776
+
2777
+ /* Fallback cuando no hay data-side resuelto aún */
2778
+ .kds-tooltip[data-state='delayed-open']:not([data-side]),
2779
+ .kds-tooltip[data-state='instant-open']:not([data-side]) {
2780
+ animation-name: kds-tooltip-in-top;
2781
+ }
2782
+
2783
+ .kds-tooltip[data-state='closed'] { animation-name: kds-tooltip-out; }
2784
+
2785
+ @keyframes kds-tooltip-in-top { from { opacity: 0; transform: translateY(8px) scale(0.96); } to { opacity: 1; transform: none; } }
2786
+ @keyframes kds-tooltip-in-bottom { from { opacity: 0; transform: translateY(-8px) scale(0.96); } to { opacity: 1; transform: none; } }
2787
+ @keyframes kds-tooltip-in-left { from { opacity: 0; transform: translateX(8px) scale(0.96); } to { opacity: 1; transform: none; } }
2788
+ @keyframes kds-tooltip-in-right { from { opacity: 0; transform: translateX(-8px) scale(0.96); } to { opacity: 1; transform: none; } }
2789
+
2790
+ @keyframes kds-tooltip-out {
2791
+ from { opacity: 1; transform: none; }
2792
+ to { opacity: 0; transform: scale(0.96); }
2793
+ }
2794
+
2795
+ /* Arrow (SVG path generado por Radix) */
2796
+ .kds-tooltip-arrow {
2797
+ fill: var(--kds-color-text-primary);
2798
+ }
2799
+
2392
2800
  /* ========================================
2393
2801
  Material Symbols Icon Sizing
2394
2802
  ======================================== */
@@ -2423,6 +2831,16 @@ body.dark {
2423
2831
  width: 100%;
2424
2832
  }
2425
2833
 
2834
+ /* Truncado en una línea con ellipsis. Incluye min-width:0 para que funcione
2835
+ dentro de contenedores flex/grid (el ítem puede encogerse por debajo de su
2836
+ contenido). Útil para nombres/asuntos largos en headers y filas. */
2837
+ .kds-truncate {
2838
+ min-width: 0;
2839
+ overflow: hidden;
2840
+ text-overflow: ellipsis;
2841
+ white-space: nowrap;
2842
+ }
2843
+
2426
2844
  .kds-max-w-sm {
2427
2845
  max-width: 400px;
2428
2846
  }
@@ -2466,6 +2884,11 @@ body.dark {
2466
2884
  display: flex;
2467
2885
  flex-direction: column;
2468
2886
  gap: var(--kds-spacing-3);
2887
+ /* Reset del fieldset nativo: sin borde/padding/margin por defecto del navegador. */
2888
+ margin: 0;
2889
+ padding: 0;
2890
+ border: 0;
2891
+ min-inline-size: 0;
2469
2892
  }
2470
2893
 
2471
2894
  /* Checkbox Label - BeerCSS override */
@@ -2477,7 +2900,7 @@ label.radio {
2477
2900
  border-radius: 8px;
2478
2901
  cursor: pointer;
2479
2902
  transition: background-color 0.2s;
2480
- font-size: 16px;
2903
+ font-size: var(--kds-font-size-sm);
2481
2904
  line-height: 1.5;
2482
2905
  color: var(--kds-color-text-primary);
2483
2906
  }
@@ -2516,20 +2939,56 @@ label.radio:has(input:disabled) {
2516
2939
  }
2517
2940
 
2518
2941
  /* ========================================
2519
- ONBOARDING COMPONENTS
2520
- Multi-stage onboarding flow with form validation
2942
+ RADIO GROUP — variante card (.kds-radio-group--card)
2943
+ Cada opción como fila con borde (estilo selección de banco/cuenta),
2944
+ manteniendo el control radio. Hover + opción seleccionada resaltada.
2521
2945
  ======================================== */
2522
-
2523
- /* Stage Layout - Individual stage wrapper */
2524
- .kds-stage {
2525
- max-width: var(--kds-onboarding-max-width);
2526
- margin: 0 auto;
2527
- width: 100%;
2946
+ .kds-radio-group--card {
2947
+ gap: var(--kds-spacing-2);
2528
2948
  }
2529
2949
 
2530
- /* Stage Header - Title and subtitle */
2531
- .kds-stage-header {
2532
- display: flex;
2950
+ .kds-radio-group--card label.radio {
2951
+ gap: var(--kds-spacing-1-5);
2952
+ /* Vertical con un poco más de aire; horizontal reducido para pegar el radio a la izquierda. */
2953
+ padding: var(--kds-spacing-2) var(--kds-spacing-1-5);
2954
+ /* Bordes con los valores del bank selector. */
2955
+ border: 1px solid var(--kds-color-border-subtle);
2956
+ border-radius: var(--kds-radius-card);
2957
+ background: var(--kds-color-surface);
2958
+ transition: border-color 0.2s, background 0.2s;
2959
+ }
2960
+
2961
+ /* Leve separación extra entre el radio y el texto de la opción. */
2962
+ .kds-radio-group--card label.radio span {
2963
+ margin-left: var(--kds-spacing-0-5);
2964
+ }
2965
+
2966
+ .kds-radio-group--card label.radio:hover {
2967
+ border-color: var(--kds-color-border-default);
2968
+ }
2969
+
2970
+ /* Seleccionada: poco color primario — solo un fondo faint sutil + borde gris
2971
+ (el punto del radio ya comunica la selección). */
2972
+ .kds-radio-group--card label.radio:has(input:checked) {
2973
+ border-color: var(--kds-color-border-default);
2974
+ background: var(--kds-color-primary-faint);
2975
+ }
2976
+
2977
+ /* ========================================
2978
+ ONBOARDING COMPONENTS
2979
+ Multi-stage onboarding flow with form validation
2980
+ ======================================== */
2981
+
2982
+ /* Stage Layout - Individual stage wrapper */
2983
+ .kds-stage {
2984
+ max-width: var(--kds-onboarding-max-width);
2985
+ margin: 0 auto;
2986
+ width: 100%;
2987
+ }
2988
+
2989
+ /* Stage Header - Title and subtitle */
2990
+ .kds-stage-header {
2991
+ display: flex;
2533
2992
  flex-direction: column;
2534
2993
  align-items: flex-start;
2535
2994
  gap: var(--kds-spacing-1);
@@ -3012,19 +3471,17 @@ footer > .kds-container-center {
3012
3471
  display: flex;
3013
3472
  align-items: center;
3014
3473
  gap: var(--kds-spacing-1);
3015
- padding: var(--kds-spacing-2);
3474
+ /* Padding compacto: vertical spacing-1 (8px), horizontal spacing-2 (16px). */
3475
+ padding: var(--kds-spacing-1) var(--kds-spacing-2);
3016
3476
  border-radius: var(--kds-radius-md);
3017
3477
  border: 1px solid;
3018
3478
  font-family: var(--kds-font-family-primary);
3019
3479
  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
- }
3480
+ /* Banner: ocupa siempre el ancho del contenedor. width explícito porque como
3481
+ flex item de una columna con align-items:center el cross-axis no se estira.
3482
+ Sin flex-grow para no estirarse verticalmente en contenedores flex-column. */
3483
+ width: 100%;
3484
+ flex: 0 1 auto;
3028
3485
  }
3029
3486
 
3030
3487
  .kds-alert:last-child {
@@ -3050,7 +3507,9 @@ footer > .kds-container-center {
3050
3507
  font-size: var(--kds-font-size-sm);
3051
3508
  font-weight: var(--kds-font-weight-semibold);
3052
3509
  line-height: var(--kds-line-height-normal);
3053
- margin-bottom: var(--kds-spacing-0-5);
3510
+ /* Solo margin-bottom: el padding del alert da el espaciado. Reset del resto para
3511
+ evitar el margin-block que BeerCSS aplica a <p> dentro del scope. */
3512
+ margin: 0 0 var(--kds-spacing-0-5);
3054
3513
  word-break: normal;
3055
3514
  overflow-wrap: break-word;
3056
3515
  }
@@ -3060,6 +3519,9 @@ footer > .kds-container-center {
3060
3519
  font-size: var(--kds-font-size-sm);
3061
3520
  font-weight: var(--kds-font-weight-regular);
3062
3521
  line-height: var(--kds-line-height-relaxed);
3522
+ /* Sin margen propio: el padding del alert es el único espaciado (evita el
3523
+ margin-block de <p> de BeerCSS dentro del scope). */
3524
+ margin: 0;
3063
3525
  word-break: normal;
3064
3526
  overflow-wrap: break-word;
3065
3527
  }
@@ -3105,6 +3567,32 @@ footer > .kds-container-center {
3105
3567
  color: var(--kds-color-error-dark);
3106
3568
  }
3107
3569
 
3570
+ /* Close button — icon-only, discreto, no usa kds-btn para no romper el flex layout del alert */
3571
+ .kds-alert-close {
3572
+ flex-shrink: 0;
3573
+ display: inline-flex;
3574
+ align-items: center;
3575
+ justify-content: center;
3576
+ width: 28px;
3577
+ height: 28px;
3578
+ padding: 0;
3579
+ background: transparent;
3580
+ border: 0;
3581
+ border-radius: 50%;
3582
+ color: inherit;
3583
+ cursor: pointer;
3584
+ transition: background-color 0.15s ease;
3585
+ }
3586
+
3587
+ .kds-alert-close:hover {
3588
+ background: rgba(0, 0, 0, 0.06);
3589
+ }
3590
+
3591
+ .kds-alert-close i {
3592
+ font-size: 18px;
3593
+ line-height: 1;
3594
+ }
3595
+
3108
3596
  /* Lists inside alerts */
3109
3597
  .kds-alert ul {
3110
3598
  padding-left: var(--kds-spacing-2);
@@ -4101,6 +4589,14 @@ dialog#surveyModal button.circle {
4101
4589
  text-align: center;
4102
4590
  }
4103
4591
 
4592
+ .kds-text-left {
4593
+ text-align: left;
4594
+ }
4595
+
4596
+ .kds-text-right {
4597
+ text-align: right;
4598
+ }
4599
+
4104
4600
  .kds-text-underline {
4105
4601
  text-decoration: underline;
4106
4602
  }
@@ -4113,10 +4609,62 @@ dialog#surveyModal button.circle {
4113
4609
  color: var(--on-surface-variant);
4114
4610
  }
4115
4611
 
4612
+ .kds-text-primary {
4613
+ color: var(--kds-color-primary-main);
4614
+ }
4615
+
4116
4616
  .kds-text-sm {
4117
4617
  font-size: var(--kds-font-size-sm);
4118
4618
  }
4119
4619
 
4620
+ /* ── Font-size scale utilities (additive — opt-in companions to .kds-text-sm) ── */
4621
+ .kds-text-base {
4622
+ font-size: var(--kds-font-size-base);
4623
+ }
4624
+
4625
+ .kds-text-lg {
4626
+ font-size: var(--kds-font-size-lg);
4627
+ }
4628
+
4629
+ .kds-text-xl {
4630
+ font-size: var(--kds-font-size-xl);
4631
+ }
4632
+
4633
+ .kds-text-2xl {
4634
+ font-size: var(--kds-font-size-2xl);
4635
+ }
4636
+
4637
+ /* ── Font-weight utilities ── */
4638
+ .kds-fw-regular {
4639
+ font-weight: var(--kds-font-weight-regular);
4640
+ }
4641
+
4642
+ .kds-fw-medium {
4643
+ font-weight: var(--kds-font-weight-medium);
4644
+ }
4645
+
4646
+ .kds-fw-semibold {
4647
+ font-weight: var(--kds-font-weight-semibold);
4648
+ }
4649
+
4650
+ .kds-fw-bold {
4651
+ font-weight: var(--kds-font-weight-bold);
4652
+ }
4653
+
4654
+ /* ── Text-transform utilities ── */
4655
+ .kds-text-transform-none {
4656
+ text-transform: none;
4657
+ }
4658
+
4659
+ /* ── Soft background utilities (semantic muted backgrounds for callouts) ── */
4660
+ .kds-bg-warning-soft {
4661
+ background-color: var(--kds-color-warning-soft);
4662
+ }
4663
+
4664
+ .kds-bg-info-soft {
4665
+ background-color: var(--kds-color-info-soft);
4666
+ }
4667
+
4120
4668
  /* ── Typography variants ── */
4121
4669
  .kds-text-display1 {
4122
4670
  font-family: var(--kds-font-family-primary, 'Public Sans', sans-serif);
@@ -4194,6 +4742,35 @@ dialog#surveyModal button.circle {
4194
4742
  text-transform: uppercase;
4195
4743
  }
4196
4744
 
4745
+ /* Value — dato/valor destacado (ej. nombre del comercio en el header del cobro). */
4746
+ .kds-text-value {
4747
+ font-family: var(--kds-font-family-primary, 'Public Sans', sans-serif);
4748
+ font-size: var(--kds-font-size-sm);
4749
+ font-weight: var(--kds-font-weight-semibold);
4750
+ line-height: var(--kds-line-height-relaxed);
4751
+ color: var(--kds-color-gray-800);
4752
+ }
4753
+
4754
+ /* Code — texto tipo código/referencia (mono, uppercase, gris), como el "CÓDIGO" del header. */
4755
+ .kds-text-code {
4756
+ font-family: var(--kds-font-family-mono);
4757
+ font-size: var(--kds-font-size-xs);
4758
+ font-weight: var(--kds-font-weight-regular);
4759
+ letter-spacing: var(--kds-letter-spacing-wider);
4760
+ font-variant-numeric: tabular-nums;
4761
+ color: var(--kds-color-gray-400);
4762
+ text-transform: uppercase;
4763
+ }
4764
+
4765
+ /* Caption — texto muy chico y liviano para pie de página (ej. número de versión). */
4766
+ .kds-text-caption {
4767
+ font-family: var(--kds-font-family-primary, 'Public Sans', sans-serif);
4768
+ font-size: var(--kds-font-size-2xs);
4769
+ font-weight: var(--kds-font-weight-regular);
4770
+ line-height: var(--kds-line-height-normal);
4771
+ color: var(--kds-color-gray-400);
4772
+ }
4773
+
4197
4774
  .kds-text-link {
4198
4775
  font-family: var(--kds-font-family-primary, 'Public Sans', sans-serif);
4199
4776
  font-size: inherit;
@@ -4207,6 +4784,12 @@ dialog#surveyModal button.circle {
4207
4784
  color: var(--kds-color-primary-dark, #5B3179);
4208
4785
  }
4209
4786
 
4787
+ /* Inline emphasis: bold weight only, inherits size/color/family from context so it
4788
+ works as a highlighted fragment inside any other variant (body, heading, etc.). */
4789
+ .kds-text-strong {
4790
+ font-weight: var(--kds-font-weight-bold, 700);
4791
+ }
4792
+
4210
4793
  /* Display utilities */
4211
4794
  .kds-hidden {
4212
4795
  display: none;
@@ -4327,7 +4910,9 @@ dialog#surveyModal button.circle {
4327
4910
  ======================================== */
4328
4911
 
4329
4912
  .kds-badge {
4330
- display: inline-block;
4913
+ display: inline-flex;
4914
+ align-items: center;
4915
+ gap: 4px;
4331
4916
  padding: 2px 8px;
4332
4917
  border-radius: var(--kds-radius-sm);
4333
4918
  font-size: var(--kds-font-size-xs);
@@ -4335,6 +4920,40 @@ dialog#surveyModal button.circle {
4335
4920
  line-height: 1.5;
4336
4921
  }
4337
4922
 
4923
+ /* Icon prefix inside chip — small inline icon */
4924
+ .kds-badge > i.material-symbols-outlined {
4925
+ font-size: 14px;
4926
+ line-height: 1;
4927
+ }
4928
+
4929
+ /* Deletable chip close button — discreto, sin kds-btn (eso lo agrandaba ridículo) */
4930
+ .kds-badge-close {
4931
+ display: inline-flex;
4932
+ align-items: center;
4933
+ justify-content: center;
4934
+ width: 16px;
4935
+ height: 16px;
4936
+ margin-right: -2px;
4937
+ padding: 0;
4938
+ background: transparent;
4939
+ border: 0;
4940
+ border-radius: 50%;
4941
+ color: inherit;
4942
+ cursor: pointer;
4943
+ opacity: 0.7;
4944
+ transition: opacity 0.15s, background-color 0.15s;
4945
+ }
4946
+
4947
+ .kds-badge-close:hover {
4948
+ opacity: 1;
4949
+ background: rgba(0, 0, 0, 0.1);
4950
+ }
4951
+
4952
+ .kds-badge-close i {
4953
+ font-size: 14px;
4954
+ line-height: 1;
4955
+ }
4956
+
4338
4957
  .kds-badge.success {
4339
4958
  background-color: var(--kds-color-success-container);
4340
4959
  color: var(--kds-color-success-dark);
@@ -4546,21 +5165,46 @@ dialog#surveyModal button.circle {
4546
5165
  flex-shrink: 0;
4547
5166
  }
4548
5167
 
4549
- /* Merchant tile with logo variant */
5168
+ /* Merchant tile with logo variant — el logo (avatar del comercio) llena TODO el tile.
5169
+ `display: block` evita el desborde del auto-grid; `overflow: hidden` recorta la imagen
5170
+ a las esquinas redondeadas del tile. La imagen va CUADRADA (sin border-radius propio):
5171
+ solo el contenedor recorta, evitando el hairline sub-pixel del doble redondeo. El fondo
5172
+ se deja transparente bajo el logo como respaldo (no debe verse púrpura en los bordes). */
4550
5173
  .kds-merchant-tile.logo {
4551
- padding: var(--kds-spacing-0-5);
5174
+ padding: 0;
5175
+ display: block;
5176
+ overflow: hidden;
5177
+ background: transparent;
4552
5178
  }
4553
5179
 
4554
5180
  .kds-merchant-tile.logo img {
4555
5181
  width: 100%;
4556
5182
  height: 100%;
4557
5183
  object-fit: cover;
4558
- border-radius: var(--kds-radius-md);
5184
+ border-radius: 0;
5185
+ display: block;
5186
+ }
5187
+
5188
+ /* Variante para logos que traen su propio marco/forma (logos de comercio/servicio,
5189
+ no fotos): `contain` + padding para que el logo entre completo sin recortar sus
5190
+ esquinas. Opt-in: pasar className="kds-merchant-tile--contain". */
5191
+ .kds-merchant-tile.logo.kds-merchant-tile--contain {
5192
+ padding: var(--kds-spacing-1);
5193
+ }
5194
+ .kds-merchant-tile.logo.kds-merchant-tile--contain img {
5195
+ object-fit: contain;
4559
5196
  }
4560
5197
 
4561
5198
  .kds-merchant-meta {
4562
5199
  display: flex;
4563
5200
  flex-direction: column;
5201
+ gap: var(--kds-spacing-0-5);
5202
+ }
5203
+
5204
+ /* El meta controla su propio espaciado: anula el margin sibling de BeerCSS
5205
+ (`… + :is(p,h*,…) { margin-block-start: 1rem }`) que separaba de más las líneas. */
5206
+ .kds-merchant-meta > * {
5207
+ margin-block: 0 !important;
4564
5208
  }
4565
5209
 
4566
5210
  .kds-merchant-label {
@@ -4613,6 +5257,11 @@ dialog#surveyModal button.circle {
4613
5257
  color: var(--kds-color-text-primary);
4614
5258
  }
4615
5259
 
5260
+ /* Opt-in color modifier — switches the value to the LigoPay/khipu-blue tone */
5261
+ .kds-amount-value--info {
5262
+ color: var(--kds-color-info-blue); /* #5A5FE0 */
5263
+ }
5264
+
4616
5265
  .kds-amount-value small {
4617
5266
  font-size: 18px;
4618
5267
  font-weight: 500;
@@ -4898,6 +5547,144 @@ dialog#surveyModal button.circle {
4898
5547
  box-shadow: 0 -4px 16px rgba(16,24,40,.08);
4899
5548
  }
4900
5549
 
5550
+ /* ========================================
5551
+ 08b - FAB (.kds-fab)
5552
+ Circular, icon-only floating action button with an animated hide state.
5553
+ Presentational: drive `.kds-fab--hidden` from useHideOnScroll for scroll-away.
5554
+ ======================================== */
5555
+
5556
+ .kds-fab {
5557
+ display: inline-flex;
5558
+ align-items: center;
5559
+ justify-content: center;
5560
+ width: var(--kds-spacing-5);
5561
+ height: var(--kds-spacing-5);
5562
+ padding: 0;
5563
+ border: none;
5564
+ border-radius: var(--kds-radius-full);
5565
+ background: var(--kds-color-background-paper);
5566
+ color: var(--kds-color-text-primary);
5567
+ box-shadow: var(--kds-shadow-elevated);
5568
+ cursor: pointer;
5569
+ z-index: 100;
5570
+ /* Neutralize native button rendering so the pressed state doesn't flash a square. */
5571
+ -webkit-appearance: none;
5572
+ appearance: none;
5573
+ -webkit-tap-highlight-color: transparent;
5574
+ transition: opacity var(--kds-transition-short) ease, transform var(--kds-transition-short) ease,
5575
+ background-color var(--kds-transition-shorter) ease;
5576
+ }
5577
+
5578
+ /* Keep the circle in EVERY interaction state. Uses `button.kds-fab` (element + class) to
5579
+ out-specify BeerCSS's base `button`/`button:active` rules — same trick as `.kds-btn`. */
5580
+ button.kds-fab,
5581
+ button.kds-fab:hover,
5582
+ button.kds-fab:active,
5583
+ button.kds-fab:focus,
5584
+ button.kds-fab:focus-visible {
5585
+ border-radius: var(--kds-radius-full);
5586
+ background: var(--kds-color-background-paper);
5587
+ }
5588
+
5589
+ button.kds-fab:hover:not(:disabled) {
5590
+ background: var(--kds-color-background-muted);
5591
+ }
5592
+
5593
+ /* Subtle press feedback that preserves the round shape. */
5594
+ button.kds-fab:active:not(:disabled) {
5595
+ transform: translateY(var(--kds-spacing-0-25));
5596
+ }
5597
+
5598
+ button.kds-fab:focus-visible {
5599
+ outline: var(--kds-spacing-0-25) solid var(--kds-color-primary-main);
5600
+ outline-offset: var(--kds-spacing-0-25);
5601
+ }
5602
+
5603
+ .kds-fab > i.material-symbols-outlined {
5604
+ font-size: var(--kds-spacing-button-icon-size);
5605
+ line-height: 1;
5606
+ }
5607
+
5608
+ /* Pinned corners — fixed to the viewport (stays visible on scroll-up, iframe-friendly).
5609
+ Horizontally the top-right FAB hugs the right edge of the centered content column set via
5610
+ `--kds-fab-column-width` (default 100% → viewport edge). The consumer sets that var to its
5611
+ widget width so on desktop the FAB overlaps the header's top-right corner instead of drifting
5612
+ to the far viewport edge. `max(...)` keeps at least a token inset on narrow (mobile) viewports. */
5613
+ .kds-fab--top-right,
5614
+ .kds-fab--top-left,
5615
+ .kds-fab--bottom-right,
5616
+ .kds-fab--bottom-left {
5617
+ position: fixed;
5618
+ }
5619
+
5620
+ .kds-fab--top-right {
5621
+ top: var(--kds-spacing-2);
5622
+ right: max(var(--kds-spacing-2), calc((100% - var(--kds-fab-column-width, 100%)) / 2 + var(--kds-spacing-2)));
5623
+ }
5624
+
5625
+ .kds-fab--top-left {
5626
+ top: var(--kds-spacing-2);
5627
+ left: max(var(--kds-spacing-2), calc((100% - var(--kds-fab-column-width, 100%)) / 2 + var(--kds-spacing-2)));
5628
+ }
5629
+
5630
+ .kds-fab--bottom-right {
5631
+ bottom: var(--kds-spacing-2);
5632
+ right: max(var(--kds-spacing-2), calc((100% - var(--kds-fab-column-width, 100%)) / 2 + var(--kds-spacing-2)));
5633
+ }
5634
+
5635
+ .kds-fab--bottom-left {
5636
+ bottom: var(--kds-spacing-2);
5637
+ left: max(var(--kds-spacing-2), calc((100% - var(--kds-fab-column-width, 100%)) / 2 + var(--kds-spacing-2)));
5638
+ }
5639
+
5640
+ /* Desktop: nudge the top-right FAB up so it sits partially over the header's top edge. */
5641
+ @media (min-width: 768px) {
5642
+ .kds-fab--top-right {
5643
+ top: var(--kds-spacing-1);
5644
+ }
5645
+ }
5646
+
5647
+ /* Corner-overlap: mount the FAB centered OVER the top-right corner of the content column
5648
+ (`--kds-fab-column-width`), overlapping outward — vs `--top-right` which insets inside the
5649
+ column. Mobile keeps a small inset from the viewport via the `--kds-fab-inset` token. */
5650
+ .kds-fab--corner {
5651
+ --kds-fab-inset: 5px;
5652
+ position: fixed;
5653
+ top: var(--kds-fab-inset);
5654
+ right: var(--kds-fab-inset);
5655
+ }
5656
+
5657
+ @media (min-width: 768px) {
5658
+ .kds-fab--corner {
5659
+ right: max(
5660
+ var(--kds-fab-inset),
5661
+ calc((100% - var(--kds-fab-column-width, 100%)) / 2 - var(--kds-spacing-5) / 2)
5662
+ );
5663
+ }
5664
+ }
5665
+
5666
+ /* Hidden (scroll-away) state — fade + slide toward the nearest edge */
5667
+ .kds-fab--hidden {
5668
+ opacity: 0;
5669
+ transform: translateY(calc(-1 * var(--kds-spacing-1))) scale(0.9);
5670
+ pointer-events: none;
5671
+ }
5672
+
5673
+ .kds-fab--bottom-right.kds-fab--hidden,
5674
+ .kds-fab--bottom-left.kds-fab--hidden {
5675
+ transform: translateY(var(--kds-spacing-1)) scale(0.9);
5676
+ }
5677
+
5678
+ @media (prefers-reduced-motion: reduce) {
5679
+ .kds-fab {
5680
+ transition: opacity var(--kds-transition-short) ease;
5681
+ }
5682
+
5683
+ .kds-fab--hidden {
5684
+ transform: none;
5685
+ }
5686
+ }
5687
+
4901
5688
  /* ========================================
4902
5689
  09 - SUCCESS MARK (.kds-success-mark)
4903
5690
  Large success icon for completion screens
@@ -4977,22 +5764,22 @@ dialog#surveyModal button.circle {
4977
5764
  padding: var(--kds-spacing-1) 0;
4978
5765
  }
4979
5766
 
4980
- /* Desktop: footer inside card hidden; Mobile: footer outside card hidden */
5767
+ /* Footer SIEMPRE fuera del card body (desktop y mobile): el footer interno
5768
+ (variant="inside") queda oculto en todos los breakpoints; se muestra el
5769
+ externo, hermano directo del card bajo `.kds-screen`. */
4981
5770
  .kds-card-elevated > .kds-secure-footer {
4982
5771
  display: none;
4983
5772
  }
4984
5773
 
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
- }
5774
+ .kds-screen > form {
5775
+ margin-block-start: 0 !important;
5776
+ }
4992
5777
 
4993
- .kds-screen > form {
4994
- margin-block-start: 0 !important;
4995
- }
5778
+ /* Footer externo adyacente a la card: cancela el gap del .kds-screen para que
5779
+ quede pegado debajo de la card y no flotando lejos. Las dos cards
5780
+ (header/body) sí conservan su separación. */
5781
+ .kds-screen > .kds-secure-footer {
5782
+ margin-top: calc(-1 * var(--kds-spacing-3));
4996
5783
  }
4997
5784
 
4998
5785
  .kds-secure-footer-lock {
@@ -5014,6 +5801,32 @@ dialog#surveyModal button.circle {
5014
5801
  display: block;
5015
5802
  object-fit: contain;
5016
5803
  overflow: visible;
5804
+ /* Corrección óptica: la masa visible del wordmark (el cuerpo de "Khipu") queda sobre el
5805
+ centro geométrico de su viewBox (45x15) porque el descendente de la "p" ocupa la parte
5806
+ baja. Al centrar por caja (align-items:center) el logo se ve ~1px alto respecto al texto,
5807
+ así que se baja 0.75px para alinear el centro óptico. Alto fijo (11px) → nudge consistente. */
5808
+ position: relative;
5809
+ top: 0.75px;
5810
+ }
5811
+
5812
+ /* Divisor entre el logo de Khipu y el del PSP ("Khipu | klap") */
5813
+ .kds-secure-footer-sep {
5814
+ width: 1px;
5815
+ height: 0.85em;
5816
+ background: currentColor;
5817
+ opacity: 0.35;
5818
+ flex: 0 0 auto;
5819
+ }
5820
+
5821
+ /* Logo del PSP (proveedor de pagos): lo provee el consumidor, lo alineamos al mismo
5822
+ alto que el mark de Khipu. Sirve para <img> o un elemento con .kds-psp-mark. */
5823
+ .kds-secure-footer img,
5824
+ .kds-secure-footer .kds-psp-mark {
5825
+ height: 11px;
5826
+ width: auto;
5827
+ flex: 0 0 auto;
5828
+ display: block;
5829
+ object-fit: contain;
5017
5830
  }
5018
5831
 
5019
5832
  .kds-secure-footer-code {
@@ -5029,26 +5842,45 @@ dialog#surveyModal button.circle {
5029
5842
  background: none;
5030
5843
  border: 0;
5031
5844
  padding: 0;
5845
+ /* Reset del bare `button { height: 2.5rem }` de BeerCSS (scoped en .kds-theme-root):
5846
+ sin esto el toggle quedaba en una caja de 40px con el texto centrado → espacio
5847
+ irregular arriba (pegado al resumen) y abajo (~26px hasta el panel). Con height
5848
+ auto el botón ajusta a su contenido y el ritmo lo gobiernan los márgenes. */
5849
+ height: auto;
5032
5850
  color: var(--kds-color-primary-main);
5033
5851
  font-weight: var(--kds-font-weight-medium);
5034
5852
  font-size: var(--kds-font-size-sm);
5035
- padding-bottom: var(--kds-spacing-1-25);
5036
5853
  cursor: pointer;
5037
5854
  display: inline-flex;
5038
5855
  align-items: center;
5039
5856
  gap: 0;
5040
5857
  }
5041
5858
 
5042
- .kds-expand-toggle:hover {
5859
+ .kds-expand-toggle:hover,
5860
+ .kds-expand-toggle:focus,
5861
+ .kds-expand-toggle:active {
5043
5862
  background: transparent;
5863
+ outline: none;
5864
+ box-shadow: none;
5044
5865
  }
5045
5866
 
5046
- .kds-expand-toggle:hover span {
5867
+ .kds-expand-toggle:hover span,
5868
+ .kds-expand-toggle:focus span,
5869
+ .kds-expand-toggle:active span {
5047
5870
  text-decoration: underline;
5048
5871
  }
5049
5872
 
5050
5873
  .kds-expand-toggle i {
5051
5874
  font-size: var(--kds-font-size-base);
5875
+ /* BeerCSS estiliza el `i` bare como caja flex de 30px (height/width/min: 30px), que
5876
+ inflaba el toggle y dejaba aire vertical de más alrededor del label. La achicamos a
5877
+ 1.25em MANTENIENDO el centrado flex de BeerCSS (display:flex/align/justify) — así el
5878
+ caret queda compacto Y ópticamente centrado con el texto (no usar height:auto ni
5879
+ line-height:1: descentran el glifo). */
5880
+ width: 1.25em;
5881
+ height: 1.25em;
5882
+ min-width: 0;
5883
+ min-height: 0;
5052
5884
  transition: transform var(--kds-transition-shorter, 0.2s);
5053
5885
  }
5054
5886
 
@@ -5068,7 +5900,10 @@ dialog#surveyModal button.circle {
5068
5900
  }
5069
5901
 
5070
5902
  .kds-expand-panel.open {
5071
- max-height: 800px;
5903
+ /* Fallback cap for no-JS consumers. With JS (React useExpandToggle / vanilla
5904
+ initExpandToggle) the panel's inline max-height is set to its exact scrollHeight,
5905
+ which overrides this and prevents clipping regardless of content length. */
5906
+ max-height: 2000px;
5072
5907
  margin-top: 0;
5073
5908
  }
5074
5909
 
@@ -5186,13 +6021,17 @@ dialog#surveyModal button.circle {
5186
6021
 
5187
6022
  .kds-status-block {
5188
6023
  text-align: center;
5189
- padding: var(--kds-spacing-6) 0;
6024
+ /* Padding vertical estándar: spacing-2 (16px), el mismo rhythm inter-elementos
6025
+ que usan kds-btn-stack y kds-hr-dashed. NO usar spacing-6 (48px): genera
6026
+ demasiado espacio cuando el bloque se compone con header/divider alrededor. */
6027
+ padding: var(--kds-spacing-2) 0;
5190
6028
  }
5191
6029
 
5192
6030
  .kds-status-block-icon {
5193
6031
  width: var(--kds-status-icon-size);
5194
6032
  height: var(--kds-status-icon-size);
5195
- margin: 0 auto var(--kds-spacing-3);
6033
+ /* spacing-1 (8px) icono→título: elementos relacionados dentro del bloque. */
6034
+ margin: 0 auto var(--kds-spacing-1);
5196
6035
  border-radius: var(--kds-radius-full);
5197
6036
  display: grid;
5198
6037
  place-items: center;
@@ -5208,12 +6047,18 @@ dialog#surveyModal button.circle {
5208
6047
  font-weight: var(--kds-font-weight-bold);
5209
6048
  color: var(--kds-color-text-primary);
5210
6049
  margin: 0 0 var(--kds-spacing-1);
6050
+ /* El reset global :where(h2) usa estas vars con !important; así se overridea. */
6051
+ --kds-typography-h2-margin-block-end: var(--kds-spacing-1);
5211
6052
  }
5212
6053
 
5213
6054
  .kds-status-block-description {
5214
6055
  font-size: var(--kds-font-size-sm);
5215
6056
  color: var(--kds-color-text-secondary);
5216
6057
  margin: 0;
6058
+ /* !important: anula el auto-espaciado de hermanos de BeerCSS
6059
+ (margin-block-start: 1rem, especificidad 0,5,1 imposible de igualar).
6060
+ La separación título→descripción la da el margin-bottom del título. */
6061
+ margin-top: 0 !important;
5217
6062
  }
5218
6063
 
5219
6064
  /* Variant: success */
@@ -5223,11 +6068,11 @@ dialog#surveyModal button.circle {
5223
6068
  color: var(--kds-color-success-main);
5224
6069
  }
5225
6070
 
5226
- /* Variant: pending (animated spinner) */
6071
+ /* Variant: pending (animated blue spinner, sin icono interno) */
5227
6072
  .kds-status-block[data-status="pending"] .kds-status-block-icon {
5228
6073
  background: transparent;
5229
6074
  border: 3px solid var(--kds-color-divider);
5230
- border-top-color: var(--kds-color-primary-main);
6075
+ border-top-color: var(--kds-color-info-main);
5231
6076
  animation: kds-spin 1s linear infinite;
5232
6077
  }
5233
6078
 
@@ -5245,12 +6090,21 @@ dialog#surveyModal button.circle {
5245
6090
  color: var(--kds-color-error-main);
5246
6091
  }
5247
6092
 
5248
- /* Inline variant: icon left + text right */
6093
+ /* Variant: info — usar SIEMPRE con icon "info_i" (i minúscula sin glyph circular)
6094
+ para que solo se vea nuestro círculo decorativo. Iconos como "info" tienen
6095
+ su propio círculo built-in → causarían doble círculo. */
6096
+ .kds-status-block[data-status="info"] .kds-status-block-icon {
6097
+ background: var(--kds-color-info-soft);
6098
+ border: var(--kds-border-width-md) solid var(--kds-color-info-main);
6099
+ color: var(--kds-color-info-main);
6100
+ }
6101
+
6102
+ /* Inline variant: icon left + text right */
5249
6103
  .kds-status-block.inline {
5250
6104
  text-align: left;
5251
6105
  display: flex;
5252
6106
  align-items: center;
5253
- gap: var(--kds-spacing-3);
6107
+ gap: var(--kds-spacing-2);
5254
6108
  padding: var(--kds-spacing-1) 0;
5255
6109
  }
5256
6110
 
@@ -5292,6 +6146,9 @@ dialog#surveyModal button.circle {
5292
6146
  .kds-bottom-sheet {
5293
6147
  width: 100%;
5294
6148
  max-width: var(--kds-bottom-sheet-max-width);
6149
+ max-height: 90dvh;
6150
+ display: flex;
6151
+ flex-direction: column;
5295
6152
  background: var(--kds-color-background-paper);
5296
6153
  border-radius: var(--kds-radius-2xl) var(--kds-radius-2xl) 0 0;
5297
6154
  padding: var(--kds-spacing-3-5) var(--kds-spacing-3) var(--kds-spacing-3);
@@ -5312,6 +6169,42 @@ dialog#surveyModal button.circle {
5312
6169
  background: var(--kds-color-divider);
5313
6170
  }
5314
6171
 
6172
+ /* Close button — top-right discreto, NO usa kds-btn (rompía el flex layout) */
6173
+ .kds-bottom-sheet-close {
6174
+ position: absolute;
6175
+ top: var(--kds-spacing-1);
6176
+ right: var(--kds-spacing-1);
6177
+ width: 32px;
6178
+ height: 32px;
6179
+ display: inline-flex;
6180
+ align-items: center;
6181
+ justify-content: center;
6182
+ padding: 0;
6183
+ background: transparent;
6184
+ border: 0;
6185
+ border-radius: 50%;
6186
+ color: var(--kds-color-text-secondary);
6187
+ cursor: pointer;
6188
+ transition: background-color 0.15s ease;
6189
+ }
6190
+
6191
+ .kds-bottom-sheet-close:hover {
6192
+ background: rgba(0, 0, 0, 0.06);
6193
+ }
6194
+
6195
+ .kds-bottom-sheet-close i {
6196
+ font-size: 20px;
6197
+ line-height: 1;
6198
+ }
6199
+
6200
+ /* Body container — separa el content del title/actions */
6201
+ .kds-bottom-sheet-body {
6202
+ text-align: left;
6203
+ margin-top: var(--kds-spacing-1);
6204
+ overflow-y: auto;
6205
+ min-height: 0;
6206
+ }
6207
+
5315
6208
  .kds-bottom-sheet-icon {
5316
6209
  font-size: var(--kds-font-size-4xl);
5317
6210
  }
@@ -5358,6 +6251,19 @@ dialog#surveyModal button.circle {
5358
6251
  to { opacity: 1; }
5359
6252
  }
5360
6253
 
6254
+ /* Botón con llenado progresivo (feedback visual de auto-cierre).
6255
+ Duración parametrizable con --kds-fill-duration (default 12s). */
6256
+ .kds-btn-fill-progress {
6257
+ background-image: linear-gradient(to right, var(--kds-color-primary-dark), var(--kds-color-primary-dark));
6258
+ background-repeat: no-repeat;
6259
+ background-size: 0% 100%;
6260
+ animation: kds-btn-fill var(--kds-fill-duration, 12s) linear forwards;
6261
+ }
6262
+
6263
+ @keyframes kds-btn-fill {
6264
+ to { background-size: 100% 100%; }
6265
+ }
6266
+
5361
6267
  @keyframes kds-rise {
5362
6268
  from { transform: translateY(var(--kds-spacing-2-5)); opacity: 0.4; }
5363
6269
  to { transform: none; opacity: 1; }
@@ -5545,6 +6451,30 @@ dialog#surveyModal button.circle {
5545
6451
  /* No max-height — clip-path on parent handles visual collapse without layout changes */
5546
6452
  .kds-invoice-sticky .kds-invoice-collapsible {
5547
6453
  opacity: calc(1 - var(--collapse-progress) * 1.5);
6454
+
6455
+ /* Scroll interno discreto (mobile): cuando el detalle abierto excede el viewport, el
6456
+ collapsible scrollea internamente (monto/merchant quedan fijos arriba) para poder leer
6457
+ todo el detalle. Al tocar el fondo, `overscroll-behavior: auto` ENCADENA el gesto al
6458
+ scroll del body → dispara useStickyInvoiceCollapse → colapsa el header y aparece el form
6459
+ (flujo natural). Se auto-desactiva con el detalle cerrado (contenido más corto que el
6460
+ max-height → sin scroll ni efecto visual). */
6461
+ max-height: calc(100vh - var(--kds-spacing-12) - var(--kds-spacing-4));
6462
+ max-height: calc(100dvh - var(--kds-spacing-12) - var(--kds-spacing-4));
6463
+ overflow-y: auto;
6464
+ overflow-x: hidden;
6465
+ overscroll-behavior-y: auto;
6466
+ scrollbar-width: thin;
6467
+ scrollbar-color: var(--kds-color-gray-300) transparent;
6468
+ }
6469
+ .kds-invoice-sticky .kds-invoice-collapsible::-webkit-scrollbar {
6470
+ width: 4px;
6471
+ }
6472
+ .kds-invoice-sticky .kds-invoice-collapsible::-webkit-scrollbar-thumb {
6473
+ background: var(--kds-color-gray-300);
6474
+ border-radius: var(--kds-radius-full);
6475
+ }
6476
+ .kds-invoice-sticky .kds-invoice-collapsible::-webkit-scrollbar-track {
6477
+ background: transparent;
5548
6478
  }
5549
6479
 
5550
6480
  /* Amount: 30px → 24px (progressive) */
@@ -5616,6 +6546,14 @@ dialog#surveyModal button.circle {
5616
6546
  margin-top: var(--kds-spacing-1);
5617
6547
  }
5618
6548
 
6549
+ /* Ritmo uniforme alrededor del toggle: spacing-2 arriba (resumen↔"Ver detalle")
6550
+ iguala el padding-top spacing-2 que la .kds-detail-list da debajo
6551
+ ("Ver detalle"↔primer label del panel). Antes el alto fijo del bare button
6552
+ (40px) hacía que el espacio de arriba y de abajo fueran distintos. */
6553
+ .kds-invoice-collapsible > .kds-expand-toggle {
6554
+ margin-top: var(--kds-spacing-2);
6555
+ }
6556
+
5619
6557
  /* -- Invoice Summary -- */
5620
6558
  .kds-invoice-summary {
5621
6559
  display: flex;
@@ -5639,6 +6577,22 @@ div.kds-invoice-header,
5639
6577
  padding: 0;
5640
6578
  border-radius: 0;
5641
6579
  align-content: initial;
6580
+ /* Override BeerCSS `header { min-height: ... }` — el header debe ajustarse a su
6581
+ contenido (sigue el alto del merchant tile: 64px→50px al colapsar). Sin esto
6582
+ quedaba un min-height fijo de 64px → hueco muerto debajo al colapsar en mobile. */
6583
+ min-height: 0;
6584
+ }
6585
+
6586
+ /* Mobile: center-align el header (merchant tile vs bloque monto+código) tanto
6587
+ expandido como colapsado. Al quitar el logo del header, el bloque de texto quedaba
6588
+ más bajo que el merchant tile y con `flex-start` dejaba hueco abajo; centrado los
6589
+ alinea. CSS-only (no depende de la clase .is-collapsed, que el JS de collapse no setea). */
6590
+ @media (max-width: 767px) {
6591
+ header.kds-invoice-header,
6592
+ div.kds-invoice-header,
6593
+ .kds-invoice-header {
6594
+ align-items: center;
6595
+ }
5642
6596
  }
5643
6597
 
5644
6598
  /* Reset BeerCSS sibling margin on <p> inside invoice header — specificity (0,4,1) ties BeerCSS, wins by source order */
@@ -5648,7 +6602,7 @@ div.kds-invoice-header,
5648
6602
  }
5649
6603
 
5650
6604
  .kds-invoice-amount {
5651
- font-weight: var(--kds-font-weight-bold);
6605
+ font-weight: var(--kds-font-weight-semibold);
5652
6606
  font-size: var(--kds-font-size-3xl);
5653
6607
  line-height: var(--kds-line-height-tight);
5654
6608
  letter-spacing: var(--kds-letter-spacing-wide);
@@ -5670,12 +6624,16 @@ div.kds-invoice-header,
5670
6624
  text-transform: uppercase;
5671
6625
  margin: 0;
5672
6626
  margin-block: unset;
5673
- margin: var(--kds-spacing-1) 0;
6627
+ display: flex;
6628
+ gap: var(--kds-spacing-1-5);
5674
6629
  }
5675
6630
 
5676
6631
  .kds-invoice-code-value {
6632
+ color: var(--kds-color-gray-400);
6633
+ }
6634
+
6635
+ .kds-invoice-code-value--lowercase {
5677
6636
  text-transform: lowercase;
5678
- color: var(--kds-text-primary);
5679
6637
  }
5680
6638
 
5681
6639
  .kds-invoice-merchant {
@@ -5687,6 +6645,9 @@ div.kds-invoice-header,
5687
6645
  place-items: center;
5688
6646
  color: #fff;
5689
6647
  flex-shrink: 0;
6648
+ /* Clip the slightly-oversized logo (see `.kds-invoice-merchant img`) to the
6649
+ rounded square so it can fully cover the deep-purple background. */
6650
+ overflow: hidden;
5690
6651
  }
5691
6652
 
5692
6653
  .kds-invoice-merchant i {
@@ -5706,27 +6667,43 @@ div.kds-invoice-header,
5706
6667
  }
5707
6668
 
5708
6669
  .kds-invoice-merchant img {
5709
- width: 100%;
5710
- height: 100%;
6670
+ /* Slightly larger than the container (+2px, centered by the grid) so the
6671
+ logo overflows under the parent's rounded clip and the deep-purple
6672
+ background never peeks through at the corners when the logo shrinks. */
6673
+ width: calc(100% + 2px);
6674
+ height: calc(100% + 2px);
5711
6675
  object-fit: cover;
5712
6676
  border-radius: var(--kds-radius-md);
5713
6677
  }
5714
6678
 
5715
6679
  /* -- Card Title -- */
5716
6680
  .kds-card-title {
5717
- font-weight: 700;
6681
+ font-weight: var(--kds-font-weight-semibold);
5718
6682
  font-size: var(--kds-font-size-base);
5719
6683
  line-height: 1.5;
5720
6684
  letter-spacing: -0.31px;
5721
6685
  color: var(--kds-color-text-primary);
5722
- margin-bottom: var(--kds-spacing-2) !important;
6686
+ /* sin !important: no hay regla de margin-bottom para h1 en BeerCSS que competir, y así un
6687
+ consumidor puede anularlo (p.ej. FormTitle dentro de un Stack que ya da el espacio). */
6688
+ margin-bottom: var(--kds-spacing-2);
6689
+ }
6690
+
6691
+ /* Opt-in size modifiers (additive — do NOT change default 16/600) */
6692
+ .kds-card-title--lg {
6693
+ font-size: var(--kds-font-size-xl); /* 20px */
6694
+ font-weight: var(--kds-font-weight-semibold);
6695
+ }
6696
+
6697
+ .kds-card-title--xl {
6698
+ font-size: var(--kds-font-size-2xl); /* 24px */
6699
+ font-weight: var(--kds-font-weight-semibold);
5723
6700
  }
5724
6701
 
5725
6702
  /* -- Key-Value Grid -- */
5726
6703
  .kds-kv {
5727
6704
  display: grid;
5728
6705
  grid-template-columns: auto 1fr;
5729
- column-gap: var(--kds-spacing-1-5);
6706
+ column-gap: var(--kds-spacing-1);
5730
6707
  align-items: baseline;
5731
6708
  }
5732
6709
 
@@ -5743,7 +6720,7 @@ div.kds-invoice-header,
5743
6720
  margin: 0;
5744
6721
  font-size: var(--kds-font-size-sm);
5745
6722
  line-height: var(--kds-line-height-relaxed);
5746
- font-weight: var(--kds-font-weight-bold);
6723
+ font-weight: var(--kds-font-weight-semibold);
5747
6724
  letter-spacing: normal;
5748
6725
  color: var(--kds-color-gray-800);
5749
6726
  min-width: 0;
@@ -5771,16 +6748,28 @@ div.kds-invoice-header,
5771
6748
  .kds-detail-list {
5772
6749
  display: flex;
5773
6750
  flex-direction: column;
5774
- gap: var(--kds-spacing-1-5);
6751
+ /* spacing-2 entre grupos del detalle (separación estándar entre piezas de info
6752
+ distintas, ej. "Emisor cobro" vs "Descripción"). El padding-top da la misma
6753
+ separación spacing-2 arriba del primer grupo (Asunto↔Emisor cobro). */
6754
+ gap: var(--kds-spacing-2);
5775
6755
  margin: 0;
5776
- padding: 0 0 var(--kds-spacing-2);
6756
+ padding: var(--kds-spacing-2) 0 0;
5777
6757
  }
5778
6758
 
5779
- .kds-detail-group {
5780
- margin-bottom: var(--kds-spacing-1-5);
6759
+ @media (max-width: 767px) {
6760
+ .kds-detail-list {
6761
+ padding-bottom: var(--kds-spacing-2);
6762
+ }
5781
6763
  }
5782
6764
 
5783
- .kds-detail-group:last-child {
6765
+ .kds-detail-group {
6766
+ /* El espacio ENTRE grupos lo da el `gap` de .kds-detail-list (spacing-2).
6767
+ Dentro del grupo, label↔value llevan un gap chico (spacing-0-5) para un
6768
+ ritmo parejo: antes el dt iba pegado al dd (0px) y se veía desigual frente
6769
+ a los 16px entre los demás elementos del panel. */
6770
+ display: flex;
6771
+ flex-direction: column;
6772
+ gap: var(--kds-spacing-0-5);
5784
6773
  margin-bottom: 0;
5785
6774
  }
5786
6775
 
@@ -5789,6 +6778,7 @@ div.kds-invoice-header,
5789
6778
  letter-spacing: var(--kds-letter-spacing-wide);
5790
6779
  color: var(--kds-color-text-hint);
5791
6780
  text-transform: uppercase;
6781
+ margin: 0;
5792
6782
  }
5793
6783
 
5794
6784
  .kds-detail-group dd {
@@ -5798,6 +6788,19 @@ div.kds-invoice-header,
5798
6788
  color: var(--kds-color-text-primary);
5799
6789
  }
5800
6790
 
6791
+ /* Imagen adjunta del cobro dentro del panel de detalle (ej. comprobante/producto).
6792
+ Reemplaza los inline styles del antiguo modal MerchantInfo: ancho contenido, borde
6793
+ y radio del DS. Va como un grupo más de la .kds-detail-list (hereda su gap). */
6794
+ .kds-detail-image {
6795
+ display: block;
6796
+ max-width: 100%;
6797
+ width: 160px;
6798
+ height: auto;
6799
+ object-fit: contain;
6800
+ border-radius: var(--kds-radius-md);
6801
+ border: 1px solid var(--kds-color-border);
6802
+ }
6803
+
5801
6804
  /* -- Amount Summary Row -- */
5802
6805
  .kds-amount-row {
5803
6806
  display: flex;
@@ -5857,16 +6860,16 @@ div.kds-invoice-header,
5857
6860
  font-size: var(--kds-font-size-sm);
5858
6861
  }
5859
6862
 
5860
- .kds-recap-list .k {
6863
+ .kds-recap-list .kds-key {
5861
6864
  color: var(--kds-color-text-secondary);
5862
6865
  }
5863
6866
 
5864
- .kds-recap-list .v {
6867
+ .kds-recap-list .kds-value {
5865
6868
  color: var(--kds-color-text-primary);
5866
6869
  font-weight: 500;
5867
6870
  }
5868
6871
 
5869
- .kds-recap-list .v.placeholder {
6872
+ .kds-recap-list .kds-value.placeholder {
5870
6873
  color: var(--kds-color-text-hint);
5871
6874
  }
5872
6875
 
@@ -5893,6 +6896,72 @@ a.kds-btn-success:disabled {
5893
6896
  box-shadow: none;
5894
6897
  }
5895
6898
 
6899
+ /* -- Button Warning (amber CTA) -- */
6900
+ button.kds-btn-warning,
6901
+ a.kds-btn-warning {
6902
+ background: var(--kds-color-warning-main);
6903
+ color: var(--kds-color-warning-contrast);
6904
+ border: none;
6905
+ }
6906
+
6907
+ button.kds-btn-warning:hover:not(:disabled),
6908
+ a.kds-btn-warning:hover:not(:disabled) {
6909
+ background: var(--kds-color-warning-dark);
6910
+ }
6911
+
6912
+ button.kds-btn-warning:active:not(:disabled),
6913
+ button.kds-btn-warning:focus-visible:not(:disabled),
6914
+ a.kds-btn-warning:active:not(:disabled),
6915
+ a.kds-btn-warning:focus-visible:not(:disabled) {
6916
+ background: var(--kds-color-warning-dark);
6917
+ outline: 2px solid var(--kds-color-warning-main);
6918
+ outline-offset: 2px;
6919
+ }
6920
+
6921
+ button.kds-btn-warning:disabled,
6922
+ a.kds-btn-warning:disabled {
6923
+ background-color: var(--kds-color-action-disabled-bg);
6924
+ color: var(--kds-color-action-disabled);
6925
+ box-shadow: none;
6926
+ }
6927
+
6928
+ button.kds-btn-warning::after {
6929
+ display: none;
6930
+ }
6931
+
6932
+ /* -- Button Error (red / destructive CTA) -- */
6933
+ button.kds-btn-error,
6934
+ a.kds-btn-error {
6935
+ background: var(--kds-color-error-main);
6936
+ color: var(--kds-color-error-contrast);
6937
+ border: none;
6938
+ }
6939
+
6940
+ button.kds-btn-error:hover:not(:disabled),
6941
+ a.kds-btn-error:hover:not(:disabled) {
6942
+ background: var(--kds-color-error-dark);
6943
+ }
6944
+
6945
+ button.kds-btn-error:active:not(:disabled),
6946
+ button.kds-btn-error:focus-visible:not(:disabled),
6947
+ a.kds-btn-error:active:not(:disabled),
6948
+ a.kds-btn-error:focus-visible:not(:disabled) {
6949
+ background: var(--kds-color-error-dark);
6950
+ outline: 2px solid var(--kds-color-error-main);
6951
+ outline-offset: 2px;
6952
+ }
6953
+
6954
+ button.kds-btn-error:disabled,
6955
+ a.kds-btn-error:disabled {
6956
+ background-color: var(--kds-color-action-disabled-bg);
6957
+ color: var(--kds-color-action-disabled);
6958
+ box-shadow: none;
6959
+ }
6960
+
6961
+ button.kds-btn-error::after {
6962
+ display: none;
6963
+ }
6964
+
5896
6965
  /* -- Field Helper Text -- */
5897
6966
  .kds-field-helper {
5898
6967
  font-size: var(--kds-font-size-xs);
@@ -5935,6 +7004,8 @@ button.kds-btn-success::after {
5935
7004
  /* -- Alert inline (borderless, used inside cards) -- */
5936
7005
  .kds-alert-inline {
5937
7006
  border: none;
7007
+ padding: var(--kds-spacing-1) var(--kds-spacing-1-5);
7008
+ margin-bottom: var(--kds-spacing-1-5);
5938
7009
  }
5939
7010
 
5940
7011
  /* -- Card dimmed (behind overlays) -- */
@@ -6036,17 +7107,42 @@ button.kds-btn-success::after {
6036
7107
  }
6037
7108
 
6038
7109
  .kds-bill-description-picture {
6039
- width: 120px;
6040
- height: 120px;
7110
+ max-width: 100px;
6041
7111
  object-fit: contain;
6042
7112
  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);
7113
+ margin-top: var(--kds-spacing-1-25);
6046
7114
  display: block;
6047
7115
  box-sizing: content-box;
6048
7116
  }
6049
7117
 
7118
+ /* -- Bill Attachments -- */
7119
+ .kds-bill-attachments {
7120
+ display: flex;
7121
+ flex-direction: column;
7122
+ gap: var(--kds-spacing-0-75);
7123
+ }
7124
+
7125
+ .kds-bill-attachment {
7126
+ display: inline-flex;
7127
+ align-items: center;
7128
+ justify-content: flex-start;
7129
+ width: fit-content;
7130
+ color: var(--kds-color-info-main);
7131
+ font-size: var(--kds-font-size-sm);
7132
+ font-weight: var(--kds-font-weight-medium);
7133
+ text-decoration: none;
7134
+ }
7135
+
7136
+ .kds-bill-attachment:hover span {
7137
+ text-decoration: underline;
7138
+ }
7139
+
7140
+ .kds-bill-attachment i {
7141
+ font-size: var(--kds-font-size-lg);
7142
+ min-width: unset;
7143
+ justify-content: flex-start;
7144
+ }
7145
+
6050
7146
  /* -- Share Card (mustContinue) -- */
6051
7147
  .kds-share-card {
6052
7148
  margin-top: var(--kds-spacing-2);
@@ -6156,8 +7252,10 @@ button.kds-btn-success::after {
6156
7252
  Desktop (min-width: 768px) restores padding + full border-radius. */
6157
7253
  .kds-payment-flow .kds-card-elevated {
6158
7254
  margin-top: 0;
6159
- padding-top: 0;
6160
- padding-bottom: 0;
7255
+ /* Vertical > lateral: la card del flujo respira arriba/abajo (spacing-3)
7256
+ manteniendo el padding lateral del base (spacing-2 / spacing-3 mobile). */
7257
+ padding-top: var(--kds-spacing-3);
7258
+ padding-bottom: var(--kds-spacing-3);
6161
7259
  }
6162
7260
 
6163
7261
  /* Reset BeerCSS sibling margin on form/section inside payment cards.
@@ -6172,12 +7270,43 @@ button.kds-btn-success::after {
6172
7270
  padding-bottom: 0;
6173
7271
  }
6174
7272
 
6175
- /* Payment stage full-viewport centered wrapper */
7273
+ /* Body card: alto mínimo estable para que NO salte entre estados de distinta altura
7274
+ (loader/progress ↔ formulario). Solo la body card = hijo directo de `.kds-screen`
7275
+ (los headers van dentro de `.kds-invoice-sticky-wrap`, no la afecta).
7276
+ Flex-column para que el contenido quede arriba y el footer interno (versión / secure
7277
+ footer) pueda pinearse al fondo con `margin-top: auto` aunque el contenido sea corto. */
7278
+ .kds-screen > .kds-card-elevated {
7279
+ min-height: 36rem;
7280
+ display: flex;
7281
+ flex-direction: column;
7282
+ /* mismo radio que el header (invoice-sticky usa radius-card); el base card-elevated
7283
+ es radius-md (8px) y quedaba distinto al header en desktop. */
7284
+ border-radius: var(--kds-radius-card);
7285
+ }
7286
+
7287
+ /* Variante sin radio superior: pantallas de resultado (éxito/fallo/timeout) que van
7288
+ SIN header arriba — la body card va sola y su tope queda plano (flush) en vez de
7289
+ con esquinas redondeadas sueltas. El padding del card sigue manejando el inset. */
7290
+ /* Tope plano SOLO en mobile (< 768px): la body card se pega al borde superior del widget
7291
+ (igual que el header sticky, que también aplana el tope solo en mobile). En desktop la card
7292
+ va centrada y separada, así que conserva el border-radius superior redondeado. */
7293
+ @media (max-width: 767px) {
7294
+ .kds-screen > .kds-card-elevated.kds-card-elevated--flush-top,
7295
+ .kds-card-elevated.kds-card-elevated--flush-top {
7296
+ border-top-left-radius: 0;
7297
+ border-top-right-radius: 0;
7298
+ }
7299
+ }
7300
+
7301
+ /* Payment stage — full-viewport centered wrapper.
7302
+ Fondo muted (gris) para que las cards blancas (header + body) y el gap entre
7303
+ ellas se distingan; sin esto, card blanca sobre fondo blanco se funden. */
6176
7304
  .kds-payment-stage {
6177
7305
  min-height: 100vh;
6178
7306
  display: flex;
6179
7307
  justify-content: center;
6180
7308
  padding: var(--kds-spacing-2-5) var(--kds-spacing-2) var(--kds-spacing-8);
7309
+ background: var(--kds-color-background-muted);
6181
7310
  }
6182
7311
 
6183
7312
  /* Payment flow — narrow column */
@@ -6216,10 +7345,16 @@ button.kds-btn-success::after {
6216
7345
  padding-bottom: var(--kds-spacing-2);
6217
7346
  }
6218
7347
 
6219
- /* Invoice sticky card: progressive padding-top (expanded 20px → collapsed 8px).
6220
- Overrides base rule at same specificity; source order wins here. */
7348
+ /* Invoice sticky card padding (mobile).
7349
+ Top: estático 12px. Bottom: progresivo 16px (expandido, = desktop) → 8px (colapsado).
7350
+ El padding-bottom animado se reduce al colapsar — sin depender de que el clip-path
7351
+ corte el padding abruptamente. */
6221
7352
  .kds-payment-flow .kds-card-elevated.kds-invoice-sticky {
6222
7353
  padding-top: var(--kds-spacing-1-5);
7354
+ padding-bottom: calc(
7355
+ var(--kds-spacing-2)
7356
+ - var(--kds-spacing-1) * var(--collapse-progress, 0)
7357
+ );
6223
7358
  }
6224
7359
 
6225
7360
 
@@ -6531,11 +7666,8 @@ button.kds-btn-success::after {
6531
7666
  display: none;
6532
7667
  }
6533
7668
 
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 {
7669
+ /* Search field reutilizable (standalone o dentro del KdsBankModal). */
7670
+ .kds-search-field {
6539
7671
  width: 100%;
6540
7672
  padding: var(--kds-spacing-1-25) var(--kds-spacing-1-5);
6541
7673
  border: 1px solid var(--kds-color-divider);
@@ -6547,15 +7679,57 @@ button.kds-btn-success::after {
6547
7679
  background: var(--kds-color-surface);
6548
7680
  }
6549
7681
 
6550
- .kds-bank-modal-search input::placeholder {
7682
+ .kds-search-field::placeholder {
6551
7683
  color: var(--kds-color-text-hint);
6552
7684
  }
6553
7685
 
6554
- .kds-bank-modal-search input:focus {
7686
+ .kds-search-field:focus {
6555
7687
  border-color: var(--kds-color-primary-main);
6556
7688
  box-shadow: 0 0 0 2px var(--kds-color-primary-faint);
6557
7689
  }
6558
7690
 
7691
+ /* Variante con ícono (withIcon): lupa posicionada DENTRO del input, a la derecha. */
7692
+ .kds-search-field-wrapper {
7693
+ position: relative;
7694
+ width: 100%;
7695
+ }
7696
+
7697
+ .kds-search-field-icon {
7698
+ position: absolute;
7699
+ right: var(--kds-spacing-1-5);
7700
+ top: 50%;
7701
+ transform: translateY(-50%);
7702
+ font-size: 20px;
7703
+ color: var(--kds-color-text-hint);
7704
+ pointer-events: none;
7705
+ }
7706
+
7707
+ /* Espacio a la derecha del texto para no solaparse con la lupa. */
7708
+ .kds-search-field-wrapper .kds-search-field {
7709
+ padding-right: calc(var(--kds-spacing-1-5) + var(--kds-spacing-3));
7710
+ }
7711
+
7712
+ /* Variante angosta del field (prop `narrow` de KdsTextField): ancho reducido y texto
7713
+ centrado, para inputs de pocos caracteres dispuestos en fila (ej. segmentos de
7714
+ coordenadas o código). No crece ni se encoge dentro de un flex row. */
7715
+ .field.kds-field--narrow {
7716
+ width: 6rem;
7717
+ flex: 0 0 auto;
7718
+ /* En fila horizontal no aplica el margin vertical que BeerCSS da a fields
7719
+ hermanos (regla de "flow" `... + :is(.field,...)`, specificity ~0,3,1);
7720
+ sin el reset los siguientes quedan desalineados hacia abajo. `!important`
7721
+ para ganarle a esa regla de framework. */
7722
+ margin-block: 0 !important;
7723
+ }
7724
+
7725
+ .field.kds-field--narrow > input {
7726
+ text-align: center;
7727
+ }
7728
+
7729
+ .kds-bank-modal-search {
7730
+ padding: 0 var(--kds-spacing-2) var(--kds-spacing-1-5);
7731
+ }
7732
+
6559
7733
  .kds-bank-modal-body {
6560
7734
  flex: 1;
6561
7735
  overflow-y: auto;
@@ -6653,7 +7827,7 @@ button.kds-btn-success::after {
6653
7827
  min-width: 0;
6654
7828
  }
6655
7829
 
6656
- .kds-qr-text .title {
7830
+ .kds-qr-text .kds-qr-title {
6657
7831
  font-family: var(--kds-font-family-primary);
6658
7832
  font-weight: 600;
6659
7833
  font-size: var(--kds-font-size-sm);
@@ -6661,10 +7835,15 @@ button.kds-btn-success::after {
6661
7835
  color: var(--kds-color-text-primary);
6662
7836
  }
6663
7837
 
6664
- .kds-qr-text .sub {
7838
+ .kds-qr-text .kds-qr-subtitle {
6665
7839
  font-family: var(--kds-font-family-primary);
6666
7840
  font-size: var(--kds-font-size-caption);
6667
7841
  color: var(--kds-color-text-secondary);
7842
+ /* Trunca con ellipsis en lugar de wrap a 2 líneas — evita apretar el row
7843
+ cuando el contenedor es angosto (mobile o decorator constrained). */
7844
+ overflow: hidden;
7845
+ text-overflow: ellipsis;
7846
+ white-space: nowrap;
6668
7847
  }
6669
7848
 
6670
7849
  /* QR Badge - "Rápido" indicator */
@@ -6687,46 +7866,26 @@ button.kds-btn-success::after {
6687
7866
  font-size: 20px;
6688
7867
  }
6689
7868
 
6690
- /* Mobile responsive: Ajustes para pantallas pequeñas */
7869
+ /* Mobile responsive (≤480px): respira más mantenemos padding/gap/avatar igual
7870
+ que desktop y solo agregamos truncation al subtitle y `flex-shrink: 0` en los
7871
+ ítems que deben mantener su tamaño (avatar, badge, chevron). */
6691
7872
  @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
7873
  .kds-qr-avatar {
6698
- width: var(--kds-spacing-4-5);
6699
- height: var(--kds-spacing-4-5);
6700
7874
  flex-shrink: 0;
6701
7875
  }
6702
7876
 
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
7877
  .kds-qr-text .sub {
6714
- font-size: var(--kds-font-size-caption);
6715
- line-height: var(--kds-line-height-tight);
6716
7878
  overflow: hidden;
6717
7879
  text-overflow: ellipsis;
6718
7880
  white-space: nowrap;
6719
7881
  }
6720
7882
 
6721
7883
  .kds-qr-badge {
6722
- font-size: var(--kds-font-size-caption);
6723
- padding: var(--kds-spacing-0-25) var(--kds-spacing-0-75);
6724
7884
  flex-shrink: 0;
6725
7885
  }
6726
7886
 
6727
7887
  .kds-qr-row .material-symbols-outlined:last-child,
6728
7888
  .kds-qr-row .material-icons-round:last-child {
6729
- font-size: var(--kds-font-size-base);
6730
7889
  flex-shrink: 0;
6731
7890
  }
6732
7891
  }
@@ -6755,7 +7914,11 @@ button.kds-btn-success::after {
6755
7914
  border-radius: var(--kds-spacing-0-75);
6756
7915
  font-size: var(--kds-font-size-sm);
6757
7916
  cursor: pointer;
6758
- transition: background 0.12s ease;
7917
+ /* Transición más amplia y suave: bg + color + border ease-out */
7918
+ transition:
7919
+ background-color 280ms cubic-bezier(0.2, 0.8, 0.2, 1),
7920
+ color 280ms cubic-bezier(0.2, 0.8, 0.2, 1),
7921
+ border-top-color 280ms ease-out;
6759
7922
  position: relative;
6760
7923
  user-select: none;
6761
7924
  }
@@ -6764,19 +7927,21 @@ button.kds-btn-success::after {
6764
7927
  border-top: 0;
6765
7928
  }
6766
7929
 
6767
- .kds-copyable-table-row:hover {
7930
+ /* Hover NO aplica mientras la row esté en `.copied` o `.settling`
7931
+ (evita el flicker del hover state al desaparecer el verde de "copiado") */
7932
+ .kds-copyable-table-row:not(.copied):not(.settling):hover {
6768
7933
  background: var(--kds-color-primary-faint);
6769
7934
  }
6770
7935
 
6771
- .kds-copyable-table-row:hover + .kds-copyable-table-row {
7936
+ .kds-copyable-table-row:not(.copied):not(.settling):hover + .kds-copyable-table-row {
6772
7937
  border-top-color: transparent;
6773
7938
  }
6774
7939
 
6775
- .kds-copyable-table-row:active {
7940
+ .kds-copyable-table-row:not(.copied):not(.settling):active {
6776
7941
  background: var(--kds-color-primary-selected);
6777
7942
  }
6778
7943
 
6779
- .kds-copyable-table-row .k {
7944
+ .kds-copyable-table-row .kds-key {
6780
7945
  color: var(--kds-color-text-secondary);
6781
7946
  font-size: var(--kds-font-size-caption);
6782
7947
  letter-spacing: 0.3px;
@@ -6785,7 +7950,7 @@ button.kds-btn-success::after {
6785
7950
  flex: 0 0 auto;
6786
7951
  }
6787
7952
 
6788
- .kds-copyable-table-row .v {
7953
+ .kds-copyable-table-row .kds-value {
6789
7954
  color: var(--kds-color-text-primary);
6790
7955
  font-family: 'JetBrains Mono', 'SF Mono', ui-monospace, Menlo, Consolas, monospace;
6791
7956
  font-weight: var(--kds-font-weight-medium);
@@ -6801,17 +7966,18 @@ button.kds-btn-success::after {
6801
7966
  flex: 1 1 auto;
6802
7967
  }
6803
7968
 
6804
- .kds-copyable-table-row .v::after {
7969
+ .kds-copyable-table-row .kds-value::after {
6805
7970
  content: 'content_copy';
6806
7971
  font-family: 'Material Symbols Outlined';
6807
7972
  font-size: var(--kds-font-size-sm);
6808
7973
  color: var(--kds-color-primary-main);
6809
7974
  opacity: 0;
6810
- transition: opacity 0.12s ease;
7975
+ /* Transición coordinada con la row */
7976
+ transition: opacity 280ms cubic-bezier(0.2, 0.8, 0.2, 1);
6811
7977
  flex: 0 0 auto;
6812
7978
  }
6813
7979
 
6814
- .kds-copyable-table-row:hover .v::after {
7980
+ .kds-copyable-table-row:not(.copied):not(.settling):hover .kds-value::after {
6815
7981
  opacity: 0.7;
6816
7982
  }
6817
7983
 
@@ -6819,22 +7985,76 @@ button.kds-btn-success::after {
6819
7985
  background: var(--kds-color-success-soft);
6820
7986
  }
6821
7987
 
6822
- .kds-copyable-table-row.copied .v {
7988
+ .kds-copyable-table-row.copied .kds-value {
6823
7989
  color: var(--kds-color-success-dark);
6824
7990
  }
6825
7991
 
6826
- .kds-copyable-table-row.copied .v::after {
7992
+ .kds-copyable-table-row.copied .kds-value::after {
6827
7993
  content: 'check';
6828
7994
  font-family: 'Material Symbols Outlined';
6829
7995
  color: var(--kds-color-success-dark);
6830
7996
  opacity: 1;
6831
7997
  }
6832
7998
 
7999
+ /* `.settling`: estado de "recién copiado" — la row vuelve al normal sin
8000
+ activar hover. Dura lo mismo que la transición (~300ms) y se aplica
8001
+ desde React tras quitar `.copied`. */
8002
+ .kds-copyable-table-row.settling .kds-value::after {
8003
+ /* Mantén el check con opacity 0 mientras settling, fade out suave */
8004
+ opacity: 0;
8005
+ }
8006
+
6833
8007
  /* Disable BeerCSS ripple on copyable table rows */
6834
8008
  .kds-copyable-table-row::after {
6835
8009
  display: none;
6836
8010
  }
6837
8011
 
8012
+ /* ========================================
8013
+ GRID VARIANT (.kds-copyable-table--grid)
8014
+ Read-only grid of N text cells per row. No copy,
8015
+ no role=button, no "copy all". Cells share the row
8016
+ width equally. Used inside radio options, etc.
8017
+ ======================================== */
8018
+
8019
+ /* La grilla es solo columnas de texto (como el DataTable original): sin caja,
8020
+ borde, fondo ni sombra del contenedor copyable. */
8021
+ .kds-copyable-table--grid {
8022
+ border: 0;
8023
+ border-radius: 0;
8024
+ background: none;
8025
+ padding: 0;
8026
+ box-shadow: none;
8027
+ }
8028
+
8029
+ .kds-copyable-table-row--grid {
8030
+ display: flex;
8031
+ align-items: center;
8032
+ gap: var(--kds-spacing-1-5);
8033
+ padding: 0;
8034
+ /* Sin el divisor border-top que trae la fila base del copyable. */
8035
+ border-top: 0;
8036
+ cursor: default;
8037
+ user-select: text;
8038
+ }
8039
+
8040
+ /* Separación entre filas de la grilla (sin divisores). */
8041
+ .kds-copyable-table-row--grid + .kds-copyable-table-row--grid {
8042
+ margin-top: var(--kds-spacing-0-5);
8043
+ }
8044
+
8045
+ .kds-grid-cell {
8046
+ flex: 1 1 0;
8047
+ min-width: 0;
8048
+ font-size: var(--kds-font-size-sm);
8049
+ font-weight: var(--kds-font-weight-medium);
8050
+ color: var(--kds-color-text-primary);
8051
+ word-break: break-word;
8052
+ }
8053
+
8054
+ .kds-grid-cell--disabled {
8055
+ color: var(--kds-color-text-disabled);
8056
+ }
8057
+
6838
8058
  /* ========================================
6839
8059
  COPY-ALL BUTTON (.kds-copy-all-btn)
6840
8060
  Modifier for kds-btn kds-btn-outlined that adds
@@ -6892,6 +8112,14 @@ a.kds-copy-all-btn.copied:hover {
6892
8112
  min-height: unset;
6893
8113
  }
6894
8114
 
8115
+ /* Severity variants — default stays warning (backward-compatible) */
8116
+ .kds-section-note.kds-info { color: var(--kds-color-info-dark); }
8117
+ .kds-section-note.kds-info > i { color: var(--kds-color-info-dark); }
8118
+ .kds-section-note.kds-success { color: var(--kds-color-success-dark); }
8119
+ .kds-section-note.kds-success > i { color: var(--kds-color-success-dark); }
8120
+ .kds-section-note.kds-error { color: var(--kds-color-error-dark); }
8121
+ .kds-section-note.kds-error > i { color: var(--kds-color-error-dark); }
8122
+
6895
8123
  /* ========================================
6896
8124
  INFO TOOLTIP (.kds-info-tip)
6897
8125
  Inline info button with hover/click tooltip bubble.
@@ -6987,7 +8215,7 @@ a.kds-copy-all-btn.copied:hover {
6987
8215
  .kds-monto-row {
6988
8216
  display: flex;
6989
8217
  justify-content: space-between;
6990
- align-items: flex-end;
8218
+ align-items: flex-start;
6991
8219
  padding: var(--kds-spacing-1-75) 0 var(--kds-spacing-1);
6992
8220
  border-top: 1px dashed var(--kds-color-divider);
6993
8221
  margin-top: var(--kds-spacing-1-75);
@@ -7011,3 +8239,191 @@ a.kds-copy-all-btn.copied:hover {
7011
8239
  color: var(--kds-color-text-primary);
7012
8240
  }
7013
8241
 
8242
+ /* ========================================
8243
+ PAYMENT TOTAL (.kds-payment-total)
8244
+ Bloque "monto a pagar" para QR view y email templates.
8245
+ Portado desde paylink-ligopay → <kh:paymentTotal> + materialize-config.css.
8246
+ Variantes:
8247
+ - default: amount grande (3rem) en color primario Khipu.
8248
+ - email: amount compacto (1.5rem) en color texto primario.
8249
+ Responsive (default): mobile (≤ 1024px) centra y oculta titleMobile.
8250
+ ======================================== */
8251
+
8252
+ .kds-payment-total {
8253
+ /* Component-scoped tokens — overrideables sin tocar el CSS global. */
8254
+ --kds-payment-total-title-size: var(--kds-font-size-2xl); /* 24px */
8255
+ --kds-payment-total-label-size: var(--kds-font-size-xl); /* 20px */
8256
+ --kds-payment-amount-size: 3rem; /* 48px — destacado */
8257
+ --kds-payment-amount-line-height: var(--kds-line-height-snug);
8258
+ --kds-payment-amount-color: var(--kds-color-primary-main);
8259
+ --kds-payment-amount-weight: var(--kds-font-weight-medium);
8260
+
8261
+ text-align: left;
8262
+ }
8263
+
8264
+ .kds-payment-total-title,
8265
+ .kds-payment-total-title-mobile {
8266
+ font-size: var(--kds-payment-total-title-size);
8267
+ font-weight: var(--kds-font-weight-semibold);
8268
+ color: var(--kds-color-text-primary);
8269
+ margin-bottom: var(--kds-spacing-4);
8270
+ }
8271
+
8272
+ .kds-payment-total-title-mobile {
8273
+ display: none;
8274
+ }
8275
+
8276
+ .kds-payment-total .kds-payment-label {
8277
+ font-size: var(--kds-payment-total-label-size);
8278
+ font-weight: var(--kds-font-weight-semibold);
8279
+ color: var(--kds-color-text-primary);
8280
+ margin-bottom: var(--kds-spacing-1);
8281
+ padding-top: 0;
8282
+ }
8283
+
8284
+ .kds-payment-total .kds-payment-amount {
8285
+ font-size: var(--kds-payment-amount-size);
8286
+ font-weight: var(--kds-payment-amount-weight);
8287
+ line-height: var(--kds-payment-amount-line-height);
8288
+ color: var(--kds-payment-amount-color);
8289
+ margin-bottom: var(--kds-spacing-4);
8290
+ }
8291
+
8292
+ .kds-payment-total .kds-payment-amount .kds-payment-total-decimal-sup,
8293
+ .kds-invoice-amount .kds-payment-total-decimal-sup {
8294
+ font-size: var(--kds-font-size-decimal-sup);
8295
+ font-weight: var(--kds-font-weight-semibold);
8296
+ position: relative;
8297
+ top: 0;
8298
+ }
8299
+
8300
+ /* Variante email: amount compacto en color texto primario, alineación izquierda fija */
8301
+ .kds-payment-total--email {
8302
+ --kds-payment-amount-size: var(--kds-font-size-2xl); /* 24px */
8303
+ --kds-payment-amount-line-height: var(--kds-line-height-snug);
8304
+ --kds-payment-amount-color: var(--kds-color-text-primary);
8305
+ --kds-payment-amount-weight: var(--kds-font-weight-semibold);
8306
+ }
8307
+
8308
+ /* Tone modifier: swaps the amount color to the khipu-blue / LigoPay info blue.
8309
+ Combinable with --email or with the default variant. */
8310
+ .kds-payment-total--tone-info {
8311
+ --kds-payment-amount-color: var(--kds-color-info-blue); /* #5A5FE0 */
8312
+ }
8313
+
8314
+ /* Centered modifier: removes the asymmetric desktop padding (which exists for
8315
+ LigoPay QR view) and centers all internal text. Composable with --email and
8316
+ --tone-info. */
8317
+ .kds-payment-total--centered {
8318
+ padding-left: 0;
8319
+ padding-top: 0;
8320
+ text-align: center;
8321
+ }
8322
+ .kds-payment-total--centered .kds-payment-total-title,
8323
+ .kds-payment-total--centered .kds-payment-total-title-mobile,
8324
+ .kds-payment-total--centered .kds-payment-label,
8325
+ .kds-payment-total--centered .kds-payment-amount {
8326
+ text-align: center;
8327
+ }
8328
+
8329
+ /* Responsive: mobile ≤ 1024px — centra y conmuta los títulos (sólo variante default) */
8330
+ @media (max-width: 1024px) {
8331
+ .kds-payment-total {
8332
+ text-align: center;
8333
+ }
8334
+
8335
+ .kds-payment-total .kds-payment-total-title {
8336
+ display: none;
8337
+ }
8338
+
8339
+ .kds-payment-total .kds-payment-total-title-mobile {
8340
+ display: block;
8341
+ text-align: center;
8342
+ }
8343
+
8344
+ .kds-payment-total .kds-payment-label,
8345
+ .kds-payment-total .kds-payment-amount {
8346
+ text-align: center;
8347
+ }
8348
+
8349
+ /* Email mantiene alineación izquierda incluso en mobile (matchea email clients) */
8350
+ .kds-payment-total--email,
8351
+ .kds-payment-total--email .kds-payment-label,
8352
+ .kds-payment-total--email .kds-payment-amount {
8353
+ text-align: left;
8354
+ }
8355
+ }
8356
+
8357
+ /* ==========================================================================
8358
+ GRID UTILITIES — semantic aliases for BeerCSS .grid / .s* / .m* / .l*
8359
+
8360
+ Estas utilidades son ALIAS de las clases cortas de BeerCSS para que
8361
+ consumers (payment, LigoPay, Grails legacy) puedan escribir markup con
8362
+ nombres descriptivos sin renunciar al grid de 12 columnas de BeerCSS.
8363
+
8364
+ Convención (estilo Bootstrap responsive):
8365
+ - kds-grid → .grid (12-col CSS grid + 1rem gap)
8366
+ - kds-col-sm-{1..12} → .s{1..12} (mobile, siempre aplica)
8367
+ - kds-col-md-{1..12} → .m{1..12} (>=601px)
8368
+ - kds-col-lg-{1..12} → .l{1..12} (>=993px)
8369
+
8370
+ Las definiciones originales de BeerCSS NO se tocan; estas son aditivas.
8371
+ ========================================================================== */
8372
+
8373
+ .kds-grid {
8374
+ --_gap: 1rem;
8375
+ display: grid;
8376
+ grid-template-columns: repeat(12, 1fr);
8377
+ gap: var(--_gap);
8378
+ block-size: auto;
8379
+ }
8380
+
8381
+ .kds-grid.kds-grid-no-space { --_gap: 0rem; }
8382
+ .kds-grid.kds-grid-medium-space { --_gap: 1.5rem; }
8383
+ .kds-grid.kds-grid-large-space { --_gap: 2rem; }
8384
+
8385
+ .kds-grid > * { margin: 0; }
8386
+
8387
+ .kds-col-sm-1 { grid-area: auto/span 1; }
8388
+ .kds-col-sm-2 { grid-area: auto/span 2; }
8389
+ .kds-col-sm-3 { grid-area: auto/span 3; }
8390
+ .kds-col-sm-4 { grid-area: auto/span 4; }
8391
+ .kds-col-sm-5 { grid-area: auto/span 5; }
8392
+ .kds-col-sm-6 { grid-area: auto/span 6; }
8393
+ .kds-col-sm-7 { grid-area: auto/span 7; }
8394
+ .kds-col-sm-8 { grid-area: auto/span 8; }
8395
+ .kds-col-sm-9 { grid-area: auto/span 9; }
8396
+ .kds-col-sm-10 { grid-area: auto/span 10; }
8397
+ .kds-col-sm-11 { grid-area: auto/span 11; }
8398
+ .kds-col-sm-12 { grid-area: auto/span 12; }
8399
+
8400
+ @media only screen and (min-width: 601px) {
8401
+ .kds-col-md-1 { grid-area: auto/span 1; }
8402
+ .kds-col-md-2 { grid-area: auto/span 2; }
8403
+ .kds-col-md-3 { grid-area: auto/span 3; }
8404
+ .kds-col-md-4 { grid-area: auto/span 4; }
8405
+ .kds-col-md-5 { grid-area: auto/span 5; }
8406
+ .kds-col-md-6 { grid-area: auto/span 6; }
8407
+ .kds-col-md-7 { grid-area: auto/span 7; }
8408
+ .kds-col-md-8 { grid-area: auto/span 8; }
8409
+ .kds-col-md-9 { grid-area: auto/span 9; }
8410
+ .kds-col-md-10 { grid-area: auto/span 10; }
8411
+ .kds-col-md-11 { grid-area: auto/span 11; }
8412
+ .kds-col-md-12 { grid-area: auto/span 12; }
8413
+ }
8414
+
8415
+ @media only screen and (min-width: 993px) {
8416
+ .kds-col-lg-1 { grid-area: auto/span 1; }
8417
+ .kds-col-lg-2 { grid-area: auto/span 2; }
8418
+ .kds-col-lg-3 { grid-area: auto/span 3; }
8419
+ .kds-col-lg-4 { grid-area: auto/span 4; }
8420
+ .kds-col-lg-5 { grid-area: auto/span 5; }
8421
+ .kds-col-lg-6 { grid-area: auto/span 6; }
8422
+ .kds-col-lg-7 { grid-area: auto/span 7; }
8423
+ .kds-col-lg-8 { grid-area: auto/span 8; }
8424
+ .kds-col-lg-9 { grid-area: auto/span 9; }
8425
+ .kds-col-lg-10 { grid-area: auto/span 10; }
8426
+ .kds-col-lg-11 { grid-area: auto/span 11; }
8427
+ .kds-col-lg-12 { grid-area: auto/span 12; }
8428
+ }
8429
+